Newer
Older
Gigg, Martyn Anthony
committed
//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------
#include "MantidKernel/Atom.h"
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/regex.hpp>
#include <boost/tokenizer.hpp>
Janik Zikovsky
committed
#include <stdexcept>
Gigg, Martyn Anthony
committed
{
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
typedef std::pair<std::string, std::string> str_pair;
Gigg, Martyn Anthony
committed
using PhysicalConstants::Atom;
using PhysicalConstants::getAtom;
Gigg, Martyn Anthony
committed
using PhysicalConstants::NeutronAtom;
/**
* Construct an "empty" material. Everything returns zero
*/
Material::Material() :
m_name(), m_element(0,0,0.0,0.0,0.0,0.0,0.0,0.0),
m_numberDensity(0.0), m_temperature(0.0), m_pressure(0.0)
Gigg, Martyn Anthony
committed
{
}
Gigg, Martyn Anthony
committed
/**
* Construct a material object
Janik Zikovsky
committed
* @param name :: The name of the material
* @param element :: The element it is composed from
* @param numberDensity :: Density in A^-3
* @param temperature :: The temperature in Kelvin (Default = 300K)
* @param pressure :: Pressure in kPa (Default: 101.325 kPa)
Gigg, Martyn Anthony
committed
*/
Material::Material(const std::string & name, const PhysicalConstants::NeutronAtom& element,
Gigg, Martyn Anthony
committed
const double numberDensity, const double temperature,
const double pressure) :
m_name(name), m_element(element), m_numberDensity(numberDensity),
Gigg, Martyn Anthony
committed
m_temperature(temperature), m_pressure(pressure)
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* Returns the name
* @returns A string containing the name of the material
*/
const std::string & Material::name() const
{
return m_name;
}
/**
* Get the number density
* @returns The number density of the material in A^-3
*/
double Material::numberDensity() const
{
return m_numberDensity;
}
/**
* Get the temperature
* @returns The temperature of the material in Kelvin
*/
double Material::temperature() const
{
return m_temperature;
}
/**
* Get the pressure
* @returns The pressure of the material
*/
double Material::pressure() const
{
return m_pressure;
}
/**
* Get the coherent scattering cross section for a given wavelength.
Gigg, Martyn Anthony
committed
* CURRENTLY this simply returns the value for the underlying element
Janik Zikovsky
committed
* @param lambda :: The wavelength to evaluate the cross section
Gigg, Martyn Anthony
committed
* @returns The value of the coherent scattering cross section at
* the given wavelength
*/
double Material::cohScatterXSection(const double lambda) const
Gigg, Martyn Anthony
committed
{
(void)lambda;
return m_element.coh_scatt_xs;
Gigg, Martyn Anthony
committed
}
/**
* Get the incoherent scattering cross section for a given wavelength
Gigg, Martyn Anthony
committed
* CURRENTLY this simply returns the value for the underlying element
Janik Zikovsky
committed
* @param lambda :: The wavelength to evaluate the cross section
Gigg, Martyn Anthony
committed
* @returns The value of the coherent scattering cross section at
* the given wavelength
*/
double Material::incohScatterXSection(const double lambda) const
Gigg, Martyn Anthony
committed
{
(void)lambda;
return m_element.inc_scatt_xs;
}
/**
* Get the total scattering cross section for a given wavelength
* CURRENTLY this simply returns the value for sum of the incoherent
* and coherent scattering cross sections.
Janik Zikovsky
committed
* @param lambda :: The wavelength to evaluate the cross section
* @returns The value of the total scattering cross section at
* the given wavelength
*/
double Material::totalScatterXSection(const double lambda) const
{
return m_element.tot_scatt_xs;
Gigg, Martyn Anthony
committed
}
/**
* Get the absorption cross section for a given wavelength.
* CURRENTLY This assumes a linear dependence on the wavelength with the reference
* wavelength = NeutronAtom::ReferenceLambda angstroms.
Janik Zikovsky
committed
* @param lambda :: The wavelength to evaluate the cross section
Gigg, Martyn Anthony
committed
* @returns The value of the absoprtioncross section at
* the given wavelength
*/
double Material::absorbXSection(const double lambda) const
Gigg, Martyn Anthony
committed
{
return (m_element.abs_scatt_xs) * (lambda / NeutronAtom::ReferenceLambda);
Gigg, Martyn Anthony
committed
}
Janik Zikovsky
committed
/** Save the object to an open NeXus file.
* @param file :: open NeXus file
* @param group :: name of the group to create
*/
void Material::saveNexus(::NeXus::File * file, const std::string & group) const
{
file->makeGroup(group, "NXdata", 1);
Janik Zikovsky
committed
file->putAttr("version", 1);
file->putAttr("name", m_name);
file->writeData("element_Z", m_element.z_number);
file->writeData("element_A", m_element.a_number);
file->writeData("number_density", m_numberDensity);
file->writeData("temperature", m_temperature);
file->writeData("pressure", m_pressure);
file->closeGroup();
}
/** Load the object from an open NeXus file.
* @param file :: open NeXus file
* @param group :: name of the group to open
*/
void Material::loadNexus(::NeXus::File * file, const std::string & group)
{
file->openGroup(group, "NXdata");
Janik Zikovsky
committed
file->getAttr("name", m_name);
// Find the element
uint16_t element_Z, element_A;
file->readData("element_Z", element_Z);
file->readData("element_A", element_A);
Janik Zikovsky
committed
try {
Janik Zikovsky
committed
m_element = Mantid::PhysicalConstants::getNeutronAtom(element_Z, element_A);
Janik Zikovsky
committed
}
catch (std::runtime_error &)
Janik Zikovsky
committed
{ /* ignore and use the default */ }
Janik Zikovsky
committed
file->readData("number_density", m_numberDensity);
file->readData("temperature", m_temperature);
file->readData("pressure", m_pressure);
file->closeGroup();
}
namespace { // anonymous namespace to hide the function
str_pair getAtomName(std::string &text) // TODO change to get number after letters
{
// one character doesn't need
if (text.size() <= 1)
return std::make_pair(text, "");
// check the second character
const char *s;
s = text.c_str();
if ((s[1] >= '0' && s[1]<='9') || s[1] == '.')
return std::make_pair(text.substr(0,1), text.substr(1));
else
return std::make_pair(text.substr(0,2), text.substr(2));
}
}
Material::ChemicalFormula Material::parseChemicalFormula(const std::string chemicalSymbol)
const boost::char_separator<char> ATOM_DELIM(" -");
tokenizer tokens(chemicalSymbol, ATOM_DELIM);
for (auto atom = tokens.begin(); atom != tokens.end(); ++atom)
try {
std::string name(*atom);
float numberAtoms = 1;
uint16_t aNumber = 0;
// split out the isotope bit
if (atom->find('(') != std::string::npos)
{
// error check
size_t end = atom->find(')');
if (end == std::string::npos)
{
std::stringstream msg;
msg << "Failed to parse isotope \"" << name << "\"";
throw std::runtime_error(msg.str());
}
// get the number of atoms
std::string numberAtomsStr = name.substr(end+1);
if (!numberAtomsStr.empty())
numberAtoms = boost::lexical_cast<float>(numberAtomsStr);
// split up the atom and isotope number
name = name.substr(1, end-1);
str_pair temp = getAtomName(name);
name = temp.first;
aNumber = boost::lexical_cast<uint16_t>(temp.second);
}
else // for non-isotopes
{
str_pair temp = getAtomName(name);
name = temp.first;
if (!temp.second.empty())
numberAtoms = boost::lexical_cast<float>(temp.second);
}
CF.atoms.push_back(boost::make_shared<Atom>(getAtom(name, aNumber)));
CF.numberAtoms.push_back(numberAtoms);
catch (boost::bad_lexical_cast &e)
std::stringstream msg;
msg << "While trying to parse atom \"" << (*atom)
<< "\" encountered bad_lexical_cast: " << e.what();
throw std::runtime_error(msg.str());