Source code for ase.md.langevin

"""Langevin dynamics class."""


import sys
import numpy as np
from numpy.random import standard_normal
from ase.md.md import MolecularDynamics
from ase.parallel import world


[docs]class Langevin(MolecularDynamics): """Langevin (constant N, V, T) molecular dynamics. Usage: Langevin(atoms, dt, temperature, friction) atoms The list of atoms. dt The time step. temperature The desired temperature, in energy units. friction A friction coefficient, typically 1e-4 to 1e-2. fixcm If True, the position and momentum of the center of mass is kept unperturbed. Default: True. The temperature and friction are normally scalars, but in principle one quantity per atom could be specified by giving an array. This dynamics accesses the atoms using Cartesian coordinates.""" _lgv_version = 2 # Helps Asap doing the right thing. Increment when changing stuff. def __init__(self, atoms, timestep, temperature, friction, fixcm=True, trajectory=None, logfile=None, loginterval=1, communicator=world): MolecularDynamics.__init__(self, atoms, timestep, trajectory, logfile, loginterval) self.temp = temperature self.frict = friction self.fixcm = fixcm # will the center of mass be held fixed? self.communicator = communicator self.updatevars() def set_temperature(self, temperature): self.temp = temperature self.updatevars() def set_friction(self, friction): self.frict = friction self.updatevars() def set_timestep(self, timestep): self.dt = timestep self.updatevars() def updatevars(self): dt = self.dt # If the friction is an array some other constants must be arrays too. self._localfrict = hasattr(self.frict, 'shape') lt = self.frict * dt masses = self.masses sdpos = dt * np.sqrt(self.temp / masses.reshape(-1) * (2.0/3.0 - 0.5 * lt) * lt) sdpos.shape = (-1, 1) sdmom = np.sqrt(self.temp * masses.reshape(-1) * 2.0 * (1.0 - lt) * lt) sdmom.shape = (-1, 1) pmcor = np.sqrt(3.0)/2.0 * (1.0 - 0.125 * lt) cnst = np.sqrt((1.0 - pmcor) * (1.0 + pmcor)) act0 = 1.0 - lt + 0.5 * lt * lt act1 = (1.0 - 0.5 * lt + (1.0/6.0) * lt * lt) act2 = 0.5 - (1.0/6.0) * lt + (1.0/24.0) * lt * lt c1 = act1 * dt / masses.reshape(-1) c1.shape = (-1, 1) c2 = act2 * dt * dt / masses.reshape(-1) c2.shape = (-1, 1) c3 = (act1 - act2) * dt c4 = act2 * dt del act1, act2 if self._localfrict: # If the friction is an array, so are these act0.shape = (-1, 1) c3.shape = (-1, 1) c4.shape = (-1, 1) pmcor.shape = (-1, 1) cnst.shape = (-1, 1) self.sdpos = sdpos self.sdmom = sdmom self.c1 = c1 self.c2 = c2 self.act0 = act0 self.c3 = c3 self.c4 = c4 self.pmcor = pmcor self.cnst = cnst self.natoms = self.atoms.get_number_of_atoms() # Also works in parallel Asap. def step(self, f): atoms = self.atoms p = self.atoms.get_momenta() random1 = standard_normal(size=(len(atoms), 3)) random2 = standard_normal(size=(len(atoms), 3)) if self.communicator is not None: self.communicator.broadcast(random1, 0) self.communicator.broadcast(random2, 0) rrnd = self.sdpos * random1 prnd = (self.sdmom * self.pmcor * random1 + self.sdmom * self.cnst * random2) if self.fixcm: rrnd = rrnd - np.sum(rrnd, 0) / len(atoms) prnd = prnd - np.sum(prnd, 0) / len(atoms) rrnd *= np.sqrt(self.natoms / (self.natoms - 1.0)) prnd *= np.sqrt(self.natoms / (self.natoms - 1.0)) atoms.set_positions(atoms.get_positions() + self.c1 * p + self.c2 * f + rrnd) p *= self.act0 p += self.c3 * f + prnd atoms.set_momenta(p) f = atoms.get_forces() atoms.set_momenta(atoms.get_momenta() + self.c4 * f) return f