Statistiques
| Révision :

root / ase / md / npt.py @ 16

Historique | Voir | Annoter | Télécharger (27,35 ko)

1 1 tkerber
'''Constant pressure/stress and temperature dynamics.
2 1 tkerber

3 1 tkerber
Combined Nose-Hoover and Parrinello-Rahman dynamics, creating an NPT
4 1 tkerber
(or N,stress,T) ensemble.
5 1 tkerber

6 1 tkerber
The method is the one proposed by Melchionna et al. [1] and later
7 1 tkerber
modified by Melchionna [2].  The differential equations are integrated
8 1 tkerber
using a centered difference method [3].
9 1 tkerber

10 1 tkerber
 1. S. Melchionna, G. Ciccotti and B. L. Holian, "Hoover NPT dynamics
11 1 tkerber
    for systems varying in shape and size", Molecular Physics 78, p. 533
12 1 tkerber
    (1993).
13 1 tkerber

14 1 tkerber
 2. S. Melchionna, "Constrained systems and statistical distribution",
15 1 tkerber
    Physical Review E, 61, p. 6165 (2000).
16 1 tkerber

17 1 tkerber
 3. B. L. Holian, A. J. De Groot, W. G. Hoover, and C. G. Hoover,
18 1 tkerber
    "Time-reversible equilibrium and nonequilibrium isothermal-isobaric
19 1 tkerber
    simulations with centered-difference Stoermer algorithms.", Physical
20 1 tkerber
    Review A, 41, p. 4552 (1990).
21 1 tkerber
'''
22 1 tkerber
23 1 tkerber
__docformat__ = 'reStructuredText'
24 1 tkerber
25 1 tkerber
from numpy import *
26 1 tkerber
import sys
27 1 tkerber
import time
28 1 tkerber
import weakref
29 1 tkerber
from ase.md.md import MolecularDynamics
30 1 tkerber
#from ASE.Trajectories.NetCDFTrajectory import NetCDFTrajectory
31 1 tkerber
32 1 tkerber
# Delayed imports:  If the trajectory object is reading a special ASAP version
33 1 tkerber
# of HooverNPT, that class is imported from Asap.Dynamics.NPTDynamics.
34 1 tkerber
35 1 tkerber
class NPT(MolecularDynamics):
36 1 tkerber
    '''Constant pressure/stress and temperature dynamics.
37 1 tkerber

38 1 tkerber
    Combined Nose-Hoover and Parrinello-Rahman dynamics, creating an
39 1 tkerber
    NPT (or N,stress,T) ensemble.
40 1 tkerber

41 1 tkerber
    The method is the one proposed by Melchionna et al. [1] and later
42 1 tkerber
    modified by Melchionna [2].  The differential equations are integrated
43 1 tkerber
    using a centered difference method [3].  See also NPTdynamics.tex
44 1 tkerber

45 1 tkerber
    The dynamics object is called with the following parameters:
46 1 tkerber

47 1 tkerber
    atoms
48 1 tkerber
        The list of atoms.
49 1 tkerber

50 1 tkerber
    dt
51 1 tkerber
        The timestep in units matching eV, A, u.
52 1 tkerber

53 1 tkerber
    temperature
54 1 tkerber
        The desired temperature in eV.
55 1 tkerber

56 1 tkerber
    externalstress
57 1 tkerber
        The external stress in eV/A^3.  Either a symmetric
58 1 tkerber
        3x3 tensor, a 6-vector representing the same, or a
59 1 tkerber
        scalar representing the pressure.  Note that the
60 1 tkerber
        stress is positive in tension whereas the pressure is
61 1 tkerber
        positive in compression: giving a scalar p is
62 1 tkerber
        equivalent to giving the tensor (-p, -p, -p, 0, 0, 0).
63 1 tkerber

64 1 tkerber
    ttime
65 1 tkerber
        Characteristic timescale of the thermostat.
66 1 tkerber
        Set to None to disable the thermostat.
67 1 tkerber

68 1 tkerber
    pfactor
69 1 tkerber
        A constant in the barostat differential equation.  If
70 1 tkerber
        a characteristic barostat timescale of ptime is
71 1 tkerber
        desired, set pfactor to ptime^2 * B (where B is the
72 1 tkerber
        Bulk Modulus).  Set to None to disable the barostat.
73 1 tkerber
        Typical metallic bulk moduli are of the order of
74 1 tkerber
        100 GPa or 0.6 eV/A^3.
75 1 tkerber

76 1 tkerber
    mask=None
77 1 tkerber
        Optional argument.  A tuple of three integers (0 or 1),
78 1 tkerber
        indicating if the system can change size along the
79 1 tkerber
        three Cartesian axes.  Set to (1,1,1) or None to allow
80 1 tkerber
        a fully flexible computational box.  Set to (1,1,0)
81 1 tkerber
        to disallow elongations along the z-axis etc.
82 1 tkerber

83 1 tkerber
    Useful parameter values:
84 1 tkerber

85 1 tkerber
    * The same timestep can be used as in Verlet dynamics, i.e. 5 fs is fine
86 1 tkerber
      for bulk copper.
87 1 tkerber

88 1 tkerber
    * The ttime and pfactor are quite critical[4], too small values may
89 1 tkerber
      cause instabilites and/or wrong fluctuations in T / p.  Too
90 1 tkerber
      large values cause an oscillation which is slow to die.  Good
91 1 tkerber
      values for the characteristic times seem to be 25 fs for ttime,
92 1 tkerber
      and 75 fs for ptime (used to calculate pfactor), at least for
93 1 tkerber
      bulk copper with 15000-200000 atoms.  But this is not well
94 1 tkerber
      tested, it is IMPORTANT to monitor the temperature and
95 1 tkerber
      stress/pressure fluctuations.
96 1 tkerber

97 1 tkerber
    It has the following methods:
98 1 tkerber

99 1 tkerber
    __call__(n)
100 1 tkerber
        Perform n timesteps.
101 1 tkerber
    initialize()
102 1 tkerber
        Estimates the dynamic variables for time=-1 to start
103 1 tkerber
        the algorithm.   This is automatically called before
104 1 tkerber
        the first timestep.
105 1 tkerber
    set_stress()
106 1 tkerber
        Set the external stress.  Use with care.  It is
107 1 tkerber
        preferable to set the right value when creating the
108 1 tkerber
        object.
109 1 tkerber
    set_mask()
110 1 tkerber
        Change the mask.  Use with care, as you may "freeze"
111 1 tkerber
        a fluctuation in the strain rate.
112 1 tkerber
    get_gibbs_free_energy()
113 1 tkerber
        Gibbs free energy is supposed to be preserved by this
114 1 tkerber
        dynamics.  This is mainly intended as a diagnostic
115 1 tkerber
        tool.
116 1 tkerber

117 1 tkerber
    References:
118 1 tkerber

119 1 tkerber
    1) S. Melchionna, G. Ciccotti and B. L. Holian, Molecular
120 1 tkerber
       Physics 78, p. 533 (1993).
121 1 tkerber

122 1 tkerber
    2) S. Melchionna, Physical
123 1 tkerber
       Review E 61, p. 6165 (2000).
124 1 tkerber

125 1 tkerber
    3) B. L. Holian, A. J. De Groot, W. G. Hoover, and C. G. Hoover,
126 1 tkerber
       Physical Review A 41, p. 4552 (1990).
127 1 tkerber

128 1 tkerber
    4) F. D. Di Tolla and M. Ronchetti, Physical
129 1 tkerber
       Review E 48, p. 1726 (1993).
130 1 tkerber

131 1 tkerber
    '''
132 1 tkerber
133 1 tkerber
    classname = "NPT"  # Used by the trajectory.
134 1 tkerber
    def __init__(self, atoms,
135 1 tkerber
                 timestep, temperature, externalstress, ttime, pfactor,
136 1 tkerber
                 mask=None, trajectory=None, logfile=None, loginterval=1):
137 1 tkerber
        MolecularDynamics.__init__(self, atoms, timestep, trajectory,
138 1 tkerber
                                   logfile, loginterval)
139 1 tkerber
        #self.atoms = atoms
140 1 tkerber
        #self.timestep = timestep
141 1 tkerber
        self.zero_center_of_mass_momentum(verbose=1)
142 1 tkerber
        self.temperature = temperature
143 1 tkerber
        self.set_stress(externalstress)
144 1 tkerber
        self.set_mask(mask)
145 1 tkerber
        self.eta = zeros((3,3), float)
146 1 tkerber
        self.zeta = 0.0
147 1 tkerber
        self.zeta_integrated = 0.0
148 1 tkerber
        self.initialized = 0
149 1 tkerber
        self.ttime = ttime
150 1 tkerber
        self.pfactor_given = pfactor
151 1 tkerber
        self._calculateconstants()
152 1 tkerber
        self.timeelapsed = 0.0
153 1 tkerber
        self.frac_traceless = 1
154 1 tkerber
155 1 tkerber
    def set_temperature(self, temperature):
156 1 tkerber
        self.temperature = temperature
157 1 tkerber
        self._calculateconstants()
158 1 tkerber
159 1 tkerber
    def set_stress(self, stress):
160 1 tkerber
        """Set the applied stress.
161 1 tkerber

162 1 tkerber
        Must be a symmetric 3x3 tensor, a 6-vector representing a symmetric
163 1 tkerber
        3x3 tensor, or a number representing the pressure.
164 1 tkerber
        """
165 1 tkerber
        if type(stress) == type(1.0) or type(stress) == type(1):
166 1 tkerber
            stress = array((-stress, -stress, -stress, 0.0, 0.0, 0.0))
167 1 tkerber
        elif stress.shape == (3,3):
168 1 tkerber
            if not self._issymmetric(stress):
169 1 tkerber
                raise ValueError, "The external stress must be a symmetric tensor."
170 1 tkerber
            stress = array((stress[0,0], stress[1,1], stress[2,2], stress[1,2],
171 1 tkerber
                            stress[0,2], stress[0,1]))
172 1 tkerber
        elif stress.shape != (6,):
173 1 tkerber
            raise ValueError, "The external stress has the wrong shape."
174 1 tkerber
        self.externalstress = stress
175 1 tkerber
176 1 tkerber
    def set_mask(self, mask):
177 1 tkerber
        """Set the mask indicating dynamic elements of the computational box.
178 1 tkerber

179 1 tkerber
        If set to None, all elements may change.  If set to a 3-vector
180 1 tkerber
        of ones and zeros, elements which are zero specify directions
181 1 tkerber
        along which the size of the computational box cannot change.
182 1 tkerber
        For example, if mask = {1,1,0} the length of the system along
183 1 tkerber
        the z-axis cannot change, although xz and yz shear is still
184 1 tkerber
        possible.  To disable shear globally, set the mode to diagonal
185 1 tkerber
        (not yet implemented).
186 1 tkerber
        """
187 1 tkerber
        if mask is None:
188 1 tkerber
            mask = ones((3,))
189 1 tkerber
        if not hasattr(mask, "shape"):
190 1 tkerber
            mask = array(mask)
191 1 tkerber
        if mask.shape != (3,) and mask.shape != (3,3):
192 1 tkerber
            raise "The mask has the wrong shape (must be a 3-vector or 3x3 matrix)"
193 1 tkerber
        else:
194 1 tkerber
            mask = not_equal(mask, 0)  # Make sure it is 0/1
195 1 tkerber
196 1 tkerber
        if mask.shape == (3,):
197 1 tkerber
            self.mask = outer(mask, mask)
198 1 tkerber
        else:
199 1 tkerber
            self.mask = mask
200 1 tkerber
201 1 tkerber
    def set_fraction_traceless(self, fracTraceless):
202 1 tkerber
        """set what fraction of the traceless part of the force
203 1 tkerber
        on eta is kept.
204 1 tkerber

205 1 tkerber
        By setting this to zero, the volume may change but the shape may not.
206 1 tkerber
        """
207 1 tkerber
        self.frac_traceless = fracTraceless
208 1 tkerber
209 1 tkerber
    def get_strain_rate(self):
210 1 tkerber
        "Get the strain rate as an upper-triangular 3x3 matrix"
211 1 tkerber
        return array(self.eta, copy=1)
212 1 tkerber
213 1 tkerber
    def set_strain_rate(self, rate):
214 1 tkerber
        "Set the strain rate.  Must be an upper triangular 3x3 matrix."
215 1 tkerber
        if not (rate.shape == (3,3) and self._isuppertriangular(rate)):
216 1 tkerber
            raise ValueError, "Strain rate must be an upper triangular matrix."
217 1 tkerber
        self.eta = rate
218 1 tkerber
        if self.initialized:
219 1 tkerber
            # Recalculate h_past and eta_past so they match the current value.
220 1 tkerber
            self._initialize_eta_h()
221 1 tkerber
222 1 tkerber
    def get_time(self):
223 1 tkerber
        "Get the elapsed time."
224 1 tkerber
        return self.timeelapsed
225 1 tkerber
226 1 tkerber
    def run(self, steps):
227 1 tkerber
        """Perform a number of time steps."""
228 1 tkerber
        if not self.initialized:
229 1 tkerber
            self.initialize()
230 1 tkerber
        else:
231 1 tkerber
            if self.have_the_atoms_been_changed():
232 1 tkerber
                raise NotImplementedError, "You have modified the atoms since the last timestep."
233 1 tkerber
234 1 tkerber
        for i in xrange(steps):
235 1 tkerber
            self.step()
236 1 tkerber
            self.nsteps += 1
237 1 tkerber
            self.call_observers()
238 1 tkerber
239 1 tkerber
    def have_the_atoms_been_changed(self):
240 1 tkerber
        "Checks if the user has modified the positions or momenta of the atoms"
241 1 tkerber
        limit = 1e-10
242 1 tkerber
        h = self._getbox()
243 1 tkerber
        if max(abs((h - self.h).ravel())) > limit:
244 1 tkerber
            self._warning("The computational box has been modified.")
245 1 tkerber
            return 1
246 1 tkerber
        expected_r = dot(self.q + 0.5, h)
247 1 tkerber
        err = max(abs((expected_r - self.atoms.get_positions()).ravel()))
248 1 tkerber
        if err > limit:
249 1 tkerber
            self._warning("The atomic positions have been modified: "+ str(err))
250 1 tkerber
            return 1
251 1 tkerber
        return 0
252 1 tkerber
253 1 tkerber
    def step(self):
254 1 tkerber
        """Perform a single time step.
255 1 tkerber

256 1 tkerber
        Assumes that the forces and stresses are up to date, and that
257 1 tkerber
        the positions and momenta have not been changed since last
258 1 tkerber
        timestep.
259 1 tkerber
        """
260 1 tkerber
261 1 tkerber
        ## Assumes the following variables are OK
262 1 tkerber
        # q_past, q, q_future, p, eta, eta_past, zeta, zeta_past, h, h_past
263 1 tkerber
        #
264 1 tkerber
        # q corresponds to the current positions
265 1 tkerber
        # p must be equal to self.atoms.GetCartesianMomenta()
266 1 tkerber
        # h must be equal to self.atoms.GetUnitCell()
267 1 tkerber
        #
268 1 tkerber
        #print "Making a timestep"
269 1 tkerber
        dt = self.dt
270 1 tkerber
        h_future = self.h_past + 2*dt * dot(self.h, self.eta)
271 1 tkerber
        if self.pfactor_given is None:
272 1 tkerber
            deltaeta = zeros(6, float)
273 1 tkerber
        else:
274 1 tkerber
            deltaeta = -2*dt * (self.pfact * linalg.det(self.h)
275 1 tkerber
                                * (self.atoms.get_stress()
276 1 tkerber
                                   - self.externalstress))
277 1 tkerber
278 1 tkerber
        if self.frac_traceless == 1:
279 1 tkerber
            eta_future = self.eta_past + self.mask * self._makeuppertriangular(deltaeta)
280 1 tkerber
        else:
281 1 tkerber
            trace_part, traceless_part = self._separatetrace(self._makeuppertriangular(deltaeta))
282 1 tkerber
            eta_future = self.eta_past + trace_part + self.frac_traceless * traceless_part
283 1 tkerber
284 1 tkerber
        deltazeta = 2*dt*self.tfact * (self.atoms.get_kinetic_energy()
285 1 tkerber
                                       - self.desiredEkin)
286 1 tkerber
        zeta_future = self.zeta_past + deltazeta
287 1 tkerber
        # Advance time
288 1 tkerber
        #print "Max change in scaled positions:", max(abs(self.q_future.flat - self.q.flat))
289 1 tkerber
        #print "Max change in basis set", max(abs((h_future - self.h).flat))
290 1 tkerber
        self.timeelapsed += dt
291 1 tkerber
        self.h_past = self.h
292 1 tkerber
        self.h = h_future
293 1 tkerber
        self.inv_h = linalg.inv(self.h)
294 1 tkerber
        # Do not throw away the q arrays, they are "magical" on parallel
295 1 tkerber
        # simulations (the contents migrate along with the atoms).
296 1 tkerber
        (self.q_past, self.q, self.q_future) = (self.q, self.q_future,
297 1 tkerber
                                                self.q_past)
298 1 tkerber
        self._setbox_and_positions(self.h,self.q)
299 1 tkerber
        self.eta_past = self.eta
300 1 tkerber
        self.eta = eta_future
301 1 tkerber
        self.zeta_past = self.zeta
302 1 tkerber
        self.zeta = zeta_future
303 1 tkerber
        self._synchronize()  # for parallel simulations.
304 1 tkerber
        self.zeta_integrated += dt * self.zeta
305 1 tkerber
        #self.forcecalculator()
306 1 tkerber
        force = self.atoms.get_forces()
307 1 tkerber
        # The periodic boundary conditions may have moved the atoms.
308 1 tkerber
        self.post_pbc_fix(fixfuture=0)
309 1 tkerber
        self._calculate_q_future(force)
310 1 tkerber
        self.atoms.set_momenta(dot(self.q_future-self.q_past, self.h/(2*dt)) *
311 1 tkerber
                               self._getmasses())
312 1 tkerber
        #self.stresscalculator()
313 1 tkerber
314 1 tkerber
    def initialize(self):
315 1 tkerber
        """Initialize the dynamics.
316 1 tkerber

317 1 tkerber
        The dynamics requires positions etc for the two last times to
318 1 tkerber
        do a timestep, so the algorithm is not self-starting.  This
319 1 tkerber
        method performs a 'backwards' timestep to generate a
320 1 tkerber
        configuration before the current.
321 1 tkerber
        """
322 1 tkerber
        #print "Initializing the NPT dynamics."
323 1 tkerber
        dt = self.dt
324 1 tkerber
        atoms = self.atoms
325 1 tkerber
        self.h = self._getbox()
326 1 tkerber
        if not self._isuppertriangular(self.h):
327 1 tkerber
            print "I am", self
328 1 tkerber
            print "self.h:"
329 1 tkerber
            print self.h
330 1 tkerber
            print "Min:", min((self.h[1,0], self.h[2,0], self.h[2,1]))
331 1 tkerber
            print "Max:", max((self.h[1,0], self.h[2,0], self.h[2,1]))
332 1 tkerber
            raise NotImplementedError, "Can (so far) only operate on lists of atoms where the computational box is an upper triangular matrix."
333 1 tkerber
        self.inv_h = linalg.inv(self.h)
334 1 tkerber
        # The contents of the q arrays should migrate in parallel simulations.
335 1 tkerber
        self._make_special_q_arrays()
336 1 tkerber
        self.q[:] = dot(self.atoms.get_positions(),
337 1 tkerber
                                self.inv_h) - 0.5
338 1 tkerber
        # zeta and eta were set in __init__
339 1 tkerber
        self._initialize_eta_h()
340 1 tkerber
        deltazeta = dt * self.tfact * (atoms.get_kinetic_energy() -
341 1 tkerber
                                       self.desiredEkin)
342 1 tkerber
        self.zeta_past = self.zeta - deltazeta
343 1 tkerber
        self._calculate_q_past_and_future()
344 1 tkerber
        self.initialized = 1
345 1 tkerber
346 1 tkerber
    def get_gibbs_free_energy(self):
347 1 tkerber
        """Return the Gibb's free energy, which is supposed to be conserved.
348 1 tkerber

349 1 tkerber
        Requires that the energies of the atoms are up to date.
350 1 tkerber

351 1 tkerber
        This is mainly intended as a diagnostic tool.  If called before the
352 1 tkerber
        first timestep, Initialize will be called.
353 1 tkerber
        """
354 1 tkerber
        if not self.initialized:
355 1 tkerber
            self.Initialize()
356 1 tkerber
        n = self._getnatoms()
357 1 tkerber
        #tretaTeta = sum(diagonal(matrixmultiply(transpose(self.eta),
358 1 tkerber
        #                                        self.eta)))
359 1 tkerber
        contractedeta = sum((self.eta*self.eta).ravel())
360 1 tkerber
        gibbs = (self.atoms.get_potential_energy() +
361 1 tkerber
                 self.atoms.get_kinetic_energy()
362 1 tkerber
                 - sum(self.externalstress[0:3]) * linalg.det(self.h) / 3.0)
363 1 tkerber
        if self.ttime is not None:
364 1 tkerber
            gibbs += (1.5 * n * self.temperature * (self.ttime * self.zeta)**2
365 1 tkerber
                      + 3 * self.temperature * (n-1) * self.zeta_integrated)
366 1 tkerber
        else:
367 1 tkerber
            assert self.zeta == 0.0
368 1 tkerber
        if self.pfactor_given is not None:
369 1 tkerber
            gibbs += 0.5 / self.pfact * contractedeta
370 1 tkerber
        else:
371 1 tkerber
            assert contractedeta == 0.0
372 1 tkerber
        return gibbs
373 1 tkerber
374 1 tkerber
    def get_center_of_mass_momentum(self):
375 1 tkerber
        "Get the center of mass momentum."
376 1 tkerber
        return self.atoms.get_momenta().sum(0)
377 1 tkerber
378 1 tkerber
    def zero_center_of_mass_momentum(self, verbose=0):
379 1 tkerber
        "Set the center of mass momentum to zero."
380 1 tkerber
        cm = self.get_center_of_mass_momentum()
381 1 tkerber
        abscm = sqrt(sum(cm*cm))
382 1 tkerber
        if verbose and abscm > 1e-4:
383 1 tkerber
            self._warning(self.classname+": Setting the center-of-mass momentum to zero (was %.6g %.6g %.6g)" % tuple(cm))
384 1 tkerber
        self.atoms.set_momenta(self.atoms.get_momenta()
385 1 tkerber
                               - cm / self._getnatoms())
386 1 tkerber
387 1 tkerber
    def post_pbc_fix(self, fixfuture=1):
388 1 tkerber
        """Correct for atoms moved by the boundary conditions.
389 1 tkerber

390 1 tkerber
        If the fixfuture argument is 1 (the default), q_future is also
391 1 tkerber
        corrected.  This is not necessary when post_pbc_fix() is called from
392 1 tkerber
        within Timestep(), but must be done when the user calls post_pbc_fix
393 1 tkerber
        (for example if a CNA calculation may have triggered a migration).
394 1 tkerber
        """
395 1 tkerber
        q = dot(self.atoms.get_positions(),
396 1 tkerber
                           self.inv_h) - 0.5
397 1 tkerber
        delta_q = floor(0.5 + (q - self.q))
398 1 tkerber
        self.q += delta_q
399 1 tkerber
        self.q_past += delta_q
400 1 tkerber
        if fixfuture:
401 1 tkerber
            self.q_future += delta_q
402 1 tkerber
403 1 tkerber
    def attach_atoms(self, atoms):
404 1 tkerber
        """Assign atoms to a restored dynamics object.
405 1 tkerber

406 1 tkerber
        This function must be called to set the atoms immediately after the
407 1 tkerber
        dynamics object has been read from a trajectory.
408 1 tkerber
        """
409 1 tkerber
        try:
410 1 tkerber
            self.atoms
411 1 tkerber
        except AttributeError:
412 1 tkerber
            pass
413 1 tkerber
        else:
414 1 tkerber
            raise RuntimeError, "Cannot call attach_atoms on a dynamics which already has atoms."
415 1 tkerber
        MolecularDynamics.__init__(self, atoms, self.dt)
416 1 tkerber
        ####self.atoms = atoms
417 1 tkerber
        limit = 1e-6
418 1 tkerber
        h = self._getbox()
419 1 tkerber
        if max(abs((h - self.h).ravel())) > limit:
420 1 tkerber
            raise RuntimeError, "The unit cell of the atoms does not match the unit cell stored in the file."
421 1 tkerber
        self.inv_h = linalg.inv(self.h)
422 1 tkerber
        self._make_special_q_arrays()
423 1 tkerber
        self.q[:] = dot(self.atoms.get_positions(),
424 1 tkerber
                                           self.inv_h) - 0.5
425 1 tkerber
        self._calculate_q_past_and_future()
426 1 tkerber
        self.initialized = 1
427 1 tkerber
428 1 tkerber
    def _getbox(self):
429 1 tkerber
        "Get the computational box."
430 1 tkerber
        return self.atoms.get_cell()
431 1 tkerber
432 1 tkerber
    def _getmasses(self):
433 1 tkerber
        "Get the masses as an Nx1 array."
434 1 tkerber
        return reshape(self.atoms.get_masses(), (-1,1))
435 1 tkerber
436 1 tkerber
#    def _getcartesianpositions(self):
437 1 tkerber
#        "Get the cartesian positions of the atoms"
438 1 tkerber
#        return self.atoms.get_positions()
439 1 tkerber
440 1 tkerber
#    def _getmomenta(self):
441 1 tkerber
#        "Get the (cartesian) momenta of the atoms"
442 1 tkerber
#        return self.atoms.GetCartesianMomenta()
443 1 tkerber
444 1 tkerber
#    def _getforces(self):
445 1 tkerber
#        "Get the (cartesian) forces of the atoms"
446 1 tkerber
#        return self.atoms.GetCartesianForces()
447 1 tkerber
448 1 tkerber
#    def _setmomenta(self, momenta):
449 1 tkerber
#        "Set the (cartesian) momenta of the atoms"
450 1 tkerber
#        self.atoms.SetCartesianMomenta(momenta)
451 1 tkerber
452 1 tkerber
    def _separatetrace(self, mat):
453 1 tkerber
        """return two matrices, one proportional to the identity
454 1 tkerber
        the other traceless, which sum to the given matrix
455 1 tkerber
        """
456 1 tkerber
        tracePart = ((mat[0][0] + mat[1][1] + mat[2][2]) / 3.) * identity(3)
457 1 tkerber
        return tracePart, mat - tracePart
458 1 tkerber
459 1 tkerber
    # A number of convenient helper methods
460 1 tkerber
    def _warning(self, text):
461 1 tkerber
        "Emit a warning."
462 1 tkerber
        sys.stderr.write("WARNING: "+text+"\n")
463 1 tkerber
        sys.stderr.flush()
464 1 tkerber
465 1 tkerber
    def _calculate_q_future(self, force):
466 1 tkerber
        "Calculate future q.  Needed in Timestep and Initialization."
467 1 tkerber
        dt = self.dt
468 1 tkerber
        id3 = identity(3)
469 1 tkerber
        alpha = (dt * dt) * dot(force / self._getmasses(),
470 1 tkerber
                                self.inv_h)
471 1 tkerber
        beta = dt * dot(self.h, dot(self.eta + 0.5 * self.zeta * id3,
472 1 tkerber
                                    self.inv_h))
473 1 tkerber
        inv_b = linalg.inv(beta + id3)
474 1 tkerber
        self.q_future[:] = dot(2*self.q + dot(self.q_past, beta - id3) + alpha,
475 1 tkerber
                               inv_b)
476 1 tkerber
477 1 tkerber
    def _calculate_q_past_and_future(self):
478 1 tkerber
        def ekin(p, m = self.atoms.get_masses()):
479 1 tkerber
            p2 = sum(p*p, -1)
480 1 tkerber
            return 0.5 * sum(p2 / m) / len(m)
481 1 tkerber
        p0 = self.atoms.get_momenta()
482 1 tkerber
        m = self._getmasses()
483 1 tkerber
        e0 = ekin(p0)
484 1 tkerber
        p = array(p0, copy=1)
485 1 tkerber
        dt = self.dt
486 1 tkerber
        for i in range(2):
487 1 tkerber
            self.q_past[:] = self.q - dt * dot(p / m, self.inv_h)
488 1 tkerber
            self._calculate_q_future(self.atoms.get_forces())
489 1 tkerber
            p = dot(self.q_future - self.q_past, self.h/(2*dt)) * m
490 1 tkerber
            e = ekin(p)
491 1 tkerber
            if e < 1e-5:
492 1 tkerber
                # The kinetic energy and momenta are virtually zero
493 1 tkerber
                return
494 1 tkerber
            p = (p0 - p) + p0
495 1 tkerber
496 1 tkerber
    def _initialize_eta_h(self):
497 1 tkerber
        self.h_past = self.h - self.dt * dot(self.h, self.eta)
498 1 tkerber
        if self.pfactor_given is None:
499 1 tkerber
            deltaeta = zeros(6, float)
500 1 tkerber
        else:
501 1 tkerber
            deltaeta = (-self.dt * self.pfact * linalg.det(self.h)
502 1 tkerber
                        * (self.atoms.get_stress() - self.externalstress))
503 1 tkerber
        if self.frac_traceless == 1:
504 1 tkerber
            self.eta_past = self.eta - self.mask * self._makeuppertriangular(deltaeta)
505 1 tkerber
        else:
506 1 tkerber
            trace_part, traceless_part = self._separatetrace(self._makeuppertriangular(deltaeta))
507 1 tkerber
            self.eta_past = self.eta - trace_part - self.frac_traceless * traceless_part
508 1 tkerber
509 1 tkerber
510 1 tkerber
    def _makeuppertriangular(self, sixvector):
511 1 tkerber
        "Make an upper triangular matrix from a 6-vector."
512 1 tkerber
        return array(((sixvector[0], sixvector[5], sixvector[4]),
513 1 tkerber
                      (0,            sixvector[1], sixvector[3]),
514 1 tkerber
                      (0,            0,            sixvector[2])))
515 1 tkerber
516 1 tkerber
    def _isuppertriangular(self, m):
517 1 tkerber
        "Check that a matrix is on upper triangular form."
518 1 tkerber
        return m[1,0] == m[2,0] == m[2,1] == 0.0
519 1 tkerber
520 1 tkerber
    def _calculateconstants(self):
521 1 tkerber
        "(Re)calculate some constants when pfactor, ttime or temperature have been changed."
522 1 tkerber
        n = self._getnatoms()
523 1 tkerber
        if self.ttime is None:
524 1 tkerber
            self.tfact = 0.0
525 1 tkerber
        else:
526 1 tkerber
            self.tfact = 2.0 / (3 * n * self.temperature *
527 1 tkerber
                                self.ttime * self.ttime)
528 1 tkerber
        if self.pfactor_given is None:
529 1 tkerber
            self.pfact = 0.0
530 1 tkerber
        else:
531 1 tkerber
            self.pfact = 1.0 / (self.pfactor_given
532 1 tkerber
                                * linalg.det(self._getbox()))
533 1 tkerber
            #self.pfact = 1.0/(n * self.temperature * self.ptime * self.ptime)
534 1 tkerber
        self.desiredEkin = 1.5 * (n - 1) * self.temperature
535 1 tkerber
536 1 tkerber
    def _setbox_and_positions(self, h, q):
537 1 tkerber
        """Set the computational box and the positions."""
538 1 tkerber
        self.atoms.set_cell(h, scale_atoms=True)
539 1 tkerber
        r = dot(q + 0.5, h)
540 1 tkerber
        self.atoms.set_positions(r)
541 1 tkerber
542 1 tkerber
    # A few helper methods, which have been placed in separate methods
543 1 tkerber
    # so they can be replaced in the parallel version.
544 1 tkerber
    def _synchronize(self):
545 1 tkerber
        """Synchronizes eta, h and zeta on all processors in a parallel simulation.
546 1 tkerber

547 1 tkerber
        In a parallel simulation, eta, h and zeta are communicated
548 1 tkerber
        from the master to all slaves, to prevent numerical noise from
549 1 tkerber
        causing them to diverge.
550 1 tkerber

551 1 tkerber
        In a serial simulation, do nothing.
552 1 tkerber
        """
553 1 tkerber
        pass  # This is a serial simulation object.  Do nothing.
554 1 tkerber
555 1 tkerber
    def _getnatoms(self):
556 1 tkerber
        """Get the number of atoms.
557 1 tkerber

558 1 tkerber
        In a parallel simulation, this is the total number of atoms on all
559 1 tkerber
        processors.
560 1 tkerber
        """
561 1 tkerber
        return len(self.atoms)
562 1 tkerber
563 1 tkerber
    def _make_special_q_arrays(self):
564 1 tkerber
        """Make the arrays used to store data about the atoms.
565 1 tkerber

566 1 tkerber
        In a parallel simulation, these are migrating arrays.  In a
567 1 tkerber
        serial simulation they are ordinary Numeric arrays.
568 1 tkerber
        """
569 1 tkerber
        natoms = len(self.atoms)
570 1 tkerber
        self.q = zeros((natoms,3), float)
571 1 tkerber
        self.q_past = zeros((natoms,3), float)
572 1 tkerber
        self.q_future = zeros((natoms,3), float)
573 1 tkerber
574 1 tkerber
575 1 tkerber
576 1 tkerber
# class _HooverNPTTrajectory:
577 1 tkerber
#     """A Trajectory-like object storing data in a HooverNPT object."""
578 1 tkerber
#     def InitForWrite(self):
579 1 tkerber
#         """Does initialization related to write mode."""
580 1 tkerber
#         self.CreateDimension('unlim', None)
581 1 tkerber
#         self.nc.history = 'ASE NPT trajectory'
582 1 tkerber
#         self.nc.version = '0.1'
583 1 tkerber
#         self.nc.classname = self.atoms.classname
584 1 tkerber
#         self.unlim = 0
585 1 tkerber
#         self.nc.lengthunit = units.GetLengthUnit()
586 1 tkerber
#         self.nc.energyunit = units.GetEnergyUnit()
587 1 tkerber
#         self.conversion = (1, 1)
588 1 tkerber
589 1 tkerber
#     def InitForWriteOrAppend(self):
590 1 tkerber
#         """Does initialization related to write and append mode.
591 1 tkerber
592 1 tkerber
#         Either InitForWrite or InitForReadOrAppend will have been
593 1 tkerber
#         called before calling this method.
594 1 tkerber
#         """
595 1 tkerber
#         names = copy.copy(self.known_names)
596 1 tkerber
#         if self.atoms.ttime is None:
597 1 tkerber
#             del names['ttime']
598 1 tkerber
#         if self.atoms.pfactor_given is None:
599 1 tkerber
#             del names['pfactor_given']
600 1 tkerber
#         for d in names.keys():
601 1 tkerber
#             def getdata(atoms=self.atoms, name=d):
602 1 tkerber
#                 return getattr(atoms, name)
603 1 tkerber
#             self.Add(d, data = getdata)
604 1 tkerber
605 1 tkerber
#     known_names = {
606 1 tkerber
#         #    name                 shape        typecode  once    units
607 1 tkerber
#         # ----------------------------------------------------------------
608 1 tkerber
#         'dt':              ((),                Float,    True,   (1, -0.5)),
609 1 tkerber
#         'temperature':     ((),                Float,    True,   (0, 1)),
610 1 tkerber
#         'desiredEkin':     ((),                Float,    True,   (0, 1)),
611 1 tkerber
#         'externalstress':  ((6,),              Float,    True,   (-3, 1)),
612 1 tkerber
#         'mask':            ((3, 3),            Float,    True,   (0, 0)),
613 1 tkerber
#         'ttime':           ((),                Float,    True,   (1, -0.5)),
614 1 tkerber
#         'tfact':           ((),                Float,    True,   (-2, 0)),
615 1 tkerber
#         'pfactor_given':   ((),                Float,    True,   (-1, 0)),
616 1 tkerber
#         'pfact':           ((),                Float,    True,   (-2, 0)),
617 1 tkerber
#         'frac_traceless':  ((),                Float,    True,   (0, 0)),
618 1 tkerber
#         'eta':             ((3, 3),            Float,    False,  (-1, 0.5)),
619 1 tkerber
#         'eta_past':        ((3, 3),            Float,    False,  (-1, 0.5)),
620 1 tkerber
#         'zeta':            ((),                Float,    False,  (-1, 0.5)),
621 1 tkerber
#         'zeta_past':       ((),                Float,    False,  (-1, 0.5)),
622 1 tkerber
#         'zeta_integrated': ((),                Float,    False,  (0, 0)),
623 1 tkerber
#         'h':               ((3, 3),            Float,    False,  (1, 0)),
624 1 tkerber
#         'h_past':          ((3, 3),            Float,    False,  (1, 0)),
625 1 tkerber
#         'timeelapsed':     ((),                Float,    False,  (1, -0.5))
626 1 tkerber
#         }
627 1 tkerber
628 1 tkerber
#     # This trajectory does not store a list of atoms
629 1 tkerber
#     def GetListOfAtoms(self, frame=None):
630 1 tkerber
#         raise AttributeError, "GetListOfAtoms makes no sense in a HooverNPTTrajectory"
631 1 tkerber
632 1 tkerber
#     # Instead, we store a dynamics
633 1 tkerber
#     def GetDynamics(self, frame=None):
634 1 tkerber
#         """Get a HooverNPT Dynamics object.
635 1 tkerber
636 1 tkerber
#         If a frame number is not given, the current frame is used.
637 1 tkerber
638 1 tkerber
#         The variant of the object (ASE HooverNPT, ASAP Serial/Parallel NPT)
639 1 tkerber
#         will be the same as the stored object.
640 1 tkerber
641 1 tkerber
#         After getting the dynamics, the atoms should be attached with the
642 1 tkerber
#         dynamics.attach_atoms(atoms) method.
643 1 tkerber
#         """
644 1 tkerber
#         # Bypass calling the normal constructor
645 1 tkerber
#         class Dummy:
646 1 tkerber
#             pass
647 1 tkerber
#         dyn = Dummy()
648 1 tkerber
#         dyn.__class__ = self.getClass(self.nc.classname)
649 1 tkerber
#         vars = self.nc.variables
650 1 tkerber
#         for q in self.known_names.keys():
651 1 tkerber
#             if vars.has_key(q):
652 1 tkerber
#                 once = self.known_names[q][2]
653 1 tkerber
#                 if once:
654 1 tkerber
#                     setattr(dyn, q, vars[q].getValue())
655 1 tkerber
#                 else:
656 1 tkerber
#                     setattr(dyn, q, vars[q][frame])
657 1 tkerber
#         return dyn
658 1 tkerber
659 1 tkerber
#     def getClass(self, classname):
660 1 tkerber
#         "Internal function: turns a class name into a class object."
661 1 tkerber
#         if self.nc.classname == "HooverNPT":
662 1 tkerber
#             return HooverNPT
663 1 tkerber
#         else:
664 1 tkerber
#             raise RuntimeError, ("Cannot create a dynamics of type "
665 1 tkerber
#                                  + self.nc.classname)
666 1 tkerber
667 1 tkerber
# class HooverNPTTrajectory(_HooverNPTTrajectory,NetCDFTrajectory):
668 1 tkerber
#     """A Trajectory-like object storing data in a HooverNPT object."""
669 1 tkerber
#     def __init__(self, filename, dynamics=None, mode=None, interval=1):
670 1 tkerber
#         """Open the NetCDF file.
671 1 tkerber
672 1 tkerber
#         If there is no ``dynamics`` argument, then the file is opened
673 1 tkerber
#         in read mode - otherwise, write or append mode is used.  The
674 1 tkerber
#         ``interval`` argument determines how often the configurations
675 1 tkerber
#         are written to file."""
676 1 tkerber
#         # Call the original constructor, but passing the dynamics instead of
677 1 tkerber
#         # the atoms.
678 1 tkerber
#         if dynamics is not None:
679 1 tkerber
#             # Prevents a circular reference when the trajectory is attached
680 1 tkerber
#             # to the dynamics it observes.
681 1 tkerber
#             dynamics = weakref.proxy(dynamics)
682 1 tkerber
#         NetCDFTrajectory.__init__(self, filename,
683 1 tkerber
#                                   atoms=dynamics,
684 1 tkerber
#                                   mode=mode, interval=interval)
685 1 tkerber
686 1 tkerber