mdinterface 1.0.0__tar.gz → 1.1.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.0.0/mdinterface.egg-info → mdinterface-1.1.0}/PKG-INFO +15 -4
- {mdinterface-1.0.0 → mdinterface-1.1.0}/README.md +13 -3
- mdinterface-1.1.0/mdinterface/__init__.py +15 -0
- mdinterface-1.1.0/mdinterface/build/__init__.py +3 -0
- mdinterface-1.1.0/mdinterface/build/box.py +230 -0
- mdinterface-1.1.0/mdinterface/build/continuum2sim.py +45 -0
- mdinterface-1.1.0/mdinterface/build/polymer.py +85 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/core/specie.py +18 -23
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/io/packmol.py +1 -1
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/simulationbox.py +8 -222
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/utils/__init__.py +1 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/utils/database.py +37 -1
- {mdinterface-1.0.0 → mdinterface-1.1.0/mdinterface.egg-info}/PKG-INFO +15 -4
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface.egg-info/SOURCES.txt +4 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface.egg-info/requires.txt +1 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/requirements.txt +1 -0
- mdinterface-1.0.0/mdinterface/__init__.py +0 -10
- {mdinterface-1.0.0 → mdinterface-1.1.0}/LICENSE +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/MANIFEST.in +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/core/__init__.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/core/topology.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/io/__init__.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/io/lammpswriter.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/io/read.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/utils/auxiliary.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface/utils/map.py +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface.egg-info/dependency_links.txt +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/mdinterface.egg-info/top_level.txt +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/pyproject.toml +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/setup.cfg +0 -0
- {mdinterface-1.0.0 → mdinterface-1.1.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mdinterface
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: mdinterface: A package for building interface systems in Molecular Dynamics simulations.
|
|
5
5
|
Home-page: https://gitlab.com/roncofaber/mdinterface
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,10 +16,11 @@ Requires-Dist: mdanalysis
|
|
|
16
16
|
Requires-Dist: ase
|
|
17
17
|
Requires-Dist: numpy
|
|
18
18
|
Requires-Dist: networkx
|
|
19
|
+
Requires-Dist: libarvo
|
|
19
20
|
|
|
20
|
-
# mdinterface:
|
|
21
|
+
# mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
21
22
|
|
|
22
|
-
`mdinterface` is a Python package
|
|
23
|
+
`mdinterface` is a Python package designed to build systems for Molecular Dynamics (MD) simulations. Initially developed to construct electrolyte/electrode interfaces, it is also well-suited for generating MD boxes of liquids, electrolyte systems, and polymer networks.
|
|
23
24
|
|
|
24
25
|
## Features
|
|
25
26
|
|
|
@@ -34,6 +35,12 @@ Requires-Dist: networkx
|
|
|
34
35
|
|
|
35
36
|
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all dependencies.
|
|
36
37
|
|
|
38
|
+
Additionaly, 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`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
conda install -c conda-forge packmol
|
|
42
|
+
```
|
|
43
|
+
|
|
37
44
|
## Installation
|
|
38
45
|
|
|
39
46
|
### Install using `pip`
|
|
@@ -46,7 +53,7 @@ pip install mdinterface
|
|
|
46
53
|
|
|
47
54
|
### Install directly the source code
|
|
48
55
|
|
|
49
|
-
Alternatively you can obtain `mdinterface` directly from the repository by following
|
|
56
|
+
Alternatively you can obtain `mdinterface` directly from the repository by following these steps:
|
|
50
57
|
|
|
51
58
|
Clone the repository in the desired location:
|
|
52
59
|
|
|
@@ -74,3 +81,7 @@ If you plan of making changes, clone the package and install the requirements bu
|
|
|
74
81
|
```bash
|
|
75
82
|
pip install --no-build-isolation --no-deps -e .
|
|
76
83
|
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
Check the files in [examples](mdinterface/examples/) to learn how to use the package.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# mdinterface:
|
|
1
|
+
# mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
2
2
|
|
|
3
|
-
`mdinterface` is a Python package
|
|
3
|
+
`mdinterface` is a Python package designed to build systems for Molecular Dynamics (MD) simulations. Initially developed to construct electrolyte/electrode interfaces, it is also well-suited for generating MD boxes of liquids, electrolyte systems, and polymer networks.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all dependencies.
|
|
17
17
|
|
|
18
|
+
Additionaly, 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`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
conda install -c conda-forge packmol
|
|
22
|
+
```
|
|
23
|
+
|
|
18
24
|
## Installation
|
|
19
25
|
|
|
20
26
|
### Install using `pip`
|
|
@@ -27,7 +33,7 @@ pip install mdinterface
|
|
|
27
33
|
|
|
28
34
|
### Install directly the source code
|
|
29
35
|
|
|
30
|
-
Alternatively you can obtain `mdinterface` directly from the repository by following
|
|
36
|
+
Alternatively you can obtain `mdinterface` directly from the repository by following these steps:
|
|
31
37
|
|
|
32
38
|
Clone the repository in the desired location:
|
|
33
39
|
|
|
@@ -55,3 +61,7 @@ If you plan of making changes, clone the package and install the requirements bu
|
|
|
55
61
|
```bash
|
|
56
62
|
pip install --no-build-isolation --no-deps -e .
|
|
57
63
|
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
Check the files in [examples](mdinterface/examples/) to learn how to use the package.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
3
|
+
|
|
4
|
+
`mdinterface` is a Python package designed to build systems for Molecular Dynamics (MD) simulations.
|
|
5
|
+
Initially developed to construct electrolyte/electrode interfaces, it is also well-suited for generating MD boxes of liquids,
|
|
6
|
+
electrolyte systems, and polymer networks.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = '1.1.0'
|
|
11
|
+
__date__ = '14 Jan. 2025'
|
|
12
|
+
__author__ = 'Fabrice Roncoroni'
|
|
13
|
+
__all__ = ['SimulationBox']
|
|
14
|
+
|
|
15
|
+
from .simulationbox import SimulationBox
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Tue Jan 14 10:23:57 2025
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from mdinterface.io.packmol import header, box_place, fix_place
|
|
10
|
+
from mdinterface.build.continuum2sim import discretize_concentration
|
|
11
|
+
|
|
12
|
+
import MDAnalysis as mda
|
|
13
|
+
from ase import units
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
import subprocess
|
|
18
|
+
|
|
19
|
+
#%%
|
|
20
|
+
|
|
21
|
+
def make_solvent_box(species, solvent, ions, volume, density, nions, concentration,
|
|
22
|
+
conmodel, ion_pos):
|
|
23
|
+
|
|
24
|
+
# make sure info is sound
|
|
25
|
+
assert not( nions is not None and concentration is not None),\
|
|
26
|
+
"'nions' and 'concentration' cannot both be not None"
|
|
27
|
+
|
|
28
|
+
# convert concentration to number of ions
|
|
29
|
+
if concentration is not None:
|
|
30
|
+
nions = int(concentration*np.prod(volume)*units.mol/((units.m/10)**3))
|
|
31
|
+
|
|
32
|
+
# define instructions for packmol
|
|
33
|
+
instructions = []
|
|
34
|
+
|
|
35
|
+
# populate ions
|
|
36
|
+
if (conmodel is not None) or (nions is not None and ions is not None):
|
|
37
|
+
ion_instr = populate_with_ions(ions, nions, volume, ion_pos=ion_pos,
|
|
38
|
+
conmodel=conmodel)
|
|
39
|
+
instructions.extend(ion_instr)
|
|
40
|
+
|
|
41
|
+
# add solvent
|
|
42
|
+
if solvent is not None:
|
|
43
|
+
solvent_volume = 1e-24*np.prod(volume)
|
|
44
|
+
mass = solvent.atoms.masses.sum()
|
|
45
|
+
|
|
46
|
+
# number of solvent molecules
|
|
47
|
+
nummols = int(units.mol*density*(1.0/mass)*solvent_volume)
|
|
48
|
+
|
|
49
|
+
instructions.append([solvent, nummols, "box"])
|
|
50
|
+
|
|
51
|
+
# generate universe file
|
|
52
|
+
universe = populate_box(volume, instructions)
|
|
53
|
+
|
|
54
|
+
if universe is None:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
# Create a dictionary for quick lookup of species by residue name
|
|
58
|
+
species_dict = {specie.residues.resnames[0]: specie for specie in species}
|
|
59
|
+
|
|
60
|
+
alist = []
|
|
61
|
+
for res in universe.residues:
|
|
62
|
+
resname = res.resname
|
|
63
|
+
if resname in species_dict:
|
|
64
|
+
nmol = species_dict[resname].copy()
|
|
65
|
+
nmol.atoms.positions = res.atoms.positions
|
|
66
|
+
alist.append(nmol.atoms)
|
|
67
|
+
|
|
68
|
+
solution = mda.Merge(*alist)
|
|
69
|
+
solution.dimensions = volume + [90,90,90]
|
|
70
|
+
|
|
71
|
+
return solution
|
|
72
|
+
|
|
73
|
+
# populate a box with solvent and ions
|
|
74
|
+
def populate_box(volume, instructions, input_file="input_packmol.in",
|
|
75
|
+
output_file="system.pdb"):
|
|
76
|
+
|
|
77
|
+
if not instructions:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
# check volume
|
|
81
|
+
assert len(volume) == 3, "Check volume!"
|
|
82
|
+
|
|
83
|
+
# generate box boundaries with 1 AA padding
|
|
84
|
+
box = np.concatenate(([1,1,1], np.asarray(volume)-1)).tolist()
|
|
85
|
+
|
|
86
|
+
tmp_files = ["packmol.log", "input_packmol.in", "system.pdb"]
|
|
87
|
+
with open(input_file, "w") as fout:
|
|
88
|
+
|
|
89
|
+
fout.write(header.format(output_file, np.random.randint(100000)))
|
|
90
|
+
|
|
91
|
+
for cc, instruction in enumerate(instructions):
|
|
92
|
+
|
|
93
|
+
# unpack instructions
|
|
94
|
+
mol = instruction[0]
|
|
95
|
+
rep = instruction[1]
|
|
96
|
+
typ = instruction[2]
|
|
97
|
+
|
|
98
|
+
if isinstance(rep, int):
|
|
99
|
+
if not rep:
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
if typ == "box": # normal add
|
|
103
|
+
fout.write(box_place.format(cc, rep, " ".join(map(str, box))))
|
|
104
|
+
|
|
105
|
+
elif typ == "fixed": # coordinate -> fixed point
|
|
106
|
+
fout.write(fix_place.format(cc, *rep))
|
|
107
|
+
|
|
108
|
+
elif typ == "zfixed": # small bin to use in CM
|
|
109
|
+
tbox = box.copy()
|
|
110
|
+
tbox[2] = tbox[2] - mol.estimate_specie_radius()
|
|
111
|
+
tbox[5] = tbox[5] + mol.estimate_specie_radius()
|
|
112
|
+
fout.write(box_place.format(cc, 1, " ".join(map(str, tbox))))
|
|
113
|
+
mol = mol.to_universe()
|
|
114
|
+
|
|
115
|
+
else:
|
|
116
|
+
raise "Wrong instructions"
|
|
117
|
+
|
|
118
|
+
# write tmp pdb file and store info
|
|
119
|
+
mol.atoms.write("mol_{}.pdb".format(cc))
|
|
120
|
+
tmp_files.append("mol_{}.pdb".format(cc))
|
|
121
|
+
|
|
122
|
+
# run packmol
|
|
123
|
+
try:
|
|
124
|
+
subprocess.run(['packmol < {} > packmol.log'.format(input_file)],
|
|
125
|
+
shell=True, check=True, text=True)
|
|
126
|
+
|
|
127
|
+
except:
|
|
128
|
+
print("WARNING: packmol might not have worked, check system.")
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
universe = mda.Universe(output_file)
|
|
132
|
+
except:
|
|
133
|
+
universe = None
|
|
134
|
+
|
|
135
|
+
# remove temp mol files and packmol files
|
|
136
|
+
subprocess.call(['rm'] + tmp_files)
|
|
137
|
+
|
|
138
|
+
return universe
|
|
139
|
+
|
|
140
|
+
# generate a slab from a unit cell
|
|
141
|
+
def make_interface_slab(interface_uc, xsize, ysize, layers=1):
|
|
142
|
+
|
|
143
|
+
if layers == 0 or interface_uc is None:
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
xrep = int(np.round(xsize/interface_uc.atoms.get_cell()[0][0]))
|
|
147
|
+
yrep = int(np.round(ysize/interface_uc.atoms.get_cell()[1][1]))
|
|
148
|
+
|
|
149
|
+
slab = interface_uc.copy()
|
|
150
|
+
|
|
151
|
+
if not np.isclose(np.dot(slab.atoms.cell[0], [1,0,0]), slab.atoms.cell[0][0]):
|
|
152
|
+
xrep +=1
|
|
153
|
+
print("WARNING: check interface if pattern matches")
|
|
154
|
+
|
|
155
|
+
if not np.isclose(np.dot(slab.atoms.cell[1], [0,1,0]), slab.atoms.cell[1][1]):
|
|
156
|
+
yrep +=1
|
|
157
|
+
print("WARNING: check interface if pattern matches")
|
|
158
|
+
|
|
159
|
+
slab.repeat((xrep, yrep, 1), make_cubic=True)
|
|
160
|
+
|
|
161
|
+
if layers > 1: # helps with indexing
|
|
162
|
+
slab.repeat([1,1,layers])
|
|
163
|
+
|
|
164
|
+
slab.atoms.center()
|
|
165
|
+
# slab.atoms.rattle()
|
|
166
|
+
|
|
167
|
+
return slab
|
|
168
|
+
|
|
169
|
+
#THANKS CHATGPT (but mostly me tbh)
|
|
170
|
+
def populate_with_ions(ions, nions, volume, ion_pos=False, conmodel=None):
|
|
171
|
+
def place_ion(ion, volume, ion_coords, ion_radii, zpos=None, max_attempts=100):
|
|
172
|
+
ion_radius = ion.estimate_specie_radius()
|
|
173
|
+
for _ in range(max_attempts):
|
|
174
|
+
new_coord = ion_radius + 1 + np.random.rand(3) * (volume - 2 * (ion_radius + 1))
|
|
175
|
+
if zpos is not None:
|
|
176
|
+
new_coord[2] = zpos
|
|
177
|
+
|
|
178
|
+
if not ion_coords or np.all(np.linalg.norm(ion_coords - new_coord, axis=1) >= np.array(ion_radii) + ion_radius + 1):
|
|
179
|
+
return new_coord
|
|
180
|
+
print(f"Warning: Failed to place ion {ion} after {max_attempts} attempts")
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
def place_ions_conmodel(ions, conmodel, volume, max_attempts=100):
|
|
184
|
+
instructions = []
|
|
185
|
+
ion_coords = []
|
|
186
|
+
ion_radii = []
|
|
187
|
+
for cc, ion in enumerate(ions):
|
|
188
|
+
z_coords, conc_profile = conmodel[cc]
|
|
189
|
+
z_positions = discretize_concentration(ion, conc_profile, z_coords, volume)
|
|
190
|
+
for zpos in z_positions:
|
|
191
|
+
new_coord = place_ion(ion, volume, ion_coords, ion_radii, zpos=zpos,
|
|
192
|
+
max_attempts=max_attempts)
|
|
193
|
+
if new_coord is not None:
|
|
194
|
+
ion_coords.append(new_coord)
|
|
195
|
+
ion_radii.append(ion.estimate_specie_radius())
|
|
196
|
+
instructions.append((ion.to_universe(), new_coord, "fixed"))
|
|
197
|
+
return instructions
|
|
198
|
+
|
|
199
|
+
def place_ions_random(ions, nions, volume, to_center, max_attempts=100):
|
|
200
|
+
instructions = []
|
|
201
|
+
ion_coords = []
|
|
202
|
+
ion_radii = []
|
|
203
|
+
for cc, ion in enumerate(ions):
|
|
204
|
+
nrep = nions if isinstance(nions, int) else nions[cc]
|
|
205
|
+
for _ in range(nrep):
|
|
206
|
+
new_coord = volume / 2 if to_center else place_ion(ion, volume,
|
|
207
|
+
ion_coords,
|
|
208
|
+
ion_radii,
|
|
209
|
+
max_attempts=max_attempts)
|
|
210
|
+
if new_coord is not None:
|
|
211
|
+
ion_coords.append(new_coord)
|
|
212
|
+
ion_radii.append(ion.estimate_specie_radius())
|
|
213
|
+
instructions.append((ion.to_universe(), new_coord, "fixed"))
|
|
214
|
+
return instructions
|
|
215
|
+
|
|
216
|
+
max_attempts = 100 # Limit placement attempts to avoid infinite loop
|
|
217
|
+
volume = np.array(volume)
|
|
218
|
+
|
|
219
|
+
if conmodel is not None:
|
|
220
|
+
assert len(conmodel) == len(ions), "Need one profile per specie"
|
|
221
|
+
return place_ions_conmodel(ions, conmodel, volume, max_attempts=max_attempts)
|
|
222
|
+
|
|
223
|
+
if ion_pos == "box":
|
|
224
|
+
return [(ion.to_universe(), nions if isinstance(nions, int) else nions[cc], "box") for cc, ion in enumerate(ions)]
|
|
225
|
+
|
|
226
|
+
to_center = ion_pos == "center"
|
|
227
|
+
if ion_pos == "left":
|
|
228
|
+
volume[2] /= 2
|
|
229
|
+
|
|
230
|
+
return place_ions_random(ions, nions, volume, to_center, max_attempts=max_attempts)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Tue Jan 14 10:34:26 2025
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ase import units
|
|
10
|
+
import numpy as np
|
|
11
|
+
from scipy import integrate
|
|
12
|
+
|
|
13
|
+
#%%
|
|
14
|
+
# return a discrete concentration profile given a "continuum" conc. profile
|
|
15
|
+
# for a specie. Return positions of the bins where ion should be.
|
|
16
|
+
def discretize_concentration(specie, conc_profile, z_coords, volume):
|
|
17
|
+
|
|
18
|
+
xsize, ysize, zsize = volume
|
|
19
|
+
|
|
20
|
+
conv = units.mol/1e27
|
|
21
|
+
area = xsize*ysize
|
|
22
|
+
|
|
23
|
+
# integrate conc. prof. over area
|
|
24
|
+
cumsum = integrate.cumulative_trapezoid(conc_profile, z_coords, initial=0, axis=0)
|
|
25
|
+
cumsum_conv = cumsum*conv*area
|
|
26
|
+
|
|
27
|
+
# generate bins (1 atom in each one) for K
|
|
28
|
+
Nions = 0
|
|
29
|
+
bins = []
|
|
30
|
+
for ii, con in enumerate(cumsum_conv):
|
|
31
|
+
if con > np.maximum(cumsum_conv.max()/100, Nions):
|
|
32
|
+
Nions += 1
|
|
33
|
+
bins.append(z_coords[ii])
|
|
34
|
+
|
|
35
|
+
# now find middle point of bins
|
|
36
|
+
z_pos = []
|
|
37
|
+
for ii, pos in enumerate(bins[:-1]):
|
|
38
|
+
z_pos.append((bins[ii] + bins[ii+1])/2)
|
|
39
|
+
|
|
40
|
+
# add one last ion
|
|
41
|
+
if cumsum_conv.max() % 1 > 0.5:
|
|
42
|
+
z_pos.append((z_coords.max()+bins[-1])/2)
|
|
43
|
+
Nions += 1
|
|
44
|
+
|
|
45
|
+
return z_pos
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Tue Oct 22 17:17:30 2024
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import ase
|
|
11
|
+
import ase.build
|
|
12
|
+
|
|
13
|
+
#%%
|
|
14
|
+
|
|
15
|
+
def build_polymer(system, substitute, nrep, target_distance=1.600):
|
|
16
|
+
"""
|
|
17
|
+
Build a polymer by replicating a monomer structure and replacing 'X' atoms with a substitute.
|
|
18
|
+
|
|
19
|
+
Parameters:
|
|
20
|
+
- system (ase.Atoms): The monomer structure as an ASE Atoms object.
|
|
21
|
+
- substitute (str): Chemical symbol to replace 'X' with after replication.
|
|
22
|
+
- nrep (int): Number of times to replicate the monomer.
|
|
23
|
+
- target_distance (float, optional): Desired bond distance at the joining points (in angstroms).
|
|
24
|
+
If None, the original distance between 'X' atoms is used.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
- sout (ase.Atoms): The replicated and modified polymer structure.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
system = system.copy()
|
|
31
|
+
|
|
32
|
+
# Check if nrep is at least 1
|
|
33
|
+
if nrep < 1:
|
|
34
|
+
raise ValueError("nrep must be at least 1")
|
|
35
|
+
|
|
36
|
+
# Find the indices of the 'X' atoms
|
|
37
|
+
x_idxs = np.where(np.array(system.get_chemical_symbols()) == "X")[0]
|
|
38
|
+
if len(x_idxs) != 2:
|
|
39
|
+
raise ValueError("The monomer should contain exactly two 'X' atoms.")
|
|
40
|
+
|
|
41
|
+
# Find atoms connected to X
|
|
42
|
+
ini_idx = ase.build.connected_indices(system, x_idxs[0])[1]
|
|
43
|
+
end_idx = ase.build.connected_indices(system, x_idxs[1])[1]
|
|
44
|
+
|
|
45
|
+
bnd_vec = system.get_distance(end_idx, x_idxs[1], vector=True)
|
|
46
|
+
bnd_vec = target_distance * bnd_vec / np.linalg.norm(bnd_vec)
|
|
47
|
+
|
|
48
|
+
ini_pos = system.get_positions()[ini_idx]
|
|
49
|
+
end_pos = system.get_positions()[end_idx] + bnd_vec
|
|
50
|
+
|
|
51
|
+
# Get the vector between the two 'X' atoms
|
|
52
|
+
X_vec = end_pos - ini_pos
|
|
53
|
+
|
|
54
|
+
# remember connecting points
|
|
55
|
+
is_connected = np.array(len(system)*[False])
|
|
56
|
+
is_connected[ini_idx] = True
|
|
57
|
+
is_connected[end_idx] = True
|
|
58
|
+
system.new_array("is_connected", is_connected)
|
|
59
|
+
|
|
60
|
+
# Start with a copy of the system
|
|
61
|
+
sout = system.copy()
|
|
62
|
+
sout.new_array("mon_id", np.array(len(sout)*[0]))
|
|
63
|
+
|
|
64
|
+
# Replicate the monomer
|
|
65
|
+
if nrep > 1:
|
|
66
|
+
del sout[x_idxs[1]] # Remove the second 'X' atom in the first monomer
|
|
67
|
+
|
|
68
|
+
for ii in range(nrep - 1):
|
|
69
|
+
sadd = system.copy()
|
|
70
|
+
|
|
71
|
+
if ii < nrep - 2:
|
|
72
|
+
del sadd[x_idxs] # Remove both 'X' atoms in the intermediate monomers
|
|
73
|
+
else:
|
|
74
|
+
del sadd[x_idxs[0]] # Remove the first 'X' atom in the last monomer
|
|
75
|
+
|
|
76
|
+
sadd.translate((ii + 1) * X_vec)
|
|
77
|
+
sadd.set_array("mon_id", np.array(len(sadd)*[ii+1]))
|
|
78
|
+
sout += sadd
|
|
79
|
+
|
|
80
|
+
# Replace 'X' with the substitute
|
|
81
|
+
symbols = np.array(sout.get_chemical_symbols())
|
|
82
|
+
symbols[symbols == "X"] = substitute
|
|
83
|
+
sout.set_chemical_symbols(symbols)
|
|
84
|
+
|
|
85
|
+
return sout#ase.build.sort(sout)
|
|
@@ -9,15 +9,15 @@ Created on Fri Apr 19 13:58:43 2024
|
|
|
9
9
|
import ase
|
|
10
10
|
import ase.build
|
|
11
11
|
import ase.visualize
|
|
12
|
+
from ase.data import vdw_radii
|
|
12
13
|
from ase.data.colors import jmol_colors
|
|
13
14
|
import MDAnalysis as mda
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
from mdinterface.core.topology import Atom
|
|
16
|
+
import mdinterface.utils.map as pmap
|
|
18
17
|
import mdinterface.utils.auxiliary as aux
|
|
18
|
+
from mdinterface.core.topology import Atom
|
|
19
19
|
from mdinterface.io.read import read_lammps_data_file
|
|
20
|
-
|
|
20
|
+
from mdinterface.utils.auxiliary import as_list, find_smallest_missing
|
|
21
21
|
|
|
22
22
|
import copy
|
|
23
23
|
import numpy as np
|
|
@@ -387,30 +387,25 @@ class Specie(object):
|
|
|
387
387
|
|
|
388
388
|
return atom_types
|
|
389
389
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
Estimate the radius of the sphere containing the given points.
|
|
393
|
-
|
|
394
|
-
Parameters:
|
|
395
|
-
points (numpy.ndarray): A 2D array of shape (n, 3) where n is the number of points.
|
|
396
|
-
|
|
397
|
-
Returns:
|
|
398
|
-
float: The estimated radius of the sphere.
|
|
399
|
-
"""
|
|
400
|
-
|
|
401
|
-
points = self.atoms.get_positions()
|
|
390
|
+
# method to estimate the volume of the specie
|
|
391
|
+
def estimate_specie_volume(self, probe_radius=0):
|
|
402
392
|
|
|
403
|
-
|
|
404
|
-
|
|
393
|
+
try:
|
|
394
|
+
from libarvo import molecular_vs
|
|
395
|
+
except:
|
|
396
|
+
print("libarvo NOT found. Install it.")
|
|
405
397
|
|
|
406
|
-
|
|
407
|
-
|
|
398
|
+
centers = self.atoms.get_positions()
|
|
399
|
+
radii = [vdw_radii[ii] for ii in self.atoms.get_atomic_numbers()]
|
|
408
400
|
|
|
409
|
-
|
|
410
|
-
radius = np.max(distances)
|
|
401
|
+
volume, surface = molecular_vs(centers, radii, probe_radius)
|
|
411
402
|
|
|
412
|
-
return
|
|
403
|
+
return volume
|
|
413
404
|
|
|
405
|
+
# method to estimate the radius of the specie if it were a sphere
|
|
406
|
+
def estimate_specie_radius(self, probe_radius=0):
|
|
407
|
+
volume = self.estimate_specie_volume(probe_radius=probe_radius)
|
|
408
|
+
return (3 * volume / (4 * np.pi)) ** (1/3)
|
|
414
409
|
|
|
415
410
|
def view(self):
|
|
416
411
|
ase.visualize.view(self.atoms)
|
|
@@ -8,15 +8,13 @@ Created on Tue Oct 24 15:14:41 2023
|
|
|
8
8
|
|
|
9
9
|
from mdinterface.utils.auxiliary import label_to_element, as_list, find_smallest_missing
|
|
10
10
|
from mdinterface.io.lammpswriter import DATAWriter
|
|
11
|
-
from mdinterface.
|
|
11
|
+
from mdinterface.build.box import make_interface_slab, make_solvent_box
|
|
12
12
|
|
|
13
|
+
import ase
|
|
13
14
|
import MDAnalysis as mda
|
|
14
15
|
|
|
15
|
-
import ase
|
|
16
|
-
from ase import units
|
|
17
16
|
import numpy as np
|
|
18
17
|
|
|
19
|
-
import subprocess
|
|
20
18
|
import shutil
|
|
21
19
|
|
|
22
20
|
import warnings
|
|
@@ -40,7 +38,6 @@ class SimulationBox():
|
|
|
40
38
|
|
|
41
39
|
return
|
|
42
40
|
|
|
43
|
-
|
|
44
41
|
def _setup_species(self, solvent, solute, interface, enderface):
|
|
45
42
|
|
|
46
43
|
self._solvent = None
|
|
@@ -72,7 +69,6 @@ class SimulationBox():
|
|
|
72
69
|
|
|
73
70
|
return
|
|
74
71
|
|
|
75
|
-
|
|
76
72
|
def _update_topology_indexes(self):
|
|
77
73
|
|
|
78
74
|
nitems = {
|
|
@@ -105,214 +101,6 @@ class SimulationBox():
|
|
|
105
101
|
|
|
106
102
|
return
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
# populate a box with solvent and ions
|
|
110
|
-
@staticmethod
|
|
111
|
-
def populate_box(volume, instructions, input_file="input_packmol.in",
|
|
112
|
-
output_file="system.pdb"):
|
|
113
|
-
|
|
114
|
-
if not instructions:
|
|
115
|
-
return None
|
|
116
|
-
|
|
117
|
-
# check volume
|
|
118
|
-
assert len(volume) == 3, "Check volume!"
|
|
119
|
-
|
|
120
|
-
# generate box boundaries with 1 AA padding
|
|
121
|
-
box = np.concatenate(([1,1,1], np.asarray(volume)-1)).tolist()
|
|
122
|
-
|
|
123
|
-
tmp_files = ["packmol.log", "input_packmol.in", "system.pdb"]
|
|
124
|
-
with open(input_file, "w") as fout:
|
|
125
|
-
|
|
126
|
-
fout.write(header.format(output_file, np.random.randint(100000)))
|
|
127
|
-
|
|
128
|
-
for cc, instruction in enumerate(instructions):
|
|
129
|
-
|
|
130
|
-
# unpack instructions
|
|
131
|
-
mol = instruction[0]
|
|
132
|
-
rep = instruction[1]
|
|
133
|
-
typ = instruction[2]
|
|
134
|
-
|
|
135
|
-
if isinstance(rep, int):
|
|
136
|
-
if not rep:
|
|
137
|
-
continue
|
|
138
|
-
|
|
139
|
-
if typ == "box": # normal add
|
|
140
|
-
fout.write(box_place.format(cc, rep, " ".join(map(str, box))))
|
|
141
|
-
|
|
142
|
-
elif typ == "fixed": # coordinate -> fixed point
|
|
143
|
-
|
|
144
|
-
fout.write(fix_place.format(cc, *rep))
|
|
145
|
-
# make temp box to fill
|
|
146
|
-
# tbox = box.copy()
|
|
147
|
-
# tbox[2] = rep - 1
|
|
148
|
-
# tbox[-1] = rep + 1
|
|
149
|
-
|
|
150
|
-
# fout.write(box_place.format(cc, 1, " ".join(map(str, tbox))))
|
|
151
|
-
|
|
152
|
-
else:
|
|
153
|
-
raise "Wrong instructions"
|
|
154
|
-
|
|
155
|
-
# write tmp pdb file and store info
|
|
156
|
-
mol.atoms.write("mol_{}.pdb".format(cc))
|
|
157
|
-
tmp_files.append("mol_{}.pdb".format(cc))
|
|
158
|
-
|
|
159
|
-
# run packmol
|
|
160
|
-
try:
|
|
161
|
-
subprocess.run(['packmol < {} > packmol.log'.format(input_file)],
|
|
162
|
-
shell=True, check=True, text=True)
|
|
163
|
-
|
|
164
|
-
except:
|
|
165
|
-
print("WARNING: packmol might not have worked, check system.")
|
|
166
|
-
|
|
167
|
-
try:
|
|
168
|
-
universe = mda.Universe(output_file)
|
|
169
|
-
except:
|
|
170
|
-
universe = None
|
|
171
|
-
|
|
172
|
-
# remove temp mol files and packmol files
|
|
173
|
-
subprocess.call(['rm'] + tmp_files)
|
|
174
|
-
|
|
175
|
-
return universe
|
|
176
|
-
|
|
177
|
-
# generate a slab from a unit cell
|
|
178
|
-
@staticmethod
|
|
179
|
-
def make_interface_slab(interface_uc, xsize, ysize, layers=1):
|
|
180
|
-
|
|
181
|
-
if layers == 0 or interface_uc is None:
|
|
182
|
-
return None
|
|
183
|
-
|
|
184
|
-
xrep = int(np.round(xsize/interface_uc.atoms.get_cell()[0][0]))
|
|
185
|
-
yrep = int(np.round(ysize/interface_uc.atoms.get_cell()[1][1]))
|
|
186
|
-
|
|
187
|
-
slab = interface_uc.copy()
|
|
188
|
-
|
|
189
|
-
if not np.isclose(np.dot(slab.atoms.cell[0], [1,0,0]), slab.atoms.cell[0][0]):
|
|
190
|
-
xrep +=1
|
|
191
|
-
print("WARNING: check interface if pattern matches")
|
|
192
|
-
|
|
193
|
-
if not np.isclose(np.dot(slab.atoms.cell[1], [0,1,0]), slab.atoms.cell[1][1]):
|
|
194
|
-
yrep +=1
|
|
195
|
-
print("WARNING: check interface if pattern matches")
|
|
196
|
-
|
|
197
|
-
slab.repeat((xrep, yrep, 1), make_cubic=True)
|
|
198
|
-
|
|
199
|
-
if layers > 1: # helps with indexing
|
|
200
|
-
slab.repeat([1,1,layers])
|
|
201
|
-
|
|
202
|
-
slab.atoms.center()
|
|
203
|
-
# slab.atoms.rattle()
|
|
204
|
-
|
|
205
|
-
return slab
|
|
206
|
-
|
|
207
|
-
@staticmethod #THANKS CHATGPT
|
|
208
|
-
def populate_with_ions(ions, nions, volume, ion_pos=False):
|
|
209
|
-
volume = np.array(volume)
|
|
210
|
-
ion_coords = []
|
|
211
|
-
instructions = []
|
|
212
|
-
|
|
213
|
-
to_center = False
|
|
214
|
-
|
|
215
|
-
if ion_pos == "left":
|
|
216
|
-
volume[2] /= 2
|
|
217
|
-
elif ion_pos == "center":
|
|
218
|
-
if not isinstance(nions, int) and len(nions) != 1:
|
|
219
|
-
raise ValueError("Center positioning can only be used with a single ion")
|
|
220
|
-
to_center = True
|
|
221
|
-
elif ion_pos == "box":
|
|
222
|
-
for cc, ion in enumerate(ions):
|
|
223
|
-
nrep = nions if isinstance(nions, int) else nions[cc]
|
|
224
|
-
instructions.append((ion.to_universe(), nrep, "box"))
|
|
225
|
-
return instructions
|
|
226
|
-
|
|
227
|
-
for cc, ion in enumerate(ions):
|
|
228
|
-
ion_radius = ion.estimate_sphere_radius()
|
|
229
|
-
nrep = nions[cc] if isinstance(nions, list) else nions
|
|
230
|
-
|
|
231
|
-
for _ in range(nrep):
|
|
232
|
-
max_attempts = 100 # Limit placement attempts to avoid infinite loop
|
|
233
|
-
for _ in range(max_attempts):
|
|
234
|
-
if to_center:
|
|
235
|
-
new_coord = volume / 2
|
|
236
|
-
else:
|
|
237
|
-
new_coord = ion_radius + 1 + np.random.rand(3) * (volume - 2 * (ion_radius + 1))
|
|
238
|
-
|
|
239
|
-
if ion_coords:
|
|
240
|
-
distances = np.linalg.norm(ion_coords - new_coord, axis=1)
|
|
241
|
-
if np.all(distances > 3):
|
|
242
|
-
break
|
|
243
|
-
else:
|
|
244
|
-
break
|
|
245
|
-
else:
|
|
246
|
-
print(f"Warning: Failed to place ion {ion} after {max_attempts} attempts")
|
|
247
|
-
|
|
248
|
-
ion_coords.append(new_coord)
|
|
249
|
-
instructions.append((ion.to_universe(), new_coord, "fixed"))
|
|
250
|
-
|
|
251
|
-
return instructions
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def make_solvent_box(self, solvent, ions, volume, density, nions, concentration,
|
|
255
|
-
conmodel, ion_pos):
|
|
256
|
-
|
|
257
|
-
# make sure info is sound
|
|
258
|
-
assert not( nions is not None and concentration is not None),\
|
|
259
|
-
"'nions' and 'concentration' cannot both be not None"
|
|
260
|
-
|
|
261
|
-
# convert concentration to number of ions
|
|
262
|
-
if concentration is not None:
|
|
263
|
-
nions = int(concentration*np.prod(volume)*units.mol/((units.m/10)**3))
|
|
264
|
-
|
|
265
|
-
# define instructions for packmol
|
|
266
|
-
instructions = []
|
|
267
|
-
|
|
268
|
-
# populate according to continuum model
|
|
269
|
-
if conmodel is not None:
|
|
270
|
-
z_positions = conmodel.discretize_profile(volume)
|
|
271
|
-
|
|
272
|
-
for cc, ion in enumerate(ions):
|
|
273
|
-
for z_pos in z_positions[cc]:
|
|
274
|
-
|
|
275
|
-
# radius = conmodel.species[cc].radius
|
|
276
|
-
instructions.append([ion, z_pos, "fixed"])
|
|
277
|
-
|
|
278
|
-
# populate according to fixed number of ions
|
|
279
|
-
elif nions is not None and ions is not None:
|
|
280
|
-
|
|
281
|
-
ion_instr = self.populate_with_ions(ions, nions, volume, ion_pos=ion_pos)
|
|
282
|
-
instructions.extend(ion_instr)
|
|
283
|
-
|
|
284
|
-
# add solvent
|
|
285
|
-
if solvent is not None:
|
|
286
|
-
solvent_volume = 1e-24*np.prod(volume)
|
|
287
|
-
mass = solvent.atoms.masses.sum()
|
|
288
|
-
|
|
289
|
-
# number of solvent molecules
|
|
290
|
-
nummols = int(units.mol*density*(1.0/mass)*solvent_volume)
|
|
291
|
-
|
|
292
|
-
instructions.append([solvent, nummols, "box"])
|
|
293
|
-
|
|
294
|
-
# generate universe file
|
|
295
|
-
universe = self.populate_box(volume, instructions)
|
|
296
|
-
|
|
297
|
-
if universe is None:
|
|
298
|
-
return None
|
|
299
|
-
|
|
300
|
-
# Create a dictionary for quick lookup of species by residue name
|
|
301
|
-
species_dict = {specie.residues.resnames[0]: specie for specie in self.species}
|
|
302
|
-
|
|
303
|
-
alist = []
|
|
304
|
-
for res in universe.residues:
|
|
305
|
-
resname = res.resname
|
|
306
|
-
if resname in species_dict:
|
|
307
|
-
nmol = species_dict[resname].copy()
|
|
308
|
-
nmol.atoms.positions = res.atoms.positions
|
|
309
|
-
alist.append(nmol.atoms)
|
|
310
|
-
|
|
311
|
-
solution = mda.Merge(*alist)
|
|
312
|
-
solution.dimensions = volume + [90,90,90]
|
|
313
|
-
|
|
314
|
-
return solution
|
|
315
|
-
|
|
316
104
|
@staticmethod
|
|
317
105
|
def _get_size_from_slab(slab):
|
|
318
106
|
|
|
@@ -320,9 +108,9 @@ class SimulationBox():
|
|
|
320
108
|
ysize = [0,1,0]@slab.atoms.cell@[0,1,0]
|
|
321
109
|
slab_depth = [0,0,1]@slab.atoms.cell@[0,0,1]
|
|
322
110
|
|
|
323
|
-
|
|
324
111
|
return xsize, ysize, slab_depth
|
|
325
112
|
|
|
113
|
+
# main driver to generate a simulation box given instructions
|
|
326
114
|
def make_simulation_box(self, solvent_vol, solvent_rho, nions=None,
|
|
327
115
|
concentration=None, conmodel=None, layers=1,
|
|
328
116
|
padding=1.5, to_ase=False, mirror=False,
|
|
@@ -334,8 +122,8 @@ class SimulationBox():
|
|
|
334
122
|
xsize, ysize, zsize = solvent_vol
|
|
335
123
|
|
|
336
124
|
# make slab
|
|
337
|
-
islab =
|
|
338
|
-
eslab =
|
|
125
|
+
islab = make_interface_slab(self._interface, xsize, ysize, layers=layers)
|
|
126
|
+
eslab = make_interface_slab(self._enderface, xsize, ysize, layers=layers)
|
|
339
127
|
|
|
340
128
|
xi, yi, sdi, xe, ye, sde = 0, 0, 0, 0, 0, 0
|
|
341
129
|
# update the volume with multiples of UC
|
|
@@ -358,9 +146,9 @@ class SimulationBox():
|
|
|
358
146
|
ysize = np.maximum(yi, ye)
|
|
359
147
|
|
|
360
148
|
# make solvent box
|
|
361
|
-
solvent =
|
|
362
|
-
|
|
363
|
-
|
|
149
|
+
solvent = make_solvent_box(self.species, self.solvent, self._solute,
|
|
150
|
+
[xsize, ysize, zsize], solvent_rho,
|
|
151
|
+
nions, concentration, conmodel, ion_pos)
|
|
364
152
|
|
|
365
153
|
# now build system
|
|
366
154
|
system = None
|
|
@@ -393,7 +181,6 @@ class SimulationBox():
|
|
|
393
181
|
system = mda.Merge(system.atoms, eslab.atoms)
|
|
394
182
|
zdim += sde + padding
|
|
395
183
|
|
|
396
|
-
|
|
397
184
|
system.dimensions = [xsize, ysize, zdim] + [90, 90, 90] #TODO not like this
|
|
398
185
|
|
|
399
186
|
if vacuum is not None:
|
|
@@ -593,7 +380,6 @@ class SimulationBox():
|
|
|
593
380
|
|
|
594
381
|
@property
|
|
595
382
|
def _species(self):
|
|
596
|
-
|
|
597
383
|
return np.concatenate((as_list(self._solvent), as_list(self._solute),
|
|
598
384
|
as_list(self._interface), as_list(self._enderface)))
|
|
599
385
|
|
|
@@ -13,7 +13,7 @@ from mdinterface.core.specie import Specie
|
|
|
13
13
|
from mdinterface.core.topology import Bond, Angle, Atom, Dihedral, Improper
|
|
14
14
|
|
|
15
15
|
import ase.build
|
|
16
|
-
|
|
16
|
+
from ase.build import fcc111
|
|
17
17
|
#%%
|
|
18
18
|
|
|
19
19
|
# graphene https://onlinelibrary.wiley.com/doi/10.1002/adma.201705791
|
|
@@ -109,3 +109,39 @@ class Perchlorate(Specie):
|
|
|
109
109
|
|
|
110
110
|
super().__init__(atoms=pclo, lj=lj, bonds=[b1, b2], angles=a1, cutoff=1.5, **kwargs)
|
|
111
111
|
return
|
|
112
|
+
|
|
113
|
+
#metal parameters from https://pubs.acs.org/doi/full/10.1021/jp801931d
|
|
114
|
+
class Metal111(Specie):
|
|
115
|
+
|
|
116
|
+
def __init__(self, metal, **kwargs):
|
|
117
|
+
|
|
118
|
+
slab = fcc111(metal, size=(1,2,3), orthogonal=True, periodic=True)
|
|
119
|
+
|
|
120
|
+
# those are in kcal/mol and r0, so it should be divided by 2**1/6
|
|
121
|
+
metal_params = {
|
|
122
|
+
"Ag" : [4.56, 2.955],
|
|
123
|
+
"Al" : [4.02, 2.925],
|
|
124
|
+
"Au" : [5.29, 2.951],
|
|
125
|
+
"Cu" : [4.72, 2.616],
|
|
126
|
+
"Ni" : [5.65, 2.552],
|
|
127
|
+
"Pb" : [2.93, 3.565],
|
|
128
|
+
"Pd" : [6.15, 2.819],
|
|
129
|
+
"Pt" : [7.80, 2.845],
|
|
130
|
+
"Ac" : [6.51, 3.843],
|
|
131
|
+
"Ca" : [3.36, 4.025],
|
|
132
|
+
"Ce" : [6.38, 3.734],
|
|
133
|
+
"Es" : [2.88, 4.133],
|
|
134
|
+
"Ir" : [9.20, 2.785],
|
|
135
|
+
"Fe" : [6.00, 2.590],
|
|
136
|
+
"Rh" : [7.84, 2.757],
|
|
137
|
+
"Sr" : [3.40, 4.379],
|
|
138
|
+
"Th" : [8.47, 3.683],
|
|
139
|
+
"Yb" : [2.71, 3.942]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
lj = metal_params[metal]
|
|
143
|
+
lj_copy = [lj[0], round(lj[1] / np.power(2, 1/6), 5)]
|
|
144
|
+
|
|
145
|
+
super().__init__(atoms=slab, lj=lj_copy, **kwargs)
|
|
146
|
+
|
|
147
|
+
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mdinterface
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: mdinterface: A package for building interface systems in Molecular Dynamics simulations.
|
|
5
5
|
Home-page: https://gitlab.com/roncofaber/mdinterface
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,10 +16,11 @@ Requires-Dist: mdanalysis
|
|
|
16
16
|
Requires-Dist: ase
|
|
17
17
|
Requires-Dist: numpy
|
|
18
18
|
Requires-Dist: networkx
|
|
19
|
+
Requires-Dist: libarvo
|
|
19
20
|
|
|
20
|
-
# mdinterface:
|
|
21
|
+
# mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
21
22
|
|
|
22
|
-
`mdinterface` is a Python package
|
|
23
|
+
`mdinterface` is a Python package designed to build systems for Molecular Dynamics (MD) simulations. Initially developed to construct electrolyte/electrode interfaces, it is also well-suited for generating MD boxes of liquids, electrolyte systems, and polymer networks.
|
|
23
24
|
|
|
24
25
|
## Features
|
|
25
26
|
|
|
@@ -34,6 +35,12 @@ Requires-Dist: networkx
|
|
|
34
35
|
|
|
35
36
|
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all dependencies.
|
|
36
37
|
|
|
38
|
+
Additionaly, 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`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
conda install -c conda-forge packmol
|
|
42
|
+
```
|
|
43
|
+
|
|
37
44
|
## Installation
|
|
38
45
|
|
|
39
46
|
### Install using `pip`
|
|
@@ -46,7 +53,7 @@ pip install mdinterface
|
|
|
46
53
|
|
|
47
54
|
### Install directly the source code
|
|
48
55
|
|
|
49
|
-
Alternatively you can obtain `mdinterface` directly from the repository by following
|
|
56
|
+
Alternatively you can obtain `mdinterface` directly from the repository by following these steps:
|
|
50
57
|
|
|
51
58
|
Clone the repository in the desired location:
|
|
52
59
|
|
|
@@ -74,3 +81,7 @@ If you plan of making changes, clone the package and install the requirements bu
|
|
|
74
81
|
```bash
|
|
75
82
|
pip install --no-build-isolation --no-deps -e .
|
|
76
83
|
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
Check the files in [examples](mdinterface/examples/) to learn how to use the package.
|
|
@@ -12,6 +12,10 @@ mdinterface.egg-info/SOURCES.txt
|
|
|
12
12
|
mdinterface.egg-info/dependency_links.txt
|
|
13
13
|
mdinterface.egg-info/requires.txt
|
|
14
14
|
mdinterface.egg-info/top_level.txt
|
|
15
|
+
mdinterface/build/__init__.py
|
|
16
|
+
mdinterface/build/box.py
|
|
17
|
+
mdinterface/build/continuum2sim.py
|
|
18
|
+
mdinterface/build/polymer.py
|
|
15
19
|
mdinterface/core/__init__.py
|
|
16
20
|
mdinterface/core/specie.py
|
|
17
21
|
mdinterface/core/topology.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|