Skip to content

Commit

Permalink
Re #7279. Fairly major reworking of SetSampleMaterial.
Browse files Browse the repository at this point in the history
 * Collapsed the if/else try/catch tree
 * Factored out some code into a separate function
 * Adding more verification and guidance of input parameters.
  • Loading branch information
peterfpeterson committed Jun 13, 2013
1 parent ea310d3 commit 41bcb8c
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Includes
//--------------------------------
#include "MantidAPI/Algorithm.h"
#include "MantidKernel/NeutronAtom.h"

namespace Mantid
{
Expand Down Expand Up @@ -51,6 +52,8 @@ class DLLExport SetSampleMaterial : public Mantid::API::Algorithm
virtual int version() const { return (1); }
/// Algorithm's category for identification
virtual const std::string category() const { return "Sample;DataHandling"; }
/// @inheritdocs
virtual std::map<std::string, std::string> validateInputs();

private:
/// Sets documentation strings for this algorithm
Expand All @@ -60,8 +63,9 @@ class DLLExport SetSampleMaterial : public Mantid::API::Algorithm
///Execution code
void exec();
/// Print out the list of information for the material
void logMaterial(const Kernel::Material *mat);

void fixNeutron(Kernel::NeutronAtom &neutron,
double coh_xs, double inc_xs,
double abs_xs, double tot_xs);
};

}
Expand Down
212 changes: 128 additions & 84 deletions Code/Mantid/Framework/DataHandling/src/SetSampleMaterial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ Neutron scattering lengths and cross sections of the elements and their isotopes
// Includes
//--------------------------------
#include "MantidDataHandling/SetSampleMaterial.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/ExperimentInfo.h"
#include "MantidAPI/Workspace.h"
#include "MantidAPI/Sample.h"
#include "MantidGeometry/Crystal/OrientedLattice.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidKernel/Atom.h"
#include "MantidKernel/EnabledWhenProperty.h"
#include "MantidKernel/NeutronAtom.h"
#include "MantidKernel/Material.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/PhysicalConstants.h"
Expand All @@ -65,27 +65,14 @@ namespace DataHandling
using namespace Mantid::API;
using namespace Kernel;

void SetSampleMaterial::logMaterial(const Material *mat)
{
g_log.notice() << "Sample number density ";
if (mat->numberDensity() == 1.)
g_log.notice() << "(NOT SPECIFIED) ";
g_log.notice() << "= " << mat->numberDensity() << " atoms/Angstrom^3\n";
g_log.notice() << "Cross sections for wavelength = " << NeutronAtom::ReferenceLambda << "Angstroms\n"
<< "Coherent " << mat->cohScatterXSection() << " barns\n"
<< "Incoherent " << mat->incohScatterXSection() << " barns\n"
<< "Total " << mat->totalScatterXSection() << " barns\n"
<< "Absorption " << mat->absorbXSection() << " barns\n";
}

/**
* Initialize the algorithm
*/
void SetSampleMaterial::init()
{
using namespace Mantid::Kernel;
declareProperty(
new WorkspaceProperty<MatrixWorkspace>("InputWorkspace","",Direction::InOut),
new WorkspaceProperty<Workspace>("InputWorkspace","",Direction::InOut),
"The workspace with which to associate the sample ");
declareProperty("ChemicalFormula", "", "ChemicalFormula or AtomicNumber must be given. "
"Enter a composition as a molecular formula of \n"
Expand Down Expand Up @@ -122,11 +109,14 @@ namespace DataHandling
"Number of formulas in the unit cell needed for chemical formulas with more than 1 atom");
declareProperty("UnitCellVolume", EMPTY_DBL(), mustBePositive,
"Unit cell volume in Angstoms^3 needed for chemical formulas with more than 1 atom");
declareProperty("CoherentXSection", EMPTY_DBL(), mustBePositive,
"Optional: This coherent cross-section for the sample material in barns will be used instead of tabulated");
declareProperty("IncoherentXSection", EMPTY_DBL(), mustBePositive,
"Optional: This incoherent cross-section for the sample material in barns will be used instead of tabulated");
declareProperty("AttenuationXSection", EMPTY_DBL(), mustBePositive,
"Optional: This absorption cross-section for the sample material in barns will be used instead of calculated");
"Optional: This absorption cross-section for the sample material in barns will be used instead of tabulated");
declareProperty("ScatteringXSection", EMPTY_DBL(), mustBePositive,
"Optional: This scattering cross-section (coherent + incoherent) for the sample material in barns will be used instead of calculated");

"Optional: This total scattering cross-section (coherent + incoherent) for the sample material in barns will be used instead of tabulated");

// Perform Group Associations.
std::string formulaGrp("By Formula or Atomic Number");
Expand All @@ -139,40 +129,96 @@ namespace DataHandling
setPropertyGroup("ZParameter", densityGrp);
setPropertyGroup("UnitCellVolume", densityGrp);

std::string specificValuesGrp("Enter Specific Values");
std::string specificValuesGrp("Override Cross Section Values");
setPropertyGroup("CoherentXSection", specificValuesGrp);
setPropertyGroup("IncoherentXSection", specificValuesGrp);
setPropertyGroup("AttenuationXSection", specificValuesGrp);
setPropertyGroup("ScatteringXSection", specificValuesGrp);

// Extra property settings
setPropertySettings("AtomicNumber", new Kernel::EnabledWhenProperty("ChemicalFormula", Kernel::IS_DEFAULT));
setPropertySettings("MassNumber", new Kernel::EnabledWhenProperty("ChemicalFormula", Kernel::IS_DEFAULT));

setPropertySettings("UnitCellVolume", new Kernel::EnabledWhenProperty("SampleNumberDensity", Kernel::IS_DEFAULT));
setPropertySettings("ZParameter", new Kernel::EnabledWhenProperty("SampleNumberDensity", Kernel::IS_DEFAULT));

}

std::map<std::string, std::string> SetSampleMaterial::validateInputs()
{
std::map<std::string, std::string> result;
const std::string chemicalSymbol = getProperty("ChemicalFormula");
const int z_number = getProperty("AtomicNumber");
const int a_number = getProperty("MassNumber");
if (chemicalSymbol.empty())
{
if (z_number <= 0)
{
result["ChemicalFormula"] = "Need to specify the material";

}
}
else
{
if (z_number > 0)
result["AtomicNumber"] = "Cannot specify both ChemicalFormula and AtomicNumber";
}

if (a_number > 0 && z_number <= 0)
result["AtomicNumber"] = "Specified MassNumber without AtomicNumber";

return result;
}

/**
* Add the cross sections to the neutron atom if they are not-empty
* numbers. All values are in barns.
*
* @param neutron The neutron to update
* @param coh_xs Coherent cross section
* @param inc_xs Incoherent cross section
* @param abs_xs Absorption cross section
* @param tot_xs Total scattering cross section
*/
void SetSampleMaterial::fixNeutron(NeutronAtom &neutron,
double coh_xs, double inc_xs,
double abs_xs, double tot_xs)
{
if (!isEmpty(coh_xs))
neutron.coh_scatt_xs = coh_xs;
if (!isEmpty(inc_xs))
neutron.inc_scatt_xs = inc_xs;
if (!isEmpty(abs_xs))
neutron.abs_scatt_xs = abs_xs;
if (!isEmpty(tot_xs))
neutron.tot_scatt_xs = tot_xs;
}

/**
* Execute the algorithm
*/
void SetSampleMaterial::exec()
{
// Get the input workspace
MatrixWorkspace_sptr workspace = getProperty("InputWorkspace");
Workspace_sptr workspace = getProperty("InputWorkspace");
// an ExperimentInfo object has a sample
ExperimentInfo_sptr expInfo = boost::dynamic_pointer_cast<ExperimentInfo>(workspace);
if (!bool(expInfo))
{
throw std::runtime_error("InputWorkspace does not have a sample object");
}

// determine the sample number density
double rho = getProperty("SampleNumberDensity"); // in Angstroms-3
if (isEmpty(rho))
{
// if rho isn't set then just choose it to be one
rho = 1.;

double unitCellVolume = getProperty("UnitCellVolume"); // in Angstroms^3
double zParameter = getProperty("ZParameter"); // number of atoms

// get the unit cell volume from the workspace if it isn't set
if (isEmpty(unitCellVolume) && workspace->sample().hasOrientedLattice())
if (isEmpty(unitCellVolume) && expInfo->sample().hasOrientedLattice())
{
unitCellVolume = workspace->sample().getOrientedLattice().volume();
unitCellVolume = expInfo->sample().getOrientedLattice().volume();
g_log.notice() << "found unit cell volume " << unitCellVolume << " Angstrom^-3\n";
}
// density is just number of atoms in the unit cell
Expand All @@ -181,75 +227,73 @@ namespace DataHandling
rho = zParameter / unitCellVolume;
}

// get the scattering information
// get the scattering information - this will override table values
double coh_xs = getProperty("CoherentXSection"); // in barns
double inc_xs = getProperty("IncoherentXSection"); // in barns
double sigma_atten = getProperty("AttenuationXSection"); // in barns
double sigma_s = getProperty("ScatteringXSection"); // in barns

// determine the material
const std::string chemicalSymbol = getProperty("ChemicalFormula");
const int z_number = getProperty("AtomicNumber");
const int a_number = getProperty("MassNumber");
double sigma_atten = getProperty("AttenuationXSection"); // in barns
double sigma_s = getProperty("ScatteringXSection"); // in barns

// Use user variables if all three are given
if (sigma_atten != EMPTY_DBL() && sigma_s != EMPTY_DBL() && rho != EMPTY_DBL())
Material *mat = NULL;
if (!chemicalSymbol.empty())
{
// Use chemical formula if given by user
Material::ChemicalFormula CF = Material::parseChemicalFormula(chemicalSymbol);
g_log.notice() << "Found " << CF.atoms.size() << " atoms in \"" << chemicalSymbol << "\"\n";

double numAtoms = 0.; // number of atoms in formula
NeutronAtom neutron(0, 0., 0., 0., 0., 0., 0.); // starting thing for neutronic information
for (size_t i=0; i<CF.atoms.size(); i++)
{
Atom myAtom = getAtom(CF.atoms[i], CF.aNumbers[i]);
neutron = neutron + CF.numberAtoms[i] * myAtom.neutron;

g_log.information() << myAtom << ": " << myAtom.neutron << "\n";
numAtoms += static_cast<double>(CF.numberAtoms[i]);
}
// normalize the accumulated number by the number of atoms
neutron = (1. / numAtoms) * neutron; // funny syntax b/c of operators in neutron atom

fixNeutron(neutron, coh_xs, inc_xs, sigma_atten, sigma_s);

// create the material
mat = new Material(chemicalSymbol, neutron, rho);
}
else
{
NeutronAtom *neutron = new NeutronAtom(static_cast<uint16_t>(z_number), static_cast<uint16_t>(a_number),
0.0, 0.0, sigma_s, 0.0, sigma_s, sigma_atten);
Material *mat = new Material(chemicalSymbol, *neutron, rho);
workspace->mutableSample().setMaterial(*mat);
logMaterial(mat);
return;
// try using the atomic number
Atom atom = getAtom(static_cast<uint16_t>(z_number), static_cast<uint16_t>(a_number));
NeutronAtom neutron = atom.neutron;
fixNeutron(neutron, coh_xs, inc_xs, sigma_atten, sigma_s);

// create the material
mat = new Material(chemicalSymbol, neutron, rho);
}

// Use chemical symbol if given by user
try
// set the material on workspace
if (mat != NULL)
{
Atom myAtom = getAtom(chemicalSymbol, static_cast<uint16_t>(a_number));
Material *mat = new Material(chemicalSymbol, myAtom.neutron, myAtom.number_density);
workspace->mutableSample().setMaterial(*mat);
logMaterial(mat);
expInfo->mutableSample().setMaterial(*mat);
g_log.notice() << "Sample number density ";
if (isEmpty(mat->numberDensity()))
g_log.notice() << "was not specified\n";
else
g_log.notice() << "= " << mat->numberDensity() << " atoms/Angstrom^3\n";
g_log.notice() << "Cross sections for wavelength = " << NeutronAtom::ReferenceLambda << "Angstroms\n"
<< " Coherent " << mat->cohScatterXSection() << " barns\n"
<< " Incoherent " << mat->incohScatterXSection() << " barns\n"
<< " Total " << mat->totalScatterXSection() << " barns\n"
<< " Absorption " << mat->absorbXSection() << " barns\n";
}
catch (...)
else
{
// Use chemical formula if given by user
try
{
Material::ChemicalFormula CF = Material::parseChemicalFormula(chemicalSymbol);
g_log.notice() << "Found " << CF.atoms.size() << " atoms in \"" << chemicalSymbol << "\"\n";

double numAtoms = 0.; // number of atoms in formula
NeutronAtom neutron(0, 0., 0., 0., 0., 0., 0.); // starting thing for neutronic information
for (size_t i=0; i<CF.atoms.size(); i++)
{
Atom myAtom = getAtom(CF.atoms[i], CF.aNumbers[i]);
neutron = neutron + CF.numberAtoms[i] * myAtom.neutron;

g_log.information() << myAtom << ": " << myAtom.neutron << "\n";
numAtoms += static_cast<double>(CF.numberAtoms[i]);
}
// normalize the accumulated number by the number of atoms
neutron = (1. / numAtoms) * neutron; // funny syntax b/c of operators in neutron atom

// create the material
Material *mat = new Material(chemicalSymbol, neutron, rho);
workspace->mutableSample().setMaterial(*mat);
logMaterial(mat);
}
catch (...)
{
// Use atomic and mass number if chemical formula does not work
try
{
Atom myAtom = getAtom(static_cast<uint16_t>(z_number), static_cast<uint16_t>(a_number));
Material *mat = new Material(chemicalSymbol, myAtom.neutron, myAtom.number_density);
workspace->mutableSample().setMaterial(*mat);
logMaterial(mat);
}
catch(std::invalid_argument&)
{
g_log.notice("ChemicalFormula or AtomicNumber was not found in table.");
throw std::invalid_argument("ChemicalFormula or AtomicNumber was not found in table");
}
}
throw std::runtime_error("Failed to create a material");
}

// Done!
progress(1);
}
Expand Down

0 comments on commit 41bcb8c

Please sign in to comment.