From 8a80d78e6c183efab39e01c46147e9c2813fb35d Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Tue, 3 Oct 2023 16:24:50 -0400 Subject: [PATCH] Use config throughout workflows. --- xcp_d/config.py | 15 ++ xcp_d/workflows/anatomical.py | 92 +++------- xcp_d/workflows/base.py | 292 ++++-------------------------- xcp_d/workflows/bold.py | 106 +---------- xcp_d/workflows/cifti.py | 111 +----------- xcp_d/workflows/concatenation.py | 39 +--- xcp_d/workflows/connectivity.py | 64 +++---- xcp_d/workflows/execsummary.py | 34 ++-- xcp_d/workflows/outputs.py | 50 ++--- xcp_d/workflows/plotting.py | 22 +-- xcp_d/workflows/postprocessing.py | 89 +++------ xcp_d/workflows/restingstate.py | 44 ++--- 12 files changed, 197 insertions(+), 761 deletions(-) diff --git a/xcp_d/config.py b/xcp_d/config.py index be1de9b3f..b0b3795e0 100644 --- a/xcp_d/config.py +++ b/xcp_d/config.py @@ -92,6 +92,7 @@ if not hasattr(sys, "_is_pytest_session"): sys._is_pytest_session = False # Trick to avoid sklearn's FutureWarnings + # Disable all warnings in main and children processes only on production versions if not any( ( @@ -177,9 +178,11 @@ def load(cls, settings, init=True): for k, v in settings.items(): if v is None: continue + if k in cls._paths: setattr(cls, k, Path(v).absolute()) continue + if hasattr(cls, k): setattr(cls, k, v) @@ -196,12 +199,15 @@ def get(cls): for k, v in cls.__dict__.items(): if k.startswith("_") or v is None: continue + if callable(getattr(cls, k)): continue + if k in cls._paths: v = str(v) out[k] = v + return out @@ -275,6 +281,7 @@ def get_plugin(cls): out["plugin_args"]["n_procs"] = int(cls.nprocs) if cls.memory_gb: out["plugin_args"]["memory_gb"] = float(cls.memory_gb) + return out @classmethod @@ -399,8 +406,14 @@ def init(cls): class workflow(_Config): """Configure the particular execution graph of this workflow.""" + analysis_level = "participant" + # The BIDS App analysis level (only "participant" allowed) input_type = "fmriprep" # The pipeline used to generate the preprocessed derivatives. + combineruns = False + # After denoising, concatenate each derivative from each task across runs. + cifti = False + # Whether to process ciftis or niftis. smoothing = 6 # FWHM, in millimeters, of the Gaussian smoothing kernel to apply to the denoised BOLD data. # This may be set to 0. @@ -417,6 +430,8 @@ class workflow(_Config): # Coverage threshold to apply to parcels in each atlas. min_time = 100 # Post-scrubbing threshold to apply to individual runs in the dataset. + exact_time = [] + # Produce correlation matrices limited to each requested amount of time dummy_scans = 0 # Number of volumes to remove from the beginning of each run. disable_bandpass_filter = True diff --git a/xcp_d/workflows/anatomical.py b/xcp_d/workflows/anatomical.py index 874b6e54b..444fb263d 100644 --- a/xcp_d/workflows/anatomical.py +++ b/xcp_d/workflows/anatomical.py @@ -10,6 +10,7 @@ from pkg_resources import resource_filename as pkgrf from templateflow.api import get as get_template +from xcp_d import config from xcp_d.interfaces.ants import ( ApplyTransforms, CompositeInvTransformUtil, @@ -42,12 +43,9 @@ @fill_doc def init_postprocess_anat_wf( - output_dir, - input_type, t1w_available, t2w_available, target_space, - omp_nthreads, mem_gb, name="postprocess_anat_wf", ): @@ -60,30 +58,26 @@ def init_postprocess_anat_wf( :graph2use: orig :simple_form: yes + from xcp_d import config from xcp_d.workflows.anatomical import init_postprocess_anat_wf - wf = init_postprocess_anat_wf( - output_dir=".", - input_type="fmriprep", - t1w_available=True, - t2w_available=True, - target_space="MNI152NLin6Asym", - omp_nthreads=1, - mem_gb=0.1, - name="postprocess_anat_wf", - ) + with mock_config(): + wf = init_postprocess_anat_wf( + t1w_available=True, + t2w_available=True, + target_space="MNI152NLin6Asym", + mem_gb=0.1, + name="postprocess_anat_wf", + ) Parameters ---------- - %(output_dir)s - %(input_type)s t1w_available : bool True if a preprocessed T1w is available, False if not. t2w_available : bool True if a preprocessed T2w is available, False if not. target_space : :obj:`str` Target NIFTI template for T1w. - %(omp_nthreads)s %(mem_gb)s %(name)s Default is "postprocess_anat_wf". @@ -110,6 +104,10 @@ def init_postprocess_anat_wf( t2w : :obj:`str` or None Path to the preprocessed T2w file in standard space. """ + output_dir = str(config.execution.output_dir) + input_type = config.execution.input_type + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node( @@ -287,7 +285,6 @@ def init_postprocess_anat_wf( execsummary_anatomical_plots_wf = init_execsummary_anatomical_plots_wf( t1w_available=t1w_available, t2w_available=t2w_available, - output_dir=output_dir, name="execsummary_anatomical_plots_wf", ) @@ -316,18 +313,13 @@ def init_postprocess_anat_wf( @fill_doc def init_postprocess_surfaces_wf( - fmri_dir, subject_id, - dcan_qc, - process_surfaces, mesh_available, standard_space_mesh, morphometry_files, - output_dir, t1w_available, t2w_available, mem_gb, - omp_nthreads, name="postprocess_surfaces_wf", ): """Postprocess surfaces. @@ -340,37 +332,27 @@ def init_postprocess_surfaces_wf( from xcp_d.workflows.anatomical import init_postprocess_surfaces_wf wf = init_postprocess_surfaces_wf( - fmri_dir=".", subject_id="01", - dcan_qc=True, - process_surfaces=True, mesh_available=True, standard_space_mesh=False, morphometry_files=[], - output_dir=".", t1w_available=True, t2w_available=True, mem_gb=0.1, - omp_nthreads=1, name="postprocess_surfaces_wf", ) Parameters ---------- - fmri_dir subject_id - %(dcan_qc)s - process_surfaces : bool mesh_available : bool standard_space_mesh : bool morphometry_files : list of str - %(output_dir)s t1w_available : bool True if a T1w image is available. t2w_available : bool True if a T2w image is available. %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "postprocess_surfaces_wf". @@ -391,6 +373,9 @@ def init_postprocess_surfaces_wf( myelin myelin_smoothed """ + dcan_qc = config.workflow.dcan_qc + process_surfaces = config.workflow.warp_surfaces_native2std + workflow = Workflow(name=name) inputnode = pe.Node( @@ -418,10 +403,8 @@ def init_postprocess_surfaces_wf( if dcan_qc and mesh_available: # Plot the white and pial surfaces on the brain in a brainsprite figure. brainsprite_wf = init_brainsprite_figures_wf( - output_dir=output_dir, t1w_available=t1w_available, t2w_available=t2w_available, - omp_nthreads=omp_nthreads, mem_gb=mem_gb, ) # fmt:off @@ -456,7 +439,6 @@ def init_postprocess_surfaces_wf( # At least some surfaces are already in fsLR space and must be copied, # without modification, to the output directory. copy_std_surfaces_to_datasink = init_copy_inputs_to_outputs_wf( - output_dir=output_dir, name="copy_std_surfaces_to_datasink", ) @@ -474,9 +456,7 @@ def init_postprocess_surfaces_wf( # Generate and output HCP-style surface files. hcp_surface_wfs = { hemi: init_generate_hcp_surfaces_wf( - output_dir=output_dir, mem_gb=mem_gb, - omp_nthreads=omp_nthreads, name=f"{hemi}_generate_hcp_surfaces_wf", ) for hemi in ["lh", "rh"] @@ -512,10 +492,7 @@ def init_postprocess_surfaces_wf( elif mesh_available: # Mesh files are in fsnative and must be warped to fsLR. warp_surfaces_to_template_wf = init_warp_surfaces_to_template_wf( - fmri_dir=fmri_dir, subject_id=subject_id, - output_dir=output_dir, - omp_nthreads=omp_nthreads, mem_gb=mem_gb, name="warp_surfaces_to_template_wf", ) @@ -565,10 +542,7 @@ def init_postprocess_surfaces_wf( @fill_doc def init_warp_surfaces_to_template_wf( - fmri_dir, subject_id, - output_dir, - omp_nthreads, mem_gb, name="warp_surfaces_to_template_wf", ): @@ -582,20 +556,14 @@ def init_warp_surfaces_to_template_wf( from xcp_d.workflows.anatomical import init_warp_surfaces_to_template_wf wf = init_warp_surfaces_to_template_wf( - fmri_dir=".", subject_id="01", - output_dir=".", - omp_nthreads=1, mem_gb=0.1, name="warp_surfaces_to_template_wf", ) Parameters ---------- - %(fmri_dir)s %(subject_id)s - %(output_dir)s - %(omp_nthreads)s %(mem_gb)s %(name)s Default is "warp_surfaces_to_template_wf". @@ -622,6 +590,9 @@ def init_warp_surfaces_to_template_wf( lh_wm_surf, rh_wm_surf : :obj:`str` Left- and right-hemisphere smoothed white matter surface files, in standard space. """ + fmri_dir = config.execution.fmri_dir + output_dir = config.execution.output_dir + workflow = Workflow(name=name) inputnode = pe.Node( @@ -667,7 +638,6 @@ def init_warp_surfaces_to_template_wf( # First, we create the Connectome WorkBench-compatible transform files. update_xfm_wf = init_ants_xfm_to_fsl_wf( mem_gb=mem_gb, - omp_nthreads=omp_nthreads, name="update_xfm_wf", ) @@ -704,7 +674,6 @@ def init_warp_surfaces_to_template_wf( participant_id=subject_id, hemisphere=hemi, mem_gb=mem_gb, - omp_nthreads=omp_nthreads, name=f"{hemi_label}_apply_transforms_wf", ) @@ -774,9 +743,7 @@ def init_warp_surfaces_to_template_wf( @fill_doc def init_generate_hcp_surfaces_wf( - output_dir, mem_gb, - omp_nthreads, name="generate_hcp_surfaces_wf", ): """Generate midthickness, inflated, and very-inflated HCP-style surfaces. @@ -789,17 +756,13 @@ def init_generate_hcp_surfaces_wf( from xcp_d.workflows.anatomical import init_generate_hcp_surfaces_wf wf = init_generate_hcp_surfaces_wf( - output_dir=".", mem_gb=0.1, - omp_nthreads=1, name="generate_hcp_surfaces_wf", ) Parameters ---------- - %(output_dir)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "generate_hcp_surfaces_wf". @@ -812,6 +775,9 @@ def init_generate_hcp_surfaces_wf( wm_surf : :obj:`str` The surface file to inflate. """ + output_dir = config.execution.output_dir + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node( @@ -925,7 +891,7 @@ def init_generate_hcp_surfaces_wf( @fill_doc -def init_ants_xfm_to_fsl_wf(mem_gb, omp_nthreads, name="ants_xfm_to_fsl_wf"): +def init_ants_xfm_to_fsl_wf(mem_gb, name="ants_xfm_to_fsl_wf"): """Modify ANTS-style fMRIPrep transforms to work with Connectome Workbench/FSL FNIRT. Workflow Graph @@ -937,14 +903,12 @@ def init_ants_xfm_to_fsl_wf(mem_gb, omp_nthreads, name="ants_xfm_to_fsl_wf"): wf = init_ants_xfm_to_fsl_wf( mem_gb=0.1, - omp_nthreads=1, name="ants_xfm_to_fsl_wf", ) Parameters ---------- %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "ants_xfm_to_fsl_wf". @@ -964,6 +928,8 @@ def init_ants_xfm_to_fsl_wf(mem_gb, omp_nthreads, name="ants_xfm_to_fsl_wf"): merged_inv_warpfield TODO: Add description. """ + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node( @@ -1205,7 +1171,6 @@ def init_warp_one_hemisphere_wf( participant_id, hemisphere, mem_gb, - omp_nthreads, name="warp_one_hemisphere_wf", ): """Apply transforms to warp one hemisphere's surface files into standard space. @@ -1221,15 +1186,14 @@ def init_warp_one_hemisphere_wf( participant_id="01", hemisphere="L", mem_gb=0.1, - omp_nthreads=1, name="warp_one_hemisphere_wf", ) Parameters ---------- + participant_id hemisphere : {"L", "R"} %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "warp_one_hemisphere_wf". @@ -1249,6 +1213,8 @@ def init_warp_one_hemisphere_wf( ------- warped_hemi_files """ + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node( diff --git a/xcp_d/workflows/base.py b/xcp_d/workflows/base.py index 7620c3c94..fc83f58ee 100644 --- a/xcp_d/workflows/base.py +++ b/xcp_d/workflows/base.py @@ -18,6 +18,7 @@ from nipype.pipeline import engine as pe from niworkflows.engine.workflows import LiterateWorkflow as Workflow +from xcp_d import config from xcp_d.__about__ import __version__ from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.report import AboutSummary, SubjectSummary @@ -52,39 +53,7 @@ @fill_doc def init_xcpd_wf( - fmri_dir, - output_dir, - work_dir, subject_list, - analysis_level, - task_id, - bids_filters, - bandpass_filter, - high_pass, - low_pass, - bpf_order, - fd_thresh, - motion_filter_type, - motion_filter_order, - band_stop_min, - band_stop_max, - despike, - head_radius, - params, - smoothing, - custom_confounds_folder, - dummy_scans, - random_seed, - exact_time, - cifti, - omp_nthreads, - layout=None, - process_surfaces=False, - dcan_qc=False, - input_type="fmriprep", - min_coverage=0.5, - min_time=100, - combineruns=False, name="xcpd_wf", ): """Build and organize execution of xcp_d pipeline. @@ -109,80 +78,14 @@ def init_xcpd_wf( os.mkdir(os.path.join(out_dir, "xcp_d")) wf = init_xcpd_wf( - fmri_dir=fmri_dir, - output_dir=out_dir, - work_dir=".", subject_list=["01"], - analysis_level="participant", - task_id="imagery", - bids_filters=None, - bandpass_filter=True, - high_pass=0.01, - low_pass=0.08, - bpf_order=2, - fd_thresh=0.3, - motion_filter_type=None, - motion_filter_order=4, - band_stop_min=12, - band_stop_max=20, - despike=True, - head_radius=50., - params="36P", - smoothing=6, - custom_confounds_folder=None, - dummy_scans=0, - random_seed=None, - exact_time=[], - cifti=False, - omp_nthreads=1, - layout=None, - process_surfaces=False, - dcan_qc=False, - input_type="fmriprep", - min_coverage=0.5, - min_time=100, - combineruns=False, name="xcpd_wf", ) Parameters ---------- - %(layout)s - %(bandpass_filter)s - %(high_pass)s - %(low_pass)s - %(despike)s - %(bpf_order)s - %(analysis_level)s - %(motion_filter_type)s - %(motion_filter_order)s - %(band_stop_min)s - %(band_stop_max)s - %(omp_nthreads)s - %(cifti)s - task_id : :obj:`str` or None - Task ID of BOLD series to be selected for postprocess , or ``None`` to postprocess all - bids_filters : dict or None - %(output_dir)s - %(fd_thresh)s - run_uuid : :obj:`str` - Unique identifier for execution instance subject_list : list List of subject labels - %(work_dir)s - %(head_radius)s - %(params)s - %(smoothing)s - %(custom_confounds_folder)s - %(dummy_scans)s - %(random_seed)s - %(process_surfaces)s - %(dcan_qc)s - %(input_type)s - %(min_coverage)s - %(min_time)s - %(exact_time)s - combineruns %(name)s References @@ -190,56 +93,31 @@ def init_xcpd_wf( .. footbibliography:: """ xcpd_wf = Workflow(name="xcpd_wf") - xcpd_wf.base_dir = work_dir + xcpd_wf.base_dir = config.execution.work_dir LOGGER.info(f"Beginning the {name} workflow") - write_dataset_description(fmri_dir, os.path.join(output_dir, "xcp_d")) + write_dataset_description( + config.execution.fmri_dir, + os.path.join(config.execution.output_dir, "xcp_d"), + ) for subject_id in subject_list: single_subj_wf = init_subject_wf( - layout=layout, - high_pass=high_pass, - low_pass=low_pass, - bpf_order=bpf_order, - motion_filter_type=motion_filter_type, - motion_filter_order=motion_filter_order, - band_stop_min=band_stop_min, - band_stop_max=band_stop_max, - bandpass_filter=bandpass_filter, - fmri_dir=fmri_dir, - omp_nthreads=omp_nthreads, subject_id=subject_id, - cifti=cifti, - despike=despike, - head_radius=head_radius, - params=params, - task_id=task_id, - bids_filters=bids_filters, - smoothing=smoothing, - output_dir=output_dir, - dummy_scans=dummy_scans, - random_seed=random_seed, - custom_confounds_folder=custom_confounds_folder, - fd_thresh=fd_thresh, - process_surfaces=process_surfaces, - dcan_qc=dcan_qc, - input_type=input_type, - min_coverage=min_coverage, - min_time=min_time, - exact_time=exact_time, - combineruns=combineruns, name=f"single_subject_{subject_id}_wf", ) single_subj_wf.config["execution"]["crashdump_dir"] = os.path.join( - output_dir, + config.execution.output_dir, "xcp_d", f"sub-{subject_id}", "log", ) for node in single_subj_wf._get_all_nodes(): node.config = deepcopy(single_subj_wf.config) - print(f"Analyzing data at the {analysis_level} level") + + print(f"Analyzing data at the {config.workflow.analysis_level} level") + xcpd_wf.add_nodes([single_subj_wf]) return xcpd_wf @@ -247,37 +125,7 @@ def init_xcpd_wf( @fill_doc def init_subject_wf( - fmri_dir, subject_id, - input_type, - process_surfaces, - combineruns, - cifti, - task_id, - bids_filters, - bandpass_filter, - high_pass, - low_pass, - bpf_order, - motion_filter_type, - motion_filter_order, - band_stop_min, - band_stop_max, - smoothing, - head_radius, - params, - output_dir, - custom_confounds_folder, - dummy_scans, - random_seed, - fd_thresh, - despike, - dcan_qc, - min_coverage, - min_time, - exact_time, - omp_nthreads, - layout, name, ): """Organize the postprocessing pipeline for a single subject. @@ -293,80 +141,40 @@ def init_subject_wf( fmri_dir = download_example_data() wf = init_subject_wf( - fmri_dir=fmri_dir, subject_id="01", - input_type="fmriprep", - process_surfaces=False, - combineruns=False, - cifti=False, - task_id="imagery", - bids_filters=None, - bandpass_filter=True, - high_pass=0.01, - low_pass=0.08, - bpf_order=2, - motion_filter_type=None, - motion_filter_order=4, - band_stop_min=12, - band_stop_max=20, - smoothing=6., - head_radius=50, - params="36P", - output_dir=".", - custom_confounds_folder=None, - dummy_scans=0, - random_seed=None, - fd_thresh=0.3, - despike=True, - dcan_qc=False, - min_coverage=0.5, - min_time=100, - exact_time=[], - omp_nthreads=1, - layout=None, name="single_subject_sub-01_wf", ) Parameters ---------- - %(fmri_dir)s %(subject_id)s - %(input_type)s - %(process_surfaces)s - combineruns - %(cifti)s - task_id : :obj:`str` or None - Task ID of BOLD series to be selected for postprocess , or ``None`` to postprocess all - bids_filters : dict or None - %(bandpass_filter)s - %(high_pass)s - %(low_pass)s - %(bpf_order)s - %(motion_filter_type)s - %(motion_filter_order)s - %(band_stop_min)s - %(band_stop_max)s - %(smoothing)s - %(head_radius)s - %(params)s - %(output_dir)s - %(custom_confounds_folder)s - %(dummy_scans)s - %(random_seed)s - %(fd_thresh)s - %(despike)s - %(dcan_qc)s - %(min_coverage)s - %(min_time)s - %(exact_time)s - %(omp_nthreads)s - %(layout)s %(name)s References ---------- .. footbibliography:: """ + fmri_dir = config.execution.fmri_dir + input_type = config.workflow.input_type + task_id = config.execution.task_id + bids_filters = config.execution.bids_filters + layout = config.execution.layout + process_surfaces = config.workflow.warp_surfaces_native2std + combineruns = config.workflow.combineruns + dcan_qc = config.workflow.dcan_qc + output_dir = config.execution.output_dir + fd_thresh = config.workflow.fd_thresh + motion_filter_type = config.workflow.motion_filter_type + cifti = config.workflow.cifti + motion_filter_order = config.workflow.motion_filter_order + band_stop_min = config.workflow.band_stop_min + band_stop_max = config.workflow.band_stop_max + min_time = config.workflow.min_time + exact_time = config.workflow.exact_time + head_radius = config.workflow.head_radius + custom_confounds_folder = config.workflow.custom_confounds + dummy_scans = config.workflow.dummy_scans + layout, subj_data = collect_data( bids_dir=fmri_dir, input_type=input_type, @@ -515,12 +323,9 @@ def init_subject_wf( target_space = get_entity(subj_data["anat_to_template_xfm"], "to") postprocess_anat_wf = init_postprocess_anat_wf( - output_dir=output_dir, - input_type=input_type, t1w_available=t1w_available, t2w_available=t2w_available, target_space=target_space, - omp_nthreads=omp_nthreads, mem_gb=1, name="postprocess_anat_wf", ) @@ -538,10 +343,7 @@ def init_subject_wf( # Load the atlases, warping to the same space as the BOLD data if necessary. load_atlases_wf = init_load_atlases_wf( - output_dir=output_dir, - cifti=cifti, mem_gb=1, - omp_nthreads=omp_nthreads, name="load_atlases_wf", ) load_atlases_wf.inputs.inputnode.name_source = preproc_files[0] @@ -551,18 +353,13 @@ def init_subject_wf( # Run surface post-processing workflow if we want to warp meshes to standard space *or* # generate brainsprite. postprocess_surfaces_wf = init_postprocess_surfaces_wf( - fmri_dir=fmri_dir, subject_id=subject_id, - dcan_qc=dcan_qc, mesh_available=mesh_available, standard_space_mesh=standard_space_mesh, morphometry_files=morph_file_types, - process_surfaces=process_surfaces, - output_dir=output_dir, t1w_available=t1w_available, t2w_available=t2w_available, mem_gb=1, - omp_nthreads=omp_nthreads, name="postprocess_surfaces_wf", ) @@ -611,11 +408,8 @@ def init_subject_wf( if morph_file_types: # Parcellate the morphometry files parcellate_surfaces_wf = init_parcellate_surfaces_wf( - output_dir=output_dir, files_to_parcellate=morph_file_types, - min_coverage=min_coverage, mem_gb=1, - omp_nthreads=omp_nthreads, name="parcellate_surfaces_wf", ) @@ -715,31 +509,14 @@ def init_subject_wf( postprocess_bold_wf = init_postprocess_bold_wf( bold_file=bold_file, - bandpass_filter=bandpass_filter, - high_pass=high_pass, - low_pass=low_pass, - bpf_order=bpf_order, - motion_filter_type=motion_filter_type, - motion_filter_order=motion_filter_order, - band_stop_min=band_stop_min, - band_stop_max=band_stop_max, - smoothing=smoothing, head_radius=head_radius, - params=params, - output_dir=output_dir, custom_confounds_folder=custom_confounds_folder, dummy_scans=dummy_scans, - random_seed=random_seed, - fd_thresh=fd_thresh, - despike=despike, - dcan_qc=dcan_qc, run_data=run_data, t1w_available=t1w_available, t2w_available=t2w_available, n_runs=n_runs, - min_coverage=min_coverage, exact_scans=exact_scans, - omp_nthreads=omp_nthreads, layout=layout, name=f"{'cifti' if cifti else 'nifti'}_postprocess_{run_counter}_wf", ) @@ -790,16 +567,9 @@ def init_subject_wf( if combineruns and (n_task_runs > 1): concatenate_data_wf = init_concatenate_data_wf( - output_dir=output_dir, - motion_filter_type=motion_filter_type, TR=TR, head_radius=head_radius, - params=params, - smoothing=smoothing, - cifti=cifti, - dcan_qc=dcan_qc, mem_gb=1, - omp_nthreads=omp_nthreads, name=f"concatenate_entity_set_{ent_set}_wf", ) diff --git a/xcp_d/workflows/bold.py b/xcp_d/workflows/bold.py index 2d824ccd6..cf829cebc 100644 --- a/xcp_d/workflows/bold.py +++ b/xcp_d/workflows/bold.py @@ -10,6 +10,7 @@ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from num2words import num2words +from xcp_d import config from xcp_d.interfaces.utils import ConvertTo32 from xcp_d.utils.confounds import get_custom_confounds from xcp_d.utils.doc import fill_doc @@ -30,32 +31,14 @@ @fill_doc def init_postprocess_nifti_wf( bold_file, - bandpass_filter, - high_pass, - low_pass, - bpf_order, - motion_filter_type, - motion_filter_order, - band_stop_min, - band_stop_max, - smoothing, head_radius, - params, - output_dir, custom_confounds_folder, dummy_scans, - fd_thresh, - despike, - dcan_qc, run_data, t1w_available, t2w_available, n_runs, - min_coverage, exact_scans, - random_seed, - omp_nthreads, - layout=None, name="bold_postprocess_wf", ): """Organize the bold processing workflow. @@ -96,32 +79,14 @@ def init_postprocess_nifti_wf( wf = init_postprocess_nifti_wf( bold_file=bold_file, - bandpass_filter=True, - high_pass=0.01, - low_pass=0.08, - bpf_order=2, - motion_filter_type="notch", - motion_filter_order=4, - band_stop_min=12, - band_stop_max=20, - smoothing=6, head_radius=50., - params="27P", - output_dir=".", custom_confounds_folder=custom_confounds_folder, dummy_scans=2, - fd_thresh=0.3, - despike=True, - dcan_qc=True, run_data=run_data, t1w_available=True, t2w_available=True, n_runs=1, - min_coverage=0.5, exact_scans=[], - random_seed=None, - omp_nthreads=1, - layout=layout, name="nifti_postprocess_wf", ) wf.inputs.inputnode.t1w = subj_data["t1w"] @@ -131,35 +96,17 @@ def init_postprocess_nifti_wf( ---------- bold_file: :obj:`str` bold file for post processing - %(bandpass_filter)s - %(high_pass)s - %(low_pass)s - %(bpf_order)s - %(motion_filter_type)s - %(motion_filter_order)s - %(band_stop_min)s - %(band_stop_max)s - %(smoothing)s %(head_radius)s This will already be estimated before this workflow. - %(params)s - %(output_dir)s %(custom_confounds_folder)s %(dummy_scans)s - %(fd_thresh)s - %(despike)s - %(dcan_qc)s run_data : dict t1w_available t2w_available n_runs Number of runs being postprocessed by XCP-D. This is just used for the boilerplate, as this workflow only posprocesses one run. - %(min_coverage)s %(exact_scans)s - %(random_seed)s - %(omp_nthreads)s - %(layout)s %(name)s Default is "nifti_postprocess_wf". @@ -214,6 +161,11 @@ def init_postprocess_nifti_wf( ---------- .. footbibliography:: """ + fd_thresh = config.workflow.fd_thresh + omp_nthreads = config.nipype.omp_nthreads + despike = config.workflow.despike + bandpass_filter = not config.workflow.disable_bandpass_filter + workflow = Workflow(name=name) TR = run_data["bold_metadata"]["RepetitionTime"] @@ -313,21 +265,12 @@ def init_postprocess_nifti_wf( # fmt:on prepare_confounds_wf = init_prepare_confounds_wf( - output_dir=output_dir, TR=TR, - params=params, dummy_scans=dummy_scans, - random_seed=random_seed, exact_scans=exact_scans, - motion_filter_type=motion_filter_type, - band_stop_min=band_stop_min, - band_stop_max=band_stop_max, - motion_filter_order=motion_filter_order, head_radius=head_radius, - fd_thresh=fd_thresh, custom_confounds_file=custom_confounds_file, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="prepare_confounds_wf", ) @@ -350,14 +293,7 @@ def init_postprocess_nifti_wf( denoise_bold_wf = init_denoise_bold_wf( TR=TR, - low_pass=low_pass, - high_pass=high_pass, - bpf_order=bpf_order, - bandpass_filter=bandpass_filter, - smoothing=smoothing, - cifti=False, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="denoise_bold_wf", ) @@ -380,9 +316,7 @@ def init_postprocess_nifti_wf( if despike: despike_wf = init_despike_wf( TR=TR, - cifti=False, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="despike_wf", ) @@ -407,9 +341,6 @@ def init_postprocess_nifti_wf( # fmt:on connectivity_wf = init_functional_connectivity_nifti_wf( - output_dir=output_dir, - min_coverage=min_coverage, - alff_available=bandpass_filter and (fd_thresh <= 0), mem_gb=mem_gbx["timeseries"], name="connectivity_wf", ) @@ -436,14 +367,8 @@ def init_postprocess_nifti_wf( if bandpass_filter and (fd_thresh <= 0): alff_wf = init_alff_wf( name_source=bold_file, - output_dir=output_dir, TR=TR, - low_pass=low_pass, - high_pass=high_pass, - smoothing=smoothing, - cifti=False, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="alff_wf", ) @@ -459,9 +384,7 @@ def init_postprocess_nifti_wf( reho_wf = init_reho_nifti_wf( name_source=bold_file, - output_dir=output_dir, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="reho_wf", ) @@ -476,14 +399,9 @@ def init_postprocess_nifti_wf( # fmt:on qc_report_wf = init_qc_report_wf( - output_dir=output_dir, TR=TR, head_radius=head_radius, - params=params, - dcan_qc=dcan_qc, - cifti=False, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="qc_report_wf", ) @@ -513,18 +431,8 @@ def init_postprocess_nifti_wf( # fmt:on postproc_derivatives_wf = init_postproc_derivatives_wf( - smoothing=smoothing, name_source=bold_file, - bandpass_filter=bandpass_filter, - params=params, exact_scans=exact_scans, - cifti=False, - dcan_qc=dcan_qc, - output_dir=output_dir, - low_pass=low_pass, - high_pass=high_pass, - fd_thresh=fd_thresh, - motion_filter_type=motion_filter_type, TR=TR, name="postproc_derivatives_wf", ) @@ -574,8 +482,6 @@ def init_postprocess_nifti_wf( preproc_nifti=bold_file, t1w_available=t1w_available, t2w_available=t2w_available, - output_dir=output_dir, - layout=layout, name="execsummary_functional_plots_wf", ) diff --git a/xcp_d/workflows/cifti.py b/xcp_d/workflows/cifti.py index 942908cbd..3f5b7e3c7 100644 --- a/xcp_d/workflows/cifti.py +++ b/xcp_d/workflows/cifti.py @@ -10,6 +10,7 @@ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from num2words import num2words +from xcp_d import config from xcp_d.interfaces.utils import ConvertTo32 from xcp_d.utils.confounds import get_custom_confounds from xcp_d.utils.doc import fill_doc @@ -30,32 +31,14 @@ @fill_doc def init_postprocess_cifti_wf( bold_file, - bandpass_filter, - high_pass, - low_pass, - bpf_order, - motion_filter_type, - motion_filter_order, - band_stop_min, - band_stop_max, - smoothing, head_radius, - params, - output_dir, custom_confounds_folder, dummy_scans, - fd_thresh, - despike, - dcan_qc, run_data, t1w_available, t2w_available, n_runs, - min_coverage, exact_scans, - random_seed, - omp_nthreads, - layout=None, name="cifti_postprocess_wf", ): """Organize the cifti processing workflow. @@ -94,32 +77,14 @@ def init_postprocess_cifti_wf( wf = init_postprocess_cifti_wf( bold_file=bold_file, - bandpass_filter=True, - high_pass=0.01, - low_pass=0.08, - bpf_order=2, - motion_filter_type="notch", - motion_filter_order=4, - band_stop_min=12, - band_stop_max=20, - smoothing=6, head_radius=50., - params="27P", - output_dir=".", custom_confounds_folder=custom_confounds_folder, dummy_scans=2, - fd_thresh=0.3, - despike=True, - dcan_qc=True, run_data=run_data, t1w_available=True, t2w_available=True, n_runs=1, - min_coverage=0.5, exact_scans=[], - random_seed=None, - omp_nthreads=1, - layout=layout, name="cifti_postprocess_wf", ) wf.inputs.inputnode.t1w = subj_data["t1w"] @@ -127,35 +92,17 @@ def init_postprocess_cifti_wf( Parameters ---------- bold_file - %(bandpass_filter)s - %(high_pass)s - %(low_pass)s - %(bpf_order)s - %(motion_filter_type)s - %(motion_filter_order)s - %(band_stop_min)s - %(band_stop_max)s - %(smoothing)s %(head_radius)s This will already be estimated before this workflow. - %(params)s - %(output_dir)s %(custom_confounds_folder)s %(dummy_scans)s - %(fd_thresh)s - %(despike)s - %(dcan_qc)s run_data : dict t1w_available t2w_available n_runs Number of runs being postprocessed by XCP-D. This is just used for the boilerplate, as this workflow only posprocesses one run. - %(min_coverage)s - %(random_seed)s %(exact_scans)s - %(omp_nthreads)s - %(layout)s %(name)s Default is "cifti_postprocess_wf". @@ -201,6 +148,11 @@ def init_postprocess_cifti_wf( ---------- .. footbibliography:: """ + fd_thresh = config.workflow.fd_thresh + omp_nthreads = config.nipype.omp_nthreads + despike = config.workflow.despike + bandpass_filter = not config.workflow.disable_bandpass_filter + workflow = Workflow(name=name) TR = run_data["bold_metadata"]["RepetitionTime"] @@ -289,21 +241,12 @@ def init_postprocess_cifti_wf( # fmt:on prepare_confounds_wf = init_prepare_confounds_wf( - output_dir=output_dir, TR=TR, - params=params, dummy_scans=dummy_scans, - random_seed=random_seed, exact_scans=exact_scans, - motion_filter_type=motion_filter_type, - band_stop_min=band_stop_min, - band_stop_max=band_stop_max, - motion_filter_order=motion_filter_order, head_radius=head_radius, - fd_thresh=fd_thresh, custom_confounds_file=custom_confounds_file, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="prepare_confounds_wf", ) @@ -328,14 +271,7 @@ def init_postprocess_cifti_wf( denoise_bold_wf = init_denoise_bold_wf( TR=TR, - low_pass=low_pass, - high_pass=high_pass, - bpf_order=bpf_order, - bandpass_filter=bandpass_filter, - smoothing=smoothing, - cifti=True, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="denoise_bold_wf", ) @@ -357,9 +293,7 @@ def init_postprocess_cifti_wf( if despike: despike_wf = init_despike_wf( TR=TR, - cifti=True, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="despike_wf", ) @@ -384,11 +318,7 @@ def init_postprocess_cifti_wf( # fmt:on connectivity_wf = init_functional_connectivity_cifti_wf( - min_coverage=min_coverage, - alff_available=bandpass_filter and (fd_thresh <= 0), - output_dir=output_dir, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="connectivity_wf", ) @@ -414,17 +344,11 @@ def init_postprocess_cifti_wf( ]) # fmt:on - if bandpass_filter: + if bandpass_filter and (fd_thresh <= 0): alff_wf = init_alff_wf( name_source=bold_file, - output_dir=output_dir, TR=TR, - low_pass=low_pass, - high_pass=high_pass, - smoothing=smoothing, - cifti=True, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="alff_wf", ) @@ -439,9 +363,7 @@ def init_postprocess_cifti_wf( reho_wf = init_reho_cifti_wf( name_source=bold_file, - output_dir=output_dir, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="reho_wf", ) @@ -455,14 +377,9 @@ def init_postprocess_cifti_wf( # fmt:on qc_report_wf = init_qc_report_wf( - output_dir=output_dir, TR=TR, head_radius=head_radius, - params=params, - dcan_qc=dcan_qc, - cifti=True, mem_gb=mem_gbx["timeseries"], - omp_nthreads=omp_nthreads, name="qc_report_wf", ) @@ -485,18 +402,8 @@ def init_postprocess_cifti_wf( # fmt:on postproc_derivatives_wf = init_postproc_derivatives_wf( - smoothing=smoothing, name_source=bold_file, - bandpass_filter=bandpass_filter, - params=params, exact_scans=exact_scans, - cifti=True, - dcan_qc=dcan_qc, - output_dir=output_dir, - low_pass=low_pass, - high_pass=high_pass, - fd_thresh=fd_thresh, - motion_filter_type=motion_filter_type, TR=TR, name="postproc_derivatives_wf", ) @@ -531,7 +438,7 @@ def init_postprocess_cifti_wf( ]), ]) - if bandpass_filter: + if bandpass_filter and (fd_thresh <= 0): workflow.connect([ (alff_wf, postproc_derivatives_wf, [ ("outputnode.alff", "inputnode.alff"), @@ -548,8 +455,6 @@ def init_postprocess_cifti_wf( preproc_nifti=run_data["nifti_file"], t1w_available=t1w_available, t2w_available=t2w_available, - output_dir=output_dir, - layout=layout, name="execsummary_functional_plots_wf", ) diff --git a/xcp_d/workflows/concatenation.py b/xcp_d/workflows/concatenation.py index 29b4755c2..7a6a9d7c5 100644 --- a/xcp_d/workflows/concatenation.py +++ b/xcp_d/workflows/concatenation.py @@ -3,6 +3,7 @@ from nipype.pipeline import engine as pe from niworkflows.engine.workflows import LiterateWorkflow as Workflow +from xcp_d import config from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.concatenation import ( CleanNameSource, @@ -16,16 +17,9 @@ @fill_doc def init_concatenate_data_wf( - output_dir, - motion_filter_type, - mem_gb, - omp_nthreads, TR, head_radius, - params, - smoothing, - cifti, - dcan_qc, + mem_gb, name="concatenate_data_wf", ): """Concatenate postprocessed data. @@ -38,31 +32,17 @@ def init_concatenate_data_wf( from xcp_d.workflows.concatenation import init_concatenate_data_wf wf = init_concatenate_data_wf( - output_dir=".", - motion_filter_type=None, - mem_gb=0.1, - omp_nthreads=1, TR=2, head_radius=50, - params="none", - smoothing=None, - cifti=False, - dcan_qc=True, + mem_gb=0.1, name="concatenate_data_wf", ) Parameters ---------- - %(output_dir)s - %(motion_filter_type)s - %(mem_gb)s - %(omp_nthreads)s %(TR)s %(head_radius)s - %(params)s - %(smoothing)s - %(cifti)s - %(dcan_qc)s + %(mem_gb)s %(name)s Default is "concatenate_data_wf". @@ -96,6 +76,12 @@ def init_concatenate_data_wf( %(timeseries_ciftis)s This will be a list of lists, with one sublist for each run. """ + motion_filter_type = config.workflow.motion_filter_type + output_dir = config.execution.output_dir + cifti = config.workflow.cifti + smoothing = config.workflow.smoothing + dcan_qc = config.workflow.dcan_qc + workflow = Workflow(name=name) workflow.__desc__ = """ @@ -185,14 +171,9 @@ def init_concatenate_data_wf( # Now, run the QC report workflow on the concatenated BOLD file. qc_report_wf = init_qc_report_wf( - output_dir=output_dir, TR=TR, head_radius=head_radius, - params=params, - cifti=cifti, - dcan_qc=dcan_qc, mem_gb=mem_gb, - omp_nthreads=omp_nthreads, name="concat_qc_report_wf", ) qc_report_wf.inputs.inputnode.dummy_scans = 0 diff --git a/xcp_d/workflows/connectivity.py b/xcp_d/workflows/connectivity.py index 8d2f602a8..6bb77a2ad 100644 --- a/xcp_d/workflows/connectivity.py +++ b/xcp_d/workflows/connectivity.py @@ -6,6 +6,7 @@ from nipype.pipeline import engine as pe from niworkflows.engine.workflows import LiterateWorkflow as Workflow +from xcp_d import config from xcp_d.interfaces.ants import ApplyTransforms from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.connectivity import CiftiConnect, ConnectPlot, NiftiConnect @@ -19,10 +20,7 @@ @fill_doc def init_load_atlases_wf( - output_dir, - cifti, mem_gb, - omp_nthreads, name="load_atlases_wf", ): """Load atlases and warp them to the same space as the BOLD file. @@ -35,19 +33,13 @@ def init_load_atlases_wf( from xcp_d.workflows.connectivity import init_load_atlases_wf wf = init_load_atlases_wf( - output_dir=".", - cifti=True, mem_gb=0.1, - omp_nthreads=1, name="load_atlases_wf", ) Parameters ---------- - %(output_dir)s - %(cifti)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "load_atlases_wf". @@ -63,6 +55,10 @@ def init_load_atlases_wf( atlas_labels_files parcellated_atlas_files """ + cifti = config.workflow.cifti + omp_nthreads = config.nipype.omp_nthreads + output_dir = config.execution.output_dir + workflow = Workflow(name=name) inputnode = pe.Node( @@ -309,11 +305,8 @@ def init_load_atlases_wf( @fill_doc def init_parcellate_surfaces_wf( - output_dir, files_to_parcellate, - min_coverage, mem_gb, - omp_nthreads, name="parcellate_surfaces_wf", ): """Parcellate surface files and write them out to the output directory. @@ -326,23 +319,17 @@ def init_parcellate_surfaces_wf( from xcp_d.workflows.connectivity import init_parcellate_surfaces_wf wf = init_parcellate_surfaces_wf( - output_dir=".", files_to_parcellate=["sulcal_depth", "sulcal_curv", "cortical_thickness"], - min_coverage=0.5, mem_gb=0.1, - omp_nthreads=1, name="parcellate_surfaces_wf", ) Parameters ---------- - %(output_dir)s files_to_parcellate : :obj:`list` of :obj:`str` List of surface file types to parcellate (e.g., "sulcal_depth", "sulcal_curv", "cortical_thickness"). - %(min_coverage)s %(mem_gb)s - %(omp_nthreads)s %(name)s Inputs @@ -354,6 +341,10 @@ def init_parcellate_surfaces_wf( myelin myelin_smoothed """ + omp_nthreads = config.nipype.omp_nthreads + min_coverage = config.workflow.min_coverage + output_dir = config.execution.output_dir + workflow = Workflow(name=name) SURF_DESCS = { @@ -478,9 +469,6 @@ def init_parcellate_surfaces_wf( @fill_doc def init_functional_connectivity_nifti_wf( - output_dir, - alff_available, - min_coverage, mem_gb, name="connectivity_wf", ): @@ -494,18 +482,12 @@ def init_functional_connectivity_nifti_wf( from xcp_d.workflows.connectivity import init_functional_connectivity_nifti_wf wf = init_functional_connectivity_nifti_wf( - output_dir=".", - alff_available=True, - min_coverage=0.5, mem_gb=0.1, name="connectivity_wf", ) Parameters ---------- - %(output_dir)s - alff_available - %(min_coverage)s %(mem_gb)s %(name)s Default is "connectivity_wf". @@ -531,6 +513,13 @@ def init_functional_connectivity_nifti_wf( parcellated_alff parcellated_reho """ + min_coverage = config.workflow.min_coverage + output_dir = config.execution.output_dir + bandpass_filter = not config.workflow.disable_bandpass_filter + fd_thresh = config.workflow.fd_thresh + + alff_available = bandpass_filter and (fd_thresh <= 0) + workflow = Workflow(name=name) workflow.__desc__ = f""" @@ -681,11 +670,7 @@ def init_functional_connectivity_nifti_wf( @fill_doc def init_functional_connectivity_cifti_wf( - output_dir, - alff_available, - min_coverage, mem_gb, - omp_nthreads, name="connectivity_wf", ): """Extract CIFTI time series. @@ -696,22 +681,15 @@ def init_functional_connectivity_cifti_wf( :simple_form: yes from xcp_d.workflows.connectivity import init_functional_connectivity_cifti_wf + wf = init_functional_connectivity_cifti_wf( - output_dir=".", - alff_available=True, - min_coverage=0.5, mem_gb=0.1, - omp_nthreads=1, name="connectivity_wf", ) Parameters ---------- - %(output_dir)s - alff_available - %(min_coverage)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "connectivity_wf". @@ -743,6 +721,14 @@ def init_functional_connectivity_cifti_wf( parcellated_reho parcellated_alff """ + min_coverage = config.workflow.min_coverage + output_dir = config.execution.output_dir + bandpass_filter = not config.workflow.disable_bandpass_filter + fd_thresh = config.workflow.fd_thresh + omp_nthreads = config.nipype.omp_nthreads + + alff_available = bandpass_filter and (fd_thresh <= 0) + workflow = Workflow(name=name) workflow.__desc__ = f""" Processed functional timeseries were extracted from residual BOLD using diff --git a/xcp_d/workflows/execsummary.py b/xcp_d/workflows/execsummary.py index ed053db67..531a203cd 100644 --- a/xcp_d/workflows/execsummary.py +++ b/xcp_d/workflows/execsummary.py @@ -11,6 +11,7 @@ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from pkg_resources import resource_filename as pkgrf +from xcp_d import config from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.nilearn import BinaryMath, ResampleToImage from xcp_d.interfaces.plotting import AnatomicalPlot, PNGAppend @@ -29,11 +30,9 @@ @fill_doc def init_brainsprite_figures_wf( - output_dir, t1w_available, t2w_available, mem_gb, - omp_nthreads, name="init_brainsprite_figures_wf", ): """Create mosaic and PNG files for executive summary brainsprite. @@ -46,23 +45,19 @@ def init_brainsprite_figures_wf( from xcp_d.workflows.execsummary import init_brainsprite_figures_wf wf = init_brainsprite_figures_wf( - output_dir=".", t1w_available=True, t2w_available=True, mem_gb=0.1, - omp_nthreads=1, name="brainsprite_figures_wf", ) Parameters ---------- - %(output_dir)s t1w_available : bool True if a T1w image is available. t2w_available : bool True if a T2w image is available. %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "init_brainsprite_figures_wf". @@ -77,6 +72,9 @@ def init_brainsprite_figures_wf( lh_pial_surf rh_pial_surf """ + output_dir = str(config.execution.output_dir) + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node( @@ -303,8 +301,6 @@ def init_execsummary_functional_plots_wf( preproc_nifti, t1w_available, t2w_available, - output_dir, - layout, name="execsummary_functional_plots_wf", ): """Generate the functional figures for an executive summary. @@ -320,8 +316,6 @@ def init_execsummary_functional_plots_wf( preproc_nifti=None, t1w_available=True, t2w_available=True, - output_dir=".", - layout=None, name="execsummary_functional_plots_wf", ) @@ -334,8 +328,6 @@ def init_execsummary_functional_plots_wf( Generally True. t2w_available : :obj:`bool` Generally False. - %(output_dir)s - %(layout)s %(name)s Inputs @@ -350,6 +342,9 @@ def init_execsummary_functional_plots_wf( t2w T2w image in a standard space, taken from the output of init_postprocess_anat_wf. """ + output_dir = str(config.execution.output_dir) + layout = config.execution.layout + workflow = Workflow(name=name) inputnode = pe.Node( @@ -474,7 +469,6 @@ def init_execsummary_functional_plots_wf( # fmt:on plot_anat_on_task_wf = init_plot_overlay_wf( - output_dir=output_dir, desc=f"{anat[0].upper()}{anat[1:]}OnTask", name=f"plot_{anat}_on_task_wf", ) @@ -492,7 +486,6 @@ def init_execsummary_functional_plots_wf( # fmt:on plot_task_on_anat_wf = init_plot_overlay_wf( - output_dir=output_dir, desc=f"TaskOn{anat[0].upper()}{anat[1:]}", name=f"plot_task_on_{anat}_wf", ) @@ -516,7 +509,6 @@ def init_execsummary_functional_plots_wf( def init_execsummary_anatomical_plots_wf( t1w_available, t2w_available, - output_dir, name="execsummary_anatomical_plots_wf", ): """Generate the anatomical figures for an executive summary. @@ -531,7 +523,6 @@ def init_execsummary_anatomical_plots_wf( wf = init_execsummary_anatomical_plots_wf( t1w_available=True, t2w_available=True, - output_dir=".", name="execsummary_anatomical_plots_wf", ) @@ -541,7 +532,6 @@ def init_execsummary_anatomical_plots_wf( Generally True. t2w_available : bool Generally False. - %(output_dir)s %(name)s Inputs @@ -585,7 +575,6 @@ def init_execsummary_anatomical_plots_wf( # fmt:on plot_anat_on_atlas_wf = init_plot_overlay_wf( - output_dir=output_dir, desc="AnatOnAtlas", name=f"plot_{anat}_on_atlas_wf", ) @@ -601,7 +590,6 @@ def init_execsummary_anatomical_plots_wf( # fmt:on plot_atlas_on_anat_wf = init_plot_overlay_wf( - output_dir=output_dir, desc="AtlasOnAnat", name=f"plot_atlas_on_{anat}_wf", ) @@ -624,7 +612,6 @@ def init_execsummary_anatomical_plots_wf( @fill_doc def init_plot_custom_slices_wf( - output_dir, desc, name="plot_custom_slices_wf", ): @@ -641,14 +628,12 @@ def init_plot_custom_slices_wf( from xcp_d.workflows.execsummary import init_plot_custom_slices_wf wf = init_plot_custom_slices_wf( - output_dir=".", desc="AtlasOnSubcorticals", name="plot_custom_slices_wf", ) Parameters ---------- - %(output_dir)s desc : :obj:`str` String to be used as ``desc`` entity in output filename. %(name)s @@ -664,6 +649,8 @@ def init_plot_custom_slices_wf( SINGLE_SLICES = ["x", "x", "x", "y", "y", "y", "z", "z", "z"] SLICE_NUMBERS = [36, 45, 52, 43, 54, 65, 23, 33, 39] + output_dir = str(config.execution.output_dir) + workflow = Workflow(name=name) inputnode = pe.Node( @@ -731,13 +718,14 @@ def init_plot_custom_slices_wf( def init_plot_overlay_wf( - output_dir, desc, name="plot_overlay_wf", ): """Use the default slices from slicesdir to make a plot.""" from xcp_d.interfaces.plotting import SlicesDir + output_dir = str(config.execution.output_dir) + workflow = Workflow(name=name) inputnode = pe.Node( diff --git a/xcp_d/workflows/outputs.py b/xcp_d/workflows/outputs.py index d307f3b65..862877726 100644 --- a/xcp_d/workflows/outputs.py +++ b/xcp_d/workflows/outputs.py @@ -5,6 +5,7 @@ from nipype.pipeline import engine as pe from niworkflows.engine.workflows import LiterateWorkflow as Workflow +from xcp_d import config from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.utils import FilterUndefined from xcp_d.utils.bids import get_entity @@ -12,7 +13,7 @@ @fill_doc -def init_copy_inputs_to_outputs_wf(output_dir, name="copy_inputs_to_outputs_wf"): +def init_copy_inputs_to_outputs_wf(name="copy_inputs_to_outputs_wf"): """Copy files from the preprocessing derivatives to the output folder, with no modifications. Workflow Graph @@ -23,13 +24,11 @@ def init_copy_inputs_to_outputs_wf(output_dir, name="copy_inputs_to_outputs_wf") from xcp_d.workflows.outputs import init_copy_inputs_to_outputs_wf wf = init_copy_inputs_to_outputs_wf( - output_dir=".", name="copy_inputs_to_outputs_wf", ) Parameters ---------- - %(output_dir)s %(name)s Default is "copy_inputs_to_outputs_wf". @@ -46,6 +45,8 @@ def init_copy_inputs_to_outputs_wf(output_dir, name="copy_inputs_to_outputs_wf") myelin myelin_smoothed """ + output_dir = config.execution.output_dir + workflow = Workflow(name=name) inputnode = pe.Node( @@ -124,17 +125,7 @@ def init_copy_inputs_to_outputs_wf(output_dir, name="copy_inputs_to_outputs_wf") @fill_doc def init_postproc_derivatives_wf( name_source, - bandpass_filter, - low_pass, - high_pass, - fd_thresh, - motion_filter_type, - smoothing, - params, exact_scans, - cifti, - dcan_qc, - output_dir, TR, name="postproc_derivatives_wf", ): @@ -149,17 +140,7 @@ def init_postproc_derivatives_wf( wf = init_postproc_derivatives_wf( name_source="/path/to/file.nii.gz", - bandpass_filter=True, - low_pass=0.1, - high_pass=0.008, - fd_thresh=0.3, - motion_filter_type=None, - smoothing=6, - params="36P", exact_scans=[], - cifti=False, - dcan_qc=True, - output_dir=".", TR=2., name="postproc_derivatives_wf", ) @@ -168,19 +149,7 @@ def init_postproc_derivatives_wf( ---------- name_source : :obj:`str` bold or cifti files - low_pass : float - low pass filter - high_pass : float - high pass filter - %(fd_thresh)s - %(motion_filter_type)s - %(smoothing)s - %(params)s %(exact_scans)s - %(cifti)s - %(dcan_qc)s - output_dir : :obj:`str` - output directory %(TR)s %(name)s Default is "connectivity_wf". @@ -214,6 +183,17 @@ def init_postproc_derivatives_wf( temporal_mask_metadata %(dummy_scans)s """ + dcan_qc = config.workflow.dcan_qc + output_dir = config.execution.output_dir + bandpass_filter = not config.workflow.disable_bandpass_filter + high_pass = config.workflow.lower_bpf + low_pass = config.workflow.upper_bpf + fd_thresh = config.workflow.fd_thresh + motion_filter_type = config.workflow.motion_filter_type + smoothing = config.workflow.smoothing + params = config.workflow.nuisance_regressors + cifti = config.workflow.cifti + workflow = Workflow(name=name) inputnode = pe.Node( diff --git a/xcp_d/workflows/plotting.py b/xcp_d/workflows/plotting.py index 8ae589615..f0b678000 100644 --- a/xcp_d/workflows/plotting.py +++ b/xcp_d/workflows/plotting.py @@ -5,6 +5,7 @@ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from templateflow.api import get as get_template +from xcp_d import config from xcp_d.interfaces.ants import ApplyTransforms from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.plotting import QCPlots, QCPlotsES @@ -16,14 +17,9 @@ @fill_doc def init_qc_report_wf( - output_dir, TR, head_radius, - params, - cifti, - dcan_qc, mem_gb, - omp_nthreads, name="qc_report_wf", ): """Generate quality control figures and a QC file. @@ -35,27 +31,17 @@ def init_qc_report_wf( from xcp_d.workflows.plotting import init_qc_report_wf wf = init_qc_report_wf( - output_dir=".", TR=0.5, head_radius=50, - params="none", - cifti=False, - dcan_qc=True, mem_gb=0.1, - omp_nthreads=1, name="qc_report_wf", ) Parameters ---------- - %(output_dir)s %(TR)s %(head_radius)s - %(params)s - %(cifti)s - %(dcan_qc)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "qc_report_wf". @@ -92,6 +78,12 @@ def init_qc_report_wf( ------- qc_file """ + omp_nthreads = config.nipype.omp_nthreads + cifti = config.workflow.cifti + output_dir = config.execution.output_dir + dcan_qc = config.workflow.dcan_qc + params = config.workflow.nuisance_regressors + workflow = Workflow(name=name) inputnode = pe.Node( diff --git a/xcp_d/workflows/postprocessing.py b/xcp_d/workflows/postprocessing.py index 240484881..df19921bc 100644 --- a/xcp_d/workflows/postprocessing.py +++ b/xcp_d/workflows/postprocessing.py @@ -8,6 +8,7 @@ from num2words import num2words from pkg_resources import resource_filename as pkgrf +from xcp_d import config from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.censoring import ( Censor, @@ -27,21 +28,12 @@ @fill_doc def init_prepare_confounds_wf( - output_dir, TR, - params, dummy_scans, - random_seed, exact_scans, - motion_filter_type, - band_stop_min, - band_stop_max, - motion_filter_order, head_radius, - fd_thresh, custom_confounds_file, mem_gb, - omp_nthreads, name="prepare_confounds_wf", ): """Prepare confounds. @@ -57,41 +49,23 @@ def init_prepare_confounds_wf( from xcp_d.workflows.postprocessing import init_prepare_confounds_wf wf = init_prepare_confounds_wf( - output_dir=".", TR=0.8, - params="27P", dummy_scans="auto", - random_seed=None, exact_scans=[], - motion_filter_type="notch", - band_stop_min=12, - band_stop_max=20, - motion_filter_order=4, head_radius=70, - fd_thresh=0.3, custom_confounds_file=None, mem_gb=0.1, - omp_nthreads=1, name="prepare_confounds_wf", ) Parameters ---------- - %(output_dir)s %(TR)s - %(params)s %(dummy_scans)s - %(random_seed)s - %(motion_filter_type)s - %(band_stop_min)s - %(band_stop_max)s - %(motion_filter_order)s %(head_radius)s This will already be estimated before this workflow. - %(fd_thresh)s %(custom_confounds_file)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "prepare_confounds_wf". @@ -119,6 +93,16 @@ def init_prepare_confounds_wf( %(temporal_mask)s temporal_mask_metadata : :obj:`dict` """ + output_dir = config.execution.output_dir + fd_thresh = config.workflow.fd_thresh + motion_filter_type = config.workflow.motion_filter_type + motion_filter_order = config.workflow.motion_filter_order + band_stop_min = config.workflow.band_stop_min + band_stop_max = config.workflow.band_stop_max + params = config.workflow.nuisance_regressors + random_seed = config.workflow.random_seed + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) dummy_scans_str = "" @@ -398,9 +382,7 @@ def init_prepare_confounds_wf( @fill_doc def init_despike_wf( TR, - cifti, mem_gb, - omp_nthreads, name="despike_wf", ): """Despike BOLD data with AFNI's 3dDespike. @@ -421,18 +403,14 @@ def init_despike_wf( wf = init_despike_wf( TR=0.8, - cifti=True, mem_gb=0.1, - omp_nthreads=1, name="despike_wf", ) Parameters ---------- %(TR)s - %(cifti)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "despike_wf". @@ -446,6 +424,9 @@ def init_despike_wf( bold_file : :obj:`str` The despiked NIFTI or CIFTI BOLD file. """ + cifti = config.workflow.cifti + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=["bold_file"]), name="inputnode") outputnode = pe.Node(niu.IdentityInterface(fields=["bold_file"]), name="outputnode") @@ -512,14 +493,7 @@ def init_despike_wf( @fill_doc def init_denoise_bold_wf( TR, - low_pass, - high_pass, - bpf_order, - bandpass_filter, - smoothing, - cifti, mem_gb, - omp_nthreads, name="denoise_bold_wf", ): """Denoise BOLD data. @@ -533,28 +507,14 @@ def init_denoise_bold_wf( wf = init_denoise_bold_wf( TR=0.8, - high_pass=0.01, - low_pass=0.08, - bpf_order=2, - bandpass_filter=True, - smoothing=6, - cifti=False, mem_gb=0.1, - omp_nthreads=1, name="denoise_bold_wf", ) Parameters ---------- %(TR)s - %(low_pass)s - %(high_pass)s - %(bpf_order)s - %(bandpass_filter)s - %(smoothing)s - %(cifti)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "denoise_bold_wf". @@ -572,6 +532,14 @@ def init_denoise_bold_wf( %(censored_denoised_bold)s %(smoothed_denoised_bold)s """ + bandpass_filter = not config.workflow.disable_bandpass_filter + high_pass = config.workflow.lower_bpf + low_pass = config.workflow.upper_bpf + smoothing = config.workflow.smoothing + bpf_order = config.workflow.bpf_order + cifti = config.workflow.cifti + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) workflow.__desc__ = ( @@ -699,10 +667,7 @@ def init_denoise_bold_wf( @fill_doc def init_resd_smoothing_wf( - smoothing, - cifti, mem_gb, - omp_nthreads, name="resd_smoothing_wf", ): """Smooth BOLD residuals. @@ -715,19 +680,13 @@ def init_resd_smoothing_wf( from xcp_d.workflows.postprocessing import init_resd_smoothing_wf wf = init_resd_smoothing_wf( - smoothing=6, - cifti=True, mem_gb=0.1, - omp_nthreads=1, name="resd_smoothing_wf", ) Parameters ---------- - %(smoothing)s - %(cifti)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "resd_smoothing_wf". @@ -739,6 +698,10 @@ def init_resd_smoothing_wf( ------- smoothed_bold """ + smoothing = config.workflow.smoothing + cifti = config.workflow.cifti + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=["bold_file"]), name="inputnode") outputnode = pe.Node(niu.IdentityInterface(fields=["smoothed_bold"]), name="outputnode") diff --git a/xcp_d/workflows/restingstate.py b/xcp_d/workflows/restingstate.py index b1520062b..a62873905 100644 --- a/xcp_d/workflows/restingstate.py +++ b/xcp_d/workflows/restingstate.py @@ -8,6 +8,7 @@ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from templateflow.api import get as get_template +from xcp_d import config from xcp_d.interfaces.bids import DerivativesDataSink from xcp_d.interfaces.nilearn import Smooth from xcp_d.interfaces.restingstate import ComputeALFF, ReHoNamePatch, SurfaceReHo @@ -25,14 +26,8 @@ @fill_doc def init_alff_wf( name_source, - output_dir, TR, - low_pass, - high_pass, - smoothing, - cifti, mem_gb, - omp_nthreads, name="alff_wf", ): """Compute alff for both nifti and cifti. @@ -45,28 +40,16 @@ def init_alff_wf( from xcp_d.workflows.restingstate import init_alff_wf wf = init_alff_wf( name_source="/path/to/file.nii.gz", - output_dir=".", TR=2., - low_pass=0.1, - high_pass=0.01, - smoothing=6, - cifti=False, mem_gb=0.1, - omp_nthreads=1, name="alff_wf", ) Parameters ---------- name_source - %(output_dir)s %(TR)s - %(low_pass)s - %(high_pass)s - %(smoothing)s - %(cifti)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "compute_alff_wf". @@ -85,6 +68,13 @@ def init_alff_wf( smoothed_alff smoothed alff output """ + output_dir = config.execution.output_dir + high_pass = config.workflow.lower_bpf + low_pass = config.workflow.upper_bpf + smoothing = config.workflow.smoothing + cifti = config.workflow.cifti + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) workflow.__desc__ = f""" \ @@ -217,9 +207,7 @@ def init_alff_wf( @fill_doc def init_reho_cifti_wf( name_source, - output_dir, mem_gb, - omp_nthreads, name="cifti_reho_wf", ): """Compute ReHo from surface+volumetric (CIFTI) data. @@ -233,18 +221,14 @@ def init_reho_cifti_wf( wf = init_reho_cifti_wf( name_source="/path/to/bold.dtseries.nii", - output_dir=".", mem_gb=0.1, - omp_nthreads=1, name="cifti_reho_wf", ) Parameters ---------- name_source - %(output_dir)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "cifti_reho_wf". @@ -259,6 +243,9 @@ def init_reho_cifti_wf( reho ReHo in a CIFTI file. """ + output_dir = config.execution.output_dir + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) workflow.__desc__ = """ @@ -372,9 +359,7 @@ def init_reho_cifti_wf( @fill_doc def init_reho_nifti_wf( name_source, - output_dir, mem_gb, - omp_nthreads, name="nifti_reho_wf", ): """Compute ReHo on volumetric (NIFTI) data. @@ -387,18 +372,14 @@ def init_reho_nifti_wf( from xcp_d.workflows.restingstate import init_reho_nifti_wf wf = init_reho_nifti_wf( name_source="/path/to/bold.nii.gz", - output_dir=".", mem_gb=0.1, - omp_nthreads=1, name="nifti_reho_wf", ) Parameters ---------- name_source - %(output_dir)s %(mem_gb)s - %(omp_nthreads)s %(name)s Default is "nifti_reho_wf". @@ -415,6 +396,9 @@ def init_reho_nifti_wf( reho reho output """ + output_dir = config.execution.output_dir + omp_nthreads = config.nipype.omp_nthreads + workflow = Workflow(name=name) workflow.__desc__ = """ Regional homogeneity (ReHo) [@jiang2016regional] was computed with neighborhood voxels using