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