Skip to content

Commit e682dc7

Browse files
authored
Merge pull request #393 from bpinsard/enh/fs_long
Add --fs-no-resume option to reuse existing freesurfer outputs without resuming (eg. longitudinal pipeline base)
2 parents c72116a + ba4611b commit e682dc7

File tree

5 files changed

+107
-33
lines changed

5 files changed

+107
-33
lines changed

.circleci/config.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,24 @@ jobs:
541541
- store_artifacts:
542542
path: /tmp/ds005/derivatives
543543
destination: fasttrack
544+
- run:
545+
name: Check fs-no-resume using existing freesufer output
546+
no_output_timeout: 5m
547+
command: |
548+
bash /tmp/src/smriprep/.circleci/ds005_run.sh --fs-no-resume
549+
- run:
550+
name: Clean working directory
551+
when: on_fail
552+
command: |
553+
rm -rf /tmp/ds005/work/smriprep_wf/fsdir_run_*/
554+
find /tmp/ds005/work \( -name "*.nii.gz" -or -name "*.nii" -or -name "*.gii" -or -name "*.h5" \) \
555+
-exec sh -c 'rm -f {}; touch {}' \;
556+
- store_artifacts:
557+
path: /tmp/ds005/work
558+
destination: fs_no_resume
559+
- store_artifacts:
560+
path: /tmp/ds005/derivatives
561+
destination: fs_no_resume
544562
ds054:
545563
<<: *machine_defaults
546564
environment:

smriprep/cli/run.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ def get_parser():
214214
help='Path to existing FreeSurfer subjects directory to reuse. '
215215
'(default: OUTPUT_DIR/freesurfer)',
216216
)
217+
g_fs.add_argument(
218+
'--fs-no-resume',
219+
action='store_true',
220+
dest='fs_no_resume',
221+
help='EXPERT: Import pre-computed FreeSurfer reconstruction without resuming. '
222+
'The user is responsible for ensuring that all necessary files are present.',
223+
)
217224
g_fs.add_argument(
218225
'--cifti-output',
219226
nargs='?',
@@ -616,6 +623,7 @@ def build_workflow(opts, retval):
616623
freesurfer=opts.run_reconall,
617624
fs_subjects_dir=opts.fs_subjects_dir,
618625
hires=opts.hires,
626+
fs_no_resume=opts.fs_no_resume,
619627
layout=layout,
620628
longitudinal=opts.longitudinal,
621629
low_mem=opts.low_mem,

smriprep/workflows/anatomical.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def init_anat_preproc_wf(
112112
cifti_output: ty.Literal['91k', '170k', False] = False,
113113
name: str = 'anat_preproc_wf',
114114
skull_strip_fixed_seed: bool = False,
115+
fs_no_resume: bool = False,
115116
):
116117
"""
117118
Stage the anatomical preprocessing steps of *sMRIPrep*.
@@ -184,6 +185,10 @@ def init_anat_preproc_wf(
184185
Do not use a random seed for skull-stripping - will ensure
185186
run-to-run replicability when used with --omp-nthreads 1
186187
(default: ``False``).
188+
fs_no_resume : bool
189+
EXPERT: Import pre-computed FreeSurfer reconstruction without resuming.
190+
The user is responsible for ensuring that all necessary files are present.
191+
(default: ``False``).
187192
188193
Inputs
189194
------
@@ -276,6 +281,7 @@ def init_anat_preproc_wf(
276281
sloppy=sloppy,
277282
omp_nthreads=omp_nthreads,
278283
skull_strip_fixed_seed=skull_strip_fixed_seed,
284+
fs_no_resume=fs_no_resume,
279285
)
280286
template_iterator_wf = init_template_iterator_wf(spaces=spaces, sloppy=sloppy)
281287
ds_std_volumes_wf = init_ds_anat_volumes_wf(
@@ -459,6 +465,7 @@ def init_anat_fit_wf(
459465
sloppy: bool = False,
460466
name='anat_fit_wf',
461467
skull_strip_fixed_seed: bool = False,
468+
fs_no_resume: bool = False,
462469
):
463470
"""
464471
Stage the anatomical preprocessing steps of *sMRIPrep*.
@@ -1042,6 +1049,7 @@ def init_anat_fit_wf(
10421049
name='surface_recon_wf',
10431050
omp_nthreads=omp_nthreads,
10441051
hires=hires,
1052+
fs_no_resume=fs_no_resume,
10451053
precomputed=precomputed,
10461054
)
10471055
if t2w or flair:

smriprep/workflows/base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def init_smriprep_wf(
4747
freesurfer,
4848
fs_subjects_dir,
4949
hires,
50+
fs_no_resume,
5051
layout,
5152
longitudinal,
5253
low_mem,
@@ -89,6 +90,7 @@ def init_smriprep_wf(
8990
freesurfer=True,
9091
fs_subjects_dir=None,
9192
hires=True,
93+
fs_no_resume=False,
9294
layout=BIDSLayout('.'),
9395
longitudinal=False,
9496
low_mem=False,
@@ -179,6 +181,7 @@ def init_smriprep_wf(
179181
freesurfer=freesurfer,
180182
derivatives=derivatives,
181183
hires=hires,
184+
fs_no_resume=fs_no_resume,
182185
layout=layout,
183186
longitudinal=longitudinal,
184187
low_mem=low_mem,
@@ -215,6 +218,7 @@ def init_single_subject_wf(
215218
derivatives,
216219
freesurfer,
217220
hires,
221+
fs_no_resume,
218222
layout,
219223
longitudinal,
220224
low_mem,
@@ -259,6 +263,7 @@ def init_single_subject_wf(
259263
freesurfer=True,
260264
derivatives=[],
261265
hires=True,
266+
fs_no_resume=False,
262267
layout=BIDSLayout('.'),
263268
longitudinal=False,
264269
low_mem=False,
@@ -287,6 +292,9 @@ def init_single_subject_wf(
287292
Enable FreeSurfer surface reconstruction (may increase runtime)
288293
hires : :obj:`bool`
289294
Enable sub-millimeter preprocessing in FreeSurfer
295+
fs_no_resume : bool
296+
Adjust pipeline to reuse base template
297+
of an existing longitudinal freesurfer output
290298
layout : BIDSLayout object
291299
BIDS dataset layout
292300
longitudinal : :obj:`bool`
@@ -419,6 +427,7 @@ def init_single_subject_wf(
419427
precomputed=deriv_cache,
420428
freesurfer=freesurfer,
421429
hires=hires,
430+
fs_no_resume=fs_no_resume,
422431
longitudinal=longitudinal,
423432
msm_sulc=msm_sulc,
424433
name='anat_preproc_wf',

smriprep/workflows/surfaces.py

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def init_surface_recon_wf(
6565
*,
6666
omp_nthreads: int,
6767
hires: bool,
68+
fs_no_resume: bool,
6869
precomputed: dict,
6970
name='surface_recon_wf',
7071
):
@@ -130,14 +131,21 @@ def init_surface_recon_wf(
130131
:simple_form: yes
131132
132133
from smriprep.workflows.surfaces import init_surface_recon_wf
133-
wf = init_surface_recon_wf(omp_nthreads=1, hires=True, precomputed={})
134+
wf = init_surface_recon_wf(
135+
omp_nthreads=1,
136+
hires=True,
137+
fs_no_resume=False,
138+
precomputed={})
134139
135140
Parameters
136141
----------
137142
omp_nthreads : int
138143
Maximum number of threads an individual process may use
139144
hires : bool
140145
Enable sub-millimeter preprocessing in FreeSurfer
146+
fs_no_resume : bool
147+
use precomputed freesurfer without attempting to resume
148+
(eg. for longitudinal base or fastsurfer)
141149
142150
Inputs
143151
------
@@ -239,39 +247,62 @@ def init_surface_recon_wf(
239247
name='sync',
240248
)
241249

250+
if not fs_no_resume:
251+
workflow.connect([
252+
# Configuration
253+
(inputnode, recon_config, [('t1w', 't1w_list'),
254+
('t2w', 't2w_list'),
255+
('flair', 'flair_list')]),
256+
# Passing subjects_dir / subject_id enforces serial order
257+
(inputnode, autorecon1, [('subjects_dir', 'subjects_dir'),
258+
('subject_id', 'subject_id')]),
259+
(autorecon1, skull_strip_extern, [('subjects_dir', 'subjects_dir'),
260+
('subject_id', 'subject_id')]),
261+
(skull_strip_extern, autorecon_resume_wf, [('subjects_dir', 'inputnode.subjects_dir'),
262+
('subject_id', 'inputnode.subject_id')]),
263+
# Reconstruction phases
264+
(inputnode, autorecon1, [('t1w', 'T1_files')]),
265+
(inputnode, fov_check, [('t1w', 'in_files')]),
266+
(fov_check, autorecon1, [('out', 'flags')]),
267+
(recon_config, autorecon1, [('t2w', 'T2_file'),
268+
('flair', 'FLAIR_file'),
269+
('hires', 'hires'),
270+
# First run only (recon-all saves expert options)
271+
('mris_inflate', 'mris_inflate')]),
272+
(inputnode, skull_strip_extern, [('skullstripped_t1', 'in_brain')]),
273+
(recon_config, autorecon_resume_wf, [('use_t2w', 'inputnode.use_T2'),
274+
('use_flair', 'inputnode.use_FLAIR')]),
275+
# Generate mid-thickness surfaces
276+
(autorecon_resume_wf, get_surfaces, [
277+
('outputnode.subjects_dir', 'subjects_dir'),
278+
('outputnode.subject_id', 'subject_id'),
279+
]),
280+
(autorecon_resume_wf, save_midthickness, [
281+
('outputnode.subjects_dir', 'base_directory'),
282+
('outputnode.subject_id', 'container'),
283+
]),
284+
]) # fmt:skip
285+
else:
286+
# Pretend to be the autorecon1 node so fsnative2t1w_xfm gets run ASAP
287+
fs_base_inputs = autorecon1 = pe.Node(nio.FreeSurferSource(), name='fs_base_inputs')
288+
289+
workflow.connect([
290+
(inputnode, fs_base_inputs, [
291+
('subjects_dir', 'subjects_dir'),
292+
('subject_id', 'subject_id'),
293+
]),
294+
# Generate mid-thickness surfaces
295+
(inputnode, get_surfaces, [
296+
('subjects_dir', 'subjects_dir'),
297+
('subject_id', 'subject_id'),
298+
]),
299+
(inputnode, save_midthickness, [
300+
('subjects_dir', 'base_directory'),
301+
('subject_id', 'container'),
302+
]),
303+
]) # fmt:skip
304+
242305
workflow.connect([
243-
# Configuration
244-
(inputnode, recon_config, [('t1w', 't1w_list'),
245-
('t2w', 't2w_list'),
246-
('flair', 'flair_list')]),
247-
# Passing subjects_dir / subject_id enforces serial order
248-
(inputnode, autorecon1, [('subjects_dir', 'subjects_dir'),
249-
('subject_id', 'subject_id')]),
250-
(autorecon1, skull_strip_extern, [('subjects_dir', 'subjects_dir'),
251-
('subject_id', 'subject_id')]),
252-
(skull_strip_extern, autorecon_resume_wf, [('subjects_dir', 'inputnode.subjects_dir'),
253-
('subject_id', 'inputnode.subject_id')]),
254-
# Reconstruction phases
255-
(inputnode, autorecon1, [('t1w', 'T1_files')]),
256-
(inputnode, fov_check, [('t1w', 'in_files')]),
257-
(fov_check, autorecon1, [('out', 'flags')]),
258-
(recon_config, autorecon1, [('t2w', 'T2_file'),
259-
('flair', 'FLAIR_file'),
260-
('hires', 'hires'),
261-
# First run only (recon-all saves expert options)
262-
('mris_inflate', 'mris_inflate')]),
263-
(inputnode, skull_strip_extern, [('skullstripped_t1', 'in_brain')]),
264-
(recon_config, autorecon_resume_wf, [('use_t2w', 'inputnode.use_T2'),
265-
('use_flair', 'inputnode.use_FLAIR')]),
266-
# Generate mid-thickness surfaces
267-
(autorecon_resume_wf, get_surfaces, [
268-
('outputnode.subjects_dir', 'subjects_dir'),
269-
('outputnode.subject_id', 'subject_id'),
270-
]),
271-
(autorecon_resume_wf, save_midthickness, [
272-
('outputnode.subjects_dir', 'base_directory'),
273-
('outputnode.subject_id', 'container'),
274-
]),
275306
(get_surfaces, midthickness, [
276307
('white', 'in_file'),
277308
('graymid', 'graymid'),

0 commit comments

Comments
 (0)