From 1c124b6e56544159049baf298ca33c01bb94b6d0 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Fri, 10 Jun 2022 14:14:16 +0200
Subject: [PATCH] add new mod-data postprocessing class for serial hybrid
 vehicles. pass over genet characteristics from powertrain to postprocessing
 via run-data instance

---
 .../Simulation/Data/ModalResultField.cs       |   1 +
 .../Models/Simulation/Data/VectoRunData.cs    |  10 ++
 .../Models/Simulation/Impl/JobContainer.cs    |   3 +-
 .../Strategies/SerialHybridStrategy.cs        |   2 +-
 .../OutputData/ModalDataContainer.cs          |   6 +-
 .../ModalDataPostprocessingCorrection.cs      | 103 ++++++++++++++++--
 6 files changed, 111 insertions(+), 14 deletions(-)

diff --git a/VectoCore/VectoCore/Models/Simulation/Data/ModalResultField.cs b/VectoCore/VectoCore/Models/Simulation/Data/ModalResultField.cs
index 882d1441f3..c447ede967 100644
--- a/VectoCore/VectoCore/Models/Simulation/Data/ModalResultField.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Data/ModalResultField.cs
@@ -480,6 +480,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Data
 	{
 		private static ConcurrentDictionary<ModalResultField, ModalResultFieldAttribute> _attributeDictionary =
 			new ConcurrentDictionary<ModalResultField, ModalResultFieldAttribute>();
+
 		public static string GetName(this ModalResultField field)
 		{
 			return GetAttribute(field).Name ?? field.ToString();
diff --git a/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs b/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs
index 3d2659fe3b..9dcc8106ab 100644
--- a/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs
@@ -48,6 +48,7 @@ using TUGraz.VectoCore.Models.SimulationComponent.Data;
 using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery;
 using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox;
 using TUGraz.VectoCore.Models.SimulationComponent.Impl;
+using TUGraz.VectoCore.Models.SimulationComponent.Strategies;
 using TUGraz.VectoCore.OutputData;
 using DriverData = TUGraz.VectoCore.Models.SimulationComponent.Data.DriverData;
 
@@ -157,6 +158,9 @@ namespace TUGraz.VectoCore.Models.Simulation.Data
 		[JsonIgnore]
 		public IMultistageVIFInputData MultistageVIFInputData { get; internal set; }
 
+		// container to pass genset data from powertrain to post-processing, not filled by dataadapter/rundatafactory
+		public GenSetData GenSet { get; set; }
+
 		public class AuxData
 		{
 			// ReSharper disable once InconsistentNaming
@@ -175,6 +179,12 @@ namespace TUGraz.VectoCore.Models.Simulation.Data
 			public MissionType? MissionType;
 		}
 
+		// container to pass genset data from powertrain to post-processing, not filled by dataadapter/rundatafactory
+		public class GenSetData
+		{
+			public GenSetCharacteristics GenSetCharacteristics { get; set; }
+		}
+
 		public static ValidationResult ValidateRunData(VectoRunData runData, ValidationContext validationContext)
 		{
 			var gearboxData = runData.GearboxData;
diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/JobContainer.cs b/VectoCore/VectoCore/Models/Simulation/Impl/JobContainer.cs
index 648327b84a..75c7e3e7f1 100644
--- a/VectoCore/VectoCore/Models/Simulation/Impl/JobContainer.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Impl/JobContainer.cs
@@ -349,8 +349,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 				_runsRwLock.EnterWriteLock();
 				//_unfinishedRuns.Remove(runId);
 				_unfinishedRuns.TryRemove(runId, out var tmpVal);
-				if (AllCompletedUnsafe())
-				{
+				if (AllCompletedUnsafe()) {
 					_sumWriter.Finish();
 				}
 			} finally {
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/SerialHybridStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/SerialHybridStrategy.cs
index e835e31d6e..2f6e212cb0 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/SerialHybridStrategy.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/SerialHybridStrategy.cs
@@ -277,7 +277,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 			var minGensetPower = emDtData.EfficiencyData.VoltageLevels.First().FullLoadCurve.MaxPower * StrategyParameters.GensetMinOptPowerFactor;
 
 			GenSetCharacteristics = new GenSetCharacteristics(minGensetPower);
-
+			runData.GenSet = new VectoRunData.GenSetData() { GenSetCharacteristics = GenSetCharacteristics };
 
 			// create testcontainer
 			var modData = new ModalDataContainer(runData, null, null);
diff --git a/VectoCore/VectoCore/OutputData/ModalDataContainer.cs b/VectoCore/VectoCore/OutputData/ModalDataContainer.cs
index a201cbe249..ca208b405b 100644
--- a/VectoCore/VectoCore/OutputData/ModalDataContainer.cs
+++ b/VectoCore/VectoCore/OutputData/ModalDataContainer.cs
@@ -165,7 +165,11 @@ namespace TUGraz.VectoCore.OutputData
 				return;
 			}
 
-			PostProcessingCorrection = new ModalDataPostprocessingCorrection();
+			var isSerialHybrid = runData.JobType == VectoSimulationJobType.IEPC_S ||
+								runData.JobType == VectoSimulationJobType.SerialHybridVehicle;
+			PostProcessingCorrection = isSerialHybrid
+				? new SerialHybridModalDataPostprocessingCorrection()
+				: new ModalDataPostprocessingCorrection();
 
 			var multipleEngineModes = runData.EngineData?.MultipleEngineFuelModes ?? false;
 			var fuels = runData.EngineData?.Fuels ?? new List<CombustionEngineFuelData>();
diff --git a/VectoCore/VectoCore/OutputData/ModalDataPostprocessingCorrection.cs b/VectoCore/VectoCore/OutputData/ModalDataPostprocessingCorrection.cs
index 411fea3308..e0aa4832bc 100644
--- a/VectoCore/VectoCore/OutputData/ModalDataPostprocessingCorrection.cs
+++ b/VectoCore/VectoCore/OutputData/ModalDataPostprocessingCorrection.cs
@@ -3,6 +3,7 @@ using System.Data;
 using System.Linq;
 using TUGraz.VectoCommon.BusAuxiliaries;
 using TUGraz.VectoCommon.Exceptions;
+using TUGraz.VectoCommon.InputData;
 using TUGraz.VectoCommon.Models;
 using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Models.BusAuxiliaries;
@@ -13,6 +14,88 @@ using TUGraz.VectoCore.Models.SimulationComponent.Data;
 
 namespace TUGraz.VectoCore.OutputData
 {
+	public class SerialHybridModalDataPostprocessingCorrection : ModalDataPostprocessingCorrection
+	{
+
+        protected override void SetReesCorrectionDemand(IModalDataContainer modData, VectoRunData runData,
+            CorrectedModalData r)
+        {
+            var deltaEReess = modData.TimeIntegral<WattSecond>(ModalResultField.P_reess_int.GetName());
+            var startSoc = modData.REESSStartSoC();
+            var endSoc = modData.REESSEndSoC();
+            var emEff = 0.0;
+            if (endSoc < startSoc) {
+                var etaReessChg = modData.WorkREESSChargeInternal().Value() / modData.WorkREESSChargeTerminal().Value();
+                emEff = 1.0 / (etaReessChg);
+            }
+            if (endSoc > startSoc) {
+                var etaReessDischg = modData.WorkREESSDischargeTerminal().Value() / modData.WorkREESSDischargeInternal().Value();
+                emEff = etaReessDischg;
+            }
+
+            r.DeltaEReessMech = double.IsNaN(emEff) ? 0.SI<WattSecond>() : -deltaEReess * emEff;
+        }
+
+        protected override FuelConsumptionCorrection SetFuelConsumptionCorrection(IModalDataContainer modData, VectoRunData runData,
+            CorrectedModalData r, IFuelProperties fuel)
+        {
+            var duration = modData.Duration;
+            var distance = modData.Distance;
+            var essParams = runData.DriverData.EngineStopStart;
+            var engFuel = runData.EngineData.Fuels.First(x => x.FuelData.Equals(fuel));
+
+            var fcIceIdle = engFuel.ConsumptionMap.GetFuelConsumptionValue(
+                                            0.SI<NewtonMeter>(),
+                                            runData.EngineData.IdleSpeed) *
+                                        engFuel.FuelConsumptionCorrectionFactor;
+
+            var elPowerGenerated = modData.TimeIntegral<WattSecond>(string.Format(ModalResultField.P_EM_electricMotor_el_.GetCaption(), PowertrainPosition.GEN));
+            var fcGenCharging = modData.TotalFuelConsumption(ModalResultField.FCFinal, fuel);
+            var socCorr = elPowerGenerated.IsEqual(0)
+                ? (runData.GenSet.GenSetCharacteristics.OptimalPoint.FuelConsumption /
+                    runData.GenSet.GenSetCharacteristics.OptimalPoint.ElectricPower).Cast<KilogramPerWattSecond>()
+                : (fcGenCharging / elPowerGenerated).Cast<KilogramPerWattSecond>();
+            var engLine = modData.EngineLineCorrectionFactor(fuel);
+            var comp =
+                runData.BusAuxiliaries?.PneumaticUserInputsConfig.CompressorMap
+                    .Interpolate(runData.EngineData.IdleSpeed);
+
+            var f = new FuelConsumptionCorrection {
+                Fuel = fuel,
+                Distance = distance != null && distance.IsGreater(0) ? distance : null,
+                Duration = duration != null && duration.IsGreater(0) ? duration : null,
+                EngineLineCorrectionFactor = engLine,
+                VehicleLine = modData.VehicleLineSlope(fuel),
+                FcModSum = modData.TotalFuelConsumption(ModalResultField.FCFinal, fuel),
+                FcESS_EngineStart = engLine * modData.WorkEngineStart(),
+
+                FcESS_AuxStandstill_ICEOff = r.EnergyAuxICEOffStandstill_UF * engLine,
+                FcESS_AuxStandstill_ICEOn = r.EnergyAuxICEOnStandstill_UF * engLine +
+                                            fcIceIdle * r.ICEOffTimeStandstill * (1 - r.UtilityFactorStandstill),
+
+                FcESS_AuxDriving_ICEOff = r.EnergyAuxICEOffDriving_UF * engLine,
+                FcESS_AuxDriving_ICEOn = r.EnergyAuxICEOnDriving_UF * engLine +
+                                        fcIceIdle * r.ICEOffTimeDriving * (1 - r.UtilityFactorDriving),
+
+                FcESS_DCDCMissing = r.EnergyDCDCMissing * engLine,
+                FcBusAuxPSAirDemand = engLine * r.WorkBusAuxPSCorr,
+
+                FcBusAuxPSDragICEOffStandstill = comp == null
+                    ? 0.SI<Kilogram>()
+                    : comp.PowerOff * r.ICEOffTimeStandstill * engLine * (1 - essParams.UtilityFactorStandstill),
+                FcBusAuxPSDragICEOffDriving = comp == null
+                    ? 0.SI<Kilogram>()
+                    : comp.PowerOff * r.ICEOffTimeDriving * engLine * (1 - essParams.UtilityFactorDriving),
+                FcREESSSoc = r.DeltaEReessMech * socCorr,
+                FcBusAuxEs = engLine * r.WorkBusAuxESMech,
+                FcWHR = engLine * r.WorkWHR,
+                FcAuxHtr = 0.SI<Kilogram>()
+            };
+
+            return f;
+        }
+    }
+
 	public class ModalDataPostprocessingCorrection : IModalDataPostProcessor
 	{
 		
@@ -80,17 +163,17 @@ namespace TUGraz.VectoCore.OutputData
 			return r;
 		}
 
-		private void SetReesCorrectionDemand(IModalDataContainer modData, VectoRunData runData,
+		protected virtual void SetReesCorrectionDemand(IModalDataContainer modData, VectoRunData runData,
 			CorrectedModalData r)
 		{
-			var em = runData.ElectricMachinesData?.FirstOrDefault();
+			var em = runData.ElectricMachinesData?.FirstOrDefault(x => x.Item1 != PowertrainPosition.GEN);
 			
 			if (em != null) {
 				var deltaEReess = modData.TimeIntegral<WattSecond>(ModalResultField.P_reess_int.GetName());
 				var startSoc = modData.REESSStartSoC();
 				var endSoc = modData.REESSEndSoC();
-                var emEff = 0.0;
-                if (endSoc < startSoc) {
+				var emEff = 0.0;
+				if (endSoc < startSoc) {
 					var etaEmChg = modData.ElectricMotorEfficiencyGenerate(em.Item1);
 					var etaReessChg = modData.WorkREESSChargeInternal().Value() / modData.WorkREESSChargeTerminal().Value();
 					emEff = 1.0 / (etaEmChg * etaReessChg);
@@ -107,7 +190,7 @@ namespace TUGraz.VectoCore.OutputData
 			}
 		}
 
-		private static FuelConsumptionCorrection SetFuelConsumptionCorrection(IModalDataContainer modData, VectoRunData runData,
+		protected virtual FuelConsumptionCorrection SetFuelConsumptionCorrection(IModalDataContainer modData, VectoRunData runData,
 			CorrectedModalData r, IFuelProperties fuel)
 		{
 			var duration = modData.Duration;
@@ -163,7 +246,7 @@ namespace TUGraz.VectoCore.OutputData
 			return f;
 		}
 
-		private static void SetAuxHeaterDemand(IModalDataContainer modData, CorrectedModalData r, Second duration)
+		protected virtual void SetAuxHeaterDemand(IModalDataContainer modData, CorrectedModalData r, Second duration)
 		{
 			var engineWasteheatSum = modData.FuelData.Aggregate(
 				0.SI<Joule>(),
@@ -174,7 +257,7 @@ namespace TUGraz.VectoCore.OutputData
 				: modData.AuxHeaterDemandCalc(duration, engineWasteheatSum);
 		}
 
-		private static void SetMissingDCDCEnergy(IModalDataContainer modData, VectoRunData runData, CorrectedModalData r)
+		protected virtual void SetMissingDCDCEnergy(IModalDataContainer modData, VectoRunData runData, CorrectedModalData r)
 		{
 			// either case C1, C2a, or C3a
 			var missingDCDCEnergy = modData.TimeIntegral<WattSecond>(ModalResultField.P_DCDC_missing);
@@ -203,7 +286,7 @@ namespace TUGraz.VectoCore.OutputData
 			//}
 		}
 
-		private static void SetBusAuxMissingPSWork(IModalDataContainer modData, VectoRunData runData, CorrectedModalData r)
+		protected virtual void SetBusAuxMissingPSWork(IModalDataContainer modData, VectoRunData runData, CorrectedModalData r)
 		{
 			var actuations = new Actuations() {
 				Braking = runData.BusAuxiliaries.Actuations.Braking,
@@ -234,7 +317,7 @@ namespace TUGraz.VectoCore.OutputData
 			r.WorkBusAuxPSCorr = (r.kAir * r.DeltaAir).Cast<WattSecond>();
 		}
 
-		private static void SetMissingEnergyICEOFf(IModalDataContainer modData, CorrectedModalData r)
+		protected virtual void SetMissingEnergyICEOFf(IModalDataContainer modData, CorrectedModalData r)
 		{
 			var entriesAuxICEStandstill = modData.GetValues(x => new {
 				dt = x.Field<Second>(ModalResultField.simulationInterval.GetName()),
@@ -263,7 +346,7 @@ namespace TUGraz.VectoCore.OutputData
 			r.EnergyAuxICEOnDriving = entriesAuxICEDriving.Sum(x => x.P_on * x.dt) ?? 0.SI<WattSecond>();
 		}
 
-		private static void SetWHRWork(IModalDataContainer modData, VectoRunData runData, CorrectedModalData r)
+		protected virtual void SetWHRWork(IModalDataContainer modData, VectoRunData runData, CorrectedModalData r)
 		{
 			// no post-processing correction for electric WHR for HEV (WHR is connected to REESS in simulation)
 			r.WorkWHREl = (runData.BatteryData != null || runData.SuperCapData != null) && runData.EngineData.WHRType.IsElectrical()
-- 
GitLab