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 <vector>
21
22using namespace Mantid::API;
26using namespace boost::python;
27
29
31extern template int NDArrayTypeIndex<float>::typenum;
32extern template int NDArrayTypeIndex<double>::typenum;
33} // namespace Mantid::PythonInterface::Converters
34
35namespace {
43PyObject *WrapReadOnlyNumpyFArray(const Mantid::signal_t *arr, std::vector<Py_intptr_t> dims) {
45#if NPY_API_VERSION >= 0x00000007 //(1.7)
46 auto *nparray = reinterpret_cast<PyArrayObject *>(
47 PyArray_New(&PyArray_Type, static_cast<int>(dims.size()), &dims[0], datatype, nullptr,
48 static_cast<void *>(const_cast<double *>(arr)), 0, NPY_ARRAY_FARRAY, nullptr));
49 PyArray_CLEARFLAGS(nparray, NPY_ARRAY_WRITEABLE);
50#else
51 PyArrayObject *nparray = reinterpret_cast<PyArrayObject *>(
52 PyArray_New(&PyArray_Type, static_cast<int>(dims.size()), &dims[0], datatype, nullptr,
53 static_cast<void *>(const_cast<double *>(arr)), 0, NPY_FARRAY, nullptr));
54 nparray->flags &= ~NPY_WRITEABLE;
55#endif
56 return reinterpret_cast<PyObject *>(nparray);
57}
58
64std::vector<Py_intptr_t> countDimensions(const IMDHistoWorkspace &self) {
65 size_t ndims = self.getNumDims();
66 std::vector<size_t> nd;
67 nd.reserve(ndims);
68
69 // invert dimensions in C way, e.g. slowest changing ndim goes first
70 for (size_t i = 0; i < ndims; ++i) {
71 nd.emplace_back(self.getDimension(i)->getNBins());
72 }
73
74 ndims = nd.size();
75 std::vector<Py_intptr_t> dims(ndims);
76 for (size_t i = 0; i < ndims; ++i)
77 dims[i] = static_cast<Py_intptr_t>(nd[i]);
78
79 if (dims.empty()) {
80 throw std::runtime_error("Workspace has zero dimensions!");
81 } else {
82 return dims;
83 }
84}
85
90PyObject *getSignalArrayAsNumpyArray(const IMDHistoWorkspace &self) {
91 auto dims = countDimensions(self);
92 return WrapReadOnlyNumpyFArray(self.getSignalArray(), dims);
93}
94
99PyObject *getErrorSquaredArrayAsNumpyArray(const IMDHistoWorkspace &self) {
100 auto dims = countDimensions(self);
101 return WrapReadOnlyNumpyFArray(self.getErrorSquaredArray(), dims);
102}
103
108PyObject *getNumEventsArrayAsNumpyArray(const IMDHistoWorkspace &self) {
109 auto dims = countDimensions(self);
110 return WrapReadOnlyNumpyFArray(self.getNumEventsArray(), dims);
111}
112
120void throwIfSizeIncorrect(const IMDHistoWorkspace &self, const NDArray &signal, const std::string &fnLabel) {
121 auto wsShape = countDimensions(self);
122 const size_t ndims = wsShape.size();
123 auto arrShape = signal.attr("shape");
124 if (ndims != static_cast<size_t>(len(arrShape))) {
125 std::ostringstream os;
126 os << fnLabel
127 << ": The number of dimensions doe not match the current "
128 "workspace size. Workspace="
129 << ndims << " array=" << len(arrShape);
130 throw std::invalid_argument(os.str());
131 }
132
133 for (size_t i = 0; i < ndims; ++i) {
134 int arrDim = extract<int>(arrShape[i])();
135 if (wsShape[i] != arrDim) {
136 std::ostringstream os;
137 os << fnLabel << ": The dimension size for the " << std::to_string(i) << "th dimension do not match. "
138 << "Workspace dimension size=" << wsShape[i] << ", array size=" << arrDim;
139 throw std::invalid_argument(os.str());
140 }
141 }
142}
143
152void setSignalArray(IMDHistoWorkspace &self, const NDArray &signalValues) {
153 throwIfSizeIncorrect(self, signalValues, "setSignalArray");
154 object rav = signalValues.attr("ravel")("F");
155 object flattened = rav.attr("flat");
156 auto length = len(flattened);
157 for (auto i = 0; i < length; ++i) {
158 self.setSignalAt(i, extract<double>(flattened[i])());
159 }
161}
162
171void setErrorSquaredArray(IMDHistoWorkspace &self, const NDArray &errorSquared) {
172 throwIfSizeIncorrect(self, errorSquared, "setErrorSquaredArray");
173 object rav = errorSquared.attr("ravel")("F");
174 object flattened = rav.attr("flat");
175 auto length = len(flattened);
176 for (auto i = 0; i < length; ++i) {
177 self.setErrorSquaredAt(i, extract<double>(flattened[i])());
178 }
180}
181
189void setNumEventsArray(IMDHistoWorkspace &self, const NDArray &numEvents) {
190 throwIfSizeIncorrect(self, numEvents, "setNumEventsArray");
191 object rav = numEvents.attr("ravel")("F");
192 object flattened = rav.attr("flat");
193 const auto length = static_cast<size_t>(len(flattened));
194 // Buffer conversions first so a failed Python-to-double extraction cannot partially modify the workspace.
195 std::vector<double> values;
196 values.reserve(length);
197 for (size_t i = 0; i < length; ++i) {
198 values.emplace_back(extract<double>(flattened[i])());
199 }
200 auto *dest = self.mutableNumEventsArray();
201 for (size_t i = 0; i < values.size(); ++i) {
202 dest[i] = values[i];
203 }
205}
206
210void setSignalAt(IMDHistoWorkspace &self, const size_t index, const double value) {
211 if (index >= self.getNPoints())
212 throw std::invalid_argument("setSignalAt: The index is greater than the "
213 "number of bins in the workspace");
214
215 self.setSignalAt(index, value);
216}
217} // namespace
218
220 // IMDHistoWorkspace class
221 class_<IMDHistoWorkspace, bases<IMDWorkspace, MultipleExperimentInfos>, boost::noncopyable>("IMDHistoWorkspace",
222 no_init)
223 .def("getSignalArray", &getSignalArrayAsNumpyArray, arg("self"),
224 "Returns a read-only numpy array containing the signal values")
225
226 .def("getErrorSquaredArray", &getErrorSquaredArrayAsNumpyArray, arg("self"),
227 "Returns a read-only numpy array containing the square of the error "
228 "values")
229
230 .def("getNumEventsArray", &getNumEventsArrayAsNumpyArray, arg("self"),
231 "Returns a read-only numpy array containing the number of MD events "
232 "in each bin")
233
234 .def("signalAt", &IMDHistoWorkspace::signalAt, (arg("self"), arg("index")),
235 return_value_policy<copy_non_const_reference>(), "Return a reference to the signal at the linear index")
236
237 .def("errorSquaredAt", &IMDHistoWorkspace::errorSquaredAt, (arg("self"), arg("index")),
238 return_value_policy<copy_non_const_reference>(), "Return the squared-errors at the linear index")
239
240 .def("setSignalAt", &setSignalAt, (arg("self"), arg("index"), arg("value")),
241 "Sets the signal at the specified index.")
242
243 .def("setErrorSquaredAt", &IMDHistoWorkspace::setErrorSquaredAt, (arg("self"), arg("index"), arg("value")),
244 "Sets the squared-error at the specified index.")
245
246 .def("setSignalArray", &setSignalArray, (arg("self"), arg("signalValues")),
247 "Sets the signal from a numpy array. The sizes must match the "
248 "current workspace sizes. A ValueError is thrown if not. Any link "
249 "to the original MDEventWorkspace(s) is cleared.")
250
251 .def("setErrorSquaredArray", &setErrorSquaredArray, (arg("self"), arg("errorSquared")),
252 "Sets the square of the errors from a numpy array. The sizes must "
253 "match the current workspace sizes. A ValueError is thrown if not. Any "
254 "link to the original MDEventWorkspace(s) is cleared.")
255
256 .def("setNumEventsArray", &setNumEventsArray, (arg("self"), arg("numEvents")),
257 "Sets the number of events from a numpy array. The sizes must match "
258 "the current workspace sizes, otherwise a ValueError is thrown. Any link "
259 "to the original MDEventWorkspace(s) is cleared.")
260
261 .def("setTo", &IMDHistoWorkspace::setTo, (arg("self"), arg("signal"), arg("error_squared"), arg("num_events")),
262 "Sets all signals/errors in the workspace to the given values")
263
264 .def("getInverseVolume", &IMDHistoWorkspace::getInverseVolume, arg("self"),
265 return_value_policy<return_by_value>(), "Return the inverse of volume of EACH cell in the workspace.")
266
267 .def("getLinearIndex", (size_t (IMDHistoWorkspace::*)(size_t, size_t) const) & IMDHistoWorkspace::getLinearIndex,
268 (arg("self"), arg("index1"), arg("index2")), return_value_policy<return_by_value>(),
269 "Get the 1D linear index from the 2D array")
270
271 .def("getLinearIndex",
272 (size_t (IMDHistoWorkspace::*)(size_t, size_t, size_t) const) & IMDHistoWorkspace::getLinearIndex,
273 (arg("self"), arg("index1"), arg("index2"), arg("index3")), return_value_policy<return_by_value>(),
274 "Get the 1D linear index from the 3D array")
275
276 .def("getLinearIndex",
277 (size_t (IMDHistoWorkspace::*)(size_t, size_t, size_t, size_t) const) & IMDHistoWorkspace::getLinearIndex,
278 (arg("self"), arg("index1"), arg("index2"), arg("index3"), arg("index4")),
279 return_value_policy<return_by_value>(), "Get the 1D linear index from the 4D array")
280
281 .def("getCenter", &IMDHistoWorkspace::getCenter, (arg("self"), arg("linear_index")),
282 return_value_policy<return_by_value>(), "Return the position of the center of a bin at a given position")
283
284 .def("setDisplayNormalization", &IMDHistoWorkspace::setDisplayNormalization, (arg("self"), arg("normalization")),
285 "Sets the visual normalization of"
286 " the workspace.");
287
288 //-------------------------------------------------------------------------------------------------
289
291}
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 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 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
std::size_t numEvents(Nexus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames, const std::string &prefix)
Get the number of events in the currently opened group.
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...