1 """Contains the classes that deal with constant temperature 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 thermostatting steps in the constant
20 temperature ensembles. Includes the new GLE thermostat, which can be used to
21 run PI+GLE dynamics, reducing the number of path integral beads required.
24 Thermostat: Base thermostat class with the generic methods and attributes.
25 ThermoLangevin: Holds the algorithms for a langevin thermostat.
26 ThermoPILE_L: Holds the algorithms for a path-integral langevin equation
27 thermostat, with a thermostat coupled directly to the
28 centroid coordinate of each bead.
29 ThermoPILE_G: Holds the algorithms for a path-integral langevin equation
30 thermostat, with a thermostat coupled to the kinetic energy for
32 ThermoSVR: Holds the algorithms for a stochastic velocity rescaling
34 ThermoGLE: Holds the algorithms for a generalised langevin equation
36 ThermoNMGLE: Holds the algorithms for a generalised langevin equation
37 thermostat in the normal mode representation.
38 ThermoNMGLEG: Holds the algorithms for a generalised langevin equation
39 thermostat in the normal mode representation, with kinetic energy as
40 well as potential energy sampling optimization.
43 __all__
= ['Thermostat', 'ThermoLangevin', 'ThermoPILE_L', 'ThermoPILE_G',
44 'ThermoSVR', 'ThermoGLE', 'ThermoNMGLE', 'ThermoNMGLEG']
47 from ipi
.utils
.depend
import *
48 from ipi
.utils
.units
import *
49 from ipi
.utils
.mathtools
import matrix_exp
, stab_cholesky
, root_herm
50 from ipi
.utils
.prng
import Random
51 from ipi
.utils
.messages
import verbosity
, warning
, info
52 from ipi
.engine
.beads
import Beads
53 from ipi
.engine
.normalmodes
import NormalModes
55 class Thermostat(dobject
):
56 """Base thermostat class.
58 Gives the standard methods and attributes needed in all the thermostat
62 prng: A pseudo random number generator object.
63 ndof: The number of degrees of freedom that the thermostat will be
67 dt: The time step used in the algorithms. Depends on the simulation dt.
68 temp: The simulation temperature. Higher than the system temperature by
69 a factor of the number of beads. Depends on the simulation temp.
70 ethermo: The total energy exchanged with the bath due to the thermostat.
71 p: The momentum vector that the thermostat is coupled to. Depends on the
73 m: The mass vector associated with p. Depends on the beads m object.
74 sm: The square root of the mass vector.
77 def __init__(self
, temp
= 1.0, dt
= 1.0, ethermo
=0.0):
78 """Initialises Thermostat.
81 temp: The simulation temperature. Defaults to 1.0.
82 dt: The simulation time step. Defaults to 1.0.
83 ethermo: The initial heat energy transferred to the bath.
84 Defaults to 0.0. Will be non-zero if the thermostat is
85 initialised from a checkpoint file.
88 dset(self
,"temp", depend_value(name
='temp', value
=temp
))
89 dset(self
,"dt", depend_value(name
='dt', value
=dt
))
90 dset(self
,"ethermo",depend_value(name
='ethermo',value
=ethermo
))
92 def bind(self
, beads
=None, atoms
=None, pm
=None, prng
=None, fixdof
=None):
93 """Binds the appropriate degrees of freedom to the thermostat.
95 This takes an object with degrees of freedom, and makes their momentum
96 and mass vectors members of the thermostat. It also then creates the
97 objects that will hold the data needed in the thermostat algorithms
98 and the dependency network.
101 beads: An optional beads object to take the mass and momentum vectors
103 atoms: An optional atoms object to take the mass and momentum vectors
105 pm: An optional tuple containing a single momentum value and its
107 prng: An optional pseudo random number generator object. Defaults to
109 fixdof: An optional integer which can specify the number of constraints
110 applied to the system. Defaults to zero.
113 TypeError: Raised if no appropriate degree of freedom or object
114 containing a momentum vector is specified for
115 the thermostat to couple to.
119 warning("Initializing thermostat from standard random PRNG", verbosity
.medium
)
124 if not beads
is None:
125 dset(self
,"p",beads
.p
.flatten())
126 dset(self
,"m",beads
.m3
.flatten())
127 elif not atoms
is None:
128 dset(self
,"p",dget(atoms
, "p"))
129 dset(self
,"m",dget(atoms
, "m3"))
134 raise TypeError("Thermostat.bind expects either Beads, Atoms, NormalModes, or a (p,m) tuple to bind to")
137 self
.ndof
= len(self
.p
)
139 self
.ndof
= float(len(self
.p
) - fixdof
)
142 depend_array(name
="sm", value
=np
.zeros(len(dget(self
,"m"))),
143 func
=self
.get_sm
, dependencies
=[dget(self
,"m")]))
146 """Retrieves the square root of the mass matrix.
149 A vector of the square root of the mass matrix with one value for
150 each degree of freedom.
153 return np
.sqrt(self
.m
)
156 """Dummy thermostat step."""
161 class ThermoLangevin(Thermostat
):
162 """Represents a langevin thermostat.
165 tau: Thermostat damping time scale. Larger values give a less strongly
167 T: Coefficient of the diffusive contribution of the thermostat, i.e. the
168 drift back towards equilibrium. Depends on tau and the time step.
169 S: Coefficient of the stochastic contribution of the thermostat, i.e.
170 the uncorrelated Gaussian noise. Depends on T and the temperature.
174 """Calculates the coefficient of the overall drift of the velocities."""
176 return np
.exp(-0.5*self
.dt
/self
.tau
)
179 """Calculates the coefficient of the white noise."""
181 return np
.sqrt(Constants
.kb
*self
.temp
*(1 - self
.T
**2))
183 def __init__(self
, temp
= 1.0, dt
= 1.0, tau
= 1.0, ethermo
=0.0):
184 """Initialises ThermoLangevin.
187 temp: The simulation temperature. Defaults to 1.0.
188 dt: The simulation time step. Defaults to 1.0.
189 tau: The thermostat damping timescale. Defaults to 1.0.
190 ethermo: The initial heat energy transferred to the bath.
191 Defaults to 0.0. Will be non-zero if the thermostat is
192 initialised from a checkpoint file.
195 super(ThermoLangevin
,self
).__init
__(temp
, dt
, ethermo
)
197 dset(self
,"tau",depend_value(value
=tau
,name
='tau'))
199 depend_value(name
="T",func
=self
.get_T
,
200 dependencies
=[dget(self
,"tau"), dget(self
,"dt")]))
202 depend_value(name
="S",func
=self
.get_S
,
203 dependencies
=[dget(self
,"temp"), dget(self
,"T")]))
206 """Updates the bound momentum vector with a langevin thermostat."""
208 p
= depstrip(self
.p
).copy()
209 sm
= depstrip(self
.sm
)
213 self
.ethermo
+= np
.dot(p
,p
)*0.5
215 p
+= self
.S
*self
.prng
.gvec(len(p
))
216 self
.ethermo
-= np
.dot(p
,p
)*0.5
223 class ThermoPILE_L(Thermostat
):
224 """Represents a PILE thermostat with a local centroid thermostat.
227 _thermos: The list of the different thermostats for all the ring polymer
229 nm: A normal modes object to attach the thermostat to.
230 prng: Random number generator used in the stochastic integration
234 tau: Centroid thermostat damping time scale. Larger values give a
235 less strongly coupled centroid thermostat.
236 tauk: Thermostat damping time scale for the non-centroid normal modes.
237 Depends on the ring polymer spring constant, and thus the simulation
239 pilescale: A float used to reduce the intensity of the PILE thermostat if
243 def __init__(self
, temp
= 1.0, dt
= 1.0, tau
= 1.0, ethermo
=0.0, scale
=1.0):
244 """Initialises ThermoPILE_L.
247 temp: The simulation temperature. Defaults to 1.0.
248 dt: The simulation time step. Defaults to 1.0.
249 tau: The centroid thermostat damping timescale. Defaults to 1.0.
250 ethermo: The initial conserved energy quantity. Defaults to 0.0. Will
251 be non-zero if the thermostat is initialised from a checkpoint file.
252 scale: A float used to reduce the intensity of the PILE thermostat if
256 TypeError: Raised if the thermostat is used with any object other than
257 a beads object, so that we make sure that the objects needed for the
258 normal mode transformation exist.
261 super(ThermoPILE_L
,self
).__init
__(temp
,dt
,ethermo
)
262 dset(self
,"tau",depend_value(value
=tau
,name
='tau'))
263 dset(self
,"pilescale",depend_value(value
=scale
,name
='pilescale'))
265 def bind(self
, nm
=None, prng
=None, bindcentroid
=True, fixdof
=None):
266 """Binds the appropriate degrees of freedom to the thermostat.
268 This takes a beads object with degrees of freedom, and makes its momentum
269 and mass vectors members of the thermostat. It also then creates the
270 objects that will hold the data needed in the thermostat algorithms
271 and the dependency network.
273 Gives the interface for both the PILE_L and PILE_G thermostats, which
274 only differ in their treatment of the centroid coordinate momenta.
277 nm: An optional normal mode object to take the mass and momentum
279 prng: An optional pseudo random number generator object. Defaults to
281 bindcentroid: An optional boolean which decides whether a Langevin
282 thermostat is attached to the centroid mode of each atom
283 separately, or the total kinetic energy. Defaults to True, which
284 gives a thermostat bound to each centroid momentum.
285 fixdof: An optional integer which can specify the number of constraints
286 applied to the system. Defaults to zero.
289 TypeError: Raised if no appropriate degree of freedom or object
290 containing a momentum vector is specified for
291 the thermostat to couple to.
294 if nm
is None or not type(nm
) is NormalModes
:
295 raise TypeError("ThermoPILE_L.bind expects a NormalModes argument to bind to")
301 prev_ethermo
= self
.ethermo
303 # creates a set of thermostats to be applied to individual normal modes
304 self
._thermos
= [ ThermoLangevin(temp
=1, dt
=1, tau
=1) for b
in range(nm
.nbeads
) ]
305 # optionally does not bind the centroid, so we can re-use all of this
308 self
._thermos
[0] = None
313 depend_array(name
="tauk", value
=np
.zeros(nm
.nbeads
-1,float),
314 func
=self
.get_tauk
, dependencies
=[dget(self
,"pilescale"), dget(nm
,"dynomegak")] ) )
316 # must pipe all the dependencies in such a way that values for the nm thermostats
317 # are automatically updated based on the "master" thermostat
318 def make_taugetter(k
):
319 return lambda: self
.tauk
[k
-1]
321 for t
in self
._thermos
:
326 fixdof
= None # only the centroid thermostat may have constraints
328 # bind thermostat t to the it-th bead
329 t
.bind(pm
=(nm
.pnm
[it
,:],nm
.dynm3
[it
,:]),prng
=self
.prng
, fixdof
=fixdof
)
331 deppipe(self
,"temp", t
, "temp")
332 deppipe(self
,"dt", t
, "dt")
334 # for tau it is slightly more complex
336 deppipe(self
,"tau", t
, "tau")
338 # Here we manually connect _thermos[i].tau to tauk[i].
340 dget(t
,"tau").add_dependency(dget(self
,"tauk"))
341 dget(t
,"tau")._func
= make_taugetter(it
)
342 dget(self
,"ethermo").add_dependency(dget(t
,"ethermo"))
345 # since the ethermo will be "delegated" to the normal modes thermostats,
347 # any previously-stored value between the sub-thermostats
349 for t
in self
._thermos
:
350 t
.ethermo
= prev_ethermo
/nm
.nbeads
351 dget(self
,"ethermo")._func
= self
.get_ethermo
;
352 # if we are not binding the centroid just yet, this bit of the piping
353 # is delegated to the function which is actually calling this
356 """Computes the thermostat damping time scale for the non-centroid
360 An array with the damping time scales for the non-centroid modes.
363 # Also include an optional scaling factor to reduce the intensity of NM thermostats
364 return np
.array([ self
.pilescale
/(2*self
.nm
.dynomegak
[k
]) for k
in range(1,len(self
._thermos
)) ])
366 def get_ethermo(self
):
367 """Computes the total energy transferred to the heat bath for all the
372 for t
in self
._thermos
:
377 """Updates the bound momentum vector with a PILE thermostat."""
379 # super-cool! just loop over the thermostats! it's as easy as that!
380 for t
in self
._thermos
:
384 class ThermoSVR(Thermostat
):
385 """Represents a stochastic velocity rescaling thermostat.
388 tau: Centroid thermostat damping time scale. Larger values give a
389 less strongly coupled centroid thermostat.
390 K: Scaling factor for the total kinetic energy. Depends on the
392 et: Parameter determining the strength of the thermostat coupling.
393 Depends on tau and the time step.
397 """Calculates the damping term in the propagator."""
399 return np
.exp(-0.5*self
.dt
/self
.tau
)
402 """Calculates the average kinetic energy per degree of freedom."""
404 return Constants
.kb
*self
.temp
*0.5
406 def __init__(self
, temp
= 1.0, dt
= 1.0, tau
= 1.0, ethermo
=0.0):
407 """Initialises ThermoSVR.
410 temp: The simulation temperature. Defaults to 1.0.
411 dt: The simulation time step. Defaults to 1.0.
412 tau: The thermostat damping timescale. Defaults to 1.0.
413 ethermo: The initial conserved energy quantity. Defaults to 0.0. Will
414 be non-zero if the thermostat is initialised from a checkpoint file.
417 super(ThermoSVR
,self
).__init
__(temp
,dt
,ethermo
)
419 dset(self
,"tau",depend_value(value
=tau
,name
='tau'))
421 depend_value(name
="et",func
=self
.get_et
,
422 dependencies
=[dget(self
,"tau"), dget(self
,"dt")]))
424 depend_value(name
="K",func
=self
.get_K
, dependencies
=[dget(self
,"temp")]))
427 """Updates the bound momentum vector with a stochastic velocity rescaling
428 thermostat. See G Bussi, D Donadio, M Parrinello,
429 Journal of Chemical Physics 126, 014101 (2007)
432 K
= np
.dot(depstrip(self
.p
),depstrip(self
.p
)/depstrip(self
.m
))*0.5
434 # rescaling is un-defined if the KE is zero
438 # gets the stochastic term (basically a Gamma distribution for the kinetic energy)
440 if (self
.ndof
-1)%2 == 0:
441 rg
= 2.0*self
.prng
.gamma((self
.ndof
-1)/2)
443 rg
= 2.0*self
.prng
.gamma((self
.ndof
-2)/2) + self
.prng
.g
**2
445 alpha2
= self
.et
+ self
.K
/K
*(1 - self
.et
)*(r1
**2 + rg
) + 2.0*r1
*np
.sqrt(self
.K
/K
*self
.et
*(1 - self
.et
))
446 alpha
= np
.sqrt(alpha2
)
447 if (r1
+ np
.sqrt(2*K
/self
.K
*self
.et
/(1 - self
.et
))) < 0:
450 self
.ethermo
+= K
*(1 - alpha2
)
454 class ThermoPILE_G(ThermoPILE_L
):
455 """Represents a PILE thermostat with a global centroid thermostat.
457 Simply replaces the Langevin thermostat for the centroid normal mode with
458 a global velocity rescaling thermostat.
461 def __init__(self
, temp
= 1.0, dt
= 1.0, tau
= 1.0, ethermo
=0.0, scale
= 1.0):
462 """Initialises ThermoPILE_G.
465 temp: The simulation temperature. Defaults to 1.0.
466 dt: The simulation time step. Defaults to 1.0.
467 tau: The centroid thermostat damping timescale. Defaults to 1.0.
468 ethermo: The initial conserved energy quantity. Defaults to 0.0. Will
469 be non-zero if the thermostat is initialised from a checkpoint file.
470 scale: A float used to reduce the intensity of the PILE thermostat if
474 super(ThermoPILE_G
,self
).__init
__(temp
,dt
,tau
,ethermo
)
475 dset(self
,"pilescale",depend_value(value
=scale
,name
='pilescale'))
477 def bind(self
, nm
=None, prng
=None, fixdof
=None):
478 """Binds the appropriate degrees of freedom to the thermostat.
480 This takes a beads object with degrees of freedom, and makes its momentum
481 and mass vectors members of the thermostat. It also then creates the
482 objects that will hold the data needed in the thermostat algorithms
483 and the dependency network.
485 Uses the PILE_L bind interface, with bindcentroid set to false so we can
486 specify that thermostat separately, by binding a global
487 thermostat to the centroid mode.
490 beads: An optional beads object to take the mass and momentum vectors
492 prng: An optional pseudo random number generator object. Defaults to
494 fixdof: An optional integer which can specify the number of constraints
495 applied to the system. Defaults to zero.
499 # first binds as a local PILE, then substitutes the thermostat on the centroid
500 prev_ethermo
= self
.ethermo
501 super(ThermoPILE_G
,self
).bind(nm
=nm
,prng
=prng
,bindcentroid
=False, fixdof
=fixdof
)
504 self
._thermos
[0] = ThermoSVR(temp
=1, dt
=1, tau
=1)
507 t
.bind(pm
=(nm
.pnm
[0,:],nm
.dynm3
[0,:]),prng
=self
.prng
, fixdof
=fixdof
)
508 deppipe(self
,"temp", t
, "temp")
509 deppipe(self
,"dt", t
, "dt")
510 deppipe(self
,"tau", t
, "tau")
511 dget(self
,"ethermo").add_dependency(dget(t
,"ethermo"))
513 # splits any previous ethermo between the thermostats, and finishes to bind ethermo to the sum function
514 for t
in self
._thermos
:
515 t
.ethermo
= prev_ethermo
/nm
.nbeads
516 dget(self
,"ethermo")._func
= self
.get_ethermo
;
519 class ThermoGLE(Thermostat
):
520 """Represents a GLE thermostat.
522 This is similar to a langevin thermostat, in that it uses Gaussian random
523 numbers to simulate a heat bath acting on the system, but simulates a
524 non-Markovian system by using a Markovian formulation in an extended phase
525 space. This allows for a much greater degree of flexibility, and this
526 thermostat, properly fitted, can give the an approximation to the correct
527 quantum ensemble even for a classical, 1-bead simulation. More reasonably,
528 using this thermostat allows for a far smaller number of replicas of the
529 system to be used, as the convergence of the properties
530 of the system is accelerated with respect to number of beads when PI+GLE
531 are used in combination. (See M. Ceriotti, D. E. Manolopoulos, M. Parinello,
532 J. Chem. Phys. 134, 084104 (2011)).
535 ns: The number of auxilliary degrees of freedom.
536 s: An array holding all the momenta, including the ones for the
537 auxilliary degrees of freedom.
540 A: Drift matrix giving the damping time scales for all the different
542 C: Static covariance matrix.
543 Satisfies A.C + C.transpose(A) = B.transpose(B), where B is the
544 diffusion matrix, giving the strength of the coupling of the system
545 with the heat bath, and thus the size of the stochastic
546 contribution of the thermostat.
547 T: Matrix for the diffusive contribution of the thermostat, i.e. the
548 drift back towards equilibrium. Depends on A and the time step.
549 S: Matrix for the stochastic contribution of the thermostat, i.e.
550 the uncorrelated Gaussian noise. Depends on C and T.
554 """Calculates the matrix for the overall drift of the velocities."""
556 return matrix_exp(-0.5*self
.dt
*self
.A
)
559 """Calculates the matrix for the coloured noise."""
561 SST
= Constants
.kb
*(self
.C
- np
.dot(self
.T
,np
.dot(self
.C
,self
.T
.T
)))
563 # Uses a symetric decomposition rather than Cholesky, since it is more stable
564 return root_herm(SST
)
567 """Calculates C from temp (if C is not set explicitly)"""
569 rC
= np
.identity(self
.ns
+ 1,float)*self
.temp
572 def __init__(self
, temp
= 1.0, dt
= 1.0, A
= None, C
= None, ethermo
=0.0):
573 """Initialises ThermoGLE.
576 temp: The simulation temperature. Defaults to 1.0.
577 dt: The simulation time step. Defaults to 1.0.
578 A: An optional matrix giving the drift matrix. Defaults to a single
580 C: An optional matrix giving the covariance matrix. Defaults to an
581 identity matrix times temperature with the same dimensions as the
582 total number of degrees of freedom in the system.
583 ethermo: The initial heat energy transferred to the bath.
584 Defaults to 0.0. Will be non-zero if the thermostat is
585 initialised from a checkpoint file.
588 super(ThermoGLE
,self
).__init
__(temp
,dt
,ethermo
)
591 A
= np
.identity(1,float)
592 dset(self
,"A",depend_value(value
=A
.copy(),name
='A'))
594 self
.ns
= len(self
.A
) - 1;
596 # now, this is tricky. if C is taken from temp, then we want it to be updated
597 # as a depend of temp. Otherwise, we want it to be an independent beast.
599 C
= np
.identity(self
.ns
+1,float)*self
.temp
601 depend_value(name
='C', func
=self
.get_C
,
602 dependencies
=[dget(self
,"temp")]))
604 dset(self
,"C",depend_value(value
=C
.copy(),name
='C'))
607 depend_value(name
="T",func
=self
.get_T
,
608 dependencies
=[dget(self
,"A"), dget(self
,"dt")]))
610 depend_value(name
="S",func
=self
.get_S
,
611 dependencies
=[dget(self
,"C"), dget(self
,"T")]))
615 def bind(self
, beads
=None, atoms
=None, pm
=None, prng
=None, fixdof
=None):
616 """Binds the appropriate degrees of freedom to the thermostat.
618 This takes an object with degrees of freedom, and makes their momentum
619 and mass vectors members of the thermostat. It also then creates the
620 objects that will hold the data needed in the thermostat algorithms
621 and the dependency network.
624 beads: An optional beads object to take the mass and momentum vectors
626 atoms: An optional atoms object to take the mass and momentum vectors
628 pm: An optional tuple containing a single momentum value and its
630 prng: An optional pseudo random number generator object. Defaults to
632 fixdof: An optional integer which can specify the number of constraints
633 applied to the system. Defaults to zero.
636 TypeError: Raised if no appropriate degree of freedom or object
637 containing a momentum vector is specified for
638 the thermostat to couple to.
641 super(ThermoGLE
,self
).bind(beads
,atoms
,pm
,prng
,fixdof
)
643 # allocates, initializes or restarts an array of s's
644 if self
.s
.shape
!= (self
.ns
+ 1, len(dget(self
,"m"))):
646 warning("Mismatch in GLE s array size on restart, will reinitialise to free particle.", verbosity
.low
)
647 self
.s
= np
.zeros((self
.ns
+ 1, len(dget(self
,"m"))))
649 # Initializes the s vector in the free-particle limit
650 info(" GLE additional DOFs initialised to the free-particle limit.", verbosity
.low
)
651 SC
= stab_cholesky(self
.C
*Constants
.kb
)
652 self
.s
[:] = np
.dot(SC
, self
.prng
.gvec(self
.s
.shape
))
654 info("GLE additional DOFs initialised from input.", verbosity
.medium
)
657 """Updates the bound momentum vector with a GLE thermostat"""
659 p
= depstrip(self
.p
).copy()
661 self
.s
[0,:] = self
.p
/self
.sm
663 self
.ethermo
+= np
.dot(self
.s
[0],self
.s
[0])*0.5
664 self
.s
[:] = np
.dot(self
.T
,self
.s
) + np
.dot(self
.S
,self
.prng
.gvec(self
.s
.shape
))
665 self
.ethermo
-= np
.dot(self
.s
[0],self
.s
[0])*0.5
667 self
.p
= self
.s
[0]*self
.sm
670 class ThermoNMGLE(Thermostat
):
671 """Represents a 'normal-modes' GLE thermostat.
673 An extension to the GLE thermostat which is applied in the
674 normal modes representation, and which allows to use a different
675 GLE for each normal mode
678 ns: The number of auxilliary degrees of freedom.
679 nb: The number of beads.
680 s: An array holding all the momenta, including the ones for the
681 auxilliary degrees of freedom.
684 A: Drift matrix giving the damping time scales for all the different
685 degrees of freedom (must contain nb terms).
686 C: Static covariance matrix.
687 Satisfies A.C + C.transpose(A) = B.transpose(B), where B is the
688 diffusion matrix, giving the strength of the coupling of the system
689 with the heat bath, and thus the size of the stochastic
690 contribution of the thermostat.
694 """Calculates C from temp (if C is not set explicitely)."""
696 rv
= np
.ndarray((self
.nb
, self
.ns
+1, self
.ns
+1), float)
697 for b
in range(0,self
.nb
):
698 rv
[b
] = np
.identity(self
.ns
+ 1,float)*self
.temp
701 def __init__(self
, temp
= 1.0, dt
= 1.0, A
= None, C
= None, ethermo
=0.0):
702 """Initialises ThermoGLE.
705 temp: The simulation temperature. Defaults to 1.0.
706 dt: The simulation time step. Defaults to 1.0.
707 A: An optional matrix giving the drift matrix. Defaults to a single
709 C: An optional matrix giving the covariance matrix. Defaults to an
710 identity matrix times temperature with the same dimensions as the
711 total number of degrees of freedom in the system.
712 ethermo: The initial heat energy transferred to the bath.
713 Defaults to 0.0. Will be non-zero if the thermostat is
714 initialised from a checkpoint file.
717 super(ThermoNMGLE
,self
).__init
__(temp
,dt
,ethermo
)
720 A
= np
.identity(1,float)
721 dset(self
,"A",depend_value(value
=A
.copy(),name
='A'))
723 self
.nb
= len(self
.A
)
724 self
.ns
= len(self
.A
[0]) - 1;
726 # now, this is tricky. if C is taken from temp, then we want it to be
727 # updated as a depend of temp.
728 # Otherwise, we want it to be an independent beast.
730 dset(self
,"C",depend_value(name
='C', func
=self
.get_C
, dependencies
=[dget(self
,"temp")]))
732 dset(self
,"C",depend_value(value
=C
.copy(),name
='C'))
734 def bind(self
, nm
=None, prng
=None, fixdof
=None):
735 """Binds the appropriate degrees of freedom to the thermostat.
737 This takes an object with degrees of freedom, and makes their momentum
738 and mass vectors members of the thermostat. It also then creates the
739 objects that will hold the data needed in the thermostat algorithms
740 and the dependency network. Actually, this specific thermostat requires
741 being called on a beads object.
744 nm: An optional normal modes object to take the mass and momentum
746 prng: An optional pseudo random number generator object. Defaults to
748 fixdof: An optional integer which can specify the number of constraints
749 applied to the system. Defaults to zero.
752 TypeError: Raised if no beads object is specified for
753 the thermostat to couple to.
756 if nm
is None or not type(nm
) is NormalModes
:
757 raise TypeError("ThermoNMGLE.bind expects a NormalModes argument to bind to")
764 if (nm
.nbeads
!= self
.nb
):
765 raise IndexError("The parameters in nm_gle options correspond to a bead number "+str(self
.nb
)+ " which does not match the number of beads in the path" + str(nm
.nbeads
) )
767 # allocates, initializes or restarts an array of s's
768 if self
.s
.shape
!= (self
.nb
, self
.ns
+ 1, nm
.natoms
*3) :
770 warning("Mismatch in GLE s array size on restart, will reinitialise to free particle.", verbosity
.low
)
771 self
.s
= np
.zeros((self
.nb
, self
.ns
+ 1, nm
.natoms
*3))
773 # Initializes the s vector in the free-particle limit
774 info(" GLE additional DOFs initialised to the free-particle limit.", verbosity
.low
)
775 for b
in range(self
.nb
):
776 SC
= stab_cholesky(self
.C
[b
]*Constants
.kb
)
777 self
.s
[b
] = np
.dot(SC
, self
.prng
.gvec(self
.s
[b
].shape
))
779 info("GLE additional DOFs initialised from input.", verbosity
.medium
)
781 prev_ethermo
= self
.ethermo
783 # creates a set of thermostats to be applied to individual normal modes
784 self
._thermos
= [ThermoGLE(temp
=1, dt
=1, A
=self
.A
[b
], C
=self
.C
[b
]) for b
in range(self
.nb
)]
786 # must pipe all the dependencies in such a way that values for the nm
787 # thermostats are automatically updated based on the "master" thermostat
789 return lambda: self
.A
[k
]
791 return lambda: self
.C
[k
]
794 for t
in self
._thermos
:
795 t
.s
= self
.s
[it
] # gets the s's as a slice of self.s
796 t
.bind(pm
=(nm
.pnm
[it
,:],nm
.dynm3
[it
,:]), prng
=self
.prng
) # bind thermostat t to the it-th normal mode
799 deppipe(self
,"temp", t
, "temp")
800 deppipe(self
,"dt", t
, "dt")
802 # here we pipe the A and C of individual NM to the "master" arrays
803 dget(t
,"A").add_dependency(dget(self
,"A"))
804 dget(t
,"A")._func
= make_Agetter(it
)
805 dget(t
,"C").add_dependency(dget(self
,"C"))
806 dget(t
,"C")._func
= make_Cgetter(it
)
807 dget(self
,"ethermo").add_dependency(dget(t
,"ethermo"))
810 # since the ethermo will be "delegated" to the normal modes thermostats,
812 # any previously-stored value between the sub-thermostats
813 for t
in self
._thermos
:
814 t
.ethermo
= prev_ethermo
/self
.nb
816 dget(self
,"ethermo")._func
= self
.get_ethermo
;
819 """Updates the thermostat in NM representation by looping over the
823 for t
in self
._thermos
:
826 def get_ethermo(self
):
827 """Computes the total energy transferred to the heat bath for all the nm
832 for t
in self
._thermos
:
837 class ThermoNMGLEG(ThermoNMGLE
):
838 """Represents a 'normal-modes' GLE thermostat + SVR.
840 An extension to the above NMGLE thermostat which also adds a stochastic velocity
841 rescaling to the centroid.
844 tau: Thermostat damping time scale. Larger values give a less strongly
848 def __init__(self
, temp
= 1.0, dt
= 1.0, A
= None, C
= None, tau
=1.0, ethermo
=0.0):
850 super(ThermoNMGLEG
,self
).__init
__(temp
, dt
, A
, C
, ethermo
)
851 dset(self
,"tau",depend_value(value
=tau
,name
='tau'))
853 def bind(self
, nm
=None, prng
=None, fixdof
=None):
854 """Binds the appropriate degrees of freedom to the thermostat.
856 This takes an object with degrees of freedom, and makes their momentum
857 and mass vectors members of the thermostat. It also then creates the
858 objects that will hold the data needed in the thermostat algorithms
859 and the dependency network. Actually, this specific thermostat requires
860 being called on a beads object.
863 nm: An optional normal modes object to take the mass and momentum
865 prng: An optional pseudo random number generator object. Defaults to
867 fixdof: An optional integer which can specify the number of constraints
868 applied to the system. Defaults to zero.
871 super(ThermoNMGLEG
,self
).bind(nm
, prng
, fixdof
)
873 t
= ThermoSVR(self
.temp
, self
.dt
, self
.tau
)
875 t
.bind(pm
=(nm
.pnm
[0,:],nm
.dynm3
[0,:]), prng
=self
.prng
) # bind global thermostat to centroid
878 deppipe(self
,"temp", t
, "temp")
879 deppipe(self
,"dt", t
, "dt")
880 deppipe(self
,"tau", t
, "tau")
882 dget(self
,"ethermo").add_dependency(dget(t
,"ethermo"))
883 self
._thermos
.append(t
)