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