ATLAS Offline Software
Loading...
Searching...
No Matches
SSVWeightsAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
7
8#include <fstream>
9#include <nlohmann/json.hpp>
11#include <boost/math/distributions/poisson.hpp>
12using json = nlohmann::json;
13
14namespace CP{
15 SSVWeightsAlg::SSVWeightsAlg(const std::string &name, ISvcLocator *pSvcLocator)
16 : EL::AnaAlgorithm(name, pSvcLocator){
17 declareProperty("JsonConfigFile_SSVWeightsAlg",m_jsonConfigPath_SSVWeightsAlg, "Path to the JSON config file that contains the SSV calibration results which are needed to calculate the SSV weights");
18 declareProperty("BTagging_WP", m_BTagging_WP, "b-tagging working point that is used to count the number of b-jets in the event for b-jet based SSV weight calculation");
19 declareProperty("OverlapRemoval", m_OverlapRemoval, "string that will be used in the overlap removal accessor for jets, electrons and muons; empty string means no overlap removal will be applied");
20 declareProperty("Jvt", m_Jvt, "string that will be used in the jvt accessor for jets; empty string means no jvt selection will be applied");
21 declareProperty("efficiency_Method", m_efficiency_Method, "efficiency definition that will be used to calculate the SSV weights, string can be 'Bhadron_pT_eta_based' or 'bjet_based'");
22 declareProperty("nF_Method", m_nF_Method, "average number of fake SSV definition that will be used to calculate the SSV weights, string can be 'pileup_bjet_based','pileup_based_linearfit' or 'pileup_based_binned'");
23 declareProperty("OutputVariable_Size", m_OutputVariable_Size, "number of variables that will be saved to the output, string can be 'standard','extended','additional' or 'all'");
24 }
25
27
28 ANA_MSG_INFO("Initialising SSVWeightsAlg");
29 ANA_MSG_INFO("WARNING: The Run3 SSV calibration has not been performed yet -> the scale factors are not usable yet");
30
38
42
46
50
56
57 ANA_CHECK(m_systematicsList.initialize());
58
59 //retrieve the JSON file
60 std::string json_file_SSVWeightsAlg=PathResolver::find_file(m_jsonConfigPath_SSVWeightsAlg, "DATAPATH");
61 std::ifstream jsonFile_SSVWeightsAlg(json_file_SSVWeightsAlg);
62 if (!jsonFile_SSVWeightsAlg.is_open()) {
63 ANA_MSG_ERROR("Could not open JSON file: " << m_jsonConfigPath_SSVWeightsAlg);
64 return StatusCode::FAILURE;
65 }
66
67 m_jsonConfig_SSVWeightsAlg = json::parse(jsonFile_SSVWeightsAlg);
68 jsonFile_SSVWeightsAlg.close();
69
70 return StatusCode::SUCCESS;
71 }
72
74
75 for (const auto &sys : m_systematicsList.systematicsVector()){
76 const xAOD::EventInfo *evtInfo = nullptr;
77 ANA_CHECK(m_eventInfoHandle.retrieve(evtInfo, sys));
78
79 const xAOD::VertexContainer* vertices = nullptr;
80 ANA_CHECK(m_ssvHandle.retrieve(vertices, sys));
81
82 // create SSVs
83 std::vector<const xAOD::Vertex*> SSVs;
84 for(const xAOD::Vertex* ssvvtx : *vertices){
85 SSVs.push_back(ssvvtx);
86 }
87
88 //create jets
89 const xAOD::JetContainer *jets = nullptr;
90 ANA_CHECK(m_jetsHandle.retrieve(jets, sys));
91
92 std::vector<const xAOD::Jet*> jets_PassedORJvt;
93
94 int b_jet_count=0;
95
96 static const SG::AuxElement::ConstAccessor<char> jet_btag_accessor(m_BTagging_WP);
97
98 bool useOR = !m_OverlapRemoval.empty();
99 static const SG::AuxElement::ConstAccessor<char> particle_passesOR_accessor(useOR ? m_OverlapRemoval : "passesOR"); // placeholder string (will never be used if !useOR)
100
101 bool useJvt = !m_Jvt.empty();
102 static const SG::AuxElement::ConstAccessor<char> jet_jvt_selection_accessor(useJvt ? m_Jvt : "jvt_selection"); // placeholder string (will never be used if !useJvt)
103
104 //create jets that pass overlap removal and jvt
105 for(const xAOD::Jet* jet : *jets){
106 char jet_passesOR_char = useOR ? particle_passesOR_accessor(*jet) : 1; // fallback: always true
107 char jet_jvt_selection_char = useJvt ? jet_jvt_selection_accessor(*jet) : 1; // fallback: always true
108
109 if (jet_passesOR_char && jet_jvt_selection_char){
110 jets_PassedORJvt.push_back(jet);
111
112 // Count number of bjets
113 if (jet_btag_accessor(*jet)){
114 b_jet_count = b_jet_count+1;
115 }
116 }
117 }
118
119 // create electrons
120 const xAOD::ElectronContainer *electrons = nullptr;
121 ANA_CHECK(m_electronsHandle.retrieve(electrons, sys));
122
123 std::vector<const xAOD::Electron*> electrons_PassedOR;
124
125 //create electrons that pass overlap removal
126 for(const xAOD::Electron* electron : *electrons){
127 char electron_passesOR_char = useOR ? particle_passesOR_accessor(*electron) : 1; // fallback: always true
128 if (electron_passesOR_char){
129 electrons_PassedOR.push_back (electron);
130 }
131 }
132
133 //create muons
134 const xAOD::MuonContainer *muons = nullptr;
135 ANA_CHECK(m_muonsHandle.retrieve(muons, sys));
136 std::vector<const xAOD::Muon*> muons_PassedOR;
137
138 //create muons that pass overlap removal
139 for(const xAOD::Muon* muon : *muons){
140 char muon_passesOR_char = useOR ? particle_passesOR_accessor(*muon) : 1; // fallback: always true
141 if (muon_passesOR_char){
142 muons_PassedOR.push_back( muon );
143 }
144 }
145
146 // create good SSVs
147 std::vector<const xAOD::Vertex*> good_SSVs = create_good_SSVs(jets_PassedORJvt, electrons_PassedOR, muons_PassedOR, SSVs);
148
149 //create truth b-hadrons (truthBhs)
150 std::vector<const xAOD::TruthParticle*> truthBhs;
151
152 const xAOD::TruthParticleContainer *particles = nullptr;
153 ANA_CHECK(m_truthParticlesHandle.retrieve(particles, sys));
154
155 for (const xAOD::TruthParticle *part : *particles){
156 if ( part->isBottomHadron() && isHFHadronFinalState(part, 5) ){
157 truthBhs.push_back(part);
158 }
159 }
160
161
162 //create truthBhs in acceptance
163 std::vector<const xAOD::TruthParticle*> accepted_truthBhs = create_accepted_truthBhs(truthBhs, jets_PassedORJvt);
164
165 //do the DeltaR matching between truthBh and SSV
166 std::vector<bool> truthBh_to_SSV_matched = truthBh_to_SSV_matching(accepted_truthBhs, good_SSVs);
167
168 //count matched truthBh,missed truthBh (not matched truthBh) and number of fake SSV (not matched SSV)
169 int N_matched = count_matched_objects(truthBh_to_SSV_matched);
170 int N_missed = count_not_matched_objects(truthBh_to_SSV_matched);
171 int N_fake = count_number_of_fake_SSVs(accepted_truthBhs, good_SSVs);
172
173 // retrieve scale factors and pileup
174 double SF_eff = m_jsonConfig_SSVWeightsAlg["CalibrationScaleFactors"]["SF_eff"];
175 double SF_fake_low = m_jsonConfig_SSVWeightsAlg["CalibrationScaleFactors"]["SF_fake"]["mu_low"];
176 double SF_fake_high = m_jsonConfig_SSVWeightsAlg["CalibrationScaleFactors"]["SF_fake"]["mu_high"];
177
178 double muactual = evtInfo->actualInteractionsPerCrossing();
179
180 // calculate P_eff
181 double P_eff = std::pow(SF_eff, N_matched);
182
183 //calculate P_ineff
184 double P_ineff = 1;
185 if (m_efficiency_Method == "bjet_based"){
186 P_ineff = calculate_P_ineff_bjet_based(b_jet_count, N_missed,SF_eff);
187 }
188 else if (m_efficiency_Method == "Bhadron_pT_eta_based"){
189 P_ineff = calculate_P_ineff_Bhadron_pt_eta_based(accepted_truthBhs, truthBh_to_SSV_matched, SF_eff);
190 }
191 else {
192 ATH_MSG_ERROR("No efficiency computation defined for method=" << m_efficiency_Method);
193 return StatusCode::FAILURE;
194 }
195
196
197 // calculate P_fake
198 double P_fake = 1;
199 if (m_nF_Method == "pileup_bjet_based"){
200 P_fake = calculate_P_fake_pileup_bjet_based(muactual, b_jet_count, N_fake,SF_fake_low, SF_fake_high);
201 }
202 else if (m_nF_Method == "pileup_based_linearfit"){
203 P_fake = calculate_P_fake_pileup_based_linearfit(muactual, N_fake);
204 }
205 else if (m_nF_Method == "pileup_based_binned"){
206 P_fake = calculate_P_fake_pileup_based_binned(muactual, N_fake, SF_fake_low, SF_fake_high);
207 }
208 else {
209 ATH_MSG_ERROR("No fake computation defined for method=" << m_nF_Method);
210 return StatusCode::FAILURE;
211 }
212
213
214 //calculate SSV_weight
215 double SSV_weight = P_eff * P_ineff * P_fake;
216
217 // decorate SSV weight
218 m_SSV_weight_decor.set(*evtInfo, SSV_weight, sys);
219
220 // decorate P factors
221 m_P_eff_decor.set(*evtInfo, P_eff, sys);
222 m_P_ineff_decor.set(*evtInfo, P_ineff, sys);
223 m_P_fake_decor.set(*evtInfo, P_fake, sys);
224
225 //decorate additional information
226 m_N_matched_decor.set(*evtInfo, N_matched, sys);
227 m_N_missed_decor.set(*evtInfo, N_missed, sys);
228 m_N_fake_decor.set(*evtInfo, N_fake, sys);
229
230 m_number_of_bjets_decor.set(*evtInfo, b_jet_count, sys);
231 m_number_of_accepted_Bhadrons_decor.set(*evtInfo, accepted_truthBhs.size(), sys);
232 m_number_of_good_SSVs_decor.set(*evtInfo, good_SSVs.size(), sys);
233
234 //decorate all possible P factors
235 if (m_OutputVariable_Size == "all"){
236 double P_ineff_bjet_based = calculate_P_ineff_bjet_based(b_jet_count, N_missed,SF_eff);
237 double P_ineff_pt_eta_based = calculate_P_ineff_Bhadron_pt_eta_based(accepted_truthBhs, truthBh_to_SSV_matched, SF_eff);
238 double P_fake_pileup_bjet_based = calculate_P_fake_pileup_bjet_based(muactual, b_jet_count, N_fake, SF_fake_low, SF_fake_high);
239 double P_fake_pileup_based_linearfit = calculate_P_fake_pileup_based_linearfit(muactual, N_fake);
240 double P_fake_pileup_based_binned = calculate_P_fake_pileup_based_binned(muactual, N_fake, SF_fake_low, SF_fake_high);
241
242 m_P_ineff_bjet_based_decor.set(*evtInfo, P_ineff_bjet_based, sys);
243 m_P_ineff_pt_eta_based_decor.set(*evtInfo, P_ineff_pt_eta_based, sys);
244 m_P_fake_pileup_bjet_based_decor.set(*evtInfo, P_fake_pileup_bjet_based, sys);
245 m_P_fake_pileup_based_linearfit_decor.set(*evtInfo, P_fake_pileup_based_linearfit, sys);
246 m_P_fake_pileup_based_binned_decor.set(*evtInfo, P_fake_pileup_based_binned, sys);
247 }
248 }
249 return StatusCode::SUCCESS;
250 }
251
252 // create vector that indicates which SSV is a so-called good SSV
253 std::vector<const xAOD::Vertex*> SSVWeightsAlg::create_good_SSVs(
254 const std::vector<const xAOD::Jet*> &jets,
255 const std::vector<const xAOD::Electron*> &electrons,
256 const std::vector<const xAOD::Muon*> &muons,
257 const std::vector<const xAOD::Vertex*> &SSVs) const {
258
259 static const SG::AuxElement::ConstAccessor<float> ssv_pt_accessor(("bvrtPt"));
260 static const SG::AuxElement::ConstAccessor<float> ssv_m_accessor("bvrtM");
261 static const SG::AuxElement::ConstAccessor<float> ssv_eta_accessor("bvrtEta");
262
263 std::vector<const xAOD::Vertex*> good_SSVs;
264
265 for (const xAOD::Vertex* SSV : SSVs) {
266 bool overlaps = false;
267
268 //check if SSV fails good SSV definition
269 if ( (ssv_pt_accessor(*SSV) < 3000) || (ssv_m_accessor(*SSV) < 600) || (std::abs(ssv_eta_accessor(*SSV)) > 2.5) ){
270 continue;
271 }
272
273 //check if SSV overlaps with jet
274 for (const xAOD::Jet* jet : jets) {
275 double DeltaR_jet = compute_DeltaR_between_SSV_and_particle( SSV , jet );
276 if (DeltaR_jet < 0.6){
277 overlaps = true;
278 break;
279 }
280 }
281
282 if (overlaps == true){
283 continue;
284 }
285 //check if SSV overlaps with electron
286 for (const xAOD::Electron* electron : electrons) {
287 double DeltaR_el = compute_DeltaR_between_SSV_and_particle( SSV , electron );
288 if (DeltaR_el < 0.2){
289 overlaps = true;
290 break;
291 }
292 }
293
294 if (overlaps == true){
295 continue;
296 }
297
298 //check if SSV overlaps with muon
299 for (const xAOD::Muon* muon : muons) {
300 double DeltaR_mu = compute_DeltaR_between_SSV_and_particle( SSV , muon );
301 if (DeltaR_mu < 0.2){
302 overlaps = true;
303 break;
304 }
305 }
306
307 if (overlaps == true){
308 continue;
309 }
310 good_SSVs.push_back(SSV);
311 }
312 return good_SSVs;
313 };
314
315 // 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
316 std::vector<const xAOD::TruthParticle*> SSVWeightsAlg::create_accepted_truthBhs(
317 const std::vector<const xAOD::TruthParticle*> &truthBhs,
318 const std::vector<const xAOD::Jet*> &jets) const {
319
320 std::vector<const xAOD::TruthParticle*> accepted_truthBhs;
321
322 for (const xAOD::TruthParticle* truthBh : truthBhs) {
323 // Check if truthBh fails truthBh in acceptance definition
324 if (truthBh->pt() < 2000 || (std::abs(truthBh->eta()) > 2.8)){
325 continue;
326 }
327
328 // check if truthBh overlaps with jet
329 bool overlaps = false;
330 for (const xAOD::Jet* jet : jets) {
331 double DeltaR = truthBh->p4().DeltaR(jet->p4());
332 if (DeltaR<0.6){
333 overlaps = true;
334 break;
335 }
336 }
337
338 if (overlaps == true){
339 continue;
340 }
341
342 accepted_truthBhs.push_back(truthBh);
343 }
344 return accepted_truthBhs;
345 };
346
348 const std::vector<const xAOD::TruthParticle*> &truthBhs,
349 const std::vector<const xAOD::Vertex*> &SSVs) const {
350
351 int N_fake_SSV = 0;
352 for (const xAOD::Vertex* SSV : SSVs){
353 bool foundMatch = false;
354 for (const xAOD::TruthParticle* truthBh : truthBhs){
355 double DeltaR = compute_DeltaR_between_SSV_and_particle(SSV, truthBh);
356 if (DeltaR < 0.3){
357 foundMatch = true;
358 break;
359 }
360 }
361 if (!foundMatch){
362 // In this case no match was found between the current SSV and any truth particle
363 // Hence it is a fake SSV
364 // Increase the number of fake SSV counter
365 N_fake_SSV = N_fake_SSV + 1;
366 }
367 }
368 return N_fake_SSV;
369 }
370
371
372
373
374 // create a vector that indicates if a truthBh got matched to a SSV
376 const std::vector<const xAOD::TruthParticle*> &truthBhs,
377 const std::vector<const xAOD::Vertex*> &SSVs) const {
378
379 std::vector<bool> matched_vector(truthBhs.size(), false);
380 for (size_t i = 0; i < truthBhs.size(); ++i){
381 const xAOD::TruthParticle* truthBh = truthBhs[i];
382 for (size_t j = 0; j < SSVs.size(); ++j){
383 const xAOD::Vertex* SSV = SSVs[j];
384 double DeltaR = compute_DeltaR_between_SSV_and_particle(SSV, truthBh);
385 if (DeltaR < 0.3){
386 matched_vector[i] = true;
387 break;
388 }
389 }
390 }
391 return matched_vector;
392 };
393
394 // compute the DeltaR between a SSV and another particle (jet,electron,truthparticle etc.)
396 const xAOD::Vertex* vtx,
397 const xAOD::IParticle * part) const {
398
399 static const SG::AuxElement::ConstAccessor<float> ssv_eta_accessor("bvrtEta");
400 static const SG::AuxElement::ConstAccessor<float> ssv_phi_accessor("bvrtPhi");
401 // Compute delta eta between vertex and particle
402 double eta_diff = ssv_eta_accessor(*vtx) - part->eta() ;
403
404 // Compute delta phi between vertex and particle
405 // See TLorentzVector::DeltaR function
406 double phi_diff = TVector2::Phi_mpi_pi(ssv_phi_accessor(*vtx) - part->phi() );
407
408 // Compute deltaR between vertex and particle
409 return std::sqrt( eta_diff*eta_diff + phi_diff*phi_diff );
410 }
411
412
413 // count number of matched objects
415 const std::vector<bool> &matching_vector) const {
416
417 // Count the number of times true appears in the vector
418 return std::count(matching_vector.begin(), matching_vector.end(), true);
419 }
420
421
422 // count number of objects that are not matched
424 const std::vector<bool> &matching_vector) const {
425
426 return matching_vector.size() - count_matched_objects(matching_vector);
427 }
428
429 const std::vector<const xAOD::TruthParticle*> SSVWeightsAlg::construct_not_matched_vectors(
430 const std::vector<const xAOD::TruthParticle*> &truthBhs,
431 const std::vector<bool> &matched_vector) const {
432
433 std::vector<const xAOD::TruthParticle*> missed_vector;
434 for (size_t i = 0; i < truthBhs.size(); ++i){
435 if (matched_vector[i] == false){
436 missed_vector.push_back(truthBhs[i]);
437 }
438 }
439 return missed_vector;
440 };
441
442 // Indicate if hadron is heavy flavour hadron and in the final state in the truthparticle tree
444 const xAOD::TruthParticle *part,
445 const int type) const {
446
447 for (unsigned int i = 0; i < part->nChildren(); ++i){
448 const xAOD::TruthParticle *child = part->child(i);
449 if (!child){
450 continue;
451 }
452 if (type == 5){
453 if (child->isBottomHadron()){
454 return false;
455 }
456 if (child->isGenStable()){
457 if (!isHFHadronFinalState(child, type)){
458 return false;
459 }
460 }
461 }
462
463 if (type == 4){
464 if (child->isCharmHadron()){
465 return false;
466 }
467 if (child->isGenStable()){
468 if (!isHFHadronFinalState(child, type)){
469 return false;
470 }
471 }
472 }
473 }
474 return true;
475 }
476
478 const int k,
479 const double lambda) const {
480 // Returns $P(k;\lambda) = \frac{e^{-\lambda}\lambda^k}{k!}$
481 boost::math::poisson distrib(lambda);
482 return boost::math::pdf(distrib, k);
483 }
484
485 //calculate P_ineff based on the Bhadron pT and eta
487 const std::vector<const xAOD::TruthParticle*> &accepted_truthBhs,
488 const std::vector<bool> &truthBh_to_SSV_matched,
489 double SF_eff) const{
490 //construct missed truthBhs
491 const std::vector<const xAOD::TruthParticle*> missed_truthBhs = construct_not_matched_vectors(accepted_truthBhs, truthBh_to_SSV_matched);
492
493 //read off pt bins from JSON file
494 const std::vector<double> &ptbins = m_jsonConfig_SSVWeightsAlg["Efficiency_pt_eta_based"]["pt_bins"];
495
496 double P_ineff2 = 1;
497 for (size_t i = 0; i < missed_truthBhs.size(); ++i) {
498 //retrieve pt,eta of missed truthBh
499 double pt = missed_truthBhs[i]->pt();
500 double eta = std::abs(missed_truthBhs[i]->eta());
501 std::string pt_bin_of_truthBh = "";
502 // iterate pt bins to find appropriate efficiency bin for the truthBh pT
503 for (size_t j = 0; j < ptbins.size() - 1; ++j) {
504 if (pt >= ptbins[j] && pt < ptbins[j+1]) {
505 //construct pt bin name
506 pt_bin_of_truthBh = "pt_bin_" + std::to_string((int)ptbins[j]) + "_" + std::to_string((int)ptbins[j+1]);
507 }
508 if (pt > 100000){
509 pt_bin_of_truthBh = "pt_bin_43500_100000";
510 }
511 }
512 if (pt_bin_of_truthBh == ""){
513 //no pt bin found or no missed truthBh"
514 continue;
515 }
516 //retrieve eta and efficiency bins for the pT bin
517 const std::vector<double>& eta_bins = m_jsonConfig_SSVWeightsAlg["Efficiency_pt_eta_based"][pt_bin_of_truthBh]["eta"];
518 const std::vector<double>& efficiencies = m_jsonConfig_SSVWeightsAlg["Efficiency_pt_eta_based"][pt_bin_of_truthBh]["efficiency"];
519
520 double efficiency = 1;
521
522 //iterate eta bins to find appropriate eta bin for truthBh eta
523 for (size_t k = 0; k < eta_bins.size() - 1; ++k) {
524 double eta_low = eta_bins[k];
525 double eta_up = eta_bins[k+1];
526 if (eta >= eta_low && eta < eta_up) {
527 //eta bin found -> read off corresponding efficiency
528 efficiency = efficiencies[k];
529 }
530 }
531 //calculate P_ineff using the found efficiency
532 P_ineff2 = P_ineff2*(1-SF_eff*efficiency)/(1-efficiency);
533 }
534 return P_ineff2;
535 }
536
537 //calculate P_ineff based on the bjet multiplicity
539 const int b_jet_count,
540 const int N_missed,
541 const double SF_eff) const{
542
543 double P_ineff = 1;
544 // Build the bjets key string
545 std::string bjets_key = std::to_string(b_jet_count) + "_bjets";
546 // Get the value
547
548 double epsilon = 1;
549
550 //retrieve efficiency and average number of fake SSV depending on number of jets in event
551 if (b_jet_count<5 && b_jet_count>0){
552 epsilon = m_jsonConfig_SSVWeightsAlg["Efficiency_bjet_based"][bjets_key];
553 }
554 if (b_jet_count > 4){
555 epsilon = m_jsonConfig_SSVWeightsAlg["Efficiency_bjet_based"]["4_bjets"];
556 }
557 if (b_jet_count < 1){
558 epsilon = m_jsonConfig_SSVWeightsAlg["Efficiency_bjet_based"]["1_bjets"];
559 }
560
561 P_ineff = std::pow((1-SF_eff*epsilon)/(1-epsilon), N_missed);
562
563 return P_ineff;
564 }
565
566 //calculate P_fake based on the bjet multiplicity in the high pileup and low pileup region
568 const double muactual,
569 const int b_jet_count,
570 const int N_fake,
571 const double SF_fake_low,
572 const double SF_fake_high) const{
573
574 double P_fake = 1;
575 // 2D map muactual and Nbjets
576 std::string mu_key = (muactual >= m_lowMuHighMuThreshold) ? "high_muactual" : "low_muactual";
577
578 // Build the bjets key string
579 std::string bjets_key = std::to_string(b_jet_count) + "_bjets";
580 // Get the value
581
582 double n_F_value = 0;
583
584 if (b_jet_count<5 && b_jet_count>0){
585 n_F_value = m_jsonConfig_SSVWeightsAlg["nF_pileup_bjet_based"][mu_key][bjets_key];
586 }
587 if (b_jet_count>4){
588 n_F_value = m_jsonConfig_SSVWeightsAlg["nF_pileup_bjet_based"][mu_key]["4_bjets"];
589 }
590 if (b_jet_count<1){
591 n_F_value = m_jsonConfig_SSVWeightsAlg["nF_pileup_bjet_based"][mu_key]["1_bjets"];
592 }
593
594 if (muactual >= m_lowMuHighMuThreshold){
595 P_fake = (poisson_pmf(N_fake, SF_fake_high*n_F_value))/poisson_pmf(N_fake, n_F_value);
596 }
597 else {
598 P_fake = (poisson_pmf(N_fake, SF_fake_low*n_F_value))/poisson_pmf(N_fake, n_F_value);
599 }
600
601 return P_fake;
602 }
603
604 //calculate P_fake based on a linear fit of the average number of fake SSVs (nF) to the pileup (muactual)
606 const double muactual,
607 const int N_fake) const{
608 // Extract slopes and intercepts from JSON
609 double slope_unscaled = m_jsonConfig_SSVWeightsAlg["nF_pileup_based_linearfit"]["unscaled"]["slope"];
610 double intercept_unscaled = m_jsonConfig_SSVWeightsAlg["nF_pileup_based_linearfit"]["unscaled"]["intercept"];
611
612 double slope_scaled = m_jsonConfig_SSVWeightsAlg["nF_pileup_based_linearfit"]["scaled"]["slope"];
613 double intercept_scaled = m_jsonConfig_SSVWeightsAlg["nF_pileup_based_linearfit"]["scaled"]["intercept"];
614
615 // Calculate expected counts
616 double n_F = slope_unscaled * muactual + intercept_unscaled;
617 double n_F_scaled = slope_scaled * muactual + intercept_scaled;
618
619 // Calculate P_fake
620 double P_fake2 = poisson_pmf(N_fake, n_F_scaled) / poisson_pmf(N_fake, n_F);
621
622 return P_fake2;
623 }
624
625 //calculate P_fake with the pileup binned
627 const double muactual,
628 const int N_fake,
629 const double SF_fake_low,
630 const double SF_fake_high) const{
631 // Extract bin edges and values from the JSON configuration
632 std::vector<double> muactualbins = m_jsonConfig_SSVWeightsAlg["nF_pileup_based_binned"]["muactual_bins"].get<std::vector<double>>();
633 std::vector<double> nFbins = m_jsonConfig_SSVWeightsAlg["nF_pileup_based_binned"]["values"].get<std::vector<double>>();
634
635 double nF = 0;
636 double P_fake3 = 1;
637
638 // Find the correct bin for muactual
639 for (size_t j = 0; j < muactualbins.size() - 1; ++j) {
640 if (muactual >= muactualbins[j] && muactual < muactualbins[j + 1]) {
641 nF = nFbins[j];
642 if (muactual < m_lowMuHighMuThreshold) {
643 P_fake3 = poisson_pmf(N_fake, SF_fake_low * nF) / poisson_pmf(N_fake, nF);
644 } else {
645 P_fake3 = poisson_pmf(N_fake, SF_fake_high * nF) / poisson_pmf(N_fake, nF);
646 }
647 break; // Bin found, no need to continue loop
648 }
649 }
650 return P_fake3;
651 }
652}
Scalar eta() const
pseudorapidity method
#define ATH_MSG_ERROR(x)
#define ANA_MSG_INFO(xmsg)
Macro printing info messages.
#define ANA_MSG_ERROR(xmsg)
Macro printing error messages.
#define ANA_CHECK(EXP)
check whether the given expression was successful
nlohmann::json json
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
TEfficiency * efficiency(const std::string &effName, const std::string &tDir="", const std::string &stream="")
Simplify the retrieval of registered TEfficiency.
std::vector< bool > truthBh_to_SSV_matching(const std::vector< const xAOD::TruthParticle * > &truthBhs, const std::vector< const xAOD::Vertex * > &SSVs) const
int count_not_matched_objects(const std::vector< bool > &matching_vector) const
CP::SysWriteDecorHandle< float > m_number_of_good_SSVs_decor
const double m_lowMuHighMuThreshold
CP::SysReadHandle< xAOD::JetContainer > m_jetsHandle
CP::SysReadHandle< xAOD::VertexContainer > m_ssvHandle
CP::SysWriteDecorHandle< float > m_SSV_weight_decor
std::string m_OutputVariable_Size
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 std::vector< const xAOD::Vertex * > &SSVs) const
CP::SysReadHandle< xAOD::MuonContainer > m_muonsHandle
virtual StatusCode initialize() override
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
double calculate_P_ineff_bjet_based(const int b_jet_count, const int N_missed, const double SF_eff) const
double calculate_P_fake_pileup_based_binned(const double muactual, const int N_fake, const double SF_fake_low, const double SF_fake_high) const
nlohmann::json m_jsonConfig_SSVWeightsAlg
std::string m_nF_Method
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
double calculate_P_fake_pileup_bjet_based(const double muactual, const int b_jet_count, const int N_fake, const double SF_fake_low, const double SF_fake_high) const
double calculate_P_ineff_Bhadron_pt_eta_based(const std::vector< const xAOD::TruthParticle * > &accepted_truthBh, const std::vector< bool > &truthBh_to_SSV_matched, double SF_eff) const
CP::SysWriteDecorHandle< float > m_N_missed_decor
CP::SysWriteDecorHandle< float > m_number_of_accepted_Bhadrons_decor
CP::SysWriteDecorHandle< float > m_number_of_bjets_decor
virtual StatusCode execute() override
int count_matched_objects(const std::vector< bool > &matching_vector) const
CP::SysWriteDecorHandle< float > m_N_matched_decor
CP::SysWriteDecorHandle< float > m_P_fake_pileup_bjet_based_decor
CP::SysReadHandle< xAOD::TruthParticleContainer > m_truthParticlesHandle
double poisson_pmf(const int k, const double lambda) const
CP::SysWriteDecorHandle< float > m_P_ineff_bjet_based_decor
CP::SysWriteDecorHandle< float > m_P_ineff_decor
SSVWeightsAlg(const std::string &name, ISvcLocator *pSvcLocator)
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
double calculate_P_fake_pileup_based_linearfit(const double muactual, const int N_fake) const
std::string m_OverlapRemoval
const std::vector< const xAOD::TruthParticle * > construct_not_matched_vectors(const std::vector< const xAOD::TruthParticle * > &truthBhs, const std::vector< bool > &matched_vector) const
CP::SysWriteDecorHandle< float > m_P_ineff_pt_eta_based_decor
CP::SysWriteDecorHandle< float > m_P_fake_pileup_based_binned_decor
std::string m_efficiency_Method
CP::SysWriteDecorHandle< float > m_N_fake_decor
std::string m_BTagging_WP
std::string m_Jvt
std::string m_jsonConfigPath_SSVWeightsAlg
CP::SysWriteDecorHandle< float > m_P_fake_decor
CP::SysReadHandle< xAOD::ElectronContainer > m_electronsHandle
CP::SysWriteDecorHandle< float > m_P_eff_decor
AnaAlgorithm(const std::string &name, ISvcLocator *pSvcLocator)
constructor with parameters
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
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.
Select isolated Photons, Electrons and Muons.
This module defines the arguments passed from the BATCH driver to the BATCH worker.
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".