subsurface-terra 2025.1.0rc14__py3-none-any.whl → 2025.1.0rc16__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.
Files changed (81) hide show
  1. subsurface/__init__.py +31 -31
  2. subsurface/_version.py +34 -21
  3. subsurface/api/__init__.py +13 -13
  4. subsurface/api/interfaces/__init__.py +3 -3
  5. subsurface/api/interfaces/stream.py +136 -136
  6. subsurface/api/reader/read_wells.py +78 -78
  7. subsurface/core/geological_formats/boreholes/_combine_trajectories.py +117 -117
  8. subsurface/core/geological_formats/boreholes/_map_attrs_to_survey.py +236 -0
  9. subsurface/core/geological_formats/boreholes/_survey_to_unstruct.py +163 -0
  10. subsurface/core/geological_formats/boreholes/boreholes.py +140 -116
  11. subsurface/core/geological_formats/boreholes/collars.py +26 -26
  12. subsurface/core/geological_formats/boreholes/survey.py +86 -380
  13. subsurface/core/geological_formats/fault.py +47 -47
  14. subsurface/core/reader_helpers/reader_unstruct.py +11 -11
  15. subsurface/core/reader_helpers/readers_data.py +130 -130
  16. subsurface/core/reader_helpers/readers_wells.py +13 -13
  17. subsurface/core/structs/__init__.py +3 -3
  18. subsurface/core/structs/base_structures/__init__.py +2 -2
  19. subsurface/core/structs/base_structures/_liquid_earth_mesh.py +121 -121
  20. subsurface/core/structs/base_structures/_unstructured_data_constructor.py +70 -70
  21. subsurface/core/structs/base_structures/base_structures_enum.py +6 -6
  22. subsurface/core/structs/base_structures/structured_data.py +282 -282
  23. subsurface/core/structs/base_structures/unstructured_data.py +319 -319
  24. subsurface/core/structs/structured_elements/octree_mesh.py +10 -10
  25. subsurface/core/structs/structured_elements/structured_grid.py +59 -59
  26. subsurface/core/structs/structured_elements/structured_mesh.py +9 -9
  27. subsurface/core/structs/unstructured_elements/__init__.py +3 -3
  28. subsurface/core/structs/unstructured_elements/line_set.py +72 -72
  29. subsurface/core/structs/unstructured_elements/point_set.py +43 -43
  30. subsurface/core/structs/unstructured_elements/tetrahedron_mesh.py +35 -35
  31. subsurface/core/structs/unstructured_elements/triangular_surface.py +62 -62
  32. subsurface/core/utils/utils_core.py +38 -38
  33. subsurface/modules/reader/__init__.py +13 -13
  34. subsurface/modules/reader/faults/faults.py +80 -80
  35. subsurface/modules/reader/from_binary.py +46 -46
  36. subsurface/modules/reader/mesh/_GOCAD_mesh.py +82 -82
  37. subsurface/modules/reader/mesh/_trimesh_reader.py +447 -447
  38. subsurface/modules/reader/mesh/csv_mesh_reader.py +53 -53
  39. subsurface/modules/reader/mesh/dxf_reader.py +177 -177
  40. subsurface/modules/reader/mesh/glb_reader.py +30 -30
  41. subsurface/modules/reader/mesh/mx_reader.py +232 -232
  42. subsurface/modules/reader/mesh/obj_reader.py +53 -53
  43. subsurface/modules/reader/mesh/omf_mesh_reader.py +43 -43
  44. subsurface/modules/reader/mesh/surface_reader.py +56 -56
  45. subsurface/modules/reader/mesh/surfaces_api.py +41 -41
  46. subsurface/modules/reader/profiles/__init__.py +3 -3
  47. subsurface/modules/reader/profiles/profiles_core.py +197 -197
  48. subsurface/modules/reader/read_netcdf.py +38 -38
  49. subsurface/modules/reader/topography/__init__.py +7 -7
  50. subsurface/modules/reader/topography/topo_core.py +100 -100
  51. subsurface/modules/reader/volume/read_grav3d.py +478 -428
  52. subsurface/modules/reader/volume/read_volume.py +327 -230
  53. subsurface/modules/reader/volume/segy_reader.py +105 -105
  54. subsurface/modules/reader/volume/seismic.py +173 -173
  55. subsurface/modules/reader/volume/volume_utils.py +43 -43
  56. subsurface/modules/reader/wells/DEP/__init__.py +43 -43
  57. subsurface/modules/reader/wells/DEP/_well_files_reader.py +167 -167
  58. subsurface/modules/reader/wells/DEP/_wells_api.py +61 -61
  59. subsurface/modules/reader/wells/DEP/_welly_reader.py +180 -180
  60. subsurface/modules/reader/wells/DEP/pandas_to_welly.py +212 -212
  61. subsurface/modules/reader/wells/_read_to_df.py +57 -57
  62. subsurface/modules/reader/wells/read_borehole_interface.py +148 -148
  63. subsurface/modules/reader/wells/wells_utils.py +68 -68
  64. subsurface/modules/tools/mocking_aux.py +104 -104
  65. subsurface/modules/visualization/__init__.py +2 -2
  66. subsurface/modules/visualization/to_pyvista.py +320 -320
  67. subsurface/modules/writer/to_binary.py +12 -12
  68. subsurface/modules/writer/to_rex/common.py +78 -78
  69. subsurface/modules/writer/to_rex/data_struct.py +74 -74
  70. subsurface/modules/writer/to_rex/gempy_to_rexfile.py +791 -791
  71. subsurface/modules/writer/to_rex/material_encoder.py +44 -44
  72. subsurface/modules/writer/to_rex/mesh_encoder.py +152 -152
  73. subsurface/modules/writer/to_rex/to_rex.py +115 -115
  74. subsurface/modules/writer/to_rex/utils.py +15 -15
  75. subsurface/optional_requirements.py +116 -116
  76. {subsurface_terra-2025.1.0rc14.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/METADATA +194 -194
  77. subsurface_terra-2025.1.0rc16.dist-info/RECORD +98 -0
  78. {subsurface_terra-2025.1.0rc14.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/WHEEL +1 -1
  79. {subsurface_terra-2025.1.0rc14.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/licenses/LICENSE +203 -203
  80. subsurface_terra-2025.1.0rc14.dist-info/RECORD +0 -96
  81. {subsurface_terra-2025.1.0rc14.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/top_level.txt +0 -0
@@ -1,319 +1,319 @@
1
- from dataclasses import dataclass
2
- from typing import Union, Dict, Mapping, Hashable, Any, Literal
3
-
4
- import numpy as np
5
- import pandas as pd
6
- import xarray as xr
7
-
8
- from subsurface.core.structs.base_structures._unstructured_data_constructor import vertex_and_cells_arrays_to_data_array, raw_attributes_to_dict_data_arrays
9
- from subsurface.core.structs.base_structures.base_structures_enum import SpecialCellCase
10
-
11
-
12
- @dataclass(frozen=False)
13
- class UnstructuredData:
14
- data: xr.Dataset
15
- cells_attr_name: str = "cell_attrs"
16
- vertex_attr_name: str = "vertex_attrs"
17
-
18
- """Primary structure definition for unstructured data
19
-
20
- Attributes:
21
- data (`xarray.Dataset`): Data structure where we store
22
-
23
- Args:
24
-
25
- ds (xarray.Dataset): Directly a dataset with the expected structured. This
26
- arg is specially thought for loading data from disk
27
-
28
- Notes:
29
- Depending on the shape of `edge` the following unstructured elements can
30
- be created:
31
-
32
- - cells NDArray[(Any, 0), IntX] or NDArray[(Any, 1), IntX] -> *Point cloud*.
33
- E.g. Outcrop scan with lidar
34
- - cells NDArray[(Any, 2), IntX] -> *Lines*. E.g. Borehole
35
- - cells NDArray[(Any, 3), IntX] -> *Mesh*. E.g surface-DEM Topography
36
- - cells NDArray[(Any, 4), IntX]
37
- - -> *tetrahedron*
38
- - -> *quadrilateral (or tetragon)* UNSUPPORTED?
39
- - cells NDArray[(Any, 8), IntX] -> *Hexahedron: Unstructured grid/Prisms*
40
- """
41
-
42
- def __post_init__(self):
43
- self._validate()
44
-
45
- def __repr__(self):
46
- return self.data.__repr__()
47
-
48
- @classmethod
49
- def from_array(
50
- cls,
51
- vertex: np.ndarray,
52
- cells: Union[np.ndarray, Literal["lines", "points"], SpecialCellCase],
53
- *,
54
- cells_attr: Union[None, pd.DataFrame, Dict[str, xr.DataArray]] = None,
55
- vertex_attr: Union[None, pd.DataFrame, Dict[str, xr.DataArray]] = None,
56
- coords: Mapping[Hashable, Any] = None,
57
- xarray_attributes: Mapping[Hashable, Any] = None,
58
- default_cells_attr_name: str = "cell_attrs",
59
- default_points_attr_name: str = "vertex_attrs",
60
- attributes: Union[None, pd.DataFrame, Dict[str, xr.DataArray]] = None # TODO Obsolete
61
- ):
62
- """ Constructor of UnstructuredData from arrays or pandas DataFrames.
63
-
64
- Args:
65
- vertex (np.ndarray): NDArray[(Any, 3), FloatX]: XYZ point data
66
- cells (Union[np.ndarray, Literal["lines", "points"]]): NDArray[(Any, ...), IntX]:
67
- Combination of vertex that create different geometric elements. If
68
- str use default values for either points or lines
69
- cells_attr (Union[None, pd.DataFrame, Dict[str, xr.DataArray]]: Number associated to an element
70
- vertex_attr (Union[None, pd.DataFrame, Dict[str, xr.DataArray]]: Number
71
- associated to points
72
- coords:
73
- xarray_attributes:
74
- attributes:
75
- default_cells_attr_name:
76
- default_points_attr_name:
77
-
78
- Returns:
79
-
80
- """
81
- if attributes is not None:
82
- cells_attr = attributes
83
-
84
- cells_data_array, n_cells, n_vertex, vertex_data_array = vertex_and_cells_arrays_to_data_array(
85
- cells=cells,
86
- vertex=vertex
87
- )
88
- points_attributes_xarray_dict: dict[str, xr.DataArray] = raw_attributes_to_dict_data_arrays(
89
- default_attributes_name=default_points_attr_name,
90
- n_items=n_vertex,
91
- dims=["points", "vertex_attr"],
92
- raw_attributes=vertex_attr
93
- )
94
-
95
- cells_attributes_xarray_dict: dict[str, xr.DataArray] = raw_attributes_to_dict_data_arrays(
96
- default_attributes_name=default_cells_attr_name,
97
- n_items=n_cells,
98
- dims=["cell", "cell_attr"],
99
- raw_attributes=cells_attr
100
- )
101
-
102
- xarray_dict = {
103
- "vertex": vertex_data_array,
104
- "cells" : cells_data_array,
105
- **cells_attributes_xarray_dict,
106
- **points_attributes_xarray_dict
107
- }
108
-
109
- return cls.from_data_arrays_dict(
110
- xarray_dict=xarray_dict,
111
- coords=coords,
112
- xarray_attributes=xarray_attributes,
113
- default_cells_attributes_name=default_cells_attr_name,
114
- default_points_attributes_name=default_points_attr_name
115
- )
116
-
117
- @classmethod
118
- def from_data_arrays_dict(
119
- cls,
120
- xarray_dict: Dict[str, xr.DataArray],
121
- coords: Mapping[Hashable, Any] = None,
122
- xarray_attributes: Mapping[Hashable, Any] = None,
123
- default_cells_attributes_name="cell_attrs",
124
- default_points_attributes_name="vertex_attrs"
125
- ):
126
- # TODO: xr.Dataset seems to have been changed with 2022.06. needs to be adapted for indexing
127
- ds = xr.Dataset(xarray_dict, coords=coords, attrs=xarray_attributes)
128
-
129
- # Try to unstack pandas dataframe if exist
130
- # TODO: This is an issue in wells. If it is only there maybe we should move it there
131
- try:
132
- ds = ds.reset_index('cell')
133
- except (KeyError, ValueError) as e:
134
- print(f"{e} xarray dataset must include 'cell' key (KeyError) or xarray 'cell' has no index (ValueError).")
135
-
136
- # Check that the Dataset data_vars names matches, "vertex", "cells", default_cells_attributes_name and default_points_attributes_name
137
- # and raise an error pointing out which one is missing
138
- _vars = [var in ds.data_vars for var in ["vertex", "cells", default_cells_attributes_name, default_points_attributes_name]]
139
- if not all(_vars):
140
- missing = ["vertex", "cells", default_cells_attributes_name, default_points_attributes_name]
141
- raise KeyError(f"DataArray must include the following keys: {missing}. For attributes you can"
142
- f" change the default names with default_cells_attributes_name and default_points_attributes_name.")
143
-
144
- return cls(ds, default_cells_attributes_name, default_points_attributes_name)
145
-
146
- @classmethod
147
- def from_binary_le(cls, path: str):
148
- from ._liquid_earth_mesh import LiquidEarthMesh
149
- with open(path, 'rb') as f:
150
- bytes_data = f.read()
151
- mesh = LiquidEarthMesh.from_binary(bytes_data)
152
- unstruct = cls.from_array(
153
- vertex=mesh.vertex,
154
- cells=mesh.cells,
155
- cells_attr=mesh.attributes,
156
- vertex_attr=mesh.points_attributes,
157
- xarray_attributes=None
158
- )
159
- return unstruct
160
-
161
- @classmethod
162
- def from_binary_le_legacy(cls, path_to_binary: str, path_to_json: str):
163
- import json
164
- from ._liquid_earth_mesh import LiquidEarthMesh
165
-
166
- with open(path_to_binary, 'rb') as f:
167
- body_ = f.read()
168
- with open(path_to_json, 'r') as f:
169
- header_ = json.load(f)
170
-
171
- header_json = json.dumps(header_)
172
- header_json_bytes = header_json.encode('utf-8')
173
- header_json_length = len(header_json_bytes)
174
- header_json_length_bytes = header_json_length.to_bytes(4, byteorder='little')
175
- file = header_json_length_bytes + header_json_bytes + body_
176
- mesh = LiquidEarthMesh.from_binary(file)
177
- unstruct = cls.from_array(
178
- vertex=mesh.vertex,
179
- cells=mesh.cells,
180
- cells_attr=mesh.attributes,
181
- vertex_attr=mesh.points_attributes,
182
- xarray_attributes=None
183
- )
184
- return unstruct
185
-
186
- @property
187
- def vertex(self) -> np.ndarray:
188
- return self.data['vertex'].values
189
-
190
- @property
191
- def cells(self):
192
- return self.data['cells'].values
193
-
194
- @property
195
- def attributes(self) -> pd.DataFrame:
196
- xarray = self.data[self.cells_attr_name]
197
- return xarray.to_dataframe()[self.cells_attr_name].unstack(level=1)
198
-
199
- @attributes.setter
200
- def attributes(self, dataframe):
201
- self.data[self.cells_attr_name] = xr.DataArray(dataframe, dims=['element', 'cell_attr'])
202
-
203
- @property
204
- def cell_attributes(self):
205
- return self.attributes
206
-
207
- @cell_attributes.setter
208
- def cell_attributes(self, dataframe):
209
- self.attributes = dataframe
210
-
211
- @property
212
- def points_attributes(self) -> pd.DataFrame:
213
- data_array: xr.DataArray = self.data[self.vertex_attr_name]
214
- dataframe: pd.DataFrame = data_array.to_dataframe()
215
- not_suer = dataframe[self.vertex_attr_name]
216
- unstack = not_suer.unstack(level=1)
217
- return unstack
218
-
219
- @points_attributes.setter
220
- def points_attributes(self, dataframe: pd.DataFrame):
221
- vertex_attr: xr.DataArray = self.data[self.vertex_attr_name]
222
- vertex_attr.values = dataframe.values
223
-
224
- @property
225
- def n_elements(self):
226
- return self.cells.shape[0]
227
-
228
- @property
229
- def n_vertex_per_element(self):
230
- return self.cells.shape[1]
231
-
232
- @property
233
- def n_points(self):
234
- return self.vertex.shape[0]
235
-
236
- @property
237
- def attributes_to_dict(
238
- self,
239
- orient: Literal["dict", "list", "series", "split", "tight", "index"] = "list"
240
- ):
241
- return self.attributes.to_dict(orient)
242
-
243
- @property
244
- def points_attributes_to_dict(
245
- self,
246
- orient: Literal["dict", "list", "series", "split", "tight", "index"] = "list"
247
- ):
248
- return self.points_attributes.to_dict(orient)
249
-
250
- @property
251
- def extent(self):
252
- max = self.vertex.max(axis=0)
253
- min = self.vertex.min(axis=0)
254
- extent = np.stack((min, max), axis=1).ravel()
255
- return extent
256
-
257
- def to_xarray(self):
258
- a = xr.DataArray(self.vertex, dims=['points', 'XYZ'])
259
- b = xr.DataArray(self.cells, dims=['cells', 'node'])
260
- e = xr.DataArray(self.attributes, dims=['element', 'cell_attr'])
261
- c = xr.Dataset({'v': a, 'e': b, 'a': e})
262
- return c
263
-
264
- def to_binary_legacy(self, order='F'):
265
- bytearray_le = self._to_bytearray(order)
266
- header = self._set_binary_header()
267
- return bytearray_le, header
268
-
269
- def to_binary(self, order='F') -> bytes:
270
- body_ = self._to_bytearray(order)
271
- header_ = self._set_binary_header()
272
- import json
273
- header_json = json.dumps(header_)
274
- header_json_bytes = header_json.encode('utf-8')
275
- header_json_length = len(header_json_bytes)
276
- header_json_length_bytes = header_json_length.to_bytes(4, byteorder='little')
277
- file = header_json_length_bytes + header_json_bytes + body_
278
- return file
279
-
280
- def _set_binary_header(self):
281
- header = {
282
- "vertex_shape" : self.vertex.shape,
283
- "cell_shape" : self.cells.shape,
284
- "cell_attr_shape" : self.attributes.shape,
285
- "vertex_attr_shape": self.points_attributes.shape,
286
- "cell_attr_names" : self.attributes.columns.to_list(),
287
- "cell_attr_types" : self.attributes.dtypes.astype(str).to_list(),
288
- "vertex_attr_names": self.points_attributes.columns.to_list(),
289
- "vertex_attr_types": self.attributes.dtypes.astype(str).to_list(),
290
- "xarray_attrs" : self.data.attrs
291
- }
292
- return header
293
-
294
- def _to_bytearray(self, order):
295
- vertex = self.vertex.astype('float32').tobytes(order)
296
- cells = self.cells.astype('int32').tobytes(order)
297
- cell_attribute = self.attributes.values.astype('float32').tobytes(order)
298
- vertex_attribute = self.points_attributes.values.astype('float32').tobytes(order)
299
- bytearray_le = vertex + cells + cell_attribute + vertex_attribute
300
- return bytearray_le
301
-
302
- def _validate(self):
303
- try:
304
- _ = self.data[self.cells_attr_name]['cell']
305
- _ = self.data[self.cells_attr_name]['cell_attr']
306
- except KeyError:
307
- raise KeyError('Cell attribute DataArrays must contain dimension cell and cell_attr')
308
- try:
309
- _ = self.data[self.vertex_attr_name]['vertex_attr']
310
- _ = self.data[self.vertex_attr_name]['points']
311
- except KeyError:
312
- raise KeyError('Point attribute DataArrays must contain dimensions points and vertex_attr.')
313
-
314
- # Make sure the number of vertices matches the associated data.
315
- if self.data['cells']['cell'].size != self.data[self.cells_attr_name]['cell'].size:
316
- raise AttributeError('Attributes and cells must have the same length.')
317
-
318
- if self.n_points != self.data[self.vertex_attr_name]['points'].size:
319
- raise AttributeError('points_attributes and vertex must have the same length.')
1
+ from dataclasses import dataclass
2
+ from typing import Union, Dict, Mapping, Hashable, Any, Literal
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import xarray as xr
7
+
8
+ from subsurface.core.structs.base_structures._unstructured_data_constructor import vertex_and_cells_arrays_to_data_array, raw_attributes_to_dict_data_arrays
9
+ from subsurface.core.structs.base_structures.base_structures_enum import SpecialCellCase
10
+
11
+
12
+ @dataclass(frozen=False)
13
+ class UnstructuredData:
14
+ data: xr.Dataset
15
+ cells_attr_name: str = "cell_attrs"
16
+ vertex_attr_name: str = "vertex_attrs"
17
+
18
+ """Primary structure definition for unstructured data
19
+
20
+ Attributes:
21
+ data (`xarray.Dataset`): Data structure where we store
22
+
23
+ Args:
24
+
25
+ ds (xarray.Dataset): Directly a dataset with the expected structured. This
26
+ arg is specially thought for loading data from disk
27
+
28
+ Notes:
29
+ Depending on the shape of `edge` the following unstructured elements can
30
+ be created:
31
+
32
+ - cells NDArray[(Any, 0), IntX] or NDArray[(Any, 1), IntX] -> *Point cloud*.
33
+ E.g. Outcrop scan with lidar
34
+ - cells NDArray[(Any, 2), IntX] -> *Lines*. E.g. Borehole
35
+ - cells NDArray[(Any, 3), IntX] -> *Mesh*. E.g surface-DEM Topography
36
+ - cells NDArray[(Any, 4), IntX]
37
+ - -> *tetrahedron*
38
+ - -> *quadrilateral (or tetragon)* UNSUPPORTED?
39
+ - cells NDArray[(Any, 8), IntX] -> *Hexahedron: Unstructured grid/Prisms*
40
+ """
41
+
42
+ def __post_init__(self):
43
+ self._validate()
44
+
45
+ def __repr__(self):
46
+ return self.data.__repr__()
47
+
48
+ @classmethod
49
+ def from_array(
50
+ cls,
51
+ vertex: np.ndarray,
52
+ cells: Union[np.ndarray, Literal["lines", "points"], SpecialCellCase],
53
+ *,
54
+ cells_attr: Union[None, pd.DataFrame, Dict[str, xr.DataArray]] = None,
55
+ vertex_attr: Union[None, pd.DataFrame, Dict[str, xr.DataArray]] = None,
56
+ coords: Mapping[Hashable, Any] = None,
57
+ xarray_attributes: Mapping[Hashable, Any] = None,
58
+ default_cells_attr_name: str = "cell_attrs",
59
+ default_points_attr_name: str = "vertex_attrs",
60
+ attributes: Union[None, pd.DataFrame, Dict[str, xr.DataArray]] = None # TODO Obsolete
61
+ ):
62
+ """ Constructor of UnstructuredData from arrays or pandas DataFrames.
63
+
64
+ Args:
65
+ vertex (np.ndarray): NDArray[(Any, 3), FloatX]: XYZ point data
66
+ cells (Union[np.ndarray, Literal["lines", "points"]]): NDArray[(Any, ...), IntX]:
67
+ Combination of vertex that create different geometric elements. If
68
+ str use default values for either points or lines
69
+ cells_attr (Union[None, pd.DataFrame, Dict[str, xr.DataArray]]: Number associated to an element
70
+ vertex_attr (Union[None, pd.DataFrame, Dict[str, xr.DataArray]]: Number
71
+ associated to points
72
+ coords:
73
+ xarray_attributes:
74
+ attributes:
75
+ default_cells_attr_name:
76
+ default_points_attr_name:
77
+
78
+ Returns:
79
+
80
+ """
81
+ if attributes is not None:
82
+ cells_attr = attributes
83
+
84
+ cells_data_array, n_cells, n_vertex, vertex_data_array = vertex_and_cells_arrays_to_data_array(
85
+ cells=cells,
86
+ vertex=vertex
87
+ )
88
+ points_attributes_xarray_dict: dict[str, xr.DataArray] = raw_attributes_to_dict_data_arrays(
89
+ default_attributes_name=default_points_attr_name,
90
+ n_items=n_vertex,
91
+ dims=["points", "vertex_attr"],
92
+ raw_attributes=vertex_attr
93
+ )
94
+
95
+ cells_attributes_xarray_dict: dict[str, xr.DataArray] = raw_attributes_to_dict_data_arrays(
96
+ default_attributes_name=default_cells_attr_name,
97
+ n_items=n_cells,
98
+ dims=["cell", "cell_attr"],
99
+ raw_attributes=cells_attr
100
+ )
101
+
102
+ xarray_dict = {
103
+ "vertex": vertex_data_array,
104
+ "cells" : cells_data_array,
105
+ **cells_attributes_xarray_dict,
106
+ **points_attributes_xarray_dict
107
+ }
108
+
109
+ return cls.from_data_arrays_dict(
110
+ xarray_dict=xarray_dict,
111
+ coords=coords,
112
+ xarray_attributes=xarray_attributes,
113
+ default_cells_attributes_name=default_cells_attr_name,
114
+ default_points_attributes_name=default_points_attr_name
115
+ )
116
+
117
+ @classmethod
118
+ def from_data_arrays_dict(
119
+ cls,
120
+ xarray_dict: Dict[str, xr.DataArray],
121
+ coords: Mapping[Hashable, Any] = None,
122
+ xarray_attributes: Mapping[Hashable, Any] = None,
123
+ default_cells_attributes_name="cell_attrs",
124
+ default_points_attributes_name="vertex_attrs"
125
+ ):
126
+ # TODO: xr.Dataset seems to have been changed with 2022.06. needs to be adapted for indexing
127
+ ds = xr.Dataset(xarray_dict, coords=coords, attrs=xarray_attributes)
128
+
129
+ # Try to unstack pandas dataframe if exist
130
+ # TODO: This is an issue in wells. If it is only there maybe we should move it there
131
+ try:
132
+ ds = ds.reset_index('cell')
133
+ except (KeyError, ValueError) as e:
134
+ print(f"{e} xarray dataset must include 'cell' key (KeyError) or xarray 'cell' has no index (ValueError).")
135
+
136
+ # Check that the Dataset data_vars names matches, "vertex", "cells", default_cells_attributes_name and default_points_attributes_name
137
+ # and raise an error pointing out which one is missing
138
+ _vars = [var in ds.data_vars for var in ["vertex", "cells", default_cells_attributes_name, default_points_attributes_name]]
139
+ if not all(_vars):
140
+ missing = ["vertex", "cells", default_cells_attributes_name, default_points_attributes_name]
141
+ raise KeyError(f"DataArray must include the following keys: {missing}. For attributes you can"
142
+ f" change the default names with default_cells_attributes_name and default_points_attributes_name.")
143
+
144
+ return cls(ds, default_cells_attributes_name, default_points_attributes_name)
145
+
146
+ @classmethod
147
+ def from_binary_le(cls, path: str):
148
+ from ._liquid_earth_mesh import LiquidEarthMesh
149
+ with open(path, 'rb') as f:
150
+ bytes_data = f.read()
151
+ mesh = LiquidEarthMesh.from_binary(bytes_data)
152
+ unstruct = cls.from_array(
153
+ vertex=mesh.vertex,
154
+ cells=mesh.cells,
155
+ cells_attr=mesh.attributes,
156
+ vertex_attr=mesh.points_attributes,
157
+ xarray_attributes=None
158
+ )
159
+ return unstruct
160
+
161
+ @classmethod
162
+ def from_binary_le_legacy(cls, path_to_binary: str, path_to_json: str):
163
+ import json
164
+ from ._liquid_earth_mesh import LiquidEarthMesh
165
+
166
+ with open(path_to_binary, 'rb') as f:
167
+ body_ = f.read()
168
+ with open(path_to_json, 'r') as f:
169
+ header_ = json.load(f)
170
+
171
+ header_json = json.dumps(header_)
172
+ header_json_bytes = header_json.encode('utf-8')
173
+ header_json_length = len(header_json_bytes)
174
+ header_json_length_bytes = header_json_length.to_bytes(4, byteorder='little')
175
+ file = header_json_length_bytes + header_json_bytes + body_
176
+ mesh = LiquidEarthMesh.from_binary(file)
177
+ unstruct = cls.from_array(
178
+ vertex=mesh.vertex,
179
+ cells=mesh.cells,
180
+ cells_attr=mesh.attributes,
181
+ vertex_attr=mesh.points_attributes,
182
+ xarray_attributes=None
183
+ )
184
+ return unstruct
185
+
186
+ @property
187
+ def vertex(self) -> np.ndarray:
188
+ return self.data['vertex'].values
189
+
190
+ @property
191
+ def cells(self):
192
+ return self.data['cells'].values
193
+
194
+ @property
195
+ def attributes(self) -> pd.DataFrame:
196
+ xarray = self.data[self.cells_attr_name]
197
+ return xarray.to_dataframe()[self.cells_attr_name].unstack(level=1)
198
+
199
+ @attributes.setter
200
+ def attributes(self, dataframe):
201
+ self.data[self.cells_attr_name] = xr.DataArray(dataframe, dims=['element', 'cell_attr'])
202
+
203
+ @property
204
+ def cell_attributes(self):
205
+ return self.attributes
206
+
207
+ @cell_attributes.setter
208
+ def cell_attributes(self, dataframe):
209
+ self.attributes = dataframe
210
+
211
+ @property
212
+ def points_attributes(self) -> pd.DataFrame:
213
+ data_array: xr.DataArray = self.data[self.vertex_attr_name]
214
+ dataframe: pd.DataFrame = data_array.to_dataframe()
215
+ not_suer = dataframe[self.vertex_attr_name]
216
+ unstack = not_suer.unstack(level=1)
217
+ return unstack
218
+
219
+ @points_attributes.setter
220
+ def points_attributes(self, dataframe: pd.DataFrame):
221
+ vertex_attr: xr.DataArray = self.data[self.vertex_attr_name]
222
+ vertex_attr.values = dataframe.values
223
+
224
+ @property
225
+ def n_elements(self):
226
+ return self.cells.shape[0]
227
+
228
+ @property
229
+ def n_vertex_per_element(self):
230
+ return self.cells.shape[1]
231
+
232
+ @property
233
+ def n_points(self):
234
+ return self.vertex.shape[0]
235
+
236
+ @property
237
+ def attributes_to_dict(
238
+ self,
239
+ orient: Literal["dict", "list", "series", "split", "tight", "index"] = "list"
240
+ ):
241
+ return self.attributes.to_dict(orient)
242
+
243
+ @property
244
+ def points_attributes_to_dict(
245
+ self,
246
+ orient: Literal["dict", "list", "series", "split", "tight", "index"] = "list"
247
+ ):
248
+ return self.points_attributes.to_dict(orient)
249
+
250
+ @property
251
+ def extent(self):
252
+ max = self.vertex.max(axis=0)
253
+ min = self.vertex.min(axis=0)
254
+ extent = np.stack((min, max), axis=1).ravel()
255
+ return extent
256
+
257
+ def to_xarray(self):
258
+ a = xr.DataArray(self.vertex, dims=['points', 'XYZ'])
259
+ b = xr.DataArray(self.cells, dims=['cells', 'node'])
260
+ e = xr.DataArray(self.attributes, dims=['element', 'cell_attr'])
261
+ c = xr.Dataset({'v': a, 'e': b, 'a': e})
262
+ return c
263
+
264
+ def to_binary_legacy(self, order='F'):
265
+ bytearray_le = self._to_bytearray(order)
266
+ header = self._set_binary_header()
267
+ return bytearray_le, header
268
+
269
+ def to_binary(self, order='F') -> bytes:
270
+ body_ = self._to_bytearray(order)
271
+ header_ = self._set_binary_header()
272
+ import json
273
+ header_json = json.dumps(header_)
274
+ header_json_bytes = header_json.encode('utf-8')
275
+ header_json_length = len(header_json_bytes)
276
+ header_json_length_bytes = header_json_length.to_bytes(4, byteorder='little')
277
+ file = header_json_length_bytes + header_json_bytes + body_
278
+ return file
279
+
280
+ def _set_binary_header(self):
281
+ header = {
282
+ "vertex_shape" : self.vertex.shape,
283
+ "cell_shape" : self.cells.shape,
284
+ "cell_attr_shape" : self.attributes.shape,
285
+ "vertex_attr_shape": self.points_attributes.shape,
286
+ "cell_attr_names" : self.attributes.columns.to_list(),
287
+ "cell_attr_types" : self.attributes.dtypes.astype(str).to_list(),
288
+ "vertex_attr_names": self.points_attributes.columns.to_list(),
289
+ "vertex_attr_types": self.attributes.dtypes.astype(str).to_list(),
290
+ "xarray_attrs" : self.data.attrs
291
+ }
292
+ return header
293
+
294
+ def _to_bytearray(self, order):
295
+ vertex = self.vertex.astype('float32').tobytes(order)
296
+ cells = self.cells.astype('int32').tobytes(order)
297
+ cell_attribute = self.attributes.values.astype('float32').tobytes(order)
298
+ vertex_attribute = self.points_attributes.values.astype('float32').tobytes(order)
299
+ bytearray_le = vertex + cells + cell_attribute + vertex_attribute
300
+ return bytearray_le
301
+
302
+ def _validate(self):
303
+ try:
304
+ _ = self.data[self.cells_attr_name]['cell']
305
+ _ = self.data[self.cells_attr_name]['cell_attr']
306
+ except KeyError:
307
+ raise KeyError('Cell attribute DataArrays must contain dimension cell and cell_attr')
308
+ try:
309
+ _ = self.data[self.vertex_attr_name]['vertex_attr']
310
+ _ = self.data[self.vertex_attr_name]['points']
311
+ except KeyError:
312
+ raise KeyError('Point attribute DataArrays must contain dimensions points and vertex_attr.')
313
+
314
+ # Make sure the number of vertices matches the associated data.
315
+ if self.data['cells']['cell'].size != self.data[self.cells_attr_name]['cell'].size:
316
+ raise AttributeError('Attributes and cells must have the same length.')
317
+
318
+ if self.n_points != self.data[self.vertex_attr_name]['points'].size:
319
+ raise AttributeError('points_attributes and vertex must have the same length.')
@@ -1,10 +1,10 @@
1
- from subsurface import StructuredData
2
-
3
-
4
- class OctreeMesh:
5
- """
6
- TODO: implement as Dom discussed with data frames to track the levels.
7
- """
8
-
9
- def __init__(self, data: StructuredData):
10
- raise NotImplementedError
1
+ from subsurface import StructuredData
2
+
3
+
4
+ class OctreeMesh:
5
+ """
6
+ TODO: implement as Dom discussed with data frames to track the levels.
7
+ """
8
+
9
+ def __init__(self, data: StructuredData):
10
+ raise NotImplementedError