Skip to content

Commit 50ea38e

Browse files
authored
Merge pull request #193 from MiraGeoscience/GEOPY-2794
GEOPY-2794: Change reference of Plate from center-center to top-center
2 parents 2162766 + 5f6450d commit 50ea38e

2 files changed

Lines changed: 99 additions & 29 deletions

File tree

geoapps_utils/modelling/plates.py

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# '
99
# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1010

11+
1112
import numpy as np
1213
from geoh5py import Workspace
1314
from geoh5py.objects import Octree, Surface
@@ -26,12 +27,19 @@ class PlateModel(BaseModel):
2627
"""
2728
Parameters describing the position and orientation of a dipping plate.
2829
30+
Dip rotations are applied about the plate's origin (easting, northing,
31+
and elevation) so that the origin of a dipping plate marks the center
32+
of the plate's top (up-dip) face. Without dip rotations, the plate is
33+
horizontal striking east-west with the origin at the center of the
34+
southern face.
35+
36+
2937
:param strike_length: Length of the plate in the strike direction.
3038
:param dip_length: Length of the plate in the dip direction.
3139
:param width: Width of the plate.
32-
:param easting: Easting of the plate center.
33-
:param northing: Northing of the plate center.
34-
:param elevation: Elevation of the plate center.
40+
:param easting: Easting of the center of the plate's top face.
41+
:param northing: Northing of the center of the plate's top face.
42+
:param elevation: Elevation of the center of the plate's top face.
3543
:param direction: Dip direction of the plate in degrees from North.
3644
:param dip: Dip angle of the plate in degrees below the horizontal.
3745
"""
@@ -41,9 +49,9 @@ class PlateModel(BaseModel):
4149
strike_length: float
4250
dip_length: float
4351
width: float
44-
easting: float
45-
northing: float
46-
elevation: float
52+
easting: float = 0.0
53+
northing: float = 0.0
54+
elevation: float = 0.0
4755
direction: float = Field(default=0.0, alias="dip_direction")
4856
dip: float = 0.0
4957

@@ -63,6 +71,15 @@ def __init__(self, params: PlateModel):
6371
self.params = params
6472

6573
def mask(self, mesh: Octree) -> np.ndarray:
74+
"""
75+
Return a mask for generating models with a plate anomaly.
76+
77+
:param mesh: Octree mesh object defining cell centers on which
78+
the mask will be defined.
79+
80+
:return Boolean mask that can be applied to models on the cell
81+
centers of the input mesh
82+
"""
6683
rotations = [
6784
z_rotation_matrix(np.deg2rad(self.params.direction)),
6885
x_rotation_matrix(np.deg2rad(self.params.dip)),
@@ -114,12 +131,12 @@ def triangles(self) -> np.ndarray:
114131
def vertices(self) -> np.ndarray:
115132
"""Vertices for triangulation of a rectangular prism in 3D space."""
116133

117-
u_1 = self.params.origin[0] - (self.params.strike_length / 2.0)
118-
u_2 = self.params.origin[0] + (self.params.strike_length / 2.0)
119-
v_1 = self.params.origin[1] - (self.params.dip_length / 2.0)
120-
v_2 = self.params.origin[1] + (self.params.dip_length / 2.0)
121-
w_1 = self.params.origin[2] - (self.params.width / 2.0)
122-
w_2 = self.params.origin[2] + (self.params.width / 2.0)
134+
u_1, u_2, v_1, v_2, w_1, w_2 = bounding_box(
135+
origin=list(self.params.origin),
136+
strike_length=self.params.strike_length,
137+
dip_length=self.params.dip_length,
138+
width=self.params.width,
139+
)
123140

124141
vertices = np.array(
125142
[
@@ -145,24 +162,53 @@ def _rotate(self, vertices: np.ndarray) -> np.ndarray:
145162
return rotated_vertices
146163

147164

165+
def bounding_box(
166+
origin: list[float], strike_length: float, dip_length: float, width: float
167+
) -> list[float]:
168+
"""
169+
Calculate unrotated bounding box from plate geometry.
170+
171+
:param origin: Southern face of an east-west striking horizontal plate.
172+
:param strike_length: Length of the plate in the strike (x) dimension.
173+
:param dip_length: Length of the plate in the (0) dip (y) dimension.
174+
:param width: Width of the plate (z dimension).
175+
"""
176+
xmin = origin[0] - strike_length / 2
177+
xmax = origin[0] + strike_length / 2
178+
ymin = origin[1]
179+
ymax = origin[1] + dip_length
180+
zmin = origin[2] - width / 2
181+
zmax = origin[2] + width / 2
182+
183+
return [xmin, xmax, ymin, ymax, zmin, zmax]
184+
185+
148186
def inside_plate(
149187
points: np.ndarray,
150188
plate: PlateModel,
151189
) -> np.ndarray:
152190
"""
153-
Create a plate model at a set of points from background, anomaly and size.
191+
Create a mask to identify input points located inside the parameterized plate.
192+
193+
The plate is treated as orthogonal to the coordinate axes, and any rotation
194+
parameters in the PlateModel are ignored. For rotated plates, create or wrap a
195+
Plate from the plate parameters and use Plate.mask instead.
196+
197+
The plate is treated as orthogonal to the coordinate axes, and any rotation
198+
parameters in the PlateModel are ignored. For masking rotated plates, consider
199+
constructing a Plate object to use it's mask method.
154200
155201
:param points: Array of shape (n, 3) representing the x, y, z coordinates of the
156202
model space (often the cell centers of a mesh).
157203
:param plate: Dipping plate parameters.
158204
"""
159205

160-
xmin = plate.origin[0] - plate.strike_length / 2
161-
xmax = plate.origin[0] + plate.strike_length / 2
162-
ymin = plate.origin[1] - plate.dip_length / 2
163-
ymax = plate.origin[1] + plate.dip_length / 2
164-
zmin = plate.origin[2] - plate.width / 2
165-
zmax = plate.origin[2] + plate.width / 2
206+
xmin, xmax, ymin, ymax, zmin, zmax = bounding_box(
207+
origin=list(plate.origin),
208+
strike_length=plate.strike_length,
209+
dip_length=plate.dip_length,
210+
width=plate.width,
211+
)
166212

167213
mask = (
168214
(points[:, 0] >= xmin)
@@ -183,7 +229,7 @@ def make_plate(
183229
anomaly: float = 1.0,
184230
):
185231
"""
186-
Create a plate model at a set of points from background, anomaly, size and attitude.
232+
Create a plate model at a set of points from background, anomaly, size and geometry.
187233
188234
:param points: Array of shape (n, 3) representing the x, y, z coordinates of the
189235
model space (often the cell centers of a mesh).

tests/plates_test.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,31 @@
1414
from geoh5py import Workspace
1515
from geoh5py.objects import BlockModel
1616

17-
from geoapps_utils.modelling.plates import PlateModel, inside_plate, make_plate
17+
from geoapps_utils.modelling.plates import (
18+
PlateModel,
19+
bounding_box,
20+
inside_plate,
21+
make_plate,
22+
)
23+
24+
25+
def test_bounding_box():
26+
origin = [0.0, 0.0, 0.0]
27+
strike_length = 10
28+
dip_length = 20
29+
width = 2
30+
xmin, xmax, ymin, ymax, zmin, zmax = bounding_box(
31+
origin=origin,
32+
strike_length=strike_length,
33+
dip_length=dip_length,
34+
width=width,
35+
)
36+
assert xmin == -5.0
37+
assert xmax == 5.0
38+
assert ymin == 0.0
39+
assert ymax == 20.0
40+
assert zmin == -1.0
41+
assert zmax == 1.0
1842

1943

2044
def test_inside_plate(tmp_path):
@@ -48,8 +72,8 @@ def test_inside_plate(tmp_path):
4872
validation_mask = (
4973
(grid.centroids[:, 0] >= ((-strike_length / 2) + 1))
5074
& (grid.centroids[:, 0] <= ((strike_length / 2) + 1))
51-
& (grid.centroids[:, 1] >= -dip_length / 2)
52-
& (grid.centroids[:, 1] <= dip_length / 2)
75+
& (grid.centroids[:, 1] >= 0)
76+
& (grid.centroids[:, 1] <= dip_length)
5377
& (grid.centroids[:, 2] >= -width / 2)
5478
& (grid.centroids[:, 2] <= width / 2)
5579
)
@@ -90,8 +114,8 @@ def test_make_plate(tmp_path):
90114
grid.add_data({"plate model 1": {"values": model}})
91115

92116
mask = (
93-
(grid.centroids[:, 0] >= (-dip_length / 2))
94-
& (grid.centroids[:, 0] <= (dip_length / 2))
117+
(grid.centroids[:, 0] >= 0)
118+
& (grid.centroids[:, 0] <= dip_length)
95119
& (grid.centroids[:, 1] >= (-strike_length / 2))
96120
& (grid.centroids[:, 1] <= (strike_length / 2))
97121
& (grid.centroids[:, 2] >= (-width / 2))
@@ -122,8 +146,8 @@ def test_make_plate(tmp_path):
122146
& (grid.centroids[:, 0] <= (strike_length / 2))
123147
& (grid.centroids[:, 1] >= (-width / 2))
124148
& (grid.centroids[:, 1] <= (width / 2))
125-
& (grid.centroids[:, 2] >= (-dip_length / 2))
126-
& (grid.centroids[:, 2] <= (dip_length / 2))
149+
& (grid.centroids[:, 2] >= -dip_length)
150+
& (grid.centroids[:, 2] <= 0)
127151
)
128152
assert np.all(model[mask] == 1.0)
129153

@@ -168,8 +192,8 @@ def test_make_plate_multiple(tmp_path):
168192
& (grid.centroids[:, 0] <= (width))
169193
& (grid.centroids[:, 1] >= (-strike_length / 2))
170194
& (grid.centroids[:, 1] <= (strike_length / 2))
171-
& (grid.centroids[:, 2] >= (-dip_length / 2))
172-
& (grid.centroids[:, 2] <= (dip_length / 2))
195+
& (grid.centroids[:, 2] >= -dip_length)
196+
& (grid.centroids[:, 2] <= 0)
173197
)
174198
assert np.all(model[mask] == 1.0)
175199

0 commit comments

Comments
 (0)