Skip to content

Commit a4126b2

Browse files
committed
[ENH][TEST] Improve serialization validation and update tests
Add detailed validation for serialized GeoModel instances, including debugging for string differences and hash checks. Introduce `verify_model_serialization` with pre/post-deserialization checks, and update gravity tests for structured verification and data type consistency.
1 parent d79b957 commit a4126b2

File tree

3 files changed

+235
-15
lines changed

3 files changed

+235
-15
lines changed

gempy/modules/serialization/save_load.py

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Literal
2+
13
import warnings
24

35
from ...core.data import GeoModel
@@ -23,10 +25,10 @@ def save_model(model: GeoModel, path: str | None = None, validate_serialization:
2325
ValueError
2426
If the file has an extension other than .gempy
2527
"""
26-
28+
2729
# Warning about preview
2830
warnings.warn("This function is still in development. It may not work as expected.")
29-
31+
3032
# Define the valid extension for gempy models
3133
VALID_EXTENSION = ".gempy"
3234
if path is None:
@@ -89,7 +91,7 @@ def load_model(path: str) -> GeoModel:
8991

9092
# Warning about preview
9193
warnings.warn("This function is still in development. It may not work as expected.")
92-
94+
9395
VALID_EXTENSION = ".gempy"
9496

9597
# Check if path has the valid extension
@@ -131,10 +133,70 @@ def _to_binary(header_json, body_) -> bytes:
131133

132134

133135
def _validate_serialization(original_model, model_deserialized):
136+
if False:
137+
_verify_models(model_deserialized, original_model)
138+
134139
a = hash(original_model.structural_frame.surface_points_copy.data.tobytes())
135140
b = hash(model_deserialized.structural_frame.surface_points_copy.data.tobytes())
136141
o_a = hash(original_model.structural_frame.orientations_copy.data.tobytes())
137142
o_b = hash(model_deserialized.structural_frame.orientations_copy.data.tobytes())
138143
assert a == b, "Hashes for surface points are not equal"
139144
assert o_a == o_b, "Hashes for orientations are not equal"
140-
assert model_deserialized.__str__() == original_model.__str__()
145+
original_model___str__ = original_model.__str__()
146+
deserialized___str__ = model_deserialized.__str__()
147+
if original_model___str__ != deserialized___str__:
148+
# Find first char that is not the same
149+
for i in range(min(len(original_model___str__), len(deserialized___str__))):
150+
if original_model___str__[i] != deserialized___str__[i]:
151+
break
152+
print(f"First difference at index {i}:")
153+
i1 = 50
154+
print(f"Original: {original_model___str__[i - i1:i + i1]}")
155+
print(f"Deserialized: {deserialized___str__[i - i1:i + i1]}")
156+
157+
assert deserialized___str__ == original_model___str__
158+
159+
160+
def verify_model_serialization(model: GeoModel, verify_moment: Literal["before", "after"], file_name: str):
161+
"""
162+
Verifies the serialization and deserialization process of a GeoModel instance
163+
by ensuring the serialized JSON and binary data match during either the
164+
initial or post-process phase, based on the specified verification moment.
165+
166+
Args:
167+
model: The GeoModel instance to be verified.
168+
verify_moment: A literal value specifying whether to verify the model
169+
before or after the deserialization process. Accepts "before"
170+
or "after" as valid inputs.
171+
file_name: The filename to associate with the verification process for
172+
logging or output purposes.
173+
174+
Raises:
175+
ValueError: If `verify_moment` is not set to "before" or "after".
176+
"""
177+
model_json = model.model_dump_json(by_alias=True, indent=4)
178+
179+
# Compress the binary data
180+
zlib = require_zlib()
181+
compressed_binary = zlib.compress(model.structural_frame.input_tables_binary)
182+
183+
binary_file = _to_binary(model_json, compressed_binary)
184+
185+
model_deserialized = _deserialize_binary_file(binary_file)
186+
187+
original_model = model
188+
original_model.meta.creation_date = "<DATE_IGNORED>"
189+
model_deserialized.meta.creation_date = "<DATE_IGNORED>"
190+
from verify_helper import verify_json
191+
if verify_moment == "before":
192+
verify_json(
193+
item=original_model.model_dump_json(by_alias=True, indent=4),
194+
name=file_name
195+
)
196+
elif verify_moment == "after":
197+
verify_json(
198+
item=model_deserialized.model_dump_json(by_alias=True, indent=4),
199+
name=file_name
200+
)
201+
else:
202+
raise ValueError("Invalid model parameter")

test/test_modules/_geophysics_TO_UPDATE/test_gravity.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# Importing auxiliary libraries
55
import numpy as np
66

7+
from gempy.modules.serialization.save_load import verify_model_serialization
8+
79

810
def test_gravity():
911
color_generator = gp.data.ColorsGenerator()
@@ -64,9 +66,9 @@ def test_gravity():
6466

6567
gp.set_centered_grid(
6668
grid=geo_model.grid,
67-
centers=np.array([[6, 0, 4]]),
68-
resolution=np.array([10, 10, 100]),
69-
radius=np.array([16000, 16000, 16000]) # ? This radius makes 0 sense but it is the original one in gempy v2
69+
centers=np.array([[6, 0, 4]], dtype="float"),
70+
resolution=np.array([10, 10, 100], dtype="float"),
71+
radius=np.array([16000, 16000, 16000], dtype="float") # ? This radius makes 0 sense but it is the original one in gempy v2
7072
)
7173

7274
gravity_gradient = gp.calculate_gravity_gradient(geo_model.grid.centered_grid)
@@ -75,15 +77,12 @@ def test_gravity():
7577
densities=np.array([2.6, 2.4, 3.2]),
7678
)
7779

78-
model_json = geo_model.model_dump_json(by_alias=True, indent=4)
79-
80-
return
81-
from pydantic_core import from_json
82-
83-
json = from_json(model_json, allow_partial=True)
84-
model_deserialized = gp.data.GeoModel.model_validate(json)
80+
verify_model_serialization(
81+
model=geo_model,
82+
verify_moment="after",
83+
file_name=f"verify/{geo_model.meta.name}"
84+
)
8585

86-
return
8786
gp.compute_model(geo_model)
8887

8988
print(geo_model.solutions.gravity)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
{
2+
"meta": {
3+
"name": "2-layers",
4+
"creation_date": "<DATE_IGNORED>",
5+
"last_modification_date": null,
6+
"owner": null
7+
},
8+
"structural_frame": {
9+
"structural_groups": [
10+
{
11+
"name": "default",
12+
"elements": [
13+
{
14+
"name": "surface1",
15+
"is_active": true,
16+
"_color": "#015482",
17+
"surface_points": {
18+
"name_id_map": {
19+
"surface1": 57292991
20+
},
21+
"_model_transform": null
22+
},
23+
"orientations": {
24+
"name_id_map": {
25+
"surface1": 57292991
26+
},
27+
"_model_transform": null
28+
},
29+
"scalar_field_at_interface": null,
30+
"_id": -1
31+
},
32+
{
33+
"name": "surface2",
34+
"is_active": true,
35+
"_color": "#9f0052",
36+
"surface_points": {
37+
"name_id_map": {
38+
"surface2": 21816406
39+
},
40+
"_model_transform": null
41+
},
42+
"orientations": {
43+
"name_id_map": null,
44+
"_model_transform": null
45+
},
46+
"scalar_field_at_interface": null,
47+
"_id": -1
48+
}
49+
],
50+
"structural_relation": 1,
51+
"fault_relations": null,
52+
"faults_input_data": null,
53+
"solution": null
54+
}
55+
],
56+
"is_dirty": true,
57+
"basement_color": "#ffbe00",
58+
"binary_meta_data": {
59+
"sp_binary_length": 144
60+
}
61+
},
62+
"grid": {
63+
"_octree_grid": null,
64+
"_dense_grid": {
65+
"resolution": [
66+
500,
67+
1,
68+
500
69+
],
70+
"extent": [
71+
0.0,
72+
12.0,
73+
-2.0,
74+
2.0,
75+
0.0,
76+
4.0
77+
],
78+
"_transform": null
79+
},
80+
"_custom_grid": null,
81+
"_topography": null,
82+
"_sections": null,
83+
"_centered_grid": {
84+
"centers": [
85+
[
86+
6.0,
87+
0.0,
88+
4.0
89+
]
90+
],
91+
"resolution": [
92+
10.0,
93+
10.0,
94+
100.0
95+
],
96+
"radius": [
97+
16000.0,
98+
16000.0,
99+
16000.0
100+
]
101+
},
102+
"_transform": null,
103+
"_octree_levels": -1,
104+
"active_grids": 1058
105+
},
106+
"input_transform": {
107+
"position": [
108+
-6.0,
109+
-0.0,
110+
-2.51
111+
],
112+
"rotation": [
113+
0.0,
114+
0.0,
115+
0.0
116+
],
117+
"scale": [
118+
0.08333333333333333,
119+
0.16778523489932887,
120+
0.08333333333333333
121+
],
122+
"_is_default_transform": false,
123+
"_cached_pivot": null
124+
},
125+
"_interpolation_options": {
126+
"kernel_options": {
127+
"range": 1.7,
128+
"c_o": 10.0,
129+
"uni_degree": 1,
130+
"i_res": 4.0,
131+
"gi_res": 2.0,
132+
"number_dimensions": 3,
133+
"kernel_function": "cubic",
134+
"kernel_solver": 1,
135+
"compute_condition_number": false,
136+
"optimizing_condition_number": false,
137+
"condition_number": null
138+
},
139+
"evaluation_options": {
140+
"_number_octree_levels": 1,
141+
"_number_octree_levels_surface": 4,
142+
"octree_curvature_threshold": -1.0,
143+
"octree_error_threshold": 1.0,
144+
"octree_min_level": 2,
145+
"mesh_extraction": true,
146+
"mesh_extraction_masking_options": 3,
147+
"mesh_extraction_fancy": true,
148+
"evaluation_chunk_size": 500000,
149+
"compute_scalar_gradient": false,
150+
"verbose": false
151+
},
152+
"debug": true,
153+
"cache_mode": 3,
154+
"cache_model_name": "2-layers",
155+
"block_solutions_type": 2,
156+
"sigmoid_slope": 5000000,
157+
"debug_water_tight": false
158+
}
159+
}

0 commit comments

Comments
 (0)