LoopStructural 1.0.9__cp36-cp36m-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.cp36-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,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
+