1 """Contains the classes that deal with constant pressure dynamics.
3 Copyright (C) 2013, Joshua More and Michele Ceriotti
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http.//www.gnu.org/licenses/>.
19 Contains the algorithms which propagate the position and momenta steps in the
20 constant pressure ensemble. Holds the properties directly related to
21 these ensembles, such as the internal and external pressure and stress.
24 Barostat: Base barostat class with the generic methods and attributes.
25 BaroBZP: Generates dynamics with a stochastic barostat -- see
26 Ceriotti, More, Manolopoulos, Comp. Phys. Comm. 2013 for
27 implementation details.
30 # NB: this file also contains a 'BaroMHT' class, that follows more closely the
31 # Martyna, Hughes, Tuckerman implementation of a PIMD barostat. However it is so
32 # close to the BZP implementation that we disabled it for the sake of simplicity
33 # BaroMHT: Generates dynamics according to the method of G. Martyna, A.
34 # Hughes and M. Tuckerman, J. Chem. Phys., 110, 3275.
36 __all__
= ['Barostat', 'BaroBZP']
39 from ipi
.utils
.depend
import *
40 from ipi
.utils
.units
import *
41 from ipi
.utils
.mathtools
import eigensystem_ut3x3
, invert_ut3x3
, exp_ut3x3
, det_ut3x3
42 from ipi
.inputs
.thermostats
import InputThermo
43 from ipi
.engine
.thermostats
import Thermostat
45 class Barostat(dobject
):
46 """Base barostat class.
48 Gives the standard methods and attributes needed in all the barostat classes.
51 beads: A beads object giving the atoms positions
52 cell: A cell object giving the system box.
53 forces: A forces object giving the virial and the forces acting on
55 nm: An object to do the normal mode transformation.
56 thermostat: A thermostat coupled to the barostat degrees of freedom.
57 mdof: The number of atomic degrees of freedom
60 dt: The time step used in the algorithms. Depends on the simulation dt.
61 temp: The (classical) simulation temperature. Higher than the physical
62 temperature by a factor of the number of beads.
63 tau: The timescale associated with the piston
64 pext: The external pressure
65 ebaro: The conserved quantity associated with the barostat.
66 pot: The potential energy associated with the barostat.
67 kstress: The system kinetic stress tensor.
68 stress: The system stress tensor.
69 press: The system pressure.
72 def __init__(self
, dt
=None, temp
=None, pext
=None, tau
=None, ebaro
=None, thermostat
=None):
73 """Initialises base barostat class.
75 Note that the external stress and the external pressure are synchronized.
76 This makes most sense going from the stress to the pressure, but if you
77 must go in the other direction the stress is assumed to be isotropic.
80 dt: Optional float giving the time step for the algorithms. Defaults
82 temp: Optional float giving the temperature for the thermostat.
83 Defaults to the simulation temp.
84 pext: Optional float giving the external pressure.
85 tau: Optional float giving the time scale associated with the barostat.
86 ebaro: Optional float giving the conserved quantity already stored
87 in the barostat initially. Used on restart.
88 thermostat: The thermostat connected to the barostat degree of freedom.
91 dset(self
,"dt",depend_value(name
='dt'))
96 dset(self
, "temp", depend_value(name
="temp"))
101 dset(self
,"tau",depend_value(name
='tau'))
106 dset(self
,"pext",depend_value(name
='pext'))
109 else: self
.pext
= 0.0
111 dset(self
,"ebaro",depend_value(name
='ebaro'))
112 if not ebaro
is None:
114 else: self
.ebaro
= 0.0
116 if thermostat
is None:
117 thermostat
= Thermostat()
118 self
.thermostat
= thermostat
120 # pipes timestep and temperature to the thermostat
121 deppipe(self
,"dt", self
.thermostat
, "dt")
122 deppipe(self
, "temp", self
.thermostat
,"temp")
125 def bind(self
, beads
, nm
, cell
, forces
, prng
=None, fixdof
=None):
126 """Binds beads, cell and forces to the barostat.
128 This takes a beads object, a cell object and a forcefield object and
129 makes them members of the barostat. It also then creates the objects that
130 will hold the data needed in the barostat algorithms and the dependency
134 beads: The beads object from which the bead positions are taken.
135 nm: The normal modes propagator object
136 cell: The cell object from which the system box is taken.
137 forces: The forcefield object from which the force and virial are
139 prng: The parent PRNG to bind the thermostat to
140 fixdof: The number of blocked degrees of freedom.
149 depend_value(name
='pot', func
=self
.get_pot
,
150 dependencies
=[ dget(cell
,"V"), dget(self
,"pext") ]))
152 depend_value(name
='kstress', func
=self
.get_kstress
,
153 dependencies
=[ dget(beads
,"q"), dget(beads
,"qc"), dget(beads
,"pc"), dget(forces
,"f") ]))
155 depend_value(name
='stress', func
=self
.get_stress
,
156 dependencies
=[ dget(self
,"kstress"), dget(cell
,"V"), dget(forces
,"vir") ]))
158 depend_value(name
='press', func
=self
.get_press
,
159 dependencies
=[ dget(self
,"stress") ]))
162 self
.mdof
= float(self
.beads
.natoms
)*3.0
164 self
.mdof
= float(self
.beads
.natoms
)*3.0 - float(fixdof
)
167 """Calculates the elastic strain energy of the cell."""
169 # NOTE: since there are nbeads replicas of the unit cell, the enthalpy contains a nbeads factor
170 return self
.cell
.V
*self
.pext
*self
.beads
.nbeads
172 def get_kstress(self
):
173 """Calculates the quantum centroid virial kinetic stress tensor
177 kst
= np
.zeros((3,3),float)
178 q
= depstrip(self
.beads
.q
)
179 qc
= depstrip(self
.beads
.qc
)
180 pc
= depstrip(self
.beads
.pc
)
181 m
= depstrip(self
.beads
.m
)
182 na3
= 3*self
.beads
.natoms
183 fall
= depstrip(self
.forces
.f
)
185 for b
in range(self
.beads
.nbeads
):
188 kst
[i
,j
] -= np
.dot(q
[b
,i
:na3
:3] - qc
[i
:na3
:3],
191 # NOTE: In order to have a well-defined conserved quantity, the Nf kT term in the
192 # diagonal stress estimator must be taken from the centroid kinetic energy.
194 kst
[i
,i
] += np
.dot(pc
[i
:na3
:3],pc
[i
:na3
:3]/m
) *self
.beads
.nbeads
198 def get_stress(self
):
199 """Calculates the internal stress tensor."""
201 return (self
.kstress
+ self
.forces
.vir
)/self
.cell
.V
204 """Calculates the internal pressure."""
206 return np
.trace(self
.stress
)/3.0
209 """Dummy momenta propagator step."""
214 """Dummy centroid position propagator step."""
219 class BaroBZP(Barostat
):
220 """Bussi-Zykova-Parrinello barostat class.
222 Just extends the standard class adding finite-dt propagators for the barostat
223 velocities, positions, piston.
226 p: The momentum associated with the volume degree of freedom.
227 m: The mass associated with the volume degree of freedom.
230 def __init__(self
, dt
=None, temp
=None, pext
=None, tau
=None, ebaro
=None, thermostat
=None, p
=None):
231 """Initializes BZP barostat.
234 dt: Optional float giving the time step for the algorithms. Defaults
235 to the simulation dt.
236 temp: Optional float giving the temperature for the thermostat.
237 Defaults to the simulation temp.
238 pext: Optional float giving the external pressure.
239 tau: Optional float giving the time scale associated with the barostat.
240 ebaro: Optional float giving the conserved quantity already stored
241 in the barostat initially. Used on restart.
242 thermostat: The thermostat connected to the barostat degree of freedom.
243 p: Optional initial volume conjugate momentum. Defaults to 0.
247 super(BaroBZP
, self
).__init
__(dt
, temp
, pext
, tau
, ebaro
, thermostat
)
249 dset(self
,"p", depend_array(name
='p', value
=np
.atleast_1d(0.0)))
252 self
.p
= np
.asarray([p
])
256 def bind(self
, beads
, nm
, cell
, forces
, prng
=None, fixdof
=None):
257 """Binds beads, cell and forces to the barostat.
259 This takes a beads object, a cell object and a forcefield object and
260 makes them members of the barostat. It also then creates the objects that
261 will hold the data needed in the barostat algorithms and the dependency
265 beads: The beads object from which the bead positions are taken.
266 nm: The normal modes propagator object
267 cell: The cell object from which the system box is taken.
268 forces: The forcefield object from which the force and virial are
270 prng: The parent PRNG to bind the thermostat to
271 fixdof: The number of blocked degrees of freedom.
274 super(BaroBZP
, self
).bind(beads
, nm
, cell
, forces
, prng
, fixdof
)
276 # obtain the thermostat mass from the given time constant
277 # note that the barostat temperature is nbeads times the physical T
278 dset(self
,"m", depend_array(name
='m', value
=np
.atleast_1d(0.0),
279 func
=(lambda:np
.asarray([self
.tau
**2*3*self
.beads
.natoms
*Constants
.kb
*self
.temp
])),
280 dependencies
=[ dget(self
,"tau"), dget(self
,"temp") ] ))
282 # binds the thermostat to the piston degrees of freedom
283 self
.thermostat
.bind(pm
=[ self
.p
, self
.m
], prng
=prng
)
285 dset(self
,"kin",depend_value(name
='kin',
286 func
=(lambda:0.5*self
.p
[0]**2/self
.m
[0]),
287 dependencies
= [dget(self
,"p"), dget(self
,"m")] ) )
289 # the barostat energy must be computed from bits & pieces (overwrite the default)
290 dset(self
, "ebaro", depend_value(name
='ebaro', func
=self
.get_ebaro
,
291 dependencies
=[ dget(self
, "kin"), dget(self
, "pot"),
292 dget(self
.cell
, "V"), dget(self
, "temp"),
293 dget(self
.thermostat
,"ethermo")] ))
296 """Calculates the barostat conserved quantity."""
298 return self
.thermostat
.ethermo
+ self
.kin
+ self
.pot
- np
.log(self
.cell
.V
)*Constants
.kb
*self
.temp
302 """Propagates the momenta for half a time step."""
306 dthalf3
= dthalf
**3/3.0
308 # This differs from the BZP thermostat in that it uses just one kT in the propagator.
309 # This leads to an ensemble equaivalent to Martyna-Hughes-Tuckermann for both fixed and moving COM
310 # Anyway, it is a small correction so whatever.
311 self
.p
+= dthalf
*3.0*( self
.cell
.V
* ( self
.press
- self
.beads
.nbeads
*self
.pext
) +
312 Constants
.kb
*self
.temp
)
314 fc
= np
.sum(depstrip(self
.forces
.f
),0)/self
.beads
.nbeads
315 m
= depstrip(self
.beads
.m3
)[0]
316 pc
= depstrip(self
.beads
.pc
)
318 # I am not 100% sure, but these higher-order terms come from integrating the pressure virial term,
319 # so they should need to be multiplied by nbeads to be consistent with the equations of motion in the PI context
320 # again, these are tiny tiny terms so whatever.
321 self
.p
+= (dthalf2
*np
.dot(pc
,fc
/m
) + dthalf3
*np
.dot(fc
,fc
/m
)) * self
.beads
.nbeads
323 self
.beads
.p
+= depstrip(self
.forces
.f
)*dthalf
326 """Propagates the centroid position and momentum and the volume."""
328 v
= self
.p
[0]/self
.m
[0]
329 expq
, expp
= (np
.exp(v
*self
.dt
), np
.exp(-v
*self
.dt
))
331 m
= depstrip(self
.beads
.m3
)[0]
333 self
.nm
.qnm
[0,:] *= expq
334 self
.nm
.qnm
[0,:] += ((expq
-expp
)/(2.0*v
))* (depstrip(self
.nm
.pnm
)[0,:]/m
)
335 self
.nm
.pnm
[0,:] *= expp
340 class BaroMHT(Barostat
):
341 """Martyna-Hughes-Tuckerman barostat class.
343 Just extends the standard class adding finite-dt propagators for the barostat
344 velocities, positions, piston.
347 p: The momentum associated with the volume degree of freedom.
348 m: The mass associated with the volume degree of freedom.
351 def __init__(self
, dt
=None, temp
=None, pext
=None, tau
=None, ebaro
=None, thermostat
=None, p
=None):
352 """Initializes MHT barostat.
355 dt: Optional float giving the time step for the algorithms. Defaults
356 to the simulation dt.
357 temp: Optional float giving the temperature for the thermostat.
358 Defaults to the simulation temp.
359 pext: Optional float giving the external pressure.
360 tau: Optional float giving the time scale associated with the barostat.
361 ebaro: Optional float giving the conserved quantity already stored
362 in the barostat initially. Used on restart.
363 thermostat: The thermostat connected to the barostat degree of freedom.
364 p: Optional initial volume conjugate momentum. Defaults to 0.
367 super(BaroMHT
, self
).__init
__(dt
, temp
, pext
, tau
, ebaro
, thermostat
)
369 dset(self
,"p", depend_array(name
='p', value
=np
.atleast_1d(0.0)))
372 self
.p
= np
.asarray([p
])
376 def bind(self
, beads
, nm
, cell
, forces
, prng
=None, fixdof
=None):
377 """Binds beads, cell and forces to the barostat.
379 This takes a beads object, a cell object and a forcefield object and
380 makes them members of the barostat. It also then creates the objects that
381 will hold the data needed in the barostat algorithms and the dependency
385 beads: The beads object from which the bead positions are taken.
386 nm: The normal modes propagator object
387 cell: The cell object from which the system box is taken.
388 forces: The forcefield object from which the force and virial are
390 prng: The parent PRNG to bind the thermostat to
391 fixdof: The number of blocked degrees of freedom.
394 super(BaroMHT
, self
).bind(beads
, nm
, cell
, forces
, prng
, fixdof
)
396 # obtain the thermostat mass from the given time constant
397 # note that the barostat temperature is nbeads times the physical T
398 dset(self
,"m", depend_array(name
='m', value
=np
.atleast_1d(0.0),
399 func
=(lambda:np
.asarray([self
.tau
**2*3*self
.beads
.natoms
*Constants
.kb
*self
.temp
])),
400 dependencies
=[ dget(self
,"tau"), dget(self
,"temp") ] ))
402 # binds the thermostat to the piston degrees of freedom
403 self
.thermostat
.bind(pm
=[ self
.p
, self
.m
], prng
=prng
)
405 dset(self
,"kin",depend_value(name
='kin',
406 func
=(lambda:0.5*self
.p
[0]**2/self
.m
[0]),
407 dependencies
=[dget(self
,"p"), dget(self
,"m")] ) )
409 # the barostat energy must be computed from bits & pieces (overwrite the default)
410 dset(self
, "ebaro", depend_value(name
='ebaro', func
=self
.get_ebaro
,
411 dependencies
=[ dget(self
, "kin"), dget(self
, "pot"),
412 dget(self
.cell
, "V"), dget(self
, "temp"),
413 dget(self
.thermostat
,"ethermo")]))
416 """Calculates the barostat conserved quantity."""
418 return self
.thermostat
.ethermo
+ self
.kin
+ self
.pot
421 """Propagates the momenta for half a time step."""
425 dthalf3
= dthalf
**3/3.0
427 fc
= np
.sum(depstrip(self
.forces
.f
),0)/float(self
.beads
.nbeads
)
428 m
= depstrip(self
.beads
.m3
)[0]
429 pc
= depstrip(self
.beads
.pc
)
431 self
.p
+= dthalf
*3.0*( self
.cell
.V
* ( self
.press
- self
.beads
.nbeads
*self
.pext
) +
432 float(self
.beads
.nbeads
)/self
.mdof
*np
.dot(pc
,pc
/m
) )
434 self
.beads
.p
+= depstrip(self
.forces
.f
)*dthalf
437 """Propagates the centroid position and momentum and the volume."""
439 v
= self
.p
[0]/self
.m
[0]
440 adof
= (1 + 3.0/self
.mdof
)
441 expq
, expp
= (np
.exp(v
*self
.dt
), np
.exp( -v
*self
.dt
* adof
) )
443 m
= depstrip(self
.beads
.m3
)[0]
445 self
.nm
.qnm
[0,:] *= expq
446 self
.nm
.qnm
[0,:] += ((expq
-expp
)/(v
*(1+adof
)) *
447 (depstrip(self
.nm
.pnm
)[0,:])/m
)
448 self
.nm
.pnm
[0,:] *= expp