Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
HistoToWorkspaceFactoryFast.cxx
Go to the documentation of this file.
1// @(#)root/roostats:$Id: cranmer $
2// Author: Kyle Cranmer, Akira Shibata
3/*************************************************************************
4 * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11////////////////////////////////////////////////////////////////////////////////
12
13/** \class RooStats::HistFactory::HistoToWorkspaceFactoryFast
14 * \ingroup HistFactory
15 * This class provides helper functions for creating likelihood models from histograms.
16 * It is used by RooStats::HistFactory::MakeModelAndMeasurementFast.
17 *
18 * A tutorial showing how to create a HistFactory model is hf001_example.C
19 */
20
21
22#include <RooAddition.h>
23#include <RooBinWidthFunction.h>
24#include <RooBinning.h>
25#include <RooCategory.h>
26#include <RooConstVar.h>
27#include <RooDataHist.h>
28#include <RooDataSet.h>
29#include <RooFit/ModelConfig.h>
30#include <RooFitResult.h>
31#include <RooFormulaVar.h>
32#include <RooGamma.h>
33#include <RooGaussian.h>
34#include <RooGlobalFunc.h>
35#include <RooHelpers.h>
36#include <RooHistFunc.h>
37#include <RooNumIntConfig.h>
38#include <RooPoisson.h>
39#include <RooPolyVar.h>
40#include <RooProdPdf.h>
41#include <RooProduct.h>
42#include <RooRandom.h>
43#include <RooRealSumPdf.h>
44#include <RooRealVar.h>
45#include <RooSimultaneous.h>
46#include <RooWorkspace.h>
47
52
53#include "HFMsgService.h"
54
55#include "TH1.h"
56
57// specific to this package
64
65#include <algorithm>
66#include <fstream>
67#include <iomanip>
68#include <memory>
69#include <set>
70#include <utility>
71
72constexpr double alphaLow = -5.0;
73constexpr double alphaHigh = 5.0;
74
75std::vector<double> histToVector(TH1 const &hist)
76{
77 // Must get the full size of the TH1 (No direct method to do this...)
78 int numBins = hist.GetNbinsX() * hist.GetNbinsY() * hist.GetNbinsZ();
79 std::vector<double> out(numBins);
80 int histIndex = 0;
81 for (int i = 0; i < numBins; ++i) {
82 while (hist.IsBinUnderflow(histIndex) || hist.IsBinOverflow(histIndex)) {
83 ++histIndex;
84 }
85 out[i] = hist.GetBinContent(histIndex);
86 ++histIndex;
87 }
88 return out;
89}
90
91// use this order for safety on library loading
92using namespace RooStats;
93using std::string, std::vector;
94
95using namespace RooStats::HistFactory::Detail;
97
98namespace RooStats::HistFactory {
99
104
106 Configuration const& cfg) :
107 fSystToFix( measurement.GetConstantParams() ),
108 fParamValues( measurement.GetParamValues() ),
109 fNomLumi( measurement.GetLumi() ),
110 fLumiError( measurement.GetLumi()*measurement.GetLumiRelErr() ),
111 fLowBin( measurement.GetBinLow() ),
112 fHighBin( measurement.GetBinHigh() ),
113 fCfg{cfg} {
114
115 // Set Preprocess functions
116 SetFunctionsToPreprocess( measurement.GetPreprocessFunctions() );
117
118 }
119
120 void HistoToWorkspaceFactoryFast::ConfigureWorkspaceForMeasurement( const std::string& ModelName, RooWorkspace* ws_single, Measurement& measurement ) {
121
122 // Configure a workspace by doing any
123 // necessary post-processing and by
124 // creating a ModelConfig
125
126 // Make a ModelConfig and configure it
127 ModelConfig * proto_config = static_cast<ModelConfig *>(ws_single->obj("ModelConfig"));
128 if( proto_config == nullptr ) {
129 cxcoutFHF << "Error: Did not find 'ModelConfig' object in file: " << ws_single->GetName() << std::endl;
130 throw hf_exc();
131 }
132
133 if( measurement.GetPOIList().empty() ) {
134 cxcoutWHF << "No Parametetrs of interest are set" << std::endl;
135 }
136
137
138 std::stringstream sstream;
139 sstream << "Setting Parameter(s) of Interest as: ";
140 for(auto const& item : measurement.GetPOIList()) {
141 sstream << item << " ";
142 }
143 cxcoutIHF << sstream.str() << std::endl;
144
145 RooArgSet params;
146 for(auto const& poi_name : measurement.GetPOIList()) {
147 if(RooRealVar* poi = (RooRealVar*) ws_single->var(poi_name)){
148 params.add(*poi);
149 }
150 else {
151 cxcoutWHF << "WARNING: Can't find parameter of interest: " << poi_name
152 << " in Workspace. Not setting in ModelConfig." << std::endl;
153 // throw hf_exc();
154 }
155 }
156 proto_config->SetParametersOfInterest(params);
157
158 // Name of an 'edited' model, if necessary
159 std::string NewModelName = "newSimPdf"; // <- This name is hard-coded in HistoToWorkspaceFactoryFast::EditSyt. Probably should be changed to : std::string("new") + ModelName;
160
161 // Get the pdf
162 // Notice that we get the "new" pdf, this is the one that is
163 // used in the creation of these asimov datasets since they
164 // are fitted (or may be, at least).
165 RooAbsPdf* pdf = ws_single->pdf(NewModelName);
166 if( !pdf ) pdf = ws_single->pdf( ModelName );
167 const RooArgSet* observables = ws_single->set("observables");
168
169 // Set the ModelConfig's Params of Interest
170 if(!measurement.GetPOIList().empty()){
171 proto_config->GuessObsAndNuisance(*observables, RooMsgService::instance().isActive(nullptr, RooFit::HistFactory, RooFit::INFO));
172 }
173
174 // Now, let's loop over any additional asimov datasets
175 // that we need to make
176
177 // Create a SnapShot of the nominal values
178 std::string SnapShotName = "NominalParamValues";
179 ws_single->saveSnapshot(SnapShotName, ws_single->allVars());
180
181 for( unsigned int i=0; i<measurement.GetAsimovDatasets().size(); ++i) {
182
183 // Set the variable values and "const" ness with the workspace
184 RooStats::HistFactory::Asimov& asimov = measurement.GetAsimovDatasets().at(i);
185 std::string AsimovName = asimov.GetName();
186
187 cxcoutPHF << "Generating additional Asimov Dataset: " << AsimovName << std::endl;
188 asimov.ConfigureWorkspace(ws_single);
189 std::unique_ptr<RooAbsData> asimov_dataset{AsymptoticCalculator::GenerateAsimovData(*pdf, *observables)};
190
191 cxcoutPHF << "Importing Asimov dataset" << std::endl;
192 bool failure = ws_single->import(*asimov_dataset, RooFit::Rename(AsimovName.c_str()));
193 if( failure ) {
194 cxcoutFHF << "Error: Failed to import Asimov dataset: " << AsimovName << std::endl;
195 throw hf_exc();
196 }
197
198 // Load the snapshot at the end of every loop iteration
199 // so we start each loop with a "clean" snapshot
200 ws_single->loadSnapshot(SnapShotName.c_str());
201 }
202
203 // Cool, we're done
204 return; // ws_single;
205 }
206
207
208 // We want to eliminate this interface and use the measurement directly
210
211 // This is a pretty light-weight wrapper function
212 //
213 // Take a fully configured measurement as well as
214 // one of its channels
215 //
216 // Return a workspace representing that channel
217 // Do this by first creating a vector of EstimateSummary's
218 // and this by configuring the workspace with any post-processing
219
220 // Get the channel's name
221 string ch_name = channel.GetName();
222
223 // Create a workspace for a SingleChannel from the Measurement Object
224 std::unique_ptr<RooWorkspace> ws_single{this->MakeSingleChannelWorkspace(measurement, channel)};
225 if( ws_single == nullptr ) {
226 cxcoutFHF << "Error: Failed to make Single-Channel workspace for channel: " << ch_name
227 << " and measurement: " << measurement.GetName() << std::endl;
228 throw hf_exc();
229 }
230
231 // Finally, configure that workspace based on
232 // properties of the measurement
233 HistoToWorkspaceFactoryFast::ConfigureWorkspaceForMeasurement( "model_"+ch_name, ws_single.get(), measurement );
234
235 return RooFit::makeOwningPtr(std::move(ws_single));
236
237 }
238
240
241 // This function takes a fully configured measurement
242 // which may contain several channels and returns
243 // a workspace holding the combined model
244 //
245 // This can be used, for example, within a script to produce
246 // a combined workspace on-the-fly
247 //
248 // This is a static function (for now) to make
249 // it a one-liner
250
251
252 Configuration config;
253 return MakeCombinedModel(measurement,config);
254 }
255
257
258 // This function takes a fully configured measurement
259 // which may contain several channels and returns
260 // a workspace holding the combined model
261 //
262 // This can be used, for example, within a script to produce
263 // a combined workspace on-the-fly
264 //
265 // This is a static function (for now) to make
266 // it a one-liner
267
269
270 // First, we create an instance of a HistFactory
271 HistoToWorkspaceFactoryFast histFactory(measurement, config);
272
273 // Loop over the channels and create the individual workspaces
274 std::vector<std::unique_ptr<RooWorkspace>> channel_workspaces;
275 std::vector<std::string> channel_names;
276
277 for(HistFactory::Channel& channel : measurement.GetChannels()) {
278
279 if( ! channel.CheckHistograms() ) {
280 cxcoutFHF << "MakeModelAndMeasurementsFast: Channel: " << channel.GetName()
281 << " has uninitialized histogram pointers" << std::endl;
282 throw hf_exc();
283 }
284
285 string ch_name = channel.GetName();
286 channel_names.push_back(ch_name);
287
288 // GHL: Renaming to 'MakeSingleChannelWorkspace'
289 channel_workspaces.emplace_back(histFactory.MakeSingleChannelModel(measurement, channel));
290 }
291
292
293 // Now, combine the individual channel workspaces to
294 // form the combined workspace
295 std::unique_ptr<RooWorkspace> ws{histFactory.MakeCombinedModel( channel_names, channel_workspaces )};
296
297
298 // Configure the workspace
300
301 // Done. Return the pointer
302 return RooFit::makeOwningPtr(std::move(ws));
303
304 }
305
306namespace {
307
308template <class Arg_t, typename... Args_t>
309Arg_t &emplace(RooWorkspace &ws, std::string const &name, Args_t &&...args)
310{
311 Arg_t arg{name.c_str(), name.c_str(), std::forward<Args_t>(args)...};
313 return *dynamic_cast<Arg_t *>(ws.arg(name));
314}
315
316
317/// Check whether all channel workspaces contain consistent datasets.
318///
319/// This function compares the datasets stored in each channel workspace against
320/// those in the first workspace.
321///
322/// \param chs Vector of channel workspaces to compare (first is the reference).
323/// \param ch_names Names of the channels, used for error reporting.
324/// \param allowedInconsistent Dataset names that are allowed to differ between channels.
325///
326/// \return A pair consisting of:
327/// - bool: true if all channels are consistent (after ignoring allowed datasets),
328/// false otherwise.
329/// - std::string: empty if consistent; otherwise, a detailed error message
330/// describing the inconsistencies.
331
332std::pair<bool, std::string> isChannelDataConsistent(std::vector<std::unique_ptr<RooWorkspace>> const &chs,
333 std::vector<std::string> const &ch_names,
334 std::set<std::string> const &allowedInconsistent)
335{
336 // Collect the reference list of dataset names from the first workspace
337 std::set<std::string> referenceDataNames;
338 for (RooAbsData *data : chs[0]->allData()) {
339 referenceDataNames.insert(data->GetName());
340 }
341
342 // Check that all other workspaces have the same datasets
343 for (std::size_t i = 1; i < chs.size(); ++i) {
344 std::set<std::string> thisDataNames;
345 for (RooAbsData *data : chs[i]->allData()) {
346 thisDataNames.insert(data->GetName());
347 }
348
349 // Find missing and extra datasets in this workspace
350 std::vector<std::string> missing;
351 std::vector<std::string> extra;
352 std::set_difference(referenceDataNames.begin(), referenceDataNames.end(), thisDataNames.begin(),
353 thisDataNames.end(), std::back_inserter(missing));
354 std::set_difference(thisDataNames.begin(), thisDataNames.end(), referenceDataNames.begin(),
355 referenceDataNames.end(), std::back_inserter(extra));
356
357 // Remove allowed inconsistencies
358 auto isAllowed = [&](std::string const &name) { return allowedInconsistent.count(name) != 0; };
359
360 missing.erase(std::remove_if(missing.begin(), missing.end(), isAllowed), missing.end());
361 extra.erase(std::remove_if(extra.begin(), extra.end(), isAllowed), extra.end());
362
363 if (!missing.empty() || !extra.empty()) {
364 std::stringstream errMsg;
365 errMsg << "ERROR: Inconsistent datasets across channel workspaces.\n"
366 << "Workspace for channel \"" << ch_names[i] << "\" does not match "
367 << "the datasets in channel \"" << ch_names[0] << "\".\n";
368
369 if (!missing.empty()) {
370 errMsg << " Missing datasets:\n";
371 for (const auto &name : missing) {
372 errMsg << " - " << name << "\n";
373 }
374 }
375
376 if (!extra.empty()) {
377 errMsg << " Extra datasets:\n";
378 for (const auto &name : extra) {
379 errMsg << " - " << name << "\n";
380 }
381 }
382
383 errMsg << "All channel workspaces must contain exactly the same datasets.\n";
384 return {false, errMsg.str()};
385 }
386 }
387 return {true, ""};
388}
389
390} // namespace
391
392/// Create observables of type RooRealVar. Creates 1 to 3 observables, depending on the type of the histogram.
394 RooArgList observables;
395
396 for (unsigned int idx=0; idx < fObsNameVec.size(); ++idx) {
397 if (!proto.var(fObsNameVec[idx])) {
398 const TAxis *axis = (idx == 0) ? hist->GetXaxis() : (idx == 1 ? hist->GetYaxis() : hist->GetZaxis());
399 int nbins = axis->GetNbins();
400 // create observable
401 RooRealVar &obs = emplace<RooRealVar>(proto, fObsNameVec[idx], axis->GetXmin(), axis->GetXmax());
402 if(strlen(axis->GetTitle())>0) obs.SetTitle(axis->GetTitle());
403 obs.setBins(nbins);
404 if (axis->IsVariableBinSize()) {
405 RooBinning binning(nbins, axis->GetXbins()->GetArray());
406 obs.setBinning(binning);
407 }
408 }
409
410 observables.add(*proto.var(fObsNameVec[idx]));
411 }
412
413 return observables;
414}
415
416 /// Create the nominal hist function from `hist`, and register it in the workspace.
418 const RooArgList& observables) const {
419 if(hist) {
420 cxcoutI(HistFactory) << "processing hist " << hist->GetName() << std::endl;
421 } else {
422 cxcoutF(HistFactory) << "hist is empty" << std::endl;
423 R__ASSERT(hist != nullptr);
424 return nullptr;
425 }
426
427 // determine histogram dimensionality
428 unsigned int histndim(1);
429 std::string classname = hist->ClassName();
430 if (classname.find("TH1")==0) { histndim=1; }
431 else if (classname.find("TH2")==0) { histndim=2; }
432 else if (classname.find("TH3")==0) { histndim=3; }
433 R__ASSERT( histndim==fObsNameVec.size() );
434
435 prefix += "_Hist_alphanominal";
436
437 RooDataHist histDHist(prefix + "DHist","",observables,hist);
438
439 return &emplace<RooHistFunc>(proto, prefix, observables,histDHist,0);
440 }
441
442 namespace {
443
444 void makeGaussianConstraint(RooAbsArg& param, RooWorkspace& proto, bool isUniform,
445 std::vector<std::string> & constraintTermNames) {
446 std::string paramName = param.GetName();
447 std::string nomName = "nom_" + paramName;
448 std::string constraintName = paramName + "Constraint";
449
450 // do nothing if the constraint term already exists
451 if(proto.pdf(constraintName)) return;
452
453 // case systematic is uniform (assume they are like a Gaussian but with
454 // a large width (100 instead of 1)
455 const double gaussSigma = isUniform ? 100. : 1.0;
456 if (isUniform) {
457 cxcoutIHF << "Added a uniform constraint for " << paramName << " as a Gaussian constraint with a very large sigma " << std::endl;
458 }
459
460 constraintTermNames.emplace_back(constraintName);
461 RooRealVar &paramVar = *proto.var(paramName);
462 RooRealVar &nomParam = emplace<RooRealVar>(proto, nomName, 0., -10., 10.);
463 nomParam.setConstant();
464 emplace<RooGaussian>(proto, constraintName, paramVar, nomParam, gaussSigma);
465 paramVar.setError(gaussSigma); // give param initial error to match gaussSigma
466 const_cast<RooArgSet*>(proto.set("globalObservables"))->add(nomParam);
467 }
468
469 /// Make list of abstract parameters that interpolate in space of variations.
470 RooArgList makeInterpolationParameters(std::vector<HistoSys> const& histoSysList, RooWorkspace& proto) {
471 RooArgList params( ("alpha_Hist") );
472
473 for(auto const& histoSys : histoSysList) {
474 params.add(getOrCreate<RooRealVar>(proto, "alpha_" + histoSys.GetName(), alphaLow, alphaHigh));
475 }
476
477 return params;
478 }
479
480 /// Create a linear interpolation object that holds nominal and systematics, import it into the workspace,
481 /// and return a pointer to it.
482 RooAbsArg* makeLinInterp(RooArgList const& interpolationParams,
483 RooHistFunc* nominalFunc,
484 RooWorkspace& proto, const std::vector<HistoSys>& histoSysList,
485 const string& prefix,
486 const RooArgList& obsList) {
487
488 // now make function that linearly interpolates expectation between variations
489 // get low/high variations to interpolate between
490 std::vector<double> low;
491 std::vector<double> high;
492 RooArgSet lowSet;
493 RooArgSet highSet;
494 for(unsigned int j=0; j<histoSysList.size(); ++j){
495 std::string str = prefix + "_" + std::to_string(j);
496
497 const HistoSys& histoSys = histoSysList.at(j);
498 auto lowDHist = std::make_unique<RooDataHist>(str+"lowDHist","",obsList, histoSys.GetHistoLow());
499 auto highDHist = std::make_unique<RooDataHist>(str+"highDHist","",obsList, histoSys.GetHistoHigh());
500 lowSet.addOwned(std::make_unique<RooHistFunc>((str+"low").c_str(),"",obsList,std::move(lowDHist),0));
501 highSet.addOwned(std::make_unique<RooHistFunc>((str+"high").c_str(),"",obsList,std::move(highDHist),0));
502 }
503
504 // this is sigma(params), a piece-wise linear interpolation
505 PiecewiseInterpolation interp(prefix.c_str(),"",*nominalFunc,lowSet,highSet,interpolationParams);
506 interp.setPositiveDefinite();
507 interp.setAllInterpCodes(4); // LM: change to 4 (piece-wise linear to 6th order polynomial interpolation + linear extrapolation )
508 // KC: interpo codes 1 etc. don't have proper analytic integral.
509 RooArgSet obsSet(obsList);
510 interp.setBinIntegrator(obsSet);
511 interp.forceNumInt();
512
513 proto.import(interp, RooFit::RecycleConflictNodes()); // individual params have already been imported in first loop of this function
514
515 return proto.arg(prefix);
516 }
517
518 }
519
520 // GHL: Consider passing the NormFactor list instead of the entire sample
521 std::unique_ptr<RooProduct> HistoToWorkspaceFactoryFast::CreateNormFactor(RooWorkspace& proto, string& channel, string& sigmaEpsilon, Sample& sample, bool doRatio){
522
523 std::vector<string> prodNames;
524
525 vector<NormFactor> normList = sample.GetNormFactorList();
526 vector<string> normFactorNames;
527 vector<string> rangeNames;
528
529 string overallNorm_times_sigmaEpsilon = sample.GetName() + "_" + channel + "_scaleFactors";
530 auto sigEps = proto.arg(sigmaEpsilon);
531 assert(sigEps);
532 auto normFactor = std::make_unique<RooProduct>(overallNorm_times_sigmaEpsilon.c_str(), overallNorm_times_sigmaEpsilon.c_str(), RooArgList(*sigEps));
533
534 if(!normList.empty()){
535
536 for(NormFactor &norm : normList) {
537 string varname = norm.GetName();
538 if(doRatio) {
539 varname += "_" + channel;
540 }
541
542 // GHL: Check that the NormFactor doesn't already exist
543 // (it may have been created as a function expression
544 // during preprocessing)
545 std::stringstream range;
546 range << "[" << norm.GetVal() << "," << norm.GetLow() << "," << norm.GetHigh() << "]";
547
548 if( proto.obj(varname) == nullptr) {
549 cxcoutI(HistFactory) << "making normFactor: " << norm.GetName() << std::endl;
550 // remove "doRatio" and name can be changed when ws gets imported to the combined model.
551 emplace<RooRealVar>(proto, varname, norm.GetVal(), norm.GetLow(), norm.GetHigh());
552 proto.var(varname)->setError(0); // ensure factor is assigned an initial error, even if its zero
553 }
554
555 prodNames.push_back(varname);
556 rangeNames.push_back(range.str());
557 normFactorNames.push_back(varname);
558 }
559
560
561 for (const auto& name : prodNames) {
562 auto arg = proto.arg(name);
563 assert(arg);
564 normFactor->addTerm(arg);
565 }
566
567 }
568
569 unsigned int rangeIndex=0;
570 for( vector<string>::iterator nit = normFactorNames.begin(); nit!=normFactorNames.end(); ++nit){
571 if( count (normFactorNames.begin(), normFactorNames.end(), *nit) > 1 ){
572 cxcoutI(HistFactory) <<"<NormFactor Name =\""<<*nit<<"\"> is duplicated for <Sample Name=\""
573 << sample.GetName() << "\">, but only one factor will be included. \n Instead, define something like"
574 << "\n\t<Function Name=\""<<*nit<<"Squared\" Expression=\""<<*nit<<"*"<<*nit<<"\" Var=\""<<*nit<<rangeNames.at(rangeIndex)
575 << "\"> \nin your top-level XML's <Measurement> entry and use <NormFactor Name=\""<<*nit<<"Squared\" in your channel XML file."<< std::endl;
576 }
577 ++rangeIndex;
578 }
579
580 return normFactor;
581 }
582
584 string interpName,
585 std::vector<OverallSys>& systList,
586 vector<string>& constraintTermNames,
587 vector<string>& totSystTermNames) {
588
589 // add variables for all the relative overall uncertainties we expect
590 totSystTermNames.push_back(prefix);
591
592 RooArgSet params(prefix.c_str());
593 vector<double> lowVec;
594 vector<double> highVec;
595
596 std::map<std::string, double>::iterator itconstr;
597 for(unsigned int i = 0; i < systList.size(); ++i) {
598
599 OverallSys& sys = systList.at(i);
600 std::string strname = sys.GetName();
601 const char * name = strname.c_str();
602
603 // case of no systematic (is it possible)
604 if (meas.GetNoSyst().count(sys.GetName()) > 0 ) {
605 cxcoutI(HistFactory) << "HistoToWorkspaceFast::AddConstraintTerm - skip systematic " << sys.GetName() << std::endl;
606 continue;
607 }
608 // case systematic is a gamma constraint
609 if (meas.GetGammaSyst().count(sys.GetName()) > 0 ) {
610 double relerr = meas.GetGammaSyst().find(sys.GetName() )->second;
611 if (relerr <= 0) {
612 cxcoutI(HistFactory) << "HistoToWorkspaceFast::AddConstraintTerm - zero uncertainty assigned - skip systematic " << sys.GetName() << std::endl;
613 continue;
614 }
615 const double tauVal = 1./(relerr*relerr);
616 const double sqtau = 1./relerr;
617 RooRealVar &beta = emplace<RooRealVar>(proto, "beta_" + strname, 1., 0., 10.);
618 // the global observable (y_s)
619 RooRealVar &yvar = emplace<RooRealVar>(proto, "nom_" + std::string{beta.GetName()}, tauVal, 0., 10.);
620 // the rate of the gamma distribution (theta)
621 RooRealVar &theta = emplace<RooRealVar>(proto, "theta_" + strname, 1./tauVal);
622 // find alpha as function of beta
623 RooPolyVar &alphaOfBeta = emplace<RooPolyVar>(proto, "alphaOfBeta_" + strname, beta, RooArgList{-sqtau, sqtau});
624
625 // add now the constraint itself Gamma_beta_constraint(beta, y+1, tau, 0 )
626 // build the gamma parameter k = as y_s + 1
627 RooAddition &kappa = emplace<RooAddition>(proto, "k_" + std::string{yvar.GetName()}, RooArgList{yvar, 1.0});
628 RooGamma &gamma = emplace<RooGamma>(proto, std::string{beta.GetName()} + "Constraint", beta, kappa, theta, RooFit::RooConst(0.0));
630 alphaOfBeta.Print("t");
631 gamma.Print("t");
632 }
633 constraintTermNames.push_back(gamma.GetName());
634 // set global observables
635 yvar.setConstant(true);
636 const_cast<RooArgSet*>(proto.set("globalObservables"))->add(yvar);
637
638 // add alphaOfBeta in the list of params to interpolate
639 params.add(alphaOfBeta);
640 cxcoutI(HistFactory) << "Added a gamma constraint for " << name << std::endl;
641
642 }
643 else {
644 RooRealVar& alpha = getOrCreate<RooRealVar>(proto, prefix + sys.GetName(), 0, alphaLow, alphaHigh);
645 // add the Gaussian constraint part
646 const bool isUniform = meas.GetUniformSyst().count(sys.GetName()) > 0;
647 makeGaussianConstraint(alpha, proto, isUniform, constraintTermNames);
648
649 // check if exists a log-normal constraint
650 if (meas.GetLogNormSyst().count(sys.GetName()) == 0 && meas.GetGammaSyst().count(sys.GetName()) == 0 ) {
651 // just add the alpha for the parameters of the FlexibleInterpVar function
652 params.add(alpha);
653 }
654 // case systematic is a log-normal constraint
655 if (meas.GetLogNormSyst().count(sys.GetName()) > 0 ) {
656 // log normal constraint for parameter
657 const double relerr = meas.GetLogNormSyst().find(sys.GetName() )->second;
658
659 RooFormulaVar &alphaOfBeta = emplace<RooFormulaVar>(
660 proto, "alphaOfBeta_" + sys.GetName(), "x[0]*(pow(x[1],x[2])-1.)",
661 RooArgList{emplace<RooRealVar>(proto, "tau_" + sys.GetName(), 1. / relerr),
662 emplace<RooRealVar>(proto, "kappa_" + sys.GetName(), 1. + relerr), alpha});
663
664 cxcoutI(HistFactory) << "Added a log-normal constraint for " << name << std::endl;
666 alphaOfBeta.Print("t");
667 }
668 params.add(alphaOfBeta);
669 }
670
671 }
672 // add low/high vectors
673 lowVec.push_back(sys.GetLow());
674 highVec.push_back(sys.GetHigh());
675
676 } // end sys loop
677
678 if(!systList.empty()){
679 // this is epsilon(alpha_j), a piece-wise linear interpolation
680 // LinInterpVar interp( (interpName).c_str(), "", params, 1., lowVec, highVec);
681
682 assert(!params.empty());
683 assert(lowVec.size() == params.size());
684
685 FlexibleInterpVar interp( (interpName).c_str(), "", params, 1., lowVec, highVec);
686 interp.setAllInterpCodes(5); // LM: change to 5 (piece-wise exponential to 6th order polynomial interpolation + exponential extrapolation )
687 //interp.setAllInterpCodes(0); // simple linear interpolation
688 proto.import(interp); // params have already been imported in first loop of this function
689 } else{
690 // some strange behavior if params,lowVec,highVec are empty.
691 //cout << "WARNING: No OverallSyst terms" << std::endl;
692 emplace<RooConstVar>(proto, interpName, 1.); // params have already been imported in first loop of this function
693 }
694 }
695
696
698 const vector<RooProduct*>& sampleScaleFactors, std::vector<vector<RooAbsArg*>>& sampleHistFuncs) const {
699 assert(sampleScaleFactors.size() == sampleHistFuncs.size());
700
701 // for ith bin calculate totN_i = lumi * sum_j expected_j * syst_j
702
703 if (fObsNameVec.empty() && !fObsName.empty())
704 throw std::logic_error("HistFactory didn't process the observables correctly. Please file a bug report.");
705
706 auto firstHistFunc = dynamic_cast<const RooHistFunc*>(sampleHistFuncs.front().front());
707 if (!firstHistFunc) {
708 auto piecewiseInt = dynamic_cast<const PiecewiseInterpolation*>(sampleHistFuncs.front().front());
709 firstHistFunc = dynamic_cast<const RooHistFunc*>(piecewiseInt->nominalHist());
710 }
711 assert(firstHistFunc);
712
713 // Prepare a function to divide all bin contents by bin width to get a density:
714 auto &binWidth = emplace<RooBinWidthFunction>(proto, totName + "_binWidth", *firstHistFunc, true);
715
716 // Loop through samples and create products of their functions:
717 RooArgSet coefList;
718 RooArgSet shapeList;
719 for (unsigned int i=0; i < sampleHistFuncs.size(); ++i) {
720 assert(!sampleHistFuncs[i].empty());
721 coefList.add(*sampleScaleFactors[i]);
722
723 std::vector<RooAbsArg*>& thisSampleHistFuncs = sampleHistFuncs[i];
724 thisSampleHistFuncs.push_back(&binWidth);
725
726 if (thisSampleHistFuncs.size() == 1) {
727 // Just one function. Book it.
728 shapeList.add(*thisSampleHistFuncs.front());
729 } else {
730 // Have multiple functions. We need to multiply them.
731 std::string name = thisSampleHistFuncs.front()->GetName();
732 auto pos = name.find("Hist_alpha");
733 if (pos != std::string::npos) {
734 name = name.substr(0, pos) + "shapes";
735 } else if ( (pos = name.find("nominal")) != std::string::npos) {
736 name = name.substr(0, pos) + "shapes";
737 }
738
739 RooProduct shapeProduct(name.c_str(), thisSampleHistFuncs.front()->GetTitle(), RooArgSet(thisSampleHistFuncs.begin(), thisSampleHistFuncs.end()));
740 proto.import(shapeProduct, RooFit::RecycleConflictNodes());
741 shapeList.add(*proto.function(name));
742 }
743 }
744
745 // Sum all samples
746 RooRealSumPdf tot(totName.c_str(), totName.c_str(), shapeList, coefList, true);
747 tot.specialIntegratorConfig(true)->method1D().setLabel("RooBinIntegrator") ;
748 tot.specialIntegratorConfig(true)->method2D().setLabel("RooBinIntegrator") ;
749 tot.specialIntegratorConfig(true)->methodND().setLabel("RooBinIntegrator") ;
750 tot.forceNumInt();
751
752 // for mixed generation in RooSimultaneous
753 tot.setAttribute("GenerateBinned"); // for use with RooSimultaneous::generate in mixed mode
754
755 // Enable the binned likelihood optimization
757 tot.setAttribute("BinnedLikelihood");
758 }
759
760 proto.import(tot, RooFit::RecycleConflictNodes());
761 }
762
763 //////////////////////////////////////////////////////////////////////////////
764
766
767 std::ofstream covFile(filename);
768
769 covFile << " ";
770 for (auto const *myargi : static_range_cast<RooRealVar *>(*params)) {
771 if (myargi->isConstant())
772 continue;
773 covFile << " & " << myargi->GetName();
774 }
775 covFile << "\\\\ \\hline \n";
776 for (auto const *myargi : static_range_cast<RooRealVar *>(*params)) {
777 if(myargi->isConstant()) continue;
778 covFile << myargi->GetName();
779 for (auto const *myargj : static_range_cast<RooRealVar *>(*params)) {
780 if(myargj->isConstant()) continue;
781 std::cout << myargi->GetName() << "," << myargj->GetName();
782 double corr = result->correlation(*myargi, *myargj);
783 covFile << " & " << std::fixed << std::setprecision(2) << corr;
784 }
785 std::cout << std::endl;
786 covFile << " \\\\\n";
787 }
788
789 covFile.close();
790 }
791
792
793 ///////////////////////////////////////////////
794 std::unique_ptr<RooWorkspace> HistoToWorkspaceFactoryFast::MakeSingleChannelWorkspace(Measurement& measurement, Channel& channel) {
795
796 // check inputs (see JIRA-6890 )
797
798 if (channel.GetSamples().empty()) {
799 Error("MakeSingleChannelWorkspace",
800 "The input Channel does not contain any sample - return a nullptr");
801 return nullptr;
802 }
803
804 const TH1* channel_hist_template = channel.GetSamples().front().GetHisto();
805 if (channel_hist_template == nullptr) {
806 channel.CollectHistograms();
807 channel_hist_template = channel.GetSamples().front().GetHisto();
808 }
809 if (channel_hist_template == nullptr) {
810 std::ostringstream stream;
811 stream << "The sample " << channel.GetSamples().front().GetName()
812 << " in channel " << channel.GetName() << " does not contain a histogram. This is the channel:\n";
813 channel.Print(stream);
814 Error("MakeSingleChannelWorkspace", "%s", stream.str().c_str());
815 return nullptr;
816 }
817
818 if( ! channel.CheckHistograms() ) {
819 cxcoutFHF << "MakeSingleChannelWorkspace: Channel: " << channel.GetName()
820 << " has uninitialized histogram pointers" << std::endl;
821 throw hf_exc();
822 }
823
824
825
826 // Set these by hand inside the function
827 vector<string> systToFix = measurement.GetConstantParams();
828 bool doRatio=false;
829
830 //ES// string channel_name=summary[0].channel;
831 string channel_name = channel.GetName();
832
833 /// MB: reset observable names for each new channel.
834 fObsNameVec.clear();
835
836 /// MB: label observables x,y,z, depending on histogram dimensionality
837 /// GHL: Give it the first sample's nominal histogram as a template
838 /// since the data histogram may not be present
839 if (fObsNameVec.empty()) { GuessObsNameVec(channel_hist_template); }
840
841 for ( unsigned int idx=0; idx<fObsNameVec.size(); ++idx ) {
842 fObsNameVec[idx] = "obs_" + fObsNameVec[idx] + "_" + channel_name ;
843 }
844
845 if (fObsNameVec.empty()) {
846 fObsName= "obs_" + channel_name; // set name ov observable
847 fObsNameVec.push_back( fObsName );
848 }
849
850 if (fObsNameVec.empty() || fObsNameVec.size() > 3) {
851 throw hf_exc("HistFactory is limited to 1- to 3-dimensional histograms.");
852 }
853
854 cxcoutP(HistFactory) << "\n-----------------------------------------\n"
855 << "\tStarting to process '"
856 << channel_name << "' channel with " << fObsNameVec.size() << " observables"
857 << "\n-----------------------------------------\n" << std::endl;
858
859 //
860 // our main workspace that we are using to construct the model
861 //
862 auto protoOwner = std::make_unique<RooWorkspace>(channel_name.c_str(), (channel_name+" workspace").c_str());
863 RooWorkspace &proto = *protoOwner;
864 auto proto_config = std::make_unique<ModelConfig>("ModelConfig", &proto);
865
866 // preprocess functions
867 for(auto const& func : fPreprocessFunctions){
868 cxcoutI(HistFactory) << "will preprocess this line: " << func << std::endl;
869 proto.factory(func);
870 proto.Print();
871 }
872
873 RooArgSet likelihoodTerms("likelihoodTerms");
874 RooArgSet constraintTerms("constraintTerms");
875 vector<string> likelihoodTermNames;
876 vector<string> constraintTermNames;
877 vector<string> totSystTermNames;
878 // All histogram functions to be multiplied in each sample
879 std::vector<std::vector<RooAbsArg*>> allSampleHistFuncs;
880 std::vector<RooProduct*> sampleScaleFactors;
881
882 std::vector<std::pair<string, string>> statNamePairs;
883 std::vector<std::pair<const TH1 *, std::unique_ptr<TH1>>> statHistPairs; // <nominal, error>
884 const std::string statFuncName = "mc_stat_" + channel_name;
885
886 string prefix;
887 string range;
888
889 /////////////////////////////
890 // shared parameters
891 // this is ratio of lumi to nominal lumi. We will include relative uncertainty in model
892 auto &lumiVar = getOrCreate<RooRealVar>(proto, "Lumi", fNomLumi, 0.0, 10 * fNomLumi);
893
894 // only include a lumiConstraint if there's a lumi uncert, otherwise just set the lumi constant
895 if(fLumiError != 0) {
896 auto &nominalLumiVar = emplace<RooRealVar>(proto, "nominalLumi", fNomLumi, 0., fNomLumi + 10. * fLumiError);
897 emplace<RooGaussian>(proto, "lumiConstraint", lumiVar, nominalLumiVar, fLumiError);
898 proto.var("Lumi")->setError(fLumiError/fNomLumi); // give initial error value
899 proto.var("nominalLumi")->setConstant();
900 proto.defineSet("globalObservables","nominalLumi");
901 //likelihoodTermNames.push_back("lumiConstraint");
902 constraintTermNames.push_back("lumiConstraint");
903 } else {
904 proto.var("Lumi")->setConstant();
905 proto.defineSet("globalObservables",RooArgSet()); // create empty set as is assumed it exists later
906 }
907 ///////////////////////////////////
908 // loop through estimates, add expectation, floating bin predictions,
909 // and terms that constrain floating to expectation via uncertainties
910 // GHL: Loop over samples instead, which doesn't contain the data
911 for (Sample& sample : channel.GetSamples()) {
912 string overallSystName = sample.GetName() + "_" + channel_name + "_epsilon";
913
914 string systSourcePrefix = "alpha_";
915
916 // constraintTermNames and totSystTermNames are vectors that are passed
917 // by reference and filled by this method
918 AddConstraintTerms(proto,measurement, systSourcePrefix, overallSystName,
919 sample.GetOverallSysList(), constraintTermNames , totSystTermNames);
920
921 allSampleHistFuncs.emplace_back();
922 std::vector<RooAbsArg*>& sampleHistFuncs = allSampleHistFuncs.back();
923
924 // GHL: Consider passing the NormFactor list instead of the entire sample
925 auto normFactors = CreateNormFactor(proto, channel_name, overallSystName, sample, doRatio);
926 assert(normFactors);
927
928 // Create the string for the object
929 // that is added to the RooRealSumPdf
930 // for this channel
931// string syst_x_expectedPrefix = "";
932
933 // get histogram
934 //ES// TH1* nominal = it->nominal;
935 const TH1* nominal = sample.GetHisto();
936
937 // MB : HACK no option to have both non-hist variations and hist variations ?
938 // get histogram
939 // GHL: Okay, this is going to be non-trivial.
940 // We will loop over histosys's, which contain both
941 // the low hist and the high hist together.
942
943 // Logic:
944 // - If we have no HistoSys's, do part A
945 // - else, if the histo syst's don't match, return (we ignore this case)
946 // - finally, we take the syst's and apply the linear interpolation w/ constraint
947 string expPrefix = sample.GetName() + "_" + channel_name;
948 // create roorealvar observables
949 RooArgList observables = createObservables(sample.GetHisto(), proto);
950 RooHistFunc* nominalHistFunc = MakeExpectedHistFunc(sample.GetHisto(), proto, expPrefix, observables);
951 assert(nominalHistFunc);
952
953 if(sample.GetHistoSysList().empty()) {
954 // If no HistoSys
955 cxcoutI(HistFactory) << sample.GetName() + "_" + channel_name + " has no variation histograms " << std::endl;
956
957 sampleHistFuncs.push_back(nominalHistFunc);
958 } else {
959 // If there ARE HistoSys(s)
960 // name of source for variation
961 string constraintPrefix = sample.GetName() + "_" + channel_name + "_Hist_alpha";
962
963 // make list of abstract parameters that interpolate in space of variations
964 RooArgList interpParams = makeInterpolationParameters(sample.GetHistoSysList(), proto);
965
966 // next, create the constraint terms
967 for(std::size_t i = 0; i < interpParams.size(); ++i) {
968 bool isUniform = measurement.GetUniformSyst().count(sample.GetHistoSysList()[i].GetName()) > 0;
969 makeGaussianConstraint(interpParams[i], proto, isUniform, constraintTermNames);
970 }
971
972 // finally, create the interpolated function
973 sampleHistFuncs.push_back( makeLinInterp(interpParams, nominalHistFunc, proto,
974 sample.GetHistoSysList(), constraintPrefix, observables) );
975 }
976
977 sampleHistFuncs.front()->SetTitle( (nominal && strlen(nominal->GetTitle())>0) ? nominal->GetTitle() : sample.GetName().c_str() );
978
979 ////////////////////////////////////
980 // Add StatErrors to this Channel //
981 ////////////////////////////////////
982
983 if( sample.GetStatError().GetActivate() ) {
984
985 if( fObsNameVec.size() > 3 ) {
986 cxcoutFHF << "Cannot include Stat Error for histograms of more than 3 dimensions." << std::endl;
987 throw hf_exc();
988 } else {
989
990 // If we are using StatUncertainties, we multiply this object
991 // by the ParamHistFunc and then pass that to the
992 // RooRealSumPdf by appending it's name to the list
993
994 cxcoutI(HistFactory) << "Sample: " << sample.GetName() << " to be included in Stat Error "
995 << "for channel " << channel_name
996 << std::endl;
997
998 string UncertName = sample.GetName() + "_" + channel_name + "_StatAbsolUncert";
999 std::unique_ptr<TH1> statErrorHist;
1000
1001 if( sample.GetStatError().GetErrorHist() == nullptr ) {
1002 // Make the absolute stat error
1003 cxcoutI(HistFactory) << "Making Statistical Uncertainty Hist for "
1004 << " Channel: " << channel_name
1005 << " Sample: " << sample.GetName()
1006 << std::endl;
1007 statErrorHist.reset(MakeAbsolUncertaintyHist( UncertName, nominal));
1008 } else {
1009 // clone the error histograms because in case the sample has not error hist
1010 // it is created in MakeAbsolUncertainty
1011 // we need later to clean statErrorHist
1012 statErrorHist.reset(static_cast<TH1*>(sample.GetStatError().GetErrorHist()->Clone()));
1013 // We assume the (relative) error is provided.
1014 // We must turn it into an absolute error
1015 // using the nominal histogram
1016 cxcoutI(HistFactory) << "Using external histogram for Stat Errors for "
1017 << "\tChannel: " << channel_name
1018 << "\tSample: " << sample.GetName()
1019 << "\tError Histogram: " << statErrorHist->GetName() << std::endl;
1020 // Multiply the relative stat uncertainty by the
1021 // nominal to get the overall stat uncertainty
1022 statErrorHist->Multiply( nominal );
1023 statErrorHist->SetName( UncertName.c_str() );
1024 }
1025
1026 // Save the nominal and error hists
1027 // for the building of constraint terms
1028 statHistPairs.emplace_back(nominal, std::move(statErrorHist));
1029
1030 // To do the 'conservative' version, we would need to do some
1031 // intervention here. We would probably need to create a different
1032 // ParamHistFunc for each sample in the channel. The would nominally
1033 // use the same gamma's, so we haven't increased the number of parameters
1034 // However, if a bin in the 'nominal' histogram is 0, we simply need to
1035 // change the parameter in that bin in the ParamHistFunc for this sample.
1036 // We also need to add a constraint term.
1037 // Actually, we'd probably not use the ParamHistFunc...?
1038 // we could remove the dependence in this ParamHistFunc on the ith gamma
1039 // and then create the poisson term: Pois(tau | n_exp)Pois(data | n_exp)
1040
1041
1042 // Next, try to get the common ParamHistFunc (it may have been
1043 // created by another sample in this channel)
1044 // or create it if it doesn't yet exist:
1045 RooAbsReal* paramHist = dynamic_cast<ParamHistFunc*>(proto.function(statFuncName) );
1046 if( paramHist == nullptr ) {
1047
1048 // Get a RooArgSet of the observables:
1049 // Names in the list fObsNameVec:
1050 RooArgList theObservables;
1051 for (const auto &obsName : fObsNameVec) {
1052 theObservables.add( *proto.var(obsName) );
1053 }
1054
1055 // Create the list of terms to
1056 // control the bin heights:
1057 std::string ParamSetPrefix = "gamma_stat_" + channel_name;
1058 RooArgList statFactorParams = ParamHistFunc::createParamSet(proto,
1059 ParamSetPrefix,
1060 theObservables,
1062
1063 ParamHistFunc statUncertFunc(statFuncName.c_str(), statFuncName.c_str(),
1064 theObservables, statFactorParams );
1065
1066 proto.import( statUncertFunc, RooFit::RecycleConflictNodes() );
1067
1068 paramHist = proto.function( statFuncName);
1069 }
1070
1071 // apply stat function to sample
1072 sampleHistFuncs.push_back(paramHist);
1073 }
1074 } // END: if DoMcStat
1075
1076
1077 ///////////////////////////////////////////
1078 // Create a ShapeFactor for this channel //
1079 ///////////////////////////////////////////
1080
1081 if( !sample.GetShapeFactorList().empty() ) {
1082
1083 if( fObsNameVec.size() > 3 ) {
1084 cxcoutFHF << "Cannot include Stat Error for histograms of more than 3 dimensions." << std::endl;
1085 throw hf_exc();
1086 } else {
1087
1088 cxcoutI(HistFactory) << "Sample: " << sample.GetName() << " in channel: " << channel_name
1089 << " to be include a ShapeFactor."
1090 << std::endl;
1091
1092 for(ShapeFactor& shapeFactor : sample.GetShapeFactorList()) {
1093
1094 std::string funcName = channel_name + "_" + shapeFactor.GetName() + "_shapeFactor";
1095 RooAbsArg *paramHist = proto.function(funcName);
1096 if( paramHist == nullptr ) {
1097
1098 RooArgList theObservables;
1099 for(std::string const& varName : fObsNameVec) {
1100 theObservables.add( *proto.var(varName) );
1101 }
1102
1103 // Create the Parameters
1104 RooArgList shapeFactorParams =
1105 ParamHistFunc::createParamSet(proto, "gamma_" + shapeFactor.GetName(), theObservables);
1106 for (auto *comp : shapeFactorParams) {
1107 // If the gamma is subject to a preprocess function, it is a RooAbsReal and
1108 // we don't need to set the initial value.
1109 if (auto var = dynamic_cast<RooRealVar *>(comp)) {
1110 var->setVal(shapeFactor.GetVal());
1111 var->setMin(shapeFactor.GetLow());
1112 var->setMax(shapeFactor.GetHigh());
1113 }
1114 }
1115
1116 // Create the Function
1117 ParamHistFunc shapeFactorFunc( funcName.c_str(), funcName.c_str(),
1118 theObservables, shapeFactorParams );
1119
1120 // Set an initial shape, if requested
1121 if( shapeFactor.GetInitialShape() != nullptr ) {
1122 TH1* initialShape = static_cast<TH1*>(shapeFactor.GetInitialShape()->Clone());
1123 cxcoutI(HistFactory) << "Setting Shape Factor: " << shapeFactor.GetName()
1124 << " to have initial shape from hist: "
1125 << initialShape->GetName()
1126 << std::endl;
1127 shapeFactorFunc.setShape( initialShape );
1128 }
1129
1130 // Set the variables constant, if requested
1131 if( shapeFactor.IsConstant() ) {
1132 cxcoutI(HistFactory) << "Setting Shape Factor: " << shapeFactor.GetName()
1133 << " to be constant" << std::endl;
1134 shapeFactorFunc.setConstant(true);
1135 }
1136
1137 proto.import( shapeFactorFunc, RooFit::RecycleConflictNodes() );
1138 paramHist = proto.function(funcName);
1139
1140 } // End: Create ShapeFactor ParamHistFunc
1141
1142 sampleHistFuncs.push_back(paramHist);
1143 } // End loop over ShapeFactor Systematics
1144 }
1145 } // End: if ShapeFactorName!=""
1146
1147
1148 ////////////////////////////////////////
1149 // Create a ShapeSys for this channel //
1150 ////////////////////////////////////////
1151
1152 if( !sample.GetShapeSysList().empty() ) {
1153
1154 if( fObsNameVec.size() > 3 ) {
1155 cxcoutFHF << "Cannot include Stat Error for histograms of more than 3 dimensions.\n";
1156 throw hf_exc();
1157 }
1158
1159 // List of ShapeSys ParamHistFuncs
1160 std::vector<string> ShapeSysNames;
1161
1162 for(RooStats::HistFactory::ShapeSys& shapeSys : sample.GetShapeSysList()) {
1163
1164 // Create the ParamHistFunc's
1165 // Create their constraint terms and add them
1166 // to the list of constraint terms
1167
1168 // Create a single RooProduct over all of these
1169 // paramHistFunc's
1170
1171 // Send the name of that product to the RooRealSumPdf
1172
1173 cxcoutI(HistFactory) << "Sample: " << sample.GetName() << " in channel: " << channel_name
1174 << " to include a ShapeSys." << std::endl;
1175
1176 std::string funcName = channel_name + "_" + shapeSys.GetName() + "_ShapeSys";
1177 ShapeSysNames.push_back( funcName );
1178 auto paramHist = static_cast<ParamHistFunc*>(proto.function(funcName));
1179 if( paramHist == nullptr ) {
1180
1181 //std::string funcParams = "gamma_" + it->shapeFactorName;
1182 //paramHist = CreateParamHistFunc( proto, fObsNameVec, funcParams, funcName );
1183
1184 RooArgList theObservables;
1185 for(std::string const& varName : fObsNameVec) {
1186 theObservables.add( *proto.var(varName) );
1187 }
1188
1189 // Create the Parameters
1190 std::string funcParams = "gamma_" + shapeSys.GetName();
1191 RooArgList shapeFactorParams = ParamHistFunc::createParamSet(proto,
1192 funcParams,
1193 theObservables, defaultGammaMin, defaultShapeSysGammaMax);
1194
1195 // Create the Function
1196 ParamHistFunc shapeFactorFunc( funcName.c_str(), funcName.c_str(),
1197 theObservables, shapeFactorParams );
1198
1199 proto.import( shapeFactorFunc, RooFit::RecycleConflictNodes() );
1200 paramHist = static_cast<ParamHistFunc*>(proto.function(funcName));
1201
1202 } // End: Create ShapeFactor ParamHistFunc
1203
1204 // Create the constraint terms and add
1205 // them to the workspace (proto)
1206 // as well as the list of constraint terms (constraintTermNames)
1207
1208 // The syst should be a fractional error
1209 const TH1* shapeErrorHist = shapeSys.GetErrorHist();
1210
1211 // Constraint::Type shapeConstraintType = Constraint::Gaussian;
1212 Constraint::Type systype = shapeSys.GetConstraintType();
1213 if( systype == Constraint::Gaussian) {
1214 systype = Constraint::Gaussian;
1215 }
1216 if( systype == Constraint::Poisson ) {
1217 systype = Constraint::Poisson;
1218 }
1219
1220 auto shapeConstraintsInfo = createGammaConstraints(
1221 paramHist->paramList(), histToVector(*shapeErrorHist),
1223 systype);
1224 for (auto const& term : shapeConstraintsInfo.constraints) {
1225 proto.import(*term, RooFit::RecycleConflictNodes());
1226 constraintTermNames.emplace_back(term->GetName());
1227 }
1228 // Add the "observed" value to the list of global observables:
1229 RooArgSet *globalSet = const_cast<RooArgSet *>(proto.set("globalObservables"));
1230 for (RooAbsArg * glob : shapeConstraintsInfo.globalObservables) {
1231 globalSet->add(*proto.var(glob->GetName()));
1232 }
1233
1234
1235 } // End: Loop over ShapeSys vector in this EstimateSummary
1236
1237 // Now that we have the list of ShapeSys ParamHistFunc names,
1238 // we create the total RooProduct
1239 // we multiply the expected function
1240
1241 for(std::string const& name : ShapeSysNames) {
1242 sampleHistFuncs.push_back(proto.function(name));
1243 }
1244
1245 } // End: !GetShapeSysList.empty()
1246
1247
1248 // GHL: This was pretty confusing before,
1249 // hopefully using the measurement directly
1250 // will improve it
1251 RooAbsArg *lumi = proto.arg("Lumi");
1252 if( !sample.GetNormalizeByTheory() ) {
1253 if (!lumi) {
1254 lumi = &emplace<RooRealVar>(proto, "Lumi", measurement.GetLumi());
1255 } else {
1256 static_cast<RooAbsRealLValue*>(lumi)->setVal(measurement.GetLumi());
1257 }
1258 }
1259 assert(lumi);
1260 normFactors->addTerm(lumi);
1261
1262 // Append the name of the "node"
1263 // that is to be summed with the
1264 // RooRealSumPdf
1265 proto.import(*normFactors, RooFit::RecycleConflictNodes());
1266 auto normFactorsInWS = dynamic_cast<RooProduct*>(proto.arg(normFactors->GetName()));
1267 assert(normFactorsInWS);
1268
1269 sampleScaleFactors.push_back(normFactorsInWS);
1270 } // END: Loop over EstimateSummaries
1271
1272 // If a non-zero number of samples call for
1273 // Stat Uncertainties, create the statFactor functions
1274 if(!statHistPairs.empty()) {
1275
1276 // Create the histogram of (binwise)
1277 // stat uncertainties:
1278 std::unique_ptr<TH1> fracStatError(
1279 MakeScaledUncertaintyHist(channel_name + "_StatUncert" + "_RelErr", statHistPairs));
1280 if( fracStatError == nullptr ) {
1281 cxcoutFHF << "Error: Failed to make ScaledUncertaintyHist for: " << channel_name + "_StatUncert" + "_RelErr\n";
1282 throw hf_exc();
1283 }
1284
1285 // Using this TH1* of fractinal stat errors,
1286 // create a set of constraint terms:
1287 auto chanStatUncertFunc = static_cast<ParamHistFunc*>(proto.function( statFuncName ));
1288 cxcoutI(HistFactory) << "About to create Constraint Terms from: "
1289 << chanStatUncertFunc->GetName()
1290 << " params: " << chanStatUncertFunc->paramList()
1291 << std::endl;
1292
1293 // Get the constraint type and the
1294 // rel error threshold from the (last)
1295 // EstimateSummary looped over (but all
1296 // should be the same)
1297
1298 // Get the type of StatError constraint from the channel
1299 Constraint::Type statConstraintType = channel.GetStatErrorConfig().GetConstraintType();
1300 if( statConstraintType == Constraint::Gaussian) {
1301 cxcoutI(HistFactory) << "Using Gaussian StatErrors in channel: " << channel.GetName() << std::endl;
1302 }
1303 if( statConstraintType == Constraint::Poisson ) {
1304 cxcoutI(HistFactory) << "Using Poisson StatErrors in channel: " << channel.GetName() << std::endl;
1305 }
1306
1307 double statRelErrorThreshold = channel.GetStatErrorConfig().GetRelErrorThreshold();
1308 auto statConstraintsInfo = createGammaConstraints(
1309 chanStatUncertFunc->paramList(), histToVector(*fracStatError),
1310 statRelErrorThreshold,
1311 statConstraintType);
1312 for (auto const& term : statConstraintsInfo.constraints) {
1313 proto.import(*term, RooFit::RecycleConflictNodes());
1314 constraintTermNames.emplace_back(term->GetName());
1315 }
1316 // Add the "observed" value to the list of global observables:
1317 RooArgSet *globalSet = const_cast<RooArgSet *>(proto.set("globalObservables"));
1318 for (RooAbsArg * glob : statConstraintsInfo.globalObservables) {
1319 globalSet->add(*proto.var(glob->GetName()));
1320 }
1321
1322 } // END: Loop over stat Hist Pairs
1323
1324
1325 ///////////////////////////////////
1326 // for ith bin calculate totN_i = lumi * sum_j expected_j * syst_j
1327 MakeTotalExpected(proto, channel_name+"_model",
1328 sampleScaleFactors, allSampleHistFuncs);
1329 likelihoodTermNames.push_back(channel_name+"_model");
1330
1331 //////////////////////////////////////
1332 // fix specified parameters
1333 for(unsigned int i=0; i<systToFix.size(); ++i){
1334 RooRealVar* temp = proto.var(systToFix.at(i));
1335 if(!temp) {
1336 cxcoutW(HistFactory) << "could not find variable " << systToFix.at(i)
1337 << " could not set it to constant" << std::endl;
1338 } else {
1339 // set the parameter constant
1340 temp->setConstant();
1341 }
1342 }
1343
1344 //////////////////////////////////////
1345 // final proto model
1346 for(unsigned int i=0; i<constraintTermNames.size(); ++i){
1347 RooAbsArg* proto_arg = proto.arg(constraintTermNames[i]);
1348 if( proto_arg==nullptr ) {
1349 cxcoutFHF << "Error: Cannot find arg set: " << constraintTermNames.at(i)
1350 << " in workspace: " << proto.GetName() << std::endl;
1351 throw hf_exc();
1352 }
1353 constraintTerms.add( *proto_arg );
1354 // constraintTerms.add(* proto_arg(proto.arg(constraintTermNames[i].c_str())) );
1355 }
1356 for(unsigned int i=0; i<likelihoodTermNames.size(); ++i){
1357 RooAbsArg* proto_arg = (proto.arg(likelihoodTermNames[i]));
1358 if( proto_arg==nullptr ) {
1359 cxcoutFHF << "Error: Cannot find arg set: " << likelihoodTermNames.at(i)
1360 << " in workspace: " << proto.GetName() << std::endl;
1361 throw hf_exc();
1362 }
1363 likelihoodTerms.add( *proto_arg );
1364 }
1365 proto.defineSet("constraintTerms",constraintTerms);
1366 proto.defineSet("likelihoodTerms",likelihoodTerms);
1367
1368 // list of observables
1369 RooArgList observables;
1370 std::string observablesStr;
1371
1372 for(std::string const& name : fObsNameVec) {
1373 observables.add( *proto.var(name) );
1374 if (!observablesStr.empty()) { observablesStr += ","; }
1375 observablesStr += name;
1376 }
1377
1378 // We create two sets, one for backwards compatibility
1379 // The other to make a consistent naming convention
1380 // between individual channels and the combined workspace
1381 proto.defineSet("observables", observablesStr.c_str());
1382 proto.defineSet("observablesSet", observablesStr.c_str());
1383
1384 // Create the ParamHistFunc
1385 // after observables have been made
1386 cxcoutP(HistFactory) << "\n-----------------------------------------\n"
1387 << "\timport model into workspace"
1388 << "\n-----------------------------------------\n" << std::endl;
1389
1390 auto model = std::make_unique<RooProdPdf>(
1391 ("model_" + channel_name).c_str(), // MB : have changed this into conditional pdf. Much faster for toys!
1392 "product of Poissons across bins for a single channel", constraintTerms,
1393 RooFit::Conditional(likelihoodTerms, observables));
1394 // can give channel a title by setting title of corresponding data histogram
1395 if (channel.GetData().GetHisto() && strlen(channel.GetData().GetHisto()->GetTitle())>0) {
1396 model->SetTitle(channel.GetData().GetHisto()->GetTitle());
1397 }
1398 proto.import(*model,RooFit::RecycleConflictNodes());
1399
1400 proto_config->SetPdf(*model);
1401 proto_config->SetObservables(observables);
1402 proto_config->SetGlobalObservables(*proto.set("globalObservables"));
1403 // proto.writeToFile(("results/model_"+channel+".root").c_str());
1404 // fill out nuisance parameters in model config
1405 // proto_config->GuessObsAndNuisance(*proto.data("asimovData"));
1406 proto.import(*proto_config,proto_config->GetName());
1407 proto.importClassCode();
1408
1409 ///////////////////////////
1410 // make data sets
1411 // THis works and is natural, but the memory size of the simultaneous dataset grows exponentially with channels
1412 // New Asimov Generation: Use the code in the Asymptotic calculator
1413 // Need to get the ModelConfig...
1414 int asymcalcPrintLevel = 0;
1415 if (RooMsgService::instance().isActive(nullptr, RooFit::HistFactory, RooFit::INFO)) asymcalcPrintLevel = 1;
1416 if (RooMsgService::instance().isActive(nullptr, RooFit::HistFactory, RooFit::DEBUG)) asymcalcPrintLevel = 2;
1417 AsymptoticCalculator::SetPrintLevel(asymcalcPrintLevel);
1419 // Creating the per-channel asimov dataset is only meaningful if we
1420 // actually create the files with the stored per-channel workspaces.
1421 // Otherwise, we just spend time calculating something that gets thrown
1422 // away anyway (for the combined workspace, we'll create a new Asimov).
1423 std::unique_ptr<RooAbsData> asimov_dataset(AsymptoticCalculator::GenerateAsimovData(*model, observables));
1424 proto.import(*asimov_dataset, RooFit::Rename("asimovData"));
1425 }
1426
1427 // GHL: Determine to use data if the hist isn't 'nullptr'
1428 if(TH1 const* mnominal = channel.GetData().GetHisto()) {
1429 // This works and is natural, but the memory size of the simultaneous
1430 // dataset grows exponentially with channels.
1431 std::unique_ptr<RooDataSet> dataset;
1432 if(!fCfg.storeDataError){
1433 dataset = std::make_unique<RooDataSet>("obsData","",*proto.set("observables"), RooFit::WeightVar("weightVar"));
1434 } else {
1435 const char* weightErrName="weightErr";
1436 proto.factory(TString::Format("%s[0,-1e10,1e10]",weightErrName));
1437 dataset = std::make_unique<RooDataSet>("obsData","",*proto.set("observables"), RooFit::WeightVar("weightVar"), RooFit::StoreError(*proto.var(weightErrName)));
1438 }
1439 ConfigureHistFactoryDataset( *dataset, *mnominal, proto, fObsNameVec );
1440 proto.import(*dataset);
1441 } // End: Has non-null 'data' entry
1442
1443
1444 for(auto const& data : channel.GetAdditionalData()) {
1445 if(data.GetName().empty()) {
1446 cxcoutFHF << "Error: Additional Data histogram for channel: " << channel.GetName()
1447 << " has no name! The name always needs to be set for additional datasets, "
1448 << "either via the \"Name\" tag in the XML or via RooStats::HistFactory::Data::SetName().\n";
1449 throw hf_exc();
1450 }
1451 std::string const& dataName = data.GetName();
1452 TH1 const* mnominal = data.GetHisto();
1453 if( !mnominal ) {
1454 cxcoutFHF << "Error: Additional Data histogram for channel: " << channel.GetName()
1455 << " with name: " << dataName << " is nullptr\n";
1456 throw hf_exc();
1457 }
1458
1459 // THis works and is natural, but the memory size of the simultaneous dataset grows exponentially with channels
1460 RooDataSet dataset{dataName, "", *proto.set("observables"), RooFit::WeightVar("weightVar")};
1461 ConfigureHistFactoryDataset( dataset, *mnominal, proto, fObsNameVec );
1462 proto.import(dataset);
1463
1464 }
1465
1466 if (RooMsgService::instance().isActive(nullptr, RooFit::HistFactory, RooFit::INFO)) {
1467 proto.Print();
1468 }
1469
1470 return protoOwner;
1471 }
1472
1473
1475 TH1 const& mnominal,
1476 RooWorkspace& proto,
1477 std::vector<std::string> const& obsNameVec) {
1478
1479 // Take a RooDataSet and fill it with the entries
1480 // from a TH1*, using the observable names to
1481 // determine the columns
1482
1483 if (obsNameVec.empty() ) {
1484 Error("ConfigureHistFactoryDataset","Invalid input - return");
1485 return;
1486 }
1487
1488 TAxis const* ax = mnominal.GetXaxis();
1489 TAxis const* ay = mnominal.GetYaxis();
1490 TAxis const* az = mnominal.GetZaxis();
1491
1492 // check whether the dataset needs the errors stored explicitly
1493 const bool storeWeightErr = obsDataUnbinned.weightVar()->getAttribute("StoreError");
1494
1495 for (int i=1; i<=ax->GetNbins(); ++i) { // 1 or more dimension
1496
1497 double xval = ax->GetBinCenter(i);
1498 proto.var( obsNameVec[0] )->setVal( xval );
1499
1500 if(obsNameVec.size()==1) {
1501 double fval = mnominal.GetBinContent(i);
1502 double ferr = storeWeightErr ? mnominal.GetBinError(i) : 0.;
1503 obsDataUnbinned.add( *proto.set("observables"), fval, ferr );
1504 } else { // 2 or more dimensions
1505
1506 for(int j=1; j<=ay->GetNbins(); ++j) {
1507 double yval = ay->GetBinCenter(j);
1508 proto.var( obsNameVec[1] )->setVal( yval );
1509
1510 if(obsNameVec.size()==2) {
1511 double fval = mnominal.GetBinContent(i,j);
1512 double ferr = storeWeightErr ? mnominal.GetBinError(i, j) : 0.;
1513 obsDataUnbinned.add( *proto.set("observables"), fval, ferr );
1514 } else { // 3 dimensions
1515
1516 for(int k=1; k<=az->GetNbins(); ++k) {
1517 double zval = az->GetBinCenter(k);
1518 proto.var( obsNameVec[2] )->setVal( zval );
1519 double fval = mnominal.GetBinContent(i,j,k);
1520 double ferr = storeWeightErr ? mnominal.GetBinError(i, j, k) : 0.;
1521 obsDataUnbinned.add( *proto.set("observables"), fval, ferr );
1522 }
1523 }
1524 }
1525 }
1526 }
1527 }
1528
1530 {
1531 fObsNameVec = std::vector<string>{"x", "y", "z"};
1532 fObsNameVec.resize(hist->GetDimension());
1533 }
1534
1535
1537 HistoToWorkspaceFactoryFast::MakeCombinedModel(std::vector<std::string> ch_names,
1538 std::vector<std::unique_ptr<RooWorkspace>> &chs)
1539 {
1541
1542 // check first the inputs (see JIRA-6890)
1543 if (ch_names.empty() || chs.empty() ) {
1544 Error("MakeCombinedModel","Input vectors are empty - return a nullptr");
1545 return nullptr;
1546 }
1547 if (chs.size() < ch_names.size() ) {
1548 Error("MakeCombinedModel","Input vector of workspace has an invalid size - return a nullptr");
1549 return nullptr;
1550 }
1551 std::set<std::string> ch_names_set{ch_names.begin(), ch_names.end()};
1552 if (ch_names.size() != ch_names_set.size()) {
1553 Error("MakeCombinedModel", "Input vector of channel names has duplicate names - return a nullptr");
1554 return nullptr;
1555 }
1556
1557 //
1558 /// These things were used for debugging. Maybe useful in the future
1559 //
1560
1561 std::map<string, RooAbsPdf *> pdfMap;
1562 vector<RooAbsPdf *> models;
1563
1564 RooArgList obsList;
1565 for (unsigned int i = 0; i < ch_names.size(); ++i) {
1566 obsList.add(*static_cast<ModelConfig *>(chs[i]->obj("ModelConfig"))->GetObservables());
1567 }
1568 cxcoutI(HistFactory) <<"full list of observables:\n" << obsList << std::endl;
1569
1570 RooArgSet globalObs;
1571 std::map<std::string, int> channelMap;
1572 for(unsigned int i = 0; i< ch_names.size(); ++i){
1573 string channel_name=ch_names[i];
1574 if (i == 0 && isdigit(channel_name[0])) {
1575 throw std::invalid_argument("The first channel name for HistFactory cannot start with a digit. Got " + channel_name);
1576 }
1577 if (channel_name.find(',') != std::string::npos) {
1578 throw std::invalid_argument("Channel names for HistFactory cannot contain ','. Got " + channel_name);
1579 }
1580
1581 channelMap[channel_name] = i;
1582 RooWorkspace * ch=chs[i].get();
1583
1584 RooAbsPdf* model = ch->pdf("model_"+channel_name);
1585 if (!model) {
1586 cxcoutFHF << "failed to find model for channel\n";
1587 throw hf_exc();
1588 }
1589 models.push_back(model);
1590 auto &modelConfig = *static_cast<ModelConfig *>(chs[i]->obj("ModelConfig"));
1591 // silent because observables might exist in other channel:
1592 globalObs.add(*modelConfig.GetGlobalObservables(), /*silent=*/true);
1593
1594 // constrainedParams->add( * ch->set("constrainedParams") );
1595 pdfMap[channel_name]=model;
1596 }
1597
1598 cxcoutP(HistFactory) << "\n-----------------------------------------\n"
1599 << "\tEntering combination"
1600 << "\n-----------------------------------------\n" << std::endl;
1601 auto combined = std::make_unique<RooWorkspace>("combined");
1602
1603
1604 RooCategory& channelCat = emplace<RooCategory>(*combined, "channelCat", channelMap);
1605
1606 auto simPdf= std::make_unique<RooSimultaneous>("simPdf","",pdfMap, channelCat);
1607 auto combined_config = std::make_unique<ModelConfig>("ModelConfig", combined.get());
1608 combined_config->SetWorkspace(*combined);
1609 // combined_config->SetNuisanceParameters(*constrainedParams);
1610
1611 combined->import(globalObs);
1612 combined->defineSet("globalObservables",globalObs);
1613 combined_config->SetGlobalObservables(*combined->set("globalObservables"));
1614
1615 combined->defineSet("observables",{obsList, channelCat}, /*importMissing=*/true);
1616 combined_config->SetObservables(*combined->set("observables"));
1617
1618 // Check if the channel datasets are consistent
1619 {
1620 bool isConsistent = false;
1621 std::string errMsg;
1622 std::set<std::string> allowedInconsistent{"asimovData"};
1623 std::tie(isConsistent, errMsg) = isChannelDataConsistent(chs, ch_names, allowedInconsistent);
1624 if (!isConsistent) {
1625 cxcoutFHF << errMsg;
1626 throw hf_exc();
1627 }
1628 }
1629
1630 // Now merge the observable datasets across the channels
1631 for(RooAbsData * data : chs[0]->allData()) {
1632 // We are excluding the Asimov data, because it needs to be regenerated
1633 // later after the parameter values are set.
1634 if(std::string("asimovData") == data->GetName()) {
1635 continue;
1636 }
1637 // Loop through channels, get their individual datasets,
1638 // and add them to the combined dataset
1639 std::map<std::string, RooAbsData*> dataMap;
1640 for(unsigned int i = 0; i < ch_names.size(); ++i){
1641 dataMap[ch_names[i]] = chs[i]->data(data->GetName());
1642 }
1643 combined->import(RooDataSet{data->GetName(), "", obsList, RooFit::Index(channelCat),
1644 RooFit::WeightVar("weightVar"), RooFit::Import(dataMap)});
1645 }
1646
1647
1649 combined->Print();
1650
1651 cxcoutP(HistFactory) << "\n-----------------------------------------\n"
1652 << "\tImporting combined model"
1653 << "\n-----------------------------------------\n" << std::endl;
1654 combined->import(*simPdf,RooFit::RecycleConflictNodes());
1655
1656 for(auto const& param_itr : fParamValues) {
1657 // make sure they are fixed
1658 std::string paramName = param_itr.first;
1659 double paramVal = param_itr.second;
1660
1661 if(RooRealVar* temp = combined->var( paramName )) {
1662 temp->setVal( paramVal );
1663 cxcoutI(HistFactory) <<"setting " << paramName << " to the value: " << paramVal << std::endl;
1664 } else
1665 cxcoutE(HistFactory) << "could not find variable " << paramName << " could not set its value" << std::endl;
1666 }
1667
1668
1669 for(unsigned int i=0; i<fSystToFix.size(); ++i){
1670 // make sure they are fixed
1671 if(RooRealVar* temp = combined->var(fSystToFix[i])) {
1672 temp->setConstant();
1673 cxcoutI(HistFactory) <<"setting " << fSystToFix.at(i) << " constant" << std::endl;
1674 } else
1675 cxcoutE(HistFactory) << "could not find variable " << fSystToFix.at(i) << " could not set it to constant" << std::endl;
1676 }
1677
1678 ///
1679 /// writing out the model in graphViz
1680 ///
1681 // RooAbsPdf* customized=combined->pdf("simPdf");
1682 //combined_config->SetPdf(*customized);
1683 combined_config->SetPdf(*simPdf);
1684 // combined_config->GuessObsAndNuisance(*simData);
1685 // customized->graphVizTree(("results/"+fResultsPrefixStr.str()+"_simul.dot").c_str());
1686 combined->import(*combined_config,combined_config->GetName());
1687 combined->importClassCode();
1688 // combined->writeToFile("results/model_combined.root");
1689
1690
1691 ////////////////////////////////////////////
1692 // Make toy simultaneous dataset
1693 cxcoutP(HistFactory) << "\n-----------------------------------------\n"
1694 << "\tcreate toy data"
1695 << "\n-----------------------------------------\n" << std::endl;
1696
1697
1698 // now with weighted datasets
1699 // First Asimov
1700
1701 // Create Asimov data for the combined dataset
1702 std::unique_ptr<RooAbsData> asimov_combined{AsymptoticCalculator::GenerateAsimovData(
1703 *combined->pdf("simPdf"),
1704 obsList)};
1705 if( asimov_combined ) {
1706 combined->import( *asimov_combined, RooFit::Rename("asimovData"));
1707 }
1708 else {
1709 cxcoutFHF << "Error: Failed to create combined asimov dataset\n";
1710 throw hf_exc();
1711 }
1712
1713 return RooFit::makeOwningPtr(std::move(combined));
1714 }
1715
1716
1717 TH1* HistoToWorkspaceFactoryFast::MakeAbsolUncertaintyHist( const std::string& Name, const TH1* Nominal ) {
1718
1719 // Take a nominal TH1* and create
1720 // a TH1 representing the binwise
1721 // errors (taken from the nominal TH1)
1722
1723 auto ErrorHist = static_cast<TH1*>(Nominal->Clone( Name.c_str() ));
1724 ErrorHist->Reset();
1725
1726 int numBins = Nominal->GetNbinsX()*Nominal->GetNbinsY()*Nominal->GetNbinsZ();
1727 int binNumber = 0;
1728
1729 // Loop over bins
1730 for( int i_bin = 0; i_bin < numBins; ++i_bin) {
1731
1732 binNumber++;
1733 // Ignore underflow / overflow
1734 while( Nominal->IsBinUnderflow(binNumber) || Nominal->IsBinOverflow(binNumber) ){
1735 binNumber++;
1736 }
1737
1738 double histError = Nominal->GetBinError( binNumber );
1739
1740 // Check that histError != NAN
1741 if( histError != histError ) {
1742 cxcoutFHF << "Warning: In histogram " << Nominal->GetName() << " bin error for bin " << i_bin
1743 << " is NAN. Not using Error!!!\n";
1744 throw hf_exc();
1745 // histError = sqrt( histContent );
1746 // histError = 0;
1747 }
1748
1749 // Check that histError ! < 0
1750 if( histError < 0 ) {
1751 cxcoutWHF << "Warning: In histogram " << Nominal->GetName() << " bin error for bin " << binNumber
1752 << " is < 0. Setting Error to 0" << std::endl;
1753 // histError = sqrt( histContent );
1754 histError = 0;
1755 }
1756
1757 ErrorHist->SetBinContent( binNumber, histError );
1758
1759 }
1760
1761 return ErrorHist;
1762
1763 }
1764
1765 // Take a list of < nominal, absolError > TH1* pairs
1766 // and construct a single histogram representing the
1767 // total fractional error as:
1768
1769 // UncertInQuad(bin i) = Sum: absolUncert*absolUncert
1770 // Total(bin i) = Sum: Value
1771 //
1772 // TotalFracError(bin i) = Sqrt( UncertInQuad(i) ) / TotalBin(i)
1773 std::unique_ptr<TH1> HistoToWorkspaceFactoryFast::MakeScaledUncertaintyHist( const std::string& Name, std::vector< std::pair<const TH1*, std::unique_ptr<TH1>> > const& HistVec ) const {
1774
1775
1776 unsigned int numHists = HistVec.size();
1777
1778 if( numHists == 0 ) {
1779 cxcoutE(HistFactory) << "Warning: Empty Hist Vector, cannot create total uncertainty" << std::endl;
1780 return nullptr;
1781 }
1782
1783 const TH1* HistTemplate = HistVec.at(0).first;
1784 int numBins = HistTemplate->GetNbinsX()*HistTemplate->GetNbinsY()*HistTemplate->GetNbinsZ();
1785
1786 // Check that all histograms
1787 // have the same bins
1788 for( unsigned int i = 0; i < HistVec.size(); ++i ) {
1789
1790 const TH1* nominal = HistVec.at(i).first;
1791 const TH1* error = HistVec.at(i).second.get();
1792
1793 if( nominal->GetNbinsX()*nominal->GetNbinsY()*nominal->GetNbinsZ() != numBins ) {
1794 cxcoutE(HistFactory) << "Error: Provided hists have unequal bins" << std::endl;
1795 return nullptr;
1796 }
1797 if( error->GetNbinsX()*error->GetNbinsY()*error->GetNbinsZ() != numBins ) {
1798 cxcoutE(HistFactory) << "Error: Provided hists have unequal bins" << std::endl;
1799 return nullptr;
1800 }
1801 }
1802
1803 std::vector<double> TotalBinContent( numBins, 0.0);
1804 std::vector<double> HistErrorsSqr( numBins, 0.0);
1805
1806 int binNumber = 0;
1807
1808 // Loop over bins
1809 for( int i_bins = 0; i_bins < numBins; ++i_bins) {
1810
1811 binNumber++;
1812 while( HistTemplate->IsBinUnderflow(binNumber) || HistTemplate->IsBinOverflow(binNumber) ){
1813 binNumber++;
1814 }
1815
1816 for( unsigned int i_hist = 0; i_hist < numHists; ++i_hist ) {
1817
1818 const TH1* nominal = HistVec.at(i_hist).first;
1819 const TH1* error = HistVec.at(i_hist).second.get();
1820
1821 //int binNumber = i_bins + 1;
1822
1823 double histValue = nominal->GetBinContent( binNumber );
1824 double histError = error->GetBinContent( binNumber );
1825
1826 if( histError != histError ) {
1827 cxcoutFHF << "In histogram " << error->GetName() << " bin error for bin " << binNumber
1828 << " is NAN. Not using error!!";
1829 throw hf_exc();
1830 }
1831
1832 TotalBinContent.at(i_bins) += histValue;
1833 HistErrorsSqr.at(i_bins) += histError*histError; // Add in quadrature
1834
1835 }
1836 }
1837
1838 binNumber = 0;
1839
1840 // Creat the output histogram
1841 TH1* ErrorHist = static_cast<TH1*>(HistTemplate->Clone( Name.c_str() ));
1842 ErrorHist->Reset();
1843
1844 // Fill the output histogram
1845 for( int i = 0; i < numBins; ++i) {
1846
1847 // int binNumber = i + 1;
1848 binNumber++;
1849 while( ErrorHist->IsBinUnderflow(binNumber) || ErrorHist->IsBinOverflow(binNumber) ){
1850 binNumber++;
1851 }
1852
1853 double ErrorsSqr = HistErrorsSqr.at(i);
1854 double TotalVal = TotalBinContent.at(i);
1855
1856 if( TotalVal <= 0 ) {
1857 cxcoutW(HistFactory) << "Warning: Sum of histograms for bin: " << binNumber
1858 << " is <= 0. Setting error to 0"
1859 << std::endl;
1860
1861 ErrorHist->SetBinContent( binNumber, 0.0 );
1862 continue;
1863 }
1864
1865 double RelativeError = sqrt(ErrorsSqr) / TotalVal;
1866
1867 // If we otherwise get a NAN
1868 // it's an error
1869 if( RelativeError != RelativeError ) {
1870 cxcoutE(HistFactory) << "Error: bin " << i << " error is NAN\n"
1871 << " HistErrorsSqr: " << ErrorsSqr
1872 << " TotalVal: " << TotalVal;
1873 throw hf_exc();
1874 }
1875
1876 // 0th entry in vector is
1877 // the 1st bin in TH1
1878 // (we ignore underflow)
1879
1880 // Error and bin content are interchanged because for some reason, the other functions
1881 // use the bin content to convey the error ...
1882 ErrorHist->SetBinError(binNumber, TotalVal);
1883 ErrorHist->SetBinContent(binNumber, RelativeError);
1884
1885 cxcoutI(HistFactory) << "Making Total Uncertainty for bin " << binNumber
1886 << " Error = " << sqrt(ErrorsSqr)
1887 << " CentralVal = " << TotalVal
1888 << " RelativeError = " << RelativeError << "\n";
1889
1890 }
1891
1892 return std::unique_ptr<TH1>(ErrorHist);
1893}
1894
1895} // namespace RooStats::HistFactory
#define cxcoutPHF
#define cxcoutFHF
#define cxcoutIHF
#define cxcoutWHF
std::vector< double > histToVector(TH1 const &hist)
constexpr double alphaHigh
constexpr double alphaLow
#define cxcoutI(a)
#define cxcoutW(a)
#define cxcoutF(a)
#define cxcoutE(a)
#define cxcoutP(a)
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:139
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
char name[80]
Definition TGX11.cxx:142
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
static RooArgList createParamSet(RooWorkspace &w, const std::string &, const RooArgList &Vars)
Create the list of RooRealVar parameters which represent the height of the histogram bins.
The PiecewiseInterpolation is a class that can morph distributions into each other,...
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:55
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
void setConstant(bool value=true)
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
Calculates the sum of a set of RooAbsReal terms, or when constructed with two sets,...
Definition RooAddition.h:27
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Implements a RooAbsBinning in terms of an array of boundary values, posing no constraints on the choi...
Definition RooBinning.h:27
Object to represent discrete states.
Definition RooCategory.h:28
Container class to hold N-dimensional binned data.
Definition RooDataHist.h:40
Container class to hold unbinned data.
Definition RooDataSet.h:32
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
Implementation of the Gamma PDF for RooFit/RooStats.
Definition RooGamma.h:20
Switches the message service to a different level while the instance is alive.
Definition RooHelpers.h:37
A real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:29
static RooMsgService & instance()
Return reference to singleton instance.
A RooAbsReal implementing a polynomial in terms of a list of RooAbsReal coefficients.
Definition RooPolyVar.h:25
Represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
Implements a PDF constructed from a sum of functions:
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setBins(Int_t nBins, const char *name=nullptr, bool shared=true)
Create a uniform binning under name 'name' for this variable.
void setBinning(const RooAbsBinning &binning, const char *name=nullptr, bool shared=true)
Add given binning under name 'name' with this variable.
static void SetPrintLevel(int level)
set print level (static function)
static RooAbsData * GenerateAsimovData(const RooAbsPdf &pdf, const RooArgSet &observables)
generate the asimov data for the observables (not the global ones) need to deal with the case of a si...
TODO Here, we are missing some documentation.
void ConfigureWorkspace(RooWorkspace *)
This class encapsulates all information for the statistical interpretation of one experiment.
std::vector< RooStats::HistFactory::Data > & GetAdditionalData()
retrieve vector of additional data objects
void Print(std::ostream &=std::cout)
HistFactory::StatErrorConfig & GetStatErrorConfig()
get information about threshold for statistical uncertainties and constraint term
RooStats::HistFactory::Data & GetData()
get data object
std::vector< RooStats::HistFactory::Sample > & GetSamples()
get vector of samples for this channel
std::string GetName() const
get name of channel
This class provides helper functions for creating likelihood models from histograms.
std::unique_ptr< RooProduct > CreateNormFactor(RooWorkspace &proto, std::string &channel, std::string &sigmaEpsilon, Sample &sample, bool doRatio)
std::unique_ptr< RooWorkspace > MakeSingleChannelWorkspace(Measurement &measurement, Channel &channel)
void MakeTotalExpected(RooWorkspace &proto, const std::string &totName, const std::vector< RooProduct * > &sampleScaleFactors, std::vector< std::vector< RooAbsArg * > > &sampleHistFuncs) const
std::unique_ptr< TH1 > MakeScaledUncertaintyHist(const std::string &Name, std::vector< std::pair< const TH1 *, std::unique_ptr< TH1 > > > const &HistVec) const
RooHistFunc * MakeExpectedHistFunc(const TH1 *hist, RooWorkspace &proto, std::string prefix, const RooArgList &observables) const
Create the nominal hist function from hist, and register it in the workspace.
void SetFunctionsToPreprocess(std::vector< std::string > lines)
RooFit::OwningPtr< RooWorkspace > MakeSingleChannelModel(Measurement &measurement, Channel &channel)
RooFit::OwningPtr< RooWorkspace > MakeCombinedModel(std::vector< std::string >, std::vector< std::unique_ptr< RooWorkspace > > &)
TH1 * MakeAbsolUncertaintyHist(const std::string &Name, const TH1 *Hist)
static void ConfigureWorkspaceForMeasurement(const std::string &ModelName, RooWorkspace *ws_single, Measurement &measurement)
void AddConstraintTerms(RooWorkspace &proto, Measurement &measurement, std::string prefix, std::string interpName, std::vector< OverallSys > &systList, std::vector< std::string > &likelihoodTermNames, std::vector< std::string > &totSystTermNames)
void ConfigureHistFactoryDataset(RooDataSet &obsData, TH1 const &nominal, RooWorkspace &proto, std::vector< std::string > const &obsNameVec)
static void PrintCovarianceMatrix(RooFitResult *result, RooArgSet *params, std::string filename)
RooArgList createObservables(const TH1 *hist, RooWorkspace &proto) const
Create observables of type RooRealVar. Creates 1 to 3 observables, depending on the type of the histo...
The RooStats::HistFactory::Measurement class can be used to construct a model by combining multiple R...
Configuration for an un- constrained overall systematic to scale sample normalisations.
Definition Measurement.h:60
Configuration for a constrained overall systematic to scale sample normalisations.
Definition Measurement.h:36
const std::string & GetName() const
Definition Measurement.h:40
*Un*constrained bin-by-bin variation of affected histogram.
Constrained bin-by-bin variation of affected histogram.
Constraint::Type GetConstraintType() const
< A class that holds configuration information for a model using a workspace as a store
Definition ModelConfig.h:34
Persistable container for RooFit projects.
RooAbsPdf * pdf(RooStringView name) const
Retrieve p.d.f (RooAbsPdf) with given name. A null pointer is returned if not found.
RooAbsArg * arg(RooStringView name) const
Return RooAbsArg with given name. A null pointer is returned if none is found.
bool import(const RooAbsArg &arg, const RooCmdArg &arg1={}, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}, const RooCmdArg &arg9={})
Import a RooAbsArg object, e.g.
const Double_t * GetArray() const
Definition TArrayD.h:43
Class to manage histogram axis.
Definition TAxis.h:32
Bool_t IsVariableBinSize() const
Definition TAxis.h:144
const char * GetTitle() const override
Returns title of object.
Definition TAxis.h:137
virtual Double_t GetBinCenter(Int_t bin) const
Return center of bin.
Definition TAxis.cxx:489
const TArrayD * GetXbins() const
Definition TAxis.h:138
Double_t GetXmax() const
Definition TAxis.h:142
Double_t GetXmin() const
Definition TAxis.h:141
Int_t GetNbins() const
Definition TAxis.h:127
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
TAxis * GetZaxis()
Definition TH1.h:573
virtual Int_t GetNbinsY() const
Definition TH1.h:542
virtual Int_t GetNbinsZ() const
Definition TH1.h:543
virtual Int_t GetDimension() const
Definition TH1.h:527
virtual void Reset(Option_t *option="")
Reset this histogram: contents, errors, etc.
Definition TH1.cxx:7324
TAxis * GetXaxis()
Definition TH1.h:571
virtual Int_t GetNbinsX() const
Definition TH1.h:541
TAxis * GetYaxis()
Definition TH1.h:572
Bool_t IsBinUnderflow(Int_t bin, Int_t axis=0) const
Return true if the bin is underflow.
Definition TH1.cxx:5392
Bool_t IsBinOverflow(Int_t bin, Int_t axis=0) const
Return true if the bin is overflow.
Definition TH1.cxx:5360
virtual Double_t GetBinContent(Int_t bin) const
Return content of bin number bin.
Definition TH1.cxx:5239
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:173
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1096
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2460
RooCmdArg RecycleConflictNodes(bool flag=true)
RooCmdArg Rename(const char *suffix)
RooCmdArg Conditional(const RooArgSet &pdfSet, const RooArgSet &depSet, bool depsAreCond=false)
RooConstVar & RooConst(double val)
RooCmdArg Index(RooCategory &icat)
RooCmdArg StoreError(const RooArgSet &aset)
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg Import(const char *state, TH1 &histo)
T * OwningPtr
An alias for raw pointers for indicating that the return type of a RooFit function is an owning point...
Definition Config.h:35
@ ObjectHandling
OwningPtr< T > makeOwningPtr(std::unique_ptr< T > &&ptr)
Internal helper to turn a std::unique_ptr<T> into an OwningPtr.
Definition Config.h:40
CreateGammaConstraintsOutput createGammaConstraints(RooArgList const &paramList, std::span< const double > relSigmas, double minSigma, Constraint::Type type)
Namespace for the RooStats classes.
Definition CodegenImpl.h:67
bool createPerRegionWorkspaces
Control whether individual workspace files are created for each channel (default true).
bool binnedFitOptimization
Enable or disable optimization for binned likelihood fits (default true).
bool storeDataError
Control whether errors on the data histograms are stored in the workspace (default false).