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 2026-03-26 09:43 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2026-03-26 09:43 +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
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 ]
112class BaselineMPCModel(CasadiModel):
113 config: BaselineMPCModelConfig
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 )
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="Not defined",
180 type="bool",
181 description="Flag indicating whether flexibility should be provisioned",
182 ),
183 CasadiInput(
184 name="P_el_base",
185 value=0,
186 unit="Not defined",
187 type="None",
188 description="Not defined",
189 ),
190 ]
191 states: list[CasadiState] = [
192 CasadiState(
193 name="T_zone", value=293.15, unit="K", description="Temperature of zone"
194 ),
195 CasadiState(
196 name="T_slack",
197 value=0,
198 unit="K",
199 description="Slack variable for zone temperature",
200 ),
201 ]
202 parameters: list[CasadiParameter] = [
203 CasadiParameter(
204 name="C", value=10000, unit="J/K", description="Thermal capacity of zone"
205 ),
206 CasadiParameter(
207 name="U", value=5, unit="W/K", description="Thermal conductivity of zone"
208 ),
209 CasadiParameter(
210 name="s_T",
211 value=1,
212 unit="-",
213 description="Weight for zone temperature slack var in constraint function",
214 ),
215 CasadiParameter(
216 name="r_pel",
217 value=1,
218 unit="-",
219 description="Weight for P_el in objective function",
220 ),
221 CasadiParameter(
222 name="prep_time",
223 value=900,
224 unit="s",
225 description="Preparation time before switching objective",
226 ),
227 CasadiParameter(
228 name="flex_event_duration",
229 value=7200,
230 unit="s",
231 description="Duration of the flexibility event",
232 ),
233 CasadiParameter(
234 name="market_time",
235 value=900,
236 unit="s",
237 description="Market time associated with the objective switch",
238 ),
239 CasadiParameter(
240 name="s_P", value=10, unit="Not defined", description="Not defined"
241 ),
242 ]
243 outputs: list[CasadiOutput] = [
244 CasadiOutput(
245 name="P_el",
246 unit="W",
247 description="Electrical power of heating rod (system input)",
248 )
249 ]
252class PosFlexModel(CasadiModel):
253 config: PosFlexModelConfig
255 def setup_system(self):
256 P_in_lower = ca.if_else(
257 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.lb
258 )
259 P_in_upper = ca.if_else(
260 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.ub
261 )
262 self.T_zone.ode = (self.P_in - self.U * (self.T_zone - self.T_amb)) / self.C
263 self.P_el.alg = self.P_in
264 self.constraints = [
265 (-inf, self.T_zone - self.T_slack, self.T_upper),
266 (self.T_lower, self.T_zone + self.T_slack, inf),
267 (0, self.T_slack, inf),
268 (0, self.P_in, 200),
269 (P_in_lower, self.P_in, P_in_upper),
270 ]
271 objective = sum([self.s_T * self.T_slack**2, self.r_pel * self.P_in])
272 obj_std = objective
273 obj_flex = sum([self.s_T * self.T_slack**2, self.s_P * self.P_el])
274 return ca.if_else(
275 self.time < self.prep_time.sym + self.market_time.sym,
276 obj_std,
277 ca.if_else(
278 self.time
279 < self.prep_time.sym
280 + self.flex_event_duration.sym
281 + self.market_time.sym,
282 obj_flex,
283 obj_std,
284 ),
285 )
288class NegFlexModelConfig(CasadiModelConfig):
289 inputs: list[CasadiInput] = [
290 CasadiInput(
291 name="P_in",
292 value=100,
293 unit="W",
294 description="Electrical power of heating rod (equivalent to P_el)",
295 ),
296 CasadiInput(
297 name="T_amb", value=290.15, unit="K", description="Ambient air temperature"
298 ),
299 CasadiInput(
300 name="T_upper",
301 value=294.15,
302 unit="K",
303 description="Upper boundary (soft) for T",
304 ),
305 CasadiInput(
306 name="T_lower",
307 value=290.15,
308 unit="K",
309 description="Lower boundary (soft) for T",
310 ),
311 CasadiInput(
312 name="P_in_full",
313 value=None,
314 unit="Not defined",
315 type="pd.Series",
316 description="full control trajectory output of baseline mpc",
317 ),
318 CasadiInput(
319 name="in_provision",
320 value=False,
321 unit="Not defined",
322 type="bool",
323 description="Flag indicating whether flexibility should be provisioned",
324 ),
325 CasadiInput(
326 name="P_el_base",
327 value=0,
328 unit="Not defined",
329 type="None",
330 description="Not defined",
331 ),
332 ]
333 states: list[CasadiState] = [
334 CasadiState(
335 name="T_zone", value=293.15, unit="K", description="Temperature of zone"
336 ),
337 CasadiState(
338 name="T_slack",
339 value=0,
340 unit="K",
341 description="Slack variable for zone temperature",
342 ),
343 ]
344 parameters: list[CasadiParameter] = [
345 CasadiParameter(
346 name="C", value=10000, unit="J/K", description="Thermal capacity of zone"
347 ),
348 CasadiParameter(
349 name="U", value=5, unit="W/K", description="Thermal conductivity of zone"
350 ),
351 CasadiParameter(
352 name="s_T",
353 value=1,
354 unit="-",
355 description="Weight for zone temperature slack var in constraint function",
356 ),
357 CasadiParameter(
358 name="r_pel",
359 value=1,
360 unit="-",
361 description="Weight for P_el in objective function",
362 ),
363 CasadiParameter(
364 name="prep_time",
365 value=900,
366 unit="s",
367 description="Preparation time before switching objective",
368 ),
369 CasadiParameter(
370 name="flex_event_duration",
371 value=7200,
372 unit="s",
373 description="Duration of the flexibility event",
374 ),
375 CasadiParameter(
376 name="market_time",
377 value=900,
378 unit="s",
379 description="Market time associated with the objective switch",
380 ),
381 CasadiParameter(
382 name="s_P", value=10, unit="Not defined", description="Not defined"
383 ),
384 ]
385 outputs: list[CasadiOutput] = [
386 CasadiOutput(
387 name="P_el",
388 unit="W",
389 description="Electrical power of heating rod (system input)",
390 )
391 ]
394class NegFlexModel(CasadiModel):
395 config: NegFlexModelConfig
397 def setup_system(self):
398 P_in_lower = ca.if_else(
399 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.lb
400 )
401 P_in_upper = ca.if_else(
402 self.time < self.market_time.sym, self.P_in_full.sym, self.P_in.ub
403 )
404 self.T_zone.ode = (self.P_in - self.U * (self.T_zone - self.T_amb)) / self.C
405 self.P_el.alg = self.P_in
406 self.constraints = [
407 (-inf, self.T_zone - self.T_slack, self.T_upper),
408 (self.T_lower, self.T_zone + self.T_slack, inf),
409 (0, self.T_slack, inf),
410 (0, self.P_in, 200),
411 (P_in_lower, self.P_in, P_in_upper),
412 ]
413 objective = sum([self.s_T * self.T_slack**2, self.r_pel * self.P_in])
414 obj_std = objective
415 obj_flex = sum([self.s_T * self.T_slack**2, -self.s_P * self.P_el])
416 return ca.if_else(
417 self.time < self.prep_time.sym + self.market_time.sym,
418 obj_std,
419 ca.if_else(
420 self.time
421 < self.prep_time.sym
422 + self.flex_event_duration.sym
423 + self.market_time.sym,
424 obj_flex,
425 obj_std,
426 ),
427 )