Coverage for examples/SimpleBuilding/flex_output_data/created_flex_files/flex_agents.py: 100%

52 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-10-20 14:09 +0000

1import casadi as ca 

2import pandas as pd 

3from agentlib_mpc.models.casadi_model import ( 

4 CasadiModel, 

5 CasadiInput, 

6 CasadiState, 

7 CasadiParameter, 

8 CasadiOutput, 

9 CasadiModelConfig, 

10) 

11from math import inf 

12 

13 

14class BaselineMPCModelConfig(CasadiModelConfig): 

15 inputs: list[CasadiInput] = [ 

16 CasadiInput( 

17 name="P_in", 

18 value=100, 

19 unit="W", 

20 description="Electrical power of heating rod (equivalent to P_el)", 

21 ), 

22 CasadiInput( 

23 name="T_amb", value=290.15, unit="K", description="Ambient air temperature" 

24 ), 

25 CasadiInput( 

26 name="T_upper", 

27 value=294.15, 

28 unit="K", 

29 description="Upper boundary (soft) for T", 

30 ), 

31 CasadiInput( 

32 name="T_lower", 

33 value=290.15, 

34 unit="K", 

35 description="Lower boundary (soft) for T", 

36 ), 

37 CasadiInput( 

38 name="_P_external", 

39 value=0, 

40 unit="W", 

41 type="pd.Series", 

42 description="External power profile to be provided", 

43 ), 

44 CasadiInput( 

45 name="in_provision", 

46 value=False, 

47 unit="-", 

48 type="bool", 

49 description="Flag signaling if the flexibility is in provision", 

50 ), 

51 CasadiInput( 

52 name="rel_start", 

53 value=0, 

54 unit="s", 

55 type="int", 

56 description="relative start time of the flexibility event", 

57 ), 

58 CasadiInput( 

59 name="rel_end", 

60 value=0, 

61 unit="s", 

62 type="int", 

63 description="relative end time of the flexibility event", 

64 ), 

65 ] 

66 states: list[CasadiState] = [ 

67 CasadiState( 

68 name="T_zone", value=293.15, unit="K", description="Temperature of zone" 

69 ), 

70 CasadiState( 

71 name="T_slack", 

72 value=0, 

73 unit="K", 

74 description="Slack variable for zone temperature", 

75 ), 

76 ] 

77 parameters: list[CasadiParameter] = [ 

78 CasadiParameter( 

79 name="C", value=10000, unit="J/K", description="Thermal capacity of zone" 

80 ), 

81 CasadiParameter( 

82 name="U", value=5, unit="W/K", description="Thermal conductivity of zone" 

83 ), 

84 CasadiParameter( 

85 name="s_T", 

86 value=1, 

87 unit="-", 

88 description="Weight for zone temperature slack var in constraint function", 

89 ), 

90 CasadiParameter( 

91 name="r_pel", 

92 value=1, 

93 unit="-", 

94 description="Weight for P_el in objective function", 

95 ), 

96 CasadiParameter( 

97 name="profile_deviation_weight", 

98 value=0, 

99 unit="-", 

100 description="Weight of soft constraint for deviation from accepted flexible profile", 

101 ), 

102 ] 

103 outputs: list[CasadiOutput] = [ 

104 CasadiOutput( 

105 name="P_el", 

106 unit="W", 

107 description="Electrical power of heating rod (system input)", 

108 ) 

109 ] 

110 

111 

112class BaselineMPCModel(CasadiModel): 

113 config: BaselineMPCModelConfig 

114 

115 def setup_system(self): 

116 self.T_zone.ode = (self.P_in - self.U * (self.T_zone - self.T_amb)) / self.C 

117 self.P_el.alg = self.P_in 

118 self.constraints = [ 

119 (-inf, self.T_zone - self.T_slack, self.T_upper), 

120 (self.T_lower, self.T_zone + self.T_slack, inf), 

121 (0, self.T_slack, inf), 

122 (0, self.P_in, 200), 

123 ] 

124 objective = sum([self.s_T * self.T_slack**2, self.r_pel * self.P_in]) 

125 obj_std = objective 

126 return ca.if_else( 

127 self.in_provision.sym, 

128 ca.if_else( 

129 self.time < self.rel_start.sym, 

130 obj_std, 

131 ca.if_else( 

132 self.time >= self.rel_end.sym, 

133 obj_std, 

134 sum( 

135 [ 

136 self.profile_deviation_weight 

137 * (self.P_el - self._P_external) ** 2 

138 ] 

139 ), 

140 ), 

141 ), 

142 obj_std, 

143 ) 

144 

145 

146class PosFlexModelConfig(CasadiModelConfig): 

147 inputs: list[CasadiInput] = [ 

148 CasadiInput( 

149 name="P_in", 

150 value=100, 

151 unit="W", 

152 description="Electrical power of heating rod (equivalent to P_el)", 

153 ), 

154 CasadiInput( 

155 name="T_amb", value=290.15, unit="K", description="Ambient air temperature" 

156 ), 

157 CasadiInput( 

158 name="T_upper", 

159 value=294.15, 

160 unit="K", 

161 description="Upper boundary (soft) for T", 

162 ), 

163 CasadiInput( 

164 name="T_lower", 

165 value=290.15, 

166 unit="K", 

167 description="Lower boundary (soft) for T", 

168 ), 

169 CasadiInput( 

170 name="P_in_full", 

171 value=None, 

172 unit="Not defined", 

173 type="pd.Series", 

174 description="full control trajectory output of baseline mpc", 

175 ), 

176 CasadiInput( 

177 name="in_provision", 

178 value=False, 

179 unit="-", 

180 type="bool", 

181 description="provision flag", 

182 ), 

183 ] 

184 states: list[CasadiState] = [ 

185 CasadiState( 

186 name="T_zone", value=293.15, unit="K", description="Temperature of zone" 

187 ), 

188 CasadiState( 

189 name="T_slack", 

190 value=0, 

191 unit="K", 

192 description="Slack variable for zone temperature", 

193 ), 

194 ] 

195 parameters: list[CasadiParameter] = [ 

196 CasadiParameter( 

197 name="C", value=10000, unit="J/K", description="Thermal capacity of zone" 

198 ), 

199 CasadiParameter( 

200 name="U", value=5, unit="W/K", description="Thermal conductivity of zone" 

201 ), 

202 CasadiParameter( 

203 name="s_T", 

204 value=1, 

205 unit="-", 

206 description="Weight for zone temperature slack var in constraint function", 

207 ), 

208 CasadiParameter( 

209 name="r_pel", 

210 value=1, 

211 unit="-", 

212 description="Weight for P_el in objective function", 

213 ), 

214 CasadiParameter( 

215 name="prep_time", value=0, unit="s", description="time to switch objective" 

216 ), 

217 CasadiParameter( 

218 name="flex_event_duration", 

219 value=0, 

220 unit="s", 

221 description="time to switch objective", 

222 ), 

223 CasadiParameter( 

224 name="market_time", 

225 value=0, 

226 unit="s", 

227 description="time to switch objective", 

228 ), 

229 CasadiParameter( 

230 name="s_P", 

231 value=10, 

232 unit="-", 

233 description="Weight for P in objective function", 

234 ), 

235 ] 

236 outputs: list[CasadiOutput] = [ 

237 CasadiOutput( 

238 name="P_el", 

239 unit="W", 

240 description="Electrical power of heating rod (system input)", 

241 ) 

242 ] 

243 

244 

245class PosFlexModel(CasadiModel): 

246 config: PosFlexModelConfig 

247 

248 def setup_system(self): 

249 P_in_lower = ca.if_else( 

250 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.lb 

251 ) 

252 P_in_upper = ca.if_else( 

253 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.ub 

254 ) 

255 self.T_zone.ode = (self.P_in - self.U * (self.T_zone - self.T_amb)) / self.C 

256 self.P_el.alg = self.P_in 

257 self.constraints = [ 

258 (-inf, self.T_zone - self.T_slack, self.T_upper), 

259 (self.T_lower, self.T_zone + self.T_slack, inf), 

260 (0, self.T_slack, inf), 

261 (0, self.P_in, 200), 

262 (P_in_lower, self.P_in, P_in_upper), 

263 ] 

264 objective = sum([self.s_T * self.T_slack**2, self.r_pel * self.P_in]) 

265 obj_std = objective 

266 obj_flex = sum([self.s_T * self.T_slack**2, self.s_P * self.P_el]) 

267 return ca.if_else( 

268 self.time < self.prep_time.sym + self.market_time.sym, 

269 obj_std, 

270 ca.if_else( 

271 self.time 

272 < self.prep_time.sym 

273 + self.flex_event_duration.sym 

274 + self.market_time.sym, 

275 obj_flex, 

276 obj_std, 

277 ), 

278 ) 

279 

280 

281class NegFlexModelConfig(CasadiModelConfig): 

282 inputs: list[CasadiInput] = [ 

283 CasadiInput( 

284 name="P_in", 

285 value=100, 

286 unit="W", 

287 description="Electrical power of heating rod (equivalent to P_el)", 

288 ), 

289 CasadiInput( 

290 name="T_amb", value=290.15, unit="K", description="Ambient air temperature" 

291 ), 

292 CasadiInput( 

293 name="T_upper", 

294 value=294.15, 

295 unit="K", 

296 description="Upper boundary (soft) for T", 

297 ), 

298 CasadiInput( 

299 name="T_lower", 

300 value=290.15, 

301 unit="K", 

302 description="Lower boundary (soft) for T", 

303 ), 

304 CasadiInput( 

305 name="P_in_full", 

306 value=None, 

307 unit="Not defined", 

308 type="pd.Series", 

309 description="full control trajectory output of baseline mpc", 

310 ), 

311 CasadiInput( 

312 name="in_provision", 

313 value=False, 

314 unit="-", 

315 type="bool", 

316 description="provision flag", 

317 ), 

318 ] 

319 states: list[CasadiState] = [ 

320 CasadiState( 

321 name="T_zone", value=293.15, unit="K", description="Temperature of zone" 

322 ), 

323 CasadiState( 

324 name="T_slack", 

325 value=0, 

326 unit="K", 

327 description="Slack variable for zone temperature", 

328 ), 

329 ] 

330 parameters: list[CasadiParameter] = [ 

331 CasadiParameter( 

332 name="C", value=10000, unit="J/K", description="Thermal capacity of zone" 

333 ), 

334 CasadiParameter( 

335 name="U", value=5, unit="W/K", description="Thermal conductivity of zone" 

336 ), 

337 CasadiParameter( 

338 name="s_T", 

339 value=1, 

340 unit="-", 

341 description="Weight for zone temperature slack var in constraint function", 

342 ), 

343 CasadiParameter( 

344 name="r_pel", 

345 value=1, 

346 unit="-", 

347 description="Weight for P_el in objective function", 

348 ), 

349 CasadiParameter( 

350 name="prep_time", value=0, unit="s", description="time to switch objective" 

351 ), 

352 CasadiParameter( 

353 name="flex_event_duration", 

354 value=0, 

355 unit="s", 

356 description="time to switch objective", 

357 ), 

358 CasadiParameter( 

359 name="market_time", 

360 value=0, 

361 unit="s", 

362 description="time to switch objective", 

363 ), 

364 CasadiParameter( 

365 name="s_P", 

366 value=10, 

367 unit="-", 

368 description="Weight for P in objective function", 

369 ), 

370 ] 

371 outputs: list[CasadiOutput] = [ 

372 CasadiOutput( 

373 name="P_el", 

374 unit="W", 

375 description="Electrical power of heating rod (system input)", 

376 ) 

377 ] 

378 

379 

380class NegFlexModel(CasadiModel): 

381 config: NegFlexModelConfig 

382 

383 def setup_system(self): 

384 P_in_lower = ca.if_else( 

385 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.lb 

386 ) 

387 P_in_upper = ca.if_else( 

388 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.ub 

389 ) 

390 self.T_zone.ode = (self.P_in - self.U * (self.T_zone - self.T_amb)) / self.C 

391 self.P_el.alg = self.P_in 

392 self.constraints = [ 

393 (-inf, self.T_zone - self.T_slack, self.T_upper), 

394 (self.T_lower, self.T_zone + self.T_slack, inf), 

395 (0, self.T_slack, inf), 

396 (0, self.P_in, 200), 

397 (P_in_lower, self.P_in, P_in_upper), 

398 ] 

399 objective = sum([self.s_T * self.T_slack**2, self.r_pel * self.P_in]) 

400 obj_std = objective 

401 obj_flex = sum([self.s_T * self.T_slack**2, -self.s_P * self.P_el]) 

402 return ca.if_else( 

403 self.time < self.prep_time.sym + self.market_time.sym, 

404 obj_std, 

405 ca.if_else( 

406 self.time 

407 < self.prep_time.sym 

408 + self.flex_event_duration.sym 

409 + self.market_time.sym, 

410 obj_flex, 

411 obj_std, 

412 ), 

413 )