From 1ad7f1a122618418efd570232a0963db3dabd77a Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Tue, 21 Jun 2022 17:08:33 +0200
Subject: [PATCH] bugfixes IHPC: use APTN transmission without traction
 interruption for IHPC

---
 VectoCommon/VectoCommon/Models/GearboxType.cs |   2 +-
 .../EngineeringDataAdapter.cs                 |   7 +-
 .../EngineeringModeVectoRunDataFactory.cs     |  12 ++-
 .../Simulation/Impl/PowertrainBuilder.cs      |   2 +-
 .../SimulationComponent/Data/GearboxData.cs   |   4 +-
 .../Impl/DefaultDriverStrategy.cs             |   2 +-
 .../Impl/HybridController.cs                  | 100 +++++++++++++++++-
 .../Strategies/HybridStrategy.cs              |  66 ++++--------
 .../Strategies/TestPowertrain.cs              |   2 +-
 .../OutputData/FileIO/JSONFileWriter.cs       |   2 +-
 .../OutputData/SummaryDataContainer.cs        |   4 +-
 11 files changed, 136 insertions(+), 67 deletions(-)

diff --git a/VectoCommon/VectoCommon/Models/GearboxType.cs b/VectoCommon/VectoCommon/Models/GearboxType.cs
index bedc03bf0a..b10f2acc2e 100644
--- a/VectoCommon/VectoCommon/Models/GearboxType.cs
+++ b/VectoCommon/VectoCommon/Models/GearboxType.cs
@@ -70,7 +70,7 @@ namespace TUGraz.VectoCommon.Models
 
 		[DebuggerStepThrough]
 		public static bool AutomaticTransmission(this GearboxType type) =>
-			type == GearboxType.ATPowerSplit || type == GearboxType.ATSerial || type == GearboxType.APTN;
+			type == GearboxType.ATPowerSplit || type == GearboxType.ATSerial || type == GearboxType.APTN || type == GearboxType.IHPC;
 
 		[DebuggerStepThrough]
 		public static bool ManualTransmission(this GearboxType type) =>
diff --git a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs
index b4099bce71..33745b5cf8 100644
--- a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs
+++ b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs
@@ -210,7 +210,7 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter
 
 			retVal.Inertia = engine.Inertia +
 							(gbx != null && gbx.Type.AutomaticTransmission()
-								? (gbx.Type == GearboxType.APTN ? 0.SI<KilogramSquareMeter>() : torqueConverter.Inertia)
+								? (gbx.Type == GearboxType.APTN || gbx.Type == GearboxType.IHPC ? 0.SI<KilogramSquareMeter>() : torqueConverter.Inertia)
 								: 0.SI<KilogramSquareMeter>());
 			retVal.EngineStartTime = engine.EngineStartTime ?? DeclarationData.Engine.DefaultEngineStartTime;
 			var limits = torqueLimits.ToDictionary(e => e.Gear);
@@ -333,7 +333,7 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter
 
 			SetEngineeringData(gearbox, gearshiftData, retVal);
 
-			var hasTorqueConverter = retVal.Type.AutomaticTransmission() && retVal.Type != GearboxType.APTN;
+			var hasTorqueConverter = retVal.Type.AutomaticTransmission() && retVal.Type != GearboxType.APTN && retVal.Type != GearboxType.IHPC;
 
 			var gearDifferenceRatio = hasTorqueConverter && gearbox.Gears.Count > 2
 				? gearbox.Gears[0].Ratio / gearbox.Gears[1].Ratio
@@ -431,8 +431,7 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter
 			IGearboxEngineeringInputData gearbox, IGearshiftEngineeringInputData gearshiftData, GearboxData retVal)
 		{
 			retVal.Inertia = gearbox.Type.ManualTransmission() ? gearbox.Inertia : 0.SI<KilogramSquareMeter>();
-			retVal.TractionInterruption = gearbox.Type == GearboxType.APTN ? 0.SI<Second>() : gearbox.TractionInterruption;
-			
+			retVal.TractionInterruption = gearbox.Type == GearboxType.APTN || gearbox.Type == GearboxType.IHPC ? 0.SI<Second>() : gearbox.TractionInterruption;
 		}
 
 		public AxleGearData CreateAxleGearData(IAxleGearInputData data)
diff --git a/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringModeVectoRunDataFactory.cs b/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringModeVectoRunDataFactory.cs
index da4e2d0104..17625e4109 100644
--- a/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringModeVectoRunDataFactory.cs
+++ b/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringModeVectoRunDataFactory.cs
@@ -311,9 +311,9 @@ namespace TUGraz.VectoCore.InputData.Reader.Impl
 
 				var crossWindRequired = vehicle.Components.AirdragInputData.CrossWindCorrectionMode ==
 										CrossWindCorrectionMode.VAirBetaLookupTable;
-                var ptoTransmissionData = dao.CreateBatteryElectricPTOTransmissionData(vehicle.Components.PTOTransmissionInputData);
+				var ptoTransmissionData = dao.CreateBatteryElectricPTOTransmissionData(vehicle.Components.PTOTransmissionInputData);
 
-                var drivingCycle = GetDrivingCycle(cycle, crossWindRequired);
+				var drivingCycle = GetDrivingCycle(cycle, crossWindRequired);
 
 				var vehicleData = dao.CreateVehicleData(vehicle);
 				yield return new VectoRunData
@@ -329,8 +329,8 @@ namespace TUGraz.VectoCore.InputData.Reader.Impl
 					Aux = dao.CreateAuxiliaryData(vehicle.Components.AuxiliaryInputData),
 					BusAuxiliaries = dao.CreateBusAuxiliariesData(vehicle.Components.AuxiliaryInputData, vehicleData, VectoSimulationJobType.BatteryElectricVehicle),
 					Retarder = dao.CreateRetarderData(vehicle.Components.RetarderInputData, powertrainPosition),
-                    PTO = ptoTransmissionData,
-                    Cycle = new DrivingCycleProxy(drivingCycle, cycle.Name),
+					PTO = ptoTransmissionData,
+					Cycle = new DrivingCycleProxy(drivingCycle, cycle.Name),
 					ExecutionMode = ExecutionMode.Engineering,
 					ElectricMachinesData = electricMachinesData,
 					//HybridStrategyParameters = dao.CreateHybridStrategyParameters(InputDataProvider.JobInputData.HybridStrategyParameters),
@@ -578,6 +578,10 @@ namespace TUGraz.VectoCore.InputData.Reader.Impl
 						? VectoSimulationJobType.ParallelHybridVehicle
 						: VectoSimulationJobType.ConventionalVehicle;
 
+					if (powertrainPosition == PowertrainPosition.HybridP2 && gearboxData.Type.AutomaticTransmission()) {
+						throw new VectoException(
+							"Powertrain Architecture 'hybrid electric vehicle, P2' with AT transmission not supported");
+					}
 					var vehicleData = dao.CreateVehicleData(vehicle);
 
 					var gearshiftParams = dao.CreateGearshiftData(
diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs
index f4f4561b5f..f9fc271aaa 100644
--- a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs
@@ -1627,7 +1627,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 
 		private static IGearbox GetSimpleGearbox(IVehicleContainer container, VectoRunData runData)
 		{
-			if (runData.GearboxData.Type.AutomaticTransmission() && runData.GearboxData.Type != GearboxType.APTN) {
+			if (runData.GearboxData.Type.AutomaticTransmission() && runData.GearboxData.Type != GearboxType.APTN && runData.GearboxData.Type != GearboxType.IHPC) {
 				new ATClutchInfo(container);
 				return new ATGearbox(container, null);
 			}
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/GearboxData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/GearboxData.cs
index 36384aae71..f93e0b4c04 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Data/GearboxData.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/GearboxData.cs
@@ -135,11 +135,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 					return new ValidationResult("Torque Converter can only be used with AT gearbox model");
 				}
 			} else {
-				if (gearboxData.Type.AutomaticTransmission() && gearboxData.Type != GearboxType.APTN) {
+				if (gearboxData.Type.AutomaticTransmission() && gearboxData.Type != GearboxType.APTN && gearboxData.Type != GearboxType.IHPC) {
 					return new ValidationResult("AT gearbox model requires torque converter");
 				}
 			}
-			if (gearboxData.Type.AutomaticTransmission() && gearboxData.Type != GearboxType.APTN) {
+			if (gearboxData.Type.AutomaticTransmission() && gearboxData.Type != GearboxType.APTN && gearboxData.Type != GearboxType.IHPC) {
 				gearboxData.TorqueConverterData.RequiredSpeedRatio =
 					Math.Round(gearboxData.Gears[1].TorqueConverterRatio / gearboxData.Gears[1].Ratio, 4) * 0.95;
 				result.AddRange(gearboxData.TorqueConverterData.Validate(mode, jobType, emPos, gearboxData.Type, emsMission));
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs
index be24cb9f28..0683d8833c 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs
@@ -438,7 +438,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			var axleLoss = DataBus.AxlegearInfo.AxlegearLoss();
 			var emDragLoss = CalculateElectricMotorDragLoss();
 			var iceDragLoss = 0.SI<Watt>();
-			if (dataBus.GearboxInfo.GearboxType.AutomaticTransmission()) {
+			if (dataBus.GearboxInfo.GearboxType.AutomaticTransmission() && dataBus.GearboxInfo.GearboxType != GearboxType.IHPC) {
 				if (ADAS.EcoRoll == EcoRollType.None && ATEcoRollReleaseLockupClutch) {
 					iceDragLoss = DataBus.EngineInfo.EngineDragPower(DataBus.EngineInfo.EngineSpeed);
 				}
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/HybridController.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/HybridController.cs
index 3b0b51dc8c..81a9173077 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/HybridController.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/HybridController.cs
@@ -38,9 +38,22 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		public HybridController(IVehicleContainer container, IHybridControlStrategy strategy, IElectricSystem es) : base(container)
 		{
 			_electricMotorCtl = new Dictionary<PowertrainPosition, ElectricMotorController>();
-			_shiftStrategy = container.RunData.GearboxData.Type.AutomaticTransmission()
-				? new HybridCtlATShiftStrategy(this, container)
-				: new HybridCtlShiftStrategy(this, container);
+
+			switch (container.RunData.GearboxData.Type) {
+				case GearboxType.ATPowerSplit:
+				case GearboxType.ATSerial:
+					_shiftStrategy = new HybridCtlATShiftStrategy(this, container);
+					break;
+				case GearboxType.AMT:
+					_shiftStrategy = new HybridCtlShiftStrategy(this, container);
+					break;
+				case GearboxType.APTN:
+				case GearboxType.IHPC:
+					_shiftStrategy = new HybridCtlIHPCShiftStrategy(this, container);
+					break;
+				default: throw new ArgumentException($"Unsupported Gearbox type for Hybrid Controller: {container.RunData.GearboxData.Type}");
+			}
+			
 			_hybridStrategy = strategy;
 			strategy.Controller = this;
 
@@ -291,7 +304,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			protected GearshiftPosition _nextGear { get; set; }
 
 			protected readonly GearList GearList;
-			private readonly VectoRunData _runData;
+			protected readonly VectoRunData _runData;
 
 			public HybridCtlShiftStrategy(HybridController hybridController, IVehicleContainer container) : base(
 				container)
@@ -564,5 +577,84 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 				return base.SpeedTooHighForEngine(gear, outAngularSpeed);
 			}
 		}
+
+		///=======================================================================================
+
+		public class HybridCtlIHPCShiftStrategy : HybridCtlATShiftStrategy
+		{
+			protected new APTNGearbox _gearbox;
+
+			public HybridCtlIHPCShiftStrategy(HybridController hybridController, IVehicleContainer container) : base(hybridController, container) { }
+
+			public override IGearbox Gearbox {
+				get => _gearbox;
+				set => _gearbox = value as APTNGearbox ?? throw new VectoException("This shift strategy can't handle gearbox of type {0}", value.GetType());
+			}
+
+			public override GearshiftPosition InitGear(Second absTime, Second dt, NewtonMeter torque, PerSecond outAngularVelocity)
+			{
+				if (DataBus.VehicleInfo.VehicleSpeed.IsEqual(0)) {
+					return InitStartGear(absTime, torque, outAngularVelocity);
+				}
+
+				foreach (var gear in Gears.Reverse()) {
+					var response = _gearbox.Initialize(absTime, gear, torque, outAngularVelocity);
+
+					if (response.Engine.EngineSpeed > DataBus.EngineInfo.EngineRatedSpeed || response.Engine.EngineSpeed < DataBus.EngineInfo.EngineIdleSpeed) {
+						continue;
+					}
+
+					if (!IsBelowDownShiftCurve(gear, response.Engine.PowerRequest / response.Engine.EngineSpeed, response.Engine.EngineSpeed)) {
+						_gearbox.Disengaged = false;
+						return gear;
+					}
+				}
+
+				// fallback: start with first gear;
+				_gearbox.Disengaged = false;
+				return Gears.First();
+			}
+
+			public override void Disengage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) { }
+
+			protected GearshiftPosition InitStartGear(Second absTime, NewtonMeter outTorque, PerSecond outAngularVelocity)
+			{
+				if (!DataBus.EngineCtl.CombustionEngineOn) {
+					return _nextGear;
+				}
+
+				foreach (var gear in GearList.IterateGears(MaxStartGear, GearList.First())) {
+					//for (var gear = MaxStartGear; gear > 1; gear--) {
+					var inAngularSpeed = outAngularVelocity * GearboxModelData.Gears[gear.Gear].Ratio;
+
+					var ratedSpeed = DataBus.EngineInfo.EngineRatedSpeed;
+					if (inAngularSpeed > ratedSpeed || inAngularSpeed.IsEqual(0)) {
+						continue;
+					}
+
+					var response = _gearbox.Initialize(absTime, gear, outTorque, outAngularVelocity);
+
+					var fullLoadPower =
+						response.Engine.DynamicFullLoadTorque; //EnginePowerRequest - response.DeltaFullLoad;
+					var reserve = 1 - response.Engine.TorqueOutDemand / fullLoadPower;
+
+					if (_runData != null && _runData.HybridStrategyParameters.MaxPropulsionTorque?.GetVECTOValueOrDefault(gear) != null) {
+						var tqRequest = response.Gearbox.InputTorque;
+						var maxTorque = _runData.HybridStrategyParameters.MaxPropulsionTorque[gear].FullLoadDriveTorque(response.Gearbox.InputSpeed);
+						reserve = 1 - VectoMath.Min(response.Engine.TorqueOutDemand / fullLoadPower, tqRequest / maxTorque);
+					}
+
+					if (response.Engine.EngineSpeed > DataBus.EngineInfo.EngineIdleSpeed &&
+						reserve.IsGreaterOrEqual(0)) {
+						//reserve >= GearshiftParams.StartTorqueReserve) {
+						_nextGear = gear;
+						return gear;
+					}
+				}
+
+				_nextGear = GearList.First();
+				return _nextGear;
+			}
+		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs
index f752c820de..38e36b8cf9 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs
@@ -421,46 +421,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 
 		}
 
-		//protected virtual bool? CheckUpshiftTcTc(
-		//	Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, NewtonMeter inTorque,
-		//	PerSecond inAngularVelocity, GearshiftPosition gear, GearData currentGear, IResponse response)
-		//{
-		//	// C -> C+1
-		//	var nextGearPos = GearList.Successor(gear); // GearboxModelData.Gears[gear + 1];
-		//	var nextGear = ModelData.GearboxData.Gears[nextGearPos.Gear];
-		//	var gearRatio = nextGear.TorqueConverterRatio / currentGear.TorqueConverterRatio;
-		//	var minEngineSpeed = VectoMath.Min(700.RPMtoRad(), gearRatio * (DataBus.EngineInfo.EngineN80hSpeed - 150.RPMtoRad()));
-
-		//	var nextGearboxInSpeed = outAngularVelocity * nextGear.TorqueConverterRatio;
-		//	var nextGearboxInTorque = outTorque / nextGear.TorqueConverterRatio;
-		//	var shiftLosses = _gearbox.ComputeShiftLosses(outTorque, outAngularVelocity, nextGearPos) /
-		//					ModelData.GearboxData.PowershiftShiftTime / nextGearboxInSpeed;
-		//	nextGearboxInTorque += shiftLosses;
-		//	var tcOperatingPoint =
-		//		_gearbox.TorqueConverter.FindOperatingPoint(absTime, dt, nextGearboxInTorque, nextGearboxInSpeed);
-
-		//	var engineSpeedOverMin = tcOperatingPoint.InAngularVelocity.IsGreater(minEngineSpeed);
-		//	var avgSpeed = (DataBus.EngineInfo.EngineSpeed + tcOperatingPoint.InAngularVelocity) / 2;
-		//	var engineMaxTorque = DataBus.EngineInfo.EngineStationaryFullPower(avgSpeed) / avgSpeed;
-		//	var engineInertiaTorque = Formulas.InertiaPower(
-		//								DataBus.EngineInfo.EngineSpeed, tcOperatingPoint.InAngularVelocity, ModelData.EngineData.Inertia, dt) / avgSpeed;
-		//	var engineTorqueBelowMax =
-		//		tcOperatingPoint.InTorque.IsSmallerOrEqual(engineMaxTorque - engineInertiaTorque);
-
-		//	var reachableAcceleration =
-		//		EstimateAcceleration(
-		//			outAngularVelocity, outTorque, inAngularVelocity, inTorque, gear.Gear, response); // EstimateAccelerationForGear(gear + 1, outAngularVelocity);
-		//	var minAcceleration = VectoMath.Min(
-		//		ModelData.GearboxData.TorqueConverterData.CCUpshiftMinAcceleration,
-		//		DataBus.DriverInfo.DriverAcceleration);
-		//	var minAccelerationReachable = reachableAcceleration.IsGreaterOrEqual(minAcceleration);
-
-		//	if (engineSpeedOverMin && engineTorqueBelowMax && minAccelerationReachable) {
-		//		return true;
-		//	}
-
-		//	return null;
-		//}
 	}
 
 	// =====================================================
@@ -1102,7 +1062,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 				var endSpeed = DataBus.VehicleInfo.VehicleSpeed +
 								DataBus.DriverInfo.DriverAcceleration * ModelData.GearboxData.TractionInterruption;
 				if (EngineSpeedTooLow(response)
-					&& DataBus.GearboxInfo.GearboxType.ManualTransmission() 
+					&& (DataBus.GearboxInfo.GearboxType.ManualTransmission() || DataBus.GearboxInfo.GearboxType == GearboxType.IHPC) 
 					&& endSpeed.IsSmallerOrEqual(disengageSpeedThreshold, 0.1.KMPHtoMeterPerSecond())) {
 					var responseEmOff = ResponseEmOff;
 					responseEmOff.Gear = new GearshiftPosition(0);
@@ -1234,7 +1194,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 					? (maxRecuperationResponse as ResponseDryRun).DeltaDragLoadTorque
 					: maxRecuperationResponse.Engine.TotalTorqueDemand - maxRecuperationResponse.Engine.DragTorque;
 
-				if (!maxRecuperationResponse.Engine.EngineOn && DataBus.GearboxInfo.GearboxType.AutomaticTransmission()) {
+				var isAPTWithTorqueConverter = DataBus.GearboxInfo.GearboxType.AutomaticTransmission() &&
+												DataBus.GearboxInfo.GearboxType != GearboxType.IHPC;
+				if (!maxRecuperationResponse.Engine.EngineOn && isAPTWithTorqueConverter) {
 					deltaDragTqMaxRecuperation = maxRecuperationResponse.Gearbox.InputTorque;
 				}
 
@@ -1278,7 +1240,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 						var deltaDragLoad = disengaged
 							? (r as ResponseDryRun).DeltaDragLoadTorque
 							: r.Engine.TotalTorqueDemand - r.Engine.DragTorque;
-						if (!r.Engine.EngineOn && DataBus.GearboxInfo.GearboxType.AutomaticTransmission()) {
+						if (!r.Engine.EngineOn && isAPTWithTorqueConverter) {
 							deltaDragLoad = r.Gearbox.InputTorque;
 						}
 						return deltaDragLoad;
@@ -1299,7 +1261,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 						var deltaDragLoad = disengaged
 							? (r as ResponseDryRun).DeltaDragLoadTorque
 							: r.Engine.TotalTorqueDemand - r.Engine.DragTorque;
-						if (!r.Engine.EngineOn && DataBus.GearboxInfo.GearboxType.AutomaticTransmission()) {
+						if (!r.Engine.EngineOn && isAPTWithTorqueConverter) {
 							deltaDragLoad = r.Gearbox.InputTorque;
 						}
 						return deltaDragLoad.Value();
@@ -1321,7 +1283,19 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 						}
 					};
 					entry.Response = RequestDryRun(absTime, dt, outTorque, outAngularVelocity, nextGear, entry.Setting);
-					eval.Add(entry);
+					if (entry.Response.ElectricSystem.ConsumerPower.IsGreater(0)) {
+						eval.Add(entry);
+					} else {
+						// for the found operating point, although recuperating no electric energy is generated. leave EM off
+						var off = ResponseEmOff;
+						if (vehiclespeedBelowThreshold && (emPos == PowertrainPosition.HybridP2 || emPos == PowertrainPosition.HybridP1)) {
+							off.Setting.GearboxInNeutral = true;
+						} else {
+							off.Setting.GearboxInNeutral = PreviousState.Solution?.Setting.GearboxInNeutral ?? false;
+						}
+
+						eval.Add(off);
+					}
 				} else {
 					if (emRecuperationTq.IsGreater(0)) {
 						eval.Add(
@@ -1359,7 +1333,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 				return false;
 			}
 
-			if (DataBus.GearboxInfo.GearboxType.AutomaticTransmission()) {
+			if (DataBus.GearboxInfo.GearboxType.AutomaticTransmission() && DataBus.GearboxInfo.GearboxType != GearboxType.IHPC) {
 				return firstResponse.Engine.EngineOn
 					? firstResponse.Engine.EngineSpeed.IsSmaller(ModelData.EngineData.IdleSpeed)
 					: (firstResponse.Gearbox.InputSpeed?.IsSmaller(ModelData.EngineData.IdleSpeed) ?? false);
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/TestPowertrain.cs b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/TestPowertrain.cs
index 6e45b89d22..42c94ada79 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/TestPowertrain.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/TestPowertrain.cs
@@ -81,7 +81,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies
 				}
 			}
 			
-			if (Gearbox != null && Gearbox.GearboxType.AutomaticTransmission() && Gearbox.GearboxType != GearboxType.APTN) {
+			if (Gearbox != null && Gearbox.GearboxType.AutomaticTransmission() && Gearbox.GearboxType != GearboxType.APTN && Gearbox.GearboxType != GearboxType.IHPC) {
 				TorqueConverter = Container.TorqueConverterInfo as TorqueConverter;
 				if (TorqueConverter == null) {
 					throw new VectoException("Torque converter missing for automatic transmission: {0}", Container.TorqueConverterInfo?.GetType().FullName);
diff --git a/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs b/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs
index 6dc185bc8e..d74e5936b0 100644
--- a/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs
+++ b/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs
@@ -355,7 +355,7 @@ public class JSONFileWriter : IOutputFileWriter
 		body.Add(JsonKeys.Gearbox_GearboxType, gbx.Type.ToString());
 
 		var torqueConverterDict = new Dictionary<string, object> { { "Enabled", torqueConverter != null && gbx.Type.AutomaticTransmission() } };
-		if (gbx.Type.AutomaticTransmission() && gbx.Type != GearboxType.APTN) {
+		if (gbx.Type.AutomaticTransmission() && gbx.Type != GearboxType.APTN && gbx.Type != GearboxType.IHPC) {
 			torqueConverterDict.Add("File", GetRelativePath(torqueConverter.TCData.Source, Path.GetDirectoryName(filename)));
 			torqueConverterDict.Add(JsonKeys.Gearbox_TorqueConverter_ReferenceRPM, Math.Round(torqueConverter.ReferenceRPM.AsRPM, 4));
 			torqueConverterDict.Add(JsonKeys.Gearbox_TorqueConverter_Inertia, torqueConverter.Inertia.Value());
diff --git a/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs b/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs
index 1933a89eb7..7d73e77a65 100644
--- a/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs
+++ b/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs
@@ -660,7 +660,7 @@ namespace TUGraz.VectoCore.OutputData
 			var eGbxOut = modData.TimeIntegral<WattSecond>(gbxOutSignal, x => x > 0);
 			row[Fields.AVERAGE_GEARBOX_EFFICIENCY] = eGbxIn.IsEqual(0, 1e-9) ? 0 : (eGbxOut / eGbxIn).Value();
 
-			if (runData.GearboxData != null && runData.GearboxData.Type.AutomaticTransmission() && runData.GearboxData.Type != GearboxType.APTN) {
+			if (runData.GearboxData != null && runData.GearboxData.Type.AutomaticTransmission() && runData.GearboxData.Type != GearboxType.APTN && runData.GearboxData.Type != GearboxType.IHPC) {
 				var eTcIn = modData.TimeIntegral<WattSecond>(ModalResultField.P_TC_in, x => x > 0);
 				var eTcOut = eGbxIn;
 				row[Fields.AVERAGE_TORQUE_CONVERTER_EFFICIENCY_WITHOUT_LOCKUP] = eTcIn.IsEqual(0, 1e-9) ? 0 : (eTcOut / eTcIn).Value();
@@ -1116,7 +1116,7 @@ namespace TUGraz.VectoCore.OutputData
 				row[Fields.GEAR_RATIO_LAST_GEAR] = data.Gears.Count > 0
 					? (ConvertedSI)data.Gears.Last().Value.Ratio.SI<Scalar>()
 					: (ConvertedSI)0.SI<Scalar>();
-				if (data.Type != GearboxType.APTN) {
+				if (data.Type != GearboxType.APTN && data.Type != GearboxType.IHPC) {
 					row[Fields.TORQUECONVERTER_MANUFACTURER] = data.TorqueConverterData.Manufacturer;
 					row[Fields.TORQUECONVERTER_MODEL] = data.TorqueConverterData.ModelName;
 					row[Fields.TORQUE_CONVERTER_CERTIFICATION_NUMBER] =
-- 
GitLab