Skip to main content

NeuNorm 2.0 - Modern Neutron Imaging Normalization

PyPI version codecov Documentation Status DOI DOI

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.Normalization API will not run unchanged on 2.0. See the 1.x → 2.0 migration guide to port your code, or pin NeuNorm<2 to keep using the legacy API — see NeuNorm 1.x (Legacy).


Overview

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

Key Features

  • 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

Installation

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::neunorm

From source, for development (uses pixi):

git clone https://github.com/ornlneutronimaging/NeuNorm.git
cd NeuNorm
pixi install
pixi run test

Quick Start

Each 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"]],  # optional — omit to skip dark correction
    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 — dark_paths is optional for the CCD pipelines (omit it to skip dark correction) and TPX detectors skip it entirely, 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__)"

Core Physics

Normalization Principle

Neutron imaging normalization removes detector noise, beam fluctuations, and contamination to extract the true sample transmission:

$$ T(x, y) = f_{beam} \times \frac{I_{sample} - I_{dark}}{I_{OB} - I_{dark}} $$

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-Specific Corrections

Detector Type Dark Correction Beam Correction Hot Pixels
CCD/CMOS Optional Time or p_charge Not needed
TPX1 (histogram) Not needed p_charge or shutter Not needed
TPX3 (event/histogram) Not needed p_charge (VENUS); none (MARS) Required

Uncertainty Propagation

For counting detectors, uncertainty follows Poisson statistics:

$$ \frac{\sigma_T}{T} = \sqrt{\frac{1}{N_{sample}} + \frac{1}{N_{OB}} + \left(\frac{\sigma_{p,sample}}{p_{sample}}\right)^2 + \left(\frac{\sigma_{p,OB}}{p_{OB}}\right)^2} $$

For CCD/CMOS with dark correction, additional terms account for dark current uncertainty.

Time-of-Flight (TOF) Processing

At VENUS (pulsed source), neutron wavelength is determined from flight time:

$$ \lambda = \frac{h \times t}{m_n \times L} $$

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).


Supported Workflows

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

Architecture

Technology Stack

  • Data Models: Pydantic v2 for validation
  • Array Processing: scipp with automatic variance propagation
  • TIFF I/O: Pillow (input), scitiff (output)
  • Performance: Numba JIT for hot paths (optional, via the performance extra)
  • Testing: pytest with coverage

Key Design Principles

  1. Scipp-native: All processing uses sc.DataArray with automatic uncertainty tracking
  2. Modular pipelines: Each processing step is an independent, testable module
  3. Workflow-driven: Pipelines are composed based on detector/facility combination
  4. Explicit over implicit: Configuration via Pydantic models, no hidden defaults

Documentation

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.


Contributing

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 nextqamain (next is the default development branch).


NeuNorm 1.x (Legacy)

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.


References


Acknowledgements

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.

License

BSD 3-Clause License. See LICENSE for details.

Citation

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}
}

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

neunorm-2.2.3.tar.gz (745.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

neunorm-2.2.3-py3-none-any.whl (100.4 kB view details)

Uploaded Python 3

File details

Details for the file neunorm-2.2.3.tar.gz.

File metadata

  • Download URL: neunorm-2.2.3.tar.gz
  • Upload date:
  • Size: 745.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for neunorm-2.2.3.tar.gz
Algorithm Hash digest
SHA256 7e2bbe165962acb9c5b1c1661de14315b4a3fdb3895930c794d2a98c9439065b
MD5 9d8b0d7a3bcaa1efb377b1dca8fc0813
BLAKE2b-256 4d07e3443bbeb9fe78dad7b841411b4010532fb976f4c27a322031f89f6dd39d

See more details on using hashes here.

Provenance

The following attestation bundles were made for neunorm-2.2.3.tar.gz:

Publisher: test_and_deploy.yaml on ornlneutronimaging/NeuNorm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file neunorm-2.2.3-py3-none-any.whl.

File metadata

  • Download URL: neunorm-2.2.3-py3-none-any.whl
  • Upload date:
  • Size: 100.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for neunorm-2.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 f6b274414491b5d3d908d2883d07208e18b10b684688b29430ef5bda6a74425d
MD5 ebe8b6ebfda94781331468cadf6c29de
BLAKE2b-256 54b3f9afef246d8481b13a483258d5382a9a604ae8eab5d9a37be90ec10b7200

See more details on using hashes here.

Provenance

The following attestation bundles were made for neunorm-2.2.3-py3-none-any.whl:

Publisher: test_and_deploy.yaml on ornlneutronimaging/NeuNorm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

2.4.0

2 files

2.3.0

2 files

This release

2.2.3 This release

2 files

2.2.2

2 files

2.2.1

2 files

2.2.0

2 files

2.1.0

2 files

2.0.0

2 files

1.6.12

2 files

1.6.3

2 files

1.6.2

2 files

1.6.1

2 files

1.5.3

2 files

1.5.2

2 files

1.5.1

2 files

1.5.0

2 files

1.4.5

2 files

1.4.4

2 files

1.4.3

2 files

1.4.1

2 files

1.4.0

2 files

1.3.18

2 files

1.3.17

2 files

1.3.16

2 files

1.3.15

1 file

1.3.14

2 files

1.3.13

2 files

1.3.12

2 files

1.3.11

2 files

1.3.10

2 files

1.3.9

2 files

1.3.8

2 files

1.3.7

2 files

1.3.6

2 files

1.3.5

2 files

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.2.1

2 files

1.1.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page