ATLAS Offline Software
Loading...
Searching...
No Matches
EtaJESCorrection.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
8#include <TAxis.h>
9#include <TEnv.h>
10#include <utility>
11#include <cmath>
12
20
21EtaJESCorrection::EtaJESCorrection(const std::string& name, TEnv* config, TString jetAlgo, TString calibAreaTag, bool mass, bool dev)
23 m_config(config), m_jetAlgo(std::move(jetAlgo)), m_calibAreaTag(std::move(calibAreaTag)), m_mass(mass), m_dev(dev),
25 m_lowPtExtrap(0), m_lowPtMinR(0.25),
26 m_etaBinAxis(nullptr)
27{ }
28
34
36
37 ATH_MSG_DEBUG("Initializing JES correction.");
38
39 if(!m_config){
40 ATH_MSG_ERROR("EtaJES tool received a null config pointer.");
41 return StatusCode::FAILURE;
42 }
43
44 m_jetStartScale = m_config->GetValue("EtaJESStartingScale","JetPileupScaleMomentum");
45
46 TString absoluteJESCalibFile = m_config->GetValue("AbsoluteJES.CalibFile","");
47 if(m_dev){
48 absoluteJESCalibFile.Remove(0,33);
49 absoluteJESCalibFile.Insert(0,"JetCalibTools/");
50 }
51 else{absoluteJESCalibFile.Insert(14,m_calibAreaTag);}
52 TString calibFile = PathResolverFindCalibFile(absoluteJESCalibFile.Data());
53 m_config->ReadFile(calibFile, kEnvLocal);
54 ATH_MSG_DEBUG("Reading absolute calibration factors from: " << calibFile);
55 m_jesDesc = m_config->GetValue("AbsoluteJES.Description","");
56 ATH_MSG_DEBUG("Description: " << m_jesDesc);
57
58 // minPt_JES (always in GeV) determines at which point we stop using the correction curve and switch to an extrapolated value
59 m_minPt_JES = m_config->GetValue(m_jetAlgo+".MinPtForETAJES",10);
60 //Which extrapolation method to use at low Et (Et < _minPt_JES)
61 m_lowPtExtrap = m_config->GetValue("LowPtJESExtrapolationMethod",0);
62 //For order 2 extrapolation only, set the minimum value of the response for Et = 0
63 m_lowPtMinR = m_config->GetValue("LowPtJESExtrapolationMinimumResponse",0.25);
64 //Allowing to use different minPt_JES depending on eta (only for extrapolation methon 1)
65 m_useSecondaryminPt_JES = m_config->GetValue(m_jetAlgo+".UseSecondaryMinPtForETAJES", false);
66 //Starting eta for secondary minPt_JES (Default |eta|>=1.9) (Used only if UseSecondaryMinPtForETAJES is true)
67 m_etaSecondaryminPt_JES = m_config->GetValue(m_jetAlgo+".EtaSecondaryMinPtForETAJES", 1.9);
68 //SecondaryminPt_JES (Default 7 GeV) (Used only if UseSecondaryMinPtForETAJES is true)
69 m_secondaryminPt_JES = m_config->GetValue(m_jetAlgo+".SecondaryMinPtForETAJES",7);
70 // Freeze JES correction at maximum values of energy for each eta bin
71 m_freezeJESatHighE = m_config->GetValue(m_jetAlgo+".FreezeJEScorrectionatHighE", false);
72 m_isSpline = m_config->GetValue(m_jetAlgo+".isSpline", false);
73
74 // From mswiatlo, help from dag: variable eta binning
75 std::vector<double> etaBins = JetCalibUtils::VectorizeD(m_config->GetValue("JES.EtaBins",""));
76 if (etaBins.empty()){ // default binning
77 for (int i=0;i<=90; i++)
78 etaBins.push_back(0.1*i-4.5);
79 }
80 else if (etaBins.empty()) { ATH_MSG_FATAL("JES.EtaBins incorrectly specified"); return StatusCode::FAILURE; }
81 else if (etaBins.size()>s_nEtaBins+1) {
82 ATH_MSG_FATAL( "JES.EtaBins has " << etaBins.size()-1 << " bins, can be maximally 90!" );
83 return StatusCode::FAILURE;
84 }
85 m_etaBinAxis = new TAxis(etaBins.size()-1,&etaBins[0]);
86
87 m_applyMassCorrection = m_config->GetValue("ApplyMassCorrection",false);
88
89 if(m_mass){ // Only for the calibration sequence: EtaMassJES
90 if(m_applyMassCorrection) ATH_MSG_INFO("Jet mass correction will be applied.");
91 else { ATH_MSG_FATAL( "You can't apply the mass correction unless you specify ApplyMassCorrection: true in the configuration file!"); return StatusCode::FAILURE; }
92 }
93
94 for (uint ieta=0;ieta<etaBins.size()-1;++ieta) {
95 if(!m_isSpline){
96 // Read in absolute JES calibration factors
97 TString key=Form("JES.%s_Bin%d",m_jetAlgo.Data(),ieta);
98 std::vector<double> params = JetCalibUtils::VectorizeD(m_config->GetValue(key,""));
99 m_nPar = std::ssize(params);
100 if (m_nPar<s_nParMin || m_nPar>s_nParMax) { ATH_MSG_FATAL( "Cannot read JES calib constants " << key ); return StatusCode::FAILURE; }
101 for (int ipar=0;ipar<m_nPar;++ipar) m_JESFactors[ieta][ipar] = params[ipar];
102
103 //Protections for high order extrapolation methods at low Et (Et < _minPt_JES)
104 if(m_lowPtExtrap > 0) {
105 //Calculate the slope of the response curve at the minPt for each eta bin
106 //Used in the GetLowPtJES method when Pt < minPt
107 const double *factors = m_JESFactors[ieta];
108 double Ecutoff;
109 if(!m_useSecondaryminPt_JES) Ecutoff = m_minPt_JES*cosh(etaBins[ieta]);
110 else {
111 if(fabs(etaBins[ieta]) < m_etaSecondaryminPt_JES) Ecutoff = m_minPt_JES*cosh(etaBins[ieta]);
112 else{ Ecutoff = m_secondaryminPt_JES*cosh(etaBins[ieta]);}
113 }
114 const double Rcutoff = getLogPolN(factors,Ecutoff);
115 const double Slope = getLogPolNSlope(factors,Ecutoff);
116 if(Slope > Rcutoff/Ecutoff) ATH_MSG_FATAL("Slope of calibration curve at minimum ET is too steep for the JES factors of etabin " << ieta << ", eta = " << etaBins[ieta] );
117
118 m_JES_MinPt_E[ieta] = Ecutoff;
119 m_JES_MinPt_R[ieta] = Rcutoff;
120 m_JES_MinPt_Slopes[ieta] = Slope;
121
122 //Calculate the parameters for a 2nd order polynomial extension to the calibration curve below minimum ET
123 //Used in the GetLowPtJES method when Pt < minPt
124 if(m_lowPtExtrap == 2) {
125 const double h = m_lowPtMinR;
126 const double Param1 = (2/Ecutoff)*(Rcutoff-h)-Slope;
127 const double Param2 = (0.5/Ecutoff)*(Slope-Param1);
128 //Slope of the calibration curve should always be positive
129 if( Param1 < 0 || Param1 + 2*Param2*Ecutoff < 0) ATH_MSG_FATAL("Polynomial extension to calibration curve below minimum ET is not monotonically increasing for etabin " << ieta << ", eta = " << etaBins[ieta] );
130 m_JES_MinPt_Param1[ieta] = Param1;
131 m_JES_MinPt_Param2[ieta] = Param2;
132 }
133 }
134 }
135
136 // Read in jet eta calibration factors
137 TString key=Form("EtaCorr.%s_Bin%d",m_jetAlgo.Data(),ieta);
138 std::vector<double> params = JetCalibUtils::VectorizeD(m_config->GetValue(key,""));
139 m_nPar = std::ssize(params);
140
141 for (int ipar=0;ipar<m_nPar;++ipar) m_etaCorrFactors[ieta][ipar] = params[ipar];
142
143 if(m_freezeJESatHighE){ // Read starting energy values to freeze JES correction
144 key=Form("EmaxJES.%s_Bin%d",m_jetAlgo.Data(),ieta);
145 params = JetCalibUtils::VectorizeD(m_config->GetValue(key,""));
146 if (params.size()!=1) { ATH_MSG_FATAL( "Cannot read starting energy for the freezing of JES correction " << key ); return StatusCode::FAILURE; }
147 for (uint ipar=0;ipar<1;++ipar) m_energyFreezeJES[ieta] = params[ipar];
148 }
149
151 // Read in absolute JMS calibration factors
152 key=Form("MassCorr.%s_Bin%d",m_jetAlgo.Data(),ieta);
153 params = JetCalibUtils::VectorizeD(m_config->GetValue(key,""));
154 if (std::ssize(params)!=m_nPar) {ATH_MSG_FATAL( "Cannot read JMS calib constants " << key ); return StatusCode::FAILURE;}
155 for (int ipar=0;ipar<m_nPar;++ipar) m_JMSFactors[ieta][ipar] = params[ipar];
156 }
157
158 }
159 if(m_isSpline){
160 TString absoluteJESCalibHists = m_config->GetValue("AbsoluteJES.CalibHists","");
161 if(m_dev){
162 absoluteJESCalibHists.Remove(0,33);
163 absoluteJESCalibHists.Insert(0,"JetCalibTools/");
164 }
165 else{
166 absoluteJESCalibHists.Insert(14,m_calibAreaTag);
167 }
168 TString calibHistFile = PathResolverFindCalibFile(absoluteJESCalibHists.Data());
169 loadSplineHists(calibHistFile, "etaJes");
170
171 //Protections for high order extrapolation methods at low Et (Et < _minPt_JES)
172 if(m_lowPtExtrap != 1) {
173 ATH_MSG_ERROR("Only linear extrapolations are supported for p-splines currently. Please change the config file to reflect this");
174 return StatusCode::FAILURE;
175 }
176
177 if(m_lowPtExtrap > 0) {
178 for (uint ieta=0;ieta<etaBins.size()-1;++ieta) {
179 //Calculate the slope of the response curve at the minPt for each eta bin
180 //Used in the GetLowPtJES method when Pt < minPt
181 double Ecutoff;
182 if(!m_useSecondaryminPt_JES) Ecutoff = m_minPt_JES*cosh(etaBins[ieta]);
183 else {
184 if(std::abs(etaBins[ieta]) < m_etaSecondaryminPt_JES) Ecutoff = m_minPt_JES*cosh(etaBins[ieta]);
185 else{ Ecutoff = m_secondaryminPt_JES*cosh(etaBins[ieta]);}
186 }
187 const double Rcutoff = getSplineCorr(ieta, Ecutoff);
188 const double Slope = getSplineSlope(ieta, Ecutoff);
189 if(Slope > Rcutoff/Ecutoff) ATH_MSG_WARNING("Slope of calibration curve at minimum ET is too steep for the JES factors of etabin " << ieta << ", eta = " << etaBins[ieta] );
190
191 m_JES_MinPt_E[ieta] = Ecutoff;
192 m_JES_MinPt_R[ieta] = Rcutoff;
193 m_JES_MinPt_Slopes[ieta] = Slope;
194 }
195 }
196 }
197
198
199 return StatusCode::SUCCESS;
200}
201
202
204void EtaJESCorrection::loadSplineHists(const TString & fileName, const std::string &etajes_name)
205{
206 std::unique_ptr<TFile> tmpF(TFile::Open( fileName ));
207 TList *etajes_l = static_cast<TList*>( tmpF->Get(etajes_name.c_str()));
208
209 m_etajesFactors.resize( etajes_l->GetSize() );
210 if(etajes_l->GetSize() != m_etaBinAxis->GetNbins()+1){
211 ATH_MSG_WARNING("Do not have the correct number of eta bins for " << fileName << "\t" << etajes_name << "\t" << etajes_l->GetSize() );
212 }
213
214 for(int i=0 ; i<m_etaBinAxis->GetNbins(); i++){
215 auto *pTH1 = dynamic_cast<TH1*>(etajes_l->At(i));
216 if (not pTH1) continue;
217 m_etajesFactors[i].reset(pTH1);
218 m_etajesFactors[i]->SetDirectory(nullptr);
219 }
220 tmpF->Close();
221}
222
223
225
226 xAOD::JetFourMom_t jetStartP4;
228 jetStartP4 = jet.jetP4();
229
230 //Apply the JES calibration scale factor
231 //Takes the uncorrected jet eta (in case the origin and/or 4vector jet area corrections were applied
232 float detectorEta = jet.getAttribute<float>("DetectorEta");
233
234 xAOD::JetFourMom_t calibP4 = jetStartP4*getJES( jetStartP4.e(), detectorEta );
235
236 const double etaCorr = calibP4.eta() + getEtaCorr( calibP4.e(), detectorEta );
237 double massCorr;
238 if(!m_applyMassCorrection) massCorr = calibP4.mass();
239 else{ massCorr = jetStartP4.mass()*getMassCorr(calibP4.e(), detectorEta); }
240 TLorentzVector TLVjet;
241 TLVjet.SetPtEtaPhiM( calibP4.P()/cosh(etaCorr),etaCorr,calibP4.phi(),massCorr );
242 calibP4.SetPxPyPzE( TLVjet.Px(), TLVjet.Py(), TLVjet.Pz(), TLVjet.E() );
243
244 if(m_dev){
245 float JESFactor = calibP4.e()/jetStartP4.e();
246 jet.setAttribute<float>("JetJESCalibFactor",JESFactor);
247 }
248
249 //Transfer calibrated jet properties to the Jet object
250 jet.setAttribute<xAOD::JetFourMom_t>("JetEtaJESScaleMomentum",calibP4);
251 jet.setJetP4( calibP4 );
252
253 return StatusCode::SUCCESS;
254}
255
256// E_uncorr is the EM-scale, or LC-scale jet energy
257// eta_det is the eta of the raw, constituent-scale jet (constscale_eta)
258double EtaJESCorrection::getJES(double E_uncorr, double eta_det) const {
259
260 double E = E_uncorr/m_GeV; // E in GeV
261 //Check if the Pt goes below the minimum value, if so use the special GetLowPtJES method
262 const double pt = E / std::cosh(eta_det);
263
264 const double minPt =
267
268 if (pt < minPt) {
269 double R = getLowPtJES(E, eta_det);
270 if (R == 0.)[[unlikely]]{
271 ATH_MSG_ERROR("EtaJESCorrection::getJES: Divisor 'R' is zero.");
272 return 0.;
273 }
274 return 1.0 / R;
275 }
276 // Get the factors
277 int ieta = getEtaBin(eta_det);
278
279 // Freeze correction
280 if(m_freezeJESatHighE && E>m_energyFreezeJES[ieta] && m_energyFreezeJES[ieta]!=-1) E = m_energyFreezeJES[ieta];
281
282
283 // The low pT extrapolation doesn't work for the spline yet, so putting this code here.
284 if(m_isSpline){
285 double R = getSplineCorr(ieta, E);
286 return 1.0/R;
287 }
288 const double *factors = m_JESFactors[ieta];
289
290
291 // Calculate the jet response and then the JES as 1/R
292 double R = getLogPolN(factors,E);
293 if (R == 0.)[[unlikely]]{
294 ATH_MSG_ERROR("EtaJESCorrection::getJES: Divisor 'R' is zero.");
295 return 0.;
296 }
297 return 1.0/R;
298}
299
300double EtaJESCorrection::getLowPtJES(double E_uncorr, double eta_det) const {
301 int ieta = getEtaBin(eta_det);
302 if (m_lowPtExtrap == 0) {
303 const double *factors = m_JESFactors[ieta];
304 double E = m_minPt_JES*cosh(eta_det);
305 double R = getLogPolN(factors,E);
306 return R;
307 }
308 else if (m_lowPtExtrap == 1) {
309 double Ecutoff = m_JES_MinPt_E[ieta];
310 double Rcutoff = m_JES_MinPt_R[ieta];
311 double slope = m_JES_MinPt_Slopes[ieta];
312 double R = slope*(E_uncorr-Ecutoff)+Rcutoff;
313 return R;
314 }
315 else if(m_lowPtExtrap == 2) {
316 double minR = m_lowPtMinR;
317 double R = minR + m_JES_MinPt_Param1[ieta]*E_uncorr + m_JES_MinPt_Param2[ieta]*E_uncorr*E_uncorr;
318 return R;
319 }
320 else ATH_MSG_WARNING("Incorrect specification of low Pt JES extrapolation, please check the value of the LowPtJESExtrapolationMethod config flag.");
321 return 1;
322}
323
324double EtaJESCorrection::getSplineSlope(const int ieta, const double minE) const {
325 // Don't want to use interpolation here, so instead just use the values at the bin centers near the cutoff
326 int minBin = m_etajesFactors[ieta]->FindBin(minE);
327
328 double rFirst = m_etajesFactors[ ieta ]->GetBinContent(minBin);
329 double rSecond = m_etajesFactors[ ieta ]->GetBinContent(minBin+1);
330 double binWidth = m_etajesFactors[ ieta ]->GetBinCenter(minBin+1) - m_etajesFactors[ ieta ]->GetBinCenter(minBin);
331 double slope = (rSecond - rFirst) / binWidth;
332
333 return slope;
334}
335
336
337double EtaJESCorrection::getSplineCorr(const int etaBin, double E) const {
338 double R = m_etajesFactors[ etaBin ]->Interpolate(E);
339 return R;
340}
341
342
343
344double EtaJESCorrection::getEtaCorr(double E_corr, double eta_det) const {
345 int ieta = getEtaBin(eta_det);
346 const double *factors = m_etaCorrFactors[ieta];
347
348 double E = E_corr/m_GeV;
349 if ( E < m_minPt_EtaCorr*std::cosh(eta_det) )
350 E = m_minPt_EtaCorr*std::cosh(eta_det);
352
353 double etaCorr = getLogPolN(factors,E);
354
355 // This is ( reco_eta - truth_eta )
356 // to make it an additive correction return the negative value
357 return -etaCorr;
358}
359
360double EtaJESCorrection::getMassCorr(double E_corr, double eta_det) const {
361
362 if (!m_applyMassCorrection) { ATH_MSG_FATAL( "You can't apply the mass correction unless you specify ApplyMassCorrection: true in the configuration file!" ); return 0; }
363
364 int ieta = getEtaBin(eta_det);
365 const double *factors = m_JMSFactors[ieta];
366 double E = ( E_corr/std::cosh(eta_det)<5.0*m_GeV ? 5.0*std::cosh(eta_det) : E_corr/m_GeV ); // E in GeV
367
368 double massR = getLogPolN(factors,E);
369 if (massR == 0.) [[unlikely]]{
370 ATH_MSG_ERROR( "EtaJESCorrection::getMassCorr: Divisor 'massR' is zero");
371 return 0.;
372 }
373 return 1.0/massR;
374}
375
376double EtaJESCorrection::getLogPolN(const double *factors, double x) const {
377 double y=0;
378 for ( int i=0; i<m_nPar; ++i )
379 y += factors[i]*std::pow(std::log(x),i);
380 return y;
381}
382
383double EtaJESCorrection::getLogPolNSlope(const double *factors, double x) const {
384 double y=0;
385 const double inv_x = 1. / x;
386 for ( int i=0; i<m_nPar; ++i )
387 y += i*factors[i]*std::pow(std::log(x),i-1)*inv_x;
388 return y;
389}
390
391int EtaJESCorrection::getEtaBin(double eta_det) const {
392 int bin = std::as_const(m_etaBinAxis)->FindBin(eta_det);
393 if (bin<=0) return 0;
394 if (bin>m_etaBinAxis->GetNbins()) return bin-2; // overflow
395 return bin-1;
396}
397
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
unsigned int uint
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
#define y
#define x
Header file for AthHistogramAlgorithm.
double m_JESFactors[s_nEtaBins][s_nParMax]
virtual StatusCode calibrate(xAOD::Jet &jet, JetEventInfo &) const override
static const int s_nEtaBins
unsigned int m_lowPtExtrap
virtual StatusCode initialize() override
double getSplineSlope(const int ieta, const double minE) const
double m_JES_MinPt_Slopes[s_nEtaBins]
double m_JES_MinPt_R[s_nEtaBins]
double m_JMSFactors[s_nEtaBins][s_nParMax]
double getLogPolNSlope(const double *factors, double x) const
double getJES(double E_uncorr, double eta_det) const
double m_energyFreezeJES[s_nEtaBins]
double getEtaCorr(double E_corr, double eta_det) const
double getLowPtJES(double E_uncorr, double eta_det) const
double m_JES_MinPt_Param1[s_nEtaBins]
void loadSplineHists(const TString &fileName, const std::string &etajes_name="etaJes")
Loads the calib constants from histograms in TFile named fileName.
double getSplineCorr(const int etaBin, double E) const
double getMassCorr(double E_corr, double eta_det) const
std::vector< std::unique_ptr< TH1 > > m_etajesFactors
static const int s_nParMax
double m_etaCorrFactors[s_nEtaBins][s_nParMax]
double m_JES_MinPt_Param2[s_nEtaBins]
int getEtaBin(double eta_det) const
double m_JES_MinPt_E[s_nEtaBins]
double getLogPolN(const double *factors, double x) const
virtual StatusCode setStartP4(xAOD::Jet &jet) const
JetCalibrationStep(const char *name="JetCalibrationStep")
void binWidth(TH1 *h)
Definition listroot.cxx:80
VecD VectorizeD(const TString &str, const TString &sep=" ")
STL namespace.
Jet_v1 Jet
Definition of the current "jet version".
ROOT::Math::LorentzVector< ROOT::Math::PtEtaPhiM4D< double > > JetFourMom_t
Base 4 Momentum type for Jet.
Definition JetTypes.h:17
#define unlikely(x)