From ec643d1c875632cec979c1604878ca81bd66890f Mon Sep 17 00:00:00 2001 From: 20182294 Date: Wed, 23 Jul 2025 16:56:59 +0200 Subject: [PATCH] PeakShavingForecast target implementation Made compatible for EnergyCoop, GridNode, and GridConnection + added charging efficiency --- ..._BatteryManagementPeakShavingForecast.java | 209 +++++++++++------- _alp/Classes/Class.J_EAProduction.java | 4 + _alp/Classes/Class.J_EAStorageElectric.java | 8 + 3 files changed, 146 insertions(+), 75 deletions(-) diff --git a/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java b/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java index 2f170080..a3396be8 100644 --- a/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java +++ b/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java @@ -1,97 +1,156 @@ import zeroPackage.ZeroMath; /** - * J_BatteryManagementPeakShavingForecast + * J_BatteryManagementPeakShavingForecastGrid */ public class J_BatteryManagementPeakShavingForecast implements I_BatteryManagement { + + private double[] batteryChargingSchedule_kW = new double[96]; + private GridConnection parentGC; + private Agent target = parentGC; + private OL_ResultScope targetType = OL_ResultScope.GRIDCONNECTION; + List c_targetGridConnections = new ArrayList(); + double p_timestep_h; - private double[] batteryChargingForecast_kW = new double[96]; - private GridConnection gc; - /** - * Default constructor - */ - public J_BatteryManagementPeakShavingForecast( GridConnection gc ) { - this.gc = gc; + + + public J_BatteryManagementPeakShavingForecast( GridConnection parentGC ) { + this.parentGC = parentGC; + p_timestep_h = parentGC.energyModel.p_timeStep_h; + if (parentGC instanceof GCGridBattery) { + this.setTarget(null); + } else { + this.setTarget(parentGC); + } } - - /** - * - */ - public void manageBattery() { - int index = roundToInt((gc.energyModel.t_h % 24)/gc.energyModel.p_timeStep_h); + + + + public void manageBattery() { + if (this.target == null) { + parentGC.p_batteryAsset.f_updateAllFlows(0); + return; + } + int index = roundToInt((parentGC.energyModel.t_h % 24)/p_timestep_h); if(index == 0){ - this.f_peakShavingForecast(); + this.batteryChargingSchedule_kW = this.calculateBatteryChargingSchedule(); } - gc.p_batteryAsset.f_updateAllFlows( this.batteryChargingForecast_kW[index] / gc.p_batteryAsset.getCapacityElectric_kW() ); + parentGC.p_batteryAsset.f_updateAllFlows( this.batteryChargingSchedule_kW[index] / parentGC.p_batteryAsset.getCapacityElectric_kW() ); } - - private void f_peakShavingForecast() { - double amountOfHoursInADay = 24; - double[] nettoBalance_kW = new double[96]; - - //Get elec consumption profile - J_EAProfile elecConsumptionProfile = findFirst(gc.c_profileAssets, profile -> profile.profileType == OL_ProfileAssetType.ELECTRICITYBASELOAD); - - J_EAConsumption elecConsumptionConsumptionAsset = findFirst(gc.c_consumptionAssets, cons -> cons.energyAssetType == OL_EnergyAssetType.ELECTRICITY_DEMAND); - - J_EAProduction elecProductionAsset = findFirst(gc.c_productionAssets, prod -> prod.energyAssetType == OL_EnergyAssetType.PHOTOVOLTAIC); - - //For simulation that cross the year end - double hour_of_simulation_year = gc.energyModel.t_h - gc.energyModel.p_runStartTime_h; - //traceln("hour_of_year: " + hour_of_simulation_year); + + + + private double[] getNettoBalanceForecast_kW() { + + double[] nettoBalanceTotal_kW = new double[96]; + double energyModel_time_h = parentGC.energyModel.t_h; + + //For simulation that cross the year end + double hour_of_simulation_year = energyModel_time_h - parentGC.energyModel.p_runStartTime_h; + + int startTimeDayIndex = roundToInt(hour_of_simulation_year/p_timestep_h); + int endTimeDayIndex = roundToInt((hour_of_simulation_year + 24)/p_timestep_h); + + List profileAssets = new ArrayList(); + List consumptionAssets = new ArrayList(); + List productionAssets = new ArrayList(); + + for (GridConnection GC : c_targetGridConnections){ + profileAssets.addAll(findAll(GC.c_profileAssets, profile -> profile.profileType == OL_ProfileAssetType.ELECTRICITYBASELOAD)); + consumptionAssets.addAll(findAll(GC.c_consumptionAssets, cons -> cons.energyAssetType == OL_EnergyAssetType.ELECTRICITY_DEMAND)); + productionAssets.addAll(findAll(GC.c_productionAssets, prod -> prod.energyAssetType == OL_EnergyAssetType.PHOTOVOLTAIC || prod.energyAssetType == OL_EnergyAssetType.WINDMILL)); + } + + for(J_EAProfile profileAsset : profileAssets) { + double[] tempNettoBalance_kW = ZeroMath.arrayMultiply(Arrays.copyOfRange(profileAsset.a_energyProfile_kWh, startTimeDayIndex, endTimeDayIndex), 1/p_timestep_h); + for (int i = 0; i < tempNettoBalance_kW.length; i++) { + nettoBalanceTotal_kW[i] += tempNettoBalance_kW[i]; + } + } + for(J_EAConsumption consumptionAsset : consumptionAssets) { + for(double time = energyModel_time_h; time < energyModel_time_h + 24; time += p_timestep_h){ + nettoBalanceTotal_kW[roundToInt((time-energyModel_time_h)/p_timestep_h)] += consumptionAsset.getProfilePointer().getValue(time)*consumptionAsset.yearlyDemand_kWh*consumptionAsset.getConsumptionScaling_fr(); + } + } + for(J_EAProduction productionAsset : productionAssets) { + for(double time = energyModel_time_h; time < energyModel_time_h + 24; time += p_timestep_h){ + nettoBalanceTotal_kW[roundToInt((time-energyModel_time_h)/p_timestep_h)] -= productionAsset.getProfilePointer().getValue(time)*productionAsset.getCapacityElectric_kW(); + } + } + return nettoBalanceTotal_kW; + } + + + + private double[] calculateBatteryChargingSchedule() { + + double[] nettoBalanceTotal_kW = getNettoBalanceForecast_kW(); + double amountOfHoursInADay = 24; + + //Initialize chargepoint array + double[] newBatteryChargingSchedule_kW = new double[96]; + + //Calculate the total export over the day that can be collected by the battery + double totalExport_kWh = 0; + for(int i = 0; i < nettoBalanceTotal_kW.length; i++){ + if(nettoBalanceTotal_kW[i] < 0){ + totalExport_kWh += min(parentGC.p_batteryAsset.getCapacityElectric_kW(), -nettoBalanceTotal_kW[i])*p_timestep_h; + } + } + + //Flatten the morning net balance while charging + double totalDailyImport_kWh = 0; + for(int i = 0; i < nettoBalanceTotal_kW.length; i++){ + if(i< amountOfHoursInADay/p_timestep_h){ + totalDailyImport_kWh += max(0,nettoBalanceTotal_kW[i]*p_timestep_h); + } + } - int startTimeDayIndex = roundToInt(hour_of_simulation_year/gc.energyModel.p_timeStep_h); - int endTimeDayIndex = roundToInt((hour_of_simulation_year + 24)/gc.energyModel.p_timeStep_h); + double batteryEnergyNeeded_kWh = max(0,(parentGC.p_batteryAsset.getStorageCapacity_kWh()*(1-parentGC.p_batteryAsset.getCurrentStateOfCharge_fr()))/parentGC.p_batteryAsset.getChargingEfficiency_r()-totalExport_kWh); + double averageDailyConsumption_kW = (totalDailyImport_kWh + batteryEnergyNeeded_kWh)/amountOfHoursInADay; - if(elecConsumptionProfile != null){ - nettoBalance_kW = ZeroMath.arrayMultiply(Arrays.copyOfRange(elecConsumptionProfile.a_energyProfile_kWh, startTimeDayIndex, endTimeDayIndex), 1/gc.energyModel.p_timeStep_h); - } - if(elecConsumptionConsumptionAsset != null){ - for(double time = gc.energyModel.t_h; time < gc.energyModel.t_h + 24; time += gc.energyModel.p_timeStep_h){ - nettoBalance_kW[roundToInt((time-gc.energyModel.t_h)/gc.energyModel.p_timeStep_h)] += elecConsumptionConsumptionAsset.profilePointer.getValue(time)*elecConsumptionConsumptionAsset.yearlyDemand_kWh*elecConsumptionConsumptionAsset.getConsumptionScaling_fr(); - } + //If 24 hours + for(int i = 0; i < nettoBalanceTotal_kW.length; i++){ + newBatteryChargingSchedule_kW[i] += averageDailyConsumption_kW - nettoBalanceTotal_kW[i]; + } + + return newBatteryChargingSchedule_kW; + } + + + + public void setTarget( Agent agent ) { + if ( agent == null) { + target = null; + this.targetType = null; } - - if(elecProductionAsset != null){ - for(double time = gc.energyModel.t_h; time < gc.energyModel.t_h + 24; time += gc.energyModel.p_timeStep_h){ - nettoBalance_kW[roundToInt((time-gc.energyModel.t_h)/gc.energyModel.p_timeStep_h)] -= elecProductionAsset.profilePointer.getValue(time)*elecProductionAsset.getCapacityElectric_kW(); - } + else if (agent == this.parentGC) { + target = agent; + this.targetType = OL_ResultScope.GRIDCONNECTION; + c_targetGridConnections = new ArrayList(); + c_targetGridConnections.add((GridConnection)target); } - - - ////Fill chargesetpoint Array - - //Initialize chargepoint array - this.batteryChargingForecast_kW = new double[96]; - - - //Calculate the total export over the day that can be collected by the battery - double totalExport_kWh = 0; - for(int i = 0; i < nettoBalance_kW.length; i++){ - if(nettoBalance_kW[i] < 0){ - totalExport_kWh += min(gc.p_batteryAsset.getCapacityElectric_kW(), -nettoBalance_kW[i])*gc.energyModel.p_timeStep_h; - } + else if (agent instanceof GridNode) { + target = agent; + this.targetType = OL_ResultScope.GRIDNODE; + c_targetGridConnections = new ArrayList(((GridNode)target).f_getAllLowerLVLConnectedGridConnections()); } - - - //Flatten the morning net balance while charging - double totalDailyImport_kWh = 0; - for(int i = 0; i < nettoBalance_kW.length; i++){ - if(i< amountOfHoursInADay/gc.energyModel.p_timeStep_h){ - totalDailyImport_kWh += max(0,nettoBalance_kW[i]*gc.energyModel.p_timeStep_h); - } + else if (agent instanceof EnergyCoop) { + target = agent; + this.targetType = OL_ResultScope.ENERGYCOOP; + c_targetGridConnections = new ArrayList(((EnergyCoop)target).f_getAllChildMemberGridConnections()); } - double batteryEnergyNeeded_kWh = max(0,(gc.p_batteryAsset.getStorageCapacity_kWh()*(1-gc.p_batteryAsset.getCurrentStateOfCharge_fr()))-totalExport_kWh); - double averageDailyConsumption_kW = (totalDailyImport_kWh + batteryEnergyNeeded_kWh)/amountOfHoursInADay; - - //If 24 hours - for(int i = 0; i < nettoBalance_kW.length; i++){ - this.batteryChargingForecast_kW[i] += averageDailyConsumption_kW - nettoBalance_kW[i]; + else { + throw new RuntimeException("Not able to set " + agent + " as a target for J_BatteryPeakShaving"); } - return; } + + + @Override public String toString() { - return super.toString(); + return "parentGC: " + parentGC + + ", target: " + target + + ", c_targetGridConnections: " + c_targetGridConnections; } /** diff --git a/_alp/Classes/Class.J_EAProduction.java b/_alp/Classes/Class.J_EAProduction.java index 49b5e43f..98660694 100644 --- a/_alp/Classes/Class.J_EAProduction.java +++ b/_alp/Classes/Class.J_EAProduction.java @@ -167,6 +167,10 @@ public double getEnergyCurtailed_kWh() { return this.totalEnergyCurtailed_kWh; } + public J_ProfilePointer getProfilePointer() { + return this.profilePointer; + } + @Override public void storeStatesAndReset() { this.totalEnergyCurtailed_kWh = 0; diff --git a/_alp/Classes/Class.J_EAStorageElectric.java b/_alp/Classes/Class.J_EAStorageElectric.java index 3c2687a3..5ca90a2d 100644 --- a/_alp/Classes/Class.J_EAStorageElectric.java +++ b/_alp/Classes/Class.J_EAStorageElectric.java @@ -122,6 +122,14 @@ public double getTotalDischargeAmount_kWh() { return this.discharged_kWh; } + public double getChargingEfficiency_r() { + return this.etaCharge_r; + } + + public double getDischargingEfficiency_r() { + return this.etaDischarge_r; + } + public void setStorageCapacity_kWh(double storageCapacity_kWh) { double difference_kWh = storageCapacity_kWh - this.storageCapacity_kWh; this.storageCapacity_kWh = storageCapacity_kWh;