Skip to content

Commit

Permalink
Refs #4333. Additional API exports to Python.
Browse files Browse the repository at this point in the history
  • Loading branch information
martyngigg committed Dec 15, 2011
1 parent 503fed9 commit 49ddaac
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ namespace Mantid
PyObject *wrapY(API::MatrixWorkspace &self, const size_t index);
/// Create a numpy wrapper around the original X values at the given index
PyObject *wrapE(API::MatrixWorkspace &self, const size_t index);
/// Create a numpy wrapper around the original Dx values at the given index
PyObject *wrapDx(API::MatrixWorkspace &self, const size_t index);

///@}

//** @name Numpy clones of data*/
Expand All @@ -53,6 +56,8 @@ namespace Mantid
PyObject *cloneY(API::MatrixWorkspace &self);
/// Create a numpy array from the E values of the given workspace reference
PyObject *cloneE(API::MatrixWorkspace &self);
/// Create a numpy array from the E values of the given workspace reference
PyObject *cloneDx(API::MatrixWorkspace &self);
///@}

}
Expand Down
12 changes: 10 additions & 2 deletions Code/Mantid/Framework/PythonInterface/mantid/api/src/Algorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,28 @@ void export_algorithm()
.def("alias", &IAlgorithm::alias, "Return the aliases for the algorithm")
.def("version", &IAlgorithm::version, "Returns the version number of the algorithm")
.def("category", &IAlgorithm::category, "Returns the category containing the algorithm")
.def("categories", &IAlgorithm::category, "Returns the list of categories this algorithm belongs to")
.def("getOptionalMessage", &IAlgorithm::getOptionalMessage, "Returns the optional user message attached to the algorithm")
.def("getWikiSummary", &IAlgorithm::getWikiSummary, "Returns the summary found on the wiki page")
.def("getWikiDescription", &IAlgorithm::getWikiDescription, "Returns the description found on the wiki page using wiki markup")
.def("docString", &createDocString, "Returns a doc string for the algorithm")
.def("mandatoryProperties",&getInputPropertiesWithMandatoryFirst, "Returns a list of input and in/out property names that is ordered "
"such that the mandatory properties are first followed by the optional ones.")
.def("outputProperties",&getOutputProperties, "Returns a list of the output properties on the algorithm")
.def("initialize", &IAlgorithm::initialize, "Initializes the algorithm")
.def("isInitialized", &IAlgorithm::isInitialized, "Returns True if the algorithm is initialized, False otherwise")
.def("execute", &IAlgorithm::execute, "Runs the algorithm")
.def("isExecuted", &IAlgorithm::isExecuted, "Returns true if the algorithm has been executed successfully, false otherwise")
.def("setChild", &IAlgorithm::setChild,
"If true this algorithm is run as a child algorithm. There will be no logging and nothing is stored in the Analysis Data Service")
.def("isChild", &IAlgorithm::isChild, "Returns True if the algorithm has been marked to run as a child. If True then Output workspaces "
"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")
// Special methods
.def("__str__", &IAlgorithm::toString)
;


}

void export_algorithmHierarchy()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
#include "MantidAPI/ExperimentInfo.h"
#include <boost/python/class.hpp>
#include <boost/python/copy_const_reference.hpp>

using Mantid::API::ExperimentInfo;
using boost::python::class_;
using boost::python::no_init;
using namespace boost::python;

void export_ExperimentInfo()
{
class_<ExperimentInfo,boost::noncopyable>("ExperimentInfo", no_init)

class_<ExperimentInfo, boost::noncopyable>("ExperimentInfo", no_init)
.def("getInstrument", &ExperimentInfo::getInstrument, "Returns the instrument for this run")
.def("sample", &ExperimentInfo::sample, return_value_policy<copy_const_reference>(),
"Return the Sample object. This cannot be modified, use mutableSample to modify.")
.def("mutableSample", &ExperimentInfo::mutableSample, return_value_policy<reference_existing_object>(),
"Return a modifiable Sample object.")
.def("run", &ExperimentInfo::run, return_value_policy<copy_const_reference>(),
"Return the sample object. This cannot be modified, use mutableSample to modify.")
.def("mutableRun", &ExperimentInfo::mutableRun, return_value_policy<reference_existing_object>(),
"Return a modifiable Run object.")
.def("getRunNumber", &ExperimentInfo::getRunNumber, "Returns the run identifier for this run")
;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,61 @@

#include <boost/python/class.hpp>
#include <boost/python/register_ptr_to_python.hpp>
#include <boost/python/overloads.hpp>

using namespace Mantid::API;
using Mantid::Geometry::IDetector_sptr;
using Mantid::Kernel::PropertyWithValue;
using Mantid::Kernel::DataItem_sptr;
using namespace boost::python;

namespace
{
//------------------------------- Overload macros ---------------------------
// Overloads for binIndexOf function which has 1 optional argument
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(MatrixWorkspace_binIndexOfOverloads,
MatrixWorkspace::binIndexOf, 1, 2)
}

void export_MatrixWorkspace()
{
// Leave this here for now but move it if it needs expanding to add methods
class_<IMDWorkspace, boost::python::bases<Workspace>, boost::noncopyable>("IMDWorkspace", no_init)
;

register_ptr_to_python<MatrixWorkspace_sptr>();

class_<MatrixWorkspace, boost::python::bases<ExperimentInfo,IMDWorkspace>, boost::noncopyable>("MatrixWorkspace", no_init)
//--------------------------------------- Meta information -----------------------------------------------------------------------
.def("blocksize", &MatrixWorkspace::blocksize, "Returns size of the Y data array")
.def("getNumberHistograms", &MatrixWorkspace::getNumberHistograms, "Returns the number of spectra in the workspace")
//--------------------------------------- Array access ---------------------------------------------------------------------------
.def("binIndexOf", &MatrixWorkspace::binIndexOf, MatrixWorkspace_binIndexOfOverloads(args("xvalue", "workspace_index"),
"Returns the index of the bin containing the given xvalue. The workspace_index is optional [default=0]"))
.def("getSpectrum", (ISpectrum * (MatrixWorkspace::*)(const size_t))&MatrixWorkspace::getSpectrum,
return_internal_reference<>(), "Return the spectra at the given workspace index.")
.def("getDetector", (IDetector_sptr (MatrixWorkspace::*) (const size_t) const)&MatrixWorkspace::getDetector,
"Return the Detector or DetectorGroup that is linked to the given workspace index")
.def("axes", &MatrixWorkspace::axes, "Returns the number of axes attached to the workspace")
.def("getAxis", &MatrixWorkspace::getAxis, return_internal_reference<>())
.def("isHistogramData", &MatrixWorkspace::isHistogramData, "Returns True if this is conisdered to be binned data.")
.def("YUnit", &MatrixWorkspace::YUnit, "Returns the current Y unit for the data (Y axis) in the workspace")
.def("YUnitLabel", &MatrixWorkspace::YUnitLabel, "Returns the caption for the Y axis")
//--------------------------------------- Setters -------------------------------------------------------------------------------
.def("setYUnitLabel", &MatrixWorkspace::setYUnitLabel, "Sets a new caption for the data (Y axis) in the workspace")
.def("setYUnit", &MatrixWorkspace::setYUnit, "Sets a new unit for the data (Y axis) in the workspace")
.def("setDistribution", (bool& (MatrixWorkspace::*)(const bool))&MatrixWorkspace::isDistribution,
return_value_policy<return_by_value>(), "Set distribution flag. If True the workspace has been divided by the bin-width.")
.def("replaceAxis", &MatrixWorkspace::replaceAxis)
//--------------------------------------- Data access ---------------------------------------------------------------------------
.def("readX", &Mantid::PythonInterface::Numpy::wrapX,
"Creates a read-only numpy wrapper around the original X data at the given index")
.def("readY", &Mantid::PythonInterface::Numpy::wrapY,
"Creates a read-only numpy wrapper around the original Y data at the given index")
.def("readE", &Mantid::PythonInterface::Numpy::wrapE,
"Creates a read-only numpy wrapper around the original E data at the given index")
.def("readDx", &Mantid::PythonInterface::Numpy::wrapDx,
"Creates a read-only numpy wrapper around the original E data at the given index")

.def("extractX", Mantid::PythonInterface::Numpy::cloneX,
"Extracts (copies) the X data from the workspace into a 2D numpy array. "
"Note: This can fail for large workspaces as numpy will require a block "
Expand All @@ -42,7 +73,11 @@ void export_MatrixWorkspace()
"Extracts (copies) the E data from the workspace into a 2D numpy array. "
"Note: This can fail for large workspaces as numpy will require a block "
"of memory free that will fit all of the data.")
;
.def("extractDx", Mantid::PythonInterface::Numpy::cloneDx,
"Extracts (copies) the E data from the workspace into a 2D numpy array. "
"Note: This can fail for large workspaces as numpy will require a block "
"of memory free that will fit all of the data.")
;

DECLARE_SINGLEVALUETYPEHANDLER(MatrixWorkspace, DataItem_sptr);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Mantid
namespace
{
/// Which data field are we extracting
enum DataField { XValues = 0, YValues = 1, EValues= 2 };
enum DataField { XValues = 0, YValues = 1, EValues= 2, DxValues = 3 };

/**
* Helper method for extraction to numpy.
Expand All @@ -45,11 +45,19 @@ namespace Mantid
// Find out which function we need to call to access the data
typedef const MantidVec & (MatrixWorkspace::*ArrayAccessFn)(const size_t) const;
ArrayAccessFn dataAccesor;
/**
* Can do better than this with a templated object that knows how to access the data
*/
if( field == XValues )
{
stride = workspace.readX(0).size();
dataAccesor = &MatrixWorkspace::readX;
}
else if( field == DxValues)
{
stride = workspace.readDx(0).size();
dataAccesor = &MatrixWorkspace::readDx;
}
else
{
stride = workspace.blocksize();
Expand Down Expand Up @@ -108,6 +116,16 @@ namespace Mantid
return (PyObject*)nparray;
}

/*
* Create a numpy wrapper around the original Dx values at the given index
* @param self :: A pointer to a PyObject representing the calling object
* @param index :: The index into the workspace
*/
PyObject *wrapDx(MatrixWorkspace & self, const size_t index)
{
PyArrayObject *nparray = (PyArrayObject*)wrapWithReadOnlyNumpy(self.readDx(index));
return (PyObject*)nparray;
}

// -------------------------------------- Cloned arrays---------------------------------------------------
/* Create a numpy array from the X values of the given workspace reference
Expand Down Expand Up @@ -139,6 +157,17 @@ namespace Mantid
return (PyObject*)cloneArray(self, EValues, 0, self.getNumberHistograms());
}

/* Create a numpy array from the E values of the given workspace reference
* This acts like a python method on a Matrixworkspace object
* @param self :: A pointer to a PyObject representing the calling object
* @return A 2D numpy array created from the E values
*/
PyObject * cloneDx(MatrixWorkspace &self)
{
return (PyObject*)cloneArray(self, DxValues, 0, self.getNumberHistograms());
}


}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ using namespace boost::python;
*/
void export_Detector()
{
class_<Detector, bases<IDetector, ObjComponent>, boost::noncopyable>("Detector_", no_init)
class_<Detector, bases<IDetector, ObjComponent>, boost::noncopyable>("Detector", no_init)
;
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ def test_cannot_execute_with_invalid_properties(self):
def test_execute_succeeds_with_valid_props(self):
data = [1.0,2.0,3.0]
alg = run_algorithm('CreateWorkspace',DataX=data,DataY=data,NSpec=1,UnitX='Wavelength',child=True)
self.assertEquals(alg.isExecuted(), True)
self.assertEquals(alg.getProperty('NSpec').value, 1)
self.assertEquals(type(alg.getProperty('NSpec').value), int)
self.assertEquals(alg.getProperty('NSpec').name, 'NSpec')
ws = alg.getProperty('OutputWorkspace').value
self.assertTrue(ws.getMemorySize() > 0.0 )

as_str = str(alg)
self.assertEquals(as_str, "CreateWorkspace.1(OutputWorkspace=UNUSED_NAME_FOR_CHILD,DataX=1,2,3,DataY=1,2,3,UnitX=Wavelength)")

if __name__ == '__main__':
unittest.main()

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import unittest
###############################################################################
# This has to be tested through a workspace as it cannot be created in
# Python
###############################################################################
from testhelpers import run_algorithm
from mantid.geometry import Instrument

class ExperimentInfoTest(unittest.TestCase):

def test_information_access(self):
alg = run_algorithm('Load', Filename='LOQ48127.raw', SpectrumMax=2, child=True)
expt = alg.getProperty("OutputWorkspace")
inst = expt.getInstrument()
self.assertTrue(isinstance(Instrument))
self.assertEquals(expt.getRunNumber(), 48127)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

from testhelpers import run_algorithm, can_be_instantiated

from mantid.api import MatrixWorkspace, WorkspaceProperty_Workspace, Workspace
from mantid.api import (MatrixWorkspace, WorkspaceProperty_Workspace, Workspace,
ExperimentInfo)
from mantid.geometry import Detector

import numpy as np

Expand All @@ -20,6 +22,10 @@ def setUp(self):

def test_that_one_cannot_be_instantiated_directly(self):
self.assertFalse(can_be_instantiated(MatrixWorkspace))

def test_hierarchy_is_as_expected(self):
self.assertTrue(issubclass(MatrixWorkspace, ExperimentInfo))
self.assertTrue(issubclass(MatrixWorkspace, Workspace))

def test_meta_information(self):
self.assertEquals(self._test_ws.id(), "Workspace2D")
Expand All @@ -33,6 +39,20 @@ def test_meta_information(self):
def test_workspace_data_information(self):
self.assertEquals(self._test_ws.getNumberHistograms(), 2)
self.assertEquals(self._test_ws.blocksize(), 102)
self.assertEquals(self._test_ws.YUnit(), "Counts")
self.assertEquals(self._test_ws.YUnitLabel(), "Counts")
# Workspace axes
self.assertEquals(self._test_ws.axes(), 2)

def test_detector_retrieval(self):
det = self._test_ws.getDetector(0)
self.assertTrue(isinstance(det, Detector))
self.assertEquals(det.getID(), 1)
self.assertFalse(det.isMasked())

def test_spectrum_retrieval(self):
# Spectrum
pass

def test_that_a_histogram_workspace_is_returned_as_a_MatrixWorkspace_from_a_property(self):
self.assertEquals(type(self._test_ws_prop), WorkspaceProperty_Workspace)
Expand All @@ -47,7 +67,9 @@ def test_data_can_be_extracted_to_numpy_successfully(self):
x = self._test_ws.extractX()
y = self._test_ws.extractY()
e = self._test_ws.extractE()

dx = self._test_ws.extractDx()

self.assertTrue(len(dx), 0)
self._do_numpy_comparison(self._test_ws, x, y, e)

def _do_numpy_comparison(self, workspace, x_np, y_np, e_np):
Expand Down

0 comments on commit 49ddaac

Please sign in to comment.