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.

Files changed (32) hide show
  1. LoopStructural/datatypes/_bounding_box.py +58 -3
  2. LoopStructural/datatypes/_point.py +32 -6
  3. LoopStructural/interpolators/__init__.py +1 -1
  4. LoopStructural/interpolators/_builders.py +141 -141
  5. LoopStructural/interpolators/_finite_difference_interpolator.py +11 -11
  6. LoopStructural/interpolators/_interpolator_builder.py +55 -0
  7. LoopStructural/interpolators/_interpolator_factory.py +7 -18
  8. LoopStructural/interpolators/supports/_3d_base_structured.py +4 -0
  9. LoopStructural/modelling/core/geological_model.py +9 -8
  10. LoopStructural/modelling/features/__init__.py +1 -0
  11. LoopStructural/modelling/features/_analytical_feature.py +23 -2
  12. LoopStructural/modelling/features/_base_geological_feature.py +17 -1
  13. LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
  14. LoopStructural/modelling/features/_geological_feature.py +3 -1
  15. LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
  16. LoopStructural/modelling/features/_structural_frame.py +6 -0
  17. LoopStructural/modelling/features/builders/_folded_feature_builder.py +2 -2
  18. LoopStructural/modelling/features/builders/_structural_frame_builder.py +2 -2
  19. LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
  20. LoopStructural/modelling/features/fault/_fault_segment.py +10 -2
  21. LoopStructural/modelling/input/process_data.py +7 -6
  22. LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
  23. LoopStructural/utils/__init__.py +1 -1
  24. LoopStructural/utils/_surface.py +6 -1
  25. LoopStructural/utils/_transformation.py +98 -14
  26. LoopStructural/utils/colours.py +25 -2
  27. LoopStructural/version.py +1 -1
  28. {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/METADATA +16 -2
  29. {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/RECORD +32 -30
  30. {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/WHEEL +1 -1
  31. {LoopStructural-1.6.5.dist-info → LoopStructural-1.6.7.dist-info}/LICENSE +0 -0
  32. {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(self.global_origin[0], self.global_maximum[0], self.nsteps[0])
414
- y = np.linspace(self.global_origin[1], self.global_maximum[1], self.nsteps[1])
415
- z = np.linspace(self.global_origin[2], self.global_maximum[2], self.nsteps[2])
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
- points["values"] = self.values
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(self, geom='arrow', scale=1.0, scale_function=None, normalise=True, tolerance=0.05):
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
- points = pv.PolyData(self.locations)
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
- return points.glyph(orient="vectors", geom=geom, tolerance=tolerance)
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
@@ -114,5 +114,5 @@ support_interpolator_map = {
114
114
  }
115
115
 
116
116
  from ._interpolator_factory import InterpolatorFactory
117
-
117
+ from ._interpolator_builder import InterpolatorBuilder
118
118
  # from ._api import LoopInterpolator
@@ -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
- 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
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
- 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")
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
- 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
- )
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
- 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")
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
- 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
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
- 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
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
- 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")
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
- vertices, c, tetras, inside = self.support.get_element_for_location(
175
+ node_idx, inside = self.support.position_to_cell_corners(
176
176
  points[:, : self.support.dimension]
177
177
  )
178
- # calculate volume of tetras
179
- # vecs = vertices[inside, 1:, :] - vertices[inside, 0, None, :]
180
- # vol = np.abs(np.linalg.det(vecs)) / 6
181
- A = c[inside, :]
182
- # A *= vol[:,None]
183
- idc = tetras[inside, :]
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
- if interpolatortype is None:
69
- raise ValueError("No interpolator type specified")
70
- if boundingbox is None:
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.add_value_constraints(value_constraints)
72
+ interpolator.set_value_constraints(value_constraints)
85
73
  if gradient_norm_constraints is not None:
86
- interpolator.add_gradient_constraints(gradient_norm_constraints)
74
+ interpolator.set_normal_constraints(gradient_norm_constraints)
87
75
  if gradient_constraints is not None:
88
- interpolator.add_gradient_constraints(gradient_constraints)
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)