Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/ConvertUnits.h"
Russell Taylor
committed
#include "MantidAPI/WorkspaceValidators.h"
#include "MantidAPI/AlgorithmFactory.h"
#include "MantidKernel/UnitFactory.h"
Russell Taylor
committed
#include "MantidDataObjects/Workspace2D.h"
Russell Taylor
committed
#include <cfloat>
#include <iostream>
namespace Mantid
{
namespace Algorithms
{
// Register with the algorithm factory
DECLARE_ALGORITHM(ConvertUnits)
using namespace Kernel;
using namespace API;
Russell Taylor
committed
using namespace DataObjects;
// Get a reference to the logger
Logger& ConvertUnits::g_log = Logger::get("ConvertUnits");
/// Default constructor
ConvertUnits::ConvertUnits() : Algorithm()
{
}
/// Destructor
ConvertUnits::~ConvertUnits()
{
}
/// Initialisation method
void ConvertUnits::init()
{
Russell Taylor
committed
CompositeValidator<> *wsValidator = new CompositeValidator<>;
wsValidator->add(new WorkspaceUnitValidator<>);
wsValidator->add(new HistogramValidator<>);
Roman Tolchenov
committed
declareProperty(new WorkspaceProperty<API::MatrixWorkspace>("InputWorkspace","",Direction::Input,wsValidator));
declareProperty(new WorkspaceProperty<API::MatrixWorkspace>("OutputWorkspace","",Direction::Output));
Russell Taylor
committed
// Extract the current contents of the UnitFactory to be the allowed values of the Target property
declareProperty("Target","",new ListValidator(UnitFactory::Instance().getKeys()) );
declareProperty("Emode",0,new BoundedValidator<int>(0,2));
BoundedValidator<double> *mustBePositive = new BoundedValidator<double>();
mustBePositive->setLower(0.0);
declareProperty("Efixed",0.0,mustBePositive);
Russell Taylor
committed
declareProperty("AlignBins",false);
}
/** 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
*/
void ConvertUnits::exec()
{
Russell Taylor
committed
// Get the workspaces
Roman Tolchenov
committed
API::MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
API::MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
// Check that the input workspace doesn't already have the desired unit.
// If it does, just set the output workspace to point to the input one.
Kernel::Unit_sptr inputUnit = inputWS->getAxis(0)->unit();
const std::string targetUnit = getPropertyValue("Target");
if ( inputUnit->unitID() == targetUnit )
{
g_log.information() << "Input workspace already has target unit (" << targetUnit
<< "), so just pointing the output workspace property to the input workspace." << std::endl;
setProperty("OutputWorkspace",boost::const_pointer_cast<MatrixWorkspace>(inputWS));
return;
}
// If input and output workspaces are not the same, create a new workspace for the output
if (outputWS != inputWS )
{
outputWS = WorkspaceFactory::Instance().create(inputWS);
setProperty("OutputWorkspace",outputWS);
}
Russell Taylor
committed
// Set the final unit that our output workspace will have
Russell Taylor
committed
Kernel::Unit_const_sptr outputUnit = outputWS->getAxis(0)->unit() = UnitFactory::Instance().create(targetUnit);
Russell Taylor
committed
// Check whether the Y data of the input WS is dimensioned and set output WS flag to be same
Russell Taylor
committed
const bool distribution = outputWS->isDistribution(inputWS->isDistribution());
const unsigned int size = inputWS->blocksize();
Russell Taylor
committed
// Calculate the number of spectra in this workspace
const int numberOfSpectra = inputWS->size() / size;
int iprogress_step = numberOfSpectra / 100;
if (iprogress_step == 0) iprogress_step = 1;
Russell Taylor
committed
// Loop over the histograms (detector spectra)
for (int i = 0; i < numberOfSpectra; ++i) {
Russell Taylor
committed
Russell Taylor
committed
// Take the bin width dependency out of the Y & E data
if (distribution)
{
for (unsigned int j = 0; j < size; ++j)
{
const double width = std::abs( inputWS->dataX(i)[j+1] - inputWS->dataX(i)[j] );
outputWS->dataY(i)[j] = inputWS->dataY(i)[j]*width;
outputWS->dataE(i)[j] = inputWS->dataE(i)[j]*width;
}
}
else
{
// Just copy over
outputWS->dataY(i) = inputWS->dataY(i);
Russell Taylor
committed
outputWS->dataE(i) = inputWS->dataE(i);
Russell Taylor
committed
}
// Copy over the X data (no copying will happen if the two workspaces are the same)
outputWS->dataX(i) = inputWS->dataX(i);
{
progress( double(i)/numberOfSpectra/2 );
interruption_point();
}
Russell Taylor
committed
}
Russell Taylor
committed
// Check whether there is a quick conversion available
double factor, power;
Russell Taylor
committed
if ( inputUnit->quickConversion(*outputUnit,factor,power) )
Russell Taylor
committed
// If test fails, could also check whether a quick conversion in the opposite direction has been entered
{
convertQuickly(numberOfSpectra,outputWS,factor,power);
}
else
{
Russell Taylor
committed
convertViaTOF(numberOfSpectra,inputUnit,outputWS);
Russell Taylor
committed
}
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).size() && ( outputWS->dataX(0).front() > outputWS->dataX(0).back()
|| outputWS->dataX(numberOfSpectra/2).front() > outputWS->dataX(numberOfSpectra/2).back() ) )
Russell Taylor
committed
{
this->reverse(outputWS);
}
Roman Tolchenov
committed
Russell Taylor
committed
// Rebin the data to common bins if requested, and if necessary
bool alignBins = getProperty("AlignBins");
if (alignBins && !WorkspaceHelpers::commonBoundaries(outputWS))
{
outputWS = this->alignBins(outputWS);
setProperty("OutputWorkspace",outputWS);
}
Russell Taylor
committed
// If appropriate, put back the bin width division into Y/E.
if (distribution)
{
for (int i = 0; i < numberOfSpectra; ++i) {
// There must be good case for having a 'divideByBinWidth'/'normalise' algorithm...
for (unsigned int j = 0; j < size; ++j)
{
const double width = std::abs( outputWS->dataX(i)[j+1] - outputWS->dataX(i)[j] );
Roman Tolchenov
committed
outputWS->dataY(i)[j] = outputWS->dataY(i)[j]/width;
outputWS->dataE(i)[j] = outputWS->dataE(i)[j]/width;
Russell Taylor
committed
}
}
}
Russell Taylor
committed
Russell Taylor
committed
}
/** Convert the workspace units according to a simple output = a * (input^b) relationship
Russell Taylor
committed
* @param numberOfSpectra The number of Spectra
* @param outputWS the output workspace
* @param factor the conversion factor a to apply
* @param power the Power b to apply to the conversion
*/
Roman Tolchenov
committed
void ConvertUnits::convertQuickly(const int& numberOfSpectra, API::MatrixWorkspace_sptr outputWS, const double& factor, const double& power)
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;
if ( sameBins.isValid(outputWS) )
{
// Only do the full check if the quick one passes
if ( WorkspaceHelpers::commonBoundaries(outputWS) )
{
// Calculate the new (common) X values
std::vector<double>::iterator iter;
for (iter = outputWS->dataX(0).begin(); iter != outputWS->dataX(0).end(); ++iter)
{
*iter = factor * std::pow(*iter,power);
}
// If this is a Workspace2D then loop over the other spectra passing in the pointer
Workspace2D_sptr WS2D = boost::dynamic_pointer_cast<Workspace2D>(outputWS);
if (WS2D)
{
Histogram1D::RCtype xVals;
xVals.access() = outputWS->dataX(0);
for (int j = 1; j < numberOfSpectra; ++j)
{
WS2D->setX(j,xVals);
if ( j % 100 == 0)
{
progress( 0.5 + double(j)/numberOfSpectra/2 );
interruption_point();
}
Russell Taylor
committed
}
}
return;
}
}
// 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
for (int k = 0; k < numberOfSpectra; ++k) {
Russell Taylor
committed
std::vector<double>::iterator it;
Russell Taylor
committed
for (it = outputWS->dataX(k).begin(); it != outputWS->dataX(k).end(); ++it)
Russell Taylor
committed
{
*it = factor * std::pow(*it,power);
}
}
Russell Taylor
committed
return;
Russell Taylor
committed
}
/** Convert the workspace units using TOF as an intermediate step in the conversion
Russell Taylor
committed
* @param numberOfSpectra The number of Spectra
Russell Taylor
committed
* @param fromUnit The unit of the input workspace
* @param outputWS The output workspace
Russell Taylor
committed
*/
Roman Tolchenov
committed
void ConvertUnits::convertViaTOF(const int& numberOfSpectra, Kernel::Unit_const_sptr fromUnit, API::MatrixWorkspace_sptr outputWS)
Russell Taylor
committed
{
// Get a pointer to the instrument contained in the workspace
IInstrument_const_sptr instrument = outputWS->getInstrument();
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)
Russell Taylor
committed
Geometry::IObjComponent_const_sptr source = instrument->getSource();
Geometry::IObjComponent_const_sptr sample = instrument->getSample();
double l1;
Russell Taylor
committed
try
{
Russell Taylor
committed
l1 = source->getDistance(*sample);
g_log.debug() << "Source-sample distance: " << l1 << std::endl;
Russell Taylor
committed
}
catch (Exception::NotFoundError e)
{
g_log.error("Unable to calculate source-sample distance");
Russell Taylor
committed
throw Exception::InstrumentDefinitionError("Unable to calculate source-sample distance", outputWS->getTitle());
}
Russell Taylor
committed
const int notFailed = -99;
int failedDetectorIndex = notFailed;
// Not doing anything with the Y vector in to/fromTOF yet, so just pass empty vector
std::vector<double> emptyVec;
Russell Taylor
committed
// Loop over the histograms (detector spectra)
for (int i = 0; i < numberOfSpectra; ++i) {
Russell Taylor
committed
Russell Taylor
committed
/// @todo No implementation for any of these in the geometry yet so using properties
const int emode = getProperty("Emode");
const double efixed = getProperty("Efixed");
/// @todo Don't yet consider hold-off (delta)
const double delta = 0.0;
Russell Taylor
committed
try {
Russell Taylor
committed
// Now get the detector object for this histogram
Geometry::IDetector_const_sptr det = outputWS->getDetector(i);
// Get the sample-detector distance for this detector (in metres)
Russell Taylor
committed
double l2, twoTheta;
if ( ! det->isMonitor() )
{
Russell Taylor
committed
l2 = det->getDistance(*sample);
Russell Taylor
committed
// The scattering angle for this detector (in radians).
Russell Taylor
committed
twoTheta = outputWS->detectorTwoTheta(det);
Russell Taylor
committed
}
else // If this is a monitor then make l1+l2 = source-detector distance and twoTheta=0
{
Russell Taylor
committed
l2 = det->getDistance(*source);
Russell Taylor
committed
l2 = l2 - l1;
twoTheta = 0.0;
}
if (failedDetectorIndex != notFailed)
{
g_log.information() << "Unable to calculate sample-detector[" << failedDetectorIndex << "-" << i-1 << "] distance. Zeroing spectrum." << std::endl;
failedDetectorIndex = notFailed;
}
Russell Taylor
committed
// Convert the input unit to time-of-flight
Russell Taylor
committed
fromUnit->toTOF(outputWS->dataX(i),emptyVec,l1,l2,twoTheta,emode,efixed,delta);
// Convert from time-of-flight to the desired unit
Russell Taylor
committed
outputUnit->fromTOF(outputWS->dataX(i),emptyVec,l1,l2,twoTheta,emode,efixed,delta);
Russell Taylor
committed
} catch (Exception::NotFoundError e) {
// Get to here if exception thrown when calculating distance to detector
if (failedDetectorIndex == notFailed)
{
failedDetectorIndex = i;
}
outputWS->dataX(i).assign(outputWS->dataX(i).size(),0.0);
outputWS->dataY(i).assign(outputWS->dataY(i).size(),0.0);
outputWS->dataE(i).assign(outputWS->dataE(i).size(),0.0);
}
if ( i % 100 == 0)
{
progress( 0.5 + double(i)/numberOfSpectra/2 );
interruption_point();
}
} // loop over spectra
Russell Taylor
committed
if (failedDetectorIndex != notFailed)
{
g_log.information() << "Unable to calculate sample-detector[" << failedDetectorIndex << "-" << numberOfSpectra-1 << "] distance. Zeroing spectrum." << std::endl;
}
Russell Taylor
committed
}
Russell Taylor
committed
/// Calls Rebin as a sub-algorithm to align the bins
Roman Tolchenov
committed
API::MatrixWorkspace_sptr ConvertUnits::alignBins(API::MatrixWorkspace_sptr workspace)
Russell Taylor
committed
{
// Create a Rebin child algorithm
IAlgorithm_sptr childAlg = createSubAlgorithm("Rebin");
Roman Tolchenov
committed
childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", workspace);
Russell Taylor
committed
childAlg->setProperty<std::vector<double> >("params",this->calculateRebinParams(workspace));
// Now execute the sub-algorithm. Catch and log any error
try
{
childAlg->execute();
}
catch (std::runtime_error& err)
{
g_log.error("Unable to successfully run Rebinning sub-algorithm");
throw;
}
if ( ! childAlg->isExecuted() )
{
g_log.error("Unable to successfully run Rebinning sub-algorithm");
throw std::runtime_error("Unable to successfully run Rebinning sub-algorithm");
}
else
{
return childAlg->getProperty("OutputWorkspace");
}
}
/// The Rebin parameters should cover the full range of the converted unit, with the same number of bins
Roman Tolchenov
committed
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 int numSpec = workspace->getNumberHistograms();
for (int i = 0; i < numSpec; ++i)
{
try {
Russell Taylor
committed
Geometry::IDetector_const_sptr det = workspace->getDetector(i);
Russell Taylor
committed
if ( !det->isMasked() )
Russell Taylor
committed
{
const std::vector<double> &XData = workspace->readX(i);
Russell Taylor
committed
if ( XData.front() < XMin ) XMin = XData.front();
if ( XData.back() > XMax ) XMax = XData.back();
}
} catch (Exception::NotFoundError) {} //Do nothing
}
const double step = ( XMax - XMin ) / workspace->blocksize();
std::vector<double> retval;
retval.push_back(XMin);
retval.push_back(step);
retval.push_back(XMax);
return retval;
}
Roman Tolchenov
committed
void ConvertUnits::reverse(API::MatrixWorkspace_sptr WS)
Roman Tolchenov
committed
{
Russell Taylor
committed
const int numberOfSpectra = WS->getNumberHistograms();
Roman Tolchenov
committed
// Only do the full check if the quick one passes
if ( WorkspaceHelpers::commonBoundaries(WS) )
Roman Tolchenov
committed
{
std::reverse(WS->dataX(0).begin(),WS->dataX(0).end());
Russell Taylor
committed
std::reverse(WS->dataY(0).begin(),WS->dataY(0).end());
std::reverse(WS->dataE(0).begin(),WS->dataE(0).end());
Roman Tolchenov
committed
// If this is a Workspace2D then loop over the other spectra passing in the pointer
Workspace2D_sptr WS2D = boost::dynamic_pointer_cast<Workspace2D>(WS);
if (WS2D)
{
Histogram1D::RCtype xVals;
xVals.access() = WS->dataX(0);
for (int j = 1; j < numberOfSpectra; ++j)
{
WS2D->setX(j,xVals);
std::reverse(WS->dataY(j).begin(),WS->dataY(j).end());
std::reverse(WS->dataE(j).begin(),WS->dataE(j).end());
Russell Taylor
committed
if ( j % 100 == 0) interruption_point();
Roman Tolchenov
committed
}
}
}
else
{
Russell Taylor
committed
for (int j = 0; j < numberOfSpectra; ++j)
{
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());
if ( j % 100 == 0) interruption_point();
}
Roman Tolchenov
committed
}
}
} // namespace Algorithm
} // namespace Mantid