Coverage for /builds/kinetik161/ase/ase/optimize/mdmin.py: 96.88%
32 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1from typing import IO, Optional, Union
3import numpy as np
5from ase import Atoms
6from ase.optimize.optimize import Optimizer
9class MDMin(Optimizer):
10 # default parameters
11 defaults = {**Optimizer.defaults, 'dt': 0.2}
13 def __init__(
14 self,
15 atoms: Atoms,
16 restart: Optional[str] = None,
17 logfile: Union[IO, str] = '-',
18 trajectory: Optional[str] = None,
19 dt: Optional[float] = None,
20 maxstep: Optional[float] = None,
21 master: Optional[bool] = None,
22 ):
23 """Parameters:
25 atoms: Atoms object
26 The Atoms object to relax.
28 restart: string
29 Pickle file used to store hessian matrix. If set, file with
30 such a name will be searched and hessian matrix stored will
31 be used, if the file exists.
33 trajectory: string
34 Pickle file used to store trajectory of atomic movement.
36 logfile: string
37 Text file used to write summary information.
39 dt: float
40 Time step for integrating the equation of motion.
42 maxstep: float
43 Spatial step limit in Angstrom. This allows larger values of dt
44 while being more robust to instabilities in the optimization.
46 master: boolean
47 Defaults to None, which causes only rank 0 to save files. If
48 set to true, this rank will save files.
49 """
50 Optimizer.__init__(self, atoms, restart, logfile, trajectory, master)
52 self.dt = dt or self.defaults['dt']
53 self.maxstep = maxstep or self.defaults['maxstep']
55 def initialize(self):
56 self.v = None
58 def read(self):
59 self.v, self.dt = self.load()
61 def step(self, forces=None):
62 optimizable = self.optimizable
64 if forces is None:
65 forces = optimizable.get_forces()
67 if self.v is None:
68 self.v = np.zeros((len(optimizable), 3))
69 else:
70 self.v += 0.5 * self.dt * forces
71 # Correct velocities:
72 vf = np.vdot(self.v, forces)
73 if vf < 0.0:
74 self.v[:] = 0.0
75 else:
76 self.v[:] = forces * vf / np.vdot(forces, forces)
78 self.v += 0.5 * self.dt * forces
79 pos = optimizable.get_positions()
80 dpos = self.dt * self.v
82 # For any dpos magnitude larger than maxstep, scaling
83 # is <1. We add a small float to prevent overflows/zero-div errors.
84 # All displacement vectors (rows) of dpos which have a norm larger
85 # than self.maxstep are scaled to it.
86 scaling = self.maxstep / (1e-6 + np.max(np.linalg.norm(dpos, axis=1)))
87 dpos *= np.clip(scaling, 0.0, 1.0)
88 optimizable.set_positions(pos + dpos)
89 self.dump((self.v, self.dt))