End-to-end preprocessing pipeline for the SLB Social Learning fMRI project.
This pipeline converts raw scanner exports into a structured, BIDS-compliant dataset and runs the core preprocessing steps needed for downstream QA/QC, denoising, and analysis. It is designed to be reproducible, rerunnable, and safe for shared lab use.
This pipeline implements a full fMRI preprocessing workflow, starting from raw scanner extraction and ending with validated derivatives for downstream analysis.
The workflow covers:
- Raw scanner data extraction from the remote archive
- DICOM to BIDS conversion using HeuDiConv
- Automatic fieldmap wiring via
IntendedFor - Behavioral-to-BIDS event file generation
- Standard fMRIPrep preprocessing
- Optional MRIQC
- Optional MNI6 preprocessing for ICA-AROMA
- Optional fMRIPost-AROMA denoising
The pipeline enforces strict separation between:
- Raw data — original scanner exports, never modified
- BIDS data — structured analysis input dataset
- Derivatives — outputs from fMRIPrep, MRIQC, and AROMA
- Work directories — scratch/intermediate files, safe to regenerate
- Logs — execution history for debugging and auditing
The SLB preprocessing workflow is automated using Snakemake for workflow orchestration and SLURM for computationally intensive preprocessing.
The automation builds on the existing scientific scripts documented below. Snakemake determines which participants require processing, tracks completed steps, validates preprocessing inputs, and submits eligible participants for fMRIPrep.
The production pipeline runs on the UMD SLD server:
/data/sld/homes/collab/slb/
fMRI2
|
| Daily, 2:00 AM
v
Raw-data transfer
|
v
raw_data/
|
| Daily, 6:00 AM
v
Snakemake preparation
|
v
Participant discovery
|
v
BIDS conversion
|
v
IntendedFor assignment
|
v
BIDS events generation
|
v
Participant validation
|
v
state/ready/sub-XXX.ready
|
| Saturday, 5:00 AM
v
Snakemake + SLURM
|
v
fMRIPrep
|
v
derivatives/fmriprep_runs/sub-XXX.html
|
| Monday, 9:00 AM
v
Weekly Slack summary
| Schedule | Operation | Script |
|---|---|---|
| Daily, 2:00 AM | Transfer raw scanner data | /usr/local/sbin/sync-slb.sh |
| Daily, 6:00 AM | Prepare and validate participants | run_prepare.sh |
| Daily, 8:00 AM | Report raw-data transfer changes | scripts/nightly_extract_and_notify.sh |
| Saturday, 5:00 AM | Run pending fMRIPrep jobs | run_weekend_fmriprep.sh |
| Monday, 9:00 AM | Send weekly preprocessing summary | scripts/slack_notify.sh |
The scheduled jobs are managed through cron on the SLD server.
The automation is organized into the following components:
Snakefile
workflow/
├── rules/
│ ├── discovery.smk
│ ├── preparation.smk
│ ├── validation.smk
│ └── fmriprep.smk
│
└── scripts/
└── validate_subject.py
config/
├── snakemake.yaml.example
└── snakemake-requirements-lock.txt
profiles/
└── slurm/
└── config.yaml
run_prepare.sh
run_weekend_fmriprep.sh
pipeline_status.sh
bootstrap_existing_state.sh
The workflow is designed to be idempotent: completed processing is not repeated unless the relevant inputs or required outputs change.
Each participant progresses through BIDS conversion, IntendedFor assignment, behavioral event generation, and validation.
Successful validation creates:
state/ready/sub-XXX.ready
This marker indicates that the participant is eligible for the weekend fMRIPrep workflow.
Validation checks the functional runs that actually exist, allowing for legitimately missing acquisitions.
The weekend workflow automatically identifies participants that are ready for preprocessing but do not yet have a completed fMRIPrep report.
Each fMRIPrep job currently requests:
- 16 CPUs
- 48 GB RAM
- The SLURM compute partition
The workflow allows a maximum of three concurrent fMRIPrep jobs.
This is a concurrency limit, not a weekly participant limit. When a job finishes, Snakemake can submit another eligible participant.
A participant's automated fMRIPrep stage is considered complete when the corresponding non-empty report exists:
derivatives/fmriprep_runs/sub-XXX.html
This is a processing-completion checkpoint, not a substitute for reviewing the participant's quality-control results.
The SLD cluster does not provide completed-job accounting through sacct.
The Snakemake SLURM profile therefore explicitly uses:
slurm-status-command: squeueThis allows Snakemake to track submitted jobs and recognize their completion.
Every Monday at 9:00 AM, the pipeline posts a summary to the lab's Slack channel.
The summary includes:
- Participants newly completed since the previous successful report
- Total participants with completed fMRIPrep reports
- Participants ready for fMRIPrep but still pending
The Slack notifier is implemented in:
scripts/slack_notify.sh
The notification sends operational status information. Raw imaging data, behavioral datasets, and preprocessing derivatives are not uploaded to Slack.
Run these commands from the production SLB directory on SLD.
Check daily preparation:
./run_prepare.sh --dry-runCheck pending fMRIPrep work:
./run_weekend_fmriprep.sh --dry-runCheck overall pipeline status:
./pipeline_status.shCount completed fMRIPrep reports:
find derivatives/fmriprep_runs \
-maxdepth 1 \
-type f \
-name 'sub-*.html' \
-size +0c \
| wc -lSnakemake runs locally on SLD and submits computational jobs to the UMD SLURM cluster.
The workflow does not upload research data to a Snakemake-hosted service.
Raw data, BIDS data, behavioral datasets, derivatives, credentials, and operational secrets must remain outside the repository.
The production Slack webhook is stored locally and must never be committed to version control.
| Stage | Script | Responsibility |
|---|---|---|
| 1 | sync-slb.sh (production), extract_mnc.sh (manual) |
Transfer raw scanner data from fMRI2 to SLD |
| 2 | bidsify_runs.sh |
Convert DICOMs to BIDS with HeuDiConv |
| 3 | add_intendedfor_by_task.py |
Add correct IntendedFor mappings to fieldmaps |
| 4 | build_*_bids_events_from_behavioral_runs.py |
Build BIDS events.tsv from behavioral outputs |
| 5 | run_fmriprep_runs.sh |
Run standard fMRIPrep preprocessing |
| 6 | run_mriqc.sh |
Run MRIQC for image quality assessment |
| 7 | run_fmriprep_mni6_runs.sh |
Run MNI6-only preprocessing for AROMA compatibility |
| 8 | run_fmripost_aroma_runs.sh |
Run ICA-AROMA denoising on MNI6 outputs |
Each stage:
- has clearly defined inputs and outputs
- performs one logical operation
- can be rerun independently
- preserves upstream raw inputs unchanged
Prerequisites
Before running the pipeline, make sure the following are available:
- access to the SLD server
- access to /data/sld/homes/collab/slb
- UMD network or GlobalProtect VPN if off campus
- required Linux group permissions for shared directories
- Apptainer/Singularity available on the server
- FreeSurfer license file present
- required containers available locally or pullable at runtime
/usr/local/sbin/sync-slb.sh
Automatically transfers raw SLB scanner data from the fMRI2 server to the SLD server for downstream preprocessing.
The production transfer is managed by a system-level script and runs automatically every day at 2:00 AM.
- Connects to fMRI2 using SSH with Kerberos/GSSAPI authentication.
- Uses a dedicated service account rather than personal UMD credentials.
- Transfers new or updated files using
rsync. - Preserves the raw-data directory structure.
- Performs incremental synchronization without unnecessarily copying existing files.
- Does not automatically delete destination files that are removed from the source.
- Records transfer activity and statistics in the production log.
fmri2.umd.edu
/export/software/fmri/massstorage/Caroline Charpentier/SLB Social Learning/
/data/sld/homes/collab/slb/raw_data/
The production transfer runs daily at 2:00 AM through cron:
0 2 * * * /usr/local/sbin/sync-slb.sh >> /var/log/sld-slb-sync.log 2>&1Under normal operation, no manual transfer is required.
To inspect the production transfer log:
tail -n 100 /var/log/sld-slb-sync.logTo view recent successful transfers:
grep "Completed SLD raw data pull" \
/var/log/sld-slb-sync.log | tailTo check transferred participant data:
ls -lah /data/sld/homes/collab/slb/raw_data/A successful transfer means the raw scanner data has reached SLD. It does not mean BIDS conversion or fMRIPrep has completed.
The repository also contains:
extract_mnc.sh
This is the original extraction utility and remains available for manual transfers and troubleshooting.
It is not responsible for the scheduled production transfer.
From the SLB project directory on SLD:
cd /data/sld/homes/collab/slbManual extraction:
./scripts/extract_mnc.shDry run:
./scripts/extract_mnc.sh -nRestrict extraction to specific sessions:
./scripts/extract_mnc.sh -S 202512*For normal automated operations, use the production sync-slb.sh workflow instead.
The transfer notification script is:
scripts/nightly_extract_and_notify.sh
It runs daily at 8:00 AM, after the scheduled raw-data transfer.
The notifier reads the production rsync log:
/var/log/sld-slb-sync.log
It also compares raw-data file counts and storage totals against the previous snapshot.
Slack notifications are sent when new raw data is detected or a transfer error is identified.
The notification script does not initiate the production transfer or run extract_mnc.sh.
bidsify_runs.sh
Converts raw DICOMs into a BIDS-compliant dataset using HeuDiConv and the custom SLB heuristic.
- detects candidate subject folders in
raw_data/ - runs HeuDiConv in a container
- applies
heuristic_runs.pyto map scanner sequences into BIDS outputs - creates subject-level
anat/,func/, andfmap/folders - supports reruns and forced reconversion
- resets cached
.heudiconvstate when forcing reconversion
- raw scanner folders in
raw_data/ heuristic_runs.py- HeuDiConv container
bids_runs/
├── sub-XXX/
│ ├── anat/
│ ├── func/
│ ├── fmap/
│ └── sub-XXX_scans.tsv
├── dataset_description.json
├── participants.tsv
└── task-*_bold.json
./bidsify_runs.sh
./bidsify_runs.sh XXX
./bidsify_runs.sh --force
./bidsify_runs.sh XXX --forceadd_intendedfor_by_task.py
Automatically adds correct IntendedFor entries to fieldmap JSONs so each fieldmap pair is linked to the right functional runs.
obslearn→acq-obslearnriskself,risksocial→acq-riskth,tm→acq-trust
- scans
func/for task runs - identifies matching fieldmaps by
acq-label - writes correct relative
IntendedForpaths intofmap/*.json - supports both current and legacy naming styles
- validates AP/PA pairing and missing targets
- writes a per-subject validation report
- updated fieldmap JSON files
- report JSONs in:
bids_runs/code/intendedfor_reports/
python3 add_intendedfor_by_task.py /data/sld/homes/collab/slb/bids_runs --dry-run
python3 add_intendedfor_by_task.py /data/sld/homes/collab/slb/bids_runs
python3 add_intendedfor_by_task.py /data/sld/homes/collab/slb/bids_runs sub-XXX sub-001
build_ol_bids_events_from_behavioral_runs.pybuild_sra_bids_events_from_behavioral_runs.pybuild_trust_bids_events_from_behavioral_runs.pycombined_build_bids_events_from_behavioral_runs.py
Converts behavioral outputs into BIDS-compliant events.tsv files aligned to each functional run.
- reads task-specific behavioral data
- uses task dictionaries to define onset, duration, and trial labels
- writes clean run-aligned
events.tsvfiles intobids_runs/sub-*/func/ - preserves task-specific variables needed for downstream modeling
bids_runs/sub-XXX/func/
├── sub-XXX_task-*_run-01_events.tsv
├── sub-XXX_task-*_run-02_events.tsv
python3 build_ol_bids_events_from_behavioral_runs.py
python3 build_sra_bids_events_from_behavioral_runs.py
python3 build_trust_bids_events_from_behavioral_runs.py
python3 combined_build_bids_events_from_behavioral_runs.py
run_fmriprep_runs.sh
Runs the standard fMRIPrep preprocessing stream for a subject.
- checks that the subject exists in the BIDS dataset
- runs fMRIPrep in participant mode
- applies distortion correction using wired fieldmaps
- performs standard preprocessing and confound estimation
- writes subject derivatives, reports, work files, and logs
derivatives/fmriprep_runs/
├── sub-XXX/
├── logs/
└── sourcedata/
`
derivatives/fmriprep_runs/sub-XXX.html
work/fmriprep_runs/sub-XXX/
./run_fmriprep_runs.sh XXX
run_mriqc_runs.sh
Runs MRIQC to assess the quality of anatomical and functional MRI data and generate quantitative metrics and visual reports.
MRIQC is an optional quality-control step. It does not modify the input BIDS dataset.
- Validates that the requested subjects exist in the BIDS dataset.
- Runs MRIQC in participant mode using an Apptainer container.
- Computes image-quality metrics for anatomical and functional scans.
- Generates subject-level quality-control reports.
- Supports individual subjects, multiple subjects, subject ranges, and all available subjects.
- Optionally runs group-level quality-control aggregation.
- BIDS dataset in
bids_runs/ - MRIQC Apptainer container (
mriqc_23.0.1.sif)
derivatives/mriqc_runs/
work/mriqc_runs/
logs/mriqc_<timestamp>.log
Run MRIQC for a single subject:
./scripts/run_mriqc_runs.sh 000Run for multiple subjects:
./scripts/run_mriqc_runs.sh 000 001 002Run for a range of subjects:
./scripts/run_mriqc_runs.sh --range 000 003Run for all available subjects and generate group-level reports:
./scripts/run_mriqc_runs.sh --all --groupPreview the commands without running MRIQC:
./scripts/run_mriqc_runs.sh 000 --dry-runNote: These commands use the SLD production layout, where the script is inside scripts/. In the GitHub repository, the existing script is currently at the root.
run_fmriprep_mni6_runs.sh
Runs a separate fMRIPrep stream restricted to MNI152NLin6Asym, which is required for downstream ICA-AROMA.
- runs fMRIPrep on the requested subject
- writes outputs to a separate derivatives stream
- keeps the AROMA-compatible preprocessing branch isolated from the standard branch
derivatives/fmriprep_mni6_runs/
work/fmriprep_mni6_runs/
./run_fmriprep_mni6_runs.sh XXX
run_fmripost_aroma_runs.sh
Runs fMRIPost-AROMA on the MNI6 derivatives to identify and remove structured motion-related noise.
- takes MNI6 fMRIPrep derivatives as input
- runs fMRIPost-AROMA in participant mode
- writes denoised outputs and subject-level reports
- preserves a separate AROMA derivatives branch
derivatives/fmripost_aroma_runs/
├── sub-XXX/
├── logs/
└── dataset_description.json
derivatives/fmripost_aroma_runs/sub-XXX.html
work/fmripost_aroma_runs/
./run_fmripost_aroma_runs.sh XXX