diff --git a/VectoCommon/VectoCommon/Utils/SI.cs b/VectoCommon/VectoCommon/Utils/SI.cs index d8158aaf5eb5bd6be4bee26b8b299e8dd1abdd03..cc77faa2cf5c99360b4325bec5e876163f67f5c5 100644 --- a/VectoCommon/VectoCommon/Utils/SI.cs +++ b/VectoCommon/VectoCommon/Utils/SI.cs @@ -1398,7 +1398,7 @@ namespace TUGraz.VectoCommon.Utils /// <returns></returns> public bool IsBetween(SI lower, SI upper) { - return lower <= Val && Val <= upper; + return VectoMath.Min(lower, upper) <= Val && Val <= VectoMath.Max(lower, upper); } /// <summary> @@ -1409,7 +1409,7 @@ namespace TUGraz.VectoCommon.Utils /// <returns></returns> public bool IsBetween(double lower, double upper) { - return lower <= Val && Val <= upper; + return Math.Min(lower, upper) <= Val && Val <= Math.Max(lower, upper); } #endregion diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs index c3727988583ee21942378c8087ba38040aaa6fc5..3eebd9dc66608a5cc46f29d25441af8d50abbeb0 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs @@ -58,6 +58,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine private PerSecond _n95hSpeed; // 95% of Pmax private PerSecond _n80hSpeed; // 80% of Pmax + private PerSecond _nTq99hSpeed; // 99% of max Torque above rated + private PerSecond _nTq99lSpeed; // 99% of max Torque below rated speed + private PerSecond _nP99hSpeed; // 99% of Pmax above rated speed + [Required, ValidateObject] internal readonly List<FullLoadCurveEntry> FullLoadEntries; private SortedList<PerSecond, int> _quickLookup; @@ -107,12 +111,12 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine public NewtonMeter MaxTorque { - get { return _maxTorque ?? FindMaxTorque(); } + get { return _maxTorque ?? (_maxTorque = FullLoadEntries.Max(x => x.TorqueFullLoad)); } } public NewtonMeter MaxDragTorque { - get { return _maxDragTorque ?? FindMaxDragTorque(); } + get { return _maxDragTorque ?? (_maxDragTorque = FullLoadEntries.Min(x => x.TorqueDrag)); } } public NewtonMeter FullLoadStationaryTorque(PerSecond angularVelocity) @@ -131,18 +135,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine angularVelocity); } - private NewtonMeter FindMaxTorque() - { - _maxTorque = FullLoadEntries.Max(x => x.TorqueFullLoad); - return _maxTorque; - } - - private NewtonMeter FindMaxDragTorque() - { - _maxDragTorque = FullLoadEntries.Min(x => x.TorqueDrag); - return _maxDragTorque; - } - + /// <summary> /// Compute the engine's rated speed from the given full-load curve (i.e. engine speed with max. power) /// </summary> @@ -273,6 +266,31 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine } } + public PerSecond NTq99hSpeed + { + get { return _nTq99hSpeed ?? (_nTq99hSpeed = FindEnginSpeedForTorque(0.99 * MaxTorque).Last()); } + } + + public PerSecond NTq99lSpeed + { + get { return _nTq99lSpeed ?? (_nTq99lSpeed = FindEnginSpeedForTorque(0.99 * MaxTorque).First()); } + } + + public PerSecond NP99hSpeed + { + get { return _nP99hSpeed ?? (_nP99hSpeed = ComputeNP99HSpeed()); } + } + + private PerSecond ComputeNP99HSpeed() + { + var retVal = FindEngineSpeedForPower(0.99 * MaxPower).Last(); + if (retVal <= RatedSpeed) { + throw new VectoException( + "failed to compute n_P99H - must be higher than rated speed. rated speed: {0}, n_tq99h: {1}", RatedSpeed, retVal); + } + return retVal; + } + public PerSecond LoSpeed { get { return _engineSpeedLo ?? (_engineSpeedLo = FindEngineSpeedForPower(0.55 * MaxPower).First()); } @@ -333,6 +351,31 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine return retVal.First(x => x.IsBetween(p1.EngineSpeed, p2.EngineSpeed)).SI<PerSecond>(); } + private List<PerSecond> FindEnginSpeedForTorque(NewtonMeter torque) + { + var retVal = new List<PerSecond>(); + foreach (var pair in FullLoadEntries.Pairwise(Tuple.Create)) { + var solution = FindEnginSpeedForTorque(pair.Item1, pair.Item2, torque); + if (solution != null) { + retVal.Add(solution); + } + } + retVal.Sort(); + return retVal.Distinct(new SI.EqualityComparer<PerSecond>()).ToList(); + } + + private PerSecond FindEnginSpeedForTorque(FullLoadCurveEntry p1, FullLoadCurveEntry p2, NewtonMeter torque) + { + if (p1.TorqueFullLoad.IsEqual(p2.TorqueFullLoad) && p1.TorqueFullLoad.IsEqual(torque)) { + // horizontal line in FLD that equals requested torque + return p2.EngineSpeed; + } + if (torque.IsBetween(p1.TorqueFullLoad, p2.TorqueFullLoad)) { + return VectoMath.Interpolate(p1.TorqueFullLoad, p2.TorqueFullLoad, p1.EngineSpeed, p2.EngineSpeed, torque); + } + return null; + } + private List<PerSecond> FindEngineSpeedForPower(Watt power) { var retVal = new List<PerSecond>(); diff --git a/VectoCore/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs b/VectoCore/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs index bb1d9cb4c9f8e7e07d429b88431fa6bf01c399e5..0a7b8df5d38c5cb256fc57ae620db98099b2851f 100644 --- a/VectoCore/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs +++ b/VectoCore/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs @@ -137,6 +137,46 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData AssertHelper.AreRelativeEqual(-320.SI<NewtonMeter>(), fldCurve.MaxDragTorque); } + [TestCase()] + public void TestP99HighSpeed() + { + var fldCurve = FullLoadCurveReader.ReadFromFile(CoachEngineFLD); + fldCurve.EngineData = new CombustionEngineData() {IdleSpeed = 560.RPMtoRad()}; + + var nP99h = fldCurve.NP99hSpeed; + + Assert.IsTrue(nP99h > fldCurve.PreferredSpeed); + Assert.AreEqual(fldCurve.MaxPower.Value() * 0.99, (fldCurve.FullLoadStationaryTorque(nP99h) * nP99h).Value(), 1e-3); + Assert.AreEqual(1810.67898, nP99h.AsRPM, 1e-3); + } + + [TestCase()] + public void TestTq99HighSpeed() + { + var fldCurve = FullLoadCurveReader.ReadFromFile(CoachEngineFLD); + fldCurve.EngineData = new CombustionEngineData() { IdleSpeed = 560.RPMtoRad() }; + + var nTq99h = fldCurve.NTq99hSpeed; + + Assert.IsTrue(nTq99h > fldCurve.PreferredSpeed); + Assert.AreEqual(fldCurve.MaxTorque.Value() * 0.99, fldCurve.FullLoadStationaryTorque(nTq99h).Value(), 1e-3); + Assert.AreEqual(1420.8144, nTq99h.AsRPM, 1e-3); + } + + + [TestCase()] + public void TestTq99LowSpeed() + { + var fldCurve = FullLoadCurveReader.ReadFromFile(CoachEngineFLD); + fldCurve.EngineData = new CombustionEngineData() { IdleSpeed = 560.RPMtoRad() }; + + var nTq99l = fldCurve.NTq99lSpeed; + + Assert.IsTrue(nTq99l < fldCurve.PreferredSpeed); + Assert.AreEqual(fldCurve.MaxTorque.Value() * 0.99, fldCurve.FullLoadStationaryTorque(nTq99l).Value(), 1e-3); + Assert.AreEqual(990.9626, nTq99l.AsRPM, 1e-3); + } + [TestCase] public void TestPreferredSpeed2() {