Skip to content

Commit 2e59670

Browse files
committed
[ENH] Improve binary deserialization and grid reconstruction
Added support for handling orientation binary lengths and grid-specific binary deserialization, including custom grid and topography data. Enhanced grid metadata processing and validation to enable accurate model reconstruction.
1 parent 59edc16 commit 2e59670

File tree

6 files changed

+95
-5
lines changed

6 files changed

+95
-5
lines changed

gempy/core/data/encoders/binary_encoder.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,73 @@
44
from ..orientations import OrientationsTable
55

66

7-
def deserialize_input_data_tables(binary_array: bytes, name_id_map: dict, sp_binary_length_: int) -> tuple[OrientationsTable, SurfacePointsTable]:
7+
def deserialize_input_data_tables(binary_array: bytes, name_id_map: dict,
8+
sp_binary_length_: int, ori_binary_length_: int) -> tuple[OrientationsTable, SurfacePointsTable]:
9+
"""
10+
Deserializes binary data into two tables: OrientationsTable and SurfacePointsTable.
11+
12+
This function takes a binary array, a mapping of names to IDs, and lengths for
13+
specific parts of the binary data to extract and deserialize two distinct data
14+
tables: OrientationsTable and SurfacePointsTable. It uses the provided lengths
15+
to split the binary data accordingly and reconstructs the table contents from
16+
their respective binary representations.
17+
18+
Args:
19+
binary_array (bytes): A bytes array containing the serialized data for
20+
both the OrientationsTable and SurfacePointsTable.
21+
name_id_map (dict): A dictionary mapping names to IDs which is used to
22+
help reconstruct the table objects.
23+
sp_binary_length_ (int): The length of the binary segment corresponding
24+
to the SurfacePointsTable data.
25+
ori_binary_length_ (int): The length of the binary segment corresponding
26+
to the OrientationsTable data.
27+
28+
Returns:
29+
tuple[OrientationsTable, SurfacePointsTable]: A tuple containing two table
30+
objects: first the OrientationsTable, and second the SurfacePointsTable.
31+
"""
832
sp_binary = binary_array[:sp_binary_length_]
9-
ori_binary = binary_array[sp_binary_length_:]
33+
ori_binary = binary_array[sp_binary_length_:sp_binary_length_+ori_binary_length_]
1034
# Reconstruct arrays
1135
sp_data: np.ndarray = np.frombuffer(sp_binary, dtype=SurfacePointsTable.dt)
1236
ori_data: np.ndarray = np.frombuffer(ori_binary, dtype=OrientationsTable.dt)
1337
surface_points_table = SurfacePointsTable(data=sp_data, name_id_map=name_id_map)
1438
orientations_table = OrientationsTable(data=ori_data, name_id_map=name_id_map)
1539
return orientations_table, surface_points_table
40+
41+
42+
def deserialize_grid(binary_array:bytes, custom_grid_length: int, topography_length: int) -> tuple[np.ndarray, np.ndarray]:
43+
"""
44+
Deserialize binary grid data into two numpy arrays.
45+
46+
This function takes a binary array representing a grid and splits it into two separate
47+
numpy arrays: one for the custom grid and one for the topography. The binary array is
48+
segmented based on the provided lengths for the custom grid and topography.
49+
50+
Args:
51+
binary_array: The binary data representing the combined custom grid and topography data.
52+
custom_grid_length: The length of the custom grid data segment in bytes.
53+
topography_length: The length of the topography data segment in bytes.
54+
55+
Returns:
56+
A tuple where the first element is a numpy array representing the custom grid, and
57+
the second element is a numpy array representing the topography data.
58+
59+
Raises:
60+
ValueError: If input lengths do not match the specified boundaries or binary data.
61+
"""
62+
63+
total_length = len(binary_array)
64+
custom_grid_start = total_length - custom_grid_length - topography_length
65+
custom_grid_end = total_length - topography_length
66+
67+
topography_grid_start = total_length - topography_length
68+
topography_grid_end = total_length
69+
70+
custom_grid_binary = binary_array[custom_grid_start:custom_grid_end]
71+
topography_binary = binary_array[topography_grid_start:topography_grid_end]
72+
custom_grid = np.frombuffer(custom_grid_binary)
73+
topography = np.frombuffer(topography_binary)
74+
75+
76+
return custom_grid, topography

gempy/core/data/grid.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from gempy_engine.core.data.centered_grid import CenteredGrid
99
from gempy_engine.core.data.options import EvaluationOptions
1010
from gempy_engine.core.data.transforms import Transform
11+
from .encoders.binary_encoder import deserialize_grid
12+
from .encoders.converters import loading_model_context
1113
from .grid_modules import RegularGrid, CustomGrid, Sections
1214
from .grid_modules.topography import Topography
1315

@@ -61,6 +63,24 @@ def deserialize_properties(cls, data: Union["Grid", dict], constructor: ModelWra
6163
grid._active_grids = Grid.GridTypes(data["active_grids"])
6264
# TODO: Digest binary data
6365

66+
metadata = data.get('binary_meta_data', {})
67+
context = loading_model_context.get()
68+
69+
if 'binary_body' not in context:
70+
return grid
71+
72+
custom_grid_vals, topography_vals = deserialize_grid(
73+
binary_array=context['binary_body'],
74+
custom_grid_length=metadata["custom_grid_binary_length"],
75+
topography_length=metadata["topography_binary_length"]
76+
)
77+
78+
if grid.custom_grid is not None:
79+
grid.custom_grid.values = custom_grid_vals.reshape(-1, 3)
80+
81+
if grid.topography is not None:
82+
grid.topography.set_values2d(values=topography_vals)
83+
6484
grid._update_values()
6585
return grid
6686
case _:

gempy/core/data/grid_modules/topography.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ def set_values(self, values_2d: np.ndarray):
154154
# n,3 array
155155
self.values = values_2d.reshape((-1, 3), order='C')
156156
return self
157+
158+
def set_values2d(self, values: np.ndarray):
159+
self.values_2d = values.reshape(self.resolution)
157160

158161
@property
159162
def topography_mask(self):

gempy/core/data/structural_frame.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,8 @@ def deserialize_binary(cls, data: Union["StructuralFrame", dict], constructor: M
482482
instance.orientations, instance.surface_points = deserialize_input_data_tables(
483483
binary_array=context['binary_body'],
484484
name_id_map=instance.surface_points_copy.name_id_map,
485-
sp_binary_length_=metadata["sp_binary_length"]
485+
sp_binary_length_=metadata["sp_binary_length"],
486+
ori_binary_length_=metadata["ori_binary_length"]
486487
)
487488

488489
return instance

test/test_modules/test_grids/test_custom_grid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def test_custom_grid():
3939
file_name=f"verify/{geo_model.meta.name}"
4040
)
4141

42-
sol: gp.data.Solutions = gp.compute_model(geo_model, validate_serialization=False)
42+
sol: gp.data.Solutions = gp.compute_model(geo_model, validate_serialization=True)
4343
np.testing.assert_array_equal(
4444
sol.raw_arrays.custom,
4545
np.array([3., 3., 3., 3., 1., 1., 1., 1.])

test/test_modules/test_grids/test_custom_grid.test_custom_grid.verify/fold.approved.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"is_dirty": true,
6363
"basement_color": "#ffbe00",
6464
"binary_meta_data": {
65-
"sp_binary_length": 1296
65+
"sp_binary_length": 1296,
66+
"ori_binary_length": 120
6667
}
6768
},
6869
"grid": {
@@ -89,6 +90,10 @@
8990
"_centered_grid": null,
9091
"_transform": null,
9192
"_octree_levels": -1,
93+
"binary_meta_data": {
94+
"custom_grid_binary_length": 192,
95+
"topography_binary_length": 0
96+
},
9297
"active_grids": 1029
9398
},
9499
"geophysics_input": null,

0 commit comments

Comments
 (0)