LoopStructural 1.6.5__py3-none-any.whl → 1.6.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of LoopStructural might be problematic. Click here for more details.
- LoopStructural/datatypes/_bounding_box.py +58 -3
- LoopStructural/datatypes/_point.py +32 -6
- LoopStructural/interpolators/__init__.py +1 -1
- LoopStructural/interpolators/_builders.py +141 -141
- LoopStructural/interpolators/_finite_difference_interpolator.py +11 -11
- LoopStructural/interpolators/_interpolator_builder.py +55 -0
- LoopStructural/interpolators/_interpolator_factory.py +7 -18
- LoopStructural/interpolators/supports/_3d_base_structured.py +4 -0
- LoopStructural/modelling/core/geological_model.py +9 -8
- LoopStructural/modelling/features/__init__.py +1 -0
- LoopStructural/modelling/features/_analytical_feature.py +23 -2
- LoopStructural/modelling/features/_base_geological_feature.py +17 -1
- LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
- LoopStructural/modelling/features/_geological_feature.py +3 -1
- LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
- LoopStructural/modelling/features/_structural_frame.py +6 -0
- LoopStructural/modelling/features/builders/_folded_feature_builder.py +2 -2
- LoopStructural/modelling/features/builders/_structural_frame_builder.py +2 -2
- LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
- LoopStructural/modelling/features/fault/_fault_segment.py +10 -2
- LoopStructural/modelling/input/process_data.py +7 -6
- LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
- LoopStructural/utils/__init__.py +1 -1
- LoopStructural/utils/_surface.py +6 -1
- LoopStructural/utils/_transformation.py +98 -14
- LoopStructural/utils/colours.py +25 -2
- LoopStructural/version.py +1 -1
- {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/METADATA +16 -2
- {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/RECORD +32 -30
- {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/WHEEL +1 -1
- {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/LICENSE +0 -0
- {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/top_level.txt +0 -0
|
@@ -410,9 +410,15 @@ class BoundingBox:
|
|
|
410
410
|
import pyvista as pv
|
|
411
411
|
except ImportError:
|
|
412
412
|
raise ImportError("pyvista is required for vtk support")
|
|
413
|
-
x = np.linspace(
|
|
414
|
-
|
|
415
|
-
|
|
413
|
+
x = np.linspace(
|
|
414
|
+
self.global_origin[0] + self.origin[0], self.global_maximum[0], self.nsteps[0]
|
|
415
|
+
)
|
|
416
|
+
y = np.linspace(
|
|
417
|
+
self.global_origin[1] + self.origin[1], self.global_maximum[1], self.nsteps[1]
|
|
418
|
+
)
|
|
419
|
+
z = np.linspace(
|
|
420
|
+
self.global_origin[2] + self.origin[2], self.global_maximum[2], self.nsteps[2]
|
|
421
|
+
)
|
|
416
422
|
return pv.RectilinearGrid(
|
|
417
423
|
x,
|
|
418
424
|
y,
|
|
@@ -435,3 +441,52 @@ class BoundingBox:
|
|
|
435
441
|
properties=_vertex_data,
|
|
436
442
|
name=name,
|
|
437
443
|
)
|
|
444
|
+
|
|
445
|
+
def project(self, xyz):
|
|
446
|
+
"""Project a point into the bounding box
|
|
447
|
+
|
|
448
|
+
Parameters
|
|
449
|
+
----------
|
|
450
|
+
xyz : np.ndarray
|
|
451
|
+
point to project
|
|
452
|
+
|
|
453
|
+
Returns
|
|
454
|
+
-------
|
|
455
|
+
np.ndarray
|
|
456
|
+
projected point
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
return (xyz - self.global_origin) / np.max(
|
|
460
|
+
(self.global_maximum - self.global_origin)
|
|
461
|
+
) # np.clip(xyz, self.origin, self.maximum)
|
|
462
|
+
|
|
463
|
+
def reproject(self, xyz):
|
|
464
|
+
"""Reproject a point from the bounding box to the global space
|
|
465
|
+
|
|
466
|
+
Parameters
|
|
467
|
+
----------
|
|
468
|
+
xyz : np.ndarray
|
|
469
|
+
point to reproject
|
|
470
|
+
|
|
471
|
+
Returns
|
|
472
|
+
-------
|
|
473
|
+
np.ndarray
|
|
474
|
+
reprojected point
|
|
475
|
+
"""
|
|
476
|
+
|
|
477
|
+
return xyz * np.max((self.global_maximum - self.global_origin)) + self.global_origin
|
|
478
|
+
|
|
479
|
+
def __repr__(self):
|
|
480
|
+
return f"BoundingBox({self.origin}, {self.maximum}, {self.nsteps})"
|
|
481
|
+
|
|
482
|
+
def __str__(self):
|
|
483
|
+
return f"BoundingBox({self.origin}, {self.maximum}, {self.nsteps})"
|
|
484
|
+
|
|
485
|
+
def __eq__(self, other):
|
|
486
|
+
if not isinstance(other, BoundingBox):
|
|
487
|
+
return False
|
|
488
|
+
return (
|
|
489
|
+
np.allclose(self.origin, other.origin)
|
|
490
|
+
and np.allclose(self.maximum, other.maximum)
|
|
491
|
+
and np.allclose(self.nsteps, other.nsteps)
|
|
492
|
+
)
|
|
@@ -25,11 +25,14 @@ class ValuePoints:
|
|
|
25
25
|
),
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
def vtk(self):
|
|
28
|
+
def vtk(self, scalars=None):
|
|
29
29
|
import pyvista as pv
|
|
30
30
|
|
|
31
31
|
points = pv.PolyData(self.locations)
|
|
32
|
-
|
|
32
|
+
if scalars is not None and len(scalars) == len(self.locations):
|
|
33
|
+
points.point_data['scalars'] = scalars
|
|
34
|
+
else:
|
|
35
|
+
points["values"] = self.values
|
|
33
36
|
return points
|
|
34
37
|
|
|
35
38
|
def plot(self, pyvista_kwargs={}):
|
|
@@ -123,9 +126,19 @@ class VectorPoints:
|
|
|
123
126
|
def from_dict(self, d):
|
|
124
127
|
return VectorPoints(d['locations'], d['vectors'], d['name'], d.get('properties', None))
|
|
125
128
|
|
|
126
|
-
def vtk(
|
|
129
|
+
def vtk(
|
|
130
|
+
self,
|
|
131
|
+
geom='arrow',
|
|
132
|
+
scale=0.10,
|
|
133
|
+
scale_function=None,
|
|
134
|
+
normalise=True,
|
|
135
|
+
tolerance=0.05,
|
|
136
|
+
bb=None,
|
|
137
|
+
scalars=None,
|
|
138
|
+
):
|
|
127
139
|
import pyvista as pv
|
|
128
140
|
|
|
141
|
+
_projected = False
|
|
129
142
|
vectors = np.copy(self.vectors)
|
|
130
143
|
if normalise:
|
|
131
144
|
norm = np.linalg.norm(vectors, axis=1)
|
|
@@ -133,15 +146,28 @@ class VectorPoints:
|
|
|
133
146
|
if scale_function is not None:
|
|
134
147
|
# vectors /= np.linalg.norm(vectors, axis=1)[:, None]
|
|
135
148
|
vectors *= scale_function(self.locations)[:, None]
|
|
136
|
-
|
|
149
|
+
locations = self.locations
|
|
150
|
+
if bb is not None:
|
|
151
|
+
try:
|
|
152
|
+
locations = bb.project(locations)
|
|
153
|
+
_projected = True
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.error(f'Failed to project points to bounding box: {e}')
|
|
156
|
+
logger.error('Using unprojected points, this may cause issues with the glyphing')
|
|
157
|
+
points = pv.PolyData(locations)
|
|
158
|
+
if scalars is not None and len(scalars) == len(self.locations):
|
|
159
|
+
points['scalars'] = scalars
|
|
137
160
|
points.point_data.set_vectors(vectors, 'vectors')
|
|
138
161
|
if geom == 'arrow':
|
|
139
162
|
geom = pv.Arrow(scale=scale)
|
|
140
163
|
elif geom == 'disc':
|
|
141
|
-
geom = pv.Disc(inner=0, outer=scale)
|
|
164
|
+
geom = pv.Disc(inner=0, outer=scale).rotate_y(90)
|
|
142
165
|
|
|
143
166
|
# Perform the glyph
|
|
144
|
-
|
|
167
|
+
glyphed = points.glyph(orient="vectors", geom=geom, tolerance=tolerance, scale=False)
|
|
168
|
+
if _projected:
|
|
169
|
+
glyphed.points = bb.reproject(glyphed.points)
|
|
170
|
+
return glyphed
|
|
145
171
|
|
|
146
172
|
def plot(self, pyvista_kwargs={}):
|
|
147
173
|
"""Calls pyvista plot on the vtk object
|
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
from LoopStructural.utils.exceptions import LoopException
|
|
2
|
-
import numpy as np
|
|
3
|
-
from typing import Optional
|
|
4
|
-
from LoopStructural.interpolators import (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
13
|
-
from LoopStructural.datatypes import BoundingBox
|
|
14
|
-
from LoopStructural.utils.logging import getLogger
|
|
1
|
+
# from LoopStructural.utils.exceptions import LoopException
|
|
2
|
+
# import numpy as np
|
|
3
|
+
# from typing import Optional
|
|
4
|
+
# from LoopStructural.interpolators import (
|
|
5
|
+
# P1Interpolator,
|
|
6
|
+
# P2Interpolator,
|
|
7
|
+
# FiniteDifferenceInterpolator,
|
|
8
|
+
# GeologicalInterpolator,
|
|
9
|
+
# DiscreteFoldInterpolator,
|
|
10
|
+
# StructuredGrid,
|
|
11
|
+
# TetMesh,
|
|
12
|
+
# )
|
|
13
|
+
# from LoopStructural.datatypes import BoundingBox
|
|
14
|
+
# from LoopStructural.utils.logging import getLogger
|
|
15
15
|
|
|
16
|
-
logger = getLogger(__name__)
|
|
16
|
+
# logger = getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def get_interpolator(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
) -> GeologicalInterpolator:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
19
|
+
# def get_interpolator(
|
|
20
|
+
# bounding_box: BoundingBox,
|
|
21
|
+
# interpolatortype: str,
|
|
22
|
+
# nelements: int,
|
|
23
|
+
# element_volume: Optional[float] = None,
|
|
24
|
+
# buffer: float = 0.2,
|
|
25
|
+
# dimensions: int = 3,
|
|
26
|
+
# support=None,
|
|
27
|
+
# ) -> GeologicalInterpolator:
|
|
28
|
+
# # add a buffer to the interpolation domain, this is necessary for
|
|
29
|
+
# # faults but also generally a good
|
|
30
|
+
# # idea to avoid boundary problems
|
|
31
|
+
# # buffer = bb[1, :]
|
|
32
|
+
# origin = bounding_box.with_buffer(buffer).origin
|
|
33
|
+
# maximum = bounding_box.with_buffer(buffer).maximum
|
|
34
|
+
# box_vol = np.prod(maximum - origin)
|
|
35
|
+
# if interpolatortype == "PLI":
|
|
36
|
+
# if support is None:
|
|
37
|
+
# if element_volume is None:
|
|
38
|
+
# # nelements /= 5
|
|
39
|
+
# element_volume = box_vol / nelements
|
|
40
|
+
# # calculate the step vector of a regular cube
|
|
41
|
+
# step_vector = np.zeros(3)
|
|
42
|
+
# step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
43
|
+
# # step_vector /= np.array([1,1,2])
|
|
44
|
+
# # number of steps is the length of the box / step vector
|
|
45
|
+
# nsteps = np.ceil((maximum - origin) / step_vector).astype(int)
|
|
46
|
+
# if np.any(np.less(nsteps, 3)):
|
|
47
|
+
# axis_labels = ["x", "y", "z"]
|
|
48
|
+
# for i in range(3):
|
|
49
|
+
# if nsteps[i] < 3:
|
|
50
|
+
# nsteps[i] = 3
|
|
51
|
+
# logger.error(
|
|
52
|
+
# f"Number of steps in direction {axis_labels[i]} is too small, try increasing nelements"
|
|
53
|
+
# )
|
|
54
|
+
# logger.error("Cannot create interpolator: number of steps is too small")
|
|
55
|
+
# raise ValueError("Number of steps too small cannot create interpolator")
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
# support = TetMesh(origin=origin, nsteps=nsteps, step_vector=step_vector)
|
|
58
|
+
# logger.info(
|
|
59
|
+
# "Creating regular tetrahedron mesh with %i elements \n"
|
|
60
|
+
# "for modelling using PLI" % (support.ntetra)
|
|
61
|
+
# )
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
# return P1Interpolator(support)
|
|
64
|
+
# if interpolatortype == "P2":
|
|
65
|
+
# if support is not None:
|
|
66
|
+
# logger.info(
|
|
67
|
+
# "Creating regular tetrahedron mesh with %i elements \n"
|
|
68
|
+
# "for modelling using P2" % (support.ntetra)
|
|
69
|
+
# )
|
|
70
|
+
# return P2Interpolator(support)
|
|
71
|
+
# else:
|
|
72
|
+
# raise ValueError("Cannot create P2 interpolator without support, try using PLI")
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
74
|
+
# if interpolatortype == "FDI":
|
|
75
|
+
# # find the volume of one element
|
|
76
|
+
# if element_volume is None:
|
|
77
|
+
# element_volume = box_vol / nelements
|
|
78
|
+
# # calculate the step vector of a regular cube
|
|
79
|
+
# step_vector = np.zeros(3)
|
|
80
|
+
# step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
81
|
+
# # number of steps is the length of the box / step vector
|
|
82
|
+
# nsteps = np.ceil((maximum - origin) / step_vector).astype(int)
|
|
83
|
+
# if np.any(np.less(nsteps, 3)):
|
|
84
|
+
# logger.error("Cannot create interpolator: number of steps is too small")
|
|
85
|
+
# axis_labels = ["x", "y", "z"]
|
|
86
|
+
# for i in range(3):
|
|
87
|
+
# if nsteps[i] < 3:
|
|
88
|
+
# nsteps[i] = 3
|
|
89
|
+
# # logger.error(
|
|
90
|
+
# # f"Number of steps in direction {axis_labels[i]} is too small, try increasing nelements"
|
|
91
|
+
# # )
|
|
92
|
+
# # raise ValueError("Number of steps too small cannot create interpolator")
|
|
93
|
+
# # create a structured grid using the origin and number of steps
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
95
|
+
# grid = StructuredGrid(origin=origin, nsteps=nsteps, step_vector=step_vector)
|
|
96
|
+
# logger.info(
|
|
97
|
+
# f"Creating regular grid with {grid.n_elements} elements \n" "for modelling using FDI"
|
|
98
|
+
# )
|
|
99
|
+
# return FiniteDifferenceInterpolator(grid)
|
|
100
|
+
# if interpolatortype == "DFI":
|
|
101
|
+
# if element_volume is None:
|
|
102
|
+
# nelements /= 5
|
|
103
|
+
# element_volume = box_vol / nelements
|
|
104
|
+
# # calculate the step vector of a regular cube
|
|
105
|
+
# step_vector = np.zeros(3)
|
|
106
|
+
# step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
107
|
+
# # number of steps is the length of the box / step vector
|
|
108
|
+
# nsteps = np.ceil((maximum - origin) / step_vector).astype(int)
|
|
109
|
+
# # create a structured grid using the origin and number of steps
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
111
|
+
# mesh = TetMesh(origin=origin, nsteps=nsteps, step_vector=step_vector)
|
|
112
|
+
# logger.info(
|
|
113
|
+
# f"Creating regular tetrahedron mesh with {mesh.ntetra} elements \n"
|
|
114
|
+
# "for modelling using DFI"
|
|
115
|
+
# )
|
|
116
|
+
# return DiscreteFoldInterpolator(mesh, None)
|
|
117
|
+
# raise LoopException("No interpolator")
|
|
118
|
+
# # fi interpolatortype == "DFI" and dfi is True:
|
|
119
|
+
# # if element_volume is None:
|
|
120
|
+
# # nelements /= 5
|
|
121
|
+
# # element_volume = box_vol / nelements
|
|
122
|
+
# # # calculate the step vector of a regular cube
|
|
123
|
+
# # step_vector = np.zeros(3)
|
|
124
|
+
# # step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
125
|
+
# # # number of steps is the length of the box / step vector
|
|
126
|
+
# # nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
127
|
+
# # # create a structured grid using the origin and number of steps
|
|
128
|
+
# # if "meshbuilder" in kwargs:
|
|
129
|
+
# # mesh = kwargs["meshbuilder"].build(bb, nelements)
|
|
130
|
+
# # else:
|
|
131
|
+
# # mesh = kwargs.get(
|
|
132
|
+
# # "mesh",
|
|
133
|
+
# # TetMesh(origin=bb[0, :], nsteps=nsteps, step_vector=step_vector),
|
|
134
|
+
# # )
|
|
135
|
+
# # logger.info(
|
|
136
|
+
# # f"Creating regular tetrahedron mesh with {mesh.ntetra} elements \n"
|
|
137
|
+
# # "for modelling using DFI"
|
|
138
|
+
# # )
|
|
139
|
+
# # return DFI(mesh, kwargs["fold"])
|
|
140
|
+
# # if interpolatortype == "Surfe" or interpolatortype == "surfe":
|
|
141
|
+
# # # move import of surfe to where we actually try and use it
|
|
142
|
+
# # if not surfe:
|
|
143
|
+
# # logger.warning("Cannot import Surfe, try another interpolator")
|
|
144
|
+
# # raise ImportError("Cannot import surfepy, try pip install surfe")
|
|
145
|
+
# # method = kwargs.get("method", "single_surface")
|
|
146
|
+
# # logger.info("Using surfe interpolator")
|
|
147
|
+
# # return SurfeRBFInterpolator(method)
|
|
148
|
+
# # logger.warning("No interpolator")
|
|
149
|
+
# # raise InterpolatorError("Could not create interpolator")
|
|
@@ -97,7 +97,7 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
97
97
|
self.add_interface_constraints(self.interpolation_weights["ipw"])
|
|
98
98
|
self.add_value_inequality_constraints()
|
|
99
99
|
self.add_inequality_pairs_constraints(
|
|
100
|
-
kwargs.get('inequality_pairs', None),
|
|
100
|
+
pairs=kwargs.get('inequality_pairs', None),
|
|
101
101
|
upper_bound=kwargs.get('inequality_pair_upper_bound', np.finfo(float).eps),
|
|
102
102
|
lower_bound=kwargs.get('inequality_pair_lower_bound', -np.inf),
|
|
103
103
|
)
|
|
@@ -172,16 +172,18 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
172
172
|
# get elements for points
|
|
173
173
|
points = self.get_interface_constraints()
|
|
174
174
|
if points.shape[0] > 1:
|
|
175
|
-
|
|
175
|
+
node_idx, inside = self.support.position_to_cell_corners(
|
|
176
176
|
points[:, : self.support.dimension]
|
|
177
177
|
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
idc =
|
|
184
|
-
|
|
178
|
+
gi = np.zeros(self.support.n_nodes, dtype=int)
|
|
179
|
+
gi[:] = -1
|
|
180
|
+
gi[self.region] = np.arange(0, self.nx, dtype=int)
|
|
181
|
+
idc = np.zeros(node_idx.shape).astype(int)
|
|
182
|
+
idc[:] = -1
|
|
183
|
+
idc[inside, :] = gi[node_idx[inside, :]]
|
|
184
|
+
inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
|
|
185
|
+
idc = idc[inside, :]
|
|
186
|
+
A = self.support.position_to_dof_coefs(points[inside, : self.support.dimension])
|
|
185
187
|
for unique_id in np.unique(
|
|
186
188
|
points[
|
|
187
189
|
np.logical_and(~np.isnan(points[:, self.support.dimension]), inside),
|
|
@@ -197,7 +199,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
197
199
|
).T.reshape(-1, 2)
|
|
198
200
|
interface_A = np.hstack([A[mask, :][ij[:, 0], :], -A[mask, :][ij[:, 1], :]])
|
|
199
201
|
interface_idc = np.hstack([idc[mask, :][ij[:, 0], :], idc[mask, :][ij[:, 1], :]])
|
|
200
|
-
|
|
201
202
|
# now map the index from global to region create array size of mesh
|
|
202
203
|
# initialise as np.nan, then map points inside region to 0->nx
|
|
203
204
|
gi = np.zeros(self.support.n_nodes).astype(int)
|
|
@@ -229,7 +230,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
229
230
|
points = self.get_gradient_constraints()
|
|
230
231
|
if points.shape[0] > 0:
|
|
231
232
|
# calculate unit vector for orientation data
|
|
232
|
-
# points[:,3:]/=np.linalg.norm(points[:,3:],axis=1)[:,None]
|
|
233
233
|
|
|
234
234
|
node_idx, inside = self.support.position_to_cell_corners(
|
|
235
235
|
points[:, : self.support.dimension]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from LoopStructural.interpolators import (
|
|
2
|
+
GeologicalInterpolator,
|
|
3
|
+
InterpolatorFactory,
|
|
4
|
+
InterpolatorType,
|
|
5
|
+
)
|
|
6
|
+
from LoopStructural.datatypes import BoundingBox
|
|
7
|
+
from typing import Optional, Union
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterpolatorBuilder:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
interpolatortype: Union[str, InterpolatorType],
|
|
15
|
+
bounding_box: BoundingBox,
|
|
16
|
+
nelements: int = 1000,
|
|
17
|
+
buffer: float = 0.2,
|
|
18
|
+
**kwargs,
|
|
19
|
+
):
|
|
20
|
+
self.interpolatortype = interpolatortype
|
|
21
|
+
self.bounding_box = bounding_box
|
|
22
|
+
self.nelements = nelements
|
|
23
|
+
self.buffer = buffer
|
|
24
|
+
self.kwargs = kwargs
|
|
25
|
+
self.interpolator : Optional[GeologicalInterpolator]= None
|
|
26
|
+
|
|
27
|
+
def create_interpolator(self) -> 'InterpolatorBuilder':
|
|
28
|
+
self.interpolator = InterpolatorFactory.create_interpolator(
|
|
29
|
+
interpolatortype=self.interpolatortype,
|
|
30
|
+
boundingbox=self.bounding_box,
|
|
31
|
+
nelements=self.nelements,
|
|
32
|
+
buffer=self.buffer,
|
|
33
|
+
**self.kwargs,
|
|
34
|
+
)
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
def set_value_constraints(self, value_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
38
|
+
if self.interpolator:
|
|
39
|
+
self.interpolator.set_value_constraints(value_constraints)
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def set_gradient_constraints(self, gradient_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
43
|
+
if self.interpolator:
|
|
44
|
+
self.interpolator.set_gradient_constraints(gradient_constraints)
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def set_normal_constraints(self, normal_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
48
|
+
if self.interpolator:
|
|
49
|
+
self.interpolator.set_normal_constraints(normal_constraints)
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def setup_interpolator(self, **kwargs) -> Optional[GeologicalInterpolator]:
|
|
53
|
+
if self.interpolator:
|
|
54
|
+
self.interpolator.setup(**kwargs)
|
|
55
|
+
return self.interpolator
|
|
@@ -65,25 +65,14 @@ class InterpolatorFactory:
|
|
|
65
65
|
gradient_norm_constraints: Optional[np.ndarray] = None,
|
|
66
66
|
gradient_constraints: Optional[np.ndarray] = None,
|
|
67
67
|
):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
raise ValueError("No bounding box specified")
|
|
72
|
-
if nelements is None:
|
|
73
|
-
raise ValueError("No number of elements specified")
|
|
74
|
-
if isinstance(interpolatortype, str):
|
|
75
|
-
interpolatortype = InterpolatorType._member_map_[interpolatortype].numerator
|
|
76
|
-
if support is None:
|
|
77
|
-
raise Exception("Support must be specified")
|
|
78
|
-
# supporttype = support_interpolator_map[interpolatortype]
|
|
79
|
-
# support = SupportFactory.create_support(
|
|
80
|
-
# supporttype, boundingbox, nelements, element_volume
|
|
81
|
-
# )
|
|
82
|
-
interpolator = interpolator_map[interpolatortype](support)
|
|
68
|
+
interpolator = InterpolatorFactory.create_interpolator(
|
|
69
|
+
interpolatortype, boundingbox, nelements, element_volume, support
|
|
70
|
+
)
|
|
83
71
|
if value_constraints is not None:
|
|
84
|
-
interpolator.
|
|
72
|
+
interpolator.set_value_constraints(value_constraints)
|
|
85
73
|
if gradient_norm_constraints is not None:
|
|
86
|
-
interpolator.
|
|
74
|
+
interpolator.set_normal_constraints(gradient_norm_constraints)
|
|
87
75
|
if gradient_constraints is not None:
|
|
88
|
-
interpolator.
|
|
76
|
+
interpolator.set_gradient_constraints(gradient_constraints)
|
|
77
|
+
interpolator.setup()
|
|
89
78
|
return interpolator
|
|
@@ -41,6 +41,10 @@ class BaseStructuredSupport(BaseSupport):
|
|
|
41
41
|
raise LoopException("nsteps cannot be zero")
|
|
42
42
|
if np.any(nsteps < 0):
|
|
43
43
|
raise LoopException("nsteps cannot be negative")
|
|
44
|
+
if np.any(nsteps < 3):
|
|
45
|
+
raise LoopException(
|
|
46
|
+
"step vector cannot be less than 3. Try increasing the resolution of the interpolator"
|
|
47
|
+
)
|
|
44
48
|
self._nsteps = np.array(nsteps, dtype=int) + 1
|
|
45
49
|
self._step_vector = np.array(step_vector)
|
|
46
50
|
self._origin = np.array(origin)
|