root / ase / optimize / optimize.py @ 1
Historique | Voir | Annoter | Télécharger (5,59 ko)
1 | 1 | tkerber | """Structure optimization. """
|
---|---|---|---|
2 | 1 | tkerber | |
3 | 1 | tkerber | import sys |
4 | 1 | tkerber | import pickle |
5 | 1 | tkerber | import time |
6 | 1 | tkerber | from math import sqrt |
7 | 1 | tkerber | from os.path import isfile |
8 | 1 | tkerber | |
9 | 1 | tkerber | import numpy as np |
10 | 1 | tkerber | |
11 | 1 | tkerber | from ase.parallel import rank, barrier |
12 | 1 | tkerber | from ase.io.trajectory import PickleTrajectory |
13 | 1 | tkerber | |
14 | 1 | tkerber | |
15 | 1 | tkerber | class Dynamics: |
16 | 1 | tkerber | """Base-class for all MD and structure optimization classes.
|
17 | 1 | tkerber |
|
18 | 1 | tkerber | Dynamics(atoms, logfile)
|
19 | 1 | tkerber |
|
20 | 1 | tkerber | atoms: Atoms object
|
21 | 1 | tkerber | The Atoms object to operate on
|
22 | 1 | tkerber | logfile: file object or str
|
23 | 1 | tkerber | If *logfile* is a string, a file with that name will be opened.
|
24 | 1 | tkerber | Use '-' for stdout.
|
25 | 1 | tkerber | trajectory: Trajectory object or str
|
26 | 1 | tkerber | Attach trajectory object. If *trajectory* is a string a
|
27 | 1 | tkerber | PickleTrajectory will be constructed. Use *None* for no
|
28 | 1 | tkerber | trajectory.
|
29 | 1 | tkerber | """
|
30 | 1 | tkerber | def __init__(self, atoms, logfile, trajectory): |
31 | 1 | tkerber | self.atoms = atoms
|
32 | 1 | tkerber | |
33 | 1 | tkerber | if rank != 0: |
34 | 1 | tkerber | logfile = None
|
35 | 1 | tkerber | elif isinstance(logfile, str): |
36 | 1 | tkerber | if logfile == '-': |
37 | 1 | tkerber | logfile = sys.stdout |
38 | 1 | tkerber | else:
|
39 | 1 | tkerber | logfile = open(logfile, 'a') |
40 | 1 | tkerber | self.logfile = logfile
|
41 | 1 | tkerber | |
42 | 1 | tkerber | self.observers = []
|
43 | 1 | tkerber | self.nsteps = 0 |
44 | 1 | tkerber | |
45 | 1 | tkerber | if trajectory is not None: |
46 | 1 | tkerber | if isinstance(trajectory, str): |
47 | 1 | tkerber | trajectory = PickleTrajectory(trajectory, 'w', atoms)
|
48 | 1 | tkerber | self.attach(trajectory)
|
49 | 1 | tkerber | |
50 | 1 | tkerber | def get_number_of_steps(self): |
51 | 1 | tkerber | return self.nsteps |
52 | 1 | tkerber | |
53 | 1 | tkerber | def insert_observer(self, function, position=0, interval=1, |
54 | 1 | tkerber | *args, **kwargs): |
55 | 1 | tkerber | """Insert an observer."""
|
56 | 1 | tkerber | if not callable(function): |
57 | 1 | tkerber | function = function.write |
58 | 1 | tkerber | self.observers.insert(position, (function, interval, args, kwargs))
|
59 | 1 | tkerber | |
60 | 1 | tkerber | def attach(self, function, interval=1, *args, **kwargs): |
61 | 1 | tkerber | """Attach callback function.
|
62 | 1 | tkerber |
|
63 | 1 | tkerber | At every *interval* steps, call *function* with arguments
|
64 | 1 | tkerber | *args* and keyword arguments *kwargs*."""
|
65 | 1 | tkerber | |
66 | 1 | tkerber | if not hasattr(function, '__call__'): |
67 | 1 | tkerber | function = function.write |
68 | 1 | tkerber | self.observers.append((function, interval, args, kwargs))
|
69 | 1 | tkerber | |
70 | 1 | tkerber | def call_observers(self): |
71 | 1 | tkerber | for function, interval, args, kwargs in self.observers: |
72 | 1 | tkerber | if self.nsteps % interval == 0: |
73 | 1 | tkerber | function(*args, **kwargs) |
74 | 1 | tkerber | |
75 | 1 | tkerber | |
76 | 1 | tkerber | class Optimizer(Dynamics): |
77 | 1 | tkerber | """Base-class for all structure optimization classes."""
|
78 | 1 | tkerber | def __init__(self, atoms, restart, logfile, trajectory): |
79 | 1 | tkerber | """Structure optimizer object.
|
80 | 1 | tkerber |
|
81 | 1 | tkerber | atoms: Atoms object
|
82 | 1 | tkerber | The Atoms object to relax.
|
83 | 1 | tkerber | restart: str
|
84 | 1 | tkerber | Filename for restart file. Default value is *None*.
|
85 | 1 | tkerber | logfile: file object or str
|
86 | 1 | tkerber | If *logfile* is a string, a file with that name will be opened.
|
87 | 1 | tkerber | Use '-' for stdout.
|
88 | 1 | tkerber | trajectory: Trajectory object or str
|
89 | 1 | tkerber | Attach trajectory object. If *trajectory* is a string a
|
90 | 1 | tkerber | PickleTrajectory will be constructed. Use *None* for no
|
91 | 1 | tkerber | trajectory.
|
92 | 1 | tkerber | """
|
93 | 1 | tkerber | Dynamics.__init__(self, atoms, logfile, trajectory)
|
94 | 1 | tkerber | self.restart = restart
|
95 | 1 | tkerber | |
96 | 1 | tkerber | if restart is None or not isfile(restart): |
97 | 1 | tkerber | self.initialize()
|
98 | 1 | tkerber | else:
|
99 | 1 | tkerber | self.read()
|
100 | 1 | tkerber | barrier() |
101 | 1 | tkerber | def initialize(self): |
102 | 1 | tkerber | pass
|
103 | 1 | tkerber | |
104 | 1 | tkerber | def run(self, fmax=0.05, steps=100000000): |
105 | 1 | tkerber | """Run structure optimization algorithm.
|
106 | 1 | tkerber |
|
107 | 1 | tkerber | This method will return when the forces on all individual
|
108 | 1 | tkerber | atoms are less than *fmax* or when the number of steps exceeds
|
109 | 1 | tkerber | *steps*."""
|
110 | 1 | tkerber | |
111 | 1 | tkerber | self.fmax = fmax
|
112 | 1 | tkerber | step = 0
|
113 | 1 | tkerber | while step < steps:
|
114 | 1 | tkerber | f = self.atoms.get_forces()
|
115 | 1 | tkerber | self.log(f)
|
116 | 1 | tkerber | self.call_observers()
|
117 | 1 | tkerber | if self.converged(f): |
118 | 1 | tkerber | return
|
119 | 1 | tkerber | self.step(f)
|
120 | 1 | tkerber | self.nsteps += 1 |
121 | 1 | tkerber | step += 1
|
122 | 1 | tkerber | |
123 | 1 | tkerber | def converged(self, forces=None): |
124 | 1 | tkerber | """Did the optimization converge?"""
|
125 | 1 | tkerber | if forces is None: |
126 | 1 | tkerber | forces = self.atoms.get_forces()
|
127 | 1 | tkerber | return (forces**2).sum(axis=1).max() < self.fmax**2 |
128 | 1 | tkerber | |
129 | 1 | tkerber | def log(self, forces): |
130 | 1 | tkerber | fmax = sqrt((forces**2).sum(axis=1).max()) |
131 | 1 | tkerber | e = self.atoms.get_potential_energy()
|
132 | 1 | tkerber | T = time.localtime() |
133 | 1 | tkerber | if self.logfile is not None: |
134 | 1 | tkerber | name = self.__class__.__name__
|
135 | 1 | tkerber | self.logfile.write('%s: %3d %02d:%02d:%02d %15.6f %12.4f\n' % |
136 | 1 | tkerber | (name, self.nsteps, T[3], T[4], T[5], e, fmax)) |
137 | 1 | tkerber | self.logfile.flush()
|
138 | 1 | tkerber | |
139 | 1 | tkerber | def dump(self, data): |
140 | 1 | tkerber | if rank == 0 and self.restart is not None: |
141 | 1 | tkerber | pickle.dump(data, open(self.restart, 'wb'), protocol=2) |
142 | 1 | tkerber | |
143 | 1 | tkerber | def load(self): |
144 | 1 | tkerber | return pickle.load(open(self.restart)) |
145 | 1 | tkerber | |
146 | 1 | tkerber | |
147 | 1 | tkerber | class NDPoly: |
148 | 1 | tkerber | def __init__(self, ndims=1, order=3): |
149 | 1 | tkerber | """Multivariate polynomium.
|
150 | 1 | tkerber |
|
151 | 1 | tkerber | ndims: int
|
152 | 1 | tkerber | Number of dimensions.
|
153 | 1 | tkerber | order: int
|
154 | 1 | tkerber | Order of polynomium."""
|
155 | 1 | tkerber | |
156 | 1 | tkerber | if ndims == 0: |
157 | 1 | tkerber | exponents = [()] |
158 | 1 | tkerber | else:
|
159 | 1 | tkerber | exponents = [] |
160 | 1 | tkerber | for i in range(order + 1): |
161 | 1 | tkerber | E = NDPoly(ndims - 1, order - i).exponents
|
162 | 1 | tkerber | exponents += [(i,) + tuple(e) for e in E] |
163 | 1 | tkerber | self.exponents = np.array(exponents)
|
164 | 1 | tkerber | self.c = None |
165 | 1 | tkerber | |
166 | 1 | tkerber | def __call__(self, *x): |
167 | 1 | tkerber | """Evaluate polynomial at x."""
|
168 | 1 | tkerber | return np.dot(self.c, (x**self.exponents).prod(1)) |
169 | 1 | tkerber | |
170 | 1 | tkerber | def fit(self, x, y): |
171 | 1 | tkerber | """Fit polynomium at points in x to values in y."""
|
172 | 1 | tkerber | A = (x**self.exponents[:, np.newaxis]).prod(2) |
173 | 1 | tkerber | self.c = np.linalg.solve(np.inner(A, A), np.dot(A, y))
|
174 | 1 | tkerber | |
175 | 1 | tkerber | |
176 | 1 | tkerber | def polyfit(x, y, order=3): |
177 | 1 | tkerber | """Fit polynomium at points in x to values in y.
|
178 | 1 | tkerber |
|
179 | 1 | tkerber | With D dimensions and N points, x must have shape (N, D) and y
|
180 | 1 | tkerber | must have length N."""
|
181 | 1 | tkerber | |
182 | 1 | tkerber | p = NDPoly(len(x[0]), order) |
183 | 1 | tkerber | p.fit(x, y) |
184 | 1 | tkerber | return p |