guts
gutilities.cc
Go to the documentation of this file.
1 // gutilities
2 #include "gutilities.h"
3 #include "gutsConventions.h"
4 
5 // Numbers / strings with units / io interface to CLHEP units
6 #include "CLHEP/Units/PhysicalConstants.h"
7 
8 // c++
9 // algorithm for 'transform'
10 #include <algorithm>
11 #include <sstream>
12 #include <unordered_map>
13 #include <iostream>
14 #include <fstream>
15 #include <vector>
16 #include <charconv>
17 #include <filesystem>
18 
19 namespace gutilities {
20 
21 string removeLeadingAndTrailingSpacesFromString(const std::string& input) {
22  size_t startPos = input.find_first_not_of(" \t"); // Find the first non-whitespace character
23  size_t endPos = input.find_last_not_of(" \t"); // Find the last non-whitespace character
24 
25  // If all spaces or empty, return an empty string
26  if (startPos == std::string::npos || endPos == std::string::npos) { return ""; }
27 
28  // Return the substring between startPos and endPos
29  return input.substr(startPos, endPos - startPos + 1);
30 }
31 
32 string removeAllSpacesFromString(const std::string& str) {
33  string result = str;
34  result.erase(std::remove(result.begin(), result.end(), ' '), result.end());
35  return result;
36 }
37 
38 string getFileFromPath(const std::string& path) {
39  std::size_t lastSlashPos = path.find_last_of('/');
40  if (lastSlashPos == std::string::npos) {
41  // No slashes found, return the entire path
42  return path;
43  }
44  return path.substr(lastSlashPos + 1);
45 }
46 
47 string getDirFromPath(const std::string& path) {
48  auto lastSlash = path.find_last_of('/');
49  if (lastSlash == std::string::npos) return ".";
50  return path.substr(0, lastSlash);
51 }
52 
53 namespace fs = std::filesystem;
54 
55 
56 vector<std::string> getStringVectorFromString(const std::string& input) {
57  std::vector<std::string> pvalues;
58  std::stringstream plist(input);
59  string tmp;
60  while (plist >> tmp) {
61  string trimmed = removeLeadingAndTrailingSpacesFromString(tmp);
62  if (!trimmed.empty()) { pvalues.push_back(trimmed); }
63  }
64  return pvalues;
65 }
66 
67 // Replace all occurences of specific chars in a string with a string
68 string replaceCharInStringWithChars(const std::string& input, const std::string& toReplace,
69  const std::string& replacement) {
70  string output;
71  for (const char& ch : input) {
72  if (toReplace.find(ch) != std::string::npos) { output.append(replacement); }
73  else { output.push_back(ch); }
74  }
75  return output;
76 }
77 
78 string replaceAllStringsWithString(const string& source, const string& from, const string& to) {
79  if (from.empty()) return source; // Avoid infinite loop
80 
81  string newString;
82  size_t lastPos = 0;
83  size_t findPos = source.find(from, lastPos);
84 
85  while (findPos != string::npos) {
86  // Append part before the match and the replacement string
87  newString.append(source, lastPos, findPos - lastPos);
88  newString += to;
89  lastPos = findPos + from.length();
90  findPos = source.find(from, lastPos);
91  }
92 
93  // Append the remaining part of the string after the last occurrence
94  newString += source.substr(lastPos);
95 
96  return newString;
97 }
98 
99 
100 string fillDigits(const string& word, const string& c, int ndigits) {
101  if (c.empty() || ndigits <= static_cast<int>(word.size())) return word; // Return original if no padding needed
102 
103  string filled;
104 
105  int toFill = ndigits - static_cast<int>(word.size());
106  filled.reserve(ndigits);
107 
108  filled.append(toFill, c[0]); // Use the first character of the string 'c'
109  filled += word;
110 
111  return filled;
112 }
113 
114 
115 double getG4Number(const string& v, bool warnIfNotUnit) {
117 
118  // If no '*' is found, the input is assumed to be a number without units
119  if (value.find('*') == string::npos) {
120  if (!value.empty() && warnIfNotUnit && stod(value) != 0) { std::cerr << " ! Warning: value " << v << " does not contain units." << std::endl; }
121 
122  try { return stod(value); }
123  catch (const std::exception& e) {
124  std::cerr << FATALERRORL << "stod exception in gutilities: could not convert string to double. "
125  << "Value: >" << v << "<, error: " << e.what() << std::endl;
126  exit(EC__G4NUMBERERROR);
127  }
128  }
129  else {
130  // Split the input string into the numeric part and the unit part
131  size_t pos = value.find('*');
132  string rootValue = value.substr(0, pos);
133  string units = value.substr(pos + 1);
134 
135  double answer = 0;
136  try { answer = stod(rootValue); }
137  catch (const std::exception& e) {
138  std::cerr << FATALERRORL << "stod exception in gutilities: could not convert string to double. "
139  << "Value: >" << v << "<, error: " << e.what() << std::endl;
140  exit(EC__G4NUMBERERROR);
141  }
142 
143  // Map of unit conversions for easier lookup and maintenance
144  static const std::unordered_map<string, double> unitConversion = {
145  {"m", CLHEP::m},
146  {"cm", CLHEP::cm},
147  {"mm", CLHEP::mm},
148  {"um", 1E-6 * CLHEP::m},
149  {"fm", 1E-15 * CLHEP::m},
150  {"inches", 2.54 * CLHEP::cm},
151  {"inch", 2.54 * CLHEP::cm},
152  {"deg", CLHEP::deg},
153  {"degrees", CLHEP::deg},
154  {"arcmin", CLHEP::deg / 60.0},
155  {"rad", CLHEP::rad},
156  {"mrad", CLHEP::mrad},
157  {"eV", CLHEP::eV},
158  {"MeV", CLHEP::MeV},
159  {"KeV", 0.001 * CLHEP::MeV},
160  {"GeV", CLHEP::GeV},
161  {"T", CLHEP::tesla},
162  {"T/m", CLHEP::tesla / CLHEP::m},
163  {"Tesla", CLHEP::tesla},
164  {"gauss", CLHEP::gauss},
165  {"kilogauss", CLHEP::gauss * 1000},
166  {"s", CLHEP::s},
167  {"ns", CLHEP::ns},
168  {"ms", CLHEP::ms},
169  {"us", CLHEP::us},
170  {"counts", 1}
171  };
172 
173  auto it = unitConversion.find(units);
174  if (it != unitConversion.end()) { answer *= it->second; }
175  else { std::cerr << GWARNING << ">" << units << "<: unit not recognized for string <" << v << ">" << std::endl; }
176 
177  return answer;
178  }
179 }
180 
181 double getG4Number(double input, const string& unit) {
182  string gnumber = std::to_string(input) + "*" + unit;
183  return getG4Number(gnumber, true);
184 }
185 
186 vector<double> getG4NumbersFromStringVector(const vector<string>& vstring, bool warnIfNotUnit) {
187  vector<double> output;
188  output.reserve(vstring.size());
189 
190  for (const auto& s : vstring) { output.push_back(getG4Number(s, warnIfNotUnit)); }
191 
192  return output;
193 }
194 
195 vector<double> getG4NumbersFromString(const string& vstring, bool warnIfNotUnit) {
196  return getG4NumbersFromStringVector(getStringVectorFromStringWithDelimiter(vstring, ","), warnIfNotUnit);
197 }
198 
199 
200 string parseFileAndRemoveComments(const string& filename, const string& commentChars, int verbosity) {
201  // Reading file
202  std::ifstream in(filename);
203  if (!in) {
204  std::cerr << FATALERRORL << "can't open input file " << filename << ". Check your spelling. " << std::endl;
205  exit(EC__FILENOTFOUND);
206  }
207 
208  std::stringstream strStream;
209  if (verbosity > 0) { std::cout << std::endl << CIRCLEITEM << " Loading string from " << filename << std::endl; }
210  strStream << in.rdbuf(); // Read the file
211  in.close();
212 
213  string parsedString = strStream.str();
214 
215  // Removing all occurrences of commentChars
216  size_t nFPos;
217  while ((nFPos = parsedString.find(commentChars)) != string::npos) {
218  size_t firstNL = parsedString.rfind('\n', nFPos);
219  size_t secondNL = parsedString.find('\n', nFPos);
220  parsedString.erase(firstNL, secondNL - firstNL);
221  }
222 
223  return parsedString;
224 }
225 
226 string retrieveStringBetweenChars(const string& input, const string& firstDelimiter, const string& secondDelimiter) {
227  size_t firstpos = input.find(firstDelimiter);
228  size_t secondpos = input.find(secondDelimiter);
229 
230  if (firstpos == string::npos || secondpos == string::npos) { return ""; }
231  return input.substr(firstpos + firstDelimiter.length(), secondpos - firstpos - firstDelimiter.length());
232 }
233 
234 vector<string> getStringVectorFromStringWithDelimiter(const string& input, const string& x) {
235  vector<string> pvalues;
236  string tmp;
237 
238  for (char ch : input) {
239  if (ch != x[0]) { tmp += ch; }
240  else {
241  if (!tmp.empty()) {
242  pvalues.push_back(removeLeadingAndTrailingSpacesFromString(tmp));
243  tmp.clear();
244  }
245  }
246  }
247 
248  if (!tmp.empty()) { pvalues.push_back(removeLeadingAndTrailingSpacesFromString(tmp)); }
249 
250  return pvalues;
251 }
252 
253 
254 // string search for a path with <name> from a possible list of absolute paths
255 // returns UNINITIALIZEDSTRINGQUANTITY if not found
256 // the filesystem solution does not work on linux systems.
257 // TODO: periodically try this?
258 //#include <filesystem>
259 //
260 // string searchForDirInLocations(string dirName, vector <string> possibleLocations) {
261 //
262 // for (auto trialLocation: possibleLocations) {
263 // string possibleDir = trialLocation + "/" + dirName;
264 // if (std::filesystem::exists(possibleDir)) {
265 // return possibleDir;
266 // }
267 // }
268 // return UNINITIALIZEDSTRINGQUANTITY;
269 // }
270 //
271 // vector <string> getListOfFilesInDirectory(string dirName, vector <string> extensions) {
272 //
273 // vector <string> fileList;
274 //
275 // for (const auto &entry: std::filesystem::directory_iterator(dirName)) {
276 // for (auto &extension: extensions) {
277 // if (entry.path().extension() == extension) {
278 // fileList.push_back(entry.path().filename());
279 // }
280 // }
281 // }
282 //
283 // return fileList;
284 // }
285 // end of TODO
286 
287 #include <dirent.h>
288 #include <sys/stat.h>
289 
290 bool directoryExists(const std::string& path) {
291  struct stat info{};
292  if (stat(path.c_str(), &info) != 0) {
293  return false; // Path does not exist
294  }
295  return (info.st_mode & S_IFDIR) != 0; // Check if it's a directory
296 }
297 
298 string searchForDirInLocations(const string& dirName, const vector<string>& possibleLocations) {
299  for (const auto& trialLocation : possibleLocations) {
300  string possibleDir = trialLocation + "/" + dirName;
301  if (directoryExists(possibleDir)) { return possibleDir; }
302  }
303  return "UNINITIALIZEDSTRINGQUANTITY";
304 }
305 
306 
307 bool hasExtension(const std::string& filename, const std::vector<std::string>& extensions) {
308  for (const auto& ext : extensions) {
309  if (filename.size() >= ext.size() &&
310  filename.compare(filename.size() - ext.size(), ext.size(), ext) == 0) { return true; }
311  }
312  return false;
313 }
314 
315 vector<string> getListOfFilesInDirectory(const string& dirName, const vector<string>& extensions) {
316  vector<string> fileList;
317 
318  DIR* dir = opendir(dirName.c_str());
319  if (dir) {
320  struct dirent* entry;
321  while ((entry = readdir(dir)) != nullptr) {
322  struct stat info{};
323  string filepath = dirName + "/" + entry->d_name;
324  if (stat(filepath.c_str(), &info) == 0 && S_ISREG(info.st_mode)) {
325  string filename = entry->d_name;
326  if (hasExtension(filename, extensions)) { fileList.push_back(filename); }
327  }
328  }
329  closedir(dir);
330  }
331 
332  return fileList;
333 }
334 
335 string convertToLowercase(const string& str) {
336  string lower = str;
337  transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
338  return lower;
339 }
340 
341 
342 template <class KEY, class VALUE>
343 vector<KEY> getKeys(const map<KEY, VALUE>& map) {
344  vector<KEY> keys;
345  keys.reserve(map.size()); // Reserve space for efficiency
346 
347  for (const auto& it : map) { keys.push_back(it.first); }
348 
349  return keys;
350 }
351 
352 randomModel stringToRandomModel(const std::string& str) {
353  static const std::unordered_map<std::string, randomModel> strToEnum = {
354  {"uniform", uniform},
355  {"gaussian", gaussian},
356  {"cosine", cosine},
357  {"sphere", sphere}
358  };
359 
360  auto it = strToEnum.find(str);
361  if (it != strToEnum.end()) { return it->second; }
362  else { throw std::invalid_argument("Invalid string for randomModel: " + str); }
363 }
364 
365 
366 G4Colour makeColour(std::string_view code) {
367  if (code.empty()) throw std::invalid_argument("empty colour string");
368  if (code.front() == '#') code.remove_prefix(1);
369  if (code.size() != 6 && code.size() != 7)
370  throw std::invalid_argument("colour must have 6 or 7 hex digits");
371 
372  auto hexNibble = [](char c) -> unsigned {
373  if ('0' <= c && c <= '9') return c - '0';
374  c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
375  if ('A' <= c && c <= 'F') return c - 'A' + 10;
376  throw std::invalid_argument("invalid hex digit");
377  };
378 
379  // ---- parse RRGGBB ----
380  unsigned rgb = 0;
381  for (int i = 0; i < 6; ++i)
382  rgb = (rgb << 4) | hexNibble(code[i]);
383 
384  auto byteToDouble = [](unsigned byte) { return byte / 255.0; };
385  double r = byteToDouble((rgb >> 16) & 0xFF);
386  double g = byteToDouble((rgb >> 8) & 0xFF);
387  double b = byteToDouble(rgb & 0xFF);
388 
389  // ---- optional transparency nibble ----
390  double a = 1.0;
391  if (code.size() == 7)
392  a = hexNibble(code[6]) / 15.0;
393 
394  return {r, g, b, a}; // G4Colour
395 }
396 
397 
398 }
399 
#define GWARNING
Warning label.
#define EC__FILENOTFOUND
File not found error code.
#define CIRCLEITEM
Symbol for circle item.
#define EC__G4NUMBERERROR
G4 number error code.
#define FATALERRORL
Fatal error label.
string replaceAllStringsWithString(const string &source, const string &from, const string &to)
Replaces all occurrences of a substring with another string.
Definition: gutilities.cc:78
double getG4Number(const string &v, bool warnIfNotUnit)
Converts a string representation of a number with optional units to a double.
Definition: gutilities.cc:115
bool hasExtension(const std::string &filename, const std::vector< std::string > &extensions)
Checks if a filename has one of the specified extensions.
Definition: gutilities.cc:307
vector< double > getG4NumbersFromString(const string &vstring, bool warnIfNotUnit)
Converts a comma-separated string of numbers with units to a vector of doubles.
Definition: gutilities.cc:195
string removeAllSpacesFromString(const std::string &str)
Removes all spaces from a string.
Definition: gutilities.cc:32
vector< string > getStringVectorFromStringWithDelimiter(const string &input, const string &x)
Splits a string into a vector of substrings using a specified delimiter.
Definition: gutilities.cc:234
randomModel stringToRandomModel(const std::string &str)
Converts a string to a corresponding randomModel enum value.
Definition: gutilities.cc:352
constexpr const char * to_string(randomModel m) noexcept
Definition: gutilities.h:284
vector< string > getListOfFilesInDirectory(const string &dirName, const vector< string > &extensions)
Retrieves a list of files with specific extensions from a directory.
Definition: gutilities.cc:315
vector< double > getG4NumbersFromStringVector(const vector< string > &vstring, bool warnIfNotUnit)
Converts a vector of strings representing numbers with units to a vector of doubles.
Definition: gutilities.cc:186
string retrieveStringBetweenChars(const string &input, const string &firstDelimiter, const string &secondDelimiter)
Retrieves a substring between two specified delimiters in a string.
Definition: gutilities.cc:226
string replaceCharInStringWithChars(const std::string &input, const std::string &toReplace, const std::string &replacement)
Replaces all occurrences of specified characters in a string with another string.
Definition: gutilities.cc:68
string fillDigits(const string &word, const string &c, int ndigits)
Pads a string with a specified character until it reaches a desired length.
Definition: gutilities.cc:100
randomModel
Enumeration of random models.
Definition: gutilities.h:265
@ gaussian
Gaussian distribution.
Definition: gutilities.h:267
@ uniform
Uniform distribution.
Definition: gutilities.h:266
@ sphere
Sphere distribution.
Definition: gutilities.h:269
@ cosine
Cosine distribution.
Definition: gutilities.h:268
string getDirFromPath(const std::string &path)
Extracts the directory path from a given file path.
Definition: gutilities.cc:47
string convertToLowercase(const string &str)
Converts a string to lowercase.
Definition: gutilities.cc:335
bool directoryExists(const std::string &path)
Checks if a directory exists at the given path.
Definition: gutilities.cc:290
G4Colour makeColour(std::string_view code)
Convert a hex colour string to G4Colour.
Definition: gutilities.cc:366
vector< KEY > getKeys(const map< KEY, VALUE > &map)
Retrieves all keys from a map.
Definition: gutilities.cc:343
string removeLeadingAndTrailingSpacesFromString(const std::string &input)
Removes leading and trailing spaces and tabs from a string.
Definition: gutilities.cc:21
string searchForDirInLocations(const string &dirName, const vector< string > &possibleLocations)
Searches for a directory within a list of possible locations.
Definition: gutilities.cc:298
string parseFileAndRemoveComments(const string &filename, const string &commentChars, int verbosity)
Parses a file and removes all lines containing specified comment characters.
Definition: gutilities.cc:200
string getFileFromPath(const std::string &path)
Extracts the filename from a given file path.
Definition: gutilities.cc:38
vector< std::string > getStringVectorFromString(const std::string &input)
Splits a string into a vector of strings using spaces as delimiters.
Definition: gutilities.cc:56