@@ -96,15 +96,115 @@ def test_grid_with_holes():
9696 assert grid_without_holes .global_sphere_coverage
9797
9898
99- def test_grid_encode_as ():
100- """Reads a ugrid file and encodes it as `xarray.Dataset` in various types."""
101- grid_CSne30 .encode_as ("UGRID" )
102- grid_RLL1deg .encode_as ("UGRID" )
103- grid_RLL10deg_CSne4 .encode_as ("UGRID" )
99+ def test_grid_ugrid_exodus_roundtrip ():
100+ """Test round-trip serialization of grid objects through UGRID and Exodus xarray formats.
104101
105- grid_CSne30 .encode_as ("Exodus" )
106- grid_RLL1deg .encode_as ("Exodus" )
107- grid_RLL10deg_CSne4 .encode_as ("Exodus" )
102+ Validates that grid objects can be successfully converted to xarray.Dataset
103+ objects in both UGRID and Exodus formats, serialized to disk, and reloaded
104+ while maintaining numerical accuracy and topological integrity.
105+
106+ The test verifies:
107+ - Successful conversion to UGRID and Exodus xarray formats
108+ - File I/O round-trip consistency
109+ - Preservation of face-node connectivity (exact)
110+ - Preservation of node coordinates (within numerical tolerance)
111+
112+ Raises:
113+ AssertionError: If any round-trip validation fails
114+ """
115+
116+ # Convert grids to xarray.Dataset objects in different formats
117+ ugrid_datasets = {
118+ 'CSne30' : grid_CSne30 .to_xarray ("UGRID" ),
119+ 'RLL1deg' : grid_RLL1deg .to_xarray ("UGRID" ),
120+ 'RLL10deg_CSne4' : grid_RLL10deg_CSne4 .to_xarray ("UGRID" )
121+ }
122+
123+ exodus_datasets = {
124+ 'CSne30' : grid_CSne30 .to_xarray ("Exodus" ),
125+ 'RLL1deg' : grid_RLL1deg .to_xarray ("Exodus" ),
126+ 'RLL10deg_CSne4' : grid_RLL10deg_CSne4 .to_xarray ("Exodus" )
127+ }
128+
129+ # Define test cases with corresponding grid objects
130+ test_grids = {
131+ 'CSne30' : grid_CSne30 ,
132+ 'RLL1deg' : grid_RLL1deg ,
133+ 'RLL10deg_CSne4' : grid_RLL10deg_CSne4
134+ }
135+
136+ # Perform round-trip validation for each grid type
137+ test_files = []
138+
139+ for grid_name in test_grids .keys ():
140+ ugrid_dataset = ugrid_datasets [grid_name ]
141+ exodus_dataset = exodus_datasets [grid_name ]
142+ original_grid = test_grids [grid_name ]
143+
144+ # Define output file paths
145+ ugrid_filepath = f"test_ugrid_{ grid_name } .nc"
146+ exodus_filepath = f"test_exodus_{ grid_name } .exo"
147+ test_files .append (ugrid_filepath )
148+ test_files .append (exodus_filepath )
149+
150+ # Serialize datasets to disk
151+ ugrid_dataset .to_netcdf (ugrid_filepath )
152+ exodus_dataset .to_netcdf (exodus_filepath )
153+
154+ # Reload grids from serialized files
155+ reloaded_ugrid = ux .open_grid (ugrid_filepath )
156+ reloaded_exodus = ux .open_grid (exodus_filepath )
157+
158+ # Validate topological consistency (face-node connectivity)
159+ # Integer connectivity arrays must be exactly preserved
160+ np .testing .assert_array_equal (
161+ original_grid .face_node_connectivity .values ,
162+ reloaded_ugrid .face_node_connectivity .values ,
163+ err_msg = f"UGRID face connectivity mismatch for { grid_name } "
164+ )
165+ np .testing .assert_array_equal (
166+ original_grid .face_node_connectivity .values ,
167+ reloaded_exodus .face_node_connectivity .values ,
168+ err_msg = f"Exodus face connectivity mismatch for { grid_name } "
169+ )
170+
171+ # Validate coordinate consistency with numerical tolerance
172+ # Coordinate transformations and I/O precision may introduce minor differences
173+ np .testing .assert_allclose (
174+ original_grid .node_lon .values ,
175+ reloaded_ugrid .node_lon .values ,
176+ err_msg = f"UGRID longitude mismatch for { grid_name } " ,
177+ rtol = ERROR_TOLERANCE
178+ )
179+ np .testing .assert_allclose (
180+ original_grid .node_lon .values ,
181+ reloaded_exodus .node_lon .values ,
182+ err_msg = f"Exodus longitude mismatch for { grid_name } " ,
183+ rtol = ERROR_TOLERANCE
184+ )
185+ np .testing .assert_allclose (
186+ original_grid .node_lat .values ,
187+ reloaded_ugrid .node_lat .values ,
188+ err_msg = f"UGRID latitude mismatch for { grid_name } " ,
189+ rtol = ERROR_TOLERANCE
190+ )
191+ np .testing .assert_allclose (
192+ original_grid .node_lat .values ,
193+ reloaded_exodus .node_lat .values ,
194+ err_msg = f"Exodus latitude mismatch for { grid_name } " ,
195+ rtol = ERROR_TOLERANCE
196+ )
197+
198+ # This might be need for windows "ermissionError: [WinError 32] -- file accessed by another process"
199+ reloaded_exodus ._ds .close ()
200+ reloaded_ugrid ._ds .close ()
201+ del reloaded_exodus
202+ del reloaded_ugrid
203+
204+ # Clean up temporary test files
205+ for filepath in test_files :
206+ if os .path .exists (filepath ):
207+ os .remove (filepath )
108208
109209
110210def test_grid_init_verts ():
@@ -147,21 +247,21 @@ def test_grid_init_verts():
147247
148248 assert vgrid .n_face == 6
149249 assert vgrid .n_node == 8
150- vgrid .encode_as ("UGRID" )
250+ vgrid .to_xarray ("UGRID" )
151251
152252 faces_verts_one = np .array ([
153253 np .array ([[150 , 10 ], [160 , 20 ], [150 , 30 ], [135 , 30 ], [125 , 20 ], [135 , 10 ]])
154254 ])
155255 vgrid = ux .open_grid (faces_verts_one , latlon = True )
156256 assert vgrid .n_face == 1
157257 assert vgrid .n_node == 6
158- vgrid .encode_as ("UGRID" )
258+ vgrid .to_xarray ("UGRID" )
159259
160260 faces_verts_single_face = np .array ([[150 , 10 ], [160 , 20 ], [150 , 30 ], [135 , 30 ], [125 , 20 ], [135 , 10 ]])
161261 vgrid = ux .open_grid (faces_verts_single_face , latlon = True )
162262 assert vgrid .n_face == 1
163263 assert vgrid .n_node == 6
164- vgrid .encode_as ("UGRID" )
264+ vgrid .to_xarray ("UGRID" )
165265
166266
167267def test_grid_init_verts_different_input_datatype ():
@@ -174,7 +274,7 @@ def test_grid_init_verts_different_input_datatype():
174274 vgrid = ux .open_grid (faces_verts_ndarray , latlon = True )
175275 assert vgrid .n_face == 3
176276 assert vgrid .n_node == 14
177- vgrid .encode_as ("UGRID" )
277+ vgrid .to_xarray ("UGRID" )
178278
179279 faces_verts_list = [[[150 , 10 ], [160 , 20 ], [150 , 30 ], [135 , 30 ], [125 , 20 ], [135 , 10 ]],
180280 [[125 , 20 ], [135 , 30 ], [125 , 60 ], [110 , 60 ], [100 , 30 ], [105 , 20 ]],
@@ -183,7 +283,7 @@ def test_grid_init_verts_different_input_datatype():
183283 assert vgrid .n_face == 3
184284 assert vgrid .n_node == 14
185285 assert vgrid .validate ()
186- vgrid .encode_as ("UGRID" )
286+ vgrid .to_xarray ("UGRID" )
187287
188288 faces_verts_tuples = [
189289 ((150 , 10 ), (160 , 20 ), (150 , 30 ), (135 , 30 ), (125 , 20 ), (135 , 10 )),
@@ -194,7 +294,7 @@ def test_grid_init_verts_different_input_datatype():
194294 assert vgrid .n_face == 3
195295 assert vgrid .n_node == 14
196296 assert vgrid .validate ()
197- vgrid .encode_as ("UGRID" )
297+ vgrid .to_xarray ("UGRID" )
198298
199299
200300def test_grid_init_verts_fill_values ():
0 commit comments