Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/BinaryOperation.h"
#include "MantidAPI/FrameworkManager.h"
using namespace Mantid::API;
using namespace Mantid::Kernel;
namespace Mantid
{
namespace Algorithms
{
BinaryOperation::BinaryOperation() : API::PairedGroupAlgorithm(), m_progress(NULL) {}
BinaryOperation::~BinaryOperation()
{
if (m_progress) delete m_progress;
}
Russell Taylor
committed
* Defines input and output workspaces
*
*/
declareProperty(new WorkspaceProperty<MatrixWorkspace>(inputPropName1(),"",Direction::Input));
declareProperty(new WorkspaceProperty<MatrixWorkspace>(inputPropName2(),"",Direction::Input));
declareProperty(new WorkspaceProperty<MatrixWorkspace>(outputPropName(),"",Direction::Output));
Russell Taylor
committed
*
* @throw runtime_error Thrown if algorithm cannot execute
*/
void BinaryOperation::exec()
{
// get input workspace, dynamic cast not needed
MatrixWorkspace_const_sptr lhs = getProperty(inputPropName1());
MatrixWorkspace_const_sptr rhs = getProperty(inputPropName2());
// Check that the input workspace are compatible
if (!checkCompatibility(lhs,rhs))
Russell Taylor
committed
std::ostringstream ostr;
ostr << "The two workspaces are not compatible for algorithm " << this->name();
Russell Taylor
committed
g_log.error() << ostr.str() << std::endl;
Russell Taylor
committed
throw std::invalid_argument( ostr.str() );
// Make sure the left hand workspace is the larger (this is fine if we got past the compatibility checks)
if ( rhs->size() > lhs->size() )
{
MatrixWorkspace_const_sptr smaller = lhs;
lhs = rhs;
rhs = smaller;
}
Russell Taylor
committed
// We need to create a new workspace for the output if:
// (a) the output workspace hasn't been set to one of the input ones, or
// (b) it has been, but it's not the correct dimensions
if ( (out_work != lhs && out_work != rhs) || ( out_work == rhs && ( lhs->size() > rhs->size() ) ) )
Russell Taylor
committed
{
out_work = WorkspaceFactory::Instance().create(lhs);
Russell Taylor
committed
}
Steve Williams
committed
// only overridden for some operations (plus and minus at the time of writing)
Sofia Antony
committed
operateOnSample(lhs->sample(), rhs->sample(), out_work->mutableSample());
Steve Williams
committed
Russell Taylor
committed
// Initialise the progress reporting object
m_progress = new Progress(this,0.0,1.0,lhs->getNumberHistograms());
// There are now 4 possible scenarios, shown schematically here:
// xxx x xxx xxx xxx xxx xxx x
// xxx , xxx xxx , xxx , xxx x
// xxx , xxx xxx xxx xxx x
// So work out which one we have and call the appropriate function
if ( rhs->size() == 1 ) // Single value workspace
Russell Taylor
committed
{
doSingleValue(lhs,rhs,out_work);
Russell Taylor
committed
}
else if ( rhs->getNumberHistograms() == 1 ) // Single spectrum on rhs
Russell Taylor
committed
{
doSingleSpectrum(lhs,rhs,out_work);
Russell Taylor
committed
}
else if ( rhs->blocksize() == 1 ) // Single column on rhs
{
doSingleColumn(lhs,rhs,out_work);
}
else // The two are both 2D and should be the same size
{
do2D(lhs,rhs,out_work);
}
Russell Taylor
committed
// Call the virtual unit manipulation method
setOutputUnits(lhs,rhs,out_work);
// Assign the result to the output workspace property
Russell Taylor
committed
bool BinaryOperation::checkCompatibility(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs) const
Russell Taylor
committed
Unit_const_sptr lhs_unit = Unit_sptr();
Unit_const_sptr rhs_unit = Unit_sptr();
Roman Tolchenov
committed
if ( lhs->axes() && rhs->axes() ) // If one of these is a WorkspaceSingleValue then we don't want to check units match
{
lhs_unit = lhs->getAxis(0)->unit();
rhs_unit = rhs->getAxis(0)->unit();
}
Russell Taylor
committed
const std::string lhs_unitID = ( lhs_unit ? lhs_unit->unitID() : "" );
const std::string rhs_unitID = ( rhs_unit ? rhs_unit->unitID() : "" );
// Check the workspaces have the same units and distribution flag
Russell Taylor
committed
if ( lhs_unitID != rhs_unitID && lhs->blocksize() > 1 && rhs->blocksize() > 1 )
Russell Taylor
committed
g_log.error("The two workspace are not compatible because they have different units on the X axis.");
return false;
}
// Check the size compatibility
if (!checkSizeCompatibility(lhs,rhs))
{
Russell Taylor
committed
std::ostringstream ostr;
ostr<<"The sizes of the two workspaces are not compatible for algorithm "<<this->name();
Russell Taylor
committed
g_log.error() << ostr.str() << std::endl;
Russell Taylor
committed
throw std::invalid_argument( ostr.str() );
return true;
Roman Tolchenov
committed
/** Performs a simple check to see if the sizes of two workspaces are compatible for a binary operation
Russell Taylor
committed
* In order to be size compatible then the larger workspace
* must divide be the size of the smaller workspace leaving no remainder
* @param lhs the first workspace to compare
* @param rhs the second workspace to compare
* @retval true The two workspaces are size compatible
* @retval false The two workspaces are NOT size compatible
*/
bool BinaryOperation::checkSizeCompatibility(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs) const
Roman Tolchenov
committed
{
Russell Taylor
committed
const int lhsSize = lhs->size();
const int rhsSize = rhs->size();
// A SingleValueWorkspace matches anything
if ( rhsSize == 1 ) return true;
// The rhs must not be smaller than the lhs
if ( lhsSize < rhsSize ) return false;
//
// Otherwise they must match both ways, or horizontally or vertically with the other rhs dimension=1
if ( rhs->blocksize() == 1 && lhs->getNumberHistograms() == rhs->getNumberHistograms() ) return true;
// Past this point, we require the X arrays to match. Note this only checks the first spectrum
if ( !WorkspaceHelpers::matchingBins(lhs,rhs,true) ) return false;
const int rhsSpec = rhs->getNumberHistograms();
return ( lhs->blocksize() == rhs->blocksize() && ( rhsSpec==1 || lhs->getNumberHistograms() == rhsSpec ) );
Roman Tolchenov
committed
}
/** Called when the rhs operand is a single value.
* Loops over the lhs workspace calling the abstract binary operation function.
* @param lhs The workspace which is the left hand operand
* @param rhs The workspace which is the right hand operand
* @param out The result workspace
*/
void BinaryOperation::doSingleValue(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs,API::MatrixWorkspace_sptr out)
Roman Tolchenov
committed
{
Russell Taylor
committed
// Don't propate masking from the rhs here - it would be decidedly odd if the single value was masked
// Pull out the single value and its error
const double rhsY = rhs->readY(0)[0];
const double rhsE = rhs->readE(0)[0];
// Now loop over the spectra of the left hand side calling the virtual function
const int numHists = lhs->getNumberHistograms();
Russell Taylor
committed
PARALLEL_FOR3(lhs,rhs,out)
for (int i = 0; i < numHists; ++i)
{
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
Russell Taylor
committed
out->setX(i,lhs->refX(i));
performBinaryOperation(lhs->readX(i),lhs->readY(i),lhs->readE(i),rhsY,rhsE,out->dataY(i),out->dataE(i));
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
}
/** Called when the rhs operand is a single spectrum.
* Loops over the lhs workspace calling the abstract binary operation function.
* @param lhs The workspace which is the left hand operand
* @param rhs The workspace which is the right hand operand
* @param out The result workspace
*/
void BinaryOperation::doSingleSpectrum(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs,API::MatrixWorkspace_sptr out)
Roman Tolchenov
committed
{
Russell Taylor
committed
// Propagate any masking first or it could mess up the numbers
propagateMasking(rhs,out);
// Pull out the rhs spectrum
Russell Taylor
committed
const MantidVec& rhsY = rhs->readY(0);
const MantidVec& rhsE = rhs->readE(0);
// Now loop over the spectra of the left hand side calling the virtual function
const int numHists = lhs->getNumberHistograms();
Russell Taylor
committed
PARALLEL_FOR3(lhs,rhs,out)
for (int i = 0; i < numHists; ++i)
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
Russell Taylor
committed
out->setX(i,lhs->refX(i));
performBinaryOperation(lhs->readX(i),lhs->readY(i),lhs->readE(i),rhsY,rhsE,out->dataY(i),out->dataE(i));
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
/** Called when the rhs operand is a 2D workspace of single values.
* Loops over the workspaces calling the abstract binary operation function.
* @param lhs The workspace which is the left hand operand
* @param rhs The workspace which is the right hand operand
* @param out The result workspace
*/
void BinaryOperation::doSingleColumn(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs,API::MatrixWorkspace_sptr out)
Russell Taylor
committed
// Don't propate masking from the rhs here - it would be decidedly odd if the single bin was masked
// Now loop over the spectra of the left hand side pulling out the single value from each rhs 'spectrum'
// and then calling the virtual function
const int numHists = lhs->getNumberHistograms();
Russell Taylor
committed
PARALLEL_FOR3(lhs,rhs,out)
for (int i = 0; i < numHists; ++i)
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
const double rhsY = rhs->readY(i)[0];
const double rhsE = rhs->readE(i)[0];
Russell Taylor
committed
out->setX(i,lhs->refX(i));
performBinaryOperation(lhs->readX(i),lhs->readY(i),lhs->readE(i),rhsY,rhsE,out->dataY(i),out->dataE(i));
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
}
/** Called when the two workspaces are the same size.
* Loops over the workspaces extracting the appropriate spectra and calling the abstract binary operation function.
* @param lhs The workspace which is the left hand operand
* @param rhs The workspace which is the right hand operand
* @param out The result workspace
*/
void BinaryOperation::do2D(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs,API::MatrixWorkspace_sptr out)
{
Russell Taylor
committed
// Propagate any masking first or it could mess up the numbers
propagateMasking(rhs,out);
// Loop over the spectra calling the virtual function for each one
const int numHists = lhs->getNumberHistograms();
Russell Taylor
committed
PARALLEL_FOR3(lhs,rhs,out)
for (int i = 0; i < numHists; ++i)
Roman Tolchenov
committed
{
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
Russell Taylor
committed
out->setX(i,lhs->refX(i));
performBinaryOperation(lhs->readX(i),lhs->readY(i),lhs->readE(i),rhs->readY(i),rhs->readE(i),out->dataY(i),out->dataE(i));
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
Russell Taylor
committed
/** Copies any bin masking from the smaller/rhs input workspace to the output.
* Masks on the other input workspace are copied automatically by the workspace factory.
* @param rhs The workspace which is the right hand operand
* @param out The result workspace
*/
void BinaryOperation::propagateMasking(const API::MatrixWorkspace_const_sptr rhs, API::MatrixWorkspace_sptr out)
{
const int outHists = out->getNumberHistograms();
const int rhsHists = rhs->getNumberHistograms();
for (int i = 0; i < outHists; ++i)
{
// Copy over masks from the rhs, if any exist.
// If rhs is single spectrum, copy masks from that to all spectra in the output.
if ( rhs->hasMaskedBins((rhsHists==1) ? 0 : i) )
{
const MatrixWorkspace::MaskList & masks = rhs->maskedBins( (rhsHists==1) ? 0 : i );
MatrixWorkspace::MaskList::const_iterator it;
for (it = masks.begin(); it != masks.end(); ++it)
out->maskBin(i,it->first,it->second);
}
}
}
Russell Taylor
committed
} // namespace Algorithms
} // namespace Mantid