Skip to content

Commit 5ba42ed

Browse files
committed
Bends of 180 are now linear in cosine, cleaner code
1 parent 89a4e73 commit 5ba42ed

File tree

2 files changed

+110
-18
lines changed

2 files changed

+110
-18
lines changed

quickff/program.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ def average_pars(self):
189189
self.valence.set_params(master.index, fc=fc, rv0=rv, m=m)
190190
for islave in master.slaves:
191191
self.valence.set_params(islave, fc=fc, rv0=rv, m=m)
192+
elif master.kind==5:#chebychev
193+
assert pars.shape[1]==1
194+
fc = pars[:,0].mean()
195+
self.valence.set_params(master.index, fc=fc)
196+
for islave in master.slaves:
197+
self.valence.set_params(islave, fc=fc)
192198
else:
193199
raise NotImplementedError
194200

quickff/valence.py

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
#
2424
#--
2525
from molmod.units import *
26+
from molmod.ic import bend_angle, _bend_angle_low, dihed_angle, _dihed_angle_low
2627

2728
from yaff.pes.ff import ForceField, ForcePartValence
2829
from yaff.pes.parameters import *
2930
from yaff.pes.vlist import ValenceList
30-
from yaff.pes.vlist import Harmonic, PolyFour, Fues, Cosine, Cross
31+
from yaff.pes.vlist import Harmonic, PolyFour, Fues, Cosine, Cross, Chebychev1
3132
from yaff.pes.iclist import InternalCoordinateList
32-
from yaff.pes.iclist import Bond, BendAngle, DihedCos, DihedAngle, OopDist, SqOopDist
33+
from yaff.pes.iclist import Bond, BendAngle, BendCos, DihedCos, DihedAngle, OopDist, SqOopDist
3334
from yaff.pes.dlist import DeltaList
3435
from yaff.sampling.harmonic import estimate_cart_hessian
3536

@@ -140,6 +141,15 @@ def to_string(self, valence, max_name=38, max_line=72):
140141
]
141142
units = [self.units[1], self.units[2], 'au']
142143
ndigits = [(4,3), (4,3), (1,0)]
144+
elif self.kind==5:#chebychev
145+
fcs = pars[:,0]
146+
means = [fcs.mean()]
147+
stds = [fcs.std()]
148+
formats = [
149+
'fc = %%4s %s %%3s' %(u"\u00B1"),
150+
]
151+
units = [self.units[0]]
152+
ndigits = [(4,3)]
143153
#convert term pars to string
144154
line = '%s (%s)' %(
145155
self.basename[:max_line],
@@ -213,9 +223,10 @@ def add_term(self, pot, ics, atypes, tasks, units):
213223
#define the name
214224
if len(ics)==1:
215225
tmp = {
216-
(0,0) : 'BondHarm/', (2,0) : 'BendAHarm/' ,
217-
(4,4) : 'Torsion/' , (3,1) : 'TorsC2Harm/',
218-
(10,0): 'Oopdist/' , (11,0): 'SqOopdist/' ,
226+
(0,0) : 'BondHarm/' ,
227+
(1,5) : 'BendCLin/', (2,0) : 'BendAHarm/' ,
228+
(4,4) : 'Torsion/' , (3,1) : 'TorsC2Harm/',
229+
(10,0): 'Oopdist/' , (11,0): 'SqOopdist/' ,
219230
}
220231
prefix = tmp[(ics[0].kind, pot.kind)]
221232
suffix = ''
@@ -257,7 +268,11 @@ def add_term(self, pot, ics, atypes, tasks, units):
257268
args = [(None,)*len(units)] + ics
258269
else:
259270
args = [None,]*len(units) + ics
260-
ForcePartValence.add_term(self, pot(*args))
271+
if pot.kind==5:
272+
#in case of chebychev, set sign to +1
273+
ForcePartValence.add_term(self, pot(*args,sign=1.0))
274+
else:
275+
ForcePartValence.add_term(self, pot(*args))
261276
return term
262277

263278
def modify_term(self, term_index, pot, ics, basename, tasks, units):
@@ -331,18 +346,46 @@ def init_bend_terms(self):
331346
'''
332347
Initialize all bend terms in the system based on the bends attribute
333348
of the system instance. All bend terms are given harmonic
334-
potentials.
349+
potentials either in the angle or the cosine of the angle.
335350
'''
336351
with log.section('VAL', 3, 'Initializing'):
337-
ffatypes = [self.system.ffatypes[fid] for fid in self.system.ffatype_ids]
338352
#get the angle terms
339-
nbends = 0
353+
ffatypes = [self.system.ffatypes[fid] for fid in self.system.ffatype_ids]
354+
angles = {}
340355
for angle in self.system.iter_angles():
341-
angle, types = term_sort_atypes(ffatypes, angle, 'angle')
342-
units = ['kjmol/rad**2', 'deg']
343-
self.add_term(Harmonic, [BendAngle(*angle)], types, ['PT_ALL', 'HC_FC_DIAG'], units)
344-
nbends += 1
345-
log.dump('Added %i Harmonic bend terms' %nbends)
356+
angle, types = term_sort_atypes(ffatypes, angle, 'dihedral')
357+
if types in angles.keys():
358+
angles[types].append(angle)
359+
else:
360+
angles[types] = [angle]
361+
#loop over all distinct angle types
362+
nabends = 0
363+
ncbends = 0
364+
for types, bends in angles.iteritems():
365+
do_cos = False
366+
for i, bend in enumerate(bends):
367+
if self.system.cell.nvec>0:
368+
d10 = self.system.pos[bend[0]] - self.system.pos[bend[1]]
369+
d12 = self.system.pos[bend[2]] - self.system.pos[bend[1]]
370+
self.system.cell.mic(d10)
371+
self.system.cell.mic(d12)
372+
if _bend_angle_low(d10, d12, 0)[0] > 175*deg:
373+
do_cos = True
374+
break
375+
else:
376+
rs = np.array([self.system.pos[j] for j in bend])
377+
if bend_angle(rs)[0]>175*deg:
378+
do_cos = True
379+
break
380+
#add term harmonic in angle or cos(angle)
381+
for bend in bends:
382+
if do_cos:
383+
term = self.add_term(Chebychev1, [BendCos(*bend)], types, ['HC_FC_DIAG'], ['kjmol'])
384+
ncbends += 1
385+
else:
386+
self.add_term(Harmonic, [BendAngle(*bend)], types, ['PT_ALL', 'HC_FC_DIAG'], ['kjmol/rad**2', 'deg'])
387+
nabends += 1
388+
log.dump('Added %i bend terms harmonic in the angle and %i bend terms linear in cos(angle)' %(nabends, ncbends))
346389

347390
def init_dihedral_terms(self, thresshold=20*deg):
348391
'''
@@ -360,7 +403,6 @@ def init_dihedral_terms(self, thresshold=20*deg):
360403
'''
361404
with log.section('VAL', 3, 'Initializing'):
362405
#get all dihedrals
363-
from molmod.ic import dihed_angle, _dihed_angle_low
364406
ffatypes = [self.system.ffatypes[fid] for fid in self.system.ffatype_ids]
365407
dihedrals = {}
366408
for dihedral in self.system.iter_dihedrals():
@@ -374,6 +416,7 @@ def init_dihedral_terms(self, thresshold=20*deg):
374416
for types, diheds in dihedrals.iteritems():
375417
psi0s = np.zeros(len(diheds), float)
376418
ms = np.zeros(len(diheds), float)
419+
bendskip = False
377420
for i, dihed in enumerate(diheds):
378421
if self.system.cell.nvec>0:
379422
d10 = self.system.pos[dihed[0]] - self.system.pos[dihed[1]]
@@ -382,13 +425,27 @@ def init_dihedral_terms(self, thresshold=20*deg):
382425
self.system.cell.mic(d10)
383426
self.system.cell.mic(d12)
384427
self.system.cell.mic(d23)
428+
#check if bending angle is not 180 deg
429+
bend012 = _bend_angle_low(d10, d12, 0)[0]
430+
bend123 = _bend_angle_low(d12, d23, 0)[0]
431+
if bend012>175*deg or bend123>175*deg:
432+
bendskip = True
433+
continue
385434
psi0s[i] = _dihed_angle_low(d10, d12, d23, 0)[0]
386435
else:
387436
rs = np.array([self.system.pos[j] for j in dihed])
437+
bend012 = bend_angle(rs[:3])[0]
438+
bend123 = bend_angle(rs[1:4])[0]
439+
if bend012>175*deg or bend123>175*deg:
440+
bendskip = True
441+
continue
388442
psi0s[i] = dihed_angle(rs)[0]
389443
n1 = len(self.system.neighs1[dihed[1]])
390444
n2 = len(self.system.neighs1[dihed[2]])
391445
ms[i] = get_multiplicity(n1, n2)
446+
if bendskip:
447+
log.warning('Dihedral for %s contains bend angle close to 180 deg, skipping' %('.'.join(types)))
448+
continue
392449
if np.isnan(ms).any() or ms.std()>1e-3:
393450
ms_string = str(ms)
394451
if np.isnan(ms).any():
@@ -545,7 +602,14 @@ def get_hessian_contrib(self, index, fc=None):
545602
val = ForcePartValence(self.system)
546603
kind = self.vlist.vtab[index]['kind']
547604
masterslaves = [index]+self.terms[index].slaves
548-
if kind==4:#Cosine
605+
if kind==5:#Chebychev
606+
k = self.get_params(index, only='fc')
607+
if fc is not None: k = fc
608+
for jterm in masterslaves:
609+
ics = self.terms[jterm].ics
610+
args = (k,) + tuple(ics)
611+
val.add_term(Chebychev1(*args,sign=1.0))
612+
elif kind==4:#Cosine
549613
m, k, rv = self.get_params(index)
550614
if fc is not None: k = fc
551615
for jterm in masterslaves:
@@ -603,6 +667,8 @@ def set_params(self, term_index, fc=None, rv0=None, rv1=None, m=None,
603667
if m is not None: term['par0'] = m
604668
if fc is not None: term['par1'] = fc
605669
if rv0 is not None: term['par2'] = rv0
670+
elif term['kind'] in [5]:#['Chebychev1']
671+
if fc is not None: term['par0'] = fc
606672
elif term['kind'] in [3]:#['Cross']
607673
if fc is not None: term['par0'] = fc
608674
if rv0 is not None: term['par1'] = rv0
@@ -633,6 +699,10 @@ def get_params(self, term_index, only='all'):
633699
elif only.lower()=='fc': return term['par1']
634700
elif only.lower()=='rv': return term['par2']
635701
else: raise ValueError('Invalid par kind definition %s' %only)
702+
elif term['kind'] in [5]:#['Chebychev']
703+
if only.lower()=='all': return term['par0'],
704+
elif only.lower()=='fc': return term['par0']
705+
else: raise ValueError('Invalid par kind definition %s' %only)
636706
elif term['kind'] in [3]:#['Cross']
637707
if only.lower()=='all': return term['par0'], term['par1'], term['par2']
638708
elif only.lower()=='fc': return term['par0']
@@ -660,6 +730,8 @@ def is_negligible(self, term_index):
660730
return abs(term['par1']) < 1e-6*kjmol
661731
elif term['kind'] in [3]: # ['Cross']
662732
return abs(term['par0']) < 1e-6*kjmol
733+
elif term['kind']==5 and self.terms[term_index].ics[0].kind==1: #linear in cos(angle):
734+
return abs(term['par0']) < 1e-6*kjmol
663735
else:
664736
raise NotImplementedError(
665737
'is_negligible not implemented for Yaff %s term' % term['kind'])
@@ -693,7 +765,7 @@ def dump_logger(self, print_level=1):
693765
with log.section('', print_level):
694766
sequence = [
695767
'bondharm',
696-
'bendaharm', 'bendcharm', 'bendcos',
768+
'bendaharm', 'bendclin', 'bendcharm', 'bendcos',
697769
'torsion', 'torsc2harm', 'dihedharm',
698770
'oopdist', 'cross'
699771
]
@@ -734,6 +806,20 @@ def _bendaharm_to_yaff(self):
734806
))
735807
return ParameterSection(prefix, definitions={'UNIT': units, 'PARS': pars})
736808

809+
def _bendclin_to_yaff(self):
810+
'construct a BENDCLIN section of a yaff parameter file'
811+
prefix = 'BENDCLIN'
812+
units = ParameterDefinition('UNIT', lines=['A kjmol'])
813+
pars = ParameterDefinition('PARS')
814+
for i, master in enumerate(self.iter_masters(label=prefix)):
815+
if self.is_negligible(i): continue
816+
ffatypes = master.basename.split('/')[1].split('.')
817+
K, = self.get_params(master.index)
818+
pars.lines.append('%8s %8s %8s %.10e' %(
819+
ffatypes[0], ffatypes[1], ffatypes[2], K/kjmol
820+
))
821+
return ParameterSection(prefix, definitions={'UNIT': units, 'PARS': pars})
822+
737823
def _bendcharm_to_yaff(self):
738824
'construct a BENDCHARM section of a yaff parameter file'
739825
prefix = 'BENDCHARM'
@@ -887,7 +973,7 @@ def dump_yaff(self, fn):
887973
sections = [
888974
self._bonds_to_yaff(),
889975
self._bendaharm_to_yaff(), self._bendcharm_to_yaff(),
890-
self._bendcos_to_yaff(),
976+
self._bendcos_to_yaff(), self._bendclin_to_yaff(),
891977
self._torsions_to_yaff(), self._torsc2harm_to_yaff(),
892978
self._dihedharm_to_yaff(),
893979
self._opdists_to_yaff(),self._sqopdists_to_yaff(),

0 commit comments

Comments
 (0)