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{
74 // pT cut
75 if (photon.pt() < m_pTcutMeV) return false;
76
77 // TruthType cut
79 static const SG::Accessor<int> acc_truthType("truthType");
80 if (!acc_truthType.isAvailable(photon)) {
81 ATH_MSG_WARNING("ApplyTo = TruthPhotons but truthType not available — skipping photon");
82 return false;
83 }
84 int truthType = acc_truthType(photon);
85 if (truthType < 13 || truthType > 15) return false;
86 }
87
88 return true;
89}
90
92{
93 if (!m_applyShowerShapeCuts) return true;
94
95 // weta2
96 if (ss[0] <= -10.f || ss[0] >= 10.f) return false;
97 // weta1
98 if (ss[1] <= -10.f || ss[1] >= 10.f) return false;
99 // Rphi
100 if (ss[2] <= -10.f || ss[2] >= 10.f) return false;
101 // Reta
102 if (ss[3] <= -10.f || ss[3] >= 10.f) return false;
103 // wtots1
104 if (ss[4] < -2.f || ss[4] >= 10.f) return false;
105 // Rhad
106 if (ss[5] < -2.f || ss[5] > 2.f) return false;
107 // Rhad1
108 if (ss[6] < -2.f || ss[6] > 2.f) return false;
109 // f1
110 if (ss[7] <= -2.f || ss[7] >= 2.f) return false;
111 // fracs1
112 if (ss[8] <= -2.f || ss[8] >= 5.f) return false;
113 // DeltaE
114 if (ss[9] < 0.f || ss[9] >= 5000.f) return false;
115 // Eratio
116 if (ss[10] < 0.f || ss[10] > 1.f) return false;
117
118 return true;
119}
120
122 xAOD::Photon& photon,
123 const std::vector<float>& ss) const
124{
125 if (m_fallbackFudgeTool->applyCorrection(photon) != CP::CorrectionCode::Ok) {
126 ATH_MSG_ERROR("Fallback fudge tool failed to correct photon");
128 }
129
130 // Keep default values of weta1 (index 1) and wtots1 (index 4) untouched
131 for (size_t i : {size_t(1), size_t(4)}) {
132 if (ss[i] < s_defaultValueThreshold) {
133 photon.setShowerShapeValue(ss[i], s_ssEnums[i]);
134 }
135 }
136 // Keep fracs1 (index 8) = 0 untouched (fudging smears it)
137 if (ss[8] == 0.f) {
138 photon.setShowerShapeValue(0.f, s_ssEnums[8]);
139 }
140
141 ATH_MSG_DEBUG("Photon failed shower shape cuts: fallback fudge correction applied");
143}
144
145
146// Initialize tool: read config, setup ONNX tools and accessors
148{
149 if (m_configFile.empty()) {
150 ATH_MSG_ERROR("ConfigFile property is empty. Please provide a config file to the tool.");
151 return StatusCode::FAILURE;
152 }
153
154 std::string resolvedConfig = PathResolverFindCalibFile(m_configFile);
155 if (resolvedConfig.empty()) {
156 ATH_MSG_ERROR("Failed to resolve config file \"" << m_configFile << "\"");
157 return StatusCode::FAILURE;
158 }
159 ATH_MSG_DEBUG("Use configuration file " << m_configFile);
160
161 TEnv env;
162 env.ReadFile(resolvedConfig.c_str(), kEnvLocal);
163 env.IgnoreDuplicates(false);
164
165 const int nFoldsConfig = env.GetValue("NFolds", 0);
166 if (nFoldsConfig <= 0) {
167 ATH_MSG_ERROR("NFolds not set or invalid in config: " << resolvedConfig);
168 return StatusCode::FAILURE;
169 }
170
171 if (m_nFoldsOverride > 0) {
172 if (m_nFoldsOverride > nFoldsConfig) {
173 ATH_MSG_ERROR("NFoldsOverride (" << m_nFoldsOverride.value() << ") exceeds NFolds in config (" << nFoldsConfig << ")");
174 return StatusCode::FAILURE;
175 }
177 } else {
178 m_nFolds = nFoldsConfig;
179 }
180
181 TString pattern = env.GetValue("ONNXnamePattern", "");
182 if (pattern.IsNull()) {
183 ATH_MSG_ERROR("ONNXnamePattern not set in config: " << resolvedConfig);
184 return StatusCode::FAILURE;
185 }
186 m_onnxPattern = pattern.Data();
187
188
189 TString fs = env.GetValue("FoldStrategy", "eventNumber");
190 std::string fsStr = fs.Data();
191
193
195 ATH_MSG_ERROR("FoldStrategy must be 'eventNumber' or 'eventNumber_phi', but got '" << fsStr << "' in config: " << resolvedConfig);
196 return StatusCode::FAILURE;
197 }
198
199
200 ATH_MSG_VERBOSE("NFolds = " << m_nFolds << ", pattern = " << m_onnxPattern << ", FoldStrategy = " << fsStr);
201
202 if (static_cast<int>(m_onnxToolsForward.size()) != m_nFolds ||
203 static_cast<int>(m_onnxToolsBackward.size()) != m_nFolds) {
204 ATH_MSG_ERROR("Expected "<<m_nFolds<<" forward/backward tools, "<< "but got "<<m_onnxToolsForward.size()<<" / "<< m_onnxToolsBackward.size());
205 return StatusCode::FAILURE;
206 }
207
208
210 else if (m_applyToStr == "All") m_applyToMode = ApplyToMode::All;
211 else {
212 ATH_MSG_ERROR("ApplyTo must be TruthPhotons or All, but got '" << m_applyToStr << "'");
213 return StatusCode::FAILURE;
214 }
215
216 // Cuts on SS vars to remove default values
217 m_applyShowerShapeCuts = (env.GetValue("ApplyShowerShapeCuts", 1) == 1);
218
219 ATH_MSG_INFO("ApplyTo = " << m_applyToStr << ", pTcut=" << m_pTcutMeV << " MeV, ApplyShowerShapeCuts=" << m_applyShowerShapeCuts);
220
221
222 ATH_CHECK(m_onnxToolsForward.retrieve());
223 ATH_CHECK(m_onnxToolsBackward.retrieve());
224
225 if (!m_fallbackFudgeTool.empty()) {
226 ATH_CHECK(m_fallbackFudgeTool.retrieve());
227 ATH_MSG_INFO("Photons failing shower shape cuts will be corrected with fallback fudge tool " << m_fallbackFudgeTool.name());
228 }
229
230 if (msgLvl(MSG::DEBUG)) {
231 for (int i = 0; i < m_nFolds; ++i) {
232 ATH_MSG_VERBOSE("Fold " << i << " forward model info:");
233 m_onnxToolsForward[i]->printModelInfo();
234 ATH_MSG_VERBOSE("Fold " << i << " backward model info:");
235 m_onnxToolsBackward[i]->printModelInfo();
236 }
237 }
238
239 // Prepare decorations for each shower shape
240 m_accessors.resize(s_ssVarNames.size());
241 for (size_t i = 0; i < s_ssVarNames.size(); ++i) {
242 const std::string& var = s_ssVarNames[i];
243 m_accessors[i].original = std::make_unique<SG::Accessor<float>>(var + "_original");
244 }
245
246 ATH_CHECK(m_eventInfoKey.initialize());
247
248 ATH_MSG_INFO("NF correction tool initialized with " << m_nFolds << " folds. ");
249
250 return StatusCode::SUCCESS;
251}
252
253
254// Apply NF correction to photon shower shapes.
256{
257
258 const size_t nSS = s_ssEnums.size();
259 std::vector<float> ss(nSS);
260
261 // Read shower shapes, then store original values
262 for (size_t i = 0; i < nSS; ++i) {
263 ss[i] = photon.showerShapeValue(s_ssEnums[i]);
264 (*m_accessors[i].original)(photon) = ss[i];
265 }
266
267
268 static const SG::Decorator<char> dec_pass("NFCorrectedShowerShapes");
269 static const SG::Decorator<char> dec_fudged("FallbackFudgedShowerShapes");
270
271 // Photon selection
272 const bool passPhoton = passPhotonSelection(photon);
273 const bool passSS = passPhoton && passShowerShapeCuts(ss);
274 const bool fallback = passPhoton && !passSS && !m_fallbackFudgeTool.empty();
275
276 dec_pass(photon) = passSS ? 1 : 0;
277 dec_fudged(photon) = fallback ? 1 : 0;
278
279 if (fallback) {
280 // NF is not applied because of the shower shape cuts, so use fudging instead
281 return applyFallbackFudge(photon, ss);
282 }
283
284 if (!passSS) {
285 // If selection is not passed, then SS value will be same to original
287 }
288
289
290 // Get event info and select fold
292 if (!h.isValid()) {
293 ATH_MSG_ERROR("Failed to read EventInfo via key " << m_eventInfoKey.key());
295 }
296
297 const unsigned long long eventNumber = h->eventNumber();
298 float ptGeV = photon.pt() / 1000.0f;
299 const float phi = static_cast<float>(photon.phi());
300 const int fold = selectFold(eventNumber, phi);
301
302
303 if (fold < 0 || fold >= m_nFolds) {
304 ATH_MSG_ERROR("Selected fold " << fold << " out of range [0," << (m_nFolds-1) << "]");
306 }
307
308
309 // Kinematic inputs
310 const bool isConv = photon.conversionType() != xAOD::EgammaParameters::unconverted;
311 std::vector<float> kinematic = {
312 ptGeV,
313 static_cast<float>(photon.eta()),
314 static_cast<float>(photon.phi()),
315 static_cast<float>(isConv)
316 };
317
318 // Forward inference
319 std::vector<Ort::Value> inputTensors;
320
321 const auto& onnxToolForward = m_onnxToolsForward[fold];
322
323 // index 0 is for kinematics
324 int64_t batchSizeKin = onnxToolForward->getBatchSize(
325 static_cast<int64_t>(kinematic.size()), 0);
326 if (onnxToolForward->addInput(inputTensors, kinematic, 0, batchSizeKin).isFailure()) {
327 ATH_MSG_ERROR("Fold " << fold << ": failed to add kinematic input tensor");
329 }
330
331 // index 1 is for shower shape varibales
332 int64_t batchSizeSS = onnxToolForward->getBatchSize(
333 static_cast<int64_t>(ss.size()), 1);
334 if (onnxToolForward->addInput(inputTensors, ss, 1, batchSizeSS).isFailure()) {
335 ATH_MSG_ERROR("Fold " << fold << ": failed to add shower shape input tensor");
337 }
338
339 std::vector<Ort::Value> outputTensors;
340 std::vector<float> outputData;
341 if (onnxToolForward->addOutput(outputTensors, outputData, 0, batchSizeKin).isFailure()) {
342 ATH_MSG_ERROR("Fold " << fold << ": failed to add forward output tensor");
344 }
345
346 if (onnxToolForward->inference(inputTensors, outputTensors).isFailure()) {
347 ATH_MSG_ERROR("Fold " << fold << ": forward inference failed");
349 }
350
351 float* zPtr = outputTensors[0].GetTensorMutableData<float>();
352 std::vector<float> zVec(zPtr, zPtr + nSS);
353
354
355 // Backward inference
356 std::vector<Ort::Value> inputTensorsBack;
357 std::vector<Ort::Value> outputTensorsBack;
358 std::vector<float> outputDataBack;
359
360 const auto& onnxToolBackward = m_onnxToolsBackward[fold];
361
362 // index 0 is for kinematics
363 int64_t batchSizeKinBack = onnxToolBackward->getBatchSize(
364 static_cast<int64_t>(kinematic.size()), 0);
365 if (onnxToolBackward->addInput(inputTensorsBack, kinematic, 0, batchSizeKinBack).isFailure()) {
366 ATH_MSG_ERROR("Fold " << fold << ": failed to add kinematic input tensor for backward model");
368 }
369
370 // index 1 is for shower shapes in latent space
371 int64_t batchSizeZBack = onnxToolBackward->getBatchSize(static_cast<int64_t>(zVec.size()), 1);
372
373 if (onnxToolBackward->addInput(inputTensorsBack, zVec, 1, batchSizeZBack).isFailure()) {
374 ATH_MSG_ERROR("Fold " << fold << ": failed to add z input tensor for backward model");
376 }
377
378 // index 2 is for original shower shapes (models use them to cut on std values [-5, 5])
379 if (onnxToolBackward->addInput(inputTensorsBack, ss, 2, batchSizeKinBack).isFailure()) {
380 ATH_MSG_ERROR("Fold " << fold << ": failed to add original SS input tensor for backward model");
382 }
383
384 if (onnxToolBackward->addOutput(outputTensorsBack, outputDataBack, 0, batchSizeZBack).isFailure()) {
385 ATH_MSG_ERROR("Fold " << fold << ": failed to add backward output tensor");
387 }
388
389 if (onnxToolBackward->inference(inputTensorsBack, outputTensorsBack).isFailure()) {
390 ATH_MSG_ERROR("Fold " << fold << ": backward inference failed");
392 }
393
394 const auto infoB = outputTensorsBack[0].GetTensorTypeAndShapeInfo();
395 const auto nElB = infoB.GetElementCount();
396 if (nElB != nSS) {
397 ATH_MSG_ERROR("Fold "<<fold <<": backward output has " <<nElB<< " elements, expected "<<nSS);
399 }
400
401 // Write corrected shower shapes
402 float* corrPtr = outputTensorsBack[0].GetTensorMutableData<float>();
403 for (size_t i = 0; i < nSS; ++i) {
404 photon.setShowerShapeValue(corrPtr[i], s_ssEnums[i]);
405 }
406
407 // Keep fracs1 (index 8) = 0 untouched (NF smears it)
408 if (ss[8] == 0.f) {
409 photon.setShowerShapeValue(0.f, s_ssEnums[8]);
410 }
411
412 ATH_MSG_DEBUG("NF correction applied successfully");
413
414
416}
417
418// Electrons are not supported.
420{
421 ATH_MSG_ERROR("ElectronPhotonVariableNFCorrectionTool does not support electrons.");
423}
424
425// Create corrected copy of photon.
427 xAOD::Photon*& out_photon) const
428{
429
430 out_photon = new xAOD::Photon(in_photon);
431 return applyCorrection(*out_photon);
432}
433
434// Create copy of electron (no correction).
436 xAOD::Electron*& out_electron) const
437{
438 ATH_MSG_ERROR("ElectronPhotonVariableNFCorrectionTool cannot correct electrons.");
439 out_electron = new xAOD::Electron(in_electron);
441}
#define M_PI
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
#define ATH_MSG_INFO(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).
bool passShowerShapeCuts(const std::vector< float > &ss) const
Returns true if the shower shapes pass the cuts removing default values.
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 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.
static constexpr float s_defaultValueThreshold
Original values of weta1 and wtots1 below this threshold are default values and are not fudged.
bool passPhotonSelection(const xAOD::Photon &photon) const
Returns true if the photon passes the pT and truthType selection.
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).
CP::CorrectionCode applyFallbackFudge(xAOD::Photon &photon, const std::vector< float > &ss) const
Apply the fallback fudge tool to a photon failing only the shower shape cuts.
ToolHandle< IElectronPhotonShowerShapeFudgeTool > m_fallbackFudgeTool
Fudge tool used for photons failing the shower shape cuts, empty = no fallback.
Helper class to provide type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
SG::Decorator< T, ALLOC > Decorator
Helper class to provide type-safe access to aux data, specialized for JaggedVecElt.
Definition AuxElement.h:576
@ 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".