P4 (WIP): GenCall-equivalent calling, BAF/LRR, sex, GTC v5, simurg genotype
- genotype/clusters.py: EGT aligned to manifest order (float32 mean R) - genotype/gencall.py: flair, six-case likelihood, priors/posteriors, fuzzy sub-scores and score map, vectorised with the reference's float/double evaluation order and libm transcendentals - genotype/baf_lrr.py, sex.py (rules v1/v2), sample.py (IDAT -> Gtc) - pipeline/inputs.py shared by ingest and genotype - pipeline/genotype.py + CLI: GTC per sample, sample_summary.tsv/json (OUT-060..063), run manifest - GTCs from IDATs are byte-identical to the oracle for 36 GSA samples x 2 normalization versions (NaN sign canonicalised to the x86 pattern) Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01814GKVzZVL3FuF3DeiZXhv
This commit is contained in:
1 parent
0cd7eb8aa0
commit
febdd121ef
12 files changed
+1270
-53
No files matched your search
+63
-1
@@ -1,6 +1,6 @@
|
||||
"""``simurg`` command-line interface (spec §2, plan §14).
|
||||
|
||||
Available: ``version``, ``inspect`` (P1), ``ingest`` (P2), ``normalize`` (P3).
|
||||
Available: ``version``, ``inspect`` (P1), ``ingest`` (P2), ``normalize`` (P3), ``genotype`` (P4).
|
||||
Later phases add the other commands of CLI-001.
|
||||
"""
|
||||
|
||||
@@ -274,5 +274,67 @@ def normalize_cmd(
|
||||
run_guarded(rep, lambda: run_normalize(opts, rep))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------------------
|
||||
@app.command("genotype")
|
||||
def genotype_cmd(
|
||||
bpm: Annotated[Path, typer.Option("--bpm", help="BPM manifest (required).")],
|
||||
egt: Annotated[Path, typer.Option("--egt", help="EGT cluster file (required).")],
|
||||
out: Annotated[Path, typer.Option("--out", help="Output folder (required).")],
|
||||
idats: Annotated[Path | None, typer.Option("--idats", help="Folder searched recursively for IDAT pairs.")] = None,
|
||||
sheet: Annotated[Path | None, typer.Option("--sheet", help="Sample sheet (CSV).")] = None,
|
||||
csv: Annotated[Path | None, typer.Option("--csv", help="CSV manifest, checked against the BPM.")] = None,
|
||||
cutoff: Annotated[float, typer.Option("--cutoff", help="GenCall score below which a call is a no-call.")] = 0.15,
|
||||
norm_version: Annotated[str, typer.Option("--norm-version", help="1.2.0 (current) or 1.1.2")] = "1.2.0",
|
||||
sex_version: Annotated[int, typer.Option("--sex-version", min=1, max=2, help="Sex estimate rule (1 or 2).")] = 2,
|
||||
gtc_version: Annotated[int, typer.Option("--gtc-version", help="GTC format version (3 or 5).")] = 5,
|
||||
autocall_date: Annotated[
|
||||
str | None, typer.Option("--autocall-date", help="Fixed value for GTC field 201 (reproducible output).")
|
||||
] = None,
|
||||
threads: Annotated[int, typer.Option("--threads", min=0, max=1024, help="Worker processes (0 = auto).")] = 0,
|
||||
force: Annotated[bool, typer.Option("--force", help="Replace existing outputs.")] = False,
|
||||
layout: Annotated[str, typer.Option("--layout", help="nested|flat")] = "nested",
|
||||
on_duplicate_idat: Annotated[str, typer.Option("--on-duplicate-idat", help="error|first|newest")] = "error",
|
||||
log_json: Annotated[bool, typer.Option("--log-json", "--json-log", help="JSON Lines log on stderr.")] = False,
|
||||
debug: Annotated[bool, typer.Option("--debug", help="Debug logging with tracebacks.")] = False,
|
||||
quiet: Annotated[bool, typer.Option("--quiet", "--no-progress", help="Suppress info messages.")] = False,
|
||||
) -> None:
|
||||
"""IDAT pairs → GTC files and a sample summary (GenCall-equivalent calling; phase P4)."""
|
||||
from simurg.pipeline.genotype import GenotypeOptions, run_genotype
|
||||
|
||||
if layout not in ("nested", "flat"):
|
||||
raise typer.BadParameter("--layout must be nested or flat")
|
||||
if gtc_version not in (3, 5):
|
||||
raise typer.BadParameter("--gtc-version must be 3 or 5")
|
||||
if norm_version not in ("1.2.0", "1.1.2"):
|
||||
raise typer.BadParameter("--norm-version must be 1.2.0 or 1.1.2")
|
||||
rep = Reporter(
|
||||
command="genotype",
|
||||
level="debug" if debug else "info",
|
||||
json_stderr=log_json,
|
||||
debug=debug,
|
||||
quiet=quiet,
|
||||
log_file=out / "logs" / "genotype.log",
|
||||
)
|
||||
opts = GenotypeOptions(
|
||||
out=out,
|
||||
bpm=bpm,
|
||||
egt=egt,
|
||||
idats=idats,
|
||||
sheet=sheet,
|
||||
csv=csv,
|
||||
threads=threads,
|
||||
force=force,
|
||||
layout=layout,
|
||||
on_duplicate_idat=on_duplicate_idat,
|
||||
cutoff=cutoff,
|
||||
norm_version=norm_version,
|
||||
sex_version=sex_version,
|
||||
gtc_version=gtc_version,
|
||||
autocall_date=autocall_date,
|
||||
argv=sys.argv[1:],
|
||||
)
|
||||
run_guarded(rep, lambda: run_genotype(opts, rep))
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
app()
|
||||
Reference in new issue
Block a user