diff --git a/VectoCommon/VectoCommon/Models/HybridStrategyResponse.cs b/VectoCommon/VectoCommon/Models/HybridStrategyResponse.cs index 1f6a5d165ea3b7b30ba8099c4376519793a4303f..7c9d02ca5523a72f1cb5db35220e6faf3707c971 100644 --- a/VectoCommon/VectoCommon/Models/HybridStrategyResponse.cs +++ b/VectoCommon/VectoCommon/Models/HybridStrategyResponse.cs @@ -58,7 +58,10 @@ namespace TUGraz.VectoCommon.Models { EngineTorqueDemandTooLow = 1 << 5, EngineSpeedAboveUpshift = 1 << 6, EngineSpeedBelowDownshift = 1 << 7, - NoResponseAvailable = 1 << 8, - Evaluated = 1 << 9, + BatteryBelowMinSoC = 1 << 8, + BatteryAboveMaxSoc = 1 << 9, + BatterySoCTooLow = 1 << 10, + NoResponseAvailable = 1 << 11, + Evaluated = 1 << 12, } } \ No newline at end of file diff --git a/VectoCommon/VectoCommon/Utils/SI.cs b/VectoCommon/VectoCommon/Utils/SI.cs index 55c8b582fd54ea653373ebb53be1e224e7054a33..deb03067c7c9fb0b332dcf71749b9bd454f79167 100644 --- a/VectoCommon/VectoCommon/Utils/SI.cs +++ b/VectoCommon/VectoCommon/Utils/SI.cs @@ -1082,6 +1082,11 @@ namespace TUGraz.VectoCommon.Utils { return SIBase<Ampere>.Create(ampereSecond.Value() / t.Value()); } + + public static WattSecond operator *(AmpereSecond ampereSeconds, Volt v) + { + return SIBase<WattSecond>.Create(ampereSeconds.Val * v.Value()); + } } /// <summary> diff --git a/VectoCore/VectoCore/Models/Simulation/DataBus/IBatteryInfo.cs b/VectoCore/VectoCore/Models/Simulation/DataBus/IBatteryInfo.cs index 3b9d6036e42fa719fbeb0b34728b4053bafb1ca2..26b8155839be38e66258b15c8b15183ff487e826 100644 --- a/VectoCore/VectoCore/Models/Simulation/DataBus/IBatteryInfo.cs +++ b/VectoCore/VectoCore/Models/Simulation/DataBus/IBatteryInfo.cs @@ -8,6 +8,8 @@ namespace TUGraz.VectoCore.Models.Simulation.DataBus double StateOfCharge { get; } + WattSecond StoredEnergy { get; } + //Ampere MaxCurrent { get; } Watt MaxChargePower(Second dt); diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/HybridStrategyParameters.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/HybridStrategyParameters.cs index b1a0a865f50b35f2f928cc7ff8d0abe553209a99..d1775d3b3b19d70d56d5406a2d20bd7d106adf08 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/HybridStrategyParameters.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/HybridStrategyParameters.cs @@ -1,4 +1,6 @@ -namespace TUGraz.VectoCore.Models.SimulationComponent.Data { +using TUGraz.VectoCommon.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data { public class HybridStrategyParameters { public double EquivalenceFactor { get; set; } @@ -8,5 +10,9 @@ public double MaxSoC { get; set; } public double TargetSoC { get; set; } + + public Second AuxReserveTime { get; set; } + + public Second AuxReserveChargeTime { get; set; } } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs index 01749e979bbb53d98059abdb084513f9bc24d6c3..fc6faa67df113ac1f11c9e349499b4ab795cdf0f 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs @@ -185,6 +185,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl get { return PreviousState.StateOfCharge; } } + public WattSecond StoredEnergy + { + get { + return PreviousState.StateOfCharge * ModelData.Capacity * ModelData.SOCMap.Lookup(PreviousState.StateOfCharge); + } + } + public Watt MaxChargePower(Second dt) { var maxChargeCurrent = VectoMath.Min((ModelData.MaxSOC - PreviousState.StateOfCharge) * ModelData.Capacity / dt, diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs index 2c79bfab59561dbb29e7ee18ebebb8b387d148a1..4635e1ac35acdea7c7514045f172e8504f8fff0a 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs @@ -62,6 +62,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies protected HybridStrategyParameters StrategyParameters; protected DebugData DebugData = new DebugData(); + private WattSecond BatteryDischargeEnergyThreshold; public HybridStrategy(VectoRunData runData, IVehicleContainer vehicleContainer) { @@ -119,6 +120,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies Log.Warn("Gear-range for FC-based gearshift must be either 1 or 2!"); shiftStrategyParameters.AllowedGearRangeFC = shiftStrategyParameters.AllowedGearRangeFC.LimitTo(1, 2); } + + var auxEnergyReserve = ModelData.ElectricAuxDemand * StrategyParameters.AuxReserveTime; + var minSoc = Math.Max(ModelData.BatteryData.MinSOC, StrategyParameters.MinSoC); + BatteryDischargeEnergyThreshold = ModelData.BatteryData.Capacity * minSoc * ModelData.BatteryData.SOCMap.Lookup(minSoc) + + auxEnergyReserve; } @@ -387,7 +393,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies var best = eval.Where(x => !double.IsNaN(x.Score)).OrderBy(x => x.Score).FirstOrDefault(); if (best == null) { best = eval.OrderBy(x => Math.Abs((int)currentGear - x.Gear)).FirstOrDefault( - x => !(!x.ICEOff && x.IgnoreReason.InvalidEngineSpeed())); + x => !(!x.ICEOff && x.IgnoreReason.InvalidEngineSpeed() && !(x.IgnoreReason.BatteryDemandExceeded()))); if (best == null /*&& dryRun*/) { var emEngaged = (!ElectricMotorCanPropellDuringTractionInterruption || (DataBus.GearboxInfo.GearEngaged(absTime) && eval.First().Response.Gearbox.Gear != 0)); @@ -412,7 +418,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies if (DataBus.DriverInfo.DrivingAction == DrivingAction.Accelerate && allOverload) { if (ElectricMotorCanPropellDuringTractionInterruption || DataBus.GearboxInfo.GearEngaged(absTime)) { // overload, EM can support - use solution with max EM power - best = eval.OrderBy(x => Math.Abs((int)currentGear - x.Gear)).MinBy(x => x.Setting.MechanicalAssistPower.Sum(e => e.Value ?? 0.SI<NewtonMeter>())); + var filtered = eval.Where(x => !x.IgnoreReason.BatteryDemandExceeded() && + (x.IgnoreReason & HybridConfigurationIgnoreReason.BatterySoCTooLow) == 0) + .OrderBy(x => Math.Abs((int)currentGear - x.Gear)).ToArray(); + if (filtered.Length == 0) { + best = eval.Where( + x => !x.IgnoreReason.BatteryDemandExceeded()) + .OrderBy(x => Math.Abs((int)currentGear - x.Gear)) + .ThenBy(x => -x.Response.ElectricSystem.BatteryPowerDemand.Value()).First(); + } else { + best = filtered.MinBy( + x => x.Setting.MechanicalAssistPower.Sum(e => e.Value ?? 0.SI<NewtonMeter>())); + } } } if ((DataBus.DriverInfo.DrivingAction == DrivingAction.Accelerate // || @@ -877,6 +894,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies tmp.IgnoreReason |= HybridConfigurationIgnoreReason.EngineSpeedBelowDownshift; } + SetBatteryCosts(resp, dt, tmp); + if (allowIceOff && resp.Engine.TorqueOutDemand.IsEqual(0)) { // no torque from ICE requested, ICE could be turned off tmp.FuelCosts = 0; @@ -890,7 +909,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies //} } } - tmp.BatCosts = -(resp.ElectricSystem.BatteryPowerDemand * dt).Value(); + var maxSoC = Math.Min(ModelData.BatteryData.MaxSOC, StrategyParameters.MaxSoC); var minSoC = Math.Max(ModelData.BatteryData.MinSOC, StrategyParameters.MinSoC); tmp.SoCPenalty = 1 - Math.Pow((DataBus.BatteryInfo.StateOfCharge - StrategyParameters.TargetSoC) / (0.5 * (maxSoC - minSoC)), 5); @@ -910,8 +929,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies : 1; if (!DataBus.EngineCtl.CombustionEngineOn && !tmp.ICEOff && DataBus.BatteryInfo.StateOfCharge.IsGreater(socthreshold)) { - tmp.ICEStartPenalty1 = IceRampUpCosts; - tmp.ICEStartPenalty2 = IceIdlingCosts; + tmp.ICEStartPenalty1 = IceRampUpCosts / 10; + tmp.ICEStartPenalty2 = IceIdlingCosts * 0; } else { tmp.ICEStartPenalty1 = 0; tmp.ICEStartPenalty2 = 0; @@ -921,7 +940,43 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies } } - + private void SetBatteryCosts(IResponse resp, Second dt, HybridResultEntry tmp) + { + var batEnergyStored = DataBus.BatteryInfo.StoredEnergy; + var batEnergy = resp.ElectricSystem.BatteryPowerDemand * dt; + var batPower = resp.ElectricSystem.BatteryResponse.BatteryPower; + if (!batPower.IsBetween( + resp.ElectricSystem.BatteryResponse.MaxBatteryLoadDischarge, + resp.ElectricSystem.BatteryResponse.MaxBatteryLoadCharge)) { + // battery power demand too high - would discharge below min SoC / charge above max SoC + tmp.BatCosts = double.NaN; + tmp.IgnoreReason |= batPower.IsSmaller( + resp.ElectricSystem.BatteryResponse.MaxBatteryLoadDischarge) + ? HybridConfigurationIgnoreReason.BatteryBelowMinSoC + : HybridConfigurationIgnoreReason.BatteryAboveMaxSoc; + } + if ((batEnergyStored + batEnergy).IsSmaller(BatteryDischargeEnergyThreshold)) { + // battery level would go below buffer for auxiliary power - do not alow at + tmp.BatCosts = double.NaN; + tmp.IgnoreReason |= HybridConfigurationIgnoreReason.BatterySoCTooLow; + } + if (batEnergyStored.IsSmaller(BatteryDischargeEnergyThreshold)) { + var missingBatCharge = BatteryDischargeEnergyThreshold - batEnergyStored; + var minChargePower = missingBatCharge / StrategyParameters.AuxReserveChargeTime; + if (batPower.IsSmaller(minChargePower)) { + tmp.BatCosts = double.NaN; + tmp.IgnoreReason |= HybridConfigurationIgnoreReason.BatterySoCTooLow; + } else { + tmp.BatCosts = 0; + tmp.IgnoreReason &= ~HybridConfigurationIgnoreReason.BatterySoCTooLow; + } + } + if (!double.IsNaN(tmp.BatCosts)) { + tmp.BatCosts = -(batEnergy).Value(); + } + } + + public virtual HybridStrategyResponse Initialize(NewtonMeter outTorque, PerSecond outAngularVelocity) { var retVal = new HybridStrategyResponse() @@ -1023,5 +1078,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies HybridConfigurationIgnoreReason.EngineSpeedBelowDownshift | HybridConfigurationIgnoreReason.EngineSpeedAboveUpshift)) != 0; } + + public static bool BatteryDemandExceeded(this HybridConfigurationIgnoreReason x) + { + return (x & (HybridConfigurationIgnoreReason.BatteryAboveMaxSoc | + HybridConfigurationIgnoreReason.BatteryBelowMinSoC)) != 0; + } } } \ No newline at end of file diff --git a/VectoCore/VectoCoreTest/Integration/Hybrid/ParallelHybridTest.cs b/VectoCore/VectoCoreTest/Integration/Hybrid/ParallelHybridTest.cs index c9c19209bd39204ba3dc65ba2744471902bbe5e8..992b8125c79929ef833567b183aceb9126fc7aa0 100644 --- a/VectoCore/VectoCoreTest/Integration/Hybrid/ParallelHybridTest.cs +++ b/VectoCore/VectoCoreTest/Integration/Hybrid/ParallelHybridTest.cs @@ -695,6 +695,8 @@ namespace TUGraz.VectoCore.Tests.Integration.Hybrid MinSoC = 0.22, MaxSoC = 0.8, TargetSoC = 0.5, + AuxReserveTime = 5.SI<Second>(), + AuxReserveChargeTime = 2.SI<Second>() }; } diff --git a/VectoCore/VectoCoreTest/Utils/MockBattery.cs b/VectoCore/VectoCoreTest/Utils/MockBattery.cs index 6d5d29546976d648d8d37323a37432a4ef721449..fe42e54e3784986386cac8f64dc9787f5376e761 100644 --- a/VectoCore/VectoCoreTest/Utils/MockBattery.cs +++ b/VectoCore/VectoCoreTest/Utils/MockBattery.cs @@ -26,6 +26,11 @@ namespace TUGraz.VectoCore.Tests.Utils { public double StateOfCharge { get; set; } + public WattSecond StoredEnergy + { + get { throw new System.NotImplementedException(); } + } + public Ampere MaxCurrent { get { return 375.SI<Ampere>(); }