LoopStructural 1.6.26__tar.gz → 1.6.27__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 (150) hide show
  1. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_discrete_interpolator.py +36 -39
  2. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_3d_base_structured.py +24 -12
  3. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_lambda_geological_feature.py +19 -2
  4. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fault/_fault_segment.py +23 -15
  5. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/regions.py +11 -12
  6. loopstructural-1.6.27/LoopStructural/version.py +1 -0
  7. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural.egg-info/PKG-INFO +1 -1
  8. {loopstructural-1.6.26 → loopstructural-1.6.27}/PKG-INFO +1 -1
  9. loopstructural-1.6.26/LoopStructural/version.py +0 -1
  10. {loopstructural-1.6.26 → loopstructural-1.6.27}/LICENSE +0 -0
  11. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/__init__.py +0 -0
  12. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/__init__.py +0 -0
  13. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/_base.py +0 -0
  14. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/_example_models.py +0 -0
  15. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/claudius.csv +0 -0
  16. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
  17. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/duplex.csv +0 -0
  18. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/duplexbb.txt +0 -0
  19. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
  20. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  21. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
  22. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  23. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  24. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
  25. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
  26. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
  27. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
  28. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
  29. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
  30. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
  31. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
  32. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
  33. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/intrusion.csv +0 -0
  34. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
  35. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
  36. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/onefolddata.csv +0 -0
  37. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
  38. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
  39. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
  40. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datatypes/__init__.py +0 -0
  41. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datatypes/_bounding_box.py +0 -0
  42. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datatypes/_point.py +0 -0
  43. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datatypes/_structured_grid.py +0 -0
  44. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/datatypes/_surface.py +0 -0
  45. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/export/exporters.py +0 -0
  46. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/export/file_formats.py +0 -0
  47. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/export/geoh5.py +0 -0
  48. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/export/gocad.py +0 -0
  49. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/export/omf_wrapper.py +0 -0
  50. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/__init__.py +0 -0
  51. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_api.py +0 -0
  52. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_builders.py +0 -0
  53. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_constant_norm.py +0 -0
  54. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_cython/__init__.py +0 -0
  55. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_discrete_fold_interpolator.py +0 -0
  56. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_finite_difference_interpolator.py +0 -0
  57. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_geological_interpolator.py +0 -0
  58. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_interpolator_builder.py +0 -0
  59. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_interpolator_factory.py +0 -0
  60. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_interpolatortype.py +0 -0
  61. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_operator.py +0 -0
  62. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_p1interpolator.py +0 -0
  63. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_p2interpolator.py +0 -0
  64. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/_surfe_wrapper.py +0 -0
  65. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_2d_base_unstructured.py +0 -0
  66. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +0 -0
  67. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -0
  68. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_2d_structured_grid.py +0 -0
  69. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  70. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -0
  71. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_3d_structured_grid.py +0 -0
  72. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_3d_structured_tetra.py +0 -0
  73. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +0 -0
  74. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/__init__.py +0 -0
  75. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_aabb.py +0 -0
  76. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_base_support.py +0 -0
  77. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_face_table.py +0 -0
  78. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/interpolators/supports/_support_factory.py +0 -0
  79. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/__init__.py +0 -0
  80. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/core/__init__.py +0 -0
  81. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/core/fault_topology.py +0 -0
  82. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/core/geological_model.py +0 -0
  83. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/core/stratigraphic_column.py +0 -0
  84. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/__init__.py +0 -0
  85. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_analytical_feature.py +0 -0
  86. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_base_geological_feature.py +0 -0
  87. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_cross_product_geological_feature.py +0 -0
  88. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_feature_converters.py +0 -0
  89. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_geological_feature.py +0 -0
  90. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_projected_vector_feature.py +0 -0
  91. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_region.py +0 -0
  92. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_structural_frame.py +0 -0
  93. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/_unconformity_feature.py +0 -0
  94. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/__init__.py +0 -0
  95. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/_analytical_fold_builder.py +0 -0
  96. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/_base_builder.py +0 -0
  97. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/_fault_builder.py +0 -0
  98. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +0 -0
  99. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +0 -0
  100. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +0 -0
  101. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fault/__init__.py +0 -0
  102. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fault/_fault_function.py +0 -0
  103. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fault/_fault_function_feature.py +0 -0
  104. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/__init__.py +0 -0
  105. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/_fold.py +0 -0
  106. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -0
  107. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/_foldframe.py +0 -0
  108. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/_svariogram.py +0 -0
  109. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/fold_function/__init__.py +0 -0
  110. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +0 -0
  111. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +0 -0
  112. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +0 -0
  113. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +0 -0
  114. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/input/__init__.py +0 -0
  115. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/input/fault_network.py +0 -0
  116. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/input/map2loop_processor.py +0 -0
  117. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/input/process_data.py +0 -0
  118. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/input/project_file.py +0 -0
  119. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/__init__.py +0 -0
  120. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +0 -0
  121. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +0 -0
  122. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/intrusion_builder.py +0 -0
  123. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/intrusion_feature.py +0 -0
  124. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +0 -0
  125. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +0 -0
  126. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/__init__.py +0 -0
  127. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/_surface.py +0 -0
  128. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/_transformation.py +0 -0
  129. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/colours.py +0 -0
  130. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/config.py +0 -0
  131. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/dtm_creator.py +0 -0
  132. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/exceptions.py +0 -0
  133. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/features.py +0 -0
  134. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/helper.py +0 -0
  135. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/json_encoder.py +0 -0
  136. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/linalg.py +0 -0
  137. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/logging.py +0 -0
  138. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/maths.py +0 -0
  139. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/observer.py +0 -0
  140. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/typing.py +0 -0
  141. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/utils/utils.py +0 -0
  142. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural/visualisation/__init__.py +0 -0
  143. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural.egg-info/SOURCES.txt +0 -0
  144. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural.egg-info/dependency_links.txt +0 -0
  145. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural.egg-info/requires.txt +0 -0
  146. {loopstructural-1.6.26 → loopstructural-1.6.27}/LoopStructural.egg-info/top_level.txt +0 -0
  147. {loopstructural-1.6.26 → loopstructural-1.6.27}/README.md +0 -0
  148. {loopstructural-1.6.26 → loopstructural-1.6.27}/pyproject.toml +0 -0
  149. {loopstructural-1.6.26 → loopstructural-1.6.27}/setup.cfg +0 -0
  150. {loopstructural-1.6.26 → loopstructural-1.6.27}/setup.py +0 -0
@@ -65,6 +65,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
65
65
  self.apply_scaling_matrix = True
66
66
  self.add_ridge_regulatisation = True
67
67
  self.ridge_factor = 1e-8
68
+
68
69
  def set_nelements(self, nelements: int) -> int:
69
70
  return self.support.set_nelements(nelements)
70
71
 
@@ -247,11 +248,11 @@ class DiscreteInterpolator(GeologicalInterpolator):
247
248
 
248
249
  rows = np.tile(rows, (A.shape[-1], 1)).T
249
250
  self.constraints[name] = {
250
- 'matrix': sparse.coo_matrix(
251
+ "matrix": sparse.coo_matrix(
251
252
  (A.flatten(), (rows.flatten(), idc.flatten())), shape=(n_rows, self.dof)
252
253
  ).tocsc(),
253
- 'b': B.flatten(),
254
- 'w': w,
254
+ "b": B.flatten(),
255
+ "w": w,
255
256
  }
256
257
 
257
258
  @abstractmethod
@@ -305,7 +306,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
305
306
  rows = np.tile(rows, (A.shape[-1], 1)).T
306
307
 
307
308
  self.ineq_constraints[name] = {
308
- 'matrix': sparse.coo_matrix(
309
+ "matrix": sparse.coo_matrix(
309
310
  (A.flatten(), (rows.flatten(), idc.flatten())), shape=(rows.shape[0], self.dof)
310
311
  ).tocsc(),
311
312
  "bounds": bounds,
@@ -320,7 +321,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
320
321
  rows = np.tile(rows, (a.shape[-1], 1)).T
321
322
  a = a[inside]
322
323
  cols = self.support.elements[element[inside]]
323
- self.add_inequality_constraints_to_matrix(a, points[:, 3:5], cols, 'inequality_value')
324
+ self.add_inequality_constraints_to_matrix(a, points[:, 3:5], cols, "inequality_value")
324
325
 
325
326
  def add_inequality_pairs_constraints(
326
327
  self,
@@ -354,11 +355,11 @@ class DiscreteInterpolator(GeologicalInterpolator):
354
355
  lower_interpolation = self.support.get_element_for_location(lower_points)
355
356
  if (~upper_interpolation[3]).sum() > 0:
356
357
  logger.warning(
357
- f'Upper points not in mesh {upper_points[~upper_interpolation[3]]}'
358
+ f"Upper points not in mesh {upper_points[~upper_interpolation[3]]}"
358
359
  )
359
360
  if (~lower_interpolation[3]).sum() > 0:
360
361
  logger.warning(
361
- f'Lower points not in mesh {lower_points[~lower_interpolation[3]]}'
362
+ f"Lower points not in mesh {lower_points[~lower_interpolation[3]]}"
362
363
  )
363
364
  ij = np.array(
364
365
  [
@@ -392,7 +393,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
392
393
  bounds[:, 1] = upper_bound
393
394
 
394
395
  self.add_inequality_constraints_to_matrix(
395
- a, bounds, cols, f'inequality_pairs_{pair[0]}_{pair[1]}'
396
+ a, bounds, cols, f"inequality_pairs_{pair[0]}_{pair[1]}"
396
397
  )
397
398
 
398
399
  def add_inequality_feature(
@@ -506,13 +507,14 @@ class DiscreteInterpolator(GeologicalInterpolator):
506
507
  for c in self.constraints.values():
507
508
  if len(c["w"]) == 0:
508
509
  continue
509
- mats.append(c['matrix'].multiply(c['w'][:, None]))
510
- bs.append(c['b'] * c['w'])
510
+ mats.append(c["matrix"].multiply(c["w"][:, None]))
511
+ bs.append(c["b"] * c["w"])
511
512
  A = sparse.vstack(mats)
512
513
  logger.info(f"Interpolation matrix is {A.shape[0]} x {A.shape[1]}")
513
514
 
514
515
  B = np.hstack(bs)
515
516
  return A, B
517
+
516
518
  def compute_column_scaling_matrix(self, A: sparse.csr_matrix) -> sparse.dia_matrix:
517
519
  """Compute column scaling matrix S for matrix A so that A @ S has columns with unit norm.
518
520
 
@@ -576,8 +578,8 @@ class DiscreteInterpolator(GeologicalInterpolator):
576
578
  mats = []
577
579
  bounds = []
578
580
  for c in self.ineq_constraints.values():
579
- mats.append(c['matrix'])
580
- bounds.append(c['bounds'])
581
+ mats.append(c["matrix"])
582
+ bounds.append(c["bounds"])
581
583
  if len(mats) == 0:
582
584
  return sparse.csr_matrix((0, self.dof), dtype=float), np.zeros((0, 3))
583
585
  Q = sparse.vstack(mats)
@@ -623,40 +625,40 @@ class DiscreteInterpolator(GeologicalInterpolator):
623
625
 
624
626
  Q, bounds = self.build_inequality_matrix()
625
627
  if callable(solver):
626
- logger.warning('Using custom solver')
628
+ logger.warning("Using custom solver")
627
629
  self.c = solver(A.tocsr(), b)
628
630
  self.up_to_date = True
629
631
  elif isinstance(solver, str) or solver is None:
630
- if solver not in ['cg', 'lsmr', 'admm']:
632
+ if solver not in ["cg", "lsmr", "admm"]:
631
633
  logger.warning(
632
- f'Unknown solver {solver} using cg. \n Available solvers are cg and lsmr or a custom solver as a callable function'
634
+ f"Unknown solver {solver} using cg. \n Available solvers are cg and lsmr or a custom solver as a callable function"
633
635
  )
634
- solver = 'cg'
635
- if solver == 'cg':
636
+ solver = "cg"
637
+ if solver == "cg":
636
638
  logger.info("Solving using cg")
637
- if 'atol' not in solver_kwargs or 'rtol' not in solver_kwargs:
639
+ if "atol" not in solver_kwargs or "rtol" not in solver_kwargs:
638
640
  if tol is not None:
639
- solver_kwargs['atol'] = tol
641
+ solver_kwargs["atol"] = tol
640
642
 
641
643
  logger.info(f"Solver kwargs: {solver_kwargs}")
642
644
 
643
645
  res = sparse.linalg.cg(A.T @ A, A.T @ b, **solver_kwargs)
644
646
  if res[1] > 0:
645
647
  logger.warning(
646
- f'CG reached iteration limit ({res[1]})and did not converge, check input data. Setting solution to last iteration'
648
+ f"CG reached iteration limit ({res[1]})and did not converge, check input data. Setting solution to last iteration"
647
649
  )
648
650
  self.c = res[0]
649
651
  self.up_to_date = True
650
652
 
651
- elif solver == 'lsmr':
653
+ elif solver == "lsmr":
652
654
  logger.info("Solving using lsmr")
653
655
  # if 'atol' not in solver_kwargs:
654
656
  # if tol is not None:
655
657
  # solver_kwargs['atol'] = tol
656
- if 'btol' not in solver_kwargs:
658
+ if "btol" not in solver_kwargs:
657
659
  if tol is not None:
658
- solver_kwargs['btol'] = tol
659
- solver_kwargs['atol'] = 0.
660
+ solver_kwargs["btol"] = tol
661
+ solver_kwargs["atol"] = 0.0
660
662
  logger.info(f"Setting lsmr btol to {tol}")
661
663
  logger.info(f"Solver kwargs: {solver_kwargs}")
662
664
  res = sparse.linalg.lsmr(A, b, **solver_kwargs)
@@ -674,31 +676,31 @@ class DiscreteInterpolator(GeologicalInterpolator):
674
676
  self.c = res[0]
675
677
  self.up_to_date = True
676
678
 
677
- elif solver == 'admm':
679
+ elif solver == "admm":
678
680
  logger.info("Solving using admm")
679
681
 
680
- if 'x0' in solver_kwargs:
681
- x0 = solver_kwargs['x0'](self.support)
682
+ if "x0" in solver_kwargs:
683
+ x0 = solver_kwargs["x0"](self.support)
682
684
  else:
683
685
  x0 = np.zeros(A.shape[1])
684
- solver_kwargs.pop('x0', None)
686
+ solver_kwargs.pop("x0", None)
685
687
  if Q is None:
686
688
  logger.warning("No inequality constraints, using lsmr")
687
- return self.solve_system('lsmr', solver_kwargs)
689
+ return self.solve_system("lsmr", solver_kwargs=solver_kwargs)
688
690
 
689
691
  try:
690
692
  from loopsolver import admm_solve
691
693
 
692
694
  try:
693
- linsys_solver = solver_kwargs.pop('linsys_solver', 'lsmr')
695
+ linsys_solver = solver_kwargs.pop("linsys_solver", "lsmr")
694
696
  res = admm_solve(
695
697
  A,
696
698
  b,
697
699
  Q,
698
700
  bounds,
699
701
  x0=x0,
700
- admm_weight=solver_kwargs.pop('admm_weight', 0.01),
701
- nmajor=solver_kwargs.pop('nmajor', 200),
702
+ admm_weight=solver_kwargs.pop("admm_weight", 0.01),
703
+ nmajor=solver_kwargs.pop("nmajor", 200),
702
704
  linsys_solver_kwargs=solver_kwargs,
703
705
  linsys_solver=linsys_solver,
704
706
  )
@@ -756,12 +758,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
756
758
  """
757
759
  self.update()
758
760
  evaluation_points = np.array(locations)
759
- evaluated = np.zeros(evaluation_points.shape[0])
760
- mask = np.any(evaluation_points == np.nan, axis=1)
761
-
762
- if evaluation_points[~mask, :].shape[0] > 0:
763
- evaluated[~mask] = self.support.evaluate_value(evaluation_points[~mask], self.c)
764
- return evaluated
761
+ return self.support.evaluate_value(evaluation_points, self.c)
765
762
 
766
763
  def evaluate_gradient(self, locations: np.ndarray) -> np.ndarray:
767
764
  """
@@ -792,4 +789,4 @@ class DiscreteInterpolator(GeologicalInterpolator):
792
789
  def vtk(self):
793
790
  if self.up_to_date is False:
794
791
  self.update()
795
- return self.support.vtk({'c': self.c})
792
+ return self.support.vtk({"c": self.c})
@@ -285,10 +285,8 @@ class BaseStructuredSupport(BaseSupport):
285
285
 
286
286
  def inside(self, pos):
287
287
  # check whether point is inside box
288
- inside = np.ones(pos.shape[0]).astype(bool)
289
- for i in range(3):
290
- inside *= pos[:, i] > self.origin[None, i]
291
- inside *= pos[:, i] < self.maximum[None, i]
288
+ pos = self.check_position(pos)
289
+ inside = np.all((pos > self.origin) & (pos < self.maximum), axis=1)
292
290
  return inside
293
291
 
294
292
  def check_position(self, pos: np.ndarray) -> np.ndarray:
@@ -306,11 +304,21 @@ class BaseStructuredSupport(BaseSupport):
306
304
  [type]
307
305
  [description]
308
306
  """
309
- pos = np.array(pos)
307
+ if not isinstance(pos, np.ndarray):
308
+ try:
309
+ pos = np.array(pos, dtype=float)
310
+ except Exception as e:
311
+ logger.error(
312
+ f"Position array should be a numpy array or list of points, not {type(pos)}"
313
+ )
314
+ raise ValueError(
315
+ f"Position array should be a numpy array or list of points, not {type(pos)}"
316
+ ) from e
317
+
310
318
  if len(pos.shape) == 1:
311
319
  pos = np.array([pos])
312
320
  if len(pos.shape) != 2:
313
- print("Position array needs to be a list of points or a point")
321
+ logger.error("Position array needs to be a list of points or a point")
314
322
  raise ValueError("Position array needs to be a list of points or a point")
315
323
  return pos
316
324
 
@@ -379,20 +387,24 @@ class BaseStructuredSupport(BaseSupport):
379
387
  ----------
380
388
  pos : np.array
381
389
  (N, 3) array of xyz coordinates representing the positions of N points.
382
-
390
+
383
391
  Returns
384
392
  -------
385
393
  globalidx : np.array
386
- (N, 8) array of global indices corresponding to the 8 corner nodes of the cell
387
- each point lies in. If a point lies outside the support, its corresponding entry
394
+ (N, 8) array of global indices corresponding to the 8 corner nodes of the cell
395
+ each point lies in. If a point lies outside the support, its corresponding entry
388
396
  will be set to -1.
389
397
  inside : np.array
390
398
  (N,) boolean array indicating whether each point is inside the support domain.
391
399
  """
392
400
  cell_indexes, inside = self.position_to_cell_index(pos)
393
- corner_indexes = self.cell_corner_indexes(cell_indexes)
394
- globalidx = self.global_node_indices(corner_indexes)
395
- # if global index is not inside the support set to -1
401
+ nx, ny = self.nsteps[0], self.nsteps[1]
402
+ offsets = np.array(
403
+ [0, 1, nx, nx + 1, nx * ny, nx * ny + 1, nx * ny + nx, nx * ny + nx + 1],
404
+ dtype=np.intp,
405
+ )
406
+ g = cell_indexes[:, 0] + nx * cell_indexes[:, 1] + nx * ny * cell_indexes[:, 2]
407
+ globalidx = g[:, None] + offsets[None, :] # (N, 8)
396
408
  globalidx[~inside] = -1
397
409
  return globalidx, inside
398
410
 
@@ -66,8 +66,25 @@ class LambdaGeologicalFeature(BaseFeature):
66
66
  v = np.zeros((pos.shape[0]))
67
67
  v[:] = np.nan
68
68
 
69
- mask = self._calculate_mask(pos, ignore_regions=ignore_regions)
70
- pos = self._apply_faults(pos)
69
+ # Precompute each fault's scalar value (gx = fault.__getitem__(0).evaluate_value)
70
+ # once and reuse for both the region mask check and fault application.
71
+ # FaultSegment.evaluate_value(pos) == self.__getitem__(0).evaluate_value(pos) == gx.
72
+ # apply_to_points also needs gx to determine the displacement mask — passing it
73
+ # avoids the duplicate evaluation on the np.copy of pos.
74
+ precomputed_gx = {id(f): f.evaluate_value(pos) for f in self.faults}
75
+
76
+ # Region mask: pass precomputed gx so PositiveRegion/NegativeRegion skip re-evaluation
77
+ mask = np.ones(pos.shape[0], dtype=bool)
78
+ if not ignore_regions:
79
+ for r in self.regions:
80
+ pre = precomputed_gx.get(id(getattr(r, 'feature', None)))
81
+ mask = np.logical_and(mask, r(pos, precomputed_val=pre))
82
+
83
+ # Apply faults: pass precomputed gx so apply_to_points skips one evaluate_value call
84
+ if self.faults_enabled:
85
+ for f in self.faults:
86
+ pos = f.apply_to_points(pos, precomputed_gx=precomputed_gx.get(id(f)))
87
+
71
88
  if self.function is not None:
72
89
  v[mask] = self.function(pos[mask,:])
73
90
  return v
@@ -311,13 +311,14 @@ class FaultSegment(StructuralFrame):
311
311
  d[mask] = self.faultfunction(gx[mask] + self.fault_offset, gy[mask], gz[mask])
312
312
  return d * self.displacement
313
313
 
314
- def apply_to_points(self, points, reverse=False):
314
+ def apply_to_points(self, points, reverse=False, precomputed_gx=None):
315
315
  """
316
316
  Unfault the array of points
317
317
 
318
318
  Parameters
319
319
  ----------
320
320
  points - numpy array Nx3
321
+ precomputed_gx - optional pre-evaluated gx values (same points, avoids duplicate evaluation)
321
322
 
322
323
  Returns
323
324
  -------
@@ -328,10 +329,12 @@ class FaultSegment(StructuralFrame):
328
329
  newp = np.copy(points).astype(float)
329
330
  # evaluate fault function for all points
330
331
  # then define mask for only points affected by fault
331
- gx = None
332
- gy = None
333
- gz = None
334
- if use_threads:
332
+ # gx may be supplied by caller to avoid re-evaluation (precomputed from region check)
333
+ if precomputed_gx is not None:
334
+ gx = precomputed_gx
335
+ gy = self.__getitem__(1).evaluate_value(newp)
336
+ gz = self.__getitem__(2).evaluate_value(newp)
337
+ elif use_threads:
335
338
  with ThreadPoolExecutor(max_workers=8) as executor:
336
339
  # all of these operations should be
337
340
  # independent so just run as different threads
@@ -361,11 +364,15 @@ class FaultSegment(StructuralFrame):
361
364
  d *= -1.0
362
365
  # calculate the fault frame for the evaluation points
363
366
  for _i in range(steps):
364
- gx = None
365
- gy = None
366
- gz = None
367
367
  g = None
368
- if use_threads:
368
+ if _i == 0:
369
+ # Reuse gx/gy/gz from the initial full-array evaluation above — newp[mask] hasn't
370
+ # moved yet on the first iteration, so values are identical.
371
+ gx_m = gx[mask]
372
+ gy_m = gy[mask]
373
+ gz_m = gz[mask]
374
+ g = self.__getitem__(1).evaluate_gradient(newp[mask, :], ignore_regions=True)
375
+ elif use_threads:
369
376
  with ThreadPoolExecutor(max_workers=8) as executor:
370
377
  # all of these operations should be
371
378
  # independent so just run as different threads
@@ -373,15 +380,16 @@ class FaultSegment(StructuralFrame):
373
380
  g_future = executor.submit(self.__getitem__(1).evaluate_gradient, newp[mask, :])
374
381
  gy_future = executor.submit(self.__getitem__(1).evaluate_value, newp[mask, :])
375
382
  gz_future = executor.submit(self.__getitem__(2).evaluate_value, newp[mask, :])
376
- gx = gx_future.result()
383
+ gx_m = gx_future.result()
377
384
  g = g_future.result()
378
- gy = gy_future.result()
379
- gz = gz_future.result()
385
+ gy_m = gy_future.result()
386
+ gz_m = gz_future.result()
380
387
  else:
381
- gx = self.__getitem__(0).evaluate_value(newp[mask, :])
382
- gy = self.__getitem__(1).evaluate_value(newp[mask, :])
383
- gz = self.__getitem__(2).evaluate_value(newp[mask, :])
388
+ gx_m = self.__getitem__(0).evaluate_value(newp[mask, :])
389
+ gy_m = self.__getitem__(1).evaluate_value(newp[mask, :])
390
+ gz_m = self.__getitem__(2).evaluate_value(newp[mask, :])
384
391
  g = self.__getitem__(1).evaluate_gradient(newp[mask, :], ignore_regions=True)
392
+ gx, gy, gz = gx_m, gy_m, gz_m # alias for block below
385
393
  # # get the fault frame val/grad for the points
386
394
  # determine displacement magnitude, for constant displacement
387
395
  # hanging wall should be > 0
@@ -45,23 +45,22 @@ class BaseSignRegion(BaseRegion):
45
45
  self.name = 'PositiveRegion'
46
46
  self.parent = feature
47
47
 
48
- def _calculate_value_and_distance(self, xyz)-> Tuple[np.ndarray, np.ndarray]:
49
- val = self.feature.evaluate_value(xyz)
50
- # find a point on/near 0 isosurface
48
+ def _calculate_value_and_distance(self, xyz, precomputed_val=None)-> Tuple[np.ndarray, np.ndarray]:
49
+ val = precomputed_val if precomputed_val is not None else self.feature.evaluate_value(xyz)
50
+ # find a point on/near 0 isosurface — compute once and cache on self
51
51
  if self.point is None:
52
52
  mask = np.zeros(xyz.shape[0], dtype="bool")
53
53
  mask[:] = val < 0
54
54
  if np.sum(mask) == 0:
55
55
  raise ValueError("Cannot find point on surface")
56
- centre = xyz[mask, :][0, :]
57
- else:
58
- centre = self.point
56
+ self.point = xyz[mask, :][0, :] # cache: characterises the fault, not the query points
57
+ centre = self.point
59
58
  if self.vector is None:
60
59
  average_gradient = self.feature.evaluate_gradient(np.array([centre]))[0]
61
60
  average_gradient[2] = 0
62
61
  average_gradient /= np.linalg.norm(average_gradient)
63
- else:
64
- average_gradient = self.vector
62
+ self.vector = average_gradient # cache: fault geometry doesn't change between calls
63
+ average_gradient = self.vector
65
64
 
66
65
  distance = np.einsum(
67
66
  "ij,j->i", centre[None, :] - xyz, average_gradient.reshape(-1, 3)[0, :]
@@ -80,8 +79,8 @@ class PositiveRegion(BaseSignRegion):
80
79
  self.name = 'PositiveRegion'
81
80
  self.parent = feature
82
81
 
83
- def __call__(self, xyz) -> np.ndarray:
84
- val, distance = self._calculate_value_and_distance(xyz)
82
+ def __call__(self, xyz, precomputed_val=None) -> np.ndarray:
83
+ val, distance = self._calculate_value_and_distance(xyz, precomputed_val=precomputed_val)
85
84
  return np.logical_or(
86
85
  np.logical_and(~np.isnan(val), val > 0),
87
86
  np.logical_and(np.isnan(val), distance > 0),
@@ -99,8 +98,8 @@ class NegativeRegion(BaseSignRegion):
99
98
  self.name = 'NegativeRegion'
100
99
  self.parent = feature
101
100
 
102
- def __call__(self, xyz) -> np.ndarray:
103
- val, distance = self._calculate_value_and_distance(xyz)
101
+ def __call__(self, xyz, precomputed_val=None) -> np.ndarray:
102
+ val, distance = self._calculate_value_and_distance(xyz, precomputed_val=precomputed_val)
104
103
  return np.logical_or(
105
104
  np.logical_and(~np.isnan(val), val < 0),
106
105
  np.logical_and(np.isnan(val), distance < 0),
@@ -0,0 +1 @@
1
+ __version__ = "1.6.27"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LoopStructural
3
- Version: 1.6.26
3
+ Version: 1.6.27
4
4
  Summary: 3D geological modelling
5
5
  Author-email: Lachlan Grose <lachlan.grose@monash.edu>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LoopStructural
3
- Version: 1.6.26
3
+ Version: 1.6.27
4
4
  Summary: 3D geological modelling
5
5
  Author-email: Lachlan Grose <lachlan.grose@monash.edu>
6
6
  License: MIT
@@ -1 +0,0 @@
1
- __version__ = "1.6.26"
File without changes