LoopStructural 1.6.1__py3-none-any.whl → 1.6.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of LoopStructural might be problematic. Click here for more details.
- LoopStructural/datatypes/_bounding_box.py +77 -7
- LoopStructural/datatypes/_point.py +67 -7
- LoopStructural/datatypes/_structured_grid.py +17 -0
- LoopStructural/datatypes/_surface.py +17 -0
- LoopStructural/export/omf_wrapper.py +49 -21
- LoopStructural/interpolators/__init__.py +13 -0
- LoopStructural/interpolators/_api.py +81 -13
- LoopStructural/interpolators/_builders.py +141 -141
- LoopStructural/interpolators/_discrete_fold_interpolator.py +11 -4
- LoopStructural/interpolators/_discrete_interpolator.py +100 -53
- LoopStructural/interpolators/_finite_difference_interpolator.py +78 -88
- LoopStructural/interpolators/_geological_interpolator.py +27 -10
- LoopStructural/interpolators/_interpolator_builder.py +55 -0
- LoopStructural/interpolators/_interpolator_factory.py +7 -18
- LoopStructural/interpolators/_p1interpolator.py +3 -3
- LoopStructural/interpolators/_surfe_wrapper.py +42 -12
- LoopStructural/interpolators/supports/_2d_base_unstructured.py +16 -0
- LoopStructural/interpolators/supports/_2d_structured_grid.py +44 -9
- LoopStructural/interpolators/supports/_3d_base_structured.py +28 -7
- LoopStructural/interpolators/supports/_3d_structured_grid.py +38 -12
- LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
- LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +8 -2
- LoopStructural/interpolators/supports/__init__.py +7 -0
- LoopStructural/interpolators/supports/_base_support.py +7 -0
- LoopStructural/modelling/__init__.py +1 -3
- LoopStructural/modelling/core/geological_model.py +11 -12
- LoopStructural/modelling/features/__init__.py +1 -0
- LoopStructural/modelling/features/_analytical_feature.py +48 -18
- LoopStructural/modelling/features/_base_geological_feature.py +37 -8
- LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
- LoopStructural/modelling/features/_geological_feature.py +50 -12
- LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
- LoopStructural/modelling/features/_structural_frame.py +16 -18
- LoopStructural/modelling/features/_unconformity_feature.py +3 -3
- LoopStructural/modelling/features/builders/_base_builder.py +8 -0
- LoopStructural/modelling/features/builders/_folded_feature_builder.py +47 -16
- LoopStructural/modelling/features/builders/_geological_feature_builder.py +29 -13
- LoopStructural/modelling/features/builders/_structural_frame_builder.py +7 -2
- LoopStructural/modelling/features/fault/__init__.py +1 -1
- LoopStructural/modelling/features/fault/_fault_function.py +19 -1
- LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
- LoopStructural/modelling/features/fault/_fault_segment.py +50 -53
- LoopStructural/modelling/features/fold/__init__.py +1 -2
- LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -23
- LoopStructural/modelling/features/fold/_foldframe.py +4 -4
- LoopStructural/modelling/features/fold/_svariogram.py +81 -46
- LoopStructural/modelling/features/fold/fold_function/__init__.py +27 -0
- LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +253 -0
- LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +153 -0
- LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +46 -0
- LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +151 -0
- LoopStructural/modelling/input/process_data.py +47 -26
- LoopStructural/modelling/input/project_file.py +49 -23
- LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
- LoopStructural/utils/__init__.py +1 -0
- LoopStructural/utils/_surface.py +18 -6
- LoopStructural/utils/_transformation.py +98 -14
- LoopStructural/utils/colours.py +50 -0
- LoopStructural/utils/features.py +5 -0
- LoopStructural/utils/maths.py +53 -1
- LoopStructural/version.py +1 -1
- LoopStructural-1.6.6.dist-info/METADATA +160 -0
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/RECORD +66 -59
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/WHEEL +1 -1
- LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
- LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -149
- LoopStructural-1.6.1.dist-info/METADATA +0 -81
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/LICENSE +0 -0
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/top_level.txt +0 -0
|
@@ -8,8 +8,6 @@ from ..utils import get_vectors
|
|
|
8
8
|
from ._discrete_interpolator import DiscreteInterpolator
|
|
9
9
|
from ..interpolators import InterpolatorType
|
|
10
10
|
|
|
11
|
-
from ._operator import Operator
|
|
12
|
-
|
|
13
11
|
from LoopStructural.utils import getLogger
|
|
14
12
|
|
|
15
13
|
logger = getLogger(__name__)
|
|
@@ -26,13 +24,11 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
26
24
|
"""
|
|
27
25
|
self.shape = "rectangular"
|
|
28
26
|
DiscreteInterpolator.__init__(self, grid, data=data)
|
|
29
|
-
# default weights for the interpolation matrix are 1 in x,y,z and
|
|
30
|
-
# 1/
|
|
31
27
|
self.set_interpolation_weights(
|
|
32
28
|
{
|
|
33
|
-
"dxy": 0
|
|
34
|
-
"dyz": 0
|
|
35
|
-
"dxz": 0
|
|
29
|
+
"dxy": 1.0,
|
|
30
|
+
"dyz": 1.0,
|
|
31
|
+
"dxz": 1.0,
|
|
36
32
|
"dxx": 1.0,
|
|
37
33
|
"dyy": 1.0,
|
|
38
34
|
"dzz": 1.0,
|
|
@@ -47,7 +43,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
47
43
|
}
|
|
48
44
|
)
|
|
49
45
|
|
|
50
|
-
# grid.step_vector[2]
|
|
51
46
|
self.type = InterpolatorType.FINITE_DIFFERENCE
|
|
52
47
|
|
|
53
48
|
def setup_interpolator(self, **kwargs):
|
|
@@ -88,43 +83,24 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
88
83
|
self.interpolation_weights["dyy"] = 0.1 * kwargs["regularisation"]
|
|
89
84
|
self.interpolation_weights["dzz"] = 0.1 * kwargs["regularisation"]
|
|
90
85
|
self.interpolation_weights[key] = kwargs[key]
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
weight = (
|
|
99
|
-
self.interpolation_weights["dxy"] / 4
|
|
100
|
-
) # (4*self.support.step_vector[0]*self.support.step_vector[1])
|
|
101
|
-
self.assemble_inner(operator, weight)
|
|
102
|
-
operator = Operator.Dyz_mask
|
|
103
|
-
weight = (
|
|
104
|
-
self.interpolation_weights["dyz"] / 4
|
|
105
|
-
) # (4*self.support.step_vector[1]*self.support.step_vector[2])
|
|
106
|
-
self.assemble_inner(operator, weight)
|
|
107
|
-
operator = Operator.Dxz_mask
|
|
108
|
-
weight = (
|
|
109
|
-
self.interpolation_weights["dxz"] / 4
|
|
110
|
-
) # (4*self.support.step_vector[0]*self.support.step_vector[2])
|
|
111
|
-
self.assemble_inner(operator, weight)
|
|
112
|
-
operator = Operator.Dxx_mask
|
|
113
|
-
weight = self.interpolation_weights["dxx"] / 1 # self.support.step_vector[0]**2
|
|
114
|
-
self.assemble_inner(operator, weight)
|
|
115
|
-
operator = Operator.Dyy_mask
|
|
116
|
-
weight = self.interpolation_weights["dyy"] / 1 # self.support.step_vector[1]**2
|
|
117
|
-
self.assemble_inner(operator, weight)
|
|
118
|
-
operator = Operator.Dzz_mask
|
|
119
|
-
weight = self.interpolation_weights["dzz"] / 1 # self.support.step_vector[2]**2
|
|
120
|
-
self.assemble_inner(operator, weight)
|
|
86
|
+
# either use the default operators or the ones passed to the function
|
|
87
|
+
operators = kwargs.get(
|
|
88
|
+
"operators", self.support.get_operators(weights=self.interpolation_weights)
|
|
89
|
+
)
|
|
90
|
+
for k, o in operators.items():
|
|
91
|
+
self.assemble_inner(o[0], o[1], name=k)
|
|
92
|
+
|
|
121
93
|
self.add_norm_constraints(self.interpolation_weights["npw"])
|
|
122
94
|
self.add_gradient_constraints(self.interpolation_weights["gpw"])
|
|
123
95
|
self.add_value_constraints(self.interpolation_weights["cpw"])
|
|
124
96
|
self.add_tangent_constraints(self.interpolation_weights["tpw"])
|
|
125
97
|
self.add_interface_constraints(self.interpolation_weights["ipw"])
|
|
126
98
|
self.add_value_inequality_constraints()
|
|
127
|
-
self.add_inequality_pairs_constraints(
|
|
99
|
+
self.add_inequality_pairs_constraints(
|
|
100
|
+
pairs=kwargs.get('inequality_pairs', None),
|
|
101
|
+
upper_bound=kwargs.get('inequality_pair_upper_bound', np.finfo(float).eps),
|
|
102
|
+
lower_bound=kwargs.get('inequality_pair_lower_bound', -np.inf),
|
|
103
|
+
)
|
|
128
104
|
|
|
129
105
|
def copy(self):
|
|
130
106
|
"""
|
|
@@ -167,9 +143,9 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
167
143
|
# a/=self.support.enp.product(self.support.step_vector)
|
|
168
144
|
self.add_constraints_to_least_squares(
|
|
169
145
|
a,
|
|
170
|
-
points[inside,
|
|
146
|
+
points[inside, self.support.dimension],
|
|
171
147
|
idc[inside, :],
|
|
172
|
-
w=w * points[inside,
|
|
148
|
+
w=w * points[inside, self.support.dimension + 1],
|
|
173
149
|
name="value",
|
|
174
150
|
)
|
|
175
151
|
if np.sum(inside) <= 0:
|
|
@@ -196,18 +172,25 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
196
172
|
# get elements for points
|
|
197
173
|
points = self.get_interface_constraints()
|
|
198
174
|
if points.shape[0] > 1:
|
|
199
|
-
|
|
175
|
+
node_idx, inside = self.support.position_to_cell_corners(
|
|
200
176
|
points[:, : self.support.dimension]
|
|
201
177
|
)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
idc =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
178
|
+
gi = np.zeros(self.support.n_nodes, dtype=int)
|
|
179
|
+
gi[:] = -1
|
|
180
|
+
gi[self.region] = np.arange(0, self.nx, dtype=int)
|
|
181
|
+
idc = np.zeros(node_idx.shape).astype(int)
|
|
182
|
+
idc[:] = -1
|
|
183
|
+
idc[inside, :] = gi[node_idx[inside, :]]
|
|
184
|
+
inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
|
|
185
|
+
idc = idc[inside, :]
|
|
186
|
+
A = self.support.position_to_dof_coefs(points[inside, : self.support.dimension])
|
|
187
|
+
for unique_id in np.unique(
|
|
188
|
+
points[
|
|
189
|
+
np.logical_and(~np.isnan(points[:, self.support.dimension]), inside),
|
|
190
|
+
self.support.dimension,
|
|
191
|
+
]
|
|
192
|
+
):
|
|
193
|
+
mask = points[inside, self.support.dimension] == unique_id
|
|
211
194
|
ij = np.array(
|
|
212
195
|
np.meshgrid(
|
|
213
196
|
np.arange(0, A[mask, :].shape[0]),
|
|
@@ -216,7 +199,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
216
199
|
).T.reshape(-1, 2)
|
|
217
200
|
interface_A = np.hstack([A[mask, :][ij[:, 0], :], -A[mask, :][ij[:, 1], :]])
|
|
218
201
|
interface_idc = np.hstack([idc[mask, :][ij[:, 0], :], idc[mask, :][ij[:, 1], :]])
|
|
219
|
-
|
|
220
202
|
# now map the index from global to region create array size of mesh
|
|
221
203
|
# initialise as np.nan, then map points inside region to 0->nx
|
|
222
204
|
gi = np.zeros(self.support.n_nodes).astype(int)
|
|
@@ -248,9 +230,10 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
248
230
|
points = self.get_gradient_constraints()
|
|
249
231
|
if points.shape[0] > 0:
|
|
250
232
|
# calculate unit vector for orientation data
|
|
251
|
-
# points[:,3:]/=np.linalg.norm(points[:,3:],axis=1)[:,None]
|
|
252
233
|
|
|
253
|
-
node_idx, inside = self.support.position_to_cell_corners(
|
|
234
|
+
node_idx, inside = self.support.position_to_cell_corners(
|
|
235
|
+
points[:, : self.support.dimension]
|
|
236
|
+
)
|
|
254
237
|
# calculate unit vector for node gradients
|
|
255
238
|
# this means we are only constraining direction of grad not the
|
|
256
239
|
# magnitude
|
|
@@ -267,13 +250,22 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
267
250
|
T,
|
|
268
251
|
elements,
|
|
269
252
|
inside_,
|
|
270
|
-
) = self.support.get_element_gradient_for_location(
|
|
253
|
+
) = self.support.get_element_gradient_for_location(
|
|
254
|
+
points[inside, : self.support.dimension]
|
|
255
|
+
)
|
|
271
256
|
# normalise constraint vector and scale element matrix by this
|
|
272
|
-
norm = np.linalg.norm(
|
|
257
|
+
norm = np.linalg.norm(
|
|
258
|
+
points[:, self.support.dimension : self.support.dimension + self.support.dimension],
|
|
259
|
+
axis=1,
|
|
260
|
+
)
|
|
273
261
|
points[:, 3:6] /= norm[:, None]
|
|
274
262
|
T /= norm[inside, None, None]
|
|
275
263
|
# calculate two orthogonal vectors to constraint (strike and dip vector)
|
|
276
|
-
strike_vector, dip_vector = get_vectors(
|
|
264
|
+
strike_vector, dip_vector = get_vectors(
|
|
265
|
+
points[
|
|
266
|
+
inside, self.support.dimension : self.support.dimension + self.support.dimension
|
|
267
|
+
]
|
|
268
|
+
)
|
|
277
269
|
A = np.einsum("ij,ijk->ik", strike_vector.T, T)
|
|
278
270
|
B = np.zeros(points[inside, :].shape[0])
|
|
279
271
|
self.add_constraints_to_least_squares(A, B, idc[inside, :], w=w, name="gradient")
|
|
@@ -302,7 +294,9 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
302
294
|
if points.shape[0] > 0:
|
|
303
295
|
# calculate unit vector for orientation data
|
|
304
296
|
# points[:,3:]/=np.linalg.norm(points[:,3:],axis=1)[:,None]
|
|
305
|
-
node_idx, inside = self.support.position_to_cell_corners(
|
|
297
|
+
node_idx, inside = self.support.position_to_cell_corners(
|
|
298
|
+
points[:, : self.support.dimension]
|
|
299
|
+
)
|
|
306
300
|
gi = np.zeros(self.support.n_nodes)
|
|
307
301
|
gi[:] = -1
|
|
308
302
|
gi[self.region] = np.arange(0, self.nx)
|
|
@@ -319,31 +313,23 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
319
313
|
T,
|
|
320
314
|
elements,
|
|
321
315
|
inside_,
|
|
322
|
-
) = self.support.get_element_gradient_for_location(
|
|
316
|
+
) = self.support.get_element_gradient_for_location(
|
|
317
|
+
points[inside, : self.support.dimension]
|
|
318
|
+
)
|
|
323
319
|
# T*=np.product(self.support.step_vector)
|
|
324
320
|
# T/=self.support.step_vector[0]
|
|
321
|
+
|
|
325
322
|
w /= 3
|
|
326
|
-
self.
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
idc[inside, :],
|
|
337
|
-
w=w,
|
|
338
|
-
name="norm",
|
|
339
|
-
)
|
|
340
|
-
self.add_constraints_to_least_squares(
|
|
341
|
-
T[:, 2, :],
|
|
342
|
-
points[inside, 5],
|
|
343
|
-
idc[inside, :],
|
|
344
|
-
w=w,
|
|
345
|
-
name="norm",
|
|
346
|
-
)
|
|
323
|
+
for d in range(self.support.dimension):
|
|
324
|
+
|
|
325
|
+
self.add_constraints_to_least_squares(
|
|
326
|
+
T[:, d, :],
|
|
327
|
+
points[inside, self.support.dimension + d],
|
|
328
|
+
idc[inside, :],
|
|
329
|
+
w=w,
|
|
330
|
+
name=f"norm_{d}",
|
|
331
|
+
)
|
|
332
|
+
|
|
347
333
|
if np.sum(inside) <= 0:
|
|
348
334
|
logger.warning(
|
|
349
335
|
f"{np.sum(~inside)} \
|
|
@@ -356,7 +342,7 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
356
342
|
points: np.ndarray,
|
|
357
343
|
vectors: np.ndarray,
|
|
358
344
|
w: float = 1.0,
|
|
359
|
-
|
|
345
|
+
b: float = 0,
|
|
360
346
|
name="gradient orthogonal",
|
|
361
347
|
):
|
|
362
348
|
"""
|
|
@@ -378,7 +364,9 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
378
364
|
if points.shape[0] > 0:
|
|
379
365
|
|
|
380
366
|
# calculate unit vector for orientation data
|
|
381
|
-
node_idx, inside = self.support.position_to_cell_corners(
|
|
367
|
+
node_idx, inside = self.support.position_to_cell_corners(
|
|
368
|
+
points[:, : self.support.dimension]
|
|
369
|
+
)
|
|
382
370
|
# calculate unit vector for node gradients
|
|
383
371
|
# this means we are only constraining direction of grad not the
|
|
384
372
|
# magnitude
|
|
@@ -400,13 +388,15 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
400
388
|
T,
|
|
401
389
|
elements,
|
|
402
390
|
inside_,
|
|
403
|
-
) = self.support.get_element_gradient_for_location(
|
|
391
|
+
) = self.support.get_element_gradient_for_location(
|
|
392
|
+
points[inside, : self.support.dimension]
|
|
393
|
+
)
|
|
404
394
|
T[norm > 0, :, :] /= norm[norm > 0, None, None]
|
|
405
395
|
|
|
406
396
|
# dot product of vector and element gradient = 0
|
|
407
|
-
A = np.einsum("ij,ijk->ik", vectors[inside, :
|
|
408
|
-
|
|
409
|
-
self.add_constraints_to_least_squares(A,
|
|
397
|
+
A = np.einsum("ij,ijk->ik", vectors[inside, : self.support.dimension], T)
|
|
398
|
+
b_ = np.zeros(points[inside, :].shape[0]) + b
|
|
399
|
+
self.add_constraints_to_least_squares(A, b_, idc[inside, :], w=w, name=name)
|
|
410
400
|
|
|
411
401
|
if np.sum(inside) <= 0:
|
|
412
402
|
logger.warning(
|
|
@@ -432,7 +422,7 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
432
422
|
|
|
433
423
|
# def assemble_borders(self, operator, w):
|
|
434
424
|
|
|
435
|
-
def assemble_inner(self, operator, w):
|
|
425
|
+
def assemble_inner(self, operator, w, name='regularisation'):
|
|
436
426
|
"""
|
|
437
427
|
|
|
438
428
|
Parameters
|
|
@@ -465,6 +455,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
465
455
|
B[inside],
|
|
466
456
|
idc[inside, :],
|
|
467
457
|
w=w,
|
|
468
|
-
name=
|
|
458
|
+
name=name,
|
|
469
459
|
)
|
|
470
460
|
return
|
|
@@ -7,6 +7,7 @@ from LoopStructural.utils.exceptions import LoopTypeError
|
|
|
7
7
|
from ..interpolators import InterpolatorType
|
|
8
8
|
import numpy as np
|
|
9
9
|
|
|
10
|
+
from typing import Optional
|
|
10
11
|
from ..utils import getLogger
|
|
11
12
|
|
|
12
13
|
logger = getLogger(__name__)
|
|
@@ -42,6 +43,8 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
42
43
|
self.constraints = []
|
|
43
44
|
self.__str = "Base Geological Interpolator"
|
|
44
45
|
self.valid = True
|
|
46
|
+
self.dimensions = 3 # default to 3d
|
|
47
|
+
self.support = None
|
|
45
48
|
|
|
46
49
|
@property
|
|
47
50
|
def data(self):
|
|
@@ -106,9 +109,9 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
106
109
|
|
|
107
110
|
"""
|
|
108
111
|
points = self.check_array(points)
|
|
109
|
-
if points.shape[1] ==
|
|
112
|
+
if points.shape[1] == self.dimensions + 1:
|
|
110
113
|
points = np.hstack([points, np.ones((points.shape[0], 1))])
|
|
111
|
-
if points.shape[1] <
|
|
114
|
+
if points.shape[1] < self.dimensions + 2:
|
|
112
115
|
raise ValueError("Value points must at least have X,Y,Z,val,w")
|
|
113
116
|
self.data["value"] = points
|
|
114
117
|
self.n_i = points.shape[0]
|
|
@@ -127,9 +130,9 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
127
130
|
-------
|
|
128
131
|
|
|
129
132
|
"""
|
|
130
|
-
if points.shape[1] ==
|
|
133
|
+
if points.shape[1] == self.dimensions * 2:
|
|
131
134
|
points = np.hstack([points, np.ones((points.shape[0], 1))])
|
|
132
|
-
if points.shape[1] <
|
|
135
|
+
if points.shape[1] < self.dimensions * 2 + 1:
|
|
133
136
|
raise ValueError("Gradient constraints must at least have X,Y,Z,gx,gy,gz")
|
|
134
137
|
self.n_g = points.shape[0]
|
|
135
138
|
self.data["gradient"] = points
|
|
@@ -148,9 +151,9 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
148
151
|
-------
|
|
149
152
|
|
|
150
153
|
"""
|
|
151
|
-
if points.shape[1] ==
|
|
154
|
+
if points.shape[1] == self.dimensions * 2:
|
|
152
155
|
points = np.hstack([points, np.ones((points.shape[0], 1))])
|
|
153
|
-
if points.shape[1] <
|
|
156
|
+
if points.shape[1] < self.dimensions * 2 + 1:
|
|
154
157
|
raise ValueError("Nonrmal constraints must at least have X,Y,Z,nx,ny,nz")
|
|
155
158
|
self.n_n = points.shape[0]
|
|
156
159
|
self.data["normal"] = points
|
|
@@ -169,9 +172,9 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
169
172
|
-------
|
|
170
173
|
|
|
171
174
|
"""
|
|
172
|
-
if points.shape[1] ==
|
|
175
|
+
if points.shape[1] == self.dimensions * 2:
|
|
173
176
|
points = np.hstack([points, np.ones((points.shape[0], 1))])
|
|
174
|
-
if points.shape[1] <
|
|
177
|
+
if points.shape[1] < self.dimensions * 2 + 1:
|
|
175
178
|
raise ValueError("Tangent constraints must at least have X,Y,Z,tx,ty,tz")
|
|
176
179
|
self.data["tangent"] = points
|
|
177
180
|
self.up_to_date = False
|
|
@@ -181,13 +184,13 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
181
184
|
self.up_to_date = False
|
|
182
185
|
|
|
183
186
|
def set_value_inequality_constraints(self, points: np.ndarray):
|
|
184
|
-
if points.shape[1] <
|
|
187
|
+
if points.shape[1] < self.dimensions + 2:
|
|
185
188
|
raise ValueError("Inequality constraints must at least have X,Y,Z,lower,upper")
|
|
186
189
|
self.data["inequality"] = points
|
|
187
190
|
self.up_to_date = False
|
|
188
191
|
|
|
189
192
|
def set_inequality_pairs_constraints(self, points: np.ndarray):
|
|
190
|
-
if points.shape[1] <
|
|
193
|
+
if points.shape[1] < self.dimensions + 1:
|
|
191
194
|
raise ValueError("Inequality pairs constraints must at least have X,Y,Z,rock_id")
|
|
192
195
|
|
|
193
196
|
self.data["inequality_pairs"] = points
|
|
@@ -313,6 +316,20 @@ class GeologicalInterpolator(metaclass=ABCMeta):
|
|
|
313
316
|
def add_interface_constraints(self, w: float = 1.0):
|
|
314
317
|
pass
|
|
315
318
|
|
|
319
|
+
@abstractmethod
|
|
320
|
+
def add_value_inequality_constraints(self, w: float = 1.0):
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
@abstractmethod
|
|
324
|
+
def add_inequality_pairs_constraints(
|
|
325
|
+
self,
|
|
326
|
+
w: float = 1.0,
|
|
327
|
+
upper_bound=np.finfo(float).eps,
|
|
328
|
+
lower_bound=-np.inf,
|
|
329
|
+
pairs: Optional[list] = None,
|
|
330
|
+
):
|
|
331
|
+
pass
|
|
332
|
+
|
|
316
333
|
def to_dict(self):
|
|
317
334
|
return {
|
|
318
335
|
"type": self.type,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from LoopStructural.interpolators import (
|
|
2
|
+
GeologicalInterpolator,
|
|
3
|
+
InterpolatorFactory,
|
|
4
|
+
InterpolatorType,
|
|
5
|
+
)
|
|
6
|
+
from LoopStructural.datatypes import BoundingBox
|
|
7
|
+
from typing import Optional, Union
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterpolatorBuilder:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
interpolatortype: Union[str, InterpolatorType],
|
|
15
|
+
bounding_box: BoundingBox,
|
|
16
|
+
nelements: int = 1000,
|
|
17
|
+
buffer: float = 0.2,
|
|
18
|
+
**kwargs,
|
|
19
|
+
):
|
|
20
|
+
self.interpolatortype = interpolatortype
|
|
21
|
+
self.bounding_box = bounding_box
|
|
22
|
+
self.nelements = nelements
|
|
23
|
+
self.buffer = buffer
|
|
24
|
+
self.kwargs = kwargs
|
|
25
|
+
self.interpolator : Optional[GeologicalInterpolator]= None
|
|
26
|
+
|
|
27
|
+
def create_interpolator(self) -> 'InterpolatorBuilder':
|
|
28
|
+
self.interpolator = InterpolatorFactory.create_interpolator(
|
|
29
|
+
interpolatortype=self.interpolatortype,
|
|
30
|
+
boundingbox=self.bounding_box,
|
|
31
|
+
nelements=self.nelements,
|
|
32
|
+
buffer=self.buffer,
|
|
33
|
+
**self.kwargs,
|
|
34
|
+
)
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
def set_value_constraints(self, value_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
38
|
+
if self.interpolator:
|
|
39
|
+
self.interpolator.set_value_constraints(value_constraints)
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def set_gradient_constraints(self, gradient_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
43
|
+
if self.interpolator:
|
|
44
|
+
self.interpolator.set_gradient_constraints(gradient_constraints)
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def set_normal_constraints(self, normal_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
48
|
+
if self.interpolator:
|
|
49
|
+
self.interpolator.set_normal_constraints(normal_constraints)
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def setup_interpolator(self, **kwargs) -> Optional[GeologicalInterpolator]:
|
|
53
|
+
if self.interpolator:
|
|
54
|
+
self.interpolator.setup(**kwargs)
|
|
55
|
+
return self.interpolator
|
|
@@ -65,25 +65,14 @@ class InterpolatorFactory:
|
|
|
65
65
|
gradient_norm_constraints: Optional[np.ndarray] = None,
|
|
66
66
|
gradient_constraints: Optional[np.ndarray] = None,
|
|
67
67
|
):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
raise ValueError("No bounding box specified")
|
|
72
|
-
if nelements is None:
|
|
73
|
-
raise ValueError("No number of elements specified")
|
|
74
|
-
if isinstance(interpolatortype, str):
|
|
75
|
-
interpolatortype = InterpolatorType._member_map_[interpolatortype].numerator
|
|
76
|
-
if support is None:
|
|
77
|
-
raise Exception("Support must be specified")
|
|
78
|
-
# supporttype = support_interpolator_map[interpolatortype]
|
|
79
|
-
# support = SupportFactory.create_support(
|
|
80
|
-
# supporttype, boundingbox, nelements, element_volume
|
|
81
|
-
# )
|
|
82
|
-
interpolator = interpolator_map[interpolatortype](support)
|
|
68
|
+
interpolator = InterpolatorFactory.create_interpolator(
|
|
69
|
+
interpolatortype, boundingbox, nelements, element_volume, support
|
|
70
|
+
)
|
|
83
71
|
if value_constraints is not None:
|
|
84
|
-
interpolator.
|
|
72
|
+
interpolator.set_value_constraints(value_constraints)
|
|
85
73
|
if gradient_norm_constraints is not None:
|
|
86
|
-
interpolator.
|
|
74
|
+
interpolator.set_normal_constraints(gradient_norm_constraints)
|
|
87
75
|
if gradient_constraints is not None:
|
|
88
|
-
interpolator.
|
|
76
|
+
interpolator.set_gradient_constraints(gradient_constraints)
|
|
77
|
+
interpolator.setup()
|
|
89
78
|
return interpolator
|
|
@@ -181,7 +181,7 @@ class P1Interpolator(DiscreteInterpolator):
|
|
|
181
181
|
points: np.ndarray,
|
|
182
182
|
vectors: np.ndarray,
|
|
183
183
|
w: float = 1.0,
|
|
184
|
-
|
|
184
|
+
b: float = 0,
|
|
185
185
|
name='undefined gradient orthogonal constraint',
|
|
186
186
|
):
|
|
187
187
|
"""
|
|
@@ -215,8 +215,8 @@ class P1Interpolator(DiscreteInterpolator):
|
|
|
215
215
|
norm = np.linalg.norm(vectors, axis=1)
|
|
216
216
|
vectors[norm > 0, :] /= norm[norm > 0, None]
|
|
217
217
|
A = np.einsum("ij,ijk->ik", vectors[inside, :3], grad[inside, :, :])
|
|
218
|
-
B = np.zeros(points[inside, :].shape[0]) +
|
|
219
|
-
self.add_constraints_to_least_squares(A, B, elements, w=wt, name=
|
|
218
|
+
B = np.zeros(points[inside, :].shape[0]) + b
|
|
219
|
+
self.add_constraints_to_least_squares(A, B, elements, w=wt, name=name)
|
|
220
220
|
if np.sum(inside) <= 0:
|
|
221
221
|
logger.warning(
|
|
222
222
|
f"{np.sum(~inside)} \
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
Wrapper for using surfepy
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from ..utils.
|
|
5
|
+
from ..utils.maths import get_vectors
|
|
6
6
|
from ..interpolators import GeologicalInterpolator
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
|
|
10
10
|
from ..utils import getLogger
|
|
11
|
+
import surfepy
|
|
12
|
+
from typing import Optional
|
|
11
13
|
|
|
12
14
|
logger = getLogger(__name__)
|
|
13
|
-
import surfepy
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class SurfeRBFInterpolator(GeologicalInterpolator):
|
|
@@ -19,6 +20,8 @@ class SurfeRBFInterpolator(GeologicalInterpolator):
|
|
|
19
20
|
def __init__(self, method="single_surface"):
|
|
20
21
|
GeologicalInterpolator.__init__(self)
|
|
21
22
|
self.surfe = None
|
|
23
|
+
if not method:
|
|
24
|
+
method = "single_surface"
|
|
22
25
|
if method == "single_surface":
|
|
23
26
|
logger.info("Using single surface interpolator")
|
|
24
27
|
self.surfe = surfepy.Surfe_API(1)
|
|
@@ -29,7 +32,10 @@ class SurfeRBFInterpolator(GeologicalInterpolator):
|
|
|
29
32
|
logger.info("Using surfe horizon")
|
|
30
33
|
self.surfe = surfepy.Surfe_API(4)
|
|
31
34
|
|
|
32
|
-
def
|
|
35
|
+
def set_region(self, **kwargs):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def add_gradient_constraints(self, w=1):
|
|
33
39
|
points = self.get_gradient_constraints()
|
|
34
40
|
if points.shape[0] > 0:
|
|
35
41
|
logger.info("Adding ")
|
|
@@ -40,12 +46,12 @@ class SurfeRBFInterpolator(GeologicalInterpolator):
|
|
|
40
46
|
self.surfe.SetTangentConstraints(strike_vector)
|
|
41
47
|
self.surfe.SetTangentConstraints(dip_vector)
|
|
42
48
|
|
|
43
|
-
def
|
|
49
|
+
def add_norm_constraints(self, w=1):
|
|
44
50
|
points = self.get_norm_constraints()
|
|
45
51
|
if points.shape[0] > 0:
|
|
46
52
|
self.surfe.SetPlanarConstraints(points[:, :6])
|
|
47
53
|
|
|
48
|
-
def
|
|
54
|
+
def add_value_constraints(self, w=1):
|
|
49
55
|
|
|
50
56
|
points = self.get_value_constraints()
|
|
51
57
|
if points.shape[0] > 0:
|
|
@@ -59,15 +65,39 @@ class SurfeRBFInterpolator(GeologicalInterpolator):
|
|
|
59
65
|
points[i, 3],
|
|
60
66
|
)
|
|
61
67
|
|
|
62
|
-
def
|
|
68
|
+
def add_interface_constraints(self, w=1):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def add_value_inequality_constraints(self, w=1):
|
|
72
|
+
## inequalities are causing a segfault
|
|
73
|
+
# points = self.get_value_inequality_constraints()
|
|
74
|
+
# if points.shape[0] > 0:
|
|
75
|
+
# #
|
|
76
|
+
# self.surfe.AddValueInequalityConstraints(points[:,0],points[:,1],points[:,2],points[:,3])
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
def add_inequality_pairs_constraints(
|
|
80
|
+
self,
|
|
81
|
+
w: float = 1.0,
|
|
82
|
+
upper_bound=np.finfo(float).eps,
|
|
83
|
+
lower_bound=-np.inf,
|
|
84
|
+
pairs: Optional[list] = None,
|
|
85
|
+
):
|
|
86
|
+
# self.surfe.Add
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
def reset(self):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
def add_tangent_constraints(self, w=1):
|
|
63
93
|
points = self.get_tangent_constraints()
|
|
64
94
|
if points.shape[0] > 0:
|
|
65
95
|
self.surfe.SetTangentConstraints(points[:, :6])
|
|
66
96
|
|
|
67
|
-
def
|
|
97
|
+
def solve_system(self, **kwargs):
|
|
68
98
|
self.surfe.ComputeInterpolant()
|
|
69
99
|
|
|
70
|
-
def
|
|
100
|
+
def setup_interpolator(self, **kwargs):
|
|
71
101
|
"""
|
|
72
102
|
Setup the interpolator
|
|
73
103
|
|
|
@@ -90,10 +120,10 @@ class SurfeRBFInterpolator(GeologicalInterpolator):
|
|
|
90
120
|
|
|
91
121
|
|
|
92
122
|
"""
|
|
93
|
-
self.
|
|
94
|
-
self.
|
|
95
|
-
self.
|
|
96
|
-
self.
|
|
123
|
+
self.add_gradient_constraints()
|
|
124
|
+
self.add_norm_constraints()
|
|
125
|
+
self.add_value_constraints()
|
|
126
|
+
self.add_tangent_constraints()
|
|
97
127
|
|
|
98
128
|
kernel = kwargs.get("kernel", "r3")
|
|
99
129
|
logger.info("Setting surfe RBF kernel to %s" % kernel)
|
|
@@ -338,3 +338,19 @@ class BaseUnstructured2d(BaseSupport):
|
|
|
338
338
|
"""
|
|
339
339
|
verts, c, tri, inside = self.get_element_for_location(pos, return_verts=False)
|
|
340
340
|
return self.evaluate_shape_derivatives(pos, tri)
|
|
341
|
+
|
|
342
|
+
def vtk(self, node_properties={}, cell_properties={}):
|
|
343
|
+
"""
|
|
344
|
+
Create a vtk unstructured grid from the mesh
|
|
345
|
+
"""
|
|
346
|
+
import pyvista as pv
|
|
347
|
+
|
|
348
|
+
grid = pv.UnstructuredGrid()
|
|
349
|
+
grid.points = self.nodes
|
|
350
|
+
grid.cell_types = np.ones(self.elements.shape[0]) * pv.vtk.VTK_TRIANGLE
|
|
351
|
+
grid.cells = np.c_[np.ones(self.elements.shape[0]) * 3, self.elements]
|
|
352
|
+
for key, value in node_properties.items():
|
|
353
|
+
grid.point_data[key] = value
|
|
354
|
+
for key, value in cell_properties.items():
|
|
355
|
+
grid.cell_data[key] = value
|
|
356
|
+
return grid
|