diff --git a/VectoCore/Models/Declaration/DeclarationReport.cs b/VectoCore/Models/Declaration/DeclarationReport.cs index bce841bab6952ea46766500d3b83fff4268515bd..82495c5d619cceb552c4cf7a96199822adaa97dc 100644 --- a/VectoCore/Models/Declaration/DeclarationReport.cs +++ b/VectoCore/Models/Declaration/DeclarationReport.cs @@ -75,7 +75,7 @@ namespace TUGraz.VectoCore.Models.Declaration private void WriteReport() { var titlePage = CreateTitlePage(_missions); - var cyclePages = _missions.Select((m, i) => CreateCyclePage(m.Value, i, _missions.Count)); + var cyclePages = _missions.Select((m, i) => CreateCyclePage(m.Value, i + 2, _missions.Count + 1)); MergeDocuments(titlePage, cyclePages, Path.Combine(_basePath, _jobFile + ".pdf")); } @@ -84,10 +84,11 @@ namespace TUGraz.VectoCore.Models.Declaration { var stream = new MemoryStream(); - var reader = - new PdfReader( - RessourceHelper.ReadStream(RessourceHelper.Namespace + - string.Format("Report.title{0}CyclesTemplate.pdf", missions.Count))); + 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; @@ -96,32 +97,42 @@ namespace TUGraz.VectoCore.Models.Declaration pdfFields.SetField("Date", DateTime.Now.ToString(CultureInfo.InvariantCulture)); pdfFields.SetField("Created", _creator); pdfFields.SetField("Config", - string.Format("{0:0.0}t {1} {2}", _segment.GrossVehicleMassRating / 1000, _segment.AxleConfiguration.GetName(), + string.Format(CultureInfo.InvariantCulture, "{0:0.0}t {1} {2}", + _segment.GrossVehicleMassRating.ConvertTo().Ton.Value(), + _segment.AxleConfiguration.GetName(), _segment.VehicleCategory)); - pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass); + pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass.GetClassNumber()); pdfFields.SetField("Engine", _engineStr); pdfFields.SetField("EngM", _engineModel); pdfFields.SetField("Gearbox", _gearboxStr); pdfFields.SetField("GbxM", _gearboxModel); - pdfFields.SetField("PageNr", "Page 1 of " + missions.Count); + pdfFields.SetField("PageNr", string.Format("Page {0} of {1}", 1, missions.Count + 1)); var i = 1; - foreach (var results in missions.Values) { + 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<SI>(ModalResultField.simulationInterval); - var distance = m.GetValues<SI>(ModalResultField.dist).Max().Value(); + var dt = m.GetValues<Second>(ModalResultField.simulationInterval); + var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); + var maxTime = m.GetValues<Second>(ModalResultField.time).Max(); - Func<ModalResultField, double> avgWeighted = - field => m.GetValues<SI>(field).Zip(dt, (v, t) => v / t).Average().Value(); + var avgSpeed = maxDistance / maxTime; pdfFields.SetField("Loading" + i, results.Mission.RefLoad.Value().ToString("0.0") + " t"); - pdfFields.SetField("Speed" + i, avgWeighted(ModalResultField.v_act).ToString("0.0") + " km/h"); + pdfFields.SetField("Speed" + i, + string.Format(CultureInfo.InvariantCulture, "{0:0.0} km/h", avgSpeed.ConvertTo().Kilo.Meter.Per.Hour)); var loading = results.Mission.RefLoad.Value(); - var fc = avgWeighted(ModalResultField.FCMap) / distance * 1000 * 100; + var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var fcSum = fc.Zip(dt, (fcVal, dtVal) => fcVal * dtVal).Sum(); + + var fcPerM = fcSum / maxDistance; + + + //var co2 = fcPerM.ConvertTo().Gramm * Physics.CO2PerFuelGram; + // todo: calc co2 var co2 = fc; @@ -160,26 +171,8 @@ namespace TUGraz.VectoCore.Models.Declaration return stream; } - private static string GetHDVClassImageName(Segment segment, MissionType missionType) - { - switch (segment.VehicleClass) { - case VehicleClass.Class1: - case VehicleClass.Class2: - case VehicleClass.Class3: - return "4x2r.png"; - case VehicleClass.Class4: - return missionType == MissionType.LongHaul ? "4x2rt.png" : "4x2r.png"; - case VehicleClass.Class5: - return "4x2tt.png"; - case VehicleClass.Class9: - return missionType == MissionType.LongHaul ? "6x2rt.png" : "6x2r.png"; - case VehicleClass.Class10: - return "6x2tt.png"; - } - return "Undef.png"; - } - private Stream CreateCyclePage(ResultContainer results, int i, int pgMax) + private Stream CreateCyclePage(ResultContainer results, int currentPageNr, int pageCount) { var stream = new MemoryStream(); @@ -195,7 +188,7 @@ namespace TUGraz.VectoCore.Models.Declaration string.Format("{0:0.0}t {1} {2}", _segment.GrossVehicleMassRating / 1000, _segment.AxleConfiguration.GetName(), _segment.VehicleCategory)); pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass); - pdfFields.SetField("PageNr", "Page " + (i + 1) + " of " + pgMax); + pdfFields.SetField("PageNr", string.Format("Page {0} of {1}", currentPageNr, pageCount)); pdfFields.SetField("Mission", results.Mission.MissionType.ToString()); @@ -303,21 +296,28 @@ namespace TUGraz.VectoCore.Models.Declaration BorderWidth = 3 }); + foreach (var missionResult in missions.OrderBy(m => m.Key)) { + var modData = missionResult.Value.ModData[LoadingType.ReferenceLoad]; + var fc = modData.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var dt = modData.GetValues<Second>(ModalResultField.simulationInterval); - foreach (var missionResult in missions) { - var series = new Series(missionResult.Key + " (Ref. load.)"); - var co2sum = missionResult.Value.ModData[LoadingType.ReferenceLoad].GetValues<SI>(ModalResultField.FCMap).Sum(); + var fcSum = fc.Zip(dt, (fcValue, dtValue) => fcValue * dtValue).Sum(); - var maxDistance = missionResult.Value.ModData[LoadingType.ReferenceLoad].GetValues<SI>(ModalResultField.dist).Max(); + var maxDistance = modData.GetValues<Meter>(ModalResultField.dist).Max(); var loading = missionResult.Value.Mission.Loadings[LoadingType.ReferenceLoad]; - var co2pertkm = co2sum / maxDistance / loading * 1000; - series.Points.AddXY(missionResult.Key.ToString(), co2pertkm.Value()); - - series.Points[0].Label = series.Points[0].YValues[0].ToString("0.0") + " [g/tkm]"; - series.Points[0].Font = new Font("Helvetica", 20); - series.Points[0].LabelBackColor = Color.White; + var co2gPerTKM = fcSum.ConvertTo().Gramm * Physics.CO2PerFuelGram / + (loading.ConvertTo().Ton * maxDistance.ConvertTo().Kilo.Meter); + var series = new Series(missionResult.Key + " (Ref. load.)"); + var dataPoint = new DataPoint { + Name = missionResult.Key.ToString(), + YValues = new[] { co2gPerTKM.Value() }, + Label = string.Format(co2gPerTKM.ToOutputFormat(1, showUnit: true)), + Font = new Font("Helvetica", 20), + LabelBackColor = Color.White + }; + series.Points.Add(dataPoint); co2Chart.Series.Add(series); } @@ -344,7 +344,8 @@ namespace TUGraz.VectoCore.Models.Declaration TitleFont = new Font("Helvetica", 20), LabelStyle = { Font = new Font("Helvetica", 20) }, LabelAutoFitStyle = LabelAutoFitStyles.None, - Minimum = 20.0, + Minimum = 0, + Maximum = 80 }, AxisY = { Title = "CO2 [g/km]", @@ -355,22 +356,29 @@ namespace TUGraz.VectoCore.Models.Declaration }); foreach (var missionResult in missions) { - var series = new Series { MarkerSize = 15, MarkerStyle = MarkerStyle.Circle, ChartType = SeriesChartType.Point }; + var series = new Series { + MarkerSize = 15, + MarkerStyle = MarkerStyle.Circle, + ChartType = SeriesChartType.Point + }; foreach (var pair in missionResult.Value.ModData) { - var dt = pair.Value.GetValues<SI>(ModalResultField.simulationInterval).ToDouble(); - var speed = pair.Value.GetValues<SI>(ModalResultField.v_act).ToDouble(); - var distance = pair.Value.GetValues<SI>(ModalResultField.dist).Max().Value(); + var modData = missionResult.Value.ModData[pair.Key]; + var fc = modData.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var dt = modData.GetValues<Second>(ModalResultField.simulationInterval).ToList(); - //todo get co2 value - var co2 = pair.Value.GetValues<SI>(ModalResultField.FCMap).ToDouble(); + var fcSum = fc.Zip(dt, (fcValue, dtValue) => fcValue * dtValue).Sum(); - var avgSpeed = speed.Zip(dt, (v, t) => v / t).Average(); - var avgCO2km = co2.Zip(dt, (co, t) => co / t).Average() / distance * 1000; - var loading = missionResult.Value.Mission.Loadings[pair.Key]; + var maxDistance = modData.GetValues<Meter>(ModalResultField.dist).Max(); + var maxTime = modData.GetValues<Second>(ModalResultField.time).Max(); - var point = new DataPoint(avgSpeed, avgCO2km) { - Label = loading.Value().ToString("0.0") + " t", + var avgKMH = maxDistance.ConvertTo().Kilo.Meter / maxTime.ConvertTo().Hour; + var co2gPerKM = fcSum.ConvertTo().Gramm * Physics.CO2PerFuelGram / maxDistance.ConvertTo().Kilo.Meter; + + var loading = 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()), Font = new Font("Helvetica", 16), LabelBackColor = Color.White }; @@ -523,5 +531,24 @@ namespace TUGraz.VectoCore.Models.Declaration operatingPointsChart.DrawToBitmap(tqnBitmap, new Rectangle(0, 0, tqnBitmap.Width, tqnBitmap.Height)); return tqnBitmap; } + + private static string GetHDVClassImageName(Segment segment, MissionType missionType) + { + switch (segment.VehicleClass) { + case VehicleClass.Class1: + case VehicleClass.Class2: + case VehicleClass.Class3: + return "4x2r.png"; + case VehicleClass.Class4: + return missionType == MissionType.LongHaul ? "4x2rt.png" : "4x2r.png"; + case VehicleClass.Class5: + return "4x2tt.png"; + case VehicleClass.Class9: + return missionType == MissionType.LongHaul ? "6x2rt.png" : "6x2r.png"; + case VehicleClass.Class10: + return "6x2tt.png"; + } + return "Undef.png"; + } } } \ No newline at end of file diff --git a/VectoCore/Models/Declaration/VehicleClass.cs b/VectoCore/Models/Declaration/VehicleClass.cs index fd37847b015aec856203f13ebd6fe785bd256602..d87c74588530a78d45cecea546915500c03d24a1 100644 --- a/VectoCore/Models/Declaration/VehicleClass.cs +++ b/VectoCore/Models/Declaration/VehicleClass.cs @@ -39,9 +39,9 @@ namespace TUGraz.VectoCore.Models.Declaration return text.Replace(Prefix, "").Parse<VehicleClass>(); } - public static string ToString(VehicleClass hdvClass) + public static string GetClassNumber(this VehicleClass hdvClass) { - return Prefix + hdvClass; + return hdvClass.ToString().Substring(Prefix.Length); } } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs b/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs index 251312db42a91cce4bd4898ec8f773213b860688..7043b40b3c9403723bb4da6c5ef2addca9d0aacb 100644 --- a/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs +++ b/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs @@ -61,11 +61,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine /// <param name="engineSpeed">[rad/sec]</param> /// <param name="torque">[Nm]</param> /// <returns>[kg/s]</returns> - public SI GetFuelConsumption(NewtonMeter torque, PerSecond engineSpeed) + public KilogramPerSecond GetFuelConsumption(NewtonMeter torque, PerSecond engineSpeed) { // delauney map needs is initialised with rpm, therefore the engineSpeed has to be converted. return - _fuelMap.Interpolate(torque.Value(), engineSpeed.ConvertTo().Rounds.Per.Minute.Value()).SI().Kilo.Gramm.Per.Second; + _fuelMap.Interpolate(torque.Value(), engineSpeed.ConvertTo().Rounds.Per.Minute.Value()) + .SI() + .Kilo.Gramm.Per.Second.Cast<KilogramPerSecond>(); } private static class Fields diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 33ca3d940e479b53a530cf29a5139ccc1f00ed25..34680b8cc4db67c80f0231f04607c9755d8aed73 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -216,7 +216,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } catch (VectoException ex) { Log.Warn("t: {0} - {1} n: {2} Tq: {3}", CurrentState.AbsTime, ex.Message, CurrentState.EngineSpeed, CurrentState.EngineTorque); - writer[ModalResultField.FCMap] = double.NaN.SI(); + writer[ModalResultField.FCMap] = double.NaN.SI<KilogramPerSecond>(); } } diff --git a/VectoCore/Utils/IEnumberableExtensionMethods.cs b/VectoCore/Utils/IEnumberableExtensionMethods.cs index d4fdb9fd98923178d8fa0281c83dee6dcdeca63e..1ffc5b57fe5933d844a1524260ea7a7fb41eb727 100644 --- a/VectoCore/Utils/IEnumberableExtensionMethods.cs +++ b/VectoCore/Utils/IEnumberableExtensionMethods.cs @@ -43,6 +43,12 @@ namespace TUGraz.VectoCore.Utils } } + public static T Sum<T>(this IEnumerable<T> values) where T : SIBase<T> + { + var valueList = values.ToList(); + return valueList.Any() ? valueList.Aggregate((sum, current) => sum + current) : 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 08cf111224e5271449d131696887c4c1574eb2e6..494ece570d915d5a2f1a7fe354d5b7758f233ecb 100644 --- a/VectoCore/Utils/Physics.cs +++ b/VectoCore/Utils/Physics.cs @@ -9,5 +9,6 @@ public static readonly double RollResistanceExponent = 0.9; public static readonly MeterPerSecond BaseWindSpeed = 3.SI<MeterPerSecond>(); + public static readonly double CO2PerFuelGram = 3.16; } } \ No newline at end of file diff --git a/VectoCore/Utils/SI.cs b/VectoCore/Utils/SI.cs index c6691b6c288e7eae241977dff0531c30c0a885ac..6b6d6ffdc7f2359851e3ce1ad01255e900555303 100644 --- a/VectoCore/Utils/SI.cs +++ b/VectoCore/Utils/SI.cs @@ -210,6 +210,22 @@ namespace TUGraz.VectoCore.Utils protected Kilogram(double val) : base(new SI(val).Kilo.Gramm) {} } + /// <summary> + /// SI Class for Kilogram per Second [kg]. + /// </summary> + public class KilogramPerSecond : SIBase<KilogramPerSecond> + { + [JsonConstructor, DebuggerHidden] + protected KilogramPerSecond(double val) : base(new SI(val).Kilo.Gramm.Per.Second) {} + + [DebuggerHidden] + public static Kilogram operator *(KilogramPerSecond kilogramPerSecond, Second Second) + { + return ((kilogramPerSecond as SI) * Second).Cast<Kilogram>(); + } + } + + /// <summary> /// SI Class for Ton [t] (automatically converts to [kg]) /// </summary> diff --git a/VectoCoreTest/Integration/DeclarationReportTest.cs b/VectoCoreTest/Integration/DeclarationReportTest.cs index f720b4e69349761056c105979b7a8b33b441f156..a2b6d3d6b0e917d3d402ac3bddf65e4dd4255040 100644 --- a/VectoCoreTest/Integration/DeclarationReportTest.cs +++ b/VectoCoreTest/Integration/DeclarationReportTest.cs @@ -8,7 +8,7 @@ namespace TUGraz.VectoCore.Tests.Integration public class DeclarationReportTest { [TestMethod] - public void RunDeclarationMode() + public void DeclarationReport_Test() { var sumWriter = new SummaryFileWriter(@"job-report.vsum"); var jobContainer = new JobContainer(sumWriter);