Skip to content

Commit 97f040a

Browse files
committed
Updated scripts, small changes to applied_efp.rst
1 parent baa3f22 commit 97f040a

5 files changed

Lines changed: 52 additions & 227 deletions

File tree

doc/applied_efp.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ FMO: Applied flexible EFP
77
Overview
88
--------
99

10-
As is the case with many photoactive proteins,computational methods struggle to reproduce
11-
experimental spectra for the Fenna-Matthews-Olson complex (FMO). Work by
10+
Computational methods struggle to reproduce experimental spectra in photoactive proteins.
11+
One such protein is the the Fenna-Matthews-Olson complex (FMO). Work by
1212
`Kim et al <https://pubs.acs.org/doi/full/10.1021/acs.jpclett.9b03486>`_ shows that
1313
flexible QM/EFP can be applied to FMO to correctly generate computational results in
14-
quantitative agreement to experimental spectra.
14+
quantitative agreement to experimental spectra. In short, the solvatochromic environments
15+
surrounding each bacteriochloropyll a (BChl a) pigment are heavily polarizable; EFP captures
16+
this polarizability explicitly where a standard QM/MM calculation will onyl approximate it,
17+
thus the polarizable environment is crucial to accurately model photosynthesis.
1518

1619
The key to applying EFP to your system is to carefully define the active site and EFP region.
1720
FMO is a trimeric protein with eight bacteriochloropyll a (BChl) pigments in each monomer.

examples/flex-EFP/Scripts/cut_qm.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,20 +243,21 @@ def get_screen(lines,coords,title):
243243
if title in line:
244244
start=1
245245

246-
def get_header(lines):
246+
def get_header(lines,input_name):
247247
header=[]
248+
fragname=input_name.split('.')[0]
248249
for line in lines:
249-
header.append(line)
250+
header.append(line.replace('$FRAGNAME',fragname))
250251
#print(line)
251252
if 'COORDINATES (BOHR)' in line:
252253
return header
253254

254255
def main(inp, efp):
255-
with open(inp,'r') as inp:
256-
inp_lines=inp.readlines()
257-
with open(efp,'r') as efp:
258-
efp_lines=efp.readlines()
259-
header=get_header(efp_lines)
256+
with open(inp,'r') as inp_:
257+
inp_lines=inp_.readlines()
258+
with open(efp,'r') as efp_:
259+
efp_lines=efp_.readlines()
260+
header=get_header(efp_lines,inp)
260261
rem_atoms, rem_pols = get_specials(inp_lines)
261262
keep_coords,rem_coords=get_coords(efp_lines,rem_atoms,rem_pols)
262263
keep_monop=get_monopoles(efp_lines,keep_coords)

examples/flex-EFP/Scripts/fragment_RMSD.py

Lines changed: 23 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -12,74 +12,14 @@
1212
ang_cutoff=0.20 # Minimum RMSD allowed for "good" match
1313
cutoff=ang_cutoff*1.8897259886 # Cutoff convert to Bohr
1414

15-
inp=sys.argv[1] # a_22_304.inp for example. Input file with fragment coords
15+
inp=sys.argv[1]
16+
#inp='g_952.inp' # a_22_304.inp for example. Input file with fragment coords
1617
with open(inp, "r") as orig:
1718
orgs = orig.readlines()
1819

1920
outname=inp.replace('.inp','.efp') # Output file name, same as input but changed extension.
2021
# If no match is found, then outname will not be used.
2122

22-
def rotate_vector(vector, rotation_matrix):
23-
#Different from coords because dipoles assumed centered on coordinate. NOT placed in Cartesian space
24-
return np.dot(vector,rotation_matrix)
25-
26-
def rotate_quadrupole(quadrupole, rotation_matrix):
27-
#R x Q x R_t
28-
#Quadrupoles are 3x3s. But .efp give only six values, order is assumed xx, xy, xz, yy, yz, zz
29-
#With symmetric off-diagonal elements.
30-
q = np.array([
31-
[quadrupole[0], quadrupole[3], quadrupole[4]],
32-
[quadrupole[3], quadrupole[1], quadrupole[5]],
33-
[quadrupole[4], quadrupole[5], quadrupole[2]],
34-
])
35-
q_rotated = rotation_matrix.T @ q @ rotation_matrix
36-
return [q_rotated[0, 0], q_rotated[1, 1], q_rotated[2, 2],
37-
q_rotated[0, 1], q_rotated[0, 2], q_rotated[1, 2]]
38-
39-
def rotate_octupole(octupole, rotation_matrix):
40-
#Oxyz=Rxi*Ryj*Rzk*Oijk summed over i=x,y,z;j=x,y,z;z=x,y,z;
41-
#Each term Oxyz of new tensor is composed of
42-
#27 pieces generated from old tensor and rotation matrix!
43-
#The indexes are hard-coded for some better readiability
44-
o = np.array([
45-
[[octupole[0], octupole[3], octupole[4]], [octupole[3], octupole[5], octupole[9]], [octupole[4], octupole[9], octupole[7]]],
46-
[[octupole[3], octupole[5], octupole[9]], [octupole[5], octupole[1], octupole[6]], [octupole[9], octupole[6], octupole[8]]],
47-
[[octupole[4], octupole[9], octupole[7]], [octupole[9], octupole[6], octupole[8]], [octupole[7], octupole[8], octupole[2]]],
48-
])
49-
#fancy function below does what we want. Look up np.einsum if you need clarification.
50-
#o_rotated = np.einsum('ij,jkl,lm->ikm', rotation_matrix, o, rotation_matrix.T)
51-
o_rotated = np.zeros((3, 3, 3))
52-
for p in range(0,3):
53-
for q in range(0,3):
54-
for r in range(0,3):
55-
o_rotated[p][q][r]=single_term_oct(p,q,r,o,rotation_matrix.T)
56-
return [
57-
o_rotated[0, 0, 0], o_rotated[1, 1, 1], o_rotated[2, 2, 2],
58-
o_rotated[0, 0, 1], o_rotated[0, 0, 2], o_rotated[0, 1, 1],
59-
o_rotated[1, 1, 2], o_rotated[0, 2, 2], o_rotated[1, 2, 2],
60-
o_rotated[0, 1, 2]
61-
]
62-
63-
def single_term_oct(p,q,r,octupole,rot_mat):
64-
term=0.0
65-
for i in range(0,3):
66-
for j in range(0,3):
67-
for k in range(0,3):
68-
term+=rot_mat[p][i]*rot_mat[q][j]*rot_mat[r][k]*octupole[i][j][k]
69-
return term
70-
71-
def rotate_polarizability(polarizability, rotation_matrix):
72-
#RxQxR_t
73-
q = np.array([
74-
[polarizability[0], polarizability[3], polarizability[4]],
75-
[polarizability[6], polarizability[1], polarizability[5]],
76-
[polarizability[7], polarizability[8], polarizability[2]]])
77-
q_rotated = rotation_matrix.T @ q @ rotation_matrix
78-
return [q_rotated[0, 0],q_rotated[1, 1],q_rotated[2, 2],
79-
q_rotated[0, 1],q_rotated[0, 2],q_rotated[1, 2],
80-
q_rotated[1, 0],q_rotated[2, 0],q_rotated[2, 1]]
81-
#return q_rotated.flatten()
82-
8323
def get_RMSD(coords1,coords2,mass):
8424
tot=0.0
8525
length=len(coords1)
@@ -106,8 +46,8 @@ def kabsch_algorithm(coords, coords2):
10646
u, _, vh = np.linalg.svd(covariance_matrix)
10747
rotation_matrix = np.dot(u, vh)
10848

109-
#if np.linalg.det(rotation_matrix) < 0:
110-
# rotation_matrix[:, -1] *= -1
49+
if np.linalg.det(rotation_matrix) < 0:
50+
rotation_matrix[:, -1] *= -1
11151
return rotation_matrix, com1, com2
11252

11353
def apply_transform(coords, rotation_matrix, com1, com2):
@@ -118,28 +58,34 @@ def apply_transform(coords, rotation_matrix, com1, com2):
11858
return rotated_coords
11959

12060
amino_acid_dict = {'a':'ala','r':'arg','n':'asn','d':'asp','c':'cys',
121-
'q':'gln','e':'glu','g':'gly','h':'his','i':'ile',
61+
'q':'gln','e':'glu','g':'gly','h':'hip','i':'ile',
12262
'l':'leu','k':'lys','m':'met','f':'phe','p':'pro',
12363
's':'ser','t':'thr','w':'trp','y':'tyr','v':'val',
12464
'hp':'hip','hd':'hid','he':'hie'}
12565
atom_weights = {'H':1.0,'O':15.9949100,'C':12.0000000,'N':12.0030700,'S':32.0650000,'M':23.3040000}
12666

12767
try:
128-
res=amino_acid_dict[inp[0]]
68+
res=amino_acid_dict[inp.split('_')[0]]
12969
except:
13070
print('No library folder for: '+inp)
131-
#directory = '/depot/lslipche/data/yb_boss/flexible_efp/efpdb/'+res
71+
exit()
72+
13273
directory+=res
74+
13375
file_extension = '.efp' # change this to your desired file extension
13476

77+
78+
#Grab coordinates from the input file, convert from Angstroms to bohrs.
13579
start=0
13680
xyz=[]
13781
orig_coords=[]
13882
weights=[]
83+
inp_at_lines=[]
13984
for line in orgs:
14085
if '$end' in line:
14186
start=0
14287
elif(start==1):
88+
inp_at_lines.append(line)
14389
xyz.append(float(line.split()[2])/0.529177249)
14490
xyz.append(float(line.split()[3])/0.529177249)
14591
xyz.append(float(line.split()[4])/0.529177249)
@@ -148,27 +94,23 @@ def apply_transform(coords, rotation_matrix, com1, com2):
14894
weights.append(atom_weights[line.split()[0][0]])
14995
elif 'C1' in line:
15096
start=1
151-
152-
minrmsd=20.0
97+
minrmsd=20.0 # Arbitrary starting value. This will be replaced with real RMSD values in the loop.
15398

15499
# Loop through files in the directory
155100
for filename in os.listdir(directory):
156101
if filename.endswith(file_extension):
157102
full_path = os.path.join(directory, filename)
158-
#full_path=directory+'/gly5817.efp'
159-
#print(f"Processing file: {full_path}")
160103
with open(full_path, "r") as term:
161104
terms = term.readlines()
162-
105+
else:
106+
continue
163107
start=0
164108
xyz=[]
165109
term_coords=[]
166110
for line in terms:
167111
if 'BO21' in line:
168112
start=0
169113
elif(start==1):
170-
#print(line.split()[1])
171-
#print(float(line.split()[1])*0.529177249)
172114
xyz.append(float(line.split()[1]))
173115
xyz.append(float(line.split()[2]))
174116
xyz.append(float(line.split()[3]))
@@ -181,139 +123,13 @@ def apply_transform(coords, rotation_matrix, com1, com2):
181123
rotation_matrix, com1, com2 = kabsch_algorithm(new_coords, coords2)
182124
aligned_new_coords = apply_transform(new_coords, rotation_matrix, com1,com2)
183125
rmsd=(get_RMSD(aligned_new_coords,coords2,weights))
184-
#print(rmsd)
185-
if(rmsd<minrmsd):
186-
minrmsd=rmsd
187-
if(rmsd<cutoff):
188-
match=full_path
189-
#break
190-
#print(minrmsd)
191-
#%%
192-
193-
#If good match found, transform that .efp to coords in .inp
194-
# Coords, dipoles, quadrupoles, octupoles, polarizable points and tensor
195-
# Monopoles and screen/screen2 do not need changed
196126

197-
#print(rmsd/1.8897259886, full_path, inp)
198-
#print(get_RMSD(aligned_new_coords,coords2,weights))
199-
#print(aligned_new_coords)
200-
#print(coords2)
127+
if(rmsd<minrmsd):
128+
minrmsd=rmsd
129+
match=full_path
201130

202-
if(rmsd<cutoff):
203-
with open(match, "r") as efp:
204-
f0 = efp.readlines()
205-
start=0
206-
#coords=[]
207-
temp=[]
208-
for line in f0:
209-
newline=line
210-
if ' STOP' in line:
211-
start=0
212-
elif(start==1):
213-
#coords.append(apply_transform([float(line.split()[1]),float(line.split()[2]),float(line.split()[3])],rotation_matrix,com1,com2))
214-
coords=(apply_transform([float(line.split()[1]),float(line.split()[2]),float(line.split()[3])],rotation_matrix,com1,com2))
215-
newline=line.split()[0]
216-
while(len(newline)<7):
217-
newline=newline+' '
218-
for i in range(len(coords)):
219-
newline+=str(f"{coords[i]:16.10f}")
220-
#newline=newline.replace(line.split()[i+1],str(f"{coords[i]:16.10f}"))
221-
newline+=str(f"{float(line.split()[4]):12.7f}")
222-
newline+=str(f"{float(line.split()[5]):5.1f}")+'\n'
223-
elif(start==2):
224-
dipole=rotate_vector([float(line.split()[1]),float(line.split()[2]),float(line.split()[3])],rotation_matrix)
225-
newline=line.split()[0]
226-
while(len(newline)<7):
227-
newline=newline+' '
228-
for i in range(len(dipole)):
229-
newline+=f"{dipole[i]:16.10f}"
230-
newline+='\n'
231-
elif(start==3):
232-
newline=''
233-
if '>' in line:
234-
name=line.split()[0]
235-
while(len(name)<7):
236-
name=name+' '
237-
quadrupole=[float(line.split()[1]),float(line.split()[2]),float(line.split()[3]),float(line.split()[4])]
238-
else:
239-
quadrupole.append(float(line.split()[0]))
240-
quadrupole.append(float(line.split()[1]))
241-
rotated_quad=rotate_quadrupole(quadrupole, rotation_matrix)
242-
temp.append(name+' '+f"{rotated_quad[0]:14.10f}"+' '+f"{rotated_quad[1]:14.10f}"+' '
243-
+f"{rotated_quad[2]:14.10f}"+' '+f"{rotated_quad[3]:14.10f}"+' >\n'+
244-
' '+f"{rotated_quad[4]:14.10f}"+' '+f"{rotated_quad[5]:14.10f}"+'\n')
245-
elif(start==4):
246-
newline=''
247-
if(j%3==0):
248-
name=line.split()[0]
249-
while(len(name)<7):
250-
name=name+' '
251-
#print(line)
252-
octupole=[float(line.split()[1]),float(line.split()[2]),float(line.split()[3]),float(line.split()[4])]
253-
j+=1
254-
elif(j%3==1):
255-
for i in range(4):
256-
octupole.append(float(line.split()[i]))
257-
j+=1
258-
else:
259-
octupole.append(float(line.split()[0]))
260-
octupole.append(float(line.split()[1]))
261-
rotated_oct=rotate_octupole(octupole, rotation_matrix)
262-
#print(rotated_oct)
263-
temp.append(name+' '+f"{rotated_oct[0]:13.9f}"+' '+f"{rotated_oct[1]:13.9f}"+' '
264-
+f"{rotated_oct[2]:13.9f}"+' '+f"{rotated_oct[3]:13.9f}"+' >\n'+
265-
' '+f"{rotated_oct[4]:13.9f}"+' '+f"{rotated_oct[5]:13.9f}"' '
266-
+f"{rotated_oct[6]:13.9f}"+' '+f"{rotated_oct[7]:13.9f}"+' >\n'+
267-
' '+f"{rotated_oct[8]:13.9f}"+' '+f"{rotated_oct[9]:13.9f}"+'\n')
268-
j=0
269-
elif(start==5):
270-
if 'CT' in line:
271-
newline=line.split()[0]
272-
while(len(newline)<4):
273-
newline=newline+' '
274-
coords=(apply_transform([float(line.split()[1]),float(line.split()[2]),float(line.split()[3])],rotation_matrix,com1,com2))
275-
for i in range(len(coords)):
276-
newline+=f"{coords[i]:16.10f}"
277-
newline+='\n'
278-
temp.append(newline)
279-
elif(j%3==0):
280-
polar=[float(line.split()[0]),float(line.split()[1]),float(line.split()[2]),float(line.split()[3])]
281-
j+=1
282-
elif(j%3==1):
283-
for i in range(4):
284-
polar.append(float(line.split()[i]))
285-
j+=1
286-
else:
287-
polar.append(float(line.split()[0]))
288-
rotated_polar=rotate_polarizability(polar,rotation_matrix)
289-
temp.append(' '+f"{rotated_polar[0]:14.10f}"+' '+f"{rotated_polar[1]:14.10f}"+' '+
290-
f"{rotated_polar[2]:14.10f}"+' '+f"{rotated_polar[3]:14.10f}"+' >\n'+
291-
' '+f"{rotated_polar[4]:14.10f}"+' '+f"{rotated_polar[5]:14.10f}"+' '+
292-
f"{rotated_polar[6]:14.10f}"+' '+f"{rotated_polar[7]:14.10f}"+' >\n'+
293-
f"{rotated_polar[8]:16.10f}"+'\n')
294-
j=0
295-
elif 'COORDINATES (BOHR)' in line:
296-
start=1
297-
elif ' DIPOLES' in line:
298-
start=2
299-
elif ' QUADRUPOLES' in line:
300-
start=3
301-
temp.append(line)
302-
elif ' OCTUPOLES' in line:
303-
temp.append(line)
304-
start=4
305-
j=0
306-
elif ' POLARIZABLE POINTS' in line:
307-
start=5
308-
temp.append(line)
309-
j=0
310-
if(start<3):
311-
temp.append(newline)
312-
313-
outfile=inp.split('.')[0]+'.efp'
314-
with open(outfile,'w') as outfile:
315-
for line1 in temp:
316-
outfile.write(line1)
131+
if(minrmsd<cutoff):
132+
os.system("python step4.Flexible_V5.py "+full_path+' '+inp+' -d -xr')
317133
else:
318-
with open('no_matches.txt','a') as outfile2:
319-
outfile2.write(inp+'\n')
134+
print('no match, run GAMESS for: '+inp)
135+
#os.system('gms_slurm -p 20 -q standby -v 2023 '+inp)

0 commit comments

Comments
 (0)