LoopStructural 1.6.1__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.

Files changed (129) hide show
  1. LoopStructural/__init__.py +52 -0
  2. LoopStructural/datasets/__init__.py +23 -0
  3. LoopStructural/datasets/_base.py +301 -0
  4. LoopStructural/datasets/_example_models.py +10 -0
  5. LoopStructural/datasets/data/claudius.csv +21049 -0
  6. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  7. LoopStructural/datasets/data/duplex.csv +126 -0
  8. LoopStructural/datasets/data/duplexbb.txt +2 -0
  9. LoopStructural/datasets/data/fault_trace/fault_trace.cpg +1 -0
  10. LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  11. LoopStructural/datasets/data/fault_trace/fault_trace.prj +1 -0
  12. LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  13. LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  14. LoopStructural/datasets/data/geological_map_data/bbox.csv +2 -0
  15. LoopStructural/datasets/data/geological_map_data/contacts.csv +657 -0
  16. LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +7 -0
  17. LoopStructural/datasets/data/geological_map_data/fault_edges.txt +2 -0
  18. LoopStructural/datasets/data/geological_map_data/fault_locations.csv +79 -0
  19. LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +19 -0
  20. LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +13 -0
  21. LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +207 -0
  22. LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +13 -0
  23. LoopStructural/datasets/data/intrusion.csv +1017 -0
  24. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  25. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  26. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  27. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  28. LoopStructural/datasets/data/refolded_fold.csv +205 -0
  29. LoopStructural/datasets/data/tabular_intrusion.csv +23 -0
  30. LoopStructural/datatypes/__init__.py +4 -0
  31. LoopStructural/datatypes/_bounding_box.py +422 -0
  32. LoopStructural/datatypes/_point.py +166 -0
  33. LoopStructural/datatypes/_structured_grid.py +94 -0
  34. LoopStructural/datatypes/_surface.py +184 -0
  35. LoopStructural/export/exporters.py +554 -0
  36. LoopStructural/export/file_formats.py +15 -0
  37. LoopStructural/export/geoh5.py +100 -0
  38. LoopStructural/export/gocad.py +126 -0
  39. LoopStructural/export/omf_wrapper.py +88 -0
  40. LoopStructural/interpolators/__init__.py +105 -0
  41. LoopStructural/interpolators/_api.py +143 -0
  42. LoopStructural/interpolators/_builders.py +149 -0
  43. LoopStructural/interpolators/_cython/__init__.py +0 -0
  44. LoopStructural/interpolators/_discrete_fold_interpolator.py +183 -0
  45. LoopStructural/interpolators/_discrete_interpolator.py +692 -0
  46. LoopStructural/interpolators/_finite_difference_interpolator.py +470 -0
  47. LoopStructural/interpolators/_geological_interpolator.py +380 -0
  48. LoopStructural/interpolators/_interpolator_factory.py +89 -0
  49. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  50. LoopStructural/interpolators/_operator.py +38 -0
  51. LoopStructural/interpolators/_p1interpolator.py +228 -0
  52. LoopStructural/interpolators/_p2interpolator.py +277 -0
  53. LoopStructural/interpolators/_surfe_wrapper.py +174 -0
  54. LoopStructural/interpolators/supports/_2d_base_unstructured.py +340 -0
  55. LoopStructural/interpolators/supports/_2d_p1_unstructured.py +68 -0
  56. LoopStructural/interpolators/supports/_2d_p2_unstructured.py +288 -0
  57. LoopStructural/interpolators/supports/_2d_structured_grid.py +462 -0
  58. LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  59. LoopStructural/interpolators/supports/_3d_base_structured.py +467 -0
  60. LoopStructural/interpolators/supports/_3d_p2_tetra.py +331 -0
  61. LoopStructural/interpolators/supports/_3d_structured_grid.py +470 -0
  62. LoopStructural/interpolators/supports/_3d_structured_tetra.py +746 -0
  63. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +637 -0
  64. LoopStructural/interpolators/supports/__init__.py +55 -0
  65. LoopStructural/interpolators/supports/_aabb.py +77 -0
  66. LoopStructural/interpolators/supports/_base_support.py +114 -0
  67. LoopStructural/interpolators/supports/_face_table.py +70 -0
  68. LoopStructural/interpolators/supports/_support_factory.py +32 -0
  69. LoopStructural/modelling/__init__.py +29 -0
  70. LoopStructural/modelling/core/__init__.py +0 -0
  71. LoopStructural/modelling/core/geological_model.py +1867 -0
  72. LoopStructural/modelling/features/__init__.py +32 -0
  73. LoopStructural/modelling/features/_analytical_feature.py +79 -0
  74. LoopStructural/modelling/features/_base_geological_feature.py +364 -0
  75. LoopStructural/modelling/features/_cross_product_geological_feature.py +100 -0
  76. LoopStructural/modelling/features/_geological_feature.py +288 -0
  77. LoopStructural/modelling/features/_lambda_geological_feature.py +93 -0
  78. LoopStructural/modelling/features/_region.py +18 -0
  79. LoopStructural/modelling/features/_structural_frame.py +186 -0
  80. LoopStructural/modelling/features/_unconformity_feature.py +83 -0
  81. LoopStructural/modelling/features/builders/__init__.py +5 -0
  82. LoopStructural/modelling/features/builders/_base_builder.py +111 -0
  83. LoopStructural/modelling/features/builders/_fault_builder.py +590 -0
  84. LoopStructural/modelling/features/builders/_folded_feature_builder.py +129 -0
  85. LoopStructural/modelling/features/builders/_geological_feature_builder.py +543 -0
  86. LoopStructural/modelling/features/builders/_structural_frame_builder.py +237 -0
  87. LoopStructural/modelling/features/fault/__init__.py +3 -0
  88. LoopStructural/modelling/features/fault/_fault_function.py +444 -0
  89. LoopStructural/modelling/features/fault/_fault_function_feature.py +82 -0
  90. LoopStructural/modelling/features/fault/_fault_segment.py +505 -0
  91. LoopStructural/modelling/features/fold/__init__.py +9 -0
  92. LoopStructural/modelling/features/fold/_fold.py +167 -0
  93. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +149 -0
  94. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +67 -0
  95. LoopStructural/modelling/features/fold/_foldframe.py +194 -0
  96. LoopStructural/modelling/features/fold/_svariogram.py +188 -0
  97. LoopStructural/modelling/input/__init__.py +2 -0
  98. LoopStructural/modelling/input/fault_network.py +80 -0
  99. LoopStructural/modelling/input/map2loop_processor.py +165 -0
  100. LoopStructural/modelling/input/process_data.py +650 -0
  101. LoopStructural/modelling/input/project_file.py +84 -0
  102. LoopStructural/modelling/intrusions/__init__.py +25 -0
  103. LoopStructural/modelling/intrusions/geom_conceptual_models.py +142 -0
  104. LoopStructural/modelling/intrusions/geometric_scaling_functions.py +123 -0
  105. LoopStructural/modelling/intrusions/intrusion_builder.py +672 -0
  106. LoopStructural/modelling/intrusions/intrusion_feature.py +410 -0
  107. LoopStructural/modelling/intrusions/intrusion_frame_builder.py +971 -0
  108. LoopStructural/modelling/intrusions/intrusion_support_functions.py +460 -0
  109. LoopStructural/utils/__init__.py +38 -0
  110. LoopStructural/utils/_surface.py +143 -0
  111. LoopStructural/utils/_transformation.py +76 -0
  112. LoopStructural/utils/config.py +18 -0
  113. LoopStructural/utils/dtm_creator.py +17 -0
  114. LoopStructural/utils/exceptions.py +31 -0
  115. LoopStructural/utils/helper.py +292 -0
  116. LoopStructural/utils/json_encoder.py +18 -0
  117. LoopStructural/utils/linalg.py +8 -0
  118. LoopStructural/utils/logging.py +79 -0
  119. LoopStructural/utils/maths.py +245 -0
  120. LoopStructural/utils/regions.py +103 -0
  121. LoopStructural/utils/typing.py +7 -0
  122. LoopStructural/utils/utils.py +68 -0
  123. LoopStructural/version.py +1 -0
  124. LoopStructural/visualisation/__init__.py +11 -0
  125. LoopStructural-1.6.1.dist-info/LICENSE +21 -0
  126. LoopStructural-1.6.1.dist-info/METADATA +81 -0
  127. LoopStructural-1.6.1.dist-info/RECORD +129 -0
  128. LoopStructural-1.6.1.dist-info/WHEEL +5 -0
  129. LoopStructural-1.6.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,470 @@
1
+ """
2
+ FiniteDifference interpolator
3
+ """
4
+
5
+ import numpy as np
6
+
7
+ from ..utils import get_vectors
8
+ from ._discrete_interpolator import DiscreteInterpolator
9
+ from ..interpolators import InterpolatorType
10
+
11
+ from ._operator import Operator
12
+
13
+ from LoopStructural.utils import getLogger
14
+
15
+ logger = getLogger(__name__)
16
+
17
+
18
+ class FiniteDifferenceInterpolator(DiscreteInterpolator):
19
+ def __init__(self, grid, data={}):
20
+ """
21
+ Finite difference interpolation on a regular cartesian grid
22
+
23
+ Parameters
24
+ ----------
25
+ grid : StructuredGrid
26
+ """
27
+ self.shape = "rectangular"
28
+ DiscreteInterpolator.__init__(self, grid, data=data)
29
+ # default weights for the interpolation matrix are 1 in x,y,z and
30
+ # 1/
31
+ self.set_interpolation_weights(
32
+ {
33
+ "dxy": 0.7,
34
+ "dyz": 0.7,
35
+ "dxz": 0.7,
36
+ "dxx": 1.0,
37
+ "dyy": 1.0,
38
+ "dzz": 1.0,
39
+ "dx": 1.0,
40
+ "dy": 1.0,
41
+ "dz": 1.0,
42
+ "cpw": 1.0,
43
+ "gpw": 1.0,
44
+ "npw": 1.0,
45
+ "tpw": 1.0,
46
+ "ipw": 1.0,
47
+ }
48
+ )
49
+
50
+ # grid.step_vector[2]
51
+ self.type = InterpolatorType.FINITE_DIFFERENCE
52
+
53
+ def setup_interpolator(self, **kwargs):
54
+ """
55
+
56
+ Parameters
57
+ ----------
58
+ kwargs
59
+ possible kwargs are weights for the different masks and masks.
60
+
61
+ Notes
62
+ -----
63
+ Default masks are the second derivative in x,y,z direction and the second
64
+ derivative of x wrt y and y wrt z and z wrt x. Custom masks can be used
65
+ by specifying the operator as a 3d numpy array
66
+ e.g. [ [ [ 0 0 0 ]
67
+ [ 0 1 0 ]
68
+ [ 0 0 0 ] ]
69
+ [ [ 1 1 1 ]
70
+ [ 1 1 1 ]
71
+ [ 1 1 1 ] ]
72
+ [ [ 0 0 0 ]
73
+ [ 0 1 0 ]
74
+ [ 0 0 0 ] ]
75
+
76
+ Returns
77
+ -------
78
+
79
+ """
80
+ self.reset()
81
+ for key in kwargs:
82
+ self.up_to_date = False
83
+ if "regularisation" in kwargs:
84
+ self.interpolation_weights["dxy"] = 0.1 * kwargs["regularisation"]
85
+ self.interpolation_weights["dyz"] = 0.1 * kwargs["regularisation"]
86
+ self.interpolation_weights["dxz"] = 0.1 * kwargs["regularisation"]
87
+ self.interpolation_weights["dxx"] = 0.1 * kwargs["regularisation"]
88
+ self.interpolation_weights["dyy"] = 0.1 * kwargs["regularisation"]
89
+ self.interpolation_weights["dzz"] = 0.1 * kwargs["regularisation"]
90
+ self.interpolation_weights[key] = kwargs[key]
91
+ # if we want to define the operators manually
92
+ if "operators" in kwargs:
93
+ for o in kwargs["operators"].values():
94
+ self.assemble_inner(o[0], o[1])
95
+ # otherwise just use defaults
96
+ if "operators" not in kwargs:
97
+ operator = Operator.Dxy_mask
98
+ weight = (
99
+ self.interpolation_weights["dxy"] / 4
100
+ ) # (4*self.support.step_vector[0]*self.support.step_vector[1])
101
+ self.assemble_inner(operator, weight)
102
+ operator = Operator.Dyz_mask
103
+ weight = (
104
+ self.interpolation_weights["dyz"] / 4
105
+ ) # (4*self.support.step_vector[1]*self.support.step_vector[2])
106
+ self.assemble_inner(operator, weight)
107
+ operator = Operator.Dxz_mask
108
+ weight = (
109
+ self.interpolation_weights["dxz"] / 4
110
+ ) # (4*self.support.step_vector[0]*self.support.step_vector[2])
111
+ self.assemble_inner(operator, weight)
112
+ operator = Operator.Dxx_mask
113
+ weight = self.interpolation_weights["dxx"] / 1 # self.support.step_vector[0]**2
114
+ self.assemble_inner(operator, weight)
115
+ operator = Operator.Dyy_mask
116
+ weight = self.interpolation_weights["dyy"] / 1 # self.support.step_vector[1]**2
117
+ self.assemble_inner(operator, weight)
118
+ operator = Operator.Dzz_mask
119
+ weight = self.interpolation_weights["dzz"] / 1 # self.support.step_vector[2]**2
120
+ self.assemble_inner(operator, weight)
121
+ self.add_norm_constraints(self.interpolation_weights["npw"])
122
+ self.add_gradient_constraints(self.interpolation_weights["gpw"])
123
+ self.add_value_constraints(self.interpolation_weights["cpw"])
124
+ self.add_tangent_constraints(self.interpolation_weights["tpw"])
125
+ self.add_interface_constraints(self.interpolation_weights["ipw"])
126
+ self.add_value_inequality_constraints()
127
+ self.add_inequality_pairs_constraints()
128
+
129
+ def copy(self):
130
+ """
131
+ Create a new identical interpolator
132
+
133
+ Returns
134
+ -------
135
+ returns a new empy interpolator from the same support
136
+ """
137
+ return FiniteDifferenceInterpolator(self.support)
138
+
139
+ def add_value_constraints(self, w=1.0):
140
+ """
141
+
142
+ Parameters
143
+ ----------
144
+ w : double or numpy array
145
+
146
+ Returns
147
+ -------
148
+
149
+ """
150
+
151
+ points = self.get_value_constraints()
152
+ # check that we have added some points
153
+ if points.shape[0] > 0:
154
+ node_idx, inside = self.support.position_to_cell_corners(
155
+ points[:, : self.support.dimension]
156
+ )
157
+ # print(points[inside,:].shape)
158
+ gi = np.zeros(self.support.n_nodes, dtype=int)
159
+ gi[:] = -1
160
+ gi[self.region] = np.arange(0, self.nx, dtype=int)
161
+ idc = np.zeros(node_idx.shape)
162
+ idc[:] = -1
163
+ idc[inside, :] = gi[node_idx[inside, :]]
164
+ inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
165
+ a = self.support.position_to_dof_coefs(points[inside, : self.support.dimension])
166
+ # a *= w
167
+ # a/=self.support.enp.product(self.support.step_vector)
168
+ self.add_constraints_to_least_squares(
169
+ a,
170
+ points[inside, 3],
171
+ idc[inside, :],
172
+ w=w * points[inside, 4],
173
+ name="value",
174
+ )
175
+ if np.sum(inside) <= 0:
176
+ logger.warning(
177
+ f"{np.sum(~inside)} \
178
+ value constraints not added: outside of model bounding box"
179
+ )
180
+
181
+ def add_interface_constraints(self, w=1.0):
182
+ """
183
+ Adds a constraint that defines all points
184
+ with the same 'id' to be the same value
185
+ Sets all P1-P2 = 0 for all pairs of points
186
+
187
+ Parameters
188
+ ----------
189
+ w : double
190
+ weight
191
+
192
+ Returns
193
+ -------
194
+
195
+ """
196
+ # get elements for points
197
+ points = self.get_interface_constraints()
198
+ if points.shape[0] > 1:
199
+ vertices, c, tetras, inside = self.support.get_element_for_location(
200
+ points[:, : self.support.dimension]
201
+ )
202
+ # calculate volume of tetras
203
+ # vecs = vertices[inside, 1:, :] - vertices[inside, 0, None, :]
204
+ # vol = np.abs(np.linalg.det(vecs)) / 6
205
+ A = c[inside, :]
206
+ # A *= vol[:,None]
207
+ idc = tetras[inside, :]
208
+
209
+ for unique_id in np.unique(points[np.logical_and(~np.isnan(points[:, 3]), inside), 3]):
210
+ mask = points[inside, 3] == unique_id
211
+ ij = np.array(
212
+ np.meshgrid(
213
+ np.arange(0, A[mask, :].shape[0]),
214
+ np.arange(0, A[mask, :].shape[0]),
215
+ )
216
+ ).T.reshape(-1, 2)
217
+ interface_A = np.hstack([A[mask, :][ij[:, 0], :], -A[mask, :][ij[:, 1], :]])
218
+ interface_idc = np.hstack([idc[mask, :][ij[:, 0], :], idc[mask, :][ij[:, 1], :]])
219
+
220
+ # now map the index from global to region create array size of mesh
221
+ # initialise as np.nan, then map points inside region to 0->nx
222
+ gi = np.zeros(self.support.n_nodes).astype(int)
223
+ gi[:] = -1
224
+
225
+ gi[self.region] = np.arange(0, self.nx)
226
+ interface_idc = gi[interface_idc]
227
+ outside = ~np.any(interface_idc == -1, axis=1)
228
+ self.add_constraints_to_least_squares(
229
+ interface_A[outside, :],
230
+ np.zeros(interface_A[outside, :].shape[0]),
231
+ interface_idc[outside, :],
232
+ w=w,
233
+ name="interface_{}".format(unique_id),
234
+ )
235
+
236
+ def add_gradient_constraints(self, w=1.0):
237
+ """
238
+
239
+ Parameters
240
+ ----------
241
+ w : double / numpy array
242
+
243
+ Returns
244
+ -------
245
+
246
+ """
247
+
248
+ points = self.get_gradient_constraints()
249
+ if points.shape[0] > 0:
250
+ # calculate unit vector for orientation data
251
+ # points[:,3:]/=np.linalg.norm(points[:,3:],axis=1)[:,None]
252
+
253
+ node_idx, inside = self.support.position_to_cell_corners(points[:, :3])
254
+ # calculate unit vector for node gradients
255
+ # this means we are only constraining direction of grad not the
256
+ # magnitude
257
+ gi = np.zeros(self.support.n_nodes)
258
+ gi[:] = -1
259
+ gi[self.region] = np.arange(0, self.nx)
260
+ idc = np.zeros(node_idx.shape)
261
+ idc[:] = -1
262
+ idc[inside, :] = gi[node_idx[inside, :]]
263
+ inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
264
+
265
+ (
266
+ vertices,
267
+ T,
268
+ elements,
269
+ inside_,
270
+ ) = self.support.get_element_gradient_for_location(points[inside, :3])
271
+ # normalise constraint vector and scale element matrix by this
272
+ norm = np.linalg.norm(points[:, 3:6], axis=1)
273
+ points[:, 3:6] /= norm[:, None]
274
+ T /= norm[inside, None, None]
275
+ # calculate two orthogonal vectors to constraint (strike and dip vector)
276
+ strike_vector, dip_vector = get_vectors(points[inside, 3:6])
277
+ A = np.einsum("ij,ijk->ik", strike_vector.T, T)
278
+ B = np.zeros(points[inside, :].shape[0])
279
+ self.add_constraints_to_least_squares(A, B, idc[inside, :], w=w, name="gradient")
280
+ A = np.einsum("ij,ijk->ik", dip_vector.T, T)
281
+ self.add_constraints_to_least_squares(A, B, idc[inside, :], w=w, name="gradient")
282
+ if np.sum(inside) <= 0:
283
+ logger.warning(
284
+ f" {np.sum(~inside)} \
285
+ norm constraints not added: outside of model bounding box"
286
+ )
287
+
288
+ def add_norm_constraints(self, w=1.0):
289
+ """
290
+ Add constraints to control the norm of the gradient of the scalar field
291
+
292
+ Parameters
293
+ ----------
294
+ w : double
295
+ weighting of this constraint (double)
296
+
297
+ Returns
298
+ -------
299
+
300
+ """
301
+ points = self.get_norm_constraints()
302
+ if points.shape[0] > 0:
303
+ # calculate unit vector for orientation data
304
+ # points[:,3:]/=np.linalg.norm(points[:,3:],axis=1)[:,None]
305
+ node_idx, inside = self.support.position_to_cell_corners(points[:, :3])
306
+ gi = np.zeros(self.support.n_nodes)
307
+ gi[:] = -1
308
+ gi[self.region] = np.arange(0, self.nx)
309
+ idc = np.zeros(node_idx.shape)
310
+ idc[:] = -1
311
+ idc[inside, :] = gi[node_idx[inside, :]]
312
+ inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
313
+
314
+ # calculate unit vector for node gradients
315
+ # this means we are only constraining direction of grad not the
316
+ # magnitude
317
+ (
318
+ vertices,
319
+ T,
320
+ elements,
321
+ inside_,
322
+ ) = self.support.get_element_gradient_for_location(points[inside, :3])
323
+ # T*=np.product(self.support.step_vector)
324
+ # T/=self.support.step_vector[0]
325
+ w /= 3
326
+ self.add_constraints_to_least_squares(
327
+ T[:, 0, :],
328
+ points[inside, 3],
329
+ idc[inside, :],
330
+ w=w,
331
+ name="norm",
332
+ )
333
+ self.add_constraints_to_least_squares(
334
+ T[:, 1, :],
335
+ points[inside, 4],
336
+ idc[inside, :],
337
+ w=w,
338
+ name="norm",
339
+ )
340
+ self.add_constraints_to_least_squares(
341
+ T[:, 2, :],
342
+ points[inside, 5],
343
+ idc[inside, :],
344
+ w=w,
345
+ name="norm",
346
+ )
347
+ if np.sum(inside) <= 0:
348
+ logger.warning(
349
+ f"{np.sum(~inside)} \
350
+ norm constraints not added: outside of model bounding box"
351
+ )
352
+ self.up_to_date = False
353
+
354
+ def add_gradient_orthogonal_constraints(
355
+ self,
356
+ points: np.ndarray,
357
+ vectors: np.ndarray,
358
+ w: float = 1.0,
359
+ B: float = 0,
360
+ name="gradient orthogonal",
361
+ ):
362
+ """
363
+ constraints scalar field to be orthogonal to a given vector
364
+
365
+ Parameters
366
+ ----------
367
+ points : np.darray
368
+ location to add gradient orthogonal constraint
369
+ vector : np.darray
370
+ vector to be orthogonal to, should be the same shape as points
371
+ w : double
372
+ B : np.array
373
+
374
+ Returns
375
+ -------
376
+
377
+ """
378
+ if points.shape[0] > 0:
379
+
380
+ # calculate unit vector for orientation data
381
+ node_idx, inside = self.support.position_to_cell_corners(points[:, :3])
382
+ # calculate unit vector for node gradients
383
+ # this means we are only constraining direction of grad not the
384
+ # magnitude
385
+ gi = np.zeros(self.support.n_nodes)
386
+ gi[:] = -1
387
+ gi[self.region] = np.arange(0, self.nx)
388
+ idc = np.zeros(node_idx.shape)
389
+ idc[:] = -1
390
+
391
+ idc[inside, :] = gi[node_idx[inside, :]]
392
+ inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
393
+ # normalise vector and scale element gradient matrix by norm as well
394
+ norm = np.linalg.norm(vectors, axis=1)
395
+ vectors[norm > 0, :] /= norm[norm > 0, None]
396
+
397
+ # normalise element vector to unit vector for dot product
398
+ (
399
+ vertices,
400
+ T,
401
+ elements,
402
+ inside_,
403
+ ) = self.support.get_element_gradient_for_location(points[inside, :3])
404
+ T[norm > 0, :, :] /= norm[norm > 0, None, None]
405
+
406
+ # dot product of vector and element gradient = 0
407
+ A = np.einsum("ij,ijk->ik", vectors[inside, :3], T)
408
+ B = np.zeros(points[inside, :].shape[0]) + B
409
+ self.add_constraints_to_least_squares(A, B, idc[inside, :], w=w, name=name)
410
+
411
+ if np.sum(inside) <= 0:
412
+ logger.warning(
413
+ f"{np.sum(~inside)} \
414
+ gradient constraints not added: outside of model bounding box"
415
+ )
416
+ self.up_to_date = False
417
+
418
+ def add_regularisation(self, operator, w=0.1):
419
+ """
420
+
421
+ Parameters
422
+ ----------
423
+ operator
424
+ w
425
+
426
+ Returns
427
+ -------
428
+
429
+ """
430
+ self.assemble_inner(operator, w)
431
+ # self.assemble_borders()
432
+
433
+ # def assemble_borders(self, operator, w):
434
+
435
+ def assemble_inner(self, operator, w):
436
+ """
437
+
438
+ Parameters
439
+ ----------
440
+ operator : Operator
441
+ w : double
442
+
443
+ Returns
444
+ -------
445
+
446
+ """
447
+ # First get the global indicies of the pairs of neighbours this should be an
448
+ # Nx27 array for 3d and an Nx9 array for 2d
449
+
450
+ global_indexes = self.support.neighbour_global_indexes() # np.array([ii,jj]))
451
+
452
+ a = np.tile(operator.flatten(), (global_indexes.shape[1], 1))
453
+ idc = global_indexes.T
454
+
455
+ gi = np.zeros(self.support.n_nodes)
456
+ gi[:] = -1
457
+ gi[self.region] = np.arange(0, self.nx)
458
+ idc = gi[idc]
459
+ inside = ~np.any(idc == -1, axis=1) # np.ones(a.shape[0],dtype=bool)#
460
+ # a[idc==-1] = 0
461
+ # idc[idc==-1] = 0
462
+ B = np.zeros(global_indexes.shape[1])
463
+ self.add_constraints_to_least_squares(
464
+ a[inside, :],
465
+ B[inside],
466
+ idc[inside, :],
467
+ w=w,
468
+ name="regularisation",
469
+ )
470
+ return