diff --git a/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs b/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs index 89308418c87548177a7749598e9c4286951f3f03..27ac49a6048821a3271227a65ae1406478a4f69c 100644 --- a/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs +++ b/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs @@ -16,6 +16,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox private readonly double _ratio; private readonly DelauneyMap _lossMap; // Input Speed, Output Torque (to Wheels) => Input Torque (Engine) + private NewtonMeter _minTorque = double.PositiveInfinity.SI<NewtonMeter>(); + private NewtonMeter _maxTorque = double.NegativeInfinity.SI<NewtonMeter>(); + private PerSecond _maxSpeed = double.NegativeInfinity.SI<PerSecond>(); + private PerSecond _minSpeed = double.PositiveInfinity.SI<PerSecond>(); //private readonly DelauneyMap _reverseLossMap; // Input Speed, Input Torque (Engine) => Output Torque (Wheels) public static TransmissionLossMap ReadFromFile(string fileName, double gearRatio) @@ -86,9 +90,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox _entries = entries; _lossMap = new DelauneyMap(); foreach (var entry in _entries) { - _lossMap.AddPoint(entry.InputSpeed.Value(), ((entry.InputTorque - entry.TorqueLoss) * _ratio).Value(), - entry.InputTorque.Value()); + var outTorque = (entry.InputTorque - entry.TorqueLoss) * _ratio; + _minTorque = VectoMath.Min(_minTorque, outTorque); + _maxTorque = VectoMath.Max(_maxTorque, outTorque); + + _minSpeed = VectoMath.Min(_minSpeed, entry.InputSpeed); + _maxSpeed = VectoMath.Max(_maxSpeed, entry.InputSpeed); + + _lossMap.AddPoint(entry.InputSpeed.Value(), outTorque.Value(), entry.InputTorque.Value()); } + _lossMap.Triangulate(); } @@ -101,14 +112,17 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox public NewtonMeter GearboxInTorque(PerSecond angularVelocity, NewtonMeter gbxOutTorque) { try { - var gbxInTorque = _lossMap.Interpolate(angularVelocity.Value(), gbxOutTorque.Value()).SI<NewtonMeter>(); + var limitedAngularVelocity = VectoMath.Limit(angularVelocity, _minSpeed, _maxSpeed).Value(); + var limitedTorque = VectoMath.Limit(gbxOutTorque, _minTorque, _maxTorque).Value(); + var gbxInTorque = _lossMap.Interpolate(limitedAngularVelocity, limitedTorque).SI<NewtonMeter>(); LogManager.GetLogger(GetType()).DebugFormat("GearboxLoss: {0}", gbxInTorque); // Torque at input of the gearbox must be greater or equal than the value without any losses (just torque/ratio) return VectoMath.Max(gbxInTorque, gbxOutTorque / _ratio); - } catch (Exception e) { - throw new VectoSimulationException( - string.Format("Failed to interpolate in TransmissionLossMap. angularVelocity: {0}, torque: {1}", angularVelocity, - gbxOutTorque), e); + } catch (VectoException) { + var log = LogManager.GetLogger(GetType()); + log.ErrorFormat("Failed to interpolate in TransmissionLossMap. angularVelocity: {0}, torque: {1}", angularVelocity, + gbxOutTorque); + return gbxOutTorque / _ratio; } } diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index edf6f9258e50bec0567e01d5b0388af516911606..38f25a489925012872b19e3d6814d3510b9ccd24 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -218,7 +218,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// </summary> protected virtual Watt LimitEnginePower(Watt requestedEnginePower) { - return VectoMath.Limit(requestedEnginePower, _currentState.DynamicFullLoadPower, _currentState.FullDragPower); + return VectoMath.Limit(requestedEnginePower, _currentState.FullDragPower, _currentState.DynamicFullLoadPower); } /// <summary> diff --git a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs index 5db7fd7b6000a79cd5e787a5bed393d9a46a4c97..ebaf83d14bdfaf90c552fa1f09dff17b85269ecf 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Connector.Ports.Impl; using TUGraz.VectoCore.Models.Simulation; @@ -34,6 +36,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #region ITnOutProvider + [DebuggerHidden] public ITnOutPort OutPort() { return this; @@ -48,6 +51,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl get { return _gear; } set { _gear = value; } } + #endregion #region ITnOutPort @@ -64,9 +68,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } - IResponse ITnOutPort.Request(Second absTime, Second dt, NewtonMeter torque, PerSecond engineSpeed, bool dryRun) + IResponse ITnOutPort.Request(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outEngineSpeed, bool dryRun) { - if (Gear == 0) { + if (Gear == 0 || outEngineSpeed.IsEqual(0)) { return Next.Request(absTime, dt, 0.SI<NewtonMeter>(), 0.SI<PerSecond>()); } @@ -74,11 +78,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl PerSecond inEngineSpeed; NewtonMeter inTorque; + var previousGear = _gear; + do { gearChanged = false; // calculate new inEngineSpeed and Torque for the current gear - inEngineSpeed = engineSpeed * CurrentGear.Ratio; - inTorque = CurrentGear.LossMap.GearboxInTorque(inEngineSpeed, torque); + inEngineSpeed = outEngineSpeed * CurrentGear.Ratio; + inTorque = CurrentGear.LossMap.GearboxInTorque(inEngineSpeed, outTorque); //todo: // Data.TorqueReserve ... % TorqueReserver for GearSkipping and EarlyUpshift @@ -105,7 +111,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } } while (Data.SkipGears && gearChanged); - if (gearChanged) { + if (previousGear != _gear) { return new ResponseGearShift { SimulationInterval = Data.TractionInterruption }; } diff --git a/VectoCore/Utils/VectoMath.cs b/VectoCore/Utils/VectoMath.cs index 8704d5565a6240b59d5e9e2e055e3bd651fd0bd1..412f6c03f51db77aadb3de57be334b407baaacc7 100644 --- a/VectoCore/Utils/VectoMath.cs +++ b/VectoCore/Utils/VectoMath.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using TUGraz.VectoCore.Exceptions; namespace TUGraz.VectoCore.Utils { @@ -79,8 +81,11 @@ namespace TUGraz.VectoCore.Utils return c1.CompareTo(c2) >= 0 ? c1 : c2; } - public static T Limit<T>(T value, T upperBound, T lowerBound) where T : SIBase<T> + public static T Limit<T>(T value, T lowerBound, T upperBound) where T : SIBase<T> { + if (lowerBound > upperBound) + throw new VectoException("VectoMath.Limit: lowerBound must not be greater than upperBound"); + if (value > upperBound) { return upperBound; } diff --git a/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs b/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs index acf596f0348c7620d9cf29d5d9a9b4f3bf6b0201..c80c95284908be9b565a084d9c67abce24678dfb 100644 --- a/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs @@ -1,8 +1,8 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using NLog.Targets; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.FileIO.Reader.Impl; +using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Connector.Ports.Impl; using TUGraz.VectoCore.Models.Simulation.Impl; using TUGraz.VectoCore.Models.SimulationComponent.Impl; @@ -72,11 +72,15 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent public void Gearbox_LossMapInterpolationFail() { var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); - var gearbox = new Gearbox(new VehicleContainer(), gearboxData); + var container = new VehicleContainer(); + var gearbox = new Gearbox(container, gearboxData); + + container.Gear = 1; - AssertHelper.Exception<VectoException>( - () => gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), 5000.SI<NewtonMeter>(), 10000.SI<PerSecond>()), - "Failed to interpolate in TransmissionLossMap. angularVelocity: 63800.0000 [1/s], torque: 5000.0000 [Nm]"); + var response = gearbox.OutPort() + .Request(0.SI<Second>(), 1.SI<Second>(), 5000.SI<NewtonMeter>(), 10000.SI<PerSecond>()); + + Assert.AreEqual(response.ResponseType, ResponseType.FailOverload); } [TestMethod] @@ -89,24 +93,38 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var port = new MockTnOutPort(); gearbox.InPort().Connect(port); + container.Gear = 1; + var ratio = 6.38; - //todo: serach for a overload point in gearbox fullloadcurve - //todo: serach for a overload point in engine fullloadcurve var expected = new[] { - new { t = 2129, n = 1600, loss = 55.0 }, //todo: calc exact loss - new { t = 2250, n = 1200, loss = 55.0 }, //todo: calc exact loss + new { t = 2500, n = 900 }, + new { t = 2500, n = 1700 }, + new { t = 2129, n = 1600 }, // to high for gearbox, but ok for engine }; foreach (var exp in expected) { - var torque = (exp.t.SI<NewtonMeter>() - exp.loss.SI<NewtonMeter>()) * ratio; + var torque = exp.t.SI<NewtonMeter>() * ratio; var angularVelocity = exp.n.RPMtoRad() / ratio; var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); Assert.IsInstanceOfType(response, typeof(ResponseFailOverload)); } - Assert.Inconclusive("Test if the intersection of two fullloadcurves is correct"); + var expectedCorrect = new[] { + new { t = 500, n = 700 }, + new { t = 1500, n = 1100 }, + new { t = 2000, n = 1500 }, + new { t = 2240, n = 1200 } // ok for gearbox but would be to high for engine + }; + + foreach (var exp in expectedCorrect) { + var torque = exp.t.SI<NewtonMeter>() * ratio; + var angularVelocity = exp.n.RPMtoRad() / ratio; + + var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); + Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); + } } [TestMethod] @@ -123,33 +141,29 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent // the first element 0.0 is just a placeholder for axlegear, not used in this test var expected = new[] { - new { gear = 1, t = 50, n = 800, loss = 10.108 }, - new { gear = 1, t = 2450, n = 800, loss = 58.11 }, - new { gear = 1, t = -1000, n = 800, loss = 29.11 }, - new { gear = 1, t = 850, n = 800, loss = 26.11 }, - new { gear = 1, t = 850, n = 0, loss = 22.06 }, - new { gear = 1, t = 850, n = 200, loss = 23.07 }, - new { gear = 1, t = 850, n = 2000, loss = 32.18 }, - new { gear = 2, t = 50, n = 800, loss = 10.108 }, - new { gear = 2, t = 2450, n = 800, loss = 58.11 }, - new { gear = 2, t = -1000, n = 800, loss = 29.11 }, - new { gear = 2, t = 850, n = 800, loss = 26.11 }, - new { gear = 2, t = 850, n = 0, loss = 22.06 }, - new { gear = 2, t = 850, n = 200, loss = 23.07 }, - new { gear = 2, t = 850, n = 2000, loss = 32.18 }, - new { gear = 7, t = -1000, n = 0, loss = 10.06 }, - new { gear = 7, t = -1000, n = 1200, loss = 16.132 }, - new { gear = 7, t = -1000, n = 2000, loss = 20.18 }, - new { gear = 7, t = 850, n = 0, loss = 9.31 }, - new { gear = 7, t = 850, n = 1200, loss = 15.382 }, - new { gear = 7, t = 850, n = 2000, loss = 19.43 }, - new { gear = 7, t = 2450, n = 0, loss = 17.31 }, - new { gear = 7, t = 2450, n = 1200, loss = 23.382 }, - new { gear = 7, t = 2450, n = 2000, loss = 27.43 }, + new { gear = 1, t = 50, n = 800, loss = 10.108, responseType = ResponseType.Success }, + new { gear = 1, t = 2450, n = 800, loss = 58.11, responseType = ResponseType.FailOverload }, + new { gear = 1, t = -1000, n = 800, loss = 29.11, responseType = ResponseType.Success }, + new { gear = 1, t = 850, n = 800, loss = 26.11, responseType = ResponseType.Success }, + new { gear = 1, t = 850, n = 0, loss = 22.06, responseType = ResponseType.Success }, + new { gear = 1, t = 850, n = 200, loss = 23.07, responseType = ResponseType.FailOverload }, + new { gear = 2, t = 50, n = 800, loss = 10.108, responseType = ResponseType.Success }, + new { gear = 2, t = 2450, n = 800, loss = 58.11, responseType = ResponseType.GearShift }, + new { gear = 2, t = -1000, n = 800, loss = 29.11, responseType = ResponseType.Success }, + new { gear = 2, t = 850, n = 800, loss = 26.11, responseType = ResponseType.Success }, + new { gear = 2, t = 850, n = 0, loss = 22.06, responseType = ResponseType.Success }, + new { gear = 2, t = 850, n = 200, loss = 23.07, responseType = ResponseType.GearShift }, + new { gear = 2, t = 850, n = 2000, loss = 32.18, responseType = ResponseType.GearShift }, + new { gear = 7, t = -1000, n = 0, loss = 10.06, responseType = ResponseType.Success }, + new { gear = 7, t = -1000, n = 1200, loss = 16.132, responseType = ResponseType.Success }, + new { gear = 7, t = 850, n = 0, loss = 9.31, responseType = ResponseType.Success }, + new { gear = 7, t = 850, n = 1200, loss = 15.382, responseType = ResponseType.Success }, + new { gear = 7, t = 850, n = 2000, loss = 19.43, responseType = ResponseType.GearShift }, + new { gear = 7, t = 2450, n = 0, loss = 17.31, responseType = ResponseType.Success }, + new { gear = 7, t = 2450, n = 1200, loss = 23.382, responseType = ResponseType.FailOverload } }; - for (var i = 0; i < expected.Length; i++) { - var exp = expected[i]; + foreach (var exp in expected) { var expectedT = exp.t.SI<NewtonMeter>(); var expectedN = exp.n.RPMtoRad(); var expectedLoss = exp.loss.SI<NewtonMeter>(); @@ -157,31 +171,122 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var torque = (expectedT - expectedLoss) * ratios[exp.gear]; var angularVelocity = expectedN / ratios[exp.gear]; - gearbox.Gear = (uint)exp.gear; - gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); - AssertHelper.AreRelativeEqual(0.SI<Second>(), port.AbsTime, message: i.ToString()); - AssertHelper.AreRelativeEqual(1.SI<Second>(), port.Dt, message: i.ToString()); - AssertHelper.AreRelativeEqual(expectedN, port.AngularVelocity, message: i.ToString()); - AssertHelper.AreRelativeEqual(expectedT, port.Torque, message: i.ToString()); + container.Gear = (uint)exp.gear; + var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); + Assert.AreEqual(exp.responseType, response.ResponseType, exp.ToString()); + + if (angularVelocity.IsEqual(0)) { + expectedT = 0.SI<NewtonMeter>(); + } + + if (exp.responseType == ResponseType.Success) { + AssertHelper.AreRelativeEqual(0.SI<Second>(), port.AbsTime, message: exp.ToString()); + AssertHelper.AreRelativeEqual(1.SI<Second>(), port.Dt, message: exp.ToString()); + AssertHelper.AreRelativeEqual(expectedN, port.AngularVelocity, message: exp.ToString()); + AssertHelper.AreRelativeEqual(expectedT, port.Torque, message: exp.ToString()); + } } } [TestMethod] - public void Gearbox_ShiftUp() + public void Gearbox_ShiftDown() { - Assert.Inconclusive(); + var container = new VehicleContainer(); + var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); + var gearbox = new Gearbox(container, gearboxData); + + var port = new MockTnOutPort(); + gearbox.InPort().Connect(port); + + var ratios = new[] { 0.0, 6.38, 4.63, 3.44, 2.59, 1.86, 1.35, 1, 0.76 }; + // the first element 0.0 is just a placeholder for axlegear, not used in this test + + var expected = new[] { + new { gear = 8, newGear = 7, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 7, newGear = 6, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 6, newGear = 5, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 5, newGear = 4, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 4, newGear = 3, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 3, newGear = 2, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 2, newGear = 1, t = 1500, n = 700, responseType = ResponseType.GearShift }, + new { gear = 1, newGear = 1, t = 1200, n = 700, responseType = ResponseType.Success }, + new { gear = 8, newGear = 1, t = 10000, n = 120, responseType = ResponseType.GearShift }, // skip gears + }; + + foreach (var exp in expected) { + var expectedT = exp.t.SI<NewtonMeter>(); + var expectedN = exp.n.RPMtoRad(); + + var torque = expectedT * ratios[exp.gear]; + var angularVelocity = expectedN / ratios[exp.gear]; + + container.Gear = (uint)exp.gear; + var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); + Assert.AreEqual(response.ResponseType, exp.responseType, exp.ToString()); + Assert.AreEqual((uint)exp.newGear, container.Gear, exp.ToString()); + } } [TestMethod] - public void Gearbox_ShiftDown() + public void Gearbox_ShiftUp() { - Assert.Inconclusive(); + var container = new VehicleContainer(); + var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); + var gearbox = new Gearbox(container, gearboxData); + + var port = new MockTnOutPort(); + gearbox.InPort().Connect(port); + + var ratios = new[] { 0.0, 6.38, 4.63, 3.44, 2.59, 1.86, 1.35, 1, 0.76 }; + // the first element 0.0 is just a placeholder for axlegear, not used in this test + + var expected = new[] { + new { gear = 7, newGear = 8, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 6, newGear = 7, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 5, newGear = 6, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 4, newGear = 5, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 3, newGear = 4, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 2, newGear = 3, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 1, newGear = 2, t = 1000, n = 1400, responseType = ResponseType.GearShift }, + new { gear = 8, newGear = 8, t = 1000, n = 1400, responseType = ResponseType.Success }, + new { gear = 1, newGear = 8, t = 200, n = 9000, responseType = ResponseType.GearShift }, // skip gears + }; + + foreach (var exp in expected) { + var expectedT = exp.t.SI<NewtonMeter>(); + var expectedN = exp.n.RPMtoRad(); + + var torque = expectedT * ratios[exp.gear]; + var angularVelocity = expectedN / ratios[exp.gear]; + + container.Gear = (uint)exp.gear; + var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); + Assert.AreEqual(response.ResponseType, exp.responseType, exp.ToString()); + Assert.AreEqual((uint)exp.newGear, container.Gear, exp.ToString()); + } } [TestMethod] - public void Gearbox_Overload() + public void Gearbox_NoGear() { + var container = new VehicleContainer(); + var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); + var gearbox = new Gearbox(container, gearboxData); + + var port = new MockTnOutPort(); + gearbox.InPort().Connect(port); + + container.Gear = 0; + var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), 500.SI<NewtonMeter>(), 1000.SI<PerSecond>()); + Assert.AreEqual(response.ResponseType, ResponseType.Success); + + AssertHelper.AreRelativeEqual(0.SI<Second>(), port.AbsTime); + AssertHelper.AreRelativeEqual(1.SI<Second>(), port.Dt); + AssertHelper.AreRelativeEqual(0.SI<PerSecond>(), port.AngularVelocity); + AssertHelper.AreRelativeEqual(0.SI<NewtonMeter>(), port.Torque); + + Assert.Inconclusive(); } }