Contraint Values - Extra functionality.

479 views
Skip to first unread message

Tobie Nortje

unread,
Jun 15, 2020, 10:02:40 AM6/15/20
to pypsa
HI,

We have this piece of code that adds a Reserve Margin Constraint to the model. in this instance the GeneratorSize minus its current operating power should be bigger than 100MW. This works fine and if we add other generators we get the exact behaviour we think it should. Basically whatever is left from a generation capacity is available to the 'reserve' market. (in this toy case)

BUT - and sorry  - this is a repeat question:

We only want a generator (Gen_x) to contribute its excess power to the reserve margin IF it was running at this snapshot.

I have tried to get the value of model.generator_p but it is a constraint. I read the docs and found that wrapping it in value(model.generator_p['gen1', i]) could work but still an error.

So in the below example. If only Gen1 was generating power (p>0) and Gen2 was not(p=0) then only the Gen1 extra capacity is available to the system for 'reserve'

I am sure it is a simple solution but it has been eluding us for a couple of weeks now! 

In other words. How to i get access to the value of model.generator_p["Gen1", i] so that i can do some if-then-else operations with it at every snapshot point. 


def extra_functionality(network, snapshots):
def reserve_market_1(model, i):
return (network.generators.p_nom['Gen1'] - model.generator_p["Gen1", i]) +\
               (network.generators.p_nom['Gen2'] - model.generator_p["Gen2", i]) >= 100

network.model.reserve_market = Constraint(list(snapshots), rule=reserve_market_1)

Fabian Neumann

unread,
Jun 15, 2020, 10:11:52 AM6/15/20
to py...@googlegroups.com
Hi Tobie,

to model if-else relations, you will need the binary status variable of
a committable generator:
https://pypsa.readthedocs.io/en/latest/optimal_power_flow.html#generator-unit-commitment-constraints

You could multiply this status variable with the gap between dispatch
and capacity for each generator.

Beware, this makes your problem nonconvex, and therefore much more
computationally challenging.

You won't be able to retrieve the variable value before the problem is
solved.

Hope this helps,

Fabian


On 15/06/2020 16:02, Tobie Nortje wrote:
> HI,
>
> We have this piece of code that adds a Reserve Margin Constraint to the
> model. in this instance the GeneratorSize minus its current operating
> power should be bigger than 100MW. This works fine and if we add other
> generators we get the exact behaviour we think it should. Basically
> whatever is left from a generation capacity is available to the
> 'reserve' market. (in this toy case)
>
> BUT - and sorry  - this is a repeat question:
>
> *We only want a generator (Gen_x) to contribute its excess power to the
> reserve margin IF it was running at this snapshot.*
>
> I have tried to get the value of model.generator_p but it is a
> constraint. I read the docs and found that wrapping it in
> value(model.generator_p['gen1', i]) could work but still an error.
>
> *So in the below example. If only Gen1 was generating power (p>0) and
> Gen2 was not(p=0) then only the Gen1 extra capacity is available to the
> system for 'reserve'*
>
> I am sure it is a simple solution but it has been eluding us for a
> couple of weeks now!
>
> In other words. How to i get access to the value of
> *model.generator_p["Gen1", i**] */so that i can do some if-then-else
> operations with it at every snapshot point. /
>
>
> def extra_functionality(network, snapshots):
> def reserve_market_1(model, i):
> return (network.generators.p_nom['Gen1'] -model.generator_p["Gen1", i]) +\
>
> (network.generators.p_nom['Gen2'] -model.generator_p["Gen2", i]) >= 100
>
> network.model.reserve_market = Constraint(list(snapshots), rule=reserve_market_1)
>
> --
> You received this message because you are subscribed to the Google
> Groups "pypsa" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to pypsa+un...@googlegroups.com
> <mailto:pypsa+un...@googlegroups.com>.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/pypsa/30872225-562a-4484-b860-97079aa92c5fo%40googlegroups.com
> <https://groups.google.com/d/msgid/pypsa/30872225-562a-4484-b860-97079aa92c5fo%40googlegroups.com?utm_medium=email&utm_source=footer>.

--
Karlsruhe Institute of Technology (KIT)
Institute for Automation and Applied Informatics (IAI)

Fabian NEUMANN (he/him)
PhD Student in Energy System Modelling

Phone: +49 721 608 25707 | Mobile: +49 171 2943831 |
Group: www.iai.kit.edu/english/esm.php | Personal: www.neumann.fyi |
Github: @fneum | Twitter: @fneum_

Learn about nearly-optimal power system models:
https://arxiv.org/abs/1910.01891

KIT Campus North, Building 445, Office 304
Hermann-von-Helmholtz-Platz 1
76344 Eggenstein-Leopoldshafen

KIT – The Research University in the Helmholtz Association

Fabian Neumann

unread,
Jun 15, 2020, 10:45:47 AM6/15/20
to Tobie Nortje, pypsa
Hi Tobie,

I do not think that's a really great idea to "solve a loop".

If the problem is that the status is always one, that is due to a
problem degeneracy, then you could add a small penalty term to the
objective function penalising the status variable.

Best,

Fabian N

On 15/06/2020 16:32, Tobie Nortje wrote:
> HI,
>
> Thanks. Unfortunately when I make it committable it is committable during all hours. I still then do not know if it actually produced anything or not. All we need to figure out is during the solve --- did a generator produce p>0 or not.
>
> I suppose the best would be to look for the appropriate place in the lopf solve loop and add some code there.
>
> Thanks
> Tobie
> To unsubscribe from this group and stop receiving emails from it, send an email to pypsa+un...@googlegroups.com.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/pypsa/8717a2db-9d6c-68dc-e628-9cef4c4d865b%40kit.edu.

Joshua Rhodes

unread,
Mar 19, 2022, 1:09:45 PM3/19/22
to pypsa
Would it be possible to add a more simple reserve margin constraint to the model? 

Something like: sum(all_nameplate_capacity) >= max(demand_t)*(1+RM)

Where max(demand_t) would be the timestamp with the highest summed demand across all the busses and RM is the desired reserve margin, say 15%?

It might also be great if "all_nameplate_capacity" was broken out by carrier so it could have a multiplier or something? I.e. give gas  a multiplier of 1 and maybe 'solar' a multiplier of 0.12 or something? I am just thinking of the way that ERCOT does it. 

Or I guess the sum each generator's nameplate capacity times its "generators-p_max_pu" value for each timestamp could be set at >= max(demand_t)*(1+RM)

Does that seem reasonable?

Thanks 
Joshua Rhodes

Fabian Hofmann

unread,
Mar 20, 2022, 5:28:41 PM3/20/22
to py...@googlegroups.com

Hey Joshua,


see attached a notebook that shows how the reserve margins can be modeled with custom constraints. It should be easy to add weightings to the generators.I plan to add it to the pypsa examples soon.


Best

Fabian Hofmann

To view this discussion on the web, visit https://groups.google.com/d/msgid/pypsa/a98ac006-599d-4063-bfc0-ff108b21b5a5n%40googlegroups.com.
-- 
Fabian Hofmann 

Postdoctoral Researcher
Institute of Energy Technology
Technische Universität Berlin

Group website: https://tub-ensys.github.io/
pypsa-tutorial-3-reserve_margin.ipynb

Joshua Rhodes

unread,
Mar 20, 2022, 8:47:35 PM3/20/22
to pypsa
Thanks! I am getting an error in the notebook: Is


Is there a package that was left off of the imports at the top? 

Thanks
Joshua Rhodes

Fabian Hofmann

unread,
Mar 21, 2022, 4:40:10 AM3/21/22
to py...@googlegroups.com

Hey Joshua,


sorry, I should have run the notebook before sending.

Please add the get_var function in the imports from the linopt module, so you'd have


from pypsa.linopt import define_variables, define_constraints, linexpr, get_var


Then it should run without any problems.

Best,

Fabian

Reply all
Reply to author
Forward
0 new messages