subsurface-terra 2025.1.0rc16__py3-none-any.whl → 2025.1.0rc17__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.
subsurface/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2025.1.0rc16'
32
- __version_tuple__ = version_tuple = (2025, 1, 0, 'rc16')
31
+ __version__ = version = '2025.1.0rc17'
32
+ __version_tuple__ = version_tuple = (2025, 1, 0, 'rc17')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,69 @@
1
+ import pandas as pd
2
+
3
+
4
+ def safe_convert_to_float32(df: pd.DataFrame, error_handling: str = 'raise') -> pd.DataFrame:
5
+ """
6
+ Convert DataFrame columns to float32, handling non-convertible columns.
7
+
8
+ Args:
9
+ df: DataFrame to convert
10
+ error_handling: How to handle non-convertible columns:
11
+ - 'raise': Raise an error
12
+ - 'skip': Skip non-convertible columns
13
+ - 'drop': Drop non-convertible columns
14
+
15
+ Returns:
16
+ DataFrame with converted columns
17
+ """
18
+ convertible, non_convertible = _check_convertible_to_float32(df)
19
+
20
+ if non_convertible:
21
+ if error_handling == 'raise':
22
+ raise ValueError(
23
+ f"Cannot convert columns to float32: {non_convertible}. "
24
+ f"These columns contain non-numeric data."
25
+ )
26
+ elif error_handling == 'skip':
27
+ # Only convert the convertible columns
28
+ result = df.copy()
29
+ for col in convertible:
30
+ result[col] = df[col].astype('float32')
31
+ return result
32
+ elif error_handling == 'drop':
33
+ # Drop non-convertible columns
34
+ return df[convertible].astype('float32')
35
+ else:
36
+ raise ValueError(f"Invalid error_handling: {error_handling}")
37
+
38
+ return df.astype('float32')
39
+
40
+
41
+ def _check_convertible_to_float32(df: pd.DataFrame) -> tuple[list[str], list[str]]:
42
+ """
43
+ Check which columns in a DataFrame can be safely converted to float32.
44
+
45
+ Returns:
46
+ tuple: (convertible_columns, non_convertible_columns)
47
+ """
48
+ convertible = []
49
+ non_convertible = []
50
+
51
+ for col in df.columns:
52
+ if pd.api.types.is_numeric_dtype(df[col]):
53
+ # Already numeric, can convert
54
+ convertible.append(col)
55
+ elif pd.api.types.is_bool_dtype(df[col]):
56
+ # Boolean can be converted (True->1.0, False->0.0)
57
+ convertible.append(col)
58
+ elif pd.api.types.is_string_dtype(df[col]) or pd.api.types.is_object_dtype(df[col]):
59
+ # Try to convert to numeric
60
+ try:
61
+ pd.to_numeric(df[col], errors='raise')
62
+ convertible.append(col)
63
+ except (ValueError, TypeError):
64
+ non_convertible.append(col)
65
+ else:
66
+ # Other types (datetime, timedelta, categorical, etc.)
67
+ non_convertible.append(col)
68
+
69
+ return convertible, non_convertible
@@ -299,6 +299,25 @@ class UnstructuredData:
299
299
  bytearray_le = vertex + cells + cell_attribute + vertex_attribute
300
300
  return bytearray_le
301
301
 
302
+ def _to_bytearray(self, order):
303
+ from subsurface.core.structs.base_structures._aux import safe_convert_to_float32
304
+ vertex = self.vertex.astype('float32').tobytes(order)
305
+ cells = self.cells.astype('int32').tobytes(order)
306
+
307
+ # Only include numeric columns
308
+ cell_attribute = safe_convert_to_float32(
309
+ self.attributes,
310
+ error_handling='drop'
311
+ ).values.tobytes(order)
312
+
313
+ vertex_attribute = safe_convert_to_float32(
314
+ self.points_attributes,
315
+ error_handling='drop'
316
+ ).values.tobytes(order)
317
+
318
+ bytearray_le = vertex + cells + cell_attribute + vertex_attribute
319
+ return bytearray_le
320
+
302
321
  def _validate(self):
303
322
  try:
304
323
  _ = self.data[self.cells_attr_name]['cell']
@@ -106,10 +106,9 @@ class GridData:
106
106
  )
107
107
 
108
108
 
109
- from typing import Literal
110
109
 
111
110
  def read_msh_structured_grid(grid_stream: TextIO, values_stream: TextIO, missing_value: Optional[float],
112
- attr_name: Optional[str], ordering: Literal['ijk', 'xyz', 'xyz_reverse'] = 'ijk') -> StructuredData:
111
+ attr_name: Optional[str]) -> StructuredData:
113
112
  """
114
113
  Read a structured grid mesh and values from streams and return a StructuredData object.
115
114
 
@@ -121,12 +120,7 @@ def read_msh_structured_grid(grid_stream: TextIO, values_stream: TextIO, missing
121
120
  values_stream: TextIO stream containing the property values (.mod format)
122
121
  missing_value: Value to replace with NaN in the output array
123
122
  attr_name: Name for the data attribute
124
- ordering: Data ordering in the file:
125
- - 'ijk': i (x) varies fastest, then j (y), then k (z)
126
- - 'xyz': z varies fastest, then x, then y
127
- - 'xyz_reverse': z varies fastest (reversed), then x, then y
128
- Default is 'ijk'.
129
-
123
+
130
124
  Returns:
131
125
  StructuredData object containing the grid and property values
132
126
 
@@ -154,7 +148,7 @@ def read_msh_structured_grid(grid_stream: TextIO, values_stream: TextIO, missing
154
148
  # Read all values from the stream
155
149
  lines = [line.strip() for line in values_stream if line.strip()]
156
150
 
157
- model_array = _parse_mod_file(grid, lines, missing_value=missing_value, ordering=ordering)
151
+ model_array = _parse_mod_file(grid, lines, missing_value=missing_value)
158
152
 
159
153
  except Exception as e:
160
154
  # Add context to any errors
@@ -206,7 +200,7 @@ def read_msh_file(filepath: Union[str, Path]) -> GridData:
206
200
 
207
201
  def read_mod_file(filepath: Union[str, Path], grid: GridData,
208
202
  missing_value: float = -99_999.0,
209
- ordering: Literal['ijk', 'xyz', 'xyz_reverse'] = 'ijk') -> np.ndarray:
203
+ ) -> np.ndarray:
210
204
  """
211
205
  Read a model file containing property values for a 3D grid.
212
206
 
@@ -217,11 +211,6 @@ def read_mod_file(filepath: Union[str, Path], grid: GridData,
217
211
  filepath: Path to the model file
218
212
  grid: GridData object containing the grid dimensions
219
213
  missing_value: Value to replace with NaN in the output array (default: -99_999.0)
220
- ordering: Data ordering in the file. Options:
221
- - 'ijk': i (x) varies fastest, then j (y), then k (z) - standard VTK/Fortran ordering
222
- - 'xyz': z varies fastest, then x, then y - legacy Grav3D ordering
223
- - 'xyz_reverse': z varies fastest (reversed direction), then x, then y
224
- Default is 'ijk'.
225
214
 
226
215
  Returns:
227
216
  3D numpy array of property values with shape (ny, nx, nz)
@@ -239,7 +228,7 @@ def read_mod_file(filepath: Union[str, Path], grid: GridData,
239
228
  with open(filepath, 'r') as f:
240
229
  lines = [line.strip() for line in f if line.strip()]
241
230
 
242
- model_array = _parse_mod_file(grid, lines, missing_value, ordering)
231
+ model_array = _parse_mod_file(grid, lines, missing_value)
243
232
 
244
233
  return model_array
245
234
 
@@ -265,7 +254,7 @@ def structured_data_from(array: np.ndarray, grid: GridData,
265
254
  ValueError: If array shape doesn't match grid dimensions
266
255
  """
267
256
  # Verify array shape matches grid dimensions
268
- expected_shape = (grid.dimensions.ny, grid.dimensions.nx, grid.dimensions.nz)
257
+ expected_shape = (grid.dimensions.nx, grid.dimensions.ny, grid.dimensions.nz)
269
258
  if array.shape != expected_shape:
270
259
  raise ValueError(
271
260
  f"Array shape {array.shape} doesn't match grid dimensions {expected_shape}"
@@ -277,7 +266,7 @@ def structured_data_from(array: np.ndarray, grid: GridData,
277
266
  # Create the xarray DataArray with proper coordinates
278
267
  xr_data_array = xr.DataArray(
279
268
  data=array,
280
- dims=['y', 'x', 'z'], # Dimensions in the order they appear in the array
269
+ dims=['x', 'y', 'z'], # Dimensions in the order they appear in the array
281
270
  coords={
282
271
  'x': centers['x'],
283
272
  'y': centers['y'],
@@ -424,8 +413,7 @@ def _calculate_cell_centers(grid: GridData) -> Dict[str, np.ndarray]:
424
413
  }
425
414
 
426
415
 
427
- def _parse_mod_file(grid: GridData, lines: List[str], missing_value: Optional[float],
428
- ordering: Literal['ijk', 'xyz', 'xyz_reverse'] = 'ijk') -> np.ndarray:
416
+ def _parse_mod_file(grid: GridData, lines: List[str], missing_value: Optional[float]) -> np.ndarray:
429
417
  """
430
418
  Parse model file values into a 3D numpy array.
431
419
 
@@ -433,13 +421,9 @@ def _parse_mod_file(grid: GridData, lines: List[str], missing_value: Optional[fl
433
421
  grid: GridData object containing grid dimensions
434
422
  lines: List of lines containing the values
435
423
  missing_value: Value to replace with NaN
436
- ordering: Data ordering in the file:
437
- - 'ijk': i (x) varies fastest, then j (y), then k (z)
438
- - 'xyz': z varies fastest, then x, then y
439
- - 'xyz_reverse': z varies fastest (reversed), then x, then y
440
424
 
441
425
  Returns:
442
- 3D numpy array with shape (ny, nx, nz)
426
+ 3D numpy array with shape (nx, ny, nz)
443
427
  """
444
428
  # Convert each line to a float
445
429
  values = np.array([float(line) for line in lines], dtype=float)
@@ -453,23 +437,8 @@ def _parse_mod_file(grid: GridData, lines: List[str], missing_value: Optional[fl
453
437
  f"Invalid model file: expected {expected_count} values, got {len(values)}"
454
438
  )
455
439
 
456
- # Reshape based on ordering
457
- if ordering == 'ijk':
458
- # i (x) varies fastest, then j (y), then k (z)
459
- # This is standard VTK/Fortran ordering: (k, j, i) in array dimensions
460
- model_array = values.reshape((nz, ny, nx), order='C')
461
- # Transpose to (ny, nx, nz) to match expected output shape
462
- model_array = np.transpose(model_array, (1, 2, 0))
463
- elif ordering == 'xyz':
464
- # z varies fastest, then x, then y (legacy Grav3D ordering)
465
- model_array = values.reshape((ny, nx, nz))
466
- elif ordering == 'xyz_reverse':
467
- # z varies fastest (but in reverse direction), then x, then y
468
- model_array = values.reshape((ny, nx, nz))
469
- # Reverse the z-axis (last dimension)
470
- model_array = np.flip(model_array, axis=2)
471
- else:
472
- raise ValueError(f"Invalid ordering: {ordering}. Must be 'ijk', 'xyz', or 'xyz_reverse'")
440
+ model_array = values.reshape((nz, nx, ny), order='F')
441
+ model_array = np.transpose(model_array, (1, 2, 0))[:, :, ::-1]
473
442
 
474
443
  # Replace missing values with NaN
475
444
  if missing_value is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: subsurface_terra
3
- Version: 2025.1.0rc16
3
+ Version: 2025.1.0rc17
4
4
  Summary: Subsurface data types and utilities. This version is the one used by Terranigma Solutions. Please feel free to take anything in this repository for the original one.
5
5
  Home-page: https://softwareunderground.github.io/subsurface
6
6
  Author: Software Underground
@@ -1,5 +1,5 @@
1
1
  subsurface/__init__.py,sha256=7_KttQl8rZeJG2AIZUEA_oXRWZYkJ6DUqKjqlLgF9cA,899
2
- subsurface/_version.py,sha256=nb8P61QwldK25wR1CNZGY__gFCsYePG-ZOEu5h5BQFg,722
2
+ subsurface/_version.py,sha256=gsxkMYFwv9EtAE0vhlbi1rqKVeK7ATfhxTYuf25JQK8,722
3
3
  subsurface/optional_requirements.py,sha256=SLaMSoiGTx8ea8rpHee4Zs4te0BXcxxDWC4SF0g0RuE,2767
4
4
  subsurface/api/__init__.py,sha256=Lfx7Dq0GzQsea19w8YO4v1ZSmgeLRgR6IvjvDDjjpDY,356
5
5
  subsurface/api/interfaces/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
@@ -22,11 +22,12 @@ subsurface/core/reader_helpers/readers_data.py,sha256=3NEB4A48mBBUx9XM5NRZeavCn5
22
22
  subsurface/core/reader_helpers/readers_wells.py,sha256=7ja2UJRe2oB3a4LReDfrt6rAEdNnFyUB9-pBqFKfYUc,392
23
23
  subsurface/core/structs/__init__.py,sha256=e0kDVRU-5aHrVGnHrjplXDr_HFcIAnd_coFC5cc-ytg,172
24
24
  subsurface/core/structs/base_structures/__init__.py,sha256=K3eZixAH9bbN156a7OS9mS-qd3pmBZrrOn1y9ISTHJI,92
25
+ subsurface/core/structs/base_structures/_aux.py,sha256=lJ40ELPHoZRDvXjBZd1j3r_ajtzt5q9IoDJYmcdGH1U,2415
25
26
  subsurface/core/structs/base_structures/_liquid_earth_mesh.py,sha256=ZIGLdxbOyC0avIXR6w5JMhk08JtPKJDnGd2Athtx7DM,5729
26
27
  subsurface/core/structs/base_structures/_unstructured_data_constructor.py,sha256=nSsmRqyGyrU5DKb0YQKy6FwZm6GVBTPwwTLZJsXPcyk,2671
27
28
  subsurface/core/structs/base_structures/base_structures_enum.py,sha256=_B1rsaTnPw6mJhYea0ZIfKmaF3mfkojh-3N5hGG6o5w,90
28
29
  subsurface/core/structs/base_structures/structured_data.py,sha256=N6KcAeZJSfd4Wa0PID8aOcw9IIXrNRZ_DspBzF8JPTY,10310
29
- subsurface/core/structs/base_structures/unstructured_data.py,sha256=bBorNn800ttGMk8MGIgcE2nv8XT6p0FYetpWxShNqlQ,12585
30
+ subsurface/core/structs/base_structures/unstructured_data.py,sha256=-o7XIq886MU_uJHCLFCFklQLNjyLeAx05-cAFMT5y9w,13274
30
31
  subsurface/core/structs/structured_elements/__init__.py,sha256=wQ8z6vioC98TNwnqMHlDMVFJ1yOKsmlVa6kHL1SOfZQ,43
31
32
  subsurface/core/structs/structured_elements/octree_mesh.py,sha256=3WIgKGPJ3xjjV06gGlLuW7W7POfsJi2z7p4egYD-Y7s,230
32
33
  subsurface/core/structs/structured_elements/structured_grid.py,sha256=S08sbGOu-r-rosHm6fwpqZgmkujxMMev7Z8WAwlsuak,1743
@@ -62,7 +63,7 @@ subsurface/modules/reader/profiles/profiles_core.py,sha256=tBvNgdvrpUWRIJv4aW3YY
62
63
  subsurface/modules/reader/topography/__init__.py,sha256=U41kQFNPpfYV6KJpMnkqgqLkozqXiG4tgV6rj8IW1BU,7
63
64
  subsurface/modules/reader/topography/topo_core.py,sha256=EzcRFJ_zBojVtZkZHbYaUpEIyFUiVaf6zeSWMuyAxxk,3442
64
65
  subsurface/modules/reader/volume/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
- subsurface/modules/reader/volume/read_grav3d.py,sha256=0PNPoWuMAH-5B2KBWUdNLgcnxcSMbaVY0v21KgSllmY,16406
66
+ subsurface/modules/reader/volume/read_grav3d.py,sha256=5-g65mOMYiOiLqGu3_foDZDGFUIiavkKFC7uZ99v4Dk,14493
66
67
  subsurface/modules/reader/volume/read_volume.py,sha256=fg928kOBWxGgWnrLdeOvHDRr54cM5nxb_65-lM1BVsk,13581
67
68
  subsurface/modules/reader/volume/segy_reader.py,sha256=Yd1fguaKMrgv514Yc8tBm1NxDhWNtI1qOYWWTF_TgPU,3853
68
69
  subsurface/modules/reader/volume/seismic.py,sha256=Iy5zKpjVy7vV6lKyfh_MsUpex6yBMGbovu640NlKkMM,4777
@@ -91,8 +92,8 @@ subsurface/modules/writer/to_rex/material_encoder.py,sha256=J5x1PCW2ljw4JZXLvefG
91
92
  subsurface/modules/writer/to_rex/mesh_encoder.py,sha256=dTGqHGbJKgtPgrrG2lrks5r8timfzluqj7JLMC-EBWQ,6291
92
93
  subsurface/modules/writer/to_rex/to_rex.py,sha256=dFCCWudTUzZebNrPxnM353_vaykE8PQobVtBAPQkb3E,3685
93
94
  subsurface/modules/writer/to_rex/utils.py,sha256=BmMT49WRvbbSuiW_muIvFm4ILR1vcVqh_J23leiMB44,364
94
- subsurface_terra-2025.1.0rc16.dist-info/licenses/LICENSE,sha256=QEY_TMj0mh5hY6jh6avflZOz0GmH1PgurVoF3FvwXaY,11403
95
- subsurface_terra-2025.1.0rc16.dist-info/METADATA,sha256=rPt_SUuyoUbI9IAhvUGH-s1GJcqwavwwoK0KLaPy3NI,6900
96
- subsurface_terra-2025.1.0rc16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
97
- subsurface_terra-2025.1.0rc16.dist-info/top_level.txt,sha256=f32R_tUSf83CfkpB4vjv5m2XcD8TmDX9h7F4rnEXt5A,11
98
- subsurface_terra-2025.1.0rc16.dist-info/RECORD,,
95
+ subsurface_terra-2025.1.0rc17.dist-info/licenses/LICENSE,sha256=QEY_TMj0mh5hY6jh6avflZOz0GmH1PgurVoF3FvwXaY,11403
96
+ subsurface_terra-2025.1.0rc17.dist-info/METADATA,sha256=KdRIAeSl2985ykJvSweVD9Gn1mnwbSnMmiLzH8e_yeg,6900
97
+ subsurface_terra-2025.1.0rc17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
98
+ subsurface_terra-2025.1.0rc17.dist-info/top_level.txt,sha256=f32R_tUSf83CfkpB4vjv5m2XcD8TmDX9h7F4rnEXt5A,11
99
+ subsurface_terra-2025.1.0rc17.dist-info/RECORD,,