IceCube Collaboration · Phys. Rev. Lett. 125, 141801 (2020)
eV-Scale Sterile Neutrino Search Using Eight Years of Atmospheric Muon Neutrino Data from the IceCube Neutrino Observatory
The matter-enhanced ν̄μ disappearance in a 3+1 model at TeV energies, computed with Newtrinos.jl (module osc with the Sterile flavour model and standard matter interactions, including the neutral-current potential felt by the active but not the sterile state; earth_layers), at the global-fit point used in the paper's Fig. 1 (Δm²₄₁ = 1.3 eV², sin²2θ₂₄ = 0.07, θ₃₄ = θ₁₄ = 0). Shown for the full PREM profile (PREM zoned at its density discontinuities with chord-averaged densities), for the default four-zone Earth, and for the four zones with chord-averaged densities.
Figures
Atmospheric ν̄μ disappearance vs. energy and zenith angle: full PREM, 4 zones, 4 zones chord-averaged · Fig. 1
Original figure: IceCube Collaboration, Phys. Rev. Lett. 125, 141801 (2020), Fig. 1, doi:10.1103/PhysRevLett.125.141801. Not our work; shown for comparison, all rights with the authors and publisher.

Known limitations
- No absorption: the paper's oscillogram (nuSQuIDS) includes neutrino absorption and regeneration in the Earth, which produce the broad disappearance above ~10 TeV (up to ~60 % for core-crossing paths at 100 TeV). Newtrinos computes coherent oscillations only, so this region is empty in the reproduction; for analyses above ~10 TeV absorption must be added separately.
- The resonance is reproduced: core-crossing paths reach ≈ 100 % disappearance at 3.1 TeV (cos θ_z ≈ −0.99), the mantle lobe peaks at ≈ 5.1 TeV with ≈ 54 % at cos θ_z ≈ −0.83 and extends to cos θ_z ≈ −0.25, as in the paper. With the default four-zone Earth the mantle peak moves to 5.3 TeV (51 %), and the lobe shows a step at the zone boundary cos θ_z ≈ −0.45; chord-averaging the four zones brings it to 5.2 TeV (53 %).
- The three-flavour parameters are the Newtrinos defaults (the paper does not list them; they do not affect the TeV resonance). The exactness of the 3+1 matter solver itself is checked in the NuOscProbExact entry.
Reproduce
git clone https://github.com/Newtrinos-org/Newtrinos.jl.git && cd Newtrinos.jl && git checkout e1b5a89439- Download reproduce.jl into an empty directory.
julia -t 8 --project=/path/to/Newtrinos.jl reproduce.jl
# The 3+1 matter-enhanced ν̄μ disappearance through the Earth with Newtrinos.jl (module `osc`, `Sterile` flavour model
# with standard matter interactions, and `earth_layers`), compared with IceCube, Phys. Rev. Lett. 125, 141801 (2020),
# Fig. 1, at the global-fit point Δm²₄₁ = 1.3 eV², sin²2θ₂₄ = 0.07, θ₃₄ = 0.
# 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, StructArrays
cd(@__DIR__); mkpath("ours"); mkpath("results")
osc = Newtrinos.osc.configure(Newtrinos.osc.OscillationConfig(flavour = Newtrinos.osc.Sterile(), interaction = Newtrinos.osc.SI()))
earth = Newtrinos.earth_layers.configure()
p = merge(osc.params, (Δm²₄₁ = 1.3, θ₂₄ = asin(sqrt(0.07)) / 2, θ₁₄ = 0.0, θ₃₄ = 0.0, δCP = 0.0))
# the full PREM profile: PREM zoned at its density discontinuities with chord-averaged densities (pieces ≤ 100 km),
# Yₑ 0.466 in the core and 0.494 elsewhere (as the default zones)
z = Newtrinos.earth_layers.PREM_discontinuities().zones
earth_d = Newtrinos.earth_layers.configure(Newtrinos.earth_layers.PREM(zones = z, p_fractions = [z[i] >= 5.6 ? 0.466 : 0.494 for i in 1:length(z)-1]))
E = 10 .^ range(2, 5, length = 300) # GeV
CZ = collect(range(-0.999, -0.001, length = 300))
D = Dict()
lay4 = earth.compute_layers()
earths = (default = (earth.compute_paths(CZ, lay4), lay4),
chord = earth.compute_chord_paths(CZ, lay4),
fine = earth_d.compute_chord_paths(CZ, earth_d.compute_layers(); max_length = 100.0))
for (name, (paths, layers)) in pairs(earths)
P = osc.osc_prob(E, paths, layers, p; anti = true)
D[string(name)] = 100 .* (1 .- permutedims(P[:, :, 2, 2])) # disappearance [%], [cz, E]
end
cmap = cgrad(:Blues, rev = true)
fig = Figure(size = (1700, 500))
for (k, (name, ttl)) in enumerate((("fine", "full PREM (chord-averaged)"), ("default", "4 zones (default)"), ("chord", "4 zones, chord-averaged")))
ax = Axis(fig[1, k], yscale = log10, xlabel = "cos θ_z", ylabel = k == 1 ? "E_ν (GeV)" : "",
title = "ν̄μ disappearance — $ttl", limits = (-1, 0, 1e2, 1e5))
contourf!(ax, CZ, E, D[name], levels = 0:4:100, colormap = cmap)
vlines!(ax, [-0.98, -0.83], color = :white, linestyle = :dash, linewidth = 1.5)
k == 3 && Colorbar(fig[1, 4], colormap = cmap, limits = (0, 100), label = "disappearance (%)")
end
save("ours/fig1_oscillogram.png", fig)
# position and depth of the matter resonance in the mantle and in the core
function resonance(d, czsel)
sub = d[czsel, :]
i = argmax(sub); (ci, ei) = Tuple(CartesianIndices(sub)[i])
(cz = CZ[czsel][ci], E_TeV = E[ei] / 1e3, disappearance_pct = sub[ci, ei])
end
rows = [(earth = k, region = reg, resonance(D[k], sel)...) for k in ("fine", "default", "chord")
for (reg, sel) in (("mantle", (CZ .> -0.83) .& (CZ .< -0.3)), ("core", CZ .< -0.84))]
tab = DataFrame(rows)
CSV.write("results/resonance.csv", tab)
show(stdout, tab); println()
Provenance
| Newtrinos.jl commit | e1b5a89439 (osc (Sterile, SI), earth_layers) |
|---|---|
| Run date | 2026-10-11T11:10:38Z |
| Wall time | 3.2 min (8 threads) |
| Julia | 1.12.5 |
| Machine | AMD EPYC 7702P 64-Core Processor, 128 hardware threads, Linux |
| Environment | newtrinos_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.
| File | Size | SHA-256 |
|---|---|---|
| ours/fig1_oscillogram.png | 1.2 MB | bb14f3c1c02d… |
| results/resonance.csv | 453 B | 8cf7148cbe4e… |