Statistiques
| Révision :

root / ase / calculators / elk.py @ 2

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))