packmol-memgen-minimal 1.1.16__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.
- packmol_memgen/__init__.py +2 -0
- packmol_memgen/__version__.py +34 -0
- packmol_memgen/data/LICENSE.Apache-2.0 +201 -0
- packmol_memgen/data/extra_solvents.lib +789 -0
- packmol_memgen/data/frcmod.lipid_ext +97 -0
- packmol_memgen/data/frcmod.solvents +129 -0
- packmol_memgen/data/insane_lipids.txt +138 -0
- packmol_memgen/data/insane_solvents.txt +45 -0
- packmol_memgen/data/leaprc.extra_solvents +42 -0
- packmol_memgen/data/leaprc.lipid_ext +48 -0
- packmol_memgen/data/lipid_ext.lib +12312 -0
- packmol_memgen/data/martini_v3.0.0.itp +356605 -0
- packmol_memgen/data/memgen.parm +4082 -0
- packmol_memgen/data/pdbs.tar.gz +0 -0
- packmol_memgen/data/solvent.parm +14 -0
- packmol_memgen/example/example.sh +31 -0
- packmol_memgen/lib/__init__.py +0 -0
- packmol_memgen/lib/amber.py +77 -0
- packmol_memgen/lib/charmmlipid2amber/__init__.py +0 -0
- packmol_memgen/lib/charmmlipid2amber/charmmlipid2amber.csv +7164 -0
- packmol_memgen/lib/charmmlipid2amber/charmmlipid2amber.py +225 -0
- packmol_memgen/lib/pdbremix/LICENSE +21 -0
- packmol_memgen/lib/pdbremix/__init__.py +0 -0
- packmol_memgen/lib/pdbremix/_version.py +1 -0
- packmol_memgen/lib/pdbremix/amber.py +1103 -0
- packmol_memgen/lib/pdbremix/asa.py +227 -0
- packmol_memgen/lib/pdbremix/data/aminoacid.pdb +334 -0
- packmol_memgen/lib/pdbremix/data/binaries.json +26 -0
- packmol_memgen/lib/pdbremix/data/charmm22.parameter +2250 -0
- packmol_memgen/lib/pdbremix/data/charmm22.topology +1635 -0
- packmol_memgen/lib/pdbremix/data/color_b.py +682 -0
- packmol_memgen/lib/pdbremix/data/hin.lib +130 -0
- packmol_memgen/lib/pdbremix/data/hydroxide.lib +88 -0
- packmol_memgen/lib/pdbremix/data/make_chi.py +92 -0
- packmol_memgen/lib/pdbremix/data/opls.parameter +1108 -0
- packmol_memgen/lib/pdbremix/data/opls.topology +1869 -0
- packmol_memgen/lib/pdbremix/data/phd.frcmod +82 -0
- packmol_memgen/lib/pdbremix/data/phd.leaprc +4 -0
- packmol_memgen/lib/pdbremix/data/phd.prepin +35 -0
- packmol_memgen/lib/pdbremix/data/template.pdb +334 -0
- packmol_memgen/lib/pdbremix/data/znb.frcmod +24 -0
- packmol_memgen/lib/pdbremix/data/znb.leaprc +7 -0
- packmol_memgen/lib/pdbremix/data/znb.lib +69 -0
- packmol_memgen/lib/pdbremix/data.py +264 -0
- packmol_memgen/lib/pdbremix/fetch.py +102 -0
- packmol_memgen/lib/pdbremix/force.py +627 -0
- packmol_memgen/lib/pdbremix/gromacs.py +978 -0
- packmol_memgen/lib/pdbremix/lib/__init__.py +0 -0
- packmol_memgen/lib/pdbremix/lib/docopt.py +579 -0
- packmol_memgen/lib/pdbremix/lib/pyqcprot.py +305 -0
- packmol_memgen/lib/pdbremix/namd.py +1078 -0
- packmol_memgen/lib/pdbremix/pdbatoms.py +543 -0
- packmol_memgen/lib/pdbremix/pdbtext.py +120 -0
- packmol_memgen/lib/pdbremix/protein.py +311 -0
- packmol_memgen/lib/pdbremix/pymol.py +480 -0
- packmol_memgen/lib/pdbremix/rmsd.py +203 -0
- packmol_memgen/lib/pdbremix/simulate.py +420 -0
- packmol_memgen/lib/pdbremix/spacehash.py +73 -0
- packmol_memgen/lib/pdbremix/trajectory.py +286 -0
- packmol_memgen/lib/pdbremix/util.py +273 -0
- packmol_memgen/lib/pdbremix/v3.py +16 -0
- packmol_memgen/lib/pdbremix/v3array.py +482 -0
- packmol_memgen/lib/pdbremix/v3numpy.py +350 -0
- packmol_memgen/lib/pdbremix/volume.py +155 -0
- packmol_memgen/lib/utils.py +1017 -0
- packmol_memgen/main.py +2827 -0
- packmol_memgen_minimal-1.1.16.dist-info/METADATA +664 -0
- packmol_memgen_minimal-1.1.16.dist-info/RECORD +71 -0
- packmol_memgen_minimal-1.1.16.dist-info/WHEEL +4 -0
- packmol_memgen_minimal-1.1.16.dist-info/entry_points.txt +2 -0
- packmol_memgen_minimal-1.1.16.dist-info/licenses/LICENSE +338 -0
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
__doc__ = """
|
|
4
|
+
|
|
5
|
+
Provides the Soup object to manipulate protein structures.
|
|
6
|
+
|
|
7
|
+
The Soup object contains a list of Atom objects, which are also
|
|
8
|
+
grouped into a list of Residues. The Residues provide a
|
|
9
|
+
convenient way to search and access Atoms.
|
|
10
|
+
|
|
11
|
+
Specifically, chain_ids are not used to organize the data
|
|
12
|
+
structures. In the author's experience, for the amount of work to
|
|
13
|
+
maintain chain structure, not much utility is gained. As well,
|
|
14
|
+
chain_id has only loose semantics that are not strictly
|
|
15
|
+
hierchical to residues. By sticking to a Soup as a group of
|
|
16
|
+
residues, the resultant data structure is much cleaner. Chain
|
|
17
|
+
analysis can easily be done on a case-by-case basis.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
from . import v3
|
|
22
|
+
import copy
|
|
23
|
+
import string
|
|
24
|
+
|
|
25
|
+
from . import data
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Atom:
|
|
29
|
+
"""
|
|
30
|
+
This is the basic object to hold Atom information.
|
|
31
|
+
|
|
32
|
+
The attributes are basically those of a PDB atom field.
|
|
33
|
+
However, pos and vel are proper vectors that can be manipulated
|
|
34
|
+
with the v3 3d-vector geometry library.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, pos=None, atom_type="", res_num=None):
|
|
38
|
+
"""
|
|
39
|
+
Normally initialized as an empty container, and filled
|
|
40
|
+
up progressively as fields are read by parsers.
|
|
41
|
+
"""
|
|
42
|
+
self.is_hetatm = False
|
|
43
|
+
self.pos = v3.vector() if pos is None else pos
|
|
44
|
+
self.vel = v3.vector()
|
|
45
|
+
self.mass = 0.0
|
|
46
|
+
self.charge = 0.0
|
|
47
|
+
self.type = ""
|
|
48
|
+
self.element = ""
|
|
49
|
+
self.chain_id = " "
|
|
50
|
+
self.res_type = ""
|
|
51
|
+
self.res_num = ""
|
|
52
|
+
self.res_insert = ""
|
|
53
|
+
self.bfactor = 0.0
|
|
54
|
+
self.occupancy = 0.0
|
|
55
|
+
self.num = 0
|
|
56
|
+
self.alt_conform = " "
|
|
57
|
+
|
|
58
|
+
def copy(self):
|
|
59
|
+
return copy.deepcopy(self)
|
|
60
|
+
|
|
61
|
+
def type_str(self):
|
|
62
|
+
"""
|
|
63
|
+
Format atom_type to write to a PDB file's atom line.
|
|
64
|
+
"""
|
|
65
|
+
atom_type = self.type.strip()
|
|
66
|
+
if len(atom_type) == 1:
|
|
67
|
+
atom_type = " %s " % atom_type
|
|
68
|
+
elif len(atom_type) == 2:
|
|
69
|
+
if atom_type[0].isdigit():
|
|
70
|
+
atom_type = "%s " % atom_type
|
|
71
|
+
else:
|
|
72
|
+
atom_type = " %s " % atom_type
|
|
73
|
+
elif len(atom_type) == 3:
|
|
74
|
+
if atom_type[0].isdigit():
|
|
75
|
+
atom_type = "%s " % atom_type
|
|
76
|
+
else:
|
|
77
|
+
atom_type = " %s" % atom_type
|
|
78
|
+
return atom_type
|
|
79
|
+
|
|
80
|
+
def pdb_str(self):
|
|
81
|
+
"""
|
|
82
|
+
Returns a string for output to an PDB file.
|
|
83
|
+
"""
|
|
84
|
+
if self.is_hetatm:
|
|
85
|
+
field = "HETATM"
|
|
86
|
+
else:
|
|
87
|
+
field = "ATOM "
|
|
88
|
+
x, y, z = self.pos
|
|
89
|
+
s = "%6s%5s %4s %-4s%1s%4s%1s %8.3f%8.3f%8.3f%6.2f%6.2f" % \
|
|
90
|
+
(field,
|
|
91
|
+
str(self.num)[-5:],
|
|
92
|
+
self.type_str(),
|
|
93
|
+
self.res_type,
|
|
94
|
+
self.chain_id,
|
|
95
|
+
str(self.res_num)[-4:],
|
|
96
|
+
self.res_insert,
|
|
97
|
+
x, y, z,
|
|
98
|
+
self.occupancy,
|
|
99
|
+
self.bfactor)
|
|
100
|
+
return s
|
|
101
|
+
|
|
102
|
+
def res_tag(self):
|
|
103
|
+
tag = ""
|
|
104
|
+
if self.chain_id != " " and self.chain_id != "":
|
|
105
|
+
tag += self.chain_id + ":"
|
|
106
|
+
tag += str(self.res_num)
|
|
107
|
+
if self.res_insert:
|
|
108
|
+
tag += self.res_insert
|
|
109
|
+
return tag
|
|
110
|
+
|
|
111
|
+
def __str__(self):
|
|
112
|
+
x, y, z = self.pos
|
|
113
|
+
return "%s:%s-%s" % \
|
|
114
|
+
(self.res_tag(), self.res_type, self.type)
|
|
115
|
+
|
|
116
|
+
def transform(self, matrix):
|
|
117
|
+
"""
|
|
118
|
+
Transforms the pos vector by a v3.transform matrix.
|
|
119
|
+
"""
|
|
120
|
+
new_pos = v3.transform(matrix, self.pos)
|
|
121
|
+
v3.set_vector(self.pos, new_pos)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def AtomFromPdbLine(line):
|
|
125
|
+
"""
|
|
126
|
+
Returns an Atom object from an atom line in a pdb file.
|
|
127
|
+
"""
|
|
128
|
+
atom = Atom()
|
|
129
|
+
if line.startswith('HETATM'):
|
|
130
|
+
atom.is_hetatm = True
|
|
131
|
+
else:
|
|
132
|
+
atom.is_hetatm = False
|
|
133
|
+
atom.num = int(line[6:11])
|
|
134
|
+
atom.type = line[12:16].strip(" ")
|
|
135
|
+
atom.alt_conform = line[16]
|
|
136
|
+
atom.res_type = line[17:21].strip()
|
|
137
|
+
atom.element = data.guess_element(atom.res_type, atom.type)
|
|
138
|
+
atom.chain_id = line[21]
|
|
139
|
+
atom.res_num = int(line[22:26])
|
|
140
|
+
atom.res_insert = line[26]
|
|
141
|
+
if atom.res_insert == " ":
|
|
142
|
+
atom.res_insert = ""
|
|
143
|
+
x = float(line[30:38])
|
|
144
|
+
y = float(line[38:46])
|
|
145
|
+
z = float(line[46:54])
|
|
146
|
+
v3.set_vector(atom.pos, x, y, z)
|
|
147
|
+
try:
|
|
148
|
+
atom.occupancy = float(line[54:60])
|
|
149
|
+
except:
|
|
150
|
+
atom.occupancy = 100.0
|
|
151
|
+
try:
|
|
152
|
+
atom.bfactor = float(line[60:66])
|
|
153
|
+
except:
|
|
154
|
+
atom.bfactor = 0.0
|
|
155
|
+
return atom
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# The following functions is for handling lists of atoms
|
|
159
|
+
|
|
160
|
+
def cmp_atom(a1):
|
|
161
|
+
"""
|
|
162
|
+
Sorting operator for atoms
|
|
163
|
+
"""
|
|
164
|
+
return a1.num
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def add_radii(atoms):
|
|
168
|
+
"""
|
|
169
|
+
Lookup and assign atom.radius for atoms.
|
|
170
|
+
"""
|
|
171
|
+
for atom in atoms:
|
|
172
|
+
if atom.element in data.radii:
|
|
173
|
+
atom.radius = data.radii[atom.element]
|
|
174
|
+
else:
|
|
175
|
+
atom.radius = data.radii['.']
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_center(atoms):
|
|
179
|
+
"""
|
|
180
|
+
Returns the geometric center position vector of atoms.
|
|
181
|
+
"""
|
|
182
|
+
center = v3.vector()
|
|
183
|
+
for atom in atoms:
|
|
184
|
+
center += atom.pos
|
|
185
|
+
result = v3.scale(center, 1.0/float(len(atoms)))
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_width(atoms, center=None):
|
|
190
|
+
"""
|
|
191
|
+
Returns twice the longest distance from the center.
|
|
192
|
+
"""
|
|
193
|
+
max_diff = 0
|
|
194
|
+
if center is None:
|
|
195
|
+
center = get_center(atoms)
|
|
196
|
+
for atom in atoms:
|
|
197
|
+
diff = v3.distance(atom.pos, center)
|
|
198
|
+
if diff > max_diff:
|
|
199
|
+
max_diff = diff
|
|
200
|
+
return 2*max_diff
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def read_pdb(fname):
|
|
204
|
+
"""
|
|
205
|
+
Reads a list of Atoms from a PDB file.
|
|
206
|
+
"""
|
|
207
|
+
atoms = []
|
|
208
|
+
for line in open(fname, 'r'):
|
|
209
|
+
if line.startswith(("ENDMDL", "END")):
|
|
210
|
+
break
|
|
211
|
+
if line.startswith(("ATOM", "HETATM")):
|
|
212
|
+
atoms.append(AtomFromPdbLine(line))
|
|
213
|
+
return atoms
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def write_pdb(atoms, pdb):
|
|
217
|
+
"""
|
|
218
|
+
Writes a list of atoms to a PDB file.
|
|
219
|
+
"""
|
|
220
|
+
with open(pdb, 'w') as f:
|
|
221
|
+
for atom in sorted(atoms, key=cmp_atom):
|
|
222
|
+
f.write(atom.pdb_str() + '\n')
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# Introducing the Residue structure for organizing atoms
|
|
226
|
+
|
|
227
|
+
def split_tag(tag):
|
|
228
|
+
"""
|
|
229
|
+
Returns (chain_id, res_num, insert). Empty chain_id=" " and
|
|
230
|
+
empty insert="".
|
|
231
|
+
"""
|
|
232
|
+
words = tag.split(":")
|
|
233
|
+
if len(words) > 2:
|
|
234
|
+
raise Exception("Too many : in res tag %s" % tag)
|
|
235
|
+
res_num = words[-1]
|
|
236
|
+
insert = ""
|
|
237
|
+
while not res_num[-1].isdigit():
|
|
238
|
+
insert += res_num[-1]
|
|
239
|
+
res_num = res_num[:-1]
|
|
240
|
+
res_num = int(res_num)
|
|
241
|
+
if len(words) == 1:
|
|
242
|
+
chain_id = " "
|
|
243
|
+
else:
|
|
244
|
+
chain_id = words[0]
|
|
245
|
+
if len(chain_id) > 1:
|
|
246
|
+
raise Exception("chain_id in res tag %s too long" % tag)
|
|
247
|
+
return (chain_id, res_num, insert)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class Residue:
|
|
251
|
+
"""
|
|
252
|
+
Class to collect atoms in a residue together. Allows group
|
|
253
|
+
searching where each atom in a residue must have a unique
|
|
254
|
+
atom_type.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
def __init__(self, in_type, in_chain_id, in_num, in_insert=''):
|
|
258
|
+
self.type = in_type
|
|
259
|
+
self.chain_id = in_chain_id
|
|
260
|
+
self.num = in_num
|
|
261
|
+
self.insert = in_insert
|
|
262
|
+
self._atom_dict = {}
|
|
263
|
+
|
|
264
|
+
def tag(self):
|
|
265
|
+
"""
|
|
266
|
+
Returns a name e.g. "A:12" that combines the chain_id and
|
|
267
|
+
residue number. This is a unique tag that can be used to
|
|
268
|
+
identify a residue in a Soup through get_i_residue().
|
|
269
|
+
"""
|
|
270
|
+
tag = ""
|
|
271
|
+
if self.chain_id != " " and self.chain_id != "":
|
|
272
|
+
tag += self.chain_id + ":"
|
|
273
|
+
tag += str(self.num)
|
|
274
|
+
if self.insert:
|
|
275
|
+
tag += self.insert
|
|
276
|
+
return tag
|
|
277
|
+
|
|
278
|
+
def __str__(self):
|
|
279
|
+
atom_name_list = [a.type for a in self.atoms()]
|
|
280
|
+
atom_name = " ".join(atom_name_list)
|
|
281
|
+
return "%s-%s { %s }" % (self.type, self.num, atom_name)
|
|
282
|
+
|
|
283
|
+
def copy(self):
|
|
284
|
+
return copy.deepcopy(self)
|
|
285
|
+
|
|
286
|
+
def n_atom(self):
|
|
287
|
+
return len(self._atom_dict)
|
|
288
|
+
|
|
289
|
+
def atom(self, atom_type):
|
|
290
|
+
return self._atom_dict[atom_type]
|
|
291
|
+
|
|
292
|
+
def has_atom(self, atom_type):
|
|
293
|
+
return atom_type in list(self._atom_dict.keys())
|
|
294
|
+
|
|
295
|
+
def change_atom_type(self, atom_type1, atom_type2):
|
|
296
|
+
if not self.has_atom(atom_type1):
|
|
297
|
+
return
|
|
298
|
+
atom = self._atom_dict[atom_type1]
|
|
299
|
+
atom.type = atom_type2
|
|
300
|
+
del self._atom_dict[atom_type1]
|
|
301
|
+
self._atom_dict[atom_type2] = atom
|
|
302
|
+
|
|
303
|
+
def atoms(self):
|
|
304
|
+
return list(self._atom_dict.values())
|
|
305
|
+
|
|
306
|
+
def atom_name(self, atom_type):
|
|
307
|
+
return self.type + self.num + ":" + atom_type
|
|
308
|
+
|
|
309
|
+
def insert_atom(self, atom):
|
|
310
|
+
self._atom_dict[atom.type] = atom
|
|
311
|
+
atom.chain_id = self.chain_id
|
|
312
|
+
atom.res_num = self.num
|
|
313
|
+
atom.res_type = self.type
|
|
314
|
+
|
|
315
|
+
def erase_atom(self, atom_type):
|
|
316
|
+
del self._atom_dict[atom_type]
|
|
317
|
+
|
|
318
|
+
def set_num(self, i, insert=""):
|
|
319
|
+
self.num = i
|
|
320
|
+
self.insert = insert
|
|
321
|
+
for atom in self.atoms():
|
|
322
|
+
atom.res_num = self.num
|
|
323
|
+
atom.res_insert = insert
|
|
324
|
+
|
|
325
|
+
def inc_num(self):
|
|
326
|
+
self.set_num(self.num+1, self.insert)
|
|
327
|
+
|
|
328
|
+
def dec_num(self):
|
|
329
|
+
self.set_num(self.num-1, self.insert)
|
|
330
|
+
|
|
331
|
+
def dec_insert(self):
|
|
332
|
+
l = self.insert;
|
|
333
|
+
if l == "A" or l == "a":
|
|
334
|
+
self.insert = ''
|
|
335
|
+
else:
|
|
336
|
+
i = string.ascii_letters.find(l)
|
|
337
|
+
self.insert = string.ascii_letters[i-1]
|
|
338
|
+
|
|
339
|
+
def transform(self, matrix):
|
|
340
|
+
for atom in self.atoms():
|
|
341
|
+
atom.transform(matrix)
|
|
342
|
+
|
|
343
|
+
def set_chain_id(self, chain_id):
|
|
344
|
+
self.chain_id = chain_id
|
|
345
|
+
for a in self.atoms():
|
|
346
|
+
a.chain_id = chain_id
|
|
347
|
+
|
|
348
|
+
def set_type(self, res_type):
|
|
349
|
+
self.type = res_type
|
|
350
|
+
for a in self.atoms():
|
|
351
|
+
a.res_type = res_type
|
|
352
|
+
|
|
353
|
+
def load_bfactor(self, bfactor):
|
|
354
|
+
for atom in self.atoms():
|
|
355
|
+
atom.bfactor = bfactor
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class Soup():
|
|
359
|
+
"""
|
|
360
|
+
The major class that holds a list of atoms and references them
|
|
361
|
+
to a list of residues.
|
|
362
|
+
|
|
363
|
+
The methods residues() and atoms() provide access to the
|
|
364
|
+
data structures.
|
|
365
|
+
|
|
366
|
+
Inserting of residues should be done here, as Soup will
|
|
367
|
+
administer both atom and residue lists.
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
def __init__(self, fname=""):
|
|
371
|
+
self._residues = []
|
|
372
|
+
self._atoms = []
|
|
373
|
+
if fname:
|
|
374
|
+
self.read_pdb(fname)
|
|
375
|
+
|
|
376
|
+
def clear(self):
|
|
377
|
+
del self._residues[:]
|
|
378
|
+
for atom in self._atoms:
|
|
379
|
+
del atom
|
|
380
|
+
del self._atoms[:]
|
|
381
|
+
|
|
382
|
+
def copy(self):
|
|
383
|
+
return copy.deepcopy(self)
|
|
384
|
+
|
|
385
|
+
def n_atom(self):
|
|
386
|
+
return len(self._atoms)
|
|
387
|
+
|
|
388
|
+
def atoms(self):
|
|
389
|
+
return self._atoms
|
|
390
|
+
|
|
391
|
+
def atom(self, i):
|
|
392
|
+
return _atoms[i]
|
|
393
|
+
|
|
394
|
+
def insert_atom(self, i, atom):
|
|
395
|
+
self._atoms.append(atom)
|
|
396
|
+
self.residue(i).insert_atom(atom)
|
|
397
|
+
|
|
398
|
+
def erase_atom(self, i, atom_type):
|
|
399
|
+
atom = self.residue(i).atom(atom_type)
|
|
400
|
+
self.residue(i).erase_atom(atom_type)
|
|
401
|
+
for _atom in self._atoms:
|
|
402
|
+
if _atom == atom:
|
|
403
|
+
self._atoms.remove(atom)
|
|
404
|
+
del atom
|
|
405
|
+
break
|
|
406
|
+
|
|
407
|
+
def transform(self, matrix):
|
|
408
|
+
for atom in self._atoms:
|
|
409
|
+
atom.transform(matrix)
|
|
410
|
+
|
|
411
|
+
def residues(self):
|
|
412
|
+
return self._residues
|
|
413
|
+
|
|
414
|
+
def residue(self, i):
|
|
415
|
+
return self._residues[i]
|
|
416
|
+
|
|
417
|
+
def get_i_residue(self, tag):
|
|
418
|
+
"""
|
|
419
|
+
Returns the index of residue with tag, or -1 on failure.
|
|
420
|
+
"""
|
|
421
|
+
for i, residue in enumerate(self.residues()):
|
|
422
|
+
if split_tag(tag) == (residue.chain_id, residue.num, residue.insert):
|
|
423
|
+
return i
|
|
424
|
+
raise -1
|
|
425
|
+
|
|
426
|
+
def residue_by_tag(self, tag):
|
|
427
|
+
i = self.get_i_residue(tag)
|
|
428
|
+
if i >= 0:
|
|
429
|
+
return self.residue(i)
|
|
430
|
+
else:
|
|
431
|
+
raise None
|
|
432
|
+
|
|
433
|
+
def n_residue(self):
|
|
434
|
+
return len(self._residues)
|
|
435
|
+
|
|
436
|
+
def insert_residue(self, i, res):
|
|
437
|
+
is_insertion = False
|
|
438
|
+
if i < self.n_residue()-1:
|
|
439
|
+
save_res_num = self.residue(i).num
|
|
440
|
+
if self.residue(i+1).num == save_res_num:
|
|
441
|
+
is_insertion = True
|
|
442
|
+
|
|
443
|
+
if self.n_residue() == 0:
|
|
444
|
+
res.set_num(res.num, res.insert)
|
|
445
|
+
elif i < self.n_residue():
|
|
446
|
+
res.set_num(self.residue(i).num, self.residue(i).insert)
|
|
447
|
+
else:
|
|
448
|
+
res.set_num(self.residue(i-1).num, "")
|
|
449
|
+
res.inc_num()
|
|
450
|
+
|
|
451
|
+
self._residues.insert(i, res)
|
|
452
|
+
for atom in res.atoms():
|
|
453
|
+
self.insert_atom(i, atom)
|
|
454
|
+
|
|
455
|
+
for j in range(i+1, self.n_residue()):
|
|
456
|
+
self.residue(j).inc_num()
|
|
457
|
+
|
|
458
|
+
if is_insertion:
|
|
459
|
+
while self.residue(i+1).insert:
|
|
460
|
+
for j in range(i+1, self.n_residue()):
|
|
461
|
+
if self.residue(j).res_num == save_res_num:
|
|
462
|
+
self.residue(k).dec_insert()
|
|
463
|
+
|
|
464
|
+
def append_residue(self, res):
|
|
465
|
+
self._residues.append(res)
|
|
466
|
+
for atom in res.atoms():
|
|
467
|
+
self.insert_atom(self.n_residue()-1, atom)
|
|
468
|
+
|
|
469
|
+
def erase_residue(self, i):
|
|
470
|
+
save_res_num = self.residue(i).num
|
|
471
|
+
|
|
472
|
+
for atom in self.residue(i).atoms():
|
|
473
|
+
self._atoms.remove(atom)
|
|
474
|
+
del atom
|
|
475
|
+
self._residues.pop(i)
|
|
476
|
+
|
|
477
|
+
if i < self.n_residue():
|
|
478
|
+
if self.residue(i).num == save_res_num:
|
|
479
|
+
# erasing residue in an insertion
|
|
480
|
+
for j in range(i, self.n_residue()):
|
|
481
|
+
if self.residue(j).num == erase_res_num_int:
|
|
482
|
+
self.residue(j).dec_insert()
|
|
483
|
+
else:
|
|
484
|
+
for j in range(i, self.n_residue()):
|
|
485
|
+
self.residue(j).dec_num()
|
|
486
|
+
|
|
487
|
+
def extract_soup(self, i, j):
|
|
488
|
+
extract = Soup()
|
|
489
|
+
for res in self.residues()[i:j]:
|
|
490
|
+
extract.append_residue(res.copy())
|
|
491
|
+
return extract
|
|
492
|
+
|
|
493
|
+
def insert_soup(self, i, insert):
|
|
494
|
+
for res in reversed(insert.residues()):
|
|
495
|
+
self.insert_residue(i, res.copy())
|
|
496
|
+
|
|
497
|
+
def chain_ids(self):
|
|
498
|
+
chain_id = [r.chain_id for r in self.residues()]
|
|
499
|
+
return list(set(chain_id))
|
|
500
|
+
|
|
501
|
+
def extract_chain(self, chain_id):
|
|
502
|
+
extract = Soup()
|
|
503
|
+
for res in self.residues():
|
|
504
|
+
if res.chain_id == chain_id:
|
|
505
|
+
extract.append_residue(res.copy())
|
|
506
|
+
return extract
|
|
507
|
+
|
|
508
|
+
def load_residue_bfactors(self, res_bfactors):
|
|
509
|
+
for r, b in zip(self.residues(), res_bfactors):
|
|
510
|
+
r.load_bfactor(b)
|
|
511
|
+
|
|
512
|
+
def __str__(self):
|
|
513
|
+
res_name_list = [str(res) for res in self._residues]
|
|
514
|
+
return "\n".join(res_name_list)
|
|
515
|
+
|
|
516
|
+
def read_pdb(self, fname):
|
|
517
|
+
self.clear()
|
|
518
|
+
res_num = -1
|
|
519
|
+
res_insert = " "
|
|
520
|
+
for line in open(fname, 'r').readlines():
|
|
521
|
+
if line.startswith("ATOM") or line.startswith("HETATM"):
|
|
522
|
+
atom = AtomFromPdbLine(line);
|
|
523
|
+
if (res_num != atom.res_num) or \
|
|
524
|
+
(res_insert != atom.res_insert):
|
|
525
|
+
residue = Residue(
|
|
526
|
+
atom.res_type, atom.chain_id,
|
|
527
|
+
atom.res_num, atom.res_insert)
|
|
528
|
+
self.append_residue(residue)
|
|
529
|
+
res_num = atom.res_num
|
|
530
|
+
res_insert = atom.res_insert
|
|
531
|
+
self.insert_atom(-1, atom)
|
|
532
|
+
if line.startswith(("END", "ENDMDL")):
|
|
533
|
+
return
|
|
534
|
+
|
|
535
|
+
def write_pdb(self, pdb):
|
|
536
|
+
f = open(pdb, 'w')
|
|
537
|
+
n_atom = 0
|
|
538
|
+
for res in self.residues():
|
|
539
|
+
res_atoms = res.atoms()
|
|
540
|
+
res_atoms.sort(key=cmp_atom)
|
|
541
|
+
for atom in res_atoms:
|
|
542
|
+
f.write(atom.pdb_str() + '\n')
|
|
543
|
+
f.close()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
__doc__ = """
|
|
4
|
+
|
|
5
|
+
Text manipulation of PDB protein structure files.
|
|
6
|
+
|
|
7
|
+
This is a utility function providing common operations
|
|
8
|
+
applied to PDB files that can easily be done with text
|
|
9
|
+
processing.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from . import data
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def strip_lines(pdb_txt, tag_func):
|
|
17
|
+
new_lines = []
|
|
18
|
+
for line in pdb_txt.splitlines():
|
|
19
|
+
if tag_func(line):
|
|
20
|
+
continue
|
|
21
|
+
new_lines.append(line)
|
|
22
|
+
return '\n'.join(new_lines)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def strip_hydrogens(pdb_txt):
|
|
26
|
+
|
|
27
|
+
def strip_space_and_digits(s):
|
|
28
|
+
result = ""
|
|
29
|
+
for c in s:
|
|
30
|
+
if not (c.isdigit() or c is " "):
|
|
31
|
+
result += c
|
|
32
|
+
return result
|
|
33
|
+
|
|
34
|
+
new_lines = []
|
|
35
|
+
for line in pdb_txt.splitlines():
|
|
36
|
+
if line.startswith("ATOM"):
|
|
37
|
+
raw_atom_type = line[12:16]
|
|
38
|
+
element = strip_space_and_digits(raw_atom_type)[0]
|
|
39
|
+
if element == "H":
|
|
40
|
+
continue
|
|
41
|
+
new_lines.append(line)
|
|
42
|
+
return '\n'.join(new_lines)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def strip_solvent(pdb_txt):
|
|
46
|
+
new_lines = []
|
|
47
|
+
for line in pdb_txt.splitlines():
|
|
48
|
+
res_type = line[17:20].strip().upper()
|
|
49
|
+
if not res_type in data.solvent_res_types:
|
|
50
|
+
new_lines.append(line)
|
|
51
|
+
return '\n'.join(new_lines)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def renumber_residues(pdb_txt):
|
|
55
|
+
get_res_tag = lambda line: line[17:27]
|
|
56
|
+
|
|
57
|
+
sorted_res_tags = []
|
|
58
|
+
lines = pdb_txt.splitlines()
|
|
59
|
+
for line in lines:
|
|
60
|
+
if line.startswith('ATOM') or line.startswith('HETATM'):
|
|
61
|
+
tag = get_res_tag(line)
|
|
62
|
+
if tag not in sorted_res_tags:
|
|
63
|
+
sorted_res_tags.append(tag)
|
|
64
|
+
|
|
65
|
+
res_tag_to_new_resnum = {}
|
|
66
|
+
for i, tag in enumerate(sorted_res_tags):
|
|
67
|
+
res_tag_to_new_resnum[tag] = "%4d" % (i+1)
|
|
68
|
+
|
|
69
|
+
new_lines = []
|
|
70
|
+
for line in lines:
|
|
71
|
+
new_line = line
|
|
72
|
+
for start_tag in ['ATOM', 'ANISOU', 'HETATM']:
|
|
73
|
+
if line.startswith(start_tag):
|
|
74
|
+
tag = get_res_tag(line)
|
|
75
|
+
resnum = res_tag_to_new_resnum[tag]
|
|
76
|
+
resnum = '%4d ' % (int(resnum) % 10000)
|
|
77
|
+
new_line = line[:22] + resnum + line[27:]
|
|
78
|
+
new_lines.append(new_line)
|
|
79
|
+
txt = ''.join(l + '\n' for l in new_lines)
|
|
80
|
+
return txt
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def strip_other_nmr_models(pdb_txt):
|
|
84
|
+
new_lines = []
|
|
85
|
+
for line in pdb_txt.splitlines():
|
|
86
|
+
new_lines.append(line)
|
|
87
|
+
if line.startswith("ENDMDL"):
|
|
88
|
+
break
|
|
89
|
+
return '\n'.join(new_lines)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def strip_alternative_atoms(pdb_txt):
|
|
93
|
+
new_lines = []
|
|
94
|
+
for line in pdb_txt.splitlines():
|
|
95
|
+
new_line = line
|
|
96
|
+
if line.startswith('ATOM'):
|
|
97
|
+
alt_loc = line[16]
|
|
98
|
+
if not alt_loc in [' ']:
|
|
99
|
+
if alt_loc in ['A', 'a']:
|
|
100
|
+
new_line = line[:16] + ' ' + line[17:]
|
|
101
|
+
else:
|
|
102
|
+
continue
|
|
103
|
+
new_lines.append(new_line)
|
|
104
|
+
return '\n'.join(new_lines)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def clean_pdb(in_pdb, out_pdb):
|
|
108
|
+
txt = open(in_pdb, 'r').read()
|
|
109
|
+
txt = strip_other_nmr_models(txt)
|
|
110
|
+
txt = strip_lines(txt, lambda l: l.startswith('HETATM'))
|
|
111
|
+
txt = strip_lines(txt, lambda l: l.startswith('ANISOU'))
|
|
112
|
+
txt = strip_lines(txt, lambda l: l.startswith('CONECT'))
|
|
113
|
+
txt = strip_lines(txt, lambda l: l.startswith('MASTER'))
|
|
114
|
+
txt = strip_solvent(txt)
|
|
115
|
+
txt = strip_alternative_atoms(txt)
|
|
116
|
+
txt = strip_hydrogens(txt)
|
|
117
|
+
txt = renumber_residues(txt)
|
|
118
|
+
open(out_pdb, 'w').write(txt)
|
|
119
|
+
|
|
120
|
+
|