From 856bcb6187c912df87ed66e4994a38c931786700 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 1 Jul 2020 13:42:09 +0200 Subject: [PATCH 1/8] Addressed comments in #30256 and removed data files. --- L1Trigger/Phase2L1ParticleFlow/BuildFile.xml | 17 + .../interface/BitwisePFAlgo.h | 22 + .../Phase2L1ParticleFlow/interface/COEFile.h | 44 + .../interface/CaloClusterer.h | 297 ++++++ .../interface/DiscretePFInputs.h | 259 +++++ .../interface/DiscretePFInputsIO.h | 143 +++ .../interface/HGC3DClusterEgID.h | 53 + .../interface/L1TPFUtils.h | 13 + .../interface/LinearizedPuppiAlgo.h | 33 + .../interface/PFAlgo2HGC.h | 76 ++ .../Phase2L1ParticleFlow/interface/PFAlgo3.h | 109 +++ .../interface/PFAlgoBase.h | 28 + .../interface/PUAlgoBase.h | 34 + .../interface/ParametricResolution.h | 27 + .../interface/PuppiAlgo.h | 38 + .../Phase2L1ParticleFlow/interface/Region.h | 112 +++ .../interface/RegionMapper.h | 57 ++ .../plugins/BuildFile.xml | 12 + .../plugins/L1TCorrectedPFJetProducer.cc | 58 ++ .../plugins/L1TPFCaloProducer.cc | 300 ++++++ .../plugins/L1TPFCandMerger.cc | 7 + .../plugins/L1TPFCandMultiMerger.cc | 51 + .../plugins/L1TPFCandSelector.cc | 8 + .../plugins/L1TPFProducer.cc | 400 ++++++++ .../PFClusterProducerFromHGC3DClusters.cc | 112 +++ .../PFClusterProducerFromL1EGClusters.cc | 60 ++ .../plugins/PFTrackProducerFromL1Tracks.cc | 87 ++ .../python/l1ParticleFlow_cff.py | 316 ++++++ .../python/l1pfJetMet_cff.py | 38 + .../python/l1pfProducer_cfi.py | 72 ++ .../python/pfClustersFromCombinedCalo_cfi.py | 57 ++ .../pfClustersFromHGC3DClustersEM_cfi.py | 38 + .../python/pfClustersFromHGC3DClusters_cfi.py | 65 ++ .../python/pfClustersFromL1EGClusters_cfi.py | 37 + .../python/pfTracksFromL1Tracks_cfi.py | 22 + .../Phase2L1ParticleFlow/src/BitwisePFAlgo.cc | 196 ++++ L1Trigger/Phase2L1ParticleFlow/src/COEFile.cc | 130 +++ .../Phase2L1ParticleFlow/src/CaloClusterer.cc | 636 ++++++++++++ .../src/DiscretePFInputsIO.cc | 1 + .../src/HGC3DClusterEgID.cc | 45 + .../Phase2L1ParticleFlow/src/L1TPFUtils.cc | 17 + .../src/LinearizedPuppiAlgo.cc | 142 +++ .../Phase2L1ParticleFlow/src/PFAlgo2HGC.cc | 650 +++++++++++++ L1Trigger/Phase2L1ParticleFlow/src/PFAlgo3.cc | 911 ++++++++++++++++++ .../Phase2L1ParticleFlow/src/PFAlgoBase.cc | 57 ++ .../Phase2L1ParticleFlow/src/PUAlgoBase.cc | 81 ++ .../src/ParametricResolution.cc | 48 + .../Phase2L1ParticleFlow/src/PuppiAlgo.cc | 266 +++++ L1Trigger/Phase2L1ParticleFlow/src/Region.cc | 130 +++ .../Phase2L1ParticleFlow/src/RegionMapper.cc | 341 +++++++ .../Phase2L1ParticleFlow/src/corrector.cc | 188 ++++ .../Phase2L1ParticleFlow/src/corrector.h | 65 ++ .../Phase2L1ParticleFlow/src/dbgPrintf.h | 11 + .../Phase2L1ParticleFlow/src/firmware/data.h | 209 ++++ .../src/firmware/pfalgo2hgc.h | 40 + .../src/firmware/pfalgo3.h | 50 + .../src/firmware/pfalgo_common.h | 16 + .../src/ref/pfalgo2hgc_ref.cpp | 196 ++++ .../src/ref/pfalgo2hgc_ref.h | 17 + .../src/ref/pfalgo3_ref.cpp | 485 ++++++++++ .../src/ref/pfalgo3_ref.h | 58 ++ .../src/ref/pfalgo_common_ref.cpp | 49 + .../src/ref/pfalgo_common_ref.h | 95 ++ .../src/utils/DiscretePF2Firmware.h | 69 ++ .../src/utils/Firmware2DiscretePF.h | 161 ++++ .../Phase2L1ParticleFlow/test/BuildFile.xml | 10 + .../test/l1pfJetMetTreeProducer.py | 31 + .../test/testOutputFiles.cpp | 611 ++++++++++++ 68 files changed, 9114 insertions(+) create mode 100644 L1Trigger/Phase2L1ParticleFlow/BuildFile.xml create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/BitwisePFAlgo.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/COEFile.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/CaloClusterer.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputs.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/HGC3DClusterEgID.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/L1TPFUtils.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/LinearizedPuppiAlgo.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo2HGC.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo3.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/PUAlgoBase.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/Region.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/interface/RegionMapper.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/BuildFile.xml create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/L1TCorrectedPFJetProducer.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCaloProducer.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMerger.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMultiMerger.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandSelector.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFProducer.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromHGC3DClusters.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromL1EGClusters.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/l1ParticleFlow_cff.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/l1pfProducer_cfi.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromCombinedCalo_cfi.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClustersEM_cfi.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClusters_cfi.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromL1EGClusters_cfi.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/python/pfTracksFromL1Tracks_cfi.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/BitwisePFAlgo.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/COEFile.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/CaloClusterer.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/DiscretePFInputsIO.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/HGC3DClusterEgID.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/L1TPFUtils.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/LinearizedPuppiAlgo.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/PFAlgo2HGC.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/PFAlgo3.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/PFAlgoBase.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/PUAlgoBase.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ParametricResolution.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/PuppiAlgo.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/Region.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/RegionMapper.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/corrector.cc create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/corrector.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/firmware/data.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo2hgc.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo3.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo_common.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.cpp create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.cpp create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.cpp create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/utils/DiscretePF2Firmware.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/src/utils/Firmware2DiscretePF.h create mode 100644 L1Trigger/Phase2L1ParticleFlow/test/BuildFile.xml create mode 100644 L1Trigger/Phase2L1ParticleFlow/test/l1pfJetMetTreeProducer.py create mode 100644 L1Trigger/Phase2L1ParticleFlow/test/testOutputFiles.cpp diff --git a/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml b/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml new file mode 100644 index 0000000000000..80c5ef5582689 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/BitwisePFAlgo.h b/L1Trigger/Phase2L1ParticleFlow/interface/BitwisePFAlgo.h new file mode 100644 index 0000000000000..28f804be48937 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/BitwisePFAlgo.h @@ -0,0 +1,22 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_BitwisePFAlgo_h +#define L1Trigger_Phase2L1ParticleFlow_BitwisePFAlgo_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h" + +struct pfalgo_config; + +namespace l1tpf_impl { + class BitwisePFAlgo : public PFAlgoBase { + public: + BitwisePFAlgo(const edm::ParameterSet&); + ~BitwisePFAlgo() override; + void runPF(Region& r) const override; + + protected: + enum class AlgoChoice { algo3, algo2hgc } algo_; + std::shared_ptr config_; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/COEFile.h b/L1Trigger/Phase2L1ParticleFlow/interface/COEFile.h new file mode 100644 index 0000000000000..3a61bf2ee8ecd --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/COEFile.h @@ -0,0 +1,44 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_CoeFile_h +#define L1Trigger_Phase2L1ParticleFlow_CoeFile_h + +// system include files +#include +#include +#include +#include +#include +#include + +// user include files +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputs.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/Region.h" + +namespace l1tpf_impl { + class COEFile { + public: + COEFile(const edm::ParameterSet&); + ~COEFile(); + + void close() { fclose(file); } + template + bool getBit(T value, unsigned bit) { + return (value >> bit) & 1; + } + bool is_open() { return (file != nullptr); } + void writeHeaderToFile(); + void writeTracksToFile(const std::vector& regions, bool print = false); + + protected: + FILE* file; + std::string coeFileName, bset_string_; + unsigned int ntracksmax, phiSlices; + static constexpr unsigned int tracksize = 96; + boost::dynamic_bitset<> bset_; + const std::vector track_word_block_sizes = {14, 1, 12, 16, 12, 13, 4, 3, 7, 14}; + int debug_; + }; +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/CaloClusterer.h b/L1Trigger/Phase2L1ParticleFlow/interface/CaloClusterer.h new file mode 100644 index 0000000000000..64f0a663b14c2 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/CaloClusterer.h @@ -0,0 +1,297 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_CALOCLUSTERER_H +#define L1Trigger_Phase2L1ParticleFlow_CALOCLUSTERER_H +/** + * Classes for calorimetric re-clustering + * */ + +// fwd declarations +namespace edm { + class ParameterSet; +} + +// real includes +#include +#include +#include +#include +#include +#include "DataFormats/L1TParticleFlow/interface/PFCluster.h" +#include "DataFormats/Common/interface/OrphanHandle.h" + +namespace l1tpf_calo { + class Grid { + public: + virtual ~Grid() {} + unsigned int size() const { return ncells_; } + virtual int find_cell(float eta, float phi) const = 0; + int neighbour(int icell, unsigned int idx) const { return neighbours_[icell][idx]; } + float eta(int icell) const { return eta_[icell]; } + float phi(int icell) const { return phi_[icell]; } + float etaWidth(int icell) const { return etaWidth_[icell]; } + float phiWidth(int icell) const { return phiWidth_[icell]; } + int ieta(int icell) const { return ieta_[icell]; } + int iphi(int icell) const { return iphi_[icell]; } + + protected: + Grid(unsigned int size) + : ncells_(size), + eta_(size), + etaWidth_(size), + phi_(size), + phiWidth_(size), + ieta_(size), + iphi_(size), + neighbours_(size) {} + unsigned int ncells_; + std::vector eta_, etaWidth_, phi_, phiWidth_; + std::vector ieta_, iphi_; + std::vector> neighbours_; // indices of the neigbours, -1 = none + }; + + class Phase1GridBase : public Grid { + public: + Phase1GridBase(int nEta, int nPhi, int ietaCoarse, int ietaVeryCoarse, const float *towerEtas); + + int find_cell(float eta, float phi) const override; + int ifind_cell(int ieta, int iphi) const { return cell_map_[(ieta + nEta_) + 2 * nEta_ * (iphi - 1)]; } + + protected: + const int nEta_, nPhi_, ietaCoarse_, ietaVeryCoarse_; + const float *towerEtas_; + std::vector cell_map_; + // valid ieta, iphi (does not check for outside bounds, only for non-existence of ieta=0, iphi=0, and coarser towers at high eta) + bool valid_ieta_iphi(int ieta, int iphi) const { + if (ieta == 0 || iphi == 0) + return false; + if (std::abs(ieta) >= ietaVeryCoarse_ && (iphi % 4 != 1)) + return false; + if (std::abs(ieta) >= ietaCoarse_ && (iphi % 2 != 1)) + return false; + return true; + } + // move by +/-1 around a cell; return icell or -1 if not available + int imove(int ieta, int iphi, int deta, int dphi); + }; + + class Phase1Grid : public Phase1GridBase { + public: + Phase1Grid() + : Phase1GridBase(phase1_nEta_, phase1_nPhi_, phase1_ietaCoarse_, phase1_ietaVeryCoarse_, phase1_towerEtas_) {} + + protected: + static const int phase1_nEta_ = 41, phase1_nPhi_ = 72, phase1_ietaCoarse_ = 29, phase1_ietaVeryCoarse_ = 40; + static const float phase1_towerEtas_[phase1_nEta_]; + }; + class Phase2Grid : public Phase1GridBase { + public: + Phase2Grid() + : Phase1GridBase(phase2_nEta_, phase2_nPhi_, phase2_ietaCoarse_, phase2_ietaVeryCoarse_, phase2_towerEtas_) {} + + protected: + static const int phase2_nEta_ = 48, phase2_nPhi_ = 72, phase2_ietaCoarse_ = 36, phase2_ietaVeryCoarse_ = 47; + static const float phase2_towerEtas_[phase2_nEta_]; + }; + + template + class GridData { + public: + GridData() : grid_(nullptr), data_(), empty_() {} + GridData(const Grid &grid) : grid_(&grid), data_(grid.size()), empty_() {} + + T &operator()(float eta, float phi) { return data_[grid_->find_cell(eta, phi)]; } + const T &operator()(float eta, float phi) const { return data_[grid_->find_cell(eta, phi)]; } + + const Grid &grid() const { return *grid_; } + + unsigned int size() const { return data_.size(); } + + float eta(int icell) const { return grid().eta(icell); } + float phi(int icell) const { return grid().phi(icell); } + int ieta(int icell) const { return grid().ieta(icell); } + int iphi(int icell) const { return grid().iphi(icell); } + + T &operator[](int icell) { return data_[icell]; } + const T &operator[](int icell) const { return data_[icell]; } + + const T &neigh(int icell, unsigned int idx) const { + int ineigh = grid_->neighbour(icell, idx); + return (ineigh < 0 ? empty_ : data_[ineigh]); + } + + GridData &operator=(const GridData &other) { + assert(grid_ == other.grid_); + data_ = other.data_; + return *this; + } + GridData &operator+=(const GridData &other) { + assert(grid_ == other.grid_); + for (unsigned int i = 0, n = data_.size(); i < n; ++i) { + data_[i] += other.data_[i]; + } + return *this; + } + + // always defined + void fill(const T &val) { std::fill(data_.begin(), data_.end(), val); } + void zero() { fill(T()); } + + // defined only if T has a 'clear' method + void clear() { + for (T &t : data_) + t.clear(); + } + + private: + const Grid *grid_; + std::vector data_; + const T empty_; + }; + typedef GridData EtGrid; + typedef GridData IndexGrid; + + struct PreCluster { + PreCluster() : ptLocalMax(0), ptOverNeighLocalMaxSum(0) {} + float ptLocalMax; // pt if it's a local max, zero otherwise + float ptOverNeighLocalMaxSum; // pt / (sum of ptLocalMax of neighbours); zero if no neighbours + void clear() { ptLocalMax = ptOverNeighLocalMaxSum = 0; } + }; + typedef GridData PreClusterGrid; + + struct Cluster { + Cluster() : et(0), eta(0), phi(0) {} + float et, eta, phi; + std::vector> constituents; + void clear() { + et = eta = phi = 0; + constituents.clear(); + } + }; + + struct CombinedCluster : public Cluster { + float ecal_et, hcal_et; + void clear() { + Cluster::clear(); + ecal_et = hcal_et = 0; + } + }; + + const Grid *getGrid(const std::string &type); + + class SingleCaloClusterer { + public: + SingleCaloClusterer(const edm::ParameterSet &pset); + ~SingleCaloClusterer(); + void clear(); + void add(const reco::Candidate &c) { add(c.pt(), c.eta(), c.phi()); } + void add(float pt, float eta, float phi) { rawet_(eta, phi) += pt; } + void run(); + + /// possibly grow clusters by adding unclustered energy on the sides + // note: there can be some double-counting as the same unclustered energy can go into more clusters + void grow(); + + const EtGrid &raw() const { return rawet_; } + const IndexGrid &indexGrid() const { return clusterIndex_; } + const std::vector &clusters() const { return clusters_; } + const Cluster &cluster(int i) const { + return (i == -1 || clusterIndex_[i] == -1) ? nullCluster_ : clusters_[clusterIndex_[i]]; + } + + /// non-const access to the energy: be careful to use it only before 'run()' + EtGrid &raw() { return rawet_; } + + // for the moment, generic interface that takes a cluster and returns the corrected pt + template + void correct(const Corrector &corrector) { + for (Cluster &c : clusters_) { + c.et = corrector(c); + } + } + + std::unique_ptr fetchCells(bool unclusteredOnly = false, float ptMin = 0.) const; + + std::unique_ptr fetch(float ptMin = 0.) const; + std::unique_ptr fetch(const edm::OrphanHandle &cells, + float ptMin = 0.) const; + + private: + enum class EnergyShareAlgo { + Fractions, /* each local maximum neighbour takes a share proportional to its value */ + None, /* each local maximum neighbour takes all the value (double counting!) */ + Greedy, /* assing cell to the highest local maximum neighbour */ + Crude + }; /* if there's more than one local maximum neighbour, they all take half of the value (no fp division) */ + const Grid *grid_; + EtGrid rawet_, unclustered_; + PreClusterGrid precluster_; + IndexGrid clusterIndex_, cellKey_; + std::vector clusters_; + const Cluster nullCluster_; + float zsEt_, seedEt_, minClusterEt_, minEtToGrow_; + EnergyShareAlgo energyShareAlgo_; + bool energyWeightedPosition_; // do the energy-weighted cluster position instead of the cell center + }; + + class SimpleCaloLinkerBase { + public: + SimpleCaloLinkerBase(const edm::ParameterSet &pset, + const SingleCaloClusterer &ecal, + const SingleCaloClusterer &hcal); + virtual ~SimpleCaloLinkerBase(); + virtual void clear() { clearBase(); } + virtual void run() = 0; + void clearBase() { + clusters_.clear(); + clusterIndex_.fill(-1); + } + + // for the moment, generic interface that takes a cluster and returns the corrected pt + template + void correct(const Corrector &corrector) { + for (CombinedCluster &c : clusters_) { + c.et = corrector(c); + } + } + + std::unique_ptr fetch() const; + std::unique_ptr fetch(const edm::OrphanHandle &ecal, + const edm::OrphanHandle &hcal) const; + + protected: + const Grid *grid_; + const SingleCaloClusterer &ecal_, &hcal_; + IndexGrid clusterIndex_; + std::vector clusters_; + float hoeCut_, minPhotonEt_, minHadronRawEt_, minHadronEt_; + bool noEmInHGC_; + }; + + class SimpleCaloLinker : public SimpleCaloLinkerBase { + public: + SimpleCaloLinker(const edm::ParameterSet &pset, const SingleCaloClusterer &ecal, const SingleCaloClusterer &hcal); + ~SimpleCaloLinker() override; + void clear() override; + void run() override; + + protected: + PreClusterGrid ecalToHCal_; + }; + class FlatCaloLinker : public SimpleCaloLinkerBase { + public: + FlatCaloLinker(const edm::ParameterSet &pset, const SingleCaloClusterer &ecal, const SingleCaloClusterer &hcal); + ~FlatCaloLinker() override; + void clear() override; + void run() override; + + protected: + SingleCaloClusterer combClusterer_; + }; + + // makes a calo linker (pointer will be owned by the callee) + std::unique_ptr makeCaloLinker(const edm::ParameterSet &pset, + const SingleCaloClusterer &ecal, + const SingleCaloClusterer &hcal); + +} // namespace l1tpf_calo + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputs.h b/L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputs.h new file mode 100644 index 0000000000000..4de6832b8806b --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputs.h @@ -0,0 +1,259 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_H +#define L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_H + +#if defined(__GXX_EXPERIMENTAL_CXX0X__) or defined(CMSSW) +#include +#include +#define L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE +#else +#include +#endif + +namespace l1t { + class PFTrack; + class PFCluster; + class PFCandidate; + class Muon; +} // namespace l1t + +// the serialization may be hidden if needed +#include +#include + +namespace l1tpf_impl { + + struct CaloCluster { + int16_t hwPt; + int16_t hwEmPt; + int16_t hwPtErr; + int16_t hwEta; + int16_t hwPhi; + uint16_t hwFlags; + bool isEM, used; + const l1t::PFCluster *src; + + // sorting + bool operator<(const CaloCluster &other) const { return hwPt > other.hwPt; } + +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + static constexpr float PT_SCALE = 4.0; // quantize in units of 0.25 GeV (can be changed) + static constexpr float ETAPHI_FACTOR = 4; // size of an ecal crystal in phi in integer units (our choice) + static constexpr float ETAPHI_SCALE = + ETAPHI_FACTOR * + (180. / M_PI); // M_PI/180 is the size of an ECal crystal; we make a grid that is 4 times that size + static constexpr int16_t PHI_WRAP = 360 * ETAPHI_FACTOR; // what is 3.14 in integer + + static int16_t ptToInt16(float pt) { // avoid overflows + return std::min(round(pt * CaloCluster::PT_SCALE), std::numeric_limits::max()); + } + + // filling from floating point + void fill(float pt, + float emPt, + float ptErr, + float eta, + float phi, + bool em, + unsigned int flags, + const l1t::PFCluster *source = nullptr) { + hwPt = CaloCluster::ptToInt16(pt); + hwEmPt = CaloCluster::ptToInt16(emPt); + hwPtErr = CaloCluster::ptToInt16(ptErr); + hwEta = round(eta * CaloCluster::ETAPHI_SCALE); + hwPhi = int16_t(round(phi * CaloCluster::ETAPHI_SCALE)) % CaloCluster::PHI_WRAP; + isEM = em; + used = false; + hwFlags = flags; + src = source; + } + + float floatPt() const { return float(hwPt) / CaloCluster::PT_SCALE; } + float floatEmPt() const { return float(hwEmPt) / CaloCluster::PT_SCALE; } + float floatPtErr() const { return float(hwPtErr) / CaloCluster::PT_SCALE; } + static float minFloatPt() { return float(1.0) / CaloCluster::PT_SCALE; } + float floatEta() const { return float(hwEta) / CaloCluster::ETAPHI_SCALE; } + float floatPhi() const { return float(hwPhi) / CaloCluster::ETAPHI_SCALE; } + void setFloatPt(float pt) { hwPt = round(pt * CaloCluster::PT_SCALE); } + void setFloatEmPt(float emPt) { hwEmPt = round(emPt * CaloCluster::PT_SCALE); } +#endif + }; + + // https://twiki.cern.ch/twiki/bin/view/CMS/L1TriggerPhase2InterfaceSpecifications + struct InputTrack { + uint16_t hwInvpt; + int32_t hwVtxEta; + int32_t hwVtxPhi; + bool hwCharge; + int16_t hwZ0; + uint16_t hwChi2, hwStubs; + uint16_t hwFlags; + const l1t::PFTrack *src; + +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + static constexpr float INVPT_SCALE = 2E4; // 1%/pt @ 100 GeV is 2 bits + static constexpr float VTX_PHI_SCALE = 1 / 1.6E-3; // 5 micro rad is 2 bits + static constexpr float VTX_ETA_SCALE = 1 / 1E-4; // no idea, but assume it's somewhat worse than phi + static constexpr float Z0_SCALE = 20; // 1mm is 2 bits + static constexpr int32_t VTX_ETA_1p3 = 1.3 * InputTrack::VTX_ETA_SCALE; + + // filling from floating point + void fillInput( + float pt, float eta, float phi, int charge, float dz, unsigned int flags, const l1t::PFTrack *source = nullptr) { + hwInvpt = std::min(round(1 / pt * InputTrack::INVPT_SCALE), std::numeric_limits::max()); + hwVtxEta = round(eta * InputTrack::VTX_ETA_SCALE); + hwVtxPhi = round(phi * InputTrack::VTX_PHI_SCALE); + hwCharge = (charge > 0); + hwZ0 = round(dz * InputTrack::Z0_SCALE); + hwFlags = flags; + src = source; + } + + float floatVtxPt() const { return 1 / (float(hwInvpt) / InputTrack::INVPT_SCALE); } + float floatVtxEta() const { return float(hwVtxEta) / InputTrack::VTX_ETA_SCALE; } + float floatVtxPhi() const { return float(hwVtxPhi) / InputTrack::VTX_PHI_SCALE; } + float floatDZ() const { return float(hwZ0) / InputTrack::Z0_SCALE; } + int intCharge() const { return hwCharge ? +1 : -1; } +#endif + }; + + struct PropagatedTrack : public InputTrack { + int16_t hwPt; + int16_t hwPtErr; + int16_t hwCaloPtErr; + int16_t hwEta; // at calo + int16_t hwPhi; // at calo + bool muonLink; + bool used; // note: this flag is not used in the default PF, but is used in alternative algos + bool fromPV; + + // sorting + bool operator<(const PropagatedTrack &other) const { return hwPt > other.hwPt; } + +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + void fillPropagated( + float pt, float ptErr, float caloPtErr, float caloEta, float caloPhi, unsigned int quality, bool isMuon) { + hwPt = CaloCluster::ptToInt16(pt); + hwPtErr = CaloCluster::ptToInt16(ptErr); + hwCaloPtErr = CaloCluster::ptToInt16(caloPtErr); + // saturation protection + if (hwPt == std::numeric_limits::max()) { + hwCaloPtErr = hwPt / 4; + } + hwEta = round(caloEta * CaloCluster::ETAPHI_SCALE); + hwPhi = int16_t(round(caloPhi * CaloCluster::ETAPHI_SCALE)) % CaloCluster::PHI_WRAP; + muonLink = isMuon; + used = false; + } + + float floatPt() const { return float(hwPt) / CaloCluster::PT_SCALE; } + float floatPtErr() const { return float(hwPtErr) / CaloCluster::PT_SCALE; } + float floatCaloPtErr() const { return float(hwCaloPtErr) / CaloCluster::PT_SCALE; } + float floatEta() const { return float(hwEta) / CaloCluster::ETAPHI_SCALE; } + float floatPhi() const { return float(hwPhi) / CaloCluster::ETAPHI_SCALE; } +#endif + }; + + struct Muon { + int16_t hwPt; + int16_t hwEta; // at calo + int16_t hwPhi; // at calo + uint16_t hwFlags; + bool hwCharge; + const l1t::Muon *src; + + // sorting + bool operator<(const Muon &other) const { return hwPt > other.hwPt; } + +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + void fill(float pt, float eta, float phi, int charge, unsigned int flags, const l1t::Muon *source = nullptr) { + // we assume we use the same discrete ieta, iphi grid for all particles + hwPt = round(pt * CaloCluster::PT_SCALE); + hwEta = round(eta * CaloCluster::ETAPHI_SCALE); + hwPhi = int16_t(round(phi * CaloCluster::ETAPHI_SCALE)) % CaloCluster::PHI_WRAP; + hwCharge = (charge > 0); + hwFlags = flags; + src = source; + } + float floatPt() const { return float(hwPt) / CaloCluster::PT_SCALE; } + float floatEta() const { return float(hwEta) / CaloCluster::ETAPHI_SCALE; } + float floatPhi() const { return float(hwPhi) / CaloCluster::ETAPHI_SCALE; } + int intCharge() const { return hwCharge ? +1 : -1; } +#endif + }; + + struct PFParticle { + int16_t hwPt; + int16_t hwEta; // at calo face + int16_t hwPhi; + uint8_t hwId; // CH=0, EL=1, NH=2, GAMMA=3, MU=4 + int16_t hwVtxEta; // propagate back to Vtx for charged particles (if useful?) + int16_t hwVtxPhi; + uint16_t hwFlags; + CaloCluster cluster; + PropagatedTrack track; + bool chargedPV; + uint16_t hwPuppiWeight; + uint16_t hwStatus; // for debugging + const l1t::Muon *muonsrc; + const l1t::PFCandidate *src; + + // sorting + bool operator<(const PFParticle &other) const { return hwPt > other.hwPt; } + +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + static constexpr float PUPPI_SCALE = 100; + + float floatPt() const { return float(hwPt) / CaloCluster::PT_SCALE; } + float floatEta() const { return float(hwEta) / CaloCluster::ETAPHI_SCALE; } + float floatPhi() const { return float(hwPhi) / CaloCluster::ETAPHI_SCALE; } + float floatVtxEta() const { + return (track.hwPt > 0 ? track.floatVtxEta() : float(hwVtxEta) / CaloCluster::ETAPHI_SCALE); + } + float floatVtxPhi() const { + return (track.hwPt > 0 ? track.floatVtxPhi() : float(hwVtxPhi) / CaloCluster::ETAPHI_SCALE); + } + float floatDZ() const { return float(track.hwZ0) / InputTrack::Z0_SCALE; } + float floatPuppiW() const { return float(hwPuppiWeight) / PUPPI_SCALE; } + int intCharge() const { return (track.hwPt > 0 ? track.intCharge() : 0); } + void setPuppiW(float w) { hwPuppiWeight = std::round(w * PUPPI_SCALE); } + void setFloatPt(float pt) { hwPt = round(pt * CaloCluster::PT_SCALE); } +#endif + }; + + struct InputRegion { + float etaCenter, etaMin, etaMax, phiCenter, phiHalfWidth; + float etaExtra, phiExtra; + std::vector calo; + std::vector emcalo; + std::vector track; + std::vector muon; + + InputRegion() + : etaCenter(), + etaMin(), + etaMax(), + phiCenter(), + phiHalfWidth(), + etaExtra(), + phiExtra(), + calo(), + emcalo(), + track(), + muon() {} + InputRegion( + float etacenter, float etamin, float etamax, float phicenter, float phihalfwidth, float etaextra, float phiextra) + : etaCenter(etacenter), + etaMin(etamin), + etaMax(etamax), + phiCenter(phicenter), + phiHalfWidth(phihalfwidth), + etaExtra(etaextra), + phiExtra(phiextra), + calo(), + emcalo(), + track(), + muon() {} + }; + +} // namespace l1tpf_impl +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h b/L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h new file mode 100644 index 0000000000000..ddb8b4f526b57 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h @@ -0,0 +1,143 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputsIO_H +#define L1Trigger_Phase2L1ParticleFlow_DiscretePFInputsIO_H + +#include +#include +#include + +#include "DiscretePFInputs.h" + +namespace l1tpf_impl { + void writeToFile(const CaloCluster &c, FILE *file) { + fwrite(&c.hwPt, 2, 1, file); + fwrite(&c.hwEmPt, 2, 1, file); + fwrite(&c.hwPtErr, 2, 1, file); + fwrite(&c.hwEta, 2, 1, file); + fwrite(&c.hwPhi, 2, 1, file); + fwrite(&c.hwFlags, 2, 1, file); + fwrite(&c.isEM, 1, 1, file); + // used is not written out + // src is not written out + } + void readFromFile(CaloCluster &c, FILE *file) { + fread(&c.hwPt, 2, 1, file); + fread(&c.hwEmPt, 2, 1, file); + fread(&c.hwPtErr, 2, 1, file); + fread(&c.hwEta, 2, 1, file); + fread(&c.hwPhi, 2, 1, file); + fread(&c.hwFlags, 2, 1, file); + fread(&c.isEM, 1, 1, file); + c.used = false; + c.src = nullptr; + } + + void writeToFile(const InputTrack &t, FILE *file) { + fwrite(&t.hwInvpt, 2, 1, file); + fwrite(&t.hwVtxEta, 4, 1, file); + fwrite(&t.hwVtxPhi, 4, 1, file); + fwrite(&t.hwCharge, 1, 1, file); + fwrite(&t.hwZ0, 2, 1, file); + fwrite(&t.hwChi2, 2, 1, file); + fwrite(&t.hwStubs, 2, 1, file); + fwrite(&t.hwFlags, 2, 1, file); + // src is not written out + } + void readFromFile(InputTrack &t, FILE *file) { + fread(&t.hwInvpt, 2, 1, file); + fread(&t.hwVtxEta, 4, 1, file); + fread(&t.hwVtxPhi, 4, 1, file); + fread(&t.hwCharge, 1, 1, file); + fread(&t.hwZ0, 2, 1, file); + fread(&t.hwChi2, 2, 1, file); + fread(&t.hwStubs, 2, 1, file); + fread(&t.hwFlags, 2, 1, file); + t.src = nullptr; + } + void writeToFile(const PropagatedTrack &t, FILE *file) { + writeToFile(static_cast(t), file); + fwrite(&t.hwPt, 2, 1, file); + fwrite(&t.hwPtErr, 2, 1, file); + fwrite(&t.hwCaloPtErr, 2, 1, file); + fwrite(&t.hwEta, 2, 1, file); + fwrite(&t.hwPhi, 2, 1, file); + // muonLink, used, fromPV are transient + } + void readFromFile(PropagatedTrack &t, FILE *file) { + readFromFile(static_cast(t), file); + fread(&t.hwPt, 2, 1, file); + fread(&t.hwPtErr, 2, 1, file); + fread(&t.hwCaloPtErr, 2, 1, file); + fread(&t.hwEta, 2, 1, file); + fread(&t.hwPhi, 2, 1, file); + t.muonLink = false; + t.used = false; + t.fromPV = false; + } + + void writeToFile(const Muon &m, FILE *file) { + fwrite(&m.hwPt, 2, 1, file); + fwrite(&m.hwEta, 2, 1, file); + fwrite(&m.hwPhi, 2, 1, file); + fwrite(&m.hwFlags, 2, 1, file); + fwrite(&m.hwCharge, 1, 1, file); + } + void readFromFile(Muon &m, FILE *file) { + fread(&m.hwPt, 2, 1, file); + fread(&m.hwEta, 2, 1, file); + fread(&m.hwPhi, 2, 1, file); + fread(&m.hwFlags, 2, 1, file); + fread(&m.hwCharge, 1, 1, file); + m.src = nullptr; + } + + void writeToFile(const float &pug, FILE *file) { fwrite(&pug, sizeof(float), 1, file); } + void readFromFile(float &pug, FILE *file) { fread(&pug, sizeof(float), 1, file); } + + template + void writeManyToFile(const std::vector &objs, FILE *file) { + uint32_t number = objs.size(); + fwrite(&number, 4, 1, file); + for (uint32_t i = 0; i < number; ++i) + writeToFile(objs[i], file); + } + + template + void readManyFromFile(std::vector &objs, FILE *file) { + uint32_t number; + fread(&number, 4, 1, file); + objs.resize(number); + for (uint32_t i = 0; i < number; ++i) + readFromFile(objs[i], file); + } + + void writeToFile(const InputRegion &r, FILE *file) { + assert(4 == sizeof(float)); + fwrite(&r.etaCenter, 4, 1, file); + fwrite(&r.etaMin, 4, 1, file); + fwrite(&r.etaMax, 4, 1, file); + fwrite(&r.phiCenter, 4, 1, file); + fwrite(&r.phiHalfWidth, 4, 1, file); + fwrite(&r.etaExtra, 4, 1, file); + fwrite(&r.phiExtra, 4, 1, file); + writeManyToFile(r.calo, file); + writeManyToFile(r.emcalo, file); + writeManyToFile(r.track, file); + writeManyToFile(r.muon, file); + } + void readFromFile(InputRegion &r, FILE *file) { + assert(4 == sizeof(float)); + fread(&r.etaCenter, 4, 1, file); + fread(&r.etaMin, 4, 1, file); + fread(&r.etaMax, 4, 1, file); + fread(&r.phiCenter, 4, 1, file); + fread(&r.phiHalfWidth, 4, 1, file); + fread(&r.etaExtra, 4, 1, file); + fread(&r.phiExtra, 4, 1, file); + readManyFromFile(r.calo, file); + readManyFromFile(r.emcalo, file); + readManyFromFile(r.track, file); + readManyFromFile(r.muon, file); + } + +} // namespace l1tpf_impl +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/HGC3DClusterEgID.h b/L1Trigger/Phase2L1ParticleFlow/interface/HGC3DClusterEgID.h new file mode 100644 index 0000000000000..8eae585f77f1c --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/HGC3DClusterEgID.h @@ -0,0 +1,53 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_HGC3DClusterEgID_h +#define L1Trigger_Phase2L1ParticleFlow_HGC3DClusterEgID_h +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "DataFormats/L1THGCal/interface/HGCalMulticluster.h" +#include "DataFormats/L1TParticleFlow/interface/PFCluster.h" +#include "CommonTools/Utils/interface/StringCutObjectSelector.h" +#include "CommonTools/Utils/interface/StringObjectFunction.h" + +#include "TMVA/Factory.h" +#include "TMVA/Reader.h" + +#include +#include + +namespace l1tpf { + class HGC3DClusterEgID { + public: + HGC3DClusterEgID(const edm::ParameterSet &pset); + + void prepareTMVA(); + + float passID(l1t::HGCalMulticluster c, l1t::PFCluster &cpf); + + std::string method() { return method_; } + + private: + class Var { + public: + Var(const std::string &name, const std::string &expr) : name_(name), expr_(expr) {} + void declare(TMVA::Reader &r) { r.AddVariable(name_, &val_); } + void fill(const l1t::HGCalMulticluster &c) { val_ = expr_(c); } + + private: + std::string name_; + StringObjectFunction expr_; + float val_; + }; + + bool isPUFilter_; + StringCutObjectSelector preselection_; + std::vector variables_; + std::string method_, weightsFile_; + std::unique_ptr reader_; + StringObjectFunction wp_; + }; //class +}; // namespace l1tpf + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/L1TPFUtils.h b/L1Trigger/Phase2L1ParticleFlow/interface/L1TPFUtils.h new file mode 100644 index 0000000000000..525b780afaf46 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/L1TPFUtils.h @@ -0,0 +1,13 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_L1TPFUtils_h +#define L1Trigger_Phase2L1ParticleFlow_L1TPFUtils_h +#include +#include "DataFormats/Math/interface/LorentzVector.h" + +namespace l1tpf { + std::pair propagateToCalo(const math::XYZTLorentzVector& iMom, + const math::XYZTLorentzVector& iVtx, + double iCharge, + double iBField); +} + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/LinearizedPuppiAlgo.h b/L1Trigger/Phase2L1ParticleFlow/interface/LinearizedPuppiAlgo.h new file mode 100644 index 0000000000000..fe5bd5378ec03 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/LinearizedPuppiAlgo.h @@ -0,0 +1,33 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_LinearizedPuppiAlgo_h +#define L1Trigger_Phase2L1ParticleFlow_LinearizedPuppiAlgo_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h" + +namespace l1tpf_impl { + + class LinearizedPuppiAlgo : public PuppiAlgo { + public: + LinearizedPuppiAlgo(const edm::ParameterSet &); + ~LinearizedPuppiAlgo() override; + + const std::vector &puGlobalNames() const override; + void doPUGlobals(const std::vector &rs, float npu, std::vector &globals) const override; + void runNeutralsPU(Region &r, float npu, const std::vector &globals) const override; + + protected: + void computePuppiWeights(Region &r, + float npu, + const std::vector &alphaC, + const std::vector &alphaF) const; + + std::vector puppiPriors_, puppiPriorsPhotons_; + std::vector puppiPtSlopes_, puppiPtSlopesPhotons_; + std::vector puppiPtZeros_, puppiPtZerosPhotons_; + std::vector puppiAlphaSlopes_, puppiAlphaSlopesPhotons_; + std::vector puppiAlphaZeros_, puppiAlphaZerosPhotons_; + std::vector puppiAlphaCrops_, puppiAlphaCropsPhotons_; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo2HGC.h b/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo2HGC.h new file mode 100644 index 0000000000000..c6342d9efddfe --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo2HGC.h @@ -0,0 +1,76 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PFAlgo2HGC_h +#define L1Trigger_Phase2L1ParticleFlow_PFAlgo2HGC_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h" + +namespace l1tpf_impl { + class PFAlgo2HGC : public PFAlgoBase { + public: + PFAlgo2HGC(const edm::ParameterSet &); + void runPF(Region &r) const override; + + protected: + float drMatchMu_; + enum class MuMatchMode { BoxBestByPtRatio, DrBestByPtRatio, DrBestByPtDiff } muMatchMode_; + float drMatch_, ptMatchLow_, ptMatchHigh_, maxInvisiblePt_; + bool useTrackCaloSigma_, rescaleUnmatchedTrack_, caloTrkWeightedAverage_; + enum class TkCaloLinkMetric { BestByDR = 0, BestByDRPt = 1, BestByDR2Pt2 = 2 }; + TkCaloLinkMetric tkCaloLinkMetric_; + bool caloReLinkStep_; + float caloReLinkDr_, caloReLinkThreshold_; + bool rescaleTracks_, sumTkCaloErr2_, ecalPriority_, trackEmUseAlsoTrackSigma_, emCaloUseAlsoCaloSigma_; + unsigned int tightTrackMinStubs_; + float tightTrackMaxChi2_, tightTrackMaxInvisiblePt_; + enum GoodTrackStatus { GoodTK_Calo_TkPt = 0, GoodTK_Calo_TkCaloPt = 1, GoodTk_Calo_CaloPt = 2, GoodTK_NoCalo = 3 }; + enum BadTrackStatus { BadTK_NoCalo = 1 }; + + /// do muon track linking (also sets track.muonLink) + void link_tk2mu(Region &r, std::vector &tk2mu, std::vector &mu2tk) const; + + /// track to calo matching + // tk2calo[itk] = icalo or -1 + void link_tk2calo(Region &r, std::vector &tk2calo) const; + + /// for each calo, compute the sum of the track pt + void sum_tk2calo(Region &r, + const std::vector &tk2calo, + std::vector &calo2ntk, + std::vector &calo2sumtkpt, + std::vector &calo2sumtkpterr) const; + + /// promote unlinked low pt tracks to hadrons + void unlinkedtk_algo(Region &r, const std::vector &tk2calo) const; + + /// try to recover split hadron showers (v1.0): + // take hadrons that are not track matched, close by a hadron which has an excess of track pt vs calo pt + // add this pt to the calo pt of the other cluster + // off by default, as it seems to not do much in jets even if it helps remove tails in single-pion events + void calo_relink(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr) const; + + /// process matched calo clusters, compare energy to sum track pt, compute track rescaling factor if needed + // alpha[icalo] = x < 1 if all tracks linked to icalo must have their pt rescaled by x + void linkedcalo_algo(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr, + std::vector &calo2alpha) const; + + /// process matched tracks, if necessary rescale or average + void linkedtk_algo(Region &r, + const std::vector &tk2calo, + const std::vector &calo2ntk, + const std::vector &calo2alpha) const; + + /// process unmatched calo clusters + void unlinkedcalo_algo(Region &r) const; + + /// save muons in output list + void save_muons(Region &r, const std::vector &tk2mu) const; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo3.h b/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo3.h new file mode 100644 index 0000000000000..5972218dc10fd --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo3.h @@ -0,0 +1,109 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PFAlgo3_h +#define L1Trigger_Phase2L1ParticleFlow_PFAlgo3_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h" + +namespace l1tpf_impl { + class PFAlgo3 : public PFAlgoBase { + public: + PFAlgo3(const edm::ParameterSet &); + void runPF(Region &r) const override; + + protected: + float drMatchMu_; + enum class MuMatchMode { BoxBestByPtRatio, DrBestByPtRatio, DrBestByPtDiff } muMatchMode_; + float drMatch_, ptMatchLow_, ptMatchHigh_, maxInvisiblePt_; + bool useTrackCaloSigma_, rescaleUnmatchedTrack_, caloTrkWeightedAverage_; + enum class TkCaloLinkMetric { BestByDR = 0, BestByDRPt = 1, BestByDR2Pt2 = 2 }; + float drMatchEm_, ptMinFracMatchEm_, drMatchEmHad_; + float emHadSubtractionPtSlope_; + TkCaloLinkMetric tkCaloLinkMetric_; + bool caloReLinkStep_; + float caloReLinkDr_, caloReLinkThreshold_; + bool rescaleTracks_, sumTkCaloErr2_, ecalPriority_, trackEmUseAlsoTrackSigma_, trackEmMayUseCaloMomenta_, + emCaloUseAlsoCaloSigma_; + unsigned int tightTrackMinStubs_; + float tightTrackMaxChi2_, tightTrackMaxInvisiblePt_; + enum GoodTrackStatus { GoodTK_Calo_TkPt = 0, GoodTK_Calo_TkCaloPt = 1, GoodTk_Calo_CaloPt = 2, GoodTK_NoCalo = 3 }; + enum BadTrackStatus { BadTK_NoCalo = 1 }; + + /// do muon track linking (also sets track.muonLink) + void link_tk2mu(Region &r, std::vector &tk2mu, std::vector &mu2tk) const; + + /// match all tracks to the closest EM cluster + // tk2em[itrack] = iem, or -1 if unmatched + void link_tk2em(Region &r, std::vector &tk2em) const; + + /// match all em to the closest had (can happen in parallel to the above) + // em2calo[iem] = icalo or -1 + void link_em2calo(Region &r, std::vector &em2calo) const; + + /// for each EM cluster, count and add up the pt of all the corresponding tracks (skipping muons) + void sum_tk2em(Region &r, + const std::vector &tk2em, + std::vector &em2ntk, + std::vector &em2sumtkpt, + std::vector &em2sumtkpterr) const; + + /// process ecal clusters after linking + void emcalo_algo(Region &r, + const std::vector &em2ntk, + const std::vector &em2sumtkpt, + const std::vector &em2sumtkpterr) const; + + /// promote all flagged tracks to electrons + void emtk_algo(Region &r, + const std::vector &tk2em, + const std::vector &em2ntk, + const std::vector &em2sumtkpterr) const; + + /// subtract EM component from Calo clusters for all photons and electrons (within tracker coverage) + void sub_em2calo(Region &r, const std::vector &em2calo) const; + + /// track to calo matching + // tk2calo[itk] = icalo or -1 + void link_tk2calo(Region &r, std::vector &tk2calo) const; + + /// for each calo, compute the sum of the track pt + void sum_tk2calo(Region &r, + const std::vector &tk2calo, + std::vector &calo2ntk, + std::vector &calo2sumtkpt, + std::vector &calo2sumtkpterr) const; + + /// promote unlinked low pt tracks to hadrons + void unlinkedtk_algo(Region &r, const std::vector &tk2calo) const; + + /// try to recover split hadron showers (v1.0): + // take hadrons that are not track matched, close by a hadron which has an excess of track pt vs calo pt + // add this pt to the calo pt of the other cluster + // off by default, as it seems to not do much in jets even if it helps remove tails in single-pion events + void calo_relink(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr) const; + + /// process matched calo clusters, compare energy to sum track pt, compute track rescaling factor if needed + // alpha[icalo] = x < 1 if all tracks linked to icalo must have their pt rescaled by x + void linkedcalo_algo(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr, + std::vector &calo2alpha) const; + + /// process matched tracks, if necessary rescale or average + void linkedtk_algo(Region &r, + const std::vector &tk2calo, + const std::vector &calo2ntk, + const std::vector &calo2alpha) const; + + /// process unmatched calo clusters + void unlinkedcalo_algo(Region &r) const; + + /// save muons in output list + void save_muons(Region &r, const std::vector &tk2mu) const; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h b/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h new file mode 100644 index 0000000000000..f0286a221222f --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h @@ -0,0 +1,28 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PFAlgoBase_h +#define L1Trigger_Phase2L1ParticleFlow_PFAlgoBase_h + +#include + +#include "L1Trigger/Phase2L1ParticleFlow/interface/Region.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +namespace l1tpf_impl { + + class PFAlgoBase { + public: + PFAlgoBase(const edm::ParameterSet &); + virtual ~PFAlgoBase(); + virtual void runPF(Region &r) const = 0; + + protected: + int debug_; + void initRegion(Region &r) const; + PFParticle &addTrackToPF(Region &r, const PropagatedTrack &tk) const { return addTrackToPF(r.pf, tk); } + PFParticle &addCaloToPF(Region &r, const CaloCluster &calo) const { return addCaloToPF(r.pf, calo); } + PFParticle &addTrackToPF(std::vector &pfs, const PropagatedTrack &tk) const; + PFParticle &addCaloToPF(std::vector &pfs, const CaloCluster &calo) const; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/PUAlgoBase.h b/L1Trigger/Phase2L1ParticleFlow/interface/PUAlgoBase.h new file mode 100644 index 0000000000000..0b6cfa60f6fca --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/PUAlgoBase.h @@ -0,0 +1,34 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PUAlgoBase_h +#define L1Trigger_Phase2L1ParticleFlow_PUAlgoBase_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/Region.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +namespace l1tpf_impl { + + class PUAlgoBase { + public: + PUAlgoBase(const edm::ParameterSet &); + virtual ~PUAlgoBase(); + + /// global operations + enum class VertexAlgo { Old, TP, External }; + virtual void doVertexing(std::vector &rs, + VertexAlgo algo, + float &vz) const; // region is not const since it sets the fromPV bit of the tracks + + virtual void runChargedPV(Region &r, float z0) const; + + virtual const std::vector &puGlobalNames() const; + virtual void doPUGlobals(const std::vector &rs, float npu, std::vector &globals) const = 0; + virtual void runNeutralsPU(Region &r, float npu, const std::vector &globals) const = 0; + + protected: + int debug_; + float etaCharged_, vtxRes_; + bool vtxAdaptiveCut_; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h b/L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h new file mode 100644 index 0000000000000..6079f96807beb --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h @@ -0,0 +1,27 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_ParametricResolution_h +#define L1Trigger_Phase2L1ParticleFlow_ParametricResolution_h +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/Exception.h" +#include +#include + +namespace l1tpf { + + class ParametricResolution { + public: + static std::vector getVFloat(const edm::ParameterSet &cpset, const std::string &name); + + ParametricResolution() {} + ParametricResolution(const edm::ParameterSet &cpset); + + float operator()(const float pt, const float abseta) const; + + protected: + std::vector etas_, offsets_, scales_, ptMins_, ptMaxs_; + enum class Kind { Calo, Track }; + Kind kind_; + }; + +}; // namespace l1tpf + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h b/L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h new file mode 100644 index 0000000000000..297209cde949d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h @@ -0,0 +1,38 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PuppiAlgo_h +#define L1Trigger_Phase2L1ParticleFlow_PuppiAlgo_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/PUAlgoBase.h" + +namespace l1tpf_impl { + + class PuppiAlgo : public PUAlgoBase { + public: + PuppiAlgo(const edm::ParameterSet &); + ~PuppiAlgo() override; + + const std::vector &puGlobalNames() const override; + void doPUGlobals(const std::vector &rs, float npu, std::vector &globals) const override; + void runNeutralsPU(Region &r, float npu, const std::vector &globals) const override; + + protected: + virtual void computePuppiMedRMS( + const std::vector &rs, float &alphaCMed, float &alphaCRms, float &alphaFMed, float &alphaFRms) const; + virtual void fillPuppi(Region &r) const; + virtual void computePuppiAlphas(const Region &r, std::vector &alphaC, std::vector &alphaF) const; + void computePuppiWeights(Region &r, + const std::vector &alphaC, + const std::vector &alphaF, + float alphaCMed, + float alphaCRms, + float alphaFMed, + float alphaFRms) const; + + float puppiDr_, puppiDrMin_, puppiPtMax_; + std::vector puppiEtaCuts_, puppiPtCuts_, puppiPtCutsPhotons_; + std::vector intPuppiEtaCuts_, intPuppiPtCuts_, intPuppiPtCutsPhotons_; + bool puppiUsingBareTracks_; + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/Region.h b/L1Trigger/Phase2L1ParticleFlow/interface/Region.h new file mode 100644 index 0000000000000..b022bace35f69 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/Region.h @@ -0,0 +1,112 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_Region_h +#define L1Trigger_Phase2L1ParticleFlow_Region_h + +#include "L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputs.h" +#include "DataFormats/Math/interface/deltaPhi.h" + +namespace l1tpf_impl { + struct Region : public InputRegion { + std::vector pf; + std::vector puppi; + unsigned int caloOverflow, emcaloOverflow, trackOverflow, muonOverflow, pfOverflow, puppiOverflow; + + const bool relativeCoordinates; // whether the eta,phi in each region are global or relative to the region center + const unsigned int ncaloMax, nemcaloMax, ntrackMax, nmuonMax, npfMax, npuppiMax; + Region(float etamin, + float etamax, + float phicenter, + float phiwidth, + float etaextra, + float phiextra, + bool useRelativeCoordinates, + unsigned int ncalomax, + unsigned int nemcalomax, + unsigned int ntrackmax, + unsigned int nmuonmax, + unsigned int npfmax, + unsigned int npuppimax) + : InputRegion(0.5 * (etamin + etamax), etamin, etamax, phicenter, 0.5 * phiwidth, etaextra, phiextra), + pf(), + puppi(), + caloOverflow(), + emcaloOverflow(), + trackOverflow(), + muonOverflow(), + pfOverflow(), + puppiOverflow(), + relativeCoordinates(useRelativeCoordinates), + ncaloMax(ncalomax), + nemcaloMax(nemcalomax), + ntrackMax(ntrackmax), + nmuonMax(nmuonmax), + npfMax(npfmax), + npuppiMax(npuppimax) {} + + enum InputType { calo_type = 0, emcalo_type = 1, track_type = 2, l1mu_type = 3, n_input_types = 4 }; + static const char* inputTypeName(int inputType); + + enum OutputType { + any_type = 0, + charged_type = 1, + neutral_type = 2, + electron_type = 3, + pfmuon_type = 4, + charged_hadron_type = 5, + neutral_hadron_type = 6, + photon_type = 7, + n_output_types = 8 + }; + static const char* outputTypeName(int outputType); + + unsigned int nInput(InputType type) const; + unsigned int nOutput(OutputType type, bool puppi, bool fiducial = true) const; + + // global coordinates + bool contains(float eta, float phi) const { + float dphi = deltaPhi(phiCenter, phi); + return (etaMin - etaExtra < eta && eta <= etaMax + etaExtra && -phiHalfWidth - phiExtra < dphi && + dphi <= phiHalfWidth + phiExtra); + } + // global coordinates + bool fiducial(float eta, float phi) const { + float dphi = deltaPhi(phiCenter, phi); + return (etaMin < eta && eta <= etaMax && -phiHalfWidth < dphi && dphi <= phiHalfWidth); + } + // possibly local coordinates + bool fiducialLocal(float localEta, float localPhi) const { + if (relativeCoordinates) { + float dphi = deltaPhi(0.f, localPhi); + return (etaMin < localEta + etaCenter && localEta + etaCenter <= etaMax && -phiHalfWidth < dphi && + dphi <= phiHalfWidth); + } + float dphi = deltaPhi(phiCenter, localPhi); + return (etaMin < localEta && localEta <= etaMax && -phiHalfWidth < dphi && dphi <= phiHalfWidth); + } + float regionAbsEta() const { return std::abs(etaCenter); } + float globalAbsEta(float localEta) const { return std::abs(relativeCoordinates ? localEta + etaCenter : localEta); } + float globalEta(float localEta) const { return relativeCoordinates ? localEta + etaCenter : localEta; } + float globalPhi(float localPhi) const { return relativeCoordinates ? localPhi + phiCenter : localPhi; } + float localEta(float globalEta) const { return relativeCoordinates ? globalEta - etaCenter : globalEta; } + float localPhi(float globalPhi) const { return relativeCoordinates ? deltaPhi(globalPhi, phiCenter) : globalPhi; } + + void zero() { + calo.clear(); + emcalo.clear(); + track.clear(); + muon.clear(); + pf.clear(); + puppi.clear(); + caloOverflow = 0; + emcaloOverflow = 0; + trackOverflow = 0; + muonOverflow = 0; + pfOverflow = 0; + puppiOverflow = 0; + } + + void inputSort(); + }; + +} // namespace l1tpf_impl + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/interface/RegionMapper.h b/L1Trigger/Phase2L1ParticleFlow/interface/RegionMapper.h new file mode 100644 index 0000000000000..11ca55c1d981e --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/interface/RegionMapper.h @@ -0,0 +1,57 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_RegionMapper_h +#define L1Trigger_Phase2L1ParticleFlow_RegionMapper_h + +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/Region.h" +#include "DataFormats/Math/interface/deltaPhi.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "DataFormats/L1TCorrelator/interface/TkMuon.h" +#include "DataFormats/L1TCorrelator/interface/TkMuonFwd.h" + +#include + +namespace l1tpf_impl { + class RegionMapper { + // This does the input and filling of regions. + public: + RegionMapper(const edm::ParameterSet &); + + // add object, without tracking references + void addTrack(const l1t::PFTrack &t); + void addMuon(const l1t::Muon &t); + void addMuon(const l1t::TkMuon &t); + void addCalo(const l1t::PFCluster &t); + void addEmCalo(const l1t::PFCluster &t); + + // add object, tracking references + void addTrack(const l1t::PFTrack &t, l1t::PFTrackRef ref); + void addMuon(const l1t::Muon &t, l1t::PFCandidate::MuonRef ref); + void addCalo(const l1t::PFCluster &t, l1t::PFClusterRef ref); + void addEmCalo(const l1t::PFCluster &t, l1t::PFClusterRef ref); + + void clear(); + std::vector ®ions() { return regions_; } + + std::unique_ptr fetch(bool puppi = true, float ptMin = 0.01) const; + std::unique_ptr fetchCalo(float ptMin = 0.01, bool emcalo = false) const; + std::unique_ptr fetchTracks(float ptMin = 0.01, bool fromPV = false) const; + + std::pair totAndMaxInput(/*Region::InputType*/ int type) const; + std::pair totAndMaxOutput(/*Region::OutputType*/ int type, bool puppi) const; + std::unique_ptr> vecInput(int type) const; + std::unique_ptr> vecOutput(int type, bool puppi) const; + + protected: + std::vector regions_; + bool useRelativeRegionalCoordinates_; // whether the eta,phi in each region are global or relative to the region center + enum class TrackAssoMode { atVertex, atCalo, any = 999 } trackRegionMode_; + + // these are used to link items back + std::unordered_map clusterRefMap_; + std::unordered_map trackRefMap_; + std::unordered_map muonRefMap_; + }; + +} // namespace l1tpf_impl +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/BuildFile.xml b/L1Trigger/Phase2L1ParticleFlow/plugins/BuildFile.xml new file mode 100644 index 0000000000000..3d1223a3526e1 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/BuildFile.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1TCorrectedPFJetProducer.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TCorrectedPFJetProducer.cc new file mode 100644 index 0000000000000..b642a1bc3f5cb --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TCorrectedPFJetProducer.cc @@ -0,0 +1,58 @@ +#include "DataFormats/L1TParticleFlow/interface/PFJet.h" +#include "DataFormats/JetReco/interface/Jet.h" + +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/InputTag.h" + +#include "L1Trigger/Phase2L1ParticleFlow/src/corrector.h" + +#include + +class L1TCorrectedPFJetProducer : public edm::global::EDProducer<> { +public: + explicit L1TCorrectedPFJetProducer(const edm::ParameterSet&); + ~L1TCorrectedPFJetProducer() override; + +private: + void produce(edm::StreamID, edm::Event&, const edm::EventSetup&) const override; + + edm::EDGetTokenT> jets_; + l1tpf::corrector corrector_; + bool copyDaughters_; +}; + +L1TCorrectedPFJetProducer::L1TCorrectedPFJetProducer(const edm::ParameterSet& iConfig) + : jets_(consumes>(iConfig.getParameter("jets"))), + corrector_(iConfig.getParameter("correctorFile"), iConfig.getParameter("correctorDir")), + copyDaughters_(iConfig.getParameter("copyDaughters")) { + produces>(); +} + +L1TCorrectedPFJetProducer::~L1TCorrectedPFJetProducer() {} + +void L1TCorrectedPFJetProducer::produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup&) const { + edm::Handle> jets; + iEvent.getByToken(jets_, jets); + auto out = std::make_unique>(); + + for (const auto& srcjet : *jets) { + // start out as copy + out->emplace_back(srcjet.p4()); + auto& jet = out->back(); + // copy daughters + if (copyDaughters_) { + for (const auto& dau : srcjet.daughterPtrVector()) { + jet.addConstituent(edm::Ptr(dau)); + } + } + // apply corrections + jet.calibratePt(corrector_.correctedPt(jet.pt(), jet.eta())); + } + + iEvent.put(std::move(out)); +} + +#include "FWCore/Framework/interface/MakerMacros.h" +DEFINE_FWK_MODULE(L1TCorrectedPFJetProducer); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCaloProducer.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCaloProducer.cc new file mode 100644 index 0000000000000..bcf7806c1b0b0 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCaloProducer.cc @@ -0,0 +1,300 @@ +// system include files +#include +#include + +// user include files +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/FileInPath.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "DataFormats/HcalDetId/interface/HcalTrigTowerDetId.h" +#include "DataFormats/HcalDigi/interface/HcalDigiCollections.h" +#include "CalibFormats/CaloTPG/interface/CaloTPGTranscoder.h" +#include "CalibFormats/CaloTPG/interface/CaloTPGRecord.h" +#include "L1Trigger/L1TCalorimeter/interface/CaloTools.h" + +#include "DataFormats/L1THGCal/interface/HGCalTower.h" + +#include "DataFormats/L1TCalorimeterPhase2/interface/CaloTower.h" + +#include "DataFormats/Math/interface/deltaPhi.h" + +#include "L1Trigger/Phase2L1ParticleFlow/src/corrector.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/CaloClusterer.h" + +//-------------------------------------------------------------------------------------------------- +class L1TPFCaloProducer : public edm::stream::EDProducer<> { +public: + explicit L1TPFCaloProducer(const edm::ParameterSet &); + +private: + bool ecalOnly_, debug_; + std::vector> ecalCands_; + std::vector> hcalCands_; + + std::vector> hcalDigis_; + edm::ESGetToken decoderTag_; + bool hcalDigisBarrel_, hcalDigisHF_; + std::vector> phase2barrelTowers_; + std::vector> hcalHGCTowers_; + bool hcalHGCTowersHadOnly_; + + l1tpf::corrector emCorrector_; + l1tpf::corrector hcCorrector_; + l1tpf::corrector hadCorrector_; + + l1tpf_calo::SingleCaloClusterer ecalClusterer_, hcalClusterer_; + std::unique_ptr caloLinker_; + + l1tpf::ParametricResolution resol_; + + void produce(edm::Event &, const edm::EventSetup &) override; + + void readHcalDigis_(edm::Event &event, const edm::EventSetup &); + void readPhase2BarrelCaloTowers_(edm::Event &event, const edm::EventSetup &); + void readHcalHGCTowers_(edm::Event &event, const edm::EventSetup &); + struct SimpleHGCTC { + float et, eta, phi; + SimpleHGCTC(float aet, float aeta, float aphi) : et(aet), eta(aeta), phi(aphi) {} + }; +}; + +// +// constructors and destructor +// +L1TPFCaloProducer::L1TPFCaloProducer(const edm::ParameterSet &iConfig) + : ecalOnly_(iConfig.existsAs("ecalOnly") ? iConfig.getParameter("ecalOnly") : false), + debug_(iConfig.getUntrackedParameter("debug", 0)), + decoderTag_(esConsumes(edm::ESInputTag("", ""))), + emCorrector_(iConfig.getParameter("emCorrector"), -1, debug_), + hcCorrector_(iConfig.getParameter("hcCorrector"), -1, debug_), + hadCorrector_(iConfig.getParameter("hadCorrector"), + iConfig.getParameter("hadCorrectorEmfMax"), + debug_), + ecalClusterer_(iConfig.getParameter("ecalClusterer")), + hcalClusterer_(iConfig.getParameter("hcalClusterer")), + caloLinker_(l1tpf_calo::makeCaloLinker( + iConfig.getParameter("linker"), ecalClusterer_, hcalClusterer_)), + resol_(iConfig.getParameter("resol")) { + produces("ecalCells"); + + produces("emCalibrated"); + produces("emUncalibrated"); + + for (auto &tag : iConfig.getParameter>("ecalCandidates")) { + ecalCands_.push_back(consumes(tag)); + } + + if (ecalOnly_) + return; + + produces("hcalCells"); + + produces("hcalUnclustered"); + produces("hcalUncalibrated"); + produces("hcalCalibrated"); + + produces("uncalibrated"); + produces("calibrated"); + + for (auto &tag : iConfig.getParameter>("hcalCandidates")) { + hcalCands_.push_back(consumes(tag)); + } + + for (auto &tag : iConfig.getParameter>("hcalDigis")) { + hcalDigis_.push_back(consumes(tag)); + } + if (!hcalDigis_.empty()) { + hcalDigisBarrel_ = iConfig.getParameter("hcalDigisBarrel"); + hcalDigisHF_ = iConfig.getParameter("hcalDigisHF"); + } + + for (auto &tag : iConfig.getParameter>("phase2barrelCaloTowers")) { + phase2barrelTowers_.push_back(consumes(tag)); + } + + for (auto &tag : iConfig.getParameter>("hcalHGCTowers")) { + hcalHGCTowers_.push_back(consumes(tag)); + } + if (!hcalHGCTowers_.empty()) + hcalHGCTowersHadOnly_ = iConfig.getParameter("hcalHGCTowersHadOnly"); +} + +// ------------ method called to produce the data ------------ +void L1TPFCaloProducer::produce(edm::Event &iEvent, const edm::EventSetup &iSetup) { + /// ----------------ECAL INFO------------------- + edm::Handle ecals; + for (const auto &token : ecalCands_) { + iEvent.getByToken(token, ecals); + for (const reco::Candidate &it : *ecals) { + if (debug_) + edm::LogWarning("L1TPFCaloProducer") + << "adding ECal input pt " << it.pt() << ", eta " << it.eta() << ", phi " << it.phi() << "\n"; + ecalClusterer_.add(it); + } + } + + /// ----------------HCAL INFO------------------- + if (!ecalOnly_) { + edm::Handle hcals; + for (const auto &token : hcalCands_) { + iEvent.getByToken(token, hcals); + for (const reco::Candidate &it : *hcals) { + if (debug_) + edm::LogWarning("L1TPFCaloProducer") + << "adding HCal cand input pt " << it.pt() << ", eta " << it.eta() << ", phi " << it.phi() << "\n"; + hcalClusterer_.add(it); + } + } + if (!hcalDigis_.empty()) { + readHcalDigis_(iEvent, iSetup); + } + if (!phase2barrelTowers_.empty()) { + readPhase2BarrelCaloTowers_(iEvent, iSetup); + } + if (!hcalHGCTowers_.empty()) { + readHcalHGCTowers_(iEvent, iSetup); + } + } + + /// --------------- CLUSTERING ------------------ + ecalClusterer_.run(); + + auto ecalCellsH = iEvent.put(ecalClusterer_.fetchCells(), "ecalCells"); + + iEvent.put(ecalClusterer_.fetch(ecalCellsH), "emUncalibrated"); + + if (emCorrector_.valid()) { + ecalClusterer_.correct( + [&](const l1tpf_calo::Cluster &c) -> float { return emCorrector_.correctedPt(0., c.et, std::abs(c.eta)); }); + } + + std::unique_ptr corrEcal = ecalClusterer_.fetch(ecalCellsH); + + if (debug_) { + for (const l1t::PFCluster &it : *corrEcal) { + edm::LogWarning("L1TPFCaloProducer") + << "corrected ECal cluster pt " << it.pt() << ", eta " << it.eta() << ", phi " << it.phi() << "\n"; + } + } + + auto ecalClustH = iEvent.put(std::move(corrEcal), "emCalibrated"); + + if (ecalOnly_) { + ecalClusterer_.clear(); + return; + } + + hcalClusterer_.run(); + + auto hcalCellsH = iEvent.put(hcalClusterer_.fetchCells(), "hcalCells"); + + // this we put separately for debugging + iEvent.put(hcalClusterer_.fetchCells(/*unclustered=*/true), "hcalUnclustered"); + + iEvent.put(hcalClusterer_.fetch(hcalCellsH), "hcalUncalibrated"); + + if (hcCorrector_.valid()) { + hcalClusterer_.correct( + [&](const l1tpf_calo::Cluster &c) -> float { return hcCorrector_.correctedPt(c.et, 0., std::abs(c.eta)); }); + } + + auto hcalClustH = iEvent.put(hcalClusterer_.fetch(hcalCellsH), "hcalCalibrated"); + + // Calorimeter linking + caloLinker_->run(); + + iEvent.put(caloLinker_->fetch(ecalClustH, hcalClustH), "uncalibrated"); + + if (hadCorrector_.valid()) { + caloLinker_->correct([&](const l1tpf_calo::CombinedCluster &c) -> float { + if (debug_) + edm::LogWarning("L1TPFCaloProducer") << "raw linked cluster pt " << c.et << ", eta " << c.eta << ", phi " + << c.phi << ", emPt " << c.ecal_et << "\n"; + return hadCorrector_.correctedPt(c.et, c.ecal_et, std::abs(c.eta)); + }); + } + + std::unique_ptr clusters = caloLinker_->fetch(ecalClustH, hcalClustH); + for (l1t::PFCluster &c : *clusters) { + c.setPtError(resol_(c.pt(), std::abs(c.eta()))); + if (debug_) + edm::LogWarning("L1TPFCaloProducer") << "calibrated linked cluster pt " << c.pt() << ", eta " << c.eta() + << ", phi " << c.phi() << ", emPt " << c.emEt() << "\n"; + } + iEvent.put(std::move(clusters), "calibrated"); + + ecalClusterer_.clear(); + hcalClusterer_.clear(); + caloLinker_->clear(); +} + +void L1TPFCaloProducer::readHcalDigis_(edm::Event &iEvent, const edm::EventSetup &iSetup) { + const auto &decoder = iSetup.getData(decoderTag_); + edm::Handle hcalTPs; + for (const auto &token : hcalDigis_) { + iEvent.getByToken(token, hcalTPs); + for (const auto &itr : *hcalTPs) { + HcalTrigTowerDetId id = itr.id(); + double et = decoder.hcaletValue(itr.id(), itr.t0()); + if (et <= 0) + continue; + float towerEta = l1t::CaloTools::towerEta(id.ieta()); + float towerPhi = l1t::CaloTools::towerPhi(id.ieta(), id.iphi()); + if (!hcalDigisBarrel_ && std::abs(towerEta) < 2) // |eta| < 2 => barrel (there's no HE in Phase2) + continue; + if (!hcalDigisHF_ && std::abs(towerEta) > 2) // |eta| > 2 => HF + continue; + if (debug_) + edm::LogWarning("L1TPFCaloProducer") + << "adding HCal digi input pt " << et << ", eta " << towerEta << ", phi " << towerPhi << "\n"; + hcalClusterer_.add(et, towerEta, towerPhi); + } + } +} + +void L1TPFCaloProducer::readPhase2BarrelCaloTowers_(edm::Event &event, const edm::EventSetup &) { + edm::Handle towers; + for (const auto &token : phase2barrelTowers_) { + event.getByToken(token, towers); + for (const auto &t : *towers) { + // sanity check from https://github.com/cms-l1t-offline/cmssw/blob/l1t-phase2-v3.0.2/L1Trigger/L1CaloTrigger/plugins/L1TowerCalibrator.cc#L259-L263 + if ((int)t.towerIEta() == -1016 && (int)t.towerIPhi() == -962) + continue; + if (debug_ && (t.hcalTowerEt() > 0 || t.ecalTowerEt() > 0)) { + edm::LogWarning("L1TPFCaloProducer") + << "adding phase2 L1 CaloTower eta " << t.towerEta() << " phi " << t.towerIPhi() << " ieta " + << t.towerIEta() << " iphi " << t.towerIPhi() << " ecal " << t.ecalTowerEt() << " hcal " + << t.hcalTowerEt() << "\n"; + } + hcalClusterer_.add(t.hcalTowerEt(), t.towerEta(), t.towerIPhi()); + ecalClusterer_.add(t.ecalTowerEt(), t.towerEta(), t.towerIPhi()); + } + } +} + +void L1TPFCaloProducer::readHcalHGCTowers_(edm::Event &iEvent, const edm::EventSetup &iSetup) { + edm::Handle hgcTowers; + + for (const auto &token : hcalHGCTowers_) { + iEvent.getByToken(token, hgcTowers); + for (auto it = hgcTowers->begin(0), ed = hgcTowers->end(0); it != ed; ++it) { + if (debug_) + edm::LogWarning("L1TPFCaloProducer") + << "adding HGC Tower hadEt " << it->etHad() << ", emEt " << it->etEm() << ", pt " << it->pt() << ", eta " + << it->eta() << ", phi " << it->phi() << "\n"; + hcalClusterer_.add(it->etHad(), it->eta(), it->phi()); + if (!hcalHGCTowersHadOnly_) + ecalClusterer_.add(it->etEm(), it->eta(), it->phi()); + } + } +} + +//define this as a plug-in +#include "FWCore/Framework/interface/MakerMacros.h" +DEFINE_FWK_MODULE(L1TPFCaloProducer); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMerger.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMerger.cc new file mode 100644 index 0000000000000..e61c8d394282d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMerger.cc @@ -0,0 +1,7 @@ +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "CommonTools/UtilAlgos/interface/Merger.h" + +typedef Merger> L1TPFCandMerger; + +DEFINE_FWK_MODULE(L1TPFCandMerger); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMultiMerger.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMultiMerger.cc new file mode 100644 index 0000000000000..96f36c36b794d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandMultiMerger.cc @@ -0,0 +1,51 @@ +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" + +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/transform.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "DataFormats/Common/interface/CloneTrait.h" +#include + +class L1TPFCandMultiMerger : public edm::global::EDProducer<> { +public: + explicit L1TPFCandMultiMerger(const edm::ParameterSet&); + ~L1TPFCandMultiMerger() override; + +private: + void produce(edm::StreamID, edm::Event&, const edm::EventSetup&) const override; + + std::vector instances_; + std::vector>> tokens_; +}; + +L1TPFCandMultiMerger::L1TPFCandMultiMerger(const edm::ParameterSet& iConfig) + : instances_(iConfig.getParameter>("labelsToMerge")) { + const std::vector& pfProducers = iConfig.getParameter>("pfProducers"); + tokens_.reserve(instances_.size() * pfProducers.size()); + for (unsigned int ii = 0, ni = instances_.size(); ii < ni; ++ii) { + for (const edm::InputTag& tag : pfProducers) { + tokens_.push_back( + consumes>(edm::InputTag(tag.label(), instances_[ii], tag.process()))); + } + produces>(instances_[ii]); + } +} + +L1TPFCandMultiMerger::~L1TPFCandMultiMerger() {} + +void L1TPFCandMultiMerger::produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup&) const { + edm::Handle> handle; + for (unsigned int ii = 0, it = 0, ni = instances_.size(), np = tokens_.size() / ni; ii < ni; ++ii) { + auto out = std::make_unique>(); + for (unsigned int ip = 0; ip < np; ++ip, ++it) { + iEvent.getByToken(tokens_[it], handle); + out->insert(out->end(), handle->begin(), handle->end()); + } + iEvent.put(std::move(out), instances_[ii]); + } +} + +#include "FWCore/Framework/interface/MakerMacros.h" +DEFINE_FWK_MODULE(L1TPFCandMultiMerger); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandSelector.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandSelector.cc new file mode 100644 index 0000000000000..09445cf2ab83a --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFCandSelector.cc @@ -0,0 +1,8 @@ +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "CommonTools/UtilAlgos/interface/StringCutObjectSelector.h" +#include "CommonTools/UtilAlgos/interface/SingleObjectSelector.h" + +typedef SingleObjectSelector, StringCutObjectSelector> L1TPFCandSelector; + +DEFINE_FWK_MODULE(L1TPFCandSelector); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFProducer.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFProducer.cc new file mode 100644 index 0000000000000..8c358b30d2967 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1TPFProducer.cc @@ -0,0 +1,400 @@ +// system include files +#include +#include +#include +#include + +// user include files +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/FileInPath.h" + +#include "DataFormats/Common/interface/View.h" +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "DataFormats/L1TCorrelator/interface/TkPrimaryVertex.h" + +#include "DataFormats/Math/interface/deltaR.h" + +#include "L1Trigger/Phase2L1ParticleFlow/interface/RegionMapper.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo3.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo2HGC.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/BitwisePFAlgo.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/LinearizedPuppiAlgo.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/COEFile.h" + +#include "DataFormats/L1TCorrelator/interface/TkMuon.h" +#include "DataFormats/L1TCorrelator/interface/TkMuonFwd.h" + +//-------------------------------------------------------------------------------------------------- +class L1TPFProducer : public edm::stream::EDProducer<> { +public: + explicit L1TPFProducer(const edm::ParameterSet&); + ~L1TPFProducer() override; + +private: + edm::ParameterSet config_; + int debug_; + + bool useStandaloneMuons_; + bool useTrackerMuons_; + + bool hasTracks_; + edm::EDGetTokenT tkCands_; + float trkPt_, trkMaxChi2_; + unsigned trkMinStubs_; + l1tpf_impl::PUAlgoBase::VertexAlgo vtxAlgo_; + edm::EDGetTokenT> extTkVtx_; + + edm::EDGetTokenT muCands_; // standalone muons + edm::EDGetTokenT tkMuCands_; // tk muons + + std::vector> emCands_; + std::vector> hadCands_; + + float emPtCut_, hadPtCut_; + + l1tpf_impl::RegionMapper l1regions_; + std::unique_ptr l1pfalgo_; + std::unique_ptr l1pualgo_; + + edm::EDGetTokenT TokGenOrigin_; + + // Region dump/coe + const std::string regionDumpName_, regionCOEName_; + FILE* fRegionDump_; + std::unique_ptr fRegionCOE_; + unsigned int neventscoemax_, neventsproduced_; + + // region of interest debugging + float debugEta_, debugPhi_, debugR_; + + void beginStream(edm::StreamID) override; + void produce(edm::Event&, const edm::EventSetup&) override; + void addUInt(unsigned int value, std::string iLabel, edm::Event& iEvent); +}; + +// +// constructors and destructor +// +L1TPFProducer::L1TPFProducer(const edm::ParameterSet& iConfig) + : config_(iConfig), + debug_(iConfig.getUntrackedParameter("debug", 0)), + useStandaloneMuons_(iConfig.getParameter("useStandaloneMuons")), + useTrackerMuons_(iConfig.getParameter("useTrackerMuons")), + hasTracks_(!iConfig.getParameter("tracks").label().empty()), + tkCands_(hasTracks_ ? consumes(iConfig.getParameter("tracks")) + : edm::EDGetTokenT()), + trkPt_(iConfig.getParameter("trkPtCut")), + trkMaxChi2_(iConfig.getParameter("trkMaxChi2")), + trkMinStubs_(iConfig.getParameter("trkMinStubs")), + muCands_(consumes(iConfig.getParameter("muons"))), + tkMuCands_(consumes(iConfig.getParameter("tkMuons"))), + emPtCut_(iConfig.getParameter("emPtCut")), + hadPtCut_(iConfig.getParameter("hadPtCut")), + l1regions_(iConfig), + l1pfalgo_(nullptr), + l1pualgo_(nullptr), + regionDumpName_(iConfig.getUntrackedParameter("dumpFileName", "")), + regionCOEName_(iConfig.getUntrackedParameter("coeFileName", "")), + fRegionDump_(nullptr), + fRegionCOE_(nullptr), + neventscoemax_(iConfig.getUntrackedParameter("neventscoemax_", 0)), + neventsproduced_(0), + debugEta_(iConfig.getUntrackedParameter("debugEta", 0)), + debugPhi_(iConfig.getUntrackedParameter("debugPhi", 0)), + debugR_(iConfig.getUntrackedParameter("debugR", -1)) { + produces("PF"); + produces("Puppi"); + + produces("EmCalo"); + produces("Calo"); + produces("TK"); + produces("TKVtx"); + + produces("z0"); + + for (const auto& tag : iConfig.getParameter>("emClusters")) { + emCands_.push_back(consumes(tag)); + } + for (const auto& tag : iConfig.getParameter>("hadClusters")) { + hadCands_.push_back(consumes(tag)); + } + + const std::string& algo = iConfig.getParameter("pfAlgo"); + if (algo == "PFAlgo3") { + l1pfalgo_.reset(new l1tpf_impl::PFAlgo3(iConfig)); + } else if (algo == "PFAlgo2HGC") { + l1pfalgo_.reset(new l1tpf_impl::PFAlgo2HGC(iConfig)); + } else if (algo == "BitwisePFAlgo") { + l1pfalgo_.reset(new l1tpf_impl::BitwisePFAlgo(iConfig)); + } else + throw cms::Exception("Configuration", "Unsupported PFAlgo"); + + const std::string& pualgo = iConfig.getParameter("puAlgo"); + if (pualgo == "Puppi") { + l1pualgo_.reset(new l1tpf_impl::PuppiAlgo(iConfig)); + } else if (pualgo == "LinearizedPuppi") { + l1pualgo_.reset(new l1tpf_impl::LinearizedPuppiAlgo(iConfig)); + } else + throw cms::Exception("Configuration", "Unsupported PUAlgo"); + + std::string vtxAlgo = iConfig.getParameter("vtxAlgo"); + if (vtxAlgo == "TP") + vtxAlgo_ = l1tpf_impl::PUAlgoBase::VertexAlgo::TP; + else if (vtxAlgo == "old") + vtxAlgo_ = l1tpf_impl::PUAlgoBase::VertexAlgo::Old; + else if (vtxAlgo == "external") { + vtxAlgo_ = l1tpf_impl::PUAlgoBase::VertexAlgo::External; + const std::string& vtxFormat = iConfig.getParameter("vtxFormat"); + if (vtxFormat == "TkPrimaryVertex") { + extTkVtx_ = consumes>(iConfig.getParameter("vtxCollection")); + } else + throw cms::Exception("Configuration") << "Unsupported vtxFormat " << vtxFormat << "\n"; + } else + throw cms::Exception("Configuration") << "Unsupported vtxAlgo " << vtxAlgo << "\n"; + + for (const std::string& label : l1pualgo_->puGlobalNames()) { + produces(label); + } + + if (!regionDumpName_.empty()) { + TokGenOrigin_ = consumes(iConfig.getParameter("genOrigin")); + } + for (int tot = 0; tot <= 1; ++tot) { + for (int i = 0; i < l1tpf_impl::Region::n_input_types; ++i) { + produces(std::string(tot ? "totNL1" : "maxNL1") + l1tpf_impl::Region::inputTypeName(i)); + } + for (int i = 0; i < l1tpf_impl::Region::n_output_types; ++i) { + produces(std::string(tot ? "totNL1PF" : "maxNL1PF") + l1tpf_impl::Region::outputTypeName(i)); + produces(std::string(tot ? "totNL1Puppi" : "maxNL1Puppi") + l1tpf_impl::Region::outputTypeName(i)); + } + } + for (int i = 0; i < l1tpf_impl::Region::n_input_types; ++i) { + produces>(std::string("vecNL1") + l1tpf_impl::Region::inputTypeName(i)); + } + for (int i = 0; i < l1tpf_impl::Region::n_output_types; ++i) { + produces>(std::string("vecNL1PF") + l1tpf_impl::Region::outputTypeName(i)); + produces>(std::string("vecNL1Puppi") + l1tpf_impl::Region::outputTypeName(i)); + } +} + +L1TPFProducer::~L1TPFProducer() { + // do anything here that needs to be done at desctruction time + // (e.g. close files, deallocate resources etc.) + if (fRegionDump_) + fclose(fRegionDump_); + if (fRegionCOE_) + fRegionCOE_->close(); +} + +void L1TPFProducer::beginStream(edm::StreamID id) { + if (!regionDumpName_.empty()) { + if (id == 0) { + fRegionDump_ = fopen(regionDumpName_.c_str(), "wb"); + } else { + edm::LogWarning("L1TPFProducer") + << "Job running with multiple streams, but dump file will have only events on stream zero."; + } + } + if (!regionCOEName_.empty()) { + if (id == 0) { + fRegionCOE_.reset(new l1tpf_impl::COEFile(config_)); + } else { + edm::LogWarning("L1TPFProducer") + << "Job running with multiple streams, but COE file will dump only events on stream zero."; + } + } +} + +// ------------ method called to produce the data ------------ +void L1TPFProducer::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + // clear the regions also at the beginning, in case one event didn't complete but the job continues on + l1regions_.clear(); + + /// ------ READ TRACKS ---- + if (hasTracks_) { + edm::Handle htracks; + iEvent.getByToken(tkCands_, htracks); + const auto& tracks = *htracks; + for (unsigned int itk = 0, ntk = tracks.size(); itk < ntk; ++itk) { + const auto& tk = tracks[itk]; + // adding objects to PF + if (debugR_ > 0 && deltaR(tk.eta(), tk.phi(), debugEta_, debugPhi_) > debugR_) + continue; + if (tk.pt() > trkPt_ && tk.nStubs() >= trkMinStubs_ && tk.normalizedChi2() < trkMaxChi2_) { + l1regions_.addTrack(tk, l1t::PFTrackRef(htracks, itk)); + } + } + } + + /// ------ READ MUONS ---- + /// ------- first check that not more than one version of muons (standaloneMu or trackerMu) is set to be used in l1pflow + if (useStandaloneMuons_ && useTrackerMuons_) { + throw cms::Exception( + "Configuration", + "setting useStandaloneMuons=True && useTrackerMuons=True is not to be done, as it would duplicate all muons\n"); + } + + if (useStandaloneMuons_) { + edm::Handle muons; + iEvent.getByToken(muCands_, muons); + for (auto it = muons->begin(0), ed = muons->end(0); it != ed; ++it) { + const l1t::Muon& mu = *it; + if (debugR_ > 0 && deltaR(mu.eta(), mu.phi(), debugEta_, debugPhi_) > debugR_) + continue; + l1regions_.addMuon(mu, l1t::PFCandidate::MuonRef(muons, muons->key(it))); + } + } + + if (useTrackerMuons_) { + edm::Handle muons; + iEvent.getByToken(tkMuCands_, muons); + for (auto it = muons->begin(), ed = muons->end(); it != ed; ++it) { + const l1t::TkMuon& mu = *it; + if (debugR_ > 0 && deltaR(mu.eta(), mu.phi(), debugEta_, debugPhi_) > debugR_) + continue; + l1regions_.addMuon(mu); // FIXME add a l1t::PFCandidate::MuonRef + } + } + + // ------ READ CALOS ----- + edm::Handle caloHandle; + for (const auto& tag : emCands_) { + iEvent.getByToken(tag, caloHandle); + const auto& calos = *caloHandle; + for (unsigned int ic = 0, nc = calos.size(); ic < nc; ++ic) { + const auto& calo = calos[ic]; + if (debugR_ > 0 && deltaR(calo.eta(), calo.phi(), debugEta_, debugPhi_) > debugR_) + continue; + if (calo.pt() > emPtCut_) + l1regions_.addEmCalo(calo, l1t::PFClusterRef(caloHandle, ic)); + } + } + for (const auto& tag : hadCands_) { + iEvent.getByToken(tag, caloHandle); + const auto& calos = *caloHandle; + for (unsigned int ic = 0, nc = calos.size(); ic < nc; ++ic) { + const auto& calo = calos[ic]; + if (debugR_ > 0 && deltaR(calo.eta(), calo.phi(), debugEta_, debugPhi_) > debugR_) + continue; + if (calo.pt() > hadPtCut_) + l1regions_.addCalo(calo, l1t::PFClusterRef(caloHandle, ic)); + } + } + + // First, get a copy of the discretized and corrected inputs, and write them out + iEvent.put(l1regions_.fetchCalo(/*ptmin=*/0.1, /*em=*/true), "EmCalo"); + iEvent.put(l1regions_.fetchCalo(/*ptmin=*/0.1, /*em=*/false), "Calo"); + iEvent.put(l1regions_.fetchTracks(/*ptmin=*/0.0, /*fromPV=*/false), "TK"); + if (fRegionDump_) { + uint32_t run = iEvent.id().run(), lumi = iEvent.id().luminosityBlock(); + uint64_t event = iEvent.id().event(); + fwrite(&run, sizeof(uint32_t), 1, fRegionDump_); + fwrite(&lumi, sizeof(uint32_t), 1, fRegionDump_); + fwrite(&event, sizeof(uint64_t), 1, fRegionDump_); + l1tpf_impl::writeManyToFile(l1regions_.regions(), fRegionDump_); + } + + // Then save the regions to the COE file + // Do it here because there is some sorting going on in a later function + if (fRegionCOE_ && fRegionCOE_->is_open() && neventsproduced_ < neventscoemax_) { + std::vector regions = l1regions_.regions(); + fRegionCOE_->writeTracksToFile(regions, neventsproduced_ == 0); + } + neventsproduced_++; + + // Then do the vertexing, and save it out + float z0; + if (vtxAlgo_ == l1tpf_impl::PUAlgoBase::VertexAlgo::External) { + z0 = 0; + double ptsum = 0; + if (!extTkVtx_.isUninitialized()) { + edm::Handle> vtxHandle; + iEvent.getByToken(extTkVtx_, vtxHandle); + for (const l1t::TkPrimaryVertex& vtx : *vtxHandle) { + if (ptsum == 0 || vtx.sum() > ptsum) { + z0 = vtx.zvertex(); + ptsum = vtx.sum(); + } + } + } else + throw cms::Exception("LogicError", "Inconsistent vertex configuration"); + } + l1pualgo_->doVertexing(l1regions_.regions(), vtxAlgo_, z0); + iEvent.put(std::make_unique(z0), "z0"); + if (fRegionDump_) { + fwrite(&z0, sizeof(float), 1, fRegionDump_); + edm::Handle hGenOrigin; + iEvent.getByToken(TokGenOrigin_, hGenOrigin); + const math::XYZPointF& genOrigin = *hGenOrigin; + float genZ = genOrigin.Z(); + fwrite(&genZ, sizeof(float), 1, fRegionDump_); + } + + // Then also save the tracks with a vertex cut + iEvent.put(l1regions_.fetchTracks(/*ptmin=*/0.0, /*fromPV=*/true), "TKVtx"); + + // Then run PF in each region + for (auto& l1region : l1regions_.regions()) { + l1pfalgo_->runPF(l1region); + l1pualgo_->runChargedPV(l1region, z0); + } + // save PF into the event + iEvent.put(l1regions_.fetch(false), "PF"); + + // Then get our alphas (globally) + std::vector puGlobals; + l1pualgo_->doPUGlobals(l1regions_.regions(), -1., puGlobals); // FIXME we don't have yet an external PU estimate + const std::vector& puGlobalNames = l1pualgo_->puGlobalNames(); + if (puGlobals.size() != puGlobalNames.size()) + throw cms::Exception("LogicError", "Mismatch in the number of global pileup inputs"); + for (unsigned int i = 0, n = puGlobalNames.size(); i < n; ++i) { + iEvent.put(std::make_unique(puGlobals[i]), puGlobalNames[i]); + } + if (fRegionDump_) { + l1tpf_impl::writeManyToFile(puGlobals, fRegionDump_); + } + + // Then run puppi (regionally) + for (auto& l1region : l1regions_.regions()) { + l1pualgo_->runNeutralsPU(l1region, -1., puGlobals); + } + // and save puppi + iEvent.put(l1regions_.fetch(true), "Puppi"); + + // Then go do the multiplicities + + for (int i = 0; i < l1tpf_impl::Region::n_input_types; ++i) { + auto totAndMax = l1regions_.totAndMaxInput(i); + addUInt(totAndMax.first, std::string("totNL1") + l1tpf_impl::Region::inputTypeName(i), iEvent); + addUInt(totAndMax.second, std::string("maxNL1") + l1tpf_impl::Region::inputTypeName(i), iEvent); + iEvent.put(l1regions_.vecInput(i), std::string("vecNL1") + l1tpf_impl::Region::inputTypeName(i)); + } + for (int i = 0; i < l1tpf_impl::Region::n_output_types; ++i) { + auto totAndMaxPF = l1regions_.totAndMaxOutput(i, false); + auto totAndMaxPuppi = l1regions_.totAndMaxOutput(i, true); + addUInt(totAndMaxPF.first, std::string("totNL1PF") + l1tpf_impl::Region::outputTypeName(i), iEvent); + addUInt(totAndMaxPF.second, std::string("maxNL1PF") + l1tpf_impl::Region::outputTypeName(i), iEvent); + addUInt(totAndMaxPuppi.first, std::string("totNL1Puppi") + l1tpf_impl::Region::outputTypeName(i), iEvent); + addUInt(totAndMaxPuppi.second, std::string("maxNL1Puppi") + l1tpf_impl::Region::outputTypeName(i), iEvent); + iEvent.put(l1regions_.vecOutput(i, false), std::string("vecNL1PF") + l1tpf_impl::Region::outputTypeName(i)); + iEvent.put(l1regions_.vecOutput(i, true), std::string("vecNL1Puppi") + l1tpf_impl::Region::outputTypeName(i)); + } + + // finally clear the regions + l1regions_.clear(); +} + +void L1TPFProducer::addUInt(unsigned int value, std::string iLabel, edm::Event& iEvent) { + iEvent.put(std::make_unique(value), iLabel); +} + +//define this as a plug-in +#include "FWCore/Framework/interface/MakerMacros.h" +DEFINE_FWK_MODULE(L1TPFProducer); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromHGC3DClusters.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromHGC3DClusters.cc new file mode 100644 index 0000000000000..65443eb176fdf --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromHGC3DClusters.cc @@ -0,0 +1,112 @@ +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "DataFormats/L1TParticleFlow/interface/PFCluster.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/corrector.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/HGC3DClusterEgID.h" +#include "DataFormats/L1THGCal/interface/HGCalMulticluster.h" +#include "CommonTools/Utils/interface/StringCutObjectSelector.h" + +namespace l1tpf { + class PFClusterProducerFromHGC3DClusters : public edm::stream::EDProducer<> { + public: + explicit PFClusterProducerFromHGC3DClusters(const edm::ParameterSet &); + ~PFClusterProducerFromHGC3DClusters() override {} + + private: + edm::EDGetTokenT src_; + bool emOnly_; + double etCut_; + StringCutObjectSelector preEmId_; + l1tpf::HGC3DClusterEgID emVsPionID_, emVsPUID_; + bool hasEmId_; + l1tpf::corrector corrector_; + l1tpf::ParametricResolution resol_; + + void produce(edm::Event &, const edm::EventSetup &) override; + + }; // class +} // namespace l1tpf + +l1tpf::PFClusterProducerFromHGC3DClusters::PFClusterProducerFromHGC3DClusters(const edm::ParameterSet &iConfig) + : src_(consumes(iConfig.getParameter("src"))), + emOnly_(iConfig.getParameter("emOnly")), + etCut_(iConfig.getParameter("etMin")), + preEmId_(iConfig.getParameter("preEmId")), + emVsPionID_(iConfig.getParameter("emVsPionID")), + emVsPUID_(iConfig.getParameter("emVsPUID")), + hasEmId_((iConfig.existsAs("preEmId") && !iConfig.getParameter("preEmId").empty()) || + !emVsPionID_.method().empty()), + corrector_(iConfig.getParameter("corrector"), + emOnly_ || iConfig.getParameter("corrector").empty() + ? -1 + : iConfig.getParameter("correctorEmfMax")), + resol_(iConfig.getParameter("resol")) { + if (!emVsPionID_.method().empty()) { + emVsPionID_.prepareTMVA(); + } + if (!emVsPUID_.method().empty()) { + emVsPUID_.prepareTMVA(); + } + + produces(); + if (hasEmId_) { + produces("em"); + produces("had"); + } +} + +void l1tpf::PFClusterProducerFromHGC3DClusters::produce(edm::Event &iEvent, const edm::EventSetup &) { + auto out = std::make_unique(); + std::unique_ptr outEm, outHad; + if (hasEmId_) { + outEm.reset(new l1t::PFClusterCollection()); + outHad.reset(new l1t::PFClusterCollection()); + } + edm::Handle multiclusters; + iEvent.getByToken(src_, multiclusters); + + for (auto it = multiclusters->begin(0), ed = multiclusters->end(0); it != ed; ++it) { + float pt = it->pt(), hoe = it->hOverE(); + bool isEM = hasEmId_ ? preEmId_(*it) : emOnly_; + if (emOnly_) { + if (hoe == -1) + continue; + pt /= (1 + hoe); + hoe = 0; + } + if (pt <= etCut_) + continue; + + l1t::PFCluster cluster(pt, it->eta(), it->phi(), hoe, /*isEM=*/isEM); + if (!emVsPUID_.method().empty()) { + if (!emVsPUID_.passID(*it, cluster)) { + continue; + } + } + if (!emVsPionID_.method().empty()) { + cluster.setIsEM(emVsPionID_.passID(*it, cluster)); + } + if (corrector_.valid()) + corrector_.correctPt(cluster); + cluster.setPtError(resol_(cluster.pt(), std::abs(cluster.eta()))); + + out->push_back(cluster); + out->back().addConstituent(edm::Ptr(multiclusters, multiclusters->key(it))); + if (hasEmId_) { + (isEM ? outEm : outHad)->push_back(out->back()); + } + } + + iEvent.put(std::move(out)); + if (hasEmId_) { + iEvent.put(std::move(outEm), "em"); + iEvent.put(std::move(outHad), "had"); + } +} +using l1tpf::PFClusterProducerFromHGC3DClusters; +DEFINE_FWK_MODULE(PFClusterProducerFromHGC3DClusters); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromL1EGClusters.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromL1EGClusters.cc new file mode 100644 index 0000000000000..085110ff0c2d9 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/PFClusterProducerFromL1EGClusters.cc @@ -0,0 +1,60 @@ +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "DataFormats/L1TParticleFlow/interface/PFCluster.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/corrector.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h" +#include "DataFormats/L1TCalorimeterPhase2/interface/CaloCrystalCluster.h" + +namespace l1tpf { + class PFClusterProducerFromL1EGClusters : public edm::stream::EDProducer<> { + public: + explicit PFClusterProducerFromL1EGClusters(const edm::ParameterSet &); + ~PFClusterProducerFromL1EGClusters() override {} + + private: + edm::EDGetTokenT src_; + double etCut_; + l1tpf::corrector corrector_; + l1tpf::ParametricResolution resol_; + + void produce(edm::Event &, const edm::EventSetup &) override; + + }; // class +} // namespace l1tpf + +l1tpf::PFClusterProducerFromL1EGClusters::PFClusterProducerFromL1EGClusters(const edm::ParameterSet &iConfig) + : src_(consumes(iConfig.getParameter("src"))), + etCut_(iConfig.getParameter("etMin")), + corrector_(iConfig.getParameter("corrector"), -1), + resol_(iConfig.getParameter("resol")) { + produces(); +} + +void l1tpf::PFClusterProducerFromL1EGClusters::produce(edm::Event &iEvent, const edm::EventSetup &) { + std::unique_ptr out(new l1t::PFClusterCollection()); + edm::Handle clusters; + iEvent.getByToken(src_, clusters); + + unsigned int index = 0; + for (auto it = clusters->begin(), ed = clusters->end(); it != ed; ++it, ++index) { + if (it->pt() <= etCut_) + continue; + + l1t::PFCluster cluster( + it->pt(), it->eta(), it->phi(), /*hOverE=*/0., /*isEM=*/true); // it->hovere() seems to return random values + if (corrector_.valid()) + corrector_.correctPt(cluster); + cluster.setPtError(resol_(cluster.pt(), std::abs(cluster.eta()))); + + out->push_back(cluster); + out->back().addConstituent(edm::Ptr(clusters, index)); + } + + iEvent.put(std::move(out)); +} +using l1tpf::PFClusterProducerFromL1EGClusters; +DEFINE_FWK_MODULE(PFClusterProducerFromL1EGClusters); diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc new file mode 100644 index 0000000000000..5c53c47334c24 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc @@ -0,0 +1,87 @@ +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "FWCore/Framework/interface/ESHandle.h" +#include "MagneticField/Engine/interface/MagneticField.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" +#include "FWCore/Framework/interface/ESWatcher.h" + +#include "DataFormats/L1TParticleFlow/interface/PFTrack.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/L1TPFUtils.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h" + +namespace l1tpf { + class PFTrackProducerFromL1Tracks : public edm::stream::EDProducer<> { + public: + explicit PFTrackProducerFromL1Tracks(const edm::ParameterSet &); + ~PFTrackProducerFromL1Tracks() override {} + + private: + edm::EDGetTokenT> TrackTag_; + edm::ESWatcher BFieldWatcher_; + edm::ESGetToken BFieldTag_; + int nParam_; + float fBz_; + l1tpf::ParametricResolution resolCalo_, resolTrk_; + + void produce(edm::Event &, const edm::EventSetup &) override; + + }; // class +} // namespace l1tpf + +l1tpf::PFTrackProducerFromL1Tracks::PFTrackProducerFromL1Tracks(const edm::ParameterSet &iConfig) + : TrackTag_(consumes>(iConfig.getParameter("L1TrackTag"))), + nParam_(iConfig.getParameter("nParam")), + resolCalo_(iConfig.getParameter("resolCalo")), + resolTrk_(iConfig.getParameter("resolTrack")) { + produces(); +} + +void l1tpf::PFTrackProducerFromL1Tracks::produce(edm::Event &iEvent, const edm::EventSetup &iSetup) { + if (BFieldWatcher_.check(iSetup)) { + fBz_ = iSetup.getData(BFieldTag_).inTesla(GlobalPoint(0, 0, 0)).z(); + } + + std::unique_ptr out(new l1t::PFTrackCollection()); + + // https://github.com/skinnari/cmssw/blob/80c19f1b721325c3a02ee0482f72fb974a4c3bf7/L1Trigger/TrackFindingTracklet/test/L1TrackNtupleMaker.cc + edm::Handle> htracks; + iEvent.getByToken(TrackTag_, htracks); + const auto &tracks = *htracks; + + for (unsigned int i = 0, n = tracks.size(); i < n; ++i) { + const auto &tk = tracks[i]; + + float pt = tk.momentum().perp(); + float eta = tk.momentum().eta(); + float phi = tk.momentum().phi(); + float z0 = tk.POCA().z(); //cm + int charge = tk.rInv() > 0 ? +1 : -1; + + reco::Candidate::PolarLorentzVector p4p(pt, eta, phi, 0.137); // pion mass + reco::Particle::LorentzVector p4(p4p.X(), p4p.Y(), p4p.Z(), p4p.E()); + reco::Particle::Point vtx(0., 0., z0); + + auto caloetaphi = l1tpf::propagateToCalo(p4, math::XYZTLorentzVector(0., 0., z0, 0.), charge, fBz_); + + float trkErr = resolTrk_(pt, std::abs(eta)); + float caloErr = resolCalo_(pt, std::abs(eta)); + int quality = 1; + out->emplace_back(charge, + p4, + vtx, + l1t::PFTrack::TrackRef(htracks, i), + nParam_, + caloetaphi.first, + caloetaphi.second, + trkErr, + caloErr, + quality); + } + iEvent.put(std::move(out)); +} +using l1tpf::PFTrackProducerFromL1Tracks; +DEFINE_FWK_MODULE(PFTrackProducerFromL1Tracks); diff --git a/L1Trigger/Phase2L1ParticleFlow/python/l1ParticleFlow_cff.py b/L1Trigger/Phase2L1ParticleFlow/python/l1ParticleFlow_cff.py new file mode 100644 index 0000000000000..c43e85c93f71d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/l1ParticleFlow_cff.py @@ -0,0 +1,316 @@ +import FWCore.ParameterSet.Config as cms + +from L1Trigger.Phase2L1ParticleFlow.pfTracksFromL1Tracks_cfi import pfTracksFromL1Tracks +from L1Trigger.Phase2L1ParticleFlow.pfClustersFromL1EGClusters_cfi import pfClustersFromL1EGClusters +from L1Trigger.Phase2L1ParticleFlow.pfClustersFromCombinedCalo_cfi import pfClustersFromCombinedCalo +from L1Trigger.Phase2L1ParticleFlow.l1pfProducer_cfi import l1pfProducer + +# Using phase2_hgcalV10 to customize the config for all 106X samples, since there's no other modifier for it +from Configuration.Eras.Modifier_phase2_hgcalV10_cff import phase2_hgcalV10 +from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 + +# Calorimeter part: ecal + hcal + hf only +pfClustersFromCombinedCaloHCal = pfClustersFromCombinedCalo.clone( + hcalHGCTowers = [], hcalDigis = [], + hcalDigisBarrel = True, hcalDigisHF = False, + hadCorrector = cms.string("L1Trigger/Phase2L1ParticleFlow/data/hadcorr_barrel.root"), + resol = cms.PSet( + etaBins = cms.vdouble( 0.700, 1.200, 1.600), + offset = cms.vdouble( 2.582, 2.191, -0.077), + scale = cms.vdouble( 0.122, 0.143, 0.465), + kind = cms.string('calo'), + )) +phase2_hgcalV10.toModify(pfClustersFromCombinedCaloHCal, + hadCorrector = "L1Trigger/Phase2L1ParticleFlow/data/hadcorr_barrel_106X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 0.700, 1.200, 1.600), + offset = cms.vdouble( 3.084, 2.715, 0.107), + scale = cms.vdouble( 0.118, 0.130, 0.442), + kind = cms.string('calo'), + ) +) +phase2_hgcalV11.toModify(pfClustersFromCombinedCaloHCal, + hadCorrector = "L1Trigger/Phase2L1ParticleFlow/data/hadcorr_barrel_110X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 0.700, 1.200, 1.600), + offset = cms.vdouble( 2.909, 2.864, 0.294), + scale = cms.vdouble( 0.119, 0.127, 0.442), + kind = cms.string('calo'), + ) +) + +pfTracksFromL1TracksBarrel = pfTracksFromL1Tracks.clone( + resolCalo = pfClustersFromCombinedCaloHCal.resol.clone(), +) + +pfClustersFromCombinedCaloHF = pfClustersFromCombinedCalo.clone( + ecalCandidates = [], hcalHGCTowers = [], + phase2barrelCaloTowers = [], + hadCorrector = cms.string("L1Trigger/Phase2L1ParticleFlow/data/hfcorr.root"), + resol = cms.PSet( + etaBins = cms.vdouble( 3.500, 4.000, 4.500, 5.000), + offset = cms.vdouble( 1.099, 0.930, 1.009, 1.369), + scale = cms.vdouble( 0.152, 0.151, 0.144, 0.179), + kind = cms.string('calo'), + )) +phase2_hgcalV10.toModify(pfClustersFromCombinedCaloHF, + hcalCandidates = cms.VInputTag(cms.InputTag("hgcalBackEndLayer2Producer","HGCalBackendLayer2Processor3DClustering")), + hadCorrector = "L1Trigger/Phase2L1ParticleFlow/data/hfcorr_106X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 3.500, 4.000, 4.500, 5.000), + offset = cms.vdouble(-0.846, 0.696, 1.313, 1.044), + scale = cms.vdouble( 0.815, 0.164, 0.146, 0.192), + kind = cms.string('calo'), + ) +) +phase2_hgcalV11.toModify(pfClustersFromCombinedCaloHF, + hcalCandidates = cms.VInputTag(cms.InputTag("hgcalBackEndLayer2Producer","HGCalBackendLayer2Processor3DClustering")), + hadCorrector = "L1Trigger/Phase2L1ParticleFlow/data/hfcorr_110X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 3.500, 4.000, 4.500, 5.000), + offset = cms.vdouble(-1.125, 1.220, 1.514, 1.414), + scale = cms.vdouble( 0.868, 0.159, 0.148, 0.194), + kind = cms.string('calo'), + ) +) + +# Calorimeter part: hgcal +from L1Trigger.Phase2L1ParticleFlow.pfClustersFromHGC3DClusters_cfi import pfClustersFromHGC3DClusters + +l1ParticleFlow_calo_Task = cms.Task( + pfClustersFromL1EGClusters , + pfClustersFromCombinedCaloHCal , + pfClustersFromCombinedCaloHF , + pfClustersFromHGC3DClusters +) +l1ParticleFlow_calo = cms.Sequence(l1ParticleFlow_calo_Task) + + +# PF in the barrel +l1pfProducerBarrel = l1pfProducer.clone( + # inputs + tracks = cms.InputTag('pfTracksFromL1TracksBarrel'), + emClusters = [ cms.InputTag('pfClustersFromL1EGClusters') ], + hadClusters = [ cms.InputTag('pfClustersFromCombinedCaloHCal:calibrated') ], + # track-based PUPPI + puppiUsingBareTracks = True, + puppiDrMin = 0.07, + puppiPtMax = 50., + vtxAlgo = "external", + vtxFormat = cms.string("TkPrimaryVertex"), + vtxCollection = cms.InputTag("L1TkPrimaryVertex",""), + # puppi tuning + puAlgo = "LinearizedPuppi", + puppiEtaCuts = cms.vdouble( 1.6 ), # just one bin + puppiPtCuts = cms.vdouble( 1.0 ), + puppiPtCutsPhotons = cms.vdouble( 1.0 ), + puppiPtSlopes = cms.vdouble( 0.3 ), # coefficient for pT + puppiPtSlopesPhotons = cms.vdouble( 0.3 ), + puppiPtZeros = cms.vdouble( 4.0 ), # ballpark pT from PU + puppiPtZerosPhotons = cms.vdouble( 2.5 ), + puppiAlphaSlopes = cms.vdouble( 0.7 ), # coefficient for alpha + puppiAlphaSlopesPhotons = cms.vdouble( 0.7 ), + puppiAlphaZeros = cms.vdouble( 6.0 ), # ballpark alpha from PU + puppiAlphaZerosPhotons = cms.vdouble( 6.0 ), + puppiAlphaCrops = cms.vdouble( 4 ), # max. absolute value for alpha term + puppiAlphaCropsPhotons = cms.vdouble( 4 ), + puppiPriors = cms.vdouble( 5.0 ), + puppiPriorsPhotons = cms.vdouble( 1.0 ), + # regionalize + useRelativeRegionalCoordinates = cms.bool(False), + trackRegionMode = cms.string("atCalo"), + regions = cms.VPSet( + cms.PSet( + etaBoundaries = cms.vdouble(-1.5,1.5), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.3), + phiExtra = cms.double(0.0) + ), + ), +) +l1ParticleFlow_pf_barrel_Task = cms.Task( + pfTracksFromL1TracksBarrel , + l1pfProducerBarrel +) +l1ParticleFlow_pf_barrel = cms.Sequence(l1ParticleFlow_pf_barrel_Task) + + + +# PF in HGCal +pfTracksFromL1TracksHGCal = pfTracksFromL1Tracks.clone( + resolCalo = pfClustersFromHGC3DClusters.resol.clone(), +) +l1pfProducerHGCal = l1pfProducer.clone( + # algo + pfAlgo = "PFAlgo2HGC", + # inputs + tracks = cms.InputTag('pfTracksFromL1TracksHGCal'), + emClusters = [ ], # EM clusters are not used (only added to NTuple for calibration/monitoring) + hadClusters = [ cms.InputTag("pfClustersFromHGC3DClusters") ], + # track-based PUPPI + puppiDrMin = 0.04, + puppiPtMax = 50., + puppiUsingBareTracks = True, + vtxAlgo = "external", + vtxFormat = cms.string("TkPrimaryVertex"), + vtxCollection = cms.InputTag("L1TkPrimaryVertex",""), + # puppi tuning + puAlgo = "LinearizedPuppi", + puppiEtaCuts = cms.vdouble( 2.0, 2.4, 3.1 ), # two bins in the tracker (different pT), one outside + puppiPtCuts = cms.vdouble( 1.0, 2.0, 4.0 ), + puppiPtCutsPhotons = cms.vdouble( 1.0, 2.0, 4.0 ), + puppiPtSlopes = cms.vdouble( 0.3, 0.3, 0.3 ), # coefficient for pT + puppiPtSlopesPhotons = cms.vdouble( 0.4, 0.4, 0.4 ), #When e/g ID not applied, use: cms.vdouble( 0.3, 0.3, 0.3 ), + puppiPtZeros = cms.vdouble( 5.0, 7.0, 9.0 ), # ballpark pT from PU + puppiPtZerosPhotons = cms.vdouble( 3.0, 4.0, 5.0 ), + puppiAlphaSlopes = cms.vdouble( 1.5, 1.5, 2.2 ), + puppiAlphaSlopesPhotons = cms.vdouble( 1.5, 1.5, 2.2 ), + puppiAlphaZeros = cms.vdouble( 6.0, 6.0, 9.0 ), + puppiAlphaZerosPhotons = cms.vdouble( 6.0, 6.0, 9.0 ), + puppiAlphaCrops = cms.vdouble( 3 , 3 , 4 ), # max. absolute value for alpha term + puppiAlphaCropsPhotons = cms.vdouble( 3 , 3 , 4 ), + puppiPriors = cms.vdouble( 5.0, 5.0, 7.0 ), + puppiPriorsPhotons = cms.vdouble( 1.5, 1.5, 5.0 ), #When e/g ID not applied, use: cms.vdouble( 3.5, 3.5, 7.0 ), + # regionalize + useRelativeRegionalCoordinates = cms.bool(False), + trackRegionMode = cms.string("atCalo"), + regions = cms.VPSet( + cms.PSet( + etaBoundaries = cms.vdouble(-2.5,-1.5), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.3), + phiExtra = cms.double(0.0) + ), + cms.PSet( + etaBoundaries = cms.vdouble(1.5,2.5), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.3), + phiExtra = cms.double(0.0) + ), + ), +) +l1pfProducerHGCal.linking.trackCaloDR = 0.1 # more precise cluster positions +l1pfProducerHGCal.linking.ecalPriority = False +l1pfProducerHGCalNoTK = l1pfProducerHGCal.clone(regions = cms.VPSet( + cms.PSet( + etaBoundaries = cms.vdouble(-3,-2.5), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.3), + phiExtra = cms.double(0.0) + ), + cms.PSet( + etaBoundaries = cms.vdouble(2.5,3), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.3), + phiExtra = cms.double(0.0) + ), +)) + +l1ParticleFlow_pf_hgcal_Task = cms.Task( + pfTracksFromL1TracksHGCal , + l1pfProducerHGCal , + l1pfProducerHGCalNoTK +) +l1ParticleFlow_pf_hgcal = cms.Sequence(l1ParticleFlow_pf_hgcal_Task) + + + +# PF in HF +l1pfProducerHF = l1pfProducer.clone( + # inputs + tracks = cms.InputTag(''), # no tracks + emClusters = [ ], + hadClusters = [ cms.InputTag('pfClustersFromCombinedCaloHF:calibrated') ], + hadPtCut = 15, + # not really useful, but for consistency + puppiDrMin = 0.1, + puppiPtMax = 100., + vtxAlgo = "external", + vtxFormat = cms.string("TkPrimaryVertex"), + vtxCollection = cms.InputTag("L1TkPrimaryVertex",""), + # puppi tuning + puAlgo = "LinearizedPuppi", + puppiEtaCuts = cms.vdouble( 5.5 ), # one bin + puppiPtCuts = cms.vdouble( 10. ), + puppiPtCutsPhotons = cms.vdouble( 10. ), # not used (no photons in HF) + puppiPtSlopes = cms.vdouble( 0.25), + puppiPtSlopesPhotons = cms.vdouble( 0.25), # not used (no photons in HF) + puppiPtZeros = cms.vdouble( 14. ), # ballpark pT from PU + puppiPtZerosPhotons = cms.vdouble( 14. ), # not used (no photons in HF) + puppiAlphaSlopes = cms.vdouble( 0.6 ), + puppiAlphaSlopesPhotons = cms.vdouble( 0.6 ), # not used (no photons in HF) + puppiAlphaZeros = cms.vdouble( 9.0 ), + puppiAlphaZerosPhotons = cms.vdouble( 9.0 ), # not used (no photons in HF) + puppiAlphaCrops = cms.vdouble( 4 ), + puppiAlphaCropsPhotons = cms.vdouble( 4 ), # not used (no photons in HF) + puppiPriors = cms.vdouble( 6.0 ), + puppiPriorsPhotons = cms.vdouble( 6.0 ), # not used (no photons in HF) + # regionalize + useRelativeRegionalCoordinates = cms.bool(False), + trackRegionMode = cms.string("atCalo"), + regions = cms.VPSet( + cms.PSet( + etaBoundaries = cms.vdouble(-5.5,-3), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.0), + phiExtra = cms.double(0.0) + ), + cms.PSet( + etaBoundaries = cms.vdouble(3,5.5), + phiSlices = cms.uint32(1), + etaExtra = cms.double(0.0), + phiExtra = cms.double(0.0) + ), + ) +) +l1ParticleFlow_pf_hf_Task = cms.Task( + l1pfProducerHF +) +l1ParticleFlow_pf_hf = cms.Sequence(l1ParticleFlow_pf_hf_Task) + + +# PF in the TSA Region +l1pfProducerTSA = l1pfProducerBarrel.clone( + trackRegionMode = cms.string("atVertex"), + regions = cms.VPSet( + cms.PSet( + etaBoundaries = cms.vdouble(-3,3), + phiSlices = cms.uint32(18), + etaExtra = cms.double(0.0), + phiExtra = cms.double(0.0) + ), + ), +) +l1ParticleFlow_pf_tsa = cms.Sequence( + pfTracksFromL1TracksBarrel + + l1pfProducerTSA +) + +# Merging all outputs +l1pfCandidates = cms.EDProducer("L1TPFCandMultiMerger", + pfProducers = cms.VInputTag( + cms.InputTag("l1pfProducerBarrel"), + cms.InputTag("l1pfProducerHGCal"), + cms.InputTag("l1pfProducerHGCalNoTK"), + cms.InputTag("l1pfProducerHF") + ), + labelsToMerge = cms.vstring("Calo", "TK", "TKVtx", "PF", "Puppi"), +) + +l1ParticleFlow_proper = cms.Sequence( + l1ParticleFlow_calo + + l1ParticleFlow_pf_barrel + + l1ParticleFlow_pf_hgcal + + l1ParticleFlow_pf_hf + + l1pfCandidates +) + +l1ParticleFlow = cms.Sequence(l1ParticleFlow_proper) + +l1ParticleFlowTask = cms.Task( + l1ParticleFlow_calo_Task, + l1ParticleFlow_pf_barrel_Task, + l1ParticleFlow_pf_hgcal_Task, + l1ParticleFlow_pf_hf_Task, + cms.Task(l1pfCandidates) +) diff --git a/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py b/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py new file mode 100644 index 0000000000000..86a99ba09f648 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py @@ -0,0 +1,38 @@ +import FWCore.ParameterSet.Config as cms + +from RecoMET.METProducers.PFMET_cfi import pfMet as _pfMet +_pfMet.calculateSignificance = False +l1PFMetCalo = _pfMet.clone(src = "l1pfCandidates:Calo") +l1PFMetPF = _pfMet.clone(src = "l1pfCandidates:PF") +l1PFMetPuppi = _pfMet.clone(src = "l1pfCandidates:Puppi") + +l1PFMets = cms.Sequence(l1PFMetCalo + l1PFMetPF + l1PFMetPuppi) + +from RecoJets.JetProducers.ak4PFJets_cfi import ak4PFJets as _ak4PFJets +_ak4PFJets.doAreaFastjet = False +ak4PFL1Calo = _ak4PFJets.clone(src = 'l1pfCandidates:Calo') +ak4PFL1PF = _ak4PFJets.clone(src = 'l1pfCandidates:PF') +ak4PFL1Puppi = _ak4PFJets.clone(src = 'l1pfCandidates:Puppi') + +_correctedJets = cms.EDProducer("L1TCorrectedPFJetProducer", + jets = cms.InputTag("_tag_"), + correctorFile = cms.string("L1Trigger/Phase2L1ParticleFlow/data/jecs/jecs.PU200.root"), + correctorDir = cms.string("_dir_"), + copyDaughters = cms.bool(False) +) +# Using phase2_hgcalV10 to customize the config for all 106X samples, since there's no other modifier for it +from Configuration.Eras.Modifier_phase2_hgcalV10_cff import phase2_hgcalV10 +phase2_hgcalV10.toModify(_correctedJets, correctorFile = "L1Trigger/Phase2L1ParticleFlow/data/jecs/jecs.PU200_106X.root") +from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 +phase2_hgcalV11.toModify(_correctedJets, correctorFile = "L1Trigger/Phase2L1ParticleFlow/data/jecs/jecs.PU200_110X.root") + +ak4PFL1CaloCorrected = _correctedJets.clone(jets = 'ak4PFL1Calo', correctorDir = 'L1CaloJets') +ak4PFL1PFCorrected = _correctedJets.clone(jets = 'ak4PFL1PF', correctorDir = 'L1PFJets') +ak4PFL1PuppiCorrected = _correctedJets.clone(jets = 'ak4PFL1Puppi', correctorDir = 'L1PuppiJets') + +l1PFJets = cms.Sequence( + ak4PFL1Calo + ak4PFL1PF + ak4PFL1Puppi + + ak4PFL1CaloCorrected + ak4PFL1PFCorrected + ak4PFL1PuppiCorrected +) + + diff --git a/L1Trigger/Phase2L1ParticleFlow/python/l1pfProducer_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/l1pfProducer_cfi.py new file mode 100644 index 0000000000000..08941eb2bc018 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/l1pfProducer_cfi.py @@ -0,0 +1,72 @@ +import FWCore.ParameterSet.Config as cms + +from math import sqrt + +l1pfProducer = cms.EDProducer("L1TPFProducer", + tracks = cms.InputTag('pfTracksFromL1Tracks'), + muons = cms.InputTag('simGmtStage2Digis',), + tkMuons = cms.InputTag('L1TkMuons'), + # type of muons to be used in PF (enable only one at a time) + useStandaloneMuons = cms.bool(True), + useTrackerMuons = cms.bool(False), + emClusters = cms.VInputTag(cms.InputTag('pfClustersFromHGC3DClustersEM'), cms.InputTag('pfClustersFromL1EGClusters')), + hadClusters = cms.VInputTag(cms.InputTag('pfClustersFromCombinedCalo:calibrated')), + emPtCut = cms.double(0.5), + hadPtCut = cms.double(1.0), + trkPtCut = cms.double(2.0), + trkMinStubs = cms.uint32(4), + trkMaxChi2 = cms.double(15), + etaCharged = cms.double(2.5), + puppiDr = cms.double(0.3), + puppiDrMin = cms.double(0.1), + puppiPtMax = cms.double(999), + puppiEtaCuts = cms.vdouble(1.5, 2.5, 3.0, 5.5), + puppiPtCuts = cms.vdouble(0.0, 3.0, 6.0, 8.0), + puppiPtCutsPhotons = cms.vdouble(0.0, 3.0, 6.0, 8.0), + puppiUsingBareTracks = cms.bool(False), # use PF + vtxRes = cms.double(0.333), + vtxAlgo = cms.string("TP"), + vtxAdaptiveCut = cms.bool(True), + pfAlgo = cms.string("PFAlgo3"), + puAlgo = cms.string("Puppi"), + linking = cms.PSet( + # track -> mu linking configurables + trackMuDR = cms.double(0.2), # accounts for poor resolution of standalone, and missing propagations + trackMuMatch = cms.string("boxBestByPtRatio"), # also drBestByPtRatio + # track -> em linking configurables + trackEmDR = cms.double(0.04), # 1 Ecal crystal size is 0.02, and ~2 cm in HGCal is ~0.007 + trackEmUseAlsoTrackSigma = cms.bool(True), # also use the track uncertainty for electron linking + trackEmMayUseCaloMomenta = cms.bool(True), # use calo momenta for 1 emcalo to 1 track match electrons + # em -> calo linking configurables + emCaloDR = cms.double(0.10), # 1 Hcal tower size is ~0.09 + caloEmPtMinFrac = cms.double(0.5), # Calo object must have an EM Et at least half of that of the EM cluster to allow linking + emCaloUseAlsoCaloSigma = cms.bool(True), # also use the track uncertainty for electron linking + emCaloSubtractionPtSlope = cms.double(1.2), # e/pi ratio of HCal + # track -> calo linking configurables + trackCaloLinkMetric = cms.string("bestByDRPt"), + #trackCaloLinkMetric = cms.string("bestByDR"), + trackCaloDR = cms.double(0.15), + trackCaloNSigmaLow = cms.double(2.0), + trackCaloNSigmaHigh = cms.double(sqrt(1.0)), # sqrt(x) since in the hardware we use sigma squared + useTrackCaloSigma = cms.bool(True), # take the uncertainty on the calo cluster from the track, for linking purposes + sumTkCaloErr2 = cms.bool(True), # add up track calo errors in quadrature instead of linearly + rescaleTracks = cms.bool(False), # if tracks exceed the calo, rescale the track momenta + useCaloTrkWeightedAverage = cms.bool(False), # do the weighted average of track & calo pTs if it's a 1-1 link + # how to deal with unlinked tracks + maxInvisiblePt = cms.double(10.0), # max allowed pt of a track with no calo energy + tightTrackMinStubs = cms.uint32(6), + tightTrackMaxChi2 = cms.double(50), + tightTrackMaxInvisiblePt = cms.double(20), + # how to deal with neutrals + ecalPriority = cms.bool(True), # take first ecal energy when making neutrals + # other features not turned on: reliniking of neutrals to track-matched calo clusters with track excess + caloReLink = cms.bool(False), + caloReLinkDR = cms.double(0.3), + caloReLinkThreshold = cms.double(0.5), + # other features not turned on: matching too high pt tracks to calo but rescaling track pt (not implemented in PFAlgo3) + rescaleUnmatchedTrack = cms.bool(False), + ), + debug = cms.untracked.int32(0), +) + + diff --git a/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromCombinedCalo_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromCombinedCalo_cfi.py new file mode 100644 index 0000000000000..1e3c3f2bc8612 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromCombinedCalo_cfi.py @@ -0,0 +1,57 @@ +import FWCore.ParameterSet.Config as cms + +pfClustersFromCombinedCalo = cms.EDProducer("L1TPFCaloProducer", + ecalCandidates = cms.VInputTag(cms.InputTag('pfClustersFromL1EGClusters')), # using EM from towers in HGC, no longer reading also 'pfClustersFromHGC3DClustersEM' + hcalCandidates = cms.VInputTag(), + hcalDigis = cms.VInputTag(cms.InputTag('simHcalTriggerPrimitiveDigis')), + hcalDigisBarrel = cms.bool(False), + hcalDigisHF = cms.bool(True), + phase2barrelCaloTowers = cms.VInputTag(cms.InputTag("L1EGammaClusterEmuProducer",)), + hcalHGCTowers = cms.VInputTag(cms.InputTag("hgcalTowerProducer:HGCalTowerProcessor") ), + hcalHGCTowersHadOnly = cms.bool(False), # take also EM part from towers + emCorrector = cms.string(""), # no need to correct further + hcCorrector = cms.string(""), # no correction to hcal-only in the default scheme + hadCorrector = cms.string("L1Trigger/Phase2L1ParticleFlow/data/hadcorr.root"), # correction on linked cluster + hadCorrectorEmfMax = cms.double(-1.0), + ecalClusterer = cms.PSet( + grid = cms.string("phase2"), + zsEt = cms.double(0.4), + seedEt = cms.double(0.5), + minClusterEt = cms.double(0.5), + energyWeightedPosition = cms.bool(True), + energyShareAlgo = cms.string("fractions"), + ), + hcalClusterer = cms.PSet( + grid = cms.string("phase2"), + zsEt = cms.double(0.4), + seedEt = cms.double(0.5), + minClusterEt = cms.double(0.8), + energyWeightedPosition = cms.bool(True), + energyShareAlgo = cms.string("fractions"), + ), + linker = cms.PSet( + algo = cms.string("flat"), + + zsEt = cms.double(0.0), ## Ecal and Hcal are already ZS-ed above + seedEt = cms.double(1.0), + minClusterEt = cms.double(1.0), + energyWeightedPosition = cms.bool(True), + energyShareAlgo = cms.string("fractions"), + + grid = cms.string("phase2"), + hoeCut = cms.double(0.1), + minPhotonEt = cms.double(1.0), + minHadronRawEt = cms.double(1.0), + minHadronEt = cms.double(1.0), + noEmInHGC = cms.bool(False) + ), + resol = cms.PSet( + etaBins = cms.vdouble( 1.300, 1.700, 2.800, 3.200, 4.000, 5.000), + offset = cms.vdouble( 2.572, 1.759, 1.858, 2.407, 1.185, 1.658), + scale = cms.vdouble( 0.132, 0.240, 0.090, 0.138, 0.143, 0.147), + kind = cms.string('calo'), + ), + debug = cms.untracked.int32(0), +) + + diff --git a/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClustersEM_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClustersEM_cfi.py new file mode 100644 index 0000000000000..aa13bd5ee3453 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClustersEM_cfi.py @@ -0,0 +1,38 @@ +import FWCore.ParameterSet.Config as cms + +import L1Trigger.Phase2L1ParticleFlow.pfClustersFromHGC3DClusters_cfi + +pfClustersFromHGC3DClustersEM = L1Trigger.Phase2L1ParticleFlow.pfClustersFromHGC3DClusters_cfi.pfClustersFromHGC3DClusters.clone( + emOnly = cms.bool(True), + etMin = cms.double(0.0), + corrector = cms.string("L1Trigger/Phase2L1ParticleFlow/data/emcorr_hgc.root"), + preEmId = cms.string(""), + resol = cms.PSet( + etaBins = cms.vdouble( 1.900, 2.200, 2.500, 2.800, 2.950), + offset = cms.vdouble( 0.566, 0.557, 0.456, 0.470, 0.324), + scale = cms.vdouble( 0.030, 0.024, 0.024, 0.023, 0.042), + kind = cms.string('calo'), + ) +) + + +from Configuration.Eras.Modifier_phase2_hgcalV10_cff import phase2_hgcalV10 +from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 +phase2_hgcalV10.toModify(pfClustersFromHGC3DClustersEM, + corrector = "L1Trigger/Phase2L1ParticleFlow/data/emcorr_hgc_106X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 1.700, 1.900, 2.200, 2.500, 2.800, 2.900), + offset = cms.vdouble( 2.579, 2.176, 1.678, 0.911, 0.672, -2.292), + scale = cms.vdouble( 0.048, 0.026, 0.012, 0.016, 0.022, 0.538), + kind = cms.string('calo') + ), +) +phase2_hgcalV11.toModify(pfClustersFromHGC3DClustersEM, + corrector = "L1Trigger/Phase2L1ParticleFlow/data/emcorr_hgc_110X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 1.700, 1.900, 2.200, 2.500, 2.800, 2.900), + offset = cms.vdouble( 2.581, 2.289, 1.674, 0.927, 0.604, -2.377), + scale = cms.vdouble( 0.046, 0.025, 0.016, 0.017, 0.023, 0.500), + kind = cms.string('calo') + ), +) diff --git a/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClusters_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClusters_cfi.py new file mode 100644 index 0000000000000..81fa6edf5dd30 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromHGC3DClusters_cfi.py @@ -0,0 +1,65 @@ +import FWCore.ParameterSet.Config as cms + +pfClustersFromHGC3DClusters = cms.EDProducer("PFClusterProducerFromHGC3DClusters", + src = cms.InputTag("hgcalBackEndLayer2Producer","HGCalBackendLayer2Processor3DClustering"), + corrector = cms.string("L1Trigger/Phase2L1ParticleFlow/data/hadcorr_HGCal3D_TC.root"), + correctorEmfMax = cms.double(1.125), + preEmId = cms.string("hOverE < 0.3 && hOverE >= 0"), + emVsPionID = cms.PSet( + isPUFilter = cms.bool(False), + preselection = cms.string(""), + method = cms.string("BDT"), # "" to be disabled, "BDT" to be enabled + variables = cms.VPSet( + cms.PSet(name = cms.string("fabs(eta)"), value = cms.string("abs(eta())")), + cms.PSet(name = cms.string("coreShowerLength"), value = cms.string("coreShowerLength()")), + cms.PSet(name = cms.string("maxLayer"), value = cms.string("maxLayer()")), + cms.PSet(name = cms.string("hOverE"), value = cms.string("hOverE()")), + cms.PSet(name = cms.string("sigmaZZ"), value = cms.string("sigmaZZ()")), + ), + weightsFile = cms.string("L1Trigger/Phase2L1ParticleFlow/data/hgcal_egID/Photon_vs_Pion_BDTweights.xml.gz"), + wp = cms.string("0.01") + ), + emVsPUID = cms.PSet( + isPUFilter = cms.bool(True), + preselection = cms.string(""), + method = cms.string("BDT"), # "" to be disabled, "BDT" to be enabled + variables = cms.VPSet( + cms.PSet(name = cms.string("fabs(eta)"), value = cms.string("abs(eta())")), + cms.PSet(name = cms.string("coreShowerLength"), value = cms.string("coreShowerLength()")), + cms.PSet(name = cms.string("maxLayer"), value = cms.string("maxLayer()")), + cms.PSet(name = cms.string("sigmaPhiPhiTot"), value = cms.string("sigmaPhiPhiTot()")), + ), + weightsFile = cms.string("L1Trigger/Phase2L1ParticleFlow/data/hgcal_egID/Photon_Pion_vs_Neutrino_BDTweights.xml.gz"), + wp = cms.string("-0.02") + ), + emOnly = cms.bool(False), + etMin = cms.double(1.0), + resol = cms.PSet( + etaBins = cms.vdouble( 1.900, 2.200, 2.500, 2.800, 2.950), + offset = cms.vdouble( 2.593, 3.089, 2.879, 2.664, 2.947), + scale = cms.vdouble( 0.120, 0.098, 0.099, 0.098, 0.124), + kind = cms.string('calo') + ), +) + + +from Configuration.Eras.Modifier_phase2_hgcalV10_cff import phase2_hgcalV10 +from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 +phase2_hgcalV10.toModify(pfClustersFromHGC3DClusters, + corrector = "L1Trigger/Phase2L1ParticleFlow/data/hadcorr_HGCal3D_TC_106X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 1.700, 1.900, 2.200, 2.500, 2.800, 2.900), + offset = cms.vdouble(-0.819, 0.900, 2.032, 2.841, 2.865, 1.237), + scale = cms.vdouble( 0.320, 0.225, 0.156, 0.108, 0.119, 0.338), + kind = cms.string('calo') + ), +) +phase2_hgcalV11.toModify(pfClustersFromHGC3DClusters, + corrector = "L1Trigger/Phase2L1ParticleFlow/data/hadcorr_HGCal3D_TC_110X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 1.700, 1.900, 2.200, 2.500, 2.800, 2.900), + offset = cms.vdouble( 1.413, 1.631, 2.366, 2.768, 2.908, 1.434), + scale = cms.vdouble( 0.169, 0.155, 0.132, 0.114, 0.137, 0.338), + kind = cms.string('calo') + ), +) diff --git a/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromL1EGClusters_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromL1EGClusters_cfi.py new file mode 100644 index 0000000000000..ff4e72aa7ec17 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/pfClustersFromL1EGClusters_cfi.py @@ -0,0 +1,37 @@ +import FWCore.ParameterSet.Config as cms + +pfClustersFromL1EGClusters = cms.EDProducer("PFClusterProducerFromL1EGClusters", + src = cms.InputTag("L1EGammaClusterEmuProducer",), + etMin = cms.double(0.5), + corrector = cms.string("L1Trigger/Phase2L1ParticleFlow/data/emcorr_barrel.root"), + resol = cms.PSet( + etaBins = cms.vdouble( 0.700, 1.200, 1.600), + offset = cms.vdouble( 0.873, 1.081, 1.563), + scale = cms.vdouble( 0.011, 0.015, 0.012), + kind = cms.string('calo'), + ) +) + +# use phase2_hgcalV10 to customize for 106X L1TDR MC even in the barrel, since there's no other modifier for it +from Configuration.Eras.Modifier_phase2_hgcalV10_cff import phase2_hgcalV10 +from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 +phase2_hgcalV10.toModify(pfClustersFromL1EGClusters, + corrector = "", # In this setup, TP's are already calibrated correctly :-) + # L1Trigger/Phase2L1ParticleFlow/data/emcorr_barrel_106X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 0.700, 1.200, 1.600), + offset = cms.vdouble( 0.946, 0.948, 1.171), + scale = cms.vdouble( 0.011, 0.018, 0.019), + kind = cms.string('calo') + ) +) +phase2_hgcalV11.toModify(pfClustersFromL1EGClusters, + corrector = "", # In this setup, TP's are already calibrated correctly :-) + # L1Trigger/Phase2L1ParticleFlow/data/emcorr_barrel_110X.root", + resol = cms.PSet( + etaBins = cms.vdouble( 0.700, 1.200, 1.600), + offset = cms.vdouble( 0.838, 0.924, 1.101), + scale = cms.vdouble( 0.012, 0.017, 0.018), + kind = cms.string('calo') + ) +) diff --git a/L1Trigger/Phase2L1ParticleFlow/python/pfTracksFromL1Tracks_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/pfTracksFromL1Tracks_cfi.py new file mode 100644 index 0000000000000..93f8ddb758aed --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/python/pfTracksFromL1Tracks_cfi.py @@ -0,0 +1,22 @@ +import FWCore.ParameterSet.Config as cms + +pfTracksFromL1Tracks = cms.EDProducer("PFTrackProducerFromL1Tracks", + L1TrackTag = cms.InputTag("TTTracksFromTrackletEmulation", "Level1TTTracks"), + nParam = cms.uint32(4), + resolCalo = cms.PSet( + etaBins = cms.vdouble( 1.300, 1.700, 2.800, 3.200, 4.000, 5.000), + offset = cms.vdouble( 2.688, 1.382, 2.096, 1.022, 0.757, 0.185), + scale = cms.vdouble( 0.154, 0.341, 0.105, 0.255, 0.208, 0.306), + ptMin = cms.vdouble( 5.000, 5.000, 5.000, 5.000, 5.000, 5.000), + ptMax = cms.vdouble(999999, 999999, 999999, 999999, 999999, 999999), + kind = cms.string('calo'), + ), + resolTrack = cms.PSet( + etaBins = cms.vdouble( 0.800, 1.200, 1.500, 2.000, 2.500), + offset = cms.vdouble( 0.007, 0.009, 0.011, 0.015, 0.025), + scale = cms.vdouble( 0.275, 0.404, 0.512, 0.480, 1.132), + kind = cms.string('track'), + ) + +) + diff --git a/L1Trigger/Phase2L1ParticleFlow/src/BitwisePFAlgo.cc b/L1Trigger/Phase2L1ParticleFlow/src/BitwisePFAlgo.cc new file mode 100644 index 0000000000000..570d899939449 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/BitwisePFAlgo.cc @@ -0,0 +1,196 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/BitwisePFAlgo.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h" + +//#define REG_HGCal +#include "ref/pfalgo2hgc_ref.h" +#include "ref/pfalgo3_ref.h" +#include "utils/DiscretePF2Firmware.h" +#include "utils/Firmware2DiscretePF.h" + +using namespace l1tpf_impl; + +BitwisePFAlgo::BitwisePFAlgo(const edm::ParameterSet &iConfig) : PFAlgoBase(iConfig), config_(nullptr) { + const edm::ParameterSet &bitwiseConfig = iConfig.getParameter("bitwiseConfig"); + const std::string &algo = iConfig.getParameter("bitwiseAlgo"); + debug_ = iConfig.getUntrackedParameter("debugBitwisePFAlgo", iConfig.getUntrackedParameter("debug", 0)); + if (algo == "pfalgo3") { + algo_ = AlgoChoice::algo3; + config_ = std::make_shared(bitwiseConfig.getParameter("NTRACK"), + bitwiseConfig.getParameter("NEMCALO"), + bitwiseConfig.getParameter("NCALO"), + bitwiseConfig.getParameter("NMU"), + bitwiseConfig.getParameter("NPHOTON"), + bitwiseConfig.getParameter("NSELCALO"), + bitwiseConfig.getParameter("NALLNEUTRAL"), + bitwiseConfig.getParameter("DR2MAX_TK_MU"), + bitwiseConfig.getParameter("DR2MAX_TK_EM"), + bitwiseConfig.getParameter("DR2MAX_EM_CALO"), + bitwiseConfig.getParameter("DR2MAX_TK_CALO"), + bitwiseConfig.getParameter("TK_MAXINVPT_LOOSE"), + bitwiseConfig.getParameter("TK_MAXINVPT_TIGHT")); + pfalgo3_ref_set_debug(debug_); + } else if (algo == "pfalgo2hgc") { + algo_ = AlgoChoice::algo2hgc; + config_ = std::make_shared(bitwiseConfig.getParameter("NTRACK"), + bitwiseConfig.getParameter("NCALO"), + bitwiseConfig.getParameter("NMU"), + bitwiseConfig.getParameter("NSELCALO"), + bitwiseConfig.getParameter("DR2MAX_TK_MU"), + bitwiseConfig.getParameter("DR2MAX_TK_CALO"), + bitwiseConfig.getParameter("TK_MAXINVPT_LOOSE"), + bitwiseConfig.getParameter("TK_MAXINVPT_TIGHT")); + pfalgo2hgc_ref_set_debug(debug_); + } else { + throw cms::Exception("Configuration", "Unsupported bitwiseAlgo " + algo); + } +} + +BitwisePFAlgo::~BitwisePFAlgo() {} + +void BitwisePFAlgo::runPF(Region &r) const { + initRegion(r); + + std::unique_ptr calo(new HadCaloObj[config_->nCALO]); + std::unique_ptr track(new TkObj[config_->nTRACK]); + std::unique_ptr mu(new MuObj[config_->nMU]); + std::unique_ptr outch(new PFChargedObj[config_->nTRACK]); + std::unique_ptr outne(new PFNeutralObj[config_->nSELCALO]); + std::unique_ptr outmu(new PFChargedObj[config_->nMU]); + + dpf2fw::convert(config_->nTRACK, r.track, track.get()); + dpf2fw::convert(config_->nCALO, r.calo, calo.get()); + dpf2fw::convert(config_->nMU, r.muon, mu.get()); + + if (debug_) { + dbgPrintf( + "BitwisePF\nBitwisePF region eta [ %+5.2f , %+5.2f ], phi [ %+5.2f , %+5.2f ], fiducial eta [ %+5.2f , %+5.2f " + "], phi [ %+5.2f , %+5.2f ], algo = %d\n", + r.etaMin - r.etaExtra, + r.etaMax + r.etaExtra, + r.phiCenter - r.phiHalfWidth - r.phiExtra, + r.phiCenter + r.phiHalfWidth + r.phiExtra, + r.etaMin, + r.etaMax, + r.phiCenter - r.phiHalfWidth, + r.phiCenter + r.phiHalfWidth, + static_cast(algo_)); + dbgPrintf("BitwisePF \t N(track) %3lu N(em) %3lu N(calo) %3lu N(mu) %3lu\n", + r.track.size(), + r.emcalo.size(), + r.calo.size(), + r.muon.size()); + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + dbgPrintf( + "BitwisePF \t track %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + " fid %1d calo ptErr %7.2f stubs %2d chi2 %7.1f\n", + itk, + tk.floatPt(), + tk.floatPtErr(), + tk.floatVtxEta(), + tk.floatVtxPhi(), + tk.floatEta(), + tk.floatPhi(), + int(r.fiducialLocal(tk.floatEta(), tk.floatPhi())), + tk.floatCaloPtErr(), + int(tk.hwStubs), + tk.hwChi2 * 0.1f); + } + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + const auto &em = r.emcalo[iem]; + dbgPrintf( + "BitwisePF \t EM %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + " fid %1d calo ptErr %7.2f\n", + iem, + em.floatPt(), + em.floatPtErr(), + em.floatEta(), + em.floatPhi(), + em.floatEta(), + em.floatPhi(), + int(r.fiducialLocal(em.floatEta(), em.floatPhi())), + em.floatPtErr()); + } + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + dbgPrintf( + "BitwisePF \t calo %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + " fid %1d calo ptErr %7.2f em pt %7.2f \n", + ic, + calo.floatPt(), + calo.floatPtErr(), + calo.floatEta(), + calo.floatPhi(), + calo.floatEta(), + calo.floatPhi(), + int(r.fiducialLocal(calo.floatEta(), calo.floatPhi())), + calo.floatPtErr(), + calo.floatEmPt()); + } + for (int im = 0, nm = r.muon.size(); im < nm; ++im) { + auto &mu = r.muon[im]; + dbgPrintf( + "BitwisePF \t muon %3d: pt %7.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + " fid %1d \n", + im, + mu.floatPt(), + mu.floatEta(), + mu.floatPhi(), + mu.floatEta(), + mu.floatPhi(), + int(r.fiducialLocal(mu.floatEta(), mu.floatPhi()))); + } + } + switch (algo_) { + case AlgoChoice::algo3: { + pfalgo3_config *config3 = static_cast(config_.get()); + std::unique_ptr emcalo(new EmCaloObj[config3->nEMCALO]); + std::unique_ptr outpho(new PFNeutralObj[config3->nPHOTON]); + + dpf2fw::convert(config3->nEMCALO, r.emcalo, emcalo.get()); + pfalgo3_ref(*config3, + emcalo.get(), + calo.get(), + track.get(), + mu.get(), + outch.get(), + outpho.get(), + outne.get(), + outmu.get()); + + fw2dpf::convert(config3->nTRACK, outch.get(), r.track, r.pf); // FIXME works only with a 1-1 mapping + fw2dpf::convert(config3->nPHOTON, outpho.get(), r.pf); + fw2dpf::convert(config3->nSELCALO, outne.get(), r.pf); + } break; + case AlgoChoice::algo2hgc: { + pfalgo2hgc_ref(*config_, calo.get(), track.get(), mu.get(), outch.get(), outne.get(), outmu.get()); + fw2dpf::convert(config_->nTRACK, outch.get(), r.track, r.pf); // FIXME works only with a 1-1 mapping + fw2dpf::convert(config_->nSELCALO, outne.get(), r.pf); + } break; + }; + + if (debug_) { + dbgPrintf("BitwisePF \t Output N(ch) %3u/%3u N(nh) %3u/%3u N(ph) %3u/%u [all/fiducial]\n", + r.nOutput(l1tpf_impl::Region::charged_type, false, false), + r.nOutput(l1tpf_impl::Region::charged_type, false, true), + r.nOutput(l1tpf_impl::Region::neutral_hadron_type, false, false), + r.nOutput(l1tpf_impl::Region::neutral_hadron_type, false, true), + r.nOutput(l1tpf_impl::Region::photon_type, false, false), + r.nOutput(l1tpf_impl::Region::photon_type, false, true)); + for (int ipf = 0, npf = r.pf.size(); ipf < npf; ++ipf) { + const auto &pf = r.pf[ipf]; + dbgPrintf( + "BitwisePF \t pf %3d: pt %7.2f pid %d vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + "fid %1d\n", + ipf, + pf.floatPt(), + int(pf.hwId), + pf.floatVtxEta(), + pf.floatVtxPhi(), + pf.floatEta(), + pf.floatPhi(), + int(r.fiducialLocal(pf.floatEta(), pf.floatPhi()))); + } + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/COEFile.cc b/L1Trigger/Phase2L1ParticleFlow/src/COEFile.cc new file mode 100644 index 0000000000000..b0b2c8364803e --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/COEFile.cc @@ -0,0 +1,130 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/COEFile.h" + +using namespace l1tpf_impl; + +COEFile::COEFile(const edm::ParameterSet& iConfig) + : file(nullptr), + coeFileName(iConfig.getUntrackedParameter("coeFileName", "")), + bset_string_(""), + ntracksmax(iConfig.getUntrackedParameter("ntracksmax")), + phiSlices(iConfig.getParameter>("regions")[0].getParameter("phiSlices")), + debug_(iConfig.getUntrackedParameter("debug", 0)) { + file = fopen(coeFileName.c_str(), "w"); + writeHeaderToFile(); + bset_.resize(tracksize); +} + +COEFile::~COEFile() {} + +void COEFile::writeHeaderToFile() { + char depth_width[256]; + snprintf(depth_width, + 255, + "; of depth=%i, and width=%i. In this case, values are specified\n", + ntracksmax, + tracksize * phiSlices); + std::vector vheader = {"; Sample memory initialization file for Dual Port Block Memory,\n", + "; v3.0 or later.\n", + "; Board: VCU118\n", + "; tmux: 1\n", + ";\n", + "; This .COE file specifies the contents for a block memory\n", + std::string(depth_width), + "; in binary format.\n", + "memory_initialization_radix=2;\n", + "memory_initialization_vector=\n"}; + for (uint32_t i = 0; i < vheader.size(); ++i) + fprintf(file, "%s", vheader[i].c_str()); +} + +void COEFile::writeTracksToFile(const std::vector& regions, bool print) { + PropagatedTrack current_track; + bool has_track = false; + for (unsigned int irow = 0; irow < ntracksmax; irow++) { + for (unsigned int icol = 0; icol < regions.size(); icol++) { + if (regions[icol].track.size() <= irow) + has_track = false; + else + has_track = true; + + if (has_track) { + // select the track that will be converted to a bit string + current_track = regions[icol].track[irow]; + + // convert the values in a PropogatedTrack to a 96-bit track word + for (unsigned int iblock = 0; iblock < track_word_block_sizes.size(); iblock++) { + for (unsigned int ibit = 0; ibit < track_word_block_sizes[iblock]; ibit++) { + int offset = std::accumulate(track_word_block_sizes.begin(), track_word_block_sizes.begin() + iblock, 0); + switch (iblock) { + case 0: + bset_.set(ibit + offset, getBit(current_track.hwPt, ibit)); + break; + case 1: + bset_.set(ibit + offset, current_track.hwCharge); + break; + case 2: + bset_.set(ibit + offset, getBit(current_track.hwVtxPhi, ibit)); + break; + case 3: + bset_.set(ibit + offset, getBit(current_track.hwVtxEta, ibit)); + break; + case 4: + bset_.set(ibit + offset, getBit(current_track.hwZ0, ibit)); + break; + case 5: + bset_.set(ibit + offset, false); + break; + case 6: + bset_.set(ibit + offset, getBit(current_track.hwChi2, ibit)); + break; + case 7: + bset_.set(ibit + offset, false); + break; + case 8: + bset_.set(ibit + offset, getBit(current_track.hwStubs, ibit)); + break; + case 9: + bset_.set(ibit + offset, false); + break; + } + } + } + + // print the track word to the COE file + boost::to_string(bset_, bset_string_); + fprintf(file, "%s", bset_string_.c_str()); + + // print some debugging information + if (debug_ && print && irow == 0 && icol == 0) { + printf("region: eta=[%f,%f] phi=%f+/-%f\n", + regions[icol].etaMin, + regions[icol].etaMax, + regions[icol].phiCenter, + regions[icol].phiHalfWidth); + printf("l1t::PFTrack (pT,eta,phi) [float] = (%f,%f,%f)\n", + current_track.src->p4().Pt(), + current_track.src->p4().Eta(), + current_track.src->p4().Phi()); + printf("l1t::PFTrack (pT,eta,phi) [int] = (%i,%i,%i)\n", + current_track.src->hwPt(), + current_track.src->hwEta(), + current_track.src->hwPhi()); + printf("l1tpf_impl::PropagatedTrack (1/pT,eta,phi) [int,10] = (%i,%i,%i)\n", + current_track.hwPt, + current_track.hwVtxEta, + current_track.hwVtxPhi); + printf("l1tpf_impl::PropagatedTrack (1/pT,eta,phi) [int,2] = (%s,%s,%s)\n", + std::bitset<16>(current_track.hwPt).to_string().c_str(), + std::bitset<32>(current_track.hwVtxEta).to_string().c_str(), + std::bitset<32>(current_track.hwVtxPhi).to_string().c_str()); + printf("bitset = %s\n", bset_string_.c_str()); + } + } else { + bset_.reset(); + boost::to_string(bset_, bset_string_); + fprintf(file, "%s", bset_string_.c_str()); + } + } + fprintf(file, (irow == ntracksmax - 1) ? ";\n" : ",\n"); + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/CaloClusterer.cc b/L1Trigger/Phase2L1ParticleFlow/src/CaloClusterer.cc new file mode 100644 index 0000000000000..7696909ce64c2 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/CaloClusterer.cc @@ -0,0 +1,636 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/CaloClusterer.h" + +#include + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "DataFormats/Common/interface/RefToPtr.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/Exception.h" + +const float l1tpf_calo::Phase1Grid::phase1_towerEtas_[l1tpf_calo::Phase1Grid::phase1_nEta_] = { + 0, 0.087, 0.174, 0.261, 0.348, 0.435, 0.522, 0.609, 0.696, 0.783, 0.870, 0.957, 1.044, 1.131, + 1.218, 1.305, 1.392, 1.479, 1.566, 1.653, 1.740, 1.830, 1.930, 2.043, 2.172, 2.322, 2.5, 2.650, + 2.853, 3.139, 3.314, 3.489, 3.664, 3.839, 4.013, 4.191, 4.363, 4.538, 4.716, 4.889, 5.191}; +const float l1tpf_calo::Phase2Grid::phase2_towerEtas_[l1tpf_calo::Phase2Grid::phase2_nEta_] = { + 0, 0.087, 0.174, 0.261, 0.348, 0.435, 0.522, 0.609, 0.696, 0.783, 0.870, 0.957, 1.044, 1.131, 1.218, 1.305, + 1.392, 1.479, 1.564, 1.648, 1.732, 1.817, 1.901, 1.986, 2.071, 2.155, 2.240, 2.324, 2.409, 2.493, 2.577, 2.662, + 2.747, 2.831, 2.915, 3.0, 3.139, 3.314, 3.489, 3.664, 3.839, 4.013, 4.191, 4.363, 4.538, 4.716, 4.889, 5.191}; + +l1tpf_calo::Phase1GridBase::Phase1GridBase( + int nEta, int nPhi, int ietaCoarse, int ietaVeryCoarse, const float *towerEtas) + : Grid(2 * ((ietaCoarse - 1) * nPhi + (ietaVeryCoarse - ietaCoarse) * (nPhi / 2) + + (nEta - ietaVeryCoarse + 1) * (nPhi / 4))), + nEta_(nEta), + nPhi_(nPhi), + ietaCoarse_(ietaCoarse), + ietaVeryCoarse_(ietaVeryCoarse), + towerEtas_(towerEtas), + cell_map_(2 * nEta * nPhi, -1) { + int icell = 0; + for (int ie = -nEta_; ie <= nEta_; ++ie) { + int absie = std::abs(ie); + for (int iph = 1; iph <= nPhi_; ++iph) { + if (!valid_ieta_iphi(ie, iph)) + continue; + ieta_[icell] = ie; + iphi_[icell] = iph; + eta_[icell] = (ie > 0 ? 0.5 : -0.5) * (towerEtas_[absie - 1] + towerEtas_[absie]); + etaWidth_[icell] = (towerEtas_[absie] - towerEtas_[absie - 1]); + phiWidth_[icell] = 2 * M_PI / nPhi_; + if (absie >= ietaVeryCoarse_) + phiWidth_[icell] *= 4; + else if (absie >= ietaCoarse_) + phiWidth_[icell] *= 2; + phi_[icell] = (iph - 1) * 2 * M_PI / nPhi_ + 0.5 * phiWidth_[icell]; + if (phi_[icell] > M_PI) + phi_[icell] -= 2 * M_PI; + std::fill(neighbours_[icell].begin(), neighbours_[icell].end(), -1); + cell_map_[(ie + nEta_) + 2 * nEta_ * (iph - 1)] = icell; + icell++; + } + } + assert(unsigned(icell) == ncells_); + // now link the cells + for (icell = 0; icell < int(ncells_); ++icell) { + int ie = ieta_[icell], iph = iphi_[icell]; + int ineigh = 0; + for (int deta = -1; deta <= +1; ++deta) { + for (int dphi = -1; dphi <= +1; ++dphi) { + if (deta == 0 && dphi == 0) + continue; + neighbours_[icell][ineigh++] = imove(ie, iph, deta, dphi); + } + } + } + //// consistency check 1: check that find_cell works + //// uncomment to check that there's no holes in the grid + //for (float teta = 0; teta <= 5.0; teta += 0.02) { + // for (float tphi = -M_PI; tphi <= M_PI; tphi += 0.02) { + // find_cell(+teta, tphi); + // find_cell(-teta, tphi); + // } + //} +} + +int l1tpf_calo::Phase1GridBase::find_cell(float eta, float phi) const { + int ieta = + (eta != 0) ? std::distance(towerEtas_, std::lower_bound(towerEtas_, towerEtas_ + nEta_, std::abs(eta))) : 1; + if (ieta == nEta_) + return -1; // outside bounds + assert(ieta > 0 && ieta < nEta_); + if (ieta > nEta_) + ieta = nEta_; + if (eta < 0) + ieta = -ieta; + phi = reco::reduceRange(phi); // [-PI, PI] + if (phi < 0) // then bring to [0, 2*PI] + phi += 2 * M_PI; + int iphi = std::floor(phi * nPhi_ / (2 * M_PI)); + if (phi >= 2 * M_PI) + iphi = nPhi_ - 1; // fix corner case due to roundings etc + assert(iphi < nPhi_); + if (std::abs(ieta) >= ietaVeryCoarse_) + iphi -= (iphi % 4); + else if (std::abs(ieta) >= ietaCoarse_) + iphi -= (iphi % 2); + iphi += 1; + //// uncomment to check validity of derived coordinates + //if (!valid_ieta_iphi(ieta,iphi)) { + // printf("Error in finding cell for eta %+7.4f phi %+7.4f, got ieta = %+3d iphi %2d which is not valid\n", + // eta, phi, ieta, iphi); + //} + assert(valid_ieta_iphi(ieta, iphi)); + int icell = ifind_cell(ieta, iphi); + assert(icell != -1); + + //// uncomment to check that the point is really in the cell + //if (std::abs(eta - eta_[icell]) > 0.501*etaWidth_[icell] || std::abs(deltaPhi(phi, phi_[icell])) > 0.501*phiWidth_[icell]) { + // printf("Mismatch in finding cell for eta %+7.4f phi %+7.4f, got ieta = %+3d iphi %2d which has eta %+7.4f +- %.4f phi %+7.4f +- %.4f ; deta = %+7.4f dphi = %+7.4f\n", + // eta, phi, ieta, iphi, eta_[icell], etaWidth_[icell], phi_[icell], phiWidth_[icell], eta - eta_[icell], deltaPhi(phi, phi_[icell])); + //} + //assert(std::abs(eta - eta_[icell]) <= 0.5*etaWidth_[icell]); + //assert(std::abs(deltaPhi(phi, phi_[icell])) <= 0.5*phiWidth_[icell]); + return icell; +} + +int l1tpf_calo::Phase1GridBase::imove(int ieta, int iphi, int deta, int dphi) { + int ie = ieta, iph = iphi; + switch (deta) { + case -1: + ie = (ie == -nEta_ ? 0 : (ie == +1 ? -1 : ie - 1)); + break; + case +1: + ie = (ie == +nEta_ ? 0 : (ie == -1 ? +1 : ie + 1)); + break; + case 0: + break; + default: + assert(false); + }; + if (ie == 0) + return -1; + switch (dphi) { + case -1: + iph = (iph == 1 ? nPhi_ : iph - 1); + break; + case +1: + iph = (iph == nPhi_ ? 1 : iph + 1); + break; + case 0: + break; + default: + assert(false); + }; + if (!valid_ieta_iphi(ie, iph)) + return -1; + int icell = ifind_cell(ie, iph); + assert(!(ie == ieta && iph == iphi)); + assert(icell != -1); + assert(icell != ifind_cell(ieta, iphi)); + return icell; +} + +const l1tpf_calo::Grid *l1tpf_calo::getGrid(const std::string &type) { + static const Phase1Grid _phase1Grid; + static const Phase2Grid _phase2Grid; + if (type == "phase1") + return &_phase1Grid; + else if (type == "phase2") + return &_phase2Grid; + else + throw cms::Exception("Configuration") << "Unsupported grid type '" << type << "'\n"; +} + +l1tpf_calo::SingleCaloClusterer::SingleCaloClusterer(const edm::ParameterSet &pset) + : grid_(getGrid(pset.getParameter("grid"))), + rawet_(*grid_), + unclustered_(*grid_), + precluster_(*grid_), + clusterIndex_(*grid_), + cellKey_(*grid_), + clusters_(), + nullCluster_(), + zsEt_(pset.getParameter("zsEt")), + seedEt_(pset.getParameter("seedEt")), + minClusterEt_(pset.getParameter("minClusterEt")), + minEtToGrow_(pset.existsAs("minEtToGrow") ? pset.getParameter("minEtToGrow") : -1), + energyWeightedPosition_(pset.getParameter("energyWeightedPosition")) { + std::string energyShareAlgo = pset.getParameter("energyShareAlgo"); + if (energyShareAlgo == "fractions") + energyShareAlgo_ = EnergyShareAlgo::Fractions; + else if (energyShareAlgo == "none") + energyShareAlgo_ = EnergyShareAlgo::None; + else if (energyShareAlgo == "greedy") + energyShareAlgo_ = EnergyShareAlgo::Greedy; + else if (energyShareAlgo == "crude") + energyShareAlgo_ = EnergyShareAlgo::Crude; + else + throw cms::Exception("Configuration") << "Unsupported energyShareAlgo '" << energyShareAlgo << "'\n"; +} + +l1tpf_calo::SingleCaloClusterer::~SingleCaloClusterer() {} + +void l1tpf_calo::SingleCaloClusterer::clear() { + rawet_.zero(); + clusters_.clear(); + clusterIndex_.fill(-1); +} + +void l1tpf_calo::SingleCaloClusterer::run() { + unsigned int i, ncells = grid_->size(); + + // kill zeros. count non-zeros, for linking later + cellKey_.fill(-1); + int key = 0; + for (i = 0; i < ncells; ++i) { + if (rawet_[i] < zsEt_) { + rawet_[i] = 0; + } else { + cellKey_[i] = key++; + } + } + + precluster_.clear(); + // pre-cluster step 1: at each cell, set the value equal to itself if it's a local maxima, zero otherwise + // can be done in parallel on all cells + for (i = 0; i < ncells; ++i) { + if (rawet_[i] > seedEt_) { + precluster_[i].ptLocalMax = rawet_[i]; + //// uncommment code below for debugging the clustering + //printf(" candidate precluster pt %7.2f at %4d (ieta %+3d iphi %2d)\n", rawet_[i], i, grid_->ieta(i), grid_->iphi(i)); + for (int ineigh = 0; ineigh <= 3; ++ineigh) { + if (rawet_.neigh(i, ineigh) > rawet_[i]) + precluster_[i].ptLocalMax = 0; + //// uncommment code below for debugging the clustering + //int ncell = grid_->neighbour(i,ineigh); + //if (ncell == -1) printf(" \t neigh %d is null\n", ineigh); + //else printf(" \t neigh %d at %4d (ieta %+3d iphi %2d) has pt %7.2f: comparison %1d \n", ineigh, ncell, grid_->ieta(ncell), grid_->iphi(ncell), rawet_[ncell], precluster_[i].ptLocalMax > 0); + } + for (int ineigh = 4; ineigh < 8; ++ineigh) { + if (rawet_.neigh(i, ineigh) >= rawet_[i]) + precluster_[i].ptLocalMax = 0; + //// uncommment code below for debugging the clustering + //int ncell = grid_->neighbour(i,ineigh); + //if (ncell == -1) printf(" \t neigh %d is null\n", ineigh); + //else printf(" \t neigh %d at %4d (ieta %+3d iphi %2d) has pt %7.2f: comparison %1d \n", ineigh, ncell, grid_->ieta(ncell), grid_->iphi(ncell), rawet_[ncell], precluster_[i].ptLocalMax > 0); + } + } + } + // pre-cluster step 2: compute information from neighbouring local max, for energy sharing purposes + for (i = 0; i < ncells; ++i) { + if (precluster_[i].ptLocalMax == 0) { + switch (energyShareAlgo_) { + case EnergyShareAlgo::Fractions: { + float tot = 0; + for (int ineigh = 0; ineigh < 8; ++ineigh) { + tot += precluster_.neigh(i, ineigh).ptLocalMax; + } + precluster_[i].ptOverNeighLocalMaxSum = tot ? rawet_[i] / tot : 0; + } break; + case EnergyShareAlgo::None: + precluster_[i].ptOverNeighLocalMaxSum = rawet_[i]; + break; + case EnergyShareAlgo::Greedy: { + float maxet = 0; + for (int ineigh = 0; ineigh < 8; ++ineigh) { + maxet = std::max(maxet, precluster_.neigh(i, ineigh).ptLocalMax); + } + precluster_[i].ptOverNeighLocalMaxSum = maxet; + } break; + case EnergyShareAlgo::Crude: { + int number = 0; + for (int ineigh = 0; ineigh < 8; ++ineigh) { + number += (precluster_.neigh(i, ineigh).ptLocalMax > 0); + } + precluster_[i].ptOverNeighLocalMaxSum = (number > 1 ? 0.5 : 1.0) * rawet_[i]; + } break; + } + } + } + + clusterIndex_.fill(-1); + clusters_.clear(); + unclustered_ = rawet_; + // cluster: at each localMax cell, take itself plus the weighted contributions of the neighbours + Cluster cluster; + for (i = 0; i < ncells; ++i) { + if (precluster_[i].ptLocalMax > 0) { + float myet = rawet_[i]; + float tot = myet; + float avg_eta = 0; + float avg_phi = 0; + cluster.clear(); + cluster.constituents.emplace_back(i, 1.0); + for (int ineigh = 0; ineigh < 8; ++ineigh) { + int ineighcell = grid_->neighbour(i, ineigh); + if (ineighcell == -1) + continue; // skip dummy cells + float fracet = 0; + switch (energyShareAlgo_) { + case EnergyShareAlgo::Fractions: + fracet = myet * precluster_.neigh(i, ineigh).ptOverNeighLocalMaxSum; + break; + case EnergyShareAlgo::None: + fracet = precluster_.neigh(i, ineigh).ptOverNeighLocalMaxSum; + break; + case EnergyShareAlgo::Greedy: + fracet = (myet == precluster_.neigh(i, ineigh).ptOverNeighLocalMaxSum ? rawet_.neigh(i, ineigh) : 0); + break; + case EnergyShareAlgo::Crude: + fracet = precluster_.neigh(i, ineigh).ptOverNeighLocalMaxSum; + break; + } + if (fracet == 0) + continue; + tot += fracet; + cluster.constituents.emplace_back(ineighcell, fracet / rawet_.neigh(i, ineigh)); + if (energyWeightedPosition_) { + avg_eta += fracet * (grid_->eta(ineighcell) - grid_->eta(i)); + avg_phi += fracet * deltaPhi(grid_->phi(ineighcell), grid_->phi(i)); + } + } + if (tot > minClusterEt_) { + cluster.et = tot; + unclustered_[i] = 0; + for (int ineigh = 0; ineigh < 8; ++ineigh) { + int ineighcell = grid_->neighbour(i, ineigh); + if (ineighcell == -1) + continue; // skip dummy cells + unclustered_[ineighcell] = 0; + } + if (energyWeightedPosition_) { + cluster.eta = grid_->eta(i) + avg_eta / tot; + cluster.phi = grid_->phi(i) + avg_phi / tot; + // wrap around phi + cluster.phi = reco::reduceRange(cluster.phi); + } else { + cluster.eta = grid_->eta(i); + cluster.phi = grid_->phi(i); + } + clusterIndex_[i] = clusters_.size(); + clusters_.push_back(cluster); + } + } + } + if (minEtToGrow_ > 0) + grow(); +} + +void l1tpf_calo::SingleCaloClusterer::grow() { + int selneighs[4] = {1, 3, 4, 6}; // -eta, -phi, +phi, +eta + std::vector toreset; + for (Cluster &cluster : clusters_) { + if (cluster.et > minEtToGrow_) { + int i = cluster.constituents.front().first; + for (int side = 0; side < 4; ++side) { + int neigh = grid_->neighbour(i, selneighs[side]); + if (neigh == -1) + continue; + for (int in = 0; in < 8; ++in) { + int n2 = grid_->neighbour(neigh, in); + if (n2 == -1) + continue; + cluster.et += unclustered_[n2]; + if (unclustered_[n2]) { + cluster.constituents.emplace_back(n2, 1.0); + toreset.push_back(n2); + } + } + } + } + } + for (int i : toreset) + unclustered_[i] = 0; +} + +std::unique_ptr l1tpf_calo::SingleCaloClusterer::fetchCells(bool unclusteredOnly, + float ptMin) const { + auto ret = std::make_unique(); + const EtGrid &src = (unclusteredOnly ? unclustered_ : rawet_); + for (unsigned int i = 0, ncells = grid_->size(); i < ncells; ++i) { + if (src[i] <= ptMin) + continue; + if ((unclusteredOnly == false) && (ptMin == 0)) { + assert(cellKey_[i] == int(ret->size())); + } + ret->emplace_back(src[i], grid_->eta(i), grid_->phi(i)); + ret->back().setHwEta(grid_->ieta(i)); + ret->back().setHwPhi(grid_->iphi(i)); + } + return ret; +} + +std::unique_ptr l1tpf_calo::SingleCaloClusterer::fetch(float ptMin) const { + auto ret = std::make_unique(); + for (const Cluster &cluster : clusters_) { + if (cluster.et > ptMin) { + ret->emplace_back(cluster.et, cluster.eta, cluster.phi); + } + } + return ret; +} + +std::unique_ptr l1tpf_calo::SingleCaloClusterer::fetch( + const edm::OrphanHandle &cells, float ptMin) const { + auto ret = std::make_unique(); + for (const Cluster &cluster : clusters_) { + if (cluster.et > ptMin) { + ret->emplace_back(cluster.et, cluster.eta, cluster.phi); + for (const auto &pair : cluster.constituents) { + edm::Ptr ref(cells, cellKey_[pair.first]); + ret->back().addConstituent(ref, pair.second); + } + } + } + return ret; +} + +l1tpf_calo::SimpleCaloLinkerBase::SimpleCaloLinkerBase(const edm::ParameterSet &pset, + const SingleCaloClusterer &ecal, + const SingleCaloClusterer &hcal) + : grid_(getGrid(pset.getParameter("grid"))), + ecal_(ecal), + hcal_(hcal), + clusterIndex_(*grid_), + clusters_(), + hoeCut_(pset.getParameter("hoeCut")), + minPhotonEt_(pset.getParameter("minPhotonEt")), + minHadronRawEt_(pset.getParameter("minHadronRawEt")), + minHadronEt_(pset.getParameter("minHadronEt")), + noEmInHGC_(pset.getParameter("noEmInHGC")) { + if (grid_ != &ecal.raw().grid()) + throw cms::Exception("LogicError", "Inconsistent grid between ecal and linker\n"); + if (grid_ != &hcal.raw().grid()) + throw cms::Exception("LogicError", "Inconsistent grid between hcal and linker\n"); +} + +l1tpf_calo::SimpleCaloLinkerBase::~SimpleCaloLinkerBase() {} + +std::unique_ptr l1tpf_calo::SimpleCaloLinkerBase::fetch() const { + edm::OrphanHandle ecal, hcal; + return fetch(ecal, hcal); +} + +std::unique_ptr l1tpf_calo::SimpleCaloLinkerBase::fetch( + const edm::OrphanHandle &ecal, + const edm::OrphanHandle &hcal) const { + bool setRefs = (ecal.isValid() && hcal.isValid()); + auto ret = std::make_unique(); + for (const CombinedCluster &cluster : clusters_) { + if (cluster.et > 0) { + bool photon = (cluster.hcal_et < hoeCut_ * cluster.ecal_et); + if (photon && noEmInHGC_) { + if (std::abs(cluster.eta) > 1.5 && std::abs(cluster.eta) < 3.0) { // 1.5-3 = eta range of HGCal + continue; + } + } + if (cluster.et > (photon ? minPhotonEt_ : minHadronEt_)) { + ret->emplace_back(cluster.et, + cluster.eta, + cluster.phi, + cluster.ecal_et > 0 ? std::max(cluster.et - cluster.ecal_et, 0.f) / cluster.ecal_et : -1, + photon); + if (setRefs) { + for (const auto &pair : cluster.constituents) { + assert(pair.first != 0); + if (pair.first > 0) { // 1+hcal index + ret->back().addConstituent(edm::Ptr(hcal, +pair.first - 1), pair.second); + } else { // -1-ecal index + ret->back().addConstituent(edm::Ptr(ecal, -pair.first + 1), pair.second); + } + } + } + } + } + } + return ret; +} + +l1tpf_calo::SimpleCaloLinker::SimpleCaloLinker(const edm::ParameterSet &pset, + const SingleCaloClusterer &ecal, + const SingleCaloClusterer &hcal) + : SimpleCaloLinkerBase(pset, ecal, hcal), ecalToHCal_(*grid_) {} + +l1tpf_calo::SimpleCaloLinker::~SimpleCaloLinker() {} + +void l1tpf_calo::SimpleCaloLinker::clear() { + clearBase(); + ecalToHCal_.clear(); +} + +void l1tpf_calo::SimpleCaloLinker::run() { + unsigned int i, ncells = grid_->size(); + + const EtGrid &hraw = hcal_.raw(); + const IndexGrid &ecals = ecal_.indexGrid(); + const IndexGrid &hcals = hcal_.indexGrid(); + + // for each ECal cluster, get the corresponding HCal cluster and the sum of the neighbour HCal clusters + ecalToHCal_.clear(); + for (i = 0; i < ncells; ++i) { + if (ecals[i] >= 0) { + if (hcals[i] >= 0) { + ecalToHCal_[i].ptLocalMax = hcal_.cluster(i).et; + } else { + float tot = 0; + for (int ineigh = 0; ineigh < 8; ++ineigh) { + tot += hcal_.cluster(grid_->neighbour(i, ineigh)).et; + } + ecalToHCal_[i].ptOverNeighLocalMaxSum = tot ? ecal_.cluster(i).et / tot : 0; + } + } + } + + clusterIndex_.fill(-1); + clusters_.clear(); + CombinedCluster cluster; + // promote HCal clusters to final clusters + for (i = 0; i < ncells; ++i) { + if (hcals[i] >= 0) { + const Cluster &hcal = hcal_.cluster(i); + cluster.clear(); + cluster.constituents.emplace_back(+i + 1, 1); + if (ecalToHCal_[i].ptLocalMax > 0) { + // direct linking is easy + const Cluster &ecal = ecal_.cluster(i); + if (ecal.et + hcal.et > minHadronRawEt_) { + cluster.ecal_et = ecal.et; + cluster.hcal_et = hcal.et; + cluster.et = cluster.ecal_et + cluster.hcal_et; + float wecal = cluster.ecal_et / cluster.et, whcal = 1.0 - wecal; + cluster.eta = ecal.eta * wecal + hcal.eta * whcal; + cluster.phi = ecal.phi * wecal + hcal.phi * whcal; + // wrap around phi + cluster.phi = reco::reduceRange(cluster.phi); + cluster.constituents.emplace_back(-i - 1, 1); + } + } else { + // sidewas linking is more annonying + float myet = hcal.et; + float etot = 0; + float avg_eta = 0; + float avg_phi = 0; + for (int ineigh = 0; ineigh < 8; ++ineigh) { + int ineighcell = grid_->neighbour(i, ineigh); + if (ineighcell == -1) + continue; // skip dummy cells + float fracet = myet * ecalToHCal_.neigh(i, ineigh).ptOverNeighLocalMaxSum; + if (fracet == 0) + continue; + etot += fracet; + avg_eta += fracet * (grid_->eta(ineighcell) - grid_->eta(i)); + avg_phi += fracet * deltaPhi(grid_->phi(ineighcell), grid_->phi(i)); + cluster.constituents.emplace_back(-i - 1, fracet / ecal_.cluster(ineighcell).et); + } + if (myet + etot > minHadronRawEt_) { + cluster.hcal_et = hcal.et; + cluster.ecal_et = etot; + cluster.et = myet + etot; + cluster.eta = hcal.eta + avg_eta / cluster.et; + cluster.phi = hcal.phi + avg_phi / cluster.et; + // wrap around phi + cluster.phi = reco::reduceRange(cluster.phi); + } + } + if (cluster.et > 0) { + clusterIndex_[i] = clusters_.size(); + clusters_.push_back(cluster); + } + } + } + + // promote Unlinked ECal clusters to final clusters + for (i = 0; i < ncells; ++i) { + if (ecals[i] >= 0 && ecalToHCal_[i].ptLocalMax == 0 && ecalToHCal_[i].ptOverNeighLocalMaxSum == 0) { + cluster.clear(); + const Cluster &ecal = ecal_.cluster(i); + cluster.ecal_et = ecal.et; + cluster.hcal_et = hraw[i]; + cluster.et = cluster.ecal_et + cluster.hcal_et; + cluster.eta = ecal.eta; + cluster.phi = ecal.phi; + cluster.constituents.emplace_back(-i - 1, 1); + clusterIndex_[i] = clusters_.size(); + clusters_.push_back(cluster); + } + } +} + +l1tpf_calo::FlatCaloLinker::FlatCaloLinker(const edm::ParameterSet &pset, + const SingleCaloClusterer &ecal, + const SingleCaloClusterer &hcal) + : SimpleCaloLinkerBase(pset, ecal, hcal), combClusterer_(pset) {} + +l1tpf_calo::FlatCaloLinker::~FlatCaloLinker() {} + +void l1tpf_calo::FlatCaloLinker::clear() { + clearBase(); + combClusterer_.clear(); +} + +void l1tpf_calo::FlatCaloLinker::run() { + combClusterer_.clear(); + + const EtGrid &hraw = hcal_.raw(); + const EtGrid &eraw = ecal_.raw(); + combClusterer_.raw() = eraw; + combClusterer_.raw() += hraw; + + combClusterer_.run(); + clusterIndex_ = combClusterer_.indexGrid(); + const std::vector &clustersSrc = combClusterer_.clusters(); + unsigned int nclust = clustersSrc.size(); + clusters_.resize(nclust); + for (unsigned int ic = 0; ic < nclust; ++ic) { + const Cluster &src = clustersSrc[ic]; + CombinedCluster &dst = clusters_[ic]; + dst.et = src.et; + dst.eta = src.eta; + dst.phi = src.phi; + dst.ecal_et = 0; + dst.hcal_et = 0; + for (const auto &pair : src.constituents) { + if (eraw[pair.first]) { + dst.ecal_et += pair.second * eraw[pair.first]; + dst.constituents.emplace_back(-pair.first - 1, pair.second); + } + if (hraw[pair.first]) { + dst.hcal_et += pair.second * hraw[pair.first]; + dst.constituents.emplace_back(+pair.first + 1, pair.second); + } + } + } +} + +std::unique_ptr l1tpf_calo::makeCaloLinker(const edm::ParameterSet &pset, + const SingleCaloClusterer &ecal, + const SingleCaloClusterer &hcal) { + const std::string &algo = pset.getParameter("algo"); + if (algo == "simple") { + return std::make_unique(pset, ecal, hcal); + } else if (algo == "flat") { + return std::make_unique(pset, ecal, hcal); + } else { + throw cms::Exception("Configuration") << "Unsupported linker algo '" << algo << "'\n"; + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/DiscretePFInputsIO.cc b/L1Trigger/Phase2L1ParticleFlow/src/DiscretePFInputsIO.cc new file mode 100644 index 0000000000000..67cc12e16a383 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/DiscretePFInputsIO.cc @@ -0,0 +1 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h" diff --git a/L1Trigger/Phase2L1ParticleFlow/src/HGC3DClusterEgID.cc b/L1Trigger/Phase2L1ParticleFlow/src/HGC3DClusterEgID.cc new file mode 100644 index 0000000000000..6cf9b028ad7f6 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/HGC3DClusterEgID.cc @@ -0,0 +1,45 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/HGC3DClusterEgID.h" +#include "CommonTools/MVAUtils/interface/TMVAZipReader.h" + +l1tpf::HGC3DClusterEgID::HGC3DClusterEgID(const edm::ParameterSet &pset) + : isPUFilter_(pset.getParameter("isPUFilter")), + preselection_(pset.getParameter("preselection")), + method_(pset.getParameter("method")), + weightsFile_(pset.getParameter("weightsFile")), + reader_(new TMVA::Reader()), + wp_(pset.getParameter("wp")) { + // first create all the variables + for (const auto &psvar : pset.getParameter>("variables")) { + variables_.emplace_back(psvar.getParameter("name"), psvar.getParameter("value")); + } +} + +void l1tpf::HGC3DClusterEgID::prepareTMVA() { + // Declare the variables + for (auto &var : variables_) + var.declare(*reader_); + // then read the weights + if (weightsFile_[0] != '/' && weightsFile_[0] != '.') { + weightsFile_ = edm::FileInPath(weightsFile_).fullPath(); + } + reco::details::loadTMVAWeights(&*reader_, method_, weightsFile_); +} + +float l1tpf::HGC3DClusterEgID::passID(l1t::HGCalMulticluster c, l1t::PFCluster &cpf) { + if (preselection_(c)) { + for (auto &var : variables_) + var.fill(c); + float mvaOut = reader_->EvaluateMVA(method_); + if (isPUFilter_) + cpf.setEgVsPUMVAOut(mvaOut); + else + cpf.setEgVsPionMVAOut(mvaOut); + return (mvaOut > wp_(c) ? 1 : 0); + } else { + if (isPUFilter_) + cpf.setEgVsPUMVAOut(-100.0); + else + cpf.setEgVsPionMVAOut(-100.0); + return 0; + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/L1TPFUtils.cc b/L1Trigger/Phase2L1ParticleFlow/src/L1TPFUtils.cc new file mode 100644 index 0000000000000..d0aec72574472 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/L1TPFUtils.cc @@ -0,0 +1,17 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/L1TPFUtils.h" + +#include "CommonTools/BaseParticlePropagator/interface/BaseParticlePropagator.h" +#include "CommonTools/BaseParticlePropagator/interface/RawParticle.h" +#include "DataFormats/ParticleFlowReco/interface/PFCluster.h" + +std::pair l1tpf::propagateToCalo(const math::XYZTLorentzVector& iMom, + const math::XYZTLorentzVector& iVtx, + double iCharge, + double iBField) { + BaseParticlePropagator prop = BaseParticlePropagator(RawParticle(iMom, iVtx, iCharge), 0., 0., iBField); + prop.propagateToEcalEntrance(false); + double ecalShowerDepth = reco::PFCluster::getDepthCorrection(prop.particle().momentum().E(), false, false); + math::XYZVector point = math::XYZVector(prop.particle().vertex()) + + math::XYZTLorentzVector(prop.particle().momentum()).Vect().Unit() * ecalShowerDepth; + return std::make_pair(point.eta(), point.phi()); +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/LinearizedPuppiAlgo.cc b/L1Trigger/Phase2L1ParticleFlow/src/LinearizedPuppiAlgo.cc new file mode 100644 index 0000000000000..463a114b53f6e --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/LinearizedPuppiAlgo.cc @@ -0,0 +1,142 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/LinearizedPuppiAlgo.h" +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "DataFormats/Math/interface/deltaR.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h" + +#include "Math/ProbFunc.h" + +namespace { + std::vector vd2vf(const std::vector &vd) { + std::vector ret; + ret.insert(ret.end(), vd.begin(), vd.end()); + return ret; + } +} // namespace + +using namespace l1tpf_impl; + +LinearizedPuppiAlgo::LinearizedPuppiAlgo(const edm::ParameterSet &iConfig) + : PuppiAlgo(iConfig), + puppiPriors_(vd2vf(iConfig.getParameter>("puppiPriors"))), + puppiPriorsPhotons_(vd2vf(iConfig.getParameter>("puppiPriorsPhotons"))), + puppiPtSlopes_(vd2vf(iConfig.getParameter>("puppiPtSlopes"))), + puppiPtSlopesPhotons_(vd2vf(iConfig.getParameter>("puppiPtSlopesPhotons"))), + puppiPtZeros_(vd2vf(iConfig.getParameter>("puppiPtZeros"))), + puppiPtZerosPhotons_(vd2vf(iConfig.getParameter>("puppiPtZerosPhotons"))), + puppiAlphaSlopes_(vd2vf(iConfig.getParameter>("puppiAlphaSlopes"))), + puppiAlphaSlopesPhotons_(vd2vf(iConfig.getParameter>("puppiAlphaSlopesPhotons"))), + puppiAlphaZeros_(vd2vf(iConfig.getParameter>("puppiAlphaZeros"))), + puppiAlphaZerosPhotons_(vd2vf(iConfig.getParameter>("puppiAlphaZerosPhotons"))), + puppiAlphaCrops_(vd2vf(iConfig.getParameter>("puppiAlphaCrops"))), + puppiAlphaCropsPhotons_(vd2vf(iConfig.getParameter>("puppiAlphaCropsPhotons"))) { + if (puppiPriors_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiPriors\n"); + if (puppiPtSlopes_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiPtSlopes\n"); + if (puppiPtZeros_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiPtZeros\n"); + if (puppiAlphaSlopes_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiAlphaSlopes\n"); + if (puppiAlphaZeros_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiAlphaZeros\n"); + if (puppiAlphaCrops_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiAlphaCrops\n"); + if (puppiPriorsPhotons_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiPriorsPhotons\n"); + if (puppiPtSlopesPhotons_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiPtSlopesPhotons\n"); + if (puppiPtZerosPhotons_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiPtZerosPhotons\n"); + if (puppiAlphaSlopesPhotons_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiAlphaSlopesPhotons\n"); + if (puppiAlphaZerosPhotons_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiAlphaZerosPhotons\n"); + if (puppiAlphaCropsPhotons_.size() != puppiEtaCuts_.size()) + throw cms::Exception("Configuration", "Mismatched lenght for puppiAlphaCropsPhotons\n"); +} + +LinearizedPuppiAlgo::~LinearizedPuppiAlgo() {} + +const std::vector &LinearizedPuppiAlgo::puGlobalNames() const { + static const std::vector names_{}; + return names_; +} +void LinearizedPuppiAlgo::doPUGlobals(const std::vector &rs, float npu, std::vector &globals) const { + globals.clear(); +} +void LinearizedPuppiAlgo::runNeutralsPU(Region &r, float npu, const std::vector &globals) const { + std::vector alphaC, alphaF; + PuppiAlgo::computePuppiAlphas(r, alphaC, alphaF); + computePuppiWeights(r, npu, alphaC, alphaF); + PuppiAlgo::fillPuppi(r); +} + +void LinearizedPuppiAlgo::computePuppiWeights(Region &r, + float npu, + const std::vector &alphaC, + const std::vector &alphaF) const { + if (debug_ && npu > 0) + dbgPrintf("LinPup\t npu estimate %7.2f --> log(npu/200) = %+6.2f \n", npu, std::log(npu / 200.f)); + for (unsigned int ip = 0, np = r.pf.size(); ip < np; ++ip) { + PFParticle &p = r.pf[ip]; + // charged + if (p.hwId == l1t::PFCandidate::ChargedHadron || p.hwId == l1t::PFCandidate::Electron || + p.hwId == l1t::PFCandidate::Muon) { + p.setPuppiW(p.chargedPV || p.hwId == l1t::PFCandidate::Muon ? 1.0 : 0); + if (debug_ == 2) + dbgPrintf( + "LinPup\t charged id %1d pt %7.2f eta %+5.2f phi %+5.2f fromPV %1d " + " --> puppi weight %.3f puppi pt %7.2f \n", + p.hwId, + p.floatPt(), + p.floatEta(), + p.floatPhi(), + p.chargedPV, + p.floatPuppiW(), + p.floatPt() * p.floatPuppiW()); + continue; + } + // neutral + float absEta = r.relativeCoordinates ? r.globalAbsEta(p.floatEta()) : std::abs(p.floatEta()); + bool central = absEta < etaCharged_; // FIXME could make a better integer implementation + bool photon = (p.hwId == l1t::PFCandidate::Photon); + // get alpha + float alpha = central ? alphaC[ip] : alphaF[ip]; + alpha = (alpha > 0 ? std::log(alpha) : 0); + // get eta bin + unsigned int ietaBin = 0, lastBin = puppiEtaCuts_.size() - 1; + while (ietaBin < lastBin && absEta > puppiEtaCuts_[ietaBin]) { + ietaBin++; + } + float alphaZero = (photon ? puppiAlphaZerosPhotons_ : puppiAlphaZeros_)[ietaBin]; + float alphaSlope = (photon ? puppiAlphaSlopesPhotons_ : puppiAlphaSlopes_)[ietaBin]; + float alphaCrop = (photon ? puppiAlphaCropsPhotons_ : puppiAlphaCrops_)[ietaBin]; + float x2a = std::clamp(alphaSlope * (alpha - alphaZero), -alphaCrop, alphaCrop); + // weight by pT + float ptZero = (photon ? puppiPtZerosPhotons_ : puppiPtZeros_)[ietaBin]; + float ptSlope = (photon ? puppiPtSlopesPhotons_ : puppiPtSlopes_)[ietaBin]; + float x2pt = ptSlope * (p.floatPt() - ptZero); + // weight by prior + float prior = (photon ? puppiPriorsPhotons_ : puppiPriors_)[ietaBin]; + float x2prior = (npu > 0 ? std::log(npu / 200.f) : 0) + prior; + // total + float x2 = x2a + x2pt - x2prior; + p.setPuppiW(1.0 / (1.0 + std::exp(-x2))); + if (debug_ == 1 || debug_ == 2 || debug_ == int(10 + ietaBin)) + dbgPrintf( + "LinPup\t neutral id %1d pt %7.2f eta %+5.2f phi %+5.2f alpha %+6.2f x2a %+5.2f x2pt %+6.2f x2prior " + "%+6.2f --> x2 %+6.2f --> puppi weight %.3f puppi pt %7.2f \n", + p.hwId, + p.floatPt(), + p.floatEta(), + p.floatPhi(), + alpha, + x2a, + x2pt, + -x2prior, + x2, + p.floatPuppiW(), + p.floatPt() * p.floatPuppiW()); + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/PFAlgo2HGC.cc b/L1Trigger/Phase2L1ParticleFlow/src/PFAlgo2HGC.cc new file mode 100644 index 0000000000000..e5984288e9c4a --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/PFAlgo2HGC.cc @@ -0,0 +1,650 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo2HGC.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h" + +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" + +#include "FWCore/Utilities/interface/Exception.h" + +#include "DataFormats/Math/interface/deltaR.h" + +namespace { + template + float floatDR(const T1 &t1, const T2 &t2) { + return deltaR(t1.floatEta(), t1.floatPhi(), t2.floatEta(), t2.floatPhi()); + } +} // namespace + +using namespace l1tpf_impl; + +PFAlgo2HGC::PFAlgo2HGC(const edm::ParameterSet &iConfig) : PFAlgoBase(iConfig) { + debug_ = iConfig.getUntrackedParameter("debugPFAlgo2HGC", iConfig.getUntrackedParameter("debug", 0)); + edm::ParameterSet linkcfg = iConfig.getParameter("linking"); + drMatchMu_ = linkcfg.getParameter("trackMuDR"); + + std::string muMatchMode = linkcfg.getParameter("trackMuMatch"); + if (muMatchMode == "boxBestByPtRatio") + muMatchMode_ = MuMatchMode::BoxBestByPtRatio; + else if (muMatchMode == "drBestByPtRatio") + muMatchMode_ = MuMatchMode::DrBestByPtRatio; + else if (muMatchMode == "drBestByPtDiff") + muMatchMode_ = MuMatchMode::DrBestByPtDiff; + else + throw cms::Exception("Configuration", "bad value for trackMuMatch configurable"); + + std::string tkCaloLinkMetric = linkcfg.getParameter("trackCaloLinkMetric"); + if (tkCaloLinkMetric == "bestByDR") + tkCaloLinkMetric_ = TkCaloLinkMetric::BestByDR; + else if (tkCaloLinkMetric == "bestByDRPt") + tkCaloLinkMetric_ = TkCaloLinkMetric::BestByDRPt; + else if (tkCaloLinkMetric == "bestByDR2Pt2") + tkCaloLinkMetric_ = TkCaloLinkMetric::BestByDR2Pt2; + else + throw cms::Exception("Configuration", "bad value for tkCaloLinkMetric configurable"); + + drMatch_ = linkcfg.getParameter("trackCaloDR"); + ptMatchLow_ = linkcfg.getParameter("trackCaloNSigmaLow"); + ptMatchHigh_ = linkcfg.getParameter("trackCaloNSigmaHigh"); + useTrackCaloSigma_ = linkcfg.getParameter("useTrackCaloSigma"); + maxInvisiblePt_ = linkcfg.getParameter("maxInvisiblePt"); + + caloReLinkStep_ = linkcfg.getParameter("caloReLink"); + caloReLinkDr_ = linkcfg.getParameter("caloReLinkDR"); + caloReLinkThreshold_ = linkcfg.getParameter("caloReLinkThreshold"); + rescaleTracks_ = linkcfg.getParameter("rescaleTracks"); + caloTrkWeightedAverage_ = linkcfg.getParameter("useCaloTrkWeightedAverage"); + sumTkCaloErr2_ = linkcfg.getParameter("sumTkCaloErr2"); + ecalPriority_ = linkcfg.getParameter("ecalPriority"); + tightTrackMinStubs_ = linkcfg.getParameter("tightTrackMinStubs"); + tightTrackMaxChi2_ = linkcfg.getParameter("tightTrackMaxChi2"); + tightTrackMaxInvisiblePt_ = linkcfg.getParameter("tightTrackMaxInvisiblePt"); +} + +void PFAlgo2HGC::runPF(Region &r) const { + initRegion(r); + + /// ------------- first step (can all go in parallel) ---------------- + + if (debug_) { + dbgPrintf( + "PFAlgo2HGC\nPFAlgo2HGC region eta [ %+5.2f , %+5.2f ], phi [ %+5.2f , %+5.2f ], fiducial eta [ %+5.2f , " + "%+5.2f ], phi [ %+5.2f , %+5.2f ]\n", + r.etaMin - r.etaExtra, + r.etaMax + r.etaExtra, + r.phiCenter - r.phiHalfWidth - r.phiExtra, + r.phiCenter + r.phiHalfWidth + r.phiExtra, + r.etaMin, + r.etaMax, + r.phiCenter - r.phiHalfWidth, + r.phiCenter + r.phiHalfWidth); + dbgPrintf( + "PFAlgo2HGC \t N(track) %3lu N(calo) %3lu N(mu) %3lu\n", r.track.size(), r.calo.size(), r.muon.size()); + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + dbgPrintf( + "PFAlgo2HGC \t track %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi " + "%+5.2f fid %1d calo ptErr %7.2f stubs %2d chi2 %7.1f\n", + itk, + tk.floatPt(), + tk.floatPtErr(), + tk.floatVtxEta(), + tk.floatVtxPhi(), + tk.floatEta(), + tk.floatPhi(), + int(r.fiducialLocal(tk.floatEta(), tk.floatPhi())), + tk.floatCaloPtErr(), + int(tk.hwStubs), + tk.hwChi2 * 0.1f); + } + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + dbgPrintf( + "PFAlgo2HGC \t calo %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi " + "%+5.2f fid %1d calo ptErr %7.2f em pt %7.2f isEM %1d \n", + ic, + calo.floatPt(), + calo.floatPtErr(), + calo.floatEta(), + calo.floatPhi(), + calo.floatEta(), + calo.floatPhi(), + int(r.fiducialLocal(calo.floatEta(), calo.floatPhi())), + calo.floatPtErr(), + calo.floatEmPt(), + calo.isEM); + } + for (int im = 0, nm = r.muon.size(); im < nm; ++im) { + auto &mu = r.muon[im]; + dbgPrintf( + "PFAlgo2HGC \t muon %3d: pt %7.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi " + "%+5.2f fid %1d\n", + im, + mu.floatPt(), + mu.floatEta(), + mu.floatPhi(), + mu.floatEta(), + mu.floatPhi(), + int(r.fiducialLocal(mu.floatEta(), mu.floatPhi()))); + } + } + + std::vector tk2mu(r.track.size(), -1), mu2tk(r.muon.size(), -1); + link_tk2mu(r, tk2mu, mu2tk); + + // track to calo matching (first iteration, with a lower bound on the calo pt; there may be another one later) + std::vector tk2calo(r.track.size(), -1); + link_tk2calo(r, tk2calo); + + /// ------------- next step (needs the previous) ---------------- + // for each calo, compute the sum of the track pt + std::vector calo2ntk(r.calo.size(), 0); + std::vector calo2sumtkpt(r.calo.size(), 0); + std::vector calo2sumtkpterr(r.calo.size(), 0); + sum_tk2calo(r, tk2calo, calo2ntk, calo2sumtkpt, calo2sumtkpterr); + + // in the meantime, promote unlinked low pt tracks to hadrons + unlinkedtk_algo(r, tk2calo); + + /// ------------- next step (needs the previous) ---------------- + /// OPTIONAL STEP: try to recover split hadron showers (v1.0): + // off by default, as it seems to not do much in jets even if it helps remove tails in single-pion events + if (caloReLinkStep_) + calo_relink(r, calo2ntk, calo2sumtkpt, calo2sumtkpterr); + + /// ------------- next step (needs the previous) ---------------- + // process matched calo clusters, compare energy to sum track pt + std::vector calo2alpha(r.calo.size(), 1); + linkedcalo_algo(r, calo2ntk, calo2sumtkpt, calo2sumtkpterr, calo2alpha); + + /// ------------- next step (needs the previous) ---------------- + /// process matched tracks, if necessary rescale or average + linkedtk_algo(r, tk2calo, calo2ntk, calo2alpha); + // process unmatched calo clusters + unlinkedcalo_algo(r); + // finally do muons + save_muons(r, tk2mu); +} + +void PFAlgo2HGC::link_tk2mu(Region &r, std::vector &tk2mu, std::vector &mu2tk) const { + // do a rectangular match for the moment; make a box of the same are as a 0.2 cone + int intDrMuonMatchBox = std::ceil(drMatchMu_ * CaloCluster::ETAPHI_SCALE * std::sqrt(M_PI / 4)); + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + tk2mu[itk] = false; + } + for (int imu = 0, nmu = r.muon.size(); imu < nmu; ++imu) { + const auto &mu = r.muon[imu]; + if (debug_) + dbgPrintf("PFAlgo2HGC \t muon %3d (pt %7.2f, eta %+5.2f, phi %+5.2f) \n", + imu, + mu.floatPt(), + mu.floatEta(), + mu.floatPhi()); + float minDistance = 9e9; + switch (muMatchMode_) { + case MuMatchMode::BoxBestByPtRatio: + minDistance = 4.; + break; + case MuMatchMode::DrBestByPtRatio: + minDistance = 4.; + break; + case MuMatchMode::DrBestByPtDiff: + minDistance = 0.5 * mu.floatPt(); + break; + } + int imatch = -1; + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + int deta = std::abs(mu.hwEta - tk.hwEta); + int dphi = std::abs((mu.hwPhi - tk.hwPhi) % CaloCluster::PHI_WRAP); + float dr = floatDR(mu, tk); + float dpt = std::abs(mu.floatPt() - tk.floatPt()); + float dptr = (mu.hwPt > tk.hwPt ? mu.floatPt() / tk.floatPt() : tk.floatPt() / mu.floatPt()); + bool ok = false; + float distance = 9e9; + switch (muMatchMode_) { + case MuMatchMode::BoxBestByPtRatio: + ok = (deta < intDrMuonMatchBox) && (dphi < intDrMuonMatchBox); + distance = dptr; + break; + case MuMatchMode::DrBestByPtRatio: + ok = (dr < drMatchMu_); + distance = dptr; + break; + case MuMatchMode::DrBestByPtDiff: + ok = (dr < drMatchMu_); + distance = dpt; + break; + } + if (debug_ && dr < 0.4) { + dbgPrintf( + "PFAlgo2HGC \t\t possible match with track %3d (pt %7.2f, caloeta %+5.2f, calophi %+5.2f, dr %.2f, eta " + "%+5.2f, phi %+5.2f, dr %.2f): angular %1d, distance %.3f (vs %.3f)\n", + itk, + tk.floatPt(), + tk.floatEta(), + tk.floatPhi(), + dr, + tk.floatVtxEta(), + tk.floatVtxPhi(), + deltaR(mu.floatEta(), mu.floatPhi(), tk.floatVtxEta(), tk.floatVtxPhi()), + (ok ? 1 : 0), + distance, + minDistance); + } + if (!ok) + continue; + // FIXME for the moment, we do the floating point matching in pt + if (distance < minDistance) { + minDistance = distance; + imatch = itk; + } + } + if (debug_ && imatch > -1) + dbgPrintf("PFAlgo2HGC \t muon %3d (pt %7.2f) linked to track %3d (pt %7.2f)\n", + imu, + mu.floatPt(), + imatch, + r.track[imatch].floatPt()); + if (debug_ && imatch == -1) + dbgPrintf("PFAlgo2HGC \t muon %3d (pt %7.2f) not linked to any track\n", imu, mu.floatPt()); + mu2tk[imu] = imatch; + if (imatch > -1) { + tk2mu[imatch] = imu; + r.track[imatch].muonLink = true; + } + } +} + +void PFAlgo2HGC::link_tk2calo(Region &r, std::vector &tk2calo) const { + // track to calo matching (first iteration, with a lower bound on the calo pt; there may be another one later) + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + if (tk.muonLink || tk.used) + continue; // not necessary but just a waste of CPU otherwise + float drbest = drMatch_, dptscale = 0; + switch (tkCaloLinkMetric_) { + case TkCaloLinkMetric::BestByDR: + drbest = drMatch_; + break; + case TkCaloLinkMetric::BestByDRPt: + drbest = 1.0; + dptscale = drMatch_ / tk.floatCaloPtErr(); + break; + case TkCaloLinkMetric::BestByDR2Pt2: + drbest = 1.0; + dptscale = drMatch_ / tk.floatCaloPtErr(); + break; + } + float minCaloPt = tk.floatPt() - ptMatchLow_ * tk.floatCaloPtErr(); + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t track %3d (pt %7.2f) to be matched to calo, min pT %7.2f\n", itk, tk.floatPt(), minCaloPt); + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo.used || calo.floatPt() <= minCaloPt) + continue; + float dr = floatDR(tk, calo), dq; + switch (tkCaloLinkMetric_) { + case TkCaloLinkMetric::BestByDR: + if (dr < drbest) { + tk2calo[itk] = ic; + drbest = dr; + } + break; + case TkCaloLinkMetric::BestByDRPt: + dq = dr + std::max(tk.floatPt() - calo.floatPt(), 0.) * dptscale; + if (debug_ > 2 && dr < 0.3) + dbgPrintf("PFAlgo2HGC \t\t\t track %3d (pt %7.2f) vs calo %3d (pt %7.2f): dr %.3f, dq %.3f\n", + itk, + tk.floatPt(), + ic, + calo.floatPt(), + dr, + dq); + if (dr < drMatch_ && dq < drbest) { + tk2calo[itk] = ic; + drbest = dq; + } + break; + case TkCaloLinkMetric::BestByDR2Pt2: + dq = hypot(dr, std::max(tk.floatPt() - calo.floatPt(), 0.) * dptscale); + if (debug_ > 2 && dr < 0.3) + dbgPrintf("PFAlgo2HGC \t\t\t track %3d (pt %7.2f) vs calo %3d (pt %7.2f): dr %.3f, dq %.3f\n", + itk, + tk.floatPt(), + ic, + calo.floatPt(), + dr, + dq); + if (dr < drMatch_ && dq < drbest) { + tk2calo[itk] = ic; + drbest = dq; + } + break; + } + } + if (debug_ && tk2calo[itk] != -1) + dbgPrintf("PFAlgo2HGC \t track %3d (pt %7.2f) matches to calo %3d (pt %7.2f) with dist %.3f (dr %.3f)\n", + itk, + tk.floatPt(), + tk2calo[itk], + r.calo[tk2calo[itk]].floatPt(), + drbest, + floatDR(tk, r.calo[tk2calo[itk]])); + // now we re-do this for debugging sake, it may be done for real later + if (debug_ && tk2calo[itk] == -1) { + int ibest = -1; + drbest = 0.3; + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo.used) + continue; + float dr = floatDR(tk, calo); + if (dr < drbest) { + ibest = ic; + drbest = dr; + } + } + if (ibest != -1) + dbgPrintf( + "PFAlgo2HGC \t track %3d (pt %7.2f) would match to calo %3d (pt %7.2f) with dr %.3f if the pt min and dr " + "requirement had been relaxed\n", + itk, + tk.floatPt(), + ibest, + r.calo[ibest].floatPt(), + drbest); + } + } +} + +void PFAlgo2HGC::sum_tk2calo(Region &r, + const std::vector &tk2calo, + std::vector &calo2ntk, + std::vector &calo2sumtkpt, + std::vector &calo2sumtkpterr) const { + // for each calo, compute the sum of the track pt + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + const auto &calo = r.calo[ic]; + if (r.globalAbsEta(calo.floatEta()) > 2.5) + continue; + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + if (tk2calo[itk] == ic) { + const auto &tk = r.track[itk]; + if (tk.muonLink || tk.used) + continue; + calo2ntk[ic]++; + calo2sumtkpt[ic] += tk.floatPt(); + calo2sumtkpterr[ic] += std::pow(tk.floatCaloPtErr(), sumTkCaloErr2_ ? 2 : 1); + } + } + if (sumTkCaloErr2_ && calo2sumtkpterr[ic] > 0) + calo2sumtkpterr[ic] = std::sqrt(calo2sumtkpterr[ic]); + } +} + +void PFAlgo2HGC::unlinkedtk_algo(Region &r, const std::vector &tk2calo) const { + // in the meantime, promote unlinked low pt tracks to hadrons + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + auto &tk = r.track[itk]; + if (tk2calo[itk] != -1 || tk.muonLink || tk.used) + continue; + float maxPt = (tk.hwStubs >= tightTrackMinStubs_ && tk.hwChi2 < 10. * tightTrackMaxChi2_) + ? tightTrackMaxInvisiblePt_ + : maxInvisiblePt_; + if (tk.floatPt() < maxPt) { + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t track %3d (pt %7.2f) not matched to calo, kept as charged hadron\n", itk, tk.floatPt()); + auto &p = addTrackToPF(r, tk); + p.hwStatus = GoodTK_NoCalo; + tk.used = true; + } else { + if (debug_) + dbgPrintf("PFAlgo2HGC \t track %3d (pt %7.2f) not matched to calo, dropped\n", itk, tk.floatPt()); + } + } +} + +void PFAlgo2HGC::calo_relink(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr) const { + /// OPTIONAL STEP: try to recover split hadron showers (v1.0): + // take hadrons that are not track matched, close by a hadron which has an excess of track pt vs calo pt + // add this pt to the calo pt of the other cluster + // off by default, as it seems to not do much in jets even if it helps remove tails in single-pion events + std::vector addtopt(r.calo.size(), 0); + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo2ntk[ic] != 0 || calo.used || r.globalAbsEta(calo.floatEta()) > 2.5) + continue; + int i2best = -1; + float drbest = caloReLinkDr_; + for (int ic2 = 0; ic2 < nc; ++ic2) { + const auto &calo2 = r.calo[ic2]; + if (calo2ntk[ic2] == 0 || calo2.used || r.globalAbsEta(calo2.floatEta()) > 2.5) + continue; + float dr = floatDR(calo, calo2); + //// uncomment below for more verbose debugging + //if (debug_ && dr < 0.5) dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f) with no tracks is at dr %.3f from calo %3d with pt %7.2f (sum tk pt %7.2f), track excess %7.2f +- %7.2f\n", ic, calo.floatPt(), dr, ic2, calo2.floatPt(), calo2sumtkpt[ic2], calo2sumtkpt[ic2] - calo2.floatPt(), useTrackCaloSigma_ ? calo2sumtkpterr[ic2] : calo2.floatPtErr()); + if (dr < drbest) { + float ptdiff = + calo2sumtkpt[ic2] - calo2.floatPt() + (useTrackCaloSigma_ ? calo2sumtkpterr[ic2] : calo2.floatPtErr()); + if (ptdiff >= caloReLinkThreshold_ * calo.floatPt()) { + i2best = ic2; + drbest = dr; + } + } + } + if (i2best != -1) { + const auto &calo2 = r.calo[i2best]; + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t calo %3d (pt %7.2f) with no tracks matched within dr %.3f with calo %3d with pt %7.2f (sum " + "tk pt %7.2f), track excess %7.2f +- %7.2f\n", + ic, + calo.floatPt(), + drbest, + i2best, + calo2.floatPt(), + calo2sumtkpt[i2best], + calo2sumtkpt[i2best] - calo2.floatPt(), + useTrackCaloSigma_ ? calo2sumtkpterr[i2best] : calo2.floatPtErr()); + calo.used = true; + addtopt[i2best] += calo.floatPt(); + } + } + // we do this at the end, so that the above loop is parallelizable + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + if (addtopt[ic]) { + auto &calo = r.calo[ic]; + if (debug_) + dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f, sum tk pt %7.2f) is increased to pt %7.2f after merging\n", + ic, + calo.floatPt(), + calo2sumtkpt[ic], + calo.floatPt() + addtopt[ic]); + calo.setFloatPt(calo.floatPt() + addtopt[ic]); + } + } +} + +void PFAlgo2HGC::linkedcalo_algo(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr, + std::vector &calo2alpha) const { + /// ------------- next step (needs the previous) ---------------- + // process matched calo clusters, compare energy to sum track pt + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo2ntk[ic] == 0 || calo.used) + continue; + float ptdiff = calo.floatPt() - calo2sumtkpt[ic]; + float pterr = useTrackCaloSigma_ ? calo2sumtkpterr[ic] : calo.floatPtErr(); + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t calo %3d (pt %7.2f +- %7.2f, empt %7.2f) has %2d tracks (sumpt %7.2f, sumpterr %7.2f), ptdif " + "%7.2f +- %7.2f\n", + ic, + calo.floatPt(), + calo.floatPtErr(), + calo.floatEmPt(), + calo2ntk[ic], + calo2sumtkpt[ic], + calo2sumtkpterr[ic], + ptdiff, + pterr); + if (ptdiff > +ptMatchHigh_ * pterr) { + if (ecalPriority_) { + if (calo.floatEmPt() > 1) { + float emptdiff = std::min(ptdiff, calo.floatEmPt()); + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t calo %3d (pt %7.2f, empt %7.2f) ---> make photon with pt %7.2f, reduce ptdiff to " + "%7.2f +- %7.2f\n", + ic, + calo.floatPt(), + calo.floatEmPt(), + emptdiff, + ptdiff - emptdiff, + pterr); + auto &p = addCaloToPF(r, calo); + p.setFloatPt(emptdiff); + p.hwId = l1t::PFCandidate::Photon; + ptdiff -= emptdiff; + } + if (ptdiff > 2) { + if (debug_) + dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f, empt %7.2f) ---> make also neutral hadron with pt %7.2f\n", + ic, + calo.floatPt(), + calo.floatEmPt(), + ptdiff); + auto &p = addCaloToPF(r, calo); + p.setFloatPt(ptdiff); + p.hwId = l1t::PFCandidate::NeutralHadron; + } + } else { + if (debug_) + dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f) ---> promoted to neutral with pt %7.2f\n", + ic, + calo.floatPt(), + ptdiff); + auto &p = addCaloToPF(r, calo); + p.setFloatPt(ptdiff); + calo.hwFlags = 0; + } + } else if (ptdiff > -ptMatchLow_ * pterr) { + // nothing to do (weighted average happens when we process the tracks) + calo.hwFlags = 1; + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t calo %3d (pt %7.2f) ---> to be deleted, will use tracks instead\n", ic, calo.floatPt()); + } else { + // tracks overshoot, rescale to tracks to calo + calo2alpha[ic] = rescaleTracks_ ? calo.floatPt() / calo2sumtkpt[ic] : 1.0; + calo.hwFlags = 2; + if (debug_ && rescaleTracks_) + dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f) ---> tracks overshoot and will be scaled down by %.4f\n", + ic, + calo.floatPt(), + calo2alpha[ic]); + if (debug_ && !rescaleTracks_) + dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f) ---> tracks overshoot by %.4f\n", + ic, + calo.floatPt(), + calo2sumtkpt[ic] / calo.floatPt()); + } + calo.used = true; + } +} + +void PFAlgo2HGC::linkedtk_algo(Region &r, + const std::vector &tk2calo, + const std::vector &calo2ntk, + const std::vector &calo2alpha) const { + // process matched tracks, if necessary rescale or average + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + auto &tk = r.track[itk]; + if (tk2calo[itk] == -1 || tk.muonLink || tk.used) + continue; + auto &p = addTrackToPF(r, tk); + tk.used = true; + const auto &calo = r.calo[tk2calo[itk]]; + if (calo.isEM) + p.hwId = l1t::PFCandidate::Electron; + p.cluster.src = calo.src; + if (calo.hwFlags == 1) { + // can do weighted average if there's just one track + if (calo2ntk[tk2calo[itk]] == 1 && caloTrkWeightedAverage_) { + p.hwStatus = GoodTK_Calo_TkPt; + float ptavg = tk.floatPt(); + if (tk.floatPtErr() > 0) { + float wcalo = 1.0 / std::pow(tk.floatCaloPtErr(), 2); + float wtk = 1.0 / std::pow(tk.floatPtErr(), 2); + ptavg = (calo.floatPt() * wcalo + tk.floatPt() * wtk) / (wcalo + wtk); + p.hwStatus = GoodTK_Calo_TkCaloPt; + } + p.setFloatPt(ptavg); + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t track %3d (pt %7.2f +- %7.2f) combined with calo %3d (pt %7.2f +- %7.2f (from tk) " + "yielding candidate of pt %7.2f\n", + itk, + tk.floatPt(), + tk.floatPtErr(), + tk2calo[itk], + calo.floatPt(), + tk.floatCaloPtErr(), + ptavg); + } else { + p.hwStatus = GoodTK_Calo_TkPt; + if (debug_) + dbgPrintf("PFAlgo2HGC \t track %3d (pt %7.2f) linked to calo %3d promoted to %s\n", + itk, + tk.floatPt(), + tk2calo[itk], + (p.hwId == l1t::PFCandidate::Electron ? "electron" : "charged hadron")); + } + } else if (calo.hwFlags == 2) { + // must rescale + p.setFloatPt(tk.floatPt() * calo2alpha[tk2calo[itk]]); + p.hwStatus = GoodTk_Calo_CaloPt; + if (debug_) + dbgPrintf( + "PFAlgo2HGC \t track %3d (pt %7.2f, stubs %2d chi2 %7.1f) linked to calo %3d promoted to %s with pt %7.2f " + "after maybe rescaling\n", + itk, + tk.floatPt(), + int(tk.hwStubs), + tk.hwChi2 * 0.1f, + tk2calo[itk], + (p.hwId == l1t::PFCandidate::Electron ? "electron" : "charged hadron"), + p.floatPt()); + } + } +} + +void PFAlgo2HGC::unlinkedcalo_algo(Region &r) const { + // process unmatched calo clusters + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + if (!r.calo[ic].used) { + addCaloToPF(r, r.calo[ic]); + if (debug_) + dbgPrintf("PFAlgo2HGC \t calo %3d (pt %7.2f) not linked, promoted to neutral\n", ic, r.calo[ic].floatPt()); + } + } +} + +void PFAlgo2HGC::save_muons(Region &r, const std::vector &tk2mu) const { + // finally do muons + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + if (r.track[itk].muonLink) { + auto &p = addTrackToPF(r, r.track[itk]); + p.muonsrc = r.muon[tk2mu[itk]].src; + if (debug_) + dbgPrintf("PFAlgo2HGC \t track %3d (pt %7.2f) promoted to muon.\n", itk, r.track[itk].floatPt()); + } + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/PFAlgo3.cc b/L1Trigger/Phase2L1ParticleFlow/src/PFAlgo3.cc new file mode 100644 index 0000000000000..41402a431eb6e --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/PFAlgo3.cc @@ -0,0 +1,911 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgo3.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h" + +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" + +#include "FWCore/Utilities/interface/Exception.h" + +#include "DataFormats/Math/interface/deltaR.h" + +namespace { + template + float floatDR(const T1 &t1, const T2 &t2) { + return deltaR(t1.floatEta(), t1.floatPhi(), t2.floatEta(), t2.floatPhi()); + } +} // namespace + +using namespace l1tpf_impl; + +PFAlgo3::PFAlgo3(const edm::ParameterSet &iConfig) : PFAlgoBase(iConfig) { + debug_ = iConfig.getUntrackedParameter("debugPFAlgo3", iConfig.getUntrackedParameter("debug", 0)); + edm::ParameterSet linkcfg = iConfig.getParameter("linking"); + drMatchMu_ = linkcfg.getParameter("trackMuDR"); + + std::string muMatchMode = linkcfg.getParameter("trackMuMatch"); + if (muMatchMode == "boxBestByPtRatio") + muMatchMode_ = MuMatchMode::BoxBestByPtRatio; + else if (muMatchMode == "drBestByPtRatio") + muMatchMode_ = MuMatchMode::DrBestByPtRatio; + else if (muMatchMode == "drBestByPtDiff") + muMatchMode_ = MuMatchMode::DrBestByPtDiff; + else + throw cms::Exception("Configuration", "bad value for trackMuMatch configurable"); + + std::string tkCaloLinkMetric = linkcfg.getParameter("trackCaloLinkMetric"); + if (tkCaloLinkMetric == "bestByDR") + tkCaloLinkMetric_ = TkCaloLinkMetric::BestByDR; + else if (tkCaloLinkMetric == "bestByDRPt") + tkCaloLinkMetric_ = TkCaloLinkMetric::BestByDRPt; + else if (tkCaloLinkMetric == "bestByDR2Pt2") + tkCaloLinkMetric_ = TkCaloLinkMetric::BestByDR2Pt2; + else + throw cms::Exception("Configuration", "bad value for tkCaloLinkMetric configurable"); + + drMatch_ = linkcfg.getParameter("trackCaloDR"); + ptMatchLow_ = linkcfg.getParameter("trackCaloNSigmaLow"); + ptMatchHigh_ = linkcfg.getParameter("trackCaloNSigmaHigh"); + useTrackCaloSigma_ = linkcfg.getParameter("useTrackCaloSigma"); + maxInvisiblePt_ = linkcfg.getParameter("maxInvisiblePt"); + + drMatchEm_ = linkcfg.getParameter("trackEmDR"); + trackEmUseAlsoTrackSigma_ = linkcfg.getParameter("trackEmUseAlsoTrackSigma"); + trackEmMayUseCaloMomenta_ = linkcfg.getParameter("trackEmMayUseCaloMomenta"); + emCaloUseAlsoCaloSigma_ = linkcfg.getParameter("emCaloUseAlsoCaloSigma"); + ptMinFracMatchEm_ = linkcfg.getParameter("caloEmPtMinFrac"); + drMatchEmHad_ = linkcfg.getParameter("emCaloDR"); + emHadSubtractionPtSlope_ = linkcfg.getParameter("emCaloSubtractionPtSlope"); + caloReLinkStep_ = linkcfg.getParameter("caloReLink"); + caloReLinkDr_ = linkcfg.getParameter("caloReLinkDR"); + caloReLinkThreshold_ = linkcfg.getParameter("caloReLinkThreshold"); + rescaleTracks_ = linkcfg.getParameter("rescaleTracks"); + caloTrkWeightedAverage_ = linkcfg.getParameter("useCaloTrkWeightedAverage"); + sumTkCaloErr2_ = linkcfg.getParameter("sumTkCaloErr2"); + ecalPriority_ = linkcfg.getParameter("ecalPriority"); + tightTrackMinStubs_ = linkcfg.getParameter("tightTrackMinStubs"); + tightTrackMaxChi2_ = linkcfg.getParameter("tightTrackMaxChi2"); + tightTrackMaxInvisiblePt_ = linkcfg.getParameter("tightTrackMaxInvisiblePt"); +} + +void PFAlgo3::runPF(Region &r) const { + initRegion(r); + + /// ------------- first step (can all go in parallel) ---------------- + + if (debug_) { + dbgPrintf( + "PFAlgo3\nPFAlgo3 region eta [ %+5.2f , %+5.2f ], phi [ %+5.2f , %+5.2f ], fiducial eta [ %+5.2f , %+5.2f ], " + "phi [ %+5.2f , %+5.2f ]\n", + r.etaMin - r.etaExtra, + r.etaMax + r.etaExtra, + r.phiCenter - r.phiHalfWidth - r.phiExtra, + r.phiCenter + r.phiHalfWidth + r.phiExtra, + r.etaMin, + r.etaMax, + r.phiCenter - r.phiHalfWidth, + r.phiCenter + r.phiHalfWidth); + dbgPrintf("PFAlgo3 \t N(track) %3lu N(em) %3lu N(calo) %3lu N(mu) %3lu\n", + r.track.size(), + r.emcalo.size(), + r.calo.size(), + r.muon.size()); + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + dbgPrintf( + "PFAlgo3 \t track %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + "fid %1d calo ptErr %7.2f stubs %2d chi2 %7.1f\n", + itk, + tk.floatPt(), + tk.floatPtErr(), + tk.floatVtxEta(), + tk.floatVtxPhi(), + tk.floatEta(), + tk.floatPhi(), + int(r.fiducialLocal(tk.floatEta(), tk.floatPhi())), + tk.floatCaloPtErr(), + int(tk.hwStubs), + tk.hwChi2 * 0.1f); + } + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + const auto &em = r.emcalo[iem]; + dbgPrintf( + "PFAlgo3 \t EM %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + "fid %1d calo ptErr %7.2f\n", + iem, + em.floatPt(), + em.floatPtErr(), + em.floatEta(), + em.floatPhi(), + em.floatEta(), + em.floatPhi(), + int(r.fiducialLocal(em.floatEta(), em.floatPhi())), + em.floatPtErr()); + } + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + dbgPrintf( + "PFAlgo3 \t calo %3d: pt %7.2f +- %5.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + "fid %1d calo ptErr %7.2f em pt %7.2f \n", + ic, + calo.floatPt(), + calo.floatPtErr(), + calo.floatEta(), + calo.floatPhi(), + calo.floatEta(), + calo.floatPhi(), + int(r.fiducialLocal(calo.floatEta(), calo.floatPhi())), + calo.floatPtErr(), + calo.floatEmPt()); + } + for (int im = 0, nm = r.muon.size(); im < nm; ++im) { + auto &mu = r.muon[im]; + dbgPrintf( + "PFAlgo3 \t muon %3d: pt %7.2f vtx eta %+5.2f vtx phi %+5.2f calo eta %+5.2f calo phi %+5.2f " + "fid %1d \n", + im, + mu.floatPt(), + mu.floatEta(), + mu.floatPhi(), + mu.floatEta(), + mu.floatPhi(), + int(r.fiducialLocal(mu.floatEta(), mu.floatPhi()))); + } + } + + std::vector tk2mu(r.track.size(), -1), mu2tk(r.muon.size(), -1); + link_tk2mu(r, tk2mu, mu2tk); + + // match all tracks to the closest EM cluster + std::vector tk2em(r.track.size(), -1); + link_tk2em(r, tk2em); + + // match all em to the closest had (can happen in parallel to the above) + std::vector em2calo(r.emcalo.size(), -1); + link_em2calo(r, em2calo); + + /// ------------- next step (needs the previous) ---------------- + // for each EM cluster, count and add up the pt of all the corresponding tracks (skipping muons) + std::vector em2ntk(r.emcalo.size(), 0); + std::vector em2sumtkpt(r.emcalo.size(), 0); + std::vector em2sumtkpterr(r.emcalo.size(), 0); + sum_tk2em(r, tk2em, em2ntk, em2sumtkpt, em2sumtkpterr); + + /// ------------- next step (needs the previous) ---------------- + // process ecal clusters after linking + emcalo_algo(r, em2ntk, em2sumtkpt, em2sumtkpterr); + + /// ------------- next step (needs the previous) ---------------- + // promote all flagged tracks to electrons + emtk_algo(r, tk2em, em2ntk, em2sumtkpterr); + sub_em2calo(r, em2calo); + + /// ------------- next step (needs the previous) ---------------- + // track to calo matching (first iteration, with a lower bound on the calo pt; there may be another one later) + std::vector tk2calo(r.track.size(), -1); + link_tk2calo(r, tk2calo); + + /// ------------- next step (needs the previous) ---------------- + // for each calo, compute the sum of the track pt + std::vector calo2ntk(r.calo.size(), 0); + std::vector calo2sumtkpt(r.calo.size(), 0); + std::vector calo2sumtkpterr(r.calo.size(), 0); + sum_tk2calo(r, tk2calo, calo2ntk, calo2sumtkpt, calo2sumtkpterr); + + // in the meantime, promote unlinked low pt tracks to hadrons + unlinkedtk_algo(r, tk2calo); + + /// ------------- next step (needs the previous) ---------------- + /// OPTIONAL STEP: try to recover split hadron showers (v1.0): + // off by default, as it seems to not do much in jets even if it helps remove tails in single-pion events + if (caloReLinkStep_) + calo_relink(r, calo2ntk, calo2sumtkpt, calo2sumtkpterr); + + /// ------------- next step (needs the previous) ---------------- + // process matched calo clusters, compare energy to sum track pt + std::vector calo2alpha(r.calo.size(), 1); + linkedcalo_algo(r, calo2ntk, calo2sumtkpt, calo2sumtkpterr, calo2alpha); + + /// ------------- next step (needs the previous) ---------------- + /// process matched tracks, if necessary rescale or average + linkedtk_algo(r, tk2calo, calo2ntk, calo2alpha); + // process unmatched calo clusters + unlinkedcalo_algo(r); + // finally do muons + save_muons(r, tk2mu); +} + +void PFAlgo3::link_tk2mu(Region &r, std::vector &tk2mu, std::vector &mu2tk) const { + // do a rectangular match for the moment; make a box of the same are as a 0.2 cone + int intDrMuonMatchBox = std::ceil(drMatchMu_ * CaloCluster::ETAPHI_SCALE * std::sqrt(M_PI / 4)); + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + tk2mu[itk] = false; + } + for (int imu = 0, nmu = r.muon.size(); imu < nmu; ++imu) { + const auto &mu = r.muon[imu]; + if (debug_) + dbgPrintf( + "PFAlgo3 \t muon %3d (pt %7.2f, eta %+5.2f, phi %+5.2f) \n", imu, mu.floatPt(), mu.floatEta(), mu.floatPhi()); + float minDistance = 9e9; + switch (muMatchMode_) { + case MuMatchMode::BoxBestByPtRatio: + minDistance = 4.; + break; + case MuMatchMode::DrBestByPtRatio: + minDistance = 4.; + break; + case MuMatchMode::DrBestByPtDiff: + minDistance = 0.5 * mu.floatPt(); + break; + } + int imatch = -1; + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + int deta = std::abs(mu.hwEta - tk.hwEta); + int dphi = std::abs((mu.hwPhi - tk.hwPhi) % CaloCluster::PHI_WRAP); + float dr = floatDR(mu, tk); + float dpt = std::abs(mu.floatPt() - tk.floatPt()); + float dptr = (mu.hwPt > tk.hwPt ? mu.floatPt() / tk.floatPt() : tk.floatPt() / mu.floatPt()); + bool ok = false; + float distance = 9e9; + switch (muMatchMode_) { + case MuMatchMode::BoxBestByPtRatio: + ok = (deta < intDrMuonMatchBox) && (dphi < intDrMuonMatchBox); + distance = dptr; + break; + case MuMatchMode::DrBestByPtRatio: + ok = (dr < drMatchMu_); + distance = dptr; + break; + case MuMatchMode::DrBestByPtDiff: + ok = (dr < drMatchMu_); + distance = dpt; + break; + } + if (debug_ && dr < 0.4) { + dbgPrintf( + "PFAlgo3 \t\t possible match with track %3d (pt %7.2f, caloeta %+5.2f, calophi %+5.2f, dr %.2f, eta " + "%+5.2f, phi %+5.2f, dr %.2f): angular %1d, distance %.3f (vs %.3f)\n", + itk, + tk.floatPt(), + tk.floatEta(), + tk.floatPhi(), + dr, + tk.floatVtxEta(), + tk.floatVtxPhi(), + deltaR(mu.floatEta(), mu.floatPhi(), tk.floatVtxEta(), tk.floatVtxPhi()), + (ok ? 1 : 0), + distance, + minDistance); + } + if (!ok) + continue; + // FIXME for the moment, we do the floating point matching in pt + if (distance < minDistance) { + minDistance = distance; + imatch = itk; + } + } + if (debug_ && imatch > -1) + dbgPrintf("PFAlgo3 \t muon %3d (pt %7.2f) linked to track %3d (pt %7.2f)\n", + imu, + mu.floatPt(), + imatch, + r.track[imatch].floatPt()); + if (debug_ && imatch == -1) + dbgPrintf("PFAlgo3 \t muon %3d (pt %7.2f) not linked to any track\n", imu, mu.floatPt()); + mu2tk[imu] = imatch; + if (imatch > -1) { + tk2mu[imatch] = imu; + r.track[imatch].muonLink = true; + } + } +} + +void PFAlgo3::link_tk2em(Region &r, std::vector &tk2em) const { + // match all tracks to the closest EM cluster + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + //if (tk.muonLink) continue; // not necessary I think + float drbest = drMatchEm_; + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + const auto &em = r.emcalo[iem]; + float dr = floatDR(tk, em); + if (dr < drbest) { + tk2em[itk] = iem; + drbest = dr; + } + } + if (debug_ && tk2em[itk] != -1) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) matches to EM %3d (pt %7.2f) with dr %.3f\n", + itk, + tk.floatPt(), + tk2em[itk], + tk2em[itk] == -1 ? 0.0 : r.emcalo[tk2em[itk]].floatPt(), + drbest); + } +} + +void PFAlgo3::link_em2calo(Region &r, std::vector &em2calo) const { + // match all em to the closest had (can happen in parallel to the above) + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + const auto &em = r.emcalo[iem]; + float drbest = drMatchEmHad_; + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + const auto &calo = r.calo[ic]; + if (calo.floatEmPt() < ptMinFracMatchEm_ * em.floatPt()) + continue; + float dr = floatDR(calo, em); + if (dr < drbest) { + em2calo[iem] = ic; + drbest = dr; + } + } + if (debug_ && em2calo[iem] != -1) + dbgPrintf("PFAlgo3 \t EM %3d (pt %7.2f) matches to calo %3d (pt %7.2f, empt %7.2f) with dr %.3f\n", + iem, + em.floatPt(), + em2calo[iem], + em2calo[iem] == -1 ? 0.0 : r.calo[em2calo[iem]].floatPt(), + em2calo[iem] == -1 ? 0.0 : r.calo[em2calo[iem]].floatEmPt(), + drbest); + } +} + +void PFAlgo3::sum_tk2em(Region &r, + const std::vector &tk2em, + std::vector &em2ntk, + std::vector &em2sumtkpt, + std::vector &em2sumtkpterr) const { + // for each EM cluster, count and add up the pt of all the corresponding tracks (skipping muons) + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + const auto &em = r.emcalo[iem]; + if (r.globalAbsEta(em.floatEta()) > 2.5) + continue; + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + if (tk2em[itk] == iem) { + const auto &tk = r.track[itk]; + if (tk.muonLink) + continue; + em2ntk[iem]++; + em2sumtkpt[iem] += tk.floatPt(); + em2sumtkpterr[iem] += tk.floatPtErr(); + } + } + } +} + +void PFAlgo3::emcalo_algo(Region &r, + const std::vector &em2ntk, + const std::vector &em2sumtkpt, + const std::vector &em2sumtkpterr) const { + // process ecal clusters after linking + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + auto &em = r.emcalo[iem]; + em.isEM = false; + em.used = false; + em.hwFlags = 0; + if (r.globalAbsEta(em.floatEta()) > 2.5) + continue; + if (debug_) + dbgPrintf("PFAlgo3 \t EM %3d (pt %7.2f) has %2d tracks (sumpt %7.2f, sumpterr %7.2f), ptdif %7.2f +- %7.2f\n", + iem, + em.floatPt(), + em2ntk[iem], + em2sumtkpt[iem], + em2sumtkpterr[iem], + em.floatPt() - em2sumtkpt[iem], + std::max(em2sumtkpterr[iem], em.floatPtErr())); + if (em2ntk[iem] == 0) { // Photon + em.isEM = true; + addCaloToPF(r, em); + em.used = true; + if (debug_) + dbgPrintf("PFAlgo3 \t EM %3d (pt %7.2f) ---> promoted to photon\n", iem, em.floatPt()); + continue; + } + float ptdiff = em.floatPt() - em2sumtkpt[iem]; + float pterr = trackEmUseAlsoTrackSigma_ ? std::max(em2sumtkpterr[iem], em.floatPtErr()) : em.floatPtErr(); + // avoid "pt = inf +- inf" track to become an electron. + if (pterr > 2 * em.floatPt()) { + pterr = 2 * em.floatPt(); + if (debug_) + dbgPrintf("PFAlgo3 \t EM %3d (pt %7.2f) ---> clamp pterr ---> new ptdiff %7.2f +- %7.2f\n", + iem, + em.floatPt(), + ptdiff, + pterr); + } + + if (ptdiff > -ptMatchLow_ * pterr) { + em.isEM = true; + em.used = true; + // convert leftover to a photon if significant + if (ptdiff > +ptMatchHigh_ * pterr) { + auto &p = addCaloToPF(r, em); + p.setFloatPt(ptdiff); + if (debug_) + dbgPrintf("PFAlgo3 \t EM %3d (pt %7.2f) ---> promoted to electron(s) + photon (pt %7.2f)\n", + iem, + em.floatPt(), + ptdiff); + } else { + em.hwFlags = 1; // may use calo momentum + if (debug_) + dbgPrintf("PFAlgo3 \t EM %3d (pt %7.2f) ---> promoted to electron(s)\n", iem, em.floatPt()); + } + } else { + em.isEM = false; + em.used = false; + em.hwFlags = 0; + //discardCalo(r, em, 2); + } + } +} + +void PFAlgo3::emtk_algo(Region &r, + const std::vector &tk2em, + const std::vector &em2ntk, + const std::vector &em2sumtkpterr) const { + // promote all flagged tracks to electrons + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + auto &tk = r.track[itk]; + if (tk2em[itk] == -1 || tk.muonLink) + continue; + const auto &em = r.emcalo[tk2em[itk]]; + if (em.isEM) { + auto &p = addTrackToPF(r, tk); + p.cluster.src = em.src; + // FIXME to check if this is useful + if (trackEmMayUseCaloMomenta_ && em2ntk[tk2em[itk]] == 1 && em.hwFlags == 1) { + if (em.floatPtErr() < em2sumtkpterr[tk2em[itk]]) { + p.setFloatPt(em.floatPt()); + } + } + if (debug_) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) matched to EM %3d (pt %7.2f) promoted to electron with pt %7.2f\n", + itk, + tk.floatPt(), + tk2em[itk], + em.floatPt(), + p.floatPt()); + p.hwId = l1t::PFCandidate::Electron; + tk.used = true; + } + } +} + +void PFAlgo3::sub_em2calo(Region &r, const std::vector &em2calo) const { + // subtract EM component from Calo clusters for all photons and electrons (within tracker coverage) + // kill clusters that end up below their own uncertainty, or that loose 90% of the energy, + // unless they still have live EM clusters pointing to them + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + float pt0 = calo.floatPt(), ept0 = calo.floatEmPt(), pt = pt0, ept = ept0; + bool keepme = false; + for (int iem = 0, nem = r.emcalo.size(); iem < nem; ++iem) { + if (em2calo[iem] == ic) { + const auto &em = r.emcalo[iem]; + if (em.isEM) { + if (debug_) + dbgPrintf( + "PFAlgo3 \t EM %3d (pt %7.2f) is subtracted from calo %3d (pt %7.2f) scaled by %.3f (deltaPt = " + "%7.2f)\n", + iem, + em.floatPt(), + ic, + calo.floatPt(), + emHadSubtractionPtSlope_, + emHadSubtractionPtSlope_ * em.floatPt()); + pt -= emHadSubtractionPtSlope_ * em.floatPt(); + ept -= em.floatPt(); + } else { + keepme = true; + if (debug_) + dbgPrintf( + "PFAlgo3 \t EM %3d (pt %7.2f) not subtracted from calo %3d (pt %7.2f), and calo marked to be kept " + "after EM subtraction\n", + iem, + em.floatPt(), + ic, + calo.floatPt()); + } + } + } + if (pt < pt0) { + if (debug_) + dbgPrintf( + "PFAlgo3 \t calo %3d (pt %7.2f +- %7.2f) has a subtracted pt of %7.2f, empt %7.2f -> %7.2f, isem %d\n", + ic, + calo.floatPt(), + calo.floatPtErr(), + pt, + ept0, + ept, + calo.isEM); + calo.setFloatPt(pt); + calo.setFloatEmPt(ept); + if (!keepme && + ((emCaloUseAlsoCaloSigma_ ? pt < calo.floatPtErr() : false) || pt <= 0.125 * pt0 || + (calo.isEM && ept <= 0.125 * ept0))) { // the <= is important since in firmware the pt0/8 can be zero + if (debug_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f) ----> discarded\n", ic, calo.floatPt()); + calo.used = true; + calo.setFloatPt(pt0); //discardCalo(r, calo, 1); // log this as discarded, for debugging + } + } + } +} + +void PFAlgo3::link_tk2calo(Region &r, std::vector &tk2calo) const { + // track to calo matching (first iteration, with a lower bound on the calo pt; there may be another one later) + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + const auto &tk = r.track[itk]; + if (tk.muonLink || tk.used) + continue; // not necessary but just a waste of CPU otherwise + float drbest = drMatch_, dptscale = 0; + switch (tkCaloLinkMetric_) { + case TkCaloLinkMetric::BestByDR: + drbest = drMatch_; + break; + case TkCaloLinkMetric::BestByDRPt: + drbest = 1.0; + dptscale = drMatch_ / tk.floatCaloPtErr(); + break; + case TkCaloLinkMetric::BestByDR2Pt2: + drbest = 1.0; + dptscale = drMatch_ / tk.floatCaloPtErr(); + break; + } + float minCaloPt = tk.floatPt() - ptMatchLow_ * tk.floatCaloPtErr(); + if (debug_) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) to be matched to calo, min pT %7.2f\n", itk, tk.floatPt(), minCaloPt); + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo.used || calo.floatPt() <= minCaloPt) + continue; + float dr = floatDR(tk, calo), dq; + switch (tkCaloLinkMetric_) { + case TkCaloLinkMetric::BestByDR: + if (dr < drbest) { + tk2calo[itk] = ic; + drbest = dr; + } + break; + case TkCaloLinkMetric::BestByDRPt: + dq = dr + std::max(tk.floatPt() - calo.floatPt(), 0.) * dptscale; + //if (debug_ && dr < 0.2) dbgPrintf("PFAlgo3 \t\t\t track %3d (pt %7.2f) vs calo %3d (pt %7.2f): dr %.3f, dq %.3f\n", itk, tk.floatPt(), ic, calo.floatPt(), dr, dq); + if (dr < drMatch_ && dq < drbest) { + tk2calo[itk] = ic; + drbest = dq; + } + break; + case TkCaloLinkMetric::BestByDR2Pt2: + dq = hypot(dr, std::max(tk.floatPt() - calo.floatPt(), 0.) * dptscale); + //if (debug_ && dr < 0.2) dbgPrintf("PFAlgo3 \t\t\t track %3d (pt %7.2f) vs calo %3d (pt %7.2f): dr %.3f, dq %.3f\n", itk, tk.floatPt(), ic, calo.floatPt(), dr, dq); + if (dr < drMatch_ && dq < drbest) { + tk2calo[itk] = ic; + drbest = dq; + } + break; + } + } + if (debug_ && tk2calo[itk] != -1) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) matches to calo %3d (pt %7.2f) with dist %.3f\n", + itk, + tk.floatPt(), + tk2calo[itk], + tk2calo[itk] == -1 ? 0.0 : r.calo[tk2calo[itk]].floatPt(), + drbest); + // now we re-do this for debugging sake, it may be done for real later + if (debug_ && tk2calo[itk] == -1) { + int ibest = -1; + drbest = 0.3; + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo.used) + continue; + float dr = floatDR(tk, calo); + if (dr < drbest) { + ibest = ic; + drbest = dr; + } + } + if (ibest != -1) + dbgPrintf( + "PFAlgo3 \t track %3d (pt %7.2f) would match to calo %3d (pt %7.2f) with dr %.3f if the pt min and dr " + "requirement had been relaxed\n", + itk, + tk.floatPt(), + ibest, + r.calo[ibest].floatPt(), + drbest); + } + } +} + +void PFAlgo3::sum_tk2calo(Region &r, + const std::vector &tk2calo, + std::vector &calo2ntk, + std::vector &calo2sumtkpt, + std::vector &calo2sumtkpterr) const { + // for each calo, compute the sum of the track pt + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + const auto &calo = r.calo[ic]; + if (r.globalAbsEta(calo.floatEta()) > 2.5) + continue; + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + if (tk2calo[itk] == ic) { + const auto &tk = r.track[itk]; + if (tk.muonLink || tk.used) + continue; + calo2ntk[ic]++; + calo2sumtkpt[ic] += tk.floatPt(); + calo2sumtkpterr[ic] += std::pow(tk.floatCaloPtErr(), sumTkCaloErr2_ ? 2 : 1); + } + } + if (sumTkCaloErr2_ && calo2sumtkpterr[ic] > 0) + calo2sumtkpterr[ic] = std::sqrt(calo2sumtkpterr[ic]); + } +} + +void PFAlgo3::unlinkedtk_algo(Region &r, const std::vector &tk2calo) const { + // in the meantime, promote unlinked low pt tracks to hadrons + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + auto &tk = r.track[itk]; + if (tk2calo[itk] != -1 || tk.muonLink || tk.used) + continue; + float maxPt = (tk.hwStubs >= tightTrackMinStubs_ && tk.hwChi2 < 10 * tightTrackMaxChi2_) ? tightTrackMaxInvisiblePt_ + : maxInvisiblePt_; + if (tk.floatPt() < maxPt) { + if (debug_) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) not matched to calo, kept as charged hadron\n", itk, tk.floatPt()); + auto &p = addTrackToPF(r, tk); + p.hwStatus = GoodTK_NoCalo; + tk.used = true; + } else { + if (debug_) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) not matched to calo, dropped\n", itk, tk.floatPt()); + //discardTrack(r, tk, BadTK_NoCalo); // log this as discarded, for debugging + } + } +} + +void PFAlgo3::calo_relink(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr) const { + /// OPTIONAL STEP: try to recover split hadron showers (v1.0): + // take hadrons that are not track matched, close by a hadron which has an excess of track pt vs calo pt + // add this pt to the calo pt of the other cluster + // off by default, as it seems to not do much in jets even if it helps remove tails in single-pion events + std::vector addtopt(r.calo.size(), 0); + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo2ntk[ic] != 0 || calo.used || r.globalAbsEta(calo.floatEta()) > 2.5) + continue; + int i2best = -1; + float drbest = caloReLinkDr_; + for (int ic2 = 0; ic2 < nc; ++ic2) { + const auto &calo2 = r.calo[ic2]; + if (calo2ntk[ic2] == 0 || calo2.used || r.globalAbsEta(calo2.floatEta()) > 2.5) + continue; + float dr = floatDR(calo, calo2); + //// uncomment below for more verbose debugging + //if (debug_ && dr < 0.5) dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f) with no tracks is at dr %.3f from calo %3d with pt %7.2f (sum tk pt %7.2f), track excess %7.2f +- %7.2f\n", ic, calo.floatPt(), dr, ic2, calo2.floatPt(), calo2sumtkpt[ic2], calo2sumtkpt[ic2] - calo2.floatPt(), useTrackCaloSigma_ ? calo2sumtkpterr[ic2] : calo2.floatPtErr()); + if (dr < drbest) { + float ptdiff = + calo2sumtkpt[ic2] - calo2.floatPt() + (useTrackCaloSigma_ ? calo2sumtkpterr[ic2] : calo2.floatPtErr()); + if (ptdiff >= caloReLinkThreshold_ * calo.floatPt()) { + i2best = ic2; + drbest = dr; + } + } + } + if (i2best != -1) { + const auto &calo2 = r.calo[i2best]; + if (debug_) + dbgPrintf( + "PFAlgo3 \t calo %3d (pt %7.2f) with no tracks matched within dr %.3f with calo %3d with pt %7.2f (sum tk " + "pt %7.2f), track excess %7.2f +- %7.2f\n", + ic, + calo.floatPt(), + drbest, + i2best, + calo2.floatPt(), + calo2sumtkpt[i2best], + calo2sumtkpt[i2best] - calo2.floatPt(), + useTrackCaloSigma_ ? calo2sumtkpterr[i2best] : calo2.floatPtErr()); + calo.used = true; + addtopt[i2best] += calo.floatPt(); + } + } + // we do this at the end, so that the above loop is parallelizable + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + if (addtopt[ic]) { + auto &calo = r.calo[ic]; + if (debug_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f, sum tk pt %7.2f) is increased to pt %7.2f after merging\n", + ic, + calo.floatPt(), + calo2sumtkpt[ic], + calo.floatPt() + addtopt[ic]); + calo.setFloatPt(calo.floatPt() + addtopt[ic]); + } + } +} + +void PFAlgo3::linkedcalo_algo(Region &r, + const std::vector &calo2ntk, + const std::vector &calo2sumtkpt, + const std::vector &calo2sumtkpterr, + std::vector &calo2alpha) const { + /// ------------- next step (needs the previous) ---------------- + // process matched calo clusters, compare energy to sum track pt + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + auto &calo = r.calo[ic]; + if (calo2ntk[ic] == 0 || calo.used) + continue; + float ptdiff = calo.floatPt() - calo2sumtkpt[ic]; + float pterr = useTrackCaloSigma_ ? calo2sumtkpterr[ic] : calo.floatPtErr(); + if (debug_) + dbgPrintf( + "PFAlgo3 \t calo %3d (pt %7.2f +- %7.2f, empt %7.2f) has %2d tracks (sumpt %7.2f, sumpterr %7.2f), ptdif " + "%7.2f +- %7.2f\n", + ic, + calo.floatPt(), + calo.floatPtErr(), + calo.floatEmPt(), + calo2ntk[ic], + calo2sumtkpt[ic], + calo2sumtkpterr[ic], + ptdiff, + pterr); + if (ptdiff > +ptMatchHigh_ * pterr) { + if (ecalPriority_) { + if (calo.floatEmPt() > 1) { + float emptdiff = std::min(ptdiff, calo.floatEmPt()); + if (debug_) + dbgPrintf( + "PFAlgo3 \t calo %3d (pt %7.2f, empt %7.2f) ---> make photon with pt %7.2f, reduce ptdiff to %7.2f " + "+- %7.2f\n", + ic, + calo.floatPt(), + calo.floatEmPt(), + emptdiff, + ptdiff - emptdiff, + pterr); + auto &p = addCaloToPF(r, calo); + p.setFloatPt(emptdiff); + p.hwId = l1t::PFCandidate::Photon; + ptdiff -= emptdiff; + } + if (ptdiff > 2) { + if (debug_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f, empt %7.2f) ---> make also neutral hadron with pt %7.2f\n", + ic, + calo.floatPt(), + calo.floatEmPt(), + ptdiff); + auto &p = addCaloToPF(r, calo); + p.setFloatPt(ptdiff); + p.hwId = l1t::PFCandidate::NeutralHadron; + } + } else { + if (debug_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f) ---> promoted to neutral with pt %7.2f\n", + ic, + calo.floatPt(), + ptdiff); + auto &p = addCaloToPF(r, calo); + p.setFloatPt(ptdiff); + calo.hwFlags = 0; + } + } else if (ptdiff > -ptMatchLow_ * pterr) { + // nothing to do (weighted average happens when we process the tracks) + calo.hwFlags = 1; + if (debug_) + dbgPrintf( + "PFAlgo3 \t calo %3d (pt %7.2f) ---> to be deleted, will use tracks instead\n", ic, calo.floatPt()); + //discardCalo(r, calo, 0); // log this as discarded, for debugging + } else { + // tracks overshoot, rescale to tracks to calo + calo2alpha[ic] = rescaleTracks_ ? calo.floatPt() / calo2sumtkpt[ic] : 1.0; + calo.hwFlags = 2; + if (debug_ && rescaleTracks_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f) ---> tracks overshoot and will be scaled down by %.4f\n", + ic, + calo.floatPt(), + calo2alpha[ic]); + if (debug_ && !rescaleTracks_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f) ---> tracks overshoot by %.4f\n", + ic, + calo.floatPt(), + calo2sumtkpt[ic] / calo.floatPt()); + } + calo.used = true; + } +} + +void PFAlgo3::linkedtk_algo(Region &r, + const std::vector &tk2calo, + const std::vector &calo2ntk, + const std::vector &calo2alpha) const { + // process matched tracks, if necessary rescale or average + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + auto &tk = r.track[itk]; + if (tk2calo[itk] == -1 || tk.muonLink || tk.used) + continue; + auto &p = addTrackToPF(r, tk); + tk.used = true; + const auto &calo = r.calo[tk2calo[itk]]; + p.cluster.src = calo.src; + if (calo.hwFlags == 1) { + // can do weighted average if there's just one track + if (calo2ntk[tk2calo[itk]] == 1 && caloTrkWeightedAverage_) { + p.hwStatus = GoodTK_Calo_TkPt; + float ptavg = tk.floatPt(); + if (tk.floatPtErr() > 0) { + float wcalo = 1.0 / std::pow(tk.floatCaloPtErr(), 2); + float wtk = 1.0 / std::pow(tk.floatPtErr(), 2); + ptavg = (calo.floatPt() * wcalo + tk.floatPt() * wtk) / (wcalo + wtk); + p.hwStatus = GoodTK_Calo_TkCaloPt; + } + p.setFloatPt(ptavg); + if (debug_) + dbgPrintf( + "PFAlgo3 \t track %3d (pt %7.2f +- %7.2f) combined with calo %3d (pt %7.2f +- %7.2f (from tk) yielding " + "candidate of pt %7.2f\n", + itk, + tk.floatPt(), + tk.floatPtErr(), + tk2calo[itk], + calo.floatPt(), + tk.floatCaloPtErr(), + ptavg); + } else { + p.hwStatus = GoodTK_Calo_TkPt; + if (debug_) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) linked to calo %3d promoted to charged hadron\n", + itk, + tk.floatPt(), + tk2calo[itk]); + } + } else if (calo.hwFlags == 2) { + // must rescale + p.setFloatPt(tk.floatPt() * calo2alpha[tk2calo[itk]]); + p.hwStatus = GoodTk_Calo_CaloPt; + if (debug_) + dbgPrintf( + "PFAlgo3 \t track %3d (pt %7.2f, stubs %2d chi2 %7.1f) linked to calo %3d promoted to charged hadron with " + "pt %7.2f after maybe rescaling\n", + itk, + tk.floatPt(), + int(tk.hwStubs), + tk.hwChi2 * 0.1f, + tk2calo[itk], + p.floatPt()); + } + } +} + +void PFAlgo3::unlinkedcalo_algo(Region &r) const { + // process unmatched calo clusters + for (int ic = 0, nc = r.calo.size(); ic < nc; ++ic) { + if (!r.calo[ic].used) { + addCaloToPF(r, r.calo[ic]); + if (debug_) + dbgPrintf("PFAlgo3 \t calo %3d (pt %7.2f) not linked, promoted to neutral\n", ic, r.calo[ic].floatPt()); + } + } +} + +void PFAlgo3::save_muons(Region &r, const std::vector &tk2mu) const { + // finally do muons + for (int itk = 0, ntk = r.track.size(); itk < ntk; ++itk) { + if (r.track[itk].muonLink) { + auto &p = addTrackToPF(r, r.track[itk]); + p.muonsrc = r.muon[tk2mu[itk]].src; + if (debug_) + dbgPrintf("PFAlgo3 \t track %3d (pt %7.2f) promoted to muon.\n", itk, r.track[itk].floatPt()); + } + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/PFAlgoBase.cc b/L1Trigger/Phase2L1ParticleFlow/src/PFAlgoBase.cc new file mode 100644 index 0000000000000..f3b5f3d83652b --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/PFAlgoBase.cc @@ -0,0 +1,57 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/PFAlgoBase.h" + +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" + +using namespace l1tpf_impl; + +PFAlgoBase::PFAlgoBase(const edm::ParameterSet &iConfig) : debug_(iConfig.getUntrackedParameter("debug", 0)) {} + +PFAlgoBase::~PFAlgoBase() {} + +void PFAlgoBase::initRegion(Region &r) const { + r.inputSort(); + r.pf.clear(); + r.puppi.clear(); + for (auto &c : r.calo) + c.used = false; + for (auto &c : r.emcalo) + c.used = false; + for (auto &t : r.track) { + t.used = false; + t.muonLink = false; + } +} + +PFParticle &PFAlgoBase::addTrackToPF(std::vector &pfs, const PropagatedTrack &tk) const { + PFParticle pf; + pf.hwPt = tk.hwPt; + pf.hwEta = tk.hwEta; + pf.hwPhi = tk.hwPhi; + pf.hwVtxEta = tk.hwEta; // FIXME: get from the track + pf.hwVtxPhi = tk.hwPhi; // before propagation + pf.track = tk; + pf.cluster.hwPt = 0; + pf.cluster.src = nullptr; + pf.muonsrc = nullptr; + pf.hwId = (tk.muonLink ? l1t::PFCandidate::Muon : l1t::PFCandidate::ChargedHadron); + pf.hwStatus = 0; + pfs.push_back(pf); + return pfs.back(); +} + +PFParticle &PFAlgoBase::addCaloToPF(std::vector &pfs, const CaloCluster &calo) const { + PFParticle pf; + pf.hwPt = calo.hwPt; + pf.hwEta = calo.hwEta; + pf.hwPhi = calo.hwPhi; + pf.hwVtxEta = calo.hwEta; + pf.hwVtxPhi = calo.hwPhi; + pf.track.hwPt = 0; + pf.track.src = nullptr; + pf.cluster = calo; + pf.muonsrc = nullptr; + pf.hwId = (calo.isEM ? l1t::PFCandidate::Photon : l1t::PFCandidate::NeutralHadron); + pf.hwStatus = 0; + pfs.push_back(pf); + return pfs.back(); +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/PUAlgoBase.cc b/L1Trigger/Phase2L1ParticleFlow/src/PUAlgoBase.cc new file mode 100644 index 0000000000000..b1c55079b0aa2 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/PUAlgoBase.cc @@ -0,0 +1,81 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/PUAlgoBase.h" + +#include + +using namespace l1tpf_impl; + +PUAlgoBase::PUAlgoBase(const edm::ParameterSet &iConfig) + : debug_(iConfig.getUntrackedParameter("debug", 0)), + etaCharged_(iConfig.getParameter("etaCharged")), + vtxRes_(iConfig.getParameter("vtxRes")), + vtxAdaptiveCut_(iConfig.getParameter("vtxAdaptiveCut")) {} + +PUAlgoBase::~PUAlgoBase() {} + +void PUAlgoBase::runChargedPV(Region &r, float z0) const { + int16_t iZ0 = round(z0 * InputTrack::Z0_SCALE); + int16_t iDZ = round(1.5 * vtxRes_ * InputTrack::Z0_SCALE); + int16_t iDZ2 = vtxAdaptiveCut_ ? round(4.0 * vtxRes_ * InputTrack::Z0_SCALE) : iDZ; + for (PFParticle &p : r.pf) { + bool barrel = std::abs(p.track.hwVtxEta) < InputTrack::VTX_ETA_1p3; + if (r.relativeCoordinates) + barrel = + (std::abs(r.globalAbsEta(p.track.floatVtxEta())) < 1.3); // FIXME could make a better integer implementation + p.chargedPV = (p.hwId <= 1 && std::abs(p.track.hwZ0 - iZ0) < (barrel ? iDZ : iDZ2)); + } +} + +void PUAlgoBase::doVertexing(std::vector &rs, VertexAlgo algo, float &pvdz) const { + int lNBins = int(40. / vtxRes_); + if (algo == VertexAlgo::TP) + lNBins *= 3; + std::unique_ptr h_dz(new TH1F("h_dz", "h_dz", lNBins, -20, 20)); + if (algo != VertexAlgo::External) { + for (const Region &r : rs) { + for (const PropagatedTrack &p : r.track) { + if (rs.size() > 1) { + if (!r.fiducialLocal(p.floatVtxEta(), p.floatVtxPhi())) + continue; // skip duplicates + } + h_dz->Fill(p.floatDZ(), std::min(p.floatPt(), 50.f)); + } + } + } + switch (algo) { + case VertexAlgo::External: + break; + case VertexAlgo::Old: { + int imaxbin = h_dz->GetMaximumBin(); + pvdz = h_dz->GetXaxis()->GetBinCenter(imaxbin); + }; break; + case VertexAlgo::TP: { + float max = 0; + int bmax = -1; + for (int b = 1; b <= lNBins; ++b) { + float sum3 = h_dz->GetBinContent(b) + h_dz->GetBinContent(b + 1) + h_dz->GetBinContent(b - 1); + if (bmax == -1 || sum3 > max) { + max = sum3; + bmax = b; + } + } + pvdz = h_dz->GetXaxis()->GetBinCenter(bmax); + }; break; + } + int16_t iZ0 = round(pvdz * InputTrack::Z0_SCALE); + int16_t iDZ = round(1.5 * vtxRes_ * InputTrack::Z0_SCALE); + int16_t iDZ2 = vtxAdaptiveCut_ ? round(4.0 * vtxRes_ * InputTrack::Z0_SCALE) : iDZ; + for (Region &r : rs) { + for (PropagatedTrack &p : r.track) { + bool central = std::abs(p.hwVtxEta) < InputTrack::VTX_ETA_1p3; + if (r.relativeCoordinates) + central = + (std::abs(r.globalAbsEta(p.floatVtxEta())) < 1.3); // FIXME could make a better integer implementation + p.fromPV = (std::abs(p.hwZ0 - iZ0) < (central ? iDZ : iDZ2)); + } + } +} + +const std::vector &PUAlgoBase::puGlobalNames() const { + static const std::vector empty_; + return empty_; +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ParametricResolution.cc b/L1Trigger/Phase2L1ParticleFlow/src/ParametricResolution.cc new file mode 100644 index 0000000000000..a37c76bf2ca17 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ParametricResolution.cc @@ -0,0 +1,48 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/ParametricResolution.h" + +std::vector l1tpf::ParametricResolution::getVFloat(const edm::ParameterSet &cpset, const std::string &name) { + std::vector vd = cpset.getParameter>(name); + return std::vector(vd.begin(), vd.end()); +} + +l1tpf::ParametricResolution::ParametricResolution(const edm::ParameterSet &cpset) + : etas_(getVFloat(cpset, "etaBins")), offsets_(getVFloat(cpset, "offset")), scales_(getVFloat(cpset, "scale")) { + if (cpset.existsAs>("ptMin")) { + ptMins_ = getVFloat(cpset, "ptMin"); + } else { + float ptMin = cpset.existsAs("ptMin") ? cpset.getParameter("ptMin") : 0; + ptMins_ = std::vector(etas_.size(), ptMin); + } + if (cpset.existsAs>("ptMax")) { + ptMaxs_ = getVFloat(cpset, "ptMax"); + } else { + ptMaxs_ = std::vector(etas_.size(), 1e6); + } + + std::string skind = cpset.getParameter("kind"); + if (skind == "track") + kind_ = Kind::Track; + else if (skind == "calo") + kind_ = Kind::Calo; + else + throw cms::Exception("Configuration", "Bad kind of resolution: " + skind); +} + +float l1tpf::ParametricResolution::operator()(const float pt, const float abseta) const { + for (unsigned int i = 0, n = etas_.size(); i < n; ++i) { + if (pt > ptMaxs_[i]) + continue; + if (abseta < etas_[i]) { + switch (kind_) { + case Kind::Track: + return pt * std::min(1.f, std::hypot(pt * scales_[i] * 0.001, offsets_[i])); + case Kind::Calo: + return std::min(pt, pt * scales_[i] + offsets_[i]); + if (pt < ptMins_[i]) + return pt * std::min(1, scales_[i] + offsets_[i] / ptMins_[i]); + return std::min(pt, pt * scales_[i] + offsets_[i]); + } + } + } + return std::min(pt, 0.3 * pt + 7); // saturate to 100% at 10 GeV, and to 30% at high pt +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/PuppiAlgo.cc b/L1Trigger/Phase2L1ParticleFlow/src/PuppiAlgo.cc new file mode 100644 index 0000000000000..8f93b627902ef --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/PuppiAlgo.cc @@ -0,0 +1,266 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/PuppiAlgo.h" +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "DataFormats/Math/interface/deltaR.h" +#include "L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h" + +#include "Math/ProbFunc.h" + +namespace { + std::vector vd2vf(const std::vector &vd) { + std::vector ret; + ret.insert(ret.end(), vd.begin(), vd.end()); + return ret; + } +} // namespace + +using namespace l1tpf_impl; + +PuppiAlgo::PuppiAlgo(const edm::ParameterSet &iConfig) + : PUAlgoBase(iConfig), + puppiDr_(iConfig.getParameter("puppiDr")), + puppiDrMin_(iConfig.getParameter("puppiDrMin")), + puppiPtMax_(iConfig.getParameter("puppiPtMax")), + puppiEtaCuts_(vd2vf(iConfig.getParameter>("puppiEtaCuts"))), + puppiPtCuts_(vd2vf(iConfig.getParameter>("puppiPtCuts"))), + puppiPtCutsPhotons_(vd2vf(iConfig.getParameter>("puppiPtCutsPhotons"))), + puppiUsingBareTracks_(iConfig.getParameter("puppiUsingBareTracks")) { + debug_ = iConfig.getUntrackedParameter("puppiDebug", debug_); + if (puppiEtaCuts_.size() != puppiPtCuts_.size() || puppiPtCuts_.size() != puppiPtCutsPhotons_.size()) { + throw cms::Exception("Configuration", "Bad PUPPI config"); + } + for (unsigned int i = 0, n = puppiEtaCuts_.size(); i < n; ++i) { + intPuppiEtaCuts_.push_back(std::round(puppiEtaCuts_[i] * CaloCluster::ETAPHI_SCALE)); + intPuppiPtCuts_.push_back(std::round(puppiPtCuts_[i] * CaloCluster::PT_SCALE)); + intPuppiPtCutsPhotons_.push_back(std::round(puppiPtCutsPhotons_[i] * CaloCluster::PT_SCALE)); + } +} + +PuppiAlgo::~PuppiAlgo() {} + +const std::vector &PuppiAlgo::puGlobalNames() const { + static const std::vector names_{"alphaCMed", "alphaCRms", "alphaFMed", "alphaFRms"}; + return names_; +} +void PuppiAlgo::doPUGlobals(const std::vector &rs, float npu, std::vector &globals) const { + globals.resize(4); + computePuppiMedRMS(rs, globals[0], globals[1], globals[2], globals[3]); +} + +void PuppiAlgo::runNeutralsPU(Region &r, float npu, const std::vector &globals) const { + std::vector alphaC, alphaF; + computePuppiAlphas(r, alphaC, alphaF); + computePuppiWeights(r, alphaC, alphaF, globals[0], globals[1], globals[2], globals[3]); + fillPuppi(r); +} + +void PuppiAlgo::computePuppiAlphas(const Region &r, std::vector &alphaC, std::vector &alphaF) const { + alphaC.resize(r.pf.size()); + alphaF.resize(r.pf.size()); + float puppiDr2 = std::pow(puppiDr_, 2), puppiDr2min = std::pow(puppiDrMin_, 2); + for (unsigned int ip = 0, np = r.pf.size(); ip < np; ++ip) { + const PFParticle &p = r.pf[ip]; + if (p.hwId <= 1) + continue; + // neutral + alphaC[ip] = 0; + alphaF[ip] = 0; + for (const PFParticle &p2 : r.pf) { + float dr2 = ::deltaR2(p.floatEta(), p.floatPhi(), p2.floatEta(), p2.floatPhi()); + if (dr2 > 0 && dr2 < puppiDr2) { + float w = std::pow(std::min(p2.floatPt(), puppiPtMax_), 2) / std::max(puppiDr2min, dr2); + alphaF[ip] += w; + if (p2.chargedPV) + alphaC[ip] += w; + } + } + if (puppiUsingBareTracks_) { + alphaC[ip] = 0; + for (const PropagatedTrack &p2 : r.track) { + if (!p2.fromPV) + continue; + float dr2 = ::deltaR2(p.floatEta(), p.floatPhi(), p2.floatEta(), p2.floatPhi()); + if (dr2 > 0 && dr2 < puppiDr2) { + alphaC[ip] += std::pow(std::min(p2.floatPt(), puppiPtMax_), 2) / std::max(puppiDr2min, dr2); + } + } + } + } +} + +void PuppiAlgo::computePuppiWeights(Region &r, + const std::vector &alphaC, + const std::vector &alphaF, + float alphaCMed, + float alphaCRms, + float alphaFMed, + float alphaFRms) const { + int16_t ietacut = std::round(etaCharged_ * CaloCluster::ETAPHI_SCALE); + for (unsigned int ip = 0, np = r.pf.size(); ip < np; ++ip) { + PFParticle &p = r.pf[ip]; + // charged + if (p.hwId == l1t::PFCandidate::ChargedHadron || p.hwId == l1t::PFCandidate::Electron || + p.hwId == l1t::PFCandidate::Muon) { + p.setPuppiW(p.chargedPV || p.hwId == l1t::PFCandidate::Muon ? 1.0 : 0); + if (debug_) + dbgPrintf( + "PUPPI \t charged id %1d pt %7.2f eta %+5.2f phi %+5.2f alpha %+7.2f x2 %+7.2f --> puppi weight %.3f " + "puppi pt %7.2f \n", + p.hwId, + p.floatPt(), + p.floatEta(), + p.floatPhi(), + 0., + 0., + p.floatPuppiW(), + p.floatPt() * p.floatPuppiW()); + continue; + } + // neutral + float alpha = -99, x2 = -99; + bool central = std::abs(p.hwEta) < ietacut; + if (r.relativeCoordinates) + central = + (std::abs(r.globalAbsEta(p.floatEta())) < etaCharged_); // FIXME could make a better integer implementation + if (central) { + if (alphaC[ip] > 0) { + alpha = std::log(alphaC[ip]); + x2 = (alpha - alphaCMed) * std::abs(alpha - alphaCMed) / std::pow(alphaCRms, 2); + p.setPuppiW(ROOT::Math::chisquared_cdf(x2, 1)); + } else { + p.setPuppiW(0); + } + } else { + if (alphaF[ip] > 0) { + alpha = std::log(alphaF[ip]); + x2 = (alpha - alphaFMed) * std::abs(alpha - alphaFMed) / std::pow(alphaFRms, 2); + p.setPuppiW(ROOT::Math::chisquared_cdf(x2, 1)); + } else { + p.setPuppiW(0); + } + } + if (debug_) + dbgPrintf( + "PUPPI \t neutral id %1d pt %7.2f eta %+5.2f phi %+5.2f alpha %+7.2f x2 %+7.2f --> puppi weight %.3f " + "puppi pt %7.2f \n", + p.hwId, + p.floatPt(), + p.floatEta(), + p.floatPhi(), + alpha, + x2, + p.floatPuppiW(), + p.floatPt() * p.floatPuppiW()); + } +} + +void PuppiAlgo::computePuppiMedRMS( + const std::vector &rs, float &alphaCMed, float &alphaCRms, float &alphaFMed, float &alphaFRms) const { + std::vector alphaFs; + std::vector alphaCs; + int16_t ietacut = std::round(etaCharged_ * CaloCluster::ETAPHI_SCALE); + float puppiDr2 = std::pow(puppiDr_, 2), puppiDr2min = std::pow(puppiDrMin_, 2); + for (const Region &r : rs) { + for (const PFParticle &p : r.pf) { + bool central = std::abs(p.hwEta) < ietacut; + if (r.relativeCoordinates) + central = (r.globalAbsEta(p.floatEta()) < etaCharged_); // FIXME could make a better integer implementation + if (central) { + if (p.hwId > 1 || p.chargedPV) + continue; + } + float alphaC = 0, alphaF = 0; + for (const PFParticle &p2 : r.pf) { + float dr2 = ::deltaR2(p.floatEta(), p.floatPhi(), p2.floatEta(), p2.floatPhi()); + if (dr2 > 0 && dr2 < puppiDr2) { + float w = std::pow(std::min(p2.floatPt(), puppiPtMax_), 2) / std::max(puppiDr2min, dr2); + alphaF += w; + if (p2.chargedPV) + alphaC += w; + } + } + if (puppiUsingBareTracks_) { + alphaC = 0; + for (const PropagatedTrack &p2 : r.track) { + if (!p2.fromPV) + continue; + float dr2 = ::deltaR2(p.floatEta(), p.floatPhi(), p2.floatEta(), p2.floatPhi()); + if (dr2 > 0 && dr2 < puppiDr2) { + alphaC += std::pow(std::min(p2.floatPt(), puppiPtMax_), 2) / std::max(puppiDr2min, dr2); + } + } + } + if (central) { + if (alphaC > 0) + alphaCs.push_back(std::log(alphaC)); + } else { + if (alphaF > 0) + alphaFs.push_back(std::log(alphaF)); + } + } + } + std::sort(alphaCs.begin(), alphaCs.end()); + std::sort(alphaFs.begin(), alphaFs.end()); + + if (alphaCs.size() > 1) { + alphaCMed = alphaCs[alphaCs.size() / 2 + 1]; + double sum = 0.0; + for (float alpha : alphaCs) + sum += std::pow(alpha - alphaCMed, 2); + alphaCRms = std::sqrt(float(sum) / alphaCs.size()); + } else { + alphaCMed = 8.; + alphaCRms = 8.; + } + + if (alphaFs.size() > 1) { + alphaFMed = alphaFs[alphaFs.size() / 2 + 1]; + double sum = 0.0; + for (float alpha : alphaFs) + sum += std::pow(alpha - alphaFMed, 2); + alphaFRms = std::sqrt(float(sum) / alphaFs.size()); + } else { + alphaFMed = 6.; + alphaFRms = 6.; + } + if (debug_) + dbgPrintf("PUPPI \t alphaC = %+6.2f +- %6.2f (%4lu), alphaF = %+6.2f +- %6.2f (%4lu)\n", + alphaCMed, + alphaCRms, + alphaCs.size(), + alphaFMed, + alphaFRms, + alphaFs.size()); +} + +void PuppiAlgo::fillPuppi(Region &r) const { + uint16_t PUPPIW_0p01 = std::round(0.01 * PFParticle::PUPPI_SCALE); + r.puppi.clear(); + for (PFParticle &p : r.pf) { + if (p.hwId == l1t::PFCandidate::ChargedHadron || p.hwId == l1t::PFCandidate::Electron || + p.hwId == l1t::PFCandidate::Muon) { // charged + if (p.hwPuppiWeight > 0) { + r.puppi.push_back(p); + } + } else { // neutral + if (p.hwPuppiWeight > PUPPIW_0p01) { + // FIXME would work better with PUPPI_SCALE being a power of two, to do the shift + // FIXME done with floats + int16_t hwPt = (float(p.hwPt) * float(p.hwPuppiWeight) / float(PFParticle::PUPPI_SCALE)); + int16_t hwPtCut = 0, hwAbsEta = r.relativeCoordinates + ? round(r.globalAbsEta(p.floatEta()) * CaloCluster::ETAPHI_SCALE) + : std::abs(p.hwEta); + for (unsigned int ietaBin = 0, nBins = intPuppiEtaCuts_.size(); ietaBin < nBins; ++ietaBin) { + if (hwAbsEta < intPuppiEtaCuts_[ietaBin]) { + hwPtCut = (p.hwId == l1t::PFCandidate::Photon ? intPuppiPtCutsPhotons_[ietaBin] : intPuppiPtCuts_[ietaBin]); + break; + } + } + if (hwPt > hwPtCut) { + r.puppi.push_back(p); + r.puppi.back().hwPt = hwPt; + } + } + } + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/Region.cc b/L1Trigger/Phase2L1ParticleFlow/src/Region.cc new file mode 100644 index 0000000000000..116b95410c94d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/Region.cc @@ -0,0 +1,130 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/Region.h" +#include "DataFormats/L1TParticleFlow/interface/PFCandidate.h" +#include +#include + +const char *l1tpf_impl::Region::inputTypeName(int type) { + switch (InputType(type)) { + case calo_type: + return "Calo"; + case emcalo_type: + return "EmCalo"; + case track_type: + return "TK"; + case l1mu_type: + return "Mu"; + case n_input_types: + throw cms::Exception( + "LogicError", "n_input_types is not a type to be used, but only a compile-time const for iterating on types"); + } + return "NO_SUCH_INPUT_TYPE"; +} +const char *l1tpf_impl::Region::outputTypeName(int type) { + switch (OutputType(type)) { + case any_type: + return ""; + case charged_type: + return "Charged"; + case neutral_type: + return "Neutral"; + case electron_type: + return "Electron"; + case pfmuon_type: + return "Muon"; + case charged_hadron_type: + return "ChargedHadron"; + case neutral_hadron_type: + return "NeutralHadron"; + case photon_type: + return "Photon"; + case n_output_types: + throw cms::Exception( + "LogicError", + "n_output_types is not a type to be used, but only a compile-time const for iterating on types"); + } + return "NO_SUCH_OUTPUT_TYPE"; +} + +unsigned int l1tpf_impl::Region::nInput(InputType type) const { + switch (type) { + case calo_type: + return calo.size(); + case emcalo_type: + return emcalo.size(); + case track_type: + return track.size(); + case l1mu_type: + return muon.size(); + case n_input_types: + throw cms::Exception( + "LogicError", "n_input_types is not a type to be used, but only a compile-time const for iterating on types"); + } + return 9999; +} + +unsigned int l1tpf_impl::Region::nOutput(OutputType type, bool usePuppi, bool fiducial) const { + unsigned int ret = 0; + for (const auto &p : (usePuppi ? puppi : pf)) { + if (p.hwPt <= 0) + continue; + if (fiducial && !fiducialLocal(p.floatEta(), p.floatPhi())) + continue; + switch (type) { + case any_type: + ret++; + break; + case charged_type: + if (p.intCharge() != 0) + ret++; + break; + case neutral_type: + if (p.intCharge() == 0) + ret++; + break; + case electron_type: + if (p.hwId == l1t::PFCandidate::Electron) + ret++; + break; + case pfmuon_type: + if (p.hwId == l1t::PFCandidate::Muon) + ret++; + break; + case charged_hadron_type: + if (p.hwId == l1t::PFCandidate::ChargedHadron) + ret++; + break; + case neutral_hadron_type: + if (p.hwId == l1t::PFCandidate::NeutralHadron) + ret++; + break; + case photon_type: + if (p.hwId == l1t::PFCandidate::Photon) + ret++; + break; + case n_output_types: + throw cms::Exception( + "LogicError", + "n_output_types is not a type to be used, but only a compile-time const for iterating on types"); + } + } + return ret; +} + +void l1tpf_impl::Region::inputSort() { + std::sort(calo.begin(), calo.end()); + std::sort(emcalo.begin(), emcalo.end()); + std::sort(track.begin(), track.end()); + std::sort(muon.begin(), muon.end()); + if (ncaloMax > 0 && calo.size() > ncaloMax) { + caloOverflow = calo.size() - ncaloMax; + calo.resize(ncaloMax); + } + if (nemcaloMax > 0 && emcalo.size() > nemcaloMax) { + emcaloOverflow = emcalo.size() - nemcaloMax; + emcalo.resize(nemcaloMax); + } + if (ntrackMax > 0 && track.size() > ntrackMax) { + trackOverflow = track.size() - ntrackMax; + track.resize(ntrackMax); + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/RegionMapper.cc b/L1Trigger/Phase2L1ParticleFlow/src/RegionMapper.cc new file mode 100644 index 0000000000000..d98f6b1b171cb --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/RegionMapper.cc @@ -0,0 +1,341 @@ +#include "L1Trigger/Phase2L1ParticleFlow/interface/RegionMapper.h" + +using namespace l1tpf_impl; + +RegionMapper::RegionMapper(const edm::ParameterSet &iConfig) : useRelativeRegionalCoordinates_(false) { + if (iConfig.existsAs>("regions")) { + useRelativeRegionalCoordinates_ = iConfig.getParameter("useRelativeRegionalCoordinates"); + for (const edm::ParameterSet &preg : iConfig.getParameter>("regions")) { + std::vector etaBoundaries = preg.getParameter>("etaBoundaries"); + unsigned int phiSlices = preg.getParameter("phiSlices"); + float etaExtra = preg.getParameter("etaExtra"); + float phiExtra = preg.getParameter("phiExtra"); + float phiWidth = 2 * M_PI / phiSlices; + unsigned int ncalomax = 0, nemcalomax = 0, ntrackmax = 0, nmuonmax = 0, npfmax = 0, npuppimax = 0; + if (preg.existsAs("caloNMax")) + ncalomax = preg.getParameter("caloNMax"); + if (preg.existsAs("emcaloNMax")) + nemcalomax = preg.getParameter("emcaloNMax"); + if (preg.existsAs("trackNMax")) + ntrackmax = preg.getParameter("trackNMax"); + if (preg.existsAs("muonNMax")) + nmuonmax = preg.getParameter("muonNMax"); + if (preg.existsAs("pfNMax")) + npfmax = preg.getParameter("pfNMax"); + if (preg.existsAs("puppiNMax")) + npuppimax = preg.getParameter("puppiNMax"); + for (unsigned int ieta = 0, neta = etaBoundaries.size() - 1; ieta < neta; ++ieta) { + for (unsigned int iphi = 0; iphi < phiSlices; ++iphi) { + float phiCenter = (iphi + 0.5) * phiWidth - M_PI; + regions_.push_back(Region(etaBoundaries[ieta], + etaBoundaries[ieta + 1], + phiCenter, + phiWidth, + etaExtra, + phiExtra, + useRelativeRegionalCoordinates_, + ncalomax, + nemcalomax, + ntrackmax, + nmuonmax, + npfmax, + npuppimax)); + } + } + } + std::string trackRegionMode = "TrackAssoMode::any"; + if (iConfig.existsAs("trackRegionMode")) + trackRegionMode = iConfig.getParameter("trackRegionMode"); + if (trackRegionMode == "atVertex") + trackRegionMode_ = TrackAssoMode::atVertex; + else if (trackRegionMode == "atCalo") + trackRegionMode_ = TrackAssoMode::atCalo; + else if (trackRegionMode == "any") + trackRegionMode_ = TrackAssoMode::any; + else + throw cms::Exception( + "Configuration", + "Unsupported value for trackRegionMode: " + trackRegionMode + " (allowed are 'atVertex', 'atCalo', 'any')"); + std::cout << "L1 RegionMapper: made " << regions_.size() << " regions" << std::endl; + } else { + // start off with a dummy region + unsigned int ncalomax = 0, nemcalomax = 0, ntrackmax = 0, nmuonmax = 0, npfmax = 0, npuppimax = 0; + regions_.emplace_back(-5.5, + 5.5, + 0, + 2 * M_PI, + 0.5, + 0.5, + useRelativeRegionalCoordinates_, + ncalomax, + nemcalomax, + ntrackmax, + nmuonmax, + npfmax, + npuppimax); + } +} + +void RegionMapper::clear() { + for (Region &r : regions_) + r.zero(); + clusterRefMap_.clear(); + trackRefMap_.clear(); + muonRefMap_.clear(); +} + +void RegionMapper::addTrack(const l1t::PFTrack &t) { + // now let's be optimistic and make things very simple + // we propagate in floating point the track to the calo + // we add the track to the region corresponding to its vertex (eta,phi) coordinates AND its (eta,phi) calo coordinates + for (Region &r : regions_) { + bool inside = true; + switch (trackRegionMode_) { + case TrackAssoMode::atVertex: + inside = r.contains(t.eta(), t.phi()); + break; + case TrackAssoMode::atCalo: + inside = r.contains(t.caloEta(), t.caloPhi()); + break; + case TrackAssoMode::any: + inside = r.contains(t.eta(), t.phi()) || r.contains(t.caloEta(), t.caloPhi()); + break; + } + if (inside) { + PropagatedTrack prop; + prop.fillInput(t.pt(), r.localEta(t.eta()), r.localPhi(t.phi()), t.charge(), t.vertex().Z(), t.quality(), &t); + prop.fillPropagated(t.pt(), + t.trkPtError(), + t.caloPtError(), + r.localEta(t.caloEta()), + r.localPhi(t.caloPhi()), + t.quality(), + t.isMuon()); + prop.hwStubs = t.nStubs(); + prop.hwChi2 = round(t.chi2() * 10); + r.track.push_back(prop); + } + } +} +void RegionMapper::addTrack(const l1t::PFTrack &t, l1t::PFTrackRef ref) { + addTrack(t); + trackRefMap_[&t] = ref; +} + +void RegionMapper::addMuon(const l1t::Muon &mu) { + // now let's be optimistic and make things very simple + // we don't propagate anything + for (Region &r : regions_) { + if (r.contains(mu.eta(), mu.phi())) { + Muon prop; + prop.fill(mu.pt(), r.localEta(mu.eta()), r.localPhi(mu.phi()), mu.charge(), mu.hwQual(), &mu); + r.muon.push_back(prop); + } + } +} + +void RegionMapper::addMuon(const l1t::TkMuon &mu) { + // now let's be optimistic and make things very simple + // we don't propagate anything + for (Region &r : regions_) { + if (r.contains(mu.eta(), mu.phi())) { + Muon prop; + prop.fill(mu.pt(), r.localEta(mu.eta()), r.localPhi(mu.phi()), mu.charge(), mu.hwQual()); + r.muon.push_back(prop); + } + } +} + +void RegionMapper::addMuon(const l1t::Muon &mu, l1t::PFCandidate::MuonRef ref) { + addMuon(mu); + muonRefMap_[&mu] = ref; +} + +void RegionMapper::addCalo(const l1t::PFCluster &p) { + if (p.pt() == 0) + return; + for (Region &r : regions_) { + if (r.contains(p.eta(), p.phi())) { + CaloCluster calo; + calo.fill(p.pt(), p.emEt(), p.ptError(), r.localEta(p.eta()), r.localPhi(p.phi()), p.isEM(), 0, &p); + r.calo.push_back(calo); + } + } +} +void RegionMapper::addCalo(const l1t::PFCluster &p, l1t::PFClusterRef ref) { + addCalo(p); + clusterRefMap_[&p] = ref; +} + +void RegionMapper::addEmCalo(const l1t::PFCluster &p) { + if (p.pt() == 0) + return; + for (Region &r : regions_) { + if (r.contains(p.eta(), p.phi())) { + CaloCluster calo; + calo.fill(p.pt(), p.emEt(), p.ptError(), r.localEta(p.eta()), r.localPhi(p.phi()), p.isEM(), 0, &p); + r.emcalo.push_back(calo); + } + } +} +void RegionMapper::addEmCalo(const l1t::PFCluster &p, l1t::PFClusterRef ref) { + addEmCalo(p); + clusterRefMap_[&p] = ref; +} + +std::unique_ptr RegionMapper::fetch(bool puppi, float ptMin) const { + auto ret = std::make_unique(); + for (const Region &r : regions_) { + for (const PFParticle &p : (puppi ? r.puppi : r.pf)) { + bool inside = true; + switch (trackRegionMode_) { + case TrackAssoMode::atVertex: + inside = r.fiducialLocal(p.floatVtxEta(), p.floatVtxPhi()); + break; + case TrackAssoMode::atCalo: + inside = r.fiducialLocal(p.floatEta(), p.floatPhi()); + break; + case TrackAssoMode::any: + inside = r.fiducialLocal(p.floatVtxEta(), p.floatVtxPhi()); + break; // WARNING: this may not be the best choice + } + if (!inside) + continue; + if (p.floatPt() > ptMin) { + reco::Particle::PolarLorentzVector p4( + p.floatPt(), r.globalEta(p.floatVtxEta()), r.globalPhi(p.floatVtxPhi()), 0.13f); + ret->emplace_back(l1t::PFCandidate::ParticleType(p.hwId), p.intCharge(), p4, p.floatPuppiW()); + ret->back().setVertex(reco::Particle::Point(0, 0, p.floatDZ())); + ret->back().setStatus(p.hwStatus); + if (p.cluster.src) { + auto match = clusterRefMap_.find(p.cluster.src); + if (match == clusterRefMap_.end()) { + throw cms::Exception("CorruptData") << "Invalid cluster pointer in PF candidate id " << p.hwId << " pt " + << p4.pt() << " eta " << p4.eta() << " phi " << p4.phi(); + } + ret->back().setPFCluster(match->second); + } + if (p.track.src) { + auto match = trackRefMap_.find(p.track.src); + if (match == trackRefMap_.end()) { + throw cms::Exception("CorruptData") << "Invalid track pointer in PF candidate id " << p.hwId << " pt " + << p4.pt() << " eta " << p4.eta() << " phi " << p4.phi(); + } + ret->back().setPFTrack(match->second); + } + if (p.muonsrc) { + auto match = muonRefMap_.find(p.muonsrc); + if (match == muonRefMap_.end()) { + throw cms::Exception("CorruptData") << "Invalid muon pointer in PF candidate id " << p.hwId << " pt " + << p4.pt() << " eta " << p4.eta() << " phi " << p4.phi(); + } + ret->back().setMuon(match->second); + } + } + } + } + return ret; +} + +std::unique_ptr RegionMapper::fetchCalo(float ptMin, bool emcalo) const { + auto ret = std::make_unique(); + for (const Region &r : regions_) { + for (const CaloCluster &p : (emcalo ? r.emcalo : r.calo)) { + if (!r.fiducialLocal(p.floatEta(), p.floatPhi())) + continue; + if (p.floatPt() > ptMin) { + reco::Particle::PolarLorentzVector p4(p.floatPt(), r.globalEta(p.floatEta()), r.globalPhi(p.floatPhi()), 0.13f); + l1t::PFCandidate::ParticleType kind = + (p.isEM || emcalo) ? l1t::PFCandidate::Photon : l1t::PFCandidate::NeutralHadron; + ret->emplace_back(kind, 0, p4); + if (p.src) { + auto match = clusterRefMap_.find(p.src); + if (match == clusterRefMap_.end()) { + throw cms::Exception("CorruptData") + << "Invalid cluster pointer in cluster pt " << p4.pt() << " eta " << p4.eta() << " phi " << p4.phi(); + } + ret->back().setPFCluster(match->second); + } + } + } + } + return ret; +} + +std::unique_ptr RegionMapper::fetchTracks(float ptMin, bool fromPV) const { + auto ret = std::make_unique(); + for (const Region &r : regions_) { + for (const PropagatedTrack &p : r.track) { + if (fromPV && !p.fromPV) + continue; + bool inside = true; + switch (trackRegionMode_) { + case TrackAssoMode::atVertex: + inside = r.fiducialLocal(p.floatVtxEta(), p.floatVtxPhi()); + break; + case TrackAssoMode::atCalo: + inside = r.fiducialLocal(p.floatEta(), p.floatPhi()); + break; + case TrackAssoMode::any: + inside = r.fiducialLocal(p.floatVtxEta(), p.floatVtxPhi()); + break; // WARNING: this may not be the best choice + } + if (!inside) + continue; + if (p.floatPt() > ptMin) { + reco::Particle::PolarLorentzVector p4( + p.floatVtxPt(), r.globalEta(p.floatVtxEta()), r.globalPhi(p.floatVtxPhi()), 0.13f); + l1t::PFCandidate::ParticleType kind = p.muonLink ? l1t::PFCandidate::Muon : l1t::PFCandidate::ChargedHadron; + ret->emplace_back(kind, p.intCharge(), p4); + ret->back().setVertex(reco::Particle::Point(0, 0, p.floatDZ())); + if (p.src) { + auto match = trackRefMap_.find(p.src); + if (match == trackRefMap_.end()) { + throw cms::Exception("CorruptData") + << "Invalid track pointer in PF track pt " << p4.pt() << " eta " << p4.eta() << " phi " << p4.phi(); + } + ret->back().setPFTrack(match->second); + } + } + } + } + return ret; +} + +std::pair RegionMapper::totAndMaxInput(int type) const { + unsigned ntot = 0, nmax = 0; + for (const auto &r : regions_) { + unsigned int ni = r.nInput(Region::InputType(type)); + ntot += ni; + nmax = std::max(nmax, ni); + } + return std::make_pair(ntot, nmax); +} + +std::unique_ptr> RegionMapper::vecInput(int type) const { + auto v = std::make_unique>(); + for (const auto &r : regions_) { + unsigned ni = r.nInput(Region::InputType(type)); + v->push_back(ni); + } + return v; +} + +std::pair RegionMapper::totAndMaxOutput(int type, bool puppi) const { + unsigned ntot = 0, nmax = 0; + for (const auto &r : regions_) { + unsigned int ni = r.nOutput(Region::OutputType(type), puppi); + ntot += ni; + nmax = std::max(nmax, ni); + } + return std::make_pair(ntot, nmax); +} + +std::unique_ptr> RegionMapper::vecOutput(int type, bool puppi) const { + auto v = std::make_unique>(); + for (const auto &r : regions_) { + unsigned ni = r.nOutput(Region::OutputType(type), puppi); + v->push_back(ni); + } + return v; +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/corrector.cc b/L1Trigger/Phase2L1ParticleFlow/src/corrector.cc new file mode 100644 index 0000000000000..d8c04b82690bd --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/corrector.cc @@ -0,0 +1,188 @@ +#include "L1Trigger/Phase2L1ParticleFlow/src/corrector.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "FWCore/Utilities/interface/CPUTimer.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "FWCore/ParameterSet/interface/FileInPath.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "DataFormats/L1TParticleFlow/interface/PFCluster.h" + +l1tpf::corrector::corrector(const std::string &filename, float emfMax, bool debug) : emfMax_(emfMax) { + if (!filename.empty()) + init_(filename, "", debug); +} +l1tpf::corrector::corrector(const std::string &filename, const std::string &directory, float emfMax, bool debug) + : emfMax_(emfMax) { + if (!filename.empty()) + init_(filename, directory, debug); +} + +l1tpf::corrector::corrector(TDirectory *src, float emfMax, bool debug) : emfMax_(emfMax) { init_(src, debug); } + +void l1tpf::corrector::init_(const std::string &filename, const std::string &directory, bool debug) { + std::string resolvedFileName = filename; + if (filename[0] != '/') + resolvedFileName = edm::FileInPath(filename).fullPath(); + TFile *lFile = TFile::Open(resolvedFileName.c_str()); + if (!lFile || lFile->IsZombie()) + throw cms::Exception("Configuration", "cannot read file " + filename); + + TDirectory *dir = directory.empty() ? lFile : lFile->GetDirectory(directory.c_str()); + if (!dir) + throw cms::Exception("Configuration", "cannot find directory '" + directory + "' in file " + filename); + init_(dir, debug); + + lFile->Close(); +} + +void l1tpf::corrector::init_(TDirectory *lFile, bool debug) { + TH1 *index = (TH1 *)lFile->Get("INDEX"); + if (!index) + throw cms::Exception("Configuration") + << "invalid input file " << lFile->GetPath() << ": INDEX histogram not found.\n"; + index_.reset((TH1 *)index->Clone()); + index_->SetDirectory(nullptr); + + is2d_ = index_->InheritsFrom("TH2"); + + std::unordered_map graphs; + TKey *key; + TIter nextkey(lFile->GetListOfKeys()); + while ((key = (TKey *)nextkey())) { + if (strncmp(key->GetName(), "eta_", 4) == 0) { + TGraph *gr = (TGraph *)key->ReadObj(); + if (!gr->TestBit(TGraph::kIsSortedX)) + gr->Sort(); + graphs[key->GetName()] = gr; + } + } + + neta_ = index_->GetNbinsX(); + nemf_ = (is2d_ ? index_->GetNbinsY() : 1); + corrections_.resize(neta_ * nemf_); + std::fill(corrections_.begin(), corrections_.end(), nullptr); + char buff[32]; + int ngraphs = 0; + for (unsigned int ieta = 0; ieta < neta_; ++ieta) { + for (unsigned int iemf = 0; iemf < nemf_; ++iemf) { + if (is2d_) { + snprintf(buff, 31, "eta_bin%d_emf_bin%d", ieta + 1, iemf + 1); + } else { + snprintf(buff, 31, "eta_bin%d", ieta + 1); + } + TGraph *graph = graphs[buff]; + if (debug) + edm::LogPrint("corrector") << " eta bin " << ieta << " emf bin " << iemf << " graph " << buff + << (graph ? " " : " ") << "\n"; + if (graph) { + ngraphs++; + corrections_[ieta * nemf_ + iemf] = (TGraph *)graph->Clone(); + } + if (std::abs(index_->GetXaxis()->GetBinCenter(ieta + 1)) > 3.0) { + break; // no EMF bins beyond eta = 3 + } + } + } +} + +l1tpf::corrector::corrector(const TH1 *index, float emfMax) + : index_((TH1 *)index->Clone("INDEX")), + is2d_(index->InheritsFrom("TH2")), + neta_(index->GetNbinsX()), + nemf_(is2d_ ? index->GetNbinsY() : 1), + emfMax_(emfMax) { + index_->SetDirectory(nullptr); + corrections_.resize(neta_ * nemf_); + std::fill(corrections_.begin(), corrections_.end(), nullptr); +} + +l1tpf::corrector::~corrector() { + for (TGraph *&p : corrections_) { + delete p; + p = nullptr; + } + corrections_.clear(); +} + +l1tpf::corrector::corrector(corrector &&corr) + : index_(std::move(corr.index_)), + corrections_(std::move(corr.corrections_)), + is2d_(corr.is2d_), + neta_(corr.neta_), + nemf_(corr.nemf_), + emfMax_(corr.emfMax_) {} + +l1tpf::corrector &l1tpf::corrector::operator=(corrector &&corr) { + std::swap(is2d_, corr.is2d_); + std::swap(neta_, corr.neta_); + std::swap(nemf_, corr.nemf_); + std::swap(emfMax_, corr.emfMax_); + + index_.swap(corr.index_); + corrections_.swap(corr.corrections_); + return *this; +} + +float l1tpf::corrector::correctedPt(float pt, float emPt, float eta) const { + float total = std::max(pt, emPt), abseta = std::abs(eta); + float emf = emPt / total; + if (emfMax_ > 0 && emf > emfMax_) + return total; // no correction + unsigned int ieta = std::min(std::max(1, index_->GetXaxis()->FindBin(abseta)), neta_) - 1; + unsigned int iemf = + is2d_ && abseta < 3.0 ? std::min(std::max(1, index_->GetYaxis()->FindBin(emf)), nemf_) - 1 : 0; + TGraph *graph = corrections_[ieta * nemf_ + iemf]; + if (!graph) { + throw cms::Exception("RuntimeError") << "Error trying to read calibration for eta " << eta << " emf " << emf + << " which are not available." << std::endl; + } + float ptcorr = std::min(graph->Eval(total), 4 * total); + return ptcorr; +} + +void l1tpf::corrector::correctPt(l1t::PFCluster &cluster, float preserveEmEt) const { + float newpt = correctedPt(cluster.pt(), cluster.emEt(), cluster.eta()); + cluster.calibratePt(newpt, preserveEmEt); +} + +void l1tpf::corrector::setGraph(const TGraph &graph, int ieta, int iemf) { + char buff[32]; + if (is2d_) { + snprintf(buff, 31, "eta_bin%d_emf_bin%d", (unsigned int)(ieta + 1), (unsigned int)(iemf + 1)); + } else { + snprintf(buff, 31, "eta_bin%d", (unsigned int)(ieta + 1)); + } + TGraph *gclone = (TGraph *)graph.Clone(buff); + delete corrections_[ieta * nemf_ + iemf]; + corrections_[ieta * nemf_ + iemf] = gclone; +} + +void l1tpf::corrector::writeToFile(const std::string &filename, const std::string &directory) const { + TFile *lFile = TFile::Open(filename.c_str(), "RECREATE"); + TDirectory *dir = directory.empty() ? lFile : lFile->mkdir(directory.c_str()); + writeToFile(dir); + lFile->Close(); +} + +void l1tpf::corrector::writeToFile(TDirectory *dest) const { + TH1 *index = (TH1 *)index_->Clone(); + index->SetDirectory(dest); + dest->WriteTObject(index); + + for (const TGraph *p : corrections_) { + if (p != nullptr) { + dest->WriteTObject((TGraph *)p->Clone(), p->GetName()); + } + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/corrector.h b/L1Trigger/Phase2L1ParticleFlow/src/corrector.h new file mode 100644 index 0000000000000..3ade23a229eb0 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/corrector.h @@ -0,0 +1,65 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_corrector_h +#define L1Trigger_Phase2L1ParticleFlow_corrector_h +#include +#include +#include +#include + +class TDirectory; + +namespace l1t { + class PFCluster; +} + +namespace l1tpf { + class corrector { + public: + corrector() : is2d_(false), neta_(0), nemf_(0), emfMax_(-1) {} + corrector(const std::string &iFile, float emfMax = -1, bool debug = false); + corrector(const std::string &iFile, const std::string &directory, float emfMax = -1, bool debug = false); + corrector(TDirectory *src, float emfMax = -1, bool debug = false); + // create an empty corrector (you'll need to fill the graphs later) + corrector(const TH1 *index, float emfMax = -1); + ~corrector(); + + // no copy, but can move + corrector(const corrector &corr) = delete; + corrector &operator=(const corrector &corr) = delete; + corrector(corrector &&corr); + corrector &operator=(corrector &&corr); + + float correctedPt(float et, float emEt, float eta) const; + float correctedPt(float et, float eta) const { return correctedPt(et, 0, eta); } + void correctPt(l1t::PFCluster &cluster, float preserveEmEt = true) const; + + bool valid() const { return (index_.get() != nullptr); } + + // set the graph (note: it is cloned, and the corrector owns the clone) + void setGraph(const TGraph &graph, int ieta, int iemf = 0); + + bool is2d() const { return is2d_; } + unsigned int neta() const { return neta_; } + unsigned int nemf() const { return nemf_; } + // access the index histogram + const TH1 &getIndex() const { return *index_; } + // access the graphs (owned by the corrector, may be null) + TGraph *getGraph(int ieta, int iemf = 0) { return corrections_[ieta * nemf_ + iemf]; } + const TGraph *getGraph(int ieta, int iemf = 0) const { return corrections_[ieta * nemf_ + iemf]; } + + // store the corrector + void writeToFile(const std::string &filename, const std::string &directory) const; + // store the corrector + void writeToFile(TDirectory *dest) const; + + private: + std::unique_ptr index_; + std::vector corrections_; + bool is2d_; + unsigned int neta_, nemf_; + float emfMax_; + + void init_(const std::string &iFile, const std::string &directory, bool debug); + void init_(TDirectory *src, bool debug); + }; +} // namespace l1tpf +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h b/L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h new file mode 100644 index 0000000000000..35dc25afa32d3 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/dbgPrintf.h @@ -0,0 +1,11 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_dbgPrintf_h +#define L1Trigger_Phase2L1ParticleFlow_dbgPrintf_h + +template +inline void dbgPrintf(const char *formatString, Args &&... args) { +#ifdef L1PF_DEBUG + printf(formatString, std::forward(args)...); +#endif +} + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/firmware/data.h b/L1Trigger/Phase2L1ParticleFlow/src/firmware/data.h new file mode 100644 index 0000000000000..c1ccd93ac5812 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/firmware/data.h @@ -0,0 +1,209 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_FIRMWARE_DATA_H +#define L1Trigger_Phase2L1ParticleFlow_FIRMWARE_DATA_H + +#include + +typedef ap_int<16> pt_t; +typedef ap_int<10> etaphi_t; +typedef ap_int<5> vtx_t; +typedef ap_uint<3> particleid_t; +typedef ap_int<10> z0_t; // 40cm / 0.1 +typedef ap_uint<14> tk2em_dr_t; +typedef ap_uint<14> tk2calo_dr_t; +typedef ap_uint<10> em2calo_dr_t; +typedef ap_uint<13> tk2calo_dq_t; + +enum PID { PID_Charged = 0, PID_Neutral = 1, PID_Photon = 2, PID_Electron = 3, PID_Muon = 4 }; + +// DEFINE MULTIPLICITIES +#if defined(REG_HGCal) +#define NTRACK 25 +#define NCALO 20 +#define NMU 4 +#define NSELCALO 15 +#define NALLNEUTRALS NSELCALO +// dummy +#define NEMCALO 1 +#define NPHOTON NEMCALO +// not used but must be there because used in header files +#define NNEUTRALS 1 +//-------------------------------- +#elif defined(REG_HGCalNoTK) +#define NCALO 12 +#define NNEUTRALS 8 +#define NALLNEUTRALS NCALO +// dummy +#define NMU 1 +#define NTRACK 1 +#define NEMCALO 1 +#define NPHOTON NEMCALO +#define NSELCALO 1 +//-------------------------------- +#elif defined(REG_HF) +#define NCALO 18 +#define NNEUTRALS 10 +#define NALLNEUTRALS NCALO +// dummy +#define NMU 1 +#define NTRACK 1 +#define NEMCALO 1 +#define NPHOTON NEMCALO +#define NSELCALO 1 +//-------------------------------- +#else // BARREL +#ifndef REG_Barrel +#ifndef CMSSW_GIT_HASH +#warning "No region defined, assuming it's barrel (#define REG_Barrel to suppress this)" +#endif +#endif +#if defined(BOARD_MP7) +#warning "MP7 NOT SUPPORTED ANYMORE" +#define NTRACK 14 +#define NCALO 10 +#define NMU 2 +#define NEMCALO 10 +#define NPHOTON NEMCALO +#define NSELCALO 10 +#define NALLNEUTRALS (NPHOTON + NSELCALO) +#define NNEUTRALS 15 +#elif defined(BOARD_CTP7) +#error "NOT SUPPORTED ANYMORE" +#elif defined(BOARD_KU15P) +#define NTRACK 14 +#define NCALO 10 +#define NMU 2 +#define NEMCALO 10 +#define NPHOTON NEMCALO +#define NSELCALO 10 +#define NALLNEUTRALS (NPHOTON + NSELCALO) +#define NNEUTRALS 15 +#elif defined(BOARD_VCU118) +#define NTRACK 22 +#define NCALO 15 +#define NEMCALO 13 +#define NMU 2 +#define NPHOTON NEMCALO +#define NSELCALO 10 +#define NALLNEUTRALS (NPHOTON + NSELCALO) +#define NNEUTRALS 25 +#else +#define NTRACK 22 +#define NCALO 15 +#define NEMCALO 13 +#define NMU 2 +#define NPHOTON NEMCALO +#define NSELCALO 10 +#define NALLNEUTRALS (NPHOTON + NSELCALO) +#define NNEUTRALS 25 +#endif + +#endif // region + +#if defined(BOARD_MP7) +#define PACKING_DATA_SIZE 32 +#define PACKING_NCHANN 72 +#elif defined(BOARD_KU15P) +#define PACKING_DATA_SIZE 64 +#define PACKING_NCHANN 42 +#elif defined(BOARD_VCU118) +#define PACKING_DATA_SIZE 64 +#define PACKING_NCHANN 96 +#elif defined(BOARD_APD1) +#define PACKING_DATA_SIZE 64 +#define PACKING_NCHANN 96 +#endif + +struct CaloObj { + pt_t hwPt; + etaphi_t hwEta, hwPhi; // relative to the region center, at calo +}; +struct HadCaloObj : public CaloObj { + pt_t hwEmPt; + bool hwIsEM; +}; +inline void clear(HadCaloObj& c) { + c.hwPt = 0; + c.hwEta = 0; + c.hwPhi = 0; + c.hwEmPt = 0; + c.hwIsEM = false; +} + +struct EmCaloObj { + pt_t hwPt, hwPtErr; + etaphi_t hwEta, hwPhi; // relative to the region center, at calo +}; +inline void clear(EmCaloObj& c) { + c.hwPt = 0; + c.hwPtErr = 0; + c.hwEta = 0; + c.hwPhi = 0; +} + +struct TkObj { + pt_t hwPt, hwPtErr; + etaphi_t hwEta, hwPhi; // relative to the region center, at calo + z0_t hwZ0; + bool hwTightQuality; +}; +inline void clear(TkObj& c) { + c.hwPt = 0; + c.hwPtErr = 0; + c.hwEta = 0; + c.hwPhi = 0; + c.hwZ0 = 0; + c.hwTightQuality = false; +} + +struct MuObj { + pt_t hwPt, hwPtErr; + etaphi_t hwEta, hwPhi; // relative to the region center, at vtx(?) +}; +inline void clear(MuObj& c) { + c.hwPt = 0; + c.hwPtErr = 0; + c.hwEta = 0; + c.hwPhi = 0; +} + +struct PFChargedObj { + pt_t hwPt; + etaphi_t hwEta, hwPhi; // relative to the region center, at calo + particleid_t hwId; + z0_t hwZ0; +}; +inline void clear(PFChargedObj& c) { + c.hwPt = 0; + c.hwEta = 0; + c.hwPhi = 0; + c.hwId = 0; + c.hwZ0 = 0; +} + +struct PFNeutralObj { + pt_t hwPt; + etaphi_t hwEta, hwPhi; // relative to the region center, at calo + particleid_t hwId; + pt_t hwPtPuppi; +}; +inline void clear(PFNeutralObj& c) { + c.hwPt = 0; + c.hwEta = 0; + c.hwPhi = 0; + c.hwId = 0; + c.hwPtPuppi = 0; +} + +//TMUX +#define NETA_TMUX 2 +#define NPHI_TMUX 1 +/* #define TMUX_IN 36 */ +/* #define TMUX_OUT 18 */ +#define TMUX_IN 18 +#define TMUX_OUT 6 +#define NTRACK_TMUX (NTRACK * TMUX_OUT * NETA_TMUX * NPHI_TMUX) +#define NCALO_TMUX (NCALO * TMUX_OUT * NETA_TMUX * NPHI_TMUX) +#define NEMCALO_TMUX (NEMCALO * TMUX_OUT * NETA_TMUX * NPHI_TMUX) +#define NMU_TMUX (NMU * TMUX_OUT * NETA_TMUX * NPHI_TMUX) + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo2hgc.h b/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo2hgc.h new file mode 100644 index 0000000000000..3d36fa37c2b0c --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo2hgc.h @@ -0,0 +1,40 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_FIRMWARE_PFALGO2HGC_H +#define L1Trigger_Phase2L1ParticleFlow_FIRMWARE_PFALGO2HGC_H + +#include "pfalgo_common.h" + +void pfalgo2hgc(const HadCaloObj calo[NCALO], + const TkObj track[NTRACK], + const MuObj mu[NMU], + PFChargedObj outch[NTRACK], + PFNeutralObj outne[NSELCALO], + PFChargedObj outmu[NMU]); + +#if defined(PACKING_DATA_SIZE) && defined(PACKING_NCHANN) +void packed_pfalgo2hgc(const ap_uint input[PACKING_NCHANN], + ap_uint output[PACKING_NCHANN]); +void pfalgo2hgc_pack_in(const HadCaloObj calo[NCALO], + const TkObj track[NTRACK], + const MuObj mu[NMU], + ap_uint input[PACKING_NCHANN]); +void pfalgo2hgc_unpack_in(const ap_uint input[PACKING_NCHANN], + HadCaloObj calo[NCALO], + TkObj track[NTRACK], + MuObj mu[NMU]); +void pfalgo2hgc_pack_out(const PFChargedObj outch[NTRACK], + const PFNeutralObj outne[NSELCALO], + const PFChargedObj outmu[NMU], + ap_uint output[PACKING_NCHANN]); +void pfalgo2hgc_unpack_out(const ap_uint output[PACKING_NCHANN], + PFChargedObj outch[NTRACK], + PFNeutralObj outne[NSELCALO], + PFChargedObj outmu[NMU]); +#endif + +#ifndef CMSSW_GIT_HASH +#define PFALGO_DR2MAX_TK_CALO 525 +#define PFALGO_TK_MAXINVPT_LOOSE 40 +#define PFALGO_TK_MAXINVPT_TIGHT 80 +#endif + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo3.h b/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo3.h new file mode 100644 index 0000000000000..cc7c48bff14d4 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo3.h @@ -0,0 +1,50 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_FIRMWARE_PFALGO3_H +#define L1Trigger_Phase2L1ParticleFlow_FIRMWARE_PFALGO3_H + +#include "pfalgo_common.h" + +void pfalgo3(const EmCaloObj emcalo[NEMCALO], + const HadCaloObj hadcalo[NCALO], + const TkObj track[NTRACK], + const MuObj mu[NMU], + PFChargedObj outch[NTRACK], + PFNeutralObj outpho[NPHOTON], + PFNeutralObj outne[NSELCALO], + PFChargedObj outmu[NMU]); + +#if defined(PACKING_DATA_SIZE) && defined(PACKING_NCHANN) +void packed_pfalgo3(const ap_uint input[PACKING_NCHANN], + ap_uint output[PACKING_NCHANN]); +void pfalgo3_pack_in(const EmCaloObj emcalo[NEMCALO], + const HadCaloObj hadcalo[NCALO], + const TkObj track[NTRACK], + const MuObj mu[NMU], + ap_uint input[PACKING_NCHANN]); +void pfalgo3_unpack_in(const ap_uint input[PACKING_NCHANN], + EmCaloObj emcalo[NEMCALO], + HadCaloObj hadcalo[NCALO], + TkObj track[NTRACK], + MuObj mu[NMU]); +void pfalgo3_pack_out(const PFChargedObj outch[NTRACK], + const PFNeutralObj outpho[NPHOTON], + const PFNeutralObj outne[NSELCALO], + const PFChargedObj outmu[NMU], + ap_uint output[PACKING_NCHANN]); +void pfalgo3_unpack_out(const ap_uint output[PACKING_NCHANN], + PFChargedObj outch[NTRACK], + PFNeutralObj outpho[NPHOTON], + PFNeutralObj outne[NSELCALO], + PFChargedObj outmu[NMU]); +#endif + +void pfalgo3_set_debug(bool debug); + +#ifndef CMSSW_GIT_HASH +#define PFALGO_DR2MAX_TK_CALO 1182 +#define PFALGO_DR2MAX_EM_CALO 525 +#define PFALGO_DR2MAX_TK_EM 84 +#define PFALGO_TK_MAXINVPT_LOOSE 40 +#define PFALGO_TK_MAXINVPT_TIGHT 80 +#endif + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo_common.h b/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo_common.h new file mode 100644 index 0000000000000..5fe4e1181b9db --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/firmware/pfalgo_common.h @@ -0,0 +1,16 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_FIRMWARE_PFALGO_COMMON_H +#define L1Trigger_Phase2L1ParticleFlow_FIRMWARE_PFALGO_COMMON_H + +#include "data.h" + +inline int dr2_int(etaphi_t eta1, etaphi_t phi1, etaphi_t eta2, etaphi_t phi2) { + ap_int deta = (eta1 - eta2); + ap_int dphi = (phi1 - phi2); + return deta * deta + dphi * dphi; +} + +#ifndef CMSSW_GIT_HASH +#define PFALGO_DR2MAX_TK_MU 2101 +#endif + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.cpp b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.cpp new file mode 100644 index 0000000000000..7d407a60ef7a8 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.cpp @@ -0,0 +1,196 @@ +#include "pfalgo2hgc_ref.h" + +#ifndef CMSSW_GIT_HASH +#include "../DiscretePFInputs.h" +#else +#include "../../interface/DiscretePFInputs.h" +#endif + +#include "../utils/Firmware2DiscretePF.h" +#include +#include +#include +#include + +int g_pfalgo2hgc_debug_ref_ = 0; + +void pfalgo2hgc_ref_set_debug(int debug) { g_pfalgo2hgc_debug_ref_ = debug; } + +void pfalgo2hgc_ref(const pfalgo_config &cfg, + const HadCaloObj calo[/*cfg.nCALO*/], + const TkObj track[/*cfg.nTRACK*/], + const MuObj mu[/*cfg.nMU*/], + PFChargedObj outch[/*cfg.nTRACK*/], + PFNeutralObj outne[/*cfg.nSELCALO*/], + PFChargedObj outmu[/*cfg.nMU*/]) { + if (g_pfalgo2hgc_debug_ref_) { +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + for (unsigned int i = 0; i < cfg.nTRACK; ++i) { + if (track[i].hwPt == 0) + continue; + l1tpf_impl::PropagatedTrack tk; + fw2dpf::convert(track[i], tk); + printf( + "FW \t track %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] calo ptErr %6d [ " + "%7.2f ] tight %d\n", + i, + tk.hwPt, + tk.floatPt(), + tk.hwEta, + tk.floatEta(), + tk.hwPhi, + tk.floatPhi(), + tk.hwCaloPtErr, + tk.floatCaloPtErr(), + int(track[i].hwTightQuality)); + } + for (unsigned int i = 0; i < cfg.nCALO; ++i) { + if (calo[i].hwPt == 0) + continue; + l1tpf_impl::CaloCluster c; + fw2dpf::convert(calo[i], c); + printf( + "FW \t calo %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] calo emPt %7d [ " + "%7.2f ] isEM %d \n", + i, + c.hwPt, + c.floatPt(), + c.hwEta, + c.floatEta(), + c.hwPhi, + c.floatPhi(), + c.hwEmPt, + c.floatEmPt(), + c.isEM); + } + for (unsigned int i = 0; i < cfg.nMU; ++i) { + if (mu[i].hwPt == 0) + continue; + l1tpf_impl::Muon muon; + fw2dpf::convert(mu[i], muon); + printf("FW \t muon %3d: pt %8d [ %7.2f ] muon eta %+7d [ %+5.2f ] muon phi %+7d [ %+5.2f ] \n", + i, + muon.hwPt, + muon.floatPt(), + muon.hwEta, + muon.floatEta(), + muon.hwPhi, + muon.floatPhi()); + } +#endif + } + + // constants + const pt_t TKPT_MAX_LOOSE = cfg.tk_MAXINVPT_LOOSE; + const pt_t TKPT_MAX_TIGHT = cfg.tk_MAXINVPT_TIGHT; + const int DR2MAX = cfg.dR2MAX_TK_CALO; + + //////////////////////////////////////////////////// + // TK-MU Linking + std::unique_ptr isMu(new bool[cfg.nTRACK]); + pfalgo_mu_ref(cfg, track, mu, &isMu[0], outmu, g_pfalgo2hgc_debug_ref_); + + //////////////////////////////////////////////////// + // TK-HAD Linking + + // initialize sum track pt + std::vector calo_sumtk(cfg.nCALO), calo_subpt(cfg.nCALO); + std::vector calo_sumtkErr2(cfg.nCALO); + for (unsigned int ic = 0; ic < cfg.nCALO; ++ic) { + calo_sumtk[ic] = 0; + calo_sumtkErr2[ic] = 0; + } + + // initialize good track bit + std::unique_ptr track_good(new bool[cfg.nTRACK]); + std::unique_ptr isEle(new bool[cfg.nTRACK]); + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + track_good[it] = (track[it].hwPt < (track[it].hwTightQuality ? TKPT_MAX_TIGHT : TKPT_MAX_LOOSE) || isMu[it]); + isEle[it] = false; + } + + // initialize output + for (unsigned int ipf = 0; ipf < cfg.nTRACK; ++ipf) + clear(outch[ipf]); + for (unsigned int ipf = 0; ipf < cfg.nSELCALO; ++ipf) + clear(outne[ipf]); + + // for each track, find the closest calo + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + if (track[it].hwPt > 0 && !isMu[it]) { + int ibest = best_match_with_pt_ref(cfg.nCALO, DR2MAX, calo, track[it]); + if (ibest != -1) { + if (g_pfalgo2hgc_debug_ref_) + printf("FW \t track %3d pt %7d matched to calo' %3d pt %7d\n", + it, + int(track[it].hwPt), + ibest, + int(calo[ibest].hwPt)); + track_good[it] = true; + isEle[it] = calo[ibest].hwIsEM; + calo_sumtk[ibest] += track[it].hwPt; + calo_sumtkErr2[ibest] += sqr(track[it].hwPtErr); + } + } + } + + for (unsigned int ic = 0; ic < cfg.nCALO; ++ic) { + if (calo_sumtk[ic] > 0) { + pt_t ptdiff = calo[ic].hwPt - calo_sumtk[ic]; + int sigmamult = + calo_sumtkErr2[ic]; // + (calo_sumtkErr2[ic] >> 1)); // this multiplies by 1.5 = sqrt(1.5)^2 ~ (1.2)^2 + if (g_pfalgo2hgc_debug_ref_ && (calo[ic].hwPt > 0)) { +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + l1tpf_impl::CaloCluster floatcalo; + fw2dpf::convert(calo[ic], floatcalo); + printf( + "FW \t calo' %3d pt %7d [ %7.2f ] eta %+7d [ %+5.2f ] has a sum track pt %7d, difference %7d +- %.2f \n", + ic, + int(calo[ic].hwPt), + floatcalo.floatPt(), + int(calo[ic].hwEta), + floatcalo.floatEta(), + int(calo_sumtk[ic]), + int(ptdiff), + std::sqrt(float(int(calo_sumtkErr2[ic])))); +#endif + } + if (ptdiff > 0 && ptdiff * ptdiff > sigmamult) { + calo_subpt[ic] = ptdiff; + } else { + calo_subpt[ic] = 0; + } + } else { + calo_subpt[ic] = calo[ic].hwPt; + } + if (g_pfalgo2hgc_debug_ref_ && (calo[ic].hwPt > 0)) + printf("FW \t calo' %3d pt %7d ---> %7d \n", ic, int(calo[ic].hwPt), int(calo_subpt[ic])); + } + + // copy out charged hadrons + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + if (track_good[it]) { + assert(!(isEle[it] && isMu[it])); + outch[it].hwPt = track[it].hwPt; + outch[it].hwEta = track[it].hwEta; + outch[it].hwPhi = track[it].hwPhi; + outch[it].hwZ0 = track[it].hwZ0; + outch[it].hwId = isEle[it] ? PID_Electron : (isMu[it] ? PID_Muon : PID_Charged); + } + } + + // copy out neutral hadrons with sorting and cropping + std::vector outne_all(cfg.nCALO); + for (unsigned int ipf = 0; ipf < cfg.nCALO; ++ipf) + clear(outne_all[ipf]); + for (unsigned int ic = 0; ic < cfg.nCALO; ++ic) { + if (calo_subpt[ic] > 0) { + outne_all[ic].hwPt = calo_subpt[ic]; + outne_all[ic].hwEta = calo[ic].hwEta; + outne_all[ic].hwPhi = calo[ic].hwPhi; + outne_all[ic].hwId = calo[ic].hwIsEM ? PID_Photon : PID_Neutral; + } + } + + ptsort_ref(cfg.nCALO, cfg.nSELCALO, outne_all, outne); +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.h b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.h new file mode 100644 index 0000000000000..c24af621fa28d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo2hgc_ref.h @@ -0,0 +1,17 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PFALGO2HGC_REF_H +#define L1Trigger_Phase2L1ParticleFlow_PFALGO2HGC_REF_H + +#include "../firmware/pfalgo2hgc.h" +#include "pfalgo_common_ref.h" + +void pfalgo2hgc_ref_set_debug(int debug); + +void pfalgo2hgc_ref(const pfalgo_config &cfg, + const HadCaloObj calo[/*cfg.nCALO*/], + const TkObj track[/*cfg.nTRACK*/], + const MuObj mu[/*cfg.nMU*/], + PFChargedObj outch[/*cfg.nTRACK*/], + PFNeutralObj outne[/*cfg.nSELCALO*/], + PFChargedObj outmu[/*cfg.nMU*/]); + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.cpp b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.cpp new file mode 100644 index 0000000000000..6a2d06a9551ef --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.cpp @@ -0,0 +1,485 @@ +#include "pfalgo3_ref.h" + +#ifndef CMSSW_GIT_HASH +#include "../DiscretePFInputs.h" +#else +#include "../../interface/DiscretePFInputs.h" +#endif + +#include "../utils/Firmware2DiscretePF.h" +#include +#include +#include +#include +#include + +int g_pfalgo3_debug_ref_ = 0; + +void pfalgo3_ref_set_debug(int debug) { g_pfalgo3_debug_ref_ = debug; } + +template +int tk_best_match_ref(unsigned int nCAL, unsigned int dR2MAX, const CO_t calo[/*nCAL*/], const TkObj &track) { + pt_t caloPtMin = track.hwPt - 2 * (track.hwPtErr); + if (caloPtMin < 0) + caloPtMin = 0; + int drmin = dR2MAX, ibest = -1; + for (unsigned int ic = 0; ic < nCAL; ++ic) { + if (calo[ic].hwPt <= 0) + continue; + if (doPtMin && calo[ic].hwPt <= caloPtMin) + continue; + int dr = dr2_int(track.hwEta, track.hwPhi, calo[ic].hwEta, calo[ic].hwPhi); + if (dr < drmin) { + drmin = dr; + ibest = ic; + } + } + return ibest; +} +int em_best_match_ref(unsigned int nCAL, unsigned int dR2MAX, const HadCaloObj calo[/*nCAL*/], const EmCaloObj &em) { + pt_t emPtMin = em.hwPt >> 1; + int drmin = dR2MAX, ibest = -1; + for (unsigned int ic = 0; ic < nCAL; ++ic) { + if (calo[ic].hwEmPt <= emPtMin) + continue; + int dr = dr2_int(em.hwEta, em.hwPhi, calo[ic].hwEta, calo[ic].hwPhi); + if (dr < drmin) { + drmin = dr; + ibest = ic; + } + } + return ibest; +} + +void pfalgo3_em_ref(const pfalgo3_config &cfg, + const EmCaloObj emcalo[/*cfg.nEMCALO*/], + const HadCaloObj hadcalo[/*cfg.nCALO*/], + const TkObj track[/*cfg.nTRACK*/], + const bool isMu[/*cfg.nTRACK*/], + bool isEle[/*cfg.nTRACK*/], + PFNeutralObj outpho[/*cfg.nPHOTON*/], + HadCaloObj hadcalo_out[/*cfg.nCALO*/]) { + // constants + const int DR2MAX_TE = cfg.dR2MAX_TK_EM; + const int DR2MAX_EH = cfg.dR2MAX_EM_CALO; + + // initialize sum track pt + std::vector calo_sumtk(cfg.nEMCALO); + for (unsigned int ic = 0; ic < cfg.nEMCALO; ++ic) { + calo_sumtk[ic] = 0; + } + std::vector tk2em(cfg.nTRACK); + std::vector isEM(cfg.nEMCALO); + // for each track, find the closest calo + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + if (track[it].hwPt > 0 && !isMu[it]) { + tk2em[it] = tk_best_match_ref(cfg.nEMCALO, DR2MAX_TE, emcalo, track[it]); + if (tk2em[it] != -1) { + if (g_pfalgo3_debug_ref_) + printf("FW \t track %3d pt %7d matched to em calo %3d pt %7d\n", + it, + int(track[it].hwPt), + tk2em[it], + int(emcalo[tk2em[it]].hwPt)); + calo_sumtk[tk2em[it]] += track[it].hwPt; + } + } else { + tk2em[it] = -1; + } + } + + if (g_pfalgo3_debug_ref_) { + for (unsigned int ic = 0; ic < cfg.nEMCALO; ++ic) { + if (emcalo[ic].hwPt > 0) + printf("FW \t emcalo %3d pt %7d has sumtk %7d\n", ic, int(emcalo[ic].hwPt), int(calo_sumtk[ic])); + } + } + + for (unsigned int ic = 0; ic < cfg.nEMCALO; ++ic) { + pt_t photonPt; + if (calo_sumtk[ic] > 0) { + pt_t ptdiff = emcalo[ic].hwPt - calo_sumtk[ic]; + int sigma2 = sqr(emcalo[ic].hwPtErr); + int sigma2Lo = 4 * sigma2, + sigma2Hi = sigma2; // + (sigma2>>1); // cut at 1 sigma instead of old cut at sqrt(1.5) sigma's + int ptdiff2 = ptdiff * ptdiff; + if ((ptdiff >= 0 && ptdiff2 <= sigma2Hi) || (ptdiff < 0 && ptdiff2 < sigma2Lo)) { + // electron + photonPt = 0; + isEM[ic] = true; + if (g_pfalgo3_debug_ref_) + printf("FW \t emcalo %3d pt %7d ptdiff %7d [match window: -%.2f / +%.2f] flagged as electron\n", + ic, + int(emcalo[ic].hwPt), + int(ptdiff), + std::sqrt(float(sigma2Lo)), + std::sqrt(float(sigma2Hi))); + } else if (ptdiff > 0) { + // electron + photon + photonPt = ptdiff; + isEM[ic] = true; + if (g_pfalgo3_debug_ref_) + printf( + "FW \t emcalo %3d pt %7d ptdiff %7d [match window: -%.2f / +%.2f] flagged as electron + photon of pt " + "%7d\n", + ic, + int(emcalo[ic].hwPt), + int(ptdiff), + std::sqrt(float(sigma2Lo)), + std::sqrt(float(sigma2Hi)), + int(photonPt)); + } else { + // pion + photonPt = 0; + isEM[ic] = false; + if (g_pfalgo3_debug_ref_) + printf("FW \t emcalo %3d pt %7d ptdiff %7d [match window: -%.2f / +%.2f] flagged as pion\n", + ic, + int(emcalo[ic].hwPt), + int(ptdiff), + std::sqrt(float(sigma2Lo)), + std::sqrt(float(sigma2Hi))); + } + } else { + // photon + isEM[ic] = true; + photonPt = emcalo[ic].hwPt; + if (g_pfalgo3_debug_ref_ && emcalo[ic].hwPt > 0) + printf("FW \t emcalo %3d pt %7d flagged as photon\n", ic, int(emcalo[ic].hwPt)); + } + outpho[ic].hwPt = photonPt; + outpho[ic].hwEta = photonPt ? emcalo[ic].hwEta : etaphi_t(0); + outpho[ic].hwPhi = photonPt ? emcalo[ic].hwPhi : etaphi_t(0); + outpho[ic].hwId = photonPt ? PID_Photon : particleid_t(0); + } + + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + isEle[it] = (tk2em[it] != -1) && isEM[tk2em[it]]; + if (g_pfalgo3_debug_ref_ && isEle[it]) + printf("FW \t track %3d pt %7d flagged as electron.\n", it, int(track[it].hwPt)); + } + + std::vector em2calo(cfg.nEMCALO); + for (unsigned int ic = 0; ic < cfg.nEMCALO; ++ic) { + em2calo[ic] = em_best_match_ref(cfg.nCALO, DR2MAX_EH, hadcalo, emcalo[ic]); + if (g_pfalgo3_debug_ref_ && (emcalo[ic].hwPt > 0)) { + printf("FW \t emcalo %3d pt %7d isEM %d matched to hadcalo %7d pt %7d emPt %7d isEM %d\n", + ic, + int(emcalo[ic].hwPt), + int(isEM[ic]), + em2calo[ic], + (em2calo[ic] >= 0 ? int(hadcalo[em2calo[ic]].hwPt) : -1), + (em2calo[ic] >= 0 ? int(hadcalo[em2calo[ic]].hwEmPt) : -1), + (em2calo[ic] >= 0 ? int(hadcalo[em2calo[ic]].hwIsEM) : 0)); + } + } + + for (unsigned int ih = 0; ih < cfg.nCALO; ++ih) { + hadcalo_out[ih] = hadcalo[ih]; + pt_t sub = 0; + bool keep = false; + for (unsigned int ic = 0; ic < cfg.nEMCALO; ++ic) { + if (em2calo[ic] == int(ih)) { + if (isEM[ic]) + sub += emcalo[ic].hwPt; + else + keep = true; + } + } + pt_t emdiff = hadcalo[ih].hwEmPt - sub; + pt_t alldiff = hadcalo[ih].hwPt - sub; + if (g_pfalgo3_debug_ref_ && (hadcalo[ih].hwPt > 0)) { + printf("FW \t calo %3d pt %7d has a subtracted pt of %7d, empt %7d -> %7d isem %d mustkeep %d \n", + ih, + int(hadcalo[ih].hwPt), + int(alldiff), + int(hadcalo[ih].hwEmPt), + int(emdiff), + int(hadcalo[ih].hwIsEM), + keep); + } + if (alldiff <= (hadcalo[ih].hwPt >> 4)) { + hadcalo_out[ih].hwPt = 0; // kill + hadcalo_out[ih].hwEmPt = 0; // kill + if (g_pfalgo3_debug_ref_ && (hadcalo[ih].hwPt > 0)) + printf("FW \t calo %3d pt %7d --> discarded (zero pt)\n", ih, int(hadcalo[ih].hwPt)); + } else if ((hadcalo[ih].hwIsEM && emdiff <= (hadcalo[ih].hwEmPt >> 3)) && !keep) { + hadcalo_out[ih].hwPt = 0; // kill + hadcalo_out[ih].hwEmPt = 0; // kill + if (g_pfalgo3_debug_ref_ && (hadcalo[ih].hwPt > 0)) + printf("FW \t calo %3d pt %7d --> discarded (zero em)\n", ih, int(hadcalo[ih].hwPt)); + } else { + hadcalo_out[ih].hwPt = alldiff; + hadcalo_out[ih].hwEmPt = (emdiff > 0 ? emdiff : pt_t(0)); + } + } +} + +void pfalgo3_ref(const pfalgo3_config &cfg, + const EmCaloObj emcalo[/*cfg.nEMCALO*/], + const HadCaloObj hadcalo[/*cfg.nCALO*/], + const TkObj track[/*cfg.nTRACK*/], + const MuObj mu[/*cfg.nMU*/], + PFChargedObj outch[/*cfg.nTRACK*/], + PFNeutralObj outpho[/*cfg.nPHOTON*/], + PFNeutralObj outne[/*cfg.nSELCALO*/], + PFChargedObj outmu[/*cfg.nMU*/]) { + if (g_pfalgo3_debug_ref_) { +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + for (unsigned int i = 0; i < cfg.nTRACK; ++i) { + if (track[i].hwPt == 0) + continue; + l1tpf_impl::PropagatedTrack tk; + fw2dpf::convert(track[i], tk); + printf( + "FW \t track %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] calo ptErr %6d [ " + "%7.2f ] tight %d\n", + i, + tk.hwPt, + tk.floatPt(), + tk.hwEta, + tk.floatEta(), + tk.hwPhi, + tk.floatPhi(), + tk.hwCaloPtErr, + tk.floatCaloPtErr(), + int(track[i].hwTightQuality)); + } + for (unsigned int i = 0; i < cfg.nEMCALO; ++i) { + if (emcalo[i].hwPt == 0) + continue; + l1tpf_impl::CaloCluster em; + fw2dpf::convert(emcalo[i], em); + printf( + "FW \t EM %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] calo ptErr %6d [ " + "%7.2f ] \n", + i, + em.hwPt, + em.floatPt(), + em.hwEta, + em.floatEta(), + em.hwPhi, + em.floatPhi(), + em.hwPtErr, + em.floatPtErr()); + } + for (unsigned int i = 0; i < cfg.nCALO; ++i) { + if (hadcalo[i].hwPt == 0) + continue; + l1tpf_impl::CaloCluster calo; + fw2dpf::convert(hadcalo[i], calo); + printf( + "FW \t calo %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] calo emPt %7d [ " + "%7.2f ] isEM %d \n", + i, + calo.hwPt, + calo.floatPt(), + calo.hwEta, + calo.floatEta(), + calo.hwPhi, + calo.floatPhi(), + calo.hwEmPt, + calo.floatEmPt(), + calo.isEM); + } + for (unsigned int i = 0; i < cfg.nMU; ++i) { + if (mu[i].hwPt == 0) + continue; + l1tpf_impl::Muon muon; + fw2dpf::convert(mu[i], muon); + printf("FW \t muon %3d: pt %8d [ %7.2f ] muon eta %+7d [ %+5.2f ] muon phi %+7d [ %+5.2f ] \n", + i, + muon.hwPt, + muon.floatPt(), + muon.hwEta, + muon.floatEta(), + muon.hwPhi, + muon.floatPhi()); + } +#endif + } + + // constants + const pt_t TKPT_MAX_LOOSE = cfg.tk_MAXINVPT_LOOSE; + const pt_t TKPT_MAX_TIGHT = cfg.tk_MAXINVPT_TIGHT; + const int DR2MAX = cfg.dR2MAX_TK_CALO; + + //////////////////////////////////////////////////// + // TK-MU Linking + // // we can't use std::vector here because it's specialized + std::unique_ptr isMu(new bool[cfg.nTRACK]); + pfalgo_mu_ref(cfg, track, mu, &isMu[0], outmu, g_pfalgo3_debug_ref_); + + //////////////////////////////////////////////////// + // TK-EM Linking + std::unique_ptr isEle(new bool[cfg.nTRACK]); + std::vector hadcalo_subem(cfg.nCALO); + pfalgo3_em_ref(cfg, emcalo, hadcalo, track, &isMu[0], &isEle[0], outpho, &hadcalo_subem[0]); + + //////////////////////////////////////////////////// + // TK-HAD Linking + + // initialize sum track pt + std::vector calo_sumtk(cfg.nCALO), calo_subpt(cfg.nCALO); + std::vector calo_sumtkErr2(cfg.nCALO); + for (unsigned int ic = 0; ic < cfg.nCALO; ++ic) { + calo_sumtk[ic] = 0; + calo_sumtkErr2[ic] = 0; + } + + // initialize good track bit + std::unique_ptr track_good(new bool[cfg.nTRACK]); + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + track_good[it] = + (track[it].hwPt < (track[it].hwTightQuality ? TKPT_MAX_TIGHT : TKPT_MAX_LOOSE) || isEle[it] || isMu[it]); + } + + // initialize output + for (unsigned int ipf = 0; ipf < cfg.nTRACK; ++ipf) { + clear(outch[ipf]); + } + for (unsigned int ipf = 0; ipf < cfg.nSELCALO; ++ipf) { + clear(outne[ipf]); + } + + // for each track, find the closest calo + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + if (track[it].hwPt > 0 && !isEle[it] && !isMu[it]) { + int ibest = best_match_with_pt_ref(cfg.nCALO, DR2MAX, &hadcalo_subem[0], track[it]); + //int ibest = tk_best_match_ref(cfg.nCALO, DR2MAX, &hadcalo_subem[0], track[it]); + if (ibest != -1) { + if (g_pfalgo3_debug_ref_) + printf("FW \t track %3d pt %7d matched to calo %3d pt %7d\n", + it, + int(track[it].hwPt), + ibest, + int(hadcalo_subem[ibest].hwPt)); + track_good[it] = true; + calo_sumtk[ibest] += track[it].hwPt; + calo_sumtkErr2[ibest] += sqr(track[it].hwPtErr); + } + } + } + + for (unsigned int ic = 0; ic < cfg.nCALO; ++ic) { + if (calo_sumtk[ic] > 0) { + pt_t ptdiff = hadcalo_subem[ic].hwPt - calo_sumtk[ic]; + int sigmamult = calo_sumtkErr2 + [ic]; // before we did (calo_sumtkErr2[ic] + (calo_sumtkErr2[ic] >> 1)); to multiply by 1.5 = sqrt(1.5)^2 ~ (1.2)^2 + if (g_pfalgo3_debug_ref_ && (hadcalo_subem[ic].hwPt > 0)) { +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + l1tpf_impl::CaloCluster floatcalo; + fw2dpf::convert(hadcalo_subem[ic], floatcalo); + printf( + "FW \t calo %3d pt %7d [ %7.2f ] eta %+7d [ %+5.2f ] has a sum track pt %7d, difference %7d +- %.2f \n", + ic, + int(hadcalo_subem[ic].hwPt), + floatcalo.floatPt(), + int(hadcalo_subem[ic].hwEta), + floatcalo.floatEta(), + int(calo_sumtk[ic]), + int(ptdiff), + std::sqrt(float(int(calo_sumtkErr2[ic])))); +#endif + } + if (ptdiff > 0 && ptdiff * ptdiff > sigmamult) { + calo_subpt[ic] = ptdiff; + } else { + calo_subpt[ic] = 0; + } + } else { + calo_subpt[ic] = hadcalo_subem[ic].hwPt; + } + if (g_pfalgo3_debug_ref_ && (hadcalo_subem[ic].hwPt > 0)) + printf("FW \t calo %3d pt %7d ---> %7d \n", ic, int(hadcalo_subem[ic].hwPt), int(calo_subpt[ic])); + } + + // copy out charged hadrons + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + if (track_good[it]) { + outch[it].hwPt = track[it].hwPt; + outch[it].hwEta = track[it].hwEta; + outch[it].hwPhi = track[it].hwPhi; + outch[it].hwZ0 = track[it].hwZ0; + outch[it].hwId = isEle[it] ? PID_Electron : (isMu[it] ? PID_Muon : PID_Charged); + } + } + + // copy out neutral hadrons + std::vector outne_all(cfg.nCALO); + for (unsigned int ipf = 0; ipf < cfg.nCALO; ++ipf) { + clear(outne_all[ipf]); + } + for (unsigned int ic = 0; ic < cfg.nCALO; ++ic) { + if (calo_subpt[ic] > 0) { + outne_all[ic].hwPt = calo_subpt[ic]; + outne_all[ic].hwEta = hadcalo_subem[ic].hwEta; + outne_all[ic].hwPhi = hadcalo_subem[ic].hwPhi; + outne_all[ic].hwId = PID_Neutral; + } + } + + ptsort_ref(cfg.nCALO, cfg.nSELCALO, outne_all, outne); + + if (g_pfalgo3_debug_ref_) { +#ifdef L1Trigger_Phase2L1ParticleFlow_DiscretePFInputs_MORE + std::vector tmp; + for (unsigned int i = 0; i < cfg.nTRACK; ++i) { + if (outch[i].hwPt == 0) + continue; + fw2dpf::convert(outch[i], track[i], tmp); + auto &pf = tmp.back(); + printf("FW \t outch %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] pid %d\n", + i, + pf.hwPt, + pf.floatPt(), + pf.hwEta, + pf.floatEta(), + pf.hwPhi, + pf.floatPhi(), + pf.hwId); + } + for (unsigned int i = 0; i < cfg.nPHOTON; ++i) { + if (outpho[i].hwPt == 0) + continue; + fw2dpf::convert(outpho[i], tmp); + auto &pf = tmp.back(); + printf("FW \t outph %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] pid %d\n", + i, + pf.hwPt, + pf.floatPt(), + pf.hwEta, + pf.floatEta(), + pf.hwPhi, + pf.floatPhi(), + pf.hwId); + } + for (unsigned int i = 0; i < cfg.nSELCALO; ++i) { + if (outne[i].hwPt == 0) + continue; + fw2dpf::convert(outne[i], tmp); + auto &pf = tmp.back(); + printf("FW \t outne %3d: pt %8d [ %7.2f ] calo eta %+7d [ %+5.2f ] calo phi %+7d [ %+5.2f ] pid %d\n", + i, + pf.hwPt, + pf.floatPt(), + pf.hwEta, + pf.floatEta(), + pf.hwPhi, + pf.floatPhi(), + pf.hwId); + } +#endif + } +} + +void pfalgo3_merge_neutrals_ref(const pfalgo3_config &cfg, + const PFNeutralObj pho[/*cfg.nPHOTON*/], + const PFNeutralObj ne[/*cfg.nSELCALO*/], + PFNeutralObj allne[/*cfg.nALLNEUTRALS*/]) { + int j = 0; + for (unsigned int i = 0; i < cfg.nPHOTON; ++i, ++j) + allne[j] = pho[i]; + for (unsigned int i = 0; i < cfg.nSELCALO; ++i, ++j) + allne[j] = ne[i]; +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.h b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.h new file mode 100644 index 0000000000000..43fb37ef40509 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo3_ref.h @@ -0,0 +1,58 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PFALGO3_REF_H +#define L1Trigger_Phase2L1ParticleFlow_PFALGO3_REF_H + +#include "../firmware/pfalgo3.h" +#include "pfalgo_common_ref.h" + +struct pfalgo3_config : public pfalgo_config { + unsigned int nEMCALO, nPHOTON, nALLNEUTRAL; + unsigned int dR2MAX_TK_EM; + unsigned int dR2MAX_EM_CALO; + + pfalgo3_config(unsigned int nTrack, + unsigned int nEmCalo, + unsigned int nCalo, + unsigned int nMu, + unsigned int nPhoton, + unsigned int nSelCalo, + unsigned int nAllNeutral, + unsigned int dR2Max_Tk_Mu, + unsigned int dR2Max_Tk_Em, + unsigned int dR2Max_Em_Calo, + unsigned int dR2Max_Tk_Calo, + unsigned int tk_MaxInvPt_Loose, + unsigned int tk_MaxInvPt_Tight) + : pfalgo_config(nTrack, nCalo, nMu, nSelCalo, dR2Max_Tk_Mu, dR2Max_Tk_Calo, tk_MaxInvPt_Loose, tk_MaxInvPt_Tight), + nEMCALO(nEmCalo), + nPHOTON(nPhoton), + nALLNEUTRAL(nAllNeutral), + dR2MAX_TK_EM(dR2Max_Tk_Em), + dR2MAX_EM_CALO(dR2Max_Em_Calo) {} + ~pfalgo3_config() override {} +}; + +void pfalgo3_ref_set_debug(int debug); + +void pfalgo3_em_ref(const pfalgo3_config &cfg, + const EmCaloObj emcalo[/*cfg.nEMCALO*/], + const HadCaloObj hadcalo[/*cfg.nCALO*/], + const TkObj track[/*cfg.nTRACK*/], + const bool isMu[/*cfg.nTRACK*/], + bool isEle[/*cfg.nTRACK*/], + PFNeutralObj outpho[/*cfg.nPHOTON*/], + HadCaloObj hadcalo_out[/*cfg.nCALO*/]); +void pfalgo3_ref(const pfalgo3_config &cfg, + const EmCaloObj emcalo[/*cfg.nEMCALO*/], + const HadCaloObj hadcalo[/*cfg.nCALO*/], + const TkObj track[/*cfg.nTRACK*/], + const MuObj mu[/*cfg.nMU*/], + PFChargedObj outch[/*cfg.nTRACK*/], + PFNeutralObj outpho[/*cfg.nPHOTON*/], + PFNeutralObj outne[/*cfg.nSELCALO*/], + PFChargedObj outmu[/*cfg.nMU*/]); + +void pfalgo3_merge_neutrals_ref(const pfalgo3_config &cfg, + const PFNeutralObj pho[/*cfg.nPHOTON*/], + const PFNeutralObj ne[/*cfg.nSELCALO*/], + PFNeutralObj allne[/*cfg.nALLNEUTRALS*/]); +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.cpp b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.cpp new file mode 100644 index 0000000000000..8844c0617149b --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.cpp @@ -0,0 +1,49 @@ +#include "pfalgo_common_ref.h" + +#include +#include + +void pfalgo_mu_ref(const pfalgo_config &cfg, + const TkObj track[/*cfg.nTRACK*/], + const MuObj mu[/*cfg.nMU*/], + bool isMu[/*cfg.nTRACK*/], + PFChargedObj outmu[/*cfg.nMU*/], + bool debug) { + // init + for (unsigned int ipf = 0; ipf < cfg.nMU; ++ipf) + clear(outmu[ipf]); + for (unsigned int it = 0; it < cfg.nTRACK; ++it) + isMu[it] = false; + + // for each muon, find the closest track + for (unsigned int im = 0; im < cfg.nMU; ++im) { + if (mu[im].hwPt > 0) { + int ibest = -1; + int dptmin = mu[im].hwPt >> 1; + for (unsigned int it = 0; it < cfg.nTRACK; ++it) { + unsigned int dr = dr2_int(mu[im].hwEta, mu[im].hwPhi, track[it].hwEta, track[it].hwPhi); + //printf("deltaR2(mu %d float pt %5.1f, tk %2d float pt %5.1f) = int %d (float deltaR = %.3f); int cut at %d\n", im, 0.25*int(mu[im].hwPt), it, 0.25*int(track[it].hwPt), dr, std::sqrt(float(dr))/229.2, cfg.dR2MAX_TK_MU); + if (dr < cfg.dR2MAX_TK_MU) { + int dpt = std::abs(int(track[it].hwPt - mu[im].hwPt)); + if (dpt < dptmin) { + dptmin = dpt; + ibest = it; + } + } + } + if (ibest != -1) { + outmu[im].hwPt = track[ibest].hwPt; + outmu[im].hwEta = track[ibest].hwEta; + outmu[im].hwPhi = track[ibest].hwPhi; + outmu[im].hwId = PID_Muon; + outmu[im].hwZ0 = track[ibest].hwZ0; + isMu[ibest] = true; + if (debug) + printf("FW \t muon %3d linked to track %3d \n", im, ibest); + } else { + if (debug) + printf("FW \t muon %3d not linked to any track\n", im); + } + } + } +} diff --git a/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.h b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.h new file mode 100644 index 0000000000000..87b2b7194697c --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/ref/pfalgo_common_ref.h @@ -0,0 +1,95 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_PFALGO_COMMON_REF_H +#define L1Trigger_Phase2L1ParticleFlow_PFALGO_COMMON_REF_H + +#include "../firmware/data.h" +#include "../firmware/pfalgo_common.h" +#include + +template +inline int sqr(const T &t) { + return t * t; +} + +template +int best_match_with_pt_ref(int nCAL, int dR2MAX, const CO_t calo[/*nCAL*/], const TkObj &track); + +template +void ptsort_ref(int nIn, int nOut, const T in[/*nIn*/], T out[/*nOut*/]); + +struct pfalgo_config { + unsigned int nTRACK, nCALO, nMU; + unsigned int nSELCALO; + unsigned int dR2MAX_TK_MU; + unsigned int dR2MAX_TK_CALO; + unsigned int tk_MAXINVPT_LOOSE, tk_MAXINVPT_TIGHT; + + pfalgo_config(unsigned int nTrack, + unsigned int nCalo, + unsigned int nMu, + unsigned int nSelCalo, + unsigned int dR2Max_Tk_Mu, + unsigned int dR2Max_Tk_Calo, + unsigned int tk_MaxInvPt_Loose, + unsigned int tk_MaxInvPt_Tight) + : nTRACK(nTrack), + nCALO(nCalo), + nMU(nMu), + nSELCALO(nSelCalo), + dR2MAX_TK_MU(dR2Max_Tk_Mu), + dR2MAX_TK_CALO(dR2Max_Tk_Calo), + tk_MAXINVPT_LOOSE(tk_MaxInvPt_Loose), + tk_MAXINVPT_TIGHT(tk_MaxInvPt_Tight) {} + + virtual ~pfalgo_config() {} +}; + +void pfalgo_mu_ref(const pfalgo_config &cfg, + const TkObj track[/*cfg.nTRACK*/], + const MuObj mu[/*cfg.nMU*/], + bool isMu[/*cfg.nTRACK*/], + PFChargedObj outmu[/*cfg.nMU*/], + bool debug); + +//=== begin implementation part + +template +int best_match_with_pt_ref(int nCAL, int dR2MAX, const CO_t calo[/*nCAL*/], const TkObj &track) { + pt_t caloPtMin = track.hwPt - 2 * (track.hwPtErr); + if (caloPtMin < 0) + caloPtMin = 0; + int dptscale = (dR2MAX << 8) / std::max(1, sqr(track.hwPtErr)); + int drmin = 0, ibest = -1; + for (int ic = 0; ic < nCAL; ++ic) { + if (calo[ic].hwPt <= caloPtMin) + continue; + int dr = dr2_int(track.hwEta, track.hwPhi, calo[ic].hwEta, calo[ic].hwPhi); + if (dr >= dR2MAX) + continue; + dr += ((sqr(std::max(track.hwPt - calo[ic].hwPt, 0)) * dptscale) >> 8); + if (ibest == -1 || dr < drmin) { + drmin = dr; + ibest = ic; + } + } + return ibest; +} + +template +void ptsort_ref(int nIn, int nOut, const TV &in /*[nIn]*/, T out[/*nOut*/]) { + for (int iout = 0; iout < nOut; ++iout) { + out[iout].hwPt = 0; + } + for (int it = 0; it < nIn; ++it) { + for (int iout = 0; iout < nOut; ++iout) { + if (in[it].hwPt >= out[iout].hwPt) { + for (int i2 = nOut - 1; i2 > iout; --i2) { + out[i2] = out[i2 - 1]; + } + out[iout] = in[it]; + break; + } + } + } +} + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/utils/DiscretePF2Firmware.h b/L1Trigger/Phase2L1ParticleFlow/src/utils/DiscretePF2Firmware.h new file mode 100644 index 0000000000000..0fbf23b86c8e8 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/utils/DiscretePF2Firmware.h @@ -0,0 +1,69 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_DISCRETEPF2FIRMWARE_H +#define L1Trigger_Phase2L1ParticleFlow_DISCRETEPF2FIRMWARE_H + +/// NOTE: this include is not standalone, since the path to DiscretePFInputs is different in CMSSW & Vivado_HLS + +#include "../firmware/data.h" +#include + +namespace dpf2fw { + + // convert inputs from discrete to firmware + void convert(const l1tpf_impl::PropagatedTrack &in, TkObj &out) { + out.hwPt = in.hwPt; + out.hwPtErr = in.hwCaloPtErr; + out.hwEta = in.hwEta; // @calo + out.hwPhi = in.hwPhi; // @calo + out.hwZ0 = in.hwZ0; + out.hwTightQuality = (in.hwStubs >= 6 && in.hwChi2 < 500); + } + + TkObj transformConvert(const l1tpf_impl::PropagatedTrack &in) { + TkObj out; + convert(in, out); + return out; + } + + void convert(const l1tpf_impl::CaloCluster &in, HadCaloObj &out) { + out.hwPt = in.hwPt; + out.hwEmPt = in.hwEmPt; + out.hwEta = in.hwEta; + out.hwPhi = in.hwPhi; + out.hwIsEM = in.isEM; + } + void convert(const l1tpf_impl::CaloCluster &in, EmCaloObj &out) { + out.hwPt = in.hwPt; + out.hwPtErr = in.hwPtErr; + out.hwEta = in.hwEta; + out.hwPhi = in.hwPhi; + } + void convert(const l1tpf_impl::Muon &in, MuObj &out) { + out.hwPt = in.hwPt; + out.hwPtErr = 0; // does not exist in input + out.hwEta = in.hwEta; // @calo + out.hwPhi = in.hwPhi; // @calo + } + + template + void convert(const std::vector &in, Out out[NMAX]) { + for (unsigned int i = 0, n = std::min(NMAX, in.size()); i < n; ++i) { + convert(in[i], out[i]); + } + for (unsigned int i = in.size(); i < NMAX; ++i) { + clear(out[i]); + } + } + + template + void convert(unsigned int NMAX, const std::vector &in, Out out[]) { + for (unsigned int i = 0, n = std::min(NMAX, in.size()); i < n; ++i) { + convert(in[i], out[i]); + } + for (unsigned int i = in.size(); i < NMAX; ++i) { + clear(out[i]); + } + } + +} // namespace dpf2fw + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/src/utils/Firmware2DiscretePF.h b/L1Trigger/Phase2L1ParticleFlow/src/utils/Firmware2DiscretePF.h new file mode 100644 index 0000000000000..7956a7044861d --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/src/utils/Firmware2DiscretePF.h @@ -0,0 +1,161 @@ +#ifndef L1Trigger_Phase2L1ParticleFlow_FIRMWARE2DISCRETEPF_H +#define L1Trigger_Phase2L1ParticleFlow_FIRMWARE2DISCRETEPF_H + +/// NOTE: this include is not standalone, since the path to DiscretePFInputs is different in CMSSW & Vivado_HLS + +#include "../firmware/data.h" +#include +#include + +namespace fw2dpf { + + // convert inputs from discrete to firmware + inline void convert(const PFChargedObj &src, + const l1tpf_impl::PropagatedTrack &track, + std::vector &out) { + l1tpf_impl::PFParticle pf; + pf.hwPt = src.hwPt; + pf.hwEta = src.hwEta; + pf.hwPhi = src.hwPhi; + pf.hwVtxEta = src.hwEta; // FIXME: get from the track + pf.hwVtxPhi = src.hwPhi; // before propagation + pf.track = track; // FIXME: ok only as long as there is a 1-1 mapping + pf.cluster.hwPt = 0; + pf.cluster.src = nullptr; + pf.muonsrc = nullptr; + switch (src.hwId) { + case PID_Electron: + pf.hwId = 1; + break; + case PID_Muon: + pf.hwId = 4; + break; + default: + pf.hwId = 0; + break; + }; + pf.hwStatus = 0; + out.push_back(pf); + } + // convert inputs from discrete to firmware + inline void convert(const TkObj &in, l1tpf_impl::PropagatedTrack &out); + inline void convert(const PFChargedObj &src, const TkObj &track, std::vector &out) { + l1tpf_impl::PFParticle pf; + pf.hwPt = src.hwPt; + pf.hwEta = src.hwEta; + pf.hwPhi = src.hwPhi; + pf.hwVtxEta = src.hwEta; // FIXME: get from the track + pf.hwVtxPhi = src.hwPhi; // before propagation + convert(track, pf.track); // FIXME: ok only as long as there is a 1-1 mapping + pf.cluster.hwPt = 0; + pf.cluster.src = nullptr; + pf.muonsrc = nullptr; + switch (src.hwId) { + case PID_Electron: + pf.hwId = 1; + break; + case PID_Muon: + pf.hwId = 4; + break; + default: + pf.hwId = 0; + break; + }; + pf.hwStatus = 0; + out.push_back(pf); + } + inline void convert(const PFNeutralObj &src, std::vector &out) { + l1tpf_impl::PFParticle pf; + pf.hwPt = src.hwPt; + pf.hwEta = src.hwEta; + pf.hwPhi = src.hwPhi; + pf.hwVtxEta = src.hwEta; + pf.hwVtxPhi = src.hwPhi; + pf.track.hwPt = 0; + pf.track.src = nullptr; + pf.cluster.hwPt = src.hwPt; + pf.cluster.src = nullptr; + pf.muonsrc = nullptr; + switch (src.hwId) { + case PID_Photon: + pf.hwId = 3; + break; + default: + pf.hwId = 2; + break; + } + pf.hwStatus = 0; + out.push_back(pf); + } + + // convert inputs from discrete to firmware + inline void convert(const TkObj &in, l1tpf_impl::PropagatedTrack &out) { + out.hwPt = in.hwPt; + out.hwCaloPtErr = in.hwPtErr; + out.hwEta = in.hwEta; // @calo + out.hwPhi = in.hwPhi; // @calo + out.hwZ0 = in.hwZ0; + out.src = nullptr; + } + inline void convert(const HadCaloObj &in, l1tpf_impl::CaloCluster &out) { + out.hwPt = in.hwPt; + out.hwEmPt = in.hwEmPt; + out.hwEta = in.hwEta; + out.hwPhi = in.hwPhi; + out.isEM = in.hwIsEM; + out.src = nullptr; + } + inline void convert(const EmCaloObj &in, l1tpf_impl::CaloCluster &out) { + out.hwPt = in.hwPt; + out.hwPtErr = in.hwPtErr; + out.hwEta = in.hwEta; + out.hwPhi = in.hwPhi; + out.src = nullptr; + } + inline void convert(const MuObj &in, l1tpf_impl::Muon &out) { + out.hwPt = in.hwPt; + out.hwEta = in.hwEta; // @calo + out.hwPhi = in.hwPhi; // @calo + out.src = nullptr; + } + + template + void convert(const In in[NMAX], std::vector &out) { + for (unsigned int i = 0; i < NMAX; ++i) { + if (in[i].hwPt > 0) + convert(in[i], out); + } + } + template + void convert(unsigned int NMAX, const In in[], std::vector &out) { + for (unsigned int i = 0; i < NMAX; ++i) { + if (in[i].hwPt > 0) + convert(in[i], out); + } + } + template + void convert(const PFChargedObj in[NMAX], + std::vector srctracks, + std::vector &out) { + for (unsigned int i = 0; i < NMAX; ++i) { + if (in[i].hwPt > 0) { + assert(i < srctracks.size()); + convert(in[i], srctracks[i], out); + } + } + } + inline void convert(unsigned int NMAX, + const PFChargedObj in[], + std::vector srctracks, + std::vector &out) { + for (unsigned int i = 0; i < NMAX; ++i) { + if (in[i].hwPt > 0) { + assert(i < srctracks.size()); + convert(in[i], srctracks[i], out); + } + } + } + +} // namespace fw2dpf + +#endif diff --git a/L1Trigger/Phase2L1ParticleFlow/test/BuildFile.xml b/L1Trigger/Phase2L1ParticleFlow/test/BuildFile.xml new file mode 100644 index 0000000000000..0b22a9c4949f8 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/test/BuildFile.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/L1Trigger/Phase2L1ParticleFlow/test/l1pfJetMetTreeProducer.py b/L1Trigger/Phase2L1ParticleFlow/test/l1pfJetMetTreeProducer.py new file mode 100644 index 0000000000000..c2ec3df0ffa56 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/test/l1pfJetMetTreeProducer.py @@ -0,0 +1,31 @@ +import FWCore.ParameterSet.Config as cms +from Configuration.StandardSequences.Eras import eras + +process = cms.Process("IN", eras.phase2_trigger) +process.load('Configuration.StandardSequences.Services_cff') +process.load("FWCore.MessageLogger.MessageLogger_cfi") +process.load('Configuration.Geometry.GeometryExtended2023D17Reco_cff') +process.load('Configuration.StandardSequences.MagneticField_cff') +process.load('Configuration.StandardSequences.EndOfProcess_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, '100X_upgrade2023_realistic_v1', '') + +process.source = cms.Source("PoolSource", + fileNames = cms.untracked.vstring('/store/relval/CMSSW_9_3_7/RelValTTbar_14TeV/GEN-SIM-DIGI-RAW/93X_upgrade2023_realistic_v5_2023D17noPU-v2/10000/7EC7DD7F-782C-E811-B469-0CC47A4D76A0.root'), + duplicateCheckMode = cms.untracked.string("noDuplicateCheck"), + inputCommands = cms.untracked.vstring("keep *", + "drop l1tEMTFHit2016Extras_simEmtfDigis_CSC_HLT", + "drop l1tEMTFHit2016Extras_simEmtfDigis_RPC_HLT", + "drop l1tEMTFHit2016s_simEmtfDigis__HLT", + "drop l1tEMTFTrack2016Extras_simEmtfDigis__HLT", + "drop l1tEMTFTrack2016s_simEmtfDigis__HLT") + +) +process.maxEvents = cms.untracked.PSet( input = cms.untracked.int32(500)) +process.options = cms.untracked.PSet( wantSummary = cms.untracked.bool(True) ) + +process.load('L1Trigger.Phase2L1ParticleFlow.l1pfJetMetTreeProducer_cff') + +process.p = cms.Path(process.l1pfJetMetTreeProducer) +process.TFileService = cms.Service("TFileService", fileName = cms.string("jetmetTuple.root")) diff --git a/L1Trigger/Phase2L1ParticleFlow/test/testOutputFiles.cpp b/L1Trigger/Phase2L1ParticleFlow/test/testOutputFiles.cpp new file mode 100644 index 0000000000000..fbd92c4a82221 --- /dev/null +++ b/L1Trigger/Phase2L1ParticleFlow/test/testOutputFiles.cpp @@ -0,0 +1,611 @@ +// STL includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ROOT includes +#include "TROOT.h" +#include "TSystem.h" +#include "TFile.h" +#include "TTree.h" +#include "TLorentzVector.h" + +// CMSSW includes +#include "FWCore/FWLite/interface/FWLiteEnabler.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "DataFormats/FWLite/interface/Handle.h" +#include "DataFormats/FWLite/interface/Run.h" +#include "DataFormats/FWLite/interface/LuminosityBlock.h" +#include "DataFormats/FWLite/interface/Event.h" +#include "DataFormats/L1TParticleFlow/interface/PFTrack.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/DiscretePFInputsIO.h" +#include "L1Trigger/Phase2L1ParticleFlow/interface/Region.h" + +#define NTEST 64 +#define REPORT_EVERY_N 50 +#define NTRACKS_PER_SECTOR 110 +#define NBITS_PER_TRACK 96 +static std::vector regions_; + +typedef l1tpf_impl::InputRegion Region; +typedef std::pair SectorTrackIndex; +typedef std::map TrackMap; + +struct Event { + uint32_t run, lumi; + uint64_t event; + float z0, genZ0; + std::vector puGlobals; // [float] alphaCMed, alphaCRms, alphaFMed, alphaFRms + std::vector regions; + + Event() : run(0), lumi(0), event(0), z0(0.), regions() {} + bool readFromFile(FILE *fRegionDump) { + if (!fread(&run, sizeof(uint32_t), 1, fRegionDump)) + return false; + fread(&lumi, sizeof(uint32_t), 1, fRegionDump); + fread(&event, sizeof(uint64_t), 1, fRegionDump); + l1tpf_impl::readManyFromFile(regions, fRegionDump); + fread(&z0, sizeof(float), 1, fRegionDump); + fread(&genZ0, sizeof(float), 1, fRegionDump); + l1tpf_impl::readManyFromFile(puGlobals, fRegionDump); + return true; + } +}; + +TLorentzVector makeTLorentzVectorPtEtaPhiE(float pt, float eta, float phi, float e) { + TLorentzVector v; + v.SetPtEtaPhiE(pt, eta, phi, e); + return v; +} + +/* + * Convert a bitset to a signed int64_t. + * std::bitset has built-ins for ulong and ullong. + */ +template 0 && N < 64)>> +int64_t to_int64_from_bitset(const std::bitset &b) { + int const shift = 64 - N; + return (((int64_t)b.to_ullong() << shift) >> shift); +} + +/* + * Generic implementation to search if a given value exists in a map or not. + * Adds all the keys with given value in the vector + */ +template +bool findAllInRegion(std::vector &vec, std::map mapOfElemen, T value) { + bool bResult = false; + auto it = mapOfElemen.begin(); + // Iterate through the map + while (it != mapOfElemen.end()) { + // Check if value of this entry matches with given value + if (it->first.first == value) { + // Yes found + bResult = true; + // Push the key in given map + vec.push_back(it->first); + } + // Go to next entry in map + it++; + } + return bResult; +} + +TrackMap get_tracks_from_root_file(fwlite::Event &ev, int entry = 0, bool print = false) { + TrackMap tracks_root; + + // clear the tracks currently stored in the regions + for (l1tpf_impl::Region &r : regions_) { + r.track.clear(); + } + + // go to the event under test + if (!ev.to(entry)) { + std::cerr << "ERROR::testDumpFile::get_tracks_from_root_file Unable to load the event at entry " << entry + << std::endl; + assert(ev.to(entry)); + } + if (print) + printf("ROOT::Run %u, lumi %u, event %llu \n", + ev.getRun().id().run(), + ev.getLuminosityBlock().id().luminosityBlock(), + ev.eventAuxiliary().id().event()); + + edm::InputTag trackLabel("pfTracksFromL1TracksBarrel"); + edm::Handle> h_track; + ev.getByLabel(trackLabel, h_track); + assert(h_track.isValid()); + + int ntrackstotal(0); + const auto &tracks = *h_track; + for (unsigned int itk = 0, ntk = tracks.size(); itk < ntk; ++itk) { + const auto &tk = tracks[itk]; + if (tk.pt() <= 2.0 || tk.nStubs() < 4 || tk.normalizedChi2() >= 15.0) + continue; + for (l1tpf_impl::Region &r : regions_) { + bool inside = r.contains(tk.eta(), tk.phi()); + ; + if (inside) { + l1tpf_impl::PropagatedTrack prop; + prop.fillInput( + tk.pt(), r.localEta(tk.eta()), r.localPhi(tk.phi()), tk.charge(), tk.vertex().Z(), tk.quality(), &tk); + prop.fillPropagated(tk.pt(), + tk.trkPtError(), + tk.caloPtError(), + r.localEta(tk.caloEta()), + r.localPhi(tk.caloPhi()), + tk.quality(), + tk.isMuon()); + prop.hwStubs = tk.nStubs(); + prop.hwChi2 = round(tk.chi2() * 10); + r.track.push_back(prop); + } + } + //if (print) printf("\t\t Track %u (pT,eta,phi): (%.4f,%.4f,%.4f)\n", ntrackstotal, tk.pt(), tk.eta(), tk.phi()); + ntrackstotal++; + } + for (unsigned int iregion = 0; iregion < regions_.size(); ++iregion) { + std::vector tracks_in_region = regions_[iregion].track; + if (print) + printf("\tFound region %u (eta=[%0.4f,%0.4f] phi=[%0.4f,%0.4f]) with %lu tracks\n", + iregion, + regions_[iregion].etaMin, + regions_[iregion].etaMax, + regions_[iregion].phiCenter - regions_[iregion].phiHalfWidth, + regions_[iregion].phiCenter + regions_[iregion].phiHalfWidth, + tracks_in_region.size()); + for (unsigned int it = 0; it < tracks_in_region.size(); it++) { + if (print) + printf("\t\t Track %u (pT,eta,phi): (%.4f,%.4f,%.4f)\n", + it, + tracks_in_region[it].src->p4().pt(), + tracks_in_region[it].src->p4().eta(), + tracks_in_region[it].src->p4().phi()); + tracks_root[std::make_pair(iregion, it)] = makeTLorentzVectorPtEtaPhiE(tracks_in_region[it].src->pt(), + tracks_in_region[it].src->eta(), + tracks_in_region[it].src->phi(), + tracks_in_region[it].src->pt()); + } + } + if (print) { + printf("\t================================= \n"); + printf("\tTotal tracks %u \n\n", ntrackstotal); + } + + return tracks_root; +} + +std::map, TLorentzVector> get_tracks_from_dump_file(FILE *dfile_ = nullptr, bool print = false) { + std::map, TLorentzVector> tracks_dump; + Event event_; + + if (feof(dfile_)) { + std::cerr << "ERROR::testDumpFile::get_tracks_from_dump_file We have already reached the end of the dump file" + << std::endl; + assert(!feof(dfile_)); + } + if (!event_.readFromFile(dfile_)) { + std::cerr << "ERROR::testDumpFile::get_tracks_from_dump_file Something went wrong reading from the dump file" + << std::endl; + assert(event_.readFromFile(dfile_)); + } + if (event_.regions.size() != regions_.size()) { + printf("ERROR::testDumpFile::get_tracks_from_dump_file Mismatching number of input regions: %lu\n", + event_.regions.size()); + assert(event_.regions.size() == regions_.size()); + } + if (print) + printf("Dump::Run %u, lumi %u, event %lu, regions %lu \n", + event_.run, + event_.lumi, + event_.event, + event_.regions.size()); + + unsigned int ntrackstotal(0); + float maxabseta(0), maxz(0), minz(0); + + int pv_gen = round(event_.genZ0 * l1tpf_impl::InputTrack::Z0_SCALE); + int pv_cmssw = round(event_.z0 * l1tpf_impl::InputTrack::Z0_SCALE); + + for (unsigned int is = 0; is < regions_.size(); ++is) { + const Region &r = event_.regions[is]; + if (print) + printf("\tRead region %u [%0.2f,%0.2f] with %lu tracks\n", + is, + r.phiCenter - r.phiHalfWidth, + r.phiCenter + r.phiHalfWidth, + r.track.size()); + ntrackstotal += r.track.size(); + for (unsigned int it = 0; it < r.track.size(); it++) { + tracks_dump[std::make_pair(is, it)] = makeTLorentzVectorPtEtaPhiE( + r.track[it].floatVtxPt(), r.track[it].floatVtxEta(), r.track[it].floatVtxPhi(), r.track[it].floatVtxPt()); + if (abs(r.track[it].hwVtxEta) > maxabseta) + maxabseta = abs(r.track[it].hwVtxEta); + if (r.track[it].hwZ0 > maxz) + maxz = r.track[it].hwZ0; + if (r.track[it].hwZ0 < minz) + minz = r.track[it].hwZ0; + if (print) + printf("\t\t Track %u (pT,eta,phi): (%.4f,%.4f,%.4f)\n", + it, + r.track[it].floatVtxPt(), + r.track[it].floatVtxEta(), + r.track[it].floatVtxPhi()); + } + } + + if (print) { + printf("\t================================= \n"); + printf("\tTotal tracks %u \n", ntrackstotal); + printf("\tMax abs(eta) %.2f [hw units] \n", maxabseta); + printf("\tMax abs(eta) %.4f \n", maxabseta / l1tpf_impl::InputTrack::VTX_ETA_SCALE); + printf("\t[Min,max] track z0 [%.2f,%.2f] [hw units] \n", minz, maxz); + printf("\t[Min,max] track z0 [%.2f,%.2f] [cm] \n", + minz / l1tpf_impl::InputTrack::Z0_SCALE, + maxz / l1tpf_impl::InputTrack::Z0_SCALE); + printf("\tPV (GEN) %u \n", pv_gen); + printf("\tPV (CMSSW) %u \n\n", pv_cmssw); + } + + return tracks_dump; +} + +std::map, TLorentzVector> get_tracks_from_coe_file(std::ifstream &cfile_, + bool print = false, + bool debug = false) { + std::map, TLorentzVector> tracks_coe; + std::string bset_string_; + int ntrackstotal(0); + bool skip(false); + + // check that we haven't reached the end of the file (i.e. there a more events to be read out) + if (cfile_.eof()) { + std::cerr << "ERROR::testDumpFile::get_tracks_from_coe_file We have already reached the end of the coe file" + << std::endl; + assert(!cfile_.eof()); + } + if (print) + printf("COE::Run \"unknown\", lumi \"unknown\", event \"unknown\", regions %lu? \n", regions_.size()); + + // read the lines one by one + for (unsigned int iline = 0; iline < NTRACKS_PER_SECTOR; iline++) { + bset_string_.resize(NBITS_PER_TRACK); + for (unsigned int isector = 0; isector < regions_.size(); isector++) { + cfile_.read(&bset_string_[0], 96); + std::bitset bset_(bset_string_); + if (bset_.none()) { + skip = true; + continue; + } else { + skip = false; + } + + std::bitset<14> hwPt; + std::bitset<16> hwVtxEta; + std::bitset<12> hwVtxPhi; + for (int i = 14 - 1; i >= 0; i--) { + hwPt.set(i, bset_[i]); + } + for (int i = 12 - 1; i >= 0; i--) { + hwVtxPhi.set(i, bset_[i + 15]); + } + for (int i = 16 - 1; i >= 0; i--) { + hwVtxEta.set(i, bset_[i + 27]); + } + float hwVtxPt_f = (float(hwPt.to_ulong()) / l1tpf_impl::CaloCluster::PT_SCALE); + float hwVtxEta_f = float(to_int64_from_bitset(hwVtxEta)) / l1tpf_impl::InputTrack::VTX_ETA_SCALE; + float hwVtxPhi_f = float(to_int64_from_bitset(hwVtxPhi)) / l1tpf_impl::InputTrack::VTX_PHI_SCALE; + + if (debug) { + std::cout << "bset_string_ = " << bset_string_ << std::endl; + std::cout << "\thwPt (0b) = " << std::flush; + for (int i = 14 - 1; i >= 0; i--) { + std::cout << bset_[i] << std::flush; + } + std::cout << std::endl; + std::cout << "\thwVtxPhi (0b) = " << std::flush; + for (int i = 12 - 1; i >= 0; i--) { + std::cout << bset_[i + 15] << std::flush; + } + std::cout << std::endl; + std::cout << "\thwVtxEta (0b) = " << std::flush; + for (int i = 16 - 1; i >= 0; i--) { + std::cout << bset_[i + 27] << std::flush; + } + std::cout << std::endl; + std::cout << "\thwPt (int) = " << hwPt.to_ulong() << std::endl; + std::cout << "\thwVtxPhi (int) = " << to_int64_from_bitset(hwVtxPhi) << std::endl; + std::cout << "\thwVtxEta (int) = " << to_int64_from_bitset(hwVtxEta) << std::endl; + std::cout << "\thwVtxPt_f (float) = " << hwVtxPt_f << std::endl; + std::cout << "\thwVtxPhi_f (float) = " << hwVtxPhi_f << std::endl; + std::cout << "\thwVtxEta_f (float) = " << hwVtxEta_f << std::endl; + } + + if (bset_.any()) { + ntrackstotal++; + tracks_coe[std::make_pair(isector, iline)] = + makeTLorentzVectorPtEtaPhiE(hwVtxPt_f, hwVtxEta_f, hwVtxPhi_f, hwVtxPt_f); + //if (print) printf("\t\t Track %u (pT,eta,phi): (%.4f,%.4f,%.4f)\n", it, hwPt_f, hwVtxEta_f, hwVtxPhi_f); + } + } + + // remove the trailing character + bset_string_.resize(2); + cfile_.read(&bset_string_[0], 2); + if (debug && !skip) + std::cout << "bset_string_ = " << bset_string_ << std::endl; + if (bset_string_ != ",\n" && bset_string_ != ";\n") { + std::cerr << "ERROR::testDumpFile::get_tracks_from_coe_file Something went wrong reading line " << 11 + iline + << " of the COE file" << std::endl + << "\tThe line should have ended with \',\' or \';\', but instead ended with \'" + << bset_string_ << "\'" << std::endl; + assert(bset_string_ != "," || bset_string_ != ";"); + } + } + for (unsigned int is = 0; is < regions_.size(); ++is) { + std::vector tracks_in_sector; + findAllInRegion(tracks_in_sector, tracks_coe, is); + if (print) + printf("\tRead region %u (eta=[%0.4f,%0.4f] phi=[%0.4f,%0.4f]) with %lu tracks\n", + is, + regions_[is].etaMin, + regions_[is].etaMax, + regions_[is].phiCenter - regions_[is].phiHalfWidth, + regions_[is].phiCenter + regions_[is].phiHalfWidth, + tracks_in_sector.size()); + for (unsigned int it = 0; it < tracks_in_sector.size(); it++) { + if (print) + printf("\t\t Track %u (pT,eta,phi): (%.4f,%.4f,%.4f)\n", + it, + tracks_coe[tracks_in_sector[it]].Pt(), + tracks_coe[tracks_in_sector[it]].Eta(), + tracks_coe[tracks_in_sector[it]].Phi()); + } + } + + if (print) { + printf("\t================================= \n"); + printf("\tTotal tracks %u \n\n", ntrackstotal); + } + + return tracks_coe; +} + +std::ifstream &GotoLine(std::ifstream &file, unsigned int num) { + file.seekg(std::ios::beg); + for (unsigned int i = 0; i < num - 1; ++i) { + file.ignore(std::numeric_limits::max(), '\n'); + } + return file; +} + +bool compare_lv_with_tolerance(TLorentzVector a, TLorentzVector b, const std::vector &tolerance = {0, 0, 0, 0}) { + /* + Example (Tolerance = 0.0005): + Track from ROOT file: pt=16.3452797 + InputTrack::INVPT_SCALE = 2E4 + std::numeric_limits::max() = 65535 + hwInvpt = std::min(round(1/pt * InputTrack::INVPT_SCALE), std::numeric_limits::max()) = 1224.0000 + floatVtxPt() = 1/(float(hwInvpt) / InputTrack::INVPT_SCALE) = 16.339869 + So loss of precision comes from rounding + Difference is DeltaPt=0.00541114807 + */ + if (abs(a.Pt() - b.Pt()) > tolerance[0] || abs(a.Eta() - b.Eta()) > tolerance[1] || + abs(a.Phi() - b.Phi()) > tolerance[2] || abs(a.E() - b.E()) > tolerance[3]) { + std::cerr << std::setprecision(9); + std::cerr << std::endl << "\tMismatching " << std::flush; + if (abs(a.Pt() - b.Pt()) > tolerance[0]) + std::cerr << "pT! " << a.Pt() << " vs " << b.Pt() << " where DeltaPt=" << abs(a.Pt() - b.Pt()) + << " and epsilon=" << tolerance[0] << std::endl; + else if (abs(a.Eta() - b.Eta()) > tolerance[1]) + std::cerr << "eta! " << a.Eta() << " vs " << b.Eta() << " where DeltaEta=" << abs(a.Eta() - b.Eta()) + << " and epsilon=" << tolerance[1] << std::endl; + else if (abs(a.Phi() - b.Phi()) > tolerance[2]) + std::cerr << "phi! " << a.Phi() << " vs " << b.Phi() << " where DeltaPhi=" << abs(a.Phi() - b.Phi()) + << " and epsilon=" << tolerance[2] << std::endl; + else if (abs(a.E() - b.E()) > tolerance[3]) + std::cerr << "E! " << a.E() << " vs " << b.E() << " where DeltaE=" << abs(a.E() - b.E()) + << " and epsilon=" << tolerance[3] << std::endl; + return false; + } + return true; +} + +bool compare_maps(TrackMap ref, TrackMap test) { + TLorentzVector tlv; + for (auto it = ref.begin(); it != ref.end(); it++) { + if (test.find(it->first) == test.end()) { + std::cerr << std::endl + << "\tERROR::compare_maps Can't find the test track with (sector,index)=(" << it->first.first << "," + << it->first.second << ")" << std::endl; + return false; + } + tlv = (test.find(it->first)->second); + // The pT tolerance should be 1.0/l1tpf_impl::CaloCluster::PT_SCALE, but because of the rounding this is not true and the actual resolution isn't always as good + // Instead, we will use max(1% of the pT of the reference TLorentzVector,0.25) + // We use the max statement because at low pT, the 1% definition doesn't hold anymore. This wouldn't be a problem if 1/pT were encoded rather than pT. + if (!compare_lv_with_tolerance( + (it->second), + tlv, + {float(std::max(it->second.Pt() * 1E-2, 1.0 / l1tpf_impl::CaloCluster::PT_SCALE)), + 1.0 / l1tpf_impl::InputTrack::VTX_ETA_SCALE, + 1.0 / l1tpf_impl::InputTrack::VTX_PHI_SCALE, + float(std::max(it->second.Pt() * 1E-2, 1.0 / l1tpf_impl::CaloCluster::PT_SCALE))})) { + std::cerr << std::endl + << "\tERROR::compare_maps Can't find the test track with TLorentzVector (" << it->second.Pt() << "," + << it->second.Eta() << "," << it->second.Phi() << "," << it->second.E() << ")" << std::endl + << "\t\tInstead found (" << tlv.Pt() << "," << tlv.Eta() << "," << tlv.Phi() << "," << tlv.E() + << ") at the position (sector,index)=(" << it->first.first << "," << it->first.second << ")" + << std::endl; + return false; + } + } + return true; +} + +int main(int argc, char *argv[]) { + // store some programatic information + std::stringstream usage; + usage << "usage: " << argv[0] + << " .root .dump .coe "; + + // load framework libraries + gSystem->Load("libFWCoreFWLite"); + FWLiteEnabler::enable(); + + // argc should be 5 for correct execution + // We print argv[0] assuming it is the program name + if (argc < 9) { + std::cerr << "ERROR::testDumpFile " << argc << " arguments provided" << std::endl; + for (int i = 0; i < argc; i++) { + std::cerr << "\tArgument " << i << ": " << argv[i] << std::endl; + } + std::cerr << usage.str() << std::endl; + return -1; + } + + // assign the command-line parameters to variables and setup the regions + std::string filename_root = argv[1]; + std::string filename_dump = argv[2]; + std::string filename_coe = argv[3]; + float etaExtra, phiExtra; + unsigned int nRegionsPhi; + std::vector etaBoundaries; + try { + etaExtra = atof(argv[4]); + phiExtra = atof(argv[5]); + nRegionsPhi = atoi(argv[6]); + std::vector etaBoundariesStrings(argv + 7, argv + argc); + std::size_t pos; + for (unsigned int i = 0; i < etaBoundariesStrings.size(); i++) { + etaBoundaries.push_back(std::stoi(etaBoundariesStrings[i], &pos)); + if (pos < etaBoundariesStrings[i].size()) { + std::cerr << "Trailing characters after number: " << etaBoundariesStrings[i] << '\n'; + } + } + float phiWidth = 2 * M_PI / nRegionsPhi; + for (unsigned int ieta = 0, neta = etaBoundaries.size() - 1; ieta < neta; ++ieta) { + for (unsigned int iphi = 0; iphi < nRegionsPhi; ++iphi) { + float phiCenter = (iphi + 0.5) * phiWidth - M_PI; + regions_.push_back(l1tpf_impl::Region(etaBoundaries[ieta], + etaBoundaries[ieta + 1], + phiCenter, + phiWidth, + phiExtra, + etaExtra, + false, + 0, + 0, + 0, + 0, + 0, + 0)); + } + } + } catch (std::invalid_argument const &ex) { + std::cerr << "Invalid number in one of the eta-phi arguments" << std::endl; + return -2; + } catch (std::out_of_range const &ex) { + std::cerr << "Number out of range in one of the eta-phi arguments" << std::endl; + return -3; + } + + // check the filenames + if (filename_root.find(".root") == std::string::npos) { + std::cerr << "ERROR::testDumpFile Filename 1 must be a ROOT (.root) file" << std::endl << usage.str() << std::endl; + return -4; + } else if (filename_dump.find(".dump") == std::string::npos) { + std::cerr << "ERROR::testDumpFile Filename 2 must be a binary (.dump) file" << std::endl + << usage.str() << std::endl; + return -5; + } else if (filename_coe.find(".coe") == std::string::npos) { + std::cerr << "ERROR::testDumpFile Filename 3 must be a COE (.coe) file" << std::endl << usage.str() << std::endl; + return -6; + } + + // report the program configuraion + std::cout << "Configuration:" << std::endl + << "==============" << std::endl + << "Number of tests (events): " << NTEST << std::endl + << "Report every N tests: " << REPORT_EVERY_N << std::endl + << "Number of regions (in eta-phi): " << regions_.size() << std::endl; + for (unsigned int iregion = 0; iregion < regions_.size(); iregion++) { + printf("\t%i : eta=[%0.4f,%0.4f] phi=[%0.4f,%0.4f]\n", + iregion, + regions_[iregion].etaMin, + regions_[iregion].etaMax, + regions_[iregion].phiCenter - regions_[iregion].phiHalfWidth, + regions_[iregion].phiCenter + regions_[iregion].phiHalfWidth); + } + std::cout << "Number of tracks per sector: " << NTRACKS_PER_SECTOR << std::endl + << "Number of bits per track: " << NBITS_PER_TRACK << std::endl + << "==============" << std::endl + << std::endl; + + // open the files for testing + TFile *rfile_ = TFile::Open(filename_root.c_str(), "READ"); + if (!rfile_) { + std::cerr << "ERROR::testDumpFile Cannot open '" << filename_root << "'" << std::endl; + return -7; + } + fwlite::Event rfileentry_(rfile_); + FILE *dfile_(fopen(filename_dump.c_str(), "rb")); + if (!dfile_) { + std::cerr << "ERROR::testDumpFile Cannot read '" << filename_dump << "'" << std::endl; + return -8; + } + std::ifstream cfile_(filename_coe); + if (!cfile_) { + std::cerr << "ERROR::testDumpFile Cannot read '" << filename_coe << "'" << std::endl; + return -9; + } + GotoLine(cfile_, 11); //Skip the header of the COE file + + TrackMap tracks_root, tracks_dump, tracks_coe; + + // run the tests for multiple events + for (int test = 1; test <= NTEST; ++test) { + if (test % REPORT_EVERY_N == 1) + std::cout << "Doing test " << test << " ... " << std::endl; + + tracks_root = get_tracks_from_root_file(rfileentry_, test - 1, test == 1); + tracks_dump = get_tracks_from_dump_file(dfile_, test == 1); + tracks_coe = get_tracks_from_coe_file(cfile_, test == 1); + + if (test % REPORT_EVERY_N == 1) + std::cout << "Comparing the ROOT tracks to the dump tracks in event " << test << " ... " << std::flush; + if (!compare_maps(tracks_root, tracks_dump)) + return -10; + if (test % REPORT_EVERY_N == 1) + std::cout << "DONE" << std::endl; + + if (test % REPORT_EVERY_N == 1) + std::cout << "Comparing the ROOT tracks to the coe tracks in event " << test << " ... " << std::flush; + if (!compare_maps(tracks_root, tracks_coe)) + return -11; + if (test % REPORT_EVERY_N == 1) + std::cout << "DONE" << std::endl << std::endl; + } + + std::cout << std::endl << "The dump and coe outputs match the ROOT outputs for all events!" << std::endl; + return 0; +} + +/* +USE: +g++ -I/uscms_data/d2/aperloff/YOURWORKINGAREA/TSABoard/slc7/CMSSW_10_6_0_pre4/src/L1Trigger/Phase2L1ParticleFlow/interface/ -O0 -g3 -Wall -std=c++0x -c -fmessage-length=0 testDumpFile.cpp +g++ -o testDumpFile testDumpFile.o +./testDumpFile trackerRegion_alltracks_sectors_1x18_TTbar_PU200.dump 18 + +scram b runtests +*/ From db2d1e5302a8cfda22f9a24dccf2c53b65ac5cdc Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 1 Jul 2020 14:38:41 +0200 Subject: [PATCH 2/8] Use phase2_trackerV14 to enable TrackTriggerTracks based modules in phase2 L1T sequence. --- .../Configuration/python/SimL1Emulator_cff.py | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/L1Trigger/Configuration/python/SimL1Emulator_cff.py b/L1Trigger/Configuration/python/SimL1Emulator_cff.py index d10d1df76f0d2..2d8e527c04c7e 100644 --- a/L1Trigger/Configuration/python/SimL1Emulator_cff.py +++ b/L1Trigger/Configuration/python/SimL1Emulator_cff.py @@ -77,6 +77,9 @@ from L1Trigger.L1CaloTrigger.l1EGammaEEProducer_cfi import * _phase2_siml1emulator.add(l1EGammaEEProducer) +from Configuration.Eras.Modifier_phase2_trigger_cff import phase2_trigger +phase2_trigger.toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator) + # Tk + StandaloneObj, including L1TkPrimaryVertex # ######################################################################## from L1Trigger.L1TTrackMatch.L1TkObjectProducers_cff import * @@ -96,6 +99,22 @@ _phase2_siml1emulator.add( L1TkMuons ) - -from Configuration.Eras.Modifier_phase2_trigger_cff import phase2_trigger -phase2_trigger.toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator) +#%% # PF Candidates +#%% # ######################################################################## +from L1Trigger.Phase2L1ParticleFlow.l1ParticleFlow_cff import * +_phase2_siml1emulator.add(l1ParticleFlowTask) + +from L1Trigger.Phase2L1ParticleFlow.l1pfJetMet_cff import * +# Describe here l1PFJets Task +# ############################### +l1PFJetsTask = cms.Task( + ak4PFL1Calo , ak4PFL1PF , ak4PFL1Puppi , + ak4PFL1CaloCorrected , ak4PFL1PFCorrected , ak4PFL1PuppiCorrected) +_phase2_siml1emulator.add(l1PFJetsTask) +# Describe here l1PFMets Task +# ############################### +l1PFMetsTask = cms.Task(l1PFMetCalo , l1PFMetPF , l1PFMetPuppi) +_phase2_siml1emulator.add(l1PFMetsTask) + +from Configuration.Eras.Modifier_phase2_trackerV14_cff import phase2_trackerV14 +(phase2_trigger & phase2_trackerV14).toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator) From 7510a560e1146c49fbac9ebbb91f9c47faf6bcb5 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 1 Jul 2020 18:14:04 +0200 Subject: [PATCH 3/8] Adjust path to moved file. --- L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py b/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py index 86a99ba09f648..dfcf307e6038e 100644 --- a/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py +++ b/L1Trigger/Phase2L1ParticleFlow/python/l1pfJetMet_cff.py @@ -1,6 +1,6 @@ import FWCore.ParameterSet.Config as cms -from RecoMET.METProducers.PFMET_cfi import pfMet as _pfMet +from RecoMET.METProducers.pfMet_cfi import pfMet as _pfMet _pfMet.calculateSignificance = False l1PFMetCalo = _pfMet.clone(src = "l1pfCandidates:Calo") l1PFMetPF = _pfMet.clone(src = "l1pfCandidates:PF") From 4c540eba9ac9ef0d9b757d573006fde003cfd3b0 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 1 Jul 2020 18:25:22 +0200 Subject: [PATCH 4/8] Clean-up and organize neatly. Use separate task for TrackTrigger dependent modules. --- .../Configuration/python/SimL1Emulator_cff.py | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/L1Trigger/Configuration/python/SimL1Emulator_cff.py b/L1Trigger/Configuration/python/SimL1Emulator_cff.py index 2d8e527c04c7e..77fa7966ac1e1 100644 --- a/L1Trigger/Configuration/python/SimL1Emulator_cff.py +++ b/L1Trigger/Configuration/python/SimL1Emulator_cff.py @@ -64,13 +64,11 @@ _phase2_siml1emulator.add(hgcalTriggerPrimitivesTask) from Configuration.Eras.Modifier_phase2_hgcal_cff import phase2_hgcal -#phase2_hgcal.toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator ) - from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 (phase2_hgcal & ~phase2_hgcalV11).toReplaceWith( SimL1EmulatorTask, _phase2_siml1emulator ) -#%% # Barrel EGamma -#%% # ######################################################################## +# Barrel EGamma +# ######################################################################## from L1Trigger.L1CaloTrigger.L1EGammaCrystalsEmulatorProducer_cfi import * _phase2_siml1emulator.add(L1EGammaClusterEmuProducer) @@ -80,41 +78,51 @@ from Configuration.Eras.Modifier_phase2_trigger_cff import phase2_trigger phase2_trigger.toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator) + + +# ######################################################################## +# TrackTrigger dependend modules below +# ######################################################################## +_phase2_siml1emulator_ttrack = SimL1EmulatorTask.copy() + + # Tk + StandaloneObj, including L1TkPrimaryVertex # ######################################################################## from L1Trigger.L1TTrackMatch.L1TkObjectProducers_cff import * -_phase2_siml1emulator.add(L1TkPrimaryVertex) +_phase2_siml1emulator_ttrack.add(L1TkPrimaryVertex) -_phase2_siml1emulator.add(L1TkElectronsCrystal) -_phase2_siml1emulator.add(L1TkElectronsLooseCrystal) -_phase2_siml1emulator.add(L1TkElectronsEllipticMatchCrystal) -_phase2_siml1emulator.add(L1TkIsoElectronsCrystal) -_phase2_siml1emulator.add(L1TkPhotonsCrystal) +_phase2_siml1emulator_ttrack.add(L1TkElectronsCrystal) +_phase2_siml1emulator_ttrack.add(L1TkElectronsLooseCrystal) +_phase2_siml1emulator_ttrack.add(L1TkElectronsEllipticMatchCrystal) +_phase2_siml1emulator_ttrack.add(L1TkIsoElectronsCrystal) +_phase2_siml1emulator_ttrack.add(L1TkPhotonsCrystal) -_phase2_siml1emulator.add(L1TkElectronsHGC) -_phase2_siml1emulator.add(L1TkElectronsEllipticMatchHGC) -_phase2_siml1emulator.add(L1TkIsoElectronsHGC) -_phase2_siml1emulator.add(L1TkPhotonsHGC) +_phase2_siml1emulator_ttrack.add(L1TkElectronsHGC) +_phase2_siml1emulator_ttrack.add(L1TkElectronsEllipticMatchHGC) +_phase2_siml1emulator_ttrack.add(L1TkIsoElectronsHGC) +_phase2_siml1emulator_ttrack.add(L1TkPhotonsHGC) -_phase2_siml1emulator.add( L1TkMuons ) +_phase2_siml1emulator_ttrack.add( L1TkMuons ) -#%% # PF Candidates -#%% # ######################################################################## +# PF Candidates +# ######################################################################## from L1Trigger.Phase2L1ParticleFlow.l1ParticleFlow_cff import * -_phase2_siml1emulator.add(l1ParticleFlowTask) +_phase2_siml1emulator_ttrack.add(l1ParticleFlowTask) +# PF JetMET +# ######################################################################## from L1Trigger.Phase2L1ParticleFlow.l1pfJetMet_cff import * # Describe here l1PFJets Task # ############################### l1PFJetsTask = cms.Task( ak4PFL1Calo , ak4PFL1PF , ak4PFL1Puppi , ak4PFL1CaloCorrected , ak4PFL1PFCorrected , ak4PFL1PuppiCorrected) -_phase2_siml1emulator.add(l1PFJetsTask) +_phase2_siml1emulator_ttrack.add(l1PFJetsTask) # Describe here l1PFMets Task # ############################### l1PFMetsTask = cms.Task(l1PFMetCalo , l1PFMetPF , l1PFMetPuppi) -_phase2_siml1emulator.add(l1PFMetsTask) +_phase2_siml1emulator_ttrack.add(l1PFMetsTask) from Configuration.Eras.Modifier_phase2_trackerV14_cff import phase2_trackerV14 -(phase2_trigger & phase2_trackerV14).toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator) +(phase2_trigger & phase2_trackerV14).toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator_ttrack) From 3540ca4eeb49e5ed601bed1cebd98a982e6d4ca4 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 1 Jul 2020 19:08:26 +0200 Subject: [PATCH 5/8] Organize and document better and subdivide Phase2 modules into taks for TriggerPrimitives, L1T , and L1T that depend on TrackTrigger tracks. --- .../Configuration/python/SimL1Emulator_cff.py | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/L1Trigger/Configuration/python/SimL1Emulator_cff.py b/L1Trigger/Configuration/python/SimL1Emulator_cff.py index 77fa7966ac1e1..13b21a84225aa 100644 --- a/L1Trigger/Configuration/python/SimL1Emulator_cff.py +++ b/L1Trigger/Configuration/python/SimL1Emulator_cff.py @@ -58,16 +58,36 @@ # soon to be removed when availble in GTs from L1Trigger.L1TTwinMux.fakeTwinMuxParams_cff import * -# Customisation for the phase2_hgcal era. Includes the HGCAL L1 trigger -from L1Trigger.L1THGCal.hgcalTriggerPrimitives_cff import * -_phase2_siml1emulator = SimL1EmulatorTask.copy() -_phase2_siml1emulator.add(hgcalTriggerPrimitivesTask) +# ######################################################################## +# ######################################################################## +# +# Phase-2 +# +# ######################################################################## +# ######################################################################## + +# ######################################################################## +# Phase-2 Trigger Primitives +# ######################################################################## +_phase2_tp = SimL1EmulatorTask.copy() + +# HGCAL TP +# ######################################################################## +from L1Trigger.L1THGCal.hgcalTriggerPrimitives_cff import * +_phase2_tp.add(hgcalTriggerPrimitivesTask) + +# --> add modules from Configuration.Eras.Modifier_phase2_hgcal_cff import phase2_hgcal -from Configuration.Eras.Modifier_phase2_hgcalV11_cff import phase2_hgcalV11 -(phase2_hgcal & ~phase2_hgcalV11).toReplaceWith( SimL1EmulatorTask, _phase2_siml1emulator ) +phase2_hgcal.toReplaceWith( SimL1EmulatorTask , _phase2_tp ) + + +# ######################################################################## +# Phase 2 L1T +# ######################################################################## +_phase2_siml1emulator = SimL1EmulatorTask.copy() -# Barrel EGamma +# Barrel and EndCap EGamma # ######################################################################## from L1Trigger.L1CaloTrigger.L1EGammaCrystalsEmulatorProducer_cfi import * _phase2_siml1emulator.add(L1EGammaClusterEmuProducer) @@ -75,17 +95,16 @@ from L1Trigger.L1CaloTrigger.l1EGammaEEProducer_cfi import * _phase2_siml1emulator.add(l1EGammaEEProducer) +# --> add modules from Configuration.Eras.Modifier_phase2_trigger_cff import phase2_trigger phase2_trigger.toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator) - # ######################################################################## -# TrackTrigger dependend modules below +# Phase-2 L1T - TrackTrigger dependent modules # ######################################################################## _phase2_siml1emulator_ttrack = SimL1EmulatorTask.copy() - # Tk + StandaloneObj, including L1TkPrimaryVertex # ######################################################################## from L1Trigger.L1TTrackMatch.L1TkObjectProducers_cff import * @@ -124,5 +143,6 @@ l1PFMetsTask = cms.Task(l1PFMetCalo , l1PFMetPF , l1PFMetPuppi) _phase2_siml1emulator_ttrack.add(l1PFMetsTask) +# --> add modules from Configuration.Eras.Modifier_phase2_trackerV14_cff import phase2_trackerV14 (phase2_trigger & phase2_trackerV14).toReplaceWith( SimL1EmulatorTask , _phase2_siml1emulator_ttrack) From 4c4fa068d960c887d03d68aa7c61743e66ae25ea Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 2 Jul 2020 15:16:10 +0200 Subject: [PATCH 6/8] Initialize token. --- .../Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc index 5c53c47334c24..aee20808fbb8a 100644 --- a/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc +++ b/L1Trigger/Phase2L1ParticleFlow/plugins/PFTrackProducerFromL1Tracks.cc @@ -34,6 +34,7 @@ namespace l1tpf { l1tpf::PFTrackProducerFromL1Tracks::PFTrackProducerFromL1Tracks(const edm::ParameterSet &iConfig) : TrackTag_(consumes>(iConfig.getParameter("L1TrackTag"))), + BFieldTag_{esConsumes()}, nParam_(iConfig.getParameter("nParam")), resolCalo_(iConfig.getParameter("resolCalo")), resolTrk_(iConfig.getParameter("resolTrack")) { From 13872db2eee233370fc9602d15fcd47874cbb7a4 Mon Sep 17 00:00:00 2001 From: silviodonato Date: Fri, 3 Jul 2020 00:19:36 +0200 Subject: [PATCH 7/8] add missing L1T missing dataformat in class_def.xml --- DataFormats/L1Trigger/src/classes_def.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/DataFormats/L1Trigger/src/classes_def.xml b/DataFormats/L1Trigger/src/classes_def.xml index bda2ba8e99937..2508febd4da71 100644 --- a/DataFormats/L1Trigger/src/classes_def.xml +++ b/DataFormats/L1Trigger/src/classes_def.xml @@ -261,4 +261,5 @@ + From af539f48796355f9879197051e63a8111581f8e7 Mon Sep 17 00:00:00 2001 From: silviodonato Date: Fri, 3 Jul 2020 07:04:09 +0200 Subject: [PATCH 8/8] add missing L1T missing dataformat in class_def.xml --- DataFormats/L1Trigger/src/classes_def.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataFormats/L1Trigger/src/classes_def.xml b/DataFormats/L1Trigger/src/classes_def.xml index 2508febd4da71..26dca25863157 100644 --- a/DataFormats/L1Trigger/src/classes_def.xml +++ b/DataFormats/L1Trigger/src/classes_def.xml @@ -261,5 +261,5 @@ - +