30
30
"""
31
31
32
32
import os
33
+ import re
33
34
import sys
34
35
import warnings
35
36
from copy import deepcopy
@@ -337,6 +338,9 @@ def init_single_subject_wf(subject_id: str):
337
338
338
339
# allow to run with anat-fast-track on fMRI-only dataset
339
340
if 't1w_preproc' in anatomical_cache and not subject_data ['t1w' ]:
341
+ config .loggers .workflow .debug (
342
+ 'No T1w image found; using precomputed T1w image: %s' , anatomical_cache ['t1w_preproc' ]
343
+ )
340
344
workflow .connect ([
341
345
(bidssrc , bids_info , [(('bold' , fix_multi_T1w_source_name ), 'in_file' )]),
342
346
(anat_fit_wf , summary , [('outputnode.t1w_preproc' , 't1w' )]),
@@ -533,7 +537,21 @@ def init_single_subject_wf(subject_id: str):
533
537
if config .workflow .anat_only :
534
538
return clean_datasinks (workflow )
535
539
536
- fmap_estimators , estimator_map = map_fieldmap_estimation (
540
+ fmap_cache = {}
541
+ if config .execution .derivatives :
542
+ from fmriprep .utils .bids import collect_fieldmaps
543
+
544
+ for deriv_dir in config .execution .derivatives .values ():
545
+ fmaps = collect_fieldmaps (
546
+ derivatives_dir = deriv_dir ,
547
+ entities = {'subject' : subject_id },
548
+ )
549
+ config .loggers .workflow .debug (
550
+ 'Detected precomputed fieldmaps in %s for fieldmap IDs: %s' , deriv_dir , list (fmaps )
551
+ )
552
+ fmap_cache .update (fmaps )
553
+
554
+ all_estimators , estimator_map = map_fieldmap_estimation (
537
555
layout = config .execution .layout ,
538
556
subject_id = subject_id ,
539
557
bold_data = bold_runs ,
@@ -543,6 +561,48 @@ def init_single_subject_wf(subject_id: str):
543
561
filters = config .execution .get ().get ('bids_filters' , {}).get ('fmap' ),
544
562
)
545
563
564
+ fmap_buffers = {
565
+ field : pe .Node (niu .Merge (2 ), name = f'{ field } _merge' , run_without_submitting = True )
566
+ for field in ['fmap' , 'fmap_ref' , 'fmap_coeff' , 'fmap_mask' , 'fmap_id' , 'sdc_method' ]
567
+ }
568
+
569
+ fmap_estimators = []
570
+ if all_estimators :
571
+ # Find precomputed fieldmaps that apply to this workflow
572
+ pared_cache = {}
573
+ for est in all_estimators :
574
+ if found := fmap_cache .get (re .sub (r'[^a-zA-Z0-9]' , '' , est .bids_id )):
575
+ pared_cache [est .bids_id ] = found
576
+ else :
577
+ fmap_estimators .append (est )
578
+
579
+ if pared_cache :
580
+ config .loggers .workflow .info (
581
+ 'Precomputed B0 field inhomogeneity maps found for the following '
582
+ f'{ len (pared_cache )} estimator(s): { list (pared_cache )} .'
583
+ )
584
+
585
+ fieldmaps = [fmap ['fieldmap' ] for fmap in pared_cache .values ()]
586
+ refs = [fmap ['magnitude' ] for fmap in pared_cache .values ()]
587
+ coeffs = [fmap ['coeffs' ] for fmap in pared_cache .values ()]
588
+ config .loggers .workflow .debug ('Reusing fieldmaps: %s' , fieldmaps )
589
+ config .loggers .workflow .debug ('Reusing references: %s' , refs )
590
+ config .loggers .workflow .debug ('Reusing coefficients: %s' , coeffs )
591
+
592
+ fmap_buffers ['fmap' ].inputs .in1 = fieldmaps
593
+ fmap_buffers ['fmap_ref' ].inputs .in1 = refs
594
+ fmap_buffers ['fmap_coeff' ].inputs .in1 = coeffs
595
+
596
+ # Note that masks are not emitted. The BOLD-fmap transforms cannot be
597
+ # computed with precomputed fieldmaps until we either start emitting masks
598
+ # or start skull-stripping references on the fly.
599
+ fmap_buffers ['fmap_mask' ].inputs .in1 = [
600
+ pared_cache [fmapid ].get ('mask' , 'MISSING' ) for fmapid in pared_cache
601
+ ]
602
+ fmap_buffers ['fmap_id' ].inputs .in1 = list (pared_cache )
603
+ fmap_buffers ['sdc_method' ].inputs .in1 = ['precomputed' ] * len (pared_cache )
604
+
605
+ # Estimators without precomputed fieldmaps
546
606
if fmap_estimators :
547
607
config .loggers .workflow .info (
548
608
'B0 field inhomogeneity map will be estimated with the following '
@@ -573,24 +633,31 @@ def init_single_subject_wf(subject_id: str):
573
633
if node .split ('.' )[- 1 ].startswith ('ds_' ):
574
634
fmap_wf .get_node (node ).interface .out_path_base = ''
575
635
636
+ workflow .connect ([
637
+ (fmap_wf , fmap_buffers [field ], [
638
+ # We get "sdc_method" as "method" from estimator workflows
639
+ # All else stays the same, and no other sdc_ prefixes are used
640
+ (f'outputnode.{ field .removeprefix ("sdc_" )} ' , 'in2' ),
641
+ ])
642
+ for field in fmap_buffers
643
+ ]) # fmt:skip
644
+
576
645
fmap_select_std = pe .Node (
577
646
KeySelect (fields = ['std2anat_xfm' ], key = 'MNI152NLin2009cAsym' ),
578
647
name = 'fmap_select_std' ,
579
648
run_without_submitting = True ,
580
649
)
581
650
if any (estimator .method == fm .EstimatorType .ANAT for estimator in fmap_estimators ):
582
- # fmt:off
583
651
workflow .connect ([
584
652
(anat_fit_wf , fmap_select_std , [
585
653
('outputnode.std2anat_xfm' , 'std2anat_xfm' ),
586
654
('outputnode.template' , 'keys' )]),
587
- ])
588
- # fmt:on
655
+ ]) # fmt:skip
589
656
590
657
for estimator in fmap_estimators :
591
658
config .loggers .workflow .info (
592
659
f"""\
593
- Setting- up fieldmap "{ estimator .bids_id } " ({ estimator .method } ) with \
660
+ Setting up fieldmap "{ estimator .bids_id } " ({ estimator .method } ) with \
594
661
<{ ', ' .join (s .path .name for s in estimator .sources )} >"""
595
662
)
596
663
@@ -633,7 +700,6 @@ def init_single_subject_wf(subject_id: str):
633
700
syn_preprocessing_wf .inputs .inputnode .in_epis = sources
634
701
syn_preprocessing_wf .inputs .inputnode .in_meta = source_meta
635
702
636
- # fmt:off
637
703
workflow .connect ([
638
704
(anat_fit_wf , syn_preprocessing_wf , [
639
705
('outputnode.t1w_preproc' , 'inputnode.in_anat' ),
@@ -649,8 +715,7 @@ def init_single_subject_wf(subject_id: str):
649
715
('outputnode.anat_mask' , f'in_{ estimator .bids_id } .anat_mask' ),
650
716
('outputnode.sd_prior' , f'in_{ estimator .bids_id } .sd_prior' ),
651
717
]),
652
- ])
653
- # fmt:on
718
+ ]) # fmt:skip
654
719
655
720
# Append the functional section to the existing anatomical excerpt
656
721
# That way we do not need to stream down the number of bold datasets
@@ -718,17 +783,11 @@ def init_single_subject_wf(subject_id: str):
718
783
),
719
784
]),
720
785
]) # fmt:skip
721
- if fieldmap_id :
722
- workflow .connect ([
723
- (fmap_wf , bold_wf , [
724
- ('outputnode.fmap' , 'inputnode.fmap' ),
725
- ('outputnode.fmap_ref' , 'inputnode.fmap_ref' ),
726
- ('outputnode.fmap_coeff' , 'inputnode.fmap_coeff' ),
727
- ('outputnode.fmap_mask' , 'inputnode.fmap_mask' ),
728
- ('outputnode.fmap_id' , 'inputnode.fmap_id' ),
729
- ('outputnode.method' , 'inputnode.sdc_method' ),
730
- ]),
731
- ]) # fmt:skip
786
+
787
+ workflow .connect ([
788
+ (buffer , bold_wf , [('out' , f'inputnode.{ field } ' )])
789
+ for field , buffer in fmap_buffers .items ()
790
+ ]) # fmt:skip
732
791
733
792
if config .workflow .level == 'full' :
734
793
if template_iterator_wf is not None :
0 commit comments