LoopStructural 1.0.9__cp39-cp39-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. LoopStructural/__init__.py +36 -0
  2. LoopStructural/analysis/__init__.py +2 -0
  3. LoopStructural/analysis/_fault_displacement.py +67 -0
  4. LoopStructural/analysis/_fault_intersection.py +36 -0
  5. LoopStructural/analysis/_plane_fit.py +43 -0
  6. LoopStructural/datasets/__init__.py +12 -0
  7. LoopStructural/datasets/_base.py +65 -0
  8. LoopStructural/datasets/data/claudius.csv +21049 -0
  9. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  10. LoopStructural/datasets/data/duplex.csv +126 -0
  11. LoopStructural/datasets/data/duplexbb.txt +2 -0
  12. LoopStructural/datasets/data/intrusion.csv +1017 -0
  13. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  14. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  15. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  16. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  17. LoopStructural/datasets/data/refolded_fold.csv +2126 -0
  18. LoopStructural/interpolators/__init__.py +34 -0
  19. LoopStructural/interpolators/base_structured_3d_support.py +101 -0
  20. LoopStructural/interpolators/cython/__init__.py +0 -0
  21. LoopStructural/interpolators/cython/dsi_helper.c +31046 -0
  22. LoopStructural/interpolators/cython/dsi_helper.cp39-win32.pyd +0 -0
  23. LoopStructural/interpolators/discrete_fold_interpolator.py +202 -0
  24. LoopStructural/interpolators/discrete_interpolator.py +584 -0
  25. LoopStructural/interpolators/finite_difference_interpolator.py +399 -0
  26. LoopStructural/interpolators/geological_interpolator.py +197 -0
  27. LoopStructural/interpolators/operator.py +61 -0
  28. LoopStructural/interpolators/piecewiselinear_interpolator.py +437 -0
  29. LoopStructural/interpolators/structured_grid.py +421 -0
  30. LoopStructural/interpolators/structured_tetra.py +681 -0
  31. LoopStructural/interpolators/surfe_wrapper.py +118 -0
  32. LoopStructural/modelling/__init__.py +46 -0
  33. LoopStructural/modelling/core/__init__.py +0 -0
  34. LoopStructural/modelling/core/geological_model.py +1513 -0
  35. LoopStructural/modelling/core/geological_model_graph.py +880 -0
  36. LoopStructural/modelling/core/stratigraphic_column.py +5 -0
  37. LoopStructural/modelling/fault/__init__.py +4 -0
  38. LoopStructural/modelling/fault/fault_builder.py +143 -0
  39. LoopStructural/modelling/fault/fault_function.py +188 -0
  40. LoopStructural/modelling/fault/fault_function_feature.py +76 -0
  41. LoopStructural/modelling/fault/fault_segment.py +298 -0
  42. LoopStructural/modelling/features/__init__.py +8 -0
  43. LoopStructural/modelling/features/cross_product_geological_feature.py +90 -0
  44. LoopStructural/modelling/features/geological_feature.py +252 -0
  45. LoopStructural/modelling/features/geological_feature_builder.py +414 -0
  46. LoopStructural/modelling/features/lambda_geological_feature.py +36 -0
  47. LoopStructural/modelling/features/region_feature.py +34 -0
  48. LoopStructural/modelling/features/structural_frame.py +133 -0
  49. LoopStructural/modelling/features/structural_frame_builder.py +206 -0
  50. LoopStructural/modelling/features/unconformity_feature.py +74 -0
  51. LoopStructural/modelling/fold/__init__.py +8 -0
  52. LoopStructural/modelling/fold/fold.py +143 -0
  53. LoopStructural/modelling/fold/fold_rotation_angle.py +133 -0
  54. LoopStructural/modelling/fold/fold_rotation_angle_feature.py +58 -0
  55. LoopStructural/modelling/fold/foldframe.py +193 -0
  56. LoopStructural/modelling/fold/svariogram.py +180 -0
  57. LoopStructural/utils/__init__.py +18 -0
  58. LoopStructural/utils/bounding_box.py +21 -0
  59. LoopStructural/utils/exceptions.py +10 -0
  60. LoopStructural/utils/helper.py +382 -0
  61. LoopStructural/utils/logging.py +60 -0
  62. LoopStructural/utils/map2loop.py +353 -0
  63. LoopStructural/utils/regions.py +11 -0
  64. LoopStructural/utils/utils.py +69 -0
  65. LoopStructural/visualisation/__init__.py +31 -0
  66. LoopStructural/visualisation/map_viewer.py +322 -0
  67. LoopStructural/visualisation/model_plotter.py +17 -0
  68. LoopStructural/visualisation/model_visualisation.py +1113 -0
  69. LoopStructural/visualisation/rotation_angle_plotter.py +83 -0
  70. LoopStructural/visualisation/sphinx_scraper.py +34 -0
  71. LoopStructural/visualisation/stratigraphic_column.py +60 -0
  72. LoopStructural-1.0.9.dist-info/LICENSE +21 -0
  73. LoopStructural-1.0.9.dist-info/METADATA +19 -0
  74. LoopStructural-1.0.9.dist-info/RECORD +77 -0
  75. LoopStructural-1.0.9.dist-info/WHEEL +5 -0
  76. LoopStructural-1.0.9.dist-info/top_level.txt +2 -0
  77. tests/__init__.py +0 -0
@@ -0,0 +1,193 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from LoopStructural.modelling.features.structural_frame import StructuralFrame
6
+
7
+ from LoopStructural.utils import getLogger
8
+ logger = getLogger(__name__)
9
+
10
+
11
+ class FoldFrame(StructuralFrame):
12
+ def __init__(self, name, features, fold=None):
13
+ """
14
+ A structural frame that can calculate the fold axis/limb rotation angle
15
+ Same constructor arguments as parent StructuralFrame
16
+
17
+ Parameters
18
+ ----------
19
+ name
20
+ features
21
+ """
22
+ super().__init__(name, features, fold)
23
+ self.model = None
24
+
25
+ def calculate_fold_axis_rotation(self, feature_builder,fold_axis=None):
26
+ """
27
+ Calculate the fold axis rotation angle by finding the angle between the
28
+ intersection lineation and the gradient to the 1st coordinate of the
29
+ fold frame
30
+ Parameters
31
+ ----------
32
+ feature_builder - GeologicalFeatureInterpolator
33
+ - the builder for the geological feature that is folded
34
+
35
+ Returns
36
+ -------
37
+
38
+ """
39
+ gpoints = feature_builder.get_gradient_constraints()[:,:6]
40
+ npoints = feature_builder.get_norm_constraints()[:,:6]
41
+ points = []
42
+ if gpoints.shape[0] > 0:
43
+ points.append(gpoints)
44
+ if npoints.shape[0] > 0:
45
+ points.append(npoints)
46
+ if fold_axis is not None:
47
+ if fold_axis.shape[0] > 0 and fold_axis.shape[1] == 6:
48
+ points.append(fold_axis)
49
+ if len(points) == 0:
50
+ return 0, 0
51
+ points = np.vstack(points)
52
+ # We need to ignore the fault when we are calculating the splot because it is done
53
+ # in the restored space
54
+ # self.features[0].faults_enabled = False
55
+ # self.features[1].faults_enabled = False
56
+ s1g = self.features[0].evaluate_gradient(points[:, :3])
57
+ s1g /= np.linalg.norm(s1g, axis=1)[:, None]
58
+ s1gyg = self.features[1].evaluate_gradient(points[:, :3])
59
+ s1gyg /= np.linalg.norm(s1gyg, axis=1)[:, None]
60
+ l1 = points[:, 3:]
61
+ l1 /= np.linalg.norm(l1, axis=1)[:, None]
62
+ fad = self.features[1].evaluate_value(points[:, :3])
63
+ # Turn the faults back on
64
+ # self.features[0].faults_enabled = True
65
+ # self.features[1].faults_enabled = True
66
+
67
+ # project s0 onto axis plane B X A X B
68
+ projected_l1 = np.cross(s1g, np.cross(l1, s1g, axisa=1, axisb=1),
69
+ axisa=1, axisb=1)
70
+ projected_s1gyg = np.cross(s1g, np.cross(s1gyg, s1g, axisa=1, axisb=1),
71
+ axisa=1, axisb=1)
72
+
73
+ # einsum dot product
74
+ far = np.einsum('ij,ij->i', projected_l1, projected_s1gyg)
75
+ far = np.rad2deg(np.arccos(far))
76
+ # scalar triple product
77
+ stp = np.einsum('ij,ij->i', np.cross(l1, s1gyg, axisa=1, axisb=1), s1g)
78
+ # check bounds
79
+ far-=90
80
+ # far[stp < 0] = 360.- far[stp < 0]
81
+ far[far>90] = far[far>90]+-180
82
+ far[far<-90] = far[far<-90]+180
83
+
84
+ return far, fad
85
+
86
+ def calculate_fold_limb_rotation(self, feature_builder, axis=None):
87
+ """
88
+ Calculate the fold limb rotation angle using the axis specified and
89
+ the normals to the folded foliation
90
+ Parameters
91
+ ----------
92
+ feature_builder - GeologicalFeatureInterpolator
93
+ the feature interpolator for the folded feature that has the
94
+ datapoints the fold limb rotation angle is
95
+ going to be calculated for
96
+ axis - GeologicalFeature
97
+ Optional. Fold axis feature that when queried for location
98
+ returns the fold axis
99
+
100
+ Returns
101
+ -------
102
+ fold_limb_rotation, coordinate_0
103
+ """
104
+ # self.features[0].faults_enabled = False
105
+ # self.features[1].faults_enabled = False
106
+
107
+ gpoints = feature_builder.interpolator.get_gradient_constraints()[:,:6]
108
+ npoints = feature_builder.interpolator.get_norm_constraints()[:,:6]
109
+ points = []
110
+ if gpoints.shape[0] > 0:
111
+ points.append(gpoints)
112
+ if npoints.shape[0] > 0:
113
+ points.append(npoints)
114
+ if len(points) == 0:
115
+ logger.error("No points to calculate fold rotation angle")
116
+ return np.array([0]), np.array([0])
117
+ points = np.vstack(points)
118
+ # for f in feature_builder.faults:
119
+ # points[:,:3] = f.apply_to_points(points[:,:3])
120
+ # get the normals from the points array
121
+ s0g = points[:, 3:]
122
+
123
+ s0g/=np.linalg.norm(s0g,axis=1)[:,None]
124
+ # calculate the gradient and value of the first coordinate of the
125
+ # fold frame
126
+ # for the locations and normalise
127
+ s1g = self.features[0].evaluate_gradient(points[:, :3])
128
+ s1g /= np.linalg.norm(s1g, axis=1)[:, None]
129
+ s1 = self.features[0].evaluate_value(points[:, :3])
130
+ # self.features[0].faults_enabled = True
131
+ # self.features[1].faults_enabled = True
132
+
133
+ if axis is None:
134
+ logger.info("Not using fold axis for fold limb rotation angle calculation")
135
+ r2 = np.einsum('ij,ij->i', s1g, s0g)
136
+
137
+ return np.rad2deg(np.arcsin(r2)), s1
138
+ if axis is not None:
139
+ fold_axis = axis(points[:, :3])
140
+ # project s0 onto axis plane B X A X B
141
+ projected_s0 = np.cross(fold_axis,
142
+ np.cross(s0g, fold_axis, axisa=1, axisb=1),
143
+ axisa=1, axisb=1)
144
+ projected_s1 = np.cross(fold_axis,
145
+ np.cross(s1g, fold_axis, axisa=1, axisb=1),
146
+ axisa=1, axisb=1)
147
+ projected_s0 /= np.linalg.norm(projected_s0, axis=1)[:, None]
148
+ projected_s1 /= np.linalg.norm(projected_s1, axis=1)[:, None]
149
+ r2 = np.einsum('ij,ij->i', projected_s1, projected_s0) #
150
+ # adjust the fold rotation angle so that its always between -90
151
+ # and 90
152
+ vv = np.cross(s1g, s0g, axisa=1, axisb=1)
153
+ ds = np.einsum('ij,ij->i', fold_axis, vv)
154
+ flr = np.where(ds > 0, np.rad2deg(np.arcsin(r2)),
155
+ (- np.rad2deg(np.arcsin(r2))))
156
+ flr = np.where(flr < -90, (180. + flr), flr)
157
+ flr = np.where(flr > 90, -(180. - flr), flr)
158
+ return flr, s1
159
+
160
+ def calculate_intersection_lineation(self, feature_builder):
161
+ """
162
+ Calculate the intersection lineation by finding the cross product
163
+ between the first fold frame
164
+ coordinate and the vector representing the normal to the folded
165
+ foliation
166
+ Parameters
167
+ ----------
168
+ feature_builder - GeologicalFeatureInterpolator
169
+ the feature builder that contains the data points that the
170
+ intersection lineation is calculated for
171
+
172
+
173
+ Returns Nx3 array of doubles
174
+ -------
175
+
176
+ """
177
+ self.features[0].faults_enabled = False
178
+ gpoints = feature_builder.interpolator.get_gradient_constraints()[:,:6]
179
+ npoints = feature_builder.interpolator.get_norm_constraints()[:,:6]
180
+ points = []
181
+ if gpoints.shape[0] > 0:
182
+ points.append(gpoints)
183
+ if npoints.shape[0] > 0:
184
+ points.append(npoints)
185
+ points = np.vstack(points)
186
+ s1g = self.features[0].evaluate_gradient(points[:, :3])
187
+ s1g /= np.linalg.norm(points[:, :3], axis=1)[:, None]
188
+ s0g = points[:, 3:]
189
+ s0g /= np.linalg.norm(s0g, axis=1)[:, None]
190
+ l1 = np.cross(s1g, s0g, axisa=1, axisb=1)
191
+ l1 /= np.linalg.norm(l1, axis=1)[:, None]
192
+ self.features[0].faults_enabled = True
193
+ return l1
@@ -0,0 +1,180 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from LoopStructural.utils import getLogger
6
+ logger = getLogger(__name__)
7
+
8
+
9
+ def find_peaks_and_troughs(x, y):
10
+ """
11
+
12
+ Parameters
13
+ ----------
14
+ x np.array or list
15
+ x axis data for plot
16
+ y np.array or list
17
+ y axis data for plot
18
+ Returns
19
+ -------
20
+ (np.array, np.array)
21
+ Notes
22
+ -----
23
+ Returns the loations of maxima/minima on the curve using finite
24
+ difference forward/backwards
25
+ finding the change in derivative
26
+ """
27
+ if len(x) != len(y):
28
+ return False
29
+ pairsx = []
30
+ pairsy = []
31
+ # #TODO numpyize
32
+ for i in range(0, len(x)):
33
+ if i < 1:
34
+ pairsx.append(x[i])
35
+ pairsy.append(y[i])
36
+
37
+ continue
38
+ if i > len(x) - 2:
39
+ pairsx.append(x[i])
40
+ pairsy.append(y[i])
41
+ continue
42
+ left_grad = (y[i - 1] - y[i]) / (x[i - 1] - x[i])
43
+ right_grad = (y[i] - y[i + 1]) / (x[i] - x[i + 1])
44
+ if np.sign(left_grad) != np.sign(right_grad):
45
+ pairsx.append(x[i])
46
+ pairsy.append(y[i])
47
+ return pairsx, pairsy
48
+
49
+
50
+ class SVariogram():
51
+ """
52
+ The SVariogram is an experimental semi-variogram.
53
+ """
54
+
55
+ def __init__(self, xdata, ydata):
56
+ self.xdata = xdata
57
+ self.ydata = ydata
58
+ self.dist = np.abs(self.xdata[:, None] - self.xdata[None, :])
59
+ self.variance_matrix = (self.ydata[:, None] - self.ydata[None, :]) ** 2
60
+ self.lags = None
61
+ self.variogram = 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("Using lag: %f kwarg for S-variogram"%step)
88
+
89
+ if nlag is not None:
90
+ nstep = nlag
91
+ logger.info("Using nlag %i kwarg for s-variogram"%nstep)
92
+
93
+ self.lags = np.arange(step / 2., nstep * step, step)
94
+
95
+ if nlag is None and lag is not None:
96
+ nstep = int(
97
+ np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
98
+ logger.info("Using lag kwarg but calculating nlag as %i for s-variogram"%nstep)
99
+
100
+ self.lags = np.arange(step / 2., nstep * step, step)
101
+
102
+ if lags is not None:
103
+ self.lags = lags
104
+
105
+ if self.lags is None:
106
+ # time to guess the step size
107
+ # find the average distance between elements in the input data
108
+ d = np.copy(self.dist)
109
+ d[d == 0] = np.nan
110
+
111
+ step = np.nanmean(np.nanmin(d, axis=1))*4.
112
+ # find number of steps to cover range in data
113
+ nstep = int(
114
+ np.ceil((np.nanmax(self.xdata) - np.nanmin(self.xdata)) / step))
115
+ self.lags = np.arange(step / 2., nstep * step, step)
116
+ logger.info("Using average minimum nearest neighbour distance "
117
+ "as lag distance size {} and using {} lags".format(step,nstep))
118
+ tol = self.lags[1] - self.lags[0]
119
+ self.variogram = np.zeros(self.lags.shape)
120
+ self.variogram[:] = np.nan
121
+ npairs = np.zeros(self.lags.shape)
122
+ for i in range(len(self.lags)):
123
+ logic = np.logical_and(self.dist > self.lags[i]
124
+ - tol / 2.,
125
+ self.dist < self.lags[i] + tol / 2.)
126
+ npairs[i] = np.sum(logic.astype(int))
127
+ if npairs[i] > 0:
128
+ self.variogram[i] = np.mean(self.variance_matrix[logic])
129
+ return self.lags, self.variogram, npairs
130
+
131
+ def find_wavelengths(self, **kwargs):
132
+ """
133
+ Picks the wavelengths of the fold by finding the maximum and
134
+ minimums of the s-variogram
135
+ the fold wavelength is the first minimum but it is more reliable to
136
+ use the first maximum
137
+ as the estimate of the wavelength.
138
+
139
+ Parameters
140
+ ----------
141
+ kwargs : object
142
+ """
143
+ h, var, npairs = self.calc_semivariogram(**kwargs)
144
+
145
+ px, py = find_peaks_and_troughs(h, var)
146
+
147
+ averagex = []
148
+ averagey = []
149
+ for i in range(len(px) - 1):
150
+ averagex.append((px[i] + px[i + 1]) / 2.)
151
+ averagey.append((py[i] + py[i + 1]) / 2.)
152
+ i += 1 # iterate twice
153
+ # find the extrema of the average curve
154
+ px2, py2 = find_peaks_and_troughs(averagex, averagey)
155
+ wl1 = 0.
156
+ wl1py = 0.
157
+ for i in range(len(px)):
158
+ if i > 0 and i < len(px) - 1:
159
+ if py[i] > 10:
160
+
161
+ if py[i - 1] < py[i] * .7:
162
+ if py[i + 1] < py[i] * .7:
163
+ wl1 = px[i]
164
+ if wl1 > 0.:
165
+ wl1py = py[i]
166
+ break
167
+ wl2 = 0.
168
+ for i in range(len(px2)):
169
+ if i > 0 and i < len(px2) - 1:
170
+ if py2[i - 1] < py2[i] * .90:
171
+ if py2[i + 1] < py2[i] * .90:
172
+ wl2 = px2[i]
173
+ if wl2 > 0. and wl2 > wl1 * 2 and wl1py < py2[i]:
174
+ break
175
+ if wl1 == 0.0 and wl2 == 0.0:
176
+ return 2 * (np.max(self.xdata) - np.min(self.xdata)), 0.
177
+ if np.isclose(wl1, 0.0):
178
+ return np.array([wl2 * 2., wl1 * 2.])
179
+ # wavelength is 2x the peak on the curve
180
+ return np.array([wl1 * 2., wl2 * 2.])
@@ -0,0 +1,18 @@
1
+ """
2
+ This is contains all of the utilities for loop structural
3
+
4
+ Submodules
5
+ ==========
6
+
7
+ .. autosummary::
8
+ :toctree: _autosummary
9
+
10
+ helper
11
+ utils
12
+
13
+ """
14
+ from .logging import getLogger, log_to_file, log_to_console
15
+ from .map2loop import process_map2loop, build_model
16
+ from .helper import get_data_axis_aligned_bounding_box, get_data_bounding_box, get_data_bounding_box_map
17
+ from .helper import get_dip_vector,get_strike_vector, get_vectors
18
+ from .regions import RegionEverywhere
@@ -0,0 +1,21 @@
1
+ # class BoundingBox:
2
+ # def __init__(self,origin,maximum):
3
+ # self.bb = np.array([origin,maximum])
4
+ # self.maximum = maximum
5
+ # self.name_map = {'xmin':(0,0),'ymin':(0,1),'zmin':(0,2),'xmax':(1,0),'ymax':(1,1),'zmax':(1,2)
6
+ # 'lower':(0,2),'upper':(1,2),
7
+ # 'minx':(0,0),'miny':(0,1),'minz':(0,2),'maxx':(1,0),'maxy':(1,1),'maxz':(1,2)}
8
+ # def get_value(self,name):
9
+ # ix,iy = self.name_map.get(name,(-1,-1))
10
+
11
+ # return self.bb[ix,]
12
+
13
+ # def is_inside(self,xyz):
14
+ # inside = np.zeros(xy.shape[0],dtype=bool)
15
+ # inside = np.logical_and(inside,xyz[:,0]>self.bb[0,0])
16
+ # inside = np.logical_and(inside,xyz[:,0]<self.bb[1,0])
17
+ # inside = np.logical_and(inside,xyz[:,1]>self.bb[0,1])
18
+ # inside = np.logical_and(inside,xyz[:,1]<self.bb[1,1])
19
+ # inside = np.logical_and(inside,xyz[:,2]>self.bb[0,2])
20
+ # inside = np.logical_and(inside,xyz[:,2]<self.bb[1,2])
21
+ # return inside
@@ -0,0 +1,10 @@
1
+ import logging
2
+
3
+ from LoopStructural.utils import getLogger
4
+ logger = getLogger(__name__)
5
+
6
+ class LoopBaseException(Exception):
7
+ """
8
+ Base loop exception
9
+ """
10
+ # logger.error("Raising loop base exception")