Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit 22dd4a92 authored by Harald Martini's avatar Harald Martini
Browse files

updated Fuel Cell Preprocessing to better batteries with a low charging power

parent 4b7bec21
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,7 @@ 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.ElectricComponents;
using TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricComponents.Battery;
using TUGraz.VectoCore.Models.SimulationComponent.Impl;
using TUGraz.VectoCore.OutputData;
......@@ -20,6 +21,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent
public IList<IElectricChargerPort> Charger { get; }
public IFuelCellPort FuelCell { get; private set; } = null;
protected IElectricEnergyStorage Battery;
private readonly BatterySystemData ModelData;
......@@ -32,15 +35,25 @@ namespace TUGraz.VectoCore.Models.SimulationComponent
public IElectricSystemResponse Request(Second absTime, Second dt, Watt powerDemand, bool dryRun = false)
{
powerDemand = powerDemand ?? 0.SI<Watt>();
var propellingDemand = VectoMath.Min(powerDemand, 0.SI<Watt>()); //Propelling demand is negative
var maxFcPower = VectoMath.Max(Battery.MaxChargePower(dt) - propellingDemand, 0.SI<Watt>());
var auxDemand = Consumers.Sum(x => x.PowerDemand(absTime, dt, dryRun)).DefaultIfNull(0);
var chargePower = Charger.Count == 0 ? 0.SI<Watt>() : Charger.Sum(x => x.PowerDemand(absTime, dt, powerDemand, auxDemand, dryRun));
var currentEst = powerDemand / Battery.InternalVoltage;
var fcPower = FuelCell?.PowerDemand(absTime, dt, maxFcPower, dryRun) ?? 0.SI<Watt>();
//How to losses when fuel cell is directly contributing to power demand
var currentEst = (powerDemand + fcPower) / Battery.InternalVoltage;
var connectorLoss = currentEst * (ModelData?.ConnectionSystemResistance ?? 0.SI<Ohm>() ) * currentEst;
var totalPowerDemand = powerDemand + chargePower - auxDemand - connectorLoss;
var totalPowerDemand = powerDemand + chargePower + fcPower - auxDemand - connectorLoss;
var batResponse = Battery.MainBatteryPort.Request(absTime, dt, totalPowerDemand, dryRun);
var response = dryRun
? (AbstractElectricSystemResponse)new ElectricSystemDryRunResponse(this)
: new ElectricSystemResponseSuccess(this);
......@@ -66,7 +79,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent
response.ConnectionSystemResistance = ModelData?.ConnectionSystemResistance ?? 0.SI<Ohm>();
response.ConsumerPower = powerDemand;
response.AuxPower = auxDemand;
response.ChargingPower = chargePower;
response.ChargingPower = chargePower + fcPower;
return response;
}
......@@ -98,6 +111,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent
#endregion
public void Connect(IFuelCellPort fuelCell)
{
if (FuelCell != null) {
throw new VectoException("Fuel cell is already connected to ES");
}
FuelCell = fuelCell;
}
#region Implementation of IBatteryAuxOutProvider
public void Connect(IElectricAuxPort aux)
......@@ -129,15 +154,22 @@ namespace TUGraz.VectoCore.Models.SimulationComponent
public Watt MaxChargePower(Second dt)
{
return Battery.MaxChargePower(dt);
var batMaxPower = Battery.MaxChargePower(dt);
var currentEst = batMaxPower / Battery.InternalVoltage;
var connectorLoss = currentEst * currentEst * (ModelData?.ConnectionSystemResistance ?? 0.SI<Ohm>());
return batMaxPower + connectorLoss;
}
public Watt MaxDischargePower(Second dt)
{
return Battery.MaxDischargePower(dt);
var batMaxPower = Battery.MaxDischargePower(dt);
var currentEst = batMaxPower / Battery.InternalVoltage;
var connectorLoss = currentEst * currentEst * (ModelData?.ConnectionSystemResistance ?? 0.SI<Ohm>());
return batMaxPower + connectorLoss;
}
#endregion
public class State
......
......@@ -310,11 +310,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
var electricSupplyResponse =
ElectricPower.Request(absTime, dt, electricPower, dryRun);
if (!dryRun && !DataBus.IsTestPowertrain && !emOff && !(electricSupplyResponse is ElectricSystemResponseSuccess))
{
if (!dryRun && electricSupplyResponse is ElectricSystemOverloadResponse &&
electricPower.IsEqual(0.SI<Watt>())) {
//We have a problem here! We cannot further reduce the power demand of the em.
//There is a overload in the ES even if we are not demanding any power.
}
}
if (NextComponent != null && !dryRun && !DataBus.IsTestPowertrain && !emOff && !(electricSupplyResponse is ElectricSystemResponseSuccess)) {
if ( !avgEmSpeed.IsEqual(DataBus.HybridControllerInfo.ElectricMotorSpeed(Position) / ModelData.RatioADC)) {
......
......@@ -12,7 +12,7 @@ using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
{
public class FuelCellSystem : StatefulVectoSimulationComponent<FuelCellSystem.State>, IElectricChargerPort
public class FuelCellSystem : StatefulVectoSimulationComponent<FuelCellSystem.State>, IFuelCellPort
{
private readonly IList<FuelCellString> _fuelCellStrings;
......@@ -49,18 +49,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
return power;
}
public Watt PowerDemand(Second absTime, Second dt, Watt powerDemandEletricMotor, Watt auxPower, bool dryRun)
public Watt PowerDemand(Second absTime, Second dt, Watt maxPower, bool dryRun)
{
var targetPower = ModelData.ChargingPower(_mileageCounter.Distance);
var targetPower = ModelData.ChargingPower(_mileageCounter.Distance).LimitTo(0.SI<Watt>(), maxPower);
var shareResult = _fuelCellShareMap.Lookup(targetPower, PreviousState?.Share);
var generatedPower = 0.SI<Watt>();
generatedPower += _fuelCellStrings[0].Request(targetPower * shareResult.Share.ShareA, dryRun, dt);
if (_fuelCellStrings.Count > 1) {
generatedPower += _fuelCellStrings[1].Request(targetPower * shareResult.Share.ShareB, dryRun, dt);
}
......@@ -69,6 +67,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
CurrentState.Share = shareResult.Share;
CurrentState.Power = generatedPower;
}
return generatedPower;
}
......
using System;
using Ninject.Planning.Bindings.Resolvers;
using TUGraz.VectoCommon.Utils;
namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
......@@ -7,9 +8,12 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
{
public class FCCalcEntry
{
public FCCalcEntry(Watt p_max_charging, Watt p_max_discharge, PreRunEntry preRunEntry)
private readonly Watt _p_max_cfcs = 0.SI<Watt>();
public FCCalcEntry(Watt p_max_charging, Watt p_max_discharge, PreRunEntry preRunEntry, Watt p_max_cfcs)
{
this.P_max_charging = p_max_charging;
this.P_max_charging = p_max_charging * 0.9;
this._p_max_cfcs = p_max_cfcs;
this.P_max_discharge = p_max_discharge;
this.preRunEntry = preRunEntry;
}
......@@ -42,14 +46,36 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
public Watt P_el_dem_corr { get; set; }
/// <summary>
/// Fuel Cell power without any corrections
/// Max power that can be provided by the fuel cell at a given time wrt. to battery charging power, max fuel cell power and powerdemand of the powertrain
/// </summary>
public Watt P_FC_raw { get; set; }
public Watt P_max_fc
{
get
{
var p_prop = VectoMath.Min(P_el_dem, 0.SI<Watt>()); //Just propelling no recuperation // p_prop < 0!
var remainder = P_max_charging - p_prop; //Maximum allowed power from the fuel cell
var p_max = VectoMath.Min(this._p_max_cfcs, remainder); //limited by the max fuel cell power
return p_max;
}
}
/// <summary>
/// Maximum power of the composite fuel cell system
/// </summary>
public Watt P_max_CFCS
{
get
{
return _p_max_cfcs;
}
}
/// <summary>
/// Fuel Cell power calculated based on <see cref="P_el_dem_corr"/>
/// Fuel Cell power without any corrections
/// </summary>
public Watt P_FC_raw_corr { get; set; }
public Watt P_FC_raw { get; set; }
/// <summary>
/// Fuel Cell power limited to Min/Max power, zero => fuel cell is off
......@@ -59,7 +85,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
/// <summary>
/// Remaining power that should be provided by battery
/// </summary>
public Watt P_Bat_T => P_FC + P_el_dem.LimitTo(P_max_discharge, P_max_charging);
public Watt P_Bat_T => (P_FC + P_el_dem).LimitTo(P_max_discharge, P_max_charging);
/// <summary>
/// Battery losses from P_Bat_T
......@@ -86,9 +112,15 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
/// </summary>
public double Real_SoC { get; set; } = -1;
public bool CanChangeFCPower { get; set; }
public bool CanChangeFCPower => P_FC_corr.IsSmallerOrEqual(P_max_fc);
public Watt FCPowerFinal
{
get => P_FC_corr + delta_P_FCS;
}
public Watt FCPowerFinal { get; set; }
/// <summary>
/// Resulting power provided by battery considering <see cref="P_Bat_T_Final"/>
......@@ -123,7 +155,8 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
"SoC_real [%], " +
"CanChangeFCPower, " +
"FCPowerFinal [kW]," +
"delta_P_FCS [kW],";
"delta_P_FCS [kW]," +
"P_max_charge_bat [kW],";
#region Overrides of Object
public override string ToString()
{
......@@ -143,7 +176,8 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
$"{(Real_SoC * 100).ToXMLFormat() ?? "-"}, " +
(CanChangeFCPower ? "1" : "0") + "," +
$"{FCPowerFinal?.ConvertToKiloWatt()?.ToXMLFormat() ?? "-"}, " +
$"{delta_P_FCS?.ConvertToKiloWatt()?.ToXMLFormat() ?? "-"},";
$"{delta_P_FCS?.ConvertToKiloWatt()?.ToXMLFormat() ?? "-"}," +
$"{P_max_charging?.ConvertToKiloWatt()?.ToXMLFormat() ?? "-"},";
}
#endregion
/// <summary>
......@@ -173,6 +207,8 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
public Second t { get; set; }
public Watt P_el_aux { get; set; }
public PreRunEntry()
{
......@@ -184,6 +220,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
this.s = s.s;
this.t = s.t;
this.P_es_T = s.P_es_T;
this.P_es_T = s.P_el_aux;
}
}
}
......
......@@ -118,9 +118,11 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var distCol = data?.Columns[ModalResultField.dist.GetShortCaption()];
var dsCol = data?.Columns[ModalResultField.simulationDistance.GetName()];
var esTCol = data?.Columns[ModalResultField.P_terminal_ES.GetName()];
var elAuxCol = data?.Columns[ModalResultField.P_Aux_el_HV.GetName()];
_preRunResults = data?.AsEnumerable().Select(m => new PreRunEntry() {
P_es_T = (Watt)m[esTCol],
P_el_aux = (Watt)m[elAuxCol],
t = (Second)m[timeCol],
dt = (Second)m[dtCol],
s = (Meter)m[distCol],
......@@ -164,7 +166,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var es = GetElectricSystem(batData, batData.InitialSoC, out _);
var rawFcCalcEntries = GetRawFuelCellPowerDemand(TotalDistance, fcData.MinElectricPower, fcData.MaxElectricPower, es,
var rawFcCalcEntries = GetRawFuelCellPowerDemand(TotalDistance, 0.SI<Watt>(), fcData.MaxElectricPower, es,
_preRunResults);
ApplyBatterySafetyMargin(batData, rawFcCalcEntries);
......@@ -283,7 +285,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
///For a given window size as first step we calculate the fuel cell power, try to simulate it with a tracing infinity battery and check if the SoC limits are violated
//var minFcPower = fcData.MinElectricPower;
var minFcPower = 0.SI<Watt>();
var maxFcPower = fcData.MaxElectricPower;
//var maxFcPower = fcData.MaxElectricPower;
batData = batData.Clone();
//Calculate Raw Fuel CellDemand
......@@ -293,7 +295,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var fcCalcEntries = GetRawFuelCellPowerDemand(windowSize: windowSize, minFcPower: minFcPower,
maxFcPower: maxFcPower, es, preRunResults: _preRunResults);
maxFcPower: fcData.MaxElectricPower, es, preRunResults: _preRunResults);
var processedFcCalcEntries = new List<FCCalcEntry>(_preRunResults.Length); //Holds the processed fcCalcEntries to avoid multiple enumerations
//var socLimits = ApplyBatterySafetyMargin(batData, fcCalcEntries, batSystem);
......@@ -310,7 +312,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var deltaEnergyBatInt = 0.SI<WattSecond>();
var timeFcCanChange = 0.SI<Second>();
var infinityDummyContainer = new SimpleModDataContainer();
......@@ -325,7 +327,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var correctedPreRunEntry = new PreRunEntry(entry.preRunEntry);
correctedPreRunResults.Add(correctedPreRunEntry);
var SoC = RequestFromEs(entry, infEs, infinityDummyContainer, tracingInfinityBat, maxFcPower, minFcPower, ref deltaEnergyBatInt, ref timeFcCanChange);
var SoC = RequestFromEs(entry, infEs, infinityDummyContainer, tracingInfinityBat, ref deltaEnergyBatInt);
processedFcCalcEntries.Add(entry);
if ((SoC - tracingInfinityBat.MinSoC).IsGreater(usableRange))
......@@ -349,8 +351,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
distance: windowSize,
entries: processedFcCalcEntries.ToArray(),
reason: $"SoC Range too large usable: {usableRange}, range: {SoC - tracingInfinityBat.MinSoC}");
WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt,
timeFcCanChange, false);
WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt, false);
//Return false to decrease window size
return false;
}
......@@ -360,7 +361,6 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
if (preRunResultsAreCorrected) {
//Throw away old results
deltaEnergyBatInt = 0.SI<WattSecond>();
timeFcCanChange = 0.SI<Second>();
processedFcCalcEntries.Clear();
......@@ -369,7 +369,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
infEs = GetElectricSystem(tracingInfinityBat, batData);
//Calculate updated fc demand
fcCalcEntries = GetRawFuelCellPowerDemand(windowSize, minFcPower, maxFcPower, es,
fcCalcEntries = GetRawFuelCellPowerDemand(windowSize, minFcPower, fcData.MaxElectricPower, es,
correctedPreRunResults);
......@@ -377,8 +377,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
foreach (var entry in fcCalcEntries) {
var SoC = RequestFromEs(entry, infEs, infinityDummyContainer, tracingInfinityBat, maxFcPower,
minFcPower, ref deltaEnergyBatInt, ref timeFcCanChange);
var SoC = RequestFromEs(entry, infEs, infinityDummyContainer, tracingInfinityBat, ref deltaEnergyBatInt);
processedFcCalcEntries.Add(entry);
if ((SoC - tracingInfinityBat.MinSoC).IsGreater(usableRange)) {
throw new VectoException("Violation of usable SoC should be covered");
......@@ -394,51 +393,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var remainingTime = timeFcCanChange;
//compensate delta P_bat_int
foreach (var entry in processedFcCalcEntries)
{
var fcPower = entry.P_FC;
//var fcPower = entry.P_FC_corr;
if (entry.CanChangeFCPower)
{
if (deltaEnergyBatInt.IsGreater(0)) {
//WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt, timeFcCanChange, false);
//throw new NotImplementedException("Positive delta bat_int is not implemented");
}
Watt batChangeTarget = -deltaEnergyBatInt / remainingTime;
var fcChangeTarget = batChangeTarget;
var fcPowerTarget = fcPower + fcChangeTarget;
var fcPowerActual = fcPowerTarget.LimitTo<Watt>(0.SI<Watt>(), maxFcPower);
var fcChangeActual = fcChangeTarget - (fcPowerTarget - fcPowerActual);
var batChangeActual = fcChangeActual;
deltaEnergyBatInt += batChangeActual * entry.dt;
remainingTime -= entry.dt;
entry.delta_P_FCS = fcChangeActual;
entry.FCPowerFinal = fcPowerActual;
if (!entry.FCPowerFinal.IsBetween(0.SI<Watt>(), maxFcPower))
{
//WriteEntriesToFile(windowSize, fcCalcEntries, initSoc, deltaEnergyBatInt, timeFcCanChange);
throw new VectoException("Fuel cell limits violated");
}
}
else
{
entry.FCPowerFinal = fcPower;
entry.delta_P_FCS = 0.SI<Watt>();
}
}
CompensateBatteryDelta(processedFcCalcEntries, deltaEnergyBatInt);
//Check infintiy battery again for violated SoC limits
......@@ -449,7 +404,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
foreach (var entry in processedFcCalcEntries) {
//Should be limited?
var response = tracingInfinityBat.Request(entry.t, entry.dt, entry.P_Bat_T_Final, false);
var response = infEs.Request(entry.t, entry.dt, entry.P_Bat_T_Final, false).RESSResponse;
if (!(response is RESSResponseSuccess))
{
throw new VectoException("Unexpected Response");
......@@ -471,7 +426,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
if(CalculateWithRealBattery(batData, initSoc, processedFcCalcEntries.ToArray()))
{
WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt, timeFcCanChange, true);
WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt, true);
result = new SearchResult(
distance: windowSize,
entries: processedFcCalcEntries.ToArray(),
......@@ -482,7 +437,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
}
}
WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt, timeFcCanChange, false);
WriteEntriesToFile(windowSize, processedFcCalcEntries.ToArray(), initSoc, deltaEnergyBatInt, false);
result = new SearchResult(
distance: windowSize,
entries: processedFcCalcEntries.ToArray(),
......@@ -493,9 +448,29 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
return false;
}
private static void CompensateBatteryDelta(List<FCCalcEntry> processedFcCalcEntries, WattSecond deltaEnergyBatInt)
{
var remainingTime = processedFcCalcEntries.Sum(e => e.CanChangeFCPower ? e.dt.Value() : 0).SI<Second>();
//Maximum Additional Energy that can be provided by the fuel cell in a timestep
var maxInc =
processedFcCalcEntries.Select(e => !e.CanChangeFCPower ? 0.SI<WattSecond>() : VectoMath.Max(e.P_max_fc - e.P_FC_corr, 0.SI<Watt>()) * e.dt).ToArray();
var weights = new List<double>(maxInc.Length);
var totalIncPotential = maxInc.Sum();
for (var i = 0; i < maxInc.Length; i++) {
var weight = maxInc[i] / totalIncPotential;
weights.Add(weight);
var entry = processedFcCalcEntries[i];
entry.delta_P_FCS = weights[i] * -deltaEnergyBatInt / entry.dt;
if (entry.FCPowerFinal.IsGreater(entry.P_max_CFCS)) {
throw new VectoException("Power too high");
}
}
}
private static double RequestFromEs(FCCalcEntry entry, ElectricSystem infinityEs,
SimpleModDataContainer infinityDummyContainer, TracingInfinityBatterySystem infBat, Watt maxFcPower, Watt minFcPower, ref WattSecond deltaEnergyBatInt,
ref Second timeFcCanChange)
SimpleModDataContainer infinityDummyContainer, TracingInfinityBatterySystem infBat, ref WattSecond deltaEnergyBatInt)
{
//Since we are using the infinity battery, the limit that is applied is only influenced by the properties of the battery but not by the SoC
var batPower = entry.P_Bat_T.LimitTo
......@@ -513,22 +488,10 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
deltaEnergyBatInt += infinityDummyContainer.P_REES_int * entry.dt;
entry.P_REESS_int = infinityDummyContainer.P_REES_int;
var SoC = infinityEs.StateOfCharge; //Current virtual soc
entry.SoC = SoC;
// check SoC min/max over cycle
// check P_bat min/max (
entry.CanChangeFCPower = !entry.P_FC.IsEqual(0) //Fuel cell is switched off
&& (entry.P_FC_corr.IsSmaller(maxFcPower) ||
entry.P_FC_corr.IsGreater(minFcPower)); //power is within limits
if (entry.CanChangeFCPower) {
timeFcCanChange += entry.dt;
}
return SoC;
}
......@@ -570,6 +533,10 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
batSystem.Initialize(SoC);
var dummyContainer = new SimpleModDataContainer();
var es = new ElectricSystem(null, batData);
es.Connect(batSystem);
var infBattery = new TracingInfinityBatterySystem(batData);
infBattery.Initialize((batSystem.MaxSoC + batSystem.MinSoC) / 2);
......@@ -578,17 +545,24 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
//var batPower = entry.P_Bat_T_Final;
//var maxPower = infBattery.MaxChargePower(entry.dt);
//var minPower = infBattery.MaxDischargePower(entry.dt);
var maxPower = es.MaxChargePower(entry.dt);
var minPower = es.MaxDischargePower(entry.dt);
var maxPower = batSystem.MaxChargePower(entry.dt);
var minPower = batSystem.MaxDischargePower(entry.dt);
//We have to limit the power, if we are recuperating in the prerun, it is possible, that the power limits of the battery are violated here,
//In the real simulation, there will be no recuperation if the power limit is reached
var batPower = entry.P_Bat_T_Final.LimitTo(minPower,maxPower);
//Limit batPower
var batResponse = batSystem.Request(entry.t, entry.dt, batPower, false);
var batResponse = es.Request(entry.t, entry.dt, batPower, false).RESSResponse;
if (!(batResponse is RESSResponseSuccess))
{
//Assuming no recuperation
return false;
}
......@@ -603,7 +577,7 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
[Conditional("TRACE_FC")]
private void WriteEntriesToFile(Meter windowSize, FCCalcEntry[] fcCalcEntries, double initSoc,
WattSecond deltaEnergyBatInt, Second timeFcCanChange, bool success)
WattSecond deltaEnergyBatInt, bool success)
{
lock (fileLock)
......@@ -611,7 +585,6 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
using (var fs = new StreamWriter(Path.Combine(Path.GetDirectoryName(Writer?.JobFile) ?? "", $"fuelcell_data_{RunName}_{Math.Round(windowSize.Value(), 0)}_soc_{initSoc}_{(success ? "success" : "")}.csv")))
{
fs.WriteLine($"DeltaEnergyBat: {deltaEnergyBatInt} / {deltaEnergyBatInt.ConvertToKiloWattHour()}");
fs.WriteLine($"timeFCCanIncrease: {timeFcCanChange}");
fs.WriteLine(FCCalcEntry.Header);
foreach (var entry in fcCalcEntries)
{
......@@ -673,19 +646,26 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
var P_FC_raw = preRunEntries.TimeIntegral<PreRunEntry, Second, Watt, WattSecond>((e) => e.dt, (e) => e.P_es_T) /
totalDuration;
foreach (var r in preRunEntries) {
var entry = new FCCalcEntry(maxChargingPower, maxDischargingPower, r) {
var entry = new FCCalcEntry(maxChargingPower, maxDischargingPower, r, maxFcPower) {
WindowSize = windowSize,
P_FC_raw = -P_FC_raw,
P_el_dem = r.P_es_T,
};
entry.P_FC = entry.P_FC_raw > minFcPower
? VectoMath.Min(entry.P_FC_raw, maxFcPower)
: entry.P_FC_raw.IsSmaller(0)
? 0.SI<Watt>()
: minFcPower;
entry.P_FC = entry.P_FC_raw.LimitTo(minFcPower, entry.P_max_fc);
var esResponse = es.Request(entry.t, entry.dt, entry.P_Bat_T, true);
entry.P_Bat_loss = esResponse.RESSResponse.LossPower;
if (entry.FCPowerFinal > entry.P_max_fc) {
entry.P_FC -= 2*entry.P_Bat_loss;
entry.P_Bat_loss = es.Request(entry.t, entry.dt, entry.P_Bat_T, true).RESSResponse.LossPower;
}
yield return entry;
}
......@@ -697,9 +677,8 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
for (; !wIt.EndReached; wIt.MoveNext()) {
var entry = new FCCalcEntry(maxChargingPower, maxDischargingPower, preRunEntries[wIt.Position]) {
var entry = new FCCalcEntry(maxChargingPower, maxDischargingPower, preRunEntries[wIt.Position], maxFcPower) {
WindowSize = windowSize,
};
......@@ -712,15 +691,25 @@ namespace TUGraz.VectoCore.OutputData.ModDataPostprocessing.Impl.FuelCell
emEnergy += dt * preRunEntries[wIt.CurrentIndex].P_es_T;
}
entry.P_FC_raw = -emEnergy / time;
entry.P_FC = entry.P_FC_raw > minFcPower
? VectoMath.Min(entry.P_FC_raw, maxFcPower)
: entry.P_FC_raw.IsSmaller(0)
? 0.SI<Watt>()
: minFcPower;
entry.P_el_dem = preRunEntries[wIt.Position].P_es_T;
entry.P_FC_raw = -emEnergy / time;
entry.P_FC = entry.P_FC_raw.LimitTo(minFcPower, entry.P_max_fc);
var esResponse = es.Request(entry.t, entry.dt, entry.P_Bat_T, true);
entry.P_Bat_loss = esResponse.RESSResponse.LossPower;
if (entry.FCPowerFinal > entry.P_max_fc) {
entry.P_FC -= 2*entry.P_Bat_loss; //If P_Bat_T is negative, decreasing the fuel cell power will increase the loss power. Leading to Fuel Cell power that is still too high
entry.P_Bat_loss = es.Request(entry.t, entry.dt, entry.P_Bat_T, true).RESSResponse.LossPower;
}
if (entry.FCPowerFinal > entry.P_max_fc) {
}
yield return entry;
}
......
......@@ -94,6 +94,9 @@ namespace TUGraz.VectoCore.Tests.Integration.FuelCell
//var outputFile = jobFile.Replace(".vecto", fileWriterSuffix + ".vecto");
var writer = new FileOutputWriter(jobFile);
TestContext.Progress.WriteLine($"Creating job using {jobFile}");
var factory = SimulatorFactory.CreateSimulatorFactory(ExecutionMode.Engineering, inputProvider, writer);
factory.Validate = false;
factory.WriteModalResults = true;
......@@ -138,8 +141,8 @@ namespace TUGraz.VectoCore.Tests.Integration.FuelCell
[TestCase(FCHV_E2_JOB, 0, 5, 10, 300, TestName = "FCHV E2 Job RD single FC, 5kWh , 300 kW 0")]
[TestCase(FCHV_E2_JOB, 0, 5, 10, 500, TestName = "FCHV E2 Job RD single FC, 5kWh , 500 kW 0")]
[TestCase(FCHV_E2_JOB, 0, 1, 10, 300, TestName = "FCHV E2 Job RD single FC, 1kWh , 300 kW 0")]
[TestCase(FCHV_E2_JOB, 0, 1, 10, 500, TestName = "FCHV E2 Job RD single FC, 1kWh , 500 kW 0")]
[TestCase(FCHV_E2_JOB, 0, 1, 10, 300, TestName = "FCHV E2 Job RD single FC, 1kWh , 300 kW 0", Ignore = "Battery too small")]
[TestCase(FCHV_E2_JOB, 0, 1, 10, 500, TestName = "FCHV E2 Job RD single FC, 1kWh , 500 kW 0", Ignore = "Battery too small")]
//[TestCase(FCHV_E2_JOB, 0, 0.1, 10, 300, TestName = "FCHV E2 Job RD single FC, 0.1kWh , 300 kW 0")]
//[TestCase(FCHV_E2_JOB, 0, 0.1, 10, 500, TestName = "FCHV E2 Job RD single FC, 0.1kWh , 500 kW 0")]
......@@ -162,7 +165,7 @@ namespace TUGraz.VectoCore.Tests.Integration.FuelCell
[TestCase(FCHV_E2_JOB, 1, 10, 10, 100, TestName = "FCHV E2 Job RD single FC, 10kWh , 100 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 10, 10, 300, TestName = "FCHV E2 Job RD single FC, 10kWh , 300 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 10, 10, 500, TestName = "FCHV E2 Job RD single FC, 10kWh , 500 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 5, 10, 100, TestName = "FCHV E2 Job RD single FC, 5kWh , 100 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 5, 10, 100, TestName = "FCHV E2 Job RD single FC, 5kWh , 100 kW 1", Ignore = "Battery too small")]
[TestCase(FCHV_E2_JOB, 1, 5, 10, 300, TestName = "FCHV E2 Job RD single FC, 5kWh , 300 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 5, 10, 500, TestName = "FCHV E2 Job RD single FC, 5kWh , 500 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 1, 10, 100, TestName = "FCHV E2 Job RD single FC, 1kWh , 100 kW 1", Ignore = "Battery too small")]
......@@ -198,9 +201,9 @@ namespace TUGraz.VectoCore.Tests.Integration.FuelCell
[TestCase(FCHV_E2_JOB, 1, 100, 10, 500, 150, TestName = "FCHV E2 Job RD single FC, 100kWh , 500 kW 1")]
[TestCase(FCHV_E2_JOB, 2, 80, 10, 300, 150, TestName = "FCHV E2 Job RD single FC, 80kWh , 300 kW 2")]
[TestCase(FCHV_E2_JOB, 1, 20, 10, 500, 100, TestName = "FCHV E2 Job RD single FC, 20kWh , 500 kW 1")]
[TestCase(FCHV_E2_JOB, 1, 100, 10, 500, 150, TestName = "FCHV E2 Job RD single FC, 100kWh , 500 kW ltd charge")]
[TestCase(FCHV_E2_JOB, 2, 80, 10, 300, 150, TestName = "FCHV E2 Job RD single FC, 80kWh , 300 kW ltd charge")]
[TestCase(FCHV_E2_JOB, 1, 20, 10, 500, 100, TestName = "FCHV E2 Job RD single FC, 20kWh , 500 kW ltd charge")]
[Parallelizable(ParallelScope.Children)]
//[TestCase(FCHV_E2_JOB, 2, 1, 10, 100, TestName = "FCHV E2 Job RD single FC, 1kWh , 100 kW 2")]
......@@ -280,10 +283,6 @@ namespace TUGraz.VectoCore.Tests.Integration.FuelCell
WritePostprocessingInfo(pP);
Assert.That(rd.BatteryData.InitialSoC, Is.EqualTo(pP.StartSoC));
//if (pP.BinarySearchIterations > 0) {
// Assert.Fail();
//}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment