@@ -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,13 +103,19 @@ 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 ():
113+ period_data = {} # type: ignore
114+ period_blocks = {} # type: ignore
115+ period_block_name = None
116+
109117 if block_name not in blocks :
110118 blocks [block_name ] = {}
111- period_data = {}
112- period_blocks = {} # type: ignore
113119
114120 for field_name in block .keys ():
115121 # Skip child components that have been processed as bindings
@@ -119,82 +125,78 @@ def unstructure_component(value: Component) -> dict[str, Any]:
119125 if child_spec .metadata ["block" ] == block_name : # type: ignore
120126 continue
121127
122- field_value = data [field_name ]
123- # convert:
128+ # filter out empty values and false keywords, and convert:
124129 # - paths to records
125- # - datetime to ISO format
126- # - auxiliary fields to tuples
127- # - xarray DataArrays with 'nper' dimension to kper-sliced datasets
128- # (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
129134 # - other values to their original form
130- if isinstance (field_value , Path ):
131- field_spec = xatspec .attrs [field_name ]
132- field_meta = getattr (field_spec , "metadata" , {})
133- t = path_to_tuple (field_name , field_value , inout = field_meta .get ("inout" , "fileout" ))
134- # name may have changed e.g dropping '_file' suffix
135- blocks [block_name ][t [0 ]] = t
136- elif isinstance (field_value , datetime ):
137- blocks [block_name ][field_name ] = field_value .isoformat ()
138- elif (
139- field_name == "auxiliary"
140- and hasattr (field_value , "values" )
141- and field_value is not None
142- ):
143- blocks [block_name ][field_name ] = tuple (field_value .values .tolist ())
144- elif isinstance (field_value , xr .DataArray ) and "nper" in field_value .dims :
145- has_spatial_dims = any (
146- dim in field_value .dims for dim in ["nlay" , "nrow" , "ncol" , "nodes" ]
147- )
148- if has_spatial_dims :
149- field_value = _hack_structured_grid_dims (
150- field_value ,
151- 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" )
152146 )
153-
154- period_data [field_name ] = {
155- kper : field_value .isel (nper = kper )
156- for kper in range (field_value .sizes ["nper" ])
157- }
158- else :
159- # TODO why not putting in block here but doing below? how does this even work
160- if np .issubdtype (field_value .dtype , np .str_ ):
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
161168 period_data [field_name ] = {
162- kper : field_value [kper ] for kper in range (field_value .sizes ["nper" ])
169+ kper : field_value .isel (nper = kper )
170+ for kper in range (field_value .sizes ["nper" ])
163171 }
164- else :
165- if block_name not in period_data :
166- period_data [block_name ] = {}
167- period_data [block_name ][field_name ] = field_value # type: ignore
168- else :
169- if field_value is not None :
170- if isinstance (field_value , bool ):
171- if field_value :
172- blocks [block_name ][field_name ] = field_value
173172 else :
174173 blocks [block_name ][field_name ] = field_value
175174
176- if block_name in period_data and isinstance (period_data [block_name ], dict ):
177- dataset = xr .Dataset (period_data [block_name ])
178- blocks [block_name ] = {block_name : dataset }
179- del period_data [block_name ]
175+ case _:
176+ blocks [block_name ][field_name ] = field_value
180177
178+ # invert key order, (arr_name, kper) -> (kper, arr_name)
181179 for arr_name , periods in period_data .items ():
182180 for kper , arr in periods .items ():
183181 if kper not in period_blocks :
184182 period_blocks [kper ] = {}
185183 period_blocks [kper ][arr_name ] = arr
186184
185+ # setup indexed period blocks, combine arrays into datasets
187186 for kper , block in period_blocks .items ():
188- dataset = xr .Dataset (block )
189- blocks [f"{ block_name } { kper + 1 } " ] = {block_name : dataset }
187+ assert isinstance (period_block_name , str )
188+ blocks [f"{ period_block_name } { kper + 1 } " ] = {
189+ period_block_name : xr .Dataset (block , coords = block [arr_name ].coords )
190+ }
190191
191- # total temporary hack! manually set solutiongroup 1. still need to support multiple..
192+ # total temporary hack! manually set solutiongroup 1.
193+ # TODO still need to support multiple..
192194 if "solutiongroup" in blocks :
193195 sg = blocks ["solutiongroup" ]
194196 blocks ["solutiongroup 1" ] = sg
195197 del blocks ["solutiongroup" ]
196198
197- return {name : block for name , block in blocks .items () if name != "period" }
199+ return {name : block for name , block in blocks .items () if name != period_block_name }
198200
199201
200202def _make_converter () -> Converter :
0 commit comments