Algorithm.cpp 11.4 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
#ifdef _MSC_VER
8
9
10
#pragma warning(disable : 4250) // Disable warning regarding inheritance via
                                // dominance, we have no way around it with the
                                // design
11
#endif
12
#include "MantidAPI/DistributedAlgorithm.h"
LamarMoore's avatar
LamarMoore committed
13
14
15
#include "MantidAPI/ParallelAlgorithm.h"
#include "MantidAPI/SerialAlgorithm.h"
#include "MantidKernel/WarningSuppressions.h"
16
17
#include "MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h"
#ifdef _MSC_VER
18
#pragma warning(default : 4250)
19
#endif
20
#include "MantidPythonInterface/core/Converters/PyNativeTypeExtractor.h"
21
#include "MantidPythonInterface/core/GetPointer.h"
22

23
#include <boost/optional.hpp>
24
#include <boost/python/bases.hpp>
25
#include <boost/python/class.hpp>
26
#include <boost/python/dict.hpp>
27
#include <boost/python/exception_translator.hpp>
28
#include <boost/python/overloads.hpp>
29
#include <boost/python/raw_function.hpp>
30
#include <boost/python/register_ptr_to_python.hpp>
31
#include <boost/python/scope.hpp>
32

33
#include <cstddef>
34
#include <variant>
35

36
using Mantid::API::Algorithm;
37
using Mantid::API::DistributedAlgorithm;
LamarMoore's avatar
LamarMoore committed
38
39
using Mantid::API::ParallelAlgorithm;
using Mantid::API::SerialAlgorithm;
40
using Mantid::Kernel::Direction;
LamarMoore's avatar
LamarMoore committed
41
using Mantid::PythonInterface::AlgorithmAdapter;
42
using namespace boost::python;
43

44
GET_POINTER_SPECIALIZATION(Algorithm)
45
46
47
GET_POINTER_SPECIALIZATION(SerialAlgorithm)
GET_POINTER_SPECIALIZATION(ParallelAlgorithm)
GET_POINTER_SPECIALIZATION(DistributedAlgorithm)
48

49
namespace {
50
51
52
53
using PythonAlgorithm = AlgorithmAdapter<Algorithm>;
using PythonSerialAlgorithm = AlgorithmAdapter<SerialAlgorithm>;
using PythonParallelAlgorithm = AlgorithmAdapter<ParallelAlgorithm>;
using PythonDistributedAlgorithm = AlgorithmAdapter<DistributedAlgorithm>;
54
55

// declarePyAlgProperty(property*,doc)
Samuel Jones's avatar
Samuel Jones committed
56
using declarePropertyType1 = void (*)(boost::python::object &, Mantid::Kernel::Property *, const std::string &);
57
// declarePyAlgProperty(name, defaultValue, validator, doc, direction)
Samuel Jones's avatar
Samuel Jones committed
58
59
using declarePropertyType2 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
                                      const boost::python::object &, const std::string &, const int);
60
// declarePyAlgProperty(name, defaultValue, doc, direction)
Samuel Jones's avatar
Samuel Jones committed
61
using declarePropertyType3 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
62
                                      const std::string &, const int);
63
// declarePyAlgProperty(name, defaultValue, direction)
Samuel Jones's avatar
Samuel Jones committed
64
65
using declarePropertyType4 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
                                      const int);
66

Samuel Jackson's avatar
Samuel Jackson committed
67
GNU_DIAG_OFF("unused-local-typedef")
68
69
// Ignore -Wconversion warnings coming from boost::python
// Seen with GCC 7.1.1 and Boost 1.63.0
Samuel Jackson's avatar
Samuel Jackson committed
70
GNU_DIAG_OFF("conversion")
Hahn, Steven's avatar
Hahn, Steven committed
71
// Overload types
Samuel Jones's avatar
Samuel Jones committed
72
73
74
BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType1_Overload, PythonAlgorithm::declarePyAlgProperty, 2, 3)
BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType2_Overload, PythonAlgorithm::declarePyAlgProperty, 3, 6)
BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType3_Overload, PythonAlgorithm::declarePyAlgProperty, 4, 5)
75

Samuel Jackson's avatar
Samuel Jackson committed
76
GNU_DIAG_ON("conversion")
Samuel Jackson's avatar
Samuel Jackson committed
77
GNU_DIAG_ON("unused-local-typedef")
78

Hahn, Steven's avatar
Hahn, Steven committed
79
80
81
82
83
84
/**
 * Map a CancelException to a Python KeyboardInterupt
 * @param exc A cancel exception to translate. Unused here as the message is
 * ignored
 */
void translateCancel(const Algorithm::CancelException &exc) {
85
86
  UNUSED_ARG(exc);
  PyErr_SetString(PyExc_KeyboardInterrupt, "");
87
}
88
89
90
91
92
93
94
95
96
97
98
99
100
101

template <typename T> boost::optional<T> extractArg(ssize_t index, const tuple &args) {
  if (index < len(args)) {
    return boost::optional<T>(extract<T>(args[index]));
  }
  return boost::none;
}

template <typename T> void extractKwargs(const dict &kwargs, const std::string &keyName, boost::optional<T> &out) {
  if (kwargs.has_key(keyName)) {
    out = boost::optional<T>(extract<T>(kwargs.get(keyName)));
  }
}

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
class SetPropertyVisitor {
public:
  SetPropertyVisitor(Mantid::API::Algorithm_sptr &alg, std::string const &propName)
      : m_alg(alg), m_propName(propName) {}

  void operator()(bool value) { m_alg->setProperty(m_propName, value); }
  void operator()(int value) { m_alg->setProperty(m_propName, value); }
  void operator()(double value) { m_alg->setProperty(m_propName, value); }
  void operator()(std::string const &value) { m_alg->setPropertyValue(m_propName, value); }

private:
  Mantid::API::Algorithm_sptr &m_alg;
  std::string const &m_propName;
};

117
118
// Signature createChildWithProps(self, name, startProgress, endProgress, enableLogging, version, **kwargs)
object createChildWithProps(tuple args, dict kwargs) {
119
  Mantid::API::Algorithm_sptr parentAlg = extract<Mantid::API::Algorithm_sptr>(args[0]);
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  auto name = extractArg<std::string>(1, args);
  auto startProgress = extractArg<double>(2, args);
  auto endProgress = extractArg<double>(3, args);
  auto enableLogging = extractArg<bool>(4, args);
  auto version = extractArg<int>(5, args);

  const std::array<std::string, 5> reservedNames = {"name", "startProgress", "endProgress", "enableLogging", "version"};

  extractKwargs<std::string>(kwargs, reservedNames[0], name);
  extractKwargs<double>(kwargs, reservedNames[1], startProgress);
  extractKwargs<double>(kwargs, reservedNames[2], endProgress);
  extractKwargs<bool>(kwargs, reservedNames[3], enableLogging);
  extractKwargs<int>(kwargs, reservedNames[4], version);

  if (!name.is_initialized()) {
    throw std::invalid_argument("Please specify the algorithm name");
  }

  auto childAlg = parentAlg->createChildAlgorithm(name.value(), startProgress.value_or(-1), endProgress.value_or(-1),
                                                  enableLogging.value_or(true), version.value_or(-1));

  const list keys = kwargs.keys();
  for (int i = 0; i < len(keys); ++i) {
    const std::string propName = extract<std::string>(keys[i]);

    if (std::find(reservedNames.cbegin(), reservedNames.cend(), propName) != reservedNames.cend())
      continue;

    object curArg = kwargs[keys[i]];
Gemma Guest's avatar
Gemma Guest committed
149
150
151
    if (!curArg)
      continue;

152
153
154
155
    using Mantid::PythonInterface::PyNativeTypeExtractor;
    auto nativeObj = PyNativeTypeExtractor::convert(curArg);

    std::visit(SetPropertyVisitor(childAlg, propName), nativeObj);
156
157
158
159
  }
  return object(childAlg);
}

LamarMoore's avatar
LamarMoore committed
160
} // namespace
161

162
void export_leaf_classes() {
163
  register_ptr_to_python<std::shared_ptr<Algorithm>>();
164
  register_exception_translator<Algorithm::CancelException>(&translateCancel);
165

166
  // Export Algorithm but the actual held type in Python is
167
  // std::shared_ptr<AlgorithmAdapter>
168
169
  // See
  // http://wiki.python.org/moin/boost.python/HowTo#ownership_of_C.2B-.2B-_object_extended_in_Python
Samuel Jones's avatar
Samuel Jones committed
170
  class_<Algorithm, bases<Mantid::API::IAlgorithm>, std::shared_ptr<PythonAlgorithm>, boost::noncopyable>(
171
      "Algorithm", "Base class for all algorithms")
Samuel Jones's avatar
Samuel Jones committed
172
      .def("fromString", &Algorithm::fromString, "Initialize the algorithm from a string representation")
173
      .staticmethod("fromString")
174
      .def("createChildAlgorithm", raw_function(&createChildWithProps, 1),
175
176
           "Creates and intializes a named child algorithm. Output workspaces "
           "are given a dummy name.")
Samuel Jones's avatar
Samuel Jones committed
177
178
179
      .def("declareProperty", (declarePropertyType1)&PythonAlgorithm::declarePyAlgProperty,
           declarePropertyType1_Overload((arg("self"), arg("prop"), arg("doc") = "")))
      .def("enableHistoryRecordingForChild", &Algorithm::enableHistoryRecordingForChild, (arg("self"), arg("on")),
180
           "Turns history recording on or off for an algorithm.")
Samuel Jones's avatar
Samuel Jones committed
181
182
183
184
185
186
187
      .def("declareProperty", (declarePropertyType2)&PythonAlgorithm::declarePyAlgProperty,
           declarePropertyType2_Overload((arg("self"), arg("name"), arg("defaultValue"), arg("validator") = object(),
                                          arg("doc") = "", arg("direction") = Direction::Input),
                                         "Declares a named property where the type is taken from "
                                         "the type of the defaultValue and mapped to an appropriate C++ "
                                         "type"))
      .def("declareProperty", (declarePropertyType3)&PythonAlgorithm::declarePyAlgProperty,
188
           declarePropertyType3_Overload(
Samuel Jones's avatar
Samuel Jones committed
189
               (arg("self"), arg("name"), arg("defaultValue"), arg("doc") = "", arg("direction") = Direction::Input),
190
191
192
               "Declares a named property where the type is taken from the "
               "type "
               "of the defaultValue and mapped to an appropriate C++ type"))
Samuel Jones's avatar
Samuel Jones committed
193
194
      .def("declareProperty", (declarePropertyType4)&PythonAlgorithm::declarePyAlgProperty,
           (arg("self"), arg("name"), arg("defaultValue"), arg("direction") = Direction::Input),
195
196
           "Declares a named property where the type is taken from the type "
           "of the defaultValue and mapped to an appropriate C++ type")
Samuel Jones's avatar
Samuel Jones committed
197
      .def("getLogger", &PythonAlgorithm::getLogger, arg("self"), return_value_policy<reference_existing_object>(),
198
           "Returns a reference to this algorithm's logger")
Samuel Jones's avatar
Samuel Jones committed
199
      .def("log", &PythonAlgorithm::getLogger, arg("self"), return_value_policy<reference_existing_object>(),
200
201
202
           "Returns a reference to this algorithm's logger") // Traditional name

      // deprecated methods
Samuel Jones's avatar
Samuel Jones committed
203
      .def("setWikiSummary", &PythonAlgorithm::setWikiSummary, (arg("self"), arg("summary")),
204
205
206
207
208
209
210
211
           "(Deprecated.) Set summary for the help.");

  // Prior to version 3.2 there was a separate C++ PythonAlgorithm class that
  // inherited from Algorithm and the "PythonAlgorithm"
  // name was a distinct class in Python from the Algorithm export. In 3.2 the
  // need for the C++ PythonAlgorithm class
  // was removed in favour of simply adapting the Algorithm base class. A lot of
  // client code relies on the "PythonAlgorithm" name in
212
213
214
  // Python so we simply add an alias of the Algorithm name to PythonAlgorithm
  scope().attr("PythonAlgorithm") = scope().attr("Algorithm");
}
215
216

void export_SerialAlgorithm() {
217
  register_ptr_to_python<std::shared_ptr<SerialAlgorithm>>();
Samuel Jones's avatar
Samuel Jones committed
218
219
  register_exception_translator<SerialAlgorithm::CancelException>(&translateCancel);
  class_<SerialAlgorithm, bases<Mantid::API::Algorithm>, std::shared_ptr<PythonSerialAlgorithm>, boost::noncopyable>(
220
221
222
223
224
      "SerialAlgorithm", "Base class for simple serial algorithms");
  scope().attr("PythonSerialAlgorithm") = scope().attr("SerialAlgorithm");
}

void export_ParallelAlgorithm() {
225
  register_ptr_to_python<std::shared_ptr<ParallelAlgorithm>>();
Samuel Jones's avatar
Samuel Jones committed
226
227
228
  register_exception_translator<ParallelAlgorithm::CancelException>(&translateCancel);
  class_<ParallelAlgorithm, bases<Mantid::API::Algorithm>, std::shared_ptr<PythonParallelAlgorithm>,
         boost::noncopyable>("ParallelAlgorithm", "Base class for simple parallel algorithms");
229
230
231
232
  scope().attr("PythonParallelAlgorithm") = scope().attr("ParallelAlgorithm");
}

void export_DistributedAlgorithm() {
233
  register_ptr_to_python<std::shared_ptr<DistributedAlgorithm>>();
Samuel Jones's avatar
Samuel Jones committed
234
235
236
237
  register_exception_translator<DistributedAlgorithm::CancelException>(&translateCancel);
  class_<DistributedAlgorithm, bases<Mantid::API::Algorithm>, std::shared_ptr<PythonDistributedAlgorithm>,
         boost::noncopyable>("DistributedAlgorithm", "Base class for simple distributed algorithms");
  scope().attr("PythonDistributedAlgorithm") = scope().attr("DistributedAlgorithm");
238
}