G4OCCT 0.1.0
Geant4 interface to Open CASCADE Technology (OCCT) geometry definitions
Loading...
Searching...
No Matches
G4OCCTDetectorConstruction.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-2.1-or-later
2// Copyright (C) 2026 G4OCCT Contributors
3
6
8
12#include "G4OCCT/G4OCCTSolid.hh"
13
14#include <BRepBndLib.hxx>
15#include <Bnd_Box.hxx>
16#include <IFSelect_ReturnStatus.hxx>
17#include <STEPCAFControl_Reader.hxx>
18#include <TDF_LabelSequence.hxx>
19#include <TDocStd_Document.hxx>
20#include <XCAFApp_Application.hxx>
21#include <XCAFDoc_DocumentTool.hxx>
22#include <XCAFDoc_ShapeTool.hxx>
23
24#include <G4Box.hh>
25#include <G4Exception.hh>
26#include <G4GenericMessenger.hh>
27#include <G4LogicalVolume.hh>
28#include <G4NistManager.hh>
29#include <G4PVPlacement.hh>
30#include <G4SystemOfUnits.hh>
31#include <G4UImanager.hh>
32
33#include <filesystem>
34#include <memory>
35#include <string>
36
37// ── Helper: detect whether a STEP file is an assembly ────────────────────────
38
39namespace {
40
43bool IsAssemblySTEP(const std::string& path) {
44 Handle(XCAFApp_Application) app = XCAFApp_Application::GetApplication();
45 Handle(TDocStd_Document) doc;
46 app->NewDocument("MDTV-XCAF", doc);
47
48 STEPCAFControl_Reader reader;
49 reader.SetColorMode(false);
50 reader.SetNameMode(true);
51 reader.SetMatMode(true);
52 if (reader.ReadFile(path.c_str()) != IFSelect_RetDone) {
53 return false; // let FromSTEP raise the proper exception later
54 }
55 if (!reader.Transfer(doc)) {
56 return false;
57 }
58
59 Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
60 TDF_LabelSequence freeShapes;
61 shapeTool->GetFreeShapes(freeShapes);
62
63 if (freeShapes.Length() > 1) {
64 return true;
65 }
66 if (freeShapes.Length() == 1 && shapeTool->IsAssembly(freeShapes.Value(1))) {
67 return true;
68 }
69 return false;
70}
71
72} // namespace
73
74// ── G4OCCTDetectorConstruction ────────────────────────────────────────────────
75
77
79
80void G4OCCTDetectorConstruction::DefineMessengers() {
81 fLoadMessenger =
82 std::make_unique<G4GenericMessenger>(this, "/G4OCCT/load/", "Load geometry and materials");
83
84 fLoadMessenger->DeclareMethod("step", &G4OCCTDetectorConstruction::AddSTEP)
85 .SetGuidance("Load a STEP file — auto-detects solid vs assembly.")
86 .SetParameterName("filename", false);
87
88 fLoadMessenger->DeclareMethod("assembly", &G4OCCTDetectorConstruction::AddAssembly)
89 .SetGuidance("Load a STEP file as an XDE assembly.")
90 .SetParameterName("filename", false);
91
92 fLoadMessenger->DeclareMethod("material", &G4OCCTDetectorConstruction::LoadMaterial)
93 .SetGuidance("Load a G4OCCT material-map XML file.")
94 .SetParameterName("filename", false);
95
96 fWorldMessenger =
97 std::make_unique<G4GenericMessenger>(this, "/G4OCCT/world/", "Configure the world volume");
98
99 fWorldMessenger->DeclareProperty("setMaterial", fWorldMaterial)
100 .SetGuidance("World volume material name (default: G4_AIR).")
101 .SetParameterName("name", false);
102
103 fWorldMessenger
104 ->DeclareMethodWithUnit("setSize", "mm", &G4OCCTDetectorConstruction::SetWorldHalfSize)
105 .SetGuidance("World half-size as a 3-vector.")
106 .SetParameterName("halfSize", false);
107
108 fVisMessenger =
109 std::make_unique<G4GenericMessenger>(this, "/G4OCCT/vis/", "Visualisation helpers");
110
111 fVisMessenger->DeclareMethod("open", &G4OCCTDetectorConstruction::OpenViewer)
112 .SetGuidance("Open a visualisation driver (default: OGL).")
113 .SetParameterName("driver", true)
114 .SetDefaultValue("OGL");
115
116 fVisMessenger->DeclareMethod("scene", &G4OCCTDetectorConstruction::RefreshScene)
117 .SetGuidance("Redraw and refresh the current visualisation scene.");
118}
119
120void G4OCCTDetectorConstruction::UI(const G4String& cmd) {
121 G4UImanager::GetUIpointer()->ApplyCommand(cmd);
122}
123
125 fMaterialXmlFiles.push_back(std::string(xmlFile));
126}
127
128void G4OCCTDetectorConstruction::AddSTEP(G4String stepFile) {
129 if (IsAssemblySTEP(std::string(stepFile))) {
130 AddAssembly(std::move(stepFile));
131 } else {
132 AddSolid(std::move(stepFile), "");
133 }
134}
135
136void G4OCCTDetectorConstruction::AddSolid(G4String stepFile, G4String materialName) {
137 fSolidEntries.push_back({std::string(stepFile), std::string(materialName)});
138}
139
141 fAssemblyEntries.push_back({std::string(stepFile)});
142}
143
144void G4OCCTDetectorConstruction::OpenViewer(G4String driver) {
145 if (driver.empty()) {
146 driver = "OGL";
147 }
148 UI("/vis/open " + driver);
149}
150
151void G4OCCTDetectorConstruction::RefreshScene() {
152 UI("/vis/drawVolume");
153 UI("/vis/viewer/refresh");
154}
155
157 // ── Build combined material map from all queued XML files ─────────────────
158 G4OCCTMaterialMap materialMap;
160 for (const auto& xmlFile : fMaterialXmlFiles) {
161 G4OCCTMaterialMap loaded = reader.ReadFile(xmlFile);
162 // Merge into the combined map; later files override earlier entries for
163 // the same STEP material name.
164 materialMap.Merge(loaded);
165 }
166 // If no XML files were loaded, materialMap remains empty; NIST fallback
167 // applies per-solid below.
168
169 // ── Collect bounding box info for world auto-sizing ───────────────────────
170 Bnd_Box totalBounds;
171 bool hasBounds = false;
172
173 // ── Build STEP solids ──────────────────────────────────────────────────────
174 std::vector<G4LogicalVolume*> solidLogicals;
175 for (std::size_t i = 0; i < fSolidEntries.size(); ++i) {
176 const auto& entry = fSolidEntries[i];
177 auto name = std::filesystem::path(entry.file).stem().string();
178 if (fSolidEntries.size() > 1) {
179 name += "_" + std::to_string(i);
180 }
181
182 auto* solid = G4OCCTSolid::FromSTEP(name, entry.file);
183
184 Bnd_Box bbox;
185 BRepBndLib::AddOptimal(solid->GetOCCTShape(), bbox, /*useTriangulation=*/Standard_False);
186 totalBounds.Add(bbox);
187 hasBounds = true;
188
189 // Resolve material
190 G4Material* mat = nullptr;
191 if (!entry.material.empty()) {
192 if (materialMap.Contains(entry.material)) {
193 mat = materialMap.Resolve(entry.material);
194 } else {
195 mat = G4NistManager::Instance()->FindOrBuildMaterial(entry.material);
196 if (!mat) {
197 G4Exception("G4OCCTDetectorConstruction::Construct", "G4OCCT_App_MatNotFound",
198 FatalException,
199 ("Cannot resolve material '" + entry.material + "' for solid '" + name + "'")
200 .c_str());
201 }
202 }
203 } else {
204 mat = G4NistManager::Instance()->FindOrBuildMaterial("G4_AIR");
205 }
206
207 auto* lv = new G4LogicalVolume(solid, mat, name + "_lv");
208 solidLogicals.push_back(lv);
209 }
210
211 // ── Build STEP assemblies ──────────────────────────────────────────────────
212 std::vector<std::unique_ptr<G4OCCTAssemblyVolume>> assemblies;
213 for (const auto& entry : fAssemblyEntries) {
214 auto assembly = std::unique_ptr<G4OCCTAssemblyVolume>(
215 G4OCCTAssemblyVolume::FromSTEP(entry.file, materialMap));
216 for (const auto& [lvName, lv] : assembly->GetLogicalVolumes()) {
217 if (auto* occSolid = dynamic_cast<G4OCCTSolid*>(lv->GetSolid())) {
218 Bnd_Box bbox;
219 BRepBndLib::AddOptimal(occSolid->GetOCCTShape(), bbox, Standard_False);
220 totalBounds.Add(bbox);
221 hasBounds = true;
222 }
223 }
224 assemblies.push_back(std::move(assembly));
225 }
226
227 // ── Compute world half-size ────────────────────────────────────────────────
228 G4ThreeVector halfSize = fWorldHalfSize;
229 if (halfSize.mag2() == 0.0) {
230 if (hasBounds && !totalBounds.IsVoid()) {
231 Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
232 totalBounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
233 const double pad = 1.1;
234 halfSize = G4ThreeVector(pad * std::max(std::abs(xMax), std::abs(xMin)) * mm,
235 pad * std::max(std::abs(yMax), std::abs(yMin)) * mm,
236 pad * std::max(std::abs(zMax), std::abs(zMin)) * mm);
237 // Enforce a minimum 10 cm half-size on each axis
238 for (int k = 0; k < 3; ++k) {
239 if (halfSize[k] < 100.0 * mm)
240 halfSize[k] = 100.0 * mm;
241 }
242 } else {
243 halfSize = G4ThreeVector(500 * mm, 500 * mm, 500 * mm); // 1 m³ default
244 }
245 }
246
247 // ── World volume ───────────────────────────────────────────────────────────
248 auto* worldMat = G4NistManager::Instance()->FindOrBuildMaterial(fWorldMaterial);
249 if (!worldMat) {
250 G4Exception("G4OCCTDetectorConstruction::Construct", "G4OCCT_App_WorldMat", FatalException,
251 ("Cannot find world material: " + std::string(fWorldMaterial)).c_str());
252 }
253
254 auto* worldSolid = new G4Box("world", halfSize.x(), halfSize.y(), halfSize.z());
255 auto* worldLV = new G4LogicalVolume(worldSolid, worldMat, "world_lv");
256 auto* worldPV =
257 new G4PVPlacement(nullptr, G4ThreeVector(), worldLV, "world_pv", nullptr, false, 0);
258
259 // ── Place solids at origin ─────────────────────────────────────────────────
260 for (std::size_t i = 0; i < solidLogicals.size(); ++i) {
261 auto* lv = solidLogicals[i];
262 new G4PVPlacement(nullptr, G4ThreeVector(), lv, lv->GetName() + "_pv", worldLV, false,
263 static_cast<G4int>(i));
264 }
265
266 // ── Imprint assemblies at origin ───────────────────────────────────────────
267 for (auto& assembly : assemblies) {
268 G4Transform3D identity;
269 assembly->MakeImprint(worldLV, identity);
270 }
271
272 return worldPV;
273}
Declaration of G4OCCTAssemblyVolume.
Dynamic detector construction for the g4occt interactive tool.
Declaration of G4OCCTMaterialMapReader.
Declaration of G4OCCTMaterialMap.
Declaration of G4OCCTSolid.
static G4OCCTAssemblyVolume * FromSTEP(const std::string &path, const G4OCCTMaterialMap &materialMap)
void LoadMaterial(G4String xmlFile)
Queue a material-map XML file to be loaded at construction time.
G4VPhysicalVolume * Construct() override
void AddSTEP(G4String stepFile)
Queue a STEP file as a solid or assembly (auto-detected).
void AddSolid(G4String stepFile, G4String materialName="")
Explicitly queue a STEP file as a G4OCCTSolid with an optional material override.
void AddAssembly(G4String stepFile)
Explicitly queue a STEP file as a G4OCCTAssemblyVolume.
void SetWorldHalfSize(G4ThreeVector halfSize)
Set the world half-size. Overrides auto-sizing.
~G4OCCTDetectorConstruction() override
Parses an XML material-map file into a G4OCCTMaterialMap.
G4OCCTMaterialMap ReadFile(const G4String &path)
Maps STEP material names to Geant4 G4Material objects.
bool Contains(const G4String &stepName) const
void Merge(const G4OCCTMaterialMap &other)
G4Material * Resolve(const G4String &stepName) const
Geant4 solid wrapping an Open CASCADE Technology (OCCT) TopoDS_Shape.
static G4OCCTSolid * FromSTEP(const G4String &name, const std::string &path)