Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/BinaryOperation.h"
Janik Zikovsky
committed
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/EventList.h"
#include "MantidAPI/FrameworkManager.h"
Janik Zikovsky
committed
#include "MantidAPI/MemoryManager.h"
Janik Zikovsky
committed
#include "MantidAPI/WorkspaceProperty.h"
#include "MantidGeometry/IDetector.h"
Janik Zikovsky
committed
#include "MantidKernel/Timer.h"
using namespace Mantid::Geometry;
Janik Zikovsky
committed
using namespace Mantid::DataObjects;
BinaryOperation::BinaryOperation()
: API::PairedGroupAlgorithm(),
m_ClearRHSWorkspace(false),
Janik Zikovsky
committed
m_useHistogramForRhsEventWorkspace(false),
Janik Zikovsky
committed
m_do2D_even_for_SingleColumn_on_rhs(false),
Janik Zikovsky
committed
m_progress(NULL)
Janik Zikovsky
committed
{}
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));
Janik Zikovsky
committed
declareProperty(new PropertyWithValue<bool>("AllowDifferentNumberSpectra", false, Direction::Input),
"Allow workspaces to have different number of spectra and perform\n"
"operation on LHS spectra using matching detector IDs in RHS.\n");
Janik Zikovsky
committed
declareProperty(new PropertyWithValue<bool>("ClearRHSWorkspace", false, Direction::Input),
"Clear the RHS workspace as the operation is performed.\n"
"This can help prevent maxing out available memory for large workspaces.\n"
"This is ignored unless the RHS workspace is an EventWorkpsace.\n");
Janik Zikovsky
committed
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
Janik Zikovsky
committed
/** Executes the algorithm. Will call execEvent() if appropriate.
Russell Taylor
committed
*
* @throw runtime_error Thrown if algorithm cannot execute
*/
void BinaryOperation::exec()
{
// get input workspace, dynamic cast not needed
Janik Zikovsky
committed
m_lhs = getProperty(inputPropName1());
m_rhs = getProperty(inputPropName2());
Janik Zikovsky
committed
m_AllowDifferentNumberSpectra = getProperty("AllowDifferentNumberSpectra");
Janik Zikovsky
committed
// Cast to EventWorkspace pointers
m_elhs = boost::dynamic_pointer_cast<const EventWorkspace>(m_lhs);
m_erhs = boost::dynamic_pointer_cast<const EventWorkspace>(m_rhs);
Janik Zikovsky
committed
Janik Zikovsky
committed
// We can clear the RHS workspace if it is an event,
// and we are not doing mismatched spectra (in which case you might clear it too soon!)
// and lhs is not rhs.
// and out is not rhs.
m_ClearRHSWorkspace = getProperty("ClearRHSWorkspace");
if (m_ClearRHSWorkspace)
{
if (m_AllowDifferentNumberSpectra || (!m_erhs) || (m_rhs == m_lhs) || (m_out == m_rhs))
{
//std::cout << "m_ClearRHSWorkspace = false\n";
m_ClearRHSWorkspace = false;
}
}
Janik Zikovsky
committed
//Get the output workspace
m_out = getProperty(outputPropName());
m_eout = boost::dynamic_pointer_cast<EventWorkspace>(m_out);
// Make a check of what will be needed to setup the workspaces, based on the input types.
this->checkRequirements();
Janik Zikovsky
committed
Janik Zikovsky
committed
if (m_flipSides)
Janik Zikovsky
committed
//Flip the workspaces left and right
MatrixWorkspace_const_sptr temp = m_lhs;
m_lhs = m_rhs;
m_rhs = temp;
Janik Zikovsky
committed
EventWorkspace_const_sptr etemp = m_elhs;
m_elhs = m_erhs;
m_erhs = etemp;
Janik Zikovsky
committed
Janik Zikovsky
committed
// Check that the input workspace are compatible
if (!checkCompatibility(m_lhs,m_rhs))
{
std::ostringstream ostr;
ostr << "The two workspaces are not compatible for algorithm " << this->name();
g_log.error() << ostr.str() << std::endl;
throw std::invalid_argument( ostr.str() );
}
Janik Zikovsky
committed
//Is the output going to be an EventWorkspace?
if (m_keepEventWorkspace)
{
//The output WILL be EventWorkspace (this implies lhs is EW or rhs is EW + it gets flipped)
if (!m_elhs)
throw std::runtime_error("BinaryOperation:: the output was set to be an EventWorkspace (m_keepEventWorkspace == true), but the lhs is not an EventWorkspace. There must be a mistake in the algorithm. Contact the developers.");
if (m_out == m_lhs)
{
Janik Zikovsky
committed
//Will be modifying the EventWorkspace in-place on the lhs. Good.
Janik Zikovsky
committed
if (!m_eout)
throw std::runtime_error("BinaryOperation:: the output was set to be lhs, and to be an EventWorkspace (m_keepEventWorkspace == true), but the output is not an EventWorkspace. There must be a mistake in the algorithm. Contact the developers.");
}
else
{
//You HAVE to copy the data from lhs to to the output!
//Create a copy of the lhs workspace
m_eout = boost::dynamic_pointer_cast<EventWorkspace>(API::WorkspaceFactory::Instance().create("EventWorkspace", m_elhs->getNumberHistograms(), 2, 1));
//Copy geometry, spectra map, etc. over.
API::WorkspaceFactory::Instance().initializeFromParent(m_elhs, m_eout, false);
//And we copy all the events from the lhs
m_eout->copyDataFrom( *m_elhs );
//Make sure m_out still points to the same as m_eout;
m_out = boost::dynamic_pointer_cast<API::MatrixWorkspace>(m_eout);
}
//Always clear the MRUs.
m_eout->clearMRU();
if (m_elhs) m_elhs->clearMRU();
if (m_erhs) m_erhs->clearMRU();
}
else
Russell Taylor
committed
{
Janik Zikovsky
committed
// ---- Output will be WS2D -------
// 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) output WAS == rhs, but it has been flipped for whatever reason (incorrect size)
// meaning that it now points to lhs (which we don't want to change)
// so we need to copy lhs into m_out.
if ( (m_out != m_lhs && m_out != m_rhs) ||
( m_out == m_lhs && ( m_flipSides ) ) )
{
Janik Zikovsky
committed
// Make sure to delete anything that might be in the output name.
if (AnalysisDataService::Instance().doesExist(getPropertyValue(outputPropName() )))
AnalysisDataService::Instance().remove(getPropertyValue(outputPropName() ));
Janik Zikovsky
committed
m_out = WorkspaceFactory::Instance().create(m_lhs);
}
Russell Taylor
committed
}
Steve Williams
committed
Janik Zikovsky
committed
Steve Williams
committed
// only overridden for some operations (plus and minus at the time of writing)
Janik Zikovsky
committed
operateOnRun(m_lhs->run(), m_rhs->run(), m_out->mutableRun());
Steve Williams
committed
Russell Taylor
committed
// Initialise the progress reporting object
Janik Zikovsky
committed
m_progress = new Progress(this,0.0,1.0,m_lhs->getNumberHistograms());
Janik Zikovsky
committed
// 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
Janik Zikovsky
committed
// Single value workspace on the right : if it is an EventWorkspace with 1 spectrum, 1 bin, it is treated as a scalar
if ( (m_rhs->size() == 1) && !m_do2D_even_for_SingleColumn_on_rhs )
Russell Taylor
committed
{
Janik Zikovsky
committed
doSingleValue(); //m_lhs,m_rhs,m_out
Russell Taylor
committed
}
Janik Zikovsky
committed
else if ( m_rhs->getNumberHistograms() == 1 ) // Single spectrum on rhs
Russell Taylor
committed
{
Janik Zikovsky
committed
doSingleSpectrum();
Russell Taylor
committed
}
Janik Zikovsky
committed
// Single column on rhs; if the RHS is an event workspace with one bin, it is treated as a scalar.
else if ( (m_rhs->blocksize() == 1) && !m_do2D_even_for_SingleColumn_on_rhs )
Janik Zikovsky
committed
m_indicesToMask.reserve(m_out->getNumberHistograms());
doSingleColumn();
Janik Zikovsky
committed
else // The two are both 2D and should be the same size (except if LHS is an event workspace)
Janik Zikovsky
committed
m_indicesToMask.reserve(m_out->getNumberHistograms());
Janik Zikovsky
committed
bool mismatchedSpectra =(m_AllowDifferentNumberSpectra && (m_rhs->getNumberHistograms() != m_lhs->getNumberHistograms()));
do2D(mismatchedSpectra);
Janik Zikovsky
committed
Janik Zikovsky
committed
applyMaskingToOutput(m_out);
setOutputUnits(m_lhs,m_rhs,m_out);
//For EventWorkspaces, redo the spectra to detector ID to make sure it is up-to-date. This may only be necessary for the Plus algorithm!
Janik Zikovsky
committed
if (m_eout) m_eout->generateSpectraMap();
Janik Zikovsky
committed
Russell Taylor
committed
// Assign the result to the output workspace property
Janik Zikovsky
committed
setProperty(outputPropName(),m_out);
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
/**
* Execute a binary operation on events. Should be overridden.
Janik Zikovsky
committed
* @param lhs :: left-hand event workspace
* @param rhs :: right-hand event workspace
Janik Zikovsky
committed
void BinaryOperation::execEvent( DataObjects::EventWorkspace_const_sptr lhs, DataObjects::EventWorkspace_const_sptr rhs )
{
Janik Zikovsky
committed
//This should never happen
throw Exception::NotImplementedError("BinaryOperation::execEvent() is not implemented for this operation.");
}
Janik Zikovsky
committed
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
/**
* Return true if the two workspaces are compatible for this operation
* Virtual: will be overridden as needed.
Janik Zikovsky
committed
* @param lhs :: left-hand workspace to check
* @param rhs :: right-hand workspace to check
* @return flag for the compatibility to the two workspaces
*/
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;
Janik Zikovsky
committed
ostr<<"The sizes of the two workspaces " <<
"(" << lhs->getName() << ": " << lhs->getNumberHistograms() << " spectra, blocksize " << lhs->blocksize() << ")"
<< " and " <<
"(" << rhs->getName() << ": " << rhs->getNumberHistograms() << " spectra, blocksize " << rhs->blocksize() << ")"
<< " 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;
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
/** Return true if the two workspaces can be treated as event workspaces
Janik Zikovsky
committed
* for the binary operation. If so, execEvent() will be called.
* (e.g. Plus algorithm will concatenate event lists)
Janik Zikovsky
committed
* @param lhs :: left-hand event workspace to check
* @param rhs :: right-hand event workspace to check
Janik Zikovsky
committed
* @return false by default; will be overridden by specific algorithms
*/
bool BinaryOperation::checkEventCompatibility(const API::MatrixWorkspace_const_sptr lhs,const API::MatrixWorkspace_const_sptr rhs)
{
Janik Zikovsky
committed
return false;
}
//--------------------------------------------------------------------------------------------
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
Janik Zikovsky
committed
* @param lhs :: the first workspace to compare
* @param rhs :: the second workspace to compare
Russell Taylor
committed
* @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
{
const size_t lhsSize = lhs->size();
const size_t rhsSize = rhs->size();
Janik Zikovsky
committed
// A SingleValueWorkspace on the right matches anything
Russell Taylor
committed
if ( rhsSize == 1 ) return true;
// The rhs must not be smaller than the lhs
if ( lhsSize < rhsSize ) return false;
Janik Zikovsky
committed
//Did checkRequirements() tell us that the X histogram size did not matter?
if (!m_matchXSize)
//If so, only the vertical # needs to match
return (lhs->getNumberHistograms() == rhs->getNumberHistograms());
Russell Taylor
committed
// 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;
Janik Zikovsky
committed
return ( lhs->blocksize() == rhs->blocksize() && ( rhsSpec==1 || lhs->getNumberHistograms() == rhsSpec ) );
Roman Tolchenov
committed
}
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
/**
* Checks if the spectra at the given index of either input workspace is masked. If so then the output spectra has zeroed data
* and is also masked.
Janik Zikovsky
committed
* @param lhs :: A pointer to the left-hand operand
* @param rhs :: A pointer to the right-hand operand
* @param index :: The workspace index to check
* @param out :: A pointer to the output workspace
* @returns True if further processing is not required on the spectra, false if the binary operation should be performed.
*/
bool BinaryOperation::propagateSpectraMask(const API::MatrixWorkspace_const_sptr lhs, const API::MatrixWorkspace_const_sptr rhs,
{
bool continueOp(true);
IDetector_sptr det_lhs, det_rhs;
try
{
Janik Zikovsky
committed
det_lhs = lhs->getDetector(index);
det_rhs = rhs->getDetector(index);
}
catch(std::runtime_error &)
{
}
catch(std::domain_error &)
{
// try statement will throw a domain_error when the axis is not a spectra axis.
return continueOp;
}
if( (det_lhs && det_lhs->isMasked()) || ( det_rhs && det_rhs->isMasked()) )
{
Janik Zikovsky
committed
continueOp = false;
//Zero the output data and ensure that the output spectra is masked. The masking is done outside of this
//loop modiying the parameter map in a multithreaded loop requires too much locking
m_indicesToMask.push_back(index);
}
return continueOp;
}
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
// Disable optimization under the Visual Studio compiler
// Optimizing, in combination with openmp parallelization causes occasional miscalculations
// Re-enabled below, after the doSingleValue, doSingleSpectrum, doSingleColumn & do2D methods
#ifdef _MSC_VER
#pragma optimize( "", off )
#pragma warning(disable:4748) // This is about /Gs being irrelevant when not optimizing
#endif
/**
* Called when the rhs operand is a single value.
Janik Zikovsky
committed
* Loops over the lhs workspace calling the abstract binary operation function with a single number as the rhs operand.
Janik Zikovsky
committed
void BinaryOperation::doSingleValue()
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
Janik Zikovsky
committed
const double rhsY = m_rhs->readY(0)[0];
const double rhsE = m_rhs->readE(0)[0];
// Now loop over the spectra of the left hand side calling the virtual function
Janik Zikovsky
committed
if (m_eout)
Janik Zikovsky
committed
// ---- The output is an EventWorkspace ------
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
m_out->setX(i, m_lhs->refX(i));
performEventBinaryOperation(m_eout->getEventList(i), rhsY, rhsE);
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
Janik Zikovsky
committed
else
{
// ---- Histogram Output -----
#ifndef __INTEL_COMPILER // THIS MUST BE TEMPORARY! Turn off openmp until we understand test failures
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
#endif
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
m_out->setX(i,m_lhs->refX(i));
performBinaryOperation(m_lhs->readX(i),m_lhs->readY(i),m_lhs->readE(i),rhsY,rhsE,m_out->dataY(i),m_out->dataE(i));
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
Russell Taylor
committed
}
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
Janik Zikovsky
committed
/** Called when the m_rhs operand is a 2D workspace of single values.
* Loops over the workspaces calling the abstract binary operation function with a single number as the m_rhs operand.
Janik Zikovsky
committed
void BinaryOperation::doSingleColumn()
Roman Tolchenov
committed
{
Janik Zikovsky
committed
// Don't propate masking from the m_rhs here - it would be decidedly odd if the single bin was masked
Russell Taylor
committed
Janik Zikovsky
committed
// Now loop over the spectra of the left hand side pulling m_out the single value from each m_rhs 'spectrum'
// and then calling the virtual function
Janik Zikovsky
committed
if (m_eout)
Janik Zikovsky
committed
// ---- The output is an EventWorkspace ------
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
const double rhsY = m_rhs->readY(i)[0];
const double rhsE = m_rhs->readE(i)[0];
//m_out->setX(i, m_lhs->refX(i)); //unnecessary - that was copied before.
if( propagateSpectraMask(m_lhs, m_rhs, i, m_out) )
{
performEventBinaryOperation(m_eout->getEventList(i), rhsY, rhsE);
}
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
Janik Zikovsky
committed
else
{
// ---- Histogram Output -----
#ifndef __INTEL_COMPILER // THIS MUST BE TEMPORARY! Turn off openmp until we understand test failures
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
#endif
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
const double rhsY = m_rhs->readY(i)[0];
const double rhsE = m_rhs->readE(i)[0];
m_out->setX(i,m_lhs->refX(i));
if( propagateSpectraMask(m_lhs, m_rhs, i, m_out) )
{
performBinaryOperation(m_lhs->readX(i),m_lhs->readY(i),m_lhs->readE(i),rhsY,rhsE,m_out->dataY(i),m_out->dataE(i));
}
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
Janik Zikovsky
committed
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
/** Called when the m_rhs operand is a single spectrum.
* Loops over the lhs workspace calling the abstract binary operation function.
Janik Zikovsky
committed
void BinaryOperation::doSingleSpectrum()
Russell Taylor
committed
Janik Zikovsky
committed
// Propagate any masking first or it could mess up the numbers
//TODO: Check if this works for event workspaces...
propagateBinMasks(m_rhs,m_out);
if (m_eout)
Janik Zikovsky
committed
// ----------- The output is an EventWorkspace -------------
if (m_erhs)
{
// -------- The rhs is ALSO an EventWorkspace --------
// Pull out the single eventList on the right
const EventList & rhs_spectrum = m_erhs->getEventList(0);
// Now loop over the spectra of the left hand side calling the virtual function
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
//m_out->setX(i,m_lhs->refX(i)); //unnecessary - that was copied before.
//Perform the operation on the event list on the output (== lhs)
performEventBinaryOperation(m_eout->getEventList(i), rhs_spectrum);
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
else
{
// -------- The rhs is a histogram ---------
// Pull m_out the m_rhs spectrum
const MantidVec& rhsX = m_rhs->readX(0);
const MantidVec& rhsY = m_rhs->readY(0);
const MantidVec& rhsE = m_rhs->readE(0);
// Now loop over the spectra of the left hand side calling the virtual function
#ifndef __INTEL_COMPILER // THIS MUST BE TEMPORARY! Turn off openmp until we understand test failures
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
#endif
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
//m_out->setX(i,m_lhs->refX(i)); //unnecessary - that was copied before.
//Perform the operation on the event list on the output (== lhs)
performEventBinaryOperation(m_eout->getEventList(i), rhsX, rhsY, rhsE);
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
}
else
{
// -------- The output is a histogram ----------
// (inputs can be EventWorkspaces, but their histogram representation
// will be used instead)
// Pull m_out the m_rhs spectrum
const MantidVec& rhsY = m_rhs->readY(0);
const MantidVec& rhsE = m_rhs->readE(0);
// Now loop over the spectra of the left hand side calling the virtual function
#ifndef __INTEL_COMPILER // THIS MUST BE TEMPORARY! Turn off openmp until we understand test failures
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
#endif
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
m_out->setX(i,m_lhs->refX(i));
performBinaryOperation(m_lhs->readX(i),m_lhs->readY(i),m_lhs->readE(i),rhsY,rhsE,m_out->dataY(i),m_out->dataE(i));
m_progress->report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
Janik Zikovsky
committed
//--------------------------------------------------------------------------------------------
/** Called when the two workspaces are the same size.
Janik Zikovsky
committed
* Loops over the workspaces extracting the appropriate spectra and calling the abstract binary operation function.
*
* @param mismatchedSpectra :: allow the # of spectra to not be the same. Will use the
* detector IDs to find the corresponding spectrum on RHS
Janik Zikovsky
committed
void BinaryOperation::do2D( bool mismatchedSpectra)
Janik Zikovsky
committed
BinaryOperationTable * table = NULL;
if (mismatchedSpectra)
table = BinaryOperation::buildBinaryOperationTable(m_lhs, m_rhs);
Russell Taylor
committed
// Propagate any masking first or it could mess up the numbers
Janik Zikovsky
committed
//TODO: Check if this works for event workspaces...
propagateBinMasks(m_rhs,m_out);
if (m_eout)
Roman Tolchenov
committed
{
Janik Zikovsky
committed
// ----------- The output is an EventWorkspace -------------
Janik Zikovsky
committed
if (m_erhs && !m_useHistogramForRhsEventWorkspace)
Janik Zikovsky
committed
{
Janik Zikovsky
committed
// ------------ The rhs is ALSO an EventWorkspace ---------------
Janik Zikovsky
committed
// Now loop over the spectra of each one calling the virtual function
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
Janik Zikovsky
committed
m_progress->report();
Janik Zikovsky
committed
if (mismatchedSpectra && table)
Janik Zikovsky
committed
{
Janik Zikovsky
committed
rhs_wi = (*table)[i];
if (rhs_wi < 0)
continue;
Janik Zikovsky
committed
}
Janik Zikovsky
committed
else
{
// Check for masking except when mismatched sizes
if(!propagateSpectraMask(m_lhs, m_rhs, i, m_out) )
continue;
}
//Reach here? Do the division
//Perform the operation on the event list on the output (== lhs)
performEventBinaryOperation(m_eout->getEventList(i), m_erhs->getEventList(rhs_wi));
Janik Zikovsky
committed
//Free up memory on the RHS if that is possible
if (m_ClearRHSWorkspace)
const_cast<EventList&>(m_erhs->getEventList(rhs_wi)).clear();
Janik Zikovsky
committed
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
else
{
Janik Zikovsky
committed
// -------- The rhs is a histogram, or we want to use the histogram representation of it ---------
Janik Zikovsky
committed
// Now loop over the spectra of each one calling the virtual function
#ifndef __INTEL_COMPILER // THIS MUST BE TEMPORARY! Turn off openmp until we understand test failures
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
#endif
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
Janik Zikovsky
committed
m_progress->report();
Janik Zikovsky
committed
if (mismatchedSpectra && table)
Janik Zikovsky
committed
{
Janik Zikovsky
committed
rhs_wi = (*table)[i];
if (rhs_wi < 0)
continue;
Janik Zikovsky
committed
}
Janik Zikovsky
committed
else
{
// Check for masking except when mismatched sizes
if(!propagateSpectraMask(m_lhs, m_rhs, i, m_out) )
continue;
}
Janik Zikovsky
committed
Janik Zikovsky
committed
//Reach here? Do the division
performEventBinaryOperation(m_eout->getEventList(i), m_rhs->readX(rhs_wi), m_rhs->readY(rhs_wi), m_rhs->readE(rhs_wi));
Janik Zikovsky
committed
//Free up memory on the RHS if that is possible
if (m_ClearRHSWorkspace)
const_cast<EventList&>(m_erhs->getEventList(rhs_wi)).clear();
Janik Zikovsky
committed
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
}
else
{
// -------- The output is a histogram ----------
// (inputs can be EventWorkspaces, but their histogram representation
// will be used instead)
// Now loop over the spectra of each one calling the virtual function
#ifndef __INTEL_COMPILER // THIS MUST BE TEMPORARY! Turn off openmp until we understand test failures
PARALLEL_FOR3(m_lhs,m_rhs,m_out)
#endif
for (int64_t i = 0; i < int64_t(numHists); ++i)
Janik Zikovsky
committed
{
PARALLEL_START_INTERUPT_REGION
Janik Zikovsky
committed
m_progress->report();
Janik Zikovsky
committed
m_out->setX(i,m_lhs->refX(i));
Janik Zikovsky
committed
if (mismatchedSpectra && table)
Janik Zikovsky
committed
{
Janik Zikovsky
committed
rhs_wi = (*table)[i];
if (rhs_wi < 0)
continue;
Janik Zikovsky
committed
}
Janik Zikovsky
committed
else
{
// Check for masking except when mismatched sizes
if(!propagateSpectraMask(m_lhs, m_rhs, i, m_out) )
continue;
}
//Reach here? Do the division
performBinaryOperation(m_lhs->readX(i),m_lhs->readY(i),m_lhs->readE(i),m_rhs->readY(rhs_wi),m_rhs->readE(rhs_wi),m_out->dataY(i),m_out->dataE(i));
Janik Zikovsky
committed
//Free up memory on the RHS if that is possible
if (m_ClearRHSWorkspace)
const_cast<EventList&>(m_erhs->getEventList(rhs_wi)).clear();
Janik Zikovsky
committed
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
Janik Zikovsky
committed
// Make sure we don't use outdated MRU
if (m_ClearRHSWorkspace)
m_erhs->clearMRU();
// End of optimization disabling under Visual Studio
#ifdef _MSC_VER
#pragma optimize( "", on )
#pragma warning(default:4748)
#endif
Janik Zikovsky
committed
//---------------------------------------------------------------------------------------------
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.
Janik Zikovsky
committed
* @param rhs :: The workspace which is the right hand operand
* @param out :: The result workspace
Russell Taylor
committed
*/
void BinaryOperation::propagateBinMasks(const API::MatrixWorkspace_const_sptr rhs, API::MatrixWorkspace_sptr out)
Peterson, Peter
committed
{
const int64_t outHists = out->getNumberHistograms();
const int64_t rhsHists = rhs->getNumberHistograms();
for (int64_t i = 0; i < outHists; ++i)
Russell Taylor
committed
{
// 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)
Janik Zikovsky
committed
{
Steve Williams
committed
out->flagMasked(i,it->first,it->second);
Janik Zikovsky
committed
}
Russell Taylor
committed
}
Peterson, Peter
committed
}
}
Janik Zikovsky
committed
//---------------------------------------------------------------------------------------------
/**
* Apply the requested masking to the output workspace
Janik Zikovsky
committed
* @param out :: The workspace to mask
Janik Zikovsky
committed
*/
void BinaryOperation::applyMaskingToOutput(API::MatrixWorkspace_sptr out)
{
int64_t nindices = static_cast<int64_t>(m_indicesToMask.size());
Janik Zikovsky
committed
ParameterMap &pmap = out->instrumentParameters();
PARALLEL_FOR1(out)
Janik Zikovsky
committed
PARALLEL_START_INTERUPT_REGION
try
{
IDetector_sptr det_out = out->getDetector(m_indicesToMask[i]);
PARALLEL_CRITICAL(BinaryOperation_masking)
{
pmap.addBool(det_out.get(), "masked", true);
}
}
catch(std::runtime_error &)
{
}
PARALLEL_END_INTERUPT_REGION
Janik Zikovsky
committed
PARALLEL_CHECK_INTERUPT_REGION
}
// ------- Default implementations of Event binary operations --------
/**
* Carries out the binary operation IN-PLACE on a single EventList,
Janik Zikovsky
committed
* with another EventList as the right-hand operand.
* The event lists simply get appended.
*
Janik Zikovsky
committed
* @param lhs :: Reference to the EventList that will be modified in place.
* @param rhs :: Const reference to the EventList on the right hand side.
Janik Zikovsky
committed
*/
void BinaryOperation::performEventBinaryOperation(DataObjects::EventList & lhs,
const DataObjects::EventList & rhs)
{
Janik Zikovsky
committed
throw Exception::NotImplementedError("BinaryOperation::performEventBinaryOperation() not implemented.");
}
/**
* Carries out the binary operation IN-PLACE on a single EventList,
Janik Zikovsky
committed
* with another (histogrammed) spectrum as the right-hand operand.
*
Janik Zikovsky
committed
* @param lhs :: Reference to the EventList that will be modified in place.
* @param rhsX :: The vector of rhs X bin boundaries
* @param rhsY :: The vector of rhs data values
* @param rhsE :: The vector of rhs error values
Janik Zikovsky
committed
*/
void BinaryOperation::performEventBinaryOperation(DataObjects::EventList & lhs,
const MantidVec& rhsX, const MantidVec& rhsY, const MantidVec& rhsE)
{
UNUSED_ARG(lhs);
UNUSED_ARG(rhsX);
UNUSED_ARG(rhsY);
UNUSED_ARG(rhsE);
Janik Zikovsky
committed
throw Exception::NotImplementedError("BinaryOperation::performEventBinaryOperation() not implemented.");
}
/**
* Carries out the binary operation IN-PLACE on a single EventList,
Janik Zikovsky
committed
* with a single (double) value as the right-hand operand
*
Janik Zikovsky
committed
* @param lhs :: Reference to the EventList that will be modified in place.
* @param rhsY :: The rhs data value
* @param rhsE :: The rhs error value
Janik Zikovsky
committed
*/
void BinaryOperation::performEventBinaryOperation(DataObjects::EventList & lhs,
const double& rhsY, const double& rhsE)
{
UNUSED_ARG(lhs);
UNUSED_ARG(rhsY);
UNUSED_ARG(rhsE);
Janik Zikovsky
committed
throw Exception::NotImplementedError("BinaryOperation::performEventBinaryOperation() not implemented.");
}
//---------------------------------------------------------------------------------------------
/**
* Get the type of operand from a workspace
Janik Zikovsky
committed
* @param ws :: workspace to check
Janik Zikovsky
committed
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
* @return OperandType describing what type of workspace it will be operated as.
*/
OperandType BinaryOperation::getOperandType(const API::MatrixWorkspace_const_sptr ws)
{
//An event workspace?
EventWorkspace_const_sptr ews = boost::dynamic_pointer_cast<const EventWorkspace>(ws);
if (ews)
return eEventList;
//If the workspace has no axes, then it is a WorkspaceSingleValue
if (!ws->axes())
return eNumber;
//TODO: Check if it is a single-colum one, then
// return Number;
//Otherwise, we take it as a histogram (workspace 2D)
return eHistogram;
}
//---------------------------------------------------------------------------------------------
/** Check what operation will be needed in order to apply the operation
* to these two types of workspaces. This function must be overridden
* and checked against all 9 possible combinations.
*
* Must set: m_matchXSize, m_flipSides, m_keepEventWorkspace
*/
void BinaryOperation::checkRequirements()
{
//In general, the X sizes have to match.
// (only for some EventWorkspace cases does it NOT matter...)
m_matchXSize = true;
Janik Zikovsky
committed
//Operations are not always commutative! Don't flip sides.
m_flipSides = false;
Janik Zikovsky
committed
//And in general, EventWorkspaces get turned to Workspace2D
m_keepEventWorkspace = false;
Janik Zikovsky
committed
// This will be set to true for Divide/Multiply
m_useHistogramForRhsEventWorkspace = false;
Janik Zikovsky
committed
//---------------------------------------------------------------------------------------------
Janik Zikovsky
committed
/** Build up an BinaryOperationTable for performing a binary operation
* e.g. lhs = (lhs + rhs)
* where the spectra in rhs are to go into lhs.
* This function looks to match the detector IDs in rhs to those in the lhs.
*
* @param lhs :: matrix workspace in which the operation is being done.
* @param rhs :: matrix workspace on the right hand side of the operand
* @return map from detector ID to workspace index for the RHS workspace.
* NULL if there is not a 1:1 mapping from detector ID to workspace index (e.g more than one detector per pixel).
Janik Zikovsky
committed
*/
Janik Zikovsky
committed
BinaryOperation::BinaryOperationTable * BinaryOperation::buildBinaryOperationTable(MatrixWorkspace_const_sptr lhs, MatrixWorkspace_const_sptr rhs)
Janik Zikovsky
committed
{
//An addition table is a list of pairs:
Janik Zikovsky
committed
// First int = workspace index in the EW being added
// Second int = workspace index to which it will be added in the OUTPUT EW. -1 if it should add a new entry at the end.
BinaryOperationTable * table = new BinaryOperationTable();
int rhs_nhist = static_cast<int>(rhs->getNumberHistograms());
int lhs_nhist = static_cast<int>(lhs->getNumberHistograms());
Janik Zikovsky
committed
Janik Zikovsky
committed
// Initialize the table; filled with -1 meaning no match
table->resize(lhs_nhist, -1);
Janik Zikovsky
committed
// We'll need maps from WI to Spectrum Number.
Janik Zikovsky
committed
Timer timer1;
//std::cout << timer1.elapsed() << " sec to getWorkspaceIndexToSpectrumMap\n";
Janik Zikovsky
committed
Janik Zikovsky
committed
rhs_det_to_wi = rhs->getDetectorIDToWorkspaceIndexMap(false);
//std::cout << timer1.elapsed() << " sec to getDetectorIDToWorkspaceIndexMap\n";
Janik Zikovsky
committed
PARALLEL_FOR_NO_WSP_CHECK()
for (int lhsWI = 0; lhsWI < lhs_nhist; lhsWI++)
Janik Zikovsky
committed
{
Janik Zikovsky
committed
bool done=false;
Janik Zikovsky
committed
// List of detectors on lhs side
const std::set<detid_t> & lhsDets = lhs->getSpectrum(lhsWI)->getDetectorIDs();
Janik Zikovsky
committed
Janik Zikovsky
committed
// ----------------- Matching Workspace Indices and Detector IDs --------------------------------------
//First off, try to match the workspace indices. Most times, this will be ok right away.
Russell Taylor
committed
int64_t rhsWI = lhsWI;
Janik Zikovsky
committed
{
// Get the detector IDs at that workspace index.
const std::set<detid_t> & rhsDets = rhs->getSpectrum(rhsWI)->getDetectorIDs();
Janik Zikovsky
committed
Janik Zikovsky
committed
//Checks that lhsDets is a subset of rhsDets
if (std::includes(rhsDets.begin(), rhsDets.end(), lhsDets.begin(), lhsDets.end()))
Janik Zikovsky
committed
{
//We found the workspace index right away. No need to keep looking
Janik Zikovsky
committed
(*table)[lhsWI] = rhsWI;
Janik Zikovsky
committed
done = true;
}
}
Janik Zikovsky
committed
Janik Zikovsky
committed
// ----------------- Scrambled Detector IDs with one Detector per Spectrum --------------------------------------
Janik Zikovsky
committed
if (!done && rhs_det_to_wi && (lhsDets.size() == 1))
Janik Zikovsky
committed
{
Janik Zikovsky
committed
//Didn't find it. Try to use the RHS map.
Janik Zikovsky
committed
Janik Zikovsky
committed
//First, we have to get the (single) detector ID of the LHS
std::set<detid_t>::const_iterator lhsDets_it = lhsDets.begin();
Janik Zikovsky
committed
Janik Zikovsky
committed
//Now we use the RHS map to find it. This only works if both the lhs and rhs have 1 detector per pixel
detid2index_map::iterator map_it = rhs_det_to_wi->find(lhs_detector_ID);
Janik Zikovsky
committed
if (map_it != rhs_det_to_wi->end())
Janik Zikovsky
committed
{
Janik Zikovsky
committed
rhsWI = map_it->second; //This is the workspace index in the RHS that matched lhs_detector_ID
Janik Zikovsky
committed
}
else
{
//Did not find it!
Janik Zikovsky
committed
rhsWI = -1; //Marker to mean its not in the LHS.
Janik Zikovsky
committed
// std::ostringstream mess;
// mess << "BinaryOperation: cannot find a RHS spectrum that contains the detectors in LHS workspace index " << lhsWI << "\n";
// throw std::runtime_error(mess.str());
Janik Zikovsky
committed
}
Janik Zikovsky
committed
(*table)[lhsWI] = rhsWI;
Janik Zikovsky
committed
done = true; //Great, we did it.
}
Janik Zikovsky
committed
// ----------------- LHS detectors are subset of RHS, which are Grouped --------------------------------------
Janik Zikovsky
committed
if (!done)
{
Janik Zikovsky
committed
Janik Zikovsky
committed
//Didn't find it? Now we need to iterate through the output workspace to
// match the detector ID.
// NOTE: This can be SUPER SLOW!
for (rhsWI=0; rhsWI < static_cast<int64_t>(rhs_nhist); rhsWI++)
Janik Zikovsky
committed
{
const std::set<detid_t> & rhsDets = rhs->getSpectrum(rhsWI)->getDetectorIDs();
Janik Zikovsky
committed
//Checks that lhsDets is a subset of rhsDets
if (std::includes(rhsDets.begin(), rhsDets.end(), lhsDets.begin(), lhsDets.end()))
Janik Zikovsky
committed
{
//This one is right. Now we can stop looking.
Janik Zikovsky
committed
(*table)[lhsWI] = rhsWI;
Janik Zikovsky
committed
done = true;
continue;
}
}
}
Janik Zikovsky
committed
// ------- Still nothing ! -----------