Source code for ase.md.nvtberendsen
"""Berendsen NVT dynamics class."""
import warnings
from typing import Optional
import numpy as np
from ase import Atoms
from ase.md.md import MolecularDynamics
[docs]
class NVTBerendsen(MolecularDynamics):
def __init__(
self,
atoms: Atoms,
timestep: float,
temperature: Optional[float] = None,
taut: Optional[float] = None,
fixcm: bool = True,
*,
temperature_K: Optional[float] = None,
**kwargs,
):
"""Berendsen (constant N, V, T) molecular dynamics.
Parameters
----------
atoms: Atoms object
The list of atoms.
timestep: float
The time step in ASE time units.
temperature: float
The desired temperature, in Kelvin.
temperature_K: float
Alias for *temperature*
taut: float
Time constant for Berendsen temperature coupling in ASE
time units.
fixcm: bool (optional)
If True, the position and momentum of the center of mass is
kept unperturbed. Default: True.
**kwargs : dict, optional
Additional arguments passed to :class:~ase.md.md.MolecularDynamics
base class.
"""
if 'communicator' in kwargs:
msg = (
'`communicator` has been deprecated since ASE 3.25.0 '
'and will be removed in ASE 3.26.0. Use `comm` instead.'
)
warnings.warn(msg, FutureWarning)
kwargs['comm'] = kwargs.pop('communicator')
MolecularDynamics.__init__(self, atoms, timestep, **kwargs)
if taut is None:
raise TypeError("Missing 'taut' argument.")
self.taut = taut
self.temperature = self._process_temperature(
temperature, temperature_K, 'K'
)
self.fix_com = fixcm # will the center of mass be held fixed?
def set_taut(self, taut):
self.taut = taut
def get_taut(self):
return self.taut
def set_temperature(self, temperature=None, *, temperature_K=None):
self.temperature = self._process_temperature(
temperature, temperature_K, 'K'
)
def get_temperature(self):
return self.temperature
def set_timestep(self, timestep):
self.dt = timestep
def get_timestep(self):
return self.dt
def scale_velocities(self):
"""Do the NVT Berendsen velocity scaling"""
tautscl = self.dt / self.taut
old_temperature = self.atoms.get_temperature()
scl_temperature = np.sqrt(
1.0 + (self.temperature / old_temperature - 1.0) * tautscl
)
# Limit the velocity scaling to reasonable values
if scl_temperature > 1.1:
scl_temperature = 1.1
if scl_temperature < 0.9:
scl_temperature = 0.9
p = self.atoms.get_momenta()
p = scl_temperature * p
self.atoms.set_momenta(p)
return
def step(self, forces=None):
"""Move one timestep forward using Berenden NVT molecular dynamics."""
self.scale_velocities()
# one step velocity verlet
atoms = self.atoms
if forces is None:
forces = atoms.get_forces(md=True)
p = self.atoms.get_momenta()
p += 0.5 * self.dt * forces
if self.fix_com:
# calculate the center of mass
# momentum and subtract it
psum = p.sum(axis=0) / float(len(p))
p = p - psum
self.atoms.set_positions(
self.atoms.get_positions()
+ self.dt * p / self.atoms.get_masses()[:, np.newaxis]
)
# We need to store the momenta on the atoms before calculating
# the forces, as in a parallel Asap calculation atoms may
# migrate during force calculations, and the momenta need to
# migrate along with the atoms. For the same reason, we
# cannot use self.masses in the line above.
self.atoms.set_momenta(p)
forces = self.atoms.get_forces(md=True)
atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * forces)
return forces