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,188 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ....utils import getLogger
|
|
4
|
+
|
|
5
|
+
logger = getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def find_peaks_and_troughs(x, y):
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
x np.array or list
|
|
14
|
+
x axis data for plot
|
|
15
|
+
y np.array or list
|
|
16
|
+
y axis data for plot
|
|
17
|
+
Returns
|
|
18
|
+
-------
|
|
19
|
+
(np.array, np.array)
|
|
20
|
+
Notes
|
|
21
|
+
-----
|
|
22
|
+
Returns the loations of maxima/minima on the curve using finite
|
|
23
|
+
difference forward/backwards
|
|
24
|
+
finding the change in derivative
|
|
25
|
+
"""
|
|
26
|
+
if len(x) != len(y):
|
|
27
|
+
return False
|
|
28
|
+
pairsx = []
|
|
29
|
+
pairsy = []
|
|
30
|
+
# #TODO numpyize
|
|
31
|
+
for i in range(0, len(x)):
|
|
32
|
+
if i < 1:
|
|
33
|
+
pairsx.append(x[i])
|
|
34
|
+
pairsy.append(y[i])
|
|
35
|
+
|
|
36
|
+
continue
|
|
37
|
+
if i > len(x) - 2:
|
|
38
|
+
pairsx.append(x[i])
|
|
39
|
+
pairsy.append(y[i])
|
|
40
|
+
continue
|
|
41
|
+
left_grad = (y[i - 1] - y[i]) / (x[i - 1] - x[i])
|
|
42
|
+
right_grad = (y[i] - y[i + 1]) / (x[i] - x[i + 1])
|
|
43
|
+
if np.sign(left_grad) != np.sign(right_grad):
|
|
44
|
+
pairsx.append(x[i])
|
|
45
|
+
pairsy.append(y[i])
|
|
46
|
+
return pairsx, pairsy
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SVariogram:
|
|
50
|
+
"""
|
|
51
|
+
The SVariogram is an experimental semi-variogram.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, xdata, ydata):
|
|
55
|
+
self.xdata = xdata
|
|
56
|
+
self.ydata = ydata
|
|
57
|
+
self.dist = np.abs(self.xdata[:, None] - self.xdata[None, :])
|
|
58
|
+
self.variance_matrix = (self.ydata[:, None] - self.ydata[None, :]) ** 2
|
|
59
|
+
self.lags = None
|
|
60
|
+
self.variogram = None
|
|
61
|
+
self.wavelength_guess = [None, None]
|
|
62
|
+
|
|
63
|
+
def calc_semivariogram(self, lag=None, nlag=None, lags=None):
|
|
64
|
+
"""
|
|
65
|
+
Calculate a semi-variogram for the x and y data for this object.
|
|
66
|
+
You can specify the lags as an array or specify the step size and
|
|
67
|
+
number of steps.
|
|
68
|
+
If neither are specified then the lags are created to be the average
|
|
69
|
+
spacing of the data
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
step: float
|
|
74
|
+
lag distance for the s-variogram
|
|
75
|
+
nstep: int
|
|
76
|
+
number of lags for the s-variogram
|
|
77
|
+
lags: array
|
|
78
|
+
num
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
logger.info("Calculating S-Variogram")
|
|
85
|
+
if lag is not None:
|
|
86
|
+
step = lag
|
|
87
|
+
logger.info(f"Using lag: {step} kwarg for S-variogram")
|
|
88
|
+
|
|
89
|
+
if nlag is not None:
|
|
90
|
+
nstep = nlag
|
|
91
|
+
logger.info(f"Using nlag {nstep} kwarg for s-variogram")
|
|
92
|
+
|
|
93
|
+
self.lags = np.arange(step / 2.0, nstep * step, step)
|
|
94
|
+
|
|
95
|
+
if nlag is None and lag is not None:
|
|
96
|
+
nstep = int(np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
|
|
97
|
+
logger.info(f"Using lag kwarg but calculating nlag as {nstep} for s-variogram")
|
|
98
|
+
|
|
99
|
+
self.lags = np.arange(step / 2.0, nstep * step, step)
|
|
100
|
+
|
|
101
|
+
if lags is not None:
|
|
102
|
+
self.lags = lags
|
|
103
|
+
|
|
104
|
+
if self.lags is None:
|
|
105
|
+
# time to guess the step size
|
|
106
|
+
# find the average distance between elements in the input data
|
|
107
|
+
d = np.copy(self.dist)
|
|
108
|
+
d[d == 0] = np.nan
|
|
109
|
+
|
|
110
|
+
step = np.nanmean(np.nanmin(d, axis=1)) * 4.0
|
|
111
|
+
# find number of steps to cover range in data
|
|
112
|
+
nstep = int(np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
|
|
113
|
+
if nstep > 200:
|
|
114
|
+
logger.warning(f"Variogram has too many steps: {nstep}, using 200")
|
|
115
|
+
maximum = step * nstep
|
|
116
|
+
nstep = 200
|
|
117
|
+
step = maximum / nstep
|
|
118
|
+
self.lags = np.arange(step / 2.0, nstep * step, step)
|
|
119
|
+
logger.info(
|
|
120
|
+
f"Using average minimum nearest neighbour distance as lag distance size {step} and using {nstep} lags"
|
|
121
|
+
)
|
|
122
|
+
tol = self.lags[1] - self.lags[0]
|
|
123
|
+
self.variogram = np.zeros(self.lags.shape)
|
|
124
|
+
self.variogram[:] = np.nan
|
|
125
|
+
npairs = np.zeros(self.lags.shape)
|
|
126
|
+
for i in range(len(self.lags)):
|
|
127
|
+
logic = np.logical_and(
|
|
128
|
+
self.dist > self.lags[i] - tol / 2.0,
|
|
129
|
+
self.dist < self.lags[i] + tol / 2.0,
|
|
130
|
+
)
|
|
131
|
+
npairs[i] = np.sum(logic.astype(int))
|
|
132
|
+
if npairs[i] > 0:
|
|
133
|
+
self.variogram[i] = np.mean(self.variance_matrix[logic])
|
|
134
|
+
return self.lags, self.variogram, npairs
|
|
135
|
+
|
|
136
|
+
def find_wavelengths(self, **kwargs):
|
|
137
|
+
"""
|
|
138
|
+
Picks the wavelengths of the fold by finding the maximum and
|
|
139
|
+
minimums of the s-variogram
|
|
140
|
+
the fold wavelength is the first minimum but it is more reliable to
|
|
141
|
+
use the first maximum
|
|
142
|
+
as the estimate of the wavelength.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
kwargs : object
|
|
147
|
+
"""
|
|
148
|
+
h, var, npairs = self.calc_semivariogram(**kwargs)
|
|
149
|
+
|
|
150
|
+
px, py = find_peaks_and_troughs(h, var)
|
|
151
|
+
|
|
152
|
+
averagex = []
|
|
153
|
+
averagey = []
|
|
154
|
+
for i in range(len(px) - 1):
|
|
155
|
+
averagex.append((px[i] + px[i + 1]) / 2.0)
|
|
156
|
+
averagey.append((py[i] + py[i + 1]) / 2.0)
|
|
157
|
+
i += 1 # iterate twice
|
|
158
|
+
# find the extrema of the average curve
|
|
159
|
+
px2, py2 = find_peaks_and_troughs(averagex, averagey)
|
|
160
|
+
wl1 = 0.0
|
|
161
|
+
wl1py = 0.0
|
|
162
|
+
for i in range(len(px)):
|
|
163
|
+
if i > 0 and i < len(px) - 1:
|
|
164
|
+
if py[i] > 10:
|
|
165
|
+
|
|
166
|
+
if py[i - 1] < py[i] * 0.7:
|
|
167
|
+
if py[i + 1] < py[i] * 0.7:
|
|
168
|
+
wl1 = px[i]
|
|
169
|
+
if wl1 > 0.0:
|
|
170
|
+
wl1py = py[i]
|
|
171
|
+
break
|
|
172
|
+
wl2 = 0.0
|
|
173
|
+
for i in range(len(px2)):
|
|
174
|
+
if i > 0 and i < len(px2) - 1:
|
|
175
|
+
if py2[i - 1] < py2[i] * 0.90:
|
|
176
|
+
if py2[i + 1] < py2[i] * 0.90:
|
|
177
|
+
wl2 = px2[i]
|
|
178
|
+
if wl2 > 0.0 and wl2 > wl1 * 2 and wl1py < py2[i]:
|
|
179
|
+
break
|
|
180
|
+
if wl1 == 0.0 and wl2 == 0.0:
|
|
181
|
+
self.wavelength_guess = [2 * (np.max(self.xdata) - np.min(self.xdata)), 0.0]
|
|
182
|
+
return self.wavelength_guess
|
|
183
|
+
if np.isclose(wl1, 0.0):
|
|
184
|
+
self.wavelength_guess = np.array([wl2 * 2.0, wl1 * 2.0])
|
|
185
|
+
return self.wavelength_guess
|
|
186
|
+
# wavelength is 2x the peak on the curve
|
|
187
|
+
self.wavelength_guess = np.array([wl1 * 2.0, wl2 * 2.0])
|
|
188
|
+
return self.wavelength_guess
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FaultNetwork:
|
|
5
|
+
def __init__(self, faults):
|
|
6
|
+
"""A fault network is a basic graph structure that
|
|
7
|
+
can return the faults for building a geological model
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
faults : list
|
|
12
|
+
list of fault names
|
|
13
|
+
"""
|
|
14
|
+
self.faults = faults
|
|
15
|
+
self.fault_edge_count = np.zeros(len(faults), dtype=int)
|
|
16
|
+
self.fault_edges = dict(zip(faults, np.arange(len(faults), dtype=int)))
|
|
17
|
+
self.fault_edge_properties = {}
|
|
18
|
+
# connections
|
|
19
|
+
self.connections = {}
|
|
20
|
+
|
|
21
|
+
def add_connection(self, fault1, fault2, properties=None):
|
|
22
|
+
"""fault 1 is younger than fault2
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
fault1 : string
|
|
27
|
+
name of younger fault
|
|
28
|
+
fault2 : string
|
|
29
|
+
name of older fault
|
|
30
|
+
"""
|
|
31
|
+
self.connections[fault2] = fault1
|
|
32
|
+
self.fault_edge_properties[(fault1, fault2)] = properties
|
|
33
|
+
# self.fault_edge_count[self.fault_edges[fault1]] +=1
|
|
34
|
+
self.fault_edge_count[self.fault_edges[fault1]] += 1
|
|
35
|
+
|
|
36
|
+
def get_fault_iterators(self):
|
|
37
|
+
"""
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
iterators : list
|
|
41
|
+
list of fault iterators
|
|
42
|
+
"""
|
|
43
|
+
fault_idxs = np.where(self.fault_edge_count == 0)[0]
|
|
44
|
+
iters = []
|
|
45
|
+
for f in fault_idxs:
|
|
46
|
+
fault = self.faults[f]
|
|
47
|
+
iters.append(FaultNetworkIter(fault, self))
|
|
48
|
+
return iters
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class FaultNetworkIter:
|
|
52
|
+
"""Iterator object to return the next oldest fault in a fault network following edges"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, faultname, fault_network):
|
|
55
|
+
"""[summary]
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
faultname : string
|
|
60
|
+
unique name of the fault
|
|
61
|
+
fault_network : FaultNetwork
|
|
62
|
+
the fault network with edges
|
|
63
|
+
"""
|
|
64
|
+
self.faultname = faultname
|
|
65
|
+
self.fault_network = fault_network
|
|
66
|
+
|
|
67
|
+
def __next__(self):
|
|
68
|
+
"""next method for iterator
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
FaultNetworkIterator
|
|
73
|
+
iterator for the next fault, None if the fault is end of an edge
|
|
74
|
+
"""
|
|
75
|
+
if self.faultname in self.fault_network.connections:
|
|
76
|
+
return FaultNetworkIter(
|
|
77
|
+
self.fault_network.connections[self.faultname], self.fault_network
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
return None
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from .process_data import ProcessInputData
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import networkx
|
|
5
|
+
|
|
6
|
+
from ...utils import getLogger
|
|
7
|
+
|
|
8
|
+
logger = getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Map2LoopProcessor(ProcessInputData):
|
|
12
|
+
def __init__(self, m2l_directory, use_thickness=None):
|
|
13
|
+
"""Function to build a ProcessInputData object for using m2l data
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
m2l_directory : path
|
|
18
|
+
path to a m2l root directory
|
|
19
|
+
"""
|
|
20
|
+
groups = pd.read_csv(f"{m2l_directory}/tmp/all_sorts_clean.csv", index_col=0)
|
|
21
|
+
orientations = pd.read_csv(f"{m2l_directory}/output/orientations_clean.csv")
|
|
22
|
+
formation_thickness = pd.read_csv(
|
|
23
|
+
f"{m2l_directory}/output/formation_summary_thicknesses.csv"
|
|
24
|
+
)
|
|
25
|
+
contacts = pd.read_csv(m2l_directory + "/output/contacts_clean.csv")
|
|
26
|
+
fault_displacements = pd.read_csv(f"{m2l_directory}/output/fault_displacements3.csv")
|
|
27
|
+
fault_orientations = pd.read_csv(f"{m2l_directory}/output/fault_orientations.csv")
|
|
28
|
+
fault_locations = pd.read_csv(m2l_directory + "/output/faults.csv")
|
|
29
|
+
fault_strat = pd.read_csv(f"{m2l_directory}/output/supergroup-fault-relationships.csv")
|
|
30
|
+
fault_dimensions = pd.read_csv(
|
|
31
|
+
f"{m2l_directory}/output/fault_dimensions.csv", index_col="Fault"
|
|
32
|
+
)
|
|
33
|
+
fault_graph = networkx.read_gml(f"{m2l_directory}/tmp/fault_network.gml")
|
|
34
|
+
fault_orientations.rename(columns={"formation": "fault_name"}, inplace=True)
|
|
35
|
+
|
|
36
|
+
bb = np.loadtxt(f"{m2l_directory}/tmp/bbox.csv", skiprows=1, delimiter=",")
|
|
37
|
+
fault_dimensions["displacement"] = np.nan
|
|
38
|
+
fault_dimensions["downthrow_dir"] = np.nan
|
|
39
|
+
fault_dimensions["dip_dir"] = np.nan
|
|
40
|
+
for fname in fault_dimensions.index:
|
|
41
|
+
fault_dimensions.loc[fname, "displacement"] = fault_displacements.loc[
|
|
42
|
+
fault_displacements["fname"] == fname, "vertical_displacement"
|
|
43
|
+
].max()
|
|
44
|
+
fault_dimensions.loc[fname, "downthrow_dir"] = fault_displacements.loc[
|
|
45
|
+
fault_displacements.loc[
|
|
46
|
+
fault_displacements["fname"] == fname, "vertical_displacement"
|
|
47
|
+
].idxmax(),
|
|
48
|
+
"downthrow_dir",
|
|
49
|
+
]
|
|
50
|
+
fault_dimensions.loc[fname, "dip_dir"] = fault_orientations.loc[
|
|
51
|
+
fault_orientations["fault_name"] == fname, "DipDirection"
|
|
52
|
+
].median()
|
|
53
|
+
fault_properties = fault_dimensions.rename(
|
|
54
|
+
columns={
|
|
55
|
+
"Fault": "fault_name",
|
|
56
|
+
"InfluenceDistance": "minor_axis",
|
|
57
|
+
"VerticalRadius": "intermediate_axis",
|
|
58
|
+
"HorizontalRadius": "major_axis",
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
self.process_downthrow_direction(fault_properties, fault_orientations)
|
|
62
|
+
fault_orientations["strike"] = fault_orientations["DipDirection"] + 90
|
|
63
|
+
fault_edge_properties = []
|
|
64
|
+
for e in fault_graph.edges():
|
|
65
|
+
fault_edge_properties.append({"angle": fault_graph.get_edge_data(*e)["angle"]})
|
|
66
|
+
|
|
67
|
+
fault_locations.rename(columns={"formation": "fault_name"}, inplace=True)
|
|
68
|
+
contacts.rename(columns={"formation": "name"}, inplace=True)
|
|
69
|
+
orientations.rename(columns={"formation": "name"}, inplace=True)
|
|
70
|
+
fault_stratigraphy = None
|
|
71
|
+
# make sure supergroups are in the groups dataframe
|
|
72
|
+
|
|
73
|
+
supergroups = {}
|
|
74
|
+
with open(f"{m2l_directory}/tmp/super_groups.csv") as f:
|
|
75
|
+
for line in f:
|
|
76
|
+
|
|
77
|
+
i = 0
|
|
78
|
+
for g in line.strip(",\n").split(","):
|
|
79
|
+
supergroups[g] = "supergroup_{}".format(i)
|
|
80
|
+
i += 1
|
|
81
|
+
if "supergroup" not in groups.columns:
|
|
82
|
+
groups["supergroup"] = "none"
|
|
83
|
+
for i in groups.index:
|
|
84
|
+
groups.loc[i, "supergroup"] = supergroups[groups.loc[i, "group"]]
|
|
85
|
+
# create an ordered list of stratigraphic groups for interpolation,
|
|
86
|
+
# name of the scalar field will be the name in 'supergroups' column
|
|
87
|
+
stratigraphic_order = []
|
|
88
|
+
supergroup = groups.loc[0, "supergroup"]
|
|
89
|
+
tmp = []
|
|
90
|
+
for i in groups.index:
|
|
91
|
+
if supergroup != groups.loc[i, "supergroup"]:
|
|
92
|
+
stratigraphic_order.append((supergroup, tmp))
|
|
93
|
+
supergroup = groups.loc[i, "supergroup"]
|
|
94
|
+
tmp = []
|
|
95
|
+
tmp.append(groups.loc[i, "code"])
|
|
96
|
+
|
|
97
|
+
stratigraphic_order.append((supergroup, tmp))
|
|
98
|
+
|
|
99
|
+
# stratigraphic_order = [list(groups['code'])]
|
|
100
|
+
thicknesses = dict(
|
|
101
|
+
zip(
|
|
102
|
+
list(formation_thickness["formation"]),
|
|
103
|
+
list(formation_thickness["thickness median"]),
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
fault_properties["colour"] = "black"
|
|
107
|
+
if (
|
|
108
|
+
np.sum(orientations["polarity"] == 0) > 0
|
|
109
|
+
and np.sum(orientations["polarity"] == -1) == 0
|
|
110
|
+
):
|
|
111
|
+
orientations.loc[orientations["polarity"] == 0, "polarity"] = -1
|
|
112
|
+
|
|
113
|
+
fault_stratigraphy = {}
|
|
114
|
+
for strat in fault_strat["supergroup"].unique():
|
|
115
|
+
mask = (fault_strat.loc[fault_strat["supergroup"] == strat, :] == 1).to_numpy()
|
|
116
|
+
fault_stratigraphy[strat] = fault_strat.columns[mask[0, :]].tolist()
|
|
117
|
+
super().__init__(
|
|
118
|
+
contacts,
|
|
119
|
+
orientations,
|
|
120
|
+
stratigraphic_order,
|
|
121
|
+
thicknesses=thicknesses,
|
|
122
|
+
fault_orientations=fault_orientations,
|
|
123
|
+
fault_locations=fault_locations,
|
|
124
|
+
fault_properties=fault_properties,
|
|
125
|
+
fault_edges=list(fault_graph.edges),
|
|
126
|
+
colours=dict(zip(groups["code"], groups["colour"])),
|
|
127
|
+
fault_stratigraphy=fault_stratigraphy,
|
|
128
|
+
intrusions=None,
|
|
129
|
+
use_thickness=use_thickness,
|
|
130
|
+
fault_edge_properties=fault_edge_properties,
|
|
131
|
+
)
|
|
132
|
+
self.origin = bb[[0, 1, 4]]
|
|
133
|
+
self.maximum = bb[[2, 3, 5]]
|
|
134
|
+
|
|
135
|
+
def process_downthrow_direction(self, fault_properties, fault_orientations):
|
|
136
|
+
"""Helper function to update the dip direction given downthrow direction
|
|
137
|
+
|
|
138
|
+
Fault dip direction should point to the hanging wall
|
|
139
|
+
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
fault_properties : DataFrame
|
|
143
|
+
data frame with fault name as index and downthrow direction
|
|
144
|
+
and average dip_dir as columns
|
|
145
|
+
fault_orientations : DataFrame
|
|
146
|
+
orientation data for the faults
|
|
147
|
+
"""
|
|
148
|
+
for fname in fault_properties.index:
|
|
149
|
+
if fault_properties.loc[fname, "downthrow_dir"] == 1.0:
|
|
150
|
+
logger.info(f"{fname}: Estimating downthrow direction using fault intersections")
|
|
151
|
+
# fault_intersection_angles[f]
|
|
152
|
+
if (
|
|
153
|
+
np.abs(
|
|
154
|
+
fault_properties.loc[fname, "downthrow_dir"]
|
|
155
|
+
- fault_properties.loc[fname, "dip_dir"]
|
|
156
|
+
)
|
|
157
|
+
> 90
|
|
158
|
+
):
|
|
159
|
+
fault_orientations.loc[
|
|
160
|
+
fault_orientations["fault_name"] == fname, "DipDirection"
|
|
161
|
+
] -= 180
|
|
162
|
+
fault_properties.loc[fname, "dip_dir"] -= 180
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
#
|