diff --git a/crates/fgumi-metrics/src/dedup.rs b/crates/fgumi-metrics/src/dedup.rs index 3b52d4448..853dd3715 100644 --- a/crates/fgumi-metrics/src/dedup.rs +++ b/crates/fgumi-metrics/src/dedup.rs @@ -353,9 +353,14 @@ impl crate::ProcessingMetrics for DeduplicationMetrics { } /// Serializable row for the `--duplication-ladder` sampled duplication -/// saturation curve: "after `templates_seen` templates processed (in -/// coordinate order) for this library, what cumulative fraction were -/// duplicates". +/// ladder: "after `templates_seen` templates processed (in coordinate order) +/// for this library, what cumulative fraction were duplicates". +/// +/// This is not a saturation curve. A template's duplicate status is decided +/// by the other templates at its own position, so the fraction after N +/// templates is the duplicate rate of the genome covered so far, not the rate +/// a library sequenced to N templates would show. Changes along the ladder +/// reflect regions with different duplicate rates. /// /// One row per (library, snapshot) — a library gets a row each time its /// cumulative `templates_seen` crosses a multiple of `--ladder-interval`, @@ -376,10 +381,9 @@ pub struct DuplicationLadderMetrics { /// `templates_seen` on the first snapshot. pub window_templates: u64, /// Marginal duplicate fraction over just this window's templates - /// (`window_duplicate_templates / window_templates`). Often the more legible - /// view of the saturation curve than the cumulative `duplicate_fraction`, - /// since it isolates each depth band instead of averaging over all prior - /// ones. Mirrors dupblaster's per-window complexity columns. + /// (`window_duplicate_templates / window_templates`). Often more legible than + /// the cumulative `duplicate_fraction`, since it isolates each window + /// instead of averaging over all prior ones. Mirrors dupblaster's per-window complexity columns. #[serde(with = "crate::float")] pub window_duplicate_fraction: f64, } diff --git a/src/lib/commands/dedup.rs b/src/lib/commands/dedup.rs index fd2852a95..489057dc3 100644 --- a/src/lib/commands/dedup.rs +++ b/src/lib/commands/dedup.rs @@ -256,7 +256,7 @@ pub(crate) struct CollectedDedupCounts { } ////////////////////////////////////////////////////////////////////////////// -// Duplication saturation ladder (--duplication-ladder) +// Duplication ladder (--duplication-ladder) ////////////////////////////////////////////////////////////////////////////// /// Per-library cumulative counters and emitted snapshot rows backing @@ -264,8 +264,8 @@ pub(crate) struct CollectedDedupCounts { /// /// # Ordering /// -/// A saturation curve plots "after N templates processed, in coordinate -/// order, what cumulative fraction were duplicates" — so [`Self::record`] +/// The ladder records "after N templates processed, in coordinate order, what +/// cumulative fraction were duplicates" — so [`Self::record`] /// MUST be called in strict serial/coordinate order, one call per position /// group. It is wired into the chain's `MiAssignDedup` step (see /// `pipeline::chains::commands::dedup::build_mi_assign_step`), which the @@ -291,7 +291,7 @@ pub(crate) struct DuplicationLadderRecorder { rows: Vec<(u16, u64, u64, u64, u64)>, } -/// Running cumulative state for one library's saturation ladder. +/// Running cumulative state for one library's duplication ladder. #[derive(Default)] struct LadderLibraryState { /// Cumulative templates seen so far for this library. @@ -1221,10 +1221,12 @@ pub struct MarkDuplicates { #[arg(short = 'H', long = "family-size-histogram")] pub family_size_histogram: Option, - /// Path to write the sampled duplication saturation ladder: per-library - /// cumulative duplicate fraction vs. templates seen (in coordinate - /// order), snapshotted every `--ladder-interval` templates. Off by - /// default (no recorder is built, so no added work). + /// Path to write the sampled duplication ladder: per-library cumulative + /// duplicate fraction vs. templates seen (in coordinate order), + /// snapshotted every `--ladder-interval` templates. Because templates are + /// counted in coordinate order, this shows how the duplicate rate varies + /// along the genome; it is not a saturation curve. Off by default (no + /// recorder is built, so no added work). #[arg(long = "duplication-ladder")] pub duplication_ladder: Option, diff --git a/src/lib/pipeline/chains/builder.rs b/src/lib/pipeline/chains/builder.rs index b22c1064b..5151aa163 100644 --- a/src/lib/pipeline/chains/builder.rs +++ b/src/lib/pipeline/chains/builder.rs @@ -6027,7 +6027,7 @@ impl<'a> ChainBuilder<'a> { dedup.include_unmapped, accumulators_for_process, ); - // Duplication-saturation ladder recorder (`--duplication-ladder`). + // Duplication ladder recorder (`--duplication-ladder`). // `Some` only when the flag is set; shared (via `Arc`) between the serial // MI-assign step, which accumulates it in coordinate order, and the // finalize hook, which writes it. `None` means the MI-assign step does diff --git a/src/lib/pipeline/chains/commands/dedup.rs b/src/lib/pipeline/chains/commands/dedup.rs index 66270aa42..39acc5567 100644 --- a/src/lib/pipeline/chains/commands/dedup.rs +++ b/src/lib/pipeline/chains/commands/dedup.rs @@ -148,7 +148,7 @@ impl FinalizeHook for DedupFinalizeHook { write_family_size_histogram(&final_family_sizes, path)?; } - // Write the duplication-saturation ladder if requested. Accessed through + // Write the duplication ladder if requested. Accessed through // the lock (not `Arc::try_unwrap`): the serial MI-assign step holds a // clone of this `Arc` that may still be alive here. if let Some((path, recorder)) = duplication_ladder { @@ -268,7 +268,7 @@ pub(crate) fn build_process_step( /// monotonically increasing MI offsets to each batch. /// /// When `ladder_recorder` is `Some`, this step also records the -/// `--duplication-ladder` saturation curve — per position group, in this +/// `--duplication-ladder` — per position group, in this /// serial/coordinate-order seam, **not** in the parallel serialize step. /// `MiAssign` is `Serial` + `ByItemOrdinal`, so batches reach this closure in /// input-record order and the groups within a batch are in coordinate order —