74 static void DeleteCommentsAndSpaces(std::string& line)
77 std::size_t comment = line.find(
'#');
78 line = line.substr(0, comment);
80 std::size_t firstNotSpace = line.find_first_not_of(
" \t");
81 if (firstNotSpace != std::string::npos)
82 line = line.substr(firstNotSpace);
88 std::size_t lastNotSpace = line.find_last_not_of(
" \t");
89 if (lastNotSpace != std::string::npos)
90 line = line.substr(0, lastNotSpace + 1);
100 std::string TSimpleAnalysis::HandleExpressionConfig(
const std::string& line)
102 static const std::string kCutIntr =
" if ";
104 std::size_t equal = line.find(
"=");
105 if (equal == std::string::npos)
106 return "Error: missing '='";
109 std::string histName = line.substr(0, equal);
110 DeleteCommentsAndSpaces(histName);
111 if (histName.empty())
112 return "Error: no histName found";
115 std::size_t cutPos = line.find(kCutIntr, equal);
116 std::string histExpression;
117 if (cutPos == std::string::npos)
118 histExpression = line.substr(equal + 1);
120 histExpression = line.substr(equal + 1, cutPos - equal - 1);
121 DeleteCommentsAndSpaces(histExpression);
122 if (histExpression.empty())
123 return "Error: no expression found";
127 if (cutPos != std::string::npos) {
128 histCut = line.substr(cutPos + kCutIntr.size());
129 DeleteCommentsAndSpaces(histCut);
131 return "Error: missing cut expression after 'if'";
137 auto check = fHists.insert(std::make_pair((
const std::string&)histName,
138 std::make_pair(histExpression, histCut)));
142 return "Duplicate histogram name";
156 TSimpleAnalysis::TSimpleAnalysis(
const std::string& output,
157 const std::vector<std::string>& inputFiles,
158 const std::vector<std::string>& expressions,
159 const std::string& treeName =
""):
160 fInputFiles(inputFiles), fOutputFile(output), fTreeName(treeName)
162 for (
const std::string& expr: expressions) {
163 std::string errMessage = HandleExpressionConfig(expr);
164 if (!errMessage.empty())
165 throw std::runtime_error(errMessage +
" in " + expr);
173 static std::string ExtractTreeName(std::string& firstInputFile)
175 std::string treeName =
"";
176 std::unique_ptr<TFile> inputFile{TFile::Open(firstInputFile.c_str())};
179 for (TObject* keyAsObj : *inputFile->GetListOfKeys()) {
180 TKey* key =
static_cast<TKey*
>(keyAsObj);
181 TClass* clObj = TClass::GetClass(key->GetClassName());
187 if (clObj->InheritsFrom(TTree::Class())) {
188 if (treeName.empty())
189 treeName = key->GetName();
191 ::Error(
"TSimpleAnalysis::Analyze",
"Multiple trees inside %s", firstInputFile.c_str());
197 if (treeName.empty()) {
198 ::Error(
"TSimpleAnalysis::Analyze",
"No tree inside %s", firstInputFile.c_str());
207 static bool CheckChainLoadResult(TChain* chain)
210 static const char* errors[] {
213 "invalid entry number",
214 "cannot open the file",
220 TObjArray *fileElements = chain->GetListOfFiles();
221 TIter next(fileElements);
222 while (TChainElement* chEl = (TChainElement*)next()) {
223 if (chEl->GetLoadResult() < 0) {
224 ::Error(
"TSimpleAnalysis::Run",
"Load failure in file %s: %s",
225 chEl->GetTitle(), errors[-(chEl->GetLoadResult())]);
236 bool TSimpleAnalysis::SetTreeName()
240 if (!fTreeName.empty()) {
242 int oldLevel = gErrorIgnoreLevel;
243 gErrorIgnoreLevel = kFatal;
244 if (TFile* probe = TFile::Open(fTreeName.c_str())) {
245 if (!probe->IsZombie()) {
246 fInputFiles.insert(fInputFiles.begin(), fTreeName);
251 gErrorIgnoreLevel = oldLevel;
255 if (fTreeName.empty())
256 fTreeName = ExtractTreeName(fInputFiles[0]);
257 if (fTreeName.empty())
266 bool TSimpleAnalysis::Run()
272 TFile ofile(fOutputFile.c_str(),
"RECREATE");
273 if (ofile.IsZombie()) {
274 ::Error(
"TSimpleAnalysis::Run",
"Impossible to create %s", fOutputFile.c_str());
279 auto generateHisto = [&](
const std::pair<TChain*, TDirectory*>& job) {
280 TChain* chain = job.first;
281 TDirectory* taskDir = job.second;
283 std::vector<TH1F *> vPtrHisto(fHists.size());
288 for (
const auto &histo : fHists) {
289 const std::string& expr = histo.second.first;
290 const std::string& histoName = histo.first;
291 const std::string& cut = histo.second.second;
293 chain->Draw((expr +
">>" + histoName).c_str(), cut.c_str(),
"goff");
294 TH1F *ptrHisto = (TH1F*)taskDir->Get(histoName.c_str());
297 if (!CheckChainLoadResult(chain))
298 return std::vector<TH1F *>();
300 vPtrHisto[i] = ptrHisto;
312 ROOT::EnableThreadSafety();
313 ROOT::TThreadExecutor pool(8);
316 std::vector<std::pair<TChain*, TDirectory*>> vChains;
317 for (
size_t i = 0; i < fInputFiles.size(); ++i){
318 const std::string& inputfile = fInputFiles[i];
320 ch =
new TChain(fTreeName.c_str());
321 ch->Add(inputfile.c_str());
325 TDirectory* taskDir = gROOT->mkdir(TString::Format(
"TSimpleAnalysis_taskDir_%d", (
int)i));
327 vChains.emplace_back(std::make_pair(ch, taskDir));
330 auto vFileswHists = pool.Map(generateHisto, vChains);
334 for (
auto&& histsOfJob: vFileswHists) {
335 if (histsOfJob.empty())
341 std::vector<TH1F *> vPtrHisto{vFileswHists[0]};
343 for (
unsigned j = 0; j < fHists.size(); j++) {
344 for (
unsigned i = 1; i < vFileswHists.size(); i++) {
345 if (!vFileswHists[i][j]) {
348 vPtrHisto[j] =
nullptr;
352 vPtrHisto[j]->Add(vFileswHists[i][j]);
355 vPtrHisto[j]->Write();
362 TChain* chain =
new TChain(fTreeName.c_str());
363 for (
const std::string& inputfile: fInputFiles)
364 chain->Add(inputfile.c_str());
367 auto vHisto = generateHisto({chain, gDirectory});
372 for (
auto histo: vHisto) {
386 bool TSimpleAnalysis::HandleInputFileNameConfig(
const std::string& line)
388 if (line.find(
"=") == std::string::npos) {
389 fInputFiles.push_back(line);
400 std::string TSimpleAnalysis::GetLine(
int& numbLine)
402 std::string notEmptyLine;
405 getline(fIn, notEmptyLine);
406 DeleteCommentsAndSpaces(notEmptyLine);
408 }
while (fIn && notEmptyLine.empty());
416 bool TSimpleAnalysis::Configure()
418 int readingSection = kReadingOutput;
423 fIn.open(fConfigFile);
425 ::Error(
"TSimpleAnalysis::Configure",
"File %s not found", fConfigFile.c_str());
430 line = GetLine(numbLine);
433 std::string errMessage;
435 switch (readingSection) {
444 case kReadingTreeName:
451 if (!HandleInputFileNameConfig(line)) {
453 errMessage = HandleExpressionConfig(line);
454 readingSection = kReadingExpressions;
459 case kReadingExpressions:
460 errMessage = HandleExpressionConfig(line);
465 if (!errMessage.empty()) {
466 ::Error(
"TSimpleAnalysis::Configure",
"%s in %s:%d", errMessage.c_str(),
467 fConfigFile.c_str(), numbLine);
480 bool RunSimpleAnalysis (
const char* configurationFile) {
481 TSimpleAnalysis obj(configurationFile);
482 if (!obj.Configure())