Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/ConvertUnits.h"
#include "MantidAPI/AlgorithmFactory.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/Axis.h"
#include "MantidAPI/CommonBinsValidator.h"
#include "MantidAPI/HistogramValidator.h"
Michael Whitty
committed
#include "MantidAPI/Run.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/WorkspaceUnitValidator.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/Workspace2D.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/Instrument.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/CompositeValidator.h"
#include "MantidKernel/DeltaEMode.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/UnitFactory.h"
#include <boost/math/special_functions/fpclassify.hpp>
Russell Taylor
committed
#include <cfloat>
Roman Tolchenov
committed
#include <limits>
namespace Mantid {
namespace Algorithms {
// Register with the algorithm factory
DECLARE_ALGORITHM(ConvertUnits)
using namespace Kernel;
using namespace API;
Russell Taylor
committed
using namespace DataObjects;
using boost::function;
using boost::bind;
/// Default constructor
Federico Montesino Pouzols
committed
: Algorithm(), m_numberOfSpectra(0), m_distribution(false),
m_inputEvents(false), m_inputUnit(), m_outputUnit() {}
/// Initialisation method
auto wsValidator = boost::make_shared<CompositeValidator>();
wsValidator->add<WorkspaceUnitValidator>();
wsValidator->add<HistogramValidator>();
declareProperty(make_unique<WorkspaceProperty<API::MatrixWorkspace>>(
"InputWorkspace", "", Direction::Input, wsValidator),
"Name of the input workspace");
declareProperty(make_unique<WorkspaceProperty<API::MatrixWorkspace>>(
"OutputWorkspace", "", Direction::Output),
"Name of the output workspace, can be the same as the input");
// Extract the current contents of the UnitFactory to be the allowed values of
// the Target property
declareProperty("Target", "", boost::make_shared<StringListValidator>(
UnitFactory::Instance().getKeys()),
"The name of the units to convert to (must be one of those "
"registered in\n"
"the Unit Factory)");
std::vector<std::string> propOptions{"Elastic", "Direct", "Indirect"};
declareProperty("EMode", "Elastic",
boost::make_shared<StringListValidator>(propOptions),
"The energy mode (default: elastic)");
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) . Must be\n"
"set if the target unit requires it (e.g. DeltaE)");
declareProperty("AlignBins", false,
"If true (default is false), rebins after conversion to "
"ensure that all spectra in the output workspace\n"
"have identical bin boundaries. This option is not "
"recommended (see "
"http://www.mantidproject.org/ConvertUnits).");
}
/** Executes the algorithm
Russell Taylor
committed
* @throw std::runtime_error If the input workspace has not had its unit set
* @throw NotImplementedError If the input workspace contains point (not
* histogram) data
* @throw InstrumentDefinitionError If unable to calculate source-sample
* distance
*/
Russell Taylor
committed
// Get the workspaces
MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
Russell Taylor
committed
this->setupMemberVariables(inputWS);
Roman Tolchenov
committed
// Check that the input workspace doesn't already have the desired unit.
if (m_inputUnit->unitID() == m_outputUnit->unitID()) {
const std::string outputWSName = getPropertyValue("OutputWorkspace");
const std::string inputWSName = getPropertyValue("InputWorkspace");
if (outputWSName == inputWSName) {
// If it does, just set the output workspace to point to the input one and
// be done.
g_log.information() << "Input workspace already has target unit ("
<< m_outputUnit->unitID()
<< "), so just pointing the output workspace "
"property to the input workspace.\n";
setProperty("OutputWorkspace",
boost::const_pointer_cast<MatrixWorkspace>(inputWS));
// Clone the workspace.
IAlgorithm_sptr duplicate =
createChildAlgorithm("CloneWorkspace", 0.0, 0.6);
duplicate->initialize();
duplicate->setProperty("InputWorkspace", inputWS);
duplicate->execute();
Workspace_sptr temp = duplicate->getProperty("OutputWorkspace");
auto outputWs = boost::dynamic_pointer_cast<MatrixWorkspace>(temp);
setProperty("OutputWorkspace", outputWs);
return;
}
Roman Tolchenov
committed
}
if (inputWS->x(0).size() < 2) {
std::stringstream msg;
msg << "Input workspace has invalid X axis binning parameters. Should have "
"at least 2 values. Found " << inputWS->x(0).size() << ".";
throw std::runtime_error(msg.str());
}
if (inputWS->x(0).front() > inputWS->x(0).back() ||
inputWS->x(m_numberOfSpectra / 2).front() >
inputWS->x(m_numberOfSpectra / 2).back())
throw std::runtime_error("Input workspace has invalid X axis binning "
"parameters. X values should be increasing.");
Janik Zikovsky
committed
MatrixWorkspace_sptr outputWS = this->setupOutputWorkspace(inputWS);
Russell Taylor
committed
Russell Taylor
committed
// Check whether there is a quick conversion available
double factor, power;
if (m_inputUnit->quickConversion(*m_outputUnit, factor, power))
// If test fails, could also check whether a quick conversion in the opposite
// direction has been entered
Russell Taylor
committed
{
this->convertQuickly(outputWS, factor, power);
} else {
this->convertViaTOF(m_inputUnit, outputWS);
Russell Taylor
committed
}
Russell Taylor
committed
// If the units conversion has flipped the ascending direction of X, reverse
// all the vectors
if (!outputWS->x(0).empty() &&
(outputWS->x(0).front() > outputWS->x(0).back() ||
outputWS->x(m_numberOfSpectra / 2).front() >
outputWS->x(m_numberOfSpectra / 2).back())) {
Russell Taylor
committed
this->reverse(outputWS);
}
Roman Tolchenov
committed
Russell Taylor
committed
// Need to lop bins off if converting to energy transfer.
// Don't do for EventWorkspaces, where you can easily rebin to recover the
// situation without losing information
Russell Taylor
committed
/* This is an ugly test - could be made more general by testing for DBL_MAX
values at the ends of all spectra, but that would be less efficient */
if (m_outputUnit->unitID().find("Delta") == 0 && !m_inputEvents)
outputWS = this->removeUnphysicalBins(outputWS);
Russell Taylor
committed
Russell Taylor
committed
// Rebin the data to common bins if requested, and if necessary
bool alignBins = getProperty("AlignBins");
if (alignBins && !WorkspaceHelpers::commonBoundaries(outputWS))
Russell Taylor
committed
outputWS = this->alignBins(outputWS);
Russell Taylor
committed
// If appropriate, put back the bin width division into Y/E.
if (m_distribution && !m_inputEvents) // Never do this for event workspaces
Russell Taylor
committed
{
Russell Taylor
committed
this->putBackBinWidth(outputWS);
Russell Taylor
committed
}
Russell Taylor
committed
Russell Taylor
committed
// Point the output property to the right place.
// Do right at end (workspace could could change in removeUnphysicalBins or
// alignBins methods)
setProperty("OutputWorkspace", outputWS);
Russell Taylor
committed
}
Russell Taylor
committed
/** Initialise the member variables
* @param inputWS The input workspace
void ConvertUnits::setupMemberVariables(
const API::MatrixWorkspace_const_sptr inputWS) {
Russell Taylor
committed
m_numberOfSpectra = inputWS->getNumberHistograms();
// In the context of this algorithm, we treat things as a distribution if the
// flag is set
Russell Taylor
committed
// AND the data are not dimensionless
m_distribution = inputWS->isDistribution() && !inputWS->YUnit().empty();
// Check if its an event workspace
m_inputEvents =
(boost::dynamic_pointer_cast<const EventWorkspace>(inputWS) != nullptr);
Russell Taylor
committed
m_inputUnit = inputWS->getAxis(0)->unit();
const std::string targetUnit = getPropertyValue("Target");
Russell Taylor
committed
m_outputUnit = UnitFactory::Instance().create(targetUnit);
/** Create an output workspace of the appropriate (histogram or event) type and
* copy over the data
Russell Taylor
committed
* @param inputWS The input workspace
API::MatrixWorkspace_sptr ConvertUnits::setupOutputWorkspace(
const API::MatrixWorkspace_const_sptr inputWS) {
MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
// If input and output workspaces are NOT the same, create a new workspace for
// the output
if (outputWS != inputWS) {
}
if (!m_inputEvents && m_distribution) {
// Loop over the histograms (detector spectra)
Progress prog(this, 0.0, 0.2, m_numberOfSpectra);
PARALLEL_FOR1(outputWS)
for (int64_t i = 0; i < static_cast<int64_t>(m_numberOfSpectra); ++i) {
PARALLEL_START_INTERUPT_REGION
// Take the bin width dependency out of the Y & E data
auto &X = outputWS->x(i);
auto &Y = outputWS->mutableY(i);
auto &E = outputWS->mutableE(i);
for (size_t j = 0; j < outputWS->blocksize(); ++j) {
const double width = std::abs(X[j + 1] - X[j]);
Y[j] *= width;
E[j] *= width;
}
prog.report("Convert to " + m_outputUnit->unitID());
PARALLEL_END_INTERUPT_REGION
}
// Set the final unit that our output workspace will have
outputWS->getAxis(0)->unit() = m_outputUnit;
// Store the emode
const bool overwrite(true);
outputWS->mutableRun().addProperty("deltaE-mode", getPropertyValue("EMode"),
overwrite);
return outputWS;
}
/** Convert the workspace units according to a simple output = a * (input^b)
* relationship
Russell Taylor
committed
* @param outputWS :: the output workspace
* @param factor :: the conversion factor a to apply
* @param power :: the Power b to apply to the conversion
Russell Taylor
committed
*/
void ConvertUnits::convertQuickly(API::MatrixWorkspace_sptr outputWS,
const double &factor, const double &power) {
Progress prog(this, 0.2, 1.0, m_numberOfSpectra);
int64_t numberOfSpectra_i =
static_cast<int64_t>(m_numberOfSpectra); // cast to make openmp happy
Russell Taylor
committed
Russell Taylor
committed
// See if the workspace has common bins - if so the X vector can be common
// First a quick check using the validator
CommonBinsValidator sameBins;
bool commonBoundaries = false;
if (sameBins.isValid(outputWS) == "") {
commonBoundaries = WorkspaceHelpers::commonBoundaries(outputWS);
Russell Taylor
committed
// Only do the full check if the quick one passes
Russell Taylor
committed
// Calculate the new (common) X values
for (auto &x : outputWS->mutableX(0)) {
x = factor * std::pow(x, power);
Russell Taylor
committed
}
auto xVals = outputWS->sharedX(0);
Russell Taylor
committed
PARALLEL_FOR1(outputWS)
for (int64_t j = 1; j < numberOfSpectra_i; ++j) {
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
prog.report("Convert to " + m_outputUnit->unitID());
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
}
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
if (!m_inputEvents) // if in event mode the work is done
return;
Russell Taylor
committed
}
}
Russell Taylor
committed
EventWorkspace_sptr eventWS =
boost::dynamic_pointer_cast<EventWorkspace>(outputWS);
assert(static_cast<bool>(eventWS) == m_inputEvents); // Sanity check
Russell Taylor
committed
Russell Taylor
committed
// If we get to here then the bins weren't aligned and each spectrum is unique
Russell Taylor
committed
// Loop over the histograms (detector spectra)
Russell Taylor
committed
PARALLEL_FOR1(outputWS)
for (int64_t k = 0; k < numberOfSpectra_i; ++k) {
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
if (!commonBoundaries) {
for (auto &x : outputWS->mutableX(k)) {
x = factor * std::pow(x, power);
}
Russell Taylor
committed
}
Russell Taylor
committed
// Convert the events themselves if necessary. Inefficiently.
eventWS->getSpectrum(k).convertUnitsQuickly(factor, power);
Russell Taylor
committed
}
prog.report("Convert to " + m_outputUnit->unitID());
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
}
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
if (m_inputEvents)
eventWS->clearMRU();
Russell Taylor
committed
}
/** Convert the workspace units using TOF as an intermediate step in the
* conversion
Janik Zikovsky
committed
* @param fromUnit :: The unit of the input workspace
* @param outputWS :: The output workspace
Russell Taylor
committed
*/
void ConvertUnits::convertViaTOF(Kernel::Unit_const_sptr fromUnit,
API::MatrixWorkspace_sptr outputWS) {
Russell Taylor
committed
using namespace Geometry;
Russell Taylor
committed
EventWorkspace_sptr eventWS =
boost::dynamic_pointer_cast<EventWorkspace>(outputWS);
assert(static_cast<bool>(eventWS) == m_inputEvents); // Sanity check
Progress prog(this, 0.2, 1.0, m_numberOfSpectra);
int64_t numberOfSpectra_i =
static_cast<int64_t>(m_numberOfSpectra); // cast to make openmp happy
Russell Taylor
committed
// Get a pointer to the instrument contained in the workspace
Russell Taylor
committed
Instrument_const_sptr instrument = outputWS->getInstrument();
Russell Taylor
committed
// Get the parameter map
const ParameterMap &pmap = outputWS->constInstrumentParameters();
Russell Taylor
committed
Russell Taylor
committed
// Get the unit object for each workspace
Russell Taylor
committed
Kernel::Unit_const_sptr outputUnit = outputWS->getAxis(0)->unit();
Russell Taylor
committed
// Get the distance between the source and the sample (assume in metres)
IComponent_const_sptr source = instrument->getSource();
IComponent_const_sptr sample = instrument->getSample();
if (source == nullptr || sample == nullptr) {
throw Exception::InstrumentDefinitionError("Instrument not sufficiently "
"defined: failed to get source "
"and/or sample");
double l1;
Russell Taylor
committed
l1 = source->getDistance(*sample);
g_log.debug() << "Source-sample distance: " << l1 << '\n';
} catch (Exception::NotFoundError &) {
g_log.error("Unable to calculate source-sample distance");
throw Exception::InstrumentDefinitionError(
"Unable to calculate source-sample distance", outputWS->getTitle());
}
Russell Taylor
committed
/// @todo No implementation for any of these in the geometry yet so using
/// properties
Russell Taylor
committed
const std::string emodeStr = getProperty("EMode");
// Convert back to an integer representation
int emode = 0;
if (emodeStr == "Direct")
emode = 1;
else if (emodeStr == "Indirect")
emode = 2;
Russell Taylor
committed
// Not doing anything with the Y vector in to/fromTOF yet, so just pass empty
// vector
std::vector<double> emptyVec;
const bool needEfixed =
(outputUnit->unitID().find("DeltaE") != std::string::npos ||
outputUnit->unitID().find("Wave") != std::string::npos);
Michael Whitty
committed
double efixedProp = getProperty("Efixed");
Michael Whitty
committed
//... direct efixed gather
Michael Whitty
committed
// try and get the value from the run parameters
const API::Run &run = outputWS->run();
if (run.hasProperty("Ei")) {
Kernel::Property *prop = run.getProperty("Ei");
efixedProp = boost::lexical_cast<double, std::string>(prop->value());
} else {
if (needEfixed) {
throw std::invalid_argument(
"Could not retrieve incident energy from run object");
} else {
Michael Whitty
committed
}
Michael Whitty
committed
// set the Ei value in the run parameters
API::Run &run = outputWS->mutableRun();
Gigg, Martyn Anthony
committed
run.addProperty<double>("Ei", efixedProp, true);
Michael Whitty
committed
}
} else if (emode == 0 && efixedProp == EMPTY_DBL()) // Elastic
Michael Whitty
committed
{
efixedProp = 0.0;
}
std::vector<std::string> parameters =
outputWS->getInstrument()->getStringParameter("show-signed-theta");
bool bUseSignedVersion =
(!parameters.empty()) &&
find(parameters.begin(), parameters.end(), "Always") != parameters.end();
function<double(const IDetector &)> thetaFunction =
bUseSignedVersion
? bind(&MatrixWorkspace::detectorSignedTwoTheta, outputWS, _1)
: bind(&MatrixWorkspace::detectorTwoTheta, outputWS, _1);
// Loop over the histograms (detector spectra)
Russell Taylor
committed
PARALLEL_FOR1(outputWS)
for (int64_t i = 0; i < numberOfSpectra_i; ++i) {
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
Michael Whitty
committed
double efixed = efixedProp;
Russell Taylor
committed
Russell Taylor
committed
// Now get the detector object for this histogram
Russell Taylor
committed
IDetector_const_sptr det = outputWS->getDetector(i);
// Get the sample-detector distance for this detector (in metres)
Russell Taylor
committed
double l2, twoTheta;
Russell Taylor
committed
l2 = det->getDistance(*sample);
Russell Taylor
committed
// The scattering angle for this detector (in radians).
twoTheta = thetaFunction(*det);
Russell Taylor
committed
// If an indirect instrument, try getting Efixed from the geometry
Russell Taylor
committed
{
if (efixed == EMPTY_DBL()) {
try {
Parameter_sptr par = pmap.getRecursive(det.get(), "Efixed");
if (par) {
efixed = par->value<double>();
g_log.debug() << "Detector: " << det->getID()
<< " EFixed: " << efixed << "\n";
}
} catch (std::runtime_error &) { /* Throws if a DetectorGroup, use
single provided value */
Russell Taylor
committed
}
Russell Taylor
committed
}
} else // If this is a monitor then make l1+l2 = source-detector distance
// and twoTheta=0
Russell Taylor
committed
{
Russell Taylor
committed
l2 = det->getDistance(*source);
Russell Taylor
committed
twoTheta = 0.0;
Michael Whitty
committed
efixed = DBL_MIN;
Russell Taylor
committed
// Energy transfer is meaningless for a monitor, so set l2 to 0.
if (outputUnit->unitID().find("DeltaE") != std::string::npos) {
Russell Taylor
committed
l2 = 0.0;
}
Russell Taylor
committed
}
Michael Reuter
committed
// Make local copies of the units. This allows running the loop in
// parallel
Unit *localFromUnit = fromUnit->clone();
Unit *localOutputUnit = outputUnit->clone();
Janik Zikovsky
committed
/// @todo Don't yet consider hold-off (delta)
const double delta = 0.0;
// TODO toTOF and fromTOF need to be reimplemented outside of kernel
localFromUnit->toTOF(outputWS->dataX(i), emptyVec, l1, l2, twoTheta,
emode, efixed, delta);
// Convert from time-of-flight to the desired unit
localOutputUnit->fromTOF(outputWS->dataX(i), emptyVec, l1, l2, twoTheta,
emode, efixed, delta);
Janik Zikovsky
committed
// EventWorkspace part, modifying the EventLists.
eventWS->getSpectrum(i)
.convertUnitsViaTof(localFromUnit, localOutputUnit);
// eventWS->getSpectrum(i).getTofs(tofs);
// localFromUnit->toTOF(tofs,emptyVec,l1,l2,twoTheta,emode,efixed,delta);
// localOutputUnit->fromTOF(tofs,emptyVec,l1,l2,twoTheta,emode,efixed,delta);
// eventWS->getSpectrum(i).setTofs(tofs);
Russell Taylor
committed
}
Gigg, Martyn Anthony
committed
// Clear unit memory
delete localFromUnit;
delete localOutputUnit;
Russell Taylor
committed
} catch (Exception::NotFoundError &) {
// Get to here if exception thrown when calculating distance to detector
// Since you usually (always?) get to here when there's no attached
// detectors, this call is
// the same as just zeroing out the data (calling clearData on the
// spectrum)
}
prog.report("Convert to " + m_outputUnit->unitID());
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
} // loop over spectra
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
Russell Taylor
committed
if (failedDetectorCount != 0) {
g_log.information() << "Unable to calculate sample-detector distance for "
<< failedDetectorCount
<< " spectra. Masking spectrum.\n";
}
if (m_inputEvents)
eventWS->clearMRU();
}
/// Calls Rebin as a Child Algorithm to align the bins
API::MatrixWorkspace_sptr
ConvertUnits::alignBins(API::MatrixWorkspace_sptr workspace) {
Russell Taylor
committed
// Create a Rebin child algorithm
IAlgorithm_sptr childAlg = createChildAlgorithm("Rebin");
Roman Tolchenov
committed
childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", workspace);
// Next line for EventWorkspaces - needed for as long as in/out set same keeps
// as events.
Russell Taylor
committed
childAlg->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", workspace);
childAlg->setProperty<std::vector<double>>(
"Params", this->calculateRebinParams(workspace));
childAlg->executeAsChildAlg();
return childAlg->getProperty("OutputWorkspace");
Russell Taylor
committed
}
/// The Rebin parameters should cover the full range of the converted unit, with
/// the same number of bins
const std::vector<double> ConvertUnits::calculateRebinParams(
const API::MatrixWorkspace_const_sptr workspace) const {
Russell Taylor
committed
// Need to loop round and find the full range
double XMin = DBL_MAX, XMax = DBL_MIN;
const size_t numSpec = workspace->getNumberHistograms();
for (size_t i = 0; i < numSpec; ++i) {
Russell Taylor
committed
try {
Russell Taylor
committed
Geometry::IDetector_const_sptr det = workspace->getDetector(i);
auto &XData = workspace->x(i);
Roman Tolchenov
committed
double xfront = XData.front();
double xback = XData.back();
if (boost::math::isfinite(xfront) && boost::math::isfinite(xback)) {
if (xfront < XMin)
XMin = xfront;
if (xback > XMax)
XMax = xback;
Roman Tolchenov
committed
}
Russell Taylor
committed
}
} catch (Exception::NotFoundError &) {
} // Do nothing
Russell Taylor
committed
}
const double step =
(XMax - XMin) / static_cast<double>(workspace->blocksize());
Russell Taylor
committed
Russell Taylor
committed
}
Russell Taylor
committed
/** Reverses the workspace if X values are in descending order
* @param WS The workspace to operate on
*/
void ConvertUnits::reverse(API::MatrixWorkspace_sptr WS) {
if (WorkspaceHelpers::commonBoundaries(WS) && !m_inputEvents) {
std::reverse(WS->mutableX(0).begin(), WS->mutableX(0).end());
std::reverse(WS->mutableY(0).begin(), WS->mutableY(0).end());
std::reverse(WS->mutableE(0).begin(), WS->mutableE(0).end());
Roman Tolchenov
committed
for (size_t j = 1; j < m_numberOfSpectra; ++j) {
WS->setSharedX(j, xVals);
std::reverse(WS->mutableY(j).begin(), WS->mutableY(j).end());
std::reverse(WS->mutableE(j).begin(), WS->mutableE(j).end());
if (j % 100 == 0)
interruption_point();
Russell Taylor
committed
}
} else {
EventWorkspace_sptr eventWS =
boost::dynamic_pointer_cast<EventWorkspace>(WS);
assert(static_cast<bool>(eventWS) == m_inputEvents); // Sanity check
Russell Taylor
committed
int m_numberOfSpectra_i = static_cast<int>(m_numberOfSpectra);
Russell Taylor
committed
PARALLEL_FOR1(WS)
for (int j = 0; j < m_numberOfSpectra_i; ++j) {
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
eventWS->getSpectrum(j).reverse();
std::reverse(WS->mutableX(j).begin(), WS->mutableX(j).end());
std::reverse(WS->mutableY(j).begin(), WS->mutableY(j).end());
std::reverse(WS->mutableE(j).begin(), WS->mutableE(j).end());
Russell Taylor
committed
}
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
Roman Tolchenov
committed
}
/** Unwieldy method which removes bins which lie in a physically inaccessible
* region.
* This presently only occurs in conversions to energy transfer, where the
* initial
* unit conversion sets them to +/-DBL_MAX. This method removes those bins,
* leading
Russell Taylor
committed
* to a workspace which is smaller than the input one.
* As presently implemented, it unfortunately requires testing for and
* knowledge of
* aspects of the particular units conversion instead of keeping all that in
* the
* units class. It could be made more general, but that would be less
* efficient.
Janik Zikovsky
committed
* @param workspace :: The workspace after initial unit conversion
Russell Taylor
committed
* @return The workspace after bins have been removed
*/
API::MatrixWorkspace_sptr ConvertUnits::removeUnphysicalBins(
const Mantid::API::MatrixWorkspace_const_sptr workspace) {
Russell Taylor
committed
MatrixWorkspace_sptr result;
const size_t numSpec = workspace->getNumberHistograms();
Russell Taylor
committed
const std::string emode = getProperty("Emode");
if (emode == "Direct") {
// First the easy case of direct instruments, where all spectra will need
// the
Russell Taylor
committed
// same number of bins removed
// Need to make sure we don't pick a monitor as the 'reference' X spectrum
// (X0)
Russell Taylor
committed
try {
Geometry::IDetector_const_sptr det = workspace->getDetector(i);
if (!det->isMonitor())
break;
} catch (Exception::NotFoundError &) { /* Do nothing */
}
Russell Taylor
committed
}
// Get an X spectrum to search (they're all the same, monitors excepted)
auto start = std::lower_bound(X0.cbegin(), X0.cend(), -1.0e-10 * DBL_MAX);
if (start == X0.end()) {
const std::string e("Check the input EFixed: the one given leads to all "
"bins being in the physically inaccessible region.");
Russell Taylor
committed
g_log.error(e);
throw std::invalid_argument(e);
}
MantidVec::difference_type bins = X0.cend() - start;
MantidVec::difference_type first = start - X0.cbegin();
Russell Taylor
committed
result =
WorkspaceFactory::Instance().create(workspace, numSpec, bins, bins - 1);
Russell Taylor
committed
for (size_t i = 0; i < numSpec; ++i) {
auto &X = workspace->x(i);
auto &Y = workspace->y(i);
auto &E = workspace->e(i);
result->mutableX(i).assign(X.begin() + first, X.end());
result->mutableY(i).assign(Y.begin() + first, Y.end());
result->mutableE(i).assign(E.begin() + first, E.end());
Russell Taylor
committed
}
} else if (emode == "Indirect") {
// Now the indirect instruments. In this case we could want to keep a
// different
// number of bins in each spectrum because, in general L2 is different for
// each
Russell Taylor
committed
// one.
// Thus, we first need to loop to find largest 'good' range
Russell Taylor
committed
std::vector<MantidVec::difference_type> lastBins(numSpec);
for (size_t i = 0; i < numSpec; ++i) {
const MantidVec &X = workspace->readX(i);
auto end = std::lower_bound(X.cbegin(), X.cend(), 1.0e-10 * DBL_MAX);
MantidVec::difference_type bins = end - X.cbegin();
Russell Taylor
committed
lastBins[i] = bins;
if (bins > maxBins)
maxBins = static_cast<int>(bins);
Russell Taylor
committed
}
g_log.debug() << maxBins << '\n';
Russell Taylor
committed
// Now create an output workspace large enough for the longest 'good' range
result = WorkspaceFactory::Instance().create(workspace, numSpec, maxBins,
maxBins - 1);
Russell Taylor
committed
// Next, loop again copying in the correct range for each spectrum
for (int64_t j = 0; j < int64_t(numSpec); ++j) {
result->mutableX(j).assign(edges.cbegin(), edges.cbegin() + k);
// If the entire X range is not covered, generate fake values.
std::iota(result->mutableX(j).begin() + k, result->mutableX(j).end(),
workspace->x(j)[k] + 1);
result->mutableY(j)
.assign(workspace->y(j).cbegin(), workspace->y(j).cbegin() + (k - 1));
result->mutableE(j)
.assign(workspace->e(j).cbegin(), workspace->e(j).cbegin() + (k - 1));
Russell Taylor
committed
}
}
return result;
}
Russell Taylor
committed
/** Divide by the bin width if workspace is a distribution
* @param outputWS The workspace to operate on
*/
void ConvertUnits::putBackBinWidth(const API::MatrixWorkspace_sptr outputWS) {
Russell Taylor
committed
for (size_t i = 0; i < m_numberOfSpectra; ++i) {
for (size_t j = 0; j < outSize; ++j) {
const double width = std::abs(outputWS->x(i)[j + 1] - outputWS->x(i)[j]);
outputWS->mutableY(i)[j] = outputWS->y(i)[j] / width;
outputWS->mutableE(i)[j] = outputWS->e(i)[j] / width;
Russell Taylor
committed
}
}
}
} // namespace Algorithm
} // namespace Mantid