From 80b42b80ffd795468347632cd5d1b07432d41bb4 Mon Sep 17 00:00:00 2001 From: Markus Quaritsch <markus.quaritsch@tugraz.at> Date: Tue, 13 Jul 2021 12:56:04 +0200 Subject: [PATCH] saving and loading battery system works, write modal results for batterysystem (total/average of all batteries / battery strings) --- VECTO/GUI/VehicleForm.vb | 2 +- VECTO/Input Files/Vehicle.vb | 7 ++- .../InputData/FileIO/JSON/JSONVehicleData.cs | 17 ++++-- .../Data/Battery/BatteryData.cs | 9 ++- .../SimulationComponent/Impl/BatterySystem.cs | 58 +++++++++++++++++-- .../Strategies/HybridStrategy.cs | 22 +++---- .../OutputData/FileIO/JSONFileWriter.cs | 4 +- .../OutputData/ModalDataContainer.cs | 33 ++++++++--- 8 files changed, 116 insertions(+), 36 deletions(-) diff --git a/VECTO/GUI/VehicleForm.vb b/VECTO/GUI/VehicleForm.vb index 031978f731..17faa44e86 100644 --- a/VECTO/GUI/VehicleForm.vb +++ b/VECTO/GUI/VehicleForm.vb @@ -481,7 +481,7 @@ Public Class VehicleForm If(angledrive.LossMap Is Nothing, "", GetRelativePath(angledrive.LossMap.Source, basePath)) If (vehicle.VehicleType = VectoSimulationJobType.BatteryElectricVehicle OrElse vehicle.VehicleType = VectoSimulationJobType.ParallelHybridVehicle) Then - lvREESSPacks.Clear() + lvREESSPacks.Items.Clear() For Each entry As IElectricStorageEngineeringInputData In vehicle.Components.ElectricStorage.ElectricStorageElements.OrderBy(function(x) x.StringId) lvREESSPacks.Items.Add(CreateREESSPackListViewItem(GetRelativePath(entry.REESSPack.DataSource.SourceFile, basePath), entry.Count, entry.StringId)) Next diff --git a/VECTO/Input Files/Vehicle.vb b/VECTO/Input Files/Vehicle.vb index 75d79ec6d4..4bbc2859e6 100644 --- a/VECTO/Input Files/Vehicle.vb +++ b/VECTO/Input Files/Vehicle.vb @@ -106,7 +106,8 @@ Public Class Vehicle Axles = New List(Of AxleInputData) torqueLimitsList = New List(Of ITorqueLimitInputData) - PtoLossMap = New SubPath() + ReessPacks = new List(Of Tuple(Of String,Integer,Integer)) + PtoLossMap = New SubPath() PtoCycleStandstill = New SubPath() PtoCycleDriving = new SubPath() ElectricMotorFile = New SubPath() @@ -928,12 +929,12 @@ Public Class ElectricStorageSystemWrapper Public ReadOnly Property ElectricStorageElements As IList(Of IElectricStorageDeclarationInputData) Implements IElectricStorageSystemDeclarationInputData.ElectricStorageElements get - _vehicle.ReessPacks.Select(Function(x) new ElectricStorageWrapper(x, _vehicle.FilePath)).toList() + return _vehicle.ReessPacks.Select(Function(x) new ElectricStorageWrapper(x, GetPath(_vehicle.FilePath))).Cast(of IElectricStorageDeclarationInputData) .toList() End Get End Property Public ReadOnly Property IElectricStorageSystemEngineeringInputData_ElectricStorageElements As IList(Of IElectricStorageEngineeringInputData) Implements IElectricStorageSystemEngineeringInputData.ElectricStorageElements get - + return _vehicle.ReessPacks.Select(Function(x) new ElectricStorageWrapper(x, GetPath(_vehicle.FilePath))).cast(of IElectricStorageEngineeringInputData).toList() End Get End Property End Class diff --git a/VectoCore/VectoCore/InputData/FileIO/JSON/JSONVehicleData.cs b/VectoCore/VectoCore/InputData/FileIO/JSON/JSONVehicleData.cs index c27faf5e8b..e99c766fe2 100644 --- a/VectoCore/VectoCore/InputData/FileIO/JSON/JSONVehicleData.cs +++ b/VectoCore/VectoCore/InputData/FileIO/JSON/JSONVehicleData.cs @@ -125,11 +125,20 @@ namespace TUGraz.VectoCore.InputData.FileIO.JSON protected virtual JSONElectricStorageSystemEngineeringInputData ReadBatteries() { var entries = new List<IElectricStorageEngineeringInputData>(); - foreach (var entry in Body["Batteries"]) { + if (Body["Batteries"] != null) { + foreach (var entry in Body["Batteries"]) { + entries.Add(new JSONElectricStorageEngineeringInputData() { + Count = entry.GetEx<int>("NumPacks"), + StringId = entry.GetEx<int>("StreamId"), + REESSPack = JSONInputDataFactory.ReadREESSData( + Path.Combine(BasePath, entry.GetEx<string>("BatteryFile")), false) + }); + } + } else { entries.Add(new JSONElectricStorageEngineeringInputData() { - Count = entry.GetEx<int>("NumPacks"), - StringId = entry.GetEx<int>("StreamId"), - REESSPack = JSONInputDataFactory.ReadREESSData(Path.Combine(BasePath, entry["Battery"].GetEx<string>("BatteryFile")), false) + Count = Body["Battery"].GetEx<int>("NumPacks"), + StringId = 0, + REESSPack = JSONInputDataFactory.ReadREESSData(Path.Combine(BasePath, Body["Battery"].GetEx<string>("BatteryFile")), false) }); } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Battery/BatteryData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Battery/BatteryData.cs index a52cae4734..52ba4b9dbf 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Battery/BatteryData.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Battery/BatteryData.cs @@ -17,8 +17,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Battery { public List<Tuple<int, BatteryData>> Batteries { get; internal set; } public double InitialSoC { get; internal set; } - public AmpereSecond Capacity { - get { throw new NotImplementedException();} + public AmpereSecond Capacity + { + get + { + return Batteries.Select(x => x.Item1).Distinct().OrderBy(x => x).Aggregate(0.SI<AmpereSecond>(), + (current, s) => current + Batteries.Where(x => x.Item1 == s).Min(x => x.Item2.Capacity)); + } } } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs index baacffa2c2..f915d7754a 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/BatterySystem.cs @@ -8,12 +8,13 @@ using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Models.Connector.Ports.Impl; using TUGraz.VectoCore.Models.Simulation; +using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.SimulationComponent.Data.Battery; using TUGraz.VectoCore.OutputData; namespace TUGraz.VectoCore.Models.SimulationComponent.Impl { - public class BatterySystem : VectoSimulationComponent, IElectricEnergyStorage, IElectricEnergyStoragePort + public class BatterySystem : StatefulVectoSimulationComponent<BatterySystem.State>, IElectricEnergyStorage, IElectricEnergyStoragePort { public class BatteryString { @@ -42,6 +43,7 @@ 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 WattSecond StoredEnergy => _batteries.Min(x => x.StateOfCharge * x.Capacity) * OpenCircuitVoltage; public Watt MaxDischargePower(Second dt) { @@ -79,12 +81,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl current = SelectSolution(solutions, powerDemand.Value(), dt); } + if (!dryRun) { + Current = current; + } + return _batteries.Select(x => { var demand = (x.InternalVoltage + x.InternalResistance * current) * current; return x.Request(absTime, dt, demand, dryRun); }).ToList(); } + public Ampere Current { get; set; } + private Ampere SelectSolution(double[] solutions, double sign, Second dt) { var maxCurrent = Math.Sign(sign) < 0 @@ -114,19 +122,34 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public override void CommitSimulationStep(Second time, Second simulationInterval, IModalDataContainer container) { + base.CommitSimulationStep(time, simulationInterval, container); foreach (var battery in Batteries) { foreach (var b in battery.Value.Batteries) { b.CommitSimulationStep(time, simulationInterval, container); } } - base.CommitSimulationStep(time, simulationInterval, container); + } protected override void DoWriteModalResults(Second time, Second simulationInterval, IModalDataContainer container) { - + var cellVoltage = InternalVoltage; + container[ModalResultField.U0_reess] = cellVoltage; + container[ModalResultField.U_reess_terminal] = + cellVoltage + + CurrentState.TotalCurrent * + InternalResistance; // adding both terms because pos. current charges the battery! + container[ModalResultField.I_reess] = CurrentState.TotalCurrent; + container[ModalResultField.REESSStateOfCharge] = CurrentState.StateOfCharge.SI(); + container[ModalResultField.P_reess_terminal] = CurrentState.PowerDemand; + container[ModalResultField.P_reess_int] = cellVoltage * CurrentState.TotalCurrent; + container[ModalResultField.P_reess_loss] = CurrentState.BatteryLoss; + container[ModalResultField.P_reess_charge_max] = CurrentState.MaxChargePower; + container[ModalResultField.P_reess_discharge_max] = CurrentState.MaxDischargePower; } + public Ohm InternalResistance => (1 / Batteries.Sum(bs => 1 / bs.Value.InternalResistance.Value())).SI<Ohm>(); + protected override void DoCommitSimulationStep(Second time, Second simulationInterval) { @@ -150,7 +173,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public AmpereSecond TotalCapacity => _totalCapacity ?? (_totalCapacity = Batteries.Values.Sum(bs => bs.Capacity)); - public WattSecond StoredEnergy { get; } + public WattSecond StoredEnergy => Batteries.Values.Sum(bs => bs.StoredEnergy); public Watt MaxChargePower(Second dt) { @@ -208,6 +231,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl distributedPower += power; } } + + if (limitBB.Count > 0) { + Log.Debug($"BB ${string.Join(", ", limitBB.Keys)} are at max - recalculating power distribution"); + } } while (!distributedPower.IsEqual(powerDemand)); powerDemands = powerDemands.Concat(limitBB).ToDictionary(x => x.Key, x=> x.Value); @@ -229,6 +256,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl }; } + var current = Batteries.Values.Sum(x => x.Current); + + CurrentState.StateOfCharge = StateOfCharge; + CurrentState.PowerDemand = powerDemand; + CurrentState.MaxChargePower = maxChargePower; + CurrentState.MaxDischargePower = maxDischargePower; + CurrentState.TotalCurrent = current; + CurrentState.BatteryLoss = current * InternalResistance * current; + if (responses.All(bb => bb.Value.All(b => b is RESSResponseSuccess))) { return new RESSResponseSuccess(this) { AbsTime = absTime, @@ -277,5 +313,19 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } #endregion + + public class State + { + public double StateOfCharge; + + public Second SimulationInterval; + + public Watt PowerDemand; + + public Ampere TotalCurrent; + public Watt MaxChargePower; + public Watt MaxDischargePower; + public Watt BatteryLoss; + } } } \ 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 523aeb4367..5dd8b70961 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Strategies/HybridStrategy.cs @@ -557,17 +557,17 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Strategies shiftStrategyParameters.AllowedGearRangeFC = shiftStrategyParameters.AllowedGearRangeFC.LimitTo(1, 2); } - // TODO: MQ 20210712 how to handle with batterysystem - //var auxEnergyReserve = ModelData.ElectricAuxDemand * StrategyParameters.AuxReserveTime; - //BatteryDischargeEnergyThreshold = 0.SI<WattSecond>(); - //if (auxEnergyReserve > 0) { - // var minSoc = Math.Max(ModelData.BatteryData?.MinSOC ?? ModelData.SuperCapData.MinVoltage / ModelData.SuperCapData.MaxVoltage, - // StrategyParameters.MinSoC); - // BatteryDischargeEnergyThreshold = - // ModelData.BatteryData.Capacity * minSoc * ModelData.BatteryData.SOCMap.Lookup(minSoc) + - // auxEnergyReserve; - //} - AllowEmergencyShift = false; + // TODO: MQ 20210712 how to handle with batterysystem + //var auxEnergyReserve = ModelData.ElectricAuxDemand * StrategyParameters.AuxReserveTime; + BatteryDischargeEnergyThreshold = 0.SI<WattSecond>(); + //if (auxEnergyReserve > 0) { + // var minSoc = Math.Max(ModelData.BatteryData?.MinSOC ?? ModelData.SuperCapData.MinVoltage / ModelData.SuperCapData.MaxVoltage, + // StrategyParameters.MinSoC); + // BatteryDischargeEnergyThreshold = + // ModelData.BatteryData.Capacity * minSoc * ModelData.BatteryData.SOCMap.Lookup(minSoc) + + // auxEnergyReserve; + //} + AllowEmergencyShift = false; } public virtual IHybridController Controller { protected get; set; } diff --git a/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs b/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs index eb8f9ee587..0eb4d17f6f 100644 --- a/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs +++ b/VectoCore/VectoCore/OutputData/FileIO/JSONFileWriter.cs @@ -520,7 +520,7 @@ public class JSONFileWriter : IOutputFileWriter body.Add("InitialSoC", vehicle.InitialSOC * 100); body.Add("PowertrainConfiguration", "ParallelHybrid"); body.Add("ElectricMotors", electricMotorsOut); - body.Add("Battery", battery); + body.Add("Batteries", battery); WriteFile(header, body, filename); } @@ -551,7 +551,7 @@ public class JSONFileWriter : IOutputFileWriter body.Add("InitialSoC", vehicle.InitialSOC * 100); body.Add("PowertrainConfiguration", "BatteryElectric"); body.Add("ElectricMotors", electricMotorsOut); - body.Add("Battery", battery); + body.Add("Batteries", battery); //body.Add("IdlingSpeed", vehicle.EngineIdleSpeed.AsRPM); //body.Add("Retarder", retarderOut); diff --git a/VectoCore/VectoCore/OutputData/ModalDataContainer.cs b/VectoCore/VectoCore/OutputData/ModalDataContainer.cs index 456c4a4381..45c2cd88bb 100644 --- a/VectoCore/VectoCore/OutputData/ModalDataContainer.cs +++ b/VectoCore/VectoCore/OutputData/ModalDataContainer.cs @@ -93,7 +93,7 @@ namespace TUGraz.VectoCore.OutputData ModalResultField.EM_Off_, }; - private readonly ModalResultField[] _batteryColumns = new[] { + private readonly ModalResultField[] _batterySignals = new[] { ModalResultField.U0_reess, ModalResultField.U_reess_terminal, ModalResultField.I_reess, @@ -105,6 +105,9 @@ namespace TUGraz.VectoCore.OutputData ModalResultField.P_reess_discharge_max }; + protected Dictionary<int, Dictionary<ModalResultField, DataColumn>> BatteryColumns = + new Dictionary<int, Dictionary<ModalResultField, DataColumn>>(); + public static readonly IList<ModalResultField> FuelConsumptionSignals = new[] { ModalResultField.FCMap, ModalResultField.FCNCVc, ModalResultField.FCWHTCc, // ModalResultField.FCAAUX, ModalResultField.FCICEStopStart, ModalResultField.FCFinal @@ -883,7 +886,7 @@ namespace TUGraz.VectoCore.OutputData public object this[ModalResultField key, int? idx] { get { - if (!_batteryColumns.Contains(key)) { + if (!_batterySignals.Contains(key)) { throw new VectoException("ModalResult with index is only supported for REESS fields"); } @@ -891,14 +894,26 @@ namespace TUGraz.VectoCore.OutputData } set { - if (!_batteryColumns.Contains(key)) { - throw new VectoException("ModalResult with index is only supported for REESS fields"); - } - - if (idx.HasValue) { - CurrentRow[$"{key.GetCaption()}_{idx.Value}"] = value; - } else { + if (idx == null) { CurrentRow[key.GetName()] = value; + } else { + if (!_batterySignals.Contains(key)) { + throw new VectoException("ModalResult with index is only supported for REESS fields"); + } + if (!BatteryColumns.ContainsKey(idx.Value)) { + BatteryColumns[idx.Value] = new Dictionary<ModalResultField, DataColumn>(); + } + + var entry = BatteryColumns[idx.Value]; + if (!entry.ContainsKey(key)) { + var col = Data.Columns.Add($"{key.GetName()}_{idx.Value}", typeof(SI)); + col.ExtendedProperties[ModalResults.ExtendedPropertyNames.Decimals] = key.GetAttribute().Decimals; + col.ExtendedProperties[ModalResults.ExtendedPropertyNames.OutputFactor] = key.GetAttribute().OutputFactor; + col.ExtendedProperties[ModalResults.ExtendedPropertyNames.ShowUnit] = key.GetAttribute().ShowUnit; + entry[key] = col; + } + + CurrentRow[entry[key]] = value; } } } -- GitLab