// @(#)root/roostats:$Id$ // Author: Kyle Cranmer, George Lewis /************************************************************************* * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ //_________________________________________________ /* BEGIN_HTML

The RooStats::HistFactory::Measurement class can be used to construct a model by combining multiple RooStats::HistFactory::Channel objects. It also allows to set some general properties like the integrated luminosity, its relative uncertainty or the functional form of constraints on nuisance parameters.

END_HTML */ // #include #include #include #include #include "TSystem.h" #include "TTimeStamp.h" #include "RooStats/HistFactory/Measurement.h" #include "RooStats/HistFactory/HistFactoryException.h" using namespace std; ClassImp(RooStats::HistFactory::Measurement) ; RooStats::HistFactory::Measurement::Measurement() : fPOI(), fLumi( 1.0 ), fLumiRelErr( .10 ), fBinLow( 0 ), fBinHigh( 1 ), fExportOnly( false ) { // standard constructor } /* RooStats::HistFactory::Measurement::Measurement(const Measurement& other) : POI( other.POI ), Lumi( other.Lumi ), LumiRelErr( other.LumiRelErr ), BinLow( other.BinLow ), BinHigh( other.BinHigh ), ExportOnly( other.ExportOnly ), channels( other.channels ), OutputFilePrefix( other.outputFilePrefix ), constantParams( other.constantParams ), { ; } */ RooStats::HistFactory::Measurement::Measurement(const char* Name, const char* Title) : TNamed( Name, Title ), fPOI(), fLumi( 1.0 ), fLumiRelErr( .10 ), fBinLow( 0 ), fBinHigh( 1 ), fExportOnly( false ) { // standard constructor specifying name and title of measurement } void RooStats::HistFactory::Measurement::AddConstantParam( const std::string& param ) { // set a parameter in the model to be constant // the parameter does not have to exist yet, the information will be used when // the model is actually created // Check if the parameter is already set constant // We don't need to set it constant twice, // and we issue a warning in case this is a hint // of a possible bug if( std::find(fConstantParams.begin(), fConstantParams.end(), param) != fConstantParams.end() ) { std::cout << "Warning: Setting parameter: " << param << " to constant, but it is already listed as constant. " << "You may ignore this warning." << std::endl; return; } fConstantParams.push_back( param ); } void RooStats::HistFactory::Measurement::SetParamValue( const std::string& param, double value ) { // set parameter of the model to given value // Check if this parameter is already set to a value // If so, issue a warning // (Not sure if we want to throw an exception here, or // issue a warning and move along. Thoughts?) if( fParamValues.find(param) != fParamValues.end() ) { std::cout << "Warning: Chainging parameter: " << param << " value from: " << fParamValues[param] << " to: " << value << std::endl; } // Store the parameter and its value std::cout << "Setting parameter: " << param << " value to " << value << std::endl; fParamValues[param] = value; } void RooStats::HistFactory::Measurement::AddPreprocessFunction( std::string name, std::string expression, std::string dependencies ) { // Add a preprocessed function by giving the function a name, // a functional expression, and a string with a bracketed list of dependencies (eg "SigXsecOverSM[0,3]") PreprocessFunction func(name, expression, dependencies); AddFunctionObject(func); } std::vector RooStats::HistFactory::Measurement::GetPreprocessFunctions() { // returns a list of defined proprocess function expressions std::vector PreprocessFunctionExpressions; for( unsigned int i = 0; i < fFunctionObjects.size(); ++i ) { std::string expression = fFunctionObjects.at(i).GetCommand(); PreprocessFunctionExpressions.push_back( expression ); } return PreprocessFunctionExpressions; } void RooStats::HistFactory::Measurement::AddGammaSyst(std::string syst, double uncert) { // set constraint term for given systematic to Gamma distribution fGammaSyst[syst] = uncert; } void RooStats::HistFactory::Measurement::AddLogNormSyst(std::string syst, double uncert) { // set constraint term for given systematic to LogNormal distribution fLogNormSyst[syst] = uncert; } void RooStats::HistFactory::Measurement::AddUniformSyst(std::string syst) { // set constraint term for given systematic to uniform distribution fUniformSyst[syst] = 1.0; // Is this parameter simply a dummy? } void RooStats::HistFactory::Measurement::AddNoSyst(std::string syst) { // define given systematics to have no external constraint fNoSyst[syst] = 1.0; // dummy value } bool RooStats::HistFactory::Measurement::HasChannel( std::string ChanName ) { // is the given channel part of this measurement for( unsigned int i = 0; i < fChannels.size(); ++i ) { Channel& chan = fChannels.at(i); if( chan.GetName() == ChanName ) { return true; } } return false; } RooStats::HistFactory::Channel& RooStats::HistFactory::Measurement::GetChannel( std::string ChanName ) { // get channel with given name from this measurement // throws an exception in case the channel is not found for( unsigned int i = 0; i < fChannels.size(); ++i ) { Channel& chan = fChannels.at(i); if( chan.GetName() == ChanName ) { return chan; } } // If we get here, we didn't find the channel std::cout << "Error: Did not find channel: " << ChanName << " in measurement: " << GetName() << std::endl; throw hf_exc(); // No Need to return after throwing exception // return RooStats::HistFactory::BadChannel; } /* void RooStats::HistFactory::Measurement::Print( Option_t* option ) const { RooStats::HistFactory::Measurement::Print( std::cout ); return; } */ void RooStats::HistFactory::Measurement::PrintTree( std::ostream& stream ) { // print information about measurement object in tree-like structure to given stream stream << "Measurement Name: " << GetName() << "\t OutputFilePrefix: " << fOutputFilePrefix << "\t POI: "; for(unsigned int i = 0; i < fPOI.size(); ++i) { stream << fPOI.at(i); } stream << "\t Lumi: " << fLumi << "\t LumiRelErr: " << fLumiRelErr << "\t BinLow: " << fBinLow << "\t BinHigh: " << fBinHigh << "\t ExportOnly: " << fExportOnly << std::endl; if( fConstantParams.size() != 0 ) { stream << "Constant Params: "; for( unsigned int i = 0; i < fConstantParams.size(); ++i ) { stream << " " << fConstantParams.at(i); } stream << std::endl; } if( fFunctionObjects.size() != 0 ) { stream << "Preprocess Functions: "; for( unsigned int i = 0; i < fFunctionObjects.size(); ++i ) { stream << " " << fFunctionObjects.at(i).GetCommand(); } stream << std::endl; } if( fChannels.size() != 0 ) { stream << "Channels:" << std::endl; for( unsigned int i = 0; i < fChannels.size(); ++i ) { fChannels.at(i).Print( stream ); } } std::cout << "End Measurement: " << GetName() << std::endl; } void RooStats::HistFactory::Measurement::PrintXML( std::string Directory, std::string NewOutputPrefix ) { // create XML files for this measurement in the given directory // XML files can be configured with a different output prefix // Create an XML file for this measurement // First, create the XML driver // Then, create xml files for each channel // First, check that the directory exists: // LM : fixes for Windows if( gSystem->OpenDirectory( Directory.c_str() ) == 0 ) { int success = gSystem->MakeDirectory(Directory.c_str() ); if( success != 0 ) { std::cout << "Error: Failed to make directory: " << Directory << std::endl; throw hf_exc(); } } // If supplied new Prefix, use that one: std::cout << "Printing XML Files for measurement: " << GetName() << std::endl; std::string XMLName = std::string(GetName()) + ".xml"; if( Directory != "" ) XMLName = Directory + "/" + XMLName; ofstream xml( XMLName.c_str() ); if( ! xml.is_open() ) { std::cout << "Error opening xml file: " << XMLName << std::endl; throw hf_exc(); } // Add the time xml << "" << std::endl; // Add the doctype xml << "" << std::endl << std::endl; // Add the combination name xml << "" << std::endl << std::endl; // Add the Preprocessed Functions for( unsigned int i = 0; i < fFunctionObjects.size(); ++i ) { RooStats::HistFactory::PreprocessFunction func = fFunctionObjects.at(i); func.PrintXML(xml); /* xml << "" << std::endl; */ } xml << std::endl; // Add the list of channels for( unsigned int i = 0; i < fChannels.size(); ++i ) { xml << " " << "./" << Directory << "/" << GetName() << "_" << fChannels.at(i).GetName() << ".xml" << "" << std::endl; } xml << std::endl; // Open the Measurement, Set Lumi xml << " " << std::endl; // Set the POI xml << " " ; for(unsigned int i = 0; i < fPOI.size(); ++i) { if(i==0) xml << fPOI.at(i); else xml << " " << fPOI.at(i); } xml << " " << std::endl; // Set the Constant Parameters if(fConstantParams.size()) { xml << " "; for( unsigned int i = 0; i < fConstantParams.size(); ++i ) { if (i==0) xml << fConstantParams.at(i); else xml << " " << fConstantParams.at(i);; } xml << "" << std::endl; } // Set the Parameters with new Constraint Terms std::map::iterator ConstrItr; // Gamma for( ConstrItr = fGammaSyst.begin(); ConstrItr != fGammaSyst.end(); ++ConstrItr ) { xml << "second << "\">" << ConstrItr->first << "" << std::endl; } // Uniform for( ConstrItr = fUniformSyst.begin(); ConstrItr != fUniformSyst.end(); ++ConstrItr ) { xml << "second << "\">" << ConstrItr->first << "" << std::endl; } // LogNormal for( ConstrItr = fLogNormSyst.begin(); ConstrItr != fLogNormSyst.end(); ++ConstrItr ) { xml << "second << "\">" << ConstrItr->first << "" << std::endl; } // NoSyst for( ConstrItr = fNoSyst.begin(); ConstrItr != fNoSyst.end(); ++ConstrItr ) { xml << "second << "\">" << ConstrItr->first << "" << std::endl; } // Close the Measurement xml << " " << std::endl << std::endl; // Close the combination xml << "" << std::endl; xml.close(); // Now, make the xml files // for the individual channels: std::string Prefix = std::string(GetName()) + "_"; for( unsigned int i = 0; i < fChannels.size(); ++i ) { fChannels.at(i).PrintXML( Directory, Prefix ); } std::cout << "Finished printing XML files" << std::endl; } void RooStats::HistFactory::Measurement::writeToFile( TFile* file ) { // A measurement, once fully configured, can be saved into a ROOT // file. This will persitify the Measurement object, along with any // channels and samples that have been added to it. It can then be // loaded, potentially modified, and used to create new models. // Write every histogram to the file. // Edit the measurement to point to this file // and to point to each histogram in this file // Then write the measurement itself. // Create a tempory measurement // (This is the one that is actually written) RooStats::HistFactory::Measurement outMeas( *this ); std::string OutputFileName = file->GetName(); // Collect all histograms from file: // HistCollector collector; for( unsigned int chanItr = 0; chanItr < outMeas.fChannels.size(); ++chanItr ) { // Go to the main directory // in the file file->cd(); file->Flush(); // Get the name of the channel: RooStats::HistFactory::Channel& channel = outMeas.fChannels.at( chanItr ); std::string chanName = channel.GetName(); if( ! channel.CheckHistograms() ) { std::cout << "Measurement.writeToFile(): Channel: " << chanName << " has uninitialized histogram pointers" << std::endl; throw hf_exc(); return; } // Get and cache the histograms for this channel: //collector.CollectHistograms( channel ); // Do I need this...? // channel.CollectHistograms(); // Make a directory to store the histograms // for this channel TDirectory* chanDir = file->mkdir( (chanName + "_hists").c_str() ); if( chanDir == NULL ) { std::cout << "Error: Cannot create channel " << (chanName + "_hists") << std::endl; throw hf_exc(); } chanDir->cd(); // Save the data: TDirectory* dataDir = chanDir->mkdir( "data" ); if( dataDir == NULL ) { std::cout << "Error: Cannot make directory " << chanDir << std::endl; throw hf_exc(); } dataDir->cd(); channel.fData.writeToFile( OutputFileName, GetDirPath(dataDir) ); /* // Write the data file to this directory TH1* hData = channel.data.GetHisto(); hData->Write(); // Set the location of the data // in the output measurement channel.data.InputFile = OutputFileName; channel.data.HistoName = hData->GetName(); channel.data.HistoPath = GetDirPath( dataDir ); */ // Loop over samples: for( unsigned int sampItr = 0; sampItr < channel.GetSamples().size(); ++sampItr ) { RooStats::HistFactory::Sample& sample = channel.GetSamples().at( sampItr ); std::string sampName = sample.GetName(); std::cout << "Writing sample: " << sampName << std::endl; file->cd(); chanDir->cd(); TDirectory* sampleDir = chanDir->mkdir( sampName.c_str() ); if( sampleDir == NULL ) { std::cout << "Error: Directory " << sampName << " not created properly" << std::endl; throw hf_exc(); } std::string sampleDirPath = GetDirPath( sampleDir ); if( ! sampleDir ) { std::cout << "Error making directory: " << sampName << " in directory: " << chanName << std::endl; throw hf_exc(); } // Write the data file to this directory sampleDir->cd(); sample.writeToFile( OutputFileName, sampleDirPath ); /* TH1* hSample = sample.GetHisto(); if( ! hSample ) { std::cout << "Error getting histogram for sample: " << sampName << std::endl; throw -1; } sampleDir->cd(); hSample->Write(); sample.InputFile = OutputFileName; sample.HistoName = hSample->GetName(); sample.HistoPath = sampleDirPath; */ // Write the histograms associated with // systematics /* THIS IS WHAT I"M COMMENTING sample.GetStatError().writeToFile( OutputFileName, sampleDirPath ); // Must write all systematics that contain internal histograms // (This is not all systematics) for( unsigned int i = 0; i < sample.GetHistoSysList().size(); ++i ) { sample.GetHistoSysList().at(i).writeToFile( OutputFileName, sampleDirPath ); } for( unsigned int i = 0; i < sample.GetHistoFactorList().size(); ++i ) { sample.GetHistoFactorList().at(i).writeToFile( OutputFileName, sampleDirPath ); } for( unsigned int i = 0; i < sample.GetShapeSysList().size(); ++i ) { sample.GetShapeSysList().at(i).writeToFile( OutputFileName, sampleDirPath ); } END COMMENT */ /* sample.statError.writeToFile( OutputFileName, sampleDirPath ); // Now, get the Stat config histograms if( sample.statError.HistoName != "" ) { TH1* hStatError = sample.statError.GetErrorHist(); if( ! hStatError ) { std::cout << "Error getting stat error histogram for sample: " << sampName << std::endl; throw -1; } hStatError->Write(); sample.statError.InputFile = OutputFileName; sample.statError.HistoName = hStatError->GetName(); sample.statError.HistoPath = sampleDirPath; } */ } } // Finally, write the measurement itself: std::cout << "Saved all histograms" << std::endl; file->cd(); outMeas.Write(); std::cout << "Saved Measurement" << std::endl; } std::string RooStats::HistFactory::Measurement::GetDirPath( TDirectory* dir ) { // Return the directory's path, // stripped of unnecessary prefixes std::string path = dir->GetPath(); if( path.find(":") != std::string::npos ) { size_t index = path.find(":"); path.replace( 0, index+1, "" ); } path = path + "/"; return path; /* if( path.find(":") != std::string::npos ) { size_t index = path.find(":"); SampleName.replace( 0, index, "" ); } // Remove the file: */ } void RooStats::HistFactory::Measurement::CollectHistograms() { // The most common way to add histograms to channels is to have them // stored in ROOT files and to give HistFactory the location of these // files. This means providing the path to the ROOT file and the path // and name of the histogram within that file. When providing these // in a script, HistFactory doesn't load the histogram from the file // right away. Instead, once all such histograms have been supplied, // one should run this method to open all ROOT files and to copy and // save all necessary histograms. for( unsigned int chanItr = 0; chanItr < fChannels.size(); ++chanItr) { RooStats::HistFactory::Channel& chan = fChannels.at( chanItr ); chan.CollectHistograms(); } }