v2 CN/LOH (WIP): cn build-model and cn call
Cyto model (.simurg-cyto: GC 1 kb/100 kb/1 Mb, PFB from the cluster file, optional sex-aware batch reference from normal GTCs; IN-051, CYTO-038); GC-wave correction (CYTO-003); smoothing (CYTO-004); HMM with constitutional, gain-LOH, mosaic and haploid states, forward-backward QUAL and Viterbi segments (CYTO-005/012); event statistics, effective size, post-filters, per-type thresholds, promotion/LowFraction, built-in centromere mask from UCSC cytoBand (CYTO-006..021); sex resolution (CYTO-024), ploidy; CNV VCF with bgzip+tabix (CYTO-028..031), segments parquet, cn_sample_summary.tsv and the cn_call_manifest.json sidecar.
This commit is contained in:
1 parent
f571ad8a2e
commit
724e87dc66
8 files changed
+1452
No files matched your search
@@ -666,6 +666,107 @@ def qc_controls_cmd(
|
||||
run_guarded(rep, lambda: run_qc_controls(opts, rep))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------------------
|
||||
cn_app = typer.Typer(help="Genome-wide copy number, LOH and mosaicism (CYTO-001).")
|
||||
app.add_typer(cn_app, name="cn")
|
||||
|
||||
|
||||
@cn_app.command("build-model")
|
||||
def cn_build_model_cmd(
|
||||
bpm: Annotated[Path, typer.Option("--bpm", help="BPM manifest (required).")],
|
||||
fasta: Annotated[Path, typer.Option("--fasta", help="Reference FASTA of the manifest's build (required).")],
|
||||
out: Annotated[Path, typer.Option("--out", help="Output model file (.simurg-cyto).")],
|
||||
egt: Annotated[Path | None, typer.Option("--egt", help="Cluster file: population B-allele frequencies.")] = None,
|
||||
reference_gtcs: Annotated[
|
||||
Path | None, typer.Option("--reference-gtcs", help="GTCs of normal samples (>= 24) for the batch reference.")
|
||||
] = None,
|
||||
max_reference: Annotated[int, typer.Option("--max-reference", min=1, help="Random subset size.")] = 100,
|
||||
log_json: LogJson = False,
|
||||
debug: Debug = False,
|
||||
quiet: Quiet = False,
|
||||
) -> None:
|
||||
"""Cyto model: GC covariates, PFB and an optional batch reference per probe (CYTO-038, IN-051)."""
|
||||
from simurg.pipeline.cn import run_build_model
|
||||
|
||||
rep = _reporter("cn build-model", out.parent, log_json, debug, quiet)
|
||||
run_guarded(rep, lambda: run_build_model(bpm, fasta, out, rep, egt, "auto", reference_gtcs, max_reference))
|
||||
|
||||
|
||||
def _kb(v: int) -> int:
|
||||
if not 0 <= v <= 250_000:
|
||||
raise typer.BadParameter("sizes are 0-250000 kb")
|
||||
return v * 1000
|
||||
|
||||
|
||||
@cn_app.command("call")
|
||||
def cn_call_cmd(
|
||||
bpm: Annotated[Path, typer.Option("--bpm", help="BPM manifest (required).")],
|
||||
model: Annotated[Path, typer.Option("--model", help="Cyto model from cn build-model (required).")],
|
||||
out: Annotated[Path, typer.Option("--out", help="Output folder (required).")],
|
||||
gtcs: Annotated[Path | None, typer.Option("--gtcs", help="Folder searched recursively for GTC files.")] = None,
|
||||
summary: Annotated[
|
||||
Path | None, typer.Option("--summary", help="sample_summary.tsv (default: next to GTCs).")
|
||||
] = None,
|
||||
fasta: Annotated[Path | None, typer.Option("--fasta", help="FASTA for REF padding bases.")] = None,
|
||||
min_del_probes: Annotated[int, typer.Option("--min-del-probes", min=0, max=250000)] = 10,
|
||||
min_del_size: Annotated[int, typer.Option("--min-del-size", help="kb")] = 25,
|
||||
min_dup_probes: Annotated[int, typer.Option("--min-dup-probes", min=0, max=250000)] = 10,
|
||||
min_dup_size: Annotated[int, typer.Option("--min-dup-size", help="kb")] = 50,
|
||||
min_loh_probes: Annotated[int, typer.Option("--min-loh-probes", min=0, max=250000)] = 500,
|
||||
min_loh_size: Annotated[int, typer.Option("--min-loh-size", help="kb")] = 3000,
|
||||
min_gainloh_probes: Annotated[int, typer.Option("--min-gainloh-probes", min=0, max=250000)] = 300,
|
||||
min_gainloh_size: Annotated[int, typer.Option("--min-gainloh-size", help="kb")] = 500,
|
||||
min_mdel_probes: Annotated[int, typer.Option("--min-mdel-probes", min=0, max=250000)] = 10,
|
||||
min_mdel_size: Annotated[int, typer.Option("--min-mdel-size", help="kb")] = 25,
|
||||
min_mdup_probes: Annotated[int, typer.Option("--min-mdup-probes", min=0, max=250000)] = 10,
|
||||
min_mdup_size: Annotated[int, typer.Option("--min-mdup-size", help="kb")] = 50,
|
||||
min_mloh_probes: Annotated[int, typer.Option("--min-mloh-probes", min=0, max=250000)] = 500,
|
||||
min_mloh_size: Annotated[int, typer.Option("--min-mloh-size", help="kb")] = 3000,
|
||||
smoothing: Annotated[int, typer.Option("--smoothing", min=0, max=1000)] = 5,
|
||||
max_mosaic_fraction: Annotated[float, typer.Option("--max-mosaic-fraction")] = 1.0,
|
||||
min_mosaic_fraction: Annotated[float, typer.Option("--min-mosaic-fraction")] = 0.10,
|
||||
filter_qual: Annotated[int, typer.Option("--filter-qual", min=0, max=60)] = 10,
|
||||
cn_cap: Annotated[int, typer.Option("--cn-cap", min=3, max=8)] = 4,
|
||||
gc_correct: Annotated[bool, typer.Option("--gc-correct/--no-gc-correct")] = True,
|
||||
unknown_sex_policy: Annotated[str, typer.Option("--unknown-sex-policy")] = "skip-sex-chromosomes",
|
||||
gt_mode: Annotated[str, typer.Option("--gt-mode", help="vcf|compat")] = "vcf",
|
||||
svlen_mode: Annotated[str, typer.Option("--svlen-mode", help="signed|positive")] = "signed",
|
||||
mask_bed: Annotated[Path | None, typer.Option("--mask-bed", help="Mask BED (default: built-in).")] = None,
|
||||
no_bgzip: Annotated[bool, typer.Option("--no-bgzip", help="Plain VCF (cn annotate then refuses it).")] = False,
|
||||
threads: ThreadsOpt = 0,
|
||||
force: ForceOpt = False,
|
||||
log_json: LogJson = False,
|
||||
debug: Debug = False,
|
||||
quiet: Quiet = False,
|
||||
) -> None:
|
||||
"""GTCs -> per-sample CNV/LOH/mosaic VCF, segments and summary (CLI-028)."""
|
||||
from simurg.cn.events import Thresholds
|
||||
from simurg.pipeline.cn import CnCallOptions, run_cn_call
|
||||
|
||||
if unknown_sex_policy not in ("skip-sex-chromosomes", "female", "male", "error"):
|
||||
raise typer.BadParameter("--unknown-sex-policy: skip-sex-chromosomes|female|male|error")
|
||||
if gt_mode not in ("vcf", "compat") or svlen_mode not in ("signed", "positive"):
|
||||
raise typer.BadParameter("--gt-mode vcf|compat, --svlen-mode signed|positive")
|
||||
if not 0 < max_mosaic_fraction <= 1 or not 0 <= min_mosaic_fraction < 1:
|
||||
raise typer.BadParameter("mosaic fractions: 0 < max <= 1, 0 <= min < 1")
|
||||
th = Thresholds(
|
||||
probes={"del": min_del_probes, "dup": min_dup_probes, "loh": min_loh_probes, "gainloh": min_gainloh_probes,
|
||||
"mdel": min_mdel_probes, "mdup": min_mdup_probes, "mloh": min_mloh_probes},
|
||||
size_bp={"del": _kb(min_del_size), "dup": _kb(min_dup_size), "loh": _kb(min_loh_size),
|
||||
"gainloh": _kb(min_gainloh_size), "mdel": _kb(min_mdel_size), "mdup": _kb(min_mdup_size),
|
||||
"mloh": _kb(min_mloh_size)},
|
||||
max_mosaic_fraction=max_mosaic_fraction, min_mosaic_fraction=min_mosaic_fraction, filter_qual=filter_qual,
|
||||
) # fmt: skip
|
||||
rep = _reporter("cn call", out, log_json, debug, quiet)
|
||||
opts = CnCallOptions(
|
||||
out=out, bpm=bpm, model=model, gtcs=gtcs, summary=summary, fasta=fasta, thresholds=th, smoothing=smoothing,
|
||||
cn_cap=cn_cap, gc_correct=gc_correct, unknown_sex_policy=unknown_sex_policy, gt_mode=gt_mode,
|
||||
svlen_mode=svlen_mode, mask_bed=mask_bed, bgzip=not no_bgzip, threads=threads, force=force,
|
||||
argv=sys.argv[1:],
|
||||
) # fmt: skip
|
||||
run_guarded(rep, lambda: run_cn_call(opts, rep))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------------------
|
||||
methyl_app = typer.Typer(help="Methylation arrays: control QC, detection, beta values (MET-001).")
|
||||
app.add_typer(methyl_app, name="methyl")
|
||||
|
||||
Reference in new issue
Block a user