G4OCCT 0.1.0
Geant4 interface to Open CASCADE Technology (OCCT) geometry definitions
Loading...
Searching...
No Matches
G4OCCTMaterialMapReader.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
9// Xerces — provided transitively by Geant4::G4gdml.
10// G4GDMLRead.hh already includes the Xerces DOM headers; we repeat them here
11// for clarity and to document the direct dependency.
12#include <xercesc/dom/DOM.hpp>
13#include <xercesc/parsers/XercesDOMParser.hpp>
14#include <xercesc/util/PlatformUtils.hpp>
15
16// Geant4
17#include <G4Exception.hh>
18#include <G4Material.hh>
19#include <G4NistManager.hh>
20
22 // Xerces initialization is ref-counted; safe to call multiple times.
23 xercesc::XMLPlatformUtils::Initialize();
24
25 G4OCCTMaterialMap result;
26
27 // Pre-parse fatal errors (MatReader001–005) are saved here and reported
28 // after the parser is destroyed and Terminate() has been called. Calling
29 // Terminate() inside the parser's {} scope and then returning through the
30 // scope exit would invoke XercesDOMParser::~XercesDOMParser() after
31 // Terminate() — a SEGFAULT if a non-aborting G4VExceptionHandler allows
32 // execution to continue past G4Exception().
33 G4String preFatalCode;
34 G4String preFatalMsg;
35
36 // IIFE: contains the XercesDOMParser so it is destroyed before Terminate().
37 // Early 'return' from the lambda exits the lambda scope cleanly, ensuring
38 // the parser destructor runs before Terminate() is called below.
39 [&]() {
40 xercesc::XercesDOMParser parser;
41 parser.setDoNamespaces(false);
42 parser.setDoSchema(false);
43 parser.setCreateEntityReferenceNodes(false);
44
45 try {
46 parser.parse(path.c_str());
47 } catch (const xercesc::XMLException& e) {
48 preFatalCode = "G4OCCT_MatReader001";
49 preFatalMsg = "XML error parsing '" + path + "': " + Transcode(e.getMessage());
50 return;
51 } catch (const xercesc::DOMException& e) {
52 preFatalCode = "G4OCCT_MatReader002";
53 preFatalMsg = "DOM error parsing '" + path + "': " + Transcode(e.getMessage());
54 return;
55 }
56
57 const xercesc::DOMDocument* const doc = parser.getDocument();
58 if (!doc) {
59 preFatalCode = "G4OCCT_MatReader003";
60 preFatalMsg = "Cannot open document: " + path;
61 return;
62 }
63
64 const xercesc::DOMElement* const root = doc->getDocumentElement();
65 if (!root) {
66 preFatalCode = "G4OCCT_MatReader004";
67 preFatalMsg = "Empty document: " + path;
68 return;
69 }
70
71 const G4String rootTag = Transcode(root->getTagName());
72 if (rootTag != "materials") {
73 preFatalCode = "G4OCCT_MatReader005";
74 preFatalMsg = "Root element must be <materials>, got <" + rootTag + "> in '" + path + "'";
75 return;
76 }
77
78 // ── Pass 1: isotopes and elements ──────────────────────────────────────
79 // Must precede materials so that <fraction ref="Si"/> can resolve "Si".
80 for (xercesc::DOMNode* node = root->getFirstChild(); node != nullptr;
81 node = node->getNextSibling()) {
82 if (node->getNodeType() != xercesc::DOMNode::ELEMENT_NODE) {
83 continue;
84 }
85 const auto* child = dynamic_cast<const xercesc::DOMElement*>(node);
86 if (!child) {
87 continue;
88 }
89 const G4String tag = Transcode(child->getTagName());
90 if (tag == "isotope") {
91 IsotopeRead(child);
92 } else if (tag == "element") {
93 ElementRead(child);
94 }
95 }
96
97 // ── Pass 2: materials ──────────────────────────────────────────────────
98 for (xercesc::DOMNode* node = root->getFirstChild(); node != nullptr;
99 node = node->getNextSibling()) {
100 if (node->getNodeType() != xercesc::DOMNode::ELEMENT_NODE) {
101 continue;
102 }
103 const auto* child = dynamic_cast<const xercesc::DOMElement*>(node);
104 if (!child) {
105 continue;
106 }
107 if (Transcode(child->getTagName()) != "material") {
108 continue;
109 }
110
111 // Extract our custom attributes and the GDML 'name' attribute.
112 G4String stepName;
113 G4String geant4Name;
114 G4String gdmlName;
115 const xercesc::DOMNamedNodeMap* attrs = child->getAttributes();
116 for (XMLSize_t i = 0; i < attrs->getLength(); ++i) {
117 const auto* attr = dynamic_cast<const xercesc::DOMAttr*>(attrs->item(i));
118 if (!attr) {
119 continue;
120 }
121 const G4String aName = Transcode(attr->getName());
122 const G4String aValue = Transcode(attr->getValue());
123 if (aName == "stepName") {
124 stepName = aValue;
125 } else if (aName == "geant4Name") {
126 geant4Name = aValue;
127 } else if (aName == "name") {
128 gdmlName = aValue;
129 }
130 }
131
132 if (stepName.empty()) {
133 G4Exception("G4OCCTMaterialMapReader::ReadFile", "G4OCCT_MatReader006", FatalException,
134 ("<material> element in '" + path +
135 "' is missing the required 'stepName' "
136 "attribute.")
137 .c_str());
138 continue;
139 }
140
141 G4Material* mat = nullptr;
142
143 if (!geant4Name.empty()) {
144 // ── Type 1: NIST alias ────────────────────────────────────────────
145 mat = G4NistManager::Instance()->FindOrBuildMaterial(geant4Name);
146 if (!mat) {
147 G4Exception("G4OCCTMaterialMapReader::ReadFile", "G4OCCT_MatReader007", FatalException,
148 ("NIST material '" + geant4Name + "' not found (stepName='" + stepName +
149 "'). "
150 "Check the spelling against the Geant4 NIST material database.")
151 .c_str());
152 continue;
153 }
154 } else {
155 // ── Type 2: Inline GDML material definition ───────────────────────
156 if (gdmlName.empty()) {
157 G4Exception("G4OCCTMaterialMapReader::ReadFile", "G4OCCT_MatReader008", FatalException,
158 ("Inline <material stepName=\"" + stepName + "\"> in '" + path +
159 "' requires a 'name' attribute (used as the GDML material registry key).")
160 .c_str());
161 continue;
162 }
163 // Reuse the material if it was already registered in the global
164 // G4 material table (e.g. by a preceding G4GDMLParser::Read call
165 // on the reference geometry). Creating a duplicate G4Material
166 // with the same name is a fatal error in Geant4.
167 //
168 // G4GDMLParser::Read() appends "0x<ptr>" suffixes (GenerateName)
169 // and then strips them back to plain names via StripNames(). Check
170 // both the generated name (pre-strip) and the plain name (post-strip)
171 // so that inline materials already registered under either convention
172 // are reused without re-creation.
173 mat = G4Material::GetMaterial(GenerateName(gdmlName), /*warning=*/false);
174 if (mat == nullptr) {
175 mat = G4Material::GetMaterial(gdmlName, /*warning=*/false);
176 }
177 if (mat == nullptr) {
178 // Material not yet registered — delegate full GDML parsing to
179 // the inherited method, which creates the G4Material.
180 MaterialRead(child);
181 mat = GetMaterial(GenerateName(gdmlName));
182 if (mat == nullptr) {
183 mat = G4Material::GetMaterial(gdmlName, /*warning=*/false);
184 }
185 }
186 if (!mat) {
187 G4Exception("G4OCCTMaterialMapReader::ReadFile", "G4OCCT_MatReader009", FatalException,
188 ("Failed to create inline material '" + gdmlName + "' (stepName='" +
189 stepName + "') in '" + path +
190 "'. Check that density and composition are valid.")
191 .c_str());
192 continue;
193 }
194 }
195
196 result.Add(stepName, mat);
197 }
198 }(); // XercesDOMParser destroyed here
199
200 xercesc::XMLPlatformUtils::Terminate();
201
202 if (!preFatalCode.empty()) {
203 G4Exception("G4OCCTMaterialMapReader::ReadFile", preFatalCode.c_str(), FatalException,
204 preFatalMsg.c_str());
205 // Normally unreachable: FatalException aborts. If a non-aborting handler
206 // is installed (e.g. G4OCCTFatalCatcher in tests), return an empty map so
207 // callers get a predictable, safe value.
208 return result;
209 }
210
211 return result;
212}
Declaration of G4OCCTMaterialMapReader.
G4OCCTMaterialMap ReadFile(const G4String &path)
Maps STEP material names to Geant4 G4Material objects.
void Add(const G4String &stepName, G4Material *material)