Coverage for backend/ahuora-builder/src/ahuora_builder/methods/BlockContext.py: 100%
35 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
1from ahuora_builder.methods.adapter import (
2 add_corresponding_constraint,
3 fix_slice,
4 load_initial_guesses,
5)
8from idaes.core import FlowsheetBlock
9from pyomo.core.base.constraint import Constraint
10from pyomo.environ import Block, Component, Reference
11from ahuora_builder_types.id_types import PropertyValueId
12from pyomo.core.base.indexed_component_slice import (
13 IndexedComponent_slice,
14 _IndexedComponent_slice_iter,
15)
16from pyomo.core.base.indexed_component import UnindexedComponent_set, IndexedComponent
19class BlockContext:
20 """
21 Where possible, we want to fix variables at the block level (ie. unit model, state block)
22 rather than at the flowsheet level. This is because it is easier to solve a smaller model
23 during initialization, rather than dumping complexity on the solver when solving the entire
24 flowsheet.
26 Each controlling variable in the model is accompanied by a guess variable. Normally, the
27 guess variable is fixed during initialization, and unfixed after, while the controlling
28 variable (set point) is a constraint at the flowsheet level. However, if both the guess and
29 controlling variable are on the same block, we can avoid this and fix the controlling variable
30 directly at the block level.
32 This class provides a context to store controlling variables and guess variables while fixing
33 within a block. We can then apply a simple heuristic to eliminate as many pairs of guess and
34 controlling variables as possible.
35 """
37 def __init__(self, flowsheet: FlowsheetBlock):
38 """
39 - blk: Pyomo block the Var is on (can add constraints to this block)
40 - var: Pyomo Var to fix/constrain
41 - value: value to fix/constrain the Var to
42 - id: id of the property, to store the created Constraint in the properties map
43 """
44 # property id: ( var_reference, values)
45 self._guess_vars: dict[PropertyValueId, tuple[ IndexedComponent | IndexedComponent_slice, list[float]]] = {}
46 # property id: ( var_reference, values, guess_id)
47 self._controlled_vars: dict[PropertyValueId, tuple[IndexedComponent | IndexedComponent_slice, list[float], PropertyValueId]] = {}
48 self._flowsheet = flowsheet
50 def add_guess_var(self, var_references : IndexedComponent | IndexedComponent_slice, values : list[float], propertyvalue_id : PropertyValueId):
51 self._guess_vars[propertyvalue_id] = ( var_references, values)
53 def add_controlled_var(self, var_references: IndexedComponent | IndexedComponent_slice, values: list[float], propertyvalue_id: PropertyValueId, guess_propertyvalue_id: PropertyValueId):
54 self._controlled_vars[propertyvalue_id] = (var_references, values, guess_propertyvalue_id)
56 def apply_elimination(self):
57 """
58 Try to eliminate as many guess vars/flowsheet-level constraints as possible.
59 Fix the remaining guess vars or add the remaining controlled vars as constraints.
60 """
61 fs = self._flowsheet
62 for id, (var_refs, values, guess_id) in self._controlled_vars.items():
63 # see if we can eliminate this controlled var
64 if guess_id in self._guess_vars:
65 guess_refs, guess_values = self._guess_vars[guess_id]
66 # fix the controlled var
67 c = fix_slice(var_refs, values)
68 add_corresponding_constraint(fs, c, id)
69 # load the initial guess for the guess var
70 load_initial_guesses(guess_refs, guess_values)
71 # eliminate the guess var
72 del self._guess_vars[guess_id]
73 else:
74 # add the control as a flowsheet-level constraint
75 # As the values are flattended into a list, we also need to flatten the index set into a list.
76 var_refs_list = list(var_refs.values()) # returns a list of VarData or ExpressionData objects
77 def constraint_rule(blk,idx):
78 return var_refs_list[idx] == values[idx]
79 c = Constraint(range(len(var_refs_list)), rule=constraint_rule)
80 name = f"control_constraint_{id}" # Maybe we could use the var name or something here? but it's a bit harder with indexed constraints. remember it has to be unique!
81 self._flowsheet.add_component(name, c)
82 add_corresponding_constraint(fs, c, id)
84 # fix the remaining guess vars
85 for id, (var_refs, values) in self._guess_vars.items():
86 c = fix_slice(var_refs, values)
87 self._flowsheet.guess_vars.append(c)