ATLAS Offline Software
Loading...
Searching...
No Matches
TausRUsEvaluator.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
10
11#include <nlohmann/json.hpp>
12#include <onnxruntime_cxx_api.h>
13
14#include <algorithm>
15#include <cmath>
16#include <map>
17#include <numeric>
18#include <string>
19#include <unordered_map>
20#include <utility>
21
22namespace {
23
24constexpr char TAU_ID_SCORE[] = "TausRUsTauIDScore";
25constexpr char ELE_REJ_SCORE[] = "TausRUsEleRejScore";
26constexpr char DECAY_MODE[] = "TausRUsDecayMode";
27constexpr char DECAY_MODE_SCORE_PREFIX[] = "TausRUsDecayModeScore";
28constexpr char TAU_P4[] = "TausRUsTauP4";
29constexpr char CHARGED_PION_P4[] = "TausRUsChargedPionP4";
30constexpr char NEUTRAL_PION_P4[] = "TausRUsNeutralPionP4";
31constexpr char VERTEX[] = "TausRUsVertex";
32constexpr char TAU_CHARGE[] = "TausRUsTauCharge";
33
34constexpr char TRACK_CLASS[] = "TausRUsTrackClass";
35constexpr char TRACK_SCORE_PREFIX[] = "TausRUsTrackScore";
36
37constexpr char METADATA_KEY[] = "metadata";
38
39constexpr char P4_SUFFIXES[4][5] = {"_pt", "_eta", "_phi", "_m"};
40constexpr char POSITION_SUFFIXES[3][3] = {"_x", "_y", "_z"};
41
44float twoClassScore(float signal, float background) {
45 return 1.f / (1.f + std::exp(background - signal));
46}
47
49int argMax(std::span<const float> scores) {
50 if (scores.empty()) return TausRUsEvaluator::DEFAULT_CLASS;
51 const auto largest = std::max_element(scores.begin(), scores.end());
52 return static_cast<int>(std::distance(scores.begin(), largest));
53}
54
55std::string toString(const std::vector<int64_t>& dims) {
56 std::string out = "(";
57 for (size_t i = 0; i < dims.size(); ++i) {
58 out += (i ? ", " : "") + std::to_string(dims[i]);
59 }
60 return out + ")";
61}
62
65std::map<std::string, std::vector<int64_t>> nodeShapes(const Ort::Session& session,
66 bool isInput) {
67 Ort::AllocatorWithDefaultOptions allocator;
68 std::map<std::string, std::vector<int64_t>> shapes;
69 const size_t nNodes = isInput ? session.GetInputCount() : session.GetOutputCount();
70 for (size_t i = 0; i < nNodes; ++i) {
71 const std::string name = isInput
72 ? session.GetInputNameAllocated(i, allocator).get()
73 : session.GetOutputNameAllocated(i, allocator).get();
74 const Ort::TypeInfo typeInfo = isInput ? session.GetInputTypeInfo(i)
75 : session.GetOutputTypeInfo(i);
76 std::vector<int64_t> dims = typeInfo.GetTensorTypeAndShapeInfo().GetShape();
77 if (!dims.empty() && dims[0] < 0) dims[0] = 1;
78 shapes[name] = std::move(dims);
79 }
80 return shapes;
81}
82
83} // anonymous namespace
84
85TausRUsEvaluator::TausRUsEvaluator(const std::string& name)
86 : TauRecToolBase(name),
87 m_tauIDScore(TAU_ID_SCORE),
88 m_eleRejScore(ELE_REJ_SCORE),
89 m_decayMode(DECAY_MODE),
90 m_tauCharge(TAU_CHARGE),
91 m_trackClass(TRACK_CLASS) {}
92
94
95// --------------------------------------------------------------------------
96// MARK: Read model metadata
97// --------------------------------------------------------------------------
98
99StatusCode TausRUsEvaluator::readModel(const std::string& path) {
100
101 Ort::Env env(ORT_LOGGING_LEVEL_FATAL, "");
102 Ort::SessionOptions sessionOptions;
103 sessionOptions.SetIntraOpNumThreads(1);
104 sessionOptions.SetLogSeverityLevel(4);
105 sessionOptions.DisableCpuMemArena();
106 const Ort::Session session(env, path.c_str(), sessionOptions);
107
108 Ort::AllocatorWithDefaultOptions allocator;
109 const Ort::ModelMetadata modelMetadata = session.GetModelMetadata();
110 const Ort::AllocatedStringPtr metadataString =
111 modelMetadata.LookupCustomMetadataMapAllocated(METADATA_KEY, allocator);
112 if (!metadataString) {
113 ATH_MSG_ERROR("Model " << path << " has no '" << METADATA_KEY << "' metadata");
114 return StatusCode::FAILURE;
115 }
116
117 const auto inputShapes = nodeShapes(session, true);
118 const auto outputShapes = nodeShapes(session, false);
119
120 const nlohmann::json metadata = nlohmann::json::parse(metadataString.get());
121
122 std::vector<TausRUsDataLoader::InputConfig> inputs;
123 for (const nlohmann::json& node : metadata.at("inputs")) {
125 input.name = node.at("name").get<std::string>();
126 input.collection = node.at("collection").get<std::string>();
127 input.maxObjects = node.value("max_objects", size_t{1});
128 for (const nlohmann::json& variable : node.at("variables")) {
129 input.variables.push_back({variable.at("name").get<std::string>(),
130 variable.at("offset").get<float>(),
131 variable.at("scale").get<float>()});
132 }
133 if (inputShapes.find(input.name) == inputShapes.end()) {
134 ATH_MSG_ERROR("The model has no input node '" << input.name << "'");
135 return StatusCode::FAILURE;
136 }
137 inputs.push_back(std::move(input));
138 }
139
140 ATH_CHECK(m_loader->initialize(inputs));
141
142 std::map<std::string, nlohmann::json> outputMetadata;
143 for (const nlohmann::json& node : metadata.at("outputs")) {
144 Output output;
145 output.name = node.at("name").get<std::string>();
146 output.type = node.value("type", "");
147 const auto shape = outputShapes.find(output.name);
148 if (shape == outputShapes.end()) {
149 ATH_MSG_ERROR("The model has no output node '" << output.name << "'");
150 return StatusCode::FAILURE;
151 }
152 output.dims = shape->second;
153 output.size = std::accumulate(output.dims.begin(), output.dims.end(), size_t{1},
154 std::multiplies<>());
155 outputMetadata[output.name] = node;
156 m_outputs.push_back(std::move(output));
157 }
158
159 // The heads the decoding needs to size itself.
160 m_nDecayModes = outputMetadata.at("decay_mode").at("num_classes").get<size_t>();
161 m_nTrackClasses = outputMetadata.at("tautrack_class").at("num_classes").get<size_t>();
162
163 // The pt heads share one set of quantiles, and the median is decorated.
164 const auto quantiles = outputMetadata.at("tes").at("quantiles").get<std::vector<double>>();
165 for (const char* name : {"charged_pion_pt", "neutral_pion_pt"}) {
166 if (outputMetadata.at(name).at("quantiles").get<std::vector<double>>() != quantiles) {
167 ATH_MSG_ERROR("Output '" << name << "' regresses other quantiles than 'tes'");
168 return StatusCode::FAILURE;
169 }
170 }
171 const auto median = std::find_if(quantiles.begin(), quantiles.end(),
172 [](double q) { return std::abs(q - 0.5) < 1e-6; });
173 if (median == quantiles.end()) {
174 ATH_MSG_ERROR("The pt heads regress no median quantile");
175 return StatusCode::FAILURE;
176 }
177 m_nQuantiles = quantiles.size();
178 m_ptMedianQuantile = std::distance(quantiles.begin(), median);
179
180 // The per-slot heads are decoded against the track and vertex inputs.
181 if (m_loader->maxTracks() == 0 || m_loader->maxVertices() == 0) {
182 ATH_MSG_ERROR("The model needs both a track and a vertex input");
183 return StatusCode::FAILURE;
184 }
185
186 return StatusCode::SUCCESS;
187}
188
190 ATH_CHECK(m_inferenceTool.retrieve());
191 ATH_CHECK(m_vertexInputContainer.initialize());
192
193 const std::string modelPath = find_file(m_modelFile);
194 if (modelPath.empty()) {
195 ATH_MSG_ERROR("Could not find the model file '" << m_modelFile.value() << "'");
196 return StatusCode::FAILURE;
197 }
198 m_loader = std::make_unique<TausRUsDataLoader>(name() + "_DataLoader");
199 ATH_CHECK(readModel(modelPath));
200
201 // Check the graph against what the decoding below assumes about it, so that a
202 // re-exported model with a changed head fails here rather than silently
203 // decorating nonsense.
204 auto checkNode = [this](const std::string& name, const std::string& type,
205 const std::vector<int64_t>& dims) -> StatusCode {
206 auto output = std::find_if(m_outputs.begin(), m_outputs.end(),
207 [&name](const Output& o) { return o.name == name; });
208 if (output == m_outputs.end()) {
209 ATH_MSG_ERROR("The model metadata has no output '" << name << "'");
210 return StatusCode::FAILURE;
211 }
212 if (output->type != type) {
213 ATH_MSG_ERROR("Output '" << name << "' is a '" << output->type
214 << "' head but the tool decodes it as '" << type << "'");
215 return StatusCode::FAILURE;
216 }
217 if (output->dims != dims) {
218 ATH_MSG_ERROR("Output '" << name << "' has shape " << toString(output->dims)
219 << " but the tool decodes " << toString(dims));
220 return StatusCode::FAILURE;
221 }
222 return StatusCode::SUCCESS;
223 };
224
225 ATH_CHECK(checkNode("primary", "classification", {1, 3}));
226 ATH_CHECK(checkNode("decay_mode", "classification",
227 {1, static_cast<int64_t>(m_nDecayModes)}));
228 for (const char* name : {"tes", "charged_pion_pt", "neutral_pion_pt"}) {
229 ATH_CHECK(checkNode(name, "quantile_regression",
230 {1, static_cast<int64_t>(m_nQuantiles)}));
231 }
232 for (const char* name : {"tau_eta", "charged_pion_eta", "neutral_pion_eta"}) {
233 ATH_CHECK(checkNode(name, "regression", {1, 1}));
234 }
235 for (const char* name : {"tau_phi", "charged_pion_phi", "neutral_pion_phi"}) {
236 ATH_CHECK(checkNode(name, "periodic_regression", {1, 2}));
237 }
238 ATH_CHECK(checkNode("vertex_classification", "classification",
239 {1, static_cast<int64_t>(m_loader->maxVertices())}));
240 ATH_CHECK(checkNode("tautrack_class", "classification",
241 {1, static_cast<int64_t>(m_loader->maxTracks()),
242 static_cast<int64_t>(m_nTrackClasses)}));
243
244 // The decorators, in the order of the suffixes they are named after.
245 for (size_t i = 0; i < m_nDecayModes; ++i) {
246 m_decayModeScores.emplace_back(std::string(DECAY_MODE_SCORE_PREFIX)
247 + std::to_string(i));
248 }
249 for (const auto& suffix : P4_SUFFIXES) {
250 m_tauP4.emplace_back(std::string(TAU_P4) + suffix);
251 m_chargedPionP4.emplace_back(std::string(CHARGED_PION_P4) + suffix);
252 m_neutralPionP4.emplace_back(std::string(NEUTRAL_PION_P4) + suffix);
253 }
254 for (const auto& suffix : POSITION_SUFFIXES) {
255 m_vertexPosition.emplace_back(std::string(VERTEX) + suffix);
256 }
257 for (size_t i = 0; i < m_nTrackClasses; ++i) {
258 m_trackScores.emplace_back(std::string(TRACK_SCORE_PREFIX) + std::to_string(i));
259 }
260
261 ATH_MSG_INFO("TausRUs decorates " << tauDecorationNames().size()
262 << " variables on the tau and " << trackDecorationNames().size()
263 << " on each of its up to " << m_loader->maxTracks() << " leading tracks");
264
265 // Create the decoration keys to enforce data dependencies in the scheduler
266 if (!m_tauContainerName.empty()) {
267 for (const std::string& decoration : tauDecorationNames()) {
268 m_decorKeys.emplace_back(m_tauContainerName + "." + decoration);
269 }
271 ATH_CHECK(key.initialize());
272 }
273 }
274 // The per-track decorations are written through handles on these keys, so
275 // unlike the tau ones the container has to be named.
276 if (m_tauTrackContainerName.empty()) {
277 ATH_MSG_ERROR("TauTrackContainerName is not set, but the per-track "
278 << trackDecorationNames().size() << " decorations are written on it");
279 return StatusCode::FAILURE;
280 }
281 for (const std::string& decoration : trackDecorationNames()) {
282 m_trackDecorKeys.emplace_back(m_tauTrackContainerName + "." + decoration);
283 }
285 ATH_CHECK(key.initialize());
286 }
287
288 return StatusCode::SUCCESS;
289}
290
291// --------------------------------------------------------------------------
292// MARK: Decorations
293// --------------------------------------------------------------------------
294
297std::vector<std::string> TausRUsEvaluator::tauDecorationNames() const {
298 std::vector<std::string> names{TAU_ID_SCORE, ELE_REJ_SCORE, DECAY_MODE, TAU_CHARGE};
299 for (size_t iMode = 0; iMode < m_nDecayModes; ++iMode) {
300 names.emplace_back(std::string(DECAY_MODE_SCORE_PREFIX) + std::to_string(iMode));
301 }
302 for (const char* base : {TAU_P4, CHARGED_PION_P4, NEUTRAL_PION_P4}) {
303 for (const auto& suffix : P4_SUFFIXES) {
304 names.emplace_back(std::string(base) + suffix);
305 }
306 }
307 for (const auto& suffix : POSITION_SUFFIXES) {
308 names.emplace_back(std::string(VERTEX) + suffix);
309 }
310 return names;
311}
312
313std::vector<std::string> TausRUsEvaluator::trackDecorationNames() const {
314 std::vector<std::string> names{TRACK_CLASS};
315 for (size_t iClass = 0; iClass < m_nTrackClasses; ++iClass) {
316 names.emplace_back(std::string(TRACK_SCORE_PREFIX) + std::to_string(iClass));
317 }
318 return names;
319}
320
326 for (const SG::Accessor<float>& decorator : m_decayModeScores) {
327 decorator(tau) = DEFAULT_VALUE;
328 }
329 for (const FourMomDecorators* decorators : {&m_tauP4, &m_chargedPionP4, &m_neutralPionP4}) {
330 for (const SG::Accessor<float>& decorator : *decorators) {
331 decorator(tau) = DEFAULT_VALUE;
332 }
333 }
334 for (const SG::Accessor<float>& decorator : m_vertexPosition) {
335 decorator(tau) = DEFAULT_VALUE;
336 }
337
338 // All of the tracks, not just the leading ones the network sees, so that a
339 // track dropped by the truncation is still decorated.
340 for (const xAOD::TauTrack* track : tau.allTracks()) {
341 if (!track) continue;
342 m_trackClass(*track) = DEFAULT_CLASS;
343 for (const SG::Decorator<float>& decorator : m_trackScores) {
344 decorator(*track) = DEFAULT_VALUE;
345 }
346 }
347}
348
350 const FourMomDecorators& decorators,
351 std::span<const float> ptQuantiles,
352 std::span<const float> etaValues,
353 std::span<const float> phiValues,
354 float mass) const {
355 // The pt heads regress quantiles of the log response, log(ptJetSeed / pt), so
356 // the seed pt is needed to undo it and a seed with no pt leaves the defaults.
357 const float ptJetSeed = tau.ptJetSeed();
358 if (ptJetSeed <= 0.f) {
359 ATH_MSG_DEBUG("Seed jet pt is " << ptJetSeed << ", leaving the regressed "
360 "four-momenta at their defaults");
361 return;
362 }
363
364 const float response = ptQuantiles[m_ptMedianQuantile];
365 decorators[0](tau) = std::exp(std::log(ptJetSeed) - response);
366 decorators[1](tau) = etaValues[0];
367 decorators[2](tau) = std::atan2(phiValues[PHI_SIN], phiValues[PHI_COS]);
368 decorators[3](tau) = mass;
369}
370
372 std::span<const float> scores,
373 const std::vector<const xAOD::Vertex*>& vertices) const {
374 // Only the slots backed by a real vertex carry a meaningful score: the rest
375 // score zero-padded inputs, and an event with no vertex at all leaves the
376 // defaults.
377 const size_t nSlots = std::min(vertices.size(), scores.size());
378 if (nSlots == 0) {
379 ATH_MSG_DEBUG("No vertex to choose from, leaving the vertex position at its default");
380 return;
381 }
382
383 const int selected = argMax(scores.first(nSlots));
384 const xAOD::Vertex* vertex = vertices[selected];
385 m_vertexPosition[0](tau) = vertex->x();
386 m_vertexPosition[1](tau) = vertex->y();
387 m_vertexPosition[2](tau) = vertex->z();
388}
389
391 std::span<const float> scores) const {
392
393 const std::vector<const xAOD::TauTrack*> tracks = m_loader->selectTracks(tau);
394
395 int tauCharge = 0; // set default value of charge
396 for (size_t iTrack = 0; iTrack < tracks.size(); ++iTrack) {
397 const std::span<const float> slot =
398 scores.subspan(iTrack * m_nTrackClasses, m_nTrackClasses);
399 const int trackClass = argMax(slot);
400 m_trackClass(*tracks[iTrack]) = trackClass;
401 for (size_t iClass = 0; iClass < m_nTrackClasses; ++iClass) {
402 m_trackScores[iClass](*tracks[iTrack]) = slot[iClass];
403 }
404 // only add charge to tau charge if the track is the correct one.
405 if (trackClass == TAU_TRACK_CLASS) {
406 tauCharge += static_cast<int>(tracks[iTrack]->track()->charge());
407 }
408 }
409
410 // Only append a valid charge
411 if (tauCharge == -1 || tauCharge == 1) {
412 m_tauCharge(tau) = tauCharge;
413 }
414}
415
417
418 // Set the defaults before any early return, so that a tau skipped below still
419 // carries every decoration and no consumer has to test for their presence.
420 setDefaults(tau);
421
422 if (tau.pt() < m_minTauPt) {
423 return StatusCode::SUCCESS;
424 }
425
427 if (!vertexInHandle.isValid()) {
428 ATH_MSG_ERROR("Could not retrieve vertex container "
429 << m_vertexInputContainer.key());
430 return StatusCode::FAILURE;
431 }
432
433 AthInfer::InputDataMap inputData = m_loader->loadInputs(tau, *vertexInHandle);
434
435 AthInfer::OutputDataMap outputData;
436 for (const Output& output : m_outputs) {
437 outputData[output.name] = std::make_pair(output.dims, std::vector<float>{});
438 }
439
440 // MARK: run the inference
441 ATH_CHECK(m_inferenceTool->inference(inputData, outputData));
442
443 std::unordered_map<std::string, std::span<const float>> raw;
444 raw.reserve(m_outputs.size());
445 for (const Output& output : m_outputs) {
446 const std::vector<float>& values =
447 std::get<std::vector<float>>(outputData.at(output.name).second);
448 if (values.size() != output.size) {
449 ATH_MSG_ERROR("Output '" << output.name << "' returned " << values.size()
450 << " values but the tool expects " << output.size);
451 return StatusCode::FAILURE;
452 }
453 raw[output.name] = values;
454 }
455
456 const std::span<const float> primary = raw.at("primary");
457 m_tauIDScore(tau) = twoClassScore(primary[Tau], primary[QCD]);
458 m_eleRejScore(tau) = twoClassScore(primary[Tau], primary[Electron]);
459
460 const std::span<const float> decayMode = raw.at("decay_mode");
461 m_decayMode(tau) = static_cast<float>(argMax(decayMode));
462 for (size_t iMode = 0; iMode < m_nDecayModes; ++iMode) {
463 m_decayModeScores[iMode](tau) = decayMode[iMode];
464 }
465
466 // The network gives pt, eta and phi only, so the four-momenta are completed
467 // with the PDG masses.
468 decorateFourMomentum(tau, m_tauP4, raw.at("tes"), raw.at("tau_eta"),
469 raw.at("tau_phi"),
471 decorateFourMomentum(tau, m_chargedPionP4, raw.at("charged_pion_pt"),
472 raw.at("charged_pion_eta"), raw.at("charged_pion_phi"),
474 decorateFourMomentum(tau, m_neutralPionP4, raw.at("neutral_pion_pt"),
475 raw.at("neutral_pion_eta"), raw.at("neutral_pion_phi"),
477
478 decorateVertex(tau, raw.at("vertex_classification"),
479 m_loader->selectVertices(*vertexInHandle));
480 decorateTracks(tau, raw.at("tautrack_class"));
481
482 return StatusCode::SUCCESS;
483}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_INFO(x,...)
Handle class for reading from StoreGate.
double charge(const T &p)
Definition AtlasPID.h:1003
std::vector< std::vector< float > > scores
MDT_Response response
A number of constexpr particle constants to avoid hardcoding them directly in various places.
size_t size() const
Number of registered mappings.
Helper class to provide type-safe access to aux data.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
Property holding a SG store/key/clid/attr name from which a WriteDecorHandle is made.
TauRecToolBase(const std::string &name)
std::string find_file(const std::string &fname) const
static constexpr int TAU_TRACK_CLASS
Class for true-tau track from tau_track_class head.
void decorateTracks(xAOD::TauJet &tau, std::span< const float > scores) const
Decorate each track of tau with its slot of the per-track head.
static constexpr float DEFAULT_VALUE
initialized with these default values
virtual StatusCode initialize() override
Tool initializer.
Gaudi::Property< std::string > m_tauContainerName
std::unique_ptr< TausRUsDataLoader > m_loader
std::vector< SG::Accessor< float > > FourMomDecorators
FourMomDecorators m_chargedPionP4
Gaudi::Property< std::string > m_modelFile
std::vector< SG::WriteDecorHandleKey< xAOD::TauTrackContainer > > m_trackDecorKeys
void decorateFourMomentum(xAOD::TauJet &tau, const FourMomDecorators &decorators, std::span< const float > ptQuantiles, std::span< const float > etaValues, std::span< const float > phiValues, float mass) const
Decorate the four floats of decorators from a pt, an eta and a phi head, taking the mass from mass.
size_t m_nDecayModes
Read off the metadata at initialize().
virtual StatusCode execute(xAOD::TauJet &tau) const override
Execute - called for each tau candidate.
FourMomDecorators m_neutralPionP4
Gaudi::Property< std::string > m_tauTrackContainerName
std::vector< SG::WriteDecorHandleKey< xAOD::TauJetContainer > > m_decorKeys
StatusCode readModel(const std::string &path)
Configure the data loader and build m_outputs from the metadata and node shapes of the model.
Gaudi::Property< float > m_minTauPt
SG::Accessor< float > m_decayMode
void decorateVertex(xAOD::TauJet &tau, std::span< const float > scores, const std::vector< const xAOD::Vertex * > &vertices) const
Decorate the position of the vertex that scores picks out of vertices, which must be the list the inp...
SG::Accessor< float > m_eleRejScore
SG::Accessor< float > m_tauIDScore
The decorators.
SG::Accessor< float > m_tauCharge
ToolHandle< AthInfer::IAthInferenceTool > m_inferenceTool
TausRUsEvaluator(const std::string &name="TausRUsEvaluator")
FourMomDecorators m_tauP4
void setDefaults(xAOD::TauJet &tau) const
std::vector< SG::Accessor< float > > m_vertexPosition
std::vector< std::string > trackDecorationNames() const
std::vector< std::string > tauDecorationNames() const
Every decoration written on the tau, and every one written on its tracks, which is what the output da...
std::vector< Output > m_outputs
The model's output nodes, from its metadata.
std::vector< SG::Accessor< float > > m_decayModeScores
SG::ReadHandleKey< xAOD::VertexContainer > m_vertexInputContainer
SG::Decorator< int > m_trackClass
static constexpr int DEFAULT_CLASS
std::vector< SG::Decorator< float > > m_trackScores
size_t m_ptMedianQuantile
The pt heads ('tes', 'charged_pion_pt', 'neutral_pion_pt') regress quantiles of the log response,...
Definition node.h:24
std::string base
Definition hcg.cxx:83
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
std::map< std::string, InferenceData > OutputDataMap
std::map< std::string, InferenceData > InputDataMap
constexpr double tauMassInMeV
the mass of the tau (in MeV)
constexpr double piZeroMassInMeV
the mass of the pi zero (in MeV)
constexpr double chargedPionMassInMeV
the mass of the charged pion (in MeV)
SG::Decorator< T, ALLOC > Decorator
Helper class to provide type-safe access to aux data, specialized for JaggedVecElt.
Definition AuxElement.h:576
Vertex_v1 Vertex
Define the latest version of the vertex class.
TauTrack_v1 TauTrack
Definition of the current version.
Definition TauTrack.h:16
TauJet_v3 TauJet
Definition of the current "tau version".
Definition TauJet.h:17