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