diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 7e9d5ccc4..335cce7eb 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,271 +1,271 @@ #=============================================================================== # @file CMakeLists.txt # # @author Nicolas Richart # # @date creation: Fri Dec 12 2014 # @date last modification: Mon Jan 18 2016 # # @brief CMake file for the python wrapping of akantu # # @section LICENSE # # Copyright (©) 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory # (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see . # #=============================================================================== #=============================================================================== # Configuration #=============================================================================== package_get_all_definitions(AKANTU_DEFS) list(REMOVE_ITEM AKANTU_DEFS AKANTU_CORE_CXX11) #message(${AKANTU_DEFS}) set(AKA_DEFS "") foreach (def ${AKANTU_DEFS}) list(APPEND AKA_DEFS "-D${def}") endforeach() set(AKANTU_SWIG_FLAGS -w309,325,401,317,509,503,383,384 ${AKA_DEFS}) set(AKANTU_SWIG_OUTDIR ${CMAKE_CURRENT_SOURCE_DIR}) set(AKANTU_SWIG_MODULES swig/akantu.i) #=============================================================================== # Swig wrapper #=============================================================================== set(SWIG_REQURIED_VERISON 3.0) find_package(SWIG ${SWIG_REQURIED_VERISON}) find_package(PythonInterp ${AKANTU_PREFERRED_PYTHON_VERSION} REQUIRED) mark_as_advanced(SWIG_EXECUTABLE) if(NOT PYTHON_VERSION_MAJOR LESS 3) list(APPEND AKANTU_SWIG_FLAGS -py3) endif() package_get_all_include_directories( AKANTU_LIBRARY_INCLUDE_DIRS ) set(_swig_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/swig ${AKANTU_LIBRARY_INCLUDE_DIRS} ${PROJECT_BINARY_DIR}/src ${AKANTU_EXTERNAL_INCLUDE_DIR} ) include(CMakeParseArguments) function(swig_generate_dependencies _module _depedencies _depedencies_out) set(_dependencies_script "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/_swig_generate_dependencies.cmake") file(WRITE ${_dependencies_script} " set(_include_directories ${_include_directories}) list(APPEND _include_directories \"./\") set(_dep) set(_files_to_process \${_module}) while(_files_to_process) list(GET _files_to_process 0 _file) list(REMOVE_AT _files_to_process 0) file(STRINGS \${_file} _file_content REGEX \"^%include *\\\"(.*)\\\"\") set(_includes) foreach(_line \${_file_content}) string(REGEX REPLACE \"^%include *\\\"(.*)\\\"\" \"\\\\1\" _inc \${_line}) if(_inc) list(APPEND _includes \${_inc}) endif() endforeach() foreach(_include \${_includes}) unset(_found) foreach(_inc_dir \${_include_directories}) if(EXISTS \${_inc_dir}/\${_include}) set(_found \${_inc_dir}/\${_include}) break() endif() endforeach() if(_found) list(APPEND _files_to_process \${_found}) list(APPEND _dep \${_found}) endif() endforeach() endwhile() get_filename_component(_module_we \"\${_module}\" NAME_WE) set(_dependencies_file \${CMAKE_CURRENT_BINARY_DIR}\${CMAKE_FILES_DIRECTORY}/_swig_\${_module_we}_depends.cmake) file(WRITE \"\${_dependencies_file}\" \"set(_swig_\${_module_we}_depends\") foreach(_d \${_dep}) file(APPEND \"\${_dependencies_file}\" \" \${_d}\") endforeach() file(APPEND \"\${_dependencies_file}\" \" )\") ") get_filename_component(_module_absolute "${_module}" ABSOLUTE) get_filename_component(_module_we "${_module}" NAME_WE) set(_dependencies_file ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/_swig_${_module_we}_depends.cmake) if(EXISTS ${_dependencies_file}) include(${_dependencies_file}) else() execute_process(COMMAND ${CMAKE_COMMAND} -D_module=${_module_absolute} -P ${_dependencies_script} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) include(${_dependencies_file}) endif() set(${_depedencies_out} ${_swig_${_module_we}_depends} PARENT_SCOPE) add_custom_command(OUTPUT ${_dependencies_file} COMMAND ${CMAKE_COMMAND} -D_module=${_module_absolute} -P ${_dependencies_script} COMMENT "Scanning dependencies for swig module ${_module_we}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} MAIN_DEPENDENCY ${_module_absolute} DEPENDS ${_swig_${_module_we}_depends} ) set(${_depedencies} ${_dependencies_file} PARENT_SCOPE) endfunction() function(swig_generate_wrappers project _wrappers_cpp _wrappers_py) cmake_parse_arguments(_swig_opt "" "OUTPUT_DIR;DEPENDENCIES" "EXTRA_FLAGS;INCLUDE_DIRECTORIES" ${ARGN}) if(_swig_opt_OUTPUT_DIR) set(_output_dir ${_swig_opt_OUTPUT_DIR}) else() set(_output_dir ${CMAKE_CURRENT_BINARY_DIR}) endif() set(_swig_wrappers) get_directory_property(_include_directories INCLUDE_DIRECTORIES) list(APPEND _include_directories ${_swig_opt_INCLUDE_DIRECTORIES}) if(_include_directories) string(REPLACE ";" ";-I" _swig_include_directories "${_include_directories}") endif() foreach(_module ${_swig_opt_UNPARSED_ARGUMENTS}) swig_generate_dependencies(${_module} _module_dependencies _depends_out) if(_swig_opt_DEPENDENCIES) set(${_swig_opt_DEPENDENCIES} ${_depends_out} PARENT_SCOPE) endif() get_filename_component(_module_absolute "${_module}" ABSOLUTE) get_filename_component(_module_path "${_module_absolute}" PATH) get_filename_component(_module_name "${_module}" NAME) get_filename_component(_module_we "${_module}" NAME_WE) set(_wrapper "${_output_dir}/${_module_we}_wrapper.cpp") set(_extra_wrapper "${_output_dir}/${_module_we}.py") set(_extra_wrapper_bin "${CMAKE_CURRENT_BINARY_DIR}/${_module_we}.py") if(SWIG_FOUND) set_source_files_properties("${_wrapper}" PROPERTIES GENERATED 1) set_source_files_properties("${_extra_wrapper}" PROPERTIES GENERATED 1) set(_dependencies_file ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/_swig_${_module_we}_depends.cmake) set(_ouput "${_wrapper}" "${_extra_wrapper}") add_custom_command( OUTPUT ${_ouput} COMMAND "${SWIG_EXECUTABLE}" ARGS -python -c++ ${_swig_opt_EXTRA_FLAGS} -outdir ${_output_dir} -I${_swig_include_directories} -I${_module_path} -o "${_wrapper}" "${_module_absolute}" COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_extra_wrapper} ${_extra_wrapper_bin} # MAIN_DEPENDENCY "${_module_absolute}" DEPENDS ${_module_dependencies} COMMENT "Generating swig wrapper ${_module} -> ${_wrapper}" ) list(APPEND _swig_wrappers ${_wrapper}) list(APPEND _swig_wrappers_py "${_extra_wrapper_bin}") else() if(NOT EXISTS ${_wrapper} OR NOT EXISTS "${_extra_wrapper}") message(FATAL_ERROR "The file ${_wrapper} and/or ${_extra_wrapper} does " "not exists and they cannot be generated. Install swig ${SWIG_REQURIED_VERISON} " " in order to generate them. Or get them from a different machine, " "in order to be able to compile the python interface") else() list(APPEND _swig_wrappers "${_wrapper}") list(APPEND _swig_wrappers_py "${_extra_wrapper_bin}") endif() endif() endforeach() add_custom_target(${project}_generate_swig_wrappers DEPENDS ${_swig_wrappers}) set(${_wrappers_cpp} ${_swig_wrappers} PARENT_SCOPE) set(${_wrappers_py} ${_swig_wrappers_py} PARENT_SCOPE) endfunction() swig_generate_wrappers(akantu AKANTU_SWIG_WRAPPERS_CPP AKANTU_WRAPPERS_PYTHON ${AKANTU_SWIG_MODULES} EXTRA_FLAGS ${AKANTU_SWIG_FLAGS} DEPENDENCIES _deps INCLUDE_DIRECTORIES ${_swig_include_dirs}) if(AKANTU_SWIG_WRAPPERS_CPP) set(_ext_files "${AKANTU_SWIG_WRAPPERS_CPP}") set(_inc_dirs "${_swig_include_dirs}") set(_lib_dirs "${Akantu_BINARY_DIR}/src") - string(TOUPPER CMAKE_BUILD_TYPE _config) + string(TOUPPER "${CMAKE_BUILD_TYPE}" _config) set(_akantu_lib_name "akantu${CMAKE_${_config}_POSTFIX}") get_property(_compile_flags TARGET akantu PROPERTY COMPILE_FLAGS) set(_compile_flags "${_compile_flags} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_config}}") separate_arguments(_compile_flags) get_property(_cxx_standard TARGET akantu PROPERTY CXX_STANDARD) set(_flags ${_compile_flags} -std=c++${_cxx_standard} -Wno-maybe-uninitialized -Wno-unused-parameter) set(_quiet) if(NOT CMAKE_VERBOSE_MAKEFILE) set(_quiet --quiet) endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py @ONLY) add_custom_target(_akantu ALL COMMAND ${PYTHON_EXECUTABLE} ./setup.py ${_quiet} --no-user-cfg build_ext --inplace WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${AKANTU_SWIG_WRAPPERS_CPP} akantu COMMENT "Building akantu's python interface" ) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/_akantu${CMAKE_SHARED_MODULE_SUFFIX}) install(CODE "execute_process( COMMAND ${PYTHON_EXECUTABLE} ./setup.py ${_quiet} install --prefix=${AKANTU_PYTHON_INSTALL_PREFIX} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})" ) package_declare_extra_files_to_package(python_interface ${_deps} ${PROJECT_SOURCE_DIR}/python/${AKANTU_SWIG_MODULES}) endif() diff --git a/src/python/python_functor_inline_impl.cc b/src/python/python_functor_inline_impl.cc index 0e42b821b..88ed09184 100644 --- a/src/python/python_functor_inline_impl.cc +++ b/src/python/python_functor_inline_impl.cc @@ -1,350 +1,351 @@ /** * @file python_functor_inline_impl.cc * * @author Guillaume Anciaux * * @date creation: Fri Nov 13 2015 * @date last modification: Wed Nov 18 2015 * * @brief Python functor interface * * @section LICENSE * * Copyright (©) 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory * (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "integration_point.hh" /* -------------------------------------------------------------------------- */ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #include #if PY_MAJOR_VERSION >= 3 #include #endif /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_PYTHON_FUNCTOR_INLINE_IMPL_CC__ #define __AKANTU_PYTHON_FUNCTOR_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ template inline int PythonFunctor::getPythonDataTypeCode() const { AKANTU_EXCEPTION("undefined type: " << debug::demangle(typeid(T).name())); } /* -------------------------------------------------------------------------- */ template <> inline int PythonFunctor::getPythonDataTypeCode() const { int data_typecode = NPY_NOTYPE; size_t s = sizeof(bool); switch (s) { case 1: data_typecode = NPY_BOOL; break; case 2: data_typecode = NPY_UINT16; break; case 4: data_typecode = NPY_UINT32; break; case 8: data_typecode = NPY_UINT64; break; } return data_typecode; } /* -------------------------------------------------------------------------- */ template <> inline int PythonFunctor::getPythonDataTypeCode() const { return NPY_DOUBLE; } /* -------------------------------------------------------------------------- */ template PyObject * PythonFunctor::convertToPython(const T &) const { AKANTU_DEBUG_ERROR(__func__ << " : not implemented yet !" << std::endl << debug::demangle(typeid(T).name())); } /* -------------------------------------------------------------------------- */ template <> inline PyObject * PythonFunctor::convertToPython(const double & akantu_object) const { return PyFloat_FromDouble(akantu_object); } /* -------------------------------------------------------------------------- */ template <> inline PyObject * PythonFunctor::convertToPython(const UInt & akantu_object) const { #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(akantu_object); #else return PyInt_FromLong(akantu_object); #endif } /* -------------------------------------------------------------------------- */ template <> inline PyObject * PythonFunctor::convertToPython(const bool & akantu_object) const { return PyBool_FromLong(long(akantu_object)); } /* -------------------------------------------------------------------------- */ template inline PyObject * PythonFunctor::convertToPython(const std::vector & array) const { int data_typecode = getPythonDataTypeCode(); npy_intp dims[1] = {int(array.size())}; PyObject * obj = PyArray_SimpleNewFromData(1, dims, data_typecode, const_cast(&array[0])); auto * res = (PyArrayObject *)obj; return (PyObject *)res; } /* -------------------------------------------------------------------------- */ template inline PyObject * PythonFunctor::convertToPython(const std::vector *> & array) const { PyObject * res = PyDict_New(); for (auto a : array) { PyObject * obj = this->convertToPython(*a); PyObject * name = this->convertToPython(a->getID()); PyDict_SetItem(res, name, obj); } return (PyObject *)res; } /* -------------------------------------------------------------------------- */ template inline PyObject * PythonFunctor::convertToPython(const std::map & map) const { PyObject * res = PyDict_New(); for (auto a : map) { PyObject * key = this->convertToPython(a.first); PyObject * value = this->convertToPython(a.second); PyDict_SetItem(res, key, value); } return (PyObject *)res; } /* -------------------------------------------------------------------------- */ template PyObject * PythonFunctor::convertToPython(const Vector & array) const { int data_typecode = getPythonDataTypeCode(); npy_intp dims[1] = {array.size()}; PyObject * obj = PyArray_SimpleNewFromData(1, dims, data_typecode, array.storage()); auto * res = (PyArrayObject *)obj; return (PyObject *)res; } /* -------------------------------------------------------------------------- */ template PyObject * PythonFunctor::convertToPython(const Array & array) const { int data_typecode = getPythonDataTypeCode(); npy_intp dims[2] = {array.size(), array.getNbComponent()}; PyObject * obj = PyArray_SimpleNewFromData(2, dims, data_typecode, array.storage()); auto * res = (PyArrayObject *)obj; return (PyObject *)res; } /* -------------------------------------------------------------------------- */ template PyObject * PythonFunctor::convertToPython(Array * array) const { return this->convertToPython(*array); } /* -------------------------------------------------------------------------- */ template PyObject * PythonFunctor::convertToPython(const Matrix & mat) const { int data_typecode = getPythonDataTypeCode(); npy_intp dims[2] = {mat.size(0), mat.size(1)}; PyObject * obj = PyArray_SimpleNewFromData(2, dims, data_typecode, mat.storage()); auto * res = (PyArrayObject *)obj; return (PyObject *)res; } /* -------------------------------------------------------------------------- */ template <> inline PyObject * PythonFunctor::convertToPython(const std::string & str) const { #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(str.c_str()); #else return PyString_FromString(str.c_str()); #endif } /* -------------------------------------------------------------------------- */ template <> inline PyObject * PythonFunctor::convertToPython( const IntegrationPoint & qp) const { PyObject * input = PyDict_New(); PyObject * num_point = this->convertToPython(qp.num_point); PyObject * global_num = this->convertToPython(qp.global_num); PyObject * material_id = this->convertToPython(qp.material_id); PyObject * position = this->convertToPython(qp.getPosition()); PyDict_SetItemString(input, "num_point", num_point); PyDict_SetItemString(input, "global_num", global_num); PyDict_SetItemString(input, "material_id", material_id); PyDict_SetItemString(input, "position", position); return input; } /* -------------------------------------------------------------------------- */ inline PyObject * PythonFunctor::getPythonFunction(const std::string & functor_name) const { -#if PY_MAJOR_VERSION >= 3 - if (!PyInstanceMethod_Check(this->python_obj)) -#else +#if PY_MAJOR_VERSION < 3 if (!PyInstance_Check(this->python_obj)) -#endif AKANTU_EXCEPTION("Python object is not an instance"); +#else +// does not make sense to check everything is an instance of object in python 3 +#endif + + if (not PyObject_HasAttrString(this->python_obj, functor_name.c_str())) + AKANTU_EXCEPTION("Python dictionary has no " << functor_name << " entry"); PyObject * pFunctor = PyObject_GetAttrString(this->python_obj, functor_name.c_str()); - if (!pFunctor) - AKANTU_EXCEPTION("Python dictionary has no " << functor_name << " entry"); return pFunctor; } /* -------------------------------------------------------------------------- */ inline void PythonFunctor::packArguments(__attribute__((unused)) std::vector & p_args) const {} /* -------------------------------------------------------------------------- */ template inline void PythonFunctor::packArguments(std::vector & p_args, T & p, Args &... params) const { p_args.push_back(this->convertToPython(p)); if (sizeof...(params) != 0) this->packArguments(p_args, params...); } /* -------------------------------------------------------------------------- */ template return_type PythonFunctor::callFunctor(const std::string & functor_name, Params &... parameters) const { _import_array(); std::vector arg_vector; this->packArguments(arg_vector, parameters...); PyObject * pArgs = PyTuple_New(arg_vector.size()); for (UInt i = 0; i < arg_vector.size(); ++i) { PyTuple_SetItem(pArgs, i, arg_vector[i]); } PyObject * kwargs = PyDict_New(); PyObject * pFunctor = getPythonFunction(functor_name); PyObject * res = this->callFunctor(pFunctor, pArgs, kwargs); Py_XDECREF(pArgs); Py_XDECREF(kwargs); return this->convertToAkantu(res); } /* -------------------------------------------------------------------------- */ template inline return_type PythonFunctor::convertToAkantu(PyObject * python_obj) const { if (PyList_Check(python_obj)) { return this->convertListToAkantu( python_obj); } AKANTU_DEBUG_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ template <> inline void PythonFunctor::convertToAkantu(PyObject * python_obj) const { if (python_obj != Py_None) AKANTU_DEBUG_WARNING( "functor return a value while none was expected: ignored"); } /* -------------------------------------------------------------------------- */ template <> inline std::string PythonFunctor::convertToAkantu(PyObject * python_obj) const { #if PY_MAJOR_VERSION >= 3 if (!PyUnicode_Check(python_obj)) AKANTU_EXCEPTION("cannot convert object to string"); std::wstring unicode_str(PyUnicode_AsWideCharString(python_obj, NULL)); std::wstring_convert, wchar_t> converter; return converter.to_bytes(unicode_str); #else if (!PyString_Check(python_obj)) AKANTU_EXCEPTION("cannot convert object to string"); return PyString_AsString(python_obj); #endif } /* -------------------------------------------------------------------------- */ template <> inline Real PythonFunctor::convertToAkantu(PyObject * python_obj) const { if (!PyFloat_Check(python_obj)) AKANTU_EXCEPTION("cannot convert object to float"); return PyFloat_AsDouble(python_obj); } /* -------------------------------------------------------------------------- */ template <> inline UInt PythonFunctor::convertToAkantu(PyObject * python_obj) const { #if PY_MAJOR_VERSION >= 3 if (!PyLong_Check(python_obj)) AKANTU_EXCEPTION("cannot convert object to integer"); return PyLong_AsLong(python_obj); #else if (!PyInt_Check(python_obj)) AKANTU_EXCEPTION("cannot convert object to integer"); return PyInt_AsLong(python_obj); #endif } /* -------------------------------------------------------------------------- */ template inline std::vector PythonFunctor::convertListToAkantu(PyObject * python_obj) const { std::vector res; UInt size = PyList_Size(python_obj); for (UInt i = 0; i < size; ++i) { PyObject * item = PyList_GET_ITEM(python_obj, i); res.push_back(this->convertToAkantu(item)); } return res; } /* -------------------------------------------------------------------------- */ } // akantu #endif /* __AKANTU_PYTHON_FUNCTOR_INLINE_IMPL_CC__ */