UGRID meshes and Dask¶
UgridDataset has no native dask.array path. Unstructured meshes are not rasters: the
topology (nodes, faces, edges) and the data variables are loaded as eager NumPy arrays,
because almost every mesh operation needs the whole connectivity at once.
The way you bring Dask to mesh data is to bridge to a raster: interpolate a variable onto
a regular grid with to_dataset(variable, cell_size), which returns a standard Dataset,
and then use the raster lazy path (read_array(chunks=...), to_zarr). This notebook runs
fully offline on a small test mesh.
Setup¶
%matplotlib inline
from pathlib import Path
import dask.array as da
import numpy as np
DATA = Path('../../../../examples/data')
DATA.is_dir()
True
Open a UGRID mesh (eager)¶
from pyramids.netcdf.ugrid.dataset import UgridDataset
u = UgridDataset.read_file(DATA / 'netcdf' / 'ugrid' / 'ugrid.nc')
u.n_node, u.n_face, u.n_edge, u.data_variable_names
2026-07-11 14:41:03 | INFO | pyramids.base.config | Logging is configured.
(8916, 8355, 17270, ['mesh2d_node_z', 'mesh2d_edge_type'])
Data variables are eager NumPy¶
get_data(name) returns a MeshVariable; its .data is a plain ndarray, not a Dask
array. There is no chunks= here — this is the part that is not lazy.
var = u.get_data('mesh2d_node_z')
var.location, var.shape, type(var.data).__name__, var.has_time
('node', (8916,), 'ndarray', False)
Bridge to a raster, then go lazy¶
to_dataset interpolates the mesh variable onto a regular grid (nearest by default) and
returns a Dataset. It lives in GDAL's in-memory driver, so to read it lazily — the lazy
path reopens files by path on the workers — persist it once, then reopen. From there
everything in the Dataset quickstart applies.
import tempfile
from pyramids.dataset import Dataset
xmin, ymin, xmax, ymax = u.bounds
grid = u.to_dataset('mesh2d_node_z', cell_size=(xmax - xmin) / 40)
tif = Path(tempfile.mkdtemp(prefix='pyramids-ugrid-')) / 'node_z.tif'
grid.to_file(tif) # persist the interpolated raster
gridded = Dataset.read_file(tif) # reopen file-backed
gridded.shape, gridded.epsg, gridded.cell_size
((1, 20, 40), 4326, 1721.2824206218236)
Plot the rasterised mesh¶
gridded is a concrete, file-backed Dataset — the mesh variable interpolated onto a regular grid. Plotting it
shows what the bridge produced before we hand it to the lazy raster path.
gridded.plot(band=0, title='mesh2d_node_z gridded to a raster')
<cleopatra.array_glyph.ArrayGlyph at 0x7f3b5c36d6a0>
# Now the standard raster lazy path works: read lazily and write Zarr chunks in parallel.
lazy = gridded.read_array(chunks=(10, 20))
store = tif.with_suffix('.zarr')
gridded.to_zarr(store, chunks=(1, 10, 20), mode='w')
type(lazy).__name__, lazy.chunks, sorted(p.name for p in store.iterdir())[:4]
('Array', ((10, 10), (20, 20)), ['data', 'spatial_ref', 'x', 'y'])
Summary¶
- Mesh topology and
MeshVariable.dataare eager — there is noread_variable(chunks=...). - To parallelise with Dask, grid to a
Datasetviato_dataset(...), persist it, and use the raster lazy path (read_array(chunks=...),to_zarr,from_zarr). - See also: Dataset quickstart, the Lazy rasters tutorial, and the UGRID reference pages.