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::Algorithm(), 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
}
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);
}
// 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
Roman Tolchenov
committed
Unit_sptr lhs_unit = Unit_sptr();
Unit_sptr rhs_unit = Unit_sptr();
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();
}
// Check the workspaces have the same units and distribution flag
Russell Taylor
committed
if ( lhs_unit != rhs_unit && 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->dataX(i) = lhs->readX(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->dataX(i) = lhs->readX(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->dataX(i) = lhs->readX(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->dataX(i) = lhs->readX(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
/** This method is called if one of the selected workspaces for binary operation is a workspacegroup
* @param inputWSGrp pointer to the first workspace group
* @param prop a vector holding properties
* @retval false if selected workspace groups sizes not match
*/
bool BinaryOperation::processGroups(WorkspaceGroup_sptr inputWSGrp,const std::vector<Mantid::Kernel::Property*>&prop)
{
try
{
//getting the input workspace group names
const std::vector<std::string>& inputWSNames=inputWSGrp->getNames();
int nSize=inputWSNames.size();
//return if atleast one meber is not there in the group to process
if(nSize<2)
{
g_log.error()<<"Input WorkspaceGroup has no members to process "<<std::endl;
return false;
}
std::vector<std::string> lhsWSGrp;
std::vector<std::string> rhsWSGrp;
bool bgroupExecStatus=true;
//flag used for checking failure of all memebrs of the group
bool bgroupFailed=false;
WorkspaceGroup_sptr outWSGrp= WorkspaceGroup_sptr(new WorkspaceGroup);
Russell Taylor
committed
getGroupNames(prop,lhsWSGrp,rhsWSGrp);
//get member from each group and call binary execute on each member
IAlgorithm* alg = API::FrameworkManager::Instance().createAlgorithm(this->name() ,"",1);
if(!alg)
{
g_log.error()<<"createAlgorithm failed for "<<this->name()<<std::endl;
return false;
}
std::vector<std::string>::const_iterator lhsItr=lhsWSGrp.begin();
std::vector<std::string>::const_iterator rhsItr=rhsWSGrp.begin();
int nPeriod=0;
bool bStatus=0;
Russell Taylor
committed
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
if(lhsWSGrp.size()>1 && rhsWSGrp.size()>1)
{
if(isCompatibleSizes(lhsWSGrp,rhsWSGrp))
{
for (++lhsItr,++rhsItr;lhsItr!=lhsWSGrp.end();++lhsItr,++rhsItr)
{
++nPeriod;
setProperties(alg,prop,(*lhsItr),(*rhsItr),nPeriod,outWSGrp);
bStatus=alg->execute();
bgroupExecStatus=bgroupExecStatus&bStatus;
bgroupFailed=bgroupFailed || bStatus;
}
}
}
else if(lhsWSGrp.size()==1)//if LHS is not a group workspace and RHS is group workspace
{
for (++rhsItr;rhsItr!=rhsWSGrp.end();rhsItr++)
{ ++nPeriod;
setProperties(alg,prop,(*lhsItr),(*rhsItr),nPeriod,outWSGrp);
bStatus=alg->execute();
bgroupExecStatus=bgroupExecStatus&bStatus;
bgroupFailed=bgroupFailed || bStatus;
}
}
else if (rhsWSGrp.size()==1)//if RHS is not a group workspace and LHS is a group workspace
{
for (++lhsItr;lhsItr!=lhsWSGrp.end();lhsItr++)
{ ++nPeriod;
setProperties(alg,prop,(*lhsItr),(*rhsItr),nPeriod,outWSGrp);
bStatus=alg->execute();
bgroupExecStatus=bgroupExecStatus && bStatus;
bgroupFailed=bgroupFailed || bStatus;
}
}
if(bgroupExecStatus)
setExecuted(true);
if(!bgroupFailed)
{
std::vector<std::string> names=outWSGrp->getNames();
if(!names.empty())
AnalysisDataService::Instance().remove(names[0]);
}
Russell Taylor
committed
m_notificationCenter.postNotification(new FinishedNotification(this,this->isExecuted()));
return bStatus;
}
catch(Exception::NotFoundError& )
{
return false;
}
catch(std::exception&)
{
return false;
}
return true;
}
Russell Taylor
committed
/** This method sets properties for the algorithm
* @param alg pointer to the algorithm
* @param prop a vector holding properties
* @param lhsWSName name of the LHS workspace
* @param rhsWSName name of the RHS workspace
* @param nPeriod period number
* @param outWSGrp shared pointer to output workspace
*/
void BinaryOperation::setProperties(IAlgorithm* alg,const std::vector<Kernel::Property*>&prop,
const std::string& lhsWSName,const std::string& rhsWSName,int nPeriod,WorkspaceGroup_sptr outWSGrp)
{
std::string prevPropName("");
std::string currentPropName("");
Russell Taylor
committed
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
std::vector<Mantid::Kernel::Property*>::const_iterator propItr=prop.begin();
for (propItr=prop.begin();propItr!=prop.end();propItr++)
{
if(isWorkspaceProperty(*propItr))
{
if(isInputWorkspaceProperty(*propItr))
{
currentPropName=(*propItr)->name();
if(prevPropName.empty())
{ //LHS workspace
alg->setPropertyValue(currentPropName,lhsWSName);
}
else
{ // RHS workspace
if(currentPropName.compare(prevPropName))
{ alg->setPropertyValue(currentPropName,rhsWSName);
}
}
prevPropName=currentPropName;
}//end of if loop for input workspace property
if(isOutputWorkspaceProperty(*propItr))
{
std::string str=(*propItr)->value();
std::stringstream speriodNum;
speriodNum<<nPeriod;
std::string outWSName=str+"_"+speriodNum.str();
alg->setPropertyValue((*propItr)->name(),outWSName);
if(nPeriod==1){
if(outWSGrp)outWSGrp->add(str);
AnalysisDataService::Instance().addOrReplace(str,outWSGrp );
}
if(outWSGrp)
outWSGrp->add(outWSName);
}//end of if loop for output workspace property
}//end of if loop for workspace property
else
{
alg->setPropertyValue((*propItr)->name(),(*propItr)->name());
}
}//end of for loop for property vector
Russell Taylor
committed
}
Russell Taylor
committed
/** This method checks both LHS and RHS workspaces are of same size
* @param lhsWSGrpNames a vector holding names of LHS Workspace
* @param rhsWSGrpNames a vector holding names of RHS Workspace
* @retval true if selected workspace groups are of same size
* @retval false if selected workspace groups sizes not match
*/
bool BinaryOperation::isCompatibleSizes(const std::vector<std::string> &lhsWSGrpNames, const std::vector<std::string> &rhsWSGrpNames) const
{
//if both lhs and rhs workspaces are group workspaces ,check it's size
if(lhsWSGrpNames.size()!=rhsWSGrpNames.size())
{
g_log.error("Selected workspace groups are not of same size to perform binary operation.");
return false;
}
return true;
}
Russell Taylor
committed
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
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
/** This method iterates through property vector and returns LHS and RHS workspaces group names vectors
* @param prop vector holding the properties
* @param lhsWSGrpNames a vector holding names of LHS Workspace
* @param rhsWSGrpNames a vector holding names of RHS Workspace
*/
void BinaryOperation::getGroupNames(const std::vector<Kernel::Property*>&prop,
std::vector<std::string> &lhsWSGrpNames, std::vector<std::string> &rhsWSGrpNames) const
{
std::vector<Mantid::Kernel::Property*>::const_iterator itr;
std::string prevPropName("");
std::string currentPropName("");
for (itr=prop.begin();itr!=prop.end();itr++)
{
if(isWorkspaceProperty(*itr))
{
if(isInputWorkspaceProperty(*itr))
{
currentPropName=(*itr)->name();
std::string wsName=(*itr)->value();
Workspace_sptr wsPtr=AnalysisDataService::Instance().retrieve(wsName);
WorkspaceGroup_sptr wsGrpSptr=boost::dynamic_pointer_cast<WorkspaceGroup>(wsPtr);
if(prevPropName.empty())
{ //LHSWorkspace
if(wsGrpSptr)
lhsWSGrpNames=wsGrpSptr->getNames();
else
lhsWSGrpNames.push_back(wsName);
}
else
{
if(currentPropName.compare(prevPropName))
{ //second workspace("RHSWorkSpace")
if(wsGrpSptr)
rhsWSGrpNames=wsGrpSptr->getNames();
else
rhsWSGrpNames.push_back(wsName);
}
}
prevPropName=currentPropName;
}//end of if loop for input workspace property iteration
}//end of if loop for workspace property iteration
} //end of for loop for property iteration
}
Russell Taylor
committed
} // namespace Algorithms
} // namespace Mantid