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