Skip to content

Commit 8151179

Browse files
Merge pull request #325 from wouterpeere/master
Create add function for Borefield Class
2 parents 147938f + 1cbf556 commit 8151179

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Version 2.4 (in development)
44

5+
### New features
6+
7+
* [Pull Request 325](https://github.com/MassimoCimmino/pygfunction/pull/325) - Borefields and boreholes can now be concatenated using the `+` operator, e.g. using `new_field = field_1 + field_2`.
8+
59
### Other changes
610

711
* [Issue 319](https://github.com/MassimoCimmino/pygfunction/issues/319) - Created `solvers` module. `Solver` classes are moved out of the `gfunction` module and into the new module.

pygfunction/borefield.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,42 @@ def __ne__(
107107
check = not self == other_field
108108
return check
109109

110+
def __add__(self,
111+
other_field: Union[Borehole, List[Borehole], Self]) -> Self:
112+
"""Add two borefields together"""
113+
if not isinstance(other_field, (Borehole, list, self.__class__)):
114+
raise TypeError(
115+
f'Expected Borefield, list or Borehole input;'
116+
f' got {other_field}'
117+
)
118+
# List of boreholes
119+
field = self.to_boreholes()
120+
# Convert other_field to a list if it is a Borehole
121+
if isinstance(other_field, Borehole):
122+
other_field = [other_field]
123+
# Convert borefield to a list if it is a Borefield
124+
if isinstance(other_field, self.__class__):
125+
other_field = other_field.to_boreholes()
126+
return Borefield.from_boreholes(field + other_field)
127+
128+
def __radd__(self,
129+
other_field: Union[Borehole, List[Borehole], Self]) -> Self:
130+
"""Add two borefields together"""
131+
if not isinstance(other_field, (Borehole, list, self.__class__)):
132+
raise TypeError(
133+
f'Expected Borefield, list or Borehole input;'
134+
f' got {other_field}'
135+
)
136+
# List of boreholes
137+
field = self.to_boreholes()
138+
# Convert other_field to a list if it is a Borehole
139+
if isinstance(other_field, Borehole):
140+
other_field = [other_field]
141+
# Convert borefield to a list if it is a Borefield
142+
if isinstance(other_field, self.__class__):
143+
other_field = other_field.to_boreholes()
144+
return Borefield.from_boreholes(other_field + field)
145+
110146
def evaluate_g_function(
111147
self,
112148
alpha: float,

pygfunction/boreholes.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# -*- coding: utf-8 -*-
2+
from typing import Union
23
import warnings
34

45
import numpy as np
56
from scipy.spatial.distance import pdist
7+
from typing_extensions import Self # for compatibility with Python <= 3.10
68

79
from .utilities import _initialize_figure, _format_axes, _format_axes_3d
810

@@ -54,6 +56,56 @@ def __repr__(self):
5456
f' orientation={self.orientation})')
5557
return s
5658

59+
def __add__(self, other: Union[Self, list]):
60+
"""
61+
Adds two boreholes together to form a borefield
62+
"""
63+
if not isinstance(other, (self.__class__, list)):
64+
# Check if other is a borefield and try the operation using
65+
# other.__radd__
66+
try:
67+
field = other.__radd__(self)
68+
except:
69+
# Invalid input
70+
raise TypeError(
71+
f'Expected Borefield, list or Borehole input;'
72+
f' got {other}'
73+
)
74+
elif isinstance(other, list):
75+
# Create a borefield from the borehole and a list
76+
from .borefield import Borefield
77+
field = Borefield.from_boreholes([self] + other)
78+
else:
79+
# Create a borefield from the two boreholes
80+
from .borefield import Borefield
81+
field = Borefield.from_boreholes([self, other])
82+
return field
83+
84+
def __radd__(self, other: Union[Self, list]):
85+
"""
86+
Adds two boreholes together to form a borefield
87+
"""
88+
if not isinstance(other, (self.__class__, list)):
89+
# Check if other is a borefield and try the operation using
90+
# other.__radd__
91+
try:
92+
field = other.__add__(self)
93+
except:
94+
# Invalid input
95+
raise TypeError(
96+
f'Expected Borefield, list or Borehole input;'
97+
f' got {other}'
98+
)
99+
elif isinstance(other, list):
100+
# Create a borefield from the borehole and a list
101+
from .borefield import Borefield
102+
field = Borefield.from_boreholes(other + [self])
103+
else:
104+
# Create a borefield from the two boreholes
105+
from .borefield import Borefield
106+
field = Borefield.from_boreholes([other, self])
107+
return field
108+
57109
def distance(self, target):
58110
"""
59111
Evaluate the distance between the current borehole and a target

tests/borefield_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,35 @@ def test_borefield_init(field, request):
3232
H, D, r_b, x, y, tilt=tilt, orientation=orientation)
3333
assert borefield == borefield_from_boreholes
3434

35+
36+
# Test Borefield.__add__ and Borefield.__radd__
37+
@pytest.mark.parametrize("field, other_field, field_list, other_field_list, field_borehole, other_field_borehole", [
38+
# Using Borefield objects
39+
('ten_boreholes_rectangular', 'two_boreholes_inclined', False, False, False, False),
40+
# Using Borefield objects
41+
('single_borehole', 'two_boreholes_inclined', False, False, True, False),
42+
('ten_boreholes_rectangular', 'single_borehole_short', False, False, False, True),
43+
# Using Borefield as lists
44+
('ten_boreholes_rectangular', 'two_boreholes_inclined', False, True, False, False),
45+
('ten_boreholes_rectangular', 'two_boreholes_inclined', True, False, False, False),
46+
])
47+
def test_borefield_add(field, other_field, field_list, other_field_list, field_borehole, other_field_borehole, request):
48+
field = request.getfixturevalue(field)
49+
other_field = request.getfixturevalue(other_field)
50+
reference_field = gt.borefield.Borefield.from_boreholes(
51+
field.to_boreholes() + other_field.to_boreholes()
52+
)
53+
if field_list:
54+
field = field.to_boreholes()
55+
if other_field_list:
56+
other_field = other_field.to_boreholes()
57+
if field_borehole:
58+
field = field[0]
59+
if other_field_borehole:
60+
other_field = other_field[0]
61+
assert field + other_field == reference_field
62+
63+
3564
# Test borefield comparison using __eq__
3665
@pytest.mark.parametrize("field, other_field, expected", [
3766
# Fields that are equal
@@ -53,6 +82,7 @@ def test_borefield_eq(field, other_field, expected, request):
5382
other_field = request.getfixturevalue(other_field)
5483
assert (borefield == other_field) == expected
5584

85+
5686
# Test borefield comparison using __ne__
5787
@pytest.mark.parametrize("field, other_field, expected", [
5888
# Fields that are equal

tests/boreholes_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@ def test_borehole_init():
3333
])
3434

3535

36+
# Test Borehole.__add__
37+
@pytest.mark.parametrize("borehole, other_borehole, borehole_list, other_borehole_list", [
38+
('single_borehole', 'single_borehole_short', False, False),
39+
('single_borehole', 'single_borehole_short', True, False),
40+
('single_borehole', 'single_borehole_short', False, True),
41+
])
42+
def test_borehole_add(borehole, other_borehole, borehole_list, other_borehole_list, request):
43+
borehole = request.getfixturevalue(borehole)[0]
44+
other_borehole = request.getfixturevalue(other_borehole)[0]
45+
field = gt.borefield.Borefield.from_boreholes(
46+
[borehole, other_borehole])
47+
if borehole_list:
48+
borehole = [borehole]
49+
if other_borehole_list:
50+
other_borehole = [other_borehole]
51+
assert field == borehole + other_borehole
52+
53+
3654
# Test Borehole.distance
3755
@pytest.mark.parametrize("borehole1, borehole2", [
3856
# Same borehole

0 commit comments

Comments
 (0)