LoopStructural 1.6.2__tar.gz → 1.6.5__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.2 → loopstructural-1.6.5}/LoopStructural/datatypes/_bounding_box.py +19 -4
  2. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datatypes/_point.py +36 -2
  3. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datatypes/_structured_grid.py +17 -0
  4. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datatypes/_surface.py +17 -0
  5. loopstructural-1.6.5/LoopStructural/export/omf_wrapper.py +116 -0
  6. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/__init__.py +13 -0
  7. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_api.py +81 -13
  8. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_discrete_fold_interpolator.py +11 -4
  9. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_discrete_interpolator.py +100 -53
  10. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_finite_difference_interpolator.py +68 -78
  11. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_geological_interpolator.py +27 -10
  12. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_p1interpolator.py +3 -3
  13. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_surfe_wrapper.py +42 -12
  14. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_2d_base_unstructured.py +16 -0
  15. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_2d_structured_grid.py +44 -9
  16. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_3d_base_structured.py +24 -7
  17. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_3d_structured_grid.py +38 -12
  18. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_3d_structured_tetra.py +7 -3
  19. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +8 -2
  20. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/__init__.py +7 -0
  21. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_base_support.py +7 -0
  22. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/__init__.py +1 -3
  23. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/core/geological_model.py +2 -4
  24. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_analytical_feature.py +25 -16
  25. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_base_geological_feature.py +21 -8
  26. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_geological_feature.py +47 -11
  27. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_structural_frame.py +10 -18
  28. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_unconformity_feature.py +3 -3
  29. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/builders/_base_builder.py +8 -0
  30. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/builders/_folded_feature_builder.py +45 -14
  31. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/builders/_geological_feature_builder.py +29 -13
  32. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/builders/_structural_frame_builder.py +5 -0
  33. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fault/__init__.py +1 -1
  34. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fault/_fault_function.py +19 -1
  35. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fault/_fault_segment.py +40 -51
  36. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fold/__init__.py +1 -2
  37. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +0 -23
  38. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fold/_foldframe.py +4 -4
  39. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fold/_svariogram.py +81 -46
  40. loopstructural-1.6.5/LoopStructural/modelling/features/fold/fold_function/__init__.py +27 -0
  41. loopstructural-1.6.5/LoopStructural/modelling/features/fold/fold_function/_base_fold_rotation_angle.py +253 -0
  42. loopstructural-1.6.5/LoopStructural/modelling/features/fold/fold_function/_fourier_series_fold_rotation_angle.py +153 -0
  43. loopstructural-1.6.5/LoopStructural/modelling/features/fold/fold_function/_lambda_fold_rotation_angle.py +46 -0
  44. loopstructural-1.6.5/LoopStructural/modelling/features/fold/fold_function/_trigo_fold_rotation_angle.py +151 -0
  45. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/input/process_data.py +47 -26
  46. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/input/project_file.py +49 -23
  47. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/__init__.py +1 -0
  48. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/_surface.py +11 -4
  49. loopstructural-1.6.5/LoopStructural/utils/colours.py +26 -0
  50. loopstructural-1.6.5/LoopStructural/utils/features.py +5 -0
  51. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/maths.py +51 -0
  52. loopstructural-1.6.5/LoopStructural/version.py +1 -0
  53. loopstructural-1.6.5/LoopStructural.egg-info/PKG-INFO +146 -0
  54. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural.egg-info/SOURCES.txt +7 -2
  55. loopstructural-1.6.5/PKG-INFO +146 -0
  56. loopstructural-1.6.5/README.md +99 -0
  57. loopstructural-1.6.2/LoopStructural/export/omf_wrapper.py +0 -88
  58. loopstructural-1.6.2/LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  59. loopstructural-1.6.2/LoopStructural/modelling/features/fold/_fold_rotation_angle.py +0 -149
  60. loopstructural-1.6.2/LoopStructural/version.py +0 -1
  61. loopstructural-1.6.2/LoopStructural.egg-info/PKG-INFO +0 -81
  62. loopstructural-1.6.2/PKG-INFO +0 -81
  63. loopstructural-1.6.2/README.md +0 -34
  64. {loopstructural-1.6.2 → loopstructural-1.6.5}/LICENSE +0 -0
  65. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/__init__.py +0 -0
  66. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/__init__.py +0 -0
  67. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/_base.py +0 -0
  68. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/_example_models.py +0 -0
  69. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/claudius.csv +0 -0
  70. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/claudiusbb.txt +0 -0
  71. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/duplex.csv +0 -0
  72. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/duplexbb.txt +0 -0
  73. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/fault_trace/fault_trace.cpg +0 -0
  74. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  75. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/fault_trace/fault_trace.prj +0 -0
  76. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  77. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  78. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/bbox.csv +0 -0
  79. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/contacts.csv +0 -0
  80. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +0 -0
  81. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/fault_edges.txt +0 -0
  82. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/fault_locations.csv +0 -0
  83. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +0 -0
  84. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +0 -0
  85. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +0 -0
  86. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +0 -0
  87. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/intrusion.csv +0 -0
  88. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/intrusionbb.txt +0 -0
  89. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/onefoldbb.txt +0 -0
  90. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/onefolddata.csv +0 -0
  91. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/refolded_bb.txt +0 -0
  92. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/refolded_fold.csv +0 -0
  93. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datasets/data/tabular_intrusion.csv +0 -0
  94. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/datatypes/__init__.py +0 -0
  95. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/export/exporters.py +0 -0
  96. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/export/file_formats.py +0 -0
  97. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/export/geoh5.py +0 -0
  98. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/export/gocad.py +0 -0
  99. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_builders.py +0 -0
  100. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_cython/__init__.py +0 -0
  101. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_interpolator_factory.py +0 -0
  102. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_operator.py +0 -0
  103. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/_p2interpolator.py +0 -0
  104. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_2d_p1_unstructured.py +0 -0
  105. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_2d_p2_unstructured.py +0 -0
  106. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  107. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_3d_p2_tetra.py +0 -0
  108. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_aabb.py +0 -0
  109. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_face_table.py +0 -0
  110. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/interpolators/supports/_support_factory.py +0 -0
  111. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/core/__init__.py +0 -0
  112. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/__init__.py +0 -0
  113. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_cross_product_geological_feature.py +0 -0
  114. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_lambda_geological_feature.py +0 -0
  115. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/_region.py +0 -0
  116. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/builders/__init__.py +0 -0
  117. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/builders/_fault_builder.py +0 -0
  118. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fault/_fault_function_feature.py +0 -0
  119. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/features/fold/_fold.py +0 -0
  120. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/input/__init__.py +0 -0
  121. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/input/fault_network.py +0 -0
  122. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/input/map2loop_processor.py +0 -0
  123. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/__init__.py +0 -0
  124. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/geom_conceptual_models.py +0 -0
  125. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/geometric_scaling_functions.py +0 -0
  126. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/intrusion_builder.py +0 -0
  127. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/intrusion_feature.py +0 -0
  128. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/intrusion_frame_builder.py +0 -0
  129. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/modelling/intrusions/intrusion_support_functions.py +0 -0
  130. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/_transformation.py +0 -0
  131. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/config.py +0 -0
  132. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/dtm_creator.py +0 -0
  133. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/exceptions.py +0 -0
  134. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/helper.py +0 -0
  135. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/json_encoder.py +0 -0
  136. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/linalg.py +0 -0
  137. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/logging.py +0 -0
  138. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/regions.py +0 -0
  139. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/typing.py +0 -0
  140. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/utils/utils.py +0 -0
  141. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural/visualisation/__init__.py +0 -0
  142. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural.egg-info/dependency_links.txt +0 -0
  143. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural.egg-info/requires.txt +0 -0
  144. {loopstructural-1.6.2 → loopstructural-1.6.5}/LoopStructural.egg-info/top_level.txt +0 -0
  145. {loopstructural-1.6.2 → loopstructural-1.6.5}/pyproject.toml +0 -0
  146. {loopstructural-1.6.2 → loopstructural-1.6.5}/setup.cfg +0 -0
  147. {loopstructural-1.6.2 → loopstructural-1.6.5}/setup.py +0 -0
@@ -4,6 +4,7 @@ from LoopStructural.utils.exceptions import LoopValueError
4
4
  from LoopStructural.utils import rng
5
5
  from LoopStructural.datatypes._structured_grid import StructuredGrid
6
6
  import numpy as np
7
+ import copy
7
8
 
8
9
  from LoopStructural.utils.logging import getLogger
9
10
 
@@ -147,7 +148,13 @@ class BoundingBox:
147
148
  ele_vol = box_vol / nelements
148
149
  # calculate the step vector of a regular cube
149
150
  step_vector = np.zeros(self.dimensions)
150
- step_vector[:] = ele_vol ** (1.0 / 3.0)
151
+ if self.dimensions == 2:
152
+ step_vector[:] = ele_vol ** (1.0 / 2.0)
153
+ elif self.dimensions == 3:
154
+ step_vector[:] = ele_vol ** (1.0 / 3.0)
155
+ else:
156
+ logger.warning("Can only set nelements for 2d or 3D bounding box")
157
+ return
151
158
  # number of steps is the length of the box / step vector
152
159
  nsteps = np.ceil((self.maximum - self.origin) / step_vector).astype(int)
153
160
  self.nsteps = nsteps
@@ -269,7 +276,10 @@ class BoundingBox:
269
276
  origin = self.origin - buffer * (self.maximum - self.origin)
270
277
  maximum = self.maximum + buffer * (self.maximum - self.origin)
271
278
  return BoundingBox(
272
- origin=origin, maximum=maximum, global_origin=self.global_origin + origin
279
+ origin=origin,
280
+ maximum=maximum,
281
+ global_origin=self.global_origin + origin,
282
+ dimensions=self.dimensions,
273
283
  )
274
284
 
275
285
  def get_value(self, name):
@@ -412,11 +422,16 @@ class BoundingBox:
412
422
  def structured_grid(
413
423
  self, cell_data: Dict[str, np.ndarray] = {}, vertex_data={}, name: str = "bounding_box"
414
424
  ):
425
+ # python is passing a reference to the cell_data, vertex_data dicts so we need to
426
+ # copy them to make sure that different instances of StructuredGrid are not sharing the same
427
+ # underlying objects
428
+ _cell_data = copy.deepcopy(cell_data)
429
+ _vertex_data = copy.deepcopy(vertex_data)
415
430
  return StructuredGrid(
416
431
  origin=self.global_origin,
417
432
  step_vector=self.step_vector,
418
433
  nsteps=self.nsteps,
419
- cell_properties=cell_data,
420
- properties=vertex_data,
434
+ cell_properties=_cell_data,
435
+ properties=_vertex_data,
421
436
  name=name,
422
437
  )
@@ -3,6 +3,9 @@ import numpy as np
3
3
 
4
4
  from typing import Optional, Union
5
5
  import io
6
+ from LoopStructural.utils import getLogger
7
+
8
+ logger = getLogger(__name__)
6
9
 
7
10
 
8
11
  @dataclass
@@ -29,6 +32,20 @@ class ValuePoints:
29
32
  points["values"] = self.values
30
33
  return points
31
34
 
35
+ def plot(self, pyvista_kwargs={}):
36
+ """Calls pyvista plot on the vtk object
37
+
38
+ Parameters
39
+ ----------
40
+ pyvista_kwargs : dict, optional
41
+ kwargs passed to pyvista.DataSet.plot(), by default {}
42
+ """
43
+ try:
44
+ self.vtk().plot(**pyvista_kwargs)
45
+ return
46
+ except ImportError:
47
+ logger.error("pyvista is required for vtk")
48
+
32
49
  def save(self, filename: Union[str, io.StringIO], ext=None):
33
50
  if isinstance(filename, io.StringIO):
34
51
  if ext is None:
@@ -106,12 +123,15 @@ class VectorPoints:
106
123
  def from_dict(self, d):
107
124
  return VectorPoints(d['locations'], d['vectors'], d['name'], d.get('properties', None))
108
125
 
109
- def vtk(self, geom='arrow', scale=1.0, scale_function=None, tolerance=0.05):
126
+ def vtk(self, geom='arrow', scale=1.0, scale_function=None, normalise=True, tolerance=0.05):
110
127
  import pyvista as pv
111
128
 
112
129
  vectors = np.copy(self.vectors)
130
+ if normalise:
131
+ norm = np.linalg.norm(vectors, axis=1)
132
+ vectors[norm > 0, :] /= norm[norm > 0][:, None]
113
133
  if scale_function is not None:
114
- vectors /= np.linalg.norm(vectors, axis=1)[:, None]
134
+ # vectors /= np.linalg.norm(vectors, axis=1)[:, None]
115
135
  vectors *= scale_function(self.locations)[:, None]
116
136
  points = pv.PolyData(self.locations)
117
137
  points.point_data.set_vectors(vectors, 'vectors')
@@ -123,6 +143,20 @@ class VectorPoints:
123
143
  # Perform the glyph
124
144
  return points.glyph(orient="vectors", geom=geom, tolerance=tolerance)
125
145
 
146
+ def plot(self, pyvista_kwargs={}):
147
+ """Calls pyvista plot on the vtk object
148
+
149
+ Parameters
150
+ ----------
151
+ pyvista_kwargs : dict, optional
152
+ kwargs passed to pyvista.DataSet.plot(), by default {}
153
+ """
154
+ try:
155
+ self.vtk().plot(**pyvista_kwargs)
156
+ return
157
+ except ImportError:
158
+ logger.error("pyvista is required for vtk")
159
+
126
160
  def save(self, filename):
127
161
  filename = str(filename)
128
162
  ext = filename.split('.')[-1]
@@ -1,6 +1,9 @@
1
1
  from typing import Dict
2
2
  import numpy as np
3
3
  from dataclasses import dataclass
4
+ from LoopStructural.utils import getLogger
5
+
6
+ logger = getLogger(__name__)
4
7
 
5
8
 
6
9
  @dataclass
@@ -46,6 +49,20 @@ class StructuredGrid:
46
49
  grid.cell_data[name] = data.flatten(order="F")
47
50
  return grid
48
51
 
52
+ def plot(self, pyvista_kwargs={}):
53
+ """Calls pyvista plot on the vtk object
54
+
55
+ Parameters
56
+ ----------
57
+ pyvista_kwargs : dict, optional
58
+ kwargs passed to pyvista.DataSet.plot(), by default {}
59
+ """
60
+ try:
61
+ self.vtk().plot(**pyvista_kwargs)
62
+ return
63
+ except ImportError:
64
+ logger.error("pyvista is required for vtk")
65
+
49
66
  def merge(self, other):
50
67
  if not np.all(np.isclose(self.origin, other.origin)):
51
68
  raise ValueError("Origin of grids must be the same")
@@ -2,6 +2,9 @@ from dataclasses import dataclass
2
2
  from typing import Optional
3
3
  import numpy as np
4
4
  import io
5
+ from LoopStructural.utils import getLogger
6
+
7
+ logger = getLogger(__name__)
5
8
 
6
9
 
7
10
  @dataclass
@@ -85,6 +88,20 @@ class Surface:
85
88
  surface.cell_data[k] = np.array(v)
86
89
  return surface
87
90
 
91
+ def plot(self, pyvista_kwargs={}):
92
+ """Calls pyvista plot on the vtk object
93
+
94
+ Parameters
95
+ ----------
96
+ pyvista_kwargs : dict, optional
97
+ kwargs passed to pyvista.DataSet.plot(), by default {}
98
+ """
99
+ try:
100
+ self.vtk().plot(**pyvista_kwargs)
101
+ return
102
+ except ImportError:
103
+ logger.error("pyvista is required for vtk")
104
+
88
105
  def to_dict(self, flatten=False):
89
106
  triangles = self.triangles
90
107
  vertices = self.vertices
@@ -0,0 +1,116 @@
1
+ try:
2
+ import omf
3
+ except ImportError:
4
+ raise ImportError(
5
+ "You need to install the omf package to use this feature. "
6
+ "You can install it with: pip install mira-omf"
7
+ )
8
+ import numpy as np
9
+ import datetime
10
+ import os
11
+
12
+
13
+ def get_project(filename):
14
+ if os.path.exists(filename):
15
+
16
+ try:
17
+ reader = omf.OMFReader(filename)
18
+ project = reader.get_project()
19
+ except (FileNotFoundError, ValueError):
20
+ project = omf.Project(name='LoopStructural Model')
21
+ return project
22
+ else:
23
+ return omf.Project(name='LoopStructural Model')
24
+
25
+
26
+ def get_cell_attributes(loopobject):
27
+ attributes = []
28
+ if loopobject.cell_properties:
29
+ for k, v in loopobject.cell_properties.items():
30
+ v = np.array(v)
31
+ if len(v.shape) > 1 and v.shape[1] > 1:
32
+ for i in range(v.shape[1]):
33
+ attributes.append(
34
+ omf.ScalarData(name=f'{k}_{i}', array=v[:, i], location="faces")
35
+ )
36
+ else:
37
+ attributes.append(omf.ScalarData(name=k, array=v, location="faces"))
38
+
39
+ return attributes
40
+
41
+
42
+ def get_point_attributed(loopobject):
43
+ attributes = []
44
+ if loopobject.properties:
45
+ for k, v in loopobject.properties.items():
46
+ v = np.array(v)
47
+ if len(v.shape) > 1 and v.shape[1] > 1:
48
+ for i in range(v.shape[1]):
49
+ attributes.append(
50
+ omf.ScalarData(name=f'{k}_{i}', array=v[:, i], location="vertices")
51
+ )
52
+ else:
53
+ attributes.append(omf.ScalarData(name=k, array=v, location="vertices"))
54
+
55
+ return attributes
56
+
57
+
58
+ def add_surface_to_omf(surface, filename):
59
+
60
+ attributes = []
61
+ attributes += get_cell_attributes(surface)
62
+ attributes += get_point_attributed(surface)
63
+ surface = omf.SurfaceElement(
64
+ geometry=omf.SurfaceGeometry(
65
+ vertices=surface.vertices,
66
+ triangles=surface.triangles,
67
+ ),
68
+ data=attributes,
69
+ name=surface.name,
70
+ )
71
+ project = get_project(filename)
72
+
73
+ project.elements += [surface]
74
+ project.metadata = {
75
+ "coordinate_reference_system": "epsg 3857",
76
+ "date_created": datetime.datetime.utcnow(),
77
+ "version": "v1.3",
78
+ "revision": "10",
79
+ }
80
+ omf.OMFWriter(project, filename)
81
+
82
+
83
+ def add_pointset_to_omf(points, filename):
84
+
85
+ attributes = []
86
+ attributes += get_point_attributed(points)
87
+
88
+ points = omf.PointSetElement(
89
+ vertices=points.locations,
90
+ attributes=attributes,
91
+ name=points.name,
92
+ )
93
+
94
+ project = get_project(filename)
95
+ project.elements += [points]
96
+ omf.OMFWriter(project, filename)
97
+
98
+
99
+ def add_structured_grid_to_omf(grid, filename):
100
+ print('Open Mining Format cannot store structured grids')
101
+ return
102
+ # attributes = []
103
+ # attributes += get_cell_attributes(grid)
104
+ # attributes += get_point_attributed(grid)
105
+
106
+ # vol = omf.TensorGridBlockModel(
107
+ # name=grid.name,
108
+ # tensor_u=np.ones(grid.nsteps[0]).astype(float),
109
+ # tensor_v=np.ones(grid.nsteps[0]).astype(float),
110
+ # tensor_w=np.ones(grid.nsteps[0]).astype(float),
111
+ # origin=grid.origin,
112
+ # attributes=attributes,
113
+ # )
114
+ # project = get_project(filename)
115
+ # project.elements += [vol]
116
+ # omf.save(project, filename, mode='w')
@@ -52,6 +52,7 @@ interpolator_string_map = {
52
52
  "P2": InterpolatorType.PIECEWISE_QUADRATIC,
53
53
  "P1": InterpolatorType.PIECEWISE_LINEAR,
54
54
  "DFI": InterpolatorType.DISCRETE_FOLD,
55
+ 'surfe': InterpolatorType.SURFE,
55
56
  }
56
57
  from ..interpolators._geological_interpolator import GeologicalInterpolator
57
58
  from ..interpolators._discrete_interpolator import DiscreteInterpolator
@@ -79,6 +80,11 @@ from ..interpolators._discrete_fold_interpolator import (
79
80
  from ..interpolators._p2interpolator import P2Interpolator
80
81
  from ..interpolators._p1interpolator import P1Interpolator
81
82
 
83
+ try:
84
+ from ..interpolators._surfe_wrapper import SurfeRBFInterpolator
85
+ except ImportError:
86
+ logger.warning("Surfe is not installed, SurfeRBFInterpolator will not be available")
87
+ SurfeRBFInterpolator = None
82
88
  interpolator_map = {
83
89
  InterpolatorType.BASE: GeologicalInterpolator,
84
90
  InterpolatorType.BASE_DISCRETE: DiscreteInterpolator,
@@ -87,6 +93,7 @@ interpolator_map = {
87
93
  InterpolatorType.PIECEWISE_LINEAR: P1Interpolator,
88
94
  InterpolatorType.PIECEWISE_QUADRATIC: P2Interpolator,
89
95
  InterpolatorType.BASE_DATA_SUPPORTED: GeologicalInterpolator,
96
+ InterpolatorType.SURFE: SurfeRBFInterpolator,
90
97
  }
91
98
 
92
99
  support_interpolator_map = {
@@ -100,6 +107,12 @@ support_interpolator_map = {
100
107
  3: SupportType.P2UnstructuredTetMesh,
101
108
  2: SupportType.P2Unstructured2d,
102
109
  },
110
+ InterpolatorType.SURFE: {
111
+ 3: SupportType.DataSupported,
112
+ 2: SupportType.DataSupported,
113
+ },
103
114
  }
104
115
 
105
116
  from ._interpolator_factory import InterpolatorFactory
117
+
118
+ # from ._api import LoopInterpolator
@@ -19,6 +19,8 @@ class LoopInterpolator:
19
19
  dimensions: int = 3,
20
20
  type=InterpolatorType.FINITE_DIFFERENCE,
21
21
  nelements: int = 1000,
22
+ interpolator_setup_kwargs={},
23
+ buffer: float = 0.2,
22
24
  ):
23
25
  """Scikitlearn like interface for LoopStructural interpolators
24
26
  useful for quickly building an interpolator to apply to a dataset
@@ -37,21 +39,22 @@ class LoopInterpolator:
37
39
  nelements : int, optional
38
40
  degrees of freedom for interpolator, by default 1000
39
41
  """
42
+ logger.warning("LoopInterpolator is experimental and the API is subject to change")
40
43
  self.dimensions = dimensions
41
44
  self.type = "FDI"
42
45
  self.bounding_box = bounding_box
43
46
  self.interpolator: GeologicalInterpolator = InterpolatorFactory.create_interpolator(
44
- type,
45
- bounding_box,
46
- nelements,
47
+ type, bounding_box, nelements, buffer=buffer
47
48
  )
49
+ self.interpolator_setup_kwargs = interpolator_setup_kwargs
48
50
 
49
51
  def fit(
50
52
  self,
51
53
  values: Optional[np.ndarray] = None,
52
54
  tangent_vectors: Optional[np.ndarray] = None,
53
55
  normal_vectors: Optional[np.ndarray] = None,
54
- inequality_constraints: Optional[np.ndarray] = None,
56
+ inequality_value_constraints: Optional[np.ndarray] = None,
57
+ inequality_pairs_constraints: Optional[np.ndarray] = None,
55
58
  ):
56
59
  """_summary_
57
60
 
@@ -66,16 +69,18 @@ class LoopInterpolator:
66
69
  inequality_constraints : Optional[np.ndarray], optional
67
70
  _description_, by default None
68
71
  """
72
+
69
73
  if values is not None:
70
74
  self.interpolator.set_value_constraints(values)
71
75
  if tangent_vectors is not None:
72
76
  self.interpolator.set_tangent_constraints(tangent_vectors)
73
77
  if normal_vectors is not None:
74
78
  self.interpolator.set_normal_constraints(normal_vectors)
75
- if inequality_constraints:
76
- pass
77
-
78
- self.interpolator.setup()
79
+ if inequality_value_constraints is not None:
80
+ self.interpolator.set_value_inequality_constraints(inequality_value_constraints)
81
+ if inequality_pairs_constraints is not None:
82
+ self.interpolator.set_inequality_pairs_constraints(inequality_pairs_constraints)
83
+ self.interpolator.setup(**self.interpolator_setup_kwargs)
79
84
 
80
85
  def evaluate_scalar_value(self, locations: np.ndarray) -> np.ndarray:
81
86
  """Evaluate the value of the interpolator at locations
@@ -114,30 +119,93 @@ class LoopInterpolator:
114
119
  values: Optional[np.ndarray] = None,
115
120
  tangent_vectors: Optional[np.ndarray] = None,
116
121
  normal_vectors: Optional[np.ndarray] = None,
117
- inequality_constraints: Optional[np.ndarray] = None,
122
+ inequality_value_constraints: Optional[np.ndarray] = None,
123
+ inequality_pairs_constraints: Optional[np.ndarray] = None,
118
124
  ):
119
125
  # get locations
120
126
  self.fit(
121
127
  values=values,
122
128
  tangent_vectors=tangent_vectors,
123
129
  normal_vectors=normal_vectors,
124
- inequality_constraints=inequality_constraints,
130
+ inequality_value_constraints=inequality_value_constraints,
131
+ inequality_pairs_constraints=inequality_pairs_constraints,
125
132
  )
126
133
  locations = self.interpolator.get_data_locations()
127
- return self.evalute_scalar_value(locations)
134
+ return self.evaluate_scalar_value(locations)
128
135
 
129
136
  def fit_and_evaluate_gradient(
130
137
  self,
131
138
  values: Optional[np.ndarray] = None,
132
139
  tangent_vectors: Optional[np.ndarray] = None,
133
140
  normal_vectors: Optional[np.ndarray] = None,
134
- inequality_constraints: Optional[np.ndarray] = None,
141
+ inequality_value_constraints: Optional[np.ndarray] = None,
142
+ inequality_pairs_constraints: Optional[np.ndarray] = None,
135
143
  ):
136
144
  self.fit(
137
145
  values=values,
138
146
  tangent_vectors=tangent_vectors,
139
147
  normal_vectors=normal_vectors,
140
- inequality_constraints=inequality_constraints,
148
+ inequality_value_constraints=inequality_value_constraints,
149
+ inequality_pairs_constraints=inequality_pairs_constraints,
141
150
  )
142
151
  locations = self.interpolator.get_data_locations()
143
152
  return self.evaluate_gradient(locations)
153
+
154
+ def fit_and_evaluate_value_and_gradient(
155
+ self,
156
+ values: Optional[np.ndarray] = None,
157
+ tangent_vectors: Optional[np.ndarray] = None,
158
+ normal_vectors: Optional[np.ndarray] = None,
159
+ inequality_value_constraints: Optional[np.ndarray] = None,
160
+ inequality_pairs_constraints: Optional[np.ndarray] = None,
161
+ ):
162
+ self.fit(
163
+ values=values,
164
+ tangent_vectors=tangent_vectors,
165
+ normal_vectors=normal_vectors,
166
+ inequality_value_constraints=inequality_value_constraints,
167
+ inequality_pairs_constraints=inequality_pairs_constraints,
168
+ )
169
+ locations = self.interpolator.get_data_locations()
170
+ return self.evaluate_scalar_value(locations), self.evaluate_gradient(locations)
171
+
172
+ def plot(self, ax=None, **kwargs):
173
+ """Plots a 2d map scalar field or 3d pyvista plot
174
+
175
+ Parameters
176
+ ----------
177
+ ax : matplotlib axes, optional
178
+ The axes you want to add the plot to, otherwise it will make one, by default None
179
+
180
+ Returns
181
+ -------
182
+ _type_
183
+ _description_
184
+ """
185
+ if self.dimensions == 3:
186
+ vtkgrid = self.interpolator.support.vtk()
187
+ vtkgrid['val'] = self.interpolator.c
188
+ vtkgrid.plot(**kwargs)
189
+ return vtkgrid
190
+ elif self.dimensions == 2:
191
+ if ax is None:
192
+ import matplotlib.pyplot as plt
193
+
194
+ fig, ax = plt.subplots()
195
+ val = self.interpolator.c
196
+ val = np.rot90(val.reshape(self.interpolator.support.nsteps, order='F'), 3)
197
+ ax.imshow(
198
+ val,
199
+ origin='lower',
200
+ extent=[
201
+ self.bounding_box.origin[0],
202
+ self.bounding_box.maximum[0],
203
+ self.bounding_box.origin[1],
204
+ self.bounding_box.maximum[1],
205
+ ],
206
+ **kwargs,
207
+ )
208
+ return val, ax
209
+
210
+ # def isovalue(self, value: float, **kwargs):
211
+ # self.interpolator.isovalue(value, **kwargs)
@@ -2,7 +2,7 @@
2
2
  Piecewise linear interpolator using folds
3
3
  """
4
4
 
5
- from typing import Optional
5
+ from typing import Optional, Callable
6
6
 
7
7
  import numpy as np
8
8
 
@@ -64,6 +64,7 @@ class DiscreteFoldInterpolator(PiecewiseLinearInterpolator):
64
64
  fold_normalisation=1.0,
65
65
  fold_norm=1.0,
66
66
  step=2,
67
+ mask_fn: Optional[Callable] = None,
67
68
  ):
68
69
  """
69
70
 
@@ -104,6 +105,10 @@ class DiscreteFoldInterpolator(PiecewiseLinearInterpolator):
104
105
  # calculate element volume for weighting
105
106
  vecs = nodes[:, 1:, :] - nodes[:, 0, None, :]
106
107
  vol = np.abs(np.linalg.det(vecs)) / 6
108
+ weight = np.ones(self.support.n_elements, dtype=float)
109
+ if mask_fn is not None:
110
+ weight[mask_fn(self.support.barycentre)] = 0
111
+ weight = weight[::step]
107
112
  if fold_orientation is not None:
108
113
  """
109
114
  dot product between vector in deformed ori plane = 0
@@ -120,7 +125,7 @@ class DiscreteFoldInterpolator(PiecewiseLinearInterpolator):
120
125
  B = np.zeros(A.shape[0])
121
126
  idc = self.support.get_elements()[element_idx[::step], :]
122
127
  self.add_constraints_to_least_squares(
123
- A, B, idc, w=fold_orientation, name="fold orientation"
128
+ A, B, idc, w=weight * fold_orientation, name="fold orientation"
124
129
  )
125
130
 
126
131
  if fold_axis_w is not None:
@@ -139,7 +144,9 @@ class DiscreteFoldInterpolator(PiecewiseLinearInterpolator):
139
144
  B = np.zeros(A.shape[0]).tolist()
140
145
  idc = self.support.get_elements()[element_idx[::step], :]
141
146
 
142
- self.add_constraints_to_least_squares(A, B, idc, w=fold_axis_w, name="fold axis")
147
+ self.add_constraints_to_least_squares(
148
+ A, B, idc, w=weight * fold_axis_w, name="fold axis"
149
+ )
143
150
 
144
151
  if fold_normalisation is not None:
145
152
  """
@@ -160,7 +167,7 @@ class DiscreteFoldInterpolator(PiecewiseLinearInterpolator):
160
167
  idc = self.support.get_elements()[element_idx[::step], :]
161
168
 
162
169
  self.add_constraints_to_least_squares(
163
- A, B, idc, w=fold_normalisation, name="fold normalisation"
170
+ A, B, idc, w=weight * fold_normalisation, name="fold normalisation"
164
171
  )
165
172
 
166
173
  if fold_regularisation is not None: