Newer
Older
#include "MantidAlgorithms/ConvertAxesToRealSpace.h"
#include "MantidAPI/NumericAxis.h"
#include "MantidAPI/SpectrumInfo.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/WorkspaceFactory.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/IDetector.h"
#include "MantidKernel/Unit.h"
#include "MantidKernel/UnitFactory.h"
#include <limits>
namespace Mantid {
namespace Algorithms {
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::DataObjects;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ConvertAxesToRealSpace)
/// Algorithm's name
const std::string ConvertAxesToRealSpace::name() const {
return "ConvertAxesToRealSpace";
}
/// Algorithm's version for identification. @see Algorithm::version
int ConvertAxesToRealSpace::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string ConvertAxesToRealSpace::category() const {
return "Transforms\\Units;Transforms\\Axes";
}
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string ConvertAxesToRealSpace::summary() const {
return "Converts the spectrum and TOF axes to real space values, integrating "
"the data in the process";
/** Initialize the algorithm's properties.
*/
void ConvertAxesToRealSpace::init() {
declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
"InputWorkspace", "", Direction::Input),
declareProperty(make_unique<WorkspaceProperty<Workspace2D>>(
"OutputWorkspace", "", Direction::Output),
"An output workspace.");
std::vector<std::string> propOptions;
fillUnitMap(propOptions, m_unitMap, "x", "m");
fillUnitMap(propOptions, m_unitMap, "y", "m");
fillUnitMap(propOptions, m_unitMap, "z", "m");
fillUnitMap(propOptions, m_unitMap, "r", "m");
fillUnitMap(propOptions, m_unitMap, "theta", "deg");
fillUnitMap(propOptions, m_unitMap, "phi", "deg");
fillUnitMap(propOptions, m_unitMap, "2theta", "rad");
fillUnitMap(propOptions, m_unitMap, "signed2theta", "rad");
declareProperty("VerticalAxis", "y",
boost::make_shared<StringListValidator>(propOptions),
"What will be the vertical axis ?\n");
declareProperty("HorizontalAxis", "2theta",
boost::make_shared<StringListValidator>(propOptions),
"What will be the horizontal axis?\n");
declareProperty(
make_unique<Kernel::PropertyWithValue<int>>("NumberVerticalBins", 100),
"The number of bins along the vertical axis.");
declareProperty(
make_unique<Kernel::PropertyWithValue<int>>("NumberHorizontalBins", 100),
"The number of bins along the horizontal axis.");
}
/** Execute the algorithm.
*/
void ConvertAxesToRealSpace::exec() {
MatrixWorkspace_sptr inputWs = getProperty("InputWorkspace");
// set up axes data
std::vector<AxisData> axisVector = std::vector<AxisData>(2);
std::string hAxis = getProperty("HorizontalAxis");
axisVector[0].label = hAxis;
axisVector[0].bins = getProperty("NumberHorizontalBins");
std::string vAxis = getProperty("VerticalAxis");
axisVector[1].label = vAxis;
axisVector[1].bins = getProperty("NumberVerticalBins");
for (int axisIndex = 0; axisIndex < 2; ++axisIndex) {
axisVector[axisIndex].max = std::numeric_limits<double>::min();
axisVector[axisIndex].min = std::numeric_limits<double>::max();
}
// Create the output workspace. Can't re-use the input one because we'll be
// re-ordering the spectra.
MatrixWorkspace_sptr outputWs = WorkspaceFactory::Instance().create(
inputWs, axisVector[1].bins, axisVector[0].bins, axisVector[0].bins);
// first integrate the data
IAlgorithm_sptr alg = this->createChildAlgorithm("Integration", 0, 0.4);
alg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWs);
std::string outName = "_" + inputWs->getName() + "_integrated";
alg->setProperty("OutputWorkspace", outName);
alg->executeAsChildAlg();
MatrixWorkspace_sptr summedWs = alg->getProperty("OutputWorkspace");
int nHist = static_cast<int>(summedWs->getNumberHistograms());
Progress progress(this, 0.4, 1.0, nHist * 4);
std::vector<SpectraData> dataVector(nHist);
int failedCount = 0;
const auto &spectrumInfo = summedWs->spectrumInfo();
PARALLEL_FOR_IF(Kernel::threadSafe(*summedWs, *outputWs))
for (int i = 0; i < nHist; ++i) {
try {
V3D pos = spectrumInfo.position(i);
double r, theta, phi;
pos.getSpherical(r, theta, phi);
// for each axis
for (int axisIndex = 0; axisIndex < 2; ++axisIndex) {
double axisValue = std::numeric_limits<double>::min();
std::string axisSelection = axisVector[axisIndex].label;
// get the selected value for this axis
if (axisSelection == "x") {
axisValue = pos.X();
} else if (axisSelection == "y") {
axisValue = pos.Y();
} else if (axisSelection == "z") {
axisValue = pos.Z();
} else if (axisSelection == "r") {
axisValue = r;
} else if (axisSelection == "theta") {
axisValue = theta;
} else if (axisSelection == "phi") {
axisValue = phi;
} else if (axisSelection == "2theta") {
axisValue = spectrumInfo.twoTheta(i);
} else if (axisSelection == "signed2theta") {
axisValue = spectrumInfo.signedTwoTheta(i);
if (axisIndex == 0) {
dataVector[i].horizontalValue = axisValue;
} else {
dataVector[i].verticalValue = axisValue;
}
// record the max and min values
if (axisValue > axisVector[axisIndex].max)
axisVector[axisIndex].max = axisValue;
if (axisValue < axisVector[axisIndex].min)
axisVector[axisIndex].min = axisValue;
}
} catch (const Exception::NotFoundError &) {
g_log.debug() << "Could not find detector for workspace index " << i
failedCount++;
// flag this is the datavector
dataVector[i].horizontalValue = std::numeric_limits<double>::min();
dataVector[i].verticalValue = std::numeric_limits<double>::min();
}
// take the values from the integrated data
dataVector[i].intensity = summedWs->y(i)[0];
dataVector[i].error = summedWs->e(i)[0];
progress.report("Calculating new coords");
g_log.warning() << "Could not find detector for " << failedCount
<< " spectra, see the debug log for more details.\n";
// set up the axes on the output workspace
std::vector<double> x_tmp(axisVector[0].bins);
MantidVecPtr y;
fillAxisValues(x_tmp, axisVector[0], false);
HistogramData::Points x(std::move(x_tmp));
outputWs->getAxis(0)->unit() = UnitFactory::Instance().create("Label");
Unit_sptr xUnit = outputWs->getAxis(0)->unit();
boost::shared_ptr<Units::Label> xlabel =
boost::dynamic_pointer_cast<Units::Label>(xUnit);
xlabel->setLabel(axisVector[0].label, m_unitMap[axisVector[0].label]);
MantidVec &yRef = y.access();
yRef.resize(axisVector[1].bins);
fillAxisValues(yRef, axisVector[1], false);
auto const yAxis = new NumericAxis(yRef);
boost::shared_ptr<Units::Label> ylabel =
boost::dynamic_pointer_cast<Units::Label>(
UnitFactory::Instance().create("Label"));
ylabel->setLabel(axisVector[1].label, m_unitMap[axisVector[1].label]);
yAxis->unit() = ylabel;
outputWs->replaceAxis(1, yAxis);
// work out where to put the data into the output workspace, but don't do it
// yet as that needs to be single threaded
PARALLEL_FOR_NO_WSP_CHECK()
for (int i = 0; i < nHist; ++i) {
// find write index for data point
if (dataVector[i].horizontalValue == std::numeric_limits<double>::min()) {
dataVector[i].horizontalIndex = -1;
dataVector[i].verticalIndex = -1;
} else {
int xIndex = static_cast<int>(std::distance(
x.cbegin(), std::lower_bound(x.cbegin(), x.cend(),
dataVector[i].horizontalValue)));
if (xIndex > 0)
--xIndex;
int yIndex = static_cast<int>(std::distance(
y->begin(),
std::lower_bound(y->begin(), y->end(), dataVector[i].verticalValue)));
if (yIndex > 0)
--yIndex;
dataVector[i].horizontalIndex = xIndex;
dataVector[i].verticalIndex = yIndex;
progress.report("Calculating Rebinning");
}
// set all the X arrays - share the same vector
int nOutputHist = static_cast<int>(outputWs->getNumberHistograms());
PARALLEL_FOR_IF(Kernel::threadSafe(*outputWs))
for (int i = 0; i < nOutputHist; ++i) {
outputWs->setPoints(i, x);
// insert the data into the new workspace
// single threaded
for (int i = 0; i < nHist; ++i) {
int xIndex = dataVector[i].horizontalIndex;
int yIndex = dataVector[i].verticalIndex;
// using -1 as a flag for could not find detector
if ((xIndex == -1) || (yIndex == -1)) {
// do nothing the detector could not be found
g_log.warning() << "here " << i << '\n';
auto &yVec = outputWs->mutableY(yIndex);
yVec[xIndex] = yVec[xIndex] + dataVector[i].intensity;
auto &eVec = outputWs->mutableE(yIndex);
eVec[xIndex] = eVec[xIndex] + (dataVector[i].error * dataVector[i].error);
progress.report("Assigning to new grid");
// loop over the data and sqrt the errors to complete the error calculation
PARALLEL_FOR_IF(Kernel::threadSafe(*outputWs))
for (int i = 0; i < nOutputHist; ++i) {
auto &errorVec = outputWs->mutableE(i);
std::transform(errorVec.begin(), errorVec.end(), errorVec.begin(),
static_cast<double (*)(double)>(sqrt));
progress.report("Completing Error Calculation");
// Execute the transform and bind to the output.
setProperty("OutputWorkspace", outputWs);
}
/** Fills the values in an axis linearly from min to max for a given number of
* steps
* @param vector the vector to fill
* @param axisData the data about the axis
* @param isHistogram true if the data should be a histogram rather than point
void ConvertAxesToRealSpace::fillAxisValues(MantidVec &vector,
const AxisData &axisData,
bool isHistogram) {
int numBins = axisData.bins;
double binDelta =
(axisData.max - axisData.min) / static_cast<double>(numBins);
if (isHistogram)
numBins++;
for (int i = 0; i < numBins; ++i) {
vector[i] = axisData.min + i * binDelta;
}
/** Fills the unit map and ordered vector with the same data
* @param orderedVector the vector to fill
* @param unitMap the map to fill
* @param caption the caption of the unit
* @param unit the unit of measure of the unit
*/
void ConvertAxesToRealSpace::fillUnitMap(
std::vector<std::string> &orderedVector,
std::map<std::string, std::string> &unitMap, const std::string &caption,
const std::string &unit) {
orderedVector.push_back(caption);
}
} // namespace Mantid