109 lines
3.2 KiB
C
109 lines
3.2 KiB
C
// ascee_python.h
|
|
//
|
|
// Author: J.A. de Jong - ASCEE
|
|
//
|
|
// Description:
|
|
// Routine to generate a Numpy array from an arbitrary buffer. Careful, this
|
|
// code should both be C and C++ compatible!!
|
|
//////////////////////////////////////////////////////////////////////
|
|
#pragma once
|
|
#ifndef LASP_PYARRAY_H
|
|
#define LASP_PYARRAY_H
|
|
#include "lasp_types.h"
|
|
#include <assert.h>
|
|
#include <numpy/ndarrayobject.h>
|
|
|
|
#if LASP_DOUBLE_PRECISION == 1
|
|
#define LASP_NUMPY_FLOAT_TYPE NPY_FLOAT64
|
|
#define LASP_NUMPY_COMPLEX_TYPE NPY_COMPLEX128
|
|
#else
|
|
#define LASP_NUMPY_FLOAT_TYPE NPY_FLOAT32
|
|
#endif
|
|
#include <malloc.h>
|
|
|
|
/**
|
|
* Function passed to Python to use for cleanup of
|
|
* foreignly obtained data.
|
|
**/
|
|
#define LASP_CAPSULE_NAME "pyarray_data_destructor"
|
|
static inline void capsule_cleanup(PyObject *capsule) {
|
|
void *memory = PyCapsule_GetPointer(capsule, LASP_CAPSULE_NAME);
|
|
free(memory);
|
|
}
|
|
|
|
/**
|
|
* Create a numpy array from a raw data pointer.
|
|
*
|
|
* @param data pointer to data, assumes d* for data
|
|
* @param transfer_ownership If set to true, the created Numpy array will be
|
|
* responsible for freeing the data.
|
|
* @param F_contiguous It set to true, the data is assumed to be column-major
|
|
* ordered in memory (which means we can find element d[r,c] by d[n_cols*r+c],
|
|
* if set to false. Data is assumed to be row-major ordered (which means we
|
|
* find element d[r,c] by d[n_rows*c+r]
|
|
*
|
|
* @return Numpy array
|
|
*/
|
|
static inline PyObject *data_to_ndarray(void *data, int n_rows, int n_cols,
|
|
int typenum, bool transfer_ownership,
|
|
bool F_contiguous) {
|
|
|
|
/* fprintf(stderr, "Enter data_to_ndarray\n"); */
|
|
assert(data);
|
|
|
|
PyArray_Descr *descr = PyArray_DescrFromType(typenum);
|
|
if (!descr)
|
|
return NULL;
|
|
|
|
npy_intp dims[2] = {n_rows, n_cols};
|
|
npy_intp strides[2];
|
|
|
|
int flags = 0;
|
|
if (F_contiguous) {
|
|
flags |= NPY_ARRAY_FARRAY;
|
|
strides[0] = descr->elsize;
|
|
strides[1] = descr->elsize * n_rows;
|
|
} else {
|
|
strides[0] = descr->elsize * n_rows;
|
|
strides[1] = descr->elsize;
|
|
}
|
|
|
|
PyArrayObject *arr =
|
|
(PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
|
|
descr, // Description
|
|
2, // nd
|
|
dims, // dimensions
|
|
strides, // strides
|
|
data, // Data pointer
|
|
flags, // Flags
|
|
NULL // obj
|
|
);
|
|
|
|
if (!arr) {
|
|
fprintf(stderr, "arr = 0!");
|
|
return NULL;
|
|
}
|
|
|
|
if (transfer_ownership) {
|
|
// The default destructor of Python cannot free the data, as it is allocated
|
|
// with malloc. Therefore, with this code, we tell Numpy/Python to use
|
|
// the capsule_cleanup constructor. See:
|
|
// https://stackoverflow.com/questions/54269956/crash-of-jupyter-due-to-the-use-of-pyarray-enableflags/54278170#54278170
|
|
// Note that in general it was disadvised to build all C code with MinGW on
|
|
// Windows. We do it anyway, see if we find any problems on the way.
|
|
PyObject *capsule = PyCapsule_New(data, LASP_CAPSULE_NAME, capsule_cleanup);
|
|
int res = PyArray_SetBaseObject(arr, capsule);
|
|
if (res != 0) {
|
|
fprintf(stderr, "Failed to set base object of array!");
|
|
return NULL;
|
|
}
|
|
|
|
PyArray_ENABLEFLAGS(arr, NPY_OWNDATA);
|
|
}
|
|
|
|
return (PyObject *)arr;
|
|
}
|
|
|
|
#undef LASP_CAPSULE_NAME
|
|
#endif // LASP_PYARRAY_H
|