LoopStructural 1.0.1__zip
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.
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/__init__.py +33 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__init__.py +12 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/_base.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/_base.py +65 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/claudius.csv +21049 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/claudiusbb.txt +2 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/duplex.csv +126 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/duplexbb.txt +2 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/intrusion.csv +1017 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/intrusionbb.txt +2 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/onefoldbb.txt +2 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/onefolddata.csv +2226 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/refolded_bb.txt +2 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/refolded_fold.csv +2126 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__init__.py +31 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_fold_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/finite_difference_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/geological_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/operator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/piecewiselinear_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_grid.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_tetra.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/surfe_wrapper.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__init__.py +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.c +27805 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.cp37-win_amd64.pyd +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_fold_interpolator.py +168 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_interpolator.py +551 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/finite_difference_interpolator.py +339 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/geological_interpolator.py +178 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/operator.py +46 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/piecewiselinear_interpolator.py +300 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_grid.py +460 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_tetra.py +637 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/surfe_wrapper.py +119 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__init__.py +46 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__init__.py +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/geological_model.py +1179 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__init__.py +3 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_segment.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function.py +187 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function_feature.py +75 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_segment.py +270 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__init__.py +7 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/cross_product_geological_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature_builder.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/region_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame_builder.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/unconformity_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/cross_product_geological_feature.py +77 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature.py +276 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature_builder.py +289 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/region_feature.py +31 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame.py +116 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame_builder.py +179 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/unconformity_feature.py +69 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__init__.py +8 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/foldframe.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/svariogram.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold.py +135 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle.py +132 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle_feature.py +57 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/foldframe.py +191 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/svariogram.py +179 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__init__.py +14 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/exceptions.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/helper.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/map2loop.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/utils.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/exceptions.py +9 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/helper.py +373 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/map2loop.py +229 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/utils.py +76 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__init__.py +19 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/map_viewer.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_plotter.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_visualisation.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/rotation_angle_plotter.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/sphinx_scraper.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/map_viewer.py +122 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_plotter.py +16 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_visualisation.py +704 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/rotation_angle_plotter.py +66 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/sphinx_scraper.py +34 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/PKG-INFO +10 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/SOURCES.txt +60 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/dependency_links.txt +1 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/requires.txt +3 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/top_level.txt +2 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__init__.py +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_faults.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_fold.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_refolded.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/test_faults.py +17 -0
- Miniconda/envs/loop/Lib/site-packages/tests/test_fold.py +57 -0
- Miniconda/envs/loop/Lib/site-packages/tests/test_interpolator.py +88 -0
- Miniconda/envs/loop/Lib/site-packages/tests/test_refolded.py +22 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geological features
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GeologicalFeature:
|
|
12
|
+
"""
|
|
13
|
+
Geological feature is class that is used to represent a geometrical element in a geological
|
|
14
|
+
model. For example foliations, fault planes, fold rotation angles etc.
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
name : string
|
|
19
|
+
should be a unique name for the geological feature
|
|
20
|
+
support : a ScalarField
|
|
21
|
+
holds the property values for the feature and links to the
|
|
22
|
+
support geometry
|
|
23
|
+
data : list
|
|
24
|
+
list containing geological data
|
|
25
|
+
region : list
|
|
26
|
+
list of boolean functions defining whether the feature is
|
|
27
|
+
active
|
|
28
|
+
faults : list
|
|
29
|
+
list of FaultSegments that affect this feature
|
|
30
|
+
"""
|
|
31
|
+
def __init__(self, name, interpolator, builder=None, data=None, region=None, type=None,
|
|
32
|
+
faults=[], fold = None):
|
|
33
|
+
"""Default constructor for geological feature
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
name: string
|
|
38
|
+
interpolator : GeologicalInterpolator
|
|
39
|
+
builder : GeologicalFeatureBuilder
|
|
40
|
+
data :
|
|
41
|
+
region :
|
|
42
|
+
type :
|
|
43
|
+
faults :
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
self.name = name
|
|
48
|
+
self.interpolator = interpolator
|
|
49
|
+
self.ndim = 1
|
|
50
|
+
self.data = data
|
|
51
|
+
self.builder = builder
|
|
52
|
+
self.region = region
|
|
53
|
+
self.regions = []
|
|
54
|
+
self.type = type
|
|
55
|
+
self.faults = faults
|
|
56
|
+
self.faults_enabled = True
|
|
57
|
+
self.fold=fold
|
|
58
|
+
self._attributes = {}
|
|
59
|
+
self._attributes['feature'] = self
|
|
60
|
+
self._attributes['builder'] = self.builder
|
|
61
|
+
self._attributes['faults'] = self.faults
|
|
62
|
+
if region is None:
|
|
63
|
+
self.region = 'everywhere'
|
|
64
|
+
self.model = None
|
|
65
|
+
|
|
66
|
+
def __str__(self):
|
|
67
|
+
return self.name
|
|
68
|
+
|
|
69
|
+
def __getitem__(self,key):
|
|
70
|
+
return self._attributes[key]
|
|
71
|
+
|
|
72
|
+
def __setitem__(self, key, item):
|
|
73
|
+
self._attributes[key] = item
|
|
74
|
+
|
|
75
|
+
def set_model(self, model):
|
|
76
|
+
self.model = model
|
|
77
|
+
|
|
78
|
+
def toggle_faults(self):
|
|
79
|
+
"""
|
|
80
|
+
Turn the fault off for a feature
|
|
81
|
+
This function is only really used for debugging or creating methods
|
|
82
|
+
explanation figures
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
self.faults_enabled = ~self.faults_enabled
|
|
89
|
+
|
|
90
|
+
def add_region(self, region):
|
|
91
|
+
"""
|
|
92
|
+
Adds a region where the geological feature is active to the model.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
region : boolean function(x,y,z)
|
|
97
|
+
- returns true if inside region, false if outside
|
|
98
|
+
can be passed as a lambda function e.g.
|
|
99
|
+
lambda pos : feature.evaluate_value(pos) > 0
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
self.regions.append(region)
|
|
106
|
+
|
|
107
|
+
def set_builder(self, builder):
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
builder
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
self.builder = builder
|
|
119
|
+
|
|
120
|
+
def evaluate_value(self, evaluation_points):
|
|
121
|
+
"""
|
|
122
|
+
Evaluate the scalar field value of the geological feature at the locations
|
|
123
|
+
specified
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
evaluation_points : numpy array
|
|
128
|
+
location to evaluate the scalar value
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
values : numpy array
|
|
133
|
+
numpy array containing evaluated values
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
# check if the points are within the display region
|
|
138
|
+
v = np.zeros(evaluation_points.shape[0])
|
|
139
|
+
v[:] = np.nan
|
|
140
|
+
mask = np.zeros(evaluation_points.shape[0]).astype(bool)
|
|
141
|
+
mask[:] = True
|
|
142
|
+
# check regions
|
|
143
|
+
for r in self.regions:
|
|
144
|
+
mask = np.logical_and(mask, r(evaluation_points))
|
|
145
|
+
# apply faulting after working out which regions are visible
|
|
146
|
+
if self.faults_enabled:
|
|
147
|
+
for f in self.faults:
|
|
148
|
+
evaluation_points = f.apply_to_points(evaluation_points)
|
|
149
|
+
v[mask] = self.interpolator.evaluate_value(evaluation_points[mask, :])
|
|
150
|
+
return v
|
|
151
|
+
|
|
152
|
+
def evaluate_gradient(self, evaluation_points):
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
locations : numpy array
|
|
158
|
+
location where the gradient is being evaluated
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
v = np.zeros(evaluation_points.shape)
|
|
165
|
+
v[:] = np.nan
|
|
166
|
+
mask = np.zeros(evaluation_points.shape[0]).astype(bool)
|
|
167
|
+
mask[:] = True
|
|
168
|
+
# check regions
|
|
169
|
+
for r in self.regions:
|
|
170
|
+
mask = np.logical_and(mask, r(evaluation_points))
|
|
171
|
+
|
|
172
|
+
# apply faulting after working out which regions are visible
|
|
173
|
+
if self.faults_enabled:
|
|
174
|
+
for f in self.faults:
|
|
175
|
+
evaluation_points = f.apply_to_points(evaluation_points)
|
|
176
|
+
v[mask, :] = self.interpolator.evaluate_gradient(evaluation_points)
|
|
177
|
+
|
|
178
|
+
return v
|
|
179
|
+
|
|
180
|
+
def evaluate_gradient_misfit(self):
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
|
|
186
|
+
"""
|
|
187
|
+
grad = self.interpolator.get_gradient_constraints()
|
|
188
|
+
norm = self.interpolator.get_norm_constraints()
|
|
189
|
+
dot = []
|
|
190
|
+
if grad.shape[0] > 0:
|
|
191
|
+
model_grad = self.evaluate_gradient(grad[:,:3])
|
|
192
|
+
dot.append(np.einsum('ij,ij->i',model_grad,grad[:,:3:6]).tolist())
|
|
193
|
+
|
|
194
|
+
if norm.shape[0] > 0:
|
|
195
|
+
model_norm = self.evaluate_gradient(norm[:, :3])
|
|
196
|
+
dot.append(np.einsum('ij,ij->i', model_norm, norm[:,:3:6]))
|
|
197
|
+
|
|
198
|
+
return np.array(dot)
|
|
199
|
+
|
|
200
|
+
def evaluate_value_misfit(self):
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
Returns
|
|
204
|
+
-------
|
|
205
|
+
|
|
206
|
+
"""
|
|
207
|
+
locations = self.interpolator.get_value_constraints()
|
|
208
|
+
diff = np.abs(locations[:, 3] - self.evaluate_value(locations[:, :3]))
|
|
209
|
+
diff/=(self.max()-self.min())
|
|
210
|
+
return diff
|
|
211
|
+
|
|
212
|
+
def mean(self):
|
|
213
|
+
"""
|
|
214
|
+
Calculate average of the support values
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
|
|
219
|
+
"""
|
|
220
|
+
if self.model is None:
|
|
221
|
+
return 0
|
|
222
|
+
return np.mean(self.evaluate_value(self.model.regular_grid((10,10,10))))
|
|
223
|
+
|
|
224
|
+
def min(self):
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
|
|
230
|
+
"""
|
|
231
|
+
if self.model is None:
|
|
232
|
+
return 0
|
|
233
|
+
return np.nanmin(
|
|
234
|
+
self.evaluate_value(self.model.regular_grid((10, 10, 10))))
|
|
235
|
+
|
|
236
|
+
def max(self):
|
|
237
|
+
"""
|
|
238
|
+
Calculate average of the support values
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
|
|
243
|
+
"""
|
|
244
|
+
if self.model is None:
|
|
245
|
+
return 0
|
|
246
|
+
return np.nanmax(
|
|
247
|
+
self.evaluate_value(self.model.regular_grid((10, 10, 10))))
|
|
248
|
+
|
|
249
|
+
def update(self):
|
|
250
|
+
"""
|
|
251
|
+
Calculate average of the support values
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
|
|
256
|
+
"""
|
|
257
|
+
# re-run the interpolator and update the support.
|
|
258
|
+
# this is a bit clumsy and not abstract, i think
|
|
259
|
+
# if evaluating the property doesn't require the dictionary on
|
|
260
|
+
# the nodes and actually just uses the interpolator values this
|
|
261
|
+
# would be
|
|
262
|
+
# much better.
|
|
263
|
+
self.interpolator.up_to_date = False
|
|
264
|
+
self.interpolator.update()
|
|
265
|
+
|
|
266
|
+
def get_interpolator(self):
|
|
267
|
+
"""
|
|
268
|
+
Get the interpolator used to build this feature
|
|
269
|
+
|
|
270
|
+
Returns
|
|
271
|
+
-------
|
|
272
|
+
GeologicalInterpolator
|
|
273
|
+
"""
|
|
274
|
+
return self.interpolator
|
|
275
|
+
|
|
276
|
+
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Feature builder
|
|
3
|
+
"""
|
|
4
|
+
import copy
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
from LoopStructural.utils.helper import xyz_names, val_name, normal_vec_names, \
|
|
13
|
+
weight_name, gradient_vec_names, tangent_vec_names
|
|
14
|
+
from LoopStructural.modelling.features import GeologicalFeature
|
|
15
|
+
from LoopStructural.utils.helper import get_data_bounding_box_map as get_data_bounding_box
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GeologicalFeatureInterpolator:
|
|
19
|
+
"""[summary]
|
|
20
|
+
|
|
21
|
+
[extended_summary]
|
|
22
|
+
"""
|
|
23
|
+
def __init__(self, interpolator, name='Feature', region=None, **kwargs):
|
|
24
|
+
"""
|
|
25
|
+
A builder for a GeologicalFeature will link data to the interpolator
|
|
26
|
+
and run the interpolation
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
interpolator - a GeologicalInterpolator
|
|
31
|
+
region : lambda function
|
|
32
|
+
defining whether the location (xyz) should be included in the
|
|
33
|
+
kwargs - name of the feature, region to interpolate the feature
|
|
34
|
+
"""
|
|
35
|
+
self.interpolator = interpolator
|
|
36
|
+
self.name = name
|
|
37
|
+
self.interpolator.set_property_name(self.name)
|
|
38
|
+
# everywhere region is just a lambda that returns true for all locations
|
|
39
|
+
if region is None:
|
|
40
|
+
self.region = lambda pos: np.ones(pos.shape[0], dtype=bool)
|
|
41
|
+
else:
|
|
42
|
+
self.region = region
|
|
43
|
+
header = xyz_names()+val_name()+gradient_vec_names()+\
|
|
44
|
+
normal_vec_names()+tangent_vec_names()+weight_name()
|
|
45
|
+
self.data = pd.DataFrame(columns=header)
|
|
46
|
+
self.faults = []
|
|
47
|
+
self.data_added = False
|
|
48
|
+
self.interpolator.set_region(region=self.region)
|
|
49
|
+
|
|
50
|
+
def update(self):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
def add_fault(self, fault):
|
|
54
|
+
"""
|
|
55
|
+
Add a fault to the geological feature builder
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
fault FaultSegment
|
|
60
|
+
A faultsegment to add to the geological feature
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
self.faults.append(fault)
|
|
67
|
+
|
|
68
|
+
def add_data_from_data_frame(self, data_frame):
|
|
69
|
+
"""
|
|
70
|
+
Extract data from a pandas dataframe with columns for
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
data_frame - pandas data frame
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
self.data = data_frame.copy()
|
|
81
|
+
|
|
82
|
+
def add_orthogonal_feature(self, feature, w=1., region=None):
|
|
83
|
+
self.interpolator.add_gradient_orthogonal_constraint(
|
|
84
|
+
self.interpolator.support.barycentre(),
|
|
85
|
+
feature.evaluate_gradient(self.interpolator.support.barycentre()),
|
|
86
|
+
w=w
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def add_data_to_interpolator(self, constrained=False, force_constrained=False, **kwargs):
|
|
90
|
+
"""
|
|
91
|
+
Iterates through the list of data and applies any faults active on the
|
|
92
|
+
data in the order they are added
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
# first move the data for the fault
|
|
99
|
+
logger.info("Adding %i faults to %s" % (len(self.faults), self.name))
|
|
100
|
+
data = self.data.copy()
|
|
101
|
+
# convert data locations to numpy array and then update
|
|
102
|
+
for f in self.faults:
|
|
103
|
+
data.loc[:,xyz_names()] = f.apply_to_points(
|
|
104
|
+
self.get_data_locations())
|
|
105
|
+
# Now check whether there are enough constraints for the
|
|
106
|
+
# interpolator to be able to solve
|
|
107
|
+
# we need at least 2 different value points or a single norm
|
|
108
|
+
# constraint. If there are not enough
|
|
109
|
+
# try converting grad to norms, if still not enough send user an error
|
|
110
|
+
if constrained:
|
|
111
|
+
# Change normals to gradients
|
|
112
|
+
mask = np.all(~np.isnan(data.loc[:, normal_vec_names()]),axis=1)
|
|
113
|
+
if mask.shape[0] > 0:
|
|
114
|
+
data.loc[mask, gradient_vec_names()] = data.loc[mask,
|
|
115
|
+
normal_vec_names()].to_numpy()
|
|
116
|
+
data.loc[mask, normal_vec_names()] = np.nan
|
|
117
|
+
if self.get_norm_constraints().shape[0] > 0:
|
|
118
|
+
constrained = True
|
|
119
|
+
|
|
120
|
+
if np.unique(self.get_value_constraints()[:,3]).shape[0]>1:
|
|
121
|
+
constrained = True
|
|
122
|
+
|
|
123
|
+
if not constrained or force_constrained:
|
|
124
|
+
# change gradient constraints to normal vector constraints
|
|
125
|
+
mask = np.all(~np.isnan(data.loc[:, gradient_vec_names()]), axis=1)
|
|
126
|
+
if mask.shape[0] > 0:
|
|
127
|
+
|
|
128
|
+
data.loc[mask, normal_vec_names()] = data.loc[mask,
|
|
129
|
+
gradient_vec_names()].to_numpy()
|
|
130
|
+
data.loc[mask, gradient_vec_names()] = np.nan
|
|
131
|
+
logger.info(
|
|
132
|
+
"Setting gradient points to norm constraints")
|
|
133
|
+
constrained = True
|
|
134
|
+
mask = np.all(
|
|
135
|
+
~np.isnan(data.loc[:, normal_vec_names()].to_numpy()),
|
|
136
|
+
axis=1)
|
|
137
|
+
|
|
138
|
+
if not constrained:
|
|
139
|
+
logger.error("Not enough constraints for scalar field add more")
|
|
140
|
+
# self.interpolator.reset()
|
|
141
|
+
mask = ~np.isnan(data.loc[:,val_name()].to_numpy())
|
|
142
|
+
|
|
143
|
+
if mask.shape[0]>0:
|
|
144
|
+
value_data = data.loc[mask[:,0],xyz_names()+val_name()+weight_name()].to_numpy()
|
|
145
|
+
self.interpolator.set_value_constraints(value_data)
|
|
146
|
+
|
|
147
|
+
mask = np.all(~np.isnan(data.loc[:, gradient_vec_names()].to_numpy()), axis=1)
|
|
148
|
+
if mask.shape[0]>0:
|
|
149
|
+
gradient_data = data.loc[
|
|
150
|
+
mask, xyz_names() + gradient_vec_names() + weight_name()].to_numpy()
|
|
151
|
+
self.interpolator.set_gradient_constraints(gradient_data)
|
|
152
|
+
|
|
153
|
+
mask = np.all(~np.isnan(data.loc[:, normal_vec_names()].to_numpy()), axis=1)
|
|
154
|
+
if mask.shape[0]>0:
|
|
155
|
+
normal_data = data.loc[
|
|
156
|
+
mask, xyz_names() + normal_vec_names() + weight_name()].to_numpy()
|
|
157
|
+
self.interpolator.set_normal_constraints(normal_data)
|
|
158
|
+
|
|
159
|
+
mask = np.all(~np.isnan(data.loc[:, tangent_vec_names()].to_numpy()), axis=1)
|
|
160
|
+
if mask.shape[0]>0:
|
|
161
|
+
tangent_data = data.loc[
|
|
162
|
+
mask, xyz_names() + tangent_vec_names() + weight_name()].to_numpy()
|
|
163
|
+
self.interpolator.set_tangent_constraints(tangent_data)
|
|
164
|
+
|
|
165
|
+
self.data_added = True
|
|
166
|
+
|
|
167
|
+
def get_value_constraints(self):
|
|
168
|
+
"""
|
|
169
|
+
Get the value constraints for this geological feature
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
numpy array
|
|
174
|
+
"""
|
|
175
|
+
header = xyz_names()+val_name()+weight_name()
|
|
176
|
+
mask = ~np.isnan(self.data.loc[:,val_name()].to_numpy())
|
|
177
|
+
return self.data.loc[mask[:,0],header].to_numpy()
|
|
178
|
+
|
|
179
|
+
def get_gradient_constraints(self):
|
|
180
|
+
"""
|
|
181
|
+
Get the gradient direction constraints
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
numpy array
|
|
186
|
+
"""
|
|
187
|
+
mask = np.all(
|
|
188
|
+
~np.isnan(self.data.loc[:, gradient_vec_names()].to_numpy()),
|
|
189
|
+
axis=1)
|
|
190
|
+
if mask.shape[0] > 0:
|
|
191
|
+
return self.data.loc[
|
|
192
|
+
mask, xyz_names() + gradient_vec_names() + weight_name(
|
|
193
|
+
)].to_numpy()
|
|
194
|
+
else:
|
|
195
|
+
return np.zeros(0, 7)
|
|
196
|
+
|
|
197
|
+
def get_tangent_constraints(self):
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
numpy array
|
|
203
|
+
"""
|
|
204
|
+
mask = np.all(
|
|
205
|
+
~np.isnan(self.data.loc[:, tangent_vec_names()].to_numpy()),
|
|
206
|
+
axis=1)
|
|
207
|
+
if mask.shape[0] > 0:
|
|
208
|
+
return self.data.loc[
|
|
209
|
+
mask, xyz_names() + tangent_vec_names() + weight_name(
|
|
210
|
+
)].to_numpy()
|
|
211
|
+
else:
|
|
212
|
+
return np.zeros(0, 7)
|
|
213
|
+
|
|
214
|
+
def get_norm_constraints(self):
|
|
215
|
+
"""
|
|
216
|
+
Get the gradient norm constraints
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
numpy array
|
|
221
|
+
"""
|
|
222
|
+
mask = np.all(~np.isnan(self.data.loc[:, normal_vec_names()].to_numpy()),
|
|
223
|
+
axis=1)
|
|
224
|
+
if mask.shape[0] > 0:
|
|
225
|
+
return self.data.loc[
|
|
226
|
+
mask, xyz_names() + normal_vec_names() + weight_name(
|
|
227
|
+
|
|
228
|
+
)].to_numpy()
|
|
229
|
+
else:
|
|
230
|
+
return np.zeros(0,7)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def get_data_locations(self):
|
|
234
|
+
"""
|
|
235
|
+
Get only the location for all data points
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
|
|
240
|
+
"""
|
|
241
|
+
return self.data.loc[:, xyz_names()].to_numpy()
|
|
242
|
+
|
|
243
|
+
def build(self, fold=None, fold_weights=None, data_region=None, **kwargs):
|
|
244
|
+
"""
|
|
245
|
+
Runs the interpolation and builds the geological feature
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
fold : FoldEvent
|
|
250
|
+
fold_weights : dict
|
|
251
|
+
data_region : double <1
|
|
252
|
+
If not none adds a region around the data points to the interpolation
|
|
253
|
+
with data_region as a buffer
|
|
254
|
+
kwargs
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
if data_region is not None:
|
|
262
|
+
xyz = self.get_data_locations()
|
|
263
|
+
bb, region = get_data_bounding_box(xyz, data_region)
|
|
264
|
+
self.interpolator.set_region(region=region)
|
|
265
|
+
if not self.data_added:
|
|
266
|
+
self.add_data_to_interpolator(**kwargs)
|
|
267
|
+
|
|
268
|
+
# moving this to init because it needs to be done before constraints
|
|
269
|
+
# are added?
|
|
270
|
+
if fold is not None:
|
|
271
|
+
logger.info("Adding fold to %s" % self.name)
|
|
272
|
+
self.interpolator.fold = fold
|
|
273
|
+
# if we have fold weights use those, otherwise just use default
|
|
274
|
+
if fold_weights is None:
|
|
275
|
+
self.interpolator.add_fold_constraints()
|
|
276
|
+
else:
|
|
277
|
+
self.interpolator.add_fold_constraints(fold_weights)
|
|
278
|
+
if 'cgw' not in kwargs:
|
|
279
|
+
kwargs['cgw'] = 0.
|
|
280
|
+
|
|
281
|
+
self.interpolator.setup_interpolator(**kwargs)
|
|
282
|
+
self.interpolator.solve_system(**kwargs)
|
|
283
|
+
return GeologicalFeature(self.name,
|
|
284
|
+
self.interpolator,
|
|
285
|
+
builder=self, data=self.data,
|
|
286
|
+
region=self.region,
|
|
287
|
+
faults=self.faults,
|
|
288
|
+
fold = fold
|
|
289
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structural frames
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StructuralFrame:
|
|
10
|
+
"""[summary]
|
|
11
|
+
|
|
12
|
+
[extended_summary]
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, name, features, fold=None):
|
|
15
|
+
"""
|
|
16
|
+
Structural frame is a curvilinear coordinate system defined by
|
|
17
|
+
structural
|
|
18
|
+
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
|
+
def __getitem__(self, item):
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
item index of feature to access
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
the structural frame geological feature
|
|
39
|
+
"""
|
|
40
|
+
return self.features[item]
|
|
41
|
+
|
|
42
|
+
def add_region(self, region):
|
|
43
|
+
for i in range(3):
|
|
44
|
+
self.features[i].add_region(region)
|
|
45
|
+
|
|
46
|
+
def get_feature(self, i):
|
|
47
|
+
"""
|
|
48
|
+
Return the ith feature
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
i
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
return self.features[i]
|
|
59
|
+
|
|
60
|
+
def set_data(self, data):
|
|
61
|
+
"""
|
|
62
|
+
Associate data with structural frame
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
data
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
self.data = data
|
|
73
|
+
|
|
74
|
+
def evaluate_value(self, evaluation_points, i=None):
|
|
75
|
+
"""
|
|
76
|
+
Evaluate the value of the structural frame for the points.
|
|
77
|
+
Can optionally only evaluate one coordinate
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
evaluation_points
|
|
82
|
+
i
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
if i is not None:
|
|
89
|
+
self.features[i].support.evaluate_value(evaluation_points)
|
|
90
|
+
return (self.features[0].support.evaluate_value(evaluation_points),
|
|
91
|
+
self.features[1].support.evaluate_value(evaluation_points),
|
|
92
|
+
self.features[2].support.evaluate_value(evaluation_points))
|
|
93
|
+
|
|
94
|
+
def evaluate_gradient(self, evaluation_points, i=None):
|
|
95
|
+
"""
|
|
96
|
+
Evaluate the gradient of the structural frame.
|
|
97
|
+
Can optionally only evaluate the ith coordinate
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
evaluation_points
|
|
102
|
+
i
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
if i is not None:
|
|
109
|
+
return self.features[i].support.evaluate_gradient(
|
|
110
|
+
evaluation_points)
|
|
111
|
+
return (self.features[0].support.evaluate_gradient(evaluation_points),
|
|
112
|
+
self.features[1].support.evaluate_gradient(evaluation_points),
|
|
113
|
+
self.features[2].support.evaluate_gradient(evaluation_points))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|