From ce80e54ba99a2d3c84d568a64d6fecb2475bc9ab Mon Sep 17 00:00:00 2001 From: Markus Quaritsch <markus.quaritsch@tugraz.at> Date: Wed, 22 Jul 2015 15:17:14 +0200 Subject: [PATCH] adapt file-I/O to read new input files for gearbox, engine. fix engine-only simulation runs / time-based run --- .../FileIO/DeclarationFile/EngineFileDecl.cs | 5 +- .../FileIO/DeclarationFile/GearboxFileDecl.cs | 3 +- .../FileIO/EngineeringFile/EngineFileEng.cs | 2 +- .../FileIO/EngineeringFile/GearboxFileEng.cs | 2 +- .../AbstractSimulationDataAdapter.cs | 4 +- .../DeclarationDataAdapter.cs | 35 +- .../EngineeringDataAdapter.cs | 24 +- .../DeclarationModeSimulationDataReader.cs | 8 +- .../EngineeringModeSimulationDataReader.cs | 8 +- .../Models/Declaration/DeclarationData.cs | 5 +- .../Models/Simulation/Impl/DistanceRun.cs | 8 +- .../Simulation/Impl/PowertrainBuilder.cs | 8 +- .../Simulation/Impl/SimulatorFactory.cs | 9 +- VectoCore/Models/Simulation/Impl/TimeRun.cs | 30 + VectoCore/Models/Simulation/Impl/VectoRun.cs | 4 +- .../Simulation/Impl/VehicleContainer.cs | 4 +- .../Data/CombustionEngineData.cs | 43 +- ...ullLoadCurve.cs => EngineFullLoadCurve.cs} | 1004 ++++++++--------- .../Data/GearFullLoadCurve.cs | 10 + .../Data/Gearbox/GearData.cs | 19 +- .../Models/SimulationComponent/Impl/Clutch.cs | 2 +- .../Impl/CombustionEngine.cs | 36 +- .../Impl/TimeBasedDrivingCycle.cs | 9 +- VectoCore/VectoCore.csproj | 4 +- .../Models/Simulation/DrivingCycleTests.cs | 2 +- .../CombustionEngineTest.cs | 6 +- .../FullLoadCurveTest.cs | 20 +- .../Components/12t Delivery Truck.veng | 23 +- .../Components/12t Delivery Truck.vgbx | 26 +- .../TestData/Components/24t Coach.veng | 15 +- .../TestData/Components/24t Coach.vgbx | 32 +- 31 files changed, 727 insertions(+), 683 deletions(-) create mode 100644 VectoCore/Models/Simulation/Impl/TimeRun.cs rename VectoCore/Models/SimulationComponent/Data/Engine/{FullLoadCurve.cs => EngineFullLoadCurve.cs} (94%) create mode 100644 VectoCore/Models/SimulationComponent/Data/GearFullLoadCurve.cs diff --git a/VectoCore/FileIO/DeclarationFile/EngineFileDecl.cs b/VectoCore/FileIO/DeclarationFile/EngineFileDecl.cs index ebf5920a64..baa80e0352 100644 --- a/VectoCore/FileIO/DeclarationFile/EngineFileDecl.cs +++ b/VectoCore/FileIO/DeclarationFile/EngineFileDecl.cs @@ -34,7 +34,7 @@ namespace TUGraz.VectoCore.FileIO.DeclarationFile /// } /// } /// </code> - internal class EngineFileV2Declaration : VectoEngineFile + internal class EngineFileV3Declaration : VectoEngineFile { [JsonProperty(Required = Required.Always)] public JsonDataHeader Header; [JsonProperty(Required = Required.Always)] public DataBodyDecl Body; @@ -60,7 +60,8 @@ namespace TUGraz.VectoCore.FileIO.DeclarationFile /// </summary> [JsonProperty("IdlingSpeed", Required = Required.Always)] public double IdleSpeed; - [JsonProperty(Required = Required.Always)] public IList<DataFullLoadCurve> FullLoadCurves; + //[JsonProperty(Required = Required.Always)] public IList<DataFullLoadCurve> FullLoadCurves; + [JsonProperty(Required = Required.Always)] public string FullLoadCurve; /// <summary> /// The Fuel Consumption Map is used to calculate the base Fuel Consumption (FC) value. diff --git a/VectoCore/FileIO/DeclarationFile/GearboxFileDecl.cs b/VectoCore/FileIO/DeclarationFile/GearboxFileDecl.cs index a4d7021cdc..146419ff95 100644 --- a/VectoCore/FileIO/DeclarationFile/GearboxFileDecl.cs +++ b/VectoCore/FileIO/DeclarationFile/GearboxFileDecl.cs @@ -30,7 +30,7 @@ namespace TUGraz.VectoCore.FileIO.DeclarationFile /// ... /// ] /// } - public class GearboxFileV4Declaration : VectoGearboxFile + public class GearboxFileV5Declaration : VectoGearboxFile { [JsonProperty(Required = Required.Always)] public JsonDataHeader Header; [JsonProperty(Required = Required.Always)] public DataBodyDecl Body; @@ -53,6 +53,7 @@ namespace TUGraz.VectoCore.FileIO.DeclarationFile { [JsonProperty(Required = Required.Always)] public double Ratio; [JsonProperty(Required = Required.Always)] public string LossMap; + [JsonProperty] public string FullLoadCurve; } } } \ No newline at end of file diff --git a/VectoCore/FileIO/EngineeringFile/EngineFileEng.cs b/VectoCore/FileIO/EngineeringFile/EngineFileEng.cs index 78e9288358..4d4771b06a 100644 --- a/VectoCore/FileIO/EngineeringFile/EngineFileEng.cs +++ b/VectoCore/FileIO/EngineeringFile/EngineFileEng.cs @@ -3,7 +3,7 @@ using TUGraz.VectoCore.FileIO.DeclarationFile; namespace TUGraz.VectoCore.FileIO.EngineeringFile { - internal class EngineFileV2Engineering : EngineFileV2Declaration + internal class EngineFileV3Engineering : EngineFileV3Declaration { [JsonProperty(Required = Required.Always)] public new DataBodyEng Body; diff --git a/VectoCore/FileIO/EngineeringFile/GearboxFileEng.cs b/VectoCore/FileIO/EngineeringFile/GearboxFileEng.cs index 17d164efb2..ad0c4847e4 100644 --- a/VectoCore/FileIO/EngineeringFile/GearboxFileEng.cs +++ b/VectoCore/FileIO/EngineeringFile/GearboxFileEng.cs @@ -48,7 +48,7 @@ namespace TUGraz.VectoCore.FileIO.EngineeringFile /// ... /// ] /// } - public class GearboxFileV4Engineering : GearboxFileV4Declaration + public class GearboxFileV5Engineering : GearboxFileV5Declaration { [JsonProperty(Required = Required.Always)] public new DataBodyEng Body; diff --git a/VectoCore/FileIO/Reader/DataObjectAdaper/AbstractSimulationDataAdapter.cs b/VectoCore/FileIO/Reader/DataObjectAdaper/AbstractSimulationDataAdapter.cs index deb799e312..b970b1c3a1 100644 --- a/VectoCore/FileIO/Reader/DataObjectAdaper/AbstractSimulationDataAdapter.cs +++ b/VectoCore/FileIO/Reader/DataObjectAdaper/AbstractSimulationDataAdapter.cs @@ -51,7 +51,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper return retVal; } - internal CombustionEngineData SetCommonCombustionEngineData(EngineFileV2Declaration.DataBodyDecl data, string basePath) + internal CombustionEngineData SetCommonCombustionEngineData(EngineFileV3Declaration.DataBodyDecl data, string basePath) { var retVal = new CombustionEngineData() { SavedInDeclarationMode = data.SavedInDeclarationMode, @@ -66,7 +66,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper return retVal; } - internal GearboxData SetCommonGearboxData(GearboxFileV4Declaration.DataBodyDecl data) + internal GearboxData SetCommonGearboxData(GearboxFileV5Declaration.DataBodyDecl data) { return new GearboxData() { SavedInDeclarationMode = data.SavedInDeclarationMode, diff --git a/VectoCore/FileIO/Reader/DataObjectAdaper/DeclarationDataAdapter.cs b/VectoCore/FileIO/Reader/DataObjectAdaper/DeclarationDataAdapter.cs index c33723ecf6..5c62e1c806 100644 --- a/VectoCore/FileIO/Reader/DataObjectAdaper/DeclarationDataAdapter.cs +++ b/VectoCore/FileIO/Reader/DataObjectAdaper/DeclarationDataAdapter.cs @@ -36,7 +36,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper public override CombustionEngineData CreateEngineData(VectoEngineFile engine) { - var fileV2Decl = engine as EngineFileV2Declaration; + var fileV2Decl = engine as EngineFileV3Declaration; if (fileV2Decl != null) { return CreateEngineData(fileV2Decl); } @@ -45,7 +45,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper public override GearboxData CreateGearboxData(VectoGearboxFile gearbox, CombustionEngineData engine) { - var fileV5Decl = gearbox as GearboxFileV4Declaration; + var fileV5Decl = gearbox as GearboxFileV5Declaration; if (fileV5Decl != null) { return CreateGearboxData(fileV5Decl, engine); } @@ -143,18 +143,17 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper return retVal; } - internal CombustionEngineData CreateEngineData(EngineFileV2Declaration engine) + internal CombustionEngineData CreateEngineData(EngineFileV3Declaration engine) { var retVal = SetCommonCombustionEngineData(engine.Body, engine.BasePath); retVal.Inertia = DeclarationData.Engine.EngineInertia(retVal.Displacement); - foreach (var entry in engine.Body.FullLoadCurves) { - retVal.AddFullLoadCurve(entry.Gears, FullLoadCurve.ReadFromFile(Path.Combine(engine.BasePath, entry.Path), true)); - } + retVal.FullLoadCurve = EngineFullLoadCurve.ReadFromFile(Path.Combine(engine.BasePath, engine.Body.FullLoadCurve), + true); return retVal; } - internal GearboxData CreateGearboxData(GearboxFileV4Declaration gearbox, CombustionEngineData engine) + internal GearboxData CreateGearboxData(GearboxFileV5Declaration gearbox, CombustionEngineData engine) { var retVal = SetCommonGearboxData(gearbox.Body); @@ -181,17 +180,29 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper for (uint i = 0; i < gearbox.Body.Gears.Count; i++) { var gearSettings = gearbox.Body.Gears[(int)i]; var lossMapPath = Path.Combine(gearbox.BasePath, gearSettings.LossMap); - TransmissionLossMap lossMap = TransmissionLossMap.ReadFromFile(lossMapPath, gearSettings.Ratio); + var lossMap = TransmissionLossMap.ReadFromFile(lossMapPath, gearSettings.Ratio); if (i == 0) { - retVal.AxleGearData = new GearData(lossMap, null, gearSettings.Ratio, false); + retVal.AxleGearData = new GearData() { + LossMap = lossMap, + Ratio = gearSettings.Ratio, + TorqueConverterActive = false + }; } else { - var shiftPolygon = DeclarationData.Gearbox.ComputeShiftPolygon(engine, i); - retVal._gearData.Add(i, new GearData(lossMap, shiftPolygon, gearSettings.Ratio, false)); + var fullLoad = !String.IsNullOrEmpty(gearSettings.FullLoadCurve) && gearSettings.FullLoadCurve.Equals("<NOFILE>") + ? GearFullLoadCurve.ReadFromFile(Path.Combine(gearbox.BasePath, gearSettings.FullLoadCurve)) + : null; + var shiftPolygon = DeclarationData.Gearbox.ComputeShiftPolygon(fullLoad, engine); + + retVal._gearData.Add(i, new GearData() { + LossMap = lossMap, + ShiftPolygon = shiftPolygon, + Ratio = gearSettings.Ratio, + TorqueConverterActive = false + }); } } - return retVal; } } diff --git a/VectoCore/FileIO/Reader/DataObjectAdaper/EngineeringDataAdapter.cs b/VectoCore/FileIO/Reader/DataObjectAdaper/EngineeringDataAdapter.cs index 01880224d1..002aed5ccf 100644 --- a/VectoCore/FileIO/Reader/DataObjectAdaper/EngineeringDataAdapter.cs +++ b/VectoCore/FileIO/Reader/DataObjectAdaper/EngineeringDataAdapter.cs @@ -31,7 +31,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper public override CombustionEngineData CreateEngineData(VectoEngineFile engine) { - var fileV2Eng = engine as EngineFileV2Engineering; + var fileV2Eng = engine as EngineFileV3Engineering; if (fileV2Eng != null) { return CreateEngineData(fileV2Eng); } @@ -40,7 +40,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper public override GearboxData CreateGearboxData(VectoGearboxFile gearbox, CombustionEngineData engine) { - var fileV5Eng = gearbox as GearboxFileV4Engineering; + var fileV5Eng = gearbox as GearboxFileV5Engineering; if (fileV5Eng != null) { return CreateGearboxData(fileV5Eng); } @@ -125,13 +125,12 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper /// </summary> /// <param name="engine">Engin-Data file (Engineering mode)</param> /// <returns></returns> - internal CombustionEngineData CreateEngineData(EngineFileV2Engineering engine) + internal CombustionEngineData CreateEngineData(EngineFileV3Engineering engine) { var retVal = SetCommonCombustionEngineData(engine.Body, engine.BasePath); retVal.Inertia = engine.Body.Inertia.SI<KilogramSquareMeter>(); - foreach (var entry in engine.Body.FullLoadCurves) { - retVal.AddFullLoadCurve(entry.Gears, FullLoadCurve.ReadFromFile(Path.Combine(engine.BasePath, entry.Path), false)); - } + retVal.FullLoadCurve = EngineFullLoadCurve.ReadFromFile(Path.Combine(engine.BasePath, engine.Body.FullLoadCurve), + false); return retVal; } @@ -143,7 +142,7 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper /// </summary> /// <param name="gearbox"></param> /// <returns></returns> - internal GearboxData CreateGearboxData(GearboxFileV4Engineering gearbox) + internal GearboxData CreateGearboxData(GearboxFileV5Engineering gearbox) { var retVal = SetCommonGearboxData(gearbox.Body); @@ -169,8 +168,17 @@ namespace TUGraz.VectoCore.FileIO.Reader.DataObjectAdaper var shiftPolygon = !String.IsNullOrEmpty(gearSettings.ShiftPolygon) ? ShiftPolygon.ReadFromFile(Path.Combine(gearbox.BasePath, gearSettings.ShiftPolygon)) : null; + var fullLoad = !String.IsNullOrEmpty(gearSettings.FullLoadCurve) && !gearSettings.FullLoadCurve.Equals("<NOFILE>") + ? GearFullLoadCurve.ReadFromFile(Path.Combine(gearbox.BasePath, gearSettings.FullLoadCurve)) + : null; - var gear = new GearData(lossMap, shiftPolygon, gearSettings.Ratio, gearSettings.TCactive); + var gear = new GearData() { + LossMap = lossMap, + ShiftPolygon = shiftPolygon, + FullLoadCurve = fullLoad, + Ratio = gearSettings.Ratio, + TorqueConverterActive = gearSettings.TCactive + }; if (i == 0) { retVal.AxleGearData = gear; } else { diff --git a/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs b/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs index 75cb4047c5..eb996b81e9 100644 --- a/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs +++ b/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs @@ -115,8 +115,8 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl CheckForDeclarationMode(fileInfo, "Engine"); switch (fileInfo.Version) { - case 2: - Engine = JsonConvert.DeserializeObject<EngineFileV2Declaration>(json); + case 3: + Engine = JsonConvert.DeserializeObject<EngineFileV3Declaration>(json); Engine.BasePath = Path.GetDirectoryName(file); break; default: @@ -131,8 +131,8 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl CheckForDeclarationMode(fileInfo, "Gearbox"); switch (fileInfo.Version) { - case 4: - Gearbox = JsonConvert.DeserializeObject<GearboxFileV4Declaration>(json); + case 5: + Gearbox = JsonConvert.DeserializeObject<GearboxFileV5Declaration>(json); Gearbox.BasePath = Path.GetDirectoryName(file); break; default: diff --git a/VectoCore/FileIO/Reader/Impl/EngineeringModeSimulationDataReader.cs b/VectoCore/FileIO/Reader/Impl/EngineeringModeSimulationDataReader.cs index bcbd0c2a3f..a9a53668d1 100644 --- a/VectoCore/FileIO/Reader/Impl/EngineeringModeSimulationDataReader.cs +++ b/VectoCore/FileIO/Reader/Impl/EngineeringModeSimulationDataReader.cs @@ -196,8 +196,8 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl CheckForEngineeringMode(fileInfo, "Engine"); switch (fileInfo.Version) { - case 2: - var tmp = JsonConvert.DeserializeObject<EngineFileV2Engineering>(json); + case 3: + var tmp = JsonConvert.DeserializeObject<EngineFileV3Engineering>(json); tmp.BasePath = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar; return tmp; default: @@ -217,8 +217,8 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl CheckForEngineeringMode(fileInfo, "Gearbox"); switch (fileInfo.Version) { - case 4: - var tmp = JsonConvert.DeserializeObject<GearboxFileV4Engineering>(json); + case 5: + var tmp = JsonConvert.DeserializeObject<GearboxFileV5Engineering>(json); tmp.BasePath = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar; return tmp; default: diff --git a/VectoCore/Models/Declaration/DeclarationData.cs b/VectoCore/Models/Declaration/DeclarationData.cs index df50040d6e..045ef0dc62 100644 --- a/VectoCore/Models/Declaration/DeclarationData.cs +++ b/VectoCore/Models/Declaration/DeclarationData.cs @@ -210,9 +210,10 @@ namespace TUGraz.VectoCore.Models.Declaration return false; } - internal static ShiftPolygon ComputeShiftPolygon(CombustionEngineData engine, uint gear) + internal static ShiftPolygon ComputeShiftPolygon(GearFullLoadCurve gear, CombustionEngineData engine) { - var fullLoadCurve = engine.GetFullLoadCurve(gear); + // TODO: How to compute shift-polygons exactly? (merge with engine full load?) + var fullLoadCurve = engine.FullLoadCurve; var idleSpeed = engine.IdleSpeed; var maxTorque = fullLoadCurve.MaxLoadTorque; diff --git a/VectoCore/Models/Simulation/Impl/DistanceRun.cs b/VectoCore/Models/Simulation/Impl/DistanceRun.cs index ca3294473e..5c832fdf8b 100644 --- a/VectoCore/Models/Simulation/Impl/DistanceRun.cs +++ b/VectoCore/Models/Simulation/Impl/DistanceRun.cs @@ -10,10 +10,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl { public DistanceRun(IVehicleContainer container) : base(container) {} - protected override Connector.Ports.IResponse DoSimulationStep() + protected override IResponse DoSimulationStep() { - //_dt = TimeSpan.FromSeconds(1) - TimeSpan.FromMilliseconds(_dt.Milliseconds); - // estimate distance to be traveled within the next TargetTimeInterval var ds = (Container.VehicleSpeed() * Constants.SimulationSettings.TargetTimeInterval).Cast<Meter>(); @@ -21,7 +19,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl ds = Constants.SimulationSettings.DriveOffDistance; } - var response = CyclePort.Request((Second)AbsTime, ds); + var response = CyclePort.Request(AbsTime, ds); //while (response is ResponseFailTimeInterval) { // _dt = (response as ResponseFailTimeInterval).DeltaT; @@ -32,7 +30,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl return response; } - AbsTime = (AbsTime + response.SimulationInterval / 2); + AbsTime = AbsTime + response.SimulationInterval; dt = response.SimulationInterval; return response; } diff --git a/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs b/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs index f96a671e3f..81d1ebb5ad 100644 --- a/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs +++ b/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs @@ -36,8 +36,12 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl if (_engineOnly) { cycle = new TimeBasedDrivingCycle(_container, data.Cycle); } else { - //todo: make distinction between time based and distance based driving cycle! - cycle = new DistanceBasedDrivingCycle(_container, data.Cycle); + if (data.IsEngineOnly) { + cycle = new TimeBasedDrivingCycle(_container, data.Cycle); + } else { + //todo: make distinction between time based and distance based driving cycle! + cycle = new DistanceBasedDrivingCycle(_container, data.Cycle); + } } // connect cycle --> driver --> vehicle --> wheels --> axleGear --> gearBox --> retarder --> clutch dynamic tmp = AddComponent(cycle, new Driver(_container, data.DriverData)); diff --git a/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs index 66fd5f7484..de622da14f 100644 --- a/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs +++ b/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs @@ -78,7 +78,14 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl var sumWriterDecorator = DecorateSumWriter(data.IsEngineOnly, SumWriter, data.JobFileName, jobName, data.Cycle.Name); var builder = new PowertrainBuilder(modWriter, sumWriterDecorator, DataReader.IsEngineOnly); - yield return new DistanceRun(builder.Build(data)); + VectoRun run; + if (data.IsEngineOnly) { + run = new TimeRun(builder.Build(data)); + } else { + run = new DistanceRun(builder.Build(data)); + } + + yield return run; } } diff --git a/VectoCore/Models/Simulation/Impl/TimeRun.cs b/VectoCore/Models/Simulation/Impl/TimeRun.cs new file mode 100644 index 0000000000..2404ffaa42 --- /dev/null +++ b/VectoCore/Models/Simulation/Impl/TimeRun.cs @@ -0,0 +1,30 @@ +using TUGraz.VectoCore.Models.Connector.Ports; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.Simulation.Impl +{ + public class TimeRun : VectoRun + { + public TimeRun(IVehicleContainer container) : base(container) {} + + protected override IResponse DoSimulationStep() + { + var dt = 1.SI<Second>(); + + var response = CyclePort.Request(AbsTime, dt); + + if (response is ResponseCycleFinished) { + return response; + } + + AbsTime = AbsTime + dt; + return response; + } + + protected override IResponse Initialize() + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/VectoCore/Models/Simulation/Impl/VectoRun.cs b/VectoCore/Models/Simulation/Impl/VectoRun.cs index 354c2d296b..ee2931f023 100644 --- a/VectoCore/Models/Simulation/Impl/VectoRun.cs +++ b/VectoCore/Models/Simulation/Impl/VectoRun.cs @@ -45,7 +45,9 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl do { response = DoSimulationStep(); - Container.CommitSimulationStep(AbsTime, dt); + if (response.ResponseType == ResponseType.Success) { + Container.CommitSimulationStep(AbsTime, dt); + } // set _dt to difference to next full second. AbsTime += dt; diff --git a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs index 0a2ea160d7..5127646e19 100644 --- a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs +++ b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs @@ -120,8 +120,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl } if (_dataWriter != null) { - _dataWriter[ModalResultField.time] = time; - _dataWriter[ModalResultField.simulationInterval] = simulationInterval; + _dataWriter[ModalResultField.time] = time.Value(); + _dataWriter[ModalResultField.simulationInterval] = simulationInterval.Value(); _dataWriter.CommitSimulationStep(); } } diff --git a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs index dab60bec06..63a2453586 100644 --- a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs +++ b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs @@ -15,10 +15,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data { public class CombustionEngineData : SimulationComponentData { - private readonly Dictionary<Range, FullLoadCurve> _fullLoadCurves = - new Dictionary<Range, FullLoadCurve>(); - - public string ModelName { get; internal set; } @@ -56,30 +52,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public FuelConsumptionMap ConsumptionMap { get; internal set; } - public FullLoadCurve GetFullLoadCurve(uint gear) - { - var curve = _fullLoadCurves.FirstOrDefault(kv => kv.Key.Contains(gear)); - if (curve.Key == null) { - throw new KeyNotFoundException(string.Format("GearData '{0}' was not found in the FullLoadCurves.", gear)); - } - - return curve.Value; - } - - internal void AddFullLoadCurve(string gears, FullLoadCurve fullLoadCurve) - { - var range = new Range(gears); - if (!_fullLoadCurves.ContainsKey(range)) { - fullLoadCurve.EngineData = this; - _fullLoadCurves.Add(range, fullLoadCurve); - } else { - throw new VectoException(String.Format("FullLoadCurve for gears {0} already specified!", gears)); - } - } + public EngineFullLoadCurve FullLoadCurve { get; internal set; } protected bool Equals(CombustionEngineData other) { - return Equals(_fullLoadCurves, other._fullLoadCurves) && string.Equals(ModelName, other.ModelName) && + return Equals(FullLoadCurve, other.FullLoadCurve) && string.Equals(ModelName, other.ModelName) && Equals(Displacement, other.Displacement) && Equals(IdleSpeed, other.IdleSpeed) && Equals(Inertia, other.Inertia) && Equals(WHTCUrban, other.WHTCUrban) && Equals(WHTCRural, other.WHTCRural) && Equals(WHTCMotorway, other.WHTCMotorway) && Equals(ConsumptionMap, other.ConsumptionMap); @@ -96,13 +73,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != this.GetType()) { return false; } - return Equals((CombustionEngineData) obj); + return Equals((CombustionEngineData)obj); } public override int GetHashCode() { unchecked { - var hashCode = (_fullLoadCurves != null ? _fullLoadCurves.GetHashCode() : 0); + var hashCode = (FullLoadCurve != null ? FullLoadCurve.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (ModelName != null ? ModelName.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (Displacement != null ? Displacement.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (IdleSpeed != null ? IdleSpeed.GetHashCode() : 0); @@ -120,18 +97,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { - return sourceType == typeof (string) || base.CanConvertFrom(context, sourceType); + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return value.GetType() == typeof (string) - ? new Range((string) value) + return value.GetType() == typeof(string) + ? new Range((string)value) : base.ConvertFrom(context, culture, value); } } - [TypeConverter(typeof (RangeConverter))] + [TypeConverter(typeof(RangeConverter))] private class Range { private readonly uint _end; @@ -174,13 +151,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((Range) obj); + return Equals((Range)obj); } public override int GetHashCode() { unchecked { - return (int) ((_start * 397) ^ _end); + return (int)((_start * 397) ^ _end); } } diff --git a/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs b/VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs similarity index 94% rename from VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs rename to VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs index 6f5e4a7e23..b729e81aa5 100644 --- a/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs +++ b/VectoCore/Models/SimulationComponent/Data/Engine/EngineFullLoadCurve.cs @@ -1,503 +1,503 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Diagnostics.Contracts; -using System.Linq; -using Common.Logging; -using Newtonsoft.Json; -using TUGraz.VectoCore.Exceptions; -using TUGraz.VectoCore.Models.Declaration; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine -{ - /// <summary> - /// Represents the Full load curve. - /// </summary> - public class FullLoadCurve : SimulationComponentData - { - private List<FullLoadCurveEntry> _fullLoadEntries; - private LookupData<PerSecond, Second> _pt1Data; - - private Watt _maxPower; - - private PerSecond _ratedSpeed; - private PerSecond _preferredSpeed; - private PerSecond _engineSpeedLo; // 55% of Pmax - private PerSecond _engineSpeedHi; // 70% of Pmax - private PerSecond _n95hSpeed; // 95% of Pmax - - public static FullLoadCurve ReadFromFile(string fileName, bool declarationMode = false) - { - var data = VectoCSVFile.Read(fileName); - - //todo Contract.Requires<VectoException>(data.Columns.Count != 4, "FullLoadCurve Data File must consist of 4 columns."); - if (data.Columns.Count < 3) { - throw new VectoException("FullLoadCurve Data File must consist of at least 3 columns."); - } - - //todo Contract.Requires<VectoException>(data.Rows.Count < 2, "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); - if (data.Rows.Count < 2) { - throw new VectoException( - "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); - } - - List<FullLoadCurveEntry> entriesFld; - if (HeaderIsValid(data.Columns)) { - entriesFld = CreateFromColumnNames(data); - } else { - var log = LogManager.GetLogger<FullLoadCurve>(); - log.WarnFormat( - "FullLoadCurve: Header Line is not valid. Expected: '{0}, {1}, {2}', Got: '{3}'. Falling back to column index.", - Fields.EngineSpeed, Fields.TorqueFullLoad, Fields.TorqueDrag, - string.Join(", ", data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Reverse())); - - entriesFld = CreateFromColumnIndizes(data); - } - - LookupData<PerSecond, Second> tmp; - if (declarationMode) { - tmp = new PT1(); - } else { - tmp = PT1Curve.ReadFromFile(fileName); - } - - return new FullLoadCurve { _fullLoadEntries = entriesFld, _pt1Data = tmp }; - } - - private static bool HeaderIsValid(DataColumnCollection columns) - { - Contract.Requires(columns != null); - return columns.Contains(Fields.EngineSpeed) - && columns.Contains(Fields.TorqueDrag) - && columns.Contains(Fields.TorqueFullLoad); - //&& columns.Contains(Fields.PT1); - } - - private static List<FullLoadCurveEntry> CreateFromColumnNames(DataTable data) - { - Contract.Requires(data != null); - return (from DataRow row in data.Rows - select new FullLoadCurveEntry { - EngineSpeed = row.ParseDouble(Fields.EngineSpeed).RPMtoRad(), - TorqueFullLoad = row.ParseDouble(Fields.TorqueFullLoad).SI<NewtonMeter>(), - TorqueDrag = row.ParseDouble(Fields.TorqueDrag).SI<NewtonMeter>(), - //PT1 = row.ParseDouble(Fields.PT1).SI<Second>() - }).ToList(); - } - - private static List<FullLoadCurveEntry> CreateFromColumnIndizes(DataTable data) - { - Contract.Requires(data != null); - return (from DataRow row in data.Rows - select new FullLoadCurveEntry { - EngineSpeed = row.ParseDouble(0).RPMtoRad(), - TorqueFullLoad = row.ParseDouble(1).SI<NewtonMeter>(), - TorqueDrag = row.ParseDouble(2).SI<NewtonMeter>(), - //PT1 = row.ParseDouble(3).SI<Second>() - }).ToList(); - } - - public CombustionEngineData EngineData { get; internal set; } - - /// <summary> - /// [rad/s] => [Nm] - /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <returns>[Nm]</returns> - public NewtonMeter FullLoadStationaryTorque(PerSecond angularVelocity) - { - var idx = FindIndex(angularVelocity); - return VectoMath.Interpolate(_fullLoadEntries[idx - 1].EngineSpeed, _fullLoadEntries[idx].EngineSpeed, - _fullLoadEntries[idx - 1].TorqueFullLoad, _fullLoadEntries[idx].TorqueFullLoad, - angularVelocity); - } - - /// <summary> - /// [rad/s] => [W] - /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <returns>[W]</returns> - public Watt FullLoadStationaryPower(PerSecond angularVelocity) - { - return Formulas.TorqueToPower(FullLoadStationaryTorque(angularVelocity), angularVelocity); - } - - /// <summary> - /// [rad/s] => [Nm] - /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <returns>[Nm]</returns> - public NewtonMeter DragLoadStationaryTorque(PerSecond angularVelocity) - { - var idx = FindIndex(angularVelocity); - return VectoMath.Interpolate(_fullLoadEntries[idx - 1].EngineSpeed, _fullLoadEntries[idx].EngineSpeed, - _fullLoadEntries[idx - 1].TorqueDrag, _fullLoadEntries[idx].TorqueDrag, - angularVelocity); - } - - /// <summary> - /// [rad/s] => [W]. - /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <returns>[W]</returns> - public Watt DragLoadStationaryPower(PerSecond angularVelocity) - { - Contract.Requires(angularVelocity.HasEqualUnit(new SI().Radian.Per.Second)); - Contract.Ensures(Contract.Result<SI>().HasEqualUnit(new SI().Watt)); - - return Formulas.TorqueToPower(DragLoadStationaryTorque(angularVelocity), angularVelocity); - } - - /// <summary> - /// [rad/s] => [s] - /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <returns>[s]</returns> - public Second PT1(PerSecond angularVelocity) - { - return _pt1Data.Lookup(angularVelocity); - } - - - /// <summary> - /// Get the engine's rated speed from the given full-load curve (i.e. engine speed with max. power) - /// </summary> - /// <returns>[1/s]</returns> - public PerSecond RatedSpeed - { - get - { - if (_ratedSpeed == null) { - ComputeRatedSpeed(); - } - return _ratedSpeed; - } - } - - public Watt MaxPower - { - get - { - if (_maxPower == null) { - ComputeRatedSpeed(); - } - return _maxPower; - } - } - - /// <summary> - /// Get the engine's preferred speed from the given full-load curve (i.e. Speed at 51% torque/speed-integral between idling and N95h.) - /// </summary> - public PerSecond PreferredSpeed - { - get - { - if (_preferredSpeed == null) { - ComputePreferredSpeed(); - } - return _preferredSpeed; - } - } - - public PerSecond N95hSpeed - { - get { return _n95hSpeed ?? (_n95hSpeed = FindEngineSpeedForPower(0.95 * MaxPower).Last()); } - } - - - public PerSecond LoSpeed - { - get { return _engineSpeedLo ?? (_engineSpeedLo = FindEngineSpeedForPower(0.55 * MaxPower).First()); } - } - - public PerSecond HiSpeed - { - get { return _engineSpeedHi ?? (_engineSpeedHi = FindEngineSpeedForPower(0.7 * MaxPower).Last()); } - } - - - public NewtonMeter MaxLoadTorque - { - get { return _fullLoadEntries.Max(x => x.TorqueFullLoad); } - } - - public NewtonMeter MaxDragTorque - { - get { return _fullLoadEntries.Min(x => x.TorqueDrag); } - } - - /// <summary> - /// Compute the engine's rated speed from the given full-load curve (i.e. engine speed with max. power) - /// </summary> - /// <returns>[1/s]</returns> - private void ComputeRatedSpeed() - { - var max = new Tuple<PerSecond, Watt>(0.SI<PerSecond>(), 0.SI<Watt>()); - for (var idx = 1; idx < _fullLoadEntries.Count; idx++) { - var currentMax = FindMaxPower(_fullLoadEntries[idx - 1], _fullLoadEntries[idx]); - if (currentMax.Item2 > max.Item2) { - max = currentMax; - } - } - - _ratedSpeed = max.Item1; - _maxPower = max.Item2; - } - - - private void ComputePreferredSpeed() - { - var maxArea = ComputeArea(EngineData.IdleSpeed, N95hSpeed); - - var area = 0.0; - var idx = 0; - while (++idx < _fullLoadEntries.Count) { - var additionalArea = ComputeArea(_fullLoadEntries[idx - 1].EngineSpeed, _fullLoadEntries[idx].EngineSpeed); - if (area + additionalArea > 0.51 * maxArea) { - var deltaArea = 0.51 * maxArea - area; - _preferredSpeed = ComputeEngineSpeedForSegmentArea(_fullLoadEntries[idx - 1], _fullLoadEntries[idx], deltaArea); - return; - } - area += additionalArea; - } - Log.WarnFormat("Could not compute preferred speed, check FullLoadCurve! N95h: {0}, maxArea: {1}", N95hSpeed, maxArea); - } - - private PerSecond ComputeEngineSpeedForSegmentArea(FullLoadCurveEntry p1, FullLoadCurveEntry p2, double area) - { - var k = (p2.TorqueFullLoad - p1.TorqueFullLoad) / (p2.EngineSpeed - p1.EngineSpeed); - var d = p2.TorqueFullLoad - k * p2.EngineSpeed; - - if (k.IsEqual(0.0)) { - // rectangle - // area = M * n - return (p1.EngineSpeed + (area / d.Value())); - } - - // non-constant torque, M(n) = k * n + d - // area = M(n1) * (n2 - n1) + (M(n1) + M(n2))/2 * (n2 - n1) => solve for n2 - var retVal = VectoMath.QuadraticEquationSolver(k.Value() / 2.0, d.Value(), - (k * p1.EngineSpeed * p1.EngineSpeed + 2 * p1.EngineSpeed * d).Value()); - if (retVal.Count == 0) { - Log.InfoFormat("No real solution found for requested area: P: {0}, p1: {1}, p2: {2}", area, p1, p2); - } - return retVal.First(x => x >= p1.EngineSpeed.Value() && x <= p2.EngineSpeed.Value()).SI<PerSecond>(); - } - - /// <summary> - /// [rad/s] => index. Get item index for angularVelocity. - /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <returns>index</returns> - protected int FindIndex(PerSecond angularVelocity) - { - int idx; - if (angularVelocity < _fullLoadEntries[0].EngineSpeed) { - Log.ErrorFormat("requested rpm below minimum rpm in FLD curve - extrapolating. n: {0}, rpm_min: {1}", - angularVelocity.ConvertTo().Rounds.Per.Minute, _fullLoadEntries[0].EngineSpeed.ConvertTo().Rounds.Per.Minute); - idx = 1; - } else { - idx = _fullLoadEntries.FindIndex(x => x.EngineSpeed > angularVelocity); - } - if (idx <= 0) { - idx = angularVelocity > _fullLoadEntries[0].EngineSpeed ? _fullLoadEntries.Count - 1 : 1; - } - return idx; - } - - private List<PerSecond> FindEngineSpeedForPower(Watt power) - { - var retVal = new List<PerSecond>(); - for (var idx = 1; idx < _fullLoadEntries.Count; idx++) { - var solutions = FindEngineSpeedForPower(_fullLoadEntries[idx - 1], _fullLoadEntries[idx], power); - retVal.AddRange(solutions); - } - retVal.Sort(); - return retVal; - } - - private List<PerSecond> FindEngineSpeedForPower(FullLoadCurveEntry p1, FullLoadCurveEntry p2, Watt power) - { - var k = (p2.TorqueFullLoad - p1.TorqueFullLoad) / (p2.EngineSpeed - p1.EngineSpeed); - var d = p2.TorqueFullLoad - k * p2.EngineSpeed; - - var retVal = new List<PerSecond>(); - if (k.IsEqual(0, 0.0001)) { - // constant torque, solve linear equation - // power = M * n - retVal.Add((power.Value() / d.Value()).SI<PerSecond>()); - } else { - // non-constant torque, solve quadratic equation for engine speed (n) - // power = M(n) * n = (k * n + d) * n = k * n^2 + d * n - retVal = VectoMath.QuadraticEquationSolver(k.Value(), d.Value(), -power.Value()).SI<PerSecond>().ToList(); - if (retVal.Count == 0) { - Log.InfoFormat("No real solution found for requested power demand: P: {0}, p1: {1}, p2: {2}", power, p1, p2); - } - } - retVal = retVal.Where(x => x >= p1.EngineSpeed && x <= p2.EngineSpeed).ToList(); - return retVal; - } - - private double ComputeArea(PerSecond lowEngineSpeed, PerSecond highEngineSpeed) - { - var startSegment = FindIndex(lowEngineSpeed); - var endSegment = FindIndex(highEngineSpeed); - - var area = 0.0; - if (lowEngineSpeed < _fullLoadEntries[startSegment].EngineSpeed) { - // add part of the first segment - area += ((_fullLoadEntries[startSegment].EngineSpeed - lowEngineSpeed) * - (FullLoadStationaryTorque(lowEngineSpeed) + _fullLoadEntries[startSegment].TorqueFullLoad) / 2.0).Value(); - } - for (var i = startSegment + 1; i <= endSegment; i++) { - var speedHigh = _fullLoadEntries[i].EngineSpeed; - var torqueHigh = _fullLoadEntries[i].TorqueFullLoad; - if (highEngineSpeed < _fullLoadEntries[i].EngineSpeed) { - // add part of the last segment - speedHigh = highEngineSpeed; - torqueHigh = FullLoadStationaryTorque(highEngineSpeed); - } - area += ((speedHigh - _fullLoadEntries[i - 1].EngineSpeed) * - (torqueHigh + _fullLoadEntries[i - 1].TorqueFullLoad) / 2.0).Value(); - } - return area; - } - - private Tuple<PerSecond, Watt> FindMaxPower(FullLoadCurveEntry p1, FullLoadCurveEntry p2) - { - if (p1.EngineSpeed == p2.EngineSpeed) { - return new Tuple<PerSecond, Watt>(p1.EngineSpeed, Formulas.TorqueToPower(p1.TorqueFullLoad, p1.EngineSpeed)); - } - if (p2.EngineSpeed < p1.EngineSpeed) { - var tmp = p1; - p1 = p2; - p2 = tmp; - } - // y = kx + d - var k = (p2.TorqueFullLoad - p1.TorqueFullLoad) / (p2.EngineSpeed - p1.EngineSpeed); - var d = p2.TorqueFullLoad - k * p2.EngineSpeed; - if (k == 0.SI()) { - return new Tuple<PerSecond, Watt>(p2.EngineSpeed, Formulas.TorqueToPower(p2.TorqueFullLoad, p2.EngineSpeed)); - } - var engineSpeedMaxPower = (-1 * d / (2 * k)).Cast<PerSecond>(); - if (engineSpeedMaxPower < p1.EngineSpeed || engineSpeedMaxPower > p2.EngineSpeed) { - if (k > 0) { - return new Tuple<PerSecond, Watt>(p2.EngineSpeed, Formulas.TorqueToPower(p2.TorqueFullLoad, p2.EngineSpeed)); - } - return new Tuple<PerSecond, Watt>(p1.EngineSpeed, Formulas.TorqueToPower(p1.TorqueFullLoad, p1.EngineSpeed)); - } - //return null; - var engineTorqueMaxPower = FullLoadStationaryTorque(engineSpeedMaxPower); - return new Tuple<PerSecond, Watt>(engineSpeedMaxPower, - Formulas.TorqueToPower(engineTorqueMaxPower, engineSpeedMaxPower)); - } - - private static class Fields - { - /// <summary> - /// [rpm] engine speed - /// </summary> - public const string EngineSpeed = "engine speed"; - - /// <summary> - /// [Nm] full load torque - /// </summary> - public const string TorqueFullLoad = "full load torque"; - - /// <summary> - /// [Nm] motoring torque - /// </summary> - public const string TorqueDrag = "motoring torque"; - } - - private class FullLoadCurveEntry - { - /// <summary> - /// [rad/s] engine speed - /// </summary> - public PerSecond EngineSpeed { get; set; } - - /// <summary> - /// [Nm] full load torque - /// </summary> - public NewtonMeter TorqueFullLoad { get; set; } - - /// <summary> - /// [Nm] motoring torque - /// </summary> - public NewtonMeter TorqueDrag { get; set; } - - ///// <summary> - ///// [s] PT1 time constant - ///// </summary> - //public Second PT1 { get; set; } - - #region Equality members - - protected bool Equals(FullLoadCurveEntry other) - { - return Equals(EngineSpeed, other.EngineSpeed) && Equals(TorqueFullLoad, other.TorqueFullLoad) && - Equals(TorqueDrag, other.TorqueDrag); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) { - return false; - } - if (ReferenceEquals(this, obj)) { - return true; - } - if (obj.GetType() != this.GetType()) { - return false; - } - return Equals((FullLoadCurveEntry)obj); - } - - public override int GetHashCode() - { - unchecked { - var hashCode = (EngineSpeed != null ? EngineSpeed.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (TorqueFullLoad != null ? TorqueFullLoad.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (TorqueDrag != null ? TorqueDrag.GetHashCode() : 0); - return hashCode; - } - } - - #endregion - } - - #region Equality members - - protected bool Equals(FullLoadCurve other) - { - return Equals(_fullLoadEntries, other._fullLoadEntries) && Equals(_pt1Data, other._pt1Data); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) { - return false; - } - if (ReferenceEquals(this, obj)) { - return true; - } - if (obj.GetType() != this.GetType()) { - return false; - } - return Equals((FullLoadCurve)obj); - } - - public override int GetHashCode() - { - unchecked { - return ((_fullLoadEntries != null ? _fullLoadEntries.GetHashCode() : 0) * 397) ^ - (_pt1Data != null ? _pt1Data.GetHashCode() : 0); - } - } - - #endregion - } +using System; +using System.Collections.Generic; +using System.Data; +using System.Diagnostics.Contracts; +using System.Linq; +using Common.Logging; +using Newtonsoft.Json; +using TUGraz.VectoCore.Exceptions; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine +{ + /// <summary> + /// Represents the Full load curve. + /// </summary> + public class EngineFullLoadCurve : SimulationComponentData + { + private List<FullLoadCurveEntry> _fullLoadEntries; + private LookupData<PerSecond, Second> _pt1Data; + + private Watt _maxPower; + + private PerSecond _ratedSpeed; + private PerSecond _preferredSpeed; + private PerSecond _engineSpeedLo; // 55% of Pmax + private PerSecond _engineSpeedHi; // 70% of Pmax + private PerSecond _n95hSpeed; // 95% of Pmax + + public static EngineFullLoadCurve ReadFromFile(string fileName, bool declarationMode = false) + { + var data = VectoCSVFile.Read(fileName); + + //todo Contract.Requires<VectoException>(data.Columns.Count != 4, "FullLoadCurve Data File must consist of 4 columns."); + if (data.Columns.Count < 3) { + throw new VectoException("FullLoadCurve Data File must consist of at least 3 columns."); + } + + //todo Contract.Requires<VectoException>(data.Rows.Count < 2, "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); + if (data.Rows.Count < 2) { + throw new VectoException( + "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); + } + + List<FullLoadCurveEntry> entriesFld; + if (HeaderIsValid(data.Columns)) { + entriesFld = CreateFromColumnNames(data); + } else { + var log = LogManager.GetLogger<EngineFullLoadCurve>(); + log.WarnFormat( + "FullLoadCurve: Header Line is not valid. Expected: '{0}, {1}, {2}', Got: '{3}'. Falling back to column index.", + Fields.EngineSpeed, Fields.TorqueFullLoad, Fields.TorqueDrag, + string.Join(", ", data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Reverse())); + + entriesFld = CreateFromColumnIndizes(data); + } + + LookupData<PerSecond, Second> tmp; + if (declarationMode) { + tmp = new PT1(); + } else { + tmp = PT1Curve.ReadFromFile(fileName); + } + + return new EngineFullLoadCurve { _fullLoadEntries = entriesFld, _pt1Data = tmp }; + } + + private static bool HeaderIsValid(DataColumnCollection columns) + { + Contract.Requires(columns != null); + return columns.Contains(Fields.EngineSpeed) + && columns.Contains(Fields.TorqueDrag) + && columns.Contains(Fields.TorqueFullLoad); + //&& columns.Contains(Fields.PT1); + } + + private static List<FullLoadCurveEntry> CreateFromColumnNames(DataTable data) + { + Contract.Requires(data != null); + return (from DataRow row in data.Rows + select new FullLoadCurveEntry { + EngineSpeed = row.ParseDouble(Fields.EngineSpeed).RPMtoRad(), + TorqueFullLoad = row.ParseDouble(Fields.TorqueFullLoad).SI<NewtonMeter>(), + TorqueDrag = row.ParseDouble(Fields.TorqueDrag).SI<NewtonMeter>(), + //PT1 = row.ParseDouble(Fields.PT1).SI<Second>() + }).ToList(); + } + + private static List<FullLoadCurveEntry> CreateFromColumnIndizes(DataTable data) + { + Contract.Requires(data != null); + return (from DataRow row in data.Rows + select new FullLoadCurveEntry { + EngineSpeed = row.ParseDouble(0).RPMtoRad(), + TorqueFullLoad = row.ParseDouble(1).SI<NewtonMeter>(), + TorqueDrag = row.ParseDouble(2).SI<NewtonMeter>(), + //PT1 = row.ParseDouble(3).SI<Second>() + }).ToList(); + } + + public CombustionEngineData EngineData { get; internal set; } + + /// <summary> + /// [rad/s] => [Nm] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[Nm]</returns> + public NewtonMeter FullLoadStationaryTorque(PerSecond angularVelocity) + { + var idx = FindIndex(angularVelocity); + return VectoMath.Interpolate(_fullLoadEntries[idx - 1].EngineSpeed, _fullLoadEntries[idx].EngineSpeed, + _fullLoadEntries[idx - 1].TorqueFullLoad, _fullLoadEntries[idx].TorqueFullLoad, + angularVelocity); + } + + /// <summary> + /// [rad/s] => [W] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[W]</returns> + public Watt FullLoadStationaryPower(PerSecond angularVelocity) + { + return Formulas.TorqueToPower(FullLoadStationaryTorque(angularVelocity), angularVelocity); + } + + /// <summary> + /// [rad/s] => [Nm] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[Nm]</returns> + public NewtonMeter DragLoadStationaryTorque(PerSecond angularVelocity) + { + var idx = FindIndex(angularVelocity); + return VectoMath.Interpolate(_fullLoadEntries[idx - 1].EngineSpeed, _fullLoadEntries[idx].EngineSpeed, + _fullLoadEntries[idx - 1].TorqueDrag, _fullLoadEntries[idx].TorqueDrag, + angularVelocity); + } + + /// <summary> + /// [rad/s] => [W]. + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[W]</returns> + public Watt DragLoadStationaryPower(PerSecond angularVelocity) + { + Contract.Requires(angularVelocity.HasEqualUnit(new SI().Radian.Per.Second)); + Contract.Ensures(Contract.Result<SI>().HasEqualUnit(new SI().Watt)); + + return Formulas.TorqueToPower(DragLoadStationaryTorque(angularVelocity), angularVelocity); + } + + /// <summary> + /// [rad/s] => [s] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[s]</returns> + public Second PT1(PerSecond angularVelocity) + { + return _pt1Data.Lookup(angularVelocity); + } + + + /// <summary> + /// Get the engine's rated speed from the given full-load curve (i.e. engine speed with max. power) + /// </summary> + /// <returns>[1/s]</returns> + public PerSecond RatedSpeed + { + get + { + if (_ratedSpeed == null) { + ComputeRatedSpeed(); + } + return _ratedSpeed; + } + } + + public Watt MaxPower + { + get + { + if (_maxPower == null) { + ComputeRatedSpeed(); + } + return _maxPower; + } + } + + /// <summary> + /// Get the engine's preferred speed from the given full-load curve (i.e. Speed at 51% torque/speed-integral between idling and N95h.) + /// </summary> + public PerSecond PreferredSpeed + { + get + { + if (_preferredSpeed == null) { + ComputePreferredSpeed(); + } + return _preferredSpeed; + } + } + + public PerSecond N95hSpeed + { + get { return _n95hSpeed ?? (_n95hSpeed = FindEngineSpeedForPower(0.95 * MaxPower).Last()); } + } + + + public PerSecond LoSpeed + { + get { return _engineSpeedLo ?? (_engineSpeedLo = FindEngineSpeedForPower(0.55 * MaxPower).First()); } + } + + public PerSecond HiSpeed + { + get { return _engineSpeedHi ?? (_engineSpeedHi = FindEngineSpeedForPower(0.7 * MaxPower).Last()); } + } + + + public NewtonMeter MaxLoadTorque + { + get { return _fullLoadEntries.Max(x => x.TorqueFullLoad); } + } + + public NewtonMeter MaxDragTorque + { + get { return _fullLoadEntries.Min(x => x.TorqueDrag); } + } + + /// <summary> + /// Compute the engine's rated speed from the given full-load curve (i.e. engine speed with max. power) + /// </summary> + /// <returns>[1/s]</returns> + private void ComputeRatedSpeed() + { + var max = new Tuple<PerSecond, Watt>(0.SI<PerSecond>(), 0.SI<Watt>()); + for (var idx = 1; idx < _fullLoadEntries.Count; idx++) { + var currentMax = FindMaxPower(_fullLoadEntries[idx - 1], _fullLoadEntries[idx]); + if (currentMax.Item2 > max.Item2) { + max = currentMax; + } + } + + _ratedSpeed = max.Item1; + _maxPower = max.Item2; + } + + + private void ComputePreferredSpeed() + { + var maxArea = ComputeArea(EngineData.IdleSpeed, N95hSpeed); + + var area = 0.0; + var idx = 0; + while (++idx < _fullLoadEntries.Count) { + var additionalArea = ComputeArea(_fullLoadEntries[idx - 1].EngineSpeed, _fullLoadEntries[idx].EngineSpeed); + if (area + additionalArea > 0.51 * maxArea) { + var deltaArea = 0.51 * maxArea - area; + _preferredSpeed = ComputeEngineSpeedForSegmentArea(_fullLoadEntries[idx - 1], _fullLoadEntries[idx], deltaArea); + return; + } + area += additionalArea; + } + Log.WarnFormat("Could not compute preferred speed, check FullLoadCurve! N95h: {0}, maxArea: {1}", N95hSpeed, maxArea); + } + + private PerSecond ComputeEngineSpeedForSegmentArea(FullLoadCurveEntry p1, FullLoadCurveEntry p2, double area) + { + var k = (p2.TorqueFullLoad - p1.TorqueFullLoad) / (p2.EngineSpeed - p1.EngineSpeed); + var d = p2.TorqueFullLoad - k * p2.EngineSpeed; + + if (k.IsEqual(0.0)) { + // rectangle + // area = M * n + return (p1.EngineSpeed + (area / d.Value())); + } + + // non-constant torque, M(n) = k * n + d + // area = M(n1) * (n2 - n1) + (M(n1) + M(n2))/2 * (n2 - n1) => solve for n2 + var retVal = VectoMath.QuadraticEquationSolver(k.Value() / 2.0, d.Value(), + (k * p1.EngineSpeed * p1.EngineSpeed + 2 * p1.EngineSpeed * d).Value()); + if (retVal.Count == 0) { + Log.InfoFormat("No real solution found for requested area: P: {0}, p1: {1}, p2: {2}", area, p1, p2); + } + return retVal.First(x => x >= p1.EngineSpeed.Value() && x <= p2.EngineSpeed.Value()).SI<PerSecond>(); + } + + /// <summary> + /// [rad/s] => index. Get item index for angularVelocity. + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>index</returns> + protected int FindIndex(PerSecond angularVelocity) + { + int idx; + if (angularVelocity < _fullLoadEntries[0].EngineSpeed) { + Log.ErrorFormat("requested rpm below minimum rpm in FLD curve - extrapolating. n: {0}, rpm_min: {1}", + angularVelocity.ConvertTo().Rounds.Per.Minute, _fullLoadEntries[0].EngineSpeed.ConvertTo().Rounds.Per.Minute); + idx = 1; + } else { + idx = _fullLoadEntries.FindIndex(x => x.EngineSpeed > angularVelocity); + } + if (idx <= 0) { + idx = angularVelocity > _fullLoadEntries[0].EngineSpeed ? _fullLoadEntries.Count - 1 : 1; + } + return idx; + } + + private List<PerSecond> FindEngineSpeedForPower(Watt power) + { + var retVal = new List<PerSecond>(); + for (var idx = 1; idx < _fullLoadEntries.Count; idx++) { + var solutions = FindEngineSpeedForPower(_fullLoadEntries[idx - 1], _fullLoadEntries[idx], power); + retVal.AddRange(solutions); + } + retVal.Sort(); + return retVal; + } + + private List<PerSecond> FindEngineSpeedForPower(FullLoadCurveEntry p1, FullLoadCurveEntry p2, Watt power) + { + var k = (p2.TorqueFullLoad - p1.TorqueFullLoad) / (p2.EngineSpeed - p1.EngineSpeed); + var d = p2.TorqueFullLoad - k * p2.EngineSpeed; + + var retVal = new List<PerSecond>(); + if (k.IsEqual(0, 0.0001)) { + // constant torque, solve linear equation + // power = M * n + retVal.Add((power.Value() / d.Value()).SI<PerSecond>()); + } else { + // non-constant torque, solve quadratic equation for engine speed (n) + // power = M(n) * n = (k * n + d) * n = k * n^2 + d * n + retVal = VectoMath.QuadraticEquationSolver(k.Value(), d.Value(), -power.Value()).SI<PerSecond>().ToList(); + if (retVal.Count == 0) { + Log.InfoFormat("No real solution found for requested power demand: P: {0}, p1: {1}, p2: {2}", power, p1, p2); + } + } + retVal = retVal.Where(x => x >= p1.EngineSpeed && x <= p2.EngineSpeed).ToList(); + return retVal; + } + + private double ComputeArea(PerSecond lowEngineSpeed, PerSecond highEngineSpeed) + { + var startSegment = FindIndex(lowEngineSpeed); + var endSegment = FindIndex(highEngineSpeed); + + var area = 0.0; + if (lowEngineSpeed < _fullLoadEntries[startSegment].EngineSpeed) { + // add part of the first segment + area += ((_fullLoadEntries[startSegment].EngineSpeed - lowEngineSpeed) * + (FullLoadStationaryTorque(lowEngineSpeed) + _fullLoadEntries[startSegment].TorqueFullLoad) / 2.0).Value(); + } + for (var i = startSegment + 1; i <= endSegment; i++) { + var speedHigh = _fullLoadEntries[i].EngineSpeed; + var torqueHigh = _fullLoadEntries[i].TorqueFullLoad; + if (highEngineSpeed < _fullLoadEntries[i].EngineSpeed) { + // add part of the last segment + speedHigh = highEngineSpeed; + torqueHigh = FullLoadStationaryTorque(highEngineSpeed); + } + area += ((speedHigh - _fullLoadEntries[i - 1].EngineSpeed) * + (torqueHigh + _fullLoadEntries[i - 1].TorqueFullLoad) / 2.0).Value(); + } + return area; + } + + private Tuple<PerSecond, Watt> FindMaxPower(FullLoadCurveEntry p1, FullLoadCurveEntry p2) + { + if (p1.EngineSpeed == p2.EngineSpeed) { + return new Tuple<PerSecond, Watt>(p1.EngineSpeed, Formulas.TorqueToPower(p1.TorqueFullLoad, p1.EngineSpeed)); + } + if (p2.EngineSpeed < p1.EngineSpeed) { + var tmp = p1; + p1 = p2; + p2 = tmp; + } + // y = kx + d + var k = (p2.TorqueFullLoad - p1.TorqueFullLoad) / (p2.EngineSpeed - p1.EngineSpeed); + var d = p2.TorqueFullLoad - k * p2.EngineSpeed; + if (k == 0.SI()) { + return new Tuple<PerSecond, Watt>(p2.EngineSpeed, Formulas.TorqueToPower(p2.TorqueFullLoad, p2.EngineSpeed)); + } + var engineSpeedMaxPower = (-1 * d / (2 * k)).Cast<PerSecond>(); + if (engineSpeedMaxPower < p1.EngineSpeed || engineSpeedMaxPower > p2.EngineSpeed) { + if (k > 0) { + return new Tuple<PerSecond, Watt>(p2.EngineSpeed, Formulas.TorqueToPower(p2.TorqueFullLoad, p2.EngineSpeed)); + } + return new Tuple<PerSecond, Watt>(p1.EngineSpeed, Formulas.TorqueToPower(p1.TorqueFullLoad, p1.EngineSpeed)); + } + //return null; + var engineTorqueMaxPower = FullLoadStationaryTorque(engineSpeedMaxPower); + return new Tuple<PerSecond, Watt>(engineSpeedMaxPower, + Formulas.TorqueToPower(engineTorqueMaxPower, engineSpeedMaxPower)); + } + + private static class Fields + { + /// <summary> + /// [rpm] engine speed + /// </summary> + public const string EngineSpeed = "engine speed"; + + /// <summary> + /// [Nm] full load torque + /// </summary> + public const string TorqueFullLoad = "full load torque"; + + /// <summary> + /// [Nm] motoring torque + /// </summary> + public const string TorqueDrag = "motoring torque"; + } + + private class FullLoadCurveEntry + { + /// <summary> + /// [rad/s] engine speed + /// </summary> + public PerSecond EngineSpeed { get; set; } + + /// <summary> + /// [Nm] full load torque + /// </summary> + public NewtonMeter TorqueFullLoad { get; set; } + + /// <summary> + /// [Nm] motoring torque + /// </summary> + public NewtonMeter TorqueDrag { get; set; } + + ///// <summary> + ///// [s] PT1 time constant + ///// </summary> + //public Second PT1 { get; set; } + + #region Equality members + + protected bool Equals(FullLoadCurveEntry other) + { + return Equals(EngineSpeed, other.EngineSpeed) && Equals(TorqueFullLoad, other.TorqueFullLoad) && + Equals(TorqueDrag, other.TorqueDrag); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + if (obj.GetType() != this.GetType()) { + return false; + } + return Equals((FullLoadCurveEntry)obj); + } + + public override int GetHashCode() + { + unchecked { + var hashCode = (EngineSpeed != null ? EngineSpeed.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (TorqueFullLoad != null ? TorqueFullLoad.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (TorqueDrag != null ? TorqueDrag.GetHashCode() : 0); + return hashCode; + } + } + + #endregion + } + + #region Equality members + + protected bool Equals(EngineFullLoadCurve other) + { + return Equals(_fullLoadEntries, other._fullLoadEntries) && Equals(_pt1Data, other._pt1Data); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + if (obj.GetType() != this.GetType()) { + return false; + } + return Equals((EngineFullLoadCurve)obj); + } + + public override int GetHashCode() + { + unchecked { + return ((_fullLoadEntries != null ? _fullLoadEntries.GetHashCode() : 0) * 397) ^ + (_pt1Data != null ? _pt1Data.GetHashCode() : 0); + } + } + + #endregion + } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/GearFullLoadCurve.cs b/VectoCore/Models/SimulationComponent/Data/GearFullLoadCurve.cs new file mode 100644 index 0000000000..5d5b61c961 --- /dev/null +++ b/VectoCore/Models/SimulationComponent/Data/GearFullLoadCurve.cs @@ -0,0 +1,10 @@ +namespace TUGraz.VectoCore.Models.SimulationComponent.Data +{ + public class GearFullLoadCurve + { + public static GearFullLoadCurve ReadFromFile(string file) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/Gearbox/GearData.cs b/VectoCore/Models/SimulationComponent/Data/Gearbox/GearData.cs index 8ed6c34922..e48fe2d1e0 100644 --- a/VectoCore/Models/SimulationComponent/Data/Gearbox/GearData.cs +++ b/VectoCore/Models/SimulationComponent/Data/Gearbox/GearData.cs @@ -2,23 +2,16 @@ { public class GearData { - public ShiftPolygon ShiftPolygon { get; protected set; } + public ShiftPolygon ShiftPolygon { get; internal set; } - public TransmissionLossMap LossMap { get; protected set; } + public TransmissionLossMap LossMap { get; internal set; } - public double Ratio { get; protected set; } + public GearFullLoadCurve FullLoadCurve { get; internal set; } - public bool TorqueConverterActive { get; protected set; } // TODO: think about refactoring... + public double Ratio { get; internal set; } - public double AverageEfficiency { get; set; } + public bool TorqueConverterActive { get; internal set; } // TODO: think about refactoring... - public GearData(TransmissionLossMap lossMap, ShiftPolygon shiftPolygon, double ratio, - bool torqueconverterActive) - { - LossMap = lossMap; - ShiftPolygon = shiftPolygon; - Ratio = ratio; - TorqueConverterActive = torqueconverterActive; - } + // public double AverageEfficiency { get; internal set; } } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/Clutch.cs b/VectoCore/Models/SimulationComponent/Impl/Clutch.cs index 1cd9c264c0..3b29a40f62 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Clutch.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Clutch.cs @@ -27,7 +27,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl : base(cockpit) { _idleSpeed = engineData.IdleSpeed; - _ratedSpeed = engineData.GetFullLoadCurve(0).RatedSpeed; + _ratedSpeed = engineData.FullLoadCurve.RatedSpeed; } public ClutchState State() diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 1aca930d9b..0494191925 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -88,12 +88,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl //todo: _currentState.EnginePowerLoss = enginePowerLoss; } - var currentGear = Cockpit.Gear(); - - _currentState.FullDragTorque = _data.GetFullLoadCurve(currentGear).DragLoadStationaryTorque(engineSpeed); + _currentState.FullDragTorque = _data.FullLoadCurve.DragLoadStationaryTorque(engineSpeed); _currentState.FullDragPower = Formulas.TorqueToPower(_currentState.FullDragTorque, engineSpeed); - ComputeFullLoadPower(currentGear, engineSpeed, dt); + ComputeFullLoadPower(engineSpeed, dt); ValidatePowerDemand(requestedEnginePower); @@ -115,22 +113,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected override void DoWriteModalResults(IModalDataWriter writer) { - writer[ModalResultField.PaEng] = (double)_currentState.EnginePowerLoss; - writer[ModalResultField.Pe_drag] = (double)_currentState.FullDragPower; - writer[ModalResultField.Pe_full] = (double)_currentState.DynamicFullLoadPower; - writer[ModalResultField.Pe_eng] = (double)_currentState.EnginePower; + writer[ModalResultField.PaEng] = _currentState.EnginePowerLoss.Value(); + writer[ModalResultField.Pe_drag] = _currentState.FullDragPower.Value(); + writer[ModalResultField.Pe_full] = _currentState.DynamicFullLoadPower.Value(); + writer[ModalResultField.Pe_eng] = _currentState.EnginePower.Value(); - writer[ModalResultField.Tq_drag] = (double)_currentState.FullDragTorque; - writer[ModalResultField.Tq_full] = (double)_currentState.DynamicFullLoadTorque; - writer[ModalResultField.Tq_eng] = (double)_currentState.EngineTorque; - writer[ModalResultField.n] = (double)_currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute; + writer[ModalResultField.Tq_drag] = _currentState.FullDragTorque.Value(); + writer[ModalResultField.Tq_full] = _currentState.DynamicFullLoadTorque.Value(); + writer[ModalResultField.Tq_eng] = _currentState.EngineTorque.Value(); + writer[ModalResultField.n] = _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute.Value(); try { writer[ModalResultField.FC] = - (double) - _data.ConsumptionMap.GetFuelConsumption(_currentState.EngineTorque, _currentState.EngineSpeed) - .ConvertTo() - .Gramm.Per.Hour; + _data.ConsumptionMap.GetFuelConsumption(_currentState.EngineTorque, _currentState.EngineSpeed) + .ConvertTo() + .Gramm.Per.Hour.Value(); } catch (VectoException ex) { Log.WarnFormat("t: {0} - {1} n: {2} Tq: {3}", _currentState.AbsTime, ex.Message, _currentState.EngineSpeed, _currentState.EngineTorque); @@ -223,10 +220,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// <summary> /// computes full load power from gear [-], angularVelocity [rad/s] and dt [s]. /// </summary> - /// <param name="gear"></param> /// <param name="angularVelocity">[rad/s]</param> /// <param name="dt">[s]</param> - protected void ComputeFullLoadPower(uint gear, PerSecond angularVelocity, Second dt) + protected void ComputeFullLoadPower(PerSecond angularVelocity, Second dt) { if (dt.IsEqual(0)) { throw new VectoException("ComputeFullLoadPower cannot compute for simulation interval length 0."); @@ -234,11 +230,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl //_currentState.StationaryFullLoadPower = _data.GetFullLoadCurve(gear).FullLoadStationaryPower(rpm); _currentState.StationaryFullLoadTorque = - _data.GetFullLoadCurve(gear).FullLoadStationaryTorque(angularVelocity); + _data.FullLoadCurve.FullLoadStationaryTorque(angularVelocity); _currentState.StationaryFullLoadPower = Formulas.TorqueToPower(_currentState.StationaryFullLoadTorque, angularVelocity); - double pt1 = _data.GetFullLoadCurve(gear).PT1(angularVelocity).Value(); + double pt1 = _data.FullLoadCurve.PT1(angularVelocity).Value(); // var dynFullPowerCalculated = (1 / (pt1 + 1)) * // (_currentState.StationaryFullLoadPower + pt1 * _previousState.EnginePower); diff --git a/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs b/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs index 212f545b5b..80e13b00e9 100644 --- a/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs +++ b/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs @@ -58,7 +58,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl // TODO!! var dx = 0.SI<Meter>(); - return _outPort.Request((Second)absTime, dx, Data.Entries[index].VehicleTargetSpeed, + return _outPort.Request(absTime, dt, Data.Entries[index].VehicleTargetSpeed, Data.Entries[index].RoadGradient); } @@ -86,10 +86,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected override void DoWriteModalResults(IModalDataWriter writer) { - throw new NotImplementedException(); + // TODO: write data... } - protected override void DoCommitSimulationStep() {} + protected override void DoCommitSimulationStep() + { + // TODO: commit step + } #endregion } diff --git a/VectoCore/VectoCore.csproj b/VectoCore/VectoCore.csproj index a9ffb8a21c..2e27b4c27e 100644 --- a/VectoCore/VectoCore.csproj +++ b/VectoCore/VectoCore.csproj @@ -151,8 +151,10 @@ <Compile Include="Models\Declaration\Mission.cs" /> <Compile Include="Models\Declaration\MissionType.cs" /> <Compile Include="Models\SimulationComponent\Data\Engine\PT1Curve.cs" /> + <Compile Include="Models\SimulationComponent\Data\GearFullLoadCurve.cs" /> <Compile Include="Models\Simulation\Impl\DistanceRun.cs" /> <Compile Include="Models\Simulation\Impl\PowertrainBuilder.cs" /> + <Compile Include="Models\Simulation\Impl\TimeRun.cs" /> <Compile Include="Utils\DataTableExtensionMethods.cs" /> <Compile Include="Utils\EnumHelper.cs" /> <Compile Include="Utils\RessourceHelper.cs" /> @@ -164,7 +166,7 @@ <Compile Include="Models\SimulationComponent\Data\CrossWindCorrectionMode.cs" /> <Compile Include="Models\SimulationComponent\Data\DrivingCycleData.cs" /> <Compile Include="Models\SimulationComponent\Data\Engine\FuelConsumptionMap.cs" /> - <Compile Include="Models\SimulationComponent\Data\Engine\FullLoadCurve.cs" /> + <Compile Include="Models\SimulationComponent\Data\Engine\EngineFullLoadCurve.cs" /> <Compile Include="Models\SimulationComponent\Data\IAuxiliaryCycleData.cs" /> <Compile Include="Models\SimulationComponent\Data\GearboxData.cs" /> <Compile Include="Models\SimulationComponent\Data\Gearbox\GearData.cs" /> diff --git a/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs b/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs index 25b0266dbd..68275acd63 100644 --- a/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs +++ b/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs @@ -151,7 +151,7 @@ namespace TUGraz.VectoCore.Tests.Models.Simulation while (cycleOut.Request(absTime, dt) is ResponseSuccess) { Assert.AreEqual(absTime, outPort.AbsTime); - Assert.AreEqual(dt, outPort.Ds); + Assert.AreEqual(dt, outPort.Dt); var time = absTime + dt / 2; var simulationInterval = dt; diff --git a/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs b/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs index 59edcaf7f4..b7b12bbdb9 100644 --- a/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs @@ -174,7 +174,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var i = 0; // dt = TimeSpan.FromSeconds(double.Parse(TestContext.DataRow["dt"].ToString(), CultureInfo.InvariantCulture)); // dt = TimeSpan.FromSeconds(expectedResults.Rows[i].ParseDouble(0)) - t; - var engineLoadPower = engineData.GetFullLoadCurve(0).FullLoadStationaryPower(angularSpeed); + var engineLoadPower = engineData.FullLoadCurve.FullLoadStationaryPower(angularSpeed); idlePower = Double.Parse(TestContext.DataRow["finalIdleLoad"].ToString()).SI<Watt>(); for (; t < 25; t += dt, i++) { dt = (expectedResults.Rows[i + 1].ParseDouble(0) - expectedResults.Rows[i].ParseDouble(0)).SI<Second>(); @@ -232,10 +232,6 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var idle = engineData.IdleSpeed; Assert.AreEqual(58.6430628670095, idle.Value(), 0.000001); Assert.IsTrue(idle.HasEqualUnit(0.SI<PerSecond>())); - - var flc0 = engineData.GetFullLoadCurve(0); - - AssertException<KeyNotFoundException>(() => { var flc10000 = engineData.GetFullLoadCurve(1000); }); } } } \ No newline at end of file diff --git a/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs b/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs index 79d610acec..a8adffc819 100644 --- a/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs +++ b/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs @@ -15,7 +15,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void TestFullLoadStaticTorque() { - var fldCurve = FullLoadCurve.ReadFromFile(CoachEngineFLD); + var fldCurve = EngineFullLoadCurve.ReadFromFile(CoachEngineFLD); Assert.AreEqual(1180, fldCurve.FullLoadStationaryTorque(560.RPMtoRad()).Value(), Tolerance); Assert.AreEqual(1352, fldCurve.FullLoadStationaryTorque(2000.RPMtoRad()).Value(), Tolerance); @@ -25,14 +25,14 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void TestFullLoadEngineSpeedRated() { - var fldCurve = FullLoadCurve.ReadFromFile(CoachEngineFLD); + var fldCurve = EngineFullLoadCurve.ReadFromFile(CoachEngineFLD); Assert.AreEqual(181.8444, (double)fldCurve.RatedSpeed, Tolerance); } [TestMethod] public void TestFullLoadStaticPower() { - var fldCurve = FullLoadCurve.ReadFromFile(CoachEngineFLD); + var fldCurve = EngineFullLoadCurve.ReadFromFile(CoachEngineFLD); Assert.AreEqual(69198.814183, fldCurve.FullLoadStationaryPower(560.RPMtoRad()).Value(), Tolerance); Assert.AreEqual(283162.218372, fldCurve.FullLoadStationaryPower(2000.RPMtoRad()).Value(), Tolerance); @@ -42,7 +42,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void TestDragLoadStaticTorque() { - var fldCurve = FullLoadCurve.ReadFromFile(CoachEngineFLD); + var fldCurve = EngineFullLoadCurve.ReadFromFile(CoachEngineFLD); Assert.AreEqual(-149, fldCurve.DragLoadStationaryTorque(560.RPMtoRad()).Value(), Tolerance); Assert.AreEqual(-301, fldCurve.DragLoadStationaryTorque(2000.RPMtoRad()).Value(), Tolerance); @@ -54,7 +54,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void TestDragLoadStaticPower() { - var fldCurve = FullLoadCurve.ReadFromFile(CoachEngineFLD); + var fldCurve = EngineFullLoadCurve.ReadFromFile(CoachEngineFLD); Assert.AreEqual(-8737.81636, fldCurve.DragLoadStationaryPower(560.RPMtoRad()).Value(), Tolerance); Assert.AreEqual(-63041.29254, fldCurve.DragLoadStationaryPower(2000.RPMtoRad()).Value(), Tolerance); @@ -64,7 +64,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void TestPT1() { - var fldCurve = FullLoadCurve.ReadFromFile(CoachEngineFLD); + var fldCurve = EngineFullLoadCurve.ReadFromFile(CoachEngineFLD); Assert.AreEqual(0.6, fldCurve.PT1(560.RPMtoRad()).Value(), Tolerance); Assert.AreEqual(0.25, fldCurve.PT1(2000.RPMtoRad()).Value(), Tolerance); @@ -78,7 +78,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData public void Test_FileRead_WrongFileFormat_InsufficientColumns() { AssertHelper.Exception<VectoException>( - () => FullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve insufficient columns.vfld"), + () => EngineFullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve insufficient columns.vfld"), "FullLoadCurve Data File must consist of at least 3 columns."); } @@ -88,7 +88,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void Test_FileRead_HeaderColumnsNotNamedCorrectly() { - FullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve wrong header.vfld"); + EngineFullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve wrong header.vfld"); //todo: check log file: ensure header warning was written! } @@ -98,7 +98,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData [TestMethod] public void Test_FileRead_NoHeader() { - var curve = FullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve no header.vfld"); + var curve = EngineFullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve no header.vfld"); var result = curve.FullLoadStationaryTorque(1.SI<PerSecond>()); Assert.AreNotEqual(result.Value(), 0.0); } @@ -110,7 +110,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData public void Test_FileRead_InsufficientEntries() { AssertHelper.Exception<VectoException>( - () => FullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve insufficient entries.vfld"), + () => EngineFullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve insufficient entries.vfld"), "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); } } diff --git a/VectoCoreTest/TestData/Components/12t Delivery Truck.veng b/VectoCoreTest/TestData/Components/12t Delivery Truck.veng index e4c8d2bde4..96d53aa5f1 100644 --- a/VectoCoreTest/TestData/Components/12t Delivery Truck.veng +++ b/VectoCoreTest/TestData/Components/12t Delivery Truck.veng @@ -1,25 +1,20 @@ { "Header": { - "CreatedBy": " ()", - "Date": "3/4/2015 12:31:04 PM", - "AppVersion": "2.0.4-beta3", - "FileVersion": 2 + "CreatedBy": "Raphael Luz IVT TU-Graz (14fea510-e457-4bf6-860f-a9514dc327f1)", + "Date": "25.06.2015 08:59:55", + "AppVersion": "2.2 beta", + "FileVersion": 3 }, "Body": { - "SavedInDeclMode": false, + "SavedInDeclMode": true, "ModelName": "Generic 12t Delivery Truck", "Displacement": 7700.0, "IdlingSpeed": 600.0, "Inertia": 3.789, - "FullLoadCurves": [ - { - "Path": "12t Delivery Truck.vfld", - "Gears": "0 - 99" - } - ], + "FullLoadCurve": "12t Delivery Truck.vfld", "FuelMap": "12t Delivery Truck.vmap", - "WHTC-Urban": 215.0, - "WHTC-Rural": 202.0, - "WHTC-Motorway": 204.0 + "WHTC-Urban": 0.99, + "WHTC-Rural": 0.99, + "WHTC-Motorway": 1.01 } } \ No newline at end of file diff --git a/VectoCoreTest/TestData/Components/12t Delivery Truck.vgbx b/VectoCoreTest/TestData/Components/12t Delivery Truck.vgbx index 71a6413e1f..922993ec9f 100644 --- a/VectoCoreTest/TestData/Components/12t Delivery Truck.vgbx +++ b/VectoCoreTest/TestData/Components/12t Delivery Truck.vgbx @@ -1,9 +1,9 @@ { "Header": { - "CreatedBy": "Raphael Luz IVT TU-Graz (85407225-fc3f-48a8-acda-c84a05df6837)", - "Date": "29.07.2014 16:58:58", - "AppVersion": "2.0.4-beta", - "FileVersion": 4 + "CreatedBy": "Raphael Luz IVT TU-Graz (14fea510-e457-4bf6-860f-a9514dc327f1)", + "Date": "24.06.2015 09:33:31", + "AppVersion": "2.2 beta", + "FileVersion": 5 }, "Body": { "SavedInDeclMode": true, @@ -19,37 +19,43 @@ "Ratio": 6.75, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "-" + "ShiftPolygon": "-", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 3.6, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "-" + "ShiftPolygon": "-", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 2.13, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "-" + "ShiftPolygon": "-", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 1.39, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "-" + "ShiftPolygon": "-", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 1.0, "LossMap": "Direct Gear.vtlm", "TCactive": false, - "ShiftPolygon": "-" + "ShiftPolygon": "-", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 0.78, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "-" + "ShiftPolygon": "-", + "FullLoadCurve": "<NOFILE>" } ], "TqReserve": 20.0, diff --git a/VectoCoreTest/TestData/Components/24t Coach.veng b/VectoCoreTest/TestData/Components/24t Coach.veng index b113b3cbfc..d93e66348b 100644 --- a/VectoCoreTest/TestData/Components/24t Coach.veng +++ b/VectoCoreTest/TestData/Components/24t Coach.veng @@ -1,9 +1,9 @@ { "Header": { - "CreatedBy": " ()", - "Date": "3/4/2015 12:26:24 PM", - "AppVersion": "2.0.4-beta3", - "FileVersion": 2 + "CreatedBy": "Raphael Luz IVT TU-Graz (14fea510-e457-4bf6-860f-a9514dc327f1)", + "Date": "25.06.2015 11:23:22", + "AppVersion": "2.2 beta", + "FileVersion": 3 }, "Body": { "SavedInDeclMode": false, @@ -11,12 +11,7 @@ "Displacement": 12730.0, "IdlingSpeed": 560.0, "Inertia": 3.8, - "FullLoadCurves": [ - { - "Path": "24t Coach.vfld", - "Gears": "0 - 99" - } - ], + "FullLoadCurve": "24t Coach.vfld", "FuelMap": "24t Coach.vmap", "WHTC-Urban": 0.0, "WHTC-Rural": 0.0, diff --git a/VectoCoreTest/TestData/Components/24t Coach.vgbx b/VectoCoreTest/TestData/Components/24t Coach.vgbx index 7d9bfab465..88dc64fa30 100644 --- a/VectoCoreTest/TestData/Components/24t Coach.vgbx +++ b/VectoCoreTest/TestData/Components/24t Coach.vgbx @@ -1,9 +1,9 @@ { "Header": { - "CreatedBy": "Raphael Luz IVT TU-Graz (85407225-fc3f-48a8-acda-c84a05df6837)", - "Date": "3/4/2015 12:26:24 PM", - "AppVersion": "2.0.4-beta", - "FileVersion": 4 + "CreatedBy": "Raphael Luz IVT TU-Graz (14fea510-e457-4bf6-860f-a9514dc327f1)", + "Date": "25.06.2015 11:23:31", + "AppVersion": "2.2 beta", + "FileVersion": 5 }, "Body": { "SavedInDeclMode": false, @@ -19,49 +19,57 @@ "Ratio": 6.38, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 4.63, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 3.44, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 2.59, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 1.86, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 1.35, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 1.0, "LossMap": "Direct Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" }, { "Ratio": 0.76, "LossMap": "Indirect Gear.vtlm", "TCactive": false, - "ShiftPolygon": "ShiftPolygons.vgbs" + "ShiftPolygon": "ShiftPolygons.vgbs", + "FullLoadCurve": "<NOFILE>" } ], "TqReserve": 20.0, -- GitLab