Skip to content

Commit bc29da6

Browse files
authored
Merge pull request #384 from wouterpeere/issue381-change-the-way-dhw-is-handled-in-hourly-load
First try
2 parents c6d8c2b + 63629c0 commit bc29da6

15 files changed

+1418
-91
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1212
- Changed implementation of `optimise_for_energy` to make it three times faster (issue #308).
1313
- Add support for EPW files from PVGIS (issue #376).
1414
- Fix problem with start month in optimise for power/balance (issue #380).
15-
- Return multiyear external load if multiyear load is given as an imput (issue #380).
15+
- Return multiyear external load if multiyear load is given as an input (issue #380).
16+
- Change how DHW is stored in the classes (issue #381).
1617

1718
### Fixed
1819

1920
- Problem in ConicalPipe class when working with vfr (issue #378).
21+
- Problem with start month DHW and optimisation (issue #378).
2022

2123
## [2.3.4] - 2025-07-29
2224

GHEtool/Examples/optimise_load_profile.py

Lines changed: 953 additions & 4 deletions
Large diffs are not rendered by default.

GHEtool/Methods/optimise_load_profile.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def optimise_load_profile_power(
9898
np.minimum(peak_heat_load, building_load._hourly_heating_load
9999
if isinstance(borefield.load, HourlyBuildingLoad) else building_load.hourly_heating_load_simulation_period))
100100
borefield.load.set_hourly_dhw_load(
101-
np.minimum(peak_dhw_load, building_load.hourly_dhw_load
101+
np.minimum(peak_dhw_load, building_load._hourly_dhw_load
102102
if isinstance(borefield.load, HourlyBuildingLoad) else building_load.hourly_dhw_load_simulation_period))
103103

104104
# calculate temperature profile, just for the results
@@ -154,7 +154,7 @@ def optimise_load_profile_power(
154154
external_load.set_hourly_cooling_load(
155155
np.maximum(0, building_load._hourly_cooling_load - borefield.load._hourly_cooling_load))
156156
external_load.set_hourly_dhw_load(
157-
np.maximum(0, building_load.hourly_dhw_load - borefield.load.hourly_dhw_load))
157+
np.maximum(0, building_load._hourly_dhw_load - borefield.load._hourly_dhw_load))
158158

159159
return borefield.load, external_load
160160

@@ -559,7 +559,7 @@ def optimise_load_profile_balance(
559559
np.minimum(peak_heat_load, building_load._hourly_heating_load
560560
if isinstance(borefield.load, HourlyBuildingLoad) else building_load.hourly_heating_load_simulation_period))
561561
borefield.load.set_hourly_dhw_load(
562-
np.minimum(peak_dhw_load, building_load.hourly_dhw_load
562+
np.minimum(peak_dhw_load, building_load._hourly_dhw_load
563563
if isinstance(borefield.load, HourlyBuildingLoad) else building_load.hourly_dhw_load_simulation_period))
564564

565565
# calculate temperature profile, just for the results
@@ -568,7 +568,6 @@ def optimise_load_profile_balance(
568568
# calculate relative imbalance
569569
imbalance = borefield.load.imbalance / np.maximum(borefield.load.yearly_average_injection_load,
570570
borefield.load.yearly_average_extraction_load)
571-
572571
# deviation from minimum temperature
573572
if abs(min(borefield.results.peak_extraction) - borefield.Tf_min) > temperature_threshold or \
574573
(abs(imbalance) > imbalance_factor and imbalance < 0):
@@ -642,7 +641,7 @@ def optimise_load_profile_balance(
642641
external_load.set_hourly_cooling_load(
643642
np.maximum(0, building_load._hourly_cooling_load - borefield.load._hourly_cooling_load))
644643
external_load.set_hourly_dhw_load(
645-
np.maximum(0, building_load.hourly_dhw_load - borefield.load.hourly_dhw_load))
644+
np.maximum(0, building_load._hourly_dhw_load - borefield.load._hourly_dhw_load))
646645

647646
return borefield.load, external_load
648647

GHEtool/VariableClasses/LoadData/Baseclasses/_HourlyDataBuilding.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,24 @@ def __init__(self,
3535
multiyear : bool
3636
True if multiyear data
3737
"""
38-
_LoadDataBuilding.__init__(self, efficiency_heating, efficiency_cooling, dhw, efficiency_dhw, multiyear)
38+
_LoadDataBuilding.__init__(self, efficiency_heating, efficiency_cooling, efficiency_dhw, multiyear)
3939
_HourlyData.__init__(self)
4040

41+
# overwritten by HourlyData
42+
self._multiyear = multiyear
43+
4144
# initiate variables
4245
self._hourly_heating_load: np.ndarray = np.zeros(8760)
4346
self._hourly_cooling_load: np.ndarray = np.zeros(8760)
47+
self._hourly_dhw_load: np.ndarray = None
48+
self._set_dhw(dhw)
4449

4550
# delete unnecessary variables
4651
del self._peak_cooling
4752
del self._peak_heating
4853
del self._baseload_cooling
4954
del self._baseload_heating
55+
del self._baseload_dhw
5056

5157
@abc.abstractmethod
5258
def hourly_cooling_load_simulation_period(self) -> np.ndarray:
@@ -117,6 +123,40 @@ def hourly_dhw_load(self) -> np.ndarray:
117123
"""
118124
return np.mean(self.hourly_dhw_load_simulation_period.reshape((self.simulation_period, 8760)), axis=0)
119125

126+
def _set_dhw(self, dhw: Union[float, np.ndarray]) -> None:
127+
"""
128+
This function sets the dhw.
129+
130+
Parameters
131+
----------
132+
dhw : float, np.ndarray
133+
Yearly value of array with energy demand for domestic hot water (DHW) [kWh]
134+
135+
Returns
136+
-------
137+
None
138+
139+
Raises
140+
------
141+
ValueError
142+
When a yearly value is given or values are negative.
143+
"""
144+
if dhw is None:
145+
return
146+
if self._multiyear:
147+
if isinstance(dhw, (float, int)):
148+
raise ValueError('When using a multi year data input, it is not allowed to enter a yearly DHW demand.')
149+
self.hourly_dhw_load = dhw
150+
return
151+
152+
# just hourly load
153+
if isinstance(dhw, (float, int)):
154+
if not dhw >= 0:
155+
raise ValueError(f'Please fill in a positive value for the domestic hot water instead of {dhw}.')
156+
self.hourly_dhw_load = np.full(8760, dhw / 8760)
157+
return
158+
self.hourly_dhw_load = dhw
159+
120160
def _get_hourly_cop(self, power: np.ndarray = None) -> Union[float, np.ndarray]:
121161
"""
122162
This function returns the hourly COP value for the given temperature result profile.
@@ -232,8 +272,6 @@ def hourly_extraction_load_simulation_period(self) -> np.ndarray:
232272
hourly extraction : np.ndarray
233273
Hourly extraction values [kWh/h] for the whole simulation period
234274
"""
235-
if isinstance(self.dhw, (int, float)) and self.dhw == 0.:
236-
return self._hourly_extraction_load_heating_simulation_period
237275
return self._hourly_extraction_load_heating_simulation_period + self._hourly_extraction_load_dhw_simulation_period
238276

239277
@property

GHEtool/VariableClasses/LoadData/Baseclasses/_LoadDataBuilding.py

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ class _LoadDataBuilding(_LoadData, ABC):
1717
def __init__(self,
1818
efficiency_heating: Union[int, float, COP, SCOP],
1919
efficiency_cooling: Union[int, float, EER, SEER, EERCombined],
20-
dhw: Union[float, np.ndarray] = None,
2120
efficiency_dhw: Union[int, float, COP, SCOP] = 4,
2221
multiyear: bool = False):
2322
"""
@@ -28,8 +27,6 @@ def __init__(self,
2827
Efficiency in heating
2928
efficiency_cooling : int, float, EER, SEER
3029
Efficiency in cooling
31-
dhw : float, np.ndarray
32-
Yearly value of array with energy demand for domestic hot water (DHW) [kWh]
3330
efficiency_dhw : int, float, COP, SCOP,
3431
Efficiency in DHW
3532
multiyear : bool
@@ -43,7 +40,7 @@ def __init__(self,
4340
self._baseload_cooling: np.ndarray = np.zeros(12)
4441
self._peak_heating: np.ndarray = np.zeros(12)
4542
self._peak_cooling: np.ndarray = np.zeros(12)
46-
self._dhw = None
43+
self._baseload_dhw: np.ndarray = np.zeros(12)
4744

4845
self._cop = None
4946
self._eer = None
@@ -57,15 +54,15 @@ def __init__(self,
5754
self.cop = efficiency_heating
5855
self.eer = efficiency_cooling
5956
self.cop_dhw = efficiency_dhw
60-
self.dhw = dhw
6157

6258
@abc.abstractmethod
6359
def monthly_baseload_heating_simulation_period(self) -> np.ndarray:
6460
"""
6561
This function returns the monthly heating baseload in kWh/month for the whole simulation period.
6662
6763
Returns
68-
------- baseload heating : np.ndarray
64+
-------
65+
baseload heating : np.ndarray
6966
Baseload heating for the whole simulation period
7067
"""
7168

@@ -142,8 +139,7 @@ def monthly_baseload_cooling(self) -> np.ndarray:
142139
-------
143140
monthly baseload cooling : np.ndarray
144141
"""
145-
return np.mean(self.monthly_baseload_cooling_simulation_period.reshape((self.simulation_period, 12)),
146-
axis=0)
142+
return np.mean(self.monthly_baseload_cooling_simulation_period.reshape((self.simulation_period, 12)), axis=0)
147143

148144
@property
149145
def monthly_baseload_heating(self) -> np.ndarray:
@@ -154,8 +150,7 @@ def monthly_baseload_heating(self) -> np.ndarray:
154150
-------
155151
monthly baseload heating : np.ndarray
156152
"""
157-
return np.mean(self.monthly_baseload_heating_simulation_period.reshape((self.simulation_period, 12)),
158-
axis=0)
153+
return np.mean(self.monthly_baseload_heating_simulation_period.reshape((self.simulation_period, 12)), axis=0)
159154

160155
@property
161156
def monthly_peak_cooling(self) -> np.ndarray:
@@ -166,8 +161,7 @@ def monthly_peak_cooling(self) -> np.ndarray:
166161
-------
167162
monthly peak cooling : np.ndarray
168163
"""
169-
return np.mean(self.monthly_peak_cooling_simulation_period.reshape((self.simulation_period, 12)),
170-
axis=0)
164+
return np.mean(self.monthly_peak_cooling_simulation_period.reshape((self.simulation_period, 12)), axis=0)
171165

172166
@property
173167
def monthly_peak_heating(self) -> np.ndarray:
@@ -178,8 +172,7 @@ def monthly_peak_heating(self) -> np.ndarray:
178172
-------
179173
monthly peak heating : np.ndarray
180174
"""
181-
return np.mean(self.monthly_peak_heating_simulation_period.reshape((self.simulation_period, 12)),
182-
axis=0)
175+
return np.mean(self.monthly_peak_heating_simulation_period.reshape((self.simulation_period, 12)), axis=0)
183176

184177
@property
185178
def monthly_baseload_cooling_power(self) -> np.ndarray:
@@ -522,7 +515,7 @@ def monthly_baseload_extraction_simulation_period(self) -> np.ndarray:
522515
baseload extraction : np.ndarray
523516
Baseload extraction for the whole simulation period
524517
"""
525-
if isinstance(self.dhw, (int, float)) and self.dhw == 0.:
518+
if not np.any(self.monthly_baseload_dhw):
526519
return self._monthly_baseload_extraction_heating_simulation_period
527520
return self._monthly_baseload_extraction_heating_simulation_period + self._monthly_baseload_extraction_dhw_simulation_period
528521

@@ -583,7 +576,7 @@ def monthly_peak_extraction_simulation_period(self) -> np.ndarray:
583576
peak extraction : np.ndarray
584577
Peak extraction for the whole simulation period
585578
"""
586-
if self.exclude_DHW_from_peak or (isinstance(self.dhw, (int, float)) and self.dhw == 0.):
579+
if self.exclude_DHW_from_peak:
587580
return self._monthly_peak_extraction_heating_simulation_period
588581
return self._monthly_peak_extraction_heating_simulation_period + self._monthly_peak_extraction_dhw_simulation_period
589582

@@ -740,6 +733,40 @@ def add_dhw(self, dhw: Union[float, np.ndarray]) -> None:
740733
"""
741734
self.dhw = dhw
742735

736+
def _set_dhw(self, dhw: Union[float, np.ndarray]) -> None:
737+
"""
738+
This function sets the dhw.
739+
740+
Parameters
741+
----------
742+
dhw : float, np.ndarray
743+
Yearly value of array with energy demand for domestic hot water (DHW) [kWh]
744+
745+
Returns
746+
-------
747+
None
748+
749+
Raises
750+
------
751+
ValueError
752+
When a yearly value is given or values are negative.
753+
"""
754+
if dhw is None:
755+
return
756+
if self._multiyear:
757+
if isinstance(dhw, (float, int)):
758+
raise ValueError('When using a multi year data input, it is not allowed to enter a yearly DHW demand.')
759+
self.baseload_dhw = dhw
760+
return
761+
762+
# just hourly load
763+
if isinstance(dhw, (float, int)):
764+
if not dhw >= 0:
765+
raise ValueError(f'Please fill in a positive value for the domestic hot water instead of {dhw}.')
766+
self.baseload_dhw = dhw * self.UPM / 8760.
767+
return
768+
self.baseload_dhw = dhw
769+
743770
@property
744771
def dhw(self) -> Union[float, np.ndarray]:
745772
"""
@@ -749,9 +776,9 @@ def dhw(self) -> Union[float, np.ndarray]:
749776
-------
750777
DHW object
751778
"""
752-
if self._dhw is None:
753-
return 0.
754-
return self._dhw
779+
if self._baseload_dhw is not None:
780+
return self._baseload_dhw
781+
return np.zeros(12 * self.simulation_period)
755782

756783
@dhw.setter
757784
def dhw(self, dhw: Union[float, np.ndarray]) -> None:
@@ -768,19 +795,7 @@ def dhw(self, dhw: Union[float, np.ndarray]) -> None:
768795
-------
769796
None
770797
"""
771-
if dhw is None:
772-
return
773-
if isinstance(dhw, (float, int)):
774-
if self._multiyear:
775-
raise ValueError('When using a multi year data input, it is not allowed to enter a yearly DHW demand.')
776-
if not dhw >= 0:
777-
raise ValueError(f'Please fill in a positive value for the domestic hot water instead of {dhw}.')
778-
self._dhw = dhw
779-
return
780-
if not self._check_input(dhw):
781-
raise ValueError('Wrong value for the DHW array. Please make sure the length matches that of the heating '
782-
'and cooling array.')
783-
self._dhw = dhw
798+
self._set_dhw(dhw)
784799

785800
@property
786801
def monthly_baseload_dhw_power_simulation_period(self) -> np.ndarray:

0 commit comments

Comments
 (0)