LoopStructural 1.0.4__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-37.pyc → __init__.cpython-36.pyc} +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/{_base.cpython-37.pyc → _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 +3899 -2455
- 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 +53 -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 +68 -11
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/geological_interpolator.py +8 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/operator.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/piecewiselinear_interpolator.py +97 -8
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_grid.py +25 -69
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_tetra.py +86 -43
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/surfe_wrapper.py +4 -3
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__pycache__/{__init__.cpython-37.pyc → __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 +303 -150
- 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-37.pyc → 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 +10 -44
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature_builder.py +127 -43
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/lambda_geological_feature.py +31 -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 +25 -15
- 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-37.pyc → __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 +6 -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 +5 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/logging.py +60 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/map2loop.py +47 -19
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/regions.py +11 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/utils.py +2 -53
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/{__init__.cpython-37.pyc → __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-37.pyc → 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 +17 -2
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_plotter.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_visualisation.py +152 -84
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/rotation_angle_plotter.py +2 -1
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/stratigraphic_column.py +60 -0
- Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.4-py3.7.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/PKG-INFO +1 -1
- Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.4-py3.7.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/SOURCES.txt +10 -5
- Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.4-py3.7.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/requires.txt +1 -1
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/__init__.cpython-36.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_fold_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/finite_difference_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/geological_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/operator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/piecewiselinear_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_grid.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_tetra.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/surfe_wrapper.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.cp37-win_amd64.pyd +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_segment.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/cross_product_geological_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature_builder.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame_builder.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/unconformity_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle_feature.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/foldframe.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/svariogram.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/exceptions.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/helper.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/map2loop.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/utils.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/map_viewer.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_plotter.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_visualisation.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/rotation_angle_plotter.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/__init__.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_faults.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_fold.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_interpolator.cpython-37.pyc +0 -0
- Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_refolded.cpython-37.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.4-py3.7.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.4-py3.7.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/top_level.txt +0 -0
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main entry point for creating a geological model
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import networkx as nx
|
|
9
|
+
|
|
10
|
+
from LoopStructural.datasets import normal_vector_headers
|
|
11
|
+
from LoopStructural.interpolators.discrete_fold_interpolator import \
|
|
12
|
+
DiscreteFoldInterpolator as DFI
|
|
13
|
+
from LoopStructural.interpolators.finite_difference_interpolator import \
|
|
14
|
+
FiniteDifferenceInterpolator as FDI
|
|
15
|
+
from LoopStructural.interpolators.piecewiselinear_interpolator import \
|
|
16
|
+
PiecewiseLinearInterpolator as PLI
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from LoopStructural.interpolators.surfe_wrapper import \
|
|
20
|
+
SurfeRBFInterpolator as Surfe
|
|
21
|
+
|
|
22
|
+
surfe = True
|
|
23
|
+
|
|
24
|
+
except ImportError:
|
|
25
|
+
surfe = False
|
|
26
|
+
|
|
27
|
+
from LoopStructural.interpolators.structured_grid import StructuredGrid
|
|
28
|
+
from LoopStructural.interpolators.structured_tetra import TetMesh
|
|
29
|
+
from LoopStructural.modelling.fault.fault_segment import FaultSegment
|
|
30
|
+
from LoopStructural.modelling.features import (GeologicalFeatureInterpolator,
|
|
31
|
+
RegionFeature,
|
|
32
|
+
StructuralFrameBuilder,
|
|
33
|
+
UnconformityFeature)
|
|
34
|
+
from LoopStructural.modelling.fold import FoldRotationAngle
|
|
35
|
+
from LoopStructural.modelling.fold.fold import FoldEvent
|
|
36
|
+
from LoopStructural.modelling.fold.foldframe import FoldFrame
|
|
37
|
+
from LoopStructural.utils.exceptions import LoopBaseException
|
|
38
|
+
from LoopStructural.utils.helper import (all_heading, gradient_vec_names,
|
|
39
|
+
strike_dip_vector)
|
|
40
|
+
|
|
41
|
+
from LoopStructural.utils import getLogger, log_to_file
|
|
42
|
+
logger = getLogger(__name__)
|
|
43
|
+
if not surfe:
|
|
44
|
+
logger.warning("Cannot import Surfe")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _calculate_average_intersection(series_builder, fold_frame, fold,
|
|
48
|
+
**kwargs):
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
series_builder
|
|
54
|
+
fold_frame
|
|
55
|
+
fold
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
l2 = fold_frame.calculate_intersection_lineation(
|
|
62
|
+
series_builder)
|
|
63
|
+
fold.fold_axis = np.mean(l2, axis=0)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class GeologicalModel:
|
|
67
|
+
"""
|
|
68
|
+
A geological model is the recipe for building a 3D model and can include
|
|
69
|
+
the rescaling of the model between 0 and 1.
|
|
70
|
+
|
|
71
|
+
Attributes
|
|
72
|
+
----------
|
|
73
|
+
features : list
|
|
74
|
+
Contains all features youngest to oldest
|
|
75
|
+
feature_name_index : dict
|
|
76
|
+
maps feature name to the list index of the features
|
|
77
|
+
data : pandas dataframe
|
|
78
|
+
the dataframe used for building the geological model
|
|
79
|
+
nsteps : tuple/np.array(3,dtype=int)
|
|
80
|
+
the number of steps x,y,z to evaluate the model
|
|
81
|
+
origin : tuple/np.array(3,dtype=doubles)
|
|
82
|
+
the origin of the model box
|
|
83
|
+
parameters : dict
|
|
84
|
+
a dictionary tracking the parameters used to build the model
|
|
85
|
+
scale_factor : double
|
|
86
|
+
the scale factor used to rescale the model
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
def __init__(self, origin, maximum, rescale=True, nsteps=(40, 40, 40),
|
|
91
|
+
reuse_supports=False, logfile=None, loglevel='info'):
|
|
92
|
+
"""
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
origin : numpy array
|
|
96
|
+
specifying the origin of the model
|
|
97
|
+
maximum : numpy array
|
|
98
|
+
specifying the maximum extent of the model
|
|
99
|
+
rescale : bool
|
|
100
|
+
whether to rescale the model to between 0/1
|
|
101
|
+
|
|
102
|
+
Examples
|
|
103
|
+
--------
|
|
104
|
+
Demo data
|
|
105
|
+
|
|
106
|
+
>>> from LoopStructural.datasets import load_claudius
|
|
107
|
+
>>> from LoopStructural import GeologicalModel
|
|
108
|
+
|
|
109
|
+
>>> data, bb = load_claudius()
|
|
110
|
+
|
|
111
|
+
>>> model = GeologicalModel(bb[:,0],bb[:,1]
|
|
112
|
+
>>> model.set_model_data(data)
|
|
113
|
+
>>> model.create_and_add_foliation('strati')
|
|
114
|
+
|
|
115
|
+
>>> y = np.linspace(model.bounding_box[0, 1], model.bounding_box[1, 1],
|
|
116
|
+
nsteps[1])
|
|
117
|
+
>>> z = np.linspace(model.bounding_box[1, 2], model.bounding_box[0, 2],
|
|
118
|
+
nsteps[2])
|
|
119
|
+
>>> xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
120
|
+
>>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
121
|
+
>>> model.evaluate_feature_value('strati',xyz,scale=False)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
if logfile:
|
|
126
|
+
self.logfile = logfile
|
|
127
|
+
log_to_file(logfile,loglevel)
|
|
128
|
+
|
|
129
|
+
logger.info('Initialising geological model')
|
|
130
|
+
self.features = []
|
|
131
|
+
self.feature_name_index = {}
|
|
132
|
+
self.data = None
|
|
133
|
+
self.nsteps = nsteps
|
|
134
|
+
self._str = 'Instance of LoopStructural.GeologicalModel \n'
|
|
135
|
+
self._str += '------------------------------------------ \n'
|
|
136
|
+
# we want to rescale the model area so that the maximum length is
|
|
137
|
+
# 1
|
|
138
|
+
self.origin = np.array(origin).astype(float)
|
|
139
|
+
originstr = 'Model origin: {} {} {}'.format(self.origin[0],self.origin[1],self.origin[2])
|
|
140
|
+
logger.info(originstr)
|
|
141
|
+
self._str+=originstr+'\n'
|
|
142
|
+
self.maximum = np.array(maximum).astype(float)
|
|
143
|
+
maximumstr = 'Model maximum: {} {} {}'.format(self.maximum[0],self.maximum[1],self.maximum[2])
|
|
144
|
+
logger.info(maximumstr)
|
|
145
|
+
self._str+=maximumstr+'\n'
|
|
146
|
+
|
|
147
|
+
lengths = self.maximum - self.origin
|
|
148
|
+
self.scale_factor = 1.
|
|
149
|
+
self.bounding_box = np.zeros((2, 3))
|
|
150
|
+
self.bounding_box[1, :] = self.maximum - self.origin
|
|
151
|
+
self.bounding_box[1, :] = self.maximum - self.origin
|
|
152
|
+
if rescale:
|
|
153
|
+
self.scale_factor = np.max(lengths)
|
|
154
|
+
logger.info('Rescaling model using scale factor {}'.format(self.scale_factor))
|
|
155
|
+
self._str+='Model rescale factor: {} \n'.format(self.scale_factor)
|
|
156
|
+
self._str+='The model contains {} GeologicalFeatures \n'.format(len(self.features))
|
|
157
|
+
self._str+=''
|
|
158
|
+
self._str += '------------------------------------------ \n'
|
|
159
|
+
self._str += ''
|
|
160
|
+
self.bounding_box /= self.scale_factor
|
|
161
|
+
self.support = {}
|
|
162
|
+
self.reuse_supports = reuse_supports
|
|
163
|
+
if self.reuse_supports:
|
|
164
|
+
logger.warning("Supports are shared between geological features \n"
|
|
165
|
+
"this may cause unexpected behaviour and should only\n"
|
|
166
|
+
"be use by advanced users")
|
|
167
|
+
logger.info('Reusing interpolation supports: {}'.format(self.reuse_supports))
|
|
168
|
+
self.stratigraphic_column = None
|
|
169
|
+
self.feature_graph = nx.DiGraph()
|
|
170
|
+
self.parameters = {'features': [], 'model': {'bounding_box': self.origin.tolist() + self.maximum.tolist(),
|
|
171
|
+
'rescale': rescale,
|
|
172
|
+
'nsteps': nsteps,
|
|
173
|
+
'reuse_supports': reuse_supports}}
|
|
174
|
+
|
|
175
|
+
def __str__(self):
|
|
176
|
+
return self._str
|
|
177
|
+
|
|
178
|
+
def _ipython_key_completions_(self):
|
|
179
|
+
return self.feature_name_index.keys()
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def from_map2loop_directory(cls, m2l_directory,**kwargs):
|
|
183
|
+
"""Alternate constructor for a geological model using m2l output
|
|
184
|
+
|
|
185
|
+
Uses the information saved in the map2loop files to build a geological model.
|
|
186
|
+
You can specify kwargs for building foliation using foliation_params and for
|
|
187
|
+
faults using fault_params. faults is a flag that allows for the faults to be skipped.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
m2l_directory : string
|
|
192
|
+
path to map2loop directory
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
(GeologicalModel, dict)
|
|
197
|
+
the created geological model and a dictionary of the map2loop data
|
|
198
|
+
"""
|
|
199
|
+
from LoopStructural.utils import build_model, process_map2loop
|
|
200
|
+
logger.info('LoopStructural model initialised from m2l directory: {}'.format(m2l_directory))
|
|
201
|
+
m2lflags = kwargs.pop('m2lflags',{})
|
|
202
|
+
m2l_data = process_map2loop(m2l_directory,m2lflags)
|
|
203
|
+
return build_model(m2l_data,**kwargs), m2l_data
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def from_file(cls, file):
|
|
207
|
+
"""Load a geological model from file
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
file : string
|
|
212
|
+
path to the file
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
GeologicalModel
|
|
217
|
+
the geological model object
|
|
218
|
+
"""
|
|
219
|
+
try:
|
|
220
|
+
import dill as pickle
|
|
221
|
+
except ImportError:
|
|
222
|
+
logger.error("Cannot import from file, dill not installed")
|
|
223
|
+
return None
|
|
224
|
+
model = pickle.load(open(file,'rb'))
|
|
225
|
+
if type(model) == GeologicalModel:
|
|
226
|
+
logger.info('GeologicalModel initialised from file')
|
|
227
|
+
return model
|
|
228
|
+
else:
|
|
229
|
+
logger.error('{} does not contain a geological model'.format(file))
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
def __getitem__(self, feature_name):
|
|
233
|
+
"""Accessor for feature in features using feature_name_index
|
|
234
|
+
|
|
235
|
+
Parameters
|
|
236
|
+
----------
|
|
237
|
+
feature_name : string
|
|
238
|
+
name of the feature to return
|
|
239
|
+
"""
|
|
240
|
+
return self.get_feature_by_name(feature_name)
|
|
241
|
+
|
|
242
|
+
def feature_names(self):
|
|
243
|
+
return self.feature_name_index.keys()
|
|
244
|
+
|
|
245
|
+
def fault_names(self):
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
def check_inialisation(self):
|
|
249
|
+
if self.data is None:
|
|
250
|
+
logger.error("Data not associated with GeologicalModel. Run set_data")
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
def to_file(self, file):
|
|
254
|
+
"""Save a model to a pickle file requires dill
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
file : string
|
|
259
|
+
path to file location
|
|
260
|
+
"""
|
|
261
|
+
try:
|
|
262
|
+
import dill as pickle
|
|
263
|
+
except ImportError:
|
|
264
|
+
logger.error("Cannot write to file, dill not installed \n"
|
|
265
|
+
"pip install dill")
|
|
266
|
+
return
|
|
267
|
+
try:
|
|
268
|
+
logger.info('Writing GeologicalModel to: {}'.format(file))
|
|
269
|
+
pickle.dump(self,open(file,'wb'))
|
|
270
|
+
except pickle.PicklingError:
|
|
271
|
+
logger.error('Error saving file')
|
|
272
|
+
|
|
273
|
+
def _add_feature(self, feature):
|
|
274
|
+
"""
|
|
275
|
+
Add a feature to the model stack
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
feature : GeologicalFeature
|
|
280
|
+
the geological feature to add
|
|
281
|
+
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
if feature.name in self.feature_name_index:
|
|
285
|
+
logger.info("Feature %s already exists at %i, overwriting" %
|
|
286
|
+
(feature.name, self.feature_name_index[feature.name]))
|
|
287
|
+
self.features[self.feature_name_index[feature.name]] = feature
|
|
288
|
+
else:
|
|
289
|
+
self._str += 'GeologicalFeature: {} of type - {} \n'.format(feature.name,feature.type)
|
|
290
|
+
self.features.append(feature)
|
|
291
|
+
self.feature_name_index[feature.name] = len(self.features) - 1
|
|
292
|
+
logger.info("Adding %s to model at location %i" % (
|
|
293
|
+
feature.name, len(self.features)))
|
|
294
|
+
# self._add_domain_fault_above(feature)
|
|
295
|
+
# self._add_unconformity_above(feature)
|
|
296
|
+
feature.set_model(self)
|
|
297
|
+
|
|
298
|
+
def _add_faults(self, feature_builder, features=None):
|
|
299
|
+
"""Adds all existing faults to a geological feature builder
|
|
300
|
+
|
|
301
|
+
Parameters
|
|
302
|
+
----------
|
|
303
|
+
feature_builder : GeologicalFeatureInterpolator/StructuralFrameBuilder
|
|
304
|
+
The feature buider to add the faults to
|
|
305
|
+
features : list, optional
|
|
306
|
+
A specific list of features rather than all features in the model
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
|
|
310
|
+
"""
|
|
311
|
+
if features is None:
|
|
312
|
+
features = self.features
|
|
313
|
+
for f in reversed(features):
|
|
314
|
+
if f.type == 'fault':
|
|
315
|
+
feature_builder.add_fault(f)
|
|
316
|
+
# if f.type == 'unconformity':
|
|
317
|
+
# break
|
|
318
|
+
|
|
319
|
+
def data_for_feature(self,feature_name):
|
|
320
|
+
return self.data.loc[self.data['feature_name'] == feature_name,:]
|
|
321
|
+
|
|
322
|
+
def set_model_data(self, data):
|
|
323
|
+
"""
|
|
324
|
+
Set the data array for the model
|
|
325
|
+
|
|
326
|
+
Parameters
|
|
327
|
+
----------
|
|
328
|
+
data : pandas data frame
|
|
329
|
+
with column headers corresponding to the
|
|
330
|
+
type, X, Y, Z, nx, ny, nz, val, strike, dip, dip_dir, plunge,
|
|
331
|
+
plunge_dir, azimuth
|
|
332
|
+
|
|
333
|
+
Returns
|
|
334
|
+
-------
|
|
335
|
+
Note
|
|
336
|
+
----
|
|
337
|
+
Type can be any unique identifier for the feature the data point
|
|
338
|
+
'eg' 'S0', 'S2', 'F1_axis'
|
|
339
|
+
it is then used by the create functions to get the correct data
|
|
340
|
+
"""
|
|
341
|
+
if type(data) != pd.DataFrame:
|
|
342
|
+
logger.warning(
|
|
343
|
+
"Data is not a pandas data frame, trying to read data frame "
|
|
344
|
+
"from csv")
|
|
345
|
+
try:
|
|
346
|
+
data = pd.read_csv(data)
|
|
347
|
+
except:
|
|
348
|
+
logger.error("Could not load pandas data frame from data")
|
|
349
|
+
logger.info('Adding data to GeologicalModel with {} data points'.format(len(data)))
|
|
350
|
+
self.data = data.copy()
|
|
351
|
+
self.data['X'] -= self.origin[0]
|
|
352
|
+
self.data['Y'] -= self.origin[1]
|
|
353
|
+
self.data['Z'] -= self.origin[2]
|
|
354
|
+
self.data['X'] /= self.scale_factor
|
|
355
|
+
self.data['Y'] /= self.scale_factor
|
|
356
|
+
self.data['Z'] /= self.scale_factor
|
|
357
|
+
if 'type' in self.data:
|
|
358
|
+
logger.warning("'type' is depreciated replace with 'feature_name' \n")
|
|
359
|
+
self.data.rename(columns={'type':'feature_name'},inplace=True)
|
|
360
|
+
for h in all_heading():
|
|
361
|
+
if h not in self.data:
|
|
362
|
+
self.data[h] = np.nan
|
|
363
|
+
if h == 'w':
|
|
364
|
+
self.data[h] = 1.
|
|
365
|
+
if h == 'coord':
|
|
366
|
+
self.data[h] = 0
|
|
367
|
+
|
|
368
|
+
if 'strike' in self.data and 'dip' in self.data:
|
|
369
|
+
logger.info('Converting strike and dip to vectors')
|
|
370
|
+
mask = np.all(~np.isnan(self.data.loc[:, ['strike', 'dip']]),
|
|
371
|
+
axis=1)
|
|
372
|
+
self.data.loc[mask, gradient_vec_names()] = strike_dip_vector(
|
|
373
|
+
self.data.loc[mask, 'strike'], self.data.loc[mask, 'dip'])
|
|
374
|
+
self.data.drop(['strike', 'dip'], axis=1, inplace=True)
|
|
375
|
+
# self.data.loc
|
|
376
|
+
# if 'nx' in self.data and 'ny' in self.data and 'nz' in self.data:
|
|
377
|
+
# mask = np.all(~np.isnan(self.data.loc[:, ['nx', 'ny','nz']]),
|
|
378
|
+
# axis=1)
|
|
379
|
+
# self.data.loc[mask,['nx', 'ny','nz']] /= self.scale_factor
|
|
380
|
+
|
|
381
|
+
def set_stratigraphic_column(self, stratigraphic_column,cmap='tab20'):
|
|
382
|
+
"""
|
|
383
|
+
Adds a stratigraphic column to the model
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
stratigraphic_column : dictionary
|
|
388
|
+
cmap : matplotlib.cmap
|
|
389
|
+
Returns
|
|
390
|
+
-------
|
|
391
|
+
|
|
392
|
+
Notes
|
|
393
|
+
-----
|
|
394
|
+
stratigraphic_column is a nested dictionary with the format
|
|
395
|
+
{'group':
|
|
396
|
+
{'series1':
|
|
397
|
+
{'min':0., 'max':10.,'id':0,'colour':}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
"""
|
|
402
|
+
# if the colour for a unit hasn't been specified we can just sample from
|
|
403
|
+
# a colour map e.g. tab20
|
|
404
|
+
logger.info('Adding stratigraphic column to model')
|
|
405
|
+
random_colour = True
|
|
406
|
+
n_units=0
|
|
407
|
+
for g in stratigraphic_column.keys():
|
|
408
|
+
for u in stratigraphic_column[g].keys():
|
|
409
|
+
if 'colour' in stratigraphic_column[g][u]:
|
|
410
|
+
random_colour = False
|
|
411
|
+
break
|
|
412
|
+
n_units+=1
|
|
413
|
+
if random_colour:
|
|
414
|
+
import matplotlib.cm as cm
|
|
415
|
+
cmap = cm.get_cmap(cmap,n_units)
|
|
416
|
+
cmap_colours = cmap.colors
|
|
417
|
+
ci = 0
|
|
418
|
+
for g in stratigraphic_column.keys():
|
|
419
|
+
for u in stratigraphic_column[g].keys():
|
|
420
|
+
stratigraphic_column[g][u]['colour'] = cmap_colours[ci,:]
|
|
421
|
+
|
|
422
|
+
self.stratigraphic_column = stratigraphic_column
|
|
423
|
+
|
|
424
|
+
def get_interpolator(self, interpolatortype='PLI', nelements=1e5,
|
|
425
|
+
buffer=0.2, element_volume = None, **kwargs):
|
|
426
|
+
"""
|
|
427
|
+
Returns an interpolator given the arguments, also constructs a
|
|
428
|
+
support for a discrete interpolator
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
interpolatortype : string
|
|
433
|
+
define the interpolator type
|
|
434
|
+
nelements : int
|
|
435
|
+
number of elements in the interpolator
|
|
436
|
+
buffer : double or numpy array 3x1
|
|
437
|
+
value(s) between 0,1 specifying the buffer around the bounding box
|
|
438
|
+
data_bb : bool
|
|
439
|
+
whether to use the model boundary or the boundary around
|
|
440
|
+
kwargs : no kwargs used, this just catches any additional arguments
|
|
441
|
+
|
|
442
|
+
Returns
|
|
443
|
+
-------
|
|
444
|
+
"""
|
|
445
|
+
# get an interpolator for
|
|
446
|
+
interpolator = None
|
|
447
|
+
bb = np.copy(self.bounding_box)
|
|
448
|
+
# add a buffer to the interpolation domain, this is necessary for
|
|
449
|
+
# faults but also generally a good
|
|
450
|
+
# idea to avoid boundary problems
|
|
451
|
+
# buffer = bb[1, :]
|
|
452
|
+
buffer = (bb[1,:]-bb[0,:])*buffer
|
|
453
|
+
bb[0, :] -= buffer # *(bb[1,:]-bb[0,:])
|
|
454
|
+
bb[1, :] += buffer # *(bb[1,:]-bb[0,:])
|
|
455
|
+
box_vol = (bb[1, 0]-bb[0, 0]) * (bb[1, 1]-bb[0, 1]) * (bb[1, 2]-bb[0, 2])
|
|
456
|
+
if interpolatortype == "PLI":
|
|
457
|
+
if element_volume is None:
|
|
458
|
+
nelements /= 5
|
|
459
|
+
element_volume = box_vol / nelements
|
|
460
|
+
# calculate the step vector of a regular cube
|
|
461
|
+
step_vector = np.zeros(3)
|
|
462
|
+
step_vector[:] = element_volume ** (1. / 3.)
|
|
463
|
+
# step_vector /= np.array([1,1,2])
|
|
464
|
+
# number of steps is the length of the box / step vector
|
|
465
|
+
nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
466
|
+
# create a structured grid using the origin and number of steps
|
|
467
|
+
if self.reuse_supports:
|
|
468
|
+
mesh_id = 'mesh_{}'.format(nelements)
|
|
469
|
+
mesh = self.support.get(mesh_id,
|
|
470
|
+
TetMesh(origin=bb[0, :], nsteps=nsteps,
|
|
471
|
+
step_vector=step_vector))
|
|
472
|
+
if mesh_id not in self.support:
|
|
473
|
+
self.support[mesh_id] = mesh
|
|
474
|
+
else:
|
|
475
|
+
mesh = TetMesh(origin=bb[0, :], nsteps=nsteps, step_vector=step_vector)
|
|
476
|
+
logger.info("Creating regular tetrahedron mesh with %i elements \n"
|
|
477
|
+
"for modelling using PLI" % (mesh.ntetra))
|
|
478
|
+
|
|
479
|
+
return PLI(mesh)
|
|
480
|
+
|
|
481
|
+
if interpolatortype == 'FDI':
|
|
482
|
+
|
|
483
|
+
# find the volume of one element
|
|
484
|
+
if element_volume is None:
|
|
485
|
+
element_volume = box_vol / nelements
|
|
486
|
+
# calculate the step vector of a regular cube
|
|
487
|
+
step_vector = np.zeros(3)
|
|
488
|
+
step_vector[:] = element_volume ** (1. / 3.)
|
|
489
|
+
# number of steps is the length of the box / step vector
|
|
490
|
+
nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
491
|
+
if np.any(np.less(nsteps, 3)):
|
|
492
|
+
logger.error("Cannot create interpolator: number of steps is too small")
|
|
493
|
+
return None
|
|
494
|
+
# create a structured grid using the origin and number of steps
|
|
495
|
+
if self.reuse_supports:
|
|
496
|
+
grid_id = 'grid_{}'.format(nelements)
|
|
497
|
+
grid = self.support.get(grid_id, StructuredGrid(origin=bb[0, :],
|
|
498
|
+
nsteps=nsteps,
|
|
499
|
+
step_vector=step_vector))
|
|
500
|
+
if grid_id not in self.support:
|
|
501
|
+
self.support[grid_id] = grid
|
|
502
|
+
else:
|
|
503
|
+
grid = StructuredGrid(origin=bb[0, :], nsteps=nsteps,step_vector=step_vector)
|
|
504
|
+
logger.info("Creating regular grid with %i elements \n"
|
|
505
|
+
"for modelling using FDI" % grid.n_elements)
|
|
506
|
+
return FDI(grid)
|
|
507
|
+
|
|
508
|
+
if interpolatortype == "DFI": # "fold" in kwargs:
|
|
509
|
+
if element_volume is None:
|
|
510
|
+
nelements /= 5
|
|
511
|
+
element_volume = box_vol / nelements
|
|
512
|
+
# calculate the step vector of a regular cube
|
|
513
|
+
step_vector = np.zeros(3)
|
|
514
|
+
step_vector[:] = element_volume ** (1. / 3.)
|
|
515
|
+
# number of steps is the length of the box / step vector
|
|
516
|
+
nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
517
|
+
# create a structured grid using the origin and number of steps
|
|
518
|
+
mesh = kwargs.get('mesh', TetMesh(origin=bb[0, :], nsteps=nsteps,
|
|
519
|
+
step_vector=step_vector))
|
|
520
|
+
logger.info("Creating regular tetrahedron mesh with %i elements \n"
|
|
521
|
+
"for modelling using DFI" % mesh.ntetra)
|
|
522
|
+
return DFI(mesh, kwargs['fold'])
|
|
523
|
+
if interpolatortype == 'Surfe' or interpolatortype == 'surfe' and \
|
|
524
|
+
surfe:
|
|
525
|
+
method = kwargs.get('method', 'single_surface')
|
|
526
|
+
logger.info("Using surfe interpolator")
|
|
527
|
+
return Surfe(method)
|
|
528
|
+
logger.warning("No interpolator")
|
|
529
|
+
return interpolator
|
|
530
|
+
|
|
531
|
+
def create_and_add_foliation(self, series_surface_data, **kwargs):
|
|
532
|
+
"""
|
|
533
|
+
Parameters
|
|
534
|
+
----------
|
|
535
|
+
series_surface_data : string
|
|
536
|
+
corresponding to the feature_name in the data
|
|
537
|
+
kwargs
|
|
538
|
+
|
|
539
|
+
Returns
|
|
540
|
+
-------
|
|
541
|
+
feature : GeologicalFeature
|
|
542
|
+
the created geological feature
|
|
543
|
+
"""
|
|
544
|
+
if self.check_inialisation() == False:
|
|
545
|
+
return False
|
|
546
|
+
interpolator = self.get_interpolator(**kwargs)
|
|
547
|
+
series_builder = GeologicalFeatureInterpolator(interpolator,
|
|
548
|
+
name=series_surface_data,
|
|
549
|
+
**kwargs)
|
|
550
|
+
# add data
|
|
551
|
+
series_data = self.data[self.data['feature_name'] == series_surface_data]
|
|
552
|
+
if series_data.shape[0] == 0:
|
|
553
|
+
logger.warning("No data for %s, skipping" % series_surface_data)
|
|
554
|
+
return
|
|
555
|
+
series_builder.add_data_from_data_frame(series_data)
|
|
556
|
+
self._add_faults(series_builder)
|
|
557
|
+
|
|
558
|
+
series_builder.build_arguments = kwargs
|
|
559
|
+
self._add_feature(series_builder.feature)
|
|
560
|
+
return series_builder.feature
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def _add_unconformity_above(self, feature):
|
|
564
|
+
"""
|
|
565
|
+
|
|
566
|
+
Adds a region to the feature to prevent the value from being
|
|
567
|
+
interpolated where the unconformities exists above e.g.
|
|
568
|
+
if there is another feature above and the unconformity is at 0
|
|
569
|
+
then the features added below (after) will only be visible where the
|
|
570
|
+
uncomformity is <0
|
|
571
|
+
|
|
572
|
+
Parameters
|
|
573
|
+
----------
|
|
574
|
+
feature - GeologicalFeature
|
|
575
|
+
|
|
576
|
+
Returns
|
|
577
|
+
-------
|
|
578
|
+
|
|
579
|
+
"""
|
|
580
|
+
for f in reversed(self.features):
|
|
581
|
+
if f.type == 'unconformity':
|
|
582
|
+
feature.add_region(lambda pos: f.evaluate(pos))
|
|
583
|
+
break
|
|
584
|
+
|
|
585
|
+
def _add_unconformity_below(self, feature):
|
|
586
|
+
"""
|
|
587
|
+
Adds a region to the features that represents the
|
|
588
|
+
unconformity so it is not evaluated below the unconformity
|
|
589
|
+
|
|
590
|
+
Parameters
|
|
591
|
+
----------
|
|
592
|
+
feature
|
|
593
|
+
|
|
594
|
+
Returns
|
|
595
|
+
-------
|
|
596
|
+
|
|
597
|
+
"""
|
|
598
|
+
for f in self.features:
|
|
599
|
+
if f.type == 'series' and feature.feature.name != f.name:
|
|
600
|
+
f.add_region(lambda pos: ~feature.evaluate(pos))
|
|
601
|
+
|
|
602
|
+
def add_unconformity(self, feature, value):
|
|
603
|
+
"""
|
|
604
|
+
Use an existing feature to add an unconformity to the model.
|
|
605
|
+
|
|
606
|
+
Parameters
|
|
607
|
+
----------
|
|
608
|
+
feature : GeologicalFeature
|
|
609
|
+
existing geological feature
|
|
610
|
+
value : float
|
|
611
|
+
scalar value of isosurface that represents
|
|
612
|
+
|
|
613
|
+
Returns
|
|
614
|
+
-------
|
|
615
|
+
unconformity : GeologicalFeature
|
|
616
|
+
unconformity feature
|
|
617
|
+
|
|
618
|
+
"""
|
|
619
|
+
self.parameters['features'].append({'feature_type': 'unconformity', 'feature': feature, 'value': value})
|
|
620
|
+
uc_feature = UnconformityFeature(feature, value)
|
|
621
|
+
|
|
622
|
+
# for f in self.features:
|
|
623
|
+
# f.add_region(lambda pos: uc_feature.evaluate(pos))
|
|
624
|
+
|
|
625
|
+
# see if any unconformities are above this feature if so add region
|
|
626
|
+
# self._add_unconformity_above(uc_feature)
|
|
627
|
+
# self._add_unconformity_below(feature)#, uc_feature)
|
|
628
|
+
self._add_feature(uc_feature)
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
return uc_feature
|
|
632
|
+
|
|
633
|
+
def add_onlap_unconformity(self, feature, value):
|
|
634
|
+
"""
|
|
635
|
+
Use an existing feature to add an unconformity to the model.
|
|
636
|
+
|
|
637
|
+
Parameters
|
|
638
|
+
----------
|
|
639
|
+
feature : GeologicalFeature
|
|
640
|
+
existing geological feature
|
|
641
|
+
value : float
|
|
642
|
+
scalar value of isosurface that represents
|
|
643
|
+
|
|
644
|
+
Returns
|
|
645
|
+
-------
|
|
646
|
+
unconformity_feature : GeologicalFeature
|
|
647
|
+
the created unconformity
|
|
648
|
+
|
|
649
|
+
"""
|
|
650
|
+
self.parameters['features'].append({'feature_type': 'onlap', 'feature': feature, 'value': value})
|
|
651
|
+
|
|
652
|
+
uc_feature = UnconformityFeature(feature, value)
|
|
653
|
+
|
|
654
|
+
# for f in self.features:
|
|
655
|
+
# f.add_region(lambda pos: uc_feature.evaluate(pos))
|
|
656
|
+
|
|
657
|
+
# see if any unconformities are above this feature if so add region
|
|
658
|
+
# self._add_unconformity_above(uc_feature)
|
|
659
|
+
self._add_unconformity_below(uc_feature) # , uc_feature)
|
|
660
|
+
self._add_feature(uc_feature)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
return uc_feature
|
|
664
|
+
|
|
665
|
+
def create_and_add_fault(self, fault_surface_data, displacement, renormalise=True, **kwargs):
|
|
666
|
+
"""
|
|
667
|
+
Parameters
|
|
668
|
+
----------
|
|
669
|
+
fault_surface_data : string
|
|
670
|
+
name of the fault surface data in the dataframe
|
|
671
|
+
displacement : displacement magnitude
|
|
672
|
+
kwargs : additional kwargs for Fault and interpolators
|
|
673
|
+
|
|
674
|
+
Returns
|
|
675
|
+
-------
|
|
676
|
+
fault : FaultSegment
|
|
677
|
+
created fault
|
|
678
|
+
"""
|
|
679
|
+
self.parameters['features'].append(
|
|
680
|
+
{'feature_type': 'fault', 'feature_name': fault_surface_data, 'displacement': displacement, **kwargs})
|
|
681
|
+
|
|
682
|
+
displacement_scaled = displacement / self.scale_factor
|
|
683
|
+
# create fault frame
|
|
684
|
+
interpolator = self.get_interpolator(**kwargs)
|
|
685
|
+
fault_frame_builder = StructuralFrameBuilder(interpolator,
|
|
686
|
+
name=fault_surface_data,
|
|
687
|
+
**kwargs)
|
|
688
|
+
# add data
|
|
689
|
+
fault_frame_data = self.data[
|
|
690
|
+
self.data['feature_name'] == fault_surface_data].copy()
|
|
691
|
+
if 'coord' not in fault_frame_data:
|
|
692
|
+
fault_frame_data['coord'] = 0
|
|
693
|
+
vals = fault_frame_data['val']
|
|
694
|
+
fault_frame_builder.add_data_from_data_frame(fault_frame_data)
|
|
695
|
+
# if there is no fault slip data then we could find the strike of
|
|
696
|
+
# the fault and build
|
|
697
|
+
# the second coordinate
|
|
698
|
+
# if we add a region to the fault then the fault operator doesn't
|
|
699
|
+
# work but for visualisation
|
|
700
|
+
# we want to add a region!
|
|
701
|
+
# check if this fault overprint any existing faults exist in the stack
|
|
702
|
+
|
|
703
|
+
fault_frame = fault_frame_builder.build(**kwargs)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
fault = FaultSegment(fault_frame, displacement=displacement_scaled,
|
|
707
|
+
**kwargs)
|
|
708
|
+
# for f in reversed(self.features):
|
|
709
|
+
# if f.type == 'unconformity':
|
|
710
|
+
# fault.add_region(lambda pos: f.evaluate_value(pos) <= 0)
|
|
711
|
+
# break
|
|
712
|
+
# if displacement == 0:
|
|
713
|
+
# fault.type = 'fault_inactive'
|
|
714
|
+
# self._add_feature(fault)
|
|
715
|
+
self._add_feature(fault)
|
|
716
|
+
|
|
717
|
+
return fault
|
|
718
|
+
|
|
719
|
+
def rescale(self, points, inplace=True):
|
|
720
|
+
"""
|
|
721
|
+
Convert from model scale to real world scale - in the future this
|
|
722
|
+
should also do transformations?
|
|
723
|
+
|
|
724
|
+
Parameters
|
|
725
|
+
----------
|
|
726
|
+
points : np.array((N,3),dtype=double)
|
|
727
|
+
inplace : boolean
|
|
728
|
+
whether to return a modified copy or modify the original array
|
|
729
|
+
|
|
730
|
+
Returns
|
|
731
|
+
-------
|
|
732
|
+
points : np.array((N,3),dtype=double)
|
|
733
|
+
|
|
734
|
+
"""
|
|
735
|
+
if inplace == False:
|
|
736
|
+
points = points.copy()
|
|
737
|
+
points *= self.scale_factor
|
|
738
|
+
points += self.origin
|
|
739
|
+
return points
|
|
740
|
+
|
|
741
|
+
def scale(self, points, inplace=True):
|
|
742
|
+
""" Take points in UTM coordinates and reproject
|
|
743
|
+
into scaled model space
|
|
744
|
+
|
|
745
|
+
Parameters
|
|
746
|
+
----------
|
|
747
|
+
points : np.array((N,3),dtype=float)
|
|
748
|
+
points to
|
|
749
|
+
inplace : bool, optional default = True
|
|
750
|
+
whether to copy the points array or update the passed array
|
|
751
|
+
Returns
|
|
752
|
+
-------
|
|
753
|
+
points : np.array((N,3),dtype=double)
|
|
754
|
+
|
|
755
|
+
"""
|
|
756
|
+
if inplace==False:
|
|
757
|
+
points = points.copy()
|
|
758
|
+
|
|
759
|
+
points[:, :] -= self.origin
|
|
760
|
+
points /= self.scale_factor
|
|
761
|
+
return points
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def regular_grid(self, nsteps=(50, 50, 25), shuffle = True, rescale=False):
|
|
765
|
+
"""
|
|
766
|
+
Return a regular grid within the model bounding box
|
|
767
|
+
|
|
768
|
+
Parameters
|
|
769
|
+
----------
|
|
770
|
+
nsteps : tuple
|
|
771
|
+
number of cells in x,y,z
|
|
772
|
+
|
|
773
|
+
Returns
|
|
774
|
+
-------
|
|
775
|
+
xyz : np.array((N,3),dtype=float)
|
|
776
|
+
locations of points in regular grid
|
|
777
|
+
"""
|
|
778
|
+
x = np.linspace(self.bounding_box[0, 0], self.bounding_box[1, 0],
|
|
779
|
+
nsteps[0])
|
|
780
|
+
y = np.linspace(self.bounding_box[0, 1], self.bounding_box[1, 1],
|
|
781
|
+
nsteps[1])
|
|
782
|
+
z = np.linspace(self.bounding_box[1, 2], self.bounding_box[0, 2],
|
|
783
|
+
nsteps[2])
|
|
784
|
+
xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
785
|
+
locs = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
786
|
+
if shuffle:
|
|
787
|
+
logger.info("Shuffling points")
|
|
788
|
+
np.random.shuffle(locs)
|
|
789
|
+
if rescale:
|
|
790
|
+
locs = self.rescale(locs)
|
|
791
|
+
return locs
|
|
792
|
+
|
|
793
|
+
def evaluate_model(self, xyz, scale=True):
|
|
794
|
+
"""Evaluate the stratigraphic id at each location
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
Parameters
|
|
798
|
+
----------
|
|
799
|
+
xyz : np.array((N,3),dtype=float)
|
|
800
|
+
locations
|
|
801
|
+
scale : bool
|
|
802
|
+
whether to rescale the xyz before evaluating model
|
|
803
|
+
|
|
804
|
+
Returns
|
|
805
|
+
-------
|
|
806
|
+
stratigraphic_id : np.array(N,dtype=int)
|
|
807
|
+
the stratigraphic index for locations
|
|
808
|
+
|
|
809
|
+
Examples
|
|
810
|
+
--------
|
|
811
|
+
Evaluate on a voxet
|
|
812
|
+
|
|
813
|
+
>>> x = np.linspace(model.bounding_box[0, 0], model.bounding_box[1, 0],
|
|
814
|
+
nsteps[0])
|
|
815
|
+
>>> y = np.linspace(model.bounding_box[0, 1], model.bounding_box[1, 1],
|
|
816
|
+
nsteps[1])
|
|
817
|
+
>>> z = np.linspace(model.bounding_box[1, 2], model.bounding_box[0, 2],
|
|
818
|
+
nsteps[2])
|
|
819
|
+
>>> xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
|
|
820
|
+
>>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
821
|
+
>>> model.evaluate_model(xyz)
|
|
822
|
+
|
|
823
|
+
Evaluate on points defined by regular grid function
|
|
824
|
+
|
|
825
|
+
>>> model.evaluate_model(model.regular_grid())
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
Evaluate on a map
|
|
829
|
+
|
|
830
|
+
>>> x = np.linspace(self.bounding_box[0, 0], self.bounding_box[1, 0],
|
|
831
|
+
nsteps[0])
|
|
832
|
+
>>> y = np.linspace(self.bounding_box[0, 1], self.bounding_box[1, 1],
|
|
833
|
+
nsteps[1])
|
|
834
|
+
>>> xx, yy = np.meshgrid(x, y, indexing='ij')
|
|
835
|
+
>>> zz = np.zeros_like(yy)
|
|
836
|
+
>>> xyz = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
|
|
837
|
+
>>> model.evaluate_model(xyz)
|
|
838
|
+
|
|
839
|
+
"""
|
|
840
|
+
if scale:
|
|
841
|
+
xyz = self.scale(xyz,inplace=False)
|
|
842
|
+
strat_id = np.zeros(xyz.shape[0],dtype=int)
|
|
843
|
+
for group in self.stratigraphic_column.keys():
|
|
844
|
+
if group == 'faults':
|
|
845
|
+
continue
|
|
846
|
+
feature_id = self.feature_name_index.get(group, -1)
|
|
847
|
+
if feature_id >= 0:
|
|
848
|
+
feature = self.features[feature_id]
|
|
849
|
+
vals = feature.evaluate_value(xyz)
|
|
850
|
+
for series in self.stratigraphic_column[group].values():
|
|
851
|
+
strat_id[np.logical_and(vals < series.get('max',feature.max()), vals > series.get('min',feature.min()))] = series['id']
|
|
852
|
+
if feature_id == -1:
|
|
853
|
+
logger.error('Model does not contain {}'.format(group))
|
|
854
|
+
return strat_id
|
|
855
|
+
|
|
856
|
+
def evaluate_fault_displacements(self,points,scale=True):
|
|
857
|
+
"""Evaluate the fault displacement magnitude at each location
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
Parameters
|
|
861
|
+
----------
|
|
862
|
+
xyz : np.array((N,3),dtype=float)
|
|
863
|
+
locations
|
|
864
|
+
scale : bool
|
|
865
|
+
whether to rescale the xyz before evaluating model
|
|
866
|
+
|
|
867
|
+
Returns
|
|
868
|
+
-------
|
|
869
|
+
fault_displacement : np.array(N,dtype=float)
|
|
870
|
+
the fault displacement magnitude
|
|
871
|
+
"""
|
|
872
|
+
if scale:
|
|
873
|
+
points = self.scale(points,inplace=False)
|
|
874
|
+
vals = np.zeros(points.shape[0])
|
|
875
|
+
for f in self.features:
|
|
876
|
+
if f.type == 'fault':
|
|
877
|
+
disp = f.displacementfeature.evaluate_value(points)
|
|
878
|
+
vals[~np.isnan(disp)] += disp[~np.isnan(disp)]
|
|
879
|
+
return vals*-self.scale_factor # convert from restoration magnutude to displacement
|
|
880
|
+
|
|
881
|
+
|