
ECMWF’s MARS and Metview software introduced the notion of *Fieldset* which is an
ordered collection of GRIB message. The *Fieldset* is an abstract concept and can be
implemented in many ways. In the case of MARS and Metview, a *Fieldset* is an implemented
internally as an array of *Field*, each *Field* being represented by a file path, an offset and a
length where the actual GRIB message can be found. Thus, a *Fieldset* can represent an
ordered collection of *Field* which are at various locations of several files.

*cfgrib* now provides the definition of a ``Field`` and ``Fieldset`` types in the ``cfgrib.abc`` module
and additionally a ``MappingFieldset`` for specialised use.
The implementations are based on simple python sequences and mappings so that *cfgrib*
can build a Dataset for example from something as simple as a list of dicts.

Classes that implement the ``Fieldset`` and the ``MappingFieldset`` interface
can use the low-level interface ``cfgrib.open_fielset`` to obtain a ``cfgrib.Dataset``
or they can be passed directly to *Xarray*.

The simplest *Fieldset* is a list of dictionaries:

.. code-block: python

>>> import xarray as xr
>>> fieldset = [
...     {
...         "gridType": "regular_ll",
...         "Nx": 2,
...         "Ny": 3,
...         "distinctLatitudes": [-10.0, 0.0, 10.0],
...         "distinctLongitudes": [0.0, 10.0],
...         "paramId": 130,
...         "shortName": "t",
...         "values": [[1, 2], [3, 4], [5, 6]],
...         "dataDate": 20211216,
...         "dataTime": 1200,
...     }
... ]
>>> ds = xr.open_dataset(fieldset, engine="cfgrib")
>>> ds
<xarray.Dataset>
Dimensions:    (latitude: 3, longitude: 2)
Coordinates:
    time       datetime64[ns] ...
  * latitude   (latitude) float64 -10.0 0.0 10.0
  * longitude  (longitude) float64 0.0 10.0
Data variables:
    t          (latitude, longitude) float32 ...
Attributes:
    Conventions:  CF-1.7
    history:      ...
>>> ds.mean()
<xarray.Dataset>
Dimensions:  ()
Coordinates:
    time     datetime64[ns] ...
Data variables:
    t        float32 3.5


For example you can implement a dedicated ``Fieldset`` class following this pattern:

.. code-block:: python

    from typing import Iterator

    from cfgrib import abc

    class MyFieldset(abc.Fieldset):
        def __len__(self) -> int:  # not used by cfgrib
            ...
        def __getitem__(self, item: int) -> abc.Field:
            ...
        def __iter__(self) -> Iterator[abc.Field]:
            ...


If ``__getitem__`` and ``__iter__`` implement lazy loading of GRIB fields *cfgrib* and
*Xarray* will be able to access larger-than-memory files.

In the event a ``Field`` is identified by a more complex *key* than just an sequence *index*
developers may implemnt a ``MappingFieldset`` class following this pattern:

.. code-block:: python

    from typing import ItemsView, Iterator

    from cfgrib import abc

    class MyFieldset(abc.MappingFieldset[T.Any, abc.Field]):
        def __len__(self) -> int:  # not used by cfgrib
            ...
        def __getitem__(self, item: int) -> abc.Field:
            ...
        def __iter__(self) -> Iterator[abc.Field]:  # not used by cfgrib
            ...
        def items() -> ItemsView[T.Any, abc.Field]:
            ...


Again if ``__getitem__`` and ``items`` implement lazy loading of GRIB fields *cfgrib* and
*Xarray* will be able to access larger-than-memory files.

An example of the ``MappingFieldset`` use is ``cfgrib.messages.FileStream`` that
uses the *file offset* as the *key*.
