Newer
Older
#include "MantidAlgorithms/Stitch1DMany.h"
#include "MantidAPI/ADSValidator.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceHistory.h"
#include "MantidAPI/WorkspaceProperty.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/RebinParamsValidator.h"
#include "MantidKernel/VisibleWhenProperty.h"
#include <boost/make_shared.hpp>
using namespace Mantid::Kernel;
using namespace Mantid::API;
namespace Mantid {
namespace Algorithms {
DECLARE_ALGORITHM(Stitch1DMany)
/// Initialize the algorithm's properties.
declareProperty(Kernel::make_unique<ArrayProperty<std::string>>(
"InputWorkspaces", boost::make_shared<ADSValidator>()),
"List or group of MatrixWorkspaces");
declareProperty(make_unique<WorkspaceProperty<Workspace>>(
"OutputWorkspace", "", Direction::Output),
declareProperty(make_unique<ArrayProperty<double>>(
"Params", boost::make_shared<RebinParamsValidator>(true),
"Rebinning Parameters, see Rebin algorithm for format.");
declareProperty(
make_unique<ArrayProperty<double>>("StartOverlaps", Direction::Input),
"Start overlaps for stitched workspaces; if specified, the number of "
"StartOverlaps must be 1 less than the number of input workspaces. "
"Optional.");
make_unique<ArrayProperty<double>>("EndOverlaps", Direction::Input),
"End overlaps for stitched workspaces; if specified, the number of "
"EndOverlaps must be the same as the number of StartOverlaps.");
declareProperty(make_unique<PropertyWithValue<bool>>("ScaleRHSWorkspace",
true, Direction::Input),
"Scaling either with respect to first (first hand side, LHS) "
"or second (right hand side, RHS) workspace");
declareProperty(make_unique<PropertyWithValue<bool>>("UseManualScaleFactors",
false, Direction::Input),
"True to use provided values for the scale factor.");
declareProperty(make_unique<ArrayProperty<double>>("ManualScaleFactors",
Direction::Input),
"Provided values for the scale factors; if specified, the "
"number of ManualScaleFactors must either be one (in which "
"case the provided value is applied to all input workspaces) "
"or 1 less than the number of input workspaces");
setPropertySettings("ManualScaleFactors",
make_unique<VisibleWhenProperty>("UseManualScaleFactors",
IS_EQUAL_TO, "1"));
make_unique<ArrayProperty<double>>("OutScaleFactors", Direction::Output),
"The actual used values for the scaling factors at each stitch step.");
auto scaleFactorFromPeriodValidator =
boost::make_shared<BoundedValidator<int>>();
scaleFactorFromPeriodValidator->setLower(1);
declareProperty(make_unique<PropertyWithValue<int>>(
"ScaleFactorFromPeriod", 1,
scaleFactorFromPeriodValidator, Direction::Input),
"Provided index of period to obtain scale factor from; "
"periods are indexed from 1 and used only if stitching group "
"workspaces, UseManualScaleFactors is true and "
"ManualScaleFactors is set to default.");
auto useManualScaleFactorsTrue =
VisibleWhenProperty("UseManualScaleFactors", IS_EQUAL_TO, "1");
auto manualScaleFactorsDefault =
VisibleWhenProperty("ManualScaleFactors", IS_DEFAULT);
auto scaleFactorFromPeriodVisible = make_unique<VisibleWhenProperty>(
useManualScaleFactorsTrue, manualScaleFactorsDefault, AND);
setPropertySettings("ScaleFactorFromPeriod",
std::move(scaleFactorFromPeriodVisible));
/// Load and validate the algorithm's properties.
std::map<std::string, std::string> Stitch1DMany::validateInputs() {
std::map<std::string, std::string> errors;
const std::vector<std::string> inputWorkspacesStr =
this->getProperty("InputWorkspaces");
// Add all input workspaces to a single row in the matrix
// Each 'row' are the workspaces belonging to a specific group
// Each 'column' are the workspaces belonging to a specific period
std::vector<MatrixWorkspace_sptr> inputWorkspaces;
for (const auto &ws : inputWorkspacesStr) {
auto inputMatrixWs =
AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(ws);
if (inputMatrixWs) {
inputWorkspaces.push_back(inputMatrixWs);
} else {
errors["InputWorkspaces"] = "Input workspaces must either be matrix "
"workspaces or group workspaces containing "
"matrix workspaces only";
break;
}
// Do not add workspaces if an error was found
if (!errors.count("InputWorkspaces"))
m_inputWSMatrix.push_back(inputWorkspaces);
m_numWSPerGroup = inputWorkspacesStr.size();
m_numWSPerPeriod = 1;
// Add common errors
validateCommonInputs(errors);
errors.insert(errors.begin(), errors.end());
/// Load and validate the algorithm's properties for workspace groups.
void Stitch1DMany::validateGroupWorkspacesInputs() {
std::map<std::string, std::string> errors;
const std::vector<std::string> inputWorkspacesStr =
this->getProperty("InputWorkspaces");
// Add all group workspaces and their constituent workspaces to their
// respective containers
for (const auto &groupWSName : inputWorkspacesStr) {
auto groupWS =
AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(groupWSName);
if (groupWS) {
m_inputWSGroups.push_back(groupWS);
} else {
errors["InputWorkspaces"] =
"Input workspaces must either be matrix workspaces or group "
"workspaces containing matrix workspaces only";
break;
}
m_numWSPerGroup = m_inputWSGroups.front()->size();
m_numWSPerPeriod = inputWorkspacesStr.size();
// Check all workspace groups are the same size
if (!errors.count("InputWorkspaces")) {
for (const auto &inputWsGroup : m_inputWSGroups) {
if (inputWsGroup->size() != m_numWSPerGroup) {
errors["InputWorkspaces"] =
"All workspace groups must be the same size.";
break;
}
}
}
if (!errors.count("InputWorkspaces")) {
// Each 'row' are the workspaces belonging to a specific period
// Each 'column' are the workspaces belonging to a specific group
for (size_t i = 0; i < m_numWSPerGroup; i++) {
std::vector<MatrixWorkspace_sptr> inputWorkspaces;
for (const auto &groupWS : m_inputWSGroups) {
auto inputMatrixWs =
boost::dynamic_pointer_cast<MatrixWorkspace>(groupWS->getItem(i));
if (inputMatrixWs) {
inputWorkspaces.push_back(inputMatrixWs);
} else {
errors["InputWorkspaces"] =
"Input workspaces must either be matrix workspaces or group "
"workspaces containing matrix workspaces only";
break;
}
}
if (errors.count("InputWorkspaces"))
break; // error found, stop adding workspaces
m_inputWSMatrix.push_back(inputWorkspaces);
int scaleFactorFromPeriod = this->getProperty("ScaleFactorFromPeriod");
m_scaleFactorFromPeriod = static_cast<size_t>(scaleFactorFromPeriod);
m_scaleFactorFromPeriod--; // To account for period being indexed from 1
if (m_scaleFactorFromPeriod >= m_inputWSGroups.size())
errors["ScaleFactorFromPeriod"] = "Period index out of range";
// Log all errors and throw a runtime error if an error is found
validateCommonInputs(errors);
auto &warnLog = getLogger().warning();
for (const auto &error : errors) {
warnLog << "Invalid value for " << error.first << ": " << error.second
<< "\n";
}
throw std::runtime_error("Some invalid Properties found");
}
}
/// Load and validate properties common to both group and non-group workspaces.
void Stitch1DMany::validateCommonInputs(
std::map<std::string, std::string> &errors) {
size_t numStitchableWS =
(m_numWSPerPeriod > 1) ? m_numWSPerPeriod : m_numWSPerGroup;
if (numStitchableWS < 2)
errors["InputWorkspaces"] = "At least 2 input workspaces required.";
m_startOverlaps = this->getProperty("StartOverlaps");
m_endOverlaps = this->getProperty("EndOverlaps");
m_scaleRHSWorkspace = this->getProperty("ScaleRHSWorkspace");
m_params = this->getProperty("Params");
if (!m_startOverlaps.empty() && m_startOverlaps.size() != numStitchableWS - 1)
errors["StartOverlaps"] = "If given, StartOverlaps must have one fewer "
"entries than the number of input workspaces.";
if (m_startOverlaps.size() != m_endOverlaps.size())
errors["EndOverlaps"] =
"EndOverlaps must have the same number of entries as StartOverlaps.";
m_useManualScaleFactors = this->getProperty("UseManualScaleFactors");
m_manualScaleFactors = this->getProperty("ManualScaleFactors");
if (!m_manualScaleFactors.empty()) {
if (m_manualScaleFactors.size() == 1) {
// Single value: fill with list of the same scale factor value
m_manualScaleFactors = std::vector<double>(numStitchableWS - 1,
m_manualScaleFactors.front());
} else if (m_manualScaleFactors.size() != numStitchableWS - 1) {
errors["ManualScaleFactors"] =
"If given, ManualScaleFactors must either "
"consist of one entry or one fewer entries "
"than the number of input workspaces";
}
}
/// Execute the algorithm.
std::string tempOutName;
doStitch1D(m_inputWSMatrix.front(), m_manualScaleFactors, m_outputWorkspace,
tempOutName);
// Save output
this->setProperty("OutputWorkspace", m_outputWorkspace);
this->setProperty("OutScaleFactors", m_scaleFactors);
}
/** Performs the Stitch1D algorithm at a specific workspace index.
* @param toStitch :: Vector of workspaces to be stitched
* @param scaleRhsWS :: Scaling either with respect to left or right workspaces
* @param manualScaleFactors :: Provided values for scaling factors
* @param outWS :: Output stitched workspace
* @param outName :: Output stitched workspace name
void Stitch1DMany::doStitch1D(std::vector<MatrixWorkspace_sptr> &toStitch,
const std::vector<double> &manualScaleFactors,
Workspace_sptr &outWS, std::string &outName) {
auto lhsWS = toStitch.front();
outName += "_" + lhsWS->getName();
for (size_t i = 1; i < toStitch.size(); i++) {
auto rhsWS = toStitch[i];
outName += "_" + rhsWS->getName();
IAlgorithm_sptr alg = createChildAlgorithm("Stitch1D");
alg->initialize();
alg->setProperty("LHSWorkspace", lhsWS);
alg->setProperty("RHSWorkspace", rhsWS);
if (m_startOverlaps.size() > i - 1) {
alg->setProperty("StartOverlap", m_startOverlaps[i - 1]);
alg->setProperty("EndOverlap", m_endOverlaps[i - 1]);
alg->setProperty("Params", m_params);
alg->setProperty("ScaleRHSWorkspace", m_scaleRHSWorkspace);
alg->setProperty("UseManualScaleFactor", m_useManualScaleFactors);
if (m_useManualScaleFactors)
alg->setProperty("ManualScaleFactor", manualScaleFactors[i - 1]);
alg->execute();
lhsWS = alg->getProperty("OutputWorkspace");
double outScaleFactor = alg->getProperty("OutScaleFactor");
m_scaleFactors.push_back(outScaleFactor);
if (!isChild()) {
// Copy each input workspace's history into our output workspace's history
for (const auto &inputWS : toStitch) {
lhsWS->history().addHistory(inputWS->getHistory());
}
/** Performs the Stitch1DMany algorithm at a specific period
* @param period :: The period index we are stitching at
* @param useManualScaleFactors :: True to use provided values for scale factors
* @param outName :: Output stitched workspace name
* @param outScaleFactors :: Actual values used for scale factors
*/
void Stitch1DMany::doStitch1DMany(const size_t period,
const bool useManualScaleFactors,
std::string &outName,
std::vector<double> &outScaleFactors) {
// List of workspaces to stitch
std::vector<std::string> toProcess;
for (const auto &groupWs : m_inputWSGroups) {
const std::string &wsName = groupWs->getItem(period)->getName();
toProcess.push_back(wsName);
outName += "_" + wsName;
}
IAlgorithm_sptr alg = createChildAlgorithm("Stitch1DMany");
alg->initialize();
alg->setChild(false);
alg->setProperty("InputWorkspaces", toProcess);
if (!outName.empty())
alg->setProperty("OutputWorkspace", outName);
alg->setProperty("StartOverlaps", m_startOverlaps);
alg->setProperty("EndOverlaps", m_endOverlaps);
alg->setProperty("Params", m_params);
alg->setProperty("ScaleRHSWorkspace", m_scaleRHSWorkspace);
alg->setProperty("UseManualScaleFactors", useManualScaleFactors);
if (useManualScaleFactors)
alg->setProperty("ManualScaleFactors", m_manualScaleFactors);
alg->execute();
outScaleFactors = alg->getProperty("OutScaleFactors");
}
bool Stitch1DMany::checkGroups() {
std::vector<std::string> wsNames = getProperty("InputWorkspaces");
try {
if (AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
wsNames.front()))
return true;
}
return false;
}
bool Stitch1DMany::processGroups() {
validateGroupWorkspacesInputs();
std::vector<std::string> toGroup; // List of workspaces to be grouped
std::string groupName = this->getProperty("OutputWorkspace");
std::string outName;
// Determine whether or not we are scaling workspaces using scale factors from
// a specific period
Property *manualSF = this->getProperty("ManualScaleFactors");
bool usingScaleFromPeriod = m_useManualScaleFactors && manualSF->isDefault();
if (!usingScaleFromPeriod) {
for (size_t i = 0; i < m_numWSPerGroup; ++i) {
outName = groupName;
std::vector<double> scaleFactors;
doStitch1DMany(i, m_useManualScaleFactors, outName, scaleFactors);
// Add the resulting workspace to the list to be grouped together
toGroup.push_back(outName);
// Add the scalefactors to the list so far
m_scaleFactors.insert(m_scaleFactors.end(), scaleFactors.begin(),
}
// Obtain scale factors for the specified period
std::string tempOutName;
std::vector<double> periodScaleFactors;
doStitch1DMany(m_scaleFactorFromPeriod, false, tempOutName,
periodScaleFactors);
// Iterate over each period
for (size_t i = 0; i < m_numWSPerGroup; i++) {
outName = groupName;
Workspace_sptr outStitchedWS;
doStitch1D(m_inputWSMatrix[i], periodScaleFactors, outStitchedWS,
outName);
// Add name of stitched workspaces to group list and ADS
toGroup.push_back(outName);
AnalysisDataService::Instance().addOrReplace(outName, outStitchedWS);
IAlgorithm_sptr groupAlg = createChildAlgorithm("GroupWorkspaces");
groupAlg->initialize();
groupAlg->setAlwaysStoreInADS(true);
groupAlg->setProperty("InputWorkspaces", toGroup);
groupAlg->setProperty("OutputWorkspace", groupName);
groupAlg->execute();
m_outputWorkspace =
AnalysisDataService::Instance().retrieveWS<Workspace>(groupName);
this->setProperty("OutputWorkspace", m_outputWorkspace);
this->setProperty("OutScaleFactors", m_scaleFactors);
return true;