From 3579ba08b388933729eef678be5c737edbe19599 Mon Sep 17 00:00:00 2001 From: Markus Quaritsch <markus.quaritsch@tugraz.at> Date: Fri, 3 Nov 2017 12:34:52 +0100 Subject: [PATCH] started working on EPTP powertrain creation --- VECTO.sln.DotSettings | 1 + .../Reader/DrivingCycleDataReader.cs | 61 ++- .../EngineeringEPTPModeVectoRunDataFactory.cs | 78 ++++ .../Simulation/Impl/PowertrainBuilder.cs | 39 +- .../Simulation/Impl/SimulatorFactory.cs | 35 +- .../Data/DrivingCycleData.cs | 415 +++++++++--------- .../Impl/EngineAuxiliary.cs | 318 +++++++------- VectoCore/VectoCore/VectoCore.csproj | 1 + .../Integration/EPTP/EPTPTest.cs | 37 ++ VectoCore/VectoCoreTest/VectoCoreTest.csproj | 1 + 10 files changed, 602 insertions(+), 384 deletions(-) create mode 100644 VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs create mode 100644 VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs diff --git a/VECTO.sln.DotSettings b/VECTO.sln.DotSettings index 9ba0cdb648..1cd409260f 100644 --- a/VECTO.sln.DotSettings +++ b/VECTO.sln.DotSettings @@ -24,6 +24,7 @@ <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AMT/@EntryIndexedValue">AMT</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AT/@EntryIndexedValue">AT</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CSV/@EntryIndexedValue">CSV</s:String> + <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EPTP/@EntryIndexedValue">EPTP</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HVAC/@EntryIndexedValue">HVAC</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSON/@EntryIndexedValue">JSON</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MT/@EntryIndexedValue">MT</s:String> diff --git a/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs b/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs index ba0e80a3f8..a1722ff80c 100644 --- a/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs +++ b/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs @@ -74,6 +74,9 @@ namespace TUGraz.VectoCore.InputData.Reader if (DistanceBasedCycleDataParser.ValidateHeader(cols, false)) { return CycleType.DistanceBased; } + if (EPTPCycleDataParser.ValidateHeader(cols, false)) { + return CycleType.EPTP; + } throw new VectoException("CycleFile format is unknown."); } @@ -92,7 +95,10 @@ namespace TUGraz.VectoCore.InputData.Reader return new MeasuredSpeedDataParser(); case CycleType.PTO: return new PTOCycleDataParser(); - default: + case CycleType.EPTP: + return new EPTPCycleDataParser(); + + default: throw new ArgumentOutOfRangeException("Cycle Type", type.ToString()); } } @@ -311,6 +317,8 @@ namespace TUGraz.VectoCore.InputData.Reader public const string RoadGradient = "grad"; public const string StoppingTime = "stop"; public const string EngineSpeed = "n"; + public const string EngineSpeedSuffix = "n_eng"; + public const string FanSpeed = "n_fan"; public const string Gear = "gear"; public const string AdditionalAuxPowerDemand = "Padd"; public const string AirSpeedRelativeToVehicle = "vair_res"; @@ -698,7 +706,56 @@ namespace TUGraz.VectoCore.InputData.Reader return CheckColumns(header, allowedCols, requiredCols, throwExceptions, allowAux); } } - } + + /// <summary> + /// Parser for PTO Cycles. + /// </summary> + // <t>,<v> [km/h],<Pwheel> [kW],<n_eng> [rpm],<n_fan> [rpm], <Padd> [kW] + private class EPTPCycleDataParser : AbstractCycleDataParser + { + public override IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table, bool crossWindRequired) + { + ValidateHeader(table.Columns); + + var entries = table.Rows.Cast<DataRow>().Select(row => new DrivingCycleData.DrivingCycleEntry { + Time = row.ParseDouble(Fields.Time).SI<Second>(), + VehicleTargetSpeed = row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(), + AdditionalAuxPowerDemand = row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(), + PWheel = row.ParseDouble(Fields.PWheel).SI().Kilo.Watt.Cast<Watt>(), + EngineSpeed = row.ParseDouble(Fields.EngineSpeedSuffix).RPMtoRad(), + FanSpeed = row.ParseDouble(Fields.FanSpeed).RPMtoRad() + }).ToArray(); + + return entries; + } + + public static bool ValidateHeader(DataColumnCollection header, bool throwExceptions = true) + { + var requiredCols = new[] { + Fields.Time, + Fields.VehicleSpeed, + Fields.PWheel, + Fields.EngineSpeedSuffix, + Fields.FanSpeed + }; + + var allowedCols = new[] { + Fields.Time, + Fields.VehicleSpeed, + Fields.AdditionalAuxPowerDemand, + Fields.PWheel, + Fields.EngineSpeedSuffix, + Fields.FanSpeed + }; + + const bool allowAux = true; + + return CheckColumns(header, allowedCols, requiredCols, throwExceptions, allowAux) && + CheckComboColumns(header, new[] { Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle }, throwExceptions); + } + } + + } #endregion } \ No newline at end of file diff --git a/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs b/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs new file mode 100644 index 0000000000..bf955d0242 --- /dev/null +++ b/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TUGraz.VectoCommon.InputData; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.InputData.Reader.ComponentData; +using TUGraz.VectoCore.InputData.Reader.DataObjectAdapter; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.Simulation.Data; + +namespace TUGraz.VectoCore.InputData.Reader.Impl { + internal class EngineeringEPTPModeVectoRunDataFactory : IVectoRunDataFactory + { + protected IEPTPInputDataProvider InputDataProvider; + + public EngineeringEPTPModeVectoRunDataFactory(IEPTPInputDataProvider eptpProvider) + { + InputDataProvider = eptpProvider; + } + + public IEnumerable<VectoRunData> NextRun() + { + var dao = new DeclarationDataAdapter(); + var segment = DeclarationData.Segments.Lookup(InputDataProvider.JobInputData.Vehicle.VehicleCategory, + InputDataProvider.JobInputData.Vehicle.AxleConfiguration, + InputDataProvider.JobInputData.Vehicle.GrossVehicleMassRating, + InputDataProvider.JobInputData.Vehicle.CurbMassChassis); + var driverdata = dao.CreateDriverData(); + driverdata.AccelerationCurve = AccelerationCurveReader.ReadFromStream(segment.AccelerationFile); + var tempVehicle = dao.CreateVehicleData(InputDataProvider.JobInputData.Vehicle, segment.Missions.First(), + segment.Missions.First().Loadings.First().Value, segment.MunicipalBodyWeight); + var airdragData = dao.CreateAirdragData(InputDataProvider.JobInputData.Vehicle.AirdragInputData, + segment.Missions.First(), segment); + var engineData = dao.CreateEngineData(InputDataProvider.JobInputData.Vehicle.EngineInputData, + InputDataProvider.JobInputData.Vehicle.EngineIdleSpeed, + InputDataProvider.JobInputData.Vehicle.GearboxInputData, InputDataProvider.JobInputData.Vehicle.TorqueLimits); + var axlegearData = dao.CreateAxleGearData(InputDataProvider.JobInputData.Vehicle.AxleGearInputData, false); + var angledriveData = dao.CreateAngledriveData(InputDataProvider.JobInputData.Vehicle.AngledriveInputData, false); + var gearboxData = dao.CreateGearboxData(InputDataProvider.JobInputData.Vehicle.GearboxInputData, engineData, + axlegearData.AxleGear.Ratio, + tempVehicle.DynamicTyreRadius, tempVehicle.VehicleCategory, false); + var retarderData = dao.CreateRetarderData(InputDataProvider.JobInputData.Vehicle.RetarderInputData); + + var ptoTransmissionData = dao.CreatePTOTransmissionData(InputDataProvider.JobInputData.Vehicle.PTOTransmissionInputData); + + + var aux = dao.CreateAuxiliaryData(InputDataProvider.JobInputData.Vehicle.AuxiliaryInputData(), MissionType.RegionalDelivery, segment.VehicleClass).ToList(); + aux.RemoveAll(x => x.ID == Constants.Auxiliaries.IDs.Fan); + aux.Add(new VectoRunData.AuxData { + DemandType = AuxiliaryDemandType.Direct, + ID = DrivingCycleDataReader.Fields.AdditionalAuxPowerDemand + }); + + return InputDataProvider.JobInputData.Cycles.Select(cycle => { + var drivingCycle = DrivingCycleDataReader.ReadFromDataTable(cycle.CycleData, cycle.Name, false); + return new VectoRunData { + JobName = InputDataProvider.JobInputData.Vehicle.VIN, + EngineData = engineData, + GearboxData = gearboxData, + AxleGearData = axlegearData, + AngledriveData = angledriveData, + VehicleData = dao.CreateVehicleData(InputDataProvider.JobInputData.Vehicle, segment.Missions.First(), + 0.SI<Kilogram>(), segment.MunicipalBodyWeight), + AirdragData =airdragData, + DriverData = null, + Aux = aux, + AdvancedAux = null, + Retarder = dao.CreateRetarderData(InputDataProvider.JobInputData.Vehicle.RetarderInputData), + PTO = ptoTransmissionData, + Cycle = new DrivingCycleProxy(drivingCycle, cycle.Name), + ExecutionMode = ExecutionMode.Engineering + }; + }); + } + } +} \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs index f677315e4e..88a5c904a9 100644 --- a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs +++ b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs @@ -33,6 +33,7 @@ using System; using System.Linq; using TUGraz.VectoCommon.Exceptions; using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Models.Declaration; using TUGraz.VectoCore.Models.Simulation.Data; @@ -69,6 +70,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl return BuildEngineOnly(data); case CycleType.PWheel: return BuildPWheel(data); + case CycleType.EPTP: + return BuildEPTP(data); case CycleType.MeasuredSpeed: return BuildMeasuredSpeed(data); case CycleType.MeasuredSpeedGear: @@ -124,7 +127,40 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl return container; } - private VehicleContainer BuildMeasuredSpeed(VectoRunData data) + private VehicleContainer BuildEPTP(VectoRunData data) + { + if (data.Cycle.CycleType != CycleType.PWheel) { + throw new VectoException("CycleType must be PWheel."); + } + + var container = new VehicleContainer(ExecutionMode.Engineering, _modData, _sumWriter) { RunData = data }; + var gearbox = new CycleGearbox(container, data); + + // PWheelCycle --> AxleGear --> Clutch --> Engine <-- Aux + var powertrain = new EPTPCycle(container, data.Cycle, data.AxleGearData.AxleGear.Ratio, data.VehicleData, + gearbox.ModelData.Gears.ToDictionary(g => g.Key, g => g.Value.Ratio)) + .AddComponent(new AxleGear(container, data.AxleGearData)) + .AddComponent(data.AngledriveData != null ? new Angledrive(container, data.AngledriveData) : null) + .AddComponent(gearbox, data.Retarder, container) + .AddComponent(new Clutch(container, data.EngineData)); + var engine = new CombustionEngine(container, data.EngineData, pt1Disabled: true); + + var aux = CreateAuxiliaries(data, container); + aux.AddCycle(Constants.Auxiliaries.IDs.Fan, cycleEntry => { + var fanSpeed = cycleEntry.FanSpeed.AsRPM; + return (c1 * Math.Pow(fanSpeed / c2, 3) * Math.Pow(fanSpeed / c3, 5)*1000).SI<Watt>(); + }); + + engine.Connect(aux.Port()); + var idleController = GetIdleController(data.PTO, engine, container); + + powertrain.AddComponent(engine, idleController); + //.AddAuxiliaries(container, data); + + return container; + } + + private VehicleContainer BuildMeasuredSpeed(VectoRunData data) { if (data.Cycle.CycleType != CycleType.MeasuredSpeed) { throw new VectoException("CycleType must be MeasuredSpeed."); @@ -272,7 +308,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl container.ModalData.AddAuxiliary(Constants.Auxiliaries.IDs.PTOConsumer, Constants.Auxiliaries.PowerPrefix + Constants.Auxiliaries.IDs.PTOConsumer); } - return aux; } diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs index fe23cb39d8..be8a7420b9 100644 --- a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs +++ b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs @@ -89,14 +89,23 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl private void CreateEngineeringDataReader(IInputDataProvider dataProvider) { - var engDataProvider = ToEngineeringInputDataProvider(dataProvider); - if (engDataProvider.JobInputData.EngineOnlyMode) { - DataReader = new EngineOnlyVectoRunDataFactory(engDataProvider); - _engineOnlyMode = true; - } else { - DataReader = new EngineeringModeVectoRunDataFactory(engDataProvider); - } - } + if (dataProvider is IEPTPInputDataProvider) { + var eptpProvider = dataProvider as IEPTPInputDataProvider; + DataReader = new EngineeringEPTPModeVectoRunDataFactory(eptpProvider); + return; + } + if (dataProvider is IEngineeringInputDataProvider) { + var engDataProvider = dataProvider as IEngineeringInputDataProvider; + if (engDataProvider.JobInputData.EngineOnlyMode) { + DataReader = new EngineOnlyVectoRunDataFactory(engDataProvider); + _engineOnlyMode = true; + } else { + DataReader = new EngineeringModeVectoRunDataFactory(engDataProvider); + } + return; + } + throw new VectoException("Unknown InputData for Engineering Mode!"); + } private static IDeclarationInputDataProvider ToDeclarationInputDataProvider(IInputDataProvider dataProvider) { @@ -107,15 +116,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl return declDataProvider; } - private static IEngineeringInputDataProvider ToEngineeringInputDataProvider(IInputDataProvider dataProvider) - { - var engDataProvider = dataProvider as IEngineeringInputDataProvider; - if (engDataProvider == null) { - throw new VectoException("InputDataProvider does not implement Engineering interface"); - } - return engDataProvider; - } - public bool Validate { get; set; } public IVectoRunDataFactory DataReader { get; private set; } @@ -203,6 +203,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl break; case CycleType.EngineOnly: case CycleType.PWheel: + case CycleType.EPTP: case CycleType.MeasuredSpeed: case CycleType.MeasuredSpeedGear: run = new TimeRun(builder.Build(data)); diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs index 97df6a5a1d..e9ea12ac4d 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs @@ -29,208 +29,215 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; -using System.Linq; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Data -{ - public enum CycleType - { - EngineOnly, - DistanceBased, - PWheel, - MeasuredSpeed, - MeasuredSpeedGear, - PTO - } - - public static class CycleTypeHelper - { - public static bool IsDistanceBased(this CycleType type) - { - return type == CycleType.DistanceBased; - } - } - - public interface IDrivingCycleData - { - List<DrivingCycleData.DrivingCycleEntry> Entries { get; } - string Name { get; } - CycleType CycleType { get; } - void Finish(); - } - - [CustomValidation(typeof(DrivingCycleData), "ValidateCycleData")] - public class DrivingCycleData : SimulationComponentData, IDrivingCycleData - { - internal DrivingCycleData() {} - - public List<DrivingCycleEntry> Entries { get; internal set; } - - public string Name { get; internal set; } - - public CycleType CycleType { get; internal set; } - - public void Finish() {} - - // ReSharper disable once UnusedMember.Global -- used by Validation - public static ValidationResult ValidateCycleData(DrivingCycleData cycleData, ValidationContext validationContext) - { - var mode = GetExecutionMode(validationContext); - if (mode == ExecutionMode.Declaration) { - return ValidationResult.Success; - } - - var result = new List<string>(); - if (cycleData.CycleType.IsDistanceBased()) { - var cur = cycleData.Entries[0].Distance; - for (var i = 1; i < cycleData.Entries.Count; i++) { - if (cycleData.Entries[i].Distance < cur) { - result.Add( - string.Format("distance-based cycle is not increasing strictly monotonous. entry: {0}, s_{1}: {2} s_{0}: {3}", i, - i - 1, cycleData.Entries[i - 1].Distance, cycleData.Entries[i].Distance)); - } - cur = cycleData.Entries[i].Distance; - } - } else { - var cur = cycleData.Entries[0].Time; - for (var i = 1; i < cycleData.Entries.Count; i++) { - if (cycleData.Entries[i].Time < cur) { - result.Add( - string.Format("time-based cycle is not increasing strictly monotonous. entry: {0}, t_{1}: {2} t_{0}: {3}", i, - i - 1, cycleData.Entries[i - 1].Time, cycleData.Entries[i].Time)); - } - cur = cycleData.Entries[i].Time; - } - } - if (result.Any()) { - return new ValidationResult(string.Format("Validation of Cycle {0} failed", cycleData.Name), result); - } - return ValidationResult.Success; - } - - [DebuggerDisplay( - "s:{Distance}, t:{Time}, v:{VehicleTargetSpeed}, grad:{RoadGradient}, n:{AngularVelocity}, gear:{Gear}")] - public class DrivingCycleEntry - { - public DrivingCycleEntry() {} - - public DrivingCycleEntry(DrivingCycleEntry entry) - { - Distance = entry.Distance; - Time = entry.Time; - VehicleTargetSpeed = entry.VehicleTargetSpeed; - RoadGradient = entry.RoadGradient; - Altitude = entry.Altitude; - StoppingTime = entry.StoppingTime; - AngularVelocity = entry.AngularVelocity; - Gear = entry.Gear; - AdditionalAuxPowerDemand = entry.AdditionalAuxPowerDemand; - AirSpeedRelativeToVehicle = entry.AirSpeedRelativeToVehicle; - WindYawAngle = entry.WindYawAngle; - Torque = entry.Torque; - Drag = entry.Drag; - PTOActive = entry.PTOActive; - AuxiliarySupplyPower = new Dictionary<string, Watt>(entry.AuxiliarySupplyPower); - } - - /// <summary> - /// Travelled distance used for distance-based cycles. If "t" is also defined this column will be ignored. - /// </summary> - public Meter Distance; - - /// <summary> - /// Used for time-based cycles. If neither this nor the distance. "s" is defined the data will be interpreted as 1Hz. - /// </summary> - public Second Time; - - /// <summary> - /// Required except for Engine Only Mode calculations. - /// </summary> - public MeterPerSecond VehicleTargetSpeed; - - /// <summary> - /// Optional. - /// </summary> - public Radian RoadGradient; - - /// <summary> - /// [%] Optional. - /// </summary> - public Scalar RoadGradientPercent - { - get { return (Math.Tan(RoadGradient.Value()) * 100).SI<Scalar>(); } - } - - /// <summary> - /// relative altitude of the driving cycle over distance - /// </summary> - public Meter Altitude; - - /// <summary> - /// Required for distance-based cycles. Not used in time based cycles. "stop" defines the time the vehicle spends in stop phases. - /// </summary> - public Second StoppingTime; - - /// <summary> - /// Supply Power input for each auxiliary defined in the .vecto file where xxx matches the ID of the corresponding - /// Auxiliary. ID's are not case sensitive and must not contain space or special characters. - /// </summary> - public Dictionary<string, Watt> AuxiliarySupplyPower; - - /// <summary> - /// If "n_eng_avg" is defined VECTO uses that instead of the calculated engine speed value. - /// </summary> - public PerSecond AngularVelocity; - - /// <summary> - /// [-] Gear input. Overwrites the gear shift model. - /// </summary> - public uint Gear; - - /// <summary> - /// This power input will be directly added to the engine power in addition to possible other auxiliaries. Also used in Engine Only Mode. - /// </summary> - public Watt AdditionalAuxPowerDemand; - - /// <summary> - /// Only required if Cross Wind Correction is set to Vair and Beta Input. - /// </summary> - public MeterPerSecond AirSpeedRelativeToVehicle; - - /// <summary> - /// [°] Only required if Cross Wind Correction is set to Vair and Beta Input. - /// </summary> - public double WindYawAngle; - - /// <summary> - /// Effective engine torque at clutch. Only required in Engine Only Mode. Alternatively power "Pe" can be defined. Use "DRAG" to define motoring operation. - /// </summary> - public NewtonMeter Torque; - - public bool Drag; - - /// <summary> - /// Power on the Wheels (only used in PWheel Mode). - /// </summary> - public Watt PWheel; - - public bool? TorqueConverterActive { get; set; } - - /// <summary> - /// The angular velocity at the wheel. only used in PWheelCycle. - /// </summary> - public PerSecond WheelAngularVelocity; - - /// <summary> - /// Flag if PTO Cycle is active or not. - /// </summary> - public bool PTOActive; - } - } +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Linq; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data +{ + public enum CycleType + { + EngineOnly, + DistanceBased, + PWheel, + MeasuredSpeed, + MeasuredSpeedGear, + PTO, + EPTP + } + + public static class CycleTypeHelper + { + public static bool IsDistanceBased(this CycleType type) + { + return type == CycleType.DistanceBased; + } + } + + public interface IDrivingCycleData + { + List<DrivingCycleData.DrivingCycleEntry> Entries { get; } + string Name { get; } + CycleType CycleType { get; } + void Finish(); + } + + [CustomValidation(typeof(DrivingCycleData), "ValidateCycleData")] + public class DrivingCycleData : SimulationComponentData, IDrivingCycleData + { + internal DrivingCycleData() {} + + public List<DrivingCycleEntry> Entries { get; internal set; } + + public string Name { get; internal set; } + + public CycleType CycleType { get; internal set; } + + public void Finish() {} + + // ReSharper disable once UnusedMember.Global -- used by Validation + public static ValidationResult ValidateCycleData(DrivingCycleData cycleData, ValidationContext validationContext) + { + var mode = GetExecutionMode(validationContext); + if (mode == ExecutionMode.Declaration) { + return ValidationResult.Success; + } + + var result = new List<string>(); + if (cycleData.CycleType.IsDistanceBased()) { + var cur = cycleData.Entries[0].Distance; + for (var i = 1; i < cycleData.Entries.Count; i++) { + if (cycleData.Entries[i].Distance < cur) { + result.Add( + string.Format("distance-based cycle is not increasing strictly monotonous. entry: {0}, s_{1}: {2} s_{0}: {3}", i, + i - 1, cycleData.Entries[i - 1].Distance, cycleData.Entries[i].Distance)); + } + cur = cycleData.Entries[i].Distance; + } + } else { + var cur = cycleData.Entries[0].Time; + for (var i = 1; i < cycleData.Entries.Count; i++) { + if (cycleData.Entries[i].Time < cur) { + result.Add( + string.Format("time-based cycle is not increasing strictly monotonous. entry: {0}, t_{1}: {2} t_{0}: {3}", i, + i - 1, cycleData.Entries[i - 1].Time, cycleData.Entries[i].Time)); + } + cur = cycleData.Entries[i].Time; + } + } + if (result.Any()) { + return new ValidationResult(string.Format("Validation of Cycle {0} failed", cycleData.Name), result); + } + return ValidationResult.Success; + } + + [DebuggerDisplay( + "s:{Distance}, t:{Time}, v:{VehicleTargetSpeed}, grad:{RoadGradient}, n:{AngularVelocity}, gear:{Gear}")] + public class DrivingCycleEntry + { + public DrivingCycleEntry() {} + + public DrivingCycleEntry(DrivingCycleEntry entry) + { + Distance = entry.Distance; + Time = entry.Time; + VehicleTargetSpeed = entry.VehicleTargetSpeed; + RoadGradient = entry.RoadGradient; + Altitude = entry.Altitude; + StoppingTime = entry.StoppingTime; + AngularVelocity = entry.AngularVelocity; + Gear = entry.Gear; + AdditionalAuxPowerDemand = entry.AdditionalAuxPowerDemand; + AirSpeedRelativeToVehicle = entry.AirSpeedRelativeToVehicle; + WindYawAngle = entry.WindYawAngle; + Torque = entry.Torque; + Drag = entry.Drag; + PTOActive = entry.PTOActive; + AuxiliarySupplyPower = new Dictionary<string, Watt>(entry.AuxiliarySupplyPower); + EngineSpeed = entry.EngineSpeed; + FanSpeed = entry.FanSpeed; + } + + /// <summary> + /// Travelled distance used for distance-based cycles. If "t" is also defined this column will be ignored. + /// </summary> + public Meter Distance; + + /// <summary> + /// Used for time-based cycles. If neither this nor the distance. "s" is defined the data will be interpreted as 1Hz. + /// </summary> + public Second Time; + + /// <summary> + /// Required except for Engine Only Mode calculations. + /// </summary> + public MeterPerSecond VehicleTargetSpeed; + + /// <summary> + /// Optional. + /// </summary> + public Radian RoadGradient; + + /// <summary> + /// [%] Optional. + /// </summary> + public Scalar RoadGradientPercent + { + get { return (Math.Tan(RoadGradient.Value()) * 100).SI<Scalar>(); } + } + + /// <summary> + /// relative altitude of the driving cycle over distance + /// </summary> + public Meter Altitude; + + /// <summary> + /// Required for distance-based cycles. Not used in time based cycles. "stop" defines the time the vehicle spends in stop phases. + /// </summary> + public Second StoppingTime; + + /// <summary> + /// Supply Power input for each auxiliary defined in the .vecto file where xxx matches the ID of the corresponding + /// Auxiliary. ID's are not case sensitive and must not contain space or special characters. + /// </summary> + public Dictionary<string, Watt> AuxiliarySupplyPower; + + /// <summary> + /// If "n_eng_avg" is defined VECTO uses that instead of the calculated engine speed value. + /// </summary> + public PerSecond AngularVelocity; + + /// <summary> + /// [-] Gear input. Overwrites the gear shift model. + /// </summary> + public uint Gear; + + /// <summary> + /// This power input will be directly added to the engine power in addition to possible other auxiliaries. Also used in Engine Only Mode. + /// </summary> + public Watt AdditionalAuxPowerDemand; + + /// <summary> + /// Only required if Cross Wind Correction is set to Vair and Beta Input. + /// </summary> + public MeterPerSecond AirSpeedRelativeToVehicle; + + /// <summary> + /// [°] Only required if Cross Wind Correction is set to Vair and Beta Input. + /// </summary> + public double WindYawAngle; + + /// <summary> + /// Effective engine torque at clutch. Only required in Engine Only Mode. Alternatively power "Pe" can be defined. Use "DRAG" to define motoring operation. + /// </summary> + public NewtonMeter Torque; + + public bool Drag; + + /// <summary> + /// Power on the Wheels (only used in PWheel Mode). + /// </summary> + public Watt PWheel; + + public bool? TorqueConverterActive; + + /// <summary> + /// The angular velocity at the wheel. only used in PWheelCycle. + /// </summary> + public PerSecond WheelAngularVelocity; + + /// <summary> + /// Flag if PTO Cycle is active or not. + /// </summary> + public bool PTOActive; + + public PerSecond EngineSpeed; + + public PerSecond FanSpeed; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs index 6d6af1d390..f3c84e2500 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs @@ -29,163 +29,163 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Simulation; -using TUGraz.VectoCore.Models.Simulation.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.OutputData; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -{ - /// <summary> - /// Container Class for Auxiliaries which are connected to the Engine. - /// </summary> - public class EngineAuxiliary : StatefulVectoSimulationComponent<EngineAuxiliary.State>, IAuxInProvider, - IAuxPort - { - protected readonly Dictionary<string, Func<PerSecond, Watt>> Auxiliaries = - new Dictionary<string, Func<PerSecond, Watt>>(); - - public EngineAuxiliary(IVehicleContainer container) : base(container) {} - - public IAuxPort Port() - { - return this; - } - - /// <summary> - /// Adds a constant power demand auxiliary. - /// </summary> - /// <param name="auxId"></param> - /// <param name="powerDemand"></param> - public void AddConstant(string auxId, Watt powerDemand) - { - Add(auxId, _ => powerDemand); - } - - /// <summary> - /// Adds an auxiliary which gets its power demand from the driving cycle. - /// </summary> - /// <param name="auxId"></param> - public void AddCycle(string auxId) - { - Add(auxId, _ => DataBus.CycleData.LeftSample.AdditionalAuxPowerDemand); - } - - /// <summary> - /// Adds an auxiliary which calculates the demand based on a aux-map and the engine speed. - /// </summary> - /// <param name="auxId"></param> - /// <param name="data"></param> - public void AddMapping(string auxId, AuxiliaryData data) - { - if (!DataBus.CycleData.LeftSample.AuxiliarySupplyPower.ContainsKey(auxId)) { - var error = string.Format("driving cycle does not contain column for auxiliary: {0}", - Constants.Auxiliaries.Prefix + auxId); - Log.Error(error); - throw new VectoException(error); - } - - Add(auxId, speed => { - var powerSupply = DataBus.CycleData.LeftSample.AuxiliarySupplyPower[auxId]; - var nAuxiliary = speed * data.TransmissionRatio; - var powerAuxOut = powerSupply / data.EfficiencyToSupply; - var powerAuxIn = data.GetPowerDemand(nAuxiliary, powerAuxOut); - return powerAuxIn / data.EfficiencyToEngine; - }); - } - - /// <summary> - /// Adds an auxiliary with a function returning the power demand based on the engine speed. - /// </summary> - /// <param name="auxId"></param> - /// <param name="powerLossFunction"></param> - public void Add(string auxId, Func<PerSecond, Watt> powerLossFunction) - { - Auxiliaries[auxId] = powerLossFunction; - } - - public NewtonMeter Initialize(NewtonMeter torque, PerSecond angularSpeed) - { - PreviousState.AngularSpeed = angularSpeed; - if (angularSpeed.IsEqual(0)) { - return 0.SI<NewtonMeter>(); - } - - return ComputePowerDemand(angularSpeed, false) / angularSpeed; - } - - /// <summary> - /// Calculates the torque demand for all registered auxiliaries for the the current engine - /// </summary> - /// <param name="absTime"></param> - /// <param name="dt"></param> - /// <param name="torquePowerTrain"></param> - /// <param name="torqueEngine"></param> - /// <param name="angularSpeed"></param> - /// <param name="dryRun"></param> - /// <returns></returns> - public NewtonMeter TorqueDemand(Second absTime, Second dt, NewtonMeter torquePowerTrain, NewtonMeter torqueEngine, - PerSecond angularSpeed, bool dryRun = false) - { - var avgAngularSpeed = PreviousState.AngularSpeed != null - ? (angularSpeed + PreviousState.AngularSpeed) / 2.0 - : angularSpeed; - if (!dryRun) { - CurrentState.AngularSpeed = angularSpeed; - } - if (avgAngularSpeed.IsGreater(0)) { - return ComputePowerDemand(avgAngularSpeed, dryRun) / avgAngularSpeed; - } - return 0.SI<NewtonMeter>(); - } - - protected Watt ComputePowerDemand(PerSecond engineSpeed, bool dryRun) - { - var powerDemands = new Dictionary<string, Watt>(Auxiliaries.Count); - foreach (var item in Auxiliaries) { - var value = item.Value(engineSpeed); - if (value != null) { - powerDemands[item.Key] = value; - } - } - if (!dryRun) { - CurrentState.PowerDemands = powerDemands; - } - return powerDemands.Sum(kv => kv.Value); - } - - protected override void DoWriteModalResults(IModalDataContainer container) - { - var auxPowerDemand = 0.SI<Watt>(); - if (CurrentState.PowerDemands != null) { - foreach (var kv in CurrentState.PowerDemands) { - container[kv.Key] = kv.Value; - // mk 2016-10-11: pto's should not be counted in sum auxiliary power demand - if (kv.Key != Constants.Auxiliaries.IDs.PTOTransmission && kv.Key != Constants.Auxiliaries.IDs.PTOConsumer) { - auxPowerDemand += kv.Value; - } - } - } - if (container[ModalResultField.P_aux] == null || container[ModalResultField.P_aux] == DBNull.Value) { - // only overwrite if nobody else already wrote the total aux power - container[ModalResultField.P_aux] = auxPowerDemand; - } - } - - protected override void DoCommitSimulationStep() - { - AdvanceState(); - } - - public class State - { - public PerSecond AngularSpeed; - public Dictionary<string, Watt> PowerDemands; - } - } +using System; +using System.Collections.Generic; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Simulation; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.OutputData; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + /// <summary> + /// Container Class for Auxiliaries which are connected to the Engine. + /// </summary> + public class EngineAuxiliary : StatefulVectoSimulationComponent<EngineAuxiliary.State>, IAuxInProvider, + IAuxPort + { + protected readonly Dictionary<string, Func<PerSecond, Watt>> Auxiliaries = + new Dictionary<string, Func<PerSecond, Watt>>(); + + public EngineAuxiliary(IVehicleContainer container) : base(container) {} + + public IAuxPort Port() + { + return this; + } + + /// <summary> + /// Adds a constant power demand auxiliary. + /// </summary> + /// <param name="auxId"></param> + /// <param name="powerDemand"></param> + public void AddConstant(string auxId, Watt powerDemand) + { + Add(auxId, _ => powerDemand); + } + + /// <summary> + /// Adds an auxiliary which gets its power demand from the driving cycle. + /// </summary> + /// <param name="auxId"></param> + public void AddCycle(string auxId) + { + Add(auxId, _ => DataBus.CycleData.LeftSample.AdditionalAuxPowerDemand); + } + + /// <summary> + /// Adds an auxiliary which calculates the demand based on a aux-map and the engine speed. + /// </summary> + /// <param name="auxId"></param> + /// <param name="data"></param> + public void AddMapping(string auxId, AuxiliaryData data) + { + if (!DataBus.CycleData.LeftSample.AuxiliarySupplyPower.ContainsKey(auxId)) { + var error = string.Format("driving cycle does not contain column for auxiliary: {0}", + Constants.Auxiliaries.Prefix + auxId); + Log.Error(error); + throw new VectoException(error); + } + + Add(auxId, speed => { + var powerSupply = DataBus.CycleData.LeftSample.AuxiliarySupplyPower[auxId]; + var nAuxiliary = speed * data.TransmissionRatio; + var powerAuxOut = powerSupply / data.EfficiencyToSupply; + var powerAuxIn = data.GetPowerDemand(nAuxiliary, powerAuxOut); + return powerAuxIn / data.EfficiencyToEngine; + }); + } + + /// <summary> + /// Adds an auxiliary with a function returning the power demand based on the engine speed. + /// </summary> + /// <param name="auxId"></param> + /// <param name="powerLossFunction"></param> + public void Add(string auxId, Func<PerSecond, Watt> powerLossFunction) + { + Auxiliaries[auxId] = powerLossFunction; + } + + public NewtonMeter Initialize(NewtonMeter torque, PerSecond angularSpeed) + { + PreviousState.AngularSpeed = angularSpeed; + if (angularSpeed.IsEqual(0)) { + return 0.SI<NewtonMeter>(); + } + + return ComputePowerDemand(angularSpeed, false) / angularSpeed; + } + + /// <summary> + /// Calculates the torque demand for all registered auxiliaries for the the current engine + /// </summary> + /// <param name="absTime"></param> + /// <param name="dt"></param> + /// <param name="torquePowerTrain"></param> + /// <param name="torqueEngine"></param> + /// <param name="angularSpeed"></param> + /// <param name="dryRun"></param> + /// <returns></returns> + public NewtonMeter TorqueDemand(Second absTime, Second dt, NewtonMeter torquePowerTrain, NewtonMeter torqueEngine, + PerSecond angularSpeed, bool dryRun = false) + { + var avgAngularSpeed = PreviousState.AngularSpeed != null + ? (angularSpeed + PreviousState.AngularSpeed) / 2.0 + : angularSpeed; + if (!dryRun) { + CurrentState.AngularSpeed = angularSpeed; + } + if (avgAngularSpeed.IsGreater(0)) { + return ComputePowerDemand(avgAngularSpeed, dryRun) / avgAngularSpeed; + } + return 0.SI<NewtonMeter>(); + } + + protected Watt ComputePowerDemand(PerSecond engineSpeed, bool dryRun) + { + var powerDemands = new Dictionary<string, Watt>(Auxiliaries.Count); + foreach (var item in Auxiliaries) { + var value = item.Value(engineSpeed); + if (value != null) { + powerDemands[item.Key] = value; + } + } + if (!dryRun) { + CurrentState.PowerDemands = powerDemands; + } + return powerDemands.Sum(kv => kv.Value); + } + + protected override void DoWriteModalResults(IModalDataContainer container) + { + var auxPowerDemand = 0.SI<Watt>(); + if (CurrentState.PowerDemands != null) { + foreach (var kv in CurrentState.PowerDemands) { + container[kv.Key] = kv.Value; + // mk 2016-10-11: pto's should not be counted in sum auxiliary power demand + if (kv.Key != Constants.Auxiliaries.IDs.PTOTransmission && kv.Key != Constants.Auxiliaries.IDs.PTOConsumer) { + auxPowerDemand += kv.Value; + } + } + } + if (container[ModalResultField.P_aux] == null || container[ModalResultField.P_aux] == DBNull.Value) { + // only overwrite if nobody else already wrote the total aux power + container[ModalResultField.P_aux] = auxPowerDemand; + } + } + + protected override void DoCommitSimulationStep() + { + AdvanceState(); + } + + public class State + { + public PerSecond AngularSpeed; + public Dictionary<string, Watt> PowerDemands; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/VectoCore.csproj b/VectoCore/VectoCore/VectoCore.csproj index d705d3cab6..271776d82b 100644 --- a/VectoCore/VectoCore/VectoCore.csproj +++ b/VectoCore/VectoCore/VectoCore.csproj @@ -186,6 +186,7 @@ <Compile Include="Models\SimulationComponent\Impl\TorqueConverter.cs" /> <Compile Include="Models\SimulationComponent\Impl\IdleControllerSwitcher.cs" /> <Compile Include="Models\Simulation\Data\ModalResultField.cs" /> + <Compile Include="InputData\Reader\Impl\EngineeringEPTPModeVectoRunDataFactory.cs" /> <Compile Include="OutputData\ModFilter\ActualModalDataFilter.cs" /> <Compile Include="OutputData\ModFilter\ModalData1HzFilter.cs" /> <Compile Include="OutputData\XML\AbstractXMLWriter.cs" /> diff --git a/VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs b/VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs new file mode 100644 index 0000000000..67f0d53702 --- /dev/null +++ b/VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs @@ -0,0 +1,37 @@ +using NUnit.Framework; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCore.InputData.FileIO.JSON; +using TUGraz.VectoCore.Models.Simulation.Impl; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.OutputData.FileIO; + +namespace TUGraz.VectoCore.Tests.Integration.EPTP +{ + [TestFixture] + public class EPTPTest + { + [TestCase()] + public void RunEPTP() + { + var jobFile = @"E:\QUAM\Workspace\VECTO_quam\EPTP\MAN_EPTP.vecto"; + + var fileWriter = new FileOutputWriter(jobFile); + var sumWriter = new SummaryDataContainer(fileWriter); + var jobContainer = new JobContainer(sumWriter); + var dataProvider = JSONInputDataFactory.ReadJsonJob(jobFile); + var runsFactory = new SimulatorFactory(ExecutionMode.Engineering, dataProvider, fileWriter) { + ModalResults1Hz = false, + WriteModalResults = true, + ActualModalData = false, + Validate = false, + }; + + jobContainer.AddRuns(runsFactory); + jobContainer.Execute(); + + Assert.AreEqual(true, jobContainer.AllCompleted); + + } + + } +} \ No newline at end of file diff --git a/VectoCore/VectoCoreTest/VectoCoreTest.csproj b/VectoCore/VectoCoreTest/VectoCoreTest.csproj index d68ab97396..06dec001cf 100644 --- a/VectoCore/VectoCoreTest/VectoCoreTest.csproj +++ b/VectoCore/VectoCoreTest/VectoCoreTest.csproj @@ -83,6 +83,7 @@ <Compile Include="Integration\CoachAdvancedAuxPowertrain.cs" /> <Compile Include="Integration\CoachPowerTrain.cs" /> <Compile Include="Integration\DriverStrategy\SimpleCycles.cs" /> + <Compile Include="Integration\EPTP\EPTPTest.cs" /> <Compile Include="Integration\FuelTypesTest.cs" /> <Compile Include="Integration\FullCycleDeclarationTest.cs"> <SubType>Code</SubType> -- GitLab