Newer
Older
#include "MantidKernel/MaterialBuilder.h"
#include "MantidKernel/Atom.h"
#include "MantidKernel/EmptyValues.h"
#include "MantidKernel/NeutronAtom.h"
#include "MantidKernel/make_unique.h"
namespace Mantid {
using PhysicalConstants::Atom;
using PhysicalConstants::NeutronAtom;
using PhysicalConstants::getAtom;
using PhysicalConstants::getNeutronAtom;
namespace Kernel {
/**
* Constructor
*/
MaterialBuilder::MaterialBuilder()
: m_name(), m_formula(), m_atomicNo(), m_massNo(0), m_numberDensity(),
m_zParam(), m_cellVol(), m_massDensity(), m_totalXSection(),
m_cohXSection(), m_incXSection(), m_absSection() {}
/**
* Set the string name given to the material
* @param name Human-readable name of the material. Empty string not allowed
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setName(const std::string &name) {
if (name.empty()) {
throw std::invalid_argument(
"MaterialBuilder::setName() - Empty name not allowed.");
}
m_name = name;
return *this;
}
/**
* Set the checmical formula of the material
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setFormula(const std::string &formula) {
if (m_atomicNo) {
throw std::runtime_error("MaterialBuilder::setFormula() - Atomic no. "
"already set, cannot use formula aswell.");
}
if (formula.empty()) {
throw std::invalid_argument(
"MaterialBuilder::setFormula() - Empty formula provided.");
}
typedef Material::ChemicalFormula ChemicalFormula;
try {
m_formula = Mantid::Kernel::make_unique<ChemicalFormula>(
ChemicalFormula(Material::parseChemicalFormula(formula)));
} catch (std::runtime_error & exc) {
"MaterialBuilder::setFormula() - Unable to parse chemical formula: " +
std::string(exc.what()));
}
return *this;
}
/**
* Set the type of atom by its atomic number
* @param atomicNumber Z-number of the atom
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setAtomicNumber(int atomicNumber) {
if (m_formula) {
throw std::runtime_error("MaterialBuilder::setAtomicNumber() - Formula "
"already set, cannot use atomic number aswell.");
}
m_atomicNo = atomicNumber;
return *this;
}
/**
* Set the isotope by mass number
* @param massNumber Isotope number of the atom
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setMassNumber(int massNumber) {
m_massNo = massNumber;
return *this;
}
/**
* Set the number density of the sample
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setNumberDensity(double rho) {
m_numberDensity = rho;
return *this;
}
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* Set the number of atoms in the unit cell
* @param zparam Number of atoms
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setZParameter(double zparam) {
if (m_massDensity) {
throw std::runtime_error("MaterialBuilder::setZParameter() - Mass density "
"already set, cannot use Z parameter as well.");
}
m_zParam = zparam;
return *this;
}
/**
* Set the volume of unit cell
* @param cellVolume The volume of the unit cell
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setUnitCellVolume(double cellVolume) {
if (m_massDensity) {
throw std::runtime_error(
"MaterialBuilder::setUnitCellVolume() - Mass density "
"already set, cannot use unit cell volume as well.");
}
m_cellVol = cellVolume;
return *this;
}
/**
* Set the mass density of the sample and calculate the density from this
* @param massDensity The mass density value
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setMassDensity(double massDensity) {
if (m_zParam) {
throw std::runtime_error("MaterialBuilder::setMassDensity() - Z parameter "
"already set, cannot use mass density as well.");
}
if (m_cellVol) {
throw std::runtime_error(
"MaterialBuilder::setMassDensity() - Unit cell "
"volume already set, cannot use mass density as well.");
}
m_massDensity = massDensity;
return *this;
}
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
* Set a value for the total scattering cross section
* @param xsec Value of the cross section
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setTotalScatterXSection(double xsec) {
m_totalXSection = xsec;
return *this;
}
/**
* Set a value for the coherent scattering cross section
* @param xsec Value of the cross section
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setCoherentXSection(double xsec) {
m_cohXSection = xsec;
return *this;
}
/**
* Set a value for the incoherent scattering cross section
* @param xsec Value of the cross section
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setIncoherentXSection(double xsec) {
m_incXSection = xsec;
return *this;
}
/**
* Set a value for the absorption cross section
* @param xsec Value of the cross section
* @return A reference to the this object to allow chaining
*/
MaterialBuilder &MaterialBuilder::setAbsorptionXSection(double xsec) {
m_absSection = xsec;
return *this;
}
/**
* Build the new Material object from the current set of options
* @return A new Material object
*/
Material MaterialBuilder::build() const {
NeutronAtom neutron;
double density(0.0);
std::tie(neutron, density) = createCompositionFromFormula();
} else if (m_atomicNo) {
std::tie(neutron, density) = createCompositionFromAtomicNumber();
} else {
throw std::runtime_error("MaterialBuilder::createNeutronAtom() - Please "
"specify one of chemical formula or atomic "
"number.");
}
overrideNeutronProperties(neutron);
return Material(m_name, neutron, density);
}
/**
* Create the NeutronAtom object from a chemical formula
* @return A new NeutronAtom object with the defined proprties
*/
MaterialBuilder::Composition
MaterialBuilder::createCompositionFromFormula() const {
// The zeroes here are important. By default the entries are all nan
NeutronAtom composition(0, 0., 0., 0., 0., 0., 0.);
const size_t numAtomTypes(m_formula->atoms.size());
double totalNumAtoms(0.0), rmm(0.0);
if (numAtomTypes == 1) {
composition = m_formula->atoms[0]->neutron;
totalNumAtoms = 1.0;
rmm = m_formula->atoms[0]->mass;
} else {
for (size_t i = 0; i < numAtomTypes; i++) {
const auto &atom(m_formula->atoms[i]);
const double natoms(m_formula->numberAtoms[i]);
composition = composition + natoms * atom->neutron;
totalNumAtoms += natoms;
rmm += atom->mass * natoms;
}
// normalize the accumulated number by the number of atoms
composition = (1. / totalNumAtoms) * composition;
}
const double density = getOrCalculateRho(totalNumAtoms, rmm);
return Composition{composition, density};
}
/**
* Create the NeutronAtom object from the atomic number
* @return A new NeutronAtom object with the defined proprties
*/
MaterialBuilder::Composition
MaterialBuilder::createCompositionFromAtomicNumber() const {
auto atom = getAtom(static_cast<uint16_t>(m_atomicNo.get()),
static_cast<uint16_t>(m_massNo));
return Composition{atom.neutron, getOrCalculateRho(1.0, atom.mass)};
}
/**
* Return the manually set density or calculate it from other parameters
* @param totalNumAtoms Total number of atoms
* @param rmm The relative molecular mass
* @return The number density
*/
double MaterialBuilder::getOrCalculateRho(double totalNumAtoms,
double rmm) const {
if (m_numberDensity) {
return m_numberDensity.get();
} else if (m_zParam && m_cellVol) {
return totalNumAtoms * m_zParam.get() / m_cellVol.get();
} else if (m_massDensity) {
return (m_massDensity.get() / rmm) * PhysicalConstants::N_A * 1e-24;
} else if (m_formula && m_formula->atoms.size() == 1) {
return m_formula->atoms[0]->number_density;
} else {
return EMPTY_DBL();
}
}
/**
* Override default neutron properties with those supplied
* @param neutron A reference to a NeutronAtom object
*/
void MaterialBuilder::overrideNeutronProperties(
PhysicalConstants::NeutronAtom &neutron) const {
if (m_totalXSection)
neutron.tot_scatt_xs = m_totalXSection.get();
if (m_cohXSection)
neutron.coh_scatt_xs = m_cohXSection.get();
if (m_incXSection)
neutron.inc_scatt_xs = m_incXSection.get();
if (m_absSection)
neutron.abs_scatt_xs = m_absSection.get();
}
} // namespace Kernel
} // namespace Mantid