diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs index 44e79c8802405c4047bc38b851579c355fd1bae5..52149d3f4c79204f4b2ed6527fb8f7790cf3a881 100644 --- a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs +++ b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs @@ -79,46 +79,62 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl /// </summary> public static class PowertrainBuilder { + private static readonly Dictionary<CycleType, Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>> _builders; + + private static readonly Dictionary<PowertrainPosition, Func<VectoRunData, VehicleContainer, ElectricSystem, IPowerTrainComponent, IElectricMotor>> _MeasuredSpeedBEVBuilders; + + static PowertrainBuilder() + { + var pWheelBuilders = new Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>(); + pWheelBuilders.Add(VectoSimulationJobType.ConventionalVehicle, BuildPWheelConventional); + pWheelBuilders.Add(VectoSimulationJobType.BatteryElectricVehicle, BuildPWheelBatteryElectric); + + var measuredSpeedGearBuilders = new Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>(); + measuredSpeedGearBuilders.Add(VectoSimulationJobType.ConventionalVehicle, BuildMeasuredSpeedGearConventional); + measuredSpeedGearBuilders.Add(VectoSimulationJobType.BatteryElectricVehicle, BuildMeasuredSpeedGearBatteryElectric); + + var measuredSpeedBuilders = new Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>(); + measuredSpeedBuilders.Add(VectoSimulationJobType.ConventionalVehicle, BuildMeasuredSpeedConventional); + measuredSpeedBuilders.Add(VectoSimulationJobType.BatteryElectricVehicle, BuildMeasuredSpeedBatteryElectric); + + var vtpBuilders = new Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>(); + vtpBuilders.Add(VectoSimulationJobType.ConventionalVehicle, BuildVTPConventional); + + var engineOnlyBuilders = new Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>(); + engineOnlyBuilders.Add(VectoSimulationJobType.ConventionalVehicle, BuildEngineOnly); + + var distanceBuilders = new Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>(); + distanceBuilders.Add(VectoSimulationJobType.ConventionalVehicle, BuildFullPowertrainConventional); + distanceBuilders.Add(VectoSimulationJobType.ParallelHybridVehicle, BuildFullPowertrainParallelHybrid); + distanceBuilders.Add(VectoSimulationJobType.SerialHybridVehicle, BuildFullPowertrainSerialHybrid); + distanceBuilders.Add(VectoSimulationJobType.BatteryElectricVehicle, BuildFulPowertrainBatteryElectric); + distanceBuilders.Add(VectoSimulationJobType.EngineOnlySimulation, BuildEngineOnly); + distanceBuilders.Add(VectoSimulationJobType.IEPC_E, BuildFullPowertrainIEPCE); + distanceBuilders.Add(VectoSimulationJobType.IEPC_S, BuildFullPowertrainIEPCSerial); + + _builders = new Dictionary<CycleType, Dictionary<VectoSimulationJobType, Func<VectoRunData, IModalDataContainer, WriteSumData, IVehicleContainer>>>(); + _builders.Add(CycleType.PWheel, pWheelBuilders); + _builders.Add(CycleType.MeasuredSpeed, measuredSpeedBuilders); + _builders.Add(CycleType.MeasuredSpeedGear, measuredSpeedGearBuilders); + _builders.Add(CycleType.VTP, vtpBuilders); + _builders.Add(CycleType.EngineOnly, engineOnlyBuilders); + _builders.Add(CycleType.DistanceBased, distanceBuilders); + + _MeasuredSpeedBEVBuilders = new Dictionary<PowertrainPosition, Func<VectoRunData, VehicleContainer, ElectricSystem, IPowerTrainComponent, IElectricMotor>>(); + _MeasuredSpeedBEVBuilders.Add(PowertrainPosition.BatteryElectricE2, BuildMeasuredSpeedForBEV2); + _MeasuredSpeedBEVBuilders.Add(PowertrainPosition.BatteryElectricE3, BuildMeasuredSpeedForBEV3); + _MeasuredSpeedBEVBuilders.Add(PowertrainPosition.BatteryElectricE4, BuildMeasuredSpeedForBEV4); + } public static IVehicleContainer Build(VectoRunData data, IModalDataContainer modData, ISumData sumWriter = null) { - switch (data.Cycle.CycleType) { - case CycleType.DistanceBased: - switch (data.JobType) { - case VectoSimulationJobType.ConventionalVehicle: return BuildFullPowertrainConventional(data, modData, sumWriter); - case VectoSimulationJobType.ParallelHybridVehicle: return BuildFullPowertrainParallelHybrid(data, modData, sumWriter); - case VectoSimulationJobType.SerialHybridVehicle: return BuildFullPowertrainSerialHybrid(data, modData, sumWriter); - case VectoSimulationJobType.BatteryElectricVehicle: return BuildFulPowertrainBatteryElectric(data, modData, sumWriter); - case VectoSimulationJobType.EngineOnlySimulation: return BuildEngineOnly(data, modData, sumWriter); - case VectoSimulationJobType.IEPC_E: return BuildFullPowertrainIEPCE(data, modData, sumWriter); - case VectoSimulationJobType.IEPC_S: return BuildFullPowertrainIEPCSerial(data, modData, sumWriter); - default: throw new ArgumentOutOfRangeException($"Powertrain Builder cannot build Powertrain for JobType: {data.JobType}"); - } - case CycleType.EngineOnly: return BuildEngineOnly(data, modData, sumWriter); - case CycleType.PWheel: - switch (data.JobType) - { - case VectoSimulationJobType.ConventionalVehicle: return BuildPWheelConventional(data, modData, sumWriter); - case VectoSimulationJobType.BatteryElectricVehicle: return BuildPWheelBatteryElectric(data, modData, sumWriter); - default: throw new ArgumentOutOfRangeException($"Powertrain Builder cannot build Powertrain for JobType: {data.JobType}"); - } - case CycleType.VTP: return BuildVTP(data, modData, sumWriter); - case CycleType.MeasuredSpeed: - switch (data.JobType) - { - case VectoSimulationJobType.ConventionalVehicle: return BuildMeasuredSpeedConventional(data, modData, sumWriter); - case VectoSimulationJobType.BatteryElectricVehicle: return BuildMeasuredSpeedBatteryElectric(data, modData, sumWriter); - default: throw new ArgumentOutOfRangeException($"Powertrain Builder cannot build Powertrain for JobType: {data.JobType}"); - } - case CycleType.MeasuredSpeedGear: - switch (data.JobType) - { - case VectoSimulationJobType.ConventionalVehicle: return BuildMeasuredSpeedGearConventional(data, modData, sumWriter); - case VectoSimulationJobType.BatteryElectricVehicle: return BuildMeasuredSpeedGearBatteryElectric(data, modData, sumWriter); - default: throw new ArgumentOutOfRangeException($"Powertrain Builder cannot build Powertrain for JobType: {data.JobType}"); - } - default: throw new VectoException("Powertrain Builder cannot build Powertrain for CycleType: {0}", data.Cycle.CycleType); - } + var cycleType = data.Cycle.CycleType; + + if (!_builders.ContainsKey(cycleType) || !_builders[cycleType].ContainsKey(data.JobType)) { + throw new ArgumentException($"Powertrain Builder: cannot build {cycleType} powertrain for job type: {data.JobType}"); + } + + return _builders[cycleType][data.JobType].Invoke(data, modData, sumWriter); } /// <summary> @@ -152,23 +168,41 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl new DummyDriverInfo(container); return container; + } + + private static IVehicleContainer BuildForCycle(CycleType cycleType, VectoRunData data, IModalDataContainer modData, WriteSumData sumWriter) + { + VerifyCycleType(data, cycleType); + + if (!_builders.ContainsKey(cycleType) || !_builders[cycleType].ContainsKey(data.JobType)) { + throw new ArgumentException($"Powertrain Builder: cannot build {cycleType} powertrain for job type: {data.JobType}"); + } + + return _builders[cycleType][data.JobType].Invoke(data, modData, sumWriter); } - /// <summary> - /// Builds a PWheel powertrain. - /// <code> - /// PWheelCycle - /// └AxleGear - /// ├(Angledrive) - /// ├(TransmissionOutputRetarder) - /// └CycleGearbox - /// ├(TransmissionInputRetarder) - /// └Clutch - /// └StopStartCombustionEngine - /// └(Aux) - /// </code> - /// </summary> - private static IVehicleContainer BuildPWheelConventional(VectoRunData data, IModalDataContainer modData, ISumData _sumWriter) + private static void VerifyCycleType(VectoRunData data, CycleType cycleType) + { + if (data.Cycle.CycleType != cycleType) { + throw new VectoException("CycleType must be {cycleType}."); + } + } + + /// <summary> + /// Builds a PWheel powertrain. + /// <code> + /// PWheelCycle + /// └AxleGear + /// ├(Angledrive) + /// ├(TransmissionOutputRetarder) + /// └CycleGearbox + /// ├(TransmissionInputRetarder) + /// └Clutch + /// └StopStartCombustionEngine + /// └(Aux) + /// </code> + /// </summary> + private static IVehicleContainer BuildPWheelConventional(VectoRunData data, IModalDataContainer modData, ISumData _sumWriter) { if (_sumWriter == null) throw new ArgumentNullException(nameof(_sumWriter)); @@ -206,7 +240,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl /// └(VTPTruckAuxiliaries or VTPBusAuxiliaries) /// </code> /// </summary> - private static IVehicleContainer BuildVTP(VectoRunData data, IModalDataContainer modData, ISumData sumWriter) + private static IVehicleContainer BuildVTPConventional(VectoRunData data, IModalDataContainer modData, ISumData sumWriter) { if (data.Cycle.CycleType != CycleType.VTP) { throw new VectoException("CycleType must be VTP."); @@ -277,26 +311,26 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl } engine.Connect(aux.Port()); - } - - /// <summary> - /// Builds a measured speed powertrain. - /// <code> - /// MeasuredSpeedDrivingCycle - /// └Vehicle - /// └Wheels - /// └Brakes - /// └AxleGear - /// ├(Angledrive) - /// ├(TransmissionOutputRetarder) - /// └Gearbox, ATGearbox, or APTNGearbox - /// ├(TransmissionInputRetarder) - /// ├(Clutch) - /// └StopStartCombustionEngine - /// └(Aux) - /// </code> - /// </summary> - private static IVehicleContainer BuildMeasuredSpeedConventional(VectoRunData data, IModalDataContainer modData, ISumData sumWriter) + } + + /// <summary> + /// Builds a measured speed powertrain. + /// <code> + /// MeasuredSpeedDrivingCycle + /// └Vehicle + /// └Wheels + /// └Brakes + /// └AxleGear + /// ├(Angledrive) + /// ├(TransmissionOutputRetarder) + /// └Gearbox, ATGearbox, or APTNGearbox + /// ├(TransmissionInputRetarder) + /// ├(Clutch) + /// └StopStartCombustionEngine + /// └(Aux) + /// </code> + /// </summary> + private static IVehicleContainer BuildMeasuredSpeedConventional(VectoRunData data, IModalDataContainer modData, ISumData sumWriter) { if (data.Cycle.CycleType != CycleType.MeasuredSpeed) { throw new VectoException("CycleType must be MeasuredSpeed."); @@ -317,26 +351,26 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl .AddComponent(engine, GetIdleController(data.PTO, engine, container)) .AddAuxiliaries(container, data); return container; - } - - /// <summary> - /// Builds a measured speed (with gear) powertrain. - /// <code> - /// MeasuredSpeedDrivingCycle - /// └Vehicle - /// └Wheels - /// └Brakes - /// └AxleGear - /// ├(Angledrive) - /// ├(TransmissionOutputRetarder) - /// └CycleGearbox - /// ├(TransmissionInputRetarder) - /// ├(Clutch) - /// └StopStartCombustionEngine - /// └(Aux) - /// </code> - /// </summary> - private static IVehicleContainer BuildMeasuredSpeedGearConventional(VectoRunData data, IModalDataContainer modData, ISumData sumWriter) + } + + /// <summary> + /// Builds a measured speed (with gear) powertrain. + /// <code> + /// MeasuredSpeedDrivingCycle + /// └Vehicle + /// └Wheels + /// └Brakes + /// └AxleGear + /// ├(Angledrive) + /// ├(TransmissionOutputRetarder) + /// └CycleGearbox + /// ├(TransmissionInputRetarder) + /// ├(Clutch) + /// └StopStartCombustionEngine + /// └(Aux) + /// </code> + /// </summary> + private static IVehicleContainer BuildMeasuredSpeedGearConventional(VectoRunData data, IModalDataContainer modData, ISumData sumWriter) { if (data.Cycle.CycleType != CycleType.MeasuredSpeedGear) { throw new VectoException("CycleType must be MeasuredSpeed with Gear."); @@ -787,8 +821,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl private static IVehicleContainer BuildPWheelBatteryElectric(VectoRunData data, IModalDataContainer modData, WriteSumData sumWriter) { - if (data.Cycle.CycleType != CycleType.PWheel) - throw new VectoException("CycleType must be DistanceBased or MeasuredSpeed"); + VerifyCycleType(data, CycleType.PWheel); ValidateBatteryElectric(data); var container = new VehicleContainer(data.ExecutionMode, modData, sumWriter) { RunData = data }; @@ -926,81 +959,118 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl private static IVehicleContainer BuildMeasuredSpeedBatteryElectric(VectoRunData data, IModalDataContainer modData, WriteSumData sumWriter) { - if (data.Cycle.CycleType != CycleType.MeasuredSpeed) - throw new VectoException("CycleType must be DistanceBased or MeasuredSpeed"); + VerifyCycleType(data, CycleType.MeasuredSpeed); ValidateBatteryElectric(data); var container = new VehicleContainer(data.ExecutionMode, modData, sumWriter) { RunData = data }; - var es = new ElectricSystem(container); - if (data.BatteryData != null) - { - var battery = new BatterySystem(container, data.BatteryData); + AddBatterySystem<Battery, BatterySystem>(data, container, es); + AddSuperCapacitor(data, container, es); + AddElectricAuxiliary(data, container, es); + + IPowerTrainComponent powertrain = new MeasuredSpeedDrivingCycle(container, data.Cycle) + .AddComponent(new Vehicle(container, data.VehicleData, data.AirdragData)) + .AddComponent(new Wheels(container, data.VehicleData.DynamicTyreRadius, data.VehicleData.WheelsInertia)) + .AddComponent(new Brakes(container)); + + var position = data.ElectricMachinesData.First().Item1; + + IElectricMotor em = _MeasuredSpeedBEVBuilders[position].Invoke(data, container, es, powertrain); + + AddBEVBusAuxiliaries(data, container, es, em); + + return container; + } + + private static IElectricMotor BuildMeasuredSpeedForBEV2(VectoRunData data, VehicleContainer container, ElectricSystem es, IPowerTrainComponent powertrain) + { + var ctl = new BatteryElectricMotorController(container, es); + + var strategy = (data.GearboxData.Type == GearboxType.APTN) ? + new APTNShiftStrategy(container) : new PEVAMTShiftStrategy(container); + + var gearbox = (data.GearboxData.Type == GearboxType.APTN) ? + (Gearbox) new APTNGearbox(container, strategy) : new PEVGearbox(container, strategy); + + IElectricMotor em = GetElectricMachine(PowertrainPosition.BatteryElectricE2, data.ElectricMachinesData, container, es, ctl); + + powertrain.AddComponent(new AxleGear(container, data.AxleGearData)) + .AddComponent(gearbox) + .AddComponent(em); + + new ATClutchInfo(container); + + return em; + } + + private static IElectricMotor BuildMeasuredSpeedForBEV3(VectoRunData data, VehicleContainer container, ElectricSystem es, IPowerTrainComponent powertrain) + { + var ctl = new BatteryElectricMotorController(container, es); + + IElectricMotor em = GetElectricMachine(PowertrainPosition.BatteryElectricE3, data.ElectricMachinesData, container, es, ctl); + + powertrain.AddComponent(new AxleGear(container, data.AxleGearData)) + .AddComponent(em); + + new DummyGearboxInfo(container); + new ATClutchInfo(container); + new DummyEngineInfo(container); + + return em; + } + + private static IElectricMotor BuildMeasuredSpeedForBEV4(VectoRunData data, VehicleContainer container, ElectricSystem es, IPowerTrainComponent powertrain) + { + var ctl = new BatteryElectricMotorController(container, es); + + IElectricMotor em = GetElectricMachine(PowertrainPosition.BatteryElectricE4, data.ElectricMachinesData, container, es, ctl); + + powertrain.AddComponent(em); + + new DummyGearboxInfo(container); + new DummyAxleGearInfo(container); + new ATClutchInfo(container); + + return em; + } + + private static void AddBatterySystem<TBattery, TBatterySystem>(VectoRunData data, VehicleContainer container, ElectricSystem es) + where TBattery : Battery where TBatterySystem : GenericBatterySystem<TBattery> + { + if (data.BatteryData != null) { + if (data.BatteryData.InitialSoC < data.BatteryData.Batteries.Min(x => x.Item2.MinSOC)) { + throw new VectoException("Battery: Initial SoC has to be higher than min SoC"); + } + + var battery = (TBatterySystem) Activator.CreateInstance(typeof(TBatterySystem), new object[] { container, data.BatteryData }); battery.Initialize(data.BatteryData.InitialSoC); es.Connect(battery); } + } + + private static void AddSuperCapacitor(VectoRunData data, VehicleContainer container, ElectricSystem es) + { + if (data.SuperCapData != null) { + if (data.SuperCapData.InitialSoC < data.SuperCapData.MinVoltage / data.SuperCapData.MaxVoltage) { + throw new VectoException("SuperCap: Initial SoC has to be higher than min SoC"); + } - if (data.SuperCapData != null) - { var superCap = new SuperCap(container, data.SuperCapData); superCap.Initialize(data.SuperCapData.InitialSoC); es.Connect(superCap); } + } - var ctl = new BatteryElectricMotorController(container, es); - + private static void AddElectricAuxiliary(VectoRunData data, VehicleContainer container, ElectricSystem es) + { var aux = new ElectricAuxiliary(container); aux.AddConstant("P_aux_el", data.ElectricAuxDemand ?? 0.SI<Watt>()); es.Connect(aux); + } - var timeBasedCycle = new MeasuredSpeedDrivingCycle(container, data.Cycle); - IPowerTrainComponent powertrain = timeBasedCycle - .AddComponent(new Vehicle(container, data.VehicleData, data.AirdragData)) - .AddComponent(new Wheels(container, data.VehicleData.DynamicTyreRadius, data.VehicleData.WheelsInertia)) - .AddComponent(new Brakes(container)); - - IElectricMotor em; - var position = data.ElectricMachinesData.First().Item1; - switch (position) - { - case PowertrainPosition.BatteryElectricE4: - em = GetElectricMachine(PowertrainPosition.BatteryElectricE4, data.ElectricMachinesData, container, es, ctl); - powertrain.AddComponent(em); - new DummyGearboxInfo(container); - new DummyAxleGearInfo(container); - new ATClutchInfo(container); - break; - case PowertrainPosition.BatteryElectricE3: - em = GetElectricMachine(PowertrainPosition.BatteryElectricE3, data.ElectricMachinesData, container, es, ctl); - powertrain.AddComponent(new AxleGear(container, data.AxleGearData)) - .AddComponent(em); - new DummyGearboxInfo(container); - new ATClutchInfo(container); - new DummyEngineInfo(container); - break; - case PowertrainPosition.BatteryElectricE2 when data.GearboxData.Type != GearboxType.APTN: - var strategy = new PEVAMTShiftStrategy(container); - em = GetElectricMachine(PowertrainPosition.BatteryElectricE2, data.ElectricMachinesData, - container, es, ctl); - powertrain.AddComponent(new AxleGear(container, data.AxleGearData)) - .AddComponent(new PEVGearbox(container, strategy)) - .AddComponent(em); - new ATClutchInfo(container); - break; - case PowertrainPosition.BatteryElectricE2 when data.GearboxData.Type == GearboxType.APTN: - var strategyAPTN = new APTNShiftStrategy(container); - em = GetElectricMachine(PowertrainPosition.BatteryElectricE2, data.ElectricMachinesData, - container, es, ctl); - powertrain.AddComponent(new AxleGear(container, data.AxleGearData)) - .AddComponent(new APTNGearbox(container, strategyAPTN)) - .AddComponent(em); - new ATClutchInfo(container); - break; - - default: throw new ArgumentOutOfRangeException(nameof(position), position, null); - } - + private static void AddBEVBusAuxiliaries(VectoRunData data, VehicleContainer container, ElectricSystem es, IElectricMotor em) + { if (data.BusAuxiliaries != null) { if (!data.BusAuxiliaries.ElectricalUserInputsConfig.ConnectESToREESS) @@ -1021,14 +1091,12 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl es.Connect(dcdc); em.BusAux = busAux; } + } - return container; - } - private static IVehicleContainer BuildMeasuredSpeedGearBatteryElectric(VectoRunData data, IModalDataContainer modData, WriteSumData sumWriter) + private static IVehicleContainer BuildMeasuredSpeedGearBatteryElectric(VectoRunData data, IModalDataContainer modData, WriteSumData sumWriter) { - if (data.Cycle.CycleType != CycleType.MeasuredSpeedGear) - throw new VectoException("CycleType must be DistanceBased or MeasuredSpeed"); + VerifyCycleType(data, CycleType.MeasuredSpeedGear); ValidateBatteryElectric(data); var container = new VehicleContainer(data.ExecutionMode, modData, sumWriter) { RunData = data }; diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs index 1661ed67384c8576c9aafcb10725b0f2ce608296..6f78385b7b913e4e1d50cf719a710ec4f93545dc 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Battery.cs @@ -34,7 +34,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #region Implementation of IElectricEnergyStoragePort - public void Initialize(double initialSoC) + public virtual void Initialize(double initialSoC) { CurrentState.PulseDuration = 0.SI<Second>(); PreviousState.PulseDuration = 0.SI<Second>(); @@ -47,7 +47,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl PreviousState.StateOfCharge = initialSoC; } - public IRESSResponse Request(Second absTime, Second dt, Watt powerDemand, bool dryRun = false) + public virtual IRESSResponse Request(Second absTime, Second dt, Watt powerDemand, bool dryRun = false) { var tPulse = PreviousState.PowerDemand.Sign() == powerDemand.Sign() ? PreviousState.PulseDuration @@ -106,7 +106,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl }; } - private Ampere SelectSolution(double[] solutions, double sign) + protected Ampere SelectSolution(double[] solutions, double sign) { var maxCurrent = Math.Sign(sign) < 0 ? ModelData.MaxCurrent.LookupMaxDischargeCurrent(PreviousState.StateOfCharge) @@ -114,7 +114,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return solutions.Where(x => Math.Sign(sign) == Math.Sign(x) && Math.Abs(x).IsSmallerOrEqual(Math.Abs(maxCurrent.Value()), 1e-3)).Min().SI<Ampere>(); } - private IRESSResponse PowerDemandExceeded(Second absTime, Second dt, Watt powerDemand, Watt maxDischargePower, + protected IRESSResponse PowerDemandExceeded(Second absTime, Second dt, Watt powerDemand, Watt maxDischargePower, Watt maxChargePower, Second tPulse, bool dryRun) { var maxPower = powerDemand < 0 ? maxDischargePower : maxChargePower; @@ -192,6 +192,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public double StateOfCharge => PreviousState.StateOfCharge; + public double CalculatedStateOfCharge => PreviousState.CalculatedStateOfCharge; + public WattSecond StoredEnergy => PreviousState.StateOfCharge * ModelData.Capacity * ModelData.SOCMap.Lookup(PreviousState.StateOfCharge); public Watt MaxChargePower(Second dt) @@ -260,6 +262,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public class State { public double StateOfCharge; + public double CalculatedStateOfCharge; public Second SimulationInterval; diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs index abd479fe01b31f9df6a95ecacbed5af098328836..6cf7885eca8976b1329ba53397829c44acd78f94 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs @@ -14,11 +14,12 @@ using TUGraz.VectoCore.OutputData; namespace TUGraz.VectoCore.Models.SimulationComponent.Impl { - public class BatterySystem : StatefulVectoSimulationComponent<BatterySystem.State>, IElectricEnergyStorage, IElectricEnergyStoragePort, IUpdateable + public class GenericBatterySystem<TBattery> : StatefulVectoSimulationComponent<GenericBatterySystem<TBattery>.State>, + IElectricEnergyStorage, IElectricEnergyStoragePort, IUpdateable where TBattery : Battery { public class BatteryString: IUpdateable { - protected readonly List<Battery> _batteries; + protected readonly List<TBattery> _batteries; private AmpereSecond _capacity; private AmpereSecond _capacityMinSoc; @@ -26,12 +27,12 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public BatteryString() { - _batteries = new List<Battery>(); + _batteries = new List<TBattery>(); } - public IReadOnlyList<Battery> Batteries => _batteries; + public IReadOnlyList<TBattery> Batteries => _batteries; - public void AddBattery(Battery bat) + public void AddBattery(TBattery bat) { _batteries.Add(bat); // Todo: update some properties? @@ -45,6 +46,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public AmpereSecond Capacity => _capacity ?? (_capacity = _batteries.Min(x => x.Capacity)); public double SoC => _batteries.Min(x => x.StateOfCharge * x.Capacity) / Capacity; + + public double CalculatedStateOfCharge => _batteries.Min(x => x.CalculatedStateOfCharge * x.Capacity) / Capacity; + public WattSecond StoredEnergy => _batteries.Min(x => x.StateOfCharge * x.Capacity) * OpenCircuitVoltage; public Watt MaxDischargePower(Second dt, Second tPulse) @@ -130,10 +134,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl private Scalar _minSoc; private Scalar _maxSoc; - public BatterySystem(IVehicleContainer dataBus, BatterySystemData batterySystemData) : base(dataBus) + public GenericBatterySystem(IVehicleContainer dataBus, BatterySystemData batterySystemData) : base(dataBus) { foreach (var entry in batterySystemData.Batteries) { - var bat = new Battery(null, entry.Item2); + var bat = (TBattery) Activator.CreateInstance(typeof(TBattery), new object[] { null, entry.Item2 }); if (!Batteries.ContainsKey(entry.Item1)) { Batteries[entry.Item1] = new BatteryString(); } @@ -394,7 +398,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #region Implementation of IUpdateable public bool UpdateFrom(object other) { - if (other is BatterySystem b) { + if (other is GenericBatterySystem<TBattery> b) { PreviousState = b.PreviousState.Clone(); return Batteries.All(kv => kv.Value.UpdateFrom(b.Batteries[kv.Key])); } @@ -403,5 +407,31 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } #endregion + } + + public class BatterySystem : GenericBatterySystem<Battery> + { + public BatterySystem(IVehicleContainer dataBus, BatterySystemData batterySystemData) : base(dataBus, batterySystemData) + { + } + } + + public class IdealBatterySystem : GenericBatterySystem<IdealBattery> + { + public IdealBatterySystem(IVehicleContainer dataBus, BatterySystemData batterySystemData) : base(dataBus, batterySystemData) + { + } + + public override void CommitSimulationStep(Second time, Second simulationInterval, IModalDataContainer container) + { + base.CommitSimulationStep(time, simulationInterval, container); + + if (container != null) { + container[ModalResultField.REESSStateOfCharge] = CalculatedStateOfCharge.SI(); + } + } + + public double CalculatedStateOfCharge => Batteries.Values.Sum(bs => bs.CalculatedStateOfCharge * bs.Capacity) / TotalCapacity; } + } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/IdealBattery.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/IdealBattery.cs new file mode 100644 index 0000000000000000000000000000000000000000..50df41e03166c6f4bc9eeada0cafadf1665d47e3 --- /dev/null +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/IdealBattery.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using TUGraz.VectoCore.Models.Simulation; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + public class IdealBattery : Battery + { + public const double IDEAL_STATE_OF_CHARGE = 0.5; + + public IdealBattery(IVehicleContainer container, BatteryData modelData, int idx = -1) : + base(container, modelData, idx) + { + } + + public override void Initialize(double initialSoC) + { + base.Initialize(initialSoC); + + PreviousState.CalculatedStateOfCharge = initialSoC; + PreviousState.StateOfCharge = IDEAL_STATE_OF_CHARGE; + } + + public override IRESSResponse Request(Second absTime, Second dt, Watt powerDemand, bool dryRun = false) + { + var tPulse = PreviousState.PowerDemand.Sign() == powerDemand.Sign() + ? PreviousState.PulseDuration + : 0.SI<Second>(); + + var maxChargePower = MaxChargePower(dt); + var maxDischargePower = MaxDischargePower(dt); + + if (powerDemand.IsGreater(maxChargePower, Constants.SimulationSettings.InterpolateSearchTolerance) || + powerDemand.IsSmaller(maxDischargePower, Constants.SimulationSettings.InterpolateSearchTolerance)) + { + return PowerDemandExceeded(absTime, dt, powerDemand, maxDischargePower, maxChargePower, tPulse, dryRun); + } + + var batteryState = ComputeInternalResistanceAndCurrent(PreviousState.StateOfCharge, powerDemand, tPulse); + var batteryLoss = batteryState.current * batteryState.internalResistance * batteryState.current; + + if (dryRun) { + return new RESSDryRunResponse(this) { + AbsTime = absTime, + SimulationInterval = dt, + MaxChargePower = maxChargePower, + MaxDischargePower = maxDischargePower, + PowerDemand = powerDemand, + LossPower = batteryLoss, + StateOfCharge = IDEAL_STATE_OF_CHARGE + }; + } + + var calcBatteryState = ComputeInternalResistanceAndCurrent(PreviousState.CalculatedStateOfCharge, powerDemand, tPulse); + var calcStateOfCharge = CalculateStateOfCharge(ModelData.Capacity * PreviousState.CalculatedStateOfCharge, + calcBatteryState.current, dt); + + CurrentState.SimulationInterval = dt; + CurrentState.PowerDemand = powerDemand; + CurrentState.TotalCurrent = batteryState.current; + CurrentState.BatteryLoss = batteryLoss; + CurrentState.StateOfCharge = IDEAL_STATE_OF_CHARGE; + CurrentState.CalculatedStateOfCharge = calcStateOfCharge; + CurrentState.MaxChargePower = maxChargePower; + CurrentState.MaxDischargePower = maxDischargePower; + + return new RESSResponseSuccess(this) { + AbsTime = absTime, + SimulationInterval = dt, + MaxChargePower = maxChargePower, + MaxDischargePower = maxDischargePower, + PowerDemand = powerDemand, + LossPower = batteryLoss, + StateOfCharge = IDEAL_STATE_OF_CHARGE + }; + } + + protected (Ohm internalResistance, Ampere current) ComputeInternalResistanceAndCurrent(double stateOfCharge, Watt powerDemand, + Second tPulse) + { + var internalResistance = ModelData.InternalResistance.Lookup(stateOfCharge, tPulse); + + var current = powerDemand.IsEqual(0) + ? 0.SI<Ampere>() + : SelectSolution( + VectoMath.QuadraticEquationSolver(internalResistance.Value(), InternalVoltage.Value(),-powerDemand.Value()), + powerDemand.Value()); + + return (internalResistance: internalResistance, current: current); + } + + protected double CalculateStateOfCharge(AmpereSecond currentCharge, Ampere current, Second dt) + { + return (currentCharge + current * dt) / ModelData.Capacity; + } + + protected override void DoWriteModalResults(Second absTime, Second dt, IModalDataContainer container) + { + base.DoWriteModalResults(absTime, dt, container); + container[ModalResultField.REESSStateOfCharge, BatteryId] = CurrentState.CalculatedStateOfCharge.SI(); + } + } +}