G4OCCT 0.1.0
Geant4 interface to Open CASCADE Technology (OCCT) geometry definitions
Loading...
Searching...
No Matches
main.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-2.1-or-later
2// Copyright (C) 2026 G4OCCT Contributors
3
15
18
19#include <FTFP_BERT.hh>
20#include <G4RunManagerFactory.hh>
21#include <G4UIExecutive.hh>
22#include <G4UIcommand.hh>
23#include <G4UImanager.hh>
24#include <G4VisExecutive.hh>
25
26#include <algorithm>
27#include <cctype>
28#include <climits>
29#include <cstdlib>
30#include <filesystem>
31#include <string>
32#include <vector>
33
34#ifdef __APPLE__
35#include <mach-o/dyld.h>
36#endif
37
38namespace {
39
43std::filesystem::path ExeDir() {
44#if defined(__linux__)
45 std::error_code ec;
46 auto p = std::filesystem::read_symlink("/proc/self/exe", ec);
47 if (!ec)
48 return p.parent_path();
49#elif defined(__APPLE__)
50 char buf[PATH_MAX]; // PATH_MAX from <climits>
51 uint32_t size = sizeof(buf);
52 if (_NSGetExecutablePath(buf, &size) == 0) {
53 std::error_code ec;
54 auto p = std::filesystem::canonical(buf, ec);
55 if (!ec)
56 return p.parent_path();
57 }
58#endif
59 return {};
60}
61
73G4String BuildMacroSearchPath() {
74 std::vector<std::string> dirs;
75
76 // 1. Environment variable override.
77 if (const char* env = std::getenv("G4OCCT_MACRO_PATH"))
78 if (*env != '\0')
79 dirs.emplace_back(env);
80
81 // 2. Runtime path: exe/../${CMAKE_INSTALL_DATADIR}/g4occt/macros.
82 auto exeDir = ExeDir();
83 if (!exeDir.empty()) {
84 auto runtimeDir = exeDir.parent_path() / G4OCCT_INSTALL_DATADIR / "g4occt" / "macros";
85 dirs.push_back(runtimeDir.string());
86 }
87
88 // 3. Build-tree compile-time constant (developer / CTest fallback).
89 dirs.emplace_back(G4OCCT_MACRO_DIR_BUILD);
90
91 G4String path;
92 for (const auto& d : dirs) {
93 if (d.empty())
94 continue;
95 std::error_code ec;
96 if (!std::filesystem::exists(d, ec) || ec)
97 continue;
98 if (!path.empty())
99 path += ":";
100 path += d;
101 }
102 return path;
103}
104
105void PrintUsage(const char* prog) {
106 G4cerr << "Usage: " << prog
107 << " [-m macro] [-u UIsession] [-t nThreads]"
108 " [file.step|file.stp ...] [file.xml ...]"
109 << G4endl;
110 G4cerr << " -m macro Execute Geant4 macro file (batch mode)." << G4endl;
111 G4cerr << " -u UIsession Select UI session (Qt, Xm, tcsh, …)." << G4endl;
112 G4cerr << " -t nThreads Number of worker threads (multi-threaded builds only)." << G4endl;
113 G4cerr << " file.step Load STEP file (solid or assembly, auto-detected)." << G4endl;
114 G4cerr << " file.xml Load G4OCCT material-map XML file." << G4endl;
115}
116
118std::string FileExtension(const std::string& path) {
119 auto dot = path.rfind('.');
120 if (dot == std::string::npos)
121 return {};
122 std::string ext = path.substr(dot);
123 std::transform(ext.begin(), ext.end(), ext.begin(),
124 [](unsigned char c) { return std::tolower(c); });
125 return ext;
126}
127
128} // namespace
129
130int main(int argc, char** argv) {
131 G4String macro;
132 G4String session;
133#ifdef G4MULTITHREADED
134 G4int nThreads = 0;
135#endif
136 std::vector<std::string> stepFiles;
137 std::vector<std::string> materialFiles;
138
139 // ── Parse arguments ────────────────────────────────────────────────────────
140 for (G4int i = 1; i < argc; ++i) {
141 const std::string arg(argv[i]);
142
143 if (arg == "-m") {
144 if (++i >= argc) {
145 PrintUsage(argv[0]);
146 return 1;
147 }
148 macro = argv[i];
149 } else if (arg == "-u") {
150 if (++i >= argc) {
151 PrintUsage(argv[0]);
152 return 1;
153 }
154 session = argv[i];
155 } else if (arg == "-t") {
156 // Require an explicit value for -t in all builds, and ensure we don't
157 // accidentally treat the next option token (e.g. "-m") as that value.
158 if (i + 1 >= argc) {
159 PrintUsage(argv[0]);
160 return 1;
161 }
162 const std::string nextArg(argv[i + 1]);
163 if (nextArg[0] == '-') {
164 PrintUsage(argv[0]);
165 return 1;
166 }
167 ++i; // move to the value for -t
168#ifdef G4MULTITHREADED
169 nThreads = G4UIcommand::ConvertToInt(argv[i]);
170#else
171 G4cerr << "Warning: -t option (" << argv[i]
172 << ") ignored (Geant4 built without multi-threading)." << G4endl;
173#endif
174 } else if (arg[0] == '-') {
175 G4cerr << "Unknown option: " << arg << G4endl;
176 PrintUsage(argv[0]);
177 return 1;
178 } else {
179 // Positional argument — classify by extension
180 const auto ext = FileExtension(arg);
181 if (ext == ".step" || ext == ".stp") {
182 stepFiles.push_back(arg);
183 } else if (ext == ".xml") {
184 materialFiles.push_back(arg);
185 } else {
186 G4cerr << "Unrecognised file type (expected .step, .stp, or .xml): " << arg << G4endl;
187 PrintUsage(argv[0]);
188 return 1;
189 }
190 }
191 }
192
193 // ── Interactive mode: create UI executive before run manager ──────────────
194 G4UIExecutive* ui = nullptr;
195 if (macro.empty()) {
196 ui = new G4UIExecutive(argc, argv, session);
197 }
198
199 // ── Run manager ───────────────────────────────────────────────────────────
200 auto* runManager = G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default);
201#ifdef G4MULTITHREADED
202 if (nThreads > 0) {
203 runManager->SetNumberOfThreads(nThreads);
204 }
205#endif
206
207 // ── Detector construction ─────────────────────────────────────────────────
208 auto* detConstruction = new G4OCCTDetectorConstruction;
209
210 // Pre-load material maps so they are available when Construct() is called.
211 for (const auto& f : materialFiles) {
212 detConstruction->LoadMaterial(f);
213 }
214 // Queue STEP files for loading during Construct().
215 for (const auto& f : stepFiles) {
216 detConstruction->AddSTEP(f);
217 }
218
219 runManager->SetUserInitialization(detConstruction);
220 runManager->SetUserInitialization(new FTFP_BERT(/*verbosity=*/0));
221 runManager->SetUserInitialization(new G4OCCTActionInitialization);
222
223 // ── Visualisation ─────────────────────────────────────────────────────────
224 // Only initialize vis in interactive mode; batch runs don't require it and
225 // may run in environments without display/vis support.
226 G4VisExecutive* visManager = nullptr;
227 if (ui != nullptr) {
228 visManager = new G4VisExecutive("Quiet");
229 visManager->Initialize();
230 }
231
232 // ── Macro search path ─────────────────────────────────────────────────────
233 // Three-tier fallback so the binary works after spack relocation:
234 // 1. G4OCCT_MACRO_PATH env var — user/admin override
235 // 2. <exe>/../share/g4occt/macros — runtime-relative (survives relocation)
236 // 3. G4OCCT_MACRO_DIR_BUILD — in-tree build / CTest
237 auto* UImanager = G4UImanager::GetUIpointer();
238 UImanager->SetMacroSearchPath(BuildMacroSearchPath());
239
240 // ── Execute macro or start interactive session ────────────────────────────
241 int exitCode = 0;
242
243 if (!macro.empty()) {
244 exitCode = UImanager->ApplyCommand("/control/execute " + macro);
245 } else {
246 UImanager->ApplyCommand("/control/execute init_vis.mac");
247 if (ui->IsGUI()) {
248 UImanager->ApplyCommand("/control/execute gui.mac");
249 }
250 ui->SessionStart();
251 delete ui;
252 }
253
254 delete visManager;
255 delete runManager;
256 return exitCode != 0 ? 1 : 0;
257}
Minimal user action initialisation for the g4occt executable.
Dynamic detector construction for the g4occt interactive tool.
Action initialisation for the g4occt interactive tool.
Dynamically constructs a Geant4 world from STEP solids and assemblies.
void LoadMaterial(G4String xmlFile)
Queue a material-map XML file to be loaded at construction time.
int main(int argc, char **argv)
Definition main.cc:130