LoopStructural 1.6.1__py3-none-any.whl → 1.6.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of LoopStructural might be problematic. Click here for more details.
- LoopStructural/datatypes/_bounding_box.py +77 -7
- LoopStructural/datatypes/_point.py +67 -7
- LoopStructural/datatypes/_structured_grid.py +17 -0
- LoopStructural/datatypes/_surface.py +17 -0
- LoopStructural/export/omf_wrapper.py +49 -21
- LoopStructural/interpolators/__init__.py +13 -0
- LoopStructural/interpolators/_api.py +81 -13
- LoopStructural/interpolators/_builders.py +141 -141
- LoopStructural/interpolators/_discrete_fold_interpolator.py +11 -4
- LoopStructural/interpolators/_discrete_interpolator.py +100 -53
- LoopStructural/interpolators/_finite_difference_interpolator.py +78 -88
- LoopStructural/interpolators/_geological_interpolator.py +27 -10
- LoopStructural/interpolators/_interpolator_builder.py +55 -0
- LoopStructural/interpolators/_interpolator_factory.py +7 -18
- LoopStructural/interpolators/_p1interpolator.py +3 -3
- LoopStructural/interpolators/_surfe_wrapper.py +42 -12
- LoopStructural/interpolators/supports/_2d_base_unstructured.py +16 -0
- LoopStructural/interpolators/supports/_2d_structured_grid.py +44 -9
- LoopStructural/interpolators/supports/_3d_base_structured.py +28 -7
- LoopStructural/interpolators/supports/_3d_structured_grid.py +38 -12
- LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
- LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +8 -2
- LoopStructural/interpolators/supports/__init__.py +7 -0
- LoopStructural/interpolators/supports/_base_support.py +7 -0
- LoopStructural/modelling/__init__.py +1 -3
- LoopStructural/modelling/core/geological_model.py +11 -12
- LoopStructural/modelling/features/__init__.py +1 -0
- LoopStructural/modelling/features/_analytical_feature.py +48 -18
- LoopStructural/modelling/features/_base_geological_feature.py +37 -8
- LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
- LoopStructural/modelling/features/_geological_feature.py +50 -12
- LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
- LoopStructural/modelling/features/_structural_frame.py +16 -18
- LoopStructural/modelling/features/_unconformity_feature.py +3 -3
- LoopStructural/modelling/features/builders/_base_builder.py +8 -0
- LoopStructural/modelling/features/builders/_folded_feature_builder.py +47 -16
- LoopStructural/modelling/features/builders/_geological_feature_builder.py +29 -13
- LoopStructural/modelling/features/builders/_structural_frame_builder.py +7 -2
- LoopStructural/modelling/features/fault/__init__.py +1 -1
- LoopStructural/modelling/features/fault/_fault_function.py +19 -1
- LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
- LoopStructural/modelling/features/fault/_fault_segment.py +50 -53
- LoopStructural/modelling/features/fold/__init__.py +1 -2
- LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -23
- LoopStructural/modelling/features/fold/_foldframe.py +4 -4
- LoopStructural/modelling/features/fold/_svariogram.py +81 -46
- LoopStructural/modelling/features/fold/fold_function/__init__.py +27 -0
- LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +253 -0
- LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +153 -0
- LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +46 -0
- LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +151 -0
- LoopStructural/modelling/input/process_data.py +47 -26
- LoopStructural/modelling/input/project_file.py +49 -23
- LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
- LoopStructural/utils/__init__.py +1 -0
- LoopStructural/utils/_surface.py +18 -6
- LoopStructural/utils/_transformation.py +98 -14
- LoopStructural/utils/colours.py +50 -0
- LoopStructural/utils/features.py +5 -0
- LoopStructural/utils/maths.py +53 -1
- LoopStructural/version.py +1 -1
- LoopStructural-1.6.6.dist-info/METADATA +160 -0
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/RECORD +66 -59
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/WHEEL +1 -1
- LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
- LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -149
- LoopStructural-1.6.1.dist-info/METADATA +0 -81
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/LICENSE +0 -0
- {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from LoopStructural.utils.maths import regular_tetraherdron_for_points, gradient_from_tetrahedron
|
|
1
2
|
from ....modelling.features.fault._fault_function_feature import (
|
|
2
3
|
FaultDisplacementFeature,
|
|
3
4
|
)
|
|
4
5
|
from ....modelling.features import FeatureType
|
|
5
|
-
from ....modelling.features.fault._fault_function import BaseFault, BaseFault3D
|
|
6
|
+
from ....modelling.features.fault._fault_function import BaseFault, BaseFault3D, FaultDisplacement
|
|
6
7
|
from ....utils import getLogger, NegativeRegion, PositiveRegion
|
|
7
8
|
from ....modelling.features import StructuralFrame
|
|
8
9
|
|
|
@@ -21,7 +22,14 @@ class FaultSegment(StructuralFrame):
|
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
def __init__(
|
|
24
|
-
self,
|
|
25
|
+
self,
|
|
26
|
+
name: str,
|
|
27
|
+
features: list,
|
|
28
|
+
faultfunction=None,
|
|
29
|
+
steps=10,
|
|
30
|
+
displacement=1.0,
|
|
31
|
+
fold=None,
|
|
32
|
+
model=None,
|
|
25
33
|
):
|
|
26
34
|
"""
|
|
27
35
|
A slip event of a fault
|
|
@@ -36,10 +44,10 @@ class FaultSegment(StructuralFrame):
|
|
|
36
44
|
how many integration steps for faults
|
|
37
45
|
kwargs
|
|
38
46
|
"""
|
|
39
|
-
StructuralFrame.__init__(self,
|
|
47
|
+
StructuralFrame.__init__(self, name=name, features=features, fold=fold, model=model)
|
|
40
48
|
self.type = FeatureType.FAULT
|
|
41
49
|
self.displacement = displacement
|
|
42
|
-
self._faultfunction = BaseFault.fault_displacement
|
|
50
|
+
self._faultfunction = BaseFault().fault_displacement
|
|
43
51
|
self.steps = steps
|
|
44
52
|
self.regions = []
|
|
45
53
|
self.faults_enabled = True
|
|
@@ -57,10 +65,13 @@ class FaultSegment(StructuralFrame):
|
|
|
57
65
|
def faultfunction(self, value):
|
|
58
66
|
if callable(value):
|
|
59
67
|
self._faultfunction = value
|
|
68
|
+
if issubclass(FaultDisplacement, type(value)):
|
|
69
|
+
self._faultfunction = value
|
|
70
|
+
|
|
60
71
|
elif isinstance(value, str) and value == "BaseFault":
|
|
61
|
-
self._faultfunction = BaseFault.fault_displacement
|
|
72
|
+
self._faultfunction = BaseFault().fault_displacement
|
|
62
73
|
elif isinstance(value, str) and value == "BaseFault3D":
|
|
63
|
-
self._faultfunction = BaseFault3D.fault_displacement
|
|
74
|
+
self._faultfunction = BaseFault3D().fault_displacement
|
|
64
75
|
else:
|
|
65
76
|
raise ValueError("Fault function must be a function or BaseFault")
|
|
66
77
|
|
|
@@ -110,6 +121,26 @@ class FaultSegment(StructuralFrame):
|
|
|
110
121
|
def displacementfeature(self):
|
|
111
122
|
return FaultDisplacementFeature(self, self.faultfunction, name=self.name, model=self.model)
|
|
112
123
|
|
|
124
|
+
def fault_ellipsoid(self, **kwargs):
|
|
125
|
+
try:
|
|
126
|
+
import pyvista as pv
|
|
127
|
+
|
|
128
|
+
fault_ellipsoid = pv.PolyData(
|
|
129
|
+
self.model.rescale(self.fault_centre[None, :], inplace=False)
|
|
130
|
+
)
|
|
131
|
+
fault_ellipsoid["norm"] = self.builder.fault_normal_vector[None, :]
|
|
132
|
+
|
|
133
|
+
geom = pv.ParametricEllipsoid(
|
|
134
|
+
self.fault_minor_axis,
|
|
135
|
+
self.fault_major_axis,
|
|
136
|
+
self.fault_intermediate_axis,
|
|
137
|
+
)
|
|
138
|
+
ellipsoid = fault_ellipsoid.glyph(geom=geom, **kwargs)
|
|
139
|
+
return ellipsoid
|
|
140
|
+
except ImportError:
|
|
141
|
+
logger.error("pyvista not installed")
|
|
142
|
+
return None
|
|
143
|
+
|
|
113
144
|
def set_fault_offset(self, offset: float):
|
|
114
145
|
self.fault_offset = offset
|
|
115
146
|
|
|
@@ -173,7 +204,7 @@ class FaultSegment(StructuralFrame):
|
|
|
173
204
|
return np.abs(v) > threshold
|
|
174
205
|
# return np.all(np.logical_and(v > -1,v<1),axis=1)
|
|
175
206
|
|
|
176
|
-
def evaluate_value(self, locations):
|
|
207
|
+
def evaluate_value(self, locations, ignore_regions=False):
|
|
177
208
|
"""
|
|
178
209
|
Return the value of the fault surface scalar field
|
|
179
210
|
|
|
@@ -198,7 +229,10 @@ class FaultSegment(StructuralFrame):
|
|
|
198
229
|
# except:
|
|
199
230
|
# logger.error("nan slicing")
|
|
200
231
|
# v[mask] = self.__getitem__(0).evaluate_value(locations[mask, :])
|
|
201
|
-
return self.__getitem__(0).evaluate_value(locations)
|
|
232
|
+
return self.__getitem__(0).evaluate_value(locations, ignore_regions=ignore_regions)
|
|
233
|
+
|
|
234
|
+
def ellipsoid(self):
|
|
235
|
+
pass
|
|
202
236
|
|
|
203
237
|
def mean(self):
|
|
204
238
|
return self.__getitem__(0).mean()
|
|
@@ -234,6 +268,7 @@ class FaultSegment(StructuralFrame):
|
|
|
234
268
|
logger.error("nan slicing ")
|
|
235
269
|
# need to scale with fault displacement
|
|
236
270
|
v[mask, :] = self.__getitem__(1).evaluate_gradient(locations[mask, :])
|
|
271
|
+
v[mask, :] /= np.linalg.norm(v[mask, :], axis=1)[:, None]
|
|
237
272
|
scale = self.displacementfeature.evaluate_value(locations[mask, :])
|
|
238
273
|
v[mask, :] *= scale[:, None]
|
|
239
274
|
return v
|
|
@@ -282,6 +317,7 @@ class FaultSegment(StructuralFrame):
|
|
|
282
317
|
-------
|
|
283
318
|
|
|
284
319
|
"""
|
|
320
|
+
logger.info(f'Applying fault {self.name} to points {points.shape}')
|
|
285
321
|
steps = self.steps
|
|
286
322
|
newp = np.copy(points).astype(float)
|
|
287
323
|
# evaluate fault function for all points
|
|
@@ -389,51 +425,12 @@ class FaultSegment(StructuralFrame):
|
|
|
389
425
|
# the nodes of the tetrahedron are then restored by the fault and then gradient is
|
|
390
426
|
# recalculated using the updated node positions but the original corner values
|
|
391
427
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
]
|
|
399
|
-
)
|
|
400
|
-
regular_tetrahedron *= scale_parameter
|
|
401
|
-
xyz = vector[:, :3]
|
|
402
|
-
tetrahedron = np.zeros((xyz.shape[0], 4, 3))
|
|
403
|
-
tetrahedron[:] = xyz[:, None, :]
|
|
404
|
-
tetrahedron[:, :, :] += regular_tetrahedron[None, :, :]
|
|
405
|
-
|
|
406
|
-
vectors = vector[:, 3:]
|
|
407
|
-
corners = np.einsum('ikj,ij->ik', tetrahedron - xyz[:, None, :], vectors)
|
|
408
|
-
tetrahedron = tetrahedron.reshape(-1, 3)
|
|
409
|
-
tetrahedron = self.apply_to_points(tetrahedron)
|
|
410
|
-
tetrahedron = tetrahedron.reshape(-1, 4, 3)
|
|
411
|
-
m = np.array(
|
|
412
|
-
[
|
|
413
|
-
[
|
|
414
|
-
(tetrahedron[:, 1, 0] - tetrahedron[:, 0, 0]),
|
|
415
|
-
(tetrahedron[:, 1, 1] - tetrahedron[:, 0, 1]),
|
|
416
|
-
(tetrahedron[:, 1, 2] - tetrahedron[:, 0, 2]),
|
|
417
|
-
],
|
|
418
|
-
[
|
|
419
|
-
(tetrahedron[:, 2, 0] - tetrahedron[:, 0, 0]),
|
|
420
|
-
(tetrahedron[:, 2, 1] - tetrahedron[:, 0, 1]),
|
|
421
|
-
(tetrahedron[:, 2, 2] - tetrahedron[:, 0, 2]),
|
|
422
|
-
],
|
|
423
|
-
[
|
|
424
|
-
(tetrahedron[:, 3, 0] - tetrahedron[:, 0, 0]),
|
|
425
|
-
(tetrahedron[:, 3, 1] - tetrahedron[:, 0, 1]),
|
|
426
|
-
(tetrahedron[:, 3, 2] - tetrahedron[:, 0, 2]),
|
|
427
|
-
],
|
|
428
|
-
]
|
|
429
|
-
)
|
|
430
|
-
I = np.array([[-1.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 1.0, 0.0], [-1.0, 0.0, 0.0, 1.0]])
|
|
431
|
-
m = np.swapaxes(m, 0, 2)
|
|
432
|
-
element_gradients = np.linalg.inv(m)
|
|
433
|
-
|
|
434
|
-
element_gradients = element_gradients.swapaxes(1, 2)
|
|
435
|
-
element_gradients = element_gradients @ I
|
|
436
|
-
v = np.sum(element_gradients * corners[:, None, :], axis=2)
|
|
428
|
+
tetrahedron = regular_tetraherdron_for_points(vector[:, :3], scale_parameter)
|
|
429
|
+
corners = np.einsum('ikj,ij->ik', tetrahedron - vector[:, None, :3], vector[:, 3:])
|
|
430
|
+
|
|
431
|
+
tetrahedron = self.apply_to_points(tetrahedron.reshape(-1, 3)).reshape(-1, 4, 3)
|
|
432
|
+
v = gradient_from_tetrahedron(tetrahedron, corners)
|
|
433
|
+
|
|
437
434
|
return v
|
|
438
435
|
|
|
439
436
|
def add_abutting_fault(self, abutting_fault_feature, positive=None):
|
|
@@ -4,6 +4,5 @@
|
|
|
4
4
|
|
|
5
5
|
from ._fold import FoldEvent
|
|
6
6
|
from ._svariogram import SVariogram
|
|
7
|
-
from ._fold_rotation_angle_feature import FoldRotationAngleFeature
|
|
7
|
+
from ._fold_rotation_angle_feature import FoldRotationAngleFeature
|
|
8
8
|
from ._foldframe import FoldFrame
|
|
9
|
-
from ._fold_rotation_angle import FoldRotationAngle
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import numpy as np
|
|
2
1
|
from ....modelling.features import BaseFeature
|
|
3
|
-
|
|
4
2
|
from ....utils import getLogger
|
|
5
3
|
|
|
6
4
|
logger = getLogger(__name__)
|
|
@@ -44,24 +42,3 @@ class FoldRotationAngleFeature(BaseFeature):
|
|
|
44
42
|
s1 = self.fold_frame.features[0].evaluate_value(location)
|
|
45
43
|
r = self.rotation(s1)
|
|
46
44
|
return r
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def fourier_series(x, c0, c1, c2, w):
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
Parameters
|
|
53
|
-
----------
|
|
54
|
-
x
|
|
55
|
-
c0
|
|
56
|
-
c1
|
|
57
|
-
c2
|
|
58
|
-
w
|
|
59
|
-
|
|
60
|
-
Returns
|
|
61
|
-
-------
|
|
62
|
-
|
|
63
|
-
"""
|
|
64
|
-
v = np.array(x.astype(float))
|
|
65
|
-
# v.fill(c0)
|
|
66
|
-
v = c0 + c1 * np.cos(2 * np.pi / w * x) + c2 * np.sin(2 * np.pi / w * x)
|
|
67
|
-
return np.rad2deg(np.arctan(v))
|
|
@@ -100,8 +100,8 @@ class FoldFrame(StructuralFrame):
|
|
|
100
100
|
# self.features[0].faults_enabled = False
|
|
101
101
|
# self.features[1].faults_enabled = False
|
|
102
102
|
|
|
103
|
-
gpoints = feature_builder.
|
|
104
|
-
npoints = feature_builder.
|
|
103
|
+
gpoints = feature_builder.get_gradient_constraints()[:, :6]
|
|
104
|
+
npoints = feature_builder.get_norm_constraints()[:, :6]
|
|
105
105
|
points = []
|
|
106
106
|
if gpoints.shape[0] > 0:
|
|
107
107
|
points.append(gpoints)
|
|
@@ -173,8 +173,8 @@ class FoldFrame(StructuralFrame):
|
|
|
173
173
|
|
|
174
174
|
"""
|
|
175
175
|
self.features[0].faults_enabled = False
|
|
176
|
-
gpoints = feature_builder.
|
|
177
|
-
npoints = feature_builder.
|
|
176
|
+
gpoints = feature_builder.get_gradient_constraints()[:, :6]
|
|
177
|
+
npoints = feature_builder.get_norm_constraints()[:, :6]
|
|
178
178
|
points = []
|
|
179
179
|
if gpoints.shape[0] > 0:
|
|
180
180
|
points.append(gpoints)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
|
|
2
|
+
from typing import List, Tuple, Optional
|
|
3
3
|
from ....utils import getLogger
|
|
4
4
|
|
|
5
5
|
logger = getLogger(__name__)
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def find_peaks_and_troughs(x, y):
|
|
8
|
+
def find_peaks_and_troughs(x: np.ndarray, y: np.ndarray) -> Tuple[List, List]:
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
Parameters
|
|
@@ -24,7 +24,7 @@ def find_peaks_and_troughs(x, y):
|
|
|
24
24
|
finding the change in derivative
|
|
25
25
|
"""
|
|
26
26
|
if len(x) != len(y):
|
|
27
|
-
|
|
27
|
+
raise ValueError("Cannot guess wavelength, x and y must be the same length")
|
|
28
28
|
pairsx = []
|
|
29
29
|
pairsy = []
|
|
30
30
|
# #TODO numpyize
|
|
@@ -51,55 +51,46 @@ class SVariogram:
|
|
|
51
51
|
The SVariogram is an experimental semi-variogram.
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
|
-
def __init__(self, xdata, ydata):
|
|
55
|
-
self.xdata = xdata
|
|
56
|
-
self.ydata = ydata
|
|
54
|
+
def __init__(self, xdata: np.ndarray, ydata: np.ndarray):
|
|
55
|
+
self.xdata = np.asarray(xdata)
|
|
56
|
+
self.ydata = np.asarray(ydata)
|
|
57
|
+
mask = np.logical_or(np.isnan(self.xdata), np.isnan(self.ydata))
|
|
58
|
+
self.xdata = self.xdata[~mask]
|
|
59
|
+
self.ydata = self.ydata[~mask]
|
|
60
|
+
## maybe check that xdata is not too big here, if it is then this should be done
|
|
61
|
+
## using another library maybe gstools?
|
|
57
62
|
self.dist = np.abs(self.xdata[:, None] - self.xdata[None, :])
|
|
58
63
|
self.variance_matrix = (self.ydata[:, None] - self.ydata[None, :]) ** 2
|
|
59
64
|
self.lags = None
|
|
60
65
|
self.variogram = None
|
|
61
|
-
self.
|
|
66
|
+
self.wavelength_guesses = []
|
|
62
67
|
|
|
63
|
-
def
|
|
68
|
+
def initialise_lags(self, step: Optional[float] = None, nsteps: Optional[int] = None):
|
|
64
69
|
"""
|
|
65
|
-
|
|
66
|
-
You can specify the lags as an array or specify the step size and
|
|
67
|
-
number of steps.
|
|
68
|
-
If neither are specified then the lags are created to be the average
|
|
69
|
-
spacing of the data
|
|
70
|
+
Initialise the lags for the s-variogram
|
|
70
71
|
|
|
71
72
|
Parameters
|
|
72
73
|
----------
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
lags: array
|
|
78
|
-
num
|
|
74
|
+
lag: float
|
|
75
|
+
lag distance for the s-variogram
|
|
76
|
+
nlag: int
|
|
77
|
+
number of lags for the s-variogram
|
|
79
78
|
|
|
80
79
|
Returns
|
|
81
80
|
-------
|
|
82
81
|
|
|
83
82
|
"""
|
|
84
|
-
logger.info("Calculating S-Variogram")
|
|
85
|
-
if lag is not None:
|
|
86
|
-
step = lag
|
|
87
|
-
logger.info(f"Using lag: {step} kwarg for S-variogram")
|
|
88
|
-
|
|
89
|
-
if nlag is not None:
|
|
90
|
-
nstep = nlag
|
|
91
|
-
logger.info(f"Using nlag {nstep} kwarg for s-variogram")
|
|
92
83
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
logger.info(f"Using lag kwarg but calculating nlag as {nstep} for s-variogram")
|
|
84
|
+
if nsteps is not None and step is not None:
|
|
85
|
+
logger.info(f"Using nlag {nsteps} kwarg for s-variogram")
|
|
86
|
+
logger.info(f"Using lag: {step} kwarg for S-variogram")
|
|
87
|
+
self.lags = np.arange(step / 2.0, nsteps * step, step)
|
|
98
88
|
|
|
99
|
-
|
|
89
|
+
if nsteps is None and step is not None:
|
|
90
|
+
nsteps = int(np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
|
|
91
|
+
logger.info(f"Using lag kwarg but calculating nlag as {nsteps} for s-variogram")
|
|
100
92
|
|
|
101
|
-
|
|
102
|
-
self.lags = lags
|
|
93
|
+
self.lags = np.arange(step / 2.0, nsteps * step, step)
|
|
103
94
|
|
|
104
95
|
if self.lags is None:
|
|
105
96
|
# time to guess the step size
|
|
@@ -109,15 +100,50 @@ class SVariogram:
|
|
|
109
100
|
|
|
110
101
|
step = np.nanmean(np.nanmin(d, axis=1)) * 4.0
|
|
111
102
|
# find number of steps to cover range in data
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
logger.warning(f"Variogram has too many steps: {
|
|
115
|
-
maximum = step *
|
|
103
|
+
nsteps = int(np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
|
|
104
|
+
if nsteps > 200:
|
|
105
|
+
logger.warning(f"Variogram has too many steps: {nsteps}, using 200")
|
|
106
|
+
maximum = step * nsteps
|
|
116
107
|
nstep = 200
|
|
117
108
|
step = maximum / nstep
|
|
118
|
-
self.lags = np.arange(step / 2.0,
|
|
109
|
+
self.lags = np.arange(step / 2.0, nsteps * step, step)
|
|
119
110
|
logger.info(
|
|
120
|
-
f"Using average minimum nearest neighbour distance as lag distance size {step} and using {
|
|
111
|
+
f"Using average minimum nearest neighbour distance as lag distance size {step} and using {nsteps} lags"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def calc_semivariogram(
|
|
115
|
+
self,
|
|
116
|
+
step: Optional[float] = None,
|
|
117
|
+
nsteps: Optional[int] = None,
|
|
118
|
+
lags: Optional[np.ndarray] = None,
|
|
119
|
+
):
|
|
120
|
+
"""
|
|
121
|
+
Calculate a semi-variogram for the x and y data for this object.
|
|
122
|
+
You can specify the lags as an array or specify the step size and
|
|
123
|
+
number of steps.
|
|
124
|
+
If neither are specified then the lags are created to be the average
|
|
125
|
+
spacing of the data
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
step: float
|
|
130
|
+
lag distance for the s-variogram
|
|
131
|
+
nstep: int
|
|
132
|
+
number of lags for the s-variogram
|
|
133
|
+
lags: array
|
|
134
|
+
num
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
logger.info("Calculating S-Variogram")
|
|
141
|
+
if lags is not None:
|
|
142
|
+
self.lags = lags
|
|
143
|
+
self.initialise_lags(step, nsteps)
|
|
144
|
+
if self.lags is None:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
"S-Variogram cannot calculate the variogram step size, please specify either step or nsteps"
|
|
121
147
|
)
|
|
122
148
|
tol = self.lags[1] - self.lags[0]
|
|
123
149
|
self.variogram = np.zeros(self.lags.shape)
|
|
@@ -133,7 +159,12 @@ class SVariogram:
|
|
|
133
159
|
self.variogram[i] = np.mean(self.variance_matrix[logic])
|
|
134
160
|
return self.lags, self.variogram, npairs
|
|
135
161
|
|
|
136
|
-
def find_wavelengths(
|
|
162
|
+
def find_wavelengths(
|
|
163
|
+
self,
|
|
164
|
+
step: Optional[float] = None,
|
|
165
|
+
nsteps: Optional[int] = None,
|
|
166
|
+
lags: Optional[np.ndarray] = None,
|
|
167
|
+
) -> List:
|
|
137
168
|
"""
|
|
138
169
|
Picks the wavelengths of the fold by finding the maximum and
|
|
139
170
|
minimums of the s-variogram
|
|
@@ -145,7 +176,7 @@ class SVariogram:
|
|
|
145
176
|
----------
|
|
146
177
|
kwargs : object
|
|
147
178
|
"""
|
|
148
|
-
h, var,
|
|
179
|
+
h, var, _npairs = self.calc_semivariogram(step=step, nsteps=nsteps, lags=lags)
|
|
149
180
|
|
|
150
181
|
px, py = find_peaks_and_troughs(h, var)
|
|
151
182
|
|
|
@@ -156,7 +187,8 @@ class SVariogram:
|
|
|
156
187
|
averagey.append((py[i] + py[i + 1]) / 2.0)
|
|
157
188
|
i += 1 # iterate twice
|
|
158
189
|
# find the extrema of the average curve
|
|
159
|
-
|
|
190
|
+
res = find_peaks_and_troughs(np.array(averagex), np.array(averagey))
|
|
191
|
+
px2, py2 = res
|
|
160
192
|
wl1 = 0.0
|
|
161
193
|
wl1py = 0.0
|
|
162
194
|
for i in range(len(px)):
|
|
@@ -178,11 +210,14 @@ class SVariogram:
|
|
|
178
210
|
if wl2 > 0.0 and wl2 > wl1 * 2 and wl1py < py2[i]:
|
|
179
211
|
break
|
|
180
212
|
if wl1 == 0.0 and wl2 == 0.0:
|
|
213
|
+
logger.warning(
|
|
214
|
+
'Could not automatically guess the wavelength, using 2x the range of the data'
|
|
215
|
+
)
|
|
181
216
|
self.wavelength_guess = [2 * (np.max(self.xdata) - np.min(self.xdata)), 0.0]
|
|
182
217
|
return self.wavelength_guess
|
|
183
218
|
if np.isclose(wl1, 0.0):
|
|
184
219
|
self.wavelength_guess = np.array([wl2 * 2.0, wl1 * 2.0])
|
|
185
|
-
return
|
|
220
|
+
return [wl2]
|
|
186
221
|
# wavelength is 2x the peak on the curve
|
|
187
|
-
self.wavelength_guess =
|
|
222
|
+
self.wavelength_guess = [wl1 * 2.0, wl2 * 2.0]
|
|
188
223
|
return self.wavelength_guess
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from ._trigo_fold_rotation_angle import TrigoFoldRotationAngleProfile
|
|
2
|
+
from ._fourier_series_fold_rotation_angle import FourierSeriesFoldRotationAngleProfile
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Optional
|
|
5
|
+
import numpy.typing as npt
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FoldRotationType(Enum):
|
|
10
|
+
TRIGONOMETRIC = TrigoFoldRotationAngleProfile
|
|
11
|
+
FOURIER_SERIES = FourierSeriesFoldRotationAngleProfile
|
|
12
|
+
# ADDITIONAL = AdditionalFoldRotationAngle
|
|
13
|
+
|
|
14
|
+
def __str__(self):
|
|
15
|
+
return self.name
|
|
16
|
+
|
|
17
|
+
def __repr__(self):
|
|
18
|
+
return self.name
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_fold_rotation_profile(
|
|
22
|
+
fold_rotation_type,
|
|
23
|
+
rotation_angle: Optional[npt.NDArray[np.float64]] = None,
|
|
24
|
+
fold_frame_coordinate: Optional[npt.NDArray[np.float64]] = None,
|
|
25
|
+
**kwargs,
|
|
26
|
+
):
|
|
27
|
+
return fold_rotation_type.value(rotation_angle, fold_frame_coordinate, **kwargs)
|