-
Simon Heybrock authoredSimon Heybrock authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CorrectToFile.cpp 8.80 KiB
#include "MantidAlgorithms/CorrectToFile.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/Unit.h"
#include "MantidKernel/UnitFactory.h"
namespace Mantid {
namespace Algorithms {
using namespace Mantid::API;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(CorrectToFile)
// estimate that this algorithm will spend half it's time loading the file
const double CorrectToFile::LOAD_TIME = 0.5;
void CorrectToFile::init() {
declareProperty(Kernel::make_unique<API::WorkspaceProperty<>>(
"WorkspaceToCorrect", "", Kernel::Direction::Input),
"Name of the input workspace");
declareProperty(Kernel::make_unique<API::FileProperty>(
"Filename", "", API::FileProperty::Load),
"The file containing the correction factors");
std::vector<std::string> propOptions =
Kernel::UnitFactory::Instance().getKeys();
propOptions.emplace_back("SpectrumNumber");
declareProperty("FirstColumnValue", "Wavelength",
boost::make_shared<Kernel::StringListValidator>(propOptions),
"The units of the first column of the correction file "
"(default wavelength)");
std::vector<std::string> operations{"Divide", "Multiply"};
declareProperty("WorkspaceOperation", "Divide",
boost::make_shared<Kernel::StringListValidator>(operations),
"Allowed values: Divide, Multiply (default is divide)");
declareProperty(Kernel::make_unique<API::WorkspaceProperty<>>(
"OutputWorkspace", "", Kernel::Direction::Output),
"Name of the output workspace to store the results in");
}
void CorrectToFile::exec() {
// The input workspace is the uncorrected data
MatrixWorkspace_sptr toCorrect = getProperty("WorkspaceToCorrect");
// This workspace is loaded from the RKH compatible file
MatrixWorkspace_sptr rkhInput = loadInFile(getProperty("Filename"));
// Only create the output workspace if it's not the same as the input one
MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
if (outputWS != toCorrect) {
outputWS = WorkspaceFactory::Instance().create(toCorrect);
}
const std::string operation = getProperty("WorkspaceOperation");
if (getPropertyValue("FirstColumnValue") == "SpectrumNumber") {
// the workspace (probably) contains many spectra but each with only 1 bin
doWkspAlgebra(toCorrect, rkhInput, operation, outputWS);
} else // interpolation the correction values and divide or multiply the input
// by these values
{ // the correction values should be all contained in 1 spectrum
// Check that the workspace to rebin has the same units as the one that we
// are matching to
// However, just print a warning if it isn't, don't abort (since user
// provides the file's unit)
if (toCorrect->getAxis(0)->unit()->unitID() !=
rkhInput->getAxis(0)->unit()->unitID()) {
g_log.warning("Unit on input workspace is different to that specified in "
"'FirstColumnValue' property");
}
// Get references to the correction factors
auto &Xcor = rkhInput->x(0);
auto &Ycor = rkhInput->y(0);
auto &Ecor = rkhInput->e(0);
const bool divide = operation == "Divide";
double Yfactor, correctError;
const int64_t nOutSpec =
static_cast<int64_t>(outputWS->getNumberHistograms());
const size_t nbins = outputWS->blocksize();
// Set the progress bar
Progress prg(this, 0 /*LOAD_TIME*/, 1.0, nOutSpec);
for (int64_t i = 0; i < nOutSpec; ++i) {
const auto xIn = toCorrect->points(i);
outputWS->setSharedX(i, toCorrect->sharedX(i));
auto &yOut = outputWS->mutableY(i);
auto &eOut = outputWS->mutableE(i);
auto &yIn = toCorrect->y(i);
auto &eIn = toCorrect->e(i);
for (size_t j = 0; j < nbins; ++j) {
const double currentX = xIn[j];
// Find out the index of the first correction point after this value
auto pos = std::lower_bound(Xcor.cbegin(), Xcor.cend(), currentX);
const size_t index = pos - Xcor.begin();
if (index == Xcor.size()) {
// If we're past the end of the correction factors vector, use the
// last point
Yfactor = Ycor[index - 1];
correctError = Ecor[index - 1];
} else if (index) {
// Calculate where between the two closest points our current X value
// is
const double fraction =
(currentX - Xcor[index - 1]) / (Xcor[index] - Xcor[index - 1]);
// Now linearly interpolate to find the correction factors to use
Yfactor =
Ycor[index - 1] + fraction * (Ycor[index] - Ycor[index - 1]);
correctError =
Ecor[index - 1] + fraction * (Ecor[index] - Ecor[index - 1]);
} else {
// If we're before the start of the correction factors vector, use the
// first point
Yfactor = Ycor[0];
correctError = Ecor[0];
}
// Now do the correction on the current point
if (divide) {
yOut[j] = yIn[j] / Yfactor;
// the proportional error is equal to the sum of the proportional
// errors
// re-arrange so that you don't get infinity if leftY==0. Sa = error
// on a, etc.
// c = a/b
// (Sa/a)2 + (Sb/b)2 = (Sc/c)2
// (Sa c/a)2 + (Sb c/b)2 = (Sc)2
// = (Sa 1/b)2 + (Sb (a/b2))2
// (Sc)2 = (1/b)2( (Sa)2 + (Sb a/b)2 )
eOut[j] =
sqrt(pow(eIn[j], 2) + pow(yIn[j] * correctError / Yfactor, 2)) /
Yfactor;
} else {
yOut[j] = yIn[j] * Yfactor;
// error multiplying two uncorrelated numbers, re-arrange so that you
// don't get infinity if leftY or rightY == 0
// Sa = error on a, etc.
// c = a*b
// (Sa/a)2 + (Sb/b)2 = (Sc/c)2
// (Sc)2 = (Sa c/a)2 + (Sb c/b)2 = (Sa b)2 + (Sb a)2
eOut[j] =
sqrt(pow(eIn[j] * Yfactor, 2) + pow(correctError * yIn[j], 2));
}
}
prg.report("CorrectToFile: applying " + operation);
}
}
// Set the resulting workspace
setProperty("Outputworkspace", outputWS);
}
/** Load in the RKH file for that has the correction information
* @param corrFile :: the name of the correction to load
* @return workspace containing the loaded data
* @throw runtime_error if load algorithm fails
*/
MatrixWorkspace_sptr CorrectToFile::loadInFile(const std::string &corrFile) {
g_log.information() << "Loading file " << corrFile << '\n';
progress(0, "Loading file");
IAlgorithm_sptr loadRKH =
createChildAlgorithm("LoadRKH", 0, 1.0 /*LOAD_TIME*/);
std::string rkhfile = getProperty("Filename");
loadRKH->setPropertyValue("Filename", rkhfile);
loadRKH->setPropertyValue("OutputWorkspace", "rkhout");
std::string columnValue = getProperty("FirstColumnValue");
loadRKH->setPropertyValue("FirstColumnValue", columnValue);
loadRKH->executeAsChildAlg();
g_log.debug() << corrFile << " loaded\n";
return loadRKH->getProperty("OutputWorkspace");
}
/** Multiply or divide the input workspace as specified by the user
* @param[in] lhs the first input workspace
* @param[in] rhs the last input workspace
* @param[in] algName the name of the algorithm to use on the input files
* @param[out] result the output workspace
* @throw NotFoundError if requested algorithm requested doesn't exist
* @throw runtime_error if algorithm encounters an error
*/
void CorrectToFile::doWkspAlgebra(API::MatrixWorkspace_sptr lhs,
API::MatrixWorkspace_sptr rhs,
const std::string &algName,
API::MatrixWorkspace_sptr &result) {
g_log.information() << "Initalising the algorithm " << algName << '\n';
progress(LOAD_TIME, "Applying correction");
IAlgorithm_sptr algebra = createChildAlgorithm(algName, LOAD_TIME, 1.0);
algebra->setProperty("LHSWorkspace", lhs);
algebra->setProperty("RHSWorkspace", rhs);
algebra->setProperty("OutputWorkspace", result);
try {
algebra->execute();
} catch (std::runtime_error &) {
g_log.warning() << "Error encountered while running algorithm " << algName
<< '\n';
g_log.error() << "Correction file "
<< getPropertyValue("Filename") +
" can't be used to correct workspace "
<< getPropertyValue("WorkspaceToCorrect") << '\n';
g_log.error() << "Mismatched number of spectra?\n";
throw std::runtime_error("Correct to file failed, see log for details");
}
result = algebra->getProperty("OutputWorkspace");
g_log.debug() << algName << " complete\n";
}
}
}