Skip to content

Bonner-sphere spectrometry

Neptoon has a standalone processing domain for multi-channel Bonner-sphere spectrometers. It is deliberately separate from CRNSDataHub: a Bonner spectrometer must preserve every sphere channel because each channel has its own neutron response. The Bonner workflow never merges or sums configured sphere channels.

The first supported parameter sets are the NEMUS and HERMEIS Skyview fits (report version 0.8, 2025-03-21). Sphere size is not a sufficient scientific identifier. For example, NEMUS 3-inch and HERMEIS 3-inch spheres have different response coefficients. Neptoon therefore resolves a response by instrument model + sphere label. The user-facing channel id is only a stable data label and may additionally distinguish repeated physical channels.

What is implemented

For every configured channel Neptoon can:

  1. preserve the raw channel independently;
  2. convert absolute counts to counts per hour using a channel-specific or common elapsed time;
  3. divide by a configured reference_intensity_cph to obtain relative intensity;
  4. evaluate/invert the published two-dimensional soil-moisture/air-humidity response;
  5. calculate W63 and W86 from a 10 Vol-% channel reference plus the published relative soil-moisture scaling, and interpolate the published soil-contact percentage; and
  6. optionally construct a radial proxy from a fixed Schrön-2017 reference shape scaled by that Bonner W86 response;
  7. evaluate the channel-specific published D86 depth fits, using the event-weighted range-integrated fit for ordinary time-series output; and
  8. evaluate the distance-dependent D86 fit or an explicitly labelled quantile-matched exponential layer-weight proxy through the Python API.

The response fits use soil moisture in Vol-% and absolute humidity in g/m3. Neptoon keeps volumetric soil moisture in its normal internal unit m3/m3 and performs the factor-100 conversion only at the Bonner response boundary. The reported fit domain is 1--90 Vol-% and 0--50 g/m3; Neptoon does not silently extrapolate beyond it.

The supplied Skyview reports do not define channel-specific pressure or incoming radiation corrections. Neptoon therefore does not reuse a CRNS correction merely because it is numerically available. Humidity is also not corrected a second time: it is already an independent variable in the published Bonner response model. Separate NEMUS/HERMEIS depth-fit reports provide the vertical D86 parameterisation described below.

NEMUS sphere 0 is registered for its published horizontal footprint, but the supplied NEMUS response tables do not contain intensity-fit parameters for that channel. It can be processed as a count channel when soil-moisture inversion is disabled, but Neptoon does not invent an intensity response for it.

YAML configuration

Bonner processing uses its own strict configuration rather than extending the normal sensor/process CRNS files:

config: bonner_sphere

spectrometer:
  model: hermeis
  identifier: HERMEIS01
  parameter_set: hermeis_skyview_2025_v0_8

input:
  path_to_data: HERMEIS.csv
  delimiter: ","
  datetime:
    column: DateTime
    format: "%Y-%m-%d %H:%M:%S"
    timezone: UTC

  # Use `cph` if the source columns already contain rates.
  count_units: absolute_count
  default_elapsed_time_seconds: 3600

  meteo:
    # Alternatively provide temperature_column + relative_humidity_column.
    absolute_humidity_column: AbsoluteHumidity_g_m3
    pressure_column: AirPressure_hPa

  # Optional. If omitted, vertical D86 automatically uses the published
  # event-weighted range-integrated fit. A direct range column can be mapped as:
  spatial:
    distance_column: Distance_m

  channels:
    - id: H3
      sphere: "3"
      count_column: BS3_Counts
      reference_intensity_cph: 1234.5

    - id: H3_5
      sphere: "3.5"
      count_column: BS3_5_Counts
      elapsed_time_column: BS3_5_LiveTime_s
      reference_intensity_cph: 987.2

    - id: H7W
      sphere: "7W"
      count_column: BS7W_Counts
      reference_intensity_cph: 456.7

processing:
  soil_moisture:
    enabled: true
    out_of_domain: warn_nan
  footprint:
    effective_radii: true
    radial_proxy: scaled_schroen_2017
    vertical:
      enabled: true
      # D86 is natively g/cm2. Set bulk density only when a cm output is needed.
      bulk_density_g_cm3: 1.4

output:
  save_path: HERMEIS_processed.csv

reference_intensity_cph is intentionally explicit per channel. The Skyview reports define relative response functions but do not specify how a particular field instrument's absolute count rate should be normalised. A future validated calibration method can supply this value, but the current workflow does not equate it with the ordinary CRNS N0 parameter.

Relative paths are resolved against the Bonner YAML file.

Python API

from neptoon.spectrometry.bonner_sphere import BonnerSphereProcessor

processor = BonnerSphereProcessor.from_yaml("hermeis.yaml")
data = processor.run()

Standardised channel outputs use the configured channel id:

bs_H3_raw
bs_H3_cph
bs_H3_relative_intensity
bs_H3_smv
bs_H3_w63_m
bs_H3_w86_m
bs_H3_soil_contact_percent
bs_H3_d86_g_cm2
bs_H3_d86_cm

Missing values remain channel-local. A missing H3 observation does not make H3.5 or H7W invalid at the same timestamp.

Response equations

The NEMUS parameter set contains two equation families. For the NEMUS 3, 3.5, 7, 8, 10, 12 and modified spheres the response is

\[ I_{rel} = \left[p_0(x-p_2)^{-p_1} + p_3x + p_4\right] \left(1+p_5h+p_6h^2\right). \]

For NEMUS 4, 4.5, 5 and 6 it is

\[ I_{rel} = \left[p_0(x-p_2+p_7h)^{-p_1} + p_3(x+p_4+h)\right] \left(1+p_5h+p_6h^2\right). \]

HERMEIS uses

\[ I_{rel} = \left[p_0(x-p_2+p_7h)^{-p_1} + p_3x + p_4 + p_8h\right] \left(1+p_5h+p_6h^2\right). \]

Here x is soil moisture in Vol-% and h is absolute humidity in g/m3. The complete channel-specific coefficient sets live in the immutable Bonner registry rather than user YAML, so a HERMEIS channel cannot accidentally use NEMUS coefficients.

Horizontal footprint

The published W63 and W86 tables are represented as a reference footprint plus a dimensionless soil-moisture response. The current Skyview parameter sets use 10 Vol-% as the reference point for every channel:

\[ F_{86}(c,\theta) = \frac{W86_{published}(c,\theta)}{W86_{published}(c,10\%)} \]

and

\[ W86(c,\theta) = W86_{ref}(c) \cdot F_{86}(c,\theta). \]

W63 is represented in the same way. Neptoon linearly interpolates the dimensionless scaling factors between the reported 1, 3, 6, 10, 12, 15, 20, 25, 30, 40, 50 and 90 Vol-% simulation points and returns NaN outside that interval. Multiplication by the channel reference radius exactly reconstructs the published table values. Soil-contact percentages remain a directly interpolated published diagnostic. Pure-water values are available separately through the Python API.

The optional scaled_schroen_2017 model is a deliberately labelled proxy. The Skyview reports provide containment radii rather than a complete radial probability law. Neptoon therefore takes one Schrön-2017 radial shape at the common 10 Vol-% reference soil moisture and at the report-specific footprint humidity (1 g/m3 for NEMUS, 0 g/m3 for HERMEIS). That fixed reference shape is scaled once to the channel's reference W86. For another soil moisture, only the Bonner-sphere relative response is applied:

\[ s(c,\theta) = s_{ref}(c) \cdot F_{86}(c,\theta). \]

Thus the radial proxy does not inherit an additional CRNS soil-moisture dependence from the Schrön model. Its W86 reproduces the Bonner value by construction. The published W63 is retained in metadata as a diagnostic and is not claimed to be a directly simulated Bonner radial law.

Vertical D86 depth weighting

The NEMUS and HERMEIS depth-fit reports use the 86 % depth quantile and publish two different models. Their native quantity is dry-soil mass depth in g/cm2, not geometric depth in centimetres.

With horizontal distance information the channel-specific fit is

\[ D_{86}(\theta,r) = D_\infty + \frac{A_\infty + A_r\exp(-r/L)}{\theta + \theta_0}, \]

where theta is volumetric soil moisture as a fraction (for example 0.10 for 10 Vol-%) and r is detector distance in metres. This model is available through calculate_range_dependent_d86(...) and BonnerSphereProcessor.calculate_vertical_depth(...) when a distance is supplied.

For the no-distance case the reports first combine the simulated depth distributions of all radial rings using their simulated event fractions,

\[ W(z\mid\theta)=\frac{\sum_r N_r W(z\mid\theta,r)}{\sum_r N_r}, \]

and then fit the resulting quantile depth as

\[ D_{86}(\theta)=\frac{A}{(\theta+\theta_0)^\alpha}. \]

For time-series processing Neptoon selects between these two published models from the available geometry. If no distance or coordinate mapping is configured, the entire series uses the event-weighted range-integrated D86(theta) fit. If range information is available, rows with a finite detector distance use D86(theta,r). Rows whose distance or coordinates are empty/NA automatically fall back to D86(theta) rather than producing a missing depth. The selected mode is recorded in bonner_vertical_d86_mode as range_dependent or range_integrated, and any derived distance is published as bonner_distance_m.

Three geometry inputs are supported under input.spatial: a direct distance_column in metres; local Cartesian x_column/y_column together with the detector x/y coordinates; or WGS84 latitude_column/longitude_column together with spectrometer.latitude and spectrometer.longitude. Geometry is never guessed from arbitrary column names. Omitting input.spatial is therefore the explicit no-range case.

Neptoon does not numerically average D86(theta,r) with the optional scaled-Schrön radial proxy. The depth report already supplies an event-weighted no-distance fit, whereas the Schrön shape is only an approximation to the unknown full Bonner radial probability law.

When processing.footprint.vertical.enabled: true, Neptoon writes bs_<id>_d86_g_cm2 from the automatically selected published fit. If bulk_density_g_cm3 is configured, it also writes

\[ D_{86,cm}=D_{86,g/cm^2}/\rho_b. \]

Neptoon keeps the mass-depth value as the primary product because it is what the supplied fits actually parameterise. The simple division is appropriate for the scalar/uniform bulk density configured in the time-series workflow. The low-level layer-weight helper supports layer-specific bulk density directly in cumulative mass-depth coordinates. If the supplied layer column does not contain enough dry-soil mass to reach D86, its physical d86_cm diagnostic remains NaN rather than extrapolating an unknown deeper bulk density.

The depth reports do not publish an analytic full vertical probability-density law; they publish D86 fits derived from simulated distributions. Consequently calculate_exponential_vertical_weights_proxy(...) is deliberately a proxy, not a claim to reconstruct the original simulated W(z). It uses an exponential in dry-mass depth with rate

\[ k=-\frac{\ln(1-0.86)}{D_{86}}, \]

so its untruncated cumulative probability is exactly 0.86 at the published D86. The helper integrates this proxy analytically over user-supplied soil layers and normalises the weights over the supplied finite column. Omitting distance uses the event-weighted no-distance D86; supplying distance_m uses the range-dependent fit. The standalone processor applies the same rule automatically to time-series geometry, including row-wise no-range fallback.

The depth-fit reports also warn that formal covariance errors do not fully represent systematic model mismatch when chi2/ndf is large. Neptoon retains RMSE, R2 and chi2/ndf in the immutable channel registry for diagnostics, but does not currently promote the formal coefficient errors into a physical D86 uncertainty product.