Source code for message_ix_models.model.buildings.config

"""Buildings configuration."""

import logging
from dataclasses import dataclass, field
from enum import Enum, auto
from pathlib import Path
from typing import TYPE_CHECKING, Any

import ixmp

from message_ix_models.model.workflow import Config as SolveConfig
from message_ix_models.util.config import ConfigHelper

if TYPE_CHECKING:
    from typing import TypedDict

    from message_ix_models import Context

    class DataPaths(TypedDict):
        prices: Path
        sturm_r: Path
        sturm_c: Path
        demand_static: Path


log = logging.getLogger(__name__)

#: Defaults for :attr:`Config.data_paths`.
DEFAULT_DATA_PATHS: "DataPaths" = dict(
    prices=Path("input_prices_R12.csv"),
    sturm_r=Path("report_MESSAGE_resid_SSP2_nopol_post.csv"),
    sturm_c=Path("report_MESSAGE_comm_SSP2_nopol_post.csv"),
    demand_static=Path("static_20251227.csv"),
)


[docs] class METHOD(Enum): """Enumeration of code paths in :func:`.buildings.build.main`.""" #: Code path with :func:`~.prepare_data_A`, used by :mod:`.project.navigate`. A = auto() #: Code path with :func:`~.prepare_data_B`, used by :mod:`.model.bmt`. B = auto()
def _code_dir_factory() -> Path: """Return the default value for :attr:`.Config.code_dir`. In order of precedence: 1. The directory where :mod:`message_ix_buildings` is installed. 2. The :mod:`ixmp` configuration key ``message buildings dir``, if set. The older, private MESSAGE_Buildings repository is not an installable Python package, so it cannot be imported without information on its location. This key can be set in the local :ref:`ixmp configuration file <ixmp:configuration>`. 3. A directory named :file:`./buildings` in the parent of the directory containing :mod:`message_ix_models`. """ from importlib.util import find_spec from message_ix_models.util import MESSAGE_MODELS_PATH if spec := find_spec("message_ix_buildings"): assert spec.origin is not None return Path(spec.origin).parent try: return Path(ixmp.config.get("message buildings dir")).expanduser().resolve() except AttributeError: pass # Not set return MESSAGE_MODELS_PATH.parents[1].joinpath("buildings")
[docs] @dataclass class Config(ConfigHelper): """Configuration options for :mod:`.buildings` code. The code responds to values set on an instance of this class. Raises ------ FileNotFoundError if :attr:`code_dir` points to a non-existent directory. """ #: Name or ID of STURM scenario to run. sturm_scenario: str #: Climate scenario. Either `BL` or `2C`. climate_scenario: str = "BL" #: :obj:`True` if the base scenario should be cloned. clone: bool = False #: Path to the MESSAGEix-Buildings code and data. #: #: If not set explicitly, this is populated using :func:`_code_dir_factory`. code_dir: Path = field(default_factory=_code_dir_factory) #: Paths to input data files. data_paths: "DataPaths" = field(default_factory=DEFAULT_DATA_PATHS.copy) #: Maximum number of iterations of the ACCESS–STURM–MESSAGE loop. Set to 1 for #: once-through mode. max_iterations: int = 0 #: Code path to use in :func:`.buildings.build.main`. method: METHOD = METHOD.A #: :obj:`True` if the MESSAGEix-Materials + MESSAGEix-Buildings combination is #: active with_materials: bool = True #: Path for STURM output. _output_path: Path | None = None #: Run the ACCESS model on every iteration. run_access: bool = False #: Keyword arguments for :meth:`.message_ix.Scenario.solve`. Set #: `model="MESSAGE_MACRO"` to solve scenarios using MESSAGE_MACRO. solve: dict[str, Any] = field(default_factory=lambda: dict(model="MESSAGE")) #: Similar to `solve`, but using another config class. solve_config: SolveConfig = field( default_factory=lambda: SolveConfig( solve=dict(model="MESSAGE"), reserve_margin=False ) ) #: .. todo:: Document the meaning of this setting. ssp: str = "SSP2" #: Method for running STURM. See :func:`.sturm.run`. sturm_method: str = "Rscript" def __post_init__(self) -> None: if not self.code_dir.exists(): raise FileNotFoundError(f"MESSAGEix-Buildings not found at {self.code_dir}") def set_output_path(self, context: "Context") -> None: # Base path for output during iterations self._output_path = context.get_local_path("buildings") self._output_path.mkdir(parents=True, exist_ok=True)