Optional inputs
In the previous section, you only added inputs if you needed them to supply a required input for an included component. Now, you’ll define new inputs, optional ones, that users can use to modify the behavior of the subworkflow.
Binding a component’s optional input
Section titled “Binding a component’s optional input”A good first optional input to define for the subworkflow is an input brain mask. For now, we compute this mask two
times : once with BETCROP_SYNTHBET
to control intensity normalization by PREPROC_N4
, then a second time with
BETCROP_ANTSBET
to obtain a robust mask based on a population template. A user might have a different technique
of preference to compute the brain mask, or he might want to do it by hand. To allow him to input it, add the
ch_brain_mask
input in the take
section of the subworkflow :
workflow PREPROC_ANAT {take: ch_anatomical // Structure : [ [id: string] , path(anat_image) ] ch_template // Structure : [ path(anat_ref), path(brain_proba) ] ch_brain_mask // Structure : [ [id: string] , path(brain_mask) ], optionalmain: ...
Now, you can start using the mask to improve the behavior of included components, but you have to be careful about it,
since the mask is optional. To associate it with another channel, use join
with the special argument
remainder
:
true
, which keeps incomplete elements resulting from the operation :
workflow PREPROC_ANAT {take: ch_anatomical // Structure : [ [id: string] , path(anat_image) ] ch_template // Structure : [ path(anat_ref), path(brain_proba) ] ch_brain_mask // Structure : [ [id: string] , path(brain_mask) ], optionalmain: ch_versions = Channel.empty()
ch_denoising_nlmeans = ch_anatomical .join(ch_brain_mask, remainder: true) .map{ meta, image, mask -> [meta, image, [], mask ?: []] }
DENOISING_NLMEANS( ch_denoising_nlmeans ) ch_versions = ch_versions.mix(DENOISING_NLMEANS.out.versions) ...
Skipping a component execution given an optional input
Section titled “Skipping a component execution given an optional input”If a brain mask is given in input, then brain masking modules included in the subworflow should not be executed. This is
not as simple as doing a conditional test on ch_brain_mask
, it will return true
even if
empty. Instead, you need to filter
the content of a component’s input you wish to skip, to empty it of all elements for
which a mask exists :
workflow PREPROC_ANAT {take: ch_anatomical // Structure : [ [id: string] , path(anat_image) ] ch_template // Structure : [ path(anat_ref), path(brain_proba) ] ch_brain_mask // Structure : [ [id: string] , path(brain_mask) ], optionalmain: ...
ch_betcrop_synthbet = DENOISING_NLMEANS.out.image .join(ch_brain_mask, remainder: true) .filter{ meta, image, mask -> !mask } .map{ meta, image, mask -> [meta, image, []] }
BETCROP_SYNTHBET( ch_betcrop_synthbet ) ch_versions = ch_versions.mix(BETCROP_SYNTHBET.out.versions)
Repeat the same procedure for BETCROP_ANTSBET
:
workflow PREPROC_ANAT {take: ch_anatomical // Structure : [ [id: string] , path(anat_image) ] ch_template // Structure : [ path(anat_ref), path(brain_proba) ] ch_brain_mask // Structure : [ [id: string] , path(brain_mask) ], optionalmain: ...
ch_betcrop_antsbet = PREPROC_N4.out.image .join(ch_brain_mask, remainder: true) .filter{ meta, image, mask -> !mask } .map{ meta, image, mask -> [meta, image] } .combine(ch_template)
BETCROP_ANTSBET( ch_betcrop_antsbet ) ch_versions = ch_versions.mix(BETCROP_ANTSBET.out.versions)
Below is a full example will all optional inputs defined :
// MODULESinclude { DENOISING_NLMEANS } from '../../../modules/nf-neuro/denoising/nlmeans/main'include { BETCROP_SYNTHBET } from '../../../modules/nf-neuro/betcrop/synthbet/main'include { BETCROP_ANTSBET } from '../../../modules/nf-neuro/betcrop/antsbet/main'include { PREPROC_N4 } from '../../../modules/nf-neuro/preproc/n4/main'// SUBWORKFLOWSinclude { ANATOMICAL_SEGMENTATION } from '../anatomical_segmentation/main'
workflow PREPROC_ANAT {take: ch_anatomical // Structure : [ [id: string] , path(anat_image) ] ch_template // Structure : [ path(anat_ref), path(brain_proba) ] ch_brain_mask // Structure : [ [id: string] , path(brain_mask) ], optional ch_synthbet_weights // Structure : [ [id: string] , path(weights) ], optional ch_n4_reference // Structure : [ [id: string] , path(reference) ], optional ch_freesurferseg // Structure : [ [id: string] , path(aparc+aseg) , path(wmparc) ], optional ch_lesion // Structure : [ [id: string] , path(lesion) ], optional ch_fs_license // Structure : [ [id: string] , path(license) ], optionalmain: ch_versions = Channel.empty()
ch_denoising_nlmeans = ch_anatomical .join(ch_brain_mask, remainder: true) .map{ meta, image, mask -> [meta, image, [], mask ?: []] }
DENOISING_NLMEANS( ch_denoising_nlmeans ) ch_versions = ch_versions.mix(DENOISING_NLMEANS.out.versions)
ch_betcrop_synthbet = DENOISING_NLMEANS.out.image .join(ch_brain_mask, remainder: true) .filter{ meta, image, mask -> !mask } .join(ch_synthbet_weights, remainder: true) .map{ meta, image, mask, weights -> [meta, image, weights ?: []] }
BETCROP_SYNTHBET( ch_betcrop_synthbet ) ch_versions = ch_versions.mix(BETCROP_SYNTHBET.out.versions)
ch_preproc_n4 = DENOISING_NLMEANS.out.image .join(ch_n4_reference, remainder: true) .map{ meta, image, reference -> [meta, image, reference ?: []] } .join(BETCROP_SYNTHBET.out.brain_mask)
PREPROC_N4( ch_preproc_n4 ) ch_versions = ch_versions.mix(PREPROC_N4.out.versions)
ch_betcrop_antsbet = PREPROC_N4.out.image .join(ch_brain_mask, remainder: true) .filter{ meta, image, mask -> !mask } .map{ meta, image, mask -> [meta, image] } .combine(ch_template)
BETCROP_ANTSBET( ch_betcrop_antsbet ) ch_versions = ch_versions.mix(BETCROP_ANTSBET.out.versions)
ANATOMICAL_SEGMENTATION( PREPROC_N4.out.image, ch_freesurferseg, ch_lesion, ch_license )emit: ch_anatomical = PREPROC_N4.out.image // channel: [ [id: string] , path(image) ] ch_brain_mask = BETCROP_ANTSBET.out.mask // channel: [ [id: string] , path(brain_mask) ]
wm_mask = ANATOMICAL_SEGMENTATION.out.wm_mask // channel: [ [id: string] , path(wm_mask) ] gm_mask = ANATOMICAL_SEGMENTATION.out.gm_mask // channel: [ [id: string] , path(gm_mask) ] csf_mask = ANATOMICAL_SEGMENTATION.out.csf_mask // channel: [ [id: string] , path(csf_mask) ]
wm_map = ANATOMICAL_SEGMENTATION.out.wm_map // channel: [ [id: string] , path(wm_map) ] gm_map = ANATOMICAL_SEGMENTATION.out.gm_map // channel: [ [id: string] , path(gm_map) ] csf_map = ANATOMICAL_SEGMENTATION.out.csf_map // channel: [ [id: string] , path(csf_map) ] versions = ch_versions // channel: [ path(versions.yml) ]}