10
10
11
11
from .schema import SurfacePoint , Orientation , GemPyModelJson
12
12
from gempy_engine .core .data .stack_relation_type import StackRelationType
13
- from gempy .core .data .structural_element import StructuralElement
14
13
15
14
16
15
class JsonIO :
@@ -44,8 +43,6 @@ def load_model_from_json(file_path: str):
44
43
from gempy .core .data .structural_frame import StructuralFrame
45
44
from gempy_engine .core .data import InterpolationOptions
46
45
from gempy .API .map_stack_to_surfaces_API import map_stack_to_surfaces
47
- from gempy_engine .core .data .stack_relation_type import StackRelationType
48
- from gempy .core .data .structural_group import StructuralGroup
49
46
50
47
with open (file_path , 'r' ) as f :
51
48
data = json .load (f )
@@ -54,30 +51,52 @@ def load_model_from_json(file_path: str):
54
51
if not JsonIO ._validate_json_schema (data ):
55
52
raise ValueError ("Invalid JSON schema" )
56
53
57
- # Create a mapping from surface IDs to names
58
- id_to_name = {}
54
+ # Get surface names from series data
55
+ surface_names = []
59
56
for series in data ['series' ]:
60
- for surface_name in series ['surfaces' ]:
61
- # Find the ID for this surface name from surface points
62
- for sp in data ['surface_points' ]:
63
- if sp ['id' ] not in id_to_name :
64
- id_to_name [sp ['id' ]] = surface_name
57
+ surface_names .extend (series ['surfaces' ])
58
+
59
+ # Create a mapping from surface points to their names
60
+ surface_point_names = {}
61
+ for sp in data ['surface_points' ]:
62
+ # Find the surface name that corresponds to this ID
63
+ surface_name = None
64
+ for series in data ['series' ]:
65
+ for i , name in enumerate (series ['surfaces' ]):
66
+ if i == sp ['id' ]: # Match the ID with the index in the series
67
+ surface_name = name
65
68
break
69
+ if surface_name is not None :
70
+ break
71
+ surface_point_names [sp ['id' ]] = surface_name if surface_name is not None else f"surface_{ sp ['id' ]} "
72
+
73
+ # Load surface points and orientations
74
+ surface_points = JsonIO ._load_surface_points (data ['surface_points' ], surface_point_names )
75
+ orientations = JsonIO ._load_orientations (data ['orientations' ], surface_point_names )
76
+
77
+ # Create structural frame
78
+ structural_frame = StructuralFrame .from_data_tables (surface_points , orientations )
79
+
80
+ # Create grid
81
+ grid = Grid (
82
+ extent = data ['grid_settings' ]['regular_grid_extent' ],
83
+ resolution = data ['grid_settings' ]['regular_grid_resolution' ]
84
+ )
85
+
86
+ # Create interpolation options with kernel options
87
+ interpolation_options = InterpolationOptions (
88
+ range = data ['interpolation_options' ].get ('kernel_options' , {}).get ('range' , 1.7 ),
89
+ c_o = data ['interpolation_options' ].get ('kernel_options' , {}).get ('c_o' , 10 ),
90
+ mesh_extraction = data ['interpolation_options' ].get ('mesh_extraction' , True ),
91
+ number_octree_levels = data ['interpolation_options' ].get ('number_octree_levels' , 1 )
92
+ )
66
93
67
- # Create GeoModel with default structural frame
94
+ # Create GeoModel
68
95
model = GeoModel (
69
96
name = data ['metadata' ]['name' ],
70
- structural_frame = StructuralFrame .initialize_default_structure (),
71
- grid = Grid (
72
- extent = data ['grid_settings' ]['regular_grid_extent' ],
73
- resolution = data ['grid_settings' ]['regular_grid_resolution' ]
74
- ),
75
- interpolation_options = InterpolationOptions (
76
- range = data ['interpolation_options' ].get ('kernel_options' , {}).get ('range' , 1.7 ),
77
- c_o = data ['interpolation_options' ].get ('kernel_options' , {}).get ('c_o' , 10 ),
78
- mesh_extraction = data ['interpolation_options' ].get ('mesh_extraction' , True ),
79
- number_octree_levels = data ['interpolation_options' ].get ('number_octree_levels' , 1 )
80
- )
97
+ structural_frame = structural_frame ,
98
+ grid = grid ,
99
+ interpolation_options = interpolation_options
81
100
)
82
101
83
102
# Set the metadata with proper dates
@@ -89,34 +108,15 @@ def load_model_from_json(file_path: str):
89
108
)
90
109
model .meta = model_meta
91
110
92
- # Load surface points and orientations with the ID to name mapping
93
- surface_points = JsonIO ._load_surface_points (data ['surface_points' ], id_to_name )
94
- orientations = JsonIO ._load_orientations (data ['orientations' ], id_to_name )
95
-
96
- # Create structural groups based on series data
97
- model .structural_frame .structural_groups = []
98
- for i , series in enumerate (data ['series' ]):
99
- group = StructuralGroup (
100
- name = series ['name' ],
101
- elements = [],
102
- structural_relation = StackRelationType [series .get ('structural_relation' , 'ERODE' )]
103
- )
104
- model .structural_frame .structural_groups .append (group )
105
-
106
- # Add elements to the group
107
- for surface_name in series ['surfaces' ]:
108
- element = StructuralElement (
109
- name = surface_name ,
110
- id = len (group .elements ),
111
- surface_points = surface_points .get_surface_points_by_name (surface_name ),
112
- orientations = orientations .get_orientations_by_name (surface_name ),
113
- color = next (model .structural_frame .color_generator )
114
- )
115
- group .append_element (element )
111
+ # Map series to surfaces with structural relations
112
+ mapping_object = {series ['name' ]: series ['surfaces' ] for series in data ['series' ]}
113
+ map_stack_to_surfaces (model , mapping_object , series_data = data ['series' ])
116
114
117
- # Ensure the last group has the correct structural relation for the basement
118
- if model .structural_frame .structural_groups :
119
- model .structural_frame .structural_groups [- 1 ].structural_relation = StackRelationType .BASEMENT
115
+ # Set fault relations after structural groups are set up
116
+ if 'fault_relations' in data and data ['fault_relations' ] is not None :
117
+ fault_relations = np .array (data ['fault_relations' ])
118
+ if fault_relations .shape == (len (model .structural_frame .structural_groups ), len (model .structural_frame .structural_groups )):
119
+ model .structural_frame .fault_relations = fault_relations
120
120
121
121
return model
122
122
@@ -131,10 +131,26 @@ def _load_surface_points(surface_points_data: List[SurfacePoint], id_to_name: Op
131
131
132
132
Returns:
133
133
SurfacePointsTable: A new SurfacePointsTable instance
134
+
135
+ Raises:
136
+ ValueError: If the data is invalid or missing required fields
134
137
"""
135
138
# Import here to avoid circular imports
136
139
from gempy .core .data .surface_points import SurfacePointsTable
137
140
141
+ # Validate data structure
142
+ required_fields = {'x' , 'y' , 'z' , 'nugget' , 'id' }
143
+ for i , sp in enumerate (surface_points_data ):
144
+ missing_fields = required_fields - set (sp .keys ())
145
+ if missing_fields :
146
+ raise ValueError (f"Missing required fields in surface point { i } : { missing_fields } " )
147
+
148
+ # Validate data types
149
+ if not all (isinstance (sp [field ], (int , float )) for field in ['x' , 'y' , 'z' , 'nugget' ]):
150
+ raise ValueError (f"Invalid data type in surface point { i } . All coordinates and nugget must be numeric." )
151
+ if not isinstance (sp ['id' ], int ):
152
+ raise ValueError (f"Invalid data type in surface point { i } . ID must be an integer." )
153
+
138
154
# Extract coordinates and other data
139
155
x = np .array ([sp ['x' ] for sp in surface_points_data ])
140
156
y = np .array ([sp ['y' ] for sp in surface_points_data ])
@@ -168,10 +184,28 @@ def _load_orientations(orientations_data: List[Orientation], id_to_name: Optiona
168
184
169
185
Returns:
170
186
OrientationsTable: A new OrientationsTable instance
187
+
188
+ Raises:
189
+ ValueError: If the data is invalid or missing required fields
171
190
"""
172
191
# Import here to avoid circular imports
173
192
from gempy .core .data .orientations import OrientationsTable
174
193
194
+ # Validate data structure
195
+ required_fields = {'x' , 'y' , 'z' , 'G_x' , 'G_y' , 'G_z' , 'nugget' , 'polarity' , 'id' }
196
+ for i , ori in enumerate (orientations_data ):
197
+ missing_fields = required_fields - set (ori .keys ())
198
+ if missing_fields :
199
+ raise ValueError (f"Missing required fields in orientation { i } : { missing_fields } " )
200
+
201
+ # Validate data types
202
+ if not all (isinstance (ori [field ], (int , float )) for field in ['x' , 'y' , 'z' , 'G_x' , 'G_y' , 'G_z' , 'nugget' ]):
203
+ raise ValueError (f"Invalid data type in orientation { i } . All coordinates, gradients, and nugget must be numeric." )
204
+ if not isinstance (ori .get ('polarity' , 1 ), int ) or ori .get ('polarity' , 1 ) not in {- 1 , 1 }:
205
+ raise ValueError (f"Invalid polarity in orientation { i } . Must be 1 (normal) or -1 (reverse)." )
206
+ if not isinstance (ori ['id' ], int ):
207
+ raise ValueError (f"Invalid data type in orientation { i } . ID must be an integer." )
208
+
175
209
# Extract coordinates and other data
176
210
x = np .array ([ori ['x' ] for ori in orientations_data ])
177
211
y = np .array ([ori ['y' ] for ori in orientations_data ])
0 commit comments