diff --git a/README.md b/README.md index 334e8adf..97ee0094 100755 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ environment. ## Contributing to the `nf-scil` project If you want to propose a new `module` to the repository, follow the guidelines in the -[module creation](./docs/MODULE.md) documentation. we follow standards closely +[module creation](./docs/MODULE.md) documentation. We follow standards closely aligned with `nf-core`, with some exceptions on process atomicity and how test data is handled. Modules that don't abide to them won't be accepted and PR containing them will be closed automatically. diff --git a/docs/MODULE.md b/docs/MODULE.md index 2479dd84..7a634323 100755 --- a/docs/MODULE.md +++ b/docs/MODULE.md @@ -42,7 +42,7 @@ nf-core modules create \ --author @scilus \ --label process_single \ --meta \ - + / ``` You will still have to interact with the **bioconda** prompt, still select `no`. @@ -217,7 +217,7 @@ nf-core modules create-test-yml \ --run-tests \ --force \ --no-prompts \ - + / ``` All the test case you defined will be run, watch out for errors ! Once everything runs diff --git a/modules/nf-scil/reconst/freewater/environment.yml b/modules/nf-scil/reconst/freewater/environment.yml new file mode 100644 index 00000000..1b39908c --- /dev/null +++ b/modules/nf-scil/reconst/freewater/environment.yml @@ -0,0 +1,6 @@ +name: "reconst_freewater" +channels: + - Docker + - Apptainer +dependencies: + - "scilpy" diff --git a/modules/nf-scil/reconst/freewater/main.nf b/modules/nf-scil/reconst/freewater/main.nf new file mode 100644 index 00000000..3fcfa960 --- /dev/null +++ b/modules/nf-scil/reconst/freewater/main.nf @@ -0,0 +1,78 @@ + +process RECONST_FREEWATER { + tag "$meta.id" + label 'process_single' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://scil.usherbrooke.ca/containers/scilus_2.0.2.sif': + 'scilus/scilus:2.0.2' }" + + input: + tuple val(meta), path(dwi), path(bval), path(bvec), path(mask), path(kernels) + + output: + tuple val(meta), path("*_dwi_fw_corrected.nii.gz") , emit: dwi_fw_corrected, optional: true + tuple val(meta), path("*_FIT_dir.nii.gz") , emit: dir, optional: true + tuple val(meta), path("*_FIT_FiberVolume.nii.gz") , emit: fibervolume, optional: true + tuple val(meta), path("*_FIT_FW.nii.gz") , emit: fw, optional: true + tuple val(meta), path("*_FIT_nrmse.nii.gz") , emit: nrmse, optional: true + path("kernels") , emit: kernels, optional: true + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + + def para_diff = task.ext.para_diff ? "--para_diff " + task.ext.para_diff : "" + def perp_diff_min = task.ext.perp_diff_min ? "--perp_diff_min " + task.ext.perp_diff_min : "" + def perp_diff_max = task.ext.perp_diff_max ? "--perp_diff_max " + task.ext.perp_diff_max : "" + def iso_diff = task.ext.iso_diff ? "--iso_diff " + task.ext.iso_diff : "" + def lambda1 = task.ext.lambda1 ? "--lambda1 " + task.ext.lambda1 : "" + def lambda2 = task.ext.lambda2 ? "--lambda2 " + task.ext.lambda2 : "" + def nb_threads = task.ext.nb_threads ? "--processes " + task.ext.nb_threads : "" + def b_thr = task.ext.b_thr ? "--b_thr " + task.ext.b_thr : "" + def set_kernels = kernels ? "--load_kernels $kernels" : "--save_kernels kernels/ --compute_only" + def set_mask = mask ? "--mask $mask" : "" + + """ + scil_freewater_maps.py $dwi $bval $bvec $para_diff $perp_diff_min \ + $perp_diff_max $iso_diff $lambda1 $lambda2 $nb_threads $b_thr \ + $set_mask $set_kernels + + + if [ -d "$kernels" ]; then + mv results/dwi_fw_corrected.nii.gz ${prefix}__dwi_fw_corrected.nii.gz + mv results/FIT_dir.nii.gz ${prefix}__FIT_dir.nii.gz + mv results/FIT_FiberVolume.nii.gz ${prefix}__FIT_FiberVolume.nii.gz + mv results/FIT_FW.nii.gz ${prefix}__FIT_FW.nii.gz + mv results/FIT_nrmse.nii.gz ${prefix}__FIT_nrmse.nii.gz + + rm -rf results + fi + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + scilpy: 2.0.2 + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + scil_freewater_maps.py -h + mkdir kernels + touch "${prefix}__dwi_fw_corrected.nii.gz" + touch "${prefix}__FIT_dir.nii.gz" + touch "${prefix}__FIT_FiberVolume.nii.gz" + touch "${prefix}__FIT_FW.nii.gz" + touch "${prefix}__FIT_nrmse.nii.gz" + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + scilpy: 2.0.2 + END_VERSIONS + """ +} diff --git a/modules/nf-scil/reconst/freewater/meta.yml b/modules/nf-scil/reconst/freewater/meta.yml new file mode 100644 index 00000000..7844118f --- /dev/null +++ b/modules/nf-scil/reconst/freewater/meta.yml @@ -0,0 +1,96 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json +name: "reconst_freewater" +description: Run freewater modelling pipeline using AMICO from DWI data. +keywords: + - Diffusion MRI + - Freewater + - Microstructure modeling +tools: + - "Scilpy": + description: "The Sherbrooke Connectivity Imaging Lab (SCIL) Python dMRI processing toolbox." + homepage: "https://github.com/scilus/scilpy.git" + - "Freewater": + description: "Free water elimination and mapping from diffusion MRI." + doi: "10.1002/mrm.22055" + - "AMICO": + description: "Accelerated Microstructure Imaging via Convex Optimization." + homepage: "https://github.com/daducci/AMICO" + doi: "10.1016/j.neuroimage.2014.10.026" + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + + - dwi: + type: file + description: Nifti DWI file acquired with a freewater compatible protocol (single-shell or multi-shell). + pattern: "*.{nii,nii.gz}" + + - bval: + type: file + description: B-values in FSL format. + pattern: "*.bval" + + - bvec: + type: file + description: B-vectors in FSL format. + pattern: "*.bvec" + + - mask: + type: file + description: Nifti brain mask. + pattern: "*mask.{nii,nii.gz}" + + - kernels: + type: directory + description: Folder containg kernels. + pattern: "kernels" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + + - dwi_fw_corrected: + type: file + description: Nifti file freewater corrected DWI. + pattern: "*__dwi_fw_corrected.{nii,nii.gz}" + + - dir: + type: file + description: Nifti file main direction. + pattern: "*__FIT_dir.{nii,nii.gz}" + + - fibervolume: + type: file + description: Nifti file for fiber volume. + pattern: "*__FIT_fibervolume.{nii,nii.gz}" + + - fw: + type: file + description: Nifti file for freewater (isotropic volume). + pattern: "*__FIT_FW.{nii,nii.gz}" + + - nrmse: + type: file + description: Nifti file for nrmse. + pattern: "*__FIT_nrmse.{nii,nii.gz}" + + - kernels: + type: directory + description: Folder containing kernels. + pattern: "kernels" + + - versions: + type: file + description: File containing software versions. + pattern: "versions.yml" + +authors: + - "@karanphil" diff --git a/modules/nf-scil/reconst/freewater/tests/main.nf.test b/modules/nf-scil/reconst/freewater/tests/main.nf.test new file mode 100644 index 00000000..fe860b11 --- /dev/null +++ b/modules/nf-scil/reconst/freewater/tests/main.nf.test @@ -0,0 +1,85 @@ +nextflow_process { + + name "Test Process RECONST_FREEWATER" + script "../main.nf" + process "RECONST_FREEWATER" + + tag "modules" + tag "modules_nfcore" + tag "reconst" + tag "reconst/freewater" + + tag "subworkflows" + tag "subworkflows/load_test_data" + + config "./nextflow.config" + + setup { + run("LOAD_TEST_DATA", alias: "LOAD_DATA") { + script "../../../../../subworkflows/nf-scil/load_test_data/main.nf" + process { + """ + input[0] = Channel.from( [ "commit_amico.zip" ] ) + input[1] = "test.load-test-data" + """ + } + } + } + + test("reconst - freewater") { + + when { + process { + """ + input[0] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], // meta map + file("\${test_data_directory}/dwi.nii.gz", checkIfExists: true), + file("\${test_data_directory}/dwi.bval", checkIfExists: true), + file("\${test_data_directory}/dwi.bvec", checkIfExists: true), + file("\${test_data_directory}/mask.nii.gz", checkIfExists: true), + [] + ] + } + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + + test("reconst - freewater_no_mask") { + + when { + process { + """ + input[0] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], // meta map + file("\${test_data_directory}/dwi.nii.gz", checkIfExists: true), + file("\${test_data_directory}/dwi.bval", checkIfExists: true), + file("\${test_data_directory}/dwi.bvec", checkIfExists: true), + [], + [] + ] + } + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-scil/reconst/freewater/tests/main.nf.test.snap b/modules/nf-scil/reconst/freewater/tests/main.nf.test.snap new file mode 100644 index 00000000..493b2acf --- /dev/null +++ b/modules/nf-scil/reconst/freewater/tests/main.nf.test.snap @@ -0,0 +1,156 @@ +{ + "reconst - freewater_no_mask": { + "content": [ + { + "0": [ + + ], + "1": [ + + ], + "2": [ + + ], + "3": [ + + ], + "4": [ + + ], + "5": [ + [ + "A_001.npy:md5,21363070a99f8b8e390f713642bdd580", + "A_002.npy:md5,142af968949cc3a15766e3fe35be0bf6", + "A_003.npy:md5,837697b75af5e2207151c0b8c5fe007b", + "A_004.npy:md5,7bfdaf0b3ae6b2e25ec58b74edc3d87e", + "A_005.npy:md5,994ce19ed55493bb96b7759a625b1891", + "A_006.npy:md5,d100c9ee3b05b72a05b219a31dd3192f", + "A_007.npy:md5,84d18ac92409ac364cc1b5809803e214", + "A_008.npy:md5,5073410af3faf70a4ad8d011edb1bf1e", + "A_009.npy:md5,0450dd684224c913794acf8534b12958", + "A_010.npy:md5,6cd83ae58dd4b2b46239a4add9490d21", + "A_011.npy:md5,11a85a7f246689c9f23022f9b1cd45a3" + ] + ], + "6": [ + "versions.yml:md5,8c5d0f921a9f4a719aa37c7a45699960" + ], + "dir": [ + + ], + "dwi_fw_corrected": [ + + ], + "fibervolume": [ + + ], + "fw": [ + + ], + "kernels": [ + [ + "A_001.npy:md5,21363070a99f8b8e390f713642bdd580", + "A_002.npy:md5,142af968949cc3a15766e3fe35be0bf6", + "A_003.npy:md5,837697b75af5e2207151c0b8c5fe007b", + "A_004.npy:md5,7bfdaf0b3ae6b2e25ec58b74edc3d87e", + "A_005.npy:md5,994ce19ed55493bb96b7759a625b1891", + "A_006.npy:md5,d100c9ee3b05b72a05b219a31dd3192f", + "A_007.npy:md5,84d18ac92409ac364cc1b5809803e214", + "A_008.npy:md5,5073410af3faf70a4ad8d011edb1bf1e", + "A_009.npy:md5,0450dd684224c913794acf8534b12958", + "A_010.npy:md5,6cd83ae58dd4b2b46239a4add9490d21", + "A_011.npy:md5,11a85a7f246689c9f23022f9b1cd45a3" + ] + ], + "nrmse": [ + + ], + "versions": [ + "versions.yml:md5,8c5d0f921a9f4a719aa37c7a45699960" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-05-14T17:14:18.647389" + }, + "reconst - freewater": { + "content": [ + { + "0": [ + + ], + "1": [ + + ], + "2": [ + + ], + "3": [ + + ], + "4": [ + + ], + "5": [ + [ + "A_001.npy:md5,21363070a99f8b8e390f713642bdd580", + "A_002.npy:md5,142af968949cc3a15766e3fe35be0bf6", + "A_003.npy:md5,837697b75af5e2207151c0b8c5fe007b", + "A_004.npy:md5,7bfdaf0b3ae6b2e25ec58b74edc3d87e", + "A_005.npy:md5,994ce19ed55493bb96b7759a625b1891", + "A_006.npy:md5,d100c9ee3b05b72a05b219a31dd3192f", + "A_007.npy:md5,84d18ac92409ac364cc1b5809803e214", + "A_008.npy:md5,5073410af3faf70a4ad8d011edb1bf1e", + "A_009.npy:md5,0450dd684224c913794acf8534b12958", + "A_010.npy:md5,6cd83ae58dd4b2b46239a4add9490d21", + "A_011.npy:md5,11a85a7f246689c9f23022f9b1cd45a3" + ] + ], + "6": [ + "versions.yml:md5,8c5d0f921a9f4a719aa37c7a45699960" + ], + "dir": [ + + ], + "dwi_fw_corrected": [ + + ], + "fibervolume": [ + + ], + "fw": [ + + ], + "kernels": [ + [ + "A_001.npy:md5,21363070a99f8b8e390f713642bdd580", + "A_002.npy:md5,142af968949cc3a15766e3fe35be0bf6", + "A_003.npy:md5,837697b75af5e2207151c0b8c5fe007b", + "A_004.npy:md5,7bfdaf0b3ae6b2e25ec58b74edc3d87e", + "A_005.npy:md5,994ce19ed55493bb96b7759a625b1891", + "A_006.npy:md5,d100c9ee3b05b72a05b219a31dd3192f", + "A_007.npy:md5,84d18ac92409ac364cc1b5809803e214", + "A_008.npy:md5,5073410af3faf70a4ad8d011edb1bf1e", + "A_009.npy:md5,0450dd684224c913794acf8534b12958", + "A_010.npy:md5,6cd83ae58dd4b2b46239a4add9490d21", + "A_011.npy:md5,11a85a7f246689c9f23022f9b1cd45a3" + ] + ], + "nrmse": [ + + ], + "versions": [ + "versions.yml:md5,8c5d0f921a9f4a719aa37c7a45699960" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-05-14T17:13:16.769048" + } +} \ No newline at end of file diff --git a/modules/nf-scil/reconst/freewater/tests/nextflow.config b/modules/nf-scil/reconst/freewater/tests/nextflow.config new file mode 100644 index 00000000..71b05c26 --- /dev/null +++ b/modules/nf-scil/reconst/freewater/tests/nextflow.config @@ -0,0 +1,12 @@ +process { + withName: "RECONST_FREEWATER" { + ext.para_diff = 1.7e-3 + ext.perp_diff_min = 0.1e-3 + ext.perp_diff_max = 0.7e-3 + ext.iso_diff = 3e-3 + ext.lambda1 = 0.5 + ext.lambda2 = 1e-3 + ext.b_thr = 40 + ext.nb_threads = 1 + } +} \ No newline at end of file diff --git a/modules/nf-scil/reconst/freewater/tests/tags.yml b/modules/nf-scil/reconst/freewater/tests/tags.yml new file mode 100644 index 00000000..bba61621 --- /dev/null +++ b/modules/nf-scil/reconst/freewater/tests/tags.yml @@ -0,0 +1,2 @@ +reconst/freewater: + - "modules/nf-scil/reconst/freewater/**" diff --git a/modules/nf-scil/reconst/noddi/meta.yml b/modules/nf-scil/reconst/noddi/meta.yml index da7e5bc8..23dd9458 100755 --- a/modules/nf-scil/reconst/noddi/meta.yml +++ b/modules/nf-scil/reconst/noddi/meta.yml @@ -84,7 +84,7 @@ output: - kernels: type: directory - description: Folder containg kernels. + description: Folder containing kernels. pattern: "kernels" - versions: diff --git a/poetry.lock b/poetry.lock index 9ae412e9..750dd28e 100755 --- a/poetry.lock +++ b/poetry.lock @@ -2131,13 +2131,22 @@ widechars = ["wcwidth"] [[package]] name = "textual" +<<<<<<< HEAD +version = "0.58.1" +======= version = "0.59.0" +>>>>>>> 7cb01f5b83389c9353d58e83ceba98b49ae57c88 description = "Modern Text User Interface framework" optional = false python-versions = "<4.0,>=3.8" files = [ +<<<<<<< HEAD + {file = "textual-0.58.1-py3-none-any.whl", hash = "sha256:9902ebb4b00481f6fdb0e7db821c007afa45797d81e1d0651735a07de25ece87"}, + {file = "textual-0.58.1.tar.gz", hash = "sha256:3a01be0b583f2bce38b8e9786b75ed33dddc816bba502d8e7a9ca3ca2ead3957"}, +======= {file = "textual-0.59.0-py3-none-any.whl", hash = "sha256:61d6187704731a9425da0d20217a737c8738c1649c644041f868afb6963e0022"}, {file = "textual-0.59.0.tar.gz", hash = "sha256:0fb0001ed393a9eb2c6c8598a5436cde588658a9726b5e099ae3bfb6f3ac257d"}, +>>>>>>> 7cb01f5b83389c9353d58e83ceba98b49ae57c88 ] [package.dependencies]