Coverage for backend/idaes_service/solver/methods/load_unit_model.py: 95%
67 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
1from typing import Any
3from pyomo.network import Port
4from pyomo.environ import value as pyo_value, Var
5from pyomo.core.base.constraint import ScalarConstraint
6from pyomo.core.base.units_container import units
7from idaes.core import UnitModelBlock
8from idaes.core.util.tables import _get_state_from_port
9from idaes.core.util.model_serializer import StoreSpec, from_json
11from idaes_service.solver.methods.BlockContext import BlockContext
12from .adapter_library import UnitModelConstructor
13from ..flowsheet_manager_type import FlowsheetManager
14from common.models.idaes import UnitModelSchema
15from .adapter import fix_block
16from idaes_service.solver.methods.adapter import get_component
18def add_unit_model_to_flowsheet(
19 unit_model_def: UnitModelSchema,
20 adapter_constructor: UnitModelConstructor,
21 flowsheet_manager: FlowsheetManager,
22) -> UnitModelBlock:
23 """
24 Add the unit model to the flowsheet.
25 Args:
26 unit_model_def: The definition of the unit model to be added to the flowsheet.
27 adapter_schema: Methods used to create the model and parse the arguments.
28 flowsheet_manager: Flowsheet manager to interact with the flowsheet.
29 """
30 # Create the model
31 idaes_model: UnitModelBlock =_create_model(unit_model_def, adapter_constructor, flowsheet_manager)
33 # Add the model to the flowsheet
34 component_name = f"{unit_model_def.name}_{unit_model_def.id}"
35 flowsheet_manager.model.fs.add_component(component_name, idaes_model)
37 # Import initial guesses
38 initial_values = unit_model_def.initial_values
40 if initial_values:
41 from_json(idaes_model, initial_values, wts=StoreSpec.value())
43 # Fix properties
44 block_context = BlockContext(flowsheet_manager.model.fs)
45 _fix_properties(idaes_model,unit_model_def, flowsheet_manager,block_context)
46 # Fix ports
47 _fix_ports(idaes_model, unit_model_def, flowsheet_manager,block_context)
48 # Apply degrees of freedom (add constraints for controlled vars)
49 block_context.apply_elimination()
50 return idaes_model
52def _create_model(unit_model_def : UnitModelSchema,adapter_constructor: UnitModelConstructor,flowsheet_manager : FlowsheetManager) -> UnitModelBlock:
53 """
54 Create the kwargs for the model constructor from the unit model definition.
55 """
56 arg_parsers = adapter_constructor.arg_parsers
57 args = unit_model_def.args
59 kwargs : dict[str, Any] = {}
60 for name in args:
61 if not name in arg_parsers: 61 ↛ 62line 61 didn't jump to line 62 because the condition on line 61 was never true
62 raise ValueError(
63 f"Argument {name} not found in model schema, available arguments are {[x for x in arg_parsers.keys()]}"
64 )
65 for name in arg_parsers:
66 register = arg_parsers[name]
67 # try to get the argument from the schema passed in
68 # however, in some cases the argument may not be required
69 # i.e when specified by the model adapter itself
70 # e.g methods.constant
72 # note that in the future we may have to support passing
73 # optional arguments to the model constructor
74 result = register.run(args.get(name, None),flowsheet_manager)
75 kwargs[name] = result
77 idaes_model = adapter_constructor.model_constructor(**kwargs)
78 return idaes_model
80def _fix_properties( unit_model: UnitModelBlock, unit_model_def: UnitModelSchema, flowsheet_manager : FlowsheetManager,block_context: BlockContext) -> None:
81 """
82 Fix the properties of the unit model based on the properties in the unit model definition.
83 """
84 # Loop through the properties in the unit model definition to fix the properties in the unit model
85 properties = unit_model_def.properties
86 fix_block(
87 unit_model, properties, flowsheet_manager.model.fs, block_context
88 )
91def _fix_ports(unit_model: UnitModelBlock, unit_model_def: UnitModelSchema, flowsheet_manager:FlowsheetManager,block_context: BlockContext) -> None:
92 """
93 Fix the ports of the unit model based on the ports in the unit model definition.
94 """
95 # Loop through the ports in the unit model definition to fix the ports in the unit model
96 for port_name, port_schema in unit_model_def.ports.items():
97 # Get the port from the unit model
98 port = get_component(unit_model, port_name)
99 if not isinstance(port, Port): 99 ↛ 100line 99 didn't jump to line 100 because the condition on line 99 was never true
100 raise ValueError(f"Port {port_name} not found in model")
101 # Register the port, so arcs can connect to it by id
102 flowsheet_manager.ports.register_port(port_schema.id, port)
103 # Set the port parameters
104 sb = _get_state_from_port(port, 0)
105 state_block = sb.parent_component()
107 # Prefer time-only properties_in/properties_out if available
108 # decide by port name containing "inlet"/"outlet" and "hot"/"cold"
109 pn = port_name.lower()
110 if "inlet" in pn:
111 if "hot" in pn and hasattr(unit_model, "hot_side") and hasattr(unit_model.hot_side, "properties_in"):
112 state_block = unit_model.hot_side.properties_in
113 elif "cold" in pn and hasattr(unit_model, "cold_side") and hasattr(unit_model.cold_side, "properties_in"):
114 state_block = unit_model.cold_side.properties_in
115 elif "outlet" in pn: 115 ↛ 121line 115 didn't jump to line 121 because the condition on line 115 was always true
116 if "hot" in pn and hasattr(unit_model, "hot_side") and hasattr(unit_model.hot_side, "properties_out"):
117 state_block = unit_model.hot_side.properties_out
118 elif "cold" in pn and hasattr(unit_model, "cold_side") and hasattr(unit_model.cold_side, "properties_out"):
119 state_block = unit_model.cold_side.properties_out
121 if sb.config.defined_state:
122 """
123 ie. Inlet state.
125 The inlet state needs a separate context, because its variables
126 are all fixed during initialization, so applying elimination
127 involving variables outside the inlet state would run into
128 degrees of freedom issues.
129 """
130 inlet_ctx = BlockContext(flowsheet_manager.model.fs)
131 fix_block(
132 state_block,
133 port_schema.properties,
134 flowsheet_manager.model.fs,
135 inlet_ctx,
136 )
137 inlet_ctx.apply_elimination()
138 else:
139 # The outlet state(s) can use the unit model context.
140 fix_block(
141 state_block,
142 port_schema.properties,
143 flowsheet_manager.model.fs,
144 block_context,
145 )