@@ -54,6 +54,7 @@ def __init__(
5454 topology_dimension : Literal [2 ],
5555 node_dimensions : tuple [Dim , Dim ],
5656 face_dimensions : tuple [DimDimPadding , DimDimPadding ],
57+ node_coordinates : None | tuple [Dim , Dim ] = None ,
5758 vertical_dimensions : None | tuple [DimDimPadding ] = None ,
5859 ):
5960 if cf_role != "grid_topology" :
@@ -76,6 +77,14 @@ def __init__(
7677 ):
7778 raise ValueError ("face_dimensions must be a tuple of 2 DimDimPadding for a 2D grid" )
7879
80+ if node_coordinates is not None :
81+ if not (
82+ isinstance (node_coordinates , tuple )
83+ and len (node_coordinates ) == 2
84+ and all (isinstance (nd , str ) for nd in node_coordinates )
85+ ):
86+ raise ValueError ("node_coordinates must be a tuple of 2 dimensions for a 2D grid" )
87+
7988 if vertical_dimensions is not None :
8089 if not (
8190 isinstance (vertical_dimensions , tuple )
@@ -90,21 +99,21 @@ def __init__(
9099 self .node_dimensions = node_dimensions
91100 self .face_dimensions = face_dimensions
92101
93- #! Optional attributes aren't really important to Parcels, can be added later if needed
102+ # Optional attributes
103+ self .node_coordinates = node_coordinates
104+ self .vertical_dimensions = vertical_dimensions
105+
106+ #! Some optional attributes aren't really important to Parcels, can be added later if needed
94107 # Optional attributes
95108 # # With defaults (set in init)
96109 # edge1_dimensions: tuple[Dim, DimDimPadding]
97110 # edge2_dimensions: tuple[DimDimPadding, Dim]
98111
99112 # # Without defaults
100- # node_coordinates: None | Any = None
101113 # edge1_coordinates: None | Any = None
102114 # edge2_coordinates: None | Any = None
103115 # face_coordinate: None | Any = None
104116
105- #! Important optional attribute for 2D grids with vertical layering
106- self .vertical_dimensions = vertical_dimensions
107-
108117 def __repr__ (self ) -> str :
109118 return repr_from_dunder_dict (self )
110119
@@ -121,6 +130,7 @@ def from_attrs(cls, attrs):
121130 topology_dimension = attrs ["topology_dimension" ],
122131 node_dimensions = load_mappings (attrs ["node_dimensions" ]),
123132 face_dimensions = load_mappings (attrs ["face_dimensions" ]),
133+ node_coordinates = maybe_load_mappings (attrs .get ("node_coordinates" )),
124134 vertical_dimensions = maybe_load_mappings (attrs .get ("vertical_dimensions" )),
125135 )
126136 except Exception as e :
@@ -133,6 +143,8 @@ def to_attrs(self) -> dict[str, str | int]:
133143 node_dimensions = dump_mappings (self .node_dimensions ),
134144 face_dimensions = dump_mappings (self .face_dimensions ),
135145 )
146+ if self .node_coordinates is not None :
147+ d ["node_coordinates" ] = dump_mappings (self .node_coordinates )
136148 if self .vertical_dimensions is not None :
137149 d ["vertical_dimensions" ] = dump_mappings (self .vertical_dimensions )
138150 return d
@@ -148,6 +160,7 @@ def __init__(
148160 topology_dimension : Literal [3 ],
149161 node_dimensions : tuple [Dim , Dim , Dim ],
150162 volume_dimensions : tuple [DimDimPadding , DimDimPadding , DimDimPadding ],
163+ node_coordinates : None | tuple [Dim , Dim , Dim ] = None ,
151164 ):
152165 if cf_role != "grid_topology" :
153166 raise ValueError (f"cf_role must be 'grid_topology', got { cf_role !r} " )
@@ -169,13 +182,24 @@ def __init__(
169182 ):
170183 raise ValueError ("face_dimensions must be a tuple of 2 DimDimPadding for a 2D grid" )
171184
185+ if node_coordinates is not None :
186+ if not (
187+ isinstance (node_coordinates , tuple )
188+ and len (node_coordinates ) == 3
189+ and all (isinstance (nd , str ) for nd in node_coordinates )
190+ ):
191+ raise ValueError ("node_coordinates must be a tuple of 3 dimensions for a 3D grid" )
192+
172193 # Required attributes
173194 self .cf_role = cf_role
174195 self .topology_dimension = topology_dimension
175196 self .node_dimensions = node_dimensions
176197 self .volume_dimensions = volume_dimensions
177198
178- # ! Optional attributes aren't really important to Parcels, can be added later if needed
199+ # Optional attributes
200+ self .node_coordinates = node_coordinates
201+
202+ # ! Some optional attributes aren't really important to Parcels, can be added later if needed
179203 # Optional attributes
180204 # # With defaults (set in init)
181205 # edge1_dimensions: tuple[DimDimPadding, Dim, Dim]
@@ -186,7 +210,6 @@ def __init__(
186210 # face3_dimensions: tuple[DimDimPadding, DimDimPadding, Dim]
187211
188212 # # Without defaults
189- # node_coordinates
190213 # edge *i_coordinates*
191214 # face *i_coordinates*
192215 # volume_coordinates
@@ -207,17 +230,21 @@ def from_attrs(cls, attrs):
207230 topology_dimension = attrs ["topology_dimension" ],
208231 node_dimensions = load_mappings (attrs ["node_dimensions" ]),
209232 volume_dimensions = load_mappings (attrs ["volume_dimensions" ]),
233+ node_coordinates = maybe_load_mappings (attrs .get ("node_coordinates" )),
210234 )
211235 except Exception as e :
212236 raise SGridParsingException (f"Failed to parse Grid3DMetadata from { attrs = !r} " ) from e
213237
214238 def to_attrs (self ) -> dict [str , str | int ]:
215- return dict (
239+ d = dict (
216240 cf_role = self .cf_role ,
217241 topology_dimension = self .topology_dimension ,
218242 node_dimensions = dump_mappings (self .node_dimensions ),
219243 volume_dimensions = dump_mappings (self .volume_dimensions ),
220244 )
245+ if self .node_coordinates is not None :
246+ d ["node_coordinates" ] = dump_mappings (self .node_coordinates )
247+ return d
221248
222249 def rename_dims (self , dims_dict : dict [str , str ]) -> Self :
223250 return _metadata_rename_dims (self , dims_dict )
0 commit comments