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.
- LoopStructural/__init__.py +52 -0
- LoopStructural/datasets/__init__.py +23 -0
- LoopStructural/datasets/_base.py +301 -0
- LoopStructural/datasets/_example_models.py +10 -0
- LoopStructural/datasets/data/claudius.csv +21049 -0
- LoopStructural/datasets/data/claudiusbb.txt +2 -0
- LoopStructural/datasets/data/duplex.csv +126 -0
- LoopStructural/datasets/data/duplexbb.txt +2 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.cpg +1 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.prj +1 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
- LoopStructural/datasets/data/geological_map_data/bbox.csv +2 -0
- LoopStructural/datasets/data/geological_map_data/contacts.csv +657 -0
- LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +7 -0
- LoopStructural/datasets/data/geological_map_data/fault_edges.txt +2 -0
- LoopStructural/datasets/data/geological_map_data/fault_locations.csv +79 -0
- LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +19 -0
- LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +13 -0
- LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +207 -0
- LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +13 -0
- LoopStructural/datasets/data/intrusion.csv +1017 -0
- LoopStructural/datasets/data/intrusionbb.txt +2 -0
- LoopStructural/datasets/data/onefoldbb.txt +2 -0
- LoopStructural/datasets/data/onefolddata.csv +2226 -0
- LoopStructural/datasets/data/refolded_bb.txt +2 -0
- LoopStructural/datasets/data/refolded_fold.csv +205 -0
- LoopStructural/datasets/data/tabular_intrusion.csv +23 -0
- LoopStructural/datatypes/__init__.py +4 -0
- LoopStructural/datatypes/_bounding_box.py +422 -0
- LoopStructural/datatypes/_point.py +166 -0
- LoopStructural/datatypes/_structured_grid.py +94 -0
- LoopStructural/datatypes/_surface.py +184 -0
- LoopStructural/export/exporters.py +554 -0
- LoopStructural/export/file_formats.py +15 -0
- LoopStructural/export/geoh5.py +100 -0
- LoopStructural/export/gocad.py +126 -0
- LoopStructural/export/omf_wrapper.py +88 -0
- LoopStructural/interpolators/__init__.py +105 -0
- LoopStructural/interpolators/_api.py +143 -0
- LoopStructural/interpolators/_builders.py +149 -0
- LoopStructural/interpolators/_cython/__init__.py +0 -0
- LoopStructural/interpolators/_discrete_fold_interpolator.py +183 -0
- LoopStructural/interpolators/_discrete_interpolator.py +692 -0
- LoopStructural/interpolators/_finite_difference_interpolator.py +470 -0
- LoopStructural/interpolators/_geological_interpolator.py +380 -0
- LoopStructural/interpolators/_interpolator_factory.py +89 -0
- LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
- LoopStructural/interpolators/_operator.py +38 -0
- LoopStructural/interpolators/_p1interpolator.py +228 -0
- LoopStructural/interpolators/_p2interpolator.py +277 -0
- LoopStructural/interpolators/_surfe_wrapper.py +174 -0
- LoopStructural/interpolators/supports/_2d_base_unstructured.py +340 -0
- LoopStructural/interpolators/supports/_2d_p1_unstructured.py +68 -0
- LoopStructural/interpolators/supports/_2d_p2_unstructured.py +288 -0
- LoopStructural/interpolators/supports/_2d_structured_grid.py +462 -0
- LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
- LoopStructural/interpolators/supports/_3d_base_structured.py +467 -0
- LoopStructural/interpolators/supports/_3d_p2_tetra.py +331 -0
- LoopStructural/interpolators/supports/_3d_structured_grid.py +470 -0
- LoopStructural/interpolators/supports/_3d_structured_tetra.py +746 -0
- LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +637 -0
- LoopStructural/interpolators/supports/__init__.py +55 -0
- LoopStructural/interpolators/supports/_aabb.py +77 -0
- LoopStructural/interpolators/supports/_base_support.py +114 -0
- LoopStructural/interpolators/supports/_face_table.py +70 -0
- LoopStructural/interpolators/supports/_support_factory.py +32 -0
- LoopStructural/modelling/__init__.py +29 -0
- LoopStructural/modelling/core/__init__.py +0 -0
- LoopStructural/modelling/core/geological_model.py +1867 -0
- LoopStructural/modelling/features/__init__.py +32 -0
- LoopStructural/modelling/features/_analytical_feature.py +79 -0
- LoopStructural/modelling/features/_base_geological_feature.py +364 -0
- LoopStructural/modelling/features/_cross_product_geological_feature.py +100 -0
- LoopStructural/modelling/features/_geological_feature.py +288 -0
- LoopStructural/modelling/features/_lambda_geological_feature.py +93 -0
- LoopStructural/modelling/features/_region.py +18 -0
- LoopStructural/modelling/features/_structural_frame.py +186 -0
- LoopStructural/modelling/features/_unconformity_feature.py +83 -0
- LoopStructural/modelling/features/builders/__init__.py +5 -0
- LoopStructural/modelling/features/builders/_base_builder.py +111 -0
- LoopStructural/modelling/features/builders/_fault_builder.py +590 -0
- LoopStructural/modelling/features/builders/_folded_feature_builder.py +129 -0
- LoopStructural/modelling/features/builders/_geological_feature_builder.py +543 -0
- LoopStructural/modelling/features/builders/_structural_frame_builder.py +237 -0
- LoopStructural/modelling/features/fault/__init__.py +3 -0
- LoopStructural/modelling/features/fault/_fault_function.py +444 -0
- LoopStructural/modelling/features/fault/_fault_function_feature.py +82 -0
- LoopStructural/modelling/features/fault/_fault_segment.py +505 -0
- LoopStructural/modelling/features/fold/__init__.py +9 -0
- LoopStructural/modelling/features/fold/_fold.py +167 -0
- LoopStructural/modelling/features/fold/_fold_rotation_angle.py +149 -0
- LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +67 -0
- LoopStructural/modelling/features/fold/_foldframe.py +194 -0
- LoopStructural/modelling/features/fold/_svariogram.py +188 -0
- LoopStructural/modelling/input/__init__.py +2 -0
- LoopStructural/modelling/input/fault_network.py +80 -0
- LoopStructural/modelling/input/map2loop_processor.py +165 -0
- LoopStructural/modelling/input/process_data.py +650 -0
- LoopStructural/modelling/input/project_file.py +84 -0
- LoopStructural/modelling/intrusions/__init__.py +25 -0
- LoopStructural/modelling/intrusions/geom_conceptual_models.py +142 -0
- LoopStructural/modelling/intrusions/geometric_scaling_functions.py +123 -0
- LoopStructural/modelling/intrusions/intrusion_builder.py +672 -0
- LoopStructural/modelling/intrusions/intrusion_feature.py +410 -0
- LoopStructural/modelling/intrusions/intrusion_frame_builder.py +971 -0
- LoopStructural/modelling/intrusions/intrusion_support_functions.py +460 -0
- LoopStructural/utils/__init__.py +38 -0
- LoopStructural/utils/_surface.py +143 -0
- LoopStructural/utils/_transformation.py +76 -0
- LoopStructural/utils/config.py +18 -0
- LoopStructural/utils/dtm_creator.py +17 -0
- LoopStructural/utils/exceptions.py +31 -0
- LoopStructural/utils/helper.py +292 -0
- LoopStructural/utils/json_encoder.py +18 -0
- LoopStructural/utils/linalg.py +8 -0
- LoopStructural/utils/logging.py +79 -0
- LoopStructural/utils/maths.py +245 -0
- LoopStructural/utils/regions.py +103 -0
- LoopStructural/utils/typing.py +7 -0
- LoopStructural/utils/utils.py +68 -0
- LoopStructural/version.py +1 -0
- LoopStructural/visualisation/__init__.py +11 -0
- LoopStructural-1.6.1.dist-info/LICENSE +21 -0
- LoopStructural-1.6.1.dist-info/METADATA +81 -0
- LoopStructural-1.6.1.dist-info/RECORD +129 -0
- LoopStructural-1.6.1.dist-info/WHEEL +5 -0
- 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,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)
|