Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Berendsen NPT dynamics class."""
3import numpy as np
4import warnings
6from ase.md.nvtberendsen import NVTBerendsen
7import ase.units as units
10class NPTBerendsen(NVTBerendsen):
11 def __init__(self, atoms, timestep, temperature=None,
12 *, temperature_K=None, pressure=None, pressure_au=None,
13 taut=0.5e3 * units.fs, taup=1e3 * units.fs,
14 compressibility=None, compressibility_au=None, fixcm=True,
15 trajectory=None,
16 logfile=None, loginterval=1, append_trajectory=False):
17 """Berendsen (constant N, P, T) molecular dynamics.
19 This dynamics scale the velocities and volumes to maintain a constant
20 pressure and temperature. The shape of the simulation cell is not
21 altered, if that is desired use Inhomogenous_NPTBerendsen.
23 Parameters:
25 atoms: Atoms object
26 The list of atoms.
28 timestep: float
29 The time step in ASE time units.
31 temperature: float
32 The desired temperature, in Kelvin.
34 temperature_K: float
35 Alias for ``temperature``.
37 pressure: float (deprecated)
38 The desired pressure, in bar (1 bar = 1e5 Pa). Deprecated,
39 use ``pressure_au`` instead.
41 pressure: float
42 The desired pressure, in atomic units (eV/Å^3).
44 taut: float
45 Time constant for Berendsen temperature coupling in ASE
46 time units. Default: 0.5 ps.
48 taup: float
49 Time constant for Berendsen pressure coupling. Default: 1 ps.
51 compressibility: float (deprecated)
52 The compressibility of the material, in bar-1. Deprecated,
53 use ``compressibility_au`` instead.
55 compressibility_au: float
56 The compressibility of the material, in atomic units (Å^3/eV).
58 fixcm: bool (optional)
59 If True, the position and momentum of the center of mass is
60 kept unperturbed. Default: True.
62 trajectory: Trajectory object or str (optional)
63 Attach trajectory object. If *trajectory* is a string a
64 Trajectory will be constructed. Use *None* for no
65 trajectory.
67 logfile: file object or str (optional)
68 If *logfile* is a string, a file with that name will be opened.
69 Use '-' for stdout.
71 loginterval: int (optional)
72 Only write a log line for every *loginterval* time steps.
73 Default: 1
75 append_trajectory: boolean (optional)
76 Defaults to False, which causes the trajectory file to be
77 overwriten each time the dynamics is restarted from scratch.
78 If True, the new structures are appended to the trajectory
79 file instead.
82 """
84 NVTBerendsen.__init__(self, atoms, timestep, temperature=temperature,
85 temperature_K=temperature_K,
86 taut=taut, fixcm=fixcm, trajectory=trajectory,
87 logfile=logfile, loginterval=loginterval,
88 append_trajectory=append_trajectory)
89 self.taup = taup
90 self.pressure = self._process_pressure(pressure, pressure_au)
91 if compressibility is not None and compressibility_au is not None:
92 raise TypeError(
93 "Do not give both 'compressibility' and 'compressibility_au'")
94 if compressibility is not None:
95 # Specified in bar, convert to atomic units
96 warnings.warn(FutureWarning(
97 "Specify the compressibility in atomic units."))
98 self.set_compressibility(
99 compressibility_au=compressibility / (1e5 * units.Pascal))
100 else:
101 self.set_compressibility(compressibility_au=compressibility_au)
103 def set_taup(self, taup):
104 self.taup = taup
106 def get_taup(self):
107 return self.taup
109 def set_pressure(self, pressure=None, *, pressure_au=None,
110 pressure_bar=None):
111 self.pressure = self._process_pressure(pressure, pressure_bar,
112 pressure_au)
114 def get_pressure(self):
115 return self.pressure
117 def set_compressibility(self, *, compressibility_au):
118 self.compressibility = compressibility_au
120 def get_compressibility(self):
121 return self.compressibility
123 def set_timestep(self, timestep):
124 self.dt = timestep
126 def get_timestep(self):
127 return self.dt
129 def scale_positions_and_cell(self):
130 """ Do the Berendsen pressure coupling,
131 scale the atom position and the simulation cell."""
133 taupscl = self.dt / self.taup
134 stress = self.atoms.get_stress(voigt=False, include_ideal_gas=True)
135 old_pressure = -stress.trace() / 3
136 scl_pressure = (1.0 - taupscl * self.compressibility / 3.0 *
137 (self.pressure - old_pressure))
139 #print("old_pressure", old_pressure, self.pressure)
140 #print("volume scaling by:", scl_pressure)
142 cell = self.atoms.get_cell()
143 cell = scl_pressure * cell
144 self.atoms.set_cell(cell, scale_atoms=True)
146 def step(self, forces=None):
147 """ move one timestep forward using Berenden NPT molecular dynamics."""
149 NVTBerendsen.scale_velocities(self)
150 self.scale_positions_and_cell()
152 # one step velocity verlet
153 atoms = self.atoms
155 if forces is None:
156 forces = atoms.get_forces(md=True)
158 p = self.atoms.get_momenta()
159 p += 0.5 * self.dt * forces
161 if self.fix_com:
162 # calculate the center of mass
163 # momentum and subtract it
164 psum = p.sum(axis=0) / float(len(p))
165 p = p - psum
167 self.atoms.set_positions(
168 self.atoms.get_positions() +
169 self.dt * p / self.atoms.get_masses()[:, np.newaxis])
171 # We need to store the momenta on the atoms before calculating
172 # the forces, as in a parallel Asap calculation atoms may
173 # migrate during force calculations, and the momenta need to
174 # migrate along with the atoms. For the same reason, we
175 # cannot use self.masses in the line above.
177 self.atoms.set_momenta(p)
178 forces = self.atoms.get_forces(md=True)
179 atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * forces)
181 return forces
183 def _process_pressure(self, pressure, pressure_au):
184 """Handle that pressure can be specified in multiple units.
186 For at least a transition period, Berendsen NPT dynamics in ASE can
187 have the pressure specified in either bar or atomic units (eV/Å^3).
189 Two parameters:
191 pressure: None or float
192 The original pressure specification in bar.
193 A warning is issued if this is not None.
195 pressure_au: None or float
196 Pressure in ev/Å^3.
198 Exactly one of the two pressure parameters must be different from
199 None, otherwise an error is issued.
201 Return value: Pressure in eV/Å^3.
202 """
203 if (pressure is not None) + (pressure_au is not None) != 1:
204 raise TypeError("Exactly one of the parameters 'pressure',"
205 + " and 'pressure_au' must"
206 + " be given")
208 if pressure is not None:
209 w = ("The 'pressure' parameter is deprecated, please"
210 + " specify the pressure in atomic units (eV/Å^3)"
211 + " using the 'pressure_au' parameter.")
212 warnings.warn(FutureWarning(w))
213 return pressure * (1e5 * units.Pascal)
214 else:
215 return pressure_au
218class Inhomogeneous_NPTBerendsen(NPTBerendsen):
219 """Berendsen (constant N, P, T) molecular dynamics.
221 This dynamics scale the velocities and volumes to maintain a constant
222 pressure and temperature. The size of the unit cell is allowed to change
223 independently in the three directions, but the angles remain constant.
225 Usage: NPTBerendsen(atoms, timestep, temperature, taut, pressure, taup)
227 atoms
228 The list of atoms.
230 timestep
231 The time step.
233 temperature
234 The desired temperature, in Kelvin.
236 taut
237 Time constant for Berendsen temperature coupling.
239 fixcm
240 If True, the position and momentum of the center of mass is
241 kept unperturbed. Default: True.
243 pressure
244 The desired pressure, in bar (1 bar = 1e5 Pa).
246 taup
247 Time constant for Berendsen pressure coupling.
249 compressibility
250 The compressibility of the material, water 4.57E-5 bar-1, in bar-1
252 mask
253 Specifies which axes participate in the barostat. Default (1, 1, 1)
254 means that all axes participate, set any of them to zero to disable
255 the barostat in that direction.
256 """
258 def __init__(self, atoms, timestep, temperature=None,
259 *, temperature_K=None,
260 taut=0.5e3 * units.fs, pressure=None,
261 pressure_au=None, taup=1e3 * units.fs,
262 compressibility=None, compressibility_au=None,
263 mask=(1, 1, 1), fixcm=True, trajectory=None,
264 logfile=None, loginterval=1):
266 NPTBerendsen.__init__(self, atoms, timestep, temperature=temperature,
267 temperature_K=temperature_K,
268 taut=taut, taup=taup, pressure=pressure,
269 pressure_au=pressure_au,
270 compressibility=compressibility,
271 compressibility_au=compressibility_au,
272 fixcm=fixcm, trajectory=trajectory,
273 logfile=logfile, loginterval=loginterval)
274 self.mask = mask
276 def scale_positions_and_cell(self):
277 """ Do the Berendsen pressure coupling,
278 scale the atom position and the simulation cell."""
280 taupscl = self.dt * self.compressibility / self.taup / 3.0
281 stress = - self.atoms.get_stress(include_ideal_gas=True)
282 if stress.shape == (6,):
283 stress = stress[:3]
284 elif stress.shape == (3, 3):
285 stress = [stress[i][i] for i in range(3)]
286 else:
287 raise ValueError('Cannot use a stress tensor of shape ' +
288 str(stress.shape))
289 pbc = self.atoms.get_pbc()
290 scl_pressurex = 1.0 - taupscl * (self.pressure - stress[0]) \
291 * pbc[0] * self.mask[0]
292 scl_pressurey = 1.0 - taupscl * (self.pressure - stress[1]) \
293 * pbc[1] * self.mask[1]
294 scl_pressurez = 1.0 - taupscl * (self.pressure - stress[2]) \
295 * pbc[2] * self.mask[2]
296 cell = self.atoms.get_cell()
297 cell = np.array([scl_pressurex * cell[0],
298 scl_pressurey * cell[1],
299 scl_pressurez * cell[2]])
300 self.atoms.set_cell(cell, scale_atoms=True)