Skip to content

openscm_calibration.model_runner#

Model runner class

Classes:

Name Description
ModelRunner

Callable that supports running the model

ModelRunsInputGenerator

Callable that supports generating model run inputs

OptModelRunner

Model runner used during optimisation

XToNamedPintConvertor

Callable that supports converting the x-vector to Pint quantities

Functions:

Name Description
x_and_parameters_to_named_with_units

Convert the x-vector to a dictionary and add units

ModelRunner #

Bases: Protocol[DataContainer_co]

Callable that supports running the model

Methods:

Name Description
__call__

Run the model

Source code in src/openscm_calibration/model_runner.py
class ModelRunner(Protocol[DataContainer_co]):
    """
    Callable that supports running the model
    """

    def __call__(self, **kwargs: Any) -> DataContainer_co:
        """
        Run the model
        """

__call__ #

__call__(**kwargs: Any) -> DataContainer_co

Run the model

Source code in src/openscm_calibration/model_runner.py
def __call__(self, **kwargs: Any) -> DataContainer_co:
    """
    Run the model
    """

ModelRunsInputGenerator #

Bases: Protocol

Callable that supports generating model run inputs

Methods:

Name Description
__call__

Generate model run inputs

Source code in src/openscm_calibration/model_runner.py
class ModelRunsInputGenerator(Protocol):
    """
    Callable that supports generating model run inputs
    """

    def __call__(self, **kwargs: pint.registry.UnitRegistry.Quantity) -> dict[str, Any]:
        """
        Generate model run inputs
        """

__call__ #

__call__(**kwargs: Quantity) -> dict[str, Any]

Generate model run inputs

Source code in src/openscm_calibration/model_runner.py
def __call__(self, **kwargs: pint.registry.UnitRegistry.Quantity) -> dict[str, Any]:
    """
    Generate model run inputs
    """

OptModelRunner #

Bases: Generic[DataContainer_co]

Model runner used during optimisation

Methods:

Name Description
from_parameter_order

Initialise from a parameter order definition

run_model

Run the model

Attributes:

Name Type Description
convert_x_to_names_with_units XToNamedPintConvertor

Callable to translate the x-vector into input for self.do_model_runs_input_generator

do_model_runs ModelRunner[DataContainer_co]

Function that runs the model

do_model_runs_input_generator ModelRunsInputGenerator

Generator of inputs for do_model_runs

Source code in src/openscm_calibration/model_runner.py
@define
class OptModelRunner(Generic[DataContainer_co]):
    """
    Model runner used during optimisation
    """

    convert_x_to_names_with_units: XToNamedPintConvertor
    """
    Callable to translate the x-vector into input for `self.do_model_runs_input_generator`

    This translates from the x-vector used internally, by e.g. scipy and emcee,
    into a dictionary with meaningful keys and quantities with units (as needed).
    It must produce named output that can be passed directly to
    `self.do_model_runs_input_generator`.
    """  # noqa: E501

    do_model_runs_input_generator: ModelRunsInputGenerator
    """
    Generator of inputs for ``do_model_runs``

    More specifically, the callable used to translate the parameters
    (already converted to [`pint.Quantity`][])
    into the keyword arguments required by `self.do_model_runs`.
    """

    do_model_runs: ModelRunner[DataContainer_co]
    """
    Function that runs the model

    Runs the desired experiments based on inputs generated by
    `self.do_model_runs_input_generator`.
    """

    @classmethod
    def from_parameter_order(
        cls,
        parameter_order: ParameterOrder,
        do_model_runs_input_generator: ModelRunsInputGenerator,
        do_model_runs: ModelRunner[DataContainer_co],
        get_unit_registry: Callable[[], pint.UnitRegistry] | None = None,
    ) -> OptModelRunner[DataContainer_co]:
        """
        Initialise from a parameter order definition

        This is a convenience method

        Parameters
        ----------
        parameter_order
            Parameter order from which to initialise

        do_model_runs_input_generator
            Generator of input for `do_model_runs`.
            See docstring of `self` for more details.

        do_model_runs
            Callable which does the model runs.
            See docstring of  `self` for more details.

        get_unit_registry
            Function to get unit registry.

            Passed to
            [`x_and_parameters_to_named_with_units`][openscm_calibration.model_runner.x_and_parameters_to_named_with_units].
            See docstring of that function for further details.

        Returns
        -------
        :
            Initialised instance
        """
        convert_x_to_names_with_units = partial(
            x_and_parameters_to_named_with_units,
            parameter_order=parameter_order,
            get_unit_registry=get_unit_registry,
        )

        return OptModelRunner(
            convert_x_to_names_with_units=convert_x_to_names_with_units,
            do_model_runs_input_generator=do_model_runs_input_generator,
            do_model_runs=do_model_runs,
        )

    def run_model(
        self,
        x: np.typing.NDArray[np.number[Any]],
    ) -> Any:
        """
        Run the model

        Parameters
        ----------
        x
            Vector of calibration parameter values (the x-vector)

        Returns
        -------
        :
            Results of run
        """
        x_converted_name = self.convert_x_to_names_with_units(x)

        do_model_runs_inputs = self.do_model_runs_input_generator(**x_converted_name)

        res = self.do_model_runs(**do_model_runs_inputs)

        return res

convert_x_to_names_with_units instance-attribute #

convert_x_to_names_with_units: XToNamedPintConvertor

Callable to translate the x-vector into input for self.do_model_runs_input_generator

This translates from the x-vector used internally, by e.g. scipy and emcee, into a dictionary with meaningful keys and quantities with units (as needed). It must produce named output that can be passed directly to self.do_model_runs_input_generator.

do_model_runs instance-attribute #

do_model_runs: ModelRunner[DataContainer_co]

Function that runs the model

Runs the desired experiments based on inputs generated by self.do_model_runs_input_generator.

do_model_runs_input_generator instance-attribute #

do_model_runs_input_generator: ModelRunsInputGenerator

Generator of inputs for do_model_runs

More specifically, the callable used to translate the parameters (already converted to [pint.Quantity][pint.Quantity]) into the keyword arguments required by self.do_model_runs.

from_parameter_order classmethod #

from_parameter_order(
    parameter_order: ParameterOrder,
    do_model_runs_input_generator: ModelRunsInputGenerator,
    do_model_runs: ModelRunner[DataContainer_co],
    get_unit_registry: Callable[[], UnitRegistry]
    | None = None,
) -> OptModelRunner[DataContainer_co]

Initialise from a parameter order definition

This is a convenience method

Parameters:

Name Type Description Default
parameter_order ParameterOrder

Parameter order from which to initialise

required
do_model_runs_input_generator ModelRunsInputGenerator

Generator of input for do_model_runs. See docstring of self for more details.

required
do_model_runs ModelRunner[DataContainer_co]

Callable which does the model runs. See docstring of self for more details.

required
get_unit_registry Callable[[], UnitRegistry] | None

Function to get unit registry.

Passed to x_and_parameters_to_named_with_units. See docstring of that function for further details.

None

Returns:

Type Description
OptModelRunner[DataContainer_co]

Initialised instance

Source code in src/openscm_calibration/model_runner.py
@classmethod
def from_parameter_order(
    cls,
    parameter_order: ParameterOrder,
    do_model_runs_input_generator: ModelRunsInputGenerator,
    do_model_runs: ModelRunner[DataContainer_co],
    get_unit_registry: Callable[[], pint.UnitRegistry] | None = None,
) -> OptModelRunner[DataContainer_co]:
    """
    Initialise from a parameter order definition

    This is a convenience method

    Parameters
    ----------
    parameter_order
        Parameter order from which to initialise

    do_model_runs_input_generator
        Generator of input for `do_model_runs`.
        See docstring of `self` for more details.

    do_model_runs
        Callable which does the model runs.
        See docstring of  `self` for more details.

    get_unit_registry
        Function to get unit registry.

        Passed to
        [`x_and_parameters_to_named_with_units`][openscm_calibration.model_runner.x_and_parameters_to_named_with_units].
        See docstring of that function for further details.

    Returns
    -------
    :
        Initialised instance
    """
    convert_x_to_names_with_units = partial(
        x_and_parameters_to_named_with_units,
        parameter_order=parameter_order,
        get_unit_registry=get_unit_registry,
    )

    return OptModelRunner(
        convert_x_to_names_with_units=convert_x_to_names_with_units,
        do_model_runs_input_generator=do_model_runs_input_generator,
        do_model_runs=do_model_runs,
    )

run_model #

run_model(x: NDArray[number[Any]]) -> Any

Run the model

Parameters:

Name Type Description Default
x NDArray[number[Any]]

Vector of calibration parameter values (the x-vector)

required

Returns:

Type Description
Any

Results of run

Source code in src/openscm_calibration/model_runner.py
def run_model(
    self,
    x: np.typing.NDArray[np.number[Any]],
) -> Any:
    """
    Run the model

    Parameters
    ----------
    x
        Vector of calibration parameter values (the x-vector)

    Returns
    -------
    :
        Results of run
    """
    x_converted_name = self.convert_x_to_names_with_units(x)

    do_model_runs_inputs = self.do_model_runs_input_generator(**x_converted_name)

    res = self.do_model_runs(**do_model_runs_inputs)

    return res

XToNamedPintConvertor #

Bases: Protocol

Callable that supports converting the x-vector to Pint quantities

Methods:

Name Description
__call__

Convert x to pint quantities

Source code in src/openscm_calibration/model_runner.py
class XToNamedPintConvertor(Protocol):
    """
    Callable that supports converting the x-vector to Pint quantities
    """

    def __call__(
        self,
        x: np.typing.NDArray[np.number[Any]],
    ) -> dict[str, pint.registry.UnitRegistry.Quantity]:
        """
        Convert x to pint quantities
        """

__call__ #

__call__(x: NDArray[number[Any]]) -> dict[str, Quantity]

Convert x to pint quantities

Source code in src/openscm_calibration/model_runner.py
def __call__(
    self,
    x: np.typing.NDArray[np.number[Any]],
) -> dict[str, pint.registry.UnitRegistry.Quantity]:
    """
    Convert x to pint quantities
    """

x_and_parameters_to_named_with_units #

x_and_parameters_to_named_with_units(
    x: NDArray[number[Any]],
    parameter_order: ParameterOrder,
    get_unit_registry: Callable[[], UnitRegistry]
    | None = None,
) -> dict[str, Quantity]

Convert the x-vector to a dictionary and add units

Parameters:

Name Type Description Default
x NDArray[number[Any]]

Vector of calibration parameter values

required
parameter_order ParameterOrder

Definition of the (expected) order of the parameters

required
get_unit_registry Callable[[], UnitRegistry] | None

Function to get unit registry. This allows the user to do a delayed import of the unit registry, which is important because pint's unit registries don't parallelise well.

If not provided, [pint.get_application_registry][pint.get_application_registry] is used.

None

Returns:

Type Description
dict[str, Quantity]

Parameters, named and converted to [pint.Quantity][pint.Quantity] where appropriate

Examples:

It also possible to inject a different registry as needed

>>> import pint
>>>
>>> from openscm_calibration.parameter_handling import (
...     ParameterDefinition,
...     ParameterOrder,
... )
>>>
>>> ur_plus_pop = pint.UnitRegistry()
>>> ur_plus_pop.define("thousands = [population]")
>>>
>>> def get_ur_with_pop():
...     return ur_plus_pop
>>>
>>> para_order = ParameterOrder(
...     (
...         ParameterDefinition("para_a", "m"),
...         ParameterDefinition("pop_weight", "thousands"),
...         ParameterDefinition("factor", None),
...     )
... )
>>>
>>> # Withoout the injection, an error is raised
>>> x_and_parameters_to_named_with_units(
...     [1.1, 3.2],
...     para_order,
... )
Traceback (most recent call last):
...
pint.errors.UndefinedUnitError: 'thousands' is not defined in the unit registry
>>> # With the injection, this works nicely
>>> x_and_parameters_to_named_with_units(
...     [1.1, 3.2, 4.0],
...     para_order,
...     get_ur_with_pop,
... )
{'para_a': <Quantity(1.1, 'meter')>, 'pop_weight': <Quantity(3.2, 'thousands')>, 'factor': 4.0}
Source code in src/openscm_calibration/model_runner.py
def x_and_parameters_to_named_with_units(
    x: np.typing.NDArray[np.number[Any]],
    parameter_order: ParameterOrder,
    get_unit_registry: Callable[[], pint.UnitRegistry] | None = None,
) -> dict[str, pint.registry.UnitRegistry.Quantity]:
    """
    Convert the x-vector to a dictionary and add units

    Parameters
    ----------
    x
        Vector of calibration parameter values

    parameter_order
        Definition of the (expected) order of the parameters

    get_unit_registry
        Function to get unit registry.
        This allows the user to do a delayed import of the unit registry,
        which is important because pint's unit registries don't parallelise well.

        If not provided, [`pint.get_application_registry`][] is used.

    Returns
    -------
    :
        Parameters, named and converted to [`pint.Quantity`][]
        where appropriate

    Examples
    --------
    It also possible to inject a different registry as needed
    >>> import pint
    >>>
    >>> from openscm_calibration.parameter_handling import (
    ...     ParameterDefinition,
    ...     ParameterOrder,
    ... )
    >>>
    >>> ur_plus_pop = pint.UnitRegistry()
    >>> ur_plus_pop.define("thousands = [population]")
    >>>
    >>> def get_ur_with_pop():
    ...     return ur_plus_pop
    >>>
    >>> para_order = ParameterOrder(
    ...     (
    ...         ParameterDefinition("para_a", "m"),
    ...         ParameterDefinition("pop_weight", "thousands"),
    ...         ParameterDefinition("factor", None),
    ...     )
    ... )
    >>>
    >>> # Withoout the injection, an error is raised
    >>> x_and_parameters_to_named_with_units(
    ...     [1.1, 3.2],
    ...     para_order,
    ... )
    Traceback (most recent call last):
    ...
    pint.errors.UndefinedUnitError: 'thousands' is not defined in the unit registry
    >>> # With the injection, this works nicely
    >>> x_and_parameters_to_named_with_units(
    ...     [1.1, 3.2, 4.0],
    ...     para_order,
    ...     get_ur_with_pop,
    ... )
    {'para_a': <Quantity(1.1, 'meter')>, 'pop_weight': <Quantity(3.2, 'thousands')>, 'factor': 4.0}
    """  # noqa: E501
    unit_reg = (
        get_unit_registry() if get_unit_registry else pint.get_application_registry()  # type: ignore
    )

    out: dict[str, pint.registry.UnitRegistry.Quantity] = {}
    for val, parameter in zip(x, parameter_order.parameters):
        if parameter.unit is not None:
            val_out = unit_reg.Quantity(val, parameter.unit)
        else:
            val_out = val

        out[parameter.name] = val_out

    return out