diff --git a/VectoCore/VectoCore/Models/Simulation/Data/ModalResult.cs b/VectoCore/VectoCore/Models/Simulation/Data/ModalResult.cs index 912f7cad86c66bf3d1ac12b2eb8a7cee166d69a8..e76234c8c5787b17c461d8d870a496f2bf76991f 100644 --- a/VectoCore/VectoCore/Models/Simulation/Data/ModalResult.cs +++ b/VectoCore/VectoCore/Models/Simulation/Data/ModalResult.cs @@ -34,6 +34,7 @@ using System.ComponentModel; using System.Data; using System.Reflection; using System.Runtime.Serialization; +using System.Text.RegularExpressions; using TUGraz.VectoCommon.Exceptions; using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Utils; @@ -387,6 +388,12 @@ namespace TUGraz.VectoCore.Models.Simulation.Data return GetAttribute(field).Caption ?? GetAttribute(field).Name ?? field.ToString(); } + public static string GetShortCaption(this ModalResultField field) + { + var caption = GetCaption(field); + return Regex.Replace(caption, @"\[.*?\]|\<|\>", "").Trim(); + } + public static ModalResultFieldAttribute GetAttribute(this ModalResultField field) { return (ModalResultFieldAttribute)Attribute.GetCustomAttribute(ForValue(field), typeof(ModalResultFieldAttribute)); diff --git a/VectoCore/VectoCore/OutputData/ModalDataContainer.cs b/VectoCore/VectoCore/OutputData/ModalDataContainer.cs index c4c49f516707c3d782650332564eb40bbef0b233..3a9ce33ecfa1191ab5060621f6b01c20dd20ee5c 100644 --- a/VectoCore/VectoCore/OutputData/ModalDataContainer.cs +++ b/VectoCore/VectoCore/OutputData/ModalDataContainer.cs @@ -267,6 +267,7 @@ namespace TUGraz.VectoCore.OutputData public ModalResults Filter(ModalResults data) { var absTime = 0.SI<Second>(); + var distance = 0.SI<Meter>(); var results = (ModalResults)data.Clone(); var remainingDt = 0.SI<Second>(); @@ -276,6 +277,8 @@ namespace TUGraz.VectoCore.OutputData foreach (DataRow row in data.Rows) { var currentDt = row.Field<Second>((int)ModalResultField.simulationInterval); + distance = row.Field<Meter>((int)ModalResultField.dist); + var v_act = (MeterPerSecond)row[(int)ModalResultField.v_act]; // if current + remaining time >= 1 second: take remaining row and split up currentRow to fill up 1 second. if (remainingDt > 0 && remainingDt + currentDt >= 1) { @@ -285,25 +288,36 @@ namespace TUGraz.VectoCore.OutputData var gear = row[(int)ModalResultField.Gear]; gearsList[gear] = gearsList.GetValueOrZero(gear) + diffDt; + distance += diffDt * v_act + diffDt * diffDt * (MeterPerSquareSecond)row[(int)ModalResultField.acc] / 2; + v_act += diffDt * (MeterPerSquareSecond)row[(int)ModalResultField.acc]; r.ItemArray = AddRow(remainingRow, MultiplyRow(row.ItemArray, diffDt)); absTime += diffDt; + r[(int)ModalResultField.time] = absTime; r[(int)ModalResultField.simulationInterval] = 1.SI<Second>(); r[(int)ModalResultField.Gear] = gearsList.MaxBy(kv => kv.Value).Key; + r[(int)ModalResultField.dist] = distance; + gearsList.Clear(); results.Rows.Add(r); - currentDt = VectoMath.Max(remainingDt + currentDt - 1.SI<Second>(), 0.SI<Second>()); + currentDt -= diffDt; remainingDt = 0.SI<Second>(); + remainingRow = null; } // if current row still longer than 1 second: split it to 1 second slices until it is < 1 second while (currentDt >= 1) { currentDt = currentDt - 1.SI<Second>(); + var dt = 1.SI<Second>(); var r = results.NewRow(); r.ItemArray = row.ItemArray; - absTime += 1.SI<Second>(); + absTime += dt; + distance += dt * v_act + dt * dt * (MeterPerSquareSecond)row[(int)ModalResultField.acc] / 2; + v_act += dt * (MeterPerSquareSecond)row[(int)ModalResultField.acc]; + r[(int)ModalResultField.time] = absTime; - r[(int)ModalResultField.simulationInterval] = 1.SI<Second>(); + r[(int)ModalResultField.simulationInterval] = dt; + r[(int)ModalResultField.dist] = distance; results.Rows.Add(r); } @@ -312,6 +326,7 @@ namespace TUGraz.VectoCore.OutputData var gear = row[(int)ModalResultField.Gear]; gearsList[gear] = gearsList.GetValueOrZero(gear) + currentDt; + distance += currentDt * v_act + currentDt * currentDt * (MeterPerSquareSecond)row[(int)ModalResultField.acc] / 2; remainingRow = AddRow(remainingRow, MultiplyRow(row.ItemArray, currentDt)); remainingDt += currentDt; absTime += currentDt; @@ -329,6 +344,7 @@ namespace TUGraz.VectoCore.OutputData r[(int)ModalResultField.time] = VectoMath.Ceiling(absTime); r[(int)ModalResultField.simulationInterval] = 1.SI<Second>(); r[(int)ModalResultField.Gear] = gearsList.MaxBy(kv => kv.Value).Key; + r[(int)ModalResultField.dist] = distance; results.Rows.Add(r); } @@ -338,12 +354,15 @@ namespace TUGraz.VectoCore.OutputData private static IEnumerable<object> MultiplyRow(IEnumerable<object> row, SI dt) { return row.Select(val => { - val.Switch() - .Case<SI>(si => val = si * dt.Value()) - .Case<int>(i => val = i * dt.Value()) - .Case<double>(d => val = d * dt.Value()) - .Case<float>(f => val = f * dt.Value()) - .Case<uint>(ui => val = ui * dt.Value()); + if (val is SI) + val = (SI)val * dt.Value(); + else { + val.Switch() + .Case<int>(i => val = i * dt.Value()) + .Case<double>(d => val = d * dt.Value()) + .Case<float>(f => val = f * dt.Value()) + .Case<uint>(ui => val = ui * dt.Value()); + } return val; }); } @@ -356,13 +375,20 @@ namespace TUGraz.VectoCore.OutputData if (addRow == null) { return row.ToArray(); } + return row.ZipAll(addRow, (val, addVal) => { - val.Switch() - .Case<SI>(si => val = si + (SI)addVal) - .Case<int>(i => val = i + (int)addVal) - .Case<double>(d => val = d + (double)addVal) - .Case<float>(f => val = f + (float)addVal) - .Case<uint>(ui => val = ui + (uint)addVal); + if (val is SI || addVal is SI) { + if (DBNull.Value == val) + val = addVal; + else if (DBNull.Value != addVal) + val = (SI)val + (SI)addVal; + } else { + val.Switch() + .Case<int>(i => val = i + (int)addVal) + .Case<double>(d => val = d + (double)addVal) + .Case<float>(f => val = f + (float)addVal) + .Case<uint>(ui => val = ui + (uint)addVal); + } return val; }).ToArray(); } diff --git a/VectoCore/VectoCoreTest/Integration/FullCycleDeclarationTest.cs b/VectoCore/VectoCoreTest/Integration/FullCycleDeclarationTest.cs index fcae4185bd304eeca766f5f6744c078123a4ad62..83b811b784514381d35643db0fa3ef74bd6e0b16 100644 --- a/VectoCore/VectoCoreTest/Integration/FullCycleDeclarationTest.cs +++ b/VectoCore/VectoCoreTest/Integration/FullCycleDeclarationTest.cs @@ -29,15 +29,21 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ +using System; +using System.Data; using System.IO; using System.Linq; +using System.Windows.Forms.DataVisualization.Charting; using Microsoft.VisualStudio.TestTools.UnitTesting; using TUGraz.VectoCommon.Models; using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.InputData.FileIO.JSON; +using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.Impl; using TUGraz.VectoCore.OutputData; using TUGraz.VectoCore.OutputData.FileIO; +using TUGraz.VectoCore.Tests.Utils; +using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Tests.Integration { @@ -176,6 +182,82 @@ namespace TUGraz.VectoCore.Tests.Integration Assert.IsTrue(jobContainer.Runs.All(r => r.Success), string.Concat(jobContainer.Runs.Select(r => r.ExecException))); } + [TestMethod, TestCategory("LongRunning")] + public void Truck40t_Mod1Hz_Test() + { + var modFileName = "40t_Long_Haul_Truck_RegionalDeliveryFullLoading.vmod"; + var modFileName1Hz = "40t_Long_Haul_Truck_RegionalDeliveryFullLoading_1Hz.vmod"; + + if (File.Exists(modFileName)) + File.Delete(modFileName); + + if (File.Exists(modFileName1Hz)) + File.Delete(modFileName1Hz); + + var inputData = JSONInputDataFactory.ReadJsonJob(LongHaulTruckDeclarationJob); + var fileWriter = new FileOutputWriter("Truck40t_Mod1Hz_Test.vecto"); + var factory = new SimulatorFactory(ExecutionMode.Declaration, inputData, fileWriter) { + WriteModalResults = true, + ModalResults1Hz = false + }; + var factory1Hz = new SimulatorFactory(ExecutionMode.Declaration, inputData, fileWriter) { + WriteModalResults = true, + ModalResults1Hz = true + }; + var sumData = new SummaryDataContainer(fileWriter); + var jobContainer = new JobContainer(sumData); + jobContainer.AddRuns(factory); + + var i = 5; + jobContainer.Runs[i].Run.Run(); + Assert.IsTrue(jobContainer.Runs[i].Run.FinishedWithoutErrors, + string.Format("{0}", jobContainer.Runs[i].ExecException)); + + jobContainer = new JobContainer(sumData); + jobContainer.AddRuns(factory1Hz); + + jobContainer.Runs[i].Run.Run(); + Assert.IsTrue(jobContainer.Runs[i].Run.FinishedWithoutErrors, + string.Format("{0}", jobContainer.Runs[i].ExecException)); + + var modFile = VectoCSVFile.Read(modFileName); + var modFile1Hz = VectoCSVFile.Read(modFileName1Hz); + + // test if line count matches the second count + var maxSeconds = + (int)Math.Ceiling(modFile.Rows.Cast<DataRow>().Last().ParseDouble(ModalResultField.time.GetShortCaption())); + var lineCount1Hz = modFile1Hz.Rows.Count; + + Assert.IsTrue(lineCount1Hz == maxSeconds); + + // test max distance + var maxDistance = modFile.Rows.Cast<DataRow>().Last().ParseDouble(ModalResultField.dist.GetShortCaption()); + var maxDistance1Hz = modFile1Hz.Rows.Cast<DataRow>().Last().ParseDouble(ModalResultField.dist.GetShortCaption()); + AssertHelper.AreRelativeEqual(maxDistance, maxDistance1Hz); + + // test if interval in 1Hz always is 1 + Assert.IsTrue( + modFile1Hz.Rows.Cast<DataRow>() + .All(r => r.ParseDouble(ModalResultField.simulationInterval.GetShortCaption()).IsEqual(1)), + "dt must always be 1 second"); + + // test if absTime in 1Hz always is a whole number and consecutive + Assert.IsTrue( + modFile1Hz.Rows.Cast<DataRow>() + .Select((r, index) => r.ParseDouble(ModalResultField.time.GetName()).IsEqual(index + 1)) + .All(p => p), "time must be whole numbers and consecutive"); + + // test sum of fuel consumption + var sumFuelConsumption = modFile.Rows.Cast<DataRow>().Select(r => + r.ParseDoubleOrGetDefault(ModalResultField.FCWHTCc.GetShortCaption()) * + r.ParseDouble(ModalResultField.simulationInterval.GetShortCaption())).Sum(); + var sumFuelConsumption1Hz = + modFile1Hz.Rows.Cast<DataRow>() + .Select(r => r.ParseDoubleOrGetDefault(ModalResultField.FCWHTCc.GetShortCaption())) + .Sum(); + AssertHelper.AreRelativeEqual(sumFuelConsumption, sumFuelConsumption1Hz, "Fuel Consumption is not equal", 1e-5); + } + [TestMethod, TestCategory("LongRunning")] public void Truck12t_DeclarationTest() {