ATLAS Offline Software
Loading...
Searching...
No Matches
Generic4VecCorrection.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
9#include "TH2.h"
10#include <TEnv.h>
11#include "TFile.h"
12#include "TObjString.h"
13#include <algorithm>
14#include <fstream>
15
17 const TString &jetAlgo, const TString &calibAreaTag, const TString & forceCalibFile,
18 JET_CORRTYPE correctionType, const TString & mcCampaign, const TString & simFlavour, int mcDSID,
19 const TString &generatorsInfo)
21 m_config(config), m_jetAlgo(jetAlgo), m_calibAreaTag(calibAreaTag), m_correctionType(correctionType),
22 m_simFlavour(simFlavour), m_mcDSID(mcDSID), m_generatorsInfo(generatorsInfo), m_mcCampaign(mcCampaign), m_forceCalibFile(forceCalibFile), m_skipCorrection(false), m_correctionFilePath("")
23{ }
24
30
33
35{
36 ATH_MSG_INFO("Initializing Generic4VecCorrection correction tool.");
37
38 if (!m_config){
39 ATH_MSG_FATAL("Config file not specified. Aborting.");
40 return StatusCode::FAILURE;
41 }
42
43 // Set correction-specific information & retrieve response histogram(s)
44 std::string algo_type, default_OutJetScale;
46 algo_type = "JPS_PtResidual";
47 default_OutJetScale = "JetPtResidualScaleMomentum";
49 // Save etaAxis to assist in avoiding eta-interpolation
50 m_etaAxis = *(m_only_correction_2D->GetYaxis());
51
53 algo_type = "JPS_MC2MC";
54 default_OutJetScale = "JetMC2MCScaleMomentum";
55 bool isMC = TString(m_simFlavour).Contains("FullG4",TString::kIgnoreCase) || TString(m_simFlavour).Contains("ATLFAST3",TString::kIgnoreCase);
56 if (m_simFlavour == ""){
57 ATH_MSG_WARNING("No simFlavour metadata available for this sample! Assuming it is MC, but this could cause an error if your sample is not listed in MC2MC_exceptions_DSID.json");
58 isMC = true;
59 }
60 if(!isMC){
61 if( m_forceCalibFile=="" ){
62 ATH_MSG_INFO("Will not apply JPS_MC2MC to this Data file.");
63 m_skipCorrection = true;
64 return StatusCode::SUCCESS;
65 } else {
66 ATH_MSG_WARNING("Metadata does not indicate this is MC (assuming data), but ForceCalibFile is set so will apply JPS_MC2MC correction anyway.");
67 }
68 }
70
72 algo_type = "JPS_FastSim";
73 default_OutJetScale = "JetFastSimScaleMomentum";
74
75 bool isAF3 = TString(m_simFlavour).Contains("ATLFAST3",TString::kIgnoreCase);
76 if(!isAF3){
77 if( m_forceCalibFile==""){
78 ATH_MSG_INFO("Will not apply JPS_FastSim to this Data or FullSim file.");
79 m_skipCorrection = true;
80 return StatusCode::SUCCESS;
81 } else {
82 ATH_MSG_WARNING("ForceCalibFile is set for a data or FullSim file, will apply JPS_FastSim correction anyway.");
83 }
84 }
86
87 } else{
88 ATH_MSG_FATAL("Generic4VecCorrection is incorrectly configured. Aborting.");
89 return StatusCode::FAILURE;
90 }
91
92 // Get the starting and ending jet scales
93 m_inJetScale = m_config->GetValue( (algo_type+".InJetScale").c_str(), "Default");
94 m_outJetScale = m_config->GetValue( (algo_type+".OutJetScale").c_str(), default_OutJetScale.c_str());
95 if ( m_inJetScale != "Default" || m_outJetScale != default_OutJetScale ){
96 ATH_MSG_WARNING(algo_type << " is configured to use custom jet scale input " << m_inJetScale << "and/or custom jet scale output " << m_outJetScale << ", this is expert-level only!");
97 }
98
99 ATH_MSG_INFO("Starting " << algo_type << " correction from jet scale " << m_inJetScale << " and writing out jet scale " << m_outJetScale << ", using input file " << m_correctionFilePath);
100 return StatusCode::SUCCESS;
101}
102
103// Get correctionFactor from requested histogram
104StatusCode Generic4VecCorrection::readHisto(float& correctionFactor, TH2* h_correction_2D, float x, float y) const
105{
106 // If we are outside histogram boundaries, silently take value of closest bin
107 const float minX = h_correction_2D->GetXaxis()->GetBinLowEdge(1);
108 const float maxX = h_correction_2D->GetXaxis()->GetBinLowEdge(h_correction_2D->GetNbinsX()+1);
109 const float minY = h_correction_2D->GetYaxis()->GetBinLowEdge(1);
110 const float maxY = h_correction_2D->GetYaxis()->GetBinLowEdge(h_correction_2D->GetNbinsY()+1);
111 if ( x >= maxX )
112 x = maxX - 1.e-6;
113 else if ( x <= minX )
114 x = minX + 1.e-6;
115 if ( y >= maxY )
116 y = maxY - 1.e-6;
117 else if ( y <= minY )
118 y = minY + 1.e-6;
119
120 // Interpolate final correctionFactor
121 correctionFactor = h_correction_2D->Interpolate(x,y);
122
123 return StatusCode::SUCCESS;
124}
125
126// Perform 4Vec correction for a jet
128{
129 (void)jetEventInfo; //Unused
130
131 // Skip correction if we've determined it is not to be applied to this input file
133 return StatusCode::SUCCESS;
134
135 xAOD::JetFourMom_t calibP4;
136 if (m_inJetScale == "Default")
137 calibP4 = jet.jetP4();
138 else
139 calibP4 = jet.jetP4(m_inJetScale);
140
141 float correctionFactor = 1.0;
142
143 TH2* h_correction_2D = nullptr;
144 float this_pt, this_eta;
146 this_pt = jet.pt()/1000.;
147 static const SG::ConstAccessor<float> DetectorEtaAcc ("DetectorEta");
148 this_eta = DetectorEtaAcc(jet);
149 h_correction_2D = m_only_correction_2D;
150
151 // PtResidual should not interpolate across eta bins, so set this_eta to the center of its histogram bin
152 int eta_bin = m_etaAxis.FindBin(this_eta);
153 this_eta = m_etaAxis.GetBinCenter(eta_bin);
154
156 this_pt = jet.pt()/1000.;
157 this_eta = fabs(jet.rapidity());
158 h_correction_2D = m_only_correction_2D;
160 this_pt = jet.pt()/1000.;
161 this_eta = fabs(jet.rapidity());
162
163 static const SG::ConstAccessor<int> PartonTruthLabelIDAcc ("PartonTruthLabelID");
164 if(!PartonTruthLabelIDAcc.isAvailable(jet))
165 return StatusCode::SUCCESS;
166 int jet_PID = abs(PartonTruthLabelIDAcc(jet));
167
168 // If this jet_PID is in the map, get its h_correction_2D and find the correctionFactor
169 auto correction_from_map = m_correctionHists.find(jet_PID);
170 if (correction_from_map != m_correctionHists.end()){
171 h_correction_2D = correction_from_map->second;
172 }
173 }
174
175 if (h_correction_2D){
176 ATH_CHECK( readHisto(correctionFactor, h_correction_2D, this_pt, this_eta) );
177 }
178 // Apply the correction and set it in the jet EDM
179 calibP4 *= correctionFactor;
180 jet.setAttribute<xAOD::JetFourMom_t>(m_outJetScale,calibP4);
181 jet.setJetP4(calibP4);
182
183 return StatusCode::SUCCESS;
184}
185
187{
188 std::string algo_type, calibFilePrepend, corrHistName;
190 algo_type = "JPS_PtResidual";
191 calibFilePrepend = "PtResidual";
192 corrHistName = "h_respMap_recoPt_DetEta";
194 algo_type = "JPS_FastSim";
195 calibFilePrepend = "AF3";
196 corrHistName = "h_respMap_recoPt_recoY";
197 }
198
199 // If CalibFile is set in tool configuration, we will force that generator correction.
200 // Used if metadata is not available for this file or for expert-level tests
201 TString CalibFile;
202 if (m_forceCalibFile != ""){
203 CalibFile = m_forceCalibFile;
204 ATH_MSG_WARNING("Have forced " << algo_type << " CalibFile to be " << m_forceCalibFile);
205
206 } else {
207 // Recommended method to build the correct CalibFile
208 std::string CalibFileTag = m_config->GetValue( (algo_type+".CalibFileTag").c_str(), "");
209 if( CalibFileTag == "" || m_jetAlgo == "" || m_mcCampaign == ""){
210 ATH_MSG_FATAL("At least one of the required parameters is not set, please check m_mcCampaign (" << m_mcCampaign << "), m_jetAlgo (" << m_jetAlgo << "), and " << algo_type << ".CalibFileTag (" << CalibFileTag <<")");
211 return StatusCode::FAILURE;
212 }
213 CalibFile.Append(m_calibAreaTag+"/CalibrationFactors/"+calibFilePrepend+"_"+m_mcCampaign+"_"+m_jetAlgo+"_"+CalibFileTag+".root");
214 }
215
217 if(m_correctionFilePath == ""){
218 ATH_MSG_FATAL("PathResolverFindCalibFile cannot find path to " << CalibFile);
219 return StatusCode::FAILURE;
220 }
221 std::unique_ptr<TFile> inputFile(TFile::Open(m_correctionFilePath.c_str()));
222 if (!inputFile || inputFile->IsZombie()){
223 ATH_MSG_FATAL("Cannot open " << algo_type << "'s CalibFile, even though the m_correctionFilePath exists: " << CalibFile);
224 return StatusCode::FAILURE;
225 }
226
227 m_only_correction_2D = (TH2*)inputFile->Get( corrHistName.c_str() );
229 ATH_MSG_FATAL("Failed to retrieve histogram: " << corrHistName);
230 return StatusCode::FAILURE;
231 }
232 m_only_correction_2D->SetDirectory(0);
233
234 inputFile->Close();
235 return StatusCode::SUCCESS;
236}
237
238StatusCode Generic4VecCorrection::load_json(nlohmann::json& json_object, const std::string& json_filepath) const
239{
240 std::string full_path = PathResolverFindCalibFile(json_filepath);
241 std::ifstream json_stream(full_path);
242 if( json_stream.peek() == std::ifstream::traits_type::eof() )
243 return StatusCode::FAILURE;
244
245 json_stream >> json_object;
246 return StatusCode::SUCCESS;
247}
248
249
250
252{
253
254 std::string showerModel;
256
257 // If CalibFile is set, we will force that generator correction. This is expert-level functionality
258 TString MC2MC_CalibFile;
259 if (m_forceCalibFile != ""){
260 MC2MC_CalibFile = m_forceCalibFile;
261 ATH_MSG_WARNING("For sample of showerModel " << showerModel << ", have forced MC2MC CalibFile to be " << m_forceCalibFile);
262
263 // Set showerModel to the requested version
264 TObjArray *CalibFile_fields = MC2MC_CalibFile.Tokenize("_");
265 float n_fields = CalibFile_fields->GetEntries();
266 std::string new_showerModel = ((TObjString *)(CalibFile_fields->At(n_fields-1)))->String().Data();
267 new_showerModel.resize(new_showerModel.find(".root")); //Remove .root
268 showerModel = std::move(new_showerModel);
269
270 } else {
271 // Recommended method to build the correct CalibFile
272 std::string MC2MC_CalibFileTag = m_config->GetValue("JPS_MC2MC.CalibFileTag","");
273 if( MC2MC_CalibFileTag == "" || m_jetAlgo == "" || m_mcCampaign == "" || showerModel == ""){
274 ATH_MSG_FATAL("At least one of the required parameters is not set, please check m_mcCampaign (" << m_mcCampaign << "), m_jetAlgo (" << m_jetAlgo << "), JPS_MC2MC.CalibFileTag (" << MC2MC_CalibFileTag <<"), and showerModel (" << showerModel <<")");
275 return StatusCode::FAILURE;
276 }
277 MC2MC_CalibFile.Append(m_calibAreaTag+"/CalibrationFactors/MC2MC_"+m_mcCampaign+"_"+m_jetAlgo+"_"+MC2MC_CalibFileTag+"_"+showerModel+".root");
278 }
279 // If the found / requested showerModel is the original Pythia used for calibrations, or was specifically set to None, skip the MC2MC correction
280 if (showerModel.starts_with("Pythia") || showerModel.starts_with("None")){
281 m_skipCorrection = true;
282 ATH_MSG_INFO("Will not perform MC2MC correction for this sample (Pythia or forced to None), but will write out the redundant jet scale " << m_outJetScale);
283 return StatusCode::SUCCESS;
284 }
285 m_correctionFilePath = PathResolverFindCalibFile(MC2MC_CalibFile.Data());
286 if(m_correctionFilePath == ""){
287 ATH_MSG_FATAL("PathResolverFindCalibFile cannot find path to MC2MC CalibFile: " << MC2MC_CalibFile);
288 return StatusCode::FAILURE;
289 }
290 std::unique_ptr<TFile> inputFile(TFile::Open(m_correctionFilePath.c_str()));
291 if (!inputFile || inputFile->IsZombie()){
292 ATH_MSG_FATAL("Cannot open MC2MC CalibFile, even though the m_correctionFilePath exists: " << MC2MC_CalibFile);
293 return StatusCode::FAILURE;
294 }
295 // Get list of jet PIDs to perform correction on
296 std::vector<int> considered_PIDs = {1,2,3,21}; //Always correct u/d/s/g
297 bool doCjetCorrection = m_config->GetValue("JPS_MC2MC.doCjetCorrection",true);
298 if(doCjetCorrection)
299 considered_PIDs.push_back(4);
300 bool doBjetCorrection = m_config->GetValue("JPS_MC2MC.doBjetCorrection",true);
301 if(doBjetCorrection)
302 considered_PIDs.push_back(5);
303
304 // Load all the requested MC2MC correction histograms into the correction map
305 for (auto this_PID : considered_PIDs){
306 TString this_hist_name;
307 if(this_PID == 1 || this_PID == 2 || this_PID == 3){
308 this_hist_name = "h_respMap_recoPt_recoY_q";
309 } else if(this_PID == 4){
310 this_hist_name = "h_respMap_recoPt_recoY_c";
311 } else if(this_PID == 5){
312 this_hist_name = "h_respMap_recoPt_recoY_b";
313 } else if(this_PID == 21){
314 this_hist_name = "h_respMap_recoPt_recoY_g";
315 } else {
316 ATH_MSG_FATAL("Requested PID " << this_PID << " is not supported for MC2MC correction, please contact JetETMiss.");
317 return StatusCode::FAILURE;
318 }
319 TH2* this_hist = (TH2*)inputFile->Get(this_hist_name);
320 if (!this_hist){
321 ATH_MSG_FATAL("Failed to retrieve histogram: " << this_hist_name);
322 return StatusCode::FAILURE;
323 }
324 this_hist->SetName( ("h_respMap_recoPt_recoY_"+std::to_string(this_PID)).c_str() );
325 this_hist->SetDirectory(0);
326 m_correctionHists.insert( std::make_pair(this_PID, this_hist) );
327 }
328 inputFile->Close();
329
330 return StatusCode::SUCCESS;
331}
332
333// For MC2MC, parse showerModel from generatorsInfo, of form "Powheg(v.06-02)+Herwig7(v.7.2.3p2)+EvtGen(v.2.1.1)""
334StatusCode Generic4VecCorrection::parse_showerModel(std::string& showerModel, int mcDSID, TString generatorsInfo) const
335{
336
337 // Check if an exception to the showerModel exists for this DSID
338 nlohmann::json MC2MC_exceptions_DSID;
339 ATH_CHECK( load_json(MC2MC_exceptions_DSID, "JetCalibTools/MC2MC_exceptions_DSID.json") );
340 if( MC2MC_exceptions_DSID.contains(std::to_string(mcDSID)) ){
341 showerModel = MC2MC_exceptions_DSID[std::to_string(mcDSID)];
342 ATH_MSG_INFO("Sample DSID " << mcDSID << " is in the MC2MC_exceptions_DSID list, will be forcing the showerModel " << showerModel);
343 return StatusCode::SUCCESS;
344 }
345
346 if(generatorsInfo == ""){
347 showerModel="";
348 ATH_MSG_DEBUG("No generatorsInfo string provided, cannot parse showerModel.");
349 return StatusCode::SUCCESS;
350 }
351
352 // Parse shower model from MC sample generatorInfo
353 TObjArray *generatorsInfo_fields = generatorsInfo.Tokenize("+");
354 float n_fields = generatorsInfo_fields->GetEntries();
355 std::string this_substr = ((TObjString *)(generatorsInfo_fields->At(n_fields-1)))->String().Data();
356
357 // Remove any trailing afterburners
358 while( n_fields > 0 &&
359 (this_substr.starts_with("EvtGen") ||
360 this_substr.starts_with("Photos") ||
361 this_substr.starts_with("Tauola") ) ) {
362 generatorsInfo_fields->RemoveAt(n_fields-1);
363 n_fields--;
364
365 if ( n_fields == 0 ){
366 ATH_MSG_FATAL("No valid PS/Had model found in generatorsInfo string: " << generatorsInfo);
367 return StatusCode::FAILURE;
368 }
369
370 this_substr = ((TObjString *)(generatorsInfo_fields->At(n_fields-1)))->String().Data();
371 }
372 std::string full_pshadInfo = this_substr;
373
374 // Find generator type and set default Parton Shower / Hadronization models
375 std::string genType = "";
376 std::string psType = "";
377 std::string hadType = "";
378 std::string version = "";
379 if (this_substr.starts_with("Herwigpp")){
380 genType = "Herwigpp";
381 psType = "angular";
382 hadType = "cluster";
383 } else if (this_substr.starts_with("Herwig")){
384 genType = "Herwig";
385 psType = "angular";
386 hadType = "cluster";
387 } else if (this_substr.starts_with("Sherpa")){
388 genType = "Sherpa";
389 psType = "dipole";
390 hadType = "cluster";
391 } else if (this_substr.starts_with("Pythia8B")){
392 genType = "PythiaB";
393 psType = "dipole";
394 hadType = "cluster";
395 version += "8";
396 } else if (this_substr.starts_with("Pythia")){
397 genType = "Pythia";
398 psType = "dipole";
399 hadType = "cluster";
400 } else {
401 ATH_MSG_FATAL("No valid generator type found in generatorsInfo string: " << generatorsInfo);
402 return StatusCode::FAILURE;
403 }
404
405 // Parse version number of the generator
406 this_substr = this_substr.substr(this_substr.find("(v.") + 3); // Remove up to first version number in e.g. Herwig7(v.7.2.3p2)
407 if( full_pshadInfo.starts_with("Pythia8") && !this_substr.starts_with("8")){ // Exception for out-of-order Pythia version
408 version += "8";
409 }
410
411 // Remove any trailing characters after the version number e.g. (v.7.2.3p2)
412 std::vector<std::string> version_exceptions = {"alpha", "p", "bbb", "atlas", "beta", ")"};
413 for (const auto& exception : version_exceptions) {
414 if (this_substr.find(exception) != std::string::npos) {
415 this_substr.resize(this_substr.find(exception));
416 }
417 }
418
419 // Remove all periods so that only numbers remain
420 this_substr.erase(std::remove(this_substr.begin(), this_substr.end(), '.'), this_substr.end());
421
422 // What is left is the version tag
423 version += this_substr;
424
425 // Build the full showerModel tag
426 showerModel = genType+"-"+version+"-"+psType+"-"+hadType;
427
428 // Check if a remap of this showerModel version is requested, if so use it
429 // We do this even for mapped_DSID, to allow for simple swapping of showerModel across all exceptions
430 nlohmann::json MC2MC_showerRemap;
431 ATH_CHECK( load_json(MC2MC_showerRemap, "JetCalibTools/MC2MC_showerRemap.json") );
432
433 if( MC2MC_showerRemap.contains( genType+"-"+version+"-"+psType+"-"+hadType ) ){
434 std::string replaced_showerModel = MC2MC_showerRemap[ genType+"-"+version+"-"+psType+"-"+hadType ];
435 ATH_MSG_INFO("Sample with identified showerModel " << genType+"-"+version+"-"+psType+"-"+hadType << " is in the MC2MC_showerRemap list, will be forcing the showerModel " << replaced_showerModel);
436 showerModel = std::move(replaced_showerModel);
437 } else if (MC2MC_showerRemap.contains( genType+"-"+version ) ){
438 std::string replaced_showerModel = MC2MC_showerRemap[ genType+"-"+version ];
439 replaced_showerModel += "-"+psType+"-"+hadType;
440 ATH_MSG_INFO("Sample with identified showerModel " << genType+"-"+version+"-"+psType+"-"+hadType << " is in the MC2MC_showerRemap list, will be forcing the showerModel " << replaced_showerModel);
441 showerModel = std::move(replaced_showerModel);
442 }
443
444 return StatusCode::SUCCESS;
445}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
#define y
#define x
const std::string m_calibAreaTag
const std::string m_generatorsInfo
const std::string m_simFlavour
virtual StatusCode initialize() override
StatusCode parse_showerModel(std::string &showerModel, int mcDSID, TString generatorsInfo) const
const std::string m_mcCampaign
StatusCode readHisto(float &correctionFactor, TH2 *h_correction_2D, float x, float y) const
StatusCode load_json(nlohmann::json &json_object, const std::string &json_filepath) const
const std::string m_forceCalibFile
virtual StatusCode calibrate(xAOD::Jet &jet, JetEventInfo &) const override
std::map< int, TH2 * > m_correctionHists
JetCalibrationStep(const char *name="JetCalibrationStep")
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.
DataModel_detail::iterator< DVL > remove(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end, const T &value)
Specialization of remove for DataVector/List.
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