-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathtopobonds.tcl
288 lines (252 loc) · 8.44 KB
/
topobonds.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#!/usr/bin/tclsh
# This file is part of TopoTools, a VMD package to simplify
# manipulating bonds other topology related properties.
#
# Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 by Axel Kohlmeyer <akohlmey@gmail.com>
# $Id: topobonds.tcl,v 1.16 2020/05/29 19:47:40 johns Exp $
# Return info about bonds.
# we list and count only bonds that are entirely within the selection.
proc ::TopoTools::bondinfo {infotype sel {flag none}} {
set numbonds 0
set bidxlist {}
array set bondtypes {}
set aidxlist [$sel list]
set bondlist [$sel getbonds]
set btyplist [$sel getbondtypes]
set bordlist [$sel getbondorders]
foreach a $aidxlist bl $bondlist tl $btyplist ol $bordlist {
foreach b $bl t $tl o $ol {
if {($a < $b) && ([lsearch -sorted -integer $aidxlist $b] != -1)} {
incr numbonds
switch $flag {
type {lappend bidxlist [list $a $b $t]}
order {lappend bidxlist [list $a $b $o]}
both {lappend bidxlist [list $a $b $t $o]}
lammps {lappend bidxlist [list $numbonds $a $b $t]}
none {lappend bidxlist [list $a $b]}
}
}
set bondtypes($t) 1
}
}
switch $infotype {
numbonds { return $numbonds }
numbondtypes { return [array size bondtypes] }
bondtypenames { return [lsort -ascii [array names bondtypes]] }
getbondlist { return $bidxlist }
default { return "bug? shoot the programmer!"}
}
}
# delete all contained bonds of the selection.
proc ::TopoTools::clearbonds {sel} {
# special optimization for "all" selection.
if {[string equal "all" [$sel text]]} {
set nulllist {}
for {set i 0} {$i < [$sel num]} {incr i} {
lappend nullist {}
}
$sel setbonds $nullist
return
}
set mol [$sel molid]
foreach b [bondinfo getbondlist $sel none] {
delbond $mol [lindex $b 0] [lindex $b 1]
}
}
# guess bonds from atom radii. Interface to "mol bondsrecalc".
# XXX: currently only works for selection "all".
proc ::TopoTools::guessbonds {sel} {
set mol [$sel molid]
# special optimization for "all" selection.
if {[string equal "all" [$sel text]]} {
# Use VMD's built-in bond determination heuristic to guess the bonds
mol bondsrecalc $mol
# Mark the bonds as "validated" so VMD will write
# them out when the structure gets written out,
# e.g. to a PSF file, even if no other bond editing was done.
mol dataflag $mol set bonds
return
} else {
vmdcon -err "topo guessbonds: this feature currently only works with an 'all' selection"
return
}
}
# reset bonds to data in bondlist
proc ::TopoTools::setbondlist {sel flag bondlist} {
clearbonds $sel
set nbnd [llength $bondlist]
if {$nbnd == 0} { return 0}
# set defaults
set n 0
set t unknown
set o 1
set mol [$sel molid]
set a -1
set b -1
set fract [expr {100.0/$nbnd}]
set deltat 2000
set newt $deltat
# special optimization for "all" selection.
if {[string equal "all" [$sel text]]} {
set nulllist {}
for {set i 0} {$i < [$sel num]} {incr i} {
set blist($i) $nulllist
set olist($i) $nulllist
set tlist($i) $nulllist
}
foreach bond $bondlist {
switch $flag {
type {lassign $bond a b t }
order {lassign $bond a b o }
both {lassign $bond a b t o}
lammps {lassign $bond n a b t}
none {lassign $bond a b }
}
lappend blist($a) $b
lappend blist($b) $a
lappend olist($a) $o
lappend olist($b) $o
lappend tlist($a) $t
lappend tlist($b) $t
}
set dlist {}
for {set i 0} {$i < [$sel num]} {incr i} {
lappend dlist $blist($i)
}
$sel setbonds $dlist
set dlist {}
for {set i 0} {$i < [$sel num]} {incr i} {
lappend dlist $olist($i)
}
$sel setbondorders $dlist
set dlist {}
for {set i 0} {$i < [$sel num]} {incr i} {
lappend dlist $tlist($i)
}
$sel setbondtypes $dlist
return 0
}
# XXX: fixme!
# using addbond is very inefficient with a large number of bonds
# that are being added. it is better to fill the corresponding
# bondlists directly. the code above should be better, but uses
# much more memory and needs to be generalized.
# XXX: add sanity check on data format
set i 0
foreach bond $bondlist {
incr i
set time [clock clicks -milliseconds]
if {$time > $newt} {
set percent [format "%3.1f" [expr {$i*$fract}]]
vmdcon -info "setbondlist: $percent% done."
display update ui
set newt [expr {$time + $deltat}]
}
switch $flag {
type {lassign $bond a b t }
order {lassign $bond a b o }
both {lassign $bond a b t o}
lammps {lassign $bond n a b t}
none {lassign $bond a b }
}
addbond $mol $a $b $t $o
}
return 0
}
# guess bonds type names from atom types.
proc ::TopoTools::retypebonds {sel} {
set bondlist [bondinfo getbondlist $sel none]
set atomtypes [$sel get type]
set atomindex [$sel list]
set newbonds {}
foreach bond $bondlist {
set idx [lsearch -sorted -integer $atomindex [lindex $bond 0]]
set a [lindex $atomtypes $idx]
set idx [lsearch -sorted -integer $atomindex [lindex $bond 1]]
set b [lindex $atomtypes $idx]
if { [string compare $a $b] > 0 } { set t $a; set a $b; set b $t }
set type [join [list $a $b] "-"]
lappend newbonds [list [lindex $bond 0] [lindex $bond 1] $type]
}
setbondlist $sel type $newbonds
}
# define a new bond or change an existing one.
proc ::TopoTools::addbond {mol id1 id2 type order} {
if {$id1 == $id2} {
vmdcon -err "topo addbond: invalid atom indices: $id1 $id2"
return
}
if {[catch {atomselect $mol "index $id1 $id2"} sel]} {
vmdcon -err "topo addbond: Invalid atom indices: $sel"
return
}
# make sure we have consistent indexing
lassign [$sel list] id1 id2
set bonds [$sel getbonds]
set bords [$sel getbondorders]
set btype [$sel getbondtypes]
set b1 [lindex $bonds 0]
set b2 [lindex $bonds 1]
set bo1 [lindex $bords 0]
set bo2 [lindex $bords 1]
set bt1 [lindex $btype 0]
set bt2 [lindex $btype 1]
# handle the first atom...
set pos [lsearch -exact -integer $b1 $id2]
if { $pos < 0} {
lappend b1 $id2
lappend bo1 $order
lappend bt1 $type
} else {
set bo1 [lreplace $bo1 $pos $pos $order]
set bt1 [lreplace $bt1 $pos $pos $type]
}
# ...and the second one.
set pos [lsearch -exact -integer $b2 $id1]
if { $pos < 0} {
lappend b2 $id1
lappend bo2 $order
lappend bt2 $type
} else {
set bo2 [lreplace $bo2 $pos $pos $order]
set bt2 [lreplace $bt2 $pos $pos $type]
}
# and write the modified data back.
$sel setbonds [list $b1 $b2]
if {![string equal $order 1.0]} {
$sel setbondorders [list $bo1 $bo2]
}
if {![string equal $type unknown]} {
$sel setbondtypes [list $bt1 $bt2]
}
$sel delete
}
# delete a bond.
proc ::TopoTools::delbond {mol id1 id2 {type unknown} {order 1.0}} {
if {[catch {atomselect $mol "index $id1 $id2"} sel]} {
vmdcon -err "topology delbond: Invalid atom indices: $sel"
return
}
# make sure we have consistent indexing
lassign [$sel list] id1 id2
set bonds [$sel getbonds]
set b1 [lindex $bonds 0]
set b2 [lindex $bonds 1]
# handle the first atom...
set pos [lsearch -exact -integer $b1 $id2]
if { $pos < 0} {
; # bond is not completely within selection. ignore
} else {
set b1 [lreplace $b1 $pos $pos]
}
# ...and the second one.
set pos [lsearch -exact -integer $b2 $id1]
if { $pos < 0} {
; # bond is not completely within selection. ignore...
} else {
set b2 [lreplace $b2 $pos $pos]
}
# and write the modified data back.
$sel setbonds [list $b1 $b2]
$sel delete
}