├── .coveralls.yml ├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── authors.rst ├── conf.py ├── contributing.rst ├── history.rst ├── index.rst ├── installation.rst ├── make.bat ├── readme.rst └── usage.rst ├── mdapackmol ├── __init__.py ├── mdapackmol.py └── tools.py ├── setup.cfg ├── setup.py └── tests ├── test_mdapackmol.py ├── urea.pdb └── water.pdb /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-pro 2 | repo_token: SfPR7hoMWBwftfaEgFqErPXAiBIElN08Y -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * MDA Packmol version: 2 | * Python version: 3 | * Operating System: 4 | 5 | ### Description 6 | 7 | Describe what you were trying to get done. 8 | Tell us what happened, what went wrong, and what you expected to happen. 9 | 10 | ### What I Did 11 | 12 | ``` 13 | Paste the command(s) you ran and the output. 14 | If there was a crash, please include the traceback here. 15 | ``` 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore python bytecoded files 2 | .cache/ 3 | *egg-info 4 | *.py[c|o] 5 | *.[oa] 6 | *~ 7 | *.bak 8 | *.so 9 | *.egg 10 | \.coverage 11 | build/ 12 | .pytest_cache 13 | docs/_build 14 | .egg* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - CONDA_DEPENDENCIES="mdanalysis packmol" 4 | - CONDA_CHANNELS='biobuilds conda-forge omnia' 5 | 6 | language: python 7 | python: 8 | - 3.6 9 | - 3.5 10 | - 2.7 11 | 12 | install: 13 | - git clone git://github.com/astropy/ci-helpers.git 14 | - source ci-helpers/travis/setup_conda.sh 15 | - pip install -e . 16 | - pip install pytest-cov python-coveralls 17 | 18 | script: 19 | - pytest --cov=mdapackmol/ 20 | 21 | after_success: 22 | - coveralls -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Lead 6 | ---------------- 7 | 8 | * Richard J Gowers 9 | 10 | Contributors 11 | ------------ 12 | 13 | None yet. Why not be the first? 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | An MDAnalysis wrapper around Packmol 5 | Copyright (C) 2018 Richard J Gowers 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | Also add information on how to contact you by electronic and paper mail. 21 | 22 | You should also get your employer (if you work as a programmer) or school, 23 | if any, to sign a "copyright disclaimer" for the program, if necessary. 24 | For more information on this, and how to apply and follow the GNU GPL, see 25 | . 26 | 27 | The GNU General Public License does not permit incorporating your program 28 | into proprietary programs. If your program is a subroutine library, you 29 | may consider it more useful to permit linking proprietary applications with 30 | the library. If this is what you want to do, use the GNU Lesser General 31 | Public License instead of this License. But first, please read 32 | . 33 | 34 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include LICENSE 3 | include README.rst 4 | 5 | recursive-include tests * 6 | recursive-exclude * __pycache__ 7 | recursive-exclude * *.py[co] 8 | 9 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean clean-test clean-pyc clean-build docs help 2 | .DEFAULT_GOAL := help 3 | 4 | define BROWSER_PYSCRIPT 5 | import os, webbrowser, sys 6 | 7 | try: 8 | from urllib import pathname2url 9 | except: 10 | from urllib.request import pathname2url 11 | 12 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 13 | endef 14 | export BROWSER_PYSCRIPT 15 | 16 | define PRINT_HELP_PYSCRIPT 17 | import re, sys 18 | 19 | for line in sys.stdin: 20 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 21 | if match: 22 | target, help = match.groups() 23 | print("%-20s %s" % (target, help)) 24 | endef 25 | export PRINT_HELP_PYSCRIPT 26 | 27 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 28 | 29 | help: 30 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 31 | 32 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts 33 | 34 | clean-build: ## remove build artifacts 35 | rm -fr build/ 36 | rm -fr dist/ 37 | rm -fr .eggs/ 38 | find . -name '*.egg-info' -exec rm -fr {} + 39 | find . -name '*.egg' -exec rm -f {} + 40 | 41 | clean-pyc: ## remove Python file artifacts 42 | find . -name '*.pyc' -exec rm -f {} + 43 | find . -name '*.pyo' -exec rm -f {} + 44 | find . -name '*~' -exec rm -f {} + 45 | find . -name '__pycache__' -exec rm -fr {} + 46 | 47 | clean-test: ## remove test and coverage artifacts 48 | rm -fr .tox/ 49 | rm -f .coverage 50 | rm -fr htmlcov/ 51 | rm -fr .pytest_cache 52 | 53 | lint: ## check style with flake8 54 | flake8 mdapackmol tests 55 | 56 | test: ## run tests quickly with the default Python 57 | py.test 58 | 59 | test-all: ## run tests on every Python version with tox 60 | tox 61 | 62 | coverage: ## check code coverage quickly with the default Python 63 | coverage run --source mdapackmol -m pytest 64 | coverage report -m 65 | coverage html 66 | $(BROWSER) htmlcov/index.html 67 | 68 | docs: ## generate Sphinx HTML documentation, including API docs 69 | rm -f docs/mdapackmol.rst 70 | rm -f docs/modules.rst 71 | sphinx-apidoc -o docs/ mdapackmol 72 | $(MAKE) -C docs clean 73 | $(MAKE) -C docs html 74 | $(BROWSER) docs/_build/html/index.html 75 | 76 | servedocs: docs ## compile the docs watching for changes 77 | watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . 78 | 79 | release: dist ## package and upload a release 80 | twine upload dist/* 81 | 82 | dist: clean ## builds source and wheel package 83 | python setup.py sdist 84 | python setup.py bdist_wheel 85 | ls -l dist 86 | 87 | install: clean ## install the package to the active Python's site-packages 88 | python setup.py install 89 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | MDAPackmol 3 | ========== 4 | 5 | .. |travis| image:: https://travis-ci.com/MDAnalysis/MDAPackmol.svg?branch=master 6 | :target: https://travis-ci.com/MDAnalysis/MDAPackmol 7 | .. |coveralls| image:: https://coveralls.io/repos/github/MDAnalysis/MDAPackmol/badge.svg?branch=master 8 | :target: https://coveralls.io/github/MDAnalysis/MDAPackmol? 9 | 10 | |travis| |coveralls| 11 | 12 | ----- 13 | 14 | An MDAnalysis_ wrapper around Packmol_ 15 | 16 | * Allows combining MDAnalysis and Packmol 17 | 18 | * Preserves the topology information (bonds etc) of your system after Packmol 19 | 20 | * Free software: GNU General Public License v3 21 | 22 | .. _MDAnalysis: https://www.mdanalysis.org 23 | .. _Packmol: http://m3g.iqm.unicamp.br/packmol/home.shtml 24 | 25 | Usage Example 26 | ------------- 27 | 28 | .. code-block:: python 29 | 30 | import MDAnalysis as mda 31 | import mdapackmol 32 | 33 | # load individual molecule files 34 | water = mda.Universe('water.pdb') 35 | urea = mda.Universe('urea.pdb') 36 | 37 | # call Packmol with MDAnalysis objects as arguments 38 | # the 'instructions' allow for any valid Packmol commands 39 | system = mdapackmol.packmol( 40 | [mdapackmol.PackmolStructure( 41 | water, number=1000, 42 | instructions=['inside box 0. 0. 0. 40. 40. 40.']), 43 | mdapackmol.PackmolStructure( 44 | urea, number=400, 45 | instructions=['inside box 0. 0. 0. 40. 40. 40.'])] 46 | ) 47 | 48 | # the returned system is a MDAnalysis Universe 49 | # with all topology information from building blocks retained 50 | # which can then be saved into any format 51 | # eg to Lammps data file: 52 | system.atoms.write('topology.data') 53 | 54 | 55 | Citing 56 | ------ 57 | 58 | If you find mdapackmol useful for you, please cite the following sources: 59 | 60 | * L Martinez, R Andrade, E G Birgin, J M Martinez, "Packmol: A package for building initial configurations for molecular dynamics simulations". Journal of Computational Chemistry, 30, 2157-2164, 2009. 61 | 62 | * R J Gowers, M Linke, J Barnoud, T J E Reddy, M N Melo, S L Seyler, D L Dotson, J Domanski, S Buchoux, I M Kenney, and O Beckstein. "MDAnalysis: A Python package for the rapid analysis of molecular dynamics simulations." In S. Benthall and S. Rostrup, editors, Proceedings of the 15th Python in Science Conference, pages 102-109, Austin, TX, 2016. 63 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = mdapackmol 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # mdapackmol documentation build configuration file, created by 5 | # sphinx-quickstart on Fri Jun 9 13:47:02 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another 17 | # directory, add these directories to sys.path here. If the directory is 18 | # relative to the documentation root, use os.path.abspath to make it 19 | # absolute, like shown here. 20 | # 21 | import os 22 | import sys 23 | sys.path.insert(0, os.path.abspath('..')) 24 | 25 | import mdapackmol 26 | 27 | # -- General configuration --------------------------------------------- 28 | 29 | # If your documentation needs a minimal Sphinx version, state it here. 30 | # 31 | # needs_sphinx = '1.0' 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 35 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix(es) of source filenames. 41 | # You can specify multiple suffix as a list of string: 42 | # 43 | # source_suffix = ['.rst', '.md'] 44 | source_suffix = '.rst' 45 | 46 | # The master toctree document. 47 | master_doc = 'index' 48 | 49 | # General information about the project. 50 | project = u'MDA Packmol' 51 | copyright = u"2018, Richard J Gowers" 52 | author = u"Richard J Gowers" 53 | 54 | # The version info for the project you're documenting, acts as replacement 55 | # for |version| and |release|, also used in various other places throughout 56 | # the built documents. 57 | # 58 | # The short X.Y version. 59 | version = mdapackmol.__version__ 60 | # The full version, including alpha/beta/rc tags. 61 | release = mdapackmol.__version__ 62 | 63 | # The language for content autogenerated by Sphinx. Refer to documentation 64 | # for a list of supported languages. 65 | # 66 | # This is also used if you do content translation via gettext catalogs. 67 | # Usually you set "language" from the command line for these cases. 68 | language = None 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | # This patterns also effect to html_static_path and html_extra_path 73 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 74 | 75 | # The name of the Pygments (syntax highlighting) style to use. 76 | pygments_style = 'sphinx' 77 | 78 | # If true, `todo` and `todoList` produce output, else they produce nothing. 79 | todo_include_todos = False 80 | 81 | 82 | # -- Options for HTML output ------------------------------------------- 83 | 84 | # The theme to use for HTML and HTML Help pages. See the documentation for 85 | # a list of builtin themes. 86 | # 87 | html_theme = 'alabaster' 88 | 89 | # Theme options are theme-specific and customize the look and feel of a 90 | # theme further. For a list of options available for each theme, see the 91 | # documentation. 92 | # 93 | # html_theme_options = {} 94 | 95 | # Add any paths that contain custom static files (such as style sheets) here, 96 | # relative to this directory. They are copied after the builtin static files, 97 | # so a file named "default.css" will overwrite the builtin "default.css". 98 | html_static_path = ['_static'] 99 | 100 | 101 | # -- Options for HTMLHelp output --------------------------------------- 102 | 103 | # Output file base name for HTML help builder. 104 | htmlhelp_basename = 'mdapackmoldoc' 105 | 106 | 107 | # -- Options for LaTeX output ------------------------------------------ 108 | 109 | latex_elements = { 110 | # The paper size ('letterpaper' or 'a4paper'). 111 | # 112 | # 'papersize': 'letterpaper', 113 | 114 | # The font size ('10pt', '11pt' or '12pt'). 115 | # 116 | # 'pointsize': '10pt', 117 | 118 | # Additional stuff for the LaTeX preamble. 119 | # 120 | # 'preamble': '', 121 | 122 | # Latex figure (float) alignment 123 | # 124 | # 'figure_align': 'htbp', 125 | } 126 | 127 | # Grouping the document tree into LaTeX files. List of tuples 128 | # (source start file, target name, title, author, documentclass 129 | # [howto, manual, or own class]). 130 | latex_documents = [ 131 | (master_doc, 'mdapackmol.tex', 132 | u'MDA Packmol Documentation', 133 | u'Richard J Gowers', 'manual'), 134 | ] 135 | 136 | 137 | # -- Options for manual page output ------------------------------------ 138 | 139 | # One entry per manual page. List of tuples 140 | # (source start file, name, description, authors, manual section). 141 | man_pages = [ 142 | (master_doc, 'mdapackmol', 143 | u'MDA Packmol Documentation', 144 | [author], 1) 145 | ] 146 | 147 | 148 | # -- Options for Texinfo output ---------------------------------------- 149 | 150 | # Grouping the document tree into Texinfo files. List of tuples 151 | # (source start file, target name, title, author, 152 | # dir menu entry, description, category) 153 | texinfo_documents = [ 154 | (master_doc, 'mdapackmol', 155 | u'MDA Packmol Documentation', 156 | author, 157 | 'mdapackmol', 158 | 'One line description of project.', 159 | 'Miscellaneous'), 160 | ] 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to MDA Packmol's documentation! 2 | ====================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | readme 9 | installation 10 | usage 11 | modules 12 | contributing 13 | authors 14 | history 15 | 16 | Indices and tables 17 | ================== 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | 8 | Stable release 9 | -------------- 10 | 11 | To install MDA Packmol, run this command in your terminal: 12 | 13 | .. code-block:: console 14 | 15 | $ pip install mdapackmol 16 | 17 | This is the preferred method to install MDA Packmol, as it will always install the most recent stable release. 18 | 19 | If you don't have `pip`_ installed, this `Python installation guide`_ can guide 20 | you through the process. 21 | 22 | .. _pip: https://pip.pypa.io 23 | .. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ 24 | 25 | 26 | From sources 27 | ------------ 28 | 29 | The sources for MDA Packmol can be downloaded from the `Github repo`_. 30 | 31 | You can either clone the public repository: 32 | 33 | .. code-block:: console 34 | 35 | $ git clone git://github.com/richardjgowers/mdapackmol 36 | 37 | Or download the `tarball`_: 38 | 39 | .. code-block:: console 40 | 41 | $ curl -OL https://github.com/richardjgowers/mdapackmol/tarball/master 42 | 43 | Once you have a copy of the source, you can install it with: 44 | 45 | .. code-block:: console 46 | 47 | $ python setup.py install 48 | 49 | 50 | .. _Github repo: https://github.com/richardjgowers/mdapackmol 51 | .. _tarball: https://github.com/richardjgowers/mdapackmol/tarball/master 52 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=mdapackmol 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | To use MDA Packmol in a project:: 6 | 7 | import mdapackmol 8 | -------------------------------------------------------------------------------- /mdapackmol/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Top-level package for MDA Packmol.""" 4 | 5 | __author__ = """Richard J Gowers""" 6 | __email__ = 'richardjgowers@gmail.com' 7 | __version__ = '0.1.0' 8 | 9 | from . import mdapackmol 10 | from . import tools 11 | from .mdapackmol import packmol, PackmolStructure 12 | -------------------------------------------------------------------------------- /mdapackmol/mdapackmol.py: -------------------------------------------------------------------------------- 1 | import MDAnalysis as mda 2 | import os 3 | import sys 4 | if (sys.version_info[0] < 3) or (sys.version_info[1] <= 4): 5 | import subprocess32 as subprocess 6 | else: 7 | import subprocess 8 | import warnings 9 | 10 | 11 | PACKMOL_INP = 'packmol.inp' # name of .inp file given to packmol 12 | PACKMOL_STRUCTURE_FILES = 'MDAPMinput{}.pdb' 13 | PACKMOL_OUT = 'output.pdb' 14 | 15 | 16 | class PackmolError(Exception): 17 | # if packmol didn't find solution 18 | pass 19 | 20 | class PackmolStructure(object): 21 | """A Molecule to add to the Packmol system 22 | 23 | Parameters 24 | ---------- 25 | ag : MDAnalysis.AtomGroup 26 | a single template molecule for Packmol to use 27 | number : int 28 | quantity of this molecule to add to new system 29 | instructions : list of strings 30 | list of instructions to Packmol for this molecule 31 | eg 'inside box 0. 0. 0. 40. 40. 40.' 32 | each item in the list should be a single line instruction 33 | """ 34 | def __init__(self, ag, number, instructions): 35 | self.ag = ag 36 | self.number = number 37 | self.instructions = instructions 38 | 39 | def to_packmol_inp(self, index): 40 | """Create portion of packmol.inp file from this molecule 41 | 42 | Parameters 43 | ---------- 44 | index : int 45 | the index of this template molecule within entire system 46 | 47 | Returns 48 | ------- 49 | output : str 50 | text to write to Packmol input for this molecule 51 | """ 52 | output = 'structure {}\n'.format(PACKMOL_STRUCTURE_FILES.format(index)) 53 | output += ' number {}\n'.format(self.number) 54 | for instruction in self.instructions: 55 | output += ' ' + instruction + '\n' 56 | output += 'end structure\n\n' 57 | 58 | return output 59 | 60 | def save_structure(self, index): 61 | """Save this molecule for Packmol to use 62 | 63 | Parameters 64 | ---------- 65 | index : int 66 | the index of this template molecule within entire system 67 | """ 68 | # we mangle Resnames to keep track of which molecule is which 69 | # so copy the true names, change, write out, change back to real 70 | old_resnames = self.ag.residues.resnames.copy() 71 | self.ag.residues.resnames = 'R{}'.format(index) 72 | with mda.Writer(PACKMOL_STRUCTURE_FILES.format(index)) as w: 73 | w.write(self.ag) 74 | self.ag.residues.resnames = old_resnames 75 | 76 | 77 | def make_packmol_input(structures, tolerance=None): 78 | """Convert the call into a Packmol usable input file 79 | 80 | Parameters 81 | ---------- 82 | structures : list 83 | list of PackmolStructure objects 84 | tolerance : float, optional 85 | minimum distance between molecules, defaults to 2.0 86 | """ 87 | # Check if all structures are suitable, fix them if needed 88 | for s in structures: 89 | if not hasattr(s.ag, 'resnames'): 90 | s.ag.universe.add_TopologyAttr('resnames') 91 | 92 | if tolerance is None: 93 | tolerance = 2.0 94 | 95 | with open(PACKMOL_INP, 'w') as out: 96 | out.write("# autogenerated packmol input\n\n") 97 | 98 | out.write('tolerance {}\n\n'.format(tolerance)) 99 | out.write('filetype pdb\n\n') 100 | 101 | for i, structure in enumerate(structures): 102 | out.write(structure.to_packmol_inp(i)) 103 | structure.save_structure(i) 104 | 105 | out.write('output {}\n\n'.format(PACKMOL_OUT)) 106 | 107 | 108 | def run_packmol(): 109 | """Run and check that Packmol worked correctly""" 110 | try: 111 | p = subprocess.run('packmol < {}'.format(PACKMOL_INP), 112 | check=True, 113 | shell=True, 114 | stdout=subprocess.PIPE, 115 | stderr=subprocess.PIPE) 116 | except subprocess.CalledProcessError as e: 117 | raise ValueError("Packmol failed with errorcode {}" 118 | " and stderr: {}".format(e.returncode, e.stderr)) 119 | else: 120 | with open('packmol.stdout', 'w') as out: 121 | out.write(p.stdout.decode()) 122 | 123 | 124 | def load_packmol_output(): 125 | """Parse the output of Packmol""" 126 | return mda.Universe(PACKMOL_OUT, transfer_to_memory=True) 127 | 128 | 129 | def clean_tempfiles(structures): 130 | """Delete files generated by MDAPackmol""" 131 | structure_files = [PACKMOL_STRUCTURE_FILES.format(i) for i in range(len(structures))] 132 | 133 | for f in [PACKMOL_INP, PACKMOL_OUT] + structure_files: 134 | try: 135 | os.remove(f) 136 | except FileNotFoundError: 137 | pass 138 | 139 | def reassign_topology(structures, new): 140 | """Take Packmol created Universe and add old topology features back in 141 | 142 | Attempts to reassign: 143 | - types 144 | - names 145 | - charges 146 | - masses 147 | - bonds 148 | - angles 149 | - torsions 150 | - impropers 151 | - resnames 152 | 153 | Parameters 154 | ---------- 155 | structures : list 156 | list of Packmol structures used to create the Packmol Universe 157 | new : Universe 158 | the raw output of Packmol 159 | 160 | Returns 161 | ------- 162 | new : Universe 163 | the raw output modified to best match the templates given to it 164 | """ 165 | index = 0 166 | 167 | bonds = [] 168 | angles = [] 169 | dihedrals = [] 170 | impropers = [] 171 | 172 | # add required attributes 173 | for attr in ['types', 'names', 'charges', 'masses']: 174 | if any(hasattr(pms.ag, attr) for pms in structures): 175 | new.add_TopologyAttr(attr) 176 | 177 | if not all(hasattr(pms.ag, attr) for pms in structures): 178 | warnings.warn("added attribute which not all templates had") 179 | 180 | while index < len(new.atoms): 181 | # first atom we haven't dealt with yet 182 | start = new.atoms[index] 183 | # the resname was altered to give a hint to what template it was from 184 | template = structures[int(start.resname[1:])].ag 185 | # grab atomgroup which matches template 186 | to_change = new.atoms[index:index + len(template.atoms)] 187 | 188 | # Update residue names 189 | nres = len(template.residues) 190 | new.residues[start.resindex:start.resindex + nres].resnames = template.residues.resnames 191 | 192 | # atom attributes 193 | for attr in ['types', 'names', 'charges', 'masses']: 194 | if hasattr(template.atoms, attr): 195 | setattr(to_change, attr, getattr(template.atoms, attr)) 196 | 197 | # bonds/angles/torsions 198 | if hasattr(template, 'bonds'): 199 | bonds.extend((template.bonds.to_indices() + index).tolist()) 200 | if hasattr(template, 'angles'): 201 | angles.extend((template.angles.to_indices() + index).tolist()) 202 | if hasattr(template, 'dihedrals'): 203 | dihedrals.extend((template.dihedrals.to_indices() + index).tolist()) 204 | if hasattr(template, 'impropers'): 205 | impropers.extend((template.impropers.to_indices() + index).tolist()) 206 | 207 | # update the index pointer to be on next unknown atom 208 | index += len(template.atoms) 209 | 210 | if bonds: 211 | # convert to tuples for hashability 212 | bonds = [tuple(val) for val in bonds] 213 | new.add_TopologyAttr('bonds', values=bonds) 214 | if angles: 215 | angles = [tuple(val) for val in angles] 216 | new.add_TopologyAttr('angles', values=angles) 217 | if dihedrals: 218 | dihedrals = [tuple(val) for val in dihedrals] 219 | new.add_TopologyAttr('dihedrals', values=dihedrals) 220 | if impropers: 221 | impropers = [tuple(val) for val in impropers] 222 | new.add_TopologyAttr('impropers', values=impropers) 223 | 224 | return new 225 | 226 | 227 | def packmol(structures, tolerance=None): 228 | """"Take molecules and settings and create a larger system 229 | 230 | Parameters 231 | ---------- 232 | structures : list 233 | list of PackmolStruture objects 234 | tolerance : float, optional 235 | Packmol tolerance, defaults to 2.0 236 | 237 | Returns 238 | ------- 239 | new : MDAnalysis.Universe 240 | Universe object of the system created by Packmol 241 | """ 242 | try: 243 | make_packmol_input(structures, tolerance=tolerance) 244 | 245 | run_packmol() 246 | except PackmolError: 247 | # todo: Deal with error 248 | new = None 249 | else: 250 | new = load_packmol_output() 251 | reassign_topology(structures, new) 252 | finally: 253 | clean_tempfiles(structures) 254 | 255 | return new 256 | -------------------------------------------------------------------------------- /mdapackmol/tools.py: -------------------------------------------------------------------------------- 1 | """Cool stuff which is maybe useful""" 2 | 3 | from scipy.constants import N_A 4 | 5 | def molecules_for_target_density( 6 | existing_molecules, 7 | solvent_molecule, 8 | target_density, 9 | boxsize): 10 | """Calculate how many solvent molecules to add to reach target density 11 | 12 | Parameters 13 | ---------- 14 | existing_molecules : dict 15 | mapping of AtomGroups to number in box 16 | solvent_molecule : AtomGroup 17 | solvent molecule you want to add 18 | target_density : float 19 | target system density in kg/m3 20 | boxsize : 3 floats 21 | boxsize in each dimension in Angstrom 22 | 23 | Returns 24 | ------- 25 | nsolvent, density 26 | number of solvent molecules to add, resulting density 27 | 28 | Example 29 | ------- 30 | To find how many water molecules to solvate our protein to a density of 31 | 985 kg/m^3. We load AtomGroups "protein" and "water" (making sure that 32 | the mass is correct for these). We specify that there will be 1 protein 33 | molecule in the box, and the solvent is the water AtomGroup. We then 34 | pass the density and size of our box (20x20x20 is this example):: 35 | 36 | >>> molecules_for_target_density({protein: 1}, water, 37 | 985.0, [20.0, 20.0, 20.0]) 38 | """ 39 | # system volume 40 | vol = boxsize[0] * boxsize[1] * boxsize[2] * 10 ** -30 41 | 42 | target_mass = target_density * vol # kg 43 | 44 | existing_mass = sum(mol.total_mass() * quantity 45 | for mol, quantity in existing_molecules.items()) 46 | # from g/mol to kg 47 | existing_mass /= 1000 * N_A 48 | 49 | required_mass = target_mass - existing_mass 50 | 51 | solvent_mass = solvent_molecule.total_mass() / (1000 * N_A) 52 | 53 | nreq = int(required_mass / solvent_mass) 54 | # calculate resulting density as a check 55 | actual_density = (existing_mass + nreq * solvent_mass) / vol 56 | 57 | return nreq, actual_density 58 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bumpversion:file:mdapackmol/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | 14 | [bdist_wheel] 15 | universal = 1 16 | 17 | [flake8] 18 | exclude = docs 19 | 20 | [aliases] 21 | # Define setup.py command aliases here 22 | test = pytest 23 | 24 | [tool:pytest] 25 | collect_ignore = ['setup.py'] 26 | 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """The setup script.""" 5 | 6 | from setuptools import setup, find_packages 7 | 8 | with open('README.rst') as readme_file: 9 | readme = readme_file.read() 10 | 11 | requirements = ['mdanalysis' ] 12 | 13 | setup_requirements = ['pytest-runner', ] 14 | 15 | test_requirements = ['pytest', 'pytest-cov', 'coveralls-python'] 16 | 17 | setup( 18 | author="Richard J Gowers", 19 | author_email='richardjgowers@gmail.com', 20 | classifiers=[ 21 | 'Development Status :: 2 - Pre-Alpha', 22 | 'Intended Audience :: Developers', 23 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 24 | 'Natural Language :: English', 25 | "Programming Language :: Python :: 2", 26 | 'Programming Language :: Python :: 2.7', 27 | 'Programming Language :: Python :: 3', 28 | 'Programming Language :: Python :: 3.5', 29 | 'Programming Language :: Python :: 3.6', 30 | ], 31 | description="An MDAnalysis wrapper around Packmol", 32 | install_requires=requirements, 33 | license="GNU General Public License v3", 34 | long_description=readme + '\n\n', 35 | include_package_data=True, 36 | keywords='mdapackmol', 37 | name='mdapackmol', 38 | packages=find_packages(include=['mdapackmol']), 39 | setup_requires=setup_requirements, 40 | test_suite='tests', 41 | tests_require=test_requirements, 42 | url='https://github.com/richardjgowers/mdapackmol', 43 | version='0.1.0', 44 | zip_safe=False, 45 | ) 46 | -------------------------------------------------------------------------------- /tests/test_mdapackmol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Tests for `mdapackmol` package.""" 5 | import os 6 | import pytest 7 | import MDAnalysis as mda 8 | 9 | import mdapackmol 10 | 11 | 12 | @pytest.fixture() 13 | def in_tmpdir(tmpdir): 14 | os.chdir(str(tmpdir)) 15 | 16 | yield str(tmpdir) 17 | 18 | 19 | HERE = os.path.abspath(os.path.dirname(__file__)) 20 | WATER_PDB = os.path.join(HERE, 'water.pdb') 21 | UREA_PDB = os.path.join(HERE, 'urea.pdb') 22 | 23 | @pytest.fixture() 24 | def water(): 25 | return mda.Universe(WATER_PDB) 26 | 27 | 28 | @pytest.fixture() 29 | def urea(): 30 | return mda.Universe(UREA_PDB) 31 | 32 | 33 | class TestMixture(object): 34 | @staticmethod 35 | @pytest.fixture() 36 | def mixture(in_tmpdir, water, urea): 37 | # PS(ag, number, instructions) 38 | mixture = mdapackmol.packmol( 39 | [mdapackmol.PackmolStructure( 40 | water, 41 | number=1000, 42 | instructions=['inside box 0. 0. 0. 40. 40. 40.'], 43 | ), 44 | mdapackmol.PackmolStructure( 45 | urea, 46 | number=400, 47 | instructions=['inside box 0. 0. 0. 40. 40. 40.'], 48 | ), 49 | ] 50 | ) 51 | 52 | return mixture 53 | 54 | def test_mixture_size(self, mixture, water, urea): 55 | assert len(mixture.atoms) == 1000 * len(water.atoms) + 400 * len(urea.atoms) 56 | 57 | def test_residue_size(self, mixture, water, urea): 58 | for res in mixture.residues[:3]: 59 | assert len(res.atoms) == len(water.atoms) 60 | 61 | for res in mixture.residues[1000:1003]: 62 | assert len(res.atoms) == len(urea.atoms) 63 | 64 | def test__resnames(self, mixture, water, urea): 65 | for res in mixture.residues[:3]: 66 | for atom_a, atom_b in zip(res.atoms, water.atoms): 67 | assert atom_a.resname == atom_b.resname 68 | 69 | for res in mixture.residues[1000:1003]: 70 | for atom_a, atom_b in zip(res.atoms, urea.atoms): 71 | assert atom_a.resname == atom_b.resname 72 | 73 | def test_bonds(urea, water): 74 | urea.atoms.guess_bonds() 75 | water.atoms.guess_bonds() 76 | 77 | mixture = mdapackmol.packmol( 78 | [mdapackmol.PackmolStructure( 79 | water, 80 | number=50, 81 | instructions=['inside box 0. 0. 0. 40. 40. 40.'], 82 | ), 83 | mdapackmol.PackmolStructure( 84 | urea, 85 | number=50, 86 | instructions=['inside box 0. 0. 0. 40. 40. 40.'], 87 | ), 88 | ] 89 | ) 90 | 91 | assert hasattr(mixture, 'bonds') 92 | assert len(mixture.bonds) == len(water.bonds) * 50 + len(urea.bonds) * 50 93 | 94 | 95 | def test_cleanup(urea, water): 96 | mixture = mdapackmol.packmol( 97 | [mdapackmol.PackmolStructure( 98 | water, 99 | number=2, 100 | instructions=['inside box 0. 0. 0. 40. 40. 40.'], 101 | ), 102 | mdapackmol.PackmolStructure( 103 | urea, 104 | number=2, 105 | instructions=['inside box 0. 0. 0. 40. 40. 40.'], 106 | ), 107 | ] 108 | ) 109 | 110 | assert not os.path.exists(mdapackmol.mdapackmol.PACKMOL_INP) 111 | assert not os.path.exists('output.pdb') 112 | -------------------------------------------------------------------------------- /tests/urea.pdb: -------------------------------------------------------------------------------- 1 | HEADER 2 | HETATM 1 C 2 2 0.000 0.000 1.400 3 | HETATM 2 N 2 2 -1.256 0.000 2.125 4 | HETATM 3 O 2 2 0.000 0.000 0.000 5 | HETATM 4 N 2 2 1.256 0.000 2.125 6 | HETATM 5 H 2 2 1.072 0.000 3.116 7 | HETATM 6 H 2 2 2.252 0.000 1.968 8 | HETATM 7 H 2 2 -1.072 0.000 3.116 9 | HETATM 8 H 2 2 -2.252 0.000 1.968 10 | -------------------------------------------------------------------------------- /tests/water.pdb: -------------------------------------------------------------------------------- 1 | HEADER water 2 | COMPND 3 | SOURCE 4 | HETATM 1 H HOH 1 9.626 6.787 12.673 5 | HETATM 2 H HOH 1 9.626 8.420 12.673 6 | HETATM 3 O HOH 1 10.203 7.604 12.673 7 | CONECT 1 3 8 | CONECT 2 3 9 | CONECT 3 1 2 10 | END 11 | --------------------------------------------------------------------------------