Exemplo n.º 1
0
def common_select(bids_base, out_base, workflow_name, template,
                  registration_mask, functional_match, structural_match,
                  subjects, sessions):
    """Common selection and variable processing function for SAMRI preprocessing workflows."""

    if template:
        if template == "mouse":
            template = '/usr/share/mouse-brain-atlases/dsurqec_200micron.nii'
            registration_mask = '/usr/share/mouse-brain-atlases/dsurqec_200micron_mask.nii'
        elif template == "rat":
            from samri.fetch.templates import fetch_rat_waxholm
            template = fetch_rat_waxholm()['template']
            registration_mask = fetch_rat_waxholm()['mask']
        else:
            if template:
                template = path.abspath(path.expanduser(template))
            if registration_mask:
                registration_mask = path.abspath(
                    path.expanduser(registration_mask))
    else:
        raise ValueError("No species or template path specified")
        return -1

    bids_base = path.abspath(path.expanduser(bids_base))
    if not out_base:
        out_base = path.join(bids_base, 'preprocessing')
    else:
        out_base = path.abspath(path.expanduser(out_base))
    out_dir = path.join(out_base, workflow_name)

    data_selection = bids_data_selection(bids_base, structural_match,
                                         functional_match, subjects, sessions)
    workdir = out_dir + '_work'
    if not os.path.exists(workdir):
        os.makedirs(workdir)
    data_selection.to_csv(path.join(workdir, 'data_selection.csv'))

    # generate functional and structural scan types
    functional_scan_types = data_selection.loc[data_selection.type ==
                                               'func']['acq'].values
    structural_scan_types = data_selection.loc[data_selection.type ==
                                               'anat']['acq'].values
    # we start to define nipype workflow elements (nodes, connections, meta)
    subjects_sessions = data_selection[["subject", "session"
                                        ]].drop_duplicates().values.tolist()

    _func_ind = data_selection[data_selection["type"] == "func"]
    func_ind = _func_ind.index.tolist()

    _struct_ind = data_selection[data_selection["type"] == "anat"]
    struct_ind = _struct_ind.index.tolist()

    if True:
        print(data_selection)
        print(subjects_sessions)
    return bids_base, out_base, out_dir, template, registration_mask, data_selection, functional_scan_types, structural_scan_types, subjects_sessions, func_ind, struct_ind
Exemplo n.º 2
0
    def __init__(self, work_dir: Path, masking_config_path: str):
        self.masking_config_path = masking_config_path
        self.work_dir = work_dir
        self.bids_path = work_dir / 'bids'
        self.preprocessing_dir = work_dir / 'preprocessing'
        self.data_selection = bids_data_selection(str(self.bids_path), False,
                                                  False, False, False)

        self.tmean_dir = self.preprocessing_dir / 'bids_tmean'
        self.tmean_dir.mkdir(exist_ok=True, parents=True)
Exemplo n.º 3
0
def common_select(bids_base, out_base, workflow_name, template,
                  registration_mask, functional_match, structural_match,
                  subjects, sessions, exclude):
    """Common selection and variable processing function for SAMRI preprocessing workflows.

	Parameters
	----------

	bids_base : string
		Path to the BIDS root directory.
	out_base : string
		Output base directory - inside which a directory named `workflow_name` (as well as associated directories) will be created.
	workflow_name : string
		Top level name for the output directory.
	template : string
		Path to the template to register the data to.
	registration_mask : string
		Mask to use for the registration process.
	functional_match : dict
		Dictionary specifying a whitelist to use for functional data inclusion into the workflow; if dictionary is empty no whitelist is present and all data will be considered.
	structural_match : dict
		Dictionary specifying a whitelist to use for structural data inclusion into the workflow; if dictionary is empty no whitelist is present and all data will be considered.
	subjects : list
		 A whitelist of subjects to include in the workflow, if the list is empty there is no whitelist and all sessions will be considered.
	sessions : list
		A whitelist of sessions to include in the workflow, if the list is empty there is no whitelist and all sessions will be considered.
	exclude : dict
		A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values.
		If this is specified, matching entries will be excluded in the analysis.

	Returns
	-------

	bids_base : string
		Path to the BIDS root directory.
	out_base : string
		Output base directory - inside which a directory named `workflow_name` (as well as associated directories) is located.
	out_dir : string
		Directory where output is located (gives path to workflow_name).
	template : string
		Full path to the template.
	registration_mask : string
		Full path to the registration mask.
	data_selection : df
		A Pandas dataframe of data from bids_base filtered according to structural_match, functional_match, subjects, and sessions.
	functional_scan_types : np array
		Functional scan types.
	structural_scan_types : np array
		Structural scan types.
	subjects_sessions : df
		Pandas dataframe giving names of subjects and sessions selected from bids_base.
	func_ind: list
		List of all functional scan entries.
	struct_ind: list
		List of all structural scan entries.
	"""

    if template:
        if template == "mouse":
            template = '/usr/share/mouse-brain-atlases/dsurqec_200micron.nii'
            registration_mask = '/usr/share/mouse-brain-atlases/dsurqec_200micron_mask.nii'
        elif template == "rat":
            from samri.fetch.templates import fetch_rat_waxholm
            template = fetch_rat_waxholm()['template']
            registration_mask = fetch_rat_waxholm()['mask']
        else:
            if template:
                template = path.abspath(path.expanduser(template))
            if registration_mask:
                registration_mask = path.abspath(
                    path.expanduser(registration_mask))
    else:
        raise ValueError("No species or template path specified")
        return -1

    bids_base = path.abspath(path.expanduser(bids_base))
    if not out_base:
        out_base = path.join(bids_base, 'preprocessing')
    else:
        out_base = path.abspath(path.expanduser(out_base))
    out_dir = path.join(out_base, workflow_name)

    data_selection = bids_data_selection(bids_base, structural_match,
                                         functional_match, subjects, sessions)
    workdir = out_dir + '_work'
    if not os.path.exists(workdir):
        os.makedirs(workdir)
    data_selection.to_csv(path.join(workdir, 'data_selection.csv'))

    # generate functional and structural scan types
    # PyBIDS 0.6.5 and 0.10.2 compatibility
    try:
        functional_scan_types = data_selection.loc[data_selection['type'] ==
                                                   'func']['acq'].values
        structural_scan_types = data_selection.loc[data_selection['type'] ==
                                                   'anat']['acq'].values
    except KeyError:
        functional_scan_types = data_selection.loc[
            data_selection['datatype'] == 'func']['acquisition'].values
        structural_scan_types = data_selection.loc[
            data_selection['datatype'] == 'anat']['acquisition'].values
    # we start to define nipype workflow elements (nodes, connections, meta)
    subjects_sessions = data_selection[["subject", "session"
                                        ]].drop_duplicates().values.tolist()

    if exclude:
        for key in exclude:
            data_selection = data_selection[~data_selection[key].
                                            isin(exclude[key])]

    # PyBIDS 0.6.5 and 0.10.2 compatibility
    try:
        _func_ind = data_selection[data_selection["type"] == "func"]
        _struct_ind = data_selection[data_selection["type"] == "anat"]
    except KeyError:
        _func_ind = data_selection[data_selection["datatype"] == "func"]
        _struct_ind = data_selection[data_selection["datatype"] == "anat"]

    func_ind = _func_ind.index.tolist()
    struct_ind = _struct_ind.index.tolist()

    if True:
        print(data_selection)
        print(subjects_sessions)
    return bids_base, out_base, out_dir, template, registration_mask, data_selection, functional_scan_types, structural_scan_types, subjects_sessions, func_ind, struct_ind
Exemplo n.º 4
0
def l2_common_effect(l1_dir,
	groupby="session",
	keep_work=False,
	loud=False,
	tr=1,
	nprocs=6,
	mask="/usr/share/mouse-brain-atlases/dsurqec_200micron_mask.nii",
	match={},
	n_jobs_percentage=1,
	out_base="",
	subjects=[],
	sessions=[],
	tasks=[],
	exclude={},
	include={},
	workflow_name="generic",
	debug=False,
	target_set=[],
	):
	"""Determine the common effect in a sample of 3D feature maps.

	Parameters
	----------

	n_jobs_percentage : float, optional
		Percentage of the cores present on the machine which to maximally use for deploying jobs in parallel.
	"""

	from samri.pipelines.utils import bids_data_selection

	l1_dir = path.abspath(path.expanduser(l1_dir))
	out_base = path.abspath(path.expanduser(out_base))
	mask=path.abspath(path.expanduser(mask))

	data_selection = bids_data_selection(l1_dir,
		structural_match=False,
		functional_match=match,
		subjects=False,
		sessions=False,
		verbose=True,
		)
	ind = data_selection.index.tolist()

	out_dir = path.join(out_base,workflow_name)
	workdir_name = workflow_name+'_work'
	workdir = path.join(out_base,workdir_name)
	if not os.path.exists(workdir):
		os.makedirs(workdir)

	data_selection = data_selection.sort_values(['session', 'subject'], ascending=[1, 1])
	if exclude:
		for key in exclude:
			data_selection = data_selection[~data_selection[key].isin(exclude[key])]
	if include:
		for key in include:
			data_selection = data_selection[data_selection[key].isin(include[key])]
	data_selection.to_csv(path.join(workdir,'data_selection.csv'))

	copemerge = pe.Node(interface=fsl.Merge(dimension='t'),name="copemerge")
	varcopemerge = pe.Node(interface=fsl.Merge(dimension='t'),name="varcopemerge")

	level2model = pe.Node(interface=fsl.L2Model(),name='level2model')

	flameo = pe.Node(interface=fsl.FLAMEO(), name="flameo")
	flameo.inputs.mask_file = mask
	flameo.inputs.run_mode = "ols"

	datasink = pe.Node(nio.DataSink(), name='datasink')
	datasink.inputs.base_directory = out_dir
	datasink_substitutions = [('_iterable_', '')]

	if groupby == "subject_set":
		datasink_substitutions.extend([('subject', 'sub-')])
		common_fields = ''
		common_fields += 'acq-'+data_selection.acq.drop_duplicates().item()
		try:
			common_fields += '_run-'+data_selection.run.drop_duplicates().item()
		except ValueError:
			pass

		infosource = pe.Node(interface=util.IdentityInterface(fields=['iterable']), name="infosource")
		infosource.iterables = [('iterable', target_set)]

		copes = pe.Node(name='copes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		copes.inputs.bids_dictionary_override = {'modality':'cope'}
		copes.inputs.df = data_selection
		copes.inputs.list_output = True

		varcopes = pe.Node(name='varcopes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		varcopes.inputs.bids_dictionary_override = {'modality':'varcb'}
		varcopes.inputs.df = data_selection
		varcopes.inputs.list_output = True

		workflow_connections = [
			(infosource, copes, [('iterable', 'bids_dictionary')]),
			(infosource, varcopes, [('iterable', 'bids_dictionary')]),
			(infosource, copemerge, [(('iterable',dict_and_suffix,"subject","_cope.nii.gz"), 'merged_file')]),
			(infosource, varcopemerge, [(('iterable',dict_and_suffix,"subject","_varcb.nii.gz"), 'merged_file')]),
			]
	if groupby == "subject":
		datasink_substitutions.extend([('subject', 'sub-')])
		common_fields = ''
		common_fields += 'acq-'+data_selection.acq.drop_duplicates().item()
		try:
			common_fields += '_run-'+data_selection.run.drop_duplicates().item()
		except ValueError:
			pass

		subjects = data_selection[['subject']].drop_duplicates()
		# TODO: could not find a better way to convert pandas df column into list of dicts
		subjects_ = subjects.T.to_dict()
		subjects = [subjects_[i] for i in subjects_.keys()]

		infosource = pe.Node(interface=util.IdentityInterface(fields=['iterable']), name="infosource")
		infosource.iterables = [('iterable', subjects)]

		copes = pe.Node(name='copes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		copes.inputs.bids_dictionary_override = {'modality':'cope'}
		copes.inputs.df = data_selection
		copes.inputs.list_output = True

		varcopes = pe.Node(name='varcopes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		varcopes.inputs.bids_dictionary_override = {'modality':'varcb'}
		varcopes.inputs.df = data_selection
		varcopes.inputs.list_output = True

		workflow_connections = [
			(infosource, copes, [('iterable', 'bids_dictionary')]),
			(infosource, varcopes, [('iterable', 'bids_dictionary')]),
			(infosource, copemerge, [(('iterable',dict_and_suffix,"subject","_cope.nii.gz"), 'merged_file')]),
			(infosource, varcopemerge, [(('iterable',dict_and_suffix,"subject","_varcb.nii.gz"), 'merged_file')]),
			]
	elif groupby == "subject_task":
		#does not currently work, due to missing iterator combinations (same issue as preprocessing)
		merge = pe.Node(interface=util.Merge(2), name="merge")
		infosource = pe.Node(interface=util.IdentityInterface(fields=['subject','task']), name="infosource")
		infosource.iterables = [('subject', subjects),('task', tasks)]
		datasource = pe.Node(interface=nio.DataGrabber(infields=["subject","task",], outfields=["copes", "varcbs"]), name="datasource")
		datasource.inputs.template_args = dict(
			copes=[["subject","subject","task",]],
			varcbs=[["subject","subject","task",]]
			)
		datasource.inputs.field_template = dict(
			copes="sub-%s/ses-*/sub-%s_ses-*_task-%s_cope.nii.gz",
			varcbs="sub-%s/ses-*/sub-%s_ses-*_task-%s_varcb.nii.gz",
			)
		workflow_connections = [
			(infosource, datasource, [('subject', 'subject'),('task','task')]),
			(infosource, merge, [('subject', 'in1'),('task','in2')]),
			(merge, copemerge, [(('out',add_suffix,"_cope.nii.gz"), 'merged_file')]),
			(merge, varcopemerge, [(('out',add_suffix,"_varcb.nii.gz"), 'merged_file')]),
			]
	elif groupby == "session":
		datasink_substitutions.extend([('session', 'ses-')])
		common_fields = ''
		common_fields += 'acq-'+data_selection.acq.drop_duplicates().item()
		try:
			common_fields += '_run-'+data_selection.run.drop_duplicates().item()
		except ValueError:
			pass

		sessions = data_selection[['session']].drop_duplicates()
		# TODO: could not find a better way to convert pandas df column into list of dicts
		sessions_ = sessions.T.to_dict()
		sessions = [sessions_[i] for i in sessions_.keys()]

		infosource = pe.Node(interface=util.IdentityInterface(fields=['iterable']), name="infosource")
		infosource.iterables = [('iterable', sessions)]

		copes = pe.Node(name='copes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		copes.inputs.bids_dictionary_override = {'modality':'cope'}
		copes.inputs.df = data_selection
		copes.inputs.list_output = True

		varcopes = pe.Node(name='varcopes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		varcopes.inputs.bids_dictionary_override = {'modality':'varcb'}
		varcopes.inputs.df = data_selection
		varcopes.inputs.list_output = True

		workflow_connections = [
			(infosource, copes, [('iterable', 'bids_dictionary')]),
			(infosource, varcopes, [('iterable', 'bids_dictionary')]),
			(infosource, copemerge, [(('iterable',dict_and_suffix,"session","_cope.nii.gz"), 'merged_file')]),
			(infosource, varcopemerge, [(('iterable',dict_and_suffix,"session","_varcb.nii.gz"), 'merged_file')]),
			]
	elif groupby == "task":
		datasink_substitutions.extend([('task', 'task-')])
		common_fields = ''
		common_fields += 'acq-'+data_selection.acq.drop_duplicates().item()
		try:
			common_fields += '_run-'+data_selection.run.drop_duplicates().item()
		except ValueError:
			pass
		try:
			common_fields += '_ses-'+data_selection.session.drop_duplicates().item()
		except ValueError:
			pass

		iters = data_selection[['task']].drop_duplicates()
		# TODO: could not find a better way to convert pandas df column into list of dicts
		iters_ = iters.T.to_dict()
		iters = [iters_[i] for i in iters_.keys()]

		infosource = pe.Node(interface=util.IdentityInterface(fields=['iterable']), name="infosource")
		infosource.iterables = [('iterable', iters)]

		copes = pe.Node(name='copes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		copes.inputs.bids_dictionary_override = {'modality':'cope'}
		copes.inputs.df = data_selection
		copes.inputs.list_output = True

		varcopes = pe.Node(name='varcopes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		varcopes.inputs.bids_dictionary_override = {'modality':'varcb'}
		varcopes.inputs.df = data_selection
		varcopes.inputs.list_output = True

		workflow_connections = [
			(infosource, copes, [('iterable', 'bids_dictionary')]),
			(infosource, varcopes, [('iterable', 'bids_dictionary')]),
			(infosource, copemerge, [(('iterable',dict_and_suffix,"task","_cope.nii.gz"), 'merged_file')]),
			(infosource, varcopemerge, [(('iterable',dict_and_suffix,"task","_varcb.nii.gz"), 'merged_file')]),
			]
	elif groupby == "none":
		common_fields = ''
		common_fields += 'acq-'+data_selection.acq.drop_duplicates().item()
		common_fields += '_run-'+data_selection.run.drop_duplicates().item()

		datasink_substitutions.extend([('cope1.nii.gz', common_fields+'_'+'cope.nii.gz')])
		datasink_substitutions.extend([('tstat1.nii.gz', common_fields+'_'+'tstat.nii.gz')])
		datasink_substitutions.extend([('zstat1.nii.gz', common_fields+'_'+'zstat.nii.gz')])
		datasink.inputs.substitutions = datasink_substitutions

		copes = pe.Node(name='copes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		copes.inputs.bids_dictionary_override = {'modality':'cope'}
		copes.inputs.df = data_selection
		copes.inputs.list_output = True

		varcopes = pe.Node(name='varcopes', interface=util.Function(function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection']))
		varcopes.inputs.bids_dictionary_override = {'modality':'varcb'}
		varcopes.inputs.df = data_selection
		varcopes.inputs.list_output = True

		copemerge.inputs.merged_file = 'cope.nii.gz'
		varcopemerge.inputs.merged_file = 'varcb.nii.gz'

		workflow_connections = []

	elif groupby == "mtask":
		infosource = pe.Node(interface=util.IdentityInterface(fields=['iterable']), name="infosource")
		infosource.iterables = [('iterable', tasks)]
		datasource = pe.Node(interface=nio.DataGrabber(infields=["group",], outfields=["copes", "varcbs"]), name="datasource")
		datasource.inputs.template_args = dict(
			copes=[['group']],
			varcbs=[['group']]
			)
		datasource.inputs.field_template = dict(
			copes="sub-*/ses-*/sub-*_ses-*_task-%s_cope.nii.gz ",
			varcbs="sub-*/ses-*/sub-*_ses-*_task-%s_varcb.nii.gz ",
			)
		workflow_connections = [
			(infosource, datasource, [('iterable', 'group')]),
			(infosource, copemerge, [(('iterable',add_suffix,"_cope.nii.gz"), 'merged_file')]),
			(infosource, varcopemerge, [(('iterable',add_suffix,"_varcb.nii.gz"), 'merged_file')]),
			]

	datasink_substitutions.extend([('cope1.nii.gz', common_fields+'_'+'cope.nii.gz')])
	datasink_substitutions.extend([('tstat1.nii.gz', common_fields+'_'+'tstat.nii.gz')])
	datasink_substitutions.extend([('zstat1.nii.gz', common_fields+'_'+'zstat.nii.gz')])
	datasink.inputs.substitutions = datasink_substitutions

	workflow_connections.extend([
		(copes, copemerge, [('selection', 'in_files')]),
		(varcopes, varcopemerge, [('selection', 'in_files')]),
		(varcopes, level2model, [(('selection',mylen), 'num_copes')]),
		(copemerge,flameo,[('merged_file','cope_file')]),
		(varcopemerge,flameo,[('merged_file','var_cope_file')]),
		(level2model,flameo, [('design_mat','design_file')]),
		(level2model,flameo, [('design_grp','cov_split_file')]),
		(level2model,flameo, [('design_con','t_con_file')]),
		(flameo, datasink, [('copes', '@copes')]),
		(flameo, datasink, [('fstats', '@fstats')]),
		(flameo, datasink, [('tstats', '@tstats')]),
		(flameo, datasink, [('zstats', '@zstats')]),
		])

	workflow_config = {'execution': {'crashdump_dir': path.join(out_base,'crashdump'),}}
	if debug:
		workflow_config['logging'] = {
			'workflow_level':'DEBUG',
			'utils_level':'DEBUG',
			'interface_level':'DEBUG',
			'filemanip_level':'DEBUG',
			'log_to_file':'true',
			}

	workflow = pe.Workflow(name=workdir_name)
	workflow.connect(workflow_connections)
	workflow.base_dir = out_base
	workflow.config = workflow_config
	workflow.write_graph(dotfilename=path.join(workflow.base_dir,workdir_name,"graph.dot"), graph2use="hierarchical", format="png")

	n_jobs = max(int(round(mp.cpu_count()*n_jobs_percentage)),2)
	if not loud:
		try:
			workflow.run(plugin="MultiProc", plugin_args={'n_procs' : nprocs})
		except RuntimeError:
			print("WARNING: Some expected tasks have not been found (or another RuntimeError has occured).")
		for f in listdir(getcwd()):
			if re.search("crash.*?-varcopemerge|-copemerge.*", f):
				remove(path.join(getcwd(), f))
	else:
		workflow.run(plugin="MultiProc", plugin_args={'n_procs' : n_jobs})
	if not keep_work:
		shutil.rmtree(path.join(out_base,workdir_name))
Exemplo n.º 5
0
def l1(preprocessing_dir,
	bf_path = '~/ni_data/irfs/chr_beta1.txt',
	debug=False,
	exclude={},
	habituation='confound',
	highpass_sigma=225,
	lowpass_sigma=False,
	include={},
	keep_work=False,
	out_base="",
	mask="",
	match={},
	tr=1,
	workflow_name="generic",
	modality="cbv",
	n_jobs_percentage=1,
	invert=False,
	):
	"""Calculate subject level GLM statistic scores.

	Parameters
	----------

	bf_path : str, optional
		Basis set path. It should point to a text file in the so-called FEAT/FSL "#2" format (1 entry per volume).
	exclude : dict
		A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values.
		If this is specified matching entries will be excluded in the analysis.
	debug : bool, optional
		Whether to enable nipype debug mode.
		This increases logging.
	habituation : {"", "confound", "separate_contrast", "in_main_contrast"}, optional
		How the habituation regressor should be handled.
		Anything which evaluates as False (though we recommend "") means no habituation regressor will be introduced.
	highpass_sigma : int, optional
		Highpass threshold (in seconds).
	include : dict
		A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values.
		If this is specified only matching entries will be included in the analysis.
	invert : bool
		If true the values will be inverted with respect to zero.
		This is commonly used for iron nano-particle Cerebral Blood Volume (CBV) measurements.
	keep_work : bool, optional
		Whether to keep the work directory (containing all the intermediary workflow steps, as managed by nipypye).
		This is useful for debugging and quality control.
	out_base : str, optional
		Path to the directory inside which both the working directory and the output directory will be created.
	mask : str, optional
		Path to the brain mask which shall be used to define the brain volume in the analysis.
		This has to point to an existing NIfTI file containing zero and one values only.
	n_jobs_percentage : float, optional
		Percentage of the cores present on the machine which to maximally use for deploying jobs in parallel.
	tr : int, optional
		Repetition time, in seconds.
	workflow_name : str, optional
		Name of the workflow; this will also be the name of the final output directory produced under `out_dir`.
	"""

	from samri.pipelines.utils import bids_data_selection

	preprocessing_dir = path.abspath(path.expanduser(preprocessing_dir))
	out_base = path.abspath(path.expanduser(out_base))

	data_selection = bids_data_selection(preprocessing_dir, structural_match=False, functional_match=match, subjects=False, sessions=False)
	ind = data_selection.index.tolist()

	out_dir = path.join(out_base,workflow_name)
	workdir_name = workflow_name+'_work'
	workdir = path.join(out_base,workdir_name)
	if not os.path.exists(workdir):
		os.makedirs(workdir)
	data_selection.to_csv(path.join(workdir,'data_selection.csv'))

	get_scan = pe.Node(name='get_scan', interface=util.Function(function=get_bids_scan,input_names=inspect.getargspec(get_bids_scan)[0], output_names=['scan_path','scan_type','task', 'nii_path', 'nii_name', 'events_name', 'subject_session', 'metadata_filename', 'dict_slice']))
	get_scan.inputs.ignore_exception = True
	get_scan.inputs.data_selection = data_selection
	get_scan.inputs.bids_base = preprocessing_dir
	get_scan.iterables = ("ind_type", ind)

	eventfile = pe.Node(name='eventfile', interface=util.Function(function=corresponding_eventfile,input_names=inspect.getargspec(corresponding_eventfile)[0], output_names=['eventfile']))

	if invert:
		invert = pe.Node(interface=fsl.ImageMaths(), name="invert")
		invert.inputs.op_string = '-mul -1'

	specify_model = pe.Node(interface=SpecifyModel(), name="specify_model")
	specify_model.inputs.input_units = 'secs'
	specify_model.inputs.time_repetition = tr
	specify_model.inputs.high_pass_filter_cutoff = highpass_sigma

	level1design = pe.Node(interface=Level1Design(), name="level1design")
	level1design.inputs.interscan_interval = tr
	if bf_path:
		bf_path = path.abspath(path.expanduser(bf_path))
		level1design.inputs.bases = {"custom": {"bfcustompath":bf_path}}
	else:
		# We are not adding derivatives here, as these conflict with the habituation option.
		# !!! This is not difficult to solve, and would only require the addition of an elif condition to the habituator definition, which would add multiple column copies for each of the derivs.
		level1design.inputs.bases = {'gamma': {'derivs':True, 'gammasigma':30, 'gammadelay':10}}
	level1design.inputs.model_serial_correlations = True

	modelgen = pe.Node(interface=fsl.FEATModel(), name='modelgen')
	#modelgen.inputs.ignore_exception = True

	glm = pe.Node(interface=fsl.GLM(), name='glm', iterfield='design')
#	glm.inputs.out_cope = "cope.nii.gz"
#	glm.inputs.out_varcb_name = "varcb.nii.gz"
#	#not setting a betas output file might lead to beta export in lieu of COPEs
#	glm.inputs.out_file = "betas.nii.gz"
#	glm.inputs.out_t_name = "t_stat.nii.gz"
#	glm.inputs.out_p_name = "p_stat.nii.gz"
	if mask == 'mouse':
		mask = '/usr/share/mouse-brain-atlases/dsurqec_200micron_mask.nii'
	else:
		glm.inputs.mask = path.abspath(path.expanduser(mask))
	glm.interface.mem_gb = 6
	#glm.inputs.ignore_exception = True

	out_file_name_base = 'sub-{{subject}}_ses-{{session}}_task-{{task}}_acq-{{acquisition}}_run-{{run}}_{{modality}}_{}.{}'

	betas_filename = pe.Node(name='betas_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	betas_filename.inputs.source_format = out_file_name_base.format('betas','nii.gz')
	cope_filename = pe.Node(name='cope_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	cope_filename.inputs.source_format = out_file_name_base.format('cope','nii.gz')
	varcb_filename = pe.Node(name='varcb_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	varcb_filename.inputs.source_format = out_file_name_base.format('varcb','nii.gz')
	tstat_filename = pe.Node(name='tstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	tstat_filename.inputs.source_format = out_file_name_base.format('tstat','nii.gz')
	zstat_filename = pe.Node(name='zstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	zstat_filename.inputs.source_format = out_file_name_base.format('zstat','nii.gz')
	pstat_filename = pe.Node(name='pstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	pstat_filename.inputs.source_format = out_file_name_base.format('pstat','nii.gz')
	pfstat_filename = pe.Node(name='pfstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	pfstat_filename.inputs.source_format = out_file_name_base.format('pfstat','nii.gz')
	design_filename = pe.Node(name='design', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	design_filename.inputs.source_format = out_file_name_base.format('design','mat')

	design_rename = pe.Node(interface=util.Rename(), name='design_rename')

	datasink = pe.Node(nio.DataSink(), name='datasink')
	datasink.inputs.base_directory = path.join(out_base,workflow_name)
	datasink.inputs.parameterization = False

	workflow_connections = [
		(get_scan, eventfile, [('nii_path', 'timecourse_file')]),
		(specify_model, level1design, [('session_info', 'session_info')]),
		(level1design, modelgen, [('ev_files', 'ev_files')]),
		(level1design, modelgen, [('fsf_files', 'fsf_file')]),
		(modelgen, glm, [('design_file', 'design')]),
		(modelgen, glm, [('con_file', 'contrasts')]),
		(get_scan, datasink, [(('dict_slice',bids_dict_to_dir), 'container')]),
		(get_scan, betas_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, cope_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, varcb_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, tstat_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, zstat_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, pstat_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, pfstat_filename, [('dict_slice', 'bids_dictionary')]),
		(get_scan, design_filename, [('dict_slice', 'bids_dictionary')]),
		(betas_filename, glm, [('filename', 'out_file')]),
		(cope_filename, glm, [('filename', 'out_cope')]),
		(varcb_filename, glm, [('filename', 'out_varcb_name')]),
		(tstat_filename, glm, [('filename', 'out_t_name')]),
		(zstat_filename, glm, [('filename', 'out_z_name')]),
		(pstat_filename, glm, [('filename', 'out_p_name')]),
		(pfstat_filename, glm, [('filename', 'out_pf_name')]),
		(modelgen, design_rename, [('design_file', 'in_file')]),
		(design_filename, design_rename, [('filename', 'format_string')]),
		(glm, datasink, [('out_pf', '@pfstat')]),
		(glm, datasink, [('out_p', '@pstat')]),
		(glm, datasink, [('out_z', '@zstat')]),
		(glm, datasink, [('out_t', '@tstat')]),
		(glm, datasink, [('out_cope', '@cope')]),
		(glm, datasink, [('out_varcb', '@varcb')]),
		(glm, datasink, [('out_file', '@betas')]),
		(design_rename, datasink, [('out_file', '@design')]),
		]

	if habituation:
		level1design.inputs.orthogonalization = {1: {0:0,1:0,2:0}, 2: {0:1,1:1,2:0}}
		specify_model.inputs.bids_condition_column = 'samri_l1_regressors'
		specify_model.inputs.bids_amplitude_column = 'samri_l1_amplitude'
		add_habituation = pe.Node(name='add_habituation', interface=util.Function(function=eventfile_add_habituation,input_names=inspect.getargspec(eventfile_add_habituation)[0], output_names=['out_file']))
		# Regressor names need to be prefixed with "e" plus a numerator so that Level1Design will be certain to conserve the order.
		add_habituation.inputs.original_stimulation_value='1stim'
		add_habituation.inputs.habituation_value='2habituation'
		workflow_connections.extend([
			(eventfile, add_habituation, [('eventfile', 'in_file')]),
			(add_habituation, specify_model, [('out_file', 'bids_event_file')]),
			])
	if not habituation:
		specify_model.inputs.bids_condition_column = ''
		level1design.inputs.contrasts = [('allStim','T', ['ev0'],[1])]
		workflow_connections.extend([
			(eventfile, specify_model, [('eventfile', 'bids_event_file')]),
			])
	#condition names as defined in eventfile_add_habituation:
	elif habituation=="separate_contrast":
		level1design.inputs.contrasts = [('stim','T', ['1stim','2habituation'],[1,0]),('hab','T', ['1stim','2habituation'],[0,1])]
	elif habituation=="in_main_contrast":
		level1design.inputs.contrasts = [('all','T', ['1stim','2habituation'],[1,1])]
	elif habituation=="confound":
		level1design.inputs.contrasts = [('stim','T', ["1stim", "2habituation"],[1,0])]
	else:
		print(habituation)
		raise ValueError('The value you have provided for the `habituation` parameter, namely "{}", is invalid. Please choose one of: {{None, False,"","confound","in_main_contrast","separate_contrast"}}'.format(habituation))

	if highpass_sigma or lowpass_sigma:
		bandpass = pe.Node(interface=fsl.maths.TemporalFilter(), name="bandpass")
		bandpass.inputs.highpass_sigma = highpass_sigma
		bandpass.interface.mem_gb = 16
		if lowpass_sigma:
			bandpass.inputs.lowpass_sigma = lowpass_sigma
		else:
			bandpass.inputs.lowpass_sigma = tr
		if invert:
			workflow_connections.extend([
				(get_scan, invert, [('nii_path', 'in_file')]),
				(invert, bandpass, [('out_file', 'in_file')]),
				(bandpass, specify_model, [('out_file', 'functional_runs')]),
				(bandpass, glm, [('out_file', 'in_file')]),
				(bandpass, datasink, [('out_file', '@ts_file')]),
				(get_scan, bandpass, [('nii_name', 'out_file')]),
				])
		else:
			workflow_connections.extend([
				(get_scan, bandpass, [('nii_path', 'in_file')]),
				(bandpass, specify_model, [('out_file', 'functional_runs')]),
				(bandpass, glm, [('out_file', 'in_file')]),
				(bandpass, datasink, [('out_file', '@ts_file')]),
				(get_scan, bandpass, [('nii_name', 'out_file')]),
				])
	else:
		if invert:
			workflow_connections.extend([
				(get_scan, invert, [('nii_path', 'in_file')]),
				(invert, specify_model, [('out_file', 'functional_runs')]),
				(invert, glm, [('out_file', 'in_file')]),
				(invert, datasink, [('out_file', '@ts_file')]),
				(get_scan, invert, [('nii_name', 'out_file')]),
				])
		else:
			workflow_connections.extend([
				(get_scan, specify_model, [('nii_path', 'functional_runs')]),
				(get_scan, glm, [('nii_path', 'in_file')]),
				(get_scan, datasink, [('nii_path', '@ts_file')]),
				])


	workflow_config = {'execution': {'crashdump_dir': path.join(out_base,'crashdump'),}}
	if debug:
		workflow_config['logging'] = {
			'workflow_level':'DEBUG',
			'utils_level':'DEBUG',
			'interface_level':'DEBUG',
			'filemanip_level':'DEBUG',
			'log_to_file':'true',
			}

	workflow = pe.Workflow(name=workdir_name)
	workflow.connect(workflow_connections)
	workflow.base_dir = out_base
	workflow.config = workflow_config
	workflow.write_graph(dotfilename=path.join(workflow.base_dir,workdir_name,"graph.dot"), graph2use="hierarchical", format="png")

	n_jobs = max(int(round(mp.cpu_count()*n_jobs_percentage)),2)
	workflow.run(plugin="MultiProc", plugin_args={'n_procs' : n_jobs})
	if not keep_work:
		shutil.rmtree(path.join(out_base,workdir_name))