Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions PhysicsTools/TruthInfo/interface/LogicalGraphHitIndexBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint64_t>(trackId);
}

[[nodiscard]] LogicalGraphHitIndex finish();

private:
Expand Down Expand Up @@ -72,7 +81,7 @@ namespace truth {

uint32_t nParticles_ = 0;

std::unordered_map<uint32_t, uint32_t> trackIdToParticle_;
std::unordered_map<uint64_t, uint32_t> trackIdToParticle_;
std::vector<std::vector<uint32_t>> children_;

// [channel index][particle] -> direct hit list. Subgraph hits are aggregated
Expand Down
1 change: 0 additions & 1 deletion PhysicsTools/TruthInfo/plugins/BuildFile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
<use name="DataFormats/ParticleFlowReco"/>
<use name="SimDataFormats/TrackingHit"/>
<use name="Geometry/TrackerGeometryBuilder"/>
<use name="Geometry/CommonDetUnit"/>
<use name="Geometry/CommonTopologies"/>

<use name="DataFormats/TrackReco"/>
Expand Down
25 changes: 19 additions & 6 deletions PhysicsTools/TruthInfo/plugins/LogicalGraphHitIndexProducer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -487,8 +487,12 @@ void TruthLogicalGraphHitIndexProducer::fillSimHits(edm::Event& event,
}
}

builder.addHit(
truth::HitChannel::HGCalCalo, static_cast<uint32_t>(geantTrackId), detId, simHit.energy(), recHitIndex);
builder.addHit(truth::HitChannel::HGCalCalo,
simHit.eventId().rawId(),
static_cast<uint32_t>(geantTrackId),
detId,
simHit.energy(),
recHitIndex);
}
}
}
Expand All @@ -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());
}
}
}
Expand All @@ -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());
}
}
}
Expand Down Expand Up @@ -578,7 +587,11 @@ void TruthLogicalGraphHitIndexProducer::fillMtdHits(edm::Event& event,
const auto trackId = static_cast<uint32_t>(cluster.particleId());
for (auto const& [packedHit, energy] : cluster.hits_and_energies()) {
const uint32_t moduleDetId = static_cast<uint32_t>(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);
}
}
}
Expand Down
135 changes: 127 additions & 8 deletions PhysicsTools/TruthInfo/plugins/TruthGraphAccumulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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<uint64_t>(id.rawId());
}

// Stable (status 1) GEN particles as (barcode, pdgId). Used to collapse the GEN
Expand Down Expand Up @@ -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 <class EvT>
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 <class HitT, class EvT>
void mergeHits(EvT const& ev,
std::vector<edm::InputTag> const& tags,
EncodedEventId const& eid,
std::vector<HitT>& out);

const edm::InputTag simTrackTag_;
const edm::InputTag simVertexTag_;
const edm::InputTag hepmc3Tag_;
const edm::InputTag hepmc2Tag_;
const std::vector<edm::InputTag> caloHitTags_;
const std::vector<edm::InputTag> ecalHitTags_;
const std::vector<edm::InputTag> hcalHitTags_;
const std::vector<edm::InputTag> trackerHitTags_;
const std::vector<edm::InputTag> muonHitTags_;
const std::vector<edm::InputTag> mtdHitTags_;
const std::vector<int> pileupBunchCrossings_;
const bool collapsePileupGen_;
const bool collapseSignalGen_;

std::unordered_map<int, int> 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<PCaloHit> mergedCaloHits_;
std::vector<PCaloHit> mergedEcalHits_;
std::vector<PCaloHit> 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<PSimHit> mergedTrackerHits_;
std::vector<PSimHit> mergedMuonHits_;
std::vector<PSimHit> mergedMtdHits_;

std::vector<TruthGraph::NodeRef> nodes_;
std::vector<int32_t> pdgId_;
Expand All @@ -157,18 +195,42 @@ TruthGraphAccumulator::TruthGraphAccumulator(edm::ParameterSet const& cfg,
simVertexTag_(cfg.getParameter<edm::InputTag>("simVertices")),
hepmc3Tag_(cfg.getParameter<edm::InputTag>("genEventHepMC3")),
hepmc2Tag_(cfg.getParameter<edm::InputTag>("genEventHepMC")),
caloHitTags_(cfg.getParameter<std::vector<edm::InputTag>>("caloHits")),
ecalHitTags_(cfg.getParameter<std::vector<edm::InputTag>>("ecalHits")),
hcalHitTags_(cfg.getParameter<std::vector<edm::InputTag>>("hcalHits")),
trackerHitTags_(cfg.getParameter<std::vector<edm::InputTag>>("trackerHits")),
muonHitTags_(cfg.getParameter<std::vector<edm::InputTag>>("muonHits")),
mtdHitTags_(cfg.getParameter<std::vector<edm::InputTag>>("mtdHits")),
pileupBunchCrossings_(cfg.getParameter<std::vector<int>>("pileupBunchCrossings")),
collapsePileupGen_(cfg.getParameter<bool>("collapsePileupGen")),
collapseSignalGen_(cfg.getParameter<bool>("collapseSignalGen")) {
producesCollector.produces<TruthGraph>();
producesCollector.produces<std::vector<PCaloHit>>("mergedHGCHits");
producesCollector.produces<std::vector<PCaloHit>>("mergedEcalHits");
producesCollector.produces<std::vector<PCaloHit>>("mergedHcalHits");
producesCollector.produces<std::vector<PSimHit>>("mergedTrackerHits");
producesCollector.produces<std::vector<PSimHit>>("mergedMuonHits");
producesCollector.produces<std::vector<PSimHit>>("mergedMtdHits");
iC.consumes<edm::SimTrackContainer>(simTrackTag_);
iC.consumes<edm::SimVertexContainer>(simVertexTag_);
iC.mayConsume<edm::HepMC3Product>(hepmc3Tag_);
iC.mayConsume<edm::HepMCProduct>(hepmc2Tag_);
for (auto const* tags : {&caloHitTags_, &ecalHitTags_, &hcalHitTags_})
for (auto const& tag : *tags)
iC.mayConsume<std::vector<PCaloHit>>(tag);
for (auto const* tags : {&trackerHitTags_, &muonHitTags_, &mtdHitTags_})
for (auto const& tag : *tags)
iC.mayConsume<std::vector<PSimHit>>(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();
Expand Down Expand Up @@ -270,6 +332,44 @@ void TruthGraphAccumulator::addSubEvent(std::vector<std::pair<int, int>> const&
}
}

template <class HitT, class EvT>
void TruthGraphAccumulator::mergeHits(EvT const& ev,
std::vector<edm::InputTag> const& tags,
EncodedEventId const& eid,
std::vector<HitT>& out) {
for (auto const& tag : tags) {
edm::Handle<std::vector<HitT>> 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 <class EvT>
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<edm::SimTrackContainer> tracks;
edm::Handle<edm::SimVertexContainer> vertices;
Expand All @@ -280,7 +380,9 @@ void TruthGraphAccumulator::accumulate(edm::Event const& event, edm::EventSetup
std::vector<std::pair<int, int>> 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&) {
Expand All @@ -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&) {
Expand Down Expand Up @@ -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::vector<PCaloHit>>(std::move(mergedCaloHits_)), "mergedHGCHits");
event.put(std::make_unique<std::vector<PCaloHit>>(std::move(mergedEcalHits_)), "mergedEcalHits");
event.put(std::make_unique<std::vector<PCaloHit>>(std::move(mergedHcalHits_)), "mergedHcalHits");
event.put(std::make_unique<std::vector<PSimHit>>(std::move(mergedTrackerHits_)), "mergedTrackerHits");
event.put(std::make_unique<std::vector<PSimHit>>(std::move(mergedMuonHits_)), "mergedMuonHits");
event.put(std::make_unique<std::vector<PSimHit>>(std::move(mergedMtdHits_)), "mergedMtdHits");
}

DEFINE_DIGI_ACCUMULATOR(TruthGraphAccumulator);
13 changes: 9 additions & 4 deletions PhysicsTools/TruthInfo/plugins/TruthLogicalGraphProducer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,12 @@ class TruthLogicalGraphProducer : public edm::stream::EDProducer<> {
std::vector<uint8_t> particleDirectHit;

if (dropHitlessSimSubgraphs_) {
std::unordered_set<uint32_t> 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<uint64_t>(trackId); };
std::unordered_set<uint64_t> hitKeys;
bool anyCollectionValid = false;

for (auto const& token : caloSimHitTokens_) {
Expand All @@ -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<uint32_t>(trackId));
hitKeys.insert(simKey(hit.eventId().rawId(), static_cast<uint32_t>(trackId)));
}
}

Expand All @@ -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()));
}
}

Expand All @@ -930,7 +935,7 @@ class TruthLogicalGraphProducer : public edm::stream::EDProducer<> {
continue;
if (ref.key <= 0 || ref.key > static_cast<int64_t>(std::numeric_limits<uint32_t>::max()))
continue;
if (hitTrackIds.count(static_cast<uint32_t>(ref.key)) != 0)
if (hitKeys.count(simKey(raw.nodeEventId(simNodeU32), static_cast<uint32_t>(ref.key))) != 0)
particleDirectHit[particleId] = 1;
}
} else {
Expand Down
25 changes: 25 additions & 0 deletions PhysicsTools/TruthInfo/python/customiseDropIncompleteValidators.py
Original file line number Diff line number Diff line change
@@ -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
Loading