gsystem
Loading...
Searching...
No Matches
loadGeometry.cc
Go to the documentation of this file.
1
7
8// gsystem
9#include "systemCadFactory.h"
10#include "gsystemConventions.h"
11
12// gemc
13#include "gutilities.h"
14
15// sqlite
16#include "sqlite3.h"
17
18// c++
19#include <filesystem>
20#include <unordered_map>
21
22using namespace std;
23
24namespace {
25 bool numeric_value(const std::string& value) {
26 try {
27 size_t parsed = 0;
28 // Only the parsed length matters here (is the whole string numeric?); discard the value.
29 static_cast<void>(std::stod(value, &parsed));
30 return parsed == value.size();
31 }
32 catch (const std::exception&) { return false; }
33 }
34
35 void set_resolved_cad_mesh(std::vector<std::string>& row, const std::string& resolved) {
36 constexpr int PARAMETERS_INDEX = 2;
37 constexpr int DESCRIPTION_INDEX = 19;
38 const auto values =
40 if (values.empty() ||
41 (values.size() == 1 && (values[0] == "NULL" || numeric_value(values[0])))) {
42 row[DESCRIPTION_INDEX] = resolved;
43 return;
44 }
45
46 const auto delimiter = row[PARAMETERS_INDEX].find(',');
47 const auto suffix = delimiter == std::string::npos ? "" : row[PARAMETERS_INDEX].substr(delimiter);
48 row[PARAMETERS_INDEX] = resolved + suffix;
49 }
50
52 bool geometry_column_exists(sqlite3* db, const std::string& column_name) {
53 sqlite3_stmt* stmt = nullptr;
54 if (sqlite3_prepare_v2(db, "SELECT name FROM PRAGMA_TABLE_INFO('geometry')", -1, &stmt, nullptr) != SQLITE_OK) {
55 return false;
56 }
57 bool exists = false;
58 while (sqlite3_step(stmt) == SQLITE_ROW) {
59 const unsigned char* colText = sqlite3_column_text(stmt, 0);
60 if (colText != nullptr && column_name == reinterpret_cast<const char*>(colText)) {
61 exists = true;
62 break;
63 }
64 }
65 sqlite3_finalize(stmt);
66 return exists;
67 }
68}
69
70void GSystemCADFactory::loadGeometry(GSystem* s) {
71 // skip ROOT system
72 if (s->getName() == gsystem::ROOTWORLDGVOLUMENAME) { return; }
73
74 // Resolve the directory holding the CAD meshes.
76 if (!dirLocation) {
77 log->error(gsystem::ERR_GDIRNOTFOUND, "CAD Directory >%1< not found.", s->getFilePath());
78 }
79
80 // Map each available mesh file to its stem (filename without extension), which is the volume name.
81 unordered_map<string, string> meshByName;
82 for (const auto& cf : gutilities::getListOfFilesInDirectory(dirLocation->string(), {".stl", ".ply"})) {
83 meshByName[filesystem::path(cf).stem().string()] = cf;
84 }
85
86 // The list of volumes to load - and their metadata - comes from the sqlite database, not from the
87 // directory listing: only meshes that have a matching row in the geometry table are imported.
88 string dbhost = s->get_dbhost();
89 if (dbhost.empty() || dbhost == "na") { dbhost = gsystem::GSYSTEMSQLITETDEFAULTFILE; }
90
91 vector<string> dirs = {
92 ".",
93 gutilities::gemc_root().string(),
94 (gutilities::gemc_root() / "examples").string()
95 };
96 auto dbPath = gutilities::searchForFileInLocations(dirs, dbhost);
97 if (!dbPath) {
98 log->error(gsystem::ERR_GSQLITEERROR, "CAD factory: sqlite database <%1> not found.", dbhost);
99 return;
100 }
101
102 sqlite3* db = nullptr;
103 if (sqlite3_open_v2(dbPath.value().c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
104 sqlite3_close(db);
105 log->error(gsystem::ERR_GSQLITEERROR, "CAD factory: failed to open sqlite database <%1>.", dbhost);
106 return;
107 }
108 log->info(1, "CAD factory: reading definitions from sqlite database %1", dbPath.value());
109
110 const string placement_column = geometry_column_exists(db, "g4placement_type")
111 ? "g4placement_type"
112 : "'" + string(gsystem::DEFAULTG4PLACEMENTTYPE) +
113 "' AS g4placement_type";
114 const string sql_query =
115 "SELECT DISTINCT name, solid, parameters, material, mother, position, rotations, " +
116 placement_column +
117 ", mfield, visible, style, color, opacity, digitization, identifier, copyOf, solidsOpr, mirror, "
118 "exist, description FROM geometry WHERE experiment = ? AND system = ? AND variation = ? AND run = ?";
119
120 sqlite3_stmt* stmt = nullptr;
121 if (sqlite3_prepare_v2(db, sql_query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
122 log->error(gsystem::ERR_GSQLITEERROR, "CAD factory: error preparing query: %1", sqlite3_errmsg(db));
123 sqlite3_close(db);
124 return;
125 }
126
127 string experiment = s->getExperiment();
128 string system_name = s->getName();
129 string variation = s->getVariation();
130 int runno = s->getRunno();
131
132 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
133 sqlite3_bind_text(stmt, 2, system_name.c_str(), -1, SQLITE_TRANSIENT);
134 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
135 sqlite3_bind_int(stmt, 4, runno);
136
137 int loaded = 0;
138 int rc;
139 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
140 vector<string> gvolumePars;
141 const int colCount = sqlite3_column_count(stmt);
142 for (int i = 0; i < colCount; i++) {
143 const unsigned char* colText = sqlite3_column_text(stmt, i);
144 gvolumePars.emplace_back(colText ? reinterpret_cast<const char*>(colText) : "");
145 }
146
147 const string& volumeName = gvolumePars[0];
148 const string& solidType = gvolumePars[1];
149
150 // Only CAD volumes are resolved against the mesh directory. A CAD volume defined in the
151 // database but missing its mesh file is skipped with a warning.
152 if (solidType == gsystem::GSYSTEMCADTFACTORYLABEL) {
153 auto it = meshByName.find(volumeName);
154 if (it == meshByName.end()) {
155 log->warning(
156 "CAD factory: volume <%1> is defined in the database but no mesh file was found in <%2>; "
157 "skipping.", volumeName, dirLocation->string());
158 continue;
159 }
160 set_resolved_cad_mesh(gvolumePars, (*dirLocation / it->second).string());
161 }
162
163 s->addGVolume(gvolumePars);
164 loaded++;
165 }
166
167 if (rc != SQLITE_DONE) {
168 log->error(
169 gsystem::ERR_GSQLITEERROR, "CAD factory: sqlite error while reading geometry: %1",
170 sqlite3_errmsg(db));
171 }
172
173 sqlite3_finalize(stmt);
174 sqlite3_close(db);
175
176 log->info(
177 0,
178 "CAD factory: loaded %1 volume(s) for system <%2>, variation <%3>, run %4 (%5 mesh file(s) present "
179 "in <%6>).", loaded, system_name, variation, runno, meshByName.size(), dirLocation->string());
180}
std::shared_ptr< GLogger > log
std::vector< std::string > possibleLocationOfFiles
List of candidate directories used by file-based factories.
Represents a single detector system (e.g., calorimeter, tracker).
Definition gsystem.h:34
std::string getFilePath() const
Gets the full file path of the system.
Definition gsystem.cc:237
int getRunno() const
Definition gsystem.h:128
std::string getExperiment() const
Definition gsystem.h:126
std::string getVariation() const
Definition gsystem.h:125
void addGVolume(std::vector< std::string > pars)
Build and add a volume from a serialized parameter list.
Definition gsystem.cc:89
std::string getName() const
Definition gsystem.h:123
std::string get_dbhost() const
Definition gsystem.h:129
Conventions and shared constants for the detector-system module.
constexpr char GSYSTEMSQLITETDEFAULTFILE[]
Default sqlite DB filename used when the user does not specify one.
constexpr char DEFAULTG4PLACEMENTTYPE[]
constexpr char GSYSTEMCADTFACTORYLABEL[]
constexpr int ERR_GSQLITEERROR
constexpr char ROOTWORLDGVOLUMENAME[]
Canonical name for the ROOT/world gvolume entry.
constexpr int ERR_GDIRNOTFOUND
vector< string > getStringVectorFromStringWithDelimiter(const string &input, const string &x)
std::filesystem::path gemc_root()
vector< string > getListOfFilesInDirectory(const string &dirName, const vector< string > &extensions)
std::optional< std::string > searchForFileInLocations(const std::vector< std::string > &locations, std::string_view filename)
std::optional< std::filesystem::path > searchForDirInLocations(const string &dirName, const vector< string > &possibleLocations)