Skip to content

Commit d95d985

Browse files
committed
Merge branch 'use-numpy-api-1.7'.
* update to NumPy C-API 1.7 * add helper to convert NumPy array or iterable to ObjCryst CrystVector.
2 parents d85508d + 8405a67 commit d95d985

File tree

6 files changed

+99
-26
lines changed

6 files changed

+99
-26
lines changed

src/extensions/helpers.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@
1717
*****************************************************************************/
1818

1919
#include "helpers.hpp"
20+
#include <boost/python/stl_iterator.hpp>
21+
2022
#include <iostream>
23+
#include <list>
2124

22-
namespace bp = boost::python;
25+
#include <ObjCryst/CrystVector/CrystVector.h>
26+
27+
// Use numpy here, but initialize it later in the extension module.
28+
#include "pyobjcryst_numpy_setup.hpp"
29+
#define NO_IMPORT_ARRAY
30+
#include <numpy/arrayobject.h>
2331

24-
using namespace std;
32+
33+
namespace bp = boost::python;
2534

2635
void swapstdout(std::ostream& buf)
2736
{
@@ -30,3 +39,34 @@ void swapstdout(std::ostream& buf)
3039
std::cout.rdbuf(buf.rdbuf());
3140
buf.rdbuf(cout_strbuf);
3241
}
42+
43+
44+
void assignCrystVector(CrystVector<double>& cv, bp::object obj)
45+
{
46+
// copy data directly if it is a numpy array of doubles
47+
PyArrayObject* a = PyArray_Check(obj.ptr()) ?
48+
reinterpret_cast<PyArrayObject*>(obj.ptr()) : NULL;
49+
bool isdoublenumpyarray = a &&
50+
(1 == PyArray_NDIM(a)) &&
51+
(NPY_DOUBLE == PyArray_TYPE(a));
52+
if (isdoublenumpyarray)
53+
{
54+
const double* src = static_cast<double*>(PyArray_DATA(a));
55+
npy_intp stride = PyArray_STRIDE(a, 0) / PyArray_ITEMSIZE(a);
56+
cv.resize(PyArray_SIZE(a));
57+
double* dst = cv.data();
58+
const double* last = dst + cv.size();
59+
for (; dst != last; ++dst, src += stride) *dst = *src;
60+
}
61+
// otherwise copy elementwise converting each element to a double
62+
else
63+
{
64+
bp::stl_input_iterator<double> begin(obj), end;
65+
// use intermediate list to preserve cv when conversion fails.
66+
std::list<double> values(begin, end);
67+
cv.resize(values.size());
68+
std::list<double>::const_iterator vv = values.begin();
69+
double* dst = cv.data();
70+
for (; vv != values.end(); ++vv, ++dst) *dst = *vv;
71+
}
72+
}

src/extensions/helpers.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,9 @@ bp::list setToPyList(std::set<T>& v)
109109
}
110110

111111

112+
// Extract CrystVector from a Python object
113+
template <class T> class CrystVector;
114+
115+
void assignCrystVector(CrystVector<double>& cv, bp::object obj);
116+
112117
#endif

src/extensions/powderpatternbackground_ext.cpp

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,30 @@
2121
#include <boost/python/args.hpp>
2222
#include <boost/python/copy_const_reference.hpp>
2323

24-
#include <numpy/noprefix.h>
25-
#include <numpy/arrayobject.h>
26-
2724
#include <ObjCryst/ObjCryst/PowderPattern.h>
2825

26+
#include "helpers.hpp"
27+
2928
namespace bp = boost::python;
30-
using namespace boost::python;
3129
using namespace ObjCryst;
3230

3331
namespace {
3432

3533
void _SetInterpPoints(PowderPatternBackground& b,
36-
PyObject* tth, PyObject* backgd)
34+
bp::object tth, bp::object backgd)
3735
{
38-
// cout << "_SetInterpPoints:" << tth << ", " << backgd << endl;
39-
// cout << "dimensions = " << PyArray_NDIM(tth) << endl;
40-
const unsigned long nb = *(PyArray_DIMS((PyObject*)tth));
41-
// cout << "nbPoints = " << nb << endl;
42-
CrystVector_REAL tth2(nb), backgd2(nb);
43-
// FIXME -- reuse some conversion function here
44-
//:TODO: We assume the arrays are contiguous & double (float64) !
45-
double* p = (double*) (PyArray_DATA(tth));
46-
double* p2 = (double*) (tth2.data());
47-
for (unsigned long i = 0; i < nb; i++) *p2++ = *p++;
48-
p = (double*) (PyArray_DATA(backgd));
49-
p2 = (double*) (backgd2.data());
50-
for (unsigned long i = 0; i < nb; i++) *p2++ = *p++;
51-
b.SetInterpPoints(tth2, backgd2);
36+
CrystVector_REAL cvtth, cvbackg;
37+
assignCrystVector(cvtth, tth);
38+
assignCrystVector(cvbackg, backgd);
39+
b.SetInterpPoints(cvtth, cvbackg);
5240
}
5341

5442
} // namespace
5543

44+
5645
void wrap_powderpatternbackground()
5746
{
47+
using namespace boost::python;
5848
class_<PowderPatternBackground, bases<PowderPatternComponent>, boost::noncopyable>(
5949
"PowderPatternBackground", no_init)
6050
//.def("SetParentPowderPattern", &PowderPatternBackground::SetParentPowderPattern)

src/extensions/pyobjcryst.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818

1919
#include <boost/python/module.hpp>
2020

21+
// Initialize numpy here.
22+
#include "pyobjcryst_numpy_setup.hpp"
23+
#include <numpy/arrayobject.h>
24+
25+
2126
void wrap_asymmetricunit();
2227
void wrap_atom();
2328
void wrap_crystal();
@@ -63,6 +68,9 @@ void wrap_zscatterer();
6368
// Wrappers must be called according to inheritance hierarchy
6469
BOOST_PYTHON_MODULE(_pyobjcryst)
6570
{
71+
// initialize numpy module
72+
import_array();
73+
6674
// General stuff
6775
wrap_general();
6876
wrap_io();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*****************************************************************************
2+
*
3+
* pyobjcryst Complex Modeling Initiative
4+
* (c) 2017 Brookhaven Science Associates
5+
* Brookhaven National Laboratory.
6+
* All rights reserved.
7+
*
8+
* File coded by: Pavol Juhas
9+
*
10+
* See AUTHORS.txt for a list of people who contributed.
11+
* See LICENSE.txt for license information.
12+
*
13+
******************************************************************************
14+
*
15+
* Define PY_ARRAY_UNIQUE_SYMBOL for the pyobjcryst extension module.
16+
*
17+
*****************************************************************************/
18+
19+
#ifndef PYOBJCRYST_NUMPY_SETUP_HPP_INCLUDED
20+
#define PYOBJCRYST_NUMPY_SETUP_HPP_INCLUDED
21+
22+
// Specify the version of NumPy API that will be used.
23+
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
24+
25+
// This macro is required for extension modules that are in several files.
26+
// It must be defined before inclusion of numpy/arrayobject.h
27+
#define PY_ARRAY_UNIQUE_SYMBOL PYOBJCRYST_NUMPY_ARRAY_SYMBOL
28+
29+
#endif // PYOBJCRYST_NUMPY_SETUP_HPP_INCLUDED

src/extensions/registerconverters.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@
2323

2424
#include <vector>
2525

26-
#include <numpy/noprefix.h>
27-
#include <numpy/arrayobject.h>
28-
2926
#include <ObjCryst/CrystVector/CrystVector.h>
3027
#include <ObjCryst/ObjCryst/General.h>
3128
#include <ObjCryst/ObjCryst/Crystal.h>
3229
#include <ObjCryst/ObjCryst/ScatteringPower.h>
3330
#include <ObjCryst/ObjCryst/Molecule.h>
3431

32+
// Use numpy here, but initialize it later in the extension module.
33+
#include "pyobjcryst_numpy_setup.hpp"
34+
#define NO_IMPORT_ARRAY
35+
#include <numpy/arrayobject.h>
36+
3537

3638
using namespace boost::python;
3739
using namespace ObjCryst;
@@ -56,7 +58,7 @@ typedef std::vector<MolAtom*> MolAtomVec;
5658
PyObject* makeNdArray(double * data, std::vector<npy_intp>& dims)
5759
{
5860
PyObject* pyarray = PyArray_SimpleNewFromData
59-
(dims.size(), &dims[0], PyArray_DOUBLE, (void *) data);
61+
(dims.size(), &dims[0], NPY_DOUBLE, (void *) data);
6062
PyObject* pyarraycopy = PyArray_Copy( (PyArrayObject*) pyarray );
6163
return bp::incref(pyarraycopy);
6264
}
@@ -284,7 +286,6 @@ void wrap_registerconverters()
284286
object(handle<>(pyobjcryst_ObjCrystException));
285287

286288
/* Data type converters */
287-
import_array();
288289
to_python_converter< CrystVector<double>, CrystVector_REAL_to_ndarray >();
289290
to_python_converter< CrystMatrix<double>, CrystMatrix_REAL_to_ndarray >();
290291
// From boost sources

0 commit comments

Comments
 (0)