Coverage for backend/ahuora-builder/src/ahuora_builder/tear_manager.py: 98%
45 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-13 02:47 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-13 02:47 +0000
1from pyomo.environ import value
2from pyomo.network import Arc
3from idaes.core.util.tables import _get_state_from_port
4from pyomo.core.base.expression import ScalarExpression
5from ahuora_builder_types.arc_schema import TearGuessSchema
6from .flowsheet_manager_type import FlowsheetManager
7from ahuora_builder_types import PortId
8from .methods.adapter import fix_var
11class TearManager:
12 """
13 Manages the tears in the flowsheet
14 """
16 def __init__(self, flowsheet_manager: FlowsheetManager):
17 """
18 Create a new tear manager
19 """
20 self._flowsheet_manager = flowsheet_manager
21 self._tears: list[Arc] = []
23 def load(self):
24 """
25 Load all the tears (from the recycle unitops)
26 """
27 schema = self._flowsheet_manager.schema
29 for arc_schema in schema.arcs:
30 if arc_schema.tear_guess:
31 self.add_tear(arc_schema)
33 def add_tear(self, arc_schema):
34 """
35 Add a tear to the flowsheet
36 """
37 portId = arc_schema.destination
38 guess = arc_schema.tear_guess
40 port = self._flowsheet_manager.ports.get_port(portId)
41 arc = port.arcs()[0]
42 self._tears.append(arc)
44 """
45 During model loading, we add in all the constraints and fix the variables
46 for this port. Since it is a tear, things need to be deactivated/unfixed
47 to ensure 0 degrees of freedom
49 If the state block is defined by constraints, we need to solve it to get the
50 correct values for the state variables. Then we deactivate the constraints
51 since we will be fixing the state variables instead (where applicable).
52 """
54 # hardcoding time indexes to [0] for now
55 time_indexes = [0]
57 # need to get the correct value for state variables before running
58 # sequential decomposition (since all state variables are fixed
59 # during sequential decomposition).
60 sb = _get_state_from_port(port, time_indexes[0])
61 # deactivate any guesses
62 for key, value in guess.items():
63 var = getattr(sb, key)
64 if value != True:
65 if isinstance(var,ScalarExpression):
66 pass # we want to solve with constraints
67 else:
68 # this might give too few dof if we have other constraints. We need to unfix
69 # this value
70 var.unfix()
71 if hasattr(sb.parent_component(), "_deactivate_additional_constraints"): 71 ↛ 83line 71 didn't jump to line 83 because the condition on line 71 was always true
72 # state block may be defined by some constraints, so we need to solve it.
73 # initialize the state block, which should put the correct value in
74 # the state variables
75 sb.parent_component().initialize()
76 # deactivate the state block constraints, since we should use the
77 # state variables as guesses (or fix them instead)
78 sb.parent_component()._deactivate_additional_constraints()
79 # TODO: Validate if we need this or not. Aren't there some edge cases where this is a tear but it is replacing a variable from
80 # elsewhere in the flowsheet? in these cases we can't deactivate additional constraints.
81 # we should only deactivate whatever is replacing the state variables.
83 blk = sb.parent_component()
84 for key in sb.define_state_vars():
85 if guess.get(key, False):
86 # deactivate the equality constraint (expanded arcs)
87 expanded_arc = getattr(
88 self._flowsheet_manager.model.fs, arc._name + "_expanded"
89 )
90 equality_constraint = getattr(expanded_arc, key + "_equality")
91 equality_constraint.deactivate()
93 # fix the variable
94 for b in blk.values():
95 getattr(b, key).fix()
97 else:
98 # unfix this variable
99 for b in blk.values():
100 getattr(b, key).unfix()