From c5465d0570dc53d47f1f049407fb066c407c6246 Mon Sep 17 00:00:00 2001 From: wouterpeere Date: Mon, 20 Jan 2025 11:11:33 +0100 Subject: [PATCH 1/4] First implementation of the tilt property --- GHEtool/Borefield.py | 10 +++++++--- GHEtool/VariableClasses/CustomGFunction.py | 7 ++++--- GHEtool/test/unit-tests/test_main_class.py | 16 ++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/GHEtool/Borefield.py b/GHEtool/Borefield.py index 10cff41b..37dbb92d 100644 --- a/GHEtool/Borefield.py +++ b/GHEtool/Borefield.py @@ -4,6 +4,7 @@ from __future__ import annotations import copy +import math import warnings from math import pi from typing import Tuple, Union @@ -133,6 +134,7 @@ def __init__( self.Tf_min: float = 0.0 # minimum temperature of the fluid # initiale borehole + self.avg_tilt: float = 0. self.borehole = Borehole() # initiate different sizing @@ -204,7 +206,7 @@ def depth(self) -> float: def calculate_depth(self, borehole_length: float, buried_depth: float) -> float: """ - This function calculates the depth of the borehole. + This function calculates the depth of the borehole given the average tilt of the borefield. Parameters ---------- @@ -218,8 +220,9 @@ def calculate_depth(self, borehole_length: float, buried_depth: float) -> float: float Depth of the borehole [m] """ - # TODO take into account tilt - return borehole_length + buried_depth + if np.isclose(self.avg_tilt, 0): + return borehole_length + buried_depth + return np.average([bor.H * math.cos(bor.tilt) for bor in self.borefield]) + buried_depth @property def H(self) -> float: @@ -458,6 +461,7 @@ def borefield(self, borefield: list[gt.boreholes.Borehole] = None) -> None: self.D = np.average([bor.D for bor in borefield]) self.r_b = np.average([bor.r_b for bor in borefield]) self._H = np.average([bor.H for bor in borefield]) + self.avg_tilt = np.average([bor.tilt for bor in borefield]) self.gfunction_calculation_object.remove_previous_data() unequal_length = np.any([bor.H != borefield[0].H for bor in borefield]) if unequal_length: diff --git a/GHEtool/VariableClasses/CustomGFunction.py b/GHEtool/VariableClasses/CustomGFunction.py index dfafd2c3..cb7076c7 100644 --- a/GHEtool/VariableClasses/CustomGFunction.py +++ b/GHEtool/VariableClasses/CustomGFunction.py @@ -2,6 +2,7 @@ This file contains both the CustomGFunction class and all the relevant information w.r.t. custom gfunctions. """ import copy +import math import pickle import warnings from typing import List, Union @@ -225,10 +226,10 @@ def create_custom_dataset(self, borefield: List[gt.boreholes.Borehole], alpha: U # calculate borehole buried depth D = np.average([bor.D for bor in borefield]) - # TODO correct for tilt + tilt = np.average([bor.tilt for bor in borefield]) + depth = borehole_length * math.cos(tilt) + D gfunc_uniform_T = gt.gfunction.gFunction(borefield, - alpha if isinstance(alpha, float) else alpha(borehole_length + D, - D), + alpha if isinstance(alpha, float) else alpha(depth, D), self.time_array, options=self.options, method=self.options["method"]) diff --git a/GHEtool/test/unit-tests/test_main_class.py b/GHEtool/test/unit-tests/test_main_class.py index 993f8386..b239dcf3 100644 --- a/GHEtool/test/unit-tests/test_main_class.py +++ b/GHEtool/test/unit-tests/test_main_class.py @@ -1,5 +1,6 @@ # noinspection PyPackageRequirements import copy +import math import matplotlib.pyplot as plt import numpy as np @@ -70,6 +71,7 @@ def test_nb_of_boreholes(): borefield.set_ground_parameters(data_ground_flux) assert borefield.number_of_boreholes == 120 borefield.set_borefield(gt.boreholes.rectangle_field(5, 5, 6, 6, 110, 0.1, 0.07)) + assert np.isclose(borefield.avg_tilt, 0) assert np.isclose(borefield.H, 110) assert np.isclose(borefield.r_b, 0.07) assert np.isclose(borefield.D, 0.1) @@ -78,11 +80,13 @@ def test_nb_of_boreholes(): assert np.any(borefield.gfunction_calculation_object.borehole_length_array) borefield.borefield = gt.boreholes.rectangle_field(6, 5, 6, 6, 100, 1, 0.075) assert not np.any(borefield.gfunction_calculation_object.borehole_length_array) + assert np.isclose(borefield.avg_tilt, 0) assert np.isclose(borefield.H, 100) assert np.isclose(borefield.r_b, 0.075) assert np.isclose(borefield.D, 1) borefield.gfunction(5000, 110) assert np.any(borefield.gfunction_calculation_object.borehole_length_array) + assert np.isclose(borefield.avg_tilt, 0) assert borefield.number_of_boreholes == 30 borefield.borefield = None assert not np.any(borefield.gfunction_calculation_object.borehole_length_array) @@ -91,6 +95,7 @@ def test_nb_of_boreholes(): borefield.borefield = gt.boreholes.rectangle_field(6, 5, 6, 6, 100, 1, 0.075) borefield.gfunction(5000, 110) assert np.any(borefield.gfunction_calculation_object.borehole_length_array) + assert np.isclose(borefield.avg_tilt, 0) borefield.set_borefield(None) assert not np.any(borefield.gfunction_calculation_object.borehole_length_array) assert borefield.number_of_boreholes == 0 @@ -105,6 +110,17 @@ def test_set_borefield(): assert borefield.H == 125 +def test_tilt(): + borefield = Borefield() + borefield.set_borefield([ + gt.boreholes.Borehole(100, 4, 0.075, 0, 0), + gt.boreholes.Borehole(150, 4, 0.075, 10, 0, tilt=math.pi / 9) + ]) + assert borefield.H == 125 + assert np.isclose(borefield.avg_tilt, math.pi / 18) + assert np.isclose(borefield.depth, 4 + (100 + 150 * math.cos(math.pi / 9)) / 2) + + def test_gfunction_with_irregular_borehole_depth(): borefield = Borefield() borefield.ground_data = ground_data_constant From 86227723021032fd43ea97f38dc468080a0d86d3 Mon Sep 17 00:00:00 2001 From: wouterpeere Date: Mon, 20 Jan 2025 12:03:16 +0100 Subject: [PATCH 2/4] Implemented --- CHANGELOG.md | 1 + GHEtool/Borefield.py | 5 +-- GHEtool/Examples/tilted_borefield.py | 50 ++++++++++++++++++++++ GHEtool/test/test_examples.py | 6 +++ GHEtool/test/unit-tests/test_main_class.py | 32 +++++++++++++- 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 GHEtool/Examples/tilted_borefield.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 140e7335..4e333037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added start_depth to the calculation of Tg in all GroundClasses (issue #137). - Added property 'depth' to Borefield class (issue #137). - Added function 'calculate_depth' to Borefield class (issue #137). +- Added support for titled boreholes (issue #318). ## Fixed diff --git a/GHEtool/Borefield.py b/GHEtool/Borefield.py index 37dbb92d..56285931 100644 --- a/GHEtool/Borefield.py +++ b/GHEtool/Borefield.py @@ -117,9 +117,6 @@ def __init__( self.custom_gfunction: CustomGFunction = custom_gfunction self.gfunction_calculation_object: GFunction = GFunction() - ## params w.r.t. pygfunction - self.options_pygfunction: dict = {"method": "equivalent"} - # initialize variables for temperature plotting self.results: ResultsMonthly | ResultsHourly = ResultsMonthly() @@ -462,6 +459,8 @@ def borefield(self, borefield: list[gt.boreholes.Borehole] = None) -> None: self.r_b = np.average([bor.r_b for bor in borefield]) self._H = np.average([bor.H for bor in borefield]) self.avg_tilt = np.average([bor.tilt for bor in borefield]) + # if not np.isclose(self.avg_tilt, 0): + self.gfunction_calculation_object.options['method'] = 'similarities' self.gfunction_calculation_object.remove_previous_data() unequal_length = np.any([bor.H != borefield[0].H for bor in borefield]) if unequal_length: diff --git a/GHEtool/Examples/tilted_borefield.py b/GHEtool/Examples/tilted_borefield.py new file mode 100644 index 00000000..b2e9ed3c --- /dev/null +++ b/GHEtool/Examples/tilted_borefield.py @@ -0,0 +1,50 @@ +""" +This example shows how you can design a borefield with tilted boreholes. +""" +import math + +from GHEtool import * + +import matplotlib.pyplot as plt +import numpy as np +import pygfunction as gt + + +def tilted(): + # define params + ground_data = GroundFluxTemperature(1.9, 10) + pipe_data = DoubleUTube(1.5, 0.013, 0.016, 0.4, 0.035) + fluid_data = FluidData(mfr=0.2) + fluid_data.import_fluid_from_pygfunction(gt.media.Fluid('MPG', 30, 2)) + load_data = MonthlyBuildingLoadAbsolute( + np.array([.176, .174, .141, .1, .045, 0, 0, 0, 0.012, 0.065, 0.123, 0.164]) * 8 * 1350, + np.array([0, 0, 0, 0, .112, .205, .27, .264, .149, 0, 0, 0]) * 4 * 700, + np.array([1, .991, .802, .566, .264, 0, 0, 0, .0606, .368, .698, .934]) * 8, + np.array([0, 0, 0, 0, .415, .756, 1, .976, .549, 0, 0, 0]) * 4 + ) + + # define borefield + borefield_tilted = [gt.boreholes.Borehole(150, 0.75, 0.07, -3, 0, math.pi / 7, orientation=math.pi), + gt.boreholes.Borehole(150, 0.75, 0.07, 3, 0, math.pi / 7, orientation=0)] + borefield_without_tilt = [gt.boreholes.Borehole(150, 0.75, 0.07, -3, 0), + gt.boreholes.Borehole(150, 0.75, 0.07, 3, 0)] + + # initiate GHEtool object with tilted borefield + borefield = Borefield(borefield=borefield_tilted, load=load_data) + borefield.set_ground_parameters(ground_data) + borefield.set_pipe_parameters(pipe_data) + borefield.set_fluid_parameters(fluid_data) + + borefield.print_temperature_profile() + + # initiate GHEtool object without tilted borefield + borefield = Borefield(borefield=borefield_without_tilt, load=load_data) + borefield.set_ground_parameters(ground_data) + borefield.set_pipe_parameters(pipe_data) + borefield.set_fluid_parameters(fluid_data) + + borefield.print_temperature_profile() + + +if __name__ == "__main__": + tilted() diff --git a/GHEtool/test/test_examples.py b/GHEtool/test/test_examples.py index 2904a4cf..e8c6bce6 100644 --- a/GHEtool/test/test_examples.py +++ b/GHEtool/test/test_examples.py @@ -79,3 +79,9 @@ def test_separatus(monkeypatch): design_with_single_U() design_with_double_U() design_with_separatus() + + +def test_tilted(monkeypatch): + monkeypatch.setattr(plt, 'show', lambda: None) + from GHEtool.Examples.tilted_borefield import tilted + tilted() diff --git a/GHEtool/test/unit-tests/test_main_class.py b/GHEtool/test/unit-tests/test_main_class.py index b239dcf3..09f743de 100644 --- a/GHEtool/test/unit-tests/test_main_class.py +++ b/GHEtool/test/unit-tests/test_main_class.py @@ -12,7 +12,7 @@ from GHEtool.logger import ghe_logger from GHEtool.Validation.cases import load_case from GHEtool.VariableClasses.LoadData import MonthlyGeothermalLoadAbsolute, HourlyGeothermalLoad, HourlyBuildingLoad, \ - HourlyBuildingLoadMultiYear + HourlyBuildingLoadMultiYear, MonthlyBuildingLoadAbsolute from GHEtool.VariableClasses.BaseClass import UnsolvableDueToTemperatureGradient data = GroundConstantTemperature(3, 10) @@ -1182,3 +1182,33 @@ def test_repr_(): 'Peak extraction duration [hour]: 6.0\n' \ 'Simulation period [year]: 20\n' \ 'First month of simulation [-]: 1' == borefield.__repr__() + + +def test_with_titled_borefield(): + # define params + ground_data = GroundFluxTemperature(1.9, 10) + pipe_data = DoubleUTube(1.5, 0.013, 0.016, 0.4, 0.035) + fluid_data = FluidData(mfr=0.2) + fluid_data.import_fluid_from_pygfunction(gt.media.Fluid('MPG', 30, 2)) + load_data = MonthlyBuildingLoadAbsolute( + np.array([.176, .174, .141, .1, .045, 0, 0, 0, 0.012, 0.065, 0.123, 0.164]) * 8 * 1350, + np.array([0, 0, 0, 0, .112, .205, .27, .264, .149, 0, 0, 0]) * 4 * 700, + np.array([1, .991, .802, .566, .264, 0, 0, 0, .0606, .368, .698, .934]) * 8, + np.array([0, 0, 0, 0, .415, .756, 1, .976, .549, 0, 0, 0]) * 4 + ) + + # define borefield + borefield_tilted = [gt.boreholes.Borehole(150, 0.75, 0.07, -3, 0, math.pi / 7, orientation=math.pi), + gt.boreholes.Borehole(150, 0.75, 0.07, 3, 0, math.pi / 7, orientation=0)] + + # initiate GHEtool object with tilted borefield + borefield = Borefield(borefield=borefield_tilted, load=load_data) + borefield.set_ground_parameters(ground_data) + borefield.set_pipe_parameters(pipe_data) + borefield.set_fluid_parameters(fluid_data) + borefield.set_max_avg_fluid_temperature(17) + + assert np.isclose(borefield.depth, 150 * math.cos(math.pi / 7) + 0.75) + assert np.isclose(borefield.ground_data.calculate_Tg(borefield.depth, borefield.D), 12.157557845032045) + + assert np.isclose(borefield.size_L3(), 111.58488656187147) From dc982687febd1e7277ae6aa11b0c90c59e24fedd Mon Sep 17 00:00:00 2001 From: wouterpeere Date: Mon, 20 Jan 2025 14:33:07 +0100 Subject: [PATCH 3/4] Fix tests --- GHEtool/Borefield.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GHEtool/Borefield.py b/GHEtool/Borefield.py index 56285931..13887a2a 100644 --- a/GHEtool/Borefield.py +++ b/GHEtool/Borefield.py @@ -459,8 +459,8 @@ def borefield(self, borefield: list[gt.boreholes.Borehole] = None) -> None: self.r_b = np.average([bor.r_b for bor in borefield]) self._H = np.average([bor.H for bor in borefield]) self.avg_tilt = np.average([bor.tilt for bor in borefield]) - # if not np.isclose(self.avg_tilt, 0): - self.gfunction_calculation_object.options['method'] = 'similarities' + if not np.isclose(self.avg_tilt, 0): + self.gfunction_calculation_object.options['method'] = 'similarities' self.gfunction_calculation_object.remove_previous_data() unequal_length = np.any([bor.H != borefield[0].H for bor in borefield]) if unequal_length: From 367efe47fd72923f8544a92f44ff4500e0af6b73 Mon Sep 17 00:00:00 2001 From: wouterpeere Date: Mon, 20 Jan 2025 17:03:38 +0100 Subject: [PATCH 4/4] bump version --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 9bab04bd..9e7b5419 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = GHEtool -version = 2.3.1.dev1 +version = 2.3.1.dev2 author = Wouter Peere author_email = wouter@ghetool.eu description = Python package for borefield sizing