diff --git a/HLTrigger/NGTScouting/plugins/HLTTracksRecHitsTableProducer.cc b/HLTrigger/NGTScouting/plugins/HLTTracksRecHitsTableProducer.cc new file mode 100644 index 0000000000000..ffb1738081c30 --- /dev/null +++ b/HLTrigger/NGTScouting/plugins/HLTTracksRecHitsTableProducer.cc @@ -0,0 +1,124 @@ +/** \class HLTTracksRecHitsTableProducer + * + * \brief Produces a nanoAOD flat table with recHits information for HLT tracks + * + * This producer creates a nanoAOD flat table containing the recHits global positions and errors + * starting from a collection of reco::Track. + * This data can be added as an extension to the HLTTracks table. + * The maximum number of recHits per track is fixed to a configurable value; + * if a track has more recHits, a warning is issued and the extra recHits are ignored. + * + * \author Luca Ferragina (INFN BO), 2025 + */ + +#include "CommonTools/Utils/interface/StringCutObjectSelector.h" +#include "DataFormats/BeamSpot/interface/BeamSpot.h" +#include "DataFormats/Common/interface/ValueMap.h" +#include "DataFormats/NanoAOD/interface/FlatTable.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/StreamID.h" + +class HLTTracksRecHitsTableProducer : public edm::stream::EDProducer<> { +public: + explicit HLTTracksRecHitsTableProducer(const edm::ParameterSet&); + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::Event&, const edm::EventSetup&) override; + + const edm::EDGetTokenT> tracks_; + + const std::string tableName_; + const unsigned int maxRecHits_; + const unsigned int precision_; + const bool skipNonExistingSrc_; +}; + +HLTTracksRecHitsTableProducer::HLTTracksRecHitsTableProducer(const edm::ParameterSet& params) + : tracks_(consumes>(params.getParameter("tracksSrc"))), + tableName_(params.getParameter("tableName")), + maxRecHits_(params.getParameter("maxRecHits")), + precision_(params.getParameter("precision")), + skipNonExistingSrc_(params.getParameter("skipNonExistingSrc")) { + produces(tableName_); +} + +void HLTTracksRecHitsTableProducer::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + auto tracksIn = iEvent.getHandle(tracks_); + const size_t nTracks = tracksIn.isValid() ? (*tracksIn).size() : 0; + + static constexpr float default_value = std::numeric_limits::quiet_NaN(); + + std::vector globalX(maxRecHits_ * nTracks, default_value); + std::vector globalY(maxRecHits_ * nTracks, default_value); + std::vector globalZ(maxRecHits_ * nTracks, default_value); + std::vector globalErrX(maxRecHits_ * nTracks, default_value); + std::vector globalErrY(maxRecHits_ * nTracks, default_value); + std::vector globalErrZ(maxRecHits_ * nTracks, default_value); + + if (tracksIn.isValid() || !(this->skipNonExistingSrc_)) { + const auto& tracks = *tracksIn; + for (size_t tkIndex = 0; tkIndex < nTracks; ++tkIndex) { + const auto& track = tracks[tkIndex]; + for (auto it = track.recHitsBegin(); it != track.recHitsEnd(); ++it) { + auto hit = *it; + auto globalPoint = hit->globalPosition(); + auto globalError = hit->globalPositionError(); + auto hitIndex = std::distance(track.recHitsBegin(), it); + if (hitIndex >= maxRecHits_) { + edm::LogWarning("HLTTracksRecHitsTableProducer") + << " Track " << tkIndex << " has more (" << track.recHitsSize() << ") than " << maxRecHits_ + << " recHits, skipping the rest."; + break; + } + globalX[tkIndex * maxRecHits_ + hitIndex] = globalPoint.x(); + globalY[tkIndex * maxRecHits_ + hitIndex] = globalPoint.y(); + globalZ[tkIndex * maxRecHits_ + hitIndex] = globalPoint.z(); + globalErrX[tkIndex * maxRecHits_ + hitIndex] = globalError.cxx(); + globalErrY[tkIndex * maxRecHits_ + hitIndex] = globalError.cyy(); + globalErrZ[tkIndex * maxRecHits_ + hitIndex] = globalError.czz(); + } + } + } else { + edm::LogWarning("HLTTracksRecHitsTableProducer") + << " Invalid handle for " << tableName_ << " in tracks input collection"; + } + + assert(globalX.size() == globalY.size() && globalX.size() == globalZ.size() && globalX.size() == globalErrX.size() && + globalX.size() == globalErrY.size() && globalX.size() == globalErrZ.size()); + + // Table for tracks recHits + auto tracksTable = + std::make_unique(nTracks * maxRecHits_, tableName_, /*singleton*/ false, /*extension*/ false); + tracksTable->addColumn("globalX", globalX, "RecHits global x coordinate", precision_); + tracksTable->addColumn("globalY", globalY, "RecHits global y coordinate", precision_); + tracksTable->addColumn("globalZ", globalZ, "RecHits global z coordinate", precision_); + tracksTable->addColumn("globalErrX", globalErrX, "RecHits global x error", precision_); + tracksTable->addColumn("globalErrY", globalErrY, "RecHits global y error", precision_); + tracksTable->addColumn("globalErrZ", globalErrZ, "RecHits global z error", precision_); + + iEvent.put(std::move(tracksTable), tableName_); +} + +void HLTTracksRecHitsTableProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("maxRecHits", 16)->setComment("maximum number of recHits per track to be stored in the table"); + desc.add("skipNonExistingSrc", false) + ->setComment("whether or not to skip producing the table on absent input product"); + desc.add("tableName")->setComment("name of the flat table ouput"); + desc.add("tracksSrc")->setComment("std::vector input collection"); + desc.add("precision", 7); + descriptions.addWithDefaultLabel(desc); +} + +// Define this as a plug-in +#include "FWCore/Framework/interface/MakerMacros.h" +DEFINE_FWK_MODULE(HLTTracksRecHitsTableProducer); diff --git a/HLTrigger/NGTScouting/python/HLTNanoProducer_cff.py b/HLTrigger/NGTScouting/python/HLTNanoProducer_cff.py index 844e1901ceacf..7b408a3f847bf 100644 --- a/HLTrigger/NGTScouting/python/HLTNanoProducer_cff.py +++ b/HLTrigger/NGTScouting/python/HLTNanoProducer_cff.py @@ -77,8 +77,10 @@ # Store PixelTracks objects NanoPixelTables = cms.Sequence( - hltPixelTrackTable + pixelTrackAssoc + + hltPixelTrackTable + hltPixelTrackExtTable + + hltPixelTrackRecHitsTable ) # Store variables and associators for validation purposes @@ -124,6 +126,12 @@ + NanoValTables ) +# Phase-2 HLT Nano flavour for pixel tracking validation / optimization +hltPixelOnlyNanoFlavour = cms.Sequence( + NanoGenTables + + NanoPixelTables +) + ###################################### # Customization ###################################### diff --git a/HLTrigger/NGTScouting/python/hltTracks_cfi.py b/HLTrigger/NGTScouting/python/hltTracks_cfi.py index fba14c223da62..823e56bf16298 100644 --- a/HLTrigger/NGTScouting/python/hltTracks_cfi.py +++ b/HLTrigger/NGTScouting/python/hltTracks_cfi.py @@ -1,6 +1,37 @@ import FWCore.ParameterSet.Config as cms from PhysicsTools.NanoAOD.common_cff import * +from PhysicsTools.NanoAOD.trackingAssocValueMapsProducer_cfi import trackingAssocValueMapsProducer + +tpSelectorPixelTracks = cms.PSet( + lip = cms.double(30.0), + chargedOnly = cms.bool(True), + stableOnly = cms.bool(False), + pdgId = cms.vint32(), + signalOnly = cms.bool(False), + intimeOnly = cms.bool(True), + minRapidity = cms.double(-4.5), + minHit = cms.int32(0), + ptMin = cms.double(0.9), + ptMax = cms.double(1e100), + maxRapidity = cms.double(4.5), + tip = cms.double(2.5), + invertRapidityCut = cms.bool(False), + minPhi = cms.double(-3.2), + maxPhi = cms.double(3.2), +) + +pixelTrackAssoc = trackingAssocValueMapsProducer.clone( + trackCollection = cms.InputTag("hltPhase2PixelTracks"), + associator = cms.InputTag("hltTrackAssociatorByHits"), + trackingParticles = cms.InputTag("mix", "MergedTrackTruth"), + tpSelectorPSet = tpSelectorPixelTracks, + storeTPKinematics = cms.bool(True), +) + +from Configuration.ProcessModifiers.phase2CAExtension_cff import phase2CAExtension +phase2CAExtension.toModify(pixelTrackAssoc, trackCollection = "hltPhase2PixelTracksCAExtension") + hltPixelTrackTable = cms.EDProducer( "SimpleTriggerTrackFlatTableProducer", skipNonExistingSrc = cms.bool(True), @@ -8,7 +39,6 @@ cut = cms.string(""), name = cms.string("hltPixelTrack"), doc = cms.string("HLT Pixel Track information"), - extension = cms.bool(False), variables = cms.PSet( pt = Var("pt()", "float", doc = "p_T (GeV)"), eta = Var("eta()", "float", doc = "#eta"), @@ -29,6 +59,37 @@ #isLoose = Var("quality('loose')", "bool", doc = "Loose track flag"), isTight = Var("quality('tight')", "bool", doc = "Tight track flag"), isHighPurity = Var("quality('highPurity')", "bool", doc = "High-purity track flag"), + qoverp = Var("qoverp()", "float", doc = "q/p"), + dsz = Var("dsz()", "float", doc = "dsz (cm)"), + qoverpErr = Var("qoverpError()", "float", doc = ""), + ptErr = Var("ptError()", "float", doc = ""), + lambdaErr = Var("lambdaError()", "float", doc = ""), + dszErr = Var("dszError()", "float", doc = ""), + etaErr = Var("etaError()", "float", doc = ""), + phiErr = Var("phiError()", "float", doc = ""), + ), + externalVariables = cms.PSet( + matched = cms.PSet(src = cms.InputTag("pixelTrackAssoc","matched"), + doc = cms.string("1 if matched to a TrackingParticle"), + type = cms.string("uint8")), + duplicate = cms.PSet(src = cms.InputTag("pixelTrackAssoc","duplicate"), + doc = cms.string("1 if multiple reco tracks map to same TP"), + type = cms.string("uint8")), + tpPdgId = cms.PSet(src = cms.InputTag("pixelTrackAssoc","tpPdgId"), + doc = cms.string("pdgId of matched TrackingParticle"), + type = cms.string("int16")), + tpCharge = cms.PSet(src = cms.InputTag("pixelTrackAssoc","tpCharge"), + doc = cms.string("charge of matched TrackingParticle"), + type = cms.string("int16")), + tpPt = cms.PSet(src = cms.InputTag("pixelTrackAssoc","tpPt"), + doc = cms.string("pt of matched TrackingParticle"), + type = cms.string("float")), + tpEta = cms.PSet(src = cms.InputTag("pixelTrackAssoc","tpEta"), + doc = cms.string("eta of matched TrackingParticle"), + type = cms.string("float")), + tpPhi = cms.PSet(src = cms.InputTag("pixelTrackAssoc","tpPhi"), + doc = cms.string("phi of matched TrackingParticle"), + type = cms.string("float")) ) ) @@ -39,6 +100,19 @@ beamSpot = cms.InputTag("hltOnlineBeamSpot"), precision = cms.int32(7)) +hltPixelTrackRecHitsTable = cms.EDProducer("HLTTracksRecHitsTableProducer", + tableName = cms.string("hltPixelTrackRecHits"), + skipNonExistingSrc = cms.bool(True), + tracksSrc = cms.InputTag("hltPhase2PixelTracks"), + maxRecHits = cms.uint32(16), + precision = cms.int32(7) +) + + +phase2CAExtension.toModify(hltPixelTrackTable, src = "hltPhase2PixelTracksCAExtension") +phase2CAExtension.toModify(hltPixelTrackExtTable, tracksSrc = "hltPhase2PixelTracksCAExtension") +phase2CAExtension.toModify(hltPixelTrackRecHitsTable, tracksSrc = "hltPhase2PixelTracksCAExtension") + hltGeneralTrackTable = cms.EDProducer( "SimpleTriggerTrackFlatTableProducer", skipNonExistingSrc = cms.bool(True), diff --git a/PhysicsTools/NanoAOD/plugins/BuildFile.xml b/PhysicsTools/NanoAOD/plugins/BuildFile.xml index bbe8818cfa63a..1611733e5150d 100644 --- a/PhysicsTools/NanoAOD/plugins/BuildFile.xml +++ b/PhysicsTools/NanoAOD/plugins/BuildFile.xml @@ -18,6 +18,7 @@ + @@ -36,6 +37,9 @@ + + +