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