From 762d31ddf4d5342b871dc607f8a2a7ae5802a7d7 Mon Sep 17 00:00:00 2001 From: Jonathan Lambrechts <jonathan.lambrechts@uclouvain.be> Date: Thu, 31 Oct 2013 15:58:06 +0000 Subject: [PATCH] python : transparent conversion from numpy vector to fullMatrix(in place when possible) --- CMakeLists.txt | 40 ++++++---- Common/GmshConfig.h.in | 1 + wrappers/gmshpy/gmshCommon.i | 1 + wrappers/gmshpy/gmshGeo.i | 1 + wrappers/gmshpy/gmshMesh.i | 1 + wrappers/gmshpy/gmshNumeric.i | 1 + wrappers/gmshpy/gmshPost.i | 1 + wrappers/gmshpy/gmshSolver.i | 1 + wrappers/gmshpy/gmshtypemaps.i | 129 +++++++++++++++++++++++++++++++++ 9 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 wrappers/gmshpy/gmshtypemaps.i diff --git a/CMakeLists.txt b/CMakeLists.txt index aff6ab2830..e4a2f61307 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ opt(MSVC_STATIC_RUNTIME "Enable static Visual C++ runtime" OFF) opt(MUMPS "Enable MUMPS sparse direct linear solver" OFF) opt(NATIVE_FILE_CHOOSER "Enable native file chooser in GUI" ${DEFAULT}) opt(NETGEN "Enable Netgen 3D frontal mesh generator" ${DEFAULT}) +opt(NUMPY "Enable conversion between fullMatrix and numpy array (requires SWIG)" OFF) opt(OCC "Enable Open CASCADE geometrical models" ${DEFAULT}) opt(ONELAB "Enable OneLab solver interface" ${DEFAULT}) opt(ONELAB_METAMODEL "Enable OneLab metamodels (experimental)" ${DEFAULT}) @@ -1052,6 +1053,29 @@ if(ENABLE_ACIS) endif(ACIS_LIB) endif(ENABLE_ACIS) +if(ENABLE_WRAP_PYTHON) + find_package(SWIG) + find_package(PythonLibs) + if(SWIG_FOUND AND PYTHONLIBS_FOUND) + message(STATUS "Found SWIG version " ${SWIG_VERSION}) + string(SUBSTRING ${SWIG_VERSION} 0 1 SWIG_MAJOR_VERSION) + if(SWIG_MAJOR_VERSION EQUAL 1) + message(WARNING "Python bindings require SWIG >= 2: disabling Python") + else(SWIG_MAJOR_VERSION EQUAL 1) + set_config_option(HAVE_PYTHON "Python") + endif(SWIG_MAJOR_VERSION EQUAL 1) + endif(SWIG_FOUND AND PYTHONLIBS_FOUND) +endif(ENABLE_WRAP_PYTHON) + +if(HAVE_PYTHON) + if(ENABLE_NUMPY) + find_path(NUMPY_INC "numpyconfig.h" HINTS ${PYTHON_INCLUDE_PATH} PATH_SUFFIXES /numpy) + if(NUMPY_INC) + set_config_option(HAVE_NUMPY "Numpy") + endif(NUMPY_INC) + endif(ENABLE_NUMPY) +endif(HAVE_PYTHON) + check_function_exists(vsnprintf HAVE_VSNPRINTF) if(NOT HAVE_VSNPRINTF AND NOT ENABLE_BUILD_IOS) set_config_option(HAVE_NO_VSNPRINTF "NoVsnprintf") @@ -1576,19 +1600,9 @@ else(APPLE AND ENABLE_OS_SPECIFIC_INSTALL) set(CPACK_GENERATOR TGZ) endif(APPLE AND ENABLE_OS_SPECIFIC_INSTALL) -if(ENABLE_WRAP_PYTHON) - find_package(SWIG) - find_package(PythonLibs) - if(SWIG_FOUND AND PYTHONLIBS_FOUND) - message(STATUS "Found SWIG version " ${SWIG_VERSION}) - string(SUBSTRING ${SWIG_VERSION} 0 1 SWIG_MAJOR_VERSION) - if(SWIG_MAJOR_VERSION EQUAL 1) - message(WARNING "Python bindings require SWIG >= 2: disabling Python") - else(SWIG_MAJOR_VERSION EQUAL 1) - add_subdirectory(wrappers/gmshpy) - endif(SWIG_MAJOR_VERSION EQUAL 1) - endif(SWIG_FOUND AND PYTHONLIBS_FOUND) -endif(ENABLE_WRAP_PYTHON) +if (HAVE_PYTHON) + add_subdirectory(wrappers/gmshpy) +endif(HAVE_PYTHON) if(ENABLE_WRAP_JAVA) if(NOT HAVE_BLAS OR NOT HAVE_LAPACK) diff --git a/Common/GmshConfig.h.in b/Common/GmshConfig.h.in index 1edfc74682..cf3c81d219 100644 --- a/Common/GmshConfig.h.in +++ b/Common/GmshConfig.h.in @@ -39,6 +39,7 @@ #cmakedefine HAVE_MUMPS #cmakedefine HAVE_NATIVE_FILE_CHOOSER #cmakedefine HAVE_NETGEN +#cmakedefine HAVE_NUMPY #cmakedefine HAVE_NO_INTPTR_T #cmakedefine HAVE_NO_SOCKLEN_T #cmakedefine HAVE_NO_STDINT_H diff --git a/wrappers/gmshpy/gmshCommon.i b/wrappers/gmshpy/gmshCommon.i index 47f02845dc..1efbdca9bd 100644 --- a/wrappers/gmshpy/gmshCommon.i +++ b/wrappers/gmshpy/gmshCommon.i @@ -5,6 +5,7 @@ %include std_vector.i %include std_map.i %include cpointer.i +%import "gmshtypemaps.i" %{ #include "GmshConfig.h" diff --git a/wrappers/gmshpy/gmshGeo.i b/wrappers/gmshpy/gmshGeo.i index a44493370a..da667b3997 100644 --- a/wrappers/gmshpy/gmshGeo.i +++ b/wrappers/gmshpy/gmshGeo.i @@ -4,6 +4,7 @@ %include std_string.i %include std_list.i %include std_vector.i +%import "gmshtypemaps.i" %{ #include "GmshConfig.h" diff --git a/wrappers/gmshpy/gmshMesh.i b/wrappers/gmshpy/gmshMesh.i index 38836cae21..c35f1392a6 100644 --- a/wrappers/gmshpy/gmshMesh.i +++ b/wrappers/gmshpy/gmshMesh.i @@ -2,6 +2,7 @@ %module gmshMesh %include std_string.i %include std_list.i +%import "gmshtypemaps.i" %{ #include "GmshConfig.h" diff --git a/wrappers/gmshpy/gmshNumeric.i b/wrappers/gmshpy/gmshNumeric.i index 7f564e759e..9fca5e851b 100644 --- a/wrappers/gmshpy/gmshNumeric.i +++ b/wrappers/gmshpy/gmshNumeric.i @@ -3,6 +3,7 @@ %include std_string.i %include std_vector.i +%import "gmshtypemaps.i" %{ #include "GmshConfig.h" diff --git a/wrappers/gmshpy/gmshPost.i b/wrappers/gmshpy/gmshPost.i index 46ad08d599..d30cc19dba 100644 --- a/wrappers/gmshpy/gmshPost.i +++ b/wrappers/gmshpy/gmshPost.i @@ -4,6 +4,7 @@ %include std_string.i %include std_vector.i %include std_map.i +%import "gmshtypemaps.i" %{ #include "GmshConfig.h" diff --git a/wrappers/gmshpy/gmshSolver.i b/wrappers/gmshpy/gmshSolver.i index 20b8e64462..2743ac5be8 100644 --- a/wrappers/gmshpy/gmshSolver.i +++ b/wrappers/gmshpy/gmshSolver.i @@ -3,6 +3,7 @@ %include std_string.i %include std_vector.i %include std_complex.i +%import "gmshtypemaps.i" %{ #include "GmshConfig.h" diff --git a/wrappers/gmshpy/gmshtypemaps.i b/wrappers/gmshpy/gmshtypemaps.i new file mode 100644 index 0000000000..fbc81f41c2 --- /dev/null +++ b/wrappers/gmshpy/gmshtypemaps.i @@ -0,0 +1,129 @@ +%fragment("fullMatrixConversionInit", "init") { + %#ifdef HAVE_NUMPY + import_array(); + %#endif +} + +%fragment("fullMatrixConversion", "header", fragment="fullMatrixConversionInit") { + %#include "fullMatrix.h" + %#include "GmshConfig.h" + %#ifdef HAVE_NUMPY + %#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + %#include <numpy/arrayobject.h> + %#if NPY_API_VERSION < 0x00000007 + %#define NPY_ARRAY_FARRAY NPY_FARRAY + %#endif + %#endif + fullMatrix<double> *pySequenceToFullMatrixDouble(PyObject *o) { + fullMatrix<double> *fm; + if (!PySequence_Check(o)) + return NULL; + long int nRow = PySequence_Length(o); + for (int i = 0; i < PySequence_Length(o); ++i) { + PyObject *l = PySequence_GetItem(o, i); + long int nCol = PySequence_Length(l); + if (i == 0) + fm = new fullMatrix<double>(nRow, nCol); + else if (nCol != fm->size2()) { + delete fm; + return NULL; + } + for (int j = 0; j < nCol; ++j) { + PyObject *v = PySequence_GetItem(l, j); + if (PyNumber_Check(v)) { + (*fm)(i, j) = (float) PyFloat_AsDouble(v); + } + else { + delete fm; + return NULL; + } + } + } + return fm; + } + + fullMatrix<double> *objToFullMatrixRO(PyObject *obj, bool &newMatrix, PyObject *&tmpObject) + { + fullMatrix<double> *fm = NULL; + SWIG_ConvertPtr(obj,(void **) &fm, SWIGTYPE_p_fullMatrixT_double_t, 1); + if (fm) + return fm; + %#ifdef HAVE_NUMPY + PyArrayObject *array = (PyArrayObject*)obj; + if (PyArray_Check(array) && PyArray_ISFARRAY_RO(array) && PyArray_NDIM(array) == 2) { + newMatrix = true; + return new fullMatrix<double>((double*)PyArray_DATA(array), PyArray_DIM(array, 0), PyArray_DIM(array, 1)); + } + if ((tmpObject = PyArray_FROMANY(obj, NPY_DOUBLE, 2, 2, NPY_ARRAY_FARRAY))) { + array = (PyArrayObject*)tmpObject; + newMatrix = true; + return new fullMatrix<double>((double*)PyArray_DATA(array), PyArray_DIM(array, 0), PyArray_DIM(array, 1)); + } + %#endif + if ((fm = pySequenceToFullMatrixDouble(obj))) + newMatrix = true; + return fm; + } + + fullMatrix<double> *objToFullMatrixRW(PyObject *obj, bool &newMatrix) + { + fullMatrix<double> *fm = NULL; + SWIG_ConvertPtr(obj,(void **) &fm, SWIGTYPE_p_fullMatrixT_double_t, 1); + if (fm) + return fm; + %#ifdef HAVE_NUMPY + PyArrayObject *array = (PyArrayObject*)obj; + if (PyArray_Check(array) && PyArray_ISFARRAY(array) && PyArray_NDIM(array) == 2) { + newMatrix = true; + return new fullMatrix<double>((double*)PyArray_DATA(array), PyArray_DIM(array, 0), PyArray_DIM(array, 1)); + } + %#endif + return NULL; + } +} + +%typemap(in, fragment="fullMatrixConversion") const fullMatrix<double> &(PyObject *tmpObject = NULL, bool newMatrix = false){ + $1 = objToFullMatrixRO($input, newMatrix, tmpObject); + if (!$1) { + PyErr_Format(PyExc_TypeError, "cannot convert argument %i to a fullMatrix<double>", $argnum); + SWIG_fail; + } +} +%typemap(freearg) const fullMatrix<double> &{ + if (tmpObject$argnum) Py_DECREF(tmpObject$argnum); + if (newMatrix$argnum && $1) delete $1; +} + +%typemap(in, fragment="fullMatrixConversion") fullMatrix<double> &(bool newMatrix = false){ + $1 = objToFullMatrixRW($input, newMatrix); + if (!$1) { + PyErr_Format(PyExc_TypeError, "cannot convert argument %i to a writable fullMatrix<double>", $argnum); + SWIG_fail; + } +} +%typemap(freearg) fullMatrix<double> &{ + if (newMatrix$argnum && $1) delete $1; +} + +%typemap(in, fragment="fullMatrixConversion") const fullMatrix<double> *(PyObject *tmpObject = NULL, bool newMatrix = false){ + $1 = objToFullMatrixRO($input, newMatrix, tmpObject); + if (!$1) { + PyErr_Format(PyExc_TypeError, "cannot convert argument %i to a fullMatrix<double>", $argnum); + SWIG_fail; + } +} +%typemap(freearg) const fullMatrix<double> *{ + if (tmpObject$argnum) Py_DECREF(tmpObject$argnum); + if (newMatrix$argnum && $1) delete $1; +} + +%typemap(in, fragment="fullMatrixConversion") fullMatrix<double> *(bool newMatrix = false){ + $1 = objToFullMatrixRW($input, newMatrix); + if (!$1) { + PyErr_Format(PyExc_TypeError, "cannot convert argument %i to a writable fullMatrix<double>", $argnum); + SWIG_fail; + } +} +%typemap(freearg) fullMatrix<double> *{ + if (newMatrix$argnum && $1) delete $1; +} -- GitLab