modified: TrackRecon.C made changes to account for frequent crashes due to the number of histograms. New changes allow for flsuhing of data to histograms every n events modified: run_27Al.sh
492 lines
22 KiB
C++
492 lines
22 KiB
C++
#ifndef HISTPLOTTER_H
|
|
#define HISTPLOTTER_H
|
|
#include <TCanvas.h>
|
|
#include <TROOT.h>
|
|
#include <TSystem.h>
|
|
#include <TStyle.h>
|
|
#include <iostream>
|
|
#include <TFile.h>
|
|
#include <TMemFile.h>
|
|
#include <TH1.h>
|
|
#include <TH2.h>
|
|
#include <TCutG.h>
|
|
#include <signal.h>
|
|
#include <cstdlib>
|
|
#include <utility>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
#include <set>
|
|
#include <TGraphErrors.h>
|
|
|
|
class HistPlotter
|
|
{
|
|
private:
|
|
long long barrier_count, barrier_limit; // meant to keep track of how often to call FillN() on histograms
|
|
enum
|
|
{
|
|
TFILE,
|
|
TMEMFILE
|
|
} filetype;
|
|
std::map<std::string, TObject *> oMap; //!< Maps std::string to all TH1, TH2 objects in the class
|
|
std::unordered_map<std::string, TObject *> cutsMap; //!< Maps std::string to TCutG objects held by the class
|
|
std::set<std::string> folderList; //!< List of all folder names used to nest objects
|
|
std::unordered_map<TObject *, std::string> foldersForObjects; //!< Map that returns the folder corresponding to the object whose pointer is specified
|
|
TFile *ofile = nullptr; //!< TFile pointer for the output file
|
|
TMemFile *omfile = nullptr; //!< TFile pointer for the output memfile
|
|
|
|
// Caches to permit FillN() calls
|
|
std::map<std::string, std::vector<double>> onedimcache;
|
|
std::map<std::string, std::pair<std::vector<double>, std::vector<double>>> twodimcache;
|
|
inline void FillN_All_Histograms();
|
|
|
|
public:
|
|
HistPlotter(std::string outfile, std::string type);
|
|
inline void FlushToDisk(int integral); //!< Writes all objects to file before closing, nesting objects in folders as is found necessary
|
|
inline void PrintObjects(); //!< Dump objects to std::cout for inspection
|
|
inline void ReadCuts(std::string);
|
|
inline TCutG *FindCut(std::string cut)
|
|
{
|
|
return static_cast<TCutG *>(cutsMap.at(cut));
|
|
}
|
|
inline void set_barrier_limit(long long limit) { barrier_limit = limit; }
|
|
inline void barrier_increment()
|
|
{
|
|
barrier_count++;
|
|
if (barrier_count == barrier_limit)
|
|
{
|
|
FillN_All_Histograms();
|
|
barrier_count = 0;
|
|
}
|
|
}
|
|
|
|
inline void force_flush_caches()
|
|
{
|
|
FillN_All_Histograms();
|
|
barrier_count = 0;
|
|
}
|
|
/*! \fn void FindCut()
|
|
\brief
|
|
- Searches for a cut by name 'cut' in the internal list of cuts 'cutsMap'. Ugly fails (via unresolved at()) if such a cut isn't found.
|
|
\param filename - name of the plainxtext file containing the cut file locations and identifiers
|
|
\return Pointer to the TCutG object that matches the name. Very useful to use this as plotter.FindCut("protonbarrelpid")->IsInside(deltaE, E) for instance.
|
|
*/
|
|
|
|
inline void SetNewTitle(std::string name, std::string title)
|
|
{
|
|
auto result = oMap.find(name); // result is an iterator
|
|
if (result == oMap.end())
|
|
return; // no warnings, could be changed in future
|
|
else
|
|
static_cast<TNamed *>(oMap.at(name))->SetTitle(title.c_str()); // set new title
|
|
}
|
|
|
|
// Smart functions that create a new histogram if it doesn't exist.
|
|
inline void FillGraph(const std::string &name, float valuex, float valuey, float errx = 0, float erry = 0);
|
|
inline void Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value);
|
|
inline void Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey);
|
|
inline void Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value, const std::string &folder);
|
|
inline void Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey, const std::string &folder);
|
|
// TObject* findObject(std::string key);
|
|
};
|
|
|
|
HistPlotter::HistPlotter(std::string outfile, std::string type = "")
|
|
{
|
|
/*!
|
|
\brief Constructor. Opens a TFile instance with the specified filename
|
|
\param outfile : std::string that holds the desired output ROOT filename
|
|
\return None
|
|
*/
|
|
if (type == "" || type == "TFILE")
|
|
{
|
|
ofile = new TFile(outfile.c_str(), "recreate");
|
|
filetype = TFILE;
|
|
}
|
|
else if (type == "TMEMFILE")
|
|
{
|
|
omfile = new TMemFile(outfile.c_str(), "recreate");
|
|
filetype = TMEMFILE;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Unknown type " << type << " specified for HistPlotter (use \"TFILE\" or \"TMEMFILE\"), using default \"TFILE\" " << std::endl;
|
|
ofile = new TFile(outfile.c_str(), "recreate");
|
|
filetype = TFILE;
|
|
}
|
|
barrier_count = 0;
|
|
barrier_limit = 1000;
|
|
}
|
|
|
|
void HistPlotter::FillN_All_Histograms()
|
|
{
|
|
for (auto it = oMap.begin(); it != oMap.end(); it++)
|
|
{
|
|
// it->first is std::string 'name', it->second is the TObject
|
|
if (it->second->InheritsFrom("TH1F"))
|
|
{
|
|
// FillN(size, array-of-doubles, array-of-weights); //we set array-of-weights to (1,1,1,.. (size)
|
|
static_cast<TH1F *>(it->second)->FillN(onedimcache[it->first].size(), // size
|
|
onedimcache[it->first].data(), // array
|
|
std::vector<double>(onedimcache[it->first].size(), 1.0).data()); // weight of ones
|
|
onedimcache[it->first].clear();
|
|
}
|
|
else if (it->second->InheritsFrom("TH2F"))
|
|
{
|
|
// FillN(size, array-of-doubles, array-of-weights); //we set array-of-weights to (1,1,1,.. (size))
|
|
static_cast<TH2F *>(it->second)->FillN(twodimcache[it->first].first.size(), // size
|
|
twodimcache[it->first].first.data(), // x array
|
|
twodimcache[it->first].second.data(), // y array
|
|
std::vector<double>(twodimcache[it->first].first.size(), 1.0).data()); // weight of ones
|
|
twodimcache[it->first].first.clear();
|
|
twodimcache[it->first].second.clear();
|
|
}
|
|
}
|
|
std::cout << "." << std::endl;
|
|
}
|
|
|
|
void HistPlotter::FlushToDisk(int min_integral = 0)
|
|
{
|
|
/*! \fn void FlushToDisk()
|
|
\brief Function that can be used at any point to exit smoothly by saving all ROOT objects in memory
|
|
to the output file before closing it. Obeys the binding of histograms to separate folders, if so specified.
|
|
\return No return -- void
|
|
*/
|
|
if (filetype == TMEMFILE && omfile)
|
|
{
|
|
std::cout << "Not flushing a TMemfile .. exiting .." << std::endl;
|
|
delete omfile;
|
|
return;
|
|
}
|
|
if (ofile->IsZombie() || !ofile)
|
|
{
|
|
std::cerr << "Output file is zombie, finishing up without writing to disk!" << std::endl;
|
|
return;
|
|
}
|
|
FillN_All_Histograms();
|
|
for (auto it = oMap.begin(); it != oMap.end(); it++)
|
|
{
|
|
// omap maps: name(first) to object address(second).
|
|
// foldersForObjects maps: object address(first) to foldername(second)
|
|
auto result = foldersForObjects.find(it->second); // returns <TObject* histogram,std::string foldername> pair if found
|
|
if (result != foldersForObjects.end())
|
|
{ // we try to create folder if needed and cd to it
|
|
ofile->mkdir(result->second.c_str(), "", kTRUE); // args: name, title, returnExistingDirectory
|
|
ofile->cd(result->second.c_str());
|
|
}
|
|
else
|
|
{
|
|
ofile->cd(); // toplevel for all default histograms. Default setting
|
|
}
|
|
if (((TH1F *)it->second)->Integral() > min_integral)
|
|
it->second->Write();
|
|
}
|
|
|
|
// Create a directory for all cuts, and save all cuts in them
|
|
ofile->mkdir("gCUTS", "", kTRUE);
|
|
ofile->cd("gCUTS");
|
|
for (auto it = cutsMap.begin(); it != cutsMap.end(); it++)
|
|
{
|
|
(static_cast<TNamed *>(it->second))->SetName(it->first.c_str());
|
|
it->second->Write();
|
|
}
|
|
ofile->Close();
|
|
std::cout << "Wrote " << oMap.size() << " histograms to TFile " << std::string(ofile->GetName()) << std::endl;
|
|
}
|
|
|
|
void HistPlotter::FillGraph(const std::string &name, float valuex, float valuey, float errx, float erry)
|
|
{
|
|
/*! \fn void FillGraph()
|
|
\brief
|
|
- Creates a TGraphError in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
|
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TGraph*
|
|
|
|
\param name name of the TGraph
|
|
\param valuex The xvalue
|
|
\param valuey The yvalue
|
|
\param errx The x error
|
|
\param erry The y error
|
|
\return No return void
|
|
*/
|
|
auto result = oMap.find(name);
|
|
if (result == oMap.end())
|
|
{
|
|
TGraphErrors *tempG = new TGraphErrors();
|
|
tempG->SetName(name.c_str());
|
|
oMap.insert(std::make_pair(name, static_cast<TObject *>(tempG)));
|
|
}
|
|
if (!oMap.at(name)->InheritsFrom("TGraphErrors"))
|
|
{
|
|
std::cerr << "Object " << name << " refers to something other than a TGraph*, not filling it hence!" << std::endl;
|
|
std::cerr << "Abort.." << std::endl;
|
|
FlushToDisk();
|
|
exit(-1);
|
|
}
|
|
// static_cast<TGraphErrors*>(oMap.at(name))->AddPointError(valuex,valuey,errx,erry);
|
|
}
|
|
|
|
void HistPlotter::Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value)
|
|
{
|
|
/*! \fn void Fill1D()
|
|
\brief
|
|
- Creates a TH1F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
|
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH1*
|
|
|
|
\param name name of the TH1F histogram
|
|
\param nbinsx Number of bins in the histogram
|
|
\param xlow Lower limit on x-axis
|
|
\param xhigh Upper limit on x-axis
|
|
\param value The bin corresponding to value in (nbinsx, xlow, xhigh) is incremented by 1
|
|
\return No return void
|
|
*/
|
|
auto result = oMap.find(name); // result is an iterator
|
|
if (result == oMap.end())
|
|
{
|
|
TH1F *temp1D = new TH1F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh);
|
|
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp1D)));
|
|
onedimcache.insert(std::make_pair(name, std::vector<double>()));
|
|
onedimcache[name].reserve(16384);
|
|
}
|
|
else if (foldersForObjects.find(oMap.at(name)) != foldersForObjects.end())
|
|
{ // shouldn't have a folder associated with it
|
|
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in toplevel .." << std::endl;
|
|
}
|
|
|
|
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
|
if (!oMap.at(name)->InheritsFrom("TH1F"))
|
|
{
|
|
std::cerr << "Object " << name << " refers to something other than a TH1*, not filling it hence!" << std::endl;
|
|
std::cerr << "Abort.." << std::endl;
|
|
FlushToDisk();
|
|
exit(-1);
|
|
}
|
|
onedimcache[name].emplace_back(value);
|
|
// static_cast<TH1F*>(oMap.at(name))->Fill(value);
|
|
}
|
|
|
|
void HistPlotter::Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value, const std::string &foldername)
|
|
{
|
|
/*! \fn void Fill1D()
|
|
\brief
|
|
- Creates a TH1F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
|
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH1*
|
|
- Remembers the foldername this particular histogram maps to, if provided. If not, defaults to toplevel.
|
|
|
|
\param name name of the TH1F histogram
|
|
\param nbinsx Number of bins in the histogram
|
|
\param xlow Lower limit on x-axis
|
|
\param xhigh Upper limit on x-axis
|
|
\param value The bin corresponding to value in (nbinsx, xlow, xhigh) is incremented by 1
|
|
\param foldername Name of the folder to put this histogram into. Defaults to toplevel if left empty
|
|
\return No return -- void
|
|
*/
|
|
|
|
auto result = oMap.find(name); // result is an iterator
|
|
if (result == oMap.end())
|
|
{
|
|
TH1F *temp1D = new TH1F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh);
|
|
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp1D)));
|
|
onedimcache.insert(std::make_pair(name, std::vector<double>()));
|
|
onedimcache[name].reserve(16384);
|
|
if (foldername != "")
|
|
{
|
|
if (folderList.find(foldername) == folderList.end())
|
|
{
|
|
folderList.insert(foldername);
|
|
}
|
|
foldersForObjects.insert(std::make_pair(static_cast<TObject *>(temp1D), foldername));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// object is present in map, but we enforce unique names
|
|
// it must already have a folder attached to it
|
|
if (foldersForObjects.find(oMap.at(name)) == foldersForObjects.end())
|
|
{
|
|
std::cerr << "Object " << name << " already registered at toplevel, choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
|
}
|
|
else if (foldersForObjects[oMap[name]] != foldername)
|
|
{
|
|
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
|
}
|
|
}
|
|
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
|
if (!oMap.at(name)->InheritsFrom("TH1F"))
|
|
{
|
|
std::cerr << "Object " << name << " refers to something other than a TH1*, not filling it hence!" << std::endl;
|
|
std::cerr << "Abort.." << std::endl;
|
|
FlushToDisk();
|
|
exit(-1);
|
|
}
|
|
onedimcache[name].emplace_back(value);
|
|
// static_cast<TH1F*>(oMap.at(name))->Fill(value);
|
|
}
|
|
|
|
void HistPlotter::Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey)
|
|
{
|
|
/*! \fn void Fill2D()
|
|
\brief
|
|
- Creates a TH2F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
|
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH2*
|
|
\param name name of the TH1F histogram
|
|
\param nbinsx Number of xbins in the histogram
|
|
\param xlow Lower limit on x-axis
|
|
\param xhigh Upper limit on x-axis
|
|
\param nbinsy Number of ybins in the histogram
|
|
\param ylow Lower limit on y-axis
|
|
\param yhigh Upper limit on y-axis
|
|
\param valuex
|
|
\param valuey The bin corresponding to (valuex, valuey) in (nbinsx, xlow, xhigh, ybinsx, ylow, yhigh) is incremented by 1
|
|
\return No return -- void
|
|
*/
|
|
|
|
auto result = oMap.find(name); // result is an iterator
|
|
if (result == oMap.end())
|
|
{
|
|
TH2F *temp2D = new TH2F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh, nbinsy, ylow, yhigh);
|
|
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp2D)));
|
|
twodimcache.insert(std::make_pair(name, std::make_pair(std::vector<double>(), std::vector<double>())));
|
|
twodimcache[name].first.reserve(16384);
|
|
twodimcache[name].second.reserve(16384);
|
|
}
|
|
else if (foldersForObjects.find(oMap.at(name)) != foldersForObjects.end())
|
|
{ // shouldn't have a folder associated with it
|
|
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in toplevel .." << std::endl;
|
|
}
|
|
|
|
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
|
if (!oMap.at(name)->InheritsFrom("TH2F"))
|
|
{
|
|
std::cerr << "Object " << name << " refers to something other than a TH2*, not filling it hence!" << std::endl;
|
|
std::cerr << "Abort.." << std::endl;
|
|
FlushToDisk();
|
|
exit(-1);
|
|
}
|
|
twodimcache[name].first.emplace_back(valuex);
|
|
twodimcache[name].second.emplace_back(valuey);
|
|
// static_cast<TH2F*>(oMap.at(name))->Fill(valuex,valuey);
|
|
}
|
|
|
|
void HistPlotter::Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey, const std::string &foldername)
|
|
{
|
|
/*! \fn void Fill2D()
|
|
\brief
|
|
- Creates a TH2F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
|
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH2*
|
|
- Remembers the foldername this particular histogram maps to, if provided. If not defaults to toplevel
|
|
|
|
\param name name of the TH1F histogram
|
|
\param nbinsx Number of xbins in the histogram
|
|
\param xlow Lower limit on x-axis
|
|
\param xhigh Upper limit on x-axis
|
|
\param nbinsy Number of ybins in the histogram
|
|
\param ylow Lower limit on y-axis
|
|
\param yhigh Upper limit on y-axis
|
|
\param valuex
|
|
\param valuey The bin corresponding to (valuex, valuey) in (nbinsx, xlow, xhigh, ybinsx, ylow, yhigh) is incremented by 1
|
|
\param foldername Name of the folder to put this histogram into. Defaults to toplevel if left empty
|
|
\return No return -- void
|
|
*/
|
|
|
|
auto result = oMap.find(name); // result is an iterator
|
|
if (result == oMap.end())
|
|
{
|
|
TH2F *temp2D = new TH2F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh, nbinsy, ylow, yhigh);
|
|
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp2D)));
|
|
twodimcache.insert(std::make_pair(name, std::make_pair(std::vector<double>(), std::vector<double>())));
|
|
twodimcache[name].first.reserve(16384);
|
|
twodimcache[name].second.reserve(16384);
|
|
if (foldername != "")
|
|
{
|
|
if (folderList.find(foldername) == folderList.end())
|
|
{
|
|
folderList.insert(foldername);
|
|
}
|
|
foldersForObjects.insert(std::make_pair(static_cast<TObject *>(temp2D), foldername));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// object is present in map, but we enforce unique names
|
|
// it must already have a folder attached to it
|
|
if (foldersForObjects.find(oMap.at(name)) == foldersForObjects.end())
|
|
{
|
|
std::cerr << "Object " << name << " already registered at toplevel, choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
|
}
|
|
else if (foldersForObjects[oMap.at(name)] != foldername)
|
|
{
|
|
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
|
}
|
|
}
|
|
|
|
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
|
if (!oMap.at(name)->InheritsFrom("TH2F"))
|
|
{
|
|
std::cerr << "Object " << name << " refers to something other than a TH2*, not filling it hence!" << std::endl;
|
|
std::cerr << "Abort.." << std::endl;
|
|
FlushToDisk();
|
|
exit(-1);
|
|
}
|
|
twodimcache[name].first.emplace_back(valuex);
|
|
twodimcache[name].second.emplace_back(valuey);
|
|
// static_cast<TH2F*>(oMap.at(name))->Fill(valuex,valuey);
|
|
}
|
|
|
|
void HistPlotter::ReadCuts(std::string filename)
|
|
{
|
|
/*! \fn void ReadCuts()
|
|
\brief Reads a list of cuts from a file. The file must have the format below, two columns
|
|
- Column#1 - path to a file that contains a single TCutG object named "CUTG", the default name in ROOT.
|
|
- Column#2 - The identifier name you plan to use in the code, like 'protonbarrelpid' or something, that will be searched by FindCut()
|
|
\param filename name of the plainxtext file containing the cut file locations and identifiers
|
|
\return No return -- void
|
|
*/
|
|
|
|
std::ifstream infile;
|
|
infile.open(filename);
|
|
std::string cutfilename, cutname;
|
|
for (std::string line; std::getline(infile, line);)
|
|
{
|
|
if (line.size() != 0 && line[0] == '#')
|
|
; // don't do anything with '#' lines
|
|
else
|
|
{
|
|
std::stringstream ss(line);
|
|
ss >> cutfilename >> cutname;
|
|
|
|
TFile f(cutfilename.c_str());
|
|
if (f.IsZombie())
|
|
{
|
|
std::cerr << "Cannot open cutfile " << cutfilename << " .. skipping.." << std::endl;
|
|
continue;
|
|
}
|
|
TCutG *cut = (TCutG *)(f.Get("CUTG"));
|
|
cutsMap.insert(std::make_pair(cutname, static_cast<TObject *>(cut)));
|
|
f.Close();
|
|
} // else
|
|
} // for loop
|
|
infile.close();
|
|
}
|
|
|
|
void HistPlotter::PrintObjects()
|
|
{
|
|
/*
|
|
void PrintObjects()
|
|
Prints the contents of the unordered_maps oMap and cutsMap to facilitate debugging
|
|
|
|
*/
|
|
std::cout << "Type | Name " << std::endl;
|
|
std::cout << "---- | --------------------- " << std::endl;
|
|
for (auto it = oMap.begin(); it != oMap.end(); it++)
|
|
{
|
|
std::cout << it->second->ClassName() << " | " << it->first << std::endl;
|
|
}
|
|
for (auto it = cutsMap.begin(); it != cutsMap.end(); it++)
|
|
{
|
|
std::cout << it->second->ClassName() << " | " << it->first << std::endl;
|
|
}
|
|
std::cout << "---- | --------------------- " << std::endl;
|
|
}
|
|
|
|
#endif
|