Fix extendable thermal handling in the UCBlock expansion path - #56
Merged
AlessandroPampado99 merged 2 commits intoAug 3, 2026
Merged
Conversation
UCBlock cannot expand a thermal unit: its commitment u_t is binary and a continuous design would multiply it in the max-power constraint. Such a unit was instead normalized to a 1 MW module with no design variable, inflating the objective (+426% on a pypsa-eur instance). It is now fixed at its nominal capacity, warned about, and its (constant) capex added back to the reported objective to match PyPSA. Also normalize StartUpCost and ConstTerm per module when p_nom_extendable, like the other scaled fields, so an extendable thermal no longer double-counts them under Scale.
| ) | ||
|
|
||
|
|
||
| # UCBlock cannot expand a thermal unit: its commitment u_t is a |
Collaborator
There was a problem hiding this comment.
Non trovo molto utile tutta questa cosa... Mi sembra come mettere una pezza. Potrebbe essere più utile rompere il loop e dare errore in caso, che dici? Perché tanto non avremmo comunque una corrispondenza in fo e uno user generico non capirebbe
Collaborator
There was a problem hiding this comment.
Aggiungere poi il costo per far tornare i conti in quel modo credo che prima o poi farà rompere qualcosa. Le correzioni in transformation_config vanno bene invece
Replace the fix-at-nominal-capacity fallback (warning + capex constant added back to the reported objective) with a ValueError: the converter no longer silently alters the model, it asks to fix the unit or use the InvestmentBlock path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On a pypsa-eur instance with extendable CCGTs, converting with
capacity_expansion_ucblock=Truegave an objective +426% above the PyPSAreference, while the InvestmentBlock path was within 0.06%.
The UCBlock path normalizes an extendable unit's
MaxPowerto a per-modulevalue (a design variable is meant to carry the MW), but the design fields
(
InvestmentCost/MaxCapacityDesign) are only injected for intermittentand battery units. Extendable thermals were left normalized with no design
variable, i.e. modeled as 1 MW plants. UCBlock cannot represent continuous
thermal expansion anyway: the commitment
u_tis binary and a continuousdesign would multiply it in the max-power constraint (
p_t ≤ κ·MaxPower·u_t),making the model bilinear.
A second, latent issue:
StartUpCostandConstTermwere passed through asabsolute per-generator values while every other scaled field is normalized
per module when
p_nom_extendable. UnderScale/InvestmentBlock scaling theywere multiplied by the capacity again (double-counting), affecting both paths.
Fix
ValueError: theconverter cannot represent it and does not silently alter the model. The
message asks to either set
p_nom_extendable=Falseon the unit or use theInvestmentBlock path (
capacity_expansion_ucblock=False).StartUpCost/ConstTerm→ normalized per module whenp_nom_extendable, like the other scaled fields.Validation
error above instead of returning a +426% objective.
test_ucblock15/15 pass (networks without extendable thermals areunaffected).
Not covered
QuadTerm(quadratic marginal cost) is structurally incompatible with thefleet/
Scalesemantics for thermals (S·q·p² ≠ q·(S·p)²) and cannot be fixedby normalization; it is 0 in the tested instances. A follow-up could warn when
a nonzero quadratic cost meets an extendable thermal.