gsystem
Loading...
Searching...
No Matches
loadGeometry.cc
Go to the documentation of this file.
1
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 {
26 bool geometry_column_exists(sqlite3* db, const std::string& column_name) {
27 sqlite3_stmt* stmt = nullptr;
28 if (sqlite3_prepare_v2(db, "SELECT name FROM PRAGMA_TABLE_INFO('geometry')", -1, &stmt, nullptr) != SQLITE_OK) {
29 return false;
30 }
31 bool exists = false;
32 while (sqlite3_step(stmt) == SQLITE_ROW) {
33 const unsigned char* colText = sqlite3_column_text(stmt, 0);
34 if (colText != nullptr && column_name == reinterpret_cast<const char*>(colText)) {
35 exists = true;
36 break;
37 }
38 }
39 sqlite3_finalize(stmt);
40 return exists;
41 }
42}
43
44void GSystemCADFactory::loadGeometry(GSystem* s) {
45 // skip ROOT system
46 if (s->getName() == ROOTWORLDGVOLUMENAME) { return; }
47
48 // Resolve the directory holding the CAD meshes.
50 if (!filesystem::exists(dirLocation)) {
51 log->error(ERR_GDIRNOTFOUND, "CAD Directory >" + s->getFilePath() + "< not found.");
52 return;
53 }
54
55 // Map each available mesh file to its stem (filename without extension), which is the volume name.
56 unordered_map<string, string> meshByName;
57 for (const auto& cf : gutilities::getListOfFilesInDirectory(dirLocation, {".stl", ".ply"})) {
58 meshByName[filesystem::path(cf).stem().string()] = cf;
59 }
60
61 // The list of volumes to load - and their metadata - comes from the sqlite database, not from the
62 // directory listing: only meshes that have a matching row in the geometry table are imported.
63 string dbhost = s->get_dbhost();
64 if (dbhost.empty() || dbhost == "na") { dbhost = GSYSTEMSQLITETDEFAULTFILE; }
65
66 vector<string> dirs = {
67 ".",
68 gutilities::gemc_root().string(),
69 (gutilities::gemc_root() / "examples").string()
70 };
71 auto dbPath = gutilities::searchForFileInLocations(dirs, dbhost);
72 if (!dbPath) {
73 log->error(ERR_GSQLITEERROR, "CAD factory: sqlite database <" + dbhost + "> not found.");
74 return;
75 }
76
77 sqlite3* db = nullptr;
78 if (sqlite3_open_v2(dbPath.value().c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
79 sqlite3_close(db);
80 log->error(ERR_GSQLITEERROR, "CAD factory: failed to open sqlite database <" + dbhost + ">.");
81 return;
82 }
83 log->info(1, "CAD factory: reading definitions from sqlite database ", dbPath.value());
84
85 const string placement_column = geometry_column_exists(db, "g4placement_type")
86 ? "g4placement_type"
87 : "'" DEFAULTG4PLACEMENTTYPE "' AS g4placement_type";
88 const string sql_query =
89 "SELECT DISTINCT name, solid, parameters, material, mother, position, rotations, " +
90 placement_column +
91 ", mfield, visible, style, color, opacity, digitization, identifier, copyOf, solidsOpr, mirror, "
92 "exist, description FROM geometry WHERE experiment = ? AND system = ? AND variation = ? AND run = ?";
93
94 sqlite3_stmt* stmt = nullptr;
95 if (sqlite3_prepare_v2(db, sql_query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
96 log->error(ERR_GSQLITEERROR, "CAD factory: error preparing query: ", sqlite3_errmsg(db));
97 sqlite3_close(db);
98 return;
99 }
100
101 string experiment = s->getExperiment();
102 string system_name = s->getName();
103 string variation = s->getVariation();
104 int runno = s->getRunno();
105
106 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
107 sqlite3_bind_text(stmt, 2, system_name.c_str(), -1, SQLITE_TRANSIENT);
108 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
109 sqlite3_bind_int(stmt, 4, runno);
110
111 // The "description" column (last selected field) carries the mesh path for the g4 CAD builder.
112 // The CAD factory resolves it against dirLocation so the database only needs the volume name.
113 constexpr int DESCRIPTION_INDEX = 19;
114
115 int loaded = 0;
116 int rc;
117 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
118 vector<string> gvolumePars;
119 const int colCount = sqlite3_column_count(stmt);
120 for (int i = 0; i < colCount; i++) {
121 const unsigned char* colText = sqlite3_column_text(stmt, i);
122 gvolumePars.emplace_back(colText ? reinterpret_cast<const char*>(colText) : "");
123 }
124
125 const string& volumeName = gvolumePars[0];
126 const string& solidType = gvolumePars[1];
127
128 // Only CAD volumes are resolved against the mesh directory. A CAD volume defined in the
129 // database but missing its mesh file is skipped with a warning.
130 if (solidType == GSYSTEMCADTFACTORYLABEL) {
131 auto it = meshByName.find(volumeName);
132 if (it == meshByName.end()) {
133 log->warning("CAD factory: volume <", volumeName,
134 "> is defined in the database but no mesh file was found in <",
135 dirLocation, ">; skipping.");
136 continue;
137 }
138 gvolumePars[DESCRIPTION_INDEX] = dirLocation + "/" + it->second;
139 }
140
141 s->addGVolume(gvolumePars);
142 loaded++;
143 }
144
145 if (rc != SQLITE_DONE) {
146 log->error(ERR_GSQLITEERROR, "CAD factory: sqlite error while reading geometry: ", sqlite3_errmsg(db));
147 }
148
149 sqlite3_finalize(stmt);
150 sqlite3_close(db);
151
152 log->info(0, "CAD factory: loaded ", loaded, " volume(s) for system <", system_name,
153 ">, variation <", variation, ">, run ", runno,
154 " (", meshByName.size(), " mesh file(s) present in <", dirLocation, ">).");
155}
std::shared_ptr< GLogger > log
void warning(Args &&... args) const
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
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:33
std::string getFilePath() const
Gets the full file path of the system.
Definition gsystem.cc:236
int getRunno() const
Definition gsystem.h:127
std::string getExperiment() const
Definition gsystem.h:125
std::string getVariation() const
Definition gsystem.h:124
void addGVolume(std::vector< std::string > pars)
Build and add a volume from a serialized parameter list.
Definition gsystem.cc:90
std::string getName() const
Definition gsystem.h:122
std::string get_dbhost() const
Definition gsystem.h:128
Conventions and shared constants for the detector-system module.
#define GSYSTEMCADTFACTORYLABEL
#define DEFAULTG4PLACEMENTTYPE
#define GSYSTEMSQLITETDEFAULTFILE
Default sqlite DB filename used when the user does not specify one.
#define ROOTWORLDGVOLUMENAME
Canonical name for the ROOT/world gvolume entry.
#define ERR_GDIRNOTFOUND
#define ERR_GSQLITEERROR
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)
string searchForDirInLocations(const string &dirName, const vector< string > &possibleLocations)