30 using parent_mask_t =
unsigned long long;
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) +
"]");
42 mask |= (parent_mask_t{1} <<
match.parent_index);
49 join(
const std::vector<std::string>& v, std::string_view sep =
", "){
51 if (
v.empty())
return out;
53 for (
const auto& s: v) {
57 out.append(
v.front());
58 for (std::size_t pos = 1;
pos <
v.size(); ++
pos) {
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));
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()));
78 if (
output.empty())
return "none";
79 return "[" +
join(output) +
"]";
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();
92 "n_targets: " << targets->size() <<
", "
93 "n_parents: " << truth->size() <<
", "
94 "n_cascade_candidates: " << n_cascade_candidates <<
97 void logIPMap(MsgStream&
msg,
const IPMap& ipmap,
const MSG::Level level = MSG::VERBOSE)
99 if (
msg.level() > level)
return;
100 for (
const auto& [barcode, children]: ipmap) {
111 std::map<int, std::set<int>> findAllDescendants(
int parent,
const Barcodex& barcodex, std::set<int> history = {})
113 using return_t = std::map<int,std::set<int>>;
114 auto itr = barcodex.find(parent);
115 if (itr == barcodex.end()) {
117 throw std::runtime_error(
118 "can't find barcode " + std::to_string(parent) +
" history:"
119 " {" +
join(hist) +
"}");
121 const std::set<int>&
children = itr->second;
123 if (!history.insert(parent).second) {
125 throw std::runtime_error(
"found cycle, tried to add " + std::to_string(parent) +
" to {" +
join(hist) +
"}");
127 return_t all_children;
128 for (
int child: children) {
132 for (
auto& [dec, dh]: findAllDescendants(child, barcodex, history)) {
133 all_children[dec].merge(dh);
143 if (barkids.size() > 1) {
144 std::set<int> pdg_ids;
145 TLorentzVector sum_p4;
148 sum_p4 += dupkid->p4();
149 pdg_ids.insert(dupkid->pdgId());
150 if (dupkid->nChildren() > child->
nChildren()) child = dupkid;
152 if (pdg_ids.size() != 1) {
153 throw std::runtime_error(
"same barcode, different pdgid: [" +
join(
stringify(pdg_ids)) +
"]");
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()) +
"}"
165 for (
unsigned int parent_n = 0; parent_n <
p->nParents(); parent_n++) {
167 if (!parent)
throw std::runtime_error(
"broken truth record");
168 if (
parent->pdgId() ==
p->pdgId())
return false;
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) +
"]");
184 return (
parent->hasCharm() ||
parent->hasBottom()) &&
p->isChLepton();
188 return parent->hasBottom() &&
p->hasCharm();
196 m_pids(pids.begin(), pids.end()),
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++;
210 m_dec(target) = n_match;
257 std::string pfx = jc +
"." +
m_prefix.value();
297 return StatusCode::SUCCESS;
309 using uc_t =
unsigned char;
325 if (targets->empty())
return StatusCode::SUCCESS;
330 std::vector<const xAOD::TruthParticle*> psort;
332 if (!parentids.contains(p->pdgId()))
continue;
333 if (!isOriginal(p))
continue;
337 std::sort(psort.begin(), psort.end(),[](
const auto* p1,
const auto* p2) {return p1->m() > p2->m();});
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);
349 std::vector<SG::ReadHandle<TPC>> cascades_raw;
351 cascades_raw.emplace_back(key, cxt);
353 logInputs(msgStream(), targets, phandle, cascades_raw);
361 for (
auto& cascade: cascades_raw) {
364 logIPMap(
msg(), ipmap);
366 ATH_MSG_DEBUG(
"merged cascade contains " << barcodex.size() <<
" particles");
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++;
377 for (
auto& [cbar, histbars]: findAllDescendants(
m_uid(*p), barcodex)) {
378 IPMap::mapped_type& barkids = ipmap.at(cbar);
380 std::vector<std::pair<float, const J*>> drs;
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());
393 match.deltaR = drsMinDR;
394 match.parent_index = parent_index;
396 for (
auto& histbar: histbars) {
397 match.cascade_pids.insert(selectChild(ipmap.at(histbar))->pdgId());
400 labeled_targets[drsMinMatch].push_back(std::move(
match));
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;
417 matches.begin(), matches.end(), min_dr);
419 pdgid(*j) = p->pdgId();
421 auto* container =
dynamic_cast<const TPC*
>(p->container());
422 link(*j) =
JL(*container, p->index());
424 nMatched(*j) = matches.size();
425 mask(*j) = matchMask(matches);
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();
437 cascadeCount.decorate(*j, matches);
447 matchChildCount(*j) = 0;
448 matchLink(*j) =
JL();
451 dec_energy(*j) = NAN;
455 cascadeCount.decorateDefault(*j);
465 dec.lock(targets.
get());
468 return StatusCode::SUCCESS;
489 return StatusCode::SUCCESS;
499 auto cascadeWants = [
506 if (
int n_parents = p->nParents(); n_parents == 1) {
507 if (vsl && isSoftLepton(p))
return false;
508 if (vsc && isSoftCharm(p))
return false;
510 if (targid.contains(p->pdgId()))
return true;
511 if (b && p->hasBottom())
return true;
512 if (c && p->hasCharm())
return true;
518 auto insert = [
this, &barcodex, &ipmap](
const auto* p) ->
auto& {
519 ipmap[
m_uid(*p)].insert(p);
520 return barcodex[
m_uid(*p)];
524 if (cascadeWants(p)) {
525 auto& child_set = insert(p);
526 for (
unsigned int child_n = 0; child_n < p->nChildren(); child_n++) {
537 if(ok_missing.contains(p->pdgId())) {
540 auto problem = std::format(
541 "null truth child [barcode={},pdg_id={},child={}of{}]",
543 m_uid(*p), p->pdgId(), child_n, p->nChildren());
545 if (warn_missing.contains(p->pdgId())) {
549 throw std::runtime_error(problem);
552 }
else if (cascadeWants(c)) {
555 child_set.insert(
m_uid(*c));
Scalar deltaR(const MatrixBase< Derived > &vec) const
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
ATLAS-specific HepMC functions.
static unsigned int totalSize(const MultiDimArray< T, N > &ht)
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
std::vector< int > m_pids
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
const std::string barcode
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=',')
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
unsigned int parent_index