ATLAS Offline Software
Loading...
Searching...
No Matches
GepEtaSoftKillerAlg.cxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4
6
8
9#include <vector>
10#include <algorithm>
11#include <cmath>
12
14 ISvcLocator* pSvcLocator)
15 : AthReentrantAlgorithm(name, pSvcLocator) {}
16
17
19 ATH_MSG_INFO("Initializing " << name() << "...");
20 ATH_CHECK(m_inputClustersKey.initialize());
21 ATH_CHECK(m_outputClustersKey.initialize());
22 return StatusCode::SUCCESS;
23}
24
25
26StatusCode GepEtaSoftKillerAlg::execute(const EventContext& ctx) const {
27 ATH_MSG_DEBUG("Executing " << name() << "...");
28
29 auto h_input = SG::makeHandle(m_inputClustersKey, ctx);
30 ATH_CHECK(h_input.isValid());
31 const auto& inputClusters = *h_input;
32
33 ATH_MSG_DEBUG("Read in " << inputClusters.size() << " input clusters/towers");
34
35 // Build the eta-phi grid
36 const double minEta = -m_etaMax;
37 const double maxEta = m_etaMax;
38 const double twopi = 2.0 * M_PI;
39
40 int neta = std::max(static_cast<int>((maxEta - minEta) / m_gridEtaSize + 0.5), 1);
41 double deta = (maxEta - minEta) / neta;
42
43 int nphi = static_cast<int>(twopi / m_gridPhiSize + 0.5);
44 double dphi = twopi / nphi;
45
46 std::vector<double> eta_bins(neta + 1);
47 const double minPhi = -M_PI;
48 std::vector<double> phi_bins(nphi + 1);
49
50 for (int i = 0; i <= neta; ++i) {
51 eta_bins[i] = minEta + i * deta;
52 }
53 for (int i = 0; i <= nphi; ++i) {
54 phi_bins[i] = minPhi + i * dphi;
55 }
56
57 // Grid: each cell holds pointers to its clusters
58 std::vector<std::vector<std::vector<const xAOD::CaloCluster*>>>
59 grid(neta, std::vector<std::vector<const xAOD::CaloCluster*>>(nphi));
60
61 for (const auto* cluster : inputClusters) {
62 double eta = cluster->eta();
63 double phi = cluster->phi();
64
65 int eta_bin = static_cast<int>(
66 std::distance(eta_bins.begin(),
67 std::upper_bound(eta_bins.begin(), eta_bins.end(), eta))) - 1;
68 int phi_bin = static_cast<int>(
69 std::distance(phi_bins.begin(),
70 std::upper_bound(phi_bins.begin(), phi_bins.end(), phi))) - 1;
71
72 if (eta_bin >= 0 && eta_bin < neta && phi_bin >= 0 && phi_bin < nphi) {
73 grid[eta_bin][phi_bin].push_back(cluster);
74 }
75 }
76
77 // Compute the median of max-pT values per eta-band
78 // Empty grid cells contribute 0.0 (consistent with FastJet SoftKiller)
79 const int bandWidth = m_etaBandWidth;
80 int nBands = (neta + bandWidth - 1) / bandWidth;
81 std::vector<double> medianPerBand(nBands, 0.0);
82
83 for (int band = 0; band < nBands; ++band) {
84 std::vector<double> maxPtValues;
85 int etaStart = band * bandWidth;
86 int etaEnd = std::min(etaStart + bandWidth, neta);
87
88 for (int j = 0; j < nphi; ++j) {
89 for (int i = etaStart; i < etaEnd; ++i) {
90 const auto& cell = grid[i][j];
91 if (!cell.empty()) {
92 double maxPt = (*std::max_element(
93 cell.begin(), cell.end(),
94 [](const xAOD::CaloCluster* a,
95 const xAOD::CaloCluster* b) {
96 return a->pt() < b->pt();
97 }))->pt();
98 maxPtValues.push_back(maxPt);
99 } else {
100 maxPtValues.push_back(0.0);
101 }
102 }
103 }
104
105 if (!maxPtValues.empty()) {
106 std::sort(maxPtValues.begin(), maxPtValues.end());
107 size_t mid = maxPtValues.size() / 2;
108 if (maxPtValues.size() % 2 == 0) {
109 medianPerBand[band] =
110 (maxPtValues[mid - 1] + maxPtValues[mid]) / 2.0;
111 } else {
112 medianPerBand[band] = maxPtValues[mid];
113 }
114 }
115
116 ATH_MSG_DEBUG("Eta band " << band
117 << " [" << eta_bins[etaStart] << ", "
118 << eta_bins[etaEnd] << ")"
119 << ": median pT threshold = " << medianPerBand[band]);
120 }
121
122 // Create output container
125 ATH_CHECK(h_output.record(
126 std::make_unique<xAOD::CaloClusterContainer>(),
127 std::make_unique<xAOD::CaloClusterAuxContainer>()));
128
129 // Apply eta-dependent pT threshold:
130 // weight = 1.0 if cluster pT > band median, else 0.0
131 for (int i = 0; i < neta; ++i) {
132 int band = i / bandWidth;
133 double threshold = medianPerBand[band];
134
135 for (int j = 0; j < nphi; ++j) {
136 for (const auto* cluster : grid[i][j]) {
137 double weight = (cluster->pt() > threshold) ? 1.0 : 0.0;
138
139 auto* out = h_output->push_back(
140 std::make_unique<xAOD::CaloCluster>());
141 out->setE(cluster->e() * weight);
142 out->setEta(cluster->eta());
143 out->setPhi(cluster->phi());
144 out->setM(cluster->m());
145 }
146 }
147 }
148
149 ATH_MSG_DEBUG("Output " << h_output->size()
150 << " clusters after EtaSoftKiller");
151
152 return StatusCode::SUCCESS;
153}
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
static Double_t a
constexpr double twopi
An algorithm that can be simultaneously executed in multiple threads.
Gaudi::Property< double > m_gridPhiSize
virtual StatusCode initialize() override
Gaudi::Property< double > m_etaMax
SG::ReadHandleKey< xAOD::CaloClusterContainer > m_inputClustersKey
virtual StatusCode execute(const EventContext &ctx) const override
Gaudi::Property< double > m_gridEtaSize
SG::WriteHandleKey< xAOD::CaloClusterContainer > m_outputClustersKey
Gaudi::Property< int > m_etaBandWidth
GepEtaSoftKillerAlg(const std::string &name, ISvcLocator *pSvcLocator)
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.