@@ -57,20 +57,19 @@ namespace Algorithms
"InputWorkspace (optional) to take information for the instrument, and where to evaluate the x-axis.");

this->declareProperty(
new Kernel::ArrayProperty<double>("BinningParameters", boost::make_shared<Kernel::RebinParamsValidator>()),
new Kernel::ArrayProperty<double>("BinningParameters", boost::make_shared<Kernel::RebinParamsValidator>(true)),
"A comma separated list of first bin boundary, width, last bin boundary. Optionally\n"
"this can be followed by a comma and more widths and last boundary pairs.\n"
"Negative width values indicate logarithmic binning.");


this->declareProperty("NumberWidths", 2., "Number of peak width to evaluate each peak for. Default=2.");
this->declareProperty(new API::WorkspaceProperty<API::MatrixWorkspace>("OutputWorkspace", "", Direction::Output),
"Output Workspace to put the calculated data.");

this->declareProperty("GeneratePeaks", true, "Whether or not to generate the peaks");

this->declareProperty("GenerateBackground", true, "Whether or not to generate the background");

this->declareProperty("MaxAllowedChi2", 100.0, "Maximum chi^2 of the peak allowed to calculate.");
this->declareProperty("MaxAllowedChi2", 100.0, "Maximum chi^2 of the peak allowed to calculate. Default 100.");

return;
}
@@ -81,116 +80,174 @@ namespace Algorithms
void GeneratePeaks::exec()
{
// 1. Get properties
DataObjects::TableWorkspace_sptr mPeakParametersWS = this->getProperty("PeakParametersWorkspace");
std::string mPeakFunction = this->getProperty("PeakFunction");

mInputWS = this->getProperty("InputWorkspace");
const std::vector<double> mBinParameters = this->getProperty("BinningParameters");
DataObjects::TableWorkspace_sptr peakParamWS = this->getProperty("PeakParametersWorkspace");
std::string peakFunction = this->getProperty("PeakFunction");

mGeneratePeak = this->getProperty("GeneratePeaks");
mGenerateBackground = this->getProperty("GenerateBackground");
const std::vector<double> binParameters = this->getProperty("BinningParameters");

// 2. Understand input workspace
std::set<specid_t> spectra;
getSpectraSet(mPeakParametersWS, spectra);
getSpectraSet(peakParamWS, spectra);

// 3. Set output workspace
API::MatrixWorkspace_sptr mOutputWorkspace;
if (!mInputWS && mBinParameters.empty())
API::MatrixWorkspace_sptr outputWS;
API::MatrixWorkspace_const_sptr inputWS = this->getProperty("InputWorkspace");
bool newWSFromParent = true;
if (!inputWS && binParameters.empty())
{
// a) Nothing is setup
g_log.error("Must define either InputWorkspace or BinningParameters.");
throw std::invalid_argument("Must define either InputWorkspace or BinningParameters.");
}
else if (mInputWS)
else if (inputWS)
{
// c) Generate Workspace2D from input workspace
if (!mBinParameters.empty())
if (!binParameters.empty())
g_log.notice() << "Both binning parameters and input workspace are given. "
<< "Using input worksapce to generate output workspace!" << std::endl;

mOutputWorkspace = API::WorkspaceFactory::Instance().create(mInputWS, mInputWS->getNumberHistograms(),
mInputWS->dataX(0).size(), mInputWS->dataY(0).size());
outputWS = API::WorkspaceFactory::Instance().create(inputWS, inputWS->getNumberHistograms(),
inputWS->dataX(0).size(), inputWS->dataY(0).size());

std::set<specid_t>::iterator siter;
for (siter = spectra.begin(); siter != spectra.end(); ++siter)
{
specid_t iws = *siter;
std::copy(mInputWS->dataX(iws).begin(), mInputWS->dataX(iws).end(), mOutputWorkspace->dataX(iws).begin());
std::copy(inputWS->dataX(iws).begin(), inputWS->dataX(iws).end(), outputWS->dataX(iws).begin());
}

mNewWSFromParent = true;
newWSFromParent = true;
}
else
{
// d) Generate a one-spectrum Workspace2D from binning
mOutputWorkspace = createOutputWorkspace(spectra, mBinParameters);
mNewWSFromParent = false;
outputWS = createOutputWorkspace(spectra, binParameters);
newWSFromParent = false;
}

this->setProperty("OutputWorkspace", mOutputWorkspace);
this->setProperty("OutputWorkspace", outputWS);

// 4. Generate peaks
generatePeaks(mOutputWorkspace, mPeakParametersWS, mPeakFunction);
generatePeaks(outputWS, peakParamWS, peakFunction, newWSFromParent);

return;
}

namespace { // anonymous name space
/**
* Determine if the table contains raw parameters.
*/
bool isRawTable(const std::vector<std::string> & colNames)
{
if (colNames.size() != 6)
return true;
if (colNames[0].compare("centre") != 0)
return true;
if (colNames[1].compare("width") != 0)
return true;
if (colNames[2].compare("height") != 0)
return true;
if (colNames[3].compare("backgroundintercept") != 0)
return true;
if (colNames[4].compare("backgroundslope") != 0)
return true;
if (colNames[5].compare("A2") != 0)
return true;
return false;
}

/**
* Determine how many parameters are in the peak function.
*/
std::size_t getBkgOffset(const std::vector<std::string> & colNames, const bool isRaw)
{
if (!isRaw)
return 3;

for (std::size_t i = 0; i < colNames.size(); i++)
{
if (colNames[i].substr(0,3).compare("f1.") == 0)
return i;
}
return colNames.size(); // shouldn't get here
}
}

/*
* Generate peaks
*/
void GeneratePeaks::generatePeaks(API::MatrixWorkspace_sptr dataWS, DataObjects::TableWorkspace_const_sptr peakparameters,
std::string peakfunction)
std::string peakfunction, bool newWSFromParent)
{
double maxchi2 = this->getProperty("MaxAllowedChi2");
double numWidths = this->getProperty("NumberWidths");
bool generateBackground = this->getProperty("GenerateBackground");

size_t numpeaks = peakparameters->rowCount();

// get the parameter names for the peak function
std::vector<std::string> columnNames = peakparameters->getColumnNames();
if (!columnNames.front().compare("spectrum") == 0)
{
std::stringstream msg;
msg << "First column of table should be \"spectrum\". Found \"" << columnNames.front() << "\"";
throw std::invalid_argument(msg.str());
}
columnNames.erase(columnNames.begin()); // don't need to know that name
if (!columnNames.back().compare("chi2") == 0)
{
std::stringstream msg;
msg << "Last column of table should be \"chi2\". Found \"" << columnNames.back() << "\"";
throw std::invalid_argument(msg.str());
}
columnNames.erase(columnNames.begin()+(columnNames.size()-1)); // don't need to know that name either
const bool isRaw = isRawTable(columnNames);
const std::size_t bkgOffset = getBkgOffset(columnNames, isRaw);
g_log.information() << "isRaw=" << isRaw << " bkgOffset=" << bkgOffset << "\n";

for (size_t ipk = 0; ipk < numpeaks; ipk ++)
{
// 1. Get to know workspace index
specid_t specid = static_cast<specid_t>(getTableValue(peakparameters, "spectrum", ipk));
specid_t wsindex;
if (mNewWSFromParent)
if (newWSFromParent)
wsindex = specid;
else
wsindex = mSpectrumMap[specid];

// 2. Get peak parameters
double centre = getTableValue(peakparameters, "centre", ipk);
double width = getTableValue(peakparameters, "width", ipk);
double height = getTableValue(peakparameters, "height", ipk);
double a0 = getTableValue(peakparameters, "backgroundintercept", ipk);
double a1 = getTableValue(peakparameters, "backgroundslope", ipk);
double a2 = getTableValue(peakparameters, "A2", ipk);
// 2. Generate peak function
double chi2 = getTableValue(peakparameters, "chi2", ipk);

g_log.debug() << "Peak " << ipk << ": Spec = " << specid << " -> WSIndex = " << wsindex <<
" center = " << centre << ", height = " << height << ", sigma = " << width <<
" a0 = " << a0 << ", a1 = " << a1 << ", a2 = " << a2 << std::endl;

double centre, fwhm; // output parameters
auto mfunc = createFunction(peakfunction, columnNames, isRaw, generateBackground, peakparameters, bkgOffset, ipk,
centre, fwhm);
if (chi2 > maxchi2)
{
g_log.notice() << "Skip Peak " << ipk << ": chi^2 = " << chi2 << " Larger than max. allowed chi^2" << std::endl;
g_log.notice() << "Skip Peak " << ipk << " (chi^2 " << chi2 << " > " << maxchi2 << ") at " << centre << "\n";
continue;
}
g_log.debug() << "Peak " << ipk << ": Spec = " << specid << " -> WSIndex = " << wsindex
<< " func: " << mfunc->asString() << "\n";


// 3. Build domain & function
// TODO Can make this part more smart to sum over only a portion of X range
API::FunctionDomain1DVector domain(dataWS->dataX(wsindex));
API::IFunction_sptr mfunc = this->createFunction(peakfunction, height, centre, width,
a0, a1, a2, mGeneratePeak, mGenerateBackground);
const MantidVec& X = dataWS->dataX(wsindex);
std::vector<double>::const_iterator left = std::lower_bound(X.begin(), X.end(), centre - numWidths*fwhm);
if (left == X.end())
left = X.begin();
std::vector<double>::const_iterator right = std::lower_bound(left + 1, X.end(), centre + numWidths*fwhm);
API::FunctionDomain1DVector domain(left, right); //dataWS->dataX(wsindex));

// 4. Evaluate the function
API::FunctionValues values(domain);
mfunc->function(domain, values);

// 4. Put to output
PARALLEL_FOR1(dataWS)
for (int i = 0; i < static_cast<int>(dataWS->dataY(wsindex).size()); i ++)
// 5. Put to output
std::size_t offset = (left-X.begin());
std::size_t numY = values.size();
for (std::size_t i = 0; i < numY; i ++)
{
PARALLEL_START_INTERUPT_REGION
dataWS->dataY(wsindex)[i] += values[i];
PARALLEL_END_INTERUPT_REGION
dataWS->dataY(wsindex)[i + offset] += values[i];
}
PARALLEL_CHECK_INTERUPT_REGION

} // for peak

@@ -199,43 +256,63 @@ namespace Algorithms

/**
* Create a function for fitting.
* @param withPeak If this is set to false then return only a background function.
* @return The requested function to fit.
*/
API::IFunction_sptr GeneratePeaks::createFunction(std::string m_peakFuncType,
const double height, const double centre, const double sigma,
const double a0, const double a1, const double a2, const bool withPeak, const bool withBackground)
API::IFunction_sptr GeneratePeaks::createFunction(const std::string &peakFuncType, const std::vector<std::string> &colNames,
const bool isRaw, const bool withBackground,
DataObjects::TableWorkspace_const_sptr peakParmsWS,
const std::size_t bkg_offset, const std::size_t rowNum, double &centre, double &fwhm)
{

// 1. Create return
API::CompositeFunction* fitFunc = new CompositeFunction();

// 2. setup the background
if (withBackground)
// create the peak
auto tempPeakFunc = API::FunctionFactory::Instance().createFunction(peakFuncType);
auto peakFunc = boost::dynamic_pointer_cast<IPeakFunction>(tempPeakFunc);
if (isRaw)
{
auto background =
API::FunctionFactory::Instance().createFunction("QuadraticBackground");
std::string paramName;
for (std::size_t i = 0; i < bkg_offset; i++)
{
paramName = colNames[i].substr(3);
peakFunc->setParameter(paramName, getTableValue(peakParmsWS, colNames[i], rowNum));
}
}
else
{
peakFunc->setHeight(getTableValue(peakParmsWS, "height", rowNum));
peakFunc->setCentre(getTableValue(peakParmsWS, "centre", rowNum));
peakFunc->setFwhm(getTableValue(peakParmsWS, "width", rowNum));
}
centre = peakFunc->centre();
fwhm = peakFunc->fwhm();

background->setParameter("A0", a0);
background->setParameter("A1", a1);
background->setParameter("A2", a2);
// skip out early
if (!withBackground)
return boost::shared_ptr<IFunction>(peakFunc);

fitFunc->addFunction(background);
}

// 3. setup the peak
if (withPeak)
// create the background
auto backFunc =
API::FunctionFactory::Instance().createFunction("QuadraticBackground");
if (isRaw)
{
std::string paramName;
for (std::size_t i = bkg_offset; i < colNames.size(); i++)
{
paramName = colNames[i].substr(3);
backFunc->setParameter(paramName, getTableValue(peakParmsWS, colNames[i], rowNum));
}
}
else
{
auto tempPeakFunc = API::FunctionFactory::Instance().createFunction(m_peakFuncType);
auto peakFunc = boost::dynamic_pointer_cast<IPeakFunction>(tempPeakFunc);
peakFunc->setHeight(height);
peakFunc->setCentre(centre);
peakFunc->setFwhm(sigma);

if (withPeak)
fitFunc->addFunction(peakFunc);
backFunc->setParameter("A0", getTableValue(peakParmsWS, "backgroundintercept", rowNum));
backFunc->setParameter("A1", getTableValue(peakParmsWS, "backgroundslope", rowNum));
backFunc->setParameter("A2", getTableValue(peakParmsWS, "A2", rowNum));
}

// setup the output
API::CompositeFunction* fitFunc = new CompositeFunction();
fitFunc->addFunction(peakFunc);
fitFunc->addFunction(backFunc);

return boost::shared_ptr<IFunction>(fitFunc);
}

@@ -8,13 +8,17 @@
#include "MantidAPI/NumericAxis.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/PeaksWorkspace.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidAlgorithms/CreatePeaksWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidGeometry/Instrument.h"

using Mantid::API::MatrixWorkspace;
using Mantid::API::MatrixWorkspace_sptr;
using Mantid::API::WorkspaceGroup_sptr;

using Mantid::DataObjects::EventWorkspace_sptr;
using namespace Mantid::Algorithms;
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace Mantid::Geometry;

class CheckWorkspacesMatchTest : public CxxTest::TestSuite
{
@@ -59,6 +63,84 @@ class CheckWorkspacesMatchTest : public CxxTest::TestSuite
TS_ASSERT( Mantid::API::equals(ws, ws) );
}

void testPeaks_matches()
{
if ( !checker.isInitialized() ) checker.initialize();

std::string outWS1Name("CreatePeaks1WorkspaceTest_OutputWS");
std::string outWS2Name("CreatePeaks2WorkspaceTest_OutputWS");

Workspace2D_sptr instws = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2, 10);

CreatePeaksWorkspace alg;
TS_ASSERT_THROWS_NOTHING( alg.initialize() )
TS_ASSERT( alg.isInitialized() )
TS_ASSERT_THROWS_NOTHING( alg.setProperty("InstrumentWorkspace", boost::dynamic_pointer_cast<MatrixWorkspace>(instws)) );
TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWS1Name) );
TS_ASSERT_THROWS_NOTHING( alg.setProperty("NumberOfPeaks", 13) );
TS_ASSERT_THROWS_NOTHING( alg.execute(); )
TS_ASSERT( alg.isExecuted() );

TS_ASSERT_THROWS_NOTHING( alg.initialize() )
TS_ASSERT( alg.isInitialized() )
TS_ASSERT_THROWS_NOTHING( alg.setProperty("InstrumentWorkspace", boost::dynamic_pointer_cast<MatrixWorkspace>(instws)) );
TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWS2Name) );
TS_ASSERT_THROWS_NOTHING( alg.setProperty("NumberOfPeaks", 13) );
TS_ASSERT_THROWS_NOTHING( alg.execute(); )
TS_ASSERT( alg.isExecuted() );

// Retrieve the workspace from data service.
PeaksWorkspace_sptr pws1, pws2;
TS_ASSERT_THROWS_NOTHING( pws1 = boost::dynamic_pointer_cast<PeaksWorkspace>(
AnalysisDataService::Instance().retrieve(outWS1Name) ) );
TS_ASSERT_THROWS_NOTHING( pws2 = boost::dynamic_pointer_cast<PeaksWorkspace>(
AnalysisDataService::Instance().retrieve(outWS2Name) ) );
TS_ASSERT_THROWS_NOTHING( checker.setProperty("Workspace1",boost::dynamic_pointer_cast<Workspace>(pws1)) );
TS_ASSERT_THROWS_NOTHING( checker.setProperty("Workspace2",boost::dynamic_pointer_cast<Workspace>(pws2)) );
TS_ASSERT( checker.execute() );
TS_ASSERT_EQUALS( checker.getPropertyValue("Result"), checker.successString() );
}

void testPeaks_extrapeak()
{
if ( !checker.isInitialized() ) checker.initialize();

std::string outWS3Name("CreatePeaks3WorkspaceTest_OutputWS");
std::string outWS4Name("CreatePeaks4WorkspaceTest_OutputWS");

Workspace2D_sptr instws = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2, 10);

CreatePeaksWorkspace alg;
TS_ASSERT_THROWS_NOTHING( alg.initialize() )
TS_ASSERT( alg.isInitialized() )
TS_ASSERT_THROWS_NOTHING( alg.setProperty("InstrumentWorkspace", boost::dynamic_pointer_cast<MatrixWorkspace>(instws)) );
TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWS3Name) );
TS_ASSERT_THROWS_NOTHING( alg.setProperty("NumberOfPeaks", 13) );
TS_ASSERT_THROWS_NOTHING( alg.execute(); )
TS_ASSERT( alg.isExecuted() );

TS_ASSERT_THROWS_NOTHING( alg.initialize() )
TS_ASSERT( alg.isInitialized() )
TS_ASSERT_THROWS_NOTHING( alg.setProperty("InstrumentWorkspace", boost::dynamic_pointer_cast<MatrixWorkspace>(instws)) );
TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWS4Name) );
TS_ASSERT_THROWS_NOTHING( alg.setProperty("NumberOfPeaks", 14) );
TS_ASSERT_THROWS_NOTHING( alg.execute(); )
TS_ASSERT( alg.isExecuted() );

// Retrieve the workspace from data service.
PeaksWorkspace_sptr pws1, pws2;
TS_ASSERT_THROWS_NOTHING( pws1 = boost::dynamic_pointer_cast<PeaksWorkspace>(
AnalysisDataService::Instance().retrieve(outWS3Name) ) );
TS_ASSERT_THROWS_NOTHING( pws2 = boost::dynamic_pointer_cast<PeaksWorkspace>(
AnalysisDataService::Instance().retrieve(outWS4Name) ) );
TS_ASSERT_EQUALS(pws1->getNumberPeaks(), 13);
TS_ASSERT_EQUALS(pws2->getNumberPeaks(), 14);
TS_ASSERT_THROWS_NOTHING( checker.setProperty("Workspace1",boost::dynamic_pointer_cast<Workspace>(pws1)) );
TS_ASSERT_THROWS_NOTHING( checker.setProperty("Workspace2",boost::dynamic_pointer_cast<Workspace>(pws2)) );
TS_ASSERT( checker.execute() );
TS_ASSERT_DIFFERS( checker.getPropertyValue("Result"), checker.successString() );
}

void testEvent_matches()
{
if ( !checker.isInitialized() ) checker.initialize();
@@ -67,7 +67,6 @@ class GeneratePeaksTest : public CxxTest::TestSuite
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));

@@ -131,7 +130,6 @@ class GeneratePeaksTest : public CxxTest::TestSuite
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));

@@ -180,7 +178,7 @@ class GeneratePeaksTest : public CxxTest::TestSuite
/*
* Test to use user-provided binning parameters
*/
void test_BackgroundOnly()
void test_Background()
{
// 1. Create input
DataObjects::TableWorkspace_sptr peakparmsws = createTestPeakParameters();
@@ -193,7 +191,6 @@ class GeneratePeaksTest : public CxxTest::TestSuite
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));

@@ -213,17 +210,17 @@ class GeneratePeaksTest : public CxxTest::TestSuite
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);
TS_ASSERT_DELTA(p0_y[200], 10.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);
TS_ASSERT_DELTA(p0_y[800], 20.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);
TS_ASSERT_DELTA(p1_y[400], 24.0, 1.0E-4);

// 7. Spectrum map
spec2index_map *themap = peaksws->getSpectrumToWorkspaceIndexMap();
@@ -107,8 +107,7 @@ namespace Crystal
boost::shared_ptr< Parameter > rot = pmapSv->get(bank_const.get(),("rot"));
if( rot)
{
Parameter rotP = (*rot);
pmap->addQuat(bank_const.get(), "rot",rotP.value<Quat>());
pmap->addQuat(bank_const.get(), "rot",rot->value<Quat>());


}
@@ -265,8 +265,7 @@ namespace Mantid
boost::shared_ptr<Parameter> rot = pmapSv->get(bank_const.get(),("rot"));
if( rot)
{
Parameter rotP = (*rot);
pmap->addQuat(bank_const.get(), "rot",rotP.value<Quat>());
pmap->addQuat(bank_const.get(), "rot",rot->value<Quat>());
}

vector<double> scalex = pmapSv->getDouble(bank_const->getName(),"scalex");
@@ -230,20 +230,18 @@ namespace DataHandling
{
g_log.debug() << "Group ID = " << it->first << std::endl;

for (size_t i = 0; i < it->second.size()/2; i ++){
g_log.debug() << "Detector From = " << it->second[2*i] << ", " << it->second[2*i+1] << std::endl;
for (size_t i = 0; i < it->second.size(); i ++){
detid_t detid = it->second[i];
detid2index_map::iterator itx = indexmap->find(detid);

for (detid_t detid = it->second[2*i]; detid <= it->second[2*i+1]; detid ++)
if (itx != indexmap->end())
{
detid2index_map::iterator itx = indexmap->find(detid);
if (itx != indexmap->end()){
size_t wsindex = itx->second;
mGroupWS->dataY(wsindex)[0] = it->first;
} else {
g_log.error() << "Pixel w/ ID = " << detid << " Cannot Be Located" << std::endl;
}
} // ENDFOR detid (in range)
} // ENDFOR each range
size_t wsindex = itx->second;
mGroupWS->dataY(wsindex)[0] = it->first;
} else {
g_log.error() << "Pixel w/ ID = " << detid << " Cannot Be Located" << std::endl;
}
} // ENDFOR detid (in range)
} // ENDFOR each group ID

// 3. Clear
@@ -618,7 +616,7 @@ namespace DataHandling
}

/*
* Parse a,b-c,d,... string to a vector
* Parse a,b-c,d,... string to a vector in 1-1 mapping
*/
void LoadGroupXMLFile::parseDetectorIDs(std::string inputstring, std::vector<detid_t>& detids)
{
@@ -628,16 +626,17 @@ namespace DataHandling
std::vector<int32_t> pairs;
this->parseRangeText(inputstring, singles, pairs);

// 2. Store single detectors... ..., detx, detx, ...
for (size_t i = 0; i < singles.size(); i ++){
detids.push_back(singles[i]);
// 2. Store single detectors... ..., detx,
for (size_t i = 0; i < singles.size(); i ++)
{
detids.push_back(singles[i]);
}

// 3. Store detectors pairs
for (size_t i = 0; i < pairs.size()/2; i ++){
detids.push_back(pairs[2*i]);
detids.push_back(pairs[2*i+1]);
// 3. Store detectors parsed in pairs, det0., det1, ... detN
for (size_t i = 0; i < pairs.size()/2; i ++)
{
for (detid_t detid = pairs[2*i]; detid <= pairs[2*i+1]; detid ++)
detids.push_back(detid);
}

return;
@@ -126,7 +126,7 @@ namespace DataObjects

//---------------------------------------------------------------------------------------------
/// Returns a vector of all column names.
virtual std::vector<std::string> getColumnNames()
virtual std::vector<std::string> getColumnNames() const
{
return this->columnNames;
}
@@ -108,7 +108,7 @@ namespace DataObjects
/// Gets the shared pointer to a column by index - return none-modifyable column.
API::Column_const_sptr getColumn(size_t index) const;
/// Returns a vector of all column names.
std::vector<std::string> getColumnNames();
std::vector<std::string> getColumnNames() const;
/// Number of rows in the workspace.
size_t rowCount() const {return m_rowCount;}
/// Resizes the workspace.
@@ -178,11 +178,11 @@ namespace Mantid
--m_rowCount;
}

std::vector<std::string> TableWorkspace::getColumnNames()
std::vector<std::string> TableWorkspace::getColumnNames() const
{
std::vector<std::string> nameList;
nameList.reserve(m_columns.size());
for(column_it ci=m_columns.begin();ci!=m_columns.end();ci++)
for(auto ci=m_columns.begin();ci!=m_columns.end();ci++)
nameList.push_back((*ci)->name());
return nameList;
}
@@ -69,7 +69,7 @@ namespace Mantid
* as the download method I've written is private I can't access that in unit testing.
* so adding this public method to call the private download method and testing.
*/
void testDownload(const std::string& URL,const std::string& fileName);
std::string testDownload(const std::string& URL,const std::string& fileName);

private:
/// Sets documentation strings for this algorithm
@@ -81,17 +81,14 @@ namespace Mantid
/// get location of data file or download method
int doDownload( ICATPortBindingProxy & icat);

/// This method is used when the download is done over internet
void downloadFileOverInternet(const std::string& url,const std::string& fileName);

/// If the extn of the file .raw it returns true
bool isDataFile(const std::string& fileName);

/// This method saves the downloaded file to disc
void saveFiletoDisk(std::istream& rs,const std::string &fileName);
std::string saveFiletoDisk(std::istream& rs,const std::string &fileName);

/// This method saves downloaded file to local disk
void doDownloadandSavetoLocalDrive(const std::string& URL,const std::string& fileName);
std::string doDownloadandSavetoLocalDrive(const std::string& URL,const std::string& fileName);

/// This method replaces backwardslash with forward slashes - for linux
void replaceBackwardSlash(std::string& inputString);
@@ -121,15 +121,13 @@ namespace Mantid
catalog_sptr->getDownloadURL(*citr1,url);

progress(prog,"downloading over internet...");
//now download the files from the data server to the machine where mantid is installed
downloadFileOverInternet(url,*citr2);
//get the download directory name
std::string downloadPath( Kernel::ConfigService::Instance().getString("defaultsave.directory"));
std::string downloadedFName=downloadPath+(*citr2);
//replace "\" with "/"
replaceBackwardSlash(downloadedFName);
//set the filelocation property
filelocations.push_back(downloadedFName);

// Download file from the data server to the machine where mantid is installed
std::string fullPathDownloadedFile = doDownloadandSavetoLocalDrive(url,*citr2);

// replace "\" with "/" before adding to filelocations
replaceBackwardSlash(fullPathDownloadedFile);
filelocations.push_back(fullPathDownloadedFile);
}

}
@@ -139,18 +137,6 @@ namespace Mantid

}


/**This method calls ICat API downloadDatafile and gets the URL string and uses the URL to down load file server
* @param url :: url of the file to download.
* @param fileName :: name of the file to be saved to disk
*/
void CatalogDownloadDataFiles::downloadFileOverInternet(const std::string & url,const std::string& fileName)
{
//download using Poco HttpClient session and save to local disk
doDownloadandSavetoLocalDrive(url,fileName);

}

/** This method checks the file extn and if it's a raw file reurns true
* This is useful when the we download a file over internet and save to local drive,
* to open the file in binary or ascii mode
@@ -175,9 +161,12 @@ namespace Mantid
/** This method downloads file over internet using Poco HTTPClientSession
* @param URL- URL of the file to down load
* @param fileName :: file name
* @return Full path of where file is saved to
*/
void CatalogDownloadDataFiles::doDownloadandSavetoLocalDrive(const std::string& URL,const std::string& fileName)
std::string CatalogDownloadDataFiles::doDownloadandSavetoLocalDrive(const std::string& URL,const std::string& fileName)
{
std::string retVal_FullPath;

clock_t start;
//use HTTP Get method to download the data file from the server to local disk
try
@@ -197,7 +186,7 @@ namespace Mantid
HTTPResponse res;
std::istream& rs = session.receiveResponse(res);
//save file to local disk
saveFiletoDisk(rs,fileName);
retVal_FullPath = saveFiletoDisk(rs,fileName);

clock_t end=clock();
float diff = float(end - start)/CLOCKS_PER_SEC;
@@ -210,20 +199,22 @@ namespace Mantid
}
catch(Poco::Exception&)
{

throw std::runtime_error("Can not download the file "+fileName +". Path is invalid for the file.");
}

return retVal_FullPath;
}

/** This method saves the input stream to a file
* @param rs :: input stream
* @param fileName :: name of the output file
* @return Full path of where file is saved to
*/
void CatalogDownloadDataFiles::saveFiletoDisk(std::istream& rs,const std::string& fileName)
std::string CatalogDownloadDataFiles::saveFiletoDisk(std::istream& rs,const std::string& fileName)
{
std::string filepath ( Kernel::ConfigService::Instance().getString("defaultsave.directory"));
filepath += fileName;
Poco::Path defaultSaveDir(Kernel::ConfigService::Instance().getString("defaultsave.directory"));
Poco::Path path(defaultSaveDir, fileName);
std::string filepath = path.toString();

std::ios_base::openmode mode;
//if raw/nexus file open it in binary mode else ascii
@@ -236,6 +227,7 @@ namespace Mantid
//copy the input stream to a file.
StreamCopier::copyStream(rs, ofs);

return filepath;
}

/** This method is used for unit testing purpose.
@@ -245,10 +237,11 @@ namespace Mantid
* so adding this public method to call the private downlaod method and testing.
* @param URL :: URL of the file to download
* @param fileName :: name of the file
* @return Full path of where file is saved to
*/
void CatalogDownloadDataFiles::testDownload(const std::string& URL,const std::string& fileName)
std::string CatalogDownloadDataFiles::testDownload(const std::string& URL,const std::string& fileName)
{
doDownloadandSavetoLocalDrive(URL,fileName);
return doDownloadandSavetoLocalDrive(URL,fileName);

}
/** This method replaces backward slash with forward slash for linux compatibility.
@@ -13,6 +13,7 @@
#include <iomanip>
#include <fstream>
#include <Poco/File.h>
#include <Poco/Path.h>

using namespace Mantid;
using namespace Mantid::ICat;
@@ -249,18 +250,19 @@ class CatalogDownloadDataFilesTest: public CxxTest::TestSuite

CatalogDownloadDataFiles downloadobj1;
clock_t start=clock();
downloadobj1.testDownload("http://download.mantidproject.org/videos/Installation.htm","test.htm");
std::string fullPathDownloadedFile = downloadobj1.testDownload("http://download.mantidproject.org/videos/Installation.htm","test.htm");
clock_t end=clock();
float diff = float(end -start)/CLOCKS_PER_SEC;

ofs<<"Time taken for http download from mantidwebserver over internet for a small file of size 1KB is "<<std::fixed << std::setprecision(2) << diff << " seconds" << std::endl;

//delete the file after execution
remove("test.htm");
// Clean up test files
// std::string htmPath = Kernel::ConfigService::Instance().getString("defaultsave.directory") + "/test.htm";
// if (Poco::File(htmPath).exists()) Poco::File(htmPath).remove();
// if (Poco::File(filepath).exists()) Poco::File(filepath).remove();

// test if fullPathDownloadedFile ok
Poco::Path defaultSaveDir(Kernel::ConfigService::Instance().getString("defaultsave.directory"));
Poco::Path path(defaultSaveDir, "test.htm");
TS_ASSERT( fullPathDownloadedFile == path.toString() );
}

private:
@@ -187,9 +187,6 @@ class DLLExport DataService
*/
virtual void add( const std::string& name, const boost::shared_ptr<T>& Tobject)
{
// Make DataService access thread-safe
Poco::Mutex::ScopedLock _lock(m_mutex);

// Don't permit an empty name for the workspace
if (name.empty())
{
@@ -198,6 +195,9 @@ class DLLExport DataService
throw std::runtime_error(error);
}

// Make DataService access thread-safe
m_mutex.lock();

// At the moment, you can't overwrite a workspace (i.e. pass in a name
// that's already in the map with a pointer to a different workspace).
// Also, there's nothing to stop the same workspace from being added
@@ -206,11 +206,13 @@ class DLLExport DataService
{
std::string error=" add : Unable to insert Data Object : '"+name+"'";
g_log.error(error);
m_mutex.unlock();
throw std::runtime_error(error);
}
else
{
g_log.debug() << "Add Data Object " << name << " successful" << std::endl;
m_mutex.unlock();

//check the workspace invisible option set
std::string name_startswith=name.substr(0,2);
@@ -247,7 +249,11 @@ class DLLExport DataService
if (it!=datamap.end())
{
g_log.debug("Data Object '"+ foundName +"' replaced in data service.\n");
m_mutex.unlock();

notificationCenter.postNotification(new BeforeReplaceNotification(name,it->second,Tobject));

m_mutex.lock();
datamap[foundName] = Tobject;

std::string name_startswith=name.substr(0,2);
@@ -261,8 +267,8 @@ class DLLExport DataService
return;
}
}
notificationCenter.postNotification(new AfterReplaceNotification(name,Tobject));
m_mutex.unlock();
notificationCenter.postNotification(new AfterReplaceNotification(name,Tobject));
}
else
{
@@ -279,20 +285,24 @@ class DLLExport DataService
void remove( const std::string& name)
{
// Make DataService access thread-safe
Poco::Mutex::ScopedLock _lock(m_mutex);
m_mutex.lock();

std::string foundName;
svc_it it = this->findNameWithCaseSearch(name, foundName);
if (it==datamap.end())
{
g_log.warning(" remove '" + name + "' cannot be found");
m_mutex.unlock();
return;
}

m_mutex.unlock();
notificationCenter.postNotification(new PreDeleteNotification(foundName,it->second));
m_mutex.lock();

g_log.information("Data Object '"+ foundName +"' deleted from data service.");
datamap.erase(it);
//API::MemoryManager::Instance().releaseFreeMemory();

m_mutex.unlock();
notificationCenter.postNotification(new PostDeleteNotification(foundName));
return;
}
@@ -302,9 +312,10 @@ class DLLExport DataService
void clear()
{
// Make DataService access thread-safe
Poco::Mutex::ScopedLock _lock(m_mutex);
m_mutex.lock();

datamap.clear();
m_mutex.unlock();
notificationCenter.postNotification(new ClearNotification());
g_log.debug() << typeid(this).name() << " cleared.\n";
}
@@ -39,12 +39,13 @@ namespace Kernel
class MANTID_KERNEL_DLL RebinParamsValidator : public TypedValidator<std::vector<double> >
{
public:
RebinParamsValidator() {}
RebinParamsValidator(bool allowEmpty=false);
virtual ~RebinParamsValidator() {}
IValidator_sptr clone() const { return boost::make_shared<RebinParamsValidator>(*this); }

private:
std::string checkValidity( const std::vector<double> &value ) const;
bool m_allowEmpty;
};

} // namespace Kernel
@@ -76,6 +76,19 @@ class MANTID_KERNEL_DLL Unit
void toTOF(std::vector<double>& xdata, std::vector<double>& ydata, const double& l1, const double& l2,
const double& twoTheta, const int& emode, const double& efixed, const double& delta);

/** Convert from the concrete unit to time-of-flight. TOF is in microseconds.
* @param xvalue :: A single X-value to convert
* @param l1 :: The source-sample distance (in metres)
* @param l2 :: The sample-detector distance (in metres)
* @param twoTheta :: The scattering angle (in radians)
* @param emode :: The energy mode (0=elastic, 1=direct geometry, 2=indirect geometry)
* @param efixed :: Value of fixed energy: EI (emode=1) or EF (emode=2) (in meV)
* @param delta :: Not currently used
* @return the value in TOF units.
*/
double convertSingleToTOF(const double xvalue, const double& l1, const double& l2,
const double& twoTheta, const int& emode, const double& efixed, const double& delta);

/** Convert from time-of-flight to the concrete unit. TOF is in microseconds.
* @param xdata :: The array of X data to be converted
* @param ydata :: Not currently used (ConvertUnits passes an empty vector)
@@ -89,6 +102,19 @@ class MANTID_KERNEL_DLL Unit
void fromTOF(std::vector<double>& xdata, std::vector<double>& ydata, const double& l1, const double& l2,
const double& twoTheta, const int& emode, const double& efixed, const double& delta);

/** Convert from the time-of-flight to the concrete unit. TOF is in microseconds.
* @param xvalue :: A single X-value to convert
* @param l1 :: The source-sample distance (in metres)
* @param l2 :: The sample-detector distance (in metres)
* @param twoTheta :: The scattering angle (in radians)
* @param emode :: The energy mode (0=elastic, 1=direct geometry, 2=indirect geometry)
* @param efixed :: Value of fixed energy: EI (emode=1) or EF (emode=2) (in meV)
* @param delta :: Not currently used
* @return the value in these units.
*/
double convertSingleFromTOF(const double xvalue, const double& l1, const double& l2,
const double& twoTheta, const int& emode, const double& efixed, const double& delta);

/** Initialize the unit to perform conversion using singleToTof() and singleFromTof()
*
* @param _l1 :: The source-sample distance (in metres)
@@ -4,6 +4,8 @@ namespace Mantid
{
namespace Kernel
{
RebinParamsValidator::RebinParamsValidator(bool allowEmpty):m_allowEmpty(allowEmpty)
{}

/** Check on the inputed bin boundaries and widths.
* @param value :: The parameter array to check
@@ -12,7 +14,13 @@ namespace Kernel
std::string RebinParamsValidator::checkValidity( const std::vector<double>& value ) const
{
// array must not be empty
if ( value.empty() ) return "Enter values for this property";
if ( value.empty() )
{
if (m_allowEmpty) // unless allowed in the constructor
return "";
else
return "Enter values for this property";
}

// it must have an odd number of values (and be at least 3 elements long)
if ( value.size()%2 == 0 )
@@ -119,6 +119,14 @@ void Unit::toTOF(std::vector<double>& xdata, std::vector<double>& ydata, const d
}


/** Convert a single value to TOF */
double Unit::convertSingleToTOF(const double xvalue, const double& l1, const double& l2,
const double& twoTheta, const int& emode, const double& efixed, const double& delta)
{
this->initialize(l1,l2, twoTheta, emode, efixed, delta);
return this->singleToTOF(xvalue);
}

//---------------------------------------------------------------------------------------
/** Perform the conversion to TOF on a vector of data */
void Unit::fromTOF(std::vector<double>& xdata, std::vector<double>& ydata, const double& _l1, const double& _l2,
@@ -132,6 +140,14 @@ void Unit::fromTOF(std::vector<double>& xdata, std::vector<double>& ydata, const
}


/** Convert a single value from TOF */
double Unit::convertSingleFromTOF(const double xvalue, const double& l1, const double& l2,
const double& twoTheta, const int& emode, const double& efixed, const double& delta)
{
this->initialize(l1,l2, twoTheta, emode, efixed, delta);
return this->singleFromTOF(xvalue);
}


namespace Units
{
@@ -11,6 +11,7 @@
#include <iostream>
#include <Poco/NObserver.h>
#include <vector>
#include "MantidKernel/MultiThreaded.h"

using namespace Mantid;
using namespace Mantid::Kernel;
@@ -32,6 +33,7 @@ class DataServiceTest : public CxxTest::TestSuite
DumbDataServiceImpl svc;

std::vector<int> vector;
Mutex m_vectorMutex;

void setUp()
{
@@ -85,9 +87,9 @@ class DataServiceTest : public CxxTest::TestSuite
// Handler for an observer, called each time an object is added
void handleAddNotification(const Poco::AutoPtr<DumbDataServiceImpl::AddNotification> & )
{
// This operation is not thread safe!
// So it will segfault if called in parallel.
m_vectorMutex.lock();
vector.push_back(123);
m_vectorMutex.unlock();
}

void test_threadSafety()
@@ -151,6 +151,8 @@ class UnitTest : public CxxTest::TestSuite
TS_ASSERT_THROWS_NOTHING( lambda.toTOF(x,y,1.0,1.0,1.0,1,1.0,1.0) )
TS_ASSERT_DELTA( x[0], 2665.4390, 0.0001 ) // 758.3352
TS_ASSERT( yy == y )

TS_ASSERT_DELTA( lambda.convertSingleToTOF(1.5, 1.0,1.0,1.0,1,1.0,1.0), 2665.4390, 0.0001 );
}

void testWavelength_fromTOF()
@@ -160,6 +162,8 @@ class UnitTest : public CxxTest::TestSuite
TS_ASSERT_THROWS_NOTHING( lambda.fromTOF(x,y,1.0,1.0,1.0,1,1.0,1.0) )
TS_ASSERT_DELTA( x[0], -5.0865, 0.0001 ) // 1.979006
TS_ASSERT( yy == y )

TS_ASSERT_DELTA( lambda.convertSingleFromTOF(1000.5, 1.0,1.0,1.0,1,1.0,1.0), -5.0865, 0.0001);
}

void testWavelength_quickConversions()
@@ -408,7 +408,8 @@ namespace MDAlgorithms
/// MergeMDFiles Algorithm - used to pass parameters etc. around
MergeMDFiles * m_alg;
/// Which block to load?
size_t m_blockNum;
size_t m_blockNumStart;
size_t m_blockNumEnd;
/// Output workspace
typename MDEventWorkspace<MDE, nd>::sptr outWS;
/// List of boxes where index = box ID, value = the box pointer.
@@ -419,120 +420,158 @@ namespace MDAlgorithms
/** Constructor
*
* @param alg :: MergeMDFiles Algorithm - used to pass parameters etc. around
* @param blockNum :: Which block to load?
* @param blockNumStart :: Start block ID to load.
* @param blockNumStart :: End block ID to load (non-inclusive)
* @param outWS :: Output workspace
* @param boxesById :: list of boxes with IDs
* @param parallelSplit :: if true, split the boxes via parallel mechanism
*/
MergeMDLoadToBoxTask(MergeMDFiles * alg, size_t blockNum, typename MDEventWorkspace<MDE, nd>::sptr outWS,
MergeMDLoadToBoxTask(MergeMDFiles * alg, size_t blockNumStart, size_t blockNumEnd,
typename MDEventWorkspace<MDE, nd>::sptr outWS,
typename std::vector<MDBoxBase<MDE,nd> *> & boxesById, bool parallelSplit)
: m_alg(alg), m_blockNum(blockNum), outWS(outWS),
: m_alg(alg), m_blockNumStart(blockNumStart), m_blockNumEnd(blockNumEnd),
outWS(outWS),
m_boxesById(boxesById), m_parallelSplit(parallelSplit)
{
this->m_cost = double(this->m_alg->eventsPerBox[this->m_blockNum]);
this->m_cost = double(this->m_alg->eventsPerBox[this->m_blockNumStart]);
}

//---------------------------------------------------------------------------------------------
/** Main method that performs the work for the task. */
void run()
{
uint64_t numEvents = this->m_alg->eventsPerBox[this->m_blockNum];
if (numEvents == 0) return;

// Find the box in the output.
MDBoxBase<MDE,nd> * outBox = this->m_boxesById[this->m_blockNum];
if (!outBox)
throw std::runtime_error("Could not find box at ID " + Strings::toString(this->m_blockNum) );
BoxController_sptr bc = outBox->getBoxController();

// Should we pre-emptively split the box
MDBox<MDE,nd> * outMDBox = dynamic_cast<MDBox<MDE,nd> *>(outBox);
if (outMDBox && (numEvents > bc->getSplitThreshold()))
// Vector of each vector of events in each box
std::vector<std::vector<MDE> > eventsPerBox(m_blockNumEnd-m_blockNumStart);
// Each of the outputted boxes
std::vector<MDBoxBase<MDE,nd> *> outputBoxes(m_blockNumEnd-m_blockNumStart);
// Running total of processed events
size_t totalEvents = 0;
// Box controller of the workspace
BoxController_sptr bc = outWS->getBoxController();

// ----------------- Prepare the boxes for each box -----------------------------------------
for (size_t blockNum = this->m_blockNumStart; blockNum < this->m_blockNumEnd; blockNum++)
{
// Yes, let's split it
MDGridBox<MDE,nd> * parent = dynamic_cast<MDGridBox<MDE,nd> *>(outMDBox->getParent());
if (parent)
uint64_t numEvents = this->m_alg->eventsPerBox[blockNum];
if (numEvents == 0) continue;

// Find the box in the output.
MDBoxBase<MDE,nd> * outBox = this->m_boxesById[blockNum];
if (!outBox)
throw std::runtime_error("Could not find box at ID " + Strings::toString(blockNum) );

// Should we pre-emptively split the box
MDBox<MDE,nd> * outMDBox = dynamic_cast<MDBox<MDE,nd> *>(outBox);
if (outMDBox && (numEvents > bc->getSplitThreshold()))
{
size_t index = parent->getChildIndexFromID( outBox->getId() );
if (index < parent->getNumChildren())
// Yes, let's split it
MDGridBox<MDE,nd> * parent = dynamic_cast<MDGridBox<MDE,nd> *>(outMDBox->getParent());
if (parent)
{
parent->splitContents(index);
// Have to update our pointer - old one was deleted!
outBox = parent->getChild(index);
size_t index = parent->getChildIndexFromID( outBox->getId() );
if (index < parent->getNumChildren())
{
parent->splitContents(index);
// Have to update our pointer - old one was deleted!
outBox = parent->getChild(index);
}
}
}
}
// Is the output a grid box?
MDGridBox<MDE,nd> * outGridBox = dynamic_cast<MDGridBox<MDE,nd> *>(outBox);

// Save the output box for later
outputBoxes[blockNum-this->m_blockNumStart] = outBox;

// Vector of events accumulated from ALL files to merge.
std::vector<MDE> events;
// Vector of events accumulated from ALL files to merge.
std::vector<MDE> & events = eventsPerBox[blockNum-this->m_blockNumStart];

// Occasionally release free memory (has an effect on Linux only).
if (numEvents > 1000000)
MemoryManager::Instance().releaseFreeMemory();
// Reserve ALL the space you will need for this vector. Should speed up a lot.
events.reserve(numEvents);
} // (for each blockNum)

// Reserve ALL the space you will need for this vector. Should speed up a lot.
events.reserve(numEvents);

// Go through each file
// --------------------- Go through each file -------------------------------------------
this->m_alg->fileMutex.lock();
//bc->fileMutex.lock();
for (size_t iw=0; iw<this->m_alg->files.size(); iw++)
{
// The file and the indexes into that file
::NeXus::File * file = this->m_alg->files[iw];
std::vector<uint64_t> & box_event_index = this->m_alg->box_indexes[iw];

uint64_t indexStart = box_event_index[this->m_blockNum*2+0];
uint64_t numEvents = box_event_index[this->m_blockNum*2+1];
// This will APPEND the events to the one vector
MDE::loadVectorFromNexusSlab(events, file, indexStart, numEvents);
for (size_t blockNum = this->m_blockNumStart; blockNum < this->m_blockNumEnd; blockNum++)
{
// This is the events vector for this particular block we are adding to
std::vector<MDE> & events = eventsPerBox[blockNum-this->m_blockNumStart];

// The file and the indexes into that file
::NeXus::File * file = this->m_alg->files[iw];
std::vector<uint64_t> & box_event_index = this->m_alg->box_indexes[iw];

uint64_t indexStart = box_event_index[blockNum*2+0];
uint64_t numEvents = box_event_index[blockNum*2+1];
if (numEvents == 0) continue;
totalEvents += numEvents;

// Occasionally release free memory (has an effect on Linux only).
if (numEvents > 1000000)
MemoryManager::Instance().releaseFreeMemory();

// This will APPEND the events to the one vector
MDE::loadVectorFromNexusSlab(events, file, indexStart, numEvents);
} // For each block
} // For each file
//bc->fileMutex.unlock();
this->m_alg->fileMutex.unlock();

if (!events.empty())

// -------------- Now we actually do the adding for each block --------------------------------------
for (size_t blockNum = this->m_blockNumStart; blockNum < this->m_blockNumEnd; blockNum++)
{
// Add all the events from the same box
outBox->addEventsUnsafe( events );
events.clear();
std::vector<MDE>().swap(events); // really free the data
// This is the events vector for this particular block we are adding to
std::vector<MDE> & events = eventsPerBox[blockNum-this->m_blockNumStart];

if (outGridBox)
if (!events.empty())
{
// Occasionally release free memory (has an effect on Linux only).
MemoryManager::Instance().releaseFreeMemory();
// Box we are adding to.
MDBoxBase<MDE,nd> * outBox = outputBoxes[blockNum-this->m_blockNumStart];

// Is the output a grid box?
MDGridBox<MDE,nd> * outGridBox = dynamic_cast<MDGridBox<MDE,nd> *>(outBox);

// Now do a split on only this box.
// Add all the events from the same box
outBox->addEventsUnsafe( events );
events.clear();
std::vector<MDE>().swap(events); // really free the data

// On option, do the split in parallel
if (m_parallelSplit)
if (outGridBox)
{
ThreadSchedulerFIFO * ts = new ThreadSchedulerFIFO();
ThreadPool tp(ts);
outGridBox->splitAllIfNeeded(ts);
tp.joinAll();
// Occasionally release free memory (has an effect on Linux only).
MemoryManager::Instance().releaseFreeMemory();

// Now do a split on only this box.

// On option, do the split in parallel
if (m_parallelSplit)
{
ThreadSchedulerFIFO * ts = new ThreadSchedulerFIFO();
ThreadPool tp(ts);
outGridBox->splitAllIfNeeded(ts);
tp.joinAll();
}
else
outGridBox->splitAllIfNeeded(NULL);

}
else
outGridBox->splitAllIfNeeded(NULL);

// HDF5 is NOT thread safe (by default) even for accessing DIFFERENT files from different threads.
// Hence we need this mutex here :(
this->m_alg->fileMutex.lock();
// Flush out any items to write.
bc->getDiskBuffer().flushCache();
this->m_alg->fileMutex.unlock();
}
} // there was something loaded
} // there was something loaded
} // (for each block)

// HDF5 is NOT thread safe (by default) even for accessing DIFFERENT files from different threads.
// Hence we need this mutex here :(
this->m_alg->fileMutex.lock();
// Flush out any items to write.
bc->getDiskBuffer().flushCache();
this->m_alg->fileMutex.unlock();

// Track the total number of added events
this->m_alg->statsMutex.lock();
this->m_alg->totalLoaded += numEvents;
this->m_alg->getLogger().debug() << "Box " << this->m_blockNum << ". Total events " << this->m_alg->totalLoaded << ". This one added " << numEvents << ". "<< std::endl;
this->m_alg->totalLoaded += totalEvents;
this->m_alg->getLogger().debug() << "Boxes " << this->m_blockNumStart << " to " << this->m_blockNumEnd << ". Total events " << this->m_alg->totalLoaded << ". These added " << totalEvents << ". "<< std::endl;
// Report the progress
this->m_alg->prog->reportIncrement(int(numEvents), "Loading Box");
this->m_alg->prog->reportIncrement(int(totalEvents), "Loading Box");
this->m_alg->statsMutex.unlock();

} // (end run)
@@ -583,27 +622,36 @@ namespace MDAlgorithms
ThreadSchedulerFIFO * ts = new ThreadSchedulerFIFO();
ThreadPool tp(ts);

for (size_t ib=0; ib<numBoxes; ib++)
size_t last_ib = 0;
size_t ib = 0;
while (ib < numBoxes)
{
// Add a task for each box that actually has some events
if (this->eventsPerBox[ib] > 0)
// Increase ib until you collect enough events
size_t accumulatedEvents = 0;
while (accumulatedEvents < 10000000 && ib < numBoxes)
{
totalEventsInTasks += eventsPerBox[ib];
MergeMDLoadToBoxTask<MDE,nd> * task = new MergeMDLoadToBoxTask<MDE,nd>(this, ib, outWS, boxesById, !Parallel);
accumulatedEvents += eventsPerBox[ib];
ib++;
}

if (!Parallel)
{
// Run the task serially only
task->run();
delete task;
}
else
{
// Enqueue to run in parallel (at the joinAll() call below).
ts->push(task);
}
totalEventsInTasks += accumulatedEvents;
MergeMDLoadToBoxTask<MDE,nd> * task = new MergeMDLoadToBoxTask<MDE,nd>(this, last_ib, ib, outWS, boxesById, !Parallel);

if (!Parallel)
{
// Run the task serially only
task->run();
delete task;
}
} // for each box
else
{
// Enqueue to run in parallel (at the joinAll() call below).
ts->push(task);
}

// Prepare the next loop
last_ib = ib;
} // (while ib in range)

// Run any final tasks
tp.joinAll();
@@ -296,7 +296,7 @@ namespace MDEvents

// Create the event with signal, error squared,
// the runIndex, the detectorID, and the centers
events.push_back( MDEvent<nd>( float(data[ii]), float(data[ii + 1]),
events.push_back( MDEvent<nd>(float(data[ii]), float(data[ii + 1]),
uint16_t(data[ii + 2]), int32_t(data[ii+3]), centers) );
}

@@ -534,7 +534,7 @@ namespace MDEvents
coord_t * centers = data + ii+2;

// Create the event with signal, error squared, and the centers
events.push_back( MDLeanEvent<nd>( float(data[ii]), float(data[ii + 1]), centers) );
events.push_back( MDLeanEvent<nd>(float(data[ii]), float(data[ii + 1]), centers) );
}

// Release the memory (all has been COPIED into MDLeanEvent's)
@@ -139,15 +139,22 @@ namespace MDEvents
"Correct the weights of events with by multiplying by the Lorentz formula: sin(theta)^2 / lambda^4");

// Box controller properties. These are the defaults
this->initBoxControllerProps("5" /*SplitInto*/, 1500 /*SplitThreshold*/, 20 /*MaxRecursionDepth*/);
this->initBoxControllerProps("2" /*SplitInto*/, 1500 /*SplitThreshold*/, 20 /*MaxRecursionDepth*/);

declareProperty(
new PropertyWithValue<int>("MinRecursionDepth", 0),
"Optional. If specified, then all the boxes will be split to this minimum recursion depth. 1 = one level of splitting, etc.\n"
"Be careful using this since it can quickly create a huge number of boxes = (SplitInto ^ (MinRercursionDepth * NumDimensions)).\n"
"But setting this property equal to MaxRecursionDepth property is necessary if one wants to generate multiple file based workspaces in order to merge them later\n");
setPropertyGroup("MinRecursionDepth", getBoxSettingsGroupName());

std::vector<double> extents(2,0);
extents[0]=-50;extents[1]=+50;
declareProperty(
new ArrayProperty<double>("Extents", extents),
"A comma separated list of min, max for each dimension,\n"
"specifying the extents of each dimension. Optional, default +-50 in each dimension.");

setPropertyGroup("Extents", getBoxSettingsGroupName());
}


@@ -356,10 +363,6 @@ namespace MDEvents
g_log.warning()<<"Lorentz Correction was already done for this workspace. LorentzCorrection was changed to false." << std::endl;
}
}
else
{
run.addProperty<bool>("LorentzCorrection", 1, true);
}
}

m_inEventWS = boost::dynamic_pointer_cast<EventWorkspace>(m_inWS);
@@ -451,6 +454,12 @@ namespace MDEvents
this->setBoxController(bc);
// We always want the box to be split (it will reject bad ones)
ws->splitBox();

// Perform minimum recursion depth splitting
int minDepth = this->getProperty("MinRecursionDepth");
int maxDepth = this->getProperty("MaxRecursionDepth");
if (minDepth>maxDepth) throw std::invalid_argument("MinRecursionDepth must be <= MaxRecursionDepth ");
ws->setMinRecursionDepth(size_t(minDepth));
}

ws->splitBox();
@@ -54,6 +54,8 @@ set ( TEST_PY_FILES
test/RunPythonScriptTest.py
test/SANSMaskCommandsTest.py
test/SANSWorkflowTest.py
test/SimpleAPILoadTest.py
test/SimpleAPIFitTest.py
test/SettingsTest.py
test/WorkspaceGroupTest.py
test/WorkspaceHistoryTest.py
@@ -1157,6 +1157,11 @@ def __iter__(self):
for name in ws_names:
yield mtd[name]

def __contains__(self, key):
"""Returns true if the key is a known workspace in the ADS
"""
return mtd.workspaceExists(key)

###########################################################################
# Framework interface
###########################################################################
@@ -242,7 +242,7 @@ def PyExec(self):
YPixelMin=BackfromYpixel,
YPixelMax=data_peak[0]-1,
NormalizeSum=True)

ws_data_bck_1_rebin = ws_data_bck_1 + '_rebin'
RebinToWorkspace(WorkspaceToRebin=ws_data_bck_1,
WorkspaceToMatch=ws_histo_data,
@@ -281,17 +281,26 @@ def PyExec(self):
Factor=0.5,
Operation="Multiply")

# nbr_x_range = 1./(Xrange[1]-Xrange[0]+1)
# Scale(InputWorkspace=ws_data_bck+'_scale',
# OutputWorkspace=ws_data_bck+'_scale',
# Factor=nbr_x_range,
# Operation="Multiply")

print 'ws_histo_data: ' + ws_histo_data
print 'ws_data: ' + ws_data

Minus(LHSWorkspace=ws_histo_data,
RHSWorkspace=ws_data_bck+'_scale',
OutputWorkspace=ws_data)

mtd.deleteWorkspace(ws_data_bck+'_scale')
# mtd.deleteWorkspace(ws_data_bck+'_scale')
mtd.deleteWorkspace(ws_data_bck)
mtd.deleteWorkspace(ws_data_bck_1_rebin)
mtd.deleteWorkspace(ws_data_bck_2_rebin)
mtd.deleteWorkspace(ws_data_bck_1)
# mtd.deleteWorkspace(ws_data_bck_1_rebin)
# mtd.deleteWorkspace(ws_data_bck_2_rebin)
# mtd.deleteWorkspace(ws_data_bck_1)
mtd.deleteWorkspace(ws_data_bck_2)
mtd.deleteWorkspace(ws_histo_data)
# mtd.deleteWorkspace(ws_histo_data)

elif (bBackLeft):

@@ -309,14 +318,14 @@ def PyExec(self):
mtd.deleteWorkspace(ws_data_bck_2_rebin)
mtd.deleteWorkspace(ws_data_bck_2)

mtd.deleteWorkspace(ws_histo_data)
# mtd.deleteWorkspace(ws_histo_data)

else:

ConvertToMatrixWorkspace(InputWorkspace=ws_histo_data,
OutputWorkspace=ws_data)

mtd.deleteWorkspace(ws_histo_data)
# mtd.deleteWorkspace(ws_histo_data)


if (NormFlag):
@@ -545,6 +554,9 @@ def PyExec(self):
geo_correction=False,
q_binning=[q_min,q_step,q_max])




print '-> replace special values'
mt = mtd[ws_data_Q]
ReplaceSpecialValues(InputWorkspace=ws_data_Q,
@@ -562,6 +574,21 @@ def PyExec(self):
print '-> sum spectra'
SumSpectra(InputWorkspace=ws_data_Q, OutputWorkspace=output_ws)




ReplaceSpecialValues(InputWorkspace=ws_integrated_data,
NaNValue=0,
NaNError=0,
InfinityValue=0,
InfinityError=0,
OutputWorkspace='_tmp1')
SumSpectra(InputWorkspace='_tmp1', OutputWorkspace='_tmp2')





#keep only none zero values
try:
print '-> keep only non-zeros values'
@@ -132,6 +132,17 @@ def algorithm_wrapper(*args, **kwargs):
alias = alias.strip()
if len(alias)>0:
globals()["%sDialog" % alias] = algorithm_wrapper

__SPECIALIZED_FUNCTIONS__ = ["Load", "Fit"]

def specialization_exists(name):
"""
Returns true if a specialization for the given name
already exists, false otherwise
@param name :: The name of a possible new function
"""
return name in __SPECIALIZED_FUNCTIONS__

def Load(*args, **kwargs):
"""
@@ -168,34 +179,7 @@ def Load(*args, **kwargs):
# A mix of keyword and non-keyword is also possible
Load('event_ws', Filename='INSTR_1000_event.nxs',Precount=True)
"""
# Small inner function to grab the mandatory arguments and translate possible
# exceptions
def get_argument_value(key, kwargs):
try:
value = kwargs[key]
del kwargs[key]
return value
except KeyError:
raise RuntimeError('%s argument not supplied to Load function' % str(key))

if len(args) == 2:
filename = args[0]
wkspace = args[1]
elif len(args) == 1:
if 'Filename' in kwargs:
wkspace = args[0]
filename = get_argument_value('Filename', kwargs)
elif 'OutputWorkspace' in kwargs:
filename = args[0]
wkspace = get_argument_value('OutputWorkspace', kwargs)
else:
raise RuntimeError('Cannot find "Filename" or "OutputWorkspace" in key word list. '
'Cannot use single positional argument.')
elif len(args) == 0:
filename = get_argument_value('Filename', kwargs)
wkspace = get_argument_value('OutputWorkspace', kwargs)
else:
raise RuntimeError('Load() takes at most 2 positional arguments, %d found.' % len(args))
filename, wkspace = get_mandatory_args('Load', ['Filename', 'OutputWorkspace'], *args, **kwargs)

# Create and execute
algm = mtd.createAlgorithm('Load')
@@ -253,17 +237,131 @@ def LoadDialog(*args, **kwargs):
algm.execute()
return algm

def Fit(*args, **kwargs):
"""
Fit defines the interface to the fitting framework within Mantid.
It can work with arbitrary data sources and therefore some options
are only available when the function & workspace type are known.
This simple wrapper takes the Function (as a string) & the InputWorkspace
as the first two arguments. The remaining arguments must be
specified by keyword.
Example:
Fit(Function='name=LinearBackground,A0=0.3', InputWorkspace=dataWS',
StartX='0.05',EndX='1.0',Output="Z1")
"""
Function, InputWorkspace = get_mandatory_args('Fit', ["Function", "InputWorkspace"], *args, **kwargs)
# Check for behaviour consistent with old API
if type(Function) == str and Function in mtd:
raise ValueError("Fit API has changed. The function must now come first in the argument list and the workspace second.")

# Create and execute
algm = mtd.createAlgorithm('Fit')
algm.setPropertyValue('Function', str(Function)) # Must be set first
algm.setPropertyValue('InputWorkspace', str(InputWorkspace))
for key, value in kwargs.iteritems():
try:
algm.setPropertyValue(key, _makeString(value).lstrip('? '))
except RuntimeError:
mtd.sendWarningMessage("You've passed a property (%s) to Fit() that doesn't apply to this workspace type." % key)
algm.execute()
return algm

# Have a better load signature for autocomplete
_signature = "\bFunction,InputWorkspace"
# Getting the code object for Load
_f = Fit.func_code
# Creating a new code object nearly identical, but with the two variable names replaced
# by the property list.
_c = _f.__new__(_f.__class__, _f.co_argcount, _f.co_nlocals, _f.co_stacksize, _f.co_flags, _f.co_code, _f.co_consts, _f.co_names,\
(_signature, "kwargs"), _f.co_filename, _f.co_name, _f.co_firstlineno, _f.co_lnotab, _f.co_freevars)
# Replace the code object of the wrapper function
Fit.func_code = _c

def FitDialog(*args, **kwargs):
"""Popup a dialog for the Fit algorithm. More help on the Fit function
is available via help(Fit).
Additional arguments available here (as keyword only) are:
- Enable :: A CSV list of properties to keep enabled in the dialog
- Disable :: A CSV list of properties to keep enabled in the dialog
- Message :: An optional message string
"""
arguments = {}
try:
function, inputworkspace = get_mandatory_args('FitDialog', ['Function', 'InputWorkspace'], *args, **kwargs)
arguments['Function'] = function
arguments['InputWorkspace'] = inputworkspace
except RuntimeError:
pass
arguments.update(kwargs)
if 'Enable' not in arguments: arguments['Enable']=''
if 'Disable' not in arguments: arguments['Disable']=''
if 'Message' not in arguments: arguments['Message']=''
algm = mtd.createAlgorithm('Fit')
algm.setPropertiesDialog(**arguments)
algm.execute()
return algm

def get_mandatory_args(func_name, required_args ,*args, **kwargs):
"""
Given a list of required arguments, parse them
from the given args & kwargs and raise an error if they
are not provided
@param func_name :: The name of the function call
@param required_args :: A list of names of required arguments
@param args :: The positional arguments to check
@param kwargs :: The keyword arguments to check
@returns A tuple of provided mandatory arguments
"""
def get_argument_value(key, kwargs):
try:
value = kwargs[key]
del kwargs[key]
return value
except KeyError:
raise RuntimeError('%s argument not supplied to %s function' % (str(key), func_name))
nrequired = len(required_args)
npositional = len(args)

if npositional == 0:
mandatory_args = []
for arg in required_args:
mandatory_args.append(get_argument_value(arg, kwargs))
elif npositional == nrequired:
mandatory_args = args
elif npositional < nrequired:
mandatory_args = []
for value in args:
mandatory_args.append(value)
# Get rest from keywords
for arg in required_args[npositional:]:
mandatory_args.append(get_argument_value(arg, kwargs))
else:
reqd_as_str = ','.join(required_args).strip(",")
raise RuntimeError('%s() takes "%s" as positional arguments. Other arguments must be specified by keyword.'
% (func_name, reqd_as_str))
return tuple(mandatory_args)


#------------------------------------------------------------------------------

def translate():
"""
Loop through the algorithms and register a function call
for each of them
"""
for algorithm in mtd._getRegisteredAlgorithms(include_hidden=True):
if algorithm[0] == "Load":
name = algorithm[0]
if specialization_exists(name):
continue
highest_version = max(algorithm[1])
# Create the algorithm object
_algm_object = mtd.createUnmanagedAlgorithm(algorithm[0], max(algorithm[1]))
create_algorithm(algorithm[0], max(algorithm[1]), _algm_object)
create_algorithm_dialog(algorithm[0], max(algorithm[1]), _algm_object)
_algm_object = mtd.createUnmanagedAlgorithm(name, max(algorithm[1]))
create_algorithm(name, highest_version, _algm_object)
create_algorithm_dialog(name, highest_version, _algm_object)

translate()
@@ -0,0 +1,62 @@
"""
Specifically tests the Fit function in the simple API
"""
import unittest
import sys
from mantidsimple import Load, Fit, FitDialog, mtd

# Case difference is to be consistent with the unittest module
def assertRaisesNothing(testobj, callable, *args, **kwargs):
"""
unittest does not have an assertRaisesNothing. This
provides that functionality
Parameters:
testobj - A unittest object
callable - A callable object
*args - Positional arguments passed to the callable as they are
**kwargs - Keyword arguments, passed on as they are
"""
try:
return callable(*args, **kwargs)
except Exception, exc:
testobj.fail("Assertion error. An exception was caught where none was expected in %s. Message: %s"
% (callable.__name__, str(exc)))

class SimpleAPIFitTest(unittest.TestCase):

_raw_ws = None

def setUp(self):
if self._raw_ws is None:
Load('IRS21360.raw',OutputWorkspace='ws', SpectrumMax=1)
self.__class__._raw_ws = mtd['ws']

def test_minimal_positional_arguments_work(self):
assertRaisesNothing(self, Fit, "name=FlatBackground", self._raw_ws)

def test_function_positional_and_workspace_keyword_arguments_work(self):
assertRaisesNothing(self, Fit, "name=FlatBackground", InputWorkspace=self._raw_ws)

def test_function_and_workspace_keyword_arguments_work(self):
assertRaisesNothing(self, Fit, Function="name=FlatBackground", InputWorkspace=self._raw_ws)

def xtest_workspace_is_created_when_output_is_requested(self): # crashes the mac
retvals = Fit("name=FlatBackground", self._raw_ws, Output="fitWS")
sys.__stderr__.write("")
self.assertTrue('fitWS_Workspace' in mtd)

def xtest_other_arguments_are_accepted_by_keyword(self): # crashes the mac
retvals = Fit("name=FlatBackground", self._raw_ws, MaxIterations=10, Output="fitWS")
self.assertTrue('fitWS_Workspace' in mtd)

def test_that_dialog_call_raises_runtime_error(self):
try:
FitDialog()
except RuntimeError, exc:
msg = str(exc)
if msg != "Can only display properties dialog in gui mode":
self.fail("Dialog function raised the correct exception type but the message was wrong: " + msg)

if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,56 @@
"""
Specifically tests the Load function in the simple API
"""
import unittest
from mantidsimple import Load, LoadDialog, mtd

# Case difference is to be consistent with the unittest module
def assertRaisesNothing(testobj, callable, *args, **kwargs):
"""
unittest does not have an assertRaisesNothing. This
provides that functionality
Parameters:
testobj - A unittest object
callable - A callable object
*args - Positional arguments passed to the callable as they are
**kwargs - Keyword arguments, passed on as they are
"""
try:
return callable(*args, **kwargs)
except Exception, exc:
testobj.fail("Assertion error. An exception was caught where none was expected in %s. Message: %s"
% (callable.__name__, str(exc)))

class SimpleAPILoadTest(unittest.TestCase):

def test_Load_call_with_just_filename_and_workspace_executes_correctly(self):
assertRaisesNothing(self, Load, 'IRS21360.raw', 'raw')
self._run_check_and_remove('raw', 116)

def test_Load_call_with_other_args_executes_correctly(self):
assertRaisesNothing(self, Load, 'IRS21360.raw', 'raw', SpectrumMax=1)
self._run_check_and_remove('raw', 1)

def test_Load_call_with_args_that_do_not_apply_executes_correctly(self):
assertRaisesNothing(self, Load, 'IRS21360.raw', 'raw', SpectrumMax=1,Append=True)
self._run_check_and_remove('raw', 1)

def test_that_dialog_call_raises_runtime_error(self):
try:
LoadDialog()
except RuntimeError, exc:
msg = str(exc)
if msg != "Can only display properties dialog in gui mode":
self.fail("Dialog function raised the correct exception type but the message was wrong: " + msg)

def _run_check_and_remove(self, name, expected_nhist):
raw = mtd[name]
self.assertEquals(expected_nhist, raw.getNumberHistograms())
mtd.deleteWorkspace(name)




if __name__ == '__main__':
unittest.main()
@@ -111,6 +111,8 @@ set ( TEST_PY_FILES
test/python/ReferenceFrameTest.py
test/python/RunTest.py
test/python/SimpleAPITest.py
test/python/SimpleAPILoadTest.py
test/python/SimpleAPIFitTest.py
test/python/TimeSeriesPropertyTest.py
test/python/QuatTest.py
test/python/V3DTest.py
@@ -76,6 +76,7 @@ void export_AnalysisDataService()
register_ptr_to_python<DataItem_wptr>();

class_<AnalysisDataServiceImpl,boost::noncopyable>("AnalysisDataServiceImpl", no_init)
.def("doesExist", &AnalysisDataServiceImpl::doesExist, "Returns True if the object is found in the service.")
.def("retrieve", &retrieveAsWeakPtr, return_value_policy<Policies::upcast_returned_value>(),
"Retrieve the named object. Raises an exception if the name does not exist")
.def("remove", &AnalysisDataServiceImpl::remove, "Remove a named object")

Large diffs are not rendered by default.

@@ -0,0 +1,62 @@
"""
Specifically tests the Fit function in the simple API
"""
import unittest
import testhelpers

from mantid.simpleapi import Load, Fit, FitDialog
from mantid import mtd, MatrixWorkspace, ITableWorkspace

class SimpleAPIFitTest(unittest.TestCase):

_raw_ws = None

def setUp(self):
if self._raw_ws is None:
ws = Load('IRS21360.raw',SpectrumMax=1)
self.__class__._raw_ws = ws

def test_minimal_positional_arguments_work(self):
testhelpers.assertRaisesNothing(self, Fit, "name=FlatBackground", self._raw_ws)

def test_function_positional_and_workspace_keyword_arguments_work(self):
testhelpers.assertRaisesNothing(self, Fit, "name=FlatBackground", InputWorkspace=self._raw_ws)

def test_function_and_workspace_keyword_arguments_work(self):
testhelpers.assertRaisesNothing(self, Fit, Function="name=FlatBackground", InputWorkspace=self._raw_ws)

def test_function_returns_are_correct_type_when_no_output_ws_requested(self):
retvals = Fit("name=FlatBackground", self._raw_ws)
self.assertEquals(len(retvals), 2)
self.assertTrue(isinstance(retvals[0], str))
self.assertTrue(isinstance(retvals[1], float))

def xtest_function_returns_are_correct_type_when_output_ws_is_requested(self): #crashes the mac
retvals = Fit("name=FlatBackground", self._raw_ws, Output="fitWS")
self.assertEquals(len(retvals), 5)
self.assertTrue(isinstance(retvals[0], str))
self.assertTrue(isinstance(retvals[1], float))
self.assertTrue(isinstance(retvals[2], ITableWorkspace))
self.assertTrue(isinstance(retvals[3], ITableWorkspace))
self.assertTrue(isinstance(retvals[4], MatrixWorkspace))

def xtest_other_arguments_are_accepted_by_keyword(self): #crashes the mac
retvals = Fit("name=FlatBackground", self._raw_ws, MaxIterations=10, Output="fitWS")
self.assertEquals(len(retvals), 5)
self.assertTrue(isinstance(retvals[0], str))
self.assertTrue(isinstance(retvals[1], float))
self.assertTrue(isinstance(retvals[2], ITableWorkspace))
self.assertTrue(isinstance(retvals[3], ITableWorkspace))
self.assertTrue(isinstance(retvals[4], MatrixWorkspace))

def test_that_dialog_call_raises_runtime_error(self):
try:
FitDialog()
except RuntimeError, exc:
msg = str(exc)
if msg != "Can only display properties dialog in gui mode":
self.fail("Dialog function raised the correct exception type but the message was wrong: " + msg)


if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,48 @@
"""
Specifically tests the Load function in the simple API
"""
import unittest
from mantid.simpleapi import Load, LoadDialog
from mantid import mtd

class SimpleAPILoadTest(unittest.TestCase):

def test_Load_returns_correct_args_when_extra_output_props_are_added_at_execute_time(self):
try:
data, monitors = Load('IRS21360.raw', LoadMonitors='Separate')
except Exception, exc:
self.fail("An error occurred when returning outputs declared at algorithm execution: '%s'" % str(exc))

def test_Load_call_with_just_filename_executes_correctly(self):
try:
raw = Load('IRS21360.raw')
except RuntimeError:
self.fail("Load with a filename should not raise an exception")
self.assertEquals(116, raw.getNumberHistograms())
mtd.remove('raw')

def test_Load_call_with_other_args_executes_correctly(self):
try:
raw = Load('IRS21360.raw',SpectrumMax=1)
except RuntimeError:
self.fail("Load with a filename and extra args should not raise an exception")
self.assertEquals(1, raw.getNumberHistograms())

def test_Load_call_with_args_that_do_not_apply_executes_correctly(self):
try:
raw = Load('IRS21360.raw',SpectrumMax=1,Append=True)
except RuntimeError:
self.fail("Load with a filename and extra args should not raise an exception")
self.assertEquals(1, raw.getNumberHistograms())

def test_that_dialog_call_raises_runtime_error(self):
try:
LoadDialog()
except RuntimeError, exc:
msg = str(exc)
if msg != "Can only display properties dialog in gui mode":
self.fail("Dialog function raised the correct exception type but the message was wrong")


if __name__ == '__main__':
unittest.main()
@@ -91,31 +91,6 @@ def _do_exec_time_props_test(self, runner):

def test_function_returns_correct_args_when_extra_output_props_are_added_at_execute_time(self):
self._do_exec_time_props_test(simpleapi.LoadRaw)

def test_Load_returns_correct_args_when_extra_output_props_are_added_at_execute_time(self):
self._do_exec_time_props_test(simpleapi.Load)

def test_Load_call_with_just_filename_executes_correctly(self):
try:
raw = simpleapi.Load('IRS21360.raw')
except RuntimeError:
self.fail("Load with a filename should not raise an exception")
self.assertEquals(116, raw.getNumberHistograms())
mtd.remove('raw')

def test_Load_call_with_other_args_executes_correctly(self):
try:
raw = simpleapi.Load('IRS21360.raw',SpectrumMax=1)
except RuntimeError:
self.fail("Load with a filename and extra args should not raise an exception")
self.assertEquals(1, raw.getNumberHistograms())

def test_Load_call_with_args_that_do_not_apply_executes_correctly(self):
try:
raw = simpleapi.Load('IRS21360.raw',SpectrumMax=1,Append=True)
except RuntimeError:
self.fail("Load with a filename and extra args should not raise an exception")
self.assertEquals(1, raw.getNumberHistograms())

def test_that_dialog_call_raises_runtime_error(self):
try:
@@ -131,7 +106,7 @@ def convert(workspace):
workspace = simpleapi.ConvertUnits(workspace, Target='Energy')
return workspace

raw = simpleapi.Load('IRS21360.raw',SpectrumMax=1)
raw = simpleapi.LoadRaw('IRS21360.raw',SpectrumMax=1)
raw = convert(raw)
# If this fails then the function above chose the name of the variabe
# over the actual object name
@@ -28,7 +28,7 @@ def run_algorithm(name, **kwargs):
return alg

# Case difference is to be consistent with the unittest module
def assertRaisesNothing(testobj, callable, *args):
def assertRaisesNothing(testobj, callable, *args, **kwargs):
"""
unittest does not have an assertRaisesNothing. This
provides that functionality
@@ -37,9 +37,10 @@ def assertRaisesNothing(testobj, callable, *args):
testobj - A unittest object
callable - A callable object
*args - Positional arguments passed to the callable as they are
**kwargs - Keyword arguments, passed on as they are
"""
try:
return callable(*args)
return callable(*args, **kwargs)
except Exception, exc:
testobj.fail("Assertion error. An exception was caught where none was expected in %s. Message: %s"
% (callable.__name__, str(exc)))
@@ -28,8 +28,10 @@ using namespace Geometry;

void EQSANSPatchSensitivity::init()
{
declareProperty(new WorkspaceProperty<>("Workspace","",Direction::InOut));
declareProperty(new WorkspaceProperty<>("PatchWorkspace","",Direction::Input));
declareProperty(new WorkspaceProperty<>("Workspace","",Direction::InOut),
"Input sensitivity workspace to be patched");
declareProperty(new WorkspaceProperty<>("PatchWorkspace","",Direction::Input),
"Workspace defining the patch. Masked detectors will be patched.");
declareProperty("UseLinearRegression",true,
"If true, a linear regression will be used instead of computing the average");
declareProperty("OutputMessage","",Direction::Output);
@@ -547,6 +547,7 @@ def createPropertiesFile(filename):
addFileV('MantidQtAPI','MQTAPI.dll','MantidQtAPI.dll',MANTIDRELEASE + '/MantidQtAPI.dll',MantidDlls)
addFileV('MantidWidgets','MWid.dll','MantidWidgets.dll',MANTIDRELEASE + '/MantidWidgets.dll',MantidDlls)
addFileV('MantidQtSliceViewer','MQTSV.dll','MantidQtSliceViewer.dll',MANTIDRELEASE + '/MantidQtSliceViewer.dll',MantidDlls)
addFileV('MantidQtImageViewer','MQTIV.dll','MantidQtImageViewer.dll',MANTIDRELEASE + '/MantidQtImageViewer.dll',MantidDlls)
addFileV('MantidQtFactory','MQTFA.dll','MantidQtFactory.dll',MANTIDRELEASE + '/MantidQtFactory.dll',MantidDlls)
addFileV('mantidqtpython','MQTP.dll','mantidqtpython.pyd',MANTIDRELEASE + '/mantidqtpython.pyd',MantidDlls)

@@ -749,6 +749,7 @@ include_directories ( src )
include_directories ( src/lib/include )
include_directories ( src/lib/3rdparty/qtcolorpicker/src )
include_directories ( ../MantidQt/SliceViewer/inc )
include_directories ( ../MantidQt/ImageViewer/inc )
include_directories ( ../MantidQt/Factory/inc )


@@ -778,6 +779,7 @@ add_executable ( MantidPlot ${WIN_CONSOLE} MACOSX_BUNDLE ${ALL_SRC} src/main.cpp
target_link_libraries ( MantidPlot
${CORE_MANTIDLIBS}
MantidQtAPI MantidWidgets MantidQtSliceViewer MantidQtFactory
MantidQtImageViewer
QtPropertyBrowser ${QT_LIBRARIES}
${QWT_LIBRARIES} ${QWTPLOT3D_LIBRARIES}
${QSCINTILLA_LIBRARIES}
@@ -9,6 +9,9 @@
#include "../ApplicationWindow.h"
#include "../MultiLayer.h"
#include "ErrorBarSettings.h"
#include "MantidKernel/CPUTimer.h"

using Mantid::Kernel::CPUTimer;

/**
Constructor
@@ -301,6 +301,9 @@ void MantidDockWidget::createWorkspaceMenuActions()
m_showListData = new QAction(tr("List Data"), this);
connect(m_showListData, SIGNAL(activated()), m_mantidUI, SLOT(showListData()));

m_showImageViewer = new QAction(tr("Show Image Viewer"), this);
connect(m_showImageViewer, SIGNAL(activated()), m_mantidUI, SLOT(showImageViewer()));

m_showSliceViewer = new QAction(tr("Show Slice Viewer"), this);
{ QIcon icon; icon.addFile(QString::fromUtf8(":/SliceViewer/icons/SliceViewerWindow_icon.png"), QSize(), QIcon::Normal, QIcon::Off);
m_showSliceViewer->setIcon(icon); }
@@ -846,6 +849,10 @@ void MantidDockWidget::addMatrixWorkspaceMenuItems(QMenu *menu, Mantid::API::Mat
// Don't plot a spectrum if only one X value
m_plotSpec->setEnabled ( matrixWS->blocksize() > 1 );
m_plotSpecErr->setEnabled ( matrixWS->blocksize() > 1 );
if( boost::dynamic_pointer_cast<const IEventWorkspace>(matrixWS) )
{
menu->addAction(m_showImageViewer); // The 2D image viewer
}
menu->addAction(m_colorFill);
// Show the color fill plot if you have more than one histogram
m_colorFill->setEnabled( ( matrixWS->axes() > 1 && matrixWS->getNumberHistograms() > 1) );
@@ -122,6 +122,7 @@ private slots:
//Context-menu actions
QAction *m_showData, *m_showInst, *m_plotSpec, *m_plotSpecErr, *m_plotSpecDistr,
*m_showDetectors, *m_showBoxData, *m_showVatesGui,
*m_showImageViewer,
*m_showSliceViewer,
*m_colorFill, *m_showLogs, *m_showHist, *m_showMDPlot, *m_showListData,
*m_saveNexus, *m_rename, *m_delete,
@@ -63,11 +63,14 @@
#include "MantidQtFactory/WidgetFactory.h"
#include "MantidAPI/MemoryManager.h"

#include "MantidQtImageViewer/EventWSImageView.h"

using namespace std;

using namespace Mantid::API;
using Mantid::Kernel::DateAndTime;
using MantidQt::SliceViewer::SliceViewerWindow;

namespace MantidException = Mantid::Kernel::Exception;

MantidUI::MantidUI(ApplicationWindow *aw):
@@ -660,6 +663,34 @@ void MantidUI::showVatesSimpleInterface()
}
}

void MantidUI::showImageViewer()
{
QString wsName = getSelectedWorkspaceName();
try
{
IEventWorkspace_sptr evwsp = boost::dynamic_pointer_cast<IEventWorkspace>(
AnalysisDataService::Instance().retrieve( wsName.toStdString()) );
if ( evwsp )
{
MantidQt::ImageView::EventWSImageView image_view( evwsp );
}
else
{
m_appWindow->writeToLogWindow("Only event workspaces are currently supported.");
m_appWindow->writeToLogWindow("Please convert to event workspace before using the ImageView.");
}
}
catch (std::runtime_error &e)
{
throw std::runtime_error(e);
}
catch (...)
{
m_appWindow->writeToLogWindow("Exception getting workspace " );
}

}


/** Create a window with a SliceViewer widget to show
* the selected workspace
@@ -340,6 +340,9 @@ public slots:
// Invoke a grid showing a table of MD summary list data.
void showListData();

// ImageViewer
void showImageViewer();

// SliceViewer
void showSliceViewer();

@@ -222,14 +222,17 @@ QString PythonScript::constructErrorMsg()
QTextStream msgStream(&message);
if( value && str_repr )
{
QString excTypeName(value->ob_type->tp_name); // This is fully qualified with the module name
excTypeName = excTypeName.section(".", -1);
QString exceptionAsStr(PyString_AsString(str_repr));
if(value->ob_type == (PyTypeObject*)PyExc_SyntaxError)
if(exception == PyExc_SyntaxError)
{
exceptionAsStr = constructSyntaxErrorStr(exceptionAsStr);
msgStream << constructSyntaxErrorStr(value);
}
msgStream << excTypeName << ": " << exceptionAsStr;
else
{
QString excTypeName(value->ob_type->tp_name); // This is fully qualified with the module name
excTypeName = excTypeName.section(".", -1);
msgStream << excTypeName << ": " << PyString_AsString(str_repr);
}

}
else
{
@@ -238,45 +241,57 @@ QString PythonScript::constructErrorMsg()
tracebackToMsg(msgStream, (PyTracebackObject *)(traceback));
msgStream << "\n";


Py_XDECREF(traceback);
Py_XDECREF(exception);
Py_XDECREF(value);
return msgStream.readAll();
}

/**
* Include the line offset & oddity that the syntax error
* line number is always 1 to high
* Constructs a string error message from a syntax exception value object
* @param syntaxError An instance of PyExc_SyntaxError
*/
QString PythonScript::constructSyntaxErrorStr(const QString & originalString)
{
// Format: "some prefix string (filename, line no)"
QStringList pieces = originalString.split(" ");
// Last 3 pieces should always be the parenthesized (filename, line no)
if(pieces.size() < 3)
QString PythonScript::constructSyntaxErrorStr(PyObject *syntaxError)
{
QString exceptionAsStr = m_pythonEnv->toString(syntaxError);
exceptionAsStr = exceptionAsStr.section("(", 0, 0).trimmed();
const QString filename = m_pythonEnv->toString(PyObject_GetAttrString(syntaxError, "filename"), true);
int lineno = static_cast<int>(m_pythonEnv->toLong(PyObject_GetAttrString(syntaxError, "lineno")));
#if PY_VERSION_HEX < 0x02070000
// Syntax errors generated by earlier versions seem to have a line number off by 1
lineno -= 1;
#endif

QString msg;
// If the text attribute is not None then an offset in the code can be shown using a ^ character
PyObject *textObject = PyObject_GetAttrString(syntaxError, "text");
if(textObject != Py_None)
{
QString text = m_pythonEnv->toString(textObject, true).trimmed();
int offset = static_cast<int>(m_pythonEnv->toLong(PyObject_GetAttrString(syntaxError, "offset")));
QString offsetMarker = QString(offset-1, ' ') + "^";
msg =
"File \"%1\", line %2\n"
" %3\n"
" %4\n"
"SyntaxError: %5";
msg = msg.arg(filename);
msg = msg.arg(lineno);
msg = msg.arg(text, offsetMarker, exceptionAsStr);
}
else
{
return QString("Unknown syntax error occurred");
msg =
"File \"%1\", line %2\n"
"SyntaxError: %3";
msg = msg.arg(filename);
msg = msg.arg(lineno);
msg = msg.arg(exceptionAsStr);
}
QString linenoAsStr = pieces.takeLast();
linenoAsStr.chop(1); // last bracket
int lineno = linenoAsStr.toInt();


pieces.removeLast(); //Remove the word line
QString filename = pieces.takeLast();
filename.chop(1); // comma
filename.remove(0,1); // first bracket

if(filename == QFileInfo(name()).fileName())
if(filename == name().c_str())
{
lineno -= 1; // Fix for bug when original exception generated
lineno = getRealLineNo(lineno);
sendLineChangeSignal(lineno, true);
}
linenoAsStr = QString::number(lineno);
QString msg = pieces.join(" ");
msg += " (" + filename + ", line " + linenoAsStr + ")";
return msg;
}

@@ -297,7 +312,7 @@ void PythonScript::tracebackToMsg(QTextStream &msgStream,

int lineno = traceback->tb_lineno;
QString filename = QString::fromAscii(PyString_AsString(traceback->tb_frame->f_code->co_filename));
if(filename == name())
if(filename == name().c_str())
{
lineno = getRealLineNo(lineno);
sendLineChangeSignal(lineno, true);
@@ -391,9 +406,9 @@ void PythonScript::endStdoutRedirect()
* @param code
* @return True if success, false otherwise
*/
bool PythonScript::compileImpl(const QString & code)
bool PythonScript::compileImpl()
{
PyObject *codeObject = compileToByteCode(code, false);
PyObject *codeObject = compileToByteCode(false);
if(codeObject)
{
return true;
@@ -408,10 +423,10 @@ bool PythonScript::compileImpl(const QString & code)
* Evaluate the code and return the value
* @return
*/
QVariant PythonScript::evaluateImpl(const QString & code)
QVariant PythonScript::evaluateImpl()
{
GlobalInterpreterLock gil;
PyObject *compiledCode = this->compileToByteCode(code, true);
PyObject *compiledCode = this->compileToByteCode(true);
if(!compiledCode)
{
return QVariant("");
@@ -437,7 +452,7 @@ QVariant PythonScript::evaluateImpl(const QString & code)
}
else
{
emit error(constructErrorMsg(), name(), 0);
emit error(constructErrorMsg(), "", 0);
return QVariant();
}
}
@@ -507,43 +522,60 @@ QVariant PythonScript::evaluateImpl(const QString & code)
}
else
{
emit error(constructErrorMsg(), name(), 0);
emit error(constructErrorMsg(), "", 0);
}
return QVariant();
}
return qret;
}

bool PythonScript::executeImpl(const QString & code)
bool PythonScript::executeImpl()
{
emit startedSerial("");
return executeString(code);
return executeString();
}

QFuture<bool> PythonScript::executeAsyncImpl(const QString & code)
QFuture<bool> PythonScript::executeAsyncImpl()
{
emit startedAsync("");
return QtConcurrent::run(this, &PythonScript::executeString, code);
return QtConcurrent::run(this, &PythonScript::executeString);
}


/// Performs the call to Python
bool PythonScript::executeString(const QString & code)
bool PythonScript::executeString()
{
emit started("Script execution started.");
bool success(false);
GlobalInterpreterLock gil;

PyObject *compiledCode = compileToByteCode(code);
PyObject *result = executeCompiledCode(compiledCode);
success = checkResult(result);
PyObject * compiledCode = compileToByteCode(false);
PyObject *result(NULL);
if(compiledCode)
{
result = executeCompiledCode(compiledCode);
}
// If an error has occurred we need to construct the error message
// before any other python code is run
QString msg;
if(!result)
{
msg = constructErrorMsg(); // Also clears error indicator
}
else
{
msg = "Script execution finished.";
success = true;
}
if(isInteractive())
{
generateAutoCompleteList();
}

Py_XDECREF(compiledCode);
Py_XDECREF(result);
if(success) emit finished(msg);
else emit error(msg, "", 0);
return success;
}

@@ -595,21 +627,18 @@ PyObject* PythonScript::executeCompiledCode(PyObject *compiledCode)
}

/**
* Check an object for a result. A null object means that the execution failed
* and an error signal is emitted. A valid pointer indicates success
* Check an object for a result. A valid pointer indicates success
* @param result The output from a PyEval call
* @return A boolean indicating success status
*/
bool PythonScript::checkResult(PyObject *result)
{
if(result)
{
emit finished("Script execution finished.");
return true;
}
else
{
emit error(constructErrorMsg(), "", 0);
return false;
}
}
@@ -618,7 +647,7 @@ bool PythonScript::checkResult(PyObject *result)
/**
* Compile the code
*/
PyObject *PythonScript::compileToByteCode(const QString & code, bool for_eval)
PyObject *PythonScript::compileToByteCode(bool for_eval)
{
// Support for the convenient col() and cell() functions.
// This can't be done anywhere else, because we need access to the local
@@ -665,8 +694,7 @@ PyObject *PythonScript::compileToByteCode(const QString & code, bool for_eval)
}
bool success(false);
// Simplest case: Code is a single expression

PyObject *compiledCode = Py_CompileString(code, nameAsCStr(), Py_file_input);
PyObject *compiledCode = Py_CompileString(codeString().c_str(), name().c_str(), Py_file_input);

if( compiledCode )
{
@@ -689,10 +717,9 @@ PyObject *PythonScript::compileToByteCode(const QString & code, bool for_eval)
signature.append(PyString_AsString(key)).append(",");
}
signature.truncate(signature.length()-1);
QString fdef = "def __doit__("+signature+"):\n";
fdef.append(code);
fdef.replace('\n',"\n\t");
compiledCode = Py_CompileString(fdef, nameAsCStr(), Py_file_input);
std::string fdef = "def __doit__("+signature.toStdString()+"):\n";
fdef += codeString();
compiledCode = Py_CompileString(fdef.c_str(), name().c_str(), Py_file_input);
if( compiledCode )
{
PyObject *tmp = PyDict_New();
@@ -706,11 +733,6 @@ PyObject *PythonScript::compileToByteCode(const QString & code, bool for_eval)
}
else
{
// Code contains statements (or errors), but we do not need to get
// a return value.
PyErr_Clear(); // silently ignore errors
compiledCode = Py_CompileString(code, nameAsCStr(), Py_file_input);
success = (compiledCode != NULL);
}

if(success)
@@ -76,9 +76,8 @@ class PythonScript : public Script, MantidQt::API::WorkspaceObserver

/// Construct the error message from the stack trace (if one exists)
QString constructErrorMsg();
/// Fix the line number of the syntax errors. This includes the offset
/// & a fix for the syntax errors always off by one
QString constructSyntaxErrorStr(const QString & originalString);
/// Special handle for syntax errors as they have no traceback
QString constructSyntaxErrorStr(PyObject *syntaxError);
/// Convert a traceback to a string
void tracebackToMsg(QTextStream &msgStream, PyTracebackObject* traceback,
bool root=true);
@@ -139,23 +138,22 @@ class PythonScript : public Script, MantidQt::API::WorkspaceObserver

// --------------------------- Script compilation/execution -----------------------------------
/// Compile the code, returning true if it was successful, false otherwise
bool compileImpl(const QString & code);
bool compileImpl();
/// Evaluate the current code and return a result as a QVariant
QVariant evaluateImpl(const QString & code);
QVariant evaluateImpl();
/// Execute the current code and return a boolean indicating success/failure
bool executeImpl(const QString &);
bool executeImpl();
/// Execute the code asynchronously, returning immediately after the execution has started
QFuture<bool> executeAsyncImpl(const QString & code);
QFuture<bool> executeAsyncImpl();

/// Performs the call to Python from a string
bool executeString(const QString & code);
bool executeString();
/// Executes the code object and returns the result, may be null
PyObject* executeCompiledCode(PyObject *compiledCode);
/// Check an object for a result.
bool checkResult(PyObject *result);

/// Compile to bytecode
PyObject * compileToByteCode(const QString &, bool for_eval=true);
PyObject * compileToByteCode(bool for_eval=true);

// ---------------------------- Variable reference ---------------------------------------------
/// Listen to add notifications from the ADS