MINERvA · Phys. Rev. D 95, 072009; addendum Phys. Rev. D 97, 019902 (2017)

Measurement of the antineutrino to neutrino charged-current interaction cross section ratio in MINERvA

Model inputs MINERvA 2017 cross sections Reproduced

The ratio of the ν̄μ to νμ inclusive charged-current cross sections, R_CC, and the two σ/E per nucleon of carbon (isoscalar corrected), 2–22 GeV (Table IV and the supplemental covariance matrices, from the arXiv source), compared with the cross sections carried by Newtrinos.jl (module xsec): the ten GENIE tunes of generator_sigma on carbon and the nominal model of CorrelatedXsec (the mean of the tunes) with its prior 1σ band, ν and ν̄ sampled jointly. The ratio is described well: χ² = 2.6 for 9 bins with the published covariance (2.1 including the model's prior covariance); the tunes give 1.3–4.6. In σ/E the νμ data at 3–7 GeV lie 8–12 % below all tunes, as the paper also finds for its GENIE model; the nominal gives χ² = 39.2 for 18 bins (21.5 with its prior covariance), the tunes 27–56.

Figures

σ(ν̄μ)/σ(νμ) on carbon: MINERvA vs the GENIE tunes and the CorrelatedXsec nominal with its prior 1σ band · Fig. 11 (right)

Paper · Fig. 11 (right)
Original: σ(ν̄μ)/σ(νμ) on carbon: MINERvA vs the GENIE tunes and the CorrelatedXsec nominal with its prior 1σ band

Original figure: MINERvA, Phys. Rev. D 95, 072009; addendum Phys. Rev. D 97, 019902 (2017), Fig. 11 (right), doi:10.1103/PhysRevD.95.072009. Not our work; shown for comparison, all rights with the authors and publisher.

Newtrinos.jl
Reproduction: σ(ν̄μ)/σ(νμ) on carbon: MINERvA vs the GENIE tunes and the CorrelatedXsec nominal with its prior 1σ band

νμ CC σ/E per nucleon of carbon · Fig. 10

Paper · Fig. 10
Original: νμ CC σ/E per nucleon of carbon

Original figure: MINERvA, Phys. Rev. D 95, 072009; addendum Phys. Rev. D 97, 019902 (2017), Fig. 10, doi:10.1103/PhysRevD.95.072009. Not our work; shown for comparison, all rights with the authors and publisher.

Newtrinos.jl
Reproduction: νμ CC σ/E per nucleon of carbon

ν̄μ CC σ/E per nucleon of carbon · Fig. 10

Paper · Fig. 10
Original: ν̄μ CC σ/E per nucleon of carbon

Original figure: MINERvA, Phys. Rev. D 95, 072009; addendum Phys. Rev. D 97, 019902 (2017), Fig. 10, doi:10.1103/PhysRevD.95.072009. Not our work; shown for comparison, all rights with the authors and publisher.

Newtrinos.jl
Reproduction: ν̄μ CC σ/E per nucleon of carbon

Known limitations

Reproduce

  1. git clone https://github.com/Newtrinos-org/Newtrinos.jl.git && cd Newtrinos.jl && git checkout 78e71c310b
  2. Download reproduce.jl and the data/ folder of this entry into an empty directory.
  3. julia -t 8 --project=/path/to/Newtrinos.jl reproduce.jl
# MINERvA ν̄μ/νμ charged-current inclusive cross-section ratio and the νμ and ν̄μ σ/E per nucleon of carbon (isoscalar
# corrected), 2–22 GeV (PRD 95, 072009 (2017); Table IV and the supplemental covariances, from the arXiv:1701.04857v2
# source, in data/), compared with the cross sections of Newtrinos.jl (module `xsec`): the GENIE tunes (`generator_sigma`,
# carbon) and the reference model of `CorrelatedXsec` with its prior 1σ band (ν, ν̄ and their ratio sampled jointly).
# Run from this directory with the Newtrinos.jl environment at the pinned commit:
#   julia --project=<path to Newtrinos.jl> reproduce.jl
using Newtrinos, CairoMakie, CSV, DataFrames, Printf, Statistics, Random, Distributions, LinearAlgebra

cd(@__DIR__); mkpath("ours"); mkpath("results")
const X = Newtrinos.xsec

readc(f) = CSV.read(f, DataFrame; comment = "#")
R = readc("data/ratio_nubar_nu.csv")
Sn, Sb = readc("data/sigma_nu_1701.csv"), readc("data/sigma_nubar_1701.csv")
covmat(f) = Matrix{Float64}(readc(f)[:, 2:end]) ./ 1000
CR, CS = covmat("data/cov_ratio_1701.csv"), covmat("data/cov_sigma_1701.csv")
@assert isapprox(sqrt.(diag(CR)), R.total; rtol = 0.05)
@assert isapprox(sqrt.(diag(CS)), vcat(Sn.total, Sb.total); rtol = 0.05)

tab = X.load_table()
E = 10 .^ range(log10(0.2), log10(25), length = 300)   # down to the threshold region
at(v, e = E) = X.at_energies(tab.E, v, e)
TUNES = Symbol.(sort(tab.tunes))
σ_tune(t, anti, e = E) = at(sum(X.generator_sigma(t, :C, :numu, anti, :CC)), e)
binE(lo, hi) = collect(range(lo, hi, length = 21))
bins = collect(zip(R.E_lo_GeV, R.E_hi_GeV))

xs = X.configure(X.CorrelatedXsec())
ref = xs.cfg.reference
sig = Dict(anti => xs.sigma(E; flavour = :numu, anti, current = :CC, target = :C) for anti in (false, true))
sigb = Dict(anti => [xs.sigma(binE(lo, hi); flavour = :numu, anti, current = :CC, target = :C) for (lo, hi) in bins] for anti in (false, true))
# model per bin: σ/E averaged flat in E within the bin; ratio of the bin-averaged σ (σ/E × E averaged)
mbin(p, anti) = [mean(f(p)) for f in sigb[anti]]
mratio(p) = [mean(fb(p) .* binE(b...)) / mean(fn(p) .* binE(b...)) for (fn, fb, b) in zip(sigb[false], sigb[true], bins)]

rng = MersenneTwister(1)
draw() = map(p -> p isa Distribution ? rand(rng, p) : p, xs.priors)
samples = [draw() for _ in 1:500]
p0 = xs.params
q(M, a) = [quantile(filter(!isnan, row), a) for row in eachrow(M)]   # ratios are undefined where a draw has σ = 0
band = Dict(anti => (M = reduce(hcat, [sig[anti](p) for p in samples]); (lo = q(M, 0.16), hi = q(M, 0.84))) for anti in (false, true))
Rs = reduce(hcat, [sig[true](p) ./ sig[false](p) for p in samples])
rband = (lo = q(Rs, 0.16), hi = q(Rs, 0.84))

ticks = ([0.2, 0.5, 1, 2, 5, 10, 20], ["0.2", "0.5", "1", "2", "5", "10", "20"])
tunecol(i) = (Makie.wong_colors()[mod1(i, 7)], 0.8)
function panel_ratio(pos)
    ax = Axis(pos, xscale = log10, xlabel = "E_ν (GeV)", ylabel = "σ(ν̄μ) / σ(νμ)", title = "MINERvA ratio, carbon (isoscalar)",
              limits = (0.2, 25, 0.2, 0.6), xticks = ticks)
    band!(ax, E, rband.lo, rband.hi, color = (:dodgerblue, 0.25), label = "CorrelatedXsec prior 1σ")
    for (i, t) in enumerate(TUNES)
        lines!(ax, E, σ_tune(t, true) ./ σ_tune(t, false), color = tunecol(i), linewidth = 1, label = "GENIE $(t)")
    end
    lines!(ax, E, sig[true](p0) ./ sig[false](p0), color = :dodgerblue, linewidth = 2.5, label = "nominal ($(ref))")
    errorbars!(ax, R.E_center_GeV, R.ratio, R.total, color = :black, whiskerwidth = 4)
    errorbars!(ax, R.E_center_GeV, R.ratio, R.E_center_GeV .- R.E_lo_GeV, R.E_hi_GeV .- R.E_center_GeV, direction = :x, color = :black)
    scatter!(ax, R.E_center_GeV, R.ratio, color = :black, label = "MINERvA (total)")
    ax
end
function panel_sigma(pos, anti, S, lab, lims)
    ax = Axis(pos, xscale = log10, xlabel = "E_ν (GeV)", ylabel = "$lab CC σ / E (10⁻³⁸ cm²/GeV per nucleon)",
              title = "MINERvA $lab, carbon (isoscalar)", limits = (0.2, 25, lims...), xticks = ticks)
    band!(ax, E, band[anti].lo, band[anti].hi, color = (:dodgerblue, 0.25), label = "CorrelatedXsec prior 1σ")
    for (i, t) in enumerate(TUNES)
        lines!(ax, E, σ_tune(t, anti), color = tunecol(i), linewidth = 1, label = "GENIE $(t)")
    end
    lines!(ax, E, sig[anti](p0), color = :dodgerblue, linewidth = 2.5, label = "nominal ($(ref))")
    y = S[!, 4]
    errorbars!(ax, S.E_center_GeV, y, S.total, color = :black, whiskerwidth = 4)
    errorbars!(ax, S.E_center_GeV, y, S.E_center_GeV .- S.E_lo_GeV, S.E_hi_GeV .- S.E_center_GeV, direction = :x, color = :black)
    scatter!(ax, S.E_center_GeV, y, color = :black, label = "MINERvA (total)")
    ax
end
for (name, mk) in (("ratio", panel_ratio), ("nu", pos -> panel_sigma(pos, false, Sn, "νμ", (0.0, 1.05))),
                   ("nubar", pos -> panel_sigma(pos, true, Sb, "ν̄μ", (0.0, 0.42))))
    fig = Figure(size = (800, 470))
    ax = mk(fig[1, 1])
    Legend(fig[1, 2], ax, framevisible = false, labelsize = 9)
    save("ours/$(name).png", fig)
end

# χ² with the published covariances; for the reference also with the model's prior covariance (from the samples) added
chi2(d, m, C) = (r = d .- m; dot(r, C \ r))
dS = vcat(Sn[!, 4], Sb[!, 4])
mS(p) = vcat(mbin(p, false), mbin(p, true))
MSs = reduce(hcat, [mS(p) for p in samples]); MRs = reduce(hcat, [mratio(p) for p in samples])
rows = [(model = "$(ref) (nominal)", chi2_ratio = chi2(R.ratio, mratio(p0), CR), chi2_sigma = chi2(dS, mS(p0), CS),
         chi2_ratio_with_prior = chi2(R.ratio, mratio(p0), CR + cov(MRs, dims = 2)),
         chi2_sigma_with_prior = chi2(dS, mS(p0), CS + cov(MSs, dims = 2)))]
tbin(t, anti) = [mean(σ_tune(t, anti, binE(b...))) for b in bins]
tratio(t) = [mean(σ_tune(t, true, binE(b...)) .* binE(b...)) / mean(σ_tune(t, false, binE(b...)) .* binE(b...)) for b in bins]
for t in TUNES
    push!(rows, (model = string(t), chi2_ratio = chi2(R.ratio, tratio(t), CR), chi2_sigma = chi2(dS, vcat(tbin(t, false), tbin(t, true)), CS),
                 chi2_ratio_with_prior = NaN, chi2_sigma_with_prior = NaN))
end
out = DataFrame(rows)
out.ndf_ratio .= nrow(R); out.ndf_sigma .= length(dS)
CSV.write("results/chi2.csv", out)
CSV.write("results/ratio.csv", DataFrame(E_lo = R.E_lo_GeV, E_hi = R.E_hi_GeV, data = R.ratio, err = R.total,
                                         reference = round.(mratio(p0), digits = 4), prior_1sigma = round.(vec(std(MRs, dims = 2)), digits = 4)))
show(stdout, out, allrows = true, allcols = true); println()

Provenance

Newtrinos.jl commit78e71c310b (xsec (generator_sigma, CorrelatedXsec))
Run date2026-10-12T14:40:57Z
Wall time1.6 min (4 threads)
Julia1.12.5
MachineAMD EPYC 7702P 64-Core Processor, 128 hardware threads, Linux
Environmentnewtrinos_Manifest.toml · run.toml

Downloads

CSV files hold one row per scan point: the scanned and profiled parameters (sin2_theta23, dm2_32 in eV², dcp in rad, nuisance parameters), dchi2 = −2Δ log L and the log-likelihood. JLD2 files contain the full NewtrinosResult objects.

FileSizeSHA-256
ours/nu.png234.6 kB30826dfb3574…
ours/nubar.png226.8 kBa2086c0e14c5…
ours/ratio.png212.2 kB460ceae74b91…
results/chi2.csv856 B61f1488c2d82…
results/ratio.csv312 B8b8f8d29621b…