mdinterface 1.0.0__py3-none-any.whl
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/__init__.py +10 -0
- mdinterface/core/__init__.py +8 -0
- mdinterface/core/specie.py +507 -0
- mdinterface/core/topology.py +220 -0
- mdinterface/io/__init__.py +8 -0
- mdinterface/io/lammpswriter.py +261 -0
- mdinterface/io/packmol.py +35 -0
- mdinterface/io/read.py +126 -0
- mdinterface/simulationbox.py +621 -0
- mdinterface/utils/__init__.py +8 -0
- mdinterface/utils/auxiliary.py +262 -0
- mdinterface/utils/database.py +111 -0
- mdinterface/utils/map.py +269 -0
- mdinterface-1.0.0.dist-info/LICENSE +201 -0
- mdinterface-1.0.0.dist-info/METADATA +76 -0
- mdinterface-1.0.0.dist-info/RECORD +18 -0
- mdinterface-1.0.0.dist-info/WHEEL +5 -0
- mdinterface-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Fri Apr 19 14:05:39 2024
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import copy
|
|
10
|
+
#%%
|
|
11
|
+
|
|
12
|
+
class Topology(object):
|
|
13
|
+
|
|
14
|
+
def __init__(self, resname=None, formula=None):
|
|
15
|
+
|
|
16
|
+
self._id = None
|
|
17
|
+
self._formula = formula
|
|
18
|
+
self._resname = resname
|
|
19
|
+
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def id(self):
|
|
24
|
+
return self._id
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def formula(self):
|
|
28
|
+
return self._formula
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def resname(self):
|
|
32
|
+
return self._resname
|
|
33
|
+
|
|
34
|
+
def set_id(self, value):
|
|
35
|
+
self._id = value
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
def set_formula(self, value):
|
|
39
|
+
self._formula = value
|
|
40
|
+
|
|
41
|
+
def set_resname(self, value):
|
|
42
|
+
self._resname = value
|
|
43
|
+
|
|
44
|
+
def copy(self):
|
|
45
|
+
return copy.deepcopy(self)
|
|
46
|
+
|
|
47
|
+
#%%
|
|
48
|
+
|
|
49
|
+
class Atom(Topology):
|
|
50
|
+
def __init__(self, symbol, label=None, eps=None, sig=None):
|
|
51
|
+
|
|
52
|
+
super(Atom, self).__init__()
|
|
53
|
+
|
|
54
|
+
self.symbol = symbol
|
|
55
|
+
|
|
56
|
+
self.eps = eps
|
|
57
|
+
self.sig = sig
|
|
58
|
+
|
|
59
|
+
if label is None:
|
|
60
|
+
label = symbol
|
|
61
|
+
|
|
62
|
+
self.set_label(label)
|
|
63
|
+
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
def set_label(self, value):
|
|
67
|
+
self._label = value
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def label(self):
|
|
72
|
+
return self._label
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def extended_label(self):
|
|
76
|
+
return self._label + "_" + self.resname
|
|
77
|
+
|
|
78
|
+
def __repr__(self):
|
|
79
|
+
return f"{self.__class__.__name__}({self.label}, eps={self.eps}, sig={self.sig})"
|
|
80
|
+
|
|
81
|
+
# TODO CHANGE BACK IF DOESN'T WORK
|
|
82
|
+
def __eq__(self, other):
|
|
83
|
+
return self.eps == other.eps and self.sig == other.sig and self.symbol == other.symbol
|
|
84
|
+
|
|
85
|
+
# def __eq__(self, other):
|
|
86
|
+
# return self.eps == other.eps and self.sig == other.sig and self.label == other.label
|
|
87
|
+
|
|
88
|
+
#%%
|
|
89
|
+
|
|
90
|
+
class Bond(Topology):
|
|
91
|
+
def __init__(self, a1, a2, kr=None, r0=None):
|
|
92
|
+
super(Bond, self).__init__()
|
|
93
|
+
self._a1 = a1
|
|
94
|
+
self._a2 = a2
|
|
95
|
+
self.kr = kr
|
|
96
|
+
self.r0 = r0
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def symbols(self):
|
|
101
|
+
return self._a1, self._a2
|
|
102
|
+
|
|
103
|
+
def __repr__(self):
|
|
104
|
+
return f"{self.__class__.__name__}({'-'.join(self.symbols)},kr={self.kr}, r0={self.r0})"
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def elements(self):
|
|
108
|
+
return self._a1.split('_')[0], self._a2.split('_')[0]
|
|
109
|
+
|
|
110
|
+
def __eq__(self, other):
|
|
111
|
+
return (self.kr == other.kr and self.r0 == other.r0 and
|
|
112
|
+
(self.elements == other.elements or self.elements == other.elements[::-1]))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Angle(Topology):
|
|
116
|
+
def __init__(self, a1, a2, a3, kr=None, theta0=None):
|
|
117
|
+
super(Angle, self).__init__()
|
|
118
|
+
self._a1 = a1
|
|
119
|
+
self._a2 = a2
|
|
120
|
+
self._a3 = a3
|
|
121
|
+
self.kr = kr
|
|
122
|
+
self.theta0 = theta0
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def symbols(self):
|
|
127
|
+
return self._a1, self._a2, self._a3
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def elements(self):
|
|
131
|
+
return self._a1.split('_')[0], self._a2.split('_')[0], self._a3.split('_')[0]
|
|
132
|
+
|
|
133
|
+
def __repr__(self):
|
|
134
|
+
return f"{self.__class__.__name__}({'-'.join(self.symbols)}, kr={self.kr}, theta0={self.theta0})"
|
|
135
|
+
|
|
136
|
+
def __eq__(self, other):
|
|
137
|
+
return (self.kr == other.kr and self.theta0 == other.theta0 and
|
|
138
|
+
(self.elements == other.elements or self.elements == other.elements[::-1]))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class Dihedral(Topology):
|
|
142
|
+
def __init__(self, a1, a2, a3, a4, A1=None, A2=None, A3=None, A4=None, A5=None):
|
|
143
|
+
super(Dihedral, self).__init__()
|
|
144
|
+
self._a1 = a1
|
|
145
|
+
self._a2 = a2
|
|
146
|
+
self._a3 = a3
|
|
147
|
+
self._a4 = a4
|
|
148
|
+
self._values = [A1, A2, A3, A4, A5]
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def symbols(self):
|
|
153
|
+
return self._a1, self._a2, self._a3, self._a4
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def values(self):
|
|
157
|
+
return self._values
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def elements(self):
|
|
161
|
+
return self._a1.split('_')[0], self._a2.split('_')[0],\
|
|
162
|
+
self._a3.split('_')[0], self._a4.split('_')[0]
|
|
163
|
+
|
|
164
|
+
def __repr__(self):
|
|
165
|
+
symbols = '-'.join(self.symbols)
|
|
166
|
+
values = '|'.join([str(ii) for ii in self._values])
|
|
167
|
+
return f"{self.__class__.__name__}({symbols}, A={values})"
|
|
168
|
+
|
|
169
|
+
def __eq__(self, other):
|
|
170
|
+
return (self.values == other.values and
|
|
171
|
+
(self.elements == other.elements or self.elements == other.elements[::-1]))
|
|
172
|
+
|
|
173
|
+
def write(self, fout):
|
|
174
|
+
atype = "{}-{}-{}-{}".format(*self.symbols)
|
|
175
|
+
if self._values[-1] is not None:
|
|
176
|
+
value = "{:>7.4f} {:>7.4f} {:>7.4f} {:>7.4f} {:>7.4f}".format(*self.values)
|
|
177
|
+
else:
|
|
178
|
+
value = "{:>7.4f} {:>7.4f} {:>7.4f} {:>7.4f}".format(*self.values[:-1])
|
|
179
|
+
fout.write("{:>5} {} # {:<8} | {}\n".format(self.id, value, atype, self.resname))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class Improper(Topology): # cvff improper style
|
|
183
|
+
def __init__(self, a1, a2="x", a3="x", a4="x", K=None, d=None, n=None):
|
|
184
|
+
super(Improper, self).__init__()
|
|
185
|
+
|
|
186
|
+
self._a1 = a1
|
|
187
|
+
self._a2 = a2
|
|
188
|
+
self._a3 = a3
|
|
189
|
+
self._a4 = a4
|
|
190
|
+
|
|
191
|
+
self._K = K
|
|
192
|
+
|
|
193
|
+
assert d in [1, -1], "Wrong 'd' value"
|
|
194
|
+
self._d = d
|
|
195
|
+
|
|
196
|
+
assert n in [0,1,2,3,4,5,6], "Wrong 'n' value"
|
|
197
|
+
self._n = n
|
|
198
|
+
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def symbols(self):
|
|
203
|
+
return self._a1, self._a2, self._a3, self._a4
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def values(self):
|
|
207
|
+
return self._K, self._d, self._n
|
|
208
|
+
|
|
209
|
+
def __repr__(self):
|
|
210
|
+
symbols = '-'.join(self.symbols)
|
|
211
|
+
values = '|'.join([str(ii) for ii in self.values])
|
|
212
|
+
return f"{self.__class__.__name__}({symbols}, K,d,n={values})"
|
|
213
|
+
|
|
214
|
+
def __eq__(self, other):
|
|
215
|
+
return (self.values == other.values and
|
|
216
|
+
self._a1.split('_')[0] == other._a1.split('_')[0])
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Wed Oct 25 15:03:47 2023
|
|
5
|
+
|
|
6
|
+
@author: roncoroni
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from MDAnalysis.lib import util, mdamath
|
|
12
|
+
from MDAnalysis.core.groups import requires
|
|
13
|
+
from MDAnalysis.exceptions import NoDataError
|
|
14
|
+
from MDAnalysis.coordinates import base
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
btype_sections = {'bond':'Bonds', 'angle':'Angles',
|
|
18
|
+
'dihedral':'Dihedrals', 'improper':'Impropers'}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DATAWriter(base.WriterBase):
|
|
22
|
+
"""Write out the current time step as a LAMMPS DATA file.
|
|
23
|
+
|
|
24
|
+
This writer supports the sections Atoms, Masses, Velocities, Bonds,
|
|
25
|
+
Angles, Dihedrals, and Impropers. This writer will write the header
|
|
26
|
+
and these sections (if applicable). Atoms section is written in the
|
|
27
|
+
"full" sub-style if charges are available or "molecular" sub-style
|
|
28
|
+
if they are not. Molecule id is set to 0 for all atoms.
|
|
29
|
+
|
|
30
|
+
Note
|
|
31
|
+
----
|
|
32
|
+
This writer assumes "conventional" or "real" LAMMPS units where length
|
|
33
|
+
is measured in Angstroms and velocity is measured in Angstroms per
|
|
34
|
+
femtosecond. To write in different units, specify `lengthunit`
|
|
35
|
+
|
|
36
|
+
If atom types are not already positive integers, the user must set them
|
|
37
|
+
to be positive integers, because the writer will not automatically
|
|
38
|
+
assign new types.
|
|
39
|
+
|
|
40
|
+
To preserve numerical atom types when writing a selection, the Masses
|
|
41
|
+
section will have entries for each atom type up to the maximum atom type.
|
|
42
|
+
If the universe does not contain atoms of some type in
|
|
43
|
+
{1, ... max(atom_types)}, then the mass for that type will be set to 1.
|
|
44
|
+
|
|
45
|
+
In order to write bonds, each selected bond type must be explicitly set to
|
|
46
|
+
an integer >= 1.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
format = 'DATA'
|
|
50
|
+
|
|
51
|
+
def __init__(self, filename, convert_units=True, **kwargs):
|
|
52
|
+
"""Set up a DATAWriter
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
filename : str
|
|
57
|
+
output filename
|
|
58
|
+
convert_units : bool, optional
|
|
59
|
+
units are converted to the MDAnalysis base format; [``True``]
|
|
60
|
+
"""
|
|
61
|
+
self.filename = util.filename(filename, ext='data', keep=True)
|
|
62
|
+
|
|
63
|
+
self.convert_units = convert_units
|
|
64
|
+
|
|
65
|
+
self.units = {'time': 'fs', 'length': 'Angstrom'}
|
|
66
|
+
self.units['length'] = kwargs.pop('lengthunit', self.units['length'])
|
|
67
|
+
self.units['time'] = kwargs.pop('timeunit', self.units['time'])
|
|
68
|
+
self.units['velocity'] = kwargs.pop('velocityunit',
|
|
69
|
+
self.units['length']+'/'+self.units['time'])
|
|
70
|
+
|
|
71
|
+
def _write_atoms(self, atoms, data):
|
|
72
|
+
self.f.write('\n')
|
|
73
|
+
self.f.write('Atoms\n')
|
|
74
|
+
self.f.write('\n')
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
charges = atoms.charges
|
|
78
|
+
except (NoDataError, AttributeError):
|
|
79
|
+
has_charges = False
|
|
80
|
+
else:
|
|
81
|
+
has_charges = True
|
|
82
|
+
|
|
83
|
+
indices = atoms.indices + 1
|
|
84
|
+
i_l = len(str(indices.max()))
|
|
85
|
+
|
|
86
|
+
unique_types, types = np.unique(atoms.types, return_inverse=True)
|
|
87
|
+
types += 1
|
|
88
|
+
t_l = len(str(len(unique_types)))
|
|
89
|
+
|
|
90
|
+
moltags = atoms.resindices
|
|
91
|
+
m_l = len(str(moltags.max()))+1
|
|
92
|
+
|
|
93
|
+
if self.convert_units:
|
|
94
|
+
coordinates = self.convert_pos_to_native(atoms.positions, inplace=False)
|
|
95
|
+
|
|
96
|
+
b_l = len(str(int(coordinates.max()))) + 1
|
|
97
|
+
b_t = b_l + 6
|
|
98
|
+
|
|
99
|
+
if has_charges:
|
|
100
|
+
for index, moltag, atype, charge, coords in zip(indices, moltags,
|
|
101
|
+
types, charges, coordinates):
|
|
102
|
+
x, y, z = coords
|
|
103
|
+
self.f.write(f"{index:{i_l}d} {moltag:{m_l}d} {atype:{t_l}d} {charge: .7f}"
|
|
104
|
+
f" {x:> {b_t}.5f} {y:> {b_t}.5f} {z:> {b_t}.5f}\n")
|
|
105
|
+
else:
|
|
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
|
+
|
|
112
|
+
def _write_velocities(self, atoms):
|
|
113
|
+
self.f.write('\n')
|
|
114
|
+
self.f.write('Velocities\n')
|
|
115
|
+
self.f.write('\n')
|
|
116
|
+
indices = atoms.indices + 1
|
|
117
|
+
velocities = self.convert_velocities_to_native(atoms.velocities,
|
|
118
|
+
inplace=False)
|
|
119
|
+
for index, vel in zip(indices, velocities):
|
|
120
|
+
self.f.write('{i:d} {x:f} {y:f} {z:f}\n'.format(i=index, x=vel[0],
|
|
121
|
+
y=vel[1], z=vel[2]))
|
|
122
|
+
|
|
123
|
+
def _write_masses(self, atoms):
|
|
124
|
+
# self.f.write('\n')
|
|
125
|
+
self.f.write('Masses\n')
|
|
126
|
+
self.f.write('\n')
|
|
127
|
+
|
|
128
|
+
for cc, atype in enumerate(np.unique(atoms.types)):
|
|
129
|
+
|
|
130
|
+
# search entire universe for mass info, not just writing selection
|
|
131
|
+
masses = set(atoms.select_atoms('type {}'.format(atype)).masses)
|
|
132
|
+
|
|
133
|
+
if len(masses) == 0:
|
|
134
|
+
mass = 1.0
|
|
135
|
+
else:
|
|
136
|
+
mass = masses.pop()
|
|
137
|
+
|
|
138
|
+
if masses:
|
|
139
|
+
raise ValueError('LAMMPS DATAWriter: to write data file, '+
|
|
140
|
+
'atoms with same type must have same mass')
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
self.f.write('{:d} {:> 8.3f} # {}\n'.format(cc+1, mass, atype))
|
|
144
|
+
|
|
145
|
+
def _write_bonds(self, bonds):
|
|
146
|
+
self.f.write('\n')
|
|
147
|
+
self.f.write('{}\n'.format(btype_sections[bonds.btype]))
|
|
148
|
+
self.f.write('\n')
|
|
149
|
+
for bond, i in zip(bonds, range(1, len(bonds)+1)):
|
|
150
|
+
try:
|
|
151
|
+
self.f.write('{:d} {:d} '.format(i, int(bond.type))+\
|
|
152
|
+
' '.join((bond.atoms.indices + 1).astype(str))+'\n')
|
|
153
|
+
except TypeError:
|
|
154
|
+
errmsg = (f"LAMMPS DATAWriter: Trying to write bond, but bond "
|
|
155
|
+
f"type {bond.type} is not numerical.")
|
|
156
|
+
raise TypeError(errmsg) from None
|
|
157
|
+
|
|
158
|
+
def _write_dimensions(self, dimensions):
|
|
159
|
+
"""Convert dimensions to triclinic vectors, convert lengths to native
|
|
160
|
+
units and then write the dimensions section
|
|
161
|
+
"""
|
|
162
|
+
if self.convert_units:
|
|
163
|
+
triv = self.convert_pos_to_native(mdamath.triclinic_vectors(
|
|
164
|
+
dimensions),inplace=False)
|
|
165
|
+
self.f.write('\n')
|
|
166
|
+
self.f.write('{:f} {:f} xlo xhi\n'.format(0., triv[0][0]))
|
|
167
|
+
self.f.write('{:f} {:f} ylo yhi\n'.format(0., triv[1][1]))
|
|
168
|
+
self.f.write('{:f} {:f} zlo zhi\n'.format(0., triv[2][2]))
|
|
169
|
+
if any([triv[1][0], triv[2][0], triv[2][1]]):
|
|
170
|
+
self.f.write('{xy:f} {xz:f} {yz:f} xy xz yz\n'.format(
|
|
171
|
+
xy=triv[1][0], xz=triv[2][0], yz=triv[2][1]))
|
|
172
|
+
self.f.write('\n')
|
|
173
|
+
|
|
174
|
+
@requires('types', 'masses')
|
|
175
|
+
def write(self, selection, frame=None):
|
|
176
|
+
"""Write selection at current trajectory frame to file.
|
|
177
|
+
|
|
178
|
+
The sections for Atoms, Masses, Velocities, Bonds, Angles,
|
|
179
|
+
Dihedrals, and Impropers (if these are defined) are
|
|
180
|
+
written. The Atoms section is written in the "full" sub-style
|
|
181
|
+
if charges are available or "molecular" sub-style if they are
|
|
182
|
+
not. Molecule id in atoms section is set to to 0.
|
|
183
|
+
|
|
184
|
+
No other sections are written to the DATA file.
|
|
185
|
+
As of this writing, other sections are not parsed into the topology
|
|
186
|
+
by the :class:`DATAReader`.
|
|
187
|
+
|
|
188
|
+
Note
|
|
189
|
+
----
|
|
190
|
+
If the selection includes a partial fragment, then only the bonds,
|
|
191
|
+
angles, etc. whose atoms are contained within the selection will be
|
|
192
|
+
included.
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
selection : AtomGroup or Universe
|
|
197
|
+
MDAnalysis AtomGroup (selection or Universe.atoms) or also Universe
|
|
198
|
+
frame : int (optional)
|
|
199
|
+
optionally move to frame number `frame`
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
u = selection.universe
|
|
203
|
+
if frame is not None:
|
|
204
|
+
u.trajectory[frame]
|
|
205
|
+
else:
|
|
206
|
+
frame = u.trajectory.ts.frame
|
|
207
|
+
|
|
208
|
+
# make sure to use atoms (Issue 46)
|
|
209
|
+
atoms = selection.atoms
|
|
210
|
+
|
|
211
|
+
# check that types can be converted to ints if they aren't ints already
|
|
212
|
+
# try:
|
|
213
|
+
# atoms.types.astype(np.int32)
|
|
214
|
+
# except ValueError:
|
|
215
|
+
# errmsg = ("LAMMPS.DATAWriter: atom types must be convertible to "
|
|
216
|
+
# "integers")
|
|
217
|
+
# raise ValueError(errmsg) from None
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
velocities = atoms.velocities
|
|
221
|
+
except (NoDataError, AttributeError):
|
|
222
|
+
has_velocities = False
|
|
223
|
+
else:
|
|
224
|
+
has_velocities = True
|
|
225
|
+
|
|
226
|
+
features = {}
|
|
227
|
+
with util.openany(self.filename, 'wt') as self.f:
|
|
228
|
+
self.f.write('LAMMPS data file via MDAnalysis\n')
|
|
229
|
+
self.f.write('\n')
|
|
230
|
+
self.f.write('{:>12d} atoms\n'.format(len(atoms)))
|
|
231
|
+
|
|
232
|
+
attrs = [('bond', 'bonds'), ('angle', 'angles'),
|
|
233
|
+
('dihedral', 'dihedrals'), ('improper', 'impropers')]
|
|
234
|
+
|
|
235
|
+
for btype, attr_name in attrs:
|
|
236
|
+
features[btype] = atoms.__getattribute__(attr_name)
|
|
237
|
+
self.f.write('{:>12d} {}\n'.format(len(features[btype]),
|
|
238
|
+
attr_name))
|
|
239
|
+
features[btype] = features[btype].atomgroup_intersection(
|
|
240
|
+
atoms, strict=True)
|
|
241
|
+
|
|
242
|
+
self.f.write('\n')
|
|
243
|
+
|
|
244
|
+
ntypes = len(set(atoms.types))
|
|
245
|
+
self.f.write('{:>12d} atom types\n'.format(ntypes))
|
|
246
|
+
|
|
247
|
+
for btype, attr in features.items():
|
|
248
|
+
self.f.write('{:>12d} {} types\n'.format(len(attr.types()),
|
|
249
|
+
btype))
|
|
250
|
+
|
|
251
|
+
self._write_dimensions(atoms.dimensions)
|
|
252
|
+
|
|
253
|
+
self._write_masses(atoms)
|
|
254
|
+
self._write_atoms(atoms, u.trajectory.ts.data)
|
|
255
|
+
for attr in features.values():
|
|
256
|
+
if attr is None or len(attr) == 0:
|
|
257
|
+
continue
|
|
258
|
+
self._write_bonds(attr)
|
|
259
|
+
|
|
260
|
+
if has_velocities:
|
|
261
|
+
self._write_velocities(atoms)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Wed Nov 22 13:57:02 2023
|
|
5
|
+
|
|
6
|
+
@author: roncoroni
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
header = """\
|
|
11
|
+
tolerance 2.0
|
|
12
|
+
output {}
|
|
13
|
+
nloop 100
|
|
14
|
+
seed {}
|
|
15
|
+
|
|
16
|
+
filetype pdb
|
|
17
|
+
# movebadrandom
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
box_place = """
|
|
21
|
+
structure mol_{}.pdb
|
|
22
|
+
number {}
|
|
23
|
+
inside box {}
|
|
24
|
+
resnumbers 2
|
|
25
|
+
end structure
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
fix_place = """
|
|
29
|
+
structure mol_{}.pdb
|
|
30
|
+
number 1
|
|
31
|
+
center
|
|
32
|
+
fixed {} {} {} 0. 0. 0.
|
|
33
|
+
resnumbers 2
|
|
34
|
+
end structure
|
|
35
|
+
"""
|
mdinterface/io/read.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Thu Oct 24 18:33:06 2024
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
import ase
|
|
12
|
+
import ase.visualize
|
|
13
|
+
import ase.io.lammpsdata
|
|
14
|
+
from mdinterface.core.topology import Atom, Bond, Angle, Dihedral, Improper
|
|
15
|
+
|
|
16
|
+
#%%
|
|
17
|
+
|
|
18
|
+
def read_lammps_data_file(filename):
|
|
19
|
+
|
|
20
|
+
system = ase.io.lammpsdata.read_lammps_data(filename)
|
|
21
|
+
system.set_pbc(False)
|
|
22
|
+
system.set_cell(None)
|
|
23
|
+
|
|
24
|
+
symbols = system.get_chemical_symbols()
|
|
25
|
+
# Initialize data containers
|
|
26
|
+
bond_coeff = []
|
|
27
|
+
angle_coeff = []
|
|
28
|
+
dihedral_coeff = []
|
|
29
|
+
improper_coeff = []
|
|
30
|
+
|
|
31
|
+
atoms = []
|
|
32
|
+
bonds = []
|
|
33
|
+
angles = []
|
|
34
|
+
dihedrals = []
|
|
35
|
+
impropers = []
|
|
36
|
+
|
|
37
|
+
# Define a dictionary to map section names to their corresponding data lists
|
|
38
|
+
section_map = {
|
|
39
|
+
'pair coeffs' : 'pair_coeff',
|
|
40
|
+
'bond coeffs' : 'bond_coeff',
|
|
41
|
+
'angle coeffs' : 'angle_coeff',
|
|
42
|
+
'dihedral coeffs' : 'dihedral_coeff',
|
|
43
|
+
'improper coeffs' : 'improper_coeff',
|
|
44
|
+
'atoms' : 'atoms',
|
|
45
|
+
'bonds' : 'bonds',
|
|
46
|
+
'angles' : 'angles',
|
|
47
|
+
'dihedrals' : 'dihedrals',
|
|
48
|
+
'impropers' : 'impropers'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Read the file and process lines
|
|
52
|
+
with open(filename, "r") as file:
|
|
53
|
+
to_read = None
|
|
54
|
+
|
|
55
|
+
for line in file:
|
|
56
|
+
line = line.strip()
|
|
57
|
+
|
|
58
|
+
if not line:
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
section_key = line.lower()
|
|
62
|
+
|
|
63
|
+
if section_key in section_map:
|
|
64
|
+
to_read = section_map[section_key]
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
line = line.split()
|
|
68
|
+
|
|
69
|
+
if to_read == "pair_coeff":
|
|
70
|
+
idx = int(line[0]) - 1
|
|
71
|
+
eps = float(line[1])
|
|
72
|
+
sig = float(line[2])
|
|
73
|
+
symbol = symbols[idx]
|
|
74
|
+
atom = Atom(symbol, f"{symbol}_{str(idx).zfill(3)}", eps, sig)
|
|
75
|
+
atoms.append(atom)
|
|
76
|
+
|
|
77
|
+
elif to_read == "bond_coeff":
|
|
78
|
+
kr = float(line[1])
|
|
79
|
+
r0 = float(line[2])
|
|
80
|
+
bond_coeff.append([kr, r0])
|
|
81
|
+
|
|
82
|
+
elif to_read == "angle_coeff":
|
|
83
|
+
kr = float(line[1])
|
|
84
|
+
theta0 = float(line[2])
|
|
85
|
+
angle_coeff.append([kr, theta0])
|
|
86
|
+
|
|
87
|
+
elif to_read == "dihedral_coeff":
|
|
88
|
+
values = [float(ii) for ii in line[1:]]
|
|
89
|
+
dihedral_coeff.append(values)
|
|
90
|
+
|
|
91
|
+
elif to_read == "improper_coeff":
|
|
92
|
+
values = [float(line[1]), int(line[2]), int(line[3])]
|
|
93
|
+
improper_coeff.append(values)
|
|
94
|
+
|
|
95
|
+
elif to_read == "bonds":
|
|
96
|
+
idx1 = int(line[2]) - 1
|
|
97
|
+
idx2 = int(line[3]) - 1
|
|
98
|
+
kr, r0 = bond_coeff[int(line[1]) - 1]
|
|
99
|
+
bond = Bond(atoms[idx1].label, atoms[idx2].label, kr, r0)
|
|
100
|
+
bonds.append(bond)
|
|
101
|
+
|
|
102
|
+
elif to_read == "angles":
|
|
103
|
+
idx1 = int(line[2]) - 1
|
|
104
|
+
idx2 = int(line[3]) - 1
|
|
105
|
+
idx3 = int(line[4]) - 1
|
|
106
|
+
kr, theta0 = angle_coeff[int(line[1]) - 1]
|
|
107
|
+
angle = Angle(atoms[idx1].label, atoms[idx2].label, atoms[idx3].label, kr, theta0)
|
|
108
|
+
angles.append(angle)
|
|
109
|
+
|
|
110
|
+
elif to_read == "dihedrals":
|
|
111
|
+
idxs = [int(ii) - 1 for ii in line[2:]]
|
|
112
|
+
values = dihedral_coeff[int(line[1]) - 1]
|
|
113
|
+
dihedral = Dihedral(atoms[idxs[0]].label, atoms[idxs[1]].label,
|
|
114
|
+
atoms[idxs[2]].label, atoms[idxs[3]].label, *values)
|
|
115
|
+
dihedrals.append(dihedral)
|
|
116
|
+
|
|
117
|
+
elif to_read == "impropers":
|
|
118
|
+
idxs = [int(ii) - 1 for ii in line[2:]]
|
|
119
|
+
values = improper_coeff[int(line[1]) - 1]
|
|
120
|
+
improper = Improper(atoms[idxs[0]].label, atoms[idxs[1]].label,
|
|
121
|
+
atoms[idxs[2]].label, atoms[idxs[3]].label,
|
|
122
|
+
K=values[0], d=values[1], n=values[2])
|
|
123
|
+
impropers.append(improper)
|
|
124
|
+
|
|
125
|
+
system.new_array("stype", np.array(atoms))
|
|
126
|
+
return system, atoms, bonds, angles, dihedrals, impropers
|