-
Notifications
You must be signed in to change notification settings - Fork 1
/
PyNMR.py
2979 lines (2504 loc) · 105 KB
/
PyNMR.py
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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import collections
import functools
import gzip
import os
import os.path as path
import pickle
import re
import shutil
import sys
import tempfile
import tkColorChooser
import tkFileDialog
import Tkinter as Tk
import tkSimpleDialog
import ttk
import urllib2
from collections import (Counter, Iterable, Mapping, MutableMapping,
OrderedDict, Sequence, defaultdict, namedtuple)
from contextlib import contextmanager
from heapq import nsmallest
from itertools import ifilterfalse, izip, product
from math import fsum, sqrt
from operator import itemgetter, mul, sub
from os import remove
from os.path import basename, exists
from sys import stderr, stdout
from pymol import cmd as PymolCmd
from pymol.cgo import CYLINDER
from pymol.cmd import extend
"""Main module declaring the module for pymol
contains interface for command line functions :
load CNS or DYANA distances constraints files
into molecular viewer, display them on the molecule
and show unSatisfied constraints according to a cutOff
with different color (White for not unSatisfied, blue for
lower limit violation, red for upper limit violation for NOEs)
"""
"""lru_cache() implementation for python 2.X
by R. Hettinger (found on ActiveState Recipes)
"""
class RHCounter(dict):
'Mapping where default values are zero'
def __missing__(self, key):
return 0
def lru_cache(maxsize=100):
'''Least-recently-used cache decorator.
Arguments to the cached function must be hashable.
Cache performance statistics stored in f.hits and f.misses.
Clear the cache with f.clear().
http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
'''
maxqueue = maxsize * 10
def decorating_function(user_function, len=len, iter=iter,
tuple=tuple, sorted=sorted, KeyError=KeyError):
cache = {} # mapping of args to results
queue = collections.deque() # order that keys have been used
refcount = RHCounter() # times each key is in the queue
sentinel = object() # marker for looping around the queue
kwd_mark = object() # separate positional and keyword args
# lookup optimizations (ugly but fast)
queue_append, queue_popleft = queue.append, queue.popleft
queue_appendleft, queue_pop = queue.appendleft, queue.pop
@functools.wraps(user_function)
def wrapper(*args, **kwds):
# cache key records both positional and keyword args
key = args
if kwds:
key += (kwd_mark,) + tuple(sorted(kwds.items()))
# record recent use of this key
queue_append(key)
refcount[key] += 1
# get cache entry or compute if not found
try:
result = cache[key]
wrapper.hits += 1
except KeyError:
result = user_function(*args, **kwds)
cache[key] = result
wrapper.misses += 1
# purge least recently used cache entry
if len(cache) > maxsize:
key = queue_popleft()
refcount[key] -= 1
while refcount[key]:
key = queue_popleft()
refcount[key] -= 1
del cache[key], refcount[key]
# periodically compact the queue by eliminating duplicate keys
# while preserving order of most recent access
if len(queue) > maxqueue:
refcount.clear()
queue_appendleft(sentinel)
for key in ifilterfalse(refcount.__contains__,
iter(queue_pop, sentinel)):
queue_appendleft(key)
refcount[key] = 1
return result
def clear():
cache.clear()
queue.clear()
refcount.clear()
wrapper.hits = wrapper.misses = 0
wrapper.hits = wrapper.misses = 0
wrapper.clear = clear
return wrapper
return decorating_function
"""scrolledlist.py: A Tkinter widget combining a Listbox with Scrollbar(s).
For details, see:
http://www.nmt.edu/tcc/help/lang/python/examples/scrolledlist/
"""
DEFAULT_WIDTH = "20"
DEFAULT_HEIGHT = "10"
class ScrolledList(ttk.Frame):
"""A compound widget containing a listbox and up to two scrollbars.
State/invariants:
.listbox: [ The Listbox widget ]
.vScrollbar:
[ if self has a vertical scrollbar ->
that scrollbar
else -> None ]
.hScrollbar:
[ if self has a vertical scrollbar ->
that scrollbar
else -> None ]
.callback: [ as passed to constructor ]
.vscroll: [ as passed to constructor ]
.hscroll: [ as passed to constructor ]
"""
def __init__(self, master=None, width=DEFAULT_WIDTH,
height=DEFAULT_HEIGHT, vscroll=1, hscroll=0, callback=None,
listvariable=None, selectmode=Tk.BROWSE):
"""Constructor for ScrolledList.
"""
# -- 1 --
# [ self := a new Frame widget child of master ]
ttk.Frame.__init__(self, master)
# -- 2 --
self.width = width
self.height = height
self.vscroll = vscroll
self.hscroll = hscroll
self.callback = callback
# -- 3 --
# [ self := self with all widgets created and registered ]
self.__createWidgets(listvariable, selectmode)
def __createWidgets(self, alistvariable, aselectmode):
"""Lay out internal widgets.
"""
# -- 1 --
# [ if self.vscroll ->
# self := self with a vertical Scrollbar widget added
# self.vScrollbar := that widget ]
# else -> I ]
if self.vscroll:
self.vScrollbar = ttk.Scrollbar(self, orient=Tk.VERTICAL)
self.vScrollbar.grid(row=0, column=1, sticky=Tk.N+Tk.S)
# -- 2 --
# [ if self.hscroll ->
# self := self with a horizontal Scrollbar widget added
# self.hScrollbar := that widget
# else -> I ]
if self.hscroll:
self.hScrollbar = ttk.Scrollbar(self, orient=Tk.HORIZONTAL)
self.hScrollbar.grid(row=1, column=0, sticky=Tk.E+Tk.W)
# -- 3 --
# [ self := self with a Listbox widget added
# self.listbox := that widget ]
self.listbox = Tk.Listbox(self, relief=Tk.SUNKEN,
width=self.width, height=self.height,
borderwidth=2, listvariable=alistvariable,
selectmode=aselectmode)
self.listbox.grid(row=0, column=0)
self.listbox.configure(exportselection=False)
# -- 4 --
# [ if self.vscroll ->
# self.listbox := self.listbox linked so that
# self.vScrollbar can reposition it ]
# self.vScrollbar := self.vScrollbar linked so that
# self.listbox can reposition it
# else -> I ]
if self.vscroll:
self.listbox["yscrollcommand"] = self.vScrollbar.set
self.vScrollbar["command"] = self.listbox.yview
# -- 5 --
# [ if self.hscroll ->
# self.listbox := self.listbox linked so that
# self.hScrollbar can reposition it ]
# self.hScrollbar := self.hScrollbar linked so that
# self.listbox can reposition it
# else -> I ]
if self.hscroll:
self.listbox["xscrollcommand"] = self.hScrollbar.set
self.hScrollbar["command"] = self.listbox.xview
# -- 6 --
# [ self.listbox := self.listbox with an event handler
# for button-1 clicks that causes self.callback
# to be called if there is one ]
self.listbox.bind("<Button-1>", self.__clickHandler)
def __clickHandler(self, event):
"""Called when the user clicks on a line in the listbox.
"""
# -- 1 --
if not self.callback:
return
# -- 2 --
# [ call self.callback(c) where c is the line index
# corresponding to event.y ]
lineNo = self.listbox.nearest(event.y)
self.callback(lineNo)
# -- 3 --
self.listbox.focus_set()
def count(self):
"""Return the number of lines in use in the listbox.
"""
return self.listbox.size()
def __getitem__(self, k):
"""Get the (k)th line from the listbox.
"""
# -- 1 --
if 0 <= k < self.count():
return self.listbox.get(k)
else:
raise IndexError("ScrolledList[%d] out of range." % k)
def get(self, index):
"""
"""
return self.listbox.get(index)
def append(self, text):
"""Append a line to the listbox.
"""
self.listbox.insert(Tk.END, text)
def insert(self, linex, text):
"""Insert a line between two existing lines.
"""
# -- 1 --
if 0 <= linex < self.count():
where = linex
else:
where = Tk.END
# -- 2 --
self.listbox.insert(where, text)
def delete(self, linex):
"""Delete a line from the listbox.
"""
if 0 <= linex < self.count():
self.listbox.delete(linex)
def clear(self):
"""Remove all lines.
"""
self.listbox.delete(0, Tk.END)
def curselection(self):
"""
"""
return self.listbox.curselection()
def bind(self, sequence=None, func=None, add=None):
"""
"""
self.listbox.bind(sequence, func, add)
def selection_set(self, first, last=None):
"""
"""
return self.listbox.selection_set(first, last)
def selection_clear(self, first, last=None):
"""
"""
self.listbox.selection_clear(first, last)
def size(self):
"""
"""
return self.listbox.size()
def event_generate(self, event):
"""
"""
self.listbox.event_generate(event)
def selectmode(self, newMode):
"""
"""
self.listbox.configure(selectmode=newMode)
@property
def exportselection(self):
"""Utility method to set constraint name
"""
return self.listbox.exportselection
@exportselection.setter
def exportselection(self, exportSetting):
"""Utility method to set constraint name
"""
self.listbox.exportselection = exportSetting
# From Tom Hennigan
def notify_delegates(method):
"""Decorator to call delegate methods. When decorating a method it will
call `on_before_method_name` and `on_after_method_name`.
Delegate methods are called before and after the actual method is called.
On the after method the return value from the method is passed in as the
`ret_value` keyword arg."""
method_name = method.__name__
# Figure out delegate method names.
before_method = 'on_before_' + method_name
exception_method = 'on_exception_in_' + method_name
after_method = 'on_after_' + method_name
def wrapper(self, *args, **kwargs):
delegates = self.get_delegates_for_method(method_name)
# Call the before methods.
for delegate in delegates:
if hasattr(delegate, before_method):
getattr(delegate, before_method)(*args, **kwargs)
try:
return_value = method(self, *args, **kwargs)
except Exception, e:
kwargs['exception'] = e
for delegate in delegates:
if hasattr(delegate, exception_method):
getattr(delegate, exception_method)(*args, **kwargs)
# Raise the exception.
raise e
# Call the after methods.
kwargs['ret_value'] = return_value
for delegate in delegates:
if hasattr(delegate, after_method):
getattr(delegate, after_method)(*args, **kwargs)
return return_value
return wrapper
class DelegateProviderMixin(object):
"""Mixin for a class that has delegates. Any method can be wrapped for
delegates using the `@notify_delegates` decorator."""
__delegates = []
def add_delegate(self, delegate):
"""Adds a delegate specifically listening on all delegate methods it
can respond to."""
self.__delegates.append((delegate, None))
def add_delegate_for_method(self, delegate, method):
"""Adds a delegate specifically listening on a certain method."""
self.__delegates.append((delegate, [method]))
def add_delegate_for_methods(self, delegate, methods):
"""Adds a delegate specifically listening on certain methods."""
self.__delegates.append((delegate, methods))
def remove_delegate(self, delegate):
"""Removes all hooks for the given delegate on the current object."""
to_remove = []
for index, (delegate_test, _methods) in enumerate(self.__delegates):
if delegate == delegate_test:
to_remove.append(index)
for index in to_remove:
del self.__delegates[index]
def get_delegates_for_method(self, method):
"""Returns all delegates that are subscribed to all methods or to just
the specific method. Delegates are returned in insertion order and only
appear once regardless of how many times they have been added to this
object."""
delegates = []
for delegate, delegate_methods in self.__delegates:
if not delegate_methods or method in delegate_methods:
if not delegate in delegates:
delegates.append(delegate)
return delegates
class NMRApplication(object):
"""
"""
def __init__(self, Core, app="NoGUI"):
"""
"""
self.NMRCommands = Core
self.log = ""
self.NMRCLI = NMRCLI(Core)
if app == "NoGUI":
stdout.write("Starting PyNMR CLI ...\n")
else:
stdout.write("Starting PyNMR GUI ...\n")
self.startGUI()
def startGUI(self):
"""
"""
self.NMRInterface = NMRGUI()
self.NMRInterface.startGUI()
self.GUIBindings()
self.setDefaults()
def setDefaults(self):
"""
"""
self.NMRInterface.preferencesPanel.setDefaults()
self.NMRInterface.mainPanel.constraintPanel.setDefaults()
self.NMRInterface.mainPanel.fileSelection.updateFilelist()
def GUIBindings(self):
"""
"""
self.NMRInterface.mainPanel.fileSelection.NMRCommands = self.NMRCommands
self.NMRInterface.mainPanel.NOEDrawing.NMRCommands = self.NMRCommands
self.NMRInterface.mainPanel.constraintPanel.structureManagement.mainApp = self
self.NMRInterface.preferencesPanel.mainApp = self
regInput = re.compile(r'[^0-9+\-\,\s]')
class NMRCLI(object):
"""
"""
def __init__(self, Core):
"""
"""
self.Core = Core
self.dataControllers = dict()
def showNOE(self, structure, managerName, residuesList, dist_range,
violationState, violCutoff, method, radius, colors,
rangeCutOff, UnSatisfactionMarker, SatisfactionMarker):
"""Command to display NMR restraints as sticks on protein structure with
different parameters : filtering according to distance, restraints display
options
"""
if structure != '':
if managerName == '' and len(self.Core) == 0:
stderr.write("No constraints loaded.\n")
else:
if managerName == '':
managerName = self.Core.keys()[0]
if managerName in self.Core:
dist_range, violationState, residuesList = interpretCLI(dist_range, violationState, residuesList)
self.Core.commandsInterpretation(managerName, residuesList,
dist_range, violationState, violCutoff,
method, rangeCutOff)
self.Core.showSticks(managerName, structure, colors, radius,
UnSatisfactionMarker, SatisfactionMarker)
self.dataControllers[managerName] = NOEDataController(self.Core.drawer.displayedConstraintsSticks.intersection(self.Core.get(managerName, "").constraintsManagerForDataType('NOE')), managerName, structure)
stdout.write(str(len(self.dataControllers[managerName])) +
" constraints used.\n")
stdout.write(str(len([residue for residue in self.dataControllers[managerName].residuesList])) +
" residues involved.\n")
else:
stderr.write("Please check constraints filename.\n")
else:
stderr.write("Please enter a structure name.\n")
def LoadConstraints(self, filename):
"""load NMR distance constraints, call for the correct file format
(CNS/CYANA),
"""
if exists(filename):
self.Core.LoadConstraints(filename)
stdout.write(str(len(self.Core[basename(filename)])) + " constraints loaded.\n")
else:
stderr.write("File : " + filename + " has not been found.\n")
def showNOEDensity(self, structure, managerName, residuesList, dist_range,
violationState, violCutoff, rangeCutOff, method, colors):
"""Command to display NMR restraints as color map on protein structure with
different parameters : filtering according to distance, restraints display
options
"""
if structure != '':
if managerName == '' and len(self.Core) == 0:
stderr.write("No constraints loaded.\n")
else:
if managerName == '':
managerName = self.Core.keys()[0]
try:
dist_range, violationState, residuesList = interpretCLI(dist_range, violationState, residuesList)
self.Core.commandsInterpretation(managerName, residuesList,
dist_range, violationState, violCutoff,
method, rangeCutOff)
self.Core.showNOEDensity(managerName, structure, colors)
self.dataControllers[managerName] = NOEDataController(self.Core.drawer.displayedConstraintsDensity.intersection(self.Core.get(managerName, "").constraintsManagerForDataType('NOE')), managerName, structure)
stdout.write(str(len(self.dataControllers[managerName])) +
" constraints used.\n")
stdout.write(str(len([residue for residue in self.dataControllers[managerName].residuesList])) +
" residues involved.\n")
except ValueError:
stderr.write("Please check constraints filename.\n")
else:
stderr.write("Please enter a structure name.\n")
def loadAndShow(self, filename, structure, residuesList, dist_range,
violationState, violCutoff, method, rangeCutOff,
radius, colors, UnSatisfactionMarker, SatisfactionMarker):
"""Combine two previous defined functions : load and display"""
self.LoadConstraints(filename)
self.showNOE(structure, basename(filename), residuesList, dist_range,
violationState, violCutoff, method, radius, colors,
rangeCutOff, UnSatisfactionMarker, SatisfactionMarker)
def downloadNMR(self, pdbCode, url):
"""
"""
self.Core.downloadFromPDB(pdbCode, url)
def cleanScreen(self, filename):
"""Call the command to clear the screen from all NMR
restraints
"""
if filename in self.Core:
self.Core.cleanScreen(filename)
del self.dataControllers[filename]
def interpretCLI(dist_range, violationState, residuesList):
"""
"""
resList = set()
if not regInput.findall(residuesList):
for resi_range in residuesList.split("+"):
aRange = resi_range.split("-")
if 1 <= len(aRange) <= 2:
resList.update(str(residueNumber) for residueNumber in xrange(int(aRange[0]), int(aRange[-1]) + 1))
else:
stderr.write("Residues set definition error : " + residuesList + "\n")
if dist_range == 'all':
dist_range = ('intra', 'sequential', 'medium', 'long')
else:
dist_range = tuple(dist_range)
if violationState == 'all':
violationState = ('unSatisfied', 'Satisfied')
else:
violationState = tuple(violationState)
return (dist_range, violationState, resList)
def loadConstraintsFromFile(fileName, managerName):
"""Starts constraints loading, uses appropriate function
depending on file type
"""
aManager = imConstraintSetManager(managerName)
with open(fileName, 'r') as file_in:
fileText = file_in.read().upper()
constraintDefinition = BaseConstraintParser.findConstraintType(fileText)
if constraintDefinition in ('XPLOR', 'CNS'):
aManager.format = "CNS"
parser = CNSParser(fileText)
elif constraintDefinition in ('DYANA', 'CYANA'):
aManager.format = "XEASY"
parser = CYANAParser(fileText)
else:
stderr.write("Incorrect or unsupported constraint type : " + constraintDefinition + ".\n")
if parser is not None:
aManager.fileText = fileText
aManager.constraints = tuple(constraint for constraint in parser)
return aManager
class imConstraintSetManager(Sequence):
"""Class to manage an immutable set of constraints
"""
AtTypeReg = re.compile(r'[CHON][A-Z]*')
def __init__(self, managerName):
self.constraints = tuple()
self.name = managerName
def __str__(self):
return "\n".join(str(constraint) for constraint in self)
def __len__(self):
return len(self.constraints)
__repr__ = __str__
def __getitem__(self, constraintIndex):
if 0 <= constraintIndex < len(self.constraints):
self.constraints[constraintIndex].id['number'] = constraintIndex
return self.constraints[constraintIndex]
else:
raise IndexError("No constraint at index " + str(constraintIndex) + ".\n")
# Constraints management methods
def setPDB(self, structure):
"""Sets the name of the structure (usually a PDB File) on which the
distance should be calculated
"""
self.structure = structure
for constraint in self.constraints:
constraint.structureName = self.structure
def associateToPDB(self):
"""read strutural data
"""
if self.structure != '':
setPDB(self.structure)
return len(self.constraints) > 0
return False
def constraintsManagerForDataType(self, dataType):
"""
"""
newManager = imConstraintSetManager(self.name + str(dataType))
newManager.constraints = tuple(constraint for constraint in self.constraints if constraint.type == dataType)
return newManager
def constraintsManagerForAtoms(self, atomDefinitions):
"""
"""
newManager = imConstraintSetManager(self.name + " for atoms " + str(atomDefinitions))
newConstraints = set()
for constraint in self.constraints:
for atom in constraint.atoms:
if atom in atomDefinitions:
newConstraints.add(constraint)
newManager.constraints = tuple(newConstraints)
return newManager
@property
def residuesList(self):
"""
"""
resis = set()
for constraint in self.constraints:
resis.update(constraint.ResiNumbers)
return resis
def intersection(self, anotherManager):
"""
"""
newManager = imConstraintSetManager("")
if isinstance(anotherManager, imConstraintSetManager):
newManager.constraints = tuple(set(self.constraints) & set(anotherManager.constraints))
newManager.name = self.name + anotherManager.name
else:
raise TypeError(str(anotherManager) + " is not a " + str(type(self)) + "\n")
return newManager
@property
def atomsList(self):
"""
"""
atomList = set()
for constraint in self.constraints:
atomList.update(constraint.atoms)
return atomList
def setPartnerAtoms(self, AtomSelection):
"""
"""
self.partnerManager = self.constraintsManagerForAtoms(AtomSelection)
def areAtomsPartner(self, anAtom):
"""
"""
return anAtom in self.partnerManager.atomsList
class ConstraintSetManager(imConstraintSetManager):
"""Class to manage a mutable set of constraints
Usable as an iterator
"""
AtTypeReg = re.compile(r'[CHON][A-Z]*')
def __init__(self, managerName):
super(ConstraintSetManager, self).__init__(managerName)
self.constraints = list()
self.structure = ""
self.format = ""
self.fileText = ""
# Constraints management methods
def removeAllConstraints(self):
"""Empties an array of constraints
"""
del self.constraints[:]
def append(self, aConstraint):
"""Add a constraint to the constraint list of the manager and
update the list of residues
"""
aConstraint.id['number'] = len(self)
self.constraints.append(aConstraint)
if aConstraint.name == "":
aConstraint.name = self.name
def extend(self, constraints):
"""
"""
for constraint in constraints:
self.append(constraint)
def removeConstraint(self, aConstraint):
"""
"""
try:
self.constraints.remove(aConstraint)
except ValueError:
stderr.write("Constraint " + str(aConstraint) +" is unknown\n")
def removeConstraints(self, Constraints):
"""
"""
for Constraint in Constraints:
self.removeConstraint(Constraint)
"""Module for drawing constraints
"""
class ConstraintDrawer(object):
"""
"""
def __init__(self, UnSatisfactionMarker="", SatisfactionMarker=""):
"""
"""
self.UnSatisfactionMarker = UnSatisfactionMarker
self.SatisfactionMarker = SatisfactionMarker
self.displayedConstraintsSticks = ConstraintSetManager("")
self.displayedConstraintsDensity = ConstraintSetManager("")
def drC(self, selectedConstraints, radius, colors):
"""
Draw an array of constraints according to the filter defined by user,
using the drawConstraint function
"""
tempList = list()
for number, aConstraint in enumerate(selectedConstraints):
if len(self.displayedConstraintsSticks) > number:
try:
deleteSelection(self.IDConstraint(aConstraint))
self.displayedConstraintsSticks.removeConstraint(aConstraint)
except ValueError:
pass
if aConstraint.satisfaction() is 'unSatisfied':
color = colors[aConstraint.constraintValues['closeness']]
elif aConstraint.satisfaction() is 'Satisfied':
color = colors['Satisfied']
self.displayedConstraintsSticks.append(aConstraint)
tempList.append(tuple([aConstraint.points, color, radius, self.IDConstraint(aConstraint)]))
# do not merge previous and next loops ! It creates a thread race which severly slows down the display in pymol
for aConstraint in tempList:
drawConstraint(*aConstraint)
return self.displayedConstraintsSticks.atomsList
def constraintsDensity(self, selectedConstraints):
"""Calculate number of constraints per residue for selected constraints
by the filter
"""
densityList = list()
tempList = list()
for number, aConstraint in enumerate(selectedConstraints):
if len(self.displayedConstraintsDensity) > number:
try:
self.displayedConstraintsDensity.removeConstraint(aConstraint)
deleteSelection(self.IDConstraint(aConstraint))
except ValueError:
pass
tempList.append(aConstraint)
# do not merge previous and next loops ! It creates a thread race which severly slows down the display in pymol
for aConstraint in tempList:
densityList.extend(atom._replace(atoms="") for atom in aConstraint.atoms)
self.displayedConstraintsDensity.append(aConstraint)
return densityList
def paD(self, selectedConstraints, structure, color_gradient):
"""Uses b-factors to simulate constraint density on structure
"""
densityList = self.constraintsDensity(selectedConstraints)
bFactors = Counter(densityList)
zeroBFactors(structure)
for atom, density in Counter(atom._replace(atoms="") for atom in densityList).iteritems():
setBfactor(structure, [atom], density)
paintDensity(color_gradient, structure)
return bFactors.keys()
def IDConstraint(self, aConstraint):
"""Returns name of constraints :
Name_(constraint number)_(structureName)_(violation_state)
"""
if aConstraint.satisfaction() is 'Satisfied':
marker = self.SatisfactionMarker
elif aConstraint.satisfaction() is 'unSatisfied':
marker = self.UnSatisfactionMarker
else:
marker = ""
return aConstraint.name + str(aConstraint.id['number']) + marker + aConstraint.structureName
"""Module for error logging
"""
error_messages = list()
def add_error_message(message):
"""
"""
error_messages.append(str(message))
def get_error_messages():
"""
"""
for message in error_messages:
yield message
def erase_all_error_messages():
"""
"""
del error_messages[:]
@contextmanager
def errorLog():
"""
"""
try:
yield None
finally:
sys.stderr.write("\n".join(get_error_messages()) + '\n')
erase_all_error_messages()
def NOEFilter(residuesList, dist_range, violationState, violCutoff, method, RangeCutOff):
"""
"""
calcDistance.method = method
def constraintFilter(aConstraint):
"""
"""
if aConstraint.getRange(RangeCutOff) in dist_range:
if any(str(aResiNumber) in residuesList for aResiNumber in aConstraint.ResiNumbers):
return aConstraint.satisfaction(violCutoff) in violationState
return False
return constraintFilter
def centerOfMass(coords):
""" Adapted from : Andreas Henschel 2006
assumes equal weights for atoms (usually protons)
"""
try:
sumCoords = (fsum(coord) for coord in izip(*coords))
numCoords = len(coords)
return tuple(coord/numCoords for coord in sumCoords)
except ValueError:
return (0, 0, 0)
def calcDistance(*coords):
""" Calculate distance according to :
((sum of all distances^-6)/number of distances)^-1/6
or (sum of all distances^-6)^-1/6
calcDistance.method should be set before use
"""
result = None
try:
distance_list = (sqrt(fsum(sub(*coord) ** 2 for coord in izip(*atoms))) for atoms in product(*coords))
sum6 = fsum(pow(distance, -6) for distance in distance_list)
if calcDistance.method == 'ave6':
number_of_distances = reduce(mul, (len(coord) for coord in coords))
elif calcDistance.method == 'sum6':
number_of_distances = 1
result = pow(sum6/number_of_distances, -1./6)
except(ValueError, TypeError):
add_error_message("Problem using coordinates : " +
str(coords) + "\n" +
" and distances list : " +
str([distance for distance in distance_list]) + "\n")
except AttributeError:
sys.stderr.write("Please set calcDistance.method before using calcDistance()\n")
return result
class NMRCore(MutableMapping):
"""Low Level Interface Class
for loading and displaying constraints
"""
def __init__(self):
self.ManagersList = dict()
self.constraintFilter = None
self.drawer = ConstraintDrawer()
def __getitem__(self, key):
"""Return a constraint Manager
"""
try:
return self.ManagersList[key]
except ValueError:
stderr.write("No constraintManager named " + str(key) + "\n")
def __setitem__(self, key, item):
self.ManagersList[key] = item
def __delitem__(self, key):
try:
del self.ManagersList[key]
except ValueError:
stderr.write("No constraintManager named " + str(key) + "\n")
def __iter__(self):
return self.ManagersList.__iter__()
def __len__(self):
return len(self.ManagersList)
def get(self, key, default=None):
"""
"""
try:
return self.ManagersList[key]
except ValueError:
return default
def keys(self):
"""