LoopStructural 1.6.3__tar.gz → 1.6.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of LoopStructural might be problematic. Click here for more details.
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datatypes/_bounding_box.py +58 -3
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datatypes/_point.py +36 -7
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/__init__.py +1 -1
- loopstructural-1.6.6/LoopStructural/interpolators/_builders.py +149 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_finite_difference_interpolator.py +11 -11
- loopstructural-1.6.6/LoopStructural/interpolators/_interpolator_builder.py +55 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_interpolator_factory.py +7 -18
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_3d_base_structured.py +4 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_3d_structured_grid.py +0 -3
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/__init__.py +0 -2
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/core/geological_model.py +11 -10
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/__init__.py +1 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_analytical_feature.py +23 -2
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_base_geological_feature.py +37 -8
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_cross_product_geological_feature.py +7 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_geological_feature.py +3 -1
- loopstructural-1.6.6/LoopStructural/modelling/features/_projected_vector_feature.py +112 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_structural_frame.py +16 -18
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_unconformity_feature.py +3 -3
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +2 -2
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +2 -2
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fault/_fault_function_feature.py +3 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fault/_fault_segment.py +35 -4
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/input/process_data.py +41 -26
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/input/project_file.py +44 -39
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/intrusion_feature.py +3 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/__init__.py +1 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/_surface.py +12 -3
- loopstructural-1.6.6/LoopStructural/utils/_transformation.py +160 -0
- loopstructural-1.6.6/LoopStructural/utils/colours.py +50 -0
- loopstructural-1.6.6/LoopStructural/version.py +1 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural.egg-info/PKG-INFO +16 -2
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural.egg-info/SOURCES.txt +2 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural.egg-info/requires.txt +15 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/PKG-INFO +16 -2
- {loopstructural-1.6.3 → loopstructural-1.6.6}/pyproject.toml +1 -0
- loopstructural-1.6.3/LoopStructural/interpolators/_builders.py +0 -149
- loopstructural-1.6.3/LoopStructural/utils/_transformation.py +0 -76
- loopstructural-1.6.3/LoopStructural/utils/colours.py +0 -26
- loopstructural-1.6.3/LoopStructural/version.py +0 -1
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LICENSE +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/_base.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/_example_models.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/claudius.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/duplex.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/duplexbb.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/intrusion.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/onefolddata.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datatypes/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datatypes/_structured_grid.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/datatypes/_surface.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/export/exporters.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/export/file_formats.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/export/geoh5.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/export/gocad.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/export/omf_wrapper.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_api.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_cython/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_discrete_fold_interpolator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_discrete_interpolator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_geological_interpolator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_operator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_p1interpolator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_p2interpolator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_surfe_wrapper.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_2d_base_unstructured.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_2d_structured_grid.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_3d_structured_tetra.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_aabb.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_base_support.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_face_table.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/supports/_support_factory.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/core/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_lambda_geological_feature.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/_region.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/builders/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/builders/_base_builder.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/builders/_fault_builder.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fault/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fault/_fault_function.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/_fold.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/_foldframe.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/_svariogram.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/fold_function/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/input/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/input/fault_network.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/input/map2loop_processor.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/intrusion_builder.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/config.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/dtm_creator.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/exceptions.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/features.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/helper.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/json_encoder.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/linalg.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/logging.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/maths.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/regions.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/typing.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/utils/utils.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/visualisation/__init__.py +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural.egg-info/dependency_links.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural.egg-info/top_level.txt +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/README.md +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/setup.cfg +0 -0
- {loopstructural-1.6.3 → loopstructural-1.6.6}/setup.py +0 -0
|
@@ -410,9 +410,15 @@ class BoundingBox:
|
|
|
410
410
|
import pyvista as pv
|
|
411
411
|
except ImportError:
|
|
412
412
|
raise ImportError("pyvista is required for vtk support")
|
|
413
|
-
x = np.linspace(
|
|
414
|
-
|
|
415
|
-
|
|
413
|
+
x = np.linspace(
|
|
414
|
+
self.global_origin[0] + self.origin[0], self.global_maximum[0], self.nsteps[0]
|
|
415
|
+
)
|
|
416
|
+
y = np.linspace(
|
|
417
|
+
self.global_origin[1] + self.origin[1], self.global_maximum[1], self.nsteps[1]
|
|
418
|
+
)
|
|
419
|
+
z = np.linspace(
|
|
420
|
+
self.global_origin[2] + self.origin[2], self.global_maximum[2], self.nsteps[2]
|
|
421
|
+
)
|
|
416
422
|
return pv.RectilinearGrid(
|
|
417
423
|
x,
|
|
418
424
|
y,
|
|
@@ -435,3 +441,52 @@ class BoundingBox:
|
|
|
435
441
|
properties=_vertex_data,
|
|
436
442
|
name=name,
|
|
437
443
|
)
|
|
444
|
+
|
|
445
|
+
def project(self, xyz):
|
|
446
|
+
"""Project a point into the bounding box
|
|
447
|
+
|
|
448
|
+
Parameters
|
|
449
|
+
----------
|
|
450
|
+
xyz : np.ndarray
|
|
451
|
+
point to project
|
|
452
|
+
|
|
453
|
+
Returns
|
|
454
|
+
-------
|
|
455
|
+
np.ndarray
|
|
456
|
+
projected point
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
return (xyz - self.global_origin) / np.max(
|
|
460
|
+
(self.global_maximum - self.global_origin)
|
|
461
|
+
) # np.clip(xyz, self.origin, self.maximum)
|
|
462
|
+
|
|
463
|
+
def reproject(self, xyz):
|
|
464
|
+
"""Reproject a point from the bounding box to the global space
|
|
465
|
+
|
|
466
|
+
Parameters
|
|
467
|
+
----------
|
|
468
|
+
xyz : np.ndarray
|
|
469
|
+
point to reproject
|
|
470
|
+
|
|
471
|
+
Returns
|
|
472
|
+
-------
|
|
473
|
+
np.ndarray
|
|
474
|
+
reprojected point
|
|
475
|
+
"""
|
|
476
|
+
|
|
477
|
+
return xyz * np.max((self.global_maximum - self.global_origin)) + self.global_origin
|
|
478
|
+
|
|
479
|
+
def __repr__(self):
|
|
480
|
+
return f"BoundingBox({self.origin}, {self.maximum}, {self.nsteps})"
|
|
481
|
+
|
|
482
|
+
def __str__(self):
|
|
483
|
+
return f"BoundingBox({self.origin}, {self.maximum}, {self.nsteps})"
|
|
484
|
+
|
|
485
|
+
def __eq__(self, other):
|
|
486
|
+
if not isinstance(other, BoundingBox):
|
|
487
|
+
return False
|
|
488
|
+
return (
|
|
489
|
+
np.allclose(self.origin, other.origin)
|
|
490
|
+
and np.allclose(self.maximum, other.maximum)
|
|
491
|
+
and np.allclose(self.nsteps, other.nsteps)
|
|
492
|
+
)
|
|
@@ -25,11 +25,14 @@ class ValuePoints:
|
|
|
25
25
|
),
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
def vtk(self):
|
|
28
|
+
def vtk(self, scalars=None):
|
|
29
29
|
import pyvista as pv
|
|
30
30
|
|
|
31
31
|
points = pv.PolyData(self.locations)
|
|
32
|
-
|
|
32
|
+
if scalars is not None and len(scalars) == len(self.locations):
|
|
33
|
+
points.point_data['scalars'] = scalars
|
|
34
|
+
else:
|
|
35
|
+
points["values"] = self.values
|
|
33
36
|
return points
|
|
34
37
|
|
|
35
38
|
def plot(self, pyvista_kwargs={}):
|
|
@@ -123,22 +126,48 @@ class VectorPoints:
|
|
|
123
126
|
def from_dict(self, d):
|
|
124
127
|
return VectorPoints(d['locations'], d['vectors'], d['name'], d.get('properties', None))
|
|
125
128
|
|
|
126
|
-
def vtk(
|
|
129
|
+
def vtk(
|
|
130
|
+
self,
|
|
131
|
+
geom='arrow',
|
|
132
|
+
scale=0.10,
|
|
133
|
+
scale_function=None,
|
|
134
|
+
normalise=True,
|
|
135
|
+
tolerance=0.05,
|
|
136
|
+
bb=None,
|
|
137
|
+
scalars=None,
|
|
138
|
+
):
|
|
127
139
|
import pyvista as pv
|
|
128
140
|
|
|
141
|
+
_projected = False
|
|
129
142
|
vectors = np.copy(self.vectors)
|
|
143
|
+
if normalise:
|
|
144
|
+
norm = np.linalg.norm(vectors, axis=1)
|
|
145
|
+
vectors[norm > 0, :] /= norm[norm > 0][:, None]
|
|
130
146
|
if scale_function is not None:
|
|
131
|
-
vectors /= np.linalg.norm(vectors, axis=1)[:, None]
|
|
147
|
+
# vectors /= np.linalg.norm(vectors, axis=1)[:, None]
|
|
132
148
|
vectors *= scale_function(self.locations)[:, None]
|
|
133
|
-
|
|
149
|
+
locations = self.locations
|
|
150
|
+
if bb is not None:
|
|
151
|
+
try:
|
|
152
|
+
locations = bb.project(locations)
|
|
153
|
+
_projected = True
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.error(f'Failed to project points to bounding box: {e}')
|
|
156
|
+
logger.error('Using unprojected points, this may cause issues with the glyphing')
|
|
157
|
+
points = pv.PolyData(locations)
|
|
158
|
+
if scalars is not None and len(scalars) == len(self.locations):
|
|
159
|
+
points['scalars'] = scalars
|
|
134
160
|
points.point_data.set_vectors(vectors, 'vectors')
|
|
135
161
|
if geom == 'arrow':
|
|
136
162
|
geom = pv.Arrow(scale=scale)
|
|
137
163
|
elif geom == 'disc':
|
|
138
|
-
geom = pv.Disc(inner=0, outer=scale)
|
|
164
|
+
geom = pv.Disc(inner=0, outer=scale).rotate_y(90)
|
|
139
165
|
|
|
140
166
|
# Perform the glyph
|
|
141
|
-
|
|
167
|
+
glyphed = points.glyph(orient="vectors", geom=geom, tolerance=tolerance, scale=False)
|
|
168
|
+
if _projected:
|
|
169
|
+
glyphed.points = bb.reproject(glyphed.points)
|
|
170
|
+
return glyphed
|
|
142
171
|
|
|
143
172
|
def plot(self, pyvista_kwargs={}):
|
|
144
173
|
"""Calls pyvista plot on the vtk object
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# from LoopStructural.utils.exceptions import LoopException
|
|
2
|
+
# import numpy as np
|
|
3
|
+
# from typing import Optional
|
|
4
|
+
# from LoopStructural.interpolators import (
|
|
5
|
+
# P1Interpolator,
|
|
6
|
+
# P2Interpolator,
|
|
7
|
+
# FiniteDifferenceInterpolator,
|
|
8
|
+
# GeologicalInterpolator,
|
|
9
|
+
# DiscreteFoldInterpolator,
|
|
10
|
+
# StructuredGrid,
|
|
11
|
+
# TetMesh,
|
|
12
|
+
# )
|
|
13
|
+
# from LoopStructural.datatypes import BoundingBox
|
|
14
|
+
# from LoopStructural.utils.logging import getLogger
|
|
15
|
+
|
|
16
|
+
# logger = getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# def get_interpolator(
|
|
20
|
+
# bounding_box: BoundingBox,
|
|
21
|
+
# interpolatortype: str,
|
|
22
|
+
# nelements: int,
|
|
23
|
+
# element_volume: Optional[float] = None,
|
|
24
|
+
# buffer: float = 0.2,
|
|
25
|
+
# dimensions: int = 3,
|
|
26
|
+
# support=None,
|
|
27
|
+
# ) -> GeologicalInterpolator:
|
|
28
|
+
# # add a buffer to the interpolation domain, this is necessary for
|
|
29
|
+
# # faults but also generally a good
|
|
30
|
+
# # idea to avoid boundary problems
|
|
31
|
+
# # buffer = bb[1, :]
|
|
32
|
+
# origin = bounding_box.with_buffer(buffer).origin
|
|
33
|
+
# maximum = bounding_box.with_buffer(buffer).maximum
|
|
34
|
+
# box_vol = np.prod(maximum - origin)
|
|
35
|
+
# if interpolatortype == "PLI":
|
|
36
|
+
# if support is None:
|
|
37
|
+
# if element_volume is None:
|
|
38
|
+
# # nelements /= 5
|
|
39
|
+
# element_volume = box_vol / nelements
|
|
40
|
+
# # calculate the step vector of a regular cube
|
|
41
|
+
# step_vector = np.zeros(3)
|
|
42
|
+
# step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
43
|
+
# # step_vector /= np.array([1,1,2])
|
|
44
|
+
# # number of steps is the length of the box / step vector
|
|
45
|
+
# nsteps = np.ceil((maximum - origin) / step_vector).astype(int)
|
|
46
|
+
# if np.any(np.less(nsteps, 3)):
|
|
47
|
+
# axis_labels = ["x", "y", "z"]
|
|
48
|
+
# for i in range(3):
|
|
49
|
+
# if nsteps[i] < 3:
|
|
50
|
+
# nsteps[i] = 3
|
|
51
|
+
# logger.error(
|
|
52
|
+
# f"Number of steps in direction {axis_labels[i]} is too small, try increasing nelements"
|
|
53
|
+
# )
|
|
54
|
+
# logger.error("Cannot create interpolator: number of steps is too small")
|
|
55
|
+
# raise ValueError("Number of steps too small cannot create interpolator")
|
|
56
|
+
|
|
57
|
+
# support = TetMesh(origin=origin, nsteps=nsteps, step_vector=step_vector)
|
|
58
|
+
# logger.info(
|
|
59
|
+
# "Creating regular tetrahedron mesh with %i elements \n"
|
|
60
|
+
# "for modelling using PLI" % (support.ntetra)
|
|
61
|
+
# )
|
|
62
|
+
|
|
63
|
+
# return P1Interpolator(support)
|
|
64
|
+
# if interpolatortype == "P2":
|
|
65
|
+
# if support is not None:
|
|
66
|
+
# logger.info(
|
|
67
|
+
# "Creating regular tetrahedron mesh with %i elements \n"
|
|
68
|
+
# "for modelling using P2" % (support.ntetra)
|
|
69
|
+
# )
|
|
70
|
+
# return P2Interpolator(support)
|
|
71
|
+
# else:
|
|
72
|
+
# raise ValueError("Cannot create P2 interpolator without support, try using PLI")
|
|
73
|
+
|
|
74
|
+
# if interpolatortype == "FDI":
|
|
75
|
+
# # find the volume of one element
|
|
76
|
+
# if element_volume is None:
|
|
77
|
+
# element_volume = box_vol / nelements
|
|
78
|
+
# # calculate the step vector of a regular cube
|
|
79
|
+
# step_vector = np.zeros(3)
|
|
80
|
+
# step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
81
|
+
# # number of steps is the length of the box / step vector
|
|
82
|
+
# nsteps = np.ceil((maximum - origin) / step_vector).astype(int)
|
|
83
|
+
# if np.any(np.less(nsteps, 3)):
|
|
84
|
+
# logger.error("Cannot create interpolator: number of steps is too small")
|
|
85
|
+
# axis_labels = ["x", "y", "z"]
|
|
86
|
+
# for i in range(3):
|
|
87
|
+
# if nsteps[i] < 3:
|
|
88
|
+
# nsteps[i] = 3
|
|
89
|
+
# # logger.error(
|
|
90
|
+
# # f"Number of steps in direction {axis_labels[i]} is too small, try increasing nelements"
|
|
91
|
+
# # )
|
|
92
|
+
# # raise ValueError("Number of steps too small cannot create interpolator")
|
|
93
|
+
# # create a structured grid using the origin and number of steps
|
|
94
|
+
|
|
95
|
+
# grid = StructuredGrid(origin=origin, nsteps=nsteps, step_vector=step_vector)
|
|
96
|
+
# logger.info(
|
|
97
|
+
# f"Creating regular grid with {grid.n_elements} elements \n" "for modelling using FDI"
|
|
98
|
+
# )
|
|
99
|
+
# return FiniteDifferenceInterpolator(grid)
|
|
100
|
+
# if interpolatortype == "DFI":
|
|
101
|
+
# if element_volume is None:
|
|
102
|
+
# nelements /= 5
|
|
103
|
+
# element_volume = box_vol / nelements
|
|
104
|
+
# # calculate the step vector of a regular cube
|
|
105
|
+
# step_vector = np.zeros(3)
|
|
106
|
+
# step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
107
|
+
# # number of steps is the length of the box / step vector
|
|
108
|
+
# nsteps = np.ceil((maximum - origin) / step_vector).astype(int)
|
|
109
|
+
# # create a structured grid using the origin and number of steps
|
|
110
|
+
|
|
111
|
+
# mesh = TetMesh(origin=origin, nsteps=nsteps, step_vector=step_vector)
|
|
112
|
+
# logger.info(
|
|
113
|
+
# f"Creating regular tetrahedron mesh with {mesh.ntetra} elements \n"
|
|
114
|
+
# "for modelling using DFI"
|
|
115
|
+
# )
|
|
116
|
+
# return DiscreteFoldInterpolator(mesh, None)
|
|
117
|
+
# raise LoopException("No interpolator")
|
|
118
|
+
# # fi interpolatortype == "DFI" and dfi is True:
|
|
119
|
+
# # if element_volume is None:
|
|
120
|
+
# # nelements /= 5
|
|
121
|
+
# # element_volume = box_vol / nelements
|
|
122
|
+
# # # calculate the step vector of a regular cube
|
|
123
|
+
# # step_vector = np.zeros(3)
|
|
124
|
+
# # step_vector[:] = element_volume ** (1.0 / 3.0)
|
|
125
|
+
# # # number of steps is the length of the box / step vector
|
|
126
|
+
# # nsteps = np.ceil((bb[1, :] - bb[0, :]) / step_vector).astype(int)
|
|
127
|
+
# # # create a structured grid using the origin and number of steps
|
|
128
|
+
# # if "meshbuilder" in kwargs:
|
|
129
|
+
# # mesh = kwargs["meshbuilder"].build(bb, nelements)
|
|
130
|
+
# # else:
|
|
131
|
+
# # mesh = kwargs.get(
|
|
132
|
+
# # "mesh",
|
|
133
|
+
# # TetMesh(origin=bb[0, :], nsteps=nsteps, step_vector=step_vector),
|
|
134
|
+
# # )
|
|
135
|
+
# # logger.info(
|
|
136
|
+
# # f"Creating regular tetrahedron mesh with {mesh.ntetra} elements \n"
|
|
137
|
+
# # "for modelling using DFI"
|
|
138
|
+
# # )
|
|
139
|
+
# # return DFI(mesh, kwargs["fold"])
|
|
140
|
+
# # if interpolatortype == "Surfe" or interpolatortype == "surfe":
|
|
141
|
+
# # # move import of surfe to where we actually try and use it
|
|
142
|
+
# # if not surfe:
|
|
143
|
+
# # logger.warning("Cannot import Surfe, try another interpolator")
|
|
144
|
+
# # raise ImportError("Cannot import surfepy, try pip install surfe")
|
|
145
|
+
# # method = kwargs.get("method", "single_surface")
|
|
146
|
+
# # logger.info("Using surfe interpolator")
|
|
147
|
+
# # return SurfeRBFInterpolator(method)
|
|
148
|
+
# # logger.warning("No interpolator")
|
|
149
|
+
# # raise InterpolatorError("Could not create interpolator")
|
|
@@ -97,7 +97,7 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
97
97
|
self.add_interface_constraints(self.interpolation_weights["ipw"])
|
|
98
98
|
self.add_value_inequality_constraints()
|
|
99
99
|
self.add_inequality_pairs_constraints(
|
|
100
|
-
kwargs.get('inequality_pairs', None),
|
|
100
|
+
pairs=kwargs.get('inequality_pairs', None),
|
|
101
101
|
upper_bound=kwargs.get('inequality_pair_upper_bound', np.finfo(float).eps),
|
|
102
102
|
lower_bound=kwargs.get('inequality_pair_lower_bound', -np.inf),
|
|
103
103
|
)
|
|
@@ -172,16 +172,18 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
172
172
|
# get elements for points
|
|
173
173
|
points = self.get_interface_constraints()
|
|
174
174
|
if points.shape[0] > 1:
|
|
175
|
-
|
|
175
|
+
node_idx, inside = self.support.position_to_cell_corners(
|
|
176
176
|
points[:, : self.support.dimension]
|
|
177
177
|
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
idc =
|
|
184
|
-
|
|
178
|
+
gi = np.zeros(self.support.n_nodes, dtype=int)
|
|
179
|
+
gi[:] = -1
|
|
180
|
+
gi[self.region] = np.arange(0, self.nx, dtype=int)
|
|
181
|
+
idc = np.zeros(node_idx.shape).astype(int)
|
|
182
|
+
idc[:] = -1
|
|
183
|
+
idc[inside, :] = gi[node_idx[inside, :]]
|
|
184
|
+
inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
|
|
185
|
+
idc = idc[inside, :]
|
|
186
|
+
A = self.support.position_to_dof_coefs(points[inside, : self.support.dimension])
|
|
185
187
|
for unique_id in np.unique(
|
|
186
188
|
points[
|
|
187
189
|
np.logical_and(~np.isnan(points[:, self.support.dimension]), inside),
|
|
@@ -197,7 +199,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
197
199
|
).T.reshape(-1, 2)
|
|
198
200
|
interface_A = np.hstack([A[mask, :][ij[:, 0], :], -A[mask, :][ij[:, 1], :]])
|
|
199
201
|
interface_idc = np.hstack([idc[mask, :][ij[:, 0], :], idc[mask, :][ij[:, 1], :]])
|
|
200
|
-
|
|
201
202
|
# now map the index from global to region create array size of mesh
|
|
202
203
|
# initialise as np.nan, then map points inside region to 0->nx
|
|
203
204
|
gi = np.zeros(self.support.n_nodes).astype(int)
|
|
@@ -229,7 +230,6 @@ class FiniteDifferenceInterpolator(DiscreteInterpolator):
|
|
|
229
230
|
points = self.get_gradient_constraints()
|
|
230
231
|
if points.shape[0] > 0:
|
|
231
232
|
# calculate unit vector for orientation data
|
|
232
|
-
# points[:,3:]/=np.linalg.norm(points[:,3:],axis=1)[:,None]
|
|
233
233
|
|
|
234
234
|
node_idx, inside = self.support.position_to_cell_corners(
|
|
235
235
|
points[:, : self.support.dimension]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from LoopStructural.interpolators import (
|
|
2
|
+
GeologicalInterpolator,
|
|
3
|
+
InterpolatorFactory,
|
|
4
|
+
InterpolatorType,
|
|
5
|
+
)
|
|
6
|
+
from LoopStructural.datatypes import BoundingBox
|
|
7
|
+
from typing import Optional, Union
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterpolatorBuilder:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
interpolatortype: Union[str, InterpolatorType],
|
|
15
|
+
bounding_box: BoundingBox,
|
|
16
|
+
nelements: int = 1000,
|
|
17
|
+
buffer: float = 0.2,
|
|
18
|
+
**kwargs,
|
|
19
|
+
):
|
|
20
|
+
self.interpolatortype = interpolatortype
|
|
21
|
+
self.bounding_box = bounding_box
|
|
22
|
+
self.nelements = nelements
|
|
23
|
+
self.buffer = buffer
|
|
24
|
+
self.kwargs = kwargs
|
|
25
|
+
self.interpolator : Optional[GeologicalInterpolator]= None
|
|
26
|
+
|
|
27
|
+
def create_interpolator(self) -> 'InterpolatorBuilder':
|
|
28
|
+
self.interpolator = InterpolatorFactory.create_interpolator(
|
|
29
|
+
interpolatortype=self.interpolatortype,
|
|
30
|
+
boundingbox=self.bounding_box,
|
|
31
|
+
nelements=self.nelements,
|
|
32
|
+
buffer=self.buffer,
|
|
33
|
+
**self.kwargs,
|
|
34
|
+
)
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
def set_value_constraints(self, value_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
38
|
+
if self.interpolator:
|
|
39
|
+
self.interpolator.set_value_constraints(value_constraints)
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def set_gradient_constraints(self, gradient_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
43
|
+
if self.interpolator:
|
|
44
|
+
self.interpolator.set_gradient_constraints(gradient_constraints)
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def set_normal_constraints(self, normal_constraints: np.ndarray) -> 'InterpolatorBuilder':
|
|
48
|
+
if self.interpolator:
|
|
49
|
+
self.interpolator.set_normal_constraints(normal_constraints)
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def setup_interpolator(self, **kwargs) -> Optional[GeologicalInterpolator]:
|
|
53
|
+
if self.interpolator:
|
|
54
|
+
self.interpolator.setup(**kwargs)
|
|
55
|
+
return self.interpolator
|
{loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/interpolators/_interpolator_factory.py
RENAMED
|
@@ -65,25 +65,14 @@ class InterpolatorFactory:
|
|
|
65
65
|
gradient_norm_constraints: Optional[np.ndarray] = None,
|
|
66
66
|
gradient_constraints: Optional[np.ndarray] = None,
|
|
67
67
|
):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
raise ValueError("No bounding box specified")
|
|
72
|
-
if nelements is None:
|
|
73
|
-
raise ValueError("No number of elements specified")
|
|
74
|
-
if isinstance(interpolatortype, str):
|
|
75
|
-
interpolatortype = InterpolatorType._member_map_[interpolatortype].numerator
|
|
76
|
-
if support is None:
|
|
77
|
-
raise Exception("Support must be specified")
|
|
78
|
-
# supporttype = support_interpolator_map[interpolatortype]
|
|
79
|
-
# support = SupportFactory.create_support(
|
|
80
|
-
# supporttype, boundingbox, nelements, element_volume
|
|
81
|
-
# )
|
|
82
|
-
interpolator = interpolator_map[interpolatortype](support)
|
|
68
|
+
interpolator = InterpolatorFactory.create_interpolator(
|
|
69
|
+
interpolatortype, boundingbox, nelements, element_volume, support
|
|
70
|
+
)
|
|
83
71
|
if value_constraints is not None:
|
|
84
|
-
interpolator.
|
|
72
|
+
interpolator.set_value_constraints(value_constraints)
|
|
85
73
|
if gradient_norm_constraints is not None:
|
|
86
|
-
interpolator.
|
|
74
|
+
interpolator.set_normal_constraints(gradient_norm_constraints)
|
|
87
75
|
if gradient_constraints is not None:
|
|
88
|
-
interpolator.
|
|
76
|
+
interpolator.set_gradient_constraints(gradient_constraints)
|
|
77
|
+
interpolator.setup()
|
|
89
78
|
return interpolator
|
|
@@ -41,6 +41,10 @@ class BaseStructuredSupport(BaseSupport):
|
|
|
41
41
|
raise LoopException("nsteps cannot be zero")
|
|
42
42
|
if np.any(nsteps < 0):
|
|
43
43
|
raise LoopException("nsteps cannot be negative")
|
|
44
|
+
if np.any(nsteps < 3):
|
|
45
|
+
raise LoopException(
|
|
46
|
+
"step vector cannot be less than 3. Try increasing the resolution of the interpolator"
|
|
47
|
+
)
|
|
44
48
|
self._nsteps = np.array(nsteps, dtype=int) + 1
|
|
45
49
|
self._step_vector = np.array(step_vector)
|
|
46
50
|
self._origin = np.array(origin)
|
|
@@ -342,9 +342,6 @@ class StructuredGrid(BaseStructuredSupport):
|
|
|
342
342
|
idc, inside = self.position_to_cell_corners(evaluation_points)
|
|
343
343
|
T = np.zeros((idc.shape[0], 3, 8))
|
|
344
344
|
T[inside, :, :] = self.get_element_gradient_for_location(evaluation_points[inside, :])[1]
|
|
345
|
-
# indices = np.array([self.position_to_cell_index(evaluation_points)])
|
|
346
|
-
# idc = self.global_indicies(indices.swapaxes(0,1))
|
|
347
|
-
# print(idc)
|
|
348
345
|
if np.max(idc[inside, :]) > property_array.shape[0]:
|
|
349
346
|
cix, ciy, ciz = self.position_to_cell_index(evaluation_points)
|
|
350
347
|
if not np.all(cix[inside] < self.nsteps_cells[0]):
|
|
@@ -25,5 +25,3 @@ except (LoopImportError, ImportError):
|
|
|
25
25
|
logger.warning(
|
|
26
26
|
"Cannot use LoopProjectfileProcessor: Loop project file cannot be imported, try installing LoopProjectFile"
|
|
27
27
|
)
|
|
28
|
-
# from LoopStructural.modelling.features import StructuralFrame
|
|
29
|
-
# from LoopStructural.modelling.features.fault import FaultSegment
|
{loopstructural-1.6.3 → loopstructural-1.6.6}/LoopStructural/modelling/core/geological_model.py
RENAMED
|
@@ -649,7 +649,7 @@ class GeologicalModel:
|
|
|
649
649
|
for g in stratigraphic_column.keys():
|
|
650
650
|
for u in stratigraphic_column[g].keys():
|
|
651
651
|
stratigraphic_column[g][u]["colour"] = cmap_colours[ci, :]
|
|
652
|
-
|
|
652
|
+
ci += 1
|
|
653
653
|
self.stratigraphic_column = stratigraphic_column
|
|
654
654
|
|
|
655
655
|
def create_and_add_foliation(
|
|
@@ -1182,7 +1182,7 @@ class GeologicalModel:
|
|
|
1182
1182
|
logger.debug(f"Reached unconformity {f.name}")
|
|
1183
1183
|
break
|
|
1184
1184
|
logger.debug(f"Adding {uc_feature.name} as unconformity to {f.name}")
|
|
1185
|
-
if f.type == FeatureType.FAULT:
|
|
1185
|
+
if f.type == FeatureType.FAULT or f.type == FeatureType.INACTIVEFAULT:
|
|
1186
1186
|
continue
|
|
1187
1187
|
if f == feature:
|
|
1188
1188
|
continue
|
|
@@ -1417,7 +1417,7 @@ class GeologicalModel:
|
|
|
1417
1417
|
return fault
|
|
1418
1418
|
|
|
1419
1419
|
# TODO move rescale to bounding box/transformer
|
|
1420
|
-
def rescale(self, points: np.ndarray, inplace: bool =
|
|
1420
|
+
def rescale(self, points: np.ndarray, inplace: bool = False) -> np.ndarray:
|
|
1421
1421
|
"""
|
|
1422
1422
|
Convert from model scale to real world scale - in the future this
|
|
1423
1423
|
should also do transformations?
|
|
@@ -1440,7 +1440,7 @@ class GeologicalModel:
|
|
|
1440
1440
|
return points
|
|
1441
1441
|
|
|
1442
1442
|
# TODO move scale to bounding box/transformer
|
|
1443
|
-
def scale(self, points: np.ndarray, inplace: bool =
|
|
1443
|
+
def scale(self, points: np.ndarray, inplace: bool = False) -> np.ndarray:
|
|
1444
1444
|
"""Take points in UTM coordinates and reproject
|
|
1445
1445
|
into scaled model space
|
|
1446
1446
|
|
|
@@ -1809,7 +1809,7 @@ class GeologicalModel:
|
|
|
1809
1809
|
grid = self.bounding_box.structured_grid(name=name)
|
|
1810
1810
|
|
|
1811
1811
|
grid.cell_properties['stratigraphy'] = self.evaluate_model(
|
|
1812
|
-
self.bounding_box.cell_centers()
|
|
1812
|
+
self.rescale(self.bounding_box.cell_centers())
|
|
1813
1813
|
)
|
|
1814
1814
|
return grid, self.stratigraphic_ids()
|
|
1815
1815
|
|
|
@@ -1824,6 +1824,7 @@ class GeologicalModel:
|
|
|
1824
1824
|
):
|
|
1825
1825
|
path = pathlib.Path(filename)
|
|
1826
1826
|
extension = path.suffix
|
|
1827
|
+
parent = path.parent
|
|
1827
1828
|
name = path.stem
|
|
1828
1829
|
stratigraphic_surfaces = self.get_stratigraphic_surfaces()
|
|
1829
1830
|
if fault_surfaces:
|
|
@@ -1832,19 +1833,19 @@ class GeologicalModel:
|
|
|
1832
1833
|
if extension == ".geoh5" or extension == '.omf':
|
|
1833
1834
|
s.save(filename)
|
|
1834
1835
|
else:
|
|
1835
|
-
s.save(f'{name}_{s.name}
|
|
1836
|
+
s.save(f'{parent}/{name}_{s.name}{extension}')
|
|
1836
1837
|
if stratigraphic_surfaces:
|
|
1837
1838
|
for s in self.get_stratigraphic_surfaces():
|
|
1838
1839
|
if extension == ".geoh5" or extension == '.omf':
|
|
1839
1840
|
s.save(filename)
|
|
1840
1841
|
else:
|
|
1841
|
-
s.save(f'{name}_{s.name}
|
|
1842
|
+
s.save(f'{parent}/{name}_{s.name}{extension}')
|
|
1842
1843
|
if block_model:
|
|
1843
1844
|
grid, ids = self.get_block_model()
|
|
1844
1845
|
if extension == ".geoh5" or extension == '.omf':
|
|
1845
1846
|
grid.save(filename)
|
|
1846
1847
|
else:
|
|
1847
|
-
grid.save(f'{name}_block_model
|
|
1848
|
+
grid.save(f'{parent}/{name}_block_model{extension}')
|
|
1848
1849
|
if stratigraphic_data:
|
|
1849
1850
|
if self.stratigraphic_column is not None:
|
|
1850
1851
|
for group in self.stratigraphic_column.keys():
|
|
@@ -1854,7 +1855,7 @@ class GeologicalModel:
|
|
|
1854
1855
|
if extension == ".geoh5" or extension == '.omf':
|
|
1855
1856
|
data.save(filename)
|
|
1856
1857
|
else:
|
|
1857
|
-
data.save(f'{name}_{group}_data
|
|
1858
|
+
data.save(f'{parent}/{name}_{group}_data{extension}')
|
|
1858
1859
|
if fault_data:
|
|
1859
1860
|
for f in self.fault_names():
|
|
1860
1861
|
for d in self.__getitem__(f).get_data():
|
|
@@ -1862,4 +1863,4 @@ class GeologicalModel:
|
|
|
1862
1863
|
|
|
1863
1864
|
d.save(filename)
|
|
1864
1865
|
else:
|
|
1865
|
-
d.save(f'{name}_{group}
|
|
1866
|
+
d.save(f'{parent}/{name}_{group}{extension}')
|
|
@@ -39,8 +39,16 @@ class AnalyticalGeologicalFeature(BaseFeature):
|
|
|
39
39
|
builder=None,
|
|
40
40
|
):
|
|
41
41
|
BaseFeature.__init__(self, name, model, faults, regions, builder)
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
try:
|
|
43
|
+
self.vector = np.array(vector, dtype=float).reshape(3)
|
|
44
|
+
except ValueError:
|
|
45
|
+
logger.error("AnalyticalGeologicalFeature: vector must be a 3 element array")
|
|
46
|
+
raise ValueError("vector must be a 3 element array")
|
|
47
|
+
try:
|
|
48
|
+
self.origin = np.array(origin, dtype=float).reshape(3)
|
|
49
|
+
except ValueError:
|
|
50
|
+
logger.error("AnalyticalGeologicalFeature: origin must be a 3 element array")
|
|
51
|
+
raise ValueError("origin must be a 3 element array")
|
|
44
52
|
self.type = FeatureType.ANALYTICAL
|
|
45
53
|
|
|
46
54
|
def to_json(self):
|
|
@@ -86,3 +94,16 @@ class AnalyticalGeologicalFeature(BaseFeature):
|
|
|
86
94
|
|
|
87
95
|
def get_data(self, value_map: Optional[dict] = None):
|
|
88
96
|
return
|
|
97
|
+
|
|
98
|
+
def copy(self, name: Optional[str] = None):
|
|
99
|
+
if name is None:
|
|
100
|
+
name = self.name
|
|
101
|
+
return AnalyticalGeologicalFeature(
|
|
102
|
+
name,
|
|
103
|
+
self.vector.copy(),
|
|
104
|
+
self.origin.copy(),
|
|
105
|
+
list(self.regions),
|
|
106
|
+
list(self.faults),
|
|
107
|
+
self.model,
|
|
108
|
+
self.builder,
|
|
109
|
+
)
|
|
@@ -291,11 +291,29 @@ class BaseFeature(metaclass=ABCMeta):
|
|
|
291
291
|
if self.model is None:
|
|
292
292
|
raise ValueError("Must specify bounding box")
|
|
293
293
|
bounding_box = self.model.bounding_box
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
regions = self.regions
|
|
295
|
+
try:
|
|
296
|
+
self.regions = [
|
|
297
|
+
r for r in self.regions if r.name != self.name and r.parent.name != self.name
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
callable = lambda xyz: (
|
|
301
|
+
self.evaluate_value(self.model.scale(xyz))
|
|
302
|
+
if self.model is not None
|
|
303
|
+
else self.evaluate_value(xyz)
|
|
304
|
+
)
|
|
305
|
+
isosurfacer = LoopIsosurfacer(bounding_box, callable=callable)
|
|
306
|
+
if name is None and self.name is not None:
|
|
307
|
+
name = self.name
|
|
308
|
+
surfaces = isosurfacer.fit(value, name)
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logger.error(f"Failed to create surface for {self.name} at value {value}")
|
|
311
|
+
logger.error(e)
|
|
312
|
+
surfaces = []
|
|
313
|
+
finally:
|
|
314
|
+
self.regions = regions
|
|
315
|
+
|
|
316
|
+
return surfaces
|
|
299
317
|
|
|
300
318
|
def scalar_field(self, bounding_box=None):
|
|
301
319
|
"""Create a scalar field for the feature
|
|
@@ -341,10 +359,10 @@ class BaseFeature(metaclass=ABCMeta):
|
|
|
341
359
|
if self.model is None:
|
|
342
360
|
raise ValueError("Must specify bounding box")
|
|
343
361
|
bounding_box = self.model.bounding_box
|
|
344
|
-
|
|
345
|
-
points = grid.points
|
|
362
|
+
points = bounding_box.cell_centers()
|
|
346
363
|
value = self.evaluate_gradient(points)
|
|
347
|
-
|
|
364
|
+
if self.model is not None:
|
|
365
|
+
points = self.model.rescale(points)
|
|
348
366
|
return VectorPoints(points, value, self.name)
|
|
349
367
|
|
|
350
368
|
@abstractmethod
|
|
@@ -362,3 +380,14 @@ class BaseFeature(metaclass=ABCMeta):
|
|
|
362
380
|
dictionary of data
|
|
363
381
|
"""
|
|
364
382
|
raise NotImplementedError
|
|
383
|
+
|
|
384
|
+
@abstractmethod
|
|
385
|
+
def copy(self, name: Optional[str] = None):
|
|
386
|
+
"""Copy the feature
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
BaseFeature
|
|
391
|
+
copied feature
|
|
392
|
+
"""
|
|
393
|
+
raise NotImplementedError
|