From 07e8a2a25ab5a62f63af77fe0dd1405cd41ee5ce Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sun, 12 Oct 2014 12:58:33 +0400 Subject: [PATCH] Add initial support for python bindings Add minimal SIP-based bindings required to implement trajectory analysis module in Python and run it --- src/pygromacs/CMakeLists.txt | 36 +++ src/pygromacs/__init__.py | 0 src/pygromacs/cmake/modules/FindGROMACS.cmake | 75 ++++++ .../cmake/modules/FindPythonLibrary.cmake | 74 ++++++ src/pygromacs/cmake/modules/FindSIP.cmake | 65 +++++ src/pygromacs/cmake/modules/FindSIP.py | 16 ++ src/pygromacs/cmake/modules/PythonCompile.py | 5 + .../cmake/modules/PythonMacros.cmake | 83 +++++++ src/pygromacs/cmake/modules/SIPMacros.cmake | 125 ++++++++++ src/pygromacs/include/vec.h | 62 +++++ src/pygromacs/sip/definitions.sip | 90 +++++++ src/pygromacs/sip/options/Options.sip | 5 + src/pygromacs/sip/options/options.sip | 35 +++ src/pygromacs/sip/string.sip | 20 ++ .../trajectoryanalysis/TrajectoryAnalysis.sip | 8 + .../sip/trajectoryanalysis/analysismodule.sip | 229 ++++++++++++++++++ .../trajectoryanalysis/analysissettings.sip | 45 ++++ src/pygromacs/test.py | 31 +++ 18 files changed, 1004 insertions(+) create mode 100644 src/pygromacs/CMakeLists.txt create mode 100644 src/pygromacs/__init__.py create mode 100644 src/pygromacs/cmake/modules/FindGROMACS.cmake create mode 100644 src/pygromacs/cmake/modules/FindPythonLibrary.cmake create mode 100644 src/pygromacs/cmake/modules/FindSIP.cmake create mode 100644 src/pygromacs/cmake/modules/FindSIP.py create mode 100644 src/pygromacs/cmake/modules/PythonCompile.py create mode 100644 src/pygromacs/cmake/modules/PythonMacros.cmake create mode 100644 src/pygromacs/cmake/modules/SIPMacros.cmake create mode 100644 src/pygromacs/include/vec.h create mode 100644 src/pygromacs/sip/definitions.sip create mode 100644 src/pygromacs/sip/options/Options.sip create mode 100644 src/pygromacs/sip/options/options.sip create mode 100644 src/pygromacs/sip/string.sip create mode 100644 src/pygromacs/sip/trajectoryanalysis/TrajectoryAnalysis.sip create mode 100644 src/pygromacs/sip/trajectoryanalysis/analysismodule.sip create mode 100644 src/pygromacs/sip/trajectoryanalysis/analysissettings.sip create mode 100644 src/pygromacs/test.py diff --git a/src/pygromacs/CMakeLists.txt b/src/pygromacs/CMakeLists.txt new file mode 100644 index 0000000000..6b68fb6975 --- /dev/null +++ b/src/pygromacs/CMakeLists.txt @@ -0,0 +1,36 @@ + +project(pygromacs) + +cmake_minimum_required(VERSION 2.6) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules ${CMAKE_SOURCE_DIR}/../../share/template/cmake) +add_compile_options(-std=c++11) + +find_package(PythonLibrary REQUIRED) +find_package(SIP REQUIRED) +include(SIPMacros) +include(PythonMacros) + +find_package(GROMACS REQUIRED) + +include_directories( + ${PYTHON_INCLUDE_PATH} + ${SIP_INCLUDE_DIR} + ${GROMACS_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/include +) + +set(SIP_INCLUDES ${CMAKE_BINARY_DIR} sip) +set(SIP_EXTRA_OPTIONS ${SIP_EXTRA_OPTIONS} -e -o) + +file(GLOB common_files_sip sip/*.sip) + +file(GLOB options_files_sip sip/options/*.sip) +set(SIP_EXTRA_FILES_DEPEND ${options_files_sip} ${common_files_sip}) +add_sip_python_module(gromacs.Options sip/options/Options.sip ${GROMACS_LIBRARIES}) + +file(GLOB trajectoryanalysis_files_sip sip/trajectoryanalysis/*.sip) +set(SIP_EXTRA_FILES_DEPEND ${trajectoryanalysis_files_sip} ${common_files_sip}) +add_sip_python_module(gromacs.TrajectoryAnalysis sip/trajectoryanalysis/TrajectoryAnalysis.sip ${GROMACS_LIBRARIES}) + +python_install(__init__.py ${PYTHON_SITE_PACKAGES_INSTALL_DIR}/gromacs) diff --git a/src/pygromacs/__init__.py b/src/pygromacs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pygromacs/cmake/modules/FindGROMACS.cmake b/src/pygromacs/cmake/modules/FindGROMACS.cmake new file mode 100644 index 0000000000..67a8a65299 --- /dev/null +++ b/src/pygromacs/cmake/modules/FindGROMACS.cmake @@ -0,0 +1,75 @@ +# +# This file is part of the GROMACS molecular simulation package. +# +# Copyright (c) 2014, by the GROMACS development team, led by +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, +# and including many others, as listed in the AUTHORS file in the +# top-level source directory and at http://www.gromacs.org. +# +# GROMACS 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 2.1 +# of the License, or (at your option) any later version. +# +# GROMACS 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 GROMACS; if not, see +# http://www.gnu.org/licenses, or write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# If you want to redistribute modifications to GROMACS, please +# consider that scientific software is very special. Version +# control is crucial - bugs must be traceable. We will be happy to +# consider code for inclusion in the official distribution, but +# derived work must not be called official GROMACS. Details are found +# in the README & COPYING files - if they are missing, get the +# official version at http://www.gromacs.org. +# +# To help us fund GROMACS development, we humbly ask that you cite +# the research papers on the package. Check out http://www.gromacs.org. + +# This file should remain version-agnostic, with all things specific to a +# particular GROMACS version remaining in the package configuration files. +# This find module only provides some convenience functionality to manage the +# suffixes etc. +# That should allow using the same FindGROMACS.cmake file with multiple +# different GROMACS installations on the same machine. + +# Propagate all flags passed to parent find_package() to the config call below. +set(_gmx_find_args "") +if (GROMACS_FIND_VERSION) + if (GROMACS_FIND_VERSION VERSION_LESS "5.1") + message(FATAL_ERROR + "This version of FindGROMACS.cmake requires GROMACS-provided " + "package configuration files, and only works to find " + "GROMACS 5.1 or later.") + endif() + list(APPEND _gmx_find_args ${GROMACS_FIND_VERSION}) + if (GROMACS_FIND_VERSION_EXACT) + list(APPEND _gmx_find_args EXACT) + endif() +endif() +if (GROMACS_FIND_REQUIRED) + list(APPEND _gmx_find_args REQUIRED) +endif() +if (GROMACS_FIND_QUIETLY) + list(APPEND _gmx_find_args QUIET) +endif() + +# Determine the actual name of the package configuration files. +set(_gmx_pkg_name gromacs) +if (DEFINED GROMACS_SUFFIX) + set(_gmx_pkg_name gromacs${GROMACS_SUFFIX}) +endif() +# Delegate all the actual work to the package configuration files. +# The CONFIGS option is not really necessary, but provides a bit better error +# messages, since we actually know what the config file should be called. +find_package(GROMACS ${_gmx_find_args} CONFIG + NAMES ${_gmx_pkg_name} + CONFIGS ${_gmx_pkg_name}-config.cmake) +unset(_gmx_find_args) +unset(_gmx_pkg_name) diff --git a/src/pygromacs/cmake/modules/FindPythonLibrary.cmake b/src/pygromacs/cmake/modules/FindPythonLibrary.cmake new file mode 100644 index 0000000000..975cb8e31b --- /dev/null +++ b/src/pygromacs/cmake/modules/FindPythonLibrary.cmake @@ -0,0 +1,74 @@ +# Find Python +# ~~~~~~~~~~~ +# Find the Python interpreter and related Python directories. +# +# This file defines the following variables: +# +# PYTHON_EXECUTABLE - The path and filename of the Python interpreter. +# +# PYTHON_SHORT_VERSION - The version of the Python interpreter found, +# excluding the patch version number. (e.g. 2.5 and not 2.5.1)) +# +# PYTHON_LONG_VERSION - The version of the Python interpreter found as a human +# readable string. +# +# PYTHON_SITE_PACKAGES_INSTALL_DIR - this cache variable can be used for installing +# own python modules. You may want to adjust this to be the +# same as ${PYTHON_SITE_PACKAGES_DIR}, but then admin +# privileges may be required for installation. +# +# PYTHON_SITE_PACKAGES_DIR - Location of the Python site-packages directory. +# +# PYTHON_INCLUDE_PATH - Directory holding the python.h include file. +# +# PYTHON_LIBRARY, PYTHON_LIBRARIES- Location of the Python library. + +# Copyright (c) 2007, Simon Edwards +# Copyright (c) 2012, Luca Beltrame +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindPackageHandleStandardArgs) + +find_package(PythonInterp) + +if (PYTHONINTERP_FOUND) + + option(INSTALL_PYTHON_FILES_IN_PYTHON_PREFIX "Install the Python files in the Python packages dir" FALSE) + + # Set the Python libraries to what we actually found for interpreters + set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") + # These are kept for compatibility + set(PYTHON_SHORT_VERSION "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") + set(PYTHON_LONG_VERSION ${PYTHON_VERSION_STRING}) + + find_package(PythonLibs QUIET) + + if(PYTHONLIBS_FOUND) + set(PYTHON_LIBRARY ${PYTHON_LIBRARIES}) + endif(PYTHONLIBS_FOUND) + + # Auto detect Python site-packages directory + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(True))" + OUTPUT_VARIABLE PYTHON_SITE_PACKAGES_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + message(STATUS "Python system site-packages directory: ${PYTHON_SITE_PACKAGES_DIR}") + if(INSTALL_PYTHON_FILES_IN_PYTHON_PREFIX) + set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${PYTHON_SITE_PACKAGES_DIR}) + else() + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(True, prefix='${CMAKE_INSTALL_PREFIX}'))" + OUTPUT_VARIABLE PYTHON_SITE_PACKAGES_INSTALL_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + + if(NOT PYTHON_SITE_PACKAGES_INSTALL_DIR STREQUAL PYTHON_SITE_PACKAGES_DIR) + message(STATUS "The Python files will be installed to ${PYTHON_SITE_PACKAGES_INSTALL_DIR}. Make sure to add them to the Python search path (e.g. by setting PYTHONPATH)") + endif() + +endif(PYTHONINTERP_FOUND) + +find_package_handle_standard_args(PythonLibrary DEFAULT_MSG PYTHON_LIBRARY) + diff --git a/src/pygromacs/cmake/modules/FindSIP.cmake b/src/pygromacs/cmake/modules/FindSIP.cmake new file mode 100644 index 0000000000..d6090a3d1d --- /dev/null +++ b/src/pygromacs/cmake/modules/FindSIP.cmake @@ -0,0 +1,65 @@ +# Find SIP +# ~~~~~~~~ +# +# SIP website: http://www.riverbankcomputing.co.uk/sip/index.php +# +# Find the installed version of SIP. FindSIP should be called after Python +# has been found. +# +# This file defines the following variables: +# +# SIP_VERSION - The version of SIP found expressed as a 6 digit hex number +# suitable for comparison as a string. +# +# SIP_VERSION_STR - The version of SIP found as a human readable string. +# +# SIP_EXECUTABLE - Path and filename of the SIP command line executable. +# +# SIP_INCLUDE_DIR - Directory holding the SIP C++ header file. +# +# SIP_DEFAULT_SIP_DIR - Default directory where .sip files should be installed +# into. + +# Copyright (c) 2007, Simon Edwards +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + + +IF(SIP_VERSION) + # Already in cache, be silent + SET(SIP_FOUND TRUE) +ELSE(SIP_VERSION) + + FIND_FILE(_find_sip_py FindSIP.py PATHS ${CMAKE_MODULE_PATH}) + + EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) + IF(sip_config) + STRING(REGEX REPLACE "^sip_version:([^\n]+).*$" "\\1" SIP_VERSION ${sip_config}) + STRING(REGEX REPLACE ".*\nsip_version_str:([^\n]+).*$" "\\1" SIP_VERSION_STR ${sip_config}) + STRING(REGEX REPLACE ".*\nsip_bin:([^\n]+).*$" "\\1" SIP_EXECUTABLE ${sip_config}) + IF(NOT SIP_DEFAULT_SIP_DIR) + STRING(REGEX REPLACE ".*\ndefault_sip_dir:([^\n]+).*$" "\\1" SIP_DEFAULT_SIP_DIR ${sip_config}) + ENDIF(NOT SIP_DEFAULT_SIP_DIR) + STRING(REGEX REPLACE ".*\nsip_inc_dir:([^\n]+).*$" "\\1" SIP_INCLUDE_DIR ${sip_config}) + FILE(TO_CMAKE_PATH ${SIP_DEFAULT_SIP_DIR} SIP_DEFAULT_SIP_DIR) + FILE(TO_CMAKE_PATH ${SIP_INCLUDE_DIR} SIP_INCLUDE_DIR) + IF(EXISTS ${SIP_EXECUTABLE}) + SET(SIP_FOUND TRUE) + ELSE() + MESSAGE(STATUS "Found SIP configuration but the sip executable could not be found.") + ENDIF() + ENDIF(sip_config) + + IF(SIP_FOUND) + IF(NOT SIP_FIND_QUIETLY) + MESSAGE(STATUS "Found SIP version: ${SIP_VERSION_STR}") + ENDIF(NOT SIP_FIND_QUIETLY) + ELSE(SIP_FOUND) + IF(SIP_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find SIP") + ENDIF(SIP_FIND_REQUIRED) + ENDIF(SIP_FOUND) + +ENDIF(SIP_VERSION) + diff --git a/src/pygromacs/cmake/modules/FindSIP.py b/src/pygromacs/cmake/modules/FindSIP.py new file mode 100644 index 0000000000..5e35d9f087 --- /dev/null +++ b/src/pygromacs/cmake/modules/FindSIP.py @@ -0,0 +1,16 @@ +# FindSIP.py +# +# Copyright (c) 2007, Simon Edwards +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +import sys +import sipconfig + +sipcfg = sipconfig.Configuration() +print("sip_version:%06.0x" % sipcfg.sip_version) +print("sip_version_str:%s" % sipcfg.sip_version_str) +print("sip_bin:%s" % sipcfg.sip_bin) +print("default_sip_dir:%s" % sipcfg.default_sip_dir) +print("sip_inc_dir:%s" % sipcfg.sip_inc_dir) + diff --git a/src/pygromacs/cmake/modules/PythonCompile.py b/src/pygromacs/cmake/modules/PythonCompile.py new file mode 100644 index 0000000000..e7cc17ee6f --- /dev/null +++ b/src/pygromacs/cmake/modules/PythonCompile.py @@ -0,0 +1,5 @@ +# By Simon Edwards +# This file is in the public domain. +import py_compile, sys +sys.exit(py_compile.main()) + diff --git a/src/pygromacs/cmake/modules/PythonMacros.cmake b/src/pygromacs/cmake/modules/PythonMacros.cmake new file mode 100644 index 0000000000..3a40095245 --- /dev/null +++ b/src/pygromacs/cmake/modules/PythonMacros.cmake @@ -0,0 +1,83 @@ +# Python macros +# ~~~~~~~~~~~~~ +# Copyright (c) 2007, Simon Edwards +# Copyright (c) 2012, Luca Beltrame +# Copyright (c) 2012, Rolf Eike Beer +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +# This file defines the following macros: +# +# PYTHON_INSTALL (SOURCE_FILE DESTINATION_DIR) +# Install the SOURCE_FILE, which is a Python .py file, into the +# destination directory during install. The file will be byte compiled +# and both the .py file and .pyc file will be installed. + +set(PYTHON_MACROS_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +macro(PYTHON_INSTALL SOURCE_FILE DESTINATION_DIR) + + find_file(_python_compile_py PythonCompile.py PATHS ${CMAKE_MODULE_PATH}) + + # Install the source file. + install(FILES ${SOURCE_FILE} DESTINATION ${DESTINATION_DIR}) + + # Byte compile and install the .pyc file, unless explicitly prevented by env.. + if("$ENV{PYTHONDONTWRITEBYTECODE}" STREQUAL "") + get_filename_component(_absfilename ${SOURCE_FILE} ABSOLUTE) + get_filename_component(_filename ${SOURCE_FILE} NAME) + get_filename_component(_filenamebase ${SOURCE_FILE} NAME_WE) + get_filename_component(_basepath ${SOURCE_FILE} PATH) + + if(WIN32) + # remove drive letter + string(REGEX REPLACE "^[a-zA-Z]:/" "/" _basepath "${_basepath}") + endif(WIN32) + + set(_bin_py ${CMAKE_CURRENT_BINARY_DIR}/${_basepath}/${_filename}) + + # Python 3.2 changed the pyc file location + if(PYTHON_VERSION_STRING VERSION_GREATER 3.1) + # To get the right version for suffix + set(_bin_pyc "${CMAKE_CURRENT_BINARY_DIR}/${_basepath}/__pycache__/${_filenamebase}.cpython-${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}.pyc") + set(_py_install_dir "${DESTINATION_DIR}/__pycache__/") + else() + set(_bin_pyc "${CMAKE_CURRENT_BINARY_DIR}/${_basepath}/${_filenamebase}.pyc") + set(_py_install_dir "${DESTINATION_DIR}") + endif() + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_basepath}) + + # Setting because it will be displayed later, in compile_python_files + set(_message "Byte-compiling ${_bin_py} to ${_bin_pyc}") + + string(REPLACE "/" "_" _rule_name "${_basepath}/${_bin_pyc}") + add_custom_target("${_rule_name}" ALL) + + get_filename_component(_abs_bin_py ${_bin_py} ABSOLUTE) + if(_abs_bin_py STREQUAL _absfilename) # Don't copy the file onto itself. + add_custom_command( + TARGET "${_rule_name}" + COMMAND "${CMAKE_COMMAND}" -E echo "${_message}" + COMMAND "${PYTHON_EXECUTABLE}" "${_python_compile_py}" "${_bin_py}" + DEPENDS "${_absfilename}" + ) + else() + add_custom_command( + TARGET "${_rule_name}" + COMMAND "${CMAKE_COMMAND}" -E echo "${_message}" + COMMAND "${CMAKE_COMMAND}" -E copy "${_absfilename}" "${_bin_py}" + COMMAND "${PYTHON_EXECUTABLE}" "${_python_compile_py}" "${_bin_py}" + DEPENDS "${_absfilename}" + ) + endif() + + install(FILES ${_bin_pyc} DESTINATION "${_py_install_dir}") + unset(_py_install_dir) + unset(_message) + + endif("$ENV{PYTHONDONTWRITEBYTECODE}" STREQUAL "") + +endmacro(PYTHON_INSTALL) + diff --git a/src/pygromacs/cmake/modules/SIPMacros.cmake b/src/pygromacs/cmake/modules/SIPMacros.cmake new file mode 100644 index 0000000000..a3ba6a60b5 --- /dev/null +++ b/src/pygromacs/cmake/modules/SIPMacros.cmake @@ -0,0 +1,125 @@ +# Macros for SIP +# ~~~~~~~~~~~~~~ +# Copyright (c) 2007, Simon Edwards +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +# SIP website: http://www.riverbankcomputing.co.uk/sip/index.php +# +# This file defines the following macros: +# +# ADD_SIP_PYTHON_MODULE (MODULE_NAME MODULE_SIP [library1, libaray2, ...]) +# Specifies a SIP file to be built into a Python module and installed. +# MODULE_NAME is the name of Python module including any path name. (e.g. +# os.sys, Foo.bar etc). MODULE_SIP the path and filename of the .sip file +# to process and compile. libraryN are libraries that the Python module, +# which is typically a shared library, should be linked to. The built +# module will also be install into Python's site-packages directory. +# +# The behaviour of the ADD_SIP_PYTHON_MODULE macro can be controlled by a +# number of variables: +# +# SIP_INCLUDES - List of directories which SIP will scan through when looking +# for included .sip files. (Corresponds to the -I option for SIP.) +# +# SIP_TAGS - List of tags to define when running SIP. (Corresponds to the -t +# option for SIP.) +# +# SIP_CONCAT_PARTS - An integer which defines the number of parts the C++ code +# of each module should be split into. Defaults to 8. (Corresponds to the +# -j option for SIP.) +# +# SIP_DISABLE_FEATURES - List of feature names which should be disabled +# running SIP. (Corresponds to the -x option for SIP.) +# +# SIP_EXTRA_OPTIONS - Extra command line options which should be passed on to +# SIP. + +SET(SIP_INCLUDES) +SET(SIP_TAGS) +SET(SIP_CONCAT_PARTS 8) +SET(SIP_DISABLE_FEATURES) +SET(SIP_EXTRA_OPTIONS) + +MACRO(ADD_SIP_PYTHON_MODULE MODULE_NAME MODULE_SIP) + + SET(EXTRA_LINK_LIBRARIES ${ARGN}) + + STRING(REPLACE "." "/" _x ${MODULE_NAME}) + GET_FILENAME_COMPONENT(_parent_module_path ${_x} PATH) + GET_FILENAME_COMPONENT(_child_module_name ${_x} NAME) + + GET_FILENAME_COMPONENT(_module_path ${MODULE_SIP} PATH) + + if(_module_path STREQUAL "") + set(CMAKE_CURRENT_SIP_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + else(_module_path STREQUAL "") + set(CMAKE_CURRENT_SIP_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_module_path}") + endif(_module_path STREQUAL "") + + GET_FILENAME_COMPONENT(_abs_module_sip ${MODULE_SIP} ABSOLUTE) + + # We give this target a long logical target name. + # (This is to avoid having the library name clash with any already + # install library names. If that happens then cmake dependancy + # tracking get confused.) + STRING(REPLACE "." "_" _logical_name ${MODULE_NAME}) + SET(_logical_name "python_module_${_logical_name}") + + FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_SIP_OUTPUT_DIR}) # Output goes in this dir. + + SET(_sip_includes) + FOREACH (_inc ${SIP_INCLUDES}) + GET_FILENAME_COMPONENT(_abs_inc ${_inc} ABSOLUTE) + LIST(APPEND _sip_includes -I ${_abs_inc}) + ENDFOREACH (_inc ) + + SET(_sip_tags) + FOREACH (_tag ${SIP_TAGS}) + LIST(APPEND _sip_tags -t ${_tag}) + ENDFOREACH (_tag) + + SET(_sip_x) + FOREACH (_x ${SIP_DISABLE_FEATURES}) + LIST(APPEND _sip_x -x ${_x}) + ENDFOREACH (_x ${SIP_DISABLE_FEATURES}) + + SET(_message "-DMESSAGE=Generating CPP code for module ${MODULE_NAME}") + SET(_sip_output_files) + FOREACH(CONCAT_NUM RANGE 0 ${SIP_CONCAT_PARTS} ) + IF( ${CONCAT_NUM} LESS ${SIP_CONCAT_PARTS} ) + SET(_sip_output_files ${_sip_output_files} ${CMAKE_CURRENT_SIP_OUTPUT_DIR}/sip${_child_module_name}part${CONCAT_NUM}.cpp ) + ENDIF( ${CONCAT_NUM} LESS ${SIP_CONCAT_PARTS} ) + ENDFOREACH(CONCAT_NUM RANGE 0 ${SIP_CONCAT_PARTS} ) + + IF(NOT WIN32) + SET(TOUCH_COMMAND touch) + ELSE(NOT WIN32) + SET(TOUCH_COMMAND echo) + # instead of a touch command, give out the name and append to the files + # this is basically what the touch command does. + FOREACH(filename ${_sip_output_files}) + FILE(APPEND filename "") + ENDFOREACH(filename ${_sip_output_files}) + ENDIF(NOT WIN32) + ADD_CUSTOM_COMMAND( + OUTPUT ${_sip_output_files} + COMMAND ${CMAKE_COMMAND} -E echo ${message} + COMMAND ${TOUCH_COMMAND} ${_sip_output_files} + COMMAND ${SIP_EXECUTABLE} ${_sip_tags} ${_sip_x} ${SIP_EXTRA_OPTIONS} -j ${SIP_CONCAT_PARTS} -c ${CMAKE_CURRENT_SIP_OUTPUT_DIR} ${_sip_includes} ${_abs_module_sip} + DEPENDS ${_abs_module_sip} ${SIP_EXTRA_FILES_DEPEND} + ) + # not sure if type MODULE could be uses anywhere, limit to cygwin for now + IF (CYGWIN) + ADD_LIBRARY(${_logical_name} MODULE ${_sip_output_files} ) + ELSE (CYGWIN) + ADD_LIBRARY(${_logical_name} SHARED ${_sip_output_files} ) + ENDIF (CYGWIN) + TARGET_LINK_LIBRARIES(${_logical_name} ${PYTHON_LIBRARY}) + TARGET_LINK_LIBRARIES(${_logical_name} ${EXTRA_LINK_LIBRARIES}) + SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES PREFIX "" OUTPUT_NAME ${_child_module_name}) + + INSTALL(TARGETS ${_logical_name} DESTINATION "${PYTHON_SITE_PACKAGES_INSTALL_DIR}/${_parent_module_path}") + +ENDMACRO(ADD_SIP_PYTHON_MODULE) + diff --git a/src/pygromacs/include/vec.h b/src/pygromacs/include/vec.h new file mode 100644 index 0000000000..a52afaeb92 --- /dev/null +++ b/src/pygromacs/include/vec.h @@ -0,0 +1,62 @@ +#ifndef VEC_H +#define VEC_H + +#include +#include +#include + +template class RealVector { +public: + T x, y, z; + RealVector(T v[3]) : x(v[0]), y(v[1]), z(v[2]) {}; + T operator[] (size_t i) { + switch (i) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw std::out_of_range("Inexistent component requested"); + }; + }; +}; + +template class Array { +private: + Inner *data; + size_t length; +public: + Array(Inner *data, size_t length) : data(data), length(length) {}; + Adaptor operator[] (size_t i) { + if (i >= length) + throw std::out_of_range("Array index out of range"); + + return Adaptor(data[i]); + }; + size_t len() { return length; }; +}; + +class Matrix { +private: + real data[3][3]; +public: + Matrix(real data[3][3]) { + std::copy((real*) data, ((real*) data) + 9, (real*) this->data); + }; + real get(size_t i, size_t j) { + if (i > 2 || j > 2) + throw std::out_of_range("Matrix index out of range"); + return data[i][j]; + } + RealVector get(size_t i) { + if (i > 2) + throw std::out_of_range("Matrix index out of range"); + return RealVector(data[i]); + } +}; + +typedef RealVector py_rvec; +typedef Array rvecs; +#endif diff --git a/src/pygromacs/sip/definitions.sip b/src/pygromacs/sip/definitions.sip new file mode 100644 index 0000000000..17a6249733 --- /dev/null +++ b/src/pygromacs/sip/definitions.sip @@ -0,0 +1,90 @@ + +%Exception std::out_of_range(SIP_IndexError) { + +%TypeHeaderCode +#include +%End + +%RaiseCode + SIP_BLOCK_THREADS + PyErr_SetString(PyExc_IndexError, sipExceptionRef.what()); + SIP_UNBLOCK_THREADS +%End +}; + +template class RealVector /NoDefaultCtors/ { +%TypeHeaderCode +#include "vec.h" +%End + +public: + T x; + T y; + T z; + T operator[] (unsigned int i) throw (std::out_of_range); + PyObject* __str__(); + %MethodCode + size_t size = PyOS_snprintf(NULL, 0, "Vector [%f, %f, %f]", sipCpp->x, sipCpp->y, sipCpp->z); + char str[size + 1]; + PyOS_snprintf(str, size + 1, "Vector [%f, %f, %f]", sipCpp->x, sipCpp->y, sipCpp->z); + + return PyUnicode_FromString(str); + %End +}; + +template class Array /NoDefaultCtors/ { +%TypeHeaderCode +#include "vec.h" +%End + +public: + Adaptor operator[] (unsigned int i) throw (std::out_of_range); + SIP_SSIZE_T __len__(); + %MethodCode + sipRes = sipCpp->len(); + %End +}; + +class Matrix /NoDefaultCtors/ { +%TypeHeaderCode +#include "vec.h" +%End + +public: + py_rvec __getitem__(int) throw (std::out_of_range); + %MethodCode + try { + sipRes = new py_rvec(sipCpp->get(a0)); + } catch (const std::out_of_range &e) { + sipIsErr = 1; + SIP_BLOCK_THREADS + PyErr_SetString(PyExc_IndexError, e.what()); + SIP_UNBLOCK_THREADS + } + %End + double __getitem__(SIP_PYTUPLE) throw (std::out_of_range); + %MethodCode + size_t i, j; + if (PyArg_ParseTuple(a0, "nn", &i, &j)) + try { + sipRes = sipCpp->get(i, j); + } catch (const std::out_of_range &e) { + sipIsErr = 1; + SIP_BLOCK_THREADS + PyErr_SetString(PyExc_IndexError, e.what()); + SIP_UNBLOCK_THREADS + } + else + sipIsErr = 1; + %End +}; + +typedef double* rvec; +typedef double* dvec; +typedef double** matrix; +typedef double** tensor; +typedef int* ivec; +typedef int** imatrix; + +typedef RealVector py_rvec; +typedef Array rvecs; diff --git a/src/pygromacs/sip/options/Options.sip b/src/pygromacs/sip/options/Options.sip new file mode 100644 index 0000000000..df22b1ca2c --- /dev/null +++ b/src/pygromacs/sip/options/Options.sip @@ -0,0 +1,5 @@ +%Module gromacs.Options + +%Include string.sip + +%Include options.sip diff --git a/src/pygromacs/sip/options/options.sip b/src/pygromacs/sip/options/options.sip new file mode 100644 index 0000000000..eed55389b9 --- /dev/null +++ b/src/pygromacs/sip/options/options.sip @@ -0,0 +1,35 @@ + +class OptionManagerInterface /NoDefaultCtors/ { +%TypeHeaderCode +#include + +#include +using gmx::OptionManagerInterface; +%End + +protected: + virtual ~OptionManagerInterface(); +}; + +class Options { +%TypeHeaderCode +#include + +#include +using gmx::Options; +%End + +public: + Options(const char *name, const char *title); + const std::string &name() const; + const std::string &title() const; + const std::string &description() const; + void setDescription (const std::string &desc); + void addManager (OptionManagerInterface *manager); + void addSubSection (Options *section); + + bool isSet(const char *name) const; + void finish(); +private: + Options(const Options &other); +}; diff --git a/src/pygromacs/sip/string.sip b/src/pygromacs/sip/string.sip new file mode 100644 index 0000000000..97b9ddb0d5 --- /dev/null +++ b/src/pygromacs/sip/string.sip @@ -0,0 +1,20 @@ +%MappedType std::string +{ +%TypeHeaderCode +#include +%End + +%ConvertToTypeCode +if (sipIsErr == NULL) + return PyBytes_Check(sipPy); + + *sipCppPtr = new std::string(PyBytes_AsString(sipPy), PyBytes_Size(sipPy)); + + return sipGetState(sipTransferObj); +%End + +%ConvertFromTypeCode +return PyBytes_FromStringAndSize(sipCpp->data(),sipCpp->length()); +%End + +}; diff --git a/src/pygromacs/sip/trajectoryanalysis/TrajectoryAnalysis.sip b/src/pygromacs/sip/trajectoryanalysis/TrajectoryAnalysis.sip new file mode 100644 index 0000000000..ba70a0101f --- /dev/null +++ b/src/pygromacs/sip/trajectoryanalysis/TrajectoryAnalysis.sip @@ -0,0 +1,8 @@ +%Module gromacs.TrajectoryAnalysis + +%Import options/Options.sip + +%Include definitions.sip + +%Include analysissettings.sip +%Include analysismodule.sip diff --git a/src/pygromacs/sip/trajectoryanalysis/analysismodule.sip b/src/pygromacs/sip/trajectoryanalysis/analysismodule.sip new file mode 100644 index 0000000000..b83b84ab10 --- /dev/null +++ b/src/pygromacs/sip/trajectoryanalysis/analysismodule.sip @@ -0,0 +1,229 @@ + +struct t_trxframe { + +%TypeHeaderCode +#include +#include "vec.h" +%End + + int flags; + int not_ok; + bool bDouble; + int natoms; + double t0; + double tf; + double tpf; + double tppf; + bool bTitle; + const char *title; + bool bStep; + int step; + bool bTime; + double time; + bool bLambda; + bool bFepState; + double lambda; + int fep_state; + bool bAtoms; + // TODO + /*void *atoms;*/ + bool bPrec; + double prec; + bool bX; + rvec *x { + %GetCode + rvecs *arr = new rvecs(sipCpp->x, sipCpp->natoms); + sipPy = sipConvertFromNewType(arr, sipType_rvecs, NULL); + %End + %SetCode + + %End + }; + bool bV; + rvec *v { + %GetCode + rvecs *arr = new rvecs(sipCpp->v, sipCpp->natoms); + sipPy = sipConvertFromNewType(arr, sipType_rvecs, NULL); + %End + %SetCode + + %End + }; + bool bF; + rvec *f { + %GetCode + rvecs *arr = new rvecs(sipCpp->f, sipCpp->natoms); + sipPy = sipConvertFromNewType(arr, sipType_rvecs, NULL); + %End + %SetCode + + %End + }; + bool bBox; + matrix box { + %GetCode + Matrix *mat = new Matrix(sipCpp->box); + sipPy = sipConvertFromNewType(mat, sipType_Matrix, NULL); + %End + %SetCode + + %End + }; + bool bPBC; + int ePBC; + // TODO + /*t_gmxvmdplugin* *vmdplugin; */ +}; + +struct t_pbc { + +%TypeHeaderCode +#include +#include "vec.h" +%End + int ePBC; + int ndim_ePBC; + int ePBCDX; + int dim; + matrix box { + %GetCode + Matrix *mat = new Matrix(sipCpp->box); + sipPy = sipConvertFromNewType(mat, sipType_Matrix, NULL); + %End + %SetCode + + %End + }; + rvec fbox_diag { + %GetCode + py_rvec *vec = new py_rvec(sipCpp->fbox_diag); + sipPy = sipConvertFromNewType(vec, sipType_py_rvec, NULL); + %End + %SetCode + + %End + }; + rvec hbox_diag { + %GetCode + py_rvec *vec = new py_rvec(sipCpp->fbox_diag); + sipPy = sipConvertFromNewType(vec, sipType_py_rvec, NULL); + %End + %SetCode + + %End + }; + rvec mhbox_diag { + %GetCode + py_rvec *vec = new py_rvec(sipCpp->mhbox_diag); + sipPy = sipConvertFromNewType(vec, sipType_py_rvec, NULL); + %End + %SetCode + + %End + }; + double max_cutoff2; + bool bLimitDistance; + double limit_distance2; + int ntric_vec; + /*ivec tric_shift[MAX_NTRICVEC];*/ + /*rvec tric_vec[MAX_NTRICVEC];*/ +}; + +class TrajectoryAnalysisModuleData /NoDefaultCtors/ { + +%TypeHeaderCode +#include +using namespace gmx; +%End + +public: + virtual void finish() = 0; +}; + +class TrajectoryAnalysisModule { + +%TypeHeaderCode +#include +using namespace gmx; +%End +public: + virtual void initOptions(Options *options, TrajectoryAnalysisSettings *settings) = 0; + virtual void optionsFinished(Options *options, TrajectoryAnalysisSettings *settings); + virtual void initAnalysis(const TrajectoryAnalysisSettings &settings, const TopologyInformation &top) = 0; + virtual void initAfterFirstFrame(const TrajectoryAnalysisSettings &settings, const t_trxframe &fr); +// virtual TrajectoryAnalysisModuleDataPointer startFrames (const AnalysisDataParallelOptions &opt, const SelectionCollection &selections); + virtual void analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc, TrajectoryAnalysisModuleData *pdata ) = 0; + virtual void finishFrames(TrajectoryAnalysisModuleData *pdata); + virtual void finishAnalysis(int nframes) = 0; + virtual void writeOutput() = 0; + const char* name() const; + const char* description() const; + int datasetCount() const; +protected: + TrajectoryAnalysisModule(const char *name, const char *description); +private: + TrajectoryAnalysisModule(const TrajectoryAnalysisModule &other); +}; + +%Exception gmx::InconsistentInputError(SIP_Exception) { + +%TypeHeaderCode +#include +using namespace std; +%End + +%RaiseCode + printf("lALLA\n"); + const char *detail = sipExceptionRef.what(); + + SIP_BLOCK_THREADS + PyErr_SetString(sipException_gmx_InconsistentInputError, detail); + SIP_UNBLOCK_THREADS +%End +}; + +class TrajectoryAnalysisCommandLineRunner { +%TypeHeaderCode +#include +%End + +public: + TrajectoryAnalysisCommandLineRunner(TrajectoryAnalysisModule *module); + int run(SIP_PYLIST) throw (gmx::InconsistentInputError); + %MethodCode + int argc = PyList_GET_SIZE(a0); + + char **argv = new char *[argc + 1]; + + // Convert the list. + for (int a = 0; a < argc; ++a) + { + PyObject *arg_obj = PyList_GET_ITEM(a0, a); + const char *arg = sipString_AsLatin1String(&arg_obj); + + if (arg) + { + arg = strdup(arg); + Py_DECREF(arg_obj); + } + else + { + arg = "unknown"; + } + + argv[a] = const_cast(arg); + } + + argv[argc] = NULL; + + try { + sipCpp->run(argc, argv); + } catch (gmx::InconsistentInputError &e) { + sipIsErr = 1; + PyErr_SetString(sipException_gmx_InconsistentInputError, e.what()); + } + %End +private: + TrajectoryAnalysisCommandLineRunner(const TrajectoryAnalysisCommandLineRunner &other); +}; + diff --git a/src/pygromacs/sip/trajectoryanalysis/analysissettings.sip b/src/pygromacs/sip/trajectoryanalysis/analysissettings.sip new file mode 100644 index 0000000000..c608ce98c3 --- /dev/null +++ b/src/pygromacs/sip/trajectoryanalysis/analysissettings.sip @@ -0,0 +1,45 @@ + +class TrajectoryAnalysisSettings { +%TypeHeaderCode +#include +using namespace gmx; +%End + +public: + enum { + efRequireTop = 1, + efUseTopX = 2, + efNoUserPBC = 16, + efNoUserRmPBC = 32 + }; + + TrajectoryAnalysisSettings(); + /* + const TimeUnitManager& timeUnitManager() const; + TimeUnit timeUnit(); + const AnalysisDataPlotSettings& plotSettings() const; + */ + unsigned long flags() const; + bool hasFlag(unsigned long flag) const; + bool hasPBC() const; + bool hasRmPBC() const; + int frflags() const; + void setFlags(unsigned long flags); + void setFlag(unsigned long flag, bool bSet = true); + void setPBC(bool bPBC); + void setRmPBC(bool bRmPBC); + void setFrameFlags(int frflags); +private: + TrajectoryAnalysisSettings(const TrajectoryAnalysisSettings &other); +}; + +class TopologyInformation { +%TypeHeaderCode +#include +using namespace gmx; +%End + +private: + TopologyInformation(const TopologyInformation &other); + ~TopologyInformation(); +}; diff --git a/src/pygromacs/test.py b/src/pygromacs/test.py new file mode 100644 index 0000000000..5c01936423 --- /dev/null +++ b/src/pygromacs/test.py @@ -0,0 +1,31 @@ +import sys +from gromacs import TrajectoryAnalysis + +class M(TrajectoryAnalysis.TrajectoryAnalysisModule): + def __init__(self): + super(M, self).__init__(b"a", b"a") + + def initOptions(self, options, settings): + print('python: initOptions') + options.setDescription(b'A stupid test module') + #settings.setFlag(TrajectoryAnalysis.TrajectoryAnalysisSettings.efRequireTop) + print('python: inited') + + def initAnalysis(self, settings, top): + print('python: initAnalysis') + + def analyzeFrame(self, frnr, frame, pbc, data): + print('python: Analyzing frame {}, {} atoms'.format(frnr, frame.natoms)) + #print(frame.box[0,0], frame.box[0,1], frame.box[0,2]) + print(pbc.box[0], pbc.box[1], pbc.box[2]) + + def finishAnalysis(self, nframes): + print('python: Analyzed {} frames'.format(nframes)) + + def writeOutput(self): + print('python: writeOutput') + +m = M() + +runner = TrajectoryAnalysis.TrajectoryAnalysisCommandLineRunner(m) +print(runner.run(sys.argv)) -- 2.22.0