LoopStructural 1.6.16__tar.gz → 1.6.18__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 (148) hide show
  1. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/__init__.py +1 -0
  2. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_geological_interpolator.py +1 -2
  3. loopstructural-1.6.18/LoopStructural/modelling/core/fault_topology.py +234 -0
  4. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/core/geological_model.py +43 -34
  5. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/core/stratigraphic_column.py +129 -25
  6. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/_svariogram.py +3 -0
  7. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/__init__.py +1 -0
  8. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/_surface.py +6 -1
  9. loopstructural-1.6.18/LoopStructural/utils/observer.py +150 -0
  10. loopstructural-1.6.18/LoopStructural/version.py +1 -0
  11. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural.egg-info/PKG-INFO +1 -1
  12. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural.egg-info/SOURCES.txt +2 -1
  13. {loopstructural-1.6.16 → loopstructural-1.6.18}/PKG-INFO +1 -1
  14. {loopstructural-1.6.16 → loopstructural-1.6.18}/setup.cfg +0 -2
  15. loopstructural-1.6.16/LoopStructural/version.py +0 -1
  16. {loopstructural-1.6.16 → loopstructural-1.6.18}/LICENSE +0 -0
  17. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/__init__.py +0 -0
  18. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/_base.py +0 -0
  19. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/_example_models.py +0 -0
  20. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/claudius.csv +0 -0
  21. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
  22. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/duplex.csv +0 -0
  23. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/duplexbb.txt +0 -0
  24. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
  25. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  26. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
  27. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  28. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  29. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
  30. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
  31. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
  32. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
  33. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
  34. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
  35. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
  36. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
  37. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
  38. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/intrusion.csv +0 -0
  39. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
  40. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
  41. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/onefolddata.csv +0 -0
  42. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
  43. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
  44. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
  45. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datatypes/__init__.py +0 -0
  46. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datatypes/_bounding_box.py +0 -0
  47. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datatypes/_point.py +0 -0
  48. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datatypes/_structured_grid.py +0 -0
  49. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/datatypes/_surface.py +0 -0
  50. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/export/exporters.py +0 -0
  51. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/export/file_formats.py +0 -0
  52. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/export/geoh5.py +0 -0
  53. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/export/gocad.py +0 -0
  54. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/export/omf_wrapper.py +0 -0
  55. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/__init__.py +0 -0
  56. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_api.py +0 -0
  57. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_builders.py +0 -0
  58. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_constant_norm.py +0 -0
  59. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_cython/__init__.py +0 -0
  60. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_discrete_fold_interpolator.py +0 -0
  61. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_discrete_interpolator.py +0 -0
  62. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_finite_difference_interpolator.py +0 -0
  63. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_interpolator_builder.py +0 -0
  64. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_interpolator_factory.py +0 -0
  65. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_interpolatortype.py +0 -0
  66. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_operator.py +0 -0
  67. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_p1interpolator.py +0 -0
  68. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_p2interpolator.py +0 -0
  69. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/_surfe_wrapper.py +0 -0
  70. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_2d_base_unstructured.py +0 -0
  71. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +0 -0
  72. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -0
  73. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_2d_structured_grid.py +0 -0
  74. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  75. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_3d_base_structured.py +0 -0
  76. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -0
  77. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_3d_structured_grid.py +0 -0
  78. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_3d_structured_tetra.py +0 -0
  79. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +0 -0
  80. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/__init__.py +0 -0
  81. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_aabb.py +0 -0
  82. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_base_support.py +0 -0
  83. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_face_table.py +0 -0
  84. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/interpolators/supports/_support_factory.py +0 -0
  85. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/__init__.py +0 -0
  86. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/core/__init__.py +0 -0
  87. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/__init__.py +0 -0
  88. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_analytical_feature.py +0 -0
  89. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_base_geological_feature.py +0 -0
  90. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_cross_product_geological_feature.py +0 -0
  91. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_geological_feature.py +0 -0
  92. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_lambda_geological_feature.py +0 -0
  93. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_projected_vector_feature.py +0 -0
  94. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_region.py +0 -0
  95. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_structural_frame.py +0 -0
  96. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/_unconformity_feature.py +0 -0
  97. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/builders/__init__.py +0 -0
  98. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/builders/_base_builder.py +0 -0
  99. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/builders/_fault_builder.py +0 -0
  100. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +0 -0
  101. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +0 -0
  102. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +0 -0
  103. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fault/__init__.py +0 -0
  104. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fault/_fault_function.py +0 -0
  105. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fault/_fault_function_feature.py +0 -0
  106. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fault/_fault_segment.py +0 -0
  107. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/__init__.py +0 -0
  108. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/_fold.py +0 -0
  109. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -0
  110. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/_foldframe.py +0 -0
  111. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/fold_function/__init__.py +0 -0
  112. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +0 -0
  113. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +0 -0
  114. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +0 -0
  115. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +0 -0
  116. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/input/__init__.py +0 -0
  117. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/input/fault_network.py +0 -0
  118. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/input/map2loop_processor.py +0 -0
  119. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/input/process_data.py +0 -0
  120. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/input/project_file.py +0 -0
  121. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/__init__.py +0 -0
  122. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +0 -0
  123. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +0 -0
  124. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/intrusion_builder.py +0 -0
  125. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/intrusion_feature.py +0 -0
  126. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +0 -0
  127. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +0 -0
  128. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/_transformation.py +0 -0
  129. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/colours.py +0 -0
  130. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/config.py +0 -0
  131. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/dtm_creator.py +0 -0
  132. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/exceptions.py +0 -0
  133. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/features.py +0 -0
  134. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/helper.py +0 -0
  135. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/json_encoder.py +0 -0
  136. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/linalg.py +0 -0
  137. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/logging.py +0 -0
  138. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/maths.py +0 -0
  139. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/regions.py +0 -0
  140. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/typing.py +0 -0
  141. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/utils/utils.py +0 -0
  142. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural/visualisation/__init__.py +0 -0
  143. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural.egg-info/dependency_links.txt +0 -0
  144. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural.egg-info/requires.txt +0 -0
  145. {loopstructural-1.6.16 → loopstructural-1.6.18}/LoopStructural.egg-info/top_level.txt +0 -0
  146. {loopstructural-1.6.16 → loopstructural-1.6.18}/README.md +0 -0
  147. {loopstructural-1.6.16 → loopstructural-1.6.18}/pyproject.toml +0 -0
  148. {loopstructural-1.6.16 → loopstructural-1.6.18}/setup.py +0 -0
@@ -20,6 +20,7 @@ ch.setLevel(logging.WARNING)
20
20
  loggers = {}
21
21
  from .modelling.core.geological_model import GeologicalModel
22
22
  from .modelling.core.stratigraphic_column import StratigraphicColumn
23
+ from .modelling.core.fault_topology import FaultTopology
23
24
  from .interpolators._api import LoopInterpolator
24
25
  from .interpolators import InterpolatorBuilder
25
26
  from .datatypes import BoundingBox
@@ -166,8 +166,7 @@ class GeologicalInterpolator(metaclass=ABCMeta):
166
166
  """
167
167
  if points.shape[1] == self.dimensions * 2:
168
168
  points = np.hstack([points, np.ones((points.shape[0], 1))])
169
- logger.warning(f"No weight provided for normal constraints, all weights are set to 1")
170
- raise Warning
169
+ logger.info("No weight provided for normal constraints, all weights are set to 1")
171
170
  if points.shape[1] < self.dimensions * 2 + 1:
172
171
  raise ValueError("Normal constraints must at least have X,Y,Z,nx,ny,nz")
173
172
  self.n_n = points.shape[0]
@@ -0,0 +1,234 @@
1
+ from ..features.fault import FaultSegment
2
+ from ...utils import Observable
3
+ from .stratigraphic_column import StratigraphicColumn
4
+ import enum
5
+ import numpy as np
6
+ class FaultRelationshipType(enum.Enum):
7
+ ABUTTING = "abutting"
8
+ FAULTED = "faulted"
9
+ NONE = "none"
10
+
11
+ class FaultTopology(Observable['FaultTopology']):
12
+ """A graph representation of the relationships between faults and the
13
+ relationship with stratigraphic units.
14
+ """
15
+ def __init__(self, stratigraphic_column: 'StratigraphicColumn'):
16
+ super().__init__()
17
+ self.faults = []
18
+ self.stratigraphic_column = stratigraphic_column
19
+ self.adjacency = {}
20
+ self.stratigraphy_fault_relationships = {}
21
+ def add_fault(self, fault: FaultSegment):
22
+ """
23
+ Adds a fault to the fault topology.
24
+ """
25
+ if not isinstance(fault, str):
26
+ raise TypeError("Expected a fault name.")
27
+
28
+ self.faults.append(fault)
29
+ self.notify('fault_added', fault=fault)
30
+
31
+ def remove_fault(self, fault: str):
32
+ """
33
+ Removes a fault from the fault topology.
34
+ """
35
+ if fault not in self.faults:
36
+ raise ValueError(f"Fault {fault} not found in the topology.")
37
+
38
+ self.faults.remove(fault)
39
+ # Remove any relationships involving this fault
40
+ self.adjacency = {k: v for k, v in self.adjacency.items() if fault not in k}
41
+ self.stratigraphy_fault_relationships = {
42
+ k: v for k, v in self.stratigraphy_fault_relationships.items() if k[1] != fault
43
+ }
44
+ self.notify('fault_removed', fault=fault)
45
+
46
+ def add_abutting_relationship(self, fault_name: str, abutting_fault: str):
47
+ """
48
+ Adds an abutting relationship between two faults.
49
+ """
50
+ if fault_name not in self.faults or abutting_fault not in self.faults:
51
+ raise ValueError("Both faults must be part of the fault topology.")
52
+
53
+ if fault_name not in self.adjacency:
54
+ self.adjacency[fault_name] = []
55
+
56
+ self.adjacency[(fault_name, abutting_fault)] = FaultRelationshipType.ABUTTING
57
+ self.notify('abutting_relationship_added', {'fault': fault_name, 'abutting_fault': abutting_fault})
58
+ def add_stratigraphy_fault_relationship(self, unit_name:str, fault_name: str):
59
+ """
60
+ Adds a relationship between a stratigraphic unit and a fault.
61
+ """
62
+ if fault_name not in self.faults:
63
+ raise ValueError("Fault must be part of the fault topology.")
64
+
65
+ if unit_name is None:
66
+ raise ValueError(f"No stratigraphic group found for unit name: {unit_name}")
67
+ self.stratigraphy_fault_relationships[(unit_name,fault_name)] = True
68
+
69
+ self.notify('stratigraphy_fault_relationship_added', {'unit': unit_name, 'fault': fault_name})
70
+ def add_faulted_relationship(self, fault_name: str, faulted_fault_name: str):
71
+ """
72
+ Adds a faulted relationship between two faults.
73
+ """
74
+ if fault_name not in self.faults or faulted_fault_name not in self.faults:
75
+ raise ValueError("Both faults must be part of the fault topology.")
76
+
77
+ if fault_name not in self.adjacency:
78
+ self.adjacency[fault_name] = []
79
+
80
+ self.adjacency[(fault_name, faulted_fault_name)] = FaultRelationshipType.FAULTED
81
+ self.notify('faulted_relationship_added', {'fault': fault_name, 'faulted_fault': faulted_fault_name})
82
+ def remove_fault_relationship(self, fault_name: str, related_fault_name: str):
83
+ """
84
+ Removes a relationship between two faults.
85
+ """
86
+ if (fault_name, related_fault_name) in self.adjacency:
87
+ del self.adjacency[(fault_name, related_fault_name)]
88
+ elif (related_fault_name, fault_name) in self.adjacency:
89
+ del self.adjacency[(related_fault_name, fault_name)]
90
+ else:
91
+ raise ValueError(f"No relationship found between {fault_name} and {related_fault_name}.")
92
+ self.notify('fault_relationship_removed', {'fault': fault_name, 'related_fault': related_fault_name})
93
+ def update_fault_relationship(self, fault_name: str, related_fault_name: str, new_relationship_type: FaultRelationshipType):
94
+ if new_relationship_type == FaultRelationshipType.NONE:
95
+ self.adjacency.pop((fault_name, related_fault_name), None)
96
+ else:
97
+ self.adjacency[(fault_name, related_fault_name)] = new_relationship_type
98
+ self.notify('fault_relationship_updated', {'fault': fault_name, 'related_fault': related_fault_name, 'new_relationship_type': new_relationship_type})
99
+ def change_relationship_type(self, fault_name: str, related_fault_name: str, new_relationship_type: FaultRelationshipType):
100
+ """
101
+ Changes the relationship type between two faults.
102
+ """
103
+ if (fault_name, related_fault_name) in self.adjacency:
104
+ self.adjacency[(fault_name, related_fault_name)] = new_relationship_type
105
+
106
+ else:
107
+ raise ValueError(f"No relationship found between {fault_name} and {related_fault_name}.")
108
+ self.notify('relationship_type_changed', {'fault': fault_name, 'related_fault': related_fault_name, 'new_relationship_type': new_relationship_type})
109
+ def get_fault_relationships(self, fault_name: str):
110
+ """
111
+ Returns a list of relationships for a given fault.
112
+ """
113
+ relationships = []
114
+ for (f1, f2), relationship_type in self.adjacency.items():
115
+ if f1 == fault_name or f2 == fault_name:
116
+ relationships.append((f1, f2, relationship_type))
117
+ return relationships
118
+ def get_fault_relationship(self, fault_name: str, related_fault_name: str):
119
+ """
120
+ Returns the relationship type between two faults.
121
+ """
122
+ return self.adjacency.get((fault_name, related_fault_name), FaultRelationshipType.NONE)
123
+ def get_faults(self):
124
+ """
125
+ Returns a list of all faults in the topology.
126
+ """
127
+ return self.faults
128
+
129
+ def get_stratigraphy_fault_relationships(self):
130
+ """
131
+ Returns a dictionary of stratigraphic unit to fault relationships.
132
+ """
133
+ return self.stratigraphy_fault_relationships
134
+ def get_fault_stratigraphic_unit_relationships(self):
135
+ units_group_pairs = self.stratigraphic_column.get_group_unit_pairs()
136
+ matrix = np.zeros((len(self.faults), len(units_group_pairs)), dtype=int)
137
+ for i, fault in enumerate(self.faults):
138
+ for j, (unit_name, _group) in enumerate(units_group_pairs):
139
+ if (unit_name, fault) in self.stratigraphy_fault_relationships:
140
+ matrix[i, j] = 1
141
+
142
+ return matrix
143
+ def get_fault_stratigraphic_relationship(self, unit_name: str, fault:str) -> bool:
144
+ """
145
+ Returns a dictionary of fault to stratigraphic unit relationships.
146
+ """
147
+ if unit_name is None:
148
+ raise ValueError(f"No stratigraphic group found for unit name: {unit_name}")
149
+ if (unit_name, fault) not in self.stratigraphy_fault_relationships:
150
+ return False
151
+ return self.stratigraphy_fault_relationships[(unit_name, fault)]
152
+
153
+ def update_fault_stratigraphy_relationship(self, unit_name: str, fault_name: str, flag: bool = True):
154
+ """
155
+ Updates the relationship between a stratigraphic unit and a fault.
156
+ """
157
+ if not flag:
158
+ if (unit_name, fault_name) in self.stratigraphy_fault_relationships:
159
+ del self.stratigraphy_fault_relationships[(unit_name, fault_name)]
160
+ else:
161
+ self.stratigraphy_fault_relationships[(unit_name, fault_name)] = flag
162
+
163
+ self.notify('stratigraphy_fault_relationship_updated', {'unit': unit_name, 'fault': fault_name})
164
+
165
+ def remove_fault_stratigraphy_relationship(self, unit_name: str, fault_name: str):
166
+ """
167
+ Removes a relationship between a stratigraphic unit and a fault.
168
+ """
169
+ if (unit_name, fault_name) not in self.stratigraphy_fault_relationships:
170
+ raise ValueError(f"No relationship found between unit {unit_name} and fault {fault_name}.")
171
+ else:
172
+ self.stratigraphy_fault_relationships.pop((unit_name, fault_name), None)
173
+
174
+ self.notify('stratigraphy_fault_relationship_removed', {'unit': unit_name, 'fault': fault_name})
175
+ def get_matrix(self):
176
+ """
177
+ Returns a matrix representation of the fault relationships.
178
+ """
179
+ matrix = np.zeros((len(self.faults), len(self.faults)), dtype=int)
180
+ for (fault_name, related_fault_name), relationship_type in self.adjacency.items():
181
+ fault_index = self.faults.index(next(f for f in self.faults if f == fault_name))
182
+ related_fault_index = self.faults.index(next(f for f in self.faults if f == related_fault_name))
183
+ if relationship_type == FaultRelationshipType.ABUTTING:
184
+ matrix[fault_index, related_fault_index] = 1
185
+ elif relationship_type == FaultRelationshipType.FAULTED:
186
+ matrix[fault_index, related_fault_index] = 2
187
+ return matrix
188
+
189
+ def to_dict(self):
190
+ """
191
+ Returns a dictionary representation of the fault topology.
192
+ """
193
+ return {
194
+ "faults": self.faults,
195
+ "adjacency": self.adjacency,
196
+ "stratigraphy_fault_relationships": self.stratigraphy_fault_relationships,
197
+ }
198
+
199
+ def update_from_dict(self, data):
200
+ """
201
+ Updates the fault topology from a dictionary representation.
202
+ """
203
+ with self.freeze_notifications():
204
+ self.faults.extend(data.get("faults", []))
205
+ adjacency = data.get("adjacency", {})
206
+ stratigraphy_fault_relationships = data.get("stratigraphy_fault_relationships", {})
207
+ for (fault,abutting_fault) in adjacency.values():
208
+ if fault not in self.faults:
209
+ self.add_fault(fault)
210
+ if abutting_fault not in self.faults:
211
+ self.add_fault(abutting_fault)
212
+ self.add_abutting_relationship(fault, abutting_fault)
213
+ for unit_name, fault_names in stratigraphy_fault_relationships.items():
214
+ for fault_name in fault_names:
215
+ if fault_name not in self.faults:
216
+ self.add_fault(fault_name)
217
+ self.add_stratigraphy_fault_relationship(unit_name, fault_name)
218
+
219
+ @classmethod
220
+ def from_dict(cls, data):
221
+ """
222
+ Creates a FaultTopology instance from a dictionary representation.
223
+ """
224
+ from .stratigraphic_column import StratigraphicColumn
225
+ stratigraphic_column = data.get("stratigraphic_column",None)
226
+ if not isinstance(stratigraphic_column, StratigraphicColumn):
227
+ if isinstance(stratigraphic_column, dict):
228
+ stratigraphic_column = StratigraphicColumn.from_dict(stratigraphic_column)
229
+ elif not isinstance(stratigraphic_column, StratigraphicColumn):
230
+ raise TypeError("Expected 'stratigraphic_column' to be a StratigraphicColumn instance or dict.")
231
+
232
+ topology = cls(stratigraphic_column)
233
+ topology.update_from_dict(data)
234
+ return topology
@@ -6,7 +6,7 @@ from ...utils import getLogger
6
6
 
7
7
  import numpy as np
8
8
  import pandas as pd
9
- from typing import List, Optional
9
+ from typing import List, Optional, Union, Dict
10
10
  import pathlib
11
11
  from ...modelling.features.fault import FaultSegment
12
12
 
@@ -123,8 +123,7 @@ class GeologicalModel:
123
123
  self.feature_name_index = {}
124
124
  self._data = pd.DataFrame() # None
125
125
 
126
- self.stratigraphic_column = StratigraphicColumn()
127
-
126
+ self._stratigraphic_column = StratigraphicColumn()
128
127
 
129
128
  self.tol = 1e-10 * np.max(self.bounding_box.maximum - self.bounding_box.origin)
130
129
  self._dtm = None
@@ -187,7 +186,6 @@ class GeologicalModel:
187
186
  ].astype(float)
188
187
  return data
189
188
 
190
-
191
189
  if "type" in data:
192
190
  logger.warning("'type' is deprecated replace with 'feature_name' \n")
193
191
  data.rename(columns={"type": "feature_name"}, inplace=True)
@@ -409,7 +407,6 @@ class GeologicalModel:
409
407
  """
410
408
  return [f.name for f in self.faults]
411
409
 
412
-
413
410
  def to_file(self, file):
414
411
  """Save a model to a pickle file requires dill
415
412
 
@@ -506,10 +503,34 @@ class GeologicalModel:
506
503
  self._data = data.copy()
507
504
  # self._data[['X','Y','Z']] = self.bounding_box.project(self._data[['X','Y','Z']].to_numpy())
508
505
 
509
-
510
506
  def set_model_data(self, data):
511
507
  logger.warning("deprecated method. Model data can now be set using the data attribute")
512
508
  self.data = data.copy()
509
+ @property
510
+ def stratigraphic_column(self):
511
+ """Get the stratigraphic column of the model
512
+
513
+ Returns
514
+ -------
515
+ StratigraphicColumn
516
+ the stratigraphic column of the model
517
+ """
518
+ return self._stratigraphic_column
519
+ @stratigraphic_column.setter
520
+ def stratigraphic_column(self, stratigraphic_column: Union[StratigraphicColumn,Dict]):
521
+ """Set the stratigraphic column of the model
522
+
523
+ Parameters
524
+ ----------
525
+ stratigraphic_column : StratigraphicColumn
526
+ the stratigraphic column to set
527
+ """
528
+ if isinstance(stratigraphic_column, dict):
529
+ self.set_stratigraphic_column(stratigraphic_column)
530
+ return
531
+ elif not isinstance(stratigraphic_column, StratigraphicColumn):
532
+ raise ValueError("stratigraphic_column must be a StratigraphicColumn object")
533
+ self._stratigraphic_column = stratigraphic_column
513
534
 
514
535
  def set_stratigraphic_column(self, stratigraphic_column, cmap="tab20"):
515
536
  """
@@ -539,14 +560,14 @@ class GeologicalModel:
539
560
  DeprecationWarning(
540
561
  "set_stratigraphic_column is deprecated, use model.stratigraphic_column.add_units instead"
541
562
  )
542
- for g in stratigraphic_column.keys():
563
+ for i, g in enumerate(stratigraphic_column.keys()):
543
564
  for u in stratigraphic_column[g].keys():
544
565
  thickness = 0
545
566
  if "min" in stratigraphic_column[g][u] and "max" in stratigraphic_column[g][u]:
546
567
  min_val = stratigraphic_column[g][u]["min"]
547
568
  max_val = stratigraphic_column[g][u].get("max", None)
548
569
  thickness = max_val - min_val if max_val is not None else None
549
- logger.warning(
570
+ logger.info(
550
571
  f"""
551
572
  model.stratigraphic_column.add_unit({u},
552
573
  colour={stratigraphic_column[g][u].get("colour", None)},
@@ -557,9 +578,11 @@ class GeologicalModel:
557
578
  colour=stratigraphic_column[g][u].get("colour", None),
558
579
  thickness=thickness,
559
580
  )
581
+
560
582
  self.stratigraphic_column.add_unconformity(
561
583
  name=''.join([g, 'unconformity']),
562
584
  )
585
+ self.stratigraphic_column.group_mapping[f'Group_{i+1}'] = g
563
586
 
564
587
  def create_and_add_foliation(
565
588
  self,
@@ -1400,7 +1423,6 @@ class GeologicalModel:
1400
1423
 
1401
1424
  return self.bounding_box.reproject(points, inplace=inplace)
1402
1425
 
1403
-
1404
1426
  # TODO move scale to bounding box/transformer
1405
1427
  def scale(self, points: np.ndarray, *, inplace: bool = False) -> np.ndarray:
1406
1428
  """Take points in UTM coordinates and reproject
@@ -1419,7 +1441,6 @@ class GeologicalModel:
1419
1441
  """
1420
1442
  return self.bounding_box.project(np.array(points).astype(float), inplace=inplace)
1421
1443
 
1422
-
1423
1444
  def regular_grid(self, *, nsteps=None, shuffle=True, rescale=False, order="C"):
1424
1445
  """
1425
1446
  Return a regular grid within the model bounding box
@@ -1494,22 +1515,18 @@ class GeologicalModel:
1494
1515
  if self.stratigraphic_column is None:
1495
1516
  logger.warning("No stratigraphic column defined")
1496
1517
  return strat_id
1497
- for group in reversed(self.stratigraphic_column.keys()):
1498
- if group == "faults":
1499
- continue
1500
- feature_id = self.feature_name_index.get(group, -1)
1518
+
1519
+ s_id = 0
1520
+ for g in reversed(self.stratigraphic_column.get_groups()):
1521
+ feature_id = self.feature_name_index.get(g.name, -1)
1501
1522
  if feature_id >= 0:
1502
- feature = self.features[feature_id]
1503
- vals = feature.evaluate_value(xyz)
1504
- for series in self.stratigraphic_column[group].values():
1505
- strat_id[
1506
- np.logical_and(
1507
- vals < series.get("max", feature.max()),
1508
- vals > series.get("min", feature.min()),
1509
- )
1510
- ] = series["id"]
1523
+ vals = self.features[feature_id].evaluate_value(xyz)
1524
+ for u in g.units:
1525
+ strat_id[np.logical_and(vals < u.max(), vals > u.min())] = s_id
1526
+ s_id += 1
1511
1527
  if feature_id == -1:
1512
- logger.error(f"Model does not contain {group}")
1528
+ logger.error(f"Model does not contain {g.name}")
1529
+
1513
1530
  return strat_id
1514
1531
 
1515
1532
  def evaluate_model_gradient(self, points: np.ndarray, *, scale: bool = True) -> np.ndarray:
@@ -1710,16 +1727,8 @@ class GeologicalModel:
1710
1727
  ids : list
1711
1728
  list of unique stratigraphic ids, featurename, unit name and min and max scalar values
1712
1729
  """
1713
- ids = []
1714
- if self.stratigraphic_column is None:
1715
- logger.warning('No stratigraphic column defined')
1716
- return ids
1717
- for group in self.stratigraphic_column.keys():
1718
- if group == "faults":
1719
- continue
1720
- for name, series in self.stratigraphic_column[group].items():
1721
- ids.append([series["id"], group, name, series['min'], series['max']])
1722
- return ids
1730
+ return self.stratigraphic_column.get_stratigraphic_ids()
1731
+
1723
1732
 
1724
1733
  def get_fault_surfaces(self, faults: List[str] = []):
1725
1734
  surfaces = []