Coverage for backend/idaes_service/solver/custom/inverted.py: 100%
30 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 pyomo.environ import Var, Constraint, units as pyunits, Block, Reference
3def add_inverted(block: Block,component_name: str):
4 """
5 Adds inverted variables and constraints to a block with variable component_name.
6 e.g if component_name is 'deltaP', adds deltaP_inverted variable, so we can do
7 pressure drop instead of pressure increase.
8 """
9 inverted_component_name = f"{component_name}_inverted"
11 component = getattr(block, component_name)
13 inverted_component = Var(
14 component.index_set(),
15 # Explanation for units:
16 # Reference() turns a var/expression into a indexed object, even if it's scalar.
17 # next() gets the first item from the indexed object
18 # This is a way of getting around the fact that an indexed component
19 # will only have its units defined at the individual index level.
20 units=next(Reference(component).values()).get_units(),
21 )
23 block.add_component(inverted_component_name, inverted_component)
25 def _inverted_rule(b, *indexes):
26 return inverted_component[indexes] == -component[indexes]
28 constraint = Constraint(
29 component.index_set(),
30 rule=_inverted_rule,
31 doc=f"Inverted {component_name} Constraint",
32 )
33 block.add_component(f"{inverted_component_name}_constraint", constraint)
35def initialise_inverted(block: Block, component_name: str):
36 """
37 Initialises the inverted deltaP variables to match the deltaP variable values,
38 or vice versa (depending on which is fixed).
40 This is generalised so you can pass any component name.
41 """
42 inverted_component_name = f"{component_name}_inverted"
43 component = getattr(block, component_name)
44 inverted_component = getattr(block, inverted_component_name)
46 for indexes in component.index_set():
47 if component[indexes].fixed:
48 # we can savely assume that the inverted variable is not
49 # fixed, otherwise this would be an overconstrained model.
50 inverted_component[indexes].value = -component[indexes].value
51 elif inverted_component[indexes].fixed:
52 component[indexes].value = -inverted_component[indexes].value
53 else:
54 # neither variable is fixed,
55 # so let's just leave them as is.
56 # Hopefully the solver can figure it out.
57 pass
59def disable_inverted(block: Block, component_name: str) -> bool:
60 """
61 Disables the inverted variables and constraints for a given component.
62 Returns True if the inverted variable was fixed, false otherwise.
63 """
64 inverted_component_name = f"{component_name}_inverted"
65 constraint_name = f"{inverted_component_name}_constraint"
67 inverted_constraint = getattr(block, constraint_name)
69 inverted_constraint.deactivate()
72def enable_inverted(block: Block, component_name: str):
73 """
74 Enables the inverted variables and constraints for a given component.
75 """
76 inverted_component_name = f"{component_name}_inverted"
77 constraint_name = f"{inverted_component_name}_constraint"
79 inverted_constraint = getattr(block, constraint_name)
81 inverted_constraint.activate()