LoopStructural 1.6.2__py3-none-any.whl → 1.6.5__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 (60) hide show
  1. LoopStructural/datatypes/_bounding_box.py +19 -4
  2. LoopStructural/datatypes/_point.py +36 -2
  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/_discrete_fold_interpolator.py +11 -4
  9. LoopStructural/interpolators/_discrete_interpolator.py +100 -53
  10. LoopStructural/interpolators/_finite_difference_interpolator.py +68 -78
  11. LoopStructural/interpolators/_geological_interpolator.py +27 -10
  12. LoopStructural/interpolators/_p1interpolator.py +3 -3
  13. LoopStructural/interpolators/_surfe_wrapper.py +42 -12
  14. LoopStructural/interpolators/supports/_2d_base_unstructured.py +16 -0
  15. LoopStructural/interpolators/supports/_2d_structured_grid.py +44 -9
  16. LoopStructural/interpolators/supports/_3d_base_structured.py +24 -7
  17. LoopStructural/interpolators/supports/_3d_structured_grid.py +38 -12
  18. LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
  19. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +8 -2
  20. LoopStructural/interpolators/supports/__init__.py +7 -0
  21. LoopStructural/interpolators/supports/_base_support.py +7 -0
  22. LoopStructural/modelling/__init__.py +1 -3
  23. LoopStructural/modelling/core/geological_model.py +2 -4
  24. LoopStructural/modelling/features/_analytical_feature.py +25 -16
  25. LoopStructural/modelling/features/_base_geological_feature.py +21 -8
  26. LoopStructural/modelling/features/_geological_feature.py +47 -11
  27. LoopStructural/modelling/features/_structural_frame.py +10 -18
  28. LoopStructural/modelling/features/_unconformity_feature.py +3 -3
  29. LoopStructural/modelling/features/builders/_base_builder.py +8 -0
  30. LoopStructural/modelling/features/builders/_folded_feature_builder.py +45 -14
  31. LoopStructural/modelling/features/builders/_geological_feature_builder.py +29 -13
  32. LoopStructural/modelling/features/builders/_structural_frame_builder.py +5 -0
  33. LoopStructural/modelling/features/fault/__init__.py +1 -1
  34. LoopStructural/modelling/features/fault/_fault_function.py +19 -1
  35. LoopStructural/modelling/features/fault/_fault_segment.py +40 -51
  36. LoopStructural/modelling/features/fold/__init__.py +1 -2
  37. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -23
  38. LoopStructural/modelling/features/fold/_foldframe.py +4 -4
  39. LoopStructural/modelling/features/fold/_svariogram.py +81 -46
  40. LoopStructural/modelling/features/fold/fold_function/__init__.py +27 -0
  41. LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +253 -0
  42. LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +153 -0
  43. LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +46 -0
  44. LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +151 -0
  45. LoopStructural/modelling/input/process_data.py +47 -26
  46. LoopStructural/modelling/input/project_file.py +49 -23
  47. LoopStructural/utils/__init__.py +1 -0
  48. LoopStructural/utils/_surface.py +11 -4
  49. LoopStructural/utils/colours.py +26 -0
  50. LoopStructural/utils/features.py +5 -0
  51. LoopStructural/utils/maths.py +51 -0
  52. LoopStructural/version.py +1 -1
  53. LoopStructural-1.6.5.dist-info/METADATA +146 -0
  54. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.5.dist-info}/RECORD +57 -52
  55. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.5.dist-info}/WHEEL +1 -1
  56. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  57. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -149
  58. LoopStructural-1.6.2.dist-info/METADATA +0 -81
  59. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.5.dist-info}/LICENSE +0 -0
  60. {LoopStructural-1.6.2.dist-info → LoopStructural-1.6.5.dist-info}/top_level.txt +0 -0
@@ -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
@@ -5,10 +5,11 @@ Cartesian grid for fold interpolator
5
5
 
6
6
  import logging
7
7
 
8
- from typing import Tuple
9
8
  import numpy as np
10
9
  from . import SupportType
11
10
  from ._base_support import BaseSupport
11
+ from typing import Dict, Tuple
12
+ from .._operator import Operator
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
@@ -33,7 +34,7 @@ class StructuredGrid2D(BaseSupport):
33
34
  step_vector - 2d list or numpy array of int
34
35
  """
35
36
  self.type = SupportType.StructuredGrid2D
36
- self.nsteps = np.array(nsteps)
37
+ self.nsteps = np.ceil(np.array(nsteps)).astype(int)
37
38
  self.step_vector = np.array(step_vector)
38
39
  self.origin = np.array(origin)
39
40
  self.maximum = origin + self.nsteps * self.step_vector
@@ -261,13 +262,22 @@ class StructuredGrid2D(BaseSupport):
261
262
  if "indexes" in kwargs:
262
263
  indexes = kwargs["indexes"]
263
264
  if "indexes" not in kwargs:
264
- ii = []
265
- jj = []
266
- for i in range(1, self.nsteps[0] - 1):
267
- for j in range(1, self.nsteps[1] - 1):
268
- ii.append(i)
269
- jj.append(j)
270
- indexes = np.array([ii, jj])
265
+ gi = np.arange(self.n_nodes)
266
+ indexes = self.global_index_to_node_index(gi)
267
+ edge_mask = (
268
+ (indexes[:, 0] > 0)
269
+ & (indexes[:, 0] < self.nsteps[0] - 1)
270
+ & (indexes[:, 1] > 0)
271
+ & (indexes[:, 1] < self.nsteps[1] - 1)
272
+ )
273
+ indexes = indexes[edge_mask, :].T
274
+ # ii = []
275
+ # jj = []
276
+ # for i in range(1, self.nsteps[0] - 1):
277
+ # for j in range(1, self.nsteps[1] - 1):
278
+ # ii.append(i)
279
+ # jj.append(j)
280
+ # indexes = np.array([ii, jj])
271
281
  # indexes = np.array(indexes).T
272
282
  if indexes.ndim != 2:
273
283
  print(indexes.ndim)
@@ -460,3 +470,28 @@ class StructuredGrid2D(BaseSupport):
460
470
 
461
471
  def onGeometryChange(self):
462
472
  pass
473
+
474
+ def vtk(self, node_properties={}, cell_properties={}):
475
+ raise NotImplementedError("VTK output not implemented for structured grid")
476
+ pass
477
+
478
+ def get_operators(self, weights: Dict[str, float]) -> Dict[str, Tuple[np.ndarray, float]]:
479
+ """Get
480
+
481
+ Parameters
482
+ ----------
483
+ weights : Dict[str, float]
484
+ _description_
485
+
486
+ Returns
487
+ -------
488
+ Dict[str, Tuple[np.ndarray, float]]
489
+ _description_
490
+ """
491
+ # in a map we only want the xy operators
492
+ operators = {
493
+ 'dxy': (Operator.Dxy_mask[1, :, :], weights['dxy'] * 2),
494
+ 'dxx': (Operator.Dxx_mask[1, :, :], weights['dxx']),
495
+ 'dyy': (Operator.Dyy_mask[1, :, :], weights['dyy']),
496
+ }
497
+ return operators
@@ -134,7 +134,15 @@ class BaseStructuredSupport(BaseSupport):
134
134
  origin = np.array(origin)
135
135
  length = self.maximum - origin
136
136
  length /= self.step_vector
137
- self._nsteps = np.ceil(length).astype(int)
137
+ self._nsteps = np.ceil(length).astype(np.int64)
138
+ self._nsteps[self._nsteps == 0] = (
139
+ 3 # need to have a minimum of 3 elements to apply the finite difference mask
140
+ )
141
+ if np.any(~(self._nsteps > 0)):
142
+ logger.error(
143
+ f"Cannot resize the interpolation support. The proposed number of steps is {self._nsteps}, these must be all > 0"
144
+ )
145
+ raise ValueError("Cannot resize the interpolation support.")
138
146
  self._origin = origin
139
147
  self.onGeometryChange()
140
148
 
@@ -150,7 +158,13 @@ class BaseStructuredSupport(BaseSupport):
150
158
  maximum = np.array(maximum, dtype=float)
151
159
  length = maximum - self.origin
152
160
  length /= self.step_vector
153
- self._nsteps = np.ceil(length).astype(int) + 1
161
+ self._nsteps = np.ceil(length).astype(np.int64)
162
+ self._nsteps[self._nsteps == 0] = 3
163
+ if np.any(~(self._nsteps > 0)):
164
+ logger.error(
165
+ f"Cannot resize the interpolation support. The proposed number of steps is {self._nsteps}, these must be all > 0"
166
+ )
167
+ raise ValueError("Cannot resize the interpolation support.")
154
168
  self.onGeometryChange()
155
169
 
156
170
  @property
@@ -236,6 +250,7 @@ class BaseStructuredSupport(BaseSupport):
236
250
  cell_indexes[inside, 0] = x[inside] // self.step_vector[None, 0]
237
251
  cell_indexes[inside, 1] = y[inside] // self.step_vector[None, 1]
238
252
  cell_indexes[inside, 2] = z[inside] // self.step_vector[None, 2]
253
+
239
254
  return cell_indexes, inside
240
255
 
241
256
  def position_to_cell_global_index(self, pos):
@@ -331,12 +346,9 @@ class BaseStructuredSupport(BaseSupport):
331
346
  return corner_indexes
332
347
 
333
348
  def position_to_cell_corners(self, pos):
334
-
335
349
  cell_indexes, inside = self.position_to_cell_index(pos)
336
350
  corner_indexes = self.cell_corner_indexes(cell_indexes)
337
-
338
351
  globalidx = self.global_node_indices(corner_indexes)
339
-
340
352
  # if global index is not inside the support set to -1
341
353
  globalidx[~inside] = -1
342
354
  return globalidx, inside
@@ -451,7 +463,7 @@ class BaseStructuredSupport(BaseSupport):
451
463
  # all elements are the same size
452
464
  return 1.0
453
465
 
454
- def vtk(self):
466
+ def vtk(self, node_properties={}, cell_properties={}):
455
467
  try:
456
468
  import pyvista as pv
457
469
  except ImportError:
@@ -464,4 +476,9 @@ class BaseStructuredSupport(BaseSupport):
464
476
  [np.zeros(self.elements.shape[0], dtype=int)[:, None] + 8, self.elements]
465
477
  )
466
478
  elements = elements.flatten()
467
- return pv.UnstructuredGrid(elements, celltype, self.nodes)
479
+ grid = pv.UnstructuredGrid(elements, celltype, self.nodes)
480
+ for key, value in node_properties.items():
481
+ grid[key] = value
482
+ for key, value in cell_properties.items():
483
+ grid.cell_arrays[key] = value
484
+ return grid
@@ -5,8 +5,10 @@ Cartesian grid for fold interpolator
5
5
 
6
6
  import numpy as np
7
7
 
8
- from ._3d_base_structured import BaseStructuredSupport
8
+ from LoopStructural.interpolators._operator import Operator
9
9
 
10
+ from ._3d_base_structured import BaseStructuredSupport
11
+ from typing import Dict, Tuple
10
12
  from . import SupportType
11
13
 
12
14
  from LoopStructural.utils import getLogger
@@ -158,13 +160,18 @@ class StructuredGrid(BaseStructuredSupport):
158
160
  if "indexes" in kwargs:
159
161
  indexes = kwargs["indexes"]
160
162
  if "indexes" not in kwargs:
161
- indexes = np.array(
162
- np.meshgrid(
163
- np.arange(1, self.nsteps[0] - 1),
164
- np.arange(1, self.nsteps[1] - 1),
165
- np.arange(1, self.nsteps[2] - 1),
166
- )
167
- ).reshape((3, -1))
163
+ gi = np.arange(self.n_nodes)
164
+ indexes = self.global_index_to_node_index(gi)
165
+ edge_mask = (
166
+ (indexes[:, 0] > 0)
167
+ & (indexes[:, 0] < self.nsteps[0] - 1)
168
+ & (indexes[:, 1] > 0)
169
+ & (indexes[:, 1] < self.nsteps[1] - 1)
170
+ & (indexes[:, 2] > 0)
171
+ & (indexes[:, 2] < self.nsteps[2] - 1)
172
+ )
173
+ indexes = indexes[edge_mask, :].T
174
+
168
175
  # indexes = np.array(indexes).T
169
176
  if indexes.ndim != 2:
170
177
  return
@@ -291,7 +298,6 @@ class StructuredGrid(BaseStructuredSupport):
291
298
  )
292
299
  idc, inside = self.position_to_cell_corners(evaluation_points)
293
300
  # print(idc[inside,:], self.n_nodes,inside)
294
-
295
301
  if idc.shape[0] != inside.shape[0]:
296
302
  raise ValueError("index does not match number of nodes")
297
303
  v = np.zeros(idc.shape)
@@ -336,9 +342,6 @@ class StructuredGrid(BaseStructuredSupport):
336
342
  idc, inside = self.position_to_cell_corners(evaluation_points)
337
343
  T = np.zeros((idc.shape[0], 3, 8))
338
344
  T[inside, :, :] = self.get_element_gradient_for_location(evaluation_points[inside, :])[1]
339
- # indices = np.array([self.position_to_cell_index(evaluation_points)])
340
- # idc = self.global_indicies(indices.swapaxes(0,1))
341
- # print(idc)
342
345
  if np.max(idc[inside, :]) > property_array.shape[0]:
343
346
  cix, ciy, ciz = self.position_to_cell_index(evaluation_points)
344
347
  if not np.all(cix[inside] < self.nsteps_cells[0]):
@@ -468,3 +471,26 @@ class StructuredGrid(BaseStructuredSupport):
468
471
  "type": self.type.numerator,
469
472
  **super().to_dict(),
470
473
  }
474
+
475
+ def get_operators(self, weights: Dict[str, float]) -> Dict[str, Tuple[np.ndarray, float]]:
476
+ """Gets the operators specific to this support
477
+
478
+ Parameters
479
+ ----------
480
+ weights : Dict[str, float]
481
+ weight value per operator
482
+
483
+ Returns
484
+ -------
485
+ operators
486
+ A dictionary with a numpy array and float weight
487
+ """
488
+ operators = {
489
+ 'dxy': (Operator.Dxy_mask, weights['dxy'] / 4),
490
+ 'dyz': (Operator.Dyz_mask, weights['dyz'] / 4),
491
+ 'dxz': (Operator.Dxz_mask, weights['dxz'] / 4),
492
+ 'dxx': (Operator.Dxx_mask, weights['dxx'] / 1),
493
+ 'dyy': (Operator.Dyy_mask, weights['dyy'] / 1),
494
+ 'dzz': (Operator.Dzz_mask, weights['dzz'] / 1),
495
+ }
496
+ return operators
@@ -729,8 +729,7 @@ class TetMesh(BaseStructuredSupport):
729
729
 
730
730
  return neighbours
731
731
 
732
- @property
733
- def vtk(self):
732
+ def vtk(self, node_properties={}, cell_properties={}):
734
733
  try:
735
734
  import pyvista as pv
736
735
  except ImportError:
@@ -743,4 +742,9 @@ class TetMesh(BaseStructuredSupport):
743
742
  [np.zeros(self.elements.shape[0], dtype=int)[:, None] + 4, self.elements]
744
743
  )
745
744
  elements = elements.flatten()
746
- return pv.UnstructuredGrid(elements, celltype, self.nodes)
745
+ grid = pv.UnstructuredGrid(elements, celltype, self.nodes)
746
+ for prop in node_properties:
747
+ grid[prop] = node_properties[prop]
748
+ for prop in cell_properties:
749
+ grid.cell_arrays[prop] = cell_properties[prop]
750
+ return grid
@@ -621,7 +621,7 @@ class UnStructuredTetMesh(BaseSupport):
621
621
  """
622
622
  return self.neighbours
623
623
 
624
- def vtk(self):
624
+ def vtk(self, node_properties={}, cell_properties={}):
625
625
  try:
626
626
  import pyvista as pv
627
627
  except ImportError:
@@ -634,4 +634,10 @@ class UnStructuredTetMesh(BaseSupport):
634
634
  [np.zeros(self.elements.shape[0], dtype=int)[:, None] + 4, self.elements]
635
635
  )
636
636
  elements = elements.flatten()
637
- return pv.UnstructuredGrid(elements, celltype, self.nodes)
637
+ grid = pv.UnstructuredGrid(elements, celltype, self.nodes)
638
+ for key, value in node_properties.items():
639
+ grid[key] = value
640
+ for key, value in cell_properties.items():
641
+ grid.cell_arrays[key] = value
642
+
643
+ return grid
@@ -18,6 +18,7 @@ class SupportType(IntEnum):
18
18
  BaseStructured = 6
19
19
  TetMesh = 10
20
20
  P2UnstructuredTetMesh = 11
21
+ DataSupported = 12
21
22
 
22
23
 
23
24
  from ._2d_base_unstructured import BaseUnstructured2d
@@ -29,6 +30,11 @@ from ._3d_unstructured_tetra import UnStructuredTetMesh
29
30
  from ._3d_structured_tetra import TetMesh
30
31
  from ._3d_p2_tetra import P2UnstructuredTetMesh
31
32
 
33
+
34
+ def no_support(*args, **kwargs):
35
+ return None
36
+
37
+
32
38
  support_map = {
33
39
  SupportType.StructuredGrid2D: StructuredGrid2D,
34
40
  SupportType.StructuredGrid: StructuredGrid,
@@ -37,6 +43,7 @@ support_map = {
37
43
  SupportType.P2Unstructured2d: P2Unstructured2d,
38
44
  SupportType.TetMesh: TetMesh,
39
45
  SupportType.P2UnstructuredTetMesh: P2UnstructuredTetMesh,
46
+ SupportType.DataSupported: no_support,
40
47
  }
41
48
 
42
49
  from ._support_factory import SupportFactory
@@ -112,3 +112,10 @@ class BaseSupport(metaclass=ABCMeta):
112
112
  Return the element size
113
113
  """
114
114
  pass
115
+
116
+ @abstractmethod
117
+ def vtk(self, node_properties={}, cell_properties={}):
118
+ """
119
+ Return a vtk object
120
+ """
121
+ pass
@@ -6,12 +6,12 @@ Geological modelling classes and functions
6
6
  __all__ = [
7
7
  "GeologicalModel",
8
8
  "ProcessInputData",
9
- "Loop3DView",
10
9
  "Map2LoopProcessor",
11
10
  "LoopProjectfileProcessor",
12
11
  ]
13
12
  from ..utils import getLogger
14
13
  from ..utils import LoopImportError
14
+ from .core.geological_model import GeologicalModel
15
15
 
16
16
  logger = getLogger(__name__)
17
17
  from ..modelling.input import (
@@ -25,5 +25,3 @@ except (LoopImportError, ImportError):
25
25
  logger.warning(
26
26
  "Cannot use LoopProjectfileProcessor: Loop project file cannot be imported, try installing LoopProjectFile"
27
27
  )
28
- # from LoopStructural.modelling.features import StructuralFrame
29
- # from LoopStructural.modelling.features.fault import FaultSegment
@@ -649,7 +649,7 @@ class GeologicalModel:
649
649
  for g in stratigraphic_column.keys():
650
650
  for u in stratigraphic_column[g].keys():
651
651
  stratigraphic_column[g][u]["colour"] = cmap_colours[ci, :]
652
-
652
+ ci += 1
653
653
  self.stratigraphic_column = stratigraphic_column
654
654
 
655
655
  def create_and_add_foliation(
@@ -825,8 +825,6 @@ class GeologicalModel:
825
825
 
826
826
  fold = FoldEvent(fold_frame, name=f"Fold_{foliation_data}", invert_norm=invert_fold_norm)
827
827
 
828
- if "fold_weights" not in kwargs:
829
- kwargs["fold_weights"] = {}
830
828
  if interpolatortype != "DFI":
831
829
  logger.warning("Folded foliation only supports DFI interpolator, changing to DFI")
832
830
  interpolatortype = "DFI"
@@ -1811,7 +1809,7 @@ class GeologicalModel:
1811
1809
  grid = self.bounding_box.structured_grid(name=name)
1812
1810
 
1813
1811
  grid.cell_properties['stratigraphy'] = self.evaluate_model(
1814
- self.bounding_box.cell_centers(), scale=False
1812
+ self.rescale(self.bounding_box.cell_centers())
1815
1813
  )
1816
1814
  return grid, self.stratigraphic_ids()
1817
1815
 
@@ -28,7 +28,16 @@ class AnalyticalGeologicalFeature(BaseFeature):
28
28
  list of FaultSegments that affect this feature
29
29
  """
30
30
 
31
- def __init__(self, name, vector, origin, regions=[], faults=[], model=None, builder=None):
31
+ def __init__(
32
+ self,
33
+ name: str,
34
+ vector: np.ndarray,
35
+ origin: np.ndarray,
36
+ regions=[],
37
+ faults=[],
38
+ model=None,
39
+ builder=None,
40
+ ):
32
41
  BaseFeature.__init__(self, name, model, faults, regions, builder)
33
42
  self.vector = np.array(vector, dtype=float)
34
43
  self.origin = np.array(origin, dtype=float)
@@ -48,16 +57,16 @@ class AnalyticalGeologicalFeature(BaseFeature):
48
57
  json["origin"] = self.origin.tolist()
49
58
  return json
50
59
 
51
- def evaluate_value(self, xyz, ignore_regions=False):
52
- xyz = np.array(xyz)
53
- if len(xyz.shape) == 1:
54
- xyz = xyz[None, :]
55
- if len(xyz.shape) != 2:
60
+ def evaluate_value(self, pos: np.ndarray, ignore_regions=False):
61
+ pos = np.array(pos)
62
+ if len(pos.shape) == 1:
63
+ pos = pos[None, :]
64
+ if len(pos.shape) != 2:
56
65
  raise ValueError("xyz must be a 1D or 2D array")
57
- xyz2 = np.zeros(xyz.shape)
58
- xyz2[:] = xyz[:]
66
+ xyz2 = np.zeros(pos.shape)
67
+ xyz2[:] = pos[:]
59
68
  for f in self.faults:
60
- xyz2[:] = f.apply_to_points(xyz)
69
+ xyz2[:] = f.apply_to_points(pos)
61
70
  if self.model is not None:
62
71
  xyz2[:] = self.model.rescale(xyz2, inplace=False)
63
72
  xyz2[:] = xyz2 - self.origin
@@ -65,13 +74,13 @@ class AnalyticalGeologicalFeature(BaseFeature):
65
74
  distance = normal[0] * xyz2[:, 0] + normal[1] * xyz2[:, 1] + normal[2] * xyz2[:, 2]
66
75
  return distance / np.linalg.norm(self.vector)
67
76
 
68
- def evaluate_gradient(self, xyz, ignore_regions=False):
69
- xyz = np.array(xyz)
70
- if len(xyz.shape) == 1:
71
- xyz = xyz[None, :]
72
- if len(xyz.shape) != 2:
73
- raise ValueError("xyz must be a 1D or 2D array")
74
- v = np.zeros(xyz.shape)
77
+ def evaluate_gradient(self, pos: np.ndarray, ignore_regions=False):
78
+ pos = np.array(pos)
79
+ if len(pos.shape) == 1:
80
+ pos = pos[None, :]
81
+ if len(pos.shape) != 2:
82
+ raise ValueError("pos must be a 1D or 2D array")
83
+ v = np.zeros(pos.shape)
75
84
  v[:, :] = self.vector[None, :]
76
85
  return v
77
86
 
@@ -291,11 +291,24 @@ class BaseFeature(metaclass=ABCMeta):
291
291
  if self.model is None:
292
292
  raise ValueError("Must specify bounding box")
293
293
  bounding_box = self.model.bounding_box
294
- callable = lambda xyz: self.evaluate_value(self.model.scale(xyz))
295
- isosurfacer = LoopIsosurfacer(bounding_box, callable=callable)
296
- if name is None and self.name is not None:
297
- name = self.name
298
- return isosurfacer.fit(value, name)
294
+ regions = self.regions
295
+ try:
296
+ self.regions = [
297
+ r for r in self.regions if r.name != self.name and r.parent.name != self.name
298
+ ]
299
+ callable = lambda xyz: self.evaluate_value(self.model.scale(xyz))
300
+ isosurfacer = LoopIsosurfacer(bounding_box, callable=callable)
301
+ if name is None and self.name is not None:
302
+ name = self.name
303
+ surfaces = isosurfacer.fit(value, name)
304
+ except Exception as e:
305
+ logger.error(f"Failed to create surface for {self.name} at value {value}")
306
+ logger.error(e)
307
+ surfaces = []
308
+ finally:
309
+ self.regions = regions
310
+
311
+ return surfaces
299
312
 
300
313
  def scalar_field(self, bounding_box=None):
301
314
  """Create a scalar field for the feature
@@ -341,10 +354,10 @@ class BaseFeature(metaclass=ABCMeta):
341
354
  if self.model is None:
342
355
  raise ValueError("Must specify bounding box")
343
356
  bounding_box = self.model.bounding_box
344
- grid = bounding_box.vtk()
345
- points = grid.points
357
+ points = bounding_box.cell_centers()
346
358
  value = self.evaluate_gradient(points)
347
-
359
+ if self.model is not None:
360
+ points = self.model.rescale(points)
348
361
  return VectorPoints(points, value, self.name)
349
362
 
350
363
  @abstractmethod