added pymol/UNRESInpGen.py - generates inputs for MD
[unres.git] / source / pymol / UNRESInpGen.py
1 # -*- coding: utf-8 -*-
2 '''
3 -------------------------------------------------------------------------------
4  UNRESInpGen.py - UNRES graphical input generator v 1.0
5 -------------------------------------------------------------------------------
6
7  Written by Dawid JagieÅ‚a (lightnir@chem.univ.gda.pl)  Oct 2012 
8
9 '''
10
11 from pymol import cmd,stored
12 from Tkinter import *
13 import tkFileDialog
14 import tkMessageBox 
15 import Pmw
16 import string
17 import os
18
19 UNRESInpGen_version="1.0"
20
21 def __init__(self):
22         self.UNRESInpGenWindow = None 
23         try:
24                 self.menuBar.addcascademenu('Plugin', 'UNRESPlugins', 'UNRESPACK Plugins', label='UNRES Plugins')
25         except:
26                 pass
27
28         self.menuBar.addmenuitem('UNRESPlugins', 'command','UNRESInpGen',
29                 label='UNRES Input Generator',
30                 command = lambda s=self: createUNRESInpGen(s) )
31
32 def createUNRESInpGen(app):
33         if (app.UNRESInpGenWindow == None):
34                 app.UNRESInpGenWindow = UNRESInpGenerator(app)
35         else:
36                 app.UNRESInpGenWindow.myToggle()        
37
38 #=================================================================
39
40
41 class UNRESInpGenerator(Toplevel):
42         global UNRESInpGen_version
43         # Class variables
44         """
45         cv = IntVar()
46         pp = IntVar()
47         cp = IntVar()
48         rv = IntVar()
49         colorize = IntVar()
50         """
51         seq_letter = IntVar()
52         writeSSbrige = IntVar()
53         OM1Val = StringVar()
54         OM2Val = StringVar()
55         objects_list = ['Select object']    
56         seq_list=[]
57         seq_length=0
58
59         weights = ['WLONG','WSCC','WSCP','WELEC','WVDWPP','WEL_LOC','WCORR4','WCORR5','WCORR6','WTURN3','WTURN4','WTURN6','WSCCOR','WSTRAIN','WBOND','WTOR','WTORD','WANG','WSCLOC','SCAL14','SCALSCP','WCORRH','CUTOFF' ]
60
61         force_fields = [ {'FF':'GAB', 'WLONG' :'1.35279', 'WSCP'   :'1.59304', 'WELEC' :'0.71534', 'WBOND' :'1.00000', 'WANG'  :'1.13873', 'WSCLOC':'0.16258', 'WTOR':'1.98599', 'WTORD':'1.57069', 'WCORRH':'0.42887', 'WCORR5':'0.00000',
62                                         'WCORR6':'0.00000', 'WEL_LOC':'0.16036', 'WTURN3':'1.68722', 'WTURN4':'0.66230', 'WTURN6':'0.00000', 'WVDWPP':'0.11371', 'WHPB':'1.00000', 'WCORR4':'0.00000', 'CUTOFF':'7.00000'},
63                     {'FF':'E0G', 'WLONG':'1.70905', 'WSCP':'2.18310', 'WELEC':'1.06684', 'WBOND':'1.00000', 'WANG':'1.17536', 'WSCLOC':'0.22070', 'WTOR':'2.65798', 'WTORD':'2.00646', 'WCORRH':'0.23541', 'WCORR5':'0.00000',
64                                         'WCORR6':'0.00000', 'WEL_LOC':'0.42789', 'WTURN3':'1.68126', 'WTURN4':'0.75080', 'WTURN6':'0.00000', 'WVDWPP':'0.27044', 'WHPB':'1.00000', 'WSCP14':'0.00000', 'CUTOFF':'7.00000', 'WCORR4':'0.00000'},
65                     {'FF':'1L2Y_1LE1', 'WLONG' :'1.00000', 'WSCP'    :'1.23315', 'WELEC' : '0.84476', 'WBOND' :'1.00000', 'WANG'  :'0.62954', 'WSCLOC': '0.10554', 'WTOR': '1.84316', 'WTORD' : '1.26571', 'WCORRH': '0.19212', 'WCORR5': '0.00000',
66                                         'WCORR6':'0.00000', 'WEL_LOC':'0.37357', 'WTURN3':'1.40323', 'WTURN4':'0.64673', 'WTURN6':'0.00000', 'WVDWPP': '0.23173', 'WHPB': '1.00000', 'WSCCOR': '0.00000', 'CUTOFF': '7.00000', 'WCORR4': '0.00000'},
67                     {'FF':'4P',  'WSC':'1.00000', 'WSCP':'2.73684', 'WELEC':'0.06833', 'WANG':'4.15526', 'WSCLOC':'0.16761', 'WTOR':'2.99546', 'WTORD':'2.89720', 'WCORRH':'1.98989', 'WCORR5':'0.00000', 'WCORR6':'0.00000', 'WEL_LOC':'1.60072',
68                                         'WTURN3':'2.36351', 'WTURN4':'1.34051', 'WTURN6':'0.00000', 'CUTOFF':'7.00000', 'WCORR4':'0.00000', 'WSCCOR':'0.00000'},
69                     {'FF':'3P',  'WSC':'1.00000', 'WSCP':'2.85111', 'WELEC':'0.36281', 'WANG':'3.95152', 'WSCLOC':'0.15244', 'WTOR':'3.00008', 'WTORD':'2.89863', 'WCORRH':'1.91423', 'WCORR5':'0.00000', 'WCORR6':'0.00000', 'WEL_LOC':'1.72128',
70                                         'WTURN3':'2.99827', 'WTURN4':'0.59174', 'WTURN6':'0.00000', 'CUTOFF':'7.00000', 'WCORR4':'0.00000', 'WSCCOR':'0.00000'},
71                         {'FF':'CASP5',          'WSC':'1.00000',        'WSCP':'1.54864', 'WELEC':'0.20016', 'WANG': '1.00572', 'WSCLOC': '0.06764', 'WTOR':'1.70537', 'WTORD':'1.24442', 'WCORRH':'0.91583', 'WCORR5':'0.00607', 'WCORR6':'0.02316', 'WEL_LOC':'1.51083',
72                                         'WTURN3':'2.00764', 'WTURN4':'0.05345', 'WTURN6':'0.05282', 'WSCCOR':'0.0', 'CUTOFF': '7.00000', 'WCORR4':'0.00000' },
73                         {'FF':'ALPHABETA',      'WSC':'1.00000', 'WSCP':'1.43178', 'WELEC':'0.41501', 'WANG':'0.37790', 'WSCLOC':'0.12880', 'WTOR':'1.98784', 'WCORRH':'2.50526', 'WCORR5':'0.23873', 'WCORR6':'0.76327', 'WEL_LOC':'2.97687', 'WTURN3':'0.09261',
74                                         'WTURN4':'0.79171', 'WTURN6':'0.01074', 'CUTOFF':'7.00000', 'WCORR4':'0.00000', 'WSCCOR':'0.00000'},
75                         {'FF':'BETA', 'WSC':'1.00000', 'WSCP':'1.10684', 'WELEC':'0.70000', 'WANG':'0.80775', 'WSCLOC':'1.91939', 'WTOR':'3.36070', 'WCORRH':'2.50000', 'WCORR5':'0.99949', 'WCORR6':'0.46247', 'WEL_LOC':'2.50000', 'WTURN3':'1.80121',
76                                         'WTURN4':'4.35377', 'WTURN6':'0.10000', 'CUTOFF':'7.00000', 'WCORR4':'0.00000', 'WSCCOR':'0.00000'},
77                         {'FF':'ALPHA', 'WSC':'1.00000', 'WSCP':'0.72364', 'WELEC':'1.10890', 'WANG':'0.68702', 'WSCLOC':'1.79888', 'WTOR':'0.30562', 'WCORRH':'1.09616', 'WCORR5':'0.17452', 'WCORR6':'0.36878', 'WEL_LOC':'0.19508', 'WTURN3':'0.00000',
78                                         'WTURN4':'0.55588', 'WTURN6':'0.11539', 'CUTOFF':'7.00000', 'WCORR4':'0.0000', 'WTORD':'0.00000', 'WSCCOR':'0.00000'},
79                         {'FF':'CASP3', 'WELEC':'1.50000', 'WSTRAIN':'1.00000', 'WTOR':'0.08617', 'WANG':'0.10384', 'WSCLOC':'0.10384', 'WCORR':'1.50000', 'WTURN3':'0.00000', 'WTURN4':'0.00000', 'WTURN6':'0.00000', 'WEL_LOC':'0.00000', 'WCORR5':'0.00000',
80                                         'WCORR6':'0.00000', 'SCAL14':'0.40000', 'SCALSCP':'1.00000', 'CUTOFF':'7.00000', 'WSCCOR':'0.00000'}
81         ]
82
83         one_letter ={'VAL':'V', 'ILE':'I', 'LEU':'L', 'GLU':'E', 'GLN':'Q', 'ASP':'D', 'ASN':'N', 'HIS':'H', 'TRP':'W', 'PHE':'F', 'TYR':'Y', 'ARG':'R', 'LYS':'K', 'SER':'S', 'THR':'T', 'MET':'M', 'ALA':'A', 'GLY':'G', 'PRO':'P', 'CYS':'C'}
84
85
86         def __init__(self,parent):
87                 # Create the GUI window 
88                 root = Toplevel.__init__(self)
89                 self.config(width=400, height=300)              # create a root window
90                 self.title("UNRES Input Generator v"+UNRESInpGen_version)                 # Set window title
91                 self.resizable(0, 0)                                # Disable window resize
92                 self.geometry('-40+40')                             # Set window placement
93
94                 # Create the Balloon.
95                 self.balloon = Pmw.Balloon(root)
96
97                 #================================ 
98                 # "Main Options" group 
99                 #
100                 self.gr1 = Pmw.Group(self,tag_text = 'Main options')
101                 self.gr1.grid(row=0, column=0,columnspan=4,sticky=W+E,padx=10, pady=5)
102
103                 # - Create title Entryfield & Label
104                 #self.l1 = Label(self.gr1.interior(), text="Title:")
105                 #self.l1.grid(row=0, column=0,sticky=E)
106                 self.e1 = Pmw.EntryField(self.gr1.interior(),
107                         labelpos='w',
108                         label_text="Title:",
109                         validate = {'max': 80 },
110                 #       value ="(Place input information here)"
111                 )
112                 self.e1.component('entry').config(width=80)
113                 self.e1.grid(row=0, column=0,columnspan=4, sticky=E)
114
115                 # - Create "choose method" OptionMenu
116                 self.OM1Val.set('MD')
117                 self.optmenu1 =  Pmw.OptionMenu(self.gr1.interior(),
118                         labelpos = 'w',
119                         label_text = 'Choose method:',
120                         menubutton_textvariable = self.OM1Val,
121                         items = ['MD', 'MREMD' ],
122                         command = self.switch_options,
123                         menubutton_width = 10
124                 )
125                 self.optmenu1.grid(row=1, column=0,columnspan=2, sticky=E)
126         
127
128                 # - Seed entryfield
129                 self.gr1.e1 = Pmw.EntryField(self.gr1.interior(),
130                         labelpos='w',
131                         label_text="Seed",
132                         validate = {'validator' : 'integer', 'min': -2147483648 , 'max' : 0 },
133                         value = "-1111333"
134                 )
135                 self.gr1.e1.component('entry').config(width=8)
136                 self.gr1.e1.grid(row=1, column=2, sticky=W)
137  
138                 #==================================
139                 # - MD options frame
140                 #
141                 self.gr1.md = Frame(self.gr1.interior())
142                 self.gr1.md.grid(row=2, column=0, columnspan=4,sticky=W+E)
143                 
144                 # -- nstep entryfield
145                 self.gr1.md.e1 = Pmw.EntryField(self.gr1.md,
146                         labelpos='w',
147                         label_text="NSTEP",
148                         validate = {'validator' : 'integer', 'min': 1 , 'max' : 1000000 },
149                         value = "500000"
150                 )
151                 self.balloon.bind(self.gr1.md.e1,'Number of calculation steps')
152                 self.gr1.md.e1.component('entry').config(width=8)
153                 self.gr1.md.e1.grid(row=0, column=0, sticky=E)
154
155                 # -- ntwe entryfield
156                 self.gr1.md.e2 = Pmw.EntryField(self.gr1.md,
157                         labelpos='w',
158                         label_text="NTWE",
159                         validate = {'validator' : 'integer', 'min': 0 , 'max' : 1000000 },
160                         value = "100"
161                 )
162                 self.balloon.bind(self.gr1.md.e2,'Frequency of energy output.\nNTWE=0 means no energy dump. ')
163                 self.gr1.md.e2.component('entry').config(width=8)
164                 self.gr1.md.e2.grid(row=0,column=1, sticky=E)
165
166                 # -- ntwx entryfield
167                 self.gr1.md.e3 = Pmw.EntryField(self.gr1.md,
168                         labelpos='w',
169                         label_text="NTWX",
170                         validate = {'validator' : 'integer', 'min': 1 , 'max' : 1000000 },
171                         value = "1000"
172                 )
173                 self.balloon.bind(self.gr1.md.e3, "Frequency of coordinate output.")
174                 self.gr1.md.e3.component('entry').config(width=8)
175                 self.gr1.md.e3.grid(row=0,column=2, sticky=E)
176
177                 # -- dt entryfield
178                 self.gr1.md.e4 = Pmw.EntryField(self.gr1.md,
179                         labelpos='w',
180                         label_text="DT",
181                         validate = {'validator' : 'real', 'min': 0.001 , 'max' : 1000 },
182                         value = "0.1")
183                 self.balloon.bind(self.gr1.md.e4, "Time step. \nThe unit is \"molecular time unit\" (mtu)\n 1 mtu = 48.9 fs")
184                 self.gr1.md.e4.component('entry').config(width=8)
185                 self.gr1.md.e4.grid(row=0,column=3, sticky=E)
186
187                 # -- damax entryfield
188                 self.gr1.md.e5 = Pmw.EntryField(self.gr1.md,
189                         labelpos='w',
190                         label_text="DAMAX",
191                         validate = {'validator' : 'real', 'min': 0.001 , 'max' : 1000 },
192                         value = "1.0")
193                 self.balloon.bind(self.gr1.md.e5, "Maximum allowed change of acceleration during a single time step.\nThe time step gets scaled down, if this is exceeded.")
194                 self.gr1.md.e5.component('entry').config(width=8)
195                 self.gr1.md.e5.grid(row=1,column=0, sticky=E)
196
197                 # -- dvmax entryfield
198                 self.gr1.md.e6 = Pmw.EntryField(self.gr1.md,
199                         labelpos='w',
200                         label_text="DVMAX",
201                         validate = {'validator' : 'real', 'min': 0.001 , 'max' : 1000 },
202                         value = "20.0")
203                 self.balloon.bind(self.gr1.md.e6, "Maximum allowed velocity (in A/mtu).")
204                 self.gr1.md.e6.component('entry').config(width=8)
205                 self.gr1.md.e6.grid(row=1,column=1, sticky=E)
206
207                 # -- restet_vel entryfield
208                 self.gr1.md.e7 = Pmw.EntryField(self.gr1.md,
209                         labelpos='w',
210                         label_text="RESET_VEL",
211                         validate = {'validator' : 'integer', 'min': 0 , 'max' : 10000000 },
212                         value = "1000")
213                 self.balloon.bind(self.gr1.md.e7, "Frequency of resetting velocities to values from Gaussian distribution")
214                 self.gr1.md.e7.component('entry').config(width=8)
215                 self.gr1.md.e7.grid(row=1,column=2, sticky=E)
216
217
218                 #=================================
219                 #  - thermostat frame
220                 #
221                 self.gr1.th = Frame(self.gr1.interior())
222                 self.gr1.th.grid(row=3, column=0, columnspan=5,sticky=W+E)
223
224                 # -- Thermostat optionmenu 
225                 self.gr1.th.optmenu1 =  Pmw.OptionMenu(self.gr1.th,
226                         labelpos = 'w',
227                         label_text = 'thermostat',
228                         items = ['Berendsen', 'Nose-Poincare 1999','Nose-Poincare 2001', 'Nose-Hoover', 'Langevin' ],
229                         command=self.set_thermostat,
230                         menubutton_width = 15
231                 )
232                 self.gr1.th.optmenu1.pack(side=LEFT)#grid(row=0, column=0, sticky=E)
233
234                 # -- t_bath entryfield
235                 self.gr1.th.e1 = Pmw.EntryField(self.gr1.th,
236                         labelpos='w',
237                         label_text="T_BATH",
238                         validate = {'validator' : 'real', 'min': 0.000001 , 'max' : 10000 },
239                         value = "300.0")
240                 self.balloon.bind(self.gr1.th.e1, "Temperature (in K) of canonical simulation.")
241                 self.gr1.th.e1.component('entry').config(width=8)
242                 self.gr1.th.e1.pack(side=LEFT)
243                 
244                 # -- tau_bath entryfield
245                 self.gr1.th.e2 = Pmw.EntryField(self.gr1.th,
246                         labelpos='w',
247                         label_text="TAU_BATH",
248                         validate = {'validator' : 'real', 'min': 0.000001 , 'max' : 1000000 },
249                         value = "1.0")
250                 self.balloon.bind(self.gr1.th.e2, "(units are mtus; 1mtu=48.9 fs) \nConstant of the coupling to the thermal bath")
251                 self.gr1.th.e2.component('entry').config(width=8)
252                 self.gr1.th.e2.pack(side=LEFT)
253                                  
254                 # -- q_np entryfield
255                 self.gr1.th.e3 = Pmw.EntryField(self.gr1.th,   
256                         labelpos='w',
257                         label_text="Q_NP",
258                         validate = {'validator' : 'real', 'min': 0.000001 , 'max' : 1000000 },
259                         value = "0.1")
260                 self.balloon.bind(self.gr1.th.e3, "Mass of the fictitious particle in the calculations with the Nose-Poincare & Nose-Hoover thermostats.")
261                 self.gr1.th.e3.component('entry').config(width=8)
262                 #self.gr1.th.e2.pack(side=LEFT)
263
264
265                 #================================= 
266                 # "Force field options" group
267                 #     
268                 self.gr2 = Pmw.Group(self,tag_text = 'Force field options')
269                 self.gr2.grid(row=2, column=0,columnspan=4,sticky=W+E,padx=10, pady=5)
270
271                 # - Create "force field parameters" Option menu
272                 self.OM2Val.set('Custom')
273                 self.optmenu2 = Pmw.OptionMenu(self.gr2.interior(),
274                 label_text = 'Force field parameters:',
275                         labelpos = 'w',
276                         menubutton_textvariable = self.OM2Val,
277                         items = ['Custom','GAB','E0G', '1L2Y_1LE1','4P','3P','CASP5','ALPHABETA','BETA','ALPHA','CASP3' ],
278                         initialitem = 'Custom',
279                         menubutton_width = 10,
280                         command=self.set_force_field
281                 )
282                 self.optmenu2.grid(row=0, column=0,columnspan=4,sticky=E)
283
284                 # Force field parameters Entry Fields   
285                 self.ef = []
286                 for i in range(0,len(self.weights)):
287                         self.ef.append(Pmw.EntryField(self.gr2.interior(), 
288                         labelpos='n',
289                         label_text=self.weights[i],
290                         validate = {'validator' : 'real','min' : 0, 'max' : 10, 'minstrict' : 0},
291                         value = '1.00000'))
292                         self.ef[i].component('entry').config(width=8)
293                         self.ef[i].grid(row=1+(i//9), column=0+(i % 9))
294                 # "Sequence" group 
295                 self.gr3 = Pmw.Group(self,tag_text = 'Sequence')
296                 self.gr3.grid(row=3, column=0,columnspan=4,sticky=W+E,padx=10, pady=5)
297                                 
298                 self.gr3.f1 = Frame(self.gr3.interior())
299                 # Object List  
300                 self.cb2 = Pmw.ComboBox(self.gr3.f1,
301                         labelpos = 'w',
302                         #entry_relief = 'raised',
303                         label_text = 'Selection/Object',
304                         scrolledlist_items = self.objects_list
305                 )
306                 self.cb2.pack(side=LEFT)
307                 # refresh button
308                 self.gr3.btn1 = Button(self.gr3.f1, text='Refresh', padx=0, pady=0 , command = self.refresh_list)
309                 self.gr3.btn1.pack(side=LEFT)  
310                 # Get sequence button 
311                 self.gr3.btn2 = Button(self.gr3.f1, text='Get Sequence', padx=0, pady=0 , command = self.get_seq)
312                 self.gr3.btn2.pack(side=LEFT)  
313                         
314                 self.gr3.f1.pack(expand="yes",fill="both")
315
316                 # Disulfide brige checkbox
317                 self.gr3.chk = Checkbutton(self.gr3.f1, text="Write disulfide bridges", variable=self.writeSSbrige)
318                 self.gr3.chk.pack(side=LEFT)
319
320                 # Sequence text
321                 self.gr3.t1 = Pmw.ScrolledText(self.gr3.interior(),
322                         columnheader = 1,
323                         columnheader_width = 3,
324                         rowheader = 1,
325                         rowheader_width = 4,
326                         usehullsize=1,
327                         hull_width=30,
328                         hull_height=180,
329                         text_padx = 4,
330                         text_pady = 4,
331                         Header_padx = 4,
332                         rowheader_pady = 4,
333                 )
334                 # Sequence text - create the header line 
335                 headerline = ''
336                 for column in range(1,21):
337                         headerline = headerline + ('%-4s' % str(column) )
338                 self.gr3.t1.component('columnheader').insert('0.0',headerline)
339
340                 self.gr3.t1.pack(expand="yes",fill="both") 
341
342                 # Add some buttons
343                 btn3=Button(self, text='Write Input', padx=0, pady=0, command = self.ok)
344                 btn3.grid(row=10, column=0, columnspan=2,sticky=N+W+S+E)
345                 btn4=Button(self, text='Close', padx=0, pady=0, command = self.myHide)
346                 btn4.grid(row=10, column=2, columnspan=2,sticky=N+W+S+E)
347
348                 # create callback to prevent window kill
349                 self.protocol("WM_DELETE_WINDOW", self.myHide)
350
351         def ToggleColor(self):
352                 if self.colorize.get()==1:
353                         for i in range(0,len(self.res_states)):
354                                 UNRESInpGenWindow.ccb[i].config(state=ACTIVE, bg=self.res_states[i][1], activebackground=self.res_states[i][1], relief=RAISED, overrelief=RAISED)
355                 else:
356                         for i in range(0,len(self.res_states)):
357                                 UNRESInpGenWindow.ccb[i].config(state=DISABLED, bg=self.Default_Color , activebackground=self.Default_Color, relief=RAISED, overrelief=RAISED)
358
359         def myShow(self):
360                 self.deiconify()
361
362         def myHide(self):
363                 self.withdraw()
364
365         def myToggle(self):
366                 if self.state() == "normal":
367                         self.myHide()
368                 elif self.state() == "withdrawn":
369                         self.myShow()
370         
371         def get_selections(self):
372                 self.objects_list = []
373                 for item in cmd.get_names("all"):
374                         if cmd.get_type(item)=="object:molecule":
375                                 self.objects_list.append(item)
376                         if cmd.get_type(item)=="selection":
377                                 if item[0]<>"_":
378                                         self.objects_list.append(item)
379
380         def get_nonstandard(self):
381                 stored.list=[]
382                 cmd.iterate("(all)","stored.list.append(resn)")
383                 stored.list=list(Set(stored.list))  # remove duplicates
384                 # Remove standard amino acids and water from list
385                 for m in ['HIS','ASP','ARG','PHE','ALA','CYS','GLY','GLN','GLU','LYS','LEU','MET','ASN','SER','TYR','THR','ILE','TRP','PRO','VAL','HOH']:
386                         try:
387                                 stored.list.remove(m)
388                         except ValueError:
389                                 pass
390                 UNRESInpGenWindow.cbcr1.setlist(stored.list)
391
392         def refresh_list(self):
393                 # Save old values
394                 old = self.objects_list
395                 try:
396                         old_cb2=self.cb2.getvalue()[0]
397                 except:
398                         old_cb2=''
399                 self.get_selections()
400
401                 # Update selectionlist if needed
402                 if not(old==self.objects_list):
403                         self.cb2.setlist(self.objects_list)
404                         self.cb2.setlist(self.objects_list)
405
406                 if not(old_cb2 in self.objects_list):
407                         # No - clear the combobox entry
408                         self.cb2.component('entryfield').clear()
409                 else:
410                         # Yes - set the old value
411                         self.cb2.selectitem(old_cb2,setentry=1)
412         
413         
414         def get_seq(self):
415                 # Get the number of chains 
416                 try: 
417                         nchains=int(len(cmd.get_chains(self.cb2.getvalue()[0])))
418                 except:
419                         nchains=0
420                 pass
421                 
422                 if (nchains>0):
423                         self.seq_list=[]
424                 offset=0
425                 seq_data=''
426                 self.seq_length=0
427                 for chain in range(0,nchains):
428                         # Get only amonoacids, N-terminus acyl and C-terminus amide
429                         # example:      (1uRC and chain F and (n. ca or (resn ACE and n. C) or (resn NH2 and n. N)))
430                         # No chain information or only one chain
431                         if nchains==1:
432                                 atomchain=cmd.get_model(str(self.cb2.getvalue()[0])+" & (n. ca|(resn ACE & n. C)|(resn NH2+NHH+NME & n. N))").atom
433                         # Chain information is present
434                         else:
435                                 atomchain=cmd.get_model(str(self.cb2.getvalue()[0])+" & chain "+cmd.get_chains(self.cb2.getvalue()[0])[chain]+" & (n. ca|(resn ACE & n. C)|(resn NH2+NHH+NME & n. N))").atom
436                         # dodaj separator lancuchow
437                         if chain>0:
438                                 self.seq_list.append("D  ")
439                                 self.seq_length+=1
440                                 
441
442                         # Zamiana chronionych
443                         # ACE ALA ALA NH2 D   ACE ALA ALA NH2
444                         # GLY ALA ALA GLY D   GLY ALA ALA GLY
445                         # Zamiana niechronionych 
446                         #     ALA ALA     D       ALA ALA
447                         # D   ALA ALA     D       ALA ALA D
448                         #
449                         # j - licznik pozycji reszty
450                         j=0
451                         for i in atomchain:
452                                 self.seq_length+=1
453                                 if str(i.resn)=="NH2":
454                                         self.seq_list.append("GLY")
455                                 elif str(i.resn)=="NHH":
456                                         self.seq_list.append("GLY")
457                                 elif str(i.resn)=="NME":
458                                         self.seq_list.append("GLY")
459                                 elif str(i.resn)=="ACE":
460                                         self.seq_list.append("GLY")
461                                 elif str(i.resn)=="GLY":
462                                         self.seq_list.append("GLY")
463                                 else:
464                                         if j==0 and chain==0: 
465                                                 self.seq_list.append("D  ")     
466                                                 self.seq_list.append(str(i.resn))
467                                                 self.seq_length+=1
468                                         elif j==len(atomchain)-1 and chain==(nchains-1):
469                                                 self.seq_list.append(str(i.resn))
470                                                 self.seq_list.append("D  ")
471                                                 self.seq_length+=1
472                                         else:
473                                                 self.seq_list.append(str(i.resn))
474                                 j+=1
475                 rows=0
476                 #row_header='0'  - old header
477                 row_header='1'
478                 # tag setup 
479                 self.gr3.t1.tag_configure('dummy', background = 'LightBlue1')
480                 tagList=[]
481                 self.gr3.t1.tag_configure('cysteine', background = 'LightGoldenrod1')
482                 CysTagList=[]
483
484                 for i in range(len(self.seq_list)):
485                         # Dummy tag
486                         if self.seq_list[i]=="D  ":
487                                 tag1 = '%d.%d' % (rows+1, ((len(seq_data)-rows)%80))
488                                 tag2 = '%d.%d' % (rows+1, ((len(seq_data)-rows)%80)+3)
489                                 tagList.append(tag1)
490                                 tagList.append(tag2)
491                         # Cysteine tag  
492                         if self.seq_list[i]=="CYS" or self.seq_list[i]=="CYX":
493                                 tag1 = '%d.%d' % (rows+1, ((len(seq_data)-rows)%80))
494                                 tag2 = '%d.%d' % (rows+1, ((len(seq_data)-rows)%80)+3)
495                                 CysTagList.append(tag1)
496                                 CysTagList.append(tag2)
497                         #       
498                         offset+=1
499                         seq_data=seq_data+self.seq_list[i]+" "
500                         # Line Wrap
501                         if offset>19:
502                                 offset=0
503                                 seq_data=seq_data+'\n'
504                                 rows+=1
505                                 #row_header=row_header+'\n'+str(rows*20)    - old header
506                                 row_header=row_header+'\n'+str(rows+1)
507
508                 self.gr3.t1.clear()
509                 self.gr3.t1.insert('end',seq_data)
510                 # Apply tags
511                 if len(tagList): 
512                         apply(self.gr3.t1.tag_add, ('dummy',) + tuple(tagList))
513                 if len(CysTagList):
514                         apply(self.gr3.t1.tag_add, ('cysteine',) + tuple(CysTagList))
515
516                 # Show row header
517                 self.gr3.t1.component('rowheader').delete(1.0, END)
518                 self.gr3.t1.component('rowheader').insert('end',row_header)     
519
520     
521         def switch_options(self,calctype):
522                 '''
523                         Displays the options available to set depending on the currently 
524                         selected calculation type
525                 '''
526                 # Hide all
527                 self.gr1.th.grid_remove()
528                 self.gr1.md.grid_remove()
529                 if  self.OM1Val.get()=="MD":
530                         self.gr1.md.grid(row=2, column=0, columnspan=5, sticky=W+E)
531                         self.gr1.th.grid(row=3, column=0, columnspan=5, sticky=W+E)
532                 
533
534         def set_force_field(self, pole):
535                 '''
536                         Enables/disables entryfields for force field perameters depending
537                         on the currently selected force field 
538                 '''
539                 for ff in self.force_fields:
540                         if ff['FF']==pole: 
541                                 #print ff
542                                 for i in range(0,len(self.weights)):
543                                         if ff.has_key(self.weights[i]):
544                                                 self.ef[i].setvalue(ff.get(self.weights[i]))
545                                                 self.ef[i].component('entry').config(state=NORMAL)
546                                         else:
547                                                 self.ef[i].component('entry').config(state=DISABLED)
548                         # Custom force field
549                         elif pole=='Custom':
550                                 for i in range(0,len(self.weights)):
551                                         self.ef[i].component('entry').config(state=NORMAL)
552
553         def set_thermostat(self,thermostat):
554                 '''
555                         Display additional widget acording to selected thermostat
556                 '''
557                 #print thermostat
558                 self.gr1.th.e2.pack_forget()
559                 self.gr1.th.e3.pack_forget()
560                 if thermostat=='Berendsen':
561                         self.gr1.th.e2.pack(side=LEFT)
562                 elif thermostat=='Nose-Poincare 1999' or thermostat=='Nose-Poincare 2001' or thermostat=='Nose-Hoover':
563                         self.gr1.th.e3.pack(side=LEFT)
564
565         def fortran_format(self,s):
566                 '''
567                     Formats string containing keywords to wrap over 80 columns 
568                 '''
569                 tmpstr=''
570                 column=1
571                 for k in s.split():
572                         if column+len(k)+1<80:
573                                 tmpstr+=k+' '
574                                 column+=len(k)+1
575                         else:
576                                 tmpstr+=" "*(80-column)+"&\n"+k+" "
577                                 column=len(k)+2
578                 tmpstr+='\n'
579                 return tmpstr
580
581         def get_weights(self):
582                 '''
583                         Get the force field weights
584                 '''
585                 s = ''
586                 for i in range(0,len(self.weights)):
587                         if self.ef[i].component('entry').cget("state")=="normal":
588                                 s+=self.weights[i]+"="+self.ef[i].getvalue()+" "
589                 return s
590
591         def get_md_opt(self):
592                 s = "NSTEP="+self.gr1.md.e1.getvalue()+" NTWE="+self.gr1.md.e2.getvalue()+" "
593                 s+= "NTWX="+self.gr1.md.e3.getvalue()+" DT="+self.gr1.md.e4.getvalue()+" "
594                 s+= "DAMAX="+self.gr1.md.e5.getvalue()+" DVMAX="+self.gr1.md.e6.getvalue()+" " 
595                 s+= "RESET_VEL="+self.gr1.md.e7.getvalue()+" " 
596                 
597                 # Thermostat options
598                 term=self.gr1.th.optmenu1.getvalue()
599                 #print term
600                 if term=="Berendsen":
601                         s+="TBF TAU_BATH="+self.gr1.th.e2.getvalue()+" "
602                 elif term=="Nose-Poincare 1999":
603                         s+="NOSEPOINCARE99 Q_NP="+self.gr1.th.e3.getvalue()+" "
604                 elif term=="Nose-Poincare 2001":
605                         s+="NOSEPOINCARE01 Q_NP="+self.gr1.th.e3.getvalue()+" "
606                 elif term=="Nose-Hoover":
607                         s+="NOSEHOOVER96 Q_NP="+self.gr1.th.e3.getvalue()+" "
608                 elif term=="Langevin":
609                         s+="LANG=1 "
610
611                 s+="T_BATH="+self.gr1.th.e1.getvalue()+" "
612                 return s
613
614
615         def get_seq_data(self):
616                 '''
617                         Dumps the sequence and disulfide bridge information
618                 '''
619                 # write sequence length 
620                 s = str(self.seq_length)+"\n"
621                 # 
622                 #try: 
623                 #       nchains=int(len(cmd.get_chains(self.cb2.getvalue()[0])))
624                 #except:
625                 #       nchains=0
626                 #pass
627
628                 chains=cmd.get_chains(self.cb2.getvalue()[0])
629                 nchains=len(chains)
630                 #print "chains  : ",chains
631                 #print "nchains : ",nchains
632
633                 # apply one space before each sequence line 
634                 seq=self.gr3.t1.get()
635                 for line in seq.split('\n'):
636                         s+=" "+line+"\n"
637                 # remove last newline character
638                 s = s[:-1]
639
640                 # disulfide bridges - get half-cysteines by selection:
641         # sele (bto. ((object) & r. CYS+CYX & n. SG) )& n. SG
642                 ncys=cmd.get_model("(bto. (("+str(self.cb2.getvalue()[0])+") & r. CYS+CYX & n. SG)) & n. SG").atom
643                 nSSbr=len(ncys)/2
644         
645                 
646                 # Is "Write disulfide brige" checked?
647                 # yes
648                 if self.writeSSbrige.get():
649                         # write number of half-cysteines
650                         s+=str(len(ncys))+"\n"
651                         if len(ncys)==0:
652                                 # No half-cysteines, cys position = 0  
653                                 s+=" 0\n"
654                         else:
655                                 # Write half-cysteine positions in sequence
656                                 #       
657                                 # get first chain
658                                 first=cmd.get_model(str(self.cb2.getvalue()[0])+"& chain "+str(chains[0])+" & n. ca")
659                         
660                                 # calculate base offset in first chain (if dummy atom present offset = 1 )      
661                                 if first.atom[0].resn=="GLY":
662                                         boff=0
663                                 else: 
664                                         boff=1 
665                         
666                                 # create dictionary of PDB chain/resi to UNRES_index
667                                 unresidx={}
668                                 for c in ncys:
669                                         # calculate residue index offset from first residue in chain containing half-cysteine
670                                         fresinchain=cmd.get_model(str(self.cb2.getvalue()[0])+"& chain "+c.chain+" & n. ca").atom
671                                         roff=int(c.resi)-int(fresinchain[0].resi)+1
672                                 
673                                         resb=0
674                                         coff=0
675                                         for r in chains:
676                                                 if c.chain==r:
677                                                         break
678                                                 resb+=len(cmd.get_model(str(self.cb2.getvalue()[0])+"& chain "+r+" & n. ca").atom)
679                                                 coff+=1
680                                         # add key/value to dictionary
681                                         unresidx[c.chain+"/"+str(c.resi)]=boff+resb+coff+roff   
682                                         s+=" "+str(boff+resb+coff+roff)
683                                 s+="\n"
684
685                                 # Write the number of disulfide bridges
686                                 s+=" "+str(nSSbr)+"\n"
687                         
688                                 lista_mostkow=[]
689                                 for c in ncys:
690                                         # get neibhoring Sulfur atom
691                                         opis=c.chain+str(c.resi)
692                                         lista_mostkow.append(opis)
693                                         nbr=cmd.get_model("(bto. ("+str(self.cb2.getvalue()[0])+" & resi "+str(c.resi)+"& n. SG & chain "+str(c.chain)+")) & n. SG").atom[0]
694                                         opis2=nbr.chain+str(nbr.resi)
695                                         if opis2 not in lista_mostkow:
696                                                 # Write witch half-cysteines build the i-th disulfide bridge
697                                                 s+=" "+str(unresidx[c.chain+"/"+str(c.resi)])+" "+str(unresidx[nbr.chain+"/"+str(nbr.resi)])+"\n"
698                                                 #print c.chain, c.resn,c.resi,"-S-S-", nbr.chain, nbr.resn,nbr.resi
699                 # Don't writeSSbrige    
700                 else:
701                         s+="0\n 0\n"
702                                 
703
704                 return s
705
706
707         def write_cshell(self, cshname,prefix):
708                 '''
709                         Writes the C-shell script needed to run UNRES calculations
710                 '''
711                 s='#!/bin/csh -f\n#\n# C-shell script generated by UNRESInpGen.py\n#\n'
712                 
713                 # number of processors per energy
714                 if self.OM1Val.get()=="MD":
715                         s+='setenv FGPROCS 1\n'
716
717                 # Potential
718                 if self.OM2Val.get()=="CASP3":
719                         s+='setenv POT LJ\n'
720                 else:
721                         s+='setenv POT GB\n'
722
723                 # input file
724                 s+='setenv PREFIX '+prefix+'\n'
725
726                 # Parallel stuff
727                 s+='setenv OUT1FILE YES\n'
728
729                 # force filed parameters
730                 s+='#---------------------------------------------------------------------\n'
731                 s+='setenv DD $UNRES_ROOT/PARAM\n'
732
733                 if self.OM2Val.get()=="CASP3":
734                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
735                         s+='setenv TORPAR $DD/torsion_cryst.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/scinter_LJ.parm\n'
736                         s+='setenv ELEPAR $DD/electr.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_GAP.parm\n'
737                         s+='setenv SCCORPAR $DD/rotcorr_AM1.parm\n'
738                 elif self.OM2Val.get()=="ALPHA" or self.OM2Val.get()=="BETA" or self.OM2Val.get()=="ALPHABETA":
739                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
740                         s+='setenv TORPAR $DD/torsion_ecepp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/scinter_GB.parm\n'
741                         s+='setenv ELEPAR $DD/electr.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_GAP.parm\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
742                 elif self.OM2Val.get()=="CASP5":
743                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
744                         s+='setenv TORPAR $DD/torsion_631Gdp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/scinter_GB.parm\n'
745                         s+='setenv ELEPAR $DD/electr_631Gdp.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_opt.parm.1igd_iter7n_c\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
746                 elif self.OM2Val.get()=="3P":
747                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
748                         s+='setenv TORPAR $DD/torsion_631Gdp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/sc_GB_opt.3P7_iter81_1r\n'
749                         s+='setenv ELEPAR $DD/electr_631Gdp.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_opt.parm.1igd_hc_iter3_3\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
750                 elif self.OM2Val.get()=="4P":
751                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
752                         s+='setenv TORPAR $DD/torsion_631Gdp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/sc_GB_opt.4P5_iter33_3r\n'
753                         s+='setenv ELEPAR $DD/electr_631Gdp.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_opt.parm.1igd_hc_iter3_3\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
754                 elif self.OM2Val.get()=="GAB":
755                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
756                         s+='setenv TORPAR $DD/torsion_631Gdp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/sc_GB_opt.1gab_3S_qclass5no310-shan2-sc-16-10-8k\n'
757                         s+='setenv ELEPAR $DD/electr_631Gdp.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_opt.parm.1igd_hc_iter3_3\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
758                 elif self.OM2Val.get()=="E0G":  
759                         s+='setenv BONDPAR $DD/bond.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/scgauss.parm\n'
760                         s+='setenv TORPAR $DD/torsion_631Gdp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/sc_GB_opt.1e0g-52-17k-2k-newclass-shan1e9_gap8g-sc\n'
761                         s+='setenv ELEPAR $DD/electr_631Gdp.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_opt.parm.1igd_hc_iter3_3\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
762                 elif self.OM2Val.get()=="1L2Y_1LE1":
763                         s+='setenv BONDPAR $DD/bond_AM1.parm\nsetenv THETPAR $DD/thetaml.5parm\nsetenv ROTPAR $DD/rotamers_AM1_aura.10022007.parm\n'
764                         s+='setenv TORPAR $DD/torsion_631Gdp.parm\nsetenv TORDPAR $DD/torsion_double_631Gdp.parm\nsetenv SIDEPAR $DD/scinter_$POT.parm\n'
765                         s+='setenv ELEPAR $DD/electr_631Gdp.parm\nsetenv SCPPAR $DD/scp.parm\nsetenv FOURIER $DD/fourier_opt.parm.1igd_hc_iter3_3\nsetenv SCCORPAR $DD/rotcorr_AM1.parm\n'
766                 elif self.OM2Val.get()=="Custom":
767                         s+='setenv BONDPAR $DD/.parm\nsetenv THETPAR $DD/.parm\nsetenv ROTPAR $DD/.parm\n'
768                         s+='setenv TORPAR $DD/.parm\nsetenv TORDPAR $DD/.parm\nsetenv SIDEPAR $DD/.parm\n'
769                         s+='setenv ELEPAR $DD/.parm\nsetenv SCPPAR $DD/.parm\nsetenv FOURIER $DD/.parm\nsetenv SCCORPAR $DD/.parm\n'
770
771
772
773                 s+='setenv PATTERN $DD/patterns.cart\n'
774                 s+='#---------------------------------------------------------------------\n'
775                 s+='$UNRES_BIN $*\n'
776
777                 f = open(cshname, 'w')
778                 f.write(s)
779                 f.close()
780
781
782         def setenv_info(self):
783                 print '''IMPORTANT: Remember to set the following envirament variables for your shell before starting calculations:
784                   UNRES_ROOT - the root directory where UNRES is installed on your system (should contain PARAM directory)
785                   UNRES_BIN  - the UNRES binary you want to execute
786                 '''
787
788
789         def ok(self):
790                 '''
791                     Writes the actual input     
792                 '''
793                 myFormats = [
794                     ('Input files','*.inp'),
795                         ('All files','*.*')
796                 ]
797                 # Write error handling for empty sequence
798                 #
799                 s=self.gr3.t1.getvalue()
800                 if len(s.strip())==0:
801                         tkMessageBox.showerror("NEED SEQUENCE DATA!", "NEED SEQUENCE DATA!\n\nYou have not loaded the sequence information. Please click \"Refresh\", choose an object from the list and click \"Get sequence\".")
802                 else:                           
803                         # Display dialog window
804                         fout = tkFileDialog.asksaveasfile(parent=self,mode='w',filetypes=myFormats,title='Save input')
805                         if fout:
806                                 print "Saving input file %s" % fout.name
807                                 # Get input header
808                                 text2save=self.fortran_format(self.e1.get())
809                                 # Get main options
810                                 mainopt="SEED="+self.gr1.e1.get()+" "
811                                 if self.OM1Val.get()=="MD":
812                                         mainopt+="MD PDBREF EXTCONF"
813                                 elif self.OM1Val.get()=="MREMD":
814                                         mainopt+="RE "
815                                 text2save+=self.fortran_format(mainopt)
816                                 # Get aux options
817
818                                 if self.OM1Val.get()=="MD":
819                                         text2save+=self.fortran_format(self.get_md_opt())
820
821                                 # Get force fields parameters
822                                 text2save+=self.fortran_format(self.get_weights())
823                 
824                                 # PDB reference
825                                 pdbreffn=str(self.cb2.getvalue()[0])+"_pdbref.pdb"
826                                 text2save+=pdbreffn+"\n"
827                                 print "Saving PDB reference structure %s" % (os.path.join(os.path.dirname(fout.name),pdbreffn))
828                                 cmd.save(os.path.join(os.path.dirname(fout.name),pdbreffn), self.cb2.getvalue()[0])
829
830                                 # C-shell script 
831                                 cshellfn=os.path.join(os.path.dirname(fout.name),"unres_"+os.path.basename(os.path.splitext(fout.name)[0])+".csh")
832                                 print "Writing C-shell script %s" % (cshellfn)
833                                 prefix=os.path.basename(os.path.splitext(fout.name)[0])
834                                 self.write_cshell(cshellfn,prefix)
835                                 
836                                 # Get sequence data
837                                 text2save+=self.get_seq_data()
838
839                                 fout.write(text2save)
840                                 fout.close()
841
842                                 # ENV Varaiables info
843                                 self.setenv_info()