11#include "MantidNexus/NexusFile.h"
13#include <boost/lexical_cast.hpp>
20using str_pair = std::pair<std::string, std::string>;
27constexpr double INV_FOUR_PI = 1. / (4. * M_PI);
29inline double scatteringLength(
const double real,
const double imag) {
32 length = std::abs(real);
33 }
else if (real == 0.) {
34 length = std::abs(imag);
36 length = std::hypot(real, imag);
39 if (!std::isnormal(length)) {
46inline double scatteringXS(
const double realLength,
const double imagLength) {
47 double lengthSqrd = (realLength * realLength) + (imagLength * imagLength);
49 if (!std::isnormal(lengthSqrd)) {
52 return .04 * M_PI * lengthSqrd;
58 const double multiplicity)
59 : atom(
std::move(atom)), multiplicity(multiplicity) {}
81 const double packingFraction,
const double temperature,
const double pressure)
82 : m_name(
std::move(
name)), m_atomTotal(0.0), m_numberDensity(numberDensity), m_packingFraction(packingFraction),
83 m_temperature(temperature), m_pressure(pressure) {
100 const double packingFraction,
const double temperature,
const double pressure)
101 : m_name(
std::move(
name)), m_chemicalFormula(), m_atomTotal(1.0), m_numberDensity(numberDensity),
102 m_packingFraction(packingFraction), m_temperature(temperature), m_pressure(pressure) {
130 double weightedTotal;
137 return subtotal +
right.atom->neutron.abs_scatt_xs *
right.multiplicity;
142 if (!std::isnormal(weightedTotal)) {
152 double weightedTotal;
158 return subtotal +
right.atom->neutron.tot_scatt_xs *
right.multiplicity;
163 if (!std::isnormal(weightedTotal)) {
295 throw std::runtime_error(
"xRayAttenuationProfile override not set");
316 std::vector<double>::const_iterator lambdaEnd)
const {
320 std::vector<double> linearCoef(std::distance(lambdaBegin, lambdaEnd));
322 std::transform(lambdaBegin, lambdaEnd, linearCoef.begin(),
323 [densityTerm,
this](
const double lambda) { return densityTerm * this->absorbXSection(lambda); });
355 const double weightedTotal =
358 return subtotal +
right.atom->neutron.coh_scatt_length_real *
right.multiplicity;
362 if (!std::isnormal(weightedTotal)) {
365 return weightedTotal;
376 const double weightedTotal =
379 return subtotal +
right.atom->neutron.coh_scatt_length_img *
right.multiplicity;
383 if (!std::isnormal(weightedTotal)) {
386 return weightedTotal;
397 const double weightedTotal =
400 return subtotal +
right.atom->neutron.inc_scatt_length_real *
right.multiplicity;
404 if (!std::isnormal(weightedTotal)) {
407 return weightedTotal;
418 const double weightedTotal =
421 return subtotal +
right.atom->neutron.inc_scatt_length_img *
right.multiplicity;
425 if (!std::isnormal(weightedTotal)) {
428 return weightedTotal;
440 return 10. * std::sqrt(crossSection) * INV_FOUR_PI;
452 lengthSqrd = real * real;
453 }
else if (real == 0.) {
454 lengthSqrd = imag * imag;
456 lengthSqrd = real * real + imag * imag;
459 if (!std::isnormal(lengthSqrd)) {
473 return 100. * crossSection * INV_FOUR_PI;
483 return 100. * crossSection * INV_FOUR_PI;
491 file->makeGroup(
group,
"NXdata",
true);
492 file->putAttr(
"version", 2);
493 file->putAttr(
"name",
m_name);
496 std::string style =
"formula";
501 style =
"userdefined";
504 file->putAttr(
"formulaStyle", style);
507 if (style ==
"formula") {
508 std::stringstream formula;
510 if (formulaUnit.atom->a_number != 0) {
513 formula << formulaUnit.atom->symbol;
514 if (formulaUnit.atom->a_number != 0) {
515 formula << formulaUnit.atom->a_number <<
")";
517 formula << formulaUnit.multiplicity <<
" ";
519 file->writeData(
"chemical_formula", formula.str());
520 }
else if (style ==
"userdefined") {
521 file->writeData(
"coh_scatt_length_real",
m_chemicalFormula[0].atom->neutron.coh_scatt_length_real);
522 file->writeData(
"coh_scatt_length_img",
m_chemicalFormula[0].atom->neutron.coh_scatt_length_img);
523 file->writeData(
"inc_scatt_length_real",
m_chemicalFormula[0].atom->neutron.inc_scatt_length_real);
524 file->writeData(
"inc_scatt_length_img",
m_chemicalFormula[0].atom->neutron.inc_scatt_length_img);
525 file->writeData(
"coh_scatt_xs",
m_chemicalFormula[0].atom->neutron.coh_scatt_xs);
526 file->writeData(
"inc_scatt_xs",
m_chemicalFormula[0].atom->neutron.inc_scatt_xs);
527 file->writeData(
"tot_scatt_xs",
m_chemicalFormula[0].atom->neutron.tot_scatt_xs);
528 file->writeData(
"abs_scatt_xs",
m_chemicalFormula[0].atom->neutron.abs_scatt_xs);
529 file->writeData(
"tot_scatt_length",
m_chemicalFormula[0].atom->neutron.tot_scatt_length);
530 file->writeData(
"coh_scatt_length",
m_chemicalFormula[0].atom->neutron.coh_scatt_length);
531 file->writeData(
"inc_scatt_length",
m_chemicalFormula[0].atom->neutron.inc_scatt_length);
546 file->openGroup(
group,
"NXdata");
547 file->getAttr(
"name",
m_name);
549 file->getAttr(
"version", version);
553 uint16_t element_Z, element_A;
554 file->readData(
"element_Z", element_Z);
555 file->readData(
"element_A", element_A);
563 }
catch (std::runtime_error &) {
565 }
else if (version == 2) {
567 file->getAttr(
"formulaStyle", style);
569 if (style ==
"formula") {
571 file->readData(
"chemical_formula", formula);
574 }
else if (style ==
"userdefined") {
592 throw std::runtime_error(
"Only know how to read version 1 or 2 for Material");
599 if (file->hasData(
"packing_fraction")) {
610str_pair getAtomName(
const std::string &text)
613 if (text.size() <= 1)
614 return std::make_pair(text,
"");
619 if ((s[1] >=
'0' && s[1] <=
'9') || s[1] ==
'.')
620 return std::make_pair(text.substr(0, 1), text.substr(1));
622 return std::make_pair(text.substr(0, 2), text.substr(2));
630 for (
const auto &atom : tokens) {
632 std::string atomName;
633 float numberAtoms = 1;
634 uint16_t aNumber = 0;
637 if (atom.find(
'(') != std::string::npos) {
639 size_t end = atom.find(
')');
640 if (end == std::string::npos) {
641 std::stringstream msg;
642 msg <<
"Failed to parse isotope \"" << atom <<
"\"";
643 throw std::runtime_error(msg.str());
647 std::string numberAtomsStr = atom.substr(end + 1);
648 if (!numberAtomsStr.empty())
649 numberAtoms = boost::lexical_cast<float>(numberAtomsStr);
652 atomName = atom.substr(1, end - 1);
653 str_pair temp = getAtomName(atomName);
655 atomName = temp.first;
656 aNumber = boost::lexical_cast<uint16_t>(temp.second);
660 atomName = temp.first;
661 if (!temp.second.empty())
662 numberAtoms = boost::lexical_cast<float>(temp.second);
665 CF.emplace_back(getAtom(atomName, aNumber),
static_cast<double>(numberAtoms));
666 }
catch (boost::bad_lexical_cast &e) {
667 std::stringstream msg;
668 msg <<
"While trying to parse atom \"" << atom <<
"\" encountered bad_lexical_cast: " << e.what();
669 throw std::runtime_error(msg.str());
const std::vector< double > * lambda
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
ChemicalFormula m_chemicalFormula
The normalized chemical formula.
double m_totalScatterXSection
Material()
Default constructor.
double attenuationCoefficient(const double lambda) const
std::optional< AttenuationProfile > m_attenuationOverride
double m_temperature
Temperature.
void setAttenuationProfile(AttenuationProfile attenuationOverride)
Allow an explicit attenuation profile to be loaded onto the material that overrides the standard line...
double attenuation(const double distance, const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Compute the attenuation at a given wavelength over the given distance.
void setXRayAttenuationProfile(AttenuationProfile attenuationProfile)
void saveNexus(Nexus::File *file, const std::string &group) const
Save the object to an open NeXus file.
std::vector< FormulaUnit > ChemicalFormula
const Material::ChemicalFormula & chemicalFormula() const
double packingFraction() const
Get the packing fraction.
std::optional< AttenuationProfile > m_xRayAttenuationProfile
static ChemicalFormula parseChemicalFormula(const std::string &chemicalSymbol)
void countAtoms()
Update the total atom count.
double cohScatterLengthReal(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the coherent scattering length for a given wavelength in fm.
void calculateLinearAbsorpXSectionByWL()
Update the linear absorption x section (by wavelength)
double incohScatterLengthSqrd(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the incoherent length squared, , for a given wavelength in .
double incohScatterLength(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the incoherent length for a given wavelength in fm.
double temperature() const
Get the temperature.
double cohScatterLength(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the coherent scattering length for a given wavelength in fm.
double cohScatterLengthSqrd(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the coherent scattering length squared, , for a given wavelength in .
double cohScatterLengthImg(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the coherent scattering length for a given wavelength in fm.
double numberDensity() const
Get the number density.
double incohScatterLengthReal(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the incoherent length for a given wavelength in fm.
double m_packingFraction
Packing fraction should be between 0 and 2.
double m_atomTotal
Total number of atoms.
double totalAtoms() const
The total number of atoms in the formula.
double cohScatterXSection() const
Get the coherent scattering cross section for a given wavelength in barns.
double totalScatterLength(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Return the total scattering length for a given wavelength in fm.
double absorbXSection(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the absorption cross section at a given wavelength in barns.
const std::string & name() const
Returns the name of the material.
double numberDensityEffective() const
Get the effective number density.
double incohScatterXSection() const
Get the incoherent cross section for a given wavelength in barns.
void loadNexus(Nexus::File *file, const std::string &group)
Load the object from an open NeXus file.
double pressure() const
Get the pressure.
double incohScatterLengthImg(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the incoherent length for a given wavelength in fm.
double linearAbsorpCoef(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Returns the linear coefficient of absorption for the material in units of cm^-1 this should match the...
double totalScatterXSection() const
Return the total scattering cross section for a given wavelength in barns.
std::string m_name
Material name.
double m_linearAbsorpXSectionByWL
double m_pressure
Pressure.
double m_numberDensity
Number density in atoms per A^-3.
bool hasValidXRayAttenuationProfile()
double xRayAttenuation(const double distance, const double energy) const
Compute the x-ray attenuation at a given energy over the given distance.
double totalScatterLengthSqrd(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Return the total scattering length squared, , for a given wavelength in .
void calculateTotalScatterXSection()
Update the total scatter x section.
@ TOK_IGNORE_EMPTY
ignore empty tokens
Struture to hold the common information for an atom.
std::pair< std::string, std::string > str_pair
A namespace containing physical constants that are required by algorithms and unit routines.
MANTID_KERNEL_DLL const Atom & getAtom(const uint16_t z_number, const uint16_t a_number=0)
MANTID_KERNEL_DLL NeutronAtom getNeutronAtom(const uint16_t z_number, const uint16_t a_number=0)
Retrieve a copy of NeutronAtom.
Structure to store neutronic scattering information for the various elements.
double coh_scatt_length_real
The real part of the coherent scattering length in fm.
double tot_scatt_length
The total scattering length in fm.
double inc_scatt_length
The incoherent scattering length in fm.
uint16_t z_number
The atomic number, or number of protons, for the atom.
double coh_scatt_length
The coherent scattering length in fm.
double coh_scatt_length_img
The imaginary part of the coherent scattering length in fm.
double inc_scatt_length_img
The imaginary part of the incoherent scattering length in fm.
double inc_scatt_xs
The incoherent scattering cross section in barns.
double tot_scatt_xs
The total scattering cross section in barns.
double abs_scatt_xs
The absorption cross section for 2200m/s neutrons in barns.
double inc_scatt_length_real
The real part of the incoherent scattering length in fm.
double coh_scatt_xs
The coherent scattering cross section in barns.
static const double ReferenceLambda
The reference wavelength value for absorption cross sections.
uint16_t a_number
The total number of protons and neutrons, or mass number, for the atom for isotopic averages this is ...