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

1from ahuora_builder.methods.adapter import ( 

2 add_corresponding_constraint, 

3 fix_slice, 

4 load_initial_guesses, 

5) 

6 

7 

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 

17 

18 

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. 

25 

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. 

31 

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 """ 

36 

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 

49 

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) 

52 

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) 

55 

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) 

83 

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)