def __init__(self, system, alpha, scalings): ''' **Arguments:** system The system to which this interaction applies. alpha The alpha parameter in the Ewald summation method. scalings A ``Scalings`` object. This object contains all the information about the energy scaling of pairwise contributions that are involved in covalent interactions. See :class:`yaff.pes.scalings.Scalings` for more details. ''' ForcePart.__init__(self, 'ewald_cor', system) if not system.cell.nvec == 3: raise TypeError('The system must have a 3D periodic cell') if system.charges is None: raise ValueError('The system does not have charges.') self.system = system self.alpha = alpha self.scalings = scalings if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' alpha: %s' % log.invlength(self.alpha)) log(' scalings: %5.3f %5.3f %5.3f' % (scalings.scale1, scalings.scale2, scalings.scale3)) log.hline()
def __init__(self, system, alpha, dielectric=1.0): ''' **Arguments:** system The system to which this interaction applies. alpha The alpha parameter in the Ewald summation method. **Optional arguments:** dielectric The scalar relative permittivity of the system. ''' ForcePart.__init__(self, 'ewald_neut', system) if not system.cell.nvec == 3: raise TypeError('The system must have a 3D periodic cell') if system.charges is None: raise ValueError('The system does not have charges.') self.system = system self.alpha = alpha self.dielectric = dielectric if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' alpha: %s' % log.invlength(self.alpha)) log(' relative permittivity: %5.3f' % self.dielectric) log.hline()
def __init__(self, system, alpha, gcut=0.35): ''' **Arguments:** system The system to which this interaction applies. alpha The alpha parameter in the Ewald summation method. gcut The cutoff in reciprocal space. ''' ForcePart.__init__(self, 'ewald_reci', system) if not system.cell.nvec == 3: raise TypeError('The system must have a 3D periodic cell.') if system.charges is None: raise ValueError('The system does not have charges.') if system.dipoles is None: raise ValueError('The system does not have dipoles.') self.system = system self.alpha = alpha self.gcut = gcut self.update_gmax() self.work = np.empty(system.natom*2) if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' alpha: %s' % log.invlength(self.alpha)) log(' gcut: %s' % log.invlength(self.gcut)) log.hline()
def __init__(self, system, grids): ''' **Arguments:** system An instance of the ``System`` class. grids A dictionary with (ffatype, grid) items. Each grid must be a three-dimensional array with energies. This force part is only applicable to systems that are 3D periodic. ''' if system.cell.nvec != 3: raise ValueError('The system must be 3d periodic for the grid term.') for grid in grids.itervalues(): if grid.ndim != 3: raise ValueError('The energy grids must be 3D numpy arrays.') ForcePart.__init__(self, 'grid', system) self.system = system self.grids = grids if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __call__(self, iterative): r'''When this point is reached, a complete time integration step was finished and PLUMED should be notified about this. ''' if not self.hooked: if log.do_high: log.hline() log("Reinitializing PLUMED") log.hline() if log.do_warning: log.warn("You are using PLUMED as a hook for your integrator. " "If PLUMED adds time-dependent forces (for instance " "when performing metadynamics) there is no energy " "conservation. The conserved quantity reported by " "YAFF is irrelevant in this case.") self.setup_plumed(timestep=iterative.timestep, restart=iterative.counter > 0) self.hooked = True # PLUMED provides a setEnergy command, which should pass the # current potential energy. It seems that this is never used, so we # don't pass anything for the moment. # current_energy = sum([part.energy for part in iterative.ff.parts[:-1] if not isinstance(part, ForcePartPlumed)]) # self.plumed.cmd("setEnergy", current_energy) self.plumedstep = iterative.counter self._internal_compute(None, None) self.plumed.cmd("update")
def make_step(self): # get relevant hessian information evals, evecs = self.hessian.get_spectrum() if log.do_high: log(" lowest eigen value: %7.1e" % evals.min()) log("highest eigen value: %7.1e" % evals.max()) # convert gradient to eigen basis grad_eigen = np.dot(evecs.T, self.g_old) while True: # Find the step with the given radius. If the hessian is positive # definite and the unconstrained step is smaller than the trust # radius, this step is returned delta_eigen = solve_trust_radius(grad_eigen, evals, self.trust_radius) radius = np.linalg.norm(delta_eigen) # convert the step to user basis delta_x = np.dot(evecs, delta_eigen) # compute the function and gradient at the new position x = self.x_old + delta_x f, g = self.fun(x, True) # compute the change in function value delta_f = f - self.f_old # compute the change in norm of the gradient delta_norm_g = np.linalg.norm(g) - np.linalg.norm(self.g_old) # must_shrink is a parameter to control the trust radius must_shrink = False if delta_f > 0: # The function must decrease, if not the trust radius is too big. if log.do_high: log("Function increases.") must_shrink = True if self.trust_radius < self.small_radius and delta_norm_g > 0: # When the trust radius becomes small, the numerical noise on # the energy may be too large to detect an increase energy. # In that case the norm of the gradient is used instead. if log.do_high: log("Gradient norm increases.") must_shrink = True if must_shrink: self.trust_radius *= 0.5 while self.trust_radius >= radius: self.trust_radius *= 0.5 if self.trust_radius < self.too_small_radius: raise RuntimeError("The trust radius becomes too small. Is the potential energy surface smooth?") else: # If we get here, we are done with the trust radius loop. if log.do_high: log.hline() # It is fine to increase the trust radius a little after a # successful step. if self.trust_radius < self.initial_trust_radius: self.trust_radius *= 2.0 # Return the results of the successful step return x, f, g
def __init__(self, system, part_pair): ''' **Arguments:** system An instance of the ``System`` class. part_pair An instance of the ``PairPot`` class. This force part is only applicable to systems that are 3D periodic. ''' if system.cell.nvec != 3: raise ValueError( 'Tail corrections can only be applied to 3D periodic systems') if part_pair.name in ['pair_ei', 'pair_eidip']: raise ValueError('Tail corrections are divergent for %s' % part_pair.name) super(ForcePartTailCorrection, self).__init__('tailcorr_%s' % (part_pair.name), system) self.ecorr, self.wcorr = part_pair.pair_pot.prepare_tailcorrections( system.natom) self.system = system if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __init__(self, system, alpha, gcut=0.35): ''' **Arguments:** system The system to which this interaction applies. alpha The alpha parameter in the Ewald summation method. gcut The cutoff in reciprocal space. ''' ForcePart.__init__(self, 'ewald_reci', system) if not system.cell.nvec == 3: raise TypeError('The system must have a 3D periodic cell.') if system.charges is None: raise ValueError('The system does not have charges.') if system.dipoles is None: raise ValueError('The system does not have dipoles.') self.system = system self.alpha = alpha self.gcut = gcut self.update_gmax() self.work = np.empty(system.natom * 2) if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' alpha: %s' % log.invlength(self.alpha)) log(' gcut: %s' % log.invlength(self.gcut)) log.hline()
def __init__(self, system, grids): ''' **Arguments:** system An instance of the ``System`` class. grids A dictionary with (ffatype, grid) items. Each grid must be a three-dimensional array with energies. This force part is only applicable to systems that are 3D periodic. ''' if system.cell.nvec != 3: raise ValueError( 'The system must be 3d periodic for the grid term.') for grid in grids.values(): if grid.ndim != 3: raise ValueError('The energy grids must be 3D numpy arrays.') ForcePart.__init__(self, 'grid', system) self.system = system self.grids = grids if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def make_step(self): # get relevant hessian information evals, evecs = self.hessian.get_spectrum() if log.do_high: log(' lowest eigen value: %7.1e' % evals.min()) log('highest eigen value: %7.1e' % evals.max()) # convert gradient to eigen basis grad_eigen = np.dot(evecs.T, self.g_old) while True: # Find the step with the given radius. If the hessian is positive # definite and the unconstrained step is smaller than the trust # radius, this step is returned delta_eigen = solve_trust_radius(grad_eigen, evals, self.trust_radius) radius = np.linalg.norm(delta_eigen) # convert the step to user basis delta_x = np.dot(evecs, delta_eigen) # compute the function and gradient at the new position x = self.x_old + delta_x f, g = self.fun(x, True) # compute the change in function value delta_f = f - self.f_old # compute the change in norm of the gradient delta_norm_g = np.linalg.norm(g) - np.linalg.norm(self.g_old) # must_shrink is a parameter to control the trust radius must_shrink = False if delta_f > 0: # The function must decrease, if not the trust radius is too big. if log.do_high: log('Function increases.') must_shrink = True if (self.trust_radius < self.small_radius and delta_norm_g > 0): # When the trust radius becomes small, the numerical noise on # the energy may be too large to detect an increase energy. # In that case the norm of the gradient is used instead. if log.do_high: log('Gradient norm increases.') must_shrink = True if must_shrink: self.trust_radius *= 0.5 while self.trust_radius >= radius: self.trust_radius *= 0.5 if self.trust_radius < self.too_small_radius: raise RuntimeError('The trust radius becomes too small. Is the potential energy surface smooth?') else: # If we get here, we are done with the trust radius loop. if log.do_high: log.hline() # It is fine to increase the trust radius a little after a # successful step. if self.trust_radius < self.initial_trust_radius: self.trust_radius *= 2.0 # Return the results of the successful step return x, f, g
def _init_derived_bonds(self): # 1-bond neighbors self.neighs1 = dict((i,set([])) for i in xrange(self.natom)) for i0, i1 in self.bonds: self.neighs1[i0].add(i1) self.neighs1[i1].add(i0) # 2-bond neighbors self.neighs2 = dict((i,set([])) for i in xrange(self.natom)) for i0, n0 in self.neighs1.iteritems(): for i1 in n0: for i2 in self.neighs1[i1]: # Require that there are no shorter paths than two bonds between # i0 and i2. Also avoid duplicates. if i2 > i0 and i2 not in self.neighs1[i0]: self.neighs2[i0].add(i2) self.neighs2[i2].add(i0) # 3-bond neighbors self.neighs3 = dict((i,set([])) for i in xrange(self.natom)) for i0, n0 in self.neighs1.iteritems(): for i1 in n0: for i3 in self.neighs2[i1]: # Require that there are no shorter paths than three bonds # between i0 and i3. Also avoid duplicates. if i3 != i0 and i3 not in self.neighs1[i0] and i3 not in self.neighs2[i0]: self.neighs3[i0].add(i3) self.neighs3[i3].add(i0) # report some basic stuff on screen if log.do_medium: log('Analysis of the bonds:') bond_types = {} for i0, i1 in self.bonds: key = tuple(sorted([self.numbers[i0], self.numbers[i1]])) bond_types[key] = bond_types.get(key, 0) + 1 log.hline() log(' First Second Count') for (num0, num1), count in sorted(bond_types.iteritems()): log('%6i %6i %5i' % (num0, num1, count)) log.hline() log.blank() log('Analysis of the neighbors:') log.hline() log('Number of first neighbors: %6i' % (sum(len(n) for n in self.neighs1.itervalues())/2)) log('Number of second neighbors: %6i' % (sum(len(n) for n in self.neighs2.itervalues())/2)) log('Number of third neighbors: %6i' % (sum(len(n) for n in self.neighs3.itervalues())/2)) # Collect all types of 'environments' for each element. This is # useful to double check the bonds envs = {} for i0 in xrange(self.natom): num0 = self.numbers[i0] nnums = tuple(sorted(self.numbers[i1] for i1 in self.neighs1[i0])) key = (num0, nnums) envs[key] = envs.get(key, 0)+1 # Print the environments on screen log.hline() log('Element Neighboring elements Count') for (num0, nnums), count in sorted(envs.iteritems()): log('%7i %20s %5i' % (num0, ','.join(str(num1) for num1 in nnums), count)) log.hline() log.blank()
def __init__(self, system, alpha, gcut=0.35, dielectric=1.0, exclude_frame=False, n_frame=0): ''' **Arguments:** system The system to which this interaction applies. alpha The alpha parameter in the Ewald summation method. **Optional arguments:** gcut The cutoff in reciprocal space. dielectric The scalar relative permittivity of the system. exclude_frame A boolean to exclude framework-framework interactions (exclude_frame=True) for efficiency sake in MC simulations. n_frame Number of framework atoms. This parameter is used to exclude framework-framework neighbors when exclude_frame=True. ''' ForcePart.__init__(self, 'ewald_reci', system) if not system.cell.nvec == 3: raise TypeError('The system must have a 3D periodic cell.') if system.charges is None: raise ValueError('The system does not have charges.') self.system = system self.alpha = alpha self.gcut = gcut self.dielectric = dielectric self.update_gmax() self.work = np.empty(system.natom * 2) if exclude_frame == True and n_frame < 0: raise ValueError( 'The number of framework atoms to exclude must be positive.') elif exclude_frame == False: n_frame = 0 self.n_frame = n_frame if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' alpha: %s' % log.invlength(self.alpha)) log(' gcut: %s' % log.invlength(self.gcut)) log(' relative permittivity: %5.3f' % self.dielectric) log.hline()
def __call__(self, iterative): if log.do_medium: if self.time0 is None: self.time0 = time.time() if log.do_medium: log.hline() log('counter Walltime') log.hline() log('%7i %10.1f' % ( iterative.counter, time.time() - self.time0, ))
def __call__(self, mc): if log.do_medium: if self.time0 is None: self.time0 = time.time() if log.do_medium: log.hline() log(' counter %s Walltime' % (mc.log_header())) log.hline() log('%12i %s %10.1f' % ( mc.counter, mc.log(), time.time() - self.time0, ))
def __init__(self, system, alpha, gcut=0.35, dielectric=1.0, exclude_frame=False, n_frame=0): ''' **Arguments:** system The system to which this interaction applies. alpha The alpha parameter in the Ewald summation method. **Optional arguments:** gcut The cutoff in reciprocal space. dielectric The scalar relative permittivity of the system. exclude_frame A boolean to exclude framework-framework interactions (exclude_frame=True) for efficiency sake in MC simulations. n_frame Number of framework atoms. This parameter is used to exclude framework-framework neighbors when exclude_frame=True. ''' ForcePart.__init__(self, 'ewald_reci', system) if not system.cell.nvec == 3: raise TypeError('The system must have a 3D periodic cell.') if system.charges is None: raise ValueError('The system does not have charges.') self.system = system self.alpha = alpha self.gcut = gcut self.dielectric = dielectric self.update_gmax() self.work = np.empty(system.natom*2) if exclude_frame == True and n_frame < 0: raise ValueError('The number of framework atoms to exclude must be positive.') elif exclude_frame == False: n_frame = 0 self.n_frame = n_frame if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' alpha: %s' % log.invlength(self.alpha)) log(' gcut: %s' % log.invlength(self.gcut)) log(' relative permittivity: %5.3f' % self.dielectric) log.hline()
def _init_log(self): if log.do_medium: log('Unit cell') log.hline() log('Number of periodic dimensions: %i' % self.cell.nvec) lengths, angles = self.cell.parameters names = 'abc' for i in range(len(lengths)): log('Cell parameter %5s: %10s' % (names[i], log.length(lengths[i]))) names = 'alpha', 'beta', 'gamma' for i in range(len(angles)): log('Cell parameter %5s: %10s' % (names[i], log.angle(angles[i]))) log.hline() log.blank()
def __init__(self, comsystem, scaling=None): ForcePart.__init__(self, 'valence_com', comsystem) #ForcePartValence.__init__(self, system) self.comlist = comsystem.comlist self.gpos = np.zeros((comsystem.gpos_dim, 3), float) self.dlist = DeltaList(self.comlist) self.iclist = InternalCoordinateList(self.dlist) self.vlist = ValenceList(self.iclist) self.scaling = scaling if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() self.term = None # volume term
def _init_log(self): if log.do_medium: log('Unit cell') log.hline() log('Number of periodic dimensions: %i' % self.cell.nvec) lengths, angles = self.cell.parameters names = 'abc' for i in xrange(len(lengths)): log('Cell parameter %5s: %10s' % (names[i], log.length(lengths[i]))) names = 'alpha', 'beta', 'gamma' for i in xrange(len(angles)): log('Cell parameter %5s: %10s' % (names[i], log.angle(angles[i]))) log.hline() log.blank()
def __init__(self, system): ''' **Arguments:** system An instance of the ``System`` class. ''' ForcePart.__init__(self, 'valence', system) self.dlist = DeltaList(system) self.iclist = InternalCoordinateList(self.dlist) self.vlist = ValenceList(self.iclist) if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __call__(self, iterative): if log.do_medium: if self.time0 is None: self.time0 = time.time() if log.do_medium: log.hline() log('Conv.val. =&the highest ratio of a convergence criterion over its threshold.') log('N =&the number of convergence criteria that is not met.') log('Worst =&the name of the convergence criterion that is worst.') log('counter Conv.val. N Worst Energy Walltime') log.hline() log('%7i % 10.3e %2i %15s %s %10.1f' % ( iterative.counter, iterative.dof.conv_val, iterative.dof.conv_count, iterative.dof.conv_worst, log.energy(iterative.epot), time.time() - self.time0, ))
def __call__(self, iterative): if log.do_medium: if self.time0 is None: self.time0 = time.time() if log.do_medium: log.hline() log('Cons.Err. =&the root of the ratio of the variance on the conserved quantity and the variance on the kinetic energy.') log('d-rmsd =&the root-mean-square displacement of the atoms.') log('g-rmsd =&the root-mean-square gradient of the energy.') log('counter Cons.Err. Temp d-RMSD g-RMSD Walltime') log.hline() log('%7i %10.5f %s %s %s %10.1f' % ( iterative.counter, iterative.cons_err, log.temperature(iterative.temp), log.length(iterative.rmsd_delta), log.force(iterative.rmsd_gpos), time.time() - self.time0, ))
def __init__(self, system, comlist=None): ''' Parameters ---------- system An instance of the ``System`` class. comlist An optional layer to derive centers of mass from the atomic positions. These centers of mass are used as input for the first layer, the relative vectors. ''' ForcePart.__init__(self, 'valence', system) self.comlist = comlist self.dlist = DeltaList(system if comlist is None else comlist) self.iclist = InternalCoordinateList(self.dlist) self.vlist = ValenceList(self.iclist) if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __init__(self, system): ''' Parameters ---------- system An instance of the ``System`` class. ''' ForcePart.__init__(self, 'valence', system) # override self.gpos to the correct size! # natom of COMSystem object will return number of beads # but gpos has to have the size (n_atoms, 3), to be consisten # with the other parts of the force field self.dlist = DeltaList(system) self.iclist = InternalCoordinateList(self.dlist) self.vlist = ValenceList(self.iclist) if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def _init_derived_scopes(self): if self.scope_ids is None: if len(self.scopes) != self.natom: raise TypeError('When the scope_ids are derived automatically, the length of the scopes list must match the number of atoms.') lookup = {} scopes = [] self.scope_ids = np.zeros(self.natom, int) for i in xrange(self.natom): scope = self.scopes[i] scope_id = lookup.get(scope) if scope_id is None: scope_id = len(scopes) scopes.append(scope) lookup[scope] = scope_id self.scope_ids[i] = scope_id self.scopes = scopes for scope in self.scopes: check_name(scope) # check the range of the ids if self.scope_ids.min() != 0 or self.scope_ids.max() != len(self.scopes)-1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following scopes are present in the system:') log.hline() log(' Scope ID Number of atoms') log.hline() for scope_id, scope in enumerate(self.scopes): log('%22s %3i %3i' % (scope, scope_id, (self.scope_ids==scope_id).sum())) log.hline() log.blank()
def __init__(self, system, nlist, scalings, pair_pot): ''' **Arguments:** system The system to which this pairwise interaction applies. nlist A ``NeighborList`` object. This has to be the same as the one passed to the ForceField object that contains this part. scalings A ``Scalings`` object. This object contains all the information about the energy scaling of pairwise contributions that are involved in covalent interactions. See :class:`yaff.pes.scalings.Scalings` for more details. pair_pot An instance of the ``PairPot`` built-in class from :mod:`yaff.pes.ext`. ''' ForcePart.__init__(self, 'pair_%s' % pair_pot.name, system) self.nlist = nlist self.scalings = scalings self.pair_pot = pair_pot self.nlist.request_rcut(pair_pot.rcut) if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline() log(' scalings: %5.3f %5.3f %5.3f' % (scalings.scale1, scalings.scale2, scalings.scale3)) log(' real space cutoff: %s' % log.length(pair_pot.rcut)) tr = pair_pot.get_truncation() if tr is None: log(' truncation: none') else: log(' truncation: %s' % tr.get_log()) self.pair_pot.log() log.hline()
def _init_derived_scopes(self): if self.scope_ids is None: if len(self.scopes) != self.natom: raise TypeError( 'When the scope_ids are derived automatically, the length of the scopes list must match the number of atoms.' ) lookup = {} scopes = [] self.scope_ids = np.zeros(self.natom, int) for i in range(self.natom): scope = self.scopes[i] scope_id = lookup.get(scope) if scope_id is None: scope_id = len(scopes) scopes.append(scope) lookup[scope] = scope_id self.scope_ids[i] = scope_id self.scopes = scopes for scope in self.scopes: check_name(scope) # check the range of the ids if self.scope_ids.min() != 0 or self.scope_ids.max() != len( self.scopes) - 1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following scopes are present in the system:') log.hline() log(' Scope ID Number of atoms') log.hline() for scope_id, scope in enumerate(self.scopes): log('%22s %3i %3i' % (scope, scope_id, (self.scope_ids == scope_id).sum())) log.hline() log.blank()
def estimate_hessian(dof, eps=1e-4): """Estimate the Hessian using the symmetric finite difference approximation. **Arguments:** dof A DOF object **Optional arguments:** eps The magnitude of the displacements """ with log.section('HESS'), timer.section('Hessian'): # Loop over all displacements if log.do_medium: log('The following displacements are computed:') log('DOF Dir Energy') log.hline() x1 = dof.x0.copy() rows = np.zeros((len(x1), len(x1)), float) for i in xrange(len(x1)): x1[i] = dof.x0[i] + eps epot, gradient_p = dof.fun(x1, do_gradient=True) if log.do_medium: log('% 7i pos %s' % (i, log.energy(epot))) x1[i] = dof.x0[i] - eps epot, gradient_m = dof.fun(x1, do_gradient=True) if log.do_medium: log('% 7i neg %s' % (i, log.energy(epot))) rows[i] = (gradient_p-gradient_m)/(2*eps) x1[i] = dof.x0[i] dof.reset() if log.do_medium: log.hline() # Enforce symmetry and return return 0.5*(rows + rows.T)
def estimate_hessian(dof, eps=1e-4): """Estimate the Hessian using the symmetric finite difference approximation. **Arguments:** dof A DOF object **Optional arguments:** eps The magnitude of the displacements """ with log.section('HESS'), timer.section('Hessian'): # Loop over all displacements if log.do_medium: log('The following displacements are computed:') log('DOF Dir Energy') log.hline() x1 = dof.x0.copy() rows = np.zeros((len(x1), len(x1)), float) for i in xrange(len(x1)): x1[i] = dof.x0[i] + eps epot, gradient_p = dof.fun(x1, do_gradient=True) if log.do_medium: log('% 7i pos %s' % (i, log.energy(epot))) x1[i] = dof.x0[i] - eps epot, gradient_m = dof.fun(x1, do_gradient=True) if log.do_medium: log('% 7i neg %s' % (i, log.energy(epot))) rows[i] = (gradient_p - gradient_m) / (2 * eps) x1[i] = dof.x0[i] dof.reset() if log.do_medium: log.hline() # Enforce symmetry and return return 0.5 * (rows + rows.T)
def __init__(self, system, pext): ''' **Arguments:** system An instance of the ``System`` class. pext The external pressure. (Positive will shrink the system.) In case of 2D-PBC, this is the surface tension. In case of 1D, this is the linear strain. This force part is only applicable to systems that are periodic. ''' if system.cell.nvec == 0: raise ValueError('The system must be periodic in order to apply a pressure') ForcePart.__init__(self, 'press', system) self.system = system self.pext = pext if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __call__(self, iterative): if log.do_medium: if self.time0 is None: self.time0 = time.time() if log.do_medium: log.hline() log('Cons.Err. =&the root of the ratio of the variance on the conserved quantity and the variance on the kinetic energy.' ) log('d-rmsd =&the root-mean-square displacement of the atoms.' ) log('g-rmsd =&the root-mean-square gradient of the energy.' ) log('counter Cons.Err. Temp d-RMSD g-RMSD Walltime' ) log.hline() log('%7i %10.5f %s %s %s %10.1f' % ( iterative.counter, iterative.cons_err, log.temperature(iterative.temp), log.length(iterative.rmsd_delta), log.force(iterative.rmsd_gpos), time.time() - self.time0, ))
def __init__(self, system, part_pair): ''' **Arguments:** system An instance of the ``System`` class. part_pair An instance of the ``PairPot`` class. This force part is only applicable to systems that are 3D periodic. ''' if system.cell.nvec != 3: raise ValueError('Tail corrections can only be applied to 3D periodic systems') if part_pair.name in ['pair_ei','pair_eidip']: raise ValueError('Tail corrections are divergent for %s'%part_pair.name) super(ForcePartTailCorrection, self).__init__('tailcorr_%s'%(part_pair.name), system) self.ecorr, self.wcorr = part_pair.pair_pot.prepare_tailcorrections(system.natom) self.system = system if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __init__(self, system, comlist=None): ''' **Arguments:** system An instance of the ``System`` class. **Optional arguments:** comlist An optional layer to derive centers of mass from the atomic positions. These centers of mass are used as input for the first layer, the relative vectors. ''' ForcePart.__init__(self, 'bias', system) self.system = system self.valence = ForcePartValence(system) if comlist is not None: raise NotImplementedError if comlist is not None: self.valence_com = ForcePartValence(system, comlist=comlist) else: self.valence_com = None self.terms = [] # The terms contributing to the bias potential are divided into three # categories: # 0) instances of BiasPotential # 1) instances of ValenceTerm with a regular DeltaList # 2) instances of ValenceTerm with a COMList # The following list facilitates looking up the terms after they have # been added self.term_lookup = [] if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def __init__(self, system, pext): ''' **Arguments:** system An instance of the ``System`` class. pext The external pressure. (Positive will shrink the system.) In case of 2D-PBC, this is the surface tension. In case of 1D, this is the linear strain. This force part is only applicable to systems that are periodic. ''' if system.cell.nvec == 0: raise ValueError( 'The system must be periodic in order to apply a pressure') ForcePart.__init__(self, 'press', system) self.system = system self.pext = pext if log.do_medium: with log.section('FPINIT'): log('Force part: %s' % self.name) log.hline()
def finalize(self): if log.do_medium: self.dof.log() log.hline()
def _init_derived_bonds(self): # 1-bond neighbors self.neighs1 = dict((i, set([])) for i in range(self.natom)) for i0, i1 in self.bonds: self.neighs1[i0].add(i1) self.neighs1[i1].add(i0) # 2-bond neighbors self.neighs2 = dict((i, set([])) for i in range(self.natom)) for i0, n0 in self.neighs1.items(): for i1 in n0: for i2 in self.neighs1[i1]: # Require that there are no shorter paths than two bonds between # i0 and i2. Also avoid duplicates. if i2 > i0 and i2 not in self.neighs1[i0]: self.neighs2[i0].add(i2) self.neighs2[i2].add(i0) # 3-bond neighbors self.neighs3 = dict((i, set([])) for i in range(self.natom)) for i0, n0 in self.neighs1.items(): for i1 in n0: for i3 in self.neighs2[i1]: # Require that there are no shorter paths than three bonds # between i0 and i3. Also avoid duplicates. if i3 != i0 and i3 not in self.neighs1[ i0] and i3 not in self.neighs2[i0]: self.neighs3[i0].add(i3) self.neighs3[i3].add(i0) # 4-bond neighbors self.neighs4 = dict((i, set([])) for i in range(self.natom)) for i0, n0 in self.neighs1.items(): for i1 in n0: for i4 in self.neighs3[i1]: # Require that there are no shorter paths than three bonds # between i0 and i4. Also avoid duplicates. if i4 != i0 and i4 not in self.neighs1[ i0] and i4 not in self.neighs2[ i0] and i4 not in self.neighs3[i0]: self.neighs4[i0].add(i4) self.neighs4[i4].add(i0) # report some basic stuff on screen if log.do_medium: log('Analysis of the bonds:') bond_types = {} for i0, i1 in self.bonds: key = tuple(sorted([self.numbers[i0], self.numbers[i1]])) bond_types[key] = bond_types.get(key, 0) + 1 log.hline() log(' First Second Count') for (num0, num1), count in sorted(bond_types.items()): log('%6i %6i %5i' % (num0, num1, count)) log.hline() log.blank() log('Analysis of the neighbors:') log.hline() log('Number of first neighbors: %6i' % (sum(len(n) for n in self.neighs1.values()) // 2)) log('Number of second neighbors: %6i' % (sum(len(n) for n in self.neighs2.values()) // 2)) log('Number of third neighbors: %6i' % (sum(len(n) for n in self.neighs3.values()) // 2)) # Collect all types of 'environments' for each element. This is # useful to double check the bonds envs = {} for i0 in range(self.natom): num0 = self.numbers[i0] nnums = tuple( sorted(self.numbers[i1] for i1 in self.neighs1[i0])) key = (num0, nnums) envs[key] = envs.get(key, 0) + 1 # Print the environments on screen log.hline() log('Element Neighboring elements Count') for (num0, nnums), count in sorted(envs.items()): log('%7i %20s %5i' % (num0, ','.join(str(num1) for num1 in nnums), count)) log.hline() log.blank()
def finalize(self): if log.do_medium: log.hline()
def write_lammps_table(ff, fn='lammps.table', rmin=0.50 * angstrom, nrows=2500, unit_style='electron'): ''' Write tables containing noncovalent interactions for LAMMPS. For every pair of ffatypes, a separate table is generated. Because electrostatic interactions require a specific treatment, point- charge electrostatics are NOT included in the tables. When distributed charge distributions (e.g. Gaussian) are used, this complicates matters. LAMMPS will still only treat point-charge electrostatics using a dedicated method (e.g. Ewald or PPPM), so the table has to contain the difference between the distributed charges and the point charge electrostatic interactions. This also means that every ffatype need a unique charge distribution, i.e. all atoms of the same atom type need to have the same charge and Gaussian radius. All pair potentials contributing to the table need to have the same scalings for near-neighbor interactions; this is however independent of the generation of the table and is dealt with elsewhere **Arguments:** ff Yaff ForceField instance **Optional arguments:** fn Filename where tables will be stored ''' # Find out if we are dealing with electrostatics from distributed charges corrections = [] for part in ff.parts: if part.name == 'pair_ei': if np.any(part.pair_pot.radii != 0.0): # Create a ForcePart with electrostatics from distributed # charges, completely in real space. pair_pot_dist = PairPotEI(part.pair_pot.charges, 0.0, part.pair_pot.rcut, tr=part.pair_pot.get_truncation(), dielectric=part.pair_pot.dielectric, radii=part.pair_pot.radii) fp_dist = ForcePartPair(ff.system, ff.nlist, part.scalings, pair_pot_dist) corrections.append((fp_dist, 1.0)) # Create a ForcePart with electrostatics from point # charges, completely in real space. pair_pot_point = PairPotEI(part.pair_pot.charges, 0.0, part.pair_pot.rcut, tr=part.pair_pot.get_truncation(), dielectric=part.pair_pot.dielectric) fp_point = ForcePartPair(ff.system, ff.nlist, part.scalings, pair_pot_point) corrections.append((fp_point, -1.0)) # Find the largest cut-off rmax = 0.0 for part in ff.parts: if part.name.startswith('pair_'): if part.name == 'pair_ei' and len(corrections) == 0: continue rmax = np.amax([rmax, part.pair_pot.rcut]) # Get LAMMPS ffatypes ffatypes, ffatype_ids = get_lammps_ffatypes(ff) # Select atom pairs for each pair of atom types ffa_pairs = [] for i in range(len(ffatypes)): index0 = np.where(ffatype_ids == i)[0][0] for j in range(i, len(ffatypes)): index1 = -1 candidates = np.where(ffatype_ids == j)[0] for cand in candidates: if cand==index0 or cand in ff.system.neighs1[index0] or\ cand in ff.system.neighs2[index0] or cand in ff.system.neighs3[index0] or\ cand in ff.system.neighs4[index0]: continue else: index1 = cand break if index1 == -1: log("ERROR constructing LAMMPS tables: there is no pair of atom types %s-%s which are not near neighbors" % (ffatypes[i], ffatypes[j])) log("Consider using a supercell to fix this problem") raise ValueError ffa_pairs.append([index0, index1]) if log.do_medium: with log.section('LAMMPS'): log("Generating LAMMPS table with covalent interactions") log.hline() log("rmin = %s | rmax = %s" % (log.length(rmin), log.length(rmax))) # We only consider one neighbor interaction ff.compute() ff.nlist.nneigh = 1 # Construct array of evenly spaced values distances = np.linspace(rmin, rmax, nrows) ftab = open(fn, 'w') ftab.write("# LAMMPS tabulated potential generated by Yaff\n") ftab.write("# All quantities in atomic units\n") ftab.write( "# The names of the tables refer to the ffatype_ids that have to be used in the Yaff system\n" ) ftab.write("#%4s %13s %21s %21s\n" % ("i", "d", "V", "F")) # Loop over all atom pairs for index0, index1 in ffa_pairs: energies = [] for d in distances: gposnn = np.zeros(ff.system.pos.shape, float) ff.nlist.neighs[0] = (index0, index1, d, 0.0, 0.0, d, 0, 0, 0) energy = 0.0 for part in ff.parts: if not part.name.startswith('pair'): continue if part.name == 'pair_ei': continue energy += part.compute(gpos=gposnn) for part, sign in corrections: gposcorr = np.zeros(ff.system.pos.shape, float) energy += sign * part.compute(gpos=gposcorr) gposnn[:] += sign * gposcorr row = [d, energy, gposnn[index0, 2]] energies.append(row) energies = np.asarray(energies) ffai = ffatypes[ffatype_ids[index0]] ffaj = ffatypes[ffatype_ids[index1]] if np.all(energies[:, 1] == 0.0): log.warn("Noncovalent energies between atoms %d (%s) and %d (%s) are zero"\ % (index0,ffai,index1,ffaj)) if np.all(energies[:, 2] == 0.0): log.warn("Noncovalent forces between atoms %d (%s) and %d (%s) are zero"\ % (index0,ffai,index1,ffaj)) if ffai > ffaj: name = '%s---%s' % (str(ffai), str(ffaj)) else: name = '%s---%s' % (str(ffaj), str(ffai)) ftab.write("%s\nN %d R %13.8f %13.8f\n\n" % (name, nrows, rmin / lammps_units[unit_style]['distance'], rmax / lammps_units[unit_style]['distance'])) for irow, row in enumerate(energies): ftab.write( "%05d %+13.8f %+21.12f %+21.12f\n" % (irow + 1, row[0] / lammps_units[unit_style]['distance'], row[1] / lammps_units[unit_style]['energy'], row[2] / lammps_units[unit_style]['energy'] * lammps_units[unit_style]['distance'])) if log.do_medium: log("%s done" % name)
def _init_derived_ffatypes(self): if self.ffatype_ids is None: if len(self.ffatypes) != self.natom: raise TypeError( 'When the ffatype_ids are derived automatically, the length of the ffatypes list must match the number of atoms.' ) lookup = {} ffatypes = [] self.ffatype_ids = np.zeros(self.natom, int) for i in range(self.natom): if self.scope_ids is None: ffatype = self.ffatypes[i] key = ffatype, None else: scope_id = self.scope_ids[i] ffatype = self.ffatypes[i] key = ffatype, scope_id ffatype_id = lookup.get(key) if ffatype_id is None: ffatype_id = len(ffatypes) ffatypes.append(ffatype) lookup[key] = ffatype_id self.ffatype_ids[i] = ffatype_id self.ffatypes = ffatypes for ffatype in self.ffatypes: check_name(ffatype) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len( self.ffatypes) - 1: raise ValueError('The ffatype_ids have incorrect bounds.') # differentiate ffatype_ids if the same ffatype_id is used in different # scopes if self.scopes is not None: self.ffatype_id_to_scope_id = {} fixed_fids = {} for i in range(self.natom): fid = self.ffatype_ids[i] sid = self.ffatype_id_to_scope_id.get(fid) if sid is None: self.ffatype_id_to_scope_id[fid] = self.scope_ids[i] elif sid != self.scope_ids[i]: # We found the same ffatype_id in a different scope_id. This # must be fixed. First check if we have already a new # scope_id ready sid = self.scope_ids[i] new_fid = fixed_fids.get((sid, fid)) if new_fid is None: # No previous new fid create, do it now. new_fid = len(self.ffatypes) # Copy the ffatype label self.ffatypes.append(self.ffatypes[fid]) # Keep track of the new fid fixed_fids[(sid, fid)] = new_fid if log.do_warning: log.warn( 'Atoms with type ID %i in scope %s were changed to type ID %i.' % (fid, self.scopes[sid], new_fid)) # Apply the new fid self.ffatype_ids[i] = new_fid self.ffatype_id_to_scope_id[new_fid] = sid # Turn the ffatypes in the scopes into array if self.ffatypes is not None: self.ffatypes = np.array(self.ffatypes, copy=False) if self.scopes is not None: self.scopes = np.array(self.scopes, copy=False) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len( self.ffatypes) - 1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following atom types are present in the system:') log.hline() if self.scopes is None: log(' Atom type ID Number of atoms') log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): log('%22s %3i %3i' % (ffatype, ffatype_id, (self.ffatype_ids == ffatype_id).sum())) else: log(' Scope Atom type ID Number of atoms' ) log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): scope = self.scopes[ self.ffatype_id_to_scope_id[ffatype_id]] log('%22s %22s %3i %3i' % (scope, ffatype, ffatype_id, (self.ffatype_ids == ffatype_id).sum())) log.hline() log.blank()
def finalize(self): self.traj.close() if log.do_medium: log.hline()
def _init_derived_ffatypes(self): if self.ffatype_ids is None: if len(self.ffatypes) != self.natom: raise TypeError('When the ffatype_ids are derived automatically, the length of the ffatypes list must match the number of atoms.') lookup = {} ffatypes = [] self.ffatype_ids = np.zeros(self.natom, int) for i in xrange(self.natom): if self.scope_ids is None: ffatype = self.ffatypes[i] key = ffatype, None else: scope_id = self.scope_ids[i] ffatype = self.ffatypes[i] key = ffatype, scope_id ffatype_id = lookup.get(key) if ffatype_id is None: ffatype_id = len(ffatypes) ffatypes.append(ffatype) lookup[key] = ffatype_id self.ffatype_ids[i] = ffatype_id self.ffatypes = ffatypes for ffatype in self.ffatypes: check_name(ffatype) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(self.ffatypes)-1: raise ValueError('The ffatype_ids have incorrect bounds.') # differentiate ffatype_ids if the same ffatype_id is used in different # scopes if self.scopes is not None: self.ffatype_id_to_scope_id = {} fixed_fids = {} for i in xrange(self.natom): fid = self.ffatype_ids[i] sid = self.ffatype_id_to_scope_id.get(fid) if sid is None: self.ffatype_id_to_scope_id[fid] = self.scope_ids[i] elif sid != self.scope_ids[i]: # We found the same ffatype_id in a different scope_id. This # must be fixed. First check if we have already a new # scope_id ready sid = self.scope_ids[i] new_fid = fixed_fids.get((sid, fid)) if new_fid is None: # No previous new fid create, do it now. new_fid = len(self.ffatypes) # Copy the ffatype label self.ffatypes.append(self.ffatypes[fid]) # Keep track of the new fid fixed_fids[(sid, fid)] = new_fid if log.do_warning: log.warn('Atoms with type ID %i in scope %s were changed to type ID %i.' % (fid, self.scopes[sid], new_fid)) # Apply the new fid self.ffatype_ids[i] = new_fid self.ffatype_id_to_scope_id[new_fid] = sid # Turn the ffatypes in the scopes into array if self.ffatypes is not None: self.ffatypes = np.array(self.ffatypes, copy=False) if self.scopes is not None: self.scopes = np.array(self.scopes, copy=False) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(self.ffatypes)-1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following atom types are present in the system:') log.hline() if self.scopes is None: log(' Atom type ID Number of atoms') log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): log('%22s %3i %3i' % (ffatype, ffatype_id, (self.ffatype_ids==ffatype_id).sum())) else: log(' Scope Atom type ID Number of atoms') log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): scope = self.scopes[self.ffatype_id_to_scope_id[ffatype_id]] log('%22s %22s %3i %3i' % (scope, ffatype, ffatype_id, (self.ffatype_ids==ffatype_id).sum())) log.hline() log.blank()