Source code for stilt.transport.stilt_r

"""
STILT-R as a transport model that runs nothing: the settings of runs imported from STILT-R.

STILT-R (`uataq/stilt <https://github.com/uataq/stilt>`_) runs HYSPLIT from
R. :func:`stilt.migration.import_stilt_r` writes the runs of a STILT-R or
X-STILT project into an output directory, each with the settings STILT-R's
files record. A variant with ``model: stilt-r`` names their folder, so
``project.status``, ``project.particles``, and ``project.jacobian`` read
them. PYSTILT cannot run STILT-R: a run of such a variant fails.
"""

from __future__ import annotations

import datetime as dt
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar

from pydantic import BaseModel, ConfigDict, Field, model_validator

from stilt.exceptions import SimulationError
from stilt.transport import ModelRun
from stilt.transport.hysplit.config import HysplitConfig

if TYPE_CHECKING:
    from stilt.receptors import Receptor


[docs] class StiltRConfig(HysplitConfig): """ The settings of imported STILT-R runs: HYSPLIT's, as STILT-R ran it, and the STILT-R version. The settings are :class:`~stilt.transport.hysplit.HysplitConfig`'s, under the same names, as STILT-R's files record them (``SETUP.CFG``, ``CONTROL``, and the ``.rds`` file; see :func:`stilt.migration.import_stilt_r`). Every one must be given: none has a default, because an imported run's settings name its folder, and a default would stand in for a value no file gave. The ``config.yaml`` variant the import prints gives them all. """ #: Fields left out of a run's recorded settings. ``version`` is recorded #: as the transport model's version instead, as HYSPLIT's build is. UNRECORDED: ClassVar[frozenset[str]] = frozenset({"exe_dir", "data_dir", "version"}) version: str | None = Field( None, description=( "The STILT-R version that made the runs, such as a commit of " "uataq/stilt. No STILT-R file records it, so the import is given " "it. Recorded as the transport model's version." ), ) @model_validator(mode="before") @classmethod def _every_setting_given(cls, data: Any) -> Any: """Require every recorded setting: an imported run has no defaults.""" if isinstance(data, dict): missing = sorted(set(cls.model_fields) - cls.UNRECORDED - set(data)) if missing: raise ValueError( f"An imported STILT-R run gives every setting, and these are " f"missing: {', '.join(missing)}. Copy the variant " "stilt.migration.import_stilt_r printed." ) return data
[docs] class StiltRMetConfig(BaseModel): """ The met of imported STILT-R runs, as written under ``mets:`` in ``config.yaml``. It is the directory the runs' ``CONTROL`` files name, as they name it. The met files need not exist any more; each run's own files are recorded in its particle file (``stilt:met_files``). """ model_config = ConfigDict(extra="forbid", frozen=True) met_path: str = Field( ..., description=( "Directory of the met files the runs read, as their CONTROL files " "name it: the met_path of STILT-R's run_stilt.r, or a folder in it." ), )
[docs] def settings(self) -> dict[str, Any]: """Return what an imported run records of its met: the directory, as written.""" return {"met_path": self.met_path}
[docs] class StiltRModel: """ The ``stilt-r`` transport model, which ``get_model("stilt-r")`` returns. It holds the settings of imported STILT-R runs and runs nothing. :meth:`run` fails, so a ``stilt run`` of a receptor STILT-R did not run records a failure for it. """ name = "stilt-r" config_class = StiltRConfig met_config_class = StiltRMetConfig #: Nothing runs, so no files are written. needs_workdir = False batched = False
[docs] def version(self, config: StiltRConfig) -> str: """ Return the STILT-R version of *config*. Raises ------ ValueError If *config* gives none. """ if config.version is None: raise ValueError( "A stilt-r variant needs version: the STILT-R version given to " "stilt.migration.import_stilt_r, as it printed the variant." ) return config.version
[docs] def data_files(self, config: StiltRConfig) -> dict[str, str] | None: """Return ``None``: the data files are STILT-R's own, fixed by its version.""" return None
[docs] def run( self, receptor: Receptor, config: StiltRConfig, met: StiltRMetConfig, window: tuple[dt.datetime, dt.datetime], workdir: Path | None = None, timeout: int | None = None, ) -> ModelRun: """ Fail: STILT-R runs are imported, not run. Raises ------ SimulationError Always, with ``reason`` ``NOT_RUNNABLE``. """ raise SimulationError( f"{receptor.id}: PYSTILT cannot run STILT-R. A stilt-r variant reads " "the runs stilt.migration.import_stilt_r wrote; run the receptor " "with a hysplit variant instead.", reason="NOT_RUNNABLE", )
__all__ = ["StiltRConfig", "StiltRMetConfig", "StiltRModel"]