Particle Cache Policy¶
1. Purpose and Scope¶
This document defines the policy for the downscaled particle disk cache
(cache=yes, cache_dir=<dir>), implemented in
src/main/strategies/search/simple_ptcl_cache.f90 and consumed by the 2D
matcher workflows only. The 3D workflows (refine3D family, abinitio3D,
probabilistic 3D preparation, matcher reconstruction) do not consume the cache
and reject cache=yes with a hard error; caching never worked reliably for 3D
and was removed from those paths.
The samplers draw a fresh ~nsample subset of the particles every iteration and
each selected particle is read at full box and Fourier-cropped to box_crop.
In probabilistic mode the same particle is read up to three times per
iteration: probabilistic scoring, search, and reconstruction. The cache writes
the iteration-independent prefix of that preprocessing to disk once and serves
all subsequent reads from it, trading disk space at cache_dir for a roughly
(box/box_crop)^2 reduction in read volume.
Because the samplers sweep near-disjoint subsets before revisiting anything, a partial cache would never hit; the cache always covers all active particles.
2. What Is Cached¶
An entry is the noise-normalized (against the full-box lmsk),
Fourier-cropped particle, stored as a real-space box_crop image. Fourier
crop -> inverse FFT -> forward FFT is an exact round trip, so reading an entry
back reproduces the same coefficients prepimg4align would have computed.
The shift is not cacheable (re-read from the project every iteration) and the
CTF is deliberately left out so the cache does not depend on CTF parameters.
Cache files (stack, index, key) live in cache_dir (else
SIMPLE_PTCL_CACHE_DIR, else the execution directory). The basename carries
the project name, box_crop, and a hash of the absolute execution directory,
so concurrent runs sharing a fast disk cannot collide and every distributed
rank recomputes the same name locally (qsys workers cd into the master's
directory).
3. Consumers¶
- 2D alignment (
prob_tab2D,cluster2D_execsearch): exact substitution. - 2D class-average restoration (
cavger_update_sumswithcropped_ptcls=.true.): deliberate numerics change — edge taper and gridding source grid live atbox_crop, no second noise normalization, shift and CTF pixel size scaled to the cropped grid. Not consumers, by design: every 3D workflow (alignment, probabilistic preparation, and reconstruction all read the full-size originals; therefine3Dexecution strategies throw oncache=yes), the one-off starting-volumereconstruct3D,volassemble(no particle reads), streaming 2D variants (long-lived processes over changing particle sets would thrash the validity fingerprint), and the flex / offload reconstruction paths.
4. Validity Contract¶
The key file is the commit record; no reader accepts cache files without a matching key. It records:
- the geometry line:
box,box_crop,smpd,smpd_crop,msk,oritype, particle and stack counts, and the execution directory verbatim (so a name-hash collision from a different directory can never validate); - the source fingerprint: every stack in project order with its particle
range, physical size and mtime, plus every particle's
stkind/indstkmapping (the range-derived fallback is covered by the per-stackfromp/top). A reordered or remapped project over unchanged stacks therefore cannot validate a stale cache.
The check is conservative: a touched file forces a rebuild. Only the rank that decides whether to rebuild pays the full fingerprint; consumers check the geometry line and inherit the verdict.
5. Lifecycle and Ownership¶
The process that builds — or adopts a valid leftover, e.g. after a killed
predecessor in the same execution directory — owns the cache files and
removes them on normal exit and on hard exception, via the
cache_cleanup_glob hook in simple_defs (called from simple_exception
and the tails of simple_exec/single_exec). Workers never take ownership,
so a dying worker cannot delete the cache under the other ranks or a
resubmitted part. Deletion is key-file-first, so a partially completed
cleanup can never leave a cache that still validates.
Cache-enabled abinitio3D uses the final active downscaling-ladder
box_crop for every refine3D stage. The stable key lets the owner fast path
reuse one cache throughout all eligible stages. Stage low-pass limits still
follow the ladder, while cache=no retains the stage-specific crop schedule.
The user's cache=yes request is re-stamped onto every stage command line, so
a stage-local fallback does not permanently disable later stages. A stage that
declines the cache releases its files; a later eligible stage may rebuild the
same final-crop cache.
Per-iteration prob_align calls hit an ownership fast path in
ptcl_cache_ensure (same owned key name, key file exists) and skip the full
revalidation.
6. Space Budget and Uniform Fallback¶
Before building, the exact predicted size (nsel * box_crop^2 * 4 bytes plus
headers) is checked against the free space at the destination (statvfs); the
cache may claim at most 25% of it. Over budget — or with no active
particles, a denoised primary source, or a non-particle oritype — the whole
run falls back to uncached execution uniformly: disable_cache flips
cache=no on both params and the command line before any worker command
line is generated.
Uniformity is mandatory, not best-effort: restoring class averages or
reconstructing from cropped particles is deliberately not the same
preprocessing as from full-size ones, so ranks must never mix modes.
ptcl_cache_assert_ready hard-stops a worker that expected a cache and
cannot find one (the classic case: node-local cache_dir not visible to
every rank).
7. Eligibility¶
The cache is refused, uniformly and at every decision level (in_use,
assert_ready, ensure), when:
box_crop >= box(nothing to gain);- the primary particle source is the denoised stack (
ptcl_src=den) — the entries derive from the raw stacks and would be the wrong pixels. A denoised objective (objfun_den=yes) is eligible: those images are read separately into a matcher-lifetime full-size workspace; oritypeis notptcl2D/ptcl3D— cls3D "particles" are class averages inos_out, which the stack fingerprint cannot see.
8. Known Limitations¶
- Signals and Fortran runtime errors bypass the cleanup hook. SIGKILL,
scancel, node failure, or a runtime abort leaves the files behind. Correctness is preserved (a rerun validates or rebuilds), and a rerun in the same execution directory reclaims the orphan, but a run in a fresh directory strands the old files incache_diruntil manually removed. - Fixed-crop cost in cache-enabled
abinitio3D. Early stages use the final ladder crop instead of their smaller stage-local crops. This increases early-stage cache size and matching work, but avoids a sequential full-size cache rewrite at every crop transition. Uncached runs keep stage-local crops. - Node-local
cache_diron multi-node jobs. The master builds on its own node; workers on other nodes then hard-stop with the uniformity error. Intentional, but there is no replicate-to-node-local mechanism. - Mid-run activation of previously inactive particles (after the ownership fast path has skipped revalidation) fails loudly at read time rather than being served silently; this does not occur in the current workflows.
- The cached reconstruction and restoration paths are accepted on statistical parity (FSC trajectory, final maps) with uncached execution, not bit equality; the alignment paths are bit-exact.
9. Future Extensions¶
- Orphan scavenger: sweep stale
ptcl_cache_*file sets incache_diratptcl_cache_ensuretime (age- or dead-key-based) to reclaim signal-killed leftovers automatically. - Benefit predicate:
ensureknowsnsel; with the planned iterations, sampling schedule, and fixed crop ratio it can compute predicted bytes saved versus the one-time build and larger early-stage matching cost, then decline uneconomical caching through the existing uniform fallback. - Stage-local reads from the final-crop cache: Fourier cropping is nested (normalization precedes any crop), so readers could truncate final-crop records to each stage's planned crop. This would preserve one cache build while recovering the smaller early-stage matching grids.
- UI promotion:
cache/cache_dirareUI_VIS_DEVELOPER; promote after validation, and consider defaultingcache=yesforabinitio3D. - Denoised-source entries: per-source cache entries would lift the
ptcl_src=denexclusion if that path becomes I/O-bound in practice.