memprod 0.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
memprod/MemPrOD.py ADDED
@@ -0,0 +1,1234 @@
1
+ import os
2
+ import sys
3
+ import warnings
4
+ import jax.numpy as jnp
5
+ from jax import tree_util
6
+ import jax
7
+ import jax.profiler
8
+ from enum import Enum
9
+ import numpy as np
10
+ from functools import partial
11
+ import matplotlib.pyplot as plt
12
+ import time
13
+ from mempro import MemPrO as ori
14
+ import argparse
15
+ from jax.sharding import PositionalSharding
16
+ from jax.experimental import mesh_utils
17
+ from scipy.optimize import minimize
18
+
19
+
20
+ def main():
21
+ warnings.filterwarnings('ignore')
22
+ os.environ["TF_CPP_MIN_LOG_LEVEL"]="3"
23
+ jax.config.update("jax_enable_x64",True)
24
+
25
+ martini_default_path = os.environ["PATH_TO_MARTINI"]
26
+
27
+ #Reading command line args
28
+ parser = argparse.ArgumentParser(prog="MemPrOD",description="This is a program for the prediction of deformations caused by membrane proteins.")
29
+ parser.add_argument("-f", "--file_name",help = "Input file name (.pdb)")
30
+ parser.add_argument("-o","--output",help="Name of the output directory (Default: Deformations)")
31
+ parser.add_argument("-ni","--iters",help="Number of minimisation iterations (Default: 75)")
32
+ parser.set_defaults(iters=75)
33
+ parser.add_argument("-ng","--grid_density",help="Spacing of grid points (Default: 3)")
34
+ parser.set_defaults(grid_density=3)
35
+ parser.add_argument("-ncav","--no_cavity_surface",action="store_false",help="Toggle use of a surface finder that is more accurate but more expensive.")
36
+ parser.set_defaults(no_cavity_surface=True)
37
+ parser.add_argument("-itp","--itp_file",help="Path to force field (martini_v3.itp)")
38
+ parser.set_defaults(itp_file=martini_default_path)
39
+ parser.add_argument("-bd","--build_system",help = "Build a MD ready CG-system for ranks < n (Default: n=0)")
40
+ parser.set_defaults(build_system=False)
41
+ parser.add_argument("-bd_args","--build_arguments",help="Arguments to pass to insane when building system")
42
+ parser.set_defaults(build_arguments="")
43
+ parser.add_argument("-cut","--cutoff",help="Cutoff for LJ interactions (Deafult: 12)")
44
+ parser.set_defaults(cutoff=12)
45
+ parser.add_argument("-k1","--k1_const",help="Spring constant for neighbouring grid point Z deviations (Deafult: 0.25)")
46
+ parser.set_defaults(k1_const=0.25)
47
+ parser.add_argument("-k2","--k2_const",help="Spring constant for membrane compression (Deafult: 0)")
48
+ parser.set_defaults(k2_const=0)
49
+ parser.add_argument("-zshift","--zshift",help="Z shift of membrane for proteins that are missoriented due to lack of deformations.")
50
+ parser.set_defaults(zshift=0)
51
+ parser.add_argument("-mt","--membrane_thickness",help="Initial thickness of membrane in Angstroms (Deafult: 38)")
52
+ parser.set_defaults(membrane_thickness=38.0)
53
+ parser.add_argument("-wb","--write_bfactors",action="store_true",help="Toggle writing charge at each membrane segment instead of z position.")
54
+ parser.set_defaults(write_bfactors=False)
55
+ parser.add_argument("-res","--additional_residues",help="Comma seperate list of additional residues in input files (eg: POPG) Note the only ATOM entries will be read, all HETATM entries will be ignored.")
56
+ parser.set_defaults(additional_residues="")
57
+ parser.add_argument("-res_itp","--additional_residues_itp_file",help="Path to the itp file describing all additional residues, and bead types associated to beads in the residue. A CG representation is required, for atomisitic inputs an additional file is required which describes the beading.")
58
+ parser.set_defaults(additional_residues_itp_file="")
59
+ parser.add_argument("-res_cg","--residue_cg_file",help="Folder that contains a files of name RES.pdb describing the beading for each additional RES - see CG2AT github for examples")
60
+ parser.set_defaults(residue_cg_file="")
61
+ args = parser.parse_args()
62
+
63
+ #Error checking user inputs
64
+ add_reses = args.additional_residues.split(",")
65
+ if(len(add_reses) > 0):
66
+ if(len(add_reses[0]) > 0):
67
+ print("Using additional residues:",", ".join(add_reses))
68
+ if args.additional_residues_itp_file == "":
69
+ print("ERROR: Additional residue itp is required if using additional residues.")
70
+ exit()
71
+ if args.residue_cg_file == "":
72
+ print("WARNING: You have not added beading information for the added residues, this will cause an error if orienting a atomisitic input.")
73
+
74
+ if(len(add_reses) > 0):
75
+ if(len(add_reses[0]) > 0):
76
+ for i in add_reses:
77
+ ori.add_Reses(i,args.additional_residues_itp_file)
78
+ if len(args.residue_cg_file) > 0 :
79
+ ori.add_AtomToBeads(args.residue_cg_file.lstrip("/")+"/"+i+".pdb")
80
+ mem_data = [0,0]
81
+
82
+ try:
83
+ mem_data[1] = float(args.membrane_thickness)-10
84
+ except:
85
+ print("ERROR: Could not read value of -mt. Must be a float > 10.")
86
+ exit()
87
+ if(mem_data[1] <= 0):
88
+ print("ERROR: Could not read value of -mt. Must be a float > 10.")
89
+ exit()
90
+
91
+
92
+
93
+ try:
94
+ iters = int(args.iters)
95
+ except:
96
+ print("ERROR: Could not read value of -ni. Must be an integer >= 0.")
97
+ exit()
98
+ if(iters < 0):
99
+ print("ERROR: Could not read value of -ni. Must be an integer >= 0.")
100
+ exit()
101
+
102
+
103
+ try:
104
+ build_system = int(args.build_system)
105
+ except:
106
+ print("ERROR: Could not read value of -bd. Must be an integer > -1.")
107
+ exit()
108
+ if(build_system < 0):
109
+ print("ERROR: Could not read value of -bd. Must be an integer > -1.")
110
+ exit()
111
+
112
+
113
+
114
+ fn = args.file_name
115
+
116
+ if(not os.path.exists(fn)):
117
+ print("ERROR: Cannot find file: "+fn)
118
+ exit()
119
+
120
+ orient_dir = args.output
121
+
122
+ #Setting Martini itp file
123
+ martini_file = args.itp_file
124
+
125
+ if(not os.path.exists(martini_file)):
126
+ print("ERROR: Cannot find file: "+martini_file)
127
+ exit()
128
+
129
+
130
+ #Creating folders to hold data
131
+ if(orient_dir == None):
132
+ if(not os.path.exists("Deformations/")):
133
+ os.mkdir("Deformations/")
134
+ orient_dir = "Deformations/"
135
+ else:
136
+ if orient_dir[-1] != "/":
137
+ orient_dir += "/"
138
+ if(not os.path.exists(orient_dir)):
139
+ os.mkdir(orient_dir)
140
+
141
+
142
+ locdir = orient_dir+"LocData/"
143
+ if(not os.path.exists(locdir)):
144
+ os.mkdir(locdir)
145
+
146
+ memdir = orient_dir+"Membrane_Data/"
147
+ if(not os.path.exists(memdir)):
148
+ os.mkdir(memdir)
149
+
150
+ timer = time.time()
151
+ tot_time = timer
152
+
153
+ #Some useful functions
154
+
155
+ @jax.jit
156
+ def flat_ind(ind1,ind2,max_ind1):
157
+ return ind2*max_ind1+ind1
158
+ @jax.jit
159
+
160
+ def unflat_ind(indf,max_ind1):
161
+ ind1 = indf%max_ind1
162
+ ind2 = jnp.array((indf-ind1)/max_ind1,dtype=int)
163
+ return ind1,ind2
164
+
165
+ @jax.jit
166
+ def put_into_grid(x,r,n,s):
167
+ cx = x[0]-s[0]
168
+ cy = x[1]-s[1]
169
+ norm_x = cx/r[0]
170
+ norm_y = cy/r[1]
171
+ grid_x = n[0]*norm_x
172
+ grid_y = n[1]*norm_y
173
+ return jnp.array(jnp.floor(grid_x),dtype=int),jnp.array(jnp.floor(grid_y),dtype=int)
174
+
175
+ twopowsixth = jnp.power(2,1/6)
176
+
177
+ def write_point(points,fn):
178
+ new_file = open(fn,"w")
179
+ count = 0
180
+ for i in points:
181
+ count += 1
182
+ count_str = (6-len(str(count)))*" "+str(count)
183
+ c = "ATOM "+count_str+" BB DUM 1 0.000 0.000 15.000 1.00 0.00"
184
+ xp = np.format_float_positional(i[0],precision=3)
185
+ yp = np.format_float_positional(i[1],precision=3)
186
+ zp = np.format_float_positional(i[2],precision=3)
187
+ xp += "0"*(3-len((xp.split(".")[1])))
188
+ yp += "0"*(3-len((yp.split(".")[1])))
189
+ zp += "0"*(3-len((zp.split(".")[1])))
190
+ new_c = c[:30]+(" "*(8-len(xp)))+xp+(" "*(8-len(yp)))+yp+(" "*(8-len(zp))) +zp+c[54:]+"\n"
191
+ new_file.write(new_c)
192
+ new_file.close()
193
+
194
+ def write_protein(bfacs,fn,ofn):
195
+ new_file = open(fn,"r")
196
+ n_lines = new_file.readlines()
197
+ new_file.close()
198
+ out_file = open(ofn,"w")
199
+ bc = 0
200
+ for c in n_lines:
201
+ if len(c) > 54 and "[" not in c and c.startswith("ATOM"):
202
+ bbp = np.format_float_positional(bfacs[map_to_beads[bc]],precision=3)
203
+ bbp += "0"*(3-len((bbp.split(".")[1])))
204
+ new_c = c[:60]+(" "*(8-len(bbp)))+bbp+c[68:]
205
+ bc+=1
206
+ out_file.write(new_c)
207
+ else:
208
+ out_file.write(c)
209
+ out_file.close()
210
+
211
+
212
+ #lennard jones function with values <r cutoff
213
+ @jax.jit
214
+ def lj(r,min_pos):
215
+ ret_val = 0
216
+ def lmp(r):
217
+ return min_pos*twopowsixth
218
+ def nlmp(r):
219
+ return r
220
+ r = jax.lax.cond(r<min_pos*twopowsixth,lmp,nlmp,r)
221
+ ret_val = 4*(jnp.power((min_pos/r),12)-jnp.power((min_pos/r),6))
222
+ return ret_val
223
+
224
+
225
+ def get_loop_inds(n,nx,ny):
226
+ indsx = np.arange(nx-2*n+2)+(n-1)
227
+ indsy = np.arange(ny-2*n+2)+(n-1)
228
+ y0 = np.zeros(nx-2*n+2)+indsy[0]
229
+ yn = np.zeros(nx-2*n+2)+indsy[-1]
230
+ p0 = np.zeros(nx-2*n+2)-1
231
+ pp0 = np.zeros(nx-2*n+2)
232
+ pp0[0] += 1
233
+ pp0[-1] -= 1
234
+ pn = np.zeros(nx-2*n+2)+1
235
+ line1 = np.concatenate((indsx[:,None],y0[:,None],pp0[:,None],pn[:,None]),axis=1)
236
+ line2 = np.concatenate((indsx[:,None],yn[:,None],pp0[:,None],p0[:,None]),axis=1)
237
+ x0 = np.zeros(ny-2*n)+indsx[0]
238
+ xn = np.zeros(ny-2*n)+indsx[-1]
239
+ p0 = np.zeros(ny-2*n)-1
240
+ pp0 = np.zeros(ny-2*n)
241
+ pn = np.zeros(ny-2*n)+1
242
+ line3 = np.concatenate((x0[:,None],indsy[1:-1,None],pn[:,None],pp0[:,None]),axis=1)
243
+ line4 = np.concatenate((xn[:,None],indsy[1:-1,None],p0[:,None],pp0[:,None]),axis=1)
244
+ return np.concatenate((line1,line2,line3,line4))
245
+
246
+ def get_all_loop_inds(k,nx,ny):
247
+ all_inds = np.empty((0,4))
248
+ for i in range(k):
249
+ all_inds = np.concatenate((get_loop_inds(i+1,nx,ny),all_inds))
250
+ return all_inds
251
+
252
+ def get_new_zs(new_zs,loop_inds):
253
+ nx = new_zs.shape[0]
254
+ ny = new_zs.shape[1]
255
+ def loop1(new_zs,ind):
256
+ inder = jnp.array(loop_inds[ind],dtype=int)
257
+ ix = inder[0]
258
+ iy = inder[1]
259
+ inderm1 = inder.at[:2].set(inder[:2]+inder[2:])
260
+ ixl = inderm1[0]
261
+ iyl = inderm1[1]
262
+ new_zs = new_zs.at[ix,iy,0].set(new_zs[ix,iy,0]+new_zs[ixl,iyl,0])
263
+ return new_zs , ind
264
+ new_zs,_ = jax.lax.scan(loop1,new_zs,jnp.arange(loop_inds.shape[0]))
265
+ return new_zs
266
+
267
+
268
+
269
+
270
+
271
+
272
+ #Sigmoid function
273
+ @jax.jit
274
+ def sj(x,grad):
275
+ ret_val = -x*grad
276
+ def overflow_pos(ret_val):
277
+ ret_val = 100.0
278
+ return ret_val
279
+ def overflow_neg(ret_val):
280
+ ret_val = -100.0
281
+ return ret_val
282
+ def not_overflow(ret_val):
283
+ return ret_val
284
+
285
+ ret_val = jax.lax.cond(-x*grad>100,overflow_pos,not_overflow,ret_val)
286
+ ret_val = jax.lax.cond(-x*grad<-100,overflow_neg,not_overflow,ret_val)
287
+ return 1.0/(1.0+jnp.exp(ret_val))
288
+
289
+
290
+ #Two variations of the mean field function
291
+ @jax.jit
292
+ def smj(x,grad,bead_num,mem_structure):
293
+ l1min = (W_B_mins[bead_num]+LH1_B_mins[bead_num]-jnp.abs(W_B_mins[bead_num]-LH1_B_mins[bead_num]))/2
294
+ l2min = (W_B_mins[bead_num]+LH2_B_mins[bead_num]-jnp.abs(W_B_mins[bead_num]-LH2_B_mins[bead_num]))/2
295
+ l3min = (W_B_mins[bead_num]+LH3_B_mins[bead_num]-jnp.abs(W_B_mins[bead_num]-LH3_B_mins[bead_num]))/2
296
+ bd1 = (1.0-sj(x-mem_structure[0],grad))*W_B_mins[bead_num]+sj(x-mem_structure[0],grad)*l1min
297
+ bd2 = (1.0-sj(x-mem_structure[1],grad))*bd1+sj(x-mem_structure[1],grad)*l2min
298
+ bd3 = (1.0-sj(x-mem_structure[2],grad))*bd2+sj(x-mem_structure[2],grad)*l3min
299
+ bd4 = (1.0-sj(x-mem_structure[3],grad))*bd3+sj(x-mem_structure[3],grad)*LT1_B_mins[bead_num]
300
+ bd5 = (1.0-sj(x-mem_structure[4],grad))*bd4+sj(x-mem_structure[4],grad)*l3min
301
+ bd6 = (1.0-sj(x-mem_structure[5],grad))*bd5+sj(x-mem_structure[5],grad)*l2min
302
+ bd7 = (1.0-sj(x-mem_structure[6],grad))*bd6+sj(x-mem_structure[6],grad)*l1min
303
+ bd8 = (1.0-sj(x-mem_structure[7],grad))*bd7+sj(x-mem_structure[7],grad)*W_B_mins[bead_num]
304
+ return bd8-W_B_mins[bead_num]
305
+
306
+
307
+ @jax.jit
308
+ def smjh(x,grad,bead_num,mem_structure):
309
+ bd1 = (1.0-sj(x-mem_structure[0],grad))*W_B_mins[bead_num]+sj(x-mem_structure[0],grad)*LH1_B_mins[bead_num]
310
+ bd2 = (1.0-sj(x-mem_structure[1],grad))*bd1+sj(x-mem_structure[1],grad)*LH2_B_mins[bead_num]
311
+ bd3 = (1.0-sj(x-mem_structure[2],grad))*bd2+sj(x-mem_structure[2],grad)*LH3_B_mins[bead_num]
312
+ bd4 = (1.0-sj(x-mem_structure[3],grad))*bd3+sj(x-mem_structure[3],grad)*LT1_B_mins[bead_num]
313
+ bd5 = (1.0-sj(x-mem_structure[4],grad))*bd4+sj(x-mem_structure[4],grad)*LH3_B_mins[bead_num]
314
+ bd6 = (1.0-sj(x-mem_structure[5],grad))*bd5+sj(x-mem_structure[5],grad)*LH2_B_mins[bead_num]
315
+ bd7 = (1.0-sj(x-mem_structure[6],grad))*bd6+sj(x-mem_structure[6],grad)*LH1_B_mins[bead_num]
316
+ bd8 = (1.0-sj(x-mem_structure[7],grad))*bd7+sj(x-mem_structure[7],grad)*W_B_mins[bead_num]
317
+ return bd8-W_B_mins[bead_num]
318
+
319
+ #Creates a list containing the membrane structure for a given membrane thickness
320
+ def get_mem_struc(mt):
321
+ memt_tails = mt
322
+ memt_heads = 10.0
323
+ memt_total = memt_tails+memt_heads
324
+
325
+ h1_w = memt_heads/6.0
326
+ h2_w = memt_heads/6.0
327
+ h3_w = memt_heads/6.0
328
+
329
+ l_w = memt_tails
330
+ meml = -l_w/2.0 -h1_w -h2_w-h3_w
331
+ mem_structure = jnp.array([meml,meml+h1_w,meml+h2_w+h1_w,meml+h2_w+h1_w+h3_w,meml+h2_w+h1_w+h3_w+l_w,meml+h2_w+h1_w+2*h3_w+l_w,meml+2*h2_w+h1_w+2*h3_w+l_w,meml+2*h2_w+2*h1_w+2*h3_w+l_w])
332
+ return mem_structure
333
+
334
+ #A fucntion that reads a martini file to get interaction strengths
335
+ def get_int_strength(bead_1,bead_2,martini_file):
336
+ if(bead_2 == "GHOST"):
337
+ return 0
338
+ string = " "*(6-len(bead_1))+bead_1+" "*(6-len(bead_2))+bead_2
339
+ string2 = " "*(6-len(bead_2))+bead_2+" "*(6-len(bead_1))+bead_1
340
+ mfile = open(martini_file,"r")
341
+ content = mfile.readlines()
342
+ for i,line in enumerate(content):
343
+ if(string in line or string2 in line):
344
+ return -float(line[32:45])
345
+
346
+ #Gets the interations between protein beads and the enviroment. This is used to create the mean fields
347
+ def get_mem_def(martini_file):
348
+ Beadtype_names = list(Beadtype.keys())
349
+ no_beadtypes = len(Beadtype_names)
350
+ #We use interactions strengths from martini using a POPE(Q4p)/POPG(P4)/POPC(Q1)? lipid as a template
351
+ W_B_mins = jnp.array([get_int_strength("W",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
352
+ LH1_B_mins = jnp.array([get_int_strength("P4",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
353
+ LH2_B_mins = jnp.array([get_int_strength("Q5",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
354
+ LH3_B_mins = jnp.array([get_int_strength("SN4a",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
355
+ LH4_B_mins = jnp.array([get_int_strength("N4a",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
356
+ LT1_B_mins = jnp.array([get_int_strength("C1",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
357
+ LT2_B_mins = jnp.array([get_int_strength("C4h",Beadtype_names[i],martini_file)-get_int_strength("W",Beadtype_names[i],martini_file) for i in range(no_beadtypes)])
358
+ Charge_B_mins =jnp.array([0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],dtype="float64")
359
+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 # #
360
+ return (W_B_mins,LH1_B_mins,LH2_B_mins,LH3_B_mins,LH4_B_mins,LT1_B_mins,LT2_B_mins,Charge_B_mins)
361
+
362
+ #Gets the n closest neighbours in a grid
363
+ def get_nbh_inds(inder,nx,ny,n):
364
+ ix,iy = unflat_ind(inder,nx)
365
+
366
+ defs = jnp.arange(2*n+1)-n
367
+ nbh_inds = jnp.zeros(defs.shape[0]*defs.shape[0])
368
+ def l1(nbh_inds,ind):
369
+ ind1 = ind
370
+ def l2(nbh_inds,ind):
371
+ new_x = ix+defs[ind1]
372
+ new_y = iy+defs[ind]
373
+ def wrap_up(new_ind,ind_x):
374
+ return new_ind+ind_x
375
+ def wrap_down(new_ind,ind_x):
376
+ return new_ind-ind_x
377
+ def nothing(new_ind,ind_x):
378
+ return new_ind
379
+
380
+ new_x = jax.lax.cond(new_x>=nx,wrap_down,nothing,new_x,nx)
381
+ new_y = jax.lax.cond(new_y>=ny,wrap_down,nothing,new_y,ny)
382
+ new_x = jax.lax.cond(new_x<0,wrap_up,nothing,new_x,nx)
383
+ new_y = jax.lax.cond(new_y<0,wrap_up,nothing,new_y,ny)
384
+
385
+ nbh_inds = nbh_inds.at[ind1*defs.shape[0]+ind].set(flat_ind(new_x,new_y,nx))
386
+ return nbh_inds,ind
387
+ nbh_inds,_=jax.lax.scan(l2,nbh_inds,jnp.arange(defs.shape[0]))
388
+ return nbh_inds,ind
389
+ nbh_inds,_=jax.lax.scan(l1,nbh_inds,jnp.arange(defs.shape[0]))
390
+ return nbh_inds
391
+
392
+ #Writes the deformations pdb file
393
+ def write_pdb(start_z,grid_in,grid_ban,nx,ny,exx,exy,gc,defo_dir,locdir,memdir,bfacs,memdata,rot_mat_inv):
394
+ BanGrid = np.zeros((nx,ny,2))
395
+ Data = np.zeros((nx,ny,2))
396
+ new_file = open(defo_dir+"Deformations.pdb","w")
397
+ grid_in = np.array(grid_in)
398
+ count = 0
399
+ xs = np.linspace(-exx,exx,nx)
400
+ ys = np.linspace(-exy,exy,ny)
401
+ np.savez(locdir+"Xlin.npz",xs)
402
+ np.savez(locdir+"Ylin.npz",ys)
403
+ np.savez(memdir+"Xlin.npz",xs)
404
+ np.savez(memdir+"Ylin.npz",ys)
405
+ for i in range(nx):
406
+ for j in range(ny):
407
+ if(grid_ban[i,j] == 1):
408
+ ms = get_mem_struc(grid_in[i,j,1])
409
+ ms_av = get_mem_struc(grid_in[0,0,1])
410
+ z_pluss = [ms[0],ms[7]]
411
+ z_pluss_av = [ms_av[0],ms_av[7]]
412
+ for nn,zz in enumerate(z_pluss):
413
+ pm1 = (nn-0.5)*2
414
+ zp = zz+start_z/5
415
+ z_dev = np.mean(grid_in[0,:,0]+z_pluss_av[nn]+start_z/5)
416
+ zp_vpos = jnp.array(((grid_in[i,j,0]+zp+vext)/(2*vext))*vert_grid_size,dtype=int)
417
+ zp_vposc = jnp.array(((grid_in[i,j,0]+vext)/(2*vext))*vert_grid_size,dtype=int)
418
+ if(zp_vpos < zp_vposc):
419
+ lb = zp_vpos
420
+ rb = zp_vposc
421
+ else:
422
+ lb = zp_vposc
423
+ rb = zp_vpos
424
+ lenny = rb-lb
425
+ if(lb > 49):
426
+ lb = 49
427
+ if(rb > 49):
428
+ rb=49
429
+ if(lb<0):
430
+ lb=0
431
+ if(rb<0):
432
+ rb=0
433
+ covp = jnp.sum(bgrid_o[i,j,lb:rb])/lenny
434
+ if(covp > 0.6):
435
+ BanGrid[i,j,nn] = 1
436
+ Data[i,j,nn] = grid_in[i,j,0]+zp
437
+ if(bfacs):
438
+ bbp = np.format_float_positional(gc[i,j,nn],precision=3)
439
+ else:
440
+ bbp = np.format_float_positional((grid_in[i,j,0]+zp-z_dev)*pm1,precision=3)
441
+ bbp += "0"*(3-len((bbp.split(".")[1])))
442
+ count += 1
443
+ count_str = (6-len(str(count)))*" "+str(count)
444
+ c = "ATOM "+count_str+" BB DUM Z 1 0.000 0.000 15.000 1.00 0.00"
445
+
446
+ pos = np.array([xs[i],ys[j],0])
447
+ pos = np.dot(rot_mat_inv,pos)
448
+ xp = np.format_float_positional(pos[0],precision=3)
449
+ yp = np.format_float_positional(pos[1],precision=3)
450
+ zp = np.format_float_positional(grid_in[i,j,0]+zp,precision=3)
451
+ xp += "0"*(3-len((xp.split(".")[1])))
452
+ yp += "0"*(3-len((yp.split(".")[1])))
453
+ zp += "0"*(3-len((zp.split(".")[1])))
454
+ new_c = c[:30]+(" "*(8-len(xp)))+xp+(" "*(8-len(yp)))+yp+(" "*(8-len(zp))) +zp+c[54:60]+(" "*(8-len(bbp)))+bbp+c[66:]+"\n"
455
+ new_file.write(new_c)
456
+ new_file.close()
457
+ np.savez(locdir+"BotBan.npz",BanGrid[:,:,0])
458
+ np.savez(locdir+"TopBan.npz",BanGrid[:,:,1])
459
+ np.savez(locdir+"BotData.npz",Data[:,:,0])
460
+ np.savez(locdir+"TopData.npz",Data[:,:,1])
461
+ return BanGrid[:,:,0],BanGrid[:,:,1]
462
+
463
+ #calcualtes the eleectrostatic force using below
464
+ def calc_esf(start_z,grid_in,grid_ban,nx,ny,exx,exy,cposes,ctypes):
465
+ grid_in = np.array(grid_in)
466
+ grid_charge = np.zeros((nx,ny,2))
467
+ count = 0
468
+ xs = np.linspace(-exx,exx,nx)
469
+ ys = np.linspace(-exy,exy,ny)
470
+ for i in range(nx):
471
+ for j in range(ny):
472
+ if(grid_ban[i,j] == 1):
473
+ ms = get_mem_struc(grid_in[i,j,1])
474
+ z_pluss = [ms[0],ms[7]]
475
+ for nn,zz in enumerate(z_pluss):
476
+ zp = zz+start_z/5
477
+ x_pos = xs[i]
478
+ y_pos = ys[j]
479
+ z_pos = grid_in[i,j,0]+zp
480
+ grid_charge[i][j][nn] = calc_esf_p(jnp.array([x_pos,y_pos,z_pos]),cposes,ctypes)
481
+ return grid_charge
482
+
483
+ #calcues the electrostatic force
484
+ @jax.jit
485
+ def calc_esf_p(pos,cposes,ctypes):
486
+ ctypes = jnp.array(ctypes,dtype=int)
487
+ charge_const = 92.64
488
+ Charge_B_mins =jnp.array([0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],dtype="float64")
489
+ def esf_loop(carry,ind):
490
+ charge_cont = Charge_B_mins[ctypes[ind]]*charge_const/jnp.linalg.norm(pos-cposes[ind])
491
+ carry += charge_cont
492
+ return carry,ind
493
+ tot_c,_ = jax.lax.scan(esf_loop,0,jnp.arange(cposes.shape[0]))
494
+ return tot_c
495
+
496
+
497
+
498
+
499
+
500
+ #Creating a helper class that deals with loading the PDB files
501
+ PDB_helper_test = ori.PDB_helper(fn,False,0,np.array([]),args.no_cavity_surface,True)
502
+
503
+ #Loading PDB
504
+ pos_mean = PDB_helper_test.load_pdb()
505
+
506
+
507
+ #Getting surface
508
+ print("Getting surface residues...")
509
+ timer = time.time()
510
+ jax.block_until_ready(PDB_helper_test.get_surface())
511
+ PDB_helper_test.recenter_bvals()
512
+ data = PDB_helper_test.get_data()
513
+
514
+ Beadtype = ori.Beadtype
515
+ int_data = get_mem_def(martini_file)
516
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
517
+
518
+ #storing important data
519
+ surf_poses = data[2]+pos_mean
520
+ poses = data[5]+pos_mean
521
+ surf_bvals = data[4]
522
+ bead_types = data[3]
523
+ surface_no = data[0]
524
+ map_to_beads = data[9]
525
+
526
+
527
+ #Rotates the protein to minimise the overall size of the grid. This improves efficiency for wide proteins.
528
+ print("Optimising grid...")
529
+ timer = time.time()
530
+
531
+ angs = jnp.linspace(0,jnp.pi,200)
532
+ def min_extent(carry,ind):
533
+ nang = angs[ind]
534
+ rot_mat = jnp.array([[jnp.cos(nang),-jnp.sin(nang),0],[jnp.sin(nang),jnp.cos(nang),0],[0,0,1]])
535
+ test_surf_poses = jnp.dot(rot_mat,poses.T).T
536
+ x_ext = jnp.maximum(-jnp.min(test_surf_poses[:,0]),jnp.max(test_surf_poses[:,0]))
537
+ y_ext = jnp.maximum(-jnp.min(test_surf_poses[:,1]),jnp.max(test_surf_poses[:,1]))
538
+ def lower(carry):
539
+ carry[0] = nang
540
+ carry[1] = x_ext*y_ext
541
+ return carry
542
+ def higher(carry):
543
+ return carry
544
+ carry = jax.lax.cond(carry[1] > x_ext*y_ext,lower,higher,carry)
545
+ return carry,ind
546
+
547
+ carry,_ = jax.lax.scan(min_extent,[0.0,jnp.inf],jnp.arange(200))
548
+ nang = carry[0]
549
+ rot_mat = jnp.array([[jnp.cos(nang),-jnp.sin(nang),0],[jnp.sin(nang),jnp.cos(nang),0],[0,0,1]])
550
+ rot_mat_inv = jnp.array([[jnp.cos(-nang),-jnp.sin(-nang),0],[jnp.sin(-nang),jnp.cos(-nang),0],[0,0,1]])
551
+ surf_poses = jnp.dot(rot_mat,surf_poses.T).T
552
+ poses = jnp.dot(rot_mat,poses.T).T
553
+
554
+
555
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
556
+ timer = time.time()
557
+
558
+ #Getting the chared residues for charge calculations later
559
+ print("Getting charged residues...")
560
+
561
+ surf_cposes = jnp.zeros((surf_poses.shape[0],3))
562
+ cbead_types = jnp.zeros(bead_types.shape[0])
563
+
564
+ def cloop(carry,ind):
565
+ def charged(carry):
566
+ new0 = carry[0].at[carry[2]].set(surf_poses[ind])
567
+ new1 = carry[1].at[carry[2]].set(bead_types[ind])
568
+ new2 = carry[2] + 1
569
+ return (new0,new1,new2)
570
+ def ncharged(carry):
571
+ return carry
572
+ carry = jax.lax.cond((bead_types[ind] == 12)+(bead_types[ind] == 13)+(bead_types[ind] == 16)+(bead_types[ind] == 17),charged,ncharged,carry)
573
+ return carry,ind
574
+
575
+ carry = (surf_cposes,cbead_types,0)
576
+ carry,_ = jax.lax.scan(cloop,carry,jnp.arange(surf_poses.shape[0]))
577
+ cposes = carry[0][:carry[2]]
578
+ ctypes = carry[1][:carry[2]]
579
+
580
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
581
+
582
+
583
+ #setting up variables used later
584
+
585
+ vert_grid_size = 50
586
+ vext = 50
587
+ vlin = jnp.linspace(-vext,vext,vert_grid_size)
588
+
589
+ indexer = np.nonzero((np.array(surf_poses[:,2]) < 50)*(np.array(surf_poses[:,2] > -50)))
590
+
591
+ surf_poses = surf_poses[indexer]
592
+ surf_bvals = surf_bvals[indexer]
593
+ bead_types = bead_types[indexer]
594
+
595
+ indexer2 = np.nonzero((np.array(poses[:,2]) < 50)*(np.array(poses[:,2] > -50)))
596
+
597
+ all_poses = poses.copy()
598
+ poses = poses[indexer2]
599
+
600
+ cutoff = int(args.cutoff)
601
+
602
+ x_ext = max(-jnp.min(surf_poses[:,0]),jnp.max(surf_poses[:,0]))+25
603
+ y_ext = max(-jnp.min(surf_poses[:,1]),jnp.max(surf_poses[:,1]))+25
604
+ grid_den = float(args.grid_density)
605
+
606
+ rad = int(4.0/grid_den)
607
+
608
+ grid_size_x = int(2*x_ext/grid_den)
609
+ grid_size_y = int(2*y_ext/grid_den)
610
+
611
+ loop_inds = jnp.array(get_all_loop_inds(12,grid_size_x,grid_size_y))
612
+ loop_inds_end = np.array(get_loop_inds(1,grid_size_x,grid_size_y),dtype=int)
613
+
614
+ nxx = jnp.array(x_ext/cutoff,dtype=int)
615
+ nyy = jnp.array(y_ext/cutoff,dtype=int)
616
+
617
+ no_beads = surf_poses.shape[0]
618
+
619
+ #Putting beads into bins for more efficient allocation of beads to membrane segments later on
620
+ print("Binning surface... (This could take a short while)")
621
+ timer = time.time()
622
+ @jax.jit
623
+ def allo_bead(surf_poses):
624
+ no_beads = surf_poses.shape[0]
625
+ big_grid = jnp.zeros((grid_size_x,grid_size_y,no_beads+1),dtype=int)-1
626
+ big_grid = big_grid.at[:,:,no_beads].set(0)
627
+ def allocacte_beads(big_grid,ind):
628
+ bind = ind
629
+ x_pos = surf_poses[bind][0]
630
+ y_pos = surf_poses[bind][1]
631
+ xgrid,ygrid = put_into_grid([x_pos,y_pos],[2*x_ext,2*y_ext],[nxx,nyy],[-x_ext,-y_ext])
632
+ big_grid = big_grid.at[xgrid,ygrid,big_grid[xgrid,ygrid,-1]].set(bind)
633
+ big_grid = big_grid.at[xgrid,ygrid,-1].set(big_grid[xgrid,ygrid,-1]+1)
634
+ return big_grid,ind
635
+ big_grid,_ = jax.lax.scan(allocacte_beads,big_grid,jnp.arange(no_beads))
636
+ return big_grid
637
+
638
+ big_grid = allo_bead(surf_poses)
639
+ big_gridb = allo_bead(poses)
640
+
641
+ max_beads = jnp.max(big_grid[:,:,-1])
642
+
643
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
644
+ timer = time.time()
645
+
646
+ #Allocating beads to membrane segments for use when calculating the potential. This takes some time and so is pre-calculated.
647
+ print("Allocating beads to membrane segments... (This could take a short while)")
648
+
649
+ xs = jnp.linspace(-x_ext,x_ext,grid_size_x)
650
+ ys = jnp.linspace(-y_ext,y_ext,grid_size_y)
651
+ indxes = jnp.zeros((grid_size_x,grid_size_y,2),dtype=int)
652
+ def indloop1(indxes,ind):
653
+ def indloop2(indxes,ind2):
654
+ indxes = indxes.at[ind,ind2].set(jnp.array([ind,ind2]))
655
+ return indxes,ind2
656
+ indxes,_ = jax.lax.scan(indloop2,indxes,jnp.arange(grid_size_y))
657
+ return indxes,ind
658
+ indxes,_=jax.lax.scan(indloop1,indxes,jnp.arange(grid_size_x))
659
+
660
+ indxes = indxes.reshape((grid_size_x*grid_size_y,2))
661
+
662
+ def allo_grid_vmap(cutoff_val,big_grid,surf_poses):
663
+ max_beads = jnp.max(big_grid[:,:,-1])
664
+ @partial(jax.vmap,in_axes=0)
665
+ def agrid_vmap(inds):
666
+ grid_p = jnp.zeros((no_beads+1,2),dtype=float)-1
667
+ grid_p = grid_p.at[no_beads,:].set(0)
668
+ ind2 = inds[0]
669
+ ind3 = inds[1]
670
+ indx,indy = put_into_grid([xs[ind2],ys[ind3]],[2*x_ext,2*y_ext],[nxx,nyy],[-x_ext,-y_ext])
671
+ sq = jnp.array([[0,0],[-1,0],[1,0],[0,-1],[0,1],[-1,1],[-1,-1],[1,1],[1,-1]],dtype=int)
672
+ def allocate_grid3(grid_p,ind):
673
+ def allocate_grid4(grid_p,sind):
674
+ bgind = big_grid[(indx+sq[sind,0])%grid_size_x,(indy+sq[sind,1])%grid_size_y][ind]
675
+ def close(grid_p,dist):
676
+ indeex = jnp.array(grid_p[-1,0],dtype=int)
677
+ grid_p = grid_p.at[indeex,0].set(bgind)
678
+ grid_p = grid_p.at[indeex,1].set(dist)
679
+ grid_p = grid_p.at[-1,0].set(grid_p[-1,0]+1)
680
+ return grid_p
681
+ def far(grid_p,dist):
682
+ return grid_p
683
+ def is_bead(grid_p):
684
+ pos = surf_poses[bgind][0:2]
685
+ grid_pos = jnp.array([xs[ind2],ys[ind3]])
686
+ dist = jnp.linalg.norm(pos-grid_pos)
687
+ grid_p = jax.lax.cond(dist < cutoff_val, close,far,grid_p,dist)
688
+ return grid_p
689
+ def isn_bead(grid_p):
690
+ return grid_p
691
+ grid_p= jax.lax.cond(bgind != -1,is_bead,isn_bead,grid_p)
692
+ return grid_p,sind
693
+ grid_p,_ = jax.lax.scan(allocate_grid4,grid_p,jnp.arange(9))
694
+ return grid_p,ind
695
+ grid_p,_ = jax.lax.scan(allocate_grid3,grid_p,jnp.arange(max_beads))
696
+ return grid_p
697
+ grid_p = agrid_vmap(indxes)
698
+ grid = grid_p.reshape((grid_size_x,grid_size_y,no_beads+1,2))
699
+ return grid
700
+
701
+ grid = allo_grid_vmap(cutoff,big_grid,surf_poses).block_until_ready()
702
+ grid_block = allo_grid_vmap(3.0,big_gridb,poses)
703
+
704
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
705
+ timer = time.time()
706
+
707
+ #Creating a smooth function that indicates if a point is with the protein or not.
708
+ print("Blocking membrane inside protein...")
709
+
710
+ max_beads_block = jnp.array(jnp.max(grid_block[:,:,-1,0]),dtype=int)
711
+ bgrid = jnp.zeros((grid_size_x,grid_size_y,vert_grid_size))+1
712
+
713
+ def gblock1(bgrid,ind):
714
+ ind2 = ind
715
+ def gblock2(bgrid,ind):
716
+ ind3 = ind
717
+ def bloop(bgrid,ind):
718
+ bind = jnp.array(grid_block[ind2,ind3,ind,0],dtype=int)
719
+ def mins(bgrid):
720
+ return bgrid
721
+ def nmins(bgrid):
722
+ atom_pos = poses[bind,2]
723
+ indap = jnp.array(((atom_pos+vext)/(2*vext))*vert_grid_size,dtype=int)
724
+ bgrid = bgrid.at[ind2,ind3,indap].set(0)
725
+ bgrid = bgrid.at[ind2,ind3,indap-1].set(0)
726
+ bgrid = bgrid.at[ind2,ind3,indap+1].set(0)
727
+ return bgrid
728
+ bgrid = jax.lax.cond(bind != -1,nmins,mins,bgrid)
729
+ return bgrid,ind
730
+ bgrid,_ = jax.lax.scan(bloop,bgrid,jnp.arange(max_beads_block))
731
+ return bgrid,ind
732
+ bgrid,_ = jax.lax.scan(gblock2,bgrid,jnp.arange(grid_size_y))
733
+ return bgrid,ind
734
+ bgrid,_ = jax.lax.scan(gblock1,bgrid,jnp.arange(grid_size_x))
735
+
736
+
737
+ mem_structure = get_mem_struc(mem_data[1])
738
+
739
+ def accessable(grid):
740
+ ngrid = jnp.zeros_like(grid)
741
+ def aloop1(ngrid,ind1):
742
+ def aloop2(ngrid,ind2):
743
+ t1 = jnp.sum(jnp.array([grid[ind1,ind2],grid[ind1-1,ind2],grid[ind1-1,ind2-1],grid[ind1,ind2-1]]))
744
+ t2 = jnp.sum(jnp.array([grid[ind1,ind2],grid[ind1+1,ind2],grid[ind1+1,ind2-1],grid[ind1,ind2-1]]))
745
+ t3 = jnp.sum(jnp.array([grid[ind1,ind2],grid[ind1-1,ind2],grid[ind1-1,ind2+1],grid[ind1,ind2+1]]))
746
+ t4 = jnp.sum(jnp.array([grid[ind1,ind2],grid[ind1+1,ind2],grid[ind1+1,ind2+1],grid[ind1,ind2+1]]))
747
+ def good(ngrid):
748
+ ngrid = ngrid.at[ind1,ind2].set(1)
749
+ return ngrid
750
+ def bad(ngrid):
751
+ return ngrid
752
+ ngrid = jax.lax.cond(jnp.logical_or(jnp.logical_or(t1==4,t2==4),jnp.logical_or(t3==4,t4==4)),good,bad,ngrid)
753
+ return ngrid,ind2
754
+ ngrid,_= jax.lax.scan(aloop2,ngrid,jnp.arange(ngrid.shape[1]))
755
+ return ngrid,ind1
756
+ ngrid,_ = jax.lax.scan(aloop1,ngrid,jnp.arange(ngrid.shape[0]))
757
+ return ngrid
758
+
759
+ grid_ban = jnp.zeros((grid_size_x,grid_size_y),dtype=int)+1
760
+ @jax.jit
761
+ def fill_mid(grid_ban,tol):
762
+ rdic_carry = jnp.zeros((grid_size_x*grid_size_y,3))
763
+ numer = grid_size_x*grid_size_y
764
+ rdic_carry = rdic_carry.at[0,0].set(1)
765
+ def remove_disc(rdic_carry,ind):
766
+ def check(rdic_carry):
767
+ rdic_carry = rdic_carry.at[ind,2].set(1)
768
+ def is_block(rdic_carry):
769
+ return rdic_carry
770
+ def is_no_block(rdic_carry):
771
+ nb_inds = jnp.array(get_nbh_inds(ind,grid_size_x,grid_size_y,1),dtype=int)
772
+ rdic_carry = rdic_carry.at[nb_inds,0].set(rdic_carry[nb_inds,0]+1)
773
+ rdic_carry = rdic_carry.at[ind,1].set(1)
774
+ return rdic_carry
775
+ rdic_carry = jax.lax.cond(grid_ban[unflat_ind(ind,grid_size_x)]>tol,is_block,is_no_block,rdic_carry)
776
+ return rdic_carry
777
+ def no_check(rdic_carry):
778
+ return rdic_carry
779
+ rdic_carry = jax.lax.cond((rdic_carry[ind,0]>0)*(rdic_carry[ind,2]==0),check,no_check,rdic_carry)
780
+ return rdic_carry,ind
781
+
782
+ rdic_carry,_ = jax.lax.scan(remove_disc,rdic_carry,jnp.arange(numer))
783
+ rdic_carry,_ = jax.lax.scan(remove_disc,rdic_carry,-jnp.arange(numer)+(numer-1))
784
+ transp_arange = jnp.arange(numer).reshape(grid_size_x,grid_size_y)
785
+ transp_arange = transp_arange.T
786
+ transp_arange = transp_arange.reshape(numer)
787
+ rdic_carry,_ = jax.lax.scan(remove_disc,rdic_carry,transp_arange)
788
+ rdic_carry,_ = jax.lax.scan(remove_disc,rdic_carry,-transp_arange+(numer-1))
789
+ return rdic_carry[:,2].reshape(grid_size_y,grid_size_x).T
790
+
791
+ bgrid_o = jnp.zeros_like(bgrid)
792
+
793
+ def fill_out_all(carry,ind):
794
+ bgrid = carry[0]
795
+ bgrid_o = carry[1]
796
+ tempb = fill_mid(1-accessable(bgrid[:,:,ind]),0.5)
797
+ bgrid_o = bgrid_o.at[:,:,ind].set(tempb)
798
+ bgrid = bgrid.at[:,:,ind].set(fill_mid(1-tempb,0.5))
799
+ return (bgrid,bgrid_o),ind
800
+
801
+ carryb,_ = jax.lax.scan(fill_out_all,(bgrid,bgrid_o),jnp.arange(vert_grid_size))
802
+ bgrid = carryb[0]
803
+ bgrid_o = carryb[1]
804
+
805
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
806
+ timer = time.time()
807
+
808
+ #This function indicates if grid point (i,j) and height z is within the protein.
809
+ @jax.jit
810
+ def bgrid_weight(i,j,z):
811
+ zind = ((z+vext)/(2*vext))*vert_grid_size
812
+ zind_l = jnp.array(jnp.floor(zind),dtype=int)-5
813
+ tot = 0
814
+
815
+ def above(zindy):
816
+ return vert_grid_size-1
817
+ def below(zindy):
818
+ return 0
819
+ def nab(zindy):
820
+ return jnp.array(zindy,dtype=int)
821
+
822
+ def bwloop(tot,ind):
823
+ zind_u = zind_l+ind
824
+ zind_u = jax.lax.cond(zind_u > vert_grid_size-1,above,nab,zind_u)
825
+ zind_u = jax.lax.cond(zind_u < 0,below,nab,zind_u)
826
+ tot += bgrid[i,j,zind_u]
827
+
828
+ return tot,ind
829
+ tot,_ = jax.lax.scan(bwloop,tot,jnp.arange(10))
830
+
831
+ zind_u = jax.lax.cond(zind_l+10 > vert_grid_size-1,above,nab,zind_l+10)
832
+ zind_u = jax.lax.cond(zind_l+10 < 0,below,nab,zind_l+10)
833
+
834
+ zind_l = jax.lax.cond(zind_l > vert_grid_size-1,above,nab,zind_l)
835
+ zind_l = jax.lax.cond(zind_l < 0,below,nab,zind_l)
836
+
837
+ tot += (bgrid[i,j,zind_u]-bgrid[i,j,zind_l])*(zind-zind_l-5)
838
+ return (1-jnp.cos(jnp.pi*(tot/10)))/2.0
839
+
840
+ max_beads = jnp.array(jnp.max(grid[:,:,-1,0]),dtype=int)
841
+ W_B_mins = int_data[0]
842
+ LH1_B_mins = int_data[1]
843
+ LH2_B_mins = int_data[2]
844
+ LH3_B_mins = int_data[3]
845
+ LH4_B_mins = int_data[4]
846
+ LT1_B_mins = int_data[5]
847
+ LT2_B_mins = int_data[6]
848
+ Charge_B_mins =int_data[7]
849
+
850
+ #Prevents gaps between protein and membrane
851
+ def intbgrid(ind2,ind3,zt,mt,zd):
852
+ mem_structure = get_mem_struc(mt)
853
+ z_disp = zt+zd
854
+ logs = jnp.linspace(mem_structure[0],mem_structure[-1],10)
855
+ sumer = 0
856
+ def z_range(sumer,ind):
857
+ zr = logs[ind]+z_disp
858
+ sumer += bgrid_weight(ind2,ind3,zr)
859
+ return sumer, ind
860
+ sumer,_ =jax.lax.scan(z_range,sumer,jnp.arange(10))
861
+ return (sj(-sumer+4,3))*(-sumer-200)/5.0
862
+
863
+
864
+
865
+ #Potential function. If surface bead or membrane segment is within the protein it's contribution is ignored
866
+ def get_pot(ind2,ind3,zt,mt,zd):
867
+ no_beads = grid[ind2,ind3,-1,0]
868
+ z_disp = zt+zd
869
+ def calc_grid3(tot,ind):
870
+ mem_structure = get_mem_struc(mt)
871
+ bind = jnp.array(grid[ind2,ind3,ind,0],dtype=int)
872
+ dist = grid[ind2,ind3,ind,1]
873
+ def nomin(tot):
874
+ z_pos = surf_poses[bind][2]-z_disp
875
+ xy_pos = surf_poses[bind][0:2]
876
+ indx,indy = put_into_grid([xy_pos[0],xy_pos[1]],[2*x_ext,2*y_ext],[nxx,nyy],[-x_ext,-y_ext])
877
+ bead_num = bead_types[bind]
878
+ disterz = jnp.sqrt(dist*dist)
879
+ def close(tot):
880
+ def upper(tot):
881
+ tot = tot.at[0].set(tot[0]+smj(z_pos,0.5,bead_num,mem_structure)*-1*lj(disterz,8.0)*bgrid_weight(ind2,ind3,z_pos)*bgrid_weight(indx,indy,z_pos))
882
+ return tot
883
+ def lower(tot):
884
+ tot = tot.at[1].set(tot[1]+smj(z_pos,0.5,bead_num,mem_structure)*-1*lj(disterz,8.0)*bgrid_weight(ind2,ind3,z_pos)*bgrid_weight(indx,indy,z_pos))
885
+ return tot
886
+ tot = jax.lax.cond(z_pos>0,upper,lower,tot)
887
+ return tot
888
+ def far(tot):
889
+ return tot
890
+ tot = jax.lax.cond((dist<float(args.cutoff))*(dist > 0),close,far,tot)
891
+ return tot
892
+ def miner(tot):
893
+ return tot
894
+ tot = jax.lax.cond(bind > -1e-1,nomin,miner,tot)
895
+ return tot,ind
896
+ def edge():
897
+ return (zt*zt)*1e-7+((mt-mem_data[1])*(mt-mem_data[1]))*1e-7
898
+ def nedge():
899
+ return (zt*zt)*1e-6+((mt-mem_data[1])*(mt-mem_data[1]))*1e-6
900
+ s_val = jax.lax.cond(jnp.logical_or(jnp.logical_or(ind3==0,ind3==grid.shape[1]-1),jnp.logical_or(ind2==0,ind2==grid.shape[0]-1)),edge,nedge)
901
+ tot,_ = jax.lax.scan(calc_grid3,jnp.array([s_val,s_val]),jnp.arange(max_beads))
902
+ tot += intbgrid(ind2,ind3,zt,mt,zd)
903
+ return tot,no_beads
904
+
905
+ #Calculates contacts with head groups and acyl tails
906
+ def calc_contacts(new_zs,poses,zdisp,xs,ys):
907
+ bfacsH = jnp.zeros(poses.shape[0])
908
+ bfacsT = jnp.zeros(poses.shape[0])
909
+ mindd = 3
910
+ def ccloop1(bfacs,ind1):
911
+ def ccloop2(bfacs,ind2):
912
+ mt = new_zs[ind1,ind2,1]
913
+ z = new_zs[ind1,ind2,0]
914
+ pos1 = jnp.array([xs[ind1],ys[ind2],z-mt/2.0-4+zdisp])
915
+ pos2 = jnp.array([xs[ind1],ys[ind2],z+mt/2.0+4+zdisp])
916
+ def ccloop3(bfacs,ind3):
917
+ bfacsH = bfacs[0]
918
+ bfacsT = bfacs[1]
919
+ posprot = poses[ind3]
920
+ dist1 = jnp.linalg.norm(posprot-pos1)/10
921
+ dist2 = jnp.linalg.norm(posprot-pos2)/10
922
+
923
+ bfacsH = bfacsH.at[ind3].set(bfacsH[ind3]+jnp.exp(-jnp.max(jnp.array([dist1*dist1,mindd])))*bgrid_weight(ind1,ind2,z-mt/2.0+4+zdisp)+jnp.exp(-jnp.max(jnp.array([dist2*dist2,mindd])))*bgrid_weight(ind1,ind2,z-mt/2.0-4+zdisp))
924
+ zscan = jnp.linspace(pos1[2],pos2[2],40)
925
+ def ccloop4(bfacsT,ind4):
926
+ pos3 = pos1.copy()
927
+ pos3 = pos3.at[2].set(zscan[ind4])
928
+ dist3 = jnp.linalg.norm(posprot-pos3)/10
929
+ bfacsT = bfacsT.at[ind3].set(bfacsT[ind3]+jnp.exp(-jnp.max(jnp.array([dist3*dist3,mindd])))*bgrid_weight(ind1,ind2,zscan[ind4]))
930
+ return bfacsT,ind4
931
+ bfacsT,_ = jax.lax.scan(ccloop4,bfacsT,jnp.arange(40))
932
+ return (bfacsH,bfacsT),ind3
933
+ bfacs,_ = jax.lax.scan(ccloop3,bfacs,jnp.arange(poses.shape[0]))
934
+ return bfacs,ind2
935
+ bfacs,_ = jax.lax.scan(ccloop2,bfacs,jnp.arange(new_zs.shape[1]))
936
+ return bfacs,ind1
937
+ bfacs,_ = jax.lax.scan(ccloop1,(bfacsH,bfacsT),jnp.arange(new_zs.shape[0]))
938
+ return bfacs
939
+
940
+ PhtoW = get_int_strength("C1","W",martini_file)-get_int_strength("C1","W",martini_file)
941
+ PhtoPh = get_int_strength("C1","C1",martini_file)-get_int_strength("C1","W",martini_file)
942
+ PhtoH = get_int_strength("P4","C1",martini_file)-get_int_strength("C1","W",martini_file)
943
+ HtoH = get_int_strength("P4","P4",martini_file)-get_int_strength("C1","W",martini_file)
944
+ HtoW = get_int_strength("P4","W",martini_file)-get_int_strength("C1","W",martini_file)
945
+ WtoW = get_int_strength("W","W",martini_file)-get_int_strength("C1","W",martini_file)
946
+
947
+
948
+ #A smooth function that describes the membrane seegment (0 water, 1 head, 2 acyl tails)
949
+ @jax.jit
950
+ def lip_sm(x,grad,lower,upper):
951
+ bd1 = (1.0-sj(x-lower+5,grad))*2+sj(x-lower+5,grad)*1
952
+ bd2 = (1.0-sj(x-lower,grad))*bd1+sj(x-lower,grad)*0
953
+ bd3 = (1.0-sj(x-upper,grad))*bd2+sj(x-upper,grad)*1
954
+ bd4 = (1.0-sj(x-upper-5,grad))*bd3+sj(x-upper-5,grad)*2
955
+ return bd4
956
+
957
+ int_mat = jnp.array([[PhtoPh,PhtoH,PhtoW],[PhtoH,HtoH,HtoW],[PhtoW,HtoW,WtoW]])
958
+
959
+ #This function calculates the potential between two membrane segments via 3d linear interpolations using membrane descriptions as above
960
+ @jax.jit
961
+ def rpot_col(new_zs,inda1,indb1,inda2,indb2,dist,zd,k1):
962
+ dist = jnp.sqrt((inda1-inda2)*(inda1-inda2)+(indb1-indb2)*(indb1-indb2))
963
+ def close():
964
+
965
+
966
+ posi1 = new_zs[inda1,indb1]
967
+ upper1 = zd+posi1[0]+0.5*posi1[1]
968
+ lower1 = zd+posi1[0]-0.5*posi1[1]
969
+
970
+ posi2 = new_zs[inda2,indb2]
971
+ upper2 = zd+posi2[0]+0.5*posi2[1]
972
+ lower2 = zd+posi2[0]-0.5*posi2[1]
973
+
974
+ lupper = jnp.min(jnp.array([upper1,upper2]))
975
+ ulower = jnp.max(jnp.array([lower1,lower2]))
976
+
977
+ uupper = jnp.max(jnp.array([upper1,upper2]))
978
+ llower = jnp.min(jnp.array([lower1,lower2]))
979
+
980
+ poverlap = posi1[0]-posi2[0]
981
+ up_dist = upper1-upper2
982
+ lo_dist = lower1-lower2
983
+
984
+
985
+ def small():
986
+ return jnp.linspace(lower1,upper1,30)
987
+ def nsmall():
988
+ return jnp.linspace(lower2,upper2,30)
989
+
990
+ test1 = jax.lax.cond(upper1-lower1<upper2-lower2,small,nsmall)
991
+ test1 = jnp.concatenate((test1,jnp.array([lower1-2,upper1+2])))
992
+ test1 = jnp.concatenate((test1,jnp.array([lower2-2,upper2+2])))
993
+
994
+ overlap = 0
995
+ def rloop(overlap,ind):
996
+ mta = lip_sm(test1[ind],4,lower1,upper1)
997
+ mtb = lip_sm(test1[ind],4,lower2,upper2)
998
+ mtbL = jnp.floor(mtb)
999
+ mtaI = jnp.array(mta,dtype=int)
1000
+ partb = mtb-mtbL
1001
+ mtbU = jnp.ceil(mtb)
1002
+
1003
+ mtaL = jnp.floor(mta)
1004
+ mtbI = jnp.array(mtb,dtype=int)
1005
+ parta = mta-mtaL
1006
+ mtaU = jnp.ceil(mta)
1007
+
1008
+ intLL = int_mat[jnp.array(mtaL,dtype=int),jnp.array(mtbL,dtype=int)]
1009
+ intLU = int_mat[jnp.array(mtaL,dtype=int),jnp.array(mtbU,dtype=int)]
1010
+ intUL = int_mat[jnp.array(mtaU,dtype=int),jnp.array(mtbL,dtype=int)]
1011
+ intUU = int_mat[jnp.array(mtaU,dtype=int),jnp.array(mtbU,dtype=int)]
1012
+
1013
+ intLLS = int_mat[jnp.array(mtbL,dtype=int),jnp.array(mtbL,dtype=int)]
1014
+ intLUS = int_mat[jnp.array(mtbL,dtype=int),jnp.array(mtbU,dtype=int)]
1015
+ intULS = int_mat[jnp.array(mtbU,dtype=int),jnp.array(mtbL,dtype=int)]
1016
+ intUUS = int_mat[jnp.array(mtbU,dtype=int),jnp.array(mtbU,dtype=int)]
1017
+
1018
+ intVL = intLL*(1-partb)+partb*intLU
1019
+
1020
+ intVU = intUL*(1-partb)+partb*intUU
1021
+
1022
+ intV = intVL*(1-parta)+parta*intVU
1023
+
1024
+ intVLS = intLLS*(1-partb)+partb*intLUS
1025
+
1026
+ intVUS = intULS*(1-partb)+partb*intUUS
1027
+
1028
+ intVS = intVLS*(1-parta)+parta*intVUS
1029
+
1030
+ overlap += intV
1031
+
1032
+ return overlap,ind
1033
+ overlap,_ = jax.lax.scan(rloop,overlap,jnp.arange(test1.shape[0]))
1034
+
1035
+ return overlap,poverlap,up_dist,lo_dist
1036
+ def nclose():
1037
+ return 0.0,0.0,0.0,0.0
1038
+ overlap,poverlap,up_dist,lo_dist = jax.lax.cond(dist<100,close,nclose)
1039
+ flat_d = 10
1040
+ usplit = sj(up_dist-flat_d,1)*jnp.power(up_dist,2)+(1-sj(up_dist+flat_d,1))*jnp.power(up_dist,2)
1041
+ lsplit = sj(lo_dist-flat_d,1)*jnp.power(lo_dist,2)+(1-sj(lo_dist+flat_d,1))*jnp.power(lo_dist,2)
1042
+ return k1*overlap*-lj(dist,1.0)+lsplit+usplit
1043
+
1044
+ #calculates the full potential due to membrane-membrane interactions
1045
+ @jax.jit
1046
+ def nbh_calc(new_zs,k,k2,zd):
1047
+ new_zs = get_new_zs(new_zs,loop_inds)
1048
+ grad_grid = jnp.zeros((grid_size_x,grid_size_y,2))
1049
+ def nbh_grad_calc1(grad_grid,ind2):
1050
+ def nbh_grad_calc2(grad_grid,ind3):
1051
+ inder = flat_ind(ind2,ind3,grid_size_x)
1052
+ all_nbhs = jnp.array(get_nbh_inds(inder,grid_size_x,grid_size_y,1),dtype=int)
1053
+ tot = jnp.zeros(2)
1054
+ posi1 = new_zs[ind2,ind3]
1055
+ upper1 = zd+posi1[0]+0.5*posi1[1]
1056
+ lower1 = zd+posi1[0]-0.5*posi1[1]
1057
+ u_mult1 = bgrid_weight(ind2,ind3,upper1)
1058
+ l_mult1 = bgrid_weight(ind2,ind3,lower1)
1059
+ min_mult = (jnp.abs(u_mult1-l_mult1)+u_mult1+l_mult1)/2
1060
+
1061
+ tot = tot.at[1].set(tot[1]+min_mult*0.5*k2*(mem_data[1]-new_zs[ind2,ind3,1])*(mem_data[1]-new_zs[ind2,ind3,1]))
1062
+
1063
+ def nbh_grad_calc3(tot,ind):
1064
+ inda,indb = unflat_ind(all_nbhs[ind],grid_size_x)
1065
+ def diff(tot):
1066
+ tot = tot.at[0].set(tot[0]+rpot_col(new_zs,inda,indb,ind2,ind3,0,zd,k))
1067
+ return tot
1068
+ def ndiff(tot):
1069
+ return tot
1070
+ tot = jax.lax.cond(jnp.logical_and(inda==ind2,indb==ind3),ndiff,diff,tot)
1071
+ return tot,ind
1072
+ tot,_ = jax.lax.scan(nbh_grad_calc3,tot,jnp.arange(all_nbhs.shape[0]))
1073
+ grad_grid = grad_grid.at[ind2,ind3].set(tot)
1074
+ return grad_grid,ind3
1075
+ grad_grid,_ = jax.lax.scan(nbh_grad_calc2,grad_grid,jnp.arange(grid_size_y))
1076
+ return grad_grid,ind2
1077
+ grad_grid,_ = jax.lax.scan(nbh_grad_calc1,grad_grid,jnp.arange(grid_size_x))
1078
+ return grad_grid
1079
+
1080
+
1081
+ #calculates the potential over all membrane segments
1082
+ @jax.jit
1083
+ def calc_graphs(xy,zd):
1084
+ xy = get_new_zs(xy,loop_inds)
1085
+ grid_vals = jnp.zeros((grid_size_x,grid_size_y,2))
1086
+ def calc_grid(grid_vals,ind):
1087
+ ind2 = ind
1088
+ def calc_grid2(grid_vals,ind):
1089
+ ind3 = ind
1090
+ z_disp = xy[ind2,ind3,0]+zd
1091
+ mt = xy[ind2,ind3,1]
1092
+ tot,_ = get_pot(ind2,ind3,xy[ind2,ind3,0],mt,zd)
1093
+ grid_vals = grid_vals.at[ind2,ind3].set(tot)
1094
+ return grid_vals,ind
1095
+ grid_vals,_ = jax.lax.scan(calc_grid2,grid_vals,jnp.arange(grid_size_y))
1096
+ return grid_vals,ind
1097
+ grid_vals,_ = jax.lax.scan(calc_grid,grid_vals,jnp.arange(grid_size_x))
1098
+ return grid_vals
1099
+
1100
+ #used for evaluating potential over all membrane segments for creating Potential graphs
1101
+ def get_pot_graph(in_xy):
1102
+ k = float(args.k1_const)
1103
+ k2 = float(args.k2_const)
1104
+ xy = in_xy[:grid_size_x*grid_size_y*2]
1105
+ zd = in_xy[-1]/5
1106
+ xy = xy.reshape((grid_size_x,grid_size_y,2))
1107
+ return calc_graphs(xy,zd)
1108
+
1109
+ #setting up the total potential function
1110
+ @jax.jit
1111
+ def total_pot_fun(in_xy):
1112
+ k = float(args.k1_const)
1113
+ k2 = float(args.k2_const)
1114
+ xy = in_xy[:grid_size_x*grid_size_y*2]
1115
+ zd = in_xy[-1]/5
1116
+ xy = xy.reshape((grid_size_x,grid_size_y,2))
1117
+ return jnp.sum(calc_graphs(xy,zd))+jnp.sum(nbh_calc(xy,k,k2,zd))
1118
+
1119
+ #differentiating using jax
1120
+ ac_grad_fun = jax.grad(total_pot_fun,argnums=0)
1121
+
1122
+
1123
+ #wrapping for use with numpy based minimisation algorithm (As the jax.numpy one had issues at time of writing - this could be updated once this works)
1124
+ def nwrap_tpot(in_xy):
1125
+ in_xyj = jnp.array(in_xy)
1126
+ return total_pot_fun(in_xyj)
1127
+
1128
+ def nwrap_grad(in_xy):
1129
+ in_xyj = jnp.array(in_xy)
1130
+ test = np.array(ac_grad_fun(in_xyj))
1131
+ return test
1132
+
1133
+
1134
+
1135
+ new_zs = jnp.zeros((grid_size_x,grid_size_y,2))
1136
+ new_zs = new_zs.at[:,:,1].set(mem_data[1])
1137
+ start_z = float(args.zshift)*5
1138
+ start_oz = 0
1139
+
1140
+
1141
+ print("Misc..")
1142
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
1143
+ print("Minimisation... (This will take a while)")
1144
+
1145
+ timer = time.time()
1146
+
1147
+ #writing bead positions for debugging purposes
1148
+ write_point(np.array(surf_poses),memdir+"Surface.pdb")
1149
+ write_point(np.array(all_poses),memdir+"All_beads.pdb")
1150
+
1151
+ #main minimisation loop
1152
+ try_again=True
1153
+ while try_again:
1154
+ min_out = minimize(nwrap_tpot, np.concatenate((np.array(new_zs.flatten()),np.array([start_z]))),jac = nwrap_grad ,method='BFGS', tol=2e-4,options={"gtol":0.002,"maxiter":iters})
1155
+ new_zs = min_out.x[:grid_size_x*grid_size_y*2]
1156
+ start_z = min_out.x[-1]
1157
+ new_zs = new_zs.reshape((grid_size_x,grid_size_y,2))
1158
+ new_zs = np.array(get_new_zs(jnp.array(new_zs),loop_inds))
1159
+ total_z = np.mean(new_zs[loop_inds_end[:,0],loop_inds_end[:,1],0])
1160
+ new_zs[:,:,0] -= total_z
1161
+ start_z += total_z*5
1162
+ if(min_out.status == 2):
1163
+ try_again = False
1164
+ elif(min_out.status == 1):
1165
+ try_again = False
1166
+ else:
1167
+ try_again=not min_out.success
1168
+
1169
+
1170
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
1171
+
1172
+ #calcuating graphs, contacts and general post processing
1173
+ print("Post processing...")
1174
+ timer = time.time()
1175
+
1176
+ np.savez(memdir+"Membrane_pos.npz",new_zs)
1177
+ np.savez(memdir+"Z_change.npz",start_z/5.0)
1178
+ np.savez(memdir+"Rotation_matrix.npz",rot_mat)
1179
+
1180
+
1181
+ bfacs = calc_contacts(jnp.array(new_zs),jnp.array(all_poses),start_z/5.0,xs,ys)
1182
+ bfacs_at = np.array(bfacs[1])
1183
+ bfacs_hg = np.array(bfacs[0])
1184
+
1185
+ bfacs_at /= np.max(bfacs_at)
1186
+ bfacs_hg /= np.max(bfacs_hg)
1187
+
1188
+ write_protein(bfacs_hg,fn,orient_dir+"HeadGroup_contacts.pdb")
1189
+ write_protein(bfacs_at,fn,orient_dir+"AcylTail_contacts.pdb")
1190
+
1191
+
1192
+ gc = calc_esf(start_z,new_zs,grid_ban,grid_size_x,grid_size_y,x_ext,y_ext,cposes,ctypes)
1193
+ potg = get_pot_graph(min_out.x)
1194
+ plt.imshow(potg[:,:,0])
1195
+ plt.savefig(orient_dir+"Potential_Upper.svg")
1196
+ plt.clf()
1197
+ plt.imshow(potg[:,:,1])
1198
+ plt.savefig(orient_dir+"Potential_Lower.svg")
1199
+ plt.clf()
1200
+
1201
+
1202
+ BotBan, TopBan = write_pdb(start_z,new_zs,grid_ban,grid_size_x,grid_size_y,x_ext,y_ext,gc,orient_dir,locdir,memdir,args.write_bfactors,mem_data,rot_mat_inv)
1203
+
1204
+ print("Z change:",np.format_float_positional(start_z/5.0,precision=3))
1205
+ plt.imshow((new_zs[:,:,0]+0.5*new_zs[:,:,1])*TopBan)
1206
+ plt.savefig(orient_dir+"Deformations_Upper.svg")
1207
+ plt.clf
1208
+ plt.imshow((new_zs[:,:,0]-0.5*new_zs[:,:,1])*BotBan)
1209
+ plt.savefig(orient_dir+"Deformations_Lower.svg")
1210
+ plt.clf
1211
+
1212
+
1213
+
1214
+ print("Done in:", str(np.format_float_positional(time.time()-timer,precision=3))+"s")
1215
+
1216
+
1217
+
1218
+ plt.imshow(gc[:,:,0]*BotBan)
1219
+ plt.savefig(orient_dir+"Charge_Lower.svg")
1220
+ plt.clf
1221
+ plt.imshow(gc[:,:,1]*TopBan)
1222
+ plt.savefig(orient_dir+"Charge_Upper.svg")
1223
+ plt.clf
1224
+
1225
+
1226
+ run_txt = open(orient_dir+"Run_command.txt","w")
1227
+ run_txt.write(" ".join(sys.argv))
1228
+ run_txt.close()
1229
+
1230
+ print("Total time:",str(np.format_float_positional(time.time()-tot_time,precision=3))+"s")
1231
+
1232
+
1233
+ if __name__ == "__main__":
1234
+ main()