Skip to content

Transport improvements from 2025-W20 #349

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

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
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
10 changes: 3 additions & 7 deletions .github/workflows/transport.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@ env:
"SSP4",
"SSP4 policy",
"SSP5",
"SSP5 policy",
"EDITS-CA",
"EDITS-HA",
"LED-SSP1",
"LED-SSP2"
"SSP5 policy"
]

# Currently disabled:
Expand All @@ -49,8 +45,8 @@ env:

on:
# Uncomment these lines for debugging, but leave them commented on 'main'
# pull_request:
# branches: [ main ]
pull_request:
branches: [ main ]
# push:
# branches: [ main ]
schedule:
Expand Down
15 changes: 9 additions & 6 deletions message_ix_models/data/sdmx/IIASA_ECE_AGENCIES(0.1).xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<mes:Structure xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:com="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" xmlns:data="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/structurespecific" xmlns:str="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/structure" xmlns:mes="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" xmlns:gen="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" xmlns:footer="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message/footer">
<?xml version='1.0' encoding='utf-8'?>
<mes:Structure xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:com="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" xmlns:md="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/metadata/generic" xmlns:md_ss="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/metadata/structurespecific" xmlns:data="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/structurespecific" xmlns:str="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/structure" xmlns:mes="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" xmlns:gen="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" xmlns:footer="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message/footer">
<mes:Header>
<mes:ID>none</mes:ID>
<mes:Test>false</mes:Test>
<mes:Prepared>2023-09-04T16:31:44.700655</mes:Prepared>
<mes:Source xml:lang="en">Generated by message_ix_models 2023.5.32.dev20+g8d51636</mes:Source>
<mes:Prepared>2025-05-19T22:09:29.424849</mes:Prepared>
<mes:Sender id="none"/>
<mes:Source xml:lang="en">Generated by message_ix_models 2025.1.11.dev398+gdaf97af4a.d20250504</mes:Source>
</mes:Header>
<mes:Structures>
<str:OrganisationSchemes>
<str:AgencyScheme version="0.1" isExternalReference="false" isFinal="false" agencyID="IIASA_ECE" id="AGENCIES" urn="urn:sdmx:org.sdmx.infomodel.base.AgencyScheme=IIASA_ECE:AGENCIES(0.1)">
<str:AgencyScheme isExternalReference="false" isFinal="false" agencyID="IIASA_ECE" version="0.1" id="AGENCIES" urn="urn:sdmx:org.sdmx.infomodel.base.AgencyScheme=IIASA_ECE:AGENCIES(0.1)">
<com:Description xml:lang="en">Agencies referenced by data structures in message_ix_models</com:Description>
<str:Agency id="IIASA_ECE">
<str:Agency id="IIASA_ECE" urn="urn:sdmx:org.sdmx.infomodel.base.Agency=IIASA_ECE:AGENCIES(0.1).IIASA_ECE">
<com:Name xml:lang="en">IIASA Energy, Climate, and Environment Program</com:Name>
</str:Agency>
<str:Agency id="ICONICS">
Expand All @@ -17,7 +20,7 @@
<str:URI>https://depts.washington.edu/iconics/</str:URI>
</str:Contact>
</str:Agency>
<str:Agency id="IEA">
<str:Agency id="IEA" urn="urn:sdmx:org.sdmx.infomodel.base.Agency=IIASA_ECE:AGENCIES(0.1).IEA">
<com:Name xml:lang="en">International Energy Agency</com:Name>
<str:Contact>
<str:URI>https://iea.org</str:URI>
Expand Down
7 changes: 7 additions & 0 deletions message_ix_models/model/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class Config(ConfigHelper):
#: at :doc:`/pkg-data/relation`.
relations: str = "A"

#: ID of the relation used to constrain global total CO₂ emissions. A code with this
#: ID **must** be in the code list identified by :attr:`relations`.
#:
#: In :mod:`message_data`, this ID was stored in the module data variable
#: :py:`message_data.projects.engage.runscript_main.RELATION_GLOBAL_CO2`.
relation_global_co2: str = "CO2_Emission_Global_Total"

#: The 'year' codelist (time periods) to use, Must be one of the lists of periods
#: described at :doc:`/pkg-data/year`.
years: str = "B"
Expand Down
17 changes: 7 additions & 10 deletions message_ix_models/model/transport/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,17 @@ def short_hash(value: str) -> str:
def tax_emission(context: "Context", scenario: "Scenario", price: float) -> "Scenario":
"""Add emission tax.

This function calls code from :mod:`message_data.projects.navigate.workflow`,
:mod:`message_data.tools.utilities`, and other non-public locations. It cannot be
used without access to those codes.
See also
--------
message_ix_models.project.engage.workflow.step_0
message_ix_models.project.navigate.workflow.tax_emission
"""
from message_ix import make_df

from message_ix_models.project.engage import workflow as engage_workflow
from message_ix_models.project.navigate import workflow as navigate_workflow
from message_ix_models.util import broadcast

try:
from message_data.projects.engage import workflow as engage_workflow
from message_data.projects.navigate import workflow as navigate_workflow
except ImportError:
raise RuntimeError("Requires non-public code from message_data")

# Add ENGAGE-style emissions accounting
scenario = engage_workflow.step_0(context, scenario)

Expand Down Expand Up @@ -266,7 +263,7 @@ def generate(
lambda _, s: initial_new_capacity_up_v311(s, safety_factor=1.05),
)

# This block copied from message_data.projects.navigate.workflow
# This block copied from message_ix_models.project.navigate.workflow
if config.policy:
# Add a carbon tax
name = wf.add_step(f"{label} with tax", name, tax_emission, price=1000.0)
Expand Down
54 changes: 29 additions & 25 deletions message_ix_models/project/engage/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,22 @@
from message_ix import Scenario

from message_ix_models import Context, ScenarioInfo
from message_ix_models.model.workflow import Config, solve
from message_ix_models.util import broadcast, identify_nodes
from message_ix_models.model import workflow as model_workflow
from message_ix_models.util import HAS_MESSAGE_DATA, broadcast, identify_nodes
from message_ix_models.workflow import Workflow

try:
# NB These modules have not been migrated from message_data.projects.engage
from .runscript_main import glb_co2_relation as RELATION_GLOBAL_CO2
from .scenario_runner import ScenarioRunner
except ImportError:
RELATION_GLOBAL_CO2 = ""
if HAS_MESSAGE_DATA:
# NB This module has not been migrated from message_data.projects.engage.
from message_data.projects.engage.scenario_runner import ScenarioRunner
else:
ScenarioRunner = type("ScenarioRunner", (), {})


log = logging.getLogger(__name__)


@dataclass
class PolicyConfig(Config):
class PolicyConfig(model_workflow.Config):
"""Configuration for the 3-step ENGAGE workflow for climate policy scenarios."""

#: Label of the climate policy scenario, often related to a global carbon budget in
Expand Down Expand Up @@ -170,7 +169,7 @@ def step_0(context: Context, scenario: Scenario, **kwargs) -> Scenario:
These operations must occur no matter which combinations of 1 or more of
:func:`step_1`, :func:`step_2`, and/or :func:`step_3` are to be run on `scenario`.
"""
from message_data.tools.utilities import (
from message_ix_models.tools import (
add_AFOLU_CO2_accounting,
add_alternative_TCE_accounting,
add_CO2_emission_constraint,
Expand All @@ -183,22 +182,29 @@ def step_0(context: Context, scenario: Scenario, **kwargs) -> Scenario:
except ValueError:
pass # Solution did not exist

remove_emission_bounds(scenario)
remove_emission_bounds.main(scenario)

# Identify the node codelist used by `scenario` (in case it is not set on `context`)
context.model.regions = identify_nodes(scenario)

kw = dict(relation_name=RELATION_GLOBAL_CO2, reg=f"{context.model.regions}_GLB")
kw = dict(
relation_name=context.model.relation_global_co2,
reg=f"{context.model.regions}_GLB",
)

# “Step1.3 Make changes required to run the ENGAGE setup” (per .runscript_main)
log.info("Add separate FFI and AFOLU CO2 accounting")
add_FFI_CO2_accounting(scenario, **kw)
add_AFOLU_CO2_accounting(scenario, **kw)
add_FFI_CO2_accounting.main(scenario, **kw, constraint_value=None)
add_AFOLU_CO2_accounting.add_AFOLU_CO2_accounting(
scenario, **kw, constraint_value=None
)

log.info("Add alternative TCE accounting")
add_alternative_TCE_accounting(scenario)
add_alternative_TCE_accounting.main(scenario)

add_CO2_emission_constraint(scenario, **kw, constraint_value=0.0, type_rel="lower")
add_CO2_emission_constraint.main(
scenario, **kw, constraint_value=0.0, type_rel="lower"
)

return scenario

Expand Down Expand Up @@ -226,7 +232,7 @@ def step_1(context: Context, scenario: Scenario, config: PolicyConfig) -> Scenar

def step_2(context: Context, scenario: Scenario, config: PolicyConfig) -> Scenario:
"""Step 2 of the ENGAGE climate policy workflow."""
from message_data.tools.utilities import add_emission_trajectory
from message_ix_models.tools import add_emission_trajectory

# Retrieve a pandas.DataFrame with the CO2 emissions trajectory
#
Expand All @@ -245,7 +251,7 @@ def step_2(context: Context, scenario: Scenario, config: PolicyConfig) -> Scenar
pass # Solution did not exist

# Add this trajectory as bound_emission values
add_emission_trajectory(
add_emission_trajectory.main(
scenario,
data=df,
type_emission="TCE_CO2",
Expand All @@ -255,9 +261,8 @@ def step_2(context: Context, scenario: Scenario, config: PolicyConfig) -> Scenar

with scenario.transact(message="Remove lower bound on global total CO₂ emissions"):
name = "relation_lower"
scenario.remove_par(
name, scenario.par(name, filters={"relation": [RELATION_GLOBAL_CO2]})
)
filters = dict(relation=[context.model.relation_global_co2])
scenario.remove_par(name, scenario.par(name, filters=filters))

return scenario

Expand Down Expand Up @@ -305,9 +310,8 @@ def step_3(context: Context, scenario: Scenario, config: PolicyConfig) -> Scenar
# As in step_2, remove the lower bound on global CO2 emissions. This is
# necessary if step_2 was not run (for instance, NAVIGATE T6.2 protocol)
name = "relation_lower"
scenario.remove_par(
name, scenario.par(name, filters={"relation": [RELATION_GLOBAL_CO2]})
)
filters = dict(relation=[context.model.relation_global_co2])
scenario.remove_par(name, scenario.par(name, filters=filters))

return scenario

Expand Down Expand Up @@ -391,7 +395,7 @@ def add_steps(
# Add a step to solve the scenario (except for after step_0); update the
# step name for the next loop iteration
s = workflow.add_step(
f"{s} solved", s, solve, config=cfg, set_as_default=True
f"{s} solved", s, model_workflow.solve, config=cfg, set_as_default=True
)

# Return the name of the last of the added steps
Expand Down
7 changes: 5 additions & 2 deletions message_ix_models/project/ssp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import logging
import re
from typing import Union
from typing import TYPE_CHECKING, Union

from .structure import SSP, SSP_2017, SSP_2024, generate

if TYPE_CHECKING:
from message_ix_models.util.sdmx import URNLookupEnum

__all__ = [
"SSP",
"SSP_2017",
Expand Down Expand Up @@ -38,7 +41,7 @@ def __init__(self, default: Union[SSP_2017, SSP_2024]):
def __set_name__(self, owner, name):
self._name = "_" + name

def __get__(self, obj, type) -> Union[SSP_2017, SSP_2024]:
def __get__(self, obj, type) -> "URNLookupEnum":
if obj is None:
return None # type: ignore [return-value]

Expand Down
20 changes: 15 additions & 5 deletions message_ix_models/project/ssp/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import sdmx.urn
from sdmx.model import common, v21

from message_ix_models.util.sdmx import make_enum, register_agency, write
from message_ix_models.util.sdmx import ItemSchemeEnumType, read, register_agency, write

if TYPE_CHECKING:
from os import PathLike
Expand Down Expand Up @@ -183,8 +183,18 @@ def generate(context: "Context", base_dir: Optional["PathLike"] = None):
write(cl, base_dir)


#: Enumeration of codes for SSP 2017 edition.
SSP = SSP_2017 = make_enum("ICONICS:SSP(2017)")
class SSP_2017(metaclass=ItemSchemeEnumType):
"""Enumeration of codes for SSP 2017 edition."""

#: Enumeration of codes for SSP 2024 edition.
SSP_2024 = make_enum("ICONICS:SSP(2024)")
def _get_item_scheme(self):
return read("ICONICS:SSP(2017)")


SSP = SSP_2017


class SSP_2024(metaclass=ItemSchemeEnumType):
"""Enumeration of codes for SSP 2024 edition."""

def _get_item_scheme(self):
return read("ICONICS:SSP(2024)")
4 changes: 1 addition & 3 deletions message_ix_models/tests/model/transport/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def test_bare(request, test_context, tmp_path, regions, years):
@mark.parametrize(
"build",
(
pytest.param(True, marks=make_mark["gh"](328)), # Run .transport.build.main()
True, # Run .transport.build.main()
False, # Use data from an Excel export
),
)
Expand Down Expand Up @@ -202,7 +202,6 @@ def test_simulated(request, test_context, build, regions="R12", years="B"):

@build.get_computer.minimum_version
@MARK[10]
@make_mark["gh"](328)
def test_simulated_iamc(
request, tmp_path_factory, test_context, regions="R12", years="B"
) -> None:
Expand Down Expand Up @@ -252,7 +251,6 @@ def test_simulated_iamc(

@build.get_computer.minimum_version
@MARK[10]
@make_mark["gh"](328)
@mark.usefixtures("quiet_genno")
@pytest.mark.parametrize(
"plot_name",
Expand Down
3 changes: 2 additions & 1 deletion message_ix_models/tests/project/test_ssp.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def test_enum():
# Same SSP ID from different enums are not equivalent
assert SSP_2017["1"] != SSP_2024["1"]
assert SSP_2017["1"] is not SSP_2024["1"]
assert SSP["1"] != SSP_2024["1"]
# NB Ignored because of https://github.com/python/mypy/issues/7568
assert SSP["1"] != SSP_2024["1"] # type: ignore [misc]


@pytest.mark.parametrize(
Expand Down
Loading
Loading