2323#
2424#--
2525from molmod .units import *
26+ from molmod .ic import bend_angle , _bend_angle_low , dihed_angle , _dihed_angle_low
2627
2728from yaff .pes .ff import ForceField , ForcePartValence
2829from yaff .pes .parameters import *
2930from 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
3132from 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
3334from yaff .pes .dlist import DeltaList
3435from 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