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 1 commit 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.
| 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?
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.