ATLAS Offline Software
Loading...
Searching...
No Matches
ElectronPhotonVariableNFCorrectionTool.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
9
11
12#include "TEnv.h"
13#include "TString.h"
14
15#include <cmath>
16#include <algorithm>
17
18
19
20// Ordered list of shower shapes used by the tool
21// The order must match the ONNX model inputs and outputs
22const std::vector<std::string> ElectronPhotonVariableNFCorrectionTool::s_ssVarNames = {
23 "weta2", "weta1", "Rphi", "Reta", "wtots1", "Rhad", "Rhad1", "f1", "fracs1", "DeltaE", "Eratio"
24};
25
26// Mapping of shower shapes to xAOD enums (same order as s_ssVarNames)
27const std::vector<xAOD::EgammaParameters::ShowerShapeType> ElectronPhotonVariableNFCorrectionTool::s_ssEnums = {
39};
40
41
42// Constructor, declares properties
46
47// Select fold index based on event number (and optionally pT)
48int ElectronPhotonVariableNFCorrectionTool::selectFold(unsigned long long eventNumber, float phi) const
49{
50 if (m_nFolds <= 1) return 0;
51
52 unsigned long long key = eventNumber;
53
55 const long long phiBin = static_cast<long long>(std::floor((phi + static_cast<float>(M_PI)) * 100.0f));
56 key = eventNumber + static_cast<unsigned long long>(phiBin);
57 }
58
59 return static_cast<int>(key % m_nFolds);
60}
61
62// Convert string from config to fold strategy
65{
66 if (s == "eventNumber") return FoldStrategy::EventNumber;
67 if (s == "eventNumber_phi") return FoldStrategy::EventNumberPhi;
68 ATH_MSG_WARNING("Unknown FoldStrategy '" << s << "'");
70}
71
73 const xAOD::Photon& photon,
74 const std::vector<float>& ss) const
75{
76 // pT cut
77 if (photon.pt() < m_pTcutMeV) return false;
78
79 // TruthType cut
81 static const SG::AuxElement::Accessor<int> acc_truthType("truthType");
82 if (!acc_truthType.isAvailable(photon)) {
83 ATH_MSG_WARNING("ApplyTo = TruthPhotons but truthType not available — skipping photon");
84 return false;
85 }
86 int truthType = acc_truthType(photon);
87 if (truthType < 13 || truthType > 15) return false;
88 }
89
90 // Shower shape cuts
92 // weta2
93 if (ss[0] <= -10.f || ss[0] >= 10.f) return false;
94 // weta1
95 if (ss[1] <= -10.f || ss[1] >= 10.f) return false;
96 // Rphi
97 if (ss[2] <= -10.f || ss[2] >= 10.f) return false;
98 // Reta
99 if (ss[3] <= -10.f || ss[3] >= 10.f) return false;
100 // wtots1
101 if (ss[4] < -2.f || ss[4] >= 10.f) return false;
102 // Rhad
103 if (ss[5] < -2.f || ss[5] > 2.f) return false;
104 // Rhad1
105 if (ss[6] < -2.f || ss[6] > 2.f) return false;
106 // f1
107 if (ss[7] <= -2.f || ss[7] >= 2.f) return false;
108 // fracs1
109 if (ss[8] <= -2.f || ss[8] >= 5.f) return false;
110 // DeltaE
111 if (ss[9] < 0.f || ss[9] >= 5000.f) return false;
112 // Eratio
113 if (ss[10] < 0.f || ss[10] > 1.f) return false;
114 }
115
116 return true;
117}
118
119
120
121// Initialize tool: read config, setup ONNX tools and accessors
123{
124 if (m_configFile.empty()) {
125 ATH_MSG_ERROR("ConfigFile property is empty. Please provide a config file to the tool.");
126 return StatusCode::FAILURE;
127 }
128
129 std::string resolvedConfig = PathResolverFindCalibFile(m_configFile);
130 if (resolvedConfig.empty()) {
131 ATH_MSG_ERROR("Failed to resolve config file \"" << m_configFile << "\"");
132 return StatusCode::FAILURE;
133 }
134 ATH_MSG_DEBUG("Use configuration file " << m_configFile);
135
136 TEnv env;
137 env.ReadFile(resolvedConfig.c_str(), kEnvLocal);
138 env.IgnoreDuplicates(false);
139
140 const int nFoldsConfig = env.GetValue("NFolds", 0);
141 if (nFoldsConfig <= 0) {
142 ATH_MSG_ERROR("NFolds not set or invalid in config: " << resolvedConfig);
143 return StatusCode::FAILURE;
144 }
145
146 if (m_nFoldsOverride > 0) {
147 if (m_nFoldsOverride > nFoldsConfig) {
148 ATH_MSG_ERROR("NFoldsOverride (" << m_nFoldsOverride.value() << ") exceeds NFolds in config (" << nFoldsConfig << ")");
149 return StatusCode::FAILURE;
150 }
152 } else {
153 m_nFolds = nFoldsConfig;
154 }
155
156 TString pattern = env.GetValue("ONNXnamePattern", "");
157 if (pattern.IsNull()) {
158 ATH_MSG_ERROR("ONNXnamePattern not set in config: " << resolvedConfig);
159 return StatusCode::FAILURE;
160 }
161 m_onnxPattern = pattern.Data();
162
163
164 TString fs = env.GetValue("FoldStrategy", "eventNumber");
165 std::string fsStr = fs.Data();
166
168
170 ATH_MSG_ERROR("FoldStrategy must be 'eventNumber' or 'eventNumber_phi', but got '" << fsStr << "' in config: " << resolvedConfig);
171 return StatusCode::FAILURE;
172 }
173
174
175 ATH_MSG_VERBOSE("NFolds = " << m_nFolds << ", pattern = " << m_onnxPattern << ", FoldStrategy = " << fsStr);
176
177 if (static_cast<int>(m_onnxToolsForward.size()) != m_nFolds ||
178 static_cast<int>(m_onnxToolsBackward.size()) != m_nFolds) {
179 ATH_MSG_ERROR("Expected "<<m_nFolds<<" forward/backward tools, "<< "but got "<<m_onnxToolsForward.size()<<" / "<< m_onnxToolsBackward.size());
180 return StatusCode::FAILURE;
181 }
182
183
185 else if (m_applyToStr == "All") m_applyToMode = ApplyToMode::All;
186 else {
187 ATH_MSG_ERROR("ApplyTo must be TruthPhotons or All, but got '" << m_applyToStr << "'");
188 return StatusCode::FAILURE;
189 }
190
191 // Cuts on SS vars to remove default values
192 m_applyShowerShapeCuts = (env.GetValue("ApplyShowerShapeCuts", 1) == 1);
193
194 ATH_MSG_INFO("ApplyTo = " << m_applyToStr << ", pTcut=" << m_pTcutMeV << " MeV, ApplyShowerShapeCuts=" << m_applyShowerShapeCuts);
195
196
197 ATH_CHECK(m_onnxToolsForward.retrieve());
198 ATH_CHECK(m_onnxToolsBackward.retrieve());
199
200 if (msgLvl(MSG::DEBUG)) {
201 for (int i = 0; i < m_nFolds; ++i) {
202 ATH_MSG_VERBOSE("Fold " << i << " forward model info:");
203 m_onnxToolsForward[i]->printModelInfo();
204 ATH_MSG_VERBOSE("Fold " << i << " backward model info:");
205 m_onnxToolsBackward[i]->printModelInfo();
206 }
207 }
208
209 // Prepare decorations for each shower shape
210 m_accessors.resize(s_ssVarNames.size());
211 for (size_t i = 0; i < s_ssVarNames.size(); ++i) {
212 const std::string& var = s_ssVarNames[i];
213 m_accessors[i].original = std::make_unique<SG::AuxElement::Accessor<float>>(var + "_original");
214 }
215
216 ATH_CHECK(m_eventInfoKey.initialize());
217
218 ATH_MSG_INFO("NF correction tool initialized with " << m_nFolds << " folds. ");
219
220 return StatusCode::SUCCESS;
221}
222
223
224// Apply NF correction to photon shower shapes.
226{
227
228 const size_t nSS = s_ssEnums.size();
229 std::vector<float> ss(nSS);
230
231 // Read shower shapes, then store original values
232 for (size_t i = 0; i < nSS; ++i) {
233 ss[i] = photon.showerShapeValue(s_ssEnums[i]);
234 (*m_accessors[i].original)(photon) = ss[i];
235 }
236
237
238 static const SG::AuxElement::Decorator<char> dec_pass("NFCorrectedShowerShapes");
239
240 // Photon selection
241 bool pass = passSelectionCuts(photon, ss);
242
243 dec_pass(photon) = pass ? 1 : 0;
244
245 if (!pass) {
246 // If selection is not passed, then SS value will be same to original
248 }
249
250
251 // Get event info and select fold
253 if (!h.isValid()) {
254 ATH_MSG_ERROR("Failed to read EventInfo via key " << m_eventInfoKey.key());
256 }
257
258 const unsigned long long eventNumber = h->eventNumber();
259 float ptGeV = photon.pt() / 1000.0f;
260 const float phi = static_cast<float>(photon.phi());
261 const int fold = selectFold(eventNumber, phi);
262
263
264 if (fold < 0 || fold >= m_nFolds) {
265 ATH_MSG_ERROR("Selected fold " << fold << " out of range [0," << (m_nFolds-1) << "]");
267 }
268
269
270 // Kinematic inputs
271 const bool isConv = photon.conversionType() != xAOD::EgammaParameters::unconverted;
272 std::vector<float> kinematic = {
273 ptGeV,
274 static_cast<float>(photon.eta()),
275 static_cast<float>(photon.phi()),
276 static_cast<float>(isConv)
277 };
278
279 // Forward inference
280 std::vector<Ort::Value> inputTensors;
281
282 const auto& onnxToolForward = m_onnxToolsForward[fold];
283
284 // index 0 is for kinematics
285 int64_t batchSizeKin = onnxToolForward->getBatchSize(
286 static_cast<int64_t>(kinematic.size()), 0);
287 if (onnxToolForward->addInput(inputTensors, kinematic, 0, batchSizeKin).isFailure()) {
288 ATH_MSG_ERROR("Fold " << fold << ": failed to add kinematic input tensor");
290 }
291
292 // index 1 is for shower shape varibales
293 int64_t batchSizeSS = onnxToolForward->getBatchSize(
294 static_cast<int64_t>(ss.size()), 1);
295 if (onnxToolForward->addInput(inputTensors, ss, 1, batchSizeSS).isFailure()) {
296 ATH_MSG_ERROR("Fold " << fold << ": failed to add shower shape input tensor");
298 }
299
300 std::vector<Ort::Value> outputTensors;
301 std::vector<float> outputData;
302 if (onnxToolForward->addOutput(outputTensors, outputData, 0, batchSizeKin).isFailure()) {
303 ATH_MSG_ERROR("Fold " << fold << ": failed to add forward output tensor");
305 }
306
307 if (onnxToolForward->inference(inputTensors, outputTensors).isFailure()) {
308 ATH_MSG_ERROR("Fold " << fold << ": forward inference failed");
310 }
311
312 float* zPtr = outputTensors[0].GetTensorMutableData<float>();
313 std::vector<float> zVec(zPtr, zPtr + nSS);
314
315
316 // Backward inference
317 std::vector<Ort::Value> inputTensorsBack;
318 std::vector<Ort::Value> outputTensorsBack;
319 std::vector<float> outputDataBack;
320
321 const auto& onnxToolBackward = m_onnxToolsBackward[fold];
322
323 // index 0 is for kinematics
324 int64_t batchSizeKinBack = onnxToolBackward->getBatchSize(
325 static_cast<int64_t>(kinematic.size()), 0);
326 if (onnxToolBackward->addInput(inputTensorsBack, kinematic, 0, batchSizeKinBack).isFailure()) {
327 ATH_MSG_ERROR("Fold " << fold << ": failed to add kinematic input tensor for backward model");
329 }
330
331 // index 1 is for shower shapes in latent space
332 int64_t batchSizeZBack = onnxToolBackward->getBatchSize(static_cast<int64_t>(zVec.size()), 1);
333
334 if (onnxToolBackward->addInput(inputTensorsBack, zVec, 1, batchSizeZBack).isFailure()) {
335 ATH_MSG_ERROR("Fold " << fold << ": failed to add z input tensor for backward model");
337 }
338
339 // index 2 is for original shower shapes (models use them to cut on std values [-5, 5])
340 if (onnxToolBackward->addInput(inputTensorsBack, ss, 2, batchSizeKinBack).isFailure()) {
341 ATH_MSG_ERROR("Fold " << fold << ": failed to add original SS input tensor for backward model");
343 }
344
345 if (onnxToolBackward->addOutput(outputTensorsBack, outputDataBack, 0, batchSizeZBack).isFailure()) {
346 ATH_MSG_ERROR("Fold " << fold << ": failed to add backward output tensor");
348 }
349
350 if (onnxToolBackward->inference(inputTensorsBack, outputTensorsBack).isFailure()) {
351 ATH_MSG_ERROR("Fold " << fold << ": backward inference failed");
353 }
354
355 const auto infoB = outputTensorsBack[0].GetTensorTypeAndShapeInfo();
356 const auto nElB = infoB.GetElementCount();
357 if (nElB != nSS) {
358 ATH_MSG_ERROR("Fold "<<fold <<": backward output has " <<nElB<< " elements, expected "<<nSS);
360 }
361
362 // Write corrected shower shapes
363 float* corrPtr = outputTensorsBack[0].GetTensorMutableData<float>();
364 for (size_t i = 0; i < nSS; ++i) {
365 photon.setShowerShapeValue(corrPtr[i], s_ssEnums[i]);
366 }
367
368 ATH_MSG_DEBUG("NF correction applied successfully");
369
370
372}
373
374// Electrons are not supported.
376{
377 ATH_MSG_ERROR("ElectronPhotonVariableNFCorrectionTool does not support electrons.");
379}
380
381// Create corrected copy of photon.
383 xAOD::Photon*& out_photon) const
384{
385
386 out_photon = new xAOD::Photon(in_photon);
387 return applyCorrection(*out_photon);
388}
389
390// Create copy of electron (no correction).
392 xAOD::Electron*& out_electron) const
393{
394 ATH_MSG_ERROR("ElectronPhotonVariableNFCorrectionTool cannot correct electrons.");
395 out_electron = new xAOD::Electron(in_electron);
397}
#define M_PI
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
static Double_t fs
static Double_t ss
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
bool msgLvl(const MSG::Level lvl) const
Header file for AthHistogramAlgorithm.
Return value from object correction CP tools.
@ Error
Some error happened during the object correction.
@ Ok
The correction was done successfully.
std::vector< SSAccessors > m_accessors
Per-variable accessors aligned with s_ssVarNames.
ToolHandleArray< AthOnnx::IOnnxRuntimeInferenceTool > m_onnxToolsForward
ToolHandleArray for forward ONNX models (one tool per fold).
static const std::vector< std::string > s_ssVarNames
List of shower shape variable names (order must match model I/O).
FoldStrategy parseFoldStrategy(const std::string &s) const
Parse fold strategy string from config.
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfoKey
ReadHandleKey for EventInfo used for fold selection.
int selectFold(unsigned long long eventNumber, float phi) const
Select fold index for the current event/photon.
virtual const CP::CorrectionCode correctedCopy(const xAOD::Photon &in_photon, xAOD::Photon *&out_photon) const override
Make a corrected copy of the passed photon.
std::string m_onnxPattern
Models path pattern string from config.
ToolHandleArray< AthOnnx::IOnnxRuntimeInferenceTool > m_onnxToolsBackward
ToolHandleArray for backward ONNX models (one tool per fold).
bool passSelectionCuts(const xAOD::Photon &photon, const std::vector< float > &ss) const
Returns true if NF correction should be applied to this photon.
bool m_applyShowerShapeCuts
Cuts applied to remove default values of shower shapes.
ElectronPhotonVariableNFCorrectionTool(const std::string &name)
Standard constructor.
static const std::vector< xAOD::EgammaParameters::ShowerShapeType > s_ssEnums
Egamma shower shape enum mapping for reading/writing values (order matches s_ssVarNames).
int m_nFolds
Number of model folds configured (must match tool handle array sizes).
virtual const CP::CorrectionCode applyCorrection(xAOD::Photon &photon) const override
Apply the Normalizing Flow correction to the passed photon.
virtual StatusCode initialize() override
Initialize the class instance.
Gaudi::Property< std::string > m_configFile
The configuration file for the tool, application mode and minimum photon pT cut in MeV.
FoldStrategy m_foldStrategy
Selected fold strategy (configured via FoldStrategy in the config).
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
@ unconverted
unconverted photon
@ wtots1
shower width is determined in a window detaxdphi = 0,0625 ×~0,2, corresponding typically to 20 strips...
@ f1
E1/E = fraction of energy reconstructed in the first sampling, where E1 is energy in all strips belon...
Definition EgammaEnums.h:53
@ Eratio
(emaxs1-e2tsts1)/(emaxs1+e2tsts1)
@ DeltaE
e2tsts1-emins1
@ fracs1
shower shape in the shower core : [E(+/-3)-E(+/-1)]/E(+/-1), where E(+/-n) is the energy in ± n strip...
@ weta2
the lateral width is calculated with a window of 3x5 cells using the energy weighted sum over all cel...
@ weta1
shower width using +/-3 strips around the one with the maximal energy deposit: w3 strips = sqrt{sum(E...
Definition EgammaEnums.h:98
Photon_v1 Photon
Definition of the current "egamma version".
Electron_v1 Electron
Definition of the current "egamma version".