LoopStructural 1.0.3__zip → 1.0.71.dev0__zip
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.
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/__init__.py +12 -7
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/_base.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__init__.py +3 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/base_structured_3d_support.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_fold_interpolator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_interpolator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/finite_difference_interpolator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/geological_interpolator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/operator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/piecewiselinear_interpolator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_grid.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_tetra.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/surfe_wrapper.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/base_structured_3d_support.py +101 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.c +4137 -2716
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.cp36-win_amd64.pyd +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_fold_interpolator.py +56 -22
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_interpolator.py +61 -28
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/finite_difference_interpolator.py +71 -11
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/geological_interpolator.py +22 -3
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/operator.py +16 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/piecewiselinear_interpolator.py +150 -11
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_grid.py +31 -69
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_tetra.py +89 -45
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/surfe_wrapper.py +7 -8
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model_graph.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/stratigraphic_column.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/geological_model.py +515 -197
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/geological_model_graph.py +881 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/stratigraphic_column.py +5 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__init__.py +1 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_builder.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_segment.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_builder.py +127 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function_feature.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_segment.py +30 -3
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__init__.py +1 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/cross_product_geological_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature_builder.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/lambda_geological_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/region_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame_builder.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/unconformity_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/cross_product_geological_feature.py +18 -5
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature.py +22 -49
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature_builder.py +171 -47
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/lambda_geological_feature.py +31 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/region_feature.py +3 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame.py +28 -11
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame_builder.py +32 -22
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/unconformity_feature.py +6 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle_feature.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/foldframe.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/svariogram.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold.py +13 -5
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle.py +5 -4
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle_feature.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/foldframe.py +7 -5
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/svariogram.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__init__.py +5 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/bounding_box.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/exceptions.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/helper.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/logging.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/map2loop.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/regions.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/utils.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/bounding_box.py +21 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/exceptions.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/helper.py +10 -2
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/logging.py +60 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/map2loop.py +128 -37
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/regions.py +11 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/utils.py +40 -47
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/map_viewer.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_plotter.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_visualisation.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/rotation_angle_plotter.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/sphinx_scraper.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/stratigraphic_column.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/map_viewer.py +236 -36
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_plotter.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_visualisation.py +427 -79
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/rotation_angle_plotter.py +29 -12
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/stratigraphic_column.py +60 -0
- Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/PKG-INFO +1 -1
- Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/SOURCES.txt +10 -5
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.71.dev0-py3.6.egg-info/requires.txt +8 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.3-py3.6.egg-info/requires.txt +0 -3
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_faults.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_fold.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_interpolator.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_refolded.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/test_faults.py +0 -17
- Miniconda/envs/loop/Lib/site-packages/tests/test_fold.py +0 -57
- Miniconda/envs/loop/Lib/site-packages/tests/test_interpolator.py +0 -88
- Miniconda/envs/loop/Lib/site-packages/tests/test_refolded.py +0 -22
- /Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/dependency_links.txt +0 -0
- /Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/top_level.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main entry point for creating a geological model
|
|
3
|
+
"""
|
|
1
4
|
import logging
|
|
2
5
|
|
|
3
6
|
import numpy as np
|
|
4
7
|
import pandas as pd
|
|
5
|
-
|
|
6
8
|
from LoopStructural.datasets import normal_vector_headers
|
|
7
9
|
from LoopStructural.interpolators.discrete_fold_interpolator import \
|
|
8
10
|
DiscreteFoldInterpolator as DFI
|
|
@@ -11,34 +13,25 @@ from LoopStructural.interpolators.finite_difference_interpolator import \
|
|
|
11
13
|
from LoopStructural.interpolators.piecewiselinear_interpolator import \
|
|
12
14
|
PiecewiseLinearInterpolator as PLI
|
|
13
15
|
|
|
14
|
-
try:
|
|
15
|
-
from LoopStructural.interpolators.surfe_wrapper import \
|
|
16
|
-
SurfeRBFInterpolator as Surfe
|
|
17
|
-
|
|
18
|
-
surfe = True
|
|
19
|
-
|
|
20
|
-
except ImportError:
|
|
21
|
-
surfe = False
|
|
22
16
|
|
|
23
|
-
from LoopStructural.
|
|
24
|
-
|
|
17
|
+
from LoopStructural.interpolators.structured_grid import StructuredGrid
|
|
18
|
+
from LoopStructural.interpolators.structured_tetra import TetMesh
|
|
25
19
|
from LoopStructural.modelling.fault.fault_segment import FaultSegment
|
|
26
|
-
from LoopStructural.modelling.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
from LoopStructural.modelling.features import UnconformityFeature
|
|
32
|
-
from LoopStructural.modelling.fold.fold import FoldEvent
|
|
20
|
+
from LoopStructural.modelling.fault import FaultBuilder
|
|
21
|
+
from LoopStructural.modelling.features import (GeologicalFeatureInterpolator,
|
|
22
|
+
RegionFeature,
|
|
23
|
+
StructuralFrameBuilder,
|
|
24
|
+
UnconformityFeature)
|
|
33
25
|
from LoopStructural.modelling.fold import FoldRotationAngle
|
|
26
|
+
from LoopStructural.modelling.fold.fold import FoldEvent
|
|
34
27
|
from LoopStructural.modelling.fold.foldframe import FoldFrame
|
|
35
|
-
from LoopStructural.interpolators.structured_grid import StructuredGrid
|
|
36
|
-
from LoopStructural.interpolators.structured_tetra import TetMesh
|
|
37
28
|
from LoopStructural.utils.exceptions import LoopBaseException
|
|
29
|
+
from LoopStructural.utils.helper import (all_heading, gradient_vec_names,
|
|
30
|
+
strike_dip_vector)
|
|
31
|
+
|
|
32
|
+
from LoopStructural.utils import getLogger, log_to_file
|
|
33
|
+
logger = getLogger(__name__)
|
|
38
34
|
|
|
39
|
-
logger = logging.getLogger(__name__)
|
|
40
|
-
if not surfe:
|
|
41
|
-
logger.warning("Cannot import Surfe")
|
|
42
35
|
|
|
43
36
|
|
|
44
37
|
def _calculate_average_intersection(series_builder, fold_frame, fold,
|
|
@@ -62,12 +55,30 @@ def _calculate_average_intersection(series_builder, fold_frame, fold,
|
|
|
62
55
|
|
|
63
56
|
class GeologicalModel:
|
|
64
57
|
"""
|
|
65
|
-
A geological model is the recipe for building a 3D model and
|
|
66
|
-
the rescaling
|
|
67
|
-
|
|
58
|
+
A geological model is the recipe for building a 3D model and can include
|
|
59
|
+
the rescaling of the model between 0 and 1.
|
|
60
|
+
|
|
61
|
+
Attributes
|
|
62
|
+
----------
|
|
63
|
+
features : list
|
|
64
|
+
Contains all features youngest to oldest
|
|
65
|
+
feature_name_index : dict
|
|
66
|
+
maps feature name to the list index of the features
|
|
67
|
+
data : pandas dataframe
|
|
68
|
+
the dataframe used for building the geological model
|
|
69
|
+
nsteps : tuple/np.array(3,dtype=int)
|
|
70
|
+
the number of steps x,y,z to evaluate the model
|
|
71
|
+
origin : tuple/np.array(3,dtype=doubles)
|
|
72
|
+
the origin of the model box
|
|
73
|
+
parameters : dict
|
|
74
|
+
a dictionary tracking the parameters used to build the model
|
|
75
|
+
scale_factor : double
|
|
76
|
+
the scale factor used to rescale the model
|
|
77
|
+
|
|
78
|
+
|
|
68
79
|
"""
|
|
69
80
|
def __init__(self, origin, maximum, rescale=True, nsteps=(40, 40, 40),
|
|
70
|
-
reuse_supports=False):
|
|
81
|
+
reuse_supports=False, logfile=None, loglevel='info'):
|
|
71
82
|
"""
|
|
72
83
|
Parameters
|
|
73
84
|
----------
|
|
@@ -78,41 +89,91 @@ class GeologicalModel:
|
|
|
78
89
|
rescale : bool
|
|
79
90
|
whether to rescale the model to between 0/1
|
|
80
91
|
|
|
92
|
+
Examples
|
|
93
|
+
--------
|
|
94
|
+
Demo data
|
|
95
|
+
|
|
96
|
+
>>> from LoopStructural.datasets import load_claudius
|
|
97
|
+
>>> from LoopStructural import GeologicalModel
|
|
98
|
+
|
|
99
|
+
>>> data, bb = load_claudius()
|
|
100
|
+
|
|
101
|
+
>>> model = GeologicalModel(bb[:,0],bb[:,1]
|
|
102
|
+
>>> model.set_model_data(data)
|
|
103
|
+
>>> model.create_and_add_foliation('strati')
|
|
104
|
+
|
|
105
|
+
>>> y = np.linspace(model.bounding_box[0, 1], model.bounding_box[1, 1],
|
|
106
|
+
nsteps[1])
|
|
107
|
+
>>> z = np.linspace(model.bounding_box[1, 2], model.bounding_box[0, 2],
|
|
108
|
+
nsteps[2])
|
|
109
|
+
>>> xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
110
|
+
>>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
111
|
+
>>> model.evaluate_feature_value('strati',xyz,scale=False)
|
|
112
|
+
|
|
113
|
+
|
|
81
114
|
"""
|
|
115
|
+
if logfile:
|
|
116
|
+
self.logfile = logfile
|
|
117
|
+
log_to_file(logfile,loglevel)
|
|
118
|
+
|
|
119
|
+
logger.info('Initialising geological model')
|
|
82
120
|
self.features = []
|
|
83
121
|
self.feature_name_index = {}
|
|
84
122
|
self.data = None
|
|
85
123
|
self.nsteps = nsteps
|
|
86
|
-
|
|
124
|
+
self._str = 'Instance of LoopStructural.GeologicalModel \n'
|
|
125
|
+
self._str += '------------------------------------------ \n'
|
|
87
126
|
# we want to rescale the model area so that the maximum length is
|
|
88
127
|
# 1
|
|
89
128
|
self.origin = np.array(origin).astype(float)
|
|
90
|
-
|
|
129
|
+
originstr = 'Model origin: {} {} {}'.format(self.origin[0],self.origin[1],self.origin[2])
|
|
130
|
+
logger.info(originstr)
|
|
131
|
+
self._str+=originstr+'\n'
|
|
91
132
|
self.maximum = np.array(maximum).astype(float)
|
|
133
|
+
maximumstr = 'Model maximum: {} {} {}'.format(self.maximum[0],self.maximum[1],self.maximum[2])
|
|
134
|
+
logger.info(maximumstr)
|
|
135
|
+
self._str+=maximumstr+'\n'
|
|
136
|
+
|
|
92
137
|
lengths = self.maximum - self.origin
|
|
93
138
|
self.scale_factor = 1.
|
|
94
139
|
self.bounding_box = np.zeros((2, 3))
|
|
95
140
|
self.bounding_box[1, :] = self.maximum - self.origin
|
|
96
141
|
self.bounding_box[1, :] = self.maximum - self.origin
|
|
97
142
|
if rescale:
|
|
98
|
-
self.scale_factor = np.max(lengths)
|
|
99
|
-
|
|
143
|
+
self.scale_factor = float(np.max(lengths))
|
|
144
|
+
logger.info('Rescaling model using scale factor {}'.format(self.scale_factor))
|
|
145
|
+
self._str+='Model rescale factor: {} \n'.format(self.scale_factor)
|
|
146
|
+
self._str+='The model contains {} GeologicalFeatures \n'.format(len(self.features))
|
|
147
|
+
self._str+=''
|
|
148
|
+
self._str += '------------------------------------------ \n'
|
|
149
|
+
self._str += ''
|
|
100
150
|
self.bounding_box /= self.scale_factor
|
|
101
151
|
self.support = {}
|
|
102
152
|
self.reuse_supports = reuse_supports
|
|
153
|
+
if self.reuse_supports:
|
|
154
|
+
logger.warning("Supports are shared between geological features \n"
|
|
155
|
+
"this may cause unexpected behaviour and should only\n"
|
|
156
|
+
"be use by advanced users")
|
|
157
|
+
logger.info('Reusing interpolation supports: {}'.format(self.reuse_supports))
|
|
103
158
|
self.stratigraphic_column = None
|
|
104
159
|
self.parameters = {'features': [], 'model': {'bounding_box': self.origin.tolist() + self.maximum.tolist(),
|
|
105
160
|
'rescale': rescale,
|
|
106
161
|
'nsteps': nsteps,
|
|
107
162
|
'reuse_supports': reuse_supports}}
|
|
163
|
+
|
|
164
|
+
def __str__(self):
|
|
165
|
+
return self._str
|
|
108
166
|
|
|
167
|
+
def _ipython_key_completions_(self):
|
|
168
|
+
return self.feature_name_index.keys()
|
|
169
|
+
|
|
109
170
|
@classmethod
|
|
110
171
|
def from_map2loop_directory(cls, m2l_directory,**kwargs):
|
|
111
172
|
"""Alternate constructor for a geological model using m2l output
|
|
112
173
|
|
|
113
174
|
Uses the information saved in the map2loop files to build a geological model.
|
|
114
175
|
You can specify kwargs for building foliation using foliation_params and for
|
|
115
|
-
faults using fault_params.
|
|
176
|
+
faults using fault_params. faults is a flag that allows for the faults to be skipped.
|
|
116
177
|
|
|
117
178
|
Parameters
|
|
118
179
|
----------
|
|
@@ -124,12 +185,26 @@ class GeologicalModel:
|
|
|
124
185
|
(GeologicalModel, dict)
|
|
125
186
|
the created geological model and a dictionary of the map2loop data
|
|
126
187
|
"""
|
|
127
|
-
from LoopStructural.utils import
|
|
128
|
-
|
|
188
|
+
from LoopStructural.utils import build_model, process_map2loop
|
|
189
|
+
logger.info('LoopStructural model initialised from m2l directory: {}'.format(m2l_directory))
|
|
190
|
+
m2lflags = kwargs.pop('m2lflags',{})
|
|
191
|
+
m2l_data = process_map2loop(m2l_directory,m2lflags)
|
|
129
192
|
return build_model(m2l_data,**kwargs), m2l_data
|
|
130
193
|
|
|
131
194
|
@classmethod
|
|
132
195
|
def from_file(cls, file):
|
|
196
|
+
"""Load a geological model from file
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
file : string
|
|
201
|
+
path to the file
|
|
202
|
+
|
|
203
|
+
Returns
|
|
204
|
+
-------
|
|
205
|
+
GeologicalModel
|
|
206
|
+
the geological model object
|
|
207
|
+
"""
|
|
133
208
|
try:
|
|
134
209
|
import dill as pickle
|
|
135
210
|
except ImportError:
|
|
@@ -137,18 +212,49 @@ class GeologicalModel:
|
|
|
137
212
|
return None
|
|
138
213
|
model = pickle.load(open(file,'rb'))
|
|
139
214
|
if type(model) == GeologicalModel:
|
|
215
|
+
logger.info('GeologicalModel initialised from file')
|
|
140
216
|
return model
|
|
141
217
|
else:
|
|
142
218
|
logger.error('{} does not contain a geological model'.format(file))
|
|
143
219
|
return None
|
|
220
|
+
|
|
221
|
+
def __getitem__(self, feature_name):
|
|
222
|
+
"""Accessor for feature in features using feature_name_index
|
|
144
223
|
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
feature_name : string
|
|
227
|
+
name of the feature to return
|
|
228
|
+
"""
|
|
229
|
+
return self.get_feature_by_name(feature_name)
|
|
230
|
+
|
|
231
|
+
def feature_names(self):
|
|
232
|
+
return self.feature_name_index.keys()
|
|
233
|
+
|
|
234
|
+
def fault_names(self):
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
def check_inialisation(self):
|
|
238
|
+
if self.data is None:
|
|
239
|
+
logger.error("Data not associated with GeologicalModel. Run set_data")
|
|
240
|
+
return False
|
|
241
|
+
|
|
145
242
|
def to_file(self, file):
|
|
243
|
+
"""Save a model to a pickle file requires dill
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
file : string
|
|
248
|
+
path to file location
|
|
249
|
+
"""
|
|
146
250
|
try:
|
|
147
251
|
import dill as pickle
|
|
148
252
|
except ImportError:
|
|
149
|
-
logger.error("Cannot write to file, dill not installed"
|
|
253
|
+
logger.error("Cannot write to file, dill not installed \n"
|
|
254
|
+
"pip install dill")
|
|
150
255
|
return
|
|
151
256
|
try:
|
|
257
|
+
logger.info('Writing GeologicalModel to: {}'.format(file))
|
|
152
258
|
pickle.dump(self,open(file,'wb'))
|
|
153
259
|
except pickle.PicklingError:
|
|
154
260
|
logger.error('Error saving file')
|
|
@@ -169,6 +275,7 @@ class GeologicalModel:
|
|
|
169
275
|
(feature.name, self.feature_name_index[feature.name]))
|
|
170
276
|
self.features[self.feature_name_index[feature.name]] = feature
|
|
171
277
|
else:
|
|
278
|
+
self._str += 'GeologicalFeature: {} of type - {} \n'.format(feature.name,feature.type)
|
|
172
279
|
self.features.append(feature)
|
|
173
280
|
self.feature_name_index[feature.name] = len(self.features) - 1
|
|
174
281
|
logger.info("Adding %s to model at location %i" % (
|
|
@@ -176,7 +283,10 @@ class GeologicalModel:
|
|
|
176
283
|
self._add_domain_fault_above(feature)
|
|
177
284
|
self._add_unconformity_above(feature)
|
|
178
285
|
feature.set_model(self)
|
|
179
|
-
|
|
286
|
+
|
|
287
|
+
def data_for_feature(self,feature_name):
|
|
288
|
+
return self.data.loc[self.data['feature_name'] == feature_name,:]
|
|
289
|
+
|
|
180
290
|
def set_model_data(self, data):
|
|
181
291
|
"""
|
|
182
292
|
Set the data array for the model
|
|
@@ -204,7 +314,7 @@ class GeologicalModel:
|
|
|
204
314
|
data = pd.read_csv(data)
|
|
205
315
|
except:
|
|
206
316
|
logger.error("Could not load pandas data frame from data")
|
|
207
|
-
|
|
317
|
+
logger.info('Adding data to GeologicalModel with {} data points'.format(len(data)))
|
|
208
318
|
self.data = data.copy()
|
|
209
319
|
self.data['X'] -= self.origin[0]
|
|
210
320
|
self.data['Y'] -= self.origin[1]
|
|
@@ -213,7 +323,7 @@ class GeologicalModel:
|
|
|
213
323
|
self.data['Y'] /= self.scale_factor
|
|
214
324
|
self.data['Z'] /= self.scale_factor
|
|
215
325
|
if 'type' in self.data:
|
|
216
|
-
logger.warning("'type' is
|
|
326
|
+
logger.warning("'type' is depreciated replace with 'feature_name' \n")
|
|
217
327
|
self.data.rename(columns={'type':'feature_name'},inplace=True)
|
|
218
328
|
for h in all_heading():
|
|
219
329
|
if h not in self.data:
|
|
@@ -224,6 +334,7 @@ class GeologicalModel:
|
|
|
224
334
|
self.data[h] = 0
|
|
225
335
|
|
|
226
336
|
if 'strike' in self.data and 'dip' in self.data:
|
|
337
|
+
logger.info('Converting strike and dip to vectors')
|
|
227
338
|
mask = np.all(~np.isnan(self.data.loc[:, ['strike', 'dip']]),
|
|
228
339
|
axis=1)
|
|
229
340
|
self.data.loc[mask, gradient_vec_names()] = strike_dip_vector(
|
|
@@ -255,14 +366,14 @@ class GeologicalModel:
|
|
|
255
366
|
data_temp['Z'] /= self.scale_factor
|
|
256
367
|
self.data.concat([self.data, data_temp], sort=True)
|
|
257
368
|
|
|
258
|
-
def set_stratigraphic_column(self, stratigraphic_column):
|
|
369
|
+
def set_stratigraphic_column(self, stratigraphic_column,cmap='tab20'):
|
|
259
370
|
"""
|
|
260
371
|
Adds a stratigraphic column to the model
|
|
261
372
|
|
|
262
373
|
Parameters
|
|
263
374
|
----------
|
|
264
375
|
stratigraphic_column : dictionary
|
|
265
|
-
|
|
376
|
+
cmap : matplotlib.cmap
|
|
266
377
|
Returns
|
|
267
378
|
-------
|
|
268
379
|
|
|
@@ -271,14 +382,46 @@ class GeologicalModel:
|
|
|
271
382
|
stratigraphic_column is a nested dictionary with the format
|
|
272
383
|
{'group':
|
|
273
384
|
{'series1':
|
|
274
|
-
{'min':0., 'max':10.,'id':0}
|
|
385
|
+
{'min':0., 'max':10.,'id':0,'colour':}
|
|
275
386
|
}
|
|
276
387
|
}
|
|
277
388
|
|
|
278
389
|
"""
|
|
390
|
+
# if the colour for a unit hasn't been specified we can just sample from
|
|
391
|
+
# a colour map e.g. tab20
|
|
392
|
+
logger.info('Adding stratigraphic column to model')
|
|
393
|
+
random_colour = True
|
|
394
|
+
n_units=0
|
|
395
|
+
for g in stratigraphic_column.keys():
|
|
396
|
+
for u in stratigraphic_column[g].keys():
|
|
397
|
+
if 'colour' in stratigraphic_column[g][u]:
|
|
398
|
+
random_colour = False
|
|
399
|
+
break
|
|
400
|
+
n_units+=1
|
|
401
|
+
if random_colour:
|
|
402
|
+
import matplotlib.cm as cm
|
|
403
|
+
cmap = cm.get_cmap(cmap,n_units)
|
|
404
|
+
cmap_colours = cmap.colors
|
|
405
|
+
ci = 0
|
|
406
|
+
for g in stratigraphic_column.keys():
|
|
407
|
+
for u in stratigraphic_column[g].keys():
|
|
408
|
+
stratigraphic_column[g][u]['colour'] = cmap_colours[ci,:]
|
|
409
|
+
|
|
279
410
|
self.stratigraphic_column = stratigraphic_column
|
|
280
411
|
|
|
281
412
|
def create_from_feature_list(self, features):
|
|
413
|
+
"""Initialises a model from a dictionary containing the features
|
|
414
|
+
|
|
415
|
+
Parameters
|
|
416
|
+
----------
|
|
417
|
+
features : [type]
|
|
418
|
+
[description]
|
|
419
|
+
|
|
420
|
+
Raises
|
|
421
|
+
------
|
|
422
|
+
LoopBaseException
|
|
423
|
+
[description]
|
|
424
|
+
"""
|
|
282
425
|
for f in features:
|
|
283
426
|
featuretype = f.pop('featuretype', None)
|
|
284
427
|
if featuretype is None:
|
|
@@ -291,7 +434,7 @@ class GeologicalModel:
|
|
|
291
434
|
self.create_and_add_folded_foliation(f)
|
|
292
435
|
|
|
293
436
|
def get_interpolator(self, interpolatortype='PLI', nelements=1e5,
|
|
294
|
-
buffer=0.2, **kwargs):
|
|
437
|
+
buffer=0.2, element_volume = None, **kwargs):
|
|
295
438
|
"""
|
|
296
439
|
Returns an interpolator given the arguments, also constructs a
|
|
297
440
|
support for a discrete interpolator
|
|
@@ -323,51 +466,64 @@ class GeologicalModel:
|
|
|
323
466
|
bb[1, :] += buffer # *(bb[1,:]-bb[0,:])
|
|
324
467
|
box_vol = (bb[1, 0]-bb[0, 0]) * (bb[1, 1]-bb[0, 1]) * (bb[1, 2]-bb[0, 2])
|
|
325
468
|
if interpolatortype == "PLI":
|
|
326
|
-
|
|
327
|
-
|
|
469
|
+
if element_volume is None:
|
|
470
|
+
nelements /= 5
|
|
471
|
+
element_volume = box_vol / nelements
|
|
328
472
|
# calculate the step vector of a regular cube
|
|
329
473
|
step_vector = np.zeros(3)
|
|
330
|
-
step_vector[:] =
|
|
474
|
+
step_vector[:] = element_volume ** (1. / 3.)
|
|
331
475
|
# step_vector /= np.array([1,1,2])
|
|
332
476
|
# number of steps is the length of the box / step vector
|
|
333
477
|
nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
334
478
|
# create a structured grid using the origin and number of steps
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
self.support
|
|
479
|
+
if self.reuse_supports:
|
|
480
|
+
mesh_id = 'mesh_{}'.format(nelements)
|
|
481
|
+
mesh = self.support.get(mesh_id,
|
|
482
|
+
TetMesh(origin=bb[0, :], nsteps=nsteps,
|
|
483
|
+
step_vector=step_vector))
|
|
484
|
+
if mesh_id not in self.support:
|
|
485
|
+
self.support[mesh_id] = mesh
|
|
486
|
+
else:
|
|
487
|
+
mesh = TetMesh(origin=bb[0, :], nsteps=nsteps, step_vector=step_vector)
|
|
341
488
|
logger.info("Creating regular tetrahedron mesh with %i elements \n"
|
|
342
489
|
"for modelling using PLI" % (mesh.ntetra))
|
|
343
490
|
|
|
344
491
|
return PLI(mesh)
|
|
345
492
|
|
|
346
493
|
if interpolatortype == 'FDI':
|
|
494
|
+
|
|
347
495
|
# find the volume of one element
|
|
348
|
-
|
|
496
|
+
if element_volume is None:
|
|
497
|
+
element_volume = box_vol / nelements
|
|
349
498
|
# calculate the step vector of a regular cube
|
|
350
499
|
step_vector = np.zeros(3)
|
|
351
|
-
step_vector[:] =
|
|
500
|
+
step_vector[:] = element_volume ** (1. / 3.)
|
|
352
501
|
# number of steps is the length of the box / step vector
|
|
353
502
|
nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
503
|
+
if np.any(np.less(nsteps, 3)):
|
|
504
|
+
logger.error("Cannot create interpolator: number of steps is too small")
|
|
505
|
+
return None
|
|
354
506
|
# create a structured grid using the origin and number of steps
|
|
355
|
-
|
|
356
|
-
|
|
507
|
+
if self.reuse_supports:
|
|
508
|
+
grid_id = 'grid_{}'.format(nelements)
|
|
509
|
+
grid = self.support.get(grid_id, StructuredGrid(origin=bb[0, :],
|
|
357
510
|
nsteps=nsteps,
|
|
358
511
|
step_vector=step_vector))
|
|
359
|
-
|
|
360
|
-
|
|
512
|
+
if grid_id not in self.support:
|
|
513
|
+
self.support[grid_id] = grid
|
|
514
|
+
else:
|
|
515
|
+
grid = StructuredGrid(origin=bb[0, :], nsteps=nsteps,step_vector=step_vector)
|
|
361
516
|
logger.info("Creating regular grid with %i elements \n"
|
|
362
517
|
"for modelling using FDI" % grid.n_elements)
|
|
363
518
|
return FDI(grid)
|
|
364
519
|
|
|
365
520
|
if interpolatortype == "DFI": # "fold" in kwargs:
|
|
366
|
-
|
|
367
|
-
|
|
521
|
+
if element_volume is None:
|
|
522
|
+
nelements /= 5
|
|
523
|
+
element_volume = box_vol / nelements
|
|
368
524
|
# calculate the step vector of a regular cube
|
|
369
525
|
step_vector = np.zeros(3)
|
|
370
|
-
step_vector[:] =
|
|
526
|
+
step_vector[:] = element_volume ** (1. / 3.)
|
|
371
527
|
# number of steps is the length of the box / step vector
|
|
372
528
|
nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
373
529
|
# create a structured grid using the origin and number of steps
|
|
@@ -376,8 +532,19 @@ class GeologicalModel:
|
|
|
376
532
|
logger.info("Creating regular tetrahedron mesh with %i elements \n"
|
|
377
533
|
"for modelling using DFI" % mesh.ntetra)
|
|
378
534
|
return DFI(mesh, kwargs['fold'])
|
|
379
|
-
if interpolatortype == 'Surfe' or interpolatortype == 'surfe'
|
|
380
|
-
|
|
535
|
+
if interpolatortype == 'Surfe' or interpolatortype == 'surfe':
|
|
536
|
+
# move import of surfe to where we actually try and use it
|
|
537
|
+
try:
|
|
538
|
+
from LoopStructural.interpolators.surfe_wrapper import \
|
|
539
|
+
SurfeRBFInterpolator as Surfe
|
|
540
|
+
|
|
541
|
+
surfe = True
|
|
542
|
+
|
|
543
|
+
except ImportError:
|
|
544
|
+
surfe = False
|
|
545
|
+
if not surfe:
|
|
546
|
+
logger.warning("Cannot import Surfe, try another interpolator")
|
|
547
|
+
raise ImportError
|
|
381
548
|
method = kwargs.get('method', 'single_surface')
|
|
382
549
|
logger.info("Using surfe interpolator")
|
|
383
550
|
return Surfe(method)
|
|
@@ -397,6 +564,8 @@ class GeologicalModel:
|
|
|
397
564
|
feature : GeologicalFeature
|
|
398
565
|
the created geological feature
|
|
399
566
|
"""
|
|
567
|
+
if self.check_inialisation() == False:
|
|
568
|
+
return False
|
|
400
569
|
self.parameters['features'].append({'feature_type': 'foliation', 'feature_name': series_surface_data, **kwargs})
|
|
401
570
|
interpolator = self.get_interpolator(**kwargs)
|
|
402
571
|
series_builder = GeologicalFeatureInterpolator(interpolator,
|
|
@@ -411,13 +580,53 @@ class GeologicalModel:
|
|
|
411
580
|
self._add_faults(series_builder)
|
|
412
581
|
|
|
413
582
|
# build feature
|
|
414
|
-
series_feature = series_builder.build(**kwargs)
|
|
583
|
+
# series_feature = series_builder.build(**kwargs)
|
|
584
|
+
series_feature = series_builder.feature
|
|
585
|
+
series_builder.build_arguments = kwargs
|
|
415
586
|
series_feature.type = 'series'
|
|
416
587
|
# see if any unconformities are above this feature if so add region
|
|
417
588
|
# self._add_unconformity_above(series_feature)self._add_feature(series_feature)
|
|
418
589
|
self._add_feature(series_feature)
|
|
419
590
|
return series_feature
|
|
420
591
|
|
|
592
|
+
def create_and_add_dtm(self, series_surface_data, **kwargs):
|
|
593
|
+
"""
|
|
594
|
+
Parameters
|
|
595
|
+
----------
|
|
596
|
+
series_surface_data : string
|
|
597
|
+
corresponding to the feature_name in the data
|
|
598
|
+
kwargs
|
|
599
|
+
|
|
600
|
+
Returns
|
|
601
|
+
-------
|
|
602
|
+
feature : GeologicalFeature
|
|
603
|
+
the created geological feature
|
|
604
|
+
"""
|
|
605
|
+
if self.check_inialisation() == False:
|
|
606
|
+
return False
|
|
607
|
+
self.parameters['features'].append({'feature_type': 'foliation', 'feature_name': series_surface_data, **kwargs})
|
|
608
|
+
interpolator = self.get_interpolator(**kwargs)
|
|
609
|
+
series_builder = GeologicalFeatureInterpolator(interpolator,
|
|
610
|
+
name=series_surface_data,
|
|
611
|
+
**kwargs)
|
|
612
|
+
# add data
|
|
613
|
+
series_data = self.data[self.data['feature_name'] == series_surface_data]
|
|
614
|
+
if series_data.shape[0] == 0:
|
|
615
|
+
logger.warning("No data for %s, skipping" % series_surface_data)
|
|
616
|
+
return
|
|
617
|
+
series_builder.add_data_from_data_frame(series_data)
|
|
618
|
+
# self._add_faults(series_builder)
|
|
619
|
+
|
|
620
|
+
# build feature
|
|
621
|
+
# series_feature = series_builder.build(**kwargs)
|
|
622
|
+
series_feature = series_builder.feature
|
|
623
|
+
series_builder.build_arguments = kwargs
|
|
624
|
+
series_feature.type = 'dtm'
|
|
625
|
+
# see if any unconformities are above this feature if so add region
|
|
626
|
+
# self._add_unconformity_above(series_feature)self._add_feature(series_feature)
|
|
627
|
+
self._add_feature(series_feature)
|
|
628
|
+
return series_feature
|
|
629
|
+
|
|
421
630
|
def create_and_add_fold_frame(self, foldframe_data, **kwargs):
|
|
422
631
|
"""
|
|
423
632
|
Parameters
|
|
@@ -432,8 +641,9 @@ class GeologicalModel:
|
|
|
432
641
|
fold_frame : FoldFrame
|
|
433
642
|
the created fold frame
|
|
434
643
|
"""
|
|
644
|
+
if self.check_inialisation() == False:
|
|
645
|
+
return False
|
|
435
646
|
self.parameters['features'].append({'feature_type': 'fold_frame', 'feature_name': foldframe_data, **kwargs})
|
|
436
|
-
result = {}
|
|
437
647
|
# create fault frame
|
|
438
648
|
interpolator = self.get_interpolator(**kwargs)
|
|
439
649
|
#
|
|
@@ -455,16 +665,18 @@ class GeologicalModel:
|
|
|
455
665
|
|
|
456
666
|
return fold_frame
|
|
457
667
|
|
|
458
|
-
def create_and_add_folded_foliation(self, foliation_data, fold_frame=None,
|
|
668
|
+
def create_and_add_folded_foliation(self, foliation_data, fold_frame=None, svario=True,
|
|
459
669
|
**kwargs):
|
|
460
670
|
"""
|
|
461
671
|
Create a folded foliation field from data and a fold frame
|
|
462
672
|
|
|
463
673
|
Parameters
|
|
464
674
|
----------
|
|
465
|
-
foliation_data :
|
|
675
|
+
foliation_data : str
|
|
466
676
|
unique string in type column of data frame
|
|
467
677
|
fold_frame : FoldFrame
|
|
678
|
+
svario : Boolean
|
|
679
|
+
whether to calculate svariograms, saves time if avoided
|
|
468
680
|
kwargs
|
|
469
681
|
additional kwargs to be passed through to other functions
|
|
470
682
|
|
|
@@ -473,13 +685,15 @@ class GeologicalModel:
|
|
|
473
685
|
feature : GeologicalFeature
|
|
474
686
|
created geological feature
|
|
475
687
|
"""
|
|
688
|
+
if self.check_inialisation() == False:
|
|
689
|
+
return False
|
|
476
690
|
self.parameters['features'].append(
|
|
477
691
|
{'feature_type': 'fold_foliation', 'feature_name': foliation_data, 'fold_frame': fold_frame, **kwargs})
|
|
478
692
|
if fold_frame is None:
|
|
479
693
|
logger.info("Using last feature as fold frame")
|
|
480
694
|
fold_frame = self.features[-1]
|
|
481
695
|
assert type(fold_frame) == FoldFrame, "Please specify a FoldFrame"
|
|
482
|
-
fold = FoldEvent(fold_frame)
|
|
696
|
+
fold = FoldEvent(fold_frame,name='Fold_{}'.format(foliation_data))
|
|
483
697
|
fold_interpolator = self.get_interpolator("DFI", fold=fold, **kwargs)
|
|
484
698
|
series_builder = GeologicalFeatureInterpolator(
|
|
485
699
|
interpolator=fold_interpolator,
|
|
@@ -488,16 +702,19 @@ class GeologicalModel:
|
|
|
488
702
|
series_builder.add_data_from_data_frame(
|
|
489
703
|
self.data[self.data['feature_name'] == foliation_data])
|
|
490
704
|
self._add_faults(series_builder)
|
|
491
|
-
|
|
492
705
|
series_builder.add_data_to_interpolator(True)
|
|
493
|
-
|
|
494
|
-
|
|
706
|
+
fold_axis = kwargs.get('fold_axis',None)
|
|
707
|
+
if fold_axis is not None:
|
|
708
|
+
fold_axis = np.array(fold_axis)
|
|
709
|
+
if len(fold_axis.shape) == 1:
|
|
710
|
+
fold.fold_axis = fold_axis
|
|
711
|
+
|
|
495
712
|
if "av_fold_axis" in kwargs:
|
|
496
713
|
_calculate_average_intersection(series_builder, fold_frame, fold)
|
|
497
714
|
if fold.fold_axis is None:
|
|
498
715
|
far, fad = fold_frame.calculate_fold_axis_rotation(
|
|
499
716
|
series_builder)
|
|
500
|
-
fold_axis_rotation = FoldRotationAngle(far, fad)
|
|
717
|
+
fold_axis_rotation = FoldRotationAngle(far, fad,svario=svario)
|
|
501
718
|
a_wl = kwargs.get("axis_wl", None)
|
|
502
719
|
if 'axis_function' in kwargs:
|
|
503
720
|
# allow predefined function to be used
|
|
@@ -508,24 +725,26 @@ class GeologicalModel:
|
|
|
508
725
|
# give option of passing own fold limb rotation function
|
|
509
726
|
flr, fld = fold_frame.calculate_fold_limb_rotation(
|
|
510
727
|
series_builder)
|
|
511
|
-
fold_limb_rotation = FoldRotationAngle(flr, fld)
|
|
728
|
+
fold_limb_rotation = FoldRotationAngle(flr, fld,svario=svario)
|
|
512
729
|
l_wl = kwargs.get("limb_wl", None)
|
|
513
730
|
if 'limb_function' in kwargs:
|
|
514
731
|
# allow for predefined functions to be used
|
|
515
732
|
fold_limb_rotation.set_function(kwargs['limb_function'])
|
|
516
733
|
else:
|
|
517
|
-
fold_limb_rotation.fit_fourier_series(wl=l_wl)
|
|
734
|
+
fold_limb_rotation.fit_fourier_series(wl=l_wl,**kwargs)
|
|
518
735
|
fold.fold_limb_rotation = fold_limb_rotation
|
|
519
736
|
# fold_limb_fitter = kwargs.get("fold_limb_function",
|
|
520
737
|
# _interpolate_fold_limb_rotation_angle)
|
|
521
738
|
# fold_limb_fitter(series_builder, fold_frame, fold, result, **kwargs)
|
|
522
|
-
kwargs['fold_weights'] = kwargs.get('fold_weights',
|
|
739
|
+
kwargs['fold_weights'] = kwargs.get('fold_weights', {})
|
|
523
740
|
|
|
524
741
|
self._add_faults(series_builder)
|
|
525
742
|
# build feature
|
|
526
743
|
kwargs['cgw'] = 0.
|
|
527
744
|
kwargs['fold'] = fold
|
|
528
|
-
series_feature = series_builder.build(**kwargs)
|
|
745
|
+
# series_feature = series_builder.build(**kwargs)
|
|
746
|
+
series_feature = series_builder.feature
|
|
747
|
+
series_builder.build_arguments = kwargs
|
|
529
748
|
series_feature.type = 'series'
|
|
530
749
|
# see if any unconformities are above this feature if so add region
|
|
531
750
|
# self._add_unconformity_above(series_feature)self._add_feature(series_feature)
|
|
@@ -541,8 +760,10 @@ class GeologicalModel:
|
|
|
541
760
|
Parameters
|
|
542
761
|
----------
|
|
543
762
|
fold_frame_data : string
|
|
763
|
+
name of the feature to be added
|
|
544
764
|
|
|
545
|
-
fold_frame : StructuralFrame
|
|
765
|
+
fold_frame : StructuralFrame, optional
|
|
766
|
+
the fold frame for the fold if not specified uses last feature added
|
|
546
767
|
|
|
547
768
|
kwargs
|
|
548
769
|
|
|
@@ -551,16 +772,20 @@ class GeologicalModel:
|
|
|
551
772
|
fold_frame : FoldFrame
|
|
552
773
|
created fold frame
|
|
553
774
|
"""
|
|
775
|
+
if self.check_inialisation() == False:
|
|
776
|
+
return False
|
|
554
777
|
self.parameters['features'].append(
|
|
555
778
|
{'feature_type': 'folded_fold_frame', 'feature_name': fold_frame_data, 'fold_frame': fold_frame, **kwargs})
|
|
556
779
|
if fold_frame is None:
|
|
557
780
|
logger.info("Using last feature as fold frame")
|
|
558
781
|
fold_frame = self.features[-1]
|
|
559
782
|
assert type(fold_frame) == FoldFrame, "Please specify a FoldFrame"
|
|
560
|
-
fold = FoldEvent(fold_frame)
|
|
783
|
+
fold = FoldEvent(fold_frame,name='Fold_{}'.format(fold_frame_data))
|
|
561
784
|
fold_interpolator = self.get_interpolator("DFI", fold=fold, **kwargs)
|
|
785
|
+
gy_fold_interpolator = self.get_interpolator("DFI", fold=fold, **kwargs)
|
|
786
|
+
|
|
562
787
|
frame_interpolator = self.get_interpolator(**kwargs)
|
|
563
|
-
interpolators = [fold_interpolator,
|
|
788
|
+
interpolators = [fold_interpolator, gy_fold_interpolator,
|
|
564
789
|
frame_interpolator.copy()]
|
|
565
790
|
fold_frame_builder = StructuralFrameBuilder(
|
|
566
791
|
interpolators=interpolators, name=fold_frame_data, **kwargs)
|
|
@@ -569,13 +794,18 @@ class GeologicalModel:
|
|
|
569
794
|
|
|
570
795
|
## add the data to the interpolator for the main foliation
|
|
571
796
|
fold_frame_builder[0].add_data_to_interpolator(True)
|
|
797
|
+
|
|
572
798
|
if "fold_axis" in kwargs:
|
|
799
|
+
logger.info("Using cylindrical fold axis")
|
|
573
800
|
fold.fold_axis = kwargs['fold_axis']
|
|
574
801
|
if "av_fold_axis" in kwargs:
|
|
802
|
+
logger.info("Using average intersection lineation for \n"
|
|
803
|
+
"fold axis")
|
|
575
804
|
_calculate_average_intersection(fold_frame_builder[0], fold_frame,
|
|
576
805
|
fold)
|
|
577
806
|
|
|
578
807
|
if fold.fold_axis is None:
|
|
808
|
+
logger.info("Fitting fold axis rotation angle")
|
|
579
809
|
far, fad = fold_frame.calculate_fold_axis_rotation(
|
|
580
810
|
fold_frame_builder[0])
|
|
581
811
|
fold_axis_rotation = FoldRotationAngle(far, fad)
|
|
@@ -600,12 +830,11 @@ class GeologicalModel:
|
|
|
600
830
|
# fold_limb_fitter = kwargs.get("fold_limb_function",
|
|
601
831
|
# _interpolate_fold_limb_rotation_angle)
|
|
602
832
|
# fold_limb_fitter(series_builder, fold_frame, fold, result, **kwargs)
|
|
603
|
-
kwargs['fold_weights'] = kwargs.get('fold_weights',
|
|
833
|
+
kwargs['fold_weights'] = kwargs.get('fold_weights', {})
|
|
604
834
|
|
|
605
835
|
for i in range(3):
|
|
606
836
|
self._add_faults(fold_frame_builder[i])
|
|
607
837
|
# build feature
|
|
608
|
-
kwargs['cgw'] = 0.
|
|
609
838
|
kwargs['fold'] = fold
|
|
610
839
|
self._add_faults(fold_frame_builder[0])
|
|
611
840
|
self._add_faults(fold_frame_builder[1])
|
|
@@ -622,12 +851,14 @@ class GeologicalModel:
|
|
|
622
851
|
return fold_frame
|
|
623
852
|
|
|
624
853
|
def _add_faults(self, feature_builder, features=None):
|
|
625
|
-
"""
|
|
626
|
-
|
|
854
|
+
"""Adds all existing faults to a geological feature builder
|
|
855
|
+
|
|
627
856
|
Parameters
|
|
628
857
|
----------
|
|
629
|
-
feature_builder
|
|
630
|
-
|
|
858
|
+
feature_builder : GeologicalFeatureInterpolator/StructuralFrameBuilder
|
|
859
|
+
The feature buider to add the faults to
|
|
860
|
+
features : list, optional
|
|
861
|
+
A specific list of features rather than all features in the model
|
|
631
862
|
Returns
|
|
632
863
|
-------
|
|
633
864
|
|
|
@@ -736,6 +967,8 @@ class GeologicalModel:
|
|
|
736
967
|
Returns
|
|
737
968
|
-------
|
|
738
969
|
"""
|
|
970
|
+
if not self.check_initialisation():
|
|
971
|
+
return False
|
|
739
972
|
# self.parameters['features'].append({'feature_type':'unconformity','feature_name':unconformity_surface_data,**kwargs})
|
|
740
973
|
interpolator = self.get_interpolator(**kwargs)
|
|
741
974
|
unconformity_feature_builder = GeologicalFeatureInterpolator(
|
|
@@ -752,7 +985,9 @@ class GeologicalModel:
|
|
|
752
985
|
self._add_faults(unconformity_feature_builder)
|
|
753
986
|
|
|
754
987
|
# build feature
|
|
755
|
-
uc_feature_base = unconformity_feature_builder.build(**kwargs)
|
|
988
|
+
# uc_feature_base = unconformity_feature_builder.build(**kwargs)
|
|
989
|
+
uc_feature_base = unconformity_feature_builder.feature
|
|
990
|
+
unconformity_feature_builder.build_arguments = kwargs
|
|
756
991
|
uc_feature_base.type = 'unconformity_base'
|
|
757
992
|
# uc_feature = UnconformityFeature(uc_feature_base,0)
|
|
758
993
|
# iterate over existing features and add the unconformity as a
|
|
@@ -852,24 +1087,41 @@ class GeologicalModel:
|
|
|
852
1087
|
self._add_faults(domain_fault_feature_builder)
|
|
853
1088
|
|
|
854
1089
|
# build feature
|
|
855
|
-
domain_fault = domain_fault_feature_builder.build(**kwargs)
|
|
1090
|
+
# domain_fault = domain_fault_feature_builder.build(**kwargs)
|
|
1091
|
+
domain_fault = domain_fault_feature_builder.feature
|
|
1092
|
+
domain_fault_feature_builder.build_arguments = kwargs
|
|
856
1093
|
domain_fault.type = 'domain_fault'
|
|
857
1094
|
self._add_feature(domain_fault)
|
|
858
1095
|
self._add_domain_fault_below(domain_fault)
|
|
859
1096
|
|
|
860
|
-
|
|
1097
|
+
domain_fault_uc = UnconformityFeature(domain_fault,0)
|
|
861
1098
|
# iterate over existing features and add the unconformity as a
|
|
862
1099
|
# region so the feature is only
|
|
863
1100
|
# evaluated where the unconformity is positive
|
|
864
|
-
return
|
|
865
|
-
|
|
866
|
-
def create_and_add_fault(self,
|
|
1101
|
+
return domain_fault_uc
|
|
1102
|
+
|
|
1103
|
+
def create_and_add_fault(self,
|
|
1104
|
+
fault_surface_data,
|
|
1105
|
+
displacement,
|
|
1106
|
+
fault_slip_vector=None,
|
|
1107
|
+
fault_center = None,
|
|
1108
|
+
fault_extent = None,
|
|
1109
|
+
fault_influence = None,
|
|
1110
|
+
fault_vectical_radius = None,
|
|
1111
|
+
faultfunction=None,
|
|
1112
|
+
**kwargs):
|
|
867
1113
|
"""
|
|
868
1114
|
Parameters
|
|
869
1115
|
----------
|
|
870
1116
|
fault_surface_data : string
|
|
871
1117
|
name of the fault surface data in the dataframe
|
|
872
1118
|
displacement : displacement magnitude
|
|
1119
|
+
fault_extent : [type], optional
|
|
1120
|
+
[description], by default None
|
|
1121
|
+
fault_influence : [type], optional
|
|
1122
|
+
[description], by default None
|
|
1123
|
+
fault_vectical_radius : [type], optional
|
|
1124
|
+
[description], by default None
|
|
873
1125
|
kwargs : additional kwargs for Fault and interpolators
|
|
874
1126
|
|
|
875
1127
|
Returns
|
|
@@ -879,98 +1131,65 @@ class GeologicalModel:
|
|
|
879
1131
|
"""
|
|
880
1132
|
self.parameters['features'].append(
|
|
881
1133
|
{'feature_type': 'fault', 'feature_name': fault_surface_data, 'displacement': displacement, **kwargs})
|
|
882
|
-
|
|
1134
|
+
if 'data_region' in kwargs:
|
|
1135
|
+
kwargs.pop('data_region')
|
|
1136
|
+
logger.error("kwarg data_region currently not supported, disabling")
|
|
883
1137
|
displacement_scaled = displacement / self.scale_factor
|
|
884
1138
|
# create fault frame
|
|
885
1139
|
interpolator = self.get_interpolator(**kwargs)
|
|
886
|
-
fault_frame_builder =
|
|
1140
|
+
fault_frame_builder = FaultBuilder(interpolator,
|
|
887
1141
|
name=fault_surface_data,
|
|
888
1142
|
**kwargs)
|
|
889
1143
|
# add data
|
|
890
|
-
fault_frame_data = self.data[
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
#
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
coord1 = pd.DataFrame([[loc[0], loc[1], loc[2], 0, 0, -1]],
|
|
923
|
-
columns=normal_vector_headers())
|
|
924
|
-
coord1['coord'] = 1
|
|
925
|
-
fault_frame_data = pd.concat([fault_frame_data, coord1],
|
|
926
|
-
sort=False)
|
|
927
|
-
|
|
928
|
-
if fault_frame_data[fault_frame_data['coord'] == 2].shape[0] == 0:
|
|
929
|
-
logger.info("Adding fault extent data as first and last point")
|
|
930
|
-
## first and last point of the line
|
|
931
|
-
value_data = fault_frame_data[fault_frame_data['val'] == 0]
|
|
932
|
-
coord2 = value_data.iloc[[0, len(value_data) - 1]]
|
|
933
|
-
coord2 = coord2.reset_index(drop=True)
|
|
934
|
-
c2_scale = kwargs.get('length_scale',1.)
|
|
935
|
-
coord2.loc[0, 'val'] = -1/c2_scale
|
|
936
|
-
coord2.loc[1, 'val'] = 1/c2_scale
|
|
937
|
-
coord2['coord'] = 2
|
|
938
|
-
fault_frame_data = pd.concat([fault_frame_data, coord2],
|
|
939
|
-
sort=False)
|
|
940
|
-
fault_frame_builder.add_data_from_data_frame(fault_frame_data)
|
|
941
|
-
# if there is no fault slip data then we could find the strike of
|
|
942
|
-
# the fault and build
|
|
943
|
-
# the second coordinate
|
|
944
|
-
# if we add a region to the fault then the fault operator doesn't
|
|
945
|
-
# work but for visualisation
|
|
946
|
-
# we want to add a region!
|
|
947
|
-
|
|
948
|
-
if 'splayregion' in kwargs and 'splay' in kwargs:
|
|
949
|
-
# result['splayregionfeature'] = RegionFeature(kwargs['splayregion'])
|
|
950
|
-
# apply splay to all parts of fault frame
|
|
951
|
-
for i in range(3):
|
|
952
|
-
# work out the values of the nodes where we want hard
|
|
953
|
-
# constraints
|
|
954
|
-
idc = np.arange(0, interpolator.support.n_nodes)[
|
|
955
|
-
kwargs['splayregion'](interpolator.support.nodes)]
|
|
956
|
-
val = kwargs['splay'][i].evaluate_value(
|
|
957
|
-
interpolator.support.nodes[
|
|
958
|
-
kwargs['splayregion'](interpolator.support.nodes), :])
|
|
959
|
-
mask = ~np.isnan(val)
|
|
960
|
-
fault_frame_builder[i].interpolator.add_equality_constraints(
|
|
961
|
-
idc[mask], val[mask])
|
|
1144
|
+
fault_frame_data = self.data[ self.data['feature_name'] == fault_surface_data].copy()
|
|
1145
|
+
mask = np.logical_and(fault_frame_data['coord']==0,~np.isnan(fault_frame_data['gz']))
|
|
1146
|
+
fault_normal_vector = fault_frame_data.loc[mask,['gx','gy','gz']].mean(axis=0).to_numpy()
|
|
1147
|
+
mask = np.logical_and(fault_frame_data['coord']==1,~np.isnan(fault_frame_data['gz']))
|
|
1148
|
+
if fault_slip_vector is None:
|
|
1149
|
+
fault_slip_vector = fault_frame_data.loc[mask,['gx','gy','gz']].mean(axis=0).to_numpy()
|
|
1150
|
+
if fault_center is not None:
|
|
1151
|
+
fault_center = self.scale(fault_center,inplace=False)
|
|
1152
|
+
if fault_center is None:
|
|
1153
|
+
# if we haven't defined a fault centre take the center of mass for lines assocaited with
|
|
1154
|
+
# the fault trace
|
|
1155
|
+
mask = np.logical_and(fault_frame_data['coord']==0,fault_frame_data['val']==0)
|
|
1156
|
+
fault_center = fault_frame_data.loc[mask,['X','Y','Z']].mean(axis=0).to_numpy()
|
|
1157
|
+
if fault_influence:
|
|
1158
|
+
fault_influence=fault_influence/self.scale_factor
|
|
1159
|
+
if fault_extent:
|
|
1160
|
+
fault_extent=fault_extent/self.scale_factor
|
|
1161
|
+
if fault_vectical_radius:
|
|
1162
|
+
fault_vectical_radius=fault_vectical_radius/self.scale_factor
|
|
1163
|
+
fault_frame_builder.create_data_from_geometry(fault_frame_data,
|
|
1164
|
+
fault_center,
|
|
1165
|
+
fault_normal_vector,
|
|
1166
|
+
fault_slip_vector,
|
|
1167
|
+
influence_distance=fault_influence,
|
|
1168
|
+
horizontal_radius=fault_extent,
|
|
1169
|
+
vertical_radius=fault_vectical_radius
|
|
1170
|
+
)
|
|
1171
|
+
if fault_influence == None or fault_extent == None or fault_vectical_radius == None:
|
|
1172
|
+
fault_frame_builder.origin = self.origin
|
|
1173
|
+
fault_frame_builder.maximum = self.maximum
|
|
1174
|
+
fault_frame_builder.set_mesh_geometry(kwargs.get('fault_buffer',0.1))
|
|
1175
|
+
# fault_frame_builder.add_data_from_data_frame(fault_frame_data)
|
|
962
1176
|
# check if this fault overprint any existing faults exist in the stack
|
|
963
1177
|
overprinted = kwargs.get('overprinted', [])
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1178
|
+
overprinted_faults = []
|
|
1179
|
+
for o in overprinted:
|
|
1180
|
+
overprinted_faults.append(self.features[self.feature_name_index[o]])
|
|
1181
|
+
self._add_faults(fault_frame_builder[0],overprinted_faults)
|
|
1182
|
+
self._add_faults(fault_frame_builder[1],overprinted_faults)
|
|
1183
|
+
self._add_faults(fault_frame_builder[2],overprinted_faults)
|
|
967
1184
|
|
|
968
1185
|
fault_frame = fault_frame_builder.build(**kwargs)
|
|
969
1186
|
if 'abut' in kwargs:
|
|
970
1187
|
fault_frame[0].add_region(lambda pos: kwargs['abut'].evaluate(pos))
|
|
971
1188
|
|
|
972
1189
|
fault = FaultSegment(fault_frame, displacement=displacement_scaled,
|
|
1190
|
+
faultfunction=faultfunction,
|
|
973
1191
|
**kwargs)
|
|
1192
|
+
fault.builder=fault_frame_builder
|
|
974
1193
|
for f in reversed(self.features):
|
|
975
1194
|
if f.type == 'unconformity':
|
|
976
1195
|
fault.add_region(lambda pos: f.evaluate_value(pos) <= 0)
|
|
@@ -982,50 +1201,54 @@ class GeologicalModel:
|
|
|
982
1201
|
|
|
983
1202
|
return fault
|
|
984
1203
|
|
|
985
|
-
def rescale(self, points):
|
|
1204
|
+
def rescale(self, points, inplace=True):
|
|
986
1205
|
"""
|
|
987
1206
|
Convert from model scale to real world scale - in the future this
|
|
988
1207
|
should also do transformations?
|
|
989
1208
|
|
|
990
1209
|
Parameters
|
|
991
1210
|
----------
|
|
992
|
-
points
|
|
1211
|
+
points : np.array((N,3),dtype=double)
|
|
1212
|
+
inplace : boolean
|
|
1213
|
+
whether to return a modified copy or modify the original array
|
|
993
1214
|
|
|
994
1215
|
Returns
|
|
995
1216
|
-------
|
|
1217
|
+
points : np.array((N,3),dtype=double)
|
|
1218
|
+
|
|
996
1219
|
"""
|
|
1220
|
+
if inplace == False:
|
|
1221
|
+
points = points.copy()
|
|
997
1222
|
points *= self.scale_factor
|
|
998
1223
|
points += self.origin
|
|
999
1224
|
return points
|
|
1000
1225
|
|
|
1001
|
-
def scale(self, points):
|
|
1002
|
-
"""
|
|
1226
|
+
def scale(self, points, inplace=True):
|
|
1227
|
+
""" Take points in UTM coordinates and reproject
|
|
1228
|
+
into scaled model space
|
|
1229
|
+
|
|
1003
1230
|
Parameters
|
|
1004
1231
|
----------
|
|
1005
1232
|
points : np.array((N,3),dtype=float)
|
|
1006
1233
|
points to
|
|
1007
|
-
|
|
1234
|
+
inplace : bool, optional default = True
|
|
1235
|
+
whether to copy the points array or update the passed array
|
|
1008
1236
|
Returns
|
|
1009
1237
|
-------
|
|
1238
|
+
points : np.array((N,3),dtype=double)
|
|
1239
|
+
|
|
1010
1240
|
"""
|
|
1011
|
-
points = points.
|
|
1012
|
-
|
|
1241
|
+
points = np.array(points).astype(float)
|
|
1242
|
+
if inplace==False:
|
|
1243
|
+
points = points.copy()
|
|
1244
|
+
# if len(points.shape) == 1:
|
|
1245
|
+
# points = points[None,:]
|
|
1246
|
+
# if len(points.shape) != 2:
|
|
1247
|
+
# logger.error("cannot scale array of dimensions".format(len(points.shape)))
|
|
1248
|
+
points -= self.origin
|
|
1013
1249
|
points /= self.scale_factor
|
|
1014
1250
|
return points
|
|
1015
1251
|
|
|
1016
|
-
def voxet(self, nsteps=(50, 50, 25)):
|
|
1017
|
-
"""
|
|
1018
|
-
Returns a voxet dict with the nsteps specified
|
|
1019
|
-
|
|
1020
|
-
Parameters
|
|
1021
|
-
----------
|
|
1022
|
-
nsteps : tuple
|
|
1023
|
-
number of cells in
|
|
1024
|
-
|
|
1025
|
-
Returns
|
|
1026
|
-
-------
|
|
1027
|
-
"""
|
|
1028
|
-
return {'bounding_box': self.bounding_box, 'nsteps': nsteps}
|
|
1029
1252
|
|
|
1030
1253
|
def regular_grid(self, nsteps=(50, 50, 25), shuffle = True, rescale=False):
|
|
1031
1254
|
"""
|
|
@@ -1050,12 +1273,13 @@ class GeologicalModel:
|
|
|
1050
1273
|
xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
1051
1274
|
locs = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
1052
1275
|
if shuffle:
|
|
1276
|
+
logger.info("Shuffling points")
|
|
1053
1277
|
np.random.shuffle(locs)
|
|
1054
1278
|
if rescale:
|
|
1055
1279
|
locs = self.rescale(locs)
|
|
1056
1280
|
return locs
|
|
1057
1281
|
|
|
1058
|
-
def evaluate_model(self, xyz,
|
|
1282
|
+
def evaluate_model(self, xyz, scale=True):
|
|
1059
1283
|
"""Evaluate the stratigraphic id at each location
|
|
1060
1284
|
|
|
1061
1285
|
|
|
@@ -1063,8 +1287,8 @@ class GeologicalModel:
|
|
|
1063
1287
|
----------
|
|
1064
1288
|
xyz : np.array((N,3),dtype=float)
|
|
1065
1289
|
locations
|
|
1066
|
-
|
|
1067
|
-
whether to rescale the model
|
|
1290
|
+
scale : bool
|
|
1291
|
+
whether to rescale the xyz before evaluating model
|
|
1068
1292
|
|
|
1069
1293
|
Returns
|
|
1070
1294
|
-------
|
|
@@ -1075,11 +1299,11 @@ class GeologicalModel:
|
|
|
1075
1299
|
--------
|
|
1076
1300
|
Evaluate on a voxet
|
|
1077
1301
|
|
|
1078
|
-
>>> x = np.linspace(
|
|
1302
|
+
>>> x = np.linspace(model.bounding_box[0, 0], model.bounding_box[1, 0],
|
|
1079
1303
|
nsteps[0])
|
|
1080
|
-
>>> y = np.linspace(
|
|
1304
|
+
>>> y = np.linspace(model.bounding_box[0, 1], model.bounding_box[1, 1],
|
|
1081
1305
|
nsteps[1])
|
|
1082
|
-
>>> z = np.linspace(
|
|
1306
|
+
>>> z = np.linspace(model.bounding_box[1, 2], model.bounding_box[0, 2],
|
|
1083
1307
|
nsteps[2])
|
|
1084
1308
|
>>> xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
1085
1309
|
>>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
@@ -1102,8 +1326,13 @@ class GeologicalModel:
|
|
|
1102
1326
|
>>> model.evaluate_model(xyz)
|
|
1103
1327
|
|
|
1104
1328
|
"""
|
|
1329
|
+
xyz = np.array(xyz)
|
|
1330
|
+
if scale:
|
|
1331
|
+
xyz = self.scale(xyz,inplace=False)
|
|
1105
1332
|
strat_id = np.zeros(xyz.shape[0],dtype=int)
|
|
1106
1333
|
for group in self.stratigraphic_column.keys():
|
|
1334
|
+
if group == 'faults':
|
|
1335
|
+
continue
|
|
1107
1336
|
feature_id = self.feature_name_index.get(group, -1)
|
|
1108
1337
|
if feature_id >= 0:
|
|
1109
1338
|
feature = self.features[feature_id]
|
|
@@ -1114,6 +1343,31 @@ class GeologicalModel:
|
|
|
1114
1343
|
logger.error('Model does not contain {}'.format(group))
|
|
1115
1344
|
return strat_id
|
|
1116
1345
|
|
|
1346
|
+
def evaluate_fault_displacements(self,points,scale=True):
|
|
1347
|
+
"""Evaluate the fault displacement magnitude at each location
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
Parameters
|
|
1351
|
+
----------
|
|
1352
|
+
xyz : np.array((N,3),dtype=float)
|
|
1353
|
+
locations
|
|
1354
|
+
scale : bool
|
|
1355
|
+
whether to rescale the xyz before evaluating model
|
|
1356
|
+
|
|
1357
|
+
Returns
|
|
1358
|
+
-------
|
|
1359
|
+
fault_displacement : np.array(N,dtype=float)
|
|
1360
|
+
the fault displacement magnitude
|
|
1361
|
+
"""
|
|
1362
|
+
if scale:
|
|
1363
|
+
points = self.scale(points,inplace=False)
|
|
1364
|
+
vals = np.zeros(points.shape[0])
|
|
1365
|
+
for f in self.features:
|
|
1366
|
+
if f.type == 'fault':
|
|
1367
|
+
disp = f.displacementfeature.evaluate_value(points)
|
|
1368
|
+
vals[~np.isnan(disp)] += disp[~np.isnan(disp)]
|
|
1369
|
+
return vals*-self.scale_factor # convert from restoration magnutude to displacement
|
|
1370
|
+
|
|
1117
1371
|
def get_feature_by_name(self, feature_name):
|
|
1118
1372
|
"""Returns a feature from the mode given a name
|
|
1119
1373
|
|
|
@@ -1127,11 +1381,15 @@ class GeologicalModel:
|
|
|
1127
1381
|
-------
|
|
1128
1382
|
feature : GeologicalFeature
|
|
1129
1383
|
the geological feature with the specified name, or none if no feature
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
|
|
1130
1387
|
"""
|
|
1131
1388
|
feature_index = self.feature_name_index.get(feature_name,-1)
|
|
1132
|
-
if feature_index >
|
|
1389
|
+
if feature_index > -1:
|
|
1133
1390
|
return self.features[feature_index]
|
|
1134
1391
|
else:
|
|
1392
|
+
logger.error("{} does not exist!".format(feature_name))
|
|
1135
1393
|
return None
|
|
1136
1394
|
|
|
1137
1395
|
def evaluate_feature_value(self, feature_name, xyz, scale=True):
|
|
@@ -1151,11 +1409,31 @@ class GeologicalModel:
|
|
|
1151
1409
|
-------
|
|
1152
1410
|
np.array((N))
|
|
1153
1411
|
vector of scalar values
|
|
1412
|
+
|
|
1413
|
+
Examples
|
|
1414
|
+
--------
|
|
1415
|
+
Evaluate on a voxet using model boundaries
|
|
1416
|
+
|
|
1417
|
+
>>> x = np.linspace(model.bounding_box[0, 0], model.bounding_box[1, 0],
|
|
1418
|
+
nsteps[0])
|
|
1419
|
+
>>> y = np.linspace(model.bounding_box[0, 1], model.bounding_box[1, 1],
|
|
1420
|
+
nsteps[1])
|
|
1421
|
+
>>> z = np.linspace(model.bounding_box[1, 2], model.bounding_box[0, 2],
|
|
1422
|
+
nsteps[2])
|
|
1423
|
+
>>> xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
1424
|
+
>>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
1425
|
+
>>> model.evaluate_feature_vaue('feature',xyz,scale=False)
|
|
1426
|
+
|
|
1427
|
+
Evaluate on points in UTM coordinates
|
|
1428
|
+
|
|
1429
|
+
>>> model.evaluate_feature_vaue('feature',utm_xyz)
|
|
1430
|
+
|
|
1154
1431
|
"""
|
|
1155
1432
|
feature = self.get_feature_by_name(feature_name)
|
|
1156
1433
|
if feature:
|
|
1434
|
+
scaled_xyz = xyz
|
|
1157
1435
|
if scale:
|
|
1158
|
-
scaled_xyz = self.scale(xyz)
|
|
1436
|
+
scaled_xyz = self.scale(xyz,inplace=False)
|
|
1159
1437
|
return feature.evaluate_value(scaled_xyz)
|
|
1160
1438
|
else:
|
|
1161
1439
|
return np.zeros(xyz.shape[0])
|
|
@@ -1179,8 +1457,48 @@ class GeologicalModel:
|
|
|
1179
1457
|
"""
|
|
1180
1458
|
feature = self.get_feature_by_name(feature_name)
|
|
1181
1459
|
if feature:
|
|
1460
|
+
scaled_xyz = xyz
|
|
1182
1461
|
if scale:
|
|
1183
|
-
scaled_xyz = self.scale(xyz)
|
|
1462
|
+
scaled_xyz = self.scale(xyz, inplace = False)
|
|
1184
1463
|
return feature.evaluate_gradient(scaled_xyz)
|
|
1185
1464
|
else:
|
|
1186
|
-
return np.zeros(xyz.shape[0])
|
|
1465
|
+
return np.zeros(xyz.shape[0])
|
|
1466
|
+
|
|
1467
|
+
def update(self,verbose=False,progressbar=True):
|
|
1468
|
+
total_dof = 0
|
|
1469
|
+
nfeatures = 0
|
|
1470
|
+
for f in self.features:
|
|
1471
|
+
if f.type=='fault':
|
|
1472
|
+
nfeatures+=3
|
|
1473
|
+
total_dof+=f[0].interpolator.nx*3
|
|
1474
|
+
if f.type == 'series':
|
|
1475
|
+
nfeatures+=1
|
|
1476
|
+
total_dof+=f.interpolator.nx
|
|
1477
|
+
if verbose==True:
|
|
1478
|
+
print('Updating geological model. There are: \n'
|
|
1479
|
+
'{} geological features that need to be interpolated\n'.format(nfeatures)
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
from tqdm.auto import tqdm
|
|
1483
|
+
import time
|
|
1484
|
+
start = time.time()
|
|
1485
|
+
sizecounter = 0
|
|
1486
|
+
|
|
1487
|
+
# Load tqdm with size counter instead of file counter
|
|
1488
|
+
with tqdm(total=nfeatures) as pbar:
|
|
1489
|
+
buf=0
|
|
1490
|
+
for f in self.features:
|
|
1491
|
+
pbar.set_description('Interpolating {}'.format(f.name))
|
|
1492
|
+
if f.type == 'fault':
|
|
1493
|
+
for i in range(3):
|
|
1494
|
+
buf+=1
|
|
1495
|
+
f[i].builder.update()
|
|
1496
|
+
pbar.update()
|
|
1497
|
+
if f.type == 'series':
|
|
1498
|
+
f.builder.update()
|
|
1499
|
+
pbar.update()
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
if verbose:
|
|
1503
|
+
print("Model update took: {} seconds".format(time.time()-start))
|
|
1504
|
+
|