Geometry and selection workflow
The selection stage estimates whether a generated LLP decay would be observable in the ANUBIS detector concept. It operates on selection-ready dataframe bundles created from HepMC events or loaded from the database. The ordering of the cutflow is physics-motivated: geometry and tracking acceptance are evaluated before ATLAS-associated kinematic and isolation cuts.
Event objects
HepMC events are converted into dataframes and split into analysis objects:
LLP candidates, selected by PDG identifier;
recursively collected LLP decay products;
charged and neutral prompt final states not produced by the LLP;
anti-\(k_t\) jet candidates built from prompt final-state particles;
event-level missing transverse energy, if available or computed from MET components.
The nominal prompt-object definitions used by the paper are configurable in the code. They use final-state particles not produced by the LLP, require promptness with respect to the interaction point, and impose transverse-momentum thresholds before isolation and jet clustering are evaluated.
Geometry model
The geometry layer represents the ATLAS UX1 cavern, the ATLAS exclusion region, the PX14/PX16 shaft regions and configurable ANUBIS tracking layers. The nominal configuration described in the paper uses two tracking layers following the cavern ceiling, separated by about one metre, with the first layer close to the ceiling. The number of stations, the number of RPCs per layer and the RPC efficiency are configurable through the geometry/selection configuration.
The geometry cut is intentionally applied before MET or isolation cuts. A signal candidate first has to decay in a region where ANUBIS can plausibly observe charged daughters; only then do the ATLAS-associated background rejection cuts become meaningful.
Nominal cutflow
The current SelectionEngine applies the following standard LLP-level steps:
nLLP_LLPdecay: keep LLP candidates that decay rather than stable outgoing particles.nLLP_InCavernornLLP_InShaft: require the decay vertex to lie in the selected ANUBIS geometry mode.nLLP_NotInATLAS: reject vertices inside the ATLAS detector volume.nLLP_Geometry: require the LLP trajectory to intersect the required number of ANUBIS stations.nLLP_Tracker: require charged LLP decay products to produce the requested number of track intersections in the RPC layers.nLLP_MET: require missing transverse energy above the configured threshold, nominally30 GeV.nLLP_IsoJet/nLLP_IsoCharged/nLLP_IsoAll: reject candidates too close in \(\Delta R\) to prompt jets or charged tracks.
The MET and isolation requirements encode the strategy that ANUBIS can use information from the ATLAS event. LLP decays missed by ATLAS can be associated with apparent missing energy, while cavern-going muons and punch-through jets can be reduced by isolation against prompt charged tracks and jets.
Minimal configuration
from setanubis import (
ATLASCavernGeometry,
ATLASCavernGeometryConfig,
SelectionGeometryAdapter,
SelectionConfig,
RunConfig,
MinThresholds,
MinDR,
SelectionPipelineBuilder,
SelectionManager,
EventsBundleSource,
)
geometry_backend = ATLASCavernGeometry.create(
ATLASCavernGeometryConfig(mode="ceiling", origin="IP", use_cache=False)
)
geometry = SelectionGeometryAdapter(geometry_backend)
selection = SelectionConfig(
geometry=geometry,
minMET=30.0,
minP=MinThresholds(LLP=0.1, chargedTrack=0.1, neutralTrack=0.1, jet=0.1),
minPt=MinThresholds(LLP=0.0, chargedTrack=5.0, neutralTrack=5.0, jet=15.0),
minDR=MinDR(jet=0.4, chargedTrack=0.4, neutralTrack=0.4),
nStations=2,
nIntersections=2,
nTracks=2,
)
pipeline = (
SelectionPipelineBuilder()
.set_options(add_jets=True, compute_isolation=True, selection_mode="standard")
.build()
)
source = EventsBundleSource.from_bundle_file("sample_bundle.pkl.gz")
combined = SelectionManager(pipeline).run_many(
named_sources=[("HNL_scan_point", source)],
sel_cfg=selection,
run_cfg=RunConfig(reweightLifetime=False, plotTrajectory=False),
)
print(combined.cutflow_sum)
Intermediate-stage tracing
Tracing is disabled by default so normal scans do not retain duplicate dataframes. Enable it for debugging, validation, or cutflow studies:
result = pipeline.run(
source,
selection,
RunConfig(capture_intermediate=True),
)
trace = result["trace"]
# Ordered pandas DataFrames are available for interactive analysis.
met_candidates = trace.stage_dataframes["MET"]
print(trace.candidate_summary)
print(trace.event_summary)
# The report is standalone and embeds its JSON summary in the HTML page.
trace.write_report("selection_trace_output")
The candidate summary records every stage passed by each LLP, the first failed
stage and the last stage reached. The event summary aggregates those flags and
candidate counts by eventNumber. The full stage DataFrames remain available
in Python and can optionally be included as records in the JSON output.
The runnable
examples/Selection/example_real_selection_trace_report.py example uses seven
real HNL events selected from a 4,000-event corpus. The sample includes the
smallest event found for each observed outcome: failures at InCavern,
NotInATLAS, Geometry, Tracker, MET and IsoJets, plus one event
that reaches Final. No real event in the supplied corpus first failed at
LLPDecay, IsoCharged or IsoAll.
The aligned HepMC2, gzip CSV, trusted gzip-pickle bundle and JSON provenance
manifest are stored under examples/Selection/InputFiles and together occupy
less than 1 MB. The manifest records the source filename, source event ordinal
and original event number for every retained event. The synthetic
example_selection_trace_report.py remains available for deliberately
constructed branch-by-branch demonstrations.
Warning
Pickle-based bundles can execute arbitrary Python code while loading. Only
open bundles generated by a trusted SET-ANUBIS workflow or obtained from a
verified source. Use CSV/JSON/Parquet for exchange across trust boundaries
where the dataframe schema permits it. See the repository SECURITY.md.
BundleIO writes gzip-compressed pickle data and detects gzip from the file
signature when loading. Legacy compressed files named *.pkl therefore remain
readable, although new files should use the explicit *.pkl.gz suffix.
Lifetime reweighting
For scans where the generated kinematics are reusable but the LLP lifetime changes, the pipeline can reweight decay positions instead of regenerating the full sample for every lifetime point. This reduces CPU cost and storage while preserving a route back to the original generated event metadata.
Sensitivity input
The selection acceptance is the fraction of generated LLP candidates that pass the full cutflow. It is intended to be combined with luminosity, production cross section, branching fraction and a signal efficiency assumption, for example
The required event yield can be chosen by the user. The paper discusses both a background-free style threshold and a more conservative threshold motivated by ANUBIS background estimates.
Recommended examples
setanubis/SetAnubis/examples/Selection/dev_examples/example_df_event_from_hepmc.pysetanubis/SetAnubis/examples/Selection/dev_examples/example_sampledfs_from_df.pysetanubis/SetAnubis/examples/Selection/dev_examples/example_jets_and_pT_deltaR_cuts.pysetanubis/SetAnubis/examples/Selection/example_hepmc_from_db.pysetanubis/SetAnubis/examples/Selection/example_selection_pipeline.pysetanubis/SetAnubis/examples/Selection/example_real_selection_trace_report.pysetanubis/SetAnubis/examples/Selection/example_selection_trace_report.py