CombineHarvester
CombineHarvester.cc
Go to the documentation of this file.
2 #include <iostream>
3 #include <map>
4 #include <string>
5 #include <vector>
6 #include <list>
7 #include "RooFormulaVar.h"
14 
15 namespace ch {
16 
17 CombineHarvester::CombineHarvester() : verbosity_(0), log_(&(std::cout)) {
18  // if (verbosity_ >= 3) {
19  // log() << "[CombineHarvester] Constructor called: " << this << "\n";
20  // }
21  flags_["check-negative-bins-on-import"] = true;
22  flags_["zero-negative-bins-on-import"] = false;
23  flags_["allow-missing-shapes"] = true;
24  flags_["workspaces-use-clone"] = false;
25  flags_["workspace-uuid-recycle"] = true;
26  flags_["import-parameter-err"] = true;
27  flags_["filters-use-regex"] = false;
28  flags_["fix-rooconstvar"] = false;
29  // std::cout << "[CombineHarvester] Constructor called for " << this << "\n";
30 }
31 
33  // std::cout << "[CombineHarvester] Destructor called for " << this << "\n";
34 }
35 
36 void swap(CombineHarvester& first, CombineHarvester& second) {
37  using std::swap;
38  // std::cout << "[CombineHarvester] Swap " << &first << " <-> "
39  // << &second << "\n";
40  swap(first.obs_, second.obs_);
41  swap(first.procs_, second.procs_);
42  swap(first.systs_, second.systs_);
43  swap(first.params_, second.params_);
44  swap(first.wspaces_, second.wspaces_);
45  swap(first.verbosity_, second.verbosity_);
46  swap(first.flags_, second.flags_);
47  swap(first.post_lines_, second.post_lines_);
48  swap(first.log_, second.log_);
49  swap(first.auto_stats_settings_, second.auto_stats_settings_);
50 }
51 
53  : obs_(other.obs_),
54  procs_(other.procs_),
55  systs_(other.systs_),
56  params_(other.params_),
57  wspaces_(other.wspaces_),
58  flags_(other.flags_),
59  auto_stats_settings_(other.auto_stats_settings_),
60  post_lines_(other.post_lines_),
61  verbosity_(other.verbosity_),
62  log_(other.log_) {
63  // std::cout << "[CombineHarvester] Copy-constructor called " << &other
64  // << " -> " << this << "\n";
65 }
66 
67 
68 void CombineHarvester::SetFlag(std::string const& flag, bool const& value) {
69  auto it = flags_.find(flag);
70  if (it != flags_.end()) {
71  FNLOG(std::cout) << "Changing value of flag \"" << it->first << "\" from " << it->second << " to " << value << "\n";
72  it->second = value;
73  } else {
74  FNLOG(std::cout) << "Created new flag \"" << flag << "\" with value " << value << "\n";
75  flags_[flag] = value;
76  }
77 }
78 
79 bool CombineHarvester::GetFlag(std::string const& flag) const {
80  auto it = flags_.find(flag);
81  if (it != flags_.end()) {
82  return it->second;
83  } else {
84  throw std::runtime_error(FNERROR("Flag " + flag + " is not defined"));
85  }
86 }
87 
89  CombineHarvester cpy;
90  // std::cout << "[CombineHarvester] Deep copy called " << this
91  // << " -> " << &cpy << "\n";
92  cpy.obs_.resize(obs_.size());
93  cpy.procs_.resize(procs_.size());
94  cpy.systs_.resize(systs_.size());
95  cpy.flags_ = flags_;
96  cpy.verbosity_ = verbosity_;
97  cpy.post_lines_ = post_lines_;
98  cpy.log_ = log_;
99 
100  // Build a map of workspace object pointers
101  std::map<RooAbsData const*, RooAbsData *> dat_map;
102  std::map<RooAbsReal const*, RooAbsReal *> pdf_map;
103  std::map<RooRealVar const*, RooRealVar *> var_map;
104  std::map<RooAbsReal const*, RooAbsReal *> fun_map;
105 
106  // Loop through each workspace and make a full copy
107  for (auto const& it : wspaces_) {
108  std::string ws_name = it.first;
109  RooWorkspace const& o_wsp = *(it.second.get());
110  cpy.wspaces_.insert({ws_name, std::make_shared<RooWorkspace>(o_wsp)});
111  RooWorkspace & n_wsp = *(cpy.wspaces_.at(ws_name).get());
112 
113  std::list<RooAbsData *> o_dat = o_wsp.allData();
114  RooArgSet const& o_pdf = o_wsp.allPdfs();
115  RooArgSet const& o_var = o_wsp.allVars();
116  RooArgSet const& o_fun = o_wsp.allFunctions();
117 
118  std::list<RooAbsData *> n_dat = n_wsp.allData();
119  RooArgSet const& n_pdf = n_wsp.allPdfs();
120  RooArgSet const& n_var = n_wsp.allVars();
121  RooArgSet const& n_fun = n_wsp.allFunctions();
122 
123  auto o_dat_it = o_dat.begin();
124  auto n_dat_it = n_dat.begin();
125  for (; o_dat_it != o_dat.end(); ++o_dat_it, ++n_dat_it) {
126  dat_map[*o_dat_it] = *n_dat_it;
127  }
128 
129  Int_t nPdf = o_pdf.getSize();
130 
131  for (Int_t i=0; i<nPdf; ++i) {
132 
133  RooAbsReal* o_pdf_ptr = dynamic_cast<RooAbsReal *>(o_pdf[i]);
134  RooAbsReal* n_pdf_ptr = dynamic_cast<RooAbsReal *>(n_pdf[i]);
135  if (o_pdf_ptr && n_pdf_ptr) pdf_map[o_pdf_ptr] = n_pdf_ptr;
136  }
137 
138  Int_t nVar = o_var.getSize();
139 
140  for (Int_t i=0; i<nVar; ++i) {
141  RooRealVar *o_var_ptr = dynamic_cast<RooRealVar*>(o_var[i]);
142  RooRealVar *n_var_ptr = dynamic_cast<RooRealVar*>(n_var[i]);
143  if (o_var_ptr && n_var_ptr) var_map[o_var_ptr] = n_var_ptr;
144  }
145 
146  Int_t nFun = o_fun.getSize();
147 
148  for (Int_t i=0; i<nFun; ++i) {
149  RooAbsReal* o_fun_ptr = dynamic_cast<RooAbsReal *>(o_fun[i]);
150  RooAbsReal* n_fun_ptr = dynamic_cast<RooAbsReal *>(n_fun[i]);
151  if (o_fun_ptr && n_fun_ptr) fun_map[o_fun_ptr] = n_fun_ptr;
152  }
153 
154  }
155 
156 
157  for (std::size_t i = 0; i < cpy.obs_.size(); ++i) {
158  if (obs_[i]) {
159  cpy.obs_[i] = std::make_shared<Observation>(*(obs_[i]));
160  if (obs_[i]->data())
161  cpy.obs_[i]->set_data(dat_map.at(obs_[i]->data()));
162  }
163  }
164 
165  for (std::size_t i = 0; i < cpy.procs_.size(); ++i) {
166  if (procs_[i]) {
167  cpy.procs_[i] = std::make_shared<Process>(*(procs_[i]));
168  if (procs_[i]->pdf())
169  cpy.procs_[i]->set_pdf(pdf_map.at(procs_[i]->pdf()));
170  if (procs_[i]->observable())
171  cpy.procs_[i]->set_observable(var_map.at(procs_[i]->observable()));
172  if (procs_[i]->data())
173  cpy.procs_[i]->set_data(dat_map.at(procs_[i]->data()));
174  if (procs_[i]->norm())
175  cpy.procs_[i]->set_norm(fun_map.at(procs_[i]->norm()));
176  }
177  }
178 
179  for (std::size_t i = 0; i < cpy.systs_.size(); ++i) {
180  if (systs_[i]) {
181  cpy.systs_[i] = std::make_shared<Systematic>(*(systs_[i]));
182  if (systs_[i]->data_u() || systs_[i]->data_d()) {
183  cpy.systs_[i]->set_data(
184  static_cast<RooDataHist*>(dat_map.at(systs_[i]->data_u())),
185  static_cast<RooDataHist*>(dat_map.at(systs_[i]->data_d())),
186  nullptr);
187  }
188  }
189  }
190 
191  for (auto const& it : params_) {
192  if (it.second) {
193  cpy.params_.insert({it.first, std::make_shared<Parameter>(*(it.second))});
194  } else {
195  params_.insert({it.first, nullptr});
196  }
197  }
198 
199  for (auto it : cpy.params_) {
200  for (unsigned i = 0; i < it.second->vars().size(); ++i) {
201  it.second->vars()[i] = var_map.at(it.second->vars()[i]);
202  }
203  }
204  return cpy;
205 }
206 
208  // std::cout << "[CombineHarvester] Move-constructor called " <<
209  // &other << " -> " << this << "\n";
210  swap(*this, other);
211 }
212 
214  // std::cout << "[CombineHarvester] Assignment operator called " << &other <<
215  // " -> " << this << "\n";
216  swap(*this, other);
217  return (*this);
218 }
219 
221  // std::cout << "[CombineHarvester] Shallow copy method cp() called from " <<
222  // this << "\n";
223  return CombineHarvester(*this);
224 }
225 
227  return PrintObs().PrintProcs().PrintSysts().PrintParams();
228 }
229 
231  std::cout << Observation::PrintHeader;
232  for (unsigned i = 0; i < obs_.size(); ++i)
233  std::cout << *(obs_[i]) << "\n";
234  return *this;
235 }
236 
238  std::cout << Process::PrintHeader;
239  for (unsigned i = 0; i < procs_.size(); ++i)
240  std::cout << *(procs_[i]) << "\n";
241  return *this;
242 }
243 
245  std::cout << Systematic::PrintHeader;
246  for (unsigned i = 0; i < systs_.size(); ++i)
247  std::cout << *(systs_[i]) << "\n";
248  return *this;
249 }
250 
252  std::cout << Parameter::PrintHeader;
253  for (auto const& it : params_) std::cout << *(it.second) << "\n";
254  return *this;
255 }
256 
257 
281 void CombineHarvester::LoadShapes(Observation* entry,
282  std::vector<HistMapping> const& mappings) {
283  // Pre-condition #1
284  if (entry->shape() || entry->data()) {
285  throw std::runtime_error(FNERROR("Observation already contains a shape"));
286  }
287 
288  if (verbosity_ >= 2) {
289  LOGLINE(log(), "Extracting shapes for Observation:");
290  log() << Observation::PrintHeader << *entry << "\n";
291  LOGLINE(log(), "Mappings:");
292  for (HistMapping const& m : mappings) log() << m << "\n";
293  }
294 
295  // Pre-condition #2
296  // ResolveMapping will throw if this fails
297  if (mappings.size() == 0) {
298  if (verbosity_ >= 2) LOGLINE(log(), "No mappings in this card, assuming FAKE");
299  return;
300  }
301  HistMapping mapping =
302  ResolveMapping(entry->process(), entry->bin(), mappings);
303  boost::replace_all(mapping.pattern, "$CHANNEL", entry->bin());
304  boost::replace_all(mapping.pattern, "$BIN", entry->bin());
305  boost::replace_all(mapping.pattern, "$PROCESS", entry->process());
306  boost::replace_all(mapping.pattern, "$MASS", entry->mass());
307 
308  if (verbosity_ >= 2) {
309  LOGLINE(log(), "Resolved Mapping:");
310  log() << mapping << "\n";
311  }
312 
313  if (mapping.is_fake) {
314  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is FAKE");
315  } else if (mapping.IsHist()) {
316  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type in TH1");
317  // Pre-condition #3
318  // GetClonedTH1 will throw if this fails
319  std::unique_ptr<TH1> h = GetClonedTH1(mapping.file.get(), mapping.pattern);
320  // Post-conditions #1 and #2
321  entry->set_shape(std::move(h), true);
322  } else if (mapping.IsData()) {
323  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is RooAbsData");
324  // Pre-condition #3
325  // SetupWorkspace will throw if workspace not found
326  RooAbsData* dat = mapping.ws->data(mapping.WorkspaceObj().c_str());
327  if (!dat) {
328  throw std::runtime_error(FNERROR("RooAbsData not found in workspace"));
329  }
330  // Post-condition #1
331  entry->set_data(dat);
332  entry->set_rate(dat->sumEntries());
333  }
334 }
335 
365 void CombineHarvester::LoadShapes(Process* entry,
366  std::vector<HistMapping> const& mappings) {
367  // Pre-condition #1
368  if (entry->shape() || entry->pdf()) {
369  throw std::runtime_error(FNERROR("Process already contains a shape"));
370  }
371 
372  if (verbosity_ >= 2) {
373  LOGLINE(log(), "Extracting shapes for Process:");
374  log() << Process::PrintHeader << *entry << "\n";
375  LOGLINE(log(), "Mappings:");
376  for (HistMapping const& m : mappings) log() << m << "\n";
377  }
378 
379  // Pre-condition #2
380  // ResolveMapping will throw if this fails
381  if (mappings.size() == 0) {
382  if (verbosity_ >= 2) LOGLINE(log(), "No mappings in this card, assuming FAKE");
383  return;
384  }
385  HistMapping mapping =
386  ResolveMapping(entry->process(), entry->bin(), mappings);
387  boost::replace_all(mapping.pattern, "$CHANNEL", entry->bin());
388  boost::replace_all(mapping.pattern, "$BIN", entry->bin());
389  boost::replace_all(mapping.pattern, "$PROCESS", entry->process());
390  boost::replace_all(mapping.pattern, "$MASS", entry->mass());
391 
392  if (verbosity_ >= 2) {
393  LOGLINE(log(), "Resolved Mapping:");
394  log() << mapping << "\n";
395  }
396 
397  if (mapping.is_fake) {
398  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is FAKE");
399  } else if (mapping.IsHist()) {
400  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is TH1");
401  // Pre-condition #3
402  // GetClonedTH1 will throw if this fails
403  std::unique_ptr<TH1> h = GetClonedTH1(mapping.file.get(), mapping.pattern);
404 
405  if (flags_.at("check-negative-bins-on-import")) {
406  if (HasNegativeBins(h.get())) {
407  LOGLINE(log(), "Warning: process shape has negative bins");
408  log() << Process::PrintHeader << *entry << "\n";
409  if (flags_.at("zero-negative-bins-on-import")) {
410  ZeroNegativeBins(h.get());
411  }
412  }
413  }
414  // Post-conditions #1 and #2
415  entry->set_shape(std::move(h), true);
416  } else if (mapping.IsPdf()) {
417  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is RooAbsPdf/RooAbsData");
418  // Pre-condition #3
419  // Try and get this as RooAbsData first. If this doesn't work try pdf
420  RooAbsData* data = mapping.ws->data(mapping.WorkspaceObj().c_str());
421  RooAbsReal* pdf = mapping.ws->function(mapping.WorkspaceObj().c_str());
422  if (data) {
423  if (verbosity_ >= 2) {
424  data->printStream(log(), data->defaultPrintContents(0),
425  data->defaultPrintStyle(0), "[LoadShapes] ");
426  }
427  entry->set_data(data);
428  entry->set_rate(data->sumEntries());
429  } else if (pdf) {
430  if (verbosity_ >= 2) {
431  pdf->printStream(log(), pdf->defaultPrintContents(0),
432  pdf->defaultPrintStyle(0), "[LoadShapes] ");
433  }
434  // Post-condition #1
435  entry->set_pdf(pdf);
436  } else { // Pre-condition #3
437  if (flags_.at("allow-missing-shapes")) {
438  LOGLINE(log(), "Warning, shape missing:");
439  log() << Process::PrintHeader << *entry << "\n";
440  } else {
441  throw std::runtime_error(FNERROR("RooAbsPdf not found in workspace"));
442  }
443  }
444 
445  HistMapping norm_mapping = mapping;
446  if (!data && norm_mapping.syst_pattern != "") {
447  // For when we're not parsing a datacard, syst_pattern is being using to
448  // note a different mapping for the normalisation term
449  norm_mapping.pattern = norm_mapping.syst_pattern;
450  boost::replace_all(norm_mapping.pattern, "$CHANNEL", entry->bin());
451  boost::replace_all(norm_mapping.pattern, "$BIN", entry->bin());
452  boost::replace_all(norm_mapping.pattern, "$PROCESS", entry->process());
453  boost::replace_all(norm_mapping.pattern, "$MASS", entry->mass());
454  } else {
455  norm_mapping.pattern += "_norm";
456  }
457  RooAbsReal* norm =
458  norm_mapping.ws->function(norm_mapping.WorkspaceObj().c_str());
459  if (norm) {
460  // Post-condition #3
461  entry->set_norm(norm);
462  if (verbosity_ >= 2) {
463  LOGLINE(log(), "Normalisation RooAbsReal found");
464  norm->printStream(log(), norm->defaultPrintContents(0),
465  norm->defaultPrintStyle(0), "[LoadShapes] ");
466  }
467  // If we can upcast norm to a RooRealVar then we can interpret
468  // it as a free parameter that should be added to the list
469  RooRealVar* norm_var = dynamic_cast<RooRealVar*>(norm);
470  if (norm_var) {
471  RooArgSet tmp_set(*norm_var);
472  ImportParameters(&tmp_set);
473  }
474  }
475 
476  // Post-condition #4
477  // Import any paramters of the RooAbsPdf and the RooRealVar
478  RooAbsData const* data_obj = FindMatchingData(entry);
479  if (data_obj) {
480  if (verbosity_ >= 2) LOGLINE(log(), "Matching RooAbsData has been found");
481  if (pdf&&!data) {
482  RooArgSet argset = ParametersByName(pdf, data_obj->get());
483  ImportParameters(&argset);
484  if (!entry->observable()) {
485  std::string var_name;
486  if (data_obj) var_name = data_obj->get()->first()->GetName();
487  entry->set_observable(
488  (RooRealVar*)entry->pdf()->findServer(var_name.c_str()));
489  }
490  }
491  if (norm) {
492  RooArgSet argset = ParametersByName(norm, data_obj->get());
493  ImportParameters(&argset);
494  }
495  } else {
496  if (verbosity_ >= 2)
497  LOGLINE(log(), "No RooAbsData found, assume observable CMS_th1x");
498  RooRealVar mx("CMS_th1x" , "CMS_th1x", 0, 1);
499  RooArgSet tmp_set(mx);
500  if (pdf) {
501  RooArgSet argset = ParametersByName(pdf, &tmp_set);
502  ImportParameters(&argset);
503  }
504  if (norm) {
505  RooArgSet argset = ParametersByName(norm, &tmp_set);
506  ImportParameters(&argset);
507  }
508  }
509  }
510 }
511 
512 void CombineHarvester::LoadShapes(Systematic* entry,
513  std::vector<HistMapping> const& mappings) {
514  if (entry->shape_u() || entry->shape_d() ||
515  entry->data_u() || entry->data_d()) {
516  throw std::runtime_error(FNERROR("Systematic already contains a shape"));
517  }
518 
519  if (verbosity_ >= 2) {
520  LOGLINE(log(), "Extracting shapes for Systematic:");
521  log() << Systematic::PrintHeader << *entry << "\n";
522  LOGLINE(log(), "Mappings:");
523  for (HistMapping const& m : mappings) log() << m << "\n";
524  }
525 
526  // Pre-condition #2
527  // ResolveMapping will throw if this fails
528  HistMapping mapping =
529  ResolveMapping(entry->process(), entry->bin(), mappings);
530  boost::replace_all(mapping.pattern, "$CHANNEL", entry->bin());
531  boost::replace_all(mapping.pattern, "$BIN", entry->bin());
532  boost::replace_all(mapping.pattern, "$PROCESS", entry->process());
533  boost::replace_all(mapping.pattern, "$MASS", entry->mass());
534  std::string p_s =
535  mapping.IsPdf() ? mapping.SystWorkspaceObj() : mapping.syst_pattern;
536  boost::replace_all(p_s, "$CHANNEL", entry->bin());
537  boost::replace_all(p_s, "$BIN", entry->bin());
538  boost::replace_all(p_s, "$PROCESS", entry->process());
539  boost::replace_all(p_s, "$MASS", entry->mass());
540  std::string p_s_hi = p_s;
541  std::string p_s_lo = p_s;
542  boost::replace_all(p_s_hi, "$SYSTEMATIC", entry->name() + "Up");
543  boost::replace_all(p_s_lo, "$SYSTEMATIC", entry->name() + "Down");
544  if (mapping.IsHist()) {
545  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is TH1");
546  std::unique_ptr<TH1> h = GetClonedTH1(mapping.file.get(), mapping.pattern);
547  std::unique_ptr<TH1> h_u = GetClonedTH1(mapping.file.get(), p_s_hi);
548  std::unique_ptr<TH1> h_d = GetClonedTH1(mapping.file.get(), p_s_lo);
549 
550  if (flags_.at("check-negative-bins-on-import")) {
551  if (HasNegativeBins(h.get())) {
552  LOGLINE(log(), "Warning: Systematic shape has negative bins");
553  log() << Systematic::PrintHeader << *entry << "\n";
554  if (flags_.at("zero-negative-bins-on-import")) {
555  ZeroNegativeBins(h.get());
556  }
557  }
558 
559  if (HasNegativeBins(h_u.get())) {
560  LOGLINE(log(), "Warning: Systematic shape_u has negative bins");
561  log() << Systematic::PrintHeader << *entry << "\n";
562  if (flags_.at("zero-negative-bins-on-import")) {
563  ZeroNegativeBins(h_u.get());
564  }
565  }
566 
567  if (HasNegativeBins(h_d.get())) {
568  LOGLINE(log(), "Warning: Systematic shape_d has negative bins");
569  log() << Systematic::PrintHeader << *entry << "\n";
570  if (flags_.at("zero-negative-bins-on-import")) {
571  ZeroNegativeBins(h_d.get());
572  }
573  }
574  }
575  entry->set_shapes(std::move(h_u), std::move(h_d), h.get());
576  } else if (mapping.IsPdf()) {
577  if (verbosity_ >= 2) LOGLINE(log(), "Mapping type is RooDataHist");
578  // Try and get this as RooAbsData first. If this doesn't work try pdf
579 
580  RooDataHist* h =
581  (mapping.sys_ws) ? dynamic_cast<RooDataHist*>(mapping.sys_ws->data(mapping.WorkspaceObj().c_str())) : nullptr;
582  RooDataHist* h_u =
583  (mapping.sys_ws) ? dynamic_cast<RooDataHist*>(mapping.sys_ws->data(p_s_hi.c_str())): nullptr;
584  RooDataHist* h_d =
585  (mapping.sys_ws) ? dynamic_cast<RooDataHist*>(mapping.sys_ws->data(p_s_lo.c_str())): nullptr;
586  RooAbsReal* pdf = (mapping.sys_ws) ? mapping.sys_ws->function(mapping.WorkspaceObj().c_str()):nullptr;
587  RooAbsReal* pdf_u = (mapping.sys_ws) ? mapping.sys_ws->function(p_s_hi.c_str()):nullptr;
588  RooAbsReal* pdf_d = (mapping.sys_ws) ? mapping.sys_ws->function(p_s_lo.c_str()):nullptr;
589 
590  if ((!h || !h_u || !h_d) && (!pdf || !pdf_u || !pdf_d)) {
591  if (flags_.at("allow-missing-shapes")) {
592  LOGLINE(log(), "Warning, shape missing:");
593  log() << Systematic::PrintHeader << *entry << "\n";
594  } else {
595  throw std::runtime_error(
596  FNERROR("All shapes must be of type RooDataHist"));
597  }
598  } else if (h && h_u && h_d){
599  entry->set_data(h_u, h_d, h);
600  } else if (pdf && pdf_u &&pdf_d){
601  entry->set_pdf(pdf_u,pdf_d,pdf);
602  }
603  } else {
604  throw std::runtime_error(
605  FNERROR("Resolved mapping is not of TH1 / RooAbsData type"));
606  }
607 }
608 
628 HistMapping const& CombineHarvester::ResolveMapping(
629  std::string const& process, std::string const& bin,
630  std::vector<HistMapping> const& mappings) {
631  StrPairVec attempts = GenerateShapeMapAttempts(process, bin);
632  for (unsigned a = 0; a < attempts.size(); ++a) {
633  for (unsigned m = 0; m < mappings.size(); ++m) {
634  if ((attempts[a].first == mappings[m].process) &&
635  (attempts[a].second == mappings[m].category)) {
636  return mappings[m];
637  }
638  }
639  }
640  // If we get this far then we didn't find a valid mapping
641  FNLOG(log()) << "Searching for mapping for (" << bin << "," << process << ")\n";
642  FNLOG(log()) << "Avaiable mappings:\n";
643  for (auto const& m : mappings) {
644  FNLOG(log()) << m << "\n";
645  }
646  throw std::runtime_error(FNERROR("Valid mapping not found"));
647 }
648 
649 CombineHarvester::StrPairVec CombineHarvester::GenerateShapeMapAttempts(
650  std::string process, std::string category) {
651  CombineHarvester::StrPairVec result;
652  result.push_back(std::make_pair(process , category));
653  result.push_back(std::make_pair("*" , category));
654  result.push_back(std::make_pair(process , "*"));
655  result.push_back(std::make_pair("*" , "*"));
656  return result;
657 }
658 
659 std::shared_ptr<RooWorkspace> CombineHarvester::SetupWorkspace(
660  RooWorkspace const& ws, bool can_rename) {
661  bool name_in_use = false;
662 
663  // 1) Does the UUID of this ws match any other ws in the map?
664  for (auto const& it : wspaces_) {
665  // - Yes: just return a ptr to the matched ws
666  if (it.second->uuid() == ws.uuid() && flags_["workspace-uuid-recycle"]) {
667  FNLOGC(log(), verbosity_ >= 1)
668  << "Workspace with name " << it.second->GetName()
669  << " has the same UUID, will use this one\n";
670  return it.second;
671  }
672  if (!name_in_use && strcmp(it.second->GetName(), ws.GetName()) == 0) {
673  name_in_use = true;
674  }
675  }
676 
677  // 2) No: Ok will clone it in. Is the ws name already in use?
678  if (!name_in_use) {
679  // - No: clone with same name and return
680  // IMPORTANT: Don't used RooWorkspace::Clone(), it seems to introduce
681  // bugs
682  if (GetFlag("workspaces-use-clone")) {
683  wspaces_[std::string(ws.GetName())] = std::shared_ptr<RooWorkspace>(
684  reinterpret_cast<RooWorkspace*>(ws.Clone(ws.GetName()))
685  );
686  } else {
687  wspaces_[std::string(ws.GetName())] =
688  std::make_shared<RooWorkspace>(RooWorkspace(ws));
689  }
690  return wspaces_.at(ws.GetName());
691  }
692 
693  // 3) Am I allowed to rename (default no)?
694  if (!can_rename) {
695  // - No: throw runtime error
696  throw std::runtime_error(FNERROR("A different workspace with name " +
697  std::string(ws.GetName()) +
698  " already exists"));
699  }
700 
701  // 4) Yes: determine first available nameX, clone, return
702  std::set<int> used_ints = {0};
703  std::string src_name(ws.GetName());
704  for (auto const& it : wspaces_) {
705  std::string test_name(it.second->GetName());
706  if (test_name.find(src_name) == 0) {
707  std::string postfix = test_name.substr(src_name.size());
708  try {
709  int number = boost::lexical_cast<int>(postfix);
710  used_ints.insert(number);
711  } catch (boost::bad_lexical_cast & e) {
712  }
713  }
714  }
715  std::string new_name =
716  src_name + boost::lexical_cast<std::string>(*(used_ints.rbegin()) + 1);
717  FNLOGC(log(), verbosity_ >= 1) << "Workspace with name " << src_name
718  << " already defined, renaming to " << new_name
719  << "\n";
720 
721  std::shared_ptr<RooWorkspace> new_wsp;
722  if (GetFlag("workspaces-use-clone")) {
723  new_wsp = std::shared_ptr<RooWorkspace>(
724  reinterpret_cast<RooWorkspace*>(ws.Clone(new_name.c_str()))
725  );
726  } else {
727  new_wsp = std::make_shared<RooWorkspace>(RooWorkspace(ws));
728  }
729  new_wsp->SetName(new_name.c_str());
730  wspaces_[new_name] = new_wsp;
731  return wspaces_.at(new_name);
732 }
733 
734 void CombineHarvester::ImportParameters(RooArgSet *vars) {
735  for (RooAbsArg *x : *vars) {
736  RooRealVar *y = dynamic_cast<RooRealVar*>(x);
737  if (y) {
738  if (!params_.count(y->GetName())) {
739  if (verbosity_ >= 1) {
740  log() << "[ImportParameters] Creating parameter from RooRealVar:\n";
741  y->printStream(log(), y->defaultPrintContents(0),
742  y->defaultPrintStyle(0), "[ImportParameters] ");
743  }
744  Parameter par;
745  par.set_name(y->GetName());
746  par.set_val(y->getVal());
747  if ((y->hasError() || y->hasAsymError()) &&
748  flags_["import-parameter-err"]) {
749  par.set_err_d(y->getErrorLo());
750  par.set_err_u(y->getErrorHi());
751  } else {
752  par.set_err_d(0.);
753  par.set_err_u(0.);
754  }
755  params_[y->GetName()] = std::make_shared<Parameter>(par);
756  } else {
757  if (verbosity_ >= 1)
758  log() << "[ImportParameters] Parameter \"" << y->GetName()
759  << "\" already exists\n";
760  }
761  Parameter *param = params_[y->GetName()].get();
762  std::vector<RooRealVar *> & p_vars = param->vars();
763  if (std::find(p_vars.begin(), p_vars.end(), y) == p_vars.end()) {
764  p_vars.push_back(y);
765  if (verbosity_ >= 1)
766  log() << "[ImportParameters] Parameter now stores " << p_vars.size()
767  << " link(s) to RooRealVar objects\n";
768  } else {
769  if (verbosity_ >= 1)
770  log() << "[ImportParameters] Parameter already stores link to "
771  "RooRealVar object\n";
772  }
773  }
774  }
775 }
776 
777 RooAbsData const* CombineHarvester::FindMatchingData(Process const* proc) {
778  RooAbsData const* data_obj = nullptr;
779  for (unsigned i = 0; i < obs_.size(); ++i) {
780  if (proc->bin() == obs_[i]->bin() &&
781  proc->bin_id() == obs_[i]->bin_id()) {
782  data_obj = obs_[i]->data();
783  }
784  }
785  return data_obj;
786 }
787 
788 ch::Parameter* CombineHarvester::SetupRateParamVar(std::string const& name,
789  double val, bool is_ext_arg) {
790  RooWorkspace *ws = nullptr;
791  if (!wspaces_.count("_rateParams")) {
792  ws = this->SetupWorkspace(RooWorkspace("_rateParams","_rateParams")).get();
793  } else {
794  ws = wspaces_.at("_rateParams").get();
795  }
796  // Parameter doesn't exist in the workspace - let's create it
797  RooRealVar *var = ws->var(name.c_str());
798  if (!var) {
799  // The value doesn't matter, our Parameter object will set it later
800  RooRealVar tmp_var(name.c_str(), name.c_str(), 0);
801  ws->import(tmp_var);
802  var = ws->var(name.c_str());
803  FNLOGC(log(), verbosity_ > 1)
804  << "Created new RooRealVar for rateParam: " << name << "\n";
805  if (verbosity_ > 1) var->Print();
806  } else {
807  FNLOGC(log(), verbosity_ > 1)
808  << "Reusing existing RooRealVar for rateParam: " << name << "\n";
809  }
810  if (is_ext_arg) var->setAttribute("extArg");
811  Parameter * param = nullptr;
812  if (!params_.count(name)) {
813  params_[name] = std::make_shared<Parameter>(Parameter());
814  param = params_.at(name).get();
815  param->set_name(name);
816  param->set_err_u(0.);
817  param->set_err_d(0.);
818  } else {
819  param = params_.at(name).get();
820  }
821  // If the RooRealVar in the workpsace isn't in the list, add it
822  bool var_in_par = false;
823  for (auto const& ptr : param->vars()) {
824  if (ptr == var) {
825  var_in_par = true;
826  break;
827  }
828  }
829  if (!var_in_par) {
830  param->vars().push_back(var);
831  }
832  // Then this propagates the value to the RooRealVar
833  FNLOGC(log(), verbosity_ > 1) << "Updating parameter value from "
834  << param->val();
835  param->set_val(val);
836  if (verbosity_ > 1) log() << " to " << param->val() << "\n";
837  return param;
838 }
839 
840 void CombineHarvester::SetupRateParamFunc(std::string const& name,
841  std::string const& formula,
842  std::string const& pars) {
843  RooWorkspace *ws = nullptr;
844  if (!wspaces_.count("_rateParams")) {
845  ws = this->SetupWorkspace(RooWorkspace("_rateParams","_rateParams")).get();
846  } else {
847  ws = wspaces_.at("_rateParams").get();
848  }
849  RooAbsReal *form = ws->function(name.c_str());
850  // No parameter to make, this is a formula
851  if (!form) {
852  RooFormulaVar formularvar(name.c_str(), name.c_str(),
853  formula.c_str(),
854  RooArgList(ws->argSet(pars.c_str())));
855  ws->import(formularvar);
856  form = ws->function(name.c_str());
857  FNLOGC(log(), verbosity_ > 1)
858  << "Created new RooFormulaVar for rateParam: " << name << "\n";
859  if (verbosity_ > 1) form->Print();
860  }
861 }
862 
863 void CombineHarvester::SetupRateParamWspObj(std::string const& name,
864  std::string const& obj, bool is_ext_arg) {
865  RooWorkspace *ws = nullptr;
866  if (!wspaces_.count("_rateParams")) {
867  ws = this->SetupWorkspace(RooWorkspace("_rateParams","_rateParams")).get();
868  } else {
869  ws = wspaces_.at("_rateParams").get();
870  }
871  ws->import((obj+":"+name).c_str(), RooFit::RecycleConflictNodes());
872  ws->arg(name.c_str())->setStringAttribute("wspSource", obj.c_str());
873  if (is_ext_arg) ws->arg(name.c_str())->setAttribute("extArg");
874 }
875 
876 bool CombineHarvester::SetupRateParamWspObjFromWsStore(std::string const& name, std::string const& obj,
877  std::map<std::string, std::shared_ptr<RooWorkspace>> const& ws_store) {
878  bool found_arg_in_ws = false;
879  RooWorkspace *ws = nullptr;
880  if (!wspaces_.count("_rateParams")) {
881  ws = this->SetupWorkspace(RooWorkspace("_rateParams","_rateParams")).get();
882  } else {
883  ws = wspaces_.at("_rateParams").get();
884  }
885  std::vector<std::string> tokens;
886  boost::split(tokens, obj, boost::is_any_of(":"));
887 
888  std::string file_name_extArg = tokens[0];
889  std::string wsp_name_extArg = tokens[1];
890 
891  RooAbsArg* warg = nullptr;
892  for (auto const& it : ws_store) {
893  if (boost::ends_with(it.first, file_name_extArg+wsp_name_extArg)){
894  warg = it.second->arg(name.c_str()) ;
895  break;
896  }
897  }
898 
899  if(warg && !ws->arg(name.c_str())) {
900  ws->import(*warg, RooFit::RecycleConflictNodes());
901  ws->arg(name.c_str())->setStringAttribute("wspSource", obj.c_str());
902  ws->arg(name.c_str())->setAttribute("extArg");
903  found_arg_in_ws = true;
904  }
905  return found_arg_in_ws;
906 }
907 
908 void CombineHarvester::SetAutoMCStats(CombineHarvester &target, double thresh, bool sig, int mode) {
909  for (auto const& bin : this->bin_set()) {
910  target.auto_stats_settings_[bin] = AutoMCStatsSettings(thresh, sig, mode);
911  }
912 }
913 
914 void CombineHarvester::RenameAutoMCStatsBin(std::string const& oldname, std::string const& newname) {
915  auto it = auto_stats_settings_.find(oldname);
916  if (it != auto_stats_settings_.end()) {
917  auto_stats_settings_[newname] = it->second;
918  auto_stats_settings_.erase(it);
919  }
920 }
921 
922 std::set<std::string> CombineHarvester::GetAutoMCStatsBins() const {
923  std::set<std::string> result;
924  for (auto const& it : auto_stats_settings_) {
925  result.insert(it.first);
926  }
927  return result;
928 }
929 
930 void CombineHarvester::AddExtArgValue(std::string const& name, double const& value) {
931  ch::Parameter* param = SetupRateParamVar(name, value, true);
932  param->set_err_u(0.);
933  param->set_err_d(0.);
934 }
935 
936 double CombineHarvester::getParFromWs(const std::string name){
937  double r=0.; bool found=false;
938  for (auto & item : wspaces_) {
939  if (item.second.get()->var(name.c_str())) {
940  if (found) std::cout<<"WARNING-DUPLICATE: ALREADY FOUND "<<name<<"in an other ws"<<r<<std::endl;
941  r=item.second.get()->var(name.c_str())->getVal();
942  found=true;
943  }
944  }
945  return r;
946  }
947 
948 void CombineHarvester::setParInWs(const std::string name,double value) {
949  for (auto & item : wspaces_) {
950  if (item.second.get()->var(name.c_str())){
951  if ( item.second.get()->var(name.c_str())->getMin() >value) item.second.get()->var(name.c_str())->setMin(value-0.001);
952  if ( item.second.get()->var(name.c_str())->getMax() <value) item.second.get()->var(name.c_str())->setMax(value+0.001);
953  item.second.get()->var(name.c_str())->setVal(value);
954  }
955  }
956  }
957 
958 void CombineHarvester::renameParInWs(const std::string& name, const std::string& newName, const std::string& wsName)
959 {
960  for (auto & item : wspaces_) {
961  if ( (wsName=="" or item.first == wsName ) and item.second.get()->var(name.c_str())){
962  item.second.get()->var(name.c_str())->SetName(newName.c_str());
963  }
964  }
965 }
966 
967 }
#define FNLOGC(x, y)
Definition: Logging.h:14
#define LOGLINE(x, y)
Definition: Logging.h:11
#define FNERROR(x)
Definition: Logging.h:9
#define FNLOG(x)
Definition: Logging.h:13
CombineHarvester & PrintObs()
CombineHarvester & process(std::vector< std::string > const &vec, bool cond=true)
void RenameAutoMCStatsBin(std::string const &oldname, std::string const &newname)
void renameParInWs(const std::string &name, const std::string &newName, const std::string &wsName="")
void SetFlag(std::string const &flag, bool const &value)
Set a named flag value.
CombineHarvester & operator=(CombineHarvester other)
CombineHarvester & PrintAll()
friend void swap(CombineHarvester &first, CombineHarvester &second)
CombineHarvester & bin(std::vector< std::string > const &vec, bool cond=true)
void SetAutoMCStats(CombineHarvester &target, double thresh, bool sig=false, int mode=1)
std::set< std::string > bin_set()
bool GetFlag(std::string const &flag) const
CombineHarvester & PrintParams()
std::set< std::string > GetAutoMCStatsBins() const
CombineHarvester & PrintProcs()
CombineHarvester deep()
Creates and retunrs a deep copy of the CombineHarvester instance.
CombineHarvester & PrintSysts()
double getParFromWs(const std::string name)
CombineHarvester cp()
Creates and returns a shallow copy of the CombineHarvester instance.
void AddExtArgValue(std::string const &name, double const &value)
void setParInWs(const std::string name, double value)
TH1 const * shape() const
Definition: Observation.h:27
static std::ostream & PrintHeader(std::ostream &out)
Definition: Observation.cc:128
RooAbsData const * data() const
Definition: Observation.h:35
void set_err_u(double const &err_u)
Definition: Parameter.h:33
void set_err_d(double const &err_d)
Definition: Parameter.h:36
static std::ostream & PrintHeader(std::ostream &out)
Definition: Parameter.cc:62
static std::ostream & PrintHeader(std::ostream &out)
Definition: Process.cc:153
static std::ostream & PrintHeader(std::ostream &out)
Definition: Systematic.cc:251
Definition: Algorithm.h:10
bool HasNegativeBins(TH1 const *h)
Definition: Utilities.cc:339
void swap(CombineHarvester &first, CombineHarvester &second)
std::unique_ptr< TH1 > GetClonedTH1(TFile *file, std::string const &path)
Definition: TFileIO.cc:12
void ZeroNegativeBins(TH1 *h)
Definition: Utilities.cc:349
RooArgSet ParametersByName(RooAbsReal const *pdf, RooArgSet const *dat_vars)
Definition: Utilities.cc:18