Closed sstroemer closed 1 month ago
Thanks @sstroemer. The bug looks to have been introduced in v0.7. It is as you suggest in v0.6.10.
I don't like vom
/fom
. Had I not seen them in context in this issue, I wouldn't have known what they are. cost_operation_variable
, cost_operation_fixed
, cost_investment_total
look to me like an reasonable compromise.
It's about more/less expressions, but also the value to the user of those expressions. The current split of investment costs is really just to stop cost_investment
being excessively bloated with lots of conditionals. I like your "alternative way" - it's cleaner. Question is, would users prefer to be able to inspect their investment costs before or after annualisation? I don't have a definite answer to this.
I don't like
vom
/fom
.
Sorry, that wasn't meant as a "hard suggestion" to adopt these namings, just the assumption that it might be reasonable since stuff like om_annual
is already in use, and users may be used to those from stuff like the ens.dk techn. database. I like the explicit ones like cost_operation_fixed
much more!
It's about more/less expressions, but also the value to the user of those expressions.
Question is, would users prefer to be able to inspect their investment costs before or after annualisation?
I assume both are of interest: Before gives me the total costs that my system spends, after gives me access to calculate proper annual shares of different cost types.
However, the only reason I brought up the more expressions topic is that implementing these as auxiliary variables with equality constraints may considerably hurt some ways to solve the models. I'm not entirely sure what the reason behind that is (compared to "expressions", that Pyomo and most others support out of the box, which are basically for free).
I like the explicit ones like cost_operation_fixed much more!
Great. Let's go with that, then.
I assume both are of interest
Both might be of interest, but it does increase model size in memory (expression objects just take up memory) and increases build time. It probably isn't a considerable increase as investment costs don't have a timeseries dimension, but something we should explore.
I wouldn't have them as auxiliary variables, for sure (that's why all our costs are "expressions", leveraging what Pyomo etc. support out-of-the-box).
I wouldn't have them as auxiliary variables, for sure (that's why all our costs are "expressions", leveraging what Pyomo etc. support out-of-the-box).
My bad, I did not check it for v0.7.0
, that comment was based on the previous version (where I struggled a lot with stuff like cost(monetary__region2__battery_)
). Please ignore that comment!
Both might be of interest, but it does increase model size in memory (expression objects just take up memory) and increases build time. It probably isn't a considerable increase as investment costs don't have a timeseries dimension, but something we should explore.
Just my personal opinion: As you indicated, due to these not being temporal, the memory and time-to-build cost is almost none compared to the rest of the model. Especially since they are not even "large expressions" (as in, they group investment variables, leading to a low amount of variable-coefficient pairs in the underlying data structure). But again, this is just quality-of-life and nothing important.
What happened?
Description
Currently, the internal annualisation of costs is done in
cost_investment
. The way it's done may be different to what some users may expect, based on the naming conventions, and may lead to confusion or misinterpretation of input data (and therefore wrong results).The main motivation is the following observation of the calculation of
cost_investment
, which roughly looks like:$$cost\textunderscore investment = \alpha \cdot (\delta \cdot total\textunderscore capex \cdot (1 + FOM{\%}) + FOM{absolute})$$
Here $\delta$ is some sort of depreciation/annualisation factor ($\alpha$ just allows to generalize that for periods different to a single year) which weights the total capital expenditure (correct), but also the parts of the fixed operation and maintenance costs ($FOM$). The latter may not necessarily be expected by users, because these are often given as either
EUR/MW/a
(represented bycost_om_annual
, here $FOM_{absolute}$, correct) or%/a
(represented bycost_om_annual_investment_fraction
, here $FOM_{\%}$ - potentially misleading).For both cases, it is a recurring fixed annual payment, which means it's already "per year" and does not have to be annualised. In the end, this leads to considerably reduced costs if using the "FOM are 2-5% depending on technology" approach (e.g., directly using Danish Energy Agency data would be fine, using the processed PyPSA technology-data would lead to wrong results, since they convert everything to %).
If that is actually a problem (and not intended), the easiest fix could be reworking to:
$$cost\textunderscore investment = \alpha \cdot ((\delta + FOM{\%}) \cdot total\textunderscore capex + FOM{absolute})$$
Everything below just contains additional thoughts, that could be relevant when doing the (breaking) change.
Thoughts
I'd like to point out / propose the following changes:
Renaming
cost_var
could be renamed tocost_operation_var
(or something similar) - these are commonly referred to as VOM, variable operation and maintenance costs, which could be reflected by usingcost_vom
.cost_operation_fix
for fixed costs of operation. Similar to the above, these are commonly referred to as FOM, fixed operation and maintenance costs, which could be reflected by usingcost_fom
.cost_investment
tocost_investment_total
, to better communicate that this is a (total) sum expression.cost_investment_total_annualised
, indicating that this accounts for annualisation.Changing calculations
Initial suggestion
cost_vom
is given by the calculation ofcost_var
as it is.cost_investment_total
is given as "the inner sum of investment costs" of the currentcost_investment
, roughlysum(cost_investment_flow_cap, over=carriers) + cost_investment_storage_cap + cost_investment_source_cap + cost_investment_area_use + cost_investment_purchase
.cost_fom = annualisation_weight * (sum(cost_om_annual * flow_cap, over=carriers) + cost_investment_total * cost_om_annual_investment_fraction)
.cost_investment_total_annualised = annualisation_weight * depreciation_rate * cost_investment_total
.cost = cost_vom + cost_fom + cost_investment_total_annualised
.Drawbacks:
annualisation_weight
is now needed in two separate locations.An alternative way
This looks like a bigger change, which is why I did not want to propose this as the main way to go, but it may be worth considering (steps 1 and 2 are identical):
cost_vom
is given by the calculation ofcost_var
as it is.cost_investment_total
is given as "the inner sum of investment costs" of the currentcost_investment
, roughlysum(cost_investment_flow_cap, over=carriers) + cost_investment_storage_cap + cost_investment_source_cap + cost_investment_area_use + cost_investment_purchase
.cost_fom = sum(cost_om_annual * flow_cap, over=carriers) + cost_investment_total * cost_om_annual_investment_fraction
.cost = cost_vom + annualisation_weight * (depreciation_rate * cost_investment_total + cost_fom)
.This introduces less global expressions, and
annualisation_weight
is only needed in one location.Which operating systems have you used?
Version
latest (4fc6b84fe3f17d6533765c5543192e773deb05a9)
Relevant log output
No response