root / ase / calculators / elk.py @ 16
Historique | Voir | Annoter | Télécharger (4,99 ko)
1 |
import os |
---|---|
2 |
|
3 |
import numpy as np |
4 |
|
5 |
from ase.units import Bohr, Hartree |
6 |
|
7 |
elk_parameters = { |
8 |
'swidth': Hartree,
|
9 |
} |
10 |
|
11 |
class ELK: |
12 |
def __init__(self, dir='.', xc=None, kpts=None, tasks=[0], **kwargs): |
13 |
for key, value in kwargs.items(): |
14 |
if key in elk_parameters: |
15 |
kwargs[key] /= elk_parameters[key] |
16 |
|
17 |
if xc is not None: |
18 |
if 'xctype' in kwargs: |
19 |
raise ValueError("You can't use both 'xc' and 'xctype'!") |
20 |
else:
|
21 |
kwargs['xctype'] = {'LDA': 3, # PW92 |
22 |
'PBE': 20, |
23 |
'REVPBE': 21, |
24 |
'PBESOL': 22, |
25 |
'WC06': 26, |
26 |
'AM05': 30}[xc.upper()] |
27 |
|
28 |
if kpts is not None: |
29 |
if 'autokpt' in kwargs: |
30 |
if kwargs['autokpt']: |
31 |
raise ValueError("You can't use both 'kpts' and 'autokpt'!") |
32 |
if 'ngridk' in kwargs: |
33 |
raise ValueError("You can't use both 'kpts' and 'ngridk'!") |
34 |
if 'vkloff' in kwargs: |
35 |
raise ValueError("You can't use both 'kpts' and 'vkloff'!") |
36 |
else:
|
37 |
kwargs['ngridk'] = kpts
|
38 |
vkloff = [] |
39 |
for nk in kpts: |
40 |
if nk % 2 == 0: # shift kpoint away from gamma point |
41 |
vkloff.append(0.5 / nk)
|
42 |
else:
|
43 |
vkloff.append(0)
|
44 |
kwargs['vkloff'] = vkloff
|
45 |
|
46 |
kwargs['tasks'] = tasks
|
47 |
|
48 |
self.parameters = kwargs
|
49 |
|
50 |
self.dir = dir |
51 |
self.energy = None |
52 |
|
53 |
self.converged = False |
54 |
|
55 |
def update(self, atoms): |
56 |
if (not self.converged or |
57 |
len(self.numbers) != len(atoms) or |
58 |
(self.numbers != atoms.get_atomic_numbers()).any()):
|
59 |
self.initialize(atoms)
|
60 |
self.calculate(atoms)
|
61 |
elif ((self.positions != atoms.get_positions()).any() or |
62 |
(self.pbc != atoms.get_pbc()).any() or |
63 |
(self.cell != atoms.get_cell()).any()):
|
64 |
self.calculate(atoms)
|
65 |
|
66 |
def initialize(self, atoms): |
67 |
self.numbers = atoms.get_atomic_numbers().copy()
|
68 |
self.write(atoms)
|
69 |
|
70 |
def get_potential_energy(self, atoms): |
71 |
self.update(atoms)
|
72 |
return self.energy |
73 |
|
74 |
def get_forces(self, atoms): |
75 |
self.update(atoms)
|
76 |
return self.forces.copy() |
77 |
|
78 |
def get_stress(self, atoms): |
79 |
self.update(atoms)
|
80 |
return self.stress.copy() |
81 |
|
82 |
def calculate(self, atoms): |
83 |
self.positions = atoms.get_positions().copy()
|
84 |
self.cell = atoms.get_cell().copy()
|
85 |
self.pbc = atoms.get_pbc().copy()
|
86 |
|
87 |
self.initialize(atoms)
|
88 |
assert os.system('cd %s; elk' % self.dir) == 0 |
89 |
self.read()
|
90 |
|
91 |
self.converged = True |
92 |
|
93 |
def write(self, atoms): |
94 |
if not os.path.isdir(self.dir): |
95 |
os.mkdir(self.dir)
|
96 |
fd = open('%s/elk.in' % self.dir, 'w') |
97 |
for key, value in self.parameters.items(): |
98 |
fd.write('%s\n' % key)
|
99 |
if isinstance(value, bool): |
100 |
fd.write('.%s.\n\n' % ('false', 'true')[value]) |
101 |
elif isinstance(value, (int, float)): |
102 |
fd.write('%s\n\n' % value)
|
103 |
else:
|
104 |
fd.write('%s\n\n' % ' '.join([str(x) for x in value])) |
105 |
|
106 |
fd.write('avec\n')
|
107 |
for vec in atoms.cell: |
108 |
fd.write('%.14f %.14f %.14f\n' % tuple(vec / Bohr)) |
109 |
fd.write('\n')
|
110 |
|
111 |
fd.write("sppath\n'%s'\n\n" % os.environ['ELK_SPECIES_PATH']) |
112 |
|
113 |
species = {} |
114 |
symbols = [] |
115 |
for a, symbol in enumerate(atoms.get_chemical_symbols()): |
116 |
if symbol in species: |
117 |
species[symbol].append(a) |
118 |
else:
|
119 |
species[symbol] = [a] |
120 |
symbols.append(symbol) |
121 |
fd.write('atoms\n%d\n' % len(species)) |
122 |
scaled = atoms.get_scaled_positions() |
123 |
for symbol in symbols: |
124 |
fd.write("'%s.in'\n" % symbol)
|
125 |
fd.write('%d\n' % len(species[symbol])) |
126 |
for a in species[symbol]: |
127 |
fd.write('%.14f %.14f %.14f 0.0 0.0 0.0\n' % tuple(scaled[a])) |
128 |
|
129 |
def read(self): |
130 |
fd = open('%s/TOTENERGY.OUT' % self.dir, 'r') |
131 |
self.energy = float(fd.readlines()[-1]) * Hartree |
132 |
# Forces:
|
133 |
INFO_file = '%s/INFO.OUT' % self.dir |
134 |
if os.path.isfile(INFO_file) or os.path.islink(INFO_file): |
135 |
text = open(INFO_file).read().lower()
|
136 |
assert 'convergence targets achieved' in text |
137 |
assert not 'reached self-consistent loops maximum' in text |
138 |
lines = iter(text.split('\n')) |
139 |
forces = [] |
140 |
atomnum = 0
|
141 |
for line in lines: |
142 |
if line.rfind('total force') > -1: |
143 |
forces.append(np.array([float(f) for f in line.split(':')[1].split()])) |
144 |
atomnum =+ 1
|
145 |
self.forces = np.array(forces)
|
146 |
else:
|
147 |
raise RuntimeError |
148 |
# Stress
|
149 |
self.stress = np.empty((3, 3)) |