LoopStructural 1.0.9__cp38-cp38-win32.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.
- LoopStructural/__init__.py +36 -0
- LoopStructural/analysis/__init__.py +2 -0
- LoopStructural/analysis/_fault_displacement.py +67 -0
- LoopStructural/analysis/_fault_intersection.py +36 -0
- LoopStructural/analysis/_plane_fit.py +43 -0
- LoopStructural/datasets/__init__.py +12 -0
- LoopStructural/datasets/_base.py +65 -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/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 +2126 -0
- LoopStructural/interpolators/__init__.py +34 -0
- LoopStructural/interpolators/base_structured_3d_support.py +101 -0
- LoopStructural/interpolators/cython/__init__.py +0 -0
- LoopStructural/interpolators/cython/dsi_helper.c +31046 -0
- LoopStructural/interpolators/cython/dsi_helper.cp38-win32.pyd +0 -0
- LoopStructural/interpolators/discrete_fold_interpolator.py +202 -0
- LoopStructural/interpolators/discrete_interpolator.py +584 -0
- LoopStructural/interpolators/finite_difference_interpolator.py +399 -0
- LoopStructural/interpolators/geological_interpolator.py +197 -0
- LoopStructural/interpolators/operator.py +61 -0
- LoopStructural/interpolators/piecewiselinear_interpolator.py +437 -0
- LoopStructural/interpolators/structured_grid.py +421 -0
- LoopStructural/interpolators/structured_tetra.py +681 -0
- LoopStructural/interpolators/surfe_wrapper.py +118 -0
- LoopStructural/modelling/__init__.py +46 -0
- LoopStructural/modelling/core/__init__.py +0 -0
- LoopStructural/modelling/core/geological_model.py +1513 -0
- LoopStructural/modelling/core/geological_model_graph.py +880 -0
- LoopStructural/modelling/core/stratigraphic_column.py +5 -0
- LoopStructural/modelling/fault/__init__.py +4 -0
- LoopStructural/modelling/fault/fault_builder.py +143 -0
- LoopStructural/modelling/fault/fault_function.py +188 -0
- LoopStructural/modelling/fault/fault_function_feature.py +76 -0
- LoopStructural/modelling/fault/fault_segment.py +298 -0
- LoopStructural/modelling/features/__init__.py +8 -0
- LoopStructural/modelling/features/cross_product_geological_feature.py +90 -0
- LoopStructural/modelling/features/geological_feature.py +252 -0
- LoopStructural/modelling/features/geological_feature_builder.py +414 -0
- LoopStructural/modelling/features/lambda_geological_feature.py +36 -0
- LoopStructural/modelling/features/region_feature.py +34 -0
- LoopStructural/modelling/features/structural_frame.py +133 -0
- LoopStructural/modelling/features/structural_frame_builder.py +206 -0
- LoopStructural/modelling/features/unconformity_feature.py +74 -0
- LoopStructural/modelling/fold/__init__.py +8 -0
- LoopStructural/modelling/fold/fold.py +143 -0
- LoopStructural/modelling/fold/fold_rotation_angle.py +133 -0
- LoopStructural/modelling/fold/fold_rotation_angle_feature.py +58 -0
- LoopStructural/modelling/fold/foldframe.py +193 -0
- LoopStructural/modelling/fold/svariogram.py +180 -0
- LoopStructural/utils/__init__.py +18 -0
- LoopStructural/utils/bounding_box.py +21 -0
- LoopStructural/utils/exceptions.py +10 -0
- LoopStructural/utils/helper.py +382 -0
- LoopStructural/utils/logging.py +60 -0
- LoopStructural/utils/map2loop.py +353 -0
- LoopStructural/utils/regions.py +11 -0
- LoopStructural/utils/utils.py +69 -0
- LoopStructural/visualisation/__init__.py +31 -0
- LoopStructural/visualisation/map_viewer.py +322 -0
- LoopStructural/visualisation/model_plotter.py +17 -0
- LoopStructural/visualisation/model_visualisation.py +1113 -0
- LoopStructural/visualisation/rotation_angle_plotter.py +83 -0
- LoopStructural/visualisation/sphinx_scraper.py +34 -0
- LoopStructural/visualisation/stratigraphic_column.py +60 -0
- LoopStructural-1.0.9.dist-info/LICENSE +21 -0
- LoopStructural-1.0.9.dist-info/METADATA +19 -0
- LoopStructural-1.0.9.dist-info/RECORD +77 -0
- LoopStructural-1.0.9.dist-info/WHEEL +5 -0
- LoopStructural-1.0.9.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Feature builder
|
|
3
|
+
"""
|
|
4
|
+
import copy
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
from LoopStructural.utils import getLogger
|
|
11
|
+
logger = getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
from LoopStructural.utils.helper import xyz_names, val_name, normal_vec_names, \
|
|
14
|
+
weight_name, gradient_vec_names, tangent_vec_names, interface_name
|
|
15
|
+
from LoopStructural.modelling.features import GeologicalFeature
|
|
16
|
+
from LoopStructural.utils.helper import get_data_bounding_box_map as get_data_bounding_box
|
|
17
|
+
from LoopStructural.utils import get_data_axis_aligned_bounding_box
|
|
18
|
+
from LoopStructural.utils import RegionEverywhere
|
|
19
|
+
|
|
20
|
+
class GeologicalFeatureInterpolator:
|
|
21
|
+
"""[summary]
|
|
22
|
+
|
|
23
|
+
[extended_summary]
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, interpolator, name='Feature', region=None, **kwargs):
|
|
26
|
+
"""
|
|
27
|
+
Constructor for a GeologicalFeatureInterpolator
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
interpolator : GeologicalInterpolator
|
|
32
|
+
An empty GeologicalInterpolator
|
|
33
|
+
region : lambda function
|
|
34
|
+
defining whether the location (xyz) should be included in the
|
|
35
|
+
kwargs - name of the feature, region to interpolate the feature
|
|
36
|
+
"""
|
|
37
|
+
self._interpolator = interpolator
|
|
38
|
+
self._name = name
|
|
39
|
+
self._interpolator.set_property_name(self._name)
|
|
40
|
+
# everywhere region is just a lambda that returns true for all locations
|
|
41
|
+
if region is None:
|
|
42
|
+
self.region = RegionEverywhere()
|
|
43
|
+
else:
|
|
44
|
+
self.region = region
|
|
45
|
+
header = xyz_names()+val_name()+gradient_vec_names()+\
|
|
46
|
+
normal_vec_names()+tangent_vec_names()+weight_name()
|
|
47
|
+
self.data = pd.DataFrame(columns=header)
|
|
48
|
+
self.faults = []
|
|
49
|
+
self.data_added = False
|
|
50
|
+
self._interpolator.set_region(region=self.region)
|
|
51
|
+
self._feature = None
|
|
52
|
+
self._up_to_date = False
|
|
53
|
+
self._build_arguments = {}
|
|
54
|
+
self.fold = None
|
|
55
|
+
self._feature = GeologicalFeature(self._name,
|
|
56
|
+
self._interpolator,
|
|
57
|
+
builder=self,
|
|
58
|
+
region=self.region,
|
|
59
|
+
faults=self.faults,
|
|
60
|
+
fold = self.fold
|
|
61
|
+
)
|
|
62
|
+
self._orthogonal_features = {}
|
|
63
|
+
@property
|
|
64
|
+
def feature(self):
|
|
65
|
+
return self._feature
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def build_arguments(self):
|
|
69
|
+
return self._build_arguments
|
|
70
|
+
|
|
71
|
+
@build_arguments.setter
|
|
72
|
+
def build_arguments(self, build_arguments):
|
|
73
|
+
# self._build_arguments = {}
|
|
74
|
+
for k, i in build_arguments.items():
|
|
75
|
+
self._build_arguments[k] = i
|
|
76
|
+
|
|
77
|
+
def update(self):
|
|
78
|
+
self.build(**self.build_arguments)
|
|
79
|
+
@property
|
|
80
|
+
def name(self):
|
|
81
|
+
return self._name
|
|
82
|
+
@property
|
|
83
|
+
def interpolator(self):
|
|
84
|
+
return self._interpolator
|
|
85
|
+
|
|
86
|
+
def up_to_date(self):
|
|
87
|
+
#has anything changed in the builder since we built the feature? if so update
|
|
88
|
+
if self._up_to_date == False:
|
|
89
|
+
self.update()
|
|
90
|
+
#check if the interpolator is up to date, if not solve
|
|
91
|
+
if self._interpolator.up_to_date == False:
|
|
92
|
+
self.update()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def add_fault(self, fault):
|
|
96
|
+
"""
|
|
97
|
+
Add a fault to the geological feature builder
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
fault : FaultSegment
|
|
102
|
+
A faultsegment to add to the geological feature
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
self._up_to_date = False
|
|
109
|
+
self.faults.append(fault)
|
|
110
|
+
|
|
111
|
+
def add_data_from_data_frame(self, data_frame, overwrite = False):
|
|
112
|
+
"""
|
|
113
|
+
Extract data from a pandas dataframe with columns for
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
data_frame : pd.DataFrame
|
|
118
|
+
a dataframe containing the data to be added
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
self.data = data_frame.copy()
|
|
125
|
+
|
|
126
|
+
def add_orthogonal_feature(self, feature, w=1., region=None,step=1,B=0):
|
|
127
|
+
"""
|
|
128
|
+
Add a constraint to the interpolator so that the gradient of an exisitng feature is orthogonal
|
|
129
|
+
to the feature being built. E.g. dot product between gradients should be = 0
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
feature : GeologicalFeature
|
|
134
|
+
feature which we want to be orthogonal to
|
|
135
|
+
w : double
|
|
136
|
+
how much to weight in least squares sense
|
|
137
|
+
region : unused
|
|
138
|
+
step : int
|
|
139
|
+
numpy slicing step size to see how many tetras to add
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
|
|
144
|
+
Notes
|
|
145
|
+
-----
|
|
146
|
+
The constraint can be applied to a random subset of the tetrahedral elements in the mesh
|
|
147
|
+
in theory this shu
|
|
148
|
+
"""
|
|
149
|
+
self._orthogonal_features[feature.name] = [feature,w,region,step,B]
|
|
150
|
+
self._up_to_date = False
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def add_data_to_interpolator(self, constrained=False, force_constrained=False, **kwargs):
|
|
154
|
+
"""
|
|
155
|
+
Iterates through the list of data and applies any faults active on the
|
|
156
|
+
data in the order they are added
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
-----------
|
|
160
|
+
constrained : boolean
|
|
161
|
+
force_constrained : boolean
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
# first move the data for the fault
|
|
168
|
+
logger.info("Adding %i faults to %s" % (len(self.faults), self.name))
|
|
169
|
+
data = self.data.copy()
|
|
170
|
+
# convert data locations to numpy array and then update
|
|
171
|
+
for f in self.faults:
|
|
172
|
+
data.loc[:,xyz_names()] = f.apply_to_points(
|
|
173
|
+
self.get_data_locations())
|
|
174
|
+
# Now check whether there are enough constraints for the
|
|
175
|
+
# interpolator to be able to solve
|
|
176
|
+
# we need at least 2 different value points or a single norm
|
|
177
|
+
# constraint. If there are not enough
|
|
178
|
+
# try converting grad to norms, if still not enough send user an error
|
|
179
|
+
if constrained:
|
|
180
|
+
# Change normals to gradients
|
|
181
|
+
mask = np.all(~np.isnan(data.loc[:, normal_vec_names()]),axis=1)
|
|
182
|
+
if mask.shape[0] > 0:
|
|
183
|
+
data.loc[mask, gradient_vec_names()] = data.loc[mask,
|
|
184
|
+
normal_vec_names()].to_numpy()
|
|
185
|
+
data.loc[mask, normal_vec_names()] = np.nan
|
|
186
|
+
if self.get_norm_constraints().shape[0] > 0:
|
|
187
|
+
constrained = True
|
|
188
|
+
|
|
189
|
+
if np.unique(self.get_value_constraints()[:,3]).shape[0]>1:
|
|
190
|
+
constrained = True
|
|
191
|
+
|
|
192
|
+
if not constrained or force_constrained:
|
|
193
|
+
# change gradient constraints to normal vector constraints
|
|
194
|
+
mask = np.all(~np.isnan(data.loc[:, gradient_vec_names()]), axis=1)
|
|
195
|
+
if mask.shape[0] > 0:
|
|
196
|
+
|
|
197
|
+
data.loc[mask, normal_vec_names()] = data.loc[mask,
|
|
198
|
+
gradient_vec_names()].to_numpy()
|
|
199
|
+
data.loc[mask, gradient_vec_names()] = np.nan
|
|
200
|
+
logger.info(
|
|
201
|
+
"Setting gradient points to norm constraints")
|
|
202
|
+
constrained = True
|
|
203
|
+
mask = np.all(
|
|
204
|
+
~np.isnan(data.loc[:, normal_vec_names()].to_numpy()),
|
|
205
|
+
axis=1)
|
|
206
|
+
|
|
207
|
+
if not constrained:
|
|
208
|
+
logger.error("Not enough constraints for scalar field add more")
|
|
209
|
+
# self.interpolator.reset()
|
|
210
|
+
mask = ~np.isnan(data.loc[:,val_name()].to_numpy())
|
|
211
|
+
# add value constraints
|
|
212
|
+
if mask.shape[0]>0:
|
|
213
|
+
value_data = data.loc[mask[:,0],xyz_names()+val_name()+weight_name()].to_numpy()
|
|
214
|
+
self.interpolator.set_value_constraints(value_data)
|
|
215
|
+
|
|
216
|
+
# add gradient constraints
|
|
217
|
+
mask = np.all(~np.isnan(data.loc[:, gradient_vec_names()].to_numpy()), axis=1)
|
|
218
|
+
if mask.shape[0]>0:
|
|
219
|
+
gradient_data = data.loc[
|
|
220
|
+
mask, xyz_names() + gradient_vec_names() + weight_name()].to_numpy()
|
|
221
|
+
self.interpolator.set_gradient_constraints(gradient_data)
|
|
222
|
+
|
|
223
|
+
# add normal vector data
|
|
224
|
+
mask = np.all(~np.isnan(data.loc[:, normal_vec_names()].to_numpy()), axis=1)
|
|
225
|
+
if mask.shape[0]>0:
|
|
226
|
+
normal_data = data.loc[
|
|
227
|
+
mask, xyz_names() + normal_vec_names() + weight_name()].to_numpy()
|
|
228
|
+
self.interpolator.set_normal_constraints(normal_data)
|
|
229
|
+
|
|
230
|
+
# add tangent data
|
|
231
|
+
mask = np.all(~np.isnan(data.loc[:, tangent_vec_names()].to_numpy()), axis=1)
|
|
232
|
+
if mask.shape[0]>0:
|
|
233
|
+
tangent_data = data.loc[
|
|
234
|
+
mask, xyz_names() + tangent_vec_names() + weight_name()].to_numpy()
|
|
235
|
+
self.interpolator.set_tangent_constraints(tangent_data)
|
|
236
|
+
|
|
237
|
+
# add interface constraints
|
|
238
|
+
mask = np.all(~np.isnan(data.loc[:, interface_name()].to_numpy()), axis=1)
|
|
239
|
+
if mask.shape[0] > 0:
|
|
240
|
+
interface_data = data.loc[
|
|
241
|
+
mask, xyz_names() + interface_name() + weight_name()].to_numpy()
|
|
242
|
+
self.interpolator.set_interface_constraints(interface_data)
|
|
243
|
+
|
|
244
|
+
self.data_added = True
|
|
245
|
+
self._up_to_date = False
|
|
246
|
+
|
|
247
|
+
def install_gradient_constraint(self):
|
|
248
|
+
for g in self._orthogonal_features.values():
|
|
249
|
+
feature,w,region,step,B = g
|
|
250
|
+
vector = feature.evaluate_gradient(self.interpolator.support.barycentre())
|
|
251
|
+
vector /= np.linalg.norm(vector,axis=1)[:,None]
|
|
252
|
+
element_idx = np.arange(self.interpolator.support.n_elements)
|
|
253
|
+
np.random.shuffle(element_idx)
|
|
254
|
+
self.interpolator.add_gradient_orthogonal_constraint(
|
|
255
|
+
self.interpolator.support.barycentre()[element_idx[::step],:],
|
|
256
|
+
vector[element_idx[::step],:],
|
|
257
|
+
w=w,
|
|
258
|
+
B=B
|
|
259
|
+
)
|
|
260
|
+
def get_value_constraints(self):
|
|
261
|
+
"""
|
|
262
|
+
Get the value constraints for this geological feature
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
np.array((N,4),dtype=double)
|
|
267
|
+
"""
|
|
268
|
+
header = xyz_names()+val_name()+weight_name()
|
|
269
|
+
mask = ~np.isnan(self.data.loc[:,val_name()].to_numpy())
|
|
270
|
+
return self.data.loc[mask[:,0],header].to_numpy()
|
|
271
|
+
|
|
272
|
+
def get_gradient_constraints(self):
|
|
273
|
+
"""
|
|
274
|
+
Get the gradient direction constraints
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
numpy array
|
|
279
|
+
"""
|
|
280
|
+
mask = np.all(
|
|
281
|
+
~np.isnan(self.data.loc[:, gradient_vec_names()].to_numpy()),
|
|
282
|
+
axis=1)
|
|
283
|
+
if mask.shape[0] > 0:
|
|
284
|
+
return self.data.loc[
|
|
285
|
+
mask, xyz_names() + gradient_vec_names() + weight_name(
|
|
286
|
+
)].to_numpy()
|
|
287
|
+
else:
|
|
288
|
+
return np.zeros((0, 7))
|
|
289
|
+
|
|
290
|
+
def get_tangent_constraints(self):
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
numpy array
|
|
296
|
+
"""
|
|
297
|
+
mask = np.all(
|
|
298
|
+
~np.isnan(self.data.loc[:, tangent_vec_names()].to_numpy()),
|
|
299
|
+
axis=1)
|
|
300
|
+
if mask.shape[0] > 0:
|
|
301
|
+
return self.data.loc[
|
|
302
|
+
mask, xyz_names() + tangent_vec_names() + weight_name(
|
|
303
|
+
)].to_numpy()
|
|
304
|
+
else:
|
|
305
|
+
return np.zeros((0, 7))
|
|
306
|
+
|
|
307
|
+
def get_norm_constraints(self):
|
|
308
|
+
"""
|
|
309
|
+
Get the gradient norm constraints
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
numpy array
|
|
314
|
+
"""
|
|
315
|
+
mask = np.all(~np.isnan(self.data.loc[:, normal_vec_names()].to_numpy()),
|
|
316
|
+
axis=1)
|
|
317
|
+
if mask.shape[0] > 0:
|
|
318
|
+
return self.data.loc[
|
|
319
|
+
mask, xyz_names() + normal_vec_names() + weight_name(
|
|
320
|
+
|
|
321
|
+
)].to_numpy()
|
|
322
|
+
else:
|
|
323
|
+
return np.zeros((0,7))
|
|
324
|
+
|
|
325
|
+
def get_interface_constraints(self):
|
|
326
|
+
mask = np.all(~np.isnan(self.data.loc[:, interface_name()].to_numpy()), axis=1)
|
|
327
|
+
if mask.shape[0] > 0:
|
|
328
|
+
return self.data.loc[
|
|
329
|
+
mask, xyz_names() + interface_name() + weight_name()].to_numpy()
|
|
330
|
+
else:
|
|
331
|
+
return np.zeros((0,5))
|
|
332
|
+
|
|
333
|
+
def get_data_locations(self):
|
|
334
|
+
"""
|
|
335
|
+
Get only the location for all data points
|
|
336
|
+
|
|
337
|
+
Returns
|
|
338
|
+
-------
|
|
339
|
+
|
|
340
|
+
"""
|
|
341
|
+
return self.data.loc[:, xyz_names()].to_numpy()
|
|
342
|
+
def set_interpolation_geometry(self,origin,maximum):
|
|
343
|
+
"""Update the interpolation support geometry to new bounding box
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
origin : np.array(3)
|
|
348
|
+
origin vector
|
|
349
|
+
maximum : np.array(3)
|
|
350
|
+
maximum vector
|
|
351
|
+
"""
|
|
352
|
+
logger.info("Setting mesh origin: {} {} {} ".format(origin[0],origin[1],origin[2]))
|
|
353
|
+
logger.info("Setting mesh maximum: {} {} {}".format(maximum[0],maximum[1],maximum[2]))
|
|
354
|
+
self.interpolator.support.origin = origin
|
|
355
|
+
self.interpolator.support.maximum = maximum
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
self._up_to_date = False
|
|
359
|
+
|
|
360
|
+
def build(self, fold=None, fold_weights={}, data_region=None, **kwargs):
|
|
361
|
+
"""
|
|
362
|
+
Runs the interpolation and builds the geological feature
|
|
363
|
+
|
|
364
|
+
Parameters
|
|
365
|
+
----------
|
|
366
|
+
fold : FoldEvent
|
|
367
|
+
fold_weights : dict
|
|
368
|
+
data_region : double <1
|
|
369
|
+
If not none adds a region around the data points to the interpolation
|
|
370
|
+
with data_region as a buffer
|
|
371
|
+
kwargs
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
self.add_data_to_interpolator(**kwargs)
|
|
380
|
+
if data_region is not None:
|
|
381
|
+
xyz = self.interpolator.get_data_locations()
|
|
382
|
+
bb, region = get_data_bounding_box(xyz, data_region)
|
|
383
|
+
# if self.model.reuse_supports == False:
|
|
384
|
+
if np.any(np.min(bb[0,:])< self.interpolator.support.origin):
|
|
385
|
+
neworigin = np.min([self.interpolator.support.origin,bb[0,:]],axis=0)
|
|
386
|
+
logger.info("Changing origin of support for {} from {} {} {} to {} {} {}"\
|
|
387
|
+
.format(self.name,self.interpolator.support.origin[0],\
|
|
388
|
+
self.interpolator.support.origin[1],self.interpolator.support.origin[2],\
|
|
389
|
+
neworigin[0],neworigin[1],neworigin[2]))
|
|
390
|
+
self.interpolator.support.origin = neworigin
|
|
391
|
+
if np.any(np.max(bb[0,:])< self.interpolator.support.maximum):
|
|
392
|
+
newmax = np.max([self.interpolator.support.maximum,bb[0,:]],axis=0)
|
|
393
|
+
logger.info("Changing origin of support for {} from {} {} {} to {} {} {}"\
|
|
394
|
+
.format(self.name,self.interpolator.support.maximum[0],
|
|
395
|
+
self.interpolator.support.maximum[1],self.interpolator.support.maximum[2],\
|
|
396
|
+
newmax[0],newmax[1],newmax[2]))
|
|
397
|
+
|
|
398
|
+
self.interpolator.support.maximum = newmax
|
|
399
|
+
self.interpolator.set_region(region=region)
|
|
400
|
+
# moving this to init because it needs to be done before constraints
|
|
401
|
+
# are added?
|
|
402
|
+
if fold is not None:
|
|
403
|
+
logger.info("Adding fold to %s" % self.name)
|
|
404
|
+
self.interpolator.fold = fold
|
|
405
|
+
# if we have fold weights use those, otherwise just use default
|
|
406
|
+
self.interpolator.add_fold_constraints(**fold_weights)
|
|
407
|
+
if 'cgw' not in kwargs:
|
|
408
|
+
# try adding very small cg
|
|
409
|
+
kwargs['cgw'] = 0.0
|
|
410
|
+
self.install_gradient_constraint()
|
|
411
|
+
self.interpolator.setup_interpolator(**kwargs)
|
|
412
|
+
self.interpolator.solve_system(**kwargs)
|
|
413
|
+
self._up_to_date = True
|
|
414
|
+
return self._feature
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geological features
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LambdaGeologicalFeature:
|
|
12
|
+
def __init__(self,function = None,name = 'unnamed_lambda', gradient_function = None, model = None):
|
|
13
|
+
self.function = function
|
|
14
|
+
self.name = name
|
|
15
|
+
self.gradient_function = gradient_function
|
|
16
|
+
self.model = model
|
|
17
|
+
def evaluate_value(self, xyz):
|
|
18
|
+
v = np.zeros((xyz.shape[0]))
|
|
19
|
+
if self.function is None:
|
|
20
|
+
v[:] = np.nan
|
|
21
|
+
else:
|
|
22
|
+
v[:] = self.function(xyz)
|
|
23
|
+
return v
|
|
24
|
+
def evaluate_gradient(self,xyz):
|
|
25
|
+
v = np.zeros((xyz.shape[0],3))
|
|
26
|
+
if self.gradient_function is None:
|
|
27
|
+
v[:,:] = np.nan
|
|
28
|
+
else:
|
|
29
|
+
v[:,:] = self.gradient_function(xyz)
|
|
30
|
+
return v
|
|
31
|
+
def min(self):
|
|
32
|
+
return 0
|
|
33
|
+
def max(self):
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class RegionFeature:
|
|
2
|
+
"""
|
|
3
|
+
"""
|
|
4
|
+
def __init__(self, function):
|
|
5
|
+
"""
|
|
6
|
+
Create a GeologicalFeature to represent a region in a model
|
|
7
|
+
The region is defined by a boolean function on position.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
function lambda function
|
|
12
|
+
lambda function true inside region, false outside region
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
self.function = function
|
|
16
|
+
self.name = 'region'
|
|
17
|
+
|
|
18
|
+
def evaluate_value(self, pos):
|
|
19
|
+
return self.function(pos).astype(float)
|
|
20
|
+
|
|
21
|
+
def mean(self):
|
|
22
|
+
return 0
|
|
23
|
+
|
|
24
|
+
def max(self):
|
|
25
|
+
return 1
|
|
26
|
+
|
|
27
|
+
def min(self):
|
|
28
|
+
return -1
|
|
29
|
+
|
|
30
|
+
def name(self):
|
|
31
|
+
return self.name
|
|
32
|
+
|
|
33
|
+
# class VectorFeature:
|
|
34
|
+
# def __init__(self, function)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structural frames
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
import numpy as np
|
|
6
|
+
from LoopStructural.utils import getLogger
|
|
7
|
+
logger = getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StructuralFrame:
|
|
11
|
+
"""[summary]
|
|
12
|
+
|
|
13
|
+
[extended_summary]
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, name, features, fold=None):
|
|
16
|
+
"""
|
|
17
|
+
Structural frame is a curvilinear coordinate system defined by
|
|
18
|
+
structural observations associated with a fault or fold.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
name - name of the structural frame
|
|
23
|
+
features - list of features to build the frame with
|
|
24
|
+
"""
|
|
25
|
+
self.name = name
|
|
26
|
+
self.features = features
|
|
27
|
+
self.data = None
|
|
28
|
+
self.fold = fold
|
|
29
|
+
|
|
30
|
+
def __getitem__(self, item):
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
item index of feature to access
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
the structural frame geological feature
|
|
40
|
+
"""
|
|
41
|
+
return self.features[item]
|
|
42
|
+
|
|
43
|
+
def set_model(self, model):
|
|
44
|
+
"""Link the model that created the frame to the frame
|
|
45
|
+
and the features that make up the frame
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
model : GeologicalModel
|
|
50
|
+
the geological model that created the fold frame
|
|
51
|
+
"""
|
|
52
|
+
self.model = model
|
|
53
|
+
for f in self.features:
|
|
54
|
+
if f is None:
|
|
55
|
+
continue
|
|
56
|
+
f.set_model(model)
|
|
57
|
+
|
|
58
|
+
def add_region(self, region):
|
|
59
|
+
for i in range(3):
|
|
60
|
+
self.features[i].add_region(region)
|
|
61
|
+
|
|
62
|
+
def get_feature(self, i):
|
|
63
|
+
"""
|
|
64
|
+
Return the ith feature
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
i
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
return self.features[i]
|
|
75
|
+
|
|
76
|
+
def set_data(self, data):
|
|
77
|
+
"""
|
|
78
|
+
Associate data with structural frame
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
data
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
self.data = data
|
|
89
|
+
|
|
90
|
+
def evaluate_value(self, evaluation_points):
|
|
91
|
+
"""
|
|
92
|
+
Evaluate the value of the structural frame for the points.
|
|
93
|
+
Can optionally only evaluate one coordinate
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
evaluation_points
|
|
98
|
+
i
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
v = np.zeros(evaluation_points.shape) #create new 3d array of correct length
|
|
105
|
+
v[:] = np.nan
|
|
106
|
+
v[:,0] = self.features[0].evaluate_value(evaluation_points)
|
|
107
|
+
v[:,1] = self.features[1].evaluate_value(evaluation_points)
|
|
108
|
+
v[:,2] = self.features[2].evaluate_value(evaluation_points)
|
|
109
|
+
return v
|
|
110
|
+
|
|
111
|
+
def evaluate_gradient(self, evaluation_points, i=None):
|
|
112
|
+
"""
|
|
113
|
+
Evaluate the gradient of the structural frame.
|
|
114
|
+
Can optionally only evaluate the ith coordinate
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
evaluation_points
|
|
119
|
+
i
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
if i is not None:
|
|
126
|
+
return self.features[i].support.evaluate_gradient(
|
|
127
|
+
evaluation_points)
|
|
128
|
+
return (self.features[0].support.evaluate_gradient(evaluation_points),
|
|
129
|
+
self.features[1].support.evaluate_gradient(evaluation_points),
|
|
130
|
+
self.features[2].support.evaluate_gradient(evaluation_points))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|