diff --git a/User Manual/fileformat/VSUM.md b/User Manual/fileformat/VSUM.md index 6e12ca6645fb2ca31cf3743df1780f5532f9e8f8..092b6206ff98d267ff02d5f8d5b75159019b69f5 100644 --- a/User Manual/fileformat/VSUM.md +++ b/User Manual/fileformat/VSUM.md @@ -7,20 +7,22 @@ The .vsum file includes total / average results for each calculation run in one | Name | Unit | Description | ----- | ---- | ----------------------------------------- -| Job | [-] | Job number. Format is "x-y" with x = file number and y = cycle number +| Job | [-] | Job number in the format "x-y" (where x = file number and y = cycle number) | Input File | [-] | Name of the input file | Cycle | [-] | Name of the cycle file +| Status | [-] | The result status of the run (Success, Aborted) | time | [s] | Total simulation time -| distance | [km] | Total travelled distance +| distance | [km] | Total traveled distance | speed | [km/h] | Average vehicle speed | ∆altitude | [m] | Altitude difference between start and end of cycle | Ppos | [kW] | Average positive engine power | Pneg | [kW] | Average negative engine power -| FC-Final | [g/km] \& [l/100km] \& [l/100tkm] | Average fuel consumption. Final value after all corrections. -| FC-Map | [g/h] & [g/km] | Fuel consumption interpolated form [Fuel Map](#fuel-consumption-calculation). -| FC-AUXc | [g/km] | Fuel consumption after [Auxiliary-Start/Stop Correction](#fuel-consumption-calculation). (Based on FC.) +| FC-Final | [g/km], [l/100km], [l/100tkm] | Average fuel consumption. Final value after all corrections. +| FC-Map | [g/h], [g/km] | Fuel consumption interpolated fromm [Fuel Map](#fuel-consumption-calculation). +| FC-AUXc | [g/h], [g/km] | Fuel consumption after [Auxiliary-Start/Stop Correction](#fuel-consumption-calculation). (Based on FC.) | FC-WHTCc | [g/km] | Fuel consumption after [WHTC Correction](#fuel-consumption-calculation). (Based on FC-AUXc.) -| Co~2~ | [g/km] & [g/tkm] | Average CO~2~ emissions. +| Co~2~ | [g/km], [g/tkm] | Average CO~2~ emissions. +| PwheelPos | [kW] | Average positive wheel power | Pbrake | [kW] | Average brake power (not including engine drag) | EposICE | [kWh] | Total positive engine work | EnegICE | [kWh] | Total negative engine work (engine brake) @@ -33,10 +35,10 @@ The .vsum file includes total / average results for each calculation run in one | Ebrake | [kWh] | Total work dissipated in mechanical braking (sum of service brakes, retader and additional engine exhaust brakes) | Etransm | [kWh] | Total work of transmission losses | Eretarder | [kWh] | Total retarder losses -| Etorqueconv | [kWh] | Total torque converter losses +| Etorqueconv | [kWh] | Total torque converter losses | Mass | [kg] | Vehicle mass (equals **Curb Weight Vehicle** plus **Curb Weight Extra Trailer/Body**, see [Vehicle Editor](#vehicle-editor)) | Loading | [kg] | Vehicle loading (see [Vehicle Editor](#vehicle-editor)) -| a | [m/s2] | Average acceleration +| a | [m/s²] | Average acceleration | a_pos | [m/s²] | Average acceleration in acceleration phases \* | a_neg | [m/s²] | Average deceleration in deceleration phases \* | Acc.Noise | [m/s²] | Acceleration noise diff --git a/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs b/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs index 1a6ea2570ac334428f444fff073021c85c5010ac..b0c5f8834d519013a775b42625703673d5deeb21 100644 --- a/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs +++ b/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Principal; using Newtonsoft.Json; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.FileIO.DeclarationFile; @@ -47,9 +48,10 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl var gearboxData = dao.CreateGearboxData(Gearbox, engineData); var gearboxTypeString = string.Format("{0}-Speed {1}", gearboxData.Gears.Count, gearboxData.Type); - // todo: set correct <USERNAME> in Report - var report = new DeclarationReport(engineData.FullLoadCurve, segment, "<USERNAME>", engineData.ModelName, - engineTypeString, gearboxData.ModelName, gearboxTypeString, Job.BasePath, Job.JobFile, resultCount); + var report = new DeclarationReport(engineData.FullLoadCurve, segment, WindowsIdentity.GetCurrent().Name, + engineData.ModelName, + engineTypeString, gearboxData.ModelName, gearboxTypeString, Job.BasePath, + Path.GetFileNameWithoutExtension(Job.JobFile), resultCount); foreach (var mission in segment.Missions) { DrivingCycleData cycle; @@ -75,6 +77,8 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl Report = report, Mission = mission, }; + simulationRunData.EngineData.WHTCCorrectionFactor = DeclarationData.WHTCCorrection.Lookup(mission.MissionType, + engineData.WHTCRural.Value(), engineData.WHTCUrban.Value(), engineData.WHTCMotorway.Value()); simulationRunData.Cycle.Name = mission.MissionType.ToString(); simulationRunData.VehicleData.VehicleClass = segment.VehicleClass; yield return simulationRunData; diff --git a/VectoCore/Models/Declaration/DeclarationReport.cs b/VectoCore/Models/Declaration/DeclarationReport.cs index 139b444725fad3873267e7b2252044b527873a41..79fbd2bb229fe7a9b95c8168084e069bf2a2527e 100644 --- a/VectoCore/Models/Declaration/DeclarationReport.cs +++ b/VectoCore/Models/Declaration/DeclarationReport.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Windows.Forms.DataVisualization.Charting; +using NLog; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.SimulationComponent.Data; using TUGraz.VectoCore.Utils; @@ -162,14 +163,12 @@ namespace TUGraz.VectoCore.Models.Declaration private Stream CreateTitlePage(Dictionary<MissionType, ResultContainer> missions) { var stream = new MemoryStream(); - var resourceName = string.Format("{0}Report.title{1}CyclesTemplate.pdf", RessourceHelper.Namespace, missions.Count); var inputStream = RessourceHelper.ReadStream(resourceName); - var reader = new PdfReader(inputStream); - var stamper = new PdfStamper(reader, stream); + var pdfFields = stamper.AcroFields; pdfFields.SetField("version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()); pdfFields.SetField("Job", _jobFile); @@ -191,33 +190,21 @@ namespace TUGraz.VectoCore.Models.Declaration foreach (var results in missions.Values.OrderBy(m => m.Mission.MissionType)) { pdfFields.SetField("Mission" + i, results.Mission.MissionType.ToString()); - var m = results.ModData[LoadingType.ReferenceLoad]; - var dt = m.GetValues<Second>(ModalResultField.simulationInterval); - var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); - var maxTime = m.GetValues<Second>(ModalResultField.time).Max(); - - var avgSpeed = maxDistance / maxTime; + var data = results.ModData[LoadingType.ReferenceLoad]; pdfFields.SetField("Loading" + i, results.Mission.RefLoad.ConvertTo().Ton.ToOutputFormat(1) + " t"); - pdfFields.SetField("Speed" + i, avgSpeed.ConvertTo().Kilo.Meter.Per.Hour.ToOutputFormat(1) + " km/h"); - - var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); - var fcWeight = fc.Zip(dt, (fcVal, dtVal) => fcVal * dtVal).Sum(); - var fcVolume = (fcWeight / Physics.FuelDensity).Cast<CubicMeter>(); - var co2Weight = fcWeight * Physics.CO2PerFuelWeight; + pdfFields.SetField("Speed" + i, data.Speed().ConvertTo().Kilo.Meter.Per.Hour.ToOutputFormat(1) + " km/h"); - var fcAvgVolume = fcVolume / maxDistance; - var co2AvgWeight = co2Weight / maxDistance; + var fcLiterPer100Km = data.FuelConsumptionLiterPer100Kilometer(); + pdfFields.SetField("FC" + i, fcLiterPer100Km.ToOutputFormat(1)); var loadingTon = results.Mission.RefLoad.ConvertTo().Ton; - - var fcLiterPer100Km = fcAvgVolume.ConvertTo().Cubic.Dezi.Meter * 100.SI().Kilo.Meter; var fcLiterPer100Tonkm = fcLiterPer100Km / loadingTon; - var co2GrammPerKm = co2AvgWeight.ConvertTo().Gramm.Per.Kilo.Meter; + pdfFields.SetField("FCt" + i, fcLiterPer100Tonkm.ToOutputFormat(1)); + + var co2GrammPerKm = data.CO2PerMeter().ConvertTo().Gramm.Per.Kilo.Meter; var co2GrammPerTonKm = co2GrammPerKm / loadingTon; - pdfFields.SetField("FC" + i, fcLiterPer100Km.ToOutputFormat(1)); - pdfFields.SetField("FCt" + i, fcLiterPer100Tonkm.ToOutputFormat(1)); pdfFields.SetField("CO2" + i, co2GrammPerKm.ToOutputFormat(1)); pdfFields.SetField("CO2t" + i, co2GrammPerTonKm.ToOutputFormat(1)); i++; @@ -277,34 +264,23 @@ namespace TUGraz.VectoCore.Models.Declaration foreach (var pair in results.ModData) { var loadingType = pair.Key; - var m = pair.Value; - - var dt = m.GetValues<Second>(ModalResultField.simulationInterval); - var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); - var maxTime = m.GetValues<Second>(ModalResultField.time).Max(); - var avgSpeed = maxDistance / maxTime; - - var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); - var fcWeight = fc.Zip(dt, (fcVal, dtVal) => fcVal * dtVal).Sum(); - var fcVolume = (fcWeight / Physics.FuelDensity).Cast<CubicMeter>(); - var co2Weight = fcWeight * Physics.CO2PerFuelWeight; - var fcAvgVolume = fcVolume / maxDistance; - var co2AvgWeight = co2Weight / maxDistance; - var loadingTon = results.Mission.Loadings[loadingType].ConvertTo().Ton; + var data = pair.Value; - var fcLiterPer100Km = fcAvgVolume.ConvertTo().Cubic.Dezi.Meter * 100.SI().Kilo.Meter; - var co2GrammPerKm = co2AvgWeight.ConvertTo().Gramm.Per.Kilo.Meter; + var loadingTon = results.Mission.Loadings[loadingType].ConvertTo().Ton; + var loadAppendix = loadingType.GetShortName(); - var loadString = loadingType.GetShortName(); + pdfFields.SetField("Load" + loadAppendix, loadingTon.ToOutputFormat(1) + " t"); + pdfFields.SetField("Speed" + loadAppendix, data.Speed().ConvertTo().Kilo.Meter.Per.Hour.ToOutputFormat(1)); - pdfFields.SetField("Load" + loadString, - results.Mission.Loadings[loadingType].ConvertTo().Ton.ToOutputFormat(1) + " t"); - pdfFields.SetField("Speed" + loadString, avgSpeed.ConvertTo().Kilo.Meter.Per.Hour.ToOutputFormat(1)); - pdfFields.SetField("FCkm" + loadString, fcLiterPer100Km.ToOutputFormat(1)); - pdfFields.SetField("FCtkm" + loadString, + var fcLiterPer100Km = data.FuelConsumptionLiterPer100Kilometer(); + pdfFields.SetField("FCkm" + loadAppendix, fcLiterPer100Km.ToOutputFormat(1)); + pdfFields.SetField("FCtkm" + loadAppendix, loadingTon.IsEqual(0) ? "-" : (fcLiterPer100Km / loadingTon).ToOutputFormat(1)); - pdfFields.SetField("CO2km" + loadString, co2GrammPerKm.ToOutputFormat(1)); - pdfFields.SetField("CO2tkm" + loadString, + + + var co2GrammPerKm = data.CO2PerMeter().ConvertTo().Gramm.Per.Kilo.Meter; + pdfFields.SetField("CO2km" + loadAppendix, co2GrammPerKm.ToOutputFormat(1)); + pdfFields.SetField("CO2tkm" + loadAppendix, loadingTon.IsEqual(0) ? "-" : (co2GrammPerKm / loadingTon).ToOutputFormat(1)); } @@ -387,24 +363,16 @@ namespace TUGraz.VectoCore.Models.Declaration }); foreach (var missionResult in missions.OrderBy(m => m.Key)) { - var m = missionResult.Value.ModData[LoadingType.ReferenceLoad]; - var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); - var dt = m.GetValues<Second>(ModalResultField.simulationInterval); - - var fcWeight = fc.Zip(dt, (fcValue, dtValue) => fcValue * dtValue).Sum(); - - var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); - var loading = missionResult.Value.Mission.Loadings[LoadingType.ReferenceLoad]; + var data = missionResult.Value.ModData[LoadingType.ReferenceLoad]; - var co2Weight = fcWeight * Physics.CO2PerFuelWeight; - - var co2GPerTonKm = co2Weight.ConvertTo().Gramm / (maxDistance.ConvertTo().Kilo.Meter * loading.ConvertTo().Ton); + var loadingTon = missionResult.Value.Mission.Loadings[LoadingType.ReferenceLoad].ConvertTo().Ton; + var co2GrammPerTonKm = data.CO2PerMeter().ConvertTo().Gramm.Per.Kilo.Meter / loadingTon; var series = new Series(missionResult.Key + " (Ref. load.)"); var dataPoint = new DataPoint { Name = missionResult.Key.ToString(), - YValues = new[] { co2GPerTonKm.Value() }, - Label = co2GPerTonKm.ToOutputFormat(1, showUnit: true), + YValues = new[] { co2GrammPerTonKm.Value() }, + Label = co2GrammPerTonKm.ToOutputFormat(1, showUnit: true), Font = new Font("Helvetica", 20), LabelBackColor = Color.White }; @@ -458,23 +426,13 @@ namespace TUGraz.VectoCore.Models.Declaration ChartType = SeriesChartType.Point }; foreach (var pair in missionResult.Value.ModData) { - var modData = missionResult.Value.ModData[pair.Key]; - var fc = modData.GetValues<KilogramPerSecond>(ModalResultField.FCMap); - var dt = modData.GetValues<Second>(ModalResultField.simulationInterval).ToList(); - - var fcSum = fc.Zip(dt, (fcValue, dtValue) => fcValue * dtValue).Sum(); - - - var maxDistance = modData.GetValues<Meter>(ModalResultField.dist).Max(); - var maxTime = modData.GetValues<Second>(ModalResultField.time).Max(); - - var avgKmh = maxDistance.ConvertTo().Kilo.Meter / maxTime.ConvertTo().Hour; - var co2GPerKm = fcSum.ConvertTo().Gramm * Physics.CO2PerFuelWeight / maxDistance.ConvertTo().Kilo.Meter; + var data = missionResult.Value.ModData[pair.Key]; - var loading = missionResult.Value.Mission.Loadings[pair.Key].ConvertTo().Ton; + var co2GramPerKilometer = data.CO2PerMeter().ConvertTo().Gramm.Per.Kilo.Meter; + var loadingTon = missionResult.Value.Mission.Loadings[pair.Key].ConvertTo().Ton; - var point = new DataPoint(avgKmh.Value(), co2GPerKm.Value()) { - Label = string.Format(CultureInfo.InvariantCulture, "{0:0.0} t", loading.Value()), + var point = new DataPoint(data.Speed().ConvertTo().Kilo.Meter.Per.Hour.Value(), co2GramPerKilometer.Value()) { + Label = string.Format(CultureInfo.InvariantCulture, "{0:0.0} t", loadingTon.Value()), Font = new Font("Helvetica", 16), LabelBackColor = Color.White }; @@ -627,8 +585,8 @@ namespace TUGraz.VectoCore.Models.Declaration var dataPoints = new Series("load points (Ref. load.)") { ChartType = SeriesChartType.Point, Color = Color.Red }; dataPoints.Points.DataBindXY( - modData.GetValues<SI>(ModalResultField.n).Select(x => x.ConvertTo().Rounds.Per.Minute).ToDouble(), - modData.GetValues<SI>(ModalResultField.Tq_eng).ToDouble()); + modData.GetValues<PerSecond>(ModalResultField.n).Select(x => x.ConvertTo().Rounds.Per.Minute).ToDouble(), + modData.GetValues<NewtonMeter>(ModalResultField.Tq_eng).ToDouble()); operatingPointsChart.Series.Add(dataPoints); operatingPointsChart.Update(); diff --git a/VectoCore/Models/Declaration/WHTCCorrection.cs b/VectoCore/Models/Declaration/WHTCCorrection.cs index adb0eb173cfb7f5ad6c51dc7ad7f62cf01b1e75c..18967378f26330f42cd440b9b16731337fc421a7 100644 --- a/VectoCore/Models/Declaration/WHTCCorrection.cs +++ b/VectoCore/Models/Declaration/WHTCCorrection.cs @@ -8,7 +8,8 @@ namespace TUGraz.VectoCore.Models.Declaration { public class WHTCCorrection : LookupData<MissionType, double, double, double, double> { - private Dictionary<MissionType, WHTCCorrectionEntry> _data = new Dictionary<MissionType, WHTCCorrectionEntry>(); + private readonly Dictionary<MissionType, WHTCCorrectionEntry> _data = + new Dictionary<MissionType, WHTCCorrectionEntry>(); protected const string ResourceId = "TUGraz.VectoCore.Resources.Declaration.WHTC-Weighting-Factors.csv"; diff --git a/VectoCore/Models/Simulation/Data/IModalDataWriter.cs b/VectoCore/Models/Simulation/Data/IModalDataWriter.cs index 9b4f31e8fe378a3bc2ef3f3e80bc86e42188b199..3f225287bff78a0549f6c320410c8fa15a8fad61 100644 --- a/VectoCore/Models/Simulation/Data/IModalDataWriter.cs +++ b/VectoCore/Models/Simulation/Data/IModalDataWriter.cs @@ -77,7 +77,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Data return data.GetValues<SI>(col).Where(filter ?? (x => x != null)).Sum(); } - public static SI Average(this IEnumerable<SI> self, Func<SI, bool> filter = null) + public static SI Average(this IEnumerable<SI> self, Func<SI, bool> filter) { var values = self.Where(filter ?? (x => x != null && !double.IsNaN(x.Value()))).ToList(); return values.Any() ? values.Sum() / values.Count : null; @@ -93,58 +93,57 @@ namespace TUGraz.VectoCore.Models.Simulation.Data return self ?? defaultValue; } - public static object AverageAccelerationsPositive(this IModalDataWriter data) + public static MeterPerSquareSecond AccelerationsPositive3SecondAverage(this IModalDataWriter data) { - var acceleration3SecondAverage = Calculate3SecondAverage(Accelerations(data)); - return acceleration3SecondAverage.Average(x => x > 0.125).DefaultIfNull(); + var acceleration3SecondAverage = AccelerationPer3Seconds(data); + return acceleration3SecondAverage.Where(x => x > 0.125).Average(); } - public static object AverageAccelerationsNegative(this IModalDataWriter data) + public static MeterPerSquareSecond AccelerationNoise(this IModalDataWriter data) { - var acceleration3SecondAverage = Calculate3SecondAverage(Accelerations(data)); - return acceleration3SecondAverage.Average(x => x < -0.125).DefaultIfNull(); + var avg = data.AccelerationAverage(); + var accelerationAverages = AccelerationPerSecond(data).ToList(); + var sqareAvg = accelerationAverages.Select(x => (x - avg) * (x - avg)).Sum() / accelerationAverages.Count; + return sqareAvg.Sqrt().Cast<MeterPerSquareSecond>(); } - public static object PercentAccelerationTime(this IModalDataWriter data) + public static MeterPerSquareSecond AverageAccelerations3SecondNegative(this IModalDataWriter data) { - var acceleration3SecondAverage = Calculate3SecondAverage(Accelerations(data)).ToList(); + var acceleration3SecondAverage = AccelerationPer3Seconds(data); + return acceleration3SecondAverage.Where(x => x < -0.125).Average(); + } + + public static Scalar PercentAccelerationTime(this IModalDataWriter data) + { + var acceleration3SecondAverage = AccelerationPer3Seconds(data).ToList(); return 100.SI<Scalar>() * acceleration3SecondAverage.Count(x => x > 0.125) / acceleration3SecondAverage.Count; } - public static object PercentDecelerationTime(this IModalDataWriter data) + public static Scalar PercentDecelerationTime(this IModalDataWriter data) { - var acceleration3SecondAverage = Calculate3SecondAverage(Accelerations(data)).ToList(); + var acceleration3SecondAverage = AccelerationPer3Seconds(data).ToList(); return 100.SI<Scalar>() * acceleration3SecondAverage.Count(x => x < -0.125) / acceleration3SecondAverage.Count; } - public static object PercentCruiseTime(this IModalDataWriter data) + public static Scalar PercentCruiseTime(this IModalDataWriter data) { - var acceleration3SecondAverage = Calculate3SecondAverage(Accelerations(data)).ToList(); - return 100.SI<Scalar>() * acceleration3SecondAverage.Count(x => x < 0.125 && x > -0.125) / + var acceleration3SecondAverage = AccelerationPer3Seconds(data).ToList(); + return 100.SI<Scalar>() * acceleration3SecondAverage.Count(x => x.IsBetween(-0.125, -0.125)) / acceleration3SecondAverage.Count; } - public static SI PercentStopTime(this IModalDataWriter data) + public static Scalar PercentStopTime(this IModalDataWriter data) { - var acceleration3SecondAverage = Calculate3SecondAverage(Accelerations(data)); - if (acceleration3SecondAverage.Any()) {} - var pStopTime = data.GetValues<SI>(ModalResultField.v_act) - .Zip(SimulationIntervals(data), (velocity, dt) => new { velocity, dt }) - .Where(x => x.velocity < 0.1) - .Sum(x => x.dt.Value()); - return 100 * pStopTime / SimulationIntervals(data).Sum(); - } + var stopTime = data.GetValues<MeterPerSecond>(ModalResultField.v_act) + .Zip(data.SimulationIntervals(), (v, dt) => new { v, dt }) + .Where(x => x.v < 0.1).Select(x => x.dt).Sum(); - public static SI AverageAccelerations(this IModalDataWriter data) - { - return Accelerations(data).Average(); + return 100 * (stopTime / data.Duration()).Cast<Scalar>(); } - public static List<SI> Accelerations(this IModalDataWriter data) + public static MeterPerSquareSecond AccelerationAverage(this IModalDataWriter data) { - var accValues = data.GetValues<SI>(ModalResultField.acc).Cast<MeterPerSquareSecond>(); - var accelerations = CalculateAverageOverSeconds(SimulationIntervals(data), accValues).ToList(); - return accelerations; + return data.TimeIntegral<MeterPerSecond>(ModalResultField.acc) / data.Duration(); } public static Second[] SimulationIntervals(this IModalDataWriter data) @@ -152,188 +151,236 @@ namespace TUGraz.VectoCore.Models.Simulation.Data return data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); } - public static SI AltitudeDelta(this IModalDataWriter data) + public static Meter AltitudeDelta(this IModalDataWriter data) { - return data.GetValues<SI>(ModalResultField.altitude).Last() - - data.GetValues<SI>(ModalResultField.altitude).First(); + return data.GetValues<Meter>(ModalResultField.altitude).Last() - + data.GetValues<Meter>(ModalResultField.altitude).First(); } - public static SI PowerAccelerations(this IModalDataWriter data) + public static WattSecond PowerAccelerations(this IModalDataWriter data) { - var zero = 0.SI<Watt>(); + var paEngine = data.TimeIntegral<WattSecond>(ModalResultField.PaEng); + var paGearbox = data.TimeIntegral<WattSecond>(ModalResultField.PaGB); + return paEngine + paGearbox; + } - var paeng = data.Sum(ModalResultField.PaEng) ?? zero; - var pagb = data.Sum(ModalResultField.PaGB) ?? zero; + public static WattSecond WorkTransmission(this IModalDataWriter data) + { + var plossdiff = data.TimeIntegral<WattSecond>(ModalResultField.PlossGB); + var plossgb = data.TimeIntegral<WattSecond>(ModalResultField.PlossDiff); + return plossdiff + plossgb; + } - return paeng + pagb; + public static WattSecond WorkRetarder(this IModalDataWriter data) + { + return data.TimeIntegral<WattSecond>(ModalResultField.PlossRetarder); } - public static SI WorkTransmission(this IModalDataWriter data) + public static WattSecond WorkTorqueConverter(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - var plossdiff = TimeIntegralPower(ModalResultField.PlossGB, data, simulationIntervals); - var plossgb = TimeIntegralPower(ModalResultField.PlossDiff, data, simulationIntervals); - return plossdiff + plossgb; + //TODO (MK, 2015-11-10): return torque converter work - this was currently not possible because torque converter is not implemented. + return 0.SI<WattSecond>(); } - public static object PowerLossRetarder(this IModalDataWriter data) + public static Second Duration(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return TimeIntegralPower(ModalResultField.PlossRetarder, data, simulationIntervals).DefaultIfNull(); + return (data.Max(ModalResultField.time) - data.Min(ModalResultField.time)).Cast<Second>(); } - public static SI Duration(this IModalDataWriter data) + public static Meter Distance(this IModalDataWriter data) { - return data.Max(ModalResultField.time) - data.Min(ModalResultField.time); + return (data.Max(ModalResultField.dist) - data.Min(ModalResultField.dist)).Cast<Meter>(); } - public static SI Distance(this IModalDataWriter data) + public static WattSecond WorkTotalMechanicalBrake(this IModalDataWriter data) { - return data.Max(ModalResultField.dist) - data.Min(ModalResultField.dist); + return data.TimeIntegral<WattSecond>(ModalResultField.Pbrake); } + public static WattSecond WorkAuxiliaries(this IModalDataWriter data) + { + return data.TimeIntegral<WattSecond>(ModalResultField.Paux); + } - public static object WorkTotalMechanicalBrake(this IModalDataWriter data) + public static WattSecond WorkRoadGradientResistance(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return TimeIntegralPower(ModalResultField.Pbrake, data, simulationIntervals).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Pgrad); } - public static object WorkAuxiliaries(this IModalDataWriter data) + public static WattSecond WorkRollingResistance(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return TimeIntegralPower(ModalResultField.Paux, data, simulationIntervals).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Proll); } - public static object WorkRoadGradientResistance(this IModalDataWriter data) + public static WattSecond WorkAirResistance(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return TimeIntegralPower(ModalResultField.Pgrad, data, simulationIntervals).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Pair); } - public static object WorkRollingResistance(this IModalDataWriter data) + public static WattSecond EngineWorkPositive(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return TimeIntegralPower(ModalResultField.Proll, data, simulationIntervals).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Pe_eng, x => x > 0); } - public static object WorkAirResistance(this IModalDataWriter data) + public static WattSecond EngineWorkNegative(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return TimeIntegralPower(ModalResultField.Pair, data, simulationIntervals).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Pe_eng, x => x < 0); } - public static object WorkEngineNegative(this IModalDataWriter data) + public static Watt PowerBrake(this IModalDataWriter data) { - return data.Average(ModalResultField.Pe_eng, x => x < 0).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Pbrake) / data.Duration(); } - public static object WorkEnginePositive(this IModalDataWriter data) + public static Watt PowerWheelPositive(this IModalDataWriter data) { - return data.Average(ModalResultField.Pe_eng, x => x > 0).DefaultIfNull(); + return data.TimeIntegral<WattSecond>(ModalResultField.Pwheel, x => x > 0) / data.Duration(); } - public static object PowerBrake(this IModalDataWriter data) + public static KilogramPerMeter FuelConsumptionWHTCCorrected(this IModalDataWriter data) { - return data.Average(ModalResultField.Pbrake).DefaultIfNull(); + return data.TimeIntegral<Kilogram>(ModalResultField.FCWHTCc) / data.Distance(); } - public static object FuelConsumptionWHTCCorrected(this IModalDataWriter data) + public static KilogramPerSecond FuelConsumptionWHTCCorrectedPerSecond(this IModalDataWriter data) { - return data.Average(ModalResultField.FCWHTCc).DefaultIfNull(); + return data.TimeIntegral<Kilogram>(ModalResultField.FCWHTCc) / data.Duration(); } - public static object FuelConsumptionAuxStartStopCorrected(this IModalDataWriter data) + public static KilogramPerMeter FuelConsumptionAuxStartStopCorrected(this IModalDataWriter data) { - return data.Average(ModalResultField.FCAUXc).DefaultIfNull(); + return data.TimeIntegral<Kilogram>(ModalResultField.FCAUXc) / data.Distance(); } - public static SI FuelConsumption(this IModalDataWriter data) + public static KilogramPerSecond FuelConsumptionAuxStartStopCorrectedPerSecond(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return - (TimeIntegralFuelConsumption(ModalResultField.FCMap, data, simulationIntervals) / Duration(data)).ConvertTo() - .Gramm.Per.Hour; + return data.TimeIntegral<Kilogram>(ModalResultField.FCAUXc) / data.Duration(); } - public static SI FuelConsumptionPerKilometer(this IModalDataWriter data) + public static KilogramPerMeter FuelConsumptionFinal(this IModalDataWriter data) { - var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - return - (TimeIntegralFuelConsumption(ModalResultField.FCMap, data, simulationIntervals) / Distance(data)).ConvertTo() - .Gramm.Per.Kilo.Meter; + return data.TimeIntegral<Kilogram>(ModalResultField.FCWHTCc) / data.Distance(); } - public static object Pneg(this IModalDataWriter data) + public static SI FuelConsumptionFinalLiterPer100Kilometer(this IModalDataWriter data) { - return data.Average(ModalResultField.Pe_eng, x => x < 0).DefaultIfNull(); + var fcVolumePerMeter = data.FuelConsumptionFinal() / Physics.FuelDensity; + return fcVolumePerMeter.ConvertTo().Cubic.Dezi.Meter * 100.SI().Kilo.Meter; } - public static object Ppos(this IModalDataWriter data) + public static KilogramPerMeter CO2PerMeter(this IModalDataWriter data) { - return data.Average(ModalResultField.Pe_eng, x => x > 0).DefaultIfNull(); + return data.TimeIntegral<Kilogram>(ModalResultField.FCMap) * Physics.CO2PerFuelWeight / data.Distance(); } - public static SI Speed(this IModalDataWriter data) + public static SI FuelConsumptionLiterPer100Kilometer(this IModalDataWriter data) { - return (Distance(data) / Duration(data)).ConvertTo().Kilo.Meter.Per.Hour; + var fcVolumePerMeter = data.FuelConsumptionPerMeter() / Physics.FuelDensity; + return fcVolumePerMeter.ConvertTo().Cubic.Dezi.Meter * 100.SI().Kilo.Meter; } - private static SI TimeIntegralPower(ModalResultField field, IModalDataWriter data, - IEnumerable<Second> simulationIntervals) + public static KilogramPerSecond FuelConsumptionPerSecond(this IModalDataWriter data) { - return data.GetValues<Watt>(field) - .Zip(simulationIntervals, (P, dt) => dt.ConvertTo().Hour * (P ?? 0.SI<Watt>()).ConvertTo().Kilo.Watt) - .Sum(); + return data.TimeIntegral<Kilogram>(ModalResultField.FCMap) / data.Duration(); } - private static Kilogram TimeIntegralFuelConsumption(ModalResultField field, IModalDataWriter data, - IEnumerable<Second> simulationIntervals) + public static KilogramPerMeter FuelConsumptionPerMeter(this IModalDataWriter data) { - return data.GetValues<KilogramPerSecond>(field) - .Zip(simulationIntervals, (FC, dt) => dt * (FC ?? 0.SI<KilogramPerSecond>())) - .Sum().Cast<Kilogram>(); + return data.TimeIntegral<Kilogram>(ModalResultField.FCMap) / data.Distance(); } - private static IEnumerable<SI> Calculate3SecondAverage(IReadOnlyList<SI> accelerations) + public static Watt EnginePowerNegativeAverage(this IModalDataWriter data) { - if (accelerations.Count >= 3) { - var runningAverage = (accelerations[0] + accelerations[1] + accelerations[2]) / 3.0; - for (var i = 2; i < accelerations.Count() - 1; i++) { - runningAverage -= accelerations[i - 2] / 3.0; - runningAverage += accelerations[i + 1] / 3.0; - yield return runningAverage; - } + var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval); + var values = data.GetValues<Watt>(ModalResultField.Pe_eng) + .Zip(simulationIntervals, (value, dt) => new { Dt = dt, Value = value * dt }) + .Where(v => v.Value < 0).ToList(); + if (values.Any()) { + return values.Select(v => v.Value).Sum() / values.Select(v => v.Dt).Sum(); } + return 0.SI<Watt>(); } + public static Watt EnginePowerPositiveAverage(this IModalDataWriter data) + { + var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval); + var values = data.GetValues<Watt>(ModalResultField.Pe_eng) + .Zip(simulationIntervals, (value, dt) => new { Dt = dt, Value = value * dt }) + .Where(v => v.Value > 0).ToList(); + if (values.Any()) { + return values.Select(v => v.Value).Sum() / values.Select(v => v.Dt).Sum(); + } + return 0.SI<Watt>(); + } - private static IEnumerable<SI> CalculateAverageOverSeconds(IEnumerable<Second> dtValues, - IEnumerable<MeterPerSquareSecond> accValues) + public static MeterPerSecond Speed(this IModalDataWriter data) { - var dtSum = 0.SI().Second; - var accSum = 0.SI().Meter.Per.Second; - var acceleration = dtValues.Zip(accValues, (dt, acc) => new { dt, acc }).ToList(); - foreach (var x in acceleration.ToList()) { - var currentX = x; + return Distance(data) / Duration(data); + } - while (dtSum + currentX.dt >= 1) { - var splitX = new { dt = 1.SI<Second>() - dtSum, currentX.acc }; - yield return accSum; - dtSum = 0.SI<Second>(); - accSum = 0.SI<MeterPerSecond>(); + public static WattSecond AuxiliaryWork(this IModalDataWriter data, DataColumn auxCol) + { + var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval); + return data.GetValues<Watt>(auxCol).Zip(simulationIntervals, (value, dt) => value * dt).Sum().Cast<WattSecond>(); + } + + + private static T TimeIntegral<T>(this IModalDataWriter data, ModalResultField field, Func<SI, bool> filter = null) + where T : SIBase<T> + { + var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval); + var filteredList = data.GetValues<SI>(field) + .Zip(simulationIntervals, (value, dt) => new { value, dt }) + .Where(x => filter == null || filter(x.value)).ToList(); - currentX = new { dt = currentX.dt - splitX.dt, currentX.acc }; + return filteredList.Any() + ? filteredList.Select(x => (x.value == null ? SIBase<T>.Create(0) : x.value * x.dt)).Sum().Cast<T>() + : SIBase<T>.Create(0); + } + + private static IEnumerable<MeterPerSquareSecond> AccelerationPer3Seconds(IModalDataWriter data) + { + var accelerationAverages = AccelerationPerSecond(data).ToList(); + var runningAverage = (accelerationAverages[0] + accelerationAverages[1] + accelerationAverages[2]) / 3.0; + yield return runningAverage; + + for (var i = 2; i < accelerationAverages.Count() - 1; i++) { + runningAverage -= accelerationAverages[i - 2] / 3.0; + runningAverage += accelerationAverages[i + 1] / 3.0; + yield return runningAverage; + } + } + + /// <summary> + /// Calculates the average acceleration for whole seconds. + /// </summary> + private static IEnumerable<MeterPerSquareSecond> AccelerationPerSecond(IModalDataWriter data) + { + var dtSum = 0.SI<Second>(); + var accAvg = 0.SI<MeterPerSecond>(); + + var accValues = data.GetValues<MeterPerSquareSecond>(ModalResultField.acc); + + foreach (var value in accValues.Zip(SimulationIntervals(data), (acc, dt) => new { acc, dt })) { + var dt = value.dt; + var acc = value.acc; + + while (dtSum + dt >= 1) { + var diffDt = 1.SI<Second>() - dtSum; + yield return (accAvg + acc * diffDt) / 1.SI<Second>(); + dt -= diffDt; + dtSum = 0.SI<Second>(); + accAvg = 0.SI<MeterPerSecond>(); } - if (currentX.dt > 0) { - accSum += currentX.dt * currentX.acc ?? 0.SI(); - dtSum += currentX.dt; + if (dt > 0) { + accAvg += acc * dt; + dtSum += dt; } } // return remaining data. acts like extrapolation to next whole second. if (dtSum > 0) { - yield return accSum; + yield return accAvg / 1.SI<Second>(); } } } diff --git a/VectoCore/Models/Simulation/Data/SummaryFileWriter.cs b/VectoCore/Models/Simulation/Data/SummaryFileWriter.cs index d6d221a3d814e0b84af2ee0ea3199b68a026afb4..63ee0db2724161e67b20bc3363ccf13fa533b519 100644 --- a/VectoCore/Models/Simulation/Data/SummaryFileWriter.cs +++ b/VectoCore/Models/Simulation/Data/SummaryFileWriter.cs @@ -8,7 +8,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Data { public delegate void WriteSumData(IModalDataWriter data, Kilogram vehicleMass, Kilogram loading); - /// <summary> /// Class for the sum file in vecto. /// </summary> @@ -53,10 +52,10 @@ namespace TUGraz.VectoCore.Models.Simulation.Data private const string PCRUISE = "pCruise [%]"; private const string PSTOP = "pStop [%]"; private const string ETORQUECONV = "Etorqueconv [kWh]"; - private const string CO2 = "CO2 [g/km]"; - private const string CO2T = "CO2 [g/tkm]"; + private const string CO2KM = "CO2 [g/km]"; + private const string CO2TKM = "CO2 [g/tkm]"; private const string FCFINAL = "FC-Final [g/km]"; - private const string FCFINAL_LITER = "FC-Final [l/100km]"; + private const string FCFINAL_LITERPER100KM = "FC-Final [l/100km]"; private const string FCFINAL_LITERPER100TKM = "FC-Final [l/100tkm]"; private const string ACCNOISE = "Acc.Noise [m/s^2]"; // ReSharper restore InconsistentNaming @@ -67,7 +66,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Data protected SummaryFileWriter() {} - private IList<string> _auxColumns = new List<string>(); + private readonly IList<string> _auxColumns = new List<string>(); /// <summary> /// Initializes a new instance of the <see cref="SummaryFileWriter"/> class. @@ -86,11 +85,13 @@ namespace TUGraz.VectoCore.Models.Simulation.Data _table.Columns.AddRange(new[] { TIME, DISTANCE, SPEED, ALTITUDE, PPOS, PNEG, FCMAP, FCMAPKM, FCAUXC, FCAUXCKM, FCWHTCC, FCWHTCCKM, PWHEELPOS, PBRAKE, EPOSICE, ENEGICE, EAIR, EROLL, EGRAD, EACC, EAUX, EBRAKE, ETRANSM, ERETARDER, MASS, LOADING, ACCELERATIONS, APOS, - ANEG, PACC, PDEC, PCRUISE, PSTOP, ETORQUECONV, CO2, CO2T, FCFINAL, FCFINAL_LITER, FCFINAL_LITERPER100TKM, ACCNOISE + ANEG, PACC, PDEC, PCRUISE, PSTOP, ETORQUECONV, CO2KM, CO2TKM, FCFINAL, FCFINAL_LITERPER100KM, FCFINAL_LITERPER100TKM, + ACCNOISE }.Select(x => new DataColumn(x, typeof(SI))).ToArray()); } - public virtual void Write(bool isEngineOnly, IModalDataWriter data, string jobFileName, string jobName, string cycleFileName, + public virtual void Write(bool isEngineOnly, IModalDataWriter data, string jobFileName, string jobName, + string cycleFileName, Kilogram vehicleMass, Kilogram vehicleLoading) { if (isEngineOnly) { @@ -110,12 +111,11 @@ namespace TUGraz.VectoCore.Models.Simulation.Data row[CYCLE] = cycleFileName; row[STATUS] = data.RunStatus; row[TIME] = data.Duration(); - row[PPOS] = data.Ppos(); - row[PNEG] = data.Pneg(); - row[FCMAP] = data.FuelConsumption(); - row[FCAUXC] = data.FuelConsumptionAuxStartStopCorrected(); - row[FCWHTCC] = data.FuelConsumptionWHTCCorrected(); - + row[PPOS] = data.EnginePowerPositiveAverage().ConvertTo().Kilo.Watt; + row[PNEG] = data.EnginePowerNegativeAverage().ConvertTo().Kilo.Watt; + row[FCMAP] = data.FuelConsumptionPerSecond().ConvertTo().Gramm.Per.Hour; + row[FCAUXC] = data.FuelConsumptionAuxStartStopCorrectedPerSecond().ConvertTo().Gramm.Per.Hour; + row[FCWHTCC] = data.FuelConsumptionWHTCCorrectedPerSecond().ConvertTo().Gramm.Per.Hour; WriteAuxiliaries(data, row); _table.Rows.Add(row); @@ -128,74 +128,70 @@ namespace TUGraz.VectoCore.Models.Simulation.Data _engineOnly = false; var row = _table.NewRow(); + _table.Rows.Add(row); row[JOB] = jobName; row[INPUTFILE] = jobFileName; row[CYCLE] = cycleFileName; row[STATUS] = data.RunStatus; row[TIME] = data.Duration(); - row[DISTANCE] = data.Distance(); - row[SPEED] = data.Speed(); - row[PPOS] = data.Ppos(); - row[PNEG] = data.Pneg(); - row[FCMAP] = data.FuelConsumption(); - row[FCMAPKM] = data.FuelConsumptionPerKilometer(); - row[FCAUXC] = data.FuelConsumptionAuxStartStopCorrected(); - row[FCWHTCC] = data.FuelConsumptionWHTCCorrected(); - row[PBRAKE] = data.PowerBrake(); - row[EPOSICE] = data.WorkEnginePositive(); - row[ENEGICE] = data.WorkEngineNegative(); - row[EAIR] = data.WorkAirResistance(); - row[EROLL] = data.WorkRollingResistance(); - row[EGRAD] = data.WorkRoadGradientResistance(); - row[EAUX] = data.WorkAuxiliaries(); - row[EBRAKE] = data.WorkTotalMechanicalBrake(); - row[ETRANSM] = data.WorkTransmission(); - row[ERETARDER] = data.PowerLossRetarder(); - row[EACC] = data.PowerAccelerations(); + row[DISTANCE] = data.Distance().ConvertTo().Kilo.Meter; + row[SPEED] = data.Speed().ConvertTo().Kilo.Meter.Per.Hour; row[ALTITUDE] = data.AltitudeDelta(); - - WriteAuxiliaries(data, row); - - if (vehicleMass != null) { - row[MASS] = vehicleMass; + row[PPOS] = data.EnginePowerPositiveAverage().ConvertTo().Kilo.Watt; + row[PNEG] = data.EnginePowerNegativeAverage().ConvertTo().Kilo.Watt; + row[FCFINAL] = data.FuelConsumptionFinal().ConvertTo().Gramm.Per.Kilo.Meter; + row[FCFINAL_LITERPER100KM] = data.FuelConsumptionFinalLiterPer100Kilometer(); + if (!vehicleLoading.IsEqual(0)) { + row[FCFINAL_LITERPER100TKM] = data.FuelConsumptionFinalLiterPer100Kilometer() / vehicleLoading.ConvertTo().Ton; } - - if (vehicleLoading != null) { - row[LOADING] = vehicleLoading; + row[FCMAP] = data.FuelConsumptionPerSecond().ConvertTo().Gramm.Per.Hour; + row[FCMAPKM] = data.FuelConsumptionPerMeter().ConvertTo().Gramm.Per.Kilo.Meter; + row[FCAUXC] = data.FuelConsumptionAuxStartStopCorrectedPerSecond().ConvertTo().Gramm.Per.Hour; + row[FCAUXCKM] = data.FuelConsumptionAuxStartStopCorrected().ConvertTo().Gramm.Per.Kilo.Meter; + row[FCWHTCC] = data.FuelConsumptionWHTCCorrectedPerSecond().ConvertTo().Gramm.Per.Hour; + row[FCWHTCCKM] = data.FuelConsumptionWHTCCorrected().ConvertTo().Gramm.Per.Kilo.Meter; + row[CO2KM] = data.CO2PerMeter().ConvertTo().Gramm.Per.Kilo.Meter; + if (!vehicleLoading.IsEqual(0)) { + row[CO2TKM] = data.CO2PerMeter().ConvertTo().Gramm.Per.Kilo.Meter / vehicleLoading.ConvertTo().Ton; } - - row[ACCELERATIONS] = data.AverageAccelerations(); - row[APOS] = data.AverageAccelerationsPositive(); - row[ANEG] = data.AverageAccelerationsNegative(); + row[PWHEELPOS] = data.PowerWheelPositive().ConvertTo().Kilo.Watt; + row[PBRAKE] = data.PowerBrake().ConvertTo().Kilo.Watt; + row[EPOSICE] = data.EngineWorkPositive().ConvertTo().Kilo.Watt.Hour; + row[ENEGICE] = data.EngineWorkNegative().ConvertTo().Kilo.Watt.Hour; + row[EAIR] = data.WorkAirResistance().ConvertTo().Kilo.Watt.Hour; + row[EROLL] = data.WorkRollingResistance().ConvertTo().Kilo.Watt.Hour; + row[EGRAD] = data.WorkRoadGradientResistance().ConvertTo().Kilo.Watt.Hour; + row[EACC] = data.PowerAccelerations().ConvertTo().Kilo.Watt.Hour; + row[EAUX] = data.WorkAuxiliaries().ConvertTo().Kilo.Watt.Hour; + WriteAuxiliaries(data, row); + row[EBRAKE] = data.WorkTotalMechanicalBrake().ConvertTo().Kilo.Watt.Hour; + row[ETRANSM] = data.WorkTransmission().ConvertTo().Kilo.Watt.Hour; + row[ERETARDER] = data.WorkRetarder().ConvertTo().Kilo.Watt.Hour; + row[ETORQUECONV] = data.WorkTorqueConverter().ConvertTo().Kilo.Watt.Hour; + row[MASS] = vehicleMass; + row[LOADING] = vehicleLoading; + row[ACCELERATIONS] = data.AccelerationAverage(); + row[APOS] = data.AccelerationsPositive3SecondAverage(); + row[ANEG] = data.AverageAccelerations3SecondNegative(); + row[ACCNOISE] = data.AccelerationNoise(); row[PACC] = data.PercentAccelerationTime(); row[PDEC] = data.PercentDecelerationTime(); row[PCRUISE] = data.PercentCruiseTime(); row[PSTOP] = data.PercentStopTime(); - - _table.Rows.Add(row); } [MethodImpl(MethodImplOptions.Synchronized)] private void WriteAuxiliaries(IModalDataWriter data, DataRow row) { - var simulationInterval = data.GetValues<Second>(ModalResultField.simulationInterval).ToArray(); - - _auxColumns = _auxColumns.Union(data.Auxiliaries.Select(kv => "Eaux_" + kv.Key + " [kWh]")).ToList(); - - var sum = 0.SI().Kilo.Watt.Hour.ConvertTo().Kilo.Watt.Hour; foreach (var aux in data.Auxiliaries) { var colName = "Eaux_" + aux.Key + " [kWh]"; if (!_table.Columns.Contains(colName)) { _table.Columns.Add(colName, typeof(SI)); + _auxColumns.Add(colName); } - var currentSum = data.GetValues<Watt>(aux.Value) - .Zip(simulationInterval, (P, dt) => P.ConvertTo().Kilo.Watt * dt.ConvertTo().Hour) - .Sum(); - row[colName] = currentSum; - sum += currentSum; + row[colName] = data.AuxiliaryWork(aux.Value).ConvertTo().Kilo.Watt.Hour; } - row[EAUX] = sum; } public virtual void Finish() @@ -210,13 +206,16 @@ namespace TUGraz.VectoCore.Models.Simulation.Data dataColumns.AddRange(_auxColumns); dataColumns.AddRange(new[] { - PPOS, PNEG, FCMAP, FCMAPKM, FCAUXC, FCAUXCKM, FCWHTCC, FCWHTCCKM, CO2, CO2T, FCFINAL, FCFINAL_LITERPER100TKM, - FCFINAL_LITER, PWHEELPOS, PBRAKE, EPOSICE, ENEGICE, EAIR, EROLL, EGRAD, EACC, EAUX, EBRAKE, ETRANSM, + PPOS, PNEG, FCMAP, FCMAPKM, FCAUXC, FCAUXCKM, FCWHTCC, FCWHTCCKM, CO2KM, CO2TKM, FCFINAL, FCFINAL_LITERPER100KM, + FCFINAL_LITERPER100TKM, PWHEELPOS, PBRAKE, EPOSICE, ENEGICE, EAIR, EROLL, EGRAD, EACC, EAUX, EBRAKE, ETRANSM, ERETARDER, ETORQUECONV, MASS, LOADING, ACCELERATIONS, APOS, ANEG, ACCNOISE, PACC, PDEC, PCRUISE, PSTOP }); } - VectoCSVFile.Write(_sumFileName, new DataView(_table).ToTable(false, dataColumns.ToArray())); + var sortedAndFilteredTable = new DataView(_table, "", JOB, DataViewRowState.CurrentRows).ToTable(false, + dataColumns.ToArray()); + + VectoCSVFile.Write(_sumFileName, sortedAndFilteredTable); } } } \ No newline at end of file diff --git a/VectoCore/Models/Simulation/Data/VectoRunData.cs b/VectoCore/Models/Simulation/Data/VectoRunData.cs index 6ecf013f7f2f958d38ae135b5c7b89fc31afeb02..2237d4ec074816cf68a48bab2e33567290e7373c 100644 --- a/VectoCore/Models/Simulation/Data/VectoRunData.cs +++ b/VectoCore/Models/Simulation/Data/VectoRunData.cs @@ -2,7 +2,6 @@ using System.Runtime.Serialization; using TUGraz.VectoCore.Models.Declaration; using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Impl; using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.Simulation.Data diff --git a/VectoCore/Models/Simulation/Impl/JobContainer.cs b/VectoCore/Models/Simulation/Impl/JobContainer.cs index e6e7c135834f5c656e46aa04d5264e5ab001da43..e25d268d6fac0a72885553c0c2c917b0ae00922b 100644 --- a/VectoCore/Models/Simulation/Impl/JobContainer.cs +++ b/VectoCore/Models/Simulation/Impl/JobContainer.cs @@ -80,9 +80,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl entry.Started = true; entry.Worker.RunWorkerAsync(); } - //Task.WaitAll(_runs.Select(r => Task.Factory.StartNew(r.Run)).ToArray()); - - //_sumWriter.Finish(); } public void Cancel() diff --git a/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs index 8006c017cce5c6e7b01d56fff6ee8352e2b11b77..4093dcfea6f4ac5b61d3f21595bca8f46cd3842b 100644 --- a/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs +++ b/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Threading; using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.FileIO.Reader; @@ -11,6 +12,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl { public class SimulatorFactory : LoggingObject { + private static int _jobNumberCounter; + public enum FactoryMode { EngineeringMode, @@ -23,7 +26,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl public SimulatorFactory(FactoryMode mode, string jobFile) { Log.Fatal("########## VectoCore Version {0} ##########", Assembly.GetExecutingAssembly().GetName().Version); - JobNumber = 0; + JobNumber = Interlocked.Increment(ref _jobNumberCounter); _mode = mode; switch (mode) { case FactoryMode.DeclarationMode: @@ -67,7 +70,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl modWriter.WriteModalResults = WriteModalResults; var builder = new PowertrainBuilder(modWriter, DataReader.IsEngineOnly, (writer, mass, loading) => - SumWriter.Write(d.IsEngineOnly, modWriter, d.JobFileName, string.Format("{0}-{1}", JobNumber, i++), d.Cycle.Name, + SumWriter.Write(d.IsEngineOnly, modWriter, d.JobFileName, string.Format("{0}-{1}", JobNumber, i++), + d.Cycle.Name + ".vdri", mass, loading)); VectoRun run; diff --git a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs index 32a8a3d1d524a9ce294039f49179a53bd0bb5c2e..226e25fb27efbb63fb88aea5b9ec8437d3f0f701 100644 --- a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs +++ b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs @@ -23,6 +23,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public EngineFullLoadCurve FullLoadCurve { get; internal set; } + private double _whtcCorrectionFactor = 1; + + public double WHTCCorrectionFactor + { + get { return _whtcCorrectionFactor; } + internal set { _whtcCorrectionFactor = value; } + } + #region Equality Member protected bool Equals(CombustionEngineData other) @@ -62,6 +70,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data return hashCode; } } + #endregion } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 2f03a9ebc611fc6e70f8d2cb42aebe84745a0a7a..0ae4c257d05e5b1a7843e7aa4753a42bb0c770b3 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -6,6 +6,7 @@ using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Connector.Ports.Impl; +using TUGraz.VectoCore.Models.Declaration; using TUGraz.VectoCore.Models.Simulation; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.DataBus; @@ -214,8 +215,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl writer[ModalResultField.n] = CurrentState.EngineSpeed; try { - writer[ModalResultField.FCMap] = - Data.ConsumptionMap.GetFuelConsumption(CurrentState.EngineTorque, CurrentState.EngineSpeed); + var fc = Data.ConsumptionMap.GetFuelConsumption(CurrentState.EngineTorque, CurrentState.EngineSpeed); + writer[ModalResultField.FCMap] = fc; + + //todo (MK, 2015-11-11): calculate aux start stop correction when start stop functionality is implemented in v3 + var fcaux = fc; + writer[ModalResultField.FCAUXc] = fcaux; + writer[ModalResultField.FCWHTCc] = fcaux * Data.WHTCCorrectionFactor; } catch (VectoException ex) { Log.Warn("t: {0} - {1} n: {2} Tq: {3}", CurrentState.AbsTime, ex.Message, CurrentState.EngineSpeed, CurrentState.EngineTorque); diff --git a/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/Models/SimulationComponent/Impl/Driver.cs index 275879fad82af33c285b6d580a5d48996a55a6af..048cda32490370f53fbc65dce215e35f4f920057 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Driver.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Driver.cs @@ -1,15 +1,12 @@ using System; -using System.CodeDom; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Security.Cryptography.X509Certificates; using NLog; using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Connector.Ports.Impl; -using TUGraz.VectoCore.Models.Declaration; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.DataBus; using TUGraz.VectoCore.Models.Simulation.Impl; diff --git a/VectoCore/Utils/IEnumberableExtensionMethods.cs b/VectoCore/Utils/IEnumberableExtensionMethods.cs index 1ffc5b57fe5933d844a1524260ea7a7fb41eb727..48cc64ca4bc85515389666dcfd381d7734be3145 100644 --- a/VectoCore/Utils/IEnumberableExtensionMethods.cs +++ b/VectoCore/Utils/IEnumberableExtensionMethods.cs @@ -49,6 +49,12 @@ namespace TUGraz.VectoCore.Utils return valueList.Any() ? valueList.Aggregate((sum, current) => sum + current) : null; } + public static T Average<T>(this IEnumerable<T> values) where T : SIBase<T> + { + var valueList = values.ToList(); + return valueList.Any() ? valueList.Aggregate((sum, current) => sum + current) / valueList.Count : null; + } + public static SI Sum(this IEnumerable<SI> values) { var valueList = values.ToList(); diff --git a/VectoCore/Utils/Physics.cs b/VectoCore/Utils/Physics.cs index c814e3c0e53e058a8901faf55f7e27045914bcf1..2b18f0625e35034482339a7be26aec20a67335b5 100644 --- a/VectoCore/Utils/Physics.cs +++ b/VectoCore/Utils/Physics.cs @@ -20,7 +20,7 @@ /// <summary> - /// Factor to convert from fuel weight to co2 weight. + /// fuel[kg] => co2[kg]. Factor to convert from fuel weight to co2 weight. /// </summary> public static readonly double CO2PerFuelWeight = 3.16; } diff --git a/VectoCore/Utils/SI.cs b/VectoCore/Utils/SI.cs index e17a8fb4c99f5df99f876e6c3da8be09a898f1b1..77e17394349ec46ddbe8b8c9151dcd1d3e644c32 100644 --- a/VectoCore/Utils/SI.cs +++ b/VectoCore/Utils/SI.cs @@ -30,11 +30,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator +. /// </summary> - /// <param name="si1">The si1.</param> - /// <param name="si2">The si2.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Scalar operator +(Scalar si1, Scalar si2) { @@ -44,11 +39,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator +. /// </summary> - /// <param name="si1">The si1.</param> - /// <param name="si2">The si2.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Scalar operator +(Scalar si1, double si2) { @@ -58,11 +48,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator +. /// </summary> - /// <param name="si1">The si1.</param> - /// <param name="si2">The si2.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Scalar operator +(double si1, Scalar si2) { @@ -72,11 +57,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator -. /// </summary> - /// <param name="si1">The si1.</param> - /// <param name="si2">The si2.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Scalar operator -(Scalar si1, Scalar si2) { @@ -86,11 +66,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator -. /// </summary> - /// <param name="si1">The si1.</param> - /// <param name="si2">The si2.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Scalar operator -(Scalar si1, double si2) { @@ -100,11 +75,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator -. /// </summary> - /// <param name="si1">The si1.</param> - /// <param name="si2">The si2.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Scalar operator -(double si1, Scalar si2) { @@ -178,18 +148,14 @@ namespace TUGraz.VectoCore.Utils Denominator = new[] { Unit.s, Unit.s }; } + /// <summary> - /// Implements the operator /. + /// Implements the operator *. /// </summary> - /// <param name="meterPerSecond">The meter per second.</param> - /// <param name="meterPerSquareSecond">The meter per square second.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] - public static Second operator /(MeterPerSecond meterPerSecond, MeterPerSquareSecond meterPerSquareSecond) + public static MeterPerSecond operator *(MeterPerSquareSecond meterPerSecond, Second second) { - return SIBase<Second>.Create(meterPerSecond.Value() / meterPerSquareSecond.Val); + return SIBase<MeterPerSecond>.Create(meterPerSecond.Val * second.Value()); } } @@ -215,6 +181,32 @@ namespace TUGraz.VectoCore.Utils { Numerator = new[] { Unit.m }; } + + [DebuggerHidden] + public static MeterPerSecond operator /(Meter meter, Second second) + { + return ((meter as SI) / second).Cast<MeterPerSecond>(); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + [DebuggerHidden] + public static Second operator /(Meter second, MeterPerSecond meterPerSecond) + { + return SIBase<Second>.Create(second.Val / meterPerSecond.Value()); + } + } + + public class KilogramPerMeter : SIBase<KilogramPerMeter> + { + [JsonConstructor, DebuggerHidden] + protected KilogramPerMeter(double val) + : base(val) + { + Numerator = new[] { Unit.k, Unit.g }; + Denominator = new[] { Unit.m }; + } } /// <summary> @@ -227,6 +219,18 @@ namespace TUGraz.VectoCore.Utils { Numerator = new[] { Unit.k, Unit.g }; } + + [DebuggerHidden] + public static KilogramPerSecond operator /(Kilogram kg, Second second) + { + return SIBase<KilogramPerSecond>.Create(kg.Val / second.Value()); + } + + [DebuggerHidden] + public static KilogramPerMeter operator /(Kilogram kg, Meter m) + { + return SIBase<KilogramPerMeter>.Create(kg.Val / m.Value()); + } } /// <summary> @@ -312,6 +316,22 @@ namespace TUGraz.VectoCore.Utils } } + public class WattSecond : SIBase<WattSecond> + { + [JsonConstructor, DebuggerHidden] + protected WattSecond(double val) : base(val) + { + Numerator = new[] { Unit.W, Unit.s }; + } + + [DebuggerHidden] + public static Watt operator /(WattSecond wattSecond, Second second) + { + return SIBase<Watt>.Create(wattSecond.Val / second.Value()); + } + } + + /// <summary> /// SI Class for Watt [W]. /// </summary> @@ -350,6 +370,12 @@ namespace TUGraz.VectoCore.Utils { return SIBase<NewtonMeter>.Create(watt.Val / perSecond.Value()); } + + [DebuggerHidden] + public static WattSecond operator *(Watt watt, Second second) + { + return SIBase<WattSecond>.Create(watt.Val * second.Value()); + } } /// <summary> @@ -381,11 +407,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator /. /// </summary> - /// <param name="meterPerSecond">The meter per second.</param> - /// <param name="meter">The meter.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static PerSecond operator /(MeterPerSecond meterPerSecond, Meter meter) { @@ -395,25 +416,24 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator /. /// </summary> - /// <param name="second">The second.</param> - /// <param name="meterPerSecond">The meter per second.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] - public static Second operator /(Meter second, MeterPerSecond meterPerSecond) + public static Second operator /(MeterPerSecond meterPerSecond, MeterPerSquareSecond meterPerSquareSecond) + { + return SIBase<Second>.Create(meterPerSecond.Val / meterPerSquareSecond.Value()); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + [DebuggerHidden] + public static MeterPerSquareSecond operator /(MeterPerSecond meterPerSecond, Second second) { - return SIBase<Second>.Create(second.Value() / meterPerSecond.Val); + return SIBase<MeterPerSquareSecond>.Create(meterPerSecond.Val / second.Value()); } /// <summary> /// Implements the operator *. /// </summary> - /// <param name="meterPerSecond">The meter per second.</param> - /// <param name="second">The second.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Meter operator *(MeterPerSecond meterPerSecond, Second second) { @@ -423,11 +443,6 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Implements the operator *. /// </summary> - /// <param name="meterPerSecond">The meter per second.</param> - /// <param name="second">The second.</param> - /// <returns> - /// The result of the operator. - /// </returns> [DebuggerHidden] public static Meter operator *(Second second, MeterPerSecond meterPerSecond) { @@ -1592,6 +1607,17 @@ namespace TUGraz.VectoCore.Utils return lower <= Val && Val <= upper; } + /// <summary> + /// Determines whether the SI is between lower and uppper bound. + /// </summary> + /// <param name="lower">The lower bound.</param> + /// <param name="upper">The upper bound.</param> + /// <returns></returns> + public bool IsBetween(double lower, double upper) + { + return lower <= Val && Val <= upper; + } + #endregion #region ToString diff --git a/VectoCoreTest/Integration/DeclarationReportTest.cs b/VectoCoreTest/Integration/DeclarationReportTest.cs index a2b6d3d6b0e917d3d402ac3bddf65e4dd4255040..ffc0f31ea65f3cb81d6f3a7abc54eca4a90189c3 100644 --- a/VectoCoreTest/Integration/DeclarationReportTest.cs +++ b/VectoCoreTest/Integration/DeclarationReportTest.cs @@ -1,4 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.IO; +using System.Threading; +using Microsoft.VisualStudio.TestTools.UnitTesting; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.Impl; @@ -10,18 +12,33 @@ namespace TUGraz.VectoCore.Tests.Integration [TestMethod] public void DeclarationReport_Test() { - var sumWriter = new SummaryFileWriter(@"job-report.vsum"); - var jobContainer = new JobContainer(sumWriter); + if (File.Exists("job-report.vsum")) { + File.Delete("job-report.vsum"); + } + + if (File.Exists("job-report.pdf")) { + File.Delete("job-report.pdf"); + } - var factory = new SimulatorFactory(SimulatorFactory.FactoryMode.DeclarationMode, @"TestData\Jobs\job-report.vecto"); + var sumWriter = + new SummaryFileWriter( + @"C:\Users\Krisper\Documents\vecto-sim\Generic Vehicles\Declaration Mode\40t Long Haul Truck\job-report.vsum"); + var jobContainer = new JobContainer(sumWriter); + var factory = new SimulatorFactory(SimulatorFactory.FactoryMode.DeclarationMode, + @"C:\Users\Krisper\Documents\vecto-sim\Generic Vehicles\Declaration Mode\40t Long Haul Truck\40t_Long_Haul_Truck.vecto"); + //var factory = new SimulatorFactory(SimulatorFactory.FactoryMode.DeclarationMode, @"TestData\Jobs\job-report.vecto"); jobContainer.AddRuns(factory); jobContainer.Execute(); - //ResultFileHelper.TestSumFile(@"TestData\Results\Integration\job.vsum", @"job.vsum"); + jobContainer.WaitFinished(); - //ResultFileHelper.TestModFile(@"TestData\Results\Integration\job_1-Gear-Test-dist.vmod", - // @"TestData\job_1-Gear-Test-dist.vmod", testRowCount: false); + Assert.IsTrue( + File.Exists( + @"C:\Users\Krisper\Documents\vecto-sim\Generic Vehicles\Declaration Mode\40t Long Haul Truck\job-report.vsum")); + Assert.IsTrue( + File.Exists( + @"C:\Users\Krisper\Documents\vecto-sim\Generic Vehicles\Declaration Mode\40t Long Haul Truck\40t_Long_Haul_Truck.pdf")); } } } \ No newline at end of file