Coverage for backend/idaes_service/solver/custom/energy/acBus.py: 34%

91 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 Constraint, 

5 Set, 

6 Var, 

7 Suffix, 

8 units as pyunits, 

9) 

10from pyomo.environ import Reals 

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

12from idaes.core.util.model_statistics import degrees_of_freedom 

13# Import IDAES cores 

14from idaes.core import ( 

15 declare_process_block_class, 

16 UnitModelBlockData, 

17 useDefault, 

18 MaterialBalanceType, 

19 MaterialFlowBasis, 

20) 

21from idaes.core.util.config import is_physical_parameter_block 

22import idaes.core.util.scaling as iscale 

23import idaes.logger as idaeslog 

24from idaes.models.unit_models import separator 

25from idaes.core.util.tables import create_stream_table_dataframe 

26from idaes.core.util.exceptions import ConfigurationError, BurntToast, PropertyNotSupportedError 

27 

28from idaes.models.unit_models import separator 

29#Import enum 

30from enum import Enum 

31 

32# Set up logger 

33_log = idaeslog.getLogger(__name__) 

34 

35# Enumerate options for balances 

36class SplittingType(Enum): 

37 """ 

38 Enum of supported material split types. 

39 """ 

40 

41 totalFlow = 1 

42 phaseFlow = 2 

43 componentFlow = 3 

44 phaseComponentFlow = 4 

45 

46 

47 

48# When using this file the name "acBus" is what is imported 

49@declare_process_block_class("acBus") 

50class acBusData(UnitModelBlockData): 

51 """ 

52 Zero order acbus model 

53 """ 

54 

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

56 CONFIG = ConfigBlock() 

57 

58 CONFIG.declare( 

59 "dynamic", 

60 ConfigValue( 

61 domain=In([False]), 

62 default=False, 

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

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

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

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

67 ), 

68 ) 

69 CONFIG.declare( 

70 "has_holdup", 

71 ConfigValue( 

72 default=False, 

73 domain=In([False]), 

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

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

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

77 this must be False.""", 

78 ), 

79 ) 

80 CONFIG.declare( 

81 "property_package", 

82 ConfigValue( 

83 default=useDefault, 

84 domain=is_physical_parameter_block, 

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

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

87 **default** - useDefault. 

88 **Valid values:** { 

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

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

91 ), 

92 ) 

93 CONFIG.declare( 

94 "property_package_args", 

95 ConfigBlock( 

96 implicit=True, 

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

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

99 and used when constructing these, 

100 **default** - None. 

101 **Valid values:** { 

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

103 ), 

104 ) 

105 CONFIG.declare( 

106 "num_inlets", 

107 ConfigValue( 

108 default=False, 

109 domain=int, 

110 description="Number of inlets to add", 

111 doc="Number of inlets to add", 

112 ), 

113 ) 

114 CONFIG.declare( 

115 "num_outlets", 

116 ConfigValue( 

117 default=False, 

118 domain=int, 

119 description="Number of outlets to add", 

120 doc="Number of outlets to add", 

121 ), 

122 ) 

123 CONFIG.declare( 

124 "material_balance_type", 

125 ConfigValue( 

126 default=MaterialBalanceType.useDefault, 

127 domain=In(MaterialBalanceType), 

128 description="Material balance construction flag", 

129 doc="""Indicates what type of mass balance should be constructed, 

130 **default** - MaterialBalanceType.useDefault. 

131 **Valid values:** { 

132 **MaterialBalanceType.useDefault - refer to property package for default 

133 balance type 

134 **MaterialBalanceType.none** - exclude material balances, 

135 **MaterialBalanceType.componentPhase** - use phase component balances, 

136 **MaterialBalanceType.componentTotal** - use total component balances, 

137 **MaterialBalanceType.elementTotal** - use total element balances, 

138 **MaterialBalanceType.total** - use total material balance.}""", 

139 ), 

140 ) 

141 

142 

143 def build(self): 

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

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

146 super().build() 

147 

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

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

150 

151 

152 # Defining parameters of state block class 

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

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

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

156 

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

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

159 num_inlets = self.config.num_inlets 

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

161 self.inlet_set = Set(initialize=self.inlet_list) 

162 self.inlet_blocks = [] 

163 

164 for name in self.inlet_list: 

165 # add properties_inlet_1, properties_inlet2 etc 

166 state_block = self.config.property_package.state_block_class( 

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

168 ) 

169 self.inlet_blocks.append(state_block) 

170 # Dynamic equivalent to self.properties_inlet_1 = stateblock 

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

172 # also add the port 

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

174 

175 

176 # Add outlet state blocks 

177 

178 num_outlets = self.config.num_outlets 

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

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

181 self.outlet_blocks = [] 

182 

183 for name in self.outlet_list: 

184 tmp_dict["defined_state"] = False 

185 state_block = self.config.property_package.state_block_class( 

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

187 ) 

188 self.outlet_blocks.append(state_block) 

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

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

191 

192 # Add variable for power splitting 

193 self.split_fraction = Var( 

194 self.flowsheet().time, 

195 self.outlet_set, 

196 initialize=1.0, 

197 #units = pyunits.dimensionless, 

198 doc="How the power is split between outlets", 

199 ) 

200 

201 #Obtain the power components from the inlets 

202 @self.Expression( 

203 self.flowsheet().time, 

204 ) 

205 def total_active_power(b,t): 

206 return sum(state_block[t].active_power for state_block in self.inlet_blocks) 

207 

208 @self.Expression( 

209 self.flowsheet().time, 

210 ) 

211 def total_reactive_power(b,t): 

212 return sum(state_block[t].reactive_power for state_block in self.inlet_blocks) 

213 

214 @self.Expression( 

215 self.flowsheet().time, 

216 ) 

217 def total_voltage(b,t): 

218 return sum(state_block[t].voltage for state_block in self.inlet_blocks) 

219 

220 #Add constraints 

221 

222 @self.Constraint( 

223 self.flowsheet().time, 

224 self.outlet_list, 

225 doc="active power split", 

226 ) 

227 def eq_active_power(b,t,o): 

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

229 return outlet_block[t].active_power == ( 

230 self.total_active_power[t] * b.split_fraction[t,o]) 

231 

232 @self.Constraint( 

233 self.flowsheet().time, 

234 self.outlet_list, 

235 doc="reactive power split", 

236 ) 

237 def eq_reactive_power(b,t,o): 

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

239 return outlet_block[t].reactive_power == ( 

240 self.total_reactive_power[t] * b.split_fraction[t,o]) 

241 

242 @self.Constraint( 

243 self.flowsheet().time, 

244 self.outlet_list, 

245 doc="voltage split", 

246 ) 

247 def eq_voltage(b,t,o): 

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

249 return outlet_block[t].voltage == ( 

250 self.total_voltage[t] * b.split_fraction[t,o]) 

251 

252 

253 @self.Constraint( 

254 self.flowsheet().time, 

255 doc="Split fraction sum to 1", 

256 ) 

257 def eq_split_fraction_sum(b, t): 

258 return sum(b.split_fraction[t, o] for o in self.outlet_list) == 1.0 

259 

260 

261 def calculate_scaling_factors(self): 

262 super().calculate_scaling_factors() 

263 

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

265 

266 pass 

267 

268 

269 def _get_stream_table_contents(self, time_point=0): 

270 

271 io_dict = {} 

272 for inlet_name in self.inlet_list: 

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

274 

275 out_dict = {} 

276 for outlet_name in self.outlet_list: 

277 out_dict[outlet_name] = getattr(self, outlet_name) # get a reference to the port 

278 

279 

280