Fix: ramp_limit_start_up attribute is ignored in binary UC#1540
Fix: ramp_limit_start_up attribute is ignored in binary UC#1540
Conversation
|
The implementation This is not unambiguous, so @FabianHofmann check carefully if it follows your understanding. |
FabianHofmann
left a comment
There was a problem hiding this comment.
Thanks @Irieo. The fix is valuable. I have one implementation comment: the constraints are now very scattered.
This pr fixes the first snapshot for the non-rolling horizon case by deriving the initial status from up_time_before in an extra constraint. In the rolling horizon case this is already happening, there is a initial start status.
deal or no deal? : I would like to proposal how to integrate it into the rolling horizon constraints to have things more side by side. would you mind if I make a pr against yours? or create a separate one, to the see the effective diff? and if I don't do it this week, we pull this pr in
pypsa/optimization/constraints.py
Outdated
| if not c.committables.empty: | ||
| ramp_limit_start_up = c.da.ramp_limit_start_up | ||
| ramp_limit_shut_down = c.da.ramp_limit_shut_down |
There was a problem hiding this comment.
we should select by com_i here too, right? otherwise all_null and ramp_limit_{start_up/shut_down} have different shapes, leading to broadcast nans
There was a problem hiding this comment.
You're right this is logically imprecise (should lock only relevant subset of components). I think combination of things that NaN is now a default (so applies for all non-committable) and that later all_null includes .all() that reduces the array to a single boolean made the overall logic to run through w/o errors
| up_time_before = c.da.up_time_before.sel(name=com_i) | ||
| starts_from_on = up_time_before > 0 | ||
| if starts_from_on.any(): | ||
| # For first snapshot: p(0) >= (p_nom - ramp_limit_shut_down * p_nom) |
There was a problem hiding this comment.
this assumes the component was dispatched at full capacity in the pre-optimization snapshot right? I am not sure if that is a good assumption. it is a bit arbitrary (the model forces you to be). I could imagine it is a likewise assumption that the dispatch was at p_min_pu and therefore it is feasible to shut down at the very first snapshot. So I am not sure if we should add this in the first place.
There was a problem hiding this comment.
Exactly. This indeed assumes a full capacity in the pre-optimization. I saw arguments for both p_nom and p_min_pu. I think the latter is less intuitive assumption, although it allows switching off units in the first snapshot for any value exceeding p_min_pu (so any reasonable value)
| ramp_limit_down,static or series,per unit,NaN,"Maximum active power decrease from one snapshot to the next, per unit of the nominal power. Ignored if NaN. Does not consider snapshot weightings.",Input (optional) | ||
| ramp_limit_start_up,float,per unit,1,"Maximum active power increase at start up, per unit of the nominal power. Only used if `committable=True`.",Input (optional) | ||
| ramp_limit_shut_down,float,per unit,1,"Maximum active power decrease at shut down, per unit of the nominal power. Only used if `committable=True`.",Input (optional) | ||
| ramp_limit_start_up,float,per unit,NaN,"Maximum active power increase at start up, per unit of the nominal power. Only used if `committable=True`. Ignored if NaN.",Input (optional) |
There was a problem hiding this comment.
this is a breaking change (not in pypsa functions, but possibly downstream). let's write that out in the release notes
There was a problem hiding this comment.
Right, it is mentioned in the release notes (I happen to push that simultaneously with the comment)
upd: I mean release notes mention change in default for the ramp_limit_start_up/down, w/o "breaking"
There was a problem hiding this comment.
This is a breaking change (not in pypsa functions, but possibly downstream). let's write that out in the release notes
@FabianHofmann
Is it though? Result will be the same, just some none constraining constraints are not going to be written anymore. Which is only a problem if you do custom stuff on top of them? At least that is my understanding and then I am also fine with the rename. Specially since this is first of all fixing a quite big bug. Otherwise my moral compass would probably go on
Full 🟢 for making it more compact. So Deal (w/ either of the two ways you suggest) |
Co-authored-by: Fabian Hofmann <[email protected]>
Closes # #592 (considering the implications of the bug, this should have rather been high priority)
Problem
The
ramp_limit_start_upandramp_limit_shut_downattributes were completely ignored in binary unit commitment, even when explicitly set.This happened because:
a) Constraint creation check only evaluated
ramp_limit_up/ramp_limit_down, ignoring values of start_up/shut_downb) The ramping constraint
p[t] - p[t-1] <= ramp_up * p_nom * status[t-1] + ramp_start_up * p_nom * (status[t] - status[t-1])was not applied for the first snapshot due to an overlap with the rolling horizon logic.For more detail on binary UC formulation see these docs
Fix
ramp_limit_upandramp_limit_start_uphave defaultsNaNand1accordingly adds a touch of madness here. In this implementation, both defaults are aligned to NaN. Leave it to @lkstrp's moral compass.ramp_limit_start_up/down orramp_limit_up/down are explicitly set.Other
Reproduce
NB for testing
ramp_limit_shut_downfunctionality that prevents immediate shutdown when the unit starts ON (eventually forcing production even with zero demand) you might want to add slack variable to trace correct behavior in otherwise infeasible problem:Checklist
docs.docs/release-notes.mdof the upcoming release is included.