guts
Loading...
Searching...
No Matches
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
19namespace gutilities {
20
21string 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
32string 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
38string 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
47string 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
53namespace fs = std::filesystem;
54
55
56vector<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
68string 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
78string 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
100string 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// add near your includes:
115#include <locale.h> // strtod_l / _strtod_l
116
117// Parse a whole string_view as a double using the "C" numeric locale.
118// Returns true on full-consume success; false otherwise.
119static bool parse_double_clocale(std::string_view sv, double& out) {
120 std::string tmp(sv); // strtod_l needs a 0-terminated buffer
121#if defined(_WIN32)
122 _locale_t loc = _create_locale(LC_NUMERIC, "C");
123 char* end = nullptr;
124 out = _strtod_l(tmp.c_str(), &end, loc);
125 _free_locale(loc);
126 return end == tmp.c_str() + tmp.size();
127#else
128 locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
129 char* end = nullptr;
130 out = strtod_l(tmp.c_str(), &end, loc);
131 freelocale(loc);
132 return end == tmp.c_str() + tmp.size();
133#endif
134}
135
136
137// --- strict, locale-independent getG4Number: only accepts '*' as unit sep ---
138double getG4Number(const string& v, bool warnIfNotUnit) {
140 if (value.empty()) {
141 std::cerr << FATALERRORL << "empty numeric string.\n";
142 exit(EC__G4NUMBERERROR);
143 }
144
145 // Normalize a single decimal comma to dot when no dot is present
146 if (value.find('.') == string::npos) {
147 size_t firstComma = value.find(',');
148 if (firstComma != string::npos && value.find(',', firstComma + 1) == string::npos) {
149 value = replaceAllStringsWithString(value, ",", ".");
150 }
151 }
152
153 const size_t starCount = static_cast<size_t>(std::count(value.begin(), value.end(), '*'));
154
155 // --- Case 1: no '*' → pure number (strictly no trailing garbage) ---
156 if (value.find('*') == string::npos) {
157 double out = 0.0;
158 // normalize a single decimal comma to dot if needed
159 if (value.find('.') == string::npos) {
160 auto firstComma = value.find(',');
161 if (firstComma != string::npos && value.find(',', firstComma + 1) == string::npos)
162 value = replaceAllStringsWithString(value, ",", ".");
163 }
164 if (!parse_double_clocale(value, out)) {
165 std::cerr << FATALERRORL << "missing '*' before unit or invalid number in <" << v << ">.\n";
166 exit(EC__G4NUMBERERROR);
167 }
168 if (warnIfNotUnit && out != 0.0) {
169 std::cerr << " ! Warning: value " << v << " does not contain units." << std::endl;
170 }
171 return out;
172 }
173
174
175 // --- Case 2: must be exactly one '*' ---
176 if (starCount > 1) {
177 std::cerr << FATALERRORL << "multiple '*' separators are not allowed in <" << v << ">.\n";
178 exit(EC__G4NUMBERERROR);
179 }
180
181 // --- Exactly one '*' → split "<number>*<unit>" ---
182 const size_t pos = value.find('*');
183 string left = removeLeadingAndTrailingSpacesFromString(value.substr(0, pos));
184 string right = removeLeadingAndTrailingSpacesFromString(value.substr(pos + 1));
185 if (left.empty() || right.empty()) {
186 std::cerr << FATALERRORL << "expected '<number>*<unit>', got <" << v << ">.\n";
187 exit(EC__G4NUMBERERROR);
188 }
189
190 // normalize a single decimal comma in the numeric part
191 if (left.find('.') == string::npos) {
192 auto c = left.find(',');
193 if (c != string::npos && left.find(',', c + 1) == string::npos)
194 left = replaceAllStringsWithString(left, ",", ".");
195 }
196
197 double numeric = 0.0;
198 if (!parse_double_clocale(left, numeric)) {
199 std::cerr << FATALERRORL << "invalid numeric part before '*' in <" << v << ">.\n";
200 exit(EC__G4NUMBERERROR);
201 }
202
203 // sanitize unit and proceed with your existing unit table logic...
204 right = replaceAllStringsWithString(right, "µ", "u");
205 string unit = convertToLowercase(right);
206
207 // (keep your unitConversion map and SI prefix handling as-is)
208
209
210 // Unit table (lowercase keys)
211 static const std::unordered_map<string, double> unitConversion = {
212 // length
213 {"m", CLHEP::m}, {"cm", CLHEP::cm}, {"mm", CLHEP::mm},
214 {"um", 1E-6 * CLHEP::m}, {"fm", 1E-15 * CLHEP::m},
215 {"inch", 2.54 * CLHEP::cm}, {"inches", 2.54 * CLHEP::cm},
216 // angle
217 {"deg", CLHEP::deg}, {"degrees", CLHEP::deg}, {"arcmin", CLHEP::deg / 60.0},
218 {"rad", CLHEP::rad}, {"mrad", CLHEP::mrad},
219 // energy
220 {"ev", CLHEP::eV}, {"kev", 1e3 * CLHEP::eV}, {"mev", CLHEP::MeV}, {"gev", CLHEP::GeV},
221 // magnetic field
222 {"t", CLHEP::tesla}, {"tesla", CLHEP::tesla}, {"t/m", CLHEP::tesla / CLHEP::m},
223 {"gauss", CLHEP::gauss}, {"kilogauss", 1000.0 * CLHEP::gauss},
224 // time
225 {"s", CLHEP::s}, {"ns", CLHEP::ns}, {"ms", CLHEP::ms}, {"us", CLHEP::us},
226 // dimensionless
227 {"counts", 1.0}
228 };
229
230 // Exact unit match
231 if (auto it = unitConversion.find(unit); it != unitConversion.end()) {
232 return numeric * it->second;
233 }
234
235 // SI prefix handling: mT, uT, mm, um, etc.
236 auto si_prefix_factor = [](char p) -> double {
237 switch (p) {
238 case 'Y': return 1e24; case 'Z': return 1e21; case 'E': return 1e18;
239 case 'P': return 1e15; case 'T': return 1e12; case 'G': return 1e9;
240 case 'M': return 1e6; case 'k': return 1e3; case 'h': return 1e2;
241 case 'd': return 1e-1; case 'c': return 1e-2; case 'm': return 1e-3;
242 case 'u': return 1e-6; case 'n': return 1e-9; case 'p': return 1e-12;
243 case 'f': return 1e-15; case 'a': return 1e-18; case 'z': return 1e-21; case 'y': return 1e-24;
244 default: return 0.0;
245 }
246 };
247
248 if (unit.size() >= 2) {
249 const double pf = si_prefix_factor(unit.front());
250 if (pf != 0.0) {
251 const string base = unit.substr(1);
252 if (auto it2 = unitConversion.find(base); it2 != unitConversion.end()) {
253 return numeric * pf * it2->second;
254 }
255 }
256 }
257
258 // Unknown unit: warn & return numeric part (keep your legacy behavior)
259 std::cerr << GWARNING << ">" << right << "<: unit not recognized for string <" << v << ">" << std::endl;
260 return numeric;
261}
262
263
264double getG4Number(double input, const string& unit) {
265 string gnumber = std::to_string(input) + "*" + unit;
266 return getG4Number(gnumber, true);
267}
268
269vector<double> getG4NumbersFromStringVector(const vector<string>& vstring, bool warnIfNotUnit) {
270 vector<double> output;
271 output.reserve(vstring.size());
272
273 for (const auto& s : vstring) { output.push_back(getG4Number(s, warnIfNotUnit)); }
274
275 return output;
276}
277
278vector<double> getG4NumbersFromString(const string& vstring, bool warnIfNotUnit) {
279 return getG4NumbersFromStringVector(getStringVectorFromStringWithDelimiter(vstring, ","), warnIfNotUnit);
280}
281
282
283string parseFileAndRemoveComments(const string& filename, const string& commentChars, int verbosity) {
284 // Reading file
285 std::ifstream in(filename);
286 if (!in) {
287 std::cerr << FATALERRORL << "can't open input file " << filename << ". Check your spelling. " << std::endl;
288 exit(EC__FILENOTFOUND);
289 }
290
291 std::stringstream strStream;
292 if (verbosity > 0) { std::cout << std::endl << CIRCLEITEM << " Loading string from " << filename << std::endl; }
293 strStream << in.rdbuf(); // Read the file
294 in.close();
295
296 string parsedString = strStream.str();
297
298 // Removing all occurrences of commentChars
299 size_t nFPos;
300 while ((nFPos = parsedString.find(commentChars)) != string::npos) {
301 size_t firstNL = parsedString.rfind('\n', nFPos);
302 size_t secondNL = parsedString.find('\n', nFPos);
303 parsedString.erase(firstNL, secondNL - firstNL);
304 }
305
306 return parsedString;
307}
308
309string retrieveStringBetweenChars(const string& input, const string& firstDelimiter, const string& secondDelimiter) {
310 size_t firstpos = input.find(firstDelimiter);
311 size_t secondpos = input.find(secondDelimiter);
312
313 if (firstpos == string::npos || secondpos == string::npos) { return ""; }
314 return input.substr(firstpos + firstDelimiter.length(), secondpos - firstpos - firstDelimiter.length());
315}
316
317vector<string> getStringVectorFromStringWithDelimiter(const string& input, const string& x) {
318 vector<string> pvalues;
319 string tmp;
320
321 for (char ch : input) {
322 if (ch != x[0]) { tmp += ch; }
323 else {
324 if (!tmp.empty()) {
325 pvalues.push_back(removeLeadingAndTrailingSpacesFromString(tmp));
326 tmp.clear();
327 }
328 }
329 }
330
331 if (!tmp.empty()) { pvalues.push_back(removeLeadingAndTrailingSpacesFromString(tmp)); }
332
333 return pvalues;
334}
335
336
337// string search for a path with <name> from a possible list of absolute paths
338// returns UNINITIALIZEDSTRINGQUANTITY if not found
339// the filesystem solution does not work on linux systems.
340// TODO: periodically try this?
341//#include <filesystem>
342//
343// string searchForDirInLocations(string dirName, vector <string> possibleLocations) {
344//
345// for (auto trialLocation: possibleLocations) {
346// string possibleDir = trialLocation + "/" + dirName;
347// if (std::filesystem::exists(possibleDir)) {
348// return possibleDir;
349// }
350// }
351// return UNINITIALIZEDSTRINGQUANTITY;
352// }
353//
354// vector <string> getListOfFilesInDirectory(string dirName, vector <string> extensions) {
355//
356// vector <string> fileList;
357//
358// for (const auto &entry: std::filesystem::directory_iterator(dirName)) {
359// for (auto &extension: extensions) {
360// if (entry.path().extension() == extension) {
361// fileList.push_back(entry.path().filename());
362// }
363// }
364// }
365//
366// return fileList;
367// }
368// end of TODO
369
370#include <dirent.h>
371#include <sys/stat.h>
372
373bool directoryExists(const std::string& path) {
374 struct stat info{};
375 if (stat(path.c_str(), &info) != 0) {
376 return false; // Path does not exist
377 }
378 return (info.st_mode & S_IFDIR) != 0; // Check if it's a directory
379}
380
381string searchForDirInLocations(const string& dirName, const vector<string>& possibleLocations) {
382 for (const auto& trialLocation : possibleLocations) {
383 string possibleDir = trialLocation + "/" + dirName;
384 if (directoryExists(possibleDir)) { return possibleDir; }
385 }
386 return "UNINITIALIZEDSTRINGQUANTITY";
387}
388
389
390bool hasExtension(const std::string& filename, const std::vector<std::string>& extensions) {
391 for (const auto& ext : extensions) {
392 if (filename.size() >= ext.size() &&
393 filename.compare(filename.size() - ext.size(), ext.size(), ext) == 0) { return true; }
394 }
395 return false;
396}
397
398vector<string> getListOfFilesInDirectory(const string& dirName, const vector<string>& extensions) {
399 vector<string> fileList;
400
401 DIR* dir = opendir(dirName.c_str());
402 if (dir) {
403 struct dirent* entry;
404 while ((entry = readdir(dir)) != nullptr) {
405 struct stat info{};
406 string filepath = dirName + "/" + entry->d_name;
407 if (stat(filepath.c_str(), &info) == 0 && S_ISREG(info.st_mode)) {
408 string filename = entry->d_name;
409 if (hasExtension(filename, extensions)) { fileList.push_back(filename); }
410 }
411 }
412 closedir(dir);
413 }
414
415 return fileList;
416}
417
418string convertToLowercase(const string& str) {
419 string lower = str;
420 transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
421 return lower;
422}
423
424
425template <class KEY, class VALUE>
426vector<KEY> getKeys(const map<KEY, VALUE>& map) {
427 vector<KEY> keys;
428 keys.reserve(map.size()); // Reserve space for efficiency
429
430 for (const auto& it : map) { keys.push_back(it.first); }
431
432 return keys;
433}
434
435randomModel stringToRandomModel(const std::string& str) {
436 static const std::unordered_map<std::string, randomModel> strToEnum = {
437 {"uniform", uniform},
438 {"gaussian", gaussian},
439 {"cosine", cosine},
440 {"sphere", sphere}
441 };
442
443 auto it = strToEnum.find(str);
444 if (it != strToEnum.end()) { return it->second; }
445 else { throw std::invalid_argument("Invalid string for randomModel: " + str); }
446}
447
448
449G4Colour makeColour(std::string_view code) {
450 if (code.empty()) throw std::invalid_argument("empty colour string");
451 if (code.front() == '#') code.remove_prefix(1);
452 if (code.size() != 6 && code.size() != 7)
453 throw std::invalid_argument("colour must have 6 or 7 hex digits");
454
455 auto hexNibble = [](char c) -> unsigned {
456 if ('0' <= c && c <= '9') return c - '0';
457 c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
458 if ('A' <= c && c <= 'F') return c - 'A' + 10;
459 throw std::invalid_argument("invalid hex digit");
460 };
461
462 // ---- parse RRGGBB ----
463 unsigned rgb = 0;
464 for (int i = 0; i < 6; ++i)
465 rgb = (rgb << 4) | hexNibble(code[i]);
466
467 auto byteToDouble = [](unsigned byte) { return byte / 255.0; };
468 double r = byteToDouble((rgb >> 16) & 0xFF);
469 double g = byteToDouble((rgb >> 8) & 0xFF);
470 double b = byteToDouble(rgb & 0xFF);
471
472 // ---- optional transparency nibble ----
473 double a = 1.0;
474 if (code.size() == 7)
475 a = hexNibble(code[6]) / 15.0;
476
477 return {r, g, b, a}; // G4Colour
478}
479
480std::optional<std::string> searchForFileInLocations(
481 const std::vector<std::string>& locations,
482 std::string_view filename) {
483 namespace fs = std::filesystem;
484
485 for (const auto& loc : locations) {
486 if (loc.empty()) continue;
487
488 fs::path p(loc);
489 fs::path candidate = (!filename.empty() && fs::is_directory(p))
490 ? (p / filename)
491 : p;
492
493 std::error_code ec;
494 const bool ok = fs::exists(candidate, ec) && fs::is_regular_file(candidate, ec);
495 if (ok) return candidate.string();
496 }
497 return std::nullopt;
498}
499
500}
#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.
bool hasExtension(const std::string &filename, const std::vector< std::string > &extensions)
Checks if a filename has one of the specified extensions.
vector< double > getG4NumbersFromString(const string &vstring, bool warnIfNotUnit)
Converts a comma-separated string of numbers with units to a vector of doubles.
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.
randomModel stringToRandomModel(const std::string &str)
Converts a string to a corresponding randomModel enum value.
vector< string > getListOfFilesInDirectory(const string &dirName, const vector< string > &extensions)
Retrieves a list of files with specific extensions from a directory.
vector< double > getG4NumbersFromStringVector(const vector< string > &vstring, bool warnIfNotUnit)
Converts a vector of strings representing numbers with units to a vector of doubles.
string retrieveStringBetweenChars(const string &input, const string &firstDelimiter, const string &secondDelimiter)
Retrieves a substring between two specified delimiters in a string.
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.
randomModel
Enumeration of random models.
Definition gutilities.h:269
@ gaussian
Gaussian distribution.
Definition gutilities.h:271
@ uniform
Uniform distribution.
Definition gutilities.h:270
@ sphere
Sphere distribution.
Definition gutilities.h:273
@ cosine
Cosine distribution.
Definition gutilities.h:272
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.
bool directoryExists(const std::string &path)
Checks if a directory exists at the given path.
G4Colour makeColour(std::string_view code)
Convert a hex colour string to G4Colour.
vector< KEY > getKeys(const map< KEY, VALUE > &map)
Retrieves all keys from a map.
string removeLeadingAndTrailingSpacesFromString(const std::string &input)
Removes leading and trailing spaces and tabs from a string.
Definition gutilities.cc:21
std::optional< std::string > searchForFileInLocations(const std::vector< std::string > &locations, std::string_view filename)
string searchForDirInLocations(const string &dirName, const vector< string > &possibleLocations)
Searches for a directory within a list of possible locations.
string parseFileAndRemoveComments(const string &filename, const string &commentChars, int verbosity)
Parses a file and removes all lines containing specified comment characters.
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