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.
- LoopStructural/__init__.py +52 -0
- LoopStructural/datasets/__init__.py +23 -0
- LoopStructural/datasets/_base.py +301 -0
- LoopStructural/datasets/_example_models.py +10 -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/fault_trace/fault_trace.cpg +1 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.prj +1 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
- LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
- LoopStructural/datasets/data/geological_map_data/bbox.csv +2 -0
- LoopStructural/datasets/data/geological_map_data/contacts.csv +657 -0
- LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +7 -0
- LoopStructural/datasets/data/geological_map_data/fault_edges.txt +2 -0
- LoopStructural/datasets/data/geological_map_data/fault_locations.csv +79 -0
- LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +19 -0
- LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +13 -0
- LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +207 -0
- LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +13 -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 +205 -0
- LoopStructural/datasets/data/tabular_intrusion.csv +23 -0
- LoopStructural/datatypes/__init__.py +4 -0
- LoopStructural/datatypes/_bounding_box.py +422 -0
- LoopStructural/datatypes/_point.py +166 -0
- LoopStructural/datatypes/_structured_grid.py +94 -0
- LoopStructural/datatypes/_surface.py +184 -0
- LoopStructural/export/exporters.py +554 -0
- LoopStructural/export/file_formats.py +15 -0
- LoopStructural/export/geoh5.py +100 -0
- LoopStructural/export/gocad.py +126 -0
- LoopStructural/export/omf_wrapper.py +88 -0
- LoopStructural/interpolators/__init__.py +105 -0
- LoopStructural/interpolators/_api.py +143 -0
- LoopStructural/interpolators/_builders.py +149 -0
- LoopStructural/interpolators/_cython/__init__.py +0 -0
- LoopStructural/interpolators/_discrete_fold_interpolator.py +183 -0
- LoopStructural/interpolators/_discrete_interpolator.py +692 -0
- LoopStructural/interpolators/_finite_difference_interpolator.py +470 -0
- LoopStructural/interpolators/_geological_interpolator.py +380 -0
- LoopStructural/interpolators/_interpolator_factory.py +89 -0
- LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
- LoopStructural/interpolators/_operator.py +38 -0
- LoopStructural/interpolators/_p1interpolator.py +228 -0
- LoopStructural/interpolators/_p2interpolator.py +277 -0
- LoopStructural/interpolators/_surfe_wrapper.py +174 -0
- LoopStructural/interpolators/supports/_2d_base_unstructured.py +340 -0
- LoopStructural/interpolators/supports/_2d_p1_unstructured.py +68 -0
- LoopStructural/interpolators/supports/_2d_p2_unstructured.py +288 -0
- LoopStructural/interpolators/supports/_2d_structured_grid.py +462 -0
- LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
- LoopStructural/interpolators/supports/_3d_base_structured.py +467 -0
- LoopStructural/interpolators/supports/_3d_p2_tetra.py +331 -0
- LoopStructural/interpolators/supports/_3d_structured_grid.py +470 -0
- LoopStructural/interpolators/supports/_3d_structured_tetra.py +746 -0
- LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +637 -0
- LoopStructural/interpolators/supports/__init__.py +55 -0
- LoopStructural/interpolators/supports/_aabb.py +77 -0
- LoopStructural/interpolators/supports/_base_support.py +114 -0
- LoopStructural/interpolators/supports/_face_table.py +70 -0
- LoopStructural/interpolators/supports/_support_factory.py +32 -0
- LoopStructural/modelling/__init__.py +29 -0
- LoopStructural/modelling/core/__init__.py +0 -0
- LoopStructural/modelling/core/geological_model.py +1867 -0
- LoopStructural/modelling/features/__init__.py +32 -0
- LoopStructural/modelling/features/_analytical_feature.py +79 -0
- LoopStructural/modelling/features/_base_geological_feature.py +364 -0
- LoopStructural/modelling/features/_cross_product_geological_feature.py +100 -0
- LoopStructural/modelling/features/_geological_feature.py +288 -0
- LoopStructural/modelling/features/_lambda_geological_feature.py +93 -0
- LoopStructural/modelling/features/_region.py +18 -0
- LoopStructural/modelling/features/_structural_frame.py +186 -0
- LoopStructural/modelling/features/_unconformity_feature.py +83 -0
- LoopStructural/modelling/features/builders/__init__.py +5 -0
- LoopStructural/modelling/features/builders/_base_builder.py +111 -0
- LoopStructural/modelling/features/builders/_fault_builder.py +590 -0
- LoopStructural/modelling/features/builders/_folded_feature_builder.py +129 -0
- LoopStructural/modelling/features/builders/_geological_feature_builder.py +543 -0
- LoopStructural/modelling/features/builders/_structural_frame_builder.py +237 -0
- LoopStructural/modelling/features/fault/__init__.py +3 -0
- LoopStructural/modelling/features/fault/_fault_function.py +444 -0
- LoopStructural/modelling/features/fault/_fault_function_feature.py +82 -0
- LoopStructural/modelling/features/fault/_fault_segment.py +505 -0
- LoopStructural/modelling/features/fold/__init__.py +9 -0
- LoopStructural/modelling/features/fold/_fold.py +167 -0
- LoopStructural/modelling/features/fold/_fold_rotation_angle.py +149 -0
- LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +67 -0
- LoopStructural/modelling/features/fold/_foldframe.py +194 -0
- LoopStructural/modelling/features/fold/_svariogram.py +188 -0
- LoopStructural/modelling/input/__init__.py +2 -0
- LoopStructural/modelling/input/fault_network.py +80 -0
- LoopStructural/modelling/input/map2loop_processor.py +165 -0
- LoopStructural/modelling/input/process_data.py +650 -0
- LoopStructural/modelling/input/project_file.py +84 -0
- LoopStructural/modelling/intrusions/__init__.py +25 -0
- LoopStructural/modelling/intrusions/geom_conceptual_models.py +142 -0
- LoopStructural/modelling/intrusions/geometric_scaling_functions.py +123 -0
- LoopStructural/modelling/intrusions/intrusion_builder.py +672 -0
- LoopStructural/modelling/intrusions/intrusion_feature.py +410 -0
- LoopStructural/modelling/intrusions/intrusion_frame_builder.py +971 -0
- LoopStructural/modelling/intrusions/intrusion_support_functions.py +460 -0
- LoopStructural/utils/__init__.py +38 -0
- LoopStructural/utils/_surface.py +143 -0
- LoopStructural/utils/_transformation.py +76 -0
- LoopStructural/utils/config.py +18 -0
- LoopStructural/utils/dtm_creator.py +17 -0
- LoopStructural/utils/exceptions.py +31 -0
- LoopStructural/utils/helper.py +292 -0
- LoopStructural/utils/json_encoder.py +18 -0
- LoopStructural/utils/linalg.py +8 -0
- LoopStructural/utils/logging.py +79 -0
- LoopStructural/utils/maths.py +245 -0
- LoopStructural/utils/regions.py +103 -0
- LoopStructural/utils/typing.py +7 -0
- LoopStructural/utils/utils.py +68 -0
- LoopStructural/version.py +1 -0
- LoopStructural/visualisation/__init__.py +11 -0
- LoopStructural-1.6.1.dist-info/LICENSE +21 -0
- LoopStructural-1.6.1.dist-info/METADATA +81 -0
- LoopStructural-1.6.1.dist-info/RECORD +129 -0
- LoopStructural-1.6.1.dist-info/WHEEL +5 -0
- 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
|