Skip to content

Commit

Permalink
Clean up the workflows.
Browse files Browse the repository at this point in the history
  • Loading branch information
tsalo committed May 15, 2024
1 parent 774e0e2 commit eb4d009
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 130 deletions.
34 changes: 34 additions & 0 deletions src/fmripost_aroma/utils/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,44 @@
from pathlib import Path

from bids.layout import BIDSLayout
from bids.utils import listify

from fmripost_aroma.data import load as load_data


def extract_entities(file_list):
"""Return a dictionary of common entities given a list of files.
Examples
--------
>>> extract_entities("sub-01/anat/sub-01_T1w.nii.gz")
{'subject': '01', 'suffix': 'T1w', 'datatype': 'anat', 'extension': '.nii.gz'}
>>> extract_entities(["sub-01/anat/sub-01_T1w.nii.gz"] * 2)
{'subject': '01', 'suffix': 'T1w', 'datatype': 'anat', 'extension': '.nii.gz'}
>>> extract_entities(["sub-01/anat/sub-01_run-1_T1w.nii.gz",
... "sub-01/anat/sub-01_run-2_T1w.nii.gz"])
{'subject': '01', 'run': [1, 2], 'suffix': 'T1w', 'datatype': 'anat', 'extension': '.nii.gz'}
"""
from collections import defaultdict

from bids.layout import parse_file_entities

entities = defaultdict(list)
for e, v in [
ev_pair for f in listify(file_list) for ev_pair in parse_file_entities(f).items()
]:
entities[e].append(v)

def _unique(inlist):
inlist = sorted(set(inlist))
if len(inlist) == 1:
return inlist[0]
return inlist

return {k: _unique(v) for k, v in entities.items()}


def collect_derivatives(
raw_dir: Path | None,
derivatives_dir: Path,
Expand Down
19 changes: 19 additions & 0 deletions src/fmripost_aroma/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,22 @@ def get_spectrum(data: np.array, tr: float):
freqs = np.fft.rfftfreq((power_spectrum.shape[0] * 2) - 1, tr)
idx = np.argsort(freqs)
return power_spectrum[idx, :], freqs[idx]


def _get_wf_name(bold_fname, prefix):
"""Derive the workflow name for supplied BOLD file.
>>> _get_wf_name("/completely/made/up/path/sub-01_task-nback_bold.nii.gz", "aroma")
'aroma_task_nback_wf'
>>> _get_wf_name(
... "/completely/made/up/path/sub-01_task-nback_run-01_echo-1_bold.nii.gz",
... "preproc",
... )
'preproc_task_nback_run_01_echo_1_wf'
"""
from nipype.utils.filemanip import split_filename

fname = split_filename(bold_fname)[1]
fname_nosub = '_'.join(fname.split('_')[1:-1])
return f"{prefix}_{fname_nosub.replace('-', '_')}_wf"
38 changes: 10 additions & 28 deletions src/fmripost_aroma/workflows/aroma.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from fmripost_aroma import config
from fmripost_aroma.interfaces.aroma import AROMAClassifier
from fmripost_aroma.interfaces.bids import DerivativesDataSink
from fmripost_aroma.utils.utils import _get_wf_name


def init_ica_aroma_wf(
Expand All @@ -47,11 +48,7 @@ def init_ica_aroma_wf(
#. Smooth data using FSL `susan`, with a kernel width FWHM=6.0mm.
#. Run FSL `melodic` outside of ICA-AROMA to generate the report
#. Run ICA-AROMA
#. Aggregate identified motion components (aggressive) to TSV
#. Return ``classified_motion_ICs`` and ``melodic_mix`` for user to complete
non-aggressive denoising in T1w space
#. Calculate ICA-AROMA-identified noise components
(columns named ``AROMAAggrCompXX``)
#. Aggregate components and classifications to TSVs
There is a current discussion on whether other confounds should be extracted
before or after denoising `here
Expand Down Expand Up @@ -376,9 +373,10 @@ def init_denoise_wf(bold_file):
niu.IdentityInterface(
fields=[
'bold_file',
'bold_mask_std',
'bold_mask',
'confounds',
'skip_vols',
'spatial_reference',
],
),
name='inputnode',
Expand All @@ -403,7 +401,7 @@ def init_denoise_wf(bold_file):
workflow.connect([
(inputnode, denoise, [
('confounds', 'confounds'),
('bold_mask_std', 'mask_file'),
('bold_mask', 'mask_file'),
]),
(rm_non_steady_state, denoise, [('bold_cut', 'bold_file')]),
]) # fmt:skip
Expand All @@ -429,7 +427,11 @@ def init_denoise_wf(bold_file):
run_without_submitting=True,
mem_gb=config.DEFAULT_MEMORY_MIN_GB,
)
workflow.connect([(add_non_steady_state, ds_denoised, [('bold_add', 'denoised_file')])])
workflow.connect([
# spatial_reference needs to be parsed into space, cohort, res, den, etc.
(inputnode, ds_denoised, [('spatial_reference', 'space')]),
(add_non_steady_state, ds_denoised, [('bold_add', 'denoised_file')]),
]) # fmt:skip

return workflow

Expand Down Expand Up @@ -479,26 +481,6 @@ def _add_volumes(bold_file, bold_cut_file, skip_vols):
return out


def _get_wf_name(bold_fname, prefix):
"""
Derive the workflow name for supplied BOLD file.
>>> _get_wf_name("/completely/made/up/path/sub-01_task-nback_bold.nii.gz", "aroma")
'aroma_task_nback_wf'
>>> _get_wf_name(
... "/completely/made/up/path/sub-01_task-nback_run-01_echo-1_bold.nii.gz",
... "preproc",
... )
'preproc_task_nback_run_01_echo_1_wf'
"""
from nipype.utils.filemanip import split_filename

fname = split_filename(bold_fname)[1]
fname_nosub = '_'.join(fname.split('_')[1:-1])
return f"{prefix}_{fname_nosub.replace('-', '_')}_wf"


def _select_melodic_files(melodic_dir):
"""Select the mixing and component maps from the Melodic output."""
import os
Expand Down
Loading

0 comments on commit eb4d009

Please sign in to comment.