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.

Files changed (119) hide show
  1. Miniconda/envs/loop/Lib/site-packages/LoopStructural/__init__.py +33 -0
  2. Miniconda/envs/loop/Lib/site-packages/LoopStructural/__pycache__/__init__.cpython-37.pyc +0 -0
  3. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__init__.py +12 -0
  4. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/__init__.cpython-37.pyc +0 -0
  5. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/_base.cpython-37.pyc +0 -0
  6. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/_base.py +65 -0
  7. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/claudius.csv +21049 -0
  8. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/claudiusbb.txt +2 -0
  9. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/duplex.csv +126 -0
  10. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/duplexbb.txt +2 -0
  11. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/intrusion.csv +1017 -0
  12. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/intrusionbb.txt +2 -0
  13. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/onefoldbb.txt +2 -0
  14. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/onefolddata.csv +2226 -0
  15. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/refolded_bb.txt +2 -0
  16. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/data/refolded_fold.csv +2126 -0
  17. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__init__.py +31 -0
  18. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/__init__.cpython-37.pyc +0 -0
  19. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_fold_interpolator.cpython-37.pyc +0 -0
  20. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_interpolator.cpython-37.pyc +0 -0
  21. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/finite_difference_interpolator.cpython-37.pyc +0 -0
  22. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/geological_interpolator.cpython-37.pyc +0 -0
  23. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/operator.cpython-37.pyc +0 -0
  24. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/piecewiselinear_interpolator.cpython-37.pyc +0 -0
  25. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_grid.cpython-37.pyc +0 -0
  26. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_tetra.cpython-37.pyc +0 -0
  27. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/surfe_wrapper.cpython-37.pyc +0 -0
  28. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__init__.py +0 -0
  29. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__pycache__/__init__.cpython-37.pyc +0 -0
  30. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.c +27805 -0
  31. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.cp37-win_amd64.pyd +0 -0
  32. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_fold_interpolator.py +168 -0
  33. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_interpolator.py +551 -0
  34. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/finite_difference_interpolator.py +339 -0
  35. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/geological_interpolator.py +178 -0
  36. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/operator.py +46 -0
  37. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/piecewiselinear_interpolator.py +300 -0
  38. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_grid.py +460 -0
  39. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_tetra.py +637 -0
  40. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/surfe_wrapper.py +119 -0
  41. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__init__.py +46 -0
  42. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__pycache__/__init__.cpython-37.pyc +0 -0
  43. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__init__.py +0 -0
  44. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/__init__.cpython-37.pyc +0 -0
  45. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model.cpython-37.pyc +0 -0
  46. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/geological_model.py +1179 -0
  47. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__init__.py +3 -0
  48. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/__init__.cpython-37.pyc +0 -0
  49. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function.cpython-37.pyc +0 -0
  50. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function_feature.cpython-37.pyc +0 -0
  51. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_segment.cpython-37.pyc +0 -0
  52. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function.py +187 -0
  53. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function_feature.py +75 -0
  54. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_segment.py +270 -0
  55. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__init__.py +7 -0
  56. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/__init__.cpython-37.pyc +0 -0
  57. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/cross_product_geological_feature.cpython-37.pyc +0 -0
  58. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature.cpython-37.pyc +0 -0
  59. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature_builder.cpython-37.pyc +0 -0
  60. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/region_feature.cpython-37.pyc +0 -0
  61. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame.cpython-37.pyc +0 -0
  62. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame_builder.cpython-37.pyc +0 -0
  63. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/unconformity_feature.cpython-37.pyc +0 -0
  64. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/cross_product_geological_feature.py +77 -0
  65. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature.py +276 -0
  66. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature_builder.py +289 -0
  67. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/region_feature.py +31 -0
  68. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame.py +116 -0
  69. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame_builder.py +179 -0
  70. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/unconformity_feature.py +69 -0
  71. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__init__.py +8 -0
  72. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/__init__.cpython-37.pyc +0 -0
  73. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold.cpython-37.pyc +0 -0
  74. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle.cpython-37.pyc +0 -0
  75. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle_feature.cpython-37.pyc +0 -0
  76. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/foldframe.cpython-37.pyc +0 -0
  77. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/svariogram.cpython-37.pyc +0 -0
  78. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold.py +135 -0
  79. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle.py +132 -0
  80. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle_feature.py +57 -0
  81. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/foldframe.py +191 -0
  82. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/svariogram.py +179 -0
  83. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__init__.py +14 -0
  84. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/__init__.cpython-37.pyc +0 -0
  85. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/exceptions.cpython-37.pyc +0 -0
  86. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/helper.cpython-37.pyc +0 -0
  87. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/map2loop.cpython-37.pyc +0 -0
  88. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/utils.cpython-37.pyc +0 -0
  89. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/exceptions.py +9 -0
  90. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/helper.py +373 -0
  91. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/map2loop.py +229 -0
  92. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/utils.py +76 -0
  93. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__init__.py +19 -0
  94. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/__init__.cpython-37.pyc +0 -0
  95. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/map_viewer.cpython-37.pyc +0 -0
  96. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_plotter.cpython-37.pyc +0 -0
  97. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_visualisation.cpython-37.pyc +0 -0
  98. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/rotation_angle_plotter.cpython-37.pyc +0 -0
  99. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/sphinx_scraper.cpython-37.pyc +0 -0
  100. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/map_viewer.py +122 -0
  101. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_plotter.py +16 -0
  102. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_visualisation.py +704 -0
  103. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/rotation_angle_plotter.py +66 -0
  104. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/sphinx_scraper.py +34 -0
  105. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/PKG-INFO +10 -0
  106. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/SOURCES.txt +60 -0
  107. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/dependency_links.txt +1 -0
  108. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/requires.txt +3 -0
  109. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.1-py3.7.egg-info/top_level.txt +2 -0
  110. Miniconda/envs/loop/Lib/site-packages/tests/__init__.py +0 -0
  111. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/__init__.cpython-37.pyc +0 -0
  112. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_faults.cpython-37.pyc +0 -0
  113. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_fold.cpython-37.pyc +0 -0
  114. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_interpolator.cpython-37.pyc +0 -0
  115. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_refolded.cpython-37.pyc +0 -0
  116. Miniconda/envs/loop/Lib/site-packages/tests/test_faults.py +17 -0
  117. Miniconda/envs/loop/Lib/site-packages/tests/test_fold.py +57 -0
  118. Miniconda/envs/loop/Lib/site-packages/tests/test_interpolator.py +88 -0
  119. Miniconda/envs/loop/Lib/site-packages/tests/test_refolded.py +22 -0
@@ -0,0 +1,1179 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ from LoopStructural.datasets import normal_vector_headers
7
+ from LoopStructural.interpolators.discrete_fold_interpolator import \
8
+ DiscreteFoldInterpolator as DFI
9
+ from LoopStructural.interpolators.finite_difference_interpolator import \
10
+ FiniteDifferenceInterpolator as FDI
11
+ from LoopStructural.interpolators.piecewiselinear_interpolator import \
12
+ PiecewiseLinearInterpolator as PLI
13
+
14
+ try:
15
+ from LoopStructural.interpolators.surfe_wrapper import \
16
+ SurfeRBFInterpolator as Surfe
17
+
18
+ surfe = True
19
+
20
+ except ImportError:
21
+ surfe = False
22
+
23
+ from LoopStructural.utils.helper import all_heading, gradient_vec_names, \
24
+ strike_dip_vector
25
+ from LoopStructural.modelling.fault.fault_segment import FaultSegment
26
+ from LoopStructural.modelling.features import \
27
+ GeologicalFeatureInterpolator
28
+ from LoopStructural.modelling.features import RegionFeature
29
+ from LoopStructural.modelling.features import \
30
+ StructuralFrameBuilder
31
+ from LoopStructural.modelling.features import UnconformityFeature
32
+ from LoopStructural.modelling.fold.fold import FoldEvent
33
+ from LoopStructural.modelling.fold import FoldRotationAngle
34
+ from LoopStructural.modelling.fold.foldframe import FoldFrame
35
+ from LoopStructural.interpolators.structured_grid import StructuredGrid
36
+ from LoopStructural.interpolators.structured_tetra import TetMesh
37
+ from LoopStructural.utils.exceptions import LoopBaseException
38
+
39
+ logger = logging.getLogger(__name__)
40
+ if not surfe:
41
+ logger.warning("Cannot import Surfe")
42
+
43
+
44
+ def _calculate_average_intersection(series_builder, fold_frame, fold,
45
+ **kwargs):
46
+ """
47
+
48
+ Parameters
49
+ ----------
50
+ series_builder
51
+ fold_frame
52
+ fold
53
+
54
+ Returns
55
+ -------
56
+
57
+ """
58
+ l2 = fold_frame.calculate_intersection_lineation(
59
+ series_builder)
60
+ fold.fold_axis = np.mean(l2, axis=0)
61
+
62
+
63
+ class GeologicalModel:
64
+ """
65
+ A geological model is the recipe for building a 3D model and includes
66
+ the rescaling
67
+ of the model between 0 and 1.
68
+ """
69
+ def __init__(self, origin, maximum, rescale=True, nsteps=(40, 40, 40),
70
+ reuse_supports=False):
71
+ """
72
+ Parameters
73
+ ----------
74
+ origin : numpy array
75
+ specifying the origin of the model
76
+ maximum : numpy array
77
+ specifying the maximum extent of the model
78
+ rescale : bool
79
+ whether to rescale the model to between 0/1
80
+
81
+ """
82
+ self.features = []
83
+ self.feature_name_index = {}
84
+ self.data = None
85
+ self.nsteps = nsteps
86
+
87
+ # we want to rescale the model area so that the maximum length is
88
+ # 1
89
+ self.origin = np.array(origin).astype(float)
90
+
91
+ self.maximum = np.array(maximum).astype(float)
92
+ lengths = self.maximum - self.origin
93
+ self.scale_factor = 1.
94
+ self.bounding_box = np.zeros((2, 3))
95
+ self.bounding_box[1, :] = self.maximum - self.origin
96
+ self.bounding_box[1, :] = self.maximum - self.origin
97
+ if rescale:
98
+ self.scale_factor = np.max(lengths)
99
+
100
+ self.bounding_box /= self.scale_factor
101
+ self.support = {}
102
+ self.reuse_supports = reuse_supports
103
+ self.stratigraphic_column = None
104
+ self.parameters = {'features': [], 'model': {'bounding_box': self.origin.tolist() + self.maximum.tolist(),
105
+ 'rescale': rescale,
106
+ 'nsteps': nsteps,
107
+ 'reuse_supports': reuse_supports}}
108
+
109
+ @classmethod
110
+ def from_map2loop_directory(cls, m2l_directory,**kwargs):
111
+ """Alternate constructor for a geological model using m2l output
112
+
113
+ Uses the information saved in the map2loop files to build a geological model.
114
+ You can specify kwargs for building foliation using foliation_params and for
115
+ faults using fault_params. skip_faults is a flag that allows for the faults to be skipped.
116
+
117
+ Parameters
118
+ ----------
119
+ m2l_directory : string
120
+ path to map2loop directory
121
+
122
+ Returns
123
+ -------
124
+ (GeologicalModel, dict)
125
+ the created geological model and a dictionary of the map2loop data
126
+ """
127
+ from LoopStructural.utils import process_map2loop, build_model
128
+ m2l_data = process_map2loop(m2l_directory)
129
+ return build_model(m2l_data,**kwargs), m2l_data
130
+
131
+ @classmethod
132
+ def from_file(cls, file):
133
+ try:
134
+ import dill as pickle
135
+ except ImportError:
136
+ logger.error("Cannot import from file, dill not installed")
137
+ return None
138
+ model = pickle.load(open(file,'rb'))
139
+ if type(model) == GeologicalModel:
140
+ return model
141
+ else:
142
+ logger.error('{} does not contain a geological model'.format(file))
143
+ return None
144
+
145
+ def to_file(self, file):
146
+ try:
147
+ import dill as pickle
148
+ except ImportError:
149
+ logger.error("Cannot write to file, dill not installed")
150
+ return
151
+ try:
152
+ pickle.dump(self,open(file,'wb'))
153
+ except pickle.PicklingError:
154
+ logger.error('Error saving file')
155
+
156
+ def _add_feature(self, feature):
157
+ """
158
+ Add a feature to the model stack
159
+
160
+ Parameters
161
+ ----------
162
+ feature : GeologicalFeature
163
+ the geological feature to add
164
+
165
+ """
166
+
167
+ if feature.name in self.feature_name_index:
168
+ logger.info("Feature %s already exists at %i, overwriting" %
169
+ (feature.name, self.feature_name_index[feature.name]))
170
+ self.features[self.feature_name_index[feature.name]] = feature
171
+ else:
172
+ self.features.append(feature)
173
+ self.feature_name_index[feature.name] = len(self.features) - 1
174
+ logger.info("Adding %s to model at location %i" % (
175
+ feature.name, len(self.features)))
176
+ self._add_domain_fault_above(feature)
177
+ self._add_unconformity_above(feature)
178
+ feature.set_model(self)
179
+
180
+ def set_model_data(self, data):
181
+ """
182
+ Set the data array for the model
183
+
184
+ Parameters
185
+ ----------
186
+ data : pandas data frame
187
+ with column headers corresponding to the
188
+ type, X, Y, Z, nx, ny, nz, val, strike, dip, dip_dir, plunge,
189
+ plunge_dir, azimuth
190
+
191
+ Returns
192
+ -------
193
+ Note
194
+ ----
195
+ Type can be any unique identifier for the feature the data point
196
+ 'eg' 'S0', 'S2', 'F1_axis'
197
+ it is then used by the create functions to get the correct data
198
+ """
199
+ if type(data) != pd.DataFrame:
200
+ logger.warning(
201
+ "Data is not a pandas data frame, trying to read data frame "
202
+ "from csv")
203
+ try:
204
+ data = pd.read_csv(data)
205
+ except:
206
+ logger.error("Could not load pandas data frame from data")
207
+
208
+ self.data = data.copy()
209
+ self.data['X'] -= self.origin[0]
210
+ self.data['Y'] -= self.origin[1]
211
+ self.data['Z'] -= self.origin[2]
212
+ self.data['X'] /= self.scale_factor
213
+ self.data['Y'] /= self.scale_factor
214
+ self.data['Z'] /= self.scale_factor
215
+ if 'type' in self.data:
216
+ logger.warning("'type' is being replaced with 'feature_name' \n")
217
+ self.data.rename(columns={'type':'feature_name'},inplace=True)
218
+ for h in all_heading():
219
+ if h not in self.data:
220
+ self.data[h] = np.nan
221
+ if h == 'w':
222
+ self.data[h] = 1.
223
+ if h == 'coord':
224
+ self.data[h] = 0
225
+
226
+ if 'strike' in self.data and 'dip' in self.data:
227
+ mask = np.all(~np.isnan(self.data.loc[:, ['strike', 'dip']]),
228
+ axis=1)
229
+ self.data.loc[mask, gradient_vec_names()] = strike_dip_vector(
230
+ self.data.loc[mask, 'strike'], self.data.loc[mask, 'dip'])
231
+ self.data.drop(['strike', 'dip'], axis=1, inplace=True)
232
+ # self.data.loc
233
+
234
+ def extend_model_data(self, newdata):
235
+ """
236
+ Extends the data frame
237
+
238
+ Parameters
239
+ ----------
240
+ newdata : pandas data frame
241
+ data to add to the existing dataframe
242
+ Returns
243
+ -------
244
+ """
245
+ logger.warning("Extend data is untested and may have unexpected consequences")
246
+ data_temp = newdata.copy()
247
+ data_temp['X'] -= self.origin[0]
248
+ data_temp['Y'] -= self.origin[1]
249
+ data_temp['Z'] -= self.origin[2]
250
+ data_temp['X'] /= self.scale_factor
251
+ data_temp['Y'] /= self.scale_factor
252
+ data_temp['Z'] /= self.scale_factor
253
+ self.data.concat([self.data, data_temp], sort=True)
254
+
255
+ def set_stratigraphic_column(self, stratigraphic_column):
256
+ """
257
+ Adds a stratigraphic column to the model
258
+
259
+ Parameters
260
+ ----------
261
+ stratigraphic_column : dictionary
262
+
263
+ Returns
264
+ -------
265
+
266
+ Notes
267
+ -----
268
+ stratigraphic_column is a nested dictionary with the format
269
+ {'group':
270
+ {'series1':
271
+ {'min':0., 'max':10.,'id':0}
272
+ }
273
+ }
274
+
275
+ """
276
+ self.stratigraphic_column = stratigraphic_column
277
+
278
+ def create_from_feature_list(self, features):
279
+ for f in features:
280
+ featuretype = f.pop('featuretype', None)
281
+ if featuretype is None:
282
+ raise LoopBaseException
283
+ if featuretype == 'strati':
284
+ self.create_and_add_foliation(f)
285
+ # if featuretype == 'fault':
286
+ # self.create_and_add_fault(f)
287
+ if featuretype == 'folded_strati':
288
+ self.create_and_add_folded_foliation(f)
289
+
290
+ def get_interpolator(self, interpolatortype='PLI', nelements=1e5,
291
+ buffer=0.2, **kwargs):
292
+ """
293
+ Returns an interpolator given the arguments, also constructs a
294
+ support for a discrete interpolator
295
+
296
+ Parameters
297
+ ----------
298
+ interpolatortype : string
299
+ define the interpolator type
300
+ nelements : int
301
+ number of elements in the interpolator
302
+ buffer : double or numpy array 3x1
303
+ value(s) between 0,1 specifying the buffer around the bounding box
304
+ data_bb : bool
305
+ whether to use the model boundary or the boundary around
306
+ kwargs : no kwargs used, this just catches any additional arguments
307
+
308
+ Returns
309
+ -------
310
+ """
311
+ # get an interpolator for
312
+ interpolator = None
313
+ bb = np.copy(self.bounding_box)
314
+ # add a buffer to the interpolation domain, this is necessary for
315
+ # faults but also generally a good
316
+ # idea to avoid boundary problems
317
+ bb[0, :] -= buffer # *(bb[1,:]-bb[0,:])
318
+ bb[1, :] += buffer # *(bb[1,:]-bb[0,:])
319
+ if interpolatortype == "PLI":
320
+ nelements /= 5
321
+ ele_vol = bb[1, 0] * bb[1, 1] * bb[1, 2] / nelements
322
+ # calculate the step vector of a regular cube
323
+ step_vector = np.zeros(3)
324
+ step_vector[:] = ele_vol ** (1. / 3.)
325
+ # number of steps is the length of the box / step vector
326
+ nsteps = ((bb[1, :] - bb[0, :]) / step_vector).astype(int)
327
+ # create a structured grid using the origin and number of steps
328
+ mesh_id = 'mesh_{}'.format(nelements)
329
+ mesh = self.support.get(mesh_id,
330
+ TetMesh(origin=bb[0, :], nsteps=nsteps,
331
+ step_vector=step_vector))
332
+ if mesh_id not in self.support:
333
+ self.support[mesh_id] = mesh
334
+ logger.info("Creating regular tetrahedron mesh with %i elements \n"
335
+ "for modelling using PLI" % (mesh.ntetra))
336
+
337
+ return PLI(mesh)
338
+
339
+ if interpolatortype == 'FDI':
340
+ # find the volume of one element
341
+ ele_vol = bb[1, 0] * bb[1, 1] * bb[1, 2] / nelements
342
+ # calculate the step vector of a regular cube
343
+ step_vector = np.zeros(3)
344
+ step_vector[:] = ele_vol ** (1. / 3.)
345
+ # number of steps is the length of the box / step vector
346
+ nsteps = ((bb[1, :] - bb[0, :]) / step_vector).astype(int)
347
+ # create a structured grid using the origin and number of steps
348
+ grid_id = 'grid_{}'.format(nelements)
349
+ grid = self.support.get(grid_id, StructuredGrid(origin=bb[0, :],
350
+ nsteps=nsteps,
351
+ step_vector=step_vector))
352
+ if grid_id not in self.support:
353
+ self.support[grid_id] = grid
354
+ logger.info("Creating regular grid with %i elements \n"
355
+ "for modelling using FDI" % grid.n_elements)
356
+ return FDI(grid)
357
+
358
+ if interpolatortype == "DFI": # "fold" in kwargs:
359
+ nelements /= 5
360
+ ele_vol = bb[1, 0] * bb[1, 1] * bb[1, 2] / nelements
361
+ # calculate the step vector of a regular cube
362
+ step_vector = np.zeros(3)
363
+ step_vector[:] = ele_vol ** (1. / 3.)
364
+ # number of steps is the length of the box / step vector
365
+ nsteps = ((bb[1, :] - bb[0, :]) / step_vector).astype(int)
366
+ # create a structured grid using the origin and number of steps
367
+ mesh = kwargs.get('mesh', TetMesh(origin=bb[0, :], nsteps=nsteps,
368
+ step_vector=step_vector))
369
+ logger.info("Creating regular tetrahedron mesh with %i elements \n"
370
+ "for modelling using DFI" % mesh.ntetra)
371
+ return DFI(mesh, kwargs['fold'])
372
+ if interpolatortype == 'Surfe' or interpolatortype == 'surfe' and \
373
+ surfe:
374
+ method = kwargs.get('method', 'single_surface')
375
+ logger.info("Using surfe interpolator")
376
+ return Surfe(method)
377
+ logger.warning("No interpolator")
378
+ return interpolator
379
+
380
+ def create_and_add_foliation(self, series_surface_data, **kwargs):
381
+ """
382
+ Parameters
383
+ ----------
384
+ series_surface_data : string
385
+ corresponding to the feature_name in the data
386
+ kwargs
387
+
388
+ Returns
389
+ -------
390
+ feature : GeologicalFeature
391
+ the created geological feature
392
+ """
393
+ self.parameters['features'].append({'feature_type': 'foliation', 'feature_name': series_surface_data, **kwargs})
394
+ interpolator = self.get_interpolator(**kwargs)
395
+ series_builder = GeologicalFeatureInterpolator(interpolator,
396
+ name=series_surface_data,
397
+ **kwargs)
398
+ # add data
399
+ series_data = self.data[self.data['feature_name'] == series_surface_data]
400
+ if series_data.shape[0] == 0:
401
+ logger.warning("No data for %s, skipping" % series_surface_data)
402
+ return
403
+ series_builder.add_data_from_data_frame(series_data)
404
+ self._add_faults(series_builder)
405
+
406
+ # build feature
407
+ series_feature = series_builder.build(**kwargs)
408
+ series_feature.type = 'series'
409
+ # see if any unconformities are above this feature if so add region
410
+ # self._add_unconformity_above(series_feature)self._add_feature(series_feature)
411
+ self._add_feature(series_feature)
412
+ return series_feature
413
+
414
+ def create_and_add_fold_frame(self, foldframe_data, **kwargs):
415
+ """
416
+ Parameters
417
+ ----------
418
+ foldframe_data : string
419
+ unique string in feature_name column
420
+
421
+ kwargs
422
+
423
+ Returns
424
+ -------
425
+ fold_frame : FoldFrame
426
+ the created fold frame
427
+ """
428
+ self.parameters['features'].append({'feature_type': 'fold_frame', 'feature_name': foldframe_data, **kwargs})
429
+ result = {}
430
+ # create fault frame
431
+ interpolator = self.get_interpolator(**kwargs)
432
+ #
433
+ fold_frame_builder = StructuralFrameBuilder(interpolator,
434
+ name=foldframe_data,
435
+ **kwargs)
436
+ # add data
437
+ fold_frame_data = self.data[self.data['feature_name'] == foldframe_data]
438
+ fold_frame_builder.add_data_from_data_frame(fold_frame_data)
439
+ self._add_faults(fold_frame_builder[0])
440
+ self._add_faults(fold_frame_builder[1])
441
+ self._add_faults(fold_frame_builder[2])
442
+
443
+ fold_frame = fold_frame_builder.build(frame=FoldFrame, **kwargs)
444
+ # for i in range(3):
445
+ # self._add_unconformity_above(fold_frame[i])
446
+ fold_frame.type = 'structuralframe'
447
+ self._add_feature(fold_frame)
448
+
449
+ return fold_frame
450
+
451
+ def create_and_add_folded_foliation(self, foliation_data, fold_frame=None,
452
+ **kwargs):
453
+ """
454
+ Create a folded foliation field from data and a fold frame
455
+
456
+ Parameters
457
+ ----------
458
+ foliation_data : string
459
+ unique string in type column of data frame
460
+ fold_frame : FoldFrame
461
+ kwargs
462
+ additional kwargs to be passed through to other functions
463
+
464
+ Returns
465
+ -------
466
+ feature : GeologicalFeature
467
+ created geological feature
468
+ """
469
+ self.parameters['features'].append(
470
+ {'feature_type': 'fold_foliation', 'feature_name': foliation_data, 'fold_frame': fold_frame, **kwargs})
471
+ if fold_frame is None:
472
+ logger.info("Using last feature as fold frame")
473
+ fold_frame = self.features[-1]
474
+ assert type(fold_frame) == FoldFrame, "Please specify a FoldFrame"
475
+ fold = FoldEvent(fold_frame)
476
+ fold_interpolator = self.get_interpolator("DFI", fold=fold, **kwargs)
477
+ series_builder = GeologicalFeatureInterpolator(
478
+ interpolator=fold_interpolator,
479
+ name=foliation_data)
480
+
481
+ series_builder.add_data_from_data_frame(
482
+ self.data[self.data['feature_name'] == foliation_data])
483
+ self._add_faults(series_builder)
484
+
485
+ series_builder.add_data_to_interpolator(True)
486
+ if "fold_axis" in kwargs:
487
+ fold.fold_axis = kwargs['fold_axis']
488
+ if "av_fold_axis" in kwargs:
489
+ _calculate_average_intersection(series_builder, fold_frame, fold)
490
+ if fold.fold_axis is None:
491
+ far, fad = fold_frame.calculate_fold_axis_rotation(
492
+ series_builder)
493
+ fold_axis_rotation = FoldRotationAngle(far, fad)
494
+ a_wl = kwargs.get("axis_wl", None)
495
+ if 'axis_function' in kwargs:
496
+ # allow predefined function to be used
497
+ fold_axis_rotation.set_function(kwargs['axis_function'])
498
+ else:
499
+ fold_axis_rotation.fit_fourier_series(wl=a_wl)
500
+ fold.fold_axis_rotation = fold_axis_rotation
501
+ # give option of passing own fold limb rotation function
502
+ flr, fld = fold_frame.calculate_fold_limb_rotation(
503
+ series_builder)
504
+ fold_limb_rotation = FoldRotationAngle(flr, fld)
505
+ l_wl = kwargs.get("limb_wl", None)
506
+ if 'limb_function' in kwargs:
507
+ # allow for predefined functions to be used
508
+ fold_limb_rotation.set_function(kwargs['limb_function'])
509
+ else:
510
+ fold_limb_rotation.fit_fourier_series(wl=l_wl)
511
+ fold.fold_limb_rotation = fold_limb_rotation
512
+ # fold_limb_fitter = kwargs.get("fold_limb_function",
513
+ # _interpolate_fold_limb_rotation_angle)
514
+ # fold_limb_fitter(series_builder, fold_frame, fold, result, **kwargs)
515
+ kwargs['fold_weights'] = kwargs.get('fold_weights', None)
516
+
517
+ self._add_faults(series_builder)
518
+ # build feature
519
+ kwargs['cgw'] = 0.
520
+ kwargs['fold'] = fold
521
+ series_feature = series_builder.build(**kwargs)
522
+ series_feature.type = 'series'
523
+ # see if any unconformities are above this feature if so add region
524
+ # self._add_unconformity_above(series_feature)self._add_feature(series_feature)
525
+ # result['support'] = series_feature.get_interpolator().support
526
+ self._add_feature(series_feature)
527
+ return series_feature
528
+
529
+ def create_and_add_folded_fold_frame(self, fold_frame_data,
530
+ fold_frame=None,
531
+ **kwargs):
532
+ """
533
+
534
+ Parameters
535
+ ----------
536
+ fold_frame_data : string
537
+
538
+ fold_frame : StructuralFrame
539
+
540
+ kwargs
541
+
542
+ Returns
543
+ -------
544
+ fold_frame : FoldFrame
545
+ created fold frame
546
+ """
547
+ self.parameters['features'].append(
548
+ {'feature_type': 'folded_fold_frame', 'feature_name': fold_frame_data, 'fold_frame': fold_frame, **kwargs})
549
+ if fold_frame is None:
550
+ logger.info("Using last feature as fold frame")
551
+ fold_frame = self.features[-1]
552
+ assert type(fold_frame) == FoldFrame, "Please specify a FoldFrame"
553
+ fold = FoldEvent(fold_frame)
554
+ fold_interpolator = self.get_interpolator("DFI", fold=fold, **kwargs)
555
+ frame_interpolator = self.get_interpolator(**kwargs)
556
+ interpolators = [fold_interpolator, frame_interpolator,
557
+ frame_interpolator.copy()]
558
+ fold_frame_builder = StructuralFrameBuilder(
559
+ interpolators=interpolators, name=fold_frame_data, **kwargs)
560
+ fold_frame_builder.add_data_from_data_frame(
561
+ self.data[self.data['feature_name'] == fold_frame_data])
562
+
563
+ ## add the data to the interpolator for the main foliation
564
+ fold_frame_builder[0].add_data_to_interpolator(True)
565
+ if "fold_axis" in kwargs:
566
+ fold.fold_axis = kwargs['fold_axis']
567
+ if "av_fold_axis" in kwargs:
568
+ _calculate_average_intersection(fold_frame_builder[0], fold_frame,
569
+ fold)
570
+
571
+ if fold.fold_axis is None:
572
+ far, fad = fold_frame.calculate_fold_axis_rotation(
573
+ fold_frame_builder[0])
574
+ fold_axis_rotation = FoldRotationAngle(far, fad)
575
+ a_wl = kwargs.get("axis_wl", None)
576
+ if 'axis_function' in kwargs:
577
+ # allow predefined function to be used
578
+ fold_axis_rotation.set_function(kwargs['axis_function'])
579
+ else:
580
+ fold_axis_rotation.fit_fourier_series(wl=a_wl)
581
+ fold.fold_axis_rotation = fold_axis_rotation
582
+ # give option of passing own fold limb rotation function
583
+ flr, fld = fold_frame.calculate_fold_limb_rotation(
584
+ fold_frame_builder[0])
585
+ fold_limb_rotation = FoldRotationAngle(flr, fld)
586
+ l_wl = kwargs.get("limb_wl", None)
587
+ if 'limb_function' in kwargs:
588
+ # allow for predefined functions to be used
589
+ fold_limb_rotation.set_function(kwargs['limb_function'])
590
+ else:
591
+ fold_limb_rotation.fit_fourier_series(wl=l_wl)
592
+ fold.fold_limb_rotation = fold_limb_rotation
593
+ # fold_limb_fitter = kwargs.get("fold_limb_function",
594
+ # _interpolate_fold_limb_rotation_angle)
595
+ # fold_limb_fitter(series_builder, fold_frame, fold, result, **kwargs)
596
+ kwargs['fold_weights'] = kwargs.get('fold_weights', None)
597
+
598
+ for i in range(3):
599
+ self._add_faults(fold_frame_builder[i])
600
+ # build feature
601
+ kwargs['cgw'] = 0.
602
+ kwargs['fold'] = fold
603
+ self._add_faults(fold_frame_builder[0])
604
+ self._add_faults(fold_frame_builder[1])
605
+ self._add_faults(fold_frame_builder[2])
606
+ fold_frame = fold_frame_builder.build(**kwargs, frame=FoldFrame)
607
+ fold_frame.type = 'structuralframe'
608
+ # see if any unconformities are above this feature if so add region
609
+ # for i in range(3):
610
+ # self._add_unconformity_above(fold_frame[i])
611
+
612
+ self._add_feature(fold_frame)
613
+
614
+
615
+ return fold_frame
616
+
617
+ def _add_faults(self, feature_builder, features=None):
618
+ """
619
+
620
+ Parameters
621
+ ----------
622
+ feature_builder
623
+
624
+ Returns
625
+ -------
626
+
627
+ """
628
+ if features is None:
629
+ features = self.features
630
+ for f in reversed(features):
631
+ if f.type == 'fault':
632
+ feature_builder.add_fault(f)
633
+ # if f.type == 'unconformity':
634
+ # break
635
+ def _add_domain_fault_above(self, feature):
636
+ """
637
+ Looks through the feature list and adds any domain faults to the feature. The domain fault masks everything
638
+ where the fault scalar field is < 0 as being active when added to feature.
639
+
640
+ Parameters
641
+ ----------
642
+ feature : GeologicalFeatureInterpolator
643
+ the feature being added to the model where domain faults should be added
644
+
645
+ Returns
646
+ -------
647
+
648
+ """
649
+ for f in reversed(self.features):
650
+ if f.name == feature.name:
651
+ continue
652
+ if f.type == 'domain_fault':
653
+ feature.add_region(lambda pos: f.evaluate_value(pos) < 0)
654
+ break
655
+
656
+ def _add_domain_fault_below(self, domain_fault):
657
+ """
658
+ Looks through the feature list and adds any the domain_fault to the features that already exist in the stack
659
+ until an unconformity is reached. domain faults to the feature. The domain fault masks everything
660
+ where the fault scalar field is < 0 as being active when added to feature.
661
+
662
+ Parameters
663
+ ----------
664
+ feature : GeologicalFeatureInterpolator
665
+ the feature being added to the model where domain faults should be added
666
+
667
+ Returns
668
+ -------
669
+
670
+ """
671
+ for f in reversed(self.features):
672
+ if f.name == domain_fault.name:
673
+ continue
674
+ f.add_region(lambda pos: domain_fault.evaluate_value(pos) > 0)
675
+ if f.type == 'unconformity':
676
+ break
677
+
678
+ def _add_unconformity_above(self, feature):
679
+ """
680
+
681
+ Adds a region to the feature to prevent the value from being
682
+ interpolated where the unconformities exists above e.g.
683
+ if there is another feature above and the unconformity is at 0
684
+ then the features added below (after) will only be visible where the
685
+ uncomformity is <0
686
+
687
+ Parameters
688
+ ----------
689
+ feature - GeologicalFeature
690
+
691
+ Returns
692
+ -------
693
+
694
+ """
695
+ for f in reversed(self.features):
696
+ if f.type == 'unconformity':
697
+ feature.add_region(lambda pos: f.evaluate(pos))
698
+ break
699
+
700
+ def _add_unconformity_below(self, feature):
701
+ """
702
+ Adds a region to the features that represents the
703
+ unconformity so it is not evaluated below the unconformity
704
+
705
+ Parameters
706
+ ----------
707
+ feature
708
+
709
+ Returns
710
+ -------
711
+
712
+ """
713
+ for f in self.features:
714
+ if f.type == 'series' and feature.feature.name != f.name:
715
+ f.add_region(lambda pos: ~feature.evaluate(pos))
716
+ # for f in reversed(self.features):
717
+ # if f.type == 'unconformity':
718
+ # feature.add_region(lambda pos: f.evaluate(pos))
719
+ # break
720
+ # feature.add_region(lambda pos: ~uc.evaluate(pos))
721
+
722
+ def create_and_add_unconformity(self, unconformity_surface_data, **kwargs):
723
+ """
724
+ Parameters
725
+ ----------
726
+ unconformity_surface_data : string
727
+ name of the unconformity data in the data frame
728
+
729
+ Returns
730
+ -------
731
+ """
732
+ # self.parameters['features'].append({'feature_type':'unconformity','feature_name':unconformity_surface_data,**kwargs})
733
+ interpolator = self.get_interpolator(**kwargs)
734
+ unconformity_feature_builder = GeologicalFeatureInterpolator(
735
+ interpolator, name=unconformity_surface_data)
736
+ # add data
737
+ unconformity_data = self.data[
738
+ self.data['feature_name'] == unconformity_surface_data]
739
+
740
+ unconformity_feature_builder.add_data_from_data_frame(
741
+ unconformity_data)
742
+ # look through existing features if there is a fault before an
743
+ # unconformity
744
+ # then add to the feature, once we get to an unconformity stop
745
+ self._add_faults(unconformity_feature_builder)
746
+
747
+ # build feature
748
+ uc_feature_base = unconformity_feature_builder.build(**kwargs)
749
+ uc_feature_base.type = 'unconformity_base'
750
+ # uc_feature = UnconformityFeature(uc_feature_base,0)
751
+ # iterate over existing features and add the unconformity as a
752
+ # region so the feature is only
753
+ # evaluated where the unconformity is positive
754
+ return self.add_unconformity(uc_feature_base, 0)
755
+
756
+ def add_unconformity(self, feature, value):
757
+ """
758
+ Use an existing feature to add an unconformity to the model.
759
+
760
+ Parameters
761
+ ----------
762
+ feature : GeologicalFeature
763
+ existing geological feature
764
+ value : float
765
+ scalar value of isosurface that represents
766
+
767
+ Returns
768
+ -------
769
+ unconformity : GeologicalFeature
770
+ unconformity feature
771
+
772
+ """
773
+ self.parameters['features'].append({'feature_type': 'unconformity', 'feature': feature, 'value': value})
774
+ uc_feature = UnconformityFeature(feature, value)
775
+
776
+ # for f in self.features:
777
+ # f.add_region(lambda pos: uc_feature.evaluate(pos))
778
+
779
+ # see if any unconformities are above this feature if so add region
780
+ # self._add_unconformity_above(uc_feature)
781
+ # self._add_unconformity_below(feature)#, uc_feature)
782
+ self._add_feature(uc_feature)
783
+
784
+
785
+ return uc_feature
786
+
787
+ def add_onlap_unconformity(self, feature, value):
788
+ """
789
+ Use an existing feature to add an unconformity to the model.
790
+
791
+ Parameters
792
+ ----------
793
+ feature : GeologicalFeature
794
+ existing geological feature
795
+ value : float
796
+ scalar value of isosurface that represents
797
+
798
+ Returns
799
+ -------
800
+ unconformity_feature : GeologicalFeature
801
+ the created unconformity
802
+
803
+ """
804
+ self.parameters['features'].append({'feature_type': 'onlap', 'feature': feature, 'value': value})
805
+
806
+ uc_feature = UnconformityFeature(feature, value)
807
+
808
+ # for f in self.features:
809
+ # f.add_region(lambda pos: uc_feature.evaluate(pos))
810
+
811
+ # see if any unconformities are above this feature if so add region
812
+ # self._add_unconformity_above(uc_feature)
813
+ self._add_unconformity_below(uc_feature) # , uc_feature)
814
+ self._add_feature(uc_feature)
815
+
816
+
817
+ return uc_feature
818
+
819
+ def create_and_add_domain_fault(self, fault_surface_data, **kwargs):
820
+ """
821
+ Parameters
822
+ ----------
823
+ fault_surface_data : string
824
+ name of the domain fault data in the data frame
825
+
826
+ Returns
827
+ -------
828
+ domain_Fault : GeologicalFeature
829
+ the created domain fault
830
+
831
+ """
832
+ # self.parameters['features'].append({'feature_type':'unconformity','feature_name':unconformity_surface_data,**kwargs})
833
+ interpolator = self.get_interpolator(**kwargs)
834
+ domain_fault_feature_builder = GeologicalFeatureInterpolator(
835
+ interpolator, name=fault_surface_data)
836
+ # add data
837
+ unconformity_data = self.data[
838
+ self.data['feature_name'] == fault_surface_data]
839
+
840
+ domain_fault_feature_builder.add_data_from_data_frame(
841
+ unconformity_data)
842
+ # look through existing features if there is a fault before an
843
+ # unconformity
844
+ # then add to the feature, once we get to an unconformity stop
845
+ self._add_faults(domain_fault_feature_builder)
846
+
847
+ # build feature
848
+ domain_fault = domain_fault_feature_builder.build(**kwargs)
849
+ domain_fault.type = 'domain_fault'
850
+ self._add_feature(domain_fault)
851
+ self._add_domain_fault_below(domain_fault)
852
+
853
+ # uc_feature = UnconformityFeature(uc_feature_base,0)
854
+ # iterate over existing features and add the unconformity as a
855
+ # region so the feature is only
856
+ # evaluated where the unconformity is positive
857
+ return domain_fault
858
+
859
+ def create_and_add_fault(self, fault_surface_data, displacement, **kwargs):
860
+ """
861
+ Parameters
862
+ ----------
863
+ fault_surface_data : string
864
+ name of the fault surface data in the dataframe
865
+ displacement : displacement magnitude
866
+ kwargs : additional kwargs for Fault and interpolators
867
+
868
+ Returns
869
+ -------
870
+ fault : FaultSegment
871
+ created fault
872
+ """
873
+ self.parameters['features'].append(
874
+ {'feature_type': 'fault', 'feature_name': fault_surface_data, 'displacement': displacement, **kwargs})
875
+
876
+ displacement_scaled = displacement / self.scale_factor
877
+ # create fault frame
878
+ interpolator = self.get_interpolator(**kwargs)
879
+ fault_frame_builder = StructuralFrameBuilder(interpolator,
880
+ name=fault_surface_data,
881
+ **kwargs)
882
+ # add data
883
+ fault_frame_data = self.data[
884
+ self.data['feature_name'] == fault_surface_data].copy()
885
+ if 'coord' not in fault_frame_data:
886
+ fault_frame_data['coord'] = 0
887
+ vals = fault_frame_data['val']
888
+ if len(np.unique(vals[~np.isnan(vals)])) == 1:
889
+ xyz = fault_frame_data[['X', 'Y', 'Z']].to_numpy()
890
+ p1 = xyz[0, :] # fault_frame_data.loc[0 ,['X','Y']]
891
+ p2 = xyz[-1, :] # fault_frame_data.loc[-1 ,['X','Y']]
892
+ # get a vector that goes from p1-p2 and normalise
893
+ vector = p1 - p2
894
+ length = np.linalg.norm(vector)
895
+ vector /= length
896
+ # now create the orthogonal vector
897
+ # newvector = np.zeros(3)
898
+ length /= 3
899
+ # length/=2
900
+ # print(fault_frame_data)
901
+ mask = ~np.isnan(fault_frame_data['nx'])
902
+ vectors = fault_frame_data[mask][['nx', 'ny', 'nz']].to_numpy()
903
+ lengths = np.linalg.norm(vectors, axis=1)
904
+ vectors /= lengths[:, None]
905
+ fault_frame_data.loc[mask, ['nx', 'ny', 'nz']] = vectors
906
+ if 'strike' in fault_frame_data.columns and 'dip' in \
907
+ fault_frame_data.columns:
908
+ fault_frame_data = fault_frame_data.drop(['dip', 'strike'],
909
+ axis=1)
910
+ # print(fault_frame_data)
911
+ # if there is no slip direction data assume vertical
912
+ if fault_frame_data[fault_frame_data['coord'] == 1].shape[0] == 0:
913
+ logger.info("Adding fault frame slip")
914
+ loc = np.mean(fault_frame_data[['X', 'Y', 'Z']], axis=0)
915
+ coord1 = pd.DataFrame([[loc[0], loc[1], loc[2], 0, 0, -1]],
916
+ columns=normal_vector_headers())
917
+ coord1['coord'] = 1
918
+ fault_frame_data = pd.concat([fault_frame_data, coord1],
919
+ sort=False)
920
+
921
+ if fault_frame_data[fault_frame_data['coord'] == 2].shape[0] == 0:
922
+ logger.info("Adding fault extent data as first and last point")
923
+ ## first and last point of the line
924
+ value_data = fault_frame_data[fault_frame_data['val'] == 0]
925
+ coord2 = value_data.iloc[[0, len(value_data) - 1]]
926
+ coord2 = coord2.reset_index(drop=True)
927
+ c2_scale = kwargs.get('length_scale',1.)
928
+ coord2.loc[0, 'val'] = -1/c2_scale
929
+ coord2.loc[1, 'val'] = 1/c2_scale
930
+ coord2['coord'] = 2
931
+ fault_frame_data = pd.concat([fault_frame_data, coord2],
932
+ sort=False)
933
+ fault_frame_builder.add_data_from_data_frame(fault_frame_data)
934
+ # if there is no fault slip data then we could find the strike of
935
+ # the fault and build
936
+ # the second coordinate
937
+ # if we add a region to the fault then the fault operator doesn't
938
+ # work but for visualisation
939
+ # we want to add a region!
940
+
941
+ if 'splayregion' in kwargs and 'splay' in kwargs:
942
+ # result['splayregionfeature'] = RegionFeature(kwargs['splayregion'])
943
+ # apply splay to all parts of fault frame
944
+ for i in range(3):
945
+ # work out the values of the nodes where we want hard
946
+ # constraints
947
+ idc = np.arange(0, interpolator.support.n_nodes)[
948
+ kwargs['splayregion'](interpolator.support.nodes)]
949
+ val = kwargs['splay'][i].evaluate_value(
950
+ interpolator.support.nodes[
951
+ kwargs['splayregion'](interpolator.support.nodes), :])
952
+ mask = ~np.isnan(val)
953
+ fault_frame_builder[i].interpolator.add_equality_constraints(
954
+ idc[mask], val[mask])
955
+ # check if this fault overprint any existing faults exist in the stack
956
+ overprinted = kwargs.get('overprinted', [])
957
+ self._add_faults(fault_frame_builder[0],overprinted)
958
+ self._add_faults(fault_frame_builder[1],overprinted)
959
+ self._add_faults(fault_frame_builder[2],overprinted)
960
+
961
+ fault_frame = fault_frame_builder.build(**kwargs)
962
+ if 'abut' in kwargs:
963
+ fault_frame[0].add_region(lambda pos: kwargs['abut'].evaluate(pos))
964
+
965
+ fault = FaultSegment(fault_frame, displacement=displacement_scaled,
966
+ **kwargs)
967
+ for f in reversed(self.features):
968
+ if f.type == 'unconformity':
969
+ fault.add_region(lambda pos: f.evaluate_value(pos) <= 0)
970
+ break
971
+ if displacement == 0:
972
+ fault.type = 'fault_inactive'
973
+ self._add_feature(fault)
974
+
975
+
976
+ return fault
977
+
978
+ def rescale(self, points):
979
+ """
980
+ Convert from model scale to real world scale - in the future this
981
+ should also do transformations?
982
+
983
+ Parameters
984
+ ----------
985
+ points
986
+
987
+ Returns
988
+ -------
989
+ """
990
+ points *= self.scale_factor
991
+ points += self.origin
992
+ return points
993
+
994
+ def scale(self, points):
995
+ """
996
+ Parameters
997
+ ----------
998
+ points : np.array((N,3),dtype=float)
999
+ points to
1000
+
1001
+ Returns
1002
+ -------
1003
+ """
1004
+ points = points.copy()
1005
+ points[:, :] -= self.origin
1006
+ points /= self.scale_factor
1007
+ return points
1008
+
1009
+ def voxet(self, nsteps=(50, 50, 25)):
1010
+ """
1011
+ Returns a voxet dict with the nsteps specified
1012
+
1013
+ Parameters
1014
+ ----------
1015
+ nsteps : tuple
1016
+ number of cells in
1017
+
1018
+ Returns
1019
+ -------
1020
+ """
1021
+ return {'bounding_box': self.bounding_box, 'nsteps': nsteps}
1022
+
1023
+ def regular_grid(self, nsteps=(50, 50, 25), shuffle = True, rescale=True):
1024
+ """
1025
+ Return a regular grid within the model bounding box
1026
+
1027
+ Parameters
1028
+ ----------
1029
+ nsteps : tuple
1030
+ number of cells in x,y,z
1031
+
1032
+ Returns
1033
+ -------
1034
+ xyz : np.array((N,3),dtype=float)
1035
+ locations of points in regular grid
1036
+ """
1037
+ x = np.linspace(self.bounding_box[0, 0], self.bounding_box[1, 0],
1038
+ nsteps[0])
1039
+ y = np.linspace(self.bounding_box[0, 1], self.bounding_box[1, 1],
1040
+ nsteps[1])
1041
+ z = np.linspace(self.bounding_box[1, 2], self.bounding_box[0, 2],
1042
+ nsteps[2])
1043
+ xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
1044
+ locs = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
1045
+ if shuffle:
1046
+ np.random.shuffle(locs)
1047
+ if rescale:
1048
+ locs = self.rescale(locs)
1049
+ return locs
1050
+
1051
+ def evaluate_model(self, xyz, rescale=True):
1052
+ """Evaluate the stratigraphic id at each location
1053
+
1054
+
1055
+ Parameters
1056
+ ----------
1057
+ xyz : np.array((N,3),dtype=float)
1058
+ locations
1059
+ rescale : bool
1060
+ whether to rescale the model
1061
+
1062
+ Returns
1063
+ -------
1064
+ stratigraphic_id : np.array(N,dtype=int)
1065
+ the stratigraphic index for locations
1066
+
1067
+ Examples
1068
+ --------
1069
+ Evaluate on a voxet
1070
+
1071
+ >>> x = np.linspace(self.bounding_box[0, 0], self.bounding_box[1, 0],
1072
+ nsteps[0])
1073
+ >>> y = np.linspace(self.bounding_box[0, 1], self.bounding_box[1, 1],
1074
+ nsteps[1])
1075
+ >>> z = np.linspace(self.bounding_box[1, 2], self.bounding_box[0, 2],
1076
+ nsteps[2])
1077
+ >>> xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
1078
+ >>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
1079
+ >>> model.evaluate_model(xyz)
1080
+
1081
+ Evaluate on points defined by regular grid function
1082
+
1083
+ >>> model.evaluate_model(model.regular_grid())
1084
+
1085
+
1086
+ Evaluate on a map
1087
+
1088
+ >>> x = np.linspace(self.bounding_box[0, 0], self.bounding_box[1, 0],
1089
+ nsteps[0])
1090
+ >>> y = np.linspace(self.bounding_box[0, 1], self.bounding_box[1, 1],
1091
+ nsteps[1])
1092
+ >>> xx, yy = np.meshgrid(x, y, indexing='ij')
1093
+ >>> zz = np.zeros_like(yy)
1094
+ >>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
1095
+ >>> model.evaluate_model(xyz)
1096
+
1097
+ """
1098
+ strat_id = np.zeros(xyz.shape[0],dtype=int)
1099
+ for group in self.stratigraphic_column.keys():
1100
+ feature_id = self.feature_name_index.get(group, -1)
1101
+ if feature_id >= 0:
1102
+ feature = self.features[feature_id]
1103
+ vals = feature.evaluate_value(xyz)
1104
+ for series in self.stratigraphic_column[group].values():
1105
+ strat_id[np.logical_and(vals < series.get('max',feature.max()), vals > series.get('min',feature.min()))] = series['id']
1106
+ if feature_id == -1:
1107
+ logger.error('Model does not contain {}'.format(group))
1108
+ return strat_id
1109
+
1110
+ def get_feature_by_name(self, feature_name):
1111
+ """Returns a feature from the mode given a name
1112
+
1113
+
1114
+ Parameters
1115
+ ----------
1116
+ feature_name : string
1117
+ the name of the feature
1118
+
1119
+ Returns
1120
+ -------
1121
+ feature : GeologicalFeature
1122
+ the geological feature with the specified name, or none if no feature
1123
+ """
1124
+ feature_index = self.feature_name_index.get(feature_name,-1)
1125
+ if feature_index >0:
1126
+ return self.features[feature_index]
1127
+ else:
1128
+ return None
1129
+
1130
+ def evaluate_feature_value(self, feature_name, xyz, scale=True):
1131
+ """Evaluate the scalar value of the geological feature given the name at locations
1132
+ xyz
1133
+
1134
+ Parameters
1135
+ ----------
1136
+ feature_name : string
1137
+ name of the feature
1138
+ xyz : np.array((N,3))
1139
+ locations to evaluate
1140
+ scale : bool, optional
1141
+ whether to scale real world points into model scale, by default True
1142
+
1143
+ Returns
1144
+ -------
1145
+ np.array((N))
1146
+ vector of scalar values
1147
+ """
1148
+ feature = self.get_feature_by_name(feature_name)
1149
+ if feature:
1150
+ if scale:
1151
+ scaled_xyz = self.scale(xyz)
1152
+ return feature.evaluate_value(scaled_xyz)
1153
+ else:
1154
+ return np.zeros(xyz.shape[0])
1155
+
1156
+ def evaluate_feature_gradient(self, feature_name, xyz, scale=True):
1157
+ """Evaluate the gradient of the geological feature at a location
1158
+
1159
+ Parameters
1160
+ ----------
1161
+ feature_name : string
1162
+ name of the geological feature
1163
+ xyz : np.array((N,3))
1164
+ locations to evaluate
1165
+ scale : bool, optional
1166
+ whether to scale real world points into model scale, by default True
1167
+
1168
+ Returns
1169
+ -------
1170
+ results : np.array((N,3))
1171
+ gradient of the scalar field at the locations specified
1172
+ """
1173
+ feature = self.get_feature_by_name(feature_name)
1174
+ if feature:
1175
+ if scale:
1176
+ scaled_xyz = self.scale(xyz)
1177
+ return feature.evaluate_gradient(scaled_xyz)
1178
+ else:
1179
+ return np.zeros(xyz.shape[0])