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