ATLAS Offline Software
Loading...
Searching...
No Matches
JetDNNCalibStep.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
7
8#include <TLorentzVector.h>
9#include <algorithm>
10#include <cmath>
11
12
14
15 ATH_CHECK( m_inputVariables.retrieve() );
16 ATH_CHECK( m_npvKey.initialize() );
17 ATH_CHECK( m_muKey.initialize() );
18 ATH_CHECK( m_onnxTool.retrieve() );
19
20 if (static_cast<int>(m_inputVariables.size()) != m_onnxInputShape) {
21 ATH_MSG_FATAL("Number of input variables (" << m_inputVariables.size() << ") does not match onnxInputShape (" << m_onnxInputShape << ")");
22 return StatusCode::FAILURE;
23 }
24
25 return StatusCode::SUCCESS;
26}
27
29
31
32 SG::ReadHandle<xAOD::VertexContainer> primaryVertexContainer(m_npvKey);
33 float NPV = JetCalibUtils::countNPV(*primaryVertexContainer);
34 jc.setValue("NPV" , NPV);
35
37 float mu = eventInfoDecor(0);
38 jc.setValue("mu", mu);
39
40 // Pass 1: compute normalized input features per jet, resolving degenerate/invalid jets
41 // immediately (they never reach the DNN). Surviving jets are batched into a single ONNX
42 // inference call below, instead of one call per jet, since the model has a dynamic batch
43 // dimension ([-1, 21] in, [-1, 3] out).
44 std::vector<xAOD::Jet*> batchJets;
45 std::vector<xAOD::JetFourMom_t> batchStartP4;
46 std::vector<float> batchInputValues;
47 batchJets.reserve(jets.size());
48 batchStartP4.reserve(jets.size());
49 batchInputValues.reserve(jets.size() * m_inputVariables.size());
50
51 for (xAOD::Jet* jet : jets){
52
53 const xAOD::JetFourMom_t jetStartP4 = jet->getAttribute<xAOD::JetFourMom_t>(m_jetInScale);
54
55 // Sync the jet's active 4-vector to InScale, so any variable reading jet.e()/jet.m()/etc.
56 // directly (e.g. log_e/log_m below) sees the same values as jetStartP4, matching
57 // GlobalLargeRDNNCalibration's setStartP4() convention.
58 jet->setJetP4(jetStartP4);
59
60 // Don't apply calibration for jets with negative or null mass or for one constituent jets
61 if (jetStartP4.mass() <= 0 || jet->numConstituents() == 1) {
62 jet->setAttribute<xAOD::JetFourMom_t>(m_jetOutScale, jetStartP4);
63 continue;
64 }
65
66 std::vector<float> inputTensorValues;
67 inputTensorValues.reserve(m_inputVariables.size());
68
69 for (long unsigned int i=0; i < m_inputVariables.size(); i++) {
70
71 // log_e/log_m/log_m_cap40: InputVariable.cpp already returns log(x*scale) for these, with
72 // eScale folded in via the VarTool's own Scale property (see JetCalibStepsConfig.py), so
73 // eScale must NOT be re-applied below for them, or it would be double-counted.
74 static const std::vector<std::string> logScaledVars = {"log_e", "log_m", "log_m_cap40"};
75 const std::string& varName = m_inputVariables[i].name();
76 bool eScaleAlreadyApplied = std::any_of(logScaledVars.begin(), logScaledVars.end(),
77 [&varName](const std::string& n) { return varName.find(n) != std::string::npos; });
78
79 float inputVar = m_inputVariables[i]->getValue(*jet, jc);
80 float eScale = m_eScales[i];
81 float normOffset = m_normOffsets[i];
82 float normScale = m_normScales[i];
83 float normalisedVar;
84
85 if (eScaleAlreadyApplied) {
86 normalisedVar = inputVar*normScale + normOffset;
87 } else {
88 normalisedVar = inputVar*eScale*normScale + normOffset;
89 }
90
91 ATH_MSG_DEBUG(m_inputVariables[i].name() << ": raw=" << inputVar << " eScale=" << eScale
92 << " normOffset=" << normOffset << " normScale=" << normScale << " normalised=" << normalisedVar);
93
94 inputTensorValues.push_back(normalisedVar);
95 }
96
97 int nNan = std::count_if(inputTensorValues.begin(), inputTensorValues.end(),
98 [](float f){ return std::isnan(f) || std::isinf(f); });
99 if (nNan > 0) {
100 ATH_MSG_WARNING("Encountered NaN or inf value in input features, will not apply calibration to this jet");
101 jet->setJetP4(jetStartP4);
102 jet->setAttribute<xAOD::JetFourMom_t>(m_jetOutScale, jetStartP4);
103 continue;
104 }
105
106 batchJets.push_back(jet);
107 batchStartP4.push_back(jetStartP4);
108 batchInputValues.insert(batchInputValues.end(), inputTensorValues.begin(), inputTensorValues.end());
109 }
110
111 if (batchJets.empty()) {
112 return StatusCode::SUCCESS;
113 }
114
115 const int64_t nBatch = static_cast<int64_t>(batchJets.size());
116
117 AthInfer::InputDataMap inputData;
118 inputData["input_1"] = std::make_pair(
119 std::vector<int64_t>{nBatch, m_onnxInputShape}, std::move(batchInputValues)
120 );
121
122 AthInfer::OutputDataMap outputData;
123 outputData["outputE"] = std::make_pair(std::vector<int64_t>{nBatch, m_onnxOutputShape}, std::vector<float>{});
124 outputData["outputM"] = std::make_pair(std::vector<int64_t>{nBatch, m_onnxOutputShape}, std::vector<float>{});
125
126 ATH_CHECK( m_onnxTool->inference(inputData, outputData) );
127
128 const std::vector<float>& outputE = std::get<std::vector<float>>(outputData["outputE"].second);
129 const std::vector<float>& outputM = std::get<std::vector<float>>(outputData["outputM"].second);
130
131 // Pass 2: combine each jet's response and rescale, in the same order the batch was filled
132 for (int64_t idx = 0; idx < nBatch; idx++) {
133
134 xAOD::Jet* jet = batchJets[idx];
135 const xAOD::JetFourMom_t& jetStartP4 = batchStartP4[idx];
136
137 // First element of each jet's output is the predicted response; remaining elements are unused here
138 float predRespE = outputE.at(idx * m_onnxOutputShape);
139 float predRespM = outputM.at(idx * m_onnxOutputShape);
140
141 ATH_MSG_DEBUG("jetStartP4: pt=" << jetStartP4.pt() << " eta=" << jetStartP4.eta()
142 << " e=" << jetStartP4.e() << " m=" << jetStartP4.mass());
143 ATH_MSG_DEBUG("Predicted response: E=" << predRespE << " M=" << predRespM);
144
145 if (predRespE == 0 || predRespM == 0) {
146 ATH_MSG_WARNING("DNN predictions give 0 values, will not apply calibration to this jet");
147 jet->setJetP4(jetStartP4);
148 jet->setAttribute<xAOD::JetFourMom_t>(m_jetOutScale, jetStartP4);
149 continue;
150 }
151
152 // Combine the energy and mass response predictions, following the same approach as GlobalLargeRDNNCalibration
153 float calibE = jetStartP4.e() / predRespE;
154
155 // Calibrate the mass at all mass values (log_m_cap40 protects the DNN input from extreme
156 // log(mass) values for very light jets, so no output-side mass cutoff is needed here).
157 float calibM = jetStartP4.mass() / predRespM;
158
159 // Propagate energy and mass calibration to jet pT
160 float calibpT = std::sqrt(calibE*calibE - calibM*calibM) / std::cosh(jetStartP4.eta());
161
162 TLorentzVector TLVjet;
163 TLVjet.SetPtEtaPhiM(calibpT, jetStartP4.eta(), jetStartP4.phi(), calibM);
164 xAOD::JetFourMom_t calibP4;
165 calibP4.SetPxPyPzE(TLVjet.Px(), TLVjet.Py(), TLVjet.Pz(), TLVjet.E());
166
167 jet->setJetP4(calibP4);
168
169 // Set the output scale
170 jet->setAttribute<xAOD::JetFourMom_t>(m_jetOutScale,calibP4);
171 }
172 return StatusCode::SUCCESS;
173}
174
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_FATAL(x,...)
Handle class for reading a decoration on an object.
SG::ReadDecorHandleKey< xAOD::EventInfo > m_muKey
ToolHandle< AthInfer::IAthInferenceTool > m_onnxTool
Gaudi::Property< int > m_onnxInputShape
virtual StatusCode calibrate(xAOD::JetContainer &) const override
Apply calibration to a jet container.
Gaudi::Property< int > m_onnxOutputShape
Gaudi::Property< std::vector< float > > m_eScales
Gaudi::Property< std::vector< float > > m_normOffsets
Gaudi::Property< std::string > m_jetOutScale
Gaudi::Property< std::vector< float > > m_normScales
Gaudi::Property< std::string > m_jetInScale
ToolHandleArray< JetHelper::IVarTool > m_inputVariables
virtual StatusCode initialize() override
Dummy implementation of the initialisation function.
SG::ReadHandleKey< xAOD::VertexContainer > m_npvKey
Class JetContext Designed to read AOD information related to the event, N vertices,...
Definition JetContext.h:27
bool setValue(std::string_view name, const T value, bool allowOverwrite=false)
Definition JetContext.h:58
Handle class for reading a decoration on an object.
std::map< std::string, InferenceData > OutputDataMap
std::map< std::string, InferenceData > InputDataMap
int countNPV(const VXCONT &vxCont)
Jet_v1 Jet
Definition of the current "jet version".
JetContainer_v1 JetContainer
Definition of the current "jet container version".
ROOT::Math::LorentzVector< ROOT::Math::PtEtaPhiM4D< double > > JetFourMom_t
Base 4 Momentum type for Jet.
Definition JetTypes.h:17