@@ -31,7 +31,7 @@ def path_to_tuple(name: str, value: Path, inout: FileInOut) -> tuple[str, ...]:
3131 return tuple (t )
3232
3333
34- def get_binding_blocks (value : Component ) -> dict [str , dict [str , list [tuple [str , ...]]]]:
34+ def make_binding_blocks (value : Component ) -> dict [str , dict [str , list [tuple [str , ...]]]]:
3535 if not isinstance (value , Context ):
3636 return {}
3737
@@ -103,15 +103,20 @@ def unstructure_component(value: Component) -> dict[str, Any]:
103103 xatspec = xattree .get_xatspec (type (value ))
104104 data = xattree .asdict (value )
105105
106- blocks .update (binding_blocks := get_binding_blocks (value ))
106+ # create child component binding blocks
107+ blocks .update (make_binding_blocks (value ))
107108
109+ # process blocks in order, unstructuring fields as needed,
110+ # then slice period data into separate kper-indexed blocks
111+ # each of which contains a dataset indexed for that period.
108112 for block_name , block in blockspec .items ():
109- if block_name not in blocks :
110- blocks [block_name ] = {}
111- period_data = {}
113+ period_data = {} # type: ignore
112114 period_blocks = {} # type: ignore
113115 period_block_name = None
114116
117+ if block_name not in blocks :
118+ blocks [block_name ] = {}
119+
115120 for field_name in block .keys ():
116121 # Skip child components that have been processed as bindings
117122 if isinstance (value , Context ) and field_name in xatspec .children :
@@ -120,60 +125,64 @@ def unstructure_component(value: Component) -> dict[str, Any]:
120125 if child_spec .metadata ["block" ] == block_name : # type: ignore
121126 continue
122127
123- field_value = data [field_name ]
124- # convert:
128+ # filter out empty values and false keywords, and convert:
125129 # - paths to records
126- # - datetime to ISO format
127- # - auxiliary fields to tuples
128- # - xarray DataArrays with 'nper' dimension to kper-sliced datasets
129- # (and split the period data into separate kper-indexed blocks)
130+ # - datetimes to ISO format
131+ # - filter out false keywords
132+ # - 'auxiliary' fields to tuples
133+ # - xarray DataArrays with 'nper' dim to dict of kper-sliced datasets
130134 # - other values to their original form
131- if isinstance (field_value , Path ):
132- field_spec = xatspec .attrs [field_name ]
133- field_meta = getattr (field_spec , "metadata" , {})
134- t = path_to_tuple (field_name , field_value , inout = field_meta .get ("inout" , "fileout" ))
135- # name may have changed e.g dropping '_file' suffix
136- blocks [block_name ][t [0 ]] = t
137- elif isinstance (field_value , datetime ):
138- blocks [block_name ][field_name ] = field_value .isoformat ()
139- elif (
140- field_name == "auxiliary"
141- and hasattr (field_value , "values" )
142- and field_value is not None
143- ):
144- blocks [block_name ][field_name ] = tuple (field_value .values .tolist ())
145- elif isinstance (field_value , xr .DataArray ) and "nper" in field_value .dims :
146- has_spatial_dims = any (
147- dim in field_value .dims for dim in ["nlay" , "nrow" , "ncol" , "nodes" ]
148- )
149- if has_spatial_dims :
150- field_value = _hack_structured_grid_dims (
151- field_value ,
152- structured_grid_dims = value .parent .data .dims , # type: ignore
135+ match field_value := data [field_name ]:
136+ case None :
137+ continue
138+ case bool ():
139+ if field_value :
140+ blocks [block_name ][field_name ] = field_value
141+ case Path ():
142+ field_spec = xatspec .attrs [field_name ]
143+ field_meta = getattr (field_spec , "metadata" , {})
144+ t = path_to_tuple (
145+ field_name , field_value , inout = field_meta .get ("inout" , "fileout" )
153146 )
154- if "period" in block_name :
155- period_block_name = block_name
156- period_data [field_name ] = {
157- kper : field_value .isel (nper = kper )
158- for kper in range (field_value .sizes ["nper" ])
159- }
160- else :
161- blocks [block_name ][field_name ] = field_value
162- else :
163- if field_value is not None :
164- # only include boolean fields (keywords) if true
165- if isinstance (field_value , bool ):
166- if field_value :
167- blocks [block_name ][field_name ] = field_value
147+ # name may have changed e.g dropping '_file' suffix
148+ blocks [block_name ][t [0 ]] = t
149+ case datetime ():
150+ blocks [block_name ][field_name ] = field_value .isoformat ()
151+ case t if (
152+ field_name == "auxiliary"
153+ and hasattr (field_value , "values" )
154+ and field_value is not None
155+ ):
156+ blocks [block_name ][field_name ] = tuple (field_value .values .tolist ())
157+ case xr .DataArray () if "nper" in field_value .dims :
158+ has_spatial_dims = any (
159+ dim in field_value .dims for dim in ["nlay" , "nrow" , "ncol" , "nodes" ]
160+ )
161+ if has_spatial_dims :
162+ field_value = _hack_structured_grid_dims (
163+ field_value ,
164+ structured_grid_dims = value .parent .data .dims , # type: ignore
165+ )
166+ if "period" in block_name :
167+ period_block_name = block_name
168+ period_data [field_name ] = {
169+ kper : field_value .isel (nper = kper )
170+ for kper in range (field_value .sizes ["nper" ])
171+ }
168172 else :
169173 blocks [block_name ][field_name ] = field_value
170174
175+ case _:
176+ blocks [block_name ][field_name ] = field_value
177+
178+ # invert key order, (arr_name, kper) -> (kper, arr_name)
171179 for arr_name , periods in period_data .items ():
172180 for kper , arr in periods .items ():
173181 if kper not in period_blocks :
174182 period_blocks [kper ] = {}
175183 period_blocks [kper ][arr_name ] = arr
176184
185+ # setup indexed period blocks, combine arrays into datasets
177186 for kper , block in period_blocks .items ():
178187 assert isinstance (period_block_name , str )
179188 blocks [f"{ period_block_name } { kper + 1 } " ] = {
0 commit comments