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

1from pyomo.environ import Var, Constraint, units as pyunits, Block, Reference 

2 

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" 

10 

11 component = getattr(block, component_name) 

12 

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 ) 

22 

23 block.add_component(inverted_component_name, inverted_component) 

24 

25 def _inverted_rule(b, *indexes): 

26 return inverted_component[indexes] == -component[indexes] 

27 

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) 

34 

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). 

39 

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) 

45 

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 

58 

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" 

66 

67 inverted_constraint = getattr(block, constraint_name) 

68 

69 inverted_constraint.deactivate() 

70 

71 

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" 

78 

79 inverted_constraint = getattr(block, constraint_name) 

80 

81 inverted_constraint.activate()