Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooMCStudy.cxx
Go to the documentation of this file.
1 /*****************************************************************************
2  * Project: RooFit *
3  * Package: RooFitCore *
4  * @(#)root/roofitcore:$Id$
5  * Authors: *
6  * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
7  * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
8  * *
9  * Copyright (c) 2000-2005, Regents of the University of California *
10  * and Stanford University. All rights reserved. *
11  * *
12  * Redistribution and use in source and binary forms, *
13  * with or without modification, are permitted according to the terms *
14  * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
15  *****************************************************************************/
16 
17 /**
18 \file RooMCStudy.cxx
19 \class RooMCStudy
20 \ingroup Roofitcore
21 
22 RooMCStudy is a helper class to facilitate Monte Carlo studies
23 such as 'goodness-of-fit' studies, that involve fitting a PDF
24 to multiple toy Monte Carlo sets. These may be generated from either same PDF
25 or from a different PDF with similar parameters.
26 
27 Given a fit and a generator PDF (they might be identical), RooMCStudy can produce
28 toyMC samples and/or fit these.
29 It accumulates the post-fit parameters of each iteration in a dataset. These can be
30 retrieved using fitParams() or fitParDataSet(). This dataset additionally contains the
31 variables
32 - NLL: The value of the negative log-likelihood for each run.
33 - ngen: The number of events generated for each run.
34 
35 Additional plotting routines simplify the task of plotting
36 the distribution of the minimized likelihood, the fitted parameter values,
37 fitted error and pull distribution.
38 
39 RooMCStudy provides the option to insert add-in modules
40 that modify the generate-and-fit cycle and allow to perform
41 extra steps in the cycle. Output of these modules can be stored
42 alongside the fit results in the aggregate results dataset.
43 These study modules should derive from the class RooAbsMCStudyModule.
44 
45 Check the RooFit tutorials
46 - rf801_mcstudy.C
47 - rf802_mcstudy_addons.C
48 - rf803_mcstudy_addons2.C
49 - rf804_mcstudy_constr.C
50 for usage examples.
51 **/
52 
53 
54 
55 #include "RooFit.h"
56 #include "Riostream.h"
57 
58 #include "RooMCStudy.h"
59 #include "RooAbsMCStudyModule.h"
60 
61 #include "RooGenContext.h"
62 #include "RooAbsPdf.h"
63 #include "RooDataSet.h"
64 #include "RooDataHist.h"
65 #include "RooRealVar.h"
66 #include "RooFitResult.h"
67 #include "RooErrorVar.h"
68 #include "RooFormulaVar.h"
69 #include "RooArgList.h"
70 #include "RooPlot.h"
71 #include "RooGenericPdf.h"
72 #include "RooRandom.h"
73 #include "RooCmdConfig.h"
74 #include "RooGlobalFunc.h"
75 #include "RooPullVar.h"
76 #include "RooMsgService.h"
77 #include "RooProdPdf.h"
78 
79 using namespace std ;
80 
81 ClassImp(RooMCStudy);
82  ;
83 
84 
85 /**
86 Construct Monte Carlo Study Manager. This class automates generating data from a given PDF,
87 fitting the PDF to data and accumulating the fit statistics.
88 
89 \param[in] model The PDF to be studied
90 \param[in] observables The variables of the PDF to be considered observables
91 \param[in] argX Arguments from the table below
92 
93 <table>
94 <tr><th> Optional arguments <th>
95 <tr><td> Silence() <td> Suppress all RooFit messages during running below PROGRESS level
96 <tr><td> FitModel(const RooAbsPdf&) <td> The PDF for fitting if it is different from the PDF for generating.
97 <tr><td> ConditionalObservables(const RooArgSet& set) <td> The set of observables that the PDF should _not_ be normalized over
98 <tr><td> Binned(Bool_t flag) <td> Bin the dataset before fitting it. Speeds up fitting of large data samples
99 <tr><td> FitOptions(const char*) <td> Classic fit options, provided for backward compatibility
100 <tr><td> FitOptions(....) <td> Options to be used for fitting. All named arguments inside FitOptions() are passed to RooAbsPdf::fitTo().
101  `Save()` is especially interesting to be able to retrieve fit results of each run using fitResult().
102 <tr><td> Verbose(Bool_t flag) <td> Activate informational messages in event generation phase
103 <tr><td> Extended(Bool_t flag) <td> Determine number of events for each sample anew from a Poisson distribution
104 <tr><td> Constrain(const RooArgSet& pars) <td> Apply internal constraints on given parameters in fit and sample constrained parameter values from constraint p.d.f for each toy.
105 <tr><td> ExternalConstraints(const RooArgSet& ) <td> Apply internal constraints on given parameters in fit and sample constrained parameter values from constraint p.d.f for each toy.
106 <tr><td> ProtoData(const RooDataSet&, Bool_t randOrder)
107  <td> Prototype data for the event generation. If the randOrder flag is set, the order of the dataset will be re-randomized for each generation
108  cycle to protect against systematic biases if the number of generated events does not exactly match the number of events in the prototype dataset
109  at the cost of reduced precision with mu equal to the specified number of events
110 </table>
111 */
112 RooMCStudy::RooMCStudy(const RooAbsPdf& model, const RooArgSet& observables,
113  const RooCmdArg& arg1, const RooCmdArg& arg2,
114  const RooCmdArg& arg3,const RooCmdArg& arg4,const RooCmdArg& arg5,
115  const RooCmdArg& arg6,const RooCmdArg& arg7,const RooCmdArg& arg8) : TNamed("mcstudy","mcstudy")
116 
117 {
118  // Stuff all arguments in a list
119  RooLinkedList cmdList;
120  cmdList.Add(const_cast<RooCmdArg*>(&arg1)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg2)) ;
121  cmdList.Add(const_cast<RooCmdArg*>(&arg3)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg4)) ;
122  cmdList.Add(const_cast<RooCmdArg*>(&arg5)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg6)) ;
123  cmdList.Add(const_cast<RooCmdArg*>(&arg7)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg8)) ;
124 
125  // Select the pdf-specific commands
126  RooCmdConfig pc(Form("RooMCStudy::RooMCStudy(%s)",model.GetName())) ;
127 
128  pc.defineObject("fitModel","FitModel",0,0) ;
129  pc.defineObject("condObs","ProjectedDependents",0,0) ;
130  pc.defineObject("protoData","PrototypeData",0,0) ;
131  pc.defineSet("cPars","Constrain",0,0) ;
132  pc.defineSet("extCons","ExternalConstraints",0,0) ;
133  pc.defineInt("silence","Silence",0,0) ;
134  pc.defineInt("randProtoData","PrototypeData",0,0) ;
135  pc.defineInt("verboseGen","Verbose",0,0) ;
136  pc.defineInt("extendedGen","Extended",0,0) ;
137  pc.defineInt("binGenData","Binned",0,0) ;
138  pc.defineString("fitOpts","FitOptions",0,"") ;
139  pc.defineInt("dummy","FitOptArgs",0,0) ;
140  pc.defineMutex("FitOptions","FitOptArgs") ; // can have either classic or new-style fit options
141  pc.defineMutex("Constrain","FitOptions") ; // constraints only work with new-style fit options
142  pc.defineMutex("ExternalConstraints","FitOptions") ; // constraints only work with new-style fit options
143 
144  // Process and check varargs
145  pc.process(cmdList) ;
146  if (!pc.ok(kTRUE)) {
147  // WVE do something here
148  throw std::string("RooMCStudy::RooMCStudy() Error in parsing arguments passed to contructor") ;
149  return ;
150  }
151 
152  // Save fit command options
153  if (pc.hasProcessed("FitOptArgs")) {
154  RooCmdArg* fitOptArg = static_cast<RooCmdArg*>(cmdList.FindObject("FitOptArgs")) ;
155  for (Int_t i=0 ; i<fitOptArg->subArgs().GetSize() ;i++) {
156  _fitOptList.Add(new RooCmdArg(static_cast<RooCmdArg&>(*fitOptArg->subArgs().At(i)))) ;
157  }
158  }
159 
160  // Decode command line arguments
161  _silence = pc.getInt("silence") ;
162  _verboseGen = pc.getInt("verboseGen") ;
163  _extendedGen = pc.getInt("extendedGen") ;
164  _binGenData = pc.getInt("binGenData") ;
165  _randProto = pc.getInt("randProtoData") ;
166 
167  // Process constraints specifications
168  const RooArgSet* cParsTmp = pc.getSet("cPars") ;
169  const RooArgSet* extCons = pc.getSet("extCons") ;
170 
171  RooArgSet* cPars = new RooArgSet ;
172  if (cParsTmp) {
173  cPars->add(*cParsTmp) ;
174  }
175 
176  // If constraints are specified, add to fit options
177  if (cPars) {
178  _fitOptList.Add(RooFit::Constrain(*cPars).Clone()) ;
179  }
180  if (extCons) {
181  _fitOptList.Add(RooFit::ExternalConstraints(*extCons).Clone()) ;
182  }
183 
184  // Make list of all constraints
185  RooArgSet allConstraints ;
186  RooArgSet consPars ;
187  if (cPars) {
188  RooArgSet* constraints = model.getAllConstraints(observables,*cPars,kTRUE) ;
189  if (constraints) {
190  allConstraints.add(*constraints) ;
191  delete constraints ;
192  }
193  }
194 
195  // Construct constraint p.d.f
196  if (allConstraints.getSize()>0) {
197  _constrPdf = new RooProdPdf("mcs_constr_prod","RooMCStudy constraints product",allConstraints) ;
198 
199  if (cPars) {
200  consPars.add(*cPars) ;
201  } else {
202  RooArgSet* params = model.getParameters(observables) ;
203  RooArgSet* cparams = _constrPdf->getObservables(*params) ;
204  consPars.add(*cparams) ;
205  delete params ;
206  delete cparams ;
207  }
208  _constrGenContext = _constrPdf->genContext(consPars,0,0,_verboseGen) ;
209 
210  _perExptGenParams = kTRUE ;
211 
212  coutI(Generation) << "RooMCStudy::RooMCStudy: INFO have pdf with constraints, will generate parameters from constraint pdf for each experiment" << endl ;
213 
214 
215  } else {
216  _constrPdf = 0 ;
217  _constrGenContext=0 ;
218 
219  _perExptGenParams = kFALSE ;
220  }
221 
222 
223  // Extract generator and fit models
224  _genModel = const_cast<RooAbsPdf*>(&model) ;
225  _genSample = 0 ;
226  RooAbsPdf* fitModel = static_cast<RooAbsPdf*>(pc.getObject("fitModel",0)) ;
227  _fitModel = fitModel ? fitModel : _genModel ;
228 
229  // Extract conditional observables and prototype data
230  _genProtoData = static_cast<RooDataSet*>(pc.getObject("protoData",0)) ;
231  if (pc.getObject("condObs",0)) {
232  _projDeps.add(static_cast<RooArgSet&>(*pc.getObject("condObs",0))) ;
233  }
234 
235  _dependents.add(observables) ;
236 
237  _allDependents.add(_dependents) ;
238  _fitOptions = pc.getString("fitOpts") ;
239  _canAddFitResults = kTRUE ;
240 
241  if (_extendedGen && _genProtoData && !_randProto) {
242  oocoutW(_fitModel,Generation) << "RooMCStudy::RooMCStudy: WARNING Using generator option 'e' (Poisson distribution of #events) together " << endl
243  << " with a prototype dataset implies incomplete sampling or oversampling of proto data." << endl
244  << " Use option \"r\" to randomize prototype dataset order and thus to randomize" << endl
245  << " the set of over/undersampled prototype events for each generation cycle." << endl ;
246  }
247 
248  _genParams = _genModel->getParameters(&_dependents) ;
249  if (!_binGenData) {
250  _genContext = _genModel->genContext(_dependents,_genProtoData,0,_verboseGen) ;
251  _genContext->attach(*_genParams) ;
252  } else {
253  _genContext = 0 ;
254  }
255 
256  _genInitParams = (RooArgSet*) _genParams->snapshot(kFALSE) ;
257 
258  // Store list of parameters and save initial values separately
259  _fitParams = _fitModel->getParameters(&_dependents) ;
260  _fitInitParams = (RooArgSet*) _fitParams->snapshot(kTRUE) ;
261 
262  _nExpGen = _extendedGen ? _genModel->expectedEvents(&_dependents) : 0 ;
263 
264  // Place holder for NLL
265  _nllVar = new RooRealVar("NLL","-log(Likelihood)",0) ;
266 
267  // Place holder for number of generated events
268  _ngenVar = new RooRealVar("ngen","number of generated events",0) ;
269 
270  // Create data set containing parameter values, errors and pulls
271  RooArgSet tmp2(*_fitParams) ;
272  tmp2.add(*_nllVar) ;
273  tmp2.add(*_ngenVar) ;
274 
275  // Mark all variable to store their errors in the dataset
276  tmp2.setAttribAll("StoreError",kTRUE) ;
277  tmp2.setAttribAll("StoreAsymError",kTRUE) ;
278  TString fpdName ;
279  if (_fitModel==_genModel) {
280  fpdName = Form("fitParData_%s",_fitModel->GetName()) ;
281  } else {
282  fpdName= Form("fitParData_%s_%s",_fitModel->GetName(),_genModel->GetName()) ;
283  }
284 
285  _fitParData = new RooDataSet(fpdName.Data(),"Fit Parameters DataSet",tmp2) ;
286  tmp2.setAttribAll("StoreError",kFALSE) ;
287  tmp2.setAttribAll("StoreAsymError",kFALSE) ;
288 
289  if (_perExptGenParams) {
290  _genParData = new RooDataSet("genParData","Generated Parameters dataset",*_genParams) ;
291  } else {
292  _genParData = 0 ;
293  }
294 
295  // Append proto variables to allDependents
296  if (_genProtoData) {
297  _allDependents.add(*_genProtoData->get(),kTRUE) ;
298  }
299 
300  // Call module initializers
301  list<RooAbsMCStudyModule*>::iterator iter ;
302  for (iter=_modList.begin() ; iter!= _modList.end() ; ++iter) {
303  Bool_t ok = (*iter)->doInitializeInstance(*this) ;
304  if (!ok) {
305  oocoutE(_fitModel,Generation) << "RooMCStudy::ctor: removing study module " << (*iter)->GetName() << " from analysis chain because initialization failed" << endl ;
306  iter = _modList.erase(iter) ;
307  }
308  }
309 
310 }
311 
312 
313 ////////////////////////////////////////////////////////////////////////////////
314 /// \deprecated PLEASE USE CONSTRUCTOR WITH NAMED ARGUMENTS. RETAINED FOR BACKWARD COMPATIBILY.
315 ///
316 /// Constructor with a generator and fit model. Both models may point
317 /// to the same object. The 'dependents' set of variables is generated
318 /// in the generator phase. The optional prototype dataset is passed to
319 /// the generator
320 ///
321 /// Available generator options
322 /// v - Verbose
323 /// e - Extended: use Poisson distribution for Nevts generated
324 ///
325 /// Available fit options
326 /// See RooAbsPdf::fitTo()
327 ///
328 
329 RooMCStudy::RooMCStudy(const RooAbsPdf& genModel, const RooAbsPdf& fitModel,
330  const RooArgSet& dependents, const char* genOptions,
331  const char* fitOptions, const RooDataSet* genProtoData,
332  const RooArgSet& projDeps) :
333  TNamed("mcstudy","mcstudy"),
334  _genModel((RooAbsPdf*)&genModel),
335  _genProtoData(genProtoData),
336  _projDeps(projDeps),
337  _constrPdf(0),
338  _constrGenContext(0),
339  _dependents(dependents),
340  _allDependents(dependents),
341  _fitModel((RooAbsPdf*)&fitModel),
342  _nllVar(0),
343  _ngenVar(0),
344  _genParData(0),
345  _fitOptions(fitOptions),
346  _canAddFitResults(kTRUE),
347  _perExptGenParams(0),
348  _silence(kFALSE)
349 {
350  // Decode generator options
351  TString genOpt(genOptions) ;
352  genOpt.ToLower() ;
353  _verboseGen = genOpt.Contains("v") ;
354  _extendedGen = genOpt.Contains("e") ;
355  _binGenData = genOpt.Contains("b") ;
356  _randProto = genOpt.Contains("r") ;
357 
358  if (_extendedGen && genProtoData && !_randProto) {
359  oocoutE(_fitModel,Generation) << "RooMCStudy::RooMCStudy: WARNING Using generator option 'e' (Poisson distribution of #events) together " << endl
360  << " with a prototype dataset implies incomplete sampling or oversampling of proto data." << endl
361  << " Use option \"r\" to randomize prototype dataset order and thus to randomize" << endl
362  << " the set of over/undersampled prototype events for each generation cycle." << endl ;
363  }
364 
365  if (!_binGenData) {
366  _genContext = genModel.genContext(dependents,genProtoData,0,_verboseGen) ;
367  } else {
368  _genContext = 0 ;
369  }
370  _genParams = _genModel->getParameters(&_dependents) ;
371  _genSample = 0 ;
372  RooArgSet* tmp = genModel.getParameters(&dependents) ;
373  _genInitParams = (RooArgSet*) tmp->snapshot(kFALSE) ;
374  delete tmp ;
375 
376  // Store list of parameters and save initial values separately
377  _fitParams = fitModel.getParameters(&dependents) ;
378  _fitInitParams = (RooArgSet*) _fitParams->snapshot(kTRUE) ;
379 
380  _nExpGen = _extendedGen ? genModel.expectedEvents(&dependents) : 0 ;
381 
382  // Place holder for NLL
383  _nllVar = new RooRealVar("NLL","-log(Likelihood)",0) ;
384 
385  // Place holder for number of generated events
386  _ngenVar = new RooRealVar("ngen","number of generated events",0) ;
387 
388  // Create data set containing parameter values, errors and pulls
389  RooArgSet tmp2(*_fitParams) ;
390  tmp2.add(*_nllVar) ;
391  tmp2.add(*_ngenVar) ;
392 
393  // Mark all variable to store their errors in the dataset
394  tmp2.setAttribAll("StoreError",kTRUE) ;
395  tmp2.setAttribAll("StoreAsymError",kTRUE) ;
396  _fitParData = new RooDataSet("fitParData","Fit Parameters DataSet",tmp2) ;
397  tmp2.setAttribAll("StoreError",kFALSE) ;
398  tmp2.setAttribAll("StoreAsymError",kFALSE) ;
399 
400  // Append proto variables to allDependents
401  if (genProtoData) {
402  _allDependents.add(*genProtoData->get(),kTRUE) ;
403  }
404 
405  // Call module initializers
406  list<RooAbsMCStudyModule*>::iterator iter ;
407  for (iter=_modList.begin() ; iter!= _modList.end() ; ++iter) {
408  Bool_t ok = (*iter)->doInitializeInstance(*this) ;
409  if (!ok) {
410  oocoutE(_fitModel,Generation) << "RooMCStudy::ctor: removing study module " << (*iter)->GetName() << " from analysis chain because initialization failed" << endl ;
411  iter = _modList.erase(iter) ;
412  }
413  }
414 
415 }
416 
417 
418 
419 ////////////////////////////////////////////////////////////////////////////////
420 
421 RooMCStudy::~RooMCStudy()
422 {
423  _genDataList.Delete() ;
424  _fitOptList.Delete() ;
425  delete _ngenVar ;
426  delete _fitParData ;
427  delete _genParData ;
428  delete _fitInitParams ;
429  delete _fitParams ;
430  delete _genInitParams ;
431  delete _genParams ;
432  delete _genContext ;
433  delete _nllVar ;
434  delete _constrPdf ;
435  delete _constrGenContext ;
436 }
437 
438 
439 
440 ////////////////////////////////////////////////////////////////////////////////
441 /// Insert given RooMCStudy add-on module to the processing chain
442 /// of this MCStudy object
443 
444 void RooMCStudy::addModule(RooAbsMCStudyModule& module)
445 {
446  module.doInitializeInstance(*this) ;
447  _modList.push_back(&module) ;
448 }
449 
450 
451 
452 ////////////////////////////////////////////////////////////////////////////////
453 /// Run engine method. Generate and/or fit, according to flags, 'nSamples' samples of 'nEvtPerSample' events.
454 /// If keepGenData is set, all generated data sets will be kept in memory and can be accessed
455 /// later via genData().
456 ///
457 /// When generating, data sets will be written out in ascii form if the pattern string is supplied
458 /// The pattern, which is a template for snprintf, should look something like "data/toymc_%04d.dat"
459 /// and should contain one integer field that encodes the sample serial number.
460 ///
461 /// When fitting only, data sets may optionally be read from ascii files, using the same file
462 /// pattern.
463 ///
464 
465 Bool_t RooMCStudy::run(Bool_t doGenerate, Bool_t DoFit, Int_t nSamples, Int_t nEvtPerSample, Bool_t keepGenData, const char* asciiFilePat)
466 {
467  RooFit::MsgLevel oldLevel(RooFit::FATAL) ;
468  if (_silence) {
469  oldLevel = RooMsgService::instance().globalKillBelow() ;
470  RooMsgService::instance().setGlobalKillBelow(RooFit::PROGRESS) ;
471  }
472 
473  list<RooAbsMCStudyModule*>::iterator iter ;
474  for (iter=_modList.begin() ; iter!= _modList.end() ; ++iter) {
475  (*iter)->initializeRun(nSamples) ;
476  }
477 
478  Int_t prescale = nSamples>100 ? Int_t(nSamples/100) : 1 ;
479 
480  while(nSamples--) {
481 
482  if (nSamples%prescale==0) {
483  oocoutP(_fitModel,Generation) << "RooMCStudy::run: " ;
484  if (doGenerate) ooccoutI(_fitModel,Generation) << "Generating " ;
485  if (doGenerate && DoFit) ooccoutI(_fitModel,Generation) << "and " ;
486  if (DoFit) ooccoutI(_fitModel,Generation) << "fitting " ;
487  ooccoutP(_fitModel,Generation) << "sample " << nSamples << endl ;
488  }
489 
490  _genSample = 0;
491  Bool_t existingData = kFALSE ;
492  if (doGenerate) {
493  // Generate sample
494  Int_t nEvt(nEvtPerSample) ;
495 
496  // Reset generator parameters to initial values
497  *_genParams = *_genInitParams ;
498 
499  // If constraints are present, sample generator values from constraints
500  if (_constrPdf) {
501  RooDataSet* tmp = _constrGenContext->generate(1) ;
502  *_genParams = *tmp->get() ;
503  delete tmp ;
504  }
505 
506  // Save generated parameters if required
507  if (_genParData) {
508  _genParData->add(*_genParams) ;
509  }
510 
511  // Call module before-generation hook
512  list<RooAbsMCStudyModule*>::iterator iter2 ;
513  for (iter2=_modList.begin() ; iter2!= _modList.end() ; ++iter2) {
514  (*iter2)->processBeforeGen(nSamples) ;
515  }
516 
517  if (_binGenData) {
518 
519  // Calculate the number of (extended) events for this run
520  if (_extendedGen) {
521  _nExpGen = _genModel->expectedEvents(&_dependents) ;
522  nEvt = RooRandom::randomGenerator()->Poisson(nEvtPerSample==0?_nExpGen:nEvtPerSample) ;
523  }
524 
525  // Binned generation
526  _genSample = _genModel->generateBinned(_dependents,nEvt) ;
527 
528  } else {
529 
530  // Calculate the number of (extended) events for this run
531  if (_extendedGen) {
532  _nExpGen = _genModel->expectedEvents(&_dependents) ;
533  nEvt = RooRandom::randomGenerator()->Poisson(nEvtPerSample==0?_nExpGen:nEvtPerSample) ;
534  }
535 
536  // Optional randomization of protodata for this run
537  if (_randProto && _genProtoData && _genProtoData->numEntries()!=nEvt) {
538  oocoutI(_fitModel,Generation) << "RooMCStudy: (Re)randomizing event order in prototype dataset (Nevt=" << nEvt << ")" << endl ;
539  Int_t* newOrder = _genModel->randomizeProtoOrder(_genProtoData->numEntries(),nEvt) ;
540  _genContext->setProtoDataOrder(newOrder) ;
541  delete[] newOrder ;
542  }
543 
544  coutP(Generation) << "RooMCStudy: now generating " << nEvt << " events" << endl ;
545 
546  // Actual generation of events
547  if (nEvt>0) {
548  _genSample = _genContext->generate(nEvt) ;
549  } else {
550  // Make empty dataset
551  _genSample = new RooDataSet("emptySample","emptySample",_dependents) ;
552  }
553  }
554 
555 
556  //} else if (asciiFilePat && &asciiFilePat) { //warning: the address of 'asciiFilePat' will always evaluate as 'true'
557  } else if (asciiFilePat) {
558 
559  // Load sample from ASCII file
560  char asciiFile[1024] ;
561  snprintf(asciiFile,1024,asciiFilePat,nSamples) ;
562  RooArgList depList(_allDependents) ;
563  _genSample = RooDataSet::read(asciiFile,depList,"q") ;
564 
565  } else {
566 
567  // Load sample from internal list
568  _genSample = (RooDataSet*) _genDataList.At(nSamples) ;
569  existingData = kTRUE ;
570  if (!_genSample) {
571  oocoutW(_fitModel,Generation) << "RooMCStudy::run: WARNING: Sample #" << nSamples << " not loaded, skipping" << endl ;
572  continue ;
573  }
574  }
575 
576  // Save number of generated events
577  _ngenVar->setVal(_genSample->sumEntries()) ;
578 
579  // Call module between generation and fitting hook
580  list<RooAbsMCStudyModule*>::iterator iter3 ;
581  for (iter3=_modList.begin() ; iter3!= _modList.end() ; ++iter3) {
582  (*iter3)->processBetweenGenAndFit(nSamples) ;
583  }
584 
585  if (DoFit) fitSample(_genSample) ;
586 
587  // Call module between generation and fitting hook
588  for (iter3=_modList.begin() ; iter3!= _modList.end() ; ++iter3) {
589  (*iter3)->processAfterFit(nSamples) ;
590  }
591 
592  // Optionally write to ascii file
593  if (doGenerate && asciiFilePat && *asciiFilePat) {
594  char asciiFile[1024] ;
595  snprintf(asciiFile,1024,asciiFilePat,nSamples) ;
596  RooDataSet* unbinnedData = dynamic_cast<RooDataSet*>(_genSample) ;
597  if (unbinnedData) {
598  unbinnedData->write(asciiFile) ;
599  } else {
600  coutE(InputArguments) << "RooMCStudy::run(" << GetName() << ") ERROR: ASCII writing of binned datasets is not supported" << endl ;
601  }
602  }
603 
604  // Add to list or delete
605  if (!existingData) {
606  if (keepGenData) {
607  _genDataList.Add(_genSample) ;
608  } else {
609  delete _genSample ;
610  }
611  }
612  }
613 
614  for (iter=_modList.begin() ; iter!= _modList.end() ; ++iter) {
615  RooDataSet* auxData = (*iter)->finalizeRun() ;
616  if (auxData) {
617  _fitParData->merge(auxData) ;
618  }
619  }
620 
621  _canAddFitResults = kFALSE ;
622 
623  if (_genParData) {
624  const RooArgSet* genPars = _genParData->get() ;
625  TIterator* iter2 = genPars->createIterator() ;
626  RooAbsArg* arg ;
627  while((arg=(RooAbsArg*)iter2->Next())) {
628  _genParData->changeObservableName(arg->GetName(),Form("%s_gen",arg->GetName())) ;
629  }
630  delete iter2 ;
631 
632  _fitParData->merge(_genParData) ;
633  }
634 
635  if (DoFit) calcPulls() ;
636 
637  if (_silence) {
638  RooMsgService::instance().setGlobalKillBelow(oldLevel) ;
639  }
640 
641  return kFALSE ;
642 }
643 
644 
645 
646 
647 
648 
649 ////////////////////////////////////////////////////////////////////////////////
650 /// Generate and fit 'nSamples' samples of 'nEvtPerSample' events.
651 /// If keepGenData is set, all generated data sets will be kept in memory and can be accessed
652 /// later via genData().
653 ///
654 /// Data sets will be written out in ascii form if the pattern string is supplied.
655 /// The pattern, which is a template for snprintf, should look something like "data/toymc_%04d.dat"
656 /// and should contain one integer field that encodes the sample serial number.
657 ///
658 
659 Bool_t RooMCStudy::generateAndFit(Int_t nSamples, Int_t nEvtPerSample, Bool_t keepGenData, const char* asciiFilePat)
660 {
661  // Clear any previous data in memory
662  _fitResList.Delete() ; // even though the fit results are owned by gROOT, we still want to scratch them here.
663  _genDataList.Delete() ;
664  _fitParData->reset() ;
665 
666  return run(kTRUE,kTRUE,nSamples,nEvtPerSample,keepGenData,asciiFilePat) ;
667 }
668 
669 
670 
671 ////////////////////////////////////////////////////////////////////////////////
672 /// Generate 'nSamples' samples of 'nEvtPerSample' events.
673 /// If keepGenData is set, all generated data sets will be kept in memory
674 /// and can be accessed later via genData().
675 ///
676 /// Data sets will be written out in ascii form if the pattern string is supplied.
677 /// The pattern, which is a template for snprintf, should look something like "data/toymc_%04d.dat"
678 /// and should contain one integer field that encodes the sample serial number.
679 ///
680 
681 Bool_t RooMCStudy::generate(Int_t nSamples, Int_t nEvtPerSample, Bool_t keepGenData, const char* asciiFilePat)
682 {
683  // Clear any previous data in memory
684  _genDataList.Delete() ;
685 
686  return run(kTRUE,kFALSE,nSamples,nEvtPerSample,keepGenData,asciiFilePat) ;
687 }
688 
689 
690 
691 ////////////////////////////////////////////////////////////////////////////////
692 /// Fit 'nSamples' datasets, which are read from ASCII files.
693 ///
694 /// The ascii file pattern, which is a template for snprintf, should look something like "data/toymc_%04d.dat"
695 /// and should contain one integer field that encodes the sample serial number.
696 ///
697 
698 Bool_t RooMCStudy::fit(Int_t nSamples, const char* asciiFilePat)
699 {
700  // Clear any previous data in memory
701  _fitResList.Delete() ; // even though the fit results are owned by gROOT, we still want to scratch them here.
702  _fitParData->reset() ;
703 
704  return run(kFALSE,kTRUE,nSamples,0,kFALSE,asciiFilePat) ;
705 }
706 
707 
708 
709 ////////////////////////////////////////////////////////////////////////////////
710 /// Fit 'nSamples' datasets, as supplied in 'dataSetList'
711 ///
712 
713 Bool_t RooMCStudy::fit(Int_t nSamples, TList& dataSetList)
714 {
715  // Clear any previous data in memory
716  _fitResList.Delete() ; // even though the fit results are owned by gROOT, we still want to scratch them here.
717  _genDataList.Delete() ;
718  _fitParData->reset() ;
719 
720  // Load list of data sets
721  TIterator* iter = dataSetList.MakeIterator() ;
722  RooAbsData* gset ;
723  while((gset=(RooAbsData*)iter->Next())) {
724  _genDataList.Add(gset) ;
725  }
726  delete iter ;
727 
728  return run(kFALSE,kTRUE,nSamples,0,kTRUE,0) ;
729 }
730 
731 
732 
733 ////////////////////////////////////////////////////////////////////////////////
734 /// Reset all fit parameters to the initial model
735 /// parameters at the time of the RooMCStudy constructor
736 
737 void RooMCStudy::resetFitParams()
738 {
739  *_fitParams = *_fitInitParams ;
740 }
741 
742 
743 
744 ////////////////////////////////////////////////////////////////////////////////
745 /// Internal function. Performs actual fit according to specifications
746 
747 RooFitResult* RooMCStudy::doFit(RooAbsData* genSample)
748 {
749  // Fit model to data set
750  TString fitOpt2(_fitOptions) ; fitOpt2.Append("r") ;
751  if (_silence) {
752  fitOpt2.Append("b") ;
753  }
754 
755  // Optionally bin dataset before fitting
756  RooAbsData* data ;
757  if (_binGenData) {
758  RooArgSet* depList = _fitModel->getObservables(genSample) ;
759  data = new RooDataHist(genSample->GetName(),genSample->GetTitle(),*depList,*genSample) ;
760  delete depList ;
761  } else {
762  data = genSample ;
763  }
764 
765  RooFitResult* fr ;
766  if (_fitOptList.GetSize()==0) {
767  if (_projDeps.getSize()>0) {
768  fr = (RooFitResult*) _fitModel->fitTo(*data,RooFit::ConditionalObservables(_projDeps),RooFit::FitOptions(fitOpt2)) ;
769  } else {
770  fr = (RooFitResult*) _fitModel->fitTo(*data,RooFit::FitOptions(fitOpt2)) ;
771  }
772  } else {
773  RooCmdArg save = RooFit::Save() ;
774  RooCmdArg condo = RooFit::ConditionalObservables(_projDeps) ;
775  RooCmdArg plevel = RooFit::PrintLevel(-1) ;
776  RooLinkedList fitOptList(_fitOptList) ;
777  fitOptList.Add(&save) ;
778  if (_projDeps.getSize()>0) {
779  fitOptList.Add(&condo) ;
780  }
781  if (_silence) {
782  fitOptList.Add(&plevel) ;
783  }
784  fr = (RooFitResult*) _fitModel->fitTo(*data,fitOptList) ;
785  }
786 
787  if (_binGenData) delete data ;
788 
789  return fr ;
790 }
791 
792 
793 
794 ////////////////////////////////////////////////////////////////////////////////
795 /// Redo fit on 'current' toy sample, or if genSample is not NULL
796 /// do fit on given sample instead
797 
798 RooFitResult* RooMCStudy::refit(RooAbsData* genSample)
799 {
800  if (!genSample) {
801  genSample = _genSample ;
802  }
803 
804  RooFitResult* fr(0) ;
805  if (genSample->sumEntries()>0) {
806  fr = doFit(genSample) ;
807  }
808 
809  return fr ;
810 }
811 
812 
813 
814 ////////////////////////////////////////////////////////////////////////////////
815 /// Internal method. Fit given dataset with fit model. If fit
816 /// converges (TMinuit status code zero) The fit results are appended
817 /// to the fit results dataset
818 ///
819 /// If the fit option "r" is supplied, the RooFitResult
820 /// objects will always be saved, regardless of the
821 /// fit status. RooFitResults objects can be retrieved
822 /// later via fitResult().
823 ///
824 
825 Bool_t RooMCStudy::fitSample(RooAbsData* genSample)
826 {
827  // Reset all fit parameters to their initial values
828  resetFitParams() ;
829 
830  // Perform actual fit
831  Bool_t ok ;
832  RooFitResult* fr(0) ;
833  if (genSample->sumEntries()>0) {
834  fr = doFit(genSample) ;
835  ok = (fr->status()==0) ;
836  } else {
837  ok = kFALSE ;
838  }
839 
840  // If fit converged, store parameters and NLL
841  if (ok) {
842  _nllVar->setVal(fr->minNll()) ;
843  RooArgSet tmp(*_fitParams) ;
844  tmp.add(*_nllVar) ;
845  tmp.add(*_ngenVar) ;
846 
847  _fitParData->add(tmp) ;
848  }
849 
850  // Store fit result if requested by user
851  Bool_t userSaveRequest = kFALSE ;
852  if (_fitOptList.GetSize()>0) {
853  if (_fitOptList.FindObject("Save")) userSaveRequest = kTRUE ;
854  } else {
855  if (_fitOptions.Contains("r")) userSaveRequest = kTRUE ;
856  }
857 
858  if (userSaveRequest) {
859  _fitResList.Add(fr) ;
860  } else {
861  delete fr ;
862  }
863 
864  return !ok ;
865 }
866 
867 
868 
869 ////////////////////////////////////////////////////////////////////////////////
870 /// Utility function to add fit result from external fit to this RooMCStudy
871 /// and process its results through the standard RooMCStudy statistics gathering tools.
872 /// This function allows users to run the toy MC generation and/or fitting
873 /// in a distributed way and to collect and analyze the results in a RooMCStudy
874 /// as if they were run locally.
875 ///
876 /// This method is only functional if this RooMCStudy object is cleanm, i.e. it was not used
877 /// to generate and/or fit any samples.
878 
879 Bool_t RooMCStudy::addFitResult(const RooFitResult& fr)
880 {
881  if (!_canAddFitResults) {
882  oocoutE(_fitModel,InputArguments) << "RooMCStudy::addFitResult: ERROR cannot add fit results in current state" << endl ;
883  return kTRUE ;
884  }
885 
886  // Transfer contents of fit result to fitParams ;
887  *_fitParams = RooArgSet(fr.floatParsFinal()) ;
888 
889  // If fit converged, store parameters and NLL
890  Bool_t ok = (fr.status()==0) ;
891  if (ok) {
892  _nllVar->setVal(fr.minNll()) ;
893  RooArgSet tmp(*_fitParams) ;
894  tmp.add(*_nllVar) ;
895  tmp.add(*_ngenVar) ;
896  _fitParData->add(tmp) ;
897  }
898 
899  // Store fit result if requested by user
900  if (_fitOptions.Contains("r")) {
901  _fitResList.Add((TObject*)&fr) ;
902  }
903 
904  return kFALSE ;
905 }
906 
907 
908 
909 ////////////////////////////////////////////////////////////////////////////////
910 /// Calculate the pulls for all fit parameters in
911 /// the fit results data set, and add them to that dataset.
912 
913 void RooMCStudy::calcPulls()
914 {
915  for (auto it = _fitParams->begin(); it != _fitParams->end(); ++it) {
916  const auto par = static_cast<RooRealVar*>(*it);
917  RooErrorVar* err = par->errorVar();
918  _fitParData->addColumn(*err);
919  delete err;
920 
921  TString name(par->GetName()), title(par->GetTitle()) ;
922  name.Append("pull") ;
923  title.Append(" Pull") ;
924 
925  if (!par->hasError(false)) {
926  coutW(Generation) << "Fit parameter '" << par->GetName() << "' does not have an error."
927  " A pull distribution cannot be generated. This might be caused by the parameter being constant or"
928  " because the fits were not run." << std::endl;
929  continue;
930  }
931 
932  // First look in fitParDataset to see if per-experiment generated value has been stored
933  auto genParOrig = static_cast<RooAbsReal*>(_fitParData->get()->find(Form("%s_gen",par->GetName())));
934  if (genParOrig && _perExptGenParams) {
935 
936  RooPullVar pull(name,title,*par,*genParOrig) ;
937  _fitParData->addColumn(pull,kFALSE) ;
938 
939  } else {
940  // If not use fixed generator value
941  genParOrig = static_cast<RooAbsReal*>(_genInitParams->find(par->GetName()));
942 
943  if (!genParOrig) {
944  std::size_t index = it - _fitParams->begin();
945  genParOrig = index < _genInitParams->size() ?
946  static_cast<RooAbsReal*>((*_genInitParams)[index]) :
947  nullptr;
948 
949  if (genParOrig) {
950  coutW(Generation) << "The fit parameter '" << par->GetName() << "' is not in the model that was used to generate toy data. "
951  "The parameter '" << genParOrig->GetName() << "'=" << genParOrig->getVal() << " was found at the same position in the generator model."
952  " It will be used to compute pulls."
953  "\nIf this is not desired, the parameters of the generator model need to be renamed or reordered." << std::endl;
954  }
955  }
956 
957  if (genParOrig) {
958  std::unique_ptr<RooAbsReal> genPar(static_cast<RooAbsReal*>(genParOrig->Clone("truth")));
959  RooPullVar pull(name,title,*par,*genPar);
960 
961  _fitParData->addColumn(pull,kFALSE) ;
962  } else {
963  coutE(Generation) << "Cannot generate pull distribution for the fit parameter '" << par->GetName() << "'."
964  "\nNo similar parameter was found in the set of parameters that were used to generate toy data." << std::endl;
965  }
966  }
967  }
968 }
969 
970 
971 
972 
973 ////////////////////////////////////////////////////////////////////////////////
974 /// Return a RooDataSet containing the post-fit parameters of each toy cycle.
975 /// This dataset also contains any additional output that was generated
976 /// by study modules that were added to this RooMCStudy.
977 /// By default, the two following variables are added (apart from fit parameters):
978 /// - NLL: The value of the negative log-likelihood for each run.
979 /// - ngen: Number of events generated for each run.
980 const RooDataSet& RooMCStudy::fitParDataSet()
981 {
982  if (_canAddFitResults) {
983  calcPulls() ;
984  _canAddFitResults = kFALSE ;
985  }
986 
987  return *_fitParData ;
988 }
989 
990 
991 
992 ////////////////////////////////////////////////////////////////////////////////
993 /// Return an argset with the fit parameters for the given sample number
994 ///
995 /// NB: The fit parameters are only stored for successfull fits,
996 /// thus the maximum sampleNum can be less that the number
997 /// of generated samples and if so, the indeces will
998 /// be out of synch with genData() and fitResult()
999 
1000 const RooArgSet* RooMCStudy::fitParams(Int_t sampleNum) const
1001 {
1002  // Check if sampleNum is in range
1003  if (sampleNum<0 || sampleNum>=_fitParData->numEntries()) {
1004  oocoutE(_fitModel,InputArguments) << "RooMCStudy::fitParams: ERROR, invalid sample number: " << sampleNum << endl ;
1005  return 0 ;
1006  }
1007 
1008  return _fitParData->get(sampleNum) ;
1009 }
1010 
1011 
1012 
1013 ////////////////////////////////////////////////////////////////////////////////
1014 /// Return the RooFitResult of the fit with the given run number.
1015 ///
1016 /// \note Fit results are not saved by default. This requires passing `FitOptions(Save(), ...)`
1017 /// to the constructor.
1018 const RooFitResult* RooMCStudy::fitResult(Int_t sampleNum) const
1019 {
1020  // Check if sampleNum is in range
1021  if (sampleNum<0 || sampleNum>=_fitResList.GetSize()) {
1022  oocoutE(_fitModel,InputArguments) << "RooMCStudy::fitResult: ERROR, invalid sample number: " << sampleNum << endl ;
1023  return 0 ;
1024  }
1025 
1026  // Retrieve fit result object
1027  const RooFitResult* fr = (RooFitResult*) _fitResList.At(sampleNum) ;
1028  if (fr) {
1029  return fr ;
1030  } else {
1031  oocoutE(_fitModel,InputArguments) << "RooMCStudy::fitResult: ERROR, no fit result saved for sample "
1032  << sampleNum << ", did you use the 'r; fit option?" << endl ;
1033  }
1034  return 0 ;
1035 }
1036 
1037 
1038 
1039 ////////////////////////////////////////////////////////////////////////////////
1040 /// Return the given generated dataset. This method will only return datasets
1041 /// if during the run cycle it was indicated that generator data should be saved.
1042 
1043 RooAbsData* RooMCStudy::genData(Int_t sampleNum) const
1044 {
1045  // Check that generated data was saved
1046  if (_genDataList.GetSize()==0) {
1047  oocoutE(_fitModel,InputArguments) << "RooMCStudy::genData() ERROR, generated data was not saved" << endl ;
1048  return 0 ;
1049  }
1050 
1051  // Check if sampleNum is in range
1052  if (sampleNum<0 || sampleNum>=_genDataList.GetSize()) {
1053  oocoutE(_fitModel,InputArguments) << "RooMCStudy::genData() ERROR, invalid sample number: " << sampleNum << endl ;
1054  return 0 ;
1055  }
1056 
1057  return (RooAbsData*) _genDataList.At(sampleNum) ;
1058 }
1059 
1060 
1061 
1062 ////////////////////////////////////////////////////////////////////////////////
1063 /// Plot the distribution of fitted values of a parameter. The parameter shown is the one from which the RooPlot
1064 /// was created, e.g.
1065 ///
1066 /// RooPlot* frame = param.frame(100,-10,10) ;
1067 /// mcstudy.paramOn(frame,LineStyle(kDashed)) ;
1068 ///
1069 /// Any named arguments passed to plotParamOn() are forwarded to the underlying plotOn() call
1070 
1071 RooPlot* RooMCStudy::plotParamOn(RooPlot* frame, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3, const RooCmdArg& arg4,
1072  const RooCmdArg& arg5, const RooCmdArg& arg6, const RooCmdArg& arg7, const RooCmdArg& arg8)
1073 {
1074  _fitParData->plotOn(frame,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) ;
1075  return frame ;
1076 }
1077 
1078 
1079 
1080 ////////////////////////////////////////////////////////////////////////////////
1081 /// Plot the distribution of the fitted value of the given parameter on a newly created frame.
1082 ///
1083 /// <table>
1084 /// <tr><th> Optional arguments <th>
1085 /// <tr><td> FrameRange(double lo, double hi) <td> Set range of frame to given specification
1086 /// <tr><td> FrameBins(int bins) <td> Set default number of bins of frame to given number
1087 /// <tr><td> Frame() <td> Pass supplied named arguments to RooAbsRealLValue::frame() function. See there
1088 /// for list of allowed arguments
1089 /// </table>
1090 /// If no frame specifications are given, the AutoRange() feature will be used to set the range
1091 /// Any other named argument is passed to the RooAbsData::plotOn() call. See that function for allowed options
1092 
1093 RooPlot* RooMCStudy::plotParam(const char* paramName, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3, const RooCmdArg& arg4,
1094  const RooCmdArg& arg5, const RooCmdArg& arg6, const RooCmdArg& arg7, const RooCmdArg& arg8)
1095 {
1096 
1097  // Find parameter in fitParDataSet
1098  RooRealVar* param = static_cast<RooRealVar*>(_fitParData->get()->find(paramName)) ;
1099  if (!param) {
1100  oocoutE(_fitModel,InputArguments) << "RooMCStudy::plotParam: ERROR: no parameter defined with name " << paramName << endl ;
1101  return 0 ;
1102  }
1103 
1104  // Forward to implementation below
1105  return plotParam(*param,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) ;
1106 }
1107 
1108 
1109 
1110 ////////////////////////////////////////////////////////////////////////////////
1111 /// Plot the distribution of the fitted value of the given parameter on a newly created frame.
1112 /// \copydetails RooMCStudy::plotParam(const char* paramName, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3, const RooCmdArg& arg4,
1113 /// const RooCmdArg& arg5, const RooCmdArg& arg6, const RooCmdArg& arg7, const RooCmdArg& arg8)
1114 
1115 RooPlot* RooMCStudy::plotParam(const RooRealVar& param, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3, const RooCmdArg& arg4,
1116  const RooCmdArg& arg5, const RooCmdArg& arg6, const RooCmdArg& arg7, const RooCmdArg& arg8)
1117 {
1118  // Stuff all arguments in a list
1119  RooLinkedList cmdList;
1120  cmdList.Add(const_cast<RooCmdArg*>(&arg1)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg2)) ;
1121  cmdList.Add(const_cast<RooCmdArg*>(&arg3)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg4)) ;
1122  cmdList.Add(const_cast<RooCmdArg*>(&arg5)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg6)) ;
1123  cmdList.Add(const_cast<RooCmdArg*>(&arg7)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg8)) ;
1124 
1125  RooPlot* frame = makeFrameAndPlotCmd(param, cmdList) ;
1126  if (frame) {
1127  _fitParData->plotOn(frame, cmdList) ;
1128  }
1129 
1130  return frame ;
1131 }
1132 
1133 
1134 
1135 ////////////////////////////////////////////////////////////////////////////////
1136 /// Plot the distribution of the -log(L) values on a newly created frame.
1137 ///
1138 /// <table>
1139 /// <tr><th> Optional arguments <th>
1140 /// <tr><td> FrameRange(double lo, double hi) <td> Set range of frame to given specification
1141 /// <tr><td> FrameBins(int bins) <td> Set default number of bins of frame to given number
1142 /// <tr><td> Frame() <td> Pass supplied named arguments to RooAbsRealLValue::frame() function. See there
1143 /// for list of allowed arguments
1144 /// </table>
1145 ///
1146 /// If no frame specifications are given, the AutoRange() feature will be used to set the range.
1147 /// Any other named argument is passed to the RooAbsData::plotOn() call. See that function for allowed options
1148 
1149 RooPlot* RooMCStudy::plotNLL(const RooCmdArg& arg1, const RooCmdArg& arg2,
1150  const RooCmdArg& arg3, const RooCmdArg& arg4,
1151  const RooCmdArg& arg5, const RooCmdArg& arg6,
1152  const RooCmdArg& arg7, const RooCmdArg& arg8)
1153 {
1154  return plotParam(*_nllVar,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) ;
1155 }
1156 
1157 
1158 
1159 ////////////////////////////////////////////////////////////////////////////////
1160 /// Plot the distribution of the fit errors for the specified parameter on a newly created frame.
1161 ///
1162 /// <table>
1163 /// <tr><th> Optional arguments <th>
1164 /// <tr><td> FrameRange(double lo, double hi) <td> Set range of frame to given specification
1165 /// <tr><td> FrameBins(int bins) <td> Set default number of bins of frame to given number
1166 /// <tr><td> Frame() <td> Pass supplied named arguments to RooAbsRealLValue::frame() function. See there
1167 /// for list of allowed arguments
1168 /// </table>
1169 ///
1170 /// If no frame specifications are given, the AutoRange() feature will be used to set a default range.
1171 /// Any other named argument is passed to the RooAbsData::plotOn() call. See that function for allowed options.
1172 
1173 RooPlot* RooMCStudy::plotError(const RooRealVar& param, const RooCmdArg& arg1, const RooCmdArg& arg2,
1174  const RooCmdArg& arg3, const RooCmdArg& arg4,
1175  const RooCmdArg& arg5, const RooCmdArg& arg6,
1176  const RooCmdArg& arg7, const RooCmdArg& arg8)
1177 {
1178  if (_canAddFitResults) {
1179  calcPulls() ;
1180  _canAddFitResults=kFALSE ;
1181  }
1182 
1183  RooErrorVar* evar = param.errorVar() ;
1184  RooRealVar* evar_rrv = static_cast<RooRealVar*>(evar->createFundamental()) ;
1185  RooPlot* frame = plotParam(*evar_rrv,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) ;
1186  delete evar_rrv ;
1187  delete evar ;
1188  return frame ;
1189 }
1190 
1191 
1192 
1193 ////////////////////////////////////////////////////////////////////////////////
1194 /// Plot the distribution of pull values for the specified parameter on a newly created frame. If asymmetric
1195 /// errors are calculated in the fit (by MINOS) those will be used in the pull calculation.
1196 ///
1197 /// If the parameters of the models for generation and fit differ, simple heuristics are used to find the
1198 /// corresponding parameters:
1199 /// - Parameters have the same name: They will be used to compute pulls.
1200 /// - Parameters have different names: The position of the fit parameter in the set of fit parameters will be
1201 /// computed. The parameter at the same position in the set of generator parameters will be used.
1202 ///
1203 /// Further options:
1204 /// <table>
1205 /// <tr><th> Arguments <th> Effect
1206 /// <tr><td> FrameRange(double lo, double hi) <td> Set range of frame to given specification
1207 /// <tr><td> FrameBins(int bins) <td> Set default number of bins of frame to given number
1208 /// <tr><td> Frame() <td> Pass supplied named arguments to RooAbsRealLValue::frame() function. See there
1209 /// for list of allowed arguments
1210 /// <tr><td> FitGauss(Bool_t flag) <td> Add a gaussian fit to the frame
1211 /// </table>
1212 ///
1213 /// If no frame specifications are given, the AutoSymRange() feature will be used to set a default range.
1214 /// Any other named argument is passed to the RooAbsData::plotOn(). See that function for allowed options.
1215 
1216 RooPlot* RooMCStudy::plotPull(const RooRealVar& param, const RooCmdArg& arg1, const RooCmdArg& arg2,
1217  const RooCmdArg& arg3, const RooCmdArg& arg4,
1218  const RooCmdArg& arg5, const RooCmdArg& arg6,
1219  const RooCmdArg& arg7, const RooCmdArg& arg8)
1220 {
1221  // Stuff all arguments in a list
1222  RooLinkedList cmdList;
1223  cmdList.Add(const_cast<RooCmdArg*>(&arg1)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg2)) ;
1224  cmdList.Add(const_cast<RooCmdArg*>(&arg3)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg4)) ;
1225  cmdList.Add(const_cast<RooCmdArg*>(&arg5)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg6)) ;
1226  cmdList.Add(const_cast<RooCmdArg*>(&arg7)) ; cmdList.Add(const_cast<RooCmdArg*>(&arg8)) ;
1227 
1228  TString name(param.GetName()), title(param.GetTitle()) ;
1229  name.Append("pull") ; title.Append(" Pull") ;
1230  RooRealVar pvar(name,title,-100,100) ;
1231  pvar.setBins(100) ;
1232 
1233 
1234  RooPlot* frame = makeFrameAndPlotCmd(pvar, cmdList, kTRUE) ;
1235  if (frame) {
1236 
1237  // Pick up optonal FitGauss command from list
1238  RooCmdConfig pc(Form("RooMCStudy::plotPull(%s)",_genModel->GetName())) ;
1239  pc.defineInt("fitGauss","FitGauss",0,0) ;
1240  pc.allowUndefined() ;
1241  pc.process(cmdList) ;
1242  Bool_t fitGauss=pc.getInt("fitGauss") ;
1243 
1244  // Pass stripped command list to plotOn()
1245  pc.stripCmdList(cmdList,"FitGauss") ;
1246  const bool success = _fitParData->plotOn(frame,cmdList) ;
1247 
1248  if (!success) {
1249  coutF(Plotting) << "No pull distribution for the parameter '" << param.GetName() << "'. Check logs for errors." << std::endl;
1250  return frame;
1251  }
1252 
1253  // Add Gaussian fit if requested
1254  if (fitGauss) {
1255  RooRealVar pullMean("pullMean","Mean of pull",0,-10,10) ;
1256  RooRealVar pullSigma("pullSigma","Width of pull",1,0.1,5) ;
1257  RooGenericPdf pullGauss("pullGauss","Gaussian of pull",
1258  "exp(-0.5*(@0-@1)*(@0-@1)/(@2*@2))",
1259  RooArgSet(pvar,pullMean,pullSigma)) ;
1260  pullGauss.fitTo(*_fitParData,RooFit::Minos(0),RooFit::PrintLevel(-1)) ;
1261  pullGauss.plotOn(frame) ;
1262  pullGauss.paramOn(frame,_fitParData) ;
1263  }
1264  }
1265  return frame;
1266 }
1267 
1268 
1269 
1270 ////////////////////////////////////////////////////////////////////////////////
1271 /// Internal function. Construct RooPlot from given parameter and modify the list of named
1272 /// arguments 'cmdList' to only contain the plot arguments that should be forwarded to
1273 /// RooAbsData::plotOn()
1274 
1275 RooPlot* RooMCStudy::makeFrameAndPlotCmd(const RooRealVar& param, RooLinkedList& cmdList, Bool_t symRange) const
1276 {
1277  // Select the frame-specific commands
1278  RooCmdConfig pc(Form("RooMCStudy::plotParam(%s)",_genModel->GetName())) ;
1279  pc.defineInt("nbins","Bins",0,0) ;
1280  pc.defineDouble("xlo","Range",0,0) ;
1281  pc.defineDouble("xhi","Range",1,0) ;
1282  pc.defineInt("dummy","FrameArgs",0,0) ;
1283  pc.defineMutex("Bins","FrameArgs") ;
1284  pc.defineMutex("Range","FrameArgs") ;
1285 
1286  // Process and check varargs
1287  pc.allowUndefined() ;
1288  pc.process(cmdList) ;
1289  if (!pc.ok(kTRUE)) {
1290  return 0 ;
1291  }
1292 
1293  // Make frame according to specs
1294  Int_t nbins = pc.getInt("nbins") ;
1295  Double_t xlo = pc.getDouble("xlo") ;
1296  Double_t xhi = pc.getDouble("xhi") ;
1297  RooPlot* frame ;
1298 
1299  if (pc.hasProcessed("FrameArgs")) {
1300  // Explicit frame arguments are given, pass them on
1301  RooCmdArg* frameArg = static_cast<RooCmdArg*>(cmdList.FindObject("FrameArgs")) ;
1302  frame = param.frame(frameArg->subArgs()) ;
1303  } else {
1304  // FrameBins, FrameRange or none are given, build custom frame command list
1305  RooCmdArg bins = RooFit::Bins(nbins) ;
1306  RooCmdArg range = RooFit::Range(xlo,xhi) ;
1307  RooCmdArg autor = symRange ? RooFit::AutoSymRange(*_fitParData,0.2) : RooFit::AutoRange(*_fitParData,0.2) ;
1308  RooLinkedList frameCmdList ;
1309 
1310  if (pc.hasProcessed("Bins")) frameCmdList.Add(&bins) ;
1311  if (pc.hasProcessed("Range")) {
1312  frameCmdList.Add(&range) ;
1313  } else {
1314  frameCmdList.Add(&autor) ;
1315  }
1316  frame = param.frame(frameCmdList) ;
1317  }
1318 
1319  // Filter frame command from list and pass on to plotOn()
1320  pc.stripCmdList(cmdList,"FrameArgs,Bins,Range") ;
1321 
1322  return frame ;
1323 }
1324 
1325 
1326 
1327 ////////////////////////////////////////////////////////////////////////////////
1328 /// Create a RooPlot of the -log(L) distribution in the range lo-hi
1329 /// with 'nBins' bins
1330 
1331 RooPlot* RooMCStudy::plotNLL(Double_t lo, Double_t hi, Int_t nBins)
1332 {
1333  RooPlot* frame = _nllVar->frame(lo,hi,nBins) ;
1334 
1335  _fitParData->plotOn(frame) ;
1336  return frame ;
1337 }
1338 
1339 
1340 
1341 ////////////////////////////////////////////////////////////////////////////////
1342 /// Create a RooPlot of the distribution of the fitted errors of the given parameter.
1343 /// The frame is created with a range [lo,hi] and plotted data will be binned in 'nbins' bins
1344 
1345 RooPlot* RooMCStudy::plotError(const RooRealVar& param, Double_t lo, Double_t hi, Int_t nbins)
1346 {
1347  if (_canAddFitResults) {
1348  calcPulls() ;
1349  _canAddFitResults=kFALSE ;
1350  }
1351 
1352  RooErrorVar* evar = param.errorVar() ;
1353  RooPlot* frame = evar->frame(lo,hi,nbins) ;
1354  _fitParData->plotOn(frame) ;
1355 
1356  delete evar ;
1357  return frame ;
1358 }
1359 
1360 
1361 
1362 ////////////////////////////////////////////////////////////////////////////////
1363 /// Create a RooPlot of the pull distribution for the given
1364 /// parameter. The range lo-hi is plotted in nbins. If fitGauss is
1365 /// set, an unbinned ML fit of the distribution to a Gaussian p.d.f
1366 /// is performed. The fit result is overlaid on the returned RooPlot
1367 /// and a box with the fitted mean and sigma is added.
1368 ///
1369 /// If the parameters of the models for generation and fit differ, simple heuristics are used to find the
1370 /// corresponding parameters:
1371 /// - Parameters have the same name: They will be used to compute pulls.
1372 /// - Parameters have different names: The position of the fit parameter in the set of fit parameters will be
1373 /// computed. The parameter at the same position in the set of generator parameters will be used.
1374 
1375 RooPlot* RooMCStudy::plotPull(const RooRealVar& param, Double_t lo, Double_t hi, Int_t nbins, Bool_t fitGauss)
1376 {
1377  if (_canAddFitResults) {
1378  calcPulls() ;
1379  _canAddFitResults=kFALSE ;
1380  }
1381 
1382 
1383  TString name(param.GetName()), title(param.GetTitle()) ;
1384  name.Append("pull") ; title.Append(" Pull") ;
1385  RooRealVar pvar(name,title,lo,hi) ;
1386  pvar.setBins(nbins) ;
1387 
1388  RooPlot* frame = pvar.frame() ;
1389  const bool success = _fitParData->plotOn(frame);
1390 
1391  if (!success) {
1392  coutF(Plotting) << "No pull distribution for the parameter '" << param.GetName() << "'. Check logs for errors." << std::endl;
1393  return frame;
1394  }
1395 
1396  if (fitGauss) {
1397  RooRealVar pullMean("pullMean","Mean of pull",0,lo,hi) ;
1398  RooRealVar pullSigma("pullSigma","Width of pull",1,0,5) ;
1399  RooGenericPdf pullGauss("pullGauss","Gaussian of pull",
1400  "exp(-0.5*(@0-@1)*(@0-@1)/(@2*@2))",
1401  RooArgSet(pvar,pullMean,pullSigma)) ;
1402  pullGauss.fitTo(*_fitParData,RooFit::Minos(0),RooFit::PrintLevel(-1)) ;
1403  pullGauss.plotOn(frame) ;
1404  pullGauss.paramOn(frame,_fitParData) ;
1405  }
1406 
1407  return frame ;
1408 }
1409 
1410 
1411 ////////////////////////////////////////////////////////////////////////////////
1412 /// If one of the TObject we have a referenced to is deleted, remove the
1413 /// reference.
1414 
1415 void RooMCStudy::RecursiveRemove(TObject *obj)
1416 {
1417  _fitResList.RecursiveRemove(obj);
1418  _genDataList.RecursiveRemove(obj);
1419  _fitOptList.RecursiveRemove(obj);
1420  if (_ngenVar == obj) _ngenVar = nullptr;
1421 
1422  if (_fitParData) _fitParData->RecursiveRemove(obj);
1423  if (_fitParData == obj) _fitParData = nullptr;
1424 
1425  if (_genParData) _genParData->RecursiveRemove(obj);
1426  if (_genParData == obj) _genParData = nullptr;
1427 }
1428