Skip to content

Commit 6d58963

Browse files
authored
Merge pull request #340 from boutproject/lazy-load-collect
Accelerate collect() by switching to lazy loading
2 parents 793545b + 10f42a4 commit 6d58963

1 file changed

Lines changed: 53 additions & 18 deletions

File tree

xbout/load.py

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -542,24 +542,58 @@ def collect(
542542
ds : numpy.ndarray
543543
544544
"""
545-
from os.path import join
545+
from pathlib import Path as _Path
546546

547-
datapath = join(path, prefix + "*.nc")
547+
datapath_glob = str(_Path(path) / (prefix + "*.nc"))
548548

549-
ds, _ = _auto_open_mfboutdataset(
550-
datapath, keep_xboundaries=xguards, keep_yboundaries=yguards, info=info
551-
)
549+
# Fast path: use lazy loader which only opens one file for metadata.
550+
# Falls back to open_mfdataset if the directory cannot be detected or
551+
# the variable is not supported by the lazy loader.
552+
try:
553+
path_obj = _Path(path)
554+
if path_obj.is_dir():
555+
ds = lazyload.lazy_open_boutdataset(
556+
path,
557+
keep_xboundaries=xguards,
558+
keep_yboundaries=yguards,
559+
info=info,
560+
prefix=prefix,
561+
)
562+
else:
563+
raise ValueError("path is not a directory")
564+
565+
if varname not in ds:
566+
raise KeyError(
567+
"No variable, {} was found in {}.".format(varname, datapath_glob)
568+
)
569+
570+
da = ds[varname]
571+
dims = list(da.dims)
552572

553-
if varname not in ds:
554-
raise KeyError("No variable, {} was found in {}.".format(varname, datapath))
573+
except Exception:
574+
# Fall back to the slow multi-file open
575+
ds, _ = _auto_open_mfboutdataset(
576+
datapath_glob,
577+
keep_xboundaries=xguards,
578+
keep_yboundaries=yguards,
579+
info=info,
580+
)
555581

556-
dims = list(ds.dims)
557-
inds = [tind, xind, yind, zind]
582+
if varname not in ds:
583+
raise KeyError(
584+
"No variable, {} was found in {}.".format(varname, datapath_glob)
585+
)
586+
587+
da = ds[varname]
588+
dims = list(ds.dims)
589+
590+
inds = {"t": tind, "x": xind, "y": yind, "z": zind}
558591

559592
selection = {}
560593

561594
# Convert indexing values to an isel suitable format
562-
for dim, ind in zip(dims, inds):
595+
for dim in dims:
596+
ind = inds.get(dim)
563597
if isinstance(ind, int):
564598
indexer = [ind]
565599
elif isinstance(ind, list):
@@ -570,25 +604,26 @@ def collect(
570604
else:
571605
indexer = None
572606

573-
if indexer:
607+
if indexer is not None:
574608
selection[dim] = indexer
575609

576610
try:
577-
version = ds["BOUT_VERSION"]
578-
except KeyError:
579-
# If BOUT Version is not saved in the dataset
611+
version = ds.attrs.get("metadata", {}).get("BOUT_VERSION", 0)
612+
if version == 0 and "BOUT_VERSION" in ds:
613+
version = float(ds["BOUT_VERSION"].values)
614+
except Exception:
580615
version = 0
581616

582617
# Subtraction of z-dimensional data occurs in boutdata.collect
583618
# if BOUT++ version is old - same feature added here
584619
if (version < 3.5) and ("z" in dims):
585-
zsize = int(ds["nz"]) - 1
586-
ds = ds.isel(z=slice(zsize))
620+
zsize = int(ds.attrs.get("metadata", {}).get("nz", da.sizes["z"]))
621+
da = da.isel(z=slice(zsize))
587622

588623
if selection:
589-
ds = ds.isel(selection)
624+
da = da.isel(selection)
590625

591-
result = ds[varname].values
626+
result = da.values
592627

593628
# Close netCDF files to ensure they are not locked if collect is called again
594629
ds.close()

0 commit comments

Comments
 (0)