Mantid
Loading...
Searching...
No Matches
Algorithm.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 +
7#ifdef _MSC_VER
8#pragma warning(disable : 4250) // Disable warning regarding inheritance via
9 // dominance, we have no way around it with the
10 // design
11#endif
14#ifdef _MSC_VER
15#pragma warning(default : 4250)
16#endif
19
21#include <boost/python/bases.hpp>
22#include <boost/python/class.hpp>
23#include <boost/python/dict.hpp>
24#include <boost/python/exception_translator.hpp>
25#include <boost/python/overloads.hpp>
26#include <optional>
27// As of boost 1.67 raw_function.hpp tries to pass
28// through size_t types through to make_function
29// which accepts int type, emitting a warning
30#ifdef _MSC_VER
31#pragma warning(push)
32#pragma warning(disable : 4267)
33#endif
34#include <boost/python/raw_function.hpp>
35#ifdef _MSC_VER
36#pragma warning(pop)
37#endif
38#include <boost/python/register_ptr_to_python.hpp>
39#include <boost/python/scope.hpp>
40#include <boost/variant.hpp>
41#include <boost/variant/static_visitor.hpp>
42
43#include <H5Cpp.h>
44#include <cstddef>
45#include <string>
46#include <variant>
47#include <vector>
48
52using namespace boost::python;
53
55
56namespace {
57using PythonAlgorithm = AlgorithmAdapter<Algorithm>;
58
59// declarePyAlgProperty(property*,doc)
60using declarePropertyType1 = void (*)(boost::python::object &, Mantid::Kernel::Property *, const std::string &);
61// declarePyAlgProperty(name, defaultValue, validator, doc, direction)
62using declarePropertyType2 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
63 const boost::python::object &, const std::string &, const int);
64// declarePyAlgProperty(name, defaultValue, doc, direction)
65using declarePropertyType3 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
66 const std::string &, const int);
67// declarePyAlgProperty(name, defaultValue, direction)
68using declarePropertyType4 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
69 const int);
70
71GNU_DIAG_OFF("unused-local-typedef")
72// Ignore -Wconversion warnings coming from boost::python
73// Seen with GCC 7.1.1 and Boost 1.63.0
74GNU_DIAG_OFF("conversion")
75// Overload types
76// cppcheck-suppress unknownMacro
77BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType1_Overload, PythonAlgorithm::declarePyAlgProperty, 2, 3)
78BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType2_Overload, PythonAlgorithm::declarePyAlgProperty, 3, 6)
79BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType3_Overload, PythonAlgorithm::declarePyAlgProperty, 4, 5)
80
81GNU_DIAG_ON("conversion")
82GNU_DIAG_ON("unused-local-typedef")
83
84GNU_DIAG_OFF("maybe-uninitialized")
90void translateCancel(const Algorithm::CancelException &exc) {
91 UNUSED_ARG(exc);
92 PyErr_SetString(PyExc_KeyboardInterrupt, "");
93}
94
100void translateH5Exception(const H5::Exception &exc) {
101 auto handleFrame = [](unsigned int n, const H5E_error_t *err, void *ss_arg) -> herr_t {
102 std::stringstream &ss_ = *((std::stringstream *)ss_arg);
103
104 ss_ << " #" << n << ": " << err->desc << "\n";
105
106 return (herr_t)0;
107 };
108
109 std::stringstream ss;
110 exc.walkErrorStack(H5E_WALK_DOWNWARD, handleFrame, /*client_data*/ &ss);
111
112 PyErr_SetString(PyExc_RuntimeError, ss.str().c_str());
113}
114
115template <typename T> std::optional<T> extractArg(ssize_t index, const tuple &args) {
116 if (index < len(args)) {
117 return std::optional<T>(extract<T>(args[index]));
118 }
119 return std::nullopt;
120}
121
122template <typename T> void extractKwargs(const dict &kwargs, const std::string &keyName, std::optional<T> &out) {
123 if (!kwargs.has_key(keyName)) {
124 return;
125 }
126 if (out != std::nullopt) {
127 throw std::invalid_argument("Parameter called '" + keyName +
128 "' was specified twice."
129 " This must be either positional or a kwarg, but not both.");
130 }
131 out = std::optional<T>(extract<T>(kwargs.get(keyName)));
132}
133
134class SetPropertyVisitor final : public Mantid::PythonInterface::IPyTypeVisitor {
135public:
136 SetPropertyVisitor(Mantid::API::Algorithm_sptr &alg, std::string const &propName)
137 : m_alg(alg), m_propName(propName) {}
138
139 void operator()(bool value) const override { setProp(value); }
140 void operator()(int value) const override { setProp(value); }
141 void operator()(double value) const override { setProp(value); }
142 void operator()(std::string value) const override { m_alg->setPropertyValue(m_propName, value); }
143 void operator()(Mantid::API::Workspace_sptr ws) const override { m_alg->setProperty(m_propName, std::move(ws)); }
144
145 void operator()(std::vector<bool> value) const override { setProp(value); }
146 void operator()(std::vector<int> value) const override { setProp(value); }
147 void operator()(std::vector<double> value) const override { setProp(value); }
148 void operator()(std::vector<std::string> value) const override { setProp(value); }
149
150 using Mantid::PythonInterface::IPyTypeVisitor::operator();
151
152private:
153 template <typename T> void setProp(const T &val) const { m_alg->setProperty(m_propName, val); }
154
156 std::string const &m_propName;
157};
158
159// Signature createChildWithProps(self, name, startProgress, endProgress, enableLogging, version, **kwargs)
160object createChildWithProps(tuple args, dict kwargs) {
161 Mantid::API::Algorithm_sptr parentAlg = extract<Mantid::API::Algorithm_sptr>(args[0]);
162 auto name = extractArg<std::string>(1, args);
163 auto startProgress = extractArg<double>(2, args);
164 auto endProgress = extractArg<double>(3, args);
165 auto enableLogging = extractArg<bool>(4, args);
166 auto version = extractArg<int>(5, args);
167
168 const std::array<std::string, 6> reservedNames = {"name", "startProgress", "endProgress",
169 "enableLogging", "version", "StoreInADS"};
170
171 extractKwargs<std::string>(kwargs, reservedNames[0], name);
172 extractKwargs<double>(kwargs, reservedNames[1], startProgress);
173 extractKwargs<double>(kwargs, reservedNames[2], endProgress);
174 extractKwargs<bool>(kwargs, reservedNames[3], enableLogging);
175 extractKwargs<int>(kwargs, reservedNames[4], version);
176
177 if (!name.has_value()) {
178 throw std::invalid_argument("Please specify the algorithm name");
179 }
180 auto childAlg = parentAlg->createChildAlgorithm(name.value(), startProgress.value_or(-1), endProgress.value_or(-1),
181 enableLogging.value_or(true), version.value_or(-1));
182
183 if (kwargs.has_key(reservedNames[5])) {
184 // We set StoreInADS here if it hasn't been set before and it is present in kwargs
185 std::optional<bool> storeADS = std::nullopt;
186 extractKwargs<bool>(kwargs, reservedNames[5], storeADS);
187 childAlg->setAlwaysStoreInADS(storeADS.value_or(false));
188 }
189
190 const list keys = kwargs.keys();
191 for (int i = 0; i < len(keys); ++i) {
192 const std::string propName = extract<std::string>(keys[i]);
193
194 if (std::find(reservedNames.cbegin(), reservedNames.cend(), propName) != reservedNames.cend())
195 continue;
196
197 object curArg = kwargs[keys[i]];
198 if (curArg.is_none())
199 continue;
200
202 auto nativeObj = PyNativeTypeExtractor::convert(curArg);
203
204 boost::apply_visitor(SetPropertyVisitor(childAlg, propName), nativeObj);
205 }
206 return object(childAlg);
207}
208
209GNU_DIAG_ON("maybe-uninitialized")
210} // namespace
211
213 register_ptr_to_python<std::shared_ptr<Algorithm>>();
214 register_exception_translator<Algorithm::CancelException>(&translateCancel);
215 register_exception_translator<H5::Exception>(&translateH5Exception);
216
217 // Export Algorithm but the actual held type in Python is
218 // std::shared_ptr<AlgorithmAdapter>
219 // See
220 // http://wiki.python.org/moin/boost.python/HowTo#ownership_of_C.2B-.2B-_object_extended_in_Python
221 class_<Algorithm, bases<Mantid::API::IAlgorithm>, std::shared_ptr<PythonAlgorithm>, boost::noncopyable>(
222 "Algorithm", "Base class for all algorithms")
223 .def("fromString", &Algorithm::fromString, "Initialize the algorithm from a string representation")
224 .staticmethod("fromString")
225 .def("createChildAlgorithm", raw_function(&createChildWithProps, std::size_t(1)),
226 "Creates and intializes a named child algorithm. Output workspaces "
227 "are given a dummy name.")
228 .def("declareProperty", (declarePropertyType1)&PythonAlgorithm::declarePyAlgProperty,
229 declarePropertyType1_Overload((arg("self"), arg("prop"), arg("doc") = "")))
230 .def("enableHistoryRecordingForChild", &Algorithm::enableHistoryRecordingForChild, (arg("self"), arg("on")),
231 "Turns history recording on or off for an algorithm.")
232 .def("enableHistoryRecordingForProcessGroups", &Algorithm::enableHistoryRecordingForProcessGroups,
233 (arg("self"), arg("on")),
234 "Turns parent history recording on or off for algorithms that handle group outputs directly.")
235 .def("declareProperty", (declarePropertyType2)&PythonAlgorithm::declarePyAlgProperty,
236 declarePropertyType2_Overload((arg("self"), arg("name"), arg("defaultValue"), arg("validator") = object(),
237 arg("doc") = "", arg("direction") = Direction::Input),
238 "Declares a named property where the type is taken from "
239 "the type of the defaultValue and mapped to an appropriate C++ "
240 "type"))
241 .def("declareProperty", (declarePropertyType3)&PythonAlgorithm::declarePyAlgProperty,
242 declarePropertyType3_Overload(
243 (arg("self"), arg("name"), arg("defaultValue"), arg("doc") = "", arg("direction") = Direction::Input),
244 "Declares a named property where the type is taken from the "
245 "type "
246 "of the defaultValue and mapped to an appropriate C++ type"))
247 .def("declareProperty", (declarePropertyType4)&PythonAlgorithm::declarePyAlgProperty,
248 (arg("self"), arg("name"), arg("defaultValue"), arg("direction") = Direction::Input),
249 "Declares a named property where the type is taken from the type "
250 "of the defaultValue and mapped to an appropriate C++ type")
251 .def("declareOrReplaceProperty", &PythonAlgorithm::declareOrReplacePyAlgProperty,
252 (arg("self"), arg("name"), arg("defaultValue"), arg("validator") = object(), arg("doc") = "",
253 arg("direction") = Direction::Input),
254 "Declares or replaces a named property where the type is taken from "
255 "the type of the defaultValue and mapped to an appropriate C++ "
256 "type")
257 .def("getLogger", &PythonAlgorithm::getLogger, arg("self"), return_value_policy<reference_existing_object>(),
258 "Returns a reference to this algorithm's logger")
259 .def("log", &PythonAlgorithm::getLogger, arg("self"), return_value_policy<reference_existing_object>(),
260 "Returns a reference to this algorithm's logger") // Traditional name
261
262 // deprecated methods
263 .def("setWikiSummary", &PythonAlgorithm::setWikiSummary, (arg("self"), arg("summary")),
264 "(Deprecated.) Set summary for the help.");
265
266 // Prior to version 3.2 there was a separate C++ PythonAlgorithm class that
267 // inherited from Algorithm and the "PythonAlgorithm"
268 // name was a distinct class in Python from the Algorithm export. In 3.2 the
269 // need for the C++ PythonAlgorithm class
270 // was removed in favour of simply adapting the Algorithm base class. A lot of
271 // client code relies on the "PythonAlgorithm" name in
272 // Python so we simply add an alias of the Algorithm name to PythonAlgorithm
273 scope().attr("PythonAlgorithm") = scope().attr("Algorithm");
274}
std::string name
Definition Run.cpp:60
double value
The value of the point.
Definition FitMW.cpp:51
Algorithm *const m_alg
#define GET_POINTER_SPECIALIZATION(TYPE)
Definition GetPointer.h:17
std::map< DeltaEMode::Type, std::string > index
int herr_t
void export_leaf_classes()
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition System.h:44
#define GNU_DIAG_ON(x)
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
static IAlgorithm_sptr fromString(const std::string &input)
De-serialize an object from a string.
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
void enableHistoryRecordingForChild(const bool on) override
Change the state of the history recording flag.
void enableHistoryRecordingForProcessGroups(const bool on) override
Change the state of the processGroups history recording flag.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
Base class for properties.
Definition Property.h:94
Provides a layer class for boost::python to allow C++ virtual functions to be overridden in a Python ...
virtual void operator()(bool value) const =0
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53