ATLAS Offline Software
Loading...
Searching...
No Matches
LhoodMM_tools.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#include <iostream>
13#include "TH1.h"
14#include "TH2.h"
15#include "TH3.h"
16#include "TMatrixT.h"
17#include "TMath.h"
18#include "TFile.h"
19#include "TTree.h"
20#include "TChain.h"
21#include "TTreeReader.h"
22#include "TTreeReaderValue.h"
23#include "TKey.h"
24#include "TDirectory.h"
25#include <chrono>
26#include <memory>
27
28using namespace CP;
31using std::string;
32using std::vector;
33using Clock=std::chrono::high_resolution_clock;
34
35
37
38std::mutex LhoodMM_tools::s_mutex;
39
40
43{
44
46
47 //setup fixHistogramNormalization property
48 declareProperty("FixHistogramNormalization",
50 "Boolean to determine whether or not histograms are scaled such that their normalization "
51 "is equal to the fake yield computed for the entire sample (true = yes, do the scaling)");
52 declareProperty("DoFakeFactorFit",
54 "Give results corresponding to the fake factor method rather than the matrix method");
55
56 // set everything to default values
57 reset();
58}
59
63
64
65
70
71
76
78
79 m_global_fitInfo.reset();
80
81 m_prevSave = false;
82
83 m_nfakes_std = 1.;
85
86 m_maxWeight = 1.;
87
88 m_printLevel = -1;
89
91
93
94 m_fixNormalization = false;
95
96 m_coeffs.clear();
97 m_coeffs.resize(s_nLepMax);
98
99 for (int ilep = 0; ilep < s_nLepMax; ilep++) {
100 m_coeffs[ilep].clear();
101 m_coeffs[ilep].resize((0x1 << (ilep+1)) );
102 for (int ientry = 0; ientry < (0x1 << (ilep+1)); ientry++) {
103 m_coeffs[ilep][ientry].resize((0x1 << (ilep+1)) );
104 for (int jentry = 0; jentry < (0x1 << (ilep+1)); jentry++) {
105 m_coeffs[ilep][ientry][jentry] =0.;
106 }
107 }
108 }
109
110 m_real_indices.clear();
112 m_fake_indices.clear();
114
115 m_maxnlep_loose = 0;
116
117 m_requireSS = false;
118 m_requireOS = false;
119
120 m_fsvec.clear();
121 m_fsvec.resize(s_nLepMax);
122
123 m_OSfrac.clear();
124 m_OSfrac.resize(s_nLepMax);
125
126 for (int ilep = 0; ilep <s_nLepMax; ilep++) {
127 m_OSfrac[ilep].resize(ilep);
128 }
129
130 m_doFakeFactor = false;
131
132 m_nrf_mat_vec.clear();
133 m_MMmatrix_vec.clear();
134 m_nrf_mat_vec.resize(s_nLepMax);
136 m_ntlpred_vec.clear();
137 m_ntlpred_vec.resize(s_nLepMax);
138 for (int ilep = 0; ilep < s_nLepMax; ilep++) {
139 m_nrf_mat_vec[ilep] = std::shared_ptr<TMatrixT<double>>(std::make_shared<TMatrixT<double>>(0x1 << (ilep+1),1) );
140 m_MMmatrix_vec[ilep] = std::shared_ptr<TMatrixT<double>>(std::make_shared<TMatrixT<double>> ((0x1 << (ilep+1)),(0x1 << (ilep+1))));
141 m_ntlpred_vec[ilep] = std::shared_ptr<TMatrixT<double>>(std::make_shared< TMatrixT<double>>(0x1 << (ilep+1),1));
142 }
143
144 m_lastSaveIndex = 0;
145
146 m_alreadyMerged = false;
147
148}
149
150StatusCode LhoodMM_tools::setFitType(const std::string& ft) {
151 if (ft == "FF") {
152 m_doFakeFactor = true;
153 return StatusCode::SUCCESS;
154 } else if (ft == "MM") {
155 m_doFakeFactor = false;
156 return StatusCode::SUCCESS;
157 } else {
158 ATH_MSG_ERROR("Error in LhoodMM_tools::setFitType: please specify \"MM\" for matrix method or \"FF\" for fake factor method");
159 return StatusCode::FAILURE;
160 }
161}
162
163StatusCode LhoodMM_tools::register1DHistogram(TH1* h1, const float *val) {
164
166
167 if(sc != StatusCode::SUCCESS) return sc;
168
169
170 auto currentMap = m_fitInfo_1dhisto_map.find(h1);
171 if (currentMap == m_fitInfo_1dhisto_map.end() ) {
173 // size all the vectors appropriately
174 int ncells = h1->GetNcells();
175 auto *fitinfovec = &m_fitInfo_1dhisto_map.find(h1)->second;
176 fitinfovec->resize(ncells);
177 ATH_MSG_VERBOSE("Just resized fitinfovec");
178 for (int icell = 0; icell < ncells; icell++) {
179 LhoodMMFitInfo* fitInfo = &(fitinfovec->at(icell));
180 fitInfo->reset();
181 }
182
183 ATH_MSG_INFO("Registered a 1D histogram "<<h1->GetName());
184 } else {
185 ATH_MSG_ERROR("Error in LhoodMM_tools::register1DHistogram: histogram has already been registered");
186 return StatusCode::FAILURE;
187 }
188 return StatusCode::SUCCESS;
189}
190
191StatusCode LhoodMM_tools::register2DHistogram(TH2* h2, const float *xval, const float *yval) {
192
193 auto sc = BaseFakeBkgTool::register2DHistogram(h2, xval, yval);
194
195 if(sc != StatusCode::SUCCESS) return sc;
196
197 auto currentMap = m_fitInfo_2dhisto_map.find(h2);
198 if (currentMap == m_fitInfo_2dhisto_map.end() ) {
200 // size all the vectors appropriately
201 int ncells = h2->GetNcells();
202 auto *fitinfovec = &m_fitInfo_2dhisto_map.find(h2)->second;
203 fitinfovec->resize(ncells);
204 for (int icell = 0; icell < ncells; icell++) {
205 LhoodMMFitInfo* fitInfo = &(fitinfovec->at(icell));
206 fitInfo->reset();
207 }
208 ATH_MSG_INFO("Registered a 2D histogram "<<h2->GetName());
209 } else {
210 ATH_MSG_ERROR("Error in LhoodMM_tools::register2DHistogram: histogram has already been registered");
211 return StatusCode::FAILURE;
212 }
213 return StatusCode::SUCCESS;
214}
215
216StatusCode LhoodMM_tools::register3DHistogram(TH3* h3, const float *xval, const float *yval, const float *zval) {
217
218 auto sc = BaseFakeBkgTool::register3DHistogram(h3, xval, yval, zval);
219
220 if(sc != StatusCode::SUCCESS) return sc;
221
222 auto currentMap = m_fitInfo_3dhisto_map.find(h3);
223 if (currentMap == m_fitInfo_3dhisto_map.end() ) {
225 // size all the vectors appropriately
226 int ncells = h3->GetNcells();
227 auto *fitinfovec = &m_fitInfo_3dhisto_map.find(h3)->second;
228 fitinfovec->resize(ncells);
229 for (int icell = 0; icell < ncells; icell++) {
230 LhoodMMFitInfo* fitInfo = &(fitinfovec->at(icell));
231 fitInfo->reset();
232 }
233 ATH_MSG_INFO("Registered a 3D histogram "<<h3->GetName());
234 } else {
235 ATH_MSG_ERROR("Error in LhoodMM_tools::register3DHistogram: histogram has already been registered");
236 return StatusCode::FAILURE;
237 }
238 return StatusCode::SUCCESS;
239}
240
241StatusCode LhoodMM_tools::addEventCustom(const std::vector<bool>& isTight_vals,
242 const std::vector<Efficiency>& realEff_vals,
243 const std::vector<Efficiency>& fakeEff_vals,
244 const std::vector<int>& charges,
245 float extraweight) {
246
247 if (extraweight > m_maxWeight) m_maxWeight = extraweight;
248
249 int nlep = isTight_vals.size();
250
251 m_needToResize = false;
252 if (nlep > m_maxnlep_loose) {
253 m_maxnlep_loose = nlep;
254 m_needToResize = true;
255 }
256
257 LhoodMMEvent mmevt(nlep, realEff_vals, fakeEff_vals, isTight_vals, charges, extraweight);
258
260
261
262 return StatusCode::SUCCESS;
263
264}
265
267
268 int nlep = m_particles.size();
269 if (nlep == 0) {
270 ATH_MSG_WARNING("Attempt to add an event with 0 leptons. This event will be ignored.");
271 return StatusCode::SUCCESS;
272 }
273 std::vector<bool> isTight_vals;
274 std::vector<Efficiency> realEff_vals;
275 std::vector<Efficiency> fakeEff_vals;
276 std::vector<int> charges;
277 std::vector<FakeBkgTools::ParticleData>::const_iterator particles_it;
278 for (particles_it = m_particles.begin(); particles_it != m_particles.end(); ++particles_it) {
279 const FakeBkgTools::ParticleData& p = *particles_it;
280 isTight_vals.push_back(p.tight);
281 realEff_vals.push_back(p.real_efficiency);
282 fakeEff_vals.push_back(p.fake_efficiency);
283 double r_eff = p.real_efficiency.value(this);
284 double f_eff = p.fake_efficiency.value(this);
285
286 if (particles_it == m_particles.begin() ){
287
288 for(const std::pair<short unsigned int, FakeBkgTools::Uncertainty> kv : p.real_efficiency.uncertainties)
289 {
290 ATH_MSG_DEBUG("real eff uncertainties for first lepton are " << kv.second.up << " " << kv.second.down);
291 }
292 for(const std::pair<short unsigned int, FakeBkgTools::Uncertainty> kv : p.fake_efficiency.uncertainties)
293 {
294 ATH_MSG_DEBUG("fake eff uncertainties for first lepton are " << kv.second.up << " " << kv.second.down);
295 }
296 }
297 charges.push_back(p.charge);
298 if ( r_eff < 0.01 && f_eff< 0.01) {
299 ATH_MSG_DEBUG("Found bad efficiency values");
300 }
301 }
302
303 return addEventCustom( isTight_vals, realEff_vals, fakeEff_vals, charges, m_externalWeight);
304}
305
306StatusCode LhoodMM_tools::getTotalYield(float& yield, float& statErrUp, float& statErrDown) {
307
308 if (m_progressFileName != "none" && m_alreadyMerged == false) {
310 }
311
312 //set output level according to debug flag, also check whether setPrintLevel was called directly
313 msgLvl(MSG::DEBUG) || msgLvl(MSG::VERBOSE) || (m_printLevel > 0) ? m_printLevel = 1 : m_printLevel = -1;
314
315 Double_t poserr, negerr;
316
318 yield = nfakes(&poserr,&negerr);
319
320 ATH_MSG_DEBUG("Leaving getTotalYield with yield = " << yield);
321
322 statErrUp = poserr;
323 statErrDown = -negerr;
324
325 return fillHistograms();
326}
327
329
330 int nlep = mmevt.nlep();
331 //increment global matrix
332 if (m_needToResize) {
333 m_global_fitInfo.resizeVectors(nlep);
334 }
335
337
338 // increment matrix terms for all registered histograms
339 // if size needs to increase, this must be done for all bins...
340
341 for (auto map1_iter = m_values_1dhisto_map.begin(); map1_iter != m_values_1dhisto_map.end(); map1_iter++) {
342 const float* val = map1_iter->second;
343 TH1* h = map1_iter->first;
344 auto histoMap = m_fitInfo_1dhisto_map.find(map1_iter->first);
345 if (histoMap != m_fitInfo_1dhisto_map.end() ) {
346 int icell;
347 LhoodMMFitInfo *fitInfo;
348 if (m_needToResize) {
349 for (icell = 0; icell<h->GetNcells(); icell++) {
350 fitInfo = &histoMap->second[icell];
351 fitInfo->resizeVectors(nlep);
352 }
353 }
354 icell = h->FindBin(*val);
355 ATH_MSG_VERBOSE("fitInfo vector size is " << histoMap->second.size());
356 fitInfo = &histoMap->second[icell];
357 ATH_MSG_VERBOSE("icell is " << icell);
358 ATH_MSG_VERBOSE("fitInfo totEvents is " << fitInfo->totEvents);
359 ATH_MSG_VERBOSE("need to resize? " << m_needToResize);
360 ANA_CHECK(incrementOneMatrixSet(*fitInfo, mmevt));
361 ATH_MSG_VERBOSE("need to resize now? " << m_needToResize);
362 } else {
363 ATH_MSG_ERROR("Can not find entry for 1d histogram");
364 return StatusCode::FAILURE;
365 }
366 }
367
368 std::map<TH2*, std::pair<const float*, const float*> >::iterator map2_iter;
369 for (map2_iter = m_values_2dhisto_map.begin(); map2_iter != m_values_2dhisto_map.end(); ++map2_iter) {
370 std::pair<const float*, const float*> val = map2_iter->second;
371 TH2* h = map2_iter->first;
372
373 auto histoMap = m_fitInfo_2dhisto_map.find(map2_iter->first);
374 if (histoMap != m_fitInfo_2dhisto_map.end() ) {
375 int icell;
376 LhoodMMFitInfo *fitInfo;
377 if (m_needToResize) {
378 for (icell = 0; icell<h->GetNcells(); icell++) {
379 fitInfo = &histoMap->second[icell];
380 fitInfo->resizeVectors(nlep);
381 }
382 }
383 icell = h->FindBin(*(val.first), *(val.second));
384 fitInfo = &histoMap->second[icell];
385 ANA_CHECK(incrementOneMatrixSet(*fitInfo, mmevt));
386 } else {
387 ATH_MSG_ERROR("Can not find entry for 2d histogram");
388 return StatusCode::FAILURE;
389 }
390
391 }
392
393 std::map<TH3*, std::tuple<const float*, const float*, const float*> >::iterator map3_iter;
394 for (map3_iter = m_values_3dhisto_map.begin(); map3_iter != m_values_3dhisto_map.end(); ++map3_iter) {
395 std::tuple<const float*, const float*, const float*> val = map3_iter->second;
396 TH3* h = map3_iter->first;
397 auto histoMap = m_fitInfo_3dhisto_map.find(map3_iter->first);
398 if (histoMap != m_fitInfo_3dhisto_map.end() ) {
399 int icell;
400 LhoodMMFitInfo *fitInfo;
401 if (m_needToResize) {
402 for (icell = 0; icell<h->GetNcells(); icell++) {
403 fitInfo = &histoMap->second[icell];
404 fitInfo->resizeVectors(nlep);
405 }
406 }
407 icell = h->FindBin(*(std::get<0>(val)), *(std::get<1>(val)), *(std::get<2>(val)) );
408 fitInfo = &histoMap->second[icell];
409 ANA_CHECK(incrementOneMatrixSet(*fitInfo, mmevt));
410 } else {
411 ATH_MSG_ERROR("Can not find entry for 3d histogram");
412 return StatusCode::FAILURE;
413 }
414 }
415
416 return StatusCode::SUCCESS;
417}
418
420 const LhoodMMEvent& mmevt) {
421
422 int nlep = mmevt.nlep();
423 int lepidx = nlep-1;
424 int rank = 0x1 << nlep;
425
426 fitInfo.totEvents++;
427 fitInfo.eventCount[lepidx]++;
428
429
430 if (nlep <= s_nLepMax) {
431 ATH_MSG_VERBOSE("In incrementMatrices, how many uncertainties? " << mmevt.realEffObj(0).uncertainties.size());
432 ATH_MSG_VERBOSE("In incrementMatrices, m_totEvents = " << fitInfo.totEvents);
433
434 unsigned int catIndex = 0;
435 for (int jlep = 0; jlep < nlep; jlep++) {
436 catIndex += (!mmevt.isTight(jlep)) << jlep;
437 }
438 double weight = mmevt.weight();
439
440 ATH_MSG_VERBOSE("event_cat.size() = " << fitInfo.event_cat.size());
441 ATH_MSG_VERBOSE("event_sumw2.size() = " << fitInfo.event_sumw2.size());
442
443 ATH_MSG_VERBOSE("nlep= " << nlep);
444 ATH_MSG_VERBOSE("catIndex= " << catIndex);
445 ATH_MSG_VERBOSE("fitInfo.event_cat.at(nlep-1).size() = " << fitInfo.event_cat.at(lepidx).size());
446
447 fitInfo.event_cat[lepidx][catIndex] +=weight;
448 fitInfo.event_sumw2[lepidx][catIndex]+=weight*weight;
449
450 ATH_MSG_VERBOSE("Done updating event_cat and event_sumw2");
451
452 //check to see if there is at least one OS pair...
453 for (int icomb = 0; icomb < (0x1 << nlep); icomb++) {
454 int totcharge = 0;
455 std::bitset<s_nLepMax+1> tights(icomb);
456 int ntight = tights.count();
457
458 for (int jlep = 0; jlep < nlep; jlep++) {
459 if (tights[jlep]) {
460 totcharge += mmevt.charge(jlep);
461 }
462 }
463
464 ATH_MSG_VERBOSE("Setting OSfrac_denom[" << lepidx << "][" << ntight << "]");
465 ATH_MSG_VERBOSE("size is " << fitInfo.OSfrac_denom.size() );
466 ATH_MSG_VERBOSE("size is " << fitInfo.OSfrac_denom[lepidx].size() );
467 ATH_MSG_VERBOSE("weight is " << weight );
468 fitInfo.OSfrac_denom[lepidx][ntight]+=weight;
469 ATH_MSG_VERBOSE("Setting OSfrac_num[" << lepidx << "][" << ntight << "]");
470 if ((TMath::Abs(totcharge) < ntight) || ntight == 1) fitInfo.OSfrac_num[lepidx][ntight]+=weight;
471 }
472
473
474 std::vector<std::vector<FakeBkgTools::Efficiency>> vals(2, std::vector<FakeBkgTools::Efficiency>(nlep));
475 for (int ilep = 0; ilep < nlep; ilep++) {
476 ATH_MSG_VERBOSE("1 vals[0].size() = " << vals[0].size());
477 ATH_MSG_VERBOSE("getting efficiency values for lepton " << ilep);
478 ATH_MSG_VERBOSE("how many leptons are there? " << mmevt.nlep());
479 ATH_MSG_VERBOSE("nlep-ilep-1 = " << nlep-ilep-1);
480 ATH_MSG_VERBOSE("2 vals[0].size() = " << vals[0].size());
481 ATH_MSG_VERBOSE("vals[1].size() = " << vals[1].size());
482 vals[0][ilep] = mmevt.realEffObj(ilep);
483 if (m_doFakeFactor) {
484 vals[0][ilep].setToConst(1.0);
485 }
486 vals[1][ilep] = mmevt.fakeEffObj(ilep);
487 ATH_MSG_VERBOSE("Real and fake efficiencies for lepton " << ilep << ": " << vals[0][nlep-ilep-1].value(this) << " " << vals[1][nlep-ilep-1].value(this));
488 ATH_MSG_VERBOSE("this is surely harmless");
489 ATH_MSG_VERBOSE("3 vals[0].size() = " << vals[0].size());
490 }
491
492 FakeBkgTools::Efficiency curr_coeff_num;
493 ATH_MSG_VERBOSE("how many uncertainties in curr_coeff_num? " << curr_coeff_num.uncertainties.size());
494 for (int irf = 0; irf < rank; irf++) {
495 for (int itl = 0; itl < rank; itl++) {
496 curr_coeff_num.setToConst(1.);
497 for (int ilep = 0; ilep < nlep; ilep++) {
498 if (itl & (0x1 << ilep) ) {
499 if (irf & (0x1 << ilep)) {
500 FakeBkgTools::Efficiency tmpEff = vals[1][ilep];
501 curr_coeff_num.multiply(tmpEff.subFromOne());
502 } else {
503 FakeBkgTools::Efficiency tmpEff = vals[0][ilep];
504 curr_coeff_num.multiply(tmpEff.subFromOne());
505 }
506 } else {
507 if (irf & (0x1 << ilep) ) {
508 curr_coeff_num.multiply(vals[1][ilep]);
509 } else {
510 curr_coeff_num.multiply(vals[0][ilep]);
511 }
512 }
513 }
514 ATH_MSG_VERBOSE("about to set m_coeffs_num[" << lepidx << "][" << itl << "][" << irf << "]");
515 ATH_MSG_VERBOSE("setting normterm " << (itl<<nlep) + irf);
516 fitInfo.normterms[lepidx][(itl<<nlep) + irf].add(curr_coeff_num);
517 fitInfo.coeffs_num[lepidx][itl][irf].add(curr_coeff_num);
518 ATH_MSG_VERBOSE("how many uncertainties in coeffs_num? " << fitInfo.coeffs_num[lepidx][itl][irf].uncertainties.size());
519 ATH_MSG_VERBOSE("done setting normterm ");
520 }
521 }
522 return StatusCode::SUCCESS;
523 } else {
524 ATH_MSG_ERROR( "Error: can only handle " << s_nLepMax << " leptons; you tried " << nlep);
525 return StatusCode::FAILURE;
526 }
527
528}
529
531
533
534 for (int ilep = 0; ilep < m_maxnlep_loose; ilep++) {
535 for (int jlep = 0; jlep < ilep; jlep++) {
536 if (m_current_fitInfo->OSfrac_denom[ilep][jlep] > 0) {
537 m_OSfrac[ilep][jlep] = m_current_fitInfo->OSfrac_num[ilep][jlep]/m_current_fitInfo->OSfrac_denom[ilep][jlep];
538 } else {
539 m_OSfrac[ilep][jlep] = 1.;
540 }
541 }
542 }
543
544 return StatusCode::SUCCESS;
545}
546
547
548double LhoodMM_tools::logPoisson(double obs, double pred) {
549
550 double f = -(obs*TMath::Log(pred)-pred);
551 if (obs > 0) f += obs*TMath::Log(obs)-obs;
552 return f;
553}
554
555#ifdef FAKEBKGTOOLS_ATLAS_ENVIRONMENT
556#define ASG_MSG_VERBOSE(x) do { if ( m_current_lhoodMM_tool->msgLvl(MSG::VERBOSE)) m_current_lhoodMM_tool->msg(MSG::VERBOSE) << x << endmsg; } while(0)
557#else
558#define ASG_MSG_VERBOSE(x) do { if(m_current_lhoodMM_tool->msgLvl(MSG::VERBOSE)) std::cout << x << std::endl; } while (0)
559#endif
560
561void LhoodMM_tools::fcn_minnlep_maxnlep(Int_t &npar, Double_t *gin, Double_t &f, Double_t *par, Int_t iflag)
562{
563
565
566 const bool verbose = (l->m_printLevel > 0);
567
568 if (verbose) {
569 ASG_MSG_VERBOSE("Parameters input to fcn_minnlep_maxnlep:" );
570 for (int ipar = 0; ipar < npar; ipar++) {
571 ASG_MSG_VERBOSE(ipar << ": " << par[ipar]);
572 }
573 }
574 int minnlep = l->m_minnlep;
575 int maxnlep_loose = l->m_maxnlep_loose;
576
577 Double_t f_tot = 0.;
578
579 Double_t rsq = par[0];
580
581 Double_t sinterm_tot = 1.;
582
583 int theta_nlep_index = 1 + maxnlep_loose -minnlep;
584 ASG_MSG_VERBOSE("theta_nlep_index initial = " << theta_nlep_index);
585 for (int ilep = minnlep; ilep <= maxnlep_loose; ilep++) {
586 theta_nlep_index += l->m_real_indices[ilep-1].size();
587 ASG_MSG_VERBOSE("theta_nlep_index for ilep = " << ilep << " = " << theta_nlep_index);
588 }
589
590 Double_t pars_thisnlep[s_maxRank]; // a bit of a waste of memory, but avoids compiler warnings for variable-
591
592 if(verbose) ASG_MSG_VERBOSE("theta_nlep_index = " << theta_nlep_index);
593
594 int real_index = 1;
595 for (int ilep = minnlep; ilep <= maxnlep_loose; ilep++) {
596 if (l->m_current_fitInfo->eventCount[ilep-1] == 0) {
597 ASG_MSG_VERBOSE("m_real_indices[" << ilep-1 << "].size() = " << l->m_real_indices[ilep-1].size());
598 real_index += l->m_real_indices[ilep-1].size();
599 for (int ipar = 0; ipar < (int)l->m_fake_indices[ilep-1].size()-1; ipar++) {
600 theta_nlep_index++;
601 }
602 continue;
603 }
604 l->m_curr_nlep = ilep;
605 Int_t npar_thisnlep = 0x1 << ilep;
606
607 int theta_tot_index = l->m_theta_tot_start_index+ilep-minnlep;
608 if(verbose) ASG_MSG_VERBOSE("theta_tot_index, npar = " << theta_tot_index << " " << npar);
609 if(verbose) ASG_MSG_VERBOSE("sinterm_tot, par[theta_tot_index] = " << sinterm_tot << " " << par[theta_tot_index]);
610 if (l->m_maxnlep_loose - minnlep > 0) {
611 double costerm;
612 if (ilep < l->m_maxnlep_loose ) {
613 costerm = TMath::Cos(par[theta_tot_index]);
614 } else {
615 costerm = 1.;
616 }
617 if(verbose) ASG_MSG_VERBOSE("r sinterm_tot costerm = " << rsq <<" " << sinterm_tot << " " << costerm);
618 pars_thisnlep[0] = rsq*sinterm_tot*costerm*sinterm_tot*costerm;
619 sinterm_tot *= TMath::Sin(par[theta_tot_index]);
620 } else {
621 pars_thisnlep[0] = rsq;
622 }
623 if(verbose) ASG_MSG_VERBOSE("pars_thisnlep[0] = " << pars_thisnlep[0]);
624 int par_index = 1;
625 ASG_MSG_VERBOSE("m_real_indices[ilep-1].size() = " << l->m_real_indices[ilep-1].size());
626 for (unsigned ipar = 0; ipar < l->m_real_indices[ilep-1].size(); ipar++) {
627 pars_thisnlep[par_index] = par[real_index+ipar];
628 if(verbose) ASG_MSG_VERBOSE("r pars_thisnlep[" << par_index << "] = " << pars_thisnlep[par_index]);
629 par_index++;
630 }
631
632 for (int ipar = 0; ipar < (int)l->m_fake_indices[ilep-1].size()-1; ipar++) {
633 ASG_MSG_VERBOSE("theta_nlep_index = " << theta_nlep_index );
634 pars_thisnlep[par_index+ipar] = par[theta_nlep_index];
635 if(verbose) ASG_MSG_VERBOSE("f pars_thisnlep[" << par_index+ipar <<"] = " << pars_thisnlep[par_index+ipar]);
636 theta_nlep_index++;
637 }
638 fcn_nlep(npar_thisnlep, gin, f, pars_thisnlep, iflag);
639 f_tot += f;
640
641 real_index += l->m_real_indices[ilep-1].size();
642 }
643
644 f = f_tot;
645}
646
647void LhoodMM_tools::fcn_nlep(Int_t &npar, Double_t *, Double_t &f, Double_t *par, Int_t)
648{
649
651
652 const bool verbose = (l->m_printLevel > 0);
653
654 int nlep = l->m_curr_nlep;
655 int lepidx = nlep-1;
656 int rank = 0x1 << nlep;
657
658 if(verbose) {
659 ASG_MSG_VERBOSE("npar = " << npar);
660 for (int ipar = 0; ipar < npar; ipar++) {
661 ASG_MSG_VERBOSE("Parameter " << ipar << " = " << par[ipar]);
662 }
663 }
664
665 if(verbose) {
666 std::string txt = "testing variable transform: angle pars in fcn: ";
667 for (int i = 0; i < npar; i++) {
668 txt += std::to_string(par[i]) + " ";
669 }
670 ASG_MSG_VERBOSE(txt);
671 }
672
673 // remaining parameters are the angles used to divide up the fakes contribution
674
675 std::shared_ptr<TMatrixT<double>> nrf, MMmatrix, ntlpred;
676 nrf = l->m_nrf_mat_vec[lepidx];
677 if(verbose) {
678 ASG_MSG_VERBOSE("What does nrf look like?");
679 nrf->Print();
680 }
681 MMmatrix = l->m_MMmatrix_vec[lepidx];
682 ntlpred = l->m_ntlpred_vec[lepidx];
683
684 (*nrf)(0,0) = par[0];
685 double rsq = TMath::Abs(par[0]);
686 if(verbose) ASG_MSG_VERBOSE("rsq = " << rsq);
687 int rsize = l->m_real_indices[lepidx].size();
688 int fsize = l->m_fake_indices[lepidx].size();
689
690 for (int ipar = 0; ipar < rsize; ipar++) {
691 ASG_MSG_VERBOSE("In fcn, setting real par " << l->m_real_indices[lepidx][ipar] << " to " << par[ipar+1]);
692 (*nrf)(l->m_real_indices[lepidx][ipar], 0) = par[ipar+1];
693 }
694 double sinterm = 1.;
695
696 if(verbose) ASG_MSG_VERBOSE("nrf[0] = " << (*nrf)(0,0));
697
698 for (int ipar = 0; ipar < fsize; ipar++) {
699 if(verbose) ASG_MSG_VERBOSE("r, sinterm, par[ipar+1] " << rsq << " " << sinterm << " " << par[ipar+1]);
700 if (ipar < fsize-1 ) {
701 double costerm = TMath::Cos(par[rsize+ipar+1]);
702 if(verbose) ASG_MSG_VERBOSE("for setting fake parameter, sinterm = " << sinterm << " par index = " << l->m_real_indices[lepidx].size()+ipar+1);
703 (*nrf)(l->m_fake_indices[lepidx][ipar],0) = rsq*sinterm*costerm*sinterm*costerm;
704 } else {
705 (*nrf)(l->m_fake_indices[lepidx][ipar],0) = rsq*sinterm*sinterm;
706 }
707
708 ASG_MSG_VERBOSE("nrf[" << ipar << "] = " << (*nrf)(ipar,0));
709 ASG_MSG_VERBOSE("In fcn, setting fake par " << l->m_fake_indices[lepidx][ipar] << " to " << (*nrf)(l->m_fake_indices[lepidx][ipar],0));
710
711 sinterm *= TMath::Sin(par[rsize+ipar+1]);
712 }
713
714 ASG_MSG_VERBOSE("Testing variable transform: nrf in fcn: ");
715 if(verbose) {
716 nrf->Print();
717 }
718
719 *ntlpred = (*MMmatrix)*(*nrf);
720
721 ASG_MSG_VERBOSE("Printing the matrix in fcn");
722 if(verbose) {
723 MMmatrix->Print();
724 nrf->Print();
725 }
726 f = 0;
727 int ipar_start;
728 if (l->m_doFakeFactor) {
729 ipar_start = 1;
730 } else {
731 ipar_start = 0;
732 }
733
734 for (int ipar = ipar_start; ipar < rank; ipar++) {
735 if(verbose) ASG_MSG_VERBOSE("Comparing parameter " << ipar << ": " << l->m_current_fitInfo->event_cat.at(lepidx).at(ipar) << " to " << (*ntlpred)(ipar,0));
736 // following scaling for weighted events motivated by arXiv:1309.1287
737 int nobs = l->m_current_fitInfo->event_cat.at(lepidx).at(ipar);
738 double s = 1.;
739 if (nobs > 0) {
740 s= l->m_current_fitInfo->event_sumw2.at(lepidx).at(ipar)/nobs;
741 }
742 if (verbose) ASG_MSG_VERBOSE("s = " << s);
743 f += logPoisson(1.*nobs/s, (*ntlpred)(ipar,0)/s);
744 if (verbose) ASG_MSG_VERBOSE("f = " << f);
745 }
746
747}
748
749
750double LhoodMM_tools::nfakes(Double_t *poserr, Double_t *negerr) {
751 // This top-level function modifies (via a call to setup()) the static variable m_current_lhoodMM_tool,
752 // which is used to provide information to the minimized function fcn_nlep.
753 // It also uses TMinuit for the minimisation.
754 // Re-entrancy (by different class instances in different threads) is thus enforced with a critical section.
755 std::lock_guard<std::mutex> lock(LhoodMM_tools::s_mutex);
756 if (m_current_fitInfo->totEvents == 0) {
757 *poserr = 0.;
758 *negerr = 0.;
759 return 0.;
760 }
761
762 m_fitStatus = 0;
763
765 m_maxnlep = 0;
766
767 m_requireSS = false;
768 m_requireOS = false;
769
770 for (int ilep = 0; ilep < s_nLepMax; ilep++) {
771 string error;
772 // possible issue here -- reassigning vector elements?
773 m_fsvec[ilep].reset(new FakeBkgTools::FinalState(0, ilep+1, m_selection, m_process, error));
774 if (error.size() > 0) {
775 ATH_MSG_VERBOSE("Unable to parse selection " << m_selection << " with process " << m_process << " with " << ilep+1 << " leptons. Error is " << error);
776 continue; // unable to parse selection
777 }
778 if (m_fsvec[ilep]->hasSS() ) {
779 m_requireSS = true;
780 }
781 if (m_fsvec[ilep]->hasOS() ) {
782 m_requireOS = true;
783 }
784 m_fsvec[ilep]->setSS(false);
785 m_fsvec[ilep]->setOS(false);
786
787 // charges will only give the charges of the leptons in the most recently
788 // added event. That's why the tricks with the hasSS, hasOS, setSS, and
789 // setOS functions are played above (so we don't actually have to care
790 // about the values here)
791 auto charges = m_fsvec[ilep]->retrieveCharges(m_particles);
792
793 for (int icomb = 0; icomb < (0x1 << (ilep+1)); icomb++) {
794 FakeBkgTools::FSBitset tights(icomb);
795 ATH_MSG_VERBOSE("ilep " << ilep << " (0x1 << ilep) " << std::hex << (0x1 << ilep) << " icomb " << std::hex << icomb << std::dec);
796 ATH_MSG_VERBOSE("tights = " << std::hex << tights << std::dec);
797 ATH_MSG_VERBOSE("charges = " << std::hex << charges << std::dec);
798
799 if (m_fsvec[ilep]->accept_selection(tights, charges)) {
800 int nlep = ilep+1;
801 ATH_MSG_VERBOSE("tights = " << std::hex << tights << std::dec << " nlep = " << nlep);
802 if (nlep > m_maxnlep) m_maxnlep = nlep;
803 if (nlep < m_minnlep) m_minnlep = nlep;
804 }
805 }
806
807 }
808
809 //protect against selection strings that don't explicitly require at least
810 //one lepton
811 if (m_minnlep < 1) m_minnlep = 1;
812
813 ATH_MSG_VERBOSE("m_minnlep, m_maxnlep = " << m_minnlep << " " << m_maxnlep);
814 int minNlep_proc = m_maxnlep;
815 int maxNlep_proc = 0;
816 for (int n=m_minnlep;n<=m_maxnlep;++n) {
817 for(unsigned c=0;c<(1u<<n);++c) // loop on all possible n-particles tight/loose combinations; there are 2^n of them
818 {
819 FakeBkgTools::FSBitset fakes = c;
820 FakeBkgTools::FSBitset reals = 0;
821 FakeBkgTools::FSBitset tights = 0;
822 for (int ibit = 0; ibit < n; ibit++) {
823 reals.set(ibit, ~fakes[ibit]);
824 tights.set(ibit, 1);
825 }
826 if (m_fsvec[n-1]->accept_process(n, reals, tights) ) {
827 if(n < minNlep_proc) minNlep_proc = n; // minNlep set to "number of tight leptons in the combination with the least number fo tight leptons"
828 if (n > maxNlep_proc) {
829 maxNlep_proc = n;
830 }
831 ATH_MSG_VERBOSE("maxNlep_proc = "<< maxNlep_proc);
832 break;
833 }
834 }
835 }
836
837 m_minnlep = minNlep_proc;
838 m_maxnlep = maxNlep_proc;
839 if(setup() != StatusCode::SUCCESS) return 0.;
840
843 }
844
846 ATH_MSG_WARNING("Selection requires more leptons than are in any event in the sample; returning nfakes = 0 +/- 0 " << m_maxnlep_loose);
847 *poserr = 0.;
848 *negerr = 0.;
849 return 0.;
850 }
851
852 ATH_MSG_VERBOSE("In nfakes, m_minnlep, m_maxnlep, m_maxnlep_loose = " << m_minnlep << " " << m_maxnlep << " " << m_maxnlep_loose);
853
854 double nfake_fit, nfake_fitErr;
855
856 int npar = 0;
857 for (int ilep = m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
858 npar += 0x1 << ilep;
859 }
860
861 ATH_MSG_VERBOSE("In nfakes, npar = " << npar);
862
863 std::unique_ptr<TMinuit_LHMM> lhoodFit(new TMinuit_LHMM(npar));
864 lhoodFit->SetPrintLevel(m_printLevel);
865 lhoodFit->SetFCN(fcn_minnlep_maxnlep);
866
867
868 Double_t arglist[10];
869 Int_t ierflg = 0;
870
871 arglist[0] = 0.5;
872 lhoodFit->mnexcm("SET ERR", arglist ,1,ierflg);;
873
874 m_nfakes_std = 0;
876
877 ATH_MSG_VERBOSE("About to initialize parameters");
878
879 vector<double> init_pars;
880 init_pars.resize(npar);
881 int index = 0;
882 vector< vector <double> > loc_init_pars;
883 loc_init_pars.resize(m_maxnlep_loose);
884
885 for (int ilep = m_minnlep; ilep <=m_maxnlep_loose; ilep++) {
886 loc_init_pars[ilep-1].resize(0x1 << ilep);
887 get_init_pars(loc_init_pars[ilep-1], ilep);
888 for (int ipar = 0; ipar < (0x1 << ilep); ipar++) {
889 init_pars[index+ipar] = loc_init_pars[ilep-1][ipar];
890 }
891 index+= 0x1 << ilep;
892 }
893
894 Double_t step = TMath::Max(loc_init_pars[m_minnlep-1][0]/1000,0.001);
895 ATH_MSG_VERBOSE("parameters initialized OK");
896 ATH_MSG_VERBOSE("m_nfakes_std_err = " << m_nfakes_std_err);
897
898 m_nfakes_std_err = TMath::Sqrt(m_nfakes_std_err);
899
900 vector<TString> parameterName;
901 vector<int> nreal_start_indices;
902 nreal_start_indices.resize(m_maxnlep_loose);
903 parameterName.push_back("nfake_tot");
904 TString sreal = "nreal";
905 for (int ilep = m_minnlep; ilep<= m_maxnlep_loose; ilep++) {
906 char tmpchar[20];
907 sprintf(tmpchar, "_%i", ilep);
908 TString tmpstr = sreal;
909 tmpstr.Append(tmpchar);
910 for (unsigned isublep = 0; isublep < m_real_indices[ilep-1].size(); isublep++) {
911 TString locsreal = tmpstr;
912 char tmpchar2[20];
913 sprintf(tmpchar2, "_%u", isublep);
914 locsreal.Append(tmpchar2);
915 parameterName.push_back(locsreal);
916 if (isublep == 0) {
917 nreal_start_indices[ilep-1] = (parameterName.size());
918 ATH_MSG_VERBOSE("nreal_start_indices[" << ilep-1 << "] = " << nreal_start_indices[ilep-1]);
919 }
920 }
921 }
922
923 TString stheta_tot = "theta_tot";
924 for (int ilep = m_minnlep; ilep<m_maxnlep_loose; ilep++) {
925 char tmpchar[20];
926 sprintf(tmpchar, "_%i", ilep-m_minnlep);
927 TString tmpstr = stheta_tot;
928 tmpstr.Append(tmpchar);
929 parameterName.push_back(tmpstr);
930 }
931
932 for (int ilep = m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
933 TString stheta = "theta_";
934 char tmpchar[20];
935 sprintf(tmpchar, "%i", ilep);
936 TString tmpstr = std::move(stheta);
937 tmpstr.Append(tmpchar);
938 tmpstr.Append("_");
939 ATH_MSG_VERBOSE("How many fake indices?" << m_fake_indices[ilep-1].size());
940 for(int jlep = 0; jlep < (int)m_fake_indices[ilep-1].size() - 1; jlep++) {
941 TString locstheta = tmpstr;
942 char tmpchar2[20];
943 sprintf(tmpchar2, "%i", jlep);
944 locstheta.Append(tmpchar2);
945 parameterName.push_back(locstheta);
946 }
947
948 }
949
950 vector<double> theta_tot;
951 ATH_MSG_VERBOSE("m_maxnlep_loose = " << m_maxnlep_loose << " m_minnlep = " << m_minnlep);
952 theta_tot.resize(m_maxnlep_loose-m_minnlep);
953 ATH_MSG_VERBOSE("theta_tot.size() = " << theta_tot.size());
954 ATH_MSG_VERBOSE("m_nfakes_std = " << m_nfakes_std);
955 if (m_nfakes_std > 0.) {
956 double sinterm = 1.;
957 int theta_index = 0;
958 for (int ilep = m_minnlep; ilep < m_maxnlep_loose; ilep++ ){
959 ATH_MSG_VERBOSE("nfakes for nlep = " << ilep << " used to find theta_tot = " << loc_init_pars[ilep-1][0]);
960 theta_tot[theta_index] = TMath::ACos(TMath::Sqrt(TMath::Max(loc_init_pars[ilep-1][0],0.)/(m_nfakes_std))/sinterm);
961 if (TMath::IsNaN( theta_tot[theta_index] ) ) {
962 theta_tot[theta_index] = s_piover4;
963 }
964 sinterm *= TMath::Sin(theta_tot[theta_index]);
965 theta_index++;
966 }
967 } else {
968 int theta_index = 0;
969 for (int ilep = m_minnlep; ilep < m_maxnlep_loose; ilep++ ){
970 theta_tot[theta_index] = s_piover4;
971 theta_index++;
972 }
973 }
974
975 ATH_MSG_VERBOSE("About to set upper limits");
976 vector<double> upper_limits;
977 upper_limits.resize(npar);
978
979 upper_limits[0] = TMath::Max(5.*m_maxWeight*m_current_fitInfo->totEvents, 0.01);
980
981 int real_index = 1;
982 for (int ilep = m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
983 for (unsigned isublep = 0; isublep < m_real_indices[ilep-1].size(); isublep++) {
984 upper_limits[real_index] = 5*m_maxWeight*m_current_fitInfo->eventCount[ilep-1];
985 real_index++;
986 }
987 }
988
989 for (int ipar = real_index; ipar < npar; ipar++) {
990 upper_limits[ipar] = s_piover2;
991 }
992
993 //re-organize from "per-lepton" parameters to global parameters
994 vector<double> init_par_values;
995 init_par_values.resize(npar);
996 ATH_MSG_VERBOSE("Setting parameter 0 to " << TMath::Max(m_nfakes_std,0.5));
997 init_par_values[0] = TMath::Max(m_nfakes_std,0.);
998 int init_index = 1;
999 int glob_index = 1;
1000 for (int ilep= m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
1001 for (unsigned isublep = 0; isublep < m_real_indices[ilep-1].size(); isublep++) {
1002 ATH_MSG_VERBOSE("Setting parameter " << glob_index << " to " << init_pars[init_index+isublep]);
1003 init_par_values[glob_index] = init_pars[init_index+isublep];
1004 glob_index++;
1005 }
1006 init_index+= pow(2,ilep);
1007 }
1008
1010 for (int ilep = m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
1011 m_theta_tot_start_index += m_real_indices[ilep-1].size();
1012 }
1013 vector<int> theta_start_indices;
1014 theta_start_indices.resize(m_maxnlep_loose);
1016 for (int i = m_minnlep; i < m_maxnlep_loose; i++) {
1017 theta_start_indices[i] = theta_start_indices[i-1] + m_fake_indices[i-1].size() - 1;
1018 }
1019
1020 for (int ilep= m_minnlep; ilep < m_maxnlep_loose; ilep++) {
1021 ATH_MSG_VERBOSE("Setting parameter " << m_theta_tot_start_index+ilep-m_minnlep << " to " << theta_tot[ilep-m_minnlep]);
1022 init_par_values[m_theta_tot_start_index+ilep-m_minnlep] = theta_tot[ilep-m_minnlep];
1023 }
1024
1025 ATH_MSG_VERBOSE( "all done!");
1026
1028 index = 1;
1029 for (int ilep= m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
1030 for (int ipar = 0; ipar < (int)m_fake_indices[ilep-1].size() - 1; ipar++) {
1031 ATH_MSG_VERBOSE("Setting parameter " << currpar << " to " << init_pars[index+m_real_indices[ilep-1].size()+ipar]);
1032 init_par_values[currpar] = init_pars[index+m_real_indices[ilep-1].size()+ipar];
1033 currpar++;
1034 }
1035 index+= 0x1 << ilep;
1036 }
1037 int ipar;
1038 for (ipar = 0; ipar < npar; ipar++) {
1039 lhoodFit->mnparm(ipar, parameterName[ipar], init_par_values[ipar], step, 0., upper_limits[ipar], ierflg);
1040 }
1041
1042 ATH_MSG_VERBOSE("About to fix some parameters");
1043 ATH_MSG_VERBOSE("m_minnlep = " << m_minnlep);
1044 ATH_MSG_VERBOSE("m_maxnlep_loose = " << m_maxnlep_loose);
1045 // account for case where there may be no leptons of a given multiplicity
1046 // (or no fake leptons allowed by the process string) by
1047 // fixing the parameters that are relevant to that multiplicity.
1048 // Also check that at least one lepton multiplicity is valid
1049
1050 int nGoodLeptonMult = 0;
1051
1052 for (int ilep = m_minnlep; ilep <= m_maxnlep_loose; ilep++) {
1053 if (m_current_fitInfo->eventCount[ilep-1] == 0 ||
1054 m_fake_indices[ilep-1].size() == 0) {
1055 // start with the nreal parameters
1056 for (unsigned ipar = nreal_start_indices[ilep-1]; ipar < nreal_start_indices[ilep-1] + m_real_indices[ilep-1].size(); ipar++) {
1057 arglist[0] = ipar;
1058 arglist[1] = 0.;
1059 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1060 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1061 }
1062 //now the theta_tot angle associated with this multiplicity
1063 arglist[0] = m_theta_tot_start_index + ilep - m_minnlep +1;
1064 arglist[1] = s_piover2;
1065 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1066 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1067
1068 //now all the angle parameters for this lepton multiplicity
1069 for (unsigned ipar = theta_start_indices[ilep-1]+1; ipar < theta_start_indices[ilep-1] + m_fake_indices[ilep-1].size() ; ipar++) {
1070 arglist[0] = ipar;
1071 arglist[1] = 0.;
1072 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1073 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1074 }
1075 } else {
1076 nGoodLeptonMult++;
1077 }
1078 index += (0x1 << ilep) - 2;
1079 }
1080
1081 if (nGoodLeptonMult == 0) {
1082 ATH_MSG_VERBOSE("No possible fake contribution for any lepton multiplicity");
1083 *poserr = 0;
1084 *negerr = 0;
1085 return 0;
1086 }
1087
1088 arglist[0] = 5000;
1089 arglist[1] = 1.0;
1090 lhoodFit->mnexcm("MIGRAD", arglist ,2,ierflg);
1091 ATH_MSG_VERBOSE("ierflg = " << ierflg);
1092 lhoodFit->mnimpr();
1093
1094 Double_t amin, edm, errdef;
1095 Int_t nvpar, nparx, icstat;
1096 lhoodFit->mnstat(amin,edm,errdef,nvpar,nparx,icstat);
1097
1098 lhoodFit->GetParameter(0, nfake_fit, nfake_fitErr);
1099
1100 Double_t gcc;
1101
1102 if (poserr && negerr) {
1103 arglist[0] = 500;
1104 arglist[1] = 1.0;
1105 lhoodFit->mnexcm("MINOS", arglist ,2,ierflg);
1106 lhoodFit->mnerrs(0, *poserr, *negerr, nfake_fitErr, gcc);
1107
1108 // re-retrieve fit values, since MINOS might have found a new minimum
1109 lhoodFit->mnstat(amin,edm,errdef,nvpar,nparx,icstat);
1110 lhoodFit->GetParameter(0, nfake_fit, nfake_fitErr);
1111
1112 // check that MINOS succeeded. If not, fix it...
1113
1114 if ( *poserr < 1.e-5) {
1115 *poserr = fixPosErr(nfake_fit, lhoodFit.get());
1116 }
1117 if (*negerr > -1.e-5 ) {
1118 *negerr = fixNegErr(nfake_fit, lhoodFit.get());
1119 }
1120 //resort to parabolic errors if all else fails
1121 if (*negerr > -1.e-5) {
1122 *negerr = -nfake_fitErr;
1123 // prevent negative error from extending below 0
1124 if (nfake_fit + *negerr < 0.) {
1125 *negerr = -nfake_fit;
1126 }
1127 }
1128
1129 if (*poserr < 1.e-5) {
1130 *poserr = nfake_fitErr;
1131 }
1132
1133 }
1134
1135 return nfake_fit;
1136
1137}
1138
1140
1141 StatusCode status = StatusCode::SUCCESS;
1142
1143 for (auto map1_iter=m_fitInfo_1dhisto_map.begin(); map1_iter != m_fitInfo_1dhisto_map.end(); map1_iter++) {
1144 auto & fitInfo_vec = map1_iter->second;
1145 TH1* histogram = map1_iter->first;
1146 status = fillHisto_internal(fitInfo_vec, histogram);
1147 }
1148
1149 for (auto map2_iter=m_fitInfo_2dhisto_map.begin(); map2_iter != m_fitInfo_2dhisto_map.end(); map2_iter++) {
1150 auto& fitInfo_vec = map2_iter->second;
1151 TH2* histogram = (TH2*)map2_iter->first;
1152 status = fillHisto_internal(fitInfo_vec, histogram);
1153 ATH_MSG_VERBOSE("Inside fill code, mean = " << histogram->ProjectionX()->GetMean());
1154 }
1155
1156 for (auto map3_iter=m_fitInfo_3dhisto_map.begin(); map3_iter != m_fitInfo_3dhisto_map.end(); map3_iter++) {
1157 auto& fitInfo_vec = map3_iter->second;
1158 TH3* histogram = (TH3*)map3_iter->first;
1159 status = fillHisto_internal(fitInfo_vec, histogram);
1160 ATH_MSG_VERBOSE("Inside fill code, mean = " << histogram->ProjectionX()->GetMean());
1161 }
1162
1163 return status;
1164}
1165
1166StatusCode LhoodMM_tools::fillHisto_internal(const std::vector< LhoodMMFitInfo>& fitInfo_vec, TH1* h) {
1167
1168 // If fixNormalization is set to true, the fake yield for the entire sample
1169 // will be calculated first, and then the histogram will be scaled at the end
1170 // so that its normalization matches this yield. Otherwise, the sum of the
1171 // bin entries is not guaranteed to match yield calculated on the sample
1172 // as a whole.
1173
1174 // As a general rule of thumb, if the histogram being created is the key one
1175 // for the analysis (e.g. used to set limits) then it's best to keep
1176 // fixNormalization false. On the other hand, if the analysis is based on
1177 // total yield, and the histogram is a control/validation plot, it's best
1178 // to set fixNormalization to true.
1179
1180 int nbins = h->GetNcells();
1181
1182 ATH_MSG_VERBOSE("nbins = " << nbins);
1183 Double_t nf, poserr, negerr, shift = 0.;
1184
1185 for (int ibin = 0; ibin < nbins; ibin++) {
1186
1187 m_current_fitInfo = &fitInfo_vec[ibin];
1188 int totEvents = m_current_fitInfo->totEvents;
1189 if (totEvents > 0) {
1190 ATH_MSG_VERBOSE("Filling bin " << ibin << " with " << totEvents << " events");
1191 nf = nfakes(&poserr, &negerr);
1192 h->SetBinContent(ibin, nf+shift);
1193 if (TMath::IsNaN(h->GetBinContent(ibin))) {
1194 h->SetBinContent(ibin,0.);
1195 h->SetBinError(ibin, 0.);
1196 } else {
1197 h->SetBinError(ibin,TMath::Max(poserr,-negerr));
1198 }
1199 } else {
1200 h->SetBinContent(ibin,0.);
1201 h->SetBinError(ibin, 0.);
1202 }
1203 ATH_MSG_VERBOSE("Result is " << h->GetBinContent(ibin) << " +/- " << h->GetBinError(ibin));
1204 }
1205
1206
1207 if (m_fixNormalization) {
1208 double poserr, negerr;
1209 double totFakes;
1210
1212 totFakes = nfakes(&poserr, &negerr);
1213
1214 // find total content of histogram, including under- and overflows
1215 double totHistContent = 0.;
1216 for (int ibin = 0; ibin < nbins; ibin++) {
1217 totHistContent += h->GetBinContent(ibin);
1218 }
1219
1220 ATH_MSG_VERBOSE("totHistContent = " << totHistContent);
1221 ATH_MSG_VERBOSE("sum of weights = " << h->GetSumOfWeights());
1222
1223 double scaleFactor;
1224 if (totHistContent > 0.) {
1225 scaleFactor = totFakes/totHistContent;
1226 } else {
1227 scaleFactor = 1.;
1228 }
1229 for (int ibin = 1; ibin <= nbins; ibin++) {
1230 h->SetBinContent(ibin, scaleFactor*h->GetBinContent(ibin));
1231 h->SetBinError(ibin, scaleFactor*h->GetBinError(ibin));
1232 }
1233 }
1234 return StatusCode::SUCCESS;
1235}
1236
1237
1239
1241 return m_nfakes_std;
1242}
1243
1249
1250void LhoodMM_tools::get_init_pars(vector<double> &init_pars, int nlep) {
1251
1252 ATH_MSG_VERBOSE("Setting initial parameters for nlep = " << nlep);
1253
1254 int lepidx = nlep-1;
1255
1256 vector<double> nrf;
1257 nrf.resize(0x1 <<nlep);
1258 get_analytic(nrf, nlep);
1259 m_real_indices[lepidx].clear();
1260 m_fake_indices[lepidx].clear();
1261
1262 std::string txt = "Testing variable transform: Initial nrf: ";
1263 for (auto i = nrf.begin(); i != nrf.end(); ++i)
1264 txt += std::to_string(*i) + ' ';
1265 ATH_MSG_VERBOSE(txt);
1266
1267 vector<double> init_angles;
1268
1269 double nfakes_std_thisnlep = 0;
1270
1271 ATH_MSG_VERBOSE("m_fsvec.size() = " << m_fsvec.size() );
1272 for (int ipar = 0; ipar < 0x1 <<nlep; ipar++) {
1273 ATH_MSG_VERBOSE("ipar = " << ipar );
1274 FakeBkgTools::FSBitset fakes = ipar;
1276 for (int ibit = 0; ibit < nlep; ibit++) {
1277 reals.set(ibit, ~fakes[ibit]);
1278 }
1279 ATH_MSG_VERBOSE("reals set" );
1280 bool countsAsFake = false;
1281 for (int jpar = 0; jpar < 0x1 <<nlep; jpar++) {
1282 FakeBkgTools::FSBitset tights = jpar;
1283 for (int kpar = 0; kpar < 0x1 <<nlep; kpar++) {
1284 FakeBkgTools::FSBitset charges = kpar;
1285 if (!countsAsFake &&
1286 m_fsvec[lepidx]->accept_process(nlep, reals, tights) &&
1287 m_fsvec[lepidx]->accept_selection(tights, charges) ) {
1288 ATH_MSG_VERBOSE("accepted " << ipar);
1289 nfakes_std_thisnlep += nrf[ipar];
1290 m_fake_indices[lepidx].push_back(ipar);
1291 countsAsFake = true;
1292 break;
1293 }
1294 }
1295 }
1296 if (!countsAsFake) {
1297 ATH_MSG_VERBOSE("trying to push onto m_real_indices");
1298 ATH_MSG_VERBOSE("m_real_indices.size() = " << m_real_indices.size());
1299 m_real_indices[lepidx].push_back(ipar);
1300 }
1301 }
1302
1303 init_pars[0] = nfakes_std_thisnlep;
1304 for (unsigned ipar = 1; ipar <= m_real_indices[lepidx].size(); ipar++) {
1305 init_pars[ipar] = nrf[m_real_indices[lepidx][ipar-1]];
1306 }
1307
1308 if (nfakes_std_thisnlep > 0.5) {
1309 double sinterm = 1.;
1310 int ifake = 0;
1311 for (int ipar = m_real_indices[lepidx].size()+1; ipar < (0x1 << nlep); ipar++) {
1312 init_pars[ipar] = TMath::ACos(TMath::Sqrt(TMath::Max(nrf[m_fake_indices[lepidx][ifake] ], 0.)/(nfakes_std_thisnlep))/sinterm);
1313 sinterm *= TMath::Sin(init_pars[ipar]);
1314 ifake++;
1315 }
1316 } else {
1317 for (int ipar = m_real_indices[lepidx].size()+1; ipar < (0x1 << nlep); ipar++) {
1318 init_pars[ipar] = s_piover4;
1319 }
1320 }
1321
1322 if (m_printLevel > 0) {
1323 txt = "testing variable transform: Initial pars: ";
1324 for (int i = 0; i < (0x1 << nlep); i++) {
1325 txt += std::to_string(init_pars[i]) + " ";
1326 }
1327 ATH_MSG_VERBOSE(txt);
1328 }
1329
1330 // fix any nan's...
1331 for (int ipar = 2; ipar < (0x1 << nlep); ipar++) {
1332 if (TMath::IsNaN(init_pars[ipar])) {
1333 init_pars[ipar] = 0.;
1334 }
1335 ATH_MSG_VERBOSE("Setting angle parameter " << ipar << " to " << init_pars[ipar]);
1336 }
1337}
1338
1339
1340void LhoodMM_tools::get_analytic(vector<double>& nrf, const int nlep) {
1341
1342 m_perfectFit = true;
1343 ATH_MSG_VERBOSE("just getting started with nlep = " << nlep);
1344
1345 ATH_MSG_VERBOSE("m_minnlepreg = " << m_minnlep << " m_maxnlep = " << m_maxnlep);
1346
1347
1348 int lepidx = nlep-1;
1349
1350 if (m_current_fitInfo->eventCount[lepidx] == 0) return;
1351
1352 const int rank = 0x1 << nlep;
1353
1354 std::vector<FakeBkgTools::Efficiency> coeff_denom(rank);
1355
1356 ATH_MSG_VERBOSE("All set! ");
1357
1358 FakeBkgTools::FSBitset charges = m_fsvec[lepidx]->retrieveCharges(m_particles);
1359 for (int irf = 0; irf < rank; irf++) {
1360 // add up all the relevant terms in the denominator to translate the
1361 // loose sample counts to tight sample counts within the required range of
1362 // tight lepton multiplicity
1363 coeff_denom[irf].setToConst(0.);
1364 coeff_denom[0] = m_current_fitInfo->coeffs_num[lepidx][0][0]; // don't care about this one, but it can't be 0
1365 float chargefactor ;
1366 FakeBkgTools::FSBitset fakes = irf;
1368 for (int ibit = 0; ibit < nlep; ibit++) {
1369 reals.set(ibit, ~fakes[ibit]);
1370 }
1371 for (int itl = 0; itl < rank; itl++) {
1372 chargefactor = 1.0;
1373 FakeBkgTools::FSBitset antitights = itl;
1374 FakeBkgTools::FSBitset tights = 0;
1375 for (int ibit = 0; ibit < nlep; ibit++) {
1376 tights.set(ibit, ~antitights[ibit]);
1377 }
1378 ATH_MSG_VERBOSE("tights " << tights);
1379 ATH_MSG_VERBOSE("reals " << reals);
1380 ATH_MSG_VERBOSE("charges " << charges);
1381 if (m_fsvec[lepidx]->accept_selection(tights, charges)
1382 && m_fsvec[lepidx]->accept_process(nlep, reals, tights) ) {
1383 ATH_MSG_VERBOSE("Accepted in LhoodMM_tools " << irf);
1384 ATH_MSG_VERBOSE("index is " << (itl<<nlep) + irf);
1385 ATH_MSG_VERBOSE("Adding " << m_current_fitInfo->normterms[lepidx][(itl<<nlep) + irf].value(this) << " to " << irf);
1386 // assume that dilepton fakes are evenly split between OS and SS
1387 if (nlep > 2 && tights.count() == 2) {
1388 if (m_requireOS || m_requireSS) {
1389 chargefactor = 0.5;
1390 ATH_MSG_VERBOSE("Setting SSfactor ");
1391 }
1392 }
1393 if (nlep == 2 && tights.count() == 2) {
1394 if (m_requireOS || m_requireSS) {
1395 chargefactor = m_OSfrac[2][2];
1396 }
1397 }
1398 if (m_requireOS) {
1399 chargefactor = m_OSfrac[lepidx][tights.count()];
1400 }
1401 ATH_MSG_VERBOSE("chargefactor = " << chargefactor << " for nlep = " << nlep);
1402 ATH_MSG_VERBOSE("normterm = " << m_current_fitInfo->normterms[lepidx][(itl<<nlep) + irf].value(this));
1403 FakeBkgTools::Efficiency tmpEff = m_current_fitInfo->normterms[lepidx][(itl<<nlep) + irf];
1404 tmpEff.multiply(chargefactor);
1405 ATH_MSG_VERBOSE("how much space? " << coeff_denom.size());
1406 ATH_MSG_VERBOSE("irf = " << irf);
1407 coeff_denom[irf].add(tmpEff);
1408 ATH_MSG_VERBOSE("m_normterms[" << nlep << "][" << (itl<<nlep) + irf << "]= " << m_current_fitInfo->normterms[lepidx][(itl<<nlep) + irf].value(this));
1409 ATH_MSG_VERBOSE("chargefactor = " << chargefactor);
1410 ATH_MSG_VERBOSE("coeff_denom[" << irf << "] = " << coeff_denom[irf].value(this));
1411 }
1412 }
1413
1414 // The following are "don't care" terms, but need to have non-zero
1415 // denominators
1416 if (coeff_denom[irf].nominal == 0.) {
1417 coeff_denom[irf] = m_current_fitInfo->coeffs_num[lepidx][0][irf];
1418 }
1419
1420 for (int itl = 0; itl < rank; itl++) {
1421 ATH_MSG_VERBOSE("coeff_denom[" << irf << "] = " << coeff_denom[irf].value(this));
1422 m_coeffs[lepidx][itl][irf] = m_current_fitInfo->coeffs_num[lepidx][itl][irf].value(this)/coeff_denom[irf].value(this);
1423 }
1424 }
1425
1426 ATH_MSG_VERBOSE("About to do matrix stuff");
1427 std::shared_ptr<TMatrixT<double>> MMmatrix;
1428 MMmatrix = m_MMmatrix_vec[lepidx];
1429
1430 for (int i = 0; i < rank; i++) {
1431 for (int j = 0; j < rank; j++) {
1432 ATH_MSG_VERBOSE(i << " " << j << " " << m_current_fitInfo->coeffs_num[lepidx][i][j].value(this) << " " << coeff_denom[j].value(this) << " " << m_coeffs[lepidx][i][j]);
1433 (*MMmatrix)(i,j) = m_coeffs[lepidx][i][j];
1434 }
1435 }
1436
1437 const bool verbose = this->msgLvl(MSG::VERBOSE);
1438 ATH_MSG_VERBOSE("Printing the matrix in get_analytic ");
1439 if(verbose) MMmatrix->Print();
1440
1441 TMatrixT<double> MMmatrix_inv(rank,rank);
1442 MMmatrix_inv = *MMmatrix;
1443 MMmatrix_inv.Invert();
1444
1445 TMatrixT<double> MMmatrix_sqr = MMmatrix_inv;
1446 for (int i = 0; i < rank; i++) {
1447 for (int j = 0; j < rank; j++) {
1448 MMmatrix_sqr(i,j) *= MMmatrix_sqr[i][j];
1449 }
1450 }
1451
1452 TMatrixT<double> nevents_mat(rank,1), nfake_mat(rank,1), nfake_err_mat(rank,1);
1453 for (int i = 0; i < rank; i++) {
1454 nevents_mat(i,0) = m_current_fitInfo->event_cat.at(lepidx).at(i);
1455 }
1456
1457 ATH_MSG_VERBOSE("Printing nevents matrix");
1458 if(verbose) nevents_mat.Print();
1459
1460 nfake_mat = MMmatrix_inv*nevents_mat;
1461
1462 ATH_MSG_VERBOSE("MMmatrix:");
1463 if(verbose) MMmatrix->Print();
1464 ATH_MSG_VERBOSE("nevents_lll_mat:");
1465 if(verbose) nevents_mat.Print();
1466 ATH_MSG_VERBOSE("nfake_mat:");
1467 if(verbose) nfake_mat.Print();
1468
1469
1470 nfake_err_mat = MMmatrix_sqr*nevents_mat;
1471
1472 int n_proc_acc = 0;
1473 for (int ipar = 0; ipar < (0x1 <<nlep) ; ipar++) {
1474 nrf[ipar] = nfake_mat(ipar, 0);
1475 if (nrf[ipar] < 0) m_perfectFit = false;
1476 ATH_MSG_VERBOSE("nrf[" << ipar << "] = " << nrf[ipar]);
1477 FakeBkgTools::FSBitset fakes = ipar;
1479 FakeBkgTools::FSBitset tights = 0;
1480 for (int ibit = 0; ibit < nlep; ibit++) {
1481 tights.set(ibit, 1);
1482 reals.set(ibit, ~fakes[ibit]);
1483 }
1484 if (m_fsvec[lepidx]->accept_process(nlep, reals, tights) && m_fsvec[lepidx]->accept_selection(tights, charges)) {
1485 ATH_MSG_VERBOSE("Adding " << nfake_mat(ipar,0) << " to m_nfakes_std");
1486 n_proc_acc++;
1487 m_nfakes_std += nfake_mat(ipar,0);
1488 m_nfakes_std_err += nfake_err_mat(ipar, 0);
1489 }
1490 }
1491
1492 ATH_MSG_VERBOSE("Accepted " << n_proc_acc << " processes for nlep = " << nlep);
1493 ATH_MSG_VERBOSE("m_nfakes_std = " << m_nfakes_std);
1494
1495}
1496
1497double LhoodMM_tools::fixPosErr(double n_fake_fit, TMinuit_LHMM* lhoodFit) {
1498
1499 ATH_MSG_VERBOSE("Trying to fix bad positive error");
1500
1501 // get current value of -lnL
1502 Double_t f_from_fit, junk;
1503 Int_t ijunk;
1504
1505 // do a binary search to find real value of positive error
1506 double n_fake_guess_hi = TMath::Max(n_fake_fit*5,1.);
1507 double n_fake_guess_lo = n_fake_fit;
1508 double n_fake_guess = n_fake_guess_hi;
1509 double f_with_guess;
1510
1511 bool errFound = 0;
1512 bool stopSearch = 0;
1513
1514 double convergeCriteria = 0.01;
1515
1516 int nfake_tot_index = 1;
1517
1518 Double_t arglist[10];
1519
1520 int ierflg;
1521
1522 arglist[0] = nfake_tot_index;
1523 arglist[1] = n_fake_fit;
1524 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1525 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1526
1527 arglist[0] = 10000;
1528 arglist[1] = 1.0;
1529 lhoodFit->mnexcm("MIGRAD", arglist ,2,ierflg);
1530 lhoodFit->mnimpr();
1531
1532 lhoodFit->mnstat(f_from_fit, junk, junk, ijunk, ijunk, ijunk);
1533 ATH_MSG_VERBOSE("f_from_fit = " << f_from_fit);
1534
1535 while (!stopSearch) {
1536 // fit with n_fake_tot fixed to guess value
1537 arglist[0] = nfake_tot_index;
1538 arglist[1] = n_fake_guess;
1539 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1540 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1541
1542 arglist[0] = 10000;
1543 arglist[1] = 1.0;
1544 lhoodFit->mnexcm("MIGRAD", arglist ,2,ierflg);
1545 lhoodFit->mnimpr();
1546
1547 lhoodFit->mnstat(f_with_guess, junk, junk, ijunk, ijunk, ijunk);
1548
1549 ATH_MSG_VERBOSE("nlep, n_fake_guess, n_fake_guess_lo, n_fake_guesss_hi, f_from_fit, f_with_guess: " << "?" << " " << n_fake_guess << " " << n_fake_guess_lo << " " << n_fake_guess_hi << " " << f_from_fit << " " << f_with_guess);
1550
1551 if (TMath::IsNaN(f_with_guess)) {
1552 f_with_guess = f_from_fit + 1.;
1553 }
1554 if ((f_with_guess - f_from_fit) > 0.5) {
1555 n_fake_guess_hi = n_fake_guess;
1556 } else {
1557 n_fake_guess_lo = n_fake_guess;
1558 }
1559
1560 n_fake_guess = 0.5*(n_fake_guess_lo+n_fake_guess_hi);
1561
1562 ATH_MSG_VERBOSE( "n_fake_guess_lo, hi = " << n_fake_guess_hi << " " << n_fake_guess_lo);
1563 if ((n_fake_guess_hi - n_fake_guess_lo)/n_fake_guess_hi < convergeCriteria) {
1564 stopSearch = 1;
1565 ATH_MSG_VERBOSE("(n_fake_guess_lo, n_fake_fit = " << n_fake_guess_lo << " " << n_fake_fit);
1566 if (n_fake_guess_lo > n_fake_fit) {
1567 errFound = 1;
1568 }
1569
1570 }
1571 }
1572
1573 // reset nfakes to value found from original fit, so it's read out properly
1574 // later
1575 arglist[0] = nfake_tot_index;
1576 arglist[1] = n_fake_fit;
1577 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1578
1579 if (errFound) {
1580 return n_fake_guess - n_fake_fit;
1581 } else {
1582 ATH_MSG_VERBOSE("Setting fitStatus to 1 in fixPosErr");
1583 m_fitStatus = 1;
1584 return -1.;
1585 }
1586}
1587
1588double LhoodMM_tools::fixNegErr(double n_fake_fit, TMinuit_LHMM* lhoodFit) {
1589
1590 ATH_MSG_VERBOSE("Trying to fix bad negative error");
1591
1592 // get current value of -lnL
1593 Double_t f_from_fit, junk;
1594 Int_t ijunk;
1595
1596 lhoodFit->mnstat(f_from_fit, junk, junk, ijunk, ijunk, ijunk);
1597
1598 // do a binary search to find real value of positive error
1599 double n_fake_guess_hi = n_fake_fit;
1600 double n_fake_guess_lo = 0.;
1601 double n_fake_guess = n_fake_guess_lo;
1602 double f_with_guess;
1603
1604 bool errFound = 0;
1605 bool stopSearch = 0;
1606 double convergeCriteria = 0.01;
1607 double min_n_fake_guess = 0.05;
1608
1609 int nfake_tot_index = 1;
1610
1611 Double_t arglist[10];
1612
1613 int ierflg;
1614
1615 arglist[0] = nfake_tot_index;
1616 arglist[1] = n_fake_fit;
1617 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1618 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1619
1620 arglist[0] = 10000;
1621 arglist[1] = 1.0;
1622 lhoodFit->mnexcm("MIGRAD", arglist ,2,ierflg);
1623 lhoodFit->mnimpr();
1624
1625 lhoodFit->mnstat(f_from_fit, junk, junk, ijunk, ijunk, ijunk);
1626
1627 while (!stopSearch) {
1628 // fit with n_fake_tot fixed to guess value
1629
1630
1631 arglist[0] = nfake_tot_index;
1632 arglist[1] = n_fake_guess;
1633 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1634 lhoodFit->mnexcm("FIX PAR", arglist, 1, ierflg);
1635
1636 arglist[0] = 10000;
1637 arglist[1] = 1.0;
1638 lhoodFit->mnexcm("MIGRAD", arglist ,2,ierflg);
1639 lhoodFit->mnimpr();
1640 lhoodFit->mnstat(f_with_guess, junk, junk, ijunk, ijunk, ijunk);
1641
1642 ATH_MSG_VERBOSE("nlep, n_fake_guess, n_fake_guess_lo, n_fake_guesss_hi, f_from_fit, f_with_guess: " << "?" << " " << n_fake_guess << " " << n_fake_guess_lo << " " << n_fake_guess_hi << " " << f_from_fit << " " << f_with_guess);
1643
1644 if ((f_with_guess - f_from_fit) > 0.5) {
1645 n_fake_guess_lo = n_fake_guess;
1646 } else {
1647 n_fake_guess_hi = n_fake_guess;
1648 }
1649 n_fake_guess = 0.5*(n_fake_guess_lo+n_fake_guess_hi);
1650
1651 if (((n_fake_guess_hi - n_fake_guess_lo)/n_fake_guess_hi < convergeCriteria) || (n_fake_guess_hi < min_n_fake_guess) ) {
1652 stopSearch = 1;
1653 if (n_fake_guess_hi < n_fake_fit) {
1654 errFound = 1;
1655 }
1656 }
1657 }
1658
1659 // reset nfakes to value found from original fit, so it's read out properly
1660 // later
1661 arglist[0] = nfake_tot_index;
1662 arglist[1] = n_fake_fit;
1663 lhoodFit->mnexcm("SET PAR", arglist, 2, ierflg);
1664
1665 if (errFound) {
1666 return n_fake_guess - n_fake_fit;
1667 } else {
1668 ATH_MSG_VERBOSE("Setting fitStatus to 1 in fixNegErr");
1669 m_fitStatus = 1;
1670 return 1.;
1671 }
1672}
1673
1674StatusCode LhoodMM_tools::saveProgress(TDirectory* dir) {
1675
1676 ATH_MSG_VERBOSE("Saving progress");
1677
1678 if (m_prevSave) {
1679 ATH_MSG_ERROR("Multiple calls to saveProgress are not supported");
1680 return StatusCode::FAILURE;
1681 }
1682
1683 m_prevSave = true;
1684
1685 std::unique_ptr<TTree> t(new TTree("LhoodMM_progress", "Stores current info from LhoodMM_toos"));
1686
1687 std::unique_ptr<TTree> t_nlep(new TTree("LhoodMM_progress_nlep", "Stores minimum and maximum lepton multiplicities"));
1688
1689 auto fitInfoBranch = t->Branch("glb_fitInfo", &m_global_fitInfo);
1690 ATH_MSG_VERBOSE("Branch split level is " << fitInfoBranch->GetSplitLevel() );
1691 t->Branch("fitInfo_1dhisto_map", &m_fitInfo_1dhisto_map);
1692 t->Branch("fitInfo_2dhisto_map", &m_fitInfo_2dhisto_map);
1693 t->Branch("fitInfo_3dhisto_map", &m_fitInfo_3dhisto_map);
1694
1695 t_nlep->Branch("maxnlep", &m_maxnlep_loose);
1696
1697 ATH_MSG_VERBOSE("Filling tree...");
1698 t->Fill();
1699 t_nlep->Fill();
1700 dir->cd();
1701 t->Write();
1702 t_nlep->Write();
1703
1704 return StatusCode::SUCCESS;
1705}
1706
1708
1709 ATH_MSG_VERBOSE("Merging sub jobs");
1710
1711 m_alreadyMerged = true;
1712 std::string filename = PathResolverFindDataFile(m_progressFileName);
1713 std::unique_ptr<TFile> fin( new TFile(filename.c_str()));
1714 if (fin == nullptr) {
1715 ATH_MSG_ERROR("Unable to open merged input file " << filename );
1716 return StatusCode::FAILURE;
1717 }
1718 if(m_progressFileDirectory.length()) {
1719 if(!fin->cd(m_progressFileDirectory.c_str())) {
1720 ATH_MSG_ERROR("Unable to find the directory " << m_progressFileDirectory << " inside the file " << filename);
1721 return StatusCode::FAILURE;
1722 }
1723 }
1724
1725 std::string prefix = (m_progressFileDirectory.length()? "/" + m_progressFileDirectory + "/" : "");
1726
1727 TTree *t_nlep = (TTree*)fin->Get((prefix + "LhoodMM_progress_nlep").c_str());
1728 if (t_nlep == nullptr) {
1729 ATH_MSG_ERROR("Unable to find LhoodMM_progress_nlep tree in " << filename);
1730 return StatusCode::FAILURE;
1731 }
1732
1733 int merged_maxnlep, merged_maxnlep_prev = -1;
1734 t_nlep->SetBranchAddress("maxnlep", &merged_maxnlep);
1735
1736 TTree *t = (TTree*)fin->Get((prefix + "LhoodMM_progress").c_str());
1737 if (t == nullptr) {
1738 ATH_MSG_ERROR("Unable to find LhoodMM_progress tree in " << filename);
1739 return StatusCode::FAILURE;
1740 }
1741
1742 // check that we are not trying to merge subjobs with inconsistent lepton
1743 // multiplicities
1744 Int_t nentries = (Int_t)t_nlep->GetEntries();
1745 for (Int_t ievt = 0; ievt < nentries; ievt++) {
1746 t_nlep->GetEntry(ievt);
1747 if (ievt > 0 && (merged_maxnlep != merged_maxnlep_prev)) {
1748 ATH_MSG_ERROR("Attempting to merge files with different lepton multiplicities. This is not supported.");
1749 return StatusCode::FAILURE;
1750 }
1751 merged_maxnlep_prev = merged_maxnlep;
1752 }
1753
1754 m_maxnlep_loose = merged_maxnlep;
1755
1756 std::unique_ptr<LhoodMMFitInfo> tmpFitInfo(new LhoodMMFitInfo);
1757
1758 tmpFitInfo->resizeVectors(m_maxnlep_loose);
1759 m_global_fitInfo.resizeVectors(m_maxnlep_loose);
1760
1761 LhoodMMFitInfo* fitInfoPtr = tmpFitInfo.get();
1762 t->SetBranchAddress("glb_fitInfo", &fitInfoPtr );
1763
1764 ATH_MSG_VERBOSE("About to add LhoodMMFitInfos");
1765
1766 // prepare to merge any histograms
1767
1768 std::unique_ptr<std::map<TH1*, std::vector< LhoodMMFitInfo > > > tmp_fitInfo_1dhisto_map(new std::map<TH1*, std::vector< LhoodMMFitInfo > >);
1769 auto *tmp_fitInfo_1dhisto_map_ptr = tmp_fitInfo_1dhisto_map.get();
1770 t->SetBranchAddress("fitInfo_1dhisto_map", &tmp_fitInfo_1dhisto_map_ptr);
1771 std::unique_ptr<std::map<TH2*, std::vector< LhoodMMFitInfo > > > tmp_fitInfo_2dhisto_map(new std::map<TH2*, std::vector< LhoodMMFitInfo > >);
1772 auto *tmp_fitInfo_2dhisto_map_ptr = tmp_fitInfo_2dhisto_map.get();
1773 t->SetBranchAddress("fitInfo_2dhisto_map", &tmp_fitInfo_2dhisto_map_ptr);
1774 std::map<TH3*, std::vector< LhoodMMFitInfo > > *tmp_fitInfo_3dhisto_map = new std::map<TH3*, std::vector< LhoodMMFitInfo > >;
1775 t->SetBranchAddress("fitInfo_3dhisto_map", &tmp_fitInfo_3dhisto_map);
1776
1777 nentries = (Int_t)t->GetEntries();
1778 for (Int_t ievt = 0; ievt < nentries; ievt++) {
1779 t->GetEntry(ievt);
1780 ATH_MSG_VERBOSE("Adding LhoodMMFitInto with " << (*tmpFitInfo).totEvents << " events, with m_maxnlep_loose = " << m_maxnlep_loose );
1781 m_global_fitInfo.add(*tmpFitInfo, m_maxnlep_loose);
1782 for (auto& hm : m_fitInfo_1dhisto_map) {
1783 TH1F* histogram = (TH1F*)hm.first;
1784 std::string hname = histogram->GetName();
1785 for (auto& im: *tmp_fitInfo_1dhisto_map) {
1786 ATH_MSG_VERBOSE("Found a matching histogram");
1787 TH1F* ihistogram = (TH1F*)im.first;
1788 std::string iname = ihistogram->GetName();
1789 if (hname == iname) {
1790 int ncells = histogram->GetNcells();
1791 for (int icell = 0; icell<ncells; icell++) {
1792 hm.second[icell].resizeVectors(m_maxnlep_loose);
1793 hm.second[icell].add(im.second[icell], m_maxnlep_loose);
1794 }
1795 }
1796 }
1797 }
1798 for (auto& hm : m_fitInfo_2dhisto_map) {
1799 TH1F* histogram = (TH1F*)hm.first;
1800 std::string hname = histogram->GetName();
1801 for (auto& im: *tmp_fitInfo_2dhisto_map) {
1802 ATH_MSG_VERBOSE("Found a matching histogram");
1803 TH1F* ihistogram = (TH1F*)im.first;
1804 std::string iname = ihistogram->GetName();
1805 if (hname == iname) {
1806 int ncells = histogram->GetNcells();
1807 for (int icell = 0; icell<ncells; icell++) {
1808 hm.second[icell].resizeVectors(m_maxnlep_loose);
1809 hm.second[icell].add(im.second[icell], m_maxnlep_loose);
1810 }
1811 }
1812 }
1813 }
1814 for(auto& hm : m_fitInfo_3dhisto_map){
1815 TH1F* histogram = (TH1F*)hm.first;
1816 std::string hname = histogram->GetName();
1817 for (auto& im: *tmp_fitInfo_3dhisto_map){
1818 ATH_MSG_VERBOSE("Found a matching histogram");
1819 TH1F* ihistogram = (TH1F*)im.first;
1820 std::string iname = ihistogram->GetName();
1821 if (hname == iname) {
1822 int ncells = histogram->GetNcells();
1823 for (int icell = 0; icell<ncells; icell++) {
1824 hm.second[icell].resizeVectors(m_maxnlep_loose);
1825 hm.second[icell].add(im.second[icell], m_maxnlep_loose);
1826 }
1827 }
1828 }
1829 }
1830
1831 ATH_MSG_VERBOSE("Added " << ievt);
1832
1833 }
1834
1835
1836 ATH_MSG_VERBOSE("Merged totEvents is " << m_global_fitInfo.totEvents);
1837 for (int ilep = 0; ilep < m_maxnlep_loose; ilep++) {
1838 ATH_MSG_VERBOSE("Merged event count is " << m_global_fitInfo.eventCount[ilep]);
1839 }
1840
1841 return StatusCode::SUCCESS;
1842}
1843
1844
1845
1846
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
#define ANA_CHECK(EXP)
check whether the given expression was successful
static Double_t sc
#define ASG_MSG_VERBOSE(x)
std::chrono::high_resolution_clock Clock
std::string PathResolverFindDataFile(const std::string &logical_file_name)
constexpr int pow(int base, int exp) noexcept
std::string histogram
Definition chains.cxx:52
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
bool msgLvl(const MSG::Level lvl) const
std::vector< FakeBkgTools::ParticleData > m_particles
bool m_unlimitedSystematicVariations
used to prevent multiple calls to applySystematicVariation() when unsupported set to true in a partic...
std::map< TH2 *, std::pair< const float *, const float * > > m_values_2dhisto_map
std::string m_progressFileName
property ProgressFileName
virtual StatusCode initialize() override
Dummy implementation of the initialisation function.
virtual StatusCode register1DHistogram(TH1 *h1, const float *val) override
associates a 1D histogram to the tool, to obtain a binned estimate of the fake lepton background the ...
std::string m_progressFileDirectory
property ProgressFileDirectory
std::map< TH3 *, std::tuple< const float *, const float *, const float * > > m_values_3dhisto_map
std::string m_selection
'selection' settings used to compute the total yield / fill histograms
virtual StatusCode register3DHistogram(TH3 *h3, const float *xval, const float *yval, const float *zval) override
associates a 3D histogram to the tool, to obtain a binned estimate of the fake lepton background the ...
BaseFakeBkgTool(const std::string &toolname)
std::string m_process
'process' settings used to compute the total yield / fill histograms
virtual StatusCode register2DHistogram(TH2 *h2, const float *xval, const float *yval) override
associates a 2D histogram to the tool, to obtain a binned estimate of the fake lepton background the ...
std::map< TH1 *, const float * > m_values_1dhisto_map
double fixNegErr(double n_fake_fit, TMinuit_LHMM *lhoodFit)
static void fcn_nlep(Int_t &npar, Double_t *gin, Double_t &f, Double_t *par, Int_t iflag)
virtual StatusCode addEventCustom() override
double m_nfakes_std_perEventWeight
static void fcn_minnlep_maxnlep(Int_t &npar, Double_t *gin, Double_t &f, Double_t *par, Int_t iflag)
std::vector< std::vector< double > > m_OSfrac
double fixPosErr(double n_fake_fit, TMinuit_LHMM *lhoodFit)
double nfakes_std(double *error)
virtual StatusCode register3DHistogram(TH3 *h3, const float *xval, const float *yval, const float *zval) override
associates a 3D histogram to the tool, to obtain a binned estimate of the fake lepton background the ...
static std::mutex s_mutex
std::vector< std::shared_ptr< TMatrixT< double > > > m_ntlpred_vec
std::map< TH3 *, std::vector< LhoodMMFitInfo > > m_fitInfo_3dhisto_map
virtual FakeBkgTools::Client clientForDB() override final
This indicates which type of efficiencies/fake factor need to be filled.
const LhoodMMFitInfo * m_current_fitInfo
void get_init_pars(std::vector< double > &init_pars, int nlep)
std::vector< std::vector< int > > m_fake_indices
std::vector< std::vector< int > > m_real_indices
double nfakes_std_perEventWeight(double *error)
virtual StatusCode saveProgress(TDirectory *dir) override
StatusCode incrementOneMatrixSet(LhoodMMFitInfo &fitInfo, const LhoodMMEvent &mmevt)
std::map< TH1 *, std::vector< LhoodMMFitInfo > > m_fitInfo_1dhisto_map
std::map< TH2 *, std::vector< LhoodMMFitInfo > > m_fitInfo_2dhisto_map
StatusCode fillHistograms()
LhoodMM_tools(const std::string &name)
virtual StatusCode getTotalYield(float &yield, float &statErrUp, float &statErrDown) override final
returns the accumulated fake lepton background yield (or compute it, in the case of the likelihood ma...
static constexpr int s_maxRank
StatusCode setFitType(const std::string &ft)
StatusCode incrementMatrices(const LhoodMMEvent &mmevt)
std::vector< std::unique_ptr< FakeBkgTools::FinalState > > m_fsvec
std::vector< std::shared_ptr< TMatrixT< double > > > m_MMmatrix_vec
static const double s_piover2
void get_analytic(std::vector< double > &nrf, const int nlep)
StatusCode mergeSubJobs()
std::vector< std::shared_ptr< TMatrixT< double > > > m_nrf_mat_vec
static const double s_piover4
static double logPoisson(double obs, double pred)
virtual StatusCode register2DHistogram(TH2 *h2, const float *xval, const float *yval) override
associates a 2D histogram to the tool, to obtain a binned estimate of the fake lepton background the ...
std::vector< std::vector< std::vector< double > > > m_coeffs
virtual void reset()
double m_nfakes_std_err_perEventWeight
virtual StatusCode initialize() override
Dummy implementation of the initialisation function.
static constexpr int s_nLepMax
virtual StatusCode register1DHistogram(TH1 *h1, const float *val) override
associates a 1D histogram to the tool, to obtain a binned estimate of the fake lepton background the ...
StatusCode fillHisto_internal(const std::vector< LhoodMMFitInfo > &fitInfo_vec, TH1 *h)
static LhoodMM_tools * m_current_lhoodMM_tool
double nfakes(Double_t *poserr, Double_t *negerr)
LhoodMMFitInfo m_global_fitInfo
const FakeBkgTools::Efficiency & fakeEffObj(unsigned ilep) const
int charge(unsigned ilep) const
bool isTight(unsigned ilep) const
const FakeBkgTools::Efficiency & realEffObj(unsigned ilep) const
unsigned nlep() const
float weight() const
bool add(const std::string &hname, TKey *tobj)
Definition fastadd.cxx:55
bool verbose
Definition hcg.cxx:73
Select isolated Photons, Electrons and Muons.
std::bitset< maxCombinations()> FSBitset
Definition index.py:1
setRawEt setRawPhi int
JetConstituentVector::iterator iterator
std::vector< std::vector< std::vector< FakeBkgTools::Efficiency > > > coeffs_num
void resizeVectors(unsigned nlep)
std::vector< double > eventCount
std::vector< std::vector< FakeBkgTools::Efficiency > > normterms
std::vector< std::vector< double > > OSfrac_denom
std::vector< std::vector< double > > event_sumw2
std::vector< std::vector< double > > OSfrac_num
std::vector< std::vector< double > > event_cat
a structure to hold an efficiency together with a variable number of uncertainties
Efficiency & multiply(const Efficiency &rhs, float weight=1.f)
the first version of multiply() takes the product of two Efficiencies, setting the up and down variat...
std::map< uint16_t, FakeBkgTools::Uncertainty > uncertainties
Efficiency & subFromOne()
subFromOne() sets nominal and varied values to 1 - previous value.
Efficiency & setToConst(float value=1.f)
setToConst() sets the nominal and all varied values to the same constant