Verleyen et al. (2022)
The code below is used in the article of Verleyen et al. (2022) 1.
1"""
2This document contains all the scripts for the figures of Verleyen et al. (2022).
3One needs GHEtool version 2.0.6 to run this code.
4"""
5import math
6
7import matplotlib.pyplot as plt
8import numpy as np
9
10# code for making figures black-and-white ready
11from cycler import cycler
12
13from GHEtool import Borefield, GroundData
14
15color_c = cycler('color', ['k'])
16style_c = cycler('linestyle', ['-', '--', ':', '-.'])
17markr_c = cycler('marker', ['', '.', 'o'])
18c_cms = color_c * markr_c * style_c
19c_csm = color_c * style_c * markr_c
20plt.rc('axes', prop_cycle=c_cms)
21plt.rcParams['lines.markersize'] = 8
22
23
24def figure_1():
25 """
26 This function generates the first figure (part a/b) of the article.
27 """
28
29 # initiate ground data
30 ground_data = GroundData(2.4, 10, 0.12)
31
32 # initiate borefield model
33 borefield = Borefield()
34 borefield.set_ground_parameters(ground_data)
35 borefield.create_rectangular_borefield(10, 10, 6, 6, 100, 4)
36
37 # initiate depth array
38 depth = 150
39
40 # dimensionless time
41 ts = 150**2 / (9 * ground_data.k_s)
42
43 # time array
44 nb_of_timesteps = 50
45 time_dimensionless = np.linspace(2, 14, nb_of_timesteps)
46
47 # convert to seconds
48 time_in_seconds = np.exp(time_dimensionless) * ts
49
50 # calculate g-functions
51 result = np.zeros(nb_of_timesteps)
52 result = borefield.gfunction(time_in_seconds, depth)
53
54 # create figure
55 fig, axs = plt.subplots(1, 2, figsize=(10, 3), constrained_layout=True)
56
57 # create figure g-function (lin)
58 # plot g-functions
59 axs[0].plot(time_in_seconds/8760/3600, result / (2 * math.pi * 2.4))
60
61 # layout
62 axs[0].set_title("Step response when applying a constant heat injection")
63 axs[0].set_xlabel("Time (years)")
64 axs[0].set_ylabel("Temperature difference (K)")
65 axs[0].set_ylim(0, 5)
66 axs[0].set_xlim(0, 40)
67
68 # create figure g-function (semi-log)
69 # plot g-functions
70 axs[1].plot(time_dimensionless, result)
71
72 # layout
73 axs[1].set_title("Equivalent g-function for the step response")
74 axs[1].set_xlabel("ln(t/ts)")
75 axs[1].set_ylabel("g-function value")
76 axs[1].set_ylim(-2, 60)
77
78 # plt.legend()
79 plt.show()
80
81
82def figure_2():
83 """
84 This function generates the second figure of the article.
85 """
86
87 # initiate ground data
88 ground_data = GroundData(2.4, 10, 0.12)
89
90 # initiate borefield model
91 borefield = Borefield()
92 borefield.set_ground_parameters(ground_data)
93 borefield.create_rectangular_borefield(10, 10, 6, 6, 100, 4)
94
95 # initiate depth array
96 depths = np.array([25, 50, 100, 150, 200])
97
98 # dimensionless time
99 ts = 150**2 / (9 * ground_data.k_s)
100
101 # time array
102 nb_of_timesteps = 50
103 time_dimensionless = np.linspace(2, 14, nb_of_timesteps)
104
105 # convert to seconds
106 time_in_seconds = np.exp(time_dimensionless) * ts
107
108 # calculate g-functions
109 results = np.zeros((5, nb_of_timesteps))
110 for i in range(5):
111 results[i] = borefield.gfunction(time_in_seconds, depths[i])
112
113 # create figure
114 plt.figure()
115
116 # plot g-functions
117 plt.plot(time_dimensionless, results[0], label="25m")
118 plt.plot(time_dimensionless, results[1], label="50m")
119 plt.plot(time_dimensionless, results[2], label="100m")
120 plt.plot(time_dimensionless, results[3], label="150m")
121 plt.plot(time_dimensionless, results[4], label="200m")
122
123 # plot lines for Ra
124 line1 = math.log(6*3600/ts)
125 line2 = math.log((20 * 8760 + 730) * 3600 / ts)
126 plt.vlines(line1, ymin=-5, ymax=5, colors="black", lw=0.75, ls="--")
127 plt.vlines(line2, ymin=-5, ymax=50, colors="black", lw=0.75, ls="--")
128
129 plt.annotate('', xy=(line1, -0.4), xytext=(line2, -0.4),
130 arrowprops=dict(arrowstyle='<->', color='black'))
131 plt.text((line1+line2)/2, 0.2, "Ra", horizontalalignment='center')
132
133 # layout
134 # plt.title("G-function values for different borefield depths")
135 plt.xlabel("ln(t/ts)")
136 plt.ylabel("g-function value")
137 plt.ylim(-2, 60)
138
139 plt.legend()
140 plt.show()
141
142
143def figure_3():
144 """
145 This function creates the third figure of the article.
146 """
147
148 # initiate borefield model
149 borefield = Borefield()
150
151 # initiate depth for evaluations
152 nb_depths = 20
153 depth_array = np.linspace(50, 350, nb_depths)
154
155 # initiate list of borefield configurations
156 configs = [(10, 10), (11, 11), (12, 12), (14, 14),
157 (15, 15), (18, 18), (20, 20)]
158
159 results = []
160
161 for n1, n2 in configs:
162 depths = []
163 for H in depth_array:
164 # set ground data
165 ground_data = GroundData(2.4, 10, 0.12)
166
167 # set ground data
168 borefield.set_ground_parameters(ground_data)
169
170 # set borefield
171 borefield.create_rectangular_borefield(n1, n2, 7, 7, H, 4)
172
173 # calculate gfunction
174 gfunction = borefield.gfunction(borefield.time, H)
175
176 # calculate Ra
177 Ra = (gfunction[2] - gfunction[1]) / (2 * math.pi * ground_data.k_s)
178
179 # add to depths
180 depths.append(Ra)
181
182 # add to results
183 results.append(depths)
184
185 # create figure
186 plt.figure()
187 for i, config in enumerate(configs):
188 plt.plot(depth_array, results[i], label=str(config[0]) + "x" + str(config[1]))
189
190 # plt.title("Ra for different borefield configurations")
191 plt.xlabel("Depth (m)")
192 plt.ylabel("Ra (mK/W)")
193
194 plt.legend()
195 plt.show()
196
197
198def figure_4():
199 """
200 This code creates the fourth figure of the article
201 """
202 # initiate borefield
203 borefield = Borefield()
204
205 # set the correct sizing method
206 borefield.sizing_setup(L2_sizing=True)
207
208 # initiate array with imbalances
209 imbalance_array = np.linspace(200, 1600, 20)
210
211 # initiate list of borefield configurations
212 configs = [(10, 10), (11, 11), (12, 12), (14, 14),
213 (15, 15), (18, 18), (20, 20)]
214
215 # initiate loads
216 monthly_load_heating_percentage = np.array([0.155, 0.148, 0.125, .099, .064, 0., 0., 0., 0.061, 0.087, .117, 0.144])
217 monthly_load_cooling_percentage = np.array([0.025, 0.05, 0.05, .05, .075, .1, .2, .2, .1, .075, .05, .025])
218 monthly_load_heating = monthly_load_heating_percentage * 100 * 10 ** 3 # kWh
219 monthly_load_cooling_init = monthly_load_cooling_percentage * 100 * 10 ** 3 # kWh
220 peak_cooling_init = np.array([0., 0, 34., 69., 133., 187., 213., 240., 160., 37., 0., 0.]) # Peak cooling in kW
221 peak_heating = np.array([160., 142, 102., 55., 0., 0., 0., 0., 40.4, 85., 119., 136.])
222
223 # set heating loads
224 borefield.set_peak_heating(peak_heating)
225 borefield.set_baseload_heating(monthly_load_heating)
226
227 results = []
228 for i, config in enumerate(configs):
229 depth_array = []
230 for imbalance in imbalance_array:
231 # initiate ground data
232 ground_data = GroundData(2.4, 10, 0.12)
233
234 # set ground data
235 borefield.set_ground_parameters(ground_data)
236
237 # set borefield
238 borefield.create_rectangular_borefield(config[0], config[1], 7, 7, 100, 4)
239
240 # calculate loads
241 extra_load = imbalance / 12 * 10 ** 3 # kWh
242 monthly_load_cooling = monthly_load_cooling_init + extra_load
243 peak_cooling = peak_cooling_init + extra_load / 730
244
245 # set cooling loads
246 borefield.set_peak_cooling(peak_cooling)
247 borefield.set_baseload_cooling(monthly_load_cooling)
248 try:
249 depth = borefield.size()
250 depth_array.append(depth)
251 except:
252 pass
253
254 results.append(depth_array)
255
256 # create figure
257 plt.figure()
258 for i, config in enumerate(configs):
259 plt.plot(imbalance_array[:len(results[i])], results[i], label=str(config[0]) + "x" + str(config[1]))
260
261 # plt.title("Depth for different imbalances")
262 plt.xlabel("Imbalance (MWh/y)")
263 plt.ylabel("Depth (m)")
264
265 plt.legend()
266 plt.show()
267
268
269def figure_5():
270 """
271 This function creates the fifth figure of the article.
272 """
273
274 # initiate borefield
275 borefield = Borefield()
276
277 # set the correct sizing method
278 borefield.sizing_setup(L2_sizing=True)
279
280 # initiate array with imbalances
281 imbalance_array = np.linspace(200, 1600, 20)
282
283 # initiate list of borefield configurations
284 configs = [(10, 10), (11, 11), (12, 12), (14, 14),
285 (15, 15), (18, 18), (20, 20)]
286
287 # initiate loads
288 monthly_load_heating_percentage = np.array([0.155, 0.148, 0.125, .099, .064, 0., 0., 0., 0.061, 0.087, .117, 0.144])
289 monthly_load_cooling_percentage = np.array([0.025, 0.05, 0.05, .05, .075, .1, .2, .2, .1, .075, .05, .025])
290 monthly_load_heating = monthly_load_heating_percentage * 100 * 10 ** 3 # kWh
291 monthly_load_cooling_init = monthly_load_cooling_percentage * 100 * 10 ** 3 # kWh
292 peak_cooling_init = np.array([0., 0, 34., 69., 133., 187., 213., 240., 160., 37., 0., 0.]) # Peak cooling in kW
293 peak_heating = np.array([160., 142, 102., 55., 0., 0., 0., 0., 40.4, 85., 119., 136.])
294
295 # set heating loads
296 borefield.set_peak_heating(peak_heating)
297 borefield.set_baseload_heating(monthly_load_heating)
298
299 results = []
300 for i, config in enumerate(configs):
301 Ra_array = []
302 for imbalance in imbalance_array:
303 # initiate ground data
304 ground_data = GroundData(2.4, 10, 0.12)
305
306 # set ground data
307 borefield.set_ground_parameters(ground_data)
308
309 # set borefield
310 borefield.create_rectangular_borefield(config[0], config[1], 7, 7, 100, 4)
311
312 # calculate loads
313 extra_load = imbalance / 12 * 10 ** 3 # kWh
314 monthly_load_cooling = monthly_load_cooling_init + extra_load
315 peak_cooling = peak_cooling_init + extra_load / 730
316
317 # set cooling loads
318 borefield.set_peak_cooling(peak_cooling)
319 borefield.set_baseload_cooling(monthly_load_cooling)
320 try:
321 depth = borefield.size()
322
323 # calculate gfunction
324 gfunction = borefield.gfunction(borefield.time, depth)
325
326 # calculate Ra
327 Ra = (gfunction[2] - gfunction[1]) / (2 * math.pi * borefield.k_s)
328
329 Ra_array.append(Ra)
330 except:
331 pass
332
333 results.append(Ra_array)
334
335 # create figure
336 plt.figure()
337 for i, config in enumerate(configs):
338 plt.plot(imbalance_array[:len(results[i])], results[i], label=str(config[0]) + "x" + str(config[1]))
339
340 # plt.title("Ra for different borefield configurations")
341 plt.xlabel("Imbalance (MWh/y)")
342 plt.ylabel("Ra (mK/W)")
343
344 plt.legend()
345 plt.show()
346
347
348def figure_7():
349 """
350 This function creates the seventh figure in the article.
351 """
352
353 # initiate borefield
354 borefield = Borefield()
355
356 # set the correct sizing method
357 borefield.sizing_setup(L2_sizing=True)
358
359 # initiate array with imbalances
360 imbalance_array = np.linspace(100, 500, 10)
361
362 # initiate list of borefield configurations
363 configs = [(7, 15), (15, 15)]
364
365 # initiate loads
366 monthly_load_heating_percentage = np.array([0.155, 0.148, 0.125, .099, .064, 0., 0., 0., 0.061, 0.087, .117, 0.144])
367 monthly_load_cooling_percentage = np.array([0.025, 0.05, 0.05, .05, .075, .1, .2, .2, .1, .075, .05, .025])
368 monthly_load_heating = monthly_load_heating_percentage * 100 * 10 ** 3 # kWh
369 monthly_load_cooling_init = monthly_load_cooling_percentage * 100 * 10 ** 3 # kWh
370 peak_cooling_init = np.array([0., 0, 34., 69., 133., 187., 213., 240., 160., 37., 0., 0.]) # Peak cooling in kW
371 peak_heating = np.array([160., 142, 102., 55., 0., 0., 0., 0., 40.4, 85., 119., 136.])
372
373 # set heating loads
374 borefield.set_peak_heating(peak_heating)
375 borefield.set_baseload_heating(monthly_load_heating)
376
377 results = []
378 for i, config in enumerate(configs):
379 Ra_array = []
380 for imbalance in imbalance_array:
381 # initiate ground data
382 ground_data = GroundData(2.4, 10, 0.12)
383
384 # set ground data
385 borefield.set_ground_parameters(ground_data)
386
387 # set borefield
388 borefield.create_rectangular_borefield(config[0], config[1], 7, 7, 100, 4)
389
390 # calculate loads
391 extra_load = imbalance / 12 * 10 ** 3 # kWh
392 monthly_load_cooling = monthly_load_cooling_init + extra_load
393 peak_cooling = peak_cooling_init + extra_load / 730
394
395 # set cooling loads
396 borefield.set_peak_cooling(peak_cooling)
397 borefield.set_baseload_cooling(monthly_load_cooling)
398 try:
399 depth = borefield.size()
400
401 # calculate gfunction
402 gfunction = borefield.gfunction(borefield.time, depth)
403
404 # calculate Ra
405 Ra = (gfunction[2] - gfunction[1]) / (2 * math.pi * borefield.k_s)
406
407 Ra_array.append(Ra)
408 except:
409 pass
410
411 results.append(Ra_array)
412
413 # create figure
414 plt.figure()
415 for i, config in enumerate(configs):
416 plt.plot(imbalance_array[:len(results[i])], results[i], label=str(config[0]) + "x" + str(config[1]))
417
418 # plt.title("Ra for different borefield configurations")
419 plt.xlabel("Imbalance (MWh/y)")
420 plt.ylabel("Ra (mK/W)")
421
422 plt.annotate('', xy=(300, 2.167), xytext=(350, 2.205),
423 arrowprops=dict(arrowstyle='<-', color='black'))
424 plt.annotate('', xy=(280, 2.15), xytext=(240, 2.02),
425 arrowprops=dict(arrowstyle='<-', color='black'))
426
427 plt.legend()
428 plt.show()
429
430
431def figure_8():
432 """
433 This function creates the eight figure of the article.
434 """
435
436 # initiate borefield
437 borefield1 = Borefield()
438 borefield2 = Borefield()
439
440 # set the correct sizing method
441 borefield1.sizing_setup(L2_sizing=True)
442 borefield2.sizing_setup(L2_sizing=True)
443
444 # initiate array with imbalances percentages
445 imbalance_array = np.linspace(30, 70, 20)
446
447 # initiate list of borefield configurations
448 configs = [((20, 6), (20, 6)),
449 ((18, 8), (16, 6)),
450 ((14, 12), (18, 4)),
451 ((16, 12), (16, 5))]
452
453 # initiate imbalance
454 imbalance = 800
455
456 # initiate loads
457 monthly_load_heating_percentage = np.array([0.155, 0.148, 0.125, .099, .064, 0., 0., 0., 0.061, 0.087, 0.117, 0.144])
458 monthly_load_cooling_percentage = np.array([0.025, 0.05, 0.05, .05, .075, .1, .2, .2, .1, .075, .05, .025])
459 monthly_load_heating = monthly_load_heating_percentage * (100 + imbalance) * 10 ** 3
460 monthly_load_cooling = monthly_load_cooling_percentage * 100 * 10 ** 3 # kWh
461 peak_cooling = np.array([0., 0., 22., 44., 83., 117., 134., 150., 100., 23., 0., 0.])
462 peak_heating = np.array([300., 268., 191., 103., 75., 0., 0., 38., 76., 160., 224., 255.])
463
464 results = []
465 for i, config_pair in enumerate(configs):
466 config1, config2 = config_pair
467 ratio_of_nb_of_boreholes = config1[0] * config1[1] / (config2[0] * config2[0] + config1[0] * config1[1])
468
469 # initiate ground data
470 ground_data1 = GroundData(2.4, 10, 0.14)
471 ground_data2 = GroundData(2.4, 10, 0.14)
472
473 # set ground data
474 borefield1.set_ground_parameters(ground_data1)
475 borefield2.set_ground_parameters(ground_data2)
476
477 # set borefields
478 borefield1.create_rectangular_borefield(config1[0], config1[1], 7, 7, 100, 4)
479 borefield2.create_rectangular_borefield(config2[0], config2[1], 7, 7, 100, 4)
480
481 # set cooling peak according to the ratio of nb_of_boreholes
482 borefield1.set_peak_cooling(peak_cooling * ratio_of_nb_of_boreholes)
483 borefield2.set_peak_cooling(peak_cooling * (1-ratio_of_nb_of_boreholes))
484
485 results_temp = []
486
487 for imbalance_percentage in imbalance_array:
488
489 # set the imbalance loads
490 borefield1.set_baseload_heating(monthly_load_heating * imbalance_percentage/100)
491 borefield2.set_baseload_heating(monthly_load_heating * (100 - imbalance_percentage)/100)
492
493 # set peak load heating
494 borefield1.set_peak_heating((peak_heating + imbalance_percentage/100 * imbalance / 12 / 730 * 10 ** 3))
495 borefield2.set_peak_heating((peak_heating + (100 - imbalance_percentage) / 100 * imbalance / 12 / 730 * 10 ** 3))
496
497 # set baseload cooling equally over the fields
498 borefield1.set_baseload_cooling(monthly_load_cooling * imbalance_percentage/100)
499 borefield2.set_baseload_cooling(monthly_load_cooling * (100 - imbalance_percentage)/100)
500
501 try:
502 depth1 = borefield1.size()
503 depth2 = borefield2.size()
504
505 results_temp.append(depth1 * config1[0] * config1[1] + depth2 * config2[0] * config2[1])
506 except:
507 results_temp.append(0)
508
509 results.append(results_temp)
510
511 # create figure
512 plt.figure()
513 for i, config in enumerate(configs):
514 plt.plot(imbalance_array[:len(results[i])], results[i], label=str(config[0]) + "x" + str(config[1]))
515
516 # plt.title("Effect of imbalance distribution on total borefield length")
517 plt.xlabel("Percentage of imbalance on field with largest number of boreholes")
518 plt.ylabel("Total borefield length (m)")
519
520 plt.legend()
521 plt.show()
522
523
524if __name__ == "__main__":
525 figure_1()
526 figure_2()
527 figure_3()
528 figure_4()
529 figure_5()
530 figure_7()
531 figure_8()
References
- 1
Verleyen, L., Peere, W., Michiels, E., Boydens, W., Helsen, L. (2022). The beauty of reason and insight: a story about 30 years old borefield equations. IEA HPT Magazine 40(3), 36-39, https://doi.org/10.23697/6q4n-3223