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) ) ) )