Architecture¶
A short tour of the csttool codebase. The aim is to give a contributor enough orientation to find the right module for a change. For why the pipeline is shaped the way it is, see Design Decisions.
Top-level layout¶
csttool/
├── src/csttool/ # the package
├── tests/ # pytest suite (unit + integration)
├── docs/ # this mkdocs site
├── thesis/ # supporting LaTeX thesis
├── scripts/ # one-off scripts (figure generation, validation)
├── reports/ # generated artefacts (not user-facing)
├── pyproject.toml # package metadata & dependencies
└── mkdocs.yml # docs build config
Package layout (src/csttool/)¶
csttool/
├── __init__.py
├── cli/ # argparse setup and subcommand entry points
│ ├── __init__.py # main() — entry point registered in pyproject.toml
│ └── commands/ # one file per subcommand (cmd_check, cmd_run, ...)
├── ingest/ # DICOM → NIfTI conversion and dataset validation
├── bids/ # BIDS layout helpers (raw and derivatives)
├── preprocess/ # denoising, unringing, motion correction, masking
├── tracking/ # CSA-ODF, deterministic propagation, whole-brain tractography
├── extract/ # atlas registration + ROI-based CST extraction
├── metrics/ # scalar metrics, tract profiles, HTML/PDF reports
├── validation/ # bundle-vs-reference comparison (Dice, overlap, etc.)
├── batch/ # multi-subject orchestration (BIDS + manifest)
├── reproducibility/ # RNG seeding, provenance logging, version capture
└── data/ # bundled atlases and template files
Each top-level module exposes a small public surface in its __init__.py. The corresponding CLI subcommand lives in cli/commands/ and is a thin wrapper around that public surface — argparse parsing → call the public function → handle exit codes.
Request flow: csttool run¶
The full pipeline path through the codebase:
cli/__init__.py:mainparses the global argv and dispatches by subcommand name.cli/commands/run.py:cmd_runparses run-specific flags and calls each stage in turn:ingest.import_subject→ DICOM/NIfTI ingestion, BIDS-style staging.preprocess.preprocess→ Patch2Self/NLMeans denoise, optional Gibbs unringing, optional motion correction, brain-mask via median Otsu.tracking.modules.run_tracking→ CSA-ODF model fit, deterministic propagation withThresholdStoppingCriterion, writes whole-brain.trk.extract.extract_cst→ atlas registration (ANTs SyN vianilearn), ROI dilation, filter streamlines by chosen method.metrics.compute_metrics→ analyse each hemisphere, compute laterality index, render HTML report, optionally rasterise to PDF.
Each stage is independently CLI-callable (csttool preprocess, csttool track, etc.) — the public entry function is the same one run invokes.
Reproducibility module¶
reproducibility/ is the home for cross-cutting concerns that affect every stage:
- RNG seeding (
--rng-seed, default42). - Run-log writing: every CLI invocation produces a
provenance.jsoncapturing Python version, package versions, exact command line and resolved input paths.
If you add a new stochastic step anywhere in the pipeline, plumb it through this module so determinism guarantees stay intact.
Tests¶
tests/
├── unit/ # pure-Python, no I/O on real datasets
├── integration/ # CLI invocations on small synthetic data
└── fixtures/ # tiny synthetic DWI volumes, gradient files
Unit tests live next to the module they cover when practical (the layout mirrors src/csttool/). Integration tests live under tests/integration/ and are slower.
Where to make common changes¶
| Change | Module |
|---|---|
| New CLI flag | cli/commands/<command>.py + corresponding public function + docs/reference/parameters.md + per-command reference page |
| New extraction method | extract/modules/ + choice in cli/commands/extract.py + note in explanation/design-decisions.md |
| New scalar metric | metrics/modules/ + report template under metrics/modules/reports/ |
| New denoising backend | preprocess/modules/ + dispatch in preprocess/preprocess.py |
| New atlas | data/ + helper in extract/ |