Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

**obsarray** is an extension to [xarray](https://docs.xarray.dev/en/stable/) for defining, storing and interfacing with uncertainty information using standardised metadata. It is particularly designed to work well with [netCDF](https://www.unidata.ucar.edu/software/netcdf/) files and for the Earth Observation community.

> **Upcoming change:** obsarray currently follows v0.1 of the
> [UNC specification](https://comet-toolkit.github.io/unc_website/) for
> uncertainty metadata. A future release will default to
> [draft v0.2](https://comet-toolkit.github.io/unc_website/specification/draft-v0.2/unc_specification.html),
> which is proposed for inclusion in the CF conventions and is not backwards
> compatible with v0.1. See
> [what this means for you](https://obsarray.readthedocs.io/en/latest/content/user/unc_conventions.html).

obsarray is part of the [CoMet Toolkit](https://www.comet-toolkit.org) (community metrology toolkit), and can combined with the [punpy](https://punpy.readthedocs.io/en/latest/) (propagating uncertainties in python) module for very simple propagation of defined data uncertainties through arbitrary python functions.

## Installation
Expand Down
8 changes: 7 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,13 @@
# theme further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
html_theme_options = {
"announcement": (
"<strong>Upcoming change:</strong> obsarray will move to draft v0.2 of the UNC "
'specification. <a href="https://obsarray.readthedocs.io/en/latest/content/user/'
'unc_conventions.html">Find out what this means for you</a>.'
),
}

html_logo = "figs/obsarray.png"

Expand Down
12 changes: 11 additions & 1 deletion docs/content/user/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,14 @@ Utility functions
.. autosummary::
:toctree: generated/

utils.append_names
utils.append_names

Options
=======

.. autosummary::
:toctree: generated/

options.set_options
options.get_options
conventions.UNCConventionsWarning
89 changes: 89 additions & 0 deletions docs/content/user/unc_conventions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.. _unc_conventions:

#############################################
Upcoming change: UNC specification draft v0.2
#############################################

**obsarray** stores uncertainty information following the `Uncertainty metadata Naming Conventions (UNC)
specification <https://comet-toolkit.github.io/unc_website/>`_. It currently follows
`v0.1 <https://comet-toolkit.github.io/unc_website/specification/v0.1/unc_specification.html>`_.

`Draft v0.2 <https://comet-toolkit.github.io/unc_website/specification/draft-v0.2/unc_specification.html>`_
is a major revision, developed with the CF community and proposed for inclusion in the
`CF conventions <https://cfconventions.org>`_ as a new "Uncertainty" chapter. A future release of **obsarray**
will support draft v0.2 and make it the default. Support for v0.1 will remain available, selectable with the
``unc_conventions`` option (see :ref:`below <unc_conventions_option>`).

What will change
================

Datasets following v0.1 are **not compatible** with draft v0.2. The main changes to the metadata obsarray writes
are:

.. list-table::
:widths: 50 50
:header-rows: 1

* - v0.1 (current)
- Draft v0.2
* - ``unc_comps`` attribute on the observation variable (list of names)
- ``uncertainty_variables`` attribute on the data variable (blank-separated string of names)
* - ``pdf_shape`` attribute (default ``"gaussian"``)
- ``probability_distribution`` attribute
* - ``err_corr_dim<i>_name``, ``_form``, ``_params`` and ``_units`` attributes
- Single ``error_correlation`` string attribute
* - Error-correlation forms ``random`` and ``systematic``
- Error-correlation structural types ``independent`` and ``common``
* - Meaning of the uncertainty values not defined
- Required ``coverage_interval`` and ``coverage_probability`` attributes

The full list is in `Appendix B of the draft v0.2 specification
<https://comet-toolkit.github.io/unc_website/specification/draft-v0.2/unc_specification.html#appendix-b-changes-from-v0-1>`_.

The UNC conventions warning
===========================

Until a conventions version is chosen explicitly, **obsarray** issues an :py:class:`~obsarray.conventions.UNCConventionsWarning`
the first time in a session that it writes uncertainty metadata (for example, when adding an uncertainty variable
with ``ds.unc[...]`` or creating a dataset from a template that includes one). It is shown once per session, and
reading existing uncertainty information does not trigger it.

.. _unc_conventions_option:

Choosing the conventions version
================================

To keep the current behaviour and silence the warning, choose v0.1 explicitly with
:py:class:`~obsarray.options.set_options`, either for the rest of the session:

.. code-block:: python

import obsarray

obsarray.set_options(unc_conventions="v0.1")

or only within a block:

.. code-block:: python

with obsarray.set_options(unc_conventions="v0.1"):
ds.unc["temperature"]["u_noise"] = (["x"], u_noise, {"units": "K"})

``"v0.1"`` is currently the only supported value. ``"v0.2"`` will become available once draft v0.2 support is
added.

The warning can also be filtered like any other Python warning:

.. code-block:: python

import warnings
import obsarray

warnings.filterwarnings("ignore", category=obsarray.UNCConventionsWarning)

.. note::
**For developers of packages built on obsarray:** if your package writes uncertainty metadata on behalf of
its users, wrap that code in ``with obsarray.set_options(unc_conventions="v0.1"):``. This keeps your output
unchanged when obsarray's default changes, and avoids showing your users a warning about code they don't
control. Avoid calling ``obsarray.set_options`` globally from library code, as that changes the setting for
your users' own obsarray code too.
3 changes: 2 additions & 1 deletion docs/content/user/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ In this user guide, you will find detailed descriptions and examples that descri

unc_accessor
flag_accessor
templater
templater
unc_conventions
2 changes: 2 additions & 0 deletions obsarray/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from obsarray.templater.dstemplater import DSTemplater
from obsarray.templater.dswriter import DSWriter
from obsarray.utils import append_names
from obsarray.options import set_options, get_options
from obsarray.conventions import UNCConventionsWarning

try:
__version__ = version("obsarray")
Expand Down
84 changes: 84 additions & 0 deletions obsarray/conventions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""conventions - handling of the UNC metadata conventions version obsarray follows"""

import inspect
import os
import warnings

from obsarray.options import OPTIONS

__author__ = "Sam Hunt <sam.hunt@npl.co.uk>"
__all__ = ["UNCConventionsWarning"]


UNC_CONVENTIONS_DOCS_URL = (
"https://obsarray.readthedocs.io/en/latest/content/user/unc_conventions.html"
)

_UNC_CONVENTIONS_MESSAGE = (
"obsarray currently writes uncertainty metadata following v0.1 of the UNC "
"specification. A future release will default to UNC draft v0.2, which is proposed "
"for inclusion into the CF conventions and is not backwards compatible with v0.1 (for "
"example, 'unc_comps' becomes 'uncertainty_variables' and 'err_corr_*' becomes "
"'error_correlation'). See " + UNC_CONVENTIONS_DOCS_URL + " for details. To keep "
"the current behaviour and silence this warning, call "
'obsarray.set_options(unc_conventions="v0.1").'
)

# set once the warning has been issued, so it is only shown once per session
_warned = False


class UNCConventionsWarning(FutureWarning):
"""
Warning that obsarray's default UNC conventions version will change from v0.1 to draft v0.2

Issued once per session, the first time obsarray writes uncertainty metadata, unless a conventions
version has been chosen explicitly with :py:class:`~obsarray.options.set_options`. It can also be
filtered like any other warning:

.. code-block:: python

import warnings
import obsarray

warnings.filterwarnings("ignore", category=obsarray.UNCConventionsWarning)
"""


def warn_unc_conventions() -> None:
"""
Warns, once per session, that the default UNC conventions version will change - unless a version has
been chosen explicitly with :py:class:`~obsarray.options.set_options`
"""

global _warned

if _warned or OPTIONS["unc_conventions"] is not None:
return

_warned = True
warnings.warn(
_UNC_CONVENTIONS_MESSAGE, UNCConventionsWarning, stacklevel=_find_stack_level()
)


def _find_stack_level() -> int:
"""
Returns the ``stacklevel`` of the first frame outside obsarray, so warnings point at the calling code

:return: stack level to pass to :py:func:`warnings.warn` from the function calling this one
"""

pkg_dir = os.path.dirname(os.path.abspath(__file__)) + os.sep

frame = inspect.currentframe()
level = 0
while frame is not None and frame.f_code.co_filename.startswith(pkg_dir):
frame = frame.f_back
level += 1

return level


if __name__ == "__main__":
pass
78 changes: 78 additions & 0 deletions obsarray/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""options - global obsarray options"""

from typing import Any, Dict

__author__ = "Sam Hunt <sam.hunt@npl.co.uk>"
__all__ = ["set_options", "get_options"]


# None means "not chosen explicitly" - obsarray then uses its default behaviour
OPTIONS: Dict[str, Any] = {"unc_conventions": None}

_VALID_VALUES = {"unc_conventions": ("v0.1",)}


class set_options:
"""
Sets obsarray global options, either for the rest of the session or within a ``with`` block

Available options:

* ``unc_conventions`` - version of the `UNC specification
<https://comet-toolkit.github.io/unc_website/>`_ obsarray follows when writing uncertainty metadata.
Currently the only supported value is ``"v0.1"`` (obsarray's current behaviour). Setting it explicitly
also silences the :py:class:`~obsarray.conventions.UNCConventionsWarning` about the upcoming change of
default to draft v0.2.

.. code-block:: python

import obsarray

# for the rest of the session
obsarray.set_options(unc_conventions="v0.1")

# or only within a block
with obsarray.set_options(unc_conventions="v0.1"):
ds.unc["temperature"]["u_noise"] = (["x"], u_noise, {"units": "K"})

:param kwargs: option names and their new values
"""

def __init__(self, **kwargs: Any):
self._old: Dict[str, Any] = {}

for name, value in kwargs.items():
if name not in OPTIONS:
raise ValueError(
f"{name!r} is not an obsarray option, must be one of {sorted(OPTIONS)}"
)

if value not in _VALID_VALUES[name]:
raise ValueError(
f"{value!r} is not a valid value for {name!r}, must be one of "
f"{list(_VALID_VALUES[name])}"
)

self._old[name] = OPTIONS[name]

OPTIONS.update(kwargs)

def __enter__(self) -> None:
return None

def __exit__(self, *exc_info: Any) -> None:
OPTIONS.update(self._old)


def get_options() -> Dict[str, Any]:
"""
Returns a copy of the current obsarray global options

:return: option names and their values (``None`` if not set explicitly)
"""

return dict(OPTIONS)


if __name__ == "__main__":
pass
3 changes: 3 additions & 0 deletions obsarray/templater/dataset_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy
from numpy.typing import DTypeLike
from typing import Union, Optional, List, Dict, Tuple
from obsarray.conventions import warn_unc_conventions


__author__ = "Sam Hunt <sam.hunt@npl.co.uk>"
Expand Down Expand Up @@ -146,6 +147,8 @@ def create_unc_variable(
populated to the effect of this assumption.
"""

warn_unc_conventions()

# define uncertainty variable attributes, based on FIDUCEO Full FCDR definition (if required)
attributes = {} if attributes is None else attributes

Expand Down
Loading
Loading