Hybrid8 start spectrum for ratio-iteration unfolding#

The Hybrid8 spectrum is the standard a-priori (start) spectrum of the MSANDB code family used for cosmic-ray neutron spectrometry with Bonner spheres at balloon altitudes. It is the start spectrum that the MSANDB iterative unfolding applies its measured-to-calculated count-rate ratios to (G. Simmer, V. Mares, E. Weitzenegger and W. Rühm, Iterative unfolding for Bonner sphere spectrometers using the MSANDB code — Sensitivity analysis and dose calculation, Radiation Measurements 45 (2010) 1-9, doi:10.1016/j.radmeas.2009.03.023).

The original Hybrid8 spectrum is a tabulated multi-group spectrum. bssunfold.core.hybrid8_start() provides a smooth parametric analogue that follows the qualitative description of the cosmic-ray start spectrum given by S. Barros, V. Mares, R. Bedogni, M. Reginatto, A. Esposito, I. F. Gonçalves, P. Vaz and W. Rühm, Comparison of unfolding codes for neutron spectrometry with Bonner spheres, Radiation Protection Dosimetry 161 (2014) 46-52, doi:10.1093/rpd/nct353: a thermal region, a flat epithermal 1/E region, an evaporation peak around 2 MeV and a cascade peak around 100 MeV.

Why a start spectrum matters#

Ratio-iteration methods (SAND-II family — see Iterative Ratio Methods (SAND-II Family)) correct a trial spectrum multiplicatively from the measured-to-calculated ratios. The trial spectrum is therefore not a mere numerical convenience: it defines the first iterate, the trajectory of the iteration and — because the iteration is stopped early to counter semi-convergence — the effective regularization of the result. A physically shaped start spectrum keeps the early iterates in the region of the physically plausible spectra, so early stopping removes less structure than it does for a flat start.

The start spectrum does not change the fixed point of the iteration: with the same number of iterations and the same stopping rule, SAND-II converges to the same multiplicative update law; only the path and the amount of implicit regularization differ.

Mathematical formulation#

The shape is defined by anchor points \((E_k, S_k)\) on the fluence-per-logarithmic-energy convention \(S(E) = E\,\Phi(E)\), and is interpolated linearly in log-log space,

\[S(E) \;=\; \exp\!\Big( \mathrm{interp}_{\log E}\big[\log S_k\big]\Big),\]

with the end values clamped outside the anchor range. The returned spectrum is the flux-density convention \(\Phi(E)\), obtained by dividing out the energy,

\[\Phi(E_j) \;=\; \frac{S(E_j)}{E_j},\]

and is finally normalized over the energy grid so that

\[\sum_j \Phi_j E_j \;=\; \mathrm{total\_fluence},\]

i.e. the fluence rate integrated over the grid equals the requested total_fluence (default 1.0). The result is strictly positive on any valid grid, which is what the logarithmic ratio update of the SAND-II family requires.

Anchor table#

The 16 anchors of bssunfold.core.HYBRID8_ANCHORS describe the four Hybrid8 regions — thermal rise below ~0.1 MeV, the epithermal 1/E valley around 30 MeV, the evaporation peak at 2 MeV and the cascade peak at 100 MeV — plus the stiff power-law decrease towards the GeV range:

E (MeV)

S(E) = E·Φ (relative)

Region

1e-9

0.010

thermal rise

1e-7

0.030

thermal rise

1e-5

0.050

thermal plateau

1e-3

0.100

thermal plateau

0.020

0.300

rise towards the evaporation peak

0.100

0.500

rise towards the evaporation peak

0.500

0.850

rise towards the evaporation peak

2.000

1.000

evaporation peak (maxwellian)

5.000

0.550

epithermal descent

10.000

0.350

epithermal descent

30.000

0.220

epithermal valley (1/E region)

60.000

0.550

rise towards the cascade peak

100.000

1.000

cascade peak

300.000

0.200

stiff descent

1000.000

0.030

stiff descent

2000.000

0.008

stiff descent

Usage#

The named start spectrum is selected through the starting_spectrum parameter of the SAND-II solver:

from bssunfold import Detector

detector = Detector()

# flat start (default)
result = detector.unfold_sandii(readings)

# Hybrid8 cosmic-ray start spectrum
result = detector.unfold_sandii(readings, starting_spectrum="hybrid8")

An explicit initial_spectrum always takes precedence over the named start, so the Hybrid8 shape can also be rescaled or used as the seed of another scheme:

import numpy as np
from bssunfold.core import hybrid8_start

# evaluate the Hybrid8 shape on the detector grid (default total_fluence=1.0)
phi0 = hybrid8_start(detector.E_MeV, total_fluence=2.5)

# explicit a-priori spectrum wins over the named start
result = detector.unfold_sandii(
    readings,
    initial_spectrum=phi0,
    starting_spectrum="hybrid8",
)

Caveats#

  • The Hybrid8 spectrum describes a cosmic-ray environment. For laboratory sources (accelerator-driven spectra, Cf-252, fission assemblies) the evaporation/cascade structure of a 2 MeV / 100 MeV cosmic-ray spectrum may not be representative; the start spectrum should then be supplied explicitly, or the run compared against a flat start.

  • The shape is defined by a small set of hand-tuned anchors, not by the original multi-group Hybrid8 table; it reproduces the qualitative four-region structure, not the tabulated values bin by bin.

  • The start spectrum only makes a difference for the ratio-iteration family. Regularized (Tikhonov-type) solvers in this package do not use a start spectrum at all.

API reference#

See also Detector Class for the full Detector API reference.

bssunfold.core.unfold_hybrid8.hybrid8_start(E_MeV: ndarray | list[float], total_fluence: float = 1.0) → ndarray[source]

Evaluate the Hybrid8-style start spectrum on an energy grid.

Parameters:
  • E_MeV (array_like) – Energy grid in MeV (bin centers), as used by Detector.E_MeV. The returned values are fluence per bin (flux-density convention, i.e. Phi(E)), obtained from the E * Phi(E) anchor shape by division by E.

  • total_fluence (float, optional) – Fluence rate normalized over the grid so that sum(spectrum * E_MeV) == total_fluence (default: 1.0).

Returns:

Positive spectrum array with the same length as E_MeV.

Return type:

np.ndarray

bssunfold.core.unfold_hybrid8.HYBRID8_ANCHORS: tuple[tuple[float, float], ...] = ((1e-09, 0.01), (1e-07, 0.03), (1e-05, 0.05), (0.001, 0.1), (0.02, 0.3), (0.1, 0.5), (0.5, 0.85), (2.0, 1.0), (5.0, 0.55), (10.0, 0.35), (30.0, 0.22), (60.0, 0.55), (100.0, 1.0), (300.0, 0.2), (1000.0, 0.03), (2000.0, 0.008))

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable’s items.

If the argument is a tuple, the return value is the same object.