-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
445 lines (391 loc) · 18.7 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
import gurobipy as gp
from itertools import product
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def division(a,b):
if a == 0:
return 0
if b == 0:
return 0
return a/b
#####################################################################################
# Partie 1
#####################################################################################
# 1.1 ###############################################################################
class Centrale:
'''
Class permettant de regrouper les informations sur un type de centrale pour faciliter
l'implementation du code
'''
def __init__(self, name, N, Pmin, Pmax, Cmwh):
self.name = name
self.N = N
self.Pmin = Pmin
self.Pmax = Pmax
self.Cmwh = Cmwh
def variable_decision_thermique(model,dict_Thermique):
'''
Fontion générant les variables de décision
Output :
dict_N[X,t] dictionnaire contenant les variables de décision associées au nombre de centrale X en fonctionnement à l'instant t
dict_P[X,t] dictionnaire contenant les variables de décision sur la puissance total des centrales X à l'instant t
'''
dict_N = {}
dict_P = {}
for t in range(24):
for X in dict_Thermique:
dict_N[X,t] = model.addVar(lb=0,ub=dict_Thermique[X].N,vtype=gp.GRB.INTEGER,name=f"Nombre de centrale {X} allumées à {t}h")
dict_P[X,t] = model.addVar(name=f"Puissance totale {X} à {t}h")
return dict_N, dict_P
def contraintes_puissance_thermique(model,dict_N,dict_P,dict_Thermique):
'''
Ajoute au modèle les contraintes de puissance que les centrales peuvent atteindre.
Les centrales allumées doivent avoir une puissance entre Pmin et Pmax
'''
for t in range(24):
for X in dict_Thermique:
model.addConstr(dict_P[X,t]<=dict_N[X,t] * dict_Thermique[X].Pmax, name=f"borne sup puissance, {X} à {t}h")
model.addConstr(dict_P[X,t]>=dict_N[X,t] * dict_Thermique[X].Pmin, name=f"borne inf puissance, {X} à {t}h")
def contraintes_equilibre(model,dict_P,dict_Thermique,consommation):
'''
Ajoute au modèle la contraite d'équilibre offre-demande
'''
for t in range(24):
model.addConstr(gp.quicksum([dict_P[X,t] for X in dict_Thermique])==consommation[t])
#####################################################################################
# Partie 2
#####################################################################################
# 2.1 ###############################################################################
class Centrale2:
'''
Class permettant de regrouper les informations sur un type de centrale pour faciliter
l'implementation du code
'''
def __init__(self, name, N, Pmin, Pmax, Cstart, Cbase, Cmwh):
self.name = name
self.N = N
self.Pmin = Pmin
self.Pmax = Pmax
self.Cstart = Cstart
self.Cbase = Cbase
self.Cmwh = Cmwh
# 2.2 ###############################################################################
def variable_decision_thermique_avec_demarrage(model,dict_Thermique):
'''
Fontion générant les variables de décision
Output :
dict_N[X,t] dictionnaire contenant les variables de décision associées au nombre de centrale X en fonctionnement à l'instant t
dict_P[X,t] dictionnaire contenant les variables de décision sur la puissance total des centrales X à l'instant t
dict_Nstart[X,t] dictionnaire contenant les variables de décision sur le nombre de centrales X démarrées à l'instant t
'''
dict_N, dict_P = variable_decision_thermique(model,dict_Thermique)
dict_Nstart = {}
for X in dict_Thermique:
for t in range(24):
dict_Nstart[X,t] = model.addVar(lb=0,ub=dict_Thermique[X].N,vtype=gp.GRB.INTEGER,name=f"Nombre de centrale {X} démarrées à {t}h")
return dict_N, dict_Nstart, dict_P
def contraintes_demarrage(model,dict_N,dict_Nstart,dict_Thermique,cyclique=True):
'''
Ajoute au modèle les contraintes sur le démarrage des centrales
'''
heure = np.arange(24)
for t in range(24):
for X in dict_Thermique:
if cyclique:
# 2.2.2
model.addConstr(dict_Nstart[X,t]<=dict_Thermique[X].N-dict_N[X,heure[t-1]], name = f"Nombre max de centrale {X} démarable à {t}")
# 2.2.1
model.addConstr(dict_N[X,t]<=dict_Nstart[X,t]+dict_N[X,heure[t-1]], name = f"Nombre max de centrale {X} en fonctionnement à {t}")
else:
if t ==0:
# 2.2.2
model.addConstr(dict_Nstart[X,t]<= dict_Thermique[X].N, name = f"Nombre max de centrale {X} démarable à {t}")
# 2.2.1
model.addConstr(dict_N[X,t] <= dict_Nstart[X,t], name = f"Nombre max de centrale {X} en fonctionnement à {t}")
else:
# 2.2.2
model.addConstr(dict_Nstart[X,t]<= dict_Thermique[X].N - dict_N[X,t-1], name = f"Nombre max de centrale {X} démarable à {t}")
# 2.2.1
model.addConstr(dict_N[X,t] <= dict_Nstart[X,t] + dict_N[X,t-1], name = f"Nombre max de centrale {X} en fonctionnement à {t}")
#####################################################################################
# Partie 3
#####################################################################################
def contraintes_reserve_de_puissance(model,dict_N,dict_Thermique,consommation):
'''
Ajoute au modèle la contrainte de réserve de puissance de 15% par rapport à la demande
'''
for t in range(24):
model.addConstr(gp.quicksum([dict_N[X,t]*dict_Thermique[X].Pmax for X in dict_Thermique])>= 1.15 * consommation[t], name = f"Réserve de puissance à {t}")
#####################################################################################
# Partie 5
#####################################################################################
# 5.1 ###############################################################################
class Centrale_hydro:
'''
Class permettant de regrouper les informations sur un type de centrale pour faciliter
l'implementation du code
'''
def __init__(self, name, P, Cstart, Cheure, debit):
self.name = name
self.P = P
self.Cstart = Cstart
self.Cheure = Cheure
self.debit = debit
def variable_decision_hydraulique(model,dict_Hydro):
'''
Génère les variables de décision du problème liées à l'hydraulique
output :
dict_H[Y,t] dictionnaire contenant les VD sur le fonctionnement de la centrale Y à l'instant t
dict_Hstart[Y,t] dictionnaire contenant les VD sur le démarrage des centrales Y à l'instant t
'''
dict_H = {}
dict_Hstart = {}
for Y in dict_Hydro:
for t in range(24):
dict_H[Y,t] = model.addVar(vtype=gp.GRB.BINARY, name = f"Centrale Hydro {Y} fonctionne à l'heure {t}")
dict_Hstart[Y,t] = model.addVar(vtype=gp.GRB.BINARY, name = f"Centrale Hydro {Y} démarre à l'heure {t}")
return dict_H, dict_Hstart
def contraintes_hydraulique(model,dict_Hstart,dict_H,dict_Hydro):
'''
Ajoute les contraintes de démarrage de l'hydraulique (5.1.2)
'''
heure = np.arange(24)
for t in range(24):
for Y in dict_Hydro:
# model.addConstr(dict_Hstart[Y,t]<=dict_H[Y,t], name = f"Le nombre de centrale {Y} en fonctionnement supérieur au nombre démarré à {t}")
model.addConstr(dict_H[Y,t] <= dict_Hstart[Y,t] + dict_H[Y,heure[t-1]], name = f"Contrainte centrale {Y} fonctionnant à {t}")
def contraintes_equilibre_avec_hydro(model,dict_H,dict_P,dict_Thermique,dict_Hydro,consommation):
'''
Ajoute les contraintes d'équilibre offre-demande avec de l'hydraulique
(5.1.2)
'''
for t in range(24):
model.addConstr(gp.quicksum([dict_P[X,t] for X in dict_Thermique])+gp.quicksum(dict_H[Y,t]*dict_Hydro[Y].P for Y in dict_Hydro)==consommation[t], name = f"Equilibre offre-demande à l'instant {t}")
def contraintes_reserve_avec_hydro(model,dict_N,dict_Thermique,dict_Hydro,consommation):
'''
Ajoute les contraintes de réserve de puissance avec de l'hydraulique
(5.1.3)
'''
for t in range(24):
model.addConstr(
gp.quicksum([dict_N[X,t]*dict_Thermique[X].Pmax for X in dict_Thermique]) +
gp.quicksum([dict_Hydro[Y].P for Y in dict_Hydro])>= 1.15 * consommation[t],
name = f"Réserve de puissance à {t}")
# 5.2 ###############################################################################
def contraintes_reservoir(model,dict_S,dict_Hydro,dict_H,debit_S):
'''
Ajoute la contrainte d'équilibre du réservoir sur la journée
'''
model.addConstr(
gp.quicksum([dict_S[t] * debit_S - gp.quicksum([dict_H[Y,t] * dict_Hydro[Y].debit for Y in dict_Hydro]) for t in range(24)])==0,
name="Equilibre niveau réservoir"
)
def contraintes_equilibre_avec_STEP(model,dict_H,dict_P,dict_Thermique,dict_Hydro,dict_S,consommation):
'''
Ajoute la contrainte d'équilibre offre-demande en prenant en compte le pompage
'''
for t in range(24):
model.addConstr(
gp.quicksum([dict_P[X,t] for X in dict_Thermique])+gp.quicksum(dict_H[Y,t]*dict_Hydro[Y].P for Y in dict_Hydro) - dict_S[t] ==consommation[t],
name = f"Equilibre offre-demande à l'instant {t}")
# 5.3 ###############################################################################
class Centrale_hydro2:
'''
Class permettant de regrouper les informations sur un type de centrale pour faciliter
l'implementation du code
'''
def __init__(self, name, P, Cstart, Cheure, Palier, debit):
self.name = name
self.P = P
self.Cstart = Cstart
self.Cheure = Cheure
self.Palier = Palier
self.debit = debit
def palier_max(self):
return max(self.Palier)
def variables_decision_hydraulique_palier(model,dict_Hydro):
'''
Génère les variables de décision du problème liées à l'hydraulique
output :
dict_H[Y,n,t] dictionnaire contenant les VD sur le fonctionnement de la centrale Y au palier n à l'instant t
dict_Hstart[Y,t] dictionnaire contenant les VD sur le démarrage des centrales Y à l'instant t
'''
dict_H = {}
dict_Hstart = {}
for Y in dict_Hydro:
for t in range(24):
dict_Hstart[Y,t] = model.addVar(vtype=gp.GRB.BINARY, name = f"Centrale Hydro {Y} démarre à l'heure {t}")
for n in dict_Hydro[Y].Palier:
dict_H[Y,n,t] = model.addVar(vtype=gp.GRB.BINARY, name = f"Centrale Hydro {Y} fonctionne au palier {n} à l'instant {t}")
return dict_H, dict_Hstart
def contraintes_equilibre_palier(model,dict_P,dict_Thermique,dict_H,dict_Hydro,dict_S,consommation):
'''
Ajoute la contrainte d'équilibre offre-demande avec les paliers de l'hydraulique
(5.3.1)
'''
for t in range(24):
model.addConstr(
gp.quicksum([dict_P[X,t] for X in dict_Thermique])+
gp.quicksum([gp.quicksum([dict_H[Y,n,t]*dict_Hydro[Y].P[n] for n in dict_Hydro[Y].Palier]) for Y in dict_Hydro])
-dict_S[t] == consommation[t],
name = f"Equilibre offre-demande à l'instant {t}")
def contraintes_reserve_palier(model,dict_N,dict_Thermique,dict_Hydro,consommation):
'''
Ajoute la contrainte de réserve de puissance avec paliers hydraulique
(5.3.2)
'''
for t in range(24):
model.addConstr(
gp.quicksum([dict_N[X,t]*dict_Thermique[X].Pmax for X in dict_Thermique]) +
gp.quicksum([dict_Hydro[Y].P[dict_Hydro[Y].palier_max()] for Y in dict_Hydro])>= 1.15 * consommation[t],
name = f"Réserve de puissance à {t}"
)
def contraintes_reservoir_palier(model,dict_H,dict_Hydro,dict_S,debit_S):
'''
Ajoute la contrainte d'équilbre réservoir avec palier
(5.3.3)
'''
model.addConstr(
gp.quicksum([dict_S[t] * debit_S - gp.quicksum([gp.quicksum([dict_H[Y,n,t] * dict_Hydro[Y].debit[n] for n in dict_Hydro[Y].Palier]) for Y in dict_Hydro]) for t in range(24)])==0,
name="Equilibre niveau réservoir"
)
def contraintes_hydraulique_palier(model,dict_H,dict_Hydro,dict_Hstart):
'''
Ajoute les contraintes sur le fonctionnement des paliers hydraulique
'''
heures = np.arange(24)
for t in range(24):
for Y in dict_Hydro:
# 5.3.4
model.addConstr(
gp.quicksum([dict_H[Y,n,t] for n in dict_Hydro[Y].Palier])<=1,
name = f"Un seul palier en fonctionnement {t,Y}"
)
# 5.3.5
model.addConstr(
gp.quicksum([dict_H[Y,n,t] for n in dict_Hydro[Y].Palier]) <= gp.quicksum([dict_H[Y,n,heures[t-1]] for n in dict_Hydro[Y].Palier]) + dict_Hstart[Y,t]
)
# 5.4 ###############################################################################
def contraintes_hydraulique_pompage(model,dict_H,dict_Hydro,dict_N_s,dict_S,M):
'''
Ajoute les contraintes sur l'exclusion de pompage en cas d'hydraulique
'''
for t in range(24):
# 5.4.1
model.addConstr(
gp.quicksum([dict_H[Y,n,t] for Y in dict_Hydro for n in dict_Hydro[Y].Palier])/len(dict_Hydro) <= 1 - dict_N_s[t],
name=f"Contrainte pompage selon fonctionnement hydro à {t}"
)
# 5.4.2
model.addConstr(
dict_S[t] <= M * dict_N_s[t],
name=f"Contrainte fonctionnement pompage à {t}"
)
#####################################################################################
# Partie 6
#####################################################################################
# 6.1 ###############################################################################
def variables_decision_desagregation_thermique(model,dict_Thermique):
''''
Fontion générant les variables de décision
Output :
dict_N[X,k,t] dictionnaire contenant les VD sur la k-ieme centrale X en fonctionnement à l'instant t
dict_P[X,k,t] dictionnaire contenant les VD de la puissance de la k-ieme centrales X à l'instant t
dict_Nstart[X,k,t] dictionnaire contenant les VD sur la k-ieme centrale X démarre à l'instant t
'''
dict_N = {}
dict_P = {}
dict_Nstart = {}
for t in range(24):
for X in dict_Thermique:
for k in range(dict_Thermique[X].N):
dict_N[X,k,t] = model.addVar(vtype=gp.GRB.BINARY,name=f"centrale {X,k} fonctionne à {t}h")
dict_P[X,k,t] = model.addVar(name=f"Puissance {X,k} à {t}h")
dict_Nstart[X,k,t] = model.addVar(vtype=gp.GRB.BINARY,name=f"centrale {X,k} démarre à {t}h")
return dict_N, dict_P, dict_Nstart
def contraintes_thermique_desagregation(model,dict_P,dict_N,dict_Nstart,dict_Thermique):
'''
Ajout des contraintes sur les centrales thermiques désagrégées
'''
heure = np.arange(24)
for t in range(24):
for X in dict_Thermique:
for k in range(dict_Thermique[X].N):
# (6.1.1)
model.addConstr(
dict_P[X,k,t] <= dict_N[X,k,t] * dict_Thermique[X].Pmax, name=f"borne sup puissance {X,k,t}"
)
# (6.1.1)
model.addConstr(
dict_P[X,k,t] >= dict_N[X,k,t] * dict_Thermique[X].Pmin, name=f"borne inf puissance {X,k,t}"
)
# (6.1.2)
model.addConstr(
dict_N[X,k,t] <= dict_N[X,k,heure[t-1]] + dict_Nstart[X,k,t],name=f"Contrainte démarrage {X,k,t}"
)
def contraintes_equilibre_desagregation(model,dict_P,dict_Thermique,dict_H,dict_Hydro,dict_S,consommation):
'''
Ajout des contraintes d'équilibre au modèle lorsque les centrales sont désagrégées
(6.1.3)
'''
for t in range(24):
model.addConstr(
gp.quicksum([dict_P[X,k,t] for X in dict_Thermique for k in range(dict_Thermique[X].N)])+
gp.quicksum([dict_H[Y,n,t] * dict_Hydro[Y].P[n] for Y in dict_Hydro for n in dict_Hydro[Y].Palier])-
dict_S[t] == consommation[t], name=f"équilibre offre-demande à {t}"
)
def contraintes_reserve_desagregation(model,dict_N,dict_Thermique,dict_Hydro,consommation):
'''
Ajout des contraintes de réserve au modèle lorsque les centrales sont désagrégées
(6.1.4)
'''
for t in range(24):
model.addConstr(
gp.quicksum([dict_N[X,k,t]*dict_Thermique[X].Pmax for X in dict_Thermique for k in range(dict_Thermique[X].N)]) +
gp.quicksum([dict_Hydro[Y].P[dict_Hydro[Y].palier_max()] for Y in dict_Hydro])>= 1.15 * consommation[t],
name = f"Réserve de puissance à {t}"
)
#####################################################################################
# Partie 7
#####################################################################################
# 7.1 ###############################################################################
#####################################################################################
# Partie 8
#####################################################################################
# 8.1 ###############################################################################
class Centrale3:
'''
Class permettant de regrouper les informations sur un type de centrale pour faciliter
l'implementation du code
'''
def __init__(self, name, N, Pmin, Pmax, Cstart, Cbase, Cmwh, Rampe_Montante, Rampe_Start, Rampe_Descendante, Rampe_Stop):
self.name = name
self.N = N
self.Pmin = Pmin
self.Pmax = Pmax
self.Cstart = Cstart
self.Cbase = Cbase
self.Cmwh = Cmwh
self.Rampe_Montante = Rampe_Montante
self.Rampe_Descendante = Rampe_Descendante
self.Rampe_Start = Rampe_Start
self.Rampe_Stop = Rampe_Stop
def contraintes_rampes(model,dict_P,dict_N,dict_Nstart,dict_Thermique):
'''
Ajoute les contraintes de rampes au modèle
'''
heure = np.arange(24)
for t in range(24):
for X in dict_Thermique:
for k in range(dict_Thermique[X].N):
# 8.2
model.addConstr(
dict_P[X,k,t]-dict_P[X,k,heure[t-1]]<=dict_Thermique[X].Rampe_Montante + dict_Nstart[X,k,t]*(dict_Thermique[X].Rampe_Start-dict_Thermique[X].Rampe_Montante),
name= f"Contraintes montantes {X,k}, à {t}"
)
#8.3
model.addConstr(
dict_P[X,k,t]-dict_P[X,k,heure[t-1]]>=-dict_Thermique[X].Rampe_Montante + (dict_N[X,k,t]-dict_N[X,k,heure[t-1]])*(dict_Thermique[X].Rampe_Start-dict_Thermique[X].Rampe_Montante),
name= f"Contraintes descendantes {X,k}, à {t}"
)