PIVPy tutorial¶
This tutorial is a practical, code-first walkthrough of common PIVPy workflows. It focuses on the xarray-based API and several PIVMAT-inspired helpers.
Prerequisites¶
Install pivpy¶
Recommended (fast, reproducible):
uv venv
uv pip install pivpy
Or with optional dependencies:
uv pip install 'pivpy[full]'
Load a dataset¶
PIVPy’s main object is an xarray.Dataset. Most post-processing is exposed via
the accessor ds.piv.
import pivpy.pivpy # registers the .piv accessor
from pivpy import io
ds = io.read_piv('your_file.vec')
Typical dataset structure:
Dims:
('y', 'x', 't')Variables:
u,v, and (often)chc
Create a scalar dataset from an image (PIVMAT-style im2pivmat)¶
If you have a 2D image array (shape (y, x)), you can wrap it into a PIVPy
scalar Dataset using pivpy.io.im2pivmat():
import numpy as np
from pivpy import io
import pivpy.pivpy # registers the .piv accessor
im = np.random.rand(128, 256)
ds_im = io.im2pivmat(im, namew='I', unit='au')
# Plot the scalar field
ds_im.piv.showscal(property='w')
Save animations (PIVMAT-style showf / imvectomovie)¶
PIVPy can render a Dataset as a movie efficiently by creating the Matplotlib artists once and updating them frame-by-frame (fast renderer).
In-memory Dataset (uses the t dimension as frames):
import pivpy.pivpy # registers the .piv accessor
from pivpy import io
ds = io.create_sample_Dataset(n_frames=20)
# Write a movie (requires FFmpeg for mp4/avi)
ds.piv.to_movie('movie.mp4', fps=10, show='vector', nthArr=2, scalingFactor=10.0)
# Or collect frames as RGBA arrays (no external encoders required)
frames = ds.piv.to_movie(None, return_frames=True, show='vector')
Streaming from disk (loads files one-by-one, PIVMAT imvectomovie-style):
from pivpy import graphics
# Convert many files to a movie without loading all of them into RAM
graphics.imvectomovie('data/*.vc7', 'movie.mp4', fps=10, show='vector', nthArr=3)
Notes:
Writing
.mp4/.aviuses Matplotlib’s FFmpeg writer (FFmpeg must be installed).Writing
.gifuses Matplotlib’s Pillow writer (Pillow must be installed).Use
show='scalar'andscalar='w'to render scalar-only datasets.
Interpolate missing data (PIVMAT-style interpf)¶
PIVMAT’s interpf fills missing values (0 or NaN) inside fields. In PIVPy
this is available as:
import pivpy.pivpy # registers the .piv accessor
from pivpy import io
ds = io.create_sample_Dataset(n_frames=5)
ds_filled = ds.piv.interpf(method=0) # Laplacian inpainting (smooth)
# Faster options:
ds_nn = ds.piv.interpf(method=1) # nearest-neighbor
ds_lin = ds.piv.interpf(method=2) # linear + nearest fallback
# Control what is considered "missing":
ds_nan_only = ds.piv.interpf(method=1, missing='nan')
ds_zero_only = ds.piv.interpf(method=1, missing='0')
Joint PDF of two scalar fields (PIVMAT-style jpdfscal)¶
You can compute a joint histogram (“joint PDF” in PIVMAT terminology) of two scalar variables and display it with filled contours:
import pivpy.pivpy # registers the .piv accessor
from pivpy import io
ds = io.create_sample_Dataset(n_frames=10)
ds = ds.piv.vec2scal('vorticity', name='w')
ds = ds.piv.vec2scal('divergence', name='div')
jpdf = ds.piv.jpdfscal('w', 'div', nbin=101)
fig, ax = ds.piv.jpdfscal_disp('w', 'div', nbin=101)
Probe time series (PIVMAT-style probef and probeaverf)¶
Sample the time evolution at a point (with bilinear interpolation):
p = ds.piv.probef(20.0, 30.0, variables=['u', 'v'])
# p['u'] and p['v'] are 1D time series over the dataset's t dimension
Average over a rectangular probe area [x1, y1, x2, y2]:
pavg = ds.piv.probeaverf([10.0, 10.0, 30.0, 40.0], variables=['u', 'v'])
Spatio-temporal diagram (PIVMAT-style spatiotempf)¶
Sample a scalar field along a line segment for each time frame:
ds = ds.piv.vec2scal('vorticity', name='w')
st = ds.piv.spatiotempf([0.0, 50.0], [10.0, 10.0], var='w', n=200)
# st['st'] has dims (t, s) and can be plotted with imshow/pcolormesh.
Temporal correlation (PIVMAT-style tempcorrf)¶
Compute the temporal correlation function $f(T)$ of a scalar (or vector) time series averaged over space:
cor = ds.piv.tempcorrf(variables=['w'], normalize=True)
# cor['f'] is the correlation vs lag cor['t']
Gradient of a scalar field (PIVMAT-style gradientf)¶
Compute the gradient of any scalar variable (for example a vorticity field
stored in ds['w']). The result is a new Dataset with vector components
stored as u and v:
import pivpy.pivpy # registers the .piv accessor
from pivpy import io
ds = io.create_sample_Dataset(n_frames=1)
ds = ds.piv.vec2scal('vorticity', name='w')
grad = ds.piv.gradientf(variable='w')
# grad contains grad['u'] = dw/dx and grad['v'] = dw/dy
You can plot it like any other vector field:
grad.piv.showf()
Histogram of a field (PIVMAT-style histf)¶
Compute a histogram for a scalar field (values are stacked over the full field and all time frames):
h = ds.piv.histf(variable='w')
# h is a Dataset with coordinate h['bin'] and counts h['h']
By default, zeros are treated as invalid and excluded. To include zeros, pass
opt='0':
h0 = ds.piv.histf(variable='w', opt='0')
Vector mode (no variable specified) computes histograms for both components of the velocity field:
hv = ds.piv.histf()
# hv contains hv['hx'] for u (or vx) and hv['hy'] for v (or vy)
Graphical histogram display (PIVMAT-style)¶
PIVPy also includes plotting helpers inspired by PIVMAT’s histscal_disp and
histvec_disp.
Scalar histogram display (with optional PDF normalization):
from pivpy import graphics
fig, ax = graphics.histscal_disp(ds, variable='w', opt='n')
Vector histogram display:
fig, ax = graphics.histvec_disp(ds, opt='n')
Extract a rectangular region (PIVMAT-style extractf)¶
Extract a rectangle defined in physical units:
sub = ds.piv.extractf([0.0, 0.0, 10.0, 5.0], 'phys') # [x1, y1, x2, y2]
Or extract using mesh indices (MATLAB-like 1-based, inclusive):
sub = ds.piv.extractf([10, 5, 50, 40], 'mesh')
Optionally retrieve the effective mesh rectangle after clamping:
sub, mesh_rect = ds.piv.extractf([0.0, 0.0, 10.0, 5.0], 'phys', return_rect=True)
Spatial correlation (PIVMAT-style corrm and corrf)¶
Matrix correlation along one direction¶
Compute a PIVMAT-like correlation map (returns a DataArray with a lag dimension):
cu = ds.piv.corrm(variable='u', dim='x')
cv = ds.piv.corrm(variable='v', dim='y', half=True)
Spatial correlation function and integral scales¶
Compute the 1D correlation function f(r) and integral scales:
cor = ds.piv.corrf(variable='u', dim='x', normalize=True)
The result is an xarray.Dataset with:
cor['r']: separation lengthcor['f']: correlation functionscalar diagnostics:
isinf,r5/is5,r2/is2,r1/is1,r0/is0
Butterworth filtering (PIVMAT-style bwfilterf)¶
Apply a low-pass Butterworth filter in Fourier space:
ds_low = ds.piv.bwfilterf(filtsize=3.0, order=8.0, mode='low', trunc=True)
Apply a high-pass filter:
ds_high = ds.piv.bwfilterf(filtsize=3.0, order=8.0, mode='high')
PIVMAT-compatible option wrapper¶
If you prefer PIVMAT-like option strings:
ds_high2 = ds.piv.bwfilterf_pm(3.0, 8.0, 'high', 'trunc')
Spatial convolution filtering (PIVMAT-style filterf)¶
Apply a Gaussian convolution filter (NaN-aware). Use 'same' to keep the same size:
ds_smooth = ds.piv.filterf(1.0, 'gauss', 'same')
Or omit 'same' to get Matlab conv2(...,'valid') behavior (smaller field):
ds_smooth_valid = ds.piv.filterf(1.0, 'gauss')
Batch processing over filename series (PIVMAT-style batchf)¶
batchf expands bracket patterns (a safe subset of PIVMAT’s expandstr)
and processes files one-by-one.
from pivpy.io import batchf
# Apply an accessor method by name
results = batchf(
'pivpy/data/day2/day2a00500[0:5].T000.D000.P003.H001.L.vec',
'averf',
)
You can also pass a callable:
def mean_speed(ds):
return ((ds['u'] ** 2 + ds['v'] ** 2) ** 0.5).mean().item()
speeds = batchf('pivpy/data/day2/day2a00500[0:5].*.vec', mean_speed)
Check for updates on PyPI¶
PIVPy can compare the installed version against the latest PyPI version:
import pivpy
res = pivpy.check_update(verbose=True)
# res.status:
# 0 = server unavailable
# 1 = up-to-date
# 2 = update available
# 3 = installed version newer than PyPI
Notes on running headless¶
If you run tests or scripts on a machine without a display, set a non-interactive Matplotlib backend:
MPLBACKEND=Agg uv run pytest -q