diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 66cbd91b..c473a9d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,11 +2,6 @@ name: Tests Commit on: push: - branches: - - '*' # matches every branch that doesn't contain a '/' - - '*/*' # matches every branch containing a single '/' - - '**' # matches every branch - - '!main' # excludes master jobs: diff --git a/GHEtool/VariableClasses/LoadData/GeothermalLoad/HourlyGeothermalLoad.py b/GHEtool/VariableClasses/LoadData/GeothermalLoad/HourlyGeothermalLoad.py index 930f5c74..84b4291a 100644 --- a/GHEtool/VariableClasses/LoadData/GeothermalLoad/HourlyGeothermalLoad.py +++ b/GHEtool/VariableClasses/LoadData/GeothermalLoad/HourlyGeothermalLoad.py @@ -25,7 +25,7 @@ class HourlyGeothermalLoad(_LoadData): # define parameters for conversion to monthly loads START = pd.to_datetime("2019-01-01 00:00:00") END = pd.to_datetime("2019-12-31 23:59:00") - HOURS_SERIES = pd.Series(pd.date_range(START, END, freq="1H")) + HOURS_SERIES = pd.Series(pd.date_range(START, END, freq="1h")) def __init__(self, heating_load: ArrayLike | None = None, cooling_load: ArrayLike | None = None, diff --git a/GHEtool/main_class.py b/GHEtool/main_class.py index 849d3111..a98dd45e 100644 --- a/GHEtool/main_class.py +++ b/GHEtool/main_class.py @@ -220,7 +220,7 @@ def set_borefield(self, borefield: list[gt.boreholes.Borehole] = None) -> None: """ self.borefield = borefield - def create_rectangular_borefield(self, N_1: int, N_2: int, B_1: int, B_2: int, H: float, D: float = 1, + def create_rectangular_borefield(self, N_1: int, N_2: int, B_1: float, B_2: float, H: float, D: float = 1, r_b: float = 0.075): """ This function creates a rectangular borefield. @@ -280,6 +280,100 @@ def create_circular_borefield(self, N: int, R: float, H: float, D: float = 1, r_ self.set_borefield(borefield) return borefield + def create_U_shaped_borefield(self, N_1: int, N_2: int, B_1: float, B_2: float, H: float, D: float = 1, r_b: float = 0.075): + """ + This function creates a U shaped borefield. + It calls the pygfunction module in the background. + The documentation of this function is based on pygfunction. + + Parameters + ---------- + N_1 : int + Number of boreholes in the x direction + N_2 : int + Number of boreholes in the y direction + B_1 : int + Distance between adjacent boreholes in the x direction [m] + B_2 : int + Distance between adjacent boreholes in the y direction [m] + H : float + Borehole depth [m] + D : float + Borehole buried depth [m] + r_b : float + Borehole radius [m] + + Returns + ------- + pygfunction borefield object + """ + borefield = gt.boreholes.U_shaped_field(N_1, N_2, B_1, B_2, H, D, r_b) + self.set_borefield(borefield) + return borefield + + def create_L_shaped_borefield(self, N_1: int, N_2: int, B_1: float, B_2: float, H: float, D: float = 1, r_b: float = 0.075): + """ + This function creates a L shaped borefield. + It calls the pygfunction module in the background. + The documentation of this function is based on pygfunction. + + Parameters + ---------- + N_1 : int + Number of boreholes in the x direction + N_2 : int + Number of boreholes in the y direction + B_1 : int + Distance between adjacent boreholes in the x direction [m] + B_2 : int + Distance between adjacent boreholes in the y direction [m] + H : float + Borehole depth [m] + D : float + Borehole buried depth [m] + r_b : float + Borehole radius [m] + + Returns + ------- + pygfunction borefield object + """ + borefield = gt.boreholes.L_shaped_field(N_1, N_2, B_1, B_2, H, D, r_b) + self.set_borefield(borefield) + return borefield + + def create_box_shaped_borefield(self, N_1: int, N_2: int, B_1: float, B_2: float, H: float, D: float = 1, r_b: float = 0.075): + """ + This function creates a box shaped borefield. + It calls the pygfunction module in the background. + The documentation of this function is based on pygfunction. + + Parameters + ---------- + N_1 : int + Number of boreholes in the x direction + N_2 : int + Number of boreholes in the y direction + B_1 : int + Distance between adjacent boreholes in the x direction [m] + B_2 : int + Distance between adjacent boreholes in the y direction [m] + H : float + Borehole depth [m] + D : float + Borehole buried depth [m] + r_b : float + Borehole radius [m] + + Returns + ------- + pygfunction borefield object + """ + borefield = gt.boreholes.box_shaped_field(N_1, N_2, B_1, B_2, H, D, r_b) + self.set_borefield(borefield) + return borefield + + @property def borefield(self): """ diff --git a/GHEtool/test/unit-tests/test_main_class.py b/GHEtool/test/unit-tests/test_main_class.py index 111d79cb..f01ab4e0 100644 --- a/GHEtool/test/unit-tests/test_main_class.py +++ b/GHEtool/test/unit-tests/test_main_class.py @@ -21,26 +21,26 @@ borefield_gt = gt.boreholes.rectangle_field(10, 12, 6, 6, 110, 4, 0.075) # Monthly loading values -peakCooling = [0., 0, 34., 69., 133., 187., 213., 240., 160., 37., 0., 0.] # Peak cooling in kW -peakHeating = [160., 142, 102., 55., 0., 0., 0., 0., 40.4, 85., 119., 136.] # Peak heating in kW +peakCooling = [0.0, 0, 34.0, 69.0, 133.0, 187.0, 213.0, 240.0, 160.0, 37.0, 0.0, 0.0] # Peak cooling in kW +peakHeating = [160.0, 142, 102.0, 55.0, 0.0, 0.0, 0.0, 0.0, 40.4, 85.0, 119.0, 136.0] # Peak heating in kW # annual heating and cooling load -annualHeatingLoad = 300*10**3 # kWh -annualCoolingLoad = 160*10**3 # kWh +annualHeatingLoad = 300 * 10**3 # kWh +annualCoolingLoad = 160 * 10**3 # kWh # percentage of annual load per month (15.5% for January ...) -monthlyLoadHeatingPercentage = [0.155, 0.148, 0.125, .099, .064, 0., 0., 0., 0.061, 0.087, 0.117, 0.144] -monthlyLoadCoolingPercentage = [0.025, 0.05, 0.05, .05, .075, .1, .2, .2, .1, .075, .05, .025] +monthlyLoadHeatingPercentage = [0.155, 0.148, 0.125, 0.099, 0.064, 0.0, 0.0, 0.0, 0.061, 0.087, 0.117, 0.144] +monthlyLoadCoolingPercentage = [0.025, 0.05, 0.05, 0.05, 0.075, 0.1, 0.2, 0.2, 0.1, 0.075, 0.05, 0.025] # resulting load per month -monthlyLoadHeating = list(map(lambda x: x * annualHeatingLoad, monthlyLoadHeatingPercentage)) # kWh -monthlyLoadCooling = list(map(lambda x: x * annualCoolingLoad, monthlyLoadCoolingPercentage)) # kWh +monthlyLoadHeating = list(map(lambda x: x * annualHeatingLoad, monthlyLoadHeatingPercentage)) # kWh +monthlyLoadCooling = list(map(lambda x: x * annualCoolingLoad, monthlyLoadCoolingPercentage)) # kWh def borefields_equal(borefield_one, borefield_two) -> bool: for i in range(len(borefield_one)): if borefield_one[i].__dict__ != borefield_two[i].__dict__: - return False # pragma: no cover + return False # pragma: no cover return True @@ -108,6 +108,27 @@ def test_create_circular_field(): borefields_equal(borefield.borefield, gt.boreholes.circle_field(10, 10, 100, 1, 0.075)) +def test_create_U_shaped_field(): + borefield = Borefield() + borefield.create_U_shaped_borefield(10, 9, 6, 6, 110, 4, 0.075) + assert borefield.number_of_boreholes == 9*2+(10-2) + borefields_equal(borefield.borefield, gt.boreholes.U_shaped_field(10, 10, 6, 6, 110, 4, 0.075)) + + +def test_create_L_shaped_field(): + borefield = Borefield() + borefield.create_L_shaped_borefield(10, 9, 6, 6, 110, 4, 0.075) + assert borefield.number_of_boreholes == 10+ 8 + borefields_equal(borefield.borefield, gt.boreholes.L_shaped_field(10, 10, 6, 6, 110, 4, 0.075)) + + +def test_create_box_shaped_field(): + borefield = Borefield() + borefield.create_box_shaped_borefield(10, 8, 6, 6, 110, 4, 0.075) + assert borefield.number_of_boreholes == 10*2 + (8-2)*2 + borefields_equal(borefield.borefield, gt.boreholes.box_shaped_field(10, 10, 6, 6, 110, 4, 0.075)) + + def test_update_depth(): borefield = Borefield() borefield.borefield = copy.deepcopy(borefield_gt) @@ -297,10 +318,14 @@ def test_Tg(): assert borefield._Tg(20) == borefield.ground_data.calculate_Tg(20) -@pytest.mark.parametrize("ground_data, constant_Rb, result", - zip([ground_data_constant, data_ground_flux, ground_data_constant, data_ground_flux], - [True, True, False, False], - [39.994203323480214, 38.70946566704161, 30.924434615896764, 30.245606119498383])) +@pytest.mark.parametrize( + "ground_data, constant_Rb, result", + zip( + [ground_data_constant, data_ground_flux, ground_data_constant, data_ground_flux], + [True, True, False, False], + [39.994203323480214, 38.70946566704161, 30.924434615896764, 30.245606119498383], + ), +) def test_Ahmadfard(ground_data, constant_Rb, result): borefield = Borefield() borefield.borefield = copy.deepcopy(borefield_gt) @@ -313,10 +338,15 @@ def test_Ahmadfard(ground_data, constant_Rb, result): assert np.isclose(result, borefield._Ahmadfard(th, qh, qm, qa)) assert np.isclose(result, borefield.H) -@pytest.mark.parametrize("ground_data, constant_Rb, result", - zip([ground_data_constant, data_ground_flux, ground_data_constant, data_ground_flux], - [True, True, False, False], - [48.76844845370183, 46.593433439950985, 38.53491016745154, 37.100782551185])) + +@pytest.mark.parametrize( + "ground_data, constant_Rb, result", + zip( + [ground_data_constant, data_ground_flux, ground_data_constant, data_ground_flux], + [True, True, False, False], + [48.76844845370183, 46.593433439950985, 38.53491016745154, 37.100782551185], + ), +) def test_Carcel(ground_data, constant_Rb, result): borefield = Borefield() borefield.borefield = copy.deepcopy(borefield_gt) @@ -406,8 +436,7 @@ def test_size_L2_value_errors(): assert True -@pytest.mark.parametrize("quadrant, result", - zip([1, 2, 3, 4], [74.55862437702756, 96.85342542746277, 27.2041541800546, 21.903857780936665])) +@pytest.mark.parametrize("quadrant, result", zip([1, 2, 3, 4], [74.55862437702756, 96.85342542746277, 27.2041541800546, 21.903857780936665])) def test_size_L2(quadrant, result): borefield = Borefield() borefield.borefield = copy.deepcopy(borefield_gt) @@ -435,8 +464,8 @@ def test_size_L3_value_errors(): except ValueError: assert True -@pytest.mark.parametrize("quadrant, result", - zip([1, 2, 3, 4], [56.37136629360852, 71.42698877336204, 26.722846792067735, 21.333161686968708])) + +@pytest.mark.parametrize("quadrant, result", zip([1, 2, 3, 4], [56.37136629360852, 71.42698877336204, 26.722846792067735, 21.333161686968708])) def test_size_L3(quadrant, result): borefield = Borefield() borefield.borefield = copy.deepcopy(borefield_gt) @@ -791,7 +820,7 @@ def test_gfunction(): def test_load_duration(monkeypatch): borefield = Borefield() - monkeypatch.setattr(plt, 'show', lambda: None) + monkeypatch.setattr(plt, "show", lambda: None) borefield.set_ground_parameters(ground_data_constant) borefield.borefield = copy.deepcopy(borefield_gt) load = HourlyGeothermalLoad() @@ -803,7 +832,7 @@ def test_load_duration(monkeypatch): def test_optimise_load_profile(monkeypatch): borefield = Borefield() - monkeypatch.setattr(plt, 'show', lambda: None) + monkeypatch.setattr(plt, "show", lambda: None) borefield.set_ground_parameters(ground_data_constant) borefield.borefield = copy.deepcopy(borefield_gt) load = HourlyGeothermalLoad() @@ -818,7 +847,7 @@ def test_optimise_load_profile(monkeypatch): def test_optimise_borefield_small(monkeypatch): borefield = Borefield() - monkeypatch.setattr(plt, 'show', lambda: None) + monkeypatch.setattr(plt, "show", lambda: None) borefield.set_ground_parameters(ground_data_constant) borefield.create_rectangular_borefield(5, 1, 6, 6, 100) load = HourlyGeothermalLoad() @@ -833,7 +862,7 @@ def test_optimise_borefield_small(monkeypatch): def test_optimise_borefield_wrong_threshold(monkeypatch): borefield = Borefield() - monkeypatch.setattr(plt, 'show', lambda: None) + monkeypatch.setattr(plt, "show", lambda: None) borefield.set_ground_parameters(ground_data_constant) borefield.create_rectangular_borefield(5, 1, 6, 6, 100) load = HourlyGeothermalLoad() @@ -845,6 +874,7 @@ def test_optimise_borefield_wrong_threshold(monkeypatch): except ValueError: assert True + def test_calculate_quadrants_without_data(): borefield = Borefield() borefield.borefield = copy.deepcopy(borefield_gt) @@ -880,7 +910,7 @@ def test_calculate_temperature_profile(): try: borefield.calculate_temperatures(hourly=True) - assert False # pragma: no cover + assert False # pragma: no cover except ValueError: assert True @@ -890,7 +920,7 @@ def test_optimise_load_profile_without_hourly_data(): borefield.load = MonthlyGeothermalLoadAbsolute(*load_case(1)) try: borefield.optimise_load_profile(borefield.load) - assert False # pragma: no cover + assert False # pragma: no cover except ValueError: assert True borefield.load = HourlyGeothermalLoad() @@ -900,14 +930,21 @@ def test_optimise_load_profile_without_hourly_data(): borefield.optimise_load_profile(borefield.load) -@pytest.mark.parametrize("H, result", - zip(range(110, 130, 1), [122.99210426454648, 122.99135446500962, 122.99135409065917, - 122.99135403971272, 122.99135403719148, 122.9913540367823, - 122.99135403674013, 122.99135403673134, 122.99221686744185, - 122.99217229220649, 122.99135553171544, 122.99220727438545, - 122.99477143007601, 122.9921794642058, 122.99220615327106, - 122.99221262582992, 122.99221900364599, 122.9922252887964, - 122.99223148329897, 122.99223758911434])) +@pytest.mark.parametrize( + "H, result", + zip( + range(110, 130, 1), + [ + 122.99210426454648, 122.99135446500962, 122.99135409065917, 122.99135403971272, + 122.99135403719148, 122.9913540367823, 122.99135403674013, 122.99135403673134, + 122.99221686744185, 122.99217229220649, 122.99135553171544, 122.99220727438545, + 122.99477143007601, 122.9921794642058, 122.99220615327106, 122.99221262582992, + 122.99221900364599, 122.9922252887964, 122.99223148329897, 122.99223758911434, + ], + ), +) + + def test_effect_H_init(H, result): borefield = Borefield() borefield.ground_data = GroundConstantTemperature(3, 11) @@ -954,9 +991,7 @@ def test_calculate_next_depth_deep_sizing(): assert np.isclose(borefield.calculate_next_depth_deep_sizing(128.16618036528823), 130.8812255630479) -@pytest.mark.parametrize("case, result", - zip((1, 2, 3, 4), - [131.90418292004594, 0, 139.46239300837794, 131.90418292004594])) +@pytest.mark.parametrize("case, result", zip((1, 2, 3, 4), [131.90418292004594, 0, 139.46239300837794, 131.90418292004594])) def test_deep_sizing(case, result): borefield = Borefield() borefield.ground_data = GroundFluxTemperature(3, 10) diff --git a/requirements.txt b/requirements.txt index 21762f3b..2b3bd3c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ matplotlib>=3.5.2 numpy>=1.23.1 -pandas>=1.4.3 +pyarrow>=15.0.0 +pandas>=2.2.0 pygfunction>=2.2.1 scipy>=1.8.1