Closed wouterpeere closed 7 months ago
Thank you @wouterpeere. For the minimum spacing B
to be correctly implemented, shouldn't the vertical spacing be B_y = sqrt(3)/2
, so that the spacing B
is also respected on the diagonally adjacent boreholes?
Hi,
What about implementing both, because I think the first solution is easier to handle for the user. Considering the first solution the width and length of the borefield is the multiplication of the number of boreholes times the distance. The same distance on diagonal is indeed the denser solution. Here my code as suggestion:
def dense_field(
number_width: int,
number_length: int,
distance_width: float,
distance_length: float,
depth: float,
burial_depth: float,
borehole_radius: float,
include_last_borehole: bool = True,
same_distance_diagonal: bool = True,
) -> list[Borehole]:
"""
Build a list of boreholes in a dense bore field configuration.
Here, the high density cylinder packing is used. This means that every borehole is a distance B (in meters)
away from each other.
Parameters
----------
number_width : int
Number of borehole in the x direction.
number_length : int
Number of borehole in the y direction.
distance_width : float
Distance (in meters) between adjacent boreholes in width direction.
distance_length : float
Distance (in meters) between adjacent boreholes in length direciton.
depth : float
Borehole length (in meters).
burial_depth : float
Borehole buried depth (in meters).
borehole_radius : float
Borehole radius (in meters).
include_last_borehole : bool
True if each row of boreholes should have equal lengths. False, if the uneven rows have one borehole less
so they are contained within the imaginary 'box' around the borefield
same_distance_diagonal: bool
should be the same distance on the diagonal (True) or in width and length direction (False) be set
Returns
-------
boreField : list of Borehole objects
List of boreholes in the dense bore field.
Examples
--------
>>> import pygfunction as gt
>>> boreField = gt.boreholes.dense_field(number_width=3, number_length=2, distance_width=5., distance_length=5., depth=100., burial_depth=2.5,
>>> borehole_radius=0.05, include_last_borehole=True)
The bore field is constructed line by line. For N_1=3 and N_2=3, the bore
field layout is as follows::
6 7 8
3 4 5
0 1 2
"""
borefield = []
# check for line
if number_width == 1 or number_length == 1:
return rectangle_field(number_width, number_length, distance_width, distance_length, depth, burial_depth, borehole_radius)
fac: float = np.sqrt(2) if same_distance_diagonal else 1
for j in range(number_length): # y direction
for i in range(number_width): # x direction
x = i * distance_width * fac + (distance_length * fac / 2 if j % 2 == 1 else 0)
y = j * distance_length * fac / 2
if include_last_borehole or (j % 2 == 0 or i != number_width - 1): # last borehole in the x direction on an oneven row
borefield.append(Borehole(depth, burial_depth, borehole_radius, x, y))
return borefield
@pytest.mark.parametrize(
"N_1, N_2, B, include_last_element, same_distance_diagonal",
[
(n1, n2, b, last, diag)
for n1, n2, b in [(1, 1, 5.0), (2, 1, 5.0), (1, 2, 5.0), (2, 2, 5.0), (10, 9, 7.5), (10, 10, 7.5)]
for last in [True, False]
for diag in [True, False]
],
)
def test_dense_field(N_1, N_2, B, include_last_element, same_distance_diagonal):
H = 150.0 # Borehole length [m]
D = 4.0 # Borehole buried depth [m]
r_b = 0.075 # Borehole radius [m]
# Generate the bore field
field = gt.boreholes.dense_field(N_1, N_2, B, B, H, D, r_b, include_last_borehole=include_last_element, same_distance_diagonal=same_distance_diagonal)
# Evaluate the borehole to borehole distances
x = np.array([b.x for b in field])
y = np.array([b.y for b in field])
dis = np.sqrt(np.subtract.outer(x, x) ** 2 + np.subtract.outer(y, y) ** 2)[~np.eye(len(field), dtype=bool)]
if include_last_element or N_1 == 1 or N_2 == 1:
assert len(field) == N_1 * N_2
elif N_2 % 2 == 0:
assert len(field) == N_2 * (2 * N_1 - 1) / 2
else:
assert len(field) == (N_2 - 1) * (2 * N_1 - 1) / 2 + N_1
if N_1 > 1 and N_2 > 1:
if same_distance_diagonal:
assert np.isclose(np.sqrt(x[y > 0.1][0] ** 2 + y[y > 0.1][0] ** 2), B)
else:
assert np.isclose(x[y > 0.1][0], B / 2)
assert np.isclose(y[y > 0.1][0], B / 2)
assert np.all(
[
np.allclose(H, [b.H for b in field]),
np.allclose(D, [b.D for b in field]),
np.allclose(r_b, [b.r_b for b in field]),
]
)
@tblanke I like the idea of having both options. I would prefer having two functions instead of the same_distance_diagonal
argument, which I believe would be more intuitive for the user. B_2
(distance_length
) should be the distance between rows, rather than twice the distance.
def rectangle_field_triangular(N_1, N_2, B_1, B_2, H, D, r_b, include_last_borehole, tilt=0., origin=None):
"""
Build a list of boreholes in a rectangular bore field configuration, with boreholes
placed in a triangular pattern.
Parameters
----------
N_1 : int
Number of borehole in the x direction.
N_2 : int
Number of borehole in the y direction.
B_1 : float
Distance (in meters) between adjacent boreholes in the x direction.
B_2 : float
Distance (in meters) between adjacent boreholes in the y direction.
H : float
Borehole length (in meters).
D : float
Borehole buried depth (in meters).
r_b : float
Borehole radius (in meters).
include_last_borehole : bool
True if each row of boreholes should have equal lengths. False, if the uneven rows have one borehole less
so they are contained within the imaginary 'box' around the borefield
tilt : float, optional
Angle (in radians) from vertical of the axis of the borehole. The
orientation of the tilt is orthogonal to the origin coordinate.
Default is 0.
origin : tuple, optional
A coordinate indicating the origin of reference for orientation of
boreholes.
Default is the center of the rectangle.
Returns
-------
boreField : list of Borehole objects
List of boreholes in the rectangular bore field.
Notes
-----
Boreholes located at the origin will remain vertical.
Examples
--------
>>> boreField = gt.boreholes.rectangle_field(N_1=3, N_2=2, B_1=5., B_2=5.,
H=100., D=2.5, r_b=0.05)
The bore field is constructed line by line. For N_1=3 and N_2=3, the bore
field layout is as follows, if `include_last_borehole` is True::
6 7 8
3 4 5
0 1 2
and if `include_last_borehole` is False::
5 6 7
3 4
0 1 2
"""
borefield = []
if origin is None:
# When no origin is supplied, compute the origin to be at the center of
# the rectangle
x0 = (N_1 - 1) / 2 * B_1
y0 = (N_2 - 1) / 2 * B_2
else:
x0, y0 = origin
for j in range(N_2):
for i in range(N_1):
x = i * B_1 + (B_1 / 2 if j % 2 == 1 else 0)
y = j * B_2
# The borehole is inclined only if it does not lie on the origin
if np.sqrt((x - x0)**2 + (y - y0)**2) > r_b:
orientation = np.arctan2(y - y0, x - x0)
if i < (N_1 - 1) or include_last_borehole or (j % 2 == 0):
borefield.append(
Borehole(
H, D, r_b, x, y, tilt=tilt, orientation=orientation))
else:
if i < (N_1 - 1) or include_last_borehole or (j % 2 == 0):
borefield.append(Borehole(H, D, r_b, x, y))
return borefield
def dense_rectangle_field(N_1, N_2, B, H, D, r_b, include_last_borehole, tilt=0., origin=None):
"""
Build a list of boreholes in a rectangular bore field configuration, with boreholes
placed in a hexagonal pattern.
Parameters
----------
N_1 : int
Number of borehole in the x direction.
N_2 : int
Number of borehole in the y direction.
B_1 : float
Distance (in meters) between adjacent boreholes.
H : float
Borehole length (in meters).
D : float
Borehole buried depth (in meters).
r_b : float
Borehole radius (in meters).
include_last_borehole : bool
True if each row of boreholes should have equal lengths. False, if the uneven rows have one borehole less
so they are contained within the imaginary 'box' around the borefield
tilt : float, optional
Angle (in radians) from vertical of the axis of the borehole. The
orientation of the tilt is orthogonal to the origin coordinate.
Default is 0.
origin : tuple, optional
A coordinate indicating the origin of reference for orientation of
boreholes.
Default is the center of the rectangle.
Returns
-------
boreField : list of Borehole objects
List of boreholes in the rectangular bore field.
Notes
-----
Boreholes located at the origin will remain vertical.
Examples
--------
>>> boreField = gt.boreholes.rectangle_field(N_1=3, N_2=2, B_1=5., B_2=5.,
H=100., D=2.5, r_b=0.05, include_last_borehole=True)
The bore field is constructed line by line. For N_1=3 and N_2=3, the bore
field layout is as follows, if `include_last_borehole` is True::
6 7 8
3 4 5
0 1 2
and if `include_last_borehole` is False::
5 6 7
3 4
0 1 2
"""
borefield = rectangle_field_triangular(N_1, N_2, B, sqrt(3)/2 * B, H, D, r_b, include_last_borehole, tilt=tilt, origin=origin)
return borefield
Hi @tblanke
Good idea indeed!
@MassimoCimmino I will implement the code you wrote above and add some tests.
Best, Wouter
@all-contributors please add @wouterpeere for code, ideas, bug. please add @tblanke for code, ideas, bug.
@MassimoCimmino
I've put up a pull request to add @wouterpeere! :tada:
I've put up a pull request to add @tblanke! :tada:
@all-contributors please add @tblanke for code, ideas, bug.
@MassimoCimmino
I've put up a pull request to add @tblanke! :tada:
I wrote another function to create a 'dens borefield' where the boreholes are packed as densely as possible with a minimum distance B. Instead of creating a regular grid, where all the boreholes are underneath each other, this function moves every uneven row to the right, so it's y-coordinate can be lower.
This creates borefields like: