Mantid
Loading...
Searching...
No Matches
IMDHistoWorkspace.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 +
13
14#include <boost/python/class.hpp>
15#include <boost/python/copy_non_const_reference.hpp>
16#define PY_ARRAY_UNIQUE_SYMBOL API_ARRAY_API
17#define NO_IMPORT_ARRAY
18#include <numpy/arrayobject.h>
19
20#include <algorithm>
21#include <stdexcept>
22#include <vector>
23
24using namespace Mantid::API;
28using namespace boost::python;
29
31
33extern template int NDArrayTypeIndex<float>::typenum;
34extern template int NDArrayTypeIndex<double>::typenum;
35} // namespace Mantid::PythonInterface::Converters
36
37namespace {
45PyObject *WrapReadOnlyNumpyFArray(const Mantid::signal_t *arr, std::vector<Py_intptr_t> dims) {
47#if NPY_API_VERSION >= 0x00000007 //(1.7)
48 auto *nparray = reinterpret_cast<PyArrayObject *>(
49 PyArray_New(&PyArray_Type, static_cast<int>(dims.size()), &dims[0], datatype, nullptr,
50 static_cast<void *>(const_cast<double *>(arr)), 0, NPY_ARRAY_FARRAY, nullptr));
51 PyArray_CLEARFLAGS(nparray, NPY_ARRAY_WRITEABLE);
52#else
53 PyArrayObject *nparray = reinterpret_cast<PyArrayObject *>(
54 PyArray_New(&PyArray_Type, static_cast<int>(dims.size()), &dims[0], datatype, nullptr,
55 static_cast<void *>(const_cast<double *>(arr)), 0, NPY_FARRAY, nullptr));
56 nparray->flags &= ~NPY_WRITEABLE;
57#endif
58 return reinterpret_cast<PyObject *>(nparray);
59}
60
66std::vector<Py_intptr_t> countDimensions(const IMDHistoWorkspace &self) {
67 size_t ndims = self.getNumDims();
68 std::vector<size_t> nd;
69 nd.reserve(ndims);
70
71 // invert dimensions in C way, e.g. slowest changing ndim goes first
72 for (size_t i = 0; i < ndims; ++i) {
73 nd.emplace_back(self.getDimension(i)->getNBins());
74 }
75
76 ndims = nd.size();
77 std::vector<Py_intptr_t> dims(ndims);
78 for (size_t i = 0; i < ndims; ++i)
79 dims[i] = static_cast<Py_intptr_t>(nd[i]);
80
81 if (dims.empty()) {
82 throw std::runtime_error("Workspace has zero dimensions!");
83 } else {
84 return dims;
85 }
86}
87
92PyObject *getSignalArrayAsNumpyArray(const IMDHistoWorkspace &self) {
93 auto dims = countDimensions(self);
94 return WrapReadOnlyNumpyFArray(self.getSignalArray(), dims);
95}
96
101PyObject *getErrorSquaredArrayAsNumpyArray(const IMDHistoWorkspace &self) {
102 auto dims = countDimensions(self);
103 return WrapReadOnlyNumpyFArray(self.getErrorSquaredArray(), dims);
104}
105
110PyObject *getNumEventsArrayAsNumpyArray(const IMDHistoWorkspace &self) {
111 auto dims = countDimensions(self);
112 return WrapReadOnlyNumpyFArray(self.getNumEventsArray(), dims);
113}
114
122void throwIfSizeIncorrect(const IMDHistoWorkspace &self, const NDArray &signal, const std::string &fnLabel) {
123 auto wsShape = countDimensions(self);
124 const size_t ndims = wsShape.size();
125 auto arrShape = signal.attr("shape");
126 if (ndims != static_cast<size_t>(len(arrShape))) {
127 std::ostringstream os;
128 os << fnLabel
129 << ": The number of dimensions doe not match the current "
130 "workspace size. Workspace="
131 << ndims << " array=" << len(arrShape);
132 throw std::invalid_argument(os.str());
133 }
134
135 for (size_t i = 0; i < ndims; ++i) {
136 int arrDim = extract<int>(arrShape[i])();
137 if (wsShape[i] != arrDim) {
138 std::ostringstream os;
139 os << fnLabel << ": The dimension size for the " << std::to_string(i) << "th dimension do not match. "
140 << "Workspace dimension size=" << wsShape[i] << ", array size=" << arrDim;
141 throw std::invalid_argument(os.str());
142 }
143 }
144}
145
159void copyToBuffer(const NDArray &values, Mantid::signal_t *dest) {
160 int flags = NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_ALIGNED;
161 const int type = PyArray_TYPE(reinterpret_cast<PyArrayObject *>(values.ptr()));
162 if (type == NPY_OBJECT || PyTypeNum_ISFLOAT(type))
163 flags |= NPY_ARRAY_FORCECAST;
164 PyObject *converted = PyArray_FROMANY(values.ptr(), NPY_DOUBLE, 0, 0, flags);
165 // Throw directly rather than through throw_error_already_set(), so static analysis sees that execution stops here
166 if (!converted)
167 throw error_already_set();
168 handle<> owner(converted); // releases the converted array, also if std::copy throws
169 auto *array = reinterpret_cast<PyArrayObject *>(converted);
170 // Raw pointer access below requires an aligned, native float64 array in Fortran order
171 if (PyArray_TYPE(array) != NPY_DOUBLE || !PyArray_ISFARRAY_RO(array) || !PyArray_ISNOTSWAPPED(array))
172 throw std::runtime_error("numpy did not convert the array to an aligned, native float64 array in Fortran order");
173 const auto *source = static_cast<const double *>(PyArray_DATA(array));
174 std::copy(source, source + PyArray_SIZE(array), dest);
175}
176
184void setSignalArray(IMDHistoWorkspace &self, const NDArray &signalValues) {
185 throwIfSizeIncorrect(self, signalValues, "setSignalArray");
186 copyToBuffer(signalValues, self.mutableSignalArray());
188}
189
197void setErrorSquaredArray(IMDHistoWorkspace &self, const NDArray &errorSquared) {
198 throwIfSizeIncorrect(self, errorSquared, "setErrorSquaredArray");
199 copyToBuffer(errorSquared, self.mutableErrorSquaredArray());
201}
202
209void setNumEventsArray(IMDHistoWorkspace &self, const NDArray &numEvents) {
210 throwIfSizeIncorrect(self, numEvents, "setNumEventsArray");
211 copyToBuffer(numEvents, self.mutableNumEventsArray());
213}
214
218void setSignalAt(IMDHistoWorkspace &self, const size_t index, const double value) {
219 if (index >= self.getNPoints())
220 throw std::invalid_argument("setSignalAt: The index is greater than the "
221 "number of bins in the workspace");
222
223 self.setSignalAt(index, value);
224}
225} // namespace
226
228 // IMDHistoWorkspace class
229 class_<IMDHistoWorkspace, bases<IMDWorkspace, MultipleExperimentInfos>, boost::noncopyable>("IMDHistoWorkspace",
230 no_init)
231 .def("getSignalArray", &getSignalArrayAsNumpyArray, arg("self"),
232 "Returns a read-only numpy array containing the signal values")
233
234 .def("getErrorSquaredArray", &getErrorSquaredArrayAsNumpyArray, arg("self"),
235 "Returns a read-only numpy array containing the square of the error "
236 "values")
237
238 .def("getNumEventsArray", &getNumEventsArrayAsNumpyArray, arg("self"),
239 "Returns a read-only numpy array containing the number of MD events "
240 "in each bin")
241
242 .def("signalAt", &IMDHistoWorkspace::signalAt, (arg("self"), arg("index")),
243 return_value_policy<copy_non_const_reference>(), "Return a reference to the signal at the linear index")
244
245 .def("errorSquaredAt", &IMDHistoWorkspace::errorSquaredAt, (arg("self"), arg("index")),
246 return_value_policy<copy_non_const_reference>(), "Return the squared-errors at the linear index")
247
248 .def("setSignalAt", &setSignalAt, (arg("self"), arg("index"), arg("value")),
249 "Sets the signal at the specified index.")
250
251 .def("setErrorSquaredAt", &IMDHistoWorkspace::setErrorSquaredAt, (arg("self"), arg("index"), arg("value")),
252 "Sets the squared-error at the specified index.")
253
254 .def("setSignalArray", &setSignalArray, (arg("self"), arg("signalValues")),
255 "Sets the signal from a numpy array. The sizes must match the "
256 "current workspace sizes. A ValueError is thrown if not. Any link "
257 "to the original MDEventWorkspace(s) is cleared.")
258
259 .def("setErrorSquaredArray", &setErrorSquaredArray, (arg("self"), arg("errorSquared")),
260 "Sets the square of the errors from a numpy array. The sizes must "
261 "match the current workspace sizes. A ValueError is thrown if not. Any "
262 "link to the original MDEventWorkspace(s) is cleared.")
263
264 .def("setNumEventsArray", &setNumEventsArray, (arg("self"), arg("numEvents")),
265 "Sets the number of events from a numpy array. The sizes must match "
266 "the current workspace sizes, otherwise a ValueError is thrown. Any link "
267 "to the original MDEventWorkspace(s) is cleared.")
268
269 .def("setTo", &IMDHistoWorkspace::setTo, (arg("self"), arg("signal"), arg("error_squared"), arg("num_events")),
270 "Sets all signals/errors in the workspace to the given values")
271
272 .def("getInverseVolume", &IMDHistoWorkspace::getInverseVolume, arg("self"),
273 return_value_policy<return_by_value>(), "Return the inverse of volume of EACH cell in the workspace.")
274
275 .def("getLinearIndex", (size_t (IMDHistoWorkspace::*)(size_t, size_t) const) & IMDHistoWorkspace::getLinearIndex,
276 (arg("self"), arg("index1"), arg("index2")), return_value_policy<return_by_value>(),
277 "Get the 1D linear index from the 2D array")
278
279 .def("getLinearIndex",
280 (size_t (IMDHistoWorkspace::*)(size_t, size_t, size_t) const) & IMDHistoWorkspace::getLinearIndex,
281 (arg("self"), arg("index1"), arg("index2"), arg("index3")), return_value_policy<return_by_value>(),
282 "Get the 1D linear index from the 3D array")
283
284 .def("getLinearIndex",
285 (size_t (IMDHistoWorkspace::*)(size_t, size_t, size_t, size_t) const) & IMDHistoWorkspace::getLinearIndex,
286 (arg("self"), arg("index1"), arg("index2"), arg("index3"), arg("index4")),
287 return_value_policy<return_by_value>(), "Get the 1D linear index from the 4D array")
288
289 .def("getCenter", &IMDHistoWorkspace::getCenter, (arg("self"), arg("linear_index")),
290 return_value_policy<return_by_value>(), "Return the position of the center of a bin at a given position")
291
292 .def("setDisplayNormalization", &IMDHistoWorkspace::setDisplayNormalization, (arg("self"), arg("normalization")),
293 "Sets the visual normalization of"
294 " the workspace.");
295
296 //-------------------------------------------------------------------------------------------------
297
299}
double value
The value of the point.
Definition FitMW.cpp:51
#define GET_POINTER_SPECIALIZATION(TYPE)
Definition GetPointer.h:17
std::map< DeltaEMode::Type, std::string > index
tagPyArrayObject PyArrayObject
void export_IMDHistoWorkspace()
Abstract interface to MDHistoWorkspace, for use in exposing to Python.
virtual signal_t & errorSquaredAt(size_t index)=0
virtual Mantid::Kernel::VMD getCenter(size_t linearIndex) const =0
virtual const signal_t * getErrorSquaredArray() const =0
virtual const signal_t * getNumEventsArray() const =0
virtual signal_t & signalAt(size_t index)=0
virtual signal_t * mutableNumEventsArray()=0
virtual void setErrorSquaredAt(size_t index, signal_t value)=0
virtual const signal_t * getSignalArray() const =0
virtual signal_t * mutableSignalArray()=0
virtual void setDisplayNormalization(const Mantid::API::MDNormalization &preferredNormalization)=0
virtual void setSignalAt(size_t index, signal_t value)=0
virtual coord_t getInverseVolume() const =0
See the MDHistoWorkspace definition for descriptions of these.
virtual signal_t * mutableErrorSquaredArray()=0
virtual size_t getLinearIndex(size_t index1, size_t index2) const =0
virtual void setTo(signal_t signal, signal_t errorSquared, signal_t numEvents)=0
virtual uint64_t getNPoints() const =0
Get the number of points associated with the workspace.
virtual std::shared_ptr< const Mantid::Geometry::IMDDimension > getDimension(size_t index) const
Get a dimension.
virtual size_t getNumDims() const
void clearOriginalWorkspaces()
Clear original workspaces.
Thin object wrapper around a numpy array.
Definition NDArray.h:31
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition MDTypes.h:36
std::string to_string(const wide_integer< Bits, Signed > &n)
Defines a mapping between C++ type given by the template parameter and numpy type enum NPY_TYPES.
Encapsulates the registration required for an interface type T that sits on top of a Kernel::DataItem...