From 5d24e38b932336b6cc152b38539c51862c371b38 Mon Sep 17 00:00:00 2001
From: "VKMTHD\\haraldmartini" <harald.martini@student.tugraz.at>
Date: Wed, 8 Feb 2023 14:40:21 +0100
Subject: [PATCH] implemented s-hev strategy parameters for vehicles with
 supercap

---
 .../HybridStrategyParameterDataAdapter.cs     | 119 +++++++++++++-----
 .../SimulationComponent/Impl/SuperCap.cs      |   7 ++
 .../HybridStrategyDataAdapterTest.cs          |  37 +++++-
 3 files changed, 131 insertions(+), 32 deletions(-)

diff --git a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/SimulationComponents/StrategyDataAdapter/HybridStrategyParameterDataAdapter.cs b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/SimulationComponents/StrategyDataAdapter/HybridStrategyParameterDataAdapter.cs
index 1beed0b120..b4fb1e23dc 100644
--- a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/SimulationComponents/StrategyDataAdapter/HybridStrategyParameterDataAdapter.cs
+++ b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/SimulationComponents/StrategyDataAdapter/HybridStrategyParameterDataAdapter.cs
@@ -9,6 +9,7 @@ using TUGraz.VectoCore.Models.Simulation.Data;
 using TUGraz.VectoCore.Models.SimulationComponent.Data;
 using TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricComponents.Battery;
 using TUGraz.VectoCore.Models.SimulationComponent.Impl;
+using TUGraz.VectoCore.Utils;
 
 namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter.SimulationComponents.StrategyDataAdapter
 {
@@ -80,12 +81,10 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter.SimulationComponen
 			SuperCapData superCapData, Kilogram vehicleMass, VectoRunData.OvcHevMode ovcMode)
 		{
 			if (batterySystemData == null && superCapData == null) {
-				return null;
+				throw new VectoException("Either Battery or SuperCap must be set");
 			}
 
-			if (superCapData != null) {
-				throw new VectoException("Super cap for serial hybrid is not implemented");
-			}
+			
 			var result = new HybridStrategyParameters();
 			
 			result.AuxReserveTime = null;
@@ -99,47 +98,79 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter.SimulationComponen
 			result.EquivalenceFactorDischarge = double.NaN;
 			result.CostFactorSOCExponent = double.NaN;
 
-	
 
-			SetGenericParameters(result, batterySystemData, superCapData, vehicleMass, out var deltaSoc, out var tmpSystem);
 
-			switch (ovcMode) {
-				case VectoRunData.OvcHevMode.ChargeSustaining:
-					result.InitialSoc = result.MinSoC + deltaSoc;
-					break;
-				case VectoRunData.OvcHevMode.ChargeDepleting:
-					result.InitialSoc = (tmpSystem.MaxSoC + tmpSystem.MinSoC) / 2;
-					result.TargetSoC = result.InitialSoc - 1;
-					break;
-				default:
-					throw new ArgumentOutOfRangeException(nameof(ovcMode), ovcMode, null);
+			if (batterySystemData != null) {
+				SetGenericParameters(ref result, batterySystemData, vehicleMass, ovcMode);
 			}
+			if (superCapData != null) {
+				SetGenericParameters(ref result, superCapData, vehicleMass, ovcMode);
+			}
+
 
 			return result;
 		}
 
-		private void SetGenericParameters(HybridStrategyParameters result, BatterySystemData batterySystemData,
-			SuperCapData superCapData, Kilogram vehicleMass, out double deltaSoc, out BatterySystem tmpSystem)
+		private void SetGenericParameters(ref HybridStrategyParameters result, SuperCapData superCapData, Kilogram vehicleMass, VectoRunData.OvcHevMode ovcMode)
 		{
-			tmpSystem = null;
-			if (batterySystemData != null) {
-				tmpSystem = new BatterySystem(null, batterySystemData);
+
+			
+			SetSuperCapParameters(result, superCapData, vehicleMass, 70.KMPHtoMeterPerSecond());
+			if (result.MinSoC >= result.TargetSoC) {
+				SetSuperCapParameters(result, superCapData, vehicleMass, 30.KMPHtoMeterPerSecond());
+			}
+
+
+
+
+
+
+
+
+
+			if (result.MinSoC >= result.TargetSoC || result.TargetSoC >= 1) {
+				throw new VectoException("Min SOC higher than Target SOC");
 			}
+		}
+
+		private void SetSuperCapParameters(HybridStrategyParameters result, SuperCapData superCapData, Kilogram vehicleMass, MeterPerSecond velocity)
+		{
+			var min_sc_energy = SC_Energy(superCapData.MinVoltage, superCapData.Capacity);
+			var max_sc_energy = SC_Energy(superCapData.MaxVoltage, superCapData.Capacity);
+
+			var kin_energy = KineticEnergy(vehicleMass, velocity);
 
-			deltaSoc = CalculatedDeltaSocSHev(vehicleMass, tmpSystem, superCapData);
+			var min_hs_energy = min_sc_energy + kin_energy;
+			var target_hs_energy = max_sc_energy - kin_energy;
 
-			var reessMinSoc = tmpSystem?.MinSoC ?? (superCapData.MinVoltage / superCapData.MaxVoltage);
-			var reessMaxSoc = tmpSystem?.MaxSoC ?? 1;
+			var u_min = SC_Voltage(min_hs_energy, superCapData.Capacity);
+			var u_target = SC_Voltage(target_hs_energy, superCapData.Capacity);
 
+			result.MinSoC = u_min / superCapData.MaxVoltage;
+			result.TargetSoC = u_target / superCapData.MaxVoltage;
+			result.InitialSoc =
+				Math.Sqrt(((Math.Pow(superCapData.MaxVoltage.Value(),2) + Math.Pow(superCapData.MinVoltage.Value(),2)) / 2.0)) /
+				superCapData.MaxVoltage.Value();
+			result.MaxSoC = 1;
+		}
 
+		private void SetGenericParameters(ref HybridStrategyParameters result,
+			BatterySystemData batterySystemData, 
+			Kilogram vehicleMass,
+			VectoRunData.OvcHevMode ovcMode)
+		{
+			var tmpSystem = new BatterySystem(null, batterySystemData);
+			var deltaSoc = CalculateDeltaSocSHev(vehicleMass, tmpSystem, 100.KMPHtoMeterPerSecond());
+
+			var reessMinSoc = tmpSystem.MinSoC;
+			var reessMaxSoc = tmpSystem.MaxSoC;
 
 			result.MinSoC = reessMinSoc + 2 * deltaSoc;
 			result.TargetSoC = tmpSystem.MaxSoC - 5 * deltaSoc;
 			result.MaxSoC = double.NaN;
 
-
 			if (result.MinSoC >= result.TargetSoC) {
-				deltaSoc = CalculatedDeltaSocSHev(vehicleMass, tmpSystem, superCapData, 50);
+				deltaSoc = CalculateDeltaSocSHev(vehicleMass, tmpSystem, 50.KMPHtoMeterPerSecond());
 				//Small battery
 				result.TargetSoC = reessMaxSoc - 1 * deltaSoc;
 				result.MinSoC = reessMinSoc + 2 * deltaSoc;
@@ -147,18 +178,44 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter.SimulationComponen
 					throw new VectoException("Min SOC higher than Target SOC");
 				}
 			}
+
+			switch (ovcMode)
+			{
+				case VectoRunData.OvcHevMode.ChargeSustaining:
+					result.InitialSoc = result.MinSoC + deltaSoc;
+					break;
+				case VectoRunData.OvcHevMode.ChargeDepleting:
+					result.InitialSoc = (tmpSystem.MaxSoC + tmpSystem.MinSoC) / 2;
+					result.TargetSoC = result.InitialSoc - 1;
+					break;
+				default:
+					throw new ArgumentOutOfRangeException(nameof(ovcMode), ovcMode, null);
+			}
 		}
 
-		private double CalculatedDeltaSocSHev(Kilogram vehicleMass, 
-			BatterySystem tmpSystem, SuperCapData superCap, double kmph = 100)
+		private double CalculateDeltaSocSHev(Kilogram vehicleMass, 
+			BatterySystem tmpSystem, MeterPerSecond v)
 		{
 
-			var v_nom = tmpSystem?.NominalVoltage ?? (superCap.MaxVoltage + superCap.MinVoltage)/2;
+			var v_nom = tmpSystem.NominalVoltage;
 			var c_nom = tmpSystem.Capacity.AsAmpHour;
-			var v = kmph.KMPHtoMeterPerSecond();
-			var result = ((vehicleMass / 2 * v * v) / 3600) * (1 / v_nom) * (1 / c_nom);
+			var result = KineticEnergy(vehicleMass, v).ConvertToWattHour()  / v_nom / c_nom;
 			return result.Value();
 		}
 
+		private static WattSecond KineticEnergy(Kilogram vehicleMass, MeterPerSecond v)
+		{
+			return ((vehicleMass / 2.0) * v * v).Cast<WattSecond>();
+		}
+
+		private static Volt SC_Voltage(WattSecond energy, Farad capacity)
+		{
+			return Math.Sqrt((2.0 * energy / capacity).Value()).SI<Volt>();
+		}
+
+		private static Joule SC_Energy(Volt voltage, Farad capacity)
+		{
+			return 0.5 * capacity * voltage * voltage;
+		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/SuperCap.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/SuperCap.cs
index 0f22bd36d8..5279712b60 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/SuperCap.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/SuperCap.cs
@@ -56,7 +56,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		public double MinSoC => ModelData.MinVoltage / ModelData.MaxVoltage;
 
 		public double MaxSoC => 1;
+
+		/// <summary>
+		/// [Warning("Not implemented in super cap, returns null")]
+		/// </summary>
 		public AmpereSecond Capacity => null;
+		/// <summary>
+		/// [Warning("Not implemented in super cap, returns null")]
+		/// </summary>
 		public Volt NominalVoltage => null;
 
 		public void Initialize(double initialSoC)
diff --git a/VectoCore/VectoCoreTest/Models/Declaration/DataAdapter/HybridStrategyDataAdapterTest.cs b/VectoCore/VectoCoreTest/Models/Declaration/DataAdapter/HybridStrategyDataAdapterTest.cs
index a9de4c7dd9..40a14f5808 100644
--- a/VectoCore/VectoCoreTest/Models/Declaration/DataAdapter/HybridStrategyDataAdapterTest.cs
+++ b/VectoCore/VectoCoreTest/Models/Declaration/DataAdapter/HybridStrategyDataAdapterTest.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
 using Castle.Components.DictionaryAdapter;
 using Moq;
 using NUnit.Framework;
+using TUGraz.VectoCommon.Exceptions;
 using TUGraz.VectoCommon.InputData;
 using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.InputData.Reader.DataObjectAdapter.SimulationComponents.StrategyDataAdapter;
@@ -22,7 +23,6 @@ public class HybridStrategyDataAdapterTest
     [TestCase(0.7375, 0.2625, 11273.9176, 778.61, 247500, false, 0.624646591, 0.307641364)]
 	[TestCase(0.6, 0.2, 11000, 778.61, 80000, false, 0.582967134, 0.234065732)]
 	[TestCase(0.6, 0.2, 11000, 778.61, 5000, true, 0.268131464, 0.634065732)]
-
 	public void SerialHybridStrategyTest(double bat_soc_max, double bat_soc_min, double vehicle_mass, double nominalVoltage, double nominalCapacity, bool exception, double expected_target_soc, double expected_min_soc)
 	{
 		var mass = vehicle_mass.SI<Kilogram>();
@@ -83,5 +83,40 @@ public class HybridStrategyDataAdapterTest
 
 	}
 
+	[TestCase(1000.00000000000000 	,10.00000000000000	,1000.00000000000000	,1.00000000000000	,0.61496863314570	,0.78861497592102	,0.70714213564177	,1.00000000000000	,false)]
+	//[TestCase(40000.00000000000000	,10.00000000000000	,1000.00000000000000	,25.00000000000000,	0.77784206083558,	0.62853936105471,	0.70714213564177,	1.00000000000000,	true)] //fallback 30 kmh
+	[TestCase(1000.00000000000000 	,10.00000000000000	,1000.00000000000000	,25.00000000000000,	0.12338337323207,	0.99240946348263,	0.70714213564177,	1.00000000000000,	false)]
+	[TestCase(40000.00000000000000	,10.00000000000000	,1000.00000000000000	,25.00000000000000,	0.33348329959851,	0.94280904158206,	0.70714213564177,	1.00000000000000,	false)]
+	[TestCase(40000.00000000000000	,10.00000000000000	,1000.00000000000000	,3.00000000000000	,0.96230240877072	,0.27216552697591	,0.70714213564177	,1.00000000000000	,true)]
+	public void SerialHybridSuperCapTest(double mass_kg, double U_min_V, double U_max_V, double C_F, double exp_soc_min, double exp_soc_target, double exp_soc_initial, double exp_soc_max, bool exception = false)
+	{
+		var dataAdapter = new SerialHybridStrategyParameterDataAdapter();
+
+		var mass = mass_kg.SI<Kilogram>();
+		var Umin_sc = U_min_V.SI<Volt>();
+		var Umax_sc = U_max_V.SI<Volt>();
+		var Cap_sc = C_F.SI<Farad>();
+
+
+		var scData = new SuperCapData() {
+			Capacity = Cap_sc,
+			MaxVoltage = Umax_sc,
+			MinVoltage = Umin_sc,
+		};
+		if (exception) {
+			Assert.Throws<VectoException>(() =>
+				dataAdapter.CreateHybridStrategyParameters(null, scData, mass,
+					VectoRunData.OvcHevMode.ChargeSustaining));
+			Assert.Pass();
+		}
+		var parameter = dataAdapter.CreateHybridStrategyParameters(null, scData, mass, VectoRunData.OvcHevMode.ChargeSustaining);
+
+		Assert.AreEqual(exp_soc_target, parameter.TargetSoC, 1E-3);
+		Assert.AreEqual(exp_soc_min, parameter.MinSoC, 1E-3);
+		Assert.AreEqual(exp_soc_max, parameter.MaxSoC, 1E-3);
+
+
+		Assert.AreEqual(exp_soc_initial, parameter.InitialSoc, 1E-3);
+	}
 
 }
\ No newline at end of file
-- 
GitLab