def compute_iqms(settings, name='ComputeIQMs'): """Workflow that actually computes the IQMs""" workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'subject_id', 'session_id', 'task_id', 'run_id', 'orig', 'epi_mean', 'brainmask', 'hmc_epi', 'hmc_fd', 'in_tsnr', 'metadata']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_dvars', 'outliers', 'out_spikes', 'out_fft']), name='outputnode') deriv_dir = check_folder(op.abspath(op.join(settings['output_dir'], 'derivatives'))) # Compute DVARS dvnode = pe.Node(nac.ComputeDVARS(save_plot=False, save_all=True), name='ComputeDVARS') # AFNI quality measures fwhm = pe.Node(afni.FWHMx(combine=True, detrend=True), name='smoothness') # fwhm.inputs.acf = True # add when AFNI >= 16 outliers = pe.Node(afni.OutlierCount(fraction=True, out_file='ouliers.out'), name='outliers') quality = pe.Node(afni.QualityIndex(automask=True), out_file='quality.out', name='quality') # FFT spikes finder spikes_fft = pe.Node(niu.Function( input_names=['in_file'], output_names=['n_spikes', 'out_spikes', 'out_fft'], function=slice_wise_fft), name='SpikesFinderFFT') measures = pe.Node(FunctionalQC(), name='measures') workflow.connect([ (inputnode, dvnode, [('orig', 'in_file'), ('brainmask', 'in_mask')]), (inputnode, measures, [('epi_mean', 'in_epi'), ('brainmask', 'in_mask'), ('hmc_epi', 'in_hmc'), ('hmc_fd', 'in_fd'), ('in_tsnr', 'in_tsnr')]), (inputnode, fwhm, [('epi_mean', 'in_file'), ('brainmask', 'mask')]), (inputnode, spikes_fft, [('orig', 'in_file')]), (inputnode, quality, [('hmc_epi', 'in_file')]), (inputnode, outliers, [('hmc_epi', 'in_file'), ('brainmask', 'mask')]), (dvnode, measures, [('out_all', 'in_dvars')]), (dvnode, outputnode, [('out_all', 'out_dvars')]), (outliers, outputnode, [('out_file', 'outliers')]), (spikes_fft, outputnode, [('out_spikes', 'out_spikes'), ('out_fft', 'out_fft')]) ]) # Save to JSON file datasink = pe.Node(IQMFileSink( modality='bold', out_dir=deriv_dir), name='datasink') workflow.connect([ (inputnode, datasink, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('task_id', 'task_id'), ('run_id', 'run_id'), ('metadata', 'metadata')]), (outliers, datasink, [(('out_file', _parse_tout), 'aor')]), (quality, datasink, [(('out_file', _parse_tqual), 'aqi')]), (measures, datasink, [('out_qc', 'root')]), (spikes_fft, datasink, [('n_spikes', 'spikes_num')]), (fwhm, datasink, [(('fwhm', fwhm_dict), 'root0')]), (datasink, outputnode, [('out_file', 'out_file')]) ]) return workflow
# and expects an output (what the function returns) called subs. The actual function # which was created above is called get_subs. # I can assign the input either through a workflow connect syntax or by simplying hardcoding it. # in this case I hard coded it by saying that .inputs.func_files = func_files getsubs = pe.Node(Function(input_names=['func_files'], output_names=['subs'], function=get_subs), name='getsubs') getsubs.inputs.func_files = func_files # Here I am inputing just the first run functional data # I want to use afni's 3dToutcount to find the number of # outliers at each volume. I will use this information to # later select the earliest volume with the least number of outliers # to serve as the base for the motion correction id_outliers = pe.Node(afni.OutlierCount(), name='id_outliers') id_outliers.inputs.in_file = func_files[0] id_outliers.inputs.automask = True id_outliers.inputs.legendre = True id_outliers.inputs.polort = 4 id_outliers.inputs.out_file = 'outlier_file' ''' CURRENTLY CRASHING COMMENTING OUT TO WORK ON LATER #ATM ONLY: Add an unwarping mapnode here using the field maps calc_distor_corr = pe.Node(afni.Qwarp(), name = 'calc_distor_corr') calc_distor_corr.inputs.plusminus = True calc_distor_corr.inputs.pblur = [0.05, 0.05] calc_distor_corr.inputs.minpatch = 9 calc_distor_corr.inputs.noweight = True calc_distor_corr.inputs.outputtype = 'NIFTI_GZ'
# and expects an output (what the function returns) called subs. The actual function # which was created above is called get_subs. # I can assign the input either through a workflow connect syntax or by simplying hardcoding it. # in this case I hard coded it by saying that .inputs.func_files = func_files getsubs = pe.Node(Function(input_names=['func_files'], output_names=['subs'], function=get_subs), name='getsubs') getsubs.inputs.func_files = func_files # Here I am inputing just the first run functional data # I want to use afni's 3dToutcount to find the number of # outliers at each volume. I will use this information to # later select the earliest volume with the least number of outliers # to serve as the base for the motion correction id_outliers = pe.Node(afni.OutlierCount(), name = 'id_outliers') id_outliers.inputs.in_file = func_files[0] id_outliers.inputs.automask = True id_outliers.inputs.legendre = True id_outliers.inputs.polort = 4 id_outliers.inputs.out_file = 'outlier_file' ''' CURRENTLY CRASHING COMMENTING OUT TO WORK ON LATER #ATM ONLY: Add an unwarping mapnode here using the field maps calc_distor_corr = pe.Node(afni.Qwarp(), name = 'calc_distor_corr') calc_distor_corr.inputs.plusminus = True calc_distor_corr.inputs.pblur = [0.05, 0.05] calc_distor_corr.inputs.minpatch = 9
def compute_iqms(settings, name='ComputeIQMs'): """ Workflow that actually computes the IQMs .. workflow:: from mriqc.workflows.functional import compute_iqms wf = compute_iqms(settings={'output_dir': 'out'}) """ from .utils import _tofloat from ..interfaces.transitional import GCOR biggest_file_gb = settings.get("biggest_file_size_gb", 1) workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file', 'in_ras', 'epi_mean', 'brainmask', 'hmc_epi', 'hmc_fd', 'fd_thres', 'in_tsnr', 'metadata', 'exclude_index']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_dvars', 'outliers', 'out_spikes', 'out_fft']), name='outputnode') # Set FD threshold inputnode.inputs.fd_thres = settings.get('fd_thres', 0.2) # Compute DVARS dvnode = pe.Node(nac.ComputeDVARS(save_plot=False, save_all=True), name='ComputeDVARS', mem_gb=biggest_file_gb * 3) # AFNI quality measures fwhm_interface = get_fwhmx() fwhm = pe.Node(fwhm_interface, name='smoothness') # fwhm.inputs.acf = True # add when AFNI >= 16 outliers = pe.Node(afni.OutlierCount(fraction=True, out_file='outliers.out'), name='outliers', mem_gb=biggest_file_gb * 2.5) quality = pe.Node(afni.QualityIndex(automask=True), out_file='quality.out', name='quality', mem_gb=biggest_file_gb * 3) gcor = pe.Node(GCOR(), name='gcor', mem_gb=biggest_file_gb * 2) measures = pe.Node(FunctionalQC(), name='measures', mem_gb=biggest_file_gb * 3) workflow.connect([ (inputnode, dvnode, [('hmc_epi', 'in_file'), ('brainmask', 'in_mask')]), (inputnode, measures, [('epi_mean', 'in_epi'), ('brainmask', 'in_mask'), ('hmc_epi', 'in_hmc'), ('hmc_fd', 'in_fd'), ('fd_thres', 'fd_thres'), ('in_tsnr', 'in_tsnr')]), (inputnode, fwhm, [('epi_mean', 'in_file'), ('brainmask', 'mask')]), (inputnode, quality, [('hmc_epi', 'in_file')]), (inputnode, outliers, [('hmc_epi', 'in_file'), ('brainmask', 'mask')]), (inputnode, gcor, [('hmc_epi', 'in_file'), ('brainmask', 'mask')]), (dvnode, measures, [('out_all', 'in_dvars')]), (fwhm, measures, [(('fwhm', _tofloat), 'in_fwhm')]), (dvnode, outputnode, [('out_all', 'out_dvars')]), (outliers, outputnode, [('out_file', 'outliers')]) ]) # Add metadata meta = pe.Node(ReadSidecarJSON(), name='metadata', run_without_submitting=True) addprov = pe.Node(niu.Function(function=_add_provenance), name='provenance', run_without_submitting=True) addprov.inputs.settings = { 'fd_thres': settings.get('fd_thres', 0.2), 'hmc_fsl': settings.get('hmc_fsl', True), 'webapi_url': settings.get('webapi_url'), 'webapi_port': settings.get('webapi_port'), } # Save to JSON file datasink = pe.Node(IQMFileSink( modality='bold', out_dir=str(settings['output_dir']), dataset=settings.get('dataset_name', 'unknown')), name='datasink', run_without_submitting=True) workflow.connect([ (inputnode, datasink, [('in_file', 'in_file'), ('exclude_index', 'dummy_trs')]), (inputnode, meta, [('in_file', 'in_file')]), (inputnode, addprov, [('in_file', 'in_file')]), (meta, datasink, [('subject', 'subject_id'), ('session', 'session_id'), ('task', 'task_id'), ('acquisition', 'acq_id'), ('reconstruction', 'rec_id'), ('run', 'run_id'), ('out_dict', 'metadata')]), (addprov, datasink, [('out', 'provenance')]), (outliers, datasink, [(('out_file', _parse_tout), 'aor')]), (gcor, datasink, [(('out', _tofloat), 'gcor')]), (quality, datasink, [(('out_file', _parse_tqual), 'aqi')]), (measures, datasink, [('out_qc', 'root')]), (datasink, outputnode, [('out_file', 'out_file')]) ]) # FFT spikes finder if settings.get('fft_spikes_detector', False): from .utils import slice_wise_fft spikes_fft = pe.Node(niu.Function( input_names=['in_file'], output_names=['n_spikes', 'out_spikes', 'out_fft'], function=slice_wise_fft), name='SpikesFinderFFT') workflow.connect([ (inputnode, spikes_fft, [('in_ras', 'in_file')]), (spikes_fft, outputnode, [('out_spikes', 'out_spikes'), ('out_fft', 'out_fft')]), (spikes_fft, datasink, [('n_spikes', 'spikes_num')]) ]) return workflow
def compute_iqms(settings, name='ComputeIQMs'): """ Workflow that actually computes the IQMs .. workflow:: from mriqc.workflows.functional import compute_iqms wf = compute_iqms(settings={'output_dir': 'out'}) """ from mriqc.workflows.utils import _tofloat biggest_file_gb = settings.get("biggest_file_size_gb", 1) workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'subject_id', 'session_id', 'task_id', 'acq_id', 'rec_id', 'run_id', 'orig', 'epi_mean', 'brainmask', 'hmc_epi', 'hmc_fd', 'fd_thres', 'in_tsnr', 'metadata' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_dvars', 'outliers', 'out_spikes', 'out_fft']), name='outputnode') #Set FD threshold inputnode.inputs.fd_thres = settings.get('fd_thres', 0.2) deriv_dir = check_folder( op.abspath(op.join(settings['output_dir'], 'derivatives'))) # Compute DVARS dvnode = pe.Node(nac.ComputeDVARS(save_plot=False, save_all=True), name='ComputeDVARS') dvnode.interface.estimated_memory_gb = biggest_file_gb * 3 # AFNI quality measures fwhm = pe.Node(afni.FWHMx(combine=True, detrend=True), name='smoothness') # fwhm.inputs.acf = True # add when AFNI >= 16 outliers = pe.Node(afni.OutlierCount(fraction=True, out_file='ouliers.out'), name='outliers') outliers.interface.estimated_memory_gb = biggest_file_gb * 2.5 quality = pe.Node(afni.QualityIndex(automask=True), out_file='quality.out', name='quality') quality.interface.estimated_memory_gb = biggest_file_gb * 3 measures = pe.Node(FunctionalQC(), name='measures') measures.interface.estimated_memory_gb = biggest_file_gb * 3 workflow.connect([(inputnode, dvnode, [('hmc_epi', 'in_file'), ('brainmask', 'in_mask')]), (inputnode, measures, [('epi_mean', 'in_epi'), ('brainmask', 'in_mask'), ('hmc_epi', 'in_hmc'), ('hmc_fd', 'in_fd'), ('fd_thres', 'fd_thres'), ('in_tsnr', 'in_tsnr')]), (inputnode, fwhm, [('epi_mean', 'in_file'), ('brainmask', 'mask')]), (inputnode, quality, [('hmc_epi', 'in_file')]), (inputnode, outliers, [('hmc_epi', 'in_file'), ('brainmask', 'mask')]), (dvnode, measures, [('out_all', 'in_dvars')]), (fwhm, measures, [(('fwhm', _tofloat), 'in_fwhm')]), (dvnode, outputnode, [('out_all', 'out_dvars')]), (outliers, outputnode, [('out_file', 'outliers')])]) # Save to JSON file datasink = pe.Node(IQMFileSink(modality='bold', out_dir=deriv_dir), name='datasink') workflow.connect([ (inputnode, datasink, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('task_id', 'task_id'), ('acq_id', 'acq_id'), ('rec_id', 'rec_id'), ('run_id', 'run_id'), ('metadata', 'metadata')]), (outliers, datasink, [(('out_file', _parse_tout), 'aor')]), (quality, datasink, [(('out_file', _parse_tqual), 'aqi')]), (measures, datasink, [('out_qc', 'root')]), (datasink, outputnode, [('out_file', 'out_file')]) ]) if settings.get('fft_spikes_detector', False): # FFT spikes finder spikes_fft = pe.Node(niu.Function( input_names=['in_file'], output_names=['n_spikes', 'out_spikes', 'out_fft'], function=slice_wise_fft), name='SpikesFinderFFT') workflow.connect([ (inputnode, spikes_fft, [('orig', 'in_file')]), (spikes_fft, outputnode, [('out_spikes', 'out_spikes'), ('out_fft', 'out_fft')]), (spikes_fft, datasink, [('n_spikes', 'spikes_num')]) ]) return workflow
def create_rs_qc(subjectlist): # main workflow for extended qc of diffusion/rsfmri data # fsl output type fsl.FSLCommand.set_default_output_type('NIFTI_GZ') # some hard coded things fd_thres = 0.2 tr = 2 # Specify the location of the preprocessed data data_dir = "/data/pt_life/LIFE_fu/wd_preprocessing/hcp_prep_workflow/resting/" working_dir = "/data/pt_life/LIFE_fu/wd_preprocessing/" #MODIFY freesurfer_dir = "/data/pt_life_freesurfer/freesurfer_all" qc = Workflow(name="qc") qc.base_dir = working_dir + '/' qc.config['execution']['crashdump_dir'] = qc.base_dir + "/crash_files" qc.config['execution'] = {'hash_method': 'content'} #first get all data needed identitynode = Node(util.IdentityInterface(fields=['subject']), name='identitynode') identitynode.iterables = ('subject', subjectlist) info = dict(func=[[ 'transform_timeseries/', '_subject_', 'subj', '/merge/rest2anat.nii.gz' ]], dvars=[[ 'transform_timeseries/', '_subject_', 'subj', '/dvars/rest2anat_dvars.tsv' ]], motpars=[[ '/motion_correction/', '_subject_', 'subj', '/mcflirt/rest_realigned.nii.gz.par' ]], brainmask=[[ 'transform_timeseries/', '_subject_', 'subj', '/resample_brain/T1_brain_mask_lowres.nii.gz' ]]) ds_rs = Node(interface=nio.DataGrabber( infields=['subj'], outfields=['func', 'dvars', 'motpars', 'brainmask']), name='ds_rs') ds_rs.inputs.base_directory = data_dir ds_rs.inputs.template = '%s%s%s%s' ds_rs.inputs.template_args = info ds_rs.inputs.sort_filelist = True def juggle_subj(input_id): import pandas as pd from datetime import datetime as dt import os import random, string sic_pseudo = pd.read_csv( "/data/gh_gr_agingandobesity_share/life_shared/Data/Preprocessed/derivatives/pseudo_mrt_20201214.csv" ) tmp = sic_pseudo.loc[sic_pseudo.sic == input_id, 'pseudonym'] pseudo = tmp.get_values()[0] + "_fu" return pseudo rename = Node(util.Function(input_names=['input_id'], output_names=['output_id'], function=juggle_subj), name="rename") get_fs = Node(nio.FreeSurferSource(), name="get_fs") get_fs.inputs.subjects_dir = freesurfer_dir get_correct_aseg = Node(util.Function(input_names=['in_list'], output_names=['out_aseg'], function=get_aseg), name="get_correct_aseg") convert = Node(fs.MRIConvert(), name="convert") convert.inputs.out_type = "niigz" downsample = Node(afni.Resample(resample_mode='NN', outputtype='NIFTI_GZ', out_file='aparcaseg_lowres.nii.gz'), name='downsample') calc_fd_official = Node(FramewiseDisplacement(parameter_source='FSL'), name='calc_fd_official') calc_fd = Node(util.Function( input_names=['realignment_parameters_file', 'parameter_source'], output_names=['FD_power', 'fn'], function=calc_frame_displacement), name="calc_fd") calc_fd.inputs.parameter_source = 'FSL' outliers = Node(afni.OutlierCount(fraction=True, out_file='outliers.out'), name='outliers', mem_gb=1 * 2.5) bigplot = Node(util.Function(input_names=[ 'func', 'seg', 'tr', 'fd_thres', 'outliers', 'dvars', 'fd', 'subj', 'outfile' ], output_names=['fn', 'dataframe'], function=make_the_plot), name="bigplot") bigplot.inputs.tr = tr bigplot.inputs.fd_thres = fd_thres bigplot.inputs.outfile = "summary_fmriplot.png" fftplot = Node(util.Function(input_names=['fn_pd', 'tr'], output_names=['fn'], function=plot_fft), name="fftplot") fftplot.inputs.tr = tr datasink = Node(name="datasink", interface=nio.DataSink()) datasink.inputs.base_directory = "/data/pt_life_restingstate_followup/Results/QA" datasink.inputs.substitutions = [('_subject_', '')] qc.connect([ (identitynode, rename, [('subject', 'input_id')]), (rename, get_fs, [('output_id', 'subject_id')]), (identitynode, ds_rs, [('subject', 'subj')]), (identitynode, bigplot, [('subject', 'subj')]), (get_fs, get_correct_aseg, [('aparc_aseg', 'in_list')]), (get_correct_aseg, convert, [('out_aseg', 'in_file')]), (convert, downsample, [('out_file', 'in_file')]), (ds_rs, downsample, [('func', 'master')]), (downsample, bigplot, [('out_file', 'seg')]), (ds_rs, calc_fd, [('motpars', 'realignment_parameters_file')]), (ds_rs, calc_fd_official, [('motpars', 'in_file')]), (ds_rs, bigplot, [('func', 'func')]), (ds_rs, bigplot, [('dvars', 'dvars')]), (calc_fd, bigplot, [('fn', 'fd')]), #FD_power (ds_rs, outliers, [('func', 'in_file')]), (ds_rs, outliers, [('brainmask', 'mask')]), (outliers, bigplot, [('out_file', 'outliers')]), (bigplot, datasink, [('fn', 'detailedQA.@bigplot')]), (bigplot, fftplot, [('dataframe', 'fn_pd')]), (bigplot, datasink, [('dataframe', 'detailedQA.metrics.@dataframe')]), (fftplot, datasink, [('fn', 'detailedQA.@fftplot')]), (calc_fd, datasink, [('fn', 'detailedQA.metrics.@fd')]), (calc_fd_official, datasink, [('out_file', 'detailedQA.metrics.@fd_official')]) ]) qc.run(plugin="MultiProc", plugin_args={"n_procs": 16, "non_daemon": True}) return qc
def create_rs_qc(subjectlist): # main workflow for extended qc of diffusion/rsfmri data # fsl output type fsl.FSLCommand.set_default_output_type('NIFTI_GZ') # some hard coded things fd_thres = 0.2 tr = 1.4 # Specify the location of the preprocessed data data_dir = "/data/pt_02030/wd_preprocessing/hcp_prep_workflow/resting/" working_dir = "/data/pt_02030/wd_preprocessing/" #MODIFY freesurfer_dir = "/data/pt_02030/preprocessed/freesurfer/" qc = Workflow(name="qc") qc.base_dir = working_dir + '/' qc.config['execution']['crashdump_dir'] = qc.base_dir + "/crash_files" #first get all data needed identitynode = Node(util.IdentityInterface(fields=['subject']), name='identitynode') identitynode.iterables = ('subject', subjectlist) info = dict(func=[[ 'transform_timeseries/', '_subject_', 'subj', '/merge/rest2anat.nii.gz' ]], dvars=[[ 'transform_timeseries/', '_subject_', 'subj', '/dvars/rest2anat_dvars.tsv' ]], motpars=[[ '/motion_correction/', '_subject_', 'subj', '/mcflirt/rest_realigned.nii.gz.par' ]], brainmask=[[ 'transform_timeseries/', '_subject_', 'subj', '/resample_brain/T1_brain_mask_lowres.nii.gz' ]]) ds_rs = Node(interface=nio.DataGrabber( infields=['subj'], outfields=['func', 'dvars', 'motpars', 'brainmask']), name='ds_rs') ds_rs.inputs.base_directory = data_dir ds_rs.inputs.template = '%s%s%s%s' ds_rs.inputs.template_args = info ds_rs.inputs.sort_filelist = True get_fs = Node(nio.FreeSurferSource(), name="get_fs") get_fs.inputs.subjects_dir = freesurfer_dir get_correct_aseg = Node(util.Function(input_names=['in_list'], output_names=['out_aseg'], function=get_aseg), name="get_correct_aseg") convert = Node(fs.MRIConvert(), name="convert") convert.inputs.out_type = "niigz" downsample = Node(afni.Resample(resample_mode='NN', outputtype='NIFTI_GZ', out_file='aparcaseg_lowres.nii.gz'), name='downsample') calc_fd = Node(util.Function( input_names=['realignment_parameters_file', 'parameter_source'], output_names=['FD_power', 'fn'], function=calc_frame_displacement), name="calc_fd") calc_fd.inputs.parameter_source = 'FSL' outliers = Node(afni.OutlierCount(fraction=True, out_file='outliers.out'), name='outliers', mem_gb=1 * 2.5) bigplot = Node(util.Function(input_names=[ 'func', 'seg', 'tr', 'fd_thres', 'outliers', 'dvars', 'fd', 'subj', 'outfile' ], output_names=['fn', 'dataframe'], function=make_the_plot), name="bigplot") bigplot.inputs.tr = tr bigplot.inputs.fd_thres = fd_thres bigplot.inputs.outfile = "summary_fmriplot.png" fftplot = Node(util.Function(input_names=['fn_pd', 'tr'], output_names=['fn'], function=plot_fft), name="fftplot") fftplot.inputs.tr = tr datasink = Node(name="datasink", interface=nio.DataSink()) datasink.inputs.base_directory = "/data/pt_02030/preprocessed/reports/" datasink.inputs.substitutions = [('_subject_', '')] qc.connect([(identitynode, get_fs, [('subject', 'subject_id')]), (identitynode, ds_rs, [('subject', 'subj')]), (identitynode, bigplot, [('subject', 'subj')]), (get_fs, get_correct_aseg, [('aparc_aseg', 'in_list')]), (get_correct_aseg, convert, [('out_aseg', 'in_file')]), (convert, downsample, [('out_file', 'in_file')]), (ds_rs, downsample, [('func', 'master')]), (downsample, bigplot, [('out_file', 'seg')]), (ds_rs, calc_fd, [('motpars', 'realignment_parameters_file')]), (ds_rs, bigplot, [('func', 'func')]), (ds_rs, bigplot, [('dvars', 'dvars')]), (calc_fd, bigplot, [('FD_power', 'fd')]), (ds_rs, outliers, [('func', 'in_file')]), (ds_rs, outliers, [('brainmask', 'mask')]), (outliers, bigplot, [('out_file', 'outliers')]), (bigplot, datasink, [('fn', 'detailedQA.@bigplot')]), (bigplot, fftplot, [('dataframe', 'fn_pd')]), (bigplot, datasink, [('dataframe', 'detailedQA.metrics.@dataframe')]), (fftplot, datasink, [('fn', 'detailedQA.@fftplot')]), (calc_fd, datasink, [('fn', 'detailedQA.metrics.@fd')])]) qc.run(plugin="MultiProc", plugin_args={"n_procs": 16, "non_daemon": True}) return qc