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"
19
20#include <boost/python/class.hpp>
21#include <boost/python/def.hpp>
22#include <mutex>
23
24// Python frameobject. This is under the boost includes so that boost will have
25// done the
26// include of Python.h which it ensures is done correctly
27#include <frameobject.h>
28
38
39using namespace boost::python;
40
42
44
52template <> std::shared_ptr<IFunction> PythonObjectInstantiator<IFunction>::createInstance() const {
53 using namespace boost::python;
55
56 // The class may instantiate different objects depending on whether
57 // it is being created by the function factory or not
58 bool const isClassFactoryAware = PyObject_HasAttrString(m_classObject.ptr(), "_factory_use");
59
60 if (isClassFactoryAware) {
61 m_classObject.attr("_factory_use")();
62 }
63 object instance = m_classObject();
64 if (isClassFactoryAware) {
65 m_classObject.attr("_factory_free")();
66 }
67 auto instancePtr = extract<std::shared_ptr<IFunction>>(instance)();
68 auto *deleter = std::get_deleter<converter::shared_ptr_deleter, IFunction>(instancePtr);
69 instancePtr.reset(instancePtr.get(), GILSharedPtrDeleter(*deleter));
70 return instancePtr;
71}
72
73} // namespace Mantid::PythonInterface
74
75namespace {
77
78//------------------------------------------------------------------------------------------------------
84PyObject *getFunctionNames(FunctionFactoryImpl const *const self) {
85 std::vector<std::string> names;
86 {
87 // We need to release the GIL here to prevent a deadlock when using Python log channels
90 }
91
92 PyObject *registered = PyList_New(0);
93 for (const auto &name : names) {
94 PyObject *value = to_python_value<const std::string &>()(name);
95 if (PyList_Append(registered, value))
96 throw std::runtime_error("Failed to insert value into PyList");
97 }
98
99 return registered;
100}
101
102//------------------------------------------------------------------------------------------------------
108PyObject *getBackgroundFunctionNames(FunctionFactoryImpl const *const self) {
109 const auto &names = self->getFunctionNames<Mantid::API::IFunction>();
110
111 PyObject *registered = PyList_New(0);
112 for (const auto &name : names) {
113 auto fun = self->createFunction(name);
114 if (dynamic_cast<IBackgroundFunction *>(fun.get())) {
115 PyObject *bkg_function = to_python_value<const std::string &>()(name);
116 if (PyList_Append(registered, bkg_function))
117 throw std::runtime_error("Failed to insert value into PyList");
118 }
119 }
120
121 return registered;
122}
123
124//------------------------------------------------------------------------------------------------------
130PyObject *getPeakFunctionNames(FunctionFactoryImpl const *const self) {
131 const auto &names = self->getFunctionNames<Mantid::API::IFunction>();
132
133 PyObject *registered = PyList_New(0);
134 for (const auto &name : names) {
135 auto fun = self->createFunction(name);
136 if (dynamic_cast<IPeakFunction *>(fun.get())) {
137 PyObject *peak_function = to_python_value<const std::string &>()(name);
138 if (PyList_Append(registered, peak_function))
139 throw std::runtime_error("Failed to insert value into PyList");
140 }
141 }
142
143 return registered;
144}
145
146//------------------------------------------------------------------------------------------------------
153Mantid::API::IPeakFunction_sptr createPeakFunction(FunctionFactoryImpl const *const self, const std::string &name) {
154 auto fun = self->createFunction(name);
155 auto peakFun = std::dynamic_pointer_cast<Mantid::API::IPeakFunction>(fun);
156 if (!peakFun) {
157 throw std::invalid_argument(name + " is not a PeakFunction");
158 }
159 return peakFun;
160}
161
162//------------------------------------------------------------------------------------------------------
172Mantid::API::CompositeFunction_sptr createCompositeFunction(FunctionFactoryImpl const *const self,
173 const std::string &name) {
174 auto fun = self->createFunction(name);
175 auto composite = std::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
176 if (composite) {
177 return composite;
178 }
179 std::string error_message = name + " is not a composite function.";
180 throw std::invalid_argument(error_message);
181}
182
183//----- Function registration -----
184
186std::recursive_mutex FUNCTION_REGISTER_MUTEX;
187
192void subscribe(FunctionFactoryImpl &self, PyObject *classObject) {
193 UninstallTrace uninstallTrace;
194 std::lock_guard<std::recursive_mutex> lock(FUNCTION_REGISTER_MUTEX);
195 static auto *baseClass =
196 const_cast<PyTypeObject *>(converter::registered<IFunction>::converters.to_python_target_type());
197
198 // obj should be a class deriving from IFunction
199 // PyObject_IsSubclass can set the error handler if classObject
200 // is not a class so this needs to be checked in two stages
201 const bool isSubClass = (PyObject_IsSubclass(classObject, reinterpret_cast<PyObject *>(baseClass)) == 1);
202 if (PyErr_Occurred() || !isSubClass) {
203 throw std::invalid_argument(std::string("subscribe(): Unexpected type. "
204 "Expected a class derived from "
205 "IFunction1D or IPeakFunction, "
206 "found: ") +
207 classObject->ob_type->tp_name);
208 }
209 // Instantiator will store a reference to the class object, so increase
210 // reference count with borrowed template
211 auto creator = std::make_unique<PythonObjectInstantiator<IFunction>>(object(handle<>(borrowed(classObject))));
212
213 // Can the function be created and initialized? It really shouldn't go in
214 // to the factory if not
215 auto func = creator->createInstance();
216 func->initialize();
217
218 // Takes ownership of instantiator
219 self.subscribe(func->name(), std::move(creator), FunctionFactoryImpl::OverwriteCurrent);
220}
222} // namespace
223
225
226 class_<FunctionFactoryImpl, boost::noncopyable>("FunctionFactoryImpl", no_init)
227 .def("getFunctionNames", &getFunctionNames, arg("self"), "Returns a list of the currently available functions")
228 .def("createCompositeFunction", &createCompositeFunction, (arg("self"), arg("name")),
229 "Return a pointer to the requested function")
230 .def("createFunction", &FunctionFactoryImpl::createFunction, (arg("self"), arg("type")),
231 "Return a pointer to the requested function")
232 .def("createInitialized", &FunctionFactoryImpl::createInitialized, (arg("self"), arg("init_expr")),
233 "Return a pointer to the requested function")
234 .def("createInitializedMultiDomainFunction", &FunctionFactoryImpl::createInitializedMultiDomainFunction,
235 (arg("self"), arg("init_expr"), arg("domain_number")), "Return a pointer to the requested function")
236 .def("subscribe", &subscribe, (arg("self"), arg("object")),
237 "Register a Python class derived from IFunction into the factory")
238 .def("unsubscribe", &FunctionFactoryImpl::unsubscribe, (arg("self"), arg("class_name")),
239 "Remove a type from the factory")
240 .def("Instance", &FunctionFactory::Instance, return_value_policy<reference_existing_object>(),
241 "Returns a reference to the FunctionFactory singleton")
242 .def("getBackgroundFunctionNames", &getBackgroundFunctionNames, arg("self"),
243 "Returns a list of the currently available background functions")
244 .def("getPeakFunctionNames", &getPeakFunctionNames, arg("self"),
245 "Returns a list of the currently available peak functions")
246 .def("createPeakFunction", &createPeakFunction, (arg("self"), arg("name")), "Return pointer to peak function.")
247 .staticmethod("Instance");
248}
std::string name
Definition Run.cpp:60
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:166
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
A composite function defined on a CompositeDomain.
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.
Defines a structure for releasing the Python GIL using the RAII pattern.
RAII handler to temporarily remove and reinstall a Python trace function.
std::shared_ptr< IPeakFunction > IPeakFunction_sptr
Mantid::Kernel::SingletonHolder< FunctionFactoryImpl > FunctionFactory
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.