Statistiques
| Révision :

root / ase / calculators / fleur.py @ 5

Historique | Voir | Annoter | Télécharger (14,15 ko)

1 1 tkerber
"""This module defines an ASE interface to FLAPW code FLEUR.
2 1 tkerber

3 1 tkerber
http://www.flapw.de
4 1 tkerber
"""
5 1 tkerber
6 1 tkerber
import os
7 1 tkerber
try:
8 1 tkerber
    from subprocess import Popen, PIPE
9 1 tkerber
except ImportError:
10 1 tkerber
    from os import popen3
11 1 tkerber
else:
12 1 tkerber
    def popen3(cmd):
13 1 tkerber
        p = Popen(cmd, shell=True, close_fds=True,
14 1 tkerber
                  stdin=PIPE, stdout=PIPE, stderr=PIPE)
15 1 tkerber
        return p.stdin, p.stdout, p.stderr
16 1 tkerber
import re
17 1 tkerber
18 1 tkerber
import numpy as np
19 1 tkerber
20 1 tkerber
from ase.units import Hartree, Bohr
21 1 tkerber
22 1 tkerber
class FLEUR:
23 1 tkerber
    """Class for doing FLEUR calculations.
24 1 tkerber

25 1 tkerber
    In order to use fleur one has to define the following environment
26 1 tkerber
    variables:
27 1 tkerber

28 1 tkerber
    FLEUR_INPGEN path to the input generator (inpgen.x) of fleur
29 1 tkerber

30 1 tkerber
    FLEUR path to the fleur executable. Note that fleur uses different
31 1 tkerber
    executable for real and complex cases (systems with/without inversion
32 1 tkerber
    symmetry), so FLEUR must point to the correct executable.
33 1 tkerber

34 1 tkerber
    It is probable that user needs to tune manually the input file before
35 1 tkerber
    the actual calculation, so in addition to the standard
36 1 tkerber
    get_potential_energy function this class defines the following utility
37 1 tkerber
    functions:
38 1 tkerber

39 1 tkerber
    write_inp
40 1 tkerber
        generate the input file `inp`
41 1 tkerber
    initialize_density
42 1 tkerber
        creates the initial density after possible manual edits of `inp`
43 1 tkerber
    calculate
44 1 tkerber
        convergence the total energy. With fleur, one specifies always
45 1 tkerber
        only the number of SCF-iterations so this function launches
46 1 tkerber
        the executable several times and monitors the convergence.
47 1 tkerber
    relax
48 1 tkerber
        Uses fleur's internal algorithm for structure
49 1 tkerber
        optimization. Requires that the proper optimization parameters
50 1 tkerber
        (atoms to optimize etc.) are specified by hand in `inp`
51 1 tkerber

52 1 tkerber
    """
53 1 tkerber
    def __init__(self, xc='LDA', kpts=None, nbands=None, convergence=None,
54 1 tkerber
                 width=None, kmax=None, mixer=None, maxiter=40,
55 1 tkerber
                 maxrelax=20, workdir=None):
56 1 tkerber
57 1 tkerber
        """Construct FLEUR-calculator object.
58 1 tkerber

59 1 tkerber
        Parameters
60 1 tkerber
        ==========
61 1 tkerber
        xc: str
62 1 tkerber
            Exchange-correlation functional. Must be one of LDA, PBE,
63 1 tkerber
            RPBE.
64 1 tkerber
        kpts: list of three int
65 1 tkerber
            Monkhost-Pack sampling.
66 1 tkerber
        nbands: int
67 1 tkerber
            Number of bands. (not used at the moment)
68 1 tkerber
        convergence: dictionary
69 1 tkerber
            Convergence parameters (currently only energy in eV)
70 1 tkerber
            {'energy' : float}
71 1 tkerber
        width: float
72 1 tkerber
            Fermi-distribution width in eV.
73 1 tkerber
        kmax: float
74 1 tkerber
            Plane wave cutoff in a.u.
75 1 tkerber
        mixer: dictionary
76 1 tkerber
            Mixing parameters imix, alpha, spinf
77 1 tkerber
            {'imix' : int, 'alpha' : float, 'spinf' : float}
78 1 tkerber
        maxiter: int
79 1 tkerber
            Maximum number of SCF iterations
80 1 tkerber
        maxrelax: int
81 1 tkerber
            Maximum number of relaxation steps
82 1 tkerber
        workdir: str
83 1 tkerber
            Working directory for the calculation
84 1 tkerber
        """
85 1 tkerber
86 1 tkerber
        self.xc = xc
87 1 tkerber
        self.kpts = kpts
88 1 tkerber
        self.nbands = nbands
89 1 tkerber
        self.width = width
90 1 tkerber
        self.kmax = kmax
91 1 tkerber
        self.maxiter = maxiter
92 1 tkerber
        self.maxrelax = maxrelax
93 1 tkerber
        self.mixer = mixer
94 1 tkerber
95 1 tkerber
        if convergence:
96 1 tkerber
            self.convergence = convergence
97 1 tkerber
            self.convergence['energy'] /= Hartree
98 1 tkerber
        else:
99 1 tkerber
            self.convergence = {'energy' : 0.0001}
100 1 tkerber
101 1 tkerber
102 1 tkerber
103 1 tkerber
        self.start_dir = None
104 1 tkerber
        self.workdir = workdir
105 1 tkerber
        if self.workdir:
106 1 tkerber
            self.start_dir = os.getcwd()
107 1 tkerber
            if not os.path.isdir(workdir):
108 1 tkerber
                os.mkdir(workdir)
109 1 tkerber
        else:
110 1 tkerber
            self.workdir = '.'
111 1 tkerber
            self.start_dir = '.'
112 1 tkerber
113 1 tkerber
        self.converged = False
114 1 tkerber
115 1 tkerber
    def update(self, atoms):
116 1 tkerber
        """Update a FLEUR calculation."""
117 1 tkerber
118 1 tkerber
        if (not self.converged or
119 1 tkerber
            len(self.numbers) != len(atoms) or
120 1 tkerber
            (self.numbers != atoms.get_atomic_numbers()).any()):
121 1 tkerber
            self.initialize(atoms)
122 1 tkerber
            self.calculate(atoms)
123 1 tkerber
        elif ((self.positions != atoms.get_positions()).any() or
124 1 tkerber
              (self.pbc != atoms.get_pbc()).any() or
125 1 tkerber
              (self.cell != atoms.get_cell()).any()):
126 1 tkerber
            self.converged = False
127 1 tkerber
            self.initialize(atoms)
128 1 tkerber
            self.calculate(atoms)
129 1 tkerber
130 1 tkerber
    def initialize(self, atoms):
131 1 tkerber
        """Create an input file inp and generate starting density."""
132 1 tkerber
133 1 tkerber
        self.converged = False
134 1 tkerber
        self.initialize_inp(atoms)
135 1 tkerber
        self.initialize_density()
136 1 tkerber
137 1 tkerber
    def initialize_inp(self, atoms):
138 1 tkerber
        """Create a inp file"""
139 1 tkerber
        os.chdir(self.workdir)
140 1 tkerber
141 1 tkerber
        self.numbers = atoms.get_atomic_numbers().copy()
142 1 tkerber
        self.positions = atoms.get_positions().copy()
143 1 tkerber
        self.cell = atoms.get_cell().copy()
144 1 tkerber
        self.pbc = atoms.get_pbc().copy()
145 1 tkerber
146 1 tkerber
        # create the input
147 1 tkerber
        self.write_inp(atoms)
148 1 tkerber
149 1 tkerber
        os.chdir(self.start_dir)
150 1 tkerber
151 1 tkerber
    def initialize_density(self):
152 1 tkerber
        """Creates a new starting density."""
153 1 tkerber
154 1 tkerber
        os.chdir(self.workdir)
155 1 tkerber
        # remove possible conflicting files
156 1 tkerber
        files2remove = ['cdn1', 'fl7para', 'stars', 'wkf2',
157 1 tkerber
                        'kpts', 'broyd', 'broyd.7', 'tmat', 'tmas']
158 1 tkerber
159 1 tkerber
        for f in files2remove:
160 1 tkerber
            if os.path.isfile(f):
161 1 tkerber
                os.remove(f)
162 1 tkerber
163 1 tkerber
        # generate the starting density
164 1 tkerber
        os.system("sed -i -e 's/strho=./strho=T/' inp")
165 1 tkerber
        try:
166 1 tkerber
            fleur_exe = os.environ['FLEUR']
167 1 tkerber
        except KeyError:
168 1 tkerber
            raise RuntimeError('Please set FLEUR')
169 1 tkerber
        cmd = popen3(fleur_exe)[2]
170 1 tkerber
        stat = cmd.read()
171 1 tkerber
        if '!' in stat:
172 1 tkerber
            raise RuntimeError('FLEUR exited with a code %s' % stat)
173 1 tkerber
        os.system("sed -i -e 's/strho=./strho=F/' inp")
174 1 tkerber
175 1 tkerber
        os.chdir(self.start_dir)
176 1 tkerber
177 1 tkerber
    def get_potential_energy(self, atoms, force_consistent=False):
178 1 tkerber
        self.update(atoms)
179 1 tkerber
180 1 tkerber
        if force_consistent:
181 1 tkerber
            return self.efree * Hartree
182 1 tkerber
        else:
183 1 tkerber
            # Energy extrapolated to zero Kelvin:
184 1 tkerber
            return  (self.etotal + self.efree) / 2 * Hartree
185 1 tkerber
186 1 tkerber
    def get_forces(self, atoms):
187 1 tkerber
        self.update(atoms)
188 1 tkerber
        # electronic structure is converged, so let's calculate forces:
189 1 tkerber
        # TODO
190 1 tkerber
        return np.array((0.0, 0.0, 0.0))
191 1 tkerber
192 1 tkerber
    def get_stress(self, atoms):
193 1 tkerber
        raise NotImplementedError
194 1 tkerber
195 1 tkerber
    def get_dipole_moment(self, atoms):
196 1 tkerber
        """Returns total dipole moment of the system."""
197 1 tkerber
        raise NotImplementedError
198 1 tkerber
199 1 tkerber
    def calculate(self, atoms):
200 1 tkerber
        """Converge a FLEUR calculation to self-consistency.
201 1 tkerber

202 1 tkerber
           Input files should be generated before calling this function
203 1 tkerber
           FLEUR performs always fixed number of SCF steps. This function
204 1 tkerber
           reduces the number of iterations gradually, however, a minimum
205 1 tkerber
           of five SCF steps is always performed.
206 1 tkerber
        """
207 1 tkerber
208 1 tkerber
        os.chdir(self.workdir)
209 1 tkerber
        try:
210 1 tkerber
            fleur_exe = os.environ['FLEUR']
211 1 tkerber
        except KeyError:
212 1 tkerber
            raise RuntimeError('Please set FLEUR')
213 1 tkerber
214 1 tkerber
        self.niter = 0
215 1 tkerber
        out = ''
216 1 tkerber
        err = ''
217 1 tkerber
        while not self.converged:
218 1 tkerber
            if self.niter > self.maxiter:
219 1 tkerber
                raise RuntimeError('FLEUR failed to convergence in %d iterations' % self.maxiter)
220 1 tkerber
221 1 tkerber
            p = Popen(fleur_exe, shell=True, stdin=PIPE, stdout=PIPE,
222 1 tkerber
                      stderr=PIPE)
223 1 tkerber
            stat = p.wait()
224 1 tkerber
            out = p.stdout.read()
225 1 tkerber
            err = p.stderr.read()
226 1 tkerber
            print err.strip()
227 1 tkerber
            if stat != 0:
228 1 tkerber
                raise RuntimeError('FLEUR exited with a code %d' % stat)
229 1 tkerber
            # catenate new output with the old one
230 1 tkerber
            os.system('cat out >> out.old')
231 1 tkerber
            self.read()
232 1 tkerber
            self.check_convergence()
233 1 tkerber
234 1 tkerber
        os.rename('out.old', 'out')
235 1 tkerber
        # After convergence clean up broyd* files
236 1 tkerber
        os.system('rm -f broyd*')
237 1 tkerber
        os.chdir(self.start_dir)
238 1 tkerber
        return out, err
239 1 tkerber
240 1 tkerber
    def relax(self, atoms):
241 1 tkerber
        """Currently, user has to manually define relaxation parameters
242 1 tkerber
           (atoms to relax, relaxation directions, etc.) in inp file
243 1 tkerber
           before calling this function."""
244 1 tkerber
245 1 tkerber
        nrelax = 0
246 1 tkerber
        relaxed = False
247 1 tkerber
        while not relaxed:
248 1 tkerber
            # Calculate electronic structure
249 1 tkerber
            self.calculate(atoms)
250 1 tkerber
            # Calculate the Pulay forces
251 1 tkerber
            os.system("sed -i -e 's/l_f=./l_f=T/' inp")
252 1 tkerber
            while True:
253 1 tkerber
                self.converged = False
254 1 tkerber
                out, err = self.calculate(atoms)
255 1 tkerber
                if 'GEO new' in err:
256 1 tkerber
                    os.chdir(self.workdir)
257 1 tkerber
                    os.rename('inp_new', 'inp')
258 1 tkerber
                    os.chdir(self.start_dir)
259 1 tkerber
                    break
260 1 tkerber
            if 'GEO: Des woas' in err:
261 1 tkerber
                relaxed = True
262 1 tkerber
                break
263 1 tkerber
            nrelax += 1
264 1 tkerber
            # save the out and cdn1 files
265 1 tkerber
            os.system('cp out out_%d' % nrelax)
266 1 tkerber
            os.system('cp cdn1 cdn1_%d' % nrelax)
267 1 tkerber
            if nrelax > self.maxrelax:
268 1 tkerber
                raise RuntimeError('Failed to relax in %d iterations' % self.maxrelax)
269 1 tkerber
            self.converged = False
270 1 tkerber
271 1 tkerber
272 1 tkerber
    def write_inp(self, atoms):
273 1 tkerber
        """Write the `inp` input file of FLEUR.
274 1 tkerber

275 1 tkerber
        First, the information from Atoms is written to the simple input
276 1 tkerber
        file and the actual input file `inp` is then generated with the
277 1 tkerber
        FLEUR input generator. The location of input generator is specified
278 1 tkerber
        in the environment variable FLEUR_INPGEN.
279 1 tkerber

280 1 tkerber
        Finally, the `inp` file is modified according to the arguments of
281 1 tkerber
        the FLEUR calculator object.
282 1 tkerber
        """
283 1 tkerber
284 1 tkerber
        fh = open('inp_simple', 'w')
285 1 tkerber
        fh.write('FLEUR input generated with ASE\n')
286 1 tkerber
        fh.write('\n')
287 1 tkerber
288 1 tkerber
        if atoms.pbc[2]:
289 1 tkerber
            film = 'f'
290 1 tkerber
        else:
291 1 tkerber
            film = 't'
292 1 tkerber
        fh.write('&input film=%s /' % film)
293 1 tkerber
        fh.write('\n')
294 1 tkerber
295 1 tkerber
        for vec in atoms.get_cell():
296 1 tkerber
            fh.write(' ')
297 1 tkerber
            for el in vec:
298 1 tkerber
                fh.write(' %21.16f' % (el/Bohr))
299 1 tkerber
            fh.write('\n')
300 1 tkerber
        fh.write(' %21.16f\n' % 1.0)
301 1 tkerber
        fh.write(' %21.16f %21.16f %21.16f\n' % (1.0, 1.0, 1.0))
302 1 tkerber
        fh.write('\n')
303 1 tkerber
304 1 tkerber
        natoms = len(atoms)
305 1 tkerber
        fh.write(' %6d\n' % natoms)
306 1 tkerber
        positions = atoms.get_scaled_positions()
307 1 tkerber
        if not atoms.pbc[2]:
308 1 tkerber
            # in film calculations z position has to be in absolute
309 1 tkerber
            # coordinates and symmetrical
310 1 tkerber
            cart_pos = atoms.get_positions()
311 1 tkerber
            cart_pos[:, 2] -= atoms.get_cell()[2, 2]/2.0
312 1 tkerber
            positions[:, 2] = cart_pos[:, 2] / Bohr
313 1 tkerber
        atomic_numbers = atoms.get_atomic_numbers()
314 1 tkerber
        for Z, pos in zip(atomic_numbers, positions):
315 1 tkerber
            fh.write('%3d' % Z)
316 1 tkerber
            for el in pos:
317 1 tkerber
                fh.write(' %21.16f' % el)
318 1 tkerber
            fh.write('\n')
319 1 tkerber
320 1 tkerber
        fh.close()
321 1 tkerber
        try:
322 1 tkerber
            inpgen = os.environ['FLEUR_INPGEN']
323 1 tkerber
        except KeyError:
324 1 tkerber
            raise RuntimeError('Please set FLEUR_INPGEN')
325 1 tkerber
326 1 tkerber
        # rename the previous inp if it exists
327 1 tkerber
        if os.path.isfile('inp'):
328 1 tkerber
            os.rename('inp', 'inp.bak')
329 1 tkerber
        os.system('%s < inp_simple' % inpgen)
330 1 tkerber
331 1 tkerber
        # read the whole inp-file for possible modifications
332 1 tkerber
        fh = open('inp', 'r')
333 1 tkerber
        lines = fh.readlines()
334 1 tkerber
        fh.close()
335 1 tkerber
336 1 tkerber
337 1 tkerber
        for ln, line in enumerate(lines):
338 1 tkerber
            # XC potential
339 1 tkerber
            if line.startswith('pbe'):
340 1 tkerber
                if self.xc == 'PBE':
341 1 tkerber
                    pass
342 1 tkerber
                elif self.xc == 'RPBE':
343 1 tkerber
                    lines[ln] = 'rpbe   non-relativi\n'
344 1 tkerber
                elif self.xc == 'LDA':
345 1 tkerber
                    lines[ln] = 'mjw    non-relativic\n'
346 1 tkerber
                    del lines[ln+1]
347 1 tkerber
                else:
348 1 tkerber
                    raise RuntimeError('XC-functional %s is not supported' % self.xc)
349 1 tkerber
            # kmax
350 1 tkerber
            if self.kmax and line.startswith('Window'):
351 1 tkerber
                line = '%10.5f\n' % self.kmax
352 1 tkerber
                lines[ln+2] = line
353 1 tkerber
354 1 tkerber
            # Fermi width
355 1 tkerber
            if self.width and line.startswith('gauss'):
356 1 tkerber
                line = 'gauss=F   %7.5ftria=F\n' % (self.width / Hartree)
357 1 tkerber
                lines[ln] = line
358 1 tkerber
            # kpts
359 1 tkerber
            if self.kpts and line.startswith('nkpt'):
360 1 tkerber
                line = 'nkpt=      nx=%2d,ny=%2d,nz=%2d\n' % (self.kpts[0],
361 1 tkerber
                                                              self.kpts[1],
362 1 tkerber
                                                              self.kpts[2])
363 1 tkerber
                lines[ln] = line
364 1 tkerber
            # Mixing
365 1 tkerber
            if self.mixer and line.startswith('itmax'):
366 1 tkerber
                imix = self.mixer['imix']
367 1 tkerber
                alpha = self.mixer['alpha']
368 1 tkerber
                spinf = self.mixer['spinf']
369 1 tkerber
                line_end = 'imix=%2d,alpha=%6.2f,spinf=%6.2f\n' % (imix,
370 1 tkerber
                                                                   alpha,
371 1 tkerber
                                                                   spinf)
372 1 tkerber
                line = line[:21] + line_end
373 1 tkerber
                lines[ln] = line
374 1 tkerber
375 1 tkerber
        # write everything back to inp
376 1 tkerber
        fh = open('inp', 'w')
377 1 tkerber
        for line in lines:
378 1 tkerber
            fh.write(line)
379 1 tkerber
        fh.close()
380 1 tkerber
381 1 tkerber
    def read(self):
382 1 tkerber
        """Read results from FLEUR's text-output file `out`."""
383 1 tkerber
384 1 tkerber
        lines = open('out', 'r').readlines()
385 1 tkerber
386 1 tkerber
        # total energies
387 1 tkerber
        self.total_energies = []
388 1 tkerber
        pat = re.compile('(.*total energy=)(\s)*([-0-9.]*)')
389 1 tkerber
        for line in lines:
390 1 tkerber
            m = pat.match(line)
391 1 tkerber
            if m:
392 1 tkerber
                self.total_energies.append(float(m.group(3)))
393 1 tkerber
        self.etotal = self.total_energies[-1]
394 1 tkerber
395 1 tkerber
        # free_energies
396 1 tkerber
        self.free_energies = []
397 1 tkerber
        pat = re.compile('(.*free energy=)(\s)*([-0-9.]*)')
398 1 tkerber
        for line in lines:
399 1 tkerber
            m = pat.match(line)
400 1 tkerber
            if m:
401 1 tkerber
                self.free_energies.append(float(m.group(3)))
402 1 tkerber
        self.efree = self.free_energies[-1]
403 1 tkerber
404 1 tkerber
        # TODO forces, charge density difference...
405 1 tkerber
406 1 tkerber
    def check_convergence(self):
407 1 tkerber
        """Check the convergence of calculation"""
408 1 tkerber
        energy_error = np.ptp(self.total_energies[-3:])
409 1 tkerber
        self.converged = energy_error < self.convergence['energy']
410 1 tkerber
411 1 tkerber
        # TODO check charge convergence
412 1 tkerber
413 1 tkerber
        # reduce the itmax in inp
414 1 tkerber
        lines = open('inp', 'r').readlines()
415 1 tkerber
        pat = re.compile('(itmax=)([ 0-9]*)')
416 1 tkerber
        fh = open('inp', 'w')
417 1 tkerber
        for line in lines:
418 1 tkerber
            m = pat.match(line)
419 1 tkerber
            if m:
420 1 tkerber
                itmax = int(m.group(2))
421 1 tkerber
                self.niter += itmax
422 1 tkerber
                itmax_new = itmax / 2
423 1 tkerber
                itmax = max(5, itmax_new)
424 1 tkerber
                line = 'itmax=%2d' % itmax + line[8:]
425 1 tkerber
            fh.write(line)
426 1 tkerber
        fh.close()