Source code for message_ix_models.model.transport.util

"""Utility code for MESSAGEix-Transport."""

import logging
from functools import lru_cache
from pathlib import Path
from typing import TYPE_CHECKING, Iterable, Union

import pandas as pd

from message_ix_models import Context
from message_ix_models.model.structure import get_codes
from message_ix_models.util import package_data_path

if TYPE_CHECKING:
    import numbers

log = logging.getLogger(__name__)


[docs]def input_commodity_level( context: Context, df: pd.DataFrame, default_level=None ) -> pd.DataFrame: """Add input 'commodity' and 'level' to `df` based on 'technology'. .. deprecated:: 2023-02-27 Use :func:`.computations.input_commodity_level` instead. """ # Retrieve transport technology information from configuration t_info = context.transport.spec.add.set["technology"] # Retrieve general commodity information c_info = get_codes("commodity") @lru_cache() def t_cl(t: str) -> pd.Series: """Return the commodity and level given technology `t`.""" # Retrieve the "input" annotation for this technology input = t_info[t_info.index(t)].eval_annotation("input") # Commodity ID commodity = input["commodity"] # Retrieve the code for this commodity c_code = c_info[c_info.index(commodity)] # Level, in order of precedence: # 1. Technology-specific input level from `t_code`. # 2. Default level for the commodity from `c_code`. # 3. `default_level` argument to this function. level = ( input.get("level", None) or c_code.eval_annotation("level") or default_level ) return pd.Series(dict(commodity=commodity, level=level)) # Process every row in `df`; return a new DataFrame return df.combine_first(df["technology"].apply(t_cl))
[docs]def path_fallback(context_or_regions: Union[Context, str], *parts) -> Path: """Return a :class:`.Path` constructed from `parts`. If ``context.model.regions`` (or a string value as the first argument) is defined and the file exists in a subdirectory of :file:`data/transport/{regions}/`, return its path; otherwise, return the path in :file:`data/transport/`. """ if isinstance(context_or_regions, str): regions = context_or_regions else: # Use a value from a Context object, or a default regions = context_or_regions.model.regions candidates = ( package_data_path("transport", regions, *parts), package_data_path("transport", *parts), ) for c in candidates: if c.exists(): return c raise FileNotFoundError(candidates)
[docs]def sum_numeric(iterable: Iterable, /, start=0) -> "numbers.Real": """Sum only the numeric values in `iterable`.""" result = start for item in iterable: try: result += item except TypeError: pass return result