LoopStructural 1.6.7__py3-none-any.whl → 1.6.8__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 (45) hide show
  1. LoopStructural/__init__.py +1 -0
  2. LoopStructural/datatypes/_bounding_box.py +50 -10
  3. LoopStructural/datatypes/_point.py +18 -11
  4. LoopStructural/datatypes/_structured_grid.py +37 -9
  5. LoopStructural/datatypes/_surface.py +3 -3
  6. LoopStructural/export/geoh5.py +4 -2
  7. LoopStructural/interpolators/__init__.py +1 -0
  8. LoopStructural/interpolators/_discrete_interpolator.py +18 -0
  9. LoopStructural/interpolators/_finite_difference_interpolator.py +64 -11
  10. LoopStructural/interpolators/_geological_interpolator.py +9 -0
  11. LoopStructural/interpolators/_interpolator_builder.py +98 -19
  12. LoopStructural/interpolators/_interpolator_factory.py +2 -3
  13. LoopStructural/interpolators/_surfe_wrapper.py +3 -0
  14. LoopStructural/interpolators/supports/_2d_base_unstructured.py +3 -0
  15. LoopStructural/interpolators/supports/_2d_structured_grid.py +3 -0
  16. LoopStructural/interpolators/supports/_3d_base_structured.py +28 -5
  17. LoopStructural/interpolators/supports/_3d_structured_grid.py +2 -0
  18. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +21 -13
  19. LoopStructural/interpolators/supports/_base_support.py +4 -0
  20. LoopStructural/interpolators/supports/_support_factory.py +12 -4
  21. LoopStructural/modelling/core/geological_model.py +5 -6
  22. LoopStructural/modelling/features/_base_geological_feature.py +2 -2
  23. LoopStructural/modelling/features/_cross_product_geological_feature.py +1 -2
  24. LoopStructural/modelling/features/_geological_feature.py +2 -5
  25. LoopStructural/modelling/features/_lambda_geological_feature.py +0 -1
  26. LoopStructural/modelling/features/_projected_vector_feature.py +1 -2
  27. LoopStructural/modelling/features/_unconformity_feature.py +0 -1
  28. LoopStructural/modelling/features/builders/_base_builder.py +4 -2
  29. LoopStructural/modelling/features/builders/_geological_feature_builder.py +21 -25
  30. LoopStructural/modelling/features/builders/_structural_frame_builder.py +9 -4
  31. LoopStructural/modelling/features/fault/_fault_segment.py +1 -1
  32. LoopStructural/modelling/features/fold/__init__.py +1 -3
  33. LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +0 -1
  34. LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +0 -1
  35. LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +0 -1
  36. LoopStructural/modelling/input/process_data.py +4 -2
  37. LoopStructural/utils/_surface.py +2 -2
  38. LoopStructural/utils/_transformation.py +28 -13
  39. LoopStructural/utils/colours.py +3 -1
  40. LoopStructural/version.py +1 -1
  41. {LoopStructural-1.6.7.dist-info → LoopStructural-1.6.8.dist-info}/METADATA +3 -3
  42. {LoopStructural-1.6.7.dist-info → LoopStructural-1.6.8.dist-info}/RECORD +45 -45
  43. {LoopStructural-1.6.7.dist-info → LoopStructural-1.6.8.dist-info}/LICENSE +0 -0
  44. {LoopStructural-1.6.7.dist-info → LoopStructural-1.6.8.dist-info}/WHEEL +0 -0
  45. {LoopStructural-1.6.7.dist-info → LoopStructural-1.6.8.dist-info}/top_level.txt +0 -0
@@ -18,14 +18,13 @@ class InterpolatorFactory:
18
18
  nelements: Optional[int] = None,
19
19
  element_volume: Optional[float] = None,
20
20
  support=None,
21
- buffer: float = 0.2,
21
+ buffer: Optional[float] = None,
22
22
  ):
23
23
  if interpolatortype is None:
24
24
  raise ValueError("No interpolator type specified")
25
25
  if boundingbox is None:
26
26
  raise ValueError("No bounding box specified")
27
- if nelements is None:
28
- raise ValueError("No number of elements specified")
27
+
29
28
  if isinstance(interpolatortype, str):
30
29
  interpolatortype = interpolator_string_map[interpolatortype]
31
30
  if support is None:
@@ -35,6 +35,9 @@ class SurfeRBFInterpolator(GeologicalInterpolator):
35
35
  def set_region(self, **kwargs):
36
36
  pass
37
37
 
38
+ def set_nelements(self, nelements) -> int:
39
+ return 0
40
+
38
41
  def add_gradient_constraints(self, w=1):
39
42
  points = self.get_gradient_constraints()
40
43
  if points.shape[0] > 0:
@@ -68,6 +68,9 @@ class BaseUnstructured2d(BaseSupport):
68
68
  _initialise_aabb(self)
69
69
  return self._aabb_table
70
70
 
71
+ def set_nelements(self, nelements) -> int:
72
+ raise NotImplementedError
73
+
71
74
  @property
72
75
  def shared_elements(self):
73
76
  if np.sum(self._shared_elements) == 0:
@@ -66,6 +66,9 @@ class StructuredGrid2D(BaseSupport):
66
66
  def n_nodes(self):
67
67
  return self.nsteps[0] * self.nsteps[1]
68
68
 
69
+ def set_nelements(self, nelements) -> int:
70
+ raise NotImplementedError("Cannot set number of elements for 2D structured grid")
71
+
69
72
  @property
70
73
  def n_elements(self):
71
74
  return self.nsteps_cells[0] * self.nsteps_cells[1]
@@ -3,6 +3,7 @@ from abc import abstractmethod
3
3
  import numpy as np
4
4
  from LoopStructural.utils import getLogger
5
5
  from . import SupportType
6
+ from typing import Tuple
6
7
 
7
8
  logger = getLogger(__name__)
8
9
 
@@ -34,6 +35,11 @@ class BaseStructuredSupport(BaseSupport):
34
35
  # we use property decorators to update these when different parts of
35
36
  # the geometry need to change
36
37
  # inisialise the private attributes
38
+ # cast to numpy array, to allow list like input
39
+ origin = np.array(origin)
40
+ nsteps = np.array(nsteps)
41
+ step_vector = np.array(step_vector)
42
+
37
43
  self.type = SupportType.BaseStructured
38
44
  if np.any(step_vector == 0):
39
45
  logger.warning(f"Step vector {step_vector} has zero values")
@@ -41,10 +47,10 @@ class BaseStructuredSupport(BaseSupport):
41
47
  raise LoopException("nsteps cannot be zero")
42
48
  if np.any(nsteps < 0):
43
49
  raise LoopException("nsteps cannot be negative")
44
- if np.any(nsteps < 3):
45
- raise LoopException(
46
- "step vector cannot be less than 3. Try increasing the resolution of the interpolator"
47
- )
50
+ # if np.any(nsteps < 3):
51
+ # raise LoopException(
52
+ # "step vector cannot be less than 3. Try increasing the resolution of the interpolator"
53
+ # )
48
54
  self._nsteps = np.array(nsteps, dtype=int) + 1
49
55
  self._step_vector = np.array(step_vector)
50
56
  self._origin = np.array(origin)
@@ -56,6 +62,23 @@ class BaseStructuredSupport(BaseSupport):
56
62
  self.rotation_xy = rotation_xy
57
63
  self.interpolator = None
58
64
 
65
+ @property
66
+ def volume(self):
67
+ return np.prod(self.maximum - self.origin)
68
+
69
+ def set_nelements(self, nelements) -> int:
70
+ box_vol = self.volume
71
+ ele_vol = box_vol / nelements
72
+ # calculate the step vector of a regular cube
73
+ step_vector = np.zeros(3)
74
+
75
+ step_vector[:] = ele_vol ** (1.0 / 3.0)
76
+
77
+ # number of steps is the length of the box / step vector
78
+ nsteps = np.ceil((self.maximum - self.origin) / step_vector).astype(int)
79
+ self.nsteps = nsteps
80
+ return self.n_elements
81
+
59
82
  def to_dict(self):
60
83
  return {
61
84
  "origin": self.origin,
@@ -229,7 +252,7 @@ class BaseStructuredSupport(BaseSupport):
229
252
  """ """
230
253
  return np.einsum("ijk,ik->ij", self.rotation_xy[None, :, :], pos)
231
254
 
232
- def position_to_cell_index(self, pos: np.ndarray) -> np.ndarray:
255
+ def position_to_cell_index(self, pos: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
233
256
  """Get the indexes (i,j,k) of a cell
234
257
  that a point is inside
235
258
 
@@ -40,6 +40,8 @@ class StructuredGrid(BaseStructuredSupport):
40
40
  self.regions["everywhere"] = np.ones(self.n_nodes).astype(bool)
41
41
 
42
42
  def onGeometryChange(self):
43
+ if self.interpolator is not None:
44
+ self.interpolator.reset()
43
45
  pass
44
46
 
45
47
  @property
@@ -61,19 +61,24 @@ class UnStructuredTetMesh(BaseSupport):
61
61
  length = self.maximum - self.minimum
62
62
  self.minimum -= length * 0.1
63
63
  self.maximum += length * 0.1
64
- if aabb_nsteps is None:
65
- box_vol = np.prod(self.maximum - self.minimum)
66
- element_volume = box_vol / (len(self.elements) / 20)
67
- # calculate the step vector of a regular cube
68
- step_vector = np.zeros(3)
69
- step_vector[:] = element_volume ** (1.0 / 3.0)
70
- # number of steps is the length of the box / step vector
71
- aabb_nsteps = np.ceil((self.maximum - self.minimum) / step_vector).astype(int)
72
- # make sure there is at least one cell in every dimension
73
- aabb_nsteps[aabb_nsteps < 2] = 2
74
- aabb_nsteps = np.array(aabb_nsteps, dtype=int)
75
- step_vector = (self.maximum - self.minimum) / (aabb_nsteps - 1)
76
- self.aabb_grid = StructuredGrid(self.minimum, nsteps=aabb_nsteps, step_vector=step_vector)
64
+ if self.elements.shape[0] < 2000:
65
+ self.aabb_grid = StructuredGrid(self.minimum, nsteps=[2, 2, 2], step_vector=[1, 1, 1])
66
+ else:
67
+ if aabb_nsteps is None:
68
+ box_vol = np.prod(self.maximum - self.minimum)
69
+ element_volume = box_vol / (len(self.elements) / 20)
70
+ # calculate the step vector of a regular cube
71
+ step_vector = np.zeros(3)
72
+ step_vector[:] = element_volume ** (1.0 / 3.0)
73
+ # number of steps is the length of the box / step vector
74
+ aabb_nsteps = np.ceil((self.maximum - self.minimum) / step_vector).astype(int)
75
+ # make sure there is at least one cell in every dimension
76
+ aabb_nsteps[aabb_nsteps < 2] = 2
77
+ aabb_nsteps = np.array(aabb_nsteps, dtype=int)
78
+ step_vector = (self.maximum - self.minimum) / (aabb_nsteps - 1)
79
+ self.aabb_grid = StructuredGrid(
80
+ self.minimum, nsteps=aabb_nsteps, step_vector=step_vector
81
+ )
77
82
  # make a big table to store which tetra are in which element.
78
83
  # if this takes up too much memory it could be simplified by using sparse matrices or dict but
79
84
  # at the expense of speed
@@ -87,6 +92,9 @@ class UnStructuredTetMesh(BaseSupport):
87
92
  self._init_face_table()
88
93
  self._initialise_aabb()
89
94
 
95
+ def set_nelements(self, nelements):
96
+ raise NotImplementedError("Cannot set number of elements for unstructured mesh")
97
+
90
98
  @property
91
99
  def nodes(self):
92
100
  return self._nodes
@@ -119,3 +119,7 @@ class BaseSupport(metaclass=ABCMeta):
119
119
  Return a vtk object
120
120
  """
121
121
  pass
122
+
123
+ @abstractmethod
124
+ def set_nelements(self, nelements) -> int:
125
+ pass
@@ -1,4 +1,6 @@
1
1
  from LoopStructural.interpolators.supports import support_map, SupportType
2
+ import numpy as np
3
+ from typing import Optional
2
4
 
3
5
 
4
6
  class SupportFactory:
@@ -20,13 +22,19 @@ class SupportFactory:
20
22
 
21
23
  @staticmethod
22
24
  def create_support_from_bbox(
23
- support_type, bounding_box, nelements, element_volume=None, buffer: float = 0.2
25
+ support_type, bounding_box, nelements, element_volume=None, buffer: Optional[float] = None
24
26
  ):
25
27
  if isinstance(support_type, str):
26
28
  support_type = SupportType._member_map_[support_type].numerator
27
- bbox = bounding_box.with_buffer(buffer=buffer)
28
- bbox.nelements = nelements
29
+ if buffer is not None:
30
+ bounding_box = bounding_box.with_buffer(buffer=buffer)
31
+ if element_volume is not None:
32
+ nelements = int(np.prod(bounding_box.length) / element_volume)
33
+ if nelements is not None:
34
+ bounding_box.nelements = nelements
29
35
 
30
36
  return support_map[support_type](
31
- origin=bbox.origin, step_vector=bbox.step_vector, nsteps=bbox.nsteps
37
+ origin=bounding_box.origin,
38
+ step_vector=bounding_box.step_vector,
39
+ nsteps=bounding_box.nsteps,
32
40
  )
@@ -711,11 +711,10 @@ class GeologicalModel:
711
711
  # build feature
712
712
  # series_feature = series_builder.build(**kwargs)
713
713
  series_feature = series_builder.feature
714
- series_builder.build_arguments = kwargs
714
+ series_builder.update_build_arguments(kwargs | {"domain": True, 'tol': tol})
715
715
  # this support is built for the entire model domain? Possibly would
716
716
  # could just pass a regular grid of points - mask by any above unconformities??
717
- series_builder.build_arguments['domain'] = True
718
- series_builder.build_arguments["tol"] = tol
717
+
719
718
  series_feature.type = FeatureType.INTERPOLATED
720
719
  self._add_feature(series_feature)
721
720
  return series_feature
@@ -850,7 +849,7 @@ class GeologicalModel:
850
849
 
851
850
  # series_feature = series_builder.build(**kwargs)
852
851
  series_feature = series_builder.feature
853
- series_builder.build_arguments = kwargs
852
+ series_builder.update_build_arguments(kwargs)
854
853
  series_feature.type = FeatureType.INTERPOLATED
855
854
  series_feature.fold = fold
856
855
 
@@ -1264,7 +1263,7 @@ class GeologicalModel:
1264
1263
  # build feature
1265
1264
  # domain_fault = domain_fault_feature_builder.build(**kwargs)
1266
1265
  domain_fault = domain_fault_feature_builder.feature
1267
- domain_fault_feature_builder.build_arguments = kwargs
1266
+ domain_fault_feature_builder.update_build_arguments(kwargs)
1268
1267
  domain_fault.type = FeatureType.DOMAINFAULT
1269
1268
  self._add_feature(domain_fault)
1270
1269
  self._add_domain_fault_below(domain_fault)
@@ -1809,7 +1808,7 @@ class GeologicalModel:
1809
1808
  grid = self.bounding_box.structured_grid(name=name)
1810
1809
 
1811
1810
  grid.cell_properties['stratigraphy'] = self.evaluate_model(
1812
- self.rescale(self.bounding_box.cell_centers())
1811
+ self.rescale(self.bounding_box.cell_centres())
1813
1812
  )
1814
1813
  return grid, self.stratigraphic_ids()
1815
1814
 
@@ -338,7 +338,7 @@ class BaseFeature(metaclass=ABCMeta):
338
338
  )
339
339
  grid.properties[self.name] = value
340
340
 
341
- value = self.evaluate_value(bounding_box.cell_centers(order='F'))
341
+ value = self.evaluate_value(bounding_box.cell_centres(order='F'))
342
342
  grid.cell_properties[self.name] = value
343
343
  return grid
344
344
 
@@ -359,7 +359,7 @@ class BaseFeature(metaclass=ABCMeta):
359
359
  if self.model is None:
360
360
  raise ValueError("Must specify bounding box")
361
361
  bounding_box = self.model.bounding_box
362
- points = bounding_box.cell_centers()
362
+ points = bounding_box.cell_centres()
363
363
  value = self.evaluate_gradient(points)
364
364
  if self.model is not None:
365
365
  points = self.model.rescale(points)
@@ -1,5 +1,4 @@
1
- """
2
- """
1
+ """ """
3
2
 
4
3
  import numpy as np
5
4
  from typing import Optional
@@ -6,7 +6,6 @@ from LoopStructural.utils.maths import regular_tetraherdron_for_points, gradient
6
6
  from ...modelling.features import BaseFeature
7
7
  from ...utils import getLogger
8
8
  from ...modelling.features import FeatureType
9
- from ...interpolators import GeologicalInterpolator
10
9
  import numpy as np
11
10
  from typing import Optional, List, Union
12
11
  from ...datatypes import ValuePoints, VectorPoints
@@ -40,8 +39,7 @@ class GeologicalFeature(BaseFeature):
40
39
  def __init__(
41
40
  self,
42
41
  name: str,
43
- interpolator: GeologicalInterpolator,
44
- builder=None,
42
+ builder,
45
43
  regions: list = [],
46
44
  faults: list = [],
47
45
  model=None,
@@ -61,8 +59,8 @@ class GeologicalFeature(BaseFeature):
61
59
  """
62
60
  BaseFeature.__init__(self, name, model, faults, regions, builder)
63
61
  self.name = name
64
- self.interpolator = interpolator
65
62
  self.builder = builder
63
+ self.interpolator = self.builder.interpolator if self.builder is not None else None
66
64
  self.type = FeatureType.INTERPOLATED
67
65
 
68
66
  def to_json(self):
@@ -262,7 +260,6 @@ class GeologicalFeature(BaseFeature):
262
260
  regions=[], # feature.regions.copy(), # don't want to share regionsbetween unconformity and # feature.regions,
263
261
  builder=self.builder,
264
262
  model=self.model,
265
- interpolator=self.interpolator,
266
263
  )
267
264
  return feature
268
265
 
@@ -12,7 +12,6 @@ logger = getLogger(__name__)
12
12
 
13
13
 
14
14
  class LambdaGeologicalFeature(BaseFeature):
15
-
16
15
  def __init__(
17
16
  self,
18
17
  function: Optional[Callable[[np.ndarray], np.ndarray]] = None,
@@ -1,5 +1,4 @@
1
- """
2
- """
1
+ """ """
3
2
 
4
3
  import numpy as np
5
4
  from typing import Optional
@@ -24,7 +24,6 @@ class UnconformityFeature(GeologicalFeature):
24
24
  regions=[], # feature.regions.copy(), # don't want to share regionsbetween unconformity and # feature.regions,
25
25
  builder=feature.builder,
26
26
  model=feature.model,
27
- interpolator=feature.interpolator,
28
27
  )
29
28
  self.value = value
30
29
  self.type = FeatureType.UNCONFORMITY if onlap is False else FeatureType.ONLAPUNCONFORMITY
@@ -47,10 +47,12 @@ class BaseBuilder:
47
47
  def build_arguments(self):
48
48
  return self._build_arguments
49
49
 
50
- @build_arguments.setter
51
- def build_arguments(self, build_arguments):
50
+ def update_build_arguments(self, build_arguments):
52
51
  # self._build_arguments = {}
52
+ logger.info(f"Setting build arguments for {self.name}")
53
53
  for k, i in build_arguments.items():
54
+ logger.info(f"Setting {k} to {i} for {self.name}")
55
+ logger.info(f"{k} is currrently {self._build_arguments.get(k, None)}")
54
56
  if i != self._build_arguments.get(k, None):
55
57
  logger.info(f"Setting {k} to {i} for {self.name}")
56
58
  self._build_arguments[k] = i
@@ -25,7 +25,6 @@ from ....modelling.features.builders import BaseBuilder
25
25
  from ....utils.helper import (
26
26
  get_data_bounding_box_map as get_data_bounding_box,
27
27
  )
28
- from ....utils import RegionEverywhere
29
28
  from ....interpolators import DiscreteInterpolator
30
29
  from ....interpolators import InterpolatorFactory
31
30
 
@@ -39,7 +38,6 @@ class GeologicalFeatureBuilder(BaseBuilder):
39
38
  bounding_box,
40
39
  nelements: int = 1000,
41
40
  name="Feature",
42
- interpolation_region=None,
43
41
  model=None,
44
42
  **kwargs,
45
43
  ):
@@ -61,7 +59,7 @@ class GeologicalFeatureBuilder(BaseBuilder):
61
59
  nelements=nelements,
62
60
  buffer=kwargs.get("buffer", 0.2),
63
61
  )
64
-
62
+
65
63
  if not issubclass(type(interpolator), GeologicalInterpolator):
66
64
  raise TypeError(
67
65
  "interpolator is {} and must be a GeologicalInterpolator".format(type(interpolator))
@@ -78,21 +76,17 @@ class GeologicalFeatureBuilder(BaseBuilder):
78
76
  )
79
77
  self.data = pd.DataFrame(columns=header)
80
78
  self.data_added = False
81
- self._interpolation_region = None
82
- self.interpolation_region = interpolation_region
83
- if self.interpolation_region is not None:
84
- self._interpolator.set_region(region=self.interpolation_region)
85
79
 
86
80
  self._feature = GeologicalFeature(
87
81
  self._name,
88
- self._interpolator,
89
82
  builder=self,
90
83
  regions=[],
91
84
  faults=self.faults,
92
85
  )
93
86
  self._orthogonal_features = {}
94
87
  self._equality_constraints = {}
95
-
88
+ # add default parameters
89
+ self.update_build_arguments({'cpw':1.0,'npw':1.0,'regularisation':1.0,'nelements':self.interpolator.n_elements})
96
90
  def set_not_up_to_date(self, caller):
97
91
  logger.info(
98
92
  f"Setting {self.name} to not up to date from an instance of {caller.__class__.__name__}"
@@ -104,20 +98,12 @@ class GeologicalFeatureBuilder(BaseBuilder):
104
98
  def interpolator(self):
105
99
  return self._interpolator
106
100
 
107
- @property
108
- def interpolation_region(self):
109
- return self._interpolation_region
110
-
111
- @interpolation_region.setter
112
- def interpolation_region(self, interpolation_region):
113
- if interpolation_region is not None:
114
- self._interpolation_region = interpolation_region
115
- self._interpolator.set_region(region=self._interpolation_region)
116
- else:
117
- self._interpolation_region = RegionEverywhere()
118
- self._interpolator.set_region(region=self._interpolation_region)
119
- logger.info(f'Setting interpolation region {self.name}')
120
- self._up_to_date = False
101
+ @interpolator.setter
102
+ def interpolator(self, interpolator):
103
+ if not issubclass(type(interpolator), GeologicalInterpolator):
104
+ raise TypeError(
105
+ "interpolator is {} and must be a GeologicalInterpolator".format(type(interpolator))
106
+ )
121
107
 
122
108
  def add_data_from_data_frame(self, data_frame, overwrite=False):
123
109
  """
@@ -183,6 +169,7 @@ class GeologicalFeatureBuilder(BaseBuilder):
183
169
 
184
170
  """
185
171
  if self.data_added:
172
+ logger.info("Data already added to interpolator")
186
173
  return
187
174
  # first move the data for the fault
188
175
  logger.info(f"Adding {len(self.faults)} faults to {self.name}")
@@ -475,7 +462,7 @@ class GeologicalFeatureBuilder(BaseBuilder):
475
462
  is big enough to capture the faulted feature.
476
463
  """
477
464
  if self.interpolator.support is not None:
478
-
465
+ logger.info(f"Checking interpolation geometry for {self.name}")
479
466
  origin = self.interpolator.support.origin
480
467
  maximum = self.interpolator.support.maximum
481
468
  pts = self.model.bounding_box.with_buffer(buffer).regular_grid(local=True)
@@ -488,7 +475,7 @@ class GeologicalFeatureBuilder(BaseBuilder):
488
475
  ]
489
476
  self.interpolator.support.origin = origin
490
477
  self.interpolator.support.maximum = maximum
491
-
478
+ self.update_build_arguments({'nelements':self.interpolator.n_elements})
492
479
  def build(self, data_region=None, **kwargs):
493
480
  """
494
481
  Runs the interpolation and builds the geological feature
@@ -511,6 +498,15 @@ class GeologicalFeatureBuilder(BaseBuilder):
511
498
  for f in self.faults:
512
499
  f.builder.update()
513
500
  domain = kwargs.get("domain", None)
501
+ if 'nelements' in kwargs:
502
+ # if the number of elements has changed then update the interpolator
503
+ logger.info(f'Interpolator has {self.interpolator.n_elements} elements')
504
+ logger.info(f'Kwargs has {kwargs["nelements"]} elements')
505
+ if self.interpolator.n_elements != kwargs['nelements']:
506
+ logger.info('Setting nelements to {} for {}'.format(kwargs['nelements'], self.name))
507
+ self.build_arguments['nelements'] = self.interpolator.set_nelements(kwargs['nelements'])
508
+ logger.info(f'Interpolator nelements {self.interpolator.n_elements}')
509
+ self._up_to_date = False
514
510
  if domain:
515
511
  self.check_interpolation_geometry(None)
516
512
  self.add_data_to_interpolator(**kwargs)
@@ -120,7 +120,12 @@ class StructuralFrameBuilder:
120
120
  model=self.model,
121
121
  )
122
122
  self._frame.builder = self
123
-
123
+ @property
124
+ def build_arguments(self):
125
+ return self.builders[0].build_arguments
126
+ def update_build_arguments(self, kwargs):
127
+ for i in range(3):
128
+ self.builders[i].update_build_arguments(kwargs)
124
129
  @property
125
130
  def frame(self):
126
131
  return self._frame
@@ -197,7 +202,7 @@ class StructuralFrameBuilder:
197
202
  if len(self.builders[0].data) > 0:
198
203
  logger.info(f"Building {self.name} coordinate 0")
199
204
  kwargs["regularisation"] = regularisation[0]
200
- self.builders[0].build_arguments = kwargs
205
+ self.builders[0].update_build_arguments(kwargs)
201
206
  kwargs.pop("fold", None)
202
207
 
203
208
  # make sure that all of the coordinates are using the same region
@@ -206,7 +211,7 @@ class StructuralFrameBuilder:
206
211
  if w2 > 0:
207
212
  self.builders[2].add_orthogonal_feature(self.builders[0].feature, w2, step=step)
208
213
  kwargs["regularisation"] = regularisation[2]
209
- self.builders[2].build_arguments = kwargs
214
+ self.builders[2].update_build_arguments(kwargs)
210
215
 
211
216
  if len(self.builders[1].data) > 0:
212
217
  logger.info(f"Building {self.name} coordinate 1")
@@ -215,7 +220,7 @@ class StructuralFrameBuilder:
215
220
  if w3 > 0 and len(self.builders[2].data) > 0:
216
221
  self.builders[1].add_orthogonal_feature(self.builders[2].feature, w2, step=step)
217
222
  kwargs["regularisation"] = regularisation[1]
218
- self.builders[1].build_arguments = kwargs
223
+ self.builders[1].update_build_arguments(kwargs)
219
224
 
220
225
  if len(self.builders[2].data) == 0:
221
226
  from LoopStructural.modelling.features import (
@@ -270,7 +270,7 @@ class FaultSegment(StructuralFrame):
270
270
  v[mask, :] = self.__getitem__(1).evaluate_gradient(locations[mask, :])
271
271
  v[mask, :] /= np.linalg.norm(v[mask, :], axis=1)[:, None]
272
272
  scale = self.displacementfeature.evaluate_value(locations[mask, :])
273
- v[mask, :] *= scale[:, None]
273
+ v[mask, :] *= scale[:, None] * self.displacement
274
274
  return v
275
275
 
276
276
  def evaluate_displacement(self, points):
@@ -1,6 +1,4 @@
1
- """
2
-
3
- """
1
+ """ """
4
2
 
5
3
  from ._fold import FoldEvent
6
4
  from ._svariogram import SVariogram
@@ -12,7 +12,6 @@ logger = getLogger(__name__)
12
12
 
13
13
 
14
14
  class BaseFoldRotationAngleProfile(metaclass=ABCMeta):
15
-
16
15
  def __init__(
17
16
  self,
18
17
  rotation_angle: Optional[npt.NDArray[np.float64]] = None,
@@ -8,7 +8,6 @@ logger = getLogger(__name__)
8
8
 
9
9
 
10
10
  class LambdaFoldRotationAngleProfile(BaseFoldRotationAngleProfile):
11
-
12
11
  def __init__(
13
12
  self,
14
13
  fn: Callable[[np.ndarray], np.ndarray],
@@ -8,7 +8,6 @@ logger = getLogger(__name__)
8
8
 
9
9
 
10
10
  class TrigoFoldRotationAngleProfile(BaseFoldRotationAngleProfile):
11
-
12
11
  def __init__(
13
12
  self,
14
13
  rotation_angle: Optional[npt.NDArray[np.float64]] = None,
@@ -78,7 +78,9 @@ class ProcessInputData:
78
78
  self.contacts = contacts
79
79
  self._contact_orientations = None
80
80
  self.contact_orientations = contact_orientations
81
- self._fault_orientations = pd.DataFrame(columns=["X", "Y", "Z", "gx", "gy", "gz", "coord", "feature_name"])
81
+ self._fault_orientations = pd.DataFrame(
82
+ columns=["X", "Y", "Z", "gx", "gy", "gz", "coord", "feature_name"]
83
+ )
82
84
  self.fault_orientations = fault_orientations
83
85
  self._fault_locations = None
84
86
  self.fault_locations = fault_locations
@@ -313,7 +315,7 @@ class ProcessInputData:
313
315
  pts = self.fault_orientations.loc[
314
316
  self.fault_orientations["feature_name"] == fname, ["gx", "gy", "gz"]
315
317
  ]
316
- if len(pts)>0:
318
+ if len(pts) > 0:
317
319
  fault_properties.loc[
318
320
  fname,
319
321
  ["avgNormalEasting", "avgNormalNorthing", "avgNormalAltitude"],
@@ -63,7 +63,7 @@ class LoopIsosurfacer:
63
63
  self,
64
64
  values: Optional[Union[list, int, float]],
65
65
  name: Optional[Union[List[str], str]] = None,
66
- local=False
66
+ local=False,
67
67
  ) -> surface_list:
68
68
  """Extract isosurfaces from the interpolator
69
69
 
@@ -89,7 +89,7 @@ class LoopIsosurfacer:
89
89
  raise ValueError("No interpolator of callable function set")
90
90
 
91
91
  surfaces = []
92
- all_values = self.callable(self.bounding_box.regular_grid(local=local))
92
+ all_values = self.callable(self.bounding_box.regular_grid(local=local, order='C'))
93
93
  ## set value to mean value if its not specified
94
94
  if values is None:
95
95
  values = [((np.nanmax(all_values) - np.nanmin(all_values)) / 2) + np.nanmin(all_values)]