Large diffs are not rendered by default.

@@ -62,6 +62,11 @@ namespace Mantid
void rebinToOutput(const Geometry::Quadrilateral & inputQ, API::MatrixWorkspace_const_sptr inputWS,
const size_t i, const size_t j, API::MatrixWorkspace_sptr outputWS,
const std::vector<double> & verticalAxis);
/// Rebin the input quadrilateral to to output grid
void rebinToFractionalOutput(const Geometry::Quadrilateral & inputQ, API::MatrixWorkspace_const_sptr inputWS,
const size_t i, const size_t j, API::MatrixWorkspace_sptr outputWS,
const std::vector<double> & verticalAxis);

/// Find the intersect region on the output grid
bool getIntersectionRegion(API::MatrixWorkspace_const_sptr outputWS, const std::vector<double> & verticalAxis,
const Geometry::Quadrilateral & inputQ,
@@ -83,6 +83,11 @@ namespace Mantid
/// Get angles and calculate angular widths.
void getValuesAndWidths(API::MatrixWorkspace_const_sptr workspace);

/// Create the output workspace
API::MatrixWorkspace_sptr setUpOutputWorkspace(API::MatrixWorkspace_const_sptr inputWorkspace,
const std::vector<double> & binParams, std::vector<double>& newAxis);


/// E Mode
int m_emode;
/// EFixed has been provided
@@ -6,7 +6,7 @@ Convert a Workspace2D
#include "MantidAlgorithms/ConvertToMaskingWorkspace.h"
#include "MantidKernel/System.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidDataObjects/SpecialWorkspace2D.h"
#include "MantidDataObjects/MaskWorkspace.h"
#include "MantidAPI/WorkspaceProperty.h"

using namespace Mantid::Kernel;
@@ -36,7 +36,7 @@ namespace Algorithms
void ConvertToMaskingWorkspace::initDocs()
{
this->setWikiSummary("Convert a Workspace to a Masking workspace");
this->setOptionalMessage("Convert Workspace2D to a SpecailWorkspace2D.");
this->setOptionalMessage("Convert Workspace2D to a MaskWorkspace.");
}

void ConvertToMaskingWorkspace::init()
@@ -45,7 +45,7 @@ namespace Algorithms
this->declareProperty(new API::WorkspaceProperty<DataObjects::Workspace2D>("InputWorkspace", "", Direction::Input),
"Input Workspace2D. Must have instrument associated, and cannot be focussed.");

this->declareProperty(new API::WorkspaceProperty<DataObjects::SpecialWorkspace2D>("OutputWorkspace", "", Direction::Output),
this->declareProperty(new API::WorkspaceProperty<DataObjects::MaskWorkspace>("OutputWorkspace", "", Direction::Output),
"Output masking workspace.");

return;
@@ -64,7 +64,7 @@ namespace Algorithms
throw std::invalid_argument("Input workspace has not instrument set up");
}

DataObjects::SpecialWorkspace2D_sptr maskWS(new DataObjects::SpecialWorkspace2D(myinstrument));
DataObjects::MaskWorkspace_sptr maskWS(new DataObjects::MaskWorkspace(myinstrument));
g_log.debug() << "Output Masking Workspace has " << maskWS->getNumberHistograms() << " specs" << std::endl;
std::vector<detid_t> detids = myinstrument->getDetectorIDs();
g_log.debug() << "Instrument has " << detids.size() << " Detectors " << std::endl;
@@ -24,6 +24,7 @@ GetDetectorOffsets("InputW","OutputW",0.01,2.0,1.8,2.2,"output.cal")
#include "MantidAPI/IBackgroundFunction.h"
#include "MantidAPI/CompositeFunction.h"
#include "MantidDataObjects/OffsetsWorkspace.h"
#include "MantidDataObjects/MaskWorkspace.h"
#include <boost/math/special_functions/fpclassify.hpp>
#include <fstream>
#include <iomanip>
@@ -112,7 +113,7 @@ namespace Mantid
// Create the output OffsetsWorkspace
OffsetsWorkspace_sptr outputW(new OffsetsWorkspace(inputW->getInstrument()));
// Create the output MaskWorkspace
MatrixWorkspace_sptr maskWS(new SpecialWorkspace2D(inputW->getInstrument()));
MaskWorkspace_sptr maskWS(new MaskWorkspace(inputW->getInstrument()));
//To get the workspace index from the detector ID
detid2index_map * pixel_to_wi = maskWS->getDetectorIDToWorkspaceIndexMap(true);

@@ -189,6 +189,62 @@ namespace Mantid
}
}

/**
* Rebin the input quadrilateral to the output grid
* @param inputQ The input polygon
* @param inputWS The input workspace containing the input intensity values
* @param i The index in the vertical axis direction that inputQ references
* @param j The index in the horizontal axis direction that inputQ references
* @param outputWS A pointer to the output workspace that accumulates the data
* @param verticalAxis A vector containing the output vertical axis bin boundaries
*/
void Rebin2D::rebinToFractionalOutput(const Geometry::Quadrilateral & inputQ,
MatrixWorkspace_const_sptr inputWS,
const size_t i, const size_t j,
MatrixWorkspace_sptr outputWS,
const std::vector<double> & verticalAxis)
{
const MantidVec & X = outputWS->readX(0);
size_t qstart(0), qend(verticalAxis.size()-1), en_start(0), en_end(X.size() - 1);
if( !getIntersectionRegion(outputWS, verticalAxis, inputQ, qstart, qend, en_start, en_end)) return;

for( size_t qi = qstart; qi < qend; ++qi )
{
const double vlo = verticalAxis[qi];
const double vhi = verticalAxis[qi+1];
for( size_t ei = en_start; ei < en_end; ++ei )
{
const V2D ll(X[ei], vlo);
const V2D lr(X[ei+1], vlo);
const V2D ur(X[ei+1], vhi);
const V2D ul(X[ei], vhi);
const Quadrilateral outputQ(ll, lr, ur, ul);

try
{
ConvexPolygon overlap = intersectionByLaszlo(outputQ, inputQ);
const double weight = overlap.area()/inputQ.area();
double yValue = inputWS->readY(i)[j] * weight;
double eValue = inputWS->readE(i)[j] * weight;
const double overlapWidth = overlap.largestX() - overlap.smallestX();
if(inputWS->isDistribution())
{
yValue *= overlapWidth;
eValue *= overlapWidth;
}
eValue = eValue*eValue;
PARALLEL_CRITICAL(overlap)
{
outputWS->dataY(qi)[ei] += yValue;
outputWS->dataE(qi)[ei] += eValue;
}
}
catch(Geometry::NoIntersectionException &)
{}
}
}
}

/**
* Find the possible region of intersection on the output workspace for the given polygon
* @param outputWS A pointer to the output workspace
@@ -12,11 +12,15 @@ Converts a 2D workspace from units of spectrum number/energy transfer to the int
//----------------------------------------------------------------------
#include "MantidAlgorithms/SofQW3.h"
#include "MantidAlgorithms/SofQW.h"
#include "MantidAPI/NumericAxis.h"
#include "MantidAPI/SpectraAxis.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
#include "MantidGeometry/Math/LaszloIntersection.h"
#include "MantidGeometry/Math/Quadrilateral.h"
#include "MantidGeometry/Math/Vertex2D.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidKernel/VectorHelper.h"

namespace Mantid
{
@@ -97,9 +101,9 @@ namespace Algorithms
throw std::invalid_argument("The input workspace must have common binning across all spectra");
}

MatrixWorkspace_sptr outputWS = SofQW::setUpOutputWorkspace(inputWS,
MatrixWorkspace_sptr outputWS = this->setUpOutputWorkspace(inputWS,
getProperty("QAxisBinning"),
m_Qout);
m_Qout);
g_log.notice() << "Workspace type: " << outputWS->id() << std::endl;
setProperty("OutputWorkspace", outputWS);
const size_t nEnergyBins = inputWS->blocksize();
@@ -158,14 +162,14 @@ namespace Algorithms
const V2D ul(dE_j, this->calculateQ(efixed, dE_j, thetaUpper, phiUpper));
Quadrilateral inputQ = Quadrilateral(ll, lr, ur, ul);

this->rebinToOutput(inputQ, inputWS, i, j, outputWS, m_Qout);
this->rebinToFractionalOutput(inputQ, inputWS, i, j, outputWS, m_Qout);
}

PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION

normaliseOutput(outputWS, inputWS);
this->normaliseOutput(outputWS, inputWS);
}

/**
@@ -412,6 +416,50 @@ namespace Algorithms
}
}

/** Creates the output workspace, setting the axes according to the input binning parameters
* @param[in] inputWorkspace The input workspace
* @param[in] binParams The bin parameters from the user
* @param[out] newAxis The 'vertical' axis defined by the given parameters
* @return A pointer to the newly-created workspace
*/
API::MatrixWorkspace_sptr SofQW3::setUpOutputWorkspace(API::MatrixWorkspace_const_sptr inputWorkspace,
const std::vector<double> & binParams, std::vector<double>& newAxis)
{
// Create vector to hold the new X axis values
MantidVecPtr xAxis;
xAxis.access() = inputWorkspace->readX(0);
const int xLength = static_cast<int>(xAxis->size());
// Create a vector to temporarily hold the vertical ('y') axis and populate that
const int yLength = static_cast<int>(VectorHelper::createAxisFromRebinParams(binParams,newAxis));

// Create the output workspace
MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create("RebinnedOutput", yLength - 1, xLength, xLength - 1);
WorkspaceFactory::Instance().initializeFromParent(inputWorkspace,
outputWorkspace, true);

// Create a numeric axis to replace the default vertical one
Axis* const verticalAxis = new NumericAxis(yLength);
outputWorkspace->replaceAxis(1, verticalAxis);

// Now set the axis values
for (int i = 0; i < yLength - 1; ++i)
{
outputWorkspace->setX(i, xAxis);
verticalAxis->setValue(i, newAxis[i]);
}
// One more to set on the 'y' axis
verticalAxis->setValue(yLength - 1, newAxis[yLength - 1]);

// Set the axis units
verticalAxis->unit() = UnitFactory::Instance().create("MomentumTransfer");
verticalAxis->title() = "|Q|";

// Set the X axis title (for conversion to MD)
outputWorkspace->getAxis(0)->title() = "Energy transfer";

return outputWorkspace;
}

} // namespace Mantid
} // namespace Algorithms

@@ -9,7 +9,7 @@

#include "MantidAlgorithms/ConvertToMaskingWorkspace.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"
#include "MantidDataObjects/SpecialWorkspace2D.h"
#include "MantidDataObjects/MaskWorkspace.h"

using namespace Mantid;
using namespace Mantid::Algorithms;
@@ -90,7 +90,7 @@ class ConvertToMaskingWorkspaceTest : public CxxTest::TestSuite
wsexist = false;
TS_ASSERT(wsexist);

DataObjects::SpecialWorkspace2D_sptr maskWS = boost::dynamic_pointer_cast<DataObjects::SpecialWorkspace2D>(outputWS);
DataObjects::MaskWorkspace_sptr maskWS = boost::dynamic_pointer_cast<DataObjects::MaskWorkspace>(outputWS);
wsexist = true;
if (!maskWS)
wsexist = false;
@@ -29,6 +29,11 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite
TS_ASSERT(alg.isInitialized());
}

void test_genTimeOnly()
{

}


};

@@ -8,6 +8,16 @@
#include <iomanip>

#include "MantidAlgorithms/GeneratePeaks.h"
#include "MantidDataObjects/TableWorkspace.h"
#include "MantidAPI/Column.h"
#include "MantidAPI/TableRow.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceFactory.h"

#include "MantidAPI/FunctionDomain.h"
#include "MantidAPI/FunctionValues.h"
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/FunctionDomain1D.h"

using namespace Mantid;
using namespace Mantid::Algorithms;
@@ -27,8 +37,267 @@ class GeneratePeaksTest : public CxxTest::TestSuite
GeneratePeaks alg;
TS_ASSERT_THROWS_NOTHING(alg.initialize());
TS_ASSERT(alg.isInitialized());

DataObjects::TableWorkspace_sptr peakparmsws = createTestPeakParameters();

TS_ASSERT_EQUALS(peakparmsws->rowCount(), 4);

API::Column_sptr col0 = peakparmsws->getColumn("spectrum");
API::Column_sptr col1 = peakparmsws->getColumn("centre");

TS_ASSERT_EQUALS((*col0)[2], 2);
TS_ASSERT_DELTA((*col1)[1], 8.0, 1.0E-8);

return;
}

/*
* Test to use user-provided binning parameters
*/
void test_UserBinningParameters()
{
// 1. Create input
DataObjects::TableWorkspace_sptr peakparmsws = createTestPeakParameters();

GeneratePeaks alg;
alg.initialize();

// 3. Set value
TS_ASSERT_THROWS_NOTHING(alg.setProperty("PeakParametersWorkspace", peakparmsws));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("PeakFunction", "Gaussian"));
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("BinningParameters", "0.0, 0.01, 10.0"));
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("OutputWorkspace", "Test01WS"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GeneratePeaks", true));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateBackground", false));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxAllowedChi2", 100.0));

// 4. Execute
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());

// 5. Get result
API::MatrixWorkspace_const_sptr peaksws =
boost::dynamic_pointer_cast<API::MatrixWorkspace>(AnalysisDataService::Instance().retrieve("Test01WS"));
TS_ASSERT(peaksws);

// 6. Check result
TS_ASSERT_EQUALS(peaksws->getNumberHistograms(), 2);

// a) Peak 0:
MantidVec p0_x = peaksws->readX(0);
MantidVec p0_y = peaksws->readY(0);
TS_ASSERT_DELTA(p0_x[200], 2.0, 1.0E-8);
TS_ASSERT_DELTA(p0_y[200], 5.0, 1.0E-4);

TS_ASSERT_DELTA(p0_x[201], 2.01, 1.0E-8);
TS_ASSERT_DELTA(p0_y[201], 4.96546, 1.0E-4);

// b) Peak 1:
TS_ASSERT_DELTA(p0_x[800], 8.0, 1.0E-8);
TS_ASSERT_DELTA(p0_y[800], 10.0, 1.0E-4);

// c) Peak 2:
MantidVec p1_x = peaksws->readX(1);
MantidVec p1_y = peaksws->readY(1);
TS_ASSERT_DELTA(p1_x[400], 4.0, 1.0E-8);
TS_ASSERT_DELTA(p1_y[400], 20.0, 1.0E-4);

// 7. Spectrum map
spec2index_map *themap = peaksws->getSpectrumToWorkspaceIndexMap();
size_t index0 = (*themap)[0];
size_t index2 = (*themap)[2];
TS_ASSERT_EQUALS(index0, 0);
TS_ASSERT_EQUALS(index2, 1)

// 8. Clean
delete themap;
AnalysisDataService::Instance().remove("Test01WS");

return;
}

void test_FromInputWorkspace()
{
// 1. Create input
DataObjects::TableWorkspace_sptr peakparmsws = createTestPeakParameters();
API::MatrixWorkspace_sptr inputws = createTestInputWorkspace();

GeneratePeaks alg;
alg.initialize();

// 3. Set value
TS_ASSERT_THROWS_NOTHING(alg.setProperty("PeakParametersWorkspace", peakparmsws));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("PeakFunction", "Gaussian"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputws));
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("BinningParameters", "0.0, 0.01, 10.0"));
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("OutputWorkspace", "Test02WS"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GeneratePeaks", true));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateBackground", false));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxAllowedChi2", 100.0));

// 4. Execute
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());

// 5. Get result
API::MatrixWorkspace_const_sptr peaksws =
boost::dynamic_pointer_cast<API::MatrixWorkspace>(AnalysisDataService::Instance().retrieve("Test02WS"));
TS_ASSERT(peaksws);

// 6. Check result
TS_ASSERT_EQUALS(peaksws->getNumberHistograms(), 5);

// a) Peak 0:
MantidVec p0_x = peaksws->readX(0);
MantidVec p0_y = peaksws->readY(0);
TS_ASSERT_DELTA(p0_x[50], 2.0, 1.0E-8);
TS_ASSERT_DELTA(p0_y[50], 5.0, 1.0E-4);

TS_ASSERT_DELTA(p0_x[51], 2.02, 1.0E-8);
TS_ASSERT_DELTA(p0_y[51], 4.86327, 1.0E-4);

// b) Peak 1:
TS_ASSERT_DELTA(p0_x[350], 8.0, 1.0E-8);
TS_ASSERT_DELTA(p0_y[350], 10.0, 1.0E-4);

// c) Peak 2:
MantidVec p1_x = peaksws->readX(2);
MantidVec p1_y = peaksws->readY(2);
TS_ASSERT_DELTA(p1_x[150], 4.0, 1.0E-8);
TS_ASSERT_DELTA(p1_y[150], 20.0, 1.0E-4);

// 7. Spectrum map
spec2index_map *themap = peaksws->getSpectrumToWorkspaceIndexMap();
TS_ASSERT_EQUALS(themap->size(), 5);
size_t index0 = (*themap)[0];
size_t index2 = (*themap)[2];
TS_ASSERT_EQUALS(index0, 0);
TS_ASSERT_EQUALS(index2, 1)

return;
}

/*
* Test to use user-provided binning parameters
*/
void test_BackgroundOnly()
{
// 1. Create input
DataObjects::TableWorkspace_sptr peakparmsws = createTestPeakParameters();

GeneratePeaks alg;
alg.initialize();

// 3. Set value
TS_ASSERT_THROWS_NOTHING(alg.setProperty("PeakParametersWorkspace", peakparmsws));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("PeakFunction", "Gaussian"));
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("BinningParameters", "0.0, 0.01, 10.0"));
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("OutputWorkspace", "Test01WS"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GeneratePeaks", false));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateBackground", true));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxAllowedChi2", 100.0));

// 4. Execute
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());

// 5. Get result
API::MatrixWorkspace_const_sptr peaksws =
boost::dynamic_pointer_cast<API::MatrixWorkspace>(AnalysisDataService::Instance().retrieve("Test01WS"));
TS_ASSERT(peaksws);

// 6. Check result
TS_ASSERT_EQUALS(peaksws->getNumberHistograms(), 2);

// a) Peak 0:
MantidVec p0_x = peaksws->readX(0);
MantidVec p0_y = peaksws->readY(0);
TS_ASSERT_DELTA(p0_x[200], 2.0, 1.0E-8);
TS_ASSERT_DELTA(p0_y[200], 9.0, 1.0E-4);

// b) Peak 1:
TS_ASSERT_DELTA(p0_x[800], 8.0, 1.0E-8);
TS_ASSERT_DELTA(p0_y[800], 27.0, 1.0E-4);

// c) Peak 2:
MantidVec p1_x = peaksws->readX(1);
MantidVec p1_y = peaksws->readY(1);
TS_ASSERT_DELTA(p1_x[400], 4.0, 1.0E-8);
TS_ASSERT_DELTA(p1_y[400], 4.0, 1.0E-4);

// 7. Spectrum map
spec2index_map *themap = peaksws->getSpectrumToWorkspaceIndexMap();
size_t index0 = (*themap)[0];
size_t index2 = (*themap)[2];
TS_ASSERT_EQUALS(index0, 0);
TS_ASSERT_EQUALS(index2, 1)

AnalysisDataService::Instance().remove("Test01WS");
return;
}

/*
* Generate a TableWorkspace containing 3 peaks on 2 spectra
* spectra 0: center = 2.0, width = 0.2, height = 5, a0 = 1.0, a1 = 2.0, a2 = 0
* spectra 0: center = 8.0, width = 0.1, height = 10, a0 = 2.0, a1 = 1.0, a2 = 0
* spectra 2: center = 4.0, width = 0.4, height = 20, a0 = 4.0, a1 = 0.0, a2 = 0
*/
DataObjects::TableWorkspace_sptr createTestPeakParameters()
{
// 1. Build a TableWorkspace
DataObjects::TableWorkspace_sptr peakparms =
boost::shared_ptr<DataObjects::TableWorkspace>(new DataObjects::TableWorkspace);
peakparms->addColumn("int", "spectrum");
peakparms->addColumn("double", "centre");
peakparms->addColumn("double", "width");
peakparms->addColumn("double", "height");
peakparms->addColumn("double", "backgroundintercept");
peakparms->addColumn("double", "backgroundslope");
peakparms->addColumn("double", "A2");
peakparms->addColumn("double", "chi2");

// 2. Add value
API::TableRow row0 = peakparms->appendRow();
row0 << 0 << 2.0 << 0.2 << 5.0 << 1.0 << 2.0 << 0.0 << 0.1;
API::TableRow row1 = peakparms->appendRow();
row1 << 0 << 8.0 << 0.1 << 10.0 << 2.0 << 1.0 << 0.0 << 0.2;
API::TableRow row2 = peakparms->appendRow();
row2 << 2 << 4.0 << 0.4 << 20.0 << 4.0 << 0.0 << 0.0 << 0.2;
API::TableRow row3 = peakparms->appendRow();
row3 << 2 << 4.5 << 0.4 << 20.0 << 1.0 << 9.0 << 0.0 << 1000.2;


return peakparms;
}

/*
* Create a MatrixWorkspace containing 5 spectra
* Binning parameter = 1.0, 0.02, 9.0
*/
API::MatrixWorkspace_sptr createTestInputWorkspace()
{
// 1. Create empty workspace
double minx = 1.0;
double maxx = 9.0;
double dx = 0.02;
size_t size = static_cast<size_t>((maxx-minx)/dx)+1;
API::MatrixWorkspace_sptr inpWS = API::WorkspaceFactory::Instance().create("Workspace2D", 5, size, size-1);

// 2. Put x values and y values
for (size_t iw = 0; iw < inpWS->getNumberHistograms(); iw ++)
{
for (size_t ix = 0; ix < inpWS->dataX(iw).size(); ++ix)
{
inpWS->dataX(iw)[ix] = minx + double(ix)*dx;
}
for (size_t iy = 0; iy < inpWS->dataY(iw).size(); ++iy)
{
inpWS->dataY(iw)[iy] = 100.0;
}
}

return inpWS;
}

};

@@ -6,7 +6,7 @@
//----------------------------------------------------------------------
#include "MantidAPI/Algorithm.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/SpecialWorkspace2D.h"
#include "MantidDataObjects/MaskWorkspace.h"

namespace Mantid
{
@@ -17,7 +17,7 @@ namespace DataHandling
Required Properties:
<UL>
<LI> Workspace - The name of the (input & output) Workspace2D on which to perform the algorithm </LI>
<LI> Workspace - The name of the (input & output) Workspace on which to perform the algorithm </LI>
</UL>
Optional Properties (One should be set. The highest listed below will be used if more than one is.):
@@ -27,10 +27,7 @@ namespace DataHandling
<LI> WorkspaceIndexList - An ArrayProperty containing the workspace indices to mask </LI>
</UL>
@author Russell Taylor, Tessella Support Services plc
@date 15/04/2008
Copyright &copy; 2008-2010 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
Copyright &copy; 2008-2012 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
@@ -72,7 +69,7 @@ class DLLExport MaskDetectors : public API::Algorithm
void fillIndexListFromSpectra(std::vector<size_t>& indexList, const std::vector<specid_t>& spectraList,
const API::MatrixWorkspace_sptr WS);
void appendToIndexListFromWS(std::vector<size_t>& indexList, const API::MatrixWorkspace_sptr maskedWorkspace);
void appendToIndexListFromMaskWS(std::vector<size_t>& indexList, const DataObjects::SpecialWorkspace2D_const_sptr maskedWorkspace);
void appendToIndexListFromMaskWS(std::vector<size_t>& indexList, const DataObjects::MaskWorkspace_const_sptr maskedWorkspace);
};

} // namespace DataHandling
@@ -93,6 +93,26 @@ namespace

return wsName;
}

/**
* Helper function that takes a vector of vectors of items and flattens it into
* a single vector of items.
*/
std::vector<std::string> flattenVecOfVec(std::vector<std::vector<std::string> > vecOfVec)
{
std::vector<std::string> flattenedVec;

std::vector<std::vector<std::string> >::const_iterator it = vecOfVec.begin();

for(; it != vecOfVec.end(); ++it)
{
flattenedVec.insert(
flattenedVec.end(),
it->begin(), it->end());
}

return flattenedVec;
}
}

namespace Mantid
@@ -139,12 +159,39 @@ namespace Mantid
{
// Get back full path before passing to getFileLoader method, and also
// find out whether this is a multi file load.
std::vector<std::vector<std::string> > fileNames = getProperty("Filename");
std::vector<std::string> fileNames = flattenVecOfVec(getProperty("Filename"));
// If it's a single file load, then it's fine to change loader.
if(isSingleFile(fileNames))
if(fileNames.size() == 1)
{
IDataFileChecker_sptr loader = getFileLoader(getPropertyValue(name));
if( loader ) declareLoaderProperties(loader);
assert(loader); // (getFileLoader should throw if no loader is found.)
declareLoaderProperties(loader);
}

// Else we've got multiple files, and must enforce the rule that only one type of loader is allowed.
// Allowing more than one would mean that "extra" properties defined by the class user (for example
// "LoadLogFiles") are potentially ambiguous.
else if(fileNames.size() > 1)
{
IDataFileChecker_sptr loader = getFileLoader(fileNames[0]);

// If the first file has a loader ...
if( loader )
{
// ... store it's name and version and check that all other files have loaders with the same name and version.
std::string name = loader->name();
int version = loader->version();
for(size_t i = 1; i < fileNames.size(); ++i)
{
loader = getFileLoader(fileNames[i]);

if( name != loader->name() || version != loader->version() )
throw std::runtime_error("Cannot load multiple files when more than one Loader is needed.");
}
}

assert(loader); // (getFileLoader should throw if no loader is found.)
declareLoaderProperties(loader);
}
}
}
@@ -638,13 +685,37 @@ namespace Mantid
const std::string & wsName)
{
Mantid::API::IAlgorithm_sptr loadAlg = createSubAlgorithm("Load", 1);

// Here, as a workaround for groupworkspaces who's members have names but no
// accompanying entries in the ADS, we set the sub algo to not be a child ...
// accompanying entries in the ADS, we set the sub algo to not be a child.
loadAlg->setChild(false);
// ... and now, so that the the workspaces dont appear in the window, we prefix
// all workspace names with "__".
loadAlg->setPropertyValue("Filename",fileName);
loadAlg->setPropertyValue("OutputWorkspace","__" + wsName);

// Get the list properties for the concrete loader load algorithm
const std::vector<Kernel::Property*> & props = getProperties();

// Loop through and set the properties on the sub algorithm
std::vector<Kernel::Property*>::const_iterator itr;
for (itr = props.begin(); itr != props.end(); ++itr)
{
const std::string propName = (*itr)->name();

if( this->existsProperty(propName) )
{
if(propName == "Filename")
{
loadAlg->setPropertyValue("Filename",fileName);
}
else if(propName == "OutputWorkspace")
{
loadAlg->setPropertyValue("OutputWorkspace","__" + wsName);
}
else
{
loadAlg->setPropertyValue(propName, getPropertyValue(propName));
}
}
}

loadAlg->executeAsSubAlg();

return AnalysisDataService::Instance().retrieve("__" + wsName);
@@ -11,7 +11,9 @@ The set of detectors to be masked can be given as a list of either spectrum numb
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/MaskDetectors.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/MaskWorkspace.h"
#include "MantidAPI/SpectraDetectorMap.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidAPI/WorkspaceValidators.h"
@@ -90,6 +92,9 @@ void MaskDetectors::exec()
// Is it an event workspace?
EventWorkspace_sptr eventWS = boost::dynamic_pointer_cast<EventWorkspace>(WS);

// Is it a Mask Workspace ?
MaskWorkspace_sptr isMaskWS = boost::dynamic_pointer_cast<MaskWorkspace>(WS);

std::vector<size_t> indexList = getProperty("WorkspaceIndexList");
std::vector<specid_t> spectraList = getProperty("SpectraList");
const std::vector<detid_t> detectorList = getProperty("DetectorList");
@@ -118,19 +123,21 @@ void MaskDetectors::exec()
fillIndexListFromSpectra(indexList,mySpectraList,WS);
}
// If we have a workspace that could contain masking,copy that in too

if( prevMasking )
{

DataObjects::SpecialWorkspace2D_const_sptr maskWS = boost::dynamic_pointer_cast<DataObjects::SpecialWorkspace2D>(prevMasking);
DataObjects::MaskWorkspace_const_sptr maskWS = boost::dynamic_pointer_cast<DataObjects::MaskWorkspace>(prevMasking);
if (maskWS)
{
if (maskWS->getInstrument()->getDetectorIDs().size() != WS->getInstrument()->getDetectorIDs().size())
{
throw std::runtime_error("Size mismatch between input Workspace and SpecialWorkspace2D");
throw std::runtime_error("Size mismatch between input Workspace and MaskWorkspace");
}

g_log.debug() << "Extracting mask from MaskWorkspace (" << maskWS->name() << ")" << std::endl;
appendToIndexListFromMaskWS(indexList, maskWS);
} else
}
else
{
// Check the provided workspace has the same number of spectra as the input
if(prevMasking->getNumberHistograms() > WS->getNumberHistograms() )
@@ -191,6 +198,18 @@ void MaskDetectors::exec()
//Also clear the MRU for event workspaces.
eventWS->clearMRU();
}

if (isMaskWS)
{
// If the input was a mask workspace, then extract the mask to ensure
// we are returning the correct thing.
IAlgorithm_sptr alg = createSubAlgorithm("ExtractMask");
alg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", WS);
alg->executeAsSubAlg();
MatrixWorkspace_sptr ws = alg->getProperty("OutputWorkspace");
setProperty("Workspace", ws);
}

/*
This rebuild request call, gives the workspace the opportunity to rebuild the nearest neighbours map
and therfore pick up any detectors newly masked with this algorithm.
@@ -263,7 +282,7 @@ void MaskDetectors::appendToIndexListFromWS(std::vector<size_t>& indexList, cons
* @param indexList :: An existing list of indices
* @param maskedWorkspace :: An workspace with masked spectra
*/
void MaskDetectors::appendToIndexListFromMaskWS(std::vector<size_t>& indexList, const DataObjects::SpecialWorkspace2D_const_sptr maskedWorkspace)
void MaskDetectors::appendToIndexListFromMaskWS(std::vector<size_t>& indexList, const DataObjects::MaskWorkspace_const_sptr maskedWorkspace)
{
// Convert the vector of properties into a set for easy searching
std::set<int64_t> existingIndices(indexList.begin(), indexList.end());
@@ -276,6 +295,7 @@ void MaskDetectors::appendToIndexListFromMaskWS(std::vector<size_t>& indexList,

if( maskedWorkspace->dataY(i-startIndex)[0] > 0.5 && existingIndices.count(i) == 0 )
{
g_log.debug() << "Adding WorkspaceIndex " << i << " to mask." << std::endl;
indexList.push_back(i);
}
}
@@ -46,7 +46,6 @@ namespace Mantid
g_log.debug() << "SNSDataArchive URL = \'" << URL << "\'\n";

std::string wsResult = "";
std::string result = "";

//#ifdef _WIN32
// // Return an empty string
@@ -462,12 +462,12 @@ namespace Mantid
g_log.debug() << "SaveGSS(): MultipyByBinwidth = " << MultiplyByBinWidth << std::endl;

const size_t datasize = Y.size();
double bc1 = *(X.begin()); // minimum TOF in microseconds
double bc1 = X.front(); // minimum TOF in microseconds
if (bc1 <= 0.)
{
throw std::runtime_error("Cannot write out logarithmic data starting at zero");
}
double bc2 = *(X.rbegin() + 1); // maximum TOF (in microseconds?)
double bc2 = 0.5 * (*(X.rbegin()) + *(X.rbegin()+ 1)); // maximum TOF (in microseconds?)
double bc3 = (*(X.begin() + 1) - bc1) / bc1; // deltaT/T

g_log.debug() << "SaveGSS(): Min TOF = " << bc1 << std::endl;
@@ -482,6 +482,33 @@ class LoadTest : public CxxTest::TestSuite
TS_ASSERT(ws2);
removeGroupFromADS(output);
}

void testMultiFilesExtraProperties()
{
IAlgorithm_sptr proxy = AlgorithmManager::Instance().create("Load");

TS_ASSERT_THROWS_NOTHING(proxy->setPropertyValue("Filename","IRS21360,26173,38633.raw"));
TS_ASSERT_THROWS_NOTHING(proxy->setPropertyValue("OutputWorkspace","test"));

TS_ASSERT_THROWS_NOTHING(proxy->setPropertyValue("SpectrumMin","10"));
TS_ASSERT_THROWS_NOTHING(proxy->setPropertyValue("SpectrumMax","100"));

TS_ASSERT_THROWS_NOTHING(proxy->execute());

// get result
WorkspaceGroup_sptr wsg = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("test");
TS_ASSERT(wsg);

// get first ws in group
std::vector<std::string> childNames = wsg->getNames();
MatrixWorkspace_sptr childWs = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(childNames[0]);
TS_ASSERT(childWs);

// Make sure that it contains the requested number of spectra as per SpectrumMin and SpectrumMax
TS_ASSERT_EQUALS(childWs->getNumberHistograms(), 91);

removeGroupFromADS(wsg);
}
};

#endif /*LOADTEST_H_*/
@@ -50,7 +50,7 @@ class MaskDetectorsTest : public CxxTest::TestSuite
/*
* Generate a Workspace which can be (1) EventWorkspace, (2) Workspace2D, and (3) SpecialWorkspace2D
*/
void setUpWS(bool event, const std::string & name = "testSpace", bool specialworkspace2d = false, size_t numspec=9)
void setUpWS(bool event, const std::string & name = "testSpace", bool asMaskWorkspace = false, size_t numspec=9)
{
// 1. Instrument
Instrument_sptr instr = boost::dynamic_pointer_cast<Instrument>(ComponentCreationHelper::createTestInstrumentCylindrical(1, false));
@@ -81,7 +81,7 @@ class MaskDetectorsTest : public CxxTest::TestSuite
spaceEvent->setAllX(x);

}
else if (!specialworkspace2d)
else if (!asMaskWorkspace)
{
space = WorkspaceFactory::Instance().create("Workspace2D",numspec,6,5);
Workspace2D_sptr space2D = boost::dynamic_pointer_cast<Workspace2D>(space);
@@ -99,8 +99,8 @@ class MaskDetectorsTest : public CxxTest::TestSuite
}
else
{
// In case of SpecialWorkspace2D
Mantid::DataObjects::SpecialWorkspace2D_sptr specspace(new Mantid::DataObjects::SpecialWorkspace2D(instr));
// In case of MaskWorkspace
Mantid::DataObjects::MaskWorkspace_sptr specspace(new Mantid::DataObjects::MaskWorkspace(instr));
for (size_t i = 0; i < specspace->getNumberHistograms(); i ++)
{
// default to use all the detectors
@@ -109,11 +109,11 @@ class MaskDetectorsTest : public CxxTest::TestSuite
space = boost::dynamic_pointer_cast<MatrixWorkspace>(specspace);
}

if (!specialworkspace2d){
if (!asMaskWorkspace){
space->setInstrument(instr);
space->generateSpectraMap();

std::cout << "another way of find number of detetors = " << space->getInstrument()->getDetectorIDs().size() << std::endl;
std::cout << "another way of find number of detectors = " << space->getInstrument()->getDetectorIDs().size() << std::endl;
}
// Register the workspace in the data service
AnalysisDataService::Instance().addOrReplace(name, space);
@@ -310,9 +310,9 @@ class MaskDetectorsTest : public CxxTest::TestSuite
}

/*
* Test for masking detectors by SpecialWorkspace2D
* Test for masking detectors by using a MaskWorkspace
*/
void test_Giving_A_SpecialWorkspace2D()
void test_Using_A_MaskWorkspace()
{
// 1. Create 2 workspaces
const std::string inputWSName("inputWS"), existingMaskName("existingMask");
@@ -323,34 +323,6 @@ class MaskDetectorsTest : public CxxTest::TestSuite
MatrixWorkspace_sptr inputWS =
AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(inputWSName);

/* Some test output.
std::cout << std::endl;
for (size_t i = 0; i < existingMask->getNumberHistograms(); i ++)
{
std::cout << "Histogram " << i;
std::set<detid_t> detids = existingMask->getSpectrum(i)->getDetectorIDs();
if (detids.size() > 0){
detid_t detid = *detids.begin();
std::cout << " has " << detids.size() << " detectors including " << detid << "\t\t";
} else {
std::cout << " has ZERO detectors associated" << "\t\t";
}
Mantid::Geometry::IDetector_const_sptr detector = existingMask->getDetector(i);
std::cout << "Is monitor? = " << detector->isMonitor() << std::endl;
} // ENDFOR
for (size_t i = 0; i < inputWS->getNumberHistograms(); i ++)
{
std::cout << "Histogram " << i;
std::set<detid_t> detids = inputWS->getSpectrum(i)->getDetectorIDs();
if (detids.size() > 0){
detid_t detid = *detids.begin();
std::cout << " has " << detids.size() << " detectors including " << detid << std::endl;
} else {
std::cout << " has ZERO detectors associated" << std::endl;
}
} // ENDFOR
*/

// 2. Mask some detectors: Mask workspace indexes 0, 3, 4
std::set<int> masked_indices;
masked_indices.insert(0);
@@ -4,15 +4,41 @@
#include <cxxtest/TestSuite.h>

#include "MantidDataObjects/MaskWorkspace.h"
#include "MantidGeometry/Instrument.h"
#include "MantidTestHelpers/ComponentCreationHelper.h"

class MaskWorkspaceTest : public CxxTest::TestSuite
{
public:

void test_Nothing()
void test_default_constructor()
{
//TODO: Test Something!
TS_ASSERT_THROWS_NOTHING(new Mantid::DataObjects::MaskWorkspace());
}

void test_constructor_using_length()
{
int nDetectors = 10;
Mantid::DataObjects::MaskWorkspace* maskWS = new Mantid::DataObjects::MaskWorkspace(nDetectors);
TS_ASSERT_EQUALS(maskWS->getNumberHistograms(), nDetectors);
TS_ASSERT_EQUALS(maskWS->blocksize(), 1);
}

void test_constructure_using_instrument()
{
int pixels = 10;

Mantid::Geometry::Instrument_sptr inst =
ComponentCreationHelper::createTestInstrumentRectangular2(1,pixels);
inst->setName("MaskWorkspaceTest_Instrument");

Mantid::DataObjects::MaskWorkspace* maskWS =
new Mantid::DataObjects::MaskWorkspace(inst, false);

TS_ASSERT_EQUALS(maskWS->getNumberHistograms(), pixels*pixels);

}

};

#endif // MANTID_DATAOBJECTS_MASKWORKSPACETEST_H
@@ -347,7 +347,8 @@ namespace Mantid
// Need to be able to enter the while loop
nodeQueue.push_back(node);
int nlevel = 0;
while( !nodeQueue.empty() && (nlevels == 0 || nlevel < nlevels))
const bool limitSearch(nlevels > 0);
while( !nodeQueue.empty() )
{
node = nodeQueue.front();
nodeQueue.pop_front();
@@ -371,6 +372,7 @@ namespace Mantid
}
}
nlevel++;
if( limitSearch && nlevel == nlevels) break;
}// while-end

// If we have reached here then the search failed
@@ -213,6 +213,7 @@ namespace DataObjects
class PeaksWorkspace;
class GroupingWorkspace;
class OffsetsWorkspace;
class MaskWorkspace;
class SpecialWorkspace2D;
class Workspace2D;
}
@@ -270,8 +271,10 @@ std::string getUnmangledTypeName(const std::type_info& type)
string("PeaksWorkspace")));
typestrings.insert(make_pair(typeid(boost::shared_ptr<GroupingWorkspace>).name(),
string("GroupingWorkspace")));
typestrings.insert(make_pair(typeid(boost::shared_ptr<OffsetsWorkspace>).name(),
typestrings.insert(make_pair(typeid(boost::shared_ptr<OffsetsWorkspace>).name(),
string("OffsetsWorkspace")));
typestrings.insert(make_pair(typeid(boost::shared_ptr<MaskWorkspace>).name(),
string("MaskWorkspace")));
typestrings.insert(make_pair(typeid(boost::shared_ptr<SpecialWorkspace2D>).name(),
string("SpecialWorkspace2D")));
typestrings.insert(make_pair(typeid(boost::shared_ptr<IMDHistoWorkspace>).name(),
@@ -7,6 +7,7 @@ set ( SRC_FILES
src/MantidVecHelper.cpp
src/PyAlgorithmWrapper.cpp
src/PythonInterfaceFunctions.cpp
src/PythonThreading.cpp
src/PythonWrapper.cpp
src/WorkspaceProxies.cpp
src/api_exports.cpp
@@ -24,6 +25,7 @@ set ( INC_FILES
inc/MantidPythonAPI/MantidVecHelper.h
inc/MantidPythonAPI/PyAlgorithmWrapper.h
inc/MantidPythonAPI/PythonInterfaceFunctions.h
inc/MantidPythonAPI/PythonThreading.h
inc/MantidPythonAPI/WorkspaceProxies.h
inc/MantidPythonAPI/api_exports.h
inc/MantidPythonAPI/geometryhelper.h
@@ -1109,26 +1109,11 @@ def setPropertiesDialog(self, *args, **kwargs):
presets += name + '=' + _makeString(value) + '|'

# finally run the configured dialog
dialog = _qti.app.mantidUI.createPropertyInputDialog(self.name(), presets, message, enabled_list, disabled_list)
adapter = _qti.ThreadAdapter(_qti.app, _qti.app.mantidUI)
dialog = adapter.createPropertyInputDialog(self.name(), presets, message, enabled_list, disabled_list)
if dialog == False:
sys.exit('Information: Script execution cancelled')

def execute(self, *args, **kwargs):
"""
Execute the configured algorithm.
"""
self.setPropertyValues(*args, **kwargs)
try:
# Was the async property added ?
run_async = self.__async__
except AttributeError:
run_async = False
if run_async:
success = _qti.app.mantidUI.runAlgorithmAsync_PyCallback(self.name())
if success == False:
sys.exit('An error occurred while running %s. See results log for details.' % self.name())
else:
self._getHeldObject().execute()
#---------------------------------------------------------------------------------------

class MantidPyFramework(FrameworkManager):
@@ -1205,10 +1190,10 @@ def initialise(self, gui=None):
self.__gui__ = gui

# Run through init steps
self._importSimpleAPIToMain()
self._pyalg_loader.load_modules(refresh=False)
# Janik Zikovsky, sep 29, 2011: Commented out this line to speed up starting. Seems to have had no ill effect
#self._importSimpleAPIToMain()
# Ensure the fake Python algorithm functions are overwritten by
# the real ones
self._importSimpleAPIToMain()

self.__is_initialized = True

@@ -1356,7 +1341,7 @@ def _addToPySearchPath(dirs):

def _importSimpleAPIToMain(self):
import mantidsimple
mantidsimple.translate() #Unnecessary : called already when importing.
mantidsimple.translate()
for name in dir(mantidsimple):
if name.startswith('_'):
continue
@@ -1553,10 +1538,7 @@ def __init__(self):
super(PythonAlgorithm,self).__init__()
# Dictionary of property names/types
self._proptypes = {}

def clone(self):
return copy.deepcopy(self)


def name(self):
return self.__class__.__name__

@@ -50,7 +50,7 @@ namespace PythonAPI
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class PyAlgorithmBase : public Mantid::API::CloneableAlgorithm
class PyAlgorithmBase : public Mantid::API::Algorithm
{

public:
@@ -317,30 +317,9 @@ DECLARE_DEFAULTRETURN(PyAlgorithmBase*, NULL)
/**
* A callback structure that can route calls into Python
*/
class PyAlgorithmCallback : public PyAlgorithmBase
class PyAlgorithmWrapper : public PyAlgorithmBase, public boost::python::wrapper<PyAlgorithmBase>
{

public:
///Constructor
PyAlgorithmCallback(PyObject *self);

///Destructor
virtual ~PyAlgorithmCallback();

/// Called when a delete is performed inside a shared pointer
virtual void kill()
{
PythonGIL gil;
// !-----
// This order is very important as the decref causes Python to call the destructor on this object
// and the destructor checks the value of the killed flag
// !-----
m_ref_killed = true;
Py_DECREF(m_self);
}

///Overridden clone method
virtual CloneableAlgorithm * clone();
///Overridden name method
const std::string name() const;
///Overridden version method
@@ -354,12 +333,8 @@ class PyAlgorithmCallback : public PyAlgorithmBase
/// Overridden algorithm exec method
virtual void exec();

/// A pointer referring to the Python object
PyObject *m_self;
/// A boolean flagging whether Py_DECREF has been called yet.
/// A shared pointer can't use 'delete' here so it uses kill() instead. If that is the
/// case then the destructor shouldn't decref the reference count as well
bool m_ref_killed;
/// Returns the PyObject that owns this wrapper, i.e. self
PyObject * getSelf() const;
};


@@ -3,6 +3,7 @@

#include <MantidPythonAPI/FrameworkManagerProxy.h>
#include "MantidPythonAPI/BoostPython_Silent.h"
#include "MantidPythonAPI/PythonThreading.h"

namespace Mantid
{
@@ -34,47 +35,6 @@ namespace Mantid
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
*/

// @cond UNDOC
// We have to perform some magic due to threading issues. There are 2 scenarios:
// 1) If asynchronous execution of code is requested from currently executing Python code then we must acquire the GIL before we
// perform an call up to python;
// 2) If asynchronous execution of code is requested from outside of running Python code then the GIL is not required since there is
// no other thread to lock against and worse still if we try then we get a deadlock

// See http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock for more information on the GIL

class PythonGIL
{
public:
///Constructor
PythonGIL() : m_tstate(NULL), m_gil_state(PyGILState_UNLOCKED), m_locked(false)
{
if( !PyThreadState_GetDict() )
{
m_gil_state = PyGILState_Ensure();
m_tstate = PyThreadState_GET();
m_locked = true;
}
}

///Destructor
~PythonGIL()
{
if( m_locked )
{
PyThreadState_Swap(m_tstate);
PyGILState_Release(m_gil_state);
}
}
private:
/// Store the thread state
PyThreadState *m_tstate;
/// Store the GIL state
PyGILState_STATE m_gil_state;
/// If we've locked the state
bool m_locked;
};

/// Handle a Python error state
void handlePythonError(const bool with_trace=true);
/// A structure t handle default returns for template functions
@@ -127,9 +87,9 @@ namespace Mantid
static ResultType dispatch(PyObject *object, const std::string & func_name)
{
DefaultReturn<ResultType> default_value;
GlobalInterpreterLock gil;
try
{
PythonGIL gil;
return boost::python::call_method<ResultType>(object, func_name.c_str());
}
catch(boost::python::error_already_set&)
@@ -146,9 +106,9 @@ namespace Mantid

static void dispatch(PyObject *object, const std::string & func_name)
{
GlobalInterpreterLock gil;
try
{
PythonGIL gil;
boost::python::call_method<void>(object, func_name.c_str());
}
catch(boost::python::error_already_set&)
@@ -171,9 +131,9 @@ namespace Mantid
static ResultType dispatch(PyObject *object, const std::string & func_name, const ArgType & arg)
{
DefaultReturn<ResultType> default_value;
GlobalInterpreterLock gil;
try
{
PythonGIL gil;
return boost::python::call_method<ResultType>(object, func_name.c_str(), arg);
}
catch(boost::python::error_already_set&)
@@ -191,9 +151,9 @@ namespace Mantid

static void dispatch(PyObject *object, const std::string & func_name, const ArgType & arg)
{
GlobalInterpreterLock gil;
try
{
PythonGIL gil;
boost::python::call_method<void>(object, func_name.c_str(), arg);
}
catch(boost::python::error_already_set&)
@@ -0,0 +1,103 @@
#ifndef MANTID_PYTHONAPI_PYTHONALGORITHMINSTANTIATOR_H_
#define MANTID_PYTHONAPI_PYTHONALGORITHMINSTANTIATOR_H_

/*
Copyright &copy; 2011 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "MantidKernel/Instantiator.h"
#include "MantidAPI/IAlgorithm.h"
#include "MantidPythonAPI/PythonThreading.h"

#include <boost/python/object.hpp>
#include <boost/python/extract.hpp>

namespace Mantid
{
namespace PythonAPI
{
///@cond
/**
* A custom deleter to be used with shared_ptr objects that wrap objects
* instantiated in Python
*/
template<typename T>
struct NoDelete
{
public:
void operator()(T const *) // No-op as ptr was not created with new
{}
};
///@endcond


template<typename Base>
class PythonObjectInstantiator : public Kernel::AbstractInstantiator<Base>
{
public:
/// Constructor taking a Python class object wrapped as a boost::python:object
PythonObjectInstantiator(boost::python::object classObject)
: m_classObject(classObject) {}

/// Creates an instance of the object as shared_ptr to the Base type
boost::shared_ptr<Base> createInstance() const;

/// Creates an instance of the object as raw pointer to the Base type
Base* createUnwrappedInstance() const;

private:
/// The class name
boost::python::object m_classObject;
};

/**
* Creates an instance of the object as shared_ptr to the Base type
* @returns A shared_ptr to Base.
*/
template<typename Base>
boost::shared_ptr<Base> PythonObjectInstantiator<Base>::createInstance() const
{
// A custom deleter is required since calling delete is not what we want
return boost::shared_ptr<Base>(createUnwrappedInstance(), NoDelete<Base>());
}

/**
* Creates an instance of the object as raw ptr to the Base type
* @returns A raw ptr to Base.
*/
template<typename Base>
Base * PythonObjectInstantiator<Base>::createUnwrappedInstance() const
{
using namespace boost::python;
GlobalInterpreterLock gil;
PyObject *alg = PyObject_CallObject(m_classObject.ptr(), NULL); // No args
object wrap(handle<>(borrowed(alg))); // borrowed: Do not decrement reference count on destruction
Base *ptr = extract<Base*>(wrap);
return ptr;
}

}
}

#endif /* MANTID_PYTHONAPI_PYTHONALGORITHMINSTANTIATOR_H_ */
@@ -0,0 +1,75 @@
#ifndef MANTID_PYTHONAPI_PYTHONTHREADING_H_
#define MANTID_PYTHONAPI_PYTHONTHREADING_H_
/**
Defines utility functions and classes for dealing with Python threads
Copyright &copy; 2012 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
#include "MantidPythonAPI/BoostPython_Silent.h"

namespace Mantid
{
namespace PythonAPI
{

/// Saves a pointer to the PyThreadState of the main thread
void saveMainThreadState(PyThreadState *threadState);

/**
* Defines a structure for creating and destroying a
* Python thread state using the RAII pattern
*/
struct PythonThreadState
{
explicit PythonThreadState();
~PythonThreadState();
private:
PythonThreadState(const PythonThreadState&);
PythonThreadState& operator=(const PythonThreadState&);

PyThreadState * m_mainThreadState;
PyThreadState * m_thisThreadState;
};

/**
* Defines a structure for acquiring/releasing the Python GIL
* using the RAII pattern
*/
struct GlobalInterpreterLock
{
GlobalInterpreterLock() : m_state(PyGILState_Ensure())
{}

~GlobalInterpreterLock()
{
PyGILState_Release(m_state);
}
private:
/// Current GIL state
PyGILState_STATE m_state;
};


}
}


#endif /* MANTID_PYTHONAPI_PYTHONTHREADING_H_ */
@@ -266,7 +266,26 @@ def translate():
create_algorithm(algorithm[0], max(algorithm[1]), _algm_object)
create_algorithm_dialog(algorithm[0], max(algorithm[1]), _algm_object)

translate()
def fake_python_alg_functions():
"""
Creates fake, no-op functions to fool imports for
Python algorithms. The real definitions will
be created after all of the algorithms have been registered
"""
def create_fake_function(files):
def fake_function():
pass
for pyfile in files:
if pyfile.endswith(".py"):
name = pyfile.strip(".py")
fake_function.__name__ = name
if name not in globals():
globals()[name] = fake_function
#
directories = mtd.getConfigProperty("pythonalgorithms.directories").split(';')
for top in directories:
for root, dirs, files in os.walk(top):
create_fake_function(files)

translate()
fake_python_alg_functions()
@@ -2,6 +2,7 @@
// Includes
//------------------------------------
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AlgorithmFactory.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/DeprecatedAlgorithm.h"
#include "MantidAPI/FrameworkManager.h"
@@ -12,6 +13,8 @@
#include "MantidKernel/Strings.h"
#include "MantidPythonAPI/FrameworkManagerProxy.h"
#include "MantidPythonAPI/PyAlgorithmWrapper.h"
#include "MantidPythonAPI/PythonObjectInstantiator.h"

#include <fstream>
#include <sstream>
#include <stdexcept>
@@ -537,18 +540,17 @@ bool FrameworkManagerProxy::workspaceExists(const std::string & name) const
*/
void FrameworkManagerProxy::registerPyAlgorithm(boost::python::object pyobj)
{
Mantid::API::CloneableAlgorithm* cppobj = boost::python::extract<Mantid::API::CloneableAlgorithm*>(pyobj);
if( cppobj )
{
if( ! Mantid::API::AlgorithmFactory::Instance().storeCloneableAlgorithm(cppobj) )
{
g_log.error("Unable to register Python algorithm \"" + cppobj->name() + "\"");
}
}
else
{
throw std::runtime_error("Unrecognized object type in Python algorithm registration.");
}
using Mantid::PythonAPI::PythonObjectInstantiator;
using Mantid::API::Algorithm;
using Mantid::API::AlgorithmFactory;
using boost::python::object;
using boost::python::handle;
using boost::python::borrowed;

PyObject *classObject = PyObject_GetAttrString(pyobj.ptr(), "__class__");
object classType(handle<>(borrowed(classObject)));
// Takes ownership of instantiator and replaces any existing algorithm
AlgorithmFactory::Instance().subscribe(new PythonObjectInstantiator<Algorithm>(classType), true);
}

//--------------------------------------------------------------------------
@@ -2,7 +2,8 @@
// Includes
//--------------------------------------------
#include "MantidPythonAPI/BoostPython_Silent.h"
#include <MantidPythonAPI/PyAlgorithmWrapper.h>
#include "MantidPythonAPI/PyAlgorithmWrapper.h"
#include "MantidPythonAPI/PythonThreading.h"

using namespace Mantid::PythonAPI;

@@ -12,84 +13,62 @@ using namespace Mantid::PythonAPI;
/**
* Constructor
*/
PyAlgorithmBase::PyAlgorithmBase() : CloneableAlgorithm()
PyAlgorithmBase::PyAlgorithmBase() : Algorithm()
{
}

//--------------------------------------------------
// PyAlgorithmCallback
// PyAlgorithmWrapper
//--------------------------------------------------
/**
* Constructor for callback
* @param self :: The python object to use to perform the callback
*/
PyAlgorithmCallback::PyAlgorithmCallback(PyObject *self) : PyAlgorithmBase(), m_self(self), m_ref_killed(false)
{
Py_INCREF(m_self);
}

/**
* Destructor
*/
PyAlgorithmCallback::~PyAlgorithmCallback()
{
if( !m_ref_killed )
{
Py_XDECREF(m_self);
}
}

/**
* Return a clone of this object
* @returns a pointer to the clone object
*/
Mantid::API::CloneableAlgorithm * PyAlgorithmCallback::clone()
{
// Call the python clone method to get back a full copy of the whole object and
// then return the C++ object extracted from this
PyAlgorithmBase *cloned = PyCall_NoArg<PyAlgorithmBase*>::dispatch(m_self, "clone");
return cloned;
}

/**
* Call the name method on the Python object
* @returns The name of the algorithm
*/
const std::string PyAlgorithmCallback::name() const
const std::string PyAlgorithmWrapper::name() const
{
return PyCall_NoArg<const std::string>::dispatch(m_self, "name");
GlobalInterpreterLock gil;
PyObject *self = getSelf();
return std::string(self->ob_type->tp_name);
}

/**
* Call the version method on the Python object
* @returns The version number of the algorithm
*/
int PyAlgorithmCallback::version() const
int PyAlgorithmWrapper::version() const
{
return PyCall_NoArg<const int>::dispatch(m_self, "version");
return PyCall_NoArg<const int>::dispatch(getSelf(), "version");
}

/**
* Call the category method on the Python object
* @returns The category this algorithm belongs to
*/
const std::string PyAlgorithmCallback::category() const
const std::string PyAlgorithmWrapper::category() const
{
return PyCall_NoArg<const std::string>::dispatch(m_self, "category");
return PyCall_NoArg<const std::string>::dispatch(getSelf(), "category");
}

/**
* Perform initialization for this algorithm. This just calls up to Python.
*/
void PyAlgorithmCallback::init()
void PyAlgorithmWrapper::init()
{
PyCall_NoArg<void>::dispatch(m_self, "PyInit");
PyCall_NoArg<void>::dispatch(getSelf(), "PyInit");
}

/**
* Execute this algorithm. This just calls up to Python.
*/
void PyAlgorithmCallback::exec()
void PyAlgorithmWrapper::exec()
{
PyCall_NoArg<void>::dispatch(m_self, "PyExec");
PyCall_NoArg<void>::dispatch(getSelf(), "PyExec");
}

/// Returns the PyObject that owns this wrapper, i.e. self
/// @returns A pointer to self
PyObject * PyAlgorithmWrapper::getSelf() const
{
return boost::python::detail::wrapper_base_::get_owner(*this);
}

@@ -32,7 +32,7 @@ namespace {
/// Convert a python error state to a C++ exception so that Mantid can catch it
void handlePythonError(const bool with_trace)
{
PythonGIL gil;
GlobalInterpreterLock gil;
if( !PyErr_Occurred() ) {
boost::python::throw_error_already_set();
return;
@@ -0,0 +1,52 @@
#include "MantidPythonAPI/PythonThreading.h"
#include <Poco/Thread.h>

namespace // <anonymous>
{
/// The main thread state
PyThreadState *g_mainThreadState = NULL;
}

namespace Mantid
{
namespace PythonAPI
{

/// Saves a pointer to the PyThreadState of the main thread
void saveMainThreadState(PyThreadState *threadState)
{
g_mainThreadState = threadState;
}

//-----------------------------------------------------------------------------
// PythonThreadState
//-----------------------------------------------------------------------------

PythonThreadState::PythonThreadState()
: m_mainThreadState(NULL), m_thisThreadState(NULL)
{
m_mainThreadState = g_mainThreadState;
assert(m_mainThreadState);
if(Poco::Thread::current())
{
std::cerr << "In child C thread\n";
PyEval_AcquireLock();
PyInterpreterState * mainInterpreterState = m_mainThreadState->interp;
m_thisThreadState = PyThreadState_New(mainInterpreterState);
PyThreadState_Swap(m_thisThreadState);
}
}

PythonThreadState::~PythonThreadState()
{
if(m_thisThreadState)
{
PyThreadState_Swap(m_mainThreadState);
PyThreadState_Clear(m_thisThreadState);
PyThreadState_Delete(m_thisThreadState);
PyEval_ReleaseLock();
}
}

}
}
@@ -8,6 +8,8 @@
#include "MantidPythonAPI/stl_proxies.h"
#include "MantidPythonAPI/MantidVecHelper.h"
#include "MantidKernel/DllOpen.h"
#include "MantidPythonAPI/PythonThreading.h"

// Noisy MSVC compiler that complains about Boost stuff we can do nothing about. For some reason
// it wouldn't accept it in the BoostPython_Silent.h header
#ifdef _MSC_VER
@@ -45,6 +47,8 @@ BOOST_PYTHON_MODULE(libMantidPythonAPI)
{
/// @cond
using namespace Mantid::PythonAPI;
saveMainThreadState(PyThreadState_Get());

boost::python::def("mantid_build_version", mantid_version);
MantidVecHelper::initializeDependencies(); // This initializes numpy if it is available
// Export some frequently used stl containers
@@ -28,6 +28,7 @@
#include "MantidPythonAPI/PyAlgorithmWrapper.h"
//Poco
#include <Poco/ActiveResult.h>
#include <Poco/Thread.h>
#include "MantidAPI/IMDWorkspace.h"
#include "MantidAPI/IPeak.h"

@@ -123,9 +124,53 @@ using namespace boost::python;
return self.getProperty(prop_name);
}

// Overloads for createSubAlgorithm function which has 1 optional argument
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(PyAlgorithmBase_createSubAlgorithmOverloader, PythonAPI::PyAlgorithmBase::_createSubAlgorithm, 1, 2)
namespace
{
// Overloads for createSubAlgorithm function which has 1 optional argument
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(PyAlgorithmBase_createSubAlgorithmOverloader, PythonAPI::PyAlgorithmBase::_createSubAlgorithm, 1, 2)

struct AllowCThreads
{
AllowCThreads() : m_tracefunc(NULL), m_tracearg(NULL), m_saved(NULL)
{
PyThreadState *curThreadState = PyThreadState_GET();
assert(curThreadState != NULL);
m_tracefunc = curThreadState->c_tracefunc;
m_tracearg = curThreadState->c_traceobj;
Py_XINCREF(m_tracearg);
PyEval_SetTrace(NULL,NULL);
m_saved = PyEval_SaveThread();
}
~AllowCThreads()
{
PyEval_RestoreThread(m_saved);
PyEval_SetTrace(m_tracefunc, m_tracearg);
Py_XDECREF(m_tracearg);
}
private:
Py_tracefunc m_tracefunc;
PyObject *m_tracearg;
PyThreadState *m_saved;
};


/**
* Releases the GIL and disables any tracer functions, executes the calling algorithm object
* and re-acquires the GIL and resets the tracing functions.
* The trace functions are disabled as they can seriously hamper performance of Python algorithms
*
* As an algorithm is a potentially time-consuming operation, this allows other threads
* to execute python code while this thread is executing C code
* @param self :: A reference to the calling object
*/
bool executeWhileReleasingGIL(IAlgorithm & self)
{
bool result(false);
AllowCThreads threadStateHolder;
result = self.execute();
return result;
}
}
void export_ialgorithm()
{

@@ -148,8 +193,7 @@ using namespace boost::python;
.def("getWikiSummary", &API::IAlgorithm::getWikiSummary)
.def("getWikiDescription", &API::IAlgorithm::getWikiDescription)
.def("initialize", &API::IAlgorithm::initialize)
.def("execute", &API::IAlgorithm::execute)
.def("executeAsync", &API::IAlgorithm::executeAsync)
.def("execute", &executeWhileReleasingGIL)
.def("isRunningAsync", &API::IAlgorithm::isRunningAsync)
.def("isInitialized", &API::IAlgorithm::isInitialized)
.def("isLogging", &API::IAlgorithm::isLogging)
@@ -170,8 +214,6 @@ using namespace boost::python;
;
class_< API::Algorithm, bases<API::IAlgorithm>, boost::noncopyable>("IAlgorithm", no_init)
;
class_< API::CloneableAlgorithm, bases<API::Algorithm>, boost::noncopyable>("CloneableAlgorithm", no_init)
;

/// Algorithm properties
class_<Mantid::Kernel::PropertyWithValue<IAlgorithm_sptr>,
@@ -184,33 +226,32 @@ using namespace boost::python;
//PyAlgorithmBase
//Save some typing for all of the templated declareProperty and getProperty methods
#define EXPORT_DECLAREPROPERTY(type, suffix)\
.def("declareProperty_"#suffix,(void(PyAlgorithmBase::*)(const std::string &, type, const std::string &,const unsigned int))&PyAlgorithmBase::_declareProperty<type>) \
.def("declareProperty_"#suffix,(void(PyAlgorithmBase::*)(const std::string &, type, Kernel::IValidator &,const std::string &,const unsigned int))&PyAlgorithmBase::_declareProperty<type>) \
.def("declareListProperty_"#suffix,(void(PyAlgorithmBase::*)(const std::string &, boost::python::list, const std::string &,const unsigned int))&PyAlgorithmBase::_declareListProperty<type>)\
.def("declareListProperty_"#suffix,(void(PyAlgorithmBase::*)(const std::string &, boost::python::list, Kernel::IValidator &,const std::string &,const unsigned int))&PyAlgorithmBase::_declareListProperty<type>) \
.def("declareProperty_"#suffix,(void(PyAlgorithmWrapper::*)(const std::string &, type, const std::string &,const unsigned int))&PyAlgorithmBase::_declareProperty<type>) \
.def("declareProperty_"#suffix,(void(PyAlgorithmWrapper::*)(const std::string &, type, Kernel::IValidator &,const std::string &,const unsigned int))&PyAlgorithmBase::_declareProperty<type>) \
.def("declareListProperty_"#suffix,(void(PyAlgorithmWrapper::*)(const std::string &, boost::python::list, const std::string &,const unsigned int))&PyAlgorithmBase::_declareListProperty<type>)\
.def("declareListProperty_"#suffix,(void(PyAlgorithmWrapper::*)(const std::string &, boost::python::list, Kernel::IValidator &,const std::string &,const unsigned int))&PyAlgorithmBase::_declareListProperty<type>) \

#define EXPORT_GETPROPERTY(type, suffix)\
.def("getProperty_"#suffix,(type(PyAlgorithmBase::*)(const std::string &))&PyAlgorithmBase::_getProperty<type>)
.def("getProperty_"#suffix,(type(PyAlgorithmWrapper::*)(const std::string &))&PyAlgorithmBase::_getProperty<type>)

#define EXPORT_GETLISTPROPERTY(type, suffix)\
.def("getListProperty_"#suffix,(std::vector<type>(PyAlgorithmBase::*)(const std::string &))&PyAlgorithmBase::_getListProperty<type>)
.def("getListProperty_"#suffix,(std::vector<type>(PyAlgorithmWrapper::*)(const std::string &))&PyAlgorithmBase::_getListProperty<type>)

class_< PyAlgorithmBase, boost::shared_ptr<PyAlgorithmCallback>, bases<API::CloneableAlgorithm>,
boost::noncopyable >("PyAlgorithmBase")
.enable_pickling()
.def("_setWorkspaceProperty", &PyAlgorithmBase::_setWorkspaceProperty)
.def("_setMatrixWorkspaceProperty", &PyAlgorithmBase::_setMatrixWorkspaceProperty)
.def("_setTableWorkspaceProperty", &PyAlgorithmBase::_setTableWorkspaceProperty)
.def("_declareFileProperty", &PyAlgorithmBase::_declareFileProperty)
.def("_declareWorkspace", (void(PyAlgorithmBase::*)(const std::string &, const std::string &,const std::string &, const unsigned int))&PyAlgorithmBase::_declareWorkspace)
.def("_declareWorkspace", (void(PyAlgorithmBase::*)(const std::string &, const std::string &,Kernel::IValidator&,const std::string &, const unsigned int))&PyAlgorithmBase::_declareWorkspace)
.def("_declareMatrixWorkspace", (void(PyAlgorithmBase::*)(const std::string &, const std::string &,const std::string &, const unsigned int))&PyAlgorithmBase::_declareMatrixWorkspace)
.def("_declareMatrixWorkspace", (void(PyAlgorithmBase::*)(const std::string &, const std::string &,Kernel::IValidator&,const std::string &, const unsigned int))&PyAlgorithmBase::_declareMatrixWorkspace)
.def("_declareTableWorkspace", &PyAlgorithmBase::_declareTableWorkspace)
.def("_declareAlgorithmProperty", &PyAlgorithmBase::_declareAlgorithmProperty)
.def("_setAlgorithmProperty", &PyAlgorithmBase::_setAlgorithmProperty)
.def("_getAlgorithmProperty", &PyAlgorithmBase::_getAlgorithmProperty)
.def("log", &PyAlgorithmBase::getLogger, return_internal_reference<>())

class_< PyAlgorithmWrapper, bases<API::Algorithm>, boost::noncopyable >("PyAlgorithmBase")
.def("_setWorkspaceProperty", &PyAlgorithmWrapper::_setWorkspaceProperty)
.def("_setMatrixWorkspaceProperty", &PyAlgorithmWrapper::_setMatrixWorkspaceProperty)
.def("_setTableWorkspaceProperty", &PyAlgorithmWrapper::_setTableWorkspaceProperty)
.def("_declareFileProperty", &PyAlgorithmWrapper::_declareFileProperty)
.def("_declareWorkspace", (void(PyAlgorithmWrapper::*)(const std::string &, const std::string &,const std::string &, const unsigned int))&PyAlgorithmWrapper::_declareWorkspace)
.def("_declareWorkspace", (void(PyAlgorithmWrapper::*)(const std::string &, const std::string &,Kernel::IValidator&,const std::string &, const unsigned int))&PyAlgorithmWrapper::_declareWorkspace)
.def("_declareMatrixWorkspace", (void(PyAlgorithmWrapper::*)(const std::string &, const std::string &,const std::string &, const unsigned int))&PyAlgorithmWrapper::_declareMatrixWorkspace)
.def("_declareMatrixWorkspace", (void(PyAlgorithmWrapper::*)(const std::string &, const std::string &,Kernel::IValidator&,const std::string &, const unsigned int))&PyAlgorithmWrapper::_declareMatrixWorkspace)
.def("_declareTableWorkspace", &PyAlgorithmWrapper::_declareTableWorkspace)
.def("_declareAlgorithmProperty", &PyAlgorithmWrapper::_declareAlgorithmProperty)
.def("_setAlgorithmProperty", &PyAlgorithmWrapper::_setAlgorithmProperty)
.def("_getAlgorithmProperty", &PyAlgorithmWrapper::_getAlgorithmProperty)
.def("log", &PyAlgorithmWrapper::getLogger, return_internal_reference<>())
.def("_createSubAlgorithm", &PyAlgorithmBase::_createSubAlgorithm, PyAlgorithmBase_createSubAlgorithmOverloader()[return_value_policy< return_by_value >()] )
EXPORT_DECLAREPROPERTY(int, int)
EXPORT_DECLAREPROPERTY(double, dbl)
@@ -56,12 +56,17 @@ namespace Mantid

/// Returns a category of the algorithm.
virtual const std::string category() const;
/// A default category, chosen if there is no override
std::string defaultCategory() const;

private:
/// Private init for this algorithm
virtual void init();
/// Private exec for this algorithm
virtual void exec();

/// Returns the self object
PyObject * getSelf() const;
};
}
}
@@ -0,0 +1,165 @@
#ifndef MANTID_PYTHONINTERFACE_CALLMETHOD_H_
#define MANTID_PYTHONINTERFACE_CALLMETHOD_H_
/**
Copyright &copy; 2012 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
*/
#include "MantidPythonInterface/kernel/Environment/Threading.h"
#include "MantidPythonInterface/kernel/Environment/WrapperHelpers.h"

#include <boost/python/class.hpp>
#include <boost/python/call_method.hpp>


namespace Mantid { namespace PythonInterface {
namespace Environment
{

/// Handle a Python error state
DLLExport void translateErrorToException(const bool withTrace = true);

///
/// A wrapper around the boost::call_method to ensure that
/// the GIL is locked while the call is happening
///

/** @name No argument Python calls */
//@{
/**
* Perform a call to a python function that takes no arguments and returns a value
*/
template<typename ResultType>
struct DLLExport CallMethod_NoArg
{
/**
* Dispatch a call to the method on the given object. If the method does not exist
* then return the defaultValue
* @param self The object containing the method definition
* @param funcName The method name
* @param defaultValue A default value if the method does not exist
* @return
*/
static ResultType dispatchWithDefaultReturn(PyObject *self, const char * funcName, const ResultType & defaultValue)
{
GlobalInterpreterLock gil;
if(Environment::typeHasAttribute(self, funcName))
{
try
{
return boost::python::call_method<ResultType>(self, funcName);
}
catch(boost::python::error_already_set&)
{
translateErrorToException();
}
}
return defaultValue;
}

/**
* Dispatch a call to the method on the given object. If the method does not exist
* then raise a std::runtime_error exception
* @param self The object containing the method definition
* @param funcName The method name
* @param errorMsg An error message to pass to the generated exception
* @return
*/
static ResultType dispatchWithException(PyObject *self, const char * funcName, const char * errorMsg)
{
GlobalInterpreterLock gil;
if(Environment::typeHasAttribute(self, funcName))
{
try
{
return boost::python::call_method<ResultType>(self, funcName);
}
catch(boost::python::error_already_set&)
{
translateErrorToException();
}
}
else
{
throw std::runtime_error(errorMsg);
}
return ResultType();
}
};

///Specialization for void return type
template<>
struct DLLExport CallMethod_NoArg<void>
{
/**
* Dispatch a call to the method on the given object. If the method does not exist
* then do nothing
* @param self The object containing the method definition
* @param funcName The method name
* @param defaultValue A default value if the method does not exist
* @return
*/
static void dispatchWithDefaultReturn(PyObject *self, const char * funcName)
{
GlobalInterpreterLock gil;
if(Environment::typeHasAttribute(self, funcName))
{
try
{
boost::python::call_method<void>(self, funcName);
}
catch(boost::python::error_already_set&)
{
translateErrorToException();
}
}
}
/**
* Dispatch a call to the method on the given object. If the method does not exist
* then raise a runtime_error
* @param self The object containing the method definition
* @param funcName The method name
* @param errorMsg An error message if the method does not exist
* @return
*/
static void dispatchWithException(PyObject *self, const char * funcName, const char * errorMsg)
{
GlobalInterpreterLock gil;
if(Environment::typeHasAttribute(self, funcName))
{
try
{
boost::python::call_method<void>(self, funcName);
}
catch(boost::python::error_already_set&)
{
translateErrorToException();
}
}
else
{
throw std::runtime_error(errorMsg);
}
}
};
//@}

}
}}

#endif //MANTID_PYTHONINTERFACE_CALLMETHOD_H_
@@ -0,0 +1,72 @@
#ifndef MANTID_PYTHONINTERFACE_PYTHONTHREADING_H_
#define MANTID_PYTHONINTERFACE_PYTHONTHREADING_H_
/**
Defines utility functions and classes for dealing with Python threads
Copyright &copy; 2012 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
#include <boost/python/detail/wrap_python.hpp>

namespace Mantid { namespace PythonInterface {
namespace Environment
{

/// Saves a pointer to the PyThreadState of the main thread
void saveMainThreadState(PyThreadState *threadState);

/**
* Defines a structure for creating and destroying a
* Python thread state using the RAII pattern
*/
struct PythonThreadState
{
explicit PythonThreadState();
~PythonThreadState();
private:
PythonThreadState(const PythonThreadState&);
PythonThreadState& operator=(const PythonThreadState&);

PyThreadState * m_mainThreadState;
PyThreadState * m_thisThreadState;
};

/**
* Defines a structure for acquiring/releasing the Python GIL
* using the RAII pattern
*/
struct GlobalInterpreterLock
{
GlobalInterpreterLock() : m_state(PyGILState_Ensure())
{}

~GlobalInterpreterLock()
{
PyGILState_Release(m_state);
}
private:
/// Current GIL state
PyGILState_STATE m_state;
};
}
}}


#endif /* MANTID_PYTHONAPI_PYTHONTHREADING_H_ */
@@ -6,6 +6,7 @@
//-----------------------------------------------------------------------------
#include "MantidKernel/System.h"
#include <boost/python/wrapper.hpp>
#include <stdexcept>

namespace Mantid
{
@@ -40,6 +41,19 @@ namespace Mantid
bool DLLExport typeHasAttribute(PyObject * obj, const char * attr);
/// An overload for the above taking a wrapper reference
bool DLLExport typeHasAttribute(const boost::python::detail::wrapper_base & wrapper, const char * attr);

/**
* Defines an exception for an undefined attribute
*/
class UndefinedAttributeError : std::runtime_error
{
public:
/// Construct the exception
UndefinedAttributeError() :
std::runtime_error("")
{
}
};
}
}
}
@@ -28,6 +28,7 @@
//-----------------------------------------------------------------------------
#include "MantidKernel/Instantiator.h"
#include "MantidAPI/IAlgorithm.h"
#include "MantidPythonInterface/kernel/Environment/Threading.h"

#include <boost/python/object.hpp>
#include <boost/python/extract.hpp>
@@ -89,6 +90,7 @@ namespace Mantid
Base * PythonObjectInstantiator<Base>::createUnwrappedInstance() const
{
using namespace boost::python;
Environment::GlobalInterpreterLock gil;
PyObject *alg = PyObject_CallObject(m_classObject.ptr(), NULL); // No args
object wrap(handle<>(borrowed(alg))); // borrowed: Do not decrement reference count on destruction
Base *ptr = extract<Base*>(wrap);
@@ -40,28 +40,3 @@
from mantid.kernel import config as _cfg
# Disabled for the time being as all algorithms are of the old kind
#_plugins.load(_cfg['pythonalgorithm.directories'])

###############################################################################
# When in GUI mode we want to be picky about algorithm execution as we
# currently can't run the scripts in a separate thread:
#
# Full managed algorithms called from a script - asynchronous
# Child algorithms called from with Python algorithms - synchronous
# So that users can just all alg.execute() and have it do the right thing we will
# replace the default method with the selective one here
###############################################################################
import mantid
if mantid.__gui__ == True:
# Save the original function
_orig_execute = IAlgorithm.execute
import _qti
def _execute_wrapper(self):
if hasattr(self, '__async__') and self.__async__:
success = _qti.app.mantidUI.runAlgorithmAsync_PyCallback(self.name())
if not success:
raise RuntimeError('An error occurred while running %s. See results log for details.' % self.name())
else:
_orig_execute(self)

# Replace the attribute
setattr(IAlgorithm, 'execute', _execute_wrapper)
@@ -8,6 +8,8 @@
#include "MantidKernel/Strings.h"
#include "MantidPythonInterface/kernel/SharedPtrToPythonMacro.h"

#include <Poco/Thread.h>

#include <boost/python/object.hpp>
#include <boost/python/bases.hpp>
#include <boost/python/class.hpp>
@@ -164,6 +166,47 @@ namespace
return buffer.str();
}

struct AllowCThreads
{
AllowCThreads() : m_tracefunc(NULL), m_tracearg(NULL), m_saved(NULL)
{
PyThreadState *curThreadState = PyThreadState_GET();
assert(curThreadState != NULL);
m_tracefunc = curThreadState->c_tracefunc;
m_tracearg = curThreadState->c_traceobj;
Py_XINCREF(m_tracearg);
PyEval_SetTrace(NULL,NULL);
m_saved = PyEval_SaveThread();
}
~AllowCThreads()
{
PyEval_RestoreThread(m_saved);
PyEval_SetTrace(m_tracefunc, m_tracearg);
Py_XDECREF(m_tracearg);
}
private:
Py_tracefunc m_tracefunc;
PyObject *m_tracearg;
PyThreadState *m_saved;
};

/**
* Releases the GIL and disables any tracer functions, executes the calling algorithm object
* and re-acquires the GIL and resets the tracing functions.
* The trace functions are disabled as they can seriously hamper performance of Python algorithms
*
* As an algorithm is a potentially time-consuming operation, this allows other threads
* to execute python code while this thread is executing C code
* @param self :: A reference to the calling object
*/
bool executeWhileReleasingGIL(IAlgorithm & self)
{
bool result(false);
AllowCThreads threadStateHolder;
result = self.execute();
return result;
}

}

void export_ialgorithm()
@@ -195,7 +238,7 @@ void export_ialgorithm()
"are NOT stored in the Analysis Data Service but must be retrieved from the property.")
.def("setLogging", &IAlgorithm::setLogging, "Toggle logging on/off.")
.def("initialize", &IAlgorithm::initialize, "Initializes the algorithm")
.def("execute", &IAlgorithm::execute, "Runs the algorithm")
.def("execute", &executeWhileReleasingGIL, "Runs the algorithm and returns whether it has been successful")
// Special methods
.def("__str__", &IAlgorithm::toString)
;
@@ -1,8 +1,8 @@
#include "MantidPythonInterface/api/PythonAlgorithm/AlgorithmWrapper.h"
#include "MantidPythonInterface/kernel/Environment/WrapperHelpers.h"
#include <boost/python/class.hpp>
#include "MantidPythonInterface/kernel/Environment/CallMethod.h"

using namespace boost::python;
#include <boost/python/class.hpp>

//-----------------------------------------------------------------------------
// AlgorithmWrapper definition
@@ -11,34 +11,24 @@ namespace Mantid
{
namespace PythonInterface
{
using namespace boost::python;
using Environment::CallMethod_NoArg;

/**
* Returns the name of the algorithm. If not overridden
* it returns the class name
*/
const std::string AlgorithmWrapper::name() const
{
static const char * method = "name";
std::string name;
if( Environment::typeHasAttribute(*this, method) )
{
name = call<std::string>(get_override(method).ptr()); // Avoid a warning with just calling return fn() which docs say you can do.
}
else
{
name = this->defaultName();
}
return name;
return CallMethod_NoArg<std::string>::dispatchWithDefaultReturn(getSelf(), "name", defaultName());
}

/**
* Returns the base class version of name
*/
const std::string AlgorithmWrapper::defaultName() const
{
// Use the class name
PyObject *self = boost::python::detail::wrapper_base_::get_owner(*this);
return std::string(self->ob_type->tp_name);
return std::string(getSelf()->ob_type->tp_name);
}

/**
@@ -47,17 +37,7 @@ namespace Mantid
*/
int AlgorithmWrapper::version() const
{
static const char * method = "version";
int version(1);
if( Environment::typeHasAttribute(*this, method) )
{
version = call<int>(get_override(method).ptr()); // Avoid a warning with just calling return fn() which docs say you can do.
}
else
{
version = this->defaultVersion();
}
return version;
return CallMethod_NoArg<int>::dispatchWithDefaultReturn(getSelf(), "version", defaultVersion());
}

int AlgorithmWrapper::defaultVersion() const
@@ -71,13 +51,16 @@ namespace Mantid
*/
const std::string AlgorithmWrapper::category() const
{
static const char * method = "category";
std::string cat("PythonAlgorithms");
if ( Environment::typeHasAttribute(*this, method) )
{
cat = call<std::string>(this->get_override(method).ptr());
}
return cat;
return CallMethod_NoArg<std::string>::dispatchWithDefaultReturn(getSelf(), "category", defaultCategory());
}

/**
* A default category, chosen if there is no override
* @returns A default category
*/
std::string AlgorithmWrapper::defaultCategory() const
{
return "PythonAlgorithms";
}

/**
@@ -86,16 +69,10 @@ namespace Mantid
*/
void AlgorithmWrapper::init()
{
if( boost::python::override fn = this->get_override("PyInit") )
{
fn();
}
else
{
std::ostringstream os;
os << "Python algorithm '" << this->name() << "' does not define the PyInit function, cannot initialize.";
throw std::runtime_error(os.str());
}
std::ostringstream os;
os << "Python algorithm '" << this->name()
<< "' does not define the PyInit function, cannot initialize.";
CallMethod_NoArg<void>::dispatchWithException(getSelf(), "PyInit", os.str().c_str());
}

/**
@@ -104,19 +81,19 @@ namespace Mantid
*/
void AlgorithmWrapper::exec()
{

std::ostringstream os;
os << "Python algorithm '" << this->name()
<< "' does not define the PyExec function, cannot execute.";
CallMethod_NoArg<void>::dispatchWithException(getSelf(), "PyExec", os.str().c_str());
}

if( boost::python::override fn = this->get_override("PyExec") )
{
//fn();
throw Kernel::Exception::NotImplementedError("Sort out the threading!!!!!");
}
else
{
std::ostringstream os;
os << "Python algorithm '" << this->name() << "' does not define the PyExec function, cannot execute.";
throw std::runtime_error(os.str());
}
/**
* Returns the PyObject that owns this wrapper, i.e. self
* @returns A pointer to self
*/
PyObject * AlgorithmWrapper::getSelf() const
{
return boost::python::detail::wrapper_base_::get_owner(*this);
}

}
@@ -39,6 +39,8 @@ set ( SRC_FILES
src/Registry/TypeRegistry.cpp
src/Registry/UpcastRegistry.cpp
src/Environment/CallStack.cpp
src/Environment/CallMethod.cpp
src/Environment/Threading.cpp
src/Environment/WrapperHelpers.cpp
)

@@ -53,6 +55,8 @@ set ( INC_FILES
${HEADER_DIR}/kernel/Converters/PyObjectToV3D.h
${HEADER_DIR}/kernel/Converters/PySequenceToVector.h
${HEADER_DIR}/kernel/Environment/CallStack.h
${HEADER_DIR}/kernel/Environment/CallMethod.h
${HEADER_DIR}/kernel/Environment/Threading.h
${HEADER_DIR}/kernel/Environment/WrapperHelpers.h
${HEADER_DIR}/kernel/Policies/MatrixToNumpy.h
${HEADER_DIR}/kernel/Policies/VectorToNumpy.h
@@ -0,0 +1,78 @@
//-------------------------------------------
// Includes
//-------------------------------------------
#include "MantidPythonInterface/kernel/Environment/CallMethod.h"
#include <sstream>
#include <frameobject.h>

using std::stringstream;

namespace Mantid
{
namespace PythonInterface
{
namespace Environment
{
namespace
{
void tracebackToMsg(stringstream & msg, PyTracebackObject* traceback, bool root=true)
{
if(traceback == NULL) return;
msg << "\n ";
if (root)
msg << "at";
else
msg << "caused by";

msg << " line " << traceback->tb_lineno
<< " in \'" << PyString_AsString(traceback->tb_frame->f_code->co_filename) << "\'";
tracebackToMsg(msg, traceback->tb_next, false);
}
}

/**
* Convert a python error state to a C++ exception
*/
void translateErrorToException(const bool withTrace)
{
GlobalInterpreterLock gil;
if( !PyErr_Occurred() )
{
boost::python::throw_error_already_set();
return;
}
PyObject *exception(NULL), *value(NULL), *traceback(NULL);
PyErr_Fetch(&exception, &value, &traceback);
PyErr_NormalizeException(&exception, &value, &traceback);
PyErr_Clear();
PyObject *str_repr = PyObject_Str(value);
stringstream msg;
msg << "Python error: ";
if( value && str_repr )
{
msg << PyString_AsString(str_repr);
}
else
{
msg << "Unknown exception has occurred.";
}
if (withTrace)
{
tracebackToMsg(msg, (PyTracebackObject *)(traceback));
}

// Ensure we decrement the reference count on the traceback and exception
// objects as they hold references to local the stack variables that were
// present when the exception was raised. This could include child algorithms
// with workspaces stored that would not otherwise be cleaned up until the
// program exited.
Py_XDECREF(traceback);
Py_XDECREF(exception);
Py_XDECREF(value);
// Raise this error as a C++ error
throw std::runtime_error(msg.str());
}

}
}
}
@@ -0,0 +1,54 @@
#include "MantidPythonInterface/kernel/Environment/Threading.h"
#include <Poco/Thread.h>

namespace // <anonymous>
{
/// The main thread state
PyThreadState *g_mainThreadState = NULL;
}

namespace Mantid
{
namespace PythonInterface
{
namespace Environment
{

/// Saves a pointer to the PyThreadState of the main thread
void saveMainThreadState(PyThreadState *threadState)
{
g_mainThreadState = threadState;
}

//-----------------------------------------------------------------------------
// PythonThreadState
//-----------------------------------------------------------------------------

PythonThreadState::PythonThreadState()
: m_mainThreadState(NULL), m_thisThreadState(NULL)
{
m_mainThreadState = g_mainThreadState;
assert(m_mainThreadState);
if(Poco::Thread::current())
{
PyEval_AcquireLock();
PyInterpreterState * mainInterpreterState = m_mainThreadState->interp;
m_thisThreadState = PyThreadState_New(mainInterpreterState);
PyThreadState_Swap(m_thisThreadState);
}
}

PythonThreadState::~PythonThreadState()
{
if(m_thisThreadState)
{
PyThreadState_Swap(m_mainThreadState);
PyThreadState_Clear(m_thisThreadState);
PyThreadState_Delete(m_thisThreadState);
PyEval_ReleaseLock();
}
}

}
}
}
@@ -8,6 +8,7 @@

#include "MantidKernel/MantidVersion.h"
#include "MantidPythonInterface/kernel/Registry/TypeRegistry.h"
#include "MantidPythonInterface/kernel/Environment/Threading.h"

// See http://docs.scipy.org/doc/numpy/reference/c-api.array.html#PY_ARRAY_UNIQUE_SYMBOL
#define PY_ARRAY_UNIQUE_SYMBOL KERNEL_ARRAY_API
@@ -21,6 +22,8 @@ using boost::python::def;

BOOST_PYTHON_MODULE(_kernel)
{
Mantid::PythonInterface::Environment::saveMainThreadState(PyThreadState_Get());

// Doc string options - User defined, python arguments, C++ call signatures
boost::python::docstring_options docstrings(true, true, false);
// Import numpy
@@ -59,7 +59,7 @@ void SetupEQSANSReduction::init()
declareProperty("BeamCenterY", EMPTY_DBL(), "Position of the beam center, in pixel");

// Option 2: Find it (expose properties from FindCenterOfMass)
declareProperty(new API::FileProperty("BeamCenterFile", "", API::FileProperty::OptionalLoad, ".nxs"),
declareProperty(new API::FileProperty("BeamCenterFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
"The name of the input event Nexus file to load");
declareProperty("Tolerance", EMPTY_DBL(), "Tolerance on the center of mass position between each iteration [m]. Default: 0.00125");
auto positiveDouble = boost::make_shared<BoundedValidator<double> >();
@@ -76,19 +76,19 @@ void SetupEQSANSReduction::init()
setPropertyGroup("BeamRadius", center_grp);

// Dark current
declareProperty(new API::FileProperty("DarkCurrentFile", "", API::FileProperty::OptionalLoad, ".nxs"),
declareProperty(new API::FileProperty("DarkCurrentFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
"The name of the input event Nexus file to load as dark current.");

// Sensitivity
std::string eff_grp = "Sensitivity";
declareProperty(new API::FileProperty("SensitivityFile", "", API::FileProperty::OptionalLoad, ".nxs"),
declareProperty(new API::FileProperty("SensitivityFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
"Flood field or sensitivity file.");
declareProperty("MinEfficiency", EMPTY_DBL(), positiveDouble,
"Minimum efficiency for a pixel to be considered (default: no minimum).");
declareProperty("MaxEfficiency", EMPTY_DBL(), positiveDouble,
"Maximum efficiency for a pixel to be considered (default: no maximum).");
declareProperty("UseDefaultDC", true, "If true, the dark current subtracted from the sample data will also be subtracted from the flood field.");
declareProperty(new API::FileProperty("SensitivityDarkCurrentFile", "", API::FileProperty::OptionalLoad, ".nxs"),
declareProperty(new API::FileProperty("SensitivityDarkCurrentFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
"The name of the input file to load as dark current.");
declareProperty("SensitivityBeamCenterX", EMPTY_DBL(), "Position of the beam center for the sensitivity data, in pixel");
declareProperty("SensitivityBeamCenterY", EMPTY_DBL(), "Position of the beam center for the sensitivity data, in pixel");