mdinterface 1.2.0__tar.gz → 1.3.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.
- {mdinterface-1.2.0 → mdinterface-1.3.0}/PKG-INFO +43 -18
- mdinterface-1.2.0/mdinterface.egg-info/PKG-INFO → mdinterface-1.3.0/README.md +28 -35
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/__init__.py +2 -2
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/core/specie.py +11 -4
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/database/ions.py +28 -4
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/database/molecules.py +10 -3
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/externals/pyscf.py +5 -2
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/io/lammpswriter.py +30 -20
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/simulationbox.py +16 -7
- mdinterface-1.3.0/mdinterface/utils/draw.py +40 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/utils/graphs.py +64 -40
- mdinterface-1.3.0/mdinterface/utils/poisson.py +239 -0
- mdinterface-1.3.0/mdinterface/utils/units.py +42 -0
- mdinterface-1.2.0/README.md → mdinterface-1.3.0/mdinterface.egg-info/PKG-INFO +60 -13
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface.egg-info/SOURCES.txt +4 -1
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface.egg-info/requires.txt +11 -1
- {mdinterface-1.2.0 → mdinterface-1.3.0}/requirements.txt +0 -1
- {mdinterface-1.2.0 → mdinterface-1.3.0}/setup.cfg +14 -4
- {mdinterface-1.2.0 → mdinterface-1.3.0}/LICENSE +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/MANIFEST.in +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/build/__init__.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/build/box.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/build/charges.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/build/continuum2sim.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/build/polymerize.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/config.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/core/__init__.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/core/polymer.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/core/topology.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/database/__init__.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/database/graphene.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/database/metals.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/externals/__init__.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/externals/ligpargen.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/externals/obabel.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/io/__init__.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/io/packmol.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/io/read.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/utils/__init__.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/utils/auxiliary.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface/utils/map.py +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface.egg-info/dependency_links.txt +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/mdinterface.egg-info/top_level.txt +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/pyproject.toml +0 -0
- {mdinterface-1.2.0 → mdinterface-1.3.0}/setup.py +0 -0
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: mdinterface
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Build Interface Systems for Molecular Dynamics Simulations
|
|
5
5
|
Home-page: https://gitlab.com/roncofaber/mdinterface
|
|
6
|
+
Author: Fabrice Roncoroni
|
|
7
|
+
Author-email: fabrice.roncoroni@gmail.com
|
|
6
8
|
License: Apache-2.0
|
|
7
9
|
Classifier: Programming Language :: Python :: 3
|
|
8
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
11
|
Classifier: Operating System :: OS Independent
|
|
10
12
|
Classifier: Topic :: Software Development
|
|
11
13
|
Classifier: Topic :: Scientific/Engineering
|
|
12
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.8
|
|
13
15
|
Description-Content-Type: text/markdown
|
|
14
16
|
License-File: LICENSE
|
|
15
17
|
Requires-Dist: mdanalysis
|
|
16
18
|
Requires-Dist: ase
|
|
17
19
|
Requires-Dist: numpy
|
|
18
20
|
Requires-Dist: networkx
|
|
19
|
-
Requires-Dist: libarvo
|
|
20
21
|
Requires-Dist: platformdirs
|
|
21
22
|
Requires-Dist: configparser
|
|
23
|
+
Provides-Extra: volume
|
|
24
|
+
Requires-Dist: libarvo; extra == "volume"
|
|
25
|
+
Provides-Extra: resp
|
|
26
|
+
Requires-Dist: pyscf; extra == "resp"
|
|
27
|
+
Requires-Dist: pymbxas; extra == "resp"
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: libarvo; extra == "all"
|
|
30
|
+
Requires-Dist: pyscf; extra == "all"
|
|
31
|
+
Dynamic: license-file
|
|
22
32
|
|
|
23
33
|
<div style="display: flex; align-items: center;">
|
|
24
34
|
<img src="./assets/mdinterface.png" alt="Logo" width="80" style="margin-right: 10px;">
|
|
@@ -37,16 +47,17 @@ Using `mdinterface` you can:
|
|
|
37
47
|
|
|
38
48
|
- Create layered simulation boxes with solvents, solutes, and interface slabs.
|
|
39
49
|
- Populate your system with ions and solvents using PACKMOL, and provide the starting concentration profile of the species to get the MD where you want it to be, but faster.
|
|
40
|
-
- Import common molecules/metals/polymers with pre-defined classical force fields parameters from the database, or automatically generate new OPLS-AA force field parameters using [https://github.com/Isra3l/ligpargen
|
|
50
|
+
- Import common molecules/metals/polymers with pre-defined classical force fields parameters from the database, or automatically generate new OPLS-AA force field parameters using [LigParGen](https://github.com/Isra3l/ligpargen).
|
|
41
51
|
- Generate polymer chains of any length from a starting monomer.
|
|
42
52
|
- Estimate the RESP charges of molecules using the [PySCF electronic structure code](https://github.com/pyscf/pyscf).
|
|
43
|
-
- Automatically write LAMMPS data files and coefficients, so you can start making them atoms dance!
|
|
53
|
+
- Automatically write LAMMPS data files and coefficients, so you can start making them atoms dance as soon as possible!
|
|
54
|
+
- Integrate your workflow with [MDAnalysis](https://github.com/MDAnalysis/mdanalysis): create your molecules with `mdinterface` and convert them to `mda.Universe` objects with a simple interface.
|
|
44
55
|
|
|
45
56
|
## Requirements
|
|
46
57
|
|
|
47
58
|
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all mandatory dependencies.
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
Additionally, you need the `packmol` utility installed to generate MD boxes. You can follow the instructions at [https://m3g.github.io/packmol/](https://m3g.github.io/packmol/) to install it. Alternatively, you can install it using `conda`:
|
|
50
61
|
|
|
51
62
|
```bash
|
|
52
63
|
conda install -c conda-forge packmol
|
|
@@ -56,7 +67,7 @@ conda install -c conda-forge packmol
|
|
|
56
67
|
|
|
57
68
|
#### Automatic OPLS-AA force field generator with LigParGen
|
|
58
69
|
|
|
59
|
-
If you want to use LigParGen, please follow the instructions on their [GitHub](https://github.com/Isra3l/ligpargen). To make sure that BOSS is recognized by the package, specify the environment variable `BOSSdir`.
|
|
70
|
+
If you want to use LigParGen, please follow the instructions on their [GitHub](https://github.com/Isra3l/ligpargen). If you are having trouble installing, you can try my own [fork](https://github.com/roncofaber/ligpargen) of the original repo. To make sure that BOSS is recognized by the package, specify the environment variable `BOSSdir`.
|
|
60
71
|
|
|
61
72
|
You can do this manually every time, or just add the path to the `config.ini` file to your config directory:
|
|
62
73
|
|
|
@@ -70,7 +81,7 @@ The config directory is found using [`platformdirs`](https://pypi.org/project/pl
|
|
|
70
81
|
|
|
71
82
|
### RESP charge analysis with PySCF
|
|
72
83
|
|
|
73
|
-
To use the RESP charge analysis feature, you need to install [PySCF](https://github.com/pyscf/pyscf) and [PyMBXAS](https://gitlab.com/roncofaber/pymbxas).
|
|
84
|
+
To use the RESP charge analysis feature, you need to install [PySCF](https://github.com/pyscf/pyscf) and [PyMBXAS](https://gitlab.com/roncofaber/pymbxas). To my knowledge, the RESP feature is only implemented in [gpu4pyscf](https://github.com/pyscf/gpu4pyscf) at the moment, so follow the repo instructions on how to install it.
|
|
74
85
|
|
|
75
86
|
## Installation
|
|
76
87
|
|
|
@@ -92,25 +103,32 @@ Clone the repository in the desired location:
|
|
|
92
103
|
git clone git@gitlab.com:roncofaber/mdinterface.git
|
|
93
104
|
```
|
|
94
105
|
|
|
95
|
-
Install the
|
|
106
|
+
Install the package:
|
|
96
107
|
|
|
97
108
|
```bash
|
|
98
109
|
cd mdinterface
|
|
99
|
-
|
|
110
|
+
pip install .
|
|
100
111
|
```
|
|
101
112
|
|
|
102
|
-
Install
|
|
113
|
+
### Install a development environment
|
|
114
|
+
|
|
115
|
+
If you plan of making changes, clone the package and add it to your development environment with:
|
|
103
116
|
|
|
104
117
|
```bash
|
|
105
|
-
pip install .
|
|
118
|
+
pip install --no-build-isolation -e .
|
|
106
119
|
```
|
|
107
120
|
|
|
108
|
-
### Install
|
|
121
|
+
### Install optional packages
|
|
109
122
|
|
|
110
|
-
|
|
123
|
+
You can install optional dependencies with the following commands:
|
|
111
124
|
|
|
112
125
|
```bash
|
|
113
|
-
|
|
126
|
+
# install libarvo to estimate species:
|
|
127
|
+
pip install mdinterface[volume] volumes
|
|
128
|
+
# install pyscf and pymbxas (you still need gpu4pyscf):
|
|
129
|
+
pip install mdinterface[resp]
|
|
130
|
+
# install all of the above options:
|
|
131
|
+
pip install mdinterface[all]
|
|
114
132
|
```
|
|
115
133
|
|
|
116
134
|
## Usage
|
|
@@ -120,9 +138,16 @@ Creating a new Specie (with its topology attributes) is as simple as doing:
|
|
|
120
138
|
```python
|
|
121
139
|
#%% Make a specie, and use LigParGen to estimate FF parameters
|
|
122
140
|
my_specie = Specie("CH3ONO", ligpargen=True) # molecule is in ASE database
|
|
141
|
+
|
|
142
|
+
# make a specie from any ASE readable file
|
|
143
|
+
my_specie = Specie("methylnitrite.xyz", ligpargen=True)
|
|
144
|
+
|
|
145
|
+
# convert specie to mdanalysis universe (and all the attributes!)
|
|
146
|
+
my_specie.to_universe()
|
|
147
|
+
|
|
123
148
|
```
|
|
124
149
|
|
|
125
|
-
Please, check the files in [examples](mdinterface/examples/) to learn how to use the package. Including setting up simulation boxes, creating polymers and much more
|
|
150
|
+
Please, check the files in [examples](mdinterface/examples/) to learn how to use the package. Including setting up simulation boxes, creating polymers, perform RESP analysis and much more!
|
|
126
151
|
|
|
127
152
|
## Roadmap
|
|
128
153
|
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.2
|
|
2
|
-
Name: mdinterface
|
|
3
|
-
Version: 1.2.0
|
|
4
|
-
Summary: mdinterface: A package for building interface systems in Molecular Dynamics simulations.
|
|
5
|
-
Home-page: https://gitlab.com/roncofaber/mdinterface
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Classifier: Topic :: Software Development
|
|
11
|
-
Classifier: Topic :: Scientific/Engineering
|
|
12
|
-
Requires-Python: >=3.6
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: mdanalysis
|
|
16
|
-
Requires-Dist: ase
|
|
17
|
-
Requires-Dist: numpy
|
|
18
|
-
Requires-Dist: networkx
|
|
19
|
-
Requires-Dist: libarvo
|
|
20
|
-
Requires-Dist: platformdirs
|
|
21
|
-
Requires-Dist: configparser
|
|
22
|
-
|
|
23
1
|
<div style="display: flex; align-items: center;">
|
|
24
2
|
<img src="./assets/mdinterface.png" alt="Logo" width="80" style="margin-right: 10px;">
|
|
25
3
|
<div style="display: flex; flex-direction: column;">
|
|
@@ -37,16 +15,17 @@ Using `mdinterface` you can:
|
|
|
37
15
|
|
|
38
16
|
- Create layered simulation boxes with solvents, solutes, and interface slabs.
|
|
39
17
|
- Populate your system with ions and solvents using PACKMOL, and provide the starting concentration profile of the species to get the MD where you want it to be, but faster.
|
|
40
|
-
- Import common molecules/metals/polymers with pre-defined classical force fields parameters from the database, or automatically generate new OPLS-AA force field parameters using [https://github.com/Isra3l/ligpargen
|
|
18
|
+
- Import common molecules/metals/polymers with pre-defined classical force fields parameters from the database, or automatically generate new OPLS-AA force field parameters using [LigParGen](https://github.com/Isra3l/ligpargen).
|
|
41
19
|
- Generate polymer chains of any length from a starting monomer.
|
|
42
20
|
- Estimate the RESP charges of molecules using the [PySCF electronic structure code](https://github.com/pyscf/pyscf).
|
|
43
|
-
- Automatically write LAMMPS data files and coefficients, so you can start making them atoms dance!
|
|
21
|
+
- Automatically write LAMMPS data files and coefficients, so you can start making them atoms dance as soon as possible!
|
|
22
|
+
- Integrate your workflow with [MDAnalysis](https://github.com/MDAnalysis/mdanalysis): create your molecules with `mdinterface` and convert them to `mda.Universe` objects with a simple interface.
|
|
44
23
|
|
|
45
24
|
## Requirements
|
|
46
25
|
|
|
47
26
|
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all mandatory dependencies.
|
|
48
27
|
|
|
49
|
-
|
|
28
|
+
Additionally, you need the `packmol` utility installed to generate MD boxes. You can follow the instructions at [https://m3g.github.io/packmol/](https://m3g.github.io/packmol/) to install it. Alternatively, you can install it using `conda`:
|
|
50
29
|
|
|
51
30
|
```bash
|
|
52
31
|
conda install -c conda-forge packmol
|
|
@@ -56,7 +35,7 @@ conda install -c conda-forge packmol
|
|
|
56
35
|
|
|
57
36
|
#### Automatic OPLS-AA force field generator with LigParGen
|
|
58
37
|
|
|
59
|
-
If you want to use LigParGen, please follow the instructions on their [GitHub](https://github.com/Isra3l/ligpargen). To make sure that BOSS is recognized by the package, specify the environment variable `BOSSdir`.
|
|
38
|
+
If you want to use LigParGen, please follow the instructions on their [GitHub](https://github.com/Isra3l/ligpargen). If you are having trouble installing, you can try my own [fork](https://github.com/roncofaber/ligpargen) of the original repo. To make sure that BOSS is recognized by the package, specify the environment variable `BOSSdir`.
|
|
60
39
|
|
|
61
40
|
You can do this manually every time, or just add the path to the `config.ini` file to your config directory:
|
|
62
41
|
|
|
@@ -70,7 +49,7 @@ The config directory is found using [`platformdirs`](https://pypi.org/project/pl
|
|
|
70
49
|
|
|
71
50
|
### RESP charge analysis with PySCF
|
|
72
51
|
|
|
73
|
-
To use the RESP charge analysis feature, you need to install [PySCF](https://github.com/pyscf/pyscf) and [PyMBXAS](https://gitlab.com/roncofaber/pymbxas).
|
|
52
|
+
To use the RESP charge analysis feature, you need to install [PySCF](https://github.com/pyscf/pyscf) and [PyMBXAS](https://gitlab.com/roncofaber/pymbxas). To my knowledge, the RESP feature is only implemented in [gpu4pyscf](https://github.com/pyscf/gpu4pyscf) at the moment, so follow the repo instructions on how to install it.
|
|
74
53
|
|
|
75
54
|
## Installation
|
|
76
55
|
|
|
@@ -92,25 +71,32 @@ Clone the repository in the desired location:
|
|
|
92
71
|
git clone git@gitlab.com:roncofaber/mdinterface.git
|
|
93
72
|
```
|
|
94
73
|
|
|
95
|
-
Install the
|
|
74
|
+
Install the package:
|
|
96
75
|
|
|
97
76
|
```bash
|
|
98
77
|
cd mdinterface
|
|
99
|
-
|
|
78
|
+
pip install .
|
|
100
79
|
```
|
|
101
80
|
|
|
102
|
-
Install
|
|
81
|
+
### Install a development environment
|
|
82
|
+
|
|
83
|
+
If you plan of making changes, clone the package and add it to your development environment with:
|
|
103
84
|
|
|
104
85
|
```bash
|
|
105
|
-
pip install .
|
|
86
|
+
pip install --no-build-isolation -e .
|
|
106
87
|
```
|
|
107
88
|
|
|
108
|
-
### Install
|
|
89
|
+
### Install optional packages
|
|
109
90
|
|
|
110
|
-
|
|
91
|
+
You can install optional dependencies with the following commands:
|
|
111
92
|
|
|
112
93
|
```bash
|
|
113
|
-
|
|
94
|
+
# install libarvo to estimate species:
|
|
95
|
+
pip install mdinterface[volume] volumes
|
|
96
|
+
# install pyscf and pymbxas (you still need gpu4pyscf):
|
|
97
|
+
pip install mdinterface[resp]
|
|
98
|
+
# install all of the above options:
|
|
99
|
+
pip install mdinterface[all]
|
|
114
100
|
```
|
|
115
101
|
|
|
116
102
|
## Usage
|
|
@@ -120,9 +106,16 @@ Creating a new Specie (with its topology attributes) is as simple as doing:
|
|
|
120
106
|
```python
|
|
121
107
|
#%% Make a specie, and use LigParGen to estimate FF parameters
|
|
122
108
|
my_specie = Specie("CH3ONO", ligpargen=True) # molecule is in ASE database
|
|
109
|
+
|
|
110
|
+
# make a specie from any ASE readable file
|
|
111
|
+
my_specie = Specie("methylnitrite.xyz", ligpargen=True)
|
|
112
|
+
|
|
113
|
+
# convert specie to mdanalysis universe (and all the attributes!)
|
|
114
|
+
my_specie.to_universe()
|
|
115
|
+
|
|
123
116
|
```
|
|
124
117
|
|
|
125
|
-
Please, check the files in [examples](mdinterface/examples/) to learn how to use the package. Including setting up simulation boxes, creating polymers and much more
|
|
118
|
+
Please, check the files in [examples](mdinterface/examples/) to learn how to use the package. Including setting up simulation boxes, creating polymers, perform RESP analysis and much more!
|
|
126
119
|
|
|
127
120
|
## Roadmap
|
|
128
121
|
|
|
@@ -6,8 +6,8 @@ Initially developed to construct electrolyte/electrode interfaces, it is also we
|
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = '1.
|
|
10
|
-
__date__ = '
|
|
9
|
+
__version__ = '1.3.0'
|
|
10
|
+
__date__ = '17 Jul. 2025'
|
|
11
11
|
__author__ = 'Fabrice Roncoroni'
|
|
12
12
|
__all__ = ['SimulationBox', "Specie", "Polymer"]
|
|
13
13
|
|
|
@@ -20,6 +20,7 @@ from mdinterface.utils.auxiliary import as_list, find_smallest_missing, round_li
|
|
|
20
20
|
from mdinterface.externals import run_ligpargen, run_OBChargeModel, calculate_RESP_charges
|
|
21
21
|
from mdinterface.utils.graphs import molecule_to_graph, find_unique_paths_of_length,\
|
|
22
22
|
find_improper_idxs,find_relevant_distances, find_atom_types
|
|
23
|
+
from mdinterface.utils.draw import draw_bond_markers
|
|
23
24
|
|
|
24
25
|
import copy
|
|
25
26
|
import numpy as np
|
|
@@ -176,7 +177,7 @@ class Specie(object):
|
|
|
176
177
|
def _atom_types_from_lj(self, lj):
|
|
177
178
|
|
|
178
179
|
# use function to retrieve IDs
|
|
179
|
-
atom_type_ids, types_map = find_atom_types(self.
|
|
180
|
+
atom_type_ids, types_map = find_atom_types(self.graph, max_depth=1)
|
|
180
181
|
|
|
181
182
|
atom_types = []
|
|
182
183
|
for atom_id in atom_type_ids:
|
|
@@ -220,7 +221,7 @@ class Specie(object):
|
|
|
220
221
|
|
|
221
222
|
self._atoms = atoms
|
|
222
223
|
self._graph = molecule_to_graph(atoms, cutoff_scale=cutoff)
|
|
223
|
-
|
|
224
|
+
|
|
224
225
|
return
|
|
225
226
|
|
|
226
227
|
def _update_topology(self):
|
|
@@ -507,15 +508,21 @@ class Specie(object):
|
|
|
507
508
|
centers=centers, Ninv=Ninv)
|
|
508
509
|
return unique_pairs_list
|
|
509
510
|
|
|
510
|
-
def plot_graph(self,
|
|
511
|
+
def plot_graph(self, show_bonds=False):
|
|
511
512
|
|
|
512
513
|
colors = [jmol_colors[a.number] for a in self.atoms]
|
|
513
514
|
|
|
515
|
+
# node_pos = nx.spring_layout(self.graph, k=1.5/np.sqrt(self.graph.order()))
|
|
516
|
+
node_pos = nx.kamada_kawai_layout(self.graph)
|
|
517
|
+
|
|
514
518
|
fig, ax = plt.subplots()
|
|
515
|
-
nx.draw(self.graph, with_labels=True, node_color=colors,
|
|
519
|
+
nx.draw(self.graph, pos=node_pos, with_labels=True, node_color=colors,
|
|
516
520
|
node_size=1000, edge_color='black', linewidths=2, font_size=15,
|
|
517
521
|
edgecolors="black", ax=ax, width=2)
|
|
518
522
|
|
|
523
|
+
if show_bonds:
|
|
524
|
+
draw_bond_markers(ax, self, node_pos, jmol_colors)
|
|
525
|
+
|
|
519
526
|
plt.show()
|
|
520
527
|
|
|
521
528
|
return
|
|
@@ -20,7 +20,8 @@ import ase
|
|
|
20
20
|
# Jorgensen: https://pubs.acs.org/doi/10.1021/ct600252r
|
|
21
21
|
# Cheatham : https://pubs.acs.org/doi/10.1021/jp8001614
|
|
22
22
|
# Sengupta : https://pubs.acs.org/doi/10.1021/acs.jcim.0c01390 (12-6 HFE param)
|
|
23
|
-
# Dang :
|
|
23
|
+
# Dang : https://doi.org/10.1063/1.462555, https://doi.org/10.1063/1.466363
|
|
24
|
+
|
|
24
25
|
ions_parameters = {
|
|
25
26
|
"F": {
|
|
26
27
|
"charge": -1.0,
|
|
@@ -28,7 +29,7 @@ ions_parameters = {
|
|
|
28
29
|
"jorgensen": [0.71000, 3.0500],
|
|
29
30
|
"cheatham" : [0.00336, 4.1035],
|
|
30
31
|
"sengupta" : [0.24140, 3.2678],
|
|
31
|
-
"dang" : [0.18000, 3.1180]
|
|
32
|
+
"dang" : [0.20000, 3.1680] # was [0.18000, 3.1180]
|
|
32
33
|
}
|
|
33
34
|
},
|
|
34
35
|
"Cl": {
|
|
@@ -107,6 +108,8 @@ ions_parameters = {
|
|
|
107
108
|
|
|
108
109
|
def lookup_parameters(element, ffield):
|
|
109
110
|
try:
|
|
111
|
+
if ffield.lower() == "merz":
|
|
112
|
+
ffield = "sengupta"
|
|
110
113
|
charge = ions_parameters[element]["charge"]
|
|
111
114
|
lj = ions_parameters[element]["ffield"][ffield.lower()]
|
|
112
115
|
return charge, lj
|
|
@@ -125,7 +128,7 @@ class Ion(Specie):
|
|
|
125
128
|
**kwargs: Additional keyword arguments to pass to the Specie superclass.
|
|
126
129
|
"""
|
|
127
130
|
|
|
128
|
-
def __init__(self, element, ffield="Jorgensen", chg_scaling=0
|
|
131
|
+
def __init__(self, element, ffield="Jorgensen", chg_scaling=1.0, **kwargs):
|
|
129
132
|
|
|
130
133
|
charge, lj = lookup_parameters(element, ffield)
|
|
131
134
|
lj = {element: lj}
|
|
@@ -166,7 +169,9 @@ class Perchlorate(Specie):
|
|
|
166
169
|
return
|
|
167
170
|
|
|
168
171
|
|
|
169
|
-
# hydronium
|
|
172
|
+
# hydronium parameters:
|
|
173
|
+
# std: https://pubs.acs.org/doi/pdf/10.1021/jp036842c
|
|
174
|
+
# netz: https://refubium.fu-berlin.de/bitstream/handle/fub188/15473/1.4942771.pdf
|
|
170
175
|
class Hydronium(Specie):
|
|
171
176
|
def __init__(self, **kwargs):
|
|
172
177
|
|
|
@@ -186,3 +191,22 @@ class Hydronium(Specie):
|
|
|
186
191
|
|
|
187
192
|
super().__init__(hyd, charges=charges, bonds=b1, angles=a1, lj=lj, **kwargs)
|
|
188
193
|
return
|
|
194
|
+
|
|
195
|
+
# hydroxide parameters:
|
|
196
|
+
# netz: https://refubium.fu-berlin.de/bitstream/handle/fub188/15473/1.4942771.pdf
|
|
197
|
+
class Hydroxide(Specie):
|
|
198
|
+
def __init__(self, **kwargs):
|
|
199
|
+
|
|
200
|
+
# make ion by cheating and making NH3 first
|
|
201
|
+
hoh = ase.build.molecule("OH")
|
|
202
|
+
|
|
203
|
+
# bond is fixed in paper #FIXME used hydronium bond with r0=1
|
|
204
|
+
b1 = Bond("O", "H", kr=1085.9565/2, r0=1.000)
|
|
205
|
+
|
|
206
|
+
charges = [-1.000, 0.000]
|
|
207
|
+
|
|
208
|
+
# converted J to cal
|
|
209
|
+
lj = {"O": [0.01195, 3.8100], "H": [0.000, 0.000]}
|
|
210
|
+
|
|
211
|
+
super().__init__(hoh, charges=charges, bonds=b1, lj=lj, **kwargs)
|
|
212
|
+
return
|
|
@@ -19,15 +19,22 @@ class Water(Specie):
|
|
|
19
19
|
a1 = Angle("H", "O", "H", kr=55, theta0=104.52)
|
|
20
20
|
charges = [-0.83, 0.415, 0.415]
|
|
21
21
|
lj = {"O": [0.102, 3.188], "H": [0.0, 1.0]}
|
|
22
|
+
|
|
23
|
+
if model.lower() == "charmm":
|
|
24
|
+
b1 = Bond("O", "H", kr=450, r0=0.9572)
|
|
25
|
+
a1 = Angle("H", "O", "H", kr=55, theta0=104.52)
|
|
26
|
+
charges = [-0.834, 0.417, 0.417]
|
|
27
|
+
lj = {"O": [0.1521, 3.1507], "H": [0.0460, 0.4]}
|
|
22
28
|
|
|
23
29
|
super().__init__("H2O", charges=charges, bonds=b1, angles=a1, lj=lj, **kwargs)
|
|
24
30
|
return
|
|
25
31
|
|
|
32
|
+
# 22 Apr. 2025 correction: all bond terms have been divider by 2:
|
|
26
33
|
#oxygen https://pubs.acs.org/doi/10.1021/acs.jctc.0c01132 /!\: divide sig by 2**(1/6)
|
|
27
34
|
class Oxygen(Specie):
|
|
28
35
|
def __init__(self, **kwargs):
|
|
29
36
|
|
|
30
|
-
b1 = Bond("O", "O", kr=1640.4, r0=1.2074)
|
|
37
|
+
b1 = Bond("O", "O", kr=1640.4/2, r0=1.2074)
|
|
31
38
|
lj = {"O" : [0.1047, 2.9373]}
|
|
32
39
|
|
|
33
40
|
super().__init__("O2", charges = 0.0, lj=lj, bonds=b1, **kwargs)
|
|
@@ -37,7 +44,7 @@ class Oxygen(Specie):
|
|
|
37
44
|
class Hydrogen(Specie):
|
|
38
45
|
def __init__(self, Hset="std", **kwargs):
|
|
39
46
|
|
|
40
|
-
b1 = Bond("H", "H", kr=700, r0=0.7414)
|
|
47
|
+
b1 = Bond("H", "H", kr=700/2, r0=0.7414)
|
|
41
48
|
|
|
42
49
|
if Hset.lower() == "std": # standard 12-6 set
|
|
43
50
|
lj = {"H" : [0.0153, 2.5996]}
|
|
@@ -51,7 +58,7 @@ class Hydrogen(Specie):
|
|
|
51
58
|
class Nitrogen(Specie):
|
|
52
59
|
def __init__(self, **kwargs):
|
|
53
60
|
|
|
54
|
-
b1 = Bond("N", "N", kr=3190, r0=1.0977)
|
|
61
|
+
b1 = Bond("N", "N", kr=3190/2, r0=1.0977)
|
|
55
62
|
lj = {"N" : [0.0797, 3.2197]}
|
|
56
63
|
|
|
57
64
|
super().__init__("N2", charges = 0.0, lj=lj, bonds=b1, **kwargs)
|
|
@@ -40,15 +40,18 @@ def calculate_RESP_charges(specie, basis='def2-svpd', xc="b3lyp", calc_type="RKS
|
|
|
40
40
|
|
|
41
41
|
atoms = mole_to_ase(mol)
|
|
42
42
|
|
|
43
|
+
mol.cart = True # PySCF uses spherical basis by default
|
|
44
|
+
|
|
43
45
|
# run calculation and calculate density matrix
|
|
44
46
|
mf = make_pyscf_calculator(mol, xc=xc, calc_type=calc_type, gpu=gpu)
|
|
47
|
+
|
|
45
48
|
mf.kernel()
|
|
46
49
|
dm = mf.make_rdm1()
|
|
47
50
|
|
|
48
51
|
# now, start RESP with ESP first!
|
|
49
52
|
|
|
50
53
|
# ESP charge (do I need this?)
|
|
51
|
-
q0 = esp.esp_solve(mol, dm)
|
|
54
|
+
# q0 = esp.esp_solve(mol, dm)
|
|
52
55
|
|
|
53
56
|
# RESP charge // first stage fitting
|
|
54
57
|
q1 = esp.resp_solve(mol, dm, maxit=maxit)
|
|
@@ -67,7 +70,7 @@ def calculate_RESP_charges(specie, basis='def2-svpd', xc="b3lyp", calc_type="RKS
|
|
|
67
70
|
equal_constraints.append(tmp_idx.tolist())
|
|
68
71
|
|
|
69
72
|
# RESP charge // second stage fitting
|
|
70
|
-
q2 = esp.resp_solve(mol, dm, resp_a=
|
|
73
|
+
q2 = esp.resp_solve(mol, dm, resp_a=5e-4, resp_b=0.1, tol=1e-7,
|
|
71
74
|
sum_constraints=sum_constraints,
|
|
72
75
|
equal_constraints=equal_constraints, maxit=maxit)
|
|
73
76
|
|
|
@@ -68,17 +68,16 @@ class DATAWriter(base.WriterBase):
|
|
|
68
68
|
self.units['velocity'] = kwargs.pop('velocityunit',
|
|
69
69
|
self.units['length']+'/'+self.units['time'])
|
|
70
70
|
|
|
71
|
-
def _write_atoms(self, atoms, data):
|
|
71
|
+
def _write_atoms(self, atoms, data, atom_style):
|
|
72
72
|
self.f.write('\n')
|
|
73
73
|
self.f.write('Atoms\n')
|
|
74
74
|
self.f.write('\n')
|
|
75
75
|
|
|
76
76
|
try:
|
|
77
77
|
charges = atoms.charges
|
|
78
|
+
has_charges = True
|
|
78
79
|
except (NoDataError, AttributeError):
|
|
79
80
|
has_charges = False
|
|
80
|
-
else:
|
|
81
|
-
has_charges = True
|
|
82
81
|
|
|
83
82
|
indices = atoms.indices + 1
|
|
84
83
|
i_l = len(str(indices.max()))
|
|
@@ -96,18 +95,26 @@ class DATAWriter(base.WriterBase):
|
|
|
96
95
|
b_l = len(str(int(coordinates.max()))) + 1
|
|
97
96
|
b_t = b_l + 6
|
|
98
97
|
|
|
99
|
-
if
|
|
100
|
-
for index,
|
|
101
|
-
types, charges, coordinates):
|
|
98
|
+
if atom_style == "atomic":
|
|
99
|
+
for index, atype, coords in zip(indices, types, coordinates):
|
|
102
100
|
x, y, z = coords
|
|
103
|
-
self.f.write(f"{index:{i_l}d}
|
|
101
|
+
self.f.write(f"{index:{i_l}d} {atype:{t_l}d}"
|
|
104
102
|
f" {x:> {b_t}.5f} {y:> {b_t}.5f} {z:> {b_t}.5f}\n")
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
elif atom_style == "full":
|
|
104
|
+
|
|
105
|
+
if not has_charges:
|
|
106
|
+
for index, moltag, atype, coords in zip(indices, moltags, types,
|
|
107
|
+
coordinates):
|
|
108
|
+
x, y, z = coords
|
|
109
|
+
self.f.write(f"{index:{i_l}d} {moltag:{m_l}d} {atype:{t_l}d}"
|
|
110
|
+
f" {x:> {b_t}.5f} {y:> {b_t}.5f} {z:> {b_t}.5f}\n")
|
|
111
|
+
elif has_charges:
|
|
112
|
+
for index, moltag, atype, charge, coords in zip(indices, moltags,
|
|
113
|
+
types, charges, coordinates):
|
|
114
|
+
x, y, z = coords
|
|
115
|
+
self.f.write(f"{index:{i_l}d} {moltag:{m_l}d} {atype:{t_l}d}"
|
|
116
|
+
f" {charge: .7f}"
|
|
117
|
+
f" {x:> {b_t}.5f} {y:> {b_t}.5f} {z:> {b_t}.5f}\n")
|
|
111
118
|
|
|
112
119
|
def _write_velocities(self, atoms):
|
|
113
120
|
self.f.write('\n')
|
|
@@ -172,7 +179,7 @@ class DATAWriter(base.WriterBase):
|
|
|
172
179
|
self.f.write('\n')
|
|
173
180
|
|
|
174
181
|
@requires('types', 'masses')
|
|
175
|
-
def write(self, selection, frame=None):
|
|
182
|
+
def write(self, selection, frame=None, atom_style='full'):
|
|
176
183
|
"""Write selection at current trajectory frame to file.
|
|
177
184
|
|
|
178
185
|
The sections for Atoms, Masses, Velocities, Bonds, Angles,
|
|
@@ -233,11 +240,14 @@ class DATAWriter(base.WriterBase):
|
|
|
233
240
|
('dihedral', 'dihedrals'), ('improper', 'impropers')]
|
|
234
241
|
|
|
235
242
|
for btype, attr_name in attrs:
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
try:
|
|
244
|
+
features[btype] = atoms.__getattribute__(attr_name)
|
|
245
|
+
self.f.write('{:>12d} {}\n'.format(len(features[btype]),
|
|
246
|
+
attr_name))
|
|
247
|
+
features[btype] = features[btype].atomgroup_intersection(
|
|
248
|
+
atoms, strict=True)
|
|
249
|
+
except:
|
|
250
|
+
pass
|
|
241
251
|
|
|
242
252
|
self.f.write('\n')
|
|
243
253
|
|
|
@@ -251,7 +261,7 @@ class DATAWriter(base.WriterBase):
|
|
|
251
261
|
self._write_dimensions(atoms.dimensions)
|
|
252
262
|
|
|
253
263
|
self._write_masses(atoms)
|
|
254
|
-
self._write_atoms(atoms, u.trajectory.ts.data)
|
|
264
|
+
self._write_atoms(atoms, u.trajectory.ts.data, atom_style)
|
|
255
265
|
for attr in features.values():
|
|
256
266
|
if attr is None or len(attr) == 0:
|
|
257
267
|
continue
|
|
@@ -142,7 +142,7 @@ class SimulationBox():
|
|
|
142
142
|
def make_simulation_box(self, xysize, layering, padding=1.5, to_ase=False,
|
|
143
143
|
write_data=False, filename="data.lammps",
|
|
144
144
|
center_electrode=False, layered=False, hijack=None,
|
|
145
|
-
match_cell=False,
|
|
145
|
+
match_cell=False, atom_style="full", write_coeff=True):
|
|
146
146
|
|
|
147
147
|
# define approximate cross_section
|
|
148
148
|
assert len(xysize) == 2, "'xysize' should have length of 2 [xsize, ysize]"
|
|
@@ -210,7 +210,8 @@ class SimulationBox():
|
|
|
210
210
|
|
|
211
211
|
# write data file
|
|
212
212
|
if write_data:
|
|
213
|
-
self.write_lammps_file(system, filename=filename,
|
|
213
|
+
self.write_lammps_file(system, filename=filename,
|
|
214
|
+
atom_style=atom_style, write_coeff=write_coeff)
|
|
214
215
|
|
|
215
216
|
# convert to ase, or not
|
|
216
217
|
if to_ase:
|
|
@@ -218,14 +219,22 @@ class SimulationBox():
|
|
|
218
219
|
return system
|
|
219
220
|
|
|
220
221
|
def write_lammps_file(self, system, write_coeff=True, filename="data.lammps",
|
|
221
|
-
|
|
222
|
+
atom_style="full"):
|
|
222
223
|
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
# just make sure we are not messing things up
|
|
225
|
+
system = system.copy()
|
|
226
|
+
|
|
227
|
+
# remove coefficients
|
|
228
|
+
if not write_coeff:
|
|
229
|
+
for attribute in ["bonds", "angles", "dihedrals", "impropers"]:
|
|
230
|
+
try:
|
|
231
|
+
system.del_TopologyAttr(attribute)
|
|
232
|
+
except:
|
|
233
|
+
pass
|
|
225
234
|
|
|
226
235
|
# first write data file
|
|
227
236
|
with DATAWriter(filename) as dt:
|
|
228
|
-
dt.write(system.atoms)
|
|
237
|
+
dt.write(system.atoms, atom_style=atom_style)
|
|
229
238
|
|
|
230
239
|
# now write coeff where they belong
|
|
231
240
|
if write_coeff:
|
|
@@ -428,7 +437,7 @@ class SimulationBox():
|
|
|
428
437
|
if not slabs:
|
|
429
438
|
return xsize, ysize
|
|
430
439
|
|
|
431
|
-
xsize_t, ysize_t = np.
|
|
440
|
+
xsize_t, ysize_t = np.nan, np.nan
|
|
432
441
|
|
|
433
442
|
for tslab in slabs:
|
|
434
443
|
xi, yi, _ = self._get_size_from_slab(tslab)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Mon Jul 14 18:55:16 2025
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
|
|
11
|
+
def draw_bond_markers(ax, mol, node_pos, jmol_colors):
|
|
12
|
+
# Assign marker and color for each bond type
|
|
13
|
+
bond_markers = ['o', 's', 'D', '^', 'v', '*', 'P', 'X']
|
|
14
|
+
bond_colors = ['red', 'blue', 'green', 'orange', 'purple', 'brown', 'cyan', 'magenta']
|
|
15
|
+
bondtype_to_marker = {}
|
|
16
|
+
bondtype_to_color = {}
|
|
17
|
+
for idx, bond in enumerate(mol._btype):
|
|
18
|
+
bondtype_to_marker[idx] = bond_markers[idx % len(bond_markers)]
|
|
19
|
+
bondtype_to_color[idx] = bond_colors[idx % len(bond_colors)]
|
|
20
|
+
|
|
21
|
+
# Draw markers at bond midpoints
|
|
22
|
+
for (a, b), btype_idx in zip(mol.bonds[0], mol.bonds[1]):
|
|
23
|
+
marker = bondtype_to_marker[btype_idx]
|
|
24
|
+
color = bondtype_to_color[btype_idx]
|
|
25
|
+
x0, y0 = node_pos[a]
|
|
26
|
+
x1, y1 = node_pos[b]
|
|
27
|
+
xm, ym = (x0 + x1) / 2, (y0 + y1) / 2
|
|
28
|
+
ax.scatter(xm, ym, marker=marker, color=color, s=100, zorder=5)
|
|
29
|
+
|
|
30
|
+
# Legend
|
|
31
|
+
legend_handles = []
|
|
32
|
+
for idx, bond in enumerate(mol._btype):
|
|
33
|
+
marker = bondtype_to_marker[idx]
|
|
34
|
+
color = bondtype_to_color[idx]
|
|
35
|
+
label = f"$k_r={bond.kr}$, $r_0={bond.r0}$"
|
|
36
|
+
h = plt.Line2D([0], [0], marker=marker, color='w', markerfacecolor=color, markersize=10, label=label, linestyle='None')
|
|
37
|
+
legend_handles.append(h)
|
|
38
|
+
ax.legend(handles=legend_handles, loc='best', fontsize=10, framealpha=1.0, edgecolor='k')
|
|
39
|
+
|
|
40
|
+
return
|