BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSGDMLPreprocessor.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) BDSIM Collaboration, 2001 - 2026.
3
4This file is part of BDSIM.
5
6BDSIM is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published
8by the Free Software Foundation version 3 of the License.
9
10BDSIM is distributed in the hope that it will be useful, but
11WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
17*/
18#ifdef USE_GDML
19#include "BDSDebug.hh"
20#include "BDSException.hh"
21#include "BDSGDMLPreprocessor.hh"
22#include "BDSTemporaryFiles.hh"
23#include "BDSUtilities.hh"
24
25#include <xercesc/dom/DOM.hpp>
26#include <xercesc/framework/LocalFileFormatTarget.hpp>
27#include <xercesc/framework/StdOutFormatTarget.hpp>
28#include <xercesc/parsers/XercesDOMParser.hpp>
29#include <xercesc/sax/HandlerBase.hpp>
30#include <xercesc/util/PlatformUtils.hpp>
31#include <xercesc/util/XMLString.hpp>
32
33#include "globals.hh"
34#include "G4String.hh"
35#include "G4Version.hh"
36
37#include <algorithm>
38#include <fstream>
39#include <istream>
40#include <map>
41#include <ostream>
42#include <sstream>
43#include <string>
44#include <vector>
45#include <regex>
46
47using namespace xercesc;
48
49G4String BDS::PreprocessGDML(const G4String& file,
50 const G4String& prefix,
51 G4bool preprocessSchema)
52{
53 if (BDS::EndsWith(file, ".gmad"))
54 {throw BDSException(__METHOD_NAME__, "trying to read a GMAD file (\"" + file + "\") as a GDML file - check file or change extension.");}
55 BDSGDMLPreprocessor processor;
56 G4String processedFile = processor.PreprocessFile(file,
57 prefix,
58 preprocessSchema);
59 return processedFile;
60}
61
62G4String BDS::PreprocessGDMLSchemaOnly(const G4String& file)
63{
64 // open file
65 if (BDS::EndsWith(file, ".gmad"))
66 {throw BDSException(__METHOD_NAME__, "trying to read a GMAD file (\"" + file + "\") as a GDML file - check file or change extension.");}
67 std::ifstream inputFile;
68 inputFile.open(file.c_str());
69 if (!inputFile.is_open())
70 {throw BDSException(__METHOD_NAME__, "Invalid file \"" + file + "\"");}
71 else
72 {G4cout << __METHOD_NAME__ << "updating GDML Schema to local copy for file:\n \"" << file << "\"" << G4endl;}
73
74 // create new temporary file that modified gdml can be written to.
75 G4String newFile = BDSTemporaryFiles::Instance()->CreateTemporaryFile(file);
76
77 std::ofstream outFile;
78 outFile.open(newFile);
79
80 G4String localSchema = BDS::GDMLSchemaLocation();
81 int i = 0;
82 std::regex gdmlTag("\\<gdml");
83 std::string line;
84 while (std::getline(inputFile, line))
85 {
86 if (i < 10)
87 { // expect schema in first 10 lines of file - efficiency
88 if (std::regex_search(line, gdmlTag))
89 {
90 std::regex schema("xsi:noNamespaceSchemaLocation=\"(\\S+)\"");
91 std::string newLine;
92 std::string prefix = "xsi:noNamespaceSchemaLocation=\"";
93 std::regex_replace(std::back_inserter(newLine), line.begin(), line.end(), schema, prefix+localSchema+"\"$2");
94 outFile << newLine;
95 }
96 else
97 {outFile << line;}
98 }
99 else
100 {outFile << line;}
101 outFile << "\n";
102 i++;
103 }
104 outFile.close();
105 return newFile;
106}
107
109{
110 G4String result;
111 G4String bdsimExecPath = BDS::GetBDSIMExecPath();
112 G4String localPath = bdsimExecPath + "src-external/gdml/schema/gdml.xsd";
113 G4String installPath = bdsimExecPath + "../share/bdsim/gdml/schema/gdml.xsd";
114 if (FILE *file = fopen(localPath.c_str(), "r"))
115 {
116 fclose(file);
117 return localPath;
118 }
119 else if ( (file = fopen(installPath.c_str(), "r")) )
120 {
121 fclose(file);
122 return installPath;
123 }
124 else
125 {
126 G4String bdsimLibraryPath = BDS::GetBDSIMLibraryPath();
127 G4String libraryInstallPath = bdsimLibraryPath + "../share/bdsim/gdml/schema/gdml.xsd";
128 if ( (file = fopen(libraryInstallPath.c_str(), "r")) )
129 {
130 fclose(file);
131 return libraryInstallPath;
132 }
133 else
134 {throw BDSException(__METHOD_NAME__, "ERROR: local GDML schema could not be found!");}
135 }
136}
137
138BDSGDMLPreprocessor::BDSGDMLPreprocessor()
139{
140 //ignoreNodes = {"setup"};
141 ignoreAttrs = {"formula"};
142}
143
144BDSGDMLPreprocessor::~BDSGDMLPreprocessor()
145{;}
146
147G4String BDSGDMLPreprocessor::PreprocessFile(const G4String& file,
148 const G4String& prefix,
149 G4bool preprocessSchema)
150{
151 G4cout << __METHOD_NAME__ << "Preprocessing GDML file " << file << G4endl;
152
153 try
154 {XMLPlatformUtils::Initialize();}
155 catch (const XMLException& toCatch)
156 {
157 char* message = XMLString::transcode(toCatch.getMessage());
158 std::stringstream messageSS;
159 messageSS << "Error during initialization! :\n" << message << "\n";
160 XMLString::release(&message);
161 throw BDSException(__METHOD_NAME__, messageSS.str());
162 }
163
165 G4String filename;
166 BDS::SplitPathAndFileName(file, parentDir, filename);
167
168 XercesDOMParser* parser = new XercesDOMParser();
169 parser->setValidationScheme(XercesDOMParser::Val_Always);
170 parser->setDoNamespaces(true); // optional
171
172 ErrorHandler* errHandler = (ErrorHandler*) new HandlerBase();
173 parser->setErrorHandler(errHandler);
174
175 try
176 {parser->parse(file.c_str());}
177 catch (const XMLException& toCatch)
178 {
179 char* message = XMLString::transcode(toCatch.getMessage());
180 std::stringstream messageSS;
181 messageSS << "Exception message is: \n" << message << "\n";
182 XMLString::release(&message);
183 throw BDSException(__METHOD_NAME__, messageSS.str());
184 }
185 catch (const DOMException& toCatch)
186 {
187 char* message = XMLString::transcode(toCatch.msg);
188 std::stringstream messageSS;
189 messageSS << "Exception message is: \n" << message << "\n";
190 XMLString::release(&message);
191 throw BDSException(__METHOD_NAME__, messageSS.str());
192 }
193 catch (...)
194 {throw BDSException(__METHOD_NAME__, "Unexpected Exception - possibly malformed GDML file: " + filename);}
195
196 // walk through all nodes to extract names and attributes
197 DOMDocument* doc = parser->getDocument();
198 DOMElement* docRootNode = doc->getDocumentElement();
199 DOMNodeIterator* docWalker = doc->createNodeIterator(docRootNode, DOMNodeFilter::SHOW_ELEMENT,nullptr,true);
200 // map structure and all names used
201 ReadDoc(docWalker, preprocessSchema);
202
203 // reset iterator
204 docWalker->detach();
205 docWalker = doc->createNodeIterator(docRootNode, DOMNodeFilter::SHOW_ELEMENT,nullptr,true);
206
207 // rewrite all names in loaded structure.
208 ProcessDoc(docWalker, prefix);
209
210 // create new temporary file that modified gdml can be written to.
211 G4String newFile = BDSTemporaryFiles::Instance()->CreateTemporaryFile(file, prefix);
212
213 // write file from DOM
214 DOMImplementation* pImplement = DOMImplementationRegistry::getDOMImplementation(XMLString::transcode("LS"));
215 DOMLSSerializer* pSerializer = (static_cast<DOMImplementationLS*>(pImplement))->createLSSerializer();
216 DOMConfiguration* pDomConfiguration = pSerializer->getDomConfig();
217 pDomConfiguration->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
218 XMLFormatTarget* pTarget = new LocalFileFormatTarget(newFile);
219 DOMLSOutput* pDomLsOutput = (static_cast<DOMImplementationLS*>(pImplement))->createLSOutput();
220 pDomLsOutput->setByteStream(pTarget);
221 pSerializer->write(doc, pDomLsOutput);
222 pSerializer->release();
223
224 G4cout << __METHOD_NAME__ << "Preprocessing complete" << G4endl;
225
226 delete pTarget;
227 delete parser;
228 delete errHandler;
229
230 return newFile;
231}
232
233void BDSGDMLPreprocessor::ReadDoc(DOMNodeIterator* docIterator,
234 G4bool processSchema)
235{
236 for (DOMNode* currentNode = docIterator->nextNode(); currentNode != 0; currentNode = docIterator->nextNode())
237 {ReadNode(currentNode, processSchema);}
238}
239
240void BDSGDMLPreprocessor::ReadNode(DOMNode* node,
241 G4bool processSchema)
242{
243 if (!node)
244 {return;}
245
246 std::string thisNodeName = XMLString::transcode(node->getNodeName());
247 if (thisNodeName == "gdml" && processSchema)
248 {// to update location of schema
249 ProcessGDMLNode(node->getAttributes());
250 return;
251 }
252 auto search = std::find(ignoreNodes.begin(), ignoreNodes.end(), thisNodeName);
253 if (search != ignoreNodes.end())
254 {return;} // ignore this node
255 else
256 {ReadAttributes(node->getAttributes());}
257}
258
259void BDSGDMLPreprocessor::ProcessGDMLNode(DOMNamedNodeMap* attributeMap)
260{
261 if (!attributeMap)
262 {return;}
263
264 for (XMLSize_t i = 0; i < attributeMap->getLength(); i++)
265 {
266 DOMNode* attr = attributeMap->item(i);
267 std::string nodeName = XMLString::transcode(attr->getNodeName());
268 if (nodeName == "xsi:noNamespaceSchemaLocation")
269 {
270 G4String nodeValue = G4String(XMLString::transcode(attr->getNodeValue()));
271 G4String newNodeValue;
272 if (nodeValue.substr(0,2) == "./")
273 {
274 G4String remainder = nodeValue.substr(2); // strip off ./
275#if G4VERSION_NUMBER > 1099
276 newNodeValue = parentDir + remainder;
277#else
278 newNodeValue = remainder.prepend(parentDir); // prepend parent directory
279#endif
280 }
281 else
282 {newNodeValue = BDS::GDMLSchemaLocation();}
283 attr->setNodeValue(XMLString::transcode(newNodeValue.c_str()));
284 }
285 }
286}
287
288void BDSGDMLPreprocessor::ReadAttributes(DOMNamedNodeMap* attributeMap)
289{
290 if (!attributeMap)
291 {return;}
292
293 for (XMLSize_t i = 0; i < attributeMap->getLength(); i++)
294 {
295 DOMNode* attr = attributeMap->item(i);
296 std::string name = XMLString::transcode(attr->getNodeValue());
297 auto search = std::find(ignoreAttrs.begin(), ignoreAttrs.end(), name);
298 if (search != ignoreAttrs.end())
299 {continue;} // ignore this attribute
300 if (XMLString::compareIString(attr->getNodeName(), XMLString::transcode("name")) == 0)
301 {
302 names.push_back(name);
303 count[name] = 0;
304 }
305 }
306}
307
308void BDSGDMLPreprocessor::ProcessDoc(DOMNodeIterator* docIterator,
309 const G4String& prefix)
310{
311 for (DOMNode* currentNode = docIterator->nextNode(); currentNode != 0; currentNode = docIterator->nextNode())
312 {ProcessNode(currentNode, prefix);}
313}
314
315void BDSGDMLPreprocessor::ProcessNode(DOMNode* node,
316 const G4String& prefix)
317{
318 if (!node)
319 {return;}
320
321 std::string thisNodeName = XMLString::transcode(node->getNodeName());
322 auto search = std::find(ignoreNodes.begin(), ignoreNodes.end(), thisNodeName);
323 if (search != ignoreNodes.end())
324 {return;} // ignore this node
325 else if (thisNodeName == "property")
326 {
327 ProcessAttributes(node->getAttributes(), prefix, true);
328 }
329 else
330 {ProcessAttributes(node->getAttributes(), prefix);}
331}
332
333G4String BDSGDMLPreprocessor::ProcessedNodeName(const G4String& nodeName,
334 const G4String& prefix)
335{return prefix + "_" + nodeName;}
336
337void BDSGDMLPreprocessor::ProcessAttributes(DOMNamedNodeMap* attributeMap,
338 const G4String& prefix,
339 bool property)
340{
341 if (!attributeMap)
342 {return;}
343
344 for (XMLSize_t i = 0; i < attributeMap->getLength(); i++)
345 {
346 DOMNode* attr = attributeMap->item(i);
347 std::string name = XMLString::transcode(attr->getNodeValue());
348
349 auto search = std::find(ignoreAttrs.begin(), ignoreAttrs.end(), name);
350 if (search != ignoreAttrs.end())
351 {continue;} // ignore this attribute
352
353 // the property names should be not appended as Geant4 is expecting RINDEX etc,
354 // not prefix_RINDEX
355 if (XMLString::compareIString(attr->getNodeName(),
356 XMLString::transcode("name")) == 0 && property)
357 {continue;}
358
359 if (XMLString::compareIString(attr->getNodeName(),
360 XMLString::transcode("name")) == 0)
361 {
362 std::string newName = ProcessedNodeName(name, prefix);
363 attr->setNodeValue(XMLString::transcode(newName.c_str()));
364 }
365 else
366 {
367 std::string expression = XMLString::transcode(attr->getNodeValue());
368 // Iterate over all the names that have been defined.
369 for (const auto& defined_name : names)
370 {
371 // Check if whole name is found (don't match substrings).
372 // \\b = word boundary. $& = the matched string.
373 std::regex whole_name(std::string("\\b") + defined_name + "\\b");
374 expression = std::regex_replace(expression, whole_name, prefix + "_$&");
375 }
376 attr->setNodeValue(XMLString::transcode((expression).c_str()));
377 }
378 }
379}
380
381#else
382// insert empty function to avoid no symbols warning
383void _SymbolToPreventWarningGDML(){;}
384#endif
General exception with possible name of object and message.
Process a GDML file to allow multiple file loading.
static G4String ProcessedNodeName(const G4String &nodeName, const G4String &prefix)
std::vector< std::string > ignoreNodes
Nodes to ignore.
G4String PreprocessFile(const G4String &file, const G4String &prefix, G4bool preprocessSchema=true)
std::vector< std::string > names
Names to replace.
std::map< std::string, int > count
Debugging.
std::vector< std::string > ignoreAttrs
Attributes to ignore.
G4String parentDir
Directory of main gdml file.
static BDSTemporaryFiles * Instance()
Singleton accessor.
G4String CreateTemporaryFile(const G4String &originalFilePath, G4String fileNamePrefix="", G4String fileNameSuffix="")
G4bool EndsWith(const std::string &expression, const std::string &suffix)
Return true if a string "expression" ends with "suffix".
G4String GDMLSchemaLocation()
Get GDML Schema location included with BDSIM.
std::string GetBDSIMLibraryPath()
Returns the path from which the BDSIM library is loaded.
void SplitPathAndFileName(const G4String &filePath, G4String &path, G4String &filename)
std::string GetBDSIMExecPath()