From e511e9934a951e21c1dbd852f48daed9fb8e8ea6 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <quaritsch@ivt.tugraz.at>
Date: Wed, 25 Jan 2023 15:03:14 +0100
Subject: [PATCH] Adding testcase for calculating electric ranges (using mock
 data) finish implementation calculating weighted results (ovc/non-ovc)

---
 .../Models/Declaration/DeclarationData.cs     | 60 ++++++++++++--
 .../VectoCore/OutputData/DeclarationReport.cs |  3 +-
 .../OutputData/XML/XMLDeclarationReport.cs    |  5 +-
 .../Reports/TestXMLResultsWriting.cs          | 81 ++++++++++++++-----
 4 files changed, 120 insertions(+), 29 deletions(-)

diff --git a/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs b/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs
index db1ad76314..00e1aa316d 100644
--- a/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs
+++ b/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs
@@ -44,6 +44,7 @@ using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Configuration;
 using TUGraz.VectoCore.InputData.FileIO.JSON;
 using TUGraz.VectoCore.InputData.Reader.ComponentData;
+using TUGraz.VectoCore.InputData.Reader.DataObjectAdapter.SimulationComponents;
 using TUGraz.VectoCore.Models.BusAuxiliaries.DownstreamModules.Impl.Electrics;
 using TUGraz.VectoCore.Models.Declaration.Auxiliaries;
 using TUGraz.VectoCore.Models.Declaration.VehicleOperation;
@@ -55,6 +56,8 @@ using TUGraz.VectoCore.Utils;
 using TUGraz.VectoCore.Models.BusAuxiliaries.DownstreamModules.Impl.HVAC;
 using TUGraz.VectoCore.Models.Simulation.Data;
 using TUGraz.VectoCore.Models.Simulation.Impl;
+using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery;
+using TUGraz.VectoCore.Models.SimulationComponent.Impl;
 using TUGraz.VectoCore.OutputData;
 using TUGraz.VectoCore.OutputData.XML;
 
@@ -1391,6 +1394,10 @@ namespace TUGraz.VectoCore.Models.Declaration
 			if (cdResult.Status != VectoRun.Status.Success || csResult.Status != VectoRun.Status.Success) {
 				return null;
 			}
+			var batteryData = cdResult.BatteryData;
+			if (batteryData == null) {
+				throw new VectoException("Battery Data is required for OVC post-processing");
+			}
 			var vehicleOperation = VehicleOperation.LookupVehicleOperation(cdResult.VehicleClass, cdResult.Mission);
 
 			var D9_dailySpecificMileage = vehicleOperation.Mileage.DailyMileage;
@@ -1400,16 +1407,15 @@ namespace TUGraz.VectoCore.Models.Declaration
 			var D13_realWorldFactorUsageStartSoC = vehicleOperation.RealWorldUsageFactors.StartSoCBeforeMission;
 			var D14_realWorldFactorChargeDuringMission = vehicleOperation.RealWorldUsageFactors.StationaryChargingDuringMission;
 
-			var D15_chargingEffBatt = 0.975;  // TODO!!
-			var D16_ = 1.0;
+			
+			var D15_chargingEffBatt = CalculateChargingEfficiency(cdResult, vehicleOperation, batteryData);
 			var D18_maxStatChargingPower = cdResult.MaxChargingPower;
 
-			var D19_useableBatteryCapacity = 0.SI<WattSecond>(); // TODO!
 			var D20_energyConsumptionCdMode = cdResult.ElectricEnergyConsumption / cdResult.Distance;
 			var D21_fuelConsumptionCdMode = cdResult.FuelData.Sum(x => cdResult.FuelConsumptionFinal(x.FuelType).TotalFuelConsumptionCorrected * x.LowerHeatingValueVecto);
 			var D22_fuelConsumptionCsMode = csResult.FuelData.Sum(x => csResult.FuelConsumptionFinal(x.FuelType).TotalFuelConsumptionCorrected * x.LowerHeatingValueVecto);
 
-			var D24_useableBatteryCapForR_CDA = D19_useableBatteryCapacity * D16_;
+			var D24_useableBatteryCapForR_CDA = cdResult.BatteryData.UseableStoredEnergy;
 			var D25_actualChargeDepletingRange = D24_useableBatteryCapForR_CDA / D20_energyConsumptionCdMode;
 
 			var D29_elRangefromStartSoC_ChargingAtDepot = D25_actualChargeDepletingRange * D13_realWorldFactorUsageStartSoC;
@@ -1465,6 +1471,35 @@ namespace TUGraz.VectoCore.Models.Declaration
 			return retVal;
 		}
 
+		private static double CalculateChargingEfficiency(IResultEntry cdResult,
+			VehicleOperationLookup.VehicleOperationData vehicleOperation, BatterySystemData batteryData)
+		{
+			var minDepotChgPwr = 10.SI(Unit.SI.Kilo.Watt).Cast<Watt>();
+			var depotChargingDuration = 6.SI(Unit.SI.Hour).Cast<Second>();
+			var depotChargingPower =
+				VectoMath.Max(minDepotChgPwr, batteryData.UseableStoredEnergy / depotChargingDuration);
+			var inMissionChargingPower = VectoMath.Min(vehicleOperation.StationaryChargingMaxPwrInfrastructure, cdResult.MaxChargingPower);
+
+			var tmpBattery = new BatterySystem(null, batteryData);
+			var centerSoC = (tmpBattery.MinSoC + tmpBattery.MaxSoC) / 2.0;
+			tmpBattery.Initialize(centerSoC);
+
+			var respChgBatDepot = tmpBattery.Request(0.SI<Second>(), 1.SI<Second>(), depotChargingPower, true);
+			var respChgBatInMission = tmpBattery.Request(0.SI<Second>(), 1.SI<Second>(), inMissionChargingPower, true);
+
+			var etaChgBatDepot = 1 - (respChgBatDepot.LossPower / respChgBatDepot.PowerDemand).Value();
+			var etaChgBatInMission = 1 - (respChgBatInMission.LossPower / respChgBatInMission.PowerDemand).Value();
+
+			var chargedEnergyDepot = depotChargingPower * depotChargingDuration;
+			var chargedEnergyInMission = inMissionChargingPower *
+										vehicleOperation.StationaryChargingDuringMission_AvgDurationPerEvent *
+										vehicleOperation.StationaryChargingDuringMission_NbrEvents;
+			var totalChargedEnergy = chargedEnergyDepot + chargedEnergyInMission;
+
+			return etaChgBatDepot * chargedEnergyDepot / totalChargedEnergy +
+					etaChgBatInMission * chargedEnergyInMission / totalChargedEnergy;
+		}
+
 		public static IWeightedResult CalculateWeightedSummary(IList<IResultEntry> entries)
 		{
 			if (entries == null || !entries.Any()) {
@@ -1531,12 +1566,21 @@ namespace TUGraz.VectoCore.Models.Declaration
 			};
 		}
 
-		public static void SetElectricRangesPEV(IResultEntry resultEntry, VectoRunData runData, IModalDataContainer data)
+		public static ElectricRangesPEV SetElectricRangesPEV(VectoRunData runData, IModalDataContainer data)
 		{
+			var retVal = new ElectricRangesPEV();
 			// ToDo MQ 2022-12-12: add correct calculation method!
-			resultEntry.ActualChargeDepletingRange = 100.SI<Meter>();
-			resultEntry.EquivalentAllElectricRange = 100.SI<Meter>();
-			resultEntry.ZeroCO2EmissionsRange = 100.SI<Meter>();
+			retVal.ActualChargeDepletingRange = 100.SI<Meter>();
+			retVal.EquivalentAllElectricRange = 100.SI<Meter>();
+			retVal.ZeroCO2EmissionsRange = 100.SI<Meter>();
+			return retVal;
+		}
+
+		public struct ElectricRangesPEV
+		{
+			public Meter ZeroCO2EmissionsRange { get; set; }
+			public Meter ActualChargeDepletingRange { get; set; }
+			public Meter EquivalentAllElectricRange { get; set; }
 		}
 	}
 }
diff --git a/VectoCore/VectoCore/OutputData/DeclarationReport.cs b/VectoCore/VectoCore/OutputData/DeclarationReport.cs
index 5dfb9a2755..f48d13a831 100644
--- a/VectoCore/VectoCore/OutputData/DeclarationReport.cs
+++ b/VectoCore/VectoCore/OutputData/DeclarationReport.cs
@@ -40,6 +40,7 @@ using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Models.Declaration;
 using TUGraz.VectoCore.Models.Simulation.Data;
 using TUGraz.VectoCore.Models.Simulation.Impl;
+using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery;
 using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine;
 
 namespace TUGraz.VectoCore.OutputData
@@ -133,7 +134,7 @@ namespace TUGraz.VectoCore.OutputData
 
 		string StackTrace { get; }
 
-		WattSecond UseableBatteryCapacity { get; }
+		BatterySystemData BatteryData { get; }
 	}
 
 	public interface IWeightedResult
diff --git a/VectoCore/VectoCore/OutputData/XML/XMLDeclarationReport.cs b/VectoCore/VectoCore/OutputData/XML/XMLDeclarationReport.cs
index 84b44a6343..0bf3deea2d 100644
--- a/VectoCore/VectoCore/OutputData/XML/XMLDeclarationReport.cs
+++ b/VectoCore/VectoCore/OutputData/XML/XMLDeclarationReport.cs
@@ -46,6 +46,7 @@ using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Models.Declaration;
 using TUGraz.VectoCore.Models.Simulation.Data;
 using TUGraz.VectoCore.Models.Simulation.Impl;
+using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery;
 using TUGraz.VectoCore.OutputData.XML.DeclarationReports.CustomerInformationFile;
 using TUGraz.VectoCore.OutputData.XML.DeclarationReports.ManufacturerReport;
 
@@ -86,7 +87,7 @@ namespace TUGraz.VectoCore.OutputData.XML
 				VehicleClass = runData.Mission?.BusParameter?.BusGroup ?? runData.VehicleData.VehicleClass;
 				PassengerCount = runData.VehicleData.PassengerCount;
 				MaxChargingPower = runData.MaxChargingPower;
-				UseableBatteryCapacity = runData.BatteryData?.UseableStoredEnergy;
+				BatteryData = runData.BatteryData;
 			}
 
 			public MissionType Mission { get; set; }
@@ -142,7 +143,7 @@ namespace TUGraz.VectoCore.OutputData.XML
 			public VectoRun.Status Status { get; private set; }
 
 			public string StackTrace { get; private set; }
-			public WattSecond UseableBatteryCapacity { get; private set; }
+			public BatterySystemData BatteryData { get; private set; }
 
 			public PerSecond EngineSpeedDrivingMin { get; private set; }
 			public PerSecond EngineSpeedDrivingAvg { get; private set; }
diff --git a/VectoCore/VectoCoreTest/Reports/TestXMLResultsWriting.cs b/VectoCore/VectoCoreTest/Reports/TestXMLResultsWriting.cs
index edf6a7fdf7..ce9817cb2e 100644
--- a/VectoCore/VectoCoreTest/Reports/TestXMLResultsWriting.cs
+++ b/VectoCore/VectoCoreTest/Reports/TestXMLResultsWriting.cs
@@ -14,10 +14,12 @@ using TUGraz.VectoCommon.InputData;
 using TUGraz.VectoCommon.Models;
 using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.InputData.FileIO.XML;
+using TUGraz.VectoCore.InputData.Reader.ComponentData;
 using TUGraz.VectoCore.Models.Declaration;
 using TUGraz.VectoCore.Models.Simulation.Data;
 using TUGraz.VectoCore.Models.Simulation.Impl;
 using TUGraz.VectoCore.Models.SimulationComponent.Data;
+using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery;
 using TUGraz.VectoCore.OutputData;
 using TUGraz.VectoCore.OutputData.XML;
 using TUGraz.VectoCore.OutputData.XML.DeclarationReports.Common;
@@ -135,7 +137,6 @@ public class TestXMLResultsWriting
 		WriteToFile("CIF", doc, runData, success, exempted);
 	}
 
-	
 
 	[
 		TestCase(VectoSimulationJobType.ConventionalVehicle, false, false, true, TestName = "CIF_ReportResult_WritingResults: CompletedBus Conv SUCCESS"),
@@ -422,6 +423,42 @@ public class TestXMLResultsWriting
 		WriteToFile("VIF", doc, runData, success, exempted);
 	}
 
+	// ---------
+
+	[TestCase()]
+	public void TestCalculateOVCWeightedResult(params FuelType[] fuels)
+	{
+		var jobType = VectoSimulationJobType.ParallelHybridVehicle;
+		var vehicleCategory = VehicleCategory.RigidTruck;
+		var ovcmode = VectoRunData.OvcHevMode.ChargeDepleting;
+		var runData = GetMockRunData(vehicleCategory, jobType, true, false, ovcmode, fuels);
+		var modData = GetMockModData(VectoRun.Status.Success, fuels, ovcmode);
+
+		var cdResult = GetResultEntry(runData);
+		cdResult.SetResultData(runData, modData, 1);
+
+		var run2 = GetMockRunData(vehicleCategory, jobType, true, false, VectoRunData.OvcHevMode.ChargeSustaining, fuels);
+		var modData2 = GetMockModData(VectoRun.Status.Success, fuels, VectoRunData.OvcHevMode.ChargeSustaining);
+		var csResult = GetResultEntry(run2);
+		csResult.SetResultData(run2, modData2, 1);
+
+		var weighted = DeclarationData.CalculateWeightedResult(cdResult, csResult);
+
+		Console.WriteLine($"{weighted.ActualChargeDepletingRange.Value().ToXMLFormat(3)} {weighted.EquivalentAllElectricRange.Value().ToXMLFormat(3)} {weighted.ZeroCO2EmissionsRange.Value().ToXMLFormat(3)} {weighted.UtilityFactor.ToXMLFormat(3)}" +
+						$" {weighted.ElectricEnergyConsumption.Value().ToXMLFormat(3)} {weighted.FuelConsumption[FuelData.Diesel].Value().ToXMLFormat(3)} {weighted.CO2Total.Value().ToXMLFormat(3)}");
+
+		//1518.750 1366.875 1366.875 0.004 797877.345 30.890 20.000
+
+		Assert.AreEqual(1518.750, weighted.ActualChargeDepletingRange.Value(), 1e-3);
+		Assert.AreEqual(1366.875, weighted.EquivalentAllElectricRange.Value(), 1e-3);
+		Assert.AreEqual(1366.875, weighted.ZeroCO2EmissionsRange.Value(), 1e-3);
+		Assert.AreEqual(0.004, weighted.UtilityFactor, 1e-3);
+		Assert.AreEqual(797877.345, weighted.ElectricEnergyConsumption.Value(), 1e-3);
+		Assert.AreEqual(30.890, weighted.FuelConsumption[FuelData.Diesel].Value(), 1e-3);
+		Assert.AreEqual(20.000, weighted.CO2Total.Value(), 1e-3);
+
+	}
+
 	// ===================================
 
 	private static void WriteToConsole(XDocument doc)
@@ -485,17 +522,8 @@ public class TestXMLResultsWriting
 
 	private static XMLDeclarationReport.ResultEntry GetResultEntry(VectoRunData runData)
 	{
-		var resultEntry = new XMLDeclarationReport.ResultEntry() {
-			Mission = runData.Mission.MissionType,
-			LoadingType = runData.Loading,
-			FuelMode = runData.EngineData?.FuelMode ?? 0,
-			FuelData = runData.EngineData?.Fuels.Select(x => x.FuelData).ToList(),
-			Payload = runData.VehicleData.Loading,
-			TotalVehicleMass = runData.VehicleData.TotalVehicleMass,
-			CargoVolume = runData.VehicleData.CargoVolume,
-			VehicleClass = runData.VehicleData.VehicleClass,
-			PassengerCount = runData.VehicleData.PassengerCount,
-		};
+		var resultEntry = new XMLDeclarationReport.ResultEntry();
+		resultEntry.Initialize(runData);
 		return resultEntry;
 	}
 
@@ -523,7 +551,7 @@ public class TestXMLResultsWriting
 		return doc;
 	}
 
-	private IModalDataContainer GetMockModData(VectoRun.Status runStatus, FuelType[] fuelTypes)
+	private IModalDataContainer GetMockModData(VectoRun.Status runStatus, FuelType[] fuelTypes, VectoRunData.OvcHevMode ovcMode = VectoRunData.OvcHevMode.NotApplicable)
 	{
 		var fuels = fuelTypes == null || fuelTypes.Length == 0 ? new[] { FuelType.DieselCI } : fuelTypes;
 
@@ -551,21 +579,22 @@ public class TestXMLResultsWriting
 		modData.Setup(x => x.CorrectedModalData).Returns(mc.Object);
 
 		var fcCorrected = new Dictionary<FuelType, IFuelConsumptionCorrection>();
+		var ovcFactor = ovcMode == VectoRunData.OvcHevMode.ChargeDepleting ? 0.1 : 1.0;
 		foreach (var fuelType in fuels) {
 			var factor = fcCorrected.Count == 0 ? 1 : 0.1;
 			var fc = new Mock<IFuelConsumptionCorrection>();
 			fc.Setup(x => x.Fuel).Returns(DeclarationData.FuelData.Lookup(fuelType, TankSystem.Liquefied));
-			fc.Setup(x => x.TotalFuelConsumptionCorrected).Returns(31.SI<Kilogram>() * factor);
-			fc.Setup(x => x.EnergyDemand).Returns(31.SI<Kilogram>() * factor * FuelData.Diesel.LowerHeatingValueVecto);
+			fc.Setup(x => x.TotalFuelConsumptionCorrected).Returns(31.SI<Kilogram>() * factor * ovcFactor);
+			fc.Setup(x => x.EnergyDemand).Returns(31.SI<Kilogram>() * factor * ovcFactor * FuelData.Diesel.LowerHeatingValueVecto);
 			fcCorrected.Add(fuelType, fc.Object);
 		}
 		mc.Setup(x => x.FuelCorrection).Returns(fcCorrected);
 
 		mc.Setup(x => x.CO2Total).Returns(20.SI<Kilogram>());
 		mc.Setup(x => x.FuelEnergyConsumptionTotal).Returns(1e9.SI<Joule>());
-		mc.Setup(x => x.ElectricEnergyConsumption).Returns(200.SI(Unit.SI.Mega.Joule).Cast<WattSecond>());
-
 
+		var elOvcFactor = ovcMode == VectoRunData.OvcHevMode.ChargeSustaining ? 0 : 1.0;
+		mc.Setup(x => x.ElectricEnergyConsumption).Returns(200.SI(Unit.SI.Mega.Joule).Cast<WattSecond>() * elOvcFactor);
 
 		return modData.Object;
 	}
@@ -582,6 +611,7 @@ public class TestXMLResultsWriting
 			Exempted = exempted,
 			JobType = jobType,
 			Loading = LoadingType.LowLoading,
+			MaxChargingPower = 250.SI(Unit.SI.Kilo.Watt).Cast<Watt>(),
 			VehicleData = new VehicleData() {
 				CurbMass = 7600.SI<Kilogram>(),
 				Loading = 5000.SI<Kilogram>(),
@@ -589,7 +619,7 @@ public class TestXMLResultsWriting
 				PassengerCount = 20,
 				VehicleClass = VehicleClass.Class5,
 				VehicleCategory = vehicleCategory,
-				OffVehicleCharging = offVehicleCharging
+				OffVehicleCharging = offVehicleCharging,
 			},
 			EngineData = new CombustionEngineData() {
 				FuelMode = 0,
@@ -599,6 +629,21 @@ public class TestXMLResultsWriting
 			Retarder = new RetarderData() {
 				Type = RetarderType.None,
 			},
+			BatteryData = new BatterySystemData() {
+				Batteries = new List<Tuple<int, BatteryData>>() {
+					Tuple.Create(1, new BatteryData() {
+						BatteryId = 0,
+						Capacity = 7.5.SI(Unit.SI.Ampere.Hour).Cast<AmpereSecond>(),
+						ChargeSustainingBattery = true,
+						MinSOC = 0.2,
+						MaxSOC = 0.8,
+						SOCMap = BatterySOCReader.Create("SoC, V\n0, 600\n100, 650\n".ToStream()),
+						InternalResistance = BatteryInternalResistanceReader.Create("SoC, Ri-2, Ri-10, Ri-20\n0, 20, 20, 20\n100, 20, 20, 20\n".ToStream(), true),
+						MaxCurrent = BatteryMaxCurrentReader.Create("SoC, I_charge, I_discharge\n0, 300, 300\n100, 500, 500\n".ToStream())
+						
+					})
+				}
+			}
 		};
 	}
 }
\ No newline at end of file
-- 
GitLab