Add committed i3 test fixture + generator for extractor edge cases#906
Add committed i3 test fixture + generator for extractor edge cases#906sevmag wants to merge 3 commits into
Conversation
Adds an 8-event hand-seeded i3 fixture (data/tests/i3/i3_fixture/i3_fixture_events.i3.zst) and its generator (tests/data/generate_i3_fixture.py) so the IceCube i3 extractors -- starting with I3Calorimetry and the I3Extractor helpers it relies on -- can be unit-tested. The fixture carries no IceCube-internal data (GraphNeT's own public GCD, PROPOSAL open cross-section tables, fixed RNG seed) and is committed so CI reads it directly and never has to run icetray/PROPOSAL. Motivated by graphnet-team#905. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ToS7BT8sSuyhzDGJFRGQuX
Aske-Rosted
left a comment
There was a problem hiding this comment.
I've put a few comments.
| ptype = "MuMinus" if i % 2 == 0 else "MuPlus" | ||
| muon = _make_particle(ptype, energy, vertex, mu_dir) | ||
| tree.append_child(primary, muon) | ||
| return tree |
There was a problem hiding this comment.
I think this is the wrong way of going about it. Here you end up with particles which have slightly different directions, however the muon bundles in ice are actually pretty much collinear with some positional spread.
Something like:
if i == 0:
offset = np.zeros(3) # core muon on the axis
else:
phi = 2.0 * np.pi * (i - 1) / (n - 1)
r = CORSIKA_BUNDLE_RADIUS # transverse offset, in meters
offset = r * (np.cos(phi) * u + np.sin(phi) * v)
mu_pos = vertex + offset
ptype = "MuMinus" if i % 2 == 0 else "MuPlus"
muon = _make_particle(ptype, energy, mu_pos, direction) # shared direction
tree.append_child(primary, muon)```
With CORSIKA_BUNDLE_RADIUS ~10m
| tree.add_primary(neutrino) | ||
| tree.append_child(neutrino, hadrons) | ||
| tree.append_child(neutrino, lepton) | ||
| return tree |
There was a problem hiding this comment.
This event, going by the description, is not a type of event that we actually have. If there are no charge recording then nothing is triggered.
There was a problem hiding this comment.
Good point I guess what I was going for are the events where the only charge we see in the detector comes from coincident background events. I will add a coincident background muon that reaches the detector
| tree.add_primary(neutrino) | ||
| tree.append_child(neutrino, hadrons) | ||
| tree.append_child(neutrino, lepton) | ||
| return tree |
There was a problem hiding this comment.
I don't think this is expected to happen... If I remember correctly then the cases where I encountered a nan energy neutrino primary then it will have a neutrino daughter with a defined energy.
| f"{ctx}: expected the first in-ice neutrino below the top of " | ||
| "the tree so get_primaries recurses (top-level in-ice nu: " | ||
| f"{has_top_in_ice_nu}, deeper in-ice nu: {has_below_in_ice_nu})" | ||
| ) |
There was a problem hiding this comment.
wouldn't this fail for the nan-energy event you generated?
There was a problem hiding this comment.
it would fail, that is true. But this function is only called when.
frame.Has("TopPrimaryOutsideIce")
and frame["TopPrimaryOutsideIce"].valueevaluates to true, which is only the case for the EdgeCase with the name "non_top_level_in_ice_nu"
…dent-bg + nan events - corsika_muon_bundle: make the bundle collinear (all muons share the shower direction) with a ~10 m transverse positional spread, instead of fanning the muon directions. - Replace no_descendants_reach_hull with coincident_background_muon: a signal neutrino whose products stop short of the array plus a coincident pi+ -> muon that does cross it, so the only detector light is background (a real event type; a zero-charge event never triggers). - nan_primary_energy: the NaN-energy in-ice primary now carries a neutrino daughter with defined energy (via an NC-then-CC chain), matching real NaN primaries, so check_primary_energy falls back to that daughter. - Regenerate the committed fixture accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Aske-Rosted
left a comment
There was a problem hiding this comment.
I think this looks good enough for an initial set of test events, applying the extractors to these will probably reveal if we have to do any further refining.
What
Adds a small, public i3 test fixture and the script that generates it, so the IceCube i3 extractors — starting with
I3Calorimetryand theI3Extractorhelpers it relies on — can finally be unit-tested. Motivated by #905.data/tests/i3/i3_fixture/i3_fixture_events.i3.zst— 8 hand-seeded events, each built to realise one extractor edge case. Committed (~13 MB) and cached, so CI reads it directly and never has to run icetray/PROPOSAL.tests/data/generate_i3_fixture.py— the generator that produces it (fixed RNG seed → reproducible; every frame validated at generation time).It's a general extractor fixture, not calorimetry-specific — the current events happen to cover the
I3Calorimetryedge cases, but the same file can back tests for other i3 extractors.Why it's safe to commit (no IceCube-internal data)
data/tests/i3/oscNext_genie_level7_v02/…) — the fixture reuses it, no new GCD added.The 8 events
through_going_muonstopping_track_containedstarting_tracktau_to_mu_decaynan_primary_energycheck_primary_energyfalls back to daughtersnon_top_level_in_ice_nuget_primariesrecurses (find_in_ice_daughters)no_descendants_reach_hulle_total = 0+ warning; emptyMMCTrackListcorsika_muon_bundleis_corsika=True(empty target, full background)Locations
data/tests/i3/, next to the other committed test i3 data and reachable via the sameTEST_DATA_DIRmechanism (src/graphnet/constants.py).tests/data/(it's code, anddata/holds only data). Its paths are resolved relative to the file, so regeneration reads/writes the worktree regardless of whichgraphnetinstall is importable.Running it
Inside the GraphNeT icetray Docker image:
The first run builds PROPOSAL's interpolation tables in a scratch dir (cached afterwards); full generation is a few minutes (dominated by the 10 PeV τ). The generator passes the full pre-commit suite (black/flake8/docformatter/pydocstyle/mypy).
Not in this PR
The unit tests themselves — those are the follow-up tracked in #905. This PR just lands the fixture + generator so the tests have data to read. The
corsika_muon_bundleis an illustrative hand-seeded bundle (each muon is really propagated by PROPOSAL, but the multiplicity/spectrum are hand-chosen) — see #905 for the residual coverage gaps a fuller suite should still add.Part of #905.