WrapWithNDArray.cpp 4.35 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
8
9
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
10
11
#include "MantidPythonInterface/core/Converters/WrapWithNDArray.h"
#include "MantidPythonInterface/core/Converters/NDArrayTypeIndex.h"
12
13

#include <boost/python/list.hpp>
14
#define PY_ARRAY_UNIQUE_SYMBOL CORE_ARRAY_API
15
16
17
18
19
#define NO_IMPORT_ARRAY
#include <numpy/arrayobject.h>

#include <string>

20
21
22
23
24
25
26
namespace {

/**
  Destructor for a capsule object being passed to Python. This helps release
  memory of arrays passed to Python.
  See: https://stackoverflow.com/questions/52731884/pyarray-simplenewfromdata
*/
Harry Saunders's avatar
Harry Saunders committed
27
28
template <typename T> void capsule_cleanup(PyObject *capsule) {
  auto *memory = static_cast<T *>(PyCapsule_GetPointer(capsule, NULL));
29
30
31
32
33
  delete[] memory;
}

} // namespace

34
35
36
37
38
39
40
41
42
43
44
45
46
namespace Mantid::PythonInterface::Converters {
#ifdef __APPLE__
extern template int NDArrayTypeIndex<bool>::typenum;
extern template int NDArrayTypeIndex<int>::typenum;
extern template int NDArrayTypeIndex<long>::typenum;
extern template int NDArrayTypeIndex<long long>::typenum;
extern template int NDArrayTypeIndex<unsigned int>::typenum;
extern template int NDArrayTypeIndex<unsigned long>::typenum;
extern template int NDArrayTypeIndex<unsigned long long>::typenum;
extern template int NDArrayTypeIndex<float>::typenum;
extern template int NDArrayTypeIndex<double>::typenum;
#endif

47
48
49
50
51
52
53
54
55
56
57
58
59
60
namespace {
/**
 * Flip the writable flag to ensure the array is read only
 * Numpy v1.7 removed access to the flags fields directly
 * and introduced the PyClear_Flags function.
 * @param arr A pointer to a numpy array
 */
void markReadOnly(PyArrayObject *arr) {
#if NPY_API_VERSION >= 0x00000007 //(1.7)
  PyArray_CLEARFLAGS(arr, NPY_ARRAY_WRITEABLE);
#else
  arr->flags &= ~NPY_WRITEABLE;
#endif
}
LamarMoore's avatar
LamarMoore committed
61
} // namespace
62

63
64
namespace Impl {

65
66
67
68
69
70
71
72
73
/**
 * Defines the wrapWithNDArray specialization for C array types
 *
 * Wraps an array in a numpy array structure without copying the data
 * @param carray :: A pointer to the HEAD of the array
 * @param ndims :: The dimensionality of the array
 * @param dims :: The length of the arrays in each dimension
 * @param mode :: A mode switch to define whether the final array is read
 *only/read-write
74
75
 * @param oMode :: A mode switch defining whether to transfer ownership of the
 *returned array to python
76
77
78
 * @return A pointer to a numpy ndarray object
 */
template <typename ElementType>
Samuel Jones's avatar
Samuel Jones committed
79
PyObject *wrapWithNDArray(const ElementType *carray, const int ndims, Py_intptr_t *dims, const NumpyWrapMode mode,
80
                          const OwnershipMode oMode /* = Cpp */) {
81
  int datatype = NDArrayTypeIndex<ElementType>::typenum;
Samuel Jones's avatar
Samuel Jones committed
82
83
  auto *nparray = (PyArrayObject *)PyArray_SimpleNewFromData(ndims, dims, datatype,
                                                             static_cast<void *>(const_cast<ElementType *>(carray)));
84

85
  if (oMode == Python) {
Samuel Jones's avatar
Samuel Jones committed
86
    PyObject *capsule = PyCapsule_New(const_cast<ElementType *>(carray), NULL, capsule_cleanup<ElementType>);
87
88
89
    PyArray_SetBaseObject((PyArrayObject *)nparray, capsule);
  }

90
91
  if (mode == ReadOnly)
    markReadOnly(nparray);
92
  return reinterpret_cast<PyObject *>(nparray);
93
}
94

95
96
97
//-----------------------------------------------------------------------
// Explicit instantiations
//-----------------------------------------------------------------------
Samuel Jones's avatar
Samuel Jones committed
98
99
100
#define INSTANTIATE_WRAPNUMPY(ElementType)                                                                             \
  template DLLExport PyObject *wrapWithNDArray<ElementType>(const ElementType *, const int ndims, Py_intptr_t *dims,   \
                                                            const NumpyWrapMode mode, const OwnershipMode oMode);
101

102
103
104
105
106
107
108
109
110
111
///@cond Doxygen doesn't seem to like this...
INSTANTIATE_WRAPNUMPY(int)
INSTANTIATE_WRAPNUMPY(long)
INSTANTIATE_WRAPNUMPY(long long)
INSTANTIATE_WRAPNUMPY(unsigned int)
INSTANTIATE_WRAPNUMPY(unsigned long)
INSTANTIATE_WRAPNUMPY(unsigned long long)
INSTANTIATE_WRAPNUMPY(double)
INSTANTIATE_WRAPNUMPY(float)
///@endcond
112
113
} // namespace Impl
} // namespace Mantid::PythonInterface::Converters