6#include "CLHEP/Units/PhysicalConstants.h"
9#include "G4UImanager.hh"
20#include <unordered_map>
38 size_t startPos = input.find_first_not_of(
" \t");
39 size_t endPos = input.find_last_not_of(
" \t");
42 if (startPos == std::string::npos || endPos == std::string::npos) {
return ""; }
45 return input.substr(startPos, endPos - startPos + 1);
58 while (!s.empty() && std::isspace(
static_cast<unsigned char>(s.front()))) s.remove_prefix(1);
59 while (!s.empty() && std::isspace(
static_cast<unsigned char>(s.back()))) s.remove_suffix(1);
70 result.erase(std::remove(result.begin(), result.end(),
' '), result.end());
80 std::size_t lastSlashPos = path.find_last_of(
'/');
81 if (lastSlashPos == std::string::npos) {
85 return path.substr(lastSlashPos + 1);
94 auto lastSlash = path.find_last_of(
'/');
95 if (lastSlash == std::string::npos)
return ".";
96 return path.substr(0, lastSlash);
99namespace fs = std::filesystem;
108 std::vector<std::string> pvalues;
109 std::stringstream plist(input);
111 while (plist >> tmp) {
113 if (!trimmed.empty()) { pvalues.push_back(trimmed); }
124 const std::string& replacement) {
126 for (
const char& ch : input) {
127 if (toReplace.find(ch) != std::string::npos) { output.append(replacement); }
128 else { output.push_back(ch); }
139 if (from.empty())
return source;
143 size_t findPos = source.find(from, lastPos);
145 while (findPos != string::npos) {
147 newString.append(source, lastPos, findPos - lastPos);
149 lastPos = findPos + from.length();
150 findPos = source.find(from, lastPos);
154 newString += source.substr(lastPos);
165string fillDigits(
const string& word,
const string& c,
int ndigits) {
166 if (c.empty() || ndigits <=
static_cast<int>(word.size()))
return word;
170 int toFill = ndigits -
static_cast<int>(word.size());
171 filled.reserve(ndigits);
173 filled.append(toFill, c[0]);
196static bool parse_double_clocale(std::string_view sv,
double& out) {
199 _locale_t loc = _create_locale(LC_NUMERIC,
"C");
201 out = _strtod_l(tmp.c_str(), &end, loc);
203 return end == tmp.c_str() + tmp.size();
205 locale_t loc = newlocale(LC_NUMERIC_MASK,
"C", (locale_t)0);
207 out = strtod_l(tmp.c_str(), &end, loc);
209 return end == tmp.c_str() + tmp.size();
218 std::cerr <<
FATALERRORL <<
"empty numeric string.\n";
223 if (value.find(
'.') == string::npos) {
224 size_t firstComma = value.find(
',');
225 if (firstComma != string::npos && value.find(
',', firstComma + 1) == string::npos) {
230 const size_t starCount =
static_cast<size_t>(std::count(value.begin(), value.end(),
'*'));
233 if (value.find(
'*') == string::npos) {
236 if (value.find(
'.') == string::npos) {
237 auto firstComma = value.find(
',');
238 if (firstComma != string::npos && value.find(
',', firstComma + 1) == string::npos)
241 if (!parse_double_clocale(value, out)) {
242 std::cerr <<
FATALERRORL <<
"missing '*' before unit or invalid number in <" << v <<
">.\n";
245 if (warnIfNotUnit && out != 0.0) {
246 std::cerr <<
" ! Warning: value " << v <<
" does not contain units." << std::endl;
254 std::cerr <<
FATALERRORL <<
"multiple '*' separators are not allowed in <" << v <<
">.\n";
259 const size_t pos = value.find(
'*');
262 if (left.empty() || right.empty()) {
263 std::cerr <<
FATALERRORL <<
"expected '<number>*<unit>', got <" << v <<
">.\n";
268 if (left.find(
'.') == string::npos) {
269 auto c = left.find(
',');
270 if (c != string::npos && left.find(
',', c + 1) == string::npos)
274 double numeric = 0.0;
275 if (!parse_double_clocale(left, numeric)) {
276 std::cerr <<
FATALERRORL <<
"invalid numeric part before '*' in <" << v <<
">.\n";
288 static const std::unordered_map<string, double> unitConversion = {
290 {
"m", CLHEP::m}, {
"cm", CLHEP::cm}, {
"mm", CLHEP::mm},
291 {
"um", 1E-6 * CLHEP::m}, {
"fm", 1E-15 * CLHEP::m},
292 {
"inch", 2.54 * CLHEP::cm}, {
"inches", 2.54 * CLHEP::cm},
294 {
"deg", CLHEP::deg}, {
"degrees", CLHEP::deg}, {
"arcmin", CLHEP::deg / 60.0},
295 {
"rad", CLHEP::rad}, {
"mrad", CLHEP::mrad},
297 {
"ev", CLHEP::eV}, {
"kev", 1e3 * CLHEP::eV}, {
"mev", CLHEP::MeV}, {
"gev", CLHEP::GeV},
299 {
"t", CLHEP::tesla}, {
"tesla", CLHEP::tesla}, {
"t/m", CLHEP::tesla / CLHEP::m},
300 {
"gauss", CLHEP::gauss}, {
"kilogauss", 1000.0 * CLHEP::gauss},
302 {
"s", CLHEP::s}, {
"ns", CLHEP::ns}, {
"ms", CLHEP::ms}, {
"us", CLHEP::us},
308 if (
auto it = unitConversion.find(unit); it != unitConversion.end()) {
309 return numeric * it->second;
313 auto si_prefix_factor = [](
char p) ->
double {
315 case 'Y':
return 1e24;
316 case 'Z':
return 1e21;
317 case 'E':
return 1e18;
318 case 'P':
return 1e15;
319 case 'T':
return 1e12;
320 case 'G':
return 1e9;
321 case 'M':
return 1e6;
322 case 'k':
return 1e3;
323 case 'h':
return 1e2;
324 case 'd':
return 1e-1;
325 case 'c':
return 1e-2;
326 case 'm':
return 1e-3;
327 case 'u':
return 1e-6;
328 case 'n':
return 1e-9;
329 case 'p':
return 1e-12;
330 case 'f':
return 1e-15;
331 case 'a':
return 1e-18;
332 case 'z':
return 1e-21;
333 case 'y':
return 1e-24;
338 if (unit.size() >= 2) {
339 const double pf = si_prefix_factor(unit.front());
341 const string base = unit.substr(1);
342 if (
auto it2 = unitConversion.find(base); it2 != unitConversion.end()) {
343 return numeric * pf * it2->second;
349 std::cerr <<
GWARNING <<
">" << right <<
"<: unit not recognized for string <" << v <<
">" << std::endl;
355 string gnumber = std::to_string(input) +
"*" + unit;
360 vector<double> output;
361 output.reserve(vstring.size());
363 for (
const auto& s : vstring) { output.push_back(
getG4Number(s, warnIfNotUnit)); }
375 std::ifstream in(filename);
377 std::cerr <<
FATALERRORL <<
"can't open input file " << filename <<
". Check your spelling. " << std::endl;
381 std::stringstream strStream;
383 std::cout << std::endl <<
CIRCLEITEM <<
" Loading string from " << filename << std::endl;
385 strStream << in.rdbuf();
388 string parsedString = strStream.str();
392 while ((nFPos = parsedString.find(commentChars)) != string::npos) {
393 size_t firstNL = parsedString.rfind(
'\n', nFPos);
394 size_t secondNL = parsedString.find(
'\n', nFPos);
395 size_t eraseStart = (firstNL == string::npos) ? 0 : firstNL;
396 size_t eraseLen = (secondNL == string::npos)
398 : secondNL - eraseStart;
399 parsedString.erase(eraseStart, eraseLen);
406 const string& secondDelimiter) {
407 size_t firstpos = input.find(firstDelimiter);
408 size_t secondpos = input.find(secondDelimiter);
410 if (firstpos == string::npos || secondpos == string::npos) {
return ""; }
411 return input.substr(firstpos + firstDelimiter.length(), secondpos - firstpos - firstDelimiter.length());
415 vector<string> pvalues;
418 for (
char ch : input) {
419 if (ch != x[0]) { tmp += ch; }
469 if (stat(path.c_str(), &info) != 0) {
472 return (info.st_mode & S_IFDIR) != 0;
476 for (
const auto& trialLocation : possibleLocations) {
477 string possibleDir = trialLocation +
"/" + dirName;
480 return "UNINITIALIZEDSTRINGQUANTITY";
484bool hasExtension(
const std::string& filename,
const std::vector<std::string>& extensions) {
485 for (
const auto& ext : extensions) {
486 if (filename.size() >= ext.size() &&
487 filename.compare(filename.size() - ext.size(), ext.size(), ext) == 0) {
return true; }
493 vector<string> fileList;
495 DIR* dir = opendir(dirName.c_str());
497 struct dirent* entry;
498 while ((entry = readdir(dir)) !=
nullptr) {
500 string filepath = dirName +
"/" + entry->d_name;
501 if (stat(filepath.c_str(), &info) == 0 && S_ISREG(info.st_mode)) {
502 string filename = entry->d_name;
503 if (
hasExtension(filename, extensions)) { fileList.push_back(filename); }
514 transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
519template <
class KEY,
class VALUE>
520vector<KEY>
getKeys(
const map<KEY, VALUE>& map) {
522 keys.reserve(map.size());
524 for (
const auto& it : map) { keys.push_back(it.first); }
530 static const std::unordered_map<std::string, randomModel> strToEnum = {
537 auto it = strToEnum.find(str);
538 if (it != strToEnum.end()) {
return it->second; }
539 else {
throw std::invalid_argument(
"Invalid string for randomModel: " + str); }
544 if (code.empty())
throw std::invalid_argument(
"empty colour string");
545 if (code.front() ==
'#') code.remove_prefix(1);
546 if (code.size() != 6)
547 throw std::invalid_argument(
"colour must have 6 or 7 hex digits");
549 auto hexNibble = [](
char c) ->
unsigned {
550 if (
'0' <= c && c <=
'9')
return c -
'0';
551 c =
static_cast<char>(std::toupper(
static_cast<unsigned char>(c)));
552 if (
'A' <= c && c <=
'F')
return c -
'A' + 10;
553 throw std::invalid_argument(
"invalid hex digit");
558 for (
int i = 0; i < 6; ++i)
559 rgb = (rgb << 4) | hexNibble(code[i]);
561 auto byteToDouble = [](
unsigned byte) {
return byte / 255.0; };
562 double r = byteToDouble((rgb >> 16) & 0xFF);
563 double g = byteToDouble((rgb >> 8) & 0xFF);
564 double b = byteToDouble(rgb & 0xFF);
566 return {r, g, b, opacity};
570 const std::vector<std::string>& locations,
571 std::string_view filename) {
572 namespace fs = std::filesystem;
574 for (
const auto& loc : locations) {
575 if (loc.empty())
continue;
578 fs::path candidate = (!filename.empty() && fs::is_directory(p))
583 const bool ok = fs::exists(candidate, ec) && fs::is_regular_file(candidate, ec);
584 if (ok)
return candidate.string();
591 if (s.empty())
return true;
593 auto eq = [](std::string_view a, std::string_view b) {
594 if (a.size() != b.size())
return false;
595 for (
size_t i = 0; i < a.size(); ++i)
596 if (std::tolower(
static_cast<unsigned char>(a[i])) != std::tolower(
static_cast<unsigned char>(b[i])))
604 G4UImanager* g4uim = G4UImanager::GetUIpointer();
605 if (g4uim ==
nullptr) {
return; }
606 g4uim->ApplyCommand(command);
Public API for the gutilities namespace.
Common constants and console-formatting macros used across gutilities and related code.
#define GWARNING
Standardized warning label prefix (bold yellow).
#define EC__FILENOTFOUND
Process exit code used when an expected file cannot be opened or found.
#define CIRCLEITEM
Hollow bullet glyph used for list formatting in console logs.
#define EC__G4NUMBERERROR
Process exit code used when parsing a Geant4-style numeric string fails.
#define UNINITIALIZEDSTRINGQUANTITY
Sentinel string representing an uninitialized string quantity.
#define FATALERRORL
Standardized fatal error label prefix (bold red).
string replaceAllStringsWithString(const string &source, const string &from, const string &to)
Replaces all occurrences of a substring with another string.
double getG4Number(const string &v, bool warnIfNotUnit)
Converts a string representation of a number with optional units to a double.
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.
vector< string > getStringVectorFromStringWithDelimiter(const string &input, const string &x)
Splits a string into a vector of substrings using a specified delimiter.
G4Colour makeG4Colour(std::string_view code, double opacity)
Convert a hex colour string to G4Colour.
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.
string fillDigits(const string &word, const string &c, int ndigits)
Pads a string with a specified character until it reaches a desired length.
bool is_unset(std::string_view s)
Determine whether a string should be treated as "unset".
randomModel
Enumeration of random models.
@ gaussian
Gaussian distribution.
@ uniform
Uniform distribution.
@ sphere
Sphere distribution.
@ cosine
Cosine distribution.
string getDirFromPath(const std::string &path)
Extracts the directory path from a given file path.
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.
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.
std::optional< std::string > searchForFileInLocations(const std::vector< std::string > &locations, std::string_view filename)
Search for a regular file across candidate locations.
void apply_uimanager_commands(const std::string &command)
Apply a single Geant4 UI command if a UI manager is available.
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.
bool hasExtension(const std::string &filename, const std::vector< std::string > &extensions)
string getFileFromPath(const std::string &path)
Extracts the filename from a given file path.
vector< std::string > getStringVectorFromString(const std::string &input)
Splits a string into a vector of strings using whitespace as delimiters.