Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### `Fixed`

- [#206](https://github.com/IntGenomicsLab/lrsomatic/pull/206) - A remote (http, https or ftp) ClinVar is now downloaded once per run by the new `VEPPLUGIN_CLINVAR` step in `PREPARE_VEP_PLUGINS`, instead of being staged by Nextflow as a foreign file; local and cloud-storage paths are staged as before. `GERMLINE_VEP` and `SOMATIC_VEP` re-checked the foreign file on its host for every sample, and NCBI answered the burst from a multi-sample GRCh38 run with HTTP 503, so `SOMATIC_VEP` failed with `Can't stage file ...clinvar_20260829.vcf.gz`; `-resume` could not recover, since the failed check changed the staging cache key. The download is checked against the new `--vep_clinvar_md5` and `--vep_clinvar_tbi_md5`, set by default to the checksums NCBI (GRCh38, VCF only) and Ensembl (CHM13, VCF and index) publish, so the pinned release cannot change silently. Resuming a run that already finished re-runs `GERMLINE_VEP` and `SOMATIC_VEP` once, since ClinVar now comes from a task rather than the stage cache. The ClinVar sizes in `docs/usage.md` are also corrected, and `docs/output.md` now documents `vep_plugins/` (@AmberVerhasselt).
- [#203](https://github.com/IntGenomicsLab/lrsomatic/pull/203) - `CLAIRS` no longer runs with `--haplotagged_tumor_bam_provided_so_skip_intermediate_phasing_and_haplotagging`. Since somatic calling was moved ahead of `PHASING_HAPLOTYPING` (v1.1.0), ClairS has received the untagged minimap2 BAM, so the flag made it skip its own phasing and haplotagging and call every paired sample without haplotype information: the full-alignment model saw no `HP` tags and the haplotype filtering step had nothing to filter on, the same as `--disable_phasing`. ClairS now runs Clair3 on the normal and tumour BAMs and phases the tumour itself. **Paired somatic calls change** (fewer false positives expected), and `CLAIRS` takes longer and uses more work-directory space (@ljwharbers).
- [#203](https://github.com/IntGenomicsLab/lrsomatic/pull/203) - `docs/output.md` now lists the ClairS SNV output as `snvs.vcf.gz`, the name the pipeline publishes, instead of `snv.vcf.gz` (@ljwharbers).
- [#196](https://github.com/IntGenomicsLab/lrsomatic/pull/196) - `LRSOMATICREPORT` now points `XDG_CACHE_HOME` at the task directory alongside `HOME` and `TMPDIR`. Singularity/Apptainer inherit the host environment, so on sites that set it outside the bind-mounted work tree the render died with `Read-only file system (os error 30): mkdir '<...>/.cache/quarto'` (@AmberVerhasselt, @ljwharbers).
Expand Down
8 changes: 7 additions & 1 deletion conf/igenomes.config
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ params.genomes = [
vep_alphamissense_tbi : "https://g-608c0c.273595.03c0.data.globus.org/VEP_plugins/AlphaMissense_hg38.tsv.gz.tbi",
// A dated release rather than the rolling vcf_GRCh38/clinvar.vcf.gz, whose VCF and
// separately-downloaded index both move weekly: two runs would otherwise annotate
// differently, and one straddling an update could pair a new VCF with an old index
// differently, and one straddling an update could pair a new VCF with an old index.
// Downloaded once per run by VEPPLUGIN_CLINVAR, which checks it against NCBI's published MD5
vep_clinvar : "https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/archive_2.0/2026/clinvar_20260829.vcf.gz",
vep_clinvar_tbi : "https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/archive_2.0/2026/clinvar_20260829.vcf.gz.tbi",
vep_clinvar_md5 : "dffae274fd559ee764d1de44f36df2cf",
// No vep_clinvar_tbi_md5: NCBI publishes no checksum for its indexes
// CADD is opt-in: the SNV table alone is 81 GB, and Nextflow's foreign-file cache is
// per session, so every non-resumed run would re-stage it. Enable with --vep_cadd_snv
// and --vep_cadd_indel (each with its _tbi); see docs/usage.md.
Expand Down Expand Up @@ -60,6 +63,9 @@ params.genomes = [
vep_polyphen_sift_db : "https://ftp.ensembl.org/pub/release-115/variation/pangenomes/Human/homo_sapiens_pangenome_PolyPhen_SIFT_20240502.db",
vep_clinvar : "https://ftp.ensembl.org/pub/rapid-release/species/Homo_sapiens/GCA_009914755.4/ensembl/variation/2022_10/vcf/2024_07/clinvar_20240624_GCA_009914755.4.vcf.gz",
vep_clinvar_tbi : "https://ftp.ensembl.org/pub/rapid-release/species/Homo_sapiens/GCA_009914755.4/ensembl/variation/2022_10/vcf/2024_07/clinvar_20240624_GCA_009914755.4.vcf.gz.tbi",
// From the CHECKSUMS file Ensembl publishes alongside the VCF and its index
vep_clinvar_md5 : "98bde67975c5f0b95230672f642a3c07",
vep_clinvar_tbi_md5 : "50b634a69c36e2479cc2c8f71a8eafd6",
// SigProfilerMatrixGenerator CHM13-T2T payload (SigProfilerSuite/SigProfilerMatrixGenerator#250);
// not yet hosted on the AlexandrovLab FTP, so it is fetched from the IntGenomicsLab Globus collection
sigprofiler_genome : "CHM13-T2T",
Expand Down
12 changes: 9 additions & 3 deletions conf/modules.config
Original file line number Diff line number Diff line change
Expand Up @@ -675,12 +675,18 @@ process {
time = { 24.h * task.attempt }
}

// The wget container carries no CA bundle, as for WGET above; the pinned MD5 still checks the download
withName : '.*:VEPPLUGIN_CLINVAR' {
ext.args = { "--no-check-certificate" }
}

// Published so a later run can skip both the download and the reshaping by pointing
// --vep_revel / --vep_eve (and their _tbi) at these files
withName : '.*:VEPPLUGIN_(REVEL|EVE)' {
// --vep_revel / --vep_eve / --vep_clinvar (and their _tbi) at these files
withName : '.*:VEPPLUGIN_(REVEL|EVE|CLINVAR)' {

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 block replaces the default publishDir, and the default is where the saveAs that drops versions.yml lives. REVEL and EVE report their versions through topics, so this never mattered for them. VEPPLUGIN_CLINVAR emits a versions.yml, so every run now publishes <outdir>/vep_plugins/versions.yml.

Suggested change
withName : '.*:VEPPLUGIN_(REVEL|EVE|CLINVAR)' {
withName : '.*:VEPPLUGIN_(REVEL|EVE|CLINVAR)' {
publishDir = [
mode: params.publish_dir_mode,
path: { "${params.outdir}/vep_plugins" },
saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
]
}

publishDir = [
mode: params.publish_dir_mode,
path: { "${params.outdir}/vep_plugins" }
path: { "${params.outdir}/vep_plugins" },
saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
]
}

Expand Down
25 changes: 25 additions & 0 deletions docs/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,31 @@ Phased variant calls produced by Longphase. Present in all samples.

</details>

#### `vep_plugins`

<details markdown="1">
<summary>Output files</summary>

```
├── vep_plugins
│ ├── clinvar_20260829.vcf.gz
│ ├── clinvar_20260829.vcf.gz.tbi
│ ├── revel_grch38.tsv.gz
│ ├── revel_grch38.tsv.gz.tbi
│ ├── eve_merged.vcf.gz
│ ├── eve_merged.vcf.gz.tbi
```

| File | Description |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `clinvar_<date>.vcf.gz{,.tbi}` | ClinVar release and index as downloaded and MD5-checked by `VEPPLUGIN_CLINVAR`; only for a remote `--vep_clinvar` |
| `revel_grch38.tsv.gz{,.tbi}` | REVEL scores re-sorted on GRCh38 and indexed by `VEPPLUGIN_REVEL` |
| `eve_merged.vcf.gz{,.tbi}` | Per-protein EVE VCFs merged and indexed by `VEPPLUGIN_EVE`; only with the opt-in `--vep_eve` |

Pass these to a later run as `--vep_clinvar`/`--vep_revel`/`--vep_eve` with their `_tbi` to skip the download and preparation. On CHM13 the ClinVar file is the CHM13-lifted Ensembl release instead.

</details>

#### Plugin fields in the `CSQ` annotation

The germline and somatic VCFs carry these extra subfields inside VEP's `CSQ` INFO annotation, on
Expand Down
17 changes: 14 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ opt-in. See [VEP plugins](#vep-plugins) for sizes, licence terms and per-assembl
| `--vep_polyphen_sift_db` | Ensembl pangenome PolyPhen/SIFT SQLite database, for the `PolyPhen_SIFT` plugin. Needed on CHM13 only |
| `--vep_clinvar` | ClinVar VCF, added as a VEP `--custom` annotation |
| `--vep_clinvar_tbi` | Index for `--vep_clinvar`. Required whenever `--vep_clinvar` is set |
| `--vep_clinvar_md5` | Expected MD5 of a remote `--vep_clinvar`, checked after download. Dropped when `--vep_clinvar` is overridden |
| `--vep_clinvar_tbi_md5` | Expected MD5 of the downloaded `--vep_clinvar_tbi`. Set on CHM13 only; dropped when either ClinVar file is overridden |
| `--vep_clinvar_fields` | Comma-separated ClinVar INFO fields to carry through. Default = `"CLNSIG,CLNREVSTAT,CLNDN"` |
| `--vep_cadd_snv` | CADD SNV score file, for the `CADD` plugin. No default — 81 GB, so opt-in; prefer a local path. GRCh38 only |
| `--vep_cadd_snv_tbi` | Index for `--vep_cadd_snv`. Required whenever `--vep_cadd_snv` is set |
Expand Down Expand Up @@ -468,7 +470,7 @@ Plugins are applied to the germline and somatic VEP runs, not to the structural-
| **SIFT** | already in the VEP cache, no file needed | `PolyPhen_SIFT` plugin | CHM13: 13 GB database |
| **PolyPhen** | already in the VEP cache, no file needed | `PolyPhen_SIFT` plugin | as above, the same database |
| **AlphaMissense** | `AlphaMissense` plugin | `AlphaMissenseProtein` plugin | 613 MB (GRCh38) / 1.1 GB (CHM13) |
| **ClinVar** | `--custom` annotation | `--custom`, CHM13-lifted VCF | 105 MB (GRCh38) / 190 MB (CHM13) |
| **ClinVar** | `--custom` annotation | `--custom`, CHM13-lifted VCF | 193 MB (GRCh38) / 99 MB (CHM13) |
| **CADD** | `CADD` plugin, opt-in | not available — see below | none — `--vep_cadd_snv` enables it (81 GB + 1.2 GB) |
| **REVEL** | `REVEL` plugin | not available — see below | 667 MB release zip |
| **EVE** | `EVE` plugin, opt-in | not available — see below | none — `--vep_eve` enables it (9.6 GB) |
Expand Down Expand Up @@ -527,6 +529,15 @@ Whether an index is required depends on the shape of what you supply:
- **AlphaMissense, ClinVar and CADD** are used exactly as given, so their `_tbi` parameter is
always required alongside them. Overriding a data file drops the default index: supply both, or
neither.
- **A remote ClinVar** (http, https or ftp) is downloaded once per run by `VEPPLUGIN_CLINVAR`, with
its index, rather than staged by `GERMLINE_VEP` and `SOMATIC_VEP` for every sample: NCBI answers
the burst of requests a multi-sample run sends with HTTP 503. The index must then be a URL too. The
download is checked against `--vep_clinvar_md5` (and `--vep_clinvar_tbi_md5` where set), so a
release re-published under the same name fails the run instead of changing the annotation. Both
defaults carry the MD5 their host publishes, and the CHM13 default also pins its index; NCBI
publishes no index checksum. With your own URL, pass its MD5 too, or the pipeline warns that the
release is not verified. Local and cloud-storage (`s3://`, `gs://`, `az://`) paths are staged as
given. The files are published to `<outdir>/vep_plugins/` for reuse as local paths.
- **REVEL and EVE** ship as zip archives. Pass a `.zip` and the pipeline unpacks and reshapes it;
pass a prepared file and its index to use it directly. Remote zips are fetched with `wget` rather
than staged by Nextflow, and EVE's 9.6 GB archive serves slowly, so expect hours.
Expand All @@ -538,8 +549,8 @@ Whether an index is required depends on the shape of what you supply:
| AlphaMissense (GRCh38) | `https://storage.googleapis.com/dm_alphamissense/AlphaMissense_hg38.tsv.gz`, with an index we host | 613 MB | no, the index is fetched ready | CC BY 4.0 |
| AlphaMissense (protein) | a gene-symbol-keyed table we host, built from the AlphaMissense protein-space release | 1.1 GB | no, fetched ready — see `CITATIONS.md` | CC BY 4.0 |
| Pangenome PolyPhen/SIFT | `https://ftp.ensembl.org/pub/release-115/variation/pangenomes/Human/homo_sapiens_pangenome_PolyPhen_SIFT_20240502.db` | 13 GB | no, it is an SQLite database | Ensembl / EMBL-EBI open |
| ClinVar (GRCh38) | `clinvar_20260829.vcf.gz` under `https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/archive_2.0/2026/` (+ `.tbi`) | 105 MB | no, `.tbi` is published | public domain |
| ClinVar (CHM13) | `clinvar_20240624_GCA_009914755.4.vcf.gz` under `https://ftp.ensembl.org/pub/rapid-release/species/Homo_sapiens/GCA_009914755.4/` | 190 MB | no, `.tbi` is published | public domain |
| ClinVar (GRCh38) | `clinvar_20260829.vcf.gz` under `https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/archive_2.0/2026/` (+ `.tbi`) | 193 MB | downloaded once, MD5-checked | public domain |
| ClinVar (CHM13) | `clinvar_20240624_GCA_009914755.4.vcf.gz` under `https://ftp.ensembl.org/pub/rapid-release/species/Homo_sapiens/GCA_009914755.4/` | 99 MB | downloaded once, MD5-checked | public domain |
| CADD v1.7 SNVs (opt-in) | `https://krishna.gs.washington.edu/download/CADD/v1.7/GRCh38/whole_genome_SNVs.tsv.gz` (+ `.tbi`) | 81 GB | no, `.tbi` is published | free for non-commercial use |
| CADD v1.7 indels (opt-in) | `https://krishna.gs.washington.edu/download/CADD/v1.7/GRCh38/gnomad.genomes.r4.0.indel.tsv.gz` (+ `.tbi`) | 1.2 GB | no, `.tbi` is published | free for non-commercial use |
| REVEL v1.3 | `https://rothsj06.dmz.hpc.mssm.edu/revel-v1.3_all_chromosomes.zip` | 667 MB | unpacked, re-sorted on GRCh38, indexed | free for non-commercial use |
Expand Down
8 changes: 8 additions & 0 deletions modules/local/vepplugin/clinvar/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json
channels:
- conda-forge
- bioconda
dependencies:
# renovate: datasource=conda depName=conda-forge/wget
- conda-forge::wget=1.21.4
68 changes: 68 additions & 0 deletions modules/local/vepplugin/clinvar/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
process VEPPLUGIN_CLINVAR {
tag "${vcf_url.toString().tokenize('/').last()}"
label 'process_single'

conda "${moduleDir}/environment.yml"
container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container
? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/3b/3b54fa9135194c72a18d00db6b399c03248103f87e43ca75e4b50d61179994b3/data'
: 'community.wave.seqera.io/library/wget:1.21.4--8b0fcde81c17be5e'}"

input:
tuple val(vcf_url), val(tbi_url), val(md5), val(tbi_md5)

output:
path "${vcf_name}{,.tbi}", emit: files
// versions.yml rather than an eval() topic: eval outputs are numbered pipeline-wide, so adding
// one shifts the cache key of every other task that has one and breaks -resume of existing runs
path "versions.yml" , emit: versions

when:
task.ext.when == null || task.ext.when

script:
def args = task.ext.args ?: ''
vcf_name = vcf_url.toString().tokenize('/').last()
def sums = [ md5 ? "${md5} ${vcf_name}" : null, tbi_md5 ? "${tbi_md5} ${vcf_name}.tbi" : null ].findAll()
def check = sums ? "printf '%s\\n' ${sums.collect { line -> "'${line}'" }.join(' ')} | md5sum -c -" : ''
"""
# Retry a transient 503 from NCBI: a linear backoff of 1 s, 2 s, ... up to 10 s, about 45 s over 10 tries
wget \\
--no-verbose \\
--tries=10 \\
--waitretry=10 \\
--retry-on-http-error=429,500,502,503,504 \\
${args} \\
-O ${vcf_name} \\
${vcf_url}

# Saved next to the VCF under the name VEP looks for, whatever the host calls it
wget \\
--no-verbose \\
--tries=10 \\
--waitretry=10 \\
--retry-on-http-error=429,500,502,503,504 \\
${args} \\
-O ${vcf_name}.tbi \\
${tbi_url}

# Pinned checksums keep the release fixed: a host that re-publishes under the same name fails here
${check}

cat <<-END_VERSIONS > versions.yml
"${task.process}":
wget: \$(wget --version | head -1 | cut -d ' ' -f 3)
END_VERSIONS
"""

stub:
vcf_name = vcf_url.toString().tokenize('/').last()
"""
echo "" | gzip > ${vcf_name}
touch ${vcf_name}.tbi

cat <<-END_VERSIONS > versions.yml
"${task.process}":
wget: \$(wget --version | head -1 | cut -d ' ' -f 3)
END_VERSIONS
"""
}
51 changes: 51 additions & 0 deletions modules/local/vepplugin/clinvar/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json
name: "vepplugin_clinvar"
description: Download a dated ClinVar VCF and its index once per run, checked against a pinned MD5, for the VEP --custom annotation
keywords:
- vep
- clinvar
- download
- annotation
tools:
- "wget":
description: "GNU Wget is a free software package for retrieving files using HTTP, HTTPS, FTP and FTPS"
homepage: "https://www.gnu.org/software/wget/"
documentation: "https://www.gnu.org/software/wget/manual/wget.html"
licence: ["GPL-3.0-or-later"]
identifier: ""

input:
- - vcf_url:
type: string
description: URL of the ClinVar VCF; its basename becomes the output name
- tbi_url:
type: string
description: URL of the tabix index published alongside the VCF
- md5:
type: string
description: |
Expected MD5 of the VCF. When set, a download that does not match fails
the task. May be null, in which case the release is not verified.
- tbi_md5:
type: string
description: |
Expected MD5 of the index, checked the same way. May be null, as for
NCBI, which publishes no checksum for its indexes.

output:
files:
- "${vcf_name}{,.tbi}":
type: file
description: The ClinVar VCF and its tabix index
pattern: "*.vcf.gz{,.tbi}"
versions:
- versions.yml:
type: file
description: File containing software versions
pattern: "versions.yml"

authors:
- "@AmberVerhasselt"
maintainers:
- "@AmberVerhasselt"
41 changes: 41 additions & 0 deletions modules/local/vepplugin/clinvar/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
nextflow_process {

name "Test Process VEPPLUGIN_CLINVAR"
script "../main.nf"
process "VEPPLUGIN_CLINVAR"

// "small" is what .github/workflows/nf-test.yml selects on for pull_request
tag "small"
tag "modules"
tag "modules_local"
tag "vepplugin_clinvar"

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.

Nit: PR CI runs only tag "small" tests (.github/workflows/nf-test.yml:53,102). The nf-test-changes job lists this test, but no shard runs it. It is a stub test, so adding tag "small" costs nothing.


// Stub only: a real run downloads the full ClinVar release
test("clinvar release - stub") {

options "-stub"

when {
process {
"""
input[0] = [
'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/archive_2.0/2026/clinvar_20260829.vcf.gz',
'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/archive_2.0/2026/clinvar_20260829.vcf.gz.tbi',
'dffae274fd559ee764d1de44f36df2cf',
null // NCBI publishes no checksum for its index
]
"""
}
}

then {
assert process.success
assertAll(
// the VCF keeps its release name, and the index sits beside it under the name VEP looks for
{ assert process.out.files[0].collect { f -> file(f).name }.sort() == [ 'clinvar_20260829.vcf.gz', 'clinvar_20260829.vcf.gz.tbi' ] },
{ assert snapshot(process.out).match() }
)
}

}
}
Loading
Loading