@@ -135,3 +135,76 @@ def to_component(cls, binding_tuple: tuple | list, workspace: Path) -> Component
135135 component .name = terms [0 ] # type: ignore[attr-defined]
136136
137137 return component # type: ignore[return-value]
138+
139+
140+ def resolve_bindings (component : Component , workspace : Path ) -> Component :
141+ """
142+ Recursively resolve binding tuples to component instances.
143+
144+ Processes a component after structuring to:
145+ 1. Detect binding lists (list of tuples with type/fname/terms)
146+ 2. Recursively load referenced components via Binding.to_component()
147+ 3. Replace binding lists with dicts of component instances
148+
149+ Parameters
150+ ----------
151+ component : Component
152+ Component to process (may contain binding lists)
153+ workspace : Path
154+ Workspace directory for resolving file paths
155+
156+ Returns
157+ -------
158+ Component
159+ Component with bindings resolved to component instances
160+ """
161+ from flopy4 .mf6 .simulation import Simulation
162+
163+ # Check if this is a Simulation - resolve models, solutions, exchanges
164+ if isinstance (component , Simulation ):
165+ # Resolve models: list of binding tuples → dict of Model instances
166+ if hasattr (component , "models" ) and isinstance (component .models , list ):
167+ resolved_models = {}
168+ for binding_tuple in component .models :
169+ if isinstance (binding_tuple , (tuple , list )):
170+ model = Binding .to_component (binding_tuple , workspace )
171+ # Use the name from the binding terms as the key
172+ key = binding_tuple [2 ] if len (binding_tuple ) > 2 else model .name # type: ignore
173+ resolved_models [key ] = model
174+ component .models = resolved_models # type: ignore
175+
176+ # Resolve solutions: dict[group, list of tuples] → dict[name, Solution]
177+ if hasattr (component , "solutions" ) and isinstance (component .solutions , dict ):
178+ resolved_solutions = {}
179+ for group_key , bindings in list (component .solutions .items ()): # type: ignore
180+ if isinstance (bindings , list ):
181+ for binding_tuple in bindings :
182+ if isinstance (binding_tuple , (tuple , list )):
183+ solution = Binding .to_component (binding_tuple , workspace )
184+ # Use the name from the binding terms as the key
185+ key = (
186+ binding_tuple [2 ] if len (binding_tuple ) > 2 else solution .name # type: ignore
187+ )
188+ resolved_solutions [key ] = solution
189+ component .solutions = resolved_solutions # type: ignore
190+
191+ # Resolve exchanges: list of binding tuples → dict of Exchange instances
192+ if hasattr (component , "exchanges" ) and isinstance (component .exchanges , list ):
193+ resolved_exchanges = {}
194+ for binding_tuple in component .exchanges :
195+ if isinstance (binding_tuple , (tuple , list )):
196+ exchange = Binding .to_component (binding_tuple , workspace )
197+ # Use exchange type + model names as key
198+ key = f"{ exchange .__class__ .__name__ } _{ binding_tuple [2 ]} _{ binding_tuple [3 ]} " # type: ignore
199+ resolved_exchanges [key ] = exchange
200+ component .exchanges = resolved_exchanges # type: ignore
201+
202+ # Resolve TDIS if it's a binding
203+ if hasattr (component , "tdis" ) and isinstance (component .tdis , (tuple , list )):
204+ component .tdis = Binding .to_component (component .tdis , workspace ) # type: ignore
205+
206+ # For Model components, resolve package bindings if needed
207+ # (currently packages are typically in griddata/perioddata fields, not as bindings)
208+ # This can be extended in the future if needed
209+
210+ return component
0 commit comments