├── .codacy.yml
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── pull_request_template.md
└── workflows
│ ├── lint.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── ADMIN.rst
├── CHANGES.rst
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.rst
├── README.md
├── docs
├── Makefile
├── logo_mdgo.svg
├── make.bat
├── mdgo-white.svg
└── source
│ ├── _static
│ ├── logo_mdgo.png
│ └── mdgo-white.png
│ ├── aqueous.rst
│ ├── change_log.rst
│ ├── conf.py
│ ├── index.rst
│ ├── introduction.rst
│ ├── latest_changes.rst
│ ├── mdgo.conductivity.rst
│ ├── mdgo.coordination.rst
│ ├── mdgo.core.rst
│ ├── mdgo.forcefield.rst
│ ├── mdgo.mdgopackmol.rst
│ ├── mdgo.msd.rst
│ ├── mdgo.residence_time.rst
│ ├── mdgo.rst
│ ├── mdgo.util.rst
│ ├── mdgo.volume.rst
│ ├── modules.rst
│ └── rtd_requirements.txt
├── mdgo
├── __init__.py
├── conductivity.py
├── coordination.py
├── core
│ ├── __init__.py
│ ├── analysis.py
│ └── run.py
├── forcefield
│ ├── __init__.py
│ ├── aqueous.py
│ ├── charge.py
│ ├── crawler.py
│ ├── data
│ │ ├── ion_lj_params.json
│ │ └── water
│ │ │ ├── water_opc.lmp
│ │ │ ├── water_opc3.lmp
│ │ │ ├── water_spc.lmp
│ │ │ ├── water_spce.lmp
│ │ │ ├── water_tip3p_ew.lmp
│ │ │ ├── water_tip3p_fb.lmp
│ │ │ ├── water_tip4p_2005.lmp
│ │ │ ├── water_tip4p_ew.lmp
│ │ │ └── water_tip4p_fb.lmp
│ ├── maestro.py
│ └── pubchem.py
├── msd.py
├── residence_time.py
├── templates
│ ├── mae_cmd_assignbond.txt
│ └── mae_cmd_noassignbond.txt
└── util
│ ├── __init__.py
│ ├── coord.py
│ ├── dict_utils.py
│ ├── num.py
│ ├── packmol.py
│ ├── reformat.py
│ └── volume.py
├── pylintrc
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── setup.cfg
├── setup.py
├── tasks.py
└── tests
├── __init__.py
├── packmol
├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
└── packmol
├── test_conductivity.py
├── test_coordination.py
├── test_core.py
├── test_files
├── C.lmp
├── CCOC(=O)OC.lmp
├── DEC.xyz
├── EC.xyz
├── EMC.gro
├── EMC.itp
├── EMC.lmp
├── EMC.lmp.xyz
├── EMC.pdb
├── EMC.xyz
├── LiPF6.xyz
├── LiTFSi.xyz
├── PF6.xyz
├── TFSI.xyz
├── gen2_light
│ ├── gen2_mdgo.data
│ └── gen2_mdgo_unwrapped_nvt_main.dcd
└── subdir with spaces
│ └── EMC.xyz
├── test_forcefield.py
├── test_mdgopackmol.py
├── test_msd.py
├── test_residence_time.py
├── test_util.py
└── test_volume.py
/.codacy.yml:
--------------------------------------------------------------------------------
1 | ---
2 | exclude_paths:
3 | - tests/**
4 | - docs*/**
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Version [e.g. 22]
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[Feature request]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Summary
2 |
3 | Include a summary of major changes in bullet points:
4 |
5 | * Feature 1
6 | * Feature 2
7 | * Fix 1
8 | * Fix 2
9 |
10 | ## Additional dependencies introduced (if any)
11 |
12 | * List all new dependencies needed and justify why. While adding dependencies that bring
13 | significantly useful functionality is perfectly fine, adding ones that
14 | add trivial functionality, e.g., to use one single easily implementable
15 | function, is frowned upon. Provide a justification why that dependency is needed.
16 | Especially frowned upon are circular dependencies, e.g., depending on derivative
17 | modules of pymatgen such as custodian or Fireworks.
18 |
19 | ## TODO (if any)
20 |
21 | If this is a work-in-progress, write something about what else needs
22 | to be done
23 |
24 | * Feature 1 supports A, but not B.
25 |
26 | ## Checklist
27 |
28 | Work-in-progress pull requests are encouraged. Please put [WIP]
29 | in the pull request title.
30 |
31 | Before a pull request can be merged, the following items must be checked:
32 |
33 | - [ ] Code is in the [standard Python style](https://www.python.org/dev/peps/pep-0008/). The easiest way to handle this
34 | is to run the following in the **correct sequence** on your local machine. Start with running
35 | [black](https://black.readthedocs.io/en/stable/index.html) on your new code. This will automatically reformat
36 | your code to PEP8 conventions and removes most issues. Then run
37 | [pycodestyle](https://pycodestyle.readthedocs.io/en/latest/), followed by
38 | [flake8](http://flake8.pycqa.org/en/latest/).
39 | - [ ] Docstrings have been added in the [Google docstring format](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html).
40 | Run [pydocstyle](http://www.pydocstyle.org/en/2.1.1/index.html) on your code.
41 | - [ ] Type annotations are **highly** encouraged. Run [mypy](http://mypy-lang.org/) to type check your code.
42 | - [ ] Tests have been added for any new functionality or bug fixes.
43 | - [ ] All linting and tests pass.
44 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | # Trigger the workflow on push or pull request,
5 | # but only for the main branch
6 | push:
7 | branches:
8 | - main
9 | pull_request:
10 | branches:
11 | - main
12 |
13 | jobs:
14 | run-linters:
15 | name: Run linters
16 | runs-on: ubuntu-latest
17 | strategy:
18 | max-parallel: 1
19 | matrix:
20 | python-version: ['3.10']
21 | steps:
22 | - name: Check out Git repository
23 | uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 | - name: Set up Python ${{ matrix.python-version }}
27 | uses: actions/setup-python@v5
28 | with:
29 | python-version: ${{ matrix.python-version }}
30 | - name: Install dependencies
31 | run: |
32 | pip install --upgrade ruff mypy
33 |
34 | - name: ruff
35 | run: |
36 | ruff --version
37 | ruff .
38 | ruff format --check .
39 |
40 | - name: mypy
41 | run: |
42 | mypy --version
43 | rm -rf .mypy_cache
44 | mypy ${{ github.event.repository.name }}
45 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | release:
5 | types: [ created ]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | test:
10 |
11 | strategy:
12 | max-parallel: 20
13 | matrix:
14 | os: [ macos-latest ]
15 | python-version: [ 3.8, 3.9 ]
16 | # This distribution of tests is designed to ensure an approximately even time to finish for parallel jobs.
17 | pkg:
18 | - tests
19 |
20 | runs-on: ${{ matrix.os }}
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Set up Python ${{ matrix.python-version }}
25 | uses: actions/setup-python@v2
26 | with:
27 | python-version: ${{ matrix.python-version }}
28 | - uses: actions/cache@v2
29 | if: startsWith(runner.os, 'macOS')
30 | with:
31 | path: ~/Library/Caches/pip
32 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
33 | restore-keys: |
34 | ${{ runner.os }}-pip-
35 | - uses: actions/cache@v2
36 | if: startsWith(runner.os, 'Windows')
37 | with:
38 | path: ~\AppData\Local\pip\Cache
39 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
40 | restore-keys: |
41 | ${{ runner.os }}-pip-
42 | - name: Install dependencies
43 | run: |
44 | pip install --upgrade pip wheel
45 | pip install -r requirements.txt
46 | pip install -r requirements-dev.txt
47 | pip install -e .
48 | - name: Prepare Packmol
49 | run: |
50 | echo $HOME
51 | echo "$HOME/work/mdgo/mdgo/tests/packmol" >> $GITHUB_PATH
52 | - name: Prepare Selenium
53 | # https://github.com/marketplace/actions/setup-chromedriver
54 | uses: nanasess/setup-chromedriver@master
55 | - name: Start XVFB
56 | run: |
57 | export DISPLAY=:99
58 | chromedriver --url-base=/wd/hub &
59 | sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
60 | - name: pytest ${{ matrix.pkg }}
61 | run: |
62 | pytest ${{ matrix.pkg }}
63 |
64 | release:
65 | needs: test
66 | strategy:
67 | max-parallel: 2
68 | matrix:
69 | os: [ macos-latest ]
70 | python-version: [ 3.8 ]
71 | runs-on: ${{ matrix.os }}
72 |
73 | steps:
74 | - uses: actions/checkout@v2
75 | - name: Set up Python ${{ matrix.python-version }}
76 | uses: actions/setup-python@v2
77 | with:
78 | python-version: ${{ matrix.python-version }}
79 | - uses: actions/cache@v2
80 | if: startsWith(runner.os, 'macOS')
81 | with:
82 | path: ~/Library/Caches/pip
83 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
84 | restore-keys: |
85 | ${{ runner.os }}-pip-
86 | - name: Install dependencies
87 | run: |
88 | pip install --upgrade pip wheel
89 | pip install -r requirements.txt
90 | pip install -e .
91 | - name: Release
92 | env:
93 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
94 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
95 | run: |
96 | pip install setuptools wheel twine
97 | python setup.py sdist bdist_wheel
98 | twine upload dist/*.whl
99 | twine upload --skip-existing dist/*.tar.gz
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | # Trigger the workflow on push or pull request,
5 | # but only for the main branch
6 | push:
7 | branches:
8 | - main
9 | pull_request:
10 | branches:
11 | - main
12 |
13 | jobs:
14 | test:
15 |
16 | strategy:
17 | max-parallel: 20
18 | matrix:
19 | os: [ macos-latest ]
20 | python-version: [ '3.9', '3.10' ]
21 | # This distribution of tests is designed to ensure an approximately even time to finish for parallel jobs.
22 | pkg:
23 | - tests --ignore=tests/test_mdgopackmol.py
24 |
25 | runs-on: ${{ matrix.os }}
26 |
27 | steps:
28 | - uses: actions/checkout@v4
29 | - name: Set up Python ${{ matrix.python-version }}
30 | uses: actions/setup-python@v5
31 | with:
32 | python-version: ${{ matrix.python-version }}
33 | - uses: actions/cache@v4
34 | if: startsWith(runner.os, 'macOS')
35 | with:
36 | path: ~/Library/Caches/pip
37 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
38 | restore-keys: |
39 | ${{ runner.os }}-pip-
40 | - uses: actions/cache@v4
41 | if: startsWith(runner.os, 'Windows')
42 | with:
43 | path: ~\AppData\Local\pip\Cache
44 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
45 | restore-keys: |
46 | ${{ runner.os }}-pip-
47 | - name: Install dependencies
48 | run: |
49 | pip install --upgrade pip wheel
50 | pip install -r requirements.txt
51 | pip install -r requirements-dev.txt
52 | pip install -e .
53 | - name: Prepare Packmol
54 | run: |
55 | echo $HOME
56 | echo "$HOME/work/mdgo/mdgo/tests/packmol" >> $GITHUB_PATH
57 | - name: Prepare Selenium
58 | # https://github.com/marketplace/actions/setup-chromedriver
59 | uses: nanasess/setup-chromedriver@master
60 | - name: Start XVFB
61 | run: |
62 | export DISPLAY=:99
63 | chromedriver --url-base=/wd/hub &
64 | sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
65 | - name: pytest ${{ matrix.pkg }}
66 | run: |
67 | pytest ${{ matrix.pkg }}
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # drafted examples
10 | mdgo_example/
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | share/python-wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 | MANIFEST
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .nox/
46 | .coverage
47 | .coverage.*
48 | .cache
49 | nosetests.xml
50 | coverage.xml
51 | *.cover
52 | *.py,cover
53 | .hypothesis/
54 | .pytest_cache/
55 | cover/
56 |
57 | # Translations
58 | *.mo
59 | *.pot
60 |
61 | # Django stuff:
62 | *.log
63 | local_settings.py
64 | db.sqlite3
65 | db.sqlite3-journal
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | .pybuilder/
79 | target/
80 |
81 | # Jupyter Notebook
82 | *.ipynb
83 | .ipynb_checkpoints
84 |
85 | # IPython
86 | profile_default/
87 | ipython_config.py
88 |
89 | # pyenv
90 | # For a library or package, you might want to ignore these files since the code is
91 | # intended to run in multiple environments; otherwise, check them in:
92 | # .python-version
93 |
94 | # pipenv
95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
98 | # install all needed dependencies.
99 | #Pipfile.lock
100 |
101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
102 | __pypackages__/
103 |
104 | # Celery stuff
105 | celerybeat-schedule
106 | celerybeat.pid
107 |
108 | # SageMath parsed files
109 | *.sage.py
110 |
111 | # Environments
112 | .env
113 | .venv
114 | env/
115 | venv/
116 | ENV/
117 | env.bak/
118 | venv.bak/
119 |
120 | # Spyder project settings
121 | .spyderproject
122 | .spyproject
123 |
124 | # Rope project settings
125 | .ropeproject
126 |
127 | # mkdocs documentation
128 | /site
129 |
130 | # mypy
131 | .mypy_cache/
132 | .dmypy.json
133 | dmypy.json
134 |
135 | # Pyre type checker
136 | .pyre/
137 |
138 | # pytype static type analyzer
139 | .pytype/
140 |
141 | # Cython debug symbols
142 | cython_debug/
143 |
144 | #IDE metadata
145 | **.idea/**
146 |
147 | # mac os
148 | .DS_Store
149 | .vscode/settings.json
150 |
--------------------------------------------------------------------------------
/ADMIN.rst:
--------------------------------------------------------------------------------
1 | Introduction
2 | ============
3 |
4 | This documentation provides a guide for mdgo administrators. The following
5 | assumes you are using miniconda or Anaconda.
6 |
7 | Releases
8 | ========
9 |
10 | The general procedure to releasing mdgo comprises the following steps:
11 |
12 | 1. Wait for all unittests to pass on CircleCI.
13 | 2. Update and edit change log.
14 | 3. Release PyPI versions + doc.
15 | 4. Release conda versions.
16 |
17 | Initial setup
18 | -------------
19 |
20 | Install some conda tools first::
21 |
22 | conda install --yes conda-build anaconda-client
23 | conda config --add channels matsci
24 |
25 | Mdgo uses `invoke `_ to automate releases. You will
26 | also need sphinx. Install these using::
27 |
28 | pip install --upgrade invoke sphinx
29 |
30 | For 2018, we will release both py27 and py37 versions of mdgo. Create
31 | environments for py37 using conda::
32 |
33 | conda create --yes -n py37 python=3.7
34 |
35 | For each env, install required packages followed by dev install for
36 | mdgo::
37 |
38 | conda activate py37
39 | pip install -r requirements.txt
40 | pip install -r requirements-optional.txt
41 | python setup.py develop
42 |
43 | Add your PyPI username and password and GITHUB_RELEASE_TOKEN into your
44 | environment::
45 |
46 | export TWINE_USERNAME=PYPIUSERNAME
47 | export TWINE_PASSWORD=PYPIPASSWORD
48 | export GITHUB_RELEASES_TOKEN=TOKEN_YOU_GET_FROM_GITHUB
49 |
50 | You may want to add these to your .bash_profile to avoid having to type these
51 | each time.
52 |
53 | Machine-specific issues
54 | ~~~~~~~~~~~~~~~~~~~~~~~
55 |
56 | The above instructions are general, but there are some known issues that are
57 | machine-specific:
58 |
59 | * It can be useful to `pip install --upgrade pip twine setuptools` (this may
60 | be necessary if there are authentication errors when connecting to PyPI).
61 |
62 | Doing the release
63 | -----------------
64 |
65 | Ensure appropriate environment variables are set including `DISCOURSE_API_USERNAME`,
66 | `DISCOURSE_API_KEY` and `GITHUB_RELEASES_TOKEN`.
67 |
68 | First update the change log. The autogenerated change log is simply a list of
69 | commit messages since the last version. Make sure to edit the log for brevity
70 | and to attribute significant features to appropriate developers::
71 |
72 | conda activate py37
73 | invoke update-changelog
74 |
75 | Then, do the release with the following sequence of commands (you can put them
76 | in a bash script in your PATH somewhere)::
77 |
78 | conda activate py37
79 | invoke release --nodoc
80 | invoke update-doc
81 | conda deactivate
82 | python setup.py develop
83 |
84 | Double check that the releases are properly done on Pypi. If you are releasing
85 | on a Mac, you should see a mdgo.version.tar.gz and two wheels (Py37 and
86 | P). There will be a py37 wheel for Windows that is generated by Appveyor.
87 |
88 | Materials.sh
89 | ------------
90 |
91 | Fork and clone the `materials.sh `_.
92 | This repo contains the conda skeletons to build the conda versions for various
93 | matsci codes on the Anaconda `matsci channel `_.
94 |
95 | The first time this is run, you may need to `pip install beautifulsoup4`.
96 |
97 | If you doing this for the first time, make sure conda-build and anaconda-client
98 | are installed::
99 |
100 | conda install --yes conda-build anaconda-client
101 |
102 | Update the mdgo meta.yaml::
103 |
104 | invoke update-pypi mdgo
105 |
106 | Build the mac versions manually::
107 |
108 | invoke build-conda mdgo
109 |
110 | Commit and push to repo, which will build the Linux and Windows versions.
111 |
112 | Check that the `matsci channel `_ versions are
113 | properly updated.
--------------------------------------------------------------------------------
/CHANGES.rst:
--------------------------------------------------------------------------------
1 | Change log
2 | ==========
3 |
4 | v0.3.1
5 | ------
6 |
7 | v0.3.0
8 | ------
9 |
10 | v0.2.4
11 | ------
12 |
13 |
14 | v0.2.3
15 | ------
16 |
17 |
18 | v0.2.2
19 | ------
20 |
21 | * Packmol: overhaul and add tests by @rkingsbury in https://github.com/HT-MD/mdgo/pull/17
22 | * Overhaul forcefield parameters for ions by @rkingsbury in https://github.com/HT-MD/mdgo/pull/15
23 | * Add a Codacy badge to README.md by @codacy-badger in https://github.com/HT-MD/mdgo/pull/19
24 |
25 | * @codacy-badger made their first contribution in https://github.com/HT-MD/mdgo/pull/19
26 |
27 | **Full Changelog**: https://github.com/HT-MD/mdgo/compare/v0.2.1...v0.2.2
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, and in the interest of
4 | fostering an open and welcoming community, we pledge to respect all people who
5 | contribute through reporting issues, posting feature requests, updating
6 | documentation, submitting pull requests or patches, and other activities.
7 |
8 | We are committed to making participation in this project a harassment-free
9 | experience for everyone, regardless of level of experience, gender, gender
10 | identity and expression, sexual orientation, disability, personal appearance,
11 | body size, race, ethnicity, age, religion, or nationality.
12 |
13 | Examples of unacceptable behavior by participants include:
14 |
15 | * The use of sexualized language or imagery
16 | * Personal attacks
17 | * Trolling or insulting/derogatory comments
18 | * Public or private harassment
19 | * Publishing other's private information, such as physical or electronic
20 | addresses, without explicit permission
21 | * Other unethical or unprofessional conduct
22 |
23 | Project maintainers have the right and responsibility to remove, edit, or
24 | reject comments, commits, code, wiki edits, issues, and other contributions
25 | that are not aligned to this Code of Conduct, or to ban temporarily or
26 | permanently any contributor for other behaviors that they deem inappropriate,
27 | threatening, offensive, or harmful.
28 |
29 | By adopting this Code of Conduct, project maintainers commit themselves to
30 | fairly and consistently applying these principles to every aspect of managing
31 | this project. Project maintainers who do not follow or enforce the Code of
32 | Conduct may be permanently removed from the project team.
33 |
34 | This Code of Conduct applies both within project spaces and in public spaces
35 | when an individual is representing the project or its community.
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
38 | reported by contacting a project maintainer at conduct@materialsproject.org. All
39 | complaints will be reviewed and investigated and will result in a response that
40 | is deemed necessary and appropriate to the circumstances. Maintainers are
41 | obligated to maintain confidentiality with regard to the reporter of an
42 | incident to the extent possible by law and institutional policy.
43 |
44 |
45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
46 | version 1.3.0, available at https://www.contributor-covenant.org/version/1/3/0/code-of-conduct.html
47 |
48 | [homepage]: https://www.contributor-covenant.org
49 |
--------------------------------------------------------------------------------
/CONTRIBUTING.rst:
--------------------------------------------------------------------------------
1 | Collaborative Github Workflow
2 | =============================
3 |
4 | For developers interested in expanding mdgo for their own purposes, we
5 | recommend forking mdgo directly from the
6 | `mdgo GitHub repo`_. Here's a typical workflow (adapted from
7 | http://www.eqqon.com/index.php/Collaborative_Github_Workflow):
8 |
9 | .. note::
10 |
11 | Ignore the Github fork queue. Let the maintainer of mdgo worry about
12 | the fork queue.
13 |
14 | 1. Create a free GitHub account (if you don't already have one) and perform the
15 | necessary setup (e.g., install SSH keys etc.).
16 | 2. Fork the mdgo GitHub repo, i.e., go to the main
17 | `mdgo GitHub repo`_ and click fork to create a copy of the mdgo code
18 | base on your own Github account.
19 | 3. Install git on your local machine (if you don't already have it).
20 | 4. Clone *your forked repo* to your local machine. You will work mostly with
21 | your local repo and only publish changes when they are ready to be merged:
22 |
23 | ::
24 |
25 | git clone git@github.com:YOURNAME/mdgo.git
26 |
27 | Note that the entire Github repo is fairly large because of the presence of
28 | test files, but these are absolutely necessary for rigorous testing of the
29 | code.
30 | 5. It is highly recommended you install all the optional dependencies as well.
31 | 6. Code (see `Coding Guidelines`_). Commit early and commit often. Keep your
32 | code up to date. You need to add the main repository to the list of your
33 | remotes. Let's name the upstream repo as mdmain.
34 |
35 | ::
36 |
37 | git remote add mdmain git://github.com/HT-MD/mdgo.git
38 |
39 | Make sure your repository is clean (no uncommitted changes) and is currently
40 | on the main branch. If not, commit or stash any changes and switch to the
41 | main.
42 |
43 | ::
44 |
45 | git checkout main
46 |
47 | Then you can pull all the new commits from the main line
48 |
49 | ::
50 |
51 | git pull mdmain main
52 |
53 | Remember, pull is a combination of the commands fetch and merge, so there may
54 | be merge conflicts to be manually resolved.
55 |
56 | 7. Publish your contributions. Assuming that you now have a couple of commits
57 | that you would like to contribute to the main repository. Please follow the
58 | following steps:
59 |
60 | a. If your change is based on a relatively old state of the main repository,
61 | then you should probably bring your repository up-to-date first to see if
62 | the change is not creating any merge conflicts.
63 | b. Check that everything compiles cleanly and passes all tests.
64 | The mdgo repo comes with a complete set of tests for all modules. If
65 | you have written new modules or methods, you must write tests for the new
66 | code as well (see `Coding Guidelines`_). Install and run pytest in your
67 | local repo directory and fix all errors before continuing further.
68 | c. If everything is ok, publish the commits to your github repository.
69 |
70 | ::
71 |
72 | git push origin main
73 |
74 | 8. Now that your commit is published, it doesn't mean that it has already been
75 | merged into the main repository. You should issue a merge request to
76 | mdgo maintainers. They will pull your commits and run their own tests
77 | before releasing.
78 |
79 | "Work-in-progress" pull requests are encouraged, especially if this is your
80 | first time contributing to mdgo, and the maintainers will be happy to
81 | help or provide code review as necessary. Put "[WIP]" in the title of your
82 | pull request to indicate it's not ready to be merged.
83 |
84 | Coding Guidelines
85 | =================
86 |
87 | Given that mdgo is intended to be long-term code base, we adopt very strict
88 | quality control and coding guidelines for all contributions to mdgo. The
89 | following must be satisfied for your contributions to be accepted into mdgo.
90 |
91 | 1. **Unit tests** are required for all new modules and methods. The only way to
92 | minimize code regression is to ensure that all code are well-tested. If the
93 | maintainer cannot test your code, the contribution will be rejected.
94 | 2. **Python PEP 8** `code style `_.
95 | We allow a few exceptions when they are well-justified (e.g., Element's
96 | atomic number is given a variable name of capital Z, in line with accepted
97 | scientific convention), but generally, PEP 8 must be observed. Code style
98 | will be automatically checked for all PRs and must pass before any PR is merged.
99 | To aid you, you can copy the example pre-commit hook into your .git/hooks
100 | directly. This will automatically run pycodestyle and other linting services
101 | prior to any commits. At the very least, copy pre-commit to .git/hooks/pre-push.
102 | 3. **Python 3**. We only support Python 3.7+.
103 | 4. **Documentation** required for all modules, classes and methods. In
104 | particular, the method doc strings should make clear the arguments expected
105 | and the return values. For complex algorithms (e.g., an Ewald summation), a
106 | summary of the algorithm should be provided, and preferably with a link to a
107 | publication outlining the method in detail.
108 | 5. **IDE**. We highly recommend the use of Pycharm. You should also set up
109 | pycodestyle and turn those on within the IDE setup. This will warn of any
110 | issues with coding styles. Many code style errors can be done by simply
111 | selecting the entire code and using the Code->Reformat Code within Pycharm.
112 |
113 | For the above, if in doubt, please refer to the core classes in mdgo for
114 | examples of what is expected.
115 |
116 |
117 | .. _`mdgo GitHub repo`: https://github.com/HT-MD/mdgo
118 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | 
4 |
5 | [](https://app.codacy.com/gh/HT-MD/mdgo?utm_source=github.com&utm_medium=referral&utm_content=HT-MD/mdgo&utm_campaign=Badge_Grade_Settings)
6 |
7 | [](https://mdgo.readthedocs.io/)  
8 |
9 | An all-in-one code base for the classical molecualr dynamics (MD) simulation setup and results analysis.
10 |
11 | # 1. Installation
12 |
13 | ## 1.1 Installing from PyPI
14 |
15 | To install the latest release version of mdgo:
16 |
17 | `pip install mdgo`
18 |
19 | ## 1.2 Installing from source code
20 |
21 | Mdgo requires numpy, pandas, matplotlib, scipy, tqdm, statsmodels, pymatgen>=2022.0.9, pubchempy, selenium, MDAnalysis (>=2.0.0) and their dependencies.
22 |
23 | ### Getting Source Code
24 |
25 | If not available already, use the following steps.
26 |
27 | 1. Install [git](http://git-scm.com), if not already packaged with your system.
28 |
29 | 2. Download the mdgo source code using the command:
30 |
31 | `https://github.com/HouGroup/mdgo.git`
32 |
33 | ### Installation
34 |
35 | 1. Navigate to mdgo root directory:
36 |
37 | `cd mdgo`
38 |
39 | 2. Install the code, using the command:
40 |
41 | `pip install .`
42 |
43 | 3. The latest version MDAnalysis (>=2.0.0) is recommended, you may download the source code of the latest MDAnalysis from github and install using pip to replace an existing version.
44 |
45 | ### Installation in development mode
46 |
47 | 1. Navigate to mdgo root directory:
48 |
49 | `cd mdgo`
50 |
51 | 2. Install the code in "editable" mode, using the command::
52 |
53 | `pip install -e .`
54 |
55 | 3. The latest version MDAnalysis (>=2.0.0) is recommended, you may download the source code of the latest MDAnalysis from github and install using pip to replace an existing version.
56 |
57 | # 2. Features
58 |
59 | 1. Retrieving compound structure and information from PubChem
60 | - Supported searching text:
61 | - cid, name, smiles, inchi, inchikey or formula
62 | - Supported output format:
63 | - smiles code, PDB, XML, ASNT/B, JSON, SDF, CSV, PNG, TXT
64 | 2. Retrieving water and ion models
65 | - Supported water models:
66 | - SCP, SPC/E, TIP3P_EW, TIP4P_EW, TIP4P_2005
67 | - Supported ion models:
68 | - alkali, ammonium, and halide monovalent ions by Jensen and Jorgensen
69 | - alkali and halide monovalent ions by Joung and Cheatham
70 | - alkali and alkaline-earth metal cations by Åqvist
71 | 3. Write OPLS-AA forcefield file from LigParGen
72 | - Supported input format:
73 | - mol/pdb
74 | - SMILES code
75 | - Supported output format:
76 | - LAMMPS(.lmp)
77 | - GROMACS(.gro, .itp)
78 | 4. Write OPLS-AA forcefield file from Maestro
79 | - Supported input format:
80 | - Any [format that Maestro support]
81 | - Supported output format:
82 | - LAMMPS(.lmp)
83 | - Others pending\...
84 | 5. Packmol wrapper
85 | - Supported input format:
86 | - xyz
87 | - Others pending\...
88 | 6. Basic simulation properties
89 | - Initial box dimension
90 | - Equilibrium box dimension
91 | - Salt concentration
92 | 7. Conductivity analysis
93 | - Green--Kubo conductivity
94 | - Nernst--Einstein conductivity
95 | 8. Coordination analysis
96 | - The distribution of the coordination number of single species
97 | - The integral of radial distribution function (The average
98 | coordination numbers of multiple species)
99 | - Solvation structure write out
100 | - Population of solvent separated ion pairs (SSIP), contact ion
101 | pairs (CIP), and aggregates (AGG)
102 | - The trajectory (distance) of cation and coordinating species as
103 | a function of time
104 | - The hopping frequency of cation between binding sites
105 | - The distribution heat map of cation around binding sites
106 | - The averaged nearest neighbor distance of a species
107 | 9. Diffusion analysis
108 | - The mean square displacement of all species
109 | - The mean square displacement of coordinated species and
110 | uncoordinated species, separately
111 | - Self-diffusion coefficients
112 | 10. Residence time analysis
113 | - The residence time of all species
114 |
115 | # 3. Citation
116 |
117 | When using mdgo in published work, please cite the following paper:
118 |
119 | * Hou, T.; Fong, K. D.; Wang, J.; Persson, K. A. The solvation structure, transport properties and reduction behavior of carbonate-based electrolytes of lithium-ion batteries. *Chem. Sci.* **2021**, *12*, 14740-14751. [[doi](https://doi.org/10.1039/D1SC04265C)]
120 |
121 | [format that Maestro support]: https://www.schrodinger.com/kb/1278
122 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
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/logo_mdgo.svg:
--------------------------------------------------------------------------------
1 |
53 |
--------------------------------------------------------------------------------
/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=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/mdgo-white.svg:
--------------------------------------------------------------------------------
1 |
53 |
--------------------------------------------------------------------------------
/docs/source/_static/logo_mdgo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouGroup/mdgo/b3d4dab22dddd67505d15e0ed3305c01d4f602e4/docs/source/_static/logo_mdgo.png
--------------------------------------------------------------------------------
/docs/source/_static/mdgo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouGroup/mdgo/b3d4dab22dddd67505d15e0ed3305c01d4f602e4/docs/source/_static/mdgo-white.png
--------------------------------------------------------------------------------
/docs/source/aqueous.rst:
--------------------------------------------------------------------------------
1 |
2 | ================================
3 | Force Fields for Aqueous Systems
4 | ================================
5 |
6 | The Aqueous module provides tools for setting up molecular dynamics simulations
7 | involving water and ions.
8 |
9 | Water Models
10 | ============
11 |
12 | `mdgo` contains parameters for several popular water models. This section lists
13 | a brief description and literature reference to the available models.
14 |
15 | SPC
16 | ---
17 |
18 | TIP3P-EW
19 | --------
20 |
21 | TIP3P-FB
22 | --------
23 |
24 | Wang, L., Martinez, T. J., Pande, V.S., Building Force Fields: An Automatic, Systematic,
25 | and Reproducible Approach. J. Phys. Chem. Lett. 2014, 5, 11, 1885–1891.
26 | https://pubs.acs.org/doi/abs/10.1021/jz500737m
27 |
28 | Parameters are given in Supporting Table 1. Note that the epsilon for Oxygen must be converted
29 | from kJ/mol to kcal/mol.
30 |
31 | TIP4P-EW
32 | --------
33 |
34 | [Vega & de Miguel, J Chem Phys 126:154707 (2007), Vega et al, Faraday Discuss 141:251 (2009)].
35 |
36 | TIP4P-FB
37 | --------
38 |
39 | Wang, L., Martinez, T. J., Pande, V.S., Building Force Fields: An Automatic, Systematic,
40 | and Reproducible Approach. J. Phys. Chem. Lett. 2014, 5, 11, 1885–1891.
41 | https://pubs.acs.org/doi/abs/10.1021/jz500737m
42 |
43 | Parameters are given in Supporting Table 1. Note that the epsilon for oxygen must be converted
44 | from kJ/mol to kcal/mol.
45 |
46 | TIP4P-2005
47 | ----------
48 |
49 | Abascal & Vega, J Chem Phys 123:234505 (2005)
50 |
51 | OPC
52 | ----
53 |
54 | Izadi, Anandakrishnan, and Onufriev, Building Water Models: A Different Approach.
55 | J. Phys. Chem. Lett. 2014, 5, 21, 3863–3871 https://doi.org/10.1021/jz501780a
56 |
57 | Parameters are given in Table 2. Note that the epsilon for oxygen must be converted
58 | from kJ/mol to kcal/mol.
59 |
60 | OPC3
61 | ----
62 |
63 | Izadi and Onufriev, Accuracy limit of rigid 3-point water models
64 | J. Chemical Physics 145, 074501, 2016. https://doi.org/10.1063/1.4960175
65 |
66 | Parameters are given in Table II. Note that the epsilon for oxygen must be converted
67 | from kJ/mol to kcal/mol.
68 |
69 |
70 | Ion Parameter Sets
71 | ==================
72 |
73 | ``mdgo`` contains a compilation of several sets of Lennard Jones
74 | parameters for ions in water. All values are reported as :math:`\sigma_i`
75 | and :math:`\epsilon_i` in the equation
76 |
77 | .. math::
78 |
79 | E = 4 \\epsilon_i \\left[ \\left( \\frac{\sigma_i}{r} \\right)^{12} - \\left( \\frac{\sigma_i}{r} \\right)^{6} \\right]
80 |
81 | Values of :math:`\sigma_i` and :math:`\epsilon_i` are given in Angstrom
82 | and kcal/mol, respectively, corresponding to the ‘real’ units system in
83 | LAMMPS.
84 |
85 | Aqvist (aq)
86 | -----------
87 |
88 | Aqvist, J. Ion-Water Interaction Potentials Derived from Free Energy
89 | Perturbation Slmulations J. Phys. Chem. 1990, 94, 8021– 8024.
90 | https://pubs.acs.org/doi/10.1021/j100384a009
91 |
92 | Values were parameterized to the SPC water model and are reported in
93 | Table I and II as :math:`A_i` and :math:`B_i` coefficients in the
94 | following form of the Lennard-Jones potential:
95 |
96 | .. math::
97 |
98 |
99 | E = \left[ \left( \frac{A_i^2}{r} \right)^{12} - \left( \frac{B_i^2}{r} \right)^{6} \right]
100 |
101 | This parameter set is a work in progress!
102 |
103 | Jensen and Jorgensen (jj)
104 | -------------------------
105 |
106 | Jensen, K. P. and Jorgensen, W. L., Halide, Ammonium, and Alkali Metal
107 | Ion Parameters for Modeling Aqueous Solutions. J. Chem. Theory Comput.
108 | 2006, 2, 6, 1499–1509. https://pubs.acs.org/doi/abs/10.1021/ct600252r
109 |
110 | Values were parameterized to the TIP4P water model using geometric
111 | combining rules and are reported directly as sigma_i and epsilon_i in
112 | Table 2.
113 |
114 | Joung-Cheatham (jc)
115 | -------------------
116 |
117 | Joung, and Thomas E. Cheatham, Thomas E. III, Determination of Alkali
118 | and Halide Monovalent Ion Parameters for Use in Explicitly Solvated
119 | Biomolecular Simulations. J. Phys. Chem. B 2008, 112, 30, 9020–9041.
120 | https://pubs.acs.org/doi/10.1021/jp8001614
121 |
122 | Values were parameterized for the SPC/E, TIP3P, and TIP4P_EW water
123 | models using Lorentz-Berthelot combining rules (LAMMPS: ‘arithmetic’)
124 | and are reported in Table 5 as :math:`R_{min}`/2 and epsilon_i. R_min/2
125 | values are converted to :math:`\sigma_i` values using
126 | :math:`\sigma_i = R_{min}/2 * 2^(5/6)`
127 |
128 | Li and Merz group (lm)
129 | ----------------------
130 |
131 | Sengupta et al. Parameterization of Monovalent Ions for the OPC3, OPC,
132 | TIP3P-FB, and TIP4P-FB Water Models. J. Chem. Information Modeling
133 | 61(2), 2021. https://pubs.acs.org/doi/10.1021/acs.jcim.0c01390
134 |
135 | Li et al. Systematic Parametrization of Divalent Metal Ions for the
136 | OPC3, OPC, TIP3P-FB, and TIP4P-FB Water Models. J. Chem. Theory and
137 | Computation 16(7), 2020.
138 | https://pubs.acs.org/doi/10.1021/acs.jctc.0c00194
139 |
140 | Li et al. Parametrization of Trivalent and Tetravalent Metal Ions for
141 | the OPC3, OPC, TIP3P-FB, and TIP4P-FB Water Models. J. Chem. Theory and
142 | Computation 17(4), 2021.
143 | https://pubs.acs.org/doi/10.1021/acs.jctc.0c01320
144 |
145 | Values were parameterized for the OPC, OPC3, TIP3P-FB, and TIP4P-FB
146 | water models using Lorentz-Berthelot combining rules (LAMMPS:
147 | ‘arithmetic’) and are reported in Table 3 as :math:`R_{min}`/2 and
148 | epsilon_i. R_min/2 values are converted to :math:`\sigma_i` values using
149 | :math:`\sigma_i = R_{min}/2 * 2^(5/6)`. This set of values is optimized
150 | for reproducing ion-oxygen distance. An alternate set of values optimized for
151 | hydration free energies is available in the original papers.
152 |
153 |
--------------------------------------------------------------------------------
/docs/source/change_log.rst:
--------------------------------------------------------------------------------
1 | ============
2 | Change log
3 | ============
4 |
5 | v0.2.4
6 | ------
7 |
8 |
9 | v0.2.3
10 | ------
11 |
12 |
13 | v0.2.2
14 | ------
15 |
16 | * Packmol: overhaul and add tests by @rkingsbury in https://github.com/HT-MD/mdgo/pull/17
17 | * Overhaul forcefield parameters for ions by @rkingsbury in https://github.com/HT-MD/mdgo/pull/15
18 | * Add a Codacy badge to README.md by @codacy-badger in https://github.com/HT-MD/mdgo/pull/19
19 |
20 | * @codacy-badger made their first contribution in https://github.com/HT-MD/mdgo/pull/19
21 |
22 | **Full Changelog**: https://github.com/HT-MD/mdgo/compare/v0.2.1...v0.2.2
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | """Configuration file for the Sphinx documentation builder."""
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | from __future__ import annotations
14 |
15 | import os
16 | import sys
17 |
18 | sys.path.insert(0, os.path.abspath("../../mdgo"))
19 |
20 | # -- Project information -----------------------------------------------------
21 |
22 | project = "mdgo"
23 | copyright = "2021, Tingzheng Hou"
24 | author = "Tingzheng Hou"
25 |
26 | # The full version, including alpha/beta/rc tags
27 | release = "0.1.0"
28 |
29 |
30 | # -- General configuration ---------------------------------------------------
31 |
32 | # Add any Sphinx extension module names here, as strings. They can be
33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
34 | # ones.
35 |
36 | extensions = [
37 | "sphinx.ext.autodoc",
38 | "sphinx.ext.intersphinx",
39 | "sphinx.ext.doctest",
40 | "sphinx.ext.autosummary",
41 | "sphinx.ext.mathjax",
42 | "sphinx.ext.viewcode",
43 | "sphinx.ext.napoleon",
44 | "sphinx.ext.todo",
45 | "sphinx_rtd_theme",
46 | "sphinx_autodoc_typehints",
47 | ]
48 |
49 | source_suffix = [".rst"]
50 | autodoc_member_order = "bysource"
51 |
52 | # Add any paths that contain templates here, relative to this directory.
53 | templates_path = ["_templates"]
54 |
55 | # List of patterns, relative to source directory, that match files and
56 | # directories to ignore when looking for source files.
57 | # This pattern also affects html_static_path and html_extra_path.
58 | exclude_patterns: list[str] = []
59 |
60 |
61 | # -- Options for HTML output -------------------------------------------------
62 |
63 | # The theme to use for HTML and HTML Help pages. See the documentation for
64 | # a list of builtin themes.
65 | #
66 |
67 | html_theme = "sphinx_rtd_theme"
68 |
69 | # Add any paths that contain custom static files (such as style sheets) here,
70 | # relative to this directory. They are copied after the builtin static files,
71 | # so a file named "default.css" will overwrite the builtin "default.css".
72 | html_static_path = ["_static"]
73 | html_logo = "_static/mdgo-white.png"
74 | html_theme_options = {
75 | "logo_only": True,
76 | "display_version": False,
77 | }
78 |
79 | autodoc_typehints = "description"
80 |
81 | autodoc_mock_imports = [
82 | "typing_extensions",
83 | "numpy",
84 | "pandas",
85 | "matplotlib",
86 | "scipy",
87 | "tqdm",
88 | "monty",
89 | "pymatgen",
90 | "statsmodels",
91 | "pubchempy",
92 | "MDAnalysis",
93 | "selenium",
94 | "matplotlib.pyplot",
95 | "MDAnalysis.lib",
96 | "MDAnalysis.lib.distances",
97 | "MDAnalysis.analysis",
98 | "MDAnalysis.analysis.msd",
99 | "MDAnalysis.analysis.distances",
100 | "pymatgen.io",
101 | "pymatgen.io.lammps",
102 | "pymatgen.io.lammps.data",
103 | "statsmodels.tsa",
104 | "statsmodels.tsa.stattools",
105 | "scipy.signal",
106 | "scipy.optimize",
107 | "selenium.common",
108 | "selenium.common.exceptions",
109 | "selenium.webdriver",
110 | "selenium.webdriver.support",
111 | "selenium.webdriver.support.ui",
112 | "selenium.webdriver.common",
113 | "selenium.webdriver.common.by",
114 | ]
115 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | ==============================================
2 | Mdgo documentation
3 | ==============================================
4 |
5 | .. toctree::
6 | :hidden:
7 |
8 | introduction
9 | modules
10 | change_log
11 |
12 | .. include:: introduction.rst
--------------------------------------------------------------------------------
/docs/source/introduction.rst:
--------------------------------------------------------------------------------
1 | ============
2 | Introduction
3 | ============
4 |
5 | Welcome to the documentation site for mdgo! Mdgo is an python toolkit for classical molecualr dynamics (MD) simulation setup and results analysis, especially for electrolyte systems. The purpose of making this package is for supporting a high-throughput workflow for screening novel electrolytes for battery use. Currently, the package is under active development.
6 |
7 | .. contents::
8 | :local:
9 |
10 | Features
11 | ========
12 |
13 | Please see :any:`modules ` for detailed documentation of the code.
14 |
15 | #. Retriving compound structure and information from PubChem
16 |
17 | * Supported searching text:
18 |
19 | * cid, name, smiles, inchi, inchikey or formula
20 |
21 | * Supported output format:
22 |
23 | * smiles code, PDB, XML, ASNT/B, JSON, SDF, CSV, PNG, TXT
24 |
25 | #. Retrieving water and ion models
26 |
27 | * Supported water models:
28 |
29 | * SCP, SPC/E, TIP3P_EW, TIP4P_EW, TIP4P_2005
30 |
31 | * Supported ion models:
32 |
33 | * alkali, ammonium, and halide monovalent ions by Jensen and Jorgensen
34 |
35 | * alkali and halide monovalent ions by Joung and Cheatham
36 |
37 | * alkali and alkaline-earth metal cations by Åqvist
38 |
39 | #. Write OPLS-AA forcefield file from LigParGen
40 |
41 | * Supported input format:
42 |
43 | * mol/pdb
44 |
45 | * SMILES code
46 |
47 | * Supported output format:
48 |
49 | * LAMMPS(.lmp)
50 |
51 | * GROMACS(.gro, .itp)
52 |
53 | #. Write OPLS-AA forcefield file from Maestro
54 |
55 | * Supported input format:
56 |
57 | * Any `format that Maestro support `_
58 |
59 | * Supported output format:
60 |
61 | * LAMMPS(.lmp)
62 |
63 | * Others pending...
64 |
65 | #. Packmol wrapper
66 |
67 | * Supported input format:
68 |
69 | * xyz
70 |
71 | * Others pending...
72 |
73 | #. Basic simulation properties
74 |
75 | * Initial box dimension
76 |
77 | * Equilibrium box dimension
78 |
79 | * Salt concentration
80 |
81 | #. Conductivity analysis
82 |
83 | * Green–Kubo conductivity
84 |
85 | * Nernst–Einstein conductivity
86 |
87 | #. Coordination analysis
88 |
89 | * The distribution of the coordination number of single species
90 |
91 | * The integral of radial distribution function (The average coordination numbers of multiple species)
92 |
93 | * Solvation structure write out
94 |
95 | * Population of solvent separated ion pairs (SSIP), contact ion pairs (CIP), and aggregates (AGG)
96 |
97 | * The trajectory (distance) of cation and coordinating species as a function of time
98 |
99 | * The hopping frequency of cation between binding sites
100 |
101 | * The distribution heat map of cation around binding sites
102 |
103 | * The averaged nearest neighbor distance of a species
104 |
105 | #. Diffusion analysis
106 |
107 | * The mean square displacement of all species
108 |
109 | * The mean square displacement of coordinated species and uncoordinated species, separately
110 |
111 | * Self-diffusion coefficients
112 |
113 | #. Residence time analysis
114 |
115 | * The residence time of all species
116 |
117 |
118 |
119 | Installation
120 | ============
121 |
122 | Installing from PyPI
123 | --------------------
124 |
125 | To install the latest release version of mdgo::
126 |
127 | pip install mdgo
128 |
129 | Installing from Source
130 | ----------------------
131 |
132 | Mdgo requires numpy, pandas, matplotlib, scipy, tqdm, statsmodels, pymatgen>=2022.0.9, pubchempy, selenium, MDAnalysis (version 2.0.0-dev0 prefered) and their dependencies.
133 |
134 | Getting source code
135 | ^^^^^^^^^^^^^^^^^^^
136 |
137 | If not available already, use the following steps.
138 |
139 | 1. Install `git `_ if not already packaged with your system.
140 |
141 | 2. Download the mdgo source code using the command::
142 |
143 | git clone https://github.com/htz1992213/mdgo.git
144 |
145 | Installation from source
146 | ^^^^^^^^^^^^^^^^^^^^^^^^
147 | 1. Navigate to mdgo root directory::
148 |
149 | cd mdgo
150 |
151 | 2. Install the code, using the command::
152 |
153 | pip install .
154 |
155 | 3. The latest version MDAnalysis==2.0.0.dev0 is recommended, you may download the source code of the latest MDAnalysis from github and install using pip to replace an existing version.
156 |
157 | Installation in development mode
158 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159 | 1. Navigate to mdgo root directory::
160 |
161 | cd mdgo
162 |
163 | 2. Install the code in "editable" mode, using the command::
164 |
165 | pip install -e .
166 |
167 | 3. The latest version MDAnalysis==2.0.0.dev0 is recommended, you may download the source code of the latest MDAnalysis from github and install using pip to replace an existing version.
168 |
169 | Contributing
170 | ============
171 |
172 | Reporting bugs
173 | --------------
174 |
175 | Please report any bugs and issues at mdgo's
176 | `Github Issues page `_.
177 |
178 | Developing new functionality
179 | ----------------------------
180 |
181 | You may submit new code/bugfixes by sending a pull request to the mdgo's github repository.
182 |
183 | How to cite mdgo
184 | ================
185 |
186 | pending...
187 |
188 | License
189 | =======
190 |
191 | Mdgo is released under the MIT License. The terms of the license are as
192 | follows:
193 |
194 | .. literalinclude:: ../../LICENSE.rst
195 |
196 | About the Team
197 | ==============
198 |
199 | Tingzheng Hou started mdgo in 2020 under the supervision of Prof. Kristin Persson at University of California, berkeley.
200 |
201 | Copyright Policy
202 | ================
203 |
204 | The following banner should be used in any source code file
205 | to indicate the copyright and license terms::
206 |
207 | # Copyright (c) Tingzheng Hou.
208 | # Distributed under the terms of the MIT License.
209 |
210 | Indices and tables
211 | ==================
212 |
213 | * :ref:`genindex`
214 | * :ref:`modindex`
215 | * :ref:`search`
216 |
--------------------------------------------------------------------------------
/docs/source/latest_changes.rst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouGroup/mdgo/b3d4dab22dddd67505d15e0ed3305c01d4f602e4/docs/source/latest_changes.rst
--------------------------------------------------------------------------------
/docs/source/mdgo.conductivity.rst:
--------------------------------------------------------------------------------
1 | mdgo.conductivity module
2 | ========================
3 |
4 | .. automodule:: mdgo.conductivity
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.coordination.rst:
--------------------------------------------------------------------------------
1 | mdgo.coordination module
2 | ========================
3 |
4 | .. automodule:: mdgo.coordination
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.core.rst:
--------------------------------------------------------------------------------
1 | mdgo.core module
2 | ================
3 |
4 | .. automodule:: mdgo.core
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.forcefield.rst:
--------------------------------------------------------------------------------
1 | mdgo.forcefield module
2 | ======================
3 |
4 | .. automodule:: mdgo.forcefield
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.mdgopackmol.rst:
--------------------------------------------------------------------------------
1 | mdgo.mdgopackmol module
2 | =======================
3 |
4 | .. automodule:: mdgo.mdgopackmol
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.msd.rst:
--------------------------------------------------------------------------------
1 | mdgo.msd module
2 | ===============
3 |
4 | .. automodule:: mdgo.msd
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.residence_time.rst:
--------------------------------------------------------------------------------
1 | mdgo.residence\_time module
2 | ===========================
3 |
4 | .. automodule:: mdgo.residence_time
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.rst:
--------------------------------------------------------------------------------
1 | mdgo package
2 | ============
3 |
4 | Submodules
5 | ----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | mdgo.conductivity
11 | mdgo.coordination
12 | mdgo.core
13 | mdgo.forcefield
14 | mdgo.mdgopackmol
15 | mdgo.msd
16 | mdgo.residence_time
17 | mdgo.volume
18 | mdgo.util
19 |
20 | Module contents
21 | ---------------
22 |
23 | .. automodule:: mdgo
24 | :members:
25 | :undoc-members:
26 | :show-inheritance:
27 |
--------------------------------------------------------------------------------
/docs/source/mdgo.util.rst:
--------------------------------------------------------------------------------
1 | mdgo.util module
2 | ================
3 |
4 | .. automodule:: mdgo.util
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/mdgo.volume.rst:
--------------------------------------------------------------------------------
1 | mdgo.volume module
2 | ==================
3 |
4 | .. automodule:: mdgo.volume
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/modules.rst:
--------------------------------------------------------------------------------
1 | .. _my-section:
2 |
3 | ====
4 | mdgo
5 | ====
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | mdgo
11 |
--------------------------------------------------------------------------------
/docs/source/rtd_requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx-autodoc-typehints
2 |
--------------------------------------------------------------------------------
/mdgo/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """This package contains core modules and classes molecular dynamics simulation setup and analysis."""
5 |
6 | from __future__ import annotations
7 |
8 | __author__ = "Mdgo Development Team"
9 | __email__ = "tingzheng_hou@berkeley.edu"
10 | __maintainer__ = "Tingzheng Hou"
11 | __maintainer_email__ = "tingzheng_hou@berkeley.edu"
12 | __version__ = "0.3.1"
13 |
--------------------------------------------------------------------------------
/mdgo/conductivity.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """This module implements functions to calculate the ionic conductivity."""
5 |
6 | from __future__ import annotations
7 |
8 | from typing import TYPE_CHECKING
9 |
10 | import numpy as np
11 | from scipy import stats
12 | from tqdm.auto import tqdm
13 |
14 | from mdgo.msd import msd_fft
15 |
16 | if TYPE_CHECKING:
17 | from MDAnalysis import AtomGroup, Universe
18 |
19 | __author__ = "Kara Fong, Tingzheng Hou"
20 | __version__ = "0.3.0"
21 | __maintainer__ = "Tingzheng Hou"
22 | __email__ = "tingzheng_hou@berkeley.edu"
23 | __date__ = "Jul 19, 2021"
24 |
25 |
26 | def calc_cond_msd(
27 | u: Universe,
28 | anions: AtomGroup,
29 | cations: AtomGroup,
30 | run_start: int,
31 | cation_charge: float = 1,
32 | anion_charge: float = -1,
33 | ) -> np.ndarray:
34 | """Calculates the conductivity "mean square displacement" over time.
35 |
36 | Note:
37 | Coordinates must be unwrapped (in dcd file when creating MDAnalysis Universe)
38 | Ions selections may consist of only one atom per ion, or include all of the atoms
39 | in the ion. The ion AtomGroups may consist of multiple types of cations/anions.
40 |
41 | Args:
42 | u: MDAnalysis universe
43 | anions: MDAnalysis AtomGroup containing all anions
44 | cations: MDAnalysis AtomGroup containing all cations
45 | run_start: index of trajectory from which to start analysis
46 | cation_charge: net charge of cation
47 | anion_charge: net charge of anion
48 |
49 | Returns a numpy.array containing conductivity "MSD" over time
50 | """
51 | # convert AtomGroup into list of molecules
52 | cation_list = cations.split("residue")
53 | anion_list = anions.split("residue")
54 | # compute sum over all charges and positions
55 | qr = []
56 | for _ts in tqdm(u.trajectory[run_start:]):
57 | qr_temp = np.zeros(3)
58 | for anion in anion_list:
59 | qr_temp += anion.center_of_mass() * anion_charge
60 | for cation in cation_list:
61 | qr_temp += cation.center_of_mass() * cation_charge
62 | qr.append(qr_temp)
63 | return msd_fft(np.array(qr))
64 |
65 |
66 | def get_beta(
67 | msd: np.ndarray,
68 | time_array: np.ndarray,
69 | start: int,
70 | end: int,
71 | ) -> tuple:
72 | """Fits the MSD to the form t^(beta) and returns beta. beta = 1 corresponds
73 | to the diffusive regime.
74 |
75 | Args:
76 | msd: mean squared displacement
77 | time_array: times at which position data was collected in the simulation
78 | start: index at which to start fitting linear regime of the MSD
79 | end: index at which to end fitting linear regime of the MSD
80 |
81 | Returns beta (int) and the range of beta values within the region
82 | """
83 | msd_slope = np.gradient(np.log(msd[start:end]), np.log(time_array[start:end]))
84 | beta = np.mean(np.array(msd_slope))
85 | beta_range = np.max(msd_slope) - np.min(msd_slope)
86 | return beta, beta_range
87 |
88 |
89 | def choose_msd_fitting_region(
90 | msd: np.ndarray,
91 | time_array: np.ndarray,
92 | ) -> tuple:
93 | """Chooses the optimal fitting regime for a mean-squared displacement.
94 | The MSD should be of the form t^(beta), where beta = 1 corresponds
95 | to the diffusive regime; as a rule of thumb, the MSD should exhibit this
96 | linear behavior for at least a decade of time. Finds the region of the
97 | MSD with the beta value closest to 1.
98 |
99 | Note:
100 | If a beta value greater than 0.9 cannot be found, returns a warning
101 | that the computed conductivity may not be reliable, and that longer
102 | simulations or more replicates are necessary.
103 |
104 | Args:
105 | msd: mean squared displacement
106 | time_array: times at which position data was collected in the simulation
107 |
108 | Returns at tuple with the start of the fitting regime (int), end of the
109 | fitting regime (int), and the beta value of the fitting regime (float).
110 | """
111 | beta_best = 0 # region with greatest linearity (beta = 1)
112 | # choose fitting regions to check
113 | for i in np.logspace(np.log10(2), np.log10(len(time_array) / 10), 10): # try 10 regions
114 | start = int(i)
115 | end = int(i * 10) # fit over one decade
116 | beta, beta_range = get_beta(msd, time_array, start, end)
117 | slope_tolerance = 2 # acceptable level of noise in beta values
118 | # check if beta in this region is better than regions tested so far
119 | if (np.abs(beta - 1) < np.abs(beta_best - 1) and beta_range < slope_tolerance) or beta_best == 0:
120 | beta_best = beta
121 | start_final = start
122 | end_final = end
123 | if beta_best < 0.9:
124 | print(f"WARNING: MSD is not sufficiently linear (beta = {beta_best}). Consider running simulations longer.")
125 | return start_final, end_final, beta_best
126 |
127 |
128 | def conductivity_calculator(
129 | time_array: np.ndarray,
130 | cond_array: np.ndarray,
131 | v: float,
132 | name: str,
133 | start: int,
134 | end: int,
135 | T: float,
136 | units: str = "real",
137 | ) -> float:
138 | """Calculates the overall conductivity of the system.
139 |
140 | Args:
141 | time_array: times at which position data was collected in the simulation
142 | cond_array: conductivity "mean squared displacement"
143 | v: simulation volume (Angstroms^3)
144 | name: system name
145 | start: index at which to start fitting linear regime of the MSD
146 | end: index at which to end fitting linear regime of the MSD
147 | T: temperature
148 | units: unit system (currently 'real' and 'lj' are supported)
149 |
150 | Returns the overall ionic conductivity (float)
151 | """
152 | # Unit conversions
153 | if units == "real":
154 | A2cm = 1e-8 # Angstroms to cm
155 | ps2s = 1e-12 # picoseconds to seconds
156 | e2c = 1.60217662e-19 # elementary charge to Coulomb
157 | kb = 1.38064852e-23 # Boltzmann Constant, J/K
158 | convert = e2c * e2c / ps2s / A2cm * 1000
159 | cond_units = "mS/cm"
160 | elif units == "lj":
161 | kb = 1
162 | convert = 1
163 | cond_units = "q^2/(tau sigma epsilon)"
164 | else:
165 | raise ValueError("units selection not supported")
166 |
167 | slope, _, _, _, _ = stats.linregress(time_array[start:end], cond_array[start:end])
168 | cond = slope / 6 / kb / T / v * convert
169 |
170 | print("Conductivity of " + name + ": " + str(cond) + " " + cond_units)
171 |
172 | return cond
173 |
--------------------------------------------------------------------------------
/mdgo/core/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """
5 | This package contains core modules and classes for molecular dynamics
6 | setup, run and analysis.
7 | """
8 |
9 | from __future__ import annotations
10 |
11 | __author__ = "Tingzheng Hou"
12 | __version__ = "0.3.1"
13 | __maintainer__ = "Tingzheng Hou"
14 | __email__ = "tingzheng_hou@berkeley.edu"
15 | __date__ = "Dec 19, 2023"
16 |
17 |
18 | from .analysis import MdRun
19 | from .run import MdJob
20 |
--------------------------------------------------------------------------------
/mdgo/core/run.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """This module implements a core class MdRun for molecular dynamics job setup."""
5 | from __future__ import annotations
6 |
7 |
8 | class MdJob:
9 | """A core class for MD results analysis."""
10 |
11 | def __init__(self, name):
12 | """Base constructor."""
13 | self.name = name
14 |
15 | @classmethod
16 | def from_dict(cls):
17 | """
18 | Constructor.
19 |
20 | Returns:
21 | name: The name of the class
22 |
23 | """
24 | return cls("name")
25 |
26 | @classmethod
27 | def from_recipe(cls):
28 | """
29 | Constructor.
30 |
31 | Returns:
32 | name: The name of the class
33 | """
34 | return cls("name")
35 |
--------------------------------------------------------------------------------
/mdgo/forcefield/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """
5 | This package contains modules and classes for retrieving, generating and
6 | modifying MD force filed data.
7 | """
8 |
9 | from __future__ import annotations
10 |
11 | __author__ = "Tingzheng Hou, Ryan Kingsbury"
12 | __version__ = "0.3.1"
13 | __maintainer__ = "Tingzheng Hou, Ryan Kingsbury"
14 | __email__ = "tingzheng_hou@berkeley.edu"
15 | __date__ = "Dec 19, 2023"
16 |
17 |
18 | from .aqueous import Aqueous, IonLJData
19 | from .charge import ChargeWriter
20 | from .crawler import FFcrawler
21 | from .maestro import MaestroRunner
22 | from .pubchem import PubChemRunner
23 |
--------------------------------------------------------------------------------
/mdgo/forcefield/charge.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """A class for writing, overwriting, scaling charges of a LammpsData object."""
5 |
6 | from __future__ import annotations
7 |
8 | import numpy as np
9 | from pymatgen.io.lammps.data import LammpsData
10 |
11 |
12 | class ChargeWriter:
13 | """
14 | A class for write, overwrite, scale charges of a LammpsData object.
15 | TODO: Auto determine number of significant figures of charges
16 | TODO: write to obj or write separate charge file
17 | TODO: Read LammpsData or path.
18 |
19 | Args:
20 | data: The provided LammpsData obj.
21 | precision: Number of significant figures.
22 | """
23 |
24 | def __init__(self, data: LammpsData, precision: int = 10):
25 | """Base constructor."""
26 | self.data = data
27 | self.precision = precision
28 |
29 | def scale(self, factor: float) -> LammpsData:
30 | """
31 | Scales the charge in of the in self.data and returns a new one. TODO: check if non-destructive.
32 |
33 | Args:
34 | factor: The charge scaling factor
35 |
36 | Returns:
37 | A recreated LammpsData obj
38 | """
39 | items = {}
40 | items["box"] = self.data.box
41 | items["masses"] = self.data.masses
42 | atoms = self.data.atoms.copy(deep=True)
43 | atoms["q"] = atoms["q"] * factor
44 | assert np.around(atoms.q.sum(), decimals=self.precision) == np.around(
45 | self.data.atoms.q.sum() * factor, decimals=self.precision
46 | )
47 | digit_count = 0
48 | for q in atoms["q"]:
49 | rounded = self.count_significant_figures(q)
50 | if rounded > digit_count:
51 | digit_count = rounded
52 | print("No. of significant figures to output for charges: ", digit_count)
53 | items["atoms"] = atoms
54 | items["atom_style"] = self.data.atom_style
55 | items["velocities"] = self.data.velocities
56 | items["force_field"] = self.data.force_field
57 | items["topology"] = self.data.topology
58 | return LammpsData(**items)
59 |
60 | def count_significant_figures(self, number: float) -> int:
61 | """
62 | Count significant figures in a float.
63 |
64 | Args:
65 | number: The number to count.
66 |
67 | Returns:
68 | The number of significant figures.
69 | """
70 | number_str = repr(float(number))
71 | tokens = number_str.split(".")
72 | if len(tokens) > 2:
73 | raise ValueError(f"Invalid number '{number}' only 1 decimal allowed")
74 | if len(tokens) == 2:
75 | decimal_num = tokens[1][: self.precision].rstrip("0")
76 | return len(decimal_num)
77 | return 0
78 |
--------------------------------------------------------------------------------
/mdgo/forcefield/crawler.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """
5 | This module implements two core class FFcrawler for generating
6 | LAMMPS/GROMACS data files from molecule structure using
7 | the LigParGen web server.
8 |
9 | For using the FFcrawler class:
10 |
11 | * Download the ChromeDriver executable that
12 | matches your Chrome version via https://chromedriver.chromium.org/downloads
13 | """
14 |
15 | from __future__ import annotations
16 |
17 | import os
18 | import shutil
19 | import time
20 |
21 | from pymatgen.io.lammps.data import LammpsData
22 | from selenium import webdriver
23 | from selenium.common.exceptions import TimeoutException, WebDriverException
24 | from selenium.webdriver.common.by import By
25 | from selenium.webdriver.support import expected_conditions as EC
26 | from selenium.webdriver.support.ui import WebDriverWait
27 |
28 | from mdgo.util.dict_utils import lmp_mass_to_name
29 |
30 |
31 | class FFcrawler:
32 | """
33 | Web scrapper that can automatically upload structure to the LigParGen
34 | server and download LAMMPS/GROMACS data file.
35 |
36 | Args:
37 | write_dir: Directory for writing output.
38 | chromedriver_dir: Directory to the ChromeDriver executable.
39 | headless: Whether to run Chrome in headless (silent) mode.
40 | Default to True.
41 | xyz: Whether to write the structure in the LigParGen
42 | generated data file as .xyz. Default to False. This is useful
43 | because the order and the name of the atoms could be
44 | different from the initial input.)
45 | gromacs: Whether to save GROMACS format data files.
46 | Default to False.
47 |
48 | Examples:
49 | >>> lpg = FFcrawler('/path/to/work/dir', '/path/to/chromedriver')
50 | >>> lpg.data_from_pdb("/path/to/pdb")
51 | """
52 |
53 | def __init__(
54 | self,
55 | write_dir: str,
56 | chromedriver_dir: str | None = None,
57 | headless: bool = True,
58 | xyz: bool = False,
59 | gromacs: bool = False,
60 | ):
61 | """Base constructor."""
62 | self.write_dir = write_dir
63 | self.xyz = xyz
64 | self.gromacs = gromacs
65 | self.preferences = {
66 | "download.default_directory": write_dir,
67 | "safebrowsing.enabled": "false",
68 | "profile.managed_default_content_settings.images": 2,
69 | }
70 | self.options = webdriver.ChromeOptions()
71 | self.server = webdriver.ChromeService(chromedriver_dir)
72 | self.options.add_argument(
73 | 'user-agent="Mozilla/5.0 '
74 | "(Macintosh; Intel Mac OS X 10_14_6) "
75 | "AppleWebKit/537.36 (KHTML, like Gecko) "
76 | 'Chrome/88.0.4324.146 Safari/537.36"'
77 | )
78 | self.options.add_argument("--window-size=1920,1080")
79 | self.options.add_argument("ignore-certificate-errors")
80 | if headless:
81 | self.options.add_argument("--headless")
82 | self.options.add_experimental_option("prefs", self.preferences)
83 | self.options.add_experimental_option("excludeSwitches", ["enable-automation"])
84 | if chromedriver_dir is None:
85 | self.web = webdriver.Chrome(options=self.options)
86 | else:
87 | self.web = webdriver.Chrome(service=self.server, options=self.options)
88 | self.wait = WebDriverWait(self.web, 10)
89 | self.web.get("http://traken.chem.yale.edu/ligpargen/")
90 | time.sleep(1)
91 | print("LigParGen server connected.")
92 |
93 | def quit(self):
94 | """Method for quiting ChromeDriver."""
95 | self.web.quit()
96 |
97 | def data_from_pdb(self, pdb_dir: str):
98 | """
99 | Use the LigParGen server to generate a LAMMPS data file from a pdb file.
100 | Write out a LAMMPS data file.
101 |
102 | Args:
103 | pdb_dir: The path to the input pdb structure file.
104 | """
105 | self.web.get("http://traken.chem.yale.edu/ligpargen/")
106 | upload_xpath = '//*[@id="exampleMOLFile"]'
107 | time.sleep(1)
108 | self.wait.until(EC.presence_of_element_located((By.XPATH, upload_xpath)))
109 | upload = self.web.find_element(By.XPATH, upload_xpath)
110 | try:
111 | upload.send_keys(pdb_dir)
112 | submit = self.web.find_element(By.XPATH, "/html/body/div[2]/div/div[2]/form/button[1]")
113 | submit.click()
114 | pdb_filename = os.path.basename(pdb_dir)
115 | self.download_data(os.path.splitext(pdb_filename)[0] + ".lmp")
116 | except TimeoutException:
117 | print("Timeout! Web server no response for 10s, file download failed!")
118 | except WebDriverException as e:
119 | print(e)
120 | finally:
121 | self.quit()
122 |
123 | def data_from_smiles(self, smiles_code):
124 | """
125 | Use the LigParGen server to generate a LAMMPS data file from a SMILES code.
126 | Write out a LAMMPS data file.
127 |
128 | Args:
129 | smiles_code: The SMILES code for the LigParGen input.
130 | """
131 | self.web.get("http://traken.chem.yale.edu/ligpargen/")
132 | time.sleep(1)
133 | smile = self.web.find_element(By.XPATH, '//*[@id="smiles"]')
134 | smile.send_keys(smiles_code)
135 | submit = self.web.find_element(By.XPATH, "/html/body/div[2]/div/div[2]/form/button[1]")
136 | submit.click()
137 | try:
138 | self.download_data(smiles_code + ".lmp")
139 | except TimeoutException:
140 | print("Timeout! Web server no response for 10s, file download failed!")
141 | finally:
142 | self.quit()
143 |
144 | def download_data(self, lmp_name: str):
145 | """
146 | Helper function that download and write out the LAMMPS data file.
147 |
148 | Args:
149 | lmp_name: Name of the LAMMPS data file.
150 | """
151 | print("Structure info uploaded. Rendering force field...")
152 | lmp_xpath = "/html/body/div[2]/div[2]/div[1]/div/div[14]/form/input[1]"
153 | self.wait.until(EC.presence_of_element_located((By.XPATH, lmp_xpath)))
154 | jmol = self.web.find_element(By.XPATH, "/html/body/div[2]/div[2]/div[2]")
155 | self.web.execute_script("arguments[0].remove();", jmol)
156 | self.wait.until(EC.element_to_be_clickable((By.XPATH, lmp_xpath)))
157 | data_lmp = self.web.find_element(By.XPATH, lmp_xpath)
158 | num_file = len([f for f in os.listdir(self.write_dir) if os.path.splitext(f)[1] == ".lmp"]) + 1
159 | data_lmp.click()
160 | while True:
161 | files = sorted(
162 | [
163 | os.path.join(self.write_dir, f)
164 | for f in os.listdir(self.write_dir)
165 | if os.path.splitext(f)[1] == ".lmp"
166 | ],
167 | key=os.path.getmtime,
168 | )
169 | # wait for file to finish download
170 | if len(files) < num_file:
171 | time.sleep(1)
172 | print("waiting for download to be initiated")
173 | else:
174 | newest = files[-1]
175 | if ".crdownload" in newest:
176 | time.sleep(1)
177 | print("waiting for download to complete")
178 | else:
179 | break
180 | print("Force field file downloaded.")
181 | lmp_file = newest
182 | if self.xyz:
183 | data_obj = LammpsData.from_file(lmp_file)
184 | element_id_dict = lmp_mass_to_name(data_obj.masses)
185 | coords = data_obj.atoms[["type", "x", "y", "z"]]
186 | lines = []
187 | lines.append(str(len(coords.index)))
188 | lines.append("")
189 | for _, r in coords.iterrows():
190 | element_name = element_id_dict.get(int(r["type"]))
191 | assert element_name is not None
192 | line = element_name + " " + " ".join(str(r[loc]) for loc in ["x", "y", "z"])
193 | lines.append(line)
194 |
195 | with open(os.path.join(self.write_dir, lmp_name + ".xyz"), "w") as xyz_file:
196 | xyz_file.write("\n".join(lines))
197 | print(".xyz file saved.")
198 | if self.gromacs:
199 | data_gro = self.web.find_element(By.XPATH, "/html/body/div[2]/div[2]/div[1]/div/div[8]/form/input[1]")
200 | data_itp = self.web.find_element(By.XPATH, "/html/body/div[2]/div[2]/div[1]/div/div[9]/form/input[1]")
201 | data_gro.click()
202 | data_itp.click()
203 | time.sleep(1)
204 | gro_file = max(
205 | [self.write_dir + "/" + f for f in os.listdir(self.write_dir) if os.path.splitext(f)[1] == ".gro"],
206 | key=os.path.getctime,
207 | )
208 | itp_file = max(
209 | [self.write_dir + "/" + f for f in os.listdir(self.write_dir) if os.path.splitext(f)[1] == ".itp"],
210 | key=os.path.getctime,
211 | )
212 | shutil.move(gro_file, os.path.join(self.write_dir, lmp_name[:-4] + ".gro"))
213 | shutil.move(itp_file, os.path.join(self.write_dir, lmp_name[:-4] + ".itp"))
214 | shutil.move(lmp_file, os.path.join(self.write_dir, lmp_name))
215 | print("Force field file saved.")
216 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_opc.lmp:
--------------------------------------------------------------------------------
1 | OPC3 water model
2 | # Original model from Izadi, Anandakrishnan, and Onufriev, Building Water Models:
3 | # A Different Approach. J. Phys. Chem. Lett. 2014, 5, 21, 3863–3871
4 | # https://doi.org/10.1021/jz501780a
5 | # Table 2.
6 | # real units (kcal/mol, Angstrom)
7 | # Command 'fix shake' is needed.
8 | # LJ and couloumb cutoffs of 9 and 7 angstroms are adopted from the TIP3B-FB paper
9 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
10 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
11 |
12 | 3 atoms
13 | 2 bonds
14 | 1 angles
15 |
16 | 2 atom types
17 | 1 bond types
18 | 1 angle types
19 |
20 | 0.0 3.1 xlo xhi
21 | 0.0 3.1 ylo yhi
22 | 0.0 3.1 zlo zhi
23 |
24 | Masses
25 |
26 | 1 1.00794
27 | 2 15.9994
28 |
29 | Pair Coeffs #lj/cut/tip4p/long 2 1 1 1 0.1594 9.0 7.0
30 |
31 | 1 0.0000 0.0000
32 | 2 0.21280 3.16655
33 |
34 | Bond Coeffs #harmonic
35 |
36 | 1 1000 0.8724
37 |
38 | Angle Coeffs #harmonic
39 |
40 | 1 1000 103.6
41 |
42 | Atoms
43 |
44 | 1 1 2 -1.3582 1.55000 1.55000 1.50000
45 | 2 1 1 0.6791 1.55000 2.36649 2.07736
46 | 3 1 1 0.6791 1.55000 0.73351 2.07736
47 |
48 | Bonds
49 |
50 | 1 1 1 2
51 | 2 1 1 3
52 |
53 | Angles
54 |
55 | 1 1 2 1 3
56 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_opc3.lmp:
--------------------------------------------------------------------------------
1 | OPC3 water model
2 | # Original model from Izadi and Onufriev, Accuracy limit of rigid 3-point water models
3 | # J. Chemical Physics 145, 074501, 2016. https://doi.org/10.1063/1.4960175, Table II.
4 | # real units (kcal/mol, Angstrom)
5 | # Command 'fix shake' is needed.
6 | # LJ and couloumb cutoffs of 9 and 7 angstroms are adopted from the TIP3B-FB paper
7 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
8 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
9 |
10 | 3 atoms
11 | 2 bonds
12 | 1 angles
13 |
14 | 2 atom types
15 | 1 bond types
16 | 1 angle types
17 |
18 | 0.0 3.1 xlo xhi
19 | 0.0 3.1 ylo yhi
20 | 0.0 3.1 zlo zhi
21 |
22 | Masses
23 |
24 | 1 1.00794
25 | 2 15.9994
26 |
27 | Pair Coeffs #lj/cut/coul/long 9.0 7.0
28 |
29 | 1 0.000 0.000
30 | 2 0.16340 3.17427
31 |
32 | Bond Coeffs #harmonic
33 |
34 | 1 1000 0.97888
35 |
36 | Angle Coeffs #harmonic
37 |
38 | 1 1000 109.47
39 |
40 | Atoms
41 |
42 | 1 1 2 -0.89517 1.55000 1.55000 1.50000
43 | 2 1 1 0.447585 1.55000 2.36649 2.07736
44 | 3 1 1 0.447585 1.55000 0.73351 2.07736
45 |
46 | Bonds
47 |
48 | 1 1 1 2
49 | 2 1 1 3
50 |
51 | Angles
52 |
53 | 1 1 2 1 3
54 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_spc.lmp:
--------------------------------------------------------------------------------
1 | SPC water model
2 | # Berendsen et al, in "Intermolecular forces", p. 331 (1981). Command 'fix shake' is needed.
3 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
4 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
5 |
6 | 3 atoms
7 | 2 bonds
8 | 1 angles
9 |
10 | 2 atom types
11 | 1 bond types
12 | 1 angle types
13 |
14 | 0.0 3.1 xlo xhi
15 | 0.0 3.1 ylo yhi
16 | 0.0 3.1 zlo zhi
17 |
18 | Masses
19 |
20 | 1 1.00794
21 | 2 15.9994
22 |
23 | Pair Coeffs #lj/cut/coul/long 10.0
24 |
25 | 1 0.0000 0.0000
26 | 2 0.1554 3.16557
27 |
28 | Bond Coeffs #harmonic
29 |
30 | 1 1000 1.0
31 |
32 | Angle Coeffs #harmonic
33 |
34 | 1 1000 109.47
35 |
36 | Atoms
37 |
38 | 1 1 2 -0.82 1.55000 1.55000 1.50000
39 | 2 1 1 0.41 1.55000 2.36649 2.07736
40 | 3 1 1 0.41 1.55000 0.73351 2.07736
41 |
42 | Bonds
43 |
44 | 1 1 1 2
45 | 2 1 1 3
46 |
47 | Angles
48 |
49 | 1 1 2 1 3
50 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_spce.lmp:
--------------------------------------------------------------------------------
1 | SPC/E water model
2 | # From Berendsen et al, J Phys Chem 91:6269 (1987). Command 'fix shake' is needed.
3 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
4 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
5 |
6 | 3 atoms
7 | 2 bonds
8 | 1 angles
9 |
10 | 2 atom types
11 | 1 bond types
12 | 1 angle types
13 |
14 | 0.0 3.1 xlo xhi
15 | 0.0 3.1 ylo yhi
16 | 0.0 3.1 zlo zhi
17 |
18 | Masses
19 |
20 | 1 1.00794
21 | 2 15.9994
22 |
23 | Pair Coeffs #lj/cut/coul/long 10.0
24 |
25 | 1 0.0000 0.0000
26 | 2 0.1554 3.16557
27 |
28 | Bond Coeffs #harmonic
29 |
30 | 1 1000 1.0
31 |
32 | Angle Coeffs #harmonic
33 |
34 | 1 1000 109.47
35 |
36 | Atoms
37 |
38 | 1 1 2 -0.8476 1.55000 1.55000 1.50000
39 | 2 1 1 0.4238 1.55000 2.36649 2.07736
40 | 3 1 1 0.4238 1.55000 0.73351 2.07736
41 |
42 | Bonds
43 |
44 | 1 1 1 2
45 | 2 1 1 3
46 |
47 | Angles
48 |
49 | 1 1 2 1 3
50 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_tip3p_ew.lmp:
--------------------------------------------------------------------------------
1 | Optimized TIP3P parameters to use with Ewald methods for long-range electrostatics. (e.g. pppm 1.0e-5)
2 | # Price & Brooks, J Chem Phys 121:10096 (2004).
3 |
4 | 3 atoms
5 | 2 bonds
6 | 1 angles
7 |
8 | 2 atom types
9 | 1 bond types
10 | 1 angle types
11 |
12 | 0.0 3.1 xlo xhi
13 | 0.0 3.1 ylo yhi
14 | 0.0 3.1 zlo zhi
15 |
16 | Masses
17 |
18 | 1 1.00794
19 | 2 15.9994
20 |
21 | Pair Coeffs #lj/cut/coul/long 13.0
22 |
23 | 1 0.000 0.000
24 | 2 0.102 3.188
25 |
26 | Bond Coeffs #harmonic
27 |
28 | 1 450.0 0.9572
29 |
30 | Angle Coeffs #harmonic
31 |
32 | 1 55.0 104.52
33 |
34 | Atoms
35 |
36 | 1 1 2 -0.830 1.55000 1.55000 1.50000
37 | 2 1 1 0.415 1.55000 2.36649 2.07736
38 | 3 1 1 0.415 1.55000 0.73351 2.07736
39 |
40 | Bonds
41 |
42 | 1 1 1 2
43 | 2 1 1 3
44 |
45 | Angles
46 |
47 | 1 1 2 1 3
48 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_tip3p_fb.lmp:
--------------------------------------------------------------------------------
1 | Optimized TIP3P-FB parameters for water
2 | # Original model from Wang et al., Building Force Fields: An Automatic,
3 | # Systematic, and Reproducible Approach, J. Phys. Chem. Letters 5(11), 2014.
4 | # https://pubs.acs.org/doi/10.1021/jz500737m
5 | # real units (kcal/mol, Angstrom)
6 | # Command 'fix shake' is needed.
7 | # LJ and couloumb cutoffs of 9 and 7 angstroms were used in the original work
8 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
9 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
10 |
11 | 3 atoms
12 | 2 bonds
13 | 1 angles
14 |
15 | 2 atom types
16 | 1 bond types
17 | 1 angle types
18 |
19 | 0.0 3.1 xlo xhi
20 | 0.0 3.1 ylo yhi
21 | 0.0 3.1 zlo zhi
22 |
23 | Masses
24 |
25 | 1 1.00794
26 | 2 15.9994
27 |
28 | Pair Coeffs #lj/cut/coul/long 9.0 7.0
29 |
30 | 1 0.000 0.000
31 | 2 0.15587 3.1780
32 |
33 | Bond Coeffs #harmonic
34 |
35 | 1 1000 1.0118
36 |
37 | Angle Coeffs #harmonic
38 |
39 | 1 1000 108.15
40 |
41 | Atoms
42 |
43 | 1 1 2 -0.84844 1.55000 1.55000 1.50000
44 | 2 1 1 0.42422 1.55000 2.36649 2.07736
45 | 3 1 1 0.42422 1.55000 0.73351 2.07736
46 |
47 | Bonds
48 |
49 | 1 1 1 2
50 | 2 1 1 3
51 |
52 | Angles
53 |
54 | 1 1 2 1 3
55 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_tip4p_2005.lmp:
--------------------------------------------------------------------------------
1 | This forcefield file sets a 13-Angstrom cutoff, recommended for liquid-vapor simulations
2 | # [Vega & de Miguel, J Chem Phys 126:154707 (2007), Vega et al, Faraday Discuss 141:251 (2009)]. Note that the original TIP4P/2005 model was run with an 8.5 Angstrom cutoff [Abascal & Vega, J Chem Phys 123:234505 (2005)].
3 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
4 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
5 |
6 | 3 atoms
7 | 2 bonds
8 | 1 angles
9 |
10 | 2 atom types
11 | 1 bond types
12 | 1 angle types
13 |
14 | 0.0 3.1 xlo xhi
15 | 0.0 3.1 ylo yhi
16 | 0.0 3.1 zlo zhi
17 |
18 | Masses
19 |
20 | 1 1.00794
21 | 2 15.9994
22 |
23 | Pair Coeffs #lj/cut/tip4p/long 2 1 1 1 0.1546 13.0
24 |
25 | 1 0.000 0.000
26 | 2 0.18520 3.1589
27 |
28 | Bond Coeffs #harmonic
29 |
30 | 1 1000 0.9572
31 |
32 | Angle Coeffs #harmonic
33 |
34 | 1 1000 104.52
35 |
36 | Atoms
37 |
38 | 1 1 2 -1.1128 1.55000 1.55000 1.50000
39 | 2 1 1 0.5564 1.55000 2.36649 2.07736
40 | 3 1 1 0.5564 1.55000 0.73351 2.07736
41 |
42 | Bonds
43 |
44 | 1 1 1 2
45 | 2 1 1 3
46 |
47 | Angles
48 |
49 | 1 1 2 1 3
50 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_tip4p_ew.lmp:
--------------------------------------------------------------------------------
1 | Optimized TIP4P parameters to use with Ewald methods for long-range electrostatics. (e.g. pppm/tip4p 1.0e-5)
2 | # Original model from Horn et al, J Chem Phys 120: 9665 (2004). Command 'fix shake' is needed.
3 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
4 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
5 |
6 | 3 atoms
7 | 2 bonds
8 | 1 angles
9 |
10 | 2 atom types
11 | 1 bond types
12 | 1 angle types
13 |
14 | 0.0 3.1 xlo xhi
15 | 0.0 3.1 ylo yhi
16 | 0.0 3.1 zlo zhi
17 |
18 | Masses
19 |
20 | 1 1.00794
21 | 2 15.9994
22 |
23 | Pair Coeffs #lj/cut/tip4p/long 2 1 1 1 0.125 10.0
24 |
25 | 1 0.000 0.000
26 | 2 0.16275 3.16435
27 |
28 | Bond Coeffs #harmonic
29 |
30 | 1 1000 0.9572
31 |
32 | Angle Coeffs #harmonic
33 |
34 | 1 1000 104.52
35 |
36 | Atoms
37 |
38 | 1 1 2 -1.04844 1.55000 1.55000 1.50000
39 | 2 1 1 0.52422 1.55000 2.36649 2.07736
40 | 3 1 1 0.52422 1.55000 0.73351 2.07736
41 |
42 | Bonds
43 |
44 | 1 1 1 2
45 | 2 1 1 3
46 |
47 | Angles
48 |
49 | 1 1 2 1 3
50 |
--------------------------------------------------------------------------------
/mdgo/forcefield/data/water/water_tip4p_fb.lmp:
--------------------------------------------------------------------------------
1 | Optimized TIP4P-FB parameters for water
2 | # Original model from Wang et al., Building Force Fields: An Automatic,
3 | # Systematic, and Reproducible Approach, J. Phys. Chem. Letters 5(11), 2014.
4 | # https://pubs.acs.org/doi/10.1021/jz500737m
5 | # real units (kcal/mol, Angstrom)
6 | # Command 'fix shake' is needed.
7 | # LJ and couloumb cutoffs of 9 and 7 angstroms were used in the original work
8 | # Bond and angle coefficients of 1000 are added to keep the water molecule together
9 | # during e.g. energy minimization in LAMMPS when `fix shake` is not active.
10 |
11 | 3 atoms
12 | 2 bonds
13 | 1 angles
14 |
15 | 2 atom types
16 | 1 bond types
17 | 1 angle types
18 |
19 | 0.0 3.1 xlo xhi
20 | 0.0 3.1 ylo yhi
21 | 0.0 3.1 zlo zhi
22 |
23 | Masses
24 |
25 | 1 1.00794
26 | 2 15.9994
27 |
28 | Pair Coeffs #lj/cut/tip4p/long 2 1 1 1 0.10527 9.0 7.0
29 |
30 | 1 0.000 0.000
31 | 2 0.17908 3.1655
32 |
33 | Bond Coeffs #harmonic
34 |
35 | 1 1000 0.9572
36 |
37 | Angle Coeffs #harmonic
38 |
39 | 1 1000 104.52
40 |
41 | Atoms
42 |
43 | 1 1 2 -1.05174 1.55000 1.55000 1.50000
44 | 2 1 1 0.52587 1.55000 2.36649 2.07736
45 | 3 1 1 0.52587 1.55000 0.73351 2.07736
46 |
47 | Bonds
48 |
49 | 1 1 1 2
50 | 2 1 1 3
51 |
52 | Angles
53 |
54 | 1 1 2 1 3
55 |
--------------------------------------------------------------------------------
/mdgo/forcefield/maestro.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """
5 | This module implements a core class MaestroRunner for generating
6 | LAMMPS/GROMACS data files from molecule structure using Maestro.
7 |
8 | For using the MaestroRunner class:
9 |
10 | * Download a free Maestro via https://www.schrodinger.com/freemaestro
11 |
12 | * Install the package and set the environment variable $SCHRODINGER
13 | (e.g. 'export SCHRODINGER=/opt/schrodinger/suites2021-4', please
14 | check https://www.schrodinger.com/kb/446296 or
15 | https://www.schrodinger.com/kb/1842 for details.
16 |
17 | """
18 |
19 | from __future__ import annotations
20 |
21 | import os
22 | import signal
23 | import subprocess
24 | import time
25 | from string import Template
26 | from typing import Final
27 |
28 | from mdgo.util.reformat import ff_parser
29 |
30 | MAESTRO: Final[str] = "$SCHRODINGER/maestro -console -nosplash"
31 | FFLD: Final[str] = "$SCHRODINGER/utilities/ffld_server -imae {} -version 14 -print_parameters -out_file {}"
32 | MODULE_DIR: Final[str] = os.path.dirname(os.path.abspath(__file__))
33 |
34 |
35 | class MaestroRunner:
36 | """
37 | Wrapper for the Maestro software that can be used to generate the OPLS_2005
38 | force field parameter for a molecule.
39 |
40 | Args:
41 | structure_dir: Path to the structure file.
42 | Supported input format please check
43 | https://www.schrodinger.com/kb/1278
44 | working_dir: Directory for writing intermediate
45 | and final output.
46 | out: Force field output form. Default to "lmp",
47 | the data file for LAMMPS. Other supported formats
48 | are under development.
49 | cmd_template: String template for input script
50 | with placeholders. Default to None, i.e., using
51 | the default template.
52 | assign_bond: Whether to assign bond to the input
53 | structure. Default to None.
54 |
55 | Supported input format please check https://www.schrodinger.com/kb/1278
56 |
57 | The OPLS_2005 parameters are described in
58 |
59 | Banks, J.L.; Beard, H.S.; Cao, Y.; Cho, A.E.; Damm, W.; Farid, R.;
60 | Felts, A.K.; Halgren, T.A.; Mainz, D.T.; Maple, J.R.; Murphy, R.;
61 | Philipp, D.M.; Repasky, M.P.; Zhang, L.Y.; Berne, B.J.; Friesner, R.A.;
62 | Gallicchio, E.; Levy. R.M. Integrated Modeling Program, Applied Chemical
63 | Theory (IMPACT). J. Comp. Chem. 2005, 26, 1752.
64 |
65 | The OPLS_2005 parameters are located in
66 |
67 | $SCHRODINGER/mmshare-vversion/data/f14/
68 |
69 | Examples:
70 | >>> mr = MaestroRunner('/path/to/structure', '/path/to/working/dir')
71 | >>> mr.get_mae()
72 | >>> mr.get_ff()
73 | """
74 |
75 | template_assignbond = os.path.join(MODULE_DIR, "..", "templates", "mae_cmd_assignbond.txt")
76 |
77 | template_noassignbond = os.path.join(MODULE_DIR, "..", "templates", "mae_cmd_noassignbond.txt")
78 |
79 | def __init__(
80 | self,
81 | structure_dir: str,
82 | working_dir: str,
83 | out: str = "lmp",
84 | cmd_template: str | None = None,
85 | assign_bond: bool = False,
86 | ):
87 | """Base constructor."""
88 | self.structure = structure_dir
89 | self.out = out
90 | self.structure_format = os.path.splitext(self.structure)[1][1:]
91 | self.name = os.path.splitext(os.path.split(self.structure)[-1])[0]
92 | print("Input format:", self.structure_format)
93 | self.work = working_dir
94 | self.cmd = os.path.join(self.work, "maetro_script.cmd")
95 | self.mae = os.path.join(self.work, self.name)
96 | self.ff = os.path.join(self.work, self.name + ".out")
97 | self.xyz = os.path.join(self.work, self.name + ".xyz")
98 | if cmd_template:
99 | self.cmd_template = cmd_template
100 | else:
101 | if assign_bond:
102 | with open(self.template_assignbond) as f:
103 | cmd_template = f.read()
104 | self.cmd_template = cmd_template
105 | else:
106 | with open(self.template_noassignbond) as f:
107 | cmd_template = f.read()
108 | self.cmd_template = cmd_template
109 |
110 | def get_mae(self, wait: float = 30):
111 | """Write a Maestro command script and execute it to generate a
112 | maestro file containing all the info needed.
113 |
114 | Args:
115 | wait: The time waiting for Maestro execution in seconds. Default to 30.
116 | """
117 | with open(self.cmd, "w") as f:
118 | cmd_template = Template(self.cmd_template)
119 | cmd_script = cmd_template.substitute(file=self.structure, mae=self.mae, xyz=self.xyz)
120 | f.write(cmd_script)
121 | try:
122 | p = subprocess.Popen( # pylint: disable=consider-using-with
123 | f"{MAESTRO} -c {self.cmd}",
124 | shell=True,
125 | stdout=subprocess.PIPE,
126 | stderr=subprocess.PIPE,
127 | start_new_session=True,
128 | )
129 | except subprocess.CalledProcessError as e:
130 | raise ValueError(f"Maestro failed with errorcode {e.returncode} and stderr: {e.stderr}") from e
131 |
132 | counter = 0
133 | while not os.path.isfile(self.mae + ".mae"):
134 | time.sleep(1)
135 | counter += 1
136 | if counter > wait:
137 | os.killpg(os.getpgid(p.pid), signal.SIGTERM)
138 | raise TimeoutError(f"Failed to generate Maestro file in {wait} secs!")
139 | print("Maestro file generated!")
140 | os.killpg(os.getpgid(p.pid), signal.SIGTERM)
141 |
142 | def get_ff(self):
143 | """Read the Maestro file and save the force field as LAMMPS data file."""
144 | try:
145 | subprocess.run(
146 | FFLD.format(self.mae + ".mae", self.ff),
147 | check=True,
148 | shell=True,
149 | capture_output=True,
150 | )
151 | except subprocess.CalledProcessError as e:
152 | raise ValueError(f"Maestro failed with errorcode {e.returncode} and stderr: {e.stderr}") from e
153 | print("Maestro force field file generated.")
154 | if self.out:
155 | if self.out == "lmp":
156 | with open(os.path.join(self.work, self.name + "." + self.out), "w") as f:
157 | f.write(ff_parser(self.ff, self.xyz))
158 | print("LAMMPS data file generated.")
159 | else:
160 | print("Output format not supported, ff format not converted.")
161 |
--------------------------------------------------------------------------------
/mdgo/residence_time.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """This module calculates species correlation lifetime (residence time)."""
5 |
6 | from __future__ import annotations
7 |
8 | import os
9 | from typing import TYPE_CHECKING
10 |
11 | import matplotlib.pyplot as plt
12 | import numpy as np
13 | from scipy.optimize import curve_fit
14 | from statsmodels.tsa.stattools import acovf
15 | from tqdm.auto import tqdm
16 |
17 | if TYPE_CHECKING:
18 | from MDAnalysis import Universe
19 | from MDAnalysis.core.groups import Atom
20 |
21 | __author__ = "Kara Fong, Tingzheng Hou"
22 | __version__ = "0.3.0"
23 | __maintainer__ = "Tingzheng Hou"
24 | __email__ = "tingzheng_hou@berkeley.edu"
25 | __date__ = "Jul 19, 2021"
26 |
27 |
28 | def neighbors_one_atom(
29 | nvt_run: Universe,
30 | center_atom: Atom,
31 | species: str,
32 | select_dict: dict[str, str],
33 | distance: float,
34 | run_start: int,
35 | run_end: int,
36 | ) -> dict[str, np.ndarray]:
37 | """
38 | Create adjacency matrix for one center atom.
39 |
40 | Args:
41 | nvt_run: An MDAnalysis ``Universe``.
42 | center_atom: The center atom object.
43 | species: The neighbor species in the select_dict.
44 | select_dict: A dictionary of atom species selection, where each atom species name is a key
45 | and the corresponding values are the selection language.
46 | distance: The neighbor cutoff distance.
47 | run_start: Start frame of analysis.
48 | run_end: End frame of analysis.
49 |
50 | Returns:
51 | A neighbor dict with neighbor atom id as keys and arrays of adjacent boolean (0/1) as values.
52 | """
53 | bool_values = {}
54 | for time_count, _ts in enumerate(nvt_run.trajectory[run_start:run_end:]):
55 | if species in select_dict:
56 | selection = (
57 | "("
58 | + select_dict[species]
59 | + ") and (around "
60 | + str(distance)
61 | + " index "
62 | + str(center_atom.id - 1)
63 | + ")"
64 | )
65 | shell = nvt_run.select_atoms(selection)
66 | else:
67 | raise ValueError("Invalid species selection")
68 | for atom in shell.atoms:
69 | if str(atom.id) not in bool_values:
70 | bool_values[str(atom.id)] = np.zeros(int((run_end - run_start) / 1))
71 | bool_values[str(atom.id)][time_count] = 1
72 | return bool_values
73 |
74 |
75 | def calc_acf(a_values: dict[str, np.ndarray]) -> list[np.ndarray]:
76 | """
77 | Calculate auto-correlation function (ACF).
78 |
79 | Args:
80 | a_values: A dict of adjacency matrix with neighbor atom id as keys and arrays
81 | of adjacent boolean (0/1) as values.
82 |
83 | Returns:
84 | A list of auto-correlation functions for each neighbor species.
85 | """
86 | acfs = []
87 | for neighbors in a_values.values(): # for _atom_id, neighbors in a_values.items():
88 | # atom_id_numeric = int(re.search(r"\d+", _atom_id).group())
89 | acfs.append(acovf(neighbors, demean=False, unbiased=True, fft=True))
90 | return acfs
91 |
92 |
93 | def exponential_func(
94 | x: float | np.floating | np.ndarray,
95 | a: float | np.floating | np.ndarray,
96 | b: float | np.floating | np.ndarray,
97 | c: float | np.floating | np.ndarray,
98 | ) -> np.floating | np.ndarray:
99 | """
100 | An exponential decay function.
101 |
102 | Args:
103 | x: Independent variable.
104 | a: Initial quantity.
105 | b: Exponential decay constant.
106 | c: Constant.
107 |
108 | Returns:
109 | The acf
110 | """
111 | return a * np.exp(-b * x) + c
112 |
113 |
114 | def calc_neigh_corr(
115 | nvt_run: Universe,
116 | distance_dict: dict[str, float],
117 | select_dict: dict[str, str],
118 | time_step: float,
119 | run_start: int,
120 | run_end: int,
121 | center_atom: str = "cation",
122 | ) -> tuple[np.ndarray, dict[str, np.ndarray]]:
123 | """Calculates the neighbor auto-correlation function (ACF)
124 | of selected species around center atom.
125 |
126 | Args:
127 | nvt_run: An MDAnalysis ``Universe``.
128 | distance_dict: A dict of coordination cutoff distance of the neighbor species.
129 | select_dict: A dictionary of atom species selection.
130 | time_step: Timestep between each frame, in ps.
131 | run_start: Start frame of analysis.
132 | run_end: End frame of analysis.
133 | center_atom: The center atom to calculate the ACF for. Default to "cation".
134 |
135 | Returns:
136 | A tuple containing the time series, and a dict of acf of neighbor species.
137 | """
138 | # Set up times array
139 | times = []
140 | center_atoms = nvt_run.select_atoms(select_dict[center_atom])
141 | for step, _ts in enumerate(nvt_run.trajectory[run_start:run_end]):
142 | times.append(step * time_step)
143 | times = np.array(times)
144 |
145 | acf_avg = {}
146 | for kw in distance_dict:
147 | acf_all = []
148 | for atom in tqdm(center_atoms[::]):
149 | distance = distance_dict.get(kw)
150 | assert distance is not None
151 | adjacency_matrix = neighbors_one_atom(
152 | nvt_run,
153 | atom,
154 | kw,
155 | select_dict,
156 | distance,
157 | run_start,
158 | run_end,
159 | )
160 | acfs = calc_acf(adjacency_matrix)
161 | acf_all.extend(list(acfs))
162 | acf_avg[kw] = np.mean(acf_all, axis=0)
163 | return times, acf_avg
164 |
165 |
166 | def fit_residence_time(
167 | times: np.ndarray,
168 | acf_avg_dict: dict[str, np.ndarray],
169 | cutoff_time: int,
170 | time_step: float,
171 | save_curve: str | bool = False,
172 | ) -> dict[str, np.floating]:
173 | """
174 | Use the ACF to fit the residence time (Exponential decay constant).
175 | TODO: allow defining the residence time according to a threshold value of the decay.
176 |
177 | Args:
178 | times: A time series.
179 | acf_avg_dict: A dict containing the ACFs of the species.
180 | cutoff_time: Fitting cutoff time.
181 | time_step: The time step between each frame, in ps.
182 | save_curve: Whether to save the curve as a csv file for post-processing.
183 | Default to False.
184 |
185 | Returns:
186 | A dict containing residence time of each species
187 | """
188 | acf_avg_norm = {}
189 | popt = {}
190 | pcov = {}
191 | tau = {}
192 | species_list = list(acf_avg_dict.keys())
193 |
194 | # Exponential fit of solvent-Li ACF
195 | for kw in species_list:
196 | acf_avg_norm[kw] = acf_avg_dict[kw] / acf_avg_dict[kw][0]
197 | popt[kw], pcov[kw] = curve_fit(
198 | exponential_func,
199 | times[:cutoff_time],
200 | acf_avg_norm[kw][:cutoff_time],
201 | p0=(1, 1e-4, 0),
202 | )
203 | tau[kw] = 1 / popt[kw][1] # ps
204 |
205 | # Plot ACFs
206 | colors = ["b", "g", "r", "c", "m", "y"]
207 | line_styles = ["-", "--", "-.", ":"]
208 | for i, kw in enumerate(species_list):
209 | plt.plot(times, acf_avg_norm[kw], label=kw, color=colors[i])
210 | fitted_x = np.linspace(0, cutoff_time * time_step, cutoff_time)
211 | fitted_y = exponential_func(np.linspace(0, cutoff_time * time_step, cutoff_time), *popt[kw])
212 | save_decay = np.vstack(
213 | (
214 | times[:cutoff_time],
215 | acf_avg_norm[kw][:cutoff_time],
216 | fitted_x,
217 | fitted_y,
218 | )
219 | )
220 | if save_curve:
221 | if save_curve is True:
222 | np.savetxt(f"decay{i}.csv", save_decay.T, delimiter=",")
223 | elif os.path.exists(str(save_curve)):
224 | np.savetxt(str(save_curve) + f"decay{i}.csv", save_decay.T, delimiter=",")
225 | else:
226 | raise ValueError("Please specify a bool or a path in string.")
227 | plt.plot(
228 | fitted_x,
229 | fitted_y,
230 | line_styles[i],
231 | color="k",
232 | label=kw + " Fit",
233 | )
234 |
235 | plt.xlabel("Time (ps)")
236 | plt.legend()
237 | plt.ylabel("Neighbor Auto-correlation Function")
238 | plt.ylim(0, 1)
239 | plt.xlim(0, cutoff_time * time_step)
240 | plt.show()
241 |
242 | return tau
243 |
--------------------------------------------------------------------------------
/mdgo/templates/mae_cmd_assignbond.txt:
--------------------------------------------------------------------------------
1 | entryimport $file
2 | beginundoblock Assign Bond Orders
3 | pythonrunbuiltin fix_bond_orders.fix_bond_orders
4 | endundoblock
5 | assigncharges
6 | assignforcefieldtypes
7 | prefer savescratchproject=false
8 | entryexport format=xyz $xyz
9 | entryexport format=maestro $mae
10 |
11 |
--------------------------------------------------------------------------------
/mdgo/templates/mae_cmd_noassignbond.txt:
--------------------------------------------------------------------------------
1 | entryimport $file
2 | assigncharges
3 | assignforcefieldtypes
4 | prefer savescratchproject=false
5 | entryexport format=xyz $xyz
6 | entryexport format=maestro $mae
7 |
8 |
--------------------------------------------------------------------------------
/mdgo/util/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """The util package implements various utilities that are commonly used by various packages."""
5 |
6 | from __future__ import annotations
7 |
8 | __author__ = "Tingzheng Hou"
9 | __version__ = "0.3.0"
10 | __maintainer__ = "Tingzheng Hou"
11 | __email__ = "tingzheng_hou@berkeley.edu"
12 | __date__ = "Jul 19, 2021"
13 |
14 | from typing import Final
15 |
16 | MM_of_Elements: Final[dict[str, float]] = {
17 | "H": 1.00794,
18 | "He": 4.002602,
19 | "Li": 6.941,
20 | "Be": 9.012182,
21 | "B": 10.811,
22 | "C": 12.0107,
23 | "N": 14.0067,
24 | "O": 15.9994,
25 | "F": 18.9984032,
26 | "Ne": 20.1797,
27 | "Na": 22.98976928,
28 | "Mg": 24.305,
29 | "Al": 26.9815386,
30 | "Si": 28.0855,
31 | "P": 30.973762,
32 | "S": 32.065,
33 | "Cl": 35.453,
34 | "Ar": 39.948,
35 | "K": 39.0983,
36 | "Ca": 40.078,
37 | "Sc": 44.955912,
38 | "Ti": 47.867,
39 | "V": 50.9415,
40 | "Cr": 51.9961,
41 | "Mn": 54.938045,
42 | "Fe": 55.845,
43 | "Co": 58.933195,
44 | "Ni": 58.6934,
45 | "Cu": 63.546,
46 | "Zn": 65.409,
47 | "Ga": 69.723,
48 | "Ge": 72.64,
49 | "As": 74.9216,
50 | "Se": 78.96,
51 | "Br": 79.904,
52 | "Kr": 83.798,
53 | "Rb": 85.4678,
54 | "Sr": 87.62,
55 | "Y": 88.90585,
56 | "Zr": 91.224,
57 | "Nb": 92.90638,
58 | "Mo": 95.94,
59 | "Tc": 98.9063,
60 | "Ru": 101.07,
61 | "Rh": 102.9055,
62 | "Pd": 106.42,
63 | "Ag": 107.8682,
64 | "Cd": 112.411,
65 | "In": 114.818,
66 | "Sn": 118.71,
67 | "Sb": 121.760,
68 | "Te": 127.6,
69 | "I": 126.90447,
70 | "Xe": 131.293,
71 | "Cs": 132.9054519,
72 | "Ba": 137.327,
73 | "La": 138.90547,
74 | "Ce": 140.116,
75 | "Pr": 140.90465,
76 | "Nd": 144.242,
77 | "Pm": 146.9151,
78 | "Sm": 150.36,
79 | "Eu": 151.964,
80 | "Gd": 157.25,
81 | "Tb": 158.92535,
82 | "Dy": 162.5,
83 | "Ho": 164.93032,
84 | "Er": 167.259,
85 | "Tm": 168.93421,
86 | "Yb": 173.04,
87 | "Lu": 174.967,
88 | "Hf": 178.49,
89 | "Ta": 180.9479,
90 | "W": 183.84,
91 | "Re": 186.207,
92 | "Os": 190.23,
93 | "Ir": 192.217,
94 | "Pt": 195.084,
95 | "Au": 196.966569,
96 | "Hg": 200.59,
97 | "Tl": 204.3833,
98 | "Pb": 207.2,
99 | "Bi": 208.9804,
100 | "Po": 208.9824,
101 | "At": 209.9871,
102 | "Rn": 222.0176,
103 | "Fr": 223.0197,
104 | "Ra": 226.0254,
105 | "Ac": 227.0278,
106 | "Th": 232.03806,
107 | "Pa": 231.03588,
108 | "U": 238.02891,
109 | "Np": 237.0482,
110 | "Pu": 244.0642,
111 | "Am": 243.0614,
112 | "Cm": 247.0703,
113 | "Bk": 247.0703,
114 | "Cf": 251.0796,
115 | "Es": 252.0829,
116 | "Fm": 257.0951,
117 | "Md": 258.0951,
118 | "No": 259.1009,
119 | "Lr": 262,
120 | "Rf": 267,
121 | "Db": 268,
122 | "Sg": 271,
123 | "Bh": 270,
124 | "Hs": 269,
125 | "Mt": 278,
126 | "Ds": 281,
127 | "Rg": 281,
128 | "Cn": 285,
129 | "Nh": 284,
130 | "Fl": 289,
131 | "Mc": 289,
132 | "Lv": 292,
133 | "Ts": 294,
134 | "Og": 294,
135 | "ZERO": 0,
136 | }
137 |
--------------------------------------------------------------------------------
/mdgo/util/coord.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """Utilities for manipulating coordinates under periodic boundary conditions."""
5 |
6 | from __future__ import annotations
7 |
8 | from typing import TYPE_CHECKING
9 |
10 | import numpy as np
11 |
12 | if TYPE_CHECKING:
13 | from MDAnalysis.core.groups import Atom
14 |
15 |
16 | def atom_vec(atom1: Atom, atom2: Atom, dimension: np.ndarray) -> np.ndarray:
17 | """
18 | Calculate the vector of the positions from atom2 to atom1.
19 |
20 | Args:
21 | atom1: Atom obj 1.
22 | atom2: Atom obj 2.
23 | dimension: box dimension.
24 |
25 | Return:
26 | The obtained vector
27 | """
28 | vec = [0, 0, 0]
29 | for i in range(3):
30 | diff = atom1.position[i] - atom2.position[i]
31 | if diff > dimension[i] / 2:
32 | vec[i] = diff - dimension[i]
33 | elif diff < -dimension[i] / 2:
34 | vec[i] = diff + dimension[i]
35 | else:
36 | vec[i] = diff
37 | return np.array(vec)
38 |
39 |
40 | def position_vec(
41 | pos1: list[float] | np.ndarray,
42 | pos2: list[float] | np.ndarray,
43 | dimension: list[float] | np.ndarray,
44 | ) -> np.ndarray:
45 | """
46 | Calculate the vector from pos2 to pos2.
47 |
48 | Args:
49 | pos1: Array of 3d coordinates 1.
50 | pos2: Array of 3d coordinates 2.
51 | dimension: box dimension.
52 |
53 | Return:
54 | The obtained vector.
55 | """
56 | vec: list[int | float | np.floating] = [0, 0, 0]
57 | for i in range(3):
58 | diff = pos1[i] - pos2[i]
59 | if diff > dimension[i] / 2:
60 | vec[i] = diff - dimension[i]
61 | elif diff < -dimension[i] / 2:
62 | vec[i] = diff + dimension[i]
63 | else:
64 | vec[i] = diff
65 | return np.array(vec)
66 |
67 |
68 | def angle(a: np.ndarray, b: np.ndarray, c: np.ndarray) -> np.floating:
69 | """
70 | Calculate the angle between three atoms.
71 |
72 | Args:
73 | a: Coordinates of atom A.
74 | b: Coordinates of atom B.
75 | c: Coordinates of atom C.
76 |
77 | Returns:
78 | The degree A-B-C.
79 | """
80 | ba = a - b
81 | bc = c - b
82 | cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
83 | cosine_angle = np.clip(cosine_angle, -1.0, 1.0)
84 | angle_in_radian = np.arccos(cosine_angle)
85 | return np.degrees(angle_in_radian)
86 |
--------------------------------------------------------------------------------
/mdgo/util/num.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """Utilities for manipulating numbers in data structures."""
5 |
6 | from __future__ import annotations
7 |
8 |
9 | def strip_zeros(items: list[str | float | int] | str) -> list[int] | None:
10 | """
11 | Strip the trailing zeros of a sequence.
12 |
13 | Args:
14 | items: The sequence.
15 |
16 | Return:
17 | A new list of numbers.
18 | """
19 | new_items = [int(i) for i in items]
20 | while new_items[-1] == 0:
21 | new_items.pop()
22 | while new_items[0] == 0:
23 | new_items.pop(0)
24 | if len(new_items) == 0:
25 | return None
26 | return new_items
27 |
--------------------------------------------------------------------------------
/mdgo/util/packmol.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """
5 | This module implements a core class PackmolWrapper for packing molecules
6 | into a single box.
7 |
8 | You need the Packmol package to run the code, see
9 | http://m3g.iqm.unicamp.br/packmol or
10 | http://leandro.iqm.unicamp.br/m3g/packmol/home.shtml
11 | for download and setup instructions. You may need to manually
12 | set the folder of the packmol executable to the PATH environment variable.
13 | """
14 |
15 | from __future__ import annotations
16 |
17 | import os
18 | import subprocess
19 | from pathlib import Path
20 | from shutil import which
21 |
22 | from pymatgen.core import Molecule
23 |
24 | # from pymatgen.io.core import InputFile, InputSet, InputGenerator
25 | from mdgo.util.volume import molecular_volume
26 |
27 | __author__ = "Tingzheng Hou, Ryan Kingsbury"
28 | __version__ = "1.0"
29 | __maintainer__ = "Tingzheng Hou"
30 | __email__ = "tingzheng_hou@berkeley.edu"
31 | __date__ = "Feb - Oct, 2021"
32 |
33 |
34 | # TODO - inherit from InputSet
35 | # consider adding a generator
36 | class PackmolWrapper:
37 | """
38 | Wrapper for the Packmol software that can be used to pack various types of
39 | molecules into a one single unit.
40 |
41 | Examples:
42 | >>> molecules = [{"name": "EMC",
43 | "number": 2,
44 | "coords": "/Users/th/Downloads/test_selenium/EMC.lmp.xyz"}]
45 | >>> pw = PackmolWrapper("/path/to/work/dir",
46 | ... molecules,
47 | ... [0., 0., 0., 10., 10., 10.]
48 | ... )
49 | >>> pw.make_packmol_input()
50 | >>> pw.run_packmol()
51 | """
52 |
53 | def __init__(
54 | self,
55 | path: str,
56 | molecules: list[dict],
57 | box: list[float] | None = None,
58 | tolerance: float = 2.0,
59 | seed: int = 1,
60 | control_params: dict | None = None,
61 | inputfile: str | Path = "packmol.inp",
62 | outputfile: str | Path = "packmol_out.xyz",
63 | ):
64 | """
65 | Args:
66 | path: The path to the directory for file i/o. Note that the path
67 | cannot contain any spaces.
68 | molecules: A list of dict containing information about molecules to pack
69 | into the box. Each dict requires three keys:
70 | 1. "name" - the structure name
71 | 2. "number" - the number of that molecule to pack into the box
72 | 3. "coords" - Coordinates in the form of either a Molecule object or
73 | a path to a file.
74 | For Example,
75 | {"name": "water",
76 | "number": 500,
77 | "coords": "/path/to/input/file.xyz"}
78 | box: A list of box dimensions xlo, ylo, zlo, xhi, yhi, zhi, in Å. If set to None
79 | (default), mdgo will estimate the required box size based on the volumes of
80 | the provided molecules using mdgo.volume.molecular_volume()
81 | tolerance: Tolerance for packmol, in Å.
82 | seed: Random seed for packmol. Use a value of 1 (default) for deterministic
83 | output, or -1 to generate a new random seed from the current time.
84 | control_params: Specify custom control parapeters, e,g, "maxit" and "nloop", in a dict
85 | inputfile: Path to the input file. Default to 'packmol.inp'.
86 | outputfile: Path to the output file. Default to 'output.xyz'.
87 | """
88 | self.path = path
89 | self.input = os.path.join(self.path, inputfile)
90 | self.output = os.path.join(self.path, outputfile)
91 | self.screen = os.path.join(self.path, "packmol.stdout")
92 | self.molecules = molecules
93 | self.control_params = control_params if control_params else {}
94 | self.box = box
95 | self.tolerance = tolerance
96 | self.seed = seed
97 |
98 | def run_packmol(self, timeout=30):
99 | """Run packmol and write out the packed structure.
100 |
101 | Args:
102 | timeout: Timeout in seconds.
103 |
104 | Raises:
105 | ValueError if packmol does not succeed in packing the box.
106 | TimeoutExpiredError if packmold does not finish within the timeout.
107 | """
108 | if not which("packmol"):
109 | raise RuntimeError(
110 | "PackmolWrapper requires the executable 'packmol' to be in "
111 | "the path. Please download packmol from "
112 | "https://github.com/leandromartinez98/packmol "
113 | "and follow the instructions in the README to compile. "
114 | "Don't forget to add the packmol binary to your path"
115 | )
116 | try:
117 | p = subprocess.run(
118 | f"packmol < '{self.input}'",
119 | check=True,
120 | shell=True,
121 | timeout=timeout,
122 | capture_output=True,
123 | )
124 | # this workaround is needed because packmol can fail to find
125 | # a solution but still return a zero exit code
126 | # see https://github.com/m3g/packmol/issues/28
127 | if "ERROR" in p.stdout.decode():
128 | msg = p.stdout.decode().split("ERROR")[-1]
129 | if "Could not open file." in p.stdout.decode():
130 | raise ValueError(
131 | "Your packmol might be too old to handle paths with spaces."
132 | "Please try again with a newer version or use paths without spaces."
133 | f"Packmol failed with return code 0 and stdout: {msg}"
134 | )
135 | raise ValueError(f"Packmol failed with return code 0 and stdout: {msg}")
136 | except subprocess.CalledProcessError as e:
137 | raise ValueError(f"Packmol failed with errorcode {e.returncode} and stderr: {e.stderr}") from e
138 | else:
139 | with open(self.screen, "w") as out:
140 | out.write(p.stdout.decode())
141 |
142 | # TODO - should be in InputSet.get_inputs() or Inputfile.get_string()
143 | # maybe don't define an Inputfile class, just keep internal to
144 | # InputSet
145 | def make_packmol_input(self):
146 | """Make a Packmol usable input file."""
147 | if self.box:
148 | box_list = " ".join(str(i) for i in self.box)
149 | else:
150 | # estimate the total volume of all molecules
151 | net_volume = 0.0
152 | for _idx, d in enumerate(self.molecules):
153 | mol = Molecule.from_file(d["coords"]) if not isinstance(d["coords"], Molecule) else d["coords"]
154 | # molecular volume in cubic Å
155 | vol = molecular_volume(mol, radii_type="pymatgen", molar_volume=False)
156 | # pad the calculated length by an amount related to the tolerance parameter
157 | # the amount to add was determined arbitrarily
158 | vol *= self.tolerance
159 | net_volume += vol * d["number"]
160 |
161 | box_length = net_volume ** (1.0 / 3.0)
162 | print(f"Auto determined box size is {box_length:.1f} Å per side.")
163 | box_list = f"0.0 0.0 0.0 {box_length:.1f} {box_length:.1f} {box_length:.1f}"
164 |
165 | with open(self.input, "w") as out:
166 | out.write("# " + " + ".join(str(d["number"]) + " " + d["name"] for d in self.molecules) + "\n")
167 | out.write("# Packmol input generated by mdgo.\n")
168 | for k, v in self.control_params.items():
169 | if isinstance(v, list):
170 | out.write(f'{k} {" ".join(str(x) for x in v)}\n')
171 | else:
172 | out.write(f"{k} {v!s}\n")
173 | out.write(f"seed {self.seed}\n")
174 | out.write(f"tolerance {self.tolerance}\n\n")
175 |
176 | out.write("filetype xyz\n\n")
177 | if " " in str(self.output):
178 | out.write(f'output "{self.output}"\n\n')
179 | else:
180 | out.write(f"output {self.output}\n\n")
181 |
182 | for _i, d in enumerate(self.molecules):
183 | if isinstance(d["coords"], str):
184 | if " " in d["coords"]:
185 | out.write(f'structure "{d["coords"]}"\n')
186 | else:
187 | out.write(f'structure {d["coords"]}\n')
188 | elif isinstance(d["coords"], Path):
189 | if " " in str(d["coords"]):
190 | out.write(f'structure "{d["coords"]!s}"\n')
191 | else:
192 | out.write(f'structure {d["coords"]!s}\n')
193 | elif isinstance(d["coords"], Molecule):
194 | fname = os.path.join(self.path, f'packmol_{d["name"]}.xyz')
195 | d["coords"].to(filename=fname)
196 | if " " in str(fname):
197 | out.write(f'structure "{fname}"\n')
198 | else:
199 | out.write(f"structure {fname}\n")
200 | out.write(f' number {d["number"]!s}\n')
201 | out.write(f" inside box {box_list}\n")
202 | out.write("end structure\n\n")
203 |
204 |
205 | if __name__ == "__main__":
206 | """
207 | molecules = [{"name": "EMC",
208 | "number": 2,
209 | "coords": "/Users/th/Downloads/test_selenium/EMC.lmp.xyz"}]
210 | pw = PackmolWrapper("/Users/th/Downloads/test_selenium/", molecules,
211 | [0., 0., 0., 10., 10., 10.])
212 | pw.make_packmol_input()
213 | pw.run_packmol()
214 | """
215 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools>=65.0.0",
4 | ]
5 | build-backend = "setuptools.build_meta"
6 |
7 | [tool.ruff]
8 | target-version = "py39"
9 | line-length = 120
10 | lint.select = [
11 | "B", # flake8-bugbear
12 | "C4", # flake8-comprehensions
13 | "D", # pydocstyle
14 | "E", # pycodestyle error
15 | "EXE", # flake8-executable
16 | "F", # pyflakes
17 | "FA", # flake8-future-annotations
18 | "FBT003", # boolean-positional-value-in-call
19 | "FLY", # flynt
20 | "I", # isort
21 | "ICN", # flake8-import-conventions
22 | "ISC", # flake8-implicit-str-concat
23 | "PD", # pandas-vet
24 | "PERF", # perflint
25 | "PIE", # flake8-pie
26 | "PL", # pylint
27 | "PT", # flake8-pytest-style
28 | "PYI", # flakes8-pyi
29 | "Q", # flake8-quotes
30 | "RET", # flake8-return
31 | "RSE", # flake8-raise
32 | "RUF", # Ruff-specific rules
33 | "SIM", # flake8-simplify
34 | "SLOT", # flake8-slots
35 | "TCH", # flake8-type-checking
36 | "TID", # tidy imports
37 | "TID", # flake8-tidy-imports
38 | "UP", # pyupgrade
39 | "W", # pycodestyle warning
40 | "YTT", # flake8-2020
41 | ]
42 | lint.ignore = [
43 | "B023", # Function definition does not bind loop variable
44 | "B028", # No explicit stacklevel keyword argument found
45 | "B904", # Within an except clause, raise exceptions with ...
46 | "C408", # unnecessary-collection-call
47 | "COM812", # missing trailing comma
48 | "D105", # Missing docstring in magic method
49 | "D205", # 1 blank line required between summary line and description
50 | "D212", # Multi-line docstring summary should start at the first line
51 | "NPY002", # replace legacy numpy.random with numpy.random.Generator
52 | "PD901", # pandas-df-variable-name
53 | "PERF203", # try-except-in-loop
54 | "PERF401", # manual-list-comprehension (TODO fix these or wait for autofix)
55 | "PLC1901", # can be simplified to ... as empty is falsey
56 | "PLR", # pylint refactor
57 | "PLW1514", # open() without explicit encoding argument
58 | "PLW2901", # Outer for loop variable overwritten by inner assignment target
59 | "PT013", # pytest-incorrect-pytest-import
60 | "PTH", # prefer pathlib to os.path
61 | "PYI024", # collections-named-tuple (TODO should maybe fix these)
62 | "RUF012", # Disable checks for mutable class args. This is a non-problem.
63 | "SIM105", # Use contextlib.suppress(OSError) instead of try-except-pass
64 | ]
65 | lint.pydocstyle.convention = "google"
66 | lint.isort.required-imports = ["from __future__ import annotations"]
67 | lint.isort.split-on-trailing-comma = false
68 |
69 | [tool.ruff.lint.per-file-ignores]
70 | "__init__.py" = ["F401"]
71 | "tests/**" = ["ANN201", "D", "S101"]
72 | "tasks.py" = ["D"]
73 | "pymatgen/analysis/*" = ["D"]
74 | "pymatgen/vis/*" = ["D"]
75 | "pymatgen/io/*" = ["D"]
76 | "dev_scripts/*" = ["D"]
77 |
78 | [tool.pytest.ini_options]
79 | addopts = "--durations=30 --quiet -r xXs --color=yes -p no:warnings --import-mode=importlib"
80 |
81 | [tool.coverage.run]
82 | parallel = true
83 |
84 | [tool.coverage.report]
85 | exclude_also = [
86 | "@deprecated",
87 | "@np.deprecate",
88 | "def __repr__",
89 | "except ImportError:",
90 | "if 0:",
91 | "if TYPE_CHECKING:",
92 | "if __name__ == .__main__.:",
93 | "if self.debug:",
94 | "if settings.DEBUG",
95 | "if typing.TYPE_CHECKING:",
96 | "pragma: no cover",
97 | "raise AssertionError",
98 | "raise NotImplementedError",
99 | "show_plot",
100 | ]
101 |
102 | [tool.mypy]
103 | ignore_missing_imports = true
104 | namespace_packages = true
105 | explicit_package_bases = true
106 | no_implicit_optional = false
107 | disable_error_code = "annotation-unchecked"
108 |
109 | [[tool.mypy.overrides]]
110 | module = ["requests.*", "tabulate.*"]
111 | ignore_missing_imports = true
112 |
113 | [tool.codespell]
114 | ignore-words-list = """
115 | titel,alls,ans,nd,mater,nwo,te,hart,ontop,ist,ot,fo,nax,coo,coul,ser,leary,thre,fase,
116 | rute,reson,titels,ges,scalr,strat,struc,hda,nin,ons,pres,kno,loos,lamda,lew,atomate
117 | """
118 | check-filenames = true
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | sphinx
2 | doc2dash
3 | sphinx_rtd_theme
4 | sphinx-autodoc-typehints
5 | invoke
6 | pre-commit
7 | pytest
8 | pytest-cov
9 | pytest-split
10 | mypy
11 | ruff
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib
2 | MDAnalysis>=2.2.0
3 | numpy>=1.16.0
4 | pandas
5 | monty
6 | pubchempy
7 | pymatgen
8 | scipy
9 | selenium
10 | setuptools
11 | sphinx
12 | sphinx_rtd_theme
13 | sphinx-autodoc-typehints
14 | statsmodels
15 | tqdm
16 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [isort]
2 | profile=black
3 | known_first_party=mdgo
4 |
5 | [tool:pytest]
6 | addopts = -x --durations=30 --quiet -rxXs --color=yes
7 | filterwarnings =
8 | ignore::UserWarning
9 | ignore::FutureWarning
10 | ignore::RuntimeWarning
11 | ignore::DeprecationWarning
12 | ignore::PendingDeprecationWarning
13 |
14 | [pycodestyle]
15 | count = True
16 | ignore = E121,E123,E126,E133,E226,E241,E242,E704,W503,W504,W505,E741,W605,W293,W291,W292,E203,E231
17 | max-line-length = 120
18 | statistics = True
19 | exclude=docs_rst/*.py
20 |
21 | [flake8]
22 | exclude = .git,__pycache__,docs_rst/conf.py,tests
23 | # max-complexity = 10
24 | extend-ignore = E741,W291,W293,E501,E231,E203
25 | max-line-length = 120
26 | per-file-ignores =
27 | # F401: imported but unused
28 | __init__.py: F401
29 |
30 | [pydocstyle]
31 | ignore = D103,D105,D2,D4
32 | match-dir=(?!(tests)).*
33 |
34 | [mypy]
35 | ignore_missing_imports = True
36 | namespace_packages = True
37 | explicit_package_bases = True
38 |
39 | [mypy-tabulate.*]
40 | ignore_missing_imports = True
41 |
42 | [mypy-requests.*]
43 | ignore_missing_imports = True
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Tingzheng Hou.
2 | # Distributed under the terms of the MIT License.
3 |
4 | """Setup.py for MDGO."""
5 |
6 | from __future__ import annotations
7 |
8 | import os
9 |
10 | from setuptools import find_packages, setup
11 |
12 | module_dir = os.path.dirname(os.path.abspath(__file__))
13 |
14 | with open(os.path.join(module_dir, "README.md")) as f:
15 | readme = f.read()
16 |
17 | INSTALL_REQUIRES = [
18 | "numpy>=1.16.0",
19 | "pandas",
20 | "matplotlib",
21 | "scipy",
22 | "tqdm",
23 | "pymatgen>=2022.7.8",
24 | "statsmodels",
25 | "pubchempy",
26 | "MDAnalysis>=2.2.0",
27 | "selenium",
28 | ]
29 |
30 | on_rtd = os.environ.get("READTHEDOCS") == "True"
31 | if on_rtd:
32 | INSTALL_REQUIRES = []
33 |
34 | if __name__ == "__main__":
35 | setup(
36 | name="mdgo",
37 | version="0.3.1",
38 | description="A codebase for MD simulation setup and results analysis.",
39 | long_description=readme,
40 | long_description_content_type="text/markdown",
41 | license="MIT",
42 | author="mdgo development team",
43 | author_email="tingzheng_hou@berkeley.edu",
44 | maintainer="Tingzheng Hou",
45 | maintainer_email="tingzheng_hou@berkeley.edu",
46 | url="https://github.com/HT-MD/mdgo",
47 | keywords=[
48 | "LAMMPS",
49 | "Gromacs",
50 | "Molecular dynamics",
51 | "liquid",
52 | "charge",
53 | "materials",
54 | "science",
55 | "solvation",
56 | "diffusion",
57 | "transport",
58 | "conductivity",
59 | "force field",
60 | ],
61 | classifiers=[
62 | "Programming Language :: Python :: 3",
63 | "Programming Language :: Python :: 3.8",
64 | "Programming Language :: Python :: 3.9",
65 | "Programming Language :: Python :: 3.10",
66 | "Development Status :: 3 - Alpha",
67 | "Intended Audience :: Science/Research",
68 | "License :: OSI Approved :: MIT License",
69 | "Operating System :: OS Independent",
70 | "Topic :: Scientific/Engineering :: Information Analysis",
71 | "Topic :: Scientific/Engineering :: Physics",
72 | "Topic :: Scientific/Engineering :: Chemistry",
73 | "Topic :: Software Development :: Libraries :: Python Modules",
74 | ],
75 | packages=find_packages(),
76 | install_requires=INSTALL_REQUIRES,
77 | extras_require={
78 | "web": [
79 | "sphinx",
80 | "sphinx_rtd_theme",
81 | "sphinx-autodoc-typehints",
82 | ],
83 | },
84 | python_requires=">=3.8",
85 | )
86 |
--------------------------------------------------------------------------------
/tasks.py:
--------------------------------------------------------------------------------
1 | """
2 | Pyinvoke tasks.py file for automating releases and admin stuff.
3 |
4 | To cut a new mdgo release, use `invoke update-changelog` followed by `invoke release`.
5 |
6 | Author: Tingzheng Hou
7 | """
8 | from __future__ import annotations
9 |
10 | import glob
11 | import json
12 | import os
13 | import re
14 | import subprocess
15 | import webbrowser
16 |
17 | import requests
18 | from invoke import task
19 | from monty.os import cd
20 |
21 | from mdgo import __version__ as CURRENT_VER
22 |
23 |
24 | @task
25 | def make_doc(ctx):
26 | """
27 | Generate API documentation + run Sphinx.
28 | :param ctx:
29 | """
30 | with open("CHANGES.rst") as f:
31 | contents = f.read()
32 |
33 | toks = re.split(r"\-{3,}", contents)
34 | n = len(toks[0].split()[-1])
35 | changes = [toks[0]]
36 | changes.append("\n" + "\n".join(toks[1].strip().split("\n")[0:-1]))
37 | changes = ("-" * n).join(changes)
38 |
39 | with open("docs/source/latest_changes.rst", "w") as f:
40 | f.write(changes)
41 |
42 | with cd("docs/source"):
43 | ctx.run("cp ../CHANGES.rst change_log.rst")
44 | ctx.run("rm mdgo.*.rst", warn=True)
45 | ctx.run("sphinx-apidoc --implicit-namespaces --separate -d 7 -o . -f ../mdgo")
46 | ctx.run("rm *.tests.*rst")
47 | for f in glob.glob("*.rst"):
48 | if f.startswith("mdgo") and f.endswith("rst"):
49 | newoutput = []
50 | suboutput = []
51 | subpackage = False
52 | with open(f) as fid:
53 | for line in fid:
54 | clean = line.strip()
55 | if clean == "Subpackages":
56 | subpackage = True
57 | if not subpackage and not clean.endswith("tests"):
58 | newoutput.append(line)
59 | else:
60 | if not clean.endswith("tests"):
61 | suboutput.append(line)
62 | if clean.startswith("mdgo") and not clean.endswith("tests"):
63 | newoutput.extend(suboutput)
64 | subpackage = False
65 | suboutput = []
66 |
67 | with open(f, "w") as fid:
68 | fid.write("".join(newoutput))
69 | ctx.run("make html")
70 |
71 | ctx.run("cp _static/* ../docs/html/_static", warn=True)
72 |
73 | with cd("docs"):
74 | ctx.run("rm *.html", warn=True)
75 | ctx.run("cp -r html/* .", warn=True)
76 | ctx.run("rm -r html", warn=True)
77 | ctx.run("rm -r doctrees", warn=True)
78 | ctx.run("rm -r _sources", warn=True)
79 | ctx.run("rm -r _build", warn=True)
80 |
81 | # This makes sure mdgo.readthedocs.io works to redirect to the Github page
82 | ctx.run('echo "mdgo.readthedocs.io" > CNAME')
83 | # Avoid the use of jekyll so that _dir works as intended.
84 | ctx.run("touch .nojekyll")
85 |
86 |
87 | @task
88 | def update_doc(ctx):
89 | """
90 | Update the web documentation.
91 | :param ctx:
92 | """
93 | ctx.run("cp docs_rst/conf-normal.py docs_rst/conf.py")
94 | make_doc(ctx)
95 | ctx.run("git add .")
96 | commit(ctx, "Update docs")
97 |
98 |
99 | @task
100 | def publish(ctx):
101 | """
102 | Upload release to Pypi using twine.
103 | :param ctx:
104 | """
105 | ctx.run("rm dist/*.*", warn=True)
106 | ctx.run("python setup.py sdist bdist_wheel")
107 | ctx.run("twine upload dist/*")
108 |
109 |
110 | @task
111 | def set_ver(ctx, version):
112 | with open("mdgo/__init__.py") as f:
113 | contents = f.read()
114 | contents = re.sub(r"__version__ = .*\n", '__version__ = "%s"\n' % version, contents)
115 |
116 | with open("mdgo/__init__.py", "w") as f:
117 | f.write(contents)
118 |
119 | with open("mdgo/core/__init__.py") as f:
120 | contents = f.read()
121 | contents = re.sub(r"__version__ = .*\n", '__version__ = "%s"\n' % version, contents)
122 |
123 | with open("mdgo/core/__init__.py", "w") as f:
124 | f.write(contents)
125 |
126 | with open("mdgo/forcefield/__init__.py") as f:
127 | contents = f.read()
128 | contents = re.sub(r"__version__ = .*\n", '__version__ = "%s"\n' % version, contents)
129 |
130 | with open("mdgo/forcefield/__init__.py", "w") as f:
131 | f.write(contents)
132 |
133 | with open("mdgo/util/__init__.py") as f:
134 | contents = f.read()
135 | contents = re.sub(r"__version__ = .*\n", '__version__ = "%s"\n' % version, contents)
136 |
137 | with open("mdgo/util/__init__.py", "w") as f:
138 | f.write(contents)
139 |
140 | with open("setup.py") as f:
141 | contents = f.read()
142 | contents = re.sub(r"version=([^,]+),", 'version="%s",' % version, contents)
143 |
144 | with open("setup.py", "w") as f:
145 | f.write(contents)
146 |
147 |
148 | @task
149 | def release_github(ctx, version):
150 | """
151 | Release to Github using Github API.
152 | :param ctx:
153 | """
154 | with open("CHANGES.rst") as f:
155 | contents = f.read()
156 | toks = re.split(r"\-+", contents)
157 | desc = toks[1].strip()
158 | toks = desc.split("\n")
159 | desc = "\n".join(toks[:-1]).strip()
160 | payload = {
161 | "tag_name": "v" + version,
162 | "target_commitish": "main",
163 | "name": "v" + version,
164 | "body": desc,
165 | "draft": False,
166 | "prerelease": False,
167 | }
168 | response = requests.post(
169 | "https://api.github.com/repos/HT-MD/mdgo/releases",
170 | data=json.dumps(payload),
171 | headers={"Authorization": "token " + os.environ["GITHUB_RELEASES_TOKEN"]},
172 | )
173 | print(response.text)
174 |
175 |
176 | @task
177 | def update_changelog(ctx, version, sim=False):
178 | """
179 | Create a preliminary change log using the git logs.
180 | :param ctx:
181 | """
182 | output = subprocess.check_output(["git", "log", "--pretty=format:%s", "v%s..HEAD" % CURRENT_VER])
183 | lines = []
184 | misc = []
185 | for line in output.decode("utf-8").strip().split("\n"):
186 | m = re.match(r"Merge pull request \#(\d+) from (.*)", line)
187 | if m:
188 | pr_number = m.group(1)
189 | contrib, pr_name = m.group(2).split("/", 1)
190 | response = requests.get(f"https://api.github.com/repos/HT-MD/mdgo/pulls/{pr_number}")
191 | lines.append(f"* PR #{pr_number} from @{contrib} {pr_name}")
192 | if "body" in response.json():
193 | for ll in response.json()["body"].split("\n"):
194 | ll = ll.strip()
195 | if ll in ["", "## Summary"]:
196 | continue
197 | if ll.startswith(("## Checklist", "## TODO")):
198 | break
199 | lines.append(f" {ll}")
200 | misc.append(line)
201 | with open("CHANGES.rst") as f:
202 | contents = f.read()
203 | line = "=========="
204 | toks = contents.split(line)
205 | head = "\n\nv%s\n" % version + "-" * (len(version) + 1) + "\n"
206 | toks.insert(-1, head + "\n".join(lines))
207 | if not sim:
208 | with open("CHANGES.rst", "w") as f:
209 | f.write(toks[0] + line + "".join(toks[1:]))
210 | ctx.run("open CHANGES.rst")
211 | else:
212 | print(toks[0] + line + "".join(toks[1:]))
213 | print("The following commit messages were not included...")
214 | print("\n".join(misc))
215 |
216 |
217 | @task
218 | def release(ctx, version, nodoc=False):
219 | """
220 | Run full sequence for releasing mdgo.
221 | :param ctx:
222 | :param nodoc: Whether to skip doc generation.
223 | """
224 | ctx.run("rm -r dist build mdgo.egg-info", warn=True)
225 | set_ver(ctx, version)
226 | if not nodoc:
227 | make_doc(ctx)
228 | ctx.run("git add .")
229 | commit(ctx, "Update docs")
230 | release_github(ctx, version)
231 |
232 |
233 | @task
234 | def commit(ctx, message):
235 | ctx.run(f'git commit -a -m "{message}"', warn=True)
236 | ctx.run(f'git push https://{os.environ["GITHUB_RELEASES_TOKEN"]}@github.com/HT-MD/mdgo.git', warn=True)
237 |
238 |
239 | @task
240 | def open_doc(ctx):
241 | """
242 | Open local documentation in web browser.
243 | :param ctx:
244 | """
245 | pth = os.path.abspath("docs/_build/html/index.html")
246 | webbrowser.open("file://" + pth)
247 |
248 |
249 | @task
250 | def lint(ctx):
251 | for cmd in ["pycodestyle", "mypy", "flake8", "pydocstyle"]:
252 | ctx.run("%s mdgo" % cmd)
253 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouGroup/mdgo/b3d4dab22dddd67505d15e0ed3305c01d4f602e4/tests/__init__.py
--------------------------------------------------------------------------------
/tests/packmol/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | *.mod
3 | *.o
4 | *.swp
5 |
6 |
--------------------------------------------------------------------------------
/tests/packmol/AUTHORS:
--------------------------------------------------------------------------------
1 | L. Martinez, R. Andrade, E. G. Birgin, J. M. Martinez. Packmol: A
2 | package for building initial configurations for molecular dynamics
3 | simulations. Journal of Computational Chemistry, 30(13):2157-2164,
4 | 2009.
5 |
6 | J. M. Martinez and L. Martinez. Packing optimization for automated
7 | generation of complex system's initial configurations for molecular
8 | dynamics and docking. Journal of Computational Chemistry, 24(7):819-825,
9 | 2003.
10 |
11 | Home-Page: http://m3g.iqm.unicamp.br/packmol
12 |
--------------------------------------------------------------------------------
/tests/packmol/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2009-2018 Leandro Martínez, José Mario Martínez, Ernesto Birgin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/tests/packmol/README.md:
--------------------------------------------------------------------------------
1 | # Packmol
2 |
3 | Packmol - Creates Initial Configurations for Molecular Dynamics Simulations
4 |
5 | **https://m3g.github.io/packmol**
6 |
7 | ## What is Packmol
8 |
9 | Packmol creates an initial point for molecular dynamics simulations by packing molecules in defined regions of space. The packing guarantees that short range repulsive interactions do not disrupt the simulations.
10 |
11 | The great variety of types of spatial constraints that can be attributed to the molecules, or atoms within the molecules, makes it easy to create ordered systems, such as lamellar, spherical or tubular lipid layers.
12 |
13 | The user must provide only the coordinates of one molecule of each type, the number of molecules of each type and the spatial constraints that each type of molecule must satisfy.
14 |
15 | The package is compatible with input files of PDB, TINKER, XYZ and MOLDY formats.
16 |
17 | ## Usage
18 |
19 | User guide, examples, and tutorials, are available at: https://m3g.github.io/packmol
20 |
21 | ## Installation instructions
22 |
23 | ### Multi-platform package provider with Julia
24 |
25 | If you are not familiar with compiling packages, you may find it easier to get the Julia interface for
26 | `packmol`, which provides executables for all common platforms: https://github.com/m3g/Packmol.jl
27 |
28 | Installation of the Julia programming language and of the Julia `Packmol` package are necessary, but
29 | these follow simple instructions which are described in the link above.
30 |
31 | Compilation of the package, particularly on Linux platforms is, nevertheless, easy, following the instructions
32 | below.
33 |
34 | ### Downloading
35 |
36 | 1. Download the `.tar.gz` or `.zip` files of the latest version from: https://github.com/m3g/packmol/releases
37 |
38 | 2. Unpack the files, for example with:
39 | ```bash
40 | tar -xzvf packmol-20.13.0.tar.gz
41 | ```
42 | or
43 | ```bash
44 | unzip -xzvf packmol-20.13.0.zip
45 | ```
46 | substituting the `20.13.0` with the correct version number.
47 |
48 | ### Using `make`
49 |
50 | 3. Go into the `packmol` directory, and compile the package (we assume `gfortran` or other compiler is available):
51 | ```bash
52 | cd packmol
53 | ./configure [optional: path to fortran compiler]
54 | make
55 | ```
56 |
57 | 4. An executable called `packmol` will be created in the main directory. Add that directory to your path.
58 |
59 | ### Using the Fortran Package Manager (`fpm`)
60 |
61 | 3. Install the Fortran Package Manager from: https://fpm.fortran-lang.org/en/install/index.html#install
62 |
63 | 4. Go into the `packmol` directory, and run:
64 | ```bash
65 | fpm install --profile release
66 | ```
67 | this will compile and send the executable somewhere in your `PATH`.
68 | By default (on Linux systems) it will be `~/.local/bin`. Making it available
69 | as a `packmol` command anywhere in your computer.
70 |
71 | `fpm` will look for Fortran compilers automatically and will use `gfortran`
72 | as default. To use another compiler modify the environment variable
73 | `FPM_FC=compiler`, for example for `ifort`, use in bash, `export FPM_FC=ifort`.
74 |
75 | ## References
76 |
77 | Please always cite one of the following references in publications for which Packmol was useful:
78 |
79 | L Martinez, R Andrade, EG Birgin, JM Martinez, Packmol: A package for building initial configurations for molecular dynamics simulations. Journal of Computational Chemistry, 30, 2157-2164, 2009. (http://www3.interscience.wiley.com/journal/122210103/abstract)
80 |
81 | JM Martinez, L Martinez, Packing optimization for the automated generation of complex system's initial configurations for molecular dynamics and docking. Journal of Computational Chemistry, 24, 819-825, 2003.
82 | (http://www3.interscience.wiley.com/journal/104086246/abstract)
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/tests/packmol/packmol:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouGroup/mdgo/b3d4dab22dddd67505d15e0ed3305c01d4f602e4/tests/packmol/packmol
--------------------------------------------------------------------------------
/tests/test_conductivity.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import os
4 | import unittest
5 |
6 | import MDAnalysis
7 | import numpy as np
8 |
9 | from mdgo.conductivity import calc_cond_msd, get_beta
10 |
11 | test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files")
12 |
13 |
14 | class ConductivityTest(unittest.TestCase):
15 | @classmethod
16 | def setUpClass(cls) -> None:
17 | cls.gen2 = MDAnalysis.Universe(
18 | os.path.join(test_dir, "gen2_light", "gen2_mdgo.data"),
19 | os.path.join(test_dir, "gen2_light", "gen2_mdgo_unwrapped_nvt_main.dcd"),
20 | format="LAMMPS",
21 | )
22 | cls.anions = cls.gen2.select_atoms("type 1")
23 | cls.cations = cls.gen2.select_atoms("type 3")
24 | cls.cond_array = calc_cond_msd(cls.gen2, cls.anions, cls.cations, 100, 1, -1)
25 | cls.time_array = np.array([i * 10 for i in range(cls.gen2.trajectory.n_frames - 100)])
26 |
27 | def test_calc_cond_msd(self):
28 | assert self.cond_array[0] == -2.9103830456733704e-11
29 | assert self.cond_array[1] == 112.66080481783138
30 | assert self.cond_array[-1] == 236007.76624833583
31 |
32 | def test_get_beta(self):
33 | assert get_beta(self.cond_array, self.time_array, 10, 100) == (0.8188201425517928, 0.2535110576154693)
34 | assert get_beta(self.cond_array, self.time_array, 1000, 2000) == (1.2525648107674503, 1.0120346984003845)
35 | assert get_beta(self.cond_array, self.time_array, 1500, 2500) == (1.4075552564189142, 1.3748981878979976)
36 | assert get_beta(self.cond_array, self.time_array, 2000, 4000) == (1.5021915651236932, 51.79451695748163)
37 |
38 |
39 | if __name__ == "__main__":
40 | unittest.main()
41 |
--------------------------------------------------------------------------------
/tests/test_coordination.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import unittest
4 |
5 |
6 | class MyTestCase(unittest.TestCase):
7 | def test_something(self):
8 | assert True is True
9 |
10 |
11 | if __name__ == "__main__":
12 | unittest.main()
13 |
--------------------------------------------------------------------------------
/tests/test_core.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import unittest
4 |
5 |
6 | class MyTestCase(unittest.TestCase):
7 | def test_something(self):
8 | assert True is True
9 |
10 |
11 | if __name__ == "__main__":
12 | unittest.main()
13 |
--------------------------------------------------------------------------------
/tests/test_files/C.lmp:
--------------------------------------------------------------------------------
1 | LAMMPS data file Created by LigParGen - (Written by Leela S. Dodda)
2 |
3 | 5 atoms
4 | 4 bonds
5 | 6 angles
6 | 0 dihedrals
7 | 2 impropers
8 |
9 | 5 atom types
10 | 4 bond types
11 | 6 angle types
12 | 0 dihedral types
13 | 2 improper types
14 |
15 | -0.092200 49.907800 xlo xhi
16 | 0.107450 50.107450 ylo yhi
17 | -1.030120 48.969880 zlo zhi
18 |
19 | Masses
20 |
21 | 1 12.011
22 | 2 1.008
23 | 3 1.008
24 | 4 1.008
25 | 5 1.008
26 |
27 | Pair Coeffs
28 |
29 | 1 0.066 3.5000000
30 | 2 0.030 2.5000000
31 | 3 0.030 2.5000000
32 | 4 0.030 2.5000000
33 | 5 0.030 2.5000000
34 |
35 | Bond Coeffs
36 |
37 | 1 340.0000 1.0900
38 | 2 340.0000 1.0900
39 | 3 340.0000 1.0900
40 | 4 340.0000 1.0900
41 |
42 | Angle Coeffs
43 |
44 | 1 33.000 107.800
45 | 2 33.000 107.800
46 | 3 33.000 107.800
47 | 4 33.000 107.800
48 | 5 33.000 107.800
49 | 6 33.000 107.800
50 |
51 | Dihedral Coeffs
52 |
53 |
54 | Improper Coeffs
55 |
56 | 1 0.000 -1 2
57 | 2 0.000 -1 2
58 |
59 | Atoms
60 |
61 | 1 1 1 -0.29940000 1.000 1.00000 0.00000
62 | 2 1 2 0.07480000 -0.092 1.00000 0.00000
63 | 3 1 3 0.07480000 1.363 1.00000 -1.03012
64 | 4 1 4 0.07480000 1.363 0.10745 0.51431
65 | 5 1 5 0.07480000 1.363 1.89253 0.51430
66 |
67 | Bonds
68 |
69 | 1 1 2 1
70 | 2 2 3 1
71 | 3 3 4 1
72 | 4 4 5 1
73 |
74 | Angles
75 |
76 | 1 1 2 1 3
77 | 2 2 2 1 4
78 | 3 3 2 1 5
79 | 4 4 3 1 4
80 | 5 5 4 1 5
81 | 6 6 3 1 5
82 |
83 | Dihedrals
84 |
85 |
86 | Impropers
87 |
88 | 1 1 1 2 3 4
89 | 2 2 1 2 3 5
90 |
91 |
--------------------------------------------------------------------------------
/tests/test_files/CCOC(=O)OC.lmp:
--------------------------------------------------------------------------------
1 | LAMMPS data file Created by LigParGen - (Written by Leela S. Dodda)
2 |
3 | 15 atoms
4 | 14 bonds
5 | 23 angles
6 | 19 dihedrals
7 | 7 impropers
8 |
9 | 15 atom types
10 | 14 bond types
11 | 23 angle types
12 | 19 dihedral types
13 | 7 improper types
14 |
15 | -2.012410 47.987590 xlo xhi
16 | -1.850300 48.149700 ylo yhi
17 | -1.017950 48.982050 zlo zhi
18 |
19 | Masses
20 |
21 | 1 12.011
22 | 2 12.011
23 | 3 15.999
24 | 4 12.011
25 | 5 15.999
26 | 6 15.999
27 | 7 12.011
28 | 8 1.008
29 | 9 1.008
30 | 10 1.008
31 | 11 1.008
32 | 12 1.008
33 | 13 1.008
34 | 14 1.008
35 | 15 1.008
36 |
37 | Pair Coeffs
38 |
39 | 1 0.066 3.5000000
40 | 2 0.066 3.5000000
41 | 3 0.140 2.9000000
42 | 4 0.070 3.5500000
43 | 5 0.210 2.9600000
44 | 6 0.140 2.9000000
45 | 7 0.066 3.5000000
46 | 8 0.030 2.5000000
47 | 9 0.030 2.5000000
48 | 10 0.030 2.5000000
49 | 11 0.030 2.5000000
50 | 12 0.030 2.5000000
51 | 13 0.030 2.5000000
52 | 14 0.030 2.5000000
53 | 15 0.030 2.5000000
54 |
55 | Bond Coeffs
56 |
57 | 1 268.0000 1.5290
58 | 2 320.0000 1.4100
59 | 3 214.0000 1.3270
60 | 4 570.0000 1.2290
61 | 5 214.0000 1.3270
62 | 6 320.0000 1.4100
63 | 7 340.0000 1.0900
64 | 8 340.0000 1.0900
65 | 9 340.0000 1.0900
66 | 10 340.0000 1.0900
67 | 11 340.0000 1.0900
68 | 12 340.0000 1.0900
69 | 13 340.0000 1.0900
70 | 14 340.0000 1.0900
71 |
72 | Angle Coeffs
73 |
74 | 1 50.000 109.500
75 | 2 83.000 116.900
76 | 3 83.000 123.400
77 | 4 69.900 118.180
78 | 5 83.000 116.900
79 | 6 37.500 110.700
80 | 7 37.500 110.700
81 | 8 37.500 110.700
82 | 9 37.500 110.700
83 | 10 37.500 110.700
84 | 11 35.000 109.500
85 | 12 35.000 109.500
86 | 13 35.000 109.500
87 | 14 83.000 123.400
88 | 15 33.000 107.800
89 | 16 33.000 107.800
90 | 17 33.000 107.800
91 | 18 33.000 107.800
92 | 19 35.000 109.500
93 | 20 33.000 107.800
94 | 21 35.000 109.500
95 | 22 33.000 107.800
96 | 23 33.000 107.800
97 |
98 | Dihedral Coeffs
99 |
100 | 1 -1.220 -0.126 0.422 0.000
101 | 2 0.000 5.124 0.000 0.000
102 | 3 4.669 5.124 0.000 0.000
103 | 4 0.000 0.000 0.468 0.000
104 | 5 0.000 0.000 0.198 0.000
105 | 6 0.000 0.000 0.300 0.000
106 | 7 0.000 0.000 0.300 0.000
107 | 8 4.669 5.124 0.000 0.000
108 | 9 0.000 0.000 0.300 0.000
109 | 10 0.000 0.000 0.468 0.000
110 | 11 0.000 0.000 0.198 0.000
111 | 12 0.000 0.000 0.300 0.000
112 | 13 0.000 0.000 0.300 0.000
113 | 14 0.000 0.000 0.468 0.000
114 | 15 0.000 0.000 0.198 0.000
115 | 16 0.000 5.124 0.000 0.000
116 | 17 0.000 0.000 0.198 0.000
117 | 18 0.000 0.000 0.300 0.000
118 | 19 0.000 0.000 0.198 0.000
119 |
120 | Improper Coeffs
121 |
122 | 1 10.500 -1 2
123 | 2 0.000 -1 2
124 | 3 0.000 -1 2
125 | 4 0.000 -1 2
126 | 5 0.000 -1 2
127 | 6 0.000 -1 2
128 | 7 0.000 -1 2
129 |
130 | Atoms
131 |
132 | 1 1 1 -0.28070000 1.000 1.00000 0.00000
133 | 2 1 2 0.01710000 -0.520 1.00000 0.00000
134 | 3 1 3 -0.32310000 -0.985 1.00000 1.35431
135 | 4 1 4 0.57010000 -1.060 -0.22226 1.93502
136 | 5 1 5 -0.49590000 -0.937 -1.29627 1.36526
137 | 6 1 6 -0.31640000 -1.213 -0.04501 3.26928
138 | 7 1 7 -0.04510000 -1.103 -1.25137 4.02168
139 | 8 1 8 0.10110000 1.393 1.00129 -1.01795
140 | 9 1 9 0.10110000 1.391 0.12572 0.52971
141 | 10 1 10 0.10110000 1.376 1.88379 0.52868
142 | 11 1 11 0.12390000 -0.921 0.15139 -0.56659
143 | 12 1 12 0.12390000 -0.891 1.91359 -0.47627
144 | 13 1 13 0.10760000 -0.989 -0.98456 5.07574
145 | 14 1 14 0.10760000 -0.218 -1.82392 3.72415
146 | 15 1 15 0.10760000 -2.012 -1.85030 3.90293
147 |
148 | Bonds
149 |
150 | 1 1 2 1
151 | 2 2 3 2
152 | 3 3 4 3
153 | 4 4 5 4
154 | 5 5 6 4
155 | 6 6 7 6
156 | 7 7 8 1
157 | 8 8 9 1
158 | 9 9 10 1
159 | 10 10 11 2
160 | 11 11 12 2
161 | 12 12 13 7
162 | 13 13 14 7
163 | 14 14 15 7
164 |
165 | Angles
166 |
167 | 1 1 1 2 3
168 | 2 2 2 3 4
169 | 3 3 3 4 5
170 | 4 4 3 4 6
171 | 5 5 4 6 7
172 | 6 6 2 1 8
173 | 7 7 2 1 9
174 | 8 8 2 1 10
175 | 9 9 1 2 11
176 | 10 10 1 2 12
177 | 11 11 6 7 13
178 | 12 12 6 7 14
179 | 13 13 6 7 15
180 | 14 14 5 4 6
181 | 15 15 8 1 10
182 | 16 16 11 2 12
183 | 17 17 13 7 14
184 | 18 18 8 1 9
185 | 19 19 3 2 11
186 | 20 20 14 7 15
187 | 21 21 3 2 12
188 | 22 22 9 1 10
189 | 23 23 13 7 15
190 |
191 | Dihedrals
192 |
193 | 1 1 4 3 2 1
194 | 2 2 5 4 3 2
195 | 3 3 7 6 4 3
196 | 4 4 8 1 2 3
197 | 5 5 13 7 6 4
198 | 6 6 11 2 1 8
199 | 7 7 11 2 1 9
200 | 8 8 6 4 3 2
201 | 9 9 12 2 1 10
202 | 10 10 10 1 2 3
203 | 11 11 14 7 6 4
204 | 12 12 12 2 1 9
205 | 13 13 12 2 1 8
206 | 14 14 9 1 2 3
207 | 15 15 11 2 3 4
208 | 16 16 7 6 4 5
209 | 17 17 15 7 6 4
210 | 18 18 11 2 1 10
211 | 19 19 12 2 3 4
212 |
213 | Impropers
214 |
215 | 1 1 4 3 5 6
216 | 2 2 1 9 8 2
217 | 3 3 1 10 8 2
218 | 4 4 2 1 11 3
219 | 5 5 2 1 3 12
220 | 6 6 7 13 14 6
221 | 7 7 7 13 15 6
222 |
223 |
--------------------------------------------------------------------------------
/tests/test_files/DEC.xyz:
--------------------------------------------------------------------------------
1 | 18
2 |
3 | H 1 1 0
4 | C -0.094 1 0
5 | C -0.633 1 1.41453
6 | H -0.447 0.11987 -0.5476
7 | H -0.448 1.87998 -0.5475
8 | O -2.064 0.99829 1.36735
9 | H -0.284 0.10521 1.94213
10 | H -0.287 1.89555 1.94216
11 | C -2.691 0.99654 2.57594
12 | O -4.035 0.99344 2.35302
13 | O -2.142 0.99632 3.66972
14 | C -4.855 0.98961 3.52597
15 | C -6.31 0.98471 3.10902
16 | H -4.637 0.09499 4.12004
17 | H -4.643 1.88407 4.12085
18 | H -6.532 0.10455 2.49647
19 | H -6.967 0.9804 3.98425
20 | H -6.538 1.86395 2.49704
--------------------------------------------------------------------------------
/tests/test_files/EC.xyz:
--------------------------------------------------------------------------------
1 | 10
2 | comment line
3 | C -0.369000 1.465000 0.099000
4 | O -0.283000 0.274000 0.149000
5 | O -1.490000 2.178000 0.407000
6 | O 0.638000 2.304000 -0.273000
7 | C -1.288000 3.563000 0.069000
8 | C 0.237000 3.669000 -0.049000
9 | H -1.713000 4.180000 0.868000
10 | H -1.803000 3.770000 -0.878000
11 | H 0.712000 4.025000 0.875000
12 | H 0.574000 4.273000 -0.898000
13 |
--------------------------------------------------------------------------------
/tests/test_files/EMC.gro:
--------------------------------------------------------------------------------
1 | LIGPARGEN GENERATED GRO FILE
2 | 15
3 | 1UNK O00 1 -0.071 0.138 0.000
4 | 1UNK C01 2 -0.053 0.017 0.000
5 | 1UNK O02 3 0.068 -0.046 0.000
6 | 1UNK O03 4 -0.150 -0.078 0.000
7 | 1UNK C04 5 0.182 0.041 0.000
8 | 1UNK C05 6 -0.285 -0.029 -0.000
9 | 1UNK C06 7 0.308 -0.044 -0.000
10 | 1UNK H07 8 0.180 0.104 -0.090
11 | 1UNK H08 9 0.180 0.104 0.090
12 | 1UNK H09 10 0.397 0.019 -0.000
13 | 1UNK H0A 11 0.310 -0.109 0.088
14 | 1UNK H0B 12 0.310 -0.109 -0.088
15 | 1UNK H0C 13 -0.303 0.030 0.090
16 | 1UNK H0D 14 -0.352 -0.115 0.000
17 | 1UNK H0E 15 -0.303 0.030 -0.090
18 | 1.00000 1.00000 1.00000
19 |
20 |
--------------------------------------------------------------------------------
/tests/test_files/EMC.itp:
--------------------------------------------------------------------------------
1 |
2 | ;
3 | ; GENERATED BY LigParGen Server
4 | ; Jorgensen Lab @ Yale University
5 | ;
6 | [ atomtypes ]
7 | opls_802 O802 15.9990 0.000 A 2.90000E-01 5.85760E-01
8 | opls_810 H810 1.0080 0.000 A 2.50000E-01 1.25520E-01
9 | opls_806 C806 12.0110 0.000 A 3.50000E-01 2.76144E-01
10 | opls_809 H809 1.0080 0.000 A 2.50000E-01 1.25520E-01
11 | opls_803 O803 15.9990 0.000 A 2.90000E-01 5.85760E-01
12 | opls_808 H808 1.0080 0.000 A 2.50000E-01 1.25520E-01
13 | opls_814 H814 1.0080 0.000 A 2.50000E-01 1.25520E-01
14 | opls_800 O800 15.9990 0.000 A 2.96000E-01 8.78640E-01
15 | opls_812 H812 1.0080 0.000 A 2.50000E-01 1.25520E-01
16 | opls_807 H807 1.0080 0.000 A 2.50000E-01 1.25520E-01
17 | opls_804 C804 12.0110 0.000 A 3.50000E-01 2.76144E-01
18 | opls_813 H813 1.0080 0.000 A 2.50000E-01 1.25520E-01
19 | opls_801 C801 12.0110 0.000 A 3.55000E-01 2.92880E-01
20 | opls_811 H811 1.0080 0.000 A 2.50000E-01 1.25520E-01
21 | opls_805 C805 12.0110 0.000 A 3.50000E-01 2.76144E-01
22 | [ moleculetype ]
23 | ; Name nrexcl
24 | UNK 3
25 | [ atoms ]
26 | ; nr type resnr residue atom cgnr charge mass
27 | 1 opls_800 1 UNK O00 1 -0.4944 15.9990
28 | 2 opls_801 1 UNK C01 1 0.5748 12.0110
29 | 3 opls_802 1 UNK O02 1 -0.3288 15.9990
30 | 4 opls_803 1 UNK O03 1 -0.3251 15.9990
31 | 5 opls_804 1 UNK C04 1 0.0233 12.0110
32 | 6 opls_805 1 UNK C05 1 -0.0374 12.0110
33 | 7 opls_806 1 UNK C06 1 -0.2516 12.0110
34 | 8 opls_807 1 UNK H07 1 0.1046 1.0080
35 | 9 opls_808 1 UNK H08 1 0.1046 1.0080
36 | 10 opls_809 1 UNK H09 1 0.1029 1.0080
37 | 11 opls_810 1 UNK H0A 1 0.1029 1.0080
38 | 12 opls_811 1 UNK H0B 1 0.1029 1.0080
39 | 13 opls_812 1 UNK H0C 1 0.1071 1.0080
40 | 14 opls_813 1 UNK H0D 1 0.1071 1.0080
41 | 15 opls_814 1 UNK H0E 1 0.1071 1.0080
42 | [ bonds ]
43 | 2 1 1 0.1229 476976.000
44 | 3 2 1 0.1327 179075.200
45 | 4 2 1 0.1327 179075.200
46 | 5 3 1 0.1410 267776.000
47 | 6 4 1 0.1410 267776.000
48 | 7 5 1 0.1529 224262.400
49 | 8 5 1 0.1090 284512.000
50 | 9 5 1 0.1090 284512.000
51 | 10 7 1 0.1090 284512.000
52 | 11 7 1 0.1090 284512.000
53 | 12 7 1 0.1090 284512.000
54 | 13 6 1 0.1090 284512.000
55 | 14 6 1 0.1090 284512.000
56 | 15 6 1 0.1090 284512.000
57 |
58 | [ angles ]
59 | ; ai aj ak funct c0 c1 c2 c3
60 | 1 2 3 1 123.400 694.544
61 | 1 2 4 1 123.400 694.544
62 | 2 3 5 1 116.900 694.544
63 | 2 4 6 1 116.900 694.544
64 | 3 5 7 1 109.500 418.400
65 | 3 5 8 1 109.500 292.880
66 | 3 5 9 1 109.500 292.880
67 | 5 7 10 1 110.700 313.800
68 | 5 7 11 1 110.700 313.800
69 | 5 7 12 1 110.700 313.800
70 | 4 6 13 1 109.500 292.880
71 | 4 6 14 1 109.500 292.880
72 | 4 6 15 1 109.500 292.880
73 | 11 7 12 1 107.800 276.144
74 | 13 6 15 1 107.800 276.144
75 | 7 5 9 1 110.700 313.800
76 | 10 7 12 1 107.800 276.144
77 | 3 2 4 1 118.180 584.923
78 | 13 6 14 1 107.800 276.144
79 | 14 6 15 1 107.800 276.144
80 | 10 7 11 1 107.800 276.144
81 | 7 5 8 1 110.700 313.800
82 | 8 5 9 1 107.800 276.144
83 |
84 | [ dihedrals ]
85 | ; IMPROPER DIHEDRAL ANGLES
86 | ; ai aj ak al funct c0 c1 c2 c3 c4 c5
87 | 4 2 1 3 4 180.000 43.932 2
88 |
89 | [ dihedrals ]
90 | ; PROPER DIHEDRAL ANGLES
91 | ; ai aj ak al funct c0 c1 c2 c3 c4 c5
92 | 7 5 3 2 3 -2.197 5.201 0.527 -3.531 -0.000 0.000
93 | 5 3 2 1 3 21.439 0.000 -21.439 -0.000 -0.000 0.000
94 | 6 4 2 1 3 21.439 0.000 -21.439 -0.000 -0.000 0.000
95 | 6 4 2 3 3 31.206 -9.768 -21.439 -0.000 -0.000 0.000
96 | 5 3 2 4 3 31.206 -9.768 -21.439 -0.000 -0.000 0.000
97 | 12 7 5 8 3 0.628 1.883 0.000 -2.510 -0.000 0.000
98 | 12 7 5 9 3 0.628 1.883 0.000 -2.510 -0.000 0.000
99 | 10 7 5 8 3 0.628 1.883 0.000 -2.510 -0.000 0.000
100 | 10 7 5 9 3 0.628 1.883 0.000 -2.510 -0.000 0.000
101 | 11 7 5 8 3 0.628 1.883 0.000 -2.510 -0.000 0.000
102 | 11 7 5 9 3 0.628 1.883 0.000 -2.510 -0.000 0.000
103 | 10 7 5 3 3 0.979 2.937 0.000 -3.916 -0.000 0.000
104 | 11 7 5 3 3 0.979 2.937 0.000 -3.916 -0.000 0.000
105 | 12 7 5 3 3 0.979 2.937 0.000 -3.916 -0.000 0.000
106 | 8 5 3 2 3 0.414 1.243 0.000 -1.657 -0.000 0.000
107 | 14 6 4 2 3 0.414 1.243 0.000 -1.657 -0.000 0.000
108 | 9 5 3 2 3 0.414 1.243 0.000 -1.657 -0.000 0.000
109 | 13 6 4 2 3 0.414 1.243 0.000 -1.657 -0.000 0.000
110 | 15 6 4 2 3 0.414 1.243 0.000 -1.657 -0.000 0.000
111 |
112 | [ pairs ]
113 | 1 5 1
114 | 1 6 1
115 | 4 5 1
116 | 3 6 1
117 | 2 7 1
118 | 2 8 1
119 | 2 9 1
120 | 3 10 1
121 | 3 11 1
122 | 3 12 1
123 | 2 13 1
124 | 2 14 1
125 | 2 15 1
126 | 8 10 1
127 | 9 10 1
128 | 8 11 1
129 | 9 11 1
130 | 8 12 1
131 | 9 12 1
132 |
133 |
--------------------------------------------------------------------------------
/tests/test_files/EMC.lmp:
--------------------------------------------------------------------------------
1 | LAMMPS data file Created by LigParGen - (Written by Leela S. Dodda)
2 |
3 | 15 atoms
4 | 14 bonds
5 | 23 angles
6 | 19 dihedrals
7 | 7 impropers
8 |
9 | 15 atom types
10 | 14 bond types
11 | 23 angle types
12 | 19 dihedral types
13 | 7 improper types
14 |
15 | -2.011740 47.988260 xlo xhi
16 | 0.097060 50.097060 ylo yhi
17 | -3.154030 46.845970 zlo zhi
18 |
19 | Masses
20 |
21 | 1 15.999
22 | 2 12.011
23 | 3 15.999
24 | 4 15.999
25 | 5 12.011
26 | 6 12.011
27 | 7 12.011
28 | 8 1.008
29 | 9 1.008
30 | 10 1.008
31 | 11 1.008
32 | 12 1.008
33 | 13 1.008
34 | 14 1.008
35 | 15 1.008
36 |
37 | Pair Coeffs
38 |
39 | 1 0.210 2.9600000
40 | 2 0.070 3.5500000
41 | 3 0.140 2.9000000
42 | 4 0.140 2.9000000
43 | 5 0.066 3.5000000
44 | 6 0.066 3.5000000
45 | 7 0.066 3.5000000
46 | 8 0.030 2.5000000
47 | 9 0.030 2.5000000
48 | 10 0.030 2.5000000
49 | 11 0.030 2.5000000
50 | 12 0.030 2.5000000
51 | 13 0.030 2.5000000
52 | 14 0.030 2.5000000
53 | 15 0.030 2.5000000
54 |
55 | Bond Coeffs
56 |
57 | 1 570.0000 1.2290
58 | 2 214.0000 1.3270
59 | 3 214.0000 1.3270
60 | 4 320.0000 1.4100
61 | 5 320.0000 1.4100
62 | 6 268.0000 1.5290
63 | 7 340.0000 1.0900
64 | 8 340.0000 1.0900
65 | 9 340.0000 1.0900
66 | 10 340.0000 1.0900
67 | 11 340.0000 1.0900
68 | 12 340.0000 1.0900
69 | 13 340.0000 1.0900
70 | 14 340.0000 1.0900
71 |
72 | Angle Coeffs
73 |
74 | 1 83.000 123.400
75 | 2 83.000 123.400
76 | 3 83.000 116.900
77 | 4 83.000 116.900
78 | 5 50.000 109.500
79 | 6 35.000 109.500
80 | 7 35.000 109.500
81 | 8 37.500 110.700
82 | 9 37.500 110.700
83 | 10 37.500 110.700
84 | 11 35.000 109.500
85 | 12 35.000 109.500
86 | 13 35.000 109.500
87 | 14 33.000 107.800
88 | 15 33.000 107.800
89 | 16 37.500 110.700
90 | 17 33.000 107.800
91 | 18 69.900 118.180
92 | 19 33.000 107.800
93 | 20 33.000 107.800
94 | 21 33.000 107.800
95 | 22 37.500 110.700
96 | 23 33.000 107.800
97 |
98 | Dihedral Coeffs
99 |
100 | 1 0.000 5.124 0.000 0.000
101 | 2 0.000 5.124 0.000 0.000
102 | 3 -1.220 -0.126 0.422 0.000
103 | 4 0.000 0.000 0.468 0.000
104 | 5 0.000 0.000 0.198 0.000
105 | 6 0.000 0.000 0.198 0.000
106 | 7 0.000 0.000 0.300 0.000
107 | 8 4.669 5.124 0.000 0.000
108 | 9 0.000 0.000 0.300 0.000
109 | 10 0.000 0.000 0.198 0.000
110 | 11 0.000 0.000 0.198 0.000
111 | 12 0.000 0.000 0.198 0.000
112 | 13 0.000 0.000 0.468 0.000
113 | 14 0.000 0.000 0.300 0.000
114 | 15 0.000 0.000 0.300 0.000
115 | 16 0.000 0.000 0.300 0.000
116 | 17 0.000 0.000 0.300 0.000
117 | 18 0.000 0.000 0.468 0.000
118 | 19 4.669 5.124 0.000 0.000
119 |
120 | Improper Coeffs
121 |
122 | 1 10.500 -1 2
123 | 2 0.000 -1 2
124 | 3 0.000 -1 2
125 | 4 0.000 -1 2
126 | 5 0.000 -1 2
127 | 6 0.000 -1 2
128 | 7 0.000 -1 2
129 |
130 | Atoms
131 |
132 | 1 1 1 -0.49440000 1.000 1.00000 0.00000
133 | 2 1 2 0.57480000 -0.223 1.00000 0.00000
134 | 3 1 3 -0.32880000 -1.023 1.00000 1.10234
135 | 4 1 4 -0.32510000 -1.024 0.99861 -1.10290
136 | 5 1 5 0.02330000 -0.340 1.00000 2.36095
137 | 6 1 6 -0.03740000 -0.341 0.99702 -2.36128
138 | 7 1 7 -0.25160000 -1.363 0.99819 3.47610
139 | 8 1 8 0.10460000 0.289 0.10525 2.43386
140 | 9 1 9 0.10460000 0.287 1.89585 2.43492
141 | 10 1 10 0.10290000 -0.875 0.99685 4.45447
142 | 11 1 11 0.10290000 -2.012 1.87718 3.40445
143 | 12 1 12 0.10290000 -2.011 0.11809 3.40254
144 | 13 1 13 0.10710000 0.274 1.89689 -2.45868
145 | 14 1 14 0.10710000 -1.093 0.99851 -3.15403
146 | 15 1 15 0.10710000 0.273 0.09706 -2.45671
147 |
148 | Bonds
149 |
150 | 1 1 2 1
151 | 2 2 3 2
152 | 3 3 4 2
153 | 4 4 5 3
154 | 5 5 6 4
155 | 6 6 7 5
156 | 7 7 8 5
157 | 8 8 9 5
158 | 9 9 10 7
159 | 10 10 11 7
160 | 11 11 12 7
161 | 12 12 13 6
162 | 13 13 14 6
163 | 14 14 15 6
164 |
165 | Angles
166 |
167 | 1 1 1 2 3
168 | 2 2 1 2 4
169 | 3 3 2 3 5
170 | 4 4 2 4 6
171 | 5 5 3 5 7
172 | 6 6 3 5 8
173 | 7 7 3 5 9
174 | 8 8 5 7 10
175 | 9 9 5 7 11
176 | 10 10 5 7 12
177 | 11 11 4 6 13
178 | 12 12 4 6 14
179 | 13 13 4 6 15
180 | 14 14 11 7 12
181 | 15 15 13 6 15
182 | 16 16 7 5 9
183 | 17 17 10 7 12
184 | 18 18 3 2 4
185 | 19 19 13 6 14
186 | 20 20 14 6 15
187 | 21 21 10 7 11
188 | 22 22 7 5 8
189 | 23 23 8 5 9
190 |
191 | Dihedrals
192 |
193 | 1 1 5 3 2 1
194 | 2 2 6 4 2 1
195 | 3 3 7 5 3 2
196 | 4 4 10 7 5 3
197 | 5 5 13 6 4 2
198 | 6 6 14 6 4 2
199 | 7 7 12 7 5 8
200 | 8 8 5 3 2 4
201 | 9 9 11 7 5 9
202 | 10 10 9 5 3 2
203 | 11 11 15 6 4 2
204 | 12 12 8 5 3 2
205 | 13 13 11 7 5 3
206 | 14 14 12 7 5 9
207 | 15 15 10 7 5 8
208 | 16 16 10 7 5 9
209 | 17 17 11 7 5 8
210 | 18 18 12 7 5 3
211 | 19 19 6 4 2 3
212 |
213 | Impropers
214 |
215 | 1 1 2 1 3 4
216 | 2 2 5 3 7 8
217 | 3 3 5 9 3 7
218 | 4 4 7 10 11 5
219 | 5 5 7 10 12 5
220 | 6 6 6 4 13 14
221 | 7 7 6 4 13 15
222 |
223 |
--------------------------------------------------------------------------------
/tests/test_files/EMC.lmp.xyz:
--------------------------------------------------------------------------------
1 | 15
2 |
3 | O 1.0 1.0 0.0
4 | C -0.223 1.0 0.0
5 | O -1.023 1.0 1.10234
6 | O -1.024 0.99861 -1.1029
7 | C -0.34 1.0 2.36095
8 | C -0.341 0.99702 -2.36128
9 | C -1.363 0.99819 3.4761
10 | H 0.289 0.10525 2.43386
11 | H 0.287 1.89585 2.43492
12 | H -0.875 0.99685 4.45447
13 | H -2.012 1.87718 3.40445
14 | H -2.011 0.11809 3.40254
15 | H 0.274 1.89689 -2.45868
16 | H -1.093 0.99851 -3.15403
17 | H 0.273 0.09706 -2.45671
--------------------------------------------------------------------------------
/tests/test_files/EMC.pdb:
--------------------------------------------------------------------------------
1 | TITLE cid_522046
2 | REMARK 4 COMPLIES WITH FORMAT V. 3.3, 21-NOV-2012
3 | REMARK 888
4 | REMARK 888 WRITTEN BY MDGO (CREATED BY TINGZHENG HOU)
5 | HETATM 1 O UNK 900 0.680 -0.454 0.001 1.00 0.00 O
6 | HETATM 2 O UNK 900 -1.500 -0.780 0.000 1.00 0.00 O
7 | HETATM 3 O UNK 900 -0.709 1.385 0.000 1.00 0.00 O
8 | HETATM 4 C UNK 900 1.825 0.406 -0.000 1.00 0.00 C
9 | HETATM 5 C UNK 900 3.079 -0.441 -0.000 1.00 0.00 C
10 | HETATM 6 C UNK 900 -0.528 0.175 0.001 1.00 0.00 C
11 | HETATM 7 C UNK 900 -2.846 -0.292 -0.001 1.00 0.00 C
12 | HETATM 8 H UNK 900 1.805 1.039 -0.895 1.00 0.00 H
13 | HETATM 9 H UNK 900 1.806 1.040 0.894 1.00 0.00 H
14 | HETATM 10 H UNK 900 3.975 0.186 -0.001 1.00 0.00 H
15 | HETATM 11 H UNK 900 3.103 -1.093 0.879 1.00 0.00 H
16 | HETATM 12 H UNK 900 3.103 -1.094 -0.879 1.00 0.00 H
17 | HETATM 13 H UNK 900 -3.034 0.301 0.899 1.00 0.00 H
18 | HETATM 14 H UNK 900 -3.519 -1.153 -0.001 1.00 0.00 H
19 | HETATM 15 H UNK 900 -3.033 0.300 -0.901 1.00 0.00 H
20 | CONECT 1 4 6
21 | CONECT 2 6 7
22 | CONECT 3 6
23 | CONECT 3 6
24 | CONECT 4 1 5 8 9
25 | CONECT 5 4 10 11 12
26 | CONECT 6 1 2 3
27 | CONECT 6 3
28 | CONECT 7 2 13 14 15
29 | CONECT 8 4
30 | CONECT 9 4
31 | CONECT 10 5
32 | CONECT 11 5
33 | CONECT 12 5
34 | CONECT 13 7
35 | CONECT 14 7
36 | CONECT 15 7
37 | END
38 |
--------------------------------------------------------------------------------
/tests/test_files/EMC.xyz:
--------------------------------------------------------------------------------
1 | 15
2 |
3 | C 0.18700 -1.22900 -0.00000
4 | O 1.37700 -1.84500 -0.00000
5 | O -0.76900 -2.17300 -0.00000
6 | O 0.00000 -0.03100 -0.00100
7 | C 2.53300 -0.96600 -0.00000
8 | C -2.11800 -1.66800 -0.00000
9 | C 3.76700 -1.84400 -0.00000
10 | H 2.48200 -0.32400 -0.88900
11 | H 2.48200 -0.32300 0.88800
12 | H -2.29900 -1.06000 0.89400
13 | H -2.75500 -2.55600 -0.00000
14 | H -2.29900 -1.06100 -0.89600
15 | H 4.66400 -1.20900 -0.00000
16 | H 3.79600 -2.48400 0.89100
17 | H 3.79600 -2.48400 -0.89200
18 |
--------------------------------------------------------------------------------
/tests/test_files/LiPF6.xyz:
--------------------------------------------------------------------------------
1 | 8
2 | comment line
3 | P 0.000 0.000 0.000
4 | F 1.600 0.000 0.000
5 | F 0.000 1.600 0.000
6 | F 0.000 0.000 1.600
7 | F -1.600 0.000 0.000
8 | F 0.000 -1.600 0.000
9 | F 0.000 0.000 -1.600
10 | Li 1.500 1.500 1.500
11 |
--------------------------------------------------------------------------------
/tests/test_files/LiTFSi.xyz:
--------------------------------------------------------------------------------
1 | 16
2 |
3 | S 1.05507 0.03860 -0.05294
4 | C 0.83897 1.68146 0.69485
5 | N 0.44039 -0.28197 -1.68418
6 | O 0.54145 -1.11301 0.82473
7 | O 2.54386 -0.32393 -0.15642
8 | F 0.81407 2.64204 -0.29726
9 | F -0.34126 1.72090 1.40997
10 | F 1.89449 1.94060 1.54824
11 | S -1.33167 -0.30235 -1.70408
12 | Li 1.09090 1.09186 -2.90735
13 | C -2.26217 1.23900 -1.45284
14 | O -1.66432 -0.92535 -3.06792
15 | O -1.75088 -1.43998 -0.76061
16 | F -1.52007 2.30877 -1.91390
17 | F -3.45147 1.17789 -2.15425
18 | F -2.53718 1.40794 -0.11042
19 |
--------------------------------------------------------------------------------
/tests/test_files/PF6.xyz:
--------------------------------------------------------------------------------
1 | 7
2 | comment line
3 | P 0.000 0.000 0.000
4 | F 1.600 0.000 0.000
5 | F 0.000 1.600 0.000
6 | F 0.000 0.000 1.600
7 | F -1.600 0.000 0.000
8 | F 0.000 -1.600 0.000
9 | F 0.000 0.000 -1.600
10 |
--------------------------------------------------------------------------------
/tests/test_files/TFSI.xyz:
--------------------------------------------------------------------------------
1 | 15
2 |
3 | C 22.780434 54.146672 45.365179
4 | F 22.878768 54.583165 46.672383
5 | F 22.446375 52.805867 45.352616
6 | F 21.799203 54.871104 44.716093
7 | S 24.373355 54.385695 44.520460
8 | O 25.368966 53.237750 44.745699
9 | O 25.150603 55.584772 45.083778
10 | N 24.374095 54.658060 42.773780
11 | S 23.246807 53.505802 42.053388
12 | O 24.499940 53.306699 41.183721
13 | O 22.770008 54.866430 41.516197
14 | C 22.511203 53.101717 40.378193
15 | F 23.014430 53.848260 39.309506
16 | F 21.136240 53.280136 40.422653
17 | F 22.738580 51.762762 40.103706
18 |
--------------------------------------------------------------------------------
/tests/test_files/gen2_light/gen2_mdgo_unwrapped_nvt_main.dcd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouGroup/mdgo/b3d4dab22dddd67505d15e0ed3305c01d4f602e4/tests/test_files/gen2_light/gen2_mdgo_unwrapped_nvt_main.dcd
--------------------------------------------------------------------------------
/tests/test_files/subdir with spaces/EMC.xyz:
--------------------------------------------------------------------------------
1 | 15
2 |
3 | C 0.18700 -1.22900 -0.00000
4 | O 1.37700 -1.84500 -0.00000
5 | O -0.76900 -2.17300 -0.00000
6 | O 0.00000 -0.03100 -0.00100
7 | C 2.53300 -0.96600 -0.00000
8 | C -2.11800 -1.66800 -0.00000
9 | C 3.76700 -1.84400 -0.00000
10 | H 2.48200 -0.32400 -0.88900
11 | H 2.48200 -0.32300 0.88800
12 | H -2.29900 -1.06000 0.89400
13 | H -2.75500 -2.55600 -0.00000
14 | H -2.29900 -1.06100 -0.89600
15 | H 4.66400 -1.20900 -0.00000
16 | H 3.79600 -2.48400 0.89100
17 | H 3.79600 -2.48400 -0.89200
18 |
--------------------------------------------------------------------------------
/tests/test_forcefield.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import os
4 | import shutil
5 | import sys
6 | import tempfile
7 | import unittest
8 | from io import StringIO
9 |
10 | import numpy as np
11 | import pytest
12 | from pymatgen.io.lammps.data import LammpsData
13 |
14 | from mdgo.forcefield.aqueous import Aqueous, Ion
15 | from mdgo.forcefield.crawler import FFcrawler
16 |
17 | test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files")
18 |
19 |
20 | class FFcrawlerTest(unittest.TestCase):
21 | def test_chrome(self) -> None:
22 | with open(os.path.join(test_dir, "EMC.lmp")) as f:
23 | pdf = f.readlines()
24 | with open(os.path.join(test_dir, "CCOC(=O)OC.lmp")) as f:
25 | smiles = f.readlines()
26 | with open(os.path.join(test_dir, "EMC.lmp.xyz")) as f:
27 | xyz = f.readlines()
28 | with open(os.path.join(test_dir, "EMC.gro")) as f:
29 | gro = f.readlines()
30 | with open(os.path.join(test_dir, "EMC.itp")) as f:
31 | itp = f.readlines()
32 |
33 | saved_stdout = sys.stdout
34 | download_dir = tempfile.mkdtemp()
35 | try:
36 | out = StringIO()
37 | sys.stdout = out
38 |
39 | lpg = FFcrawler(download_dir, xyz=True, gromacs=True)
40 | lpg.data_from_pdb(os.path.join(test_dir, "EMC.pdb"))
41 | assert "LigParGen server connected.\nStructure info uploaded. Rendering force field...\n" in out.getvalue()
42 | assert "Force field file downloaded.\n.xyz file saved.\nForce field file saved.\n" in out.getvalue()
43 | assert os.path.exists(os.path.join(download_dir, "EMC.lmp"))
44 | assert os.path.exists(os.path.join(download_dir, "EMC.lmp.xyz"))
45 | assert os.path.exists(os.path.join(download_dir, "EMC.gro"))
46 | assert os.path.exists(os.path.join(download_dir, "EMC.itp"))
47 | with open(os.path.join(download_dir, "EMC.lmp")) as f:
48 | pdf_actual = f.readlines()
49 | assert pdf == pdf_actual
50 | with open(os.path.join(download_dir, "EMC.lmp.xyz")) as f:
51 | xyz_actual = f.readlines()
52 | assert xyz == xyz_actual
53 | with open(os.path.join(download_dir, "EMC.gro")) as f:
54 | gro_actual = f.readlines()
55 | assert gro == gro_actual
56 | with open(os.path.join(download_dir, "EMC.itp")) as f:
57 | itp_actual = f.readlines()
58 | assert itp == itp_actual
59 | lpg = FFcrawler(download_dir)
60 | lpg.data_from_smiles("CCOC(=O)OC")
61 | with open(os.path.join(download_dir, "CCOC(=O)OC.lmp")) as f:
62 | smiles_actual = f.readlines()
63 | assert smiles_actual[:13] == smiles[:13]
64 | assert smiles_actual[18:131] == smiles[18:131]
65 | assert smiles_actual[131][:26] == " 1 1 1 -0.28"
66 | assert smiles_actual[132][:25] == " 2 1 2 0.01"
67 | assert smiles_actual[145][:25] == " 15 1 15 0.10"
68 | assert smiles_actual[146:] == smiles[146:]
69 | finally:
70 | sys.stdout = saved_stdout
71 | shutil.rmtree(download_dir)
72 |
73 |
74 | class AqueousTest(unittest.TestCase):
75 | def test_get_ion(self) -> None:
76 | """
77 | Some unit tests for get_ion
78 | """
79 | # string input, all lowercase
80 | cation_ff = Aqueous.get_ion(parameter_set="lm", water_model="opc3", ion="li+")
81 | assert isinstance(cation_ff, LammpsData)
82 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.354, atol=0.001)
83 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.0064158, atol=0.0000001)
84 |
85 | # string input, using the default ion parameter set for the water model
86 | cation_ff = Aqueous.get_ion(parameter_set="lm", water_model="opc3", ion="li+")
87 | assert isinstance(cation_ff, LammpsData)
88 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.354, atol=0.001)
89 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.0064158, atol=0.0000001)
90 |
91 | # Ion object input, all lowercase
92 | li = Ion.from_formula("Li+")
93 | cation_ff = Aqueous.get_ion(parameter_set="jc", water_model="spce", ion=li)
94 | assert isinstance(cation_ff, LammpsData)
95 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 1.409, atol=0.001)
96 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.3367344, atol=0.0000001)
97 |
98 | # anion
99 | anion_ff = Aqueous.get_ion(parameter_set="jj", water_model="tip4p", ion="F-")
100 | assert isinstance(anion_ff, LammpsData)
101 | assert np.allclose(anion_ff.force_field["Pair Coeffs"]["coeff2"].item(), 3.05, atol=0.001)
102 | assert np.allclose(anion_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.71, atol=0.0000001)
103 |
104 | # divalent, uppercase water model with hyphen
105 | cation_ff = Aqueous.get_ion(parameter_set="lm", water_model="TIP3P-FB", ion="Zn+2")
106 | assert isinstance(cation_ff, LammpsData)
107 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.495, atol=0.001)
108 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.01570749, atol=0.0000001)
109 |
110 | # trivalent, with brackets in ion name
111 | cation_ff = Aqueous.get_ion(parameter_set="lm", water_model="auto", ion="La[3+]")
112 | assert isinstance(cation_ff, LammpsData)
113 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 3.056, atol=0.001)
114 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.1485017, atol=0.0000001)
115 |
116 | # model auto selection
117 | cation_ff = Aqueous.get_ion(ion="li+")
118 | assert isinstance(cation_ff, LammpsData)
119 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 1.409, atol=0.001)
120 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.3367344, atol=0.0000001)
121 | cation_ff = Aqueous.get_ion(parameter_set="jj", ion="li+")
122 | assert isinstance(cation_ff, LammpsData)
123 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.87, atol=0.001)
124 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.0005, atol=0.0000001)
125 | cation_ff = Aqueous.get_ion(water_model="opc3", ion="li+")
126 | assert isinstance(cation_ff, LammpsData)
127 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.3537544133267763, atol=0.001)
128 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.0064158, atol=0.0000001)
129 |
130 | # ion not found
131 | with pytest.raises(ValueError, match="not found in database"):
132 | cation_ff = Aqueous.get_ion(parameter_set="jj", water_model="opc3", ion="Cu+3")
133 |
134 | # parameter set not found
135 | with pytest.raises(ValueError, match="No jensen_jorgensen parameters for water model opc3 for ion"):
136 | cation_ff = Aqueous.get_ion(parameter_set="jj", water_model="opc3", ion="Cu+")
137 |
138 | # water model not found
139 | with pytest.raises(ValueError, match="No ryan parameters for water model tip8p for ion"):
140 | cation_ff = Aqueous.get_ion(parameter_set="ryan", water_model="tip8p", ion="Cu+")
141 |
142 | # mixing rule
143 | cation_ff = Aqueous.get_ion(parameter_set="jc", water_model="spce", ion="li+", mixing_rule="LB")
144 | assert isinstance(cation_ff, LammpsData)
145 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 1.409, atol=0.001)
146 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.3367344, atol=0.0000001)
147 | cation_ff = Aqueous.get_ion(parameter_set="jj", water_model="tip4p", ion="li+", mixing_rule="arithmetic")
148 | assert isinstance(cation_ff, LammpsData)
149 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.863, atol=0.001)
150 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.0005, atol=0.0000001)
151 | cation_ff = Aqueous.get_ion(
152 | parameter_set="lm", water_model="tip3pfb", ion="li+", mixing_rule="lorentz-berthelot"
153 | )
154 | assert isinstance(cation_ff, LammpsData)
155 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.352, atol=0.001)
156 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.00633615, atol=0.0000001)
157 | cation_ff = Aqueous.get_ion(parameter_set="jc", water_model="spce", ion="li+", mixing_rule="geometric")
158 | assert isinstance(cation_ff, LammpsData)
159 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 1.653, atol=0.001)
160 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.3367344, atol=0.0000001)
161 | cation_ff = Aqueous.get_ion(parameter_set="jc", water_model="tip4pew", ion="na+", mixing_rule="geometric")
162 | assert isinstance(cation_ff, LammpsData)
163 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff2"].item(), 2.260, atol=0.001)
164 | assert np.allclose(cation_ff.force_field["Pair Coeffs"]["coeff1"].item(), 0.1684375, atol=0.0000001)
165 |
166 |
167 | if __name__ == "__main__":
168 | unittest.main()
169 |
--------------------------------------------------------------------------------
/tests/test_mdgopackmol.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import os
4 | import tempfile
5 | from pathlib import Path
6 | from subprocess import TimeoutExpired
7 |
8 | import numpy as np
9 | import pytest
10 | from pymatgen.core import Molecule
11 |
12 | from mdgo.util.packmol import PackmolWrapper
13 |
14 | test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files")
15 |
16 |
17 | @pytest.fixture()
18 | def ethanol():
19 | """
20 | Returns a Molecule of ethanol
21 | """
22 | ethanol_coords = [
23 | [0.00720, -0.56870, 0.00000],
24 | [-1.28540, 0.24990, 0.00000],
25 | [1.13040, 0.31470, 0.00000],
26 | [0.03920, -1.19720, 0.89000],
27 | [0.03920, -1.19720, -0.89000],
28 | [-1.31750, 0.87840, 0.89000],
29 | [-1.31750, 0.87840, -0.89000],
30 | [-2.14220, -0.42390, -0.00000],
31 | [1.98570, -0.13650, -0.00000],
32 | ]
33 | ethanol_atoms = ["C", "C", "O", "H", "H", "H", "H", "H", "H"]
34 |
35 | return Molecule(ethanol_atoms, ethanol_coords)
36 |
37 |
38 | @pytest.fixture()
39 | def water():
40 | """
41 | Returns a Molecule of water
42 | """
43 | water_coords = [
44 | [9.626, 6.787, 12.673],
45 | [9.626, 8.420, 12.673],
46 | [10.203, 7.604, 12.673],
47 | ]
48 | water_atoms = ["H", "H", "O"]
49 |
50 | return Molecule(water_atoms, water_coords)
51 |
52 |
53 | class TestPackmolWrapper:
54 | def test_packmol_with_molecule(self, water, ethanol):
55 | """
56 | Test coords input as Molecule
57 | """
58 | with tempfile.TemporaryDirectory() as scratch_dir:
59 | pw = PackmolWrapper(
60 | scratch_dir,
61 | molecules=[
62 | {"name": "water", "number": 10, "coords": water},
63 | {"name": "ethanol", "number": 20, "coords": ethanol},
64 | ],
65 | )
66 | pw.make_packmol_input()
67 | pw.run_packmol()
68 | assert os.path.exists(os.path.join(scratch_dir, "packmol_out.xyz"))
69 | out = Molecule.from_file(os.path.join(scratch_dir, "packmol_out.xyz"))
70 | assert out.composition.num_atoms == 10 * 3 + 20 * 9
71 |
72 | def test_packmol_with_str(self):
73 | """
74 | Test coords input as strings
75 | """
76 | with tempfile.TemporaryDirectory() as scratch_dir:
77 | pw = PackmolWrapper(
78 | scratch_dir,
79 | molecules=[
80 | {"name": "EMC", "number": 10, "coords": os.path.join(test_dir, "subdir with spaces", "EMC.xyz")},
81 | {"name": "LiTFSi", "number": 20, "coords": os.path.join(test_dir, "LiTFSi.xyz")},
82 | ],
83 | )
84 | pw.make_packmol_input()
85 | pw.run_packmol()
86 | assert os.path.exists(os.path.join(scratch_dir, "packmol_out.xyz"))
87 | out = Molecule.from_file(os.path.join(scratch_dir, "packmol_out.xyz"))
88 | assert out.composition.num_atoms == 10 * 15 + 20 * 16
89 |
90 | def test_packmol_with_path(self):
91 | """
92 | Test coords input as Path. Use a subdirectory with spaces.
93 | """
94 | p1 = Path(os.path.join(test_dir, "subdir with spaces", "EMC.xyz"))
95 | p2 = Path(os.path.join(test_dir, "LiTFSi.xyz"))
96 | with tempfile.TemporaryDirectory() as scratch_dir:
97 | pw = PackmolWrapper(
98 | scratch_dir,
99 | molecules=[
100 | {"name": "EMC", "number": 10, "coords": p1},
101 | {"name": "LiTFSi", "number": 20, "coords": p2},
102 | ],
103 | )
104 | pw.make_packmol_input()
105 | pw.run_packmol()
106 | assert os.path.exists(os.path.join(scratch_dir, "packmol_out.xyz"))
107 | out = Molecule.from_file(os.path.join(scratch_dir, "packmol_out.xyz"))
108 | assert out.composition.num_atoms == 10 * 15 + 20 * 16
109 |
110 | def test_control_params(self, water, ethanol):
111 | """
112 | Check that custom control_params work and that ValueError
113 | is raised when 'ERROR' appears in stdout (even if return code is 0)
114 | """
115 | with tempfile.TemporaryDirectory() as scratch_dir:
116 | pw = PackmolWrapper(
117 | scratch_dir,
118 | molecules=[
119 | {"name": "water", "number": 1000, "coords": water},
120 | {"name": "ethanol", "number": 2000, "coords": ethanol},
121 | ],
122 | control_params={"maxit": 0, "nloop": 0},
123 | )
124 | pw.make_packmol_input()
125 | with open(os.path.join(scratch_dir, "packmol.inp")) as f:
126 | input_string = f.read()
127 | assert "maxit 0" in input_string
128 | assert "nloop 0" in input_string
129 | with pytest.raises(ValueError, match="Packmol failed with 1"):
130 | pw.run_packmol()
131 |
132 | def test_timeout(self, water, ethanol):
133 | """
134 | Check that the timeout works
135 | """
136 | with tempfile.TemporaryDirectory() as scratch_dir:
137 | pw = PackmolWrapper(
138 | scratch_dir,
139 | molecules=[
140 | {"name": "water", "number": 1000, "coords": water},
141 | {"name": "ethanol", "number": 2000, "coords": ethanol},
142 | ],
143 | )
144 | pw.make_packmol_input()
145 | with pytest.raises(TimeoutExpired):
146 | pw.run_packmol(1)
147 |
148 | def test_no_return_and_box(self, water, ethanol):
149 | """
150 | Make sure the code raises an error if packmol doesn't
151 | exit cleanly. Also verify the box arg works properly.
152 | """
153 | with tempfile.TemporaryDirectory() as scratch_dir:
154 | pw = PackmolWrapper(
155 | scratch_dir,
156 | molecules=[
157 | {"name": "water", "number": 1000, "coords": water},
158 | {"name": "ethanol", "number": 2000, "coords": ethanol},
159 | ],
160 | box=[0, 0, 0, 2, 2, 2],
161 | )
162 | pw.make_packmol_input()
163 | with open(os.path.join(scratch_dir, "packmol.inp")) as f:
164 | input_string = f.read()
165 | assert "inside box 0 0 0 2 2 2" in input_string
166 | with pytest.raises(ValueError, match="Packmol failed with"):
167 | pw.run_packmol()
168 |
169 | def test_random_seed(self, water, ethanol):
170 | """
171 | Confirm that seed = -1 generates random structures
172 | while seed = 1 is deterministic
173 | """
174 | # deterministic output
175 | with tempfile.TemporaryDirectory() as scratch_dir:
176 | pw = PackmolWrapper(
177 | scratch_dir,
178 | molecules=[
179 | {"name": "water", "number": 10, "coords": water},
180 | {"name": "ethanol", "number": 20, "coords": ethanol},
181 | ],
182 | seed=1,
183 | inputfile="input.in",
184 | outputfile="output.xyz",
185 | )
186 | pw.make_packmol_input()
187 | pw.run_packmol()
188 | out1 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
189 | pw.run_packmol()
190 | out2 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
191 | assert np.array_equal(out1.cart_coords, out2.cart_coords)
192 |
193 | # randomly generated structures
194 | with tempfile.TemporaryDirectory() as scratch_dir:
195 | pw = PackmolWrapper(
196 | scratch_dir,
197 | molecules=[
198 | {"name": "water", "number": 10, "coords": water},
199 | {"name": "ethanol", "number": 20, "coords": ethanol},
200 | ],
201 | seed=-1,
202 | inputfile="input.in",
203 | outputfile="output.xyz",
204 | )
205 | pw.make_packmol_input()
206 | pw.run_packmol()
207 | out1 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
208 | pw.run_packmol()
209 | out2 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
210 | assert not np.array_equal(out1.cart_coords, out2.cart_coords)
211 |
212 | def test_arbitrary_filenames(self, water, ethanol):
213 | """
214 | Make sure custom input and output filenames work.
215 | Use a subdirectory with spaces.
216 | """
217 | with tempfile.TemporaryDirectory() as scratch_dir:
218 | os.mkdir(os.path.join(scratch_dir, "subdirectory with spaces"))
219 | pw = PackmolWrapper(
220 | os.path.join(scratch_dir, "subdirectory with spaces"),
221 | molecules=[
222 | {"name": "water", "number": 10, "coords": water},
223 | {"name": "ethanol", "number": 20, "coords": ethanol},
224 | ],
225 | inputfile="input.in",
226 | outputfile="output.xyz",
227 | )
228 | pw.make_packmol_input()
229 | assert os.path.exists(os.path.join(scratch_dir, "subdirectory with spaces", "input.in"))
230 | pw.run_packmol()
231 | assert os.path.exists(os.path.join(scratch_dir, "subdirectory with spaces", "output.xyz"))
232 | out = Molecule.from_file(os.path.join(scratch_dir, "subdirectory with spaces", "output.xyz"))
233 | assert out.composition.num_atoms == 10 * 3 + 20 * 9
234 |
--------------------------------------------------------------------------------
/tests/test_msd.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import os
4 | import unittest
5 |
6 | import MDAnalysis
7 | import numpy as np
8 | from numpy.testing import assert_allclose
9 |
10 | try:
11 | import tidynamics as td
12 | except ImportError:
13 | td = None
14 |
15 | import pytest
16 |
17 | from mdgo.msd import (
18 | create_position_arrays,
19 | mda_msd_wrapper,
20 | msd_fft,
21 | msd_straight_forward,
22 | onsager_ii_self,
23 | parse_msd_type,
24 | total_msd,
25 | )
26 |
27 | test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files")
28 |
29 |
30 | class MyTestCase(unittest.TestCase):
31 | @classmethod
32 | def setUpClass(cls) -> None:
33 | cls.arr1 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [1, 1, 0], [4, 4, 2]])
34 | n = 100
35 | cls.arr2 = np.cumsum(np.random.choice([-1.0, 0.0, 1.0], size=(n, 3)), axis=0)
36 | cls.fft = np.array([0.0, 8.5, 7.0, 10.5, 36.0])
37 | cls.gen2 = MDAnalysis.Universe(
38 | os.path.join(test_dir, "gen2_light", "gen2_mdgo.data"),
39 | os.path.join(test_dir, "gen2_light", "gen2_mdgo_unwrapped_nvt_main.dcd"),
40 | format="LAMMPS",
41 | )
42 | cls.dims = ["x", "y", "z"]
43 |
44 | def test_msd_straight_forward(self):
45 | assert_allclose(
46 | self.fft,
47 | msd_straight_forward(self.arr1),
48 | atol=1e-12,
49 | )
50 |
51 | def test_msd_fft(self):
52 | assert_allclose(self.fft, msd_fft(self.arr1), atol=1e-12)
53 | assert_allclose(msd_straight_forward(self.arr2), msd_fft(self.arr2), atol=1e-12)
54 |
55 | def test_create_position_arrays(self):
56 | assert_allclose(
57 | np.array([21.53381769, 14.97501839, -3.87998785]),
58 | create_position_arrays(self.gen2, 0, 100, select="type 3")[50][2],
59 | )
60 | assert_allclose(
61 | np.array([-2.78550047, -11.85487624, -17.1221954]),
62 | create_position_arrays(self.gen2, 0, 100, select="type 3")[99][10],
63 | )
64 | assert_allclose(
65 | np.array([41.1079216, 34.95127106, 18.00482368]),
66 | create_position_arrays(self.gen2, 0, 100, select="type 3", center_of_mass=False)[50][2],
67 | )
68 | assert_allclose(
69 | np.array([16.98478317, 8.27190208, 5.07116079]),
70 | create_position_arrays(self.gen2, 0, 100, select="type 3", center_of_mass=False)[99][10],
71 | )
72 |
73 | def test_parse_msd_type(self):
74 | xyz = parse_msd_type("xyz")
75 | assert ["x", "y", "z"] == self.dims[xyz[0] : xyz[1] : xyz[2]]
76 | xy = parse_msd_type("xy")
77 | assert ["x", "y"] == self.dims[xy[0] : xy[1] : xy[2]]
78 | yz = parse_msd_type("yz")
79 | assert ["y", "z"] == self.dims[yz[0] : yz[1] : yz[2]]
80 | xz = parse_msd_type("xz")
81 | assert ["x", "z"] == self.dims[xz[0] : xz[1] : xz[2]]
82 | x = parse_msd_type("x")
83 | assert ["x"] == self.dims[x[0] : x[1] : x[2]]
84 | y = parse_msd_type("y")
85 | assert ["y"] == self.dims[y[0] : y[1] : y[2]]
86 | z = parse_msd_type("z")
87 | assert ["z"] == self.dims[z[0] : z[1] : z[2]]
88 |
89 | def test_onsager_ii_self(self):
90 | onsager_ii_self_fft = onsager_ii_self(self.gen2, 0, 100, select="type 3")
91 | onsager_ii_self_nocom = onsager_ii_self(self.gen2, 0, 100, select="type 3", center_of_mass=False)
92 | onsager_ii_self_nofft = onsager_ii_self(self.gen2, 0, 100, select="type 3", fft=False)
93 |
94 | assert_allclose(32.14254152556588, onsager_ii_self_fft[50])
95 | assert_allclose(63.62190983, onsager_ii_self_fft[98])
96 | assert_allclose(67.29990019, onsager_ii_self_fft[99])
97 | assert_allclose(32.14254152556588, onsager_ii_self_nofft[50])
98 | assert_allclose(63.62190983, onsager_ii_self_nofft[98])
99 | assert_allclose(67.29990019, onsager_ii_self_nofft[99])
100 | assert_allclose(32.338364098424634, onsager_ii_self_nocom[50])
101 | assert_allclose(63.52915984813752, onsager_ii_self_nocom[98])
102 | assert_allclose(67.29599346166411, onsager_ii_self_nocom[99])
103 |
104 | def test_mda_msd_wrapper(self):
105 | mda_msd_cation = mda_msd_wrapper(self.gen2, 0, 100, select="type 3", fft=False)
106 | mda_msd_anion = mda_msd_wrapper(self.gen2, 0, 100, select="type 1", fft=False)
107 | assert_allclose(32.338364098424634, mda_msd_cation[50])
108 | assert_allclose(63.52915984813752, mda_msd_cation[98])
109 | assert_allclose(67.29599346166411, mda_msd_cation[99])
110 | assert_allclose(42.69200176568008, mda_msd_anion[50])
111 | assert_allclose(86.9209518, mda_msd_anion[98])
112 | assert_allclose(89.84668178, mda_msd_anion[99])
113 | assert_allclose(
114 | onsager_ii_self(self.gen2, 0, 10, select="type 3", msd_type="x", center_of_mass=False),
115 | mda_msd_wrapper(self.gen2, 0, 10, select="type 3", msd_type="x", fft=False),
116 | atol=1e-12,
117 | )
118 | assert_allclose(
119 | onsager_ii_self(self.gen2, 0, 10, select="type 3", msd_type="y", center_of_mass=False),
120 | mda_msd_wrapper(self.gen2, 0, 10, select="type 3", msd_type="y", fft=False),
121 | atol=1e-12,
122 | )
123 | assert_allclose(
124 | onsager_ii_self(self.gen2, 0, 10, select="type 3", msd_type="z", center_of_mass=False),
125 | mda_msd_wrapper(self.gen2, 0, 10, select="type 3", msd_type="z", fft=False),
126 | atol=1e-12,
127 | )
128 | assert_allclose(
129 | onsager_ii_self(self.gen2, 0, 100, select="type 3", msd_type="xy", center_of_mass=False),
130 | mda_msd_wrapper(self.gen2, 0, 100, select="type 3", msd_type="xy", fft=False),
131 | atol=1e-12,
132 | )
133 | assert_allclose(
134 | onsager_ii_self(self.gen2, 0, 100, select="type 3", msd_type="yz", center_of_mass=False),
135 | mda_msd_wrapper(self.gen2, 0, 100, select="type 3", msd_type="yz", fft=False),
136 | atol=1e-12,
137 | )
138 | assert_allclose(
139 | onsager_ii_self(self.gen2, 0, 100, select="type 3", msd_type="xz", center_of_mass=False),
140 | mda_msd_wrapper(self.gen2, 0, 100, select="type 3", msd_type="xz", fft=False),
141 | atol=1e-12,
142 | )
143 | if td is not None:
144 | assert_allclose(
145 | mda_msd_cation,
146 | mda_msd_wrapper(self.gen2, 0, 100, select="type 3"),
147 | atol=1e-12,
148 | )
149 |
150 | def test_total_msd(self):
151 | total_builtin_cation = total_msd(self.gen2, 0, 100, select="type 3", fft=True, built_in=True)
152 | total_mda_cation = total_msd(
153 | self.gen2, 0, 100, select="type 3", fft=False, built_in=False, center_of_mass=False
154 | )
155 | assert_allclose(total_builtin_cation[50], 32.14254152556588)
156 | assert_allclose(total_mda_cation[50], 32.338364098424634)
157 | with pytest.raises(
158 | ValueError,
159 | match="Warning! MDAnalysis does not support subtracting center "
160 | "of mass. Calculating without subtracting...",
161 | ):
162 | total_msd(self.gen2, 0, 100, select="type 3", fft=True, built_in=False, center_of_mass=True)
163 |
164 |
165 | if __name__ == "__main__":
166 | unittest.main()
167 |
--------------------------------------------------------------------------------
/tests/test_residence_time.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import unittest
4 |
5 |
6 | class MyTestCase(unittest.TestCase):
7 | def test_something(self):
8 | assert True is True
9 |
10 |
11 | if __name__ == "__main__":
12 | unittest.main()
13 |
--------------------------------------------------------------------------------
/tests/test_util.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import unittest
4 |
5 |
6 | class MyTestCase(unittest.TestCase):
7 | def test_something(self):
8 | assert True is True
9 |
10 |
11 | if __name__ == "__main__":
12 | unittest.main()
13 |
--------------------------------------------------------------------------------
/tests/test_volume.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import os
4 | import unittest
5 |
6 | from numpy.testing import assert_allclose
7 | from pymatgen.core import Molecule
8 |
9 | from mdgo.util.volume import molecular_volume
10 |
11 | test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files")
12 |
13 |
14 | class MyTestCase(unittest.TestCase):
15 | @classmethod
16 | def setUpClass(cls) -> None:
17 | cls.ec = Molecule.from_file(filename=os.path.join(test_dir, "EC.xyz"))
18 | cls.emc = Molecule.from_file(filename=os.path.join(test_dir, "EMC.xyz"))
19 | cls.dec = Molecule.from_file(filename=os.path.join(test_dir, "DEC.xyz"))
20 | cls.pf6 = Molecule.from_file(filename=os.path.join(test_dir, "PF6.xyz"))
21 | cls.tfsi = Molecule.from_file(filename=os.path.join(test_dir, "TFSI.xyz"))
22 | cls.litfsi = Molecule.from_file(filename=os.path.join(test_dir, "LiTFSI.xyz"))
23 | cls.lipf6 = Molecule.from_file(filename=os.path.join(test_dir, "LiPF6.xyz"))
24 |
25 | def test_molecular_volume(self) -> None:
26 | lipf6_volume_1 = molecular_volume(self.lipf6)
27 | lipf6_volume_2 = molecular_volume(self.lipf6, res=1.0)
28 | lipf6_volume_3 = molecular_volume(self.lipf6, radii_type="Lange")
29 | lipf6_volume_4 = molecular_volume(self.lipf6, radii_type="pymatgen")
30 | lipf6_volume_5 = molecular_volume(self.lipf6, molar_volume=False)
31 | assert_allclose(lipf6_volume_1, 47.62, atol=0.01)
32 | assert_allclose(lipf6_volume_2, 43.36, atol=0.01)
33 | assert_allclose(lipf6_volume_3, 41.49, atol=0.01)
34 | assert_allclose(lipf6_volume_4, 51.94, atol=0.01)
35 | assert_allclose(lipf6_volume_5, 79.08, atol=0.01)
36 | ec_volume_1 = molecular_volume(self.ec)
37 | ec_volume_2 = molecular_volume(self.ec, exclude_h=False)
38 | ec_volume_3 = molecular_volume(self.ec, res=1.0)
39 | ec_volume_4 = molecular_volume(self.ec, radii_type="Lange")
40 | ec_volume_5 = molecular_volume(self.ec, radii_type="pymatgen")
41 | ec_volume_6 = molecular_volume(self.ec, molar_volume=False)
42 | assert_allclose(ec_volume_1, 38.44, atol=0.01)
43 | assert_allclose(ec_volume_2, 43.17, atol=0.01)
44 | assert_allclose(ec_volume_3, 40.95, atol=0.01)
45 | assert_allclose(ec_volume_4, 41.07, atol=0.01)
46 | assert_allclose(ec_volume_5, 38.44, atol=0.01)
47 | assert_allclose(ec_volume_6, 63.83, atol=0.01)
48 | litfsi_volume_1 = molecular_volume(self.litfsi)
49 | litfsi_volume_2 = molecular_volume(self.litfsi, exclude_h=False)
50 | litfsi_volume_3 = molecular_volume(self.litfsi, res=1.0)
51 | litfsi_volume_4 = molecular_volume(self.litfsi, radii_type="Lange")
52 | litfsi_volume_5 = molecular_volume(self.litfsi, radii_type="pymatgen")
53 | litfsi_volume_6 = molecular_volume(self.litfsi, molar_volume=False)
54 | litfsi_volume_7 = molecular_volume(self.litfsi, mode="act", x_size=8, y_size=8, z_size=8)
55 | assert_allclose(litfsi_volume_1, 100.16, atol=0.01)
56 | assert_allclose(litfsi_volume_2, 100.16, atol=0.01)
57 | assert_allclose(litfsi_volume_3, 99.37, atol=0.01)
58 | assert_allclose(litfsi_volume_4, 90.78, atol=0.01)
59 | assert_allclose(litfsi_volume_5, 105.31, atol=0.01)
60 | assert_allclose(litfsi_volume_6, 166.32, atol=0.01)
61 | assert_allclose(litfsi_volume_7, 124.66, atol=0.01)
62 |
63 |
64 | if __name__ == "__main__":
65 | unittest.main()
66 |
--------------------------------------------------------------------------------