Add initial support for python bindings
authorMaxim Koltsov <maksbotan@gentoo.org>
Sun, 12 Oct 2014 08:58:33 +0000 (12:58 +0400)
committerMaxim Koltsov <maksbotan@gentoo.org>
Sun, 12 Oct 2014 08:58:33 +0000 (12:58 +0400)
Add minimal SIP-based bindings required to implement trajectory analysis
module in Python and run it

18 files changed:
src/pygromacs/CMakeLists.txt [new file with mode: 0644]
src/pygromacs/__init__.py [new file with mode: 0644]
src/pygromacs/cmake/modules/FindGROMACS.cmake [new file with mode: 0644]
src/pygromacs/cmake/modules/FindPythonLibrary.cmake [new file with mode: 0644]
src/pygromacs/cmake/modules/FindSIP.cmake [new file with mode: 0644]
src/pygromacs/cmake/modules/FindSIP.py [new file with mode: 0644]
src/pygromacs/cmake/modules/PythonCompile.py [new file with mode: 0644]
src/pygromacs/cmake/modules/PythonMacros.cmake [new file with mode: 0644]
src/pygromacs/cmake/modules/SIPMacros.cmake [new file with mode: 0644]
src/pygromacs/include/vec.h [new file with mode: 0644]
src/pygromacs/sip/definitions.sip [new file with mode: 0644]
src/pygromacs/sip/options/Options.sip [new file with mode: 0644]
src/pygromacs/sip/options/options.sip [new file with mode: 0644]
src/pygromacs/sip/string.sip [new file with mode: 0644]
src/pygromacs/sip/trajectoryanalysis/TrajectoryAnalysis.sip [new file with mode: 0644]
src/pygromacs/sip/trajectoryanalysis/analysismodule.sip [new file with mode: 0644]
src/pygromacs/sip/trajectoryanalysis/analysissettings.sip [new file with mode: 0644]
src/pygromacs/test.py [new file with mode: 0644]

diff --git a/src/pygromacs/CMakeLists.txt b/src/pygromacs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6b68fb6
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/src/pygromacs/cmake/modules/FindGROMACS.cmake b/src/pygromacs/cmake/modules/FindGROMACS.cmake
new file mode 100644 (file)
index 0000000..67a8a65
--- /dev/null
@@ -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 (file)
index 0000000..975cb8e
--- /dev/null
@@ -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 <simon@simonzone.com>
+# Copyright (c) 2012, Luca Beltrame <lbeltrame@kde.org>
+# 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 (file)
index 0000000..d6090a3
--- /dev/null
@@ -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 <simon@simonzone.com>
+# 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 (file)
index 0000000..5e35d9f
--- /dev/null
@@ -0,0 +1,16 @@
+# FindSIP.py
+#
+# Copyright (c) 2007, Simon Edwards <simon@simonzone.com>
+# 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 (file)
index 0000000..e7cc17e
--- /dev/null
@@ -0,0 +1,5 @@
+# By Simon Edwards <simon@simonzone.com>
+# 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 (file)
index 0000000..3a40095
--- /dev/null
@@ -0,0 +1,83 @@
+# Python macros
+# ~~~~~~~~~~~~~
+# Copyright (c) 2007, Simon Edwards <simon@simonzone.com>
+# Copyright (c) 2012, Luca Beltrame <lbeltrame@kde.org>
+# Copyright (c) 2012, Rolf Eike Beer <eike@sf-mail.de>
+#
+# 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 (file)
index 0000000..a3ba6a6
--- /dev/null
@@ -0,0 +1,125 @@
+# Macros for SIP
+# ~~~~~~~~~~~~~~
+# Copyright (c) 2007, Simon Edwards <simon@simonzone.com>
+# 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 (file)
index 0000000..a52afae
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef VEC_H
+#define VEC_H
+
+#include <cstdlib>
+#include <stdexcept>
+#include <gromacs/math/vectypes.h>
+
+template<typename T> 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<typename T, typename Adaptor, typename Inner> 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<real> get(size_t i) {
+        if (i > 2)
+            throw std::out_of_range("Matrix index out of range");
+        return RealVector<real>(data[i]);
+    }
+};
+
+typedef RealVector<real> py_rvec;
+typedef Array<real, py_rvec, rvec> rvecs;
+#endif
diff --git a/src/pygromacs/sip/definitions.sip b/src/pygromacs/sip/definitions.sip
new file mode 100644 (file)
index 0000000..17a6249
--- /dev/null
@@ -0,0 +1,90 @@
+
+%Exception std::out_of_range(SIP_IndexError) {
+
+%TypeHeaderCode
+#include <stdexcept>
+%End
+
+%RaiseCode
+    SIP_BLOCK_THREADS
+    PyErr_SetString(PyExc_IndexError, sipExceptionRef.what());
+    SIP_UNBLOCK_THREADS
+%End
+};
+
+template<T> 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<T, Adaptor, Inner> 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<double> py_rvec;
+typedef Array<double, py_rvec, rvec> rvecs;
diff --git a/src/pygromacs/sip/options/Options.sip b/src/pygromacs/sip/options/Options.sip
new file mode 100644 (file)
index 0000000..df22b1c
--- /dev/null
@@ -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 (file)
index 0000000..eed5538
--- /dev/null
@@ -0,0 +1,35 @@
+
+class OptionManagerInterface /NoDefaultCtors/ {
+%TypeHeaderCode
+#include <string>
+
+#include <gromacs/options/options.h>
+using gmx::OptionManagerInterface;
+%End
+
+protected:
+    virtual ~OptionManagerInterface();
+};
+
+class Options {
+%TypeHeaderCode
+#include <string>
+
+#include <gromacs/options/options.h>
+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 (file)
index 0000000..97b9ddb
--- /dev/null
@@ -0,0 +1,20 @@
+%MappedType std::string
+{
+%TypeHeaderCode
+#include <string>
+%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 (file)
index 0000000..ba70a01
--- /dev/null
@@ -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 (file)
index 0000000..b83b84a
--- /dev/null
@@ -0,0 +1,229 @@
+
+struct t_trxframe {
+
+%TypeHeaderCode
+#include <gromacs/fileio/trx.h>
+#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 <gromacs/pbcutil/pbc.h>
+#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 <gromacs/trajectoryanalysis/analysismodule.h>
+using namespace gmx;
+%End
+
+public:
+    virtual void finish() = 0;
+};
+
+class TrajectoryAnalysisModule {
+
+%TypeHeaderCode
+#include <gromacs/trajectoryanalysis/analysismodule.h>
+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 <gromacs/utility/exceptions.h>
+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 <gromacs/trajectoryanalysis/cmdlinerunner.h>
+%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<char *>(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 (file)
index 0000000..c608ce9
--- /dev/null
@@ -0,0 +1,45 @@
+
+class TrajectoryAnalysisSettings {
+%TypeHeaderCode
+#include <gromacs/trajectoryanalysis/analysissettings.h>
+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 <gromacs/trajectoryanalysis/analysissettings.h>
+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 (file)
index 0000000..5c01936
--- /dev/null
@@ -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))