LoopStructural 1.6.14__tar.gz → 1.6.16__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 (147) hide show
  1. loopstructural-1.6.16/LoopStructural/__init__.py +71 -0
  2. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datatypes/_bounding_box.py +22 -13
  3. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datatypes/_point.py +0 -1
  4. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/export/exporters.py +2 -2
  5. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/__init__.py +33 -30
  6. loopstructural-1.6.16/LoopStructural/interpolators/_constant_norm.py +205 -0
  7. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_discrete_interpolator.py +15 -14
  8. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_finite_difference_interpolator.py +10 -10
  9. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_geological_interpolator.py +9 -3
  10. loopstructural-1.6.16/LoopStructural/interpolators/_interpolatortype.py +22 -0
  11. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_p1interpolator.py +6 -2
  12. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_surfe_wrapper.py +4 -1
  13. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_2d_base_unstructured.py +1 -1
  14. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_2d_structured_grid.py +16 -0
  15. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_3d_base_structured.py +16 -0
  16. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
  17. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/core/geological_model.py +250 -312
  18. loopstructural-1.6.16/LoopStructural/modelling/core/stratigraphic_column.py +473 -0
  19. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_base_geological_feature.py +38 -2
  20. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/builders/_fault_builder.py +1 -0
  21. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +1 -1
  22. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fault/_fault_segment.py +1 -1
  23. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/intrusion_builder.py +1 -1
  24. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +1 -1
  25. loopstructural-1.6.16/LoopStructural/version.py +1 -0
  26. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural.egg-info/PKG-INFO +2 -2
  27. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural.egg-info/SOURCES.txt +3 -0
  28. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural.egg-info/requires.txt +1 -1
  29. {loopstructural-1.6.14 → loopstructural-1.6.16}/PKG-INFO +2 -2
  30. {loopstructural-1.6.14 → loopstructural-1.6.16}/pyproject.toml +1 -1
  31. loopstructural-1.6.14/LoopStructural/__init__.py +0 -53
  32. loopstructural-1.6.14/LoopStructural/version.py +0 -1
  33. {loopstructural-1.6.14 → loopstructural-1.6.16}/LICENSE +0 -0
  34. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/__init__.py +0 -0
  35. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/_base.py +0 -0
  36. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/_example_models.py +0 -0
  37. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/claudius.csv +0 -0
  38. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
  39. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/duplex.csv +0 -0
  40. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/duplexbb.txt +0 -0
  41. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
  42. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  43. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
  44. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  45. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  46. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
  47. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
  48. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
  49. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
  50. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
  51. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
  52. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
  53. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
  54. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
  55. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/intrusion.csv +0 -0
  56. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
  57. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
  58. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/onefolddata.csv +0 -0
  59. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
  60. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
  61. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
  62. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datatypes/__init__.py +0 -0
  63. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datatypes/_structured_grid.py +0 -0
  64. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/datatypes/_surface.py +0 -0
  65. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/export/file_formats.py +0 -0
  66. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/export/geoh5.py +0 -0
  67. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/export/gocad.py +0 -0
  68. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/export/omf_wrapper.py +0 -0
  69. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_api.py +0 -0
  70. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_builders.py +0 -0
  71. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_cython/__init__.py +0 -0
  72. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_discrete_fold_interpolator.py +0 -0
  73. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_interpolator_builder.py +0 -0
  74. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_interpolator_factory.py +0 -0
  75. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_operator.py +0 -0
  76. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/_p2interpolator.py +0 -0
  77. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +0 -0
  78. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -0
  79. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  80. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -0
  81. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_3d_structured_grid.py +0 -0
  82. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +0 -0
  83. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/__init__.py +0 -0
  84. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_aabb.py +0 -0
  85. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_base_support.py +0 -0
  86. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_face_table.py +0 -0
  87. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/interpolators/supports/_support_factory.py +0 -0
  88. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/__init__.py +0 -0
  89. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/core/__init__.py +0 -0
  90. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/__init__.py +0 -0
  91. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_analytical_feature.py +0 -0
  92. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_cross_product_geological_feature.py +0 -0
  93. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_geological_feature.py +0 -0
  94. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_lambda_geological_feature.py +0 -0
  95. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_projected_vector_feature.py +0 -0
  96. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_region.py +0 -0
  97. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_structural_frame.py +0 -0
  98. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/_unconformity_feature.py +0 -0
  99. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/builders/__init__.py +0 -0
  100. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/builders/_base_builder.py +0 -0
  101. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +0 -0
  102. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +0 -0
  103. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fault/__init__.py +0 -0
  104. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fault/_fault_function.py +0 -0
  105. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fault/_fault_function_feature.py +0 -0
  106. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/__init__.py +0 -0
  107. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/_fold.py +0 -0
  108. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -0
  109. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/_foldframe.py +0 -0
  110. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/_svariogram.py +0 -0
  111. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/fold_function/__init__.py +0 -0
  112. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +0 -0
  113. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +0 -0
  114. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +0 -0
  115. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +0 -0
  116. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/input/__init__.py +0 -0
  117. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/input/fault_network.py +0 -0
  118. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/input/map2loop_processor.py +0 -0
  119. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/input/process_data.py +0 -0
  120. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/input/project_file.py +0 -0
  121. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/__init__.py +0 -0
  122. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +0 -0
  123. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +0 -0
  124. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/intrusion_feature.py +0 -0
  125. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +0 -0
  126. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/__init__.py +0 -0
  127. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/_surface.py +0 -0
  128. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/_transformation.py +0 -0
  129. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/colours.py +0 -0
  130. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/config.py +0 -0
  131. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/dtm_creator.py +0 -0
  132. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/exceptions.py +0 -0
  133. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/features.py +0 -0
  134. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/helper.py +0 -0
  135. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/json_encoder.py +0 -0
  136. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/linalg.py +0 -0
  137. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/logging.py +0 -0
  138. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/maths.py +0 -0
  139. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/regions.py +0 -0
  140. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/typing.py +0 -0
  141. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/utils/utils.py +0 -0
  142. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural/visualisation/__init__.py +0 -0
  143. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural.egg-info/dependency_links.txt +0 -0
  144. {loopstructural-1.6.14 → loopstructural-1.6.16}/LoopStructural.egg-info/top_level.txt +0 -0
  145. {loopstructural-1.6.14 → loopstructural-1.6.16}/README.md +0 -0
  146. {loopstructural-1.6.14 → loopstructural-1.6.16}/setup.cfg +0 -0
  147. {loopstructural-1.6.14 → loopstructural-1.6.16}/setup.py +0 -0
@@ -0,0 +1,71 @@
1
+ """
2
+ LoopStructural
3
+ ==============
4
+
5
+ """
6
+
7
+ import logging
8
+ from logging.config import dictConfig
9
+
10
+ __all__ = ["GeologicalModel"]
11
+ import tempfile
12
+ from pathlib import Path
13
+ from .version import __version__
14
+
15
+ experimental = False
16
+ ch = logging.StreamHandler()
17
+ formatter = logging.Formatter("%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d -- %(message)s")
18
+ ch.setFormatter(formatter)
19
+ ch.setLevel(logging.WARNING)
20
+ loggers = {}
21
+ from .modelling.core.geological_model import GeologicalModel
22
+ from .modelling.core.stratigraphic_column import StratigraphicColumn
23
+ from .interpolators._api import LoopInterpolator
24
+ from .interpolators import InterpolatorBuilder
25
+ from .datatypes import BoundingBox
26
+ from .utils import log_to_console, log_to_file, getLogger, rng, get_levels
27
+
28
+ logger = getLogger(__name__)
29
+ logger.info("Imported LoopStructural")
30
+
31
+
32
+ def setLogging(level="info", handler=None):
33
+ """
34
+ Set the logging parameters for log file or custom handler
35
+
36
+ Parameters
37
+ ----------
38
+ level : str
39
+ 'info', 'warning', 'error', 'debug'
40
+ handler : logging.Handler, optional
41
+ A logging handler to use instead of the default StreamHandler
42
+ """
43
+ import LoopStructural
44
+
45
+ levels = get_levels()
46
+ level_value = levels.get(level, logging.WARNING)
47
+
48
+ # Create default handler if none provided
49
+ if handler is None:
50
+ handler = logging.StreamHandler()
51
+
52
+ formatter = logging.Formatter(
53
+ "%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d -- %(message)s"
54
+ )
55
+ handler.setFormatter(formatter)
56
+ handler.setLevel(level_value)
57
+
58
+ # Replace handlers in all known loggers
59
+ for name in LoopStructural.loggers:
60
+ logger = logging.getLogger(name)
61
+ logger.handlers = []
62
+ logger.addHandler(handler)
63
+ logger.setLevel(level_value)
64
+
65
+ # Also apply to main module logger
66
+ main_logger = logging.getLogger(__name__)
67
+ main_logger.handlers = []
68
+ main_logger.addHandler(handler)
69
+ main_logger.setLevel(level_value)
70
+
71
+ main_logger.info(f"Set logging to {level}")
@@ -17,6 +17,7 @@ class BoundingBox:
17
17
  origin: Optional[np.ndarray] = None,
18
18
  maximum: Optional[np.ndarray] = None,
19
19
  global_origin: Optional[np.ndarray] = None,
20
+ global_maximum: Optional[np.ndarray] = None,
20
21
  nsteps: Optional[np.ndarray] = None,
21
22
  step_vector: Optional[np.ndarray] = None,
22
23
  dimensions: Optional[int] = 3,
@@ -39,9 +40,12 @@ class BoundingBox:
39
40
  # we want the local coordinates to start at 0
40
41
  # otherwise uses provided origin. This is useful for having multiple bounding boxes rela
41
42
  if global_origin is not None and origin is None:
42
- origin = np.zeros(global_origin.shape)
43
+ origin = np.zeros(np.array(global_origin).shape)
44
+ if global_maximum is not None and global_origin is not None:
45
+ maximum = np.array(global_maximum) - np.array(global_origin)
46
+
43
47
  if maximum is None and nsteps is not None and step_vector is not None:
44
- maximum = origin + nsteps * step_vector
48
+ maximum = np.array(origin) + np.array(nsteps) * np.array(step_vector)
45
49
  if origin is not None and global_origin is None:
46
50
  global_origin = np.zeros(3)
47
51
  self._origin = np.array(origin)
@@ -500,48 +504,53 @@ class BoundingBox:
500
504
  name=name,
501
505
  )
502
506
 
503
- def project(self, xyz):
507
+ def project(self, xyz, inplace=False):
504
508
  """Project a point into the bounding box
505
509
 
506
510
  Parameters
507
511
  ----------
508
512
  xyz : np.ndarray
509
513
  point to project
514
+ inplace : bool, optional
515
+ Whether to modify the input array in place, by default False
510
516
 
511
517
  Returns
512
518
  -------
513
519
  np.ndarray
514
520
  projected point
515
521
  """
516
-
517
- return (xyz - self.global_origin) / np.max(
518
- (self.global_maximum - self.global_origin)
519
- ) # np.clip(xyz, self.origin, self.maximum)
522
+ if inplace:
523
+ xyz -= self.global_origin
524
+ return xyz
525
+ return (xyz - self.global_origin) # np.clip(xyz, self.origin, self.maximum)
520
526
 
521
527
  def scale_by_projection_factor(self, value):
522
528
  return value / np.max((self.global_maximum - self.global_origin))
523
529
 
524
- def reproject(self, xyz):
530
+ def reproject(self, xyz, inplace=False):
525
531
  """Reproject a point from the bounding box to the global space
526
532
 
527
533
  Parameters
528
534
  ----------
529
535
  xyz : np.ndarray
530
536
  point to reproject
531
-
537
+ inplace : bool, optional
538
+ Whether to modify the input array in place, by default False
532
539
  Returns
533
540
  -------
534
541
  np.ndarray
535
542
  reprojected point
536
543
  """
537
-
538
- return xyz * np.max((self.global_maximum - self.global_origin)) + self.global_origin
544
+ if inplace:
545
+ xyz += self.global_origin
546
+ return xyz
547
+ return xyz + self.global_origin
539
548
 
540
549
  def __repr__(self):
541
- return f"BoundingBox({self.origin}, {self.maximum}, {self.nsteps})"
550
+ return f"BoundingBox(origin:{self.origin}, maximum:{self.maximum}, nsteps:{self.nsteps})"
542
551
 
543
552
  def __str__(self):
544
- return f"BoundingBox({self.origin}, {self.maximum}, {self.nsteps})"
553
+ return f"BoundingBox(origin:{self.origin}, maximum:{self.maximum}, nsteps:{self.nsteps})"
545
554
 
546
555
  def __eq__(self, other):
547
556
  if not isinstance(other, BoundingBox):
@@ -157,7 +157,6 @@ class VectorPoints:
157
157
  try:
158
158
  locations = bb.project(locations)
159
159
  _projected = True
160
- scale = bb.scale_by_projection_factor(scale)
161
160
  except Exception as e:
162
161
  logger.error(f'Failed to project points to bounding box: {e}')
163
162
  logger.error('Using unprojected points, this may cause issues with the glyphing')
@@ -423,7 +423,7 @@ def _write_vol_evtk(model, file_name, data_label, nsteps, real_coords=True):
423
423
 
424
424
  """
425
425
  # Define grid spacing
426
- xyz = model.bounding_box.regular_grid(nsteps)
426
+ xyz = model.bounding_box.regular_grid(nsteps=nsteps)
427
427
  vals = model.evaluate_model(xyz, scale=False)
428
428
  if real_coords:
429
429
  model.rescale(xyz)
@@ -465,7 +465,7 @@ def _write_vol_gocad(model, file_name, data_label, nsteps, real_coords=True):
465
465
 
466
466
  """
467
467
  # Define grid spacing in model scale coords
468
- xyz = model.bounding_box.regular_grid(nsteps)
468
+ xyz = model.bounding_box.regular_grid(nsteps=nsteps)
469
469
 
470
470
  vals = model.evaluate_model(xyz, scale=False)
471
471
  # Use FORTRAN style indexing for GOCAD VOXET
@@ -3,6 +3,7 @@ Interpolators and interpolation supports
3
3
 
4
4
  """
5
5
 
6
+
6
7
  __all__ = [
7
8
  "InterpolatorType",
8
9
  "GeologicalInterpolator",
@@ -21,39 +22,12 @@ __all__ = [
21
22
  "StructuredGrid2D",
22
23
  "P2UnstructuredTetMesh",
23
24
  ]
24
- from enum import IntEnum
25
+ from ._interpolatortype import InterpolatorType
25
26
 
26
27
  from ..utils import getLogger
27
28
 
28
29
  logger = getLogger(__name__)
29
30
 
30
-
31
- class InterpolatorType(IntEnum):
32
- """
33
- Enum for the different interpolator types
34
-
35
- 1-9 should cover interpolators with supports
36
- 9+ are data supported
37
- """
38
-
39
- BASE = 0
40
- BASE_DISCRETE = 1
41
- FINITE_DIFFERENCE = 2
42
- DISCRETE_FOLD = 3
43
- PIECEWISE_LINEAR = 4
44
- PIECEWISE_QUADRATIC = 5
45
- BASE_DATA_SUPPORTED = 10
46
- SURFE = 11
47
-
48
-
49
- interpolator_string_map = {
50
- "FDI": InterpolatorType.FINITE_DIFFERENCE,
51
- "PLI": InterpolatorType.PIECEWISE_LINEAR,
52
- "P2": InterpolatorType.PIECEWISE_QUADRATIC,
53
- "P1": InterpolatorType.PIECEWISE_LINEAR,
54
- "DFI": InterpolatorType.DISCRETE_FOLD,
55
- 'surfe': InterpolatorType.SURFE,
56
- }
57
31
  from ..interpolators._geological_interpolator import GeologicalInterpolator
58
32
  from ..interpolators._discrete_interpolator import DiscreteInterpolator
59
33
  from ..interpolators.supports import (
@@ -79,7 +53,7 @@ from ..interpolators._discrete_fold_interpolator import (
79
53
  )
80
54
  from ..interpolators._p2interpolator import P2Interpolator
81
55
  from ..interpolators._p1interpolator import P1Interpolator
82
-
56
+ from ..interpolators._constant_norm import ConstantNormP1Interpolator, ConstantNormFDIInterpolator
83
57
  try:
84
58
  from ..interpolators._surfe_wrapper import SurfeRBFInterpolator
85
59
  except ImportError:
@@ -93,6 +67,24 @@ except ImportError:
93
67
  raise ImportError(
94
68
  "Surfe cannot be imported. Please install Surfe. pip install surfe/ conda install -c loop3d surfe"
95
69
  )
70
+
71
+ # Ensure compatibility between the fallback and imported class
72
+ SurfeRBFInterpolator = SurfeRBFInterpolator
73
+
74
+
75
+ interpolator_string_map = {
76
+ "FDI": InterpolatorType.FINITE_DIFFERENCE,
77
+ "PLI": InterpolatorType.PIECEWISE_LINEAR,
78
+ "P2": InterpolatorType.PIECEWISE_QUADRATIC,
79
+ "P1": InterpolatorType.PIECEWISE_LINEAR,
80
+ "DFI": InterpolatorType.DISCRETE_FOLD,
81
+ 'surfe': InterpolatorType.SURFE,
82
+ "FDI_CN": InterpolatorType.FINITE_DIFFERENCE_CONSTANT_NORM,
83
+ "P1_CN": InterpolatorType.PIECEWISE_LINEAR_CONSTANT_NORM,
84
+
85
+ }
86
+
87
+ # Define the mapping after all imports
96
88
  interpolator_map = {
97
89
  InterpolatorType.BASE: GeologicalInterpolator,
98
90
  InterpolatorType.BASE_DISCRETE: DiscreteInterpolator,
@@ -102,6 +94,8 @@ interpolator_map = {
102
94
  InterpolatorType.PIECEWISE_QUADRATIC: P2Interpolator,
103
95
  InterpolatorType.BASE_DATA_SUPPORTED: GeologicalInterpolator,
104
96
  InterpolatorType.SURFE: SurfeRBFInterpolator,
97
+ InterpolatorType.PIECEWISE_LINEAR_CONSTANT_NORM: ConstantNormP1Interpolator,
98
+ InterpolatorType.FINITE_DIFFERENCE_CONSTANT_NORM: ConstantNormFDIInterpolator,
105
99
  }
106
100
 
107
101
  support_interpolator_map = {
@@ -119,9 +113,18 @@ support_interpolator_map = {
119
113
  3: SupportType.DataSupported,
120
114
  2: SupportType.DataSupported,
121
115
  },
116
+ InterpolatorType.PIECEWISE_LINEAR_CONSTANT_NORM:{
117
+ 3: SupportType.TetMesh,
118
+ 2: SupportType.P1Unstructured2d,
119
+ },
120
+ InterpolatorType.FINITE_DIFFERENCE_CONSTANT_NORM: {
121
+ 3: SupportType.StructuredGrid,
122
+ 2: SupportType.StructuredGrid2D,
123
+ }
122
124
  }
123
125
 
124
126
  from ._interpolator_factory import InterpolatorFactory
125
127
  from ._interpolator_builder import InterpolatorBuilder
126
128
 
127
- # from ._api import LoopInterpolator
129
+
130
+
@@ -0,0 +1,205 @@
1
+ import numpy as np
2
+
3
+ from LoopStructural.interpolators._discrete_interpolator import DiscreteInterpolator
4
+ from LoopStructural.interpolators._finite_difference_interpolator import FiniteDifferenceInterpolator
5
+ from ._p1interpolator import P1Interpolator
6
+ from typing import Optional, Union, Callable
7
+ from scipy import sparse
8
+ from LoopStructural.utils import rng
9
+
10
+ class ConstantNormInterpolator:
11
+ """Adds a non linear constraint to an interpolator to constrain
12
+ the norm of the gradient to be a set value.
13
+
14
+ Returns
15
+ -------
16
+ _type_
17
+ _description_
18
+ """
19
+ def __init__(self, interpolator: DiscreteInterpolator,basetype):
20
+ """Initialise the constant norm inteprolator
21
+ with a discrete interpolator.
22
+
23
+ Parameters
24
+ ----------
25
+ interpolator : DiscreteInterpolator
26
+ The discrete interpolator to add constant norm to.
27
+ """
28
+ self.basetype = basetype
29
+ self.interpolator = interpolator
30
+ self.support = interpolator.support
31
+ self.random_subset = False
32
+ self.norm_length = 1.0
33
+ self.n_iterations = 20
34
+ self.store_solution_history = False
35
+ self.solution_history = []#np.zeros((self.n_iterations, self.support.n_nodes))
36
+ self.gradient_constraint_store = []
37
+ def add_constant_norm(self, w:float):
38
+ """Add a constraint to the interpolator to constrain the norm of the gradient
39
+ to be a set value
40
+
41
+ Parameters
42
+ ----------
43
+ w : float
44
+ weighting of the constraint
45
+ """
46
+ if "constant norm" in self.interpolator.constraints:
47
+ _ = self.interpolator.constraints.pop("constant norm")
48
+
49
+ element_indices = np.arange(self.support.elements.shape[0])
50
+ if self.random_subset:
51
+ rng.shuffle(element_indices)
52
+ element_indices = element_indices[: int(0.1 * self.support.elements.shape[0])]
53
+ vertices, gradient, elements, inside = self.support.get_element_gradient_for_location(
54
+ self.support.barycentre[element_indices]
55
+ )
56
+
57
+ t_g = gradient[:, :, :]
58
+ # t_n = gradient[self.support.shared_element_relationships[:, 1], :, :]
59
+ v_t = np.einsum(
60
+ "ijk,ik->ij",
61
+ t_g,
62
+ self.interpolator.c[self.support.elements[elements]],
63
+ )
64
+
65
+ v_t = v_t / np.linalg.norm(v_t, axis=1)[:, np.newaxis]
66
+ self.gradient_constraint_store.append(np.hstack([self.support.barycentre[element_indices],v_t]))
67
+ A1 = np.einsum("ij,ijk->ik", v_t, t_g)
68
+ volume = self.support.element_size[element_indices]
69
+ A1 = A1 / volume[:, np.newaxis] # normalise by element size
70
+
71
+ b = np.zeros(A1.shape[0]) + self.norm_length
72
+ b = b / volume # normalise by element size
73
+ idc = np.hstack(
74
+ [
75
+ self.support.elements[elements],
76
+ ]
77
+ )
78
+ self.interpolator.add_constraints_to_least_squares(A1, b, idc, w=w, name="constant norm")
79
+
80
+ def solve_system(
81
+ self,
82
+ solver: Optional[Union[Callable[[sparse.csr_matrix, np.ndarray], np.ndarray], str]] = None,
83
+ tol: Optional[float] = None,
84
+ solver_kwargs: dict = {},
85
+ ) -> bool:
86
+ """Solve the system of equations iteratively for the constant norm interpolator.
87
+
88
+ Parameters
89
+ ----------
90
+ solver : Optional[Union[Callable[[sparse.csr_matrix, np.ndarray], np.ndarray], str]], optional
91
+ Solver function or name, by default None
92
+ tol : Optional[float], optional
93
+ Tolerance for the solver, by default None
94
+ solver_kwargs : dict, optional
95
+ Additional arguments for the solver, by default {}
96
+
97
+ Returns
98
+ -------
99
+ bool
100
+ Success status of the solver
101
+ """
102
+ success = True
103
+ for i in range(self.n_iterations):
104
+ if i > 0:
105
+ self.add_constant_norm(w=(0.1 * i) ** 2 + 0.01)
106
+ # Ensure the interpolator is cast to P1Interpolator before calling solve_system
107
+ if isinstance(self.interpolator, self.basetype):
108
+ success = self.basetype.solve_system(self.interpolator, solver=solver, tol=tol, solver_kwargs=solver_kwargs)
109
+ if self.store_solution_history:
110
+
111
+ self.solution_history.append(self.interpolator.c)
112
+ else:
113
+ raise TypeError("self.interpolator is not an instance of P1Interpolator")
114
+ if not success:
115
+ break
116
+ return success
117
+
118
+ class ConstantNormP1Interpolator(P1Interpolator, ConstantNormInterpolator):
119
+ """Constant norm interpolator using P1 base interpolator
120
+
121
+ Parameters
122
+ ----------
123
+ P1Interpolator : class
124
+ The P1Interpolator class.
125
+ ConstantNormInterpolator : class
126
+ The ConstantNormInterpolator class.
127
+ """
128
+ def __init__(self, support):
129
+ """Initialise the constant norm P1 interpolator.
130
+
131
+ Parameters
132
+ ----------
133
+ support : _type_
134
+ _description_
135
+ """
136
+ P1Interpolator.__init__(self, support)
137
+ ConstantNormInterpolator.__init__(self, self, P1Interpolator)
138
+
139
+ def solve_system(
140
+ self,
141
+ solver: Optional[Union[Callable[[sparse.csr_matrix, np.ndarray], np.ndarray], str]] = None,
142
+ tol: Optional[float] = None,
143
+ solver_kwargs: dict = {},
144
+ ) -> bool:
145
+ """Solve the system of equations for the constant norm P1 interpolator.
146
+
147
+ Parameters
148
+ ----------
149
+ solver : Optional[Union[Callable[[sparse.csr_matrix, np.ndarray], np.ndarray], str]], optional
150
+ Solver function or name, by default None
151
+ tol : Optional[float], optional
152
+ Tolerance for the solver, by default None
153
+ solver_kwargs : dict, optional
154
+ Additional arguments for the solver, by default {}
155
+
156
+ Returns
157
+ -------
158
+ bool
159
+ Success status of the solver
160
+ """
161
+ return ConstantNormInterpolator.solve_system(self, solver=solver, tol=tol, solver_kwargs=solver_kwargs)
162
+
163
+ class ConstantNormFDIInterpolator(FiniteDifferenceInterpolator, ConstantNormInterpolator):
164
+ """Constant norm interpolator using finite difference base interpolator
165
+
166
+ Parameters
167
+ ----------
168
+ FiniteDifferenceInterpolator : class
169
+ The FiniteDifferenceInterpolator class.
170
+ ConstantNormInterpolator : class
171
+ The ConstantNormInterpolator class.
172
+ """
173
+ def __init__(self, support):
174
+ """Initialise the constant norm finite difference interpolator.
175
+
176
+ Parameters
177
+ ----------
178
+ support : _type_
179
+ _description_
180
+ """
181
+ FiniteDifferenceInterpolator.__init__(self, support)
182
+ ConstantNormInterpolator.__init__(self, self, FiniteDifferenceInterpolator)
183
+ def solve_system(
184
+ self,
185
+ solver: Optional[Union[Callable[[sparse.csr_matrix, np.ndarray], np.ndarray], str]] = None,
186
+ tol: Optional[float] = None,
187
+ solver_kwargs: dict = {},
188
+ ) -> bool:
189
+ """Solve the system of equations for the constant norm finite difference interpolator.
190
+
191
+ Parameters
192
+ ----------
193
+ solver : Optional[Union[Callable[[sparse.csr_matrix, np.ndarray], np.ndarray], str]], optional
194
+ Solver function or name, by default None
195
+ tol : Optional[float], optional
196
+ Tolerance for the solver, by default None
197
+ solver_kwargs : dict, optional
198
+ Additional arguments for the solver, by default {}
199
+
200
+ Returns
201
+ -------
202
+ bool
203
+ Success status of the solver
204
+ """
205
+ return ConstantNormInterpolator.solve_system(self, solver=solver, tol=tol, solver_kwargs=solver_kwargs)
@@ -42,7 +42,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
42
42
 
43
43
  self.shape = "rectangular"
44
44
  if self.shape == "square":
45
- self.B = np.zeros(self.nx)
45
+ self.B = np.zeros(self.dof)
46
46
  self.c_ = 0
47
47
 
48
48
  self.solver = "cg"
@@ -60,7 +60,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
60
60
  self.non_linear_constraints = []
61
61
  self.constraints = {}
62
62
  self.interpolation_weights = {}
63
- logger.info("Creating discrete interpolator with {} degrees of freedom".format(self.nx))
63
+ logger.info("Creating discrete interpolator with {} degrees of freedom".format(self.dof))
64
64
  self.type = InterpolatorType.BASE_DISCRETE
65
65
 
66
66
  def set_nelements(self, nelements: int) -> int:
@@ -78,7 +78,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
78
78
  return self.support.n_elements
79
79
 
80
80
  @property
81
- def nx(self) -> int:
81
+ def dof(self) -> int:
82
82
  """Number of degrees of freedom for the interpolator
83
83
 
84
84
  Returns
@@ -125,7 +125,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
125
125
  # self.region_function = region
126
126
  logger.info(
127
127
  "Cannot use region at the moment. Interpolation now uses region and has {} degrees of freedom".format(
128
- self.nx
128
+ self.dof
129
129
  )
130
130
  )
131
131
 
@@ -175,7 +175,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
175
175
  """
176
176
  self.constraints = {}
177
177
  self.c_ = 0
178
- self.regularisation_scale = np.ones(self.nx)
178
+ self.regularisation_scale = np.ones(self.dof)
179
179
  logger.info("Resetting interpolation constraints")
180
180
 
181
181
  def add_constraints_to_least_squares(self, A, B, idc, w=1.0, name="undefined"):
@@ -219,6 +219,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
219
219
  # normalise by rows of A
220
220
  # Should this be done? It should make the solution more stable
221
221
  length = np.linalg.norm(A, axis=1)
222
+ # length[length>0] = 1.
222
223
  B[length > 0] /= length[length > 0]
223
224
  # going to assume if any are nan they are all nan
224
225
  mask = np.any(np.isnan(A), axis=1)
@@ -245,7 +246,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
245
246
  rows = np.tile(rows, (A.shape[-1], 1)).T
246
247
  self.constraints[name] = {
247
248
  'matrix': sparse.coo_matrix(
248
- (A.flatten(), (rows.flatten(), idc.flatten())), shape=(n_rows, self.nx)
249
+ (A.flatten(), (rows.flatten(), idc.flatten())), shape=(n_rows, self.dof)
249
250
  ).tocsc(),
250
251
  'b': B.flatten(),
251
252
  'w': w,
@@ -286,7 +287,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
286
287
  A : numpy array
287
288
  matrix of coefficients
288
289
  bounds : numpy array
289
- nx3 lower, upper, 1
290
+ n*3 lower, upper, 1
290
291
  idc : numpy array
291
292
  index of constraints in the matrix
292
293
  Returns
@@ -296,14 +297,14 @@ class DiscreteInterpolator(GeologicalInterpolator):
296
297
  # map from mesh node index to region node index
297
298
  gi = np.zeros(self.support.n_nodes, dtype=int)
298
299
  gi[:] = -1
299
- gi[self.region] = np.arange(0, self.nx, dtype=int)
300
+ gi[self.region] = np.arange(0, self.dof, dtype=int)
300
301
  idc = gi[idc]
301
302
  rows = np.arange(0, idc.shape[0])
302
303
  rows = np.tile(rows, (A.shape[-1], 1)).T
303
304
 
304
305
  self.ineq_constraints[name] = {
305
306
  'matrix': sparse.coo_matrix(
306
- (A.flatten(), (rows.flatten(), idc.flatten())), shape=(rows.shape[0], self.nx)
307
+ (A.flatten(), (rows.flatten(), idc.flatten())), shape=(rows.shape[0], self.dof)
307
308
  ).tocsc(),
308
309
  "bounds": bounds,
309
310
  }
@@ -431,7 +432,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
431
432
  np.ones((value.shape[0], 1)),
432
433
  l,
433
434
  u,
434
- np.arange(0, self.nx, dtype=int),
435
+ np.arange(0, self.dof, dtype=int),
435
436
  )
436
437
 
437
438
  def add_equality_constraints(self, node_idx, values, name="undefined"):
@@ -454,7 +455,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
454
455
  # map from mesh node index to region node index
455
456
  gi = np.zeros(self.support.n_nodes)
456
457
  gi[:] = -1
457
- gi[self.region] = np.arange(0, self.nx)
458
+ gi[self.region] = np.arange(0, self.dof)
458
459
  idc = gi[node_idx]
459
460
  outside = ~(idc == -1)
460
461
 
@@ -515,7 +516,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
515
516
  if len(self.equal_constraints) > 0:
516
517
  ATA = A.T.dot(A)
517
518
  ATB = A.T.dot(B)
518
- logger.info(f"Equality block is {self.eq_const_c} x {self.nx}")
519
+ logger.info(f"Equality block is {self.eq_const_c} x {self.dof}")
519
520
  # solving constrained least squares using
520
521
  # | ATA CT | |c| = b
521
522
  # | C 0 | |y| d
@@ -540,7 +541,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
540
541
 
541
542
  C = sparse.coo_matrix(
542
543
  (np.array(a), (np.array(rows), cols)),
543
- shape=(self.eq_const_c, self.nx),
544
+ shape=(self.eq_const_c, self.dof),
544
545
  dtype=float,
545
546
  ).tocsr()
546
547
 
@@ -557,7 +558,7 @@ class DiscreteInterpolator(GeologicalInterpolator):
557
558
  mats.append(c['matrix'])
558
559
  bounds.append(c['bounds'])
559
560
  if len(mats) == 0:
560
- return sparse.csr_matrix((0, self.nx), dtype=float), np.zeros((0, 3))
561
+ return sparse.csr_matrix((0, self.dof), dtype=float), np.zeros((0, 3))
561
562
  Q = sparse.vstack(mats)
562
563
  bounds = np.vstack(bounds)
563
564
  return Q, bounds