-
Dimitar Tasev authoredDimitar Tasev authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CreateWorkspace.cpp 9.11 KiB
#include "MantidAlgorithms/CreateWorkspace.h"
#include "MantidAPI/BinEdgeAxis.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/NumericAxis.h"
#include "MantidAPI/SpectraAxis.h"
#include "MantidAPI/TextAxis.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/UnitFactory.h"
namespace Mantid {
namespace Algorithms {
using namespace Kernel;
using namespace API;
using namespace HistogramData;
DECLARE_ALGORITHM(CreateWorkspace)
/// Init function
void CreateWorkspace::init() {
std::vector<std::string> unitOptions = UnitFactory::Instance().getKeys();
unitOptions.emplace_back("SpectraNumber");
unitOptions.emplace_back("Text");
declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"Name to be given to the created workspace.");
auto required = boost::make_shared<MandatoryValidator<std::vector<double>>>();
declareProperty(Kernel::make_unique<ArrayProperty<double>>("DataX", required),
"X-axis data values for workspace.");
declareProperty(Kernel::make_unique<ArrayProperty<double>>("DataY", required),
"Y-axis data values for workspace (measures).");
declareProperty(make_unique<ArrayProperty<double>>("DataE"),
"Error values for workspace. Optional.");
declareProperty(make_unique<PropertyWithValue<int>>("NSpec", 1),
"Number of spectra to divide data into.");
declareProperty("UnitX", "", "The unit to assign to the XAxis");
declareProperty("VerticalAxisUnit", "SpectraNumber",
boost::make_shared<StringListValidator>(unitOptions),
"The unit to assign to the second Axis (leave blank for "
"default Spectra number)");
declareProperty(make_unique<ArrayProperty<std::string>>("VerticalAxisValues"),
"Values for the VerticalAxis.");
declareProperty(
make_unique<PropertyWithValue<bool>>("Distribution", false),
"Whether OutputWorkspace should be marked as a distribution.");
declareProperty("YUnitLabel", "", "Label for Y Axis");
declareProperty("WorkspaceTitle", "", "Title for Workspace");
declareProperty(make_unique<WorkspaceProperty<>>("ParentWorkspace", "",
Direction::Input,
PropertyMode::Optional),
"Name of a parent workspace.");
}
/// Input validation
std::map<std::string, std::string> CreateWorkspace::validateInputs() {
std::map<std::string, std::string> issues;
const std::string vUnit = getProperty("VerticalAxisUnit");
const std::vector<std::string> vAxis = getProperty("VerticalAxisValues");
if (vUnit == "SpectraNumber" && !vAxis.empty())
issues["VerticalAxisValues"] =
"Axis values cannot be provided when using a spectra axis";
return issues;
}
/// Exec function
void CreateWorkspace::exec() {
// Contortions to get at the vector in the property without copying it
const Property *const dataXprop = getProperty("DataX");
const Property *const dataYprop = getProperty("DataY");
const Property *const dataEprop = getProperty("DataE");
const ArrayProperty<double> *pCheck = nullptr;
pCheck = dynamic_cast<const ArrayProperty<double> *>(dataXprop);
if (!pCheck)
throw std::invalid_argument("DataX cannot be casted to a double vector");
const std::vector<double> &dataX = *pCheck;
pCheck = dynamic_cast<const ArrayProperty<double> *>(dataYprop);
if (!pCheck)
throw std::invalid_argument("DataY cannot be casted to a double vector");
const std::vector<double> &dataY = *pCheck;
pCheck = dynamic_cast<const ArrayProperty<double> *>(dataEprop);
if (!pCheck)
throw std::invalid_argument("DataE cannot be casted to a double vector");
const std::vector<double> &dataE = *pCheck;
const int nSpec = getProperty("NSpec");
const std::string xUnit = getProperty("UnitX");
const std::string vUnit = getProperty("VerticalAxisUnit");
const std::vector<std::string> vAxis = getProperty("VerticalAxisValues");
if ((vUnit != "SpectraNumber") && (static_cast<int>(vAxis.size()) != nSpec)) {
throw std::invalid_argument(
"Number of y-axis labels must match number of histograms.");
}
// Verify length of vectors makes sense with NSpec
if ((dataY.size() % nSpec) != 0) {
throw std::invalid_argument("Length of DataY must be divisible by NSpec");
}
const std::size_t ySize = dataY.size() / nSpec;
// Check whether the X values provided are to be re-used for (are common to)
// every spectrum
const bool commonX(dataX.size() == ySize || dataX.size() == ySize + 1);
std::size_t xSize;
Kernel::cow_ptr<HistogramX> XValues(nullptr);
if (commonX) {
xSize = dataX.size();
XValues = Kernel::make_cow<HistogramX>(dataX);
} else {
if (dataX.size() % nSpec != 0) {
throw std::invalid_argument("Length of DataX must be divisible by NSpec");
}
xSize = static_cast<int>(dataX.size()) / nSpec;
if (xSize < ySize || xSize > ySize + 1) {
throw std::runtime_error("DataX width must be as DataY or +1");
}
}
const bool dataE_provided = !dataE.empty();
if (dataE_provided && dataY.size() != dataE.size()) {
throw std::runtime_error(
"DataE (if provided) must be the same size as DataY");
}
// Create the OutputWorkspace
MatrixWorkspace_const_sptr parentWS = getProperty("ParentWorkspace");
MatrixWorkspace_sptr outputWS;
if (parentWS) {
// if parent is defined use it to initialise the workspace
outputWS =
WorkspaceFactory::Instance().create(parentWS, nSpec, xSize, ySize);
} else {
// otherwise create a blank workspace
outputWS =
WorkspaceFactory::Instance().create("Workspace2D", nSpec, xSize, ySize);
}
Progress progress(this, 0, 1, nSpec);
PARALLEL_FOR_IF(Kernel::threadSafe(*outputWS))
for (int i = 0; i < nSpec; i++) {
PARALLEL_START_INTERUPT_REGION
const std::vector<double>::difference_type xStart = i * xSize;
const std::vector<double>::difference_type xEnd = xStart + xSize;
const std::vector<double>::difference_type yStart = i * ySize;
const std::vector<double>::difference_type yEnd = yStart + ySize;
// Just set the pointer if common X bins. Otherwise, copy in the right chunk
// (as we do for Y).
if (commonX) {
outputWS->setSharedX(i, XValues);
} else {
outputWS->mutableX(i)
.assign(dataX.begin() + xStart, dataX.begin() + xEnd);
}
outputWS->mutableY(i).assign(dataY.begin() + yStart, dataY.begin() + yEnd);
if (dataE_provided)
outputWS->mutableE(i)
.assign(dataE.begin() + yStart, dataE.begin() + yEnd);
progress.report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
// Set the Unit of the X Axis
try {
outputWS->getAxis(0)->unit() = UnitFactory::Instance().create(xUnit);
} catch (Exception::NotFoundError &) {
outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("Label");
Unit_sptr unit = outputWS->getAxis(0)->unit();
boost::shared_ptr<Units::Label> label =
boost::dynamic_pointer_cast<Units::Label>(unit);
label->setLabel(xUnit, xUnit);
}
// Populate the VerticalAxis. A spectra one is there by default with a 1->N
// mapping
if (vUnit != "SpectraNumber") {
if (vUnit == "Text") {
auto const newAxis = new TextAxis(vAxis.size());
outputWS->replaceAxis(1, newAxis);
for (size_t i = 0; i < vAxis.size(); i++) {
newAxis->setLabel(i, vAxis[i]);
}
} else {
const size_t vAxisLength = vAxis.size();
NumericAxis *newAxis(nullptr);
if (vAxisLength == static_cast<size_t>(nSpec))
newAxis = new NumericAxis(vAxisLength); // treat as points
else if (vAxisLength == static_cast<size_t>(nSpec + 1))
newAxis = new BinEdgeAxis(vAxisLength); // treat as bin edges
else
throw std::range_error("Invalid vertical axis length. It must be the "
"same length as NSpec or 1 longer.");
newAxis->unit() = UnitFactory::Instance().create(vUnit);
outputWS->replaceAxis(1, newAxis);
for (size_t i = 0; i < vAxis.size(); i++) {
try {
newAxis->setValue(i,
boost::lexical_cast<double, std::string>(vAxis[i]));
} catch (boost::bad_lexical_cast &) {
throw std::invalid_argument("CreateWorkspace - YAxisValues property "
"could not be converted to a double.");
}
}
}
}
// Set distribution flag
outputWS->setDistribution(getProperty("Distribution"));
// Set Y Unit label
if (!parentWS || !getPropertyValue("YUnitLabel").empty()) {
outputWS->setYUnitLabel(getProperty("YUnitLabel"));
}
// Set Workspace Title
if (!parentWS || !getPropertyValue("WorkspaceTitle").empty()) {
outputWS->setTitle(getProperty("WorkspaceTitle"));
}
// Set OutputWorkspace property
setProperty("OutputWorkspace", outputWS);
}
} // Algorithms
} // Mantid