Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Zero_engine.alpx
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,16 @@
<Name><![CDATA[J_HeatingManagementPIcontrolHybridHeatpump]]></Name>
<Folder>1762850578079</Folder>
</JavaClass>
<JavaClass>
<Id>1763557451043</Id>
<Name><![CDATA[J_HeatingFunctionLibrary]]></Name>
<Folder>1762850578079</Folder>
</JavaClass>
<JavaClass>
<Id>1763570262548</Id>
<Name><![CDATA[J_GlobalParameters]]></Name>
<Folder>1752680962144</Folder>
</JavaClass>
</JavaClasses>
<RequiredLibraryReference>
<LibraryName>com.anylogic.libraries.modules.markup_descriptors</LibraryName>
Expand Down
3 changes: 2 additions & 1 deletion _alp/Classes/Class.J_EAConversion.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public J_EAConversion(Agent parentAgent, OL_EnergyAssetType energyAssetType, dou

@Override
public void f_updateAllFlows(double powerFraction_fr) {
if ( powerFraction_fr < 0 ) {
powerFraction_fr = roundToDecimal(powerFraction_fr, J_GlobalParameters.floatingPointPrecision);
if(powerFraction_fr < 0) {
throw new RuntimeException("Impossible to operate conversion asset with negative powerfraction.");
}
else if ( powerFraction_fr == 0 ) {
Expand Down
7 changes: 7 additions & 0 deletions _alp/Classes/Class.J_GlobalParameters.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* J_GlobalParameters
*/
public abstract class J_GlobalParameters {
//public final static double floatingPointErrorMargin = 1e-15;
public final static int floatingPointPrecision = 15;
}
76 changes: 76 additions & 0 deletions _alp/Classes/Class.J_HeatingFunctionLibrary.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* J_HeatingFunctionLibrary
*/
public abstract class J_HeatingFunctionLibrary {

public static double managePTAndHotWaterHeatBuffer(J_EAStorageHeat hotWaterBuffer, List<J_EAProduction> ptAssets, double hotWaterDemand_kW){
//Calculate the pt production
double ptProduction_kW = 0;
for (J_EA j_ea : ptAssets) {
ptProduction_kW -= j_ea.getLastFlows().get(OL_EnergyCarriers.HEAT);
}

//Calculate the remaining hot water energy need after pt production, also calculate the remaining unused pt production
double remainingHotWater_kW = max(0, hotWaterDemand_kW - ptProduction_kW); // Need to do this, because pt has already compensated the hot water demand in the gc flows, so just need to update this value
double remainingPTProduction_kW = max(0, ptProduction_kW - hotWaterDemand_kW);

if(hotWaterBuffer != null){
double chargeSetpoint_kW = 0;
if(remainingHotWater_kW > 0) {
chargeSetpoint_kW = -remainingHotWater_kW;
}
else if(remainingPTProduction_kW > 0) {
chargeSetpoint_kW = remainingPTProduction_kW;
}
hotWaterBuffer.v_powerFraction_fr = chargeSetpoint_kW / hotWaterBuffer.getCapacityHeat_kW();
hotWaterBuffer.f_updateAllFlows(hotWaterBuffer.v_powerFraction_fr);

double heatBufferCharge_kW = hotWaterBuffer.getLastFlows().get(OL_EnergyCarriers.HEAT);

if(remainingHotWater_kW > 0){//Only if the current pt production, wasnt enough, adjust the hotwater demand with the buffer, cause then the buffer will have tried to discharge
remainingHotWater_kW = max(0, remainingHotWater_kW + heatBufferCharge_kW);
}
else {//Curtail the remaining pt that is not used for hot water
remainingPTProduction_kW = max(0, remainingPTProduction_kW - heatBufferCharge_kW);
}
}

if (remainingPTProduction_kW > 0) {//Heat (for now always curtail over produced heat!)
for (J_EAProduction j_ea : ptAssets) {
remainingPTProduction_kW -= j_ea.curtailEnergyCarrierProduction( OL_EnergyCarriers.HEAT, remainingPTProduction_kW);

if (remainingPTProduction_kW <= 0) {
break;
}
}
}
return remainingHotWater_kW;
}

public static double manageHotWaterHeatBuffer(J_EAStorageHeat hotWaterBuffer, double hotWaterDemand_kW, double availableHeatingPower_kWth, double timeStep_h){
if(hotWaterDemand_kW > availableHeatingPower_kWth + hotWaterBuffer.getCurrentStateOfCharge_kWh() / timeStep_h) {
throw new RuntimeException("Hot water demand is higher than available power.");
}

//Heating asset should always try to fill the heat buffer as fast as possible.
double hotWaterDemandFromHeatingAsset_kW = min(availableHeatingPower_kWth, hotWaterDemand_kW + (hotWaterBuffer.getStorageCapacity_kWh() - hotWaterBuffer.getCurrentStateOfCharge_kWh()));
double heatIntoBuffer_kW = hotWaterDemandFromHeatingAsset_kW - hotWaterDemand_kW;


hotWaterBuffer.v_powerFraction_fr = heatIntoBuffer_kW / hotWaterBuffer.getCapacityHeat_kW();
hotWaterBuffer.f_updateAllFlows(hotWaterBuffer.v_powerFraction_fr);


return hotWaterDemandFromHeatingAsset_kW;
}
}










93 changes: 34 additions & 59 deletions _alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ public class J_HeatingManagementHeatpumpOffPeak implements I_HeatingManagement {
private J_EABuilding building;
private J_EAConversion heatingAsset;
private J_HeatingPreferences heatingPreferences;

private J_EAStorageHeat hotWaterBuffer;
private List<J_EAProduction> ptAssets;
private boolean hasPT = false;
private boolean hasHotWaterBuffer = false;

// PI control gains
private double P_gain_kWpDegC = 1*1;
private double I_gain_kWphDegC = 0.1*2;
Expand Down Expand Up @@ -74,13 +78,27 @@ public void manageHeating() {
calculatePreHeatParameters();
}

//Adjust the hot water and overall heat demand with the buffer and pt
double hotWaterDemand_kW = gc.p_DHWAsset != null ? gc.p_DHWAsset.getLastFlows().get(OL_EnergyCarriers.HEAT) : 0;
double remainingHotWaterDemand_kW = managePTAndHotWaterHeatBuffer(hotWaterDemand_kW);
double ptAssetPower_kW = ptAssets != null ? sum(ptAssets, pt -> pt.getLastFlows().get(OL_EnergyCarriers.HEAT)) : 0;
double additionalHeatDemand_kW = (gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.HEAT) - hotWaterDemand_kW + (-ptAssetPower_kW));

double currentHeatDemand_kW = additionalHeatDemand_kW;
double availableAssetPowerForHotWater_kWth = heatingAsset.getOutputCapacity_kW() - additionalHeatDemand_kW;

//Manage hot water if additional systems are present
if(this.hasPT) {
//Adjust the hot water and overall heat demand with the buffer and pt
double remainingHotWaterDemand_kW = J_HeatingFunctionLibrary.managePTAndHotWaterHeatBuffer(hotWaterBuffer, ptAssets, hotWaterDemand_kW); // This function updates the buffer and curtails PT if needed -> current balanceflow is updated accordingly.
currentHeatDemand_kW += remainingHotWaterDemand_kW;
}
else if(this.hasHotWaterBuffer) {
double heatDemandFromHeatingAssetForHotWater_kW = J_HeatingFunctionLibrary.manageHotWaterHeatBuffer(this.hotWaterBuffer, hotWaterDemand_kW, availableAssetPowerForHotWater_kWth, this.timeStep_h);
currentHeatDemand_kW += heatDemandFromHeatingAssetForHotWater_kW;
}
else {
currentHeatDemand_kW += hotWaterDemand_kW;
}

//Get the remaining heat demand (hot water, and potential other profiles)
double otherHeatDemand_kW = gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.HEAT);

//Determine if time is in reduced Heating interval
boolean timeIsInReducedHeatingInterval = ((timeOfDay_h - startTimeOfReducedHeatingInterval_hr + 24) % 24) < reducedHeatingIntervalLength_hr;
boolean timeIsInPreheatInterval = ((timeOfDay_h - (startTimeOfReducedHeatingInterval_hr - preHeatDuration_hr) + 24) % 24) < preHeatDuration_hr;
Expand Down Expand Up @@ -115,11 +133,11 @@ else if (timeOfDay_h < heatingPreferences.getStartOfDayTime_h() || timeOfDay_h >
double buildingHeatingDemand_kW = max(0,deltaT_degC * P_gain_kWpDegC + I_state_hDegC * I_gain_kWphDegC);

//Set asset power
double assetPower_kW = min(heatingAsset.getOutputCapacity_kW(), buildingHeatingDemand_kW + otherHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset.
double assetPower_kW = min(heatingAsset.getOutputCapacity_kW(), buildingHeatingDemand_kW + currentHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset.
heatingAsset.f_updateAllFlows( assetPower_kW / heatingAsset.getOutputCapacity_kW() );

//Set building power (other heat demand gets bias if asset does not have enough capacity)
double heatIntoBuilding_kW = max(0, assetPower_kW - otherHeatDemand_kW);
double heatIntoBuilding_kW = max(0, assetPower_kW - currentHeatDemand_kW);
building.f_updateAllFlows( heatIntoBuilding_kW / building.getCapacityHeat_kW() );
}

Expand Down Expand Up @@ -188,53 +206,6 @@ private void calculatePreHeatParameters() {
//For now, preheat duration of 2 hours is assumed.
}
}

private double managePTAndHotWaterHeatBuffer(double hotWaterDemand_kW){

//Calculate the pt production
double ptProduction_kW = 0;
List<J_EAProduction> ptAssets = findAll(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
for (J_EA j_ea : ptAssets) {
ptProduction_kW -= j_ea.getLastFlows().get(OL_EnergyCarriers.HEAT);
}

//Calculate the remaining hot water energy need after pt production, also calculate the remaining unused pt production
double remainingHotWater_kW = max(0, hotWaterDemand_kW - ptProduction_kW); // Need to do this, because pt has already compensated the hot water demand in the gc flows, so just need to update this value
double remainingPTProduction_kW = max(0, ptProduction_kW - hotWaterDemand_kW);

if(gc.p_heatBuffer != null){
double chargeSetpoint_kW = 0;
if(remainingHotWater_kW > 0) {
chargeSetpoint_kW = -remainingHotWater_kW;
}
else if(remainingPTProduction_kW > 0) {
chargeSetpoint_kW = remainingPTProduction_kW;
}
gc.p_heatBuffer.v_powerFraction_fr = chargeSetpoint_kW / gc.p_heatBuffer.getCapacityHeat_kW();
gc.p_heatBuffer.f_updateAllFlows(gc.p_heatBuffer.v_powerFraction_fr);

double heatBufferCharge_kW = gc.p_heatBuffer.getLastFlows().get(OL_EnergyCarriers.HEAT);

if(remainingHotWater_kW > 0){//Only if the current pt production, wasnt enough, adjust the hotwater demand with the buffer, cause then the buffer will have tried to discharge
remainingHotWater_kW = max(0, remainingHotWater_kW + heatBufferCharge_kW);
}
else {//Curtail the remaining pt that is not used for hot water
remainingPTProduction_kW = max(0, remainingPTProduction_kW - heatBufferCharge_kW);
if (remainingPTProduction_kW > 0) {//Heat (for now always curtail over produced heat!)
for (J_EAProduction j_ea : ptAssets) {
remainingPTProduction_kW -= j_ea.curtailEnergyCarrierProduction( OL_EnergyCarriers.HEAT, remainingPTProduction_kW);

if (remainingPTProduction_kW <= 0) {
break;
}
}
}
}
}
return remainingHotWater_kW;
}




public void setStartTimeOfReducedHeatingInterval_hr(double startTimeOfReducedHeatingInterval_hr) {
Expand Down Expand Up @@ -268,16 +239,20 @@ public void initializeAssets() {
if (!validHeatingTypes.contains(this.currentHeatingType)) {
throw new RuntimeException(this.getClass() + " does not support heating type: " + this.currentHeatingType);
}
J_EAProduction ptAsset = findFirst(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
if (ptAsset != null) {
List<J_EAProduction> ptAssets = findAll(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
if (ptAssets.size() > 0) {
if(gc.p_DHWAsset == null) {
throw new RuntimeException(this.getClass() + " requires a hot water demand to make sense to use this heating management with PT.");
}
this.ptAssets = ptAssets;
this.hasPT = true;
}
if (gc.p_heatBuffer != null) {
if(gc.p_DHWAsset == null && ptAsset == null) {
throw new RuntimeException(this.getClass() + " requires a hot water demand and PT to make sense to use this heating management with a heatbuffer.");
if(gc.p_DHWAsset == null) {
throw new RuntimeException(this.getClass() + " requires a hot water demand to make sense to use this heating management with a heatbuffer.");
}
this.hotWaterBuffer = gc.p_heatBuffer;
this.hasHotWaterBuffer = true;
}
if(gc.p_BuildingThermalAsset != null) {
this.building = gc.p_BuildingThermalAsset;
Expand Down
87 changes: 33 additions & 54 deletions _alp/Classes/Class.J_HeatingManagementPIcontrol.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public class J_HeatingManagementPIcontrol implements I_HeatingManagement {
private J_EABuilding building;
private J_EAConversion heatingAsset;
private J_HeatingPreferences heatingPreferences;

private J_EAStorageHeat hotWaterBuffer;
private List<J_EAProduction> ptAssets;
private boolean hasPT = false;
private boolean hasHotWaterBuffer = false;

// PI control gains
private double P_gain_kWpDegC = 1*1;
private double I_gain_kWphDegC = 0.1*2;
Expand Down Expand Up @@ -61,11 +65,26 @@ public void manageHeating() {
}

double hotWaterDemand_kW = gc.p_DHWAsset != null ? gc.p_DHWAsset.getLastFlows().get(OL_EnergyCarriers.HEAT) : 0;
double ptAssetPower_kW = ptAssets != null ? sum(ptAssets, pt -> pt.getLastFlows().get(OL_EnergyCarriers.HEAT)) : 0;
double additionalHeatDemand_kW = (gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.HEAT) - hotWaterDemand_kW + (-ptAssetPower_kW));

double currentHeatDemand_kW = additionalHeatDemand_kW;
double availableAssetPowerForHotWater_kWth = heatingAsset.getOutputCapacity_kW() - additionalHeatDemand_kW;

//Adjust the hot water and overall heat demand with the buffer and pt
double remainingHotWaterDemand_kW = managePTAndHotWaterHeatBuffer(hotWaterDemand_kW);
//Manage hot water if additional systems are present
if(this.hasPT) {
//Adjust the hot water and overall heat demand with the buffer and pt
double remainingHotWaterDemand_kW = J_HeatingFunctionLibrary.managePTAndHotWaterHeatBuffer(hotWaterBuffer, ptAssets, hotWaterDemand_kW); // This function updates the buffer and curtails PT if needed -> current balanceflow is updated accordingly.
currentHeatDemand_kW += remainingHotWaterDemand_kW;
}
else if(this.hasHotWaterBuffer) {
double heatDemandFromHeatingAssetForHotWater_kW = J_HeatingFunctionLibrary.manageHotWaterHeatBuffer(this.hotWaterBuffer, hotWaterDemand_kW, availableAssetPowerForHotWater_kWth, this.timeStep_h);
currentHeatDemand_kW += heatDemandFromHeatingAssetForHotWater_kW;
}
else {
currentHeatDemand_kW += hotWaterDemand_kW;
}

double otherHeatDemand_kW = gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.HEAT);

double buildingTemp_degC = building.getCurrentTemperature();
double timeOfDay_h = gc.energyModel.t_hourOfDay;
Expand All @@ -86,73 +105,33 @@ public void manageHeating() {
buildingHeatingDemand_kW = max(0,deltaT_degC * P_gain_kWpDegC + I_state_hDegC * I_gain_kWphDegC);


double assetPower_kW = min(heatingAsset.getOutputCapacity_kW(),buildingHeatingDemand_kW + otherHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset.
double assetPower_kW = min(heatingAsset.getOutputCapacity_kW(),buildingHeatingDemand_kW + currentHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset.
heatingAsset.f_updateAllFlows( assetPower_kW / heatingAsset.getOutputCapacity_kW() );

double heatIntoBuilding_kW = max(0, assetPower_kW - otherHeatDemand_kW);
double heatIntoBuilding_kW = max(0, assetPower_kW - currentHeatDemand_kW);
building.f_updateAllFlows( heatIntoBuilding_kW / building.getCapacityHeat_kW() );

}

private double managePTAndHotWaterHeatBuffer(double hotWaterDemand_kW){

//Calculate the pt production
double ptProduction_kW = 0;
List<J_EAProduction> ptAssets = findAll(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
for (J_EA j_ea : ptAssets) {
ptProduction_kW -= j_ea.getLastFlows().get(OL_EnergyCarriers.HEAT);
}

//Calculate the remaining hot water energy need after pt production, also calculate the remaining unused pt production
double remainingHotWater_kW = max(0, hotWaterDemand_kW - ptProduction_kW); // Need to do this, because pt has already compensated the hot water demand in the gc flows, so just need to update this value
double remainingPTProduction_kW = max(0, ptProduction_kW - hotWaterDemand_kW);

if(gc.p_heatBuffer != null){
double chargeSetpoint_kW = 0;
if(remainingHotWater_kW > 0) {
chargeSetpoint_kW = -remainingHotWater_kW;
}
else if(remainingPTProduction_kW > 0) {
chargeSetpoint_kW = remainingPTProduction_kW;
}
gc.p_heatBuffer.v_powerFraction_fr = chargeSetpoint_kW / gc.p_heatBuffer.getCapacityHeat_kW();
gc.p_heatBuffer.f_updateAllFlows(gc.p_heatBuffer.v_powerFraction_fr);

double heatBufferCharge_kW = gc.p_heatBuffer.getLastFlows().get(OL_EnergyCarriers.HEAT);

if(remainingHotWater_kW > 0){//Only if the current pt production, wasnt enough, adjust the hotwater demand with the buffer, cause then the buffer will have tried to discharge
remainingHotWater_kW = max(0, remainingHotWater_kW + heatBufferCharge_kW);
}
else {//Curtail the remaining pt that is not used for hot water
remainingPTProduction_kW = max(0, remainingPTProduction_kW - heatBufferCharge_kW);
if (remainingPTProduction_kW > 0) {//Heat (for now always curtail over produced heat!)
for (J_EAProduction j_ea : ptAssets) {
remainingPTProduction_kW -= j_ea.curtailEnergyCarrierProduction( OL_EnergyCarriers.HEAT, remainingPTProduction_kW);

if (remainingPTProduction_kW <= 0) {
break;
}
}
}
}
}
return remainingHotWater_kW;
}

public void initializeAssets() {
if (!validHeatingTypes.contains(this.currentHeatingType)) {
throw new RuntimeException(this.getClass() + " does not support heating type: " + this.currentHeatingType);
}
J_EAProduction ptAsset = findFirst(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
if (ptAsset != null) {
List<J_EAProduction> ptAssets = findAll(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
if (ptAssets.size() > 0) {
if(gc.p_DHWAsset == null) {
throw new RuntimeException(this.getClass() + " requires a hot water demand to make sense to use this heating management with PT.");
}
this.ptAssets = ptAssets;
this.hasPT = true;
}
if (gc.p_heatBuffer != null) {
if(gc.p_DHWAsset == null && ptAsset == null) {
throw new RuntimeException(this.getClass() + " requires a hot water demand and PT to make sense to use this heating management with a heatbuffer.");
if(gc.p_DHWAsset == null) {
throw new RuntimeException(this.getClass() + " requires a hot water demand to make sense to use this heating management with heatbuffer.");
}
this.hotWaterBuffer = gc.p_heatBuffer;
this.hasHotWaterBuffer = true;
}
if(gc.p_BuildingThermalAsset != null) {
this.building = gc.p_BuildingThermalAsset;
Expand Down
Loading