Skip to content

Coral - #205

Open
robert-a-forsyth wants to merge 21 commits into
devfrom
coral
Open

Coral#205
robert-a-forsyth wants to merge 21 commits into
devfrom
coral

Conversation

@robert-a-forsyth

Copy link
Copy Markdown
Collaborator

PR checklist

  • This comment contains a description of changes (with reason).
  • If you've fixed a bug or added code that should be tested, add tests!
  • If you've added a new tool - have you followed the pipeline conventions in the contribution docs
  • Make sure your code lints (nf-core pipelines lint).
  • Ensure the test suite passes (nextflow run . -profile test,docker --outdir <OUTDIR>).
  • Check for unexpected warnings in debug mode (nextflow run . -profile debug,test,docker --outdir <OUTDIR>).
  • Usage Documentation in docs/usage.md is updated.
  • Output Documentation in docs/output.md is updated.
  • CHANGELOG.md is updated.
  • README.md is updated (including new tool citations and authors/contributors).

robert-a-forsyth and others added 21 commits September 18, 2026 11:38
# Conflicts:
#	CHANGELOG.md
#	subworkflows/local/phasing_haplotyping.nf
#	subworkflows/local/small_variant_consensus.nf
#	tests/clair_only.nf.test.snap
#	tests/consensus.nf.test.snap
#	tests/default.nf.test.snap
#	tests/union.nf.test.snap
…l joins

BCFTOOLS_NORM ran with -Oz only, so multi-allelic records were never split.
BCFTOOLS_ISEC matches on exact CHROM/POS/REF/ALT, so a site one caller reports
as A>G,GT and the other as A>G never intersected: in consensus mode the variant
was dropped, in union mode it appeared twice. ClairS-TO emits no multi-allelic
records while DeepVariant emits ~2.5% (128,115 of 5,058,527 on B1975944), so
every DeepVariant multi-allelic site was systematically excluded from the
consensus. Adding -m -any splits them first; verified on real DeepSomatic output
(45,638 -> 45,677 records over chr1:1-5Mb, 39 split, none left multi-allelic).

This also makes the AF declaration mismatch harmless: ClairS-TO declares
FORMAT/AF as Number=1 while DeepVariant and DeepSomatic declare Number=A, and
bcftools concat only warns before keeping the first file's definition. Once
every record is biallelic the two declarations are equivalent. The comment on
STANDARDIZE_AF is corrected accordingly: it claimed DeepVariant emits VAF, but
all four callers emit FORMAT/AF and none emits VAF, so the rename is a no-op
under the default prioritize_caller='clair' and is kept as a guarantee for
WAKHAN rather than as a live conversion.

Two silent-failure paths in SMALL_VARIANT_CONSENSUS are closed. The caller
branch had no `other` arm, so an unrecognised meta.caller removed the sample
from every downstream result with a successful exit; it now errors. The
DeepVariant/Clair join was a plain join, so a sample present for one caller but
not the other was dropped just as quietly; it now uses failOnMismatch and
failOnDuplicate, matching PHASING_HAPLOTYPING.

Record counts in the merged output change, so the snapshots need regenerating.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With --germline_var_keep deepvariant the tumour-only germline arm runs DeepVariant
on the tumour BAM. DeepVariant is a germline caller with no somatic
discrimination -- its FILTER vocabulary is only PASS/RefCall/LowQual/NoCall -- so
those calls mix germline and clonal somatic variants and cannot be separated on
their own. Published unchanged, germline_smallvariants.vcf.gz and vep/germline/
carried most of the somatic call set.

DeepSomatic evaluates the same sites and does emit a verdict: FILTER=GERMLINE
("Non somatic variants"), PON, RefCall or PASS. That verdict is now transferred
onto the DeepVariant records as INFO/DS_VERDICT and used to select the germline
arm, mirroring how ClairS-TO adjudicates its own calls through NonSomatic and
VCFSPLIT. This is a caller's own adjudication rather than positional subtraction
of the somatic call set: on B1975944 only 1.14% (57,684) of DeepVariant's
5,058,527 PASS calls carry a positive somatic verdict, against ~87% positional
overlap with the union somatic set.

Only positively adjudicated germline sites are kept (GERMLINE or PON). RefCall and
sites DeepSomatic never evaluated are dropped rather than assumed germline, so the
arm is not overpopulated with unadjudicated calls; it keeps 83.1% of the input.
The verdict stays in INFO/DS_VERDICT so the decision is auditable in the published
VCF.

The transfer is three independent bcftools invocations, so it is three aliased
instances of the existing upstream modules (DS_VERDICT_QUERY, DS_VERDICT_ANNOTATE,
DS_GERMLINE_SELECT) rather than a new bespoke process. DeepSomatic FILTER is
single-valued in practice (RefCall/GERMLINE/PON/PASS over 13.7M records), so
transferring it as a string cannot inject the ";" that would break INFO parsing.

DEEPSOMATIC now runs before DEEPVARIANT in the subworkflow because the verdict is
built from its raw VCF, before the PASS filter discards the GERMLINE records. The
deep family must therefore be enabled as a pair: --germline_var_keep deepvariant
without deepsomatic in --somatic_var_keep is rejected at launch rather than
silently producing an unadjudicated germline arm.

Paired mode is untouched -- both Clair3 and DeepVariant already run on the normal
BAM there, so the germline arm needs no adjudication.

Also corrects the --smallvar_filter_pass docs: VCFTAG normalises FILTER to PASS,
so `false` does not restore the pre-filter behaviour as previously claimed.

Germline record counts change, so the snapshots need regenerating.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…bites

VCFTAG rewrites FILTER to PASS on both arms before phasing, which is needed so
that downstream tools filtering on PASS still see the germline records ClairS-TO
marks NonSomatic. But it did so destructively, and every record reaching the
published phased VCFs, VEP and the report therefore read FILTER=PASS with the
caller's verdict gone.

That also silently disabled the signature-stage filter.
SIGNATURES_BCFTOOLS_VIEW ran --apply-filters PASS on a call set whose FILTER had
already been normalised to PASS, so it could never remove anything: with
--smallvar_filter_pass false, RefCall/LowQual/GERMLINE/PON records reached
SigProfilerMatrixGenerator that previously could not, and the matrices were
computed over mostly-reference sites. The parameter was documented as restoring
the previous behaviour, which it did not.

VCFTAG now records the caller's FILTER in INFO/ORIG_FILTER before overwriting it,
as VCFSPLIT already did for the ClairS-TO arm, and the signature filter tests
that field instead. VCFSPLIT's stamp is respected rather than duplicated: both
the header line and the per-record field are added only when absent, since a
duplicate INFO key makes the record unparseable. Multi-valued FILTER is joined
with "," because ";" separates INFO fields, and a FILTER of "." is recorded as
"." rather than skipped. bcftools accepts only one of -i/-e, so the ALT="*"
exclusion is folded into the same include expression.

Verified against the module fixture with bcftools 1.20, including a record that
already carried ORIG_FILTER and one with a multi-valued FILTER: exactly one
ORIG_FILTER header line, no double stamping, and htslib parses the result. The
module test is extended to cover these cases.

Note that the module test cannot currently run locally: nf-test 0.9.3 generates
`VCFTAG(*input)` and the available Nextflow 26.04.1 rejects the spread operator
under its strict syntax, while the pipeline requires >=25.10.4 so 25.04.6 will
not run either. This is pre-existing and reproduces unchanged at 5b38dcf.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test read its output through path(...).vcf, an accessor provided by an
nft-vcf plugin that nf-test.config does not load, so it failed with
MissingPropertyException on every run and had never passed. It now uses the
built-in linesGzip accessor instead, which needs no plugin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
STANDARDIZE_AF and BCFTOOLS_ANNOTATE were two back-to-back bcftools annotate
calls on the same file with disjoint options -- one renaming the allele frequency
FORMAT key, the next stamping INFO/CALLER. --rename-annots composes with
-a/-c/-h, verified against a real VCF, so a single invocation does both and the
alias is removed along with its modules.config block.

The rename is selected by meta.rename_to, set only when combine_method is 'all';
in 'consensus' mode every surviving record comes from one caller and needs no
rename. Ordering is safe because BCFTOOLS_QUERY reads only CHROM/POS/REF/ALT and
does not care whether the rename has happened. The rendered ext.args was checked
for all three meta cases (VAF, AF, key absent): the escapes reach printf intact
and an absent key yields no --rename-annots.

The joins around the annotate step now use failOnMismatch/failOnDuplicate for the
same reason as the caller join: a missing annotation table should stop the run,
not quietly drop the sample.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CLAIR3 received --sample_name twice: the module passes it from ext.prefix, which
defaults to meta.id, and conf/modules.config passed the identical value again.
Harmless with argparse, but it reads as if the name were configurable. Removed
the config copy, matching how CLAIRSTO is already handled on dev.

The comment in PHASING_HAPLOTYPING claiming the somatic VCF is passed first
"(higher priority in phasing)" was false: BCFTOOLS_CONCAT sorts its input file
list alphabetically, so with the _germline_tagged/_somatic_tagged prefixes the
germline file is always passed first. The ordering is cosmetic under -a, which
emits in coordinate order; the comment now says so rather than describing an
intent the module discards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Picks up TAG_GERMLINE and TAG_SOMATIC, and restores the clairsto and
lrsomatic_report versions that the previous snapshot run had rolled back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	tests/clair_only.nf.test.snap
#	tests/consensus.nf.test.snap
#	tests/deep_only.nf.test.snap
#	tests/default.nf.test.snap
#	tests/union.nf.test.snap
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nextflow's strict syntax resolves `report_empty_slot()` against declared
functions, not local closure variables, so it read as undefined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Folds the standalone Downstream pipeline's amplicon analysis into lrsomatic as
an ECDNA subworkflow, on by default for tumour samples with ASCAT calls.

CoRAL seeds from ASCAT's cnvs.txt, which the nf-core ASCAT module already emits
and nothing consumed. bin/ascat_to_coral_bed.py converts it to the headerless
BED CoRAL wants, respelling contigs to match the reference: ASCAT writes "1"
where the BAM may say "chr1", and CoRAL builds chromosome sizes from the BAM
header. Zero-length and inverted segments are dropped, which CoRAL's own parser
would otherwise reject.

The two tools were previously local .sif files built from uncommitted working
trees, so the images could not be rebuilt by anyone. They are now built from
pinned commits of public forks:
- docker.io/robertaforsyth/coral:3.0.0-chm13-847f3d4
- docker.io/robertaforsyth/ampliconclassifier:2.0.0-chm13-cdeaa63
with Dockerfiles under containers/. AmpliconClassifier moves from the v1.5.2
fork to upstream v2.0.0, the first release with official CoRAL support; the
CHM13 patches applied unchanged. On B2096749 the ecDNA call is reproduced
exactly, and two amplicons v1.5.2 reported as "No amp/Invalid" now resolve as
Complex-non-cyclic and Linear.

CoRAL defaults to the open-source SCIP solver rather than Gurobi. Across the
1102 solver logs from the previous cohort the largest model was 620 rows by
438 columns and the slowest solve took 0.49 s, so a licensed solver buys
nothing at this scale. --coral_solver gurobi_direct with --gurobi_license
remains available, and serialises reconstruction because WLS licences cap
concurrent sessions.

Also fixes three bugs carried over from the Downstream modules: publishDir
paths were doubled by mkdir'ing a directory named after the publish target,
the empty-seed filter staged files just to measure them, and errorStrategy
'ignore' hid a real reconstruction failure without surfacing it.

Not included: an ecDNA section in LRSOMATICREPORT. That module renders through
render_report.R inside ghcr.io/ljwharbers/lrsomatic-report, so displaying
ecDNA results needs a change in ljwharbers/lrsomatic_report first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolve the CHANGELOG.md conflict by keeping both sides: dev's #206 and
#203 entries and this branch's #200 Wakhan bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

nf-core pipelines lint overall result: Passed ✅ ⚠️

Posted for pipeline commit 219aaaf

+| ✅ 226 tests passed       |+
#| ❔  21 tests were ignored |#
#| ❔   1 tests had warnings |#
!| ❗  43 tests had warnings |!
Details

❗ Test warnings:

  • nextflow_config - Config manifest.version should end in dev: 1.1.0
  • pipeline_todos - TODO string in nextflow.config: Specify your pipeline's command line flags
  • pipeline_todos - TODO string in nextflow.config: Update the field with the details of the contributors to your pipeline. New with Nextflow version 24.10.0
  • pipeline_todos - TODO string in README.md: Include a figure that guides the user through the major workflow steps. Many nf-core
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in meta.yml: #Add a description of the module and list keywords
  • pipeline_todos - TODO string in methods_description_template.yml: #Update the HTML below to your preferred methods description, e.g. add publication citation for this pipeline
  • pipeline_todos - TODO string in nextflow.config: Specify any additional parameters here
  • pipeline_todos - TODO string in base.config: Check the defaults for all processes
  • pipeline_todos - TODO string in base.config: Customise requirements for specific processes.
  • pipeline_todos - TODO string in CONTRIBUTING.md: Add any pipeline specific contribution guidelines here, such as coding styles, procedures, checklists etc.
  • schema_description - Ungrouped param in schema: skip_modkit
  • schema_description - No description provided in schema for parameter: generate_gvcf
  • schema_description - No description provided in schema for parameter: autocorrelation
  • schema_description - No description provided in schema for parameter: vep_custom
  • schema_description - No description provided in schema for parameter: vep_custom_tbi
  • schema_description - No description provided in schema for parameter: severus_minsupport
  • schema_description - No description provided in schema for parameter: wakhan_chroms
  • local_component_structure - prepare_vep_plugins.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - ecdna.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - vcf_pass_filter.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - deepsomatic.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - small_variant_consensus.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - prepare_aa_data_repo.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - prepare_reference_files.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - prepare_annotation.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - prepare_signatures.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - phasing_haplotyping.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure

❔ Tests ignored:

  • files_exist - File is ignored: CODE_OF_CONDUCT.md
  • files_exist - File is ignored: assets/nf-core-lrsomatic_logo_light.png
  • files_exist - File is ignored: docs/images/nf-core-lrsomatic_logo_light.png
  • files_exist - File is ignored: docs/images/nf-core-lrsomatic_logo_dark.png
  • files_exist - File is ignored: .github/ISSUE_TEMPLATE/config.yml
  • files_exist - File is ignored: .github/workflows/awstest.yml
  • files_exist - File is ignored: .github/workflows/awsfulltest.yml
  • files_exist - File is ignored: .github/CONTRIBUTING.md
  • nextflow_config - Config variable ignored: manifest.name
  • nextflow_config - Config variable ignored: manifest.homePage
  • files_unchanged - File ignored due to lint config: CODE_OF_CONDUCT.md
  • files_unchanged - File ignored due to lint config: .github/ISSUE_TEMPLATE/bug_report.yml
  • files_unchanged - File ignored due to lint config: .github/PULL_REQUEST_TEMPLATE.md
  • files_unchanged - File ignored due to lint config: .github/workflows/branch.yml
  • files_unchanged - File ignored due to lint config: .github/workflows/linting.yml
  • files_unchanged - File ignored due to lint config: assets/email_template.txt
  • files_unchanged - File ignored due to lint config: assets/nf-core-lrsomatic_logo_light.png
  • files_unchanged - File ignored due to lint config: docs/images/nf-core-lrsomatic_logo_light.png
  • files_unchanged - File ignored due to lint config: docs/images/nf-core-lrsomatic_logo_dark.png
  • files_unchanged - File ignored due to lint config: docs/README.md
  • actions_awstest - 'awstest.yml' workflow not found: /home/runner/work/lrsomatic/lrsomatic/.github/workflows/awstest.yml

❔ Tests fixed:

✅ Tests passed:

Run details

  • nf-core/tools version 4.1.0
  • Run at 2026-09-28 11:59:16

@ljwharbers ljwharbers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for folding the ecDNA analysis in. The port is careful: the conversion script, the pinned image builds and the join hardening all look good. There is one blocker and two defaults that need changing before this can merge.

Scope note: this branch is stacked on #200 (18 of its 21 commits are variant_filter's), and #200 has moved on since (now at ff88896). So I reviewed only the ecDNA commits (1376557, 001926c). Once #200 merges, please merge dev into coral so the diff here is ecDNA-only.

Blocker: AmpliconClassifier cannot find the downloaded GRCh38 data repo

  • GRCh38.tar.gz has a single top-level directory, GRCh38/. I streamed the full listing: 25 entries, including GRCh38/file_list.txt.
  • nf-core UNTAR applies --strip-components 1 whenever there is one top-level dir, so UNTAR_AA_DATA_REPO emits aa_data_repo/ with file_list.txt directly inside it.
  • AC (amplicon_classifier.py:2565-2568 at cdeaa63) opens $AA_DATA_REPO + "/" + args.ref + "/file_list.txt", i.e. aa_data_repo/GRCh38/file_list.txt. That path does not exist.
  • The resulting FileNotFoundError is not caught (only KeyError is). AMPLICONCLASSIFIER keeps the base finish strategy, so every default GRCh38 run with at least one seeded sample fails.
  • The failure is also invisible: stdout and stderr are redirected into ${prefix}_classifier.log, so .command.err is empty.
  • The docs (a CHM13/ subdirectory under --aa_data_repo) describe the parent-dir layout, so I suspect only the local-repo path was exercised. No test covers the download path, because the pipeline tests run with skip_coral = true and the module and subworkflow tests are stubs.

Default CHM13 runs now fail at launch

With the feature on by default and aa_data_repo_url: null for CHM13, PREPARE_AA_DATA_REPO calls error(). Every existing --genome CHM13 command line breaks until the user adds --aa_data_repo or --skip_ampliconclassifier, and there is no CHM13 repo to point at. I'd skip the classifier with a log.warn on CHM13 when no repo is given, so reconstruction still runs.

CoRAL failures are silently dropped

CORAL_RECONSTRUCT and CORAL_CYCLE use errorStrategy ... : 'ignore'. The comment says the subworkflow warns on the missing output, but ecdna.nf has no such warning. The commit message lists this exact problem as fixed. As it stands, any real CoRAL error (a bad --ref, a solver crash) removes the sample from the results while the run finishes green. Please either drop ignore, or join branched_seeds.seeded against CORAL_RECONSTRUCT.out.reconstruction with remainder: true and log.warn the misses.

Smaller points

  • CHANGELOG: #XXX should be #205. The test entry claims coverage of "the empty-seed branch", but none of the three ECDNA tests reaches it, because the CORAL_SEED stub always writes a non-empty seed.
  • Data repo is not pinned: the tarball is updated in place (it has a GRCh38/last_updated.txt) and nothing checks it. An MD5 as for --vep_clinvar_md5 (#206) would keep runs reproducible. Please also document that --aa_data_repo avoids re-fetching 1.1 GB (and unpacking ~4 GB) on every run.
  • Gurobi containerOptions: --bind … --env … is Apptainer syntax, and Docker rejects --bind.
  • Resources: CORAL_RECONSTRUCT and CORAL_CYCLE are process_high (12 CPUs / 72 GB) for every tumour sample, while your own numbers show sub-second solves. --solver-threads -1 is also not tied to task.cpus, which matters under Gurobi.
  • Plots vs classification: with --coral_run_cycle, CORAL_PLOT still plots the original reconstruction, not the re-extracted cycles that AC classifies.
  • Image and fork namespaces: the images and forks live in personal namespaces (docker.io/robertaforsyth, github.com/robert-a-forsyth). For long-term maintenance, a lab-owned namespace would be safer, as was done for ClairS-TO.
  • Nit: bed.toFile().length() → bed.size(), which also works on non-local work dirs.
  • pre-commit: it is red only on prettier (docs/output.md, nextflow_schema.json); pre-commit run --all-files fixes it.

Checked and fine

  • ASCAT's cnvs.txt columns match the converter (segments[2:6]).
  • coral seed always writes the seed file, possibly empty, so the size branch is sound.
  • Both Docker Hub tags exist.
  • The AA URL is live, and the wget image fetches it over HTTPS.
  • The meta keys from ascat_ch line up with ASCAT's output, so the joins pair.


// .first(): UNTAR emits a queue channel, and every sample's classifier task
// needs the same repo -- without this only the first sample would get it.
ch_data_repo = UNTAR_AA_DATA_REPO.out.untar.first()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GRCh38.tar.gz has a single top-level GRCh38/, so UNTAR strips it and this dir holds file_list.txt directly. AC reads $AA_DATA_REPO/GRCh38/file_list.txt, so every GRCh38 classifier task fails. One fix is to stage it under the ref name (e.g. set ext.prefix so the untarred dir is GRCh38, and stage it in AMPLICONCLASSIFIER as aa_data_repo/${ac_ref}). Another is a small shim in the module: if aa_data_repo/file_list.txt exists, mkdir repo && ln -s ../aa_data_repo repo/${ac_ref} and export AA_DATA_REPO=$PWD/repo. The shim keeps the documented parent-dir layout working for --aa_data_repo too.

ch_versions = ch_versions.mix(WGET_AA_DATA_REPO.out.versions, UNTAR_AA_DATA_REPO.out.versions)
}
else {
error("AmpliconClassifier needs an AmpliconArchitect data repository, which is not published for ${params.genome}. Set --aa_data_repo <path>, or use --skip_ampliconclassifier.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature is on by default and CHM13 has no URL, so this now aborts every --genome CHM13 run that doesn't add --aa_data_repo or --skip_ampliconclassifier. Could this warn and skip the classifier instead, leaving reconstruction running?


// A single unsolvable amplicon should not fail a whole cohort; the subworkflow
// warns on the missing output rather than letting the report slot go silent.
errorStrategy { task.exitStatus in 130..145 ? 'retry' : 'ignore' }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above says the subworkflow warns on the missing output, but ecdna.nf has no such warning, so a failed reconstruction just disappears (same in CORAL_CYCLE). Either drop ignore, or add the warning: join the seeded samples against CORAL_RECONSTRUCT.out.reconstruction with remainder: true and log.warn where the reconstruction is null.

--AA_results ${reconstruction} \\
-o ${prefix} \\
${args} \\
> ${prefix}_classifier.log 2>&1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hides AC's traceback from .command.err, so a failure shows as a bare non-zero exit. 2>&1 | tee ${prefix}_classifier.log keeps the published log and still surfaces the error (Nextflow runs with pipefail).

Comment thread conf/modules.config
// Gurobi licences are user-supplied and mounted; SCIP, the default, needs nothing.
containerOptions = {
params.coral_solver == 'gurobi_direct' && params.gurobi_license
? "--bind ${params.gurobi_license}:/opt/gurobi/gurobi.lic:ro --env GRB_LICENSE_FILE=/opt/gurobi/gurobi.lic"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--bind is Apptainer/Singularity-only; under -profile docker this needs -v ${params.gurobi_license}:/opt/gurobi/gurobi.lic:ro. Branch on workflow.containerEngine.

Comment thread CHANGELOG.md

- [#XXX](https://github.com/IntGenomicsLab/lrsomatic/pull/XXX) - Added ecDNA and focal amplification analysis: CoRAL reconstructs amplicon structures from each tumour BAM, seeded by ASCAT's copy-number calls, and AmpliconClassifier labels each amplicon as ecDNA, BFB or linear. Runs by default on tumour samples with ASCAT calls; turn it off with `--skip_coral`, or keep reconstruction without classification with `--skip_ampliconclassifier`. CoRAL's optimisation defaults to the open-source SCIP solver rather than Gurobi: across the 1102 solver logs from the previous standalone cohort the largest model was 620 rows by 438 columns and the slowest solve took 0.49 s, so a licensed solver buys nothing at this scale, though `--coral_solver gurobi_direct` with `--gurobi_license` remains available. AmpliconClassifier's AmpliconArchitect data repository is downloaded automatically for GRCh38 and must be supplied with `--aa_data_repo` for CHM13, which has no published repository (@robert-a-forsyth).
- [#XXX](https://github.com/IntGenomicsLab/lrsomatic/pull/XXX) - Added `ascat_to_coral_bed.py` and the `ASCAT_TO_CORAL_BED` module, converting ASCAT's `cnvs.txt` into the headerless BED CoRAL seeds from. Contigs are respelled to match the reference, since ASCAT writes `1` where the BAM may say `chr1` and CoRAL builds its chromosome sizes from the BAM header; zero-length and inverted segments are dropped, which CoRAL's own parser would otherwise reject (@robert-a-forsyth).
- [#XXX](https://github.com/IntGenomicsLab/lrsomatic/pull/XXX) - Added stub nf-tests for the `ECDNA` subworkflow and `ASCAT_TO_CORAL_BED` (tag `small`), covering the empty-seed branch, the opt-in cycle re-extraction and the `--skip_ampliconclassifier` path (@robert-a-forsyth).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#XXX → #205 (also on the two lines above). The claim about covering the empty-seed branch doesn't hold: the CORAL_SEED stub always writes a non-empty seed, and none of the three ECDNA tests forces an empty one.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants