tracmass.LazyFluxFields#
- class tracmass.LazyFluxFields(_past, _present, dtm=None, fluxes_fn=None, zarr_chunk_xy=(1, 1), timestep=None, reverse=False, dtm_end=None, _computed=<factory>)[source]#
Bases:
objectTwo time-slots of flux data held as raw xarray DataArrays.
The DataArrays may be backed by Dask (for GLORYS from Zarr) or by in-memory arrays (for the theoretical adapter). In either case, no
numpyor device memory is allocated untilcompute()is called.This object is not a JAX pytree. Pass the result of
compute()to the advection kernel instead.The two time slots are stored as separate DataArrays without a trailing time dimension. The shape of each DataArray matches the spatial shape of the variable (e.g.
(imt, jmt, km)foruflux).update_flux()advances the window by reassigning Python dict references — no Dask compute, no JAX, no GC.- Parameters:
- dtm#
Timestamp of the ‘past’ (slot 0) time level. Set by
new_flux()andupdate_flux();Nonewhen constructed via lower-level helpers likeswap_fields().- Type:
pd.TimestamporNone
- fluxes_fn#
The GCM adapter callable used to create this object, e.g.
gcm.glorys.fluxes. Stored bynew_flux()and propagated byupdate_flux()so thattime_dependent()can infer it automatically without the caller re-supplying it.- Type:
callableorNone
- timestep#
GCM output interval (always positive), copied from
grid.timestepbynew_flux().update_flux()stepsdtmby this amount.- Type:
pd.TimedeltaorNone
- reverse#
Truefor a backward run (copied fromgrid.reverse):update_flux()then walksdtmbackward and negates the velocity fluxes it loads.- Type:
- dtm_end#
The dtm_end passed to
new_flux(), orNone. Used byn_windowsto size a run.- Type:
pd.TimestamporNone
Notes
compute()keeps the present slot’s arrays (already read and cleaned) in_computed, keyed by the slot’s DataArray. Afterupdate_flux()that DataArray is the new past slot, so the nextcompute()reuses the arrays instead of reading the same snapshot again: every snapshot is read once, not twice._computedis shared by the copiesupdate_flux()makes and holds one slot per variable.- __init__(_past, _present, dtm=None, fluxes_fn=None, zarr_chunk_xy=(1, 1), timestep=None, reverse=False, dtm_end=None, _computed=<factory>)#
Methods
__init__(_past, _present[, dtm, fluxes_fn, ...])compute([include_wflux])Trigger Dask computation and transfer flux data to JAX device memory.
compute_patch(i0, i1, j0, j1, grid[, ...])Load only the sub-patch
[i0:i1, j0:j1]into device memory.preload([include_wflux])Return a copy with all Dask arrays preloaded to numpy.
Attributes
Number of GCM windows left between
dtmanddtm_end.zarr_chunk_xy- property n_windows: int | None#
Number of GCM windows left between
dtmanddtm_end.ceil(|dtm_end - dtm| / timestep), so a dtm_end that falls inside a window still has that window integrated. Decreases by one with everyupdate_flux()call.Nonewhendtm_end(ordtm/timestep) is not set.
- compute_patch(i0, i1, j0, j1, grid, include_wflux=True)[source]#
Load only the sub-patch
[i0:i1, j0:j1]into device memory.Slices the lazy xarray DataArrays before calling
.values, so Dask only computes (and reads from Zarr) the chunks that intersect the requested region.kmtanddxdyare sliced from grid and included in the returnedLocalFluxFieldsas dynamic JAX arrays, so the advection kernel can be compiled once for a fixed patch shape and reused without recompilation as patch content changes.- Parameters:
i0 (
int) – Zonal slice bounds (0-based, exclusive end).i1 (
int) – Zonal slice bounds (0-based, exclusive end).j0 (
int) – Meridional slice bounds (0-based, exclusive end).j1 (
int) – Meridional slice bounds (0-based, exclusive end).grid (
GridFields) – Full-grid static geometry;kmtanddxdyare sliced from here to match the patch.include_wflux (
bool, optional) – False skips readingwflux(returned asNone), for runs without vertical motion. Default True.
- Returns:
LocalFluxFields- Return type:
- preload(include_wflux=True)[source]#
Return a copy with all Dask arrays preloaded to numpy.
Issues a single
dask.compute()call for every variable in_pastand_present, replacing Dask-backed DataArrays with in-memory numpy-backed DataArrays. The returned object is otherwise identical and can be used anywhere aLazyFluxFieldsis expected.Calling this once per GCM window in
time_dependent_streaming()changes the I/O pattern from O(n_sub) small Dask computes (one per sub-cycle that has cell crossings) to O(1) per window. Subsequentextract_stencil()calls on the returned object use pure numpy fancy indexing — no further Zarr I/O regardless of how many sub-cycles or cell crossings occur.- Parameters:
include_wflux (
bool, optional) – False leaveswfluxout of the copy instead of reading it, for runs without vertical motion. Default True.- Returns:
LazyFluxFields– A copy with numpy-backed DataArrays and the same metadata (dtm,fluxes_fn,timestep,reverse, …).- Return type:
- compute(include_wflux=True)[source]#
Trigger Dask computation and transfer flux data to JAX device memory.
Variables are loaded one at a time (past then present) and stacked along a new trailing axis, so the peak extra allocation per variable is one numpy array of size
t0, one of sizet1, and the resulting JAX array of sizet0 + t1.NaN values in flux arrays (
uflux,vflux,wflux) are replaced with 0.0 before stacking. Land cells in GLORYS/NEMO are masked as NaN in the Zarr files; the physical no-flow boundary condition is 0.0, so NaN → 0 is the correct substitution. Without this replacement, NaN fluxes propagate through_calc_pos_1dto produce a NaN particle position, which afterastype(int32)conversion gives a garbage cell index that may point to a kmt=0 (land) cell, causing spuriousREASON_LANDdeactivations.- Parameters:
include_wflux (
bool, optional) – False skips readingwflux(returned asNone), for runs without vertical motion. Default True.- Returns:
FluxFields– Kernel-ready flux data with trailing slot dimension of size 2.- Return type: