Skip to content

Commit 83c4f1e

Browse files
authored
Merge pull request #1021 from oesteban/maint/datasinks
[MAINT] DataSinks within their workflows
2 parents 7753d83 + 79726ae commit 83c4f1e

File tree

9 files changed

+180
-252
lines changed

9 files changed

+180
-252
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,8 @@ jobs:
518518
fmriprep-docker -i poldracklab/fmriprep:latest \
519519
--config $PWD/nipype.cfg -w /tmp/ds000210/work \
520520
/tmp/data/ds000210 /tmp/ds000210/derivatives participant \
521-
--fs-no-reconall --t2s-coreg --debug --write-graph \
522-
--mem_mb 4096 --nthreads 2 -vv
521+
--fs-no-reconall --t2s-coreg --use-syn-sdc \
522+
--debug --write-graph --mem_mb 4096 --nthreads 2 -vv
523523
- run:
524524
name: Checking outputs of fMRIPrep
525525
command: |

.circleci/ds000210_outputs.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ fmriprep/sub-02/figures/sub-02_T1w_seg_brainmask.svg
2222
fmriprep/sub-02/figures/sub-02_T1w_t1_2_mni.svg
2323
fmriprep/sub-02/figures/sub-02_task-cuedSGT_run-01_echo-1_bold_flirt.svg
2424
fmriprep/sub-02/figures/sub-02_task-cuedSGT_run-01_echo-1_bold_rois.svg
25-
fmriprep/sub-02/figures/sub-02_task-cuedSGT_run-01_echo-2_bold_rois.svg
26-
fmriprep/sub-02/figures/sub-02_task-cuedSGT_run-01_echo-3_bold_rois.svg
25+
fmriprep/sub-02/figures/sub-02_task-cuedSGT_run-01_echo-1_bold_sdc_syn.svg
2726
fmriprep/sub-02/func
2827
fmriprep/sub-02/func/sub-02_task-cuedSGT_run-01_echo-1_bold_confounds.tsv
2928
fmriprep/sub-02/func/sub-02_task-cuedSGT_run-01_echo-1_bold_space-MNI152NLin2009cAsym_brainmask.nii.gz

docs/workflows.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,11 @@ T2* Driven Coregistration
355355
:simple_form: yes
356356

357357
from fmriprep.workflows.bold import init_bold_t2s_wf
358-
wf = init_bold_t2s_wf(echo_times=[13.6, 29.79, 46.59],
359-
mem_gb=3,
360-
omp_nthreads=1)
358+
wf = init_bold_t2s_wf(
359+
bold_echos=['echo1', 'echo2', 'echo3'],
360+
echo_times=[13.6, 29.79, 46.59],
361+
mem_gb=3,
362+
omp_nthreads=1)
361363

362364
If the ``--t2s-coreg`` command line argument is supplied with multi-echo
363365
:abbr:`BOLD (blood-oxygen level-dependent)` data, a T2* map is generated.

fmriprep/workflows/bold/base.py

Lines changed: 42 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
77
88
.. autofunction:: init_func_preproc_wf
9-
.. autofunction:: init_func_reports_wf
109
.. autofunction:: init_func_derivatives_wf
1110
1211
"""
@@ -40,7 +39,6 @@
4039
init_bold_surf_wf,
4140
init_bold_mni_trans_wf,
4241
init_bold_preproc_trans_wf,
43-
init_bold_preproc_report_wf,
4442
)
4543
from .util import init_bold_reference_wf
4644

@@ -269,6 +267,12 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
269267
"Using standard EPI-T1 coregistration.")
270268
t2s_coreg = False
271269

270+
# Switch stc off
271+
if multiecho and run_stc is True:
272+
LOGGER.warning('Slice-timing correction is not available for '
273+
'multiecho BOLD data (not implemented).')
274+
run_stc = False
275+
272276
# Build workflow
273277
workflow = pe.Workflow(name=wf_name)
274278
inputnode = pe.Node(niu.IdentityInterface(
@@ -298,20 +302,13 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
298302
pe_direction=metadata.get("PhaseEncodingDirection")),
299303
name='summary', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True)
300304

301-
func_reports_wf = init_func_reports_wf(reportlets_dir=reportlets_dir,
302-
freesurfer=freesurfer,
303-
use_aroma=use_aroma,
304-
use_syn=use_syn,
305-
t2s_coreg=t2s_coreg)
306-
307305
func_derivatives_wf = init_func_derivatives_wf(output_dir=output_dir,
308306
output_spaces=output_spaces,
309307
template=template,
310308
freesurfer=freesurfer,
311309
use_aroma=use_aroma)
312310

313311
workflow.connect([
314-
(inputnode, func_reports_wf, [('bold_file', 'inputnode.source_file')]),
315312
(inputnode, func_derivatives_wf, [('bold_file', 'inputnode.source_file')]),
316313
(outputnode, func_derivatives_wf, [
317314
('bold_t1', 'inputnode.bold_t1'),
@@ -406,8 +403,10 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
406403
(boldbuffer, bold_split, [('bold_file', 'in_file')]),
407404
# Generate early reference
408405
(inputnode, bold_reference_wf, [('bold_file', 'inputnode.bold_file')]),
409-
(bold_reference_wf, func_reports_wf, [
410-
('outputnode.validation_report', 'inputnode.validation_report')]),
406+
# HMC
407+
(bold_reference_wf, bold_hmc_wf, [
408+
('outputnode.raw_ref_image', 'inputnode.raw_ref_image'),
409+
('outputnode.bold_file', 'inputnode.bold_file')]),
411410
# EPI-T1 registration workflow
412411
(inputnode, bold_reg_wf, [
413412
('bold_file', 'inputnode.name_source'),
@@ -423,10 +422,6 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
423422
('t1_2_fsnative_reverse_transform', 'inputnode.t1_2_fsnative_reverse_transform')]),
424423
(bold_split, bold_reg_wf, [('out_files', 'inputnode.bold_split')]),
425424
(bold_hmc_wf, bold_reg_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]),
426-
(bold_reg_wf, func_reports_wf, [
427-
('outputnode.out_report', 'inputnode.bold_reg_report'),
428-
('outputnode.fallback', 'inputnode.bold_reg_fallback'),
429-
]),
430425
(bold_reg_wf, outputnode, [('outputnode.bold_t1', 'bold_t1'),
431426
('outputnode.bold_aseg_t1', 'bold_aseg_t1'),
432427
('outputnode.bold_aparc_t1', 'bold_aparc_t1')]),
@@ -454,24 +449,21 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
454449
('outputnode.movpar_file', 'inputnode.movpar_file')]),
455450
(bold_reg_wf, bold_confounds_wf, [
456451
('outputnode.itk_t1_to_bold', 'inputnode.t1_bold_xform')]),
457-
(bold_confounds_wf, func_reports_wf, [
458-
('outputnode.rois_report', 'inputnode.bold_rois_report')]),
459452
(bold_confounds_wf, outputnode, [
460453
('outputnode.confounds_file', 'confounds'),
461454
]),
462455
# Connect bold_bold_trans_wf
463456
(inputnode, bold_bold_trans_wf, [
464457
('bold_file', 'inputnode.name_source')]),
465458
(bold_split, bold_bold_trans_wf, [
466-
('out_files', 'inputnode.bold_split')]),
459+
('out_files', 'inputnode.bold_file')]),
467460
(bold_hmc_wf, bold_bold_trans_wf, [
468461
('outputnode.xforms', 'inputnode.hmc_xforms')]),
469462
(bold_bold_trans_wf, bold_confounds_wf, [
470463
('outputnode.bold', 'inputnode.bold'),
471464
('outputnode.bold_mask', 'inputnode.bold_mask')]),
472465
# Summary
473466
(outputnode, summary, [('confounds', 'confounds_file')]),
474-
(summary, func_reports_wf, [('out_report', 'inputnode.summary_report')]),
475467
])
476468

477469
if fmaps:
@@ -506,94 +498,45 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
506498
('outputnode.syn_bold_ref', 'inputnode.in_post')]),
507499
])
508500

509-
# Fill-in datasinks of reportlets seen so far
510-
for node in workflow.list_node_names():
511-
if node.split('.')[-1].startswith('ds_report'):
512-
workflow.get_node(node).inputs.base_directory = reportlets_dir
513-
workflow.get_node(node).inputs.source_file = bold_file
514-
515501
# if multiecho data, select first echo for hmc correction
516502
if multiecho:
517503
inputnode.iterables = ('bold_file', bold_file)
518504

519-
me_first_echo = pe.JoinNode(interface=FirstEcho(te_list=tes),
520-
joinfield=['in_files', 'ref_imgs'],
521-
joinsource='inputnode',
522-
name='me_first_echo')
505+
me_first_echo = pe.Node(FirstEcho(
506+
te_list=tes, in_files=bold_file, ref_imgs=bold_file),
507+
name='me_first_echo')
508+
# Replace reference with the echo selected with FirstEcho
509+
workflow.disconnect([
510+
(inputnode, bold_reference_wf, [
511+
('bold_file', 'inputnode.bold_file')]),
512+
])
523513
workflow.connect([
524-
(bold_reference_wf, me_first_echo, [
525-
('outputnode.bold_file', 'in_files'),
526-
('outputnode.raw_ref_image', 'ref_imgs')]),
527-
(me_first_echo, bold_hmc_wf, [
528-
('first_image', 'inputnode.bold_file'),
529-
('first_ref_image', 'inputnode.raw_ref_image')])
514+
(me_first_echo, bold_reference_wf, [
515+
('first_image', 'inputnode.bold_file')])
530516
])
531517

532518
if t2s_coreg:
533-
# use a joinNode to gather all preprocessed echos
534-
join_split_echos = pe.JoinNode(niu.IdentityInterface(fields=['echo_files']),
535-
joinsource='inputnode',
536-
joinfield='echo_files',
537-
name='join_split_echos')
538-
539519
# create a T2* map
540-
bold_t2s_wf = init_bold_t2s_wf(echo_times=tes,
541-
name='bold_t2s_wf',
542-
mem_gb=mem_gb['filesize'],
543-
omp_nthreads=omp_nthreads)
544-
545-
subset_reg_reports = pe.JoinNode(niu.Select(index=0),
546-
name='subset_reg_reports',
547-
joinsource=inputnode,
548-
joinfield=['inlist'])
549-
550-
subset_reg_fallbacks = pe.JoinNode(niu.Select(index=0),
551-
name='subset_reg_fallbacks',
552-
joinsource=inputnode,
553-
joinfield=['inlist'])
554-
555-
first_echo = pe.Node(niu.IdentityInterface(fields=['first_echo']),
556-
name='first_echo')
557-
first_echo.inputs.first_echo = ref_file
558-
559-
# remove duplicate registration reports
520+
bold_t2s_wf = init_bold_t2s_wf(bold_echos=bold_file,
521+
echo_times=tes,
522+
mem_gb=mem_gb['resampled'],
523+
omp_nthreads=omp_nthreads,
524+
name='bold_t2s_wf')
525+
bold_t2s_wf.inputs.inputnode.name_source = ref_file
526+
527+
# Replace EPI-to-T1w registration inputs
560528
workflow.disconnect([
561-
(bold_reg_wf, func_reports_wf, [
562-
('outputnode.out_report', 'inputnode.bold_reg_report'),
563-
('outputnode.fallback', 'inputnode.bold_reg_fallback')]),
564529
(bold_sdc_wf, bold_reg_wf, [
565-
('outputnode.out_warp', 'inputnode.fieldwarp'),
566530
('outputnode.bold_ref_brain', 'inputnode.ref_bold_brain'),
567531
('outputnode.bold_mask', 'inputnode.ref_bold_mask')]),
568532
])
569-
570533
workflow.connect([
571-
(first_echo, func_reports_wf, [
572-
('first_echo', 'inputnode.first_echo')]),
573-
(bold_split, join_split_echos, [
574-
('out_files', 'echo_files')]),
575-
(join_split_echos, bold_t2s_wf, [
576-
('echo_files', 'inputnode.echo_split')]),
577534
(bold_hmc_wf, bold_t2s_wf, [
578535
('outputnode.xforms', 'inputnode.hmc_xforms')]),
579536
(bold_t2s_wf, bold_reg_wf, [
580537
('outputnode.t2s_map', 'inputnode.ref_bold_brain'),
581538
('outputnode.oc_mask', 'inputnode.ref_bold_mask')]),
582-
(bold_reg_wf, subset_reg_reports, [
583-
('outputnode.out_report', 'inlist')]),
584-
(bold_reg_wf, subset_reg_fallbacks, [
585-
('outputnode.fallback', 'inlist')]),
586-
(subset_reg_reports, func_reports_wf, [
587-
('out', 'inputnode.bold_reg_report')]),
588-
(subset_reg_fallbacks, func_reports_wf, [
589-
('out', 'inputnode.bold_reg_fallback')]),
590539
])
591-
else:
592-
workflow.connect([
593-
(bold_reference_wf, bold_hmc_wf, [
594-
('outputnode.raw_ref_image', 'inputnode.raw_ref_image'),
595-
('outputnode.bold_file', 'inputnode.bold_file')])
596-
])
597540

598541
# Map final BOLD mask into T1w space (if required)
599542
if 'T1w' in output_spaces:
@@ -684,8 +627,6 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
684627
('outputnode.melodic_mix', 'melodic_mix'),
685628
('outputnode.nonaggr_denoised_file', 'nonaggr_denoised_file')]),
686629
(join, outputnode, [('out_file', 'confounds')]),
687-
(ica_aroma_wf, func_reports_wf, [
688-
('outputnode.out_report', 'inputnode.ica_aroma_report')]),
689630
])
690631

691632
# SURFACES ##################################################################################
@@ -706,89 +647,28 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer,
706647
])
707648

708649
# REPORTING ############################################################
709-
bold_bold_report_wf = init_bold_preproc_report_wf(
710-
mem_gb=mem_gb['resampled'],
711-
reportlets_dir=reportlets_dir
712-
)
713-
714-
workflow.connect([
715-
(inputnode, bold_bold_report_wf, [
716-
('bold_file', 'inputnode.name_source'),
717-
('bold_file', 'inputnode.in_pre')]), # This should be after STC
718-
(bold_bold_trans_wf, bold_bold_report_wf, [
719-
('outputnode.bold', 'inputnode.in_post')]),
720-
])
721-
722-
return workflow
723-
724-
725-
def init_func_reports_wf(reportlets_dir, freesurfer, use_aroma,
726-
use_syn, t2s_coreg, name='func_reports_wf'):
727-
"""
728-
Set up a battery of datasinks to store reports in the right location
729-
"""
730-
workflow = pe.Workflow(name=name)
731-
732-
inputnode = pe.Node(
733-
niu.IdentityInterface(
734-
fields=['source_file', 'summary_report', 'validation_report',
735-
'bold_reg_report', 'bold_reg_fallback', 'bold_rois_report',
736-
'syn_sdc_report', 'ica_aroma_report', 'first_echo']),
737-
name='inputnode')
738-
739-
ds_summary_report = pe.Node(
740-
DerivativesDataSink(base_directory=reportlets_dir,
741-
suffix='summary'),
742-
name='ds_summary_report', run_without_submitting=True,
650+
ds_report_summary = pe.Node(
651+
DerivativesDataSink(suffix='summary'),
652+
name='ds_report_summary', run_without_submitting=True,
743653
mem_gb=DEFAULT_MEMORY_MIN_GB)
744654

745-
ds_validation_report = pe.Node(
655+
ds_report_validation = pe.Node(
746656
DerivativesDataSink(base_directory=reportlets_dir,
747657
suffix='validation'),
748-
name='ds_validation_report', run_without_submitting=True,
749-
mem_gb=DEFAULT_MEMORY_MIN_GB)
750-
751-
ds_bold_rois_report = pe.Node(
752-
DerivativesDataSink(base_directory=reportlets_dir,
753-
suffix='rois'),
754-
name='ds_bold_rois_report', run_without_submitting=True,
755-
mem_gb=DEFAULT_MEMORY_MIN_GB)
756-
757-
def _bold_reg_suffix(fallback, freesurfer):
758-
if fallback:
759-
return 'coreg' if freesurfer else 'flirt'
760-
return 'bbr' if freesurfer else 'flt_bbr'
761-
762-
ds_bold_reg_report = pe.Node(
763-
DerivativesDataSink(base_directory=reportlets_dir),
764-
name='ds_bold_reg_report', run_without_submitting=True,
765-
mem_gb=DEFAULT_MEMORY_MIN_GB)
766-
767-
ds_ica_aroma_report = pe.Node(
768-
DerivativesDataSink(base_directory=reportlets_dir,
769-
suffix='ica_aroma'),
770-
name='ds_ica_aroma_report', run_without_submitting=True,
658+
name='ds_report_validation', run_without_submitting=True,
771659
mem_gb=DEFAULT_MEMORY_MIN_GB)
772660

773661
workflow.connect([
774-
(inputnode, ds_bold_reg_report, [
775-
('first_echo' if t2s_coreg else 'source_file', 'source_file')]),
776-
(inputnode, ds_summary_report, [('source_file', 'source_file'),
777-
('summary_report', 'in_file')]),
778-
(inputnode, ds_validation_report, [('source_file', 'source_file'),
779-
('validation_report', 'in_file')]),
780-
(inputnode, ds_bold_rois_report, [('source_file', 'source_file'),
781-
('bold_rois_report', 'in_file')]),
782-
(inputnode, ds_bold_reg_report, [
783-
('bold_reg_report', 'in_file'),
784-
(('bold_reg_fallback', _bold_reg_suffix, freesurfer), 'suffix')]),
662+
(summary, ds_report_summary, [('out_report', 'in_file')]),
663+
(bold_reference_wf, ds_report_validation, [
664+
('outputnode.validation_report', 'in_file')]),
785665
])
786666

787-
if use_aroma:
788-
workflow.connect([
789-
(inputnode, ds_ica_aroma_report, [('source_file', 'source_file'),
790-
('ica_aroma_report', 'in_file')]),
791-
])
667+
# Fill-in datasinks of reportlets seen so far
668+
for node in workflow.list_node_names():
669+
if node.split('.')[-1].startswith('ds_report'):
670+
workflow.get_node(node).inputs.base_directory = reportlets_dir
671+
workflow.get_node(node).inputs.source_file = ref_file
792672

793673
return workflow
794674

0 commit comments

Comments
 (0)