Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import os
2from os.path import join
3import re
4from glob import glob
5import warnings
7import numpy as np
9from ase import Atoms
10from ase.data import chemical_symbols
11from ase.units import Hartree, Bohr, fs
12from ase.calculators.calculator import Parameters
15def read_abinit_in(fd):
16 """Import ABINIT input file.
18 Reads cell, atom positions, etc. from abinit input file
19 """
21 tokens = []
23 for line in fd:
24 meat = line.split('#', 1)[0]
25 tokens += meat.lower().split()
27 # note that the file can not be scanned sequentially
29 index = tokens.index("acell")
30 unit = 1.0
31 if(tokens[index + 4].lower()[:3] != 'ang'):
32 unit = Bohr
33 acell = [unit * float(tokens[index + 1]),
34 unit * float(tokens[index + 2]),
35 unit * float(tokens[index + 3])]
37 index = tokens.index("natom")
38 natom = int(tokens[index + 1])
40 index = tokens.index("ntypat")
41 ntypat = int(tokens[index + 1])
43 index = tokens.index("typat")
44 typat = []
45 while len(typat) < natom:
46 token = tokens[index + 1]
47 if '*' in token: # e.g. typat 4*1 3*2 ...
48 nrepeat, typenum = token.split('*')
49 typat += [int(typenum)] * int(nrepeat)
50 else:
51 typat.append(int(token))
52 index += 1
53 assert natom == len(typat)
55 index = tokens.index("znucl")
56 znucl = []
57 for i in range(ntypat):
58 znucl.append(int(tokens[index + 1 + i]))
60 index = tokens.index("rprim")
61 rprim = []
62 for i in range(3):
63 rprim.append([acell[i] * float(tokens[index + 3 * i + 1]),
64 acell[i] * float(tokens[index + 3 * i + 2]),
65 acell[i] * float(tokens[index + 3 * i + 3])])
67 # create a list with the atomic numbers
68 numbers = []
69 for i in range(natom):
70 ii = typat[i] - 1
71 numbers.append(znucl[ii])
73 # now the positions of the atoms
74 if "xred" in tokens:
75 index = tokens.index("xred")
76 xred = []
77 for i in range(natom):
78 xred.append([float(tokens[index + 3 * i + 1]),
79 float(tokens[index + 3 * i + 2]),
80 float(tokens[index + 3 * i + 3])])
81 atoms = Atoms(cell=rprim, scaled_positions=xred, numbers=numbers,
82 pbc=True)
83 else:
84 if "xcart" in tokens:
85 index = tokens.index("xcart")
86 unit = Bohr
87 elif "xangst" in tokens:
88 unit = 1.0
89 index = tokens.index("xangst")
90 else:
91 raise IOError(
92 "No xred, xcart, or xangs keyword in abinit input file")
94 xangs = []
95 for i in range(natom):
96 xangs.append([unit * float(tokens[index + 3 * i + 1]),
97 unit * float(tokens[index + 3 * i + 2]),
98 unit * float(tokens[index + 3 * i + 3])])
99 atoms = Atoms(cell=rprim, positions=xangs, numbers=numbers, pbc=True)
101 try:
102 ii = tokens.index('nsppol')
103 except ValueError:
104 nsppol = None
105 else:
106 nsppol = int(tokens[ii + 1])
108 if nsppol == 2:
109 index = tokens.index('spinat')
110 magmoms = [float(tokens[index + 3 * i + 3]) for i in range(natom)]
111 atoms.set_initial_magnetic_moments(magmoms)
113 assert len(atoms) == natom
114 return atoms
117keys_with_units = {
118 'toldfe': 'eV',
119 'tsmear': 'eV',
120 'paoenergyshift': 'eV',
121 'zmunitslength': 'Bohr',
122 'zmunitsangle': 'rad',
123 'zmforcetollength': 'eV/Ang',
124 'zmforcetolangle': 'eV/rad',
125 'zmmaxdispllength': 'Ang',
126 'zmmaxdisplangle': 'rad',
127 'ecut': 'eV',
128 'pawecutdg': 'eV',
129 'dmenergytolerance': 'eV',
130 'electronictemperature': 'eV',
131 'oneta': 'eV',
132 'onetaalpha': 'eV',
133 'onetabeta': 'eV',
134 'onrclwf': 'Ang',
135 'onchemicalpotentialrc': 'Ang',
136 'onchemicalpotentialtemperature': 'eV',
137 'mdmaxcgdispl': 'Ang',
138 'mdmaxforcetol': 'eV/Ang',
139 'mdmaxstresstol': 'eV/Ang**3',
140 'mdlengthtimestep': 'fs',
141 'mdinitialtemperature': 'eV',
142 'mdtargettemperature': 'eV',
143 'mdtargetpressure': 'eV/Ang**3',
144 'mdnosemass': 'eV*fs**2',
145 'mdparrinellorahmanmass': 'eV*fs**2',
146 'mdtaurelax': 'fs',
147 'mdbulkmodulus': 'eV/Ang**3',
148 'mdfcdispl': 'Ang',
149 'warningminimumatomicdistance': 'Ang',
150 'rcspatial': 'Ang',
151 'kgridcutoff': 'Ang',
152 'latticeconstant': 'Ang'}
155def write_abinit_in(fd, atoms, param=None, species=None, pseudos=None):
156 import copy
157 from ase.calculators.calculator import kpts2mp
158 from ase.calculators.abinit import Abinit
160 if param is None:
161 param = {}
163 _param = copy.deepcopy(Abinit.default_parameters)
164 _param.update(param)
165 param = _param
167 if species is None:
168 species = sorted(set(atoms.numbers))
170 inp = {}
171 inp.update(param)
172 for key in ['xc', 'smearing', 'kpts', 'pps', 'raw']:
173 del inp[key]
175 smearing = param.get('smearing')
176 if 'tsmear' in param or 'occopt' in param:
177 assert smearing is None
179 if smearing is not None:
180 inp['occopt'] = {'fermi-dirac': 3,
181 'gaussian': 7}[smearing[0].lower()]
182 inp['tsmear'] = smearing[1]
184 inp['natom'] = len(atoms)
186 if 'nbands' in param:
187 inp['nband'] = param['nbands']
188 del inp['nbands']
190 # ixc is set from paw/xml file. Ignore 'xc' setting then.
191 if param.get('pps') not in ['pawxml']:
192 if 'ixc' not in param:
193 inp['ixc'] = {'LDA': 7,
194 'PBE': 11,
195 'revPBE': 14,
196 'RPBE': 15,
197 'WC': 23}[param['xc']]
199 magmoms = atoms.get_initial_magnetic_moments()
200 if magmoms.any():
201 inp['nsppol'] = 2
202 fd.write('spinat\n')
203 for n, M in enumerate(magmoms):
204 fd.write('%.14f %.14f %.14f\n' % (0, 0, M))
205 else:
206 inp['nsppol'] = 1
208 if param['kpts'] is not None:
209 mp = kpts2mp(atoms, param['kpts'])
210 fd.write('kptopt 1\n')
211 fd.write('ngkpt %d %d %d\n' % tuple(mp))
212 fd.write('nshiftk 1\n')
213 fd.write('shiftk\n')
214 fd.write('%.1f %.1f %.1f\n' % tuple((np.array(mp) + 1) % 2 * 0.5))
216 valid_lists = (list, np.ndarray)
217 for key in sorted(inp):
218 value = inp[key]
219 unit = keys_with_units.get(key)
220 if unit is not None:
221 if 'fs**2' in unit:
222 value /= fs**2
223 elif 'fs' in unit:
224 value /= fs
225 if isinstance(value, valid_lists):
226 if isinstance(value[0], valid_lists):
227 fd.write("{}\n".format(key))
228 for dim in value:
229 write_list(fd, dim, unit)
230 else:
231 fd.write("{}\n".format(key))
232 write_list(fd, value, unit)
233 else:
234 if unit is None:
235 fd.write("{} {}\n".format(key, value))
236 else:
237 fd.write("{} {} {}\n".format(key, value, unit))
239 if param['raw'] is not None:
240 if isinstance(param['raw'], str):
241 raise TypeError('The raw parameter is a single string; expected '
242 'a sequence of lines')
243 for line in param['raw']:
244 if isinstance(line, tuple):
245 fd.write(' '.join(['%s' % x for x in line]) + '\n')
246 else:
247 fd.write('%s\n' % line)
249 fd.write('#Definition of the unit cell\n')
250 fd.write('acell\n')
251 fd.write('%.14f %.14f %.14f Angstrom\n' % (1.0, 1.0, 1.0))
252 fd.write('rprim\n')
253 if atoms.cell.rank != 3:
254 raise RuntimeError('Abinit requires a 3D cell, but cell is {}'
255 .format(atoms.cell))
256 for v in atoms.cell:
257 fd.write('%.14f %.14f %.14f\n' % tuple(v))
259 fd.write('chkprim 0 # Allow non-primitive cells\n')
261 fd.write('#Definition of the atom types\n')
262 fd.write('ntypat %d\n' % (len(species)))
263 fd.write('znucl {}\n'.format(' '.join(str(Z) for Z in species)))
264 fd.write('#Enumerate different atomic species\n')
265 fd.write('typat')
266 fd.write('\n')
268 types = []
269 for Z in atoms.numbers:
270 for n, Zs in enumerate(species):
271 if Z == Zs:
272 types.append(n + 1)
273 n_entries_int = 20 # integer entries per line
274 for n, type in enumerate(types):
275 fd.write(' %d' % (type))
276 if n > 1 and ((n % n_entries_int) == 1):
277 fd.write('\n')
278 fd.write('\n')
280 if pseudos is not None:
281 listing = ',\n'.join(pseudos)
282 line = f'pseudos "{listing}"\n'
283 fd.write(line)
285 fd.write('#Definition of the atoms\n')
286 fd.write('xcart\n')
287 for pos in atoms.positions / Bohr:
288 fd.write('%.14f %.14f %.14f\n' % tuple(pos))
290 fd.write('chkexit 1 # abinit.exit file in the running '
291 'directory terminates after the current SCF\n')
294def write_list(fd, value, unit):
295 for element in value:
296 fd.write("{} ".format(element))
297 if unit is not None:
298 fd.write("{}".format(unit))
299 fd.write("\n")
302def read_stress(fd):
303 # sigma(1 1)= 4.02063464E-04 sigma(3 2)= 0.00000000E+00
304 # sigma(2 2)= 4.02063464E-04 sigma(3 1)= 0.00000000E+00
305 # sigma(3 3)= 4.02063464E-04 sigma(2 1)= 0.00000000E+00
306 pat = re.compile(r'\s*sigma\(\d \d\)=\s*(\S+)\s*sigma\(\d \d\)=\s*(\S+)')
307 stress = np.empty(6)
308 for i in range(3):
309 line = next(fd)
310 m = pat.match(line)
311 if m is None:
312 # Not a real value error. What should we raise?
313 raise ValueError('Line {!r} does not match stress pattern {!r}'
314 .format(line, pat))
315 s1, s2 = m.group(1, 2)
316 stress[i] = float(m.group(1))
317 stress[i + 3] = float(m.group(2))
318 unit = Hartree / Bohr**3
319 return stress * unit
322def consume_multiline(fd, headerline, nvalues, dtype):
323 """Parse abinit-formatted "header + values" sections.
325 Example:
327 typat 1 1 1 1 1
328 1 1 1 1
329 """
330 tokens = headerline.split()
331 assert tokens[0].isalpha()
333 values = tokens[1:]
334 while len(values) < nvalues:
335 line = next(fd)
336 values.extend(line.split())
337 assert len(values) == nvalues
338 return np.array(values).astype(dtype)
341def read_abinit_out(fd):
342 results = {}
344 def skipto(string):
345 for line in fd:
346 if string in line:
347 return line
348 raise RuntimeError('Not found: {}'.format(string))
350 line = skipto('Version')
351 m = re.match(r'\.*?Version\s+(\S+)\s+of ABINIT', line)
352 assert m is not None
353 version = m.group(1)
354 results['version'] = version
356 use_v9_format = int(version.split('.', 1)[0]) >= 9
358 shape_vars = {}
360 skipto('echo values of preprocessed input variables')
362 for line in fd:
363 if '===============' in line:
364 break
366 tokens = line.split()
367 if not tokens:
368 continue
370 for key in ['natom', 'nkpt', 'nband', 'ntypat']:
371 if tokens[0] == key:
372 shape_vars[key] = int(tokens[1])
374 if line.lstrip().startswith('typat'): # Avoid matching ntypat
375 types = consume_multiline(fd, line, shape_vars['natom'], int)
377 if 'znucl' in line:
378 znucl = consume_multiline(fd, line, shape_vars['ntypat'], float)
380 if 'rprim' in line:
381 cell = consume_multiline(fd, line, 9, float)
382 cell = cell.reshape(3, 3)
384 natoms = shape_vars['natom']
386 # Skip ahead to results:
387 for line in fd:
388 if 'was not enough scf cycles to converge' in line:
389 raise RuntimeError(line)
390 if 'iterations are completed or convergence reached' in line:
391 break
392 else:
393 raise RuntimeError('Cannot find results section')
395 def read_array(fd, nlines):
396 arr = []
397 for i in range(nlines):
398 line = next(fd)
399 arr.append(line.split()[1:])
400 arr = np.array(arr).astype(float)
401 return arr
403 if use_v9_format:
404 energy_header = '--- !EnergyTerms'
405 total_energy_name = 'total_energy_eV'
407 def parse_energy(line):
408 return float(line.split(':')[1].strip())
409 else:
410 energy_header = 'Components of total free energy (in Hartree) :'
411 total_energy_name = '>>>>>>>>> Etotal'
413 def parse_energy(line):
414 return float(line.rsplit('=', 2)[1]) * Hartree
416 for line in fd:
417 if 'cartesian coordinates (angstrom) at end' in line:
418 positions = read_array(fd, natoms)
419 if 'cartesian forces (eV/Angstrom) at end' in line:
420 results['forces'] = read_array(fd, natoms)
421 if 'Cartesian components of stress tensor (hartree/bohr^3)' in line:
422 results['stress'] = read_stress(fd)
424 if line.strip() == energy_header:
425 # Header not to be confused with EnergyTermsDC,
426 # therefore we don't use .startswith()
427 energy = None
428 for line in fd:
429 # Which of the listed energies should we include?
430 if total_energy_name in line:
431 energy = parse_energy(line)
432 break
433 if energy is None:
434 raise RuntimeError('No energy found in output')
435 results['energy'] = results['free_energy'] = energy
437 if 'END DATASET(S)' in line:
438 break
440 znucl_int = znucl.astype(int)
441 znucl_int[znucl_int != znucl] = 0 # (Fractional Z)
442 numbers = znucl_int[types - 1]
444 atoms = Atoms(numbers=numbers,
445 positions=positions,
446 cell=cell,
447 pbc=True)
449 results['atoms'] = atoms
450 return results
453def match_kpt_header(line):
454 headerpattern = (r'\s*kpt#\s*\S+\s*'
455 r'nband=\s*(\d+),\s*'
456 r'wtk=\s*(\S+?),\s*'
457 r'kpt=\s*(\S+)+\s*(\S+)\s*(\S+)')
458 m = re.match(headerpattern, line)
459 assert m is not None, line
460 nbands = int(m.group(1))
461 weight = float(m.group(2))
462 kvector = np.array(m.group(3, 4, 5)).astype(float)
463 return nbands, weight, kvector
466def read_eigenvalues_for_one_spin(fd, nkpts):
468 kpoint_weights = []
469 kpoint_coords = []
471 eig_kn = []
472 for ikpt in range(nkpts):
473 header = next(fd)
474 nbands, weight, kvector = match_kpt_header(header)
475 kpoint_coords.append(kvector)
476 kpoint_weights.append(weight)
478 eig_n = []
479 while len(eig_n) < nbands:
480 line = next(fd)
481 tokens = line.split()
482 values = np.array(tokens).astype(float) * Hartree
483 eig_n.extend(values)
484 assert len(eig_n) == nbands
485 eig_kn.append(eig_n)
486 assert nbands == len(eig_kn[0])
488 kpoint_weights = np.array(kpoint_weights)
489 kpoint_coords = np.array(kpoint_coords)
490 eig_kn = np.array(eig_kn)
491 return kpoint_coords, kpoint_weights, eig_kn
494def read_eig(fd):
495 line = next(fd)
496 results = {}
497 m = re.match(r'\s*Fermi \(or HOMO\) energy \(hartree\)\s*=\s*(\S+)', line)
498 if m is not None:
499 results['fermilevel'] = float(m.group(1)) * Hartree
500 line = next(fd)
502 nspins = 1
504 m = re.match(r'\s*Magnetization \(Bohr magneton\)=\s*(\S+)', line)
505 if m is not None:
506 nspins = 2
507 magmom = float(m.group(1))
508 results['magmom'] = magmom
509 line = next(fd)
511 if 'Total spin up' in line:
512 assert nspins == 2
513 line = next(fd)
515 m = re.match(r'\s*Eigenvalues \(hartree\) for nkpt\s*='
516 r'\s*(\S+)\s*k\s*points', line)
517 if 'SPIN' in line or 'spin' in line:
518 # If using spinpol with fixed magmoms, we don't get the magmoms
519 # listed before now.
520 nspins = 2
521 assert m is not None
522 nkpts = int(m.group(1))
524 eig_skn = []
526 kpts, weights, eig_kn = read_eigenvalues_for_one_spin(fd, nkpts)
527 nbands = eig_kn.shape[1]
529 eig_skn.append(eig_kn)
530 if nspins == 2:
531 line = next(fd)
532 assert 'SPIN DOWN' in line
533 _, _, eig_kn = read_eigenvalues_for_one_spin(fd, nkpts)
534 assert eig_kn.shape == (nkpts, nbands)
535 eig_skn.append(eig_kn)
536 eig_skn = np.array(eig_skn)
538 eigshape = (nspins, nkpts, nbands)
539 assert eig_skn.shape == eigshape, (eig_skn.shape, eigshape)
541 results['ibz_kpoints'] = kpts
542 results['kpoint_weights'] = weights
543 results['eigenvalues'] = eig_skn
544 return results
547def write_files_file(fd, label, ppp_list):
548 """Write files-file, the file which tells abinit about other files."""
549 prefix = label.rsplit('/', 1)[-1]
550 fd.write('%s\n' % (prefix + '.in')) # input
551 fd.write('%s\n' % (prefix + '.txt')) # output
552 fd.write('%s\n' % (prefix + 'i')) # input
553 fd.write('%s\n' % (prefix + 'o')) # output
554 fd.write('%s\n' % (prefix + '.abinit'))
555 # Provide the psp files
556 for ppp in ppp_list:
557 fd.write('%s\n' % (ppp)) # psp file path
560def get_default_abinit_pp_paths():
561 return os.environ.get('ABINIT_PP_PATH', '.').split(':')
564abinit_input_version_warning = """\
565Abinit input format has changed in Abinit9.
567ASE will currently write inputs for Abinit8 by default. Please
568silence this warning passing either Abinit(v8_legacy_format=True) to
569write the old Abinit8 format, or False for writing
570the new Abinit9+ format.
572The default will change to Abinit9+ format from ase-3.22, and this
573warning will be removed.
575Please note that stdin to Abinit should be the .files file until version 8
576but the main inputfile (conventionally abinit.in) from abinit9,
577which may require reconfiguring the ASE/Abinit shell command.
578"""
581def write_all_inputs(atoms, properties, parameters,
582 pp_paths=None,
583 raise_exception=True,
584 label='abinit',
585 *, v8_legacy_format=True):
586 species = sorted(set(atoms.numbers))
587 if pp_paths is None:
588 pp_paths = get_default_abinit_pp_paths()
589 ppp = get_ppp_list(atoms, species,
590 raise_exception=raise_exception,
591 xc=parameters.xc,
592 pps=parameters.pps,
593 search_paths=pp_paths)
595 if v8_legacy_format is None:
596 warnings.warn(abinit_input_version_warning,
597 FutureWarning)
598 v8_legacy_format = True
600 if v8_legacy_format:
601 with open(label + '.files', 'w') as fd:
602 write_files_file(fd, label, ppp)
603 pseudos = None
605 # XXX here we build the txt filename again, which is bad
606 # (also defined in the calculator)
607 output_filename = label + '.txt'
608 else:
609 pseudos = ppp # Include pseudopotentials in inputfile
610 output_filename = label + '.abo'
612 # Abinit will write to label.txtA if label.txt already exists,
613 # so we remove it if it's there:
614 if os.path.isfile(output_filename):
615 os.remove(output_filename)
617 parameters.write(label + '.ase')
619 with open(label + '.in', 'w') as fd:
620 write_abinit_in(fd, atoms, param=parameters, species=species,
621 pseudos=pseudos)
624def read_ase_and_abinit_inputs(label):
625 with open(label + '.in') as fd:
626 atoms = read_abinit_in(fd)
627 parameters = Parameters.read(label + '.ase')
628 return atoms, parameters
631def read_results(label, textfilename):
632 # filename = label + '.txt'
633 results = {}
634 with open(textfilename) as fd:
635 dct = read_abinit_out(fd)
636 results.update(dct)
637 # The eigenvalues section in the main file is shortened to
638 # a limited number of kpoints. We read the complete one from
639 # the EIG file then:
640 with open('{}o_EIG'.format(label)) as fd:
641 dct = read_eig(fd)
642 results.update(dct)
643 return results
646def get_ppp_list(atoms, species, raise_exception, xc, pps,
647 search_paths):
648 ppp_list = []
650 xcname = 'GGA' if xc != 'LDA' else 'LDA'
651 for Z in species:
652 number = abs(Z)
653 symbol = chemical_symbols[number]
655 names = []
656 for s in [symbol, symbol.lower()]:
657 for xcn in [xcname, xcname.lower()]:
658 if pps in ['paw']:
659 hghtemplate = '%s-%s-%s.paw' # E.g. "H-GGA-hard-uspp.paw"
660 names.append(hghtemplate % (s, xcn, '*'))
661 names.append('%s[.-_]*.paw' % s)
662 elif pps in ['pawxml']:
663 hghtemplate = '%s.%s%s.xml' # E.g. "H.GGA_PBE-JTH.xml"
664 names.append(hghtemplate % (s, xcn, '*'))
665 names.append('%s[.-_]*.xml' % s)
666 elif pps in ['hgh.k']:
667 hghtemplate = '%s-q%s.hgh.k' # E.g. "Co-q17.hgh.k"
668 names.append(hghtemplate % (s, '*'))
669 names.append('%s[.-_]*.hgh.k' % s)
670 names.append('%s[.-_]*.hgh' % s)
671 elif pps in ['tm']:
672 hghtemplate = '%d%s%s.pspnc' # E.g. "44ru.pspnc"
673 names.append(hghtemplate % (number, s, '*'))
674 names.append('%s[.-_]*.pspnc' % s)
675 elif pps in ['hgh', 'hgh.sc']:
676 hghtemplate = '%d%s.%s.hgh' # E.g. "42mo.6.hgh"
677 # There might be multiple files with different valence
678 # electron counts, so we must choose between
679 # the ordinary and the semicore versions for some elements.
680 #
681 # Therefore we first use glob to get all relevant files,
682 # then pick the correct one afterwards.
683 names.append(hghtemplate % (number, s, '*'))
684 names.append('%d%s%s.hgh' % (number, s, '*'))
685 names.append('%s[.-_]*.hgh' % s)
686 else: # default extension
687 names.append('%02d-%s.%s.%s' % (number, s, xcn, pps))
688 names.append('%02d[.-_]%s*.%s' % (number, s, pps))
689 names.append('%02d%s*.%s' % (number, s, pps))
690 names.append('%s[.-_]*.%s' % (s, pps))
692 found = False
693 for name in names: # search for file names possibilities
694 for path in search_paths: # in all available directories
695 filenames = glob(join(path, name))
696 if not filenames:
697 continue
698 if pps == 'paw':
699 # warning: see download.sh in
700 # abinit-pseudopotentials*tar.gz for additional
701 # information!
702 #
703 # XXXX This is probably buggy, max(filenames) uses
704 # an lexicographic order so 14 < 8, and it's
705 # untested so if I change it I'm sure things will
706 # just be inconsistent. --askhl
707 filenames[0] = max(filenames) # Semicore or hard
708 elif pps == 'hgh':
709 # Lowest valence electron count
710 filenames[0] = min(filenames)
711 elif pps == 'hgh.k':
712 # Semicore - highest electron count
713 filenames[0] = max(filenames)
714 elif pps == 'tm':
715 # Semicore - highest electron count
716 filenames[0] = max(filenames)
717 elif pps == 'hgh.sc':
718 # Semicore - highest electron count
719 filenames[0] = max(filenames)
721 if filenames:
722 found = True
723 ppp_list.append(filenames[0])
724 break
725 if found:
726 break
728 if not found:
729 ppp_list.append("Provide {}.{}.{}?".format(symbol, '*', pps))
730 if raise_exception:
731 msg = ('Could not find {} pseudopotential {} for {}'
732 .format(xcname.lower(), pps, symbol))
733 raise RuntimeError(msg)
735 return ppp_list