Coverage for backend/idaes_service/solver/custom/energy/link.py: 95%

35 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-11-06 23:27 +0000

1# Import Pyomo libraries 

2from pyomo.environ import ( 

3 Var, 

4 Suffix, 

5 units as pyunits, 

6) 

7from pyomo.common.config import ConfigBlock, ConfigValue, In 

8 

9# Import IDAES cores 

10from idaes.core import ( 

11 declare_process_block_class, 

12 UnitModelBlockData, 

13 useDefault, 

14) 

15from idaes.core.util.config import is_physical_parameter_block 

16import idaes.core.util.scaling as iscale 

17import idaes.logger as idaeslog 

18 

19# Set up logger 

20_log = idaeslog.getLogger(__name__) 

21 

22 

23# When using this file the name "Link" is what is imported 

24@declare_process_block_class("Link") 

25class LinkData(UnitModelBlockData): 

26 """ 

27 Zero order Link model 

28 """ 

29 

30 # CONFIG are options for the unit model, this simple model only has the mandatory config options 

31 CONFIG = ConfigBlock() 

32 

33 CONFIG.declare( 

34 "dynamic", 

35 ConfigValue( 

36 domain=In([False]), 

37 default=False, 

38 description="Dynamic model flag - must be False", 

39 doc="""Indicates whether this model will be dynamic or not, 

40 **default** = False. The Bus unit does not support dynamic 

41 behavior, thus this must be False.""", 

42 ), 

43 ) 

44 CONFIG.declare( 

45 "has_holdup", 

46 ConfigValue( 

47 default=False, 

48 domain=In([False]), 

49 description="Holdup construction flag - must be False", 

50 doc="""Indicates whether holdup terms should be constructed or not. 

51 **default** - False. The Bus unit does not have defined volume, thus 

52 this must be False.""", 

53 ), 

54 ) 

55 CONFIG.declare( 

56 "property_package", 

57 ConfigValue( 

58 default=useDefault, 

59 domain=is_physical_parameter_block, 

60 description="Property package to use for control volume", 

61 doc="""Property parameter object used to define property calculations, 

62 **default** - useDefault. 

63 **Valid values:** { 

64 **useDefault** - use default package from parent model or flowsheet, 

65 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", 

66 ), 

67 ) 

68 CONFIG.declare( 

69 "property_package_args", 

70 ConfigBlock( 

71 implicit=True, 

72 description="Arguments to use for constructing property packages", 

73 doc="""A ConfigBlock with arguments to be passed to a property block(s) 

74 and used when constructing these, 

75 **default** - None. 

76 **Valid values:** { 

77 see property package for documentation.}""", 

78 ), 

79 ) 

80 

81 def build(self): 

82 # build always starts by calling super().build() 

83 # This triggers a lot of boilerplate in the background for you 

84 super().build() 

85 

86 # This creates blank scaling factors, which are populated later 

87 self.scaling_factor = Suffix(direction=Suffix.EXPORT) 

88 

89 

90 # Add state blocks for inlet, outlet, and waste 

91 # These include the state variables and any other properties on demand 

92 # Add inlet block 

93 tmp_dict = dict(**self.config.property_package_args) 

94 tmp_dict["parameters"] = self.config.property_package 

95 tmp_dict["defined_state"] = True # inlet block is an inlet 

96 self.properties_in = self.config.property_package.state_block_class( 

97 self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict 

98 ) 

99 # Add outlet and waste block 

100 tmp_dict["defined_state"] = False # outlet and waste block is not an inlet 

101 self.properties_out = self.config.property_package.state_block_class( 

102 self.flowsheet().config.time, 

103 doc="Material properties of outlet", 

104 **tmp_dict 

105 ) 

106 

107 # Add ports - oftentimes users interact with these rather than the state blocks 

108 self.add_port(name="inlet", block=self.properties_in) 

109 self.add_port(name="outlet", block=self.properties_out) 

110 

111 # Add variable for efficiency 

112 self.efficiency = Var(self.flowsheet().config.time, 

113 initialize=1.0, 

114 doc="Efficiency of the bus", 

115 ) 

116 

117 # Add constraints 

118 # Usually unit models use a control volume to do the mass, energy, and momentum 

119 # balances, however, they will be explicitly written out in this example 

120 @self.Constraint( 

121 self.flowsheet().time, 

122 doc="Power usage", 

123 ) 

124 def eq_power_balance(b, t): 

125 return ( 

126 b.properties_in[t].power * self.efficiency[t] == b.properties_out[t].power 

127 ) 

128 

129 

130 def calculate_scaling_factors(self): 

131 super().calculate_scaling_factors() 

132 

133 def initialize(blk, *args, **kwargs): 

134 # Just propagate the power from inlet to outlet, good simple method of initialization 

135 for i in blk.properties_in.index_set(): 

136 if not blk.properties_out[i].power.fixed: 136 ↛ 135line 136 didn't jump to line 135 because the condition on line 136 was always true

137 blk.properties_out[i].power = blk.properties_in[i].power.value