Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
72606f7
Integration of ECMWF cfgrib driver to read GRIB files into xarray.
alexamici Oct 9, 2018
71fcbe7
Remove all coordinate renaming from the cfgrib backend.
alexamici Oct 9, 2018
6faa7b9
Move flavour selection to `cfgrib.Dataset.from_path`.
alexamici Oct 9, 2018
1469a0e
Sync xarray backend import style with xarray.
alexamici Oct 9, 2018
12811e8
Make use of the new xarray.backends.FileCachingManager.
alexamici Oct 9, 2018
a4409b6
Add just-in-case locking for ecCodes.
alexamici Oct 9, 2018
80b8788
Explicitly assign attributes to CfGribArrayWrapper
alexamici Oct 10, 2018
9dfd660
Add missing locking in CfGribArrayWrapper and use explicit_indexing_a…
alexamici Oct 10, 2018
edc4e85
Add a comment about the ugly work-around needed for filter_by_keys.
alexamici Oct 10, 2018
9b5335a
Declare correct indexing support.
alexamici Oct 10, 2018
186a504
Merge branch 'upstream' into feature/grib-support-via-cfgrib
alexamici Oct 14, 2018
485a409
Add TestCfGrib test class.
alexamici Oct 14, 2018
81f18c2
cfgrib doesn't store a file reference so no need for CachingFileManager.
alexamici Oct 14, 2018
5dedb3f
Add cfgrib testing to Travis-CI.
alexamici Oct 14, 2018
831ae4f
Naming.
alexamici Oct 14, 2018
6372e6e
Fix line lengths and get to 100% coverage.
alexamici Oct 14, 2018
8e9b2e3
Add reference to *cfgrib* engine in inline docs.
alexamici Oct 14, 2018
07b9469
First cut of the documentation.
alexamici Oct 14, 2018
340720a
Tentative test cfgrib under dask.distributed.
alexamici Oct 14, 2018
4d84f70
Better integration test.
alexamici Oct 14, 2018
0b027db
Remove explicit copyright and license boilerplate to harmonise with o…
alexamici Oct 15, 2018
a4ead54
Add a usage example.
alexamici Oct 15, 2018
ec80d86
Fix code style.
alexamici Oct 15, 2018
f30b7d0
Fix doc style.
alexamici Oct 16, 2018
223d25c
Fix docs testing. The example.grib file is not accessible.
alexamici Oct 17, 2018
2ef993f
Merge remote-tracking branch 'upstream/master' into feature/grib-supp…
alexamici Oct 17, 2018
bbf01e3
Fix merge in docs.
alexamici Oct 17, 2018
da2b9dd
Fix merge in docs.
alexamici Oct 17, 2018
eda96a4
Fix doc style.
alexamici Oct 17, 2018
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
2 changes: 1 addition & 1 deletion xarray/backends/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def maybe_decode_store(store, lock=False):
store = backends.PseudoNetCDFDataStore.open(
filename_or_obj, lock=lock, **backend_kwargs)
elif engine == 'cfgrib':
store = backends.CfGribDataStore.from_path(
store = backends.CfGribDataStore(
filename_or_obj, lock=lock, **backend_kwargs)
else:
raise ValueError('unrecognized engine for open_dataset: %r'
Expand Down
32 changes: 21 additions & 11 deletions xarray/backends/cfgrib_.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
from ..core import indexing
from ..core.utils import Frozen, FrozenOrderedDict
from .common import AbstractDataStore, BackendArray
from .file_manager import CachingFileManager
from .locks import ensure_lock, SerializableLock

# FIXME: Add a dedicated lock just in case, even if ecCodes is supposed to be thread-safe in most
# circumstances. See: https://confluence.ecmwf.int/display/ECC/Frequently+Asked+Questions
ECCODES_LOCK = SerializableLock()


class CfGribArrayWrapper(BackendArray):
Expand All @@ -50,28 +56,29 @@ class CfGribDataStore(AbstractDataStore):
"""
Implements the ``xr.AbstractDataStore`` read-only API for a GRIB file.
"""
def __init__(self, ds, lock=False):
self.ds = ds
self.lock = lock

@classmethod
def from_path(cls, path, lock=False, **backend_kwargs):
def __init__(self, filename, lock=None, **backend_kwargs):
import cfgrib
return cls(ds=cfgrib.open_file(path, **backend_kwargs), lock=lock)
if lock is None:
lock = ECCODES_LOCK
self.lock = ensure_lock(lock)
backend_kwargs['filter_by_keys'] = tuple(backend_kwargs.get('filter_by_keys', {}).items())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a little surprising to me -- can we simply pass on backend_kwargs directly to cfgrib?

Copy link
Collaborator Author

@alexamici alexamici Oct 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a ugly hack around the fact that filter_by_keys is a dict but CachingFileManager accepts only hashable backend_kwargs because they are passed to _HashedSequence.

filter_by_keys has a very nice dict semantics, so I'd prefer not to change it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that makes please. I think the need to hash arguments used to open files with CachingFileManager is unavoidable, so this is a reasonable workaround. But please add a comment explaining this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I added a comment and made the code more explicit.

self._manager = CachingFileManager(
cfgrib.open_file, filename, lock=lock, mode='r', kwargs=backend_kwargs)

@property
def ds(self):
return self._manager.acquire()

def open_store_variable(self, name, var):
if isinstance(var.data, np.ndarray):
data = var.data
else:
data = indexing.LazilyOuterIndexedArray(CfGribArrayWrapper(var.data))

dimensions = var.dimensions
attrs = var.attributes

encoding = self.ds.encoding.copy()
encoding['original_shape'] = var.data.shape

return Variable(dimensions, data, attrs, encoding)
return Variable(var.dimensions, data, var.attributes, encoding)

def get_variables(self):
return FrozenOrderedDict((k, self.open_store_variable(k, v))
Expand All @@ -87,3 +94,6 @@ def get_encoding(self):
encoding = {}
encoding['unlimited_dims'] = {k for k, v in self.ds.dimensions.items() if v is None}
return encoding

def close(self):
self._manager.close()