escpod index¶
Build the .p5s sidecar caches for a POD5 file: the
read index (O(log n) lookup by read ID instead of a full-table scan) and
the signal batch geometry (the per-batch row counts of the signal table).
The POD5 itself is never modified.
Both caches are rebuildable from the POD5 at any time, which is what separates
index from annotate — annotations are data
products that exist nowhere else, so writing them stays behind
--features experimental, while index ships in the default build.
Usage¶
escpod index [OPTIONS] <FILES>...
Arguments¶
| Argument | Description |
|---|---|
<FILES>... |
Input POD5 files and/or directories (searched recursively) |
Options¶
| Option | Description |
|---|---|
-f, --force |
Rebuild existing .p5s sidecars (annotations and scores are preserved) |
-t, --threads <N> |
Threads for parallel processing across files (default: 16, capped at available CPUs) |
-h, --help |
Print help |
Examples¶
escpod index input.pod5 # index one file
escpod index *.pod5 # index all POD5 files
escpod index data_dir/ # index a directory recursively
escpod index input.pod5 --force # rebuild an existing sidecar
What gets cached, and why¶
The read index¶
batch_idx / row_idx per read, sorted by read UUID. Without it, resolving a
set of read IDs means scanning the reads table; with it, each lookup is a
binary search. Loading the index from a sidecar rather than building it
measured 249 ms → 59 ms on a 33 GB file.
A locator is trusted only as far as the read it names: every indexed lookup
confirms the row it landed on actually holds the requested read_id before
using it.
The signal batch geometry¶
Row count is the one field an Arrow IPC footer does not carry — a Block
records a batch's offset, metadata length and body length and nothing else — so
recovering the counts means reading every batch's own message header. That is
one scattered touch per batch, measured at 15–24 ms each when cold on a
network filesystem, and it is paid on every process start, not once.
A 33 GB file with 8866 signal batches spent 4.95 s there, 78% of a cold 5000-read scattered fetch. With the geometry cached that becomes 4.82 ms.
The counts are measured, not assumed. Reading batch 0 and extrapolating is
what the official pod5 library and dorado do, and what escapepod's
nonuniform_signal_batch diagnostic exists to catch them out on — recording
the real counts costs about fifteen bytes (they are run-length encoded, so a
conformant file is one run and a short tail) and is exact for a non-uniform
file too.
When to run it¶
Once, on a networked node, before submitting jobs against a large file —
the same reason escpod resquiggle models fetch
exists. Every sidecar write records the geometry when it is not already there
(escpod index, escpod annotate, demux --annotate alike), so a sidecar
cannot end up carrying a read index and nothing else.
A sidecar that already holds both caches is skipped. One missing either — for
example written by an older demux --annotate, which recorded the index but
not the geometry — is completed in place, preserving annotations and scores.
--force re-measures even when a value is present, because rebuilding the
cache is the job this command exists to do.
Notes¶
- The sidecar is bound to its POD5 by file identifier and size, so a stale or misplaced one fails loudly rather than describing the wrong reads.
- A cached geometry is verified before use (one entry per batch the footer describes; first and last batches read for real and compared). Any mismatch logs a warning and falls back to the full walk — a stale cache costs time, never correctness.
- This format replaces the earlier
.p5iindex sidecar; delete any.p5ifiles and rerunescpod index. - Format details: The
.p5sSidecar.