LoopStructural 1.6.1__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 (129) hide show
  1. LoopStructural/__init__.py +52 -0
  2. LoopStructural/datasets/__init__.py +23 -0
  3. LoopStructural/datasets/_base.py +301 -0
  4. LoopStructural/datasets/_example_models.py +10 -0
  5. LoopStructural/datasets/data/claudius.csv +21049 -0
  6. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  7. LoopStructural/datasets/data/duplex.csv +126 -0
  8. LoopStructural/datasets/data/duplexbb.txt +2 -0
  9. LoopStructural/datasets/data/fault_trace/fault_trace.cpg +1 -0
  10. LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  11. LoopStructural/datasets/data/fault_trace/fault_trace.prj +1 -0
  12. LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  13. LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  14. LoopStructural/datasets/data/geological_map_data/bbox.csv +2 -0
  15. LoopStructural/datasets/data/geological_map_data/contacts.csv +657 -0
  16. LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +7 -0
  17. LoopStructural/datasets/data/geological_map_data/fault_edges.txt +2 -0
  18. LoopStructural/datasets/data/geological_map_data/fault_locations.csv +79 -0
  19. LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +19 -0
  20. LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +13 -0
  21. LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +207 -0
  22. LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +13 -0
  23. LoopStructural/datasets/data/intrusion.csv +1017 -0
  24. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  25. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  26. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  27. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  28. LoopStructural/datasets/data/refolded_fold.csv +205 -0
  29. LoopStructural/datasets/data/tabular_intrusion.csv +23 -0
  30. LoopStructural/datatypes/__init__.py +4 -0
  31. LoopStructural/datatypes/_bounding_box.py +422 -0
  32. LoopStructural/datatypes/_point.py +166 -0
  33. LoopStructural/datatypes/_structured_grid.py +94 -0
  34. LoopStructural/datatypes/_surface.py +184 -0
  35. LoopStructural/export/exporters.py +554 -0
  36. LoopStructural/export/file_formats.py +15 -0
  37. LoopStructural/export/geoh5.py +100 -0
  38. LoopStructural/export/gocad.py +126 -0
  39. LoopStructural/export/omf_wrapper.py +88 -0
  40. LoopStructural/interpolators/__init__.py +105 -0
  41. LoopStructural/interpolators/_api.py +143 -0
  42. LoopStructural/interpolators/_builders.py +149 -0
  43. LoopStructural/interpolators/_cython/__init__.py +0 -0
  44. LoopStructural/interpolators/_discrete_fold_interpolator.py +183 -0
  45. LoopStructural/interpolators/_discrete_interpolator.py +692 -0
  46. LoopStructural/interpolators/_finite_difference_interpolator.py +470 -0
  47. LoopStructural/interpolators/_geological_interpolator.py +380 -0
  48. LoopStructural/interpolators/_interpolator_factory.py +89 -0
  49. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  50. LoopStructural/interpolators/_operator.py +38 -0
  51. LoopStructural/interpolators/_p1interpolator.py +228 -0
  52. LoopStructural/interpolators/_p2interpolator.py +277 -0
  53. LoopStructural/interpolators/_surfe_wrapper.py +174 -0
  54. LoopStructural/interpolators/supports/_2d_base_unstructured.py +340 -0
  55. LoopStructural/interpolators/supports/_2d_p1_unstructured.py +68 -0
  56. LoopStructural/interpolators/supports/_2d_p2_unstructured.py +288 -0
  57. LoopStructural/interpolators/supports/_2d_structured_grid.py +462 -0
  58. LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  59. LoopStructural/interpolators/supports/_3d_base_structured.py +467 -0
  60. LoopStructural/interpolators/supports/_3d_p2_tetra.py +331 -0
  61. LoopStructural/interpolators/supports/_3d_structured_grid.py +470 -0
  62. LoopStructural/interpolators/supports/_3d_structured_tetra.py +746 -0
  63. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +637 -0
  64. LoopStructural/interpolators/supports/__init__.py +55 -0
  65. LoopStructural/interpolators/supports/_aabb.py +77 -0
  66. LoopStructural/interpolators/supports/_base_support.py +114 -0
  67. LoopStructural/interpolators/supports/_face_table.py +70 -0
  68. LoopStructural/interpolators/supports/_support_factory.py +32 -0
  69. LoopStructural/modelling/__init__.py +29 -0
  70. LoopStructural/modelling/core/__init__.py +0 -0
  71. LoopStructural/modelling/core/geological_model.py +1867 -0
  72. LoopStructural/modelling/features/__init__.py +32 -0
  73. LoopStructural/modelling/features/_analytical_feature.py +79 -0
  74. LoopStructural/modelling/features/_base_geological_feature.py +364 -0
  75. LoopStructural/modelling/features/_cross_product_geological_feature.py +100 -0
  76. LoopStructural/modelling/features/_geological_feature.py +288 -0
  77. LoopStructural/modelling/features/_lambda_geological_feature.py +93 -0
  78. LoopStructural/modelling/features/_region.py +18 -0
  79. LoopStructural/modelling/features/_structural_frame.py +186 -0
  80. LoopStructural/modelling/features/_unconformity_feature.py +83 -0
  81. LoopStructural/modelling/features/builders/__init__.py +5 -0
  82. LoopStructural/modelling/features/builders/_base_builder.py +111 -0
  83. LoopStructural/modelling/features/builders/_fault_builder.py +590 -0
  84. LoopStructural/modelling/features/builders/_folded_feature_builder.py +129 -0
  85. LoopStructural/modelling/features/builders/_geological_feature_builder.py +543 -0
  86. LoopStructural/modelling/features/builders/_structural_frame_builder.py +237 -0
  87. LoopStructural/modelling/features/fault/__init__.py +3 -0
  88. LoopStructural/modelling/features/fault/_fault_function.py +444 -0
  89. LoopStructural/modelling/features/fault/_fault_function_feature.py +82 -0
  90. LoopStructural/modelling/features/fault/_fault_segment.py +505 -0
  91. LoopStructural/modelling/features/fold/__init__.py +9 -0
  92. LoopStructural/modelling/features/fold/_fold.py +167 -0
  93. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +149 -0
  94. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +67 -0
  95. LoopStructural/modelling/features/fold/_foldframe.py +194 -0
  96. LoopStructural/modelling/features/fold/_svariogram.py +188 -0
  97. LoopStructural/modelling/input/__init__.py +2 -0
  98. LoopStructural/modelling/input/fault_network.py +80 -0
  99. LoopStructural/modelling/input/map2loop_processor.py +165 -0
  100. LoopStructural/modelling/input/process_data.py +650 -0
  101. LoopStructural/modelling/input/project_file.py +84 -0
  102. LoopStructural/modelling/intrusions/__init__.py +25 -0
  103. LoopStructural/modelling/intrusions/geom_conceptual_models.py +142 -0
  104. LoopStructural/modelling/intrusions/geometric_scaling_functions.py +123 -0
  105. LoopStructural/modelling/intrusions/intrusion_builder.py +672 -0
  106. LoopStructural/modelling/intrusions/intrusion_feature.py +410 -0
  107. LoopStructural/modelling/intrusions/intrusion_frame_builder.py +971 -0
  108. LoopStructural/modelling/intrusions/intrusion_support_functions.py +460 -0
  109. LoopStructural/utils/__init__.py +38 -0
  110. LoopStructural/utils/_surface.py +143 -0
  111. LoopStructural/utils/_transformation.py +76 -0
  112. LoopStructural/utils/config.py +18 -0
  113. LoopStructural/utils/dtm_creator.py +17 -0
  114. LoopStructural/utils/exceptions.py +31 -0
  115. LoopStructural/utils/helper.py +292 -0
  116. LoopStructural/utils/json_encoder.py +18 -0
  117. LoopStructural/utils/linalg.py +8 -0
  118. LoopStructural/utils/logging.py +79 -0
  119. LoopStructural/utils/maths.py +245 -0
  120. LoopStructural/utils/regions.py +103 -0
  121. LoopStructural/utils/typing.py +7 -0
  122. LoopStructural/utils/utils.py +68 -0
  123. LoopStructural/version.py +1 -0
  124. LoopStructural/visualisation/__init__.py +11 -0
  125. LoopStructural-1.6.1.dist-info/LICENSE +21 -0
  126. LoopStructural-1.6.1.dist-info/METADATA +81 -0
  127. LoopStructural-1.6.1.dist-info/RECORD +129 -0
  128. LoopStructural-1.6.1.dist-info/WHEEL +5 -0
  129. LoopStructural-1.6.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,237 @@
1
+ """
2
+ structural frame builder
3
+ """
4
+
5
+ from typing import Union
6
+
7
+ from LoopStructural.utils.exceptions import LoopException
8
+
9
+ import numpy as np
10
+
11
+ from ....utils import getLogger
12
+ from ....datatypes import BoundingBox
13
+
14
+ logger = getLogger(__name__)
15
+
16
+
17
+ from ....modelling.features.builders import GeologicalFeatureBuilder
18
+ from ....modelling.features.builders import FoldedFeatureBuilder
19
+ from ....modelling.features import StructuralFrame
20
+
21
+
22
+ class StructuralFrameBuilder:
23
+ def __init__(
24
+ self,
25
+ interpolatortype: Union[str, list],
26
+ bounding_box: BoundingBox,
27
+ nelements: Union[int, list] = 1000,
28
+ frame=StructuralFrame,
29
+ model=None,
30
+ **kwargs,
31
+ ):
32
+ """
33
+ Class for building a structural frame - has functions to set up the
34
+ interpolator with
35
+ data and also orthogonality constraints. Can build a generic
36
+ structural frame or a
37
+ subclass of the structural frame if the kwarg `frame` is specified
38
+
39
+ Parameters
40
+ ----------
41
+ interpolator - a template interpolator for the frame
42
+ kwargs
43
+ """
44
+
45
+ self.support = None
46
+ self.fault_event = None
47
+ self.name = "Undefined"
48
+ self.model = model
49
+ # self.region = 'everywhere'
50
+ self.builders = []
51
+ if "name" in kwargs:
52
+ self.name = kwargs["name"]
53
+ kwargs.pop("name")
54
+ self.data = [[], [], []]
55
+ self.fold = kwargs.pop("fold", None)
56
+ # list of interpolators
57
+ # self.interpolators = []
58
+ # Create the interpolation objects by copying the template
59
+ if isinstance(interpolatortype, str):
60
+ interpolatortype = [interpolatortype, interpolatortype, interpolatortype]
61
+ if not isinstance(interpolatortype, list):
62
+ raise LoopException(
63
+ f"interpolatortype is {type(interpolatortype)} and must be either a string or a list of strings"
64
+ )
65
+ if isinstance(nelements, (int, float)):
66
+ nelements = [nelements, nelements, nelements]
67
+ if not isinstance(nelements, list):
68
+ raise LoopException(
69
+ f"nelements is {type(nelements)} and must be either a int or a list of ints"
70
+ )
71
+ # self.builders
72
+ if self.fold:
73
+ self.builders.append(
74
+ FoldedFeatureBuilder(
75
+ interpolatortype[0],
76
+ bounding_box,
77
+ self.fold,
78
+ nelements=nelements[0],
79
+ name=f"{self.name}__0",
80
+ **kwargs,
81
+ )
82
+ )
83
+ else:
84
+ self.builders.append(
85
+ GeologicalFeatureBuilder(
86
+ interpolatortype[0],
87
+ bounding_box,
88
+ nelements[0],
89
+ name=f"{self.name}__0",
90
+ **kwargs,
91
+ )
92
+ ) # ,region=self.region))
93
+ self.builders.append(
94
+ GeologicalFeatureBuilder(
95
+ interpolatortype[1],
96
+ bounding_box,
97
+ nelements[1],
98
+ name=f"{self.name}__1",
99
+ **kwargs,
100
+ )
101
+ ) # ,region=self.region))
102
+ self.builders.append(
103
+ GeologicalFeatureBuilder(
104
+ interpolatortype[2],
105
+ bounding_box,
106
+ nelements[2],
107
+ name=f"{self.name}__2",
108
+ **kwargs,
109
+ )
110
+ ) # ,region=self.region))
111
+
112
+ self._frame = frame(
113
+ self.name,
114
+ [
115
+ self.builders[0].feature,
116
+ self.builders[1].feature,
117
+ self.builders[2].feature,
118
+ ],
119
+ fold=self.fold,
120
+ model=self.model,
121
+ )
122
+ self._frame.builder = self
123
+
124
+ @property
125
+ def frame(self):
126
+ return self._frame
127
+
128
+ def __getitem__(self, item):
129
+ return self.builders[item]
130
+
131
+ def add_fault(self, fault):
132
+ """
133
+ Add a fault to the geological feature builder
134
+
135
+ Parameters
136
+ ----------
137
+ fault : FaultSegment
138
+ A faultsegment to add to the geological feature
139
+
140
+ Returns
141
+ -------
142
+
143
+ """
144
+ for i in range(3):
145
+ self.builders[i].add_fault(fault)
146
+
147
+ def add_data_from_data_frame(self, data_frame):
148
+ """
149
+ extract the data for a fault from a data frame
150
+
151
+ Parameters
152
+ ----------
153
+ data_frame
154
+
155
+ Returns
156
+ -------
157
+
158
+ """
159
+ for i in range(3):
160
+ self.builders[i].add_data_from_data_frame(data_frame.loc[data_frame["coord"] == i, :])
161
+
162
+ def setup(self, w1=1.0, w2=1.0, w3=1.0, **kwargs):
163
+ """
164
+ Build the structural frame
165
+ Parameters
166
+ ----------
167
+ solver solver to use
168
+ frame - type of frame to build StructuralFrame or FoldFrame
169
+ w3
170
+ w2
171
+ w1
172
+ kwargs
173
+
174
+ Returns
175
+ -------
176
+
177
+ """
178
+ step = kwargs.get("step", 10)
179
+ if "gxxgy" in kwargs:
180
+ logger.warning("gxxgy deprecated please use w1")
181
+ w1 = kwargs["gxxgy"]
182
+ if "gxxgz" in kwargs:
183
+ logger.warning("gxxgz deprecated please use w2")
184
+ w2 = kwargs["gxxgz"]
185
+ if "gyxgz" in kwargs:
186
+ logger.warning("gyxgz deprecated please use w3")
187
+ w3 = kwargs["gyxgz"]
188
+
189
+ # set regularisation so the the main surface (foliation, fault) is smooth
190
+ # and the fields are allowed to vary more
191
+ regularisation = kwargs.pop("regularisation", [1.0, 1.0, 1.0])
192
+ if isinstance(regularisation, (int, float)):
193
+ regularisation = np.zeros(3) + regularisation
194
+ logger.info(f"Setting regularisation to {regularisation}")
195
+
196
+ # initialise features as none then where data exists build
197
+ if len(self.builders[0].data) > 0:
198
+ logger.info(f"Building {self.name} coordinate 0")
199
+ kwargs["regularisation"] = regularisation[0]
200
+ self.builders[0].build_arguments = kwargs
201
+ kwargs.pop("fold", None)
202
+
203
+ # make sure that all of the coordinates are using the same region
204
+ if len(self.builders[2].data) > 0:
205
+ logger.info(f"Building {self.name} coordinate 2")
206
+ if w2 > 0:
207
+ self.builders[2].add_orthogonal_feature(self.builders[0].feature, w2, step=step)
208
+ kwargs["regularisation"] = regularisation[2]
209
+ self.builders[2].build_arguments = kwargs
210
+
211
+ if len(self.builders[1].data) > 0:
212
+ logger.info(f"Building {self.name} coordinate 1")
213
+ if w1 > 0:
214
+ self.builders[1].add_orthogonal_feature(self.builders[0].feature, w1, step=step)
215
+ if w3 > 0 and len(self.builders[2].data) > 0:
216
+ self.builders[1].add_orthogonal_feature(self.builders[2].feature, w2, step=step)
217
+ kwargs["regularisation"] = regularisation[1]
218
+ self.builders[1].build_arguments = kwargs
219
+
220
+ if len(self.builders[2].data) == 0:
221
+ from LoopStructural.modelling.features import (
222
+ CrossProductGeologicalFeature,
223
+ )
224
+
225
+ logger.debug("Creating analytical structural frame coordinate 2")
226
+ c3 = CrossProductGeologicalFeature(self.name + "__2", self._frame[0], self._frame[1])
227
+ self._frame[2] = c3
228
+
229
+ # use the frame argument to build a structural frame
230
+
231
+ def update(self):
232
+ for i in range(3):
233
+ self.builders[i].update()
234
+
235
+ def up_to_date(self, callback=None):
236
+ for i in range(3):
237
+ self.builders[i].up_to_date(callback=callback)
@@ -0,0 +1,3 @@
1
+ from ._fault_function import Composite, CubicFunction, Ones, Zeros
2
+ from ._fault_function_feature import FaultDisplacementFeature
3
+ from ._fault_segment import FaultSegment
@@ -0,0 +1,444 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod, ABCMeta
4
+ from typing import Optional
5
+ import numpy as np
6
+
7
+ from ....utils import getLogger
8
+
9
+ logger = getLogger(__name__)
10
+
11
+
12
+ class FaultProfileFunction(metaclass=ABCMeta):
13
+ def __init__(self):
14
+ self.lim = [-1, 1]
15
+ pass
16
+
17
+ @abstractmethod
18
+ def to_dict(self) -> dict:
19
+ pass
20
+
21
+ @abstractmethod
22
+ def __call__(self, v: np.ndarray) -> np.ndarray:
23
+ pass
24
+
25
+ def plot(self, ax=None):
26
+ if ax is None:
27
+ import matplotlib.pyplot as plt
28
+
29
+ fig, ax = plt.subplots()
30
+ x = np.linspace(-1, 1, 100)
31
+ ax.plot(x, self(x), label="ones function")
32
+
33
+
34
+ class CubicFunction(FaultProfileFunction):
35
+ """ """
36
+
37
+ def __init__(self):
38
+ """
39
+ Class to represent a cubic function.
40
+ The cubic function is ax**3 + bx**2 + cx + d
41
+ The coefficients a,b,c,d are calculated from the constraints
42
+
43
+ """
44
+ super().__init__()
45
+ self.A = [] # np.zeros((4,4))
46
+ self.B = [] # np.zeros((4))
47
+ self.max_v = 999999
48
+ self.min_v = -99999
49
+ self.w = np.zeros(4)
50
+ self.up_to_date = False
51
+ self.value_points = []
52
+ self.gradient_points = []
53
+
54
+ def add_cstr(self, x: float, y: float):
55
+ """Add a constraint to the cubic function
56
+
57
+ Parameters
58
+ ----------
59
+ x : float
60
+ x value
61
+ y : float
62
+ y value for the function
63
+ """
64
+ self.up_to_date = False
65
+ self.A.append([x**3, x**2, x, 1.0])
66
+ self.B.append(y)
67
+ self.value_points.append([x, y])
68
+
69
+ def add_grad(self, x, g):
70
+ """Add a gradient constraint to the cubic function
71
+
72
+ Parameters
73
+ ----------
74
+ x : float
75
+ x value
76
+ g : float
77
+ gradient value
78
+ """
79
+ self.up_to_date = False
80
+ self.A.append([3 * x**2, 2 * x, 1.0, 0.0])
81
+ self.B.append(g)
82
+ self.gradient_points.append([x, g])
83
+
84
+ def add_max(self, max_v):
85
+ """Adds a ceiling value to the funciton.
86
+ This is used to limit the maximum value returned
87
+ by the function but is not a constraint for the function."""
88
+ self.max_v = max_v
89
+
90
+ def add_min(self, min_v):
91
+ """Adds a floor value to the funciton.
92
+ This is used to limit the minimum value returned
93
+ by the function but is not a constraint for the function."""
94
+ self.min_v = min_v
95
+
96
+ def set_lim(self, min_x: float, max_x: float):
97
+ """
98
+
99
+ Parameters
100
+ ----------
101
+ min_x : _type_
102
+ _description_
103
+ max_x : _type_
104
+ _description_
105
+ """
106
+ self.lim = [min_x, max_x]
107
+
108
+ def check(self):
109
+ if len(self.B) < 3:
110
+ print("underdetermined")
111
+ raise ValueError("Underdetermined")
112
+
113
+ def solve(self):
114
+ if self.up_to_date:
115
+ return
116
+ self.check()
117
+ A = np.array(self.A)
118
+ B = np.array(self.B)
119
+ ATA = A.T @ A
120
+ ATB = A.T @ B
121
+ self.w = np.linalg.lstsq(ATA, ATB, rcond=None)[0]
122
+ self.up_to_date = True
123
+
124
+ def __call__(self, v):
125
+ self.solve()
126
+ eva = self.w[0] * v**3 + self.w[1] * v**2 + self.w[2] * v + self.w[3]
127
+ eva[v > self.lim[1]] = (
128
+ self.w[0] * self.lim[1] ** 3
129
+ + self.w[1] * self.lim[1] ** 2
130
+ + self.w[2] * self.lim[1]
131
+ + self.w[3]
132
+ )
133
+ eva[v < self.lim[0]] = (
134
+ self.w[0] * self.lim[0] ** 3
135
+ + self.w[1] * self.lim[0] ** 2
136
+ + self.w[2] * self.lim[0]
137
+ + self.w[3]
138
+ )
139
+ eva[eva > self.max_v] = self.max_v
140
+ eva[eva < self.min_v] = self.min_v
141
+
142
+ return eva
143
+
144
+ def to_dict(self) -> dict:
145
+ """Export the function to a dictionary
146
+
147
+
148
+ Returns
149
+ -------
150
+ dict
151
+ Keys A, B, max_v, min_v, w, up_to_date used to create a new CubicFunction
152
+ """
153
+ return {
154
+ "A": self.A,
155
+ "B": self.B,
156
+ "max_v": self.max_v,
157
+ "min_v": self.min_v,
158
+ "w": self.w.tolist(),
159
+ "value_points": self.value_points,
160
+ "gradient_points": self.gradient_points,
161
+ "up_to_date": self.up_to_date,
162
+ }
163
+
164
+ @classmethod
165
+ def from_dict(cls, data: dict) -> CubicFunction:
166
+ """Create a fault profile function from a json dictionary
167
+
168
+ Parameters
169
+ ----------
170
+ data : dict
171
+ Dictionary containing A, B, max_v, min_v, w, up_to_date
172
+
173
+ Returns
174
+ -------
175
+ CubicFunction
176
+ An initialised function given the dictionary parameters
177
+ """
178
+ instance = cls()
179
+ instance.A = data.get("A", [])
180
+ instance.B = data.get("B", [])
181
+ instance.max_v = data.get("max_v", 999999)
182
+ instance.min_v = data.get("min_v", 999999)
183
+ instance.w = np.array(data.get("w", [0, 0, 0, 0]))
184
+ instance.value_points = data.get("value_points", [])
185
+ instance.gradient_points = data.get("gradient_points", [])
186
+ instance.up_to_date = data.get("up_to_date", False)
187
+ return instance
188
+
189
+
190
+ class Composite(FaultProfileFunction):
191
+ """
192
+ A combination of two profiles for the positive and negative values for a coordinate.
193
+ This is used to model the displacement relative to the fault frame coordinate 0
194
+ """
195
+
196
+ def __init__(self, positive: FaultProfileFunction, negative: FaultProfileFunction):
197
+ self.positive = positive
198
+ self.negative = negative
199
+
200
+ def __call__(self, v: np.ndarray) -> np.ndarray:
201
+ """calculate the displacement for the input coordinate
202
+
203
+ Parameters
204
+ ----------
205
+ v : np.ndarray
206
+ fault frame coordinate between -1 and 1
207
+
208
+ Returns
209
+ -------
210
+ np.ndarray
211
+ the displacement for the input coordinate
212
+ """
213
+ v = np.array(v)
214
+ r = np.zeros(v.shape)
215
+ r[v > 0] = self.positive(v[v > 0])
216
+ r[v < 0] = self.negative(v[v < 0])
217
+ return r
218
+
219
+ def to_dict(self) -> dict:
220
+ return {
221
+ "positive": self.positive.to_dict(),
222
+ "negative": self.negative.to_dict(),
223
+ }
224
+
225
+ @classmethod
226
+ def from_dict(cls, data: dict) -> Composite:
227
+ """Create a fault profile function from a json dictionary
228
+
229
+ Parameters
230
+ ----------
231
+ data : _type_
232
+ _description_
233
+
234
+ Returns
235
+ -------
236
+ _type_
237
+ _description_
238
+ """
239
+ positive = CubicFunction.from_dict(data["positive"])
240
+ negative = CubicFunction.from_dict(data["negative"])
241
+ return cls(positive, negative)
242
+
243
+
244
+ class Ones(FaultProfileFunction):
245
+ """
246
+ Returns a fault displacement value of one for the input coordinate
247
+ """
248
+
249
+ def __call__(self, v: np.ndarray) -> np.ndarray:
250
+ """calculate the displacement for the input coordinate
251
+
252
+ Parameters
253
+ ----------
254
+ v : np.ndarray
255
+ fault frame coordinate between -1 and 1
256
+
257
+ Returns
258
+ -------
259
+ np.ndarray
260
+ the displacement for the input coordinate
261
+ """
262
+ v = np.array(v)
263
+ return np.ones(v.shape)
264
+
265
+ def to_dict(self) -> dict:
266
+ return {}
267
+
268
+ @classmethod
269
+ def from_dict(cls, data: dict) -> Ones:
270
+ return cls()
271
+
272
+
273
+ class Zeros(FaultProfileFunction):
274
+ """
275
+ Returns a fault displacement value of zero for the input coordinate
276
+ """
277
+
278
+ def __call__(self, v: np.ndarray) -> np.ndarray:
279
+ """calculate the displacement for the input coordinate
280
+
281
+ Parameters
282
+ ----------
283
+ v : np.ndarray
284
+ fault frame coordinate between -1 and 1
285
+
286
+ Returns
287
+ -------
288
+ np.ndarray
289
+ the displacement for the input coordinate
290
+ """
291
+ v = np.array(v)
292
+ return np.zeros(v.shape)
293
+
294
+ def to_dict(self) -> dict:
295
+ return {}
296
+
297
+ @classmethod
298
+ def from_dict(cls, data: dict) -> Zeros:
299
+ return cls()
300
+
301
+
302
+ class FaultDisplacement:
303
+ def __init__(
304
+ self,
305
+ hw: Optional[FaultProfileFunction] = None,
306
+ fw: Optional[FaultProfileFunction] = None,
307
+ gx: Optional[FaultProfileFunction] = None,
308
+ gy: Optional[FaultProfileFunction] = None,
309
+ gz: Optional[FaultProfileFunction] = None,
310
+ scale=0.5,
311
+ ):
312
+ """Function for characterising the displacement of a fault in 3D space
313
+ given the coordinates of the structural frame
314
+
315
+ Parameters
316
+ ----------
317
+ hw : Optional[FaultProfileFunction], optional
318
+ hanging wall function, by default None
319
+ fw : Optional[FaultProfileFunction], optional
320
+ footwall function, by default None
321
+ gx : Optional[FaultProfileFunction], optional
322
+ displacement in direction normal to fault surface, by default None
323
+ gy : Optional[FaultProfileFunction], optional
324
+ displacement along fault slip direction, by default None
325
+ gz : Optional[FaultProfileFunction], optional
326
+ direction along fault extent direction, by default None
327
+
328
+ """
329
+ self.gx = gx
330
+ if hw is not None and fw is not None:
331
+ self.gx = Composite(hw, fw)
332
+ self.gy = gy
333
+ self.gz = gz
334
+ self.scale = scale
335
+ if self.gx is None:
336
+ print("Gx function none setting to ones")
337
+ self.gx = Ones()
338
+ if self.gy is None:
339
+ print("Gy function none setting to ones")
340
+ self.gy = Ones()
341
+ if self.gz is None:
342
+ print("Gz function none setting to ones")
343
+ self.gz = Ones()
344
+
345
+ if self.gx is None:
346
+ raise ValueError("Gx function none can't model fault")
347
+ if self.gy is None:
348
+ raise ValueError("Gy function none can't model fault")
349
+ if self.gz is None:
350
+ raise ValueError("Gz function none can't model fault")
351
+
352
+ def __call__(self, gx, gy, gz):
353
+
354
+ return self.scale * self.gx(gx) * self.gy(gy) * self.gz(gz)
355
+
356
+ def to_dict(self) -> dict:
357
+ return {
358
+ "gx": self.gx.to_dict(),
359
+ "gy": self.gy.to_dict(),
360
+ "gz": self.gz.to_dict(),
361
+ }
362
+
363
+ @classmethod
364
+ def from_dict(cls, data: dict) -> FaultDisplacement:
365
+ gx = CubicFunction.from_dict(data["gx"])
366
+ gy = CubicFunction.from_dict(data["gy"])
367
+ gz = CubicFunction.from_dict(data["gz"])
368
+ return cls(gx=gx, gy=gy, gz=gz)
369
+
370
+
371
+ class BaseFault(object):
372
+ """ """
373
+
374
+ hw = CubicFunction()
375
+ hw.add_cstr(0, 1)
376
+ hw.add_grad(0, 0)
377
+ hw.add_cstr(1, 0)
378
+ # hw.add_cstr(1,1)
379
+
380
+ hw.add_grad(1, 0)
381
+ hw.set_lim(0, 1)
382
+ fw = CubicFunction()
383
+ fw.add_cstr(0, -1)
384
+ fw.add_grad(0, 0)
385
+ fw.add_cstr(-1, 0)
386
+ fw.add_grad(-1, 0)
387
+ fw.set_lim(-1, 0)
388
+ # gyf = CubicFunction()
389
+ # gyf.add_cstr(-1, 0)
390
+ # gyf.add_cstr(1, 0)
391
+ # gyf.add_cstr(-0.2, 1)
392
+ # gyf.add_cstr(0.2, 1)
393
+ # gyf.add_grad(0, 0)
394
+ # gyf.add_min(-1)
395
+ # gyf.add_max(1)
396
+ gyf = Ones()
397
+ gzf = CubicFunction()
398
+ gzf.add_cstr(-1, 0)
399
+ gzf.add_cstr(1, 0)
400
+ gzf.add_cstr(-0.2, 1)
401
+ gzf.add_cstr(0.2, 1)
402
+ gzf.add_grad(0, 0)
403
+ gzf.add_min(-1)
404
+ gzf.add_max(1)
405
+ gxf = Composite(hw, fw)
406
+ fault_displacement = FaultDisplacement(gx=gxf, gy=gyf, gz=gzf)
407
+
408
+
409
+ class BaseFault3D(object):
410
+ """ """
411
+
412
+ hw = CubicFunction()
413
+ hw.add_cstr(0, 1)
414
+ hw.add_grad(0, 0)
415
+ hw.add_cstr(1, 0)
416
+ # hw.add_cstr(1,1)
417
+
418
+ hw.add_grad(1, 0)
419
+ hw.add_max(1)
420
+ fw = CubicFunction()
421
+ fw.add_cstr(0, -1)
422
+ fw.add_grad(0, 0)
423
+ fw.add_cstr(-1, 0)
424
+ fw.add_grad(-1, 0)
425
+ fw.add_min(-1)
426
+ gyf = CubicFunction()
427
+ gyf.add_cstr(-1, 0)
428
+ gyf.add_cstr(1, 0)
429
+ gyf.add_cstr(-0.2, 1)
430
+ gyf.add_cstr(0.2, 1)
431
+ gyf.add_grad(0, 0)
432
+ gyf.add_min(-1)
433
+ gyf.add_max(1)
434
+ # gyf = Ones()
435
+ gzf = CubicFunction()
436
+ gzf.add_cstr(-1, 0)
437
+ gzf.add_cstr(1, 0)
438
+ gzf.add_cstr(-0.2, 1)
439
+ gzf.add_cstr(0.2, 1)
440
+ gzf.add_grad(0, 0)
441
+ gzf.add_min(-1)
442
+ gzf.add_max(1)
443
+ gxf = Composite(hw, fw)
444
+ fault_displacement = FaultDisplacement(gx=gxf, gy=gyf, gz=gzf)