Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality in tools.costs to specify cost reduction scenarios and values #255

Merged
merged 15 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Next release
- Expand :doc:`repro` with sections on :ref:`repro-doc` and :ref:`versioning`, including :ref:`a list of external model names and ‘versions’ <model-names>` like “MESSAGEix-GLOBIOM 2.0” (:issue:`224`, :pull:`226`).
- Update :doc:`/transport/index` (:pull:`213`).
- Add "LED", "SSP4", and "SSP5" as values for the :program:`--ssp=…` option in :func:`.common_params` (:pull:`233`).
- Fix and update :doc:`/api/tools-costs` (:pull:`219`, :pull:`206`, :pull:`221`, :pull:`227`, :pull:`222`)
- Fix and update :doc:`/api/tools-costs` (:pull:`219`, :pull:`206`, :pull:`221`, :pull:`227`, :pull:`222`, :pull:`255`)

- Fix naming of GDP and population columns in SSP data aggregation (:pull:`219`).
- Edit inputs for storage, CSP, hydrogen, and industry technologies (:pull:`206`).
Expand All @@ -24,6 +24,7 @@ Next release
- Reconfigure use and implementation of technology variants/modules to be more agnostic (:pull:`221`).
- Change cost decay to reach reduction percentage specified on the year 2100 (:pull:`227`).
- Add `cooling` technology variant/module (:pull:`222`).
- Add functionality to specify cost reduction values and cost reduction scenarios in a module (:pull:`255`).
- Improve and extend :doc:`/material/index` (:pull:`218`, :pull:`253`).

- Release of MESSAGEix-Materials 1.1.0 (:doc:`/material/v1.1.0`).
Expand Down
4 changes: 4 additions & 0 deletions message_ix_models/data/costs/materials/cost_reduction.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Cost reduction in 2100,,,,,,
# ,,,,,,
# Units: % ,,,,,,
message_technology,technology_type,very_low,low,medium,high,very_high
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
message_technology,SSP1,SSP2,SSP3,SSP4,SSP5,LED
69 changes: 60 additions & 9 deletions message_ix_models/tests/tools/costs/test_decay.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat strange to remove tests, but we seem to cover all lines touched by the PR even without it. Is there truly no need for this unit test any longer?
Even if this function is only ever called by another function, a unit test runs faster than the whole exterior function and grants confidence when debugging the code that this particular part still works or pinpoints the failure.
If it's not too much work, I think I'd like to see the test adjusted rather than removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the test for get_cost_reduction_data() because the function was completely removed/replaced, but I now added new tests for the two new functions: _get_module_scenarios_reduction() and _get_module_cost_reduction()

Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from typing import Literal

import pandas as pd
import pytest

from message_ix_models.tools.costs import Config
from message_ix_models.tools.costs.decay import (
get_cost_reduction_data,
_get_module_cost_reduction,
_get_module_scenarios_reduction,
get_technology_reduction_scenarios_data,
project_ref_region_inv_costs_using_reduction_rates,
)
from message_ix_models.tools.costs.regional_differentiation import (
apply_regional_differentiation,
get_raw_technology_mapping,
subset_module_map,
)


Expand All @@ -21,20 +25,65 @@
("cooling", {"coal_ppl__cl_fresh", "gas_cc__air", "nuc_lc__ot_fresh"}),
),
)
def test_get_cost_reduction_data(module: str, t_exp) -> None:
# The function runs without error
result = get_cost_reduction_data(module)
def test_get_module_scenarios_reduction(
module: Literal["energy", "materials", "cooling"], t_exp: set[str]
) -> None:
tech_map = energy_map = get_raw_technology_mapping("energy")

# if module is not energy, run subset_module_map
if module != "energy":
module_map = get_raw_technology_mapping(module)
module_sub = subset_module_map(module_map)

# Remove energy technologies that exist in module mapping
energy_map = energy_map.query(
"message_technology not in @module_sub.message_technology"
)

tech_map = pd.concat([energy_map, module_sub], ignore_index=True)

result = _get_module_scenarios_reduction(module, energy_map, tech_map)

# Expected MESSAGEix-GLOBIOM technologies are present in the data
assert t_exp <= set(result.message_technology.unique())

# Values of the "cost reduction" columns are between 0 and 1
stats = result.cost_reduction.describe()
assert 0 <= stats["min"] and stats["max"] <= 1

@pytest.mark.parametrize(
"module, t_exp",
(
("energy", {"coal_ppl", "gas_ppl", "gas_cc", "solar_res1"}),
("materials", {"biomass_NH3", "MTO_petro", "furnace_foil_steel"}),
("cooling", {"coal_ppl__cl_fresh", "gas_cc__air", "nuc_lc__ot_fresh"}),
),
)
def test_get_module_cost_reduction(
module: Literal["energy", "materials", "cooling"], t_exp: set[str]
) -> None:
tech_map = energy_map = get_raw_technology_mapping("energy")

# if module is not energy, run subset_module_map
if module != "energy":
module_map = get_raw_technology_mapping(module)
module_sub = subset_module_map(module_map)

# Remove energy technologies that exist in module mapping
energy_map = energy_map.query(
"message_technology not in @module_sub.message_technology"
)

tech_map = pd.concat([energy_map, module_sub], ignore_index=True)

# The function runs without error
result = _get_module_cost_reduction(module, energy_map, tech_map)

# Expected MESSAGEix-GLOBIOM technologies are present in the data
assert t_exp <= set(result.message_technology.unique())


@pytest.mark.parametrize("module", ("energy", "materials", "cooling"))
def test_get_technology_reduction_scenarios_data(module: str) -> None:
def test_get_technology_reduction_scenarios_data(
module: Literal["energy", "materials", "cooling"],
) -> None:
config = Config()
# The function runs without error
result = get_technology_reduction_scenarios_data(config.y0, module=module)
Expand Down Expand Up @@ -62,7 +111,9 @@ def test_get_technology_reduction_scenarios_data(module: str) -> None:
),
)
def test_project_ref_region_inv_costs_using_reduction_rates(
module: Literal["energy", "materials", "cooling"], t_exp, t_excluded
module: Literal["energy", "materials", "cooling"],
t_exp: set[str],
t_excluded: set[str],
) -> None:
# Set up
config = Config(module=module)
Expand Down
Loading
Loading