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"
Russell Taylor
committed
#include "MantidDataObjects/Workspace2D.h"
#include "MantidDataObjects/EventWorkspace.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->dataX(0).size() < 2) {
std::stringstream msg;
msg << "Input workspace has invalid X axis binning parameters. Should have "
"at least 2 values. Found "
<< inputWS->dataX(0).size() << ".";
throw std::runtime_error(msg.str());
}
if (inputWS->dataX(0).front() > inputWS->dataX(0).back() ||
inputWS->dataX(m_numberOfSpectra / 2).front() >
inputWS->dataX(m_numberOfSpectra / 2).back())
throw std::runtime_error("Input workspace has invalid X axis binning "
"parameters. X values should be increasing.");
Janik Zikovsky
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
{
outputWS = this->convertQuickly(inputWS, factor, power);
outputWS = this->convertViaTOF(m_inputUnit, inputWS);
Russell Taylor
committed
}
Russell Taylor
committed
// If the units conversion has flipped the ascending direction of X, reverse
// all the vectors
if (!outputWS->dataX(0).empty() &&
(outputWS->dataX(0).front() > outputWS->dataX(0).back() ||
outputWS->dataX(m_numberOfSpectra / 2).front() >
outputWS->dataX(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
return;
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
const auto &X = outputWS->dataX(i);
auto &Y = outputWS->dataY(i);
auto &E = outputWS->dataE(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
*/
MatrixWorkspace_sptr
ConvertUnits::convertQuickly(API::MatrixWorkspace_const_sptr inputWS,
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
// create the output workspace
MatrixWorkspace_sptr outputWS = this->setupOutputWorkspace(inputWS);
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(inputWS) == "") {
commonBoundaries = WorkspaceHelpers::commonBoundaries(inputWS);
Russell Taylor
committed
// Only do the full check if the quick one passes
Russell Taylor
committed
// Calculate the new (common) X values
MantidVec::iterator iter;
for (iter = outputWS->dataX(0).begin(); iter != outputWS->dataX(0).end();
++iter) {
*iter = factor * std::pow(*iter, power);
Russell Taylor
committed
}
auto xVals = outputWS->refX(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
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) {
MantidVec::iterator it;
for (it = outputWS->dataX(k).begin(); it != outputWS->dataX(k).end();
++it) {
*it = factor * std::pow(*it, power);
}
Russell Taylor
committed
}
// Convert the events themselves if necessary.
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();
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
return outputWS;
}
/** Get the L2, theta and efixed values for a workspace index
* @param outputUnit :: The output unit
* @param source :: The source of the instrument
* @param sample :: The sample of the instrument
* @param l1 :: The source to sample distance
* @param emode :: The energy mode
* @param ws :: The workspace
* @param thetaFunction :: The function to calculate twotheta
* @param wsIndex :: The workspace index
* @param efixed :: the returned fixed energy
* @param l2 :: The returned sample - detector distance
* @param twoTheta :: the returned two theta angle
* @returns true if lookup successful, false on error
*/
bool ConvertUnits::getDetectorValues(
Kernel::Unit_const_sptr outputUnit, Geometry::IComponent_const_sptr source,
Geometry::IComponent_const_sptr sample, double l1, int emode,
MatrixWorkspace_const_sptr ws,
function<double(const Geometry::IDetector &)> thetaFunction,
int64_t wsIndex, double &efixed, double &l2, double &twoTheta) {
try {
Geometry::IDetector_const_sptr det = ws->getDetector(wsIndex);
// Get the sample-detector distance for this detector (in metres)
if (!det->isMonitor()) {
l2 = det->getDistance(*sample);
// The scattering angle for this detector (in radians).
twoTheta = thetaFunction(*det);
// If an indirect instrument, try getting Efixed from the geometry
if (emode == 2) // indirect
{
if (efixed == EMPTY_DBL()) {
try {
// Get the parameter map
Geometry::Parameter_sptr par =
ws->constInstrumentParameters().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 */
}
}
}
} else // If this is a monitor then make l1+l2 = source-detector distance
// and twoTheta=0
{
l2 = det->getDistance(*source);
l2 = l2 - l1;
twoTheta = 0.0;
efixed = DBL_MIN;
// Energy transfer is meaningless for a monitor, so set l2 to 0.
if (outputUnit->unitID().find("DeltaE") != std::string::npos) {
l2 = 0.0;
}
}
} catch (Exception::NotFoundError &) {
return false;
}
return true;
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
*/
MatrixWorkspace_sptr
ConvertUnits::convertViaTOF(Kernel::Unit_const_sptr fromUnit,
API::MatrixWorkspace_const_sptr inputWS) {
Russell Taylor
committed
using namespace Geometry;
Russell Taylor
committed
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
Instrument_const_sptr instrument = inputWS->getInstrument();
Russell Taylor
committed
Kernel::Unit_const_sptr outputUnit = m_outputUnit;
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", inputWS->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 = inputWS->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
}
}
} else if (emode == 0 && efixedProp == EMPTY_DBL()) // Elastic
Michael Whitty
committed
{
efixedProp = 0.0;
}
std::vector<std::string> parameters =
inputWS->getInstrument()->getStringParameter("show-signed-theta");
bool bUseSignedVersion =
(!parameters.empty()) &&
find(parameters.begin(), parameters.end(), "Always") != parameters.end();
function<double(const IDetector &)> thetaFunction =
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
? bind(&MatrixWorkspace::detectorSignedTwoTheta, inputWS, _1)
: bind(&MatrixWorkspace::detectorTwoTheta, inputWS, _1);
// Perform Sanity Validation before creating workspace
double checkefixed = efixedProp;
double checkl2;
double checktwoTheta;
size_t checkIndex = 0;
if (getDetectorValues(outputUnit, source, sample, l1, emode, inputWS,
thetaFunction, checkIndex, checkefixed, checkl2,
checktwoTheta)) {
const double checkdelta = 0.0;
// copy the X values for the check
auto checkXValues = inputWS->readX(checkIndex);
// Convert the input unit to time-of-flight
Unit *checkFromUnit = fromUnit->clone();
Unit *checkOutputUnit = outputUnit->clone();
checkFromUnit->toTOF(checkXValues, emptyVec, l1, checkl2, checktwoTheta,
emode, checkefixed, checkdelta);
// Convert from time-of-flight to the desired unit
checkOutputUnit->fromTOF(checkXValues, emptyVec, l1, checkl2, checktwoTheta,
emode, checkefixed, checkdelta);
// Clear unit memory
delete checkFromUnit;
delete checkOutputUnit;
}
// create the output workspace
MatrixWorkspace_sptr outputWS = this->setupOutputWorkspace(inputWS);
EventWorkspace_sptr eventWS =
boost::dynamic_pointer_cast<EventWorkspace>(outputWS);
assert(static_cast<bool>(eventWS) == m_inputEvents); // Sanity check
// 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
// Now get the detector object for this histogram
double l2;
double twoTheta;
if (getDetectorValues(outputUnit, source, sample, l1, emode, outputWS,
thetaFunction, i, efixed, l2, twoTheta)) {
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;
// Convert the input unit to time-of-flight
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);
Russell Taylor
committed
}
Gigg, Martyn Anthony
committed
// Clear unit memory
delete localFromUnit;
delete localOutputUnit;
// 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();
if (emode == 1) {
//... direct efixed gather
if (efixedProp != EMPTY_DBL()) {
// set the Ei value in the run parameters
API::Run &run = outputWS->mutableRun();
run.addProperty<double>("Ei", efixedProp, true);
}
}
return outputWS;
}
/// 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
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);
if (!det->isMasked()) {
const MantidVec &XData = workspace->readX(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) {
EventWorkspace_sptr eventWS = boost::dynamic_pointer_cast<EventWorkspace>(WS);
bool isInputEvents = static_cast<bool>(eventWS);
size_t numberOfSpectra = WS->getNumberHistograms();
if (WorkspaceHelpers::commonBoundaries(WS) && !isInputEvents) {
// common boundaries and histogram
std::reverse(WS->dataX(0).begin(), WS->dataX(0).end());
std::reverse(WS->dataY(0).begin(), WS->dataY(0).end());
std::reverse(WS->dataE(0).begin(), WS->dataE(0).end());
Roman Tolchenov
committed
auto xVals = WS->refX(0);
for (size_t j = 1; j < numberOfSpectra; ++j) {
WS->setX(j, xVals);
std::reverse(WS->dataY(j).begin(), WS->dataY(j).end());
std::reverse(WS->dataE(j).begin(), WS->dataE(j).end());
if (j % 100 == 0)
interruption_point();
Russell Taylor
committed
}
// either events or ragged boundaries
int numberOfSpectra_i = static_cast<int>(numberOfSpectra);
Russell Taylor
committed
PARALLEL_FOR1(WS)
for (int j = 0; j < numberOfSpectra_i; ++j) {
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
eventWS->getSpectrum(j).reverse();
} else {
std::reverse(WS->dataX(j).begin(), WS->dataX(j).end());
std::reverse(WS->dataY(j).begin(), WS->dataY(j).end());
std::reverse(WS->dataE(j).begin(), WS->dataE(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)
const MantidVec &X0 = workspace->readX(i);
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) {
const MantidVec &X = workspace->readX(i);
const MantidVec &Y = workspace->readY(i);
const MantidVec &E = workspace->readE(i);
result->dataX(i).assign(X.begin() + first, X.end());
result->dataY(i).assign(Y.begin() + first, Y.end());
result->dataE(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';
// 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) {
const MantidVec &X = workspace->readX(j);
const MantidVec &Y = workspace->readY(j);
const MantidVec &E = workspace->readE(j);
MantidVec &Xnew = result->dataX(j);
MantidVec &Ynew = result->dataY(j);
MantidVec &Enew = result->dataE(j);
Russell Taylor
committed
int k;
for (k = 0; k < lastBins[j] - 1; ++k) {
Russell Taylor
committed
Xnew[k] = X[k];
Ynew[k] = Y[k];
Enew[k] = E[k];
}
// If necessary, add on some fake values to the end of the X array (Y&E
// will be zero)
if (k < maxBins) {
for (int l = k; l < maxBins; ++l) {
Xnew[l] = X[k] + 1 + l - k;
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->dataX(i)[j + 1] - outputWS->dataX(i)[j]);
outputWS->dataY(i)[j] = outputWS->dataY(i)[j] / width;
outputWS->dataE(i)[j] = outputWS->dataE(i)[j] / width;
Russell Taylor
committed
}
}
}
} // namespace Algorithm
} // namespace Mantid