root / ase / calculators / emt.py @ 5
Historique | Voir | Annoter | Télécharger (11,07 ko)
1 | 1 | tkerber | """Effective medium theory potential."""
|
---|---|---|---|
2 | 1 | tkerber | |
3 | 1 | tkerber | from math import sqrt, exp, log, pi |
4 | 1 | tkerber | |
5 | 1 | tkerber | import numpy as np |
6 | 1 | tkerber | import sys |
7 | 1 | tkerber | |
8 | 1 | tkerber | from ase.data import atomic_numbers, chemical_symbols |
9 | 1 | tkerber | from ase.units import Bohr |
10 | 1 | tkerber | |
11 | 1 | tkerber | |
12 | 1 | tkerber | parameters = { |
13 | 1 | tkerber | # E0 s0 V0 eta2 kappa lambda n0
|
14 | 1 | tkerber | # eV bohr eV bohr^-1 bohr^-1 bohr^-1 bohr^-3
|
15 | 1 | tkerber | 'H': (-2.21, 0.71, 2.132, 1.652, 2.790, 1.892, 0.00547, 'dimer'), |
16 | 1 | tkerber | 'Al': (-3.28, 3.00, 1.493, 1.240, 2.000, 1.169, 0.00700, 'fcc'), |
17 | 1 | tkerber | 'Cu': (-3.51, 2.67, 2.476, 1.652, 2.740, 1.906, 0.00910, 'fcc'), |
18 | 1 | tkerber | 'Ag': (-2.96, 3.01, 2.132, 1.652, 2.790, 1.892, 0.00547, 'fcc'), |
19 | 1 | tkerber | 'Au': (-3.80, 3.00, 2.321, 1.674, 2.873, 2.182, 0.00703, 'fcc'), |
20 | 1 | tkerber | 'Ni': (-4.44, 2.60, 3.673, 1.669, 2.757, 1.948, 0.01030, 'fcc'), |
21 | 1 | tkerber | 'Pd': (-3.90, 2.87, 2.773, 1.818, 3.107, 2.155, 0.00688, 'fcc'), |
22 | 1 | tkerber | 'Pt': (-5.85, 2.90, 4.067, 1.812, 3.145, 2.192, 0.00802, 'fcc'), |
23 | 1 | tkerber | 'C': (-1.97, 1.18, 0.132, 3.652, 5.790, 2.892, 0.01322, 'dimer'), |
24 | 1 | tkerber | 'N': (-4.97, 1.18, 0.132, 2.652, 3.790, 2.892, 0.01222, 'dimer'), |
25 | 1 | tkerber | 'O': (-2.97, 1.25, 2.132, 3.652, 5.790, 4.892, 0.00850, 'dimer')} |
26 | 1 | tkerber | |
27 | 1 | tkerber | beta = 1.809 #(16 * pi / 3)**(1.0 / 3) / 2**0.5 |
28 | 1 | tkerber | # but preserve historical rounding.
|
29 | 1 | tkerber | |
30 | 1 | tkerber | #eta1 = 0.5 / Bohr
|
31 | 1 | tkerber | |
32 | 1 | tkerber | class EMT: |
33 | 1 | tkerber | disabled = False # Set to True to disable (asap does this). |
34 | 1 | tkerber | |
35 | 1 | tkerber | def __init__(self, fix_alloys=False): |
36 | 1 | tkerber | self.energy = None |
37 | 1 | tkerber | self.fix_alloys = fix_alloys
|
38 | 1 | tkerber | if self.disabled: |
39 | 1 | tkerber | print >>sys.stderr, """ |
40 | 1 | tkerber | ase.EMT has been disabled by Asap. Most likely, you
|
41 | 1 | tkerber | intended to use Asap's EMT calculator, but accidentally
|
42 | 1 | tkerber | imported ase's EMT calculator after Asap's. This could
|
43 | 1 | tkerber | happen if your script contains the lines
|
44 | 1 | tkerber |
|
45 | 1 | tkerber | from asap3 import *
|
46 | 1 | tkerber | from ase.calculators.emt import EMT
|
47 | 1 | tkerber | Swap the two lines to solve the problem.
|
48 | 1 | tkerber |
|
49 | 1 | tkerber | (or 'from ase import *' in older versions of ASE.) Swap
|
50 | 1 | tkerber | the two lines to solve the problem.
|
51 | 1 | tkerber |
|
52 | 1 | tkerber | In the UNLIKELY event that you actually wanted to use
|
53 | 1 | tkerber | ase.calculators.emt.EMT although asap3 is loaded into memory, please
|
54 | 1 | tkerber | reactivate it with the command
|
55 | 1 | tkerber | ase.calculators.emt.EMT.disabled = False
|
56 | 1 | tkerber | """
|
57 | 1 | tkerber | raise RuntimeError('ase.EMT has been disabled. ' + |
58 | 1 | tkerber | 'See message printed above.')
|
59 | 1 | tkerber | |
60 | 1 | tkerber | def get_spin_polarized(self): |
61 | 1 | tkerber | return False |
62 | 1 | tkerber | |
63 | 1 | tkerber | def initialize(self, atoms): |
64 | 1 | tkerber | self.par = {}
|
65 | 1 | tkerber | self.rc = 0.0 |
66 | 1 | tkerber | self.numbers = atoms.get_atomic_numbers()
|
67 | 1 | tkerber | maxseq = 0.0
|
68 | 1 | tkerber | seen = {} |
69 | 1 | tkerber | for Z in self.numbers: |
70 | 1 | tkerber | if Z not in seen: |
71 | 1 | tkerber | seen[Z] = True
|
72 | 1 | tkerber | ss = parameters[chemical_symbols[Z]][1] * Bohr
|
73 | 1 | tkerber | if maxseq < ss:
|
74 | 1 | tkerber | maxseq = ss |
75 | 1 | tkerber | rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4)) |
76 | 1 | tkerber | rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4)) |
77 | 1 | tkerber | self.acut = np.log(9999.0) / (rr - rc) |
78 | 1 | tkerber | for Z in self.numbers: |
79 | 1 | tkerber | if Z not in self.par: |
80 | 1 | tkerber | p = parameters[chemical_symbols[Z]] |
81 | 1 | tkerber | s0 = p[1] * Bohr
|
82 | 1 | tkerber | eta2 = p[3] / Bohr
|
83 | 1 | tkerber | kappa = p[4] / Bohr
|
84 | 1 | tkerber | x = eta2 * beta * s0 |
85 | 1 | tkerber | gamma1 = 0.0
|
86 | 1 | tkerber | gamma2 = 0.0
|
87 | 1 | tkerber | if p[7] == 'fcc': |
88 | 1 | tkerber | for i, n in enumerate([12, 6, 24, 12]): |
89 | 1 | tkerber | r = s0 * beta * sqrt(i + 1)
|
90 | 1 | tkerber | x = n / (12 * (1.0 + exp(self.acut * (r - rc)))) |
91 | 1 | tkerber | gamma1 += x * exp(-eta2 * (r - beta * s0)) |
92 | 1 | tkerber | gamma2 += x * exp(-kappa / beta * (r - beta * s0)) |
93 | 1 | tkerber | elif p[7] == 'dimer': |
94 | 1 | tkerber | r = s0 * beta |
95 | 1 | tkerber | n = 1
|
96 | 1 | tkerber | x = n / (12 * (1.0 + exp(self.acut * (r - rc)))) |
97 | 1 | tkerber | gamma1 += x * exp(-eta2 * (r - beta * s0)) |
98 | 1 | tkerber | gamma2 += x * exp(-kappa / beta * (r - beta * s0)) |
99 | 1 | tkerber | else:
|
100 | 1 | tkerber | raise RuntimeError |
101 | 1 | tkerber | |
102 | 1 | tkerber | self.par[Z] = {'E0': p[0], |
103 | 1 | tkerber | 's0': s0,
|
104 | 1 | tkerber | 'V0': p[2], |
105 | 1 | tkerber | 'eta2': eta2,
|
106 | 1 | tkerber | 'kappa': kappa,
|
107 | 1 | tkerber | 'lambda': p[5] / Bohr, |
108 | 1 | tkerber | 'n0': p[6] / Bohr**3, |
109 | 1 | tkerber | 'rc': rc,
|
110 | 1 | tkerber | 'gamma1': gamma1,
|
111 | 1 | tkerber | 'gamma2': gamma2}
|
112 | 1 | tkerber | #if rc + 0.5 > self.rc:
|
113 | 1 | tkerber | # self.rc = rc + 0.5
|
114 | 1 | tkerber | |
115 | 1 | tkerber | self.ksi = {}
|
116 | 1 | tkerber | for s1, p1 in self.par.items(): |
117 | 1 | tkerber | self.ksi[s1] = {}
|
118 | 1 | tkerber | for s2, p2 in self.par.items(): |
119 | 1 | tkerber | #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
|
120 | 1 | tkerber | # exp(eta1 * (p1['s0'] - p2['s0'])))
|
121 | 1 | tkerber | self.ksi[s1][s2] = p2['n0'] / p1['n0'] |
122 | 1 | tkerber | |
123 | 1 | tkerber | self.forces = np.empty((len(atoms), 3)) |
124 | 1 | tkerber | self.sigma1 = np.empty(len(atoms)) |
125 | 1 | tkerber | self.deds = np.empty(len(atoms)) |
126 | 1 | tkerber | |
127 | 1 | tkerber | def update(self, atoms): |
128 | 1 | tkerber | if (self.energy is None or |
129 | 1 | tkerber | len(self.numbers) != len(atoms) or |
130 | 1 | tkerber | (self.numbers != atoms.get_atomic_numbers()).any()):
|
131 | 1 | tkerber | self.initialize(atoms)
|
132 | 1 | tkerber | self.calculate(atoms)
|
133 | 1 | tkerber | elif ((self.positions != atoms.get_positions()).any() or |
134 | 1 | tkerber | (self.pbc != atoms.get_pbc()).any() or |
135 | 1 | tkerber | (self.cell != atoms.get_cell()).any()):
|
136 | 1 | tkerber | self.calculate(atoms)
|
137 | 1 | tkerber | |
138 | 1 | tkerber | def calculation_required(self, atoms, quantities): |
139 | 1 | tkerber | if len(quantities) == 0: |
140 | 1 | tkerber | return False |
141 | 1 | tkerber | |
142 | 1 | tkerber | return (self.energy is None or |
143 | 1 | tkerber | len(self.numbers) != len(atoms) or |
144 | 1 | tkerber | (self.numbers != atoms.get_atomic_numbers()).any() or |
145 | 1 | tkerber | (self.positions != atoms.get_positions()).any() or |
146 | 1 | tkerber | (self.pbc != atoms.get_pbc()).any() or |
147 | 1 | tkerber | (self.cell != atoms.get_cell()).any())
|
148 | 1 | tkerber | |
149 | 1 | tkerber | def get_potential_energy(self, atoms): |
150 | 1 | tkerber | self.update(atoms)
|
151 | 1 | tkerber | return self.energy |
152 | 1 | tkerber | |
153 | 1 | tkerber | def get_numeric_forces(self, atoms): |
154 | 1 | tkerber | self.update(atoms)
|
155 | 1 | tkerber | p = atoms.positions |
156 | 1 | tkerber | p0 = p.copy() |
157 | 1 | tkerber | forces = np.empty_like(p) |
158 | 1 | tkerber | eps = 0.0001
|
159 | 1 | tkerber | for a in range(len(p)): |
160 | 1 | tkerber | for c in range(3): |
161 | 1 | tkerber | p[a, c] += eps |
162 | 1 | tkerber | self.calculate(atoms)
|
163 | 1 | tkerber | de = self.energy
|
164 | 1 | tkerber | p[a, c] -= 2 * eps
|
165 | 1 | tkerber | self.calculate(atoms)
|
166 | 1 | tkerber | de -= self.energy
|
167 | 1 | tkerber | p[a, c] += eps |
168 | 1 | tkerber | forces[a, c] = -de / (2 * eps)
|
169 | 1 | tkerber | p[:] = p0 |
170 | 1 | tkerber | return forces
|
171 | 1 | tkerber | |
172 | 1 | tkerber | def get_forces(self, atoms): |
173 | 1 | tkerber | self.update(atoms)
|
174 | 1 | tkerber | return self.forces.copy() |
175 | 1 | tkerber | |
176 | 1 | tkerber | def get_stress(self, atoms): |
177 | 1 | tkerber | raise NotImplementedError |
178 | 1 | tkerber | |
179 | 1 | tkerber | def calculate(self, atoms): |
180 | 1 | tkerber | self.positions = atoms.get_positions().copy()
|
181 | 1 | tkerber | self.cell = atoms.get_cell().copy()
|
182 | 1 | tkerber | self.pbc = atoms.get_pbc().copy()
|
183 | 1 | tkerber | |
184 | 1 | tkerber | icell = np.linalg.inv(self.cell)
|
185 | 1 | tkerber | scaled = np.dot(self.positions, icell)
|
186 | 1 | tkerber | N = [] |
187 | 1 | tkerber | for i in range(3): |
188 | 1 | tkerber | if self.pbc[i]: |
189 | 1 | tkerber | scaled[:, i] %= 1.0
|
190 | 1 | tkerber | v = icell[:, i] |
191 | 1 | tkerber | h = 1 / sqrt(np.dot(v, v))
|
192 | 1 | tkerber | N.append(int(self.rc / h) + 1) |
193 | 1 | tkerber | else:
|
194 | 1 | tkerber | N.append(0)
|
195 | 1 | tkerber | |
196 | 1 | tkerber | R = np.dot(scaled, self.cell)
|
197 | 1 | tkerber | |
198 | 1 | tkerber | self.energy = 0.0 |
199 | 1 | tkerber | self.sigma1[:] = 0.0 |
200 | 1 | tkerber | self.forces[:] = 0.0 |
201 | 1 | tkerber | |
202 | 1 | tkerber | N1, N2, N3 = N |
203 | 1 | tkerber | natoms = len(atoms)
|
204 | 1 | tkerber | for i1 in range(-N1, N1 + 1): |
205 | 1 | tkerber | for i2 in range(-N2, N2 + 1): |
206 | 1 | tkerber | for i3 in range(-N3, N3 + 1): |
207 | 1 | tkerber | C = np.dot((i1, i2, i3), self.cell)
|
208 | 1 | tkerber | Q = R + C |
209 | 1 | tkerber | c = (i1 == 0 and i2 == 0 and i3 == 0) |
210 | 1 | tkerber | for a1 in range(natoms): |
211 | 1 | tkerber | Z1 = self.numbers[a1]
|
212 | 1 | tkerber | p1 = self.par[Z1]
|
213 | 1 | tkerber | ksi = self.ksi[Z1]
|
214 | 1 | tkerber | for a2 in range(natoms): |
215 | 1 | tkerber | if c and a2 == a1: |
216 | 1 | tkerber | continue
|
217 | 1 | tkerber | d = Q[a2] - R[a1] |
218 | 1 | tkerber | r = sqrt(np.dot(d, d)) |
219 | 1 | tkerber | if r < self.rc + 0.5: |
220 | 1 | tkerber | Z2 = self.numbers[a2]
|
221 | 1 | tkerber | if self.fix_alloys: |
222 | 1 | tkerber | p2 = self.par[Z2]
|
223 | 1 | tkerber | else:
|
224 | 1 | tkerber | p2 = p1 # Old buggy behaviour
|
225 | 1 | tkerber | self.interact1(a1, a2, d, r, p1, p2, ksi[Z2])
|
226 | 1 | tkerber | |
227 | 1 | tkerber | for a in range(natoms): |
228 | 1 | tkerber | Z = self.numbers[a]
|
229 | 1 | tkerber | p = self.par[Z]
|
230 | 1 | tkerber | try:
|
231 | 1 | tkerber | ds = -log(self.sigma1[a] / 12) / (beta * p['eta2']) |
232 | 1 | tkerber | except (OverflowError, ValueError): |
233 | 1 | tkerber | self.deds[a] = 0.0 |
234 | 1 | tkerber | self.energy -= p['E0'] |
235 | 1 | tkerber | continue
|
236 | 1 | tkerber | x = p['lambda'] * ds
|
237 | 1 | tkerber | y = exp(-x) |
238 | 1 | tkerber | z = 6 * p['V0'] * exp(-p['kappa'] * ds) |
239 | 1 | tkerber | self.deds[a] = ((x * y * p['E0'] * p['lambda'] + p['kappa'] * z) / |
240 | 1 | tkerber | (self.sigma1[a] * beta * p['eta2'])) |
241 | 1 | tkerber | e = p['E0'] * ((1 + x) * y - 1) + z |
242 | 1 | tkerber | self.energy += p['E0'] * ((1 + x) * y - 1) + z |
243 | 1 | tkerber | |
244 | 1 | tkerber | for i1 in range(-N1, N1 + 1): |
245 | 1 | tkerber | for i2 in range(-N2, N2 + 1): |
246 | 1 | tkerber | for i3 in range(-N3, N3 + 1): |
247 | 1 | tkerber | C = np.dot((i1, i2, i3), self.cell)
|
248 | 1 | tkerber | Q = R + C |
249 | 1 | tkerber | c = (i1 == 0 and i2 == 0 and i3 == 0) |
250 | 1 | tkerber | for a1 in range(natoms): |
251 | 1 | tkerber | Z1 = self.numbers[a1]
|
252 | 1 | tkerber | p1 = self.par[Z1]
|
253 | 1 | tkerber | ksi = self.ksi[Z1]
|
254 | 1 | tkerber | for a2 in range(natoms): |
255 | 1 | tkerber | if c and a2 == a1: |
256 | 1 | tkerber | continue
|
257 | 1 | tkerber | d = Q[a2] - R[a1] |
258 | 1 | tkerber | r = sqrt(np.dot(d, d)) |
259 | 1 | tkerber | if r < self.rc + 0.5: |
260 | 1 | tkerber | Z2 = self.numbers[a2]
|
261 | 1 | tkerber | if self.fix_alloys: |
262 | 1 | tkerber | p2 = self.par[Z2]
|
263 | 1 | tkerber | else:
|
264 | 1 | tkerber | p2 = p1 # Old buggy behaviour
|
265 | 1 | tkerber | self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])
|
266 | 1 | tkerber | |
267 | 1 | tkerber | def interact1(self, a1, a2, d, r, p1, p2, ksi): |
268 | 1 | tkerber | x = exp(self.acut * (r - self.rc)) |
269 | 1 | tkerber | theta = 1.0 / (1.0 + x) |
270 | 1 | tkerber | y = (0.5 * p1['V0'] * exp(-p2['kappa'] * (r / beta - p2['s0'])) * |
271 | 1 | tkerber | ksi / p1['gamma2'] * theta)
|
272 | 1 | tkerber | self.energy -= y
|
273 | 1 | tkerber | f = y * (p2['kappa'] / beta + self.acut * theta * x) * d / r |
274 | 1 | tkerber | self.forces[a1] += f
|
275 | 1 | tkerber | self.forces[a2] -= f
|
276 | 1 | tkerber | self.sigma1[a1] += (exp(-p2['eta2'] * (r - beta * p2['s0'])) * |
277 | 1 | tkerber | ksi * theta / p1['gamma1'])
|
278 | 1 | tkerber | |
279 | 1 | tkerber | def interact2(self, a1, a2, d, r, p1, p2, ksi): |
280 | 1 | tkerber | x = exp(self.acut * (r - self.rc)) |
281 | 1 | tkerber | theta = 1.0 / (1.0 + x) |
282 | 1 | tkerber | y = (exp(-p2['eta2'] * (r - beta * p2['s0'])) * |
283 | 1 | tkerber | ksi / p1['gamma1'] * theta * self.deds[a1]) |
284 | 1 | tkerber | f = y * (p2['eta2'] + self.acut * theta * x) * d / r |
285 | 1 | tkerber | self.forces[a1] -= f
|
286 | 1 | tkerber | self.forces[a2] += f
|