diff --git a/PhysicsTools/TruthInfo/interface/LogicalGraphHitIndexBuilder.h b/PhysicsTools/TruthInfo/interface/LogicalGraphHitIndexBuilder.h index 41bbdc2f30b19..06989135dd91b 100644 --- a/PhysicsTools/TruthInfo/interface/LogicalGraphHitIndexBuilder.h +++ b/PhysicsTools/TruthInfo/interface/LogicalGraphHitIndexBuilder.h @@ -18,18 +18,27 @@ namespace truth { public: explicit LogicalGraphHitIndexBuilder(uint32_t nParticles); - void setSimTrackForParticle(uint32_t particleId, uint32_t trackId); + // trackId is event-local (each mixing sub-event reuses 1,2,3,...); it MUST be + // namespaced by the packed EncodedEventId or signal and pileup collide. + void setSimTrackForParticle(uint32_t particleId, uint64_t eventId, uint32_t trackId); void addParticleChild(uint32_t parentParticleId, uint32_t childParticleId); // Add a hit on `trackId`'s SimTrack to `channel`. recHitIndex defaults to "no // recHit" for channels without a DetId->RecHit link (tracker, muon); calo/MTD // pass the mapped global recHit index. void addHit(HitChannel channel, + uint64_t eventId, uint32_t trackId, uint32_t detId, float energy, uint32_t recHitIndex = LogicalGraphHitIndex::Hit::kInvalidRecHitIndex); + // (EncodedEventId, trackId) -> global map key. The packed EncodedEventId fits in + // 32 bits (reco::EncodedEventId::rawId is uint32), so shift it into the high word. + static uint64_t simKey(uint64_t eventId, uint32_t trackId) { + return (eventId << 32) | static_cast(trackId); + } + [[nodiscard]] LogicalGraphHitIndex finish(); private: @@ -72,7 +81,7 @@ namespace truth { uint32_t nParticles_ = 0; - std::unordered_map trackIdToParticle_; + std::unordered_map trackIdToParticle_; std::vector> children_; // [channel index][particle] -> direct hit list. Subgraph hits are aggregated diff --git a/PhysicsTools/TruthInfo/plugins/BuildFile.xml b/PhysicsTools/TruthInfo/plugins/BuildFile.xml index bfac713ab153e..a13d114e56c5f 100644 --- a/PhysicsTools/TruthInfo/plugins/BuildFile.xml +++ b/PhysicsTools/TruthInfo/plugins/BuildFile.xml @@ -34,7 +34,6 @@ - diff --git a/PhysicsTools/TruthInfo/plugins/LogicalGraphHitIndexProducer.cc b/PhysicsTools/TruthInfo/plugins/LogicalGraphHitIndexProducer.cc index 9bc8e4bf7ae2d..45e5a2c4fb1a2 100644 --- a/PhysicsTools/TruthInfo/plugins/LogicalGraphHitIndexProducer.cc +++ b/PhysicsTools/TruthInfo/plugins/LogicalGraphHitIndexProducer.cc @@ -336,7 +336,7 @@ void TruthLogicalGraphHitIndexProducer::fillTrackToParticleMap(LogicalGraphView if (trackId == 0) continue; - builder.setSimTrackForParticle(particleId, trackId); + builder.setSimTrackForParticle(particleId, rawGraph.nodeEventId(simNodeU32), trackId); } for (uint32_t parentId = 0; parentId < graph.nParticles(); ++parentId) { @@ -487,8 +487,12 @@ void TruthLogicalGraphHitIndexProducer::fillSimHits(edm::Event& event, } } - builder.addHit( - truth::HitChannel::HGCalCalo, static_cast(geantTrackId), detId, simHit.energy(), recHitIndex); + builder.addHit(truth::HitChannel::HGCalCalo, + simHit.eventId().rawId(), + static_cast(geantTrackId), + detId, + simHit.energy(), + recHitIndex); } } } @@ -508,7 +512,11 @@ void TruthLogicalGraphHitIndexProducer::fillTrackerSimHits(edm::Event& event, for (auto const& simHit : *hSimHits) { // PSimHit::trackId() is the G4 trackId of the SimTrack that made the hit, // the same id space used to associate calorimeter simhits to particles. - builder.addHit(truth::HitChannel::Tracker, simHit.trackId(), simHit.detUnitId(), simHit.energyLoss()); + builder.addHit(truth::HitChannel::Tracker, + simHit.eventId().rawId(), + simHit.trackId(), + simHit.detUnitId(), + simHit.energyLoss()); } } } @@ -524,7 +532,8 @@ void TruthLogicalGraphHitIndexProducer::fillMuonSimHits(edm::Event& event, continue; for (auto const& simHit : *hSimHits) { - builder.addHit(truth::HitChannel::Muon, simHit.trackId(), simHit.detUnitId(), simHit.energyLoss()); + builder.addHit( + truth::HitChannel::Muon, simHit.eventId().rawId(), simHit.trackId(), simHit.detUnitId(), simHit.energyLoss()); } } } @@ -578,7 +587,11 @@ void TruthLogicalGraphHitIndexProducer::fillMtdHits(edm::Event& event, const auto trackId = static_cast(cluster.particleId()); for (auto const& [packedHit, energy] : cluster.hits_and_energies()) { const uint32_t moduleDetId = static_cast(packedHit >> 32); - builder.addHit(truth::HitChannel::MTD, trackId, moduleDetId, energy, recHitIndex); + // eventId 0 is correct only because the clusters above are filtered to the signal + // interaction (bx 0, event 0), so simKey(0, trackId) matches the signal track. + // If a merged pileup MTD collection is ever added, this must pass the real + // eventId like the calo/tracker channels or pileup tracks would alias the signal. + builder.addHit(truth::HitChannel::MTD, 0ull, trackId, moduleDetId, energy, recHitIndex); } } } diff --git a/PhysicsTools/TruthInfo/plugins/TruthGraphAccumulator.cc b/PhysicsTools/TruthInfo/plugins/TruthGraphAccumulator.cc index 7d76d54c61f80..1bf5afc0fa387 100644 --- a/PhysicsTools/TruthInfo/plugins/TruthGraphAccumulator.cc +++ b/PhysicsTools/TruthInfo/plugins/TruthGraphAccumulator.cc @@ -40,9 +40,12 @@ #include "FWCore/Framework/interface/ProducesCollector.h" #include "FWCore/ParameterSet/interface/ParameterSet.h" #include "FWCore/Utilities/interface/Exception.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" #include "FWCore/Utilities/interface/InputTag.h" #include "FWCore/Utilities/interface/StreamID.h" +#include "SimDataFormats/CaloHit/interface/PCaloHit.h" +#include "SimDataFormats/TrackingHit/interface/PSimHit.h" #include "SimGeneral/MixingModule/interface/DigiAccumulatorMixMod.h" #include "SimGeneral/MixingModule/interface/DigiAccumulatorMixModFactory.h" #include "SimGeneral/MixingModule/interface/PileUpEventPrincipal.h" @@ -62,9 +65,10 @@ namespace { uint64_t packEventId(EncodedEventId const& id) { - uint64_t out = 0; - std::memcpy(&out, &id, sizeof(EncodedEventId)); - return out; + // EncodedEventId is a single uint32 rawId; use the typed accessor rather than a + // byte copy so the key stays portable and cannot pick up a future member/padding. + static_assert(sizeof(EncodedEventId) == sizeof(uint32_t)); + return static_cast(id.rawId()); } // Stable (status 1) GEN particles as (barcode, pdgId). Used to collapse the GEN @@ -124,15 +128,49 @@ class TruthGraphAccumulator : public DigiAccumulatorMixMod { edm::SimVertexContainer const& vertices, EncodedEventId const& eid); + // Append this sub-event's sim-hits to the merged collections, re-tagged with `eid` + // so they carry per-interaction provenance (native hits are all tagged (0,0)). + template + void addSubEventHits(EvT const& ev, EncodedEventId const& eid); + + // Merge one sim-hit collection family (PCaloHit or PSimHit) from the sub-event, + // re-tagging each hit's eventId. Kept per subdetector family so a downstream + // consumer can apply the right sim-to-reco DetId relabelling per collection. + template + void mergeHits(EvT const& ev, + std::vector const& tags, + EncodedEventId const& eid, + std::vector& out); + const edm::InputTag simTrackTag_; const edm::InputTag simVertexTag_; const edm::InputTag hepmc3Tag_; const edm::InputTag hepmc2Tag_; + const std::vector caloHitTags_; + const std::vector ecalHitTags_; + const std::vector hcalHitTags_; + const std::vector trackerHitTags_; + const std::vector muonHitTags_; + const std::vector mtdHitTags_; const std::vector pileupBunchCrossings_; const bool collapsePileupGen_; const bool collapseSignalGen_; - std::unordered_map pileupCountByBx_; + int pileupCount_ = 0; + bool missingCaloHitsWarned_ = false; + + // Merged calorimeter sim-hits across signal + kept pileup, each re-tagged with its + // sub-event EncodedEventId so the (eventId,trackId) hit-index key resolves pileup + // nodes at RECO (the native pileup hits are consumed transiently here). Kept one + // vector per subdetector family so the relabelling at RECO stays per collection. + std::vector mergedCaloHits_; + std::vector mergedEcalHits_; + std::vector mergedHcalHits_; + // Tracking sim-hits (tracker, muon chambers, MTD) as PSimHit, same per-interaction + // re-tagging. Tracker pileup is by far the largest family; see the customise note. + std::vector mergedTrackerHits_; + std::vector mergedMuonHits_; + std::vector mergedMtdHits_; std::vector nodes_; std::vector pdgId_; @@ -157,18 +195,42 @@ TruthGraphAccumulator::TruthGraphAccumulator(edm::ParameterSet const& cfg, simVertexTag_(cfg.getParameter("simVertices")), hepmc3Tag_(cfg.getParameter("genEventHepMC3")), hepmc2Tag_(cfg.getParameter("genEventHepMC")), + caloHitTags_(cfg.getParameter>("caloHits")), + ecalHitTags_(cfg.getParameter>("ecalHits")), + hcalHitTags_(cfg.getParameter>("hcalHits")), + trackerHitTags_(cfg.getParameter>("trackerHits")), + muonHitTags_(cfg.getParameter>("muonHits")), + mtdHitTags_(cfg.getParameter>("mtdHits")), pileupBunchCrossings_(cfg.getParameter>("pileupBunchCrossings")), collapsePileupGen_(cfg.getParameter("collapsePileupGen")), collapseSignalGen_(cfg.getParameter("collapseSignalGen")) { producesCollector.produces(); + producesCollector.produces>("mergedHGCHits"); + producesCollector.produces>("mergedEcalHits"); + producesCollector.produces>("mergedHcalHits"); + producesCollector.produces>("mergedTrackerHits"); + producesCollector.produces>("mergedMuonHits"); + producesCollector.produces>("mergedMtdHits"); iC.consumes(simTrackTag_); iC.consumes(simVertexTag_); iC.mayConsume(hepmc3Tag_); iC.mayConsume(hepmc2Tag_); + for (auto const* tags : {&caloHitTags_, &ecalHitTags_, &hcalHitTags_}) + for (auto const& tag : *tags) + iC.mayConsume>(tag); + for (auto const* tags : {&trackerHitTags_, &muonHitTags_, &mtdHitTags_}) + for (auto const& tag : *tags) + iC.mayConsume>(tag); } void TruthGraphAccumulator::initializeEvent(edm::Event const&, edm::EventSetup const&) { - pileupCountByBx_.clear(); + pileupCount_ = 0; + mergedCaloHits_.clear(); + mergedEcalHits_.clear(); + mergedHcalHits_.clear(); + mergedTrackerHits_.clear(); + mergedMuonHits_.clear(); + mergedMtdHits_.clear(); nodes_.clear(); pdgId_.clear(); status_.clear(); @@ -270,6 +332,44 @@ void TruthGraphAccumulator::addSubEvent(std::vector> const& } } +template +void TruthGraphAccumulator::mergeHits(EvT const& ev, + std::vector const& tags, + EncodedEventId const& eid, + std::vector& out) { + for (auto const& tag : tags) { + edm::Handle> hits; + ev.getByLabel(tag, hits); + if (!hits.isValid()) { + // Under premixed pileup the pileup sim-hits are already digitized away, so every + // pileup handle is invalid and the merged collection ends up signal-only, silently + // reverting the pileup-aware truth to signal-only. Warn once. + if (!missingCaloHitsWarned_) { + edm::LogWarning("TruthGraphAccumulator") + << "sim-hit collection " << tag.encode() + << " not found for a sub-event; pileup-aware truth needs classic (non-premixed) pileup."; + missingCaloHitsWarned_ = true; + } + continue; + } + out.reserve(out.size() + hits->size()); + for (HitT hit : *hits) { // copy: re-tag the eventId to this sub-event + hit.setEventId(eid); + out.push_back(hit); + } + } +} + +template +void TruthGraphAccumulator::addSubEventHits(EvT const& ev, EncodedEventId const& eid) { + mergeHits(ev, caloHitTags_, eid, mergedCaloHits_); + mergeHits(ev, ecalHitTags_, eid, mergedEcalHits_); + mergeHits(ev, hcalHitTags_, eid, mergedHcalHits_); + mergeHits(ev, trackerHitTags_, eid, mergedTrackerHits_); + mergeHits(ev, muonHitTags_, eid, mergedMuonHits_); + mergeHits(ev, mtdHitTags_, eid, mergedMtdHits_); +} + void TruthGraphAccumulator::accumulate(edm::Event const& event, edm::EventSetup const&) { edm::Handle tracks; edm::Handle vertices; @@ -280,7 +380,9 @@ void TruthGraphAccumulator::accumulate(edm::Event const& event, edm::EventSetup std::vector> stableGen; if (collapseSignalGen_) stableGen = readStableGen(event, hepmc3Tag_, hepmc2Tag_); - addSubEvent(stableGen, *tracks, *vertices, EncodedEventId(0, 0)); + const EncodedEventId sigEid(0, 0); + addSubEvent(stableGen, *tracks, *vertices, sigEid); + addSubEventHits(event, sigEid); } void TruthGraphAccumulator::accumulate(PileUpEventPrincipal const& pep, edm::EventSetup const&, edm::StreamID const&) { @@ -299,8 +401,18 @@ void TruthGraphAccumulator::accumulate(PileUpEventPrincipal const& pep, edm::Eve if (collapsePileupGen_) stableGen = readStableGen(pep, hepmc3Tag_, hepmc2Tag_); - const int puIndex = ++pileupCountByBx_[bx]; - addSubEvent(stableGen, *tracks, *vertices, EncodedEventId(bx, puIndex)); + // Global counter across bunch crossings: EncodedEventId stores abs(bx), so a + // per-bx counter would give (-1,1) and (+1,1) identical packed ids. A single + // counter keeps every pileup interaction's tag unique regardless of bx sign. + const int puIndex = ++pileupCount_; + // EncodedEventId packs the event number into 16 bits; an unrealistic pileup + // multiplicity would overflow into the bunch-crossing bits and alias ids. + if (puIndex > 0xFFFF) + throw cms::Exception("TruthGraphAccumulator") + << "pileup sub-event count " << puIndex << " exceeds the 16-bit EncodedEventId event field"; + const EncodedEventId puEid(bx, puIndex); + addSubEvent(stableGen, *tracks, *vertices, puEid); + addSubEventHits(pep, puEid); } void TruthGraphAccumulator::finalizeEvent(edm::Event& event, edm::EventSetup const&) { @@ -340,6 +452,13 @@ void TruthGraphAccumulator::finalizeEvent(edm::Event& event, edm::EventSetup con throw cms::Exception("TruthGraphAccumulator") << "Produced TruthGraph is not consistent"; event.put(std::move(out)); + + event.put(std::make_unique>(std::move(mergedCaloHits_)), "mergedHGCHits"); + event.put(std::make_unique>(std::move(mergedEcalHits_)), "mergedEcalHits"); + event.put(std::make_unique>(std::move(mergedHcalHits_)), "mergedHcalHits"); + event.put(std::make_unique>(std::move(mergedTrackerHits_)), "mergedTrackerHits"); + event.put(std::make_unique>(std::move(mergedMuonHits_)), "mergedMuonHits"); + event.put(std::make_unique>(std::move(mergedMtdHits_)), "mergedMtdHits"); } DEFINE_DIGI_ACCUMULATOR(TruthGraphAccumulator); diff --git a/PhysicsTools/TruthInfo/plugins/TruthLogicalGraphProducer.cc b/PhysicsTools/TruthInfo/plugins/TruthLogicalGraphProducer.cc index 72b5d6b47b0ec..a906159522f04 100644 --- a/PhysicsTools/TruthInfo/plugins/TruthLogicalGraphProducer.cc +++ b/PhysicsTools/TruthInfo/plugins/TruthLogicalGraphProducer.cc @@ -888,7 +888,12 @@ class TruthLogicalGraphProducer : public edm::stream::EDProducer<> { std::vector particleDirectHit; if (dropHitlessSimSubgraphs_) { - std::unordered_set hitTrackIds; + // trackId is event-local (each mixing sub-event reuses 1,2,3,...), so it MUST + // be namespaced by the packed EncodedEventId or signal and pileup collide and + // the wrong particles get flagged as hit-bearing. Mirrors the same key in + // LogicalGraphHitIndexBuilder so the pruned graph stays consistent with the index. + auto simKey = [](uint64_t eventId, uint32_t trackId) { return (eventId << 32) | static_cast(trackId); }; + std::unordered_set hitKeys; bool anyCollectionValid = false; for (auto const& token : caloSimHitTokens_) { @@ -900,7 +905,7 @@ class TruthLogicalGraphProducer : public edm::stream::EDProducer<> { for (auto const& hit : *hHits) { const int trackId = hit.geantTrackId(); if (trackId > 0 && hit.energy() > 0.f) - hitTrackIds.insert(static_cast(trackId)); + hitKeys.insert(simKey(hit.eventId().rawId(), static_cast(trackId))); } } @@ -912,7 +917,7 @@ class TruthLogicalGraphProducer : public edm::stream::EDProducer<> { anyCollectionValid = true; for (auto const& hit : *hHits) { if (hit.energyLoss() > 0.f) - hitTrackIds.insert(hit.trackId()); + hitKeys.insert(simKey(hit.eventId().rawId(), hit.trackId())); } } @@ -930,7 +935,7 @@ class TruthLogicalGraphProducer : public edm::stream::EDProducer<> { continue; if (ref.key <= 0 || ref.key > static_cast(std::numeric_limits::max())) continue; - if (hitTrackIds.count(static_cast(ref.key)) != 0) + if (hitKeys.count(simKey(raw.nodeEventId(simNodeU32), static_cast(ref.key))) != 0) particleDirectHit[particleId] = 1; } } else { diff --git a/PhysicsTools/TruthInfo/python/customiseDropIncompleteValidators.py b/PhysicsTools/TruthInfo/python/customiseDropIncompleteValidators.py new file mode 100644 index 0000000000000..047550e8b7d84 --- /dev/null +++ b/PhysicsTools/TruthInfo/python/customiseDropIncompleteValidators.py @@ -0,0 +1,25 @@ +"""Harness-only customise for producing HGCAL validation plots from standalone +RECO-from-step2 jobs. It removes validators that need EventSetup records or +collections a standalone offline job does not provide (HCAL trigger-primitive +record, HLT-only reco), keeping the offline HGCal validator and its associators +intact. Not for PR configs: the CI workflows run the full chain and provide +these inputs.""" + + +def customise(process): + drop = [ + "AllHcalDigisValidation", # needs CaloTPGRecord + "hcalDigisValidationSequence", + "hltHgcalValidator", # HLT TICL reco not run in an offline RECO job + "tpHltGsfTrackAssociation", + "hltGsfTrackValidator", + "hltTrackValidator", + "hltMultiTrackValidator", + ] + for label in drop: + if hasattr(process, label): + process.__delattr__(label) + # Also blank any path/sequence referencing the dropped labels leniently by + # rebuilding the schedule without empty leftovers is unnecessary: EDM prunes + # unscheduled modules, and removing the module makes the sequence skip it. + return process diff --git a/PhysicsTools/TruthInfo/python/customiseTruthMixedReco.py b/PhysicsTools/TruthInfo/python/customiseTruthMixedReco.py new file mode 100644 index 0000000000000..0cfa324612bfb --- /dev/null +++ b/PhysicsTools/TruthInfo/python/customiseTruthMixedReco.py @@ -0,0 +1,50 @@ +"""RECO-side customise for the pileup truth chain: build the merged (signal+pileup) +logical graph from the MixingModule accumulator's raw TruthGraph (label mix) and +resolve its SimHit associations against the mixed rechits. Pairs with +mixedTruthGraphCustomize.addTruthGraphAccumulator at DIGI, giving a pileup-aware +truth graph at RECO.""" + +import FWCore.ParameterSet.Config as cms + + +def customise(process): + from PhysicsTools.TruthInfo.truthGraphValidation_cff import ( + truthLogicalGraphProducer, + detIdToRecHitMapProducer, + truthLogicalGraphHitIndexProducer, + ) + # The merged raw TruthGraph comes from the mixing accumulator, not the + # standalone truthGraphProducer. + process.truthLogicalGraphProducer = truthLogicalGraphProducer.clone( + src=cms.InputTag("mix"), + # The hitless-subgraph pruning decides which SimTracks left a calo hit; feed it + # the merged (signal+pileup) HGCal sim-hits, else every pileup subgraph looks + # hitless (its hits are in mix:mergedHGCHits, not the signal-only g4SimHits) and + # is pruned, leaving pileup branches empty. Matches the hit index below. + simHitCollections=cms.VInputTag(cms.InputTag('mix', 'mergedHGCHits')), + ) + process.detIdToRecHitMapProducer = detIdToRecHitMapProducer + # The hit index also reads the RAW merged graph (for trackId->node); point it at mix. + process.truthLogicalGraphHitIndexProducer = truthLogicalGraphHitIndexProducer.clone( + rawSrc=cms.InputTag('mix'), + # Read the merged (signal+pileup) HGCal sim-hits from the accumulator, each + # tagged with its sub-event EncodedEventId, instead of the signal-only + # g4SimHits (which lack pileup at RECO). + simHitCollections=cms.VInputTag(cms.InputTag('mix', 'mergedHGCHits')), + ) + + process.truthMixedRecoPath = cms.Path( + process.truthLogicalGraphProducer + + process.detIdToRecHitMapProducer + + process.truthLogicalGraphHitIndexProducer + ) + process.schedule.append(process.truthMixedRecoPath) + + for out in process.outputModules_().values(): + out.outputCommands.extend([ + "keep *_truthLogicalGraphProducer_*_*", + ]) + # The hit index is an intermediate graph footprint, regenerable from the graph + # plus mix:mergedHGCHits; persisting it costs ~9.7 MB/event (subgraph-hit CSR + # over every retained pileup particle), so it is kept out of the event content. + return process diff --git a/PhysicsTools/TruthInfo/python/mixedTruthGraphCustomize.py b/PhysicsTools/TruthInfo/python/mixedTruthGraphCustomize.py index 39a141eb4ee0c..2481a985315a4 100644 --- a/PhysicsTools/TruthInfo/python/mixedTruthGraphCustomize.py +++ b/PhysicsTools/TruthInfo/python/mixedTruthGraphCustomize.py @@ -33,20 +33,55 @@ def addMixedTruthGraph(process): def addTruthGraphAccumulator(process, pileupBunchCrossings=(0,), - collapsePileupGen=True): + collapsePileupGen=True, + includeTrackingHits=False): """Phase-B (B1): register TruthGraphAccumulator inside the MixingModule. The accumulator builds the mixed (signal + pileup) raw TruthGraph from the native per-sub-event SimTrack/SimVertex collections. By default only in-time pileup (bx 0) is included; pass pileupBunchCrossings to widen. The mixed graph is kept in the output as TruthGraph_mix__. + + By default the accumulator captures only the CALORIMETER sim-hits (HGCAL plus + barrel ECAL/HCAL). The tracking detectors (tracker, muon chambers, MTD) are the + largest sim-hit family at PU200 and dominate the event size (the merged tracker + PSimHits alone are tens of MB/event), so they are OFF by default. Pass + includeTrackingHits=True for a full-detector truth graph. """ + def tags(*names): + return cms.VInputTag(*[cms.InputTag("g4SimHits", n) for n in names]) + + # Tracking detectors only when explicitly requested; empty (calo-only) otherwise. + trackerHits = cms.VInputTag() + muonHits = cms.VInputTag() + mtdHits = cms.VInputTag() + if includeTrackingHits: + trackerHits = tags( + "TrackerHitsPixelBarrelLowTof", "TrackerHitsPixelBarrelHighTof", + "TrackerHitsPixelEndcapLowTof", "TrackerHitsPixelEndcapHighTof", + "TrackerHitsTIBLowTof", "TrackerHitsTIBHighTof", + "TrackerHitsTIDLowTof", "TrackerHitsTIDHighTof", + "TrackerHitsTOBLowTof", "TrackerHitsTOBHighTof", + "TrackerHitsTECLowTof", "TrackerHitsTECHighTof", + ) + muonHits = tags("MuonDTHits", "MuonCSCHits", "MuonRPCHits", "MuonGEMHits", "MuonME0Hits") + mtdHits = tags("FastTimerHitsBarrel", "FastTimerHitsEndcap") + process.mix.digitizers.truthGraph = cms.PSet( accumulatorType=cms.string("TruthGraphAccumulator"), simTracks=cms.InputTag("g4SimHits"), simVertices=cms.InputTag("g4SimHits"), genEventHepMC3=cms.InputTag("generatorSmeared"), genEventHepMC=cms.InputTag("generatorSmeared"), + caloHits=tags("HGCHitsEE", "HGCHitsHEfront", "HGCHitsHEback"), + # Barrel calorimeters, kept in separate products so the RECO consumer applies + # the right sim-to-reco DetId relabelling per collection (ECAL barrel needs + # none, HCAL uses HcalHitRelabeller). + ecalHits=tags("EcalHitsEB"), + hcalHits=tags("HcalHits"), + trackerHits=trackerHits, + muonHits=muonHits, + mtdHits=mtdHits, pileupBunchCrossings=cms.vint32(*pileupBunchCrossings), collapsePileupGen=cms.bool(collapsePileupGen), collapseSignalGen=cms.bool(False), @@ -54,5 +89,16 @@ def addTruthGraphAccumulator(process, for out in process.outputModules_().values(): out.outputCommands.append("keep TruthGraph_mix_*_*") + # The merged sim-hit collections are the union of signal + all kept pileup hits, + # and must bridge a split DIGI->RECO job (the pileup hits are gone after mixing; + # the RECO customise does not re-keep them). Drop these lines for a single-job + # DIGI+RECO where the hit index is built in the same process. + out.outputCommands.append("keep *_mix_mergedHGCHits_*") + out.outputCommands.append("keep *_mix_mergedEcalHits_*") + out.outputCommands.append("keep *_mix_mergedHcalHits_*") + if includeTrackingHits: + out.outputCommands.append("keep *_mix_mergedTrackerHits_*") + out.outputCommands.append("keep *_mix_mergedMuonHits_*") + out.outputCommands.append("keep *_mix_mergedMtdHits_*") return process diff --git a/PhysicsTools/TruthInfo/src/LogicalGraphHitIndexBuilder.cc b/PhysicsTools/TruthInfo/src/LogicalGraphHitIndexBuilder.cc index e15bac28303dd..ef3f504286a15 100644 --- a/PhysicsTools/TruthInfo/src/LogicalGraphHitIndexBuilder.cc +++ b/PhysicsTools/TruthInfo/src/LogicalGraphHitIndexBuilder.cc @@ -16,11 +16,11 @@ namespace truth { channel.resize(nParticles); } - void LogicalGraphHitIndexBuilder::setSimTrackForParticle(uint32_t particleId, uint32_t trackId) { + void LogicalGraphHitIndexBuilder::setSimTrackForParticle(uint32_t particleId, uint64_t eventId, uint32_t trackId) { if (particleId >= nParticles_) return; - trackIdToParticle_[trackId] = particleId; + trackIdToParticle_[simKey(eventId, trackId)] = particleId; } void LogicalGraphHitIndexBuilder::addParticleChild(uint32_t parentParticleId, uint32_t childParticleId) { @@ -31,11 +31,11 @@ namespace truth { } void LogicalGraphHitIndexBuilder::addHit( - HitChannel channel, uint32_t trackId, uint32_t detId, float energy, uint32_t recHitIndex) { + HitChannel channel, uint64_t eventId, uint32_t trackId, uint32_t detId, float energy, uint32_t recHitIndex) { if (energy <= 0.f) return; - auto it = trackIdToParticle_.find(trackId); + auto it = trackIdToParticle_.find(simKey(eventId, trackId)); if (it == trackIdToParticle_.end()) return; diff --git a/PhysicsTools/TruthInfo/test/BranchHitAssociator_t.cpp b/PhysicsTools/TruthInfo/test/BranchHitAssociator_t.cpp index 7986473d453d5..1bcd39d7d0db3 100644 --- a/PhysicsTools/TruthInfo/test/BranchHitAssociator_t.cpp +++ b/PhysicsTools/TruthInfo/test/BranchHitAssociator_t.cpp @@ -26,13 +26,13 @@ namespace { // cellTotal = {10:1, 11:2, 12:2} truth::LogicalGraphHitIndex buildIndex() { truth::LogicalGraphHitIndexBuilder b(2); - b.setSimTrackForParticle(0, 100); - b.setSimTrackForParticle(1, 101); + b.setSimTrackForParticle(0, 0, 100); + b.setSimTrackForParticle(1, 0, 101); b.addParticleChild(0, 1); - b.addHit(truth::HitChannel::HGCalCalo, 100, 10, 1.0f, 0); - b.addHit(truth::HitChannel::HGCalCalo, 100, 11, 1.0f, 0); - b.addHit(truth::HitChannel::HGCalCalo, 101, 11, 1.0f, 0); - b.addHit(truth::HitChannel::HGCalCalo, 101, 12, 2.0f, 0); + b.addHit(truth::HitChannel::HGCalCalo, 0, 100, 10, 1.0f, 0); + b.addHit(truth::HitChannel::HGCalCalo, 0, 100, 11, 1.0f, 0); + b.addHit(truth::HitChannel::HGCalCalo, 0, 101, 11, 1.0f, 0); + b.addHit(truth::HitChannel::HGCalCalo, 0, 101, 12, 2.0f, 0); return b.finish(); } @@ -40,14 +40,14 @@ namespace { // calo cell (10) that the tracker associator must ignore. truth::LogicalGraphHitIndex buildTrackerIndex() { truth::LogicalGraphHitIndexBuilder b(2); - b.setSimTrackForParticle(0, 100); - b.setSimTrackForParticle(1, 101); + b.setSimTrackForParticle(0, 0, 100); + b.setSimTrackForParticle(1, 0, 101); b.addParticleChild(0, 1); - b.addHit(truth::HitChannel::HGCalCalo, 100, 10, 1.0f, 0); // calo channel - b.addHit(truth::HitChannel::Tracker, 100, 20, 1.0f); - b.addHit(truth::HitChannel::Tracker, 100, 21, 1.0f); - b.addHit(truth::HitChannel::Tracker, 101, 21, 1.0f); - b.addHit(truth::HitChannel::Tracker, 101, 22, 2.0f); + b.addHit(truth::HitChannel::HGCalCalo, 0, 100, 10, 1.0f, 0); // calo channel + b.addHit(truth::HitChannel::Tracker, 0, 100, 20, 1.0f); + b.addHit(truth::HitChannel::Tracker, 0, 100, 21, 1.0f); + b.addHit(truth::HitChannel::Tracker, 0, 101, 21, 1.0f); + b.addHit(truth::HitChannel::Tracker, 0, 101, 22, 2.0f); return b.finish(); } diff --git a/PhysicsTools/TruthInfo/test/LogicalGraphHitIndexBuilder_t.cpp b/PhysicsTools/TruthInfo/test/LogicalGraphHitIndexBuilder_t.cpp index 08a1742c9ed31..b6df62668f415 100644 --- a/PhysicsTools/TruthInfo/test/LogicalGraphHitIndexBuilder_t.cpp +++ b/PhysicsTools/TruthInfo/test/LogicalGraphHitIndexBuilder_t.cpp @@ -33,15 +33,15 @@ CPPUNIT_TEST_SUITE_REGISTRATION(TestLogicalGraphHitIndexBuilder); void TestLogicalGraphHitIndexBuilder::testSubgraphHitsAreSortedContiguousAndAccumulated() { // particle 0 (track 100) -> child particle 1 (track 101) truth::LogicalGraphHitIndexBuilder builder(2); - builder.setSimTrackForParticle(0, 100); - builder.setSimTrackForParticle(1, 101); + builder.setSimTrackForParticle(0, 0, 100); + builder.setSimTrackForParticle(1, 0, 101); builder.addParticleChild(0, 1); - builder.addHit(truth::HitChannel::HGCalCalo, 100, /*detId=*/10, /*energy=*/1.0f, /*recHitIndex=*/0); - builder.addHit(truth::HitChannel::HGCalCalo, 100, /*detId=*/5, /*energy=*/2.0f, /*recHitIndex=*/1); + builder.addHit(truth::HitChannel::HGCalCalo, 0, 100, /*detId=*/10, /*energy=*/1.0f, /*recHitIndex=*/0); + builder.addHit(truth::HitChannel::HGCalCalo, 0, 100, /*detId=*/5, /*energy=*/2.0f, /*recHitIndex=*/1); builder.addHit( - truth::HitChannel::HGCalCalo, 101, /*detId=*/10, /*energy=*/3.0f, /*recHitIndex=*/0); // same detId as parent - builder.addHit(truth::HitChannel::HGCalCalo, 101, /*detId=*/20, /*energy=*/1.5f, /*recHitIndex=*/2); + truth::HitChannel::HGCalCalo, 0, 101, /*detId=*/10, /*energy=*/3.0f, /*recHitIndex=*/0); // same detId as parent + builder.addHit(truth::HitChannel::HGCalCalo, 0, 101, /*detId=*/20, /*energy=*/1.5f, /*recHitIndex=*/2); auto index = builder.finish(); @@ -66,10 +66,10 @@ void TestLogicalGraphHitIndexBuilder::testSubgraphHitsAreSortedContiguousAndAccu void TestLogicalGraphHitIndexBuilder::testDirectHitsAreSortedByDetId() { truth::LogicalGraphHitIndexBuilder builder(1); - builder.setSimTrackForParticle(0, 7); - builder.addHit(truth::HitChannel::HGCalCalo, 7, 30, 1.0f, 0); - builder.addHit(truth::HitChannel::HGCalCalo, 7, 3, 1.0f, 1); - builder.addHit(truth::HitChannel::HGCalCalo, 7, 17, 1.0f, 2); + builder.setSimTrackForParticle(0, 0, 7); + builder.addHit(truth::HitChannel::HGCalCalo, 0, 7, 30, 1.0f, 0); + builder.addHit(truth::HitChannel::HGCalCalo, 0, 7, 3, 1.0f, 1); + builder.addHit(truth::HitChannel::HGCalCalo, 0, 7, 17, 1.0f, 2); auto index = builder.finish(); auto direct = index.directHits(truth::HitChannel::HGCalCalo, 0); @@ -85,16 +85,16 @@ void TestLogicalGraphHitIndexBuilder::testSubgraphDiamondCountsSharedDescendantO // once. (Regression: the old recursive child-subgraph merge summed it once per // path, since coalesce() sums equal detIds, doubling the energy.) truth::LogicalGraphHitIndexBuilder builder(4); - builder.setSimTrackForParticle(0, 100); - builder.setSimTrackForParticle(1, 101); - builder.setSimTrackForParticle(2, 102); - builder.setSimTrackForParticle(3, 103); + builder.setSimTrackForParticle(0, 0, 100); + builder.setSimTrackForParticle(1, 0, 101); + builder.setSimTrackForParticle(2, 0, 102); + builder.setSimTrackForParticle(3, 0, 103); builder.addParticleChild(0, 1); builder.addParticleChild(0, 2); builder.addParticleChild(1, 3); builder.addParticleChild(2, 3); - builder.addHit(truth::HitChannel::HGCalCalo, 103, /*detId=*/50, /*energy=*/2.0f, /*recHitIndex=*/0); + builder.addHit(truth::HitChannel::HGCalCalo, 0, 103, /*detId=*/50, /*energy=*/2.0f, /*recHitIndex=*/0); auto index = builder.finish();