LoopStructural 1.6.12__py3-none-any.whl → 1.6.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of LoopStructural might be problematic. Click here for more details.
- LoopStructural/datatypes/_bounding_box.py +1 -1
- LoopStructural/modelling/features/_base_geological_feature.py +1 -1
- LoopStructural/modelling/features/_lambda_geological_feature.py +67 -9
- LoopStructural/modelling/features/builders/_fault_builder.py +131 -144
- LoopStructural/modelling/features/fault/_fault_function.py +4 -2
- LoopStructural/modelling/features/fault/_fault_segment.py +1 -1
- LoopStructural/utils/_surface.py +1 -1
- LoopStructural/utils/regions.py +45 -41
- LoopStructural/version.py +1 -1
- {loopstructural-1.6.12.dist-info → loopstructural-1.6.13.dist-info}/METADATA +1 -1
- {loopstructural-1.6.12.dist-info → loopstructural-1.6.13.dist-info}/RECORD +14 -14
- {loopstructural-1.6.12.dist-info → loopstructural-1.6.13.dist-info}/WHEEL +1 -1
- {loopstructural-1.6.12.dist-info → loopstructural-1.6.13.dist-info}/licenses/LICENSE +0 -0
- {loopstructural-1.6.12.dist-info → loopstructural-1.6.13.dist-info}/top_level.txt +0 -0
|
@@ -387,7 +387,7 @@ class BoundingBox:
|
|
|
387
387
|
|
|
388
388
|
if not local:
|
|
389
389
|
coordinates = [
|
|
390
|
-
np.linspace(self.global_origin[i], self.global_maximum[i], nsteps[i])
|
|
390
|
+
np.linspace(self.global_origin[i]+self.origin[i], self.global_maximum[i], nsteps[i])
|
|
391
391
|
for i in range(self.dimensions)
|
|
392
392
|
]
|
|
393
393
|
coordinate_grid = np.meshgrid(*coordinates, indexing="ij")
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Geological features
|
|
3
3
|
"""
|
|
4
|
-
|
|
4
|
+
from LoopStructural.utils.maths import regular_tetraherdron_for_points, gradient_from_tetrahedron
|
|
5
5
|
from ...modelling.features import BaseFeature
|
|
6
6
|
from ...utils import getLogger
|
|
7
7
|
from ...modelling.features import FeatureType
|
|
8
8
|
import numpy as np
|
|
9
9
|
from typing import Callable, Optional
|
|
10
|
+
from ...utils import LoopValueError
|
|
10
11
|
|
|
11
12
|
logger = getLogger(__name__)
|
|
12
13
|
|
|
@@ -18,8 +19,8 @@ class LambdaGeologicalFeature(BaseFeature):
|
|
|
18
19
|
name: str = "unnamed_lambda",
|
|
19
20
|
gradient_function: Optional[Callable[[np.ndarray], np.ndarray]] = None,
|
|
20
21
|
model=None,
|
|
21
|
-
regions: list =
|
|
22
|
-
faults: list =
|
|
22
|
+
regions: Optional[list] = None,
|
|
23
|
+
faults: Optional[list] = None,
|
|
23
24
|
builder=None,
|
|
24
25
|
):
|
|
25
26
|
"""A lambda geological feature is a wrapper for a geological
|
|
@@ -43,10 +44,11 @@ class LambdaGeologicalFeature(BaseFeature):
|
|
|
43
44
|
builder : _type_, optional
|
|
44
45
|
_description_, by default None
|
|
45
46
|
"""
|
|
46
|
-
BaseFeature.__init__(self, name, model, faults, regions, builder)
|
|
47
|
+
BaseFeature.__init__(self, name, model, faults if faults is not None else [], regions if regions is not None else [], builder)
|
|
47
48
|
self.type = FeatureType.LAMBDA
|
|
48
49
|
self.function = function
|
|
49
50
|
self.gradient_function = gradient_function
|
|
51
|
+
self.regions = regions if regions is not None else []
|
|
50
52
|
|
|
51
53
|
def evaluate_value(self, pos: np.ndarray, ignore_regions=False) -> np.ndarray:
|
|
52
54
|
"""_summary_
|
|
@@ -62,13 +64,16 @@ class LambdaGeologicalFeature(BaseFeature):
|
|
|
62
64
|
_description_
|
|
63
65
|
"""
|
|
64
66
|
v = np.zeros((pos.shape[0]))
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
v[:] = np.nan
|
|
68
|
+
|
|
69
|
+
mask = self._calculate_mask(pos, ignore_regions=ignore_regions)
|
|
70
|
+
pos = self._apply_faults(pos)
|
|
71
|
+
if self.function is not None:
|
|
72
|
+
|
|
73
|
+
v[mask] = self.function(pos[mask,:])
|
|
69
74
|
return v
|
|
70
75
|
|
|
71
|
-
def evaluate_gradient(self, pos: np.ndarray, ignore_regions=False) -> np.ndarray:
|
|
76
|
+
def evaluate_gradient(self, pos: np.ndarray, ignore_regions=False,element_scale_parameter=None) -> np.ndarray:
|
|
72
77
|
"""_summary_
|
|
73
78
|
|
|
74
79
|
Parameters
|
|
@@ -81,7 +86,60 @@ class LambdaGeologicalFeature(BaseFeature):
|
|
|
81
86
|
np.ndarray
|
|
82
87
|
_description_
|
|
83
88
|
"""
|
|
89
|
+
if pos.shape[1] != 3:
|
|
90
|
+
raise LoopValueError("Need Nx3 array of xyz points to evaluate gradient")
|
|
91
|
+
logger.info(f'Calculating gradient for {self.name}')
|
|
92
|
+
if element_scale_parameter is None:
|
|
93
|
+
if self.model is not None:
|
|
94
|
+
element_scale_parameter = np.min(self.model.bounding_box.step_vector) / 10
|
|
95
|
+
else:
|
|
96
|
+
element_scale_parameter = 1
|
|
97
|
+
else:
|
|
98
|
+
try:
|
|
99
|
+
element_scale_parameter = float(element_scale_parameter)
|
|
100
|
+
except ValueError:
|
|
101
|
+
logger.error("element_scale_parameter must be a float")
|
|
102
|
+
element_scale_parameter = 1
|
|
84
103
|
v = np.zeros((pos.shape[0], 3))
|
|
104
|
+
v = np.zeros(pos.shape)
|
|
105
|
+
v[:] = np.nan
|
|
106
|
+
mask = self._calculate_mask(pos, ignore_regions=ignore_regions)
|
|
107
|
+
# evaluate the faults on the nodes of the faulted feature support
|
|
108
|
+
# then evaluate the gradient at these points
|
|
109
|
+
if len(self.faults) > 0:
|
|
110
|
+
# generate a regular tetrahedron for each point
|
|
111
|
+
# we will then move these points by the fault and then recalculate the gradient.
|
|
112
|
+
# this should work...
|
|
113
|
+
resolved = False
|
|
114
|
+
tetrahedron = regular_tetraherdron_for_points(pos, element_scale_parameter)
|
|
115
|
+
|
|
116
|
+
while resolved:
|
|
117
|
+
for f in self.faults:
|
|
118
|
+
v = (
|
|
119
|
+
f[0]
|
|
120
|
+
.evaluate_value(tetrahedron.reshape(-1, 3), fillnan='nearest')
|
|
121
|
+
.reshape(tetrahedron.shape[0], 4)
|
|
122
|
+
)
|
|
123
|
+
flag = np.logical_or(np.all(v > 0, axis=1), np.all(v < 0, axis=1))
|
|
124
|
+
if np.any(~flag):
|
|
125
|
+
logger.warning(
|
|
126
|
+
f"Points are too close to fault {f[0].name}. Refining the tetrahedron"
|
|
127
|
+
)
|
|
128
|
+
element_scale_parameter *= 0.5
|
|
129
|
+
tetrahedron = regular_tetraherdron_for_points(pos, element_scale_parameter)
|
|
130
|
+
|
|
131
|
+
resolved = True
|
|
132
|
+
|
|
133
|
+
tetrahedron_faulted = self._apply_faults(np.array(tetrahedron.reshape(-1, 3))).reshape(
|
|
134
|
+
tetrahedron.shape
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
values = self.function(tetrahedron_faulted.reshape(-1, 3)).reshape(
|
|
138
|
+
(-1, 4)
|
|
139
|
+
)
|
|
140
|
+
v[mask, :] = gradient_from_tetrahedron(tetrahedron[mask, :, :], values[mask])
|
|
141
|
+
|
|
142
|
+
return v
|
|
85
143
|
if self.gradient_function is None:
|
|
86
144
|
v[:, :] = np.nan
|
|
87
145
|
else:
|
|
@@ -3,7 +3,6 @@ from typing import Union
|
|
|
3
3
|
from LoopStructural.utils.maths import rotation
|
|
4
4
|
from ._structural_frame_builder import StructuralFrameBuilder
|
|
5
5
|
from .. import AnalyticalGeologicalFeature
|
|
6
|
-
from LoopStructural.utils import get_vectors
|
|
7
6
|
import numpy as np
|
|
8
7
|
import pandas as pd
|
|
9
8
|
from ....utils import getLogger
|
|
@@ -52,8 +51,11 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
52
51
|
self.frame.model = model
|
|
53
52
|
self.origin = np.array([np.nan, np.nan, np.nan])
|
|
54
53
|
self.maximum = np.array([np.nan, np.nan, np.nan]) # self.model.bounding_box[1, :]
|
|
54
|
+
|
|
55
|
+
if bounding_box is None:
|
|
56
|
+
raise ValueError("BoundingBox cannot be None")
|
|
57
|
+
|
|
55
58
|
# define a maximum area to mesh adding buffer to model
|
|
56
|
-
# buffer = .2
|
|
57
59
|
self.minimum_origin = bounding_box.with_buffer(fault_bounding_box_buffer).origin
|
|
58
60
|
self.maximum_maximum = bounding_box.with_buffer(fault_bounding_box_buffer).maximum
|
|
59
61
|
|
|
@@ -66,8 +68,23 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
66
68
|
self.fault_centre = None
|
|
67
69
|
|
|
68
70
|
def update_geometry(self, points):
|
|
71
|
+
"""
|
|
72
|
+
Update the geometry of the fault by adjusting the origin and maximum bounds
|
|
73
|
+
based on the provided points.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
points : numpy.ndarray
|
|
78
|
+
Array of points used to update the fault geometry.
|
|
79
|
+
"""
|
|
80
|
+
if self.origin is None or self.maximum is None:
|
|
81
|
+
raise ValueError("Origin and maximum must be initialized before updating geometry.")
|
|
82
|
+
|
|
69
83
|
self.origin = np.nanmin(np.array([np.min(points, axis=0), self.origin]), axis=0)
|
|
70
84
|
self.maximum = np.nanmax(np.array([np.max(points, axis=0), self.maximum]), axis=0)
|
|
85
|
+
# add a small buffer 10% of current length to the origin and maximum
|
|
86
|
+
self.origin = self.origin - 0.1 * (self.maximum - self.origin)
|
|
87
|
+
self.maximum = self.maximum + 0.1 * (self.maximum - self.origin)
|
|
71
88
|
self.origin[self.origin < self.minimum_origin] = self.minimum_origin[
|
|
72
89
|
self.origin < self.minimum_origin
|
|
73
90
|
]
|
|
@@ -93,144 +110,109 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
93
110
|
fault_dip_anisotropy=1.0,
|
|
94
111
|
fault_pitch=None,
|
|
95
112
|
):
|
|
96
|
-
"""
|
|
97
|
-
specified parameters
|
|
113
|
+
"""
|
|
114
|
+
Generate the required data for building a fault frame with the specified parameters.
|
|
98
115
|
|
|
99
116
|
Parameters
|
|
100
117
|
----------
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
fault_center :
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
fault
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
fault_frame_data : pandas.DataFrame
|
|
119
|
+
DataFrame containing fault frame data.
|
|
120
|
+
fault_center : array-like, optional
|
|
121
|
+
Coordinates of the fault center.
|
|
122
|
+
fault_normal_vector : array-like, optional
|
|
123
|
+
Normal vector of the fault.
|
|
124
|
+
fault_slip_vector : array-like, optional
|
|
125
|
+
Slip vector of the fault.
|
|
126
|
+
minor_axis : float, optional
|
|
127
|
+
Minor axis length of the fault.
|
|
128
|
+
major_axis : float, optional
|
|
129
|
+
Major axis length of the fault.
|
|
130
|
+
intermediate_axis : float, optional
|
|
131
|
+
Intermediate axis length of the fault.
|
|
132
|
+
w : float, default=1.0
|
|
133
|
+
Weighting factor for the fault data.
|
|
134
|
+
points : bool, default=False
|
|
135
|
+
Whether to include points in the fault data.
|
|
136
|
+
force_mesh_geometry : bool, default=False
|
|
137
|
+
Whether to force the use of mesh geometry.
|
|
138
|
+
fault_buffer : float, default=0.2
|
|
139
|
+
Buffer size around the fault.
|
|
140
|
+
fault_trace_anisotropy : float, default=1.0
|
|
141
|
+
Anisotropy factor for the fault trace.
|
|
142
|
+
fault_dip : float, default=90
|
|
143
|
+
Dip angle of the fault in degrees.
|
|
144
|
+
fault_dip_anisotropy : float, default=1.0
|
|
145
|
+
Anisotropy factor for the fault dip.
|
|
146
|
+
fault_pitch : float, optional
|
|
147
|
+
Pitch angle of the fault.
|
|
117
148
|
"""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
raise ValueError("There are no points on the fault trace")
|
|
123
|
-
# find the middle point on the fault trace if center is not provided
|
|
124
|
-
if fault_center is None:
|
|
125
|
-
trace_mask = np.logical_and(
|
|
126
|
-
fault_frame_data["coord"] == 0, fault_frame_data["val"] == 0
|
|
127
|
-
)
|
|
128
|
-
fault_center = fault_frame_data.loc[trace_mask, ["X", "Y", "Z"]].mean(axis=0).to_numpy()
|
|
129
|
-
dist = np.linalg.norm(
|
|
130
|
-
fault_center - fault_frame_data.loc[trace_mask, ["X", "Y", "Z"]].to_numpy(), axis=1
|
|
131
|
-
)
|
|
132
|
-
# make the nan points greater than the max dist 10 is arbitrary and doesn't matter
|
|
133
|
-
dist[np.isnan(dist)] = np.nanmax(dist) + 10
|
|
134
|
-
fault_center = fault_frame_data.loc[trace_mask, ["X", "Y", "Z"]].to_numpy()[
|
|
135
|
-
np.argmin(dist), :
|
|
136
|
-
]
|
|
137
|
-
# get all of the gradient data associated with the fault trace
|
|
149
|
+
fault_trace = fault_frame_data.loc[
|
|
150
|
+
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["val"] == 0),
|
|
151
|
+
["X", "Y"],
|
|
152
|
+
].to_numpy()
|
|
138
153
|
if fault_normal_vector is None:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
146
|
-
vector_data = np.vstack(
|
|
147
|
-
[
|
|
148
|
-
vector_data,
|
|
149
|
-
fault_frame_data.loc[normal_mask, ["nx", "ny", "nz"]].to_numpy(),
|
|
150
|
-
]
|
|
151
|
-
)
|
|
154
|
+
if fault_frame_data.loc[
|
|
155
|
+
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["nx"].notna())].shape[0]>0:
|
|
156
|
+
fault_normal_vector = fault_frame_data.loc[
|
|
157
|
+
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["nx"].notna()),
|
|
158
|
+
["nx", "ny", "nz"],
|
|
159
|
+
].to_numpy().mean(axis=0)
|
|
152
160
|
|
|
153
|
-
|
|
154
|
-
logger.warning(
|
|
155
|
-
f"No orientation data for fault\n\
|
|
156
|
-
Defaulting to a dip of {fault_dip}vertical fault"
|
|
157
|
-
)
|
|
158
|
-
# if the line is long enough, estimate the normal vector
|
|
159
|
-
# by finding the centre point of the line and calculating the tangnent
|
|
160
|
-
# of the two points
|
|
161
|
-
if fault_frame_data.loc[trace_mask, :].shape[0] > 3:
|
|
162
|
-
|
|
163
|
-
pts = fault_frame_data.loc[trace_mask, ["X", "Y", "Z"]].to_numpy()
|
|
164
|
-
dist = np.abs(np.linalg.norm(fault_center - pts, axis=1))
|
|
165
|
-
# any nans just make them max distance + a bit
|
|
166
|
-
dist[np.isnan(dist)] = np.nanmax(dist) + 10
|
|
167
|
-
# idx = np.argsort(dist)
|
|
168
|
-
# direction_vector = pts[idx[-1]] - pts[idx[-2]]
|
|
169
|
-
# coefficients = np.polyfit(
|
|
170
|
-
# fault_frame_data.loc[trace_mask, "X"],
|
|
171
|
-
# fault_frame_data.loc[trace_mask, "Y"],
|
|
172
|
-
# 1,
|
|
173
|
-
# )
|
|
174
|
-
# slope, intercept = coefficients
|
|
175
|
-
slope, intercept = np.polyfit(
|
|
176
|
-
pts[dist < 0.25 * np.nanmax(dist), 0],
|
|
177
|
-
pts[dist < 0.25 * np.nanmax(dist), 1],
|
|
178
|
-
1,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
# # Create a direction vector using the slope
|
|
182
|
-
direction_vector = np.array([1, slope, 0])
|
|
183
|
-
direction_vector /= np.linalg.norm(direction_vector)
|
|
184
|
-
rotation_matrix = rotation(direction_vector[None, :], [90 - fault_dip])
|
|
185
|
-
vector_data = np.array(
|
|
186
|
-
[
|
|
187
|
-
[
|
|
188
|
-
direction_vector[1],
|
|
189
|
-
-direction_vector[0],
|
|
190
|
-
0,
|
|
191
|
-
]
|
|
192
|
-
]
|
|
193
|
-
)
|
|
194
|
-
vector_data /= np.linalg.norm(vector_data, axis=1)
|
|
195
|
-
vector_data = np.einsum("ijk,ik->ij", rotation_matrix, vector_data)
|
|
196
|
-
|
|
197
|
-
vector_data /= np.linalg.norm(vector_data, axis=1)
|
|
198
|
-
fault_normal_vector = np.mean(vector_data, axis=0)
|
|
161
|
+
else:
|
|
199
162
|
|
|
200
|
-
|
|
163
|
+
# Calculate fault strike using eigenvectors
|
|
164
|
+
pts = fault_trace - fault_trace.mean(axis=0)
|
|
165
|
+
# Calculate covariance matrix
|
|
166
|
+
cov_matrix = pts.T @ pts
|
|
167
|
+
# Get eigenvectors and eigenvalues
|
|
168
|
+
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
|
|
169
|
+
# Use eigenvector with largest eigenvalue as strike direction
|
|
170
|
+
strike_vector = eigenvectors[:, np.argmax(eigenvalues)]
|
|
171
|
+
strike_vector = np.append(strike_vector, 0) # Add z component
|
|
172
|
+
strike_vector /= np.linalg.norm(strike_vector)
|
|
173
|
+
|
|
174
|
+
fault_normal_vector = np.cross(strike_vector, [0, 0, 1])
|
|
175
|
+
# Rotate the fault normal vector according to the fault dip
|
|
176
|
+
rotation_matrix = rotation(strike_vector[None, :], np.array([90 - fault_dip]))
|
|
177
|
+
fault_normal_vector = np.einsum("ijk,ik->ij", rotation_matrix, fault_normal_vector[None, :])[0]
|
|
178
|
+
|
|
179
|
+
if not isinstance(fault_normal_vector, np.ndarray):
|
|
180
|
+
fault_normal_vector = np.array(fault_normal_vector)
|
|
181
|
+
|
|
182
|
+
if fault_pitch is not None:
|
|
183
|
+
rotation_matrix = rotation(fault_normal_vector[None, :], np.array([fault_pitch]))
|
|
184
|
+
fault_slip_vector = np.einsum("ijk,ik->ij", rotation_matrix, fault_normal_vector[None, :])[0]
|
|
201
185
|
|
|
202
|
-
# estimate the fault slip vector
|
|
203
186
|
if fault_slip_vector is None:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
logger.warning(
|
|
211
|
-
"There is no slip vector data for the fault, using vertical slip vector\n\
|
|
212
|
-
projected onto fault surface estimating from fault normal"
|
|
213
|
-
)
|
|
214
|
-
strike_vector, dip_vector = get_vectors(fault_normal_vector[None, :])
|
|
215
|
-
fault_slip_vector = dip_vector[:, 0]
|
|
216
|
-
if fault_pitch is not None:
|
|
217
|
-
print('using pitch')
|
|
218
|
-
rotm = rotation(fault_normal_vector[None,:],[fault_pitch])
|
|
219
|
-
print(rotm.shape,fault_slip_vector.shape)
|
|
220
|
-
fault_slip_vector = np.einsum("ijk,k->ij", rotm, fault_slip_vector)[0,:]
|
|
221
|
-
logger.info(f"Estimated fault slip vector: {fault_slip_vector}")
|
|
222
|
-
else:
|
|
223
|
-
fault_slip_vector = fault_slip_data.mean(axis=0).to_numpy()
|
|
187
|
+
if fault_frame_data.loc[
|
|
188
|
+
np.logical_and(fault_frame_data["coord"] == 1, fault_frame_data["nx"].notna())].shape[0]>0:
|
|
189
|
+
fault_slip_vector = fault_frame_data.loc[
|
|
190
|
+
np.logical_and(fault_frame_data["coord"] == 1, fault_frame_data["nx"].notna()),
|
|
191
|
+
["nx", "ny", "nz"],
|
|
192
|
+
].to_numpy().mean(axis=0)
|
|
224
193
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
194
|
+
else:
|
|
195
|
+
fault_slip_vector = np.cross(fault_normal_vector, [1., 0., 0.])
|
|
196
|
+
if np.linalg.norm(fault_slip_vector) == 0:
|
|
197
|
+
fault_slip_vector = np.cross(fault_normal_vector, [0., 1., 0.])
|
|
198
|
+
fault_slip_vector /= np.linalg.norm(fault_slip_vector)
|
|
199
|
+
if fault_center is None:
|
|
230
200
|
fault_trace = fault_frame_data.loc[
|
|
231
201
|
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["val"] == 0),
|
|
232
202
|
["X", "Y"],
|
|
233
203
|
].to_numpy()
|
|
204
|
+
fault_center = fault_trace.mean(axis=0)
|
|
205
|
+
fault_center = np.array([fault_center[0], fault_center[1], 0.0])
|
|
206
|
+
if not isinstance(fault_center, np.ndarray):
|
|
207
|
+
fault_center = np.array(fault_center)
|
|
208
|
+
if fault_center.shape[0] != 3:
|
|
209
|
+
raise ValueError("fault_center must be a 3 element array")
|
|
210
|
+
self.fault_normal_vector = fault_normal_vector / np.linalg.norm(fault_normal_vector)
|
|
211
|
+
self.fault_slip_vector = fault_slip_vector / np.linalg.norm(fault_slip_vector)
|
|
212
|
+
|
|
213
|
+
self.fault_centre = fault_center
|
|
214
|
+
if major_axis is None:
|
|
215
|
+
|
|
234
216
|
distance = np.linalg.norm(fault_trace[:, None, :] - fault_trace[None, :, :], axis=2)
|
|
235
217
|
if len(distance) == 0 or np.sum(distance) == 0:
|
|
236
218
|
logger.warning("There is no fault trace for {}".format(self.name))
|
|
@@ -382,6 +364,7 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
382
364
|
0,
|
|
383
365
|
w,
|
|
384
366
|
]
|
|
367
|
+
|
|
385
368
|
if major_axis is not None:
|
|
386
369
|
fault_tips[0, :] = fault_center[:3] + strike_vector * 0.5 * major_axis
|
|
387
370
|
fault_tips[1, :] = fault_center[:3] - strike_vector * 0.5 * major_axis
|
|
@@ -503,16 +486,14 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
503
486
|
self.origin - length * buffer, self.maximum + length * buffer, rotation
|
|
504
487
|
)
|
|
505
488
|
|
|
506
|
-
def add_splay(self, splay,
|
|
507
|
-
if
|
|
489
|
+
def add_splay(self, splay, splay_region=None):
|
|
490
|
+
if splay_region is None:
|
|
508
491
|
|
|
509
|
-
def
|
|
492
|
+
def default_splay_region(xyz):
|
|
510
493
|
pts = (
|
|
511
|
-
self.builders[0].data[
|
|
494
|
+
self.builders[0].data["X", "Y", "Z", "val"].to_numpy()
|
|
512
495
|
) # get_value_constraints()
|
|
513
496
|
pts = pts[pts[:, 3] == 0, :]
|
|
514
|
-
# check whether the fault is on the hanging wall or footwall of splay fault
|
|
515
|
-
|
|
516
497
|
ext_field = splay[2].evaluate_value(pts[:, :3])
|
|
517
498
|
surf_field = splay[0].evaluate_value(pts[:, :3])
|
|
518
499
|
intersection_value = ext_field[np.nanargmin(np.abs(surf_field))]
|
|
@@ -531,19 +512,24 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
531
512
|
)
|
|
532
513
|
return mask
|
|
533
514
|
|
|
515
|
+
splay_region = default_splay_region
|
|
516
|
+
|
|
534
517
|
scalefactor = splay.fault_major_axis / self.fault_major_axis
|
|
535
|
-
self.builders[0].add_equality_constraints(splay,
|
|
536
|
-
return
|
|
518
|
+
self.builders[0].add_equality_constraints(splay, splay_region, scalefactor)
|
|
519
|
+
return splay_region
|
|
537
520
|
|
|
538
521
|
def add_fault_trace_anisotropy(self, w: float = 1.0):
|
|
539
|
-
"""
|
|
522
|
+
"""
|
|
523
|
+
Add fault trace anisotropy to the model.
|
|
540
524
|
|
|
541
525
|
Parameters
|
|
542
526
|
----------
|
|
543
527
|
w : float, optional
|
|
544
|
-
|
|
528
|
+
Weighting factor for the anisotropy, by default 1.0
|
|
545
529
|
"""
|
|
546
530
|
if w > 0:
|
|
531
|
+
if self.fault_normal_vector is None:
|
|
532
|
+
raise ValueError("fault_normal_vector must be initialized before adding anisotropy.")
|
|
547
533
|
|
|
548
534
|
plane = np.array([0, 0, 1])
|
|
549
535
|
strike_vector = (
|
|
@@ -553,24 +539,25 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
553
539
|
strike_vector = np.array([strike_vector[1], -strike_vector[0], 0])
|
|
554
540
|
|
|
555
541
|
anisotropy_feature = AnalyticalGeologicalFeature(
|
|
556
|
-
vector=strike_vector, origin=[0, 0, 0], name="fault_trace_anisotropy"
|
|
542
|
+
vector=strike_vector, origin=np.array([0, 0, 0]), name="fault_trace_anisotropy"
|
|
557
543
|
)
|
|
558
|
-
# print('adding fault trace anisotropy')
|
|
559
544
|
self.builders[0].add_orthogonal_feature(
|
|
560
545
|
anisotropy_feature, w=w, region=None, step=1, B=0
|
|
561
546
|
)
|
|
562
547
|
|
|
563
548
|
def add_fault_dip_anisotropy(self, w: float = 1.0):
|
|
564
|
-
"""
|
|
549
|
+
"""
|
|
550
|
+
Add fault dip anisotropy to the model.
|
|
565
551
|
|
|
566
552
|
Parameters
|
|
567
553
|
----------
|
|
568
|
-
dip : np.ndarray
|
|
569
|
-
_description_
|
|
570
554
|
w : float, optional
|
|
571
|
-
|
|
555
|
+
Weighting factor for the anisotropy, by default 1.0
|
|
572
556
|
"""
|
|
573
557
|
if w > 0:
|
|
558
|
+
if self.fault_normal_vector is None:
|
|
559
|
+
raise ValueError("fault_normal_vector must be initialized before adding anisotropy.")
|
|
560
|
+
|
|
574
561
|
plane = np.array([0, 0, 1])
|
|
575
562
|
strike_vector = (
|
|
576
563
|
self.fault_normal_vector - np.dot(self.fault_normal_vector, plane) * plane
|
|
@@ -579,11 +566,11 @@ class FaultBuilder(StructuralFrameBuilder):
|
|
|
579
566
|
strike_vector = np.array([strike_vector[1], -strike_vector[0], 0])
|
|
580
567
|
|
|
581
568
|
dip_vector = np.cross(strike_vector, self.fault_normal_vector)
|
|
569
|
+
dip_vector /= np.linalg.norm(dip_vector)
|
|
582
570
|
|
|
583
571
|
anisotropy_feature = AnalyticalGeologicalFeature(
|
|
584
|
-
vector=dip_vector, origin=[0, 0, 0], name="fault_dip_anisotropy"
|
|
572
|
+
vector=dip_vector, origin=np.array([0, 0, 0]), name="fault_dip_anisotropy"
|
|
585
573
|
)
|
|
586
|
-
# print(f'adding fault dip anisotropy {anisotropy_feature.name}')
|
|
587
574
|
self.builders[0].add_orthogonal_feature(
|
|
588
575
|
anisotropy_feature, w=w, region=None, step=1, B=0
|
|
589
576
|
)
|
|
@@ -10,8 +10,10 @@ logger = getLogger(__name__)
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def smooth_peak(x):
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
v = np.zeros(x.shape)
|
|
14
|
+
mask = np.logical_and(x >= -1, x <= 1)
|
|
15
|
+
v[mask] = 0.25 * x[mask] ** 2 + 0.5 * x[mask] ** 4 - 1.75 * x[mask] ** 2 + 1
|
|
16
|
+
return v
|
|
15
17
|
|
|
16
18
|
class FaultProfileFunction(metaclass=ABCMeta):
|
|
17
19
|
def __init__(self):
|
|
@@ -467,7 +467,7 @@ class FaultSegment(StructuralFrame):
|
|
|
467
467
|
)
|
|
468
468
|
self.abut[abutting_fault_feature.name] = abutting_region
|
|
469
469
|
self.__getitem__(0).add_region(abutting_region)
|
|
470
|
-
|
|
470
|
+
return abutting_region
|
|
471
471
|
def save(self, filename, scalar_field=True, slip_vector=True, surface=True):
|
|
472
472
|
"""
|
|
473
473
|
Save the fault to a file
|
LoopStructural/utils/_surface.py
CHANGED
|
@@ -145,7 +145,7 @@ class LoopIsosurfacer:
|
|
|
145
145
|
values = np.zeros(verts.shape[0]) + isovalue
|
|
146
146
|
# need to add both global and local origin. If the bb is a buffer the local
|
|
147
147
|
# origin may not be 0
|
|
148
|
-
verts += self.bounding_box.global_origin
|
|
148
|
+
verts += self.bounding_box.global_origin+self.bounding_box.origin
|
|
149
149
|
surfaces.append(
|
|
150
150
|
Surface(
|
|
151
151
|
vertices=verts,
|
LoopStructural/utils/regions.py
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Tuple
|
|
2
4
|
|
|
5
|
+
class BaseRegion(ABC):
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def __init__(self, feature, vector=None, point=None):
|
|
8
|
+
self.feature = feature
|
|
9
|
+
self.vector = vector
|
|
10
|
+
self.point = point
|
|
11
|
+
self.name = None
|
|
12
|
+
self.parent = None
|
|
3
13
|
|
|
4
|
-
|
|
5
|
-
def
|
|
6
|
-
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def __call__(self, xyz) -> np.ndarray:
|
|
16
|
+
"""Evaluate the region based on the input coordinates."""
|
|
17
|
+
pass
|
|
7
18
|
|
|
8
19
|
|
|
9
20
|
class RegionEverywhere(BaseRegion):
|
|
@@ -23,19 +34,18 @@ class RegionFunction(BaseRegion):
|
|
|
23
34
|
def __call__(self, xyz):
|
|
24
35
|
return self.function(xyz)
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
class PositiveRegion:
|
|
37
|
+
class BaseSignRegion(BaseRegion):
|
|
28
38
|
"""Helper class for evaluating whether you are in the positive region of a scalar field.
|
|
29
39
|
If its outside of the support it will interpolate the average gradient at a point on the 0 isovalue
|
|
30
40
|
and calculate the distance from this. Alternatively, a point and vector can be used to save computational time
|
|
31
41
|
"""
|
|
32
42
|
|
|
33
43
|
def __init__(self, feature, vector=None, point=None):
|
|
34
|
-
|
|
35
|
-
self.
|
|
36
|
-
self.
|
|
44
|
+
super().__init__(feature, vector, point)
|
|
45
|
+
self.name = 'PositiveRegion'
|
|
46
|
+
self.parent = feature
|
|
37
47
|
|
|
38
|
-
def
|
|
48
|
+
def _calculate_value_and_distance(self, xyz)-> Tuple[np.ndarray, np.ndarray]:
|
|
39
49
|
val = self.feature.evaluate_value(xyz)
|
|
40
50
|
# find a point on/near 0 isosurface
|
|
41
51
|
if self.point is None:
|
|
@@ -52,51 +62,45 @@ class PositiveRegion:
|
|
|
52
62
|
average_gradient /= np.linalg.norm(average_gradient)
|
|
53
63
|
else:
|
|
54
64
|
average_gradient = self.vector
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
|
|
66
|
+
distance = np.einsum(
|
|
67
|
+
"ij,j->i", centre[None, :] - xyz, average_gradient.reshape(-1, 3)[0, :]
|
|
68
|
+
)
|
|
69
|
+
return val, distance
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PositiveRegion(BaseSignRegion):
|
|
73
|
+
"""Helper class for evaluating whether you are in the positive region of a scalar field.
|
|
74
|
+
If its outside of the support it will interpolate the average gradient at a point on the 0 isovalue
|
|
75
|
+
and calculate the distance from this. Alternatively, a point and vector can be used to save computational time
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, feature, vector=None, point=None):
|
|
79
|
+
super().__init__(feature, vector, point)
|
|
80
|
+
self.name = 'PositiveRegion'
|
|
81
|
+
self.parent = feature
|
|
82
|
+
|
|
83
|
+
def __call__(self, xyz) -> np.ndarray:
|
|
84
|
+
val, distance = self._calculate_value_and_distance(xyz)
|
|
59
85
|
return np.logical_or(
|
|
60
86
|
np.logical_and(~np.isnan(val), val > 0),
|
|
61
87
|
np.logical_and(np.isnan(val), distance > 0),
|
|
62
88
|
)
|
|
63
89
|
|
|
64
90
|
|
|
65
|
-
class NegativeRegion:
|
|
91
|
+
class NegativeRegion(BaseSignRegion):
|
|
66
92
|
"""Helper class for evaluating whether you are in the positive region of a scalar field.
|
|
67
93
|
If its outside of the support it will interpolate the average gradient at a point on the 0 isovalue
|
|
68
94
|
and calculate the distance from this. Alternatively, a point and vector can be used to save computational time
|
|
69
95
|
"""
|
|
70
96
|
|
|
71
97
|
def __init__(self, feature, vector=None, point=None):
|
|
72
|
-
|
|
73
|
-
self.
|
|
74
|
-
self.
|
|
98
|
+
super().__init__(feature, vector, point)
|
|
99
|
+
self.name = 'NegativeRegion'
|
|
100
|
+
self.parent = feature
|
|
75
101
|
|
|
76
|
-
def __call__(self, xyz):
|
|
77
|
-
val = self.
|
|
78
|
-
# find a point on/near 0 isosurface
|
|
79
|
-
if self.point is None:
|
|
80
|
-
mask = np.zeros(xyz.shape[0], dtype="bool")
|
|
81
|
-
mask[:] = val < 0
|
|
82
|
-
if np.sum(mask) == 0:
|
|
83
|
-
raise ValueError("Cannot find point on surface")
|
|
84
|
-
centre = xyz[mask, :][0, :]
|
|
85
|
-
else:
|
|
86
|
-
centre = self.point
|
|
87
|
-
if self.vector is None:
|
|
88
|
-
average_gradient = self.feature.evaluate_gradient(np.array([centre]))[0]
|
|
89
|
-
average_gradient[2] = 0
|
|
90
|
-
average_gradient /= np.linalg.norm(average_gradient)
|
|
91
|
-
else:
|
|
92
|
-
average_gradient = self.vector
|
|
93
|
-
distance = np.einsum("ij,j->i", xyz - centre[None, :], average_gradient)
|
|
94
|
-
# distance = ((xyz[:,0] - centre[None,0])*average_gradient[0] +
|
|
95
|
-
# (xyz[:,1] - centre[None,1])*average_gradient[1] +
|
|
96
|
-
# ( xyz[:,2] - centre[None,2])*average_gradient[2])
|
|
97
|
-
# return np.logical_or(np.logical_and(~np.isnan(val),val
|
|
98
|
-
# < 0),
|
|
99
|
-
# np.logical_and(np.isnan(val),distance>0))
|
|
102
|
+
def __call__(self, xyz) -> np.ndarray:
|
|
103
|
+
val, distance = self._calculate_value_and_distance(xyz)
|
|
100
104
|
return np.logical_or(
|
|
101
105
|
np.logical_and(~np.isnan(val), val < 0),
|
|
102
106
|
np.logical_and(np.isnan(val), distance < 0),
|
LoopStructural/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.6.
|
|
1
|
+
__version__ = "1.6.13"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
LoopStructural/__init__.py,sha256=fg_Vm1aMDYIf_CffTFopLsTx21u6deLaI7JMVpRYdOI,1378
|
|
2
|
-
LoopStructural/version.py,sha256=
|
|
2
|
+
LoopStructural/version.py,sha256=SVPBACE1uUZFOAHuNVOfFUNhVa6mv28StfR80qCbEYs,23
|
|
3
3
|
LoopStructural/datasets/__init__.py,sha256=ylb7fzJU_DyQ73LlwQos7VamqkDSGITbbnoKg7KAOmE,677
|
|
4
4
|
LoopStructural/datasets/_base.py,sha256=FB_D5ybBYHoaNbycdkpZcRffzjrrL1xp9X0k-pyob9Y,7618
|
|
5
5
|
LoopStructural/datasets/_example_models.py,sha256=Zg33IeUyh4C-lC0DRMLqCDP2IrX8L-gNV1WxJwBGjzM,113
|
|
@@ -29,7 +29,7 @@ LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv,sha256=
|
|
|
29
29
|
LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv,sha256=RysyqUAIjY6iIDUfTh11n9QUQWXB_qxKnZeN_DqNzlY,26745
|
|
30
30
|
LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv,sha256=pnSmG-wL8-kxuoHo_pgpJrfTmsZOzc8L0vxpBRh3r8A,355
|
|
31
31
|
LoopStructural/datatypes/__init__.py,sha256=lVg64DnynMm58qvYTjLrcyWH7vk2ngr9JGMo5FaiALI,160
|
|
32
|
-
LoopStructural/datatypes/_bounding_box.py,sha256=
|
|
32
|
+
LoopStructural/datatypes/_bounding_box.py,sha256=0bTujF745De-MzIch5Wzb7PD1J9j65PqHkamhLW5dZ4,18368
|
|
33
33
|
LoopStructural/datatypes/_point.py,sha256=qg3lXUA1rnu1N1cEWG0WvhvJuENfDgpEDIeYldWBaG8,7740
|
|
34
34
|
LoopStructural/datatypes/_structured_grid.py,sha256=mc-UM1Gh_BjHFItuPE4FF5wvGzJnSqF2MTx_xvrwcTk,5088
|
|
35
35
|
LoopStructural/datatypes/_surface.py,sha256=5BpPKVS4X3Kq1k3YxxAofKMgxdXhnOIcDi6NzKn2p2Q,6652
|
|
@@ -72,24 +72,24 @@ LoopStructural/modelling/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
|
|
|
72
72
|
LoopStructural/modelling/core/geological_model.py,sha256=okbEwjL8-wGKeWhvYE-1XSOE2C1nHX7M2AR__42pyAk,66464
|
|
73
73
|
LoopStructural/modelling/features/__init__.py,sha256=Vf-qd5EDBtJ1DpuXXyCcw2-wf6LWPRW5wzxDEO3vOc8,939
|
|
74
74
|
LoopStructural/modelling/features/_analytical_feature.py,sha256=U_g86LgQhYY2359rdsDqpvziYwqrWkc5EdvhJARiUWo,3597
|
|
75
|
-
LoopStructural/modelling/features/_base_geological_feature.py,sha256=
|
|
75
|
+
LoopStructural/modelling/features/_base_geological_feature.py,sha256=hx1EmDGWCE8YhypWuet6SUfpoGQCSecXj6zTF-CLRRg,12179
|
|
76
76
|
LoopStructural/modelling/features/_cross_product_geological_feature.py,sha256=GIyCHUdE6F-bse2e4puG9V2f7qRtDVfby5PRe2BboD4,3021
|
|
77
77
|
LoopStructural/modelling/features/_geological_feature.py,sha256=u6pbKj9BujX1Ijj5eVdhwGDNjrIAI16CpiAn5n8g3RY,11279
|
|
78
|
-
LoopStructural/modelling/features/_lambda_geological_feature.py,sha256=
|
|
78
|
+
LoopStructural/modelling/features/_lambda_geological_feature.py,sha256=GiB19l6v5WvvR8CitATZvCwaOfRyLuzchoXzpNupsfM,5743
|
|
79
79
|
LoopStructural/modelling/features/_projected_vector_feature.py,sha256=aifVLgn2spmK7GGlO0iHDewf1pFL-QoRzZEePTZwX1s,3017
|
|
80
80
|
LoopStructural/modelling/features/_region.py,sha256=TB4qnoTDQM2VgRjgyODN839fKe3kuRYLllJj0xnDKXo,478
|
|
81
81
|
LoopStructural/modelling/features/_structural_frame.py,sha256=e3QmNHLwuZc5PX3rLafocmBLNTclO90AXB4BRILCFC4,5044
|
|
82
82
|
LoopStructural/modelling/features/_unconformity_feature.py,sha256=2Bx0BI38YLdcNvDWuP9E1pKFN4orEUq9aC8b5xG1UVk,2362
|
|
83
83
|
LoopStructural/modelling/features/builders/__init__.py,sha256=Gqld1C-PcaXfJ8vpkWMDCmehmd3hZNYQk1knPtl59Bk,266
|
|
84
84
|
LoopStructural/modelling/features/builders/_base_builder.py,sha256=N3txGC98V08A8-k2TLdoIWgWLfblZ91kaTvciPq_QVM,3750
|
|
85
|
-
LoopStructural/modelling/features/builders/_fault_builder.py,sha256=
|
|
85
|
+
LoopStructural/modelling/features/builders/_fault_builder.py,sha256=CeQnvgDrgMIbyPV6nB0qnpY5PJG1OYTJIukRXv4df1E,25324
|
|
86
86
|
LoopStructural/modelling/features/builders/_folded_feature_builder.py,sha256=1_0BVTzcvmFl6K3_lX-jF0tiMFPmS8j6vPeSLn9MbrE,6607
|
|
87
87
|
LoopStructural/modelling/features/builders/_geological_feature_builder.py,sha256=jn2BiZlzXyWl0_TrsajpFR2wegGOpbuO5yFu2FamuYA,22014
|
|
88
88
|
LoopStructural/modelling/features/builders/_structural_frame_builder.py,sha256=ms3-fuFpDEarjzYU5W499TquOIlTwHPUibVxIypfmWY,8019
|
|
89
89
|
LoopStructural/modelling/features/fault/__init__.py,sha256=4u0KfYzmoO-ddFGo9qd9ov0gBoLqBiPAUsaw5zhEOAQ,189
|
|
90
|
-
LoopStructural/modelling/features/fault/_fault_function.py,sha256=
|
|
90
|
+
LoopStructural/modelling/features/fault/_fault_function.py,sha256=xvyoFA3dOtYilKycPotAzLkzYvy-kk-4qilhqt8Q8-8,12686
|
|
91
91
|
LoopStructural/modelling/features/fault/_fault_function_feature.py,sha256=4m0jVNx7ewrVI0pECI1wNciv8Cy8FzhZrYDjKJ_e2GU,2558
|
|
92
|
-
LoopStructural/modelling/features/fault/_fault_segment.py,sha256=
|
|
92
|
+
LoopStructural/modelling/features/fault/_fault_segment.py,sha256=dNTCY0ZyC8krrL1suSnhywSE_i5V_VZ4DJ2BieirkhI,18305
|
|
93
93
|
LoopStructural/modelling/features/fold/__init__.py,sha256=pOv20yQvshZozvmO_YFw2E7Prp9DExlm855N-0SnxbQ,175
|
|
94
94
|
LoopStructural/modelling/features/fold/_fold.py,sha256=bPnnLUSiF4uoMRg8aHoOSTPRgaM0JyLoRQPu5_A-J3w,5448
|
|
95
95
|
LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py,sha256=CXLbFRQ3CrTMAcHmfdbKcmSvvLs9_6TLe0Wqi1pK2tg,892
|
|
@@ -113,7 +113,7 @@ LoopStructural/modelling/intrusions/intrusion_feature.py,sha256=ESjtikHFJQzUnowb
|
|
|
113
113
|
LoopStructural/modelling/intrusions/intrusion_frame_builder.py,sha256=Q1TPHxREcrO7Rw71nUfACZHfYnISLjqlgkUNTPT324k,40143
|
|
114
114
|
LoopStructural/modelling/intrusions/intrusion_support_functions.py,sha256=wodakheMD62WJyoKnyX8UO-C1pje0I-5kHQEoDqShzo,13951
|
|
115
115
|
LoopStructural/utils/__init__.py,sha256=OJqNSu40SYJeC26IhoBBXDqQOogWjMGA-YokKVRrwMs,924
|
|
116
|
-
LoopStructural/utils/_surface.py,sha256=
|
|
116
|
+
LoopStructural/utils/_surface.py,sha256=Eg7x1GGfELl7bPe21_wU96Dn4JWJNReEFxwq-aIV4A4,6165
|
|
117
117
|
LoopStructural/utils/_transformation.py,sha256=peuLPH3BJ5DxnPbOuNKcqK4eXhAXdbT540L1OIsO3v0,5404
|
|
118
118
|
LoopStructural/utils/colours.py,sha256=-KRf1MXKx4L8TXnwyiunmKAX4tfy0qG68fRadyfn_bM,1163
|
|
119
119
|
LoopStructural/utils/config.py,sha256=ITGOtZTo2_QBwXkG_0AFANfE90J9siCXLzxypVmg9QA,414
|
|
@@ -125,12 +125,12 @@ LoopStructural/utils/json_encoder.py,sha256=5YNouf1TlhjEqOYgthd07MRXc0JLgxern-ny
|
|
|
125
125
|
LoopStructural/utils/linalg.py,sha256=tBXyu6NXcG2AcPuzUMnkVI4ncZWtE_MPHGj2PLXRwfY,123
|
|
126
126
|
LoopStructural/utils/logging.py,sha256=dIUWEsS2lT4G1dsf4ZYXknTR7eQkrgvGA4b_E0vMIRU,2402
|
|
127
127
|
LoopStructural/utils/maths.py,sha256=8iqdQdB2-bf14SzIzfFxvjWbzmPknqK9DI7CWEcW6XU,8402
|
|
128
|
-
LoopStructural/utils/regions.py,sha256=
|
|
128
|
+
LoopStructural/utils/regions.py,sha256=SjCC40GI7_n03G4mlcmvyrBgJFbxnvB3leBzXWco37o,3891
|
|
129
129
|
LoopStructural/utils/typing.py,sha256=29uVSTZdzXXH-jdlaYyBWZ1gQ2-nlZ2-XoVgG_PXNFY,157
|
|
130
130
|
LoopStructural/utils/utils.py,sha256=2Z4zVE6G752-SPmM29zebk82bROJxEwi_YiiJjcVED4,2438
|
|
131
131
|
LoopStructural/visualisation/__init__.py,sha256=5BDgKor8-ae6DrS7IZybJ3Wq_pTnCchxuY4EgzA7v1M,318
|
|
132
|
-
loopstructural-1.6.
|
|
133
|
-
loopstructural-1.6.
|
|
134
|
-
loopstructural-1.6.
|
|
135
|
-
loopstructural-1.6.
|
|
136
|
-
loopstructural-1.6.
|
|
132
|
+
loopstructural-1.6.13.dist-info/licenses/LICENSE,sha256=ZqGeNFOgmYevj7Ld7Q-kR4lAxWXuBRUdUmPC6XM_py8,1071
|
|
133
|
+
loopstructural-1.6.13.dist-info/METADATA,sha256=EwFSrenvuB1yMD-jpnkD6tmiESkC-xgJVTo7-lcmL3I,6454
|
|
134
|
+
loopstructural-1.6.13.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
|
|
135
|
+
loopstructural-1.6.13.dist-info/top_level.txt,sha256=QtQErKzYHfg6ddxTQ1NyaTxXBVM6qAqrM_vxEPyXZLg,15
|
|
136
|
+
loopstructural-1.6.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|