Skip to content

Commit

Permalink
Export IPeakLocation to Python
Browse files Browse the repository at this point in the history
This requires the Jacobian class to be exported too along with
an update to the CallMethods* to add an overload for a single
argument. Refs #970
  • Loading branch information
martyngigg committed Apr 16, 2013
1 parent 2396cbb commit 184aa3a
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifndef MANTID_PYTHONINTERFACE_IPEAKFUNCTIONADAPTER_H_
#define MANTID_PYTHONINTERFACE_IPEAKFUNCTIONADAPTER_H_
/**
Copyright © 2011 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://github.com/mantidproject/mantid>.
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "MantidAPI/IPeakFunction.h"
#include "MantidPythonInterface/api/FitFunctions/IFunction1DAdapter.h"

#include <boost/python/object.hpp>

namespace Mantid
{
namespace PythonInterface
{

/**
* Provides a layer class for boost::python to allow C++ virtual functions
* to be overridden in a Python object that is derived from IPeakFunction.
*
* This is essentially a transparent layer that handles the function calls up into Python.
*/
class IPeakFunctionAdapter : public virtual API::IPeakFunction, public virtual IFunction1DAdapter
{
public:
/// A constructor that looks like a Python __init__ method
IPeakFunctionAdapter(PyObject* self);

/// Calls 'centre' method in Python
double centre() const;
/// Calls 'height' method in Python
double height() const;
/// Calls 'setCentre' method in Python
void setCentre(const double c);
/// Calls 'setHeight' method in Python
void setHeight(const double h);

/// Calls Python fwhm method
double fwhm() const;
/// Called by framework when the width is changed
void setFwhm(const double w);

/// Required to solve compiler ambiguity between IPeakFunction & IFunction1DAdapter
void function1D(double* out, const double* xValues, const size_t nData) const { IPeakFunction::function1D(out,xValues,nData); }

/// Implemented Base-class method
void functionLocal(double* out, const double* xValues, const size_t nData) const;
/// Python-type signature for above method
void functionLocal(const boost::python::object & xvals, boost::python::object & out) const;
/// Implemented base-class method
void functionDerivLocal(API::Jacobian* out, const double* xValues, const size_t nData);
/// Python signature
void functionDerivLocal(const boost::python::object & xvals, API::Jacobian* out);


private:
/// The PyObject must be supplied to construct the object
DISABLE_DEFAULT_CONSTRUCT(IPeakFunctionAdapter);
DISABLE_COPY_AND_ASSIGN(IPeakFunctionAdapter);
};
}
}


#endif /* MANTID_PYTHONINTERFACE_IPEAKFUNCTIONADAPTER_H_ */
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ namespace Mantid { namespace PythonInterface {
* then raise a std::runtime_error exception
* @param self :: The object containing the method definition
* @param funcName :: The method name
* @param errorMsg :: An error message to pass to the generated exception
* @return The value of the function or the default value if it does not exist
*/
static ResultType dispatchWithException(PyObject *self, const char * funcName)
Expand All @@ -136,7 +135,6 @@ namespace Mantid { namespace PythonInterface {
* then raise a runtime_error
* @param self :: The object containing the method definition
* @param funcName :: The method name
* @param errorMsg :: An error message if the method does not exist
* @return The value of the function or the default value if it does not exist
*/
static void dispatchWithException(PyObject *self, const char * funcName)
Expand All @@ -148,6 +146,68 @@ namespace Mantid { namespace PythonInterface {
};
//@}

/** @name Single argument Python calls */
//@{
/**
* Perform a call to a python function that takes no arguments and returns a value
*/
template<typename ResultType, typename Arg1>
struct DLLExport CallMethod1
{
/**
* Dispatch a call to the method on the given object. If the method does not exist
* then return the defaultValue
* @param self :: The object containing the method definition
* @param funcName :: The method name
* @param defaultValue :: A default value if the method does not exist
* @return The value of the function or the default value if it does not exist
*/
static ResultType dispatchWithDefaultReturn(PyObject *self, const char * funcName, const ResultType & defaultValue,
const Arg1 & arg1)
{
PRE_CALL(self, funcName);
return boost::python::call_method<ResultType,Arg1>(self,funcName, arg1);
POST_CALL_DEFAULT();
}

/**
* Dispatch a call to the method on the given object. If the method does not exist
* then raise a std::runtime_error exception
* @param self :: The object containing the method definition
* @param funcName :: The method name
* @param arg1 :: The value of the first argument
* @return The value of the function or the default value if it does not exist
*/
static ResultType dispatchWithException(PyObject *self, const char * funcName, const Arg1 & arg1)
{
PRE_CALL(self,funcName);
return boost::python::call_method<ResultType,Arg1>(self, funcName, arg1);
POST_CALL_EXCEPT();
}
};

///Specialization for void return type
template<typename Arg1>
struct DLLExport CallMethod1<void,Arg1>
{
/**
* Dispatch a call to the method on the given object. If the method does not exist
* then raise a runtime_error
* @param self :: The object containing the method definition
* @param funcName :: The method name
* @param arg1 :: The value of the first argument
* @return The value of the function or the default value if it does not exist
*/
static void dispatchWithException(PyObject *self, const char * funcName, const Arg1 & arg1)
{
PRE_CALL(self, funcName);
boost::python::call_method<void,Arg1>(self,funcName,arg1);
POST_CALL_EXCEPT_VOID();
}
};
//@}


/** @name Two argument Python calls */
//@{
template<typename ResultType,typename Arg1,typename Arg2>
Expand Down Expand Up @@ -198,7 +258,8 @@ namespace Mantid { namespace PythonInterface {
* then raise a runtime_error
* @param self :: The object containing the method definition
* @param funcName :: The method name
* @param errorMsg :: An error message if the method does not exist
* @param arg1 :: The value of the first argument
* @param arg2 :: The value of the second argument
* @return The value of the function or the default value if it does not exist
*/
static void dispatchWithException(PyObject *self, const char * funcName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ set ( EXPORT_FILES
src/Exports/WorkspaceFactory.cpp
src/Exports/IFunction.cpp
src/Exports/IFunction1D.cpp
src/Exports/IPeakFunction.cpp
src/Exports/Jacobian.cpp
src/Exports/PropertyManagerDataService.cpp
src/Exports/FunctionFactory.cpp
src/Exports/Progress.cpp
Expand All @@ -64,6 +66,7 @@ set ( EXPORT_FILES
set ( SRC_FILES
src/FitFunctions/IFunctionAdapter.cpp
src/FitFunctions/IFunction1DAdapter.cpp
src/FitFunctions/IPeakFunctionAdapter.cpp
src/PythonAlgorithm/AlgorithmWrapper.cpp
src/PythonAlgorithm/PythonAlgorithm.cpp
src/CloneMatrixWorkspace.cpp
Expand All @@ -72,6 +75,7 @@ set ( SRC_FILES
set ( INC_FILES
${HEADER_DIR}/api/FitFunctions/IFunctionAdapter.h
${HEADER_DIR}/api/FitFunctions/IFunction1DAdapter.h
${HEADER_DIR}/api/FitFunctions/IPeakFunctionAdapter.h
${HEADER_DIR}/api/PythonAlgorithm/AlgorithmWrapper.h
${HEADER_DIR}/api/PythonAlgorithm/PythonAlgorithm.h
${HEADER_DIR}/api/BinaryOperations.h
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "MantidAPI/IPeakFunction.h"
#include "MantidPythonInterface/api/FitFunctions/IPeakFunctionAdapter.h"
#include <boost/python/class.hpp>

using Mantid::API::IFunction1D;
using Mantid::API::IPeakFunction;
using Mantid::PythonInterface::IPeakFunctionAdapter;
using namespace boost::python;

void export_IPeakFunction()
{
class_<IPeakFunction, bases<IFunction1D>, boost::shared_ptr<IPeakFunctionAdapter>,
boost::noncopyable>("IPeakFunction")
.def("functionLocal", (void (IPeakFunctionAdapter::*)(const object &,object&)const)&IPeakFunction::functionLocal,
"Calculate the values of the function for the given x values. The output should be stored in the out array")
;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "MantidAPI/Jacobian.h"
#include <boost/python/class.hpp>
#include <boost/python/register_ptr_to_python.hpp>

using Mantid::API::Jacobian;
using namespace boost::python;

void export_Jacobian()
{
register_ptr_to_python<Jacobian*>();

class_<Jacobian, boost::noncopyable>("Jacobian", no_init)
.def("set", &Jacobian::set, (arg("iy"),arg("ip"),arg("value")),
"Set an element of the Jacobian matrix where iy=index of data point, ip=index of parameter.")

.def("get", &Jacobian::get, (arg("iy"),arg("ip")),
"Return the given element of the Jacobian matrix where iy=index of data point, ip=index of parameter.")
;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "MantidPythonInterface/api/FitFunctions/IPeakFunctionAdapter.h"

#include "MantidPythonInterface/kernel/Converters/WrapWithNumpy.h"
#include "MantidPythonInterface/kernel/Environment/CallMethod.h"

#include <boost/python/class.hpp>

//-----------------------------------------------------------------------------
// IPeakFunction definition
//-----------------------------------------------------------------------------
namespace Mantid
{
namespace PythonInterface
{
using Environment::CallMethod0;
using Environment::CallMethod1;
using Environment::CallMethod2;
using namespace boost::python;

/**
* Construct the "wrapper" and stores the reference to the PyObject
* * @param self A reference to the calling Python object
*/
IPeakFunctionAdapter::IPeakFunctionAdapter(PyObject* self)
: API::IPeakFunction(), IFunctionAdapter(self), IFunction1DAdapter(self)
{
}

/**
*/
double IPeakFunctionAdapter::centre() const
{
return CallMethod0<double>::dispatchWithException(getSelf(), "centre");
}

/**
*/
double IPeakFunctionAdapter::height() const
{
return CallMethod0<double>::dispatchWithException(getSelf(), "height");
}

/**
* Called when the centre of the peak has been updated outside of the function
* @param c The centre of the peak
*/
void IPeakFunctionAdapter::setCentre(const double c)
{
CallMethod1<void,double>::dispatchWithException(getSelf(), "setCentre", c);
}

/**
* Called when the height of the peak has been updated outside of the function
* @param h The new height of the peak
*/
void IPeakFunctionAdapter::setHeight(const double h)
{
CallMethod1<void,double>::dispatchWithException(getSelf(), "setHeight", h);
}


/// Calls Python fwhm method
double IPeakFunctionAdapter::fwhm() const
{
return CallMethod0<double>::dispatchWithException(getSelf(), "fwhm");
}

/**
* Called when the width of the peak has been updated outside of the function
* @param w The new width of the peak. The function should update its parameters such that fwhm=w
*/
void IPeakFunctionAdapter::setFwhm(const double w)
{
return CallMethod1<void,double>::dispatchWithException(getSelf(), "setFwhm", w);
}


/**
* Translates between the C++ signature & the Python signature
* @param out The 1D data array of size nData that stores the output values
* @param xValues The input X values
* @param nData The size of the two arrays
*/
void IPeakFunctionAdapter::functionLocal(double* out, const double* xValues, const size_t nData) const
{
Py_intptr_t dims[1] = { static_cast<Py_intptr_t>(nData) } ;
object xvals = object(handle<>(Converters::WrapReadOnly::apply<double>::createFromArray(xValues, 1,dims)));
object outnp = object(handle<>(Converters::WrapReadWrite::apply<double>::createFromArray(out, 1,dims)));
functionLocal(xvals, outnp);
}

/**
* Python-type signature version of above
* @param xvals The input X values in read-only numpy array
* @param out A read/write numpy array of doubles to store the results
*/
void IPeakFunctionAdapter::functionLocal(const boost::python::object & xvals, boost::python::object & out) const
{
CallMethod2<void,object,object>::dispatchWithException(getSelf(), "functionLocal", xvals, out);
}

/**
* Translates between the C++ signature & the Python signature
* @param out The Jacobian matrix storing the partial derivatives of the function w.r.t to the parameters
* @param xValues The input X values
* @param nData The size of the two arrays
*/
void IPeakFunctionAdapter::functionDerivLocal(API::Jacobian* out, const double* xValues, const size_t nData)
{
Py_intptr_t dims[1] = { static_cast<Py_intptr_t>(nData) } ;
object xvals = object(handle<>(Converters::WrapReadOnly::apply<double>::createFromArray(xValues, 1,dims)));
functionDerivLocal(xvals, out);
}

/**
* Python-type signature version of above
* @param xvals The input X values in read-only numpy array
* @param out The Jacobian matrix storing the partial derivatives of the function w.r.t to the parameters
*/
void IPeakFunctionAdapter::functionDerivLocal(const boost::python::object & xvals, API::Jacobian* out)
{
CallMethod2<void,object,API::Jacobian*>::dispatchWithException(getSelf(), "functionDerivLocal", xvals, out);
}

}
}

0 comments on commit 184aa3a

Please sign in to comment.