LoopStructural 1.5.13__tar.gz → 1.6.0__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.

Files changed (159) hide show
  1. loopstructural-1.6.0/LoopStructural/__init__.py +52 -0
  2. loopstructural-1.6.0/LoopStructural/datatypes/__init__.py +4 -0
  3. loopstructural-1.6.0/LoopStructural/datatypes/_bounding_box.py +422 -0
  4. loopstructural-1.6.0/LoopStructural/datatypes/_point.py +112 -0
  5. loopstructural-1.6.0/LoopStructural/datatypes/_structured_grid.py +90 -0
  6. loopstructural-1.6.0/LoopStructural/datatypes/_surface.py +123 -0
  7. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/export/exporters.py +24 -33
  8. loopstructural-1.6.0/LoopStructural/export/geoh5.py +100 -0
  9. loopstructural-1.6.0/LoopStructural/export/gocad.py +126 -0
  10. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/__init__.py +10 -13
  11. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_api.py +1 -0
  12. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_builders.py +0 -1
  13. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_discrete_interpolator.py +186 -336
  14. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_finite_difference_interpolator.py +13 -47
  15. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_geological_interpolator.py +26 -10
  16. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_interpolator_factory.py +4 -2
  17. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_p1interpolator.py +11 -4
  18. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_2d_base_unstructured.py +34 -10
  19. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +1 -1
  20. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_3d_base_structured.py +3 -4
  21. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_3d_structured_grid.py +1 -1
  22. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_3d_structured_tetra.py +1 -0
  23. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +0 -1
  24. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_aabb.py +1 -1
  25. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_face_table.py +6 -6
  26. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_support_factory.py +0 -3
  27. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/__init__.py +1 -1
  28. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/core/geological_model.py +212 -75
  29. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/__init__.py +1 -0
  30. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_analytical_feature.py +6 -2
  31. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_base_geological_feature.py +107 -8
  32. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_cross_product_geological_feature.py +9 -5
  33. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_geological_feature.py +73 -7
  34. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_lambda_geological_feature.py +19 -12
  35. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_structural_frame.py +41 -13
  36. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_unconformity_feature.py +12 -3
  37. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/builders/_base_builder.py +16 -1
  38. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/builders/_fault_builder.py +97 -76
  39. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +42 -17
  40. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +3 -0
  41. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fault/_fault_function.py +3 -2
  42. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fault/_fault_function_feature.py +4 -1
  43. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fault/_fault_segment.py +52 -7
  44. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fold/_foldframe.py +2 -3
  45. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/input/process_data.py +1 -1
  46. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/input/project_file.py +1 -1
  47. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/intrusion_builder.py +1 -2
  48. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/intrusion_feature.py +4 -0
  49. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +6 -1
  50. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/__init__.py +3 -1
  51. {LoopStructural-1.5.13/LoopStructural/api → loopstructural-1.6.0/LoopStructural/utils}/_surface.py +62 -22
  52. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/exceptions.py +4 -0
  53. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/helper.py +5 -0
  54. loopstructural-1.6.0/LoopStructural/version.py +1 -0
  55. loopstructural-1.6.0/LoopStructural/visualisation/__init__.py +11 -0
  56. {LoopStructural-1.5.13 → loopstructural-1.6.0/LoopStructural.egg-info}/PKG-INFO +14 -1
  57. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural.egg-info/SOURCES.txt +4 -18
  58. loopstructural-1.6.0/LoopStructural.egg-info/requires.txt +23 -0
  59. {LoopStructural-1.5.13/LoopStructural.egg-info → loopstructural-1.6.0}/PKG-INFO +14 -1
  60. {LoopStructural-1.5.13 → loopstructural-1.6.0}/pyproject.toml +14 -2
  61. LoopStructural-1.5.13/LoopStructural/__init__.py +0 -27
  62. LoopStructural-1.5.13/LoopStructural/api/__init__.py +0 -2
  63. LoopStructural-1.5.13/LoopStructural/datatypes/__init__.py +0 -2
  64. LoopStructural-1.5.13/LoopStructural/datatypes/_bounding_box.py +0 -195
  65. LoopStructural-1.5.13/LoopStructural/datatypes/_inequality.py +0 -0
  66. LoopStructural-1.5.13/LoopStructural/datatypes/_normal.py +0 -0
  67. LoopStructural-1.5.13/LoopStructural/datatypes/_point.py +0 -5
  68. LoopStructural-1.5.13/LoopStructural/datatypes/_point_set.py +0 -6
  69. LoopStructural-1.5.13/LoopStructural/datatypes/_structured_grid.py +0 -0
  70. LoopStructural-1.5.13/LoopStructural/datatypes/_surface.py +0 -30
  71. LoopStructural-1.5.13/LoopStructural/datatypes/_tangent.py +0 -0
  72. LoopStructural-1.5.13/LoopStructural/export/isosurface.py +0 -14
  73. LoopStructural-1.5.13/LoopStructural/modelling/input/ponicode/__init__.py +0 -0
  74. LoopStructural-1.5.13/LoopStructural/version.py +0 -1
  75. LoopStructural-1.5.13/LoopStructural/visualisation/__init__.py +0 -33
  76. LoopStructural-1.5.13/LoopStructural/visualisation/_dash_view.py +0 -123
  77. LoopStructural-1.5.13/LoopStructural/visualisation/_scalar_field.py +0 -76
  78. LoopStructural-1.5.13/LoopStructural/visualisation/lavavu.py +0 -454
  79. LoopStructural-1.5.13/LoopStructural/visualisation/map_viewer.py +0 -369
  80. LoopStructural-1.5.13/LoopStructural/visualisation/model_plotter.py +0 -1009
  81. LoopStructural-1.5.13/LoopStructural/visualisation/rotation_angle_plotter.py +0 -88
  82. LoopStructural-1.5.13/LoopStructural/visualisation/sphinx_scraper.py +0 -37
  83. LoopStructural-1.5.13/LoopStructural/visualisation/stratigraphic_column.py +0 -64
  84. LoopStructural-1.5.13/LoopStructural/visualisation/vtk_exporter.py +0 -128
  85. LoopStructural-1.5.13/LoopStructural.egg-info/requires.txt +0 -6
  86. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LICENSE +0 -0
  87. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/__init__.py +0 -0
  88. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/_base.py +0 -0
  89. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/_example_models.py +0 -0
  90. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/claudius.csv +0 -0
  91. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
  92. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/duplex.csv +0 -0
  93. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/duplexbb.txt +0 -0
  94. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
  95. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  96. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
  97. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  98. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  99. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
  100. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
  101. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
  102. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
  103. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
  104. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
  105. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
  106. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
  107. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
  108. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/intrusion.csv +0 -0
  109. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
  110. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
  111. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/onefolddata.csv +0 -0
  112. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
  113. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
  114. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
  115. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/export/file_formats.py +0 -0
  116. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_cython/__init__.py +0 -0
  117. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_discrete_fold_interpolator.py +0 -0
  118. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  119. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_operator.py +0 -0
  120. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_p2interpolator.py +0 -0
  121. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/_surfe_wrapper.py +0 -0
  122. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -0
  123. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_2d_structured_grid.py +0 -0
  124. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  125. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -0
  126. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/__init__.py +0 -0
  127. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/interpolators/supports/_base_support.py +0 -0
  128. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/core/__init__.py +0 -0
  129. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/_region.py +0 -0
  130. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/builders/__init__.py +0 -0
  131. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +0 -0
  132. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fault/__init__.py +0 -0
  133. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fold/__init__.py +0 -0
  134. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fold/_fold.py +0 -0
  135. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -0
  136. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -0
  137. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/features/fold/_svariogram.py +0 -0
  138. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/input/__init__.py +0 -0
  139. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/input/fault_network.py +0 -0
  140. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/input/map2loop_processor.py +0 -0
  141. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/__init__.py +0 -0
  142. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +0 -0
  143. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +0 -0
  144. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +0 -0
  145. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/_transformation.py +0 -0
  146. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/config.py +0 -0
  147. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/dtm_creator.py +0 -0
  148. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/json_encoder.py +0 -0
  149. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/linalg.py +0 -0
  150. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/logging.py +0 -0
  151. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/maths.py +0 -0
  152. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/regions.py +0 -0
  153. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/typing.py +0 -0
  154. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural/utils/utils.py +0 -0
  155. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural.egg-info/dependency_links.txt +0 -0
  156. {LoopStructural-1.5.13 → loopstructural-1.6.0}/LoopStructural.egg-info/top_level.txt +0 -0
  157. {LoopStructural-1.5.13 → loopstructural-1.6.0}/README.md +0 -0
  158. {LoopStructural-1.5.13 → loopstructural-1.6.0}/setup.cfg +0 -0
  159. {LoopStructural-1.5.13 → loopstructural-1.6.0}/setup.py +0 -0
@@ -0,0 +1,52 @@
1
+ """
2
+ LoopStructural
3
+ ==============
4
+
5
+ """
6
+
7
+ import logging
8
+ from logging.config import dictConfig
9
+
10
+ __all__ = ["GeologicalModel"]
11
+ import tempfile
12
+ from pathlib import Path
13
+ from .version import __version__
14
+
15
+ experimental = False
16
+ ch = logging.StreamHandler()
17
+ formatter = logging.Formatter("%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d -- %(message)s")
18
+ ch.setFormatter(formatter)
19
+ ch.setLevel(logging.WARNING)
20
+ loggers = {}
21
+ from .modelling.core.geological_model import GeologicalModel
22
+ from .interpolators._api import LoopInterpolator
23
+ from .datatypes import BoundingBox
24
+ from .utils import log_to_console, log_to_file, getLogger, rng, get_levels
25
+
26
+ logger = getLogger(__name__)
27
+ logger.info("Imported LoopStructural")
28
+
29
+
30
+ def setLogging(level="info"):
31
+ """
32
+ Set the logging parameters for log file
33
+
34
+ Parameters
35
+ ----------
36
+ filename : string
37
+ name of file or path to file
38
+ level : str, optional
39
+ 'info', 'warning', 'error', 'debug' mapped to logging levels, by default 'info'
40
+ """
41
+ import LoopStructural
42
+
43
+ logger = getLogger(__name__)
44
+
45
+ levels = get_levels()
46
+ level = levels.get(level, logging.WARNING)
47
+ LoopStructural.ch.setLevel(level)
48
+
49
+ for name in LoopStructural.loggers:
50
+ logger = logging.getLogger(name)
51
+ logger.setLevel(level)
52
+ logger.info(f'Set logging to {level}')
@@ -0,0 +1,4 @@
1
+ from ._surface import Surface
2
+ from ._bounding_box import BoundingBox
3
+ from ._point import ValuePoints, VectorPoints
4
+ from ._structured_grid import StructuredGrid
@@ -0,0 +1,422 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, Union, Dict
3
+ from LoopStructural.utils.exceptions import LoopValueError
4
+ from LoopStructural.utils import rng
5
+ from LoopStructural.datatypes._structured_grid import StructuredGrid
6
+ import numpy as np
7
+
8
+ from LoopStructural.utils.logging import getLogger
9
+
10
+ logger = getLogger(__name__)
11
+
12
+
13
+ class BoundingBox:
14
+ def __init__(
15
+ self,
16
+ origin: Optional[np.ndarray] = None,
17
+ maximum: Optional[np.ndarray] = None,
18
+ global_origin: Optional[np.ndarray] = None,
19
+ nsteps: Optional[np.ndarray] = None,
20
+ step_vector: Optional[np.ndarray] = None,
21
+ dimensions: Optional[int] = 3,
22
+ ):
23
+ """A bounding box for a model, defined by the
24
+ origin, maximum and number of steps in each direction
25
+
26
+ Parameters
27
+ ----------
28
+ dimensions : int, optional
29
+ _description_, by default 3
30
+ origin : Optional[np.ndarray], optional
31
+ _description_, by default None
32
+ maximum : Optional[np.ndarray], optional
33
+ _description_, by default None
34
+ nsteps : Optional[np.ndarray], optional
35
+ _description_, by default None
36
+ """
37
+ # reproject relative to the global origin, if origin is not provided.
38
+ # we want the local coordinates to start at 0
39
+ # otherwise uses provided origin. This is useful for having multiple bounding boxes rela
40
+ if global_origin is not None and origin is None:
41
+ origin = np.zeros(global_origin.shape)
42
+ if maximum is None and nsteps is not None and step_vector is not None:
43
+ maximum = origin + nsteps * step_vector
44
+ if origin is not None and global_origin is None:
45
+ global_origin = origin
46
+ self._origin = np.array(origin)
47
+ self._maximum = np.array(maximum)
48
+ self.dimensions = dimensions
49
+ if self.origin.shape:
50
+ if self.origin.shape[0] != self.dimensions:
51
+ logger.warning(
52
+ f"Origin has {self.origin.shape[0]} dimensions but bounding box has {self.dimensions}"
53
+ )
54
+
55
+ else:
56
+ self.dimensions = dimensions
57
+ self._global_origin = global_origin
58
+ self.nsteps = np.array([50, 50, 25])
59
+ if nsteps is not None:
60
+ self.nsteps = np.array(nsteps)
61
+ self.name_map = {
62
+ "xmin": (0, 0),
63
+ "ymin": (0, 1),
64
+ "zmin": (0, 2),
65
+ "xmax": (1, 0),
66
+ "ymax": (1, 1),
67
+ "zmax": (1, 2),
68
+ "lower": (0, 2),
69
+ "upper": (1, 2),
70
+ "minx": (0, 0),
71
+ "miny": (0, 1),
72
+ "minz": (0, 2),
73
+ "maxx": (1, 0),
74
+ "maxy": (1, 1),
75
+ "maxz": (1, 2),
76
+ }
77
+
78
+ @property
79
+ def global_origin(self):
80
+ return self._global_origin
81
+
82
+ @global_origin.setter
83
+ def global_origin(self, global_origin):
84
+ if self.dimensions != len(global_origin):
85
+ logger.warning(
86
+ f"Global origin has {len(global_origin)} dimensions but bounding box has {self.dimensions}"
87
+ )
88
+ self._global_origin = global_origin
89
+
90
+ @property
91
+ def global_maximum(self):
92
+ return self.maximum - self.origin + self._global_origin
93
+
94
+ @property
95
+ def valid(self):
96
+ return self._origin is not None and self._maximum is not None
97
+
98
+ @property
99
+ def origin(self) -> np.ndarray:
100
+ if self._origin is None:
101
+ raise LoopValueError("Origin is not set")
102
+ return self._origin
103
+
104
+ @origin.setter
105
+ def origin(self, origin: np.ndarray):
106
+ if self.dimensions != len(origin):
107
+ logger.warning(
108
+ f"Origin has {len(origin)} dimensions but bounding box has {self.dimensions}"
109
+ )
110
+ self._origin = origin
111
+
112
+ @property
113
+ def maximum(self) -> np.ndarray:
114
+ if self._maximum is None:
115
+ raise LoopValueError("Maximum is not set")
116
+ return self._maximum
117
+
118
+ @maximum.setter
119
+ def maximum(self, maximum: np.ndarray):
120
+ self._maximum = maximum
121
+
122
+ @property
123
+ def nelements(self):
124
+ return self.nsteps.prod()
125
+
126
+ @property
127
+ def volume(self):
128
+ return np.prod(self.maximum - self.origin)
129
+
130
+ @property
131
+ def bb(self):
132
+ return np.array([self.origin, self.maximum])
133
+
134
+ @nelements.setter
135
+ def nelements(self, nelements: Union[int, float]):
136
+ """Update the number of elements in the associated grid
137
+ This is for visualisation, not for the interpolation
138
+ When set it will update the nsteps/step vector for cubic
139
+ elements
140
+
141
+ Parameters
142
+ ----------
143
+ nelements : int,float
144
+ The new number of elements
145
+ """
146
+ box_vol = self.volume
147
+ ele_vol = box_vol / nelements
148
+ # calculate the step vector of a regular cube
149
+ step_vector = np.zeros(self.dimensions)
150
+ step_vector[:] = ele_vol ** (1.0 / 3.0)
151
+ # number of steps is the length of the box / step vector
152
+ nsteps = np.ceil((self.maximum - self.origin) / step_vector).astype(int)
153
+ self.nsteps = nsteps
154
+
155
+ @property
156
+ def corners(self) -> np.ndarray:
157
+ """Returns the corners of the bounding box in local coordinates
158
+
159
+
160
+
161
+ Returns
162
+ -------
163
+ np.ndarray
164
+ array of corners in clockwise order
165
+ """
166
+
167
+ return np.array(
168
+ [
169
+ self.origin.tolist(),
170
+ [self.maximum[0], self.origin[1], self.origin[2]],
171
+ [self.maximum[0], self.maximum[1], self.origin[2]],
172
+ [self.origin[0], self.maximum[1], self.origin[2]],
173
+ [self.origin[0], self.origin[1], self.maximum[2]],
174
+ [self.maximum[0], self.origin[1], self.maximum[2]],
175
+ self.maximum.tolist(),
176
+ [self.origin[0], self.maximum[1], self.maximum[2]],
177
+ ]
178
+ )
179
+
180
+ @property
181
+ def corners_global(self) -> np.ndarray:
182
+ """Returns the corners of the bounding box
183
+ in the original space
184
+
185
+ Returns
186
+ -------
187
+ np.ndarray
188
+ corners of the bounding box
189
+ """
190
+ return np.array(
191
+ [
192
+ self.global_origin.tolist(),
193
+ [self.global_maximum[0], self.global_origin[1], self.global_origin[2]],
194
+ [self.global_maximum[0], self.global_maximum[1], self.global_origin[2]],
195
+ [self.global_origin[0], self.global_maximum[1], self.global_origin[2]],
196
+ [self.global_origin[0], self.global_origin[1], self.global_maximum[2]],
197
+ [self.global_maximum[0], self.global_origin[1], self.global_maximum[2]],
198
+ self.global_maximum.tolist(),
199
+ [self.global_origin[0], self.global_maximum[1], self.global_maximum[2]],
200
+ ]
201
+ )
202
+
203
+ @property
204
+ def step_vector(self):
205
+ return (self.maximum - self.origin) / self.nsteps
206
+
207
+ @property
208
+ def length(self):
209
+ return self.maximum - self.origin
210
+
211
+ def fit(self, locations: np.ndarray, local_coordinate: bool = False) -> BoundingBox:
212
+ """Initialise the bounding box from a set of points.
213
+
214
+ Parameters
215
+ ----------
216
+ locations : np.ndarray
217
+ xyz locations of the points to fit the bbox
218
+ local_coordinate : bool, optional
219
+ whether to set the origin to [0,0,0], by default False
220
+
221
+ Returns
222
+ -------
223
+ BoundingBox
224
+ A reference to the bounding box object, note this is not a new bounding box
225
+ it updates the current one in place.
226
+
227
+ Raises
228
+ ------
229
+ LoopValueError
230
+ _description_
231
+ """
232
+ if locations.shape[1] != self.dimensions:
233
+ raise LoopValueError(
234
+ f"locations array is {locations.shape[1]}D but bounding box is {self.dimensions}"
235
+ )
236
+ origin = locations.min(axis=0)
237
+ maximum = locations.max(axis=0)
238
+ if local_coordinate:
239
+ self.global_origin = origin
240
+ self.origin = np.zeros(3)
241
+ self.maximum = maximum - origin
242
+ else:
243
+ self.origin = origin
244
+ self.maximum = maximum
245
+ self.global_origin = np.zeros(3)
246
+ return self
247
+
248
+ def with_buffer(self, buffer: float = 0.2) -> BoundingBox:
249
+ """Create a new bounding box with a buffer around the existing bounding box
250
+
251
+ Parameters
252
+ ----------
253
+ buffer : float, optional
254
+ percentage to expand the dimensions by, by default 0.2
255
+
256
+ Returns
257
+ -------
258
+ BoundingBox
259
+ The new bounding box object.
260
+
261
+ Raises
262
+ ------
263
+ LoopValueError
264
+ if the current bounding box is invalid
265
+ """
266
+ if self.origin is None or self.maximum is None:
267
+ raise LoopValueError("Cannot create bounding box with buffer, no origin or maximum")
268
+ # local coordinates, rescale into the original bounding boxes global coordinates
269
+ origin = self.origin - buffer * (self.maximum - self.origin)
270
+ maximum = self.maximum + buffer * (self.maximum - self.origin)
271
+ return BoundingBox(
272
+ origin=origin, maximum=maximum, global_origin=self.global_origin + origin
273
+ )
274
+
275
+ def get_value(self, name):
276
+ ix, iy = self.name_map.get(name, (-1, -1))
277
+ if ix == -1 and iy == -1:
278
+ raise LoopValueError(f"{name} is not a valid bounding box name")
279
+ if iy == -1:
280
+ return self.origin[ix]
281
+
282
+ return self.bb[ix,]
283
+
284
+ def __getitem__(self, name):
285
+ if isinstance(name, str):
286
+ return self.get_value(name)
287
+ elif isinstance(name, tuple):
288
+ return self.origin
289
+ return self.get_value(name)
290
+
291
+ def is_inside(self, xyz):
292
+ xyz = np.array(xyz)
293
+ if len(xyz.shape) == 1:
294
+ xyz = xyz.reshape((1, -1))
295
+ if xyz.shape[1] != 3:
296
+ raise LoopValueError(
297
+ f"locations array is {xyz.shape[1]}D but bounding box is {self.dimensions}"
298
+ )
299
+ inside = np.ones(xyz.shape[0], dtype=bool)
300
+ inside = np.logical_and(inside, xyz[:, 0] > self.origin[0])
301
+ inside = np.logical_and(inside, xyz[:, 0] < self.maximum[0])
302
+ inside = np.logical_and(inside, xyz[:, 1] > self.origin[1])
303
+ inside = np.logical_and(inside, xyz[:, 1] < self.maximum[1])
304
+ inside = np.logical_and(inside, xyz[:, 2] > self.origin[2])
305
+ inside = np.logical_and(inside, xyz[:, 2] < self.maximum[2])
306
+ return inside
307
+
308
+ def regular_grid(
309
+ self,
310
+ nsteps: Optional[Union[list, np.ndarray]] = None,
311
+ shuffle: bool = False,
312
+ order: str = "C",
313
+ local: bool = True,
314
+ ) -> np.ndarray:
315
+ """Get the grid of points from the bounding box
316
+
317
+ Parameters
318
+ ----------
319
+ nsteps : Optional[Union[list, np.ndarray]], optional
320
+ number of steps, by default None uses self.nsteps
321
+ shuffle : bool, optional
322
+ Whether to return points in order or random, by default False
323
+ order : str, optional
324
+ when flattening using numpy "C" or "F", by default "C"
325
+ local : bool, optional
326
+ Whether to return the points in the local coordinate system of global
327
+ , by default True
328
+
329
+ Returns
330
+ -------
331
+ np.ndarray
332
+ numpy array N,3 of the points
333
+ """
334
+
335
+ if nsteps is None:
336
+ nsteps = self.nsteps
337
+ coordinates = [
338
+ np.linspace(self.origin[i], self.maximum[i], nsteps[i]) for i in range(self.dimensions)
339
+ ]
340
+
341
+ if not local:
342
+ coordinates = [
343
+ np.linspace(self.global_origin[i], self.global_maximum[i], nsteps[i])
344
+ for i in range(self.dimensions)
345
+ ]
346
+ coordinate_grid = np.meshgrid(*coordinates, indexing="ij")
347
+ locs = np.array([coord.flatten(order=order) for coord in coordinate_grid]).T
348
+
349
+ if shuffle:
350
+ # logger.info("Shuffling points")
351
+ rng.shuffle(locs)
352
+ return locs
353
+
354
+ def cell_centers(self, order: str = "F") -> np.ndarray:
355
+ """Get the cell centers of a regular grid
356
+
357
+ Parameters
358
+ ----------
359
+ order : str, optional
360
+ order of the grid, by default "C"
361
+
362
+ Returns
363
+ -------
364
+ np.ndarray
365
+ array of cell centers
366
+ """
367
+ locs = self.regular_grid(order=order, nsteps=self.nsteps - 1)
368
+
369
+ return locs + 0.5 * self.step_vector
370
+
371
+ def to_dict(self) -> dict:
372
+ """Export the defining characteristics of the bounding
373
+ box to a dictionary for json serialisation
374
+
375
+ Returns
376
+ -------
377
+ dict
378
+ dictionary with origin, maximum and nsteps
379
+ """
380
+ return {
381
+ "origin": self.origin.tolist(),
382
+ "maximum": self.maximum.tolist(),
383
+ "nsteps": self.nsteps.tolist(),
384
+ }
385
+
386
+ def vtk(self):
387
+ """Export the model as a pyvista RectilinearGrid
388
+
389
+ Returns
390
+ -------
391
+ pv.RectilinearGrid
392
+ a pyvista grid object
393
+
394
+ Raises
395
+ ------
396
+ ImportError
397
+ If pyvista is not installed raise import error
398
+ """
399
+ try:
400
+ import pyvista as pv
401
+ except ImportError:
402
+ raise ImportError("pyvista is required for vtk support")
403
+ x = np.linspace(self.global_origin[0], self.global_maximum[0], self.nsteps[0])
404
+ y = np.linspace(self.global_origin[1], self.global_maximum[1], self.nsteps[1])
405
+ z = np.linspace(self.global_origin[2], self.global_maximum[2], self.nsteps[2])
406
+ return pv.RectilinearGrid(
407
+ x,
408
+ y,
409
+ z,
410
+ )
411
+
412
+ def structured_grid(
413
+ self, cell_data: Dict[str, np.ndarray] = {}, vertex_data={}, name: str = "bounding_box"
414
+ ):
415
+ return StructuredGrid(
416
+ origin=self.global_origin,
417
+ step_vector=self.step_vector,
418
+ nsteps=self.nsteps,
419
+ properties_cell=cell_data,
420
+ properties_vertex=vertex_data,
421
+ name=name,
422
+ )
@@ -0,0 +1,112 @@
1
+ from dataclasses import dataclass
2
+ import numpy as np
3
+
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class ValuePoints:
9
+ locations: np.ndarray
10
+ values: np.ndarray
11
+ name: str
12
+ properties: Optional[dict] = None
13
+
14
+ def to_dict(self):
15
+ return {
16
+ "locations": self.locations,
17
+ "values": self.values,
18
+ "name": self.name,
19
+ }
20
+
21
+ def vtk(self):
22
+ import pyvista as pv
23
+
24
+ points = pv.PolyData(self.locations)
25
+ points["values"] = self.values
26
+ return points
27
+
28
+ def save(self, filename):
29
+ filename = str(filename)
30
+ ext = filename.split('.')[-1]
31
+ if ext == 'json':
32
+ import json
33
+
34
+ with open(filename, 'w') as f:
35
+ json.dump(self.to_dict(), f)
36
+ elif ext == 'vtk':
37
+ self.vtk().save(filename)
38
+
39
+ elif ext == 'geoh5':
40
+ from LoopStructural.export.geoh5 import add_points_to_geoh5
41
+
42
+ add_points_to_geoh5(filename, self)
43
+ elif ext == 'pkl':
44
+ import pickle
45
+
46
+ with open(filename, 'wb') as f:
47
+ pickle.dump(self, f)
48
+ elif ext == 'vs':
49
+ from LoopStructural.export.gocad import _write_pointset
50
+
51
+ _write_pointset(self, filename)
52
+ else:
53
+ raise ValueError(f'Unknown file extension {ext}')
54
+
55
+
56
+ @dataclass
57
+ class VectorPoints:
58
+ locations: np.ndarray
59
+ vectors: np.ndarray
60
+ name: str
61
+ properties: Optional[dict] = None
62
+
63
+ def to_dict(self):
64
+ return {
65
+ "locations": self.locations,
66
+ "vectors": self.vectors,
67
+ "name": self.name,
68
+ }
69
+
70
+ def vtk(self, geom='arrow', scale=1.0, scale_function=None, tolerance=0.05):
71
+ import pyvista as pv
72
+
73
+ vectors = np.copy(self.vectors)
74
+ if scale_function is not None:
75
+ vectors /= np.linalg.norm(vectors, axis=1)[:, None]
76
+ vectors *= scale_function(self.locations)[:, None]
77
+ points = pv.PolyData(self.locations)
78
+ points.point_data.set_vectors(vectors, 'vectors')
79
+ if geom == 'arrow':
80
+ geom = pv.Arrow(scale=scale)
81
+ elif geom == 'disc':
82
+ geom = pv.Disc(inner=0, outer=scale)
83
+
84
+ # Perform the glyph
85
+ return points.glyph(orient="vectors", geom=geom, tolerance=tolerance)
86
+
87
+ def save(self, filename):
88
+ filename = str(filename)
89
+ ext = filename.split('.')[-1]
90
+ if ext == 'json':
91
+ import json
92
+
93
+ with open(filename, 'w') as f:
94
+ json.dump(self.to_dict(), f)
95
+ elif ext == 'vtk':
96
+ self.vtk().save(filename)
97
+
98
+ elif ext == 'geoh5':
99
+ from LoopStructural.export.geoh5 import add_points_to_geoh5
100
+
101
+ add_points_to_geoh5(filename, self)
102
+ elif ext == 'pkl':
103
+ import pickle
104
+
105
+ with open(filename, 'wb') as f:
106
+ pickle.dump(self, f)
107
+ elif ext == 'vs':
108
+ from LoopStructural.export.gocad import _write_pointset
109
+
110
+ _write_pointset(self, filename)
111
+ else:
112
+ raise ValueError(f'Unknown file extension {ext}')
@@ -0,0 +1,90 @@
1
+ from typing import Dict
2
+ import numpy as np
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class StructuredGrid:
8
+ origin: np.ndarray
9
+ step_vector: np.ndarray
10
+ nsteps: np.ndarray
11
+ properties_cell: Dict[str, np.ndarray]
12
+ properties_vertex: Dict[str, np.ndarray]
13
+ name: str
14
+
15
+ def to_dict(self):
16
+ return {
17
+ "origin": self.origin,
18
+ "maximum": self.maximum,
19
+ "step_vector": self.step_vector,
20
+ "nsteps": self.nsteps,
21
+ "properties_cell": self.properties_cell,
22
+ "properties_vertex": self.properties_vertex,
23
+ "name": self.name,
24
+ }
25
+
26
+ @property
27
+ def maximum(self):
28
+ return self.origin + self.nsteps * self.step_vector
29
+
30
+ def vtk(self):
31
+ try:
32
+ import pyvista as pv
33
+ except ImportError:
34
+ raise ImportError("pyvista is required for vtk support")
35
+ x = np.linspace(self.origin[0], self.maximum[0], self.nsteps[0])
36
+ y = np.linspace(self.origin[1], self.maximum[1], self.nsteps[1])
37
+ z = np.linspace(self.origin[2], self.maximum[2], self.nsteps[2])
38
+ grid = pv.RectilinearGrid(
39
+ x,
40
+ y,
41
+ z,
42
+ )
43
+ for name, data in self.properties_vertex.items():
44
+ grid[name] = data.flatten(order="F")
45
+ for name, data in self.properties_cell.items():
46
+ grid.cell_data[name] = data.flatten(order="F")
47
+ return grid
48
+
49
+ def merge(self, other):
50
+ if not np.all(np.isclose(self.origin, other.origin)):
51
+ raise ValueError("Origin of grids must be the same")
52
+ if not np.all(np.isclose(self.step_vector, other.step_vector)):
53
+ raise ValueError("Step vector of grids must be the same")
54
+ if not np.all(np.isclose(self.nsteps, other.nsteps)):
55
+ raise ValueError("Number of steps of grids must be the same")
56
+
57
+ for name, data in other.properties_cell.items():
58
+ self.properties_cell[name] = data
59
+ for name, data in other.properties_vertex.items():
60
+ self.properties_vertex[name] = data
61
+
62
+ def save(self, filename):
63
+ filename = str(filename)
64
+ ext = filename.split('.')[-1]
65
+ if ext == 'json':
66
+ import json
67
+
68
+ with open(filename, 'w') as f:
69
+ json.dump(self.to_dict(), f)
70
+ elif ext == 'vtk':
71
+ self.vtk().save(filename)
72
+
73
+ elif ext == 'geoh5':
74
+ from LoopStructural.export.geoh5 import add_structured_grid_to_geoh5
75
+
76
+ add_structured_grid_to_geoh5(filename, self)
77
+ elif ext == 'pkl':
78
+ import pickle
79
+
80
+ with open(filename, 'wb') as f:
81
+ pickle.dump(self, f)
82
+ elif ext == 'vs':
83
+ raise NotImplementedError(
84
+ "Saving structured grids in gocad format is not yet implemented"
85
+ )
86
+ # from LoopStructural.export.gocad import _write_structued_grid
87
+
88
+ # _write_pointset(self, filename)
89
+ else:
90
+ raise ValueError(f'Unknown file extension {ext}')