LoopStructural 1.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of LoopStructural might be problematic. Click here for more details.

Files changed (129) hide show
  1. LoopStructural/__init__.py +52 -0
  2. LoopStructural/datasets/__init__.py +23 -0
  3. LoopStructural/datasets/_base.py +301 -0
  4. LoopStructural/datasets/_example_models.py +10 -0
  5. LoopStructural/datasets/data/claudius.csv +21049 -0
  6. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  7. LoopStructural/datasets/data/duplex.csv +126 -0
  8. LoopStructural/datasets/data/duplexbb.txt +2 -0
  9. LoopStructural/datasets/data/fault_trace/fault_trace.cpg +1 -0
  10. LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  11. LoopStructural/datasets/data/fault_trace/fault_trace.prj +1 -0
  12. LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  13. LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  14. LoopStructural/datasets/data/geological_map_data/bbox.csv +2 -0
  15. LoopStructural/datasets/data/geological_map_data/contacts.csv +657 -0
  16. LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +7 -0
  17. LoopStructural/datasets/data/geological_map_data/fault_edges.txt +2 -0
  18. LoopStructural/datasets/data/geological_map_data/fault_locations.csv +79 -0
  19. LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +19 -0
  20. LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +13 -0
  21. LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +207 -0
  22. LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +13 -0
  23. LoopStructural/datasets/data/intrusion.csv +1017 -0
  24. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  25. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  26. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  27. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  28. LoopStructural/datasets/data/refolded_fold.csv +205 -0
  29. LoopStructural/datasets/data/tabular_intrusion.csv +23 -0
  30. LoopStructural/datatypes/__init__.py +4 -0
  31. LoopStructural/datatypes/_bounding_box.py +422 -0
  32. LoopStructural/datatypes/_point.py +166 -0
  33. LoopStructural/datatypes/_structured_grid.py +94 -0
  34. LoopStructural/datatypes/_surface.py +184 -0
  35. LoopStructural/export/exporters.py +554 -0
  36. LoopStructural/export/file_formats.py +15 -0
  37. LoopStructural/export/geoh5.py +100 -0
  38. LoopStructural/export/gocad.py +126 -0
  39. LoopStructural/export/omf_wrapper.py +88 -0
  40. LoopStructural/interpolators/__init__.py +105 -0
  41. LoopStructural/interpolators/_api.py +143 -0
  42. LoopStructural/interpolators/_builders.py +149 -0
  43. LoopStructural/interpolators/_cython/__init__.py +0 -0
  44. LoopStructural/interpolators/_discrete_fold_interpolator.py +183 -0
  45. LoopStructural/interpolators/_discrete_interpolator.py +692 -0
  46. LoopStructural/interpolators/_finite_difference_interpolator.py +470 -0
  47. LoopStructural/interpolators/_geological_interpolator.py +380 -0
  48. LoopStructural/interpolators/_interpolator_factory.py +89 -0
  49. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  50. LoopStructural/interpolators/_operator.py +38 -0
  51. LoopStructural/interpolators/_p1interpolator.py +228 -0
  52. LoopStructural/interpolators/_p2interpolator.py +277 -0
  53. LoopStructural/interpolators/_surfe_wrapper.py +174 -0
  54. LoopStructural/interpolators/supports/_2d_base_unstructured.py +340 -0
  55. LoopStructural/interpolators/supports/_2d_p1_unstructured.py +68 -0
  56. LoopStructural/interpolators/supports/_2d_p2_unstructured.py +288 -0
  57. LoopStructural/interpolators/supports/_2d_structured_grid.py +462 -0
  58. LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  59. LoopStructural/interpolators/supports/_3d_base_structured.py +467 -0
  60. LoopStructural/interpolators/supports/_3d_p2_tetra.py +331 -0
  61. LoopStructural/interpolators/supports/_3d_structured_grid.py +470 -0
  62. LoopStructural/interpolators/supports/_3d_structured_tetra.py +746 -0
  63. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +637 -0
  64. LoopStructural/interpolators/supports/__init__.py +55 -0
  65. LoopStructural/interpolators/supports/_aabb.py +77 -0
  66. LoopStructural/interpolators/supports/_base_support.py +114 -0
  67. LoopStructural/interpolators/supports/_face_table.py +70 -0
  68. LoopStructural/interpolators/supports/_support_factory.py +32 -0
  69. LoopStructural/modelling/__init__.py +29 -0
  70. LoopStructural/modelling/core/__init__.py +0 -0
  71. LoopStructural/modelling/core/geological_model.py +1867 -0
  72. LoopStructural/modelling/features/__init__.py +32 -0
  73. LoopStructural/modelling/features/_analytical_feature.py +79 -0
  74. LoopStructural/modelling/features/_base_geological_feature.py +364 -0
  75. LoopStructural/modelling/features/_cross_product_geological_feature.py +100 -0
  76. LoopStructural/modelling/features/_geological_feature.py +288 -0
  77. LoopStructural/modelling/features/_lambda_geological_feature.py +93 -0
  78. LoopStructural/modelling/features/_region.py +18 -0
  79. LoopStructural/modelling/features/_structural_frame.py +186 -0
  80. LoopStructural/modelling/features/_unconformity_feature.py +83 -0
  81. LoopStructural/modelling/features/builders/__init__.py +5 -0
  82. LoopStructural/modelling/features/builders/_base_builder.py +111 -0
  83. LoopStructural/modelling/features/builders/_fault_builder.py +590 -0
  84. LoopStructural/modelling/features/builders/_folded_feature_builder.py +129 -0
  85. LoopStructural/modelling/features/builders/_geological_feature_builder.py +543 -0
  86. LoopStructural/modelling/features/builders/_structural_frame_builder.py +237 -0
  87. LoopStructural/modelling/features/fault/__init__.py +3 -0
  88. LoopStructural/modelling/features/fault/_fault_function.py +444 -0
  89. LoopStructural/modelling/features/fault/_fault_function_feature.py +82 -0
  90. LoopStructural/modelling/features/fault/_fault_segment.py +505 -0
  91. LoopStructural/modelling/features/fold/__init__.py +9 -0
  92. LoopStructural/modelling/features/fold/_fold.py +167 -0
  93. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +149 -0
  94. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +67 -0
  95. LoopStructural/modelling/features/fold/_foldframe.py +194 -0
  96. LoopStructural/modelling/features/fold/_svariogram.py +188 -0
  97. LoopStructural/modelling/input/__init__.py +2 -0
  98. LoopStructural/modelling/input/fault_network.py +80 -0
  99. LoopStructural/modelling/input/map2loop_processor.py +165 -0
  100. LoopStructural/modelling/input/process_data.py +650 -0
  101. LoopStructural/modelling/input/project_file.py +84 -0
  102. LoopStructural/modelling/intrusions/__init__.py +25 -0
  103. LoopStructural/modelling/intrusions/geom_conceptual_models.py +142 -0
  104. LoopStructural/modelling/intrusions/geometric_scaling_functions.py +123 -0
  105. LoopStructural/modelling/intrusions/intrusion_builder.py +672 -0
  106. LoopStructural/modelling/intrusions/intrusion_feature.py +410 -0
  107. LoopStructural/modelling/intrusions/intrusion_frame_builder.py +971 -0
  108. LoopStructural/modelling/intrusions/intrusion_support_functions.py +460 -0
  109. LoopStructural/utils/__init__.py +38 -0
  110. LoopStructural/utils/_surface.py +143 -0
  111. LoopStructural/utils/_transformation.py +76 -0
  112. LoopStructural/utils/config.py +18 -0
  113. LoopStructural/utils/dtm_creator.py +17 -0
  114. LoopStructural/utils/exceptions.py +31 -0
  115. LoopStructural/utils/helper.py +292 -0
  116. LoopStructural/utils/json_encoder.py +18 -0
  117. LoopStructural/utils/linalg.py +8 -0
  118. LoopStructural/utils/logging.py +79 -0
  119. LoopStructural/utils/maths.py +245 -0
  120. LoopStructural/utils/regions.py +103 -0
  121. LoopStructural/utils/typing.py +7 -0
  122. LoopStructural/utils/utils.py +68 -0
  123. LoopStructural/version.py +1 -0
  124. LoopStructural/visualisation/__init__.py +11 -0
  125. LoopStructural-1.6.1.dist-info/LICENSE +21 -0
  126. LoopStructural-1.6.1.dist-info/METADATA +81 -0
  127. LoopStructural-1.6.1.dist-info/RECORD +129 -0
  128. LoopStructural-1.6.1.dist-info/WHEEL +5 -0
  129. LoopStructural-1.6.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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,2 @@
1
+ from LoopStructural.modelling.input.map2loop_processor import Map2LoopProcessor
2
+ from LoopStructural.modelling.input.process_data import ProcessInputData
@@ -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
+ #