BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
Config.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2023.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "AnalysisUtilities.hh"
20#include "BinGeneration.hh"
21#include "BinLoader.hh"
22#include "BinSpecification.hh"
23#include "Config.hh"
24#include "HistogramDefSet.hh"
25#include "HistogramDef1D.hh"
26#include "HistogramDef2D.hh"
27#include "HistogramDef3D.hh"
28#include "RBDSException.hh"
29#include "SpectraParticles.hh"
30
31#include <algorithm>
32#include <cctype> // for isspace
33#include <fstream>
34#include <iostream>
35#include <iterator>
36#include <limits>
37#include <map>
38#include <regex>
39#include <set>
40#include <stdexcept>
41#include <string>
42#include <vector>
43
44ClassImp(Config)
45
46Config* Config::instance = nullptr;
47
48std::vector<std::string> Config::treeNames = {"Beam.", "Options.", "Model.", "Run.", "Event."};
49
50Config::Config(const std::string& inputFilePathIn,
51 const std::string& outputFileNameIn,
52 const std::string& defaultOutputFileSuffix):
53 allBranchesActivated(false)
54{
56
57 std::string ofn;
58 if (outputFileNameIn.empty() && !inputFilePathIn.empty())
59 {
60 ofn = RBDS::DefaultOutputName(inputFilePathIn, defaultOutputFileSuffix);
61 std::cout << "Using default output file name with \"" << defaultOutputFileSuffix << "\" suffix : " << ofn << std::endl;
62 }
63 else
64 {ofn = outputFileNameIn;}
65
66 optionsString["inputfilepath"] = inputFilePathIn;
67 optionsString["outputfilename"] = ofn;
68
69 // turn on merging only
70 branches["Event."].emplace_back("Histos");
71 branches["Run."].emplace_back("Histos");
72 optionsBool["perentryevent"] = true;
73}
74
75Config::Config(const std::string& fileNameIn,
76 const std::string& inputFilePathIn,
77 const std::string& outputFileNameIn,
78 const std::string& defaultOutputFileSuffix):
79 allBranchesActivated(false)
80{
81 InitialiseOptions(fileNameIn);
82 ParseInputFile();
83
84 if (!inputFilePathIn.empty())
85 {optionsString["inputfilepath"] = inputFilePathIn;}
86 if (!outputFileNameIn.empty())
87 {optionsString["outputfilename"] = outputFileNameIn;}
88 else
89 {
90 if (optionsString["outputfilename"].empty())
91 {// no argument supplied and also no output name in input file - default to filename+_ana.root
92 std::string newOutputFilePath = RBDS::DefaultOutputName(optionsString["inputfilepath"], defaultOutputFileSuffix);
93 optionsString["outputfilename"] = newOutputFilePath;
94 std::cout << "Using default output file name with \"" << defaultOutputFileSuffix << "\" suffix : " << optionsString.at("outputfilename") << std::endl;
95 }
96 }
97}
98
99Config::~Config()
100{
101 instance = nullptr;
102
103 for (auto& nameDefs : histoDefs)
104 {
105 for (auto& histoDef : nameDefs.second)
106 {delete histoDef;}
107 }
108 for (auto def : eventHistoDefSetsSimple)
109 {delete def;}
110 for (auto def : eventHistoDefSetsPerEntry)
111 {delete def;}
112}
113
114void Config::InitialiseOptions(const std::string& analysisFile)
115{
116 optionsString["analysisfile"] = analysisFile;
117
118 // for backwards compatibility / verbose names
119 alternateKeys["calculateopticalfunctions"] = "calculateoptics";
120 alternateKeys["calculateopticalfunctionsfilename"] = "opticsfilename";
121
122 optionsBool["debug"] = false;
123 optionsBool["calculateoptics"] = false;
124 optionsBool["emittanceonthefly"] = false;
125 optionsBool["mergehistograms"] = true;
126 optionsBool["perentrybeam"] = false;
127 optionsBool["perentryevent"] = false;
128 optionsBool["perentryrun"] = false;
129 optionsBool["perentryoption"] = false;
130 optionsBool["perentrymodel"] = false;
131 optionsBool["printout"] = true;
132 optionsBool["processsamplers"] = false;
133 optionsBool["backwardscompatible"] = false; // ignore file types for old data
134 optionsBool["verbosespectra"] = false;
135
136 optionsString["inputfilepath"] = "";
137 optionsString["outputfilename"] = "";
138 optionsString["opticsfilename"] = "";
139 optionsString["gdmlfilename"] = "";
140
141 optionsNumber["printmodulofraction"] = 0.01;
142 optionsNumber["eventstart"] = 0;
143 optionsNumber["eventend"] = -1;
144
145 // ensure keys exist for all trees.
146 for (const auto& name : treeNames)
147 {
148 histoDefs[name] = std::vector<HistogramDef*>();
149 histoDefsSimple[name] = std::vector<HistogramDef*>();
150 histoDefsPerEntry[name] = std::vector<HistogramDef*>();
151 branches[name] = std::vector<std::string>();
152 }
153}
154
155Config* Config::Instance(const std::string& fileName,
156 const std::string& inputFilePath,
157 const std::string& outputFileName,
158 const std::string& defaultOutputFileSuffix)
159{
160 if (!instance && !fileName.empty())
161 {instance = new Config(fileName, inputFilePath, outputFileName, defaultOutputFileSuffix);}
162 else if(instance && !fileName.empty())
163 {
164 std::cout << "Config::Instance> Instance present, delete and construct" << std::endl;
165 delete instance;
166 instance = new Config(fileName, inputFilePath, outputFileName, defaultOutputFileSuffix);
167 }
168 else if (!instance && fileName.empty())
169 {instance = new Config(inputFilePath, outputFileName, defaultOutputFileSuffix);}
170 // else return current instance (can be nullptr!)
171 return instance;
172}
173
174void Config::ParseInputFile()
175{
176 std::string fn = optionsString.at("analysisfile");
177 std::ifstream f(fn.c_str());
178
179 if(!f)
180 {throw RBDSException("Config::ParseInputFile>", "could not open analysis configuration file \"" + fn + "\"");}
181
182 lineCounter = 0;
183 std::string line;
184
185 // unique patterns to match
186 // match a line starting with #
187 std::regex comment("^\\#.*");
188 // match an option that has two words on the line
189 std::regex option(R"(^\s*(\S+)\s+(\S+)\s*$)");
190 // match a line starting with 'histogram', ignoring case
191 std::regex histogram("(?:simple)*histogram.*", std::regex_constants::icase);
192 // match a line starting with 'spectra', ignoring case - quite exact to avoid mismatching 'spectra' in file name in options
193 std::regex spectra("(?:simple)*spectra(?:TE|rigidity)*(?:log)*(?:\\s+)", std::regex_constants::icase);
194 // match particleset ignoring case
195 std::regex particleSet("(?:simple)*particleset", std::regex_constants::icase);
196
197 while (std::getline(f, line))
198 {
199 lineCounter++;
200 try
201 {
202 if (std::all_of(line.begin(), line.end(), isspace))
203 {continue;} // skip empty lines
204 else if (std::regex_search(line, comment))
205 {continue;} // skip lines starting with '#'
206 else if (std::regex_search(line, particleSet)) // this has to be before an option as it also has only 2 words per line
207 {ParseParticleSetLine(line);}
208 else if (std::regex_search(line, option))
209 {ParseSetting(line);}
210 else if (std::regex_search(line, histogram))
211 {ParseHistogramLine(line);} // any histogram - must be before settings
212 else if (std::regex_search(line, spectra))
213 {ParseSpectraLine(line);}
214 else
215 {continue;}
216 }
217 catch (RBDSException& e)
218 {
219 e.AppendToMessage("\nProblem is on line " + std::to_string(lineCounter) + " of configuration file: " + fn + "\n");
220 throw e;
221 }
222 }
223
224 f.close();
225
226 // set flags etc based on what options have been set
227 if (optionsBool.at("calculateoptics"))
228 {
230 optionsBool["processsamplers"] = true;
231 optionsBool["perentryevent"] = true;
232 }
233 if (optionsBool.at("mergehistograms"))
234 {
235 branches["Event."].emplace_back("Histos");
236 branches["Run."].emplace_back("Histos");
237 optionsBool["perentryevent"] = true;
238 }
239
240 // checks on event numbers
241 double eS = optionsNumber.at("eventstart");
242 double eE = optionsNumber.at("eventend");
243 if (eE < 0) // default -1
244 {eE = std::numeric_limits<double>::max();}
245 if (eS < 0 || eS > eE)
246 {throw RBDSException("Invalid starting event number " + std::to_string(eS));}
247
248 if (optionsBool.at("verbosespectra"))
250}
251
252void Config::ParseHistogramLine(const std::string& line)
253{
254 // Settings with histogram in name can be misidentified - check here.
255 // This is the easiest way to do it for now.
256 std::string copyLine = LowerCase(line);
257 if (copyLine.find("mergehistograms") != std::string::npos)
258 {
259 ParseSetting(line);
260 return;
261 }
262
263 // we know the line starts with 'histogram'
264 // extract number after it as 1st match and rest of line as 2nd match
265 std::regex histNDim("^\\s*(?:Simple)*Histogram([1-3])D[a-zA-Z]*\\s+(.*)", std::regex_constants::icase);
266 //std::regex histNDim("^Histogram([1-3])D[a-zA-Z]*\\s+(.*)", std::regex_constants::icase);
267 std::smatch match;
268
269 if (std::regex_search(line, match, histNDim))
270 {
271 int nDim = std::stoi(match[1]);
272 ParseHistogram(line, nDim);
273 }
274 else
275 {throw RBDSException("Invalid histogram type");}
276}
277
278void Config::ParseHistogram(const std::string& line, const int nDim)
279{
280 // split line on white space
281 // doesn't inspect words themselves
282 // checks number of words, ie number of columns is correct
283 std::vector<std::string> results;
284 std::regex wspace("\\s+"); // any whitespace
285 // -1 here makes it point to the suffix, ie the word rather than the whitespace
286 std::sregex_token_iterator iter(line.begin(), line.end(), wspace, -1);
287 std::sregex_token_iterator end;
288 for (; iter != end; ++iter)
289 {
290 std::string res = (*iter).str();
291 results.push_back(res);
292 }
293
294 if (results.size() < 7)
295 {throw RBDSException("Too few columns in histogram definition.");}
296 if (results.size() > 7)
297 {throw RBDSException("Too many columns in histogram definition.");}
298
299 std::string histName = results[2];
300 bool duplicateName = RegisterHistogramName(histName);
301 if (duplicateName)
302 {throw RBDSException("Duplicate histogram name \"" + histName + "\" - histograms must have unique names.");}
303
304 bool xLog = false;
305 bool yLog = false;
306 bool zLog = false;
307 ParseLog(results[0], xLog, yLog, zLog);
308
309 bool perEntry = true;
310 ParsePerEntry(results[0], perEntry);
311
312 std::string treeName = results[1];
313 CheckValidTreeName(treeName);
314
315 if (perEntry)
316 {
317 std::string treeNameWithoutPoint = treeName; // make copy to modify
318 treeNameWithoutPoint.pop_back(); // delete last character
319 treeNameWithoutPoint = LowerCase(treeNameWithoutPoint);
320 optionsBool["perentry"+treeNameWithoutPoint] = true;
321 }
322
323 std::string bins = results[3];
324 std::string binning = results[4];
325 std::string variable = results[5];
326 std::string selection = results[6];
327
328 BinSpecification xBinning;
329 BinSpecification yBinning;
330 BinSpecification zBinning;
331 ParseBins(bins, nDim,xBinning, yBinning, zBinning);
332 ParseBinning(binning, nDim, xBinning, yBinning, zBinning, xLog, yLog, zLog);
333
334 // make a check that the number of variables supplied in ttree with y:x syntax doesn't
335 // exceed the number of declared dimensions - this would result in a segfault from ROOT
336 // a single (not 2) colon with at least one character on either side
337 std::regex singleColon("\\w+(:{1})\\w+");
338 // count the number of matches by distance of match iterator from beginning
339 int nColons = static_cast<int>(std::distance(std::sregex_iterator(variable.begin(),
340 variable.end(),
341 singleColon),
342 std::sregex_iterator()));
343 if (nColons > nDim - 1)
344 {
345 std::string err = "Error: Histogram \"" + histName + "\" variable includes too many single\n";
346 err += "colons specifying more dimensions than the number of specified dimensions.\n";
347 err += "Declared dimensions: " + std::to_string(nDim) + "\n";
348 err += "Number of dimensions in variables " + std::to_string(nColons + 1);
349 throw RBDSException(err);
350 }
351
352 HistogramDef1D* result = nullptr;
353 switch (nDim)
354 {
355 case 1:
356 {
357 result = new HistogramDef1D(treeName, histName,
358 xBinning,
359 variable, selection, perEntry);
360 break;
361 }
362 case 2:
363 {
364 result = new HistogramDef2D(treeName, histName,
365 xBinning, yBinning,
366 variable, selection, perEntry);
367 break;
368 }
369 case 3:
370 {
371 result = new HistogramDef3D(treeName, histName,
372 xBinning, yBinning, zBinning,
373 variable, selection, perEntry);
374 break;
375 }
376 default:
377 {break;}
378 }
379
380 if (result)
381 {
382 histoDefs[treeName].push_back(result);
383 if (perEntry)
384 {histoDefsPerEntry[treeName].push_back(result);}
385 else
386 {histoDefsSimple[treeName].push_back(result);}
388 }
389}
390
391void Config::ParseSpectraLine(const std::string& line)
392{
393 // split line on white space
394 std::vector<std::string> results = SplitOnWhiteSpace(line);
395
396 if (results.size() < 6)
397 {throw RBDSException("Too few columns in spectra definition.");}
398 if (results.size() > 6)
399 {throw RBDSException("Too many columns in spectra definition - check there's no extra whitespace");}
400
401 bool xLog = false;
402 bool yLog = false; // duff values to fulfill function
403 bool zLog = false;
404 ParseLog(results[0], xLog, yLog, zLog);
405
406 bool perEntry = true;
407 ParsePerEntry(results[0], perEntry);
408
409 std::string variable = ".kineticEnergy"; // kinetic energy by default
410 if (ContainsWordCI(results[0], "TE"))
411 {variable = ".energy";}
412 else if (ContainsWordCI(results[0], "Rigidity"))
413 {variable = ".rigidity";}
414
415 std::string samplerName = results[1];
416 // because we can have multiple spectra on a branch and there are no user-specified names for this
417 int nSpectraThisBranch = 0;
418 auto search = spectraNames.find(samplerName);
419 if (search != spectraNames.end())
420 {// branch name already exists
421 nSpectraThisBranch = search->second;
422 search->second++;
423 }
424 else
425 {spectraNames[samplerName] = 1;}
426 std::string histogramName = samplerName + "_" + std::to_string(nSpectraThisBranch);
427 std::string selection = results[5];
428
429 BinSpecification xBinning;
430 BinSpecification yBinning;
431 BinSpecification zBinning;
432 ParseBins(results[2], 1, xBinning, yBinning, zBinning);
433 ParseBinning(results[3], 1, xBinning, yBinning, zBinning, xLog, yLog, zLog);
434
435 std::set<ParticleSpec> particles;
436 try
437 {particles = ParseParticles(results[4]);}
438 catch (RBDSException& e)
439 {
440 e.AppendToMessage("\nError in spectra particle definition.");
441 throw RBDSException(e);
442 }
443
444 // simple spectra using 'top' or 'ions' or 'particles' won't dynamically build up the pdg ids
445 // per event so we should warn the user about this as it'll create no histograms
446 if (particles.empty() && !perEntry)
447 {throw RBDSException("Simple spectra cannot be used with 'topN'- only works for specific particles");}
448
449 HistogramDef1D* def = new HistogramDef1D("Event.",
450 histogramName,
451 xBinning,
452 samplerName + variable,
453 selection, perEntry);
454
455 HistogramDefSet* result = new HistogramDefSet(samplerName,
456 def,
457 particles,
458 results[4]);
459 delete def; // no longer needed
460
461 if (perEntry)
462 {eventHistoDefSetsPerEntry.push_back(result);}
463 else
464 {eventHistoDefSetsSimple.push_back(result);}
465
466 SetBranchToBeActivated("Event.", samplerName);
467}
468
469void Config::ParseParticleSetLine(const std::string& line)
470{
471 std::vector<std::string> results = SplitOnWhiteSpace(line);
472 if (results.size() < 2)
473 {throw RBDSException("Too few columns in particle set definition.");}
474 if (results.size() > 2)
475 {throw RBDSException("Too many columns in particle set definition - check there's no extra whitespace");}
476
477 std::string samplerName = results[1];
478 SetBranchToBeActivated("Event.", samplerName);
479
480 bool perEntry = true;
481 ParsePerEntry(results[0], perEntry);
482 if (perEntry)
483 {eventParticleSetBranches.push_back(samplerName);}
484 else
485 {eventParticleSetSimpleBranches.push_back(samplerName);}
486}
487
488void Config::ParsePerEntry(const std::string& name, bool& perEntry) const
489{
490 std::string res = LowerCase(name);
491 perEntry = res.find("simple") == std::string::npos;
492}
493
494bool Config::ContainsWordCI(const std::string& input,
495 const std::string& word) const
496{
497 std::string il = LowerCase(input);
498 std::string wl = LowerCase(word);
499 return il.find(wl) != std::string::npos;
500}
501
502void Config::ParseLog(const std::string& definition,
503 bool& xLog,
504 bool& yLog,
505 bool& zLog) const
506{
507 // capture any lin log definitions after Histogram[1-3]D
508 std::regex linLogWords("(Lin)|(Log)", std::regex_constants::icase);
509 std::sregex_token_iterator iter(definition.begin(), definition.end(), linLogWords);
510 std::sregex_token_iterator end;
511 std::vector<bool*> results = {&xLog, &yLog, &zLog};
512 int index = 0;
513 for (; iter != end; ++iter, ++index)
514 {
515 std::string res = LowerCase((*iter).str());
516 *(results[index]) = res == "log";
517 }
518}
519
521{
522 UpdateRequiredBranches(def->treeName, def->variable);
523 UpdateRequiredBranches(def->treeName, def->selection);
524}
525
526void Config::UpdateRequiredBranches(const std::string& treeName,
527 const std::string& var)
528{
529 // This won't work properly for the options Tree that has "::" in the class
530 // as well as double splitting. C++ regex does not support lookahead / behind
531 // which makes it nigh on impossible to correctly identify the single : with
532 // regex. For now, only the Options tree has this and we turn it all on, so it
533 // it shouldn't be a problem (it only ever has one entry).
534 // match word; '.'; word -> here we match the token rather than the bits in-between
535 std::regex branchLeaf("(\\w+)\\.(\\w+)");
536 auto words_begin = std::sregex_iterator(var.begin(), var.end(), branchLeaf);
537 auto words_end = std::sregex_iterator();
538 for (std::sregex_iterator i = words_begin; i != words_end; ++i)
539 {
540 std::string targetBranch = (*i)[1];
541 SetBranchToBeActivated(treeName, targetBranch);
542 }
543}
544
545void Config::SetBranchToBeActivated(const std::string& treeName,
546 const std::string& branchName)
547{
548 auto& v = branches.at(treeName);
549 if (std::find(v.begin(), v.end(), branchName) == v.end())
550 {v.push_back(branchName);}
551}
552
554{
555 std::cout << "Simple histogram set definitions for Event tree:" << std::endl;
556 for (const auto* def : eventHistoDefSetsSimple)
557 {std::cout << *def << std::endl;}
558 std::cout << "PerEntry histogram set definitions for Event tree:" << std::endl;
559 for (const auto* def : eventHistoDefSetsPerEntry)
560 {std::cout << *def << std::endl;}
561}
562
563void Config::CheckValidTreeName(std::string& treeName) const
564{
565 // check it has a point at the end (simple mistake)
566 if (strcmp(&treeName.back(), ".") != 0)
567 {treeName += ".";}
568
569 if (InvalidTreeName(treeName))
570 {
571 std::string err = "Invalid tree name \"" + treeName + "\"\n";
572 err += "Tree names are one of: ";
573 for (const auto& n : treeNames)
574 {err += "\"" + n + "\" ";}
575 throw RBDSException(err);
576 }
577}
578
579bool Config::InvalidTreeName(const std::string& treeName) const
580{
581 return std::find(treeNames.begin(), treeNames.end(), treeName) == treeNames.end();
582}
583
584void Config::ParseBins(const std::string& bins,
585 int nDim,
586 BinSpecification& xBinning,
587 BinSpecification& yBinning,
588 BinSpecification& zBinning) const
589{
590 // match a number including +ve exponentials (should be +ve int)
591 std::regex number("([0-9e\\+]+)+", std::regex_constants::icase);
592 int* binValues[3] = {&xBinning.n, &yBinning.n, &zBinning.n};
593 auto words_begin = std::sregex_iterator(bins.begin(), bins.end(), number);
594 auto words_end = std::sregex_iterator();
595 int counter = 0;
596 for (std::sregex_iterator i = words_begin; i != words_end; ++i, ++counter)
597 {(*binValues[counter]) = std::stoi((*i).str());}
598 if (counter < nDim-1)
599 {throw RBDSException("Too few binning dimensions specified (N dimensions = " + std::to_string(nDim) + ") \"" + bins + "\"");}
600}
601
602void Config::ParseBinning(const std::string& binning,
603 int nDim,
604 BinSpecification& xBinning,
605 BinSpecification& yBinning,
606 BinSpecification& zBinning,
607 bool xLog,
608 bool yLog,
609 bool zLog) const
610{
611 // erase braces
612 std::string binningL = binning;
613 binningL.erase(std::remove(binningL.begin(), binningL.end(), '{'), binningL.end());
614 binningL.erase(std::remove(binningL.begin(), binningL.end(), '}'), binningL.end());
615 // simple match - let stod throw exception if wrong
616 std::regex oneDim("([0-9eE.+-]+):([0-9eE.+-]+)");
617
618 std::regex commas(",");
619 auto wordsBegin = std::sregex_token_iterator(binningL.begin(), binningL.end(), commas, -1);
620 auto wordsEnd = std::sregex_token_iterator();
621 int counter = 0;
622 std::vector<BinSpecification*> values = {&xBinning, &yBinning, &zBinning};
623 std::vector<bool> isLog = {xLog, yLog, zLog};
624 for (auto i = wordsBegin; i != wordsEnd; ++i, ++counter)
625 {
626 std::string matchS = *i;
627 if (matchS.find(".txt") != std::string::npos)
628 {// file name for variable binning
629 std::vector<double>* binEdges = RBDS::LoadBins(matchS);
630 (*values[counter]).edges = binEdges;
631 (*values[counter]).n = (int)binEdges->size() - 1;
632 (*values[counter]).low = binEdges->at(0);
633 (*values[counter]).high = binEdges->back();
634 (*values[counter]).edgesFileName = matchS;
635 }
636 else
637 {// try to match ranges
638 auto rangeBegin = std::sregex_iterator(matchS.begin(), matchS.end(), oneDim);
639 auto rangeEnd = std::sregex_iterator();
640 int counterRange = 0;
641 for (auto j = rangeBegin; j != rangeEnd; ++j, ++counterRange)
642 {// iterate over all matches and pull out first and second number
643 std::smatch matchR = *j;
644 try
645 {
646 (*values[counter]).low = std::stod(matchR[1]);
647 (*values[counter]).high = std::stod(matchR[2]);
648 }
649 catch (std::invalid_argument&) // if stod can't convert number to double
650 {throw RBDSException("Invalid binning number: \"" + matchS + "\"");}
651 if ((*values[counter]).high <= (*values[counter]).low)
652 {throw RBDSException("high bin edge is <= low bin edge \"" + binning + "\"");}
653 if (isLog[counter])
654 {
655 std::vector<double> binEdges = RBDS::LogSpace((*values[counter]).low, (*values[counter]).high, (*values[counter]).n);
656 (*values[counter]).edges = new std::vector<double>(binEdges);
657 (*values[counter]).isLogSpaced = true;
658 }
659 }
660 }
661 }
662
663 if (counter == 0)
664 {throw RBDSException("Invalid binning specification: \"" + binning + "\"");}
665 else if (counter < nDim)
666 {
667 std::string errString = "Insufficient number of binning dimensions: \n"
668 + std::to_string(nDim) + " dimension histogram, but the following was specified:\n"
669 + binning + "\nDimension defined by \"low:high\" and comma separated";
670 throw RBDSException(errString);
671 }
672 else if (counter > nDim)
673 {
674 std::string errString = "Too many binning dimension (i.e. commas) on line #"
675 + std::to_string(lineCounter) + "\n"
676 + std::to_string(nDim) + " dimension histogram, but the following was specified:\n"
677 + binning + "\nDimension defined by \"low:high\" and comma separated";
678 throw RBDSException(errString);
679 }
680}
681
682std::vector<std::string> Config::SplitOnWhiteSpace(const std::string& line) const
683{
684 std::vector<std::string> results;
685 std::regex wspace("\\s+"); // any whitespace
686 // -1 here makes it point to the suffix, ie the word rather than the wspace
687 std::sregex_token_iterator iter(line.begin(), line.end(), wspace, -1);
688 std::sregex_token_iterator end;
689 for (; iter != end; ++iter)
690 {
691 std::string res = (*iter).str();
692 results.push_back(res);
693 }
694 return results;
695}
696
697std::set<ParticleSpec> Config::ParseParticles(const std::string& word) const
698{
699 std::string wordLower = LowerCase(word);
700 // check for special keys where we would ignore any other specific particles
701 // e.g. for 'topN' we're deciding which histograms to make so don't make a
702 // ParticleSpec.
703 std::vector<std::string> specialKeys = {"top", "particles", "all", "ions"};
704 for (const auto& key : specialKeys)
705 {
706 if (wordLower.find(key) != std::string::npos)
707 {return std::set<ParticleSpec>();}
708 }
709
710 // some numbers in brackets -> try to split up
711 // detect brackets and strip off
712 std::regex inBrackets("^\\{(.+)\\}$");
713 std::smatch match;
714 auto firstSearch = std::regex_search(word, match, inBrackets);
715 if (!firstSearch)
716 {throw RBDSException("Invalid particle definition - no braces");}
717
718 // split up numbers inside brackets on commas
719 std::string numbers = match[1];
720 std::set<ParticleSpec> result;
721 std::regex commas(",");
722 // -1 here makes it point to the suffix, ie the word rather than the wspace
723 std::sregex_token_iterator iter(numbers.begin(), numbers.end(), commas, -1);
724 std::sregex_token_iterator end;
725 for (; iter != end; ++iter)
726 {
727 std::string res = (*iter).str();
728 std::regex selection("^([a-zA-Z]){1}(\\d+)");
729 std::smatch matchSelection;
730 if (std::regex_search(res, matchSelection, selection))
731 {
732 auto keySearch = RBDS::spectraParticlesKeys.find(matchSelection[1]);
733 RBDS::SpectraParticles which;
734 if (keySearch != RBDS::spectraParticlesKeys.end())
735 {which = keySearch->second;}
736 else
737 {throw RBDSException("Invalid particle specifier \"" + matchSelection[1].str() + "\"");}
738 try
739 {
740 long long int id = std::stoll(matchSelection[2].str());
741 result.insert(ParticleSpec(id, which));
742 }
743 catch (const std::exception& e)
744 {throw RBDSException(e.what());}
745 }
746 else if (res.find("total") != std::string::npos)
747 {result.insert(ParticleSpec(0, RBDS::SpectraParticles::all));}
748 else
749 {
750 try
751 {
752 long long int id = std::stoll(res);
753 result.insert(ParticleSpec(id, RBDS::SpectraParticles::all));
754 }
755 catch (const std::exception& e)
756 {throw RBDSException(e.what());}
757 }
758 }
759 return result;
760}
761
762std::string Config::LowerCase(const std::string& st) const
763{
764 std::string res = st;
765 std::transform(res.begin(), res.end(), res.begin(), ::tolower);
766 return res;
767}
768
769void Config::ParseSetting(const std::string& line)
770{
771 // match a word; at least one space; and a word including '.' and _ and / and -
772 std::regex setting("(\\w+)\\s+([\\-\\.\\/_\\w\\*]+)", std::regex_constants::icase);
773 std::smatch match;
774 if (std::regex_search(line, match, setting))
775 {
776 std::string key = LowerCase(match[1]);
777 std::string value = match[2];
778
779 if (alternateKeys.find(key) != alternateKeys.end())
780 {key = alternateKeys[key];} // reassign value to correct key
781
782 if (optionsBool.find(key) != optionsBool.end())
783 {
784 // match optional space; true or false; optional space - ignore case
785 std::regex tfRegex("\\s*(true|false)\\s*", std::regex_constants::icase);
786 std::smatch tfMatch;
787 if (std::regex_search(value, tfMatch, tfRegex))
788 {
789 std::string flag = tfMatch[1];
790 std::smatch tMatch;
791 std::regex tRegex("true", std::regex_constants::icase);
792 bool result = std::regex_search(flag, tMatch, tRegex);
793 optionsBool[key] = result;
794 }
795 else
796 {optionsBool[key] = (bool)std::stoi(value);}
797 }
798 else if (optionsString.find(key) != optionsString.end())
799 {optionsString[key] = value;}
800 else if (optionsNumber.find(key) != optionsNumber.end())
801 {optionsNumber[key] = std::stod(value);}
802 else
803 {throw RBDSException("Invalid option \"" + key + "\"");}
804 }
805 else
806 {throw RBDSException("Invalid option line \"" + line + "\"");}
807}
808
809bool Config::RegisterHistogramName(const std::string& newHistName)
810{
811 bool existsAlready = histogramNames.count(newHistName) > 0;
812 if (existsAlready)
813 {return true;}
814 else
815 {
816 histogramNames.insert(newHistName);
817 return false;
818 }
819}
Binning specification for a single dimension.
Configuration and configuration parser class.
Definition: Config.hh:43
std::map< std::string, std::vector< HistogramDef * > > histoDefs
Definition: Config.hh:57
void ParseSetting(const std::string &line)
Parse a settings line in input file and appropriate update member map.
Definition: Config.cc:769
void PrintHistogramSetDefinitions() const
Definition: Config.cc:553
void ParseHistogramLine(const std::string &line)
Parse a line beginning with histogram. Uses other functions if appropriately defined.
Definition: Config.cc:252
std::map< std::string, double > optionsNumber
Storage of options.
Definition: Config.hh:52
std::string LowerCase(const std::string &st) const
Return a lower case copy of a string.
Definition: Config.cc:762
void SetBranchToBeActivated(const std::string &treeName, const std::string &branchName)
Set a branch to be activated if not already.
Definition: Config.cc:545
void CheckValidTreeName(std::string &treeName) const
Definition: Config.cc:563
bool InvalidTreeName(const std::string &treeName) const
Definition: Config.cc:579
std::vector< std::string > eventParticleSetBranches
List of branches in event tree to produce ParticleSet objects on. (per event and simple).
Definition: Config.hh:70
std::map< std::string, int > spectraNames
Definition: Config.hh:262
static Config * Instance(const std::string &fileName="", const std::string &inputFilePath="", const std::string &outputFileName="", const std::string &defaultOutputFileSuffix="_ana")
Singleton accessor.
Definition: Config.cc:155
std::map< std::string, std::string > optionsString
Storage of options.
Definition: Config.hh:51
bool allBranchesActivated
Whether all branches will be activated - ie for optics.
Definition: Config.hh:258
Config()=delete
Private constructor for singleton pattern.
void ParseBinning(const std::string &binning, int nDim, BinSpecification &xBinning, BinSpecification &yBinning, BinSpecification &zBinning, bool xLog, bool yLog, bool zLog) const
Definition: Config.cc:602
std::vector< std::string > SplitOnWhiteSpace(const std::string &line) const
Return a vector of strings by splitting on whitespace.
Definition: Config.cc:682
std::set< ParticleSpec > ParseParticles(const std::string &word) const
Parser a list of particle PDG IDs into a set.
Definition: Config.cc:697
void ParseLog(const std::string &definition, bool &xLog, bool &yLog, bool &zLog) const
Parse whether each dimension is log or linear.
Definition: Config.cc:502
bool RegisterHistogramName(const std::string &newHistName)
Definition: Config.cc:809
std::vector< HistogramDefSet * > eventHistoDefSetsSimple
Sets of histogram definitions per particle. Only for event branch.
Definition: Config.hh:66
RBDS::BranchMap branches
Cache of which branches need to be activated for this analysis.
Definition: Config.hh:255
int lineCounter
Index of which line in the file we're on while parsing - for feedback.
Definition: Config.hh:252
std::map< std::string, std::string > alternateKeys
Private members first as required in accessors.
Definition: Config.hh:47
void UpdateRequiredBranches(const HistogramDef *def)
Definition: Config.cc:520
bool ContainsWordCI(const std::string &input, const std::string &word) const
Return true if 'input' contains 'word' - CI = case insensitive.
Definition: Config.cc:494
std::map< std::string, std::vector< HistogramDef * > > histoDefsSimple
Copy of definition used to identify only 'simple' histogram definitions. Doesn't own.
Definition: Config.hh:60
static std::vector< std::string > treeNames
Vector of permitted tree names.
Definition: Config.hh:249
void ParseParticleSetLine(const std::string &line)
Parse a particle set line.
Definition: Config.cc:469
void InitialiseOptions(const std::string &analysisFile)
Definition: Config.cc:114
void ParseSpectraLine(const std::string &line)
Parse a spectra definition line.
Definition: Config.cc:391
void ParseHistogram(const std::string &line, const int nDim)
Parse everything after the histogram declaration and check all parameters.
Definition: Config.cc:278
std::map< std::string, bool > optionsBool
Storage of options.
Definition: Config.hh:50
void ParsePerEntry(const std::string &name, bool &perEntry) const
Definition: Config.cc:488
void ParseBins(const std::string &bins, int nDim, BinSpecification &xBinning, BinSpecification &yBinning, BinSpecification &zBinning) const
Definition: Config.cc:584
std::map< std::string, std::vector< HistogramDef * > > histoDefsPerEntry
Copy of definition used to identify only 'per entry' histogram definitions. Doesn't own.
Definition: Config.hh:63
Specification for 1D histogram.
Specification for 2D histogram.
Specification for 3D Histogram.
Specification for a set of histograms.
Common specification for a histogram.
Definition: HistogramDef.hh:34
General exception with possible name of object and message.
std::vector< double > * LoadBins(const std::string &fileName)
Method to load a single column text file and return vector of values.
Definition: BinLoader.cc:30
std::string DefaultOutputName(const std::string &inputFilePath, const std::string &suffix)