Error handling

AxsDB allows for fine-grained control on error handling. Each AbsorptionDatabase instance can be assigned an error handling policy. When unspecified, a default configuration is used (retrieved with get_error_handling_config(), set with set_error_handling_config()).

Handled errors

The following errors can be controlled:

MISSING

The coordinate is missing from the database.

SCALAR

The coordinate is present, but it is scalar.

BOUNDS

One or several values are out of coordinate bounds when interpolating.

The simplest level of control consists in specifying the action that will be triggered when the error is encountered:

RAISE

Raise an exception.

WARN

Emit a warning.

IGNORE

Ignore the error silently.

BOUNDS errors can be applied more fine-grained control, as we are about to see.

Out-of-bounds errors

Out-of-bounds (OOB) values are checked early when interpolating the data. Each bound (lower and upper) can be assigned a different behaviour.

For both the WARN and IGNORE actions, two modes are implemented:

FILL

OOB points are assigned a specific.

CLAMP

The coordinate is clamped.

By default, the fill value is 0.0, and it can be customized. If None is used, np.nan will be applied.

Configuration examples

While error handling configuration is implemented by the ErrorHandlingConfiguration and associated classes, the recommended interface uses plain dictionaries. Let’s see a few examples.

Basic setup

>>> config = {
...     "t": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": "ignore",
...     },
...     "p": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": "ignore",
...     },
...     "x": {
...         "missing": "ignore",
...         "scalar": "ignore",
...         "bounds": "raise"
...     },
... }

The ErrorHandlingConfiguration.convert() constructor automatically converts simple configuration keywords to appropriate values, in particular for OOB handling entries:

>>> pprint(ErrorHandlingConfiguration.convert(config))
ErrorHandlingConfiguration(
    x=ErrorHandlingPolicy(
        missing=<IGNORE>,
        scalar=<IGNORE>,
        bounds=(
            BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0),
            BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
        )
    ),
    p=ErrorHandlingPolicy(
        missing=<RAISE>,
        scalar=<RAISE>,
        bounds=(
            BoundsPolicy(action=<IGNORE>, mode=<FILL>, fill_value=0.0),
            BoundsPolicy(action=<IGNORE>, mode=<FILL>, fill_value=0.0)
        )
    ),
    t=ErrorHandlingPolicy(
        missing=<RAISE>,
        scalar=<RAISE>,
        bounds=(
            BoundsPolicy(action=<IGNORE>, mode=<FILL>, fill_value=0.0),
            BoundsPolicy(action=<IGNORE>, mode=<FILL>, fill_value=0.0)
        )
    )
)

Full bound specification

The MISSING and SCALAR errors only expect an action specifier (ErrorHandlingAction), which is passed using the corresponding string. Out-of-bounds errors, on the other hand, are more complex and expect more settings:

>>> config = {
...     "t": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": {
...             "action": "warn",
...             "mode": "fill",
...             "fill_value": None,  # will use np.nan
...         },
...     },
...     "p": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": "ignore",
...     },
...     "x": {
...         "missing": "ignore",
...         "scalar": "ignore",
...         "bounds": "raise"
...     },
... }
>>> pprint(ErrorHandlingConfiguration.convert(config))
ErrorHandlingConfiguration(
    ...,
    t=ErrorHandlingPolicy(
        missing=<RAISE>,
        scalar=<RAISE>,
        bounds=(
            BoundsPolicy(action=<WARN>, mode=<FILL>, fill_value=None),
            BoundsPolicy(action=<WARN>, mode=<FILL>, fill_value=None)
        )
    )
)

Note that if the bounds entry receives a single value, symmetric handling policies are assumed.

Per-bound control

Different out-of-bounds error handling policies can be passed for the lower and higher bounds. For that purpose, pass a 2-tuple to the bounds entry:

>>> config = {
...     "t": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": ("warn", "raise"),
...         # When unambiguous, strings are interpreted as actions or
...         # OOB modes; omitted entries are assigned the default value
...     },
...     "p": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": "ignore",
...     },
...     "x": {
...         "missing": "ignore",
...         "scalar": "ignore",
...         "bounds": "raise"
...     },
... }
>>> pprint(ErrorHandlingConfiguration.convert(config))
ErrorHandlingConfiguration(
    ...,
    t=ErrorHandlingPolicy(
        missing=<RAISE>,
        scalar=<RAISE>,
        bounds=(
            BoundsPolicy(action=<WARN>, mode=<FILL>, fill_value=0.0),
            BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
        )
    )
)

This also works with dictionaries:

>>> config = {
...     "t": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": (
...             {"action": "warn", "mode": "clamp"},
...             "raise",
...         ),
...     },
...     "p": {
...         "missing": "raise",
...         "scalar": "raise",
...         "bounds": "ignore",
...     },
...     "x": {
...         "missing": "ignore",
...         "scalar": "ignore",
...         "bounds": "raise"
...     },
... }
>>> pprint(ErrorHandlingConfiguration.convert(config))
ErrorHandlingConfiguration(
    ...,
    t=ErrorHandlingPolicy(
        missing=<RAISE>,
        scalar=<RAISE>,
        bounds=(
            BoundsPolicy(action=<WARN>, mode=<CLAMP>, fill_value=0.0),
            BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
        )
    )
)

Partial configuration

You can specify only the dimensions you want to override, with unspecified dimensions using values from the global default configuration. This is particularly useful when you only need to adjust one aspect of the error handling configuration and want to keep all other settings at their defaults. Examples:

  • Only override pressure policy (other dimensions use defaults)

    >>> config = {
    ...     "p": {"missing": "raise", "scalar": "raise", "bounds": "raise"}
    ... }
    >>> pprint(ErrorHandlingConfiguration.convert(config))
    ErrorHandlingConfiguration(
        x=ErrorHandlingPolicy(
            missing=<IGNORE>,
            scalar=<IGNORE>,
            bounds=(
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0),
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
            )
        ),
        p=ErrorHandlingPolicy(
            missing=<RAISE>,
            scalar=<RAISE>,
            bounds=(
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0),
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
            )
        ),
        t=ErrorHandlingPolicy(
            missing=<RAISE>,
            scalar=<RAISE>,
            bounds=(
                BoundsPolicy(action=<IGNORE>, mode=<FILL>, fill_value=0.0),
                BoundsPolicy(action=<IGNORE>, mode=<FILL>, fill_value=0.0)
            )
        )
    )
    
  • Only override temperature bounds (missing/scalar use defaults)

    >>> config = {"t": {"bounds": "raise"}}
    >>> pprint(ErrorHandlingConfiguration.convert(config))
    ErrorHandlingConfiguration(
        ...,
        t=ErrorHandlingPolicy(
            missing=<RAISE>,
            scalar=<RAISE>,
            bounds=(
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0),
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
            )
        )
    )
    
  • Override multiple dimensions

    >>> config = {
    ...     "p": {"missing": "raise", "scalar": "raise", "bounds": "raise"},
    ...     "t": {"bounds": "warn"}
    ... }
    >>> pprint(ErrorHandlingConfiguration.convert(config))
    ErrorHandlingConfiguration(
        ...,
        p=ErrorHandlingPolicy(
            missing=<RAISE>,
            scalar=<RAISE>,
            bounds=(
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0),
                BoundsPolicy(action=<RAISE>, mode=<FILL>, fill_value=0.0)
            )
        ),
        t=ErrorHandlingPolicy(
            missing=<RAISE>,
            scalar=<RAISE>,
            bounds=(
                BoundsPolicy(action=<WARN>, mode=<FILL>, fill_value=0.0),
                BoundsPolicy(action=<WARN>, mode=<FILL>, fill_value=0.0)
            )
        )
    )