diff --git a/modules/nf-core/custom/clustermetrics/meta.yml b/modules/nf-core/custom/clustermetrics/meta.yml index 9e0272f7d346..5b555d49f730 100644 --- a/modules/nf-core/custom/clustermetrics/meta.yml +++ b/modules/nf-core/custom/clustermetrics/meta.yml @@ -32,7 +32,7 @@ input: numeric features (e.g. PCA scores). pattern: "*.{tsv,txt,eigenvec}" ontologies: - - edam: http://edamontology.org/format_3475 + - edam: http://edamontology.org/format_3475 # TSV - clusters: type: file description: | @@ -40,7 +40,7 @@ input: `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: @@ -51,7 +51,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 @@ -61,7 +61,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 @@ -71,7 +71,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 @@ -87,7 +87,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: diff --git a/subworkflows/nf-core/snpclustering/main.nf b/subworkflows/nf-core/snpclustering/main.nf new file mode 100644 index 000000000000..dc312450f280 --- /dev/null +++ b/subworkflows/nf-core/snpclustering/main.nf @@ -0,0 +1,84 @@ +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 { 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 + n_pcs + use_approx + algorithm + n_clusters + dbscan_eps + dbscan_min_samples + + main: + ch_beagle_input = vcf_ch.map { meta, vcf, vcf_index -> + tuple( + meta, + vcf, + vcf_index, + [], + [], + [], + [], + [], + region + ) + } + + BEAGLE5_BEAGLE(ch_beagle_input) + PLINK2_VCF(BEAGLE5_BEAGLE.out.vcf) + + 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, n_pcs, use_approx, pgen, psam, pvar) + } + + PLINK2_PCA(ch_plink_pca_input) + + CUSTOM_PCACLUSTERING( + PLINK2_PCA.out.evecfile, + algorithm, + n_clusters, + dbscan_eps, + dbscan_min_samples + ) + + ch_cluster_analysis_input = PLINK2_PCA.out.evecfile + .join(CUSTOM_PCACLUSTERING.out.clusters) + .map { meta, eigenvec, clusters -> + tuple(meta, eigenvec, 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 + 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 +} diff --git a/subworkflows/nf-core/snpclustering/meta.yml b/subworkflows/nf-core/snpclustering/meta.yml new file mode 100644 index 000000000000..6ba3f0622b02 --- /dev/null +++ b/subworkflows/nf-core/snpclustering/meta.yml @@ -0,0 +1,246 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json +name: "snpclustering" +description: > + Subworkflow for SNP clustering. Takes genotyped VCF files, imputes missing + genotypes with BEAGLE5, converts to PLINK2 pgen format, performs PCA,then runs PCA-based clustering, + cluster metrics, and cluster visualization. +keywords: + - snp + - clustering + - pca + - plink2 + - beagle + - imputation + - population genetics +components: + - beagle5/beagle + - plink2/vcf + - plink2/pca + - custom/pcaclustering + - custom/clustermetrics + - custom/clustervisualization +input: + - vcf_ch: + description: Channel with genotyped VCF and index + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - vcf: + type: file + description: Genotyped VCF file + pattern: "*.{vcf,vcf.gz}" + - vcf_index: + type: file + description: Index for the VCF file + pattern: "*.{tbi,csi}" + - refpanel_ch: + description: Optional BEAGLE reference panel channel + - genmap_ch: + description: Optional BEAGLE genetic map channel + - region: + type: string + description: Optional genomic region passed to BEAGLE + - npcs: + type: integer + description: Number of principal components to compute + - use_approx: + type: boolean + description: Whether to use approximate PCA + - algorithm: + type: string + description: Clustering algorithm to use (kmeans or dbscan) + - n_clusters: + type: integer + description: Number of clusters for k-means + - dbscan_eps: + type: float + description: DBSCAN eps parameter + - dbscan_min_samples: + type: integer + description: DBSCAN minimum samples parameter +output: + - imputed_vcf: + description: Imputed VCF from BEAGLE5 + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - vcf: + type: file + description: Imputed VCF from BEAGLE5 + pattern: "*.{vcf,vcf.gz}" + - beagle_log: + description: BEAGLE5 log file + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - log: + type: file + description: BEAGLE5 log file + pattern: "*.log" + - pgen: + description: PLINK2 pgen file + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - pgen: + type: file + description: PLINK2 pgen file + pattern: "*.pgen" + - pvar: + description: PLINK2 pvar file + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - pvar: + type: file + description: PLINK2 pvar file + pattern: "*.pvar" + - psam: + description: PLINK2 psam file + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - psam: + type: file + description: PLINK2 psam file + pattern: "*.psam" + - evecfile: + description: PCA eigenvectors from PLINK2 + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - eigenvec: + type: file + description: PCA eigenvectors from PLINK2 + pattern: "*.eigenvec" + - evfile: + description: PCA eigenvalues from PLINK2 + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - eigenval: + type: file + description: PCA eigenvalues from PLINK2 + pattern: "*.eigenval" + - pca_log: + description: PLINK2 PCA log file + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - log: + type: file + description: PLINK2 PCA log file + pattern: "*.log" + - clusters: + description: Cluster assignments + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - clusters: + type: file + description: Cluster assignments + pattern: "*.clusters.csv" + - cluster_info: + description: Clustering parameters and statistics + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - info: + type: file + description: Clustering parameters and statistics + pattern: "*.clustering_info.json" + - metrics: + description: Cluster quality metrics + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - metrics: + type: file + description: Cluster quality metrics + pattern: "*.metrics.tsv" + - k_sweep: + description: K sweep scores + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - k_sweep: + type: file + description: K sweep scores + pattern: "*.k_sweep.csv" + - selected: + description: Selected clustering parameters + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - selected: + type: file + description: Selected clustering parameters + pattern: "*.selected.json" + - metric_plots: + description: Cluster metric plots + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - plots: + type: file + description: Cluster metric plots + pattern: "*.png" + - umap_tsv: + description: UMAP coordinates per sample + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - umap_tsv: + type: file + description: UMAP coordinates per sample + pattern: "*.umap.tsv" + - tsne_tsv: + description: t-SNE coordinates per sample + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - tsne_tsv: + type: file + description: t-SNE coordinates per sample + pattern: "*.tsne.tsv" + - umap_png: + description: UMAP plot coloured by cluster + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - umap_png: + type: file + description: UMAP plot coloured by cluster + pattern: "*.umap.png" + - tsne_png: + description: t-SNE plot coloured by cluster + structure: + - meta: + type: map + description: Groovy map containing sample metadata + - tsne_png: + type: file + description: t-SNE plot coloured by cluster + pattern: "*.tsne.png" +authors: + - "@dbaku42" +maintainers: + - "@dbaku42" diff --git a/subworkflows/nf-core/snpclustering/tests/main.nf.test b/subworkflows/nf-core/snpclustering/tests/main.nf.test new file mode 100644 index 000000000000..29a3e0e2d9d3 --- /dev/null +++ b/subworkflows/nf-core/snpclustering/tests/main.nf.test @@ -0,0 +1,185 @@ +nextflow_workflow { + + name "Test Subworkflow SNPCLUSTERING" + script "../main.nf" + workflow "SNPCLUSTERING" + + tag "subworkflows" + tag "subworkflows_nfcore" + tag "subworkflows/snpclustering" + tag "snpclustering" + tag "plink2" + tag "plink2/vcf" + tag "plink2/pca" + tag "beagle5/beagle" + tag "custom/pcaclustering" + tag "custom/clustermetrics" + tag "custom/clustervisualization" + + config "./nextflow.config" + + def unstable = [ + "beagle_log", + "pca_log", + "imputed_vcf", + "pgen", + "pvar", + "k_sweep", + "metric_plots", + "cluster_info", + "umap_png", + "tsne_tsv", + "umap_tsv", + "tsne_png" + ] + + def embedStats = { path -> + def rows = file(path).readLines().findAll { it } + def header = rows.head().split('\t') as List + [ + header : header, + n_rows : rows.size() - 1, + n_cols : header.size() + ] + } + + def clusterInfo = { path -> + def info_data = new groovy.json.JsonSlurper().parse(file(path)) + if (info_data.containsKey('inertia')) { + info_data.inertia = (info_data.inertia as Double).round(4) + } + info_data + } + + test("homo_sapiens - 1000GP.chr22.vcf.gz - kmeans 3 clusters") { + when { + workflow { + """ + input[0] = Channel.of([ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz.csi', checkIfExists: true) + ]) + input[1] = Channel.empty() + input[2] = Channel.empty() + input[3] = "" + input[4] = 8 + input[5] = false + input[6] = "kmeans" + input[7] = 3 + input[8] = 0.5 + input[9] = 5 + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot( + sanitizeOutput(workflow.out, unstableKeys: unstable), + clusterInfo(workflow.out.cluster_info[0][1]), + embedStats(workflow.out.umap_tsv[0][1]), + embedStats(workflow.out.tsne_tsv[0][1]) + ).match() } + ) + } + } + + test("homo_sapiens - 1000GP.chr22.vcf.gz - dbscan") { + when { + workflow { + """ + input[0] = Channel.of([ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz.csi', checkIfExists: true) + ]) + input[1] = Channel.empty() + input[2] = Channel.empty() + input[3] = "" + input[4] = 8 + input[5] = false + input[6] = "dbscan" + input[7] = 3 + input[8] = 0.5 + input[9] = 5 + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot( + sanitizeOutput(workflow.out, unstableKeys: unstable), + clusterInfo(workflow.out.cluster_info[0][1]), + embedStats(workflow.out.umap_tsv[0][1]), + embedStats(workflow.out.tsne_tsv[0][1]) + ).match() } + ) + } + } + + test("homo_sapiens - 1000GP.chr22.vcf.gz - kmeans - stub") { + options "-stub" + when { + workflow { + """ + input[0] = Channel.of([ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz.csi', checkIfExists: true) + ]) + input[1] = Channel.empty() + input[2] = Channel.empty() + input[3] = "" + input[4] = 8 + input[5] = false + input[6] = "kmeans" + input[7] = 3 + input[8] = 0.5 + input[9] = 5 + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot(sanitizeOutput(workflow.out)).match() } + ) + } + } + + test("homo_sapiens - 1000GP.chr22.vcf.gz - dbscan - stub") { + options "-stub" + when { + workflow { + """ + input[0] = Channel.of([ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/1000GP.chr22.vcf.gz.csi', checkIfExists: true) + ]) + input[1] = Channel.empty() + input[2] = Channel.empty() + input[3] = "" + input[4] = 8 + input[5] = false + input[6] = "dbscan" + input[7] = 3 + input[8] = 0.5 + input[9] = 5 + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot(sanitizeOutput(workflow.out)).match() } + ) + } + } +} diff --git a/subworkflows/nf-core/snpclustering/tests/main.nf.test.snap b/subworkflows/nf-core/snpclustering/tests/main.nf.test.snap new file mode 100644 index 000000000000..48255cca9687 --- /dev/null +++ b/subworkflows/nf-core/snpclustering/tests/main.nf.test.snap @@ -0,0 +1,702 @@ +{ + "homo_sapiens - 1000GP.chr22.vcf.gz - kmeans 3 clusters": { + "content": [ + { + "beagle_log": [ + [ + { + "id": "test" + }, + "test.bglout.log" + ] + ], + "cluster_info": [ + [ + { + "id": "test" + }, + "test.clustering_info.json" + ] + ], + "clusters": [ + [ + { + "id": "test" + }, + "test.clusters.csv:md5,be0a1b1d684974d5eff60be2c46ceedf" + ] + ], + "evecfile": [ + [ + { + "id": "test" + }, + "test.eigenvec:md5,0e5d88169892d522252827b7897c0369" + ] + ], + "evfile": [ + [ + { + "id": "test" + }, + "test.eigenval:md5,a5d4576064133a95bbf629b99b43a0c3" + ] + ], + "imputed_vcf": [ + [ + { + "id": "test" + }, + "test.bglout.vcf.gz" + ] + ], + "k_sweep": [ + [ + { + "id": "test" + }, + "test.k_sweep.csv" + ] + ], + "metric_plots": [ + [ + { + "id": "test" + }, + [ + "test.calinski_harabasz.png", + "test.davies_bouldin.png", + "test.elbow.png", + "test.k_distance.png", + "test.silhouette.png" + ] + ] + ], + "metrics": [ + [ + { + "id": "test" + }, + "test.metrics.tsv:md5,82e1eecd3e94e64de5c1a2f5d44eb9f5" + ] + ], + "pca_log": [ + [ + { + "id": "test" + }, + "test.log" + ] + ], + "pgen": [ + [ + { + "id": "test" + }, + "test.pgen" + ] + ], + "psam": [ + [ + { + "id": "test" + }, + "test.psam:md5,31604b4a3b20fb02ef1c59e9ead65f00" + ] + ], + "pvar": [ + [ + { + "id": "test" + }, + "test.pvar" + ] + ], + "selected": [ + [ + { + "id": "test" + }, + "test.selected.json:md5,d085933630a002310508c3ef0879f1c8" + ] + ], + "tsne_png": [ + [ + { + "id": "test" + }, + "test.tsne.png" + ] + ], + "tsne_tsv": [ + [ + { + "id": "test" + }, + "test.tsne.tsv" + ] + ], + "umap_png": [ + [ + { + "id": "test" + }, + "test.umap.png" + ] + ], + "umap_tsv": [ + [ + { + "id": "test" + }, + "test.umap.tsv" + ] + ] + }, + { + "algorithm": "kmeans", + "k": 3, + "inertia": 6.2255, + "n_samples": 3202, + "n_features": 8 + }, + { + "header": [ + "sample_id", + "Dim1", + "Dim2", + "cluster" + ], + "n_rows": 3202, + "n_cols": 4 + }, + { + "header": [ + "sample_id", + "Dim1", + "Dim2", + "cluster" + ], + "n_rows": 3202, + "n_cols": 4 + } + ], + "timestamp": "2026-09-09T10:30:31.743191089", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + }, + "homo_sapiens - 1000GP.chr22.vcf.gz - dbscan": { + "content": [ + { + "beagle_log": [ + [ + { + "id": "test" + }, + "test.bglout.log" + ] + ], + "cluster_info": [ + [ + { + "id": "test" + }, + "test.clustering_info.json" + ] + ], + "clusters": [ + [ + { + "id": "test" + }, + "test.clusters.csv:md5,c119dc1c716d66ceebc220219b02a535" + ] + ], + "evecfile": [ + [ + { + "id": "test" + }, + "test.eigenvec:md5,0e5d88169892d522252827b7897c0369" + ] + ], + "evfile": [ + [ + { + "id": "test" + }, + "test.eigenval:md5,a5d4576064133a95bbf629b99b43a0c3" + ] + ], + "imputed_vcf": [ + [ + { + "id": "test" + }, + "test.bglout.vcf.gz" + ] + ], + "k_sweep": [ + [ + { + "id": "test" + }, + "test.k_sweep.csv" + ] + ], + "metric_plots": [ + [ + { + "id": "test" + }, + [ + "test.calinski_harabasz.png", + "test.davies_bouldin.png", + "test.elbow.png", + "test.k_distance.png", + "test.silhouette.png" + ] + ] + ], + "metrics": [ + [ + { + "id": "test" + }, + "test.metrics.tsv:md5,d3f1a0d467c21875176c6f8c691b8d71" + ] + ], + "pca_log": [ + [ + { + "id": "test" + }, + "test.log" + ] + ], + "pgen": [ + [ + { + "id": "test" + }, + "test.pgen" + ] + ], + "psam": [ + [ + { + "id": "test" + }, + "test.psam:md5,31604b4a3b20fb02ef1c59e9ead65f00" + ] + ], + "pvar": [ + [ + { + "id": "test" + }, + "test.pvar" + ] + ], + "selected": [ + [ + { + "id": "test" + }, + "test.selected.json:md5,138b2bae0f67f913dcead80f8202d9af" + ] + ], + "tsne_png": [ + [ + { + "id": "test" + }, + "test.tsne.png" + ] + ], + "tsne_tsv": [ + [ + { + "id": "test" + }, + "test.tsne.tsv" + ] + ], + "umap_png": [ + [ + { + "id": "test" + }, + "test.umap.png" + ] + ], + "umap_tsv": [ + [ + { + "id": "test" + }, + "test.umap.tsv" + ] + ] + }, + { + "algorithm": "dbscan", + "eps": 0.5, + "min_samples": 5, + "n_clusters_found": 1, + "n_noise": 0, + "n_samples": 3202, + "n_features": 8 + }, + { + "header": [ + "sample_id", + "Dim1", + "Dim2", + "cluster" + ], + "n_rows": 3202, + "n_cols": 4 + }, + { + "header": [ + "sample_id", + "Dim1", + "Dim2", + "cluster" + ], + "n_rows": 3202, + "n_cols": 4 + } + ], + "timestamp": "2026-09-09T10:31:04.29645672", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + }, + "homo_sapiens - 1000GP.chr22.vcf.gz - dbscan - stub": { + "content": [ + { + "beagle_log": [ + [ + { + "id": "test" + }, + "test.bglout.log:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "cluster_info": [ + [ + { + "id": "test" + }, + "test.clustering_info.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "clusters": [ + [ + { + "id": "test" + }, + "test.clusters.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "evecfile": [ + [ + { + "id": "test" + }, + "test.eigenvec:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "evfile": [ + [ + { + "id": "test" + }, + "test.eigenval:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "imputed_vcf": [ + [ + { + "id": "test" + }, + "test.bglout.vcf.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "k_sweep": [ + [ + { + "id": "test" + }, + "test.k_sweep.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "metric_plots": [ + [ + { + "id": "test" + }, + [ + "test.calinski_harabasz.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.davies_bouldin.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.elbow.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.k_distance.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.silhouette.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + ], + "metrics": [ + [ + { + "id": "test" + }, + "test.metrics.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "pca_log": [ + [ + { + "id": "test" + }, + "test.log:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "pgen": [ + [ + { + "id": "test" + }, + "test.pgen:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "psam": [ + [ + { + "id": "test" + }, + "test.psam:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "pvar": [ + [ + { + "id": "test" + }, + "test.pvar:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "selected": [ + [ + { + "id": "test" + }, + "test.selected.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tsne_png": [ + [ + { + "id": "test" + }, + "test.tsne.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tsne_tsv": [ + [ + { + "id": "test" + }, + "test.tsne.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "umap_png": [ + [ + { + "id": "test" + }, + "test.umap.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "umap_tsv": [ + [ + { + "id": "test" + }, + "test.umap.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "timestamp": "2026-09-09T10:31:26.336556716", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + }, + "homo_sapiens - 1000GP.chr22.vcf.gz - kmeans - stub": { + "content": [ + { + "beagle_log": [ + [ + { + "id": "test" + }, + "test.bglout.log:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "cluster_info": [ + [ + { + "id": "test" + }, + "test.clustering_info.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "clusters": [ + [ + { + "id": "test" + }, + "test.clusters.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "evecfile": [ + [ + { + "id": "test" + }, + "test.eigenvec:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "evfile": [ + [ + { + "id": "test" + }, + "test.eigenval:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "imputed_vcf": [ + [ + { + "id": "test" + }, + "test.bglout.vcf.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "k_sweep": [ + [ + { + "id": "test" + }, + "test.k_sweep.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "metric_plots": [ + [ + { + "id": "test" + }, + [ + "test.calinski_harabasz.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.davies_bouldin.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.elbow.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.k_distance.png:md5,d41d8cd98f00b204e9800998ecf8427e", + "test.silhouette.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + ], + "metrics": [ + [ + { + "id": "test" + }, + "test.metrics.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "pca_log": [ + [ + { + "id": "test" + }, + "test.log:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "pgen": [ + [ + { + "id": "test" + }, + "test.pgen:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "psam": [ + [ + { + "id": "test" + }, + "test.psam:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "pvar": [ + [ + { + "id": "test" + }, + "test.pvar:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "selected": [ + [ + { + "id": "test" + }, + "test.selected.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tsne_png": [ + [ + { + "id": "test" + }, + "test.tsne.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tsne_tsv": [ + [ + { + "id": "test" + }, + "test.tsne.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "umap_png": [ + [ + { + "id": "test" + }, + "test.umap.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "umap_tsv": [ + [ + { + "id": "test" + }, + "test.umap.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "timestamp": "2026-09-09T10:31:16.00969678", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + } +} \ No newline at end of file diff --git a/subworkflows/nf-core/snpclustering/tests/nextflow.config b/subworkflows/nf-core/snpclustering/tests/nextflow.config new file mode 100644 index 000000000000..b7b0b81220e5 --- /dev/null +++ b/subworkflows/nf-core/snpclustering/tests/nextflow.config @@ -0,0 +1,23 @@ +params { + npcs = 8 + use_approx = false + algorithm = "kmeans" + n_clusters = 3 + dbscan_eps = 0.5 + dbscan_min_samples = 5 +} + +process { + withName: 'BEAGLE5_BEAGLE' { + ext.args = 'seed=1' + } + + withName: 'PLINK2_VCF' { + ext.args = '--seed 1' + } + + withName: 'PLINK2_PCA' { + ext.args = '--seed 1' + } + +}