Skip to content

Commit d892c1b

Browse files
committed
Add gcta/grmcutoff module
1 parent cc2736c commit d892c1b

5 files changed

Lines changed: 375 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json
3+
channels:
4+
- conda-forge
5+
- bioconda
6+
dependencies:
7+
- bioconda::gcta=1.94.1
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
process GCTA_GRMCUTOFF {
2+
tag "${meta.id}"
3+
label 'process_medium'
4+
conda "${moduleDir}/environment.yml"
5+
container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container
6+
? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/46/46b0d05f0daa47561d87d2a9cac5e51edc2c78e26f1bbab439c688386241a274/data'
7+
: 'community.wave.seqera.io/library/gcta:1.94.1--9bc35dc424fcf6e9'}"
8+
9+
input:
10+
tuple val(meta), path(grm_files)
11+
val cutoff
12+
13+
output:
14+
tuple val(meta), path("${prefix}.grm.*"), emit: grm_files
15+
tuple val(meta), path("${prefix}.grm.id"), emit: keep_file
16+
tuple val("${task.process}"), val("gcta"), eval("gcta --version | sed -En 's/^[*] version v([0-9.]*).*/\\1/p'"), emit: versions_gcta, topic: versions
17+
18+
when:
19+
task.ext.when == null || task.ext.when
20+
21+
script:
22+
def args = task.ext.args ?: ''
23+
prefix = task.ext.prefix ?: "${meta.id}_grmcutoff"
24+
"""
25+
gcta \\
26+
--grm ${meta.id} \\
27+
--grm-cutoff ${cutoff} \\
28+
--make-grm \\
29+
--out ${prefix} \\
30+
--thread-num ${task.cpus} \\
31+
${args}
32+
"""
33+
34+
stub:
35+
prefix = task.ext.prefix ?: "${meta.id}_grmcutoff"
36+
"""
37+
touch ${prefix}.grm.id
38+
touch ${prefix}.grm.bin
39+
touch ${prefix}.grm.N.bin
40+
"""
41+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json
2+
name: "gcta_grmcutoff"
3+
description: Apply a genetic relationship cutoff to a dense GRM using `gcta --grm-cutoff`
4+
keywords:
5+
- gcta
6+
- genome-wide complex trait analysis
7+
- grm
8+
- genetic relationship matrix
9+
- genetics
10+
tools:
11+
- "gcta":
12+
description: "Genome-wide Complex Trait Analysis (GCTA) estimates genetic relationships, variance components, and association statistics from genome-wide data."
13+
homepage: "https://yanglab.westlake.edu.cn/software/gcta/"
14+
documentation: "https://yanglab.westlake.edu.cn/software/gcta/static/gcta_doc_latest.pdf"
15+
tool_dev_url: "https://yanglab.westlake.edu.cn/software/gcta/"
16+
licence: ["GPL-3.0-only"]
17+
identifier: "biotools:gcta"
18+
19+
input:
20+
- - meta:
21+
type: map
22+
description: |
23+
Groovy map containing dense GRM metadata.
24+
`meta.id` is the required GRM basename consumed by `--grm` and must match
25+
the staged dense GRM files.
26+
e.g. `[ id:'tiny_dense' ]` requires
27+
`tiny_dense.grm.id`, `tiny_dense.grm.bin`, and `tiny_dense.grm.N.bin`.
28+
- grm_files:
29+
type: file
30+
description: Dense GRM file bundle with basename `${meta.id}`
31+
pattern: "*.grm.*"
32+
ontologies: []
33+
- cutoff:
34+
type: float
35+
description: Genetic relationship cutoff value passed to `gcta --grm-cutoff`.
36+
37+
output:
38+
grm_files:
39+
- - meta:
40+
type: map
41+
description: |
42+
Groovy map containing dense GRM metadata.
43+
`meta.id` remains the dense-GRM basename contract used for `--grm`.
44+
- "${prefix}.grm.*":
45+
type: file
46+
description: Relatedness-filtered GRM file bundle
47+
pattern: "${prefix}.grm.*"
48+
ontologies: []
49+
keep_file:
50+
- - meta:
51+
type: map
52+
description: |
53+
Groovy map containing dense GRM metadata.
54+
`meta.id` remains the dense-GRM basename contract used for `--grm`.
55+
- "${prefix}.grm.id":
56+
type: file
57+
description: Keep file of unrelated individuals emitted by GCTA
58+
pattern: "${prefix}.grm.id"
59+
ontologies: []
60+
versions_gcta:
61+
- - "${task.process}":
62+
type: string
63+
description: The process the version was collected from
64+
- "gcta":
65+
type: string
66+
description: The tool name
67+
- "gcta --version | sed -En 's/^[*] version v([0-9.]*).*/\\1/p'":
68+
type: eval
69+
description: The command used to retrieve the GCTA version
70+
71+
topics:
72+
versions:
73+
- - ${task.process}:
74+
type: string
75+
description: The process the version was collected from
76+
- gcta:
77+
type: string
78+
description: The tool name
79+
- "gcta --version | sed -En 's/^[*] version v([0-9.]*).*/\\1/p'":
80+
type: eval
81+
description: The command used to retrieve the GCTA version
82+
83+
authors:
84+
- "@lyh970817"
85+
maintainers:
86+
- "@lyh970817"
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
nextflow_process {
2+
3+
name "Test Process GCTA_GRMCUTOFF"
4+
script "../main.nf"
5+
process "GCTA_GRMCUTOFF"
6+
7+
tag "modules"
8+
tag "modules_nfcore"
9+
tag "gcta"
10+
tag "gcta/grmcutoff"
11+
tag "gcta/makegrm"
12+
13+
setup {
14+
run("GCTA_MAKEGRM", alias: "GCTA_MAKEGRM_CONTRACT") {
15+
script "../../makegrm/main.nf"
16+
process {
17+
"""
18+
file('tiny_dense.mbfile').text = 'plink_simulated\\n'
19+
20+
input[0] = [
21+
[ id:'tiny_dense' ],
22+
file('tiny_dense.mbfile'),
23+
[
24+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/plink_simulated.bed', checkIfExists: true)
25+
],
26+
[
27+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/plink_simulated.bim', checkIfExists: true)
28+
],
29+
[
30+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/plink_simulated.fam', checkIfExists: true)
31+
]
32+
]
33+
"""
34+
}
35+
}
36+
37+
run("GCTA_MAKEGRM", alias: "GCTA_MAKEGRM_STUB") {
38+
script "../../makegrm/main.nf"
39+
process {
40+
"""
41+
file('stub_dense.mbfile').text = 'plink_simulated\\n'
42+
43+
input[0] = [
44+
[ id:'stub_dense' ],
45+
file('stub_dense.mbfile'),
46+
[
47+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/plink_simulated.bed', checkIfExists: true)
48+
],
49+
[
50+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/plink_simulated.bim', checkIfExists: true)
51+
],
52+
[
53+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/popgen/plink_simulated.fam', checkIfExists: true)
54+
]
55+
]
56+
"""
57+
}
58+
}
59+
}
60+
61+
test("homo_sapiens popgen - apply GRM cutoff to dense GRM") {
62+
when {
63+
process {
64+
"""
65+
input[0] = GCTA_MAKEGRM_CONTRACT.out.grm_files
66+
input[1] = 0.05
67+
"""
68+
}
69+
}
70+
71+
then {
72+
assertAll(
73+
{ assert process.success },
74+
{ assert process.out.grm_files.size() == 1 },
75+
{ assert process.out.keep_file.size() == 1 },
76+
{ assert process.out.grm_files.get(0).get(0).id == "tiny_dense" },
77+
{
78+
def grm_row = process.out.grm_files.get(0)
79+
def expected_prefix = "${grm_row.get(0).id}_grmcutoff"
80+
assert grm_row.get(1).collect { file(it).name }.sort() == [
81+
"${expected_prefix}.grm.N.bin",
82+
"${expected_prefix}.grm.bin",
83+
"${expected_prefix}.grm.id"
84+
]
85+
assert file(process.out.keep_file.get(0).get(1)).name == "${expected_prefix}.grm.id"
86+
},
87+
{
88+
assert snapshot(sanitizeOutput(process.out)).match()
89+
}
90+
)
91+
}
92+
}
93+
94+
test("homo_sapiens popgen - GRM cutoff fails when meta.id is not GRM basename") {
95+
when {
96+
process {
97+
"""
98+
input[0] = GCTA_MAKEGRM_CONTRACT.out.grm_files.map { meta, grm_files ->
99+
[[ id:'tiny_dense_mismatched' ], grm_files]
100+
}
101+
input[1] = 0.05
102+
"""
103+
}
104+
}
105+
106+
then {
107+
assertAll(
108+
{ assert !process.success },
109+
{ assert process.exitStatus != 0 }
110+
)
111+
}
112+
}
113+
114+
test("homo_sapiens popgen - GRM cutoff fails for malformed GRM tuple") {
115+
when {
116+
process {
117+
"""
118+
input[0] = GCTA_MAKEGRM_CONTRACT.out.grm_files.map { meta, grm_files ->
119+
[[ id:meta.id ]]
120+
}
121+
input[1] = 0.05
122+
"""
123+
}
124+
}
125+
126+
then {
127+
assert !process.success
128+
}
129+
}
130+
131+
test("homo_sapiens popgen - apply GRM cutoff to dense GRM - stub") {
132+
options "-stub"
133+
134+
when {
135+
process {
136+
"""
137+
input[0] = GCTA_MAKEGRM_STUB.out.grm_files
138+
input[1] = 0.125
139+
"""
140+
}
141+
}
142+
143+
then {
144+
assertAll(
145+
{ assert process.success },
146+
{ assert process.out.grm_files.get(0).get(0).id == "stub_dense" },
147+
{
148+
def grm_row = process.out.grm_files.get(0)
149+
def expected_prefix = "${grm_row.get(0).id}_grmcutoff"
150+
assert grm_row.get(1).collect { file(it).name }.sort() == [
151+
"${expected_prefix}.grm.N.bin",
152+
"${expected_prefix}.grm.bin",
153+
"${expected_prefix}.grm.id"
154+
]
155+
assert file(process.out.keep_file.get(0).get(1)).name == "${expected_prefix}.grm.id"
156+
},
157+
{
158+
assert snapshot(sanitizeOutput(process.out)).match()
159+
}
160+
)
161+
}
162+
}
163+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{
2+
"homo_sapiens popgen - apply GRM cutoff to dense GRM - stub": {
3+
"content": [
4+
{
5+
"grm_files": [
6+
[
7+
{
8+
"id": "stub_dense"
9+
},
10+
[
11+
"stub_dense_grmcutoff.grm.N.bin:md5,d41d8cd98f00b204e9800998ecf8427e",
12+
"stub_dense_grmcutoff.grm.bin:md5,d41d8cd98f00b204e9800998ecf8427e",
13+
"stub_dense_grmcutoff.grm.id:md5,d41d8cd98f00b204e9800998ecf8427e"
14+
]
15+
]
16+
],
17+
"keep_file": [
18+
[
19+
{
20+
"id": "stub_dense"
21+
},
22+
"stub_dense_grmcutoff.grm.id:md5,d41d8cd98f00b204e9800998ecf8427e"
23+
]
24+
],
25+
"versions_gcta": [
26+
[
27+
"GCTA_GRMCUTOFF",
28+
"gcta",
29+
"1.94.1"
30+
]
31+
]
32+
}
33+
],
34+
"meta": {
35+
"nf-test": "0.9.3",
36+
"nextflow": "25.10.4"
37+
},
38+
"timestamp": "2026-06-07T23:52:40.106468066"
39+
},
40+
"homo_sapiens popgen - apply GRM cutoff to dense GRM": {
41+
"content": [
42+
{
43+
"grm_files": [
44+
[
45+
{
46+
"id": "tiny_dense"
47+
},
48+
[
49+
"tiny_dense_grmcutoff.grm.N.bin:md5,06b73ea8bae8f1e5f5d4de33dbd2c75e",
50+
"tiny_dense_grmcutoff.grm.bin:md5,b1f124463eecbae86840a6651eec372d",
51+
"tiny_dense_grmcutoff.grm.id:md5,ca8c0bded6951fdd3bf0dddc97b6df6b"
52+
]
53+
]
54+
],
55+
"keep_file": [
56+
[
57+
{
58+
"id": "tiny_dense"
59+
},
60+
"tiny_dense_grmcutoff.grm.id:md5,ca8c0bded6951fdd3bf0dddc97b6df6b"
61+
]
62+
],
63+
"versions_gcta": [
64+
[
65+
"GCTA_GRMCUTOFF",
66+
"gcta",
67+
"1.94.1"
68+
]
69+
]
70+
}
71+
],
72+
"meta": {
73+
"nf-test": "0.9.3",
74+
"nextflow": "25.10.4"
75+
},
76+
"timestamp": "2026-06-07T23:52:04.979644911"
77+
}
78+
}

0 commit comments

Comments
 (0)