Mantid
Loading...
Searching...
No Matches
MaterialBuilder.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 +
8#include "MantidKernel/Atom.h"
11
12#include <memory>
13#include <numeric>
14
15namespace Mantid {
19namespace Kernel {
20
21namespace {
22inline bool isEmpty(const boost::optional<double> &value) { return !value || value == Mantid::EMPTY_DBL(); }
23constexpr auto LARGE_LAMBDA = 100; // Lambda likely to be beyond max lambda in
24 // any measured spectra. In Angstroms
25} // namespace
26
31 : m_name(), m_formula(), m_atomicNo(), m_massNo(0), m_numberDensity(), m_packingFraction(), m_zParam(), m_cellVol(),
32 m_massDensity(), m_totalXSection(), m_cohXSection(), m_incXSection(), m_absSection(),
33 m_numberDensityUnit(NumberDensityUnit::Atoms) {}
34
40MaterialBuilder &MaterialBuilder::setName(const std::string &name) {
41 if (name.empty()) {
42 throw std::invalid_argument("MaterialBuilder::setName() - Empty name not allowed.");
43 }
44 m_name = name;
45 return *this;
46}
47
53MaterialBuilder &MaterialBuilder::setFormula(const std::string &formula) {
54 if (m_name.empty()) {
55 m_name = formula;
56 }
57
58 if (m_atomicNo) {
59 throw std::runtime_error("MaterialBuilder::setFormula() - Atomic no. "
60 "already set, cannot use formula aswell.");
61 }
62 if (formula.empty()) {
63 throw std::invalid_argument("MaterialBuilder::setFormula() - Empty formula provided.");
64 }
65 using ChemicalFormula = Material::ChemicalFormula;
66 try {
67 m_formula = ChemicalFormula(ChemicalFormula(Material::parseChemicalFormula(formula)));
68 } catch (std::runtime_error &exc) {
69 throw std::invalid_argument("MaterialBuilder::setFormula() - Unable to parse chemical formula: " +
70 std::string(exc.what()));
71 }
72 return *this;
73}
74
81 if (!m_formula.empty()) {
82 throw std::runtime_error("MaterialBuilder::setAtomicNumber() - Formula "
83 "already set, cannot use atomic number aswell.");
84 }
85 m_atomicNo = atomicNumber;
86 return *this;
87}
88
95 m_massNo = massNumber;
96 return *this;
97}
98
105 if (rho != Mantid::EMPTY_DBL())
107 return *this;
108}
109
116 m_numberDensityUnit = unit;
117 return *this;
118}
119
128 if (rho_eff != Mantid::EMPTY_DBL())
129 m_numberDensityEff = rho_eff;
130 return *this;
131}
132
138 if (fraction != Mantid::EMPTY_DBL())
139 m_packingFraction = fraction;
140 return *this;
141}
142
149 m_zParam = zparam;
150 return *this;
151}
152
159 m_cellVol = cellVolume;
160 return *this;
161}
162
169 m_massDensity = massDensity;
170 return *this;
171}
172
179 if (xsec != Mantid::EMPTY_DBL())
180 m_totalXSection = xsec;
181 return *this;
182}
183
190 m_cohXSection = xsec;
191 return *this;
192}
193
200 m_incXSection = xsec;
201 return *this;
202}
203
210 m_absSection = xsec;
211 return *this;
212}
213
220 if (!filename.empty()) {
222 }
223 return *this;
224}
225
232 if (!filename.empty()) {
234 }
235 return *this;
236}
237
243
250
251 if (!m_formula.empty()) {
253 } else if (m_atomicNo) {
256 throw std::runtime_error("Please specify one of chemical formula or atomic "
257 "number or all cross sections and a number "
258 "density.");
259 }
260
261 const auto density_struct = getOrCalculateRhoAndPacking(formula);
262
263 std::unique_ptr<Material> material;
266 material =
267 std::make_unique<Material>(m_name, neutron, density_struct.number_density, density_struct.packing_fraction);
268 } else {
269 material =
270 std::make_unique<Material>(m_name, formula, density_struct.number_density, density_struct.packing_fraction);
271 }
274 material.get(), LARGE_LAMBDA);
275 material->setAttenuationProfile(materialAttenuation);
276 }
278 // don't supply a material so that extrapolation using the neutron tabulated
279 // attenuation data is turned off
281 -1);
282 material->setXRayAttenuationProfile(materialAttenuation);
283 }
284 return *material;
285}
286
292 Material::FormulaUnit unit{std::make_shared<PhysicalConstants::Atom>(
293 getAtom(static_cast<uint16_t>(m_atomicNo.get()), static_cast<uint16_t>(m_massNo))),
294 1.};
296 formula.emplace_back(unit);
297
298 return formula;
299}
300
308 // set packing fraction and both number densities to zero to start
309 density_packing result{0., 0., 0.};
310
311 // get the packing fraction
313 result.packing_fraction = m_packingFraction.get();
314
315 // if effective density has been specified
317 result.effective_number_density = m_numberDensityEff.get();
318
319 // total number of atoms is used in both density calculations
320 const double totalNumAtoms =
321 std::accumulate(formula.cbegin(), formula.cend(), 0.,
322 [](double n, const Material::FormulaUnit &f) { return n + f.multiplicity; });
323
324 // calculate the number density by one of many ways
325 if (m_numberDensity) {
326 result.number_density = m_numberDensity.get();
327 if (m_numberDensityUnit == NumberDensityUnit::FormulaUnits && totalNumAtoms > 0.) {
328 result.number_density = m_numberDensity.get() * totalNumAtoms;
329 }
330 } else if (m_zParam && m_cellVol) {
331 result.number_density = totalNumAtoms * m_zParam.get() / m_cellVol.get();
332 } else if (!formula.empty() && formula.size() == 1) {
333 result.number_density = formula.front().atom->number_density;
334 }
335
336 // calculate the effective number density
337 if (m_massDensity) {
338 // g / cc -> atoms / Angstrom^3
339 const double rmm =
340 std::accumulate(formula.cbegin(), formula.cend(), 0.,
341 [](double sum, const Material::FormulaUnit &f) { return sum + f.atom->mass * f.multiplicity; });
342 result.effective_number_density = (m_massDensity.get() * totalNumAtoms / rmm) * PhysicalConstants::N_A * 1e-24;
343 }
344
345 // count the number of values that were set and generate errors
346 int count = 0;
347 if (result.packing_fraction > 0.)
348 count++;
349 if (result.effective_number_density > 0.)
350 count++;
351 if (result.number_density > 0.)
352 count++;
353
354 // use this information to set the "missing" of the 3
355 if (count == 0) {
356 throw std::runtime_error("The number density could not be determined. Please "
357 "provide the number density, ZParameter and unit "
358 "cell volume or mass density.");
359 } else if (count == 1) {
360 result.packing_fraction = 1.;
361 if (result.number_density > 0.)
362 result.effective_number_density = result.number_density;
363 else if (result.effective_number_density > 0.)
364 result.number_density = result.effective_number_density;
365 else
366 throw std::runtime_error("Must specify the number density in some way");
367 } else if (count == 2) {
368 if (result.number_density > 0.) {
369 if (result.effective_number_density > 0.)
370 result.packing_fraction = result.effective_number_density / result.number_density;
371 else if (result.packing_fraction > 0.)
372 result.effective_number_density = result.packing_fraction * result.number_density;
373 } else if (result.effective_number_density > 0.) {
374 if (result.number_density > 0.)
375 result.packing_fraction = result.effective_number_density / result.number_density;
376 else if (result.packing_fraction > 0.)
377 result.number_density = result.effective_number_density / result.packing_fraction;
378 }
379 // do something
380 } else if (count == 3) {
381 throw std::runtime_error("The number density and effective density were over-determined");
382 }
383
384 return result;
385}
386
388 return !isEmpty(m_totalXSection) || !isEmpty(m_cohXSection) || !isEmpty(m_incXSection) || !isEmpty(m_absSection);
389}
390
392 NeutronAtom neutronAtom(0, 0., 0., 0., 0., 0., 0.);
393
394 // generate the default neutron
395 if (m_atomicNo) {
396 auto atom = getAtom(static_cast<uint16_t>(m_atomicNo.get()), static_cast<uint16_t>(m_massNo));
397 neutronAtom = atom.neutron;
398 overrideNeutronProperties(neutronAtom);
399 } else if (!m_formula.empty()) {
400 double totalNumAtoms = 0.;
401 for (const auto &formulaUnit : m_formula) {
402 neutronAtom = neutronAtom + formulaUnit.multiplicity * formulaUnit.atom->neutron;
403 totalNumAtoms += formulaUnit.multiplicity;
404 }
405 neutronAtom = (1. / totalNumAtoms) * neutronAtom;
406 overrideNeutronProperties(neutronAtom);
407 } else {
408 neutronAtom.coh_scatt_xs = *m_cohXSection;
409 neutronAtom.inc_scatt_xs = *m_incXSection;
410 neutronAtom.tot_scatt_xs = *m_totalXSection;
411 neutronAtom.abs_scatt_xs = *m_absSection;
412 calculateScatteringLengths(neutronAtom);
413 }
414 neutronAtom.a_number = 0; // signifies custom neutron atom
415 neutronAtom.z_number = 0; // signifies custom neutron atom
416
417 return neutronAtom;
418}
419
425 if (!isEmpty(m_totalXSection))
426 neutron.tot_scatt_xs = m_totalXSection.get();
427 if (!isEmpty(m_cohXSection))
428 neutron.coh_scatt_xs = m_cohXSection.get();
429 if (!isEmpty(m_incXSection))
430 neutron.inc_scatt_xs = m_incXSection.get();
431 if (!isEmpty(m_absSection))
432 neutron.abs_scatt_xs = m_absSection.get();
433}
434
435} // namespace Kernel
436} // namespace Mantid
double value
The value of the point.
Definition: FitMW.cpp:51
int count
counter
Definition: Matrix.cpp:37
Create a material from a set of user defined options.
boost::optional< int > m_atomicNo
Material::ChemicalFormula m_formula
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 & setEffectiveNumberDensity(double rho_eff)
Set the effective number density of the sample in atoms or formula units / Angstrom^3.
MaterialBuilder & setName(const std::string &name)
Set the string name given to the material.
void overrideNeutronProperties(PhysicalConstants::NeutronAtom &neutron) const
Override default neutron properties with those supplied.
boost::optional< double > m_incXSection
boost::optional< double > m_cellVol
boost::optional< double > m_totalXSection
MaterialBuilder & setMassNumber(int massNumber)
Set the isotope by mass number.
MaterialBuilder & setFormula(const std::string &formula)
Set the chemical formula of the material.
boost::optional< double > m_zParam
boost::optional< double > m_packingFraction
boost::optional< double > m_massDensity
MaterialBuilder & setZParameter(double zparam)
Set the number of formula units in the unit cell.
MaterialBuilder & setPackingFraction(double fraction)
Set the packing fraction of the material (default is 1).
MaterialBuilder & setTotalScatterXSection(double xsec)
Set a value for the total scattering cross section.
boost::optional< double > m_absSection
MaterialBuilder & setUnitCellVolume(double cellVolume)
Set the volume of unit cell.
Material::ChemicalFormula createCompositionFromAtomicNumber() const
Create the NeutronAtom object from the atomic number.
MaterialBuilder & setAbsorptionXSection(double xsec)
Set a value for the absorption cross section.
density_packing getOrCalculateRhoAndPacking(const Material::ChemicalFormula &formula) const
Return the manually set density or calculate it from other parameters.
void setAttenuationSearchPath(std::string path)
Set a value for the attenuation profile search path.
PhysicalConstants::NeutronAtom generateCustomNeutron() const
boost::optional< double > m_cohXSection
NumberDensityUnit m_numberDensityUnit
MaterialBuilder & setNumberDensityUnit(NumberDensityUnit unit)
Set the unit for number density.
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.
boost::optional< std::string > m_attenuationProfileFileName
boost::optional< std::string > m_xRayAttenuationProfileFileName
boost::optional< double > m_numberDensity
boost::optional< double > m_numberDensityEff
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.
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
Definition: Material.h:50
std::vector< FormulaUnit > ChemicalFormula
Definition: Material.h:60
static ChemicalFormula parseChemicalFormula(const std::string &chemicalSymbol)
Definition: Material.cpp:625
Struture to hold the common information for an atom.
Definition: Atom.h:20
MANTID_KERNEL_DLL int isEmpty(const std::string &A)
Determines if a string is only spaces.
Definition: Strings.cpp:359
MANTID_KERNEL_DLL const Atom & getAtom(const uint16_t z_number, const uint16_t a_number=0)
Definition: Atom.cpp:3167
static constexpr double N_A
Avagodro constant in mol-1.
Helper class which provides the Collimation Length for SANS instruments.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
Structure to hold the information for a parsed chemical formula.
Definition: Material.h:53
Structure to store neutronic scattering information for the various elements.
Definition: NeutronAtom.h:22
uint16_t z_number
The atomic number, or number of protons, for the atom.
Definition: NeutronAtom.h:44
double inc_scatt_xs
The incoherent scattering cross section in barns.
Definition: NeutronAtom.h:66
double tot_scatt_xs
The total scattering cross section in barns.
Definition: NeutronAtom.h:69
double abs_scatt_xs
The absorption cross section for 2200m/s neutrons in barns.
Definition: NeutronAtom.h:72
double coh_scatt_xs
The coherent scattering cross section in barns.
Definition: NeutronAtom.h:63
uint16_t a_number
The total number of protons and neutrons, or mass number, for the atom for isotopic averages this is ...
Definition: NeutronAtom.h:48