Skip to content

Commit 44f8e4d

Browse files
authored
Merge pull request #207 from Zenmo/UpdateHeatpumpCOP
Update heatpump cop and added PI control heating management, working with all single-heating assets.
2 parents e5c4bbc + 37f040f commit 44f8e4d

6 files changed

+260
-33
lines changed

Zero_engine.alpx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,11 @@
17191719
<Name><![CDATA[J_ChargingManagementOffPeak]]></Name>
17201720
<Folder>1755154772688</Folder>
17211721
</JavaClass>
1722+
<JavaClass>
1723+
<Id>1760367525817</Id>
1724+
<Name><![CDATA[J_HeatingManagementPIcontrol]]></Name>
1725+
<Folder>1753194088788</Folder>
1726+
</JavaClass>
17221727
</JavaClasses>
17231728
<RequiredLibraryReference>
17241729
<LibraryName>com.anylogic.libraries.modules.markup_descriptors</LibraryName>

_alp/Classes/Class.J_AVGC_data.java

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,46 @@ public class J_AVGC_data implements Serializable {
2626
public double p_avgUtilityPVPower_kWp;
2727
public double p_ratioElectricTrucks;
2828
public OL_GridConnectionHeatingType p_avgCompanyHeatingMethod;
29-
public double p_avgEVEnergyConsumptionCar_kWhpkm;
30-
public double p_avgEVEnergyConsumptionVan_kWhpkm;
31-
public double p_avgEVEnergyConsumptionTruck_kWhpkm;
29+
3230
public double p_hydrogenEnergyDensity_kWh_Nm3;
33-
public double p_avgDieselConsumptionCar_kmpl;
34-
public double p_avgGasolineConsumptionCar_kmpl;
3531
public double p_hydrogenSpecificEnergy_kWh_kg;
3632
public double p_hydrogenDensity_kg_Nm3;
3733
public double p_oxygenDensity_kg_Nm3;
38-
public double p_avgCOPHeatpump;
39-
public double p_avgEfficiencyHeatpump;
40-
public double p_avgDieselConsumptionCar_kWhpkm;
4134
public double p_oxygenProduction_kgO2pkgH2;
35+
36+
public double p_avgEVEnergyConsumptionCar_kWhpkm;
37+
public double p_avgEVEnergyConsumptionVan_kWhpkm;
38+
public double p_avgEVEnergyConsumptionTruck_kWhpkm;
39+
public double p_avgDieselConsumptionCar_kmpl;
40+
public double p_avgGasolineConsumptionCar_kmpl;
41+
public double p_avgDieselConsumptionCar_kWhpkm;
4242
public double p_avgGasolineConsumptionCar_kWhpkm;
4343
public double p_avgDieselConsumptionVan_kmpl;
4444
public double p_avgDieselConsumptionVan_kWhpkm;
4545
public double p_avgDieselConsumptionTruck_kmpl;
4646
public double p_avgDieselConsumptionTruck_kWhpkm;
47-
public double p_avgOutputTemperatureHeatpump_degC;
4847
public double p_avgHydrogenConsumptionCar_kWhpkm;
49-
public double p_avgEfficiencyGasBurner;
5048
public double p_avgHydrogenConsumptionVan_kWhpkm;
5149
public double p_avgHydrogenConsumptionTruck_kWhpkm;
50+
51+
public double p_avgCOPHeatpump;
52+
public double p_avgEfficiencyHeatpump_fr;
53+
public double p_avgOutputTemperatureHeatpump_degC;
54+
public double p_minHeatpumpElectricCapacity_kW;
55+
56+
public double p_avgEfficiencyGasBurner_fr;
5257
public double p_avgOutputTemperatureGasBurner_degC;
53-
public double p_avgEfficiencyHydrogenBurner;
58+
public double p_minGasBurnerOutputCapacity_kW;
59+
60+
public double p_avgEfficiencyHydrogenBurner_fr;
5461
public double p_avgOutputTemperatureHydrogenBurner_degC;
62+
public double p_minHydrogenBurnerOutputCapacity_kW;
63+
64+
public double p_avgEfficiencyDistrictHeatingDeliverySet_fr;
65+
public double p_avgOutputTemperatureDistrictHeatingDeliverySet_degC;
66+
public double p_minDistrictHeatingDeliverySetOutputCapacity_kW;
67+
68+
5569
public double p_avgPVPower_kWpm2;
5670
public double p_avgAnnualProductionPV_kWhpWp;
5771
public double p_avgRatioRoofPotentialPV;
@@ -61,8 +75,7 @@ public class J_AVGC_data implements Serializable {
6175
public double p_avgEfficiencyCHP_thermal_fr;
6276
public double p_avgEfficiencyCHP_electric_fr;
6377
public double p_avgOutputTemperatureCHP_degC;
64-
public double p_avgEfficiencyDistrictHeatingDeliverySet_fr;
65-
public double p_avgOutputTemperatureDistrictHeatingDeliverySet_degC;
78+
6679
public double p_v1gProbability;
6780
public double p_v2gProbability;
6881
public int p_avgEVsPerPublicCharger;

_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private double[] getNettoBalanceForecast_kW() {
113113
if(surveyHeatDemandProfile != null){
114114
double[] heatPower_kW = ZeroMath.arrayMultiply(Arrays.copyOfRange(surveyHeatDemandProfile.a_energyProfile_kWh, startTimeDayIndex, endTimeDayIndex), surveyHeatDemandProfile.getProfileScaling_fr()/p_timestep_h);
115115
//traceln(heatPower_kW);
116-
double eta_r = parentGC.energyModel.avgc_data.p_avgEfficiencyHeatpump;
116+
double eta_r = parentGC.energyModel.avgc_data.p_avgEfficiencyHeatpump_fr;
117117
double outputTemperature_degC = parentGC.energyModel.avgc_data.p_avgOutputTemperatureHeatpump_degC;
118118
for(double time = energyModel_time_h; time < energyModel_time_h + 24; time += p_timestep_h){
119119
double baseTemperature_degC = parentGC.energyModel.pp_ambientTemperature_degC.getValue(time);
@@ -125,7 +125,7 @@ private double[] getNettoBalanceForecast_kW() {
125125
for(J_EAConsumption genericHeatDemandProfile : genericHeatDemandProfiles) {
126126
if(genericHeatDemandProfile != null){
127127

128-
double eta_r = parentGC.energyModel.avgc_data.p_avgEfficiencyHeatpump;
128+
double eta_r = parentGC.energyModel.avgc_data.p_avgEfficiencyHeatpump_fr;
129129
double outputTemperature_degC = parentGC.energyModel.avgc_data.p_avgOutputTemperatureHeatpump_degC;
130130

131131
for(double time = energyModel_time_h; time < energyModel_time_h + 24; time += p_timestep_h){

_alp/Classes/Class.J_EAConversionHeatPump.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ public J_EAConversionHeatPump(Agent parentAgent, double inputElectricCapacity_kW
2929
this.timestep_h = timestep_h;
3030
this.eta_r = eta_r;
3131
this.outputTemperature_degC = outputTemperature_degC;
32-
this.COP_r = eta_r * ( 273.15 + outputTemperature_degC ) / ( outputTemperature_degC - baseTemperature_degC );
32+
3333
this.ambientTempType = ambientTempType;
34-
3534
this.updateAmbientTemperature( this.baseTemperature_degC );
35+
36+
//this.COP_r = eta_r * ( 273.15 + outputTemperature_degC ) / ( outputTemperature_degC - baseTemperature_degC );
37+
traceln("Carnot-based Heatpump COP with parameter eta_r is no longer used! Replaced by empirical COP-curve.");
38+
this.COP_r = calculateCOP(this.outputTemperature_degC, this.baseTemperature_degC);
3639

3740
this.sourceAssetHeatPower_kW = sourceAssetHeatPower_kW;
3841
this.belowZeroHeatpumpEtaReductionFactor = belowZeroHeatpumpEtaReductionFactor;
@@ -52,7 +55,8 @@ public void updateParameters(double baseTemperature_degC, double outputTemperatu
5255
if ( this.baseTemperature_degC > this.outputTemperature_degC) {
5356
traceln("**** EXCEPTION **** Heatpump baseTemperature ( " + this.baseTemperature_degC + ") > outputTemperature ( " + this.outputTemperature_degC + ") ");
5457
}
55-
this.COP_r = this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC );
58+
//this.COP_r = this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC );
59+
this.COP_r = calculateCOP(this.outputTemperature_degC, this.baseTemperature_degC); //8.74 - 0.190 * deltaT + 0.00126 * deltaT * deltaT;
5660

5761
// water heatpump should take sourceAsset power transfer limitations into account (e.g. residual heat). Ugly but effectively limiting heat power output.
5862

@@ -76,7 +80,7 @@ public void updateAmbientTemperature(double baseTemperature_degC) {
7680

7781
//traceln("J_EAHeatpump capacityHeat_kW = " + this.capacityHeat_kW + ", baseTemperature = "+ baseTemperature_degC + ", outputtemperature = "+ outputTemperature_degC);
7882
updateParameters(baseTemperature_degC, this.outputTemperature_degC);
79-
this.COP_r = this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC );
83+
this.COP_r = calculateCOP(this.outputTemperature_degC, this.baseTemperature_degC); //this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC );
8084
this.outputCapacity_kW = this.inputCapacity_kW * this.COP_r;
8185
}
8286

@@ -138,17 +142,22 @@ public void setBaseTemperature_degC( double baseTemperature_degC) {
138142
this.updateParameters( this.baseTemperature_degC, this.outputTemperature_degC);
139143
}
140144

141-
@Override
145+
/*@Override
142146
public void setEta_r( double efficiency_r) {
143147
this.eta_r = efficiency_r;
144148
this.COP_r = this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC );
145149
this.outputCapacity_kW = this.inputCapacity_kW * this.COP_r;
146-
}
150+
}*/
147151

148152
public OL_AmbientTempType getAmbientTempType() {
149153
return this.ambientTempType;
150154
}
151155

156+
private double calculateCOP(double outputTemperature_degC, double baseTemperature_degC) {
157+
double deltaT = max(1,this.outputTemperature_degC - this.baseTemperature_degC); // Limit deltaT to at least 1 degree.
158+
double COP_r = 8.74 - 0.190 * deltaT + 0.00126 * deltaT*deltaT;
159+
return COP_r;
160+
}
152161
/*
153162
@Override
154163
public String toString() {
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
2+
/**
3+
* J_HeatingManagementPIcontrol
4+
*/
5+
6+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
7+
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
8+
9+
@JsonAutoDetect(
10+
fieldVisibility = Visibility.ANY, //
11+
getterVisibility = Visibility.NONE,
12+
isGetterVisibility = Visibility.NONE,
13+
setterVisibility = Visibility.NONE,
14+
creatorVisibility = Visibility.NONE
15+
)
16+
17+
public class J_HeatingManagementPIcontrol implements I_HeatingManagement {
18+
private boolean isInitialized = false;
19+
private GridConnection gc;
20+
private List<OL_GridConnectionHeatingType> validHeatingTypes = Arrays.asList(
21+
OL_GridConnectionHeatingType.GAS_BURNER,
22+
OL_GridConnectionHeatingType.ELECTRIC_HEATPUMP,
23+
OL_GridConnectionHeatingType.HYDROGENBURNER,
24+
OL_GridConnectionHeatingType.DISTRICTHEAT,
25+
OL_GridConnectionHeatingType.LT_DISTRICTHEAT
26+
);
27+
private OL_GridConnectionHeatingType currentHeatingType;
28+
29+
private J_EABuilding building;
30+
private J_EAConversion heatingAsset;
31+
32+
public double startOfDay_h = 8;
33+
public double startOfNight_h = 23;
34+
public double dayTimeSetPoint_degC = 19;
35+
public double nightTimeSetPoint_degC = 19;
36+
public double heatingKickinTreshhold_degC = 0;// -> If not 0, need to create better management / system definition, else on/off/on/off behaviour.
37+
38+
// PI control gains
39+
private double P_gain_kWpDegC = 1;
40+
private double I_gain_kWphDegC = 0.1;
41+
private double I_state_hDegC = 0;
42+
private double timeStep_h;
43+
/**
44+
* Default constructor
45+
*/
46+
public J_HeatingManagementPIcontrol() {
47+
}
48+
49+
public J_HeatingManagementPIcontrol( GridConnection gc,OL_GridConnectionHeatingType heatingType ) {
50+
this.gc = gc;
51+
this.currentHeatingType = heatingType;
52+
this.timeStep_h = gc.energyModel.p_timeStep_h;
53+
}
54+
55+
public void manageHeating() {
56+
if ( !isInitialized ) {
57+
this.initializeAssets();
58+
}
59+
60+
double hotWaterDemand_kW = gc.p_DHWAsset != null ? gc.p_DHWAsset.getLastFlows().get(OL_EnergyCarriers.HEAT) : 0;
61+
62+
//Adjust the hot water and overall heat demand with the buffer and pt
63+
double remainingHotWaterDemand_kW = managePTAndHotWaterHeatBuffer(hotWaterDemand_kW);
64+
65+
double otherHeatDemand_kW = gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.HEAT);
66+
67+
double buildingTemp_degC = building.getCurrentTemperature();
68+
double timeOfDay_h = gc.energyModel.t_hourOfDay;
69+
double buildingHeatingDemand_kW = 0;
70+
71+
double currentSetpoint_degC = dayTimeSetPoint_degC;
72+
if (timeOfDay_h < startOfDay_h || timeOfDay_h >= startOfNight_h) {
73+
currentSetpoint_degC = nightTimeSetPoint_degC;
74+
}
75+
76+
double deltaT_degC = currentSetpoint_degC - building.getCurrentTemperature(); // Positive deltaT when heating is needed
77+
//I_state_kW += deltaT_degC * I_gain_kWphDeg * timeStep_h);
78+
I_state_hDegC = max(0,I_state_hDegC + deltaT_degC * timeStep_h); // max(0,...) to prevent buildup of negative integrator during warm periods.
79+
buildingHeatingDemand_kW = max(0,deltaT_degC * P_gain_kWpDegC + I_state_hDegC * I_gain_kWphDegC);
80+
81+
//traceln("PI control for heating: deltaT: %s, proportional feedback: %s kW, integral feedback: %s kW", deltaT_degC, deltaT_degC * P_gain_kWpDeg, I_state_kW);
82+
83+
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.
84+
heatingAsset.f_updateAllFlows( assetPower_kW / heatingAsset.getOutputCapacity_kW() );
85+
86+
double heatIntoBuilding_kW = max(0, assetPower_kW - otherHeatDemand_kW);
87+
building.f_updateAllFlows( heatIntoBuilding_kW / building.getCapacityHeat_kW() );
88+
89+
}
90+
91+
public double managePTAndHotWaterHeatBuffer(double hotWaterDemand_kW){
92+
93+
//Calculate the pt production
94+
double ptProduction_kW = 0;
95+
List<J_EAProduction> ptAssets = findAll(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
96+
for (J_EA j_ea : ptAssets) {
97+
ptProduction_kW -= j_ea.getLastFlows().get(OL_EnergyCarriers.HEAT);
98+
}
99+
100+
//Calculate the remaining hot water energy need after pt production, also calculate the remaining unused pt production
101+
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
102+
double remainingPTProduction_kW = max(0, ptProduction_kW - hotWaterDemand_kW);
103+
104+
if(gc.p_heatBuffer != null){
105+
double chargeSetpoint_kW = 0;
106+
if(remainingHotWater_kW > 0) {
107+
chargeSetpoint_kW = -remainingHotWater_kW;
108+
}
109+
else if(remainingPTProduction_kW > 0) {
110+
chargeSetpoint_kW = remainingPTProduction_kW;
111+
}
112+
gc.p_heatBuffer.v_powerFraction_fr = chargeSetpoint_kW / gc.p_heatBuffer.getCapacityHeat_kW();
113+
gc.p_heatBuffer.f_updateAllFlows(gc.p_heatBuffer.v_powerFraction_fr);
114+
115+
double heatBufferCharge_kW = gc.p_heatBuffer.getLastFlows().get(OL_EnergyCarriers.HEAT);
116+
117+
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
118+
remainingHotWater_kW = max(0, remainingHotWater_kW + heatBufferCharge_kW);
119+
}
120+
else {//Curtail the remaining pt that is not used for hot water
121+
remainingPTProduction_kW = max(0, remainingPTProduction_kW - heatBufferCharge_kW);
122+
if (remainingPTProduction_kW > 0) {//Heat (for now always curtail over produced heat!)
123+
for (J_EAProduction j_ea : ptAssets) {
124+
remainingPTProduction_kW -= j_ea.curtailEnergyCarrierProduction( OL_EnergyCarriers.HEAT, remainingPTProduction_kW);
125+
126+
if (remainingPTProduction_kW <= 0) {
127+
break;
128+
}
129+
}
130+
}
131+
}
132+
}
133+
return remainingHotWater_kW;
134+
}
135+
136+
public void initializeAssets() {
137+
if (!validHeatingTypes.contains(this.currentHeatingType)) {
138+
throw new RuntimeException(this.getClass() + " does not support heating type: " + this.currentHeatingType);
139+
}
140+
J_EAProduction ptAsset = findFirst(gc.c_productionAssets, ea -> ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL);
141+
if (ptAsset != null) {
142+
if(gc.p_DHWAsset == null) {
143+
throw new RuntimeException(this.getClass() + " requires a hot water demand to make sense to use this heating management with PT.");
144+
}
145+
}
146+
if (gc.p_heatBuffer != null) {
147+
if(gc.p_DHWAsset == null && ptAsset == null) {
148+
throw new RuntimeException(this.getClass() + " requires a hot water demand and PT to make sense to use this heating management with a heatbuffer.");
149+
}
150+
}
151+
if(gc.p_BuildingThermalAsset != null) {
152+
this.building = gc.p_BuildingThermalAsset;
153+
} else {
154+
throw new RuntimeException(this.getClass() + " can only be used for temperature control of a building thermal asset.");
155+
}
156+
if (gc.c_heatingAssets.size() == 0) {
157+
throw new RuntimeException(this.getClass() + " requires at least one heating asset.");
158+
}
159+
if (gc.c_heatingAssets.size() > 1) {
160+
throw new RuntimeException(this.getClass() + " does not support more than one heating asset.");
161+
}
162+
this.heatingAsset = gc.c_heatingAssets.get(0);
163+
if (heatingAsset instanceof J_EAConversionGasBurner) {
164+
this.currentHeatingType = OL_GridConnectionHeatingType.GAS_BURNER;
165+
} else if (heatingAsset instanceof J_EAConversionHeatPump) {
166+
if (gc.p_parentNodeHeatID != null) {
167+
this.currentHeatingType = OL_GridConnectionHeatingType.LT_DISTRICTHEAT;
168+
} else {
169+
this.currentHeatingType = OL_GridConnectionHeatingType.ELECTRIC_HEATPUMP;
170+
}
171+
} else if (heatingAsset instanceof J_EAConversionHeatDeliverySet) {
172+
this.currentHeatingType = OL_GridConnectionHeatingType.DISTRICTHEAT;
173+
} else if (heatingAsset instanceof J_EAConversionHydrogenBurner) {
174+
this.currentHeatingType = OL_GridConnectionHeatingType.HYDROGENBURNER;
175+
} else {
176+
throw new RuntimeException(this.getClass() + " Unsupported heating asset!");
177+
}
178+
179+
this.isInitialized = true;
180+
}
181+
182+
public void notInitialized() {
183+
this.isInitialized = false;
184+
}
185+
186+
public List<OL_GridConnectionHeatingType> getValidHeatingTypes() {
187+
return this.validHeatingTypes;
188+
}
189+
190+
public OL_GridConnectionHeatingType getCurrentHeatingType() {
191+
return this.currentHeatingType;
192+
}
193+
194+
@Override
195+
public String toString() {
196+
return super.toString();
197+
}
198+
199+
}

0 commit comments

Comments
 (0)