Mantid
Loading...
Searching...
No Matches
FunctionFactory.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10#include "MantidAPI/IFunction.h"
18
19#include <boost/python/class.hpp>
20#include <boost/python/def.hpp>
21#include <mutex>
22
23// Python frameobject. This is under the boost includes so that boost will have
24// done the
25// include of Python.h which it ensures is done correctly
26#include <frameobject.h>
27
36
37using namespace boost::python;
38
40
42
50template <> std::shared_ptr<IFunction> PythonObjectInstantiator<IFunction>::createInstance() const {
51 using namespace boost::python;
53
54 // The class may instantiate different objects depending on whether
55 // it is being created by the function factory or not
56 bool const isClassFactoryAware = PyObject_HasAttrString(m_classObject.ptr(), "_factory_use");
57
58 if (isClassFactoryAware) {
59 m_classObject.attr("_factory_use")();
60 }
61 object instance = m_classObject();
62 if (isClassFactoryAware) {
63 m_classObject.attr("_factory_free")();
64 }
65 auto instancePtr = extract<std::shared_ptr<IFunction>>(instance)();
66 auto *deleter = std::get_deleter<converter::shared_ptr_deleter, IFunction>(instancePtr);
67 instancePtr.reset(instancePtr.get(), GILSharedPtrDeleter(*deleter));
68 return instancePtr;
69}
70
71} // namespace Mantid::PythonInterface
72
73namespace {
75
76//------------------------------------------------------------------------------------------------------
82PyObject *getFunctionNames(FunctionFactoryImpl const *const self) {
83 const auto &names = self->getFunctionNames<Mantid::API::IFunction>();
84
85 PyObject *registered = PyList_New(0);
86 for (const auto &name : names) {
87 PyObject *value = to_python_value<const std::string &>()(name);
88 if (PyList_Append(registered, value))
89 throw std::runtime_error("Failed to insert value into PyList");
90 }
91
92 return registered;
93}
94
95//------------------------------------------------------------------------------------------------------
101PyObject *getBackgroundFunctionNames(FunctionFactoryImpl const *const self) {
102 const auto &names = self->getFunctionNames<Mantid::API::IFunction>();
103
104 PyObject *registered = PyList_New(0);
105 for (const auto &name : names) {
106 auto fun = self->createFunction(name);
107 if (dynamic_cast<IBackgroundFunction *>(fun.get())) {
108 PyObject *bkg_function = to_python_value<const std::string &>()(name);
109 if (PyList_Append(registered, bkg_function))
110 throw std::runtime_error("Failed to insert value into PyList");
111 }
112 }
113
114 return registered;
115}
116
117//------------------------------------------------------------------------------------------------------
123PyObject *getPeakFunctionNames(FunctionFactoryImpl const *const self) {
124 const auto &names = self->getFunctionNames<Mantid::API::IFunction>();
125
126 PyObject *registered = PyList_New(0);
127 for (const auto &name : names) {
128 auto fun = self->createFunction(name);
129 if (dynamic_cast<IPeakFunction *>(fun.get())) {
130 PyObject *peak_function = to_python_value<const std::string &>()(name);
131 if (PyList_Append(registered, peak_function))
132 throw std::runtime_error("Failed to insert value into PyList");
133 }
134 }
135
136 return registered;
137}
138
139//------------------------------------------------------------------------------------------------------
146Mantid::API::IPeakFunction_sptr createPeakFunction(FunctionFactoryImpl const *const self, const std::string &name) {
147 auto fun = self->createFunction(name);
148 auto peakFun = std::dynamic_pointer_cast<Mantid::API::IPeakFunction>(fun);
149 if (!peakFun) {
150 throw std::invalid_argument(name + " is not a PeakFunction");
151 }
152 return peakFun;
153}
154
155//------------------------------------------------------------------------------------------------------
165Mantid::API::CompositeFunction_sptr createCompositeFunction(FunctionFactoryImpl const *const self,
166 const std::string &name) {
167 auto fun = self->createFunction(name);
168 auto composite = std::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
169 if (composite) {
170 return composite;
171 }
172 std::string error_message = name + " is not a composite function.";
173 throw std::invalid_argument(error_message);
174}
175
176//----- Function registration -----
177
179std::recursive_mutex FUNCTION_REGISTER_MUTEX;
180
185void subscribe(FunctionFactoryImpl &self, PyObject *classObject) {
186 UninstallTrace uninstallTrace;
187 std::lock_guard<std::recursive_mutex> lock(FUNCTION_REGISTER_MUTEX);
188 static auto *baseClass =
189 const_cast<PyTypeObject *>(converter::registered<IFunction>::converters.to_python_target_type());
190
191 // obj should be a class deriving from IFunction
192 // PyObject_IsSubclass can set the error handler if classObject
193 // is not a class so this needs to be checked in two stages
194 const bool isSubClass = (PyObject_IsSubclass(classObject, reinterpret_cast<PyObject *>(baseClass)) == 1);
195 if (PyErr_Occurred() || !isSubClass) {
196 throw std::invalid_argument(std::string("subscribe(): Unexpected type. "
197 "Expected a class derived from "
198 "IFunction1D or IPeakFunction, "
199 "found: ") +
200 classObject->ob_type->tp_name);
201 }
202 // Instantiator will store a reference to the class object, so increase
203 // reference count with borrowed template
204 auto creator = std::make_unique<PythonObjectInstantiator<IFunction>>(object(handle<>(borrowed(classObject))));
205
206 // Can the function be created and initialized? It really shouldn't go in
207 // to the factory if not
208 auto func = creator->createInstance();
209 func->initialize();
210
211 // Takes ownership of instantiator
212 self.subscribe(func->name(), std::move(creator), FunctionFactoryImpl::OverwriteCurrent);
213}
215} // namespace
216
218
219 class_<FunctionFactoryImpl, boost::noncopyable>("FunctionFactoryImpl", no_init)
220 .def("getFunctionNames", &getFunctionNames, arg("self"), "Returns a list of the currently available functions")
221 .def("createCompositeFunction", &createCompositeFunction, (arg("self"), arg("name")),
222 "Return a pointer to the requested function")
223 .def("createFunction", &FunctionFactoryImpl::createFunction, (arg("self"), arg("type")),
224 "Return a pointer to the requested function")
225 .def("createInitialized", &FunctionFactoryImpl::createInitialized, (arg("self"), arg("init_expr")),
226 "Return a pointer to the requested function")
227 .def("createInitializedMultiDomainFunction", &FunctionFactoryImpl::createInitializedMultiDomainFunction,
228 (arg("self"), arg("init_expr"), arg("domain_number")), "Return a pointer to the requested function")
229 .def("subscribe", &subscribe, (arg("self"), arg("object")),
230 "Register a Python class derived from IFunction into the factory")
231 .def("unsubscribe", &FunctionFactoryImpl::unsubscribe, (arg("self"), arg("class_name")),
232 "Remove a type from the factory")
233 .def("Instance", &FunctionFactory::Instance, return_value_policy<reference_existing_object>(),
234 "Returns a reference to the FunctionFactory singleton")
235 .def("getBackgroundFunctionNames", &getBackgroundFunctionNames, arg("self"),
236 "Returns a list of the currently available background functions")
237 .def("getPeakFunctionNames", &getPeakFunctionNames, arg("self"),
238 "Returns a list of the currently available peak functions")
239 .def("createPeakFunction", &createPeakFunction, (arg("self"), arg("name")), "Return pointer to peak function.")
240 .staticmethod("Instance");
241}
double value
The value of the point.
Definition: FitMW.cpp:51
#define GET_POINTER_SPECIALIZATION(TYPE)
Definition: GetPointer.h:17
void export_FunctionFactory()
The FunctionFactory class is in charge of the creation of concrete instances of fitting functions.
std::shared_ptr< IFunction > createFunction(const std::string &type) const
Creates an instance of a function.
std::shared_ptr< MultiDomainFunction > createInitializedMultiDomainFunction(const std::string &input, size_t domainNumber) const
Creates an instnce of an inizialised multidomain function where each domain has the same function.
std::vector< std::string > getFunctionNames() const
Query available functions based on the template type.
void unsubscribe(const std::string &className)
void subscribe(const std::string &className, std::unique_ptr< AbstractFactory > pAbstractFactory, Kernel::DynamicFactory< IFunction >::SubscribeAction replace=ErrorIfExists)
std::shared_ptr< IFunction > createInitialized(const std::string &input) const
Creates an instance of a function.
An interface to a background function.
This is an interface to a fitting function - a semi-abstarct class.
Definition: IFunction.h:163
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
Definition: IPeakFunction.h:51
A composite function defined on a CompositeDomain.
Manage the lifetime of a class intended to be a singleton.
Defines a structure for acquiring/releasing the Python GIL using the RAII pattern.
std::shared_ptr< Base > createInstance() const override
Creates an instance of the object as shared_ptr to the Base type.
RAII handler to temporarily remove and reinstall a Python trace function.
std::shared_ptr< IPeakFunction > IPeakFunction_sptr
std::shared_ptr< CompositeFunction > CompositeFunction_sptr
shared pointer to the composite function base class
Special shared_ptr::deleter object that locks the GIL while deleting the underlying Python object.