Mantid
Loading...
Searching...
No Matches
MaterialXMLParser.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9
10#include "Poco/DOM/AutoPtr.h"
11#include "Poco/DOM/DOMParser.h"
12#include "Poco/DOM/Document.h"
13#include "Poco/DOM/NamedNodeMap.h"
14#include "Poco/DOM/NodeFilter.h"
15#include "Poco/DOM/NodeIterator.h"
16#include "Poco/SAX/InputSource.h"
17#include "Poco/SAX/SAXException.h"
18
19#include <boost/lexical_cast.hpp>
20
21#include <sstream>
22#include <type_traits>
23#include <unordered_map>
24
25namespace Mantid::Kernel {
26
27// -----------------------------------------------------------------------------
28// Anonymous methods
29// -----------------------------------------------------------------------------
30namespace {
31
32/*
33 * Define the list of known attributes along with a map to the relevant
34 * MaterialBuilder method to call. This avoids a long list of if/else
35 * statements when parsing the xml.
36 *
37 * The mapping uses a template type, TypedBuilderHandle, to store the method
38 * on the MaterialBuilder that should be called and the template type indicates
39 * the argument type of the method call. The struct automatically extracts
40 * the expected value type from the given string based on the template type.
41 */
42
43// Known attributes
44const char *ID_ATT = "id";
45const char *FORMULA_ATT = "formula";
46const char *ATOMNUM_ATT = "atomicnumber";
47const char *MASSNUM_ATT = "massnumber";
48const char *NDENSITY_ATT = "numberdensity";
49const char *ZPARAM_ATT = "zparameter";
50const char *CELLVOL_ATT = "unitcellvol";
51const char *MASSDENS_ATT = "massdensity";
52const char *TOTSC_ATT = "totalscatterxsec";
53const char *COHSC_ATT = "cohscatterxsec";
54const char *INCOHSC_ATT = "incohscatterxsec";
55const char *ABSORB_ATT = "absorptionxsec";
56const char *ATTENPROF_ATT = "attenuationprofile";
57const char *ATTENPROFX_ATT = "xrayattenuationprofile";
58
59// Base type to put in a hash
60struct BuilderHandle {
61 virtual ~BuilderHandle() = default;
62 virtual void operator()(MaterialBuilder &builder, const std::string &value) const = 0;
63};
64using BuilderHandle_uptr = std::unique_ptr<BuilderHandle>;
65
66// Pointer to member function on MaterialBuilder
67template <typename ArgType> using BuilderMethod = MaterialBuilder &(MaterialBuilder::*)(ArgType);
68
69// Template type where ArgType indicates the expected argument type including
70// const & ref qualifiers
71template <typename ArgType> struct TypedBuilderHandle final : public BuilderHandle {
72 // Remove const/reference qualifiers from ArgType
73 using ValueType = typename std::remove_const<typename std::remove_reference<ArgType>::type>::type;
74
75 explicit TypedBuilderHandle(BuilderMethod<ArgType> m) : BuilderHandle(), m_method(m) {}
76
77 void operator()(MaterialBuilder &builder, const std::string &value) const override {
78 auto typedVal = boost::lexical_cast<ValueType>(value);
79 (builder.*m_method)(typedVal);
80 }
81
82private:
83 BuilderMethod<ArgType> m_method;
84};
85
86using Handlers = std::unordered_map<std::string, BuilderHandle_uptr>;
87
88// Insert a handle into the given map
89template <typename ArgType> void insertHandle(Handlers *hash, const std::string &name, BuilderMethod<ArgType> m) {
90 hash->insert(std::make_pair(name, BuilderHandle_uptr(new TypedBuilderHandle<ArgType>(m))));
91}
92
93// Find the appropriate handler for a given attribute
94const BuilderHandle &findHandle(const std::string &name) {
95 static Handlers handles;
96 if (handles.empty()) {
97 insertHandle(&handles, ID_ATT, &MaterialBuilder::setName);
98 insertHandle(&handles, FORMULA_ATT, &MaterialBuilder::setFormula);
99 insertHandle(&handles, ATOMNUM_ATT, &MaterialBuilder::setAtomicNumber);
100 insertHandle(&handles, MASSNUM_ATT, &MaterialBuilder::setMassNumber);
101 insertHandle(&handles, NDENSITY_ATT, &MaterialBuilder::setNumberDensity);
102 insertHandle(&handles, ZPARAM_ATT, &MaterialBuilder::setZParameter);
103 insertHandle(&handles, CELLVOL_ATT, &MaterialBuilder::setUnitCellVolume);
104 insertHandle(&handles, MASSDENS_ATT, &MaterialBuilder::setMassDensity);
105 insertHandle(&handles, TOTSC_ATT, &MaterialBuilder::setTotalScatterXSection);
106 insertHandle(&handles, COHSC_ATT, &MaterialBuilder::setCoherentXSection);
107 insertHandle(&handles, INCOHSC_ATT, &MaterialBuilder::setIncoherentXSection);
108 insertHandle(&handles, ABSORB_ATT, &MaterialBuilder::setAbsorptionXSection);
109 insertHandle(&handles, ATTENPROF_ATT, &MaterialBuilder::setAttenuationProfileFilename);
110 insertHandle(&handles, ATTENPROFX_ATT, &MaterialBuilder::setXRayAttenuationProfileFilename);
111 }
112 auto iter = handles.find(name);
113 if (iter != handles.end())
114 return *(iter->second);
115 else
116 throw std::runtime_error("Unknown material attribute '" + name + "'");
117}
118
126void addToBuilder(MaterialBuilder *builder, const std::string &attr, const std::string &value) {
127 // Find the appropriate member function on the builder and set the value
128 // We need 3 maps for the 3 allowable value types for the builder member
129 // functions
130 const auto &setter = findHandle(attr);
131 setter(*builder, value);
132}
133} // namespace
134
135// -----------------------------------------------------------------------------
136// Public methods
137// -----------------------------------------------------------------------------
138
147Material MaterialXMLParser::parse(std::istream &istr) const {
148 using namespace Poco::XML;
149 using DocumentPtr = AutoPtr<Document>;
150
151 InputSource src(istr);
152 DOMParser parser;
153 // Do not use auto here or anywhereas the Poco API returns raw pointers
154 // but in some circumstances requires AutoPtrs to manage the memory
155 DocumentPtr doc;
156 try {
157 doc = parser.parse(&src);
158 } catch (SAXParseException &exc) {
159 std::ostringstream os;
160 os << "MaterialXMLReader::read() - Error parsing stream as XML: " << exc.what();
161 throw std::invalid_argument(os.str());
162 }
163
164 Element *rootElement = doc->documentElement();
165
166 // Iterating is apparently much faster than getElementsByTagName
167 NodeIterator nodeIter(rootElement, NodeFilter::SHOW_ELEMENT);
168 Node *node = nodeIter.nextNode();
169 Material matr;
170 bool found(false);
171 while (node) {
172 if (node->nodeName() == MATERIAL_TAG) {
173 matr = parse(static_cast<Element *>(node));
174 found = true;
175 break;
176 }
177 node = nodeIter.nextNode();
178 }
179 if (!found) {
180 throw std::invalid_argument("MaterialXMLReader::read() - No material tags found.");
181 }
182 return matr;
183}
184
194Material MaterialXMLParser::parse(Poco::XML::Element *element, const std::string &XMLFilePath) const {
195 using namespace Poco::XML;
196 using NamedNodeMapPtr = AutoPtr<NamedNodeMap>;
197 NamedNodeMapPtr attrs = element->attributes();
198 const auto id = attrs->getNamedItem(ID_ATT);
199 if (!id || id->nodeValue().empty()) {
200 throw std::invalid_argument("MaterialXMLReader::read() - No 'id' tag found "
201 "or emptry string provided.");
202 }
203 attrs->removeNamedItem(ID_ATT);
204
205 MaterialBuilder builder;
206 builder.setName(id->nodeValue());
207 builder.setAttenuationSearchPath(XMLFilePath);
208 const auto nattrs = attrs->length();
209 for (unsigned long i = 0; i < nattrs; ++i) {
210 Node *node = attrs->item(i);
211 addToBuilder(&builder, node->nodeName(), node->nodeValue());
212 }
213 return builder.build();
214}
215
216} // namespace Mantid::Kernel
double value
The value of the point.
Definition: FitMW.cpp:51
BuilderMethod< ArgType > m_method
Create a material from a set of user defined options.
MaterialBuilder & setMassDensity(double massDensity)
Set the mass density of the sample in g / cc.
MaterialBuilder & setAttenuationProfileFilename(std::string filename)
Set a value for the attenuation profile filename.
Material build() const
Build the new Material object from the current set of options.
MaterialBuilder & setName(const std::string &name)
Set the string name given to the material.
MaterialBuilder & setMassNumber(int massNumber)
Set the isotope by mass number.
MaterialBuilder & setFormula(const std::string &formula)
Set the chemical formula of the material.
MaterialBuilder & setZParameter(double zparam)
Set the number of formula units in the unit cell.
MaterialBuilder & setTotalScatterXSection(double xsec)
Set a value for the total scattering cross section.
MaterialBuilder & setUnitCellVolume(double cellVolume)
Set the volume of unit cell.
MaterialBuilder & setAbsorptionXSection(double xsec)
Set a value for the absorption cross section.
void setAttenuationSearchPath(std::string path)
Set a value for the attenuation profile search path.
MaterialBuilder & setCoherentXSection(double xsec)
Set a value for the coherent scattering cross section.
MaterialBuilder & setIncoherentXSection(double xsec)
Set a value for the incoherent scattering cross section.
MaterialBuilder & setNumberDensity(double rho)
Set the number density of the sample in atoms or formula units / Angstrom^3.
MaterialBuilder & setAtomicNumber(int atomicNumber)
Set the type of atom by its atomic number.
MaterialBuilder & setXRayAttenuationProfileFilename(std::string filename)
Set a value for the attenuation profile filename.
Material parse(std::istream &istr) const
Takes a stream that is assumed to contain a single complete material definition, reads the definition...
static constexpr const char * MATERIAL_TAG
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
Definition: Material.h:50