LoopStructural 1.6.2__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.

Files changed (69) hide show
  1. LoopStructural/datatypes/_bounding_box.py +77 -7
  2. LoopStructural/datatypes/_point.py +67 -7
  3. LoopStructural/datatypes/_structured_grid.py +17 -0
  4. LoopStructural/datatypes/_surface.py +17 -0
  5. LoopStructural/export/omf_wrapper.py +49 -21
  6. LoopStructural/interpolators/__init__.py +13 -0
  7. LoopStructural/interpolators/_api.py +81 -13
  8. LoopStructural/interpolators/_builders.py +141 -141
  9. LoopStructural/interpolators/_discrete_fold_interpolator.py +11 -4
  10. LoopStructural/interpolators/_discrete_interpolator.py +100 -53
  11. LoopStructural/interpolators/_finite_difference_interpolator.py +78 -88
  12. LoopStructural/interpolators/_geological_interpolator.py +27 -10
  13. LoopStructural/interpolators/_interpolator_builder.py +55 -0
  14. LoopStructural/interpolators/_interpolator_factory.py +7 -18
  15. LoopStructural/interpolators/_p1interpolator.py +3 -3
  16. LoopStructural/interpolators/_surfe_wrapper.py +42 -12
  17. LoopStructural/interpolators/supports/_2d_base_unstructured.py +16 -0
  18. LoopStructural/interpolators/supports/_2d_structured_grid.py +44 -9
  19. LoopStructural/interpolators/supports/_3d_base_structured.py +28 -7
  20. LoopStructural/interpolators/supports/_3d_structured_grid.py +38 -12
  21. LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
  22. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +8 -2
  23. LoopStructural/interpolators/supports/__init__.py +7 -0
  24. LoopStructural/interpolators/supports/_base_support.py +7 -0
  25. LoopStructural/modelling/__init__.py +1 -3
  26. LoopStructural/modelling/core/geological_model.py +11 -12
  27. LoopStructural/modelling/features/__init__.py +1 -0
  28. LoopStructural/modelling/features/_analytical_feature.py +48 -18
  29. LoopStructural/modelling/features/_base_geological_feature.py +37 -8
  30. LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
  31. LoopStructural/modelling/features/_geological_feature.py +50 -12
  32. LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
  33. LoopStructural/modelling/features/_structural_frame.py +16 -18
  34. LoopStructural/modelling/features/_unconformity_feature.py +3 -3
  35. LoopStructural/modelling/features/builders/_base_builder.py +8 -0
  36. LoopStructural/modelling/features/builders/_folded_feature_builder.py +47 -16
  37. LoopStructural/modelling/features/builders/_geological_feature_builder.py +29 -13
  38. LoopStructural/modelling/features/builders/_structural_frame_builder.py +7 -2
  39. LoopStructural/modelling/features/fault/__init__.py +1 -1
  40. LoopStructural/modelling/features/fault/_fault_function.py +19 -1
  41. LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
  42. LoopStructural/modelling/features/fault/_fault_segment.py +50 -53
  43. LoopStructural/modelling/features/fold/__init__.py +1 -2
  44. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -23
  45. LoopStructural/modelling/features/fold/_foldframe.py +4 -4
  46. LoopStructural/modelling/features/fold/_svariogram.py +81 -46
  47. LoopStructural/modelling/features/fold/fold_function/__init__.py +27 -0
  48. LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +253 -0
  49. LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +153 -0
  50. LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +46 -0
  51. LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +151 -0
  52. LoopStructural/modelling/input/process_data.py +47 -26
  53. LoopStructural/modelling/input/project_file.py +49 -23
  54. LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
  55. LoopStructural/utils/__init__.py +1 -0
  56. LoopStructural/utils/_surface.py +17 -5
  57. LoopStructural/utils/_transformation.py +98 -14
  58. LoopStructural/utils/colours.py +50 -0
  59. LoopStructural/utils/features.py +5 -0
  60. LoopStructural/utils/maths.py +51 -0
  61. LoopStructural/version.py +1 -1
  62. LoopStructural-1.6.6.dist-info/METADATA +160 -0
  63. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.6.dist-info}/RECORD +66 -59
  64. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.6.dist-info}/WHEEL +1 -1
  65. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  66. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -149
  67. LoopStructural-1.6.2.dist-info/METADATA +0 -81
  68. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.6.dist-info}/LICENSE +0 -0
  69. {LoopStructural-1.6.2.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.7,
34
- "dyz": 0.7,
35
- "dxz": 0.7,
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
- # if we want to define the operators manually
92
- if "operators" in kwargs:
93
- for o in kwargs["operators"].values():
94
- self.assemble_inner(o[0], o[1])
95
- # otherwise just use defaults
96
- if "operators" not in kwargs:
97
- operator = Operator.Dxy_mask
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, 3],
146
+ points[inside, self.support.dimension],
171
147
  idc[inside, :],
172
- w=w * points[inside, 4],
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
- vertices, c, tetras, inside = self.support.get_element_for_location(
175
+ node_idx, inside = self.support.position_to_cell_corners(
200
176
  points[:, : self.support.dimension]
201
177
  )
202
- # calculate volume of tetras
203
- # vecs = vertices[inside, 1:, :] - vertices[inside, 0, None, :]
204
- # vol = np.abs(np.linalg.det(vecs)) / 6
205
- A = c[inside, :]
206
- # A *= vol[:,None]
207
- idc = tetras[inside, :]
208
-
209
- for unique_id in np.unique(points[np.logical_and(~np.isnan(points[:, 3]), inside), 3]):
210
- mask = points[inside, 3] == unique_id
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(points[:, :3])
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(points[inside, :3])
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(points[:, 3:6], axis=1)
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(points[inside, 3:6])
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(points[:, :3])
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(points[inside, :3])
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.add_constraints_to_least_squares(
327
- T[:, 0, :],
328
- points[inside, 3],
329
- idc[inside, :],
330
- w=w,
331
- name="norm",
332
- )
333
- self.add_constraints_to_least_squares(
334
- T[:, 1, :],
335
- points[inside, 4],
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
- B: float = 0,
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(points[:, :3])
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(points[inside, :3])
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, :3], T)
408
- B = np.zeros(points[inside, :].shape[0]) + B
409
- self.add_constraints_to_least_squares(A, B, idc[inside, :], w=w, name=name)
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="regularisation",
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] == 4:
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] < 5:
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] == 6:
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] < 7:
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] == 6:
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] < 7:
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] == 6:
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] < 7:
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] < 5:
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] < 4:
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
- if interpolatortype is None:
69
- raise ValueError("No interpolator type specified")
70
- if boundingbox is None:
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.add_value_constraints(value_constraints)
72
+ interpolator.set_value_constraints(value_constraints)
85
73
  if gradient_norm_constraints is not None:
86
- interpolator.add_gradient_constraints(gradient_norm_constraints)
74
+ interpolator.set_normal_constraints(gradient_norm_constraints)
87
75
  if gradient_constraints is not None:
88
- interpolator.add_gradient_constraints(gradient_constraints)
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
- B: float = 0,
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]) + B
219
- self.add_constraints_to_least_squares(A, B, elements, w=wt, name="gradient orthogonal")
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.helper import get_vectors
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 add_gradient_ctr_pts(self):
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 add_norm_ctr_pts(self):
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 add_ctr_pts(self):
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 add_tangent_ctr_pts(self):
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 _solve(self, **kwargs):
97
+ def solve_system(self, **kwargs):
68
98
  self.surfe.ComputeInterpolant()
69
99
 
70
- def _setup_interpolator(self, **kwargs):
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.add_gradient_ctr_pts()
94
- self.add_norm_ctr_pts()
95
- self.add_ctr_pts()
96
- self.add_tangent_ctr_pts()
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