Package Overview#

BSSUnfold is a Python package for neutron spectrum unfolding from Bonner Sphere Spectrometers (BSS). It provides 27 unfolding algorithms, 25 spectrum comparison metrics, ICRP-116 dose calculations, and Monte Carlo uncertainty quantification. Iterative solvers are accelerated with Numba JIT compilation.

Unfolding Methods#

All 27 methods are accessible as instance methods on the bssunfold.Detector class. They are organised into the following categories:

        graph TD
    A["Unfolding Methods"] --> B["Tikhonov-type"]
    A --> C["Iterative"]
    A --> D["Bayesian"]
    A --> E["Maximum Entropy"]
    A --> F["Statistical Regularization"]
    A --> G["Optimization-based"]
    A --> H["Pipeline"]
    A --> I["Parametric"]

    B --> B1["unfold_cvxpy"]
    B --> B2["unfold_qpsolvers"]
    B --> B3["unfold_tsvd"]
    B --> B4["unfold_tikhonov_legendre"]

    C --> C1["unfold_landweber"]
     C --> C2["unfold_mlem"]
     C --> C3["unfold_mlem_stop"]
     C --> C4["unfold_mlem_odl"]
     C --> C5["unfold_gravel"]
     C --> C6["unfold_doroshenko"]
     C --> C7["unfold_kaczmarz"]

    D --> D1["unfold_bayes"]
    D --> D2["unfold_bayes_spline_regularization"]

    E --> E1["unfold_maxed"]
     F --> F1["unfold_statreg"]
     F --> F2["unfold_reconst"]

    G --> G1["unfold_lmfit"]
    G --> G2["unfold_scipy_direct_method"]

    H --> H1["unfold_combined"]

    I --> I1["unfold_parametric"]
    I --> I2["unfold_parametric_cvxpy"]
    I --> I3["unfold_parametric_qpsolvers"]
    I --> I4["unfold_parametric_combined"]
    I --> I5["unfold_parametric2"]
    I --> I6["unfold_fruit_like"]
    I --> I7["unfold_hybrid_parametric"]
    I --> I8["unfold_bayesian_parametric"]

    style A fill:#4a90d9,color:#fff
    style B fill:#e8f0fe
    style C fill:#e8f0fe
    style D fill:#e8f0fe
    style E fill:#e8f0fe
    style F fill:#e8f0fe
    style G fill:#e8f0fe
    style H fill:#e8f0fe
    style I fill:#e8f0fe
    

Method Reference#

Note

Common parameters shared by most methods: readings, initial_spectrum, calculate_errors, noise_level, n_montecarlo, save_result, random_state.

See the Index or Detector Class for complete API signatures.

Built-in Response Functions#

7 response function datasets are included as Python dicts, importable from the package root:

Built-in RF datasets#

Dataset

Source

Detectors

Energy Range

Notes

RF_GSF

GSF (Germany)

10 (0in–18in)

1e-9 – 631 MeV

Standard range

RF_PTB

PTB (Germany)

15 (0in–18in)

1e-9 – 631 MeV

Standard range

RF_LANL

LANL (USA)

11 (3in–18in)

1e-9 – 631 MeV

Includes Pb-shielded (9inPb, 12inPb, 18inPb)

RF_JINR

JINR (Dubna)

9 (0in–12in)

1e-9 – 631 MeV

Includes Cd-covered (Cd0in) and Pb-shielded (10inPb)

RF_FERMILAB

Fermilab (USA)

8 (0in–18in)

1e-9 – 631 MeV

Standard range

RF_EURADOS

EURADOS round-robin

13 (0in–12in)

1e-9 – 20 MeV

Narrower range; includes Cd2in, 3.5in, 4.5in

RF_IHEP

IHEP (Protvino)

12 (0in–18in)

1e-9 – 2000 MeV

Wider range; includes 15in

Warning

RF_EURADOS max energy is 20 MeV and RF_IHEP max energy is 2000 MeV, compared to 631 MeV for the other datasets. Use caution when comparing results across datasets with different energy ranges.

from bssunfold import Detector, RF_JINR

detector = Detector(RF_JINR)
result = detector.unfold_cvxpy(readings, regularization=1e-4)

Dose Conversion Coefficients#

4 dose conversion coefficient datasets are included for flexible dose rate calculations. The default is ICRP-116 effective dose.

Dose conversion coefficient datasets#

Dataset

Standard

Quantities

Energy Range

Notes

ICRP116 (default)

ICRP-116

AP, PA, LLAT, RLAT, ISO, ROT

1e-9 – 631 MeV

Standard range

ICRP74_effective

ICRP-74

AP, PA, RLAT, ROT, ISO

1e-9 – 398 MeV

Effective dose

NRB99_2009_effective

NRB99-2009

AP, ISO

25 eV – 20 MeV

Limited range

ICRP74_operational

ICRP-74

ADE, PDE0, PDE45, PDE60, PDE75

1e-9 – 398 MeV

Operational quantities

Warning

NRB99_2009_effective covers a limited energy range (25 eV – 20 MeV). Values outside this range are set to zero during interpolation.

from bssunfold import Detector, get_coefficients, interpolate_coefficients

# Set on Detector (affects all subsequent unfolds)
detector = Detector(cc_type="ICRP74_effective")

# Change after creation
detector.set_dose_coefficients("ICRP74_operational")

# Get coefficients directly for custom use
cc = get_coefficients("NRB99_2009_effective")
cc_interp = interpolate_coefficients(cc, detector.E_MeV)

Spectrum Comparison Metrics#

25 metrics organised into 7 categories. All implemented with pure NumPy/SciPy.

        graph TD
    A["Comparison Metrics"] --> B["Entropy"]
    A --> C["Distribution"]
    A --> D["Correlation"]
    A --> E["Error"]
    A --> F["Similarity"]
    A --> G["Chi-squared"]
    A --> H["Statistical"]

    B --> B1["kl_divergence"]
    B --> B2["cross_entropy"]
    B --> B3["entropy_difference_percent"]

    C --> C1["wasserstein_dist"]
    C --> C2["energy_dist"]
    C --> C3["kolmogorov_smirnov_stat"]

    D --> D1["pearson_r"]
    D --> D2["spearman_r"]

    E --> E1["mean_squared_error"]
    E --> E2["root_mean_squared_error"]
    E --> E3["mean_absolute_error"]
    E --> E4["mape"]
    E --> E5["r2_score"]
    E --> E6["max_error"]
    E --> E7["median_absolute_error"]

    F --> F1["cosine_similarity"]
    F --> F2["mmd_rbf"]

    G --> G1["chi_squared"]
    G --> G2["g_test"]
    G --> G3["freeman_tukey"]
    G --> G4["cressie_read"]

    H --> H1["anderson_darling"]
    H --> H2["wilcoxon_test"]
    H --> H3["mannwhitneyu_test"]
    H --> H4["standardized_mean_difference"]

    style A fill:#4a90d9,color:#fff
    

Metrics Reference#

Complete metrics reference#

Category

Metric Key

Description

Range

Entropy

kl_divergence

Kullback-Leibler divergence D_KL(p‖q)

[0, ∞)

cross_entropy

Cross-entropy H(p,q) = -∑p·log(q)

[0, ∞)

entropy_difference_percent

Relative cross-entropy excess (%)

[0, ∞)

Distribution

wasserstein_dist

Earth mover’s / Wasserstein distance

[0, ∞)

energy_dist

Energy distance between distributions

[0, ∞)

kolmogorov_smirnov_stat

Kolmogorov-Smirnov D-statistic

[0, 1]

Correlation

pearson_r

Pearson correlation coefficient

[-1, 1]

spearman_r

Spearman rank correlation

[-1, 1]

Error

mean_squared_error

Mean squared error

[0, ∞)

root_mean_squared_error

Root mean squared error

[0, ∞)

mean_absolute_error

Mean absolute error

[0, ∞)

mape

Mean absolute percentage error (%)

[0, 100]

r2_score

R² (coefficient of determination)

(-∞, 1]

max_error

Maximum residual error

[0, ∞)

median_absolute_error

Median absolute error

[0, ∞)

Similarity

cosine_similarity

Cosine similarity cos(θ)

[0, 1]

mmd_rbf

Maximum Mean Discrepancy (RBF kernel)

[0, ∞)

Chi-squared

chi_squared

Pearson’s chi-squared statistic

[0, ∞)

g_test

G-test (log-likelihood ratio)

[0, ∞)

freeman_tukey

Freeman-Tukey statistic

[0, ∞)

cressie_read

Cressie-Read power divergence

[0, ∞)

Statistical

anderson_darling

Anderson-Darling k-sample statistic

[0, ∞)

wilcoxon_test

Wilcoxon signed-rank test statistic

[0, ∞)

mannwhitneyu_test

Mann-Whitney U test statistic

[0, ∞)

standardized_mean_difference

Cohen’s d (standardized mean difference)

(-∞, ∞)

Performance#

All iterative solvers use Numba JIT-compiled inner loops when numba is installed, with automatic fallback to pure Python.

Benchmark results (60-bin grid, 500 iterations, macOS arm64)#

Solver

Before

After

Speedup

Doroshenko

40.6 ms

0.8 ms

50x

Kaczmarz

1.4 ms

0.1 ms

14x

MLEM

2.7 ms

0.4 ms

7x

GRAVEL

~2 ms

0.6 ms

3x

cvxpy

84 ms

78 ms

~1x (external solver)

qpsolvers

1.7 ms

1.6 ms

~1x (external solver)

Install numba for the best performance:

pip install bssunfold[numba]

The JIT functions are defined in bssunfold.core._numba_jit and use @njit(cache=True) for automatic disk caching of compiled code.