ATLAS Offline Software
Loading...
Searching...
No Matches
GlobalNNCalibration.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
4
5/* ***********************************************************************************\
6 * *
7 * Name: GlobalNNCalibration *
8 * Purpose: Perform the NN GSC step of the calibration *
9 * *
10 * # Date Comments By *
11 * -- -------- -------------------------- ------------------------------------------ *
12 * 1 25/06/20 First Version I. Aizenberg *
13 * 1 25/06/20 Updates to work with JetCalibTools J. Roloff *
14 * 1 21/08/23 Updates to be more configurable J. Roloff *
15\*************************************************************************************/
16
18#include <TEnv.h>
19#include "TFile.h"
20#include "TList.h"
22#include "lwtnn/parse_json.hh"
25#include <fstream>
26#include <memory>
27#include <utility>
28
29
31 : JetCalibrationStep::JetCalibrationStep("GlobalNNCalibration/GlobalNNCalibration"),
32m_config(nullptr), m_jetAlgo(""), m_calibAreaTag(""), m_dev(false), m_doSplineCorr(true)
33{
34}
35
36
37// Constructor
40m_config(nullptr), m_jetAlgo(""), m_calibAreaTag(""), m_dev(false), m_doSplineCorr(true)
41{
42}
43
44GlobalNNCalibration::GlobalNNCalibration(const std::string& name, TEnv * config, TString jetAlgo, const TString& calibAreaTag, bool dev)
45 : JetCalibrationStep::JetCalibrationStep( name.c_str() ),
46 m_config(config), m_jetAlgo(std::move(jetAlgo)), m_calibAreaTag(calibAreaTag), m_dev(dev), m_doSplineCorr(true)
47{
48
49}
50
51// Initialize
53
54 ATH_MSG_INFO("Initializing tool");
55 m_nnEtaBins = JetCalibUtils::VectorizeD( m_config->GetValue("GNNC.EtaBinsForNN","") );
56 m_closureEtaBins = JetCalibUtils::VectorizeD( m_config->GetValue("GNNC.EtaBinsForClosure","") );
57 m_NNInputs = JetCalibUtils::Vectorize( m_config->GetValue("GNNC.Inputs","") );
58 m_doSplineCorr = m_config->GetValue("GNNC.UseSplineCorr", true);
59 m_doLogPtScaling = m_config->GetValue("GNNC.UseLogPtScaling", false);
60 m_maxNNCorrection = m_config->GetValue("GNNC.MaxNNCorrection", 2.0);
61 m_minNNCorrection = m_config->GetValue("GNNC.MinNNCorrection", 0.5);
62
63 // This code is copied from the GlobalSequentialCorrection code,
64 // so while it is hard-coded, it is consistent with how these filenames are parsed.
65 if ( m_jetAlgo.EqualTo("") ) { ATH_MSG_FATAL("No jet algorithm specified. Aborting."); return StatusCode::FAILURE; }
66
67 //find the ROOT file containing response histograms, path comes from the config file.
68 TString MLGSCFile = m_config->GetValue("GNNC.MLFactorsFile","empty");
69 if ( MLGSCFile.EqualTo("empty") ) {
70 ATH_MSG_FATAL("NO MLFactorsFile specified. Aborting.");
71 return StatusCode::FAILURE;
72 }
73 if(m_dev){
74 MLGSCFile.Remove(0,33);
75 MLGSCFile.Insert(0,"JetCalibTools/");
76 }
77 else{MLGSCFile.Insert(14,m_calibAreaTag);}
78
79
80 // Get all of the different NN json files for each eta bin
81 for(unsigned int i=0; i<m_nnEtaBins.size()-1; i++){
82 TString fileName = PathResolverFindCalibFile(Form("%s_etabin_%d.json", MLGSCFile.Data(), i));
83 std::ifstream input(fileName);
84 std::unique_ptr<lwt::LightweightGraph> lwnn = std::make_unique<lwt::LightweightGraph> ( lwt::parse_json_graph(input) );
85 m_lwnns.push_back(std::move(lwnn));
86 }
87
88
89
90 // An additional correction on top of the neural network to smooth out fluctuations in the pt response
92 TString ptCalibHists = m_config->GetValue("GNNC.JPtS_CalibHists","");
93 if(m_dev){
94 ptCalibHists.Remove(0,33);
95 ptCalibHists.Insert(0,"JetCalibTools/");
96 }
97 else{
98 ptCalibHists.Insert(14,m_calibAreaTag);
99 }
100 TString calibHistFile = PathResolverFindCalibFile(ptCalibHists.Data());
101 loadSplineHists(calibHistFile, "etaJes");
102 m_JPtS_MinPt_Pt = JetCalibUtils::VectorizeD( m_config->GetValue("GNNC.ptCutoff","") );
103 if(m_JPtS_MinPt_Pt.size() != m_closureEtaBins.size()-1){
104 ATH_MSG_FATAL("Pt cutoff vector has wrong length. There should be one value per eta bin."); return StatusCode::FAILURE;
105 return StatusCode::FAILURE;
106 }
107
108 for (uint ieta=0;ieta<m_closureEtaBins.size()-1;++ieta) {
109 //Calculate the slope of the response curve at the minPt for each eta bin
110 //Used in the GetLowPtJPtS method when Pt < minPt
111 const double Rcutoff = getSplineCorr(ieta, m_JPtS_MinPt_Pt[ieta]);
112 const double Slope = getSplineSlope(ieta, m_JPtS_MinPt_Pt[ieta]);
113 if(Slope > Rcutoff/m_JPtS_MinPt_Pt[ieta]) ATH_MSG_WARNING("Slope of calibration curve at minimum ET is too steep for the JPtS factors of etabin " << ieta << ", eta = " << m_closureEtaBins[ieta] );
114
115 m_JPtS_MinPt_R.push_back(Rcutoff);
116 m_JPtS_MinPt_Slopes.push_back(Slope);
117 }
118 }
119
120
121 return StatusCode::SUCCESS;
122}
123
124
125
126StatusCode GlobalNNCalibration::calibrate(xAOD::Jet& jet, JetEventInfo& jetEventInfo) const {
127 xAOD::JetFourMom_t jetStartP4;
128 jetStartP4 = jet.jetP4();
129
130 // The NN learns the jet response, so the original jet pT is divided
131 // by the NN output to get the calibrated pT
132 int nnEtaBin = getEtaBin(jet, m_nnEtaBins);
133 int closureEtaBin = getEtaBin(jet, m_closureEtaBins);
134 std::map<std::string,double> NN_inputValues = getJetFeatures(jet, jetEventInfo);
135 std::map<std::string,std::map<std::string,double>> inputs;
136 inputs["node_0"] = NN_inputValues;
137
138 std::map<std::string, double> outputs = m_lwnns[nnEtaBin]->compute(inputs);
139 double nnCalibFactor = outputs["out_0"];
140 if(nnCalibFactor > m_maxNNCorrection) nnCalibFactor = m_maxNNCorrection;
141 if(nnCalibFactor < m_minNNCorrection) nnCalibFactor = m_minNNCorrection;
142
143 double response = nnCalibFactor;
144 if(m_doSplineCorr){
145 double jetPt = getJESPt(jet);
146 response *= getSplineCorr(closureEtaBin, jetPt/nnCalibFactor);
147 if(response == 0) response = nnCalibFactor;
148 }
149
150 xAOD::JetFourMom_t calibP4 = jetStartP4 / response;
151
152 //Transfer calibrated jet properties to the Jet object
153 jet.setAttribute<xAOD::JetFourMom_t>("JetGNNCScaleMomentum",calibP4);
154 jet.setJetP4( calibP4 );
155
156 return StatusCode::SUCCESS;
157
158}
159
160
161
162
164void GlobalNNCalibration::loadSplineHists(const TString & fileName, const std::string &ptCorr_name) {
165 std::unique_ptr<TFile> tmpF(TFile::Open( fileName ));
166 TList *ptCorr_l = dynamic_cast<TList*>( tmpF->Get(ptCorr_name.c_str()));
167 if (not ptCorr_l){
168 ATH_MSG_ERROR("TList pointer is null in GlobalNNCalibration::loadSplineHists");
169 tmpF->Close();
170 return;
171 }
172 m_ptCorrFactors.resize( ptCorr_l->GetSize() );
173 int nBinsCorr = ptCorr_l->GetSize();
174 int nEtaBins = m_closureEtaBins.size()-1;
175 if(nBinsCorr != nEtaBins){
176 ATH_MSG_WARNING("Do not have the correct number of eta bins for " << fileName << "\t" << ptCorr_name << "\t" << ptCorr_l->GetSize() );
177 }
178
179 for(unsigned int i=0 ; i<m_closureEtaBins.size()-1; i++){
180 auto *pTH1 = dynamic_cast<TH1*>(ptCorr_l->At(i));
181 if (not pTH1) continue;
182 m_ptCorrFactors[i].reset(pTH1);
183 m_ptCorrFactors[i]->SetDirectory(nullptr);
184 }
185 tmpF->Close();
186}
187
188
189
190double GlobalNNCalibration::getSplineCorr(const int etaBin, double pT) const {
191 if(pT >= m_ptCorrFactors[ etaBin ]->GetBinLowEdge( m_ptCorrFactors[ etaBin ]->GetNbinsX()+1)){
192 pT = m_ptCorrFactors[ etaBin ]->GetBinLowEdge( m_ptCorrFactors[ etaBin ]->GetNbinsX());
193 }
194 if(pT < m_JPtS_MinPt_Pt[etaBin]){
195 double ptCutoff = m_JPtS_MinPt_Pt[etaBin];
196 double Rcutoff = m_JPtS_MinPt_R[etaBin];
197 double slope = m_JPtS_MinPt_Slopes[etaBin];
198 double R = slope*(pT-ptCutoff)+Rcutoff;
199 return R;
200 }
201
202
203 double R = m_ptCorrFactors[ etaBin ]->Interpolate(pT);
204 return R;
205}
206
207
208double GlobalNNCalibration::getSplineSlope(const int ieta, const double minPt) const {
209 // Don't want to use interpolation here, so instead just use the values at the bin centers near the cutoff
210 int minBin = m_ptCorrFactors[ieta]->FindBin(minPt);
211
212 double rFirst = m_ptCorrFactors[ ieta ]->GetBinContent(minBin);
213 double rSecond = m_ptCorrFactors[ ieta ]->GetBinContent(minBin+1);
214 double binWidth = m_ptCorrFactors[ ieta ]->GetBinCenter(minBin+1) - m_ptCorrFactors[ ieta ]->GetBinCenter(minBin);
215 double slope = (rSecond - rFirst) / binWidth;
216
217 return slope;
218}
219
220
221
222
223std::map<std::string,double> GlobalNNCalibration::getJetFeatures(const xAOD::Jet& jet_reco, JetEventInfo& jetEventInfo) const {
224 std::vector<float> samplingFrac = jet_reco.getAttribute<std::vector<float> >("EnergyPerSampling");
225 xAOD::JetFourMom_t jetconstitP4 = jet_reco.getAttribute<xAOD::JetFourMom_t>("JetConstitScaleMomentum");
226
227 xAOD::JetFourMom_t jetStartP4;
228 jetStartP4 = jet_reco.jetP4();
229
230 float jetE_constitscale = jetconstitP4.e();
231
232 // Get the index of the PV
233 int PVindex = jetEventInfo.PVIndex();
234
235 //EM3 and Tile0 fraction calculations
236 //EM3 = (EMB3+EME3)/energy, Tile0 = (TileBar0+TileExt0)/energy
237 //Check the map above to make sure the correct entries of samplingFrac are being used
238 float EM0 = (samplingFrac[0]+samplingFrac[4])/jetE_constitscale;
239 float EM1 = (samplingFrac[1]+samplingFrac[5])/jetE_constitscale;
240 float EM2 = (samplingFrac[2]+samplingFrac[6])/jetE_constitscale;
241 float EM3 = (samplingFrac[3]+samplingFrac[7])/jetE_constitscale;
242 float Tile0 = (samplingFrac[12]+samplingFrac[18])/jetE_constitscale;
243 float Tile1 = (samplingFrac[13]+samplingFrac[19])/jetE_constitscale;
244 float Tile2 = (samplingFrac[14]+samplingFrac[20])/jetE_constitscale;
245 float HEC0 = (samplingFrac[8])/jetE_constitscale;
246 float HEC1 = (samplingFrac[9])/jetE_constitscale;
247 float HEC2 = (samplingFrac[10])/jetE_constitscale;
248 float HEC3 = (samplingFrac[11])/jetE_constitscale;
249
250 float FCAL0 = (samplingFrac[21])/jetE_constitscale;
251 float FCAL1 = (samplingFrac[22])/jetE_constitscale;
252 float FCAL2 = (samplingFrac[23])/jetE_constitscale;
253
254
255 // A map of possible NN inputs with their values for this jet.
256 // These may not all be included in the NN.
257 std::map<std::string,double> inputValues;
258
259 // This is a list of variables that could be included in the NN.
260 // This makes it simple to change the variables in the NN without complicated changes
261 // to the code.
262 if(m_doLogPtScaling) {
263 inputValues["jet_pt"] = log10(getJESPt(jet_reco));
264 }
265 else{
266 inputValues["jet_pt"] = getJESPt(jet_reco);
267 }
268 inputValues["EM0"] = EM0;
269 inputValues["EM1"] = EM1;
270 inputValues["EM2"] = EM2;
271 inputValues["EM3"] = EM3;
272 inputValues["TILE0"] = Tile0;
273 inputValues["TILE1"] = Tile1;
274 inputValues["TILE2"] = Tile2;
275 inputValues["HEC0"] = HEC0;
276 inputValues["HEC1"] = HEC1;
277 inputValues["HEC2"] = HEC2;
278 inputValues["HEC3"] = HEC3;
279 inputValues["FCAL0"] = FCAL0;
280 inputValues["FCAL1"] = FCAL1;
281 inputValues["FCAL2"] = FCAL2;
282 inputValues["jet_Ntrk1000"] = getJetNtrk1000(jet_reco, PVindex);
283 inputValues["jet_ChargedFraction"] = getJetChargedFraction(jet_reco, PVindex);
284 inputValues["jet_Wtrk1000"] = getJetWtrk1000(jet_reco, PVindex);
285 inputValues["jet_DetEta"] = getJetDetEta(jet_reco);
286 inputValues["jet_n90Constituents"] = jet_reco.getAttribute<float>("N90Constituents");
287 inputValues["jet_nMuSeg"] = jet_reco.getAttribute<int>("GhostMuonSegmentCount");
288 inputValues["NPV"] = jetEventInfo.NPV();
289 inputValues["averageInteractionsPerCrossing"] = jetEventInfo.mu();
290
291 // The actual NN inputs and values
292 std::map<std::string,double> NNInputValues;
293 for(const TString& input : m_NNInputs){
294 NNInputValues[input.Data()] = inputValues[input.Data()];
295 }
296
297 return NNInputValues;
298}
299
300
301
302// This needs to be fixed, but just getting a placeholder
303int GlobalNNCalibration::getEtaBin(const xAOD::Jet& jet_reco, const std::vector<double>& etaBins) const{
304 double detEta = jet_reco.getAttribute<float>("DetectorEta");
305 for(unsigned int i=1; i<etaBins.size()-1; i++){
306 if(std::abs(detEta) < etaBins[i]) return i-1;
307 }
308 // This should throw an error instead probably, since this is outside of the eta range we are calibrating
309 return etaBins.size()-2;
310}
311
312
313double GlobalNNCalibration::getJetChargedFraction(const xAOD::Jet& jet_reco, int PVindex) const{
314 static const SG::ConstAccessor<std::vector<float> > SumPtChargedPFOPt500Acc ("SumPtChargedPFOPt500");
315 if( SumPtChargedPFOPt500Acc.isAvailable(jet_reco) ) {
316 float thisChargedFraction = SumPtChargedPFOPt500Acc(jet_reco).at(PVindex);
317 thisChargedFraction /= jet_reco.jetP4(xAOD::JetConstitScaleMomentum).Pt();
318 return double(thisChargedFraction);
319 }
320
321 return -999.;
322}
323
324double GlobalNNCalibration::getJetDetEta(const xAOD::Jet& jet_reco) const {
325 static const SG::ConstAccessor<float> DetectorEtaAcc ("DetectorEta");
326 return DetectorEtaAcc.withDefault (jet_reco, -999);
327}
328
329int GlobalNNCalibration::getJetNtrk1000(const xAOD::Jet& jet_reco, int PVindex) const {
330 static const SG::ConstAccessor<std::vector<int> > NumTrkPt1000Acc ("NumTrkPt1000");
331 if(NumTrkPt1000Acc.isAvailable(jet_reco))
332 return NumTrkPt1000Acc(jet_reco).at(PVindex);
333 return -999;
334}
335
336double GlobalNNCalibration::getJetWtrk1000(const xAOD::Jet& jet_reco, int PVindex) const {
337 static const SG::ConstAccessor<std::vector<float> > TrackWidthPt1000Acc ("TrackWidthPt1000");
338 if(TrackWidthPt1000Acc.isAvailable(jet_reco))
339 return double(TrackWidthPt1000Acc(jet_reco).at(PVindex));
340 return -999.;
341}
342
343
344double GlobalNNCalibration::getJESPt(const xAOD::Jet& jet_reco) const {
345 return jet_reco.jetP4("JetEtaJESScaleMomentum").pt() / 1.e3;
346}
347
348
349
350
351
352
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
Helper class to provide constant type-safe access to aux data.
unsigned int uint
MDT_Response response
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
std::vector< std::unique_ptr< TH1 > > m_ptCorrFactors
double getJetDetEta(const xAOD::Jet &jet_reco) const
Returns the detector eta of the jet.
std::vector< double > m_closureEtaBins
std::vector< double > m_JPtS_MinPt_Slopes
std::vector< double > m_JPtS_MinPt_Pt
double getJetWtrk1000(const xAOD::Jet &jet_reco, int PVindex) const
Returns the jet width.
std::vector< TString > m_NNInputs
GlobalNNCalibration()
The constructor.
int getJetNtrk1000(const xAOD::Jet &jet_reco, int PVindex) const
Returns the number of tracks with pT > 1 GeV associated to the jet.
virtual StatusCode initialize() override
Returns the charged fraction of a jet.
void loadSplineHists(const TString &fileName, const std::string &etajes_name="etaJes")
Reads the spline histograms from the file given in the config, and stores them in m_ptCorrFactors.
double getJetChargedFraction(const xAOD::Jet &jet_reco, int PVindex) const
Returns the charged fraction of a jet.
double getJESPt(const xAOD::Jet &jet_reco) const
Returns the jet pT after the MCJES calibration.
virtual StatusCode calibrate(xAOD::Jet &jet, JetEventInfo &) const override
double getSplineCorr(const int etaBin, double E) const
Returns the correction from spline histogram, which should be applied after the NN correction.
std::map< std::string, double > getJetFeatures(const xAOD::Jet &jet_reco, JetEventInfo &jetEventInfo) const
Returns a map of possible inputs to the NN, and their corresponding values for this jet.
std::vector< double > m_nnEtaBins
int getEtaBin(const xAOD::Jet &jet_reco, const std::vector< double > &etaBins) const
Returns the eta bin, as determined by a list of bin edges.
double getSplineSlope(const int ieta, const double minPt) const
Gets the slope of the spline histogram for a given eta bin, for extrapolation of the calibration.
std::vector< double > m_JPtS_MinPt_R
std::vector< std::unique_ptr< lwt::LightweightGraph > > m_lwnns
JetCalibrationStep(const char *name="JetCalibrationStep")
double mu()
double NPV()
Helper class to provide constant type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
const_reference_type withDefault(const ELT &e, const T &deflt) const
Fetch the variable for one element, as a const reference, with a default.
bool getAttribute(AttributeID type, T &value) const
Retrieve attribute moment by enum.
JetFourMom_t jetP4() const
The full 4-momentum of the particle : internal jet type.
Definition Jet_v1.cxx:76
void binWidth(TH1 *h)
Definition listroot.cxx:80
StrV Vectorize(const TString &str, const TString &sep=" ")
VecD VectorizeD(const TString &str, const TString &sep=" ")
STL namespace.
Jet_v1 Jet
Definition of the current "jet version".
@ JetConstitScaleMomentum
Definition JetTypes.h:29
ROOT::Math::LorentzVector< ROOT::Math::PtEtaPhiM4D< double > > JetFourMom_t
Base 4 Momentum type for Jet.
Definition JetTypes.h:17