-
LamarMoore authored
Update to clang-format-5 and reformat the codebase
LamarMoore authoredUpdate to clang-format-5 and reformat the codebase
Code owners
ConvertSpectrumAxis2.cpp 11.80 KiB
#include "MantidAlgorithms/ConvertSpectrumAxis2.h"
#include "MantidAPI/InstrumentValidator.h"
#include "MantidAPI/NumericAxis.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/SpectraAxisValidator.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/DetectorInfo.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/CompositeValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/Unit.h"
#include "MantidKernel/UnitConversion.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidTypes/SpectrumDefinition.h"
#include <cfloat>
constexpr double rad2deg = 180.0 / M_PI;
namespace Mantid {
namespace Algorithms {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ConvertSpectrumAxis2)
using namespace Kernel;
using namespace API;
using namespace Geometry;
void ConvertSpectrumAxis2::init() {
// Validator for Input Workspace
auto wsVal = boost::make_shared<CompositeValidator>();
wsVal->add<SpectraAxisValidator>();
wsVal->add<InstrumentValidator>();
declareProperty(make_unique<WorkspaceProperty<>>("InputWorkspace", "",
Direction::Input, wsVal),
"The name of the input workspace.");
declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name to use for the output workspace.");
std::vector<std::string> targetOptions(7);
targetOptions[0] = "Theta";
targetOptions[1] = "SignedTheta";
targetOptions[2] = "ElasticQ";
targetOptions[3] = "ElasticQSquared";
targetOptions[4] = "theta";
targetOptions[5] = "signed_theta";
targetOptions[6] = "ElasticDSpacing";
declareProperty(
"Target", "", boost::make_shared<StringListValidator>(targetOptions),
"The unit to which spectrum axis is converted to - \"theta\" (for the "
"angle in degrees), Q or Q^2, where elastic Q is evaluated at EFixed. "
"Note that 'theta' and 'signed_theta' are there for compatibility "
"purposes; they are the same as 'Theta' and 'SignedTheta' respectively");
std::vector<std::string> eModeOptions;
eModeOptions.emplace_back("Direct");
eModeOptions.emplace_back("Indirect");
declareProperty("EMode", "Direct",
boost::make_shared<StringListValidator>(eModeOptions),
"Some unit conversions require this value to be set "
"(\"Direct\" or \"Indirect\")");
auto mustBePositive = boost::make_shared<BoundedValidator<double>>();
mustBePositive->setLower(0.0);
declareProperty("EFixed", EMPTY_DBL(), mustBePositive,
"Value of fixed energy in meV : EI (EMode=Direct) or EF "
"(EMode=Indirect))");
declareProperty("OrderAxis", true,
"Whether or not to sort the resulting"
" spectrum axis.");
}
void ConvertSpectrumAxis2::exec() {
// Get the input workspace.
API::MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
// Assign value to the member variable storing the number of histograms.
const size_t nHist = inputWS->getNumberHistograms();
// The unit to convert to.
const std::string unitTarget = getProperty("Target");
// Whether needs to be ordered
m_toOrder = getProperty("OrderAxis");
size_t nProgress = nHist;
if (m_toOrder) {
// we will need to loop twice, once to build the indexMap,
// once to copy over the spectra and set the output
nProgress *= 2;
} else {
m_axis.reserve(nHist);
}
Progress progress(this, 0.0, 1.0, nProgress);
// Call the functions to convert to the different forms of theta or Q.
if (unitTarget == "theta" || unitTarget == "Theta" ||
unitTarget == "signed_theta" || unitTarget == "SignedTheta") {
createThetaMap(progress, unitTarget, inputWS);
} else if (unitTarget == "ElasticQ" || unitTarget == "ElasticQSquared" ||
unitTarget == "ElasticDSpacing") {
createElasticQMap(progress, unitTarget, inputWS);
}
// Create an output workspace and set the property for it.
MatrixWorkspace_sptr outputWS =
createOutputWorkspace(progress, unitTarget, inputWS);
setProperty("OutputWorkspace", outputWS);
}
/** Converts X axis to theta representation
* @param progress :: Progress indicator
* @param targetUnit :: Target conversion unit
* @param inputWS :: Input Workspace
*/
void ConvertSpectrumAxis2::createThetaMap(API::Progress &progress,
const std::string &targetUnit,
API::MatrixWorkspace_sptr &inputWS) {
// Not sure about default, previously there was a call to a null function?
bool signedTheta = false;
if (targetUnit == "signed_theta" || targetUnit == "SignedTheta") {
signedTheta = true;
} else if (targetUnit == "theta" || targetUnit == "Theta") {
signedTheta = false;
}
bool warningGiven = false;
const auto &spectrumInfo = inputWS->spectrumInfo();
for (size_t i = 0; i < spectrumInfo.size(); ++i) {
if (!spectrumInfo.hasDetectors(i)) {
if (!warningGiven)
g_log.warning("The instrument definition is incomplete - spectra "
"dropped from output");
warningGiven = true;
continue;
}
if (!spectrumInfo.isMonitor(i)) {
if (signedTheta)
emplaceIndexMap(spectrumInfo.signedTwoTheta(i) * rad2deg, i);
else
emplaceIndexMap(spectrumInfo.twoTheta(i) * rad2deg, i);
} else {
emplaceIndexMap(0.0, i);
}
progress.report("Converting to theta...");
}
}
/** Convert X axis to Elastic Q representation
* @param progress :: Progress indicator
* @param targetUnit :: Target conversion unit
* @param inputWS :: Input workspace
*/
void ConvertSpectrumAxis2::createElasticQMap(
API::Progress &progress, const std::string &targetUnit,
API::MatrixWorkspace_sptr &inputWS) {
const std::string emodeStr = getProperty("EMode");
int emode = 0;
if (emodeStr == "Direct")
emode = 1;
else if (emodeStr == "Indirect")
emode = 2;
const auto &spectrumInfo = inputWS->spectrumInfo();
const auto &detectorInfo = inputWS->detectorInfo();
const size_t nHist = spectrumInfo.size();
for (size_t i = 0; i < nHist; i++) {
double theta(0.0), efixed(0.0);
if (!spectrumInfo.isMonitor(i)) {
theta = 0.5 * spectrumInfo.twoTheta(i);
/*
* Two assumptions made in the following code.
* 1. Getting the detector index of the first detector in the spectrum
* definition is enough (this should be completely safe).
* 2. That the time index is not important (first element of pair only
* accessed). i.e we are not performing scanning. Step scanning is not
* supported at the time of writing.
*/
const auto detectorIndex = spectrumInfo.spectrumDefinition(i)[0].first;
efixed = getEfixed(detectorIndex, detectorInfo, *inputWS,
emode); // get efixed
} else {
theta = DBL_MIN;
efixed = DBL_MIN;
}
// Convert to MomentumTransfer
double elasticQInAngstroms =
Kernel::UnitConversion::convertToElasticQ(theta, efixed);
if (targetUnit == "ElasticQ") {
emplaceIndexMap(elasticQInAngstroms, i);
} else if (targetUnit == "ElasticQSquared") {
// The QSquared value.
double elasticQSquaredInAngstroms =
elasticQInAngstroms * elasticQInAngstroms;
emplaceIndexMap(elasticQSquaredInAngstroms, i);
} else if (targetUnit == "ElasticDSpacing") {
double elasticDSpacing = 2 * M_PI / elasticQInAngstroms;
emplaceIndexMap(elasticDSpacing, i);
}
progress.report("Converting to " + targetUnit);
}
}
/** Create the final output workspace after converting the X axis
* @returns the final output workspace
*
* @param progress :: Progress indicator
* @param targetUnit :: Target conversion unit
* @param inputWS :: Input workspace
*/
MatrixWorkspace_sptr ConvertSpectrumAxis2::createOutputWorkspace(
API::Progress &progress, const std::string &targetUnit,
API::MatrixWorkspace_sptr &inputWS) {
MatrixWorkspace_sptr outputWorkspace = nullptr;
NumericAxis *newAxis = nullptr;
if (m_toOrder) {
// Can not re-use the input one because the spectra are re-ordered.
outputWorkspace = WorkspaceFactory::Instance().create(
inputWS, m_indexMap.size(), inputWS->x(0).size(), inputWS->y(0).size());
std::vector<double> axis;
axis.reserve(m_indexMap.size());
for (const auto &it : m_indexMap) {
axis.emplace_back(it.first);
}
newAxis = new NumericAxis(std::move(axis));
} else {
// If there is no reordering we can simply clone.
outputWorkspace = inputWS->clone();
newAxis = new NumericAxis(m_axis);
}
outputWorkspace->replaceAxis(1, newAxis);
// Set the units of the axis.
if (targetUnit == "theta" || targetUnit == "Theta" ||
targetUnit == "signed_theta" || targetUnit == "SignedTheta") {
newAxis->unit() = boost::make_shared<Units::Degrees>();
} else if (targetUnit == "ElasticQ") {
newAxis->unit() = UnitFactory::Instance().create("MomentumTransfer");
} else if (targetUnit == "ElasticQSquared") {
newAxis->unit() = UnitFactory::Instance().create("QSquared");
} else if (targetUnit == "ElasticDSpacing") {
newAxis->unit() = UnitFactory::Instance().create("dSpacing");
}
// Note that this is needed only for ordered case
if (m_toOrder) {
size_t currentIndex = 0;
std::multimap<double, size_t>::const_iterator it;
for (it = m_indexMap.begin(); it != m_indexMap.end(); ++it) {
// Copy over the data.
outputWorkspace->setHistogram(currentIndex,
inputWS->histogram(it->second));
// We can keep the spectrum numbers etc.
outputWorkspace->getSpectrum(currentIndex)
.copyInfoFrom(inputWS->getSpectrum(it->second));
++currentIndex;
progress.report("Setting output spectrum #" +
std::to_string(currentIndex));
}
}
return outputWorkspace;
}
double ConvertSpectrumAxis2::getEfixed(
const size_t detectorIndex, const Geometry::DetectorInfo &detectorInfo,
const Mantid::API::MatrixWorkspace &inputWS, const int emode) const {
double efixed(0);
double efixedProp = getProperty("Efixed");
Mantid::detid_t detectorID = detectorInfo.detectorIDs()[detectorIndex];
if (efixedProp != EMPTY_DBL()) {
efixed = efixedProp;
g_log.debug() << "Detector: " << detectorID << " Efixed: " << efixed
<< "\n";
} else {
if (emode == 1) {
if (inputWS.run().hasProperty("Ei")) {
efixed = inputWS.run().getLogAsSingleValue("Ei");
} else {
throw std::invalid_argument("Could not retrieve Efixed from the "
"workspace. Please provide a value.");
}
} else if (emode == 2) {
const auto &detectorSingle = detectorInfo.detector(detectorIndex);
std::vector<double> efixedVec =
detectorSingle.getNumberParameter("Efixed");
if (!efixedVec.empty()) {
efixed = efixedVec.at(0);
g_log.debug() << "Detector: " << detectorID << " EFixed: " << efixed
<< "\n";
} else {
g_log.warning() << "Efixed could not be found for detector "
<< detectorID << ", please provide a value\n";
throw std::invalid_argument("Could not retrieve Efixed from the "
"detector. Please provide a value.");
}
}
}
return efixed;
}
/** Emplaces inside the ordered or unordered index registry
* @param value :: value to insert
* @param wsIndex :: workspace index
*/
void ConvertSpectrumAxis2::emplaceIndexMap(double value, size_t wsIndex) {
if (m_toOrder) {
m_indexMap.emplace(value, wsIndex);
} else {
m_axis.emplace_back(value);
}
}
} // namespace Algorithms
} // namespace Mantid