def _atoms_to_charmm22_psf(system): result = ['{:8d} !NATOM'.format(system.natom)] for iatom in xrange(system.natom): ffatype = system.get_ffatype(iatom) if len(ffatype) > 4: log.warning( 'Atom type too long for CHARMM PSF file: {}'.format(ffatype)) result.append( '{:8d} A 1 MOL {:4} {:4} {:10.6f} {:13.4f} 0'. format(iatom + 1, ffatype, ffatype, system.charges[iatom], system.masses[iatom] / amu)) return '\n'.join(result)
def _atoms_to_charmm22_psf(system): result = ['{:8d} !NATOM'.format(system.natom)] for iatom in range(system.natom): ffatype = system.get_ffatype(iatom) if len(ffatype) > 4: log.warning('Atom type too long for CHARMM PSF file: {}'.format(ffatype)) if system.charges is None: charge = 0.0 else: charge = system.charges[iatom] result.append('{:8d} A 1 MOL {:4} {:4} {:10.6f} {:13.4f} 0'.format( iatom+1, ffatype, ffatype, charge, system.masses[iatom]/amu)) return '\n'.join(result)
def update_trajectory_terms(self): ''' Routine to make ``self.valence.terms`` and the term attribute of each trajectory in ``self.trajectories`` consistent again. This is usefull if the trajectory were read from a file and the ``valenceFF`` instance was modified. ''' log.dump('Updating terms of trajectories to current valenceFF terms') with log.section('PTUPD', 3): #update the terms in the trajectories to match the terms in #self.valence for traj in self.trajectories: found = False for term in self.valence.iter_terms(): if traj.term.get_atoms() == term.get_atoms(): if found: raise ValueError( 'Found two terms for trajectory %s with atom indices %s' % (traj.term.basename, str(traj.term.get_atoms()))) traj.term = term if 'PT_ALL' not in term.tasks: log.dump( 'PT_ALL not in tasks of %s-%i, deactivated PT' % (term.basename, term.index)) traj.active = False found = True if not found: log.warning( 'No term found for trajectory %s with atom indices %s, deactivating trajectory' % (traj.term.basename, str(traj.term.get_atoms()))) traj.active = False #check if every term with task PT_ALL has a trajectory associated #with it. It a trajectory is missing, generate it. for term in self.valence.iter_terms(): if 'PT_ALL' not in term.tasks: continue found = False for traj in self.trajectories: if term.get_atoms() == traj.term.get_atoms(): if found: raise ValueError( 'Found two trajectories for term %s with atom indices %s' % (term.basename, str(term.get_atoms()))) found = True if not found: log.warning( 'No trajectory found for term %s with atom indices %s. Generating it now.' % (term.basename, str(term.get_atoms()))) trajectory = self.perturbation.prepare([term])[0] self.perturbation.generate(trajectory) self.trajectories.append(trajectory)
def _check_charmm22(valence): """Print warnings for all kinds of energy terms not supported by CHARMM22. **Arguments** valence Instance of ValenceFF, which defines the force field. """ # First report all types of terms not supported by CHARMM22. skipped = set([]) supported_kinds = set(['BONDHARM', 'BENDAHARM', 'TORSION', 'TORSCHEBY1', 'TORSCHEBY2', 'TORSCHEBY3', 'TORSCHEBY4', 'TORSCHEBY6']) for term in valence.iter_terms(): kind = term.basename[:term.basename.find('/')].upper() if kind not in supported_kinds and kind not in skipped: log.warning('Not writing {} term to CHARMM22 parameter file.'.format(kind)) skipped.add(kind)
def update_trajectory_terms(self): ''' Routine to make ``self.valence.terms`` and the term attribute of each trajectory in ``self.trajectories`` consistent again. This is usefull if the trajectory were read from a file and the ``valenceFF`` instance was modified. ''' log.dump('Updating terms of trajectories to current valenceFF terms') with log.section('PTUPD', 3): #update the terms in the trajectories to match the terms in #self.valence for traj in self.trajectories: found = False for term in self.valence.iter_terms(): if traj.term.get_atoms()==term.get_atoms(): if found: raise ValueError('Found two terms for trajectory %s with atom indices %s' %(traj.term.basename, str(traj.term.get_atoms()))) traj.term = term if 'PT_ALL' not in term.tasks: log.dump('PT_ALL not in tasks of %s-%i, deactivated PT' %(term.basename, term.index)) traj.active = False found = True if not found: log.warning('No term found for trajectory %s with atom indices %s, deactivating trajectory' %(traj.term.basename, str(traj.term.get_atoms()))) traj.active = False #check if every term with task PT_ALL has a trajectory associated #with it. It a trajectory is missing, generate it. for term in self.valence.iter_terms(): if 'PT_ALL' not in term.tasks: continue found = False for traj in self.trajectories: if term.get_atoms()==traj.term.get_atoms(): if found: raise ValueError('Found two trajectories for term %s with atom indices %s' %(term.basename, str(term.get_atoms()))) found =True if not found: log.warning('No trajectory found for term %s with atom indices %s. Generating it now.' %(term.basename, str(term.get_atoms()))) trajectory = self.perturbation.prepare([term])[term.index] self.perturbation.generate(trajectory) self.trajectories.append(trajectory)
def generate(self, trajectory, remove_com=True): ''' Method to calculate the perturbation trajectory, i.e. the trajectory that scans the geometry along the direction of the ic figuring in the term with the given index (should be a diagonal term). This method should be implemented in the derived classes. **Arguments** trajectory instance of Trajectory class representing the perturbation trajectory **Optional Arguments** remove_com if set to True, removes the center of mass translation from the resulting perturbation trajectories [default=True]. ''' index = trajectory.term.index with log.section('PTGEN', 3, timer='PT Generate'): log.dump(' Generating %s(atoms=%s)' % (self.valence.terms[index].basename, trajectory.term.get_atoms())) strain = self.strains[index] natom = self.system.natom if strain is None: log.warning( 'Strain for term %i (%s) is not initialized, skipping.' % (index, self.valence.terms[index].basename)) return q0 = self.valence.iclist.ictab[self.valence.vlist.vtab[index] ['ic0']]['value'] diag = np.ones([strain.ndof + 1], float) diag[:strain.ndof] *= 0.1 * angstrom diag[strain.ndof] *= abs(q0 - trajectory.targets[0]) sol = None for iq, target in enumerate(trajectory.targets): log.dump(' Frame %i (target=%.3f)' % (iq, target)) strain.constrain_target = target if abs(target - q0) < 1e-6: sol = np.zeros([strain.ndof + 1], float) strain.gradient(sol) else: if sol is not None: init = sol.copy() else: init = np.zeros([strain.ndof + 1], float) init[-1] = np.sign(q0 - target) sol, infodict, ier, mesg = scipy.optimize.fsolve( strain.gradient, init, xtol=1e-3, full_output=True, diag=diag) if ier != 1: #fsolve did not converge, flag this frame for deletion log.dump(' %s' % mesg.replace('\n', ' ')) log.dump( ' Frame %i (target=%.3f) %s(%s) did not converge. Trying again with slightly perturbed initial conditions.' % (iq, target, self.valence.terms[index].basename, trajectory.term.get_atoms())) #try one more time init = sol.copy() init[:3 * natom] += np.random.normal( 0.0, 0.01, [3 * natom]) * angstrom sol, infodict, ier, mesg = scipy.optimize.fsolve( strain.gradient, init, xtol=1e-3, full_output=True, diag=diag) if ier != 1: log.dump(' %s' % mesg.replace('\n', ' ')) log.dump( ' Frame %i (target=%.3f) %s(%s) STILL did not converge.' % (iq, target, self.valence.terms[index].basename, trajectory.term.get_atoms())) trajectory.targets[iq] = np.nan continue x = strain.coords0 + sol[:3 * natom].reshape((-1, 3)) trajectory.values[iq] = strain.constrain_value log.dump(' Converged (value=%.3f, lagmult=%.3e)' % (strain.constrain_value, sol[3 * natom])) if remove_com: com = (x.T * self.system.masses).sum( axis=1) / self.system.masses.sum() for i in xrange(natom): x[i, :] -= com trajectory.coords[iq, :, :] = x #delete flagged frames targets = [] values = [] coords = [] for target, value, coord in zip(trajectory.targets, trajectory.values, trajectory.coords): if not np.isnan(target): targets.append(target) values.append(value) coords.append(coord) trajectory.targets = np.array(targets) trajectory.values = np.array(values) trajectory.coords = np.array(coords) return trajectory
def init_dihedral_terms(self, thresshold=20 * deg): ''' Initialize the dihedral potentials from the local topology. The dihedral potential will be one of the two following possibilities: The multiplicity m is determined from the local topology, i.e. the number of neighbors of the central two atoms in the dihedral If the equilibrium value of all instances of the torsion are within `thresshold` of 0 deg or per/2 with per = 180deg/m, the following potential will be chosen: 0.5*K*(1-cos(m*psi-m*psi0)) with psi0 = 0 or 360/(2*m) ''' with log.section('VAL', 3, 'Initializing'): #get all dihedrals from molmod.ic import dihed_angle, _dihed_angle_low ffatypes = [ self.system.ffatypes[fid] for fid in self.system.ffatype_ids ] dihedrals = {} for dihedral in self.system.iter_dihedrals(): dihedral, types = term_sort_atypes(ffatypes, dihedral, 'dihedral') if types in dihedrals.keys(): dihedrals[types].append(dihedral) else: dihedrals[types] = [dihedral] #loop over all distinct dihedral types ncos = 0 for types, diheds in dihedrals.iteritems(): psi0s = np.zeros(len(diheds), float) ms = np.zeros(len(diheds), float) for i, dihed in enumerate(diheds): if self.system.cell.nvec > 0: d10 = self.system.pos[dihed[0]] - self.system.pos[ dihed[1]] d12 = self.system.pos[dihed[2]] - self.system.pos[ dihed[1]] d23 = self.system.pos[dihed[3]] - self.system.pos[ dihed[2]] self.system.cell.mic(d10) self.system.cell.mic(d12) self.system.cell.mic(d23) psi0s[i] = _dihed_angle_low(d10, d12, d23, 0)[0] else: rs = np.array([self.system.pos[j] for j in dihed]) psi0s[i] = dihed_angle(rs)[0] n1 = len(self.system.neighs1[dihed[1]]) n2 = len(self.system.neighs1[dihed[2]]) ms[i] = get_multiplicity(n1, n2) nan = False for m in ms: if np.isnan(m): nan = True if nan or None in ms or ms.std() > 1e-3: ms_string = str(ms) if nan: ms_string = 'nan' log.warning('missing dihedral for %s (m is %s)' % ('.'.join(types), ms_string)) continue m = int(np.round(ms.mean())) rv = get_restvalue(psi0s, m, thresshold=thresshold, mode=1) if rv is not None: #a regular Cosine term is used for the dihedral potential for dihed in diheds: term = self.add_term(Cosine, [DihedAngle(*dihed)], types, ['HC_FC_DIAG'], ['au', 'kjmol', 'deg']) self.set_params(term.index, rv0=rv, m=m) ncos += 1 else: #no dihedral potential could be determine, hence it is ignored log.warning( 'missing dihedral for %s (could not determine rest value from %s)' % ('.'.join(types), str(psi0s / deg))) continue log.dump('Added %i Cosine dihedral terms' % ncos)