LoopStructural 1.6.6__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.
- LoopStructural/__init__.py +1 -0
- LoopStructural/datatypes/_bounding_box.py +50 -10
- LoopStructural/datatypes/_point.py +18 -11
- LoopStructural/datatypes/_structured_grid.py +37 -9
- LoopStructural/datatypes/_surface.py +3 -3
- LoopStructural/export/geoh5.py +4 -2
- LoopStructural/interpolators/__init__.py +1 -0
- LoopStructural/interpolators/_discrete_interpolator.py +18 -0
- LoopStructural/interpolators/_finite_difference_interpolator.py +64 -11
- LoopStructural/interpolators/_geological_interpolator.py +9 -0
- LoopStructural/interpolators/_interpolator_builder.py +98 -19
- LoopStructural/interpolators/_interpolator_factory.py +2 -3
- LoopStructural/interpolators/_surfe_wrapper.py +3 -0
- LoopStructural/interpolators/supports/_2d_base_unstructured.py +3 -0
- LoopStructural/interpolators/supports/_2d_structured_grid.py +3 -0
- LoopStructural/interpolators/supports/_3d_base_structured.py +28 -5
- LoopStructural/interpolators/supports/_3d_structured_grid.py +2 -0
- LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +21 -13
- LoopStructural/interpolators/supports/_base_support.py +4 -0
- LoopStructural/interpolators/supports/_support_factory.py +12 -4
- LoopStructural/modelling/core/geological_model.py +5 -6
- LoopStructural/modelling/features/_base_geological_feature.py +2 -2
- LoopStructural/modelling/features/_cross_product_geological_feature.py +1 -2
- LoopStructural/modelling/features/_geological_feature.py +2 -5
- LoopStructural/modelling/features/_lambda_geological_feature.py +0 -1
- LoopStructural/modelling/features/_projected_vector_feature.py +1 -2
- LoopStructural/modelling/features/_unconformity_feature.py +0 -1
- LoopStructural/modelling/features/builders/_base_builder.py +4 -2
- LoopStructural/modelling/features/builders/_geological_feature_builder.py +21 -25
- LoopStructural/modelling/features/builders/_structural_frame_builder.py +9 -4
- LoopStructural/modelling/features/fault/_fault_segment.py +1 -1
- LoopStructural/modelling/features/fold/__init__.py +1 -3
- LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +0 -1
- LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +0 -1
- LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +0 -1
- LoopStructural/modelling/input/process_data.py +9 -6
- LoopStructural/utils/_surface.py +2 -2
- LoopStructural/utils/_transformation.py +28 -13
- LoopStructural/utils/colours.py +4 -3
- LoopStructural/version.py +1 -1
- {LoopStructural-1.6.6.dist-info → LoopStructural-1.6.8.dist-info}/METADATA +3 -3
- {LoopStructural-1.6.6.dist-info → LoopStructural-1.6.8.dist-info}/RECORD +45 -45
- {LoopStructural-1.6.6.dist-info → LoopStructural-1.6.8.dist-info}/LICENSE +0 -0
- {LoopStructural-1.6.6.dist-info → LoopStructural-1.6.8.dist-info}/WHEEL +0 -0
- {LoopStructural-1.6.6.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 =
|
|
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
|
-
|
|
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:
|
|
@@ -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
|
-
|
|
46
|
-
|
|
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
|
|
|
@@ -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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
@@ -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 =
|
|
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
|
-
|
|
28
|
-
|
|
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=
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
@@ -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
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
@
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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].
|
|
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].
|
|
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].
|
|
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):
|
|
@@ -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 =
|
|
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,10 +315,11 @@ class ProcessInputData:
|
|
|
313
315
|
pts = self.fault_orientations.loc[
|
|
314
316
|
self.fault_orientations["feature_name"] == fname, ["gx", "gy", "gz"]
|
|
315
317
|
]
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
318
|
+
if len(pts) > 0:
|
|
319
|
+
fault_properties.loc[
|
|
320
|
+
fname,
|
|
321
|
+
["avgNormalEasting", "avgNormalNorthing", "avgNormalAltitude"],
|
|
322
|
+
] = np.nanmean(pts, axis=0)
|
|
320
323
|
if (
|
|
321
324
|
"avgSlipDirEasting" not in fault_properties.columns
|
|
322
325
|
or "avgSlipDirNorthing" not in fault_properties.columns
|
|
@@ -399,7 +402,7 @@ class ProcessInputData:
|
|
|
399
402
|
dataframes.append(self.contacts)
|
|
400
403
|
if self.contact_orientations is not None:
|
|
401
404
|
dataframes.append(self.contact_orientations)
|
|
402
|
-
if self.fault_orientations
|
|
405
|
+
if len(self.fault_orientations) > 0:
|
|
403
406
|
dataframes.append(self.fault_orientations)
|
|
404
407
|
if self.fault_locations is not None:
|
|
405
408
|
dataframes.append(self.fault_locations)
|
LoopStructural/utils/_surface.py
CHANGED
|
@@ -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)]
|