@@ -106,56 +106,150 @@ def add_power_limits(n, investment_year, limits_power_max):
106106 if investment_year not in limits_power_max [ct ].keys ():
107107 continue
108108
109- limit = 1e3 * limits_power_max [ct ][investment_year ] / 10
109+ lim = 1e3 * limits_power_max [ct ][investment_year ] # in MW
110110
111111 logger .info (
112- f"Adding constraint on electricity import/export from/to { ct } to be < { limit } MW"
112+ f"Adding constraint on electricity import/export from/to { ct } to be < { lim } MW"
113113 )
114- incoming_line = n .lines .index [
114+ # identify interconnectors
115+
116+ incoming_lines = n .lines [
115117 (n .lines .carrier == "AC" )
116118 & (n .lines .bus0 .str [:2 ] != ct )
117119 & (n .lines .bus1 .str [:2 ] == ct )
120+ & n .lines .active
118121 ]
119- outgoing_line = n .lines . index [
122+ outgoing_lines = n .lines [
120123 (n .lines .carrier == "AC" )
121124 & (n .lines .bus0 .str [:2 ] == ct )
122125 & (n .lines .bus1 .str [:2 ] != ct )
126+ & n .lines .active
123127 ]
124-
125- incoming_link = n .links .index [
128+ incoming_links = n .links [
126129 (n .links .carrier == "DC" )
127130 & (n .links .bus0 .str [:2 ] != ct )
128131 & (n .links .bus1 .str [:2 ] == ct )
132+ & n .links .active
129133 ]
130- outgoing_link = n .links . index [
134+ outgoing_links = n .links [
131135 (n .links .carrier == "DC" )
132136 & (n .links .bus0 .str [:2 ] == ct )
133137 & (n .links .bus1 .str [:2 ] != ct )
138+ & n .links .active
134139 ]
135140
136- # iterate over snapshots - otherwise exporting of postnetwork fails since
137- # the constraints are time dependent
138141 for t in n .snapshots :
139- incoming_line_p = n .model ["Line-s" ].loc [t , incoming_line ]
140- outgoing_line_p = n .model ["Line-s" ].loc [t , outgoing_line ]
141- incoming_link_p = n .model ["Link-p" ].loc [t , incoming_link ]
142- outgoing_link_p = n .model ["Link-p" ].loc [t , outgoing_link ]
143-
144- lhs = (
145- incoming_link_p .sum ()
146- - outgoing_link_p .sum ()
147- + incoming_line_p .sum ()
148- - outgoing_line_p .sum ()
149- ) / 10
150- # divide by 10 to avoid numerical issues
142+ # For incoming flows s > 0 means imports, s < 0 exports
143+ # For outgoing flows s > 0 means exports, s < 0 imports
144+ # to get the positive and negative parts separately, we use auxiliary variables
145+ incoming_lines_var = n .model ["Line-s" ].loc [t , incoming_lines .index ]
146+ n .model .add_variables (
147+ coords = [incoming_lines .index ],
148+ name = f"Line-s-incoming-{ ct } -aux-pos-{ t } " ,
149+ lower = 0 ,
150+ upper = incoming_lines .s_nom_max ,
151+ )
152+ n .model .add_variables (
153+ coords = [incoming_lines .index ],
154+ name = f"Line-s-incoming-{ ct } -aux-neg-{ t } " ,
155+ lower = - incoming_lines .s_nom_max ,
156+ upper = 0 ,
157+ )
158+ n .model .add_constraints (
159+ n .model [f"Line-s-incoming-{ ct } -aux-pos-{ t } " ] >= incoming_lines_var ,
160+ name = f"Line-s-incoming-{ ct } -aux-pos-constr-{ t } " ,
161+ )
162+ n .model .add_constraints (
163+ n .model [f"Line-s-incoming-{ ct } -aux-neg-{ t } " ] <= incoming_lines_var ,
164+ name = f"Line-s-incoming-{ ct } -aux-neg-constr-{ t } " ,
165+ )
151166
152- cname_upper = f"Power-import-limit-{ ct } -{ t } "
153- cname_lower = f"Power-export-limit-{ ct } -{ t } "
167+ outgoing_lines_var = n .model ["Line-s" ].loc [t , outgoing_lines .index ]
168+ n .model .add_variables (
169+ coords = [outgoing_lines .index ],
170+ name = f"Line-s-outgoing-{ ct } -aux-pos-{ t } " ,
171+ lower = 0 ,
172+ upper = outgoing_lines .s_nom_max ,
173+ )
174+ n .model .add_variables (
175+ coords = [outgoing_lines .index ],
176+ name = f"Line-s-outgoing-{ ct } -aux-neg-{ t } " ,
177+ lower = - outgoing_lines .s_nom_max ,
178+ upper = 0 ,
179+ )
180+ n .model .add_constraints (
181+ n .model [f"Line-s-outgoing-{ ct } -aux-pos-{ t } " ] >= outgoing_lines_var ,
182+ name = f"Line-s-outgoing-{ ct } -aux-pos-constr-{ t } " ,
183+ )
184+ n .model .add_constraints (
185+ n .model [f"Line-s-outgoing-{ ct } -aux-neg-{ t } " ] <= outgoing_lines_var ,
186+ name = f"Line-s-outgoing-{ ct } -aux-neg-constr-{ t } " ,
187+ )
154188
155- n .model .add_constraints (lhs <= limit , name = cname_upper )
156- n .model .add_constraints (lhs >= - limit , name = cname_lower )
189+ incoming_links_var = n .model ["Link-p" ].loc [t , incoming_links .index ]
190+ n .model .add_variables (
191+ coords = [incoming_links .index ],
192+ name = f"Link-p-incoming-{ ct } -aux-pos-{ t } " ,
193+ lower = 0 ,
194+ upper = incoming_links .p_nom_max ,
195+ )
196+ n .model .add_variables (
197+ coords = [incoming_links .index ],
198+ name = f"Link-p-incoming-{ ct } -aux-neg-{ t } " ,
199+ lower = - incoming_links .p_nom_max ,
200+ upper = 0 ,
201+ )
202+ n .model .add_constraints (
203+ n .model [f"Link-p-incoming-{ ct } -aux-pos-{ t } " ] >= incoming_links_var ,
204+ name = f"Link-p-incoming-{ ct } -aux-pos-constr-{ t } " ,
205+ )
206+ n .model .add_constraints (
207+ n .model [f"Link-p-incoming-{ ct } -aux-neg-{ t } " ] <= incoming_links_var ,
208+ name = f"Link-p-incoming-{ ct } -aux-neg-constr-{ t } " ,
209+ )
157210
158- # not adding to network as the shadow prices are not needed
211+ outgoing_links_var = n .model ["Link-p" ].loc [t , outgoing_links .index ]
212+ n .model .add_variables (
213+ coords = [outgoing_links .index ],
214+ name = f"Link-p-outgoing-{ ct } -aux-pos-{ t } " ,
215+ lower = 0 ,
216+ upper = outgoing_links .p_nom_max ,
217+ )
218+ n .model .add_variables (
219+ coords = [outgoing_links .index ],
220+ name = f"Link-p-outgoing-{ ct } -aux-neg-{ t } " ,
221+ lower = - outgoing_links .p_nom_max ,
222+ upper = 0 ,
223+ )
224+ n .model .add_constraints (
225+ n .model [f"Link-p-outgoing-{ ct } -aux-pos-{ t } " ] >= outgoing_links_var ,
226+ name = f"Link-p-outgoing-{ ct } -aux-pos-constr-{ t } " ,
227+ )
228+ n .model .add_constraints (
229+ n .model [f"Link-p-outgoing-{ ct } -aux-neg-{ t } " ] <= outgoing_links_var ,
230+ name = f"Link-p-outgoing-{ ct } -aux-neg-constr-{ t } " ,
231+ )
232+ # To constraint the absolute values of imports and exports, we have to sum the
233+ # corresponding positive and negative flows separately, using auxiliary variables
234+ import_lhs = (
235+ n .model [f"Link-p-incoming-{ ct } -aux-pos-{ t } " ].sum ()
236+ + n .model [f"Line-s-incoming-{ ct } -aux-pos-{ t } " ].sum ()
237+ - n .model [f"Link-p-outgoing-{ ct } -aux-neg-{ t } " ].sum ()
238+ - n .model [f"Line-s-outgoing-{ ct } -aux-neg-{ t } " ].sum ()
239+ ) / 10 # divide by 10 to improve numerical stability
240+ export_lhs = (
241+ n .model [f"Link-p-outgoing-{ ct } -aux-pos-{ t } " ].sum ()
242+ + n .model [f"Line-s-outgoing-{ ct } -aux-pos-{ t } " ].sum ()
243+ - n .model [f"Link-p-incoming-{ ct } -aux-neg-{ t } " ].sum ()
244+ - n .model [f"Line-s-incoming-{ ct } -aux-neg-{ t } " ].sum ()
245+ ) / 10
246+
247+ n .model .add_constraints (
248+ import_lhs <= lim / 10 , name = f"Power-import-limit-{ ct } -{ t } "
249+ )
250+ n .model .add_constraints (
251+ export_lhs <= lim / 10 , name = f"Power-export-limit-{ ct } -{ t } "
252+ )
159253
160254
161255def h2_import_limits (n , investment_year , limits_volume_max ):
0 commit comments