ATLAS Offline Software
Loading...
Searching...
No Matches
TruthParentDecoratorAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
5
8
9#include <format>
10#include <limits>
11#include <set>
12#include <stdexcept>
13
14// structure to hold info on a matched parent particle
16{
17 const xAOD::TruthParticle* parent = nullptr;
18 const xAOD::TruthParticle* child = nullptr;
19 float deltaR = 0;
20 unsigned int parent_index = 0;
21 std::set<int> cascade_pids;
22};
23
24namespace {
25
26 using Barcodex = TruthParentDecoratorAlg::Barcodex;
30 using parent_mask_t = unsigned long long;
31
32 parent_mask_t matchMask(const std::vector<MatchedParent>& matches) {
33 parent_mask_t mask = 0x0;
34 for (const auto& match: matches) {
35 constexpr size_t max_idx = std::numeric_limits<decltype(mask)>::digits;
36 if (match.parent_index >= max_idx) {
37 throw std::runtime_error(
38 "parent index overflowed the match mask "
39 "[index: " + std::to_string(match.parent_index) +
40 " , max_mask: " + std::to_string(max_idx) + "]");
41 }
42 mask |= (parent_mask_t{1} << match.parent_index);
43 }
44 return mask;
45 }
46
47 // debugging functions
48 std::string
49 join(const std::vector<std::string>& v, std::string_view sep = ", "){
50 std::string out;
51 if (v.empty()) return out;
52 auto totalSize = (v.size() - 1) * sep.size();
53 for (const auto& s: v) {
54 totalSize += s.size();
55 }
56 out.reserve(totalSize);
57 out.append(v.front());
58 for (std::size_t pos = 1; pos < v.size(); ++pos) {
59 out.append(sep);
60 out.append(v[pos]);
61 }
62 return out;
63 }
64 //
65 template <typename T>
66 std::vector<std::string> stringify(const T& container) {
67 std::vector<std::string> out;
68 for (const auto& v: container) out.push_back(std::to_string(v));
69 return out;
70 }
71 // debugging function, not used now
72 [[maybe_unused]]
73 std::string listAllChildren(const xAOD::TruthParticle* p) {
74 std::vector<std::string> output;
75 for (unsigned int child_n = 0; child_n < p->nChildren(); child_n++) {
76 output.push_back(std::to_string(p->child(child_n)->pdgId()));
77 }
78 if (output.empty()) return "none";
79 return "[" + join(output) + "]";
80 }
81
82
83 // debugging functions using MsgStream
84 void logInputs(MsgStream& msg,SG::ReadHandle<JC>& targets,SG::ReadHandle<TPC>& truth,std::vector<SG::ReadHandle<TPC>>& cascades_raw,const MSG::Level level = MSG::DEBUG)
85 {
86 if (msg.level() > level) return;
87 unsigned int n_cascade_candidates = 0;
88 for (auto& cascade: cascades_raw) {
89 n_cascade_candidates += cascade->size();
90 }
91 msg << level <<
92 "n_targets: " << targets->size() << ", "
93 "n_parents: " << truth->size() << ", "
94 "n_cascade_candidates: " << n_cascade_candidates <<
95 endmsg;
96 }
97 void logIPMap(MsgStream& msg,const IPMap& ipmap,const MSG::Level level = MSG::VERBOSE)
98 {
99 if (msg.level() > level) return;
100 for (const auto& [barcode, children]: ipmap) {
101 std::set<int> ids;
102 for (const xAOD::TruthParticle* dup: children) ids.insert(dup->pdgId());
103 msg << level << "barcode: " << barcode << ", n_dup: " << children.size()
104 << " idg_ids: [" << join(stringify(ids)) << "]" << endmsg;
105 }
106 }
107
108
109 // functions to traverse barcodex and disambiguate the selected
110 // child
111 std::map<int, std::set<int>> findAllDescendants(int parent, const Barcodex& barcodex, std::set<int> history = {})
112 {
113 using return_t = std::map<int,std::set<int>>;
114 auto itr = barcodex.find(parent);
115 if (itr == barcodex.end()) {
116 auto hist = stringify(history);
117 throw std::runtime_error(
118 "can't find barcode " + std::to_string(parent) + " history:"
119 " {" + join(hist) + "}");
120 }
121 const std::set<int>& children = itr->second;
122 if (children.empty()) return return_t{{parent, history}};
123 if (!history.insert(parent).second) {
124 auto hist = stringify(history);
125 throw std::runtime_error("found cycle, tried to add " + std::to_string(parent) + " to {" + join(hist) + "}");
126 }
127 return_t all_children;
128 for (int child: children) {
129 // We don't just merge the children here in case there are
130 // multiple histories that lead to the same discendent. Instead
131 // we merge the histories of all descendents.
132 for (auto& [dec, dh]: findAllDescendants(child, barcodex, history)) {
133 all_children[dec].merge(dh);
134 }
135 }
136 return all_children;
137 }
138
139
140 const xAOD::TruthParticle* selectChild(const IPMap::mapped_type& barkids)
141 {
142 const xAOD::TruthParticle* child = *barkids.begin();
143 if (barkids.size() > 1) {
144 std::set<int> pdg_ids;
145 TLorentzVector sum_p4;
146 // look at duplicates, take the one with the most children
147 for (const xAOD::TruthParticle* dupkid: barkids) {
148 sum_p4 += dupkid->p4();
149 pdg_ids.insert(dupkid->pdgId());
150 if (dupkid->nChildren() > child->nChildren()) child = dupkid;
151 }
152 if (pdg_ids.size() != 1) {
153 throw std::runtime_error("same barcode, different pdgid: [" + join(stringify(pdg_ids)) + "]");
154 }
155 if (float dr = child->p4().DeltaR(sum_p4); dr > 0.001) {
156 throw std::runtime_error( "Same barcode, different vector: { deltaR: " + std::to_string(dr) + ", pdgid: " + std::to_string(child->pdgId()) + "}"
157 );
158 }
159 }
160 return child;
161 }
162
163
164 bool isOriginal(const xAOD::TruthParticle* p) {
165 for (unsigned int parent_n = 0; parent_n < p->nParents(); parent_n++) {
166 const xAOD::TruthParticle* parent = p->parent(parent_n);
167 if (!parent) throw std::runtime_error("broken truth record");
168 if (parent->pdgId() == p->pdgId()) return false;
169 }
170 return true;
171 }
172
173
174 // these functions are for dealing with specific vertices
175
176 const xAOD::TruthParticle* getParent(const xAOD::TruthParticle* p) {
177 if (int n_parents = p->nParents(); n_parents != 1) {
178 throw std::logic_error("can't get parent [n_parents: " + std::to_string(n_parents) + "]");
179 }
180 return p->parent(0);
181 }
182 bool isSoftLepton(const xAOD::TruthParticle* p) {
183 const xAOD::TruthParticle* parent = getParent(p);
184 return (parent->hasCharm() || parent->hasBottom()) && p->isChLepton();
185 }
186 bool isSoftCharm(const xAOD::TruthParticle* p) {
187 const xAOD::TruthParticle* parent = getParent(p);
188 return parent->hasBottom() && p->hasCharm();
189 }
190
191}
192
193
194// cascade count decorator
195CascadeCountDecorator::CascadeCountDecorator( const std::string& name, const std::vector<int>& pids):
196 m_pids(pids.begin(), pids.end()),
197 m_dec(name),
198 // also hang on to the auxid so we can lock it later
199 m_auxid(SG::AuxTypeRegistry::instance().findAuxID(name))
200{
201}
202void CascadeCountDecorator::decorate(const SG::AuxElement& target,const std::vector<MatchedParent>& parents) const
203{
204 unsigned char n_match = 0;
205 for (const auto& parent: parents) {
206 for (const auto& pid: m_pids) {
207 if (parent.cascade_pids.contains(pid)) n_match++;
208 }
209 }
210 m_dec(target) = n_match;
211}
213{
214 m_dec(target) = 0;
215}
217{
218 // We're locking a decoration on a const container, which would be a
219 // problem if they were made by anyone else since someone else might
220 // be messing with them. But here it shouldn't be a problem since we
221 // made them.
222 auto* cont ATLAS_THREAD_SAFE = const_cast<xAOD::IParticleContainer*>(target);
223 cont->lockDecoration(m_auxid);
224}
225
226
227// main algorithm
228TruthParentDecoratorAlg::TruthParentDecoratorAlg(const std::string& name, ISvcLocator* loc):
229 AthReentrantAlgorithm(name, loc)
230{
231 // these aren't user configurable
232 declare(m_target_pdgid_key);
233 declare(m_target_dr_truth_key);
234 declare(m_target_link_key);
235 declare(m_target_index_key);
236 declare(m_target_n_matched_key);
238 declare(m_match_pdgid_key);
239 declare(m_match_children_key);
240 declare(m_match_link_key);
241 declare(m_target_mass_key);
242 declare(m_target_pt_key);
243 declare(m_target_energy_key);
244 declare(m_target_eta_key);
245 declare(m_target_phi_key);
246}
247
249 // initialize inputs
250 ATH_CHECK(m_parents_key.initialize());
251 ATH_CHECK(m_target_container_key.initialize());
252 ATH_CHECK(m_cascades_key.initialize());
253 // weird hack because Gaudi can't handle an infinite default
254 if (m_match_delta_r.value() <= 0) m_match_delta_r.value() = INFINITY;
255 // initialize outputs
256 std::string jc = m_target_container_key.key();
257 std::string pfx = jc + "." + m_prefix.value();
258 m_target_pdgid_key = pfx + "PdgId";
259 m_target_dr_truth_key = pfx + "DRTruthParticle";
260 m_target_link_key = pfx + "Link";
261 m_target_index_key = pfx + "Index";
262 m_target_n_matched_key = pfx + "NMatchedChildren";
263 m_target_match_mask_key = pfx + "ParentsMask";
264 m_match_pdgid_key = pfx + "MatchingParticlePdgId";
265 m_match_children_key = pfx + "MatchingParticleNChildren";
266 m_match_link_key = pfx + "MatchingParticleLink";
267 m_target_mass_key = pfx + "Mass";
268 m_target_pt_key = pfx + "PT";
269 m_target_energy_key = pfx + "Energy";
270 m_target_eta_key = pfx + "Eta";
271 m_target_phi_key = pfx + "Phi";
272 ATH_CHECK(m_target_pdgid_key.initialize());
273 ATH_CHECK(m_target_dr_truth_key.initialize());
274 ATH_CHECK(m_target_link_key.initialize());
275 ATH_CHECK(m_target_index_key.initialize());
276 ATH_CHECK(m_target_n_matched_key.initialize());
278 ATH_CHECK(m_match_pdgid_key.initialize());
279 ATH_CHECK(m_match_children_key.initialize());
280 ATH_CHECK(m_match_link_key.initialize());
281 ATH_CHECK(m_target_mass_key.initialize());
282 ATH_CHECK(m_target_pt_key.initialize());
283 ATH_CHECK(m_target_energy_key.initialize());
284 ATH_CHECK(m_target_eta_key.initialize());
285 ATH_CHECK(m_target_phi_key.initialize());
286
287 for (auto& [key, pids]: m_counts_matching_cascade) {
288 m_cascade_count_writer_keys.emplace_back(jc + "." + key);
289 m_cascade_count_decorators.emplace_back(key, pids);
290 }
291 for (auto& key: m_cascade_count_writer_keys) declare(key);
293
294 // ATLASRECTS-8290: this should be removed eventually
296
297 return StatusCode::SUCCESS;
298}
299
300StatusCode TruthParentDecoratorAlg::execute(const EventContext& cxt) const
301{
302 ATH_MSG_DEBUG("Executing");
303
305 // part 1: read in the particles
308
309 using uc_t = unsigned char;
324
325 if (targets->empty()) return StatusCode::SUCCESS;
326
327 // read in and sort the parent collection
329 std::set<int> parentids(m_parent_pdgids.begin(), m_parent_pdgids.end());
330 std::vector<const xAOD::TruthParticle*> psort;
331 for (const xAOD::TruthParticle* p: *phandle) {
332 if (!parentids.contains(p->pdgId())) continue;
333 if (!isOriginal(p)) continue;
334 psort.push_back(p);
335 }
336 // for lack of a better idea, store truth parents sorted by mass
337 std::sort(psort.begin(), psort.end(),[](const auto* p1, const auto* p2) {return p1->m() > p2->m();});
338 // check to make sure we don't overflow the match mask
339 constexpr size_t max_idx = std::numeric_limits<parent_mask_t>::digits;
340 if (psort.size() > max_idx) {
342 "Found too many parent particles to store in parent match mask "
343 "truncating the parent collection [max: " << max_idx << ", "
344 "n: " << psort.size() << "]");
345 psort.resize(max_idx);
346 }
347
348
349 std::vector<SG::ReadHandle<TPC>> cascades_raw;
350 for (const auto& key: m_cascades_key) {
351 cascades_raw.emplace_back(key, cxt);
352 }
353 logInputs(msgStream(), targets, phandle, cascades_raw);
354
356 // part 2: build the barcodex
358 Barcodex barcodex;
359 IPMap ipmap;
360 addTruthContainer(barcodex, ipmap, *phandle);
361 for (auto& cascade: cascades_raw) {
362 addTruthContainer(barcodex, ipmap, *cascade);
363 }
364 logIPMap(msg(), ipmap);
365
366 ATH_MSG_DEBUG("merged cascade contains " << barcodex.size() << " particles");
367
369 // Part 3: build map from targets to parents
371 std::unordered_map<const J*, std::vector<MatchedParent>> labeled_targets;
372 unsigned int n_parents = 0;
373 for (const auto* p: psort) {
374 unsigned int parent_index = n_parents++;
375 // ATLASRECTS-8290: this should be replaced with ->uid()
376 ATH_MSG_VERBOSE("pdgid: " << p->pdgId() << ", barcode: " << m_uid(*p));
377 for (auto& [cbar, histbars]: findAllDescendants(m_uid(*p), barcodex)) {
378 IPMap::mapped_type& barkids = ipmap.at(cbar);
379 const xAOD::TruthParticle* child = selectChild(barkids);
380 std::vector<std::pair<float, const J*>> drs;
381 float drsMinDR=9999;
382 const J* drsMinMatch = 0;
383 for (const auto* j: *targets) {
384 if(j->p4().DeltaR(child->p4()) < drsMinDR) {
385 drsMinDR=j->p4().DeltaR(child->p4());
386 drsMinMatch=j;
387 }
388 }
389 if(drsMinMatch){
391 match.parent = p;
392 match.child = child;
393 match.deltaR = drsMinDR;
394 match.parent_index = parent_index;
395 match.cascade_pids.insert(child->pdgId());
396 for (auto& histbar: histbars) {
397 match.cascade_pids.insert(selectChild(ipmap.at(histbar))->pdgId());
398 }
399 if (match.deltaR < m_match_delta_r) {
400 labeled_targets[drsMinMatch].push_back(std::move(match));
401 }
402 }
403 }
404 }
405
407 // Part 4: decorate!
409
410 for (const J* j: *targets) {
411 if (labeled_targets.contains(j)) {
412 const std::vector<MatchedParent>& matches = labeled_targets.at(j);
413 auto min_dr = [](auto& p1, auto& p2) {
414 return p1.deltaR < p2.deltaR;
415 };
416 const MatchedParent& nearest = *std::min_element(
417 matches.begin(), matches.end(), min_dr);
418 const xAOD::TruthParticle* p = nearest.parent;
419 pdgid(*j) = p->pdgId();
420 deltaR(*j) = nearest.deltaR;
421 auto* container = dynamic_cast<const TPC*>(p->container());
422 link(*j) = JL(*container, p->index());
423 index(*j) = nearest.parent_index;
424 nMatched(*j) = matches.size();
425 mask(*j) = matchMask(matches);
426 const xAOD::TruthParticle* child = nearest.child;
427 matchPdgId(*j) = child->pdgId();
428 matchChildCount(*j) = child->nChildren();
429 auto* matchedContainer = dynamic_cast<const TPC*>(child->container());
430 matchLink(*j) = JL(*matchedContainer, child->index());
431 dec_mass(*j) = p->m();
432 dec_pt(*j) = p->pt();
433 dec_energy(*j) = p->e();
434 dec_eta(*j) = p->eta();
435 dec_phi(*j) = p->phi();
436 for (const auto& cascadeCount: m_cascade_count_decorators) {
437 cascadeCount.decorate(*j, matches);
438 }
439 } else {
440 pdgid(*j) = 0;
441 deltaR(*j) = NAN;
442 link(*j) = JL();
443 index(*j) = -1;
444 nMatched(*j) = -1;
445 mask(*j) = 0x0;
446 matchPdgId(*j) = 0;
447 matchChildCount(*j) = 0;
448 matchLink(*j) = JL();
449 dec_mass(*j) = NAN;
450 dec_pt(*j) = NAN;
451 dec_energy(*j) = NAN;
452 dec_eta(*j) = NAN;
453 dec_phi(*j) = NAN;
454 for (const auto& cascadeCount: m_cascade_count_decorators) {
455 cascadeCount.decorateDefault(*j);
456 }
457 }
458 }
459
461 // Part 5: lock decorations
463
464 for (const auto& dec: m_cascade_count_decorators) {
465 dec.lock(targets.get());
466 }
467
468 return StatusCode::SUCCESS;
469}
470
472 if (!m_allow_missing_children_pdgids.empty()) {
473 float missing_fraction = double(m_missing_n_ignored) / m_total_children;
474 auto msg = std::format("ignored {} missing children out of {} ({:.2}%)",m_missing_n_ignored.load(),m_total_children.load(),missing_fraction * 100);
475 if (missing_fraction > m_missing_children_fraction_warning_threshold) {
477 } else {
479 }
480 }
481 if (!m_warn_missing_children_pdgids.empty()) {
482 auto msg = std::format("warned of {} missing children out of {}", m_missing_n_warned.load(), m_total_children.load());
483 if (m_missing_n_warned > 0) {
485 } else {
487 }
488 }
489 return StatusCode::SUCCESS;
490}
491
493
494 std::set<int> targid(m_cascade_pdgids.begin(), m_cascade_pdgids.end());
495 // we allow decays through any of the parent pdgids
496 targid.insert(m_parent_pdgids.begin(), m_parent_pdgids.end());
497
498 // this determines if a cascade vertex should be saved or not
499 auto cascadeWants = [
500 &targid,
501 &b=m_add_b,
502 &c=m_add_c,
505 ] (const xAOD::TruthParticle* p) {
506 if (int n_parents = p->nParents(); n_parents == 1) {
507 if (vsl && isSoftLepton(p)) return false;
508 if (vsc && isSoftCharm(p)) return false;
509 }
510 if (targid.contains(p->pdgId())) return true;
511 if (b && p->hasBottom()) return true;
512 if (c && p->hasCharm()) return true;
513 return false;
514 };
515
516 // insert a particle into the record, return the child set
517 // ATLASRECTS-8290: this should be replaced with ->uid()
518 auto insert = [this, &barcodex, &ipmap](const auto* p) -> auto& {
519 ipmap[m_uid(*p)].insert(p);
520 return barcodex[m_uid(*p)];
521 };
522
523 for (const xAOD::TruthParticle* p: container) {
524 if (cascadeWants(p)) {
525 auto& child_set = insert(p);
526 for (unsigned int child_n = 0; child_n < p->nChildren(); child_n++) {
527 const xAOD::TruthParticle* c = p->child(child_n);
528
529 // Unfortunately the truth record is often broken in various
530 // ways. There are a few configurable ways to deal with
531 // this. We keep track of the total number and how often we
532 // use these workarounds to make sure things don't go too
533 // crazy.
535 const auto& ok_missing = m_allow_missing_children_pdgids.value();
536 if (!c) {
537 if(ok_missing.contains(p->pdgId())) {
539 } else {
540 auto problem = std::format(
541 "null truth child [barcode={},pdg_id={},child={}of{}]",
542 // ATLASRECTS-8290: m_uid should be replaced with ->uid()
543 m_uid(*p), p->pdgId(), child_n, p->nChildren());
544 const auto& warn_missing = m_warn_missing_children_pdgids.value();
545 if (warn_missing.contains(p->pdgId())) {
547 ATH_MSG_WARNING(problem);
548 } else {
549 throw std::runtime_error(problem);
550 }
551 }
552 } else if (cascadeWants(c)) {
553 insert(c);
554 // ATLASRECTS-8290: m_uid should be replaced with ->uid()
555 child_set.insert(m_uid(*c));
556 }
557 };
558 }
559 }
560}
Scalar deltaR(const MatrixBase< Derived > &vec) const
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
ATLAS-specific HepMC functions.
static unsigned int totalSize(const MultiDimArray< T, N > &ht)
std::map< std::string, double > instance
Handle class for adding a decoration to an object.
std::string stringify(T obj)
#define ATLAS_THREAD_SAFE
An algorithm that can be simultaneously executed in multiple threads.
void decorate(const SG::AuxElement &target, const std::vector< MatchedParent > &parents) const
SG::AuxElement::Decorator< unsigned char > m_dec
CascadeCountDecorator(const std::string &name, const std::vector< int > &pids)
void decorateDefault(const SG::AuxElement &target) const
void lock(const xAOD::IParticleContainer *target) const
Helper class to provide constant type-safe access to aux data.
const_pointer_type get() const
Dereference the pointer, but don't cache anything.
Handle class for adding a decoration to an object.
SG::WriteDecorHandleKeyArray< JC > m_cascade_count_writer_keys
SG::WriteDecorHandleKey< JC > m_target_phi_key
void addTruthContainer(Barcodex &, IPMap &, const TPC &) const
Gaudi::Property< std::unordered_set< int > > m_allow_missing_children_pdgids
Gaudi::Property< bool > m_add_c
Gaudi::Property< bool > m_veto_soft_lepton
Gaudi::Property< bool > m_add_b
SG::WriteDecorHandleKey< JC > m_target_index_key
SG::WriteDecorHandleKey< JC > m_target_eta_key
TruthParentDecoratorAlg(const std::string &name, ISvcLocator *loc)
std::vector< CascadeCountDecorator > m_cascade_count_decorators
SG::WriteDecorHandleKey< JC > m_target_n_matched_key
std::atomic< unsigned long long > m_total_children
Gaudi::Property< bool > m_use_barcode
SG::WriteDecorHandleKey< JC > m_match_children_key
Gaudi::Property< cascade_counter_property_t > m_counts_matching_cascade
SG::WriteDecorHandleKey< JC > m_target_pdgid_key
Gaudi::Property< std::vector< int > > m_cascade_pdgids
SG::WriteDecorHandleKey< JC > m_target_energy_key
Gaudi::Property< std::string > m_prefix
std::map< int, std::set< const xAOD::TruthParticle * > > IPMap
Gaudi::Property< bool > m_veto_soft_charm
SG::ReadHandleKeyArray< TPC > m_cascades_key
SG::WriteDecorHandleKey< JC > m_match_link_key
virtual StatusCode initialize() override
SG::WriteDecorHandleKey< JC > m_target_pt_key
Gaudi::Property< std::vector< int > > m_parent_pdgids
Gaudi::Property< float > m_missing_children_fraction_warning_threshold
SG::WriteDecorHandleKey< JC > m_target_match_mask_key
virtual StatusCode execute(const EventContext &) const override
std::map< int, std::set< int > > Barcodex
SG::WriteDecorHandleKey< JC > m_target_link_key
SG::WriteDecorHandleKey< JC > m_target_dr_truth_key
SG::ReadHandleKey< JC > m_target_container_key
SG::ConstAccessor< int > m_uid
std::atomic< unsigned long long > m_missing_n_warned
SG::WriteDecorHandleKey< JC > m_match_pdgid_key
xAOD::IParticleContainer JC
SG::ReadHandleKey< TPC > m_parents_key
virtual StatusCode finalize() override
Gaudi::Property< std::unordered_set< int > > m_warn_missing_children_pdgids
Gaudi::Property< float > m_match_delta_r
xAOD::TruthParticleContainer TPC
std::atomic< unsigned long long > m_missing_n_ignored
SG::WriteDecorHandleKey< JC > m_target_mass_key
int pdgId() const
PDG ID code.
size_t nChildren() const
Number of children of this particle.
virtual FourMom_t p4() const override final
The full 4-momentum of the particle.
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:359
const std::string barcode
Forward declaration.
AuxElement(SG::AuxVectorData *container, size_t index)
Base class for elements of a container that can have aux data.
std::string join(const std::vector< std::string > &v, const char c=',')
Definition index.py:1
output
Definition merge.py:16
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
TruthParticle_v1 TruthParticle
Typedef to implementation.
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.
const xAOD::TruthParticle * parent
std::set< int > cascade_pids
const xAOD::TruthParticle * child
MsgStream & msg
Definition testRead.cxx:32