ATLAS Offline Software
Loading...
Searching...
No Matches
SSVWeightsAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
7
8
9#include <nlohmann/json.hpp>
11#include <stdexcept>
12#include <fstream>
13using json = nlohmann::json;
14
15namespace CP{
16 SSVWeightsAlg::SSVWeightsAlg(const std::string &name, ISvcLocator *pSvcLocator)
17 : EL::AnaAlgorithm(name, pSvcLocator){
18 }
19
21 ANA_MSG_INFO("Initialising SSVWeightsAlg");
22 ANA_MSG_WARNING("The Run3 SSV calibration has not been performed yet -> the scale factors are not usable yet");
23
34
35 if (m_OutputVariableSize == "standard") {
37 }
38 else if (m_OutputVariableSize == "extended") {
40 }
41 else if (m_OutputVariableSize == "additional") {
43 }
44 else if (m_OutputVariableSize == "all") {
46 }
47 else {
48 ATH_MSG_ERROR("Unknown OutputVariableSizeType: " << m_OutputVariableSize <<" , accepted options are: 'standard', 'extended', 'additional', 'all'" );
49 return StatusCode::FAILURE;
50 }
51
56 }
57
65 }
66
73 }
74
75 ANA_CHECK(m_systematicsList.initialize());
76
77 //retrieve the JSON file
78 std::string json_file_SSVWeightsAlg = PathResolverFindCalibFile(m_jsonConfigPath_SSVWeightsAlg);
79 std::ifstream jsonFile_SSVWeightsAlg(json_file_SSVWeightsAlg);
80 if (!jsonFile_SSVWeightsAlg.is_open()) {
81 ATH_MSG_ERROR("Could not open JSON file: " << m_jsonConfigPath_SSVWeightsAlg);
82 return StatusCode::FAILURE;
83 }
84
85 m_jsonConfig_SSVWeightsAlg = json::parse(jsonFile_SSVWeightsAlg);
86 jsonFile_SSVWeightsAlg.close();
87
88 // Check that b-tagging working point is the same as in the calibration
89 if (m_BTaggingWP.value() != m_jsonConfig_SSVWeightsAlg["CalibrationInformation"]["btaggingWP"].get<std::string>()){
90 ANA_MSG_ERROR("You are using b-tagging working point: "<< m_BTaggingWP.value() <<" , which is different to the one used in the SSV Calibration: " << m_jsonConfig_SSVWeightsAlg["CalibrationInformation"]["btaggingWP"].get<std::string>());
91 return StatusCode::FAILURE;
92 }
93
94 m_jetBTagAccessor.emplace(m_BTaggingWP.value());
95
96 // retrieve scale factors
97 m_SF_eff = m_jsonConfig_SSVWeightsAlg["CalibrationScaleFactors"]["SF_eff"];
98 m_SF_fake_low = m_jsonConfig_SSVWeightsAlg["CalibrationScaleFactors"]["SF_fake"]["mu_low"];
99 m_SF_fake_high = m_jsonConfig_SSVWeightsAlg["CalibrationScaleFactors"]["SF_fake"]["mu_high"];
100
101 // Initialize EfficiencyMethodClass
102 if (m_EfficiencyMethod == "bjet_based") {
104 m_EfficiencyMethodBJetBasedPtr = std::make_unique<EfficiencyMethodBJetBasedClass>( m_jsonConfig_SSVWeightsAlg );
105 }
106 else if (m_EfficiencyMethod == "Bhadron_pT_eta_based") {
108 m_EfficiencyMethodBhadronPtEtaBasedPtr = std::make_unique<EfficiencyMethodBhadronPtEtaBasedClass>( m_jsonConfig_SSVWeightsAlg );
109 }
110 else {
111 ATH_MSG_ERROR("Unknown efficiency method: " << m_EfficiencyMethod << " , accepted efficiency methods are: 'bjet_based','Bhadron_pT_eta_based'");
112 return StatusCode::FAILURE;
113 }
114
115 //Initialize nFMethodClass
116 if (m_nFMethod == "pileup_bjet_based") {
118 m_nFPileupBJetBasedPtr = std::make_unique<nFMethodPileupBJetBasedClass>( m_jsonConfig_SSVWeightsAlg );
119 }
120 else if (m_nFMethod == "pileup_based_linearfit") {
122 m_nFPileupBasedLinearFitPtr = std::make_unique<nFMethodPileupBasedLinearFitClass>( m_jsonConfig_SSVWeightsAlg );
123 }
124 else if (m_nFMethod == "pileup_based_binned") {
126 m_nFPileupBasedBinnedPtr = std::make_unique<nFMethodPileupBasedBinnedClass>( m_jsonConfig_SSVWeightsAlg );
127 }
128 else {
129 ATH_MSG_ERROR("Unknown nF method: " << m_nFMethod << " , accepted nF methods are: 'pileup_bjet_based', 'pileup_based_linearfit', 'pileup_based_binned'");
130 return StatusCode::FAILURE;
131 }
132 // If OutputVariableSize = all -> Initialize all EfficiencyMethods and nFMethods
133 // Also check if pointers have already been created (see code just above)
134 // as depending on the user method settings some of them might have been set already
137 m_EfficiencyMethodBJetBasedPtr = std::make_unique<EfficiencyMethodBJetBasedClass>( m_jsonConfig_SSVWeightsAlg );
138 }
140 m_EfficiencyMethodBhadronPtEtaBasedPtr = std::make_unique<EfficiencyMethodBhadronPtEtaBasedClass>( m_jsonConfig_SSVWeightsAlg );
141 }
143 m_nFPileupBJetBasedPtr = std::make_unique<nFMethodPileupBJetBasedClass>( m_jsonConfig_SSVWeightsAlg );
144 }
146 m_nFPileupBasedLinearFitPtr = std::make_unique<nFMethodPileupBasedLinearFitClass>( m_jsonConfig_SSVWeightsAlg );
147 }
149 m_nFPileupBasedBinnedPtr = std::make_unique<nFMethodPileupBasedBinnedClass>( m_jsonConfig_SSVWeightsAlg );
150 }
151 }
152
153
154 return StatusCode::SUCCESS;
155 }
156
157 StatusCode SSVWeightsAlg::execute(const EventContext& ctx) {
158
159 const std::vector<CP::SystematicSet> &systematics = m_systematicsList.systematicsVector();
160 if (systematics.empty()){
161 return StatusCode::SUCCESS;
162 }
163
164 //create truth b-hadrons (truthBhs); the truth particles do not depend on the systematic, so this is done once per event
165 std::vector<const xAOD::TruthParticle*> truthBhs;
166
167 const xAOD::TruthParticleContainer *particles = nullptr;
168 ANA_CHECK(m_truthParticlesHandle.retrieve(particles, systematics.front(), ctx));
169
170 for (const xAOD::TruthParticle *part : *particles){
171 if ( part->isBottomHadron() && isHFHadronFinalState(part, 5) ){
172 truthBhs.push_back(part);
173 }
174 }
175
176 for (const auto &sys : systematics){
177 const xAOD::EventInfo *evtInfo = nullptr;
178 ANA_CHECK(m_eventInfoHandle.retrieve(evtInfo, sys, ctx));
179
180 const xAOD::VertexContainer* SSVs = nullptr;
181 ANA_CHECK(m_ssvHandle.retrieve(SSVs, sys, ctx));
182
183 //create jets
184 const xAOD::JetContainer *jets = nullptr;
185 ANA_CHECK(m_jetsHandle.retrieve(jets, sys, ctx));
186
187 std::vector<const xAOD::Jet*> jets_Selected;
188 int b_jet_count=0;
189
190 //create jets that pass your jet selection
191 for(const xAOD::Jet* jet : *jets){
192 if (m_jetSelection.getBool (*jet, sys)){
193 jets_Selected.push_back(jet);
194
195 // Count number of bjets
196 if ((*m_jetBTagAccessor)(*jet)){
197 b_jet_count = b_jet_count+1;
198 }
199 }
200 }
201
202 // create electrons
203 const xAOD::ElectronContainer *electrons = nullptr;
204 ANA_CHECK(m_electronsHandle.retrieve(electrons, sys, ctx));
205
206 std::vector<const xAOD::Electron*> electrons_Selected;
207
208 //create electrons that pass your electron selection
209 for(const xAOD::Electron* electron : *electrons){
210 if (m_electronSelection.getBool (*electron, sys)){
211 electrons_Selected.push_back (electron);
212 }
213 }
214
215 //create muons
216 const xAOD::MuonContainer *muons = nullptr;
217 ANA_CHECK(m_muonsHandle.retrieve(muons, sys, ctx));
218 std::vector<const xAOD::Muon*> muons_Selected;
219
220 //create muons that pass your muon selection
221 for(const xAOD::Muon* muon : *muons){
222 if (m_muonSelection.getBool (*muon, sys)){
223 muons_Selected.push_back( muon );
224 }
225 }
226
227 // create good SSVs
228 std::vector<const xAOD::Vertex*> good_SSVs = create_good_SSVs(jets_Selected, electrons_Selected, muons_Selected, *SSVs);
229
230 //create truthBhs in acceptance
231 std::vector<const xAOD::TruthParticle*> accepted_truthBhs = create_accepted_truthBhs(truthBhs, jets_Selected);
232
233 //do the DeltaR matching between truthBh and SSV
234 std::vector<bool> truthBh_to_SSV_matched = truthBh_to_SSV_matching(accepted_truthBhs, good_SSVs);
235
236 //count matched truthBh,missed truthBh (not matched truthBh) and number of fake SSV (not matched SSV)
237 int N_matched = std::count(truthBh_to_SSV_matched.begin(), truthBh_to_SSV_matched.end(), true);
238 int N_missed = truthBh_to_SSV_matched.size() - N_matched;
239 int N_fake = count_number_of_fake_SSVs(accepted_truthBhs, good_SSVs);
240
241 // retrieve pileup
242 double muactual = evtInfo->actualInteractionsPerCrossing();
243
244 // calculate P_eff
245 double P_eff = std::pow(m_SF_eff, N_matched);
246
247 //calculate P_ineff according to the chosen method
248 double P_ineff = 1;
250 P_ineff = m_EfficiencyMethodBJetBasedPtr->getPIneff(b_jet_count, N_missed,m_SF_eff);
251 }
253 P_ineff = m_EfficiencyMethodBhadronPtEtaBasedPtr->getPIneff(accepted_truthBhs, truthBh_to_SSV_matched, m_SF_eff);
254 }
255
256 // calculate P_fake according to the chosen method
257 double P_fake = 1;
259 P_fake = m_nFPileupBJetBasedPtr->getPFake(muactual, b_jet_count, N_fake,m_SF_fake_low, m_SF_fake_high);
260 }
262 P_fake = m_nFPileupBasedLinearFitPtr->getPFake(muactual,N_fake);
263 }
265 P_fake = m_nFPileupBasedBinnedPtr->getPFake(muactual, N_fake, m_SF_fake_low, m_SF_fake_high);
266 }
267
268 //calculate SSV_weight
269 double SSV_weight = P_eff * P_ineff * P_fake;
270
271 // decorate SSV weight
272 m_SSV_weight_decor.set(*evtInfo, SSV_weight, sys);
273
275 // decorate P factors
276 m_P_eff_decor.set(*evtInfo, P_eff, sys);
277 m_P_ineff_decor.set(*evtInfo, P_ineff, sys);
278 m_P_fake_decor.set(*evtInfo, P_fake, sys);
279 }
281 //decorate additional information
282 m_N_matched_decor.set(*evtInfo, N_matched, sys);
283 m_N_missed_decor.set(*evtInfo, N_missed, sys);
284 m_N_fake_decor.set(*evtInfo, N_fake, sys);
285 m_number_of_bjets_decor.set(*evtInfo, b_jet_count, sys);
286 m_number_of_accepted_Bhadrons_decor.set(*evtInfo, accepted_truthBhs.size(), sys);
287 m_number_of_good_SSVs_decor.set(*evtInfo, good_SSVs.size(), sys);
288 }
289 //decorate all possible P factors
291 double P_ineff_bjet_based = m_EfficiencyMethodBJetBasedPtr->getPIneff(b_jet_count, N_missed,m_SF_eff);
292 double P_ineff_pt_eta_based = m_EfficiencyMethodBhadronPtEtaBasedPtr->getPIneff(accepted_truthBhs, truthBh_to_SSV_matched, m_SF_eff);
293 double P_fake_pileup_bjet_based = m_nFPileupBJetBasedPtr->getPFake(muactual, b_jet_count, N_fake, m_SF_fake_low, m_SF_fake_high);
294 double P_fake_pileup_based_linearfit = m_nFPileupBasedLinearFitPtr->getPFake(muactual,N_fake);
295 double P_fake_pileup_based_binned = m_nFPileupBasedBinnedPtr->getPFake(muactual, N_fake, m_SF_fake_low, m_SF_fake_high);
296
297 m_P_ineff_bjet_based_decor.set(*evtInfo, P_ineff_bjet_based, sys);
298 m_P_ineff_pt_eta_based_decor.set(*evtInfo, P_ineff_pt_eta_based, sys);
299 m_P_fake_pileup_bjet_based_decor.set(*evtInfo, P_fake_pileup_bjet_based, sys);
300 m_P_fake_pileup_based_linearfit_decor.set(*evtInfo, P_fake_pileup_based_linearfit, sys);
301 m_P_fake_pileup_based_binned_decor.set(*evtInfo, P_fake_pileup_based_binned, sys);
302 }
303 }
304 return StatusCode::SUCCESS;
305 }
306
307 // create vector that indicates which SSV is a so-called good SSV
308 std::vector<const xAOD::Vertex*> SSVWeightsAlg::create_good_SSVs(
309 const std::vector<const xAOD::Jet*> &jets,
310 const std::vector<const xAOD::Electron*> &electrons,
311 const std::vector<const xAOD::Muon*> &muons,
312 const xAOD::VertexContainer &SSVs) const {
313
314 static const SG::AuxElement::ConstAccessor<float> ssv_pt_accessor(("bvrtPt"));
315 static const SG::AuxElement::ConstAccessor<float> ssv_m_accessor("bvrtM");
316 static const SG::AuxElement::ConstAccessor<float> ssv_eta_accessor("bvrtEta");
317
318 std::vector<const xAOD::Vertex*> good_SSVs;
319
320 for (const xAOD::Vertex* SSV : SSVs) {
321 bool overlaps = false;
322
323 //check if SSV fails good SSV definition
324 if ( (ssv_pt_accessor(*SSV) < 3000) || (ssv_m_accessor(*SSV) < 600) || (std::abs(ssv_eta_accessor(*SSV)) > 2.5) ){
325 continue;
326 }
327
328 //check if SSV overlaps with jet
329 for (const xAOD::Jet* jet : jets) {
330 double DeltaR_jet = compute_DeltaR_between_SSV_and_particle( SSV , jet );
331 if (DeltaR_jet < 0.6){
332 overlaps = true;
333 break;
334 }
335 }
336
337 if (overlaps == true){
338 continue;
339 }
340 //check if SSV overlaps with electron
341 for (const xAOD::Electron* electron : electrons) {
342 double DeltaR_el = compute_DeltaR_between_SSV_and_particle( SSV , electron );
343 if (DeltaR_el < 0.2){
344 overlaps = true;
345 break;
346 }
347 }
348
349 if (overlaps == true){
350 continue;
351 }
352
353 //check if SSV overlaps with muon
354 for (const xAOD::Muon* muon : muons) {
355 double DeltaR_mu = compute_DeltaR_between_SSV_and_particle( SSV , muon );
356 if (DeltaR_mu < 0.2){
357 overlaps = true;
358 break;
359 }
360 }
361
362 if (overlaps == true){
363 continue;
364 }
365 good_SSVs.push_back(SSV);
366 }
367 return good_SSVs;
368 };
369
370 // You construct a vector to see if the truthBh is an acceptance. An entry in this vector is true if the truth Bh is in acceptance and false if not
371 std::vector<const xAOD::TruthParticle*> SSVWeightsAlg::create_accepted_truthBhs(
372 const std::vector<const xAOD::TruthParticle*> &truthBhs,
373 const std::vector<const xAOD::Jet*> &jets) const {
374
375 std::vector<const xAOD::TruthParticle*> accepted_truthBhs;
376
377 for (const xAOD::TruthParticle* truthBh : truthBhs) {
378 // Check if truthBh fails truthBh in acceptance definition
379 if (truthBh->pt() < 2000 || (std::abs(truthBh->eta()) > 2.8)){
380 continue;
381 }
382
383 // check if truthBh overlaps with jet
384 bool overlaps = false;
385 for (const xAOD::Jet* jet : jets) {
386 double DeltaR = truthBh->p4().DeltaR(jet->p4());
387 if (DeltaR<0.6){
388 overlaps = true;
389 break;
390 }
391 }
392
393 if (overlaps == true){
394 continue;
395 }
396
397 accepted_truthBhs.push_back(truthBh);
398 }
399 return accepted_truthBhs;
400 };
401
403 const std::vector<const xAOD::TruthParticle*> &truthBhs,
404 const std::vector<const xAOD::Vertex*> &SSVs) const {
405
406 int N_fake_SSV = 0;
407 for (const xAOD::Vertex* SSV : SSVs){
408 bool foundMatch = false;
409 for (const xAOD::TruthParticle* truthBh : truthBhs){
410 double DeltaR = compute_DeltaR_between_SSV_and_particle(SSV, truthBh);
411 if (DeltaR < 0.3){
412 foundMatch = true;
413 break;
414 }
415 }
416 if (!foundMatch){
417 // In this case no match was found between the current SSV and any truth particle
418 // Hence it is a fake SSV
419 // Increase the number of fake SSV counter
420 N_fake_SSV = N_fake_SSV + 1;
421 }
422 }
423 return N_fake_SSV;
424 }
425
426
427
428
429 // create a vector that indicates if a truthBh got matched to a SSV
431 const std::vector<const xAOD::TruthParticle*> &truthBhs,
432 const std::vector<const xAOD::Vertex*> &SSVs) const {
433
434 std::vector<bool> matched_vector(truthBhs.size(), false);
435 for (size_t i = 0; i < truthBhs.size(); ++i){
436 const xAOD::TruthParticle* truthBh = truthBhs[i];
437 for (size_t j = 0; j < SSVs.size(); ++j){
438 const xAOD::Vertex* SSV = SSVs[j];
439 double DeltaR = compute_DeltaR_between_SSV_and_particle(SSV, truthBh);
440 if (DeltaR < 0.3){
441 matched_vector[i] = true;
442 break;
443 }
444 }
445 }
446 return matched_vector;
447 };
448
449 // compute the DeltaR between a SSV and another particle (jet,electron,truthparticle etc.)
451 const xAOD::Vertex* vtx,
452 const xAOD::IParticle * part) const {
453
454 static const SG::AuxElement::ConstAccessor<float> ssv_eta_accessor("bvrtEta");
455 static const SG::AuxElement::ConstAccessor<float> ssv_phi_accessor("bvrtPhi");
456 // Compute delta eta between vertex and particle
457 double eta_diff = ssv_eta_accessor(*vtx) - part->eta() ;
458
459 // Compute delta phi between vertex and particle
460 // See TLorentzVector::DeltaR function
461 double phi_diff = TVector2::Phi_mpi_pi(ssv_phi_accessor(*vtx) - part->phi() );
462
463 // Compute deltaR between vertex and particle
464 return std::sqrt( eta_diff*eta_diff + phi_diff*phi_diff );
465 }
466
467
468 std::vector<const xAOD::TruthParticle*> SSVWeightsAlg::construct_not_matched_vectors(
469 const std::vector<const xAOD::TruthParticle*> &truthBhs,
470 const std::vector<bool> &matched_vector) {
471
472 std::vector<const xAOD::TruthParticle*> missed_vector;
473 for (size_t i = 0; i < truthBhs.size(); ++i){
474 if (matched_vector[i] == false){
475 missed_vector.push_back(truthBhs[i]);
476 }
477 }
478 return missed_vector;
479 };
480
481 // Indicate if hadron is heavy flavour hadron and in the final state in the truthparticle tree
483 const xAOD::TruthParticle *part,
484 const int type) const {
485
486 for (unsigned int i = 0; i < part->nChildren(); ++i){
487 const xAOD::TruthParticle *child = part->child(i);
488 if (!child){
489 continue;
490 }
491 if (type == 5){
492 if (child->isBottomHadron()){
493 return false;
494 }
495 if (child->isGenStable()){
496 if (!isHFHadronFinalState(child, type)){
497 return false;
498 }
499 }
500 }
501
502 if (type == 4){
503 if (child->isCharmHadron()){
504 return false;
505 }
506 if (child->isGenStable()){
507 if (!isHFHadronFinalState(child, type)){
508 return false;
509 }
510 }
511 }
512 }
513 return true;
514 }
515
517 const int k,
518 const double lambda){
519 if (lambda == 0.0 ) return k == 0 ? 1.0 : 0.0;
520 if (lambda < 0 || k < 0) return 0.0;
521 return std::exp(-lambda + k * std::log(lambda) - std::lgamma(k + 1));
522 }
523
525 : m_ptbins(jsonConfig["efficiency_Bhadron_pT_eta_based"]["pt_bins"].get<std::vector<double>>())
526 {
527 // Extract information from JSON file for EfficiencyMethod EfficiencyMethodBhadronPtEtaBased
528 for (size_t i = 0; i < m_ptbins.size() - 1; ++i) {
529 std::string pT_bin_key = "pt_bin_" + std::to_string((int)m_ptbins[i]) + "_" + std::to_string((int)m_ptbins[i+1]);
530 m_BhadronPtEtaEfficiencyMap[pT_bin_key] = jsonConfig["efficiency_Bhadron_pT_eta_based"][pT_bin_key];
531 }
532 m_upperboundpT = m_ptbins[m_ptbins.size()-1];
533 // load the pT overflow bin if it is provided by the calibration
534 m_overflowPtBinKey = "pt_bin_" + std::to_string((int)m_upperboundpT) + "plus";
535 if (jsonConfig["efficiency_Bhadron_pT_eta_based"].contains(m_overflowPtBinKey)) {
536 m_BhadronPtEtaEfficiencyMap[m_overflowPtBinKey] = jsonConfig["efficiency_Bhadron_pT_eta_based"][m_overflowPtBinKey];
537 }
538 }
539
540 //calculate P_ineff based on the Bhadron pT and eta
542 const std::vector<const xAOD::TruthParticle*> &accepted_truthBhs,
543 const std::vector<bool> &truthBh_to_SSV_matched,
544 double SF_eff) const{
545 //construct missed truthBhs
546 const std::vector<const xAOD::TruthParticle*> missed_truthBhs = construct_not_matched_vectors(accepted_truthBhs, truthBh_to_SSV_matched);
547
548 //read off pt bins from JSON file
549 const std::vector<double> &ptbins = m_ptbins;
550
551 double P_ineff = 1;
552 const std::string etaStr{"eta"};
553 const std::string effStr{"efficiency"};
554 for (size_t i = 0; i < missed_truthBhs.size(); ++i) {
555 //retrieve pt,eta of missed truthBh
556 double pt = missed_truthBhs[i]->pt();
557 double eta = std::abs(missed_truthBhs[i]->eta());
558 std::string pt_bin_of_truthBh = "";
559 if (pt >= m_upperboundpT){
561 throw std::runtime_error("EfficiencyMethodBhadronPtEtaBasedClass::getPIneff: B-hadron pT above the last pT bin edge, but no '" + m_overflowPtBinKey + "' entry in the calibration JSON file");
562 }
563 pt_bin_of_truthBh = m_overflowPtBinKey;
564 }
565 else {
566 // iterate pt bins to find appropriate efficiency bin for the truthBh pT
567 for (size_t j = 0; j < ptbins.size() - 1; ++j) {
568 if (pt >= ptbins[j] && pt < ptbins[j+1]) {
569 //construct pt bin name
570 pt_bin_of_truthBh = "pt_bin_" + std::to_string((int)ptbins[j]) + "_" + std::to_string((int)ptbins[j+1]);
571 }
572 }
573 }
574 if (pt_bin_of_truthBh == ""){
575 //no pt bin found or no missed truthBh"
576 continue;
577 }
578 //retrieve eta and efficiency bins for the pT bin
579 const std::vector<double>& eta_bins = m_BhadronPtEtaEfficiencyMap.at(pt_bin_of_truthBh).at(etaStr);
580 const std::vector<double>& efficiencies = m_BhadronPtEtaEfficiencyMap.at(pt_bin_of_truthBh).at(effStr);
581
582 std::optional<double> efficiency;
583
584 //iterate eta bins to find appropriate eta bin for truthBh eta
585 for (size_t k = 0; k < eta_bins.size() - 1; ++k) {
586 double eta_low = eta_bins[k];
587 double eta_up = eta_bins[k+1];
588 if (eta >= eta_low && eta < eta_up) {
589 //eta bin found -> read off corresponding efficiency
590 efficiency = efficiencies[k];
591 }
592 }
593 if (!efficiency){
594 //no eta bin found -> skip the truthBh, as done for truthBhs without pt bin
595 continue;
596 }
597 //calculate P_ineff using the found efficiency
598 P_ineff = P_ineff*(1-SF_eff*(*efficiency))/(1-(*efficiency));
599 }
600 return P_ineff;
601 }
602
604 : m_bjetEfficiencyMap(jsonConfig["efficiency_bjet_based"])
605 {
606 // Extract information from JSON file for EfficiencyMethodBJetBased
607 std::map<std::string, double>::iterator lastItem = std::prev(m_bjetEfficiencyMap.end());
608 std::string lastItemKey = lastItem->first;
609 m_upperboundNbjets = std::stoi(lastItemKey);
610 }
611
612
613 //calculate P_ineff based on the bjet multiplicity
615 const int b_jet_count,
616 const int N_missed,
617 const double SF_eff) const{
618
619 double P_ineff = 1;
620
621 // Get the value
622 double epsilon = 1;
623
624 //retrieve efficiency and average number of fake SSV depending on number of jets in event
625 if (b_jet_count < m_upperboundNbjets){
626 // Build the bjets key string
627 std::string bjets_key = std::to_string(b_jet_count) + "_bjets";
628 epsilon = m_bjetEfficiencyMap.at(bjets_key);
629 }
630 else{
631 std::string bjets_key = std::to_string(m_upperboundNbjets) + "p_bjets";
632 epsilon = m_bjetEfficiencyMap.at(bjets_key);
633 }
634
635 P_ineff = std::pow((1-SF_eff*epsilon)/(1-epsilon), N_missed);
636
637 return P_ineff;
638 }
639
641 : m_nFPileupBJetMap(jsonConfig["nF_pileup_bjet_based"])
642 {
643 // Extract information from JSON file for nFMethodPileupBJetBased
644 std::map<std::string, double>::iterator lastItem = std::prev(m_nFPileupBJetMap.at("high_muactual").end());
645 std::string lastItemKey = lastItem->first;
646 m_upperboundNbjets = std::stoi(lastItemKey);
647 m_lowMuHighMuThreshold = jsonConfig["CalibrationInformation"]["lowMuHighMuThreshold"];
648 }
649
650 //calculate P_fake based on the bjet multiplicity in the high pileup and low pileup region
652 const double muactual,
653 const int b_jet_count,
654 const int N_fake,
655 const double SF_fake_low,
656 const double SF_fake_high) const{
657
658 double P_fake = 1;
659 // 2D map muactual and Nbjets
660 std::string mu_key = (muactual >= m_lowMuHighMuThreshold) ? "high_muactual" : "low_muactual";
661
662 // Get the value
663 double n_F_value = 0;
664
665 if (b_jet_count < m_upperboundNbjets){
666 // Build the bjets key string
667 std::string bjets_key = std::to_string(b_jet_count) + "_bjets";
668 n_F_value = m_nFPileupBJetMap.at(mu_key).at(bjets_key);
669 }
670 else{
671 // Build the bjets key string
672 std::string bjets_key = std::to_string(m_upperboundNbjets) + "p_bjets";
673 n_F_value = m_nFPileupBJetMap.at(mu_key).at(bjets_key);
674 }
675 auto denom = poisson_pmf(N_fake, n_F_value);
676 if (denom == 0. )[[unlikely]]{
677 throw std::runtime_error("nFMethodPileupBJetBasedClass::getPFake: divide-by-zero");
678 }
679 if (muactual >= m_lowMuHighMuThreshold){
680 P_fake = (poisson_pmf(N_fake, SF_fake_high*n_F_value))/denom;
681 }
682 else {
683 P_fake = (poisson_pmf(N_fake, SF_fake_low*n_F_value))/denom;
684 }
685
686 return P_fake;
687 }
688
690 // Extract information from JSON file for nFMethodPileupBasedLinearFit
691 m_slopeUnscaled = jsonConfig["nF_pileup_based_linearfit"]["unscaled"]["slope"];
692 m_interceptUnscaled = jsonConfig["nF_pileup_based_linearfit"]["unscaled"]["intercept"];
693 m_slopeScaled = jsonConfig["nF_pileup_based_linearfit"]["scaled"]["slope"];
694 m_interceptScaled = jsonConfig["nF_pileup_based_linearfit"]["scaled"]["intercept"];
695 }
696
697 //calculate P_fake based on a linear fit of the average number of fake SSVs (nF) to the pileup (muactual)
699 const double muactual,
700 const int N_fake) const{
701 // Calculate expected counts
702 double n_F = m_slopeUnscaled * muactual + m_interceptUnscaled;
703 double n_F_scaled = m_slopeScaled * muactual + m_interceptScaled;
704 auto denom = poisson_pmf(N_fake, n_F);
705 if (denom == 0.)[[unlikely]]{
706 throw std::runtime_error("nFMethodPileupBasedLinearFitClass::getPFake: divide-by-zero.");
707 }
708 // Calculate P_fake
709 double P_fake = poisson_pmf(N_fake, n_F_scaled) / poisson_pmf(N_fake, n_F);
710
711 return P_fake;
712 }
713
715 : m_muactualBins(jsonConfig["nF_pileup_based_binned"]["muactual_bins"].get<std::vector<double>>()),
716 m_nFBins(jsonConfig["nF_pileup_based_binned"]["values"].get<std::vector<double>>())
717 {
718 // Extract information from JSON file for nFMethodPileupBasedBinned
719 m_lowMuHighMuThreshold = jsonConfig["CalibrationInformation"]["lowMuHighMuThreshold"];
720 }
721
722
723 //calculate P_fake with the pileup binned
725 const double muactual,
726 const int N_fake,
727 const double SF_fake_low,
728 const double SF_fake_high) const{
729
730 double nF = 0;
731 double P_fake = 1;
732
733 // Find the correct bin for muactual
734 for (size_t j = 0; j < m_muactualBins.size() - 1; ++j) {
735 if (muactual >= m_muactualBins[j] && muactual < m_muactualBins[j + 1]) {
736 nF = m_nFBins[j];
737 const auto denom = poisson_pmf(N_fake, nF);
738 if (denom == 0.)[[unlikely]]{
739 continue;
740 }
741 if (muactual < m_lowMuHighMuThreshold){
742 P_fake = poisson_pmf(N_fake, SF_fake_low * nF) / denom;
743 } else {
744 P_fake = poisson_pmf(N_fake, SF_fake_high * nF) / denom;
745 }
746 break; // Bin found, no need to continue loop
747 }
748 }
749 return P_fake;
750 }
751}
Scalar eta() const
pseudorapidity method
#define ATH_MSG_ERROR(x,...)
#define ANA_MSG_ERROR(xmsg,...)
Macro printing error messages.
#define ANA_MSG_INFO(xmsg,...)
Macro printing info messages.
#define ANA_CHECK(EXP)
check whether the given expression was successful
#define ANA_MSG_WARNING(xmsg,...)
Macro printing warning messages.
nlohmann::json json
static const std::vector< std::string > systematics
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
TEfficiency * efficiency(std::string_view effName, std::string_view tDir="", std::string_view stream="")
Simplify the retrieval of registered TEfficiency.
EfficiencyMethodBJetBasedClass(const nlohmann::json &jsonConfig)
double getPIneff(const int b_jet_count, const int N_missed, const double SF_eff) const
std::map< std::string, double > m_bjetEfficiencyMap
EfficiencyMethodBhadronPtEtaBasedClass(const nlohmann::json &jsonConfig)
std::map< std::string, std::map< std::string, std::vector< double > > > m_BhadronPtEtaEfficiencyMap
double getPIneff(const std::vector< const xAOD::TruthParticle * > &accepted_truthBh, const std::vector< bool > &truthBh_to_SSV_matched, double SF_eff) const
double getPFake(const double muactual, const int b_jet_count, const int N_fake, const double SF_fake_low, const double SF_fake_high) const
nFMethodPileupBJetBasedClass(const nlohmann::json &jsonConfig)
std::map< std::string, std::map< std::string, double > > m_nFPileupBJetMap
double getPFake(const double muactual, const int N_fake, const double SF_fake_low, const double SF_fake_high) const
nFMethodPileupBasedBinnedClass(const nlohmann::json &jsonConfig)
double getPFake(const double muactual, const int N_fake) const
nFMethodPileupBasedLinearFitClass(const nlohmann::json &jsonConfig)
std::optional< SG::AuxElement::ConstAccessor< char > > m_jetBTagAccessor
CP::SysReadSelectionHandle m_muonSelection
std::vector< bool > truthBh_to_SSV_matching(const std::vector< const xAOD::TruthParticle * > &truthBhs, const std::vector< const xAOD::Vertex * > &SSVs) const
CP::SysWriteDecorHandle< int > m_N_fake_decor
CP::SysReadHandle< xAOD::JetContainer > m_jetsHandle
OutputVariableSizeType m_OutputVariableSizeType
CP::SysReadHandle< xAOD::VertexContainer > m_ssvHandle
std::unique_ptr< nFMethodPileupBasedLinearFitClass > m_nFPileupBasedLinearFitPtr
CP::SysWriteDecorHandle< float > m_SSV_weight_decor
CP::SysWriteDecorHandle< int > m_number_of_accepted_Bhadrons_decor
std::unique_ptr< EfficiencyMethodBJetBasedClass > m_EfficiencyMethodBJetBasedPtr
CP::SysReadHandle< xAOD::MuonContainer > m_muonsHandle
virtual StatusCode initialize() override
CP::SysWriteDecorHandle< int > m_N_matched_decor
double compute_DeltaR_between_SSV_and_particle(const xAOD::Vertex *vtx, const xAOD::IParticle *part) const
CP::SysWriteDecorHandle< float > m_P_fake_pileup_based_linearfit_decor
std::unique_ptr< EfficiencyMethodBhadronPtEtaBasedClass > m_EfficiencyMethodBhadronPtEtaBasedPtr
nlohmann::json m_jsonConfig_SSVWeightsAlg
CP::SysWriteDecorHandle< int > m_number_of_good_SSVs_decor
bool isHFHadronFinalState(const xAOD::TruthParticle *part, const int type) const
std::vector< const xAOD::TruthParticle * > create_accepted_truthBhs(const std::vector< const xAOD::TruthParticle * > &truthBhs, const std::vector< const xAOD::Jet * > &jets) const
CP::SysReadSelectionHandle m_electronSelection
static double poisson_pmf(const int k, const double lambda)
Gaudi::Property< std::string > m_jsonConfigPath_SSVWeightsAlg
nFMethodType m_nFMethodType
CP::SysWriteDecorHandle< float > m_P_fake_pileup_bjet_based_decor
Gaudi::Property< std::string > m_nFMethod
Gaudi::Property< std::string > m_EfficiencyMethod
CP::SysReadHandle< xAOD::TruthParticleContainer > m_truthParticlesHandle
std::unique_ptr< nFMethodPileupBJetBasedClass > m_nFPileupBJetBasedPtr
CP::SysWriteDecorHandle< float > m_P_ineff_bjet_based_decor
CP::SysWriteDecorHandle< float > m_P_ineff_decor
SSVWeightsAlg(const std::string &name, ISvcLocator *pSvcLocator)
CP::SysReadSelectionHandle m_jetSelection
int count_number_of_fake_SSVs(const std::vector< const xAOD::TruthParticle * > &truthBhs, const std::vector< const xAOD::Vertex * > &SSVs) const
CP::SysReadHandle< xAOD::EventInfo > m_eventInfoHandle
CP::SysListHandle m_systematicsList
Gaudi::Property< std::string > m_OutputVariableSize
std::vector< const xAOD::Vertex * > create_good_SSVs(const std::vector< const xAOD::Jet * > &jets, const std::vector< const xAOD::Electron * > &electrons, const std::vector< const xAOD::Muon * > &muons, const xAOD::VertexContainer &SSVs) const
CP::SysWriteDecorHandle< int > m_number_of_bjets_decor
CP::SysWriteDecorHandle< int > m_N_missed_decor
Gaudi::Property< std::string > m_BTaggingWP
CP::SysWriteDecorHandle< float > m_P_ineff_pt_eta_based_decor
CP::SysWriteDecorHandle< float > m_P_fake_pileup_based_binned_decor
static std::vector< const xAOD::TruthParticle * > construct_not_matched_vectors(const std::vector< const xAOD::TruthParticle * > &truthBhs, const std::vector< bool > &matched_vector)
CP::SysWriteDecorHandle< float > m_P_fake_decor
CP::SysReadHandle< xAOD::ElectronContainer > m_electronsHandle
CP::SysWriteDecorHandle< float > m_P_eff_decor
EfficiencyMethodType m_EfficiencyMethodType
std::unique_ptr< nFMethodPileupBasedBinnedClass > m_nFPileupBasedBinnedPtr
AnaAlgorithm(const std::string &name, ISvcLocator *pSvcLocator)
constructor with parameters
virtual::StatusCode execute()
execute this algorithm
float actualInteractionsPerCrossing() const
Average interactions per crossing for the current BCID - for in-time pile-up.
Class providing the definition of the 4-vector interface.
bool isBottomHadron() const
Determine if the PID is that of a b-hadron.
bool isGenStable() const
Check if this is generator stable particle.
bool isCharmHadron() const
Determine if the PID is that of a c-hadron.
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:132
bool contains(const std::string &s, const std::string &regx)
does a string contain the substring
Definition hcg.cxx:116
Select isolated Photons, Electrons and Muons.
This module defines the arguments passed from the BATCH driver to the BATCH worker.
STL namespace.
Jet_v1 Jet
Definition of the current "jet version".
setRcore setEtHad setFside pt
ElectronContainer_v1 ElectronContainer
Definition of the current "electron container version".
EventInfo_v1 EventInfo
Definition of the latest event info version.
VertexContainer_v1 VertexContainer
Definition of the current "Vertex container version".
Vertex_v1 Vertex
Define the latest version of the vertex class.
TruthParticle_v1 TruthParticle
Typedef to implementation.
Muon_v1 Muon
Reference the current persistent version:
JetContainer_v1 JetContainer
Definition of the current "jet container version".
MuonContainer_v1 MuonContainer
Definition of the current "Muon container version".
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.
Electron_v1 Electron
Definition of the current "egamma version".
#define unlikely(x)