NeuNorm normalizes neutron imaging data and processes time-of-flight (TOF) data for ORNL imaging facilities — MARS at HFIR and VENUS at SNS.
NeuNorm 2.0 is a complete, scipp-based rewrite and a breaking change from the 1.x series. Code written against the 1.x
NeuNorm.normalization.NormalizationAPI will not run unchanged on 2.0. See the 1.x → 2.0 migration guide to port your code, or pinNeuNorm<2to keep using the legacy API — see NeuNorm 1.x (Legacy).
NeuNorm 2.0 is a ground-up rewrite for modern neutron imaging workflows at ORNL facilities:
| Facility | Beamline | Beam Type | Detectors |
|---|---|---|---|
| HFIR | MARS | Continuous | CCD/CMOS, TPX3 |
| SNS | VENUS | Pulsed (TOF) | CCD/CMOS, TPX1, TPX3 |
- Time-of-Flight (TOF) Support: Full hyperspectral data processing
- Event-Mode Processing: Direct TPX3 event handling with pulse reconstruction
- Automatic Uncertainty Propagation: Via scipp's variance tracking
- Dual-Facility Support: Unified API for MARS and VENUS workflows
- Modern Architecture: Pydantic v2 data models, scipp-based processing
From PyPI:
pip install NeuNorm
# optional extras: visualization (plopp/matplotlib) and Numba acceleration
pip install "NeuNorm[viz,performance]"From conda (the neutronimaging channel):
conda install neutronimaging::neunormFrom source, for development (uses pixi):
git clone https://github.com/ornlneutronimaging/NeuNorm.git
cd NeuNorm
pixi install
pixi run testEach detector/facility combination has a ready-made pipeline in neunorm.pipelines
that loads the data, applies the appropriate corrections, and writes the normalized
transmission — with uncertainty, detector masks, and provenance metadata — to HDF5
(or TIFF):
from pathlib import Path
from neunorm.pipelines.mars_ccd import run_mars_ccd_pipeline
# Each inner list is one acquisition "run" to combine before processing.
transmission = run_mars_ccd_pipeline(
sample_paths=[["sample_0001.tiff", "sample_0002.tiff"]],
ob_paths=[["ob_0001.tiff", "ob_0002.tiff"]],
dark_paths=[["dark_0001.tiff"]],
output_path=Path("normalized.hdf5"),
)Each detector/facility has its own pipeline — run_mars_tpx3_pipeline,
run_venus_ccd_pipeline, run_venus_tpx1_pipeline,
run_venus_tpx3_histogram_pipeline, and run_venus_tpx3_event_pipeline. They
share the same load → correct → normalize → write-to-HDF5/TIFF flow, but each
takes detector-appropriate inputs — TPX detectors skip dark_paths, the TOF
pipelines add rebin_by_tof/rebin_by_spatial, and
run_venus_tpx3_event_pipeline takes a BinningConfig and flat (per-run) path
lists. Check each function's signature in the
API reference or the per-workflow guides under
Supported Workflows. Verify your install with:
python -c "import neunorm; print(neunorm.__version__)"Neutron imaging normalization removes detector noise, beam fluctuations, and contamination to extract the true sample transmission:
Where:
- T: Transmission (0-1, may exceed 1 due to scattering)
- I_sample: Raw sample measurement (counts)
- I_OB: Open beam reference (no sample)
- I_dark: Dark current (detector noise baseline)
- f_beam: Beam intensity correction factor
| Detector Type | Dark Correction | Beam Correction | Hot Pixels |
|---|---|---|---|
| CCD/CMOS | Required | Time or p_charge | Not needed |
| TPX1 (histogram) | Not needed | p_charge or shutter | Not needed |
| TPX3 (event/histogram) | Not needed | p_charge | Required |
For counting detectors, uncertainty follows Poisson statistics:
For CCD/CMOS with dark correction, additional terms account for dark current uncertainty.
At VENUS (pulsed source), neutron wavelength is determined from flight time:
Where:
- λ = neutron wavelength
- t = time-of-flight
- L = source-to-detector distance
- m_n = neutron mass
- h = Planck's constant
This enables hyperspectral imaging with wavelength-resolved transmission T(λ, x, y).
| Workflow | Detector | Facility | TOF | Documentation |
|---|---|---|---|---|
| MARS CCD/CMOS | CCD/CMOS | HFIR | No | mars_ccd_cmos.md |
| MARS TPX3 | Timepix3 | HFIR | No | mars_tpx3.md |
| VENUS CCD/CMOS | CCD/CMOS | SNS | No | venus_ccd_cmos.md |
| VENUS TPX1 | Timepix1 | SNS | Yes | venus_tpx1.md |
| VENUS TPX3 | Timepix3 | SNS | Yes | venus_tpx3.md |
- Data Models: Pydantic v2 for validation
- Array Processing: scipp with automatic variance propagation
- TIFF I/O: scitiff (scipp ecosystem)
- Performance: Numba JIT for hot paths (optional, via the
performanceextra) - Testing: pytest with coverage
- Scipp-native: All processing uses
sc.DataArraywith automatic uncertainty tracking - Modular pipelines: Each processing step is an independent, testable module
- Workflow-driven: Pipelines are composed based on detector/facility combination
- Explicit over implicit: Configuration via Pydantic models, no hidden defaults
Full documentation — user guides plus an autodoc API reference — is hosted at
neunorm.readthedocs.io. The per-workflow guides
live under docs/workflows/. Release history is in
CHANGELOG.md, and the
1.x → 2.0 migration guide covers porting legacy code.
See CONTRIBUTING.md. Development uses pixi; please run
pixi run test and pixi run pre-commit run --all-files before opening a pull
request. Branches follow the promotion path next → qa → main (next is
the default development branch).
NeuNorm 1.x — the from NeuNorm.normalization import Normalization API — is the
previous stable line. To keep using it, pin NeuNorm<2 in your environment.
Archived 1.x documentation: archive/neunorm-1.x/README.md.
To port existing 1.x code to 2.0, see the
migration guide.
This work is sponsored by the Laboratory Directed Research and Development Program of Oak Ridge National Laboratory, managed by UT-Battelle LLC, under Contract No. DE-AC05-00OR22725 with the U.S. Department of Energy.
BSD 3-Clause License. See LICENSE for details.
If you use NeuNorm in your research, please cite:
@article{bilheux2018neunorm,
title={NeuNorm: Open-source normalization of neutron imaging data in Python},
author={Bilheux, Jean-Christophe},
journal={Journal of Open Source Software},
volume={3},
number={28},
pages={815},
year={2018},
doi={10.21105/joss.00815}
}