asyncmd 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asyncmd-0.4.0/PKG-INFO +90 -0
- asyncmd-0.4.0/README.md +42 -0
- asyncmd-0.4.0/pyproject.toml +100 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/__init__.py +7 -0
- asyncmd-0.4.0/src/asyncmd/_config.py +33 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/_version.py +22 -36
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/config.py +66 -33
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/gromacs/__init__.py +3 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/gromacs/mdconfig.py +7 -17
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/gromacs/mdengine.py +448 -424
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/gromacs/utils.py +40 -23
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/mdconfig.py +55 -165
- asyncmd-0.4.0/src/asyncmd/mdengine.py +181 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/slurm.py +210 -77
- asyncmd-0.4.0/src/asyncmd/tools.py +365 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/trajectory/__init__.py +19 -1
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/trajectory/convert.py +133 -97
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/trajectory/functionwrapper.py +211 -159
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/trajectory/propagate.py +308 -260
- asyncmd-0.4.0/src/asyncmd/trajectory/trajectory.py +846 -0
- asyncmd-0.4.0/src/asyncmd/trajectory/trajectory_cache.py +365 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd/utils.py +18 -13
- asyncmd-0.4.0/src/asyncmd.egg-info/PKG-INFO +90 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd.egg-info/SOURCES.txt +5 -1
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd.egg-info/requires.txt +7 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/tests/test_mdconfig.py +34 -148
- asyncmd-0.4.0/tests/test_slurm.py +231 -0
- asyncmd-0.4.0/tests/test_tools.py +303 -0
- asyncmd-0.4.0/tests/test_utils.py +44 -0
- asyncmd-0.3.2/PKG-INFO +0 -179
- asyncmd-0.3.2/README.md +0 -140
- asyncmd-0.3.2/pyproject.toml +0 -49
- asyncmd-0.3.2/src/asyncmd/_config.py +0 -26
- asyncmd-0.3.2/src/asyncmd/mdengine.py +0 -100
- asyncmd-0.3.2/src/asyncmd/tools.py +0 -86
- asyncmd-0.3.2/src/asyncmd/trajectory/trajectory.py +0 -1103
- asyncmd-0.3.2/src/asyncmd.egg-info/PKG-INFO +0 -179
- {asyncmd-0.3.2 → asyncmd-0.4.0}/LICENSE +0 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/setup.cfg +0 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd.egg-info/dependency_links.txt +0 -0
- {asyncmd-0.3.2 → asyncmd-0.4.0}/src/asyncmd.egg-info/top_level.txt +0 -0
asyncmd-0.4.0/PKG-INFO
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: asyncmd
|
3
|
+
Version: 0.4.0
|
4
|
+
Summary: asyncmd is a library to write concurrent code to run and analyze molecular dynamics simulations using pythons async/await syntax.
|
5
|
+
Author-email: Hendrik Jung <hendrik.jung@biophys.mpg.de>
|
6
|
+
Maintainer-email: Hendrik Jung <hendrik.jung@biophys.mpg.de>
|
7
|
+
Project-URL: Documentation, https://asyncmd.readthedocs.io/en/latest/
|
8
|
+
Project-URL: Repository, https://github.com/bio-phys/asyncmd.git
|
9
|
+
Project-URL: Issues, https://github.com/bio-phys/asyncmd/issues
|
10
|
+
Keywords: molecular dynamics,molecular-dynamics,MD,high performance computing,HPC,slurm,SLURM,gromacs,GROMACS
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
13
|
+
Classifier: Natural Language :: English
|
14
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
15
|
+
Classifier: Operating System :: OS Independent
|
16
|
+
Classifier: Programming Language :: Python
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
22
|
+
Requires-Python: >=3.10
|
23
|
+
Description-Content-Type: text/markdown
|
24
|
+
License-File: LICENSE
|
25
|
+
Requires-Dist: aiofiles
|
26
|
+
Requires-Dist: mdanalysis
|
27
|
+
Requires-Dist: numpy
|
28
|
+
Requires-Dist: scipy
|
29
|
+
Provides-Extra: docs
|
30
|
+
Requires-Dist: sphinx; extra == "docs"
|
31
|
+
Requires-Dist: myst-nb; extra == "docs"
|
32
|
+
Requires-Dist: sphinx-book-theme; extra == "docs"
|
33
|
+
Provides-Extra: tests
|
34
|
+
Requires-Dist: pytest; extra == "tests"
|
35
|
+
Requires-Dist: pytest-asyncio; extra == "tests"
|
36
|
+
Provides-Extra: tests-all
|
37
|
+
Requires-Dist: asyncmd[tests]; extra == "tests-all"
|
38
|
+
Requires-Dist: h5py; extra == "tests-all"
|
39
|
+
Requires-Dist: coverage; extra == "tests-all"
|
40
|
+
Requires-Dist: pytest-cov; extra == "tests-all"
|
41
|
+
Provides-Extra: dev
|
42
|
+
Requires-Dist: asyncmd[docs,tests-all]; extra == "dev"
|
43
|
+
Requires-Dist: jupyterlab; extra == "dev"
|
44
|
+
Requires-Dist: ipywidgets; extra == "dev"
|
45
|
+
Requires-Dist: tqdm; extra == "dev"
|
46
|
+
Requires-Dist: pylint; extra == "dev"
|
47
|
+
Dynamic: license-file
|
48
|
+
|
49
|
+
# asyncmd
|
50
|
+
|
51
|
+
[![codecov][codecov-badge]][codecov-link] [![Documentation Status][rtd-badge]][rtd-link] [![PyPI][pypi-badge]][pypi-link]
|
52
|
+
|
53
|
+
asyncmd is a library to write **concurrent** code to run and analyze molecular dynamics simulations using pythons **async/await** syntax.
|
54
|
+
Computationally costly operations can be performed locally or submitted to a queuing system.
|
55
|
+
|
56
|
+
asyncmd enables users to construct complex molecular dynamics (MD) workflows or develop and implement trajectory based enhanced sampling methods with the following key features:
|
57
|
+
|
58
|
+
- flexible, programmatic and parallel setup, control, and analysis of an arbitrary number of MD simulations
|
59
|
+
- dictionary-like interface to the MD parameters
|
60
|
+
- parallelized application of user defined (python) functions on trajectories (including the automatic caching of calculated values)
|
61
|
+
- propagation of MD until any or all user-supplied conditions are fulfilled on the trajectory
|
62
|
+
- extract molecular configurations from trajectories to (re)start an arbitrary number of MD simulations from it
|
63
|
+
|
64
|
+
## Installation
|
65
|
+
|
66
|
+
The following command will install asyncmd from [PyPi][pypi-link]:
|
67
|
+
|
68
|
+
```bash
|
69
|
+
pip install asyncmd
|
70
|
+
```
|
71
|
+
|
72
|
+
## Documentation
|
73
|
+
|
74
|
+
See the [asyncmd documentation][rtd-link] for more information.
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
All contributions are appreciated! Please refer to the [documentation][rtd-link] for information.
|
79
|
+
|
80
|
+
---
|
81
|
+
<sub>This README.md is printed from 100% recycled electrons.</sub>
|
82
|
+
|
83
|
+
[codecov-link]: https://app.codecov.io/gh/bio-phys/asyncmd
|
84
|
+
[codecov-badge]: https://img.shields.io/codecov/c/github/bio-phys/asyncmd
|
85
|
+
|
86
|
+
[rtd-link]: https://asyncmd.readthedocs.io/en/latest/
|
87
|
+
[rtd-badge]: https://readthedocs.org/projects/asyncmd/badge/?version=latest
|
88
|
+
|
89
|
+
[pypi-link]: https://pypi.org/project/asyncmd/
|
90
|
+
[pypi-badge]: https://img.shields.io/pypi/v/asyncmd
|
asyncmd-0.4.0/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# asyncmd
|
2
|
+
|
3
|
+
[![codecov][codecov-badge]][codecov-link] [![Documentation Status][rtd-badge]][rtd-link] [![PyPI][pypi-badge]][pypi-link]
|
4
|
+
|
5
|
+
asyncmd is a library to write **concurrent** code to run and analyze molecular dynamics simulations using pythons **async/await** syntax.
|
6
|
+
Computationally costly operations can be performed locally or submitted to a queuing system.
|
7
|
+
|
8
|
+
asyncmd enables users to construct complex molecular dynamics (MD) workflows or develop and implement trajectory based enhanced sampling methods with the following key features:
|
9
|
+
|
10
|
+
- flexible, programmatic and parallel setup, control, and analysis of an arbitrary number of MD simulations
|
11
|
+
- dictionary-like interface to the MD parameters
|
12
|
+
- parallelized application of user defined (python) functions on trajectories (including the automatic caching of calculated values)
|
13
|
+
- propagation of MD until any or all user-supplied conditions are fulfilled on the trajectory
|
14
|
+
- extract molecular configurations from trajectories to (re)start an arbitrary number of MD simulations from it
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
The following command will install asyncmd from [PyPi][pypi-link]:
|
19
|
+
|
20
|
+
```bash
|
21
|
+
pip install asyncmd
|
22
|
+
```
|
23
|
+
|
24
|
+
## Documentation
|
25
|
+
|
26
|
+
See the [asyncmd documentation][rtd-link] for more information.
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
All contributions are appreciated! Please refer to the [documentation][rtd-link] for information.
|
31
|
+
|
32
|
+
---
|
33
|
+
<sub>This README.md is printed from 100% recycled electrons.</sub>
|
34
|
+
|
35
|
+
[codecov-link]: https://app.codecov.io/gh/bio-phys/asyncmd
|
36
|
+
[codecov-badge]: https://img.shields.io/codecov/c/github/bio-phys/asyncmd
|
37
|
+
|
38
|
+
[rtd-link]: https://asyncmd.readthedocs.io/en/latest/
|
39
|
+
[rtd-badge]: https://readthedocs.org/projects/asyncmd/badge/?version=latest
|
40
|
+
|
41
|
+
[pypi-link]: https://pypi.org/project/asyncmd/
|
42
|
+
[pypi-badge]: https://img.shields.io/pypi/v/asyncmd
|
@@ -0,0 +1,100 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools >= 64"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "asyncmd"
|
7
|
+
version = "0.4.0"
|
8
|
+
dependencies = ["aiofiles",
|
9
|
+
"mdanalysis",
|
10
|
+
"numpy",
|
11
|
+
"scipy",
|
12
|
+
]
|
13
|
+
# if you change requires-python also change py-version for pylint below!
|
14
|
+
requires-python = ">=3.10"
|
15
|
+
authors = [{ name = "Hendrik Jung", email = "hendrik.jung@biophys.mpg.de"}]
|
16
|
+
maintainers = [{ name = "Hendrik Jung", email = "hendrik.jung@biophys.mpg.de"}]
|
17
|
+
description = """asyncmd is a library to write concurrent code to run and \
|
18
|
+
analyze molecular dynamics simulations using pythons async/await syntax."""
|
19
|
+
readme = "README.md"
|
20
|
+
keywords = ["molecular dynamics", "molecular-dynamics", "MD",
|
21
|
+
"high performance computing", "HPC",
|
22
|
+
"slurm", "SLURM",
|
23
|
+
"gromacs", "GROMACS",
|
24
|
+
]
|
25
|
+
classifiers = [
|
26
|
+
"Development Status :: 4 - Beta",
|
27
|
+
"Intended Audience :: Science/Research",
|
28
|
+
"Natural Language :: English",
|
29
|
+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
30
|
+
"Operating System :: OS Independent",
|
31
|
+
"Programming Language :: Python",
|
32
|
+
"Programming Language :: Python :: 3",
|
33
|
+
"Topic :: Scientific/Engineering",
|
34
|
+
"Topic :: Scientific/Engineering :: Chemistry",
|
35
|
+
"Topic :: Scientific/Engineering :: Physics",
|
36
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
37
|
+
]
|
38
|
+
|
39
|
+
[project.optional-dependencies]
|
40
|
+
docs = ["sphinx", "myst-nb", "sphinx-book-theme"]
|
41
|
+
tests = ["pytest", "pytest-asyncio"]
|
42
|
+
tests-all = ["asyncmd[tests]", "h5py", "coverage", "pytest-cov"]
|
43
|
+
dev = ["asyncmd[docs,tests-all]",
|
44
|
+
"jupyterlab", "ipywidgets", "tqdm", # needed for example notebooks
|
45
|
+
"pylint",
|
46
|
+
]
|
47
|
+
|
48
|
+
[project.urls]
|
49
|
+
Documentation = "https://asyncmd.readthedocs.io/en/latest/"
|
50
|
+
Repository = "https://github.com/bio-phys/asyncmd.git"
|
51
|
+
Issues = "https://github.com/bio-phys/asyncmd/issues"
|
52
|
+
|
53
|
+
[tool.setuptools.packages.find]
|
54
|
+
where = ["src"]
|
55
|
+
|
56
|
+
# pylint configuration
|
57
|
+
[tool.pylint.main]
|
58
|
+
# Return non-zero exit code if any of these messages/categories are detected,
|
59
|
+
# even if score is above --fail-under value. Syntax same as enable. Messages
|
60
|
+
# specified are enabled, while categories only check already-enabled messages.
|
61
|
+
fail-on = ["E"]
|
62
|
+
|
63
|
+
# Specify a score threshold under which the program will exit with error.
|
64
|
+
fail-under = 9.6
|
65
|
+
|
66
|
+
# List of plugins (as comma separated values of python module names) to load,
|
67
|
+
# usually to register additional checkers.
|
68
|
+
load-plugins = ["pylint.extensions.bad_builtin",
|
69
|
+
"pylint.extensions.broad_try_clause",
|
70
|
+
"pylint.extensions.check_elif",
|
71
|
+
"pylint.extensions.code_style",
|
72
|
+
"pylint.extensions.comparison_placement",
|
73
|
+
"pylint.extensions.consider_refactoring_into_while_condition",
|
74
|
+
"pylint.extensions.dict_init_mutate",
|
75
|
+
"pylint.extensions.docparams",
|
76
|
+
"pylint.extensions.eq_without_hash",
|
77
|
+
"pylint.extensions.for_any_all",
|
78
|
+
"pylint.extensions.overlapping_exceptions",
|
79
|
+
"pylint.extensions.redefined_loop_name",
|
80
|
+
"pylint.extensions.redefined_variable_type",
|
81
|
+
"pylint.extensions.set_membership",
|
82
|
+
"pylint.extensions.typing",
|
83
|
+
]
|
84
|
+
|
85
|
+
# Minimum Python version to use for version dependent checks. Will default to the
|
86
|
+
# version used to run pylint.
|
87
|
+
py-version = "3.10"
|
88
|
+
|
89
|
+
[tool.pylint."messages control"]
|
90
|
+
# Enable the message, report, category or checker with the given id(s). You can
|
91
|
+
# either give multiple identifier separated by comma (,) or put this option
|
92
|
+
# multiple time (only on the command line, not in the configuration file where it
|
93
|
+
# should appear only once). See also the "--disable" option for examples.
|
94
|
+
enable = ["all"]
|
95
|
+
|
96
|
+
[tool.pylint.design]
|
97
|
+
# increase maximum number of arguments for functions/methods (default=5)
|
98
|
+
max-args = 6
|
99
|
+
# but decrease maximum number of positional arguments (default=5)
|
100
|
+
max-positional-arguments = 4
|
@@ -12,6 +12,13 @@
|
|
12
12
|
#
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
The asyncmd toplevel module.
|
17
|
+
|
18
|
+
It imports the central Trajectory objects and the config(uration) functions for
|
19
|
+
user convenience.
|
20
|
+
It also makes public some information on the asyncmd version/git_hash.
|
21
|
+
"""
|
15
22
|
from ._version import __version__, __git_hash__
|
16
23
|
|
17
24
|
from . import config
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# This file is part of asyncmd.
|
2
|
+
#
|
3
|
+
# asyncmd is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# asyncmd is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
Configuration dictionaries to influence asyncmd runtime behavior.
|
17
|
+
|
18
|
+
NOTE: This file **only** contains the dictionaries with the values
|
19
|
+
and **no** functions to set them, the funcs all live in 'config.py'.
|
20
|
+
The idea here is that we can then without any issues import additional
|
21
|
+
stuff (like the config functions from 'slurm.py') in 'config.py'
|
22
|
+
without risking circular imports because all asyncmd files should only
|
23
|
+
need to import the _CONFIG and _SEMAPHORES dicts from '_config.py'.
|
24
|
+
"""
|
25
|
+
import asyncio
|
26
|
+
import typing
|
27
|
+
|
28
|
+
|
29
|
+
_GLOBALS: dict[str, typing.Any] = {}
|
30
|
+
_SEMAPHORES: dict[str, asyncio.BoundedSemaphore] = {}
|
31
|
+
# These semaphores are optional (i.e. can be None, which means unlimited)
|
32
|
+
# e.g. slurm_max_jobs
|
33
|
+
_OPT_SEMAPHORES: dict[str, asyncio.BoundedSemaphore | None] = {}
|
@@ -12,24 +12,15 @@
|
|
12
12
|
#
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
Helpers to populate asyncmd.__version__.
|
17
|
+
|
18
|
+
If we are in a git-repository (and detect commits since the last version-tagged
|
19
|
+
commit) we will add a git-hash to the version.
|
20
|
+
"""
|
15
21
|
import os
|
16
22
|
import subprocess
|
17
|
-
|
18
|
-
|
19
|
-
def _get_version_from_pyproject():
|
20
|
-
"""Get version string from pyproject.toml file."""
|
21
|
-
pyproject_toml = os.path.join(os.path.dirname(__file__),
|
22
|
-
"../../pyproject.toml")
|
23
|
-
with open(pyproject_toml) as f:
|
24
|
-
line = f.readline()
|
25
|
-
while line:
|
26
|
-
if line.startswith("version ="):
|
27
|
-
version_line = line
|
28
|
-
break
|
29
|
-
line = f.readline()
|
30
|
-
version = version_line.strip().split(" = ")[1]
|
31
|
-
version = version.replace('"', '').replace("'", "")
|
32
|
-
return version
|
23
|
+
import importlib.metadata
|
33
24
|
|
34
25
|
|
35
26
|
def _get_git_hash_and_tag():
|
@@ -37,39 +28,34 @@ def _get_git_hash_and_tag():
|
|
37
28
|
git_hash = ""
|
38
29
|
git_date = ""
|
39
30
|
git_tag = ""
|
40
|
-
|
31
|
+
with subprocess.Popen(
|
41
32
|
["git", "log", "-1", "--format='%H || %as || %(describe:tags=true,match=v*)'"],
|
42
33
|
stdout=subprocess.PIPE,
|
43
34
|
stderr=subprocess.PIPE,
|
44
35
|
cwd=os.path.dirname(__file__),
|
45
|
-
|
46
|
-
|
47
|
-
|
36
|
+
) as p:
|
37
|
+
stdout, _ = p.communicate()
|
38
|
+
returncode = p.returncode
|
39
|
+
if not returncode:
|
48
40
|
git_hash, git_date, git_describe = (stdout.decode("utf-8")
|
49
41
|
.replace("'", "").replace('"', '')
|
50
42
|
.strip().split("||"))
|
51
43
|
git_date = git_date.strip().replace("-", "")
|
52
44
|
git_describe = git_describe.strip()
|
53
|
-
if "-" not in git_describe
|
45
|
+
if git_describe and "-" not in git_describe:
|
54
46
|
# git-describe returns either the git-tag or (if we are not exactly
|
55
47
|
# at a tag) something like
|
56
48
|
# $GITTAG-$NUM_COMMITS_DISTANCE-$CURRENT_COMMIT_HASH
|
57
49
|
git_tag = git_describe[1:] # strip of the 'v'
|
58
50
|
return git_hash, git_date, git_tag
|
59
51
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
52
|
+
|
53
|
+
_version = importlib.metadata.version("asyncmd")
|
54
|
+
_git_hash, _git_date, _git_tag = _get_git_hash_and_tag()
|
55
|
+
__git_hash__ = _git_hash
|
56
|
+
if _version == _git_tag or not _git_hash:
|
57
|
+
# dont append git_hash to version, if it is a version-tagged commit or if
|
58
|
+
# git_hash is empty (happens if git is installed but we are not in a repo)
|
59
|
+
__version__ = _version
|
67
60
|
else:
|
68
|
-
|
69
|
-
__git_hash__ = _git_hash
|
70
|
-
if _version == _git_tag or _git_hash == "":
|
71
|
-
# dont append git_hash to version, if it is a version-tagged commit or if
|
72
|
-
# git_hash is empty (happens if git is installed but we are not in a repo)
|
73
|
-
__version__ = _version
|
74
|
-
else:
|
75
|
-
__version__ = _version + f"+git{_git_date}.{_git_hash[:7]}"
|
61
|
+
__version__ = _version + f"+git{_git_date}.{_git_hash[:7]}"
|
@@ -12,23 +12,29 @@
|
|
12
12
|
#
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
This module contains the implementation of functions configuring asyncmd behavior.
|
17
|
+
|
18
|
+
It also import the configuration functions for submodules (like slurm) to make
|
19
|
+
them accessible to users in one central place.
|
20
|
+
"""
|
15
21
|
import os
|
16
22
|
import asyncio
|
17
23
|
import logging
|
18
24
|
import resource
|
19
|
-
import typing
|
20
25
|
|
21
26
|
|
22
|
-
from ._config import _GLOBALS, _SEMAPHORES
|
27
|
+
from ._config import _GLOBALS, _SEMAPHORES, _OPT_SEMAPHORES
|
28
|
+
from .trajectory.trajectory import _update_cache_type_for_all_trajectories
|
29
|
+
# pylint: disable-next=unused-import
|
23
30
|
from .slurm import set_slurm_settings, set_all_slurm_settings
|
24
|
-
# TODO: Do we want to set the _GLOBALS defaults here? E.g. CACHE_TYPE="npz"?
|
25
31
|
|
26
32
|
|
27
33
|
logger = logging.getLogger(__name__)
|
28
34
|
|
29
35
|
|
30
36
|
# can be called by the user to (re) set maximum number of processes used
|
31
|
-
def set_max_process(num=None, max_num=None):
|
37
|
+
def set_max_process(num: int | None = None, max_num: int | None = None) -> None:
|
32
38
|
"""
|
33
39
|
Set the maximum number of concurrent python processes.
|
34
40
|
|
@@ -45,16 +51,14 @@ def set_max_process(num=None, max_num=None):
|
|
45
51
|
spawning hundreds of processes.
|
46
52
|
"""
|
47
53
|
# NOTE: I think we should use a conservative default, e.g. 0.25*cpu_count()
|
48
|
-
#
|
54
|
+
# pylint: disable-next=global-variable-not-assigned
|
49
55
|
global _SEMAPHORES
|
50
56
|
if num is None:
|
51
|
-
logical_cpu_count
|
52
|
-
|
53
|
-
num = int(logical_cpu_count / 4)
|
57
|
+
if (logical_cpu_count := os.cpu_count()) is not None:
|
58
|
+
num = max(1, int(logical_cpu_count / 4))
|
54
59
|
else:
|
55
60
|
# fallback if os.cpu_count() can not determine the number of cpus
|
56
61
|
# play it save and not have more than 2?
|
57
|
-
# TODO: think about a good number!
|
58
62
|
num = 2
|
59
63
|
if max_num is not None:
|
60
64
|
num = min((num, max_num))
|
@@ -64,7 +68,7 @@ def set_max_process(num=None, max_num=None):
|
|
64
68
|
set_max_process()
|
65
69
|
|
66
70
|
|
67
|
-
def set_max_files_open(num:
|
71
|
+
def set_max_files_open(num: int | None = None, margin: int = 30) -> None:
|
68
72
|
"""
|
69
73
|
Set the maximum number of concurrently opened files.
|
70
74
|
|
@@ -86,10 +90,11 @@ def set_max_files_open(num: typing.Optional[int] = None, margin: int = 30):
|
|
86
90
|
"""
|
87
91
|
# ensure that we do not open too many files
|
88
92
|
# resource.getrlimit returns a tuple (soft, hard); we take the soft-limit
|
89
|
-
# and to be sure 30 less (the reason
|
93
|
+
# and to be sure 30 less (the reason being that we can not use the
|
90
94
|
# semaphores from non-async code, but sometimes use the sync subprocess.run
|
91
95
|
# and subprocess.check_call [which also need files/pipes to work])
|
92
96
|
# also maybe we need other open files like a storage :)
|
97
|
+
# pylint: disable-next=global-variable-not-assigned
|
93
98
|
global _SEMAPHORES
|
94
99
|
rlim_soft = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
|
95
100
|
if num is None:
|
@@ -125,10 +130,9 @@ set_max_files_open()
|
|
125
130
|
# SLURM semaphore stuff:
|
126
131
|
# TODO: move this to slurm.py? and initialize only if slurm is available?
|
127
132
|
# slurm max job semaphore, if the user sets it it will be used,
|
128
|
-
# otherwise we can use an unlimited number of
|
133
|
+
# otherwise we can use an unlimited number of synchronous slurm-jobs
|
129
134
|
# (if the simulation requires that much)
|
130
|
-
|
131
|
-
def set_slurm_max_jobs(num: typing.Union[int, None]):
|
135
|
+
def set_slurm_max_jobs(num: int | None) -> None:
|
132
136
|
"""
|
133
137
|
Set the maximum number of simultaneously submitted SLURM jobs.
|
134
138
|
|
@@ -138,66 +142,95 @@ def set_slurm_max_jobs(num: typing.Union[int, None]):
|
|
138
142
|
The maximum number of simultaneous SLURM jobs for this invocation of
|
139
143
|
python/asyncmd. `None` means do not limit the maximum number of jobs.
|
140
144
|
"""
|
141
|
-
global
|
145
|
+
# pylint: disable-next=global-variable-not-assigned
|
146
|
+
global _OPT_SEMAPHORES
|
142
147
|
if num is None:
|
143
|
-
|
148
|
+
_OPT_SEMAPHORES["SLURM_MAX_JOB"] = None
|
144
149
|
else:
|
145
|
-
|
150
|
+
_OPT_SEMAPHORES["SLURM_MAX_JOB"] = asyncio.BoundedSemaphore(num)
|
146
151
|
|
147
152
|
|
148
153
|
set_slurm_max_jobs(num=None)
|
149
154
|
|
150
155
|
|
151
156
|
# Trajectory function value config
|
152
|
-
def
|
157
|
+
def set_trajectory_cache_type(cache_type: str,
|
158
|
+
copy_content: bool = True,
|
159
|
+
clear_old_cache: bool = False
|
160
|
+
) -> None:
|
153
161
|
"""
|
154
|
-
Set the
|
162
|
+
Set the cache type for TrajectoryFunctionWrapper values.
|
155
163
|
|
156
|
-
|
157
|
-
|
164
|
+
By default the content of the current caches is copied to the new caches.
|
165
|
+
To clear the old/previously set caches (after copying their values), pass
|
166
|
+
``clear_old_cache=True``.
|
158
167
|
|
159
168
|
Parameters
|
160
169
|
----------
|
161
170
|
cache_type : str
|
162
171
|
One of "h5py", "npz", "memory".
|
172
|
+
copy_content : bool, optional
|
173
|
+
Whether to copy the current cache content to the new cache,
|
174
|
+
by default True
|
175
|
+
clear_old_cache : bool, optional
|
176
|
+
Whether to clear the old/previously set cache, by default False.
|
163
177
|
|
164
178
|
Raises
|
165
179
|
------
|
166
180
|
ValueError
|
167
181
|
Raised if ``cache_type`` is not one of the allowed values.
|
168
182
|
"""
|
183
|
+
# pylint: disable-next=global-variable-not-assigned
|
169
184
|
global _GLOBALS
|
170
185
|
allowed_values = ["h5py", "npz", "memory"]
|
171
|
-
cache_type
|
172
|
-
if cache_type not in allowed_values:
|
186
|
+
if (cache_type := cache_type.lower()) not in allowed_values:
|
173
187
|
raise ValueError(f"Given cache type must be one of {allowed_values}."
|
174
188
|
+ f" Was: {cache_type}.")
|
175
|
-
_GLOBALS
|
189
|
+
if _GLOBALS.get("TRAJECTORY_FUNCTION_CACHE_TYPE", "not_set") != cache_type:
|
190
|
+
# only do something if the new cache type differs from what we have
|
191
|
+
_GLOBALS["TRAJECTORY_FUNCTION_CACHE_TYPE"] = cache_type
|
192
|
+
_update_cache_type_for_all_trajectories(copy_content=copy_content,
|
193
|
+
clear_old_cache=clear_old_cache,
|
194
|
+
)
|
195
|
+
|
196
|
+
|
197
|
+
set_trajectory_cache_type("npz")
|
176
198
|
|
177
199
|
|
178
|
-
def register_h5py_cache(h5py_group
|
200
|
+
def register_h5py_cache(h5py_group) -> None:
|
179
201
|
"""
|
180
202
|
Register a h5py file or group for CV value caching.
|
181
203
|
|
182
204
|
Note that this also sets the default cache type to "h5py", i.e. it calls
|
183
|
-
:func:`
|
205
|
+
:func:`set_trajectory_cache_type` with ``cache_type="h5py"``.
|
184
206
|
|
185
207
|
Note that a ``h5py.File`` is just a slightly special ``h5py.Group``, so you
|
186
|
-
can pass either. :mod:`asyncmd` will use
|
208
|
+
can pass either. :mod:`asyncmd` will use either the file or the group as
|
187
209
|
the root of its own stored values.
|
188
210
|
E.g. you will have ``h5py_group["asyncmd/TrajectoryFunctionValueCache"]``
|
189
211
|
always pointing to the cached trajectory values and if ``h5py_group`` is
|
190
|
-
the top-level group (i.e. the file) you also have
|
212
|
+
the top-level group (i.e. the file) you also have
|
213
|
+
``(file["/asyncmd/TrajectoryFunctionValueCache"] ==\
|
214
|
+
h5py_group["asyncmd/TrajectoryFunctionValueCache"])``.
|
191
215
|
|
192
216
|
Parameters
|
193
217
|
----------
|
194
218
|
h5py_group : h5py.Group or h5py.File
|
195
219
|
The file or group to use for caching.
|
196
|
-
make_default: bool,
|
197
|
-
Whether we should also make "h5py" the default trajectory function
|
198
|
-
cache type. By default False.
|
199
220
|
"""
|
221
|
+
# pylint: disable-next=global-variable-not-assigned
|
200
222
|
global _GLOBALS
|
201
|
-
if make_default:
|
202
|
-
set_default_trajectory_cache_type(cache_type="h5py")
|
203
223
|
_GLOBALS["H5PY_CACHE"] = h5py_group
|
224
|
+
set_trajectory_cache_type(cache_type="h5py")
|
225
|
+
|
226
|
+
|
227
|
+
def show_config() -> None:
|
228
|
+
"""
|
229
|
+
Print/show current configuration.
|
230
|
+
"""
|
231
|
+
print(f"Values controlling caching: {_GLOBALS}")
|
232
|
+
# pylint: disable-next=protected-access
|
233
|
+
sem_print = {key: sem._value
|
234
|
+
for key, sem in {**_SEMAPHORES, **_OPT_SEMAPHORES}.items()
|
235
|
+
if sem is not None}
|
236
|
+
print(f"Semaphores controlling resource usage: {sem_print}")
|
@@ -12,5 +12,8 @@
|
|
12
12
|
#
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
This module exports all user-facing classes and functions for running gromacs.
|
17
|
+
"""
|
15
18
|
from .mdconfig import MDP
|
16
19
|
from .mdengine import GmxEngine, SlurmGmxEngine
|
@@ -12,6 +12,9 @@
|
|
12
12
|
#
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
This file contains the MDP class to parse, modify, and write gromacs mdp files.
|
17
|
+
"""
|
15
18
|
import shlex
|
16
19
|
import logging
|
17
20
|
from ..mdconfig import LineBasedMDConfig
|
@@ -27,18 +30,6 @@ class MDP(LineBasedMDConfig):
|
|
27
30
|
option, list of values pairs. Includes automatic types for known options
|
28
31
|
and keeps track if any options have been changed compared to the original
|
29
32
|
file.
|
30
|
-
|
31
|
-
Parameters
|
32
|
-
----------
|
33
|
-
original_file : str
|
34
|
-
absolute or relative path to original config file to parse
|
35
|
-
|
36
|
-
Methods
|
37
|
-
-------
|
38
|
-
write(outfile)
|
39
|
-
write the current (modified) configuration state to a given file
|
40
|
-
parse()
|
41
|
-
read the current original_file and update own state with it
|
42
33
|
"""
|
43
34
|
|
44
35
|
_KEY_VALUE_SEPARATOR = " = "
|
@@ -298,11 +289,11 @@ class MDP(LineBasedMDConfig):
|
|
298
289
|
# before or after the equal sign
|
299
290
|
# 4. no splits at '=' and no splits at ';' -> weired line, probably
|
300
291
|
# not a valid line(?)
|
301
|
-
if splits_at_comment[0]
|
292
|
+
if not splits_at_comment[0]:
|
302
293
|
# option 2 (and 3 if the comment is before the equal sign)
|
303
294
|
# comment sign is the first letter, so the whole line is
|
304
295
|
# (most probably) a comment line
|
305
|
-
logger.debug(
|
296
|
+
logger.debug("mdp line parsed as comment: %s", line)
|
306
297
|
return {}
|
307
298
|
if ((len(splits_at_equal) == 2 and len(splits_at_comment) == 1) # option 1
|
308
299
|
# or option 3 with equal sign before comment sign
|
@@ -318,10 +309,9 @@ class MDP(LineBasedMDConfig):
|
|
318
309
|
parser.commenters = ";"
|
319
310
|
# puncutation_chars=True adds "~-./*?=" to wordchars
|
320
311
|
# such that we do not split floats and file paths and similar
|
321
|
-
tokens = list(parser)
|
322
312
|
# gromacs mdp can have 0-N tokens/values to the RHS of the '='
|
323
|
-
if
|
324
|
-
# line with empty options, e.g. 'define = '
|
313
|
+
if not (tokens := list(parser)):
|
314
|
+
# zero tokens -> line with empty options, e.g. 'define = '
|
325
315
|
return {self._key_char_replace(key): []}
|
326
316
|
# lines with content, we always return a list (and let our
|
327
317
|
# type_dispatch sort out the singleton options and the typing)
|