Skip to content

Commit

Permalink
Merge pull request #195 from iiasa/costs/fom-vintage
Browse files Browse the repository at this point in the history
Create option in `tools.costs` to keep fixed O&M costs same across vintages
  • Loading branch information
khaeru authored Jul 19, 2024
2 parents fabd07e + 3470a30 commit 4d95695
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 63 deletions.
1 change: 1 addition & 0 deletions doc/api/tools-costs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Those settings include the following; click each for the full description, allow
:attr:`~.Config.scenario_version`,
:attr:`~.Config.base_year`,
:attr:`~.Config.convergence_year`,
:attr:`~.Config.use_vintages`,
:attr:`~.Config.fom_rate`, and
:attr:`~.Config.format`.

Expand Down
4 changes: 2 additions & 2 deletions doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ Next release
- Update :doc:`/material/index` (:pull:`201`).
- Add :doc:`/project/edits` project code and documentation (:pull:`204`).
- Reduce log verbosity of :func:`.apply_spec` (:pull:`202`).
- Made fixes and updates to :doc:`/api/tools-costs` (:pull:`186`, :pull:`187`, :pull:`190`, :pull:`195`).

Changes to :doc:`/api/tools-costs`
----------------------------------
- Fix jumps in cost projections for technologies with first technology year that's after than the first model year (:pull:`186`).
- Change the use of base_year to mean the year to start modeling cost changes (:pull:`186`).
- Update cost assumptions for certain CCS technologies (:pull:`186`).
- Change the default fixed O&M reduction rate to 0 (:pull:`186`).
- Modify to use 2023 release of IEA WEO data and to use 2022 historic data for the base year (:pull:`187`).
- Change the default final year to 2110 (:pull:`190`).
- Add :attr:`~.costs.Config.use_vintages` to control whether vintages are used in computing fixed O&M costs (:pull:`195`).

v2024.4.22
==========
Expand Down
8 changes: 8 additions & 0 deletions message_ix_models/tools/costs/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ class Config:
#: Model variant for which to project costs.
module: Literal["energy", "materials"] = "energy"

#: Use vintages.
#:
#: If True, for each vintage, the fixed O&M costs will be calculated as a
#: ratio of the investment costs, that decreases by the rate of :attr:`fom_rate`.
#: If False, the fix_cost is the ratio of the investment cost for each year_act.
#: In this case, the fix_cost is the same for all vintages of the same year_act.
use_vintages: bool = False

#: .. todo:: Document the meaning of this setting.
pre_last_year_rate: float = 0.01

Expand Down
1 change: 1 addition & 0 deletions message_ix_models/tools/costs/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# - format="message",
# - method="gdp",
# - module="energy",
# - use_vintages=False,
# - node="R12",
# - ref_region —automatically determined from node
# - scenario="all",
Expand Down
100 changes: 39 additions & 61 deletions message_ix_models/tools/costs/projections.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,15 @@ def create_message_outputs(

y_base = config.base_year

dims = [
"scenario_version",
"scenario",
"message_technology",
"first_technology_year",
"region",
"year",
]

df_prod = pd.DataFrame(
product(
df_projections.scenario_version.unique(),
Expand All @@ -371,14 +380,7 @@ def create_message_outputs(
df_projections.region.unique(),
config.seq_years,
),
columns=[
"scenario_version",
"scenario",
"message_technology",
"first_technology_year",
"region",
"year",
],
columns=dims,
)

val_2020 = (
Expand All @@ -395,26 +397,9 @@ def create_message_outputs(

df_merge = (
(
df_prod.merge(
val_2020,
on=["scenario_version", "scenario", "message_technology", "region"],
)
.merge(
val_2100,
on=["scenario_version", "scenario", "message_technology", "region"],
)
.merge(
df_projections,
on=[
"scenario_version",
"scenario",
"message_technology",
"first_technology_year",
"region",
"year",
],
how="left",
)
df_prod.merge(val_2020, on=dims[:-1])
.merge(val_2100, on=dims[:-1])
.merge(df_projections, on=dims, how="left")
)
.assign(
inv_cost=lambda x: np.where(x.year <= y_base, x.inv_cost_2020, x.inv_cost),
Expand Down Expand Up @@ -477,45 +462,38 @@ def create_message_outputs(
)

dtypes.update(year_act=int)

to_merge = pd.DataFrame(
{"year_act" if config.use_vintages else "year_vtg": config.seq_years}
).assign(key=1)

def _compute_value(df: pd.DataFrame) -> pd.Series:
if not config.use_vintages:
return df.fix_cost

rate = 1.0 + config.fom_rate

return np.where(
df.year_vtg <= y_base,
np.where(
df.year_act <= y_base,
df.fix_cost,
# NB if fom_rate was 0, the latter terms collapse to 1.0 ** (…) = 1.0
df.fix_cost * rate ** (df.year_act - y_base),
),
df.fix_cost * rate ** (df.year_act - df.year_vtg),
)

fom = (
df_merge.copy()
.drop(columns=["inv_cost"])
.rename(columns={"year_vtg": "year_vtg" if config.use_vintages else "year_act"})
.assign(key=1)
.merge(
pd.DataFrame(data={"year_act": config.seq_years}).assign(key=1),
on="key",
)
.merge(to_merge, on="key")
.drop(columns=["key"])
.query("year_act >= year_vtg")
.assign(
val=lambda x: np.where(
x.year_vtg <= y_base,
np.where(
x.year_act <= y_base,
x.fix_cost,
np.where(
config.fom_rate == 0,
x.fix_cost,
x.fix_cost
* (1 + float(config.fom_rate)) ** (x.year_act - y_base),
),
),
np.where(
config.fom_rate == 0,
x.fix_cost,
x.fix_cost
* (1 + float(config.fom_rate)) ** (x.year_act - x.year_vtg),
),
)
)
.assign(unit="USD/kWa")
.rename(
columns={
"val": "value",
"message_technology": "technology",
"region": "node_loc",
}
)
.assign(value=_compute_value, unit="USD/kWa")
.rename(columns={"message_technology": "technology", "region": "node_loc"})
.reindex(
[
"scenario_version",
Expand Down

0 comments on commit 4d95695

Please sign in to comment.