Skip to content
Open
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
6bf6638
Add snpclustering subworkflow
dbaku42 Jun 4, 2026
87af5c5
fix meta.yml
dbaku42 Jun 4, 2026
0ba608f
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jun 4, 2026
07dfd41
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jun 15, 2026
3b48d11
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jul 6, 2026
8bbe838
Update subworkflows/nf-core/snpclustering/main.nf
dbaku42 Jul 6, 2026
e1862fc
Remove ext.prefix assignments from processes
dbaku42 Jul 6, 2026
57599ea
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jul 6, 2026
fc8ef72
Align include statements for consistency
dbaku42 Jul 9, 2026
6dd867d
Refactor process definitions in nextflow.config
dbaku42 Jul 9, 2026
1196416
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jul 9, 2026
508e2d0
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jul 14, 2026
22e4b02
Clean up comments in main.nf
dbaku42 Jul 21, 2026
5b65525
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Jul 21, 2026
de75749
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 17, 2026
bb78b22
WIP: fix versions emit in custom modules
dbaku42 Aug 17, 2026
33cc677
Merge branch 'master' into add-snpclustering-subworkflow
famosab Aug 18, 2026
e4f13a1
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 19, 2026
2cf394d
Merge remote-tracking branch 'origin/add-snpclustering-subworkflow' i…
dbaku42 Aug 19, 2026
a84585e
fix: adapt ch_versions.mix() to migrated topic-based emit names (plink2)
dbaku42 Aug 19, 2026
4e37881
test: regenerate snpclustering snapshots with real content, Root caus…
dbaku42 Aug 20, 2026
a204ef2
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 20, 2026
6400b7b
fix: update versions emit pattern in custom clustering modules
dbaku42 Aug 20, 2026
4851ff7
Merge remote-tracking branch 'origin/add-snpclustering-subworkflow' i…
dbaku42 Aug 20, 2026
1fba4a3
fix: ensure main.nf ends with single newline
dbaku42 Aug 20, 2026
81c77db
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 21, 2026
1f98f39
Replace inline EIGENVEC_TO_TSV process with GAWK module in snpclustering
dbaku42 Aug 25, 2026
5d6180d
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 25, 2026
b556f73
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 26, 2026
6a7677f
fix(snpclustering): use topic versions and snapshot stable md5s
dbaku42 Aug 26, 2026
e29c966
Fixing linting and unstable md5
dbaku42 Aug 26, 2026
59e09ba
Update import paths in main.nf for modules
dbaku42 Aug 26, 2026
cc0a052
Set seed 1 plink2_pca
dbaku42 Aug 26, 2026
ddc238f
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 26, 2026
44bb942
Added tsv in unstable md5
dbaku42 Aug 26, 2026
44baf0f
Add UMAP and t-SNE output file references
dbaku42 Aug 26, 2026
6017365
Fix typo in tsne_tsv variable declaration
dbaku42 Aug 26, 2026
90425b6
Update snapshot
dbaku42 Aug 26, 2026
94751cc
Update snapshot
dbaku42 Aug 26, 2026
0709ded
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 26, 2026
408df10
Merge origin/add-snpclustering-subworkflow
dbaku42 Aug 26, 2026
da041de
Merge branch 'nf-core:master' into add-snpclustering-subworkflow
dbaku42 Aug 27, 2026
2e0c680
stabilizing through config file
dbaku42 Aug 27, 2026
78122f0
Fixing env block
dbaku42 Aug 27, 2026
2d428b2
Remove unused output variables from tests to check md5 stabilty
dbaku42 Aug 27, 2026
da18b2f
Update snapshot
dbaku42 Aug 27, 2026
f788b1a
TSNE and UMAP tsv unstable md5
dbaku42 Aug 27, 2026
7c590bd
fix(snpclustering): keep test config to seeds and gawk suffix
dbaku42 Aug 31, 2026
1cc6dbf
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 31, 2026
50fe64c
Fix unstable md5sum
dbaku42 Aug 31, 2026
a066a9e
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Aug 31, 2026
0298138
Update subworkflows/nf-core/snpclustering/main.nf
dbaku42 Sep 3, 2026
bbaa6d4
Update subworkflows/nf-core/snpclustering/main.nf
dbaku42 Sep 3, 2026
bd4bd3c
Enhance description for clustering metrics
dbaku42 Sep 9, 2026
2897267
Format description in meta.yml for readability
dbaku42 Sep 9, 2026
a4a6d49
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Sep 9, 2026
1e7ac0f
fix(snpclustering): drop gawk; consume PLINK2 eigenvec directly
dbaku42 Sep 9, 2026
d3e3c51
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Sep 11, 2026
ba0d57c
Merge branch 'master' into add-snpclustering-subworkflow
dbaku42 Sep 11, 2026
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
15 changes: 7 additions & 8 deletions modules/nf-core/custom/clustermetrics/meta.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
name: "CUSTOM_CLUSTERMETRICS"
description: "Computes clustering quality metrics (silhouette, Calinski-Harabasz,
Davies-Bouldin) and performs k-sweep analysis"
description: "Computes clustering quality metrics (silhouette, Calinski-Harabasz, Davies-Bouldin) and performs k-sweep analysis"
keywords:
- clustering
- metrics
Expand Down Expand Up @@ -29,15 +28,15 @@ input:
column per numeric feature (e.g. PCA scores).
pattern: "*.tsv"
ontologies:
- edam: http://edamontology.org/format_3475
- edam: http://edamontology.org/format_3475 # TSV
- clusters:
type: file
description: |
Comma-separated cluster assignments with `sample_id` and integer
`cluster` columns. Label -1 is treated as DBSCAN noise.
pattern: "*.csv"
ontologies:
- edam: http://edamontology.org/format_3752
- edam: http://edamontology.org/format_3752 # CSV
output:
metrics:
- - meta:
Expand All @@ -48,7 +47,7 @@ output:
description: TSV with selected cluster quality metrics
pattern: "*.metrics.tsv"
ontologies:
- edam: http://edamontology.org/format_3475
- edam: http://edamontology.org/format_3475 # TSV
k_sweep:
- - meta:
type: map
Expand All @@ -58,7 +57,7 @@ output:
description: CSV with metrics for different values of k
pattern: "*.k_sweep.csv"
ontologies:
- edam: http://edamontology.org/format_3752
- edam: http://edamontology.org/format_3752 # CSV
selected:
- - meta:
type: map
Expand All @@ -68,7 +67,7 @@ output:
description: JSON with the selected/best metrics
pattern: "*.selected.json"
ontologies:
- edam: http://edamontology.org/format_3464
- edam: http://edamontology.org/format_3464 # JSON
plots:
- - meta:
type: map
Expand All @@ -84,7 +83,7 @@ output:
description: File containing software versions
pattern: "versions.yml"
ontologies:
- edam: http://edamontology.org/format_3750
- edam: http://edamontology.org/format_3750 # YAML
topics:
versions:
- versions.yml:
Expand Down
10 changes: 5 additions & 5 deletions modules/nf-core/custom/clustervisualization/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ input:
column per numeric feature (e.g. PCA scores).
pattern: "*.tsv"
ontologies:
- edam: http://edamontology.org/format_3475
- edam: http://edamontology.org/format_3475 # TSV
- clusters:
type: file
description: |
Comma-separated cluster assignments with `sample_id` and integer
`cluster` columns. Label -1 is treated as DBSCAN noise.
pattern: "*.csv"
ontologies:
- edam: http://edamontology.org/format_3752
- edam: http://edamontology.org/format_3752 # CSV
output:
umap_tsv:
- - meta:
Expand All @@ -53,7 +53,7 @@ output:
pattern: "*.umap.tsv"
ontologies:
- edam: "http://edamontology.org/operation_2432"
- edam: http://edamontology.org/format_3475
- edam: http://edamontology.org/format_3475 # TSV
tsne_tsv:
- - meta:
type: map
Expand All @@ -64,7 +64,7 @@ output:
pattern: "*.tsne.tsv"
ontologies:
- edam: "http://edamontology.org/operation_2432"
- edam: http://edamontology.org/format_3475
- edam: http://edamontology.org/format_3475 # TSV
umap_png:
- - meta:
type: map
Expand All @@ -89,7 +89,7 @@ output:
description: "Software versions used in the module"
pattern: "versions.yml"
ontologies:
- edam: http://edamontology.org/format_3750
- edam: http://edamontology.org/format_3750 # YAML
topics:
versions:
- versions.yml:
Expand Down
11 changes: 5 additions & 6 deletions modules/nf-core/custom/pcaclustering/meta.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
name: "CUSTOM_PCACLUSTERING"
description: "Performs KMeans or DBSCAN clustering on a sample-by-feature numeric
matrix (e.g. principal components, embeddings)"
description: "Performs KMeans or DBSCAN clustering on a sample-by-feature numeric matrix (e.g. principal components, embeddings)"
keywords:
- clustering
- kmeans
Expand Down Expand Up @@ -29,7 +28,7 @@ input:
components with `FID` dropped, scikit-learn embeddings, etc.).
pattern: "*.{tsv,txt}"
ontologies:
- edam: http://edamontology.org/format_3475
- edam: http://edamontology.org/format_3475 # TSV
- algorithm:
type: string
description: Clustering algorithm to use (kmeans or dbscan)
Expand All @@ -52,7 +51,7 @@ output:
description: CSV file with sample_id and assigned cluster
pattern: "*.clusters.csv"
ontologies:
- edam: http://edamontology.org/format_3752
- edam: http://edamontology.org/format_3752 # CSV
info:
- - meta:
type: map
Expand All @@ -62,14 +61,14 @@ output:
description: JSON file with clustering parameters and statistics
pattern: "*.clustering_info.json"
ontologies:
- edam: http://edamontology.org/format_3464
- edam: http://edamontology.org/format_3464 # JSON
versions:
- "versions.yml":
type: file
description: File containing software versions
pattern: "versions.yml"
ontologies:
- edam: http://edamontology.org/format_3750
- edam: http://edamontology.org/format_3750 # YAML
topics:
versions:
- versions.yml:
Expand Down
19 changes: 19 additions & 0 deletions subworkflows/nf-core/snpclustering/awk/eigenvec_to_tsv.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
BEGIN {
FS = OFS = "\t"
}

NR == 1 {
sub(/^#/, "")
$0 = $0
for (i = 1; i <= NF; i++) {
if ($i == "IID") {
$i = "sample_id"
}
}
print
next
}

{
print
}
125 changes: 125 additions & 0 deletions subworkflows/nf-core/snpclustering/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
include { BEAGLE5_BEAGLE } from '../../../modules/nf-core/beagle5/beagle'
include { PLINK2_VCF } from '../../../modules/nf-core/plink2/vcf'
include { PLINK2_PCA } from '../../../modules/nf-core/plink2/pca'
include { GAWK as GAWK_EIGENVEC_TO_TSV } from '../../../modules/nf-core/gawk'
include { CUSTOM_PCACLUSTERING } from '../../../modules/nf-core/custom/pcaclustering'
include { CUSTOM_CLUSTERMETRICS } from '../../../modules/nf-core/custom/clustermetrics'
include { CUSTOM_CLUSTERVISUALIZATION } from '../../../modules/nf-core/custom/clustervisualization'

workflow SNPCLUSTERING {
take:
vcf_ch
refpanel_ch
genmap_ch
region
npcs
Comment thread
dbaku42 marked this conversation as resolved.
Outdated
use_approx
algorithm
n_clusters
dbscan_eps
dbscan_min_samples

main:
// Bundled awk program (see ./awk/eigenvec_to_tsv.awk); shipped alongside
// the subworkflow and passed to GAWK_EIGENVEC_TO_TSV as `program_file` so
// the awk source stays readable as awk rather than as an escaped Groovy
// string.
ch_eigenvec_to_tsv_awk = file("${moduleDir}/awk/eigenvec_to_tsv.awk", checkIfExists: true)

/*
* Build BEAGLE input tuple:
* tuple val(meta), path(vcf), path(vcf_index), path(refpanel), path(refpanel_index),
* path(genmap), path(exclsamples), path(exclmarkers), val(region)
*/
ch_beagle_input = vcf_ch.map { meta, vcf, vcf_index ->
tuple(
meta,
vcf,
vcf_index,
[],
[],
[],
[],
[],
region
)
}

BEAGLE5_BEAGLE(ch_beagle_input)

/*
* Convert imputed VCF to PLINK2 pfiles
*/
PLINK2_VCF(BEAGLE5_BEAGLE.out.vcf)

/*
* PLINK2_PCA expects:
* tuple val(meta), val(npcs), val(use_approx), path(pgen), path(psam), path(pvar)
*/
ch_plink_pca_input = PLINK2_VCF.out.pgen
.join(PLINK2_VCF.out.pvar)
.join(PLINK2_VCF.out.psam)
.map { meta, pgen, pvar, psam ->
tuple(meta, npcs, use_approx, pgen, psam, pvar)
Comment thread
dbaku42 marked this conversation as resolved.
Outdated
}

PLINK2_PCA(ch_plink_pca_input)

/*
* Convert .eigenvec to a tab-separated file for downstream clustering
* modules: strips the leading '#' from the header and renames the IID
* column to sample_id. Output keeps its default naming (<meta.id>.eigenvec)
* — only the tab-separated content matters downstream, not the extension.
*/
GAWK_EIGENVEC_TO_TSV(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Personally, I wonder if having a dedicated process to remove # and rename IID to sample_id is necessary, especially considering that the recipient of this is a custom module, which could handle this trivial conversion if it is required?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fair question. I left it as a dedicated step so custom/pcaclustering stays generic (any TSV of sample × features) and does not absorb PLINK-specific quirks (# header, IID/FID). The rename to sample_id also matches what clustermetrics / clustervisualization already expect. I’d rather keep that adapter at the subworkflow boundary than special-case PLINK inside the clustering module.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it would be better to special-case PLINK format in your clustering module, since it seems to commonly consume files that were generated in PLINK format. Which in my view would actually make the module more generic?

I understand that this is a custom module, but to me it would facilitate wider adoption if this module was tailored to the inputs, and it is a very simple operation (e.g. sed 's/#//; s/IID/sample_id/' "${plinkfile}" > "${tsvfile}" should do it), and I assume that you could simply test if it is necessary based on extension of the input file.

My main concern here is that I assume spawning all of these jobs creates some overhead, and the jobs do very little. Since @famosab has been involved more than I am, maybe there are considerations here that I am not aware of.

PLINK2_PCA.out.evecfile,
ch_eigenvec_to_tsv_awk,
false
)

/*
* PCA clustering
* CUSTOM_PCACLUSTERING(tsv, algorithm, n_clusters, dbscan_eps, dbscan_min_samples)
*/
CUSTOM_PCACLUSTERING(
GAWK_EIGENVEC_TO_TSV.out.output,
algorithm,
n_clusters,
dbscan_eps,
dbscan_min_samples
)

/*
* Metrics and visualization both expect one tuple input channel
* built from: meta + tsv + cluster assignments
*/
ch_cluster_analysis_input = GAWK_EIGENVEC_TO_TSV.out.output
.join(CUSTOM_PCACLUSTERING.out.clusters)
.map { meta, tsv, clusters ->
tuple(meta, tsv, clusters)
}

CUSTOM_CLUSTERMETRICS(ch_cluster_analysis_input)
CUSTOM_CLUSTERVISUALIZATION(ch_cluster_analysis_input)

emit:
imputed_vcf = BEAGLE5_BEAGLE.out.vcf
beagle_log = BEAGLE5_BEAGLE.out.log
pgen = PLINK2_VCF.out.pgen
pvar = PLINK2_VCF.out.pvar
psam = PLINK2_VCF.out.psam
evecfile = PLINK2_PCA.out.evecfile
evfile = PLINK2_PCA.out.evfile
pca_log = PLINK2_PCA.out.logfile
tsv = GAWK_EIGENVEC_TO_TSV.out.output
clusters = CUSTOM_PCACLUSTERING.out.clusters
cluster_info = CUSTOM_PCACLUSTERING.out.info
metrics = CUSTOM_CLUSTERMETRICS.out.metrics
k_sweep = CUSTOM_CLUSTERMETRICS.out.k_sweep
selected = CUSTOM_CLUSTERMETRICS.out.selected
metric_plots = CUSTOM_CLUSTERMETRICS.out.plots
umap_tsv = CUSTOM_CLUSTERVISUALIZATION.out.umap_tsv
tsne_tsv = CUSTOM_CLUSTERVISUALIZATION.out.tsne_tsv
umap_png = CUSTOM_CLUSTERVISUALIZATION.out.umap_png
tsne_png = CUSTOM_CLUSTERVISUALIZATION.out.tsne_png
}
Loading