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.

Files changed (69) hide show
  1. LoopStructural/datatypes/_bounding_box.py +77 -7
  2. LoopStructural/datatypes/_point.py +67 -7
  3. LoopStructural/datatypes/_structured_grid.py +17 -0
  4. LoopStructural/datatypes/_surface.py +17 -0
  5. LoopStructural/export/omf_wrapper.py +49 -21
  6. LoopStructural/interpolators/__init__.py +13 -0
  7. LoopStructural/interpolators/_api.py +81 -13
  8. LoopStructural/interpolators/_builders.py +141 -141
  9. LoopStructural/interpolators/_discrete_fold_interpolator.py +11 -4
  10. LoopStructural/interpolators/_discrete_interpolator.py +100 -53
  11. LoopStructural/interpolators/_finite_difference_interpolator.py +78 -88
  12. LoopStructural/interpolators/_geological_interpolator.py +27 -10
  13. LoopStructural/interpolators/_interpolator_builder.py +55 -0
  14. LoopStructural/interpolators/_interpolator_factory.py +7 -18
  15. LoopStructural/interpolators/_p1interpolator.py +3 -3
  16. LoopStructural/interpolators/_surfe_wrapper.py +42 -12
  17. LoopStructural/interpolators/supports/_2d_base_unstructured.py +16 -0
  18. LoopStructural/interpolators/supports/_2d_structured_grid.py +44 -9
  19. LoopStructural/interpolators/supports/_3d_base_structured.py +28 -7
  20. LoopStructural/interpolators/supports/_3d_structured_grid.py +38 -12
  21. LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
  22. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +8 -2
  23. LoopStructural/interpolators/supports/__init__.py +7 -0
  24. LoopStructural/interpolators/supports/_base_support.py +7 -0
  25. LoopStructural/modelling/__init__.py +1 -3
  26. LoopStructural/modelling/core/geological_model.py +11 -12
  27. LoopStructural/modelling/features/__init__.py +1 -0
  28. LoopStructural/modelling/features/_analytical_feature.py +48 -18
  29. LoopStructural/modelling/features/_base_geological_feature.py +37 -8
  30. LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
  31. LoopStructural/modelling/features/_geological_feature.py +50 -12
  32. LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
  33. LoopStructural/modelling/features/_structural_frame.py +16 -18
  34. LoopStructural/modelling/features/_unconformity_feature.py +3 -3
  35. LoopStructural/modelling/features/builders/_base_builder.py +8 -0
  36. LoopStructural/modelling/features/builders/_folded_feature_builder.py +47 -16
  37. LoopStructural/modelling/features/builders/_geological_feature_builder.py +29 -13
  38. LoopStructural/modelling/features/builders/_structural_frame_builder.py +7 -2
  39. LoopStructural/modelling/features/fault/__init__.py +1 -1
  40. LoopStructural/modelling/features/fault/_fault_function.py +19 -1
  41. LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
  42. LoopStructural/modelling/features/fault/_fault_segment.py +50 -53
  43. LoopStructural/modelling/features/fold/__init__.py +1 -2
  44. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -23
  45. LoopStructural/modelling/features/fold/_foldframe.py +4 -4
  46. LoopStructural/modelling/features/fold/_svariogram.py +81 -46
  47. LoopStructural/modelling/features/fold/fold_function/__init__.py +27 -0
  48. LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +253 -0
  49. LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +153 -0
  50. LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +46 -0
  51. LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +151 -0
  52. LoopStructural/modelling/input/process_data.py +47 -26
  53. LoopStructural/modelling/input/project_file.py +49 -23
  54. LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
  55. LoopStructural/utils/__init__.py +1 -0
  56. LoopStructural/utils/_surface.py +18 -6
  57. LoopStructural/utils/_transformation.py +98 -14
  58. LoopStructural/utils/colours.py +50 -0
  59. LoopStructural/utils/features.py +5 -0
  60. LoopStructural/utils/maths.py +53 -1
  61. LoopStructural/version.py +1 -1
  62. LoopStructural-1.6.6.dist-info/METADATA +160 -0
  63. {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/RECORD +66 -59
  64. {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/WHEEL +1 -1
  65. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  66. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -149
  67. LoopStructural-1.6.1.dist-info/METADATA +0 -81
  68. {LoopStructural-1.6.1.dist-info → LoopStructural-1.6.6.dist-info}/LICENSE +0 -0
  69. {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, features, name, faultfunction=None, steps=10, displacement=1.0, fold=None, model=None
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, features, name, fold, model)
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
- regular_tetrahedron = np.array(
393
- [
394
- [np.sqrt(8 / 9), 0, -1 / 3],
395
- [-np.sqrt(2 / 9), np.sqrt(2 / 3), -1 / 3],
396
- [-np.sqrt(2 / 9), -np.sqrt(2 / 3), -1 / 3],
397
- [0, 0, 1],
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, fourier_series
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.interpolator.get_gradient_constraints()[:, :6]
104
- npoints = feature_builder.interpolator.get_norm_constraints()[:, :6]
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.interpolator.get_gradient_constraints()[:, :6]
177
- npoints = feature_builder.interpolator.get_norm_constraints()[:, :6]
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
- return False
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.wavelength_guess = [None, None]
66
+ self.wavelength_guesses = []
62
67
 
63
- def calc_semivariogram(self, lag=None, nlag=None, lags=None):
68
+ def initialise_lags(self, step: Optional[float] = None, nsteps: Optional[int] = None):
64
69
  """
65
- Calculate a semi-variogram for the x and y data for this object.
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
- step: float
74
- lag distance for the s-variogram
75
- nstep: int
76
- number of lags for the s-variogram
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
- self.lags = np.arange(step / 2.0, nstep * step, step)
94
-
95
- if nlag is None and lag is not None:
96
- nstep = int(np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
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
- self.lags = np.arange(step / 2.0, nstep * step, step)
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
- if lags is not None:
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
- nstep = int(np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
113
- if nstep > 200:
114
- logger.warning(f"Variogram has too many steps: {nstep}, using 200")
115
- maximum = step * nstep
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, nstep * step, step)
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 {nstep} lags"
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(self, **kwargs):
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, npairs = self.calc_semivariogram(**kwargs)
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
- px2, py2 = find_peaks_and_troughs(averagex, averagey)
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 self.wavelength_guess
220
+ return [wl2]
186
221
  # wavelength is 2x the peak on the curve
187
- self.wavelength_guess = np.array([wl1 * 2.0, wl2 * 2.0])
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)