← back to overview · mctest

Statistical acceptance in mctest

mctest can judge a test value against its target using the Monte Carlo error bars that every McStas and McXtrace run prints, instead of only a fixed ±20 % window. This note explains the rule, the assumptions behind it, and where it can mislead.

TL;DR

What a test compares

Each test definition in an instrument header gives a target intensity for one monitor, either as a single value (%Test:, also accepted under its older name %Example:) or as one value per point of a parameter scan (%TestScan:). A test run produces, for that monitor, the intensity I and its error bar ERR, as in the simulation's own summary line:

Detector: transmitted_I=2.00752e+08 transmitted_ERR=2.28872e+06 transmitted_N=50752 "transmitted.L_U1"

mctest reads I and ERR from the monitor file's # values: line, from this stdout line for NeXus output, or from the NAME_I and NAME_ERR columns of a scan's mccode.dat. In McCode, I = Σ pi over the N neutron or photon weights pi reaching the monitor, and ERR² = N/(N−1)·(Σ pi² − (Σ pi/N)²), which is Σ pi² to a very good approximation for any N worth testing. A single event gives ERR = I.

A target may also carry its own error bar, written the same way: Detector: NAME_I=… NAME_ERR=… on a %Test: line, or NAME_ERR={…} after the NAME_I={…} list of a %TestScan: line. No instrument does this yet.

The acceptance rule

With Δ = Itest − Itarget and a threshold n given by --nsigma n (or --pvalue p, see below), each value, or each point of a scan, is accepted as follows.

Threshold given Target line Error bars usable Accepted when
no any not used within ±20 % of the target
yes no _ERR yes within ±20 % or |Δ| ≤ n·√(1+f)·ERRtest
yes has _ERR yes |Δ| ≤ n·√(ERRtest² + ERRtarget²), error bars alone
yes any no within ±20 % of the target

Here f is the --statfactor (default 1, giving √2·ERRtest). A scan passes only if every point passes and the number of points matches the target list. mcviewtest colours its report by the same rule, using the threshold and factor stored with each result.

The ±20 % rule is kept as an equal alternative while targets carry no _ERR, so an error-bar threshold can only widen acceptance, never narrow it. It is dropped for a target that states its own error bar: such a target is a deliberate statistical statement and is judged by the error bars alone.

The target's error bar, when it is not recorded

A comparison of two Monte Carlo estimates needs both variances: σΔ² = σtest² + σtarget². Today's targets were recorded without their error bar. mctest assumes the target was produced at the unscaled ncount of the test, so that its error bar is that of the test run scaled back by the statistics factor:

σ_target = σ_test · √f        ⇒        σ_Δ = σ_test · √(1 + f)

Without --statfactor this is σΔ = √2·σtest. Most targets were in fact made at higher statistics than the default test, so the assumption usually overstates σtarget and errs towards accepting: it reduces false alarms, while a real regression still shows up as a large deviation. If a target came from fewer events than the test, the test becomes stricter than intended. Recording _ERR on the target line replaces the assumption with the real value.

When error bars are not trusted

An error bar from a handful of particles is itself very uncertain, and a large one would let almost any value through. For example, with only a hundred neutrons, a test value at 28 % of its target is only 2.2σ away. mctest therefore uses error bars only from enough effective events:

N_eff = (I / ERR)² ≈ (Σ p_i)² / Σ p_i²   ≥ 100

Neff is Kish's effective sample size of the weighted particles reaching the monitor: the number of unweighted events that would give the same relative error, which is then 1/√Neff, at most 10 % here. Below the limit, the value is judged by the ±20 % rule, and mctest says so in its log. For a target with _ERR, its own Neff must pass the limit as well. A zero result, or a missing error bar, always falls back to the ±20 % rule.

Choosing the threshold

For a Gaussian-distributed Δ, accepting |Δ| ≤ n·σΔ is the same test as accepting a two-sided p-value of at least p = 2·(1 − Φ(n)). --pvalue p is converted to n with this relation, so the two options are equivalent ways to set one threshold.

n (σ) Two-sided p Coverage Expected false failures per 2000 values
2.17 0.03 97 % 60
3 2.7 × 10⁻³ 99.73 % 5.4
4 6.3 × 10⁻⁵ 99.994 % 0.13
5 5.7 × 10⁻⁷ 99.99994 % 0.001

The number of comparisons decides the threshold. A full test suite judges a few thousand values once every scan point is counted, so a per-value threshold that sounds strict still produces several spurious failures per run at 3σ. The CI test suites therefore run with --nsigma 5, which keeps the expected number of false alarms per run far below one.

Scaling the statistics

--statfactor f multiplies the ncount of every test by f: the general ncount (-n, default 10⁶) for single-value tests, and the per-point ncount of each scan (the scan line's own -n, else at most 10⁵ per point). Small factors give quick, rough runs; large factors give runs good enough to record new targets with their error bars. Run timeouts grow with f, and the factor is part of the result directory's name.

The test run's own ERR follows the reduced or increased statistics automatically, so nothing needs to be back-projected from the target. Only the unrecorded target error depends on f, through the √(1+f) above.

Known limitations

Results on the CI basic tests

The McStas basic-test instruments at 10⁶ particles with MPI, judged with --nsigma 5. Deviations use the √2·ERR assumption, and only the tests where the two rules disagree, or come close, are shown.

Test % of target Deviation Neff ±20 % Used rule
BNL_H8_simple 76 % −1.0σ ≈19 fail fail (below Neff limit, ±20 %)
Test_KDSource_3 97 % −13σ ≈4×10⁵ pass pass (±20 %)
Test_KDSource_4 97 % −42σ ≈4×10⁶ pass pass (±20 %)
ESS_BEER_MCPL 109 % +4.6σ ≈6×10³ pass pass
BNL_H8 96 % −4.2σ ≈2×10⁴ pass pass

All other basic-test values, McStas and McXtrace, are within 3.7σ and 9 % of their targets. BNL_H8_simple has too few effective events at 10⁶ to be judged by its error bar and is a genuinely noisy test; it needs a higher ncount in its own test line.

With scaled statistics

The same McStas tests plus the 41-point SE_example scans, at --statfactor 0.1 (10⁵ particles per value, 10⁴ per scan point) and --statfactor 10 (10⁷ and 10⁶), with --nsigma 5.

Factor Result What happened
0.1 5 tests fail BNL_H8_simple got zero counts, templateSANS_Mantid came out at 149 %, and both SE_example scans had points outside ±20 %. All of these fell below the Neff limit and were judged by the ±20 % rule: at this factor the tests lack the statistics to be judged, which the rule reports instead of hiding.
10 all pass BNL_H8_simple, which fails ±20 % at the default ncount, comes out at 100.8 %. The scans pass with every point within 11 % of target.

Test_KDSource and ESS_BEER_MCPL return identical values at every factor, because they read a fixed particle file. Their reported deviation still changes with f (Test_KDSource_3: −17.9σ at 0.1, −13σ at 1, −5.7σ at 10), purely through the assumed target error σtest·√f. This is the limitation for file-input tests described above.

Usage

mctest --nsigma 5                     # error bars, else ±20 %
mctest --pvalue 5.7e-7                # the same threshold as a p-value
mctest --nsigma 5 --statfactor 0.1    # quick run, 10× fewer particles
mctest --nsigma 5 --statfactor 100    # reference run, e.g. to record targets with _ERR

Without --nsigma or --pvalue, mctest behaves as before: every value is judged by the ±20 % rule.