LoopStructural 1.0.9__cp39-cp39-win_amd64.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.
- LoopStructural/__init__.py +36 -0
- LoopStructural/analysis/__init__.py +2 -0
- LoopStructural/analysis/_fault_displacement.py +67 -0
- LoopStructural/analysis/_fault_intersection.py +36 -0
- LoopStructural/analysis/_plane_fit.py +43 -0
- LoopStructural/datasets/__init__.py +12 -0
- LoopStructural/datasets/_base.py +65 -0
- LoopStructural/datasets/data/claudius.csv +21049 -0
- LoopStructural/datasets/data/claudiusbb.txt +2 -0
- LoopStructural/datasets/data/duplex.csv +126 -0
- LoopStructural/datasets/data/duplexbb.txt +2 -0
- LoopStructural/datasets/data/intrusion.csv +1017 -0
- LoopStructural/datasets/data/intrusionbb.txt +2 -0
- LoopStructural/datasets/data/onefoldbb.txt +2 -0
- LoopStructural/datasets/data/onefolddata.csv +2226 -0
- LoopStructural/datasets/data/refolded_bb.txt +2 -0
- LoopStructural/datasets/data/refolded_fold.csv +2126 -0
- LoopStructural/interpolators/__init__.py +34 -0
- LoopStructural/interpolators/base_structured_3d_support.py +101 -0
- LoopStructural/interpolators/cython/__init__.py +0 -0
- LoopStructural/interpolators/cython/dsi_helper.c +31046 -0
- LoopStructural/interpolators/cython/dsi_helper.cp39-win_amd64.pyd +0 -0
- LoopStructural/interpolators/discrete_fold_interpolator.py +202 -0
- LoopStructural/interpolators/discrete_interpolator.py +584 -0
- LoopStructural/interpolators/finite_difference_interpolator.py +399 -0
- LoopStructural/interpolators/geological_interpolator.py +197 -0
- LoopStructural/interpolators/operator.py +61 -0
- LoopStructural/interpolators/piecewiselinear_interpolator.py +437 -0
- LoopStructural/interpolators/structured_grid.py +421 -0
- LoopStructural/interpolators/structured_tetra.py +681 -0
- LoopStructural/interpolators/surfe_wrapper.py +118 -0
- LoopStructural/modelling/__init__.py +46 -0
- LoopStructural/modelling/core/__init__.py +0 -0
- LoopStructural/modelling/core/geological_model.py +1513 -0
- LoopStructural/modelling/core/geological_model_graph.py +880 -0
- LoopStructural/modelling/core/stratigraphic_column.py +5 -0
- LoopStructural/modelling/fault/__init__.py +4 -0
- LoopStructural/modelling/fault/fault_builder.py +143 -0
- LoopStructural/modelling/fault/fault_function.py +188 -0
- LoopStructural/modelling/fault/fault_function_feature.py +76 -0
- LoopStructural/modelling/fault/fault_segment.py +298 -0
- LoopStructural/modelling/features/__init__.py +8 -0
- LoopStructural/modelling/features/cross_product_geological_feature.py +90 -0
- LoopStructural/modelling/features/geological_feature.py +252 -0
- LoopStructural/modelling/features/geological_feature_builder.py +414 -0
- LoopStructural/modelling/features/lambda_geological_feature.py +36 -0
- LoopStructural/modelling/features/region_feature.py +34 -0
- LoopStructural/modelling/features/structural_frame.py +133 -0
- LoopStructural/modelling/features/structural_frame_builder.py +206 -0
- LoopStructural/modelling/features/unconformity_feature.py +74 -0
- LoopStructural/modelling/fold/__init__.py +8 -0
- LoopStructural/modelling/fold/fold.py +143 -0
- LoopStructural/modelling/fold/fold_rotation_angle.py +133 -0
- LoopStructural/modelling/fold/fold_rotation_angle_feature.py +58 -0
- LoopStructural/modelling/fold/foldframe.py +193 -0
- LoopStructural/modelling/fold/svariogram.py +180 -0
- LoopStructural/utils/__init__.py +18 -0
- LoopStructural/utils/bounding_box.py +21 -0
- LoopStructural/utils/exceptions.py +10 -0
- LoopStructural/utils/helper.py +382 -0
- LoopStructural/utils/logging.py +60 -0
- LoopStructural/utils/map2loop.py +353 -0
- LoopStructural/utils/regions.py +11 -0
- LoopStructural/utils/utils.py +69 -0
- LoopStructural/visualisation/__init__.py +31 -0
- LoopStructural/visualisation/map_viewer.py +322 -0
- LoopStructural/visualisation/model_plotter.py +17 -0
- LoopStructural/visualisation/model_visualisation.py +1113 -0
- LoopStructural/visualisation/rotation_angle_plotter.py +83 -0
- LoopStructural/visualisation/sphinx_scraper.py +34 -0
- LoopStructural/visualisation/stratigraphic_column.py +60 -0
- LoopStructural-1.0.9.dist-info/LICENSE +21 -0
- LoopStructural-1.0.9.dist-info/METADATA +19 -0
- LoopStructural-1.0.9.dist-info/RECORD +77 -0
- LoopStructural-1.0.9.dist-info/WHEEL +5 -0
- LoopStructural-1.0.9.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tetmesh based on cartesian grid for piecewise linear interpolation
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from LoopStructural.interpolators.cython.dsi_helper import cg, constant_norm, fold_cg
|
|
8
|
+
|
|
9
|
+
from LoopStructural.utils import getLogger
|
|
10
|
+
logger = getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
class TetMesh:
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, origin = [0,0,0], nsteps = [10,10,10], step_vector = [1,1,1]):
|
|
17
|
+
self.origin = np.array(origin)
|
|
18
|
+
self.step_vector = np.array(step_vector)
|
|
19
|
+
self.nsteps = np.array(nsteps)
|
|
20
|
+
self.n_nodes = self.nsteps[0]*self.nsteps[1]*self.nsteps[2]
|
|
21
|
+
|
|
22
|
+
self.nsteps_cells = self.nsteps - 1
|
|
23
|
+
self.n_cell_x = self.nsteps[0] - 1
|
|
24
|
+
self.n_cell_y = self.nsteps[1] - 1
|
|
25
|
+
self.n_cell_z = self.nsteps[2] - 1
|
|
26
|
+
self.n_cells = self.n_cell_x * self.n_cell_y * self.n_cell_z
|
|
27
|
+
self.maximum = origin+self.nsteps*self.step_vector
|
|
28
|
+
self.tetra_mask_even = np.array([
|
|
29
|
+
[7,1,2,4],
|
|
30
|
+
[6,2,4,7],
|
|
31
|
+
[5,1,4,7],
|
|
32
|
+
[0,1,2,4],
|
|
33
|
+
[3,1,2,7]
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
self.tetra_mask = np.array([
|
|
37
|
+
[0,6,5,3],
|
|
38
|
+
[7,3,5,6],
|
|
39
|
+
[4,0,5,6],
|
|
40
|
+
[2,0,3,6],
|
|
41
|
+
[1,0,3,5]
|
|
42
|
+
])
|
|
43
|
+
self.ntetra = self.n_cells * 5
|
|
44
|
+
self.n_elements = self.ntetra
|
|
45
|
+
self.cg = None
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def nodes(self):
|
|
49
|
+
"""
|
|
50
|
+
Gets the nodes of the mesh as a property rather than using a function, accessible as a property! Python magic!
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
nodes : np.array((N,3))
|
|
55
|
+
Fortran ordered
|
|
56
|
+
"""
|
|
57
|
+
max = self.origin + self.nsteps_cells * self.step_vector
|
|
58
|
+
x = np.linspace(self.origin[0], max[0], self.nsteps[0])
|
|
59
|
+
y = np.linspace(self.origin[1], max[1], self.nsteps[1])
|
|
60
|
+
z = np.linspace(self.origin[2], max[2], self.nsteps[2])
|
|
61
|
+
xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
62
|
+
return np.array([xx.flatten(order='F'), yy.flatten(order='F'),
|
|
63
|
+
zz.flatten(order='F')]).T
|
|
64
|
+
|
|
65
|
+
def barycentre(self, elements = None):
|
|
66
|
+
"""
|
|
67
|
+
Return the barycentres of all tetrahedrons or of specified tetras using
|
|
68
|
+
global index
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
elements - numpy array
|
|
73
|
+
global index
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
if elements is None:
|
|
80
|
+
elements = np.arange(0,self.ntetra)
|
|
81
|
+
tetra = self.get_elements()[elements]
|
|
82
|
+
barycentre = np.sum(self.nodes[tetra][:, :, :],
|
|
83
|
+
axis=1) / 4.
|
|
84
|
+
return barycentre
|
|
85
|
+
|
|
86
|
+
def evaluate_value(self, pos, property_array):
|
|
87
|
+
"""
|
|
88
|
+
Evaluate value of interpolant
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
pos - numpy array
|
|
93
|
+
locations
|
|
94
|
+
prop - string
|
|
95
|
+
property name
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
values = np.zeros(pos.shape[0])
|
|
102
|
+
values[:] = np.nan
|
|
103
|
+
vertices, c, tetras, inside = self.get_tetra_for_location(pos)
|
|
104
|
+
values[inside] = np.sum(c[inside,:]*property_array[tetras[inside,:]],axis=1)
|
|
105
|
+
return values
|
|
106
|
+
|
|
107
|
+
def evaluate_gradient(self, pos, property_array):
|
|
108
|
+
"""
|
|
109
|
+
Evaluate the gradient of an interpolant at the locations
|
|
110
|
+
|
|
111
|
+
Parameters
|
|
112
|
+
----------
|
|
113
|
+
pos - numpy array
|
|
114
|
+
locations
|
|
115
|
+
prop - string
|
|
116
|
+
property to evaluate
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
values = np.zeros(pos.shape)
|
|
124
|
+
values[:] = np.nan
|
|
125
|
+
vertices, element_gradients, tetras, inside = self.get_tetra_gradient_for_location(pos)
|
|
126
|
+
#grads = np.zeros(tetras.shape)
|
|
127
|
+
values[inside,:] = (element_gradients[inside,:,:]*property_array[tetras[inside,None,:]]).sum(2)
|
|
128
|
+
length = np.sum(values[inside,:],axis=1)
|
|
129
|
+
# values[inside,:] /= length[:,None]
|
|
130
|
+
return values
|
|
131
|
+
|
|
132
|
+
def inside(self, pos):
|
|
133
|
+
inside = np.ones(pos.shape[0]).astype(bool)
|
|
134
|
+
for i in range(3):
|
|
135
|
+
inside *= pos[:, i] > self.origin[None, i]
|
|
136
|
+
inside *= pos[:, i] < self.origin[None, i] + \
|
|
137
|
+
self.step_vector[None, i] * self.nsteps_cells[None, i]
|
|
138
|
+
return inside
|
|
139
|
+
|
|
140
|
+
def get_tetra_for_location(self, pos):
|
|
141
|
+
"""
|
|
142
|
+
Determine the tetrahedron from a numpy array of points
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
pos : np.array
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
pos = np.array(pos)
|
|
155
|
+
inside = self.inside(pos)
|
|
156
|
+
# initialise array for tetrahedron vertices
|
|
157
|
+
vertices = np.zeros((5, 4, pos.shape[0], 3))
|
|
158
|
+
vertices[:] = np.nan
|
|
159
|
+
# get cell indexes
|
|
160
|
+
c_xi, c_yi, c_zi = self.position_to_cell_index(pos)
|
|
161
|
+
|
|
162
|
+
# determine if using +ve or -ve mask
|
|
163
|
+
even_mask = (c_xi + c_yi + c_zi) % 2 == 0
|
|
164
|
+
# get cell corners
|
|
165
|
+
xi, yi, zi = self.cell_corner_indexes(c_xi, c_yi, c_zi) # global_index_to_node_index(gi)
|
|
166
|
+
# convert to node locations
|
|
167
|
+
nodes = self.node_indexes_to_position(xi, yi, zi).T
|
|
168
|
+
|
|
169
|
+
vertices[:, :, even_mask, :] = nodes[:, even_mask, :][self.tetra_mask_even, :, :]
|
|
170
|
+
vertices[:, :, ~even_mask, :] = nodes[:, ~even_mask, :][self.tetra_mask, :, :]
|
|
171
|
+
# changing order to points, tetra, nodes, coord
|
|
172
|
+
vertices = vertices.swapaxes(0, 2)
|
|
173
|
+
vertices = vertices.swapaxes(1, 2)
|
|
174
|
+
# use scalar triple product to calculate barycentric coords
|
|
175
|
+
|
|
176
|
+
vap = pos[:, None, :] - vertices[:, :, 0, :]
|
|
177
|
+
vbp = pos[:, None, :] - vertices[:, :, 1, :]
|
|
178
|
+
# # vcp = p - points[:, 2, :]
|
|
179
|
+
# # vdp = p - points[:, 3, :]
|
|
180
|
+
vab = vertices[:, :, 1, :] - vertices[:, :, 0, :]
|
|
181
|
+
vac = vertices[:, :, 2, :] - vertices[:, :, 0, :]
|
|
182
|
+
vad = vertices[:, :, 3, :] - vertices[:, :, 0, :]
|
|
183
|
+
vbc = vertices[:, :, 2, :] - vertices[:, :, 1, :]
|
|
184
|
+
vbd = vertices[:, :, 3, :] - vertices[:, :, 1, :]
|
|
185
|
+
va = np.einsum('ikj, ikj->ik', vbp, np.cross(vbd, vbc, axisa=2, axisb=2)) / 6.
|
|
186
|
+
vb = np.einsum('ikj, ikj->ik', vap, np.cross(vac, vad, axisa=2, axisb=2)) / 6.
|
|
187
|
+
vc = np.einsum('ikj, ikj->ik', vap, np.cross(vad, vab, axisa=2, axisb=2)) / 6.
|
|
188
|
+
vd = np.einsum('ikj, ikj->ik', vap, np.cross(vab, vac, axisa=2, axisb=2)) / 6.
|
|
189
|
+
v = np.einsum('ikj, ikj->ik', vab, np.cross(vac, vad, axisa=2, axisb=2)) / 6.
|
|
190
|
+
c = np.zeros((va.shape[0], va.shape[1], 4))
|
|
191
|
+
c[:, :, 0] = va / v
|
|
192
|
+
c[:, :, 1] = vb / v
|
|
193
|
+
c[:, :, 2] = vc / v
|
|
194
|
+
c[:, :, 3] = vd / v
|
|
195
|
+
|
|
196
|
+
# if all coords are +ve then point is inside cell
|
|
197
|
+
mask = np.all(c > 0, axis=2)
|
|
198
|
+
|
|
199
|
+
inside = np.logical_and(inside,np.any(mask,axis=1))
|
|
200
|
+
# get cell corners
|
|
201
|
+
xi, yi, zi = self.cell_corner_indexes(c_xi, c_yi, c_zi)
|
|
202
|
+
#create mask to see which cells are even
|
|
203
|
+
even_mask = (c_xi + c_yi + c_zi) % 2 == 0
|
|
204
|
+
# create global node index list
|
|
205
|
+
# print('nsteps',self.nsteps, 'nsteps_cells', self.nsteps_cells)
|
|
206
|
+
# print('x',np.min(c_xi),np.max(c_xi),np.min(xi),np.max(xi))
|
|
207
|
+
# print('y',np.min(c_yi),np.max(c_yi),np.min(yi),np.max(yi))
|
|
208
|
+
# print('z',np.min(c_zi),np.max(c_zi),np.min(zi),np.max(zi))
|
|
209
|
+
|
|
210
|
+
gi = xi + yi * self.nsteps[0] + zi * self.nsteps[0] * self.nsteps[1]
|
|
211
|
+
# container for tetras
|
|
212
|
+
tetras = np.zeros((xi.shape[0], 5, 4)).astype(int)
|
|
213
|
+
|
|
214
|
+
tetras[even_mask, :, :] = gi[even_mask, :][:, self.tetra_mask_even]
|
|
215
|
+
tetras[~even_mask, :, :] = gi[~even_mask, :][:, self.tetra_mask]
|
|
216
|
+
inside = np.logical_and(inside,self.inside(pos))
|
|
217
|
+
vertices_return = np.zeros((pos.shape[0],4,3))
|
|
218
|
+
vertices_return[:] = np.nan
|
|
219
|
+
# set all masks not inside to False
|
|
220
|
+
mask[~inside,:] = False
|
|
221
|
+
vertices_return[inside,:,:] = vertices[mask,:,:]#[mask,:,:]#[inside,:,:]
|
|
222
|
+
c_return = np.zeros((pos.shape[0],4))
|
|
223
|
+
c_return[:] = np.nan
|
|
224
|
+
c_return[inside] = c[mask]
|
|
225
|
+
tetra_return = np.zeros((pos.shape[0],4)).astype(int)
|
|
226
|
+
tetra_return[:] = -1
|
|
227
|
+
tetra_return[inside,:] = tetras[mask,:]
|
|
228
|
+
return vertices_return, c_return, tetra_return, inside
|
|
229
|
+
|
|
230
|
+
def get_constant_gradient(self, region, direction=None):
|
|
231
|
+
"""
|
|
232
|
+
Get the constant gradient for the specified nodes
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
region : np.array(dtype=bool)
|
|
237
|
+
mask of nodes to calculate cg for
|
|
238
|
+
|
|
239
|
+
Returns
|
|
240
|
+
-------
|
|
241
|
+
|
|
242
|
+
"""
|
|
243
|
+
"""
|
|
244
|
+
Add the constant gradient regularisation to the system
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
w (double) - weighting of the cg parameter
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
if direction is not None:
|
|
255
|
+
print('using cg direction')
|
|
256
|
+
logger.info("Running constant gradient")
|
|
257
|
+
elements_gradients = self.get_element_gradients(np.arange(self.ntetra))
|
|
258
|
+
if elements_gradients.shape[0] != direction.shape[0]:
|
|
259
|
+
logger.error('Cannot add directional CG, vector field is not the correct length')
|
|
260
|
+
return
|
|
261
|
+
region = region.astype('int64')
|
|
262
|
+
|
|
263
|
+
neighbours = self.get_neighbours()
|
|
264
|
+
elements = self.get_elements()
|
|
265
|
+
idc, c, ncons = fold_cg(elements_gradients, direction, neighbours.astype('int64'), elements.astype('int64'), self.nodes)
|
|
266
|
+
|
|
267
|
+
idc = np.array(idc[:ncons, :])
|
|
268
|
+
c = np.array(c[:ncons, :])
|
|
269
|
+
B = np.zeros(c.shape[0])
|
|
270
|
+
return c, idc, B
|
|
271
|
+
if self.cg is None:
|
|
272
|
+
logger.info("Running constant gradient")
|
|
273
|
+
elements_gradients = self.get_element_gradients(np.arange(self.ntetra))
|
|
274
|
+
region = region.astype('int64')
|
|
275
|
+
|
|
276
|
+
neighbours = self.get_neighbours()
|
|
277
|
+
elements = self.get_elements()
|
|
278
|
+
idc, c, ncons = cg(elements_gradients, neighbours.astype('int64'), elements.astype('int64'), self.nodes,
|
|
279
|
+
region.astype('int64'))
|
|
280
|
+
|
|
281
|
+
idc = np.array(idc[:ncons, :])
|
|
282
|
+
c = np.array(c[:ncons, :])
|
|
283
|
+
B = np.zeros(c.shape[0])
|
|
284
|
+
self.cg = (c,idc,B)
|
|
285
|
+
return self.cg[0], self.cg[1], self.cg[2]
|
|
286
|
+
def get_constant_norm(self, region):
|
|
287
|
+
"""
|
|
288
|
+
Get the constant gradient for the specified nodes
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
region : np.array(dtype=bool)
|
|
293
|
+
mask of nodes to calculate cg for
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
logger.info("Running constant gradient")
|
|
301
|
+
elements_gradients = self.get_element_gradients(np.arange(self.ntetra))
|
|
302
|
+
region = region.astype('int64')
|
|
303
|
+
|
|
304
|
+
neighbours = self.get_neighbours()
|
|
305
|
+
elements = self.get_elements()
|
|
306
|
+
idc, c, ncons = constant_norm(elements_gradients, neighbours.astype('int64'), elements.astype('int64'), self.nodes,
|
|
307
|
+
region.astype('int64'))
|
|
308
|
+
|
|
309
|
+
idc = np.array(idc[:ncons, :])
|
|
310
|
+
c = np.array(c[:ncons, :])
|
|
311
|
+
B = np.zeros(c.shape[0])
|
|
312
|
+
|
|
313
|
+
return c,idc,B
|
|
314
|
+
def get_elements(self):
|
|
315
|
+
"""
|
|
316
|
+
Get a numpy array of all of the elements in the mesh
|
|
317
|
+
|
|
318
|
+
Returns
|
|
319
|
+
-------
|
|
320
|
+
numpy array elements
|
|
321
|
+
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
x = np.arange(0, self.n_cell_x)
|
|
325
|
+
y = np.arange(0, self.n_cell_y)
|
|
326
|
+
z = np.arange(0, self.n_cell_z)
|
|
327
|
+
|
|
328
|
+
c_xi, c_yi, c_zi = np.meshgrid(x, y, z,indexing='ij')
|
|
329
|
+
c_xi = c_xi.flatten(order='F')
|
|
330
|
+
c_yi = c_yi.flatten(order='F')
|
|
331
|
+
c_zi = c_zi.flatten(order='F')
|
|
332
|
+
# get cell corners
|
|
333
|
+
xi, yi, zi = self.cell_corner_indexes(c_xi, c_yi, c_zi)
|
|
334
|
+
even_mask = (c_xi + c_yi + c_zi) % 2 == 0
|
|
335
|
+
gi = xi + yi * self.nsteps[0] + zi * self.nsteps[0] * self.nsteps[1]
|
|
336
|
+
tetras = np.zeros((c_xi.shape[0], 5, 4)).astype('int64')
|
|
337
|
+
tetras[even_mask, :, :] = gi[even_mask, :][:, self.tetra_mask_even]
|
|
338
|
+
tetras[~even_mask, :, :] = gi[~even_mask, :][:, self.tetra_mask]
|
|
339
|
+
|
|
340
|
+
return tetras.reshape((tetras.shape[0]*tetras.shape[1],tetras.shape[2]))
|
|
341
|
+
|
|
342
|
+
def get_element_gradients(self, elements = None):
|
|
343
|
+
"""
|
|
344
|
+
Get the gradients of all tetras
|
|
345
|
+
|
|
346
|
+
Parameters
|
|
347
|
+
----------
|
|
348
|
+
elements
|
|
349
|
+
|
|
350
|
+
Returns
|
|
351
|
+
-------
|
|
352
|
+
|
|
353
|
+
"""
|
|
354
|
+
if elements is None:
|
|
355
|
+
elements = np.arange(0,self.ntetra)
|
|
356
|
+
x = np.arange(0, self.n_cell_x)
|
|
357
|
+
y = np.arange(0, self.n_cell_y)
|
|
358
|
+
z = np.arange(0, self.n_cell_z)
|
|
359
|
+
|
|
360
|
+
c_xi, c_yi, c_zi = np.meshgrid(x, y, z, indexing='ij')
|
|
361
|
+
c_xi = c_xi.flatten(order='F')
|
|
362
|
+
c_yi = c_yi.flatten(order='F')
|
|
363
|
+
c_zi = c_zi.flatten(order='F')
|
|
364
|
+
even_mask = (c_xi + c_yi + c_zi) % 2 == 0
|
|
365
|
+
# get cell corners
|
|
366
|
+
xi, yi, zi = self.cell_corner_indexes(c_xi, c_yi, c_zi) # global_index_to_node_index(gi)
|
|
367
|
+
# convert to node locations
|
|
368
|
+
nodes = self.node_indexes_to_position(xi, yi, zi).T
|
|
369
|
+
|
|
370
|
+
points = np.zeros((5, 4, self.n_cells, 3))
|
|
371
|
+
points[:, :, even_mask, :] = nodes[:, even_mask, :][self.tetra_mask_even, :, :]
|
|
372
|
+
points[:, :, ~even_mask, :] = nodes[:, ~even_mask, :][self.tetra_mask, :, :]
|
|
373
|
+
|
|
374
|
+
# changing order to points, tetra, nodes, coord
|
|
375
|
+
points = points.swapaxes(0, 2)
|
|
376
|
+
points = points.swapaxes(1, 2)
|
|
377
|
+
|
|
378
|
+
ps = points.reshape(points.shape[0] * points.shape[1], points.shape[2], points.shape[3])
|
|
379
|
+
|
|
380
|
+
m = np.array(
|
|
381
|
+
[[(ps[:, 1, 0] - ps[:, 0, 0]), (ps[:, 1, 1] - ps[:, 0, 1]),
|
|
382
|
+
(ps[:, 1, 2] - ps[:, 0, 2])],
|
|
383
|
+
[(ps[:, 2, 0] - ps[:, 0, 0]), (ps[:, 2, 1] - ps[:, 0, 1]),
|
|
384
|
+
(ps[:, 2, 2] - ps[:, 0, 2])],
|
|
385
|
+
[(ps[:, 3, 0] - ps[:, 0, 0]), (ps[:, 3, 1] - ps[:, 0, 1]),
|
|
386
|
+
(ps[:, 3, 2] - ps[:, 0, 2])]])
|
|
387
|
+
I = np.array(
|
|
388
|
+
[[-1., 1., 0., 0.],
|
|
389
|
+
[-1., 0., 1., 0.],
|
|
390
|
+
[-1., 0., 0., 1.]])
|
|
391
|
+
m = np.swapaxes(m, 0, 2)
|
|
392
|
+
element_gradients = np.linalg.inv(m)
|
|
393
|
+
|
|
394
|
+
element_gradients = element_gradients.swapaxes(1, 2)
|
|
395
|
+
element_gradients = element_gradients @ I
|
|
396
|
+
|
|
397
|
+
return element_gradients[elements,:,:]
|
|
398
|
+
|
|
399
|
+
def get_tetra_gradient_for_location(self, pos):
|
|
400
|
+
"""
|
|
401
|
+
Get the gradient of the tetra for a location
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
pos
|
|
406
|
+
|
|
407
|
+
Returns
|
|
408
|
+
-------
|
|
409
|
+
|
|
410
|
+
"""
|
|
411
|
+
vertices, bc, tetras, inside = self.get_tetra_for_location(pos)
|
|
412
|
+
ps = vertices
|
|
413
|
+
m = np.array(
|
|
414
|
+
[[(ps[:, 1, 0] - ps[:, 0, 0]), (ps[:, 1, 1] - ps[:, 0, 1]),(ps[:, 1, 2] - ps[:, 0, 2])],
|
|
415
|
+
[(ps[:, 2, 0] - ps[:, 0, 0]), (ps[:, 2, 1] - ps[:, 0, 1]),(ps[:, 2, 2] - ps[:, 0, 2])],
|
|
416
|
+
[(ps[:, 3, 0] - ps[:, 0, 0]), (ps[:, 3, 1] - ps[:, 0, 1]),(ps[:, 3, 2] - ps[:, 0, 2])]])
|
|
417
|
+
I = np.array(
|
|
418
|
+
[[-1., 1., 0., 0.],
|
|
419
|
+
[-1., 0., 1., 0.],
|
|
420
|
+
[-1., 0., 0., 1.]])
|
|
421
|
+
m = np.swapaxes(m, 0, 2)
|
|
422
|
+
element_gradients = np.linalg.inv(m)
|
|
423
|
+
|
|
424
|
+
element_gradients = element_gradients.swapaxes(1, 2)
|
|
425
|
+
element_gradients = element_gradients @ I
|
|
426
|
+
return vertices, element_gradients, tetras, inside
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def global_node_indicies(self, indexes):
|
|
431
|
+
"""
|
|
432
|
+
Convert from node indexes to global node index
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
indexes
|
|
437
|
+
|
|
438
|
+
Returns
|
|
439
|
+
-------
|
|
440
|
+
|
|
441
|
+
"""
|
|
442
|
+
indexes = np.array(indexes).swapaxes(0, 2)
|
|
443
|
+
return indexes[:, :, 0] + self.nsteps[None, None, 0] \
|
|
444
|
+
* indexes[:, :, 1] + self.nsteps[None, None, 0] * \
|
|
445
|
+
self.nsteps[None, None, 1] * indexes[:, :, 2]
|
|
446
|
+
|
|
447
|
+
def global_cell_indicies(self, indexes):
|
|
448
|
+
"""
|
|
449
|
+
Convert from cell indexes to global cell index
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
indexes
|
|
454
|
+
|
|
455
|
+
Returns
|
|
456
|
+
-------
|
|
457
|
+
|
|
458
|
+
"""
|
|
459
|
+
indexes = np.array(indexes).swapaxes(0, 2)
|
|
460
|
+
return indexes[:, :, 0] + self.nsteps_cells[None, None, 0] \
|
|
461
|
+
* indexes[:, :, 1] + self.nsteps_cells[None, None, 0] * \
|
|
462
|
+
self.nsteps_cells[None, None, 1] * indexes[:, :, 2]
|
|
463
|
+
|
|
464
|
+
def cell_corner_indexes(self, x_cell_index, y_cell_index, z_cell_index):
|
|
465
|
+
"""
|
|
466
|
+
Returns the indexes of the corners of a cell given its location xi,
|
|
467
|
+
yi, zi
|
|
468
|
+
|
|
469
|
+
Parameters
|
|
470
|
+
----------
|
|
471
|
+
x_cell_index
|
|
472
|
+
y_cell_index
|
|
473
|
+
z_cell_index
|
|
474
|
+
|
|
475
|
+
Returns
|
|
476
|
+
-------
|
|
477
|
+
|
|
478
|
+
"""
|
|
479
|
+
x_cell_index = np.array(x_cell_index)
|
|
480
|
+
y_cell_index = np.array(y_cell_index)
|
|
481
|
+
z_cell_index = np.array(z_cell_index)
|
|
482
|
+
|
|
483
|
+
xcorner = np.array([0, 1, 0, 1, 0, 1, 0, 1])
|
|
484
|
+
ycorner = np.array([0, 0, 1, 1, 0, 0, 1, 1])
|
|
485
|
+
zcorner = np.array([0, 0, 0, 0, 1, 1, 1, 1])
|
|
486
|
+
xcorners = x_cell_index[:, None] + xcorner[None, :]
|
|
487
|
+
ycorners = y_cell_index[:, None] + ycorner[None, :]
|
|
488
|
+
zcorners = z_cell_index[:, None] + zcorner[None, :]
|
|
489
|
+
return xcorners, ycorners, zcorners
|
|
490
|
+
|
|
491
|
+
def position_to_cell_corners(self, pos):
|
|
492
|
+
"""
|
|
493
|
+
Find the nodes that belong to a cell which contains a point
|
|
494
|
+
|
|
495
|
+
Parameters
|
|
496
|
+
----------
|
|
497
|
+
pos
|
|
498
|
+
|
|
499
|
+
Returns
|
|
500
|
+
-------
|
|
501
|
+
|
|
502
|
+
"""
|
|
503
|
+
inside = self.inside(pos)
|
|
504
|
+
ix, iy, iz = self.position_to_cell_index(pos)
|
|
505
|
+
cornersx, cornersy, cornersz = self.cell_corner_indexes(ix, iy, iz)
|
|
506
|
+
globalidx = self.global_cell_indicies(
|
|
507
|
+
np.dstack([cornersx, cornersy, cornersz]).T)
|
|
508
|
+
return globalidx, inside
|
|
509
|
+
|
|
510
|
+
def position_to_cell_index(self, pos):
|
|
511
|
+
"""
|
|
512
|
+
Find which cell a point is in
|
|
513
|
+
|
|
514
|
+
Parameters
|
|
515
|
+
----------
|
|
516
|
+
pos
|
|
517
|
+
|
|
518
|
+
Returns
|
|
519
|
+
-------
|
|
520
|
+
|
|
521
|
+
"""
|
|
522
|
+
ix = pos[:, 0] - self.origin[None, 0]
|
|
523
|
+
iy = pos[:, 1] - self.origin[None, 1]
|
|
524
|
+
iz = pos[:, 2] - self.origin[None, 2]
|
|
525
|
+
ix = ix // self.step_vector[None, 0]
|
|
526
|
+
iy = iy // self.step_vector[None, 1]
|
|
527
|
+
iz = iz // self.step_vector[None, 2]
|
|
528
|
+
return ix.astype(int), iy.astype(int), iz.astype(int)
|
|
529
|
+
|
|
530
|
+
def node_indexes_to_position(self, xindex, yindex, zindex):
|
|
531
|
+
"""
|
|
532
|
+
Get the xyz position from the node coordinates
|
|
533
|
+
|
|
534
|
+
Parameters
|
|
535
|
+
----------
|
|
536
|
+
xindex
|
|
537
|
+
yindex
|
|
538
|
+
zindex
|
|
539
|
+
|
|
540
|
+
Returns
|
|
541
|
+
-------
|
|
542
|
+
|
|
543
|
+
"""
|
|
544
|
+
x = self.origin[0] + self.step_vector[0] * xindex
|
|
545
|
+
y = self.origin[1] + self.step_vector[1] * yindex
|
|
546
|
+
z = self.origin[2] + self.step_vector[2] * zindex
|
|
547
|
+
|
|
548
|
+
return np.array([x, y, z])
|
|
549
|
+
|
|
550
|
+
def global_index_to_node_index(self, global_index):
|
|
551
|
+
"""
|
|
552
|
+
Convert from global indexes to xi,yi,zi
|
|
553
|
+
|
|
554
|
+
Parameters
|
|
555
|
+
----------
|
|
556
|
+
global_index
|
|
557
|
+
|
|
558
|
+
Returns
|
|
559
|
+
-------
|
|
560
|
+
|
|
561
|
+
"""
|
|
562
|
+
# determine the ijk indices for the global index.
|
|
563
|
+
# remainder when dividing by nx = i
|
|
564
|
+
# remained when dividing modulus of nx by ny is j
|
|
565
|
+
x_index = global_index % self.nsteps[0, None]
|
|
566
|
+
y_index = global_index // self.nsteps[0, None] % \
|
|
567
|
+
self.nsteps[1, None]
|
|
568
|
+
z_index = global_index // self.nsteps[0, None] // \
|
|
569
|
+
self.nsteps[1, None]
|
|
570
|
+
return x_index, y_index, z_index
|
|
571
|
+
|
|
572
|
+
def global_index_to_cell_index(self, global_index):
|
|
573
|
+
"""
|
|
574
|
+
Convert from global indexes to xi,yi,zi
|
|
575
|
+
|
|
576
|
+
Parameters
|
|
577
|
+
----------
|
|
578
|
+
global_index
|
|
579
|
+
|
|
580
|
+
Returns
|
|
581
|
+
-------
|
|
582
|
+
|
|
583
|
+
"""
|
|
584
|
+
# determine the ijk indices for the global index.
|
|
585
|
+
# remainder when dividing by nx = i
|
|
586
|
+
# remained when dividing modulus of nx by ny is j
|
|
587
|
+
|
|
588
|
+
x_index = global_index % self.nsteps_cells[0, None]
|
|
589
|
+
y_index = global_index // self.nsteps_cells[0, None] % \
|
|
590
|
+
self.nsteps_cells[1, None]
|
|
591
|
+
z_index = global_index // self.nsteps_cells[0, None] // \
|
|
592
|
+
self.nsteps_cells[1, None]
|
|
593
|
+
return x_index, y_index, z_index
|
|
594
|
+
|
|
595
|
+
def get_neighbours(self):
|
|
596
|
+
"""
|
|
597
|
+
This function goes through all of the elements in the mesh and assembles a numpy array
|
|
598
|
+
with the neighbours for each element
|
|
599
|
+
|
|
600
|
+
Returns
|
|
601
|
+
-------
|
|
602
|
+
|
|
603
|
+
"""
|
|
604
|
+
# elements = self.get_elements()
|
|
605
|
+
# neighbours = np.zeros((self.ntetra,4)).astype('int64')
|
|
606
|
+
# neighbours[:] = -1
|
|
607
|
+
# tetra_neighbours(elements,neighbours)
|
|
608
|
+
# return neighbours
|
|
609
|
+
tetra_index = np.arange(0, self.ntetra)
|
|
610
|
+
neighbours = np.zeros((self.ntetra, 4)).astype('int64')
|
|
611
|
+
neighbours[:] = -9999
|
|
612
|
+
neighbours[tetra_index%5 == 0,:] = tetra_index[tetra_index%5 == 0,None] \
|
|
613
|
+
+ np.arange(1,5)[None,:] # first tetra is the centre one so all of its neighbours are in the same cell
|
|
614
|
+
neighbours[tetra_index % 5 != 0, 0] = np.tile(
|
|
615
|
+
tetra_index[tetra_index % 5 == 0], (4, 1)).flatten(
|
|
616
|
+
order='F') # add first tetra to other neighbours
|
|
617
|
+
|
|
618
|
+
# now create masks for the different tetra indexes
|
|
619
|
+
one_mask = tetra_index % 5 == 1
|
|
620
|
+
two_mask = tetra_index % 5 == 2
|
|
621
|
+
three_mask = tetra_index % 5 == 3
|
|
622
|
+
four_mask = tetra_index % 5 == 4
|
|
623
|
+
|
|
624
|
+
# create masks for whether cell is odd or even
|
|
625
|
+
odd_mask = np.sum(self.global_index_to_cell_index(tetra_index // 5),
|
|
626
|
+
axis=0) % 2 == 1
|
|
627
|
+
odd_mask = ~odd_mask.astype(bool)
|
|
628
|
+
|
|
629
|
+
# apply masks to
|
|
630
|
+
masks = []
|
|
631
|
+
masks.append([np.logical_and(one_mask, odd_mask),
|
|
632
|
+
np.array([[-1, 0, 0, 1], [0, 1, 0, 2], [0, 0, 1, 3]])])
|
|
633
|
+
masks.append([np.logical_and(two_mask, odd_mask),
|
|
634
|
+
np.array([[1, 0, 0, 2], [0, -1, 0, 1], [0, 0, 1, 4]])])
|
|
635
|
+
masks.append([np.logical_and(three_mask, odd_mask),
|
|
636
|
+
np.array([[-1, 0, 0, 4], [0, -1, 0, 3], [0, 0, -1, 2]])])
|
|
637
|
+
masks.append([np.logical_and(four_mask, odd_mask),
|
|
638
|
+
np.array([[1, 0, 0, 3], [0, 1, 0, 4], [0, 0, -1, 1]])])
|
|
639
|
+
|
|
640
|
+
masks.append([np.logical_and(one_mask, ~odd_mask),
|
|
641
|
+
np.array([[1, 0, 0, 1], [0, 1, 0, 2], [0, 0, 1, 4]])])
|
|
642
|
+
masks.append([np.logical_and(two_mask, ~odd_mask),
|
|
643
|
+
np.array([[-1, 0, 0, 2], [0, -1, 0, 1], [0, 0, 1, 3]])])
|
|
644
|
+
masks.append([np.logical_and(three_mask, ~odd_mask),
|
|
645
|
+
np.array([[-1, 0, 0, 4], [0, 1, 0, 3], [0, 0, -1, 1]])])
|
|
646
|
+
masks.append([np.logical_and(four_mask, ~odd_mask),
|
|
647
|
+
np.array([[1, 0, 0, 3], [0, -1, 0, 4], [0, 0, -1, 2]])])
|
|
648
|
+
|
|
649
|
+
for m in masks:
|
|
650
|
+
logic = m[0]
|
|
651
|
+
mask = m[1]
|
|
652
|
+
c_xi, c_yi, c_zi = self.global_index_to_cell_index(
|
|
653
|
+
tetra_index[logic] // 5)
|
|
654
|
+
# mask = np.array([[1,0,0,4],[0,0,-1,2],[0,1,0,3],[0,0,0,0]])
|
|
655
|
+
neigh_cell = np.zeros((c_xi.shape[0], 3, 3)).astype(int)
|
|
656
|
+
neigh_cell[:, :, 0] = c_xi[:, None] + mask[:, 0]
|
|
657
|
+
neigh_cell[:, :, 1] = c_yi[:, None] + mask[:, 1]
|
|
658
|
+
neigh_cell[:, :, 2] = c_zi[:, None] + mask[:, 2]
|
|
659
|
+
inside = neigh_cell[:, :, 0] >= 0
|
|
660
|
+
inside = np.logical_and(inside, neigh_cell[:, :, 1] >= 0)
|
|
661
|
+
inside = np.logical_and(inside, neigh_cell[:, :, 2] >= 0)
|
|
662
|
+
inside = np.logical_and(inside,
|
|
663
|
+
neigh_cell[:, :, 0] < self.n_cell_x)
|
|
664
|
+
inside = np.logical_and(inside,
|
|
665
|
+
neigh_cell[:, :, 1] < self.n_cell_y)
|
|
666
|
+
inside = np.logical_and(inside,
|
|
667
|
+
neigh_cell[:, :, 2] < self.n_cell_z)
|
|
668
|
+
|
|
669
|
+
global_neighbour_idx = np.zeros((c_xi.shape[0], 4)).astype(int)
|
|
670
|
+
global_neighbour_idx[:] = -1
|
|
671
|
+
global_neighbour_idx = (neigh_cell[:, :, 0] + neigh_cell[:, :, 1] *
|
|
672
|
+
self.n_cell_x + neigh_cell[:, :, 2] *
|
|
673
|
+
self.n_cell_x * self.n_cell_y) * 5 + mask[:, 3]
|
|
674
|
+
|
|
675
|
+
global_neighbour_idx[~inside] = -1
|
|
676
|
+
neighbours[logic, 1:] = global_neighbour_idx
|
|
677
|
+
|
|
678
|
+
return neighbours
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
|