-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFAHMM_Core17_Trajectory.py
493 lines (417 loc) · 13.7 KB
/
FAHMM_Core17_Trajectory.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
#! /usr/bin/env python
#
# Author: Christian Lohmann 2014
#
# a little helper script to deliver the GPU-core 17 trajectories to iPad FAHMM
#
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# ATTENTION: the current actual version in AppStore not yet support this "hack"
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# Checkout on iTunes Store: https://itunes.apple.com/us/app/fah-mobile-monitor-solo/id683734944?mt=8
# Checkout on Playstore: sorry, not yet; Android version in work
#
# This script is made as ad-interim solution and in hope to get deleted the day after
# the official client/core version will be enabled to deliver the required data back
# to requesting clients.
#
# The following files will be send back to the requester
# /var/lib/fahclient/work/<wu>/01/viewerTop.json for the atoms
# /var/lib/fahclient/work/<wu>/01/viewerFrame1.json for the coordinates
# /var/lib/fahclient/work/<wu>/01/system.xml for the bonds
#
# following frames might be delivered later; just as a starter
#
# Change History
#
# 2014/10/14 1.0 initial version
# 2014/10/14 1.1 removed some leftover from original purpose of the template
# (cnt % 100 and print of client list)
#
# to run the script:
#
# nohup python FAHMM_Core17_Trajectory.py &
#
# Can be tested with a simple "telnet localhost 36331"
# then enter "trajectory 01" and press enter (or whatever workunit you have with GPU)
#
# when run as console the stdin can be used to terminate the script
# when run as daemon we should add a signal handler; right now I'm to lazy for that
#
import os
import sys
import time
import datetime
import socket
import select
import string
import re
import xml.etree.ElementTree as ET
import json
import os.path
#
# Adopt here the path pointing to your working directory from FAHClient
#
# As I only have Linux I was not able to test with Windows (wouldn't even know where the path is)
#
hostname = "linuxpowered"
port = 36331
workingPath = "/var/lib/fahclient/work/"
atomList = []
bondList = []
#
# Atom-Object
#
class Atom(object):
def __init__(self,symbol,charge,radius,mass,number):
self.symbol=symbol
self.charge=0
self.radius=0
self.mass=0
self.number=number
self.atomList = []
class __metaclass__(type):
def __iter__(self):
for attr in dir(Foo):
if not attr.startswith("__"):
yield attr
#
# Bond-Object
#
class Bond(object):
def __init__(self,atom1,atom2):
self.atom1=atom1
self.atom2=atom2
class __metaclass__(type):
def __iter__(self):
for attr in dir(Foo):
if not attr.startswith("__"):
yield attr
#
# sendFileThroughSocket
# Parameter: fn Filename with full path
# s Socket
#
# copy a file "as-is" trough the socket, in 1024 chunk of bytes
#
def sendFileThroughSocket(fn, s):
print tst,"Send filename", fn, "to", s.getsockname()
fh = open(fn,'rb') # open in read/binary
l = 1024
while (l == 1024):
b = fh.read(1024)
l = len(b)
s.send(b)
fh.close()
#
# getCorrectAtomsData
# Parameter: fn Filename with full path
#
# get the atom information from viewerTop.json
# Of main interest are the symbols, radius and number; to bad radius is not prodvided
#
def getCorrectAtomsData(fn):
json_data = open(fn, 'r')
data = json.load(json_data)
global atomList
# read all the atoms
for atomLine in data["atoms"]:
atom = Atom(atomLine[0], atomLine[1], atomLine[2], atomLine[3], atomLine[4])
atomList.append(atom)
# ignore all the bonds
# for atomLine in data["bonds"]:
json_data.close()
#
# identifyCA
# Parameter: none
#
# This method analyse the bonds for C-C couples to identify what the is AlphaCarbon
# as relevant information to be further used to determine the backchain and peptides
# building a protein
#
# Atom numbers to be played with
# H: 1
# C: 6
# N: 7
# O: 8
#
def identifyCA():
for bond in bondList:
atom1 = atomList[bond.atom1]
atom2 = atomList[bond.atom2]
# figure out if we have a couple of C-C atoms
if (atom1.number == 6 and atom2.number == 6):
cntN = 0
cntC = 0
cntO = 0
cntH = 0
flagAtom1 = True
flagAtom2 = True
# count all bonded atoms for the first C
for atom3 in atom1.atomList:
if atom3.number == 1:
cntH = cntH + 1
if atom3.number == 6:
cntC = cntC + 1
if atom3.number == 7:
cntN = cntN + 1
if atom3.number == 8:
flagAtom1 = False # can't be Alpha Carbon
cntO = cntO + 1
# count all bonded atoms for the second C
for atom3 in atom2.atomList:
if atom3.number == 1:
cntH = cntH + 1
if atom3.number == 6:
cntC = cntC + 1
if atom3.number == 7:
cntN = cntN + 1
if atom3.number == 8:
flagAtom2 = False # can't be Alpha Carbon
cntO = cntO + 1
# now checking what we found:
# there must be exactly one "O" and two "N"; "C" and "H" have different possible counts
#
# H O
# | |
# N--C--C--N
# |
# C?H
#
# Good enough for the current environment we are in ...
#
if cntO == 1 and cntN == 2 and (cntC == 2 or cntC == 3) and (cntH == 1 or cntH == 2):
if flagAtom1 == False and flagAtom2 == True:
atom2.symbol = "CA"
elif flagAtom2 == False and flagAtom1 == True:
atom1.symbol = "CA"
#
#
# sendCorrectAtomsData
# Parameter: s Stream to send the PyON message
#
# send the corrected atom data to the stream requested the trajectory
#
def sendCorrectAtomsData(st):
for atom in atomList:
l = "[\"" + atom.symbol + "\", 0, 0, 0," + str(atom.number) +"],\n"
st.send(l)
st.send("[\"FAHMM\",0,0,0,999]") # little piece of lazyness to remove the last colon
#
# getCorrectBondsData
# Parameter: fn Filename with full path
# maxindex Number of atoms we are interested in
#
# Read the full list bonds and constraints from system.xml to get the full picture.
# As the bonds/constraints also contain all the water and other atom we have the count of
# atoms serving as threshold to ignore those non-protein atoms
#
def getCorrectBondsData(fn, maxindex):
data = ET.parse(fn)
root = data.getroot()
#
# loop over the constraints
#
for bond in root.iter('Constraint'):
p1 = int(bond.get('p1'))
p2 = int(bond.get('p2'))
if (p1 < maxindex and p2 < maxindex):
bond = Bond(p1, p2)
bondList.append(bond)
atom1 = atomList[p1]
atom2 = atomList[p2]
atom1.atomList.append(atom2)
atom2.atomList.append(atom1)
#
# loop over the bonds
#
for bond in root.iter('Bond'):
p1 = int(bond.get('p1'))
p2 = int(bond.get('p2'))
if (p1 < maxindex and p2 < maxindex):
bond = Bond(p1, p2)
bondList.append(bond)
atom1 = atomList[p1]
atom2 = atomList[p2]
atom1.atomList.append(atom2)
atom2.atomList.append(atom1)
#
# sendCorrectBondsData
# Parameter: st Stream to send the bond list to
#
# Send the collective bonds from system.xml to the requesting stream
#
def sendCorrectBondsData(st):
l = ""
# get all the bonds
cntMax = len(bondList)
bondLast = bondList.pop(-1) # get the last item off the list
# get all remaining bonds out into the stream
for bond in bondList:
l = "[" + str(bond.atom1) + "," + str(bond.atom2) + "],"
st.send(l)
# get the remove last bonds out into the stream; just without colon
l = "[" + str(bondLast.atom1) + "," + str(bondLast.atom2) + "]"
st.send(l)
#
# getTrajectory(st, wu)
# Parameter: st Stream requesting the trajectory
# wu Workunit
#
# This is the main routine to get the requested trajectory of a workunit
# by reading its working folder
#
#
def getTrajectory(st, wu):
parts = wu.split()
print tst,"Get trajectory for WU", parts[1]
pn = workingPath + parts[1] + "/01/"
if not os.path.isfile(pn+"viewerFrame1.json"):
st.send("\nPyON 1 topology\n")
st.send("{\n")
st.send("\"atoms\": [],\n")
st.send("\"bonds\": []\n")
st.send("}\n")
st.send("\n---")
print tst,"no position yet known, send empty data"
return
getCorrectAtomsData(pn+"viewerTop.json")
maxIndex = len(atomList)
getCorrectBondsData(pn+"system.xml", maxIndex)
identifyCA()
print tst,"Number of atoms", len(atomList)
print tst,"Number of bonds", len(bondList)
st.send("\nPyON 1 topology\n")
st.send("{\n")
st.send("\"atoms\": [\n")
sendCorrectAtomsData(st)
st.send("\n],\n")
st.send("\"bonds\": [")
sendCorrectBondsData(st)
st.send("]\n")
st.send("}\n")
st.send("\n---")
st.send("\nPyON 1 positions\n")
#
# later we can build a loop here and copy all viewerFrame<n>.json files to
# give the movements of the protein while folding
#
# just copy the viewerFrame1.json file here; structure fits; content is ok
#
sendFileThroughSocket(pn+"viewerFrame1.json", st)
#
st.send("\n---\n")
st.send("\n>ok\n")
# cleanup to avoid double sending with next request
del atomList[:]
del bondList[:]
def FAHMMCore17Trajectory(hn, port):
backlog = 5
size = 1024*8
global ts
global tst
ts = time.time()
tst = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print "\n\n(c) Christian Lohmann 2014"
print "FAHMMCore17Trajectory running on host", hn, "listen on port", port,"\n"
#
# establish trajectory server on the given host and port
#
sockTrajectory = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockTrajectory.setblocking(0)
sockTrajectory.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sockTrajectory.bind((hn, port))
sockTrajectory.listen(backlog)
# lists of sockets
# input = [sys.stdin, sockTrajectory]
input = [sockTrajectory] # when run as daemon we don't need stdin
output = []
clientList = []
print tst,"Trajectory server created socket ", sockTrajectory.getsockname()
cnt = 1
running = 1
while running:
# error handler for the select.select
try:
ready_to_read, ready_to_write, in_error = \
select.select(input, output, [])
#print cnt, ": entries in rq", len(ready_to_read), \
# " wq", len(ready_to_write), \
# "error", len(in_error)
except IOError as e:
print tst,"select", e
if e.errno == 9:
break
except:
print tst,"select, unexpected error", sys.exc_info()[0]
print sys.exc_traceback.tb_lineno
raise
cnt = cnt + 1
try:
for s in ready_to_read:
# get actual timestamp
ts = time.time()
tst = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
if s is sockTrajectory:
# print "trajectory socket", s.getsockname()
# a new client want to connect
client, adress = sockTrajectory.accept()
if client is not None:
for c in clientList:
print tst, "forced close connection(s), only one FAHMM client is allowed per host", c
input.remove(c)
clientList.remove(c)
c.shutdown(socket.SHUT_RDWR)
c.close()
input.append(client)
clientList.append(client)
print tst, "new connection for", adress, "established"
elif s is sys.stdin:
# print "stdin"
# close all client connections
junk = sys.stdin.readline()
while len(clientList) > 0:
c = clientList[0]
print "close connection by user, FAH client is gone", c
input.remove(c)
clientList.remove(c)
c.shutdown(socket.SHUT_RDWR)
c.close()
running = 0
break
else:
# print "regular socket", s.getsockname()
# read a data block
data = s.recv(size)
if data:
# print tst,"received from", s, ":\n", data
sepline = data.splitlines(1)
for l in sepline:
# print the command; without linebreak (is part of the command)
# print tst,"command:", l,
if l.startswith("auth"): sockTrajectory.send(">OK\n")
# elif l.startswith("info"): sockTrajectory.send(l)
elif l.startswith("exit"): running = 0
# elif l.startswith("sleep"): sockTrajectory.send(l)
elif l.startswith("traj"): getTrajectory(clientList[0], l)
elif l.startswith("trajectory"): getTrajectory(clientList[0], l)
else: print tst,"command", l, "ignored"
else:
# print "end of stream, remove", s
s.close()
clientList.remove(s)
input.remove(s)
except IOError as e:
print e
if e.errno == 9:
break
except:
print "unexpected error", sys.exc_info()[0]
print sys.exc_traceback.tb_lineno
raise
sys.stdout.flush()
sockTrajectory.close()
print "FAHMMCore17Trajectory server stopped running\n"
ts = time.time()
tst = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
FAHMMCore17Trajectory(hostname, port)