LoopStructural 1.6.1__py3-none-any.whl

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 (129) hide show
  1. LoopStructural/__init__.py +52 -0
  2. LoopStructural/datasets/__init__.py +23 -0
  3. LoopStructural/datasets/_base.py +301 -0
  4. LoopStructural/datasets/_example_models.py +10 -0
  5. LoopStructural/datasets/data/claudius.csv +21049 -0
  6. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  7. LoopStructural/datasets/data/duplex.csv +126 -0
  8. LoopStructural/datasets/data/duplexbb.txt +2 -0
  9. LoopStructural/datasets/data/fault_trace/fault_trace.cpg +1 -0
  10. LoopStructural/datasets/data/fault_trace/fault_trace.dbf +0 -0
  11. LoopStructural/datasets/data/fault_trace/fault_trace.prj +1 -0
  12. LoopStructural/datasets/data/fault_trace/fault_trace.shp +0 -0
  13. LoopStructural/datasets/data/fault_trace/fault_trace.shx +0 -0
  14. LoopStructural/datasets/data/geological_map_data/bbox.csv +2 -0
  15. LoopStructural/datasets/data/geological_map_data/contacts.csv +657 -0
  16. LoopStructural/datasets/data/geological_map_data/fault_displacement.csv +7 -0
  17. LoopStructural/datasets/data/geological_map_data/fault_edges.txt +2 -0
  18. LoopStructural/datasets/data/geological_map_data/fault_locations.csv +79 -0
  19. LoopStructural/datasets/data/geological_map_data/fault_orientations.csv +19 -0
  20. LoopStructural/datasets/data/geological_map_data/stratigraphic_order.csv +13 -0
  21. LoopStructural/datasets/data/geological_map_data/stratigraphic_orientations.csv +207 -0
  22. LoopStructural/datasets/data/geological_map_data/stratigraphic_thickness.csv +13 -0
  23. LoopStructural/datasets/data/intrusion.csv +1017 -0
  24. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  25. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  26. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  27. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  28. LoopStructural/datasets/data/refolded_fold.csv +205 -0
  29. LoopStructural/datasets/data/tabular_intrusion.csv +23 -0
  30. LoopStructural/datatypes/__init__.py +4 -0
  31. LoopStructural/datatypes/_bounding_box.py +422 -0
  32. LoopStructural/datatypes/_point.py +166 -0
  33. LoopStructural/datatypes/_structured_grid.py +94 -0
  34. LoopStructural/datatypes/_surface.py +184 -0
  35. LoopStructural/export/exporters.py +554 -0
  36. LoopStructural/export/file_formats.py +15 -0
  37. LoopStructural/export/geoh5.py +100 -0
  38. LoopStructural/export/gocad.py +126 -0
  39. LoopStructural/export/omf_wrapper.py +88 -0
  40. LoopStructural/interpolators/__init__.py +105 -0
  41. LoopStructural/interpolators/_api.py +143 -0
  42. LoopStructural/interpolators/_builders.py +149 -0
  43. LoopStructural/interpolators/_cython/__init__.py +0 -0
  44. LoopStructural/interpolators/_discrete_fold_interpolator.py +183 -0
  45. LoopStructural/interpolators/_discrete_interpolator.py +692 -0
  46. LoopStructural/interpolators/_finite_difference_interpolator.py +470 -0
  47. LoopStructural/interpolators/_geological_interpolator.py +380 -0
  48. LoopStructural/interpolators/_interpolator_factory.py +89 -0
  49. LoopStructural/interpolators/_non_linear_discrete_interpolator.py +0 -0
  50. LoopStructural/interpolators/_operator.py +38 -0
  51. LoopStructural/interpolators/_p1interpolator.py +228 -0
  52. LoopStructural/interpolators/_p2interpolator.py +277 -0
  53. LoopStructural/interpolators/_surfe_wrapper.py +174 -0
  54. LoopStructural/interpolators/supports/_2d_base_unstructured.py +340 -0
  55. LoopStructural/interpolators/supports/_2d_p1_unstructured.py +68 -0
  56. LoopStructural/interpolators/supports/_2d_p2_unstructured.py +288 -0
  57. LoopStructural/interpolators/supports/_2d_structured_grid.py +462 -0
  58. LoopStructural/interpolators/supports/_2d_structured_tetra.py +0 -0
  59. LoopStructural/interpolators/supports/_3d_base_structured.py +467 -0
  60. LoopStructural/interpolators/supports/_3d_p2_tetra.py +331 -0
  61. LoopStructural/interpolators/supports/_3d_structured_grid.py +470 -0
  62. LoopStructural/interpolators/supports/_3d_structured_tetra.py +746 -0
  63. LoopStructural/interpolators/supports/_3d_unstructured_tetra.py +637 -0
  64. LoopStructural/interpolators/supports/__init__.py +55 -0
  65. LoopStructural/interpolators/supports/_aabb.py +77 -0
  66. LoopStructural/interpolators/supports/_base_support.py +114 -0
  67. LoopStructural/interpolators/supports/_face_table.py +70 -0
  68. LoopStructural/interpolators/supports/_support_factory.py +32 -0
  69. LoopStructural/modelling/__init__.py +29 -0
  70. LoopStructural/modelling/core/__init__.py +0 -0
  71. LoopStructural/modelling/core/geological_model.py +1867 -0
  72. LoopStructural/modelling/features/__init__.py +32 -0
  73. LoopStructural/modelling/features/_analytical_feature.py +79 -0
  74. LoopStructural/modelling/features/_base_geological_feature.py +364 -0
  75. LoopStructural/modelling/features/_cross_product_geological_feature.py +100 -0
  76. LoopStructural/modelling/features/_geological_feature.py +288 -0
  77. LoopStructural/modelling/features/_lambda_geological_feature.py +93 -0
  78. LoopStructural/modelling/features/_region.py +18 -0
  79. LoopStructural/modelling/features/_structural_frame.py +186 -0
  80. LoopStructural/modelling/features/_unconformity_feature.py +83 -0
  81. LoopStructural/modelling/features/builders/__init__.py +5 -0
  82. LoopStructural/modelling/features/builders/_base_builder.py +111 -0
  83. LoopStructural/modelling/features/builders/_fault_builder.py +590 -0
  84. LoopStructural/modelling/features/builders/_folded_feature_builder.py +129 -0
  85. LoopStructural/modelling/features/builders/_geological_feature_builder.py +543 -0
  86. LoopStructural/modelling/features/builders/_structural_frame_builder.py +237 -0
  87. LoopStructural/modelling/features/fault/__init__.py +3 -0
  88. LoopStructural/modelling/features/fault/_fault_function.py +444 -0
  89. LoopStructural/modelling/features/fault/_fault_function_feature.py +82 -0
  90. LoopStructural/modelling/features/fault/_fault_segment.py +505 -0
  91. LoopStructural/modelling/features/fold/__init__.py +9 -0
  92. LoopStructural/modelling/features/fold/_fold.py +167 -0
  93. LoopStructural/modelling/features/fold/_fold_rotation_angle.py +149 -0
  94. LoopStructural/modelling/features/fold/_fold_rotation_angle_feature.py +67 -0
  95. LoopStructural/modelling/features/fold/_foldframe.py +194 -0
  96. LoopStructural/modelling/features/fold/_svariogram.py +188 -0
  97. LoopStructural/modelling/input/__init__.py +2 -0
  98. LoopStructural/modelling/input/fault_network.py +80 -0
  99. LoopStructural/modelling/input/map2loop_processor.py +165 -0
  100. LoopStructural/modelling/input/process_data.py +650 -0
  101. LoopStructural/modelling/input/project_file.py +84 -0
  102. LoopStructural/modelling/intrusions/__init__.py +25 -0
  103. LoopStructural/modelling/intrusions/geom_conceptual_models.py +142 -0
  104. LoopStructural/modelling/intrusions/geometric_scaling_functions.py +123 -0
  105. LoopStructural/modelling/intrusions/intrusion_builder.py +672 -0
  106. LoopStructural/modelling/intrusions/intrusion_feature.py +410 -0
  107. LoopStructural/modelling/intrusions/intrusion_frame_builder.py +971 -0
  108. LoopStructural/modelling/intrusions/intrusion_support_functions.py +460 -0
  109. LoopStructural/utils/__init__.py +38 -0
  110. LoopStructural/utils/_surface.py +143 -0
  111. LoopStructural/utils/_transformation.py +76 -0
  112. LoopStructural/utils/config.py +18 -0
  113. LoopStructural/utils/dtm_creator.py +17 -0
  114. LoopStructural/utils/exceptions.py +31 -0
  115. LoopStructural/utils/helper.py +292 -0
  116. LoopStructural/utils/json_encoder.py +18 -0
  117. LoopStructural/utils/linalg.py +8 -0
  118. LoopStructural/utils/logging.py +79 -0
  119. LoopStructural/utils/maths.py +245 -0
  120. LoopStructural/utils/regions.py +103 -0
  121. LoopStructural/utils/typing.py +7 -0
  122. LoopStructural/utils/utils.py +68 -0
  123. LoopStructural/version.py +1 -0
  124. LoopStructural/visualisation/__init__.py +11 -0
  125. LoopStructural-1.6.1.dist-info/LICENSE +21 -0
  126. LoopStructural-1.6.1.dist-info/METADATA +81 -0
  127. LoopStructural-1.6.1.dist-info/RECORD +129 -0
  128. LoopStructural-1.6.1.dist-info/WHEEL +5 -0
  129. LoopStructural-1.6.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,554 @@
1
+ """
2
+ Routines to export geological model data to file in a variety of formats
3
+ """
4
+
5
+ import os
6
+ from pyevtk.hl import unstructuredGridToVTK, pointsToVTK
7
+ from pyevtk.vtk import VtkTriangle
8
+ import numpy as np
9
+ from skimage.measure import marching_cubes
10
+
11
+ from LoopStructural.utils.helper import create_box
12
+ from LoopStructural.export.file_formats import FileFormat
13
+ from LoopStructural.datatypes import Surface
14
+
15
+ from ..utils import getLogger
16
+
17
+ logger = getLogger(__name__)
18
+
19
+
20
+ def write_feat_surfs(
21
+ model, featurename, file_format=FileFormat.NUMPY, file_name=None, isovalue=0.0
22
+ ):
23
+ """
24
+ Writes out features from a model as 3d surfaces
25
+
26
+ Parameters
27
+ ----------
28
+ model: GeologicalModel object
29
+ Geological model to export
30
+ file_name: string
31
+ Name of file that model is exported to, including path, but without the file extension
32
+ file_format: export.fileformats.FileFormat object
33
+ OPTIONAL desired format of exported file. Supports GOCAD, VTK & NUMPY. Default is NUMPY
34
+ target_feats: list
35
+ OPTIONAL list of feature names to export, if omitted all faults are exported
36
+ isovalue: float
37
+ OPTIONAL isovalue point at which surface is generated, default is 0.0
38
+
39
+ Returns
40
+ -------
41
+ Tuple of (boolean, [ SimpleNamespace() ... ])
42
+ If successful, boolean is True and SimpleNamespace() objects have the following attributes:
43
+ verts: vertices, numpy ndarray with dtype = float64 & shape = (N,3)
44
+ faces: faces, numpy ndarray with dtype = int32 & shape = (M,3)
45
+ values: values, numpy ndarray with dtype = float32 & shape = (N,)
46
+ normals: normals, numpy ndarray with dtype = float32 & shape = (N,3)
47
+ name: name of feature e.g. fault or supergroup, string
48
+ If not successful, it returns (False, [])
49
+ """
50
+ # Set up the type of file writer function
51
+ if file_format == FileFormat.GOCAD:
52
+ write_fn = _write_feat_surfs_gocad
53
+ elif file_format == FileFormat.VTK:
54
+ write_fn = _write_feat_surfs_evtk
55
+ elif file_format == FileFormat.NUMPY:
56
+ write_fn = None
57
+ else:
58
+ logger.warning(f"Cannot export to surface file - format {file_format} not supported yet")
59
+ return False, []
60
+ # Skip if not a requested feature
61
+ if featurename not in model:
62
+ logger.warning("{featurename} is not in the model, skipping")
63
+ return False, []
64
+ has_feats = True
65
+
66
+ x = np.linspace(model.bounding_box.bb[0, 0], model.bounding_box.bb[1, 0], model.nsteps[0])
67
+ y = np.linspace(model.bounding_box.bb[0, 1], model.bounding_box.bb[1, 1], model.nsteps[1])
68
+ z = np.linspace(model.bounding_box.bb[1, 2], model.bounding_box.bb[0, 2], model.nsteps[2])
69
+ xx, yy, zz = np.meshgrid(x, y, z, indexing="ij")
70
+ points = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
71
+ val = model[featurename].evaluate_value(points)
72
+ step_vector = np.array([x[1] - x[0], y[1] - y[0], z[1] - z[0]])
73
+ logger.info(f"Creating isosurface of {featurename} at {isovalue}")
74
+
75
+ if isovalue > np.nanmax(val) or isovalue < np.nanmin(val):
76
+ logger.warning(
77
+ f"For {featurename} isovalue {isovalue} doesn't exist inside bounding box, skipping"
78
+ )
79
+ return False, []
80
+ try:
81
+ verts, faces, normals, values = marching_cubes(
82
+ val.reshape(model.nsteps, order="C"), isovalue, spacing=step_vector
83
+ )
84
+ verts += np.array(
85
+ [
86
+ model.bounding_box.bb[0, 0],
87
+ model.bounding_box.bb[0, 1],
88
+ model.bounding_box.bb[1, 2],
89
+ ]
90
+ )
91
+ model.rescale(verts)
92
+ surf = Surface(
93
+ vertices=verts,
94
+ triangles=faces,
95
+ normals=normals,
96
+ values=values,
97
+ name=featurename.replace(" ", "-"),
98
+ )
99
+
100
+ except (ValueError, RuntimeError) as e:
101
+ logger.debug(f"Exception creating feature surface {featurename}: {e}")
102
+ logger.warning(f"Cannot isosurface {featurename} at {isovalue}, skipping")
103
+ return False, []
104
+
105
+ if not has_feats:
106
+ logger.warning("Cannot locate features in model")
107
+ return False, []
108
+
109
+ # Call the file writer function
110
+ result = True
111
+ if write_fn is not None:
112
+ result = write_fn(surf, file_name)
113
+ return result
114
+
115
+
116
+ def _write_feat_surfs_evtk(surf, file_name):
117
+ """
118
+ Writes out an unstructured VTK file containing a 2d fault surface
119
+
120
+ Parameters
121
+ ----------
122
+ surf_list: [ SimpleNamespace() ... ]
123
+ Details of the surfaces, as a list of SimpleNamespace() objects. Fields are:
124
+ verts: vertices, numpy ndarray with dtype = float64 & shape = (N,3)
125
+ faces: faces, numpy ndarray with dtype = int32 & shape = (M,3)
126
+ values: values, numpy ndarray with dtype = float32 & shape = (N,)
127
+ normals: normals, numpy ndarray with dtype = float32 & shape = (N,3)
128
+ name: name of feature e.g. fault or supergroup, string
129
+
130
+ file_name: string
131
+ file name to be written out without extension
132
+
133
+ Returns
134
+ -------
135
+ True if successful
136
+
137
+ """
138
+ connectivity = np.zeros(0)
139
+ offsets = np.zeros(0)
140
+ cell_types = np.zeros(0)
141
+ x = np.zeros(0)
142
+ y = np.zeros(0)
143
+ z = np.zeros(0)
144
+ conn_idx = 0
145
+ offset_idx = 0
146
+ pointData = np.zeros(0)
147
+
148
+ # Concatenate the geometry of all features into x, y, z, connectivity, offsets & cell_types
149
+
150
+ # Accumulate x,y,z values in 1-d array
151
+ x = np.append(x, np.ascontiguousarray(surf.verts[:, 0]))
152
+ y = np.append(y, np.ascontiguousarray(surf.verts[:, 1]))
153
+ z = np.append(z, np.ascontiguousarray(surf.verts[:, 2]))
154
+
155
+ # Convert faces to int64
156
+ conn = np.array(surf.faces, dtype=np.int64)
157
+
158
+ # Reshape connections to 1d
159
+ conn = conn.reshape(conn.size)
160
+ conn += conn_idx
161
+
162
+ # Make offsets into connection array
163
+ offs = np.array(list(range(3 + offset_idx, len(conn) + offset_idx, 3)), dtype=np.int64)
164
+
165
+ # Set VTK datatype as triangles
166
+ ctype = np.zeros(offs.size)
167
+ ctype.fill(VtkTriangle.tid)
168
+
169
+ # Accumulate values in arrays
170
+ connectivity = np.append(connectivity, conn)
171
+
172
+ offsets = np.append(offsets, offs)
173
+ cell_types = np.append(cell_types, ctype)
174
+
175
+ pointData = np.append(pointData, surf.values.reshape(surf.values.size))
176
+
177
+ # Enable connections to point to next set of vertices
178
+ conn_idx = x.size
179
+
180
+ # Enable offsets to point to next set of connections
181
+ offset_idx = connectivity.size
182
+
183
+ # Write out file
184
+ try:
185
+ logger.info(f"Writing file {file_name}.vtu")
186
+ unstructuredGridToVTK(
187
+ f"{file_name}",
188
+ x,
189
+ y,
190
+ z,
191
+ connectivity=connectivity,
192
+ offsets=offsets,
193
+ cell_types=cell_types,
194
+ pointData={"values": pointData},
195
+ )
196
+ except Exception as e:
197
+ logger.warning(f"Cannot export fault surface to VTK file {file_name}: {e}")
198
+ return False
199
+
200
+ return True
201
+
202
+
203
+ def _write_feat_surfs_gocad(surf, file_name):
204
+ """
205
+ Writes out a GOCAD TSURF file for each surface in list
206
+
207
+ Parameters
208
+ ----------
209
+ surf_list: [ SimpleNamespace() ... ]
210
+ Details of the surfaces, as a list of SimpleNamespace() objects. Fields are:
211
+ verts: vertices, numpy ndarray with dtype = float64 & shape = (N,3)
212
+ faces: faces, numpy ndarray with dtype = int32 & shape = (M,3)
213
+ values: values, numpy ndarray with dtype = float32 & shape = (N,)
214
+ normals: normals, numpy ndarray with dtype = float32 & shape = (N,3)
215
+ name: name of feature e.g. fault or supergroup, string
216
+
217
+ file_name: string
218
+ Desired filename
219
+
220
+ Returns
221
+ -------
222
+ True if successful
223
+
224
+ """
225
+ from pathlib import Path
226
+
227
+ file_name = Path(file_name).with_suffix(".ts")
228
+ with open(f"{file_name}", "w") as fd:
229
+ fd.write(
230
+ f"""GOCAD TSurf 1
231
+ HEADER {{
232
+ *solid*color: #ffa500
233
+ ivolmap: false
234
+ imap: false
235
+ name: {surf.name}
236
+ }}
237
+ GOCAD_ORIGINAL_COORDINATE_SYSTEM
238
+ NAME Default
239
+ PROJECTION Unknown
240
+ DATUM Unknown
241
+ AXIS_NAME X Y Z
242
+ AXIS_UNIT m m m
243
+ ZPOSITIVE Elevation
244
+ END_ORIGINAL_COORDINATE_SYSTEM
245
+ GEOLOGICAL_FEATURE {surf.name}
246
+ GEOLOGICAL_TYPE fault
247
+ PROPERTY_CLASS_HEADER X {{
248
+ kind: X
249
+ unit: m
250
+ }}
251
+ PROPERTY_CLASS_HEADER Y {{
252
+ kind: Y
253
+ unit: m
254
+ }}
255
+ PROPERTY_CLASS_HEADER Z {{
256
+ kind: Z
257
+ unit: m
258
+ is_z: on
259
+ }}
260
+ PROPERTY_CLASS_HEADER vector3d {{
261
+ kind: Length
262
+ unit: m
263
+ }}
264
+ TFACE
265
+ """
266
+ )
267
+ v_idx = 1
268
+ v_map = {}
269
+ for idx, vert in enumerate(surf.vertices):
270
+ if not np.isnan(vert[0]) and not np.isnan(vert[1]) and not np.isnan(vert[2]):
271
+ fd.write(f"VRTX {v_idx:} {vert[0]} {vert[1]} {vert[2]} \n")
272
+ v_map[idx] = v_idx
273
+ v_idx += 1
274
+ for face in surf.triangles:
275
+ if face[0] in v_map and face[1] in v_map and face[2] in v_map:
276
+ fd.write(f"TRGL {v_map[face[0]]} {v_map[face[1]]} {v_map[face[2]]} \n")
277
+ fd.write("END\n")
278
+ return True
279
+
280
+
281
+ def write_cubeface(model, file_name, data_label, nsteps, file_format):
282
+ """
283
+ Writes out the model as a cuboid with six rectangular surfaces
284
+
285
+ Parameters
286
+ ----------
287
+ model : GeologicalModel object
288
+ Geological model to export
289
+ file_name : string
290
+ Name of file that model is exported to, including path, but without the file extension
291
+ data_label : string
292
+ A data label to insert into export file
293
+ nsteps : np.array([num-x-steps, num-y-steps, num-z-steps])
294
+ 3d array dimensions
295
+ file_format: export.fileformats.FileFormat object
296
+ Desired format of exported file. Supports VTK
297
+
298
+ Returns
299
+ -------
300
+ True if successful
301
+
302
+ """
303
+ if file_format == FileFormat.VTK:
304
+ return _write_cubeface_evtk(model, file_name, data_label, nsteps)
305
+
306
+ logger.warning(f"Cannot export to file - format {file_format} not supported yet")
307
+ return False
308
+
309
+
310
+ def write_vol(model, file_name, data_label, nsteps, file_format):
311
+ """
312
+ Writes out the model as a 3d volume grid
313
+
314
+ Parameters
315
+ ----------
316
+ model : GeologicalModel object
317
+ Geological model to export
318
+ file_name : string
319
+ Name of file that model is exported to, including path, but without the file extension
320
+ data_label : string
321
+ A data label to insert into export file
322
+ nsteps : np.array([num-x-steps, num-y-steps, num-z-steps])
323
+ 3d array dimensions
324
+ file_format: export.fileformats.FileFormat object
325
+ Desired format of exported file. Supports VTK and GOCAD
326
+
327
+ Returns
328
+ -------
329
+ True if successful
330
+
331
+ """
332
+ if file_format == FileFormat.VTK:
333
+ return _write_vol_evtk(model, file_name, data_label, nsteps)
334
+ if file_format == FileFormat.GOCAD:
335
+ return _write_vol_gocad(model, file_name, data_label, nsteps)
336
+
337
+ logger.warning(f"Cannot export to file - format {file_format} not supported yet")
338
+ return False
339
+
340
+
341
+ def _write_cubeface_evtk(model, file_name, data_label, nsteps, real_coords=True):
342
+ """
343
+ Writes out the model as a cuboid with six rectangular surfaces in VTK unstructured grid format
344
+
345
+ Parameters
346
+ ----------
347
+ model : GeologicalModel object
348
+ Geological model to export
349
+ file_name : string
350
+ Name of file that model is exported to, including path, but without the file extension
351
+ data_label : string
352
+ A data label to insert into export file
353
+ nsteps : np.array([num-x-steps, num-y-steps, num-z-steps])
354
+ 3d array dimensions
355
+
356
+ Returns
357
+ -------
358
+ True if successful
359
+
360
+ """
361
+ # Evaluate model at points
362
+ points, tri = create_box(model.bounding_box, nsteps)
363
+ val = model.evaluate_model(points, scale=False)
364
+ if real_coords:
365
+ model.rescale(points)
366
+
367
+ # Define vertices
368
+ x = np.zeros(points.shape[0])
369
+ y = np.zeros(points.shape[0])
370
+ z = np.zeros(points.shape[0])
371
+ for i in range(points.shape[0]):
372
+ x[i], y[i], z[i] = points[i][0], points[i][1], points[i][2]
373
+
374
+ # Define connectivity or vertices that belongs to each element
375
+ conn = np.zeros(tri.shape[0] * 3)
376
+ for i in range(tri.shape[0]):
377
+ conn[i * 3], conn[i * 3 + 1], conn[i * 3 + 2] = tri[i][0], tri[i][1], tri[i][2]
378
+
379
+ # Define offset of last vertex of each element
380
+ offset = np.zeros(tri.shape[0])
381
+ for i in range(tri.shape[0]):
382
+ offset[i] = (i + 1) * 3
383
+
384
+ # Define cell types
385
+ ctype = np.full(tri.shape[0], VtkTriangle.tid)
386
+
387
+ try:
388
+ unstructuredGridToVTK(
389
+ file_name,
390
+ x,
391
+ y,
392
+ z,
393
+ connectivity=conn,
394
+ offsets=offset,
395
+ cell_types=ctype,
396
+ cellData=None,
397
+ pointData={data_label: val},
398
+ )
399
+ except Exception as e:
400
+ logger.warning(f"Cannot export cuboid surface to VTK file {file_name}: {e}")
401
+ return False
402
+ return True
403
+
404
+
405
+ def _write_vol_evtk(model, file_name, data_label, nsteps, real_coords=True):
406
+ """
407
+ Writes out the model as a 3d volume grid in VTK points format
408
+
409
+ Parameters
410
+ ----------
411
+ model : GeologicalModel object
412
+ Geological model to export
413
+ file_name : string
414
+ Name of file that model is exported to, including path, but without the file extension
415
+ data_label : string
416
+ A data label to insert into export file
417
+ nsteps : np.array([num-x-steps, num-y-steps, num-z-steps])
418
+ 3d array dimensions
419
+
420
+ Returns
421
+ -------
422
+ True if successful
423
+
424
+ """
425
+ # Define grid spacing
426
+ xyz = model.bounding_box.regular_grid(nsteps)
427
+ vals = model.evaluate_model(xyz, scale=False)
428
+ if real_coords:
429
+ model.rescale(xyz)
430
+
431
+ # Define vertices - xyz.shape[0] is length of vector array
432
+ x = np.zeros(xyz.shape[0])
433
+ y = np.zeros(xyz.shape[0])
434
+ z = np.zeros(xyz.shape[0])
435
+ for i in range(xyz.shape[0]):
436
+ x[i], y[i], z[i] = xyz[i][0], xyz[i][1], xyz[i][2]
437
+
438
+ # Write to grid
439
+ try:
440
+ pointsToVTK(file_name, x, y, z, data={data_label: vals})
441
+ except Exception as e:
442
+ logger.warning(f"Cannot export volume to VTK file {file_name}: {e}")
443
+ return False
444
+ return True
445
+
446
+
447
+ def _write_vol_gocad(model, file_name, data_label, nsteps, real_coords=True):
448
+ """
449
+ Writes out the model as a 3d volume grid in GOCAD VOXET object format
450
+
451
+ Parameters
452
+ ----------
453
+ model : GeologicalModel object
454
+ Geological model to export
455
+ file_name : string
456
+ Name of file that model is exported to, including path, but without the file extension
457
+ data_label : string
458
+ A data label to insert into export file
459
+ nsteps : np.array([num-x-steps, num-y-steps, num-z-steps])
460
+ 3d array dimensions
461
+
462
+ Returns
463
+ -------
464
+ True if successful
465
+
466
+ """
467
+ # Define grid spacing in model scale coords
468
+ xyz = model.bounding_box.regular_grid(nsteps)
469
+
470
+ vals = model.evaluate_model(xyz, scale=False)
471
+ # Use FORTRAN style indexing for GOCAD VOXET
472
+ vol_vals = np.reshape(vals, nsteps, order="F")
473
+ bbox = model.bounding_box[:]
474
+
475
+ # Convert bounding box to real world scale coords
476
+ if real_coords:
477
+ model.rescale(bbox)
478
+
479
+ # If integer values
480
+ if type(vals[0]) is np.int64:
481
+ d_type = np.int8
482
+ no_data_val = None
483
+ prop_esize = 1
484
+ prop_storage_type = "Octet"
485
+
486
+ # If float values
487
+ elif type(vals[0]) is np.float32:
488
+ d_type = np.dtype(">f4")
489
+ no_data_val = -999999.0
490
+ prop_esize = 4
491
+ prop_storage_type = "Float"
492
+ else:
493
+ logger.warning(
494
+ f"Cannot export volume to GOCAD VOXET file: Unsupported type {type(vals[0])}"
495
+ )
496
+ return False
497
+
498
+ # Write out VOXET file
499
+ vo_filename = file_name + ".vo"
500
+ data_filename = file_name + "@@"
501
+ try:
502
+ with open(vo_filename, "w") as fp:
503
+ fp.write(
504
+ f"""GOCAD Voxet 1
505
+ HEADER {{
506
+ name: {os.path.basename(file_name)}
507
+ }}
508
+ GOCAD_ORIGINAL_COORDINATE_SYSTEM
509
+ NAME Default
510
+ AXIS_NAME "X" "Y" "Z"
511
+ AXIS_UNIT "m" "m" "m"
512
+ ZPOSITIVE Elevation
513
+ END_ORIGINAL_COORDINATE_SYSTEM
514
+ AXIS_O 0.000000 0.000000 0.000000
515
+ AXIS_U 1.000000 0.000000 0.000000
516
+ AXIS_V 0.000000 1.000000 0.000000
517
+ AXIS_W 0.000000 0.000000 1.000000
518
+ AXIS_MIN {bbox[0, 0]} {bbox[0, 1]} {bbox[0, 2]}
519
+ AXIS_MAX {bbox[1, 0]} {bbox[1, 1]} {bbox[1, 2]}
520
+ AXIS_N {nsteps[0]} {nsteps[1]} {nsteps[2]}
521
+ AXIS_NAME "X" "Y" "Z"
522
+ AXIS_UNIT "m" "m" "m"
523
+ AXIS_TYPE even even even
524
+ PROPERTY 1 {data_label}
525
+ PROPERTY_CLASS 1 {data_label}
526
+ PROP_UNIT 1 {data_label}
527
+ PROPERTY_CLASS_HEADER 1 {data_label} {{
528
+ }}
529
+ PROPERTY_SUBCLASS 1 QUANTITY {prop_storage_type}
530
+ """
531
+ )
532
+ if no_data_val is not None:
533
+ fp.write(f"PROP_NO_DATA_VALUE 1 {no_data_val}\n")
534
+ fp.write(
535
+ f"""PROP_ETYPE 1 IEEE
536
+ PROP_FORMAT 1 RAW
537
+ PROP_ESIZE 1 {prop_esize}
538
+ PROP_OFFSET 1 0
539
+ PROP_FILE 1 {data_filename}
540
+ END\n"""
541
+ )
542
+ except IOError as exc:
543
+ logger.warning(f"Cannot export volume to GOCAD VOXET file {vo_filename}: {exc}")
544
+ return False
545
+
546
+ # Write out accompanying binary data file
547
+ export_vals = np.array(vol_vals, dtype=d_type)
548
+ try:
549
+ with open(data_filename, "wb") as fp:
550
+ export_vals.tofile(fp)
551
+ except IOError as exc:
552
+ logger.warning(f"Cannot export volume to GOCAD VOXET data file {data_filename}: {exc}")
553
+ return False
554
+ return True
@@ -0,0 +1,15 @@
1
+ """
2
+ Exported file formats
3
+ """
4
+
5
+ from enum import Enum
6
+
7
+
8
+ class FileFormat(Enum):
9
+ """Enumeration of file export formats"""
10
+
11
+ OBJ = 1 # Not supported yet
12
+ VTK = 2
13
+ GOCAD = 3
14
+ GLTF = 4 # Not supported yet
15
+ NUMPY = 5
@@ -0,0 +1,100 @@
1
+ import geoh5py
2
+ import geoh5py.workspace
3
+ import numpy as np
4
+ from LoopStructural.datatypes import ValuePoints, VectorPoints
5
+
6
+
7
+ def add_surface_to_geoh5(filename, surface, overwrite=True, groupname="Loop"):
8
+ with geoh5py.workspace.Workspace(filename) as workspace:
9
+ group = workspace.get_entity(groupname)[0]
10
+ if not group:
11
+ group = geoh5py.groups.ContainerGroup.create(
12
+ workspace, name=groupname, allow_delete=True
13
+ )
14
+ if surface.name in workspace.list_entities_name.values():
15
+ existing_surf = workspace.get_entity(surface.name)
16
+ existing_surf[0].allow_delete = True
17
+ if overwrite:
18
+ workspace.remove_entity(existing_surf[0])
19
+ data = {}
20
+ if surface.properties is not None:
21
+ for k, v in surface.properties.items():
22
+ data[k] = {'association': "VERTEX", "values": v}
23
+ surface = geoh5py.objects.Surface.create(
24
+ workspace,
25
+ name=surface.name,
26
+ vertices=surface.vertices,
27
+ cells=surface.triangles,
28
+ parent=group,
29
+ )
30
+ surface.add_data(data)
31
+
32
+
33
+ def add_points_to_geoh5(filename, point, overwrite=True, groupname="Loop"):
34
+ with geoh5py.workspace.Workspace(filename) as workspace:
35
+ group = workspace.get_entity(groupname)[0]
36
+ if not group:
37
+ group = geoh5py.groups.ContainerGroup.create(
38
+ workspace, name=groupname, allow_delete=True
39
+ )
40
+ if point.name in workspace.list_entities_name.values():
41
+ existing_point = workspace.get_entity(point.name)
42
+ existing_point[0].allow_delete = True
43
+ if overwrite:
44
+ workspace.remove_entity(existing_point[0])
45
+ data = {}
46
+ if point.properties is not None:
47
+ for k, v in point.properties.items():
48
+ data[k] = {'association': "VERTEX", "values": v}
49
+ if isinstance(point, VectorPoints):
50
+ data['vx'] = {'association': "VERTEX", "values": point.vectors[:, 0]}
51
+ data['vy'] = {'association': "VERTEX", "values": point.vectors[:, 1]}
52
+ data['vz'] = {'association': "VERTEX", "values": point.vectors[:, 2]}
53
+
54
+ if isinstance(point, ValuePoints):
55
+ data['val'] = {'association': "VERTEX", "values": point.values}
56
+ point = geoh5py.objects.Points.create(
57
+ workspace,
58
+ name=point.name,
59
+ vertices=point.locations,
60
+ parent=group,
61
+ )
62
+ point.add_data(data)
63
+
64
+
65
+ def add_structured_grid_to_geoh5(filename, structured_grid, overwrite=True, groupname="Loop"):
66
+ with geoh5py.workspace.Workspace(filename) as workspace:
67
+ group = workspace.get_entity(groupname)[0]
68
+ if not group:
69
+ group = geoh5py.groups.ContainerGroup.create(
70
+ workspace, name=groupname, allow_delete=True
71
+ )
72
+ if structured_grid.name in workspace.list_entities_name.values():
73
+ existing_block = workspace.get_entity(structured_grid.name)
74
+ existing_block[0].allow_delete = True
75
+ if overwrite:
76
+ workspace.remove_entity(existing_block[0])
77
+ data = {}
78
+ if structured_grid.cell_properties is not None:
79
+ for k, v in structured_grid.cell_properties.items():
80
+ data[k] = {
81
+ 'association': "CELL",
82
+ "values": np.rot90(v.reshape(structured_grid.nsteps - 1, order="F")).flatten(),
83
+ }
84
+ block = geoh5py.objects.BlockModel.create(
85
+ workspace,
86
+ name=structured_grid.name,
87
+ origin=structured_grid.origin,
88
+ u_cell_delimiters=np.cumsum(
89
+ np.ones(structured_grid.nsteps[0]) * structured_grid.step_vector[0]
90
+ ), # Offsets along u
91
+ v_cell_delimiters=np.cumsum(
92
+ np.ones(structured_grid.nsteps[1]) * structured_grid.step_vector[1]
93
+ ), # Offsets along v
94
+ z_cell_delimiters=np.cumsum(
95
+ np.ones(structured_grid.nsteps[2]) * structured_grid.step_vector[2]
96
+ ), # Offsets along z (down)
97
+ rotation=0.0,
98
+ parent=group,
99
+ )
100
+ block.add_data(data)