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 
67 string getFullPathFromPath(const std::string& path) {
68  namespace fs = std::filesystem;
69  fs::path p(path); // convert the argument to fs::path
70 
71  /* 1) Already absolute ------------------------------------------------- */
72  if (p.is_absolute())
73  return fs::weakly_canonical(p);
74 
75  /* 2) Relative path (contains a slash) --------------------------------- */
76  if (p.has_parent_path()) // e.g. "./prog" or "bin/prog"
77  return fs::weakly_canonical(fs::current_path() / p);
78 
79  /* 3) Bare filename: search PATH -------------------------------------- */
80  const char* env_path = std::getenv("PATH");
81  if (env_path) {
82 #if defined(_WIN32)
83  const char delim = ';';
84 #else
85  const char delim = ':';
86 #endif
87  std::stringstream ss(env_path);
88  std::string dir;
89  while (std::getline(ss, dir, delim)) {
90  fs::path candidate = fs::path(dir) / p;
91  if (fs::exists(candidate))
92  return fs::weakly_canonical(candidate);
93  }
94  }
95 
96  throw std::runtime_error("cannot resolve executable path from '" + path + "'");
97 }
98 
99 vector<std::string> getStringVectorFromString(const std::string& input) {
100  std::vector<std::string> pvalues;
101  std::stringstream plist(input);
102  string tmp;
103  while (plist >> tmp) {
104  string trimmed = removeLeadingAndTrailingSpacesFromString(tmp);
105  if (!trimmed.empty()) { pvalues.push_back(trimmed); }
106  }
107  return pvalues;
108 }
109 
110 // Replace all occurences of specific chars in a string with a string
111 string replaceCharInStringWithChars(const std::string& input, const std::string& toReplace,
112  const std::string& replacement) {
113  string output;
114  for (const char& ch : input) {
115  if (toReplace.find(ch) != std::string::npos) { output.append(replacement); }
116  else { output.push_back(ch); }
117  }
118  return output;
119 }
120 
121 string replaceAllStringsWithString(const string& source, const string& from, const string& to) {
122  if (from.empty()) return source; // Avoid infinite loop
123 
124  string newString;
125  size_t lastPos = 0;
126  size_t findPos = source.find(from, lastPos);
127 
128  while (findPos != string::npos) {
129  // Append part before the match and the replacement string
130  newString.append(source, lastPos, findPos - lastPos);
131  newString += to;
132  lastPos = findPos + from.length();
133  findPos = source.find(from, lastPos);
134  }
135 
136  // Append the remaining part of the string after the last occurrence
137  newString += source.substr(lastPos);
138 
139  return newString;
140 }
141 
142 
143 string fillDigits(const string& word, const string& c, int ndigits) {
144  if (c.empty() || ndigits <= static_cast<int>(word.size())) return word; // Return original if no padding needed
145 
146  string filled;
147 
148  int toFill = ndigits - static_cast<int>(word.size());
149  filled.reserve(ndigits);
150 
151  filled.append(toFill, c[0]); // Use the first character of the string 'c'
152  filled += word;
153 
154  return filled;
155 }
156 
157 
158 double getG4Number(const string& v, bool warnIfNotUnit) {
160 
161  // If no '*' is found, the input is assumed to be a number without units
162  if (value.find('*') == string::npos) {
163  if (!value.empty() && warnIfNotUnit && stod(value) != 0) { std::cerr << " ! Warning: value " << v << " does not contain units." << std::endl; }
164 
165  try { return stod(value); }
166  catch (const std::exception& e) {
167  std::cerr << FATALERRORL << "stod exception in gutilities: could not convert string to double. "
168  << "Value: >" << v << "<, error: " << e.what() << std::endl;
169  exit(EC__G4NUMBERERROR);
170  }
171  }
172  else {
173  // Split the input string into the numeric part and the unit part
174  size_t pos = value.find('*');
175  string rootValue = value.substr(0, pos);
176  string units = value.substr(pos + 1);
177 
178  double answer = 0;
179  try { answer = stod(rootValue); }
180  catch (const std::exception& e) {
181  std::cerr << FATALERRORL << "stod exception in gutilities: could not convert string to double. "
182  << "Value: >" << v << "<, error: " << e.what() << std::endl;
183  exit(EC__G4NUMBERERROR);
184  }
185 
186  // Map of unit conversions for easier lookup and maintenance
187  static const std::unordered_map<string, double> unitConversion = {
188  {"m", CLHEP::m},
189  {"cm", CLHEP::cm},
190  {"mm", CLHEP::mm},
191  {"um", 1E-6 * CLHEP::m},
192  {"fm", 1E-15 * CLHEP::m},
193  {"inches", 2.54 * CLHEP::cm},
194  {"inch", 2.54 * CLHEP::cm},
195  {"deg", CLHEP::deg},
196  {"degrees", CLHEP::deg},
197  {"arcmin", CLHEP::deg / 60.0},
198  {"rad", CLHEP::rad},
199  {"mrad", CLHEP::mrad},
200  {"eV", CLHEP::eV},
201  {"MeV", CLHEP::MeV},
202  {"KeV", 0.001 * CLHEP::MeV},
203  {"GeV", CLHEP::GeV},
204  {"T", CLHEP::tesla},
205  {"T/m", CLHEP::tesla / CLHEP::m},
206  {"Tesla", CLHEP::tesla},
207  {"gauss", CLHEP::gauss},
208  {"kilogauss", CLHEP::gauss * 1000},
209  {"s", CLHEP::s},
210  {"ns", CLHEP::ns},
211  {"ms", CLHEP::ms},
212  {"us", CLHEP::us},
213  {"counts", 1}
214  };
215 
216  auto it = unitConversion.find(units);
217  if (it != unitConversion.end()) { answer *= it->second; }
218  else { std::cerr << GWARNING << ">" << units << "<: unit not recognized for string <" << v << ">" << std::endl; }
219 
220  return answer;
221  }
222 }
223 
224 double getG4Number(double input, const string& unit) {
225  string gnumber = std::to_string(input) + "*" + unit;
226  return getG4Number(gnumber, true);
227 }
228 
229 vector<double> getG4NumbersFromStringVector(const vector<string>& vstring, bool warnIfNotUnit) {
230  vector<double> output;
231  output.reserve(vstring.size());
232 
233  for (const auto& s : vstring) { output.push_back(getG4Number(s, warnIfNotUnit)); }
234 
235  return output;
236 }
237 
238 vector<double> getG4NumbersFromString(const string& vstring, bool warnIfNotUnit) {
239  return getG4NumbersFromStringVector(getStringVectorFromStringWithDelimiter(vstring, ","), warnIfNotUnit);
240 }
241 
242 
243 string parseFileAndRemoveComments(const string& filename, const string& commentChars, int verbosity) {
244  // Reading file
245  std::ifstream in(filename);
246  if (!in) {
247  std::cerr << FATALERRORL << "can't open input file " << filename << ". Check your spelling. " << std::endl;
248  exit(EC__FILENOTFOUND);
249  }
250 
251  std::stringstream strStream;
252  if (verbosity > 0) { std::cout << std::endl << CIRCLEITEM << " Loading string from " << filename << std::endl; }
253  strStream << in.rdbuf(); // Read the file
254  in.close();
255 
256  string parsedString = strStream.str();
257 
258  // Removing all occurrences of commentChars
259  size_t nFPos;
260  while ((nFPos = parsedString.find(commentChars)) != string::npos) {
261  size_t firstNL = parsedString.rfind('\n', nFPos);
262  size_t secondNL = parsedString.find('\n', nFPos);
263  parsedString.erase(firstNL, secondNL - firstNL);
264  }
265 
266  return parsedString;
267 }
268 
269 string retrieveStringBetweenChars(const string& input, const string& firstDelimiter, const string& secondDelimiter) {
270  size_t firstpos = input.find(firstDelimiter);
271  size_t secondpos = input.find(secondDelimiter);
272 
273  if (firstpos == string::npos || secondpos == string::npos) { return ""; }
274  return input.substr(firstpos + firstDelimiter.length(), secondpos - firstpos - firstDelimiter.length());
275 }
276 
277 vector<string> getStringVectorFromStringWithDelimiter(const string& input, const string& x) {
278  vector<string> pvalues;
279  string tmp;
280 
281  for (char ch : input) {
282  if (ch != x[0]) { tmp += ch; }
283  else {
284  if (!tmp.empty()) {
285  pvalues.push_back(removeLeadingAndTrailingSpacesFromString(tmp));
286  tmp.clear();
287  }
288  }
289  }
290 
291  if (!tmp.empty()) { pvalues.push_back(removeLeadingAndTrailingSpacesFromString(tmp)); }
292 
293  return pvalues;
294 }
295 
296 
297 // string search for a path with <name> from a possible list of absolute paths
298 // returns UNINITIALIZEDSTRINGQUANTITY if not found
299 // the filesystem solution does not work on linux systems.
300 // TODO: periodically try this?
301 //#include <filesystem>
302 //
303 // string searchForDirInLocations(string dirName, vector <string> possibleLocations) {
304 //
305 // for (auto trialLocation: possibleLocations) {
306 // string possibleDir = trialLocation + "/" + dirName;
307 // if (std::filesystem::exists(possibleDir)) {
308 // return possibleDir;
309 // }
310 // }
311 // return UNINITIALIZEDSTRINGQUANTITY;
312 // }
313 //
314 // vector <string> getListOfFilesInDirectory(string dirName, vector <string> extensions) {
315 //
316 // vector <string> fileList;
317 //
318 // for (const auto &entry: std::filesystem::directory_iterator(dirName)) {
319 // for (auto &extension: extensions) {
320 // if (entry.path().extension() == extension) {
321 // fileList.push_back(entry.path().filename());
322 // }
323 // }
324 // }
325 //
326 // return fileList;
327 // }
328 // end of TODO
329 
330 #include <dirent.h>
331 #include <sys/stat.h>
332 
333 bool directoryExists(const std::string& path) {
334  struct stat info{};
335  if (stat(path.c_str(), &info) != 0) {
336  return false; // Path does not exist
337  }
338  return (info.st_mode & S_IFDIR) != 0; // Check if it's a directory
339 }
340 
341 string searchForDirInLocations(const string& dirName, const vector<string>& possibleLocations) {
342  for (const auto& trialLocation : possibleLocations) {
343  string possibleDir = trialLocation + "/" + dirName;
344  if (directoryExists(possibleDir)) { return possibleDir; }
345  }
346  return "UNINITIALIZEDSTRINGQUANTITY";
347 }
348 
349 
350 bool hasExtension(const std::string& filename, const std::vector<std::string>& extensions) {
351  for (const auto& ext : extensions) {
352  if (filename.size() >= ext.size() &&
353  filename.compare(filename.size() - ext.size(), ext.size(), ext) == 0) { return true; }
354  }
355  return false;
356 }
357 
358 vector<string> getListOfFilesInDirectory(const string& dirName, const vector<string>& extensions) {
359  vector<string> fileList;
360 
361  DIR* dir = opendir(dirName.c_str());
362  if (dir) {
363  struct dirent* entry;
364  while ((entry = readdir(dir)) != nullptr) {
365  struct stat info{};
366  string filepath = dirName + "/" + entry->d_name;
367  if (stat(filepath.c_str(), &info) == 0 && S_ISREG(info.st_mode)) {
368  string filename = entry->d_name;
369  if (hasExtension(filename, extensions)) { fileList.push_back(filename); }
370  }
371  }
372  closedir(dir);
373  }
374 
375  return fileList;
376 }
377 
378 string convertToLowercase(const string& str) {
379  string lower = str;
380  transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
381  return lower;
382 }
383 
384 
385 template <class KEY, class VALUE>
386 vector<KEY> getKeys(const map<KEY, VALUE>& map) {
387  vector<KEY> keys;
388  keys.reserve(map.size()); // Reserve space for efficiency
389 
390  for (const auto& it : map) { keys.push_back(it.first); }
391 
392  return keys;
393 }
394 
395 randomModel stringToRandomModel(const std::string& str) {
396  static const std::unordered_map<std::string, randomModel> strToEnum = {
397  {"uniform", uniform},
398  {"gaussian", gaussian},
399  {"cosine", cosine},
400  {"sphere", sphere}
401  };
402 
403  auto it = strToEnum.find(str);
404  if (it != strToEnum.end()) { return it->second; }
405  else { throw std::invalid_argument("Invalid string for randomModel: " + str); }
406 }
407 
408 
409 G4Colour makeColour(std::string_view code) {
410  if (code.empty()) throw std::invalid_argument("empty colour string");
411  if (code.front() == '#') code.remove_prefix(1);
412  if (code.size() != 6 && code.size() != 7)
413  throw std::invalid_argument("colour must have 6 or 7 hex digits");
414 
415  auto hexNibble = [](char c) -> unsigned {
416  if ('0' <= c && c <= '9') return c - '0';
417  c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
418  if ('A' <= c && c <= 'F') return c - 'A' + 10;
419  throw std::invalid_argument("invalid hex digit");
420  };
421 
422  // ---- parse RRGGBB ----
423  unsigned rgb = 0;
424  for (int i = 0; i < 6; ++i)
425  rgb = (rgb << 4) | hexNibble(code[i]);
426 
427  auto byteToDouble = [](unsigned byte) { return byte / 255.0; };
428  double r = byteToDouble((rgb >> 16) & 0xFF);
429  double g = byteToDouble((rgb >> 8) & 0xFF);
430  double b = byteToDouble(rgb & 0xFF);
431 
432  // ---- optional transparency nibble ----
433  double a = 1.0;
434  if (code.size() == 7)
435  a = hexNibble(code[6]) / 15.0;
436 
437  return {r, g, b, a}; // G4Colour
438 }
439 
440 
441 }
#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:121
double getG4Number(const string &v, bool warnIfNotUnit)
Converts a string representation of a number with optional units to a double.
Definition: gutilities.cc:158
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:350
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:238
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:277
randomModel stringToRandomModel(const std::string &str)
Converts a string to a corresponding randomModel enum value.
Definition: gutilities.cc:395
constexpr const char * to_string(randomModel m) noexcept
Definition: gutilities.h:294
vector< string > getListOfFilesInDirectory(const string &dirName, const vector< string > &extensions)
Retrieves a list of files with specific extensions from a directory.
Definition: gutilities.cc:358
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:229
string retrieveStringBetweenChars(const string &input, const string &firstDelimiter, const string &secondDelimiter)
Retrieves a substring between two specified delimiters in a string.
Definition: gutilities.cc:269
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:111
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:143
randomModel
Enumeration of random models.
Definition: gutilities.h:275
@ gaussian
Gaussian distribution.
Definition: gutilities.h:277
@ uniform
Uniform distribution.
Definition: gutilities.h:276
@ sphere
Sphere distribution.
Definition: gutilities.h:279
@ cosine
Cosine distribution.
Definition: gutilities.h:278
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:378
bool directoryExists(const std::string &path)
Checks if a directory exists at the given path.
Definition: gutilities.cc:333
G4Colour makeColour(std::string_view code)
Convert a hex colour string to G4Colour.
Definition: gutilities.cc:409
vector< KEY > getKeys(const map< KEY, VALUE > &map)
Retrieves all keys from a map.
Definition: gutilities.cc:386
string removeLeadingAndTrailingSpacesFromString(const std::string &input)
Removes leading and trailing spaces and tabs from a string.
Definition: gutilities.cc:21
string getFullPathFromPath(const std::string &path)
Definition: gutilities.cc:67
string searchForDirInLocations(const string &dirName, const vector< string > &possibleLocations)
Searches for a directory within a list of possible locations.
Definition: gutilities.cc:341
string parseFileAndRemoveComments(const string &filename, const string &commentChars, int verbosity)
Parses a file and removes all lines containing specified comment characters.
Definition: gutilities.cc:243
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:99