LoopStructural 1.6.26__tar.gz → 1.7.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.
Files changed (165) hide show
  1. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/__init__.py +49 -9
  2. loopstructural-1.7.0/LoopStructural/datasets/__init__.py +26 -0
  3. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/_base.py +3 -6
  4. loopstructural-1.7.0/LoopStructural/datasets/_example_models.py +11 -0
  5. loopstructural-1.7.0/LoopStructural/datatypes/__init__.py +27 -0
  6. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/export/exporters.py +16 -13
  7. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/export/geoh5.py +99 -6
  8. loopstructural-1.7.0/LoopStructural/export/gocad.py +269 -0
  9. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/export/omf_wrapper.py +4 -4
  10. loopstructural-1.7.0/LoopStructural/geometry/__init__.py +29 -0
  11. {loopstructural-1.6.26/LoopStructural/datatypes → loopstructural-1.7.0/LoopStructural/geometry}/_structured_grid.py +27 -25
  12. loopstructural-1.7.0/LoopStructural/interpolators/__init__.py +66 -0
  13. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/interpolators/_api.py +50 -32
  14. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/interpolators/_builders.py +1 -1
  15. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/interpolators/_constant_norm.py +68 -32
  16. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/interpolators/_discrete_fold_interpolator.py +9 -7
  17. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/interpolators/_interpolatortype.py +1 -0
  18. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/__init__.py +4 -5
  19. loopstructural-1.7.0/LoopStructural/modelling/core/_feature_registry.py +30 -0
  20. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/core/fault_topology.py +20 -17
  21. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/core/geological_model.py +841 -146
  22. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/core/stratigraphic_column.py +34 -24
  23. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/__init__.py +3 -4
  24. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_analytical_feature.py +23 -9
  25. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_base_geological_feature.py +79 -39
  26. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_cross_product_geological_feature.py +4 -5
  27. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_feature_converters.py +5 -3
  28. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_geological_feature.py +38 -22
  29. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_lambda_geological_feature.py +67 -37
  30. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_projected_vector_feature.py +4 -5
  31. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_structural_frame.py +8 -5
  32. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_unconformity_feature.py +3 -4
  33. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/builders/__init__.py +3 -3
  34. loopstructural-1.7.0/LoopStructural/modelling/features/builders/_analytical_fold_builder.py +41 -0
  35. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/builders/_base_builder.py +4 -0
  36. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/builders/_fault_builder.py +12 -8
  37. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +22 -17
  38. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +30 -23
  39. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +33 -17
  40. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fault/__init__.py +1 -1
  41. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fault/_fault_function.py +28 -26
  42. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fault/_fault_function_feature.py +10 -6
  43. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fault/_fault_segment.py +34 -25
  44. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +17 -6
  45. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/_foldframe.py +6 -6
  46. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/_svariogram.py +44 -50
  47. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/fold_function/__init__.py +9 -5
  48. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +46 -31
  49. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +29 -19
  50. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +11 -5
  51. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +20 -10
  52. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/input/map2loop_processor.py +3 -4
  53. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/input/process_data.py +6 -6
  54. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/input/project_file.py +2 -1
  55. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/__init__.py +11 -9
  56. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +10 -3
  57. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +2 -3
  58. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/intrusion_builder.py +31 -20
  59. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/intrusion_feature.py +10 -9
  60. loopstructural-1.7.0/LoopStructural/modelling/intrusions/intrusion_frame.py +10 -0
  61. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +30 -19
  62. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +110 -181
  63. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/__init__.py +35 -19
  64. loopstructural-1.7.0/LoopStructural/utils/_api_registry.py +59 -0
  65. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/_surface.py +24 -18
  66. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/_transformation.py +9 -8
  67. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/dtm_creator.py +6 -2
  68. loopstructural-1.7.0/LoopStructural/utils/exceptions.py +17 -0
  69. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/helper.py +4 -4
  70. loopstructural-1.7.0/LoopStructural/utils/logging.py +175 -0
  71. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/maths.py +21 -20
  72. loopstructural-1.7.0/LoopStructural/utils/observer.py +9 -0
  73. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/regions.py +14 -15
  74. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/typing.py +2 -2
  75. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/utils.py +3 -1
  76. loopstructural-1.7.0/LoopStructural/version.py +1 -0
  77. loopstructural-1.7.0/LoopStructural/visualisation/__init__.py +15 -0
  78. {loopstructural-1.6.26 → loopstructural-1.7.0/LoopStructural.egg-info}/PKG-INFO +11 -4
  79. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural.egg-info/SOURCES.txt +5 -28
  80. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural.egg-info/requires.txt +10 -1
  81. {loopstructural-1.6.26/LoopStructural.egg-info → loopstructural-1.7.0}/PKG-INFO +11 -4
  82. {loopstructural-1.6.26 → loopstructural-1.7.0}/README.md +1 -1
  83. loopstructural-1.7.0/pyproject.toml +317 -0
  84. {loopstructural-1.6.26 → loopstructural-1.7.0}/setup.py +4 -4
  85. loopstructural-1.6.26/LoopStructural/datasets/__init__.py +0 -24
  86. loopstructural-1.6.26/LoopStructural/datasets/_example_models.py +0 -10
  87. loopstructural-1.6.26/LoopStructural/datatypes/__init__.py +0 -4
  88. loopstructural-1.6.26/LoopStructural/datatypes/_bounding_box.py +0 -696
  89. loopstructural-1.6.26/LoopStructural/datatypes/_point.py +0 -232
  90. loopstructural-1.6.26/LoopStructural/datatypes/_surface.py +0 -248
  91. loopstructural-1.6.26/LoopStructural/export/gocad.py +0 -126
  92. loopstructural-1.6.26/LoopStructural/interpolators/__init__.py +0 -132
  93. loopstructural-1.6.26/LoopStructural/interpolators/_discrete_interpolator.py +0 -795
  94. loopstructural-1.6.26/LoopStructural/interpolators/_finite_difference_interpolator.py +0 -519
  95. loopstructural-1.6.26/LoopStructural/interpolators/_geological_interpolator.py +0 -549
  96. loopstructural-1.6.26/LoopStructural/interpolators/_interpolator_builder.py +0 -134
  97. loopstructural-1.6.26/LoopStructural/interpolators/_interpolator_factory.py +0 -77
  98. loopstructural-1.6.26/LoopStructural/interpolators/_operator.py +0 -38
  99. loopstructural-1.6.26/LoopStructural/interpolators/_p1interpolator.py +0 -233
  100. loopstructural-1.6.26/LoopStructural/interpolators/_p2interpolator.py +0 -278
  101. loopstructural-1.6.26/LoopStructural/interpolators/_surfe_wrapper.py +0 -210
  102. loopstructural-1.6.26/LoopStructural/interpolators/supports/_2d_base_unstructured.py +0 -359
  103. loopstructural-1.6.26/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +0 -68
  104. loopstructural-1.6.26/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -288
  105. loopstructural-1.6.26/LoopStructural/interpolators/supports/_2d_structured_grid.py +0 -516
  106. loopstructural-1.6.26/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  107. loopstructural-1.6.26/LoopStructural/interpolators/supports/_3d_base_structured.py +0 -527
  108. loopstructural-1.6.26/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -331
  109. loopstructural-1.6.26/LoopStructural/interpolators/supports/_3d_structured_grid.py +0 -499
  110. loopstructural-1.6.26/LoopStructural/interpolators/supports/_3d_structured_tetra.py +0 -754
  111. loopstructural-1.6.26/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +0 -651
  112. loopstructural-1.6.26/LoopStructural/interpolators/supports/__init__.py +0 -62
  113. loopstructural-1.6.26/LoopStructural/interpolators/supports/_aabb.py +0 -77
  114. loopstructural-1.6.26/LoopStructural/interpolators/supports/_base_support.py +0 -125
  115. loopstructural-1.6.26/LoopStructural/interpolators/supports/_face_table.py +0 -70
  116. loopstructural-1.6.26/LoopStructural/interpolators/supports/_support_factory.py +0 -40
  117. loopstructural-1.6.26/LoopStructural/modelling/features/builders/_analytical_fold_builder.py +0 -21
  118. loopstructural-1.6.26/LoopStructural/utils/exceptions.py +0 -31
  119. loopstructural-1.6.26/LoopStructural/utils/logging.py +0 -79
  120. loopstructural-1.6.26/LoopStructural/utils/observer.py +0 -240
  121. loopstructural-1.6.26/LoopStructural/version.py +0 -1
  122. loopstructural-1.6.26/LoopStructural/visualisation/__init__.py +0 -11
  123. loopstructural-1.6.26/pyproject.toml +0 -165
  124. {loopstructural-1.6.26 → loopstructural-1.7.0}/LICENSE +0 -0
  125. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/claudius.csv +0 -0
  126. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
  127. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/duplex.csv +0 -0
  128. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/duplexbb.txt +0 -0
  129. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
  130. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  131. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
  132. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  133. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  134. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
  135. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
  136. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
  137. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
  138. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
  139. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
  140. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
  141. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
  142. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
  143. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/intrusion.csv +0 -0
  144. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
  145. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
  146. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/onefolddata.csv +0 -0
  147. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
  148. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
  149. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
  150. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/export/file_formats.py +0 -0
  151. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/interpolators/_cython/__init__.py +0 -0
  152. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/core/__init__.py +0 -0
  153. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/_region.py +0 -0
  154. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/__init__.py +2 -2
  155. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/features/fold/_fold.py +0 -0
  156. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/input/__init__.py +0 -0
  157. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/modelling/input/fault_network.py +0 -0
  158. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/colours.py +0 -0
  159. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/config.py +0 -0
  160. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/features.py +0 -0
  161. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/json_encoder.py +0 -0
  162. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural/utils/linalg.py +0 -0
  163. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural.egg-info/dependency_links.txt +0 -0
  164. {loopstructural-1.6.26 → loopstructural-1.7.0}/LoopStructural.egg-info/top_level.txt +0 -0
  165. {loopstructural-1.6.26 → loopstructural-1.7.0}/setup.cfg +0 -0
@@ -5,14 +5,35 @@ LoopStructural
5
5
  """
6
6
 
7
7
  import logging
8
- from logging.config import dictConfig
9
-
10
8
  from dataclasses import dataclass
9
+ from logging.config import dictConfig
11
10
 
12
-
13
- __all__ = ["GeologicalModel"]
11
+ __all__ = [
12
+ "BoundingBox",
13
+ "FaultTopology",
14
+ "FileSink",
15
+ "GeologicalModel",
16
+ "InterpolatorBuilder",
17
+ "LogSink",
18
+ "LoopInterpolator",
19
+ "LoopStructuralConfig",
20
+ "SqliteSink",
21
+ "StratigraphicColumn",
22
+ "StreamSink",
23
+ "add_sink",
24
+ "getLogger",
25
+ "get_levels",
26
+ "log_to_console",
27
+ "log_to_file",
28
+ "remove_sink",
29
+ "rng",
30
+ "setLogging",
31
+ "timed",
32
+ "timed_stage",
33
+ ]
14
34
  import tempfile
15
35
  from pathlib import Path
36
+
16
37
  from .version import __version__
17
38
 
18
39
  experimental = False
@@ -21,6 +42,11 @@ formatter = logging.Formatter("%(levelname)s: %(asctime)s: %(filename)s:%(lineno
21
42
  ch.setFormatter(formatter)
22
43
  ch.setLevel(logging.WARNING)
23
44
  loggers = {}
45
+ # Handlers attached via LoopStructural.utils.add_sink(); replayed onto every
46
+ # logger getLogger() creates from here on, in addition to the default `ch`.
47
+ _extra_sinks = []
48
+
49
+
24
50
  @dataclass
25
51
  class LoopStructuralConfig:
26
52
  """Configuration for LoopStructural package.
@@ -42,13 +68,27 @@ class LoopStructuralConfig:
42
68
 
43
69
  nelements: int = 10_000
44
70
 
71
+ from .geometry import BoundingBox
72
+ from .interpolators import InterpolatorBuilder
73
+ from .interpolators._api import LoopInterpolator
74
+ from .modelling.core.fault_topology import FaultTopology
45
75
  from .modelling.core.geological_model import GeologicalModel
46
76
  from .modelling.core.stratigraphic_column import StratigraphicColumn
47
- from .modelling.core.fault_topology import FaultTopology
48
- from .interpolators._api import LoopInterpolator
49
- from .interpolators import InterpolatorBuilder
50
- from .datatypes import BoundingBox
51
- from .utils import log_to_console, log_to_file, getLogger, rng, get_levels
77
+ from .utils import (
78
+ FileSink,
79
+ LogSink,
80
+ SqliteSink,
81
+ StreamSink,
82
+ add_sink,
83
+ get_levels,
84
+ getLogger,
85
+ log_to_console,
86
+ log_to_file,
87
+ remove_sink,
88
+ rng,
89
+ timed,
90
+ timed_stage,
91
+ )
52
92
 
53
93
  logger = getLogger(__name__)
54
94
  logger.info("Imported LoopStructural")
@@ -0,0 +1,26 @@
1
+ """
2
+ Demo Datasets
3
+ =============
4
+
5
+ Various datasets used for documentation and tutorials.
6
+ """
7
+
8
+ from ._base import (
9
+ load_claudius,
10
+ load_duplex,
11
+ load_fault_trace,
12
+ load_geological_map_data,
13
+ load_grose2017,
14
+ load_grose2018,
15
+ load_grose2019,
16
+ load_horizontal,
17
+ load_horizontal_v,
18
+ load_intrusion,
19
+ load_laurent2016,
20
+ load_noddy_single_fold,
21
+ load_tabular_intrusion,
22
+ load_unconformity,
23
+ normal_vector_headers,
24
+ strike_dip_headers,
25
+ value_headers,
26
+ )
@@ -1,11 +1,11 @@
1
1
  from os.path import dirname, join
2
2
  from pathlib import Path
3
- from typing import Tuple
3
+
4
4
  import numpy as np
5
5
  import pandas as pd
6
6
 
7
7
 
8
- def load_horizontal() -> Tuple[pd.DataFrame, np.ndarray]:
8
+ def load_horizontal() -> tuple[pd.DataFrame, np.ndarray]:
9
9
  """Synthetic model for horizontal layers
10
10
 
11
11
  Returns
@@ -41,7 +41,7 @@ def load_horizontal() -> Tuple[pd.DataFrame, np.ndarray]:
41
41
  return data, bb
42
42
 
43
43
 
44
- def load_horizontal_v(v=0.5) -> Tuple[pd.DataFrame, np.ndarray]:
44
+ def load_horizontal_v(v=0.5) -> tuple[pd.DataFrame, np.ndarray]:
45
45
  """Synthetic model for horizontal layers
46
46
 
47
47
  Returns
@@ -152,7 +152,6 @@ def load_grose2017():
152
152
  tuple
153
153
  pandas data frame with loopstructural dataset and numpy array for bounding box
154
154
  """
155
- pass
156
155
 
157
156
 
158
157
  def load_grose2018():
@@ -164,7 +163,6 @@ def load_grose2018():
164
163
  tuple
165
164
  pandas data frame with loopstructural dataset and numpy array for bounding box
166
165
  """
167
- pass
168
166
 
169
167
 
170
168
  def load_grose2019():
@@ -176,7 +174,6 @@ def load_grose2019():
176
174
  tuple
177
175
  pandas data frame with loopstructural dataset and numpy array for bounding box
178
176
  """
179
- pass
180
177
 
181
178
 
182
179
  def load_intrusion():
@@ -0,0 +1,11 @@
1
+ from ..utils import getLogger
2
+
3
+ logger = getLogger(__name__)
4
+
5
+ vis = True
6
+
7
+ # Visualization is optional for this module and is enabled by default.
8
+
9
+
10
+ def _build_claudius():
11
+ pass
@@ -0,0 +1,27 @@
1
+ """Deprecated import path.
2
+
3
+ ``BoundingBox``, ``Surface``, ``ValuePoints`` and ``VectorPoints`` moved to
4
+ :mod:`LoopStructural.geometry` in v1.6.x. This shim re-exports them so
5
+ existing consumers (e.g. the LoopStructural QGIS plugin) keep working, and
6
+ will be removed after two minor releases per the versioning policy in
7
+ ``ROADMAP.md``.
8
+ """
9
+
10
+ import warnings
11
+
12
+ from ..geometry import BoundingBox, Surface, ValuePoints, VectorPoints
13
+
14
+ warnings.warn(
15
+ "LoopStructural.datatypes is deprecated and will be removed in a future "
16
+ "release; import BoundingBox, Surface, ValuePoints and VectorPoints from "
17
+ "LoopStructural.geometry instead.",
18
+ DeprecationWarning,
19
+ stacklevel=2,
20
+ )
21
+
22
+ __all__ = [
23
+ "BoundingBox",
24
+ "Surface",
25
+ "ValuePoints",
26
+ "VectorPoints",
27
+ ]
@@ -3,14 +3,15 @@ Routines to export geological model data to file in a variety of formats
3
3
  """
4
4
 
5
5
  import os
6
- from pyevtk.hl import unstructuredGridToVTK, pointsToVTK
7
- from pyevtk.vtk import VtkTriangle
6
+
8
7
  import numpy as np
8
+ from pyevtk.hl import pointsToVTK, unstructuredGridToVTK
9
+ from pyevtk.vtk import VtkTriangle
9
10
  from skimage.measure import marching_cubes
10
11
 
11
- from LoopStructural.utils.helper import create_box
12
12
  from LoopStructural.export.file_formats import FileFormat
13
- from LoopStructural.datatypes import Surface
13
+ from LoopStructural.geometry import Surface
14
+ from LoopStructural.utils.helper import create_box
14
15
 
15
16
  from ..utils import getLogger
16
17
 
@@ -193,7 +194,7 @@ def _write_feat_surfs_evtk(surf, file_name):
193
194
  cell_types=cell_types,
194
195
  pointData={"values": pointData},
195
196
  )
196
- except Exception as e:
197
+ except (OSError, ValueError) as e:
197
198
  logger.warning(f"Cannot export fault surface to VTK file {file_name}: {e}")
198
199
  return False
199
200
 
@@ -396,7 +397,7 @@ def _write_cubeface_evtk(model, file_name, data_label, nsteps, real_coords=True)
396
397
  cellData=None,
397
398
  pointData={data_label: val},
398
399
  )
399
- except Exception as e:
400
+ except (OSError, ValueError) as e:
400
401
  logger.warning(f"Cannot export cuboid surface to VTK file {file_name}: {e}")
401
402
  return False
402
403
  return True
@@ -422,8 +423,9 @@ def _write_vol_evtk(model, file_name, data_label, nsteps, real_coords=True):
422
423
  True if successful
423
424
 
424
425
  """
425
- # Define grid spacing
426
- xyz = model.bounding_box.regular_grid(nsteps=nsteps)
426
+ # Define grid spacing (world coordinates -- evaluate_model/features now
427
+ # project world -> local internally)
428
+ xyz = model.bounding_box.regular_grid(nsteps=nsteps, local=False)
427
429
  vals = model.evaluate_model(xyz, scale=False)
428
430
  if real_coords:
429
431
  model.rescale(xyz)
@@ -438,7 +440,7 @@ def _write_vol_evtk(model, file_name, data_label, nsteps, real_coords=True):
438
440
  # Write to grid
439
441
  try:
440
442
  pointsToVTK(file_name, x, y, z, data={data_label: vals})
441
- except Exception as e:
443
+ except (OSError, ValueError) as e:
442
444
  logger.warning(f"Cannot export volume to VTK file {file_name}: {e}")
443
445
  return False
444
446
  return True
@@ -464,8 +466,9 @@ def _write_vol_gocad(model, file_name, data_label, nsteps, real_coords=True):
464
466
  True if successful
465
467
 
466
468
  """
467
- # Define grid spacing in model scale coords
468
- xyz = model.bounding_box.regular_grid(nsteps=nsteps)
469
+ # Define grid spacing (world coordinates -- evaluate_model/features now
470
+ # project world -> local internally)
471
+ xyz = model.bounding_box.regular_grid(nsteps=nsteps, local=False)
469
472
 
470
473
  vals = model.evaluate_model(xyz, scale=False)
471
474
  # Use FORTRAN style indexing for GOCAD VOXET
@@ -539,7 +542,7 @@ PROP_OFFSET 1 0
539
542
  PROP_FILE 1 {data_filename}
540
543
  END\n"""
541
544
  )
542
- except IOError as exc:
545
+ except OSError as exc:
543
546
  logger.warning(f"Cannot export volume to GOCAD VOXET file {vo_filename}: {exc}")
544
547
  return False
545
548
 
@@ -548,7 +551,7 @@ END\n"""
548
551
  try:
549
552
  with open(data_filename, "wb") as fp:
550
553
  export_vals.tofile(fp)
551
- except IOError as exc:
554
+ except OSError as exc:
552
555
  logger.warning(f"Cannot export volume to GOCAD VOXET data file {data_filename}: {exc}")
553
556
  return False
554
557
  return True
@@ -1,7 +1,26 @@
1
1
  import geoh5py
2
2
  import geoh5py.workspace
3
3
  import numpy as np
4
- from LoopStructural.datatypes import ValuePoints, VectorPoints
4
+
5
+ from LoopStructural.geometry import ValuePoints, VectorPoints
6
+
7
+
8
+ def add_group_to_geoh5(filename, groupname="Loop", parent=None, overwrite=True):
9
+ with geoh5py.workspace.Workspace(filename) as workspace:
10
+
11
+ group = workspace.get_entity(groupname)[0]
12
+ if group and overwrite:
13
+ group.allow_delete = True
14
+ workspace.remove_entity(group)
15
+ if not group:
16
+ group = geoh5py.groups.ContainerGroup.create(
17
+ workspace, name=groupname, allow_delete=True
18
+ )
19
+ if parent is not None:
20
+ parent = workspace.get_entity(parent)[0]
21
+ if parent:
22
+ parent.add_children(group)
23
+ return group.uid
5
24
 
6
25
 
7
26
  def add_surface_to_geoh5(filename, surface, overwrite=True, groupname="Loop"):
@@ -32,6 +51,7 @@ def add_surface_to_geoh5(filename, surface, overwrite=True, groupname="Loop"):
32
51
 
33
52
  def add_points_to_geoh5(filename, point, overwrite=True, groupname="Loop"):
34
53
  with geoh5py.workspace.Workspace(filename) as workspace:
54
+
35
55
  group = workspace.get_entity(groupname)[0]
36
56
  if not group:
37
57
  group = geoh5py.groups.ContainerGroup.create(
@@ -45,14 +65,15 @@ def add_points_to_geoh5(filename, point, overwrite=True, groupname="Loop"):
45
65
  data = {}
46
66
  if point.properties is not None:
47
67
  for k, v in point.properties.items():
48
- data[k] = {'association': "VERTEX", "values": v}
68
+ data[k] = {'association': "VERTEX", "values": np.asarray(v)}
49
69
  if isinstance(point, VectorPoints):
50
- data['vx'] = {'association': "VERTEX", "values": point.vectors[:, 0]}
51
- data['vy'] = {'association': "VERTEX", "values": point.vectors[:, 1]}
52
- data['vz'] = {'association': "VERTEX", "values": point.vectors[:, 2]}
70
+ vectors = np.asarray(point.vectors)
71
+ data['vx'] = {'association': "VERTEX", "values": vectors[:, 0]}
72
+ data['vy'] = {'association': "VERTEX", "values": vectors[:, 1]}
73
+ data['vz'] = {'association': "VERTEX", "values": vectors[:, 2]}
53
74
 
54
75
  if isinstance(point, ValuePoints):
55
- data['val'] = {'association': "VERTEX", "values": point.values}
76
+ data['values'] = {'association': "VERTEX", "values": np.asarray(point.values)}
56
77
  point = geoh5py.objects.Points.create(
57
78
  workspace,
58
79
  name=point.name,
@@ -62,6 +83,78 @@ def add_points_to_geoh5(filename, point, overwrite=True, groupname="Loop"):
62
83
  point.add_data(data)
63
84
 
64
85
 
86
+ def overwrite_object(workspace, name, overwrite):
87
+ if name in workspace.list_entities_name.values():
88
+ existing_entity = workspace.get_entity(name)
89
+ existing_entity[0].allow_delete = True
90
+ if overwrite:
91
+ workspace.remove_entity(existing_entity[0])
92
+
93
+
94
+ def add_points_from_df(
95
+ filename,
96
+ df,
97
+ name='pointset',
98
+ overwrite=True,
99
+ columns=None,
100
+ groupname="Loop",
101
+ x_col='X',
102
+ y_col='Y',
103
+ z_col='Z',
104
+ ):
105
+ """
106
+ Add points to a geoh5 file from a pandas DataFrame. The DataFrame must have columns 'name', 'X', 'Y', 'Z' for the point locations.
107
+ Additional columns can be added as data associated with the points.
108
+ Parameters
109
+ ----------
110
+ filename: str
111
+ Path to the geoh5 file.
112
+ df: pandas.DataFrame
113
+ DataFrame containing point data. Must have columns 'name', 'X', 'Y', 'Z'. Additional columns will be added as data.
114
+ overwrite: bool, optional
115
+ Whether to overwrite existing points with the same name. Default is True.
116
+ columns: list of str, optional
117
+ List of columns in the DataFrame to add as data. If None, all columns except 'name', 'X', 'Y', 'Z' will be added. Default is None.
118
+
119
+ """
120
+ if columns is None:
121
+ columns = df.columns.tolist()
122
+ if x_col not in columns or y_col not in columns or z_col not in columns:
123
+ raise ValueError(
124
+ "DataFrame must contain 'name', 'X', 'Y', 'Z' columns. "
125
+ "Specify the column names using x_col, y_col, z_col parameters if they are different."
126
+ )
127
+ with geoh5py.workspace.Workspace(filename) as workspace:
128
+ if groupname:
129
+ group = workspace.get_entity(groupname)
130
+ group = group[0] if group else None
131
+ if not group:
132
+ group = geoh5py.groups.ContainerGroup.create(
133
+ workspace,
134
+ name=groupname,
135
+ allow_delete=True,
136
+ )
137
+
138
+ location = np.array(df[[x_col, y_col, z_col]].values) # shape (n,3)
139
+
140
+ overwrite_object(workspace, name, overwrite)
141
+
142
+ pts = geoh5py.objects.Points.create(
143
+ workspace,
144
+ name=name,
145
+ vertices=location,
146
+ parent=group,
147
+ )
148
+ data = {}
149
+ for col in columns:
150
+ if col in ['name', x_col, y_col, z_col]:
151
+ continue
152
+ data[col] = {"association": "VERTEX", "values": np.array(df[col]).flatten()}
153
+
154
+ if data:
155
+ pts.add_data(data)
156
+
157
+
65
158
  def add_structured_grid_to_geoh5(filename, structured_grid, overwrite=True, groupname="Loop"):
66
159
  with geoh5py.workspace.Workspace(filename) as workspace:
67
160
  group = workspace.get_entity(groupname)[0]
@@ -0,0 +1,269 @@
1
+ import re
2
+ from pathlib import Path
3
+
4
+ import numpy as np
5
+
6
+ from LoopStructural.utils import getLogger
7
+
8
+ logger = getLogger(__name__)
9
+
10
+
11
+ def _normalise_voxet_property(values, property_name, nsteps):
12
+ array = np.asarray(values)
13
+ expected_shape = tuple(int(step) for step in nsteps)
14
+ expected_size = int(np.prod(expected_shape))
15
+
16
+ if array.shape == expected_shape:
17
+ flat_values = array.reshape(-1, order="F")
18
+ else:
19
+ flat_values = np.squeeze(array)
20
+ if flat_values.shape == expected_shape:
21
+ flat_values = flat_values.reshape(-1, order="F")
22
+ elif flat_values.ndim == 1 and flat_values.size == expected_size:
23
+ flat_values = flat_values.copy()
24
+ else:
25
+ raise ValueError(
26
+ f"Property '{property_name}' must have shape {expected_shape} or size {expected_size}"
27
+ )
28
+
29
+ if np.issubdtype(flat_values.dtype, np.integer):
30
+ if (
31
+ flat_values.size == 0
32
+ or flat_values.min() >= np.iinfo(np.int8).min
33
+ and flat_values.max() <= np.iinfo(np.int8).max
34
+ ):
35
+ export_dtype = np.int8
36
+ storage_type = "Octet"
37
+ element_size = 1
38
+ else:
39
+ export_dtype = np.dtype(">i4")
40
+ storage_type = "Integer"
41
+ element_size = 4
42
+ no_data_value = None
43
+ elif np.issubdtype(flat_values.dtype, np.floating):
44
+ export_dtype = np.dtype(">f4")
45
+ storage_type = "Float"
46
+ element_size = 4
47
+ no_data_value = -999999.0
48
+ flat_values = np.nan_to_num(flat_values, nan=no_data_value)
49
+ else:
50
+ raise ValueError(f"Property '{property_name}' has unsupported dtype {flat_values.dtype}")
51
+
52
+ return {
53
+ "values": np.asarray(flat_values, dtype=export_dtype),
54
+ "storage_type": storage_type,
55
+ "element_size": element_size,
56
+ "no_data_value": no_data_value,
57
+ }
58
+
59
+
60
+ def _write_structured_grid_gocad(grid, file_name):
61
+ """Write a StructuredGrid to GOCAD VOXET format."""
62
+ vo_path = Path(file_name).with_suffix(".vo")
63
+ axis_n = np.asarray(grid.nsteps, dtype=int)
64
+ property_source = grid.properties
65
+ axis_min = np.min(grid.nodes, axis=0)
66
+ axis_max = np.max(grid.nodes, axis=0)
67
+
68
+ if property_source:
69
+ if grid.cell_properties:
70
+ logger.warning(
71
+ "StructuredGrid GOCAD export uses point properties; cell_properties were not exported"
72
+ )
73
+ elif grid.cell_properties:
74
+ axis_n = np.asarray(grid.nsteps, dtype=int) - 1
75
+ if np.any(axis_n <= 0):
76
+ raise ValueError("StructuredGrid cell_properties require at least two grid nodes per axis")
77
+ property_source = grid.cell_properties
78
+ axis_min = np.min(grid.cell_centres, axis=0)
79
+ axis_max = np.max(grid.cell_centres, axis=0)
80
+ else:
81
+ raise ValueError("StructuredGrid has no properties to export to GOCAD")
82
+
83
+ export_properties = []
84
+ for index, (property_name, values) in enumerate(property_source.items(), start=1):
85
+ export_info = _normalise_voxet_property(values, property_name, axis_n)
86
+ safe_name = re.sub(r"[^0-9A-Za-z_-]+", "_", property_name).strip("_") or f"property_{index}"
87
+ data_path = vo_path.with_name(f"{vo_path.stem}_{safe_name}@@")
88
+ export_properties.append(
89
+ {
90
+ "index": index,
91
+ "name": safe_name,
92
+ "data_path": data_path,
93
+ **export_info,
94
+ }
95
+ )
96
+
97
+ with open(vo_path, "w") as fp:
98
+ fp.write(
99
+ f"""GOCAD Voxet 1
100
+ HEADER {{
101
+ name: {grid.name}
102
+ }}
103
+ GOCAD_ORIGINAL_COORDINATE_SYSTEM
104
+ NAME Default
105
+ AXIS_NAME \"X\" \"Y\" \"Z\"
106
+ AXIS_UNIT \"m\" \"m\" \"m\"
107
+ ZPOSITIVE Elevation
108
+ END_ORIGINAL_COORDINATE_SYSTEM
109
+ AXIS_O 0.000000 0.000000 0.000000
110
+ AXIS_U 1.000000 0.000000 0.000000
111
+ AXIS_V 0.000000 1.000000 0.000000
112
+ AXIS_W 0.000000 0.000000 1.000000
113
+ AXIS_MIN {axis_min[0]} {axis_min[1]} {axis_min[2]}
114
+ AXIS_MAX {axis_max[0]} {axis_max[1]} {axis_max[2]}
115
+ AXIS_N {axis_n[0]} {axis_n[1]} {axis_n[2]}
116
+ AXIS_NAME \"X\" \"Y\" \"Z\"
117
+ AXIS_UNIT \"m\" \"m\" \"m\"
118
+ AXIS_TYPE even even even
119
+ """
120
+ )
121
+ for export_property in export_properties:
122
+ fp.write(
123
+ f"""PROPERTY {export_property['index']} {export_property['name']}
124
+ PROPERTY_CLASS {export_property['index']} {export_property['name']}
125
+ PROP_UNIT {export_property['index']} {export_property['name']}
126
+ PROPERTY_CLASS_HEADER {export_property['index']} {export_property['name']} {{
127
+ }}
128
+ PROPERTY_SUBCLASS {export_property['index']} QUANTITY {export_property['storage_type']}
129
+ """
130
+ )
131
+ if export_property["no_data_value"] is not None:
132
+ fp.write(
133
+ f"PROP_NO_DATA_VALUE {export_property['index']} {export_property['no_data_value']}\n"
134
+ )
135
+ fp.write(
136
+ f"""PROP_ETYPE {export_property['index']} IEEE
137
+ PROP_FORMAT {export_property['index']} RAW
138
+ PROP_ESIZE {export_property['index']} {export_property['element_size']}
139
+ PROP_OFFSET {export_property['index']} 0
140
+ PROP_FILE {export_property['index']} {export_property['data_path'].name}
141
+ """
142
+ )
143
+ fp.write("END\n")
144
+
145
+ for export_property in export_properties:
146
+ with open(export_property["data_path"], "wb") as fp:
147
+ export_property["values"].tofile(fp)
148
+
149
+ return True
150
+
151
+
152
+ def _write_feat_surfs_gocad(surf, file_name):
153
+ """
154
+ Writes out a GOCAD TSURF file for each surface in list
155
+
156
+ Parameters
157
+ ----------
158
+ surf_list: [ SimpleNamespace() ... ]
159
+ Details of the surfaces, as a list of SimpleNamespace() objects. Fields are:
160
+ verts: vertices, numpy ndarray with dtype = float64 & shape = (N,3)
161
+ faces: faces, numpy ndarray with dtype = int32 & shape = (M,3)
162
+ values: values, numpy ndarray with dtype = float32 & shape = (N,)
163
+ normals: normals, numpy ndarray with dtype = float32 & shape = (N,3)
164
+ name: name of feature e.g. fault or supergroup, string
165
+
166
+ file_name: string
167
+ Desired filename
168
+
169
+ Returns
170
+ -------
171
+ True if successful
172
+
173
+ """
174
+ properties_header = None
175
+ if surf.properties:
176
+
177
+ properties_header = f"""PROPERTIES {' '.join(list(surf.properties.keys()))}
178
+ NO_DATA_VALUES -99999
179
+ PROPERTY_CLASSES {' '.join(list(surf.properties.keys()))}
180
+ """
181
+
182
+ file_name = Path(file_name).with_suffix(".ts")
183
+ with open(f"{file_name}", "w") as fd:
184
+ fd.write(
185
+ f"""GOCAD TSurf 1
186
+ HEADER {{
187
+ *solid*color: #ffa500
188
+ ivolmap: false
189
+ imap: false
190
+ name: {surf.name}
191
+ }}
192
+ GOCAD_ORIGINAL_COORDINATE_SYSTEM
193
+ NAME Default
194
+ PROJECTION Unknown
195
+ DATUM Unknown
196
+ AXIS_NAME X Y Z
197
+ AXIS_UNIT m m m
198
+ ZPOSITIVE Elevation
199
+ END_ORIGINAL_COORDINATE_SYSTEM
200
+ GEOLOGICAL_FEATURE {surf.name}
201
+ GEOLOGICAL_TYPE fault
202
+ {properties_header if properties_header else ""}
203
+ PROPERTY_CLASS_HEADER X {{
204
+ kind: X
205
+ unit: m
206
+ }}
207
+ PROPERTY_CLASS_HEADER Y {{
208
+ kind: Y
209
+ unit: m
210
+ }}
211
+ PROPERTY_CLASS_HEADER Z {{
212
+ kind: Z
213
+ unit: m
214
+ is_z: on
215
+ }}
216
+ PROPERTY_CLASS_HEADER vector3d {{
217
+ kind: Length
218
+ unit: m
219
+ }}
220
+ TFACE
221
+ """
222
+ )
223
+ v_idx = 1
224
+ v_map = {}
225
+ for idx, vert in enumerate(surf.vertices):
226
+ if not np.isnan(vert[0]) and not np.isnan(vert[1]) and not np.isnan(vert[2]):
227
+ fd.write(f"VRTX {v_idx:} {vert[0]} {vert[1]} {vert[2]}")
228
+ if surf.properties:
229
+ fd.writelines(f" {value[idx]}" for value in surf.properties.values())
230
+ fd.write("\n")
231
+ v_map[idx] = v_idx
232
+ v_idx += 1
233
+ for face in surf.triangles:
234
+ if face[0] in v_map and face[1] in v_map and face[2] in v_map:
235
+ fd.write(f"TRGL {v_map[face[0]]} {v_map[face[1]]} {v_map[face[2]]} \n")
236
+ fd.write("END\n")
237
+ return True
238
+
239
+
240
+ # def _write_pointset(points, file_name):
241
+ # """
242
+ # Write out a GOCAD VS file for a pointset
243
+
244
+ # Parameters
245
+ # ----------
246
+ # points: SimpleNamespace()
247
+ # Details of the points, as a SimpleNamespace() object. Fields are:
248
+ # locations: locations, numpy ndarray with dtype = float64 & shape = (N,3)
249
+ # vectors: vectors, numpy ndarray with dtype = float64 & shape = (N,3)
250
+ # name: name of feature e.g. fault or supergroup, string
251
+
252
+ # file_name: string
253
+ # Desired filename
254
+
255
+ # Returns
256
+ # -------
257
+ # True if successful
258
+
259
+ # """
260
+ # file_name = Path(file_name).with_suffix(".vs")
261
+ # with open(f"{file_name}", "w") as fd:
262
+ # fd.write(
263
+ # f"""GOCAD VSet 1
264
+ # HEADER {{
265
+ # name: {points.name}
266
+ # }}
267
+ # GOCAD_ORIGINAL_COORDINATE_SYSTEM
268
+ # NAME Default
269
+ # PROJECTION Unknown