diff --git a/CMakeLists.txt b/CMakeLists.txt index aff6ab2830bbb0be1552d87cf97b94f6c792f8e0..e4a2f613074f4efef79f1037b7f981302353289d 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 1edfc746828d87a67a18c8c705c2c5a34d2c463e..cf3c81d219126d2661e1d7b1cdba36107fc7cc11 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 47f02845dcf51463802bb887638c35519b99eec6..1efbdca9bd56c5f801cdbfeb8f5adec2b76a6005 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 a44493370a3960b88f5dd180a08b6374de714f85..da667b39972f6398a4d9cad475cac75eb6d23704 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 38836cae212845de6e35522d230b32a3a1f9c853..c35f1392a6e538140abf93d785eef55fd0d65888 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 7f564e759eae149d61fe4dbbe0f6855e09f316c7..9fca5e851bb094b87bdcf7a1660c02f97e04bca8 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 46ad08d5999da4c8d66617b76b21862d611a12b1..d30cc19dba658416f19e463a0fc9416965cd6aae 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 20b8e644628d225d2388979aa2e8d8a58e24f125..2743ac5be8d37ec9e6fb1cf3c5e22f8f22b3a628 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 0000000000000000000000000000000000000000..fbc81f41c202c447679f919fa5d9952accde5d43 --- /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; +}