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
@@ -555,11 +556,61 @@ def init_single_subject_wf(subject_id: str):
555
556
filters = config .execution .get ().get ('bids_filters' , {}).get ('fmap' ),
556
557
)
557
558
559
+ fmap_merge_nodes = {
560
+ field : pe .Node (niu .Merge (2 ), name = f'{ field } _merge' , run_without_submitting = True )
561
+ for field in ['fmap' , 'fmap_ref' , 'fmap_coeff' , 'fmap_mask' , 'fmap_id' , 'sdc_method' ]
562
+ }
563
+
564
+ fmap_buffer = pe .Node (
565
+ niu .IdentityInterface (
566
+ fields = ['fmap' , 'fmap_ref' , 'fmap_coeff' , 'fmap_mask' , 'fmap_id' , 'sdc_method' ]
567
+ ),
568
+ name = 'fmap_buffer' ,
569
+ )
570
+
571
+ workflow .connect (
572
+ [(fmap_merge_nodes [field ], fmap_buffer , [('out' , field )]) for field in fmap_merge_nodes ]
573
+ )
574
+
575
+ missing_estimators = []
558
576
if fmap_estimators :
577
+ # Map fmapid entity to internal bids_id
578
+ fmapid_map = {re .sub (r'[^a-zA-Z0-9]' , '' , e .bids_id ): e .bids_id for e in fmap_estimators }
579
+ pared_cache = {
580
+ fmapid_map [fmapid ]: fmap_cache [fmapid ] for fmapid in fmap_cache if fmapid in fmapid_map
581
+ }
582
+
583
+ missing_estimators = [e for e in fmap_estimators if e .bids_id not in pared_cache ]
584
+
585
+ if pared_cache :
586
+ config .loggers .workflow .info (
587
+ 'Precomputed B0 field inhomogeneity maps found for the following '
588
+ f'{ len (pared_cache )} estimator(s): { list (pared_cache )} .'
589
+ )
590
+
591
+ fmap_merge_nodes ['fmap' ].inputs .in1 = [
592
+ pared_cache [fmapid ]['fieldmap' ] for fmapid in pared_cache
593
+ ]
594
+ fmap_merge_nodes ['fmap_ref' ].inputs .in1 = [
595
+ pared_cache [fmapid ]['magnitude' ] for fmapid in pared_cache
596
+ ]
597
+ fmap_merge_nodes ['fmap_coeff' ].inputs .in1 = [
598
+ pared_cache [fmapid ]['coeffs' ] for fmapid in pared_cache
599
+ ]
600
+ # Note that masks are not emitted. The BOLD-fmap transforms cannot be
601
+ # computed with precomputed fieldmaps until we either start emitting masks
602
+ # or start skull-stripping references on the fly.
603
+ fmap_merge_nodes ['fmap_mask' ].inputs .in1 = [
604
+ pared_cache [fmapid ].get ('mask' , 'MISSING' ) for fmapid in pared_cache
605
+ ]
606
+ fmap_merge_nodes ['fmap_id' ].inputs .in1 = list (pared_cache )
607
+ fmap_merge_nodes ['sdc_method' ].inputs .in1 = ['precomputed' ] * len (pared_cache )
608
+
609
+ if missing_estimators :
559
610
config .loggers .workflow .info (
560
611
'B0 field inhomogeneity map will be estimated with the following '
561
- f'{ len (fmap_estimators )} estimator(s): '
562
- f'{ [e .method for e in fmap_estimators ]} .'
612
+ f'{ len (missing_estimators )} estimator(s): '
613
+ f'{ [e .method for e in missing_estimators ]} .'
563
614
)
564
615
565
616
from sdcflows import fieldmaps as fm
@@ -585,24 +636,31 @@ def init_single_subject_wf(subject_id: str):
585
636
if node .split ('.' )[- 1 ].startswith ('ds_' ):
586
637
fmap_wf .get_node (node ).interface .out_path_base = ''
587
638
639
+ workflow .connect ([
640
+ (fmap_wf , fmap_merge_nodes [field ], [
641
+ # We get "sdc_method" as "method" from estimator workflows
642
+ # All else stays the same, and no other sdc_ prefixes are used
643
+ (f'outputnode.{ field .removeprefix ("sdc_" )} ' , 'in2' ),
644
+ ])
645
+ for field in fmap_merge_nodes
646
+ ]) # fmt:skip
647
+
588
648
fmap_select_std = pe .Node (
589
649
KeySelect (fields = ['std2anat_xfm' ], key = 'MNI152NLin2009cAsym' ),
590
650
name = 'fmap_select_std' ,
591
651
run_without_submitting = True ,
592
652
)
593
653
if any (estimator .method == fm .EstimatorType .ANAT for estimator in fmap_estimators ):
594
- # fmt:off
595
654
workflow .connect ([
596
655
(anat_fit_wf , fmap_select_std , [
597
656
('outputnode.std2anat_xfm' , 'std2anat_xfm' ),
598
657
('outputnode.template' , 'keys' )]),
599
- ])
600
- # fmt:on
658
+ ]) # fmt:skip
601
659
602
660
for estimator in fmap_estimators :
603
661
config .loggers .workflow .info (
604
662
f"""\
605
- Setting- up fieldmap "{ estimator .bids_id } " ({ estimator .method } ) with \
663
+ Setting up fieldmap "{ estimator .bids_id } " ({ estimator .method } ) with \
606
664
<{ ', ' .join (s .path .name for s in estimator .sources )} >"""
607
665
)
608
666
@@ -645,7 +703,6 @@ def init_single_subject_wf(subject_id: str):
645
703
syn_preprocessing_wf .inputs .inputnode .in_epis = sources
646
704
syn_preprocessing_wf .inputs .inputnode .in_meta = source_meta
647
705
648
- # fmt:off
649
706
workflow .connect ([
650
707
(anat_fit_wf , syn_preprocessing_wf , [
651
708
('outputnode.t1w_preproc' , 'inputnode.in_anat' ),
@@ -661,8 +718,7 @@ def init_single_subject_wf(subject_id: str):
661
718
('outputnode.anat_mask' , f'in_{ estimator .bids_id } .anat_mask' ),
662
719
('outputnode.sd_prior' , f'in_{ estimator .bids_id } .sd_prior' ),
663
720
]),
664
- ])
665
- # fmt:on
721
+ ]) # fmt:skip
666
722
667
723
# Append the functional section to the existing anatomical excerpt
668
724
# That way we do not need to stream down the number of bold datasets
@@ -729,18 +785,11 @@ def init_single_subject_wf(subject_id: str):
729
785
'inputnode.sphere_reg_fsLR' ,
730
786
),
731
787
]),
788
+ (fmap_buffer , bold_wf , [
789
+ (field , f'inputnode.{ field } ' )
790
+ for field in fmap_merge_nodes
791
+ ]),
732
792
]) # fmt:skip
733
- if fieldmap_id :
734
- workflow .connect ([
735
- (fmap_wf , bold_wf , [
736
- ('outputnode.fmap' , 'inputnode.fmap' ),
737
- ('outputnode.fmap_ref' , 'inputnode.fmap_ref' ),
738
- ('outputnode.fmap_coeff' , 'inputnode.fmap_coeff' ),
739
- ('outputnode.fmap_mask' , 'inputnode.fmap_mask' ),
740
- ('outputnode.fmap_id' , 'inputnode.fmap_id' ),
741
- ('outputnode.method' , 'inputnode.sdc_method' ),
742
- ]),
743
- ]) # fmt:skip
744
793
745
794
if config .workflow .level == 'full' :
746
795
if template_iterator_wf is not None :
0 commit comments