CombineHarvester
CombineHarvester_Evaluate.cc
Go to the documentation of this file.
2 #include <vector>
3 #include <map>
4 #include <string>
5 #include <iostream>
6 #include <utility>
7 #include <set>
8 #include <fstream>
9 #include "boost/lexical_cast.hpp"
10 #include "boost/algorithm/string.hpp"
11 #include "boost/range/algorithm_ext/erase.hpp"
12 #include "boost/range/algorithm/find.hpp"
13 #include "boost/format.hpp"
14 #include "TDirectory.h"
15 #include "TH1.h"
16 #include "TH2.h"
24 
25 // #include "TMath.h"
26 // #include "boost/format.hpp"
27 // #include "Utilities/interface/FnPredicates.h"
28 // #include "Math/QuantFuncMathCore.h"
29 
30 namespace ch {
31 
32 CombineHarvester::ProcSystMap CombineHarvester::GenerateProcSystMap() {
33  ProcSystMap lookup(procs_.size());
34  for (unsigned i = 0; i < systs_.size(); ++i) {
35  for (unsigned j = 0; j < procs_.size(); ++j) {
36  if (MatchingProcess(*(systs_[i]), *(procs_[j]))) {
37  lookup[j].push_back(systs_[i].get());
38  }
39  }
40  }
41  return lookup;
42 }
43 
45  auto lookup = GenerateProcSystMap();
46  double err_sq = 0.0;
47  for (auto param_it : params_) {
48  double backup = param_it.second->val();
49  param_it.second->set_val(backup+param_it.second->err_d());
50  double rate_d = this->GetRateInternal(lookup, param_it.first);
51  param_it.second->set_val(backup+param_it.second->err_u());
52  double rate_u = this->GetRateInternal(lookup, param_it.first);
53  double err = std::fabs(rate_u-rate_d) / 2.0;
54  err_sq += err * err;
55  param_it.second->set_val(backup);
56  }
57  return std::sqrt(err_sq);
58 }
59 
60 double CombineHarvester::GetUncertainty(RooFitResult const* fit,
61  unsigned n_samples) {
62  return GetUncertainty(*fit, n_samples);
63 }
64 
65 double CombineHarvester::GetUncertainty(RooFitResult const& fit,
66  unsigned n_samples) {
67  auto lookup = GenerateProcSystMap();
68  double rate = GetRateInternal(lookup);
69  double err_sq = 0.0;
70 
71  // Create a backup copy of the current parameter values
72  auto backup = GetParameters();
73 
74  // Calling randomizePars() ensures that the RooArgList of sampled parameters
75  // is already created within the RooFitResult
76  RooArgList const& rands = fit.randomizePars();
77 
78  // Now create two aligned vectors of the RooRealVar parameters and the
79  // corresponding ch::Parameter pointers
80  int n_pars = rands.getSize();
81  std::vector<RooRealVar const*> r_vec(n_pars, nullptr);
82  std::vector<ch::Parameter*> p_vec(n_pars, nullptr);
83  for (unsigned n = 0; n < p_vec.size(); ++n) {
84  r_vec[n] = dynamic_cast<RooRealVar const*>(rands.at(n));
85  p_vec[n] = GetParameter(r_vec[n]->GetName());
86  }
87 
88  for (unsigned i = 0; i < n_samples; ++i) {
89  // Randomise and update values
90  fit.randomizePars();
91  for (int n = 0; n < n_pars; ++n) {
92  if (p_vec[n]) p_vec[n]->set_val(r_vec[n]->getVal());
93  }
94 
95  double rand_rate = this->GetRateInternal(lookup);
96  double err = std::fabs(rand_rate-rate);
97  err_sq += (err*err);
98  }
99  this->UpdateParameters(backup);
100  return std::sqrt(err_sq/double(n_samples));
101 }
102 
104  auto lookup = GenerateProcSystMap();
105  TH1F shape = GetShape();
106  for (int i = 1; i <= shape.GetNbinsX(); ++i) {
107  shape.SetBinError(i, 0.0);
108  }
109  for (auto param_it : params_) {
110  double backup = param_it.second->val();
111  param_it.second->set_val(backup+param_it.second->err_d());
112  TH1F shape_d = this->GetShapeInternal(lookup, param_it.first);
113  param_it.second->set_val(backup+param_it.second->err_u());
114  TH1F shape_u = this->GetShapeInternal(lookup, param_it.first);
115  for (int i = 1; i <= shape.GetNbinsX(); ++i) {
116  double err =
117  std::fabs(shape_u.GetBinContent(i) - shape_d.GetBinContent(i)) / 2.0;
118  shape.SetBinError(i, err*err + shape.GetBinError(i));
119  }
120  param_it.second->set_val(backup);
121  }
122  for (int i = 1; i <= shape.GetNbinsX(); ++i) {
123  shape.SetBinError(i, std::sqrt(shape.GetBinError(i)));
124  }
125  return shape;
126 }
127 
128 TH1F CombineHarvester::GetShapeWithUncertainty(RooFitResult const* fit,
129  unsigned n_samples) {
130  return GetShapeWithUncertainty(*fit, n_samples);
131 }
132 
133 TH1F CombineHarvester::GetShapeWithUncertainty(RooFitResult const& fit,
134  unsigned n_samples) {
135  auto lookup = GenerateProcSystMap();
136  TH1F shape = GetShapeInternal(lookup);
137  for (int i = 1; i <= shape.GetNbinsX(); ++i) {
138  shape.SetBinError(i, 0.0);
139  }
140  // Create a backup copy of the current parameter values
141  auto backup = GetParameters();
142 
143  // Calling randomizePars() ensures that the RooArgList of sampled parameters
144  // is already created within the RooFitResult
145  RooArgList const& rands = fit.randomizePars();
146 
147  // Now create two aligned vectors of the RooRealVar parameters and the
148  // corresponding ch::Parameter pointers
149  int n_pars = rands.getSize();
150  std::vector<RooRealVar const*> r_vec(n_pars, nullptr);
151  std::vector<ch::Parameter*> p_vec(n_pars, nullptr);
152  for (unsigned n = 0; n < p_vec.size(); ++n) {
153  r_vec[n] = dynamic_cast<RooRealVar const*>(rands.at(n));
154  p_vec[n] = GetParameter(r_vec[n]->GetName());
155  }
156 
157  // Main loop through n_samples
158  for (unsigned i = 0; i < n_samples; ++i) {
159  // Randomise and update values
160  fit.randomizePars();
161  for (int n = 0; n < n_pars; ++n) {
162  if (p_vec[n]) p_vec[n]->set_val(r_vec[n]->getVal());
163  }
164 
165  TH1F rand_shape = this->GetShapeInternal(lookup);
166  for (int i = 1; i <= shape.GetNbinsX(); ++i) {
167  double err =
168  std::fabs(rand_shape.GetBinContent(i) - shape.GetBinContent(i));
169  shape.SetBinError(i, err*err + shape.GetBinError(i));
170  }
171  }
172  for (int i = 1; i <= shape.GetNbinsX(); ++i) {
173  shape.SetBinError(i, std::sqrt(shape.GetBinError(i)/double(n_samples)));
174  }
175  this->UpdateParameters(backup);
176  return shape;
177 }
178 
179 TH2F CombineHarvester::GetRateCovariance(RooFitResult const& fit,
180  unsigned n_samples) {
181  auto lookup = GenerateProcSystMap();
182 
183  unsigned n = procs_.size();
184  TH1F nom("nominal", "nominal", n, 0, n);
185  TH2F res("covariance", "covariance", n, 0, n, n, 0, n);
186 
187  std::vector<CombineHarvester> ch_procs;
188  std::vector<std::string> labels;
189  unsigned nbins = this->bin_set().size();
190 
191  for (unsigned i = 0; i < procs_.size(); ++i) {
192  ch_procs.push_back(
193  this->cp().bin({procs_[i]->bin()}).process({procs_[i]->process()}));
194  if (nbins > 1) {
195  labels.push_back(procs_[i]->bin() + "," + procs_[i]->process());
196  } else {
197  labels.push_back(procs_[i]->process());
198  }
199  }
200  for (unsigned i = 0; i < procs_.size(); ++i) {
201  nom.SetBinContent(i + 1, ch_procs[i].GetRate());
202  }
203  auto backup = GetParameters();
204 
205  // Calling randomizePars() ensures that the RooArgList of sampled parameters
206  // is already created within the RooFitResult
207  RooArgList const& rands = fit.randomizePars();
208 
209  // Now create two aligned vectors of the RooRealVar parameters and the
210  // corresponding ch::Parameter pointers
211  int n_pars = rands.getSize();
212  std::vector<RooRealVar const*> r_vec(n_pars, nullptr);
213  std::vector<ch::Parameter*> p_vec(n_pars, nullptr);
214  for (unsigned n = 0; n < p_vec.size(); ++n) {
215  r_vec[n] = dynamic_cast<RooRealVar const*>(rands.at(n));
216  p_vec[n] = GetParameter(r_vec[n]->GetName());
217  }
218 
219  // Main loop through n_samples
220  for (unsigned rnd = 0; rnd < n_samples; ++rnd) {
221  // Randomise and update values
222  fit.randomizePars();
223  for (int n = 0; n < n_pars; ++n) {
224  if (p_vec[n]) p_vec[n]->set_val(r_vec[n]->getVal());
225  }
226 
227  for (int i = 1; i <= nom.GetNbinsX(); ++i) {
228  for (int j = 1; j <= nom.GetNbinsX(); ++j) {
229  int x = j;
230  int y = nom.GetNbinsX() - (i - 1);
231  res.GetXaxis()->SetBinLabel(x, labels[j - 1].c_str());
232  res.GetYaxis()->SetBinLabel(y, labels[i - 1].c_str());
233  res.SetBinContent(
234  x, y, res.GetBinContent(x, y) +
235  (ch_procs[i - 1].GetRate() - nom.GetBinContent(i)) *
236  (ch_procs[j - 1].GetRate() - nom.GetBinContent(j)));
237  }
238  }
239  }
240 
241  for (int i = 1; i <= nom.GetNbinsX(); ++i) {
242  for (int j = 1; j <= nom.GetNbinsX(); ++j) {
243  int x = j;
244  int y = nom.GetNbinsX() - (i - 1);
245  res.SetBinContent(x, y, res.GetBinContent(x, y) / double(n_samples));
246  }
247  }
248  this->UpdateParameters(backup);
249  return res;
250 }
251 
252 TH2F CombineHarvester::GetRateCorrelation(RooFitResult const& fit,
253  unsigned n_samples) {
254  TH2F cov = GetRateCovariance(fit, n_samples);
255  TH2F res = cov;
256  res.SetName("correlation");
257  res.SetTitle("correlation");
258  for (int i = 1; i <= cov.GetNbinsX(); ++i) {
259  for (int j = 1; j <= cov.GetNbinsX(); ++j) {
260  int x = j;
261  int y = cov.GetNbinsX() - (i - 1);
262  res.SetBinContent(
263  x, y,
264  cov.GetBinContent(x, y) /
265  (std::sqrt(cov.GetBinContent(i, cov.GetNbinsX() - (i - 1))) *
266  std::sqrt(cov.GetBinContent(j, cov.GetNbinsX() - (j - 1)))));
267  }
268  }
269  return res;
270 }
271 
273  auto lookup = GenerateProcSystMap();
274  return GetRateInternal(lookup);
275 }
276 
278  auto lookup = GenerateProcSystMap();
279  return GetShapeInternal(lookup);
280 }
281 
282 double CombineHarvester::GetRateInternal(ProcSystMap const& lookup,
283  std::string const& single_sys) {
284  double rate = 0.0;
285  // TH1F tot_shape
286  for (unsigned i = 0; i < procs_.size(); ++i) {
287  double p_rate = procs_[i]->rate();
288  // If we are evaluating the effect of a single parameter
289  // check the list of associated nuisances and skip if
290  // this "single_sys" is not in the list
291  // However - we can't skip if the process has a pdf, as
292  // we haven't checked what the parameters are
293  if (single_sys != "" && !procs_[i]->pdf()) {
294  if (!ch::any_of(lookup[i], [&](Systematic const* sys) {
295  return sys->name() == single_sys;
296  })) continue;
297  }
298  for (auto sys_it : lookup[i]) {
299  if (sys_it->type() == "rateParam") {
300  continue; // don't evaluate this for now
301  }
302  double x = params_[sys_it->name()]->val();
303  if (sys_it->asymm()) {
304  p_rate *= logKappaForX(x * sys_it->scale(), sys_it->value_d(),
305  sys_it->value_u());
306  } else {
307  p_rate *= std::pow(sys_it->value_u(), x * sys_it->scale());
308  }
309  }
310  rate += p_rate;
311  }
312  return rate;
313 }
314 
315 TH1F CombineHarvester::GetShapeInternal(ProcSystMap const& lookup,
316  std::string const& single_sys) {
317  TH1F shape;
318  bool shape_init = false;
319 
320  for (unsigned i = 0; i < procs_.size(); ++i) {
321  // Might be able to skip if only interested in one nuisance
322  // However - we can't skip if the process has a pdf, as
323  // we haven't checked what the parameters are
324  if (single_sys != "" && !procs_[i]->pdf()) {
325  if (!ch::any_of(lookup[i], [&](Systematic const* sys) {
326  return sys->name() == single_sys;
327  })) continue;
328  }
329 
330  double p_rate = procs_[i]->rate();
331  if (procs_[i]->shape() || procs_[i]->data()) {
332  TH1F proc_shape = procs_[i]->ShapeAsTH1F();
333  for (auto sys_it : lookup[i]) {
334  if (sys_it->type() == "rateParam") {
335  continue; // don't evaluate this for now
336  }
337  auto param_it = params_.find(sys_it->name());
338  if (param_it == params_.end()) {
339  throw std::runtime_error(
340  FNERROR("Parameter " + sys_it->name() +
341  " not found in CombineHarvester instance"));
342  }
343  double x = param_it->second->val();
344  if (sys_it->asymm()) {
345  p_rate *= logKappaForX(x * sys_it->scale(), sys_it->value_d(),
346  sys_it->value_u());
347  if (sys_it->type() == "shape" || sys_it->type() == "shapeN2" ||
348  sys_it->type() == "shapeU") {
349  bool linear = true;
350  if (sys_it->type() == "shapeN2") linear = false;
351  if (sys_it->shape_u() && sys_it->shape_d()) {
352  ShapeDiff(x * sys_it->scale(), &proc_shape, procs_[i]->shape(),
353  sys_it->shape_d(), sys_it->shape_u(), linear);
354  }
355  if (sys_it->data_u() && sys_it->data_d()) {
356  RooDataHist const* nom =
357  dynamic_cast<RooDataHist const*>(procs_[i]->data());
358  if (nom) {
359  ShapeDiff(x * sys_it->scale(), &proc_shape, nom,
360  sys_it->data_d(), sys_it->data_u());
361  }
362  }
363  }
364  } else {
365  p_rate *= std::pow(sys_it->value_u(), x * sys_it->scale());
366  }
367  }
368  for (int b = 1; b <= proc_shape.GetNbinsX(); ++b) {
369  if (proc_shape.GetBinContent(b) < 0.) proc_shape.SetBinContent(b, 0.);
370  }
371  proc_shape.Scale(p_rate);
372  if (!shape_init) {
373  proc_shape.Copy(shape);
374  shape.Reset();
375  shape_init = true;
376  }
377  shape.Add(&proc_shape);
378  } else if (procs_[i]->pdf()) {
379  if (!procs_[i]->observable()) {
380  RooAbsData const* data_obj = FindMatchingData(procs_[i].get());
381  std::string var_name = "CMS_th1x";
382  if (data_obj) var_name = data_obj->get()->first()->GetName();
383  procs_[i]->set_observable((RooRealVar *)procs_[i]->pdf()->findServer(var_name.c_str()));
384  }
385  TH1::AddDirectory(false);
386  TH1F* tmp = (TH1F*)procs_[i]->observable()->createHistogram("");
387  for (int b = 1; b <= tmp->GetNbinsX(); ++b) {
388  procs_[i]->observable()->setVal(tmp->GetBinCenter(b));
389  tmp->SetBinContent(b, tmp->GetBinWidth(b) * procs_[i]->pdf()->getVal());
390  }
391  TH1F proc_shape = *tmp;
392  delete tmp;
393  RooAbsPdf const* aspdf = dynamic_cast<RooAbsPdf const*>(procs_[i]->pdf());
394  if ((aspdf && !aspdf->selfNormalized()) || (!aspdf)) {
395  // LOGLINE(log(), "Have a pdf that is not selfNormalized");
396  // std::cout << "Integral: " << proc_shape.Integral() << "\n";
397  if (proc_shape.Integral() > 0.) {
398  proc_shape.Scale(1. / proc_shape.Integral());
399  }
400  }
401  for (auto sys_it : lookup[i]) {
402  if (sys_it->type() == "rateParam") {
403  continue; // don't evaluate this for now
404  }
405  double x = params_[sys_it->name()]->val();
406  if (sys_it->asymm()) {
407  p_rate *= logKappaForX(x * sys_it->scale(), sys_it->value_d(),
408  sys_it->value_u());
409  } else {
410  p_rate *= std::pow(sys_it->value_u(), x * sys_it->scale());
411  }
412  }
413  proc_shape.Scale(p_rate);
414  if (!shape_init) {
415  proc_shape.Copy(shape);
416  shape.Reset();
417  shape_init = true;
418  }
419  shape.Add(&proc_shape);
420  }
421  }
422  return shape;
423 }
424 
426  double rate = 0.0;
427  for (unsigned i = 0; i < obs_.size(); ++i) {
428  rate += obs_[i]->rate();
429  }
430  return rate;
431 }
432 
434  TH1F shape;
435  bool shape_init = false;
436 
437  for (unsigned i = 0; i < obs_.size(); ++i) {
438  TH1F proc_shape;
439  double p_rate = obs_[i]->rate();
440  if (obs_[i]->shape()) {
441  proc_shape = obs_[i]->ShapeAsTH1F();
442  } else if (obs_[i]->data()) {
443  TH1F* tmp = dynamic_cast<TH1F*>(obs_[i]->data()->createHistogram(
444  "", *(RooRealVar*)obs_[i]->data()->get()->first()));
445  tmp->Sumw2(false);
446  tmp->SetBinErrorOption(TH1::kPoisson);
447  proc_shape = *tmp;
448  delete tmp;
449  proc_shape.Scale(1. / proc_shape.Integral());
450  }
451  proc_shape.Scale(p_rate);
452  if (!shape_init) {
453  proc_shape.Copy(shape);
454  shape.Reset();
455  shape_init = true;
456  }
457  shape.Add(&proc_shape);
458  }
459  return shape;
460 }
461 
462 void CombineHarvester::ShapeDiff(double x,
463  TH1F * target,
464  TH1 const* nom,
465  TH1 const* low,
466  TH1 const* high,
467  bool linear) {
468  double fx = smoothStepFunc(x);
469  for (int i = 1; i <= target->GetNbinsX(); ++i) {
470  float h = high->GetBinContent(i);
471  float l = low->GetBinContent(i);
472  float n = nom->GetBinContent(i);
473  if (!linear) {
474  float t = target->GetBinContent(i);
475  target->SetBinContent(i, t > 0. ? std::log(t) : -999.);
476  h = (h > 0. && n > 0.) ? std::log(h/n) : 0.;
477  l = (l > 0. && n > 0.) ? std::log(l/n) : 0.;
478  target->SetBinContent(i, target->GetBinContent(i) +
479  0.5 * x * ((h - l) + (h + l) * fx));
480  target->SetBinContent(i, std::exp(target->GetBinContent(i)));
481  } else {
482  target->SetBinContent(i, target->GetBinContent(i) +
483  0.5 * x * ((h - l) + (h + l - 2. * n) * fx));
484  }
485  }
486 }
487 
488 void CombineHarvester::ShapeDiff(double x,
489  TH1F * target,
490  RooDataHist const* nom,
491  RooDataHist const* low,
492  RooDataHist const* high) {
493  double fx = smoothStepFunc(x);
494  for (int i = 1; i <= target->GetNbinsX(); ++i) {
495  high->get(i-1);
496  low->get(i-1);
497  nom->get(i-1);
498  // The RooDataHists are not scaled to unity (unlike in the TH1 case above)
499  // so we have to normalise the bin contents on the fly
500  float h = high->weight() / high->sumEntries();
501  float l = low->weight() / low->sumEntries();
502  float n = nom->weight() / nom->sumEntries();
503  target->SetBinContent(i, target->GetBinContent(i) +
504  0.5 * x * ((h - l) + (h + l - 2. * n) * fx));
505  }
506 }
507 
508 // void CombineHarvester::SetParameters(std::vector<ch::Parameter> params) {
509 // params_.clear();
510 // for (unsigned i = 0; i < params.size(); ++i) {
511 // params_[params[i].name()] = std::make_shared<ch::Parameter>(params[i]);
512 // }
513 // }
514 
515 void CombineHarvester::RenameParameter(std::string const& oldname,
516  std::string const& newname) {
517  auto it = params_.find(oldname);
518  if (it != params_.end()) {
519  params_[newname] = it->second;
520  params_[newname]->set_name(newname);
521  params_.erase(it);
522  }
523 }
524 
526  std::string const& name) const {
527  auto it = params_.find(name);
528  if (it != params_.end()) {
529  return it->second.get();
530  } else {
531  return nullptr;
532  }
533 }
534 
536  auto it = params_.find(name);
537  if (it != params_.end()) {
538  return it->second.get();
539  } else {
540  return nullptr;
541  }
542 }
543 
545  std::vector<ch::Parameter> const& params) {
546  for (unsigned i = 0; i < params.size(); ++i) {
547  auto it = params_.find(params[i].name());
548  if (it != params_.end()) {
549  it->second->set_val(params[i].val());
550  it->second->set_err_d(params[i].err_d());
551  it->second->set_err_u(params[i].err_u());
552  } else {
553  if (verbosity_ >= 1) {
554  LOGLINE(log(), "Parameter " + params[i].name() + " is not defined");
555  }
556  }
557  }
558 }
559 
560 void CombineHarvester::UpdateParameters(RooFitResult const& fit) {
561  for (int i = 0; i < fit.floatParsFinal().getSize(); ++i) {
562  RooRealVar const* var =
563  dynamic_cast<RooRealVar const*>(fit.floatParsFinal().at(i));
564  // check for failed cast here
565  auto it = params_.find(std::string(var->GetName()));
566  if (it != params_.end()) {
567  it->second->set_val(var->getVal());
568  it->second->set_err_d(var->getErrorLo());
569  it->second->set_err_u(var->getErrorHi());
570  } else {
571  if (verbosity_ >= 1) {
572  LOGLINE(log(),
573  "Parameter " + std::string(var->GetName()) + " is not defined");
574  }
575  }
576  }
577 }
578 
579 void CombineHarvester::UpdateParameters(RooFitResult const* fit) {
580  UpdateParameters(*fit);
581 }
582 
583 std::vector<ch::Parameter> CombineHarvester::GetParameters() const {
584  std::vector<ch::Parameter> params;
585  for (auto const& it : params_) {
586  params.push_back(*(it.second));
587  }
588  return params;
589 }
590 
591 void CombineHarvester::VariableRebin(std::vector<double> bins) {
592  // We need to keep a record of the Process rates before we rebin. The
593  // reasoning comes from the following scenario: the user might choose a new
594  // binning which excludes some of the existing bins - thus changing the
595  // process normalisation. This is fine, but we also need to adjust the shape
596  // Systematic entries - both the rebinning and the adjustment of the value_u
597  // and value_d shifts.
598  std::vector<double> prev_proc_rates(procs_.size());
599 
600  // Also hold on the scaled Process hists *after* the rebinning is done - these
601  // are needed to update the associated Systematic entries
602  std::vector<std::unique_ptr<TH1>> scaled_procs(procs_.size());
603 
604  for (unsigned i = 0; i < procs_.size(); ++i) {
605  if (procs_[i]->shape()) {
606  // Get the scaled shape here
607  std::unique_ptr<TH1> copy(procs_[i]->ClonedScaledShape());
608  // shape norm should only be "no_norm_rate"
609  prev_proc_rates[i] = procs_[i]->no_norm_rate();
610  std::unique_ptr<TH1> copy2(copy->Rebin(bins.size()-1, "", &(bins[0])));
611  // The process shape & rate will be reset here
612  procs_[i]->set_shape(std::move(copy2), true);
613  scaled_procs[i] = procs_[i]->ClonedScaledShape();
614  }
615  }
616  for (unsigned i = 0; i < obs_.size(); ++i) {
617  if (obs_[i]->shape()) {
618  std::unique_ptr<TH1> copy(obs_[i]->ClonedScaledShape());
619  std::unique_ptr<TH1> copy2(copy->Rebin(bins.size()-1, "", &(bins[0])));
620  obs_[i]->set_shape(std::move(copy2), true);
621  }
622  }
623  for (unsigned i = 0; i < systs_.size(); ++i) {
624  TH1 const* proc_hist = nullptr;
625  double prev_rate = 0.;
626  for (unsigned j = 0; j < procs_.size(); ++j) {
627  if (MatchingProcess(*(procs_[j]), *(systs_[i].get()))) {
628  proc_hist = scaled_procs[j].get();
629  prev_rate = prev_proc_rates[j];
630  }
631  }
632  if (systs_[i]->shape_u() && systs_[i]->shape_d()) {
633  // These hists will be normalised to unity
634  std::unique_ptr<TH1> copy_u(systs_[i]->ClonedShapeU());
635  std::unique_ptr<TH1> copy_d(systs_[i]->ClonedShapeD());
636 
637  // If we found a matching Process we will scale this back up to their
638  // initial rates
639  if (proc_hist) {
640  copy_u->Scale(systs_[i]->value_u() * prev_rate);
641  copy_d->Scale(systs_[i]->value_d() * prev_rate);
642  }
643  std::unique_ptr<TH1> copy2_u(
644  copy_u->Rebin(bins.size() - 1, "", &(bins[0])));
645  std::unique_ptr<TH1> copy2_d(
646  copy_d->Rebin(bins.size() - 1, "", &(bins[0])));
647  // If we have proc_hist != nullptr, set_shapes will re-calculate value_u
648  // and value_d for us, before scaling the new hists back to unity
649  systs_[i]->set_shapes(std::move(copy2_u), std::move(copy2_d), proc_hist);
650  }
651  }
652 }
653 
654 void CombineHarvester::ZeroBins(double min, double max) {
655  // We need to keep a record of the Process rates before we set bins to 0. The
656  // This is necessary because we need to make sure the process normalisation
657  // and shape systematic entries are correctly adjusted
658  std::vector<double> prev_proc_rates(procs_.size());
659 
660  // Also hold on the scaled Process hists *after* some of the bins have
661  // been set to 0 - these
662  // are needed to update the associated Systematic entries
663  std::vector<std::unique_ptr<TH1>> scaled_procs(procs_.size());
664 
665  for (unsigned i = 0; i < procs_.size(); ++i) {
666  if (procs_[i]->shape()) {
667  // Get the scaled shape here
668  std::unique_ptr<TH1> copy(procs_[i]->ClonedScaledShape());
669  // shape norm should only be "no_norm_rate"
670  prev_proc_rates[i] = procs_[i]->no_norm_rate();
671  for (int j = 1; j <= copy->GetNbinsX();j ++){
672  if(copy->GetBinLowEdge(j) >= min && copy->GetBinLowEdge(j+1) <= max){
673  copy->SetBinContent(j,0.);
674  copy->SetBinError(j,0.);
675  }
676  }
677  // The process shape & rate will be reset here
678  procs_[i]->set_shape(std::move(copy), true);
679  scaled_procs[i] = procs_[i]->ClonedScaledShape();
680  }
681  }
682  for (unsigned i = 0; i < obs_.size(); ++i) {
683  if (obs_[i]->shape()) {
684  std::unique_ptr<TH1> copy(obs_[i]->ClonedScaledShape());
685  for (int j = 1; j <= copy->GetNbinsX();j ++){
686  if(copy->GetBinLowEdge(j) >= min && copy->GetBinLowEdge(j+1) <= max){
687  copy->SetBinContent(j,0.);
688  copy->SetBinError(j,0.);
689  }
690  }
691  obs_[i]->set_shape(std::move(copy), true);
692  }
693  }
694  for (unsigned i = 0; i < systs_.size(); ++i) {
695  TH1 const* proc_hist = nullptr;
696  double prev_rate = 0.;
697  for (unsigned j = 0; j < procs_.size(); ++j) {
698  if (MatchingProcess(*(procs_[j]), *(systs_[i].get()))) {
699  proc_hist = scaled_procs[j].get();
700  prev_rate = prev_proc_rates[j];
701  }
702  }
703  if (systs_[i]->shape_u() && systs_[i]->shape_d()) {
704  // These hists will be normalised to unity
705  std::unique_ptr<TH1> copy_u(systs_[i]->ClonedShapeU());
706  std::unique_ptr<TH1> copy_d(systs_[i]->ClonedShapeD());
707 
708  // If we found a matching Process we will scale this back up to their
709  // initial rates
710  if (proc_hist) {
711  copy_u->Scale(systs_[i]->value_u() * prev_rate);
712  copy_d->Scale(systs_[i]->value_d() * prev_rate);
713  }
714  for (int j = 1; j <= copy_u->GetNbinsX();j ++){
715  if(copy_u->GetBinLowEdge(j) >= min && copy_u->GetBinLowEdge(j+1) <= max){
716  copy_u->SetBinContent(j,0.);
717  copy_u->SetBinError(j,0.);
718  }
719  if(copy_d->GetBinLowEdge(j) >= min && copy_d->GetBinLowEdge(j+1) <= max){
720  copy_d->SetBinContent(j,0.);
721  copy_d->SetBinError(j,0.);
722  }
723  }
724  // If we have proc_hist != nullptr, set_shapes will re-calculate value_u
725  // and value_d for us, before scaling the new hists back to unity
726  systs_[i]->set_shapes(std::move(copy_u), std::move(copy_d), proc_hist);
727  }
728  }
729 }
730 
731 
732 void CombineHarvester::SetPdfBins(unsigned nbins) {
733  for (unsigned i = 0; i < procs_.size(); ++i) {
734  std::set<std::string> binning_vars;
735  if (procs_[i]->pdf()) {
736  RooAbsData const* data_obj = FindMatchingData(procs_[i].get());
737  std::string var_name = "CMS_th1x";
738  if (data_obj) var_name = data_obj->get()->first()->GetName();
739  binning_vars.insert(var_name);
740  }
741  for (auto & it : wspaces_) {
742  for (auto & var : binning_vars) {
743  RooRealVar* avar =
744  dynamic_cast<RooRealVar*>(it.second->var(var.c_str()));
745  if (avar) avar->setBins(nbins);
746  }
747  }
748  }
749 }
750 
751 // This implementation "borowed" from
752 // HiggsAnalysis/CombinedLimit/src/ProcessNormalization.cc
753 double CombineHarvester::logKappaForX(double x, double k_low,
754  double k_high) const {
755  if (k_high == 0. || k_low == 0.) {
756  if (verbosity_ >= 1) {
757  LOGLINE(log(), "Have kappa=0.0 (scaling ill-defined), returning 1.0");
758  }
759  return 1.;
760  }
761  if (fabs(x) >= 0.5)
762  return (x >= 0 ? std::pow(k_high, x) : std::pow(k_low, -1.0 * x));
763  // interpolate between log(kappaHigh) and -log(kappaLow)
764  // logKappa(x) = avg + halfdiff * h(2x)
765  // where h(x) is the 3th order polynomial
766  // h(x) = (3 x^5 - 10 x^3 + 15 x)/8;
767  // chosen so that h(x) satisfies the following:
768  // h (+/-1) = +/-1
769  // h'(+/-1) = 0
770  // h"(+/-1) = 0
771  double logKhi = std::log(k_high);
772  double logKlo = -std::log(k_low);
773  double avg = 0.5 * (logKhi + logKlo), halfdiff = 0.5 * (logKhi - logKlo);
774  double twox = x + x, twox2 = twox * twox;
775  double alpha = 0.125 * twox * (twox2 * (3 * twox2 - 10.) + 15.);
776  double ret = avg + alpha * halfdiff;
777  return std::exp(ret * x);
778 }
779 
780 void CombineHarvester::SetGroup(std::string const& name,
781  std::vector<std::string> const& patterns) {
782  std::vector<boost::regex> rgx;
783  for (auto const& pt : patterns) rgx.emplace_back(pt);
784  for (auto it = params_.begin(); it != params_.end(); ++it) {
785  std::string par = it->first;
786  auto & groups = it->second->groups();
787  if (groups.count(name)) continue;
788  if (ch::contains_rgx(rgx, par)) {
789  groups.insert(name);
790  };
791  }
792 }
793 
794 void CombineHarvester::RemoveGroup(std::string const& name,
795  std::vector<std::string> const& patterns) {
796  std::vector<boost::regex> rgx;
797  for (auto const& pt : patterns) rgx.emplace_back(pt);
798  for (auto it = params_.begin(); it != params_.end(); ++it) {
799  std::string par = it->first;
800  auto & groups = it->second->groups();
801  if (!groups.count(name)) continue;
802  if (ch::contains_rgx(rgx, par)) {
803  groups.erase(name);
804  };
805  }
806 }
807 
808 void CombineHarvester::RenameGroup(std::string const& oldname,
809  std::string const& newname) {
810  for (auto it = params_.begin(); it != params_.end(); ++it) {
811  auto & groups = it->second->groups();
812  if (groups.count(oldname)) {
813  groups.erase(oldname);
814  groups.insert(newname);
815  }
816  }
817 }
818 
819 void CombineHarvester::AddDatacardLineAtEnd(std::string const& line) {
820  post_lines_.push_back(line);
821 }
822 
824  post_lines_.clear();
825 }
826 }
ch::CombineHarvester::AddDatacardLineAtEnd
void AddDatacardLineAtEnd(std::string const &line)
Add a line of text at the end of all datacards.
Definition: CombineHarvester_Evaluate.cc:819
ch::CombineHarvester::GetObservedRate
double GetObservedRate()
Definition: CombineHarvester_Evaluate.cc:425
ch::CombineHarvester::GetRateCovariance
TH2F GetRateCovariance(RooFitResult const &fit, unsigned n_samples)
Definition: CombineHarvester_Evaluate.cc:179
Parameter.h
ch::CombineHarvester::RenameGroup
void RenameGroup(std::string const &oldname, std::string const &newname)
Rename a nuisance parameter group.
Definition: CombineHarvester_Evaluate.cc:808
ch::CombineHarvester::VariableRebin
void VariableRebin(std::vector< double > bins)
Definition: CombineHarvester_Evaluate.cc:591
Utilities.h
Process.h
ch::any_of
bool any_of(const Range &r, Predicate p)
Definition: Algorithm.h:17
ch::MatchingProcess
bool MatchingProcess(T const &first, U const &second)
Definition: Utilities.h:43
FNERROR
#define FNERROR(x)
Definition: Logging.h:9
ch::CombineHarvester::GetShape
TH1F GetShape()
Definition: CombineHarvester_Evaluate.cc:277
ch::CombineHarvester::UpdateParameters
void UpdateParameters(std::vector< ch::Parameter > const &params)
Definition: CombineHarvester_Evaluate.cc:544
ch::CombineHarvester::GetUncertainty
double GetUncertainty()
Definition: CombineHarvester_Evaluate.cc:44
ch::Parameter
Definition: Parameter.h:12
ch::CombineHarvester::GetParameter
ch::Parameter const * GetParameter(std::string const &name) const
Definition: CombineHarvester_Evaluate.cc:525
ch::CombineHarvester::ClearDatacardLinesAtEnd
void ClearDatacardLinesAtEnd()
Clear all added datacard lines.
Definition: CombineHarvester_Evaluate.cc:823
ch::CombineHarvester::bin_set
std::set< std::string > bin_set()
Definition: CombineHarvester_Filters.cc:190
MakeUnique.h
ch::CombineHarvester::SetPdfBins
void SetPdfBins(unsigned nbins)
Definition: CombineHarvester_Evaluate.cc:732
ch::contains_rgx
bool contains_rgx(const std::vector< boost::regex > &r, T p)
Definition: Algorithm.h:34
Systematic.h
ch
Definition: Algorithm.h:10
Algorithm.h
ch::CombineHarvester::cp
CombineHarvester cp()
Creates and returns a shallow copy of the CombineHarvester instance.
Definition: CombineHarvester.cc:220
ch::CombineHarvester::GetParameters
std::vector< ch::Parameter > GetParameters() const
Definition: CombineHarvester_Evaluate.cc:583
ch::CombineHarvester::GetShapeWithUncertainty
TH1F GetShapeWithUncertainty()
Definition: CombineHarvester_Evaluate.cc:103
ch::Systematic
Definition: Systematic.h:12
ch::CombineHarvester::RemoveGroup
void RemoveGroup(std::string const &name, std::vector< std::string > const &patterns)
Remove parameters to a given group.
Definition: CombineHarvester_Evaluate.cc:794
ch::CombineHarvester::GetRate
double GetRate()
Definition: CombineHarvester_Evaluate.cc:272
ch::CombineHarvester::data
CombineHarvester & data()
Definition: CombineHarvester_Filters.cc:183
ch::CombineHarvester::GetObservedShape
TH1F GetObservedShape()
Definition: CombineHarvester_Evaluate.cc:433
ch::CombineHarvester::GetRateCorrelation
TH2F GetRateCorrelation(RooFitResult const &fit, unsigned n_samples)
Definition: CombineHarvester_Evaluate.cc:252
ch::CombineHarvester::bin
CombineHarvester & bin(std::vector< std::string > const &vec, bool cond=true)
Definition: CombineHarvester_Filters.cc:13
LOGLINE
#define LOGLINE(x, y)
Definition: Logging.h:11
ch::CombineHarvester::RenameParameter
void RenameParameter(std::string const &oldname, std::string const &newname)
Definition: CombineHarvester_Evaluate.cc:515
CombineHarvester.h
ch::Systematic::name
std::string const & name() const
Definition: Systematic.h:21
ch::CombineHarvester::SetGroup
void SetGroup(std::string const &name, std::vector< std::string > const &patterns)
Add parameters to a given group.
Definition: CombineHarvester_Evaluate.cc:780
Observation.h
ch::CombineHarvester::ZeroBins
void ZeroBins(double min, double max)
Definition: CombineHarvester_Evaluate.cc:654
ch::CombineHarvester::process
CombineHarvester & process(std::vector< std::string > const &vec, bool cond=true)
Definition: CombineHarvester_Filters.cc:35