Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
Measurement.cxx
Go to the documentation of this file.
1 // @(#)root/roostats:$Id$
2 // Author: Kyle Cranmer, George Lewis
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 /** \class RooStats::HistFactory::Measurement
12  * \ingroup HistFactory
13 The RooStats::HistFactory::Measurement class can be used to construct a model
14 by combining multiple RooStats::HistFactory::Channel objects. It also allows
15 to set some general properties like the integrated luminosity, its relative
16 uncertainty or the functional form of constraints on nuisance parameters.
17 */
18 
19 
20 #include <ctime>
21 #include <iostream>
22 #include <algorithm>
23 #include <sys/stat.h>
24 #include "TSystem.h"
25 #include "TTimeStamp.h"
26 
29 
30 using namespace std;
31 
32 ClassImp(RooStats::HistFactory::Measurement); ;
33 
34 /// Standard constructor
35 RooStats::HistFactory::Measurement::Measurement() :
36  fPOI(), fLumi( 1.0 ), fLumiRelErr( .10 ),
37  fBinLow( 0 ), fBinHigh( 1 ), fExportOnly( false )
38 {
39 
40 }
41 
42 /*
43 RooStats::HistFactory::Measurement::Measurement(const Measurement& other) :
44  POI( other.POI ), Lumi( other.Lumi ), LumiRelErr( other.LumiRelErr ),
45  BinLow( other.BinLow ), BinHigh( other.BinHigh ), ExportOnly( other.ExportOnly ),
46  channels( other.channels ), OutputFilePrefix( other.outputFilePrefix ),
47  constantParams( other.constantParams ), { ; }
48 */
49 
50 /// Standard constructor specifying name and title of measurement
51 RooStats::HistFactory::Measurement::Measurement(const char* Name, const char* Title) :
52  TNamed( Name, Title ),
53  fPOI(), fLumi( 1.0 ), fLumiRelErr( .10 ),
54  fBinLow( 0 ), fBinHigh( 1 ), fExportOnly( false )
55 {
56 
57 }
58 
59 
60 /// Set a parameter in the model to be constant.
61 /// the parameter does not have to exist yet, the information will be used when
62 /// the model is actually created.
63 ///
64 /// Also checks if the parameter is already set constant.
65 /// We don't need to set it constant twice,
66 /// and we issue a warning in case this is a hint
67 /// of a possible bug
68 void RooStats::HistFactory::Measurement::AddConstantParam( const std::string& param )
69 {
70 
71 
72  if( std::find(fConstantParams.begin(), fConstantParams.end(), param) != fConstantParams.end() ) {
73  std::cout << "Warning: Setting parameter: " << param
74  << " to constant, but it is already listed as constant. "
75  << "You may ignore this warning."
76  << std::endl;
77  return;
78  }
79 
80  fConstantParams.push_back( param );
81 
82 }
83 
84 
85 /// Set parameter of the model to given value
86 void RooStats::HistFactory::Measurement::SetParamValue( const std::string& param, double value )
87 {
88  // Check if this parameter is already set to a value
89  // If so, issue a warning
90  // (Not sure if we want to throw an exception here, or
91  // issue a warning and move along. Thoughts?)
92  if( fParamValues.find(param) != fParamValues.end() ) {
93  std::cout << "Warning: Chainging parameter: " << param
94  << " value from: " << fParamValues[param]
95  << " to: " << value
96  << std::endl;
97  }
98 
99  // Store the parameter and its value
100  std::cout << "Setting parameter: " << param
101  << " value to " << value
102  << std::endl;
103 
104  fParamValues[param] = value;
105 
106 }
107 
108 
109 /// Add a preprocessed function by giving the function a name,
110 /// a functional expression, and a string with a bracketed list of dependencies (eg "SigXsecOverSM[0,3]")
111 void RooStats::HistFactory::Measurement::AddPreprocessFunction( std::string name, std::string expression, std::string dependencies )
112 {
113 
114 
115  PreprocessFunction func(name, expression, dependencies);
116  AddFunctionObject(func);
117 
118 }
119 
120 /// Returns a list of defined preprocess function expressions
121 std::vector<std::string> RooStats::HistFactory::Measurement::GetPreprocessFunctions()
122 {
123 
124 
125  std::vector<std::string> PreprocessFunctionExpressions;
126  for( unsigned int i = 0; i < fFunctionObjects.size(); ++i ) {
127  std::string expression = fFunctionObjects.at(i).GetCommand();
128  PreprocessFunctionExpressions.push_back( expression );
129  }
130  return PreprocessFunctionExpressions;
131 }
132 
133 /// Set constraint term for given systematic to Gamma distribution
134 void RooStats::HistFactory::Measurement::AddGammaSyst(std::string syst, double uncert)
135 {
136  fGammaSyst[syst] = uncert;
137 }
138 
139 /// Set constraint term for given systematic to LogNormal distribution
140 void RooStats::HistFactory::Measurement::AddLogNormSyst(std::string syst, double uncert)
141 {
142  fLogNormSyst[syst] = uncert;
143 }
144 
145 /// Set constraint term for given systematic to uniform distribution
146 void RooStats::HistFactory::Measurement::AddUniformSyst(std::string syst)
147 {
148  fUniformSyst[syst] = 1.0; // Is this parameter simply a dummy?
149 }
150 
151 /// Define given systematics to have no external constraint
152 void RooStats::HistFactory::Measurement::AddNoSyst(std::string syst)
153 {
154  fNoSyst[syst] = 1.0; // dummy value
155 }
156 
157 /// Check if the given channel is part of this measurement
158 bool RooStats::HistFactory::Measurement::HasChannel( std::string ChanName )
159 {
160 
161 
162  for( unsigned int i = 0; i < fChannels.size(); ++i ) {
163 
164  Channel& chan = fChannels.at(i);
165  if( chan.GetName() == ChanName ) {
166  return true;
167  }
168 
169  }
170 
171  return false;
172 
173 }
174 
175 
176 /// Get channel with given name from this measurement
177 /// throws an exception in case the channel is not found
178 RooStats::HistFactory::Channel& RooStats::HistFactory::Measurement::GetChannel( std::string ChanName )
179 {
180  for( unsigned int i = 0; i < fChannels.size(); ++i ) {
181 
182  Channel& chan = fChannels.at(i);
183  if( chan.GetName() == ChanName ) {
184  return chan;
185  }
186 
187  }
188 
189  // If we get here, we didn't find the channel
190 
191  std::cout << "Error: Did not find channel: " << ChanName
192  << " in measurement: " << GetName() << std::endl;
193  throw hf_exc();
194 
195  // No Need to return after throwing exception
196  // return RooStats::HistFactory::BadChannel;
197 
198 
199 }
200 
201 /*
202  void RooStats::HistFactory::Measurement::Print( Option_t* option ) const {
203  RooStats::HistFactory::Measurement::Print( std::cout );
204  return;
205  }
206 */
207 
208 /// Print information about measurement object in tree-like structure to given stream
209 void RooStats::HistFactory::Measurement::PrintTree( std::ostream& stream )
210 {
211 
212 
213  stream << "Measurement Name: " << GetName()
214  << "\t OutputFilePrefix: " << fOutputFilePrefix
215  << "\t POI: ";
216  for(unsigned int i = 0; i < fPOI.size(); ++i) {
217  stream << fPOI.at(i);
218  }
219  stream << "\t Lumi: " << fLumi
220  << "\t LumiRelErr: " << fLumiRelErr
221  << "\t BinLow: " << fBinLow
222  << "\t BinHigh: " << fBinHigh
223  << "\t ExportOnly: " << fExportOnly
224  << std::endl;
225 
226 
227  if( fConstantParams.size() != 0 ) {
228  stream << "Constant Params: ";
229  for( unsigned int i = 0; i < fConstantParams.size(); ++i ) {
230  stream << " " << fConstantParams.at(i);
231  }
232  stream << std::endl;
233  }
234 
235  if( fFunctionObjects.size() != 0 ) {
236  stream << "Preprocess Functions: ";
237  for( unsigned int i = 0; i < fFunctionObjects.size(); ++i ) {
238  stream << " " << fFunctionObjects.at(i).GetCommand();
239  }
240  stream << std::endl;
241  }
242 
243  if( fChannels.size() != 0 ) {
244  stream << "Channels:" << std::endl;
245  for( unsigned int i = 0; i < fChannels.size(); ++i ) {
246  fChannels.at(i).Print( stream );
247  }
248  }
249 
250  std::cout << "End Measurement: " << GetName() << std::endl;
251 
252 }
253 
254 
255 /// Create XML files for this measurement in the given directory.
256 /// XML files can be configured with a different output prefix
257 /// Create an XML file for this measurement
258 /// First, create the XML driver
259 /// Then, create xml files for each channel
260 void RooStats::HistFactory::Measurement::PrintXML( std::string directory, std::string newOutputPrefix )
261 {
262  // First, check that the directory exists:
263  auto testExists = [](const std::string& theDirectory) {
264  void* dir = gSystem->OpenDirectory(theDirectory.c_str());
265  bool exists = dir != nullptr;
266  if (exists)
267  gSystem->FreeDirectory(dir);
268 
269  return exists;
270  };
271 
272  if ( !directory.empty() && !testExists(directory) ) {
273  int success = gSystem->MakeDirectory(directory.c_str() );
274  if( success != 0 ) {
275  std::cout << "Error: Failed to make directory: " << directory << std::endl;
276  throw hf_exc();
277  }
278  }
279 
280  // If supplied new Prefix, use that one:
281 
282  std::cout << "Printing XML Files for measurement: " << GetName() << std::endl;
283 
284  std::string XMLName = std::string(GetName()) + ".xml";
285  if( directory != "" ) XMLName = directory + "/" + XMLName;
286 
287  ofstream xml( XMLName.c_str() );
288 
289  if( ! xml.is_open() ) {
290  std::cout << "Error opening xml file: " << XMLName << std::endl;
291  throw hf_exc();
292  }
293 
294 
295  // Add the time
296  xml << "<!--" << std::endl;
297  xml << "This xml file created automatically on: " << std::endl;
298 /*
299  time_t t = time(0); // get time now
300  struct tm * now = localtime( &t );
301  xml << (now->tm_year + 1900) << '-'
302  << (now->tm_mon + 1) << '-'
303  << now->tm_mday
304  << std::endl;
305 */
306  // LM: use TTimeStamp
307  TTimeStamp t;
308  UInt_t year = 0;
309  UInt_t month = 0;
310  UInt_t day = 0;
311  t.GetDate(true, 0, &year, &month, &day);
312  xml << year << '-'
313  << month << '-'
314  << day
315  << std::endl;
316 
317  xml << "-->" << std::endl;
318 
319  // Add the doctype
320  xml << "<!DOCTYPE Combination SYSTEM 'HistFactorySchema.dtd'>" << std::endl << std::endl;
321 
322  // Add the combination name
323  if (newOutputPrefix.empty() ) newOutputPrefix = fOutputFilePrefix;
324  xml << "<Combination OutputFilePrefix=\"" << newOutputPrefix /*OutputFilePrefix*/ << "\" >" << std::endl << std::endl;
325 
326  // Add the Preprocessed Functions
327  for( unsigned int i = 0; i < fFunctionObjects.size(); ++i ) {
328  RooStats::HistFactory::PreprocessFunction func = fFunctionObjects.at(i);
329  func.PrintXML(xml);
330  /*
331  xml << "<Function Name=\"" << func.GetName() << "\" "
332  << "Expression=\"" << func.GetExpression() << "\" "
333  << "Dependents=\"" << func.GetDependents() << "\" "
334  << "/>" << std::endl;
335  */
336  }
337 
338  xml << std::endl;
339 
340  // Add the list of channels
341  for( unsigned int i = 0; i < fChannels.size(); ++i ) {
342  xml << " <Input>" << "./";
343  if (!directory.empty() ) xml << directory << "/";
344  xml << GetName() << "_" << fChannels.at(i).GetName() << ".xml" << "</Input>" << std::endl;
345  }
346 
347  xml << std::endl;
348 
349  // Open the Measurement, Set Lumi
350  xml << " <Measurement Name=\"" << GetName() << "\" "
351  << "Lumi=\"" << fLumi << "\" "
352  << "LumiRelErr=\"" << fLumiRelErr << "\" "
353  //<< "BinLow=\"" << fBinLow << "\" "
354  // << "BinHigh=\"" << fBinHigh << "\" "
355  << "ExportOnly=\"" << (fExportOnly ? std::string("True") : std::string("False")) << "\" "
356  << " >" << std::endl;
357 
358 
359  // Set the POI
360  xml << " <POI>" ;
361  for(unsigned int i = 0; i < fPOI.size(); ++i) {
362  if(i==0) xml << fPOI.at(i);
363  else xml << " " << fPOI.at(i);
364  }
365  xml << "</POI> " << std::endl;
366 
367  // Set the Constant Parameters
368  if(fConstantParams.size()) {
369  xml << " <ParamSetting Const=\"True\">";
370  for( unsigned int i = 0; i < fConstantParams.size(); ++i ) {
371  if (i==0) xml << fConstantParams.at(i);
372  else xml << " " << fConstantParams.at(i);;
373  }
374  xml << "</ParamSetting>" << std::endl;
375  }
376 
377  // Set the Parameters with new Constraint Terms
378  std::map<std::string, double>::iterator ConstrItr;
379 
380  // Gamma
381  for( ConstrItr = fGammaSyst.begin(); ConstrItr != fGammaSyst.end(); ++ConstrItr ) {
382  xml << "<ConstraintTerm Type=\"Gamma\" RelativeUncertainty=\""
383  << ConstrItr->second << "\">" << ConstrItr->first
384  << "</ConstraintTerm>" << std::endl;
385  }
386  // Uniform
387  for( ConstrItr = fUniformSyst.begin(); ConstrItr != fUniformSyst.end(); ++ConstrItr ) {
388  xml << "<ConstraintTerm Type=\"Uniform\" RelativeUncertainty=\""
389  << ConstrItr->second << "\">" << ConstrItr->first
390  << "</ConstraintTerm>" << std::endl;
391  }
392  // LogNormal
393  for( ConstrItr = fLogNormSyst.begin(); ConstrItr != fLogNormSyst.end(); ++ConstrItr ) {
394  xml << "<ConstraintTerm Type=\"LogNormal\" RelativeUncertainty=\""
395  << ConstrItr->second << "\">" << ConstrItr->first
396  << "</ConstraintTerm>" << std::endl;
397  }
398  // NoSyst
399  for( ConstrItr = fNoSyst.begin(); ConstrItr != fNoSyst.end(); ++ConstrItr ) {
400  xml << "<ConstraintTerm Type=\"NoSyst\" RelativeUncertainty=\""
401  << ConstrItr->second << "\">" << ConstrItr->first
402  << "</ConstraintTerm>" << std::endl;
403  }
404 
405 
406  // Close the Measurement
407  xml << " </Measurement> " << std::endl << std::endl;
408 
409  // Close the combination
410  xml << "</Combination>" << std::endl;
411 
412  xml.close();
413 
414  // Now, make the xml files
415  // for the individual channels:
416 
417  std::string prefix = std::string(GetName()) + "_";
418 
419  for( unsigned int i = 0; i < fChannels.size(); ++i ) {
420  fChannels.at(i).PrintXML( directory, prefix );
421  }
422 
423 
424  std::cout << "Finished printing XML files" << std::endl;
425 
426 }
427 
428 
429 /// A measurement, once fully configured, can be saved into a ROOT
430 /// file. This will persitify the Measurement object, along with any
431 /// channels and samples that have been added to it. It can then be
432 /// loaded, potentially modified, and used to create new models.
433 ///
434 /// Write every histogram to the file.
435 /// Edit the measurement to point to this file
436 /// and to point to each histogram in this file
437 /// Then write the measurement itself.
438 void RooStats::HistFactory::Measurement::writeToFile( TFile* file )
439 {
440 
441  // Create a tempory measurement
442  // (This is the one that is actually written)
443  RooStats::HistFactory::Measurement outMeas( *this );
444 
445  std::string OutputFileName = file->GetName();
446 
447  // Collect all histograms from file:
448  // HistCollector collector;
449 
450 
451  for( unsigned int chanItr = 0; chanItr < outMeas.fChannels.size(); ++chanItr ) {
452 
453  // Go to the main directory
454  // in the file
455  file->cd();
456  file->Flush();
457 
458  // Get the name of the channel:
459  RooStats::HistFactory::Channel& channel = outMeas.fChannels.at( chanItr );
460  std::string chanName = channel.GetName();
461 
462 
463  if( ! channel.CheckHistograms() ) {
464  std::cout << "Measurement.writeToFile(): Channel: " << chanName
465  << " has uninitialized histogram pointers" << std::endl;
466  throw hf_exc();
467  return;
468  }
469 
470  // Get and cache the histograms for this channel:
471  //collector.CollectHistograms( channel );
472  // Do I need this...?
473  // channel.CollectHistograms();
474 
475  // Make a directory to store the histograms
476  // for this channel
477 
478  TDirectory* chanDir = file->mkdir( (chanName + "_hists").c_str() );
479  if( chanDir == NULL ) {
480  std::cout << "Error: Cannot create channel " << (chanName + "_hists")
481  << std::endl;
482  throw hf_exc();
483  }
484  chanDir->cd();
485 
486  // Save the data:
487  TDirectory* dataDir = chanDir->mkdir( "data" );
488  if( dataDir == NULL ) {
489  std::cout << "Error: Cannot make directory " << chanDir << std::endl;
490  throw hf_exc();
491  }
492  dataDir->cd();
493 
494  channel.fData.writeToFile( OutputFileName, GetDirPath(dataDir) );
495 
496  /*
497  // Write the data file to this directory
498  TH1* hData = channel.data.GetHisto();
499  hData->Write();
500 
501  // Set the location of the data
502  // in the output measurement
503 
504  channel.data.InputFile = OutputFileName;
505  channel.data.HistoName = hData->GetName();
506  channel.data.HistoPath = GetDirPath( dataDir );
507  */
508 
509  // Loop over samples:
510 
511  for( unsigned int sampItr = 0; sampItr < channel.GetSamples().size(); ++sampItr ) {
512 
513  RooStats::HistFactory::Sample& sample = channel.GetSamples().at( sampItr );
514  std::string sampName = sample.GetName();
515 
516  std::cout << "Writing sample: " << sampName << std::endl;
517 
518  file->cd();
519  chanDir->cd();
520  TDirectory* sampleDir = chanDir->mkdir( sampName.c_str() );
521  if( sampleDir == NULL ) {
522  std::cout << "Error: Directory " << sampName << " not created properly" << std::endl;
523  throw hf_exc();
524  }
525  std::string sampleDirPath = GetDirPath( sampleDir );
526 
527  if( ! sampleDir ) {
528  std::cout << "Error making directory: " << sampName
529  << " in directory: " << chanName
530  << std::endl;
531  throw hf_exc();
532  }
533 
534  // Write the data file to this directory
535  sampleDir->cd();
536 
537  sample.writeToFile( OutputFileName, sampleDirPath );
538  /*
539  TH1* hSample = sample.GetHisto();
540  if( ! hSample ) {
541  std::cout << "Error getting histogram for sample: "
542  << sampName << std::endl;
543  throw -1;
544  }
545  sampleDir->cd();
546  hSample->Write();
547 
548  sample.InputFile = OutputFileName;
549  sample.HistoName = hSample->GetName();
550  sample.HistoPath = sampleDirPath;
551  */
552 
553  // Write the histograms associated with
554  // systematics
555 
556  /* THIS IS WHAT I"M COMMENTING
557  sample.GetStatError().writeToFile( OutputFileName, sampleDirPath );
558 
559  // Must write all systematics that contain internal histograms
560  // (This is not all systematics)
561 
562  for( unsigned int i = 0; i < sample.GetHistoSysList().size(); ++i ) {
563  sample.GetHistoSysList().at(i).writeToFile( OutputFileName, sampleDirPath );
564  }
565  for( unsigned int i = 0; i < sample.GetHistoFactorList().size(); ++i ) {
566  sample.GetHistoFactorList().at(i).writeToFile( OutputFileName, sampleDirPath );
567  }
568  for( unsigned int i = 0; i < sample.GetShapeSysList().size(); ++i ) {
569  sample.GetShapeSysList().at(i).writeToFile( OutputFileName, sampleDirPath );
570  }
571  END COMMENT */
572  /*
573  sample.statError.writeToFile( OutputFileName, sampleDirPath );
574 
575  // Now, get the Stat config histograms
576  if( sample.statError.HistoName != "" ) {
577  TH1* hStatError = sample.statError.GetErrorHist();
578  if( ! hStatError ) {
579  std::cout << "Error getting stat error histogram for sample: "
580  << sampName << std::endl;
581  throw -1;
582  }
583  hStatError->Write();
584 
585  sample.statError.InputFile = OutputFileName;
586  sample.statError.HistoName = hStatError->GetName();
587  sample.statError.HistoPath = sampleDirPath;
588 
589  }
590  */
591 
592  }
593 
594  }
595 
596 
597  // Finally, write the measurement itself:
598 
599  std::cout << "Saved all histograms" << std::endl;
600 
601  file->cd();
602  outMeas.Write();
603 
604  std::cout << "Saved Measurement" << std::endl;
605 
606 }
607 
608 /// Return the directory's path,
609 /// stripped of unnecessary prefixes
610 std::string RooStats::HistFactory::Measurement::GetDirPath( TDirectory* dir )
611 {
612 
613 
614  std::string path = dir->GetPath();
615 
616  if( path.find(":") != std::string::npos ) {
617  size_t index = path.find(":");
618  path.replace( 0, index+1, "" );
619  }
620 
621  path = path + "/";
622 
623  return path;
624 
625  /*
626  if( path.find(":") != std::string::npos ) {
627  size_t index = path.find(":");
628  SampleName.replace( 0, index, "" );
629  }
630 
631  // Remove the file:
632  */
633 
634 }
635 
636 
637 /// The most common way to add histograms to channels is to have them
638 /// stored in ROOT files and to give HistFactory the location of these
639 /// files. This means providing the path to the ROOT file and the path
640 /// and name of the histogram within that file. When providing these
641 /// in a script, HistFactory doesn't load the histogram from the file
642 /// right away. Instead, once all such histograms have been supplied,
643 /// one should run this method to open all ROOT files and to copy and
644 /// save all necessary histograms.
645 void RooStats::HistFactory::Measurement::CollectHistograms()
646 {
647 
648 
649  for( unsigned int chanItr = 0; chanItr < fChannels.size(); ++chanItr) {
650 
651  RooStats::HistFactory::Channel& chan = fChannels.at( chanItr );
652 
653  chan.CollectHistograms();
654 
655  }
656 
657 }
658 
659 
660