root / ase / calculators / fleur.py @ 1
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() |