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,126 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _write_feat_surfs_gocad(surf, file_name):
|
|
5
|
+
"""
|
|
6
|
+
Writes out a GOCAD TSURF file for each surface in list
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
surf_list: [ SimpleNamespace() ... ]
|
|
11
|
+
Details of the surfaces, as a list of SimpleNamespace() objects. Fields are:
|
|
12
|
+
verts: vertices, numpy ndarray with dtype = float64 & shape = (N,3)
|
|
13
|
+
faces: faces, numpy ndarray with dtype = int32 & shape = (M,3)
|
|
14
|
+
values: values, numpy ndarray with dtype = float32 & shape = (N,)
|
|
15
|
+
normals: normals, numpy ndarray with dtype = float32 & shape = (N,3)
|
|
16
|
+
name: name of feature e.g. fault or supergroup, string
|
|
17
|
+
|
|
18
|
+
file_name: string
|
|
19
|
+
Desired filename
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
True if successful
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
properties_header = None
|
|
29
|
+
if surf.properties:
|
|
30
|
+
|
|
31
|
+
properties_header = f"""PROPERTIES {' '.join(list(surf.properties.keys()))}
|
|
32
|
+
NO_DATA_VALUES -99999
|
|
33
|
+
PROPERTY_CLASSES {' '.join(list(surf.properties.keys()))}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
file_name = Path(file_name).with_suffix(".ts")
|
|
37
|
+
with open(f"{file_name}", "w") as fd:
|
|
38
|
+
fd.write(
|
|
39
|
+
f"""GOCAD TSurf 1
|
|
40
|
+
HEADER {{
|
|
41
|
+
*solid*color: #ffa500
|
|
42
|
+
ivolmap: false
|
|
43
|
+
imap: false
|
|
44
|
+
name: {surf.name}
|
|
45
|
+
}}
|
|
46
|
+
GOCAD_ORIGINAL_COORDINATE_SYSTEM
|
|
47
|
+
NAME Default
|
|
48
|
+
PROJECTION Unknown
|
|
49
|
+
DATUM Unknown
|
|
50
|
+
AXIS_NAME X Y Z
|
|
51
|
+
AXIS_UNIT m m m
|
|
52
|
+
ZPOSITIVE Elevation
|
|
53
|
+
END_ORIGINAL_COORDINATE_SYSTEM
|
|
54
|
+
GEOLOGICAL_FEATURE {surf.name}
|
|
55
|
+
GEOLOGICAL_TYPE fault
|
|
56
|
+
{properties_header if properties_header else ""}
|
|
57
|
+
PROPERTY_CLASS_HEADER X {{
|
|
58
|
+
kind: X
|
|
59
|
+
unit: m
|
|
60
|
+
}}
|
|
61
|
+
PROPERTY_CLASS_HEADER Y {{
|
|
62
|
+
kind: Y
|
|
63
|
+
unit: m
|
|
64
|
+
}}
|
|
65
|
+
PROPERTY_CLASS_HEADER Z {{
|
|
66
|
+
kind: Z
|
|
67
|
+
unit: m
|
|
68
|
+
is_z: on
|
|
69
|
+
}}
|
|
70
|
+
PROPERTY_CLASS_HEADER vector3d {{
|
|
71
|
+
kind: Length
|
|
72
|
+
unit: m
|
|
73
|
+
}}
|
|
74
|
+
TFACE
|
|
75
|
+
"""
|
|
76
|
+
)
|
|
77
|
+
v_idx = 1
|
|
78
|
+
v_map = {}
|
|
79
|
+
for idx, vert in enumerate(surf.vertices):
|
|
80
|
+
if not np.isnan(vert[0]) and not np.isnan(vert[1]) and not np.isnan(vert[2]):
|
|
81
|
+
fd.write(f"VRTX {v_idx:} {vert[0]} {vert[1]} {vert[2]}")
|
|
82
|
+
if surf.properties:
|
|
83
|
+
for value in surf.properties.values():
|
|
84
|
+
fd.write(f" {value[idx]}")
|
|
85
|
+
fd.write("\n")
|
|
86
|
+
v_map[idx] = v_idx
|
|
87
|
+
v_idx += 1
|
|
88
|
+
for face in surf.triangles:
|
|
89
|
+
if face[0] in v_map and face[1] in v_map and face[2] in v_map:
|
|
90
|
+
fd.write(f"TRGL {v_map[face[0]]} {v_map[face[1]]} {v_map[face[2]]} \n")
|
|
91
|
+
fd.write("END\n")
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# def _write_pointset(points, file_name):
|
|
96
|
+
# """
|
|
97
|
+
# Write out a GOCAD VS file for a pointset
|
|
98
|
+
|
|
99
|
+
# Parameters
|
|
100
|
+
# ----------
|
|
101
|
+
# points: SimpleNamespace()
|
|
102
|
+
# Details of the points, as a SimpleNamespace() object. Fields are:
|
|
103
|
+
# locations: locations, numpy ndarray with dtype = float64 & shape = (N,3)
|
|
104
|
+
# vectors: vectors, numpy ndarray with dtype = float64 & shape = (N,3)
|
|
105
|
+
# name: name of feature e.g. fault or supergroup, string
|
|
106
|
+
|
|
107
|
+
# file_name: string
|
|
108
|
+
# Desired filename
|
|
109
|
+
|
|
110
|
+
# Returns
|
|
111
|
+
# -------
|
|
112
|
+
# True if successful
|
|
113
|
+
|
|
114
|
+
# """
|
|
115
|
+
# from pathlib import Path
|
|
116
|
+
|
|
117
|
+
# file_name = Path(file_name).with_suffix(".vs")
|
|
118
|
+
# with open(f"{file_name}", "w") as fd:
|
|
119
|
+
# fd.write(
|
|
120
|
+
# f"""GOCAD VSet 1
|
|
121
|
+
# HEADER {{
|
|
122
|
+
# name: {points.name}
|
|
123
|
+
# }}
|
|
124
|
+
# GOCAD_ORIGINAL_COORDINATE_SYSTEM
|
|
125
|
+
# NAME Default
|
|
126
|
+
# PROJECTION Unknown
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
try:
|
|
2
|
+
import omf
|
|
3
|
+
except ImportError:
|
|
4
|
+
raise ImportError(
|
|
5
|
+
"You need to install the omf package to use this feature. "
|
|
6
|
+
"You can install it with: pip install --pre omf"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_project(filename):
|
|
11
|
+
try:
|
|
12
|
+
project = omf.load(filename)
|
|
13
|
+
except FileNotFoundError:
|
|
14
|
+
project = omf.Project(name='LoopStructural Model')
|
|
15
|
+
return project
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_cell_attributes(loopobject):
|
|
19
|
+
attributes = []
|
|
20
|
+
if loopobject.cell_properties:
|
|
21
|
+
attributes += [
|
|
22
|
+
omf.NumericAttribute(name=k, array=v, location="faces")
|
|
23
|
+
for k, v in loopobject.cell_properties.items()
|
|
24
|
+
]
|
|
25
|
+
return attributes
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_point_attributed(loopobject):
|
|
29
|
+
attributes = []
|
|
30
|
+
if loopobject.properties:
|
|
31
|
+
attributes += [
|
|
32
|
+
omf.NumericAttribute(name=k, array=v, location="vertices")
|
|
33
|
+
for k, v in loopobject.properties.items()
|
|
34
|
+
]
|
|
35
|
+
return attributes
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def add_surface_to_omf(surface, filename):
|
|
39
|
+
|
|
40
|
+
attributes = []
|
|
41
|
+
attributes += get_cell_attributes(surface)
|
|
42
|
+
attributes += get_point_attributed(surface)
|
|
43
|
+
surface = omf.Surface(
|
|
44
|
+
vertices=surface.vertices,
|
|
45
|
+
triangles=surface.triangles,
|
|
46
|
+
attributes=attributes,
|
|
47
|
+
name=surface.name,
|
|
48
|
+
)
|
|
49
|
+
project = get_project(filename)
|
|
50
|
+
|
|
51
|
+
project.elements += [surface]
|
|
52
|
+
omf.save(project, filename, mode='w')
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def add_pointset_to_omf(points, filename):
|
|
56
|
+
|
|
57
|
+
attributes = []
|
|
58
|
+
attributes += get_point_attributed(points)
|
|
59
|
+
|
|
60
|
+
points = omf.PointSet(
|
|
61
|
+
vertices=points.locations,
|
|
62
|
+
attributes=attributes,
|
|
63
|
+
name=points.name,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
project = get_project(filename)
|
|
67
|
+
project.elements += [points]
|
|
68
|
+
omf.save(project, filename, mode='w')
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def add_structured_grid_to_omf(grid, filename):
|
|
72
|
+
print('Open Mining Format cannot store structured grids')
|
|
73
|
+
return
|
|
74
|
+
# attributes = []
|
|
75
|
+
# attributes += get_cell_attributes(grid)
|
|
76
|
+
# attributes += get_point_attributed(grid)
|
|
77
|
+
|
|
78
|
+
# vol = omf.TensorGridBlockModel(
|
|
79
|
+
# name=grid.name,
|
|
80
|
+
# tensor_u=np.ones(grid.nsteps[0]).astype(float),
|
|
81
|
+
# tensor_v=np.ones(grid.nsteps[0]).astype(float),
|
|
82
|
+
# tensor_w=np.ones(grid.nsteps[0]).astype(float),
|
|
83
|
+
# origin=grid.origin,
|
|
84
|
+
# attributes=attributes,
|
|
85
|
+
# )
|
|
86
|
+
# project = get_project(filename)
|
|
87
|
+
# project.elements += [vol]
|
|
88
|
+
# omf.save(project, filename, mode='w')
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interpolators and interpolation supports
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"InterpolatorType",
|
|
8
|
+
"GeologicalInterpolator",
|
|
9
|
+
"DiscreteInterpolator",
|
|
10
|
+
"FiniteDifferenceInterpolator",
|
|
11
|
+
"PiecewiseLinearInterpolator",
|
|
12
|
+
"DiscreteFoldInterpolator",
|
|
13
|
+
"SurfeRBFInterpolator",
|
|
14
|
+
"P1Interpolator",
|
|
15
|
+
"P2Interpolator",
|
|
16
|
+
"TetMesh",
|
|
17
|
+
"StructuredGrid",
|
|
18
|
+
"UnStructuredTetMesh",
|
|
19
|
+
"P1Unstructured2d",
|
|
20
|
+
"P2Unstructured2d",
|
|
21
|
+
"StructuredGrid2D",
|
|
22
|
+
"P2UnstructuredTetMesh",
|
|
23
|
+
]
|
|
24
|
+
from enum import IntEnum
|
|
25
|
+
|
|
26
|
+
from ..utils import getLogger
|
|
27
|
+
|
|
28
|
+
logger = getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class InterpolatorType(IntEnum):
|
|
32
|
+
"""
|
|
33
|
+
Enum for the different interpolator types
|
|
34
|
+
|
|
35
|
+
1-9 should cover interpolators with supports
|
|
36
|
+
9+ are data supported
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
BASE = 0
|
|
40
|
+
BASE_DISCRETE = 1
|
|
41
|
+
FINITE_DIFFERENCE = 2
|
|
42
|
+
DISCRETE_FOLD = 3
|
|
43
|
+
PIECEWISE_LINEAR = 4
|
|
44
|
+
PIECEWISE_QUADRATIC = 5
|
|
45
|
+
BASE_DATA_SUPPORTED = 10
|
|
46
|
+
SURFE = 11
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
interpolator_string_map = {
|
|
50
|
+
"FDI": InterpolatorType.FINITE_DIFFERENCE,
|
|
51
|
+
"PLI": InterpolatorType.PIECEWISE_LINEAR,
|
|
52
|
+
"P2": InterpolatorType.PIECEWISE_QUADRATIC,
|
|
53
|
+
"P1": InterpolatorType.PIECEWISE_LINEAR,
|
|
54
|
+
"DFI": InterpolatorType.DISCRETE_FOLD,
|
|
55
|
+
}
|
|
56
|
+
from ..interpolators._geological_interpolator import GeologicalInterpolator
|
|
57
|
+
from ..interpolators._discrete_interpolator import DiscreteInterpolator
|
|
58
|
+
from ..interpolators.supports import (
|
|
59
|
+
TetMesh,
|
|
60
|
+
StructuredGrid,
|
|
61
|
+
UnStructuredTetMesh,
|
|
62
|
+
P1Unstructured2d,
|
|
63
|
+
P2Unstructured2d,
|
|
64
|
+
StructuredGrid2D,
|
|
65
|
+
P2UnstructuredTetMesh,
|
|
66
|
+
SupportType,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
from ..interpolators._finite_difference_interpolator import (
|
|
71
|
+
FiniteDifferenceInterpolator,
|
|
72
|
+
)
|
|
73
|
+
from ..interpolators._p1interpolator import (
|
|
74
|
+
P1Interpolator as PiecewiseLinearInterpolator,
|
|
75
|
+
)
|
|
76
|
+
from ..interpolators._discrete_fold_interpolator import (
|
|
77
|
+
DiscreteFoldInterpolator,
|
|
78
|
+
)
|
|
79
|
+
from ..interpolators._p2interpolator import P2Interpolator
|
|
80
|
+
from ..interpolators._p1interpolator import P1Interpolator
|
|
81
|
+
|
|
82
|
+
interpolator_map = {
|
|
83
|
+
InterpolatorType.BASE: GeologicalInterpolator,
|
|
84
|
+
InterpolatorType.BASE_DISCRETE: DiscreteInterpolator,
|
|
85
|
+
InterpolatorType.FINITE_DIFFERENCE: FiniteDifferenceInterpolator,
|
|
86
|
+
InterpolatorType.DISCRETE_FOLD: DiscreteFoldInterpolator,
|
|
87
|
+
InterpolatorType.PIECEWISE_LINEAR: P1Interpolator,
|
|
88
|
+
InterpolatorType.PIECEWISE_QUADRATIC: P2Interpolator,
|
|
89
|
+
InterpolatorType.BASE_DATA_SUPPORTED: GeologicalInterpolator,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
support_interpolator_map = {
|
|
93
|
+
InterpolatorType.FINITE_DIFFERENCE: {
|
|
94
|
+
2: SupportType.StructuredGrid2D,
|
|
95
|
+
3: SupportType.StructuredGrid,
|
|
96
|
+
},
|
|
97
|
+
InterpolatorType.DISCRETE_FOLD: {3: SupportType.TetMesh, 2: SupportType.P1Unstructured2d},
|
|
98
|
+
InterpolatorType.PIECEWISE_LINEAR: {3: SupportType.TetMesh, 2: SupportType.P1Unstructured2d},
|
|
99
|
+
InterpolatorType.PIECEWISE_QUADRATIC: {
|
|
100
|
+
3: SupportType.P2UnstructuredTetMesh,
|
|
101
|
+
2: SupportType.P2Unstructured2d,
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
from ._interpolator_factory import InterpolatorFactory
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from LoopStructural.interpolators import (
|
|
5
|
+
GeologicalInterpolator,
|
|
6
|
+
InterpolatorFactory,
|
|
7
|
+
InterpolatorType,
|
|
8
|
+
)
|
|
9
|
+
from LoopStructural.datatypes import BoundingBox
|
|
10
|
+
from LoopStructural.utils import getLogger
|
|
11
|
+
|
|
12
|
+
logger = getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LoopInterpolator:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
bounding_box: BoundingBox,
|
|
19
|
+
dimensions: int = 3,
|
|
20
|
+
type=InterpolatorType.FINITE_DIFFERENCE,
|
|
21
|
+
nelements: int = 1000,
|
|
22
|
+
):
|
|
23
|
+
"""Scikitlearn like interface for LoopStructural interpolators
|
|
24
|
+
useful for quickly building an interpolator to apply to a dataset
|
|
25
|
+
build a generic interpolation object speficying the bounding box
|
|
26
|
+
and then fit to constraints and evaluate the interpolator
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
bounding_box : BoundingBox
|
|
31
|
+
Area where the interpolation will work
|
|
32
|
+
dimensions : int, optional
|
|
33
|
+
number of dimensions e.g. 3d or 2d, by default 3
|
|
34
|
+
type : str, optional
|
|
35
|
+
type of interpolation algorithm FDI- finite difference, PLI - linear finite elements,
|
|
36
|
+
by default "FDI"
|
|
37
|
+
nelements : int, optional
|
|
38
|
+
degrees of freedom for interpolator, by default 1000
|
|
39
|
+
"""
|
|
40
|
+
self.dimensions = dimensions
|
|
41
|
+
self.type = "FDI"
|
|
42
|
+
self.bounding_box = bounding_box
|
|
43
|
+
self.interpolator: GeologicalInterpolator = InterpolatorFactory.create_interpolator(
|
|
44
|
+
type,
|
|
45
|
+
bounding_box,
|
|
46
|
+
nelements,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def fit(
|
|
50
|
+
self,
|
|
51
|
+
values: Optional[np.ndarray] = None,
|
|
52
|
+
tangent_vectors: Optional[np.ndarray] = None,
|
|
53
|
+
normal_vectors: Optional[np.ndarray] = None,
|
|
54
|
+
inequality_constraints: Optional[np.ndarray] = None,
|
|
55
|
+
):
|
|
56
|
+
"""_summary_
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
values : Optional[np.ndarray], optional
|
|
61
|
+
Value constraints for implicit function, by default None
|
|
62
|
+
tangent_vectors : Optional[np.ndarray], optional
|
|
63
|
+
tangent constraints for implicit function, by default None
|
|
64
|
+
normal_vectors : Optional[np.ndarray], optional
|
|
65
|
+
gradient norm constraints for implicit function, by default None
|
|
66
|
+
inequality_constraints : Optional[np.ndarray], optional
|
|
67
|
+
_description_, by default None
|
|
68
|
+
"""
|
|
69
|
+
if values is not None:
|
|
70
|
+
self.interpolator.set_value_constraints(values)
|
|
71
|
+
if tangent_vectors is not None:
|
|
72
|
+
self.interpolator.set_tangent_constraints(tangent_vectors)
|
|
73
|
+
if normal_vectors is not None:
|
|
74
|
+
self.interpolator.set_normal_constraints(normal_vectors)
|
|
75
|
+
if inequality_constraints:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
self.interpolator.setup()
|
|
79
|
+
|
|
80
|
+
def evaluate_scalar_value(self, locations: np.ndarray) -> np.ndarray:
|
|
81
|
+
"""Evaluate the value of the interpolator at locations
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
locations : np.ndarray
|
|
86
|
+
Nx3 array of locations to evaluate the interpolator at
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
np.ndarray
|
|
91
|
+
value of implicit function at locations
|
|
92
|
+
"""
|
|
93
|
+
self.interpolator.update()
|
|
94
|
+
return self.interpolator.evaluate_value(locations)
|
|
95
|
+
|
|
96
|
+
def evaluate_gradient(self, locations: np.ndarray) -> np.ndarray:
|
|
97
|
+
"""Evaluate the gradient of the interpolator at locations
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
locations : np.ndarray
|
|
102
|
+
Nx3 locations
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
np.ndarray
|
|
107
|
+
Nx3 gradient of implicit function
|
|
108
|
+
"""
|
|
109
|
+
self.interpolator.update()
|
|
110
|
+
return self.interpolator.evaluate_gradient(locations)
|
|
111
|
+
|
|
112
|
+
def fit_and_evaluate_value(
|
|
113
|
+
self,
|
|
114
|
+
values: Optional[np.ndarray] = None,
|
|
115
|
+
tangent_vectors: Optional[np.ndarray] = None,
|
|
116
|
+
normal_vectors: Optional[np.ndarray] = None,
|
|
117
|
+
inequality_constraints: Optional[np.ndarray] = None,
|
|
118
|
+
):
|
|
119
|
+
# get locations
|
|
120
|
+
self.fit(
|
|
121
|
+
values=values,
|
|
122
|
+
tangent_vectors=tangent_vectors,
|
|
123
|
+
normal_vectors=normal_vectors,
|
|
124
|
+
inequality_constraints=inequality_constraints,
|
|
125
|
+
)
|
|
126
|
+
locations = self.interpolator.get_data_locations()
|
|
127
|
+
return self.evalute_scalar_value(locations)
|
|
128
|
+
|
|
129
|
+
def fit_and_evaluate_gradient(
|
|
130
|
+
self,
|
|
131
|
+
values: Optional[np.ndarray] = None,
|
|
132
|
+
tangent_vectors: Optional[np.ndarray] = None,
|
|
133
|
+
normal_vectors: Optional[np.ndarray] = None,
|
|
134
|
+
inequality_constraints: Optional[np.ndarray] = None,
|
|
135
|
+
):
|
|
136
|
+
self.fit(
|
|
137
|
+
values=values,
|
|
138
|
+
tangent_vectors=tangent_vectors,
|
|
139
|
+
normal_vectors=normal_vectors,
|
|
140
|
+
inequality_constraints=inequality_constraints,
|
|
141
|
+
)
|
|
142
|
+
locations = self.interpolator.get_data_locations()
|
|
143
|
+
return self.evaluate_gradient(locations)
|
|
@@ -0,0 +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
|
|
15
|
+
|
|
16
|
+
logger = getLogger(__name__)
|
|
17
|
+
|
|
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")
|
|
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
|
+
)
|
|
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")
|
|
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
|
|
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
|
|
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")
|
|
File without changes
|