diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml
new file mode 100644
index 0000000..6f74bca
--- /dev/null
+++ b/.github/workflows/cpp.yml
@@ -0,0 +1,92 @@
+name: "cpp"
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build and test cpp
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-22.04
+ cc: gcc
+ cxx: g++
+ - os: ubuntu-22.04
+ cc: gcc
+ cxx: g++
+ env_list: EMSCRIPTEN=ON
+ - os: ubuntu-22.04
+ cc: clang
+ cxx: clang++
+ - os: ubuntu-22.04
+ cc: gcc
+ cxx: g++
+ cmake_args: "-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS='-march=native'"
+ - os: macos-latest
+ cc: clang
+ cxx: clang++
+ - os: windows-latest
+ cc: ''
+ cxx: ''
+ runs-on: ${{ matrix.os }}
+ env:
+ CC: ${{ matrix.cc }}
+ CXX: ${{ matrix.cxx }}
+ CMAKE_ARGS: ${{ matrix.cmake_args }}
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ # - name: install mingw & deps
+ # uses: msys2/setup-msys2@v2
+ # with:
+ # update: true
+ # install: >-
+ # git
+ # make
+ # pacboy: >-
+ # toolchain:p
+ # cmake:p
+ # ninja:p
+ # if: matrix.os == 'windows-latest'
+
+ - name: Set environment list variables
+ run: |
+ env_vars="${{ matrix.env_list }}"
+ for var in $env_vars; do
+ echo "$var" >> $GITHUB_ENV
+ done
+ if: matrix.os != 'windows-latest'
+
+ - name: Setup cmake
+ uses: jwlawson/actions-setup-cmake@v1.13
+ with:
+ cmake-version: '3.16.x'
+
+ - uses: seanmiddleditch/gha-setup-ninja@master
+
+ - name: build and test
+ run: |
+ mkdir build && cd build
+ cmake $CMAKE_ARGS -G Ninja -Dmmtf_build_examples=ON -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=debug ..
+ ninja
+ ./tests/mmtf_tests
+ ./tests/multi_cpp_test
+ ./examples/mmtf_demo ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
+ ./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
+ ./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf json
+ ./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf print
+ ./examples/print_as_pdb ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml
new file mode 100644
index 0000000..4c200d9
--- /dev/null
+++ b/.github/workflows/emscripten.yml
@@ -0,0 +1,61 @@
+name: WASM
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build-wasm-emscripten:
+ name: Pyodide
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install pyodide-build
+ run: pip install pyodide-build==0.23.4
+
+ - name: Compute emsdk version
+ id: compute-emsdk-version
+ run: |
+ pyodide xbuildenv install --download
+ EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
+ echo "emsdk-version=$EMSCRIPTEN_VERSION" >> $GITHUB_OUTPUT
+
+ - uses: mymindstorm/setup-emsdk@v12
+ with:
+ version: ${{ steps.compute-emsdk-version.outputs.emsdk-version }}
+ actions-cache-folder: emsdk-cache
+
+ # A future version of pyodide may switch to -fwasm-exceptions
+ - name: Build
+ run: CFLAGS=-fexceptions LDFLAGS=-fexceptions pyodide build
+
+ - uses: actions/upload-artifact@v3
+ with:
+ path: dist/*.whl
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - name: Set up Pyodide virtual environment
+ run: |
+ pyodide venv .venv-pyodide
+ .venv-pyodide/bin/pip install $(echo -n dist/*.whl)
+
+ - name: Test
+ run: .venv-pyodide/bin/python -m unittest src/python/tests/tests.py
+
diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml
new file mode 100644
index 0000000..cfa5cbb
--- /dev/null
+++ b/.github/workflows/pip.yml
@@ -0,0 +1,46 @@
+name: "Pip"
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build with Pip
+ runs-on: ${{ matrix.platform }}
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [windows-latest, macos-latest, ubuntu-latest]
+ python-version: ["3.8", "3.11", "3.12", "pypy-3.8"]
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ # - name: Setup cmake
+ # uses: jwlawson/actions-setup-cmake@v1.13
+ # with:
+ # cmake-version: '3.16.x'
+
+ # - uses: seanmiddleditch/gha-setup-ninja@master
+
+ - name: Build and install
+ run: pip install --verbose .
+
+ - name: Test
+ run: python src/python/tests/tests.py
+
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
new file mode 100644
index 0000000..e4f92b5
--- /dev/null
+++ b/.github/workflows/wheels.yml
@@ -0,0 +1,89 @@
+name: Wheels
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - master
+ release:
+ types:
+ - published
+
+env:
+ FORCE_COLOR: 3
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build_sdist:
+ name: Build SDist
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ - name: Build SDist
+ run: pipx run build --sdist
+
+ - name: Check metadata
+ run: pipx run twine check dist/*
+
+ - uses: actions/upload-artifact@v3
+ with:
+ path: dist/*.tar.gz
+
+
+ build_wheels:
+ name: Wheels on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ # - name: Set CMAKE_GENERATOR for Windows
+ # if: matrix.os == 'windows-latest'
+ # run: echo "CMAKE_GENERATOR=MinGW Makefiles" >> $GITHUB_ENV
+
+ - uses: pypa/cibuildwheel@v2.16.2
+ env:
+ CIBW_ARCHS_MACOS: universal2
+ CIBW_ARCHS_WINDOWS: auto ARM64
+ CMAKE_GENERATOR: ${{ env.CMAKE_GENERATOR }}
+
+ - name: Verify clean directory
+ run: git diff --exit-code
+ shell: bash
+
+ - uses: actions/upload-artifact@v3
+ with:
+ path: wheelhouse/*.whl
+
+ upload_all:
+ name: Upload if release
+ needs: [build_wheels, build_sdist]
+ runs-on: ubuntu-latest
+ if: github.event_name == 'release' && github.event.action == 'published'
+
+ steps:
+ - uses: actions/setup-python@v4
+ with:
+ python-version: "3.x"
+
+ - uses: actions/download-artifact@v3
+ with:
+ name: artifact
+ path: dist
+
+ - uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ password: ${{ secrets.pypi_password }}
diff --git a/.gitignore b/.gitignore
index a8de19a..e649fb8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,171 @@ build/*
docs/html/*
examples/out/*
examples/out_json_ref/*
+
+# python eggs
+src/python/*.egg-info
+**/__pycache__
+**/*.pyc
+
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
diff --git a/.gitmodules b/.gitmodules
index 3cd3c3c..1582527 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,12 +1,6 @@
-[submodule "Catch2"]
- path = Catch2
- url = https://github.com/catchorg/Catch2
-[submodule "msgpack-c"]
- path = msgpack-c
- url = https://github.com/msgpack/msgpack-c
-[submodule "mmtf_spec"]
- path = mmtf_spec
- url = https://github.com/rcsb/mmtf
-[submodule "pybind11"]
- path = pybind11
- url = git@github.com:pybind/pybind11.git
+[submodule "submodules/pybind11"]
+ path = submodules/pybind11
+ url = https://github.com/pybind/pybind11.git
+[submodule "submodules/mmtf_spec"]
+ path = submodules/mmtf_spec
+ url = https://github.com/rcsb/mmtf.git
diff --git a/.travis.yml b/.travis.yml
index 401c088..70fd8ea 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,57 +1,106 @@
+---
language: cpp
sudo: false
dist: trusty
linux64_addons:
addons: &linux64
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - g++-4.8
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
linux32_addons:
addons: &linux32
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - g++-4.8
- - g++-4.8-multilib
- - linux-libc-dev:i386
- - libc6-dev-i386
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
+ - g++-4.8-multilib
+ - linux-libc-dev:i386
+ - libc6-dev-i386
+linux64_cpp17addons:
+ addons: &linux64cpp17
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+
+linux64_cpp17addons:
+ addons: &linux64cpp17
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+
+
+linux64_cpp17addons_py:
+ addons: &linux64cpp17py
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - gcc-7
+ - g++-7
+
+python_test_command_sub: &python_test_command TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_python_tests.sh CC=gcc
# Set empty values for allow_failures to work
-env:
+env: TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+python:
matrix:
- fast_finish: true
- include:
- - os: linux
- env: EMSCRIPTEN=ON
- addons: *linux64
- - os: linux
- compiler: clang
- addons: *linux64
- - os: linux
- compiler: gcc
- env: ARCH=x86 CMAKE_EXTRA=-DHAVE_LIBM=/lib32/libm.so.6
- addons: *linux32
- - os: osx
- compiler: clang
+ fast_finish: true
+ include:
+ - os: linux
+ env: EMSCRIPTEN=ON TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+ addons: *linux64
+ - os: linux
+ compiler: clang
+ addons: *linux64
+ - os: linux
+ compiler: gcc
+ env: ARCH=x86 CMAKE_EXTRA=-DHAVE_LIBM=/lib32/libm.so.6 TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+ addons: *linux32
+ - os: osx
+ compiler: clang
+ - os: linux
+ compiler: gcc
+ env: CMAKE_EXTRA="-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS='-march=native'" TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+ addons: *linux64cpp17
+ dist: bionic
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17
+ dist: bionic
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17py
+ dist: bionic
+ env: *python_test_command
+ python: 3.8
+ language: python
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17py
+ dist: bionic
+ env: *python_test_command
+ python: 3.11
+ language: python
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17py
+ dist: bionic
+ env: *python_test_command
+ python: 3.12
+ language: python
before_install:
- # Setting environement
- - cd $TRAVIS_BUILD_DIR
- - source ci/setup-travis.sh
- - $CC --version
- - $CXX --version
+ # Setting environement
+ - cd $TRAVIS_BUILD_DIR
+ - source ci/setup-travis.sh
+ - $CC --version
+ - $CXX --version
script:
- - cd $TRAVIS_BUILD_DIR
- - mkdir build && cd build
- - $CMAKE_CONFIGURE cmake $CMAKE_ARGS $CMAKE_EXTRA ..
- - make -j2
- - ctest -j2 --output-on-failure
- - bash $TRAVIS_BUILD_DIR/ci/travis-test-example.sh
- - cd $TRAVIS_BUILD_DIR
+ - echo $TEST_COMMAND
+ - (eval "$TEST_COMMAND")
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8962d68..f4da72e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
+
+## v1.1.0 - 2022-10-03
### Added
- New mapDecoderFrom.. functions to decode only part of an MMTF file
- Support for extra fields in MMTF files according to the
@@ -15,10 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
[MMTF specification](https://github.com/rcsb/mmtf/pull/35).
- New methods to find polymer chains and HETATM following discussions in
[rcsb/mmtf#28](https://github.com/rcsb/mmtf/issues/28).
+- Altered submodule locations [rcsb/mmtf-cpp#37](https://github.com/rcsb/mmtf-cpp/pull/37)
+ from the base directory to the new submodules directory.
## v1.0.0 - 2019-02-05
### Added
- Initial release including decoder and encoder for the
[MMTF specification 1.0](https://github.com/rcsb/mmtf/blob/v1.0/spec.md).
-[Unreleased]: https://github.com/rcsb/mmtf-cpp/compare/v1.0.0...HEAD
+[Unreleased]: https://github.com/rcsb/mmtf-cpp/compare/v1.1.0...HEAD
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d95e07..9e4d72f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,10 @@
-
-cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.15...3.26 FATAL_ERROR)
project(mmtf-cpp VERSION 1.0.0 LANGUAGES CXX)
-option(mmtf_build_local "Use the submodule dependencies for building" OFF)
option(mmtf_build_examples "Build the examples" OFF)
+SET(MSGPACK_USE_BOOST OFF CACHE BOOL "msgpack-c uses boost by default, lets keep that off")
+# option(MSGPACK_USE_BOOST "msgpack-c uses boost by default, lets keep that off" OFF)
add_library(MMTFcpp INTERFACE)
target_compile_features(MMTFcpp INTERFACE cxx_auto_type)
@@ -13,23 +13,23 @@ target_include_directories(MMTFcpp INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
-if (mmtf_build_local)
- # use header only
- set(MSGPACKC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/msgpack-c/include)
- add_library(msgpackc INTERFACE)
- target_include_directories(msgpackc INTERFACE ${MSGPACKC_INCLUDE_DIR})
- if (BUILD_TESTS)
- set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Catch2/single_include)
- add_library(Catch INTERFACE)
- target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
- endif()
+include(FetchContent)
+FetchContent_Declare(
+ msgpack-cxx
+ GIT_REPOSITORY https://github.com/msgpack/msgpack-c.git
+ GIT_TAG cpp-6.1.0)
+FetchContent_MakeAvailable(msgpack-cxx)
+
+if(WIN32)
+ target_link_libraries(MMTFcpp INTERFACE ws2_32)
endif()
-if (NOT TARGET msgpackc)
- find_package(msgpack)
+target_link_libraries(MMTFcpp INTERFACE msgpack-cxx)
+
+if (build_py)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/python)
endif()
-target_link_libraries(MMTFcpp INTERFACE msgpackc)
if (BUILD_TESTS)
enable_testing()
diff --git a/Catch2 b/Catch2
deleted file mode 160000
index cf4b7ee..0000000
--- a/Catch2
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit cf4b7eead92773932f32c7efd2612e9d27b07557
diff --git a/README.md b/README.md
index d6964a5..4fa1df8 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
-[![Release](https://img.shields.io/github/release/rcsb/mmtf-cpp.svg?style=flat)](https://github.com/rcsb/mmtf-cpp/releases)
-[![License](https://img.shields.io/github/license/rcsb/mmtf-cpp.svg?style=flat)](https://github.com/rcsb/mmtf-cpp/blob/master/LICENSE)
-[![Build Status (Travis)](https://img.shields.io/travis/rcsb/mmtf-cpp/master.svg?style=flat)](https://travis-ci.org/rcsb/mmtf-cpp)
-[![Build Status (AppVeyor)](https://img.shields.io/appveyor/ci/rcsb/mmtf-cpp/master.svg?style=flat)](https://ci.appveyor.com/project/rcsb/mmtf-cpp)
+[![Release](https://img.shields.io/github/v/release/rcsb/mmtf-cpp)](https://github.com/rcsb/mmtf-cpp/releases)
+[![License](https://img.shields.io/github/license/rcsb/mmtf-cpp)](https://github.com/rcsb/mmtf-cpp/blob/master/LICENSE)
+[![Build Status (Travis)](https://img.shields.io/travis/com/rcsb/mmtf-cpp/master)](https://app.travis-ci.com/github/rcsb/mmtf-cpp)
The macromolecular transmission format
([MMTF](http://mmtf.rcsb.org)) is a binary encoding of biological structures.
@@ -46,6 +45,53 @@ Here, `` and `` are the paths to the
For your more complicated projects, a `CMakeLists.txt` is included for you.
+
+### Python bindings
+
+The C++ MMTF library now can build python bindings using pybind11. To use them
+you must have A) a c++11 compatible compiler and B) python >= 3.6
+
+to install, it is as simple as `pip install .`
+
+(in the future possible `pip install mmtf-cpp`)
+
+```python
+from mmtf_cppy import StructureData
+import numpy as np
+import math
+
+
+def rotation_matrix(axis, theta):
+ """
+ Return the rotation matrix associated with counterclockwise rotation about
+ the given axis by theta radians.
+ from https://stackoverflow.com/a/6802723
+ """
+ axis = np.asarray(axis)
+ axis = axis / math.sqrt(np.dot(axis, axis))
+ a = math.cos(theta / 2.0)
+ b, c, d = -axis * math.sin(theta / 2.0)
+ aa, bb, cc, dd = a * a, b * b, c * c, d * d
+ bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
+ return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
+ [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
+ [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])
+
+
+theta = 1.2
+axis = [0, 0, 1]
+
+sd = StructureData("my_favorite_structure.mmtf")
+sd.atomProperties["pymol_colorList"] = [1 if x % 2 == 0 else 5 for x in sd.xCoordList]
+xyz = np.column_stack((sd.xCoordList, sd.yCoordList, sd.zCoordList))
+xyz_rot = rotation_matrix(axis, theta).dot(xyz.T).T
+sd.xCoordList, sd.yCoordList, sd.zCoordList = np.hsplit(xyz_rot, 3)
+sd.write_to_file("my_favorite_structure_rot.mmtf")
+
+```
+
+
+
## Installation
You can also perform a system wide installation with `cmake` and `ninja` (or `make`).
To do so:
@@ -73,7 +119,7 @@ To build the tests + examples we recommend using the following lines:
git submodule update --init --recursive
mkdir build
cd build
-cmake -G Ninja -DBUILD_TESTS=ON -Dmmtf_build_local=ON -Dmmtf_build_examples=ON ..
+cmake -G Ninja -DBUILD_TESTS=ON -Dmmtf_build_examples=ON ..
ninja
chmod +x ./tests/mmtf_tests
./tests/mmtf_tests
@@ -83,18 +129,18 @@ Example codes:
- mmtf_demo.cpp: Loads an MMTF file and checks internal consistency using
mmtf::StructureData::hasConsistentData.
```bash
-./examples/mmtf_demo ../mmtf_spec/test-suite/mmtf/173D.mmtf
+./examples/mmtf_demo ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
```
- traverse.cpp: Loads an MMTF file and dumps it in human-readable forms.
```bash
-./examples/traverse ../mmtf_spec/test-suite/mmtf/173D.mmtf
-./examples/traverse ../mmtf_spec/test-suite/mmtf/173D.mmtf json
-./examples/traverse ../mmtf_spec/test-suite/mmtf/173D.mmtf print
+./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
+./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf json
+./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf print
```
- print_as_pdb.cpp: Loads an MMTF file and prints it in pdb format.
```bash
-./examples/print_as_pdb ../mmtf_spec/test-suite/mmtf/173D.mmtf
+./examples/print_as_pdb ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
```
## Benchmark
diff --git a/bindings/4lgr.mmtf b/bindings/4lgr.mmtf
deleted file mode 100644
index 53d34fb..0000000
Binary files a/bindings/4lgr.mmtf and /dev/null differ
diff --git a/bindings/bindings.cpp b/bindings/bindings.cpp
deleted file mode 100644
index 7059ce4..0000000
--- a/bindings/bindings.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-
-//#ifndef MMTF_CPP_PYBIND_BINDINGS_HH
-//#define MMTF_CPP_PYBIND_BINDINGS_HH
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace py = pybind11;
-
-std::string
-array2str1(py::array const & arr) {
- std::vector array_vec(arr.size());
- std::memcpy(array_vec.data(),arr.data(),arr.size()*sizeof(double));
- std::string ret("w");
- return ret;
-}
-
-std::string
-array2str2(std::vector const & arr) {
- //if (arr.empty()) return "[]";
- //std::stringstream ss;
- //std::string const delim(", ");
- //for (float const & f : arr) {
- // ss << f << delim;
- //}
- //std::string ret(ss.str());
- //ret.pop_back(); ret.pop_back();
- std::string ret("x");
- return ret;
-}
-
-struct Pet {
- Pet(const std::string &name) : name(name) {
- for (int i=0; i< 1000000; ++i) {
- bdata.push_back(i);
- }
- }
- void setName(const std::string &name_) { name = name_; }
- const std::string &getName() const { return name; }
-
- std::string name;
- std::vector bdata;
-};
-
-template< typename T >
-py::array
-array1d_from_vector(std::vector & m) {
- if (m.empty()) return py::array_t();
- std::vector* ptr = new std::vector(std::move(m));
- auto capsule = py::capsule(ptr, [](void* p) { delete reinterpret_cast*>(p); });
- return py::array_t(ptr->size(), // shape of array
- ptr->data(), // c-style contiguous strides for Sequence
- capsule // numpy array references this parent
- );
-}
-
-
-template< typename T >
-py::array
-array2d_from_vector(std::vector> & m) {
- if (m.empty()) return py::array_t();
- std::vector>* ptr = new std::vector>(std::move(m));
- auto capsule = py::capsule(ptr, [](void* p) { delete reinterpret_cast>*>(p); });
- return py::array_t({ptr->size(), ptr->at(0).size()}, // shape of array
- {ptr->size()*ptr->at(0).size()*sizeof(T)}, // c-style contiguous strides for Sequence
- capsule // numpy array references this parent
- );
-}
-
-py::bytes
-raw_properties(mmtf::StructureData const & sd) {
- std::stringstream bytes;
- std::map< std::string, std::map< std::string, msgpack::object > > objs({
- {"bondProperties", sd.bondProperties },
- {"atomProperties", sd.atomProperties },
- {"groupProperties", sd.groupProperties },
- {"chainProperties", sd.chainProperties },
- {"modelProperties", sd.modelProperties },
- {"extraProperties", sd.extraProperties }});
- msgpack::pack(bytes, objs);
- return py::bytes(bytes.str().data());
-}
-
-
-
-PYBIND11_MODULE(example, m) {
- py::class_(m, "Pet")
- .def(py::init())
- .def("setName", &Pet::setName)
- .def("getName", &Pet::getName)
- .def_readwrite("bdata", &Pet::bdata)
- .def("bdata2", [](Pet &m) -> py::array {
- py::buffer_info buff_info(py::buffer_info(
- m.bdata.data(), /* Pointer to buffer */
- sizeof(float), /* Size of one scalar */
- py::format_descriptor::format(), /* Python struct-style format descriptor */
- m.bdata.size() /* Number of dimensions */
- ));
- return py::array(buff_info);
- });
- m.def("array2str1", &array2str1, "array impl");
- m.def("array2str2", &array2str2, "vector impl");
-//}
-//
-//PYBIND11_MODULE(notsure, m) {
- // new stuff here
- py::class_(m, "CPPStructureData")
- .def( pybind11::init( [](){ return new mmtf::StructureData(); } ) )
- .def( pybind11::init( [](mmtf::StructureData const &o){ return new mmtf::StructureData(o); } ) )
- .def_readwrite("mmtfVersion", &mmtf::StructureData::mmtfVersion)
- .def_readwrite("mmtfProducer", &mmtf::StructureData::mmtfProducer)
- .def("unitCell", [](mmtf::StructureData &m){return array1d_from_vector(m.unitCell);})
- .def_readwrite("spaceGroup", &mmtf::StructureData::spaceGroup)
- .def_readwrite("structureId", &mmtf::StructureData::structureId)
- .def_readwrite("title", &mmtf::StructureData::title)
- .def_readwrite("depositionDate", &mmtf::StructureData::depositionDate)
- .def_readwrite("releaseDate", &mmtf::StructureData::releaseDate)
- //.def("ncsOperatorList", [](mmtf::StructureData &m){return array2d_from_vector(m.ncsOperatorList, 16);})
- .def("ncsOperatorList", [](mmtf::StructureData &m){return array2d_from_vector(m.ncsOperatorList);})
- .def_readwrite("bioAssemblyList", &mmtf::StructureData::bioAssemblyList)
- .def_readwrite("entityList", &mmtf::StructureData::entityList)
- .def_readwrite("experimentalMethods", &mmtf::StructureData::experimentalMethods)
- .def_readwrite("resolution", &mmtf::StructureData::resolution)
- .def_readwrite("rFree", &mmtf::StructureData::rFree)
- .def_readwrite("rWork", &mmtf::StructureData::rWork)
- .def_readwrite("numBonds", &mmtf::StructureData::numBonds)
- .def_readwrite("numAtoms", &mmtf::StructureData::numAtoms)
- .def_readwrite("numGroups", &mmtf::StructureData::numGroups)
- .def_readwrite("numChains", &mmtf::StructureData::numChains)
- .def_readwrite("numModels", &mmtf::StructureData::numModels)
- .def_readwrite("groupList", &mmtf::StructureData::groupList)
- .def("unitCell", [](mmtf::StructureData &m){return array1d_from_vector(m.unitCell);})
- .def("bondAtomList", [](mmtf::StructureData &m){return array1d_from_vector(m.bondAtomList);})
- .def("bondOrderList", [](mmtf::StructureData &m){return array1d_from_vector(m.bondOrderList);})
- .def("bondResonanceList", [](mmtf::StructureData &m){return array1d_from_vector(m.bondResonanceList);})
- .def("xCoordList", [](mmtf::StructureData &m){return array1d_from_vector(m.xCoordList);})
- .def("yCoordList", [](mmtf::StructureData &m){return array1d_from_vector(m.yCoordList);})
- .def("zCoordList", [](mmtf::StructureData &m){return array1d_from_vector(m.zCoordList);})
- .def("bFactorList", [](mmtf::StructureData &m){return array1d_from_vector(m.bFactorList);})
- .def("atomIdList", [](mmtf::StructureData &m){return array1d_from_vector(m.atomIdList);})
- .def_readwrite("altLocList", &mmtf::StructureData::altLocList)
- .def("occupancyList", [](mmtf::StructureData &m){return array1d_from_vector(m.occupancyList);})
- .def("groupIdList", [](mmtf::StructureData &m){return array1d_from_vector(m.groupIdList);})
- .def("groupTypeList", [](mmtf::StructureData &m){return array1d_from_vector(m.groupTypeList);})
- .def("secStructList", [](mmtf::StructureData &m){return array1d_from_vector(m.secStructList);})
- .def_readwrite("insCodeList", &mmtf::StructureData::insCodeList)
- .def("sequenceIndexList", [](mmtf::StructureData &m){return array1d_from_vector(m.sequenceIndexList);})
- .def_readwrite("chainIdList", &mmtf::StructureData::chainIdList)
- .def_readwrite("chainNameList", &mmtf::StructureData::chainNameList)
- .def("groupsPerChain", [](mmtf::StructureData &m){return array1d_from_vector(m.groupsPerChain);})
- .def("chainsPerModel", [](mmtf::StructureData &m){return array1d_from_vector(m.chainsPerModel);})
- .def("raw_properties", [](mmtf::StructureData const &m){return raw_properties(m);});
- //cl.def_readonly("msgpack_zone", &mmtf::StructureData::msgpack_zone);
- //cl.def_readwrite("bondProperties", &mmtf::StructureData::bondProperties);
- //cl.def_readwrite("atomProperties", &mmtf::StructureData::atomProperties);
- //cl.def_readwrite("groupProperties", &mmtf::StructureData::groupProperties);
- //cl.def_readwrite("chainProperties", &mmtf::StructureData::chainProperties);
- //cl.def_readwrite("modelProperties", &mmtf::StructureData::modelProperties);
- //cl.def_readwrite("extraProperties", &mmtf::StructureData::extraProperties);
- m.def("decodeFromFile", &mmtf::decodeFromFile, "decode a mmtf::StructureData from a file");
-
- py::class_(m, "CPPBioAssembly")
- .def( pybind11::init( [](){ return new mmtf::BioAssembly(); } ) )
- .def( pybind11::init( [](mmtf::BioAssembly const &o){ return new mmtf::BioAssembly(o); } ) )
- .def_readwrite("name", &mmtf::BioAssembly::name);
- // TODO ^^ insert transformlist
- py::class_(m, "CPPTransform")
- .def( pybind11::init( [](){ return new mmtf::Transform(); } ) )
- .def( pybind11::init( [](mmtf::Transform const &o){ return new mmtf::Transform(o); } ) );
- /// TODO finish ^^
- py::class_(m, "CPPGroupType")
- .def( pybind11::init( [](){ return new mmtf::GroupType(); } ) )
- .def( pybind11::init( [](mmtf::GroupType const &o){ return new mmtf::GroupType(o); } ) );
- /// TODO finish ^^
- py::class_(m, "CPPEntity")
- .def( pybind11::init( [](){ return new mmtf::Entity(); } ) )
- .def( pybind11::init( [](mmtf::Entity const &o){ return new mmtf::Entity(o); } ) );
-
-
-}
-
-
-//#endif
diff --git a/bindings/check.py b/bindings/check.py
deleted file mode 100644
index 454d46d..0000000
--- a/bindings/check.py
+++ /dev/null
@@ -1,7 +0,0 @@
-import numpy as np
-import example
-import time
-
-p = example.Pet("xx")
-
-m
diff --git a/bindings/compile.sh b/bindings/compile.sh
deleted file mode 100755
index 7dfe9b7..0000000
--- a/bindings/compile.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` -I../pybind11/include -I../msgpack-c/include -I../include bindings.cpp -o example`python3-config --extension-suffix`
-python mmtf_t.py
diff --git a/bindings/mmtf_t.py b/bindings/mmtf_t.py
deleted file mode 100644
index 8cdc1d3..0000000
--- a/bindings/mmtf_t.py
+++ /dev/null
@@ -1,173 +0,0 @@
-
-import time
-import numpy as np
-import mmtf
-import msgpack
-
-import example
-from example import CPPStructureData as CPPSD, decodeFromFile
-
-class StructureData:
- def __init__(self, file_name=None, file_bytes=None):
- if file_name:
- self.init_from_file_name(file_name)
- elif file_bytes:
- self.init_from_raw_bytes(file_bytes)
- else:
- self.raw_init()
-
- def init_from_file_name(self, file_name: str):
- cppsd = CPPSD()
- decodeFromFile(cppsd, file_name)
- self.init_from_cppsd(cppsd)
-
- def init_from_cppsd(self, cppsd: 'CPPStructureData'):
- self.mmtfVersion = cppsd.mmtfVersion
- self.mmtfProducer = cppsd.mmtfProducer
- self.unitCell = cppsd.unitCell()
- self.spaceGroup = cppsd.spaceGroup
- self.structureId = cppsd.structureId
- self.title = cppsd.title
- self.depositionDate = cppsd.depositionDate
- self.releaseDate = cppsd.releaseDate
- self.ncsOperatorList = cppsd.ncsOperatorList()
- # self.bioAssemblyList = cppsd.bioAssemblyList
- # self.entityList = cppsd.entityList
- self.experimentalMethods = cppsd.experimentalMethods
- self.resolution = cppsd.resolution
- self.rFree = cppsd.rFree
- self.rWork = cppsd.rWork
- self.numBonds = cppsd.numBonds
- self.numAtoms = cppsd.numAtoms
- self.numGroups = cppsd.numGroups
- self.numChains = cppsd.numChains
- self.numModels = cppsd.numModels
- # self.groupList = cppsd.groupList
-
- self.bondAtomList = cppsd.bondAtomList()
- self.bondOrderList = cppsd.bondOrderList()
- self.bondResonanceList = cppsd.bondResonanceList()
- self.xCoordList = cppsd.xCoordList()
- self.yCoordList = cppsd.yCoordList()
- self.zCoordList = cppsd.zCoordList()
- self.bFactorList = cppsd.bFactorList()
- self.atomIdList = cppsd.atomIdList()
- self.altLocList = cppsd.altLocList
- self.occupancyList = cppsd.occupancyList()
- # print(type(cppsd.groupIdList()))
- self.groupIdList = cppsd.groupIdList()
- # print(self.groupIdList)
- self.groupIdList = cppsd.groupIdList()
- self.groupTypeList = cppsd.groupTypeList()
- self.secStructList = cppsd.secStructList()
- self.insCodeList = cppsd.insCodeList
- self.sequenceIndexList = cppsd.sequenceIndexList()
- self.chainIdList = cppsd.chainIdList
- self.chainNameList = cppsd.chainNameList
- self.groupsPerChain = cppsd.groupsPerChain()
- self.chainsPerModel = cppsd.chainsPerModel()
-
-
- # self.bondAtomList = np.array(cppsd.bondAtomList(), copy=False)
- # self.bondOrderList = np.array(cppsd.bondOrderList(), copy=False)
- # self.bondResonanceList = np.array(cppsd.bondResonanceList(), copy=False)
- # self.xCoordList = np.array(cppsd.xCoordList(), copy=False)
- # self.yCoordList = np.array(cppsd.yCoordList(), copy=False)
- # self.zCoordList = np.array(cppsd.zCoordList(), copy=False)
- # self.bFactorList = np.array(cppsd.bFactorList(), copy=False)
- # self.atomIdList = np.array(cppsd.atomIdList(), copy=False)
- # self.altLocList = np.array(cppsd.altLocList, copy=False)
- # self.occupancyList = np.array(cppsd.occupancyList(), copy=False)
- # self.groupIdList = np.array(cppsd.groupIdList(), copy=False)
- # # print(self.groupIdList)
- # self.groupIdList = np.array(cppsd.groupIdList(), copy=False)
- # self.groupTypeList = np.array(cppsd.groupTypeList(), copy=False)
- # self.secStructList = np.array(cppsd.secStructList(), copy=False)
- # self.insCodeList = np.array(cppsd.insCodeList, copy=False)
- # self.sequenceIndexList = np.array(cppsd.sequenceIndexList(), copy=False)
- # self.chainIdList = np.array(cppsd.chainIdList, copy=False)
- # self.chainNameList = np.array(cppsd.chainNameList, copy=False)
- # self.groupsPerChain = np.array(cppsd.groupsPerChain(), copy=False)
- # self.chainsPerModel = np.array(cppsd.chainsPerModel(), copy=False)
-
- raw_properties = cppsd.raw_properties()
- raw_properties = msgpack.unpackb(raw_properties, raw=False)
- # print(type(raw_properties), len(raw_properties))
- self.bondProperties = raw_properties["bondProperties"]
- self.atomProperties = raw_properties["atomProperties"]
- self.groupProperties = raw_properties["groupProperties"]
- self.chainProperties = raw_properties["chainProperties"]
- self.modelProperties = raw_properties["modelProperties"]
- self.extraProperties = raw_properties["extraProperties"]
-
- def raw_init(self):
- self.mmtfVersion = None
- self.mmtfProducer = None
- self.unitCell = None
- self.spaceGroup = None
- self.structureId = None
- self.title = None
- self.depositionDate = None
- self.releaseDate = None
- self.ncsOperatorList = None
- self.bioAssemblyList = None
- self.entityList = None
- self.experimentalMethods = None
- self.resolution = None
- self.rFree = None
- self.rWork = None
- self.numBonds = None
- self.numAtoms = None
- self.numGroups = None
- self.numChains = None
- self.numModels = None
- self.groupList = None
- self.bondAtomList = None
- self.bondOrderList = None
- self.bondResonanceList = None
- self.xCoordList = None
- self.yCoordList = None
- self.zCoordList = None
- self.bFactorList = None
- self.atomIdList = None
- self.altLocList = None
- self.occupancyList = None
- self.groupIdList = None
- self.groupTypeList = None
- self.secStructList = None
- self.insCodeList = None
- self.sequenceIndexList = None
- self.chainIdList = None
- self.chainNameList = None
- self.groupsPerChain = None
- self.chainsPerModel = None
- self.bondProperties = None
- self.atomProperties = None
- self.groupProperties = None
- self.chainProperties = None
- self.modelProperties = None
- self.extraProperties = None
-
-
-
-start = time.time()
-for x in range(10000):
- sd = StructureData("4lgr.mmtf")
-stop = time.time()
-python_t = stop-start
-print("python", python_t)
-
-start = time.time()
-for x in range(1000):
- sd = mmtf.parse("4lgr.mmtf")
-stop = time.time()
-python_t = stop-start
-print("python og", python_t)
-
-start = time.time()
-for x in range(10000):
- cppsd = CPPSD()
- decodeFromFile(cppsd, "4lgr.mmtf")
-stop = time.time()
-cpp_t = stop-start
-print("cpp", cpp_t)
diff --git a/ci/build_and_run_python_tests.sh b/ci/build_and_run_python_tests.sh
new file mode 100755
index 0000000..e2704c9
--- /dev/null
+++ b/ci/build_and_run_python_tests.sh
@@ -0,0 +1,10 @@
+
+python3 --version
+pip3 --version
+pip3 install -r requirements.txt
+pip3 install -r requirements-dev.txt
+cd $TRAVIS_BUILD_DIR
+pip3 install .
+pytest python_src/tests/tests.py -s -vv
+
+
diff --git a/ci/build_and_run_tests.sh b/ci/build_and_run_tests.sh
new file mode 100755
index 0000000..c56538b
--- /dev/null
+++ b/ci/build_and_run_tests.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -e
+cd "$TRAVIS_BUILD_DIR"
+mkdir build && cd build
+$CMAKE_CONFIGURE cmake $CMAKE_ARGS $CMAKE_EXTRA ..
+make -j2
+ctest -j2 --output-on-failure
+bash $TRAVIS_BUILD_DIR/ci/travis-test-example.sh
+cd $TRAVIS_BUILD_DIR
diff --git a/ci/setup-travis.sh b/ci/setup-travis.sh
index 4554348..0fb5b25 100644
--- a/ci/setup-travis.sh
+++ b/ci/setup-travis.sh
@@ -25,9 +25,17 @@ if [[ "$EMSCRIPTEN" == "ON" ]]; then
fi
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
- if [[ "$CC" == "gcc" ]]; then
- export CC=gcc-4.8
- export CXX=g++-4.8
+ if [[ "$TRAVIS_DIST" == "trusty" ]]; then
+ if [[ "$CC" == "gcc" ]]; then
+ export CC=gcc-4.8
+ export CXX=g++-4.8
+ fi
+ fi
+ if [[ "$TRAVIS_DIST" == "bionic" ]]; then
+ if [[ "$CC" == "gcc" ]]; then
+ export CC=gcc-7
+ export CXX=g++-7
+ fi
fi
fi
diff --git a/ci/travis-test-example.sh b/ci/travis-test-example.sh
index 220e025..6772ac3 100644
--- a/ci/travis-test-example.sh
+++ b/ci/travis-test-example.sh
@@ -10,13 +10,13 @@ set -e
cd $TRAVIS_BUILD_DIR/examples
if [ -z "$EMSCRIPTEN" ]; then
# Compile with C++03 forced
- $CXX -I"../msgpack-c/include" -I"../include" -std=c++03 -O2 \
+ $CXX -I"../submodules/msgpack-c/include" -I"../include" -std=c++03 -O2 \
-o read_and_write read_and_write.cpp
- ./read_and_write ../mmtf_spec/test-suite/mmtf/3NJW.mmtf test.mmtf
+ ./read_and_write ../submodules/mmtf_spec/test-suite/mmtf/3NJW.mmtf test.mmtf
else
# Cannot do C++03 here and need to embed input file for running it with node
- cp ../mmtf_spec/test-suite/mmtf/3NJW.mmtf .
- $CXX -I"../msgpack-c/include" -I"../include" -O2 \
+ cp ../submodules/mmtf_spec/test-suite/mmtf/3NJW.mmtf .
+ $CXX -I"../submodules/msgpack-c/include" -I"../include" -O2 \
-o read_and_write.js read_and_write.cpp --embed-file 3NJW.mmtf
node read_and_write.js 3NJW.mmtf test.mmtf
fi
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 10eb2b8..73e883e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,4 +1,3 @@
-
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
SET(executables mmtf_demo traverse print_as_pdb tableexport read_and_write)
@@ -6,10 +5,6 @@ SET(executables mmtf_demo traverse print_as_pdb tableexport read_and_write)
foreach(exe ${executables})
add_executable(${exe} ${exe}.cpp)
target_compile_features(${exe} PRIVATE cxx_auto_type)
- if(WIN32)
- target_link_libraries(${exe} MMTFcpp ws2_32)
- else()
- target_link_libraries(${exe} MMTFcpp)
- endif()
+ target_link_libraries(${exe} MMTFcpp)
endforeach(exe)
diff --git a/include/mmtf.hpp b/include/mmtf.hpp
index 34ec332..3d7224e 100644
--- a/include/mmtf.hpp
+++ b/include/mmtf.hpp
@@ -12,5 +12,7 @@
// single include for all MMTF needs
+#define VERSION_INFO
+
#include "mmtf/decoder.hpp"
#include "mmtf/encoder.hpp"
diff --git a/include/mmtf/binary_decoder.hpp b/include/mmtf/binary_decoder.hpp
index 00b39bb..2a4eadf 100644
--- a/include/mmtf/binary_decoder.hpp
+++ b/include/mmtf/binary_decoder.hpp
@@ -34,11 +34,25 @@ class BinaryDecoder {
* Reads out binary header to prepare for call of decode.
* @param[in] obj Object to decode.
* @param[in] key Key used to report errors.
+ * @warning This uses a pointer to the data of obj. obj must stay alive
+ * while this instance exists or it will result in undefined behavior.
* @throw mmtf::DecodeError if obj is not a binary or is too short.
*/
BinaryDecoder(const msgpack::object& obj,
const std::string& key = "UNNAMED_BINARY");
+ /**
+ * @brief Initialize object given a msgpack binary string.
+ * Reads out binary header to prepare for call of decode.
+ * @param[in] str Object to decode.
+ * @param[in] key Key used to report errors.
+ * @warning This uses a pointer to the data of str. str must stay alive
+ * while this instance exists or it will result in undefined behavior.
+ * @throw mmtf::DecodeError if obj is not a binary or is too short.
+ */
+ BinaryDecoder(const std::string& str,
+ const std::string& key = "UNNAMED_BINARY");
+
/**
* @brief Decode binary msgpack object into the given target.
*
@@ -55,7 +69,7 @@ class BinaryDecoder {
* @throw mmtf::DecodeError if we fail to decode.
*/
template
- void decode(T& target);
+ void decode(T& target) const;
private:
// for error reporting
@@ -67,42 +81,47 @@ class BinaryDecoder {
const char* encodedData_;
uint32_t encodedDataLength_; // max. size for binary is 2^32 - 1
+ // helper function for constructors
+ void
+ initFromData(const char * str_data,
+ const std::size_t len);
+
// check length consistency (throws)
- void checkLength_(int32_t exp_length);
+ void checkLength_(int32_t exp_length) const;
// check if binary data is divisible by x (throws)
- void checkDivisibleBy_(int32_t item_size);
+ void checkDivisibleBy_(int32_t item_size) const;
// byte decoders
- void decodeFromBytes_(std::vector& output);
- void decodeFromBytes_(std::vector& output);
- void decodeFromBytes_(std::vector& output);
- void decodeFromBytes_(std::vector& output);
+ void decodeFromBytes_(std::vector& output) const;
+ void decodeFromBytes_(std::vector& output) const;
+ void decodeFromBytes_(std::vector& output) const;
+ void decodeFromBytes_(std::vector& output) const;
// special one: decode to vector of strings
- void decodeFromBytes_(std::vector& output);
+ void decodeFromBytes_(std::vector& output) const;
// run length decoding
// -> Int and IntOut can be any integer types
// -> Int values are blindly converted to IntOut
template
void runLengthDecode_(const std::vector& input,
- std::vector& output);
+ std::vector& output) const;
// delta decoding -> Int can be any integer type
template
- void deltaDecode_(const std::vector& input, std::vector& output);
+ void deltaDecode_(const std::vector& input, std::vector& output) const;
// variant doing it in-place
template
- void deltaDecode_(std::vector& in_out);
+ void deltaDecode_(std::vector& in_out) const;
// recursive indexing decode -> SmallInt must be smaller than Int
template
void recursiveIndexDecode_(const std::vector& input,
- std::vector& output);
+ std::vector& output) const;
// decode integer to float -> Int can be any integer type
template
- void decodeDivide_(const std::vector& input, float divisor,
- std::vector& output);
+ void decodeDivide_(const std::vector& input, float const divisor,
+ std::vector& output) const;
};
// *************************************************************************
@@ -121,11 +140,17 @@ namespace {
#ifndef __EMSCRIPTEN__
void assignBigendian4(void* dst, const char* src) {
- *((uint32_t*)dst) = ntohl(*((uint32_t*)src));
+ uint32_t tmp;
+ std::memcpy(&tmp, src, sizeof(uint32_t));
+ tmp = ntohl(tmp);
+ std::memcpy(dst, &tmp, sizeof(uint32_t));
}
void assignBigendian2(void* dst, const char* src) {
- *((uint16_t*)dst) = ntohs(*((uint16_t*)src));
+ uint16_t tmp;
+ std::memcpy(&tmp, src, sizeof(uint16_t));
+ tmp = ntohs(tmp);
+ std::memcpy(dst, &tmp, sizeof(uint16_t));
}
#else
// Need to avoid how emscripten handles memory
@@ -160,6 +185,16 @@ void arrayCopyBigendian2(void* dst, const char* src, size_t n) {
} // anon ns
+
+// note this does not set key_, you must set it in ctor
+inline void BinaryDecoder::initFromData(const char * bytes, std::size_t const len) {
+ assignBigendian4(&strategy_, bytes);
+ assignBigendian4(&length_, bytes + 4);
+ assignBigendian4(¶meter_, bytes + 8);
+ encodedData_ = bytes + 12;
+ encodedDataLength_ = len - 12;
+}
+
inline BinaryDecoder::BinaryDecoder(const msgpack::object& obj,
const std::string& key)
: key_(key) {
@@ -172,23 +207,22 @@ inline BinaryDecoder::BinaryDecoder(const msgpack::object& obj,
err << "The '" + key + "' entry is too short " << obj.via.bin.size;
throw DecodeError(err.str());
}
- // get data (encoded data is only pointed to and not parsed here)
- const char* bytes = obj.via.bin.ptr;
+ this->initFromData(obj.via.bin.ptr, obj.via.bin.size);
+}
- assignBigendian4(&strategy_, bytes);
- assignBigendian4(&length_, bytes + 4);
- assignBigendian4(¶meter_, bytes + 8);
- encodedData_ = bytes + 12;
- encodedDataLength_ = obj.via.bin.size - 12;
+inline BinaryDecoder::BinaryDecoder(const std::string& str,
+ const std::string& key)
+ : key_(key) {
+ this->initFromData(str.data(), str.size());
}
template
-void BinaryDecoder::decode(T& target) {
+void BinaryDecoder::decode(T&) const {
throw mmtf::DecodeError("Invalid target type for binary '" + key_ + "'");
}
template<>
-inline void BinaryDecoder::decode(std::vector& output) {
+inline void BinaryDecoder::decode(std::vector& output) const {
// check strategy to parse
switch (strategy_) {
@@ -248,7 +282,7 @@ inline void BinaryDecoder::decode(std::vector& output) {
}
template<>
-inline void BinaryDecoder::decode(std::vector& output) {
+inline void BinaryDecoder::decode(std::vector& output) const {
// check strategy to parse
switch (strategy_) {
@@ -275,7 +309,7 @@ inline void BinaryDecoder::decode(std::vector& output) {
}
template<>
-inline void BinaryDecoder::decode(std::vector& output) {
+inline void BinaryDecoder::decode(std::vector& output) const {
// check strategy to parse
switch (strategy_) {
@@ -296,7 +330,7 @@ inline void BinaryDecoder::decode(std::vector& output) {
}
template<>
-inline void BinaryDecoder::decode(std::vector& output) {
+inline void BinaryDecoder::decode(std::vector& output) const {
// check strategy to parse
switch (strategy_) {
@@ -342,7 +376,7 @@ inline void BinaryDecoder::decode(std::vector& output) {
}
template<>
-inline void BinaryDecoder::decode(std::vector& output) {
+inline void BinaryDecoder::decode(std::vector& output) const {
// check strategy to parse
switch (strategy_) {
@@ -363,7 +397,7 @@ inline void BinaryDecoder::decode(std::vector& output) {
}
template<>
-inline void BinaryDecoder::decode(std::vector& output) {
+inline void BinaryDecoder::decode(std::vector& output) const {
// check strategy to parse
switch (strategy_) {
@@ -386,7 +420,7 @@ inline void BinaryDecoder::decode(std::vector& output) {
}
// checks
-inline void BinaryDecoder::checkLength_(int32_t exp_length) {
+inline void BinaryDecoder::checkLength_(int32_t exp_length) const {
if (length_ != exp_length) {
std::stringstream err;
err << "Length mismatch for binary '" + key_ + "': "
@@ -395,7 +429,7 @@ inline void BinaryDecoder::checkLength_(int32_t exp_length) {
}
}
-inline void BinaryDecoder::checkDivisibleBy_(int32_t item_size) {
+inline void BinaryDecoder::checkDivisibleBy_(int32_t item_size) const {
if (encodedDataLength_ % item_size != 0) {
std::stringstream err;
err << "Binary length of '" + key_ + "': "
@@ -405,7 +439,7 @@ inline void BinaryDecoder::checkDivisibleBy_(int32_t item_size) {
}
// byte decoders
-inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
+inline void BinaryDecoder::decodeFromBytes_(std::vector& output) const {
checkDivisibleBy_(4);
// prepare memory
output.resize(encodedDataLength_ / 4);
@@ -414,7 +448,7 @@ inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
arrayCopyBigendian4(&output[0], encodedData_, encodedDataLength_);
}
}
-inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
+inline void BinaryDecoder::decodeFromBytes_(std::vector& output) const {
// prepare memory
output.resize(encodedDataLength_);
// get data
@@ -422,7 +456,7 @@ inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
memcpy(&output[0], encodedData_, encodedDataLength_);
}
}
-inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
+inline void BinaryDecoder::decodeFromBytes_(std::vector& output) const {
checkDivisibleBy_(2);
// prepare memory
output.resize(encodedDataLength_ / 2);
@@ -431,7 +465,7 @@ inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
arrayCopyBigendian2(&output[0], encodedData_, encodedDataLength_);
}
}
-inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
+inline void BinaryDecoder::decodeFromBytes_(std::vector& output) const {
checkDivisibleBy_(4);
// prepare memory
output.resize(encodedDataLength_ / 4);
@@ -441,7 +475,7 @@ inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
}
}
// special one: decode to vector of strings
-inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
+inline void BinaryDecoder::decodeFromBytes_(std::vector& output) const {
char NULL_BYTE = 0x00;
// check parameter
const int32_t str_len = parameter_;
@@ -458,7 +492,7 @@ inline void BinaryDecoder::decodeFromBytes_(std::vector& output) {
// run length decoding
template
void BinaryDecoder::runLengthDecode_(const std::vector& input,
- std::vector& output) {
+ std::vector& output) const {
// we work with pairs of numbers
checkDivisibleBy_(2);
// find out size of resulting vector (for speed)
@@ -482,7 +516,7 @@ void BinaryDecoder::runLengthDecode_(const std::vector& input,
// delta decoding
template
void BinaryDecoder::deltaDecode_(const std::vector& input,
- std::vector& output) {
+ std::vector& output) const {
// reserve space (for speed)
output.clear();
if (input.empty()) return; // ensure we have some values
@@ -494,7 +528,7 @@ void BinaryDecoder::deltaDecode_(const std::vector& input,
}
}
template
-void BinaryDecoder::deltaDecode_(std::vector& in_out) {
+void BinaryDecoder::deltaDecode_(std::vector& in_out) const {
for (size_t i = 1; i < in_out.size(); ++i) {
in_out[i] = in_out[i - 1] + in_out[i];
}
@@ -503,7 +537,7 @@ void BinaryDecoder::deltaDecode_(std::vector& in_out) {
// recursive indexing decode
template
void BinaryDecoder::recursiveIndexDecode_(const std::vector& input,
- std::vector& output) {
+ std::vector& output) const {
// get limits
const SmallInt min_int = std::numeric_limits::min();
const SmallInt max_int = std::numeric_limits::max();
@@ -528,8 +562,8 @@ void BinaryDecoder::recursiveIndexDecode_(const std::vector& input,
// decode integer to float
template
-void BinaryDecoder::decodeDivide_(const std::vector& input, float divisor,
- std::vector& output) {
+void BinaryDecoder::decodeDivide_(const std::vector& input, float const divisor,
+ std::vector& output) const {
// reserve space and get inverted divisor (for speed)
output.clear();
output.reserve(input.size());
diff --git a/include/mmtf/binary_encoder.hpp b/include/mmtf/binary_encoder.hpp
index 5224f2d..6aa07db 100644
--- a/include/mmtf/binary_encoder.hpp
+++ b/include/mmtf/binary_encoder.hpp
@@ -98,21 +98,21 @@ inline std::vector encodeInt8ToByte(std::vector vec_in);
* @param[in] vec_in Vector of ints to encode
* @return Char vector of encoded bytes
*/
-inline std::vector encodeFourByteInt(std::vector vec_in);
+inline std::vector encodeFourByteInt(std::vector const & vec_in);
/** Encode string vector encoding (type 5)
* @param[in] in_sv Vector of strings to encode
* @param[in] CHAIN_LEN Maximum length of string
* @return Char vector of encoded bytes
*/
-inline std::vector encodeStringVector(std::vector in_sv, int32_t CHAIN_LEN);
+inline std::vector encodeStringVector(std::vector const & in_sv, int32_t const CHAIN_LEN);
/** Encode Run Length Char encoding (type 6)
* @param[in] in_cv Vector of chars to encode
* @return Char vector of encoded bytes
*/
-inline std::vector encodeRunLengthChar(std::vector in_cv);
+inline std::vector encodeRunLengthChar(std::vector const & in_cv);
/** Encode Run Length Delta Int encoding (type 8)
@@ -126,20 +126,20 @@ inline std::vector encodeRunLengthDeltaInt(std::vector int_vec);
* @param[in] multiplier Multiplier to convert float to int
* @return Char vector of encoded bytes
*/
-inline std::vector encodeRunLengthFloat(std::vector floats_in, int32_t multiplier);
+inline std::vector encodeRunLengthFloat(std::vector const & floats_in, int32_t const multiplier);
/** Encode Delta Recursive Float encoding (type 10)
* @param[in] floats_in Vector of floats to encode
* @param[in] multiplier Multiplier to convert float to int
* @return Char vector of encoded bytes
*/
-inline std::vector encodeDeltaRecursiveFloat(std::vector floats_in, int32_t multiplier);
+inline std::vector encodeDeltaRecursiveFloat(std::vector const & floats_in, int32_t const multiplier);
/** Encode Run-Length 8bit int encoding (type 16)
* @param[in] int8_vec Vector of ints to encode
* @return Char vector of encoded bytes
*/
-inline std::vector encodeRunLengthInt8(std::vector int8_vec);
+inline std::vector encodeRunLengthInt8(std::vector const & int8_vec);
// *************************************************************************
// IMPLEMENTATION
@@ -148,7 +148,7 @@ inline std::vector encodeRunLengthInt8(std::vector int8_vec);
namespace { // private helpers
inline std::vector convertFloatsToInts(std::vector const & vec_in,
- int multiplier) {
+ int const multiplier) {
std::vector vec_out;
for (size_t i=0; i(round(vec_in[i]*multiplier)));
@@ -242,7 +242,7 @@ inline std::vector encodeInt8ToByte(std::vector vec_in) {
}
-inline std::vector encodeFourByteInt(std::vector vec_in) {
+inline std::vector encodeFourByteInt(std::vector const & vec_in) {
std::stringstream ss;
add_header(ss, vec_in.size(), 4, 0);
for (size_t i=0; i encodeFourByteInt(std::vector vec_in) {
}
-inline std::vector encodeStringVector(std::vector in_sv, int32_t CHAIN_LEN) {
+inline std::vector encodeStringVector(std::vector const & in_sv, int32_t const CHAIN_LEN) {
char NULL_BYTE = 0x00;
std::stringstream ss;
add_header(ss, in_sv.size(), 5, CHAIN_LEN);
@@ -271,7 +271,7 @@ inline std::vector encodeStringVector(std::vector in_sv, int3
}
-inline std::vector encodeRunLengthChar(std::vector in_cv) {
+inline std::vector encodeRunLengthChar(std::vector const & in_cv) {
std::stringstream ss;
add_header(ss, in_cv.size(), 6, 0);
std::vector int_vec = runLengthEncode(in_cv);
@@ -295,7 +295,7 @@ inline std::vector encodeRunLengthDeltaInt(std::vector int_vec) {
return stringstreamToCharVector(ss);
}
-inline std::vector encodeRunLengthFloat(std::vector floats_in, int32_t multiplier) {
+inline std::vector encodeRunLengthFloat(std::vector const & floats_in, int32_t const multiplier) {
std::stringstream ss;
add_header(ss, floats_in.size(), 9, multiplier);
std::vector int_vec = convertFloatsToInts(floats_in, multiplier);
@@ -308,7 +308,7 @@ inline std::vector encodeRunLengthFloat(std::vector floats_in, int3
}
-inline std::vector encodeDeltaRecursiveFloat(std::vector floats_in, int32_t multiplier) {
+inline std::vector encodeDeltaRecursiveFloat(std::vector const & floats_in, int32_t const multiplier) {
std::stringstream ss;
add_header(ss, floats_in.size(), 10, multiplier);
std::vector int_vec = convertFloatsToInts(floats_in, multiplier);
@@ -322,10 +322,10 @@ inline std::vector encodeDeltaRecursiveFloat(std::vector floats_in,
}
-inline std::vector encodeRunLengthInt8(std::vector int8_vec) {
+inline std::vector encodeRunLengthInt8(std::vector const & int8_vec) {
std::stringstream ss;
add_header(ss, int8_vec.size(), 16, 0);
- std::vector int_vec = runLengthEncode(int8_vec);
+ std::vector const int_vec = runLengthEncode(int8_vec);
for (size_t i=0; i(&temp), sizeof(temp));
diff --git a/include/mmtf/map_decoder.hpp b/include/mmtf/map_decoder.hpp
index ba77c44..33bee28 100644
--- a/include/mmtf/map_decoder.hpp
+++ b/include/mmtf/map_decoder.hpp
@@ -243,7 +243,7 @@ inline void MapDecoder::init_from_msgpack_obj(const msgpack::object& obj) {
inline void MapDecoder::checkType_(const std::string& key,
msgpack::type::object_type type,
- const float& target) const {
+ const float&) const {
if (type != msgpack::type::FLOAT32 && type != msgpack::type::FLOAT64) {
std::cerr << "Warning: Non-float type " << type << " found for "
"entry " << key << std::endl;
@@ -251,7 +251,7 @@ inline void MapDecoder::checkType_(const std::string& key,
}
inline void MapDecoder::checkType_(const std::string& key,
msgpack::type::object_type type,
- const int32_t& target) const {
+ const int32_t&) const {
if ( type != msgpack::type::POSITIVE_INTEGER
&& type != msgpack::type::NEGATIVE_INTEGER) {
std::cerr << "Warning: Non-int type " << type << " found for "
@@ -260,7 +260,7 @@ inline void MapDecoder::checkType_(const std::string& key,
}
inline void MapDecoder::checkType_(const std::string& key,
msgpack::type::object_type type,
- const char& target) const {
+ const char&) const {
if (type != msgpack::type::STR) {
std::cerr << "Warning: Non-string type " << type << " found for "
"entry " << key << std::endl;
@@ -268,7 +268,7 @@ inline void MapDecoder::checkType_(const std::string& key,
}
inline void MapDecoder::checkType_(const std::string& key,
msgpack::type::object_type type,
- const std::string& target) const {
+ const std::string&) const {
if (type != msgpack::type::STR) {
std::cerr << "Warning: Non-string type " << type << " found for "
"entry " << key << std::endl;
@@ -278,7 +278,7 @@ inline void MapDecoder::checkType_(const std::string& key,
template
void MapDecoder::checkType_(const std::string& key,
msgpack::type::object_type type,
- const std::vector& target) const {
+ const std::vector&) const {
if (type != msgpack::type::ARRAY && type != msgpack::type::BIN) {
std::cerr << "Warning: Non-array type " << type << " found for "
"entry " << key << std::endl;
@@ -287,9 +287,9 @@ void MapDecoder::checkType_(const std::string& key,
template
-void MapDecoder::checkType_(const std::string& key,
- msgpack::type::object_type type,
- const T & target) const {
+void MapDecoder::checkType_(const std::string&,
+ msgpack::type::object_type,
+ const T &) const {
// Do nothing -- allow all through
}
diff --git a/include/mmtf/structure_data.hpp b/include/mmtf/structure_data.hpp
index 0d37e32..c16cfe6 100644
--- a/include/mmtf/structure_data.hpp
+++ b/include/mmtf/structure_data.hpp
@@ -163,7 +163,7 @@ struct StructureData {
std::string title;
std::string depositionDate;
std::string releaseDate;
- std::vector > ncsOperatorList;
+ std::vector> ncsOperatorList;
std::vector bioAssemblyList;
std::vector entityList;
std::vector experimentalMethods;
diff --git a/mmtf_spec b/mmtf_spec
deleted file mode 160000
index 8c88834..0000000
--- a/mmtf_spec
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 8c8883457e54fb460908a57d801212c56a603aec
diff --git a/msgpack-c b/msgpack-c
deleted file mode 160000
index 7a98138..0000000
--- a/msgpack-c
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7a98138f27f27290e680bf8fbf1f8d1b089bf138
diff --git a/pybind11 b/pybind11
deleted file mode 160000
index f6c4c10..0000000
--- a/pybind11
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f6c4c1047a293ea77223c21f84f888e062212d78
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..868189f
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,85 @@
+[project]
+name = "mmtf_cppy"
+version = "1.1.1"
+description="A minimal example package (with pybind11)"
+readme = "README.md"
+url = "https://github.com/rcsb/mmtf-cpp"
+authors = [
+ { name = "Danny Farrell", email = "16297104+danpf@users.noreply.github.com" },
+]
+
+requires-python = ">=3.8"
+classifiers = [
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+]
+
+dependencies = [
+ "numpy",
+ "msgpack"
+]
+
+[build-system]
+requires = ["scikit-build-core>=0.3.3", "pybind11"]
+build-backend = "scikit_build_core.build"
+
+[tool.scikit-build]
+wheel.expand-macos-universal-tags = true
+cmake.minimum-version = "3.15"
+cmake.build-type = "Release"
+cmake.source-dir = "."
+wheel.packages = ["src/python/mmtf_cppy"]
+# Uncomment during development
+build-dir = "build/{wheel_tag}"
+
+[tool.scikit-build.cmake.define]
+build_py = "ON"
+MSGPACK_USE_BOOST = "OFF"
+
+
+[tool.cibuildwheel]
+test-command = "python {project}/src/python/tests/tests.py"
+test-skip = ["*universal2:arm64"]
+build-verbosity = 1
+
+
+[tool.ruff]
+src = ["src/python"]
+
+[tool.ruff.lint]
+extend-select = [
+ "B", # flake8-bugbear
+ "I", # isort
+ "ARG", # flake8-unused-arguments
+ "C4", # flake8-comprehensions
+ "EM", # flake8-errmsg
+ "ICN", # flake8-import-conventions
+ "G", # flake8-logging-format
+ "PGH", # pygrep-hooks
+ "PIE", # flake8-pie
+ "PL", # pylint
+ "PT", # flake8-pytest-style
+ "PTH", # flake8-use-pathlib
+ "RET", # flake8-return
+ "RUF", # Ruff-specific
+ "SIM", # flake8-simplify
+ "T20", # flake8-print
+ "UP", # pyupgrade
+ "YTT", # flake8-2020
+ "EXE", # flake8-executable
+ "NPY", # NumPy specific rules
+ "PD", # pandas-vet
+]
+ignore = [
+ "PLR", # Design related pylint codes
+]
+isort.required-imports = ["from __future__ import annotations"]
+
+[tool.ruff.per-file-ignores]
+"tests/**" = ["T20"]
+
diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt
new file mode 100644
index 0000000..83ee2d1
--- /dev/null
+++ b/src/python/CMakeLists.txt
@@ -0,0 +1,21 @@
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+FetchContent_Declare(
+ pybind11
+ GIT_REPOSITORY https://github.com/pybind/pybind11.git
+ GIT_TAG v2.11.1)
+FetchContent_MakeAvailable(pybind11)
+find_package(pybind11 CONFIG REQUIRED)
+
+find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
+
+Python_add_library(mmtf_bindings MODULE bindings.cpp WITH_SOABI)
+
+target_include_directories(mmtf_bindings PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../include
+)
+target_link_libraries(mmtf_bindings PRIVATE pybind11::headers MMTFcpp)
+target_compile_definitions(mmtf_bindings PRIVATE VERSION_INFO=${SKBUILD_PROJECT_VERSION})
+install(TARGETS mmtf_bindings DESTINATION mmtf_cppy)
diff --git a/src/python/bindings.cpp b/src/python/bindings.cpp
new file mode 100644
index 0000000..55914d2
--- /dev/null
+++ b/src/python/bindings.cpp
@@ -0,0 +1,515 @@
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace py = pybind11;
+
+/// CPP -> PY FUNCTIONS
+
+/* Notes
+ * We destory original data because it is much faster to apply move
+ * than it is to copy the data.
+ */
+
+// This destroys the original data
+template< typename T >
+py::array
+array1d_from_vector(std::vector & m) {
+ if (m.empty()) return py::array_t();
+ std::vector* ptr = new std::vector(std::move(m));
+ auto capsule = py::capsule(ptr, [](void* p) {
+ delete reinterpret_cast*>(p);
+ });
+ return py::array_t(
+ ptr->size(), // shape of array
+ ptr->data(), // c-style contiguous strides for Sequence
+ capsule // numpy array references this parent
+ );
+}
+
+
+template<>
+py::array
+array1d_from_vector(std::vector & m) {
+ //if (m.empty()) return py::array_t();
+ std::vector* ptr = new std::vector(std::move(m));
+ auto capsule = py::capsule(ptr, [](void* p) {
+ delete reinterpret_cast*>(p);
+ });
+ return py::array(
+ py::dtype("size()}, // shape of array
+ {},
+ ptr->data(), // c-style contiguous strides for Sequence
+ capsule // numpy array references this parent
+ );
+}
+
+template< >
+py::array
+array1d_from_vector(std::vector & m) {
+ return py::array(py::cast(std::move(m)));
+}
+
+template
+std::vector
+flatten2D(std::vector> const & v) {
+ std::size_t total_size = 0;
+ for (auto const & x : v)
+ total_size += x.size();
+ std::vector result;
+ result.reserve(total_size);
+ for (auto const & subv : v)
+ result.insert(result.end(), subv.begin(), subv.end());
+ return result;
+}
+
+
+// would be nice if this was faster
+template< typename T >
+py::array
+array2D_from_vector(std::vector> const & m) {
+ if (m.empty()) return py::array_t();
+ std::vector* ptr = new std::vector(flatten2D(m));
+ auto capsule = py::capsule(ptr, [](void* p) {
+ delete reinterpret_cast*>(p);
+ });
+ return py::array_t(
+ {m.size(), m.at(0).size()}, // shape of array
+ {m.at(0).size()*sizeof(T), sizeof(T)}, // c-style contiguous strides
+ ptr->data(),
+ capsule);
+}
+
+// This destroys the original data
+py::list
+dump_bio_assembly_list(mmtf::StructureData & sd) {
+ py::object py_ba_class = py::module::import("mmtf_cppy").attr("BioAssembly");
+ py::object py_t_class = py::module::import("mmtf_cppy").attr("Transform");
+ py::list bal;
+ for (mmtf::BioAssembly & cba : sd.bioAssemblyList) {
+ py::list transform_list;
+ for (mmtf::Transform & trans : cba.transformList) {
+ std::vector matrix(std::begin(trans.matrix), std::end(trans.matrix));
+ transform_list.append(
+ py_t_class(
+ array1d_from_vector(trans.chainIndexList),
+ array1d_from_vector(matrix)
+ )
+ );
+ }
+ bal.append(
+ py_ba_class(
+ transform_list,
+ py::str(cba.name)
+ )
+ );
+ }
+ return bal;
+}
+
+// This destroys the original data
+py::list
+dump_entity_list(std::vector & cpp_el) {
+ py::object entity = py::module::import("mmtf_cppy").attr("Entity");
+ py::list el;
+ for (mmtf::Entity & e : cpp_el) {
+ el.append(
+ entity(
+ array1d_from_vector(e.chainIndexList),
+ e.description,
+ e.type,
+ e.sequence)
+ );
+ }
+ return el;
+}
+
+py::bytes
+raw_properties(mmtf::StructureData const & sd) {
+ std::stringstream bytes;
+ std::map< std::string, std::map< std::string, msgpack::object > > objs({
+ {"bondProperties", sd.bondProperties},
+ {"atomProperties", sd.atomProperties},
+ {"groupProperties", sd.groupProperties},
+ {"chainProperties", sd.chainProperties},
+ {"modelProperties", sd.modelProperties},
+ {"extraProperties", sd.extraProperties}});
+ msgpack::pack(bytes, objs);
+ return py::bytes(bytes.str());
+}
+
+
+std::vector
+make_transformList(py::list const & l) {
+ std::vector tl;
+ for (auto const & trans : l) {
+ mmtf::Transform t;
+ t.chainIndexList = trans.attr("chainIndexList").cast>();
+ py::list pymatrix(trans.attr("matrix"));
+ std::size_t count(0);
+ for (auto const & x : pymatrix) {
+ t.matrix[count] = x.cast();
+ ++count;
+ }
+ tl.push_back(t);
+ }
+ return tl;
+}
+
+
+void
+set_bioAssemblyList(py::list const & obj, mmtf::StructureData & sd) {
+ std::vector bioAs;
+ for (auto const & py_bioAssembly : obj ) {
+ mmtf::BioAssembly bioA;
+ bioA.name = py::str(py_bioAssembly.attr("name"));
+ py::list py_transform_list(py_bioAssembly.attr("transformList"));
+ std::vector transform_list = make_transformList(py_transform_list);
+ bioA.transformList = transform_list;
+ bioAs.push_back(bioA);
+ }
+ sd.bioAssemblyList = bioAs;
+}
+
+
+void
+set_entityList(py::list const & obj, mmtf::StructureData & sd) {
+ std::vector entities;
+ for (auto const & py_entity : obj ) {
+ mmtf::Entity entity;
+ entity.chainIndexList = py_entity.attr("chainIndexList").cast>();
+ entity.description = py_entity.attr("description").cast();
+ entity.type = py_entity.attr("type").cast();
+ entity.sequence = py_entity.attr("sequence").cast();
+ entities.push_back(entity);
+ }
+ sd.entityList = entities;
+}
+
+
+void
+set_groupList(py::list const & obj, mmtf::StructureData & sd) {
+ std::vector groups;
+ for (auto const & py_group : obj ) {
+ mmtf::GroupType group;
+ group.formalChargeList = py_group.attr("formalChargeList").cast