Coverage for backend/idaes_service/solver/custom/energy/mainDistributionBoard.py: 81%

83 statements  

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

1# Import Pyomo libraries 

2from stringprep import in_table_a1 

3from pyomo.environ import ( 

4 Var, 

5 Suffix, 

6 units as pyunits, 

7 Set, 

8 value 

9) 

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

11 

12# Import IDAES cores 

13from idaes.core import ( 

14 declare_process_block_class, 

15 UnitModelBlockData, 

16 useDefault, 

17) 

18from idaes.core.util.config import is_physical_parameter_block 

19import idaes.core.util.scaling as iscale 

20import idaes.logger as idaeslog 

21 

22from idaes.core.util.tables import create_stream_table_dataframe 

23from idaes.core.util.math import smooth_min 

24from idaes.core.util.exceptions import ConfigurationError 

25 

26# Set up logger 

27_log = idaeslog.getLogger(__name__) 

28 

29 

30# When using this file the name "MDB" is what is imported 

31@declare_process_block_class("MDB") 

32class MDBData(UnitModelBlockData): 

33 """ 

34 Zero order power distirbution board model 

35 """ 

36 

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

38 CONFIG = ConfigBlock() 

39 

40 CONFIG.declare( 

41 "dynamic", 

42 ConfigValue( 

43 domain=In([False]), 

44 default=False, 

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

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

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

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

49 ), 

50 ) 

51 CONFIG.declare( 

52 "has_holdup", 

53 ConfigValue( 

54 default=False, 

55 domain=In([False]), 

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

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

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

59 this must be False.""", 

60 ), 

61 ) 

62 CONFIG.declare( 

63 "property_package", 

64 ConfigValue( 

65 default=useDefault, 

66 domain=is_physical_parameter_block, 

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

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

69 **default** - useDefault. 

70 **Valid values:** { 

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

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

73 ), 

74 ) 

75 CONFIG.declare( 

76 "property_package_args", 

77 ConfigBlock( 

78 implicit=True, 

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

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

81 and used when constructing these, 

82 **default** - None. 

83 **Valid values:** { 

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

85 ), 

86 ) 

87 CONFIG.declare( 

88 "num_inlets", 

89 ConfigValue( 

90 default=False, 

91 domain=int, 

92 description="Number of inlets to add", 

93 doc="""Number of inlets to add""", 

94 ), 

95 ) 

96 CONFIG.declare( 

97 "num_outlets", 

98 ConfigValue( 

99 default=False, 

100 domain=int, 

101 description="Number of outlets to add", 

102 doc="""Number of outlets to add""", 

103 ), 

104 ) 

105 

106 def build(self): 

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

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

109 super().build() 

110 

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

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

113 

114 

115 # Defining parameters of state block class 

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

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

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

119 

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

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

122 num_inlets = self.config.num_inlets 

123 

124 self.inlet_list = [ "inlet_" + str(i+1) for i in range(num_inlets) ] 

125 

126 

127 self.inlet_blocks = [] 

128 for name in self.inlet_list: 

129 # add properties_inlet_1, properties_inlet2 etc 

130 state_block = self.config.property_package.state_block_class( 

131 self.flowsheet().config.time, doc="inlet power", **tmp_dict 

132 ) 

133 self.inlet_blocks.append(state_block) 

134 # Dynamic equivalent to self.properties_inlet_1 = stateblock 

135 setattr(self,"properties_" + name, state_block) 

136 # also add the port 

137 self.add_port(name=name,block=state_block) 

138 

139 

140 num_outlets = self.config.num_outlets 

141 self.outlet_list = [ "outlet_" + str(i+1) for i in range(num_outlets) ] 

142 self.priority = [i for i in range(num_outlets)] 

143 self.outlet_set = Set(initialize=self.outlet_list) 

144 

145 

146 self.outlet_blocks = [] 

147 for name in self.outlet_list: 

148 # add properties_outlet_1, properties_outlet2 etc 

149 state_block = self.config.property_package.state_block_class( 

150 self.flowsheet().config.time, doc="outlet power", **tmp_dict 

151 ) 

152 self.outlet_blocks.append(state_block) 

153 # Dynamic equivalent to self.properties_outlet_1 = stateblock 

154 setattr(self,"properties_" + name, state_block) 

155 # also add the port 

156 self.add_port(name=name,block=state_block) 

157 

158 # Add variable for power splitting 

159 self.priorities= Var( 

160 self.flowsheet().time, 

161 self.outlet_set, 

162 initialize=1.0, 

163 units = pyunits.W, 

164 doc="How the power is split between outlets depending on priority", 

165 ) 

166 #Add array to store power demand at each outlet 

167 self.demand = [] 

168 

169 self.available = Var( 

170 self.flowsheet().time, 

171 self.outlet_list, 

172 initialize = 1.0, 

173 units = pyunits.dimensionless 

174 ) 

175 

176 #Expression for total power supply: 

177 @self.Expression( 

178 self.flowsheet().time 

179 ) 

180 def total_power(b, t): 

181 return ( 

182 sum( 

183 state_block[t].power for state_block in self.inlet_blocks 

184 ) 

185 ) 

186 

187 #Expression for total power demand: 

188 @self.Expression( 

189 self.flowsheet().time 

190 ) 

191 def total_power_demand(b, t): 

192 return ( 

193 sum( 

194 state_block[t].power for state_block in self.outlet_blocks 

195 ) 

196 ) 

197 

198 #Constraint for available power: 

199 @self.Constraint( 

200 self.flowsheet().time, 

201 self.outlet_list, 

202 doc="expression for calculating available power" 

203 ) 

204 def eq_available_power(b,t,o): 

205 p = self.outlet_list.index(o) 

206 if o == self.outlet_list[0]: 

207 return self.available[t,o] == self.total_power[t] 

208 else: 

209 outlet_block = getattr(self,"properties_" + self.outlet_list[p-1]) 

210 return self.available[t,o] == self.available[t,self.outlet_list[p-1]] - outlet_block[t].power 

211 

212 

213 @self.Constraint( 

214 self.flowsheet().time, 

215 self.outlet_list, 

216 doc = "power out" 

217 ) 

218 def eq_power_out(b,t,o): 

219 outlet_block = getattr(self,"properties_" + o) 

220 if o == self.outlet_list[-1]: 

221 return outlet_block[t].power == self.available[t,o] 

222 else: 

223 return outlet_block[t].power == smooth_min(self.priorities[t,o], self.available[t,o]) 

224 

225 @self.Constraint( 

226 self.flowsheet().time, 

227 doc = "Power at last outlet" 

228 ) 

229 def eq_last_outlet(b,t): 

230 return self.priorities[t, self.outlet_list[-1]] == self.available[t,self.outlet_list[-1]] 

231 

232 

233 def calculate_scaling_factors(self): 

234 super().calculate_scaling_factors() 

235 

236 

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

238 for t in blk.flowsheet().time: 

239 power_in = 0 

240 for state_block in blk.inlet_blocks: 

241 power_in += state_block[t].power.value 

242 

243 

244 def _get_stream_table_contents(self, time_point=0): 

245 """ 

246 Assume unit has standard configuration of 1 inlet and 1 outlet. 

247 

248 Developers should overload this as appropriate. 

249 """ 

250 

251 io_dict = {} 

252 for inlet_name in self.inlet_list: 

253 io_dict[inlet_name] = getattr(self, inlet_name) # get a reference to the port 

254 

255 io_dict = {} 

256 for outlet_name in self.outlet_list: 

257 io_dict[outlet_name] = getattr(self, outlet_name)