Hide keyboard shortcuts

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

1"""Reads Quantum ESPRESSO files. 

2 

3Read multiple structures and results from pw.x output files. Read 

4structures from pw.x input files. 

5 

6Built for PWSCF v.5.3.0 but should work with earlier and later versions. 

7Can deal with most major functionality, with the notable exception of ibrav, 

8for which we only support ibrav == 0 and force CELL_PARAMETERS to be provided 

9explicitly. 

10 

11Units are converted using CODATA 2006, as used internally by Quantum 

12ESPRESSO. 

13""" 

14 

15import os 

16import operator as op 

17import re 

18import warnings 

19from collections import OrderedDict 

20from os import path 

21 

22import numpy as np 

23 

24from ase.atoms import Atoms 

25from ase.cell import Cell 

26from ase.calculators.singlepoint import (SinglePointDFTCalculator, 

27 SinglePointKPoint) 

28from ase.calculators.calculator import kpts2ndarray, kpts2sizeandoffsets 

29from ase.dft.kpoints import kpoint_convert 

30from ase.constraints import FixAtoms, FixCartesian 

31from ase.data import chemical_symbols, atomic_numbers 

32from ase.units import create_units 

33from ase.utils import iofunction 

34 

35 

36# Quantum ESPRESSO uses CODATA 2006 internally 

37units = create_units('2006') 

38 

39# Section identifiers 

40_PW_START = 'Program PWSCF' 

41_PW_END = 'End of self-consistent calculation' 

42_PW_CELL = 'CELL_PARAMETERS' 

43_PW_POS = 'ATOMIC_POSITIONS' 

44_PW_MAGMOM = 'Magnetic moment per site' 

45_PW_FORCE = 'Forces acting on atoms' 

46_PW_TOTEN = '! total energy' 

47_PW_STRESS = 'total stress' 

48_PW_FERMI = 'the Fermi energy is' 

49_PW_HIGHEST_OCCUPIED = 'highest occupied level' 

50_PW_HIGHEST_OCCUPIED_LOWEST_FREE = 'highest occupied, lowest unoccupied level' 

51_PW_KPTS = 'number of k points=' 

52_PW_BANDS = _PW_END 

53_PW_BANDSTRUCTURE = 'End of band structure calculation' 

54 

55# ibrav error message 

56ibrav_error_message = ( 

57 'ASE does not support ibrav != 0. Note that with ibrav ' 

58 '== 0, Quantum ESPRESSO will still detect the symmetries ' 

59 'of your system because the CELL_PARAMETERS are defined ' 

60 'to a high level of precision.') 

61 

62 

63class Namelist(OrderedDict): 

64 """Case insensitive dict that emulates Fortran Namelists.""" 

65 

66 def __contains__(self, key): 

67 return super(Namelist, self).__contains__(key.lower()) 

68 

69 def __delitem__(self, key): 

70 return super(Namelist, self).__delitem__(key.lower()) 

71 

72 def __getitem__(self, key): 

73 return super(Namelist, self).__getitem__(key.lower()) 

74 

75 def __setitem__(self, key, value): 

76 super(Namelist, self).__setitem__(key.lower(), value) 

77 

78 def get(self, key, default=None): 

79 return super(Namelist, self).get(key.lower(), default) 

80 

81 

82@iofunction('rU') 

83def read_espresso_out(fileobj, index=-1, results_required=True): 

84 """Reads Quantum ESPRESSO output files. 

85 

86 The atomistic configurations as well as results (energy, force, stress, 

87 magnetic moments) of the calculation are read for all configurations 

88 within the output file. 

89 

90 Will probably raise errors for broken or incomplete files. 

91 

92 Parameters 

93 ---------- 

94 fileobj : file|str 

95 A file like object or filename 

96 index : slice 

97 The index of configurations to extract. 

98 results_required : bool 

99 If True, atomistic configurations that do not have any 

100 associated results will not be included. This prevents double 

101 printed configurations and incomplete calculations from being 

102 returned as the final configuration with no results data. 

103 

104 Yields 

105 ------ 

106 structure : Atoms 

107 The next structure from the index slice. The Atoms has a 

108 SinglePointCalculator attached with any results parsed from 

109 the file. 

110 

111 

112 """ 

113 # work with a copy in memory for faster random access 

114 pwo_lines = fileobj.readlines() 

115 

116 # TODO: index -1 special case? 

117 # Index all the interesting points 

118 indexes = { 

119 _PW_START: [], 

120 _PW_END: [], 

121 _PW_CELL: [], 

122 _PW_POS: [], 

123 _PW_MAGMOM: [], 

124 _PW_FORCE: [], 

125 _PW_TOTEN: [], 

126 _PW_STRESS: [], 

127 _PW_FERMI: [], 

128 _PW_HIGHEST_OCCUPIED: [], 

129 _PW_HIGHEST_OCCUPIED_LOWEST_FREE: [], 

130 _PW_KPTS: [], 

131 _PW_BANDS: [], 

132 _PW_BANDSTRUCTURE: [], 

133 } 

134 

135 for idx, line in enumerate(pwo_lines): 

136 for identifier in indexes: 

137 if identifier in line: 

138 indexes[identifier].append(idx) 

139 

140 # Configurations are either at the start, or defined in ATOMIC_POSITIONS 

141 # in a subsequent step. Can deal with concatenated output files. 

142 all_config_indexes = sorted(indexes[_PW_START] + 

143 indexes[_PW_POS]) 

144 

145 # Slice only requested indexes 

146 # setting results_required argument stops configuration-only 

147 # structures from being returned. This ensures the [-1] structure 

148 # is one that has results. Two cases: 

149 # - SCF of last configuration is not converged, job terminated 

150 # abnormally. 

151 # - 'relax' and 'vc-relax' re-prints the final configuration but 

152 # only 'vc-relax' recalculates. 

153 if results_required: 

154 results_indexes = sorted(indexes[_PW_TOTEN] + indexes[_PW_FORCE] + 

155 indexes[_PW_STRESS] + indexes[_PW_MAGMOM] + 

156 indexes[_PW_BANDS] + 

157 indexes[_PW_BANDSTRUCTURE]) 

158 

159 # Prune to only configurations with results data before the next 

160 # configuration 

161 results_config_indexes = [] 

162 for config_index, config_index_next in zip( 

163 all_config_indexes, 

164 all_config_indexes[1:] + [len(pwo_lines)]): 

165 if any([config_index < results_index < config_index_next 

166 for results_index in results_indexes]): 

167 results_config_indexes.append(config_index) 

168 

169 # slice from the subset 

170 image_indexes = results_config_indexes[index] 

171 else: 

172 image_indexes = all_config_indexes[index] 

173 

174 # Extract initialisation information each time PWSCF starts 

175 # to add to subsequent configurations. Use None so slices know 

176 # when to fill in the blanks. 

177 pwscf_start_info = dict((idx, None) for idx in indexes[_PW_START]) 

178 

179 for image_index in image_indexes: 

180 # Find the nearest calculation start to parse info. Needed in, 

181 # for example, relaxation where cell is only printed at the 

182 # start. 

183 if image_index in indexes[_PW_START]: 

184 prev_start_index = image_index 

185 else: 

186 # The greatest start index before this structure 

187 prev_start_index = [idx for idx in indexes[_PW_START] 

188 if idx < image_index][-1] 

189 

190 # add structure to reference if not there 

191 if pwscf_start_info[prev_start_index] is None: 

192 pwscf_start_info[prev_start_index] = parse_pwo_start( 

193 pwo_lines, prev_start_index) 

194 

195 # Get the bounds for information for this structure. Any associated 

196 # values will be between the image_index and the following one, 

197 # EXCEPT for cell, which will be 4 lines before if it exists. 

198 for next_index in all_config_indexes: 

199 if next_index > image_index: 

200 break 

201 else: 

202 # right to the end of the file 

203 next_index = len(pwo_lines) 

204 

205 # Get the structure 

206 # Use this for any missing data 

207 prev_structure = pwscf_start_info[prev_start_index]['atoms'] 

208 if image_index in indexes[_PW_START]: 

209 structure = prev_structure.copy() # parsed from start info 

210 else: 

211 if _PW_CELL in pwo_lines[image_index - 5]: 

212 # CELL_PARAMETERS would be just before positions if present 

213 cell, cell_alat = get_cell_parameters( 

214 pwo_lines[image_index - 5:image_index]) 

215 else: 

216 cell = prev_structure.cell 

217 cell_alat = pwscf_start_info[prev_start_index]['alat'] 

218 

219 # give at least enough lines to parse the positions 

220 # should be same format as input card 

221 n_atoms = len(prev_structure) 

222 positions_card = get_atomic_positions( 

223 pwo_lines[image_index:image_index + n_atoms + 1], 

224 n_atoms=n_atoms, cell=cell, alat=cell_alat) 

225 

226 # convert to Atoms object 

227 symbols = [label_to_symbol(position[0]) for position in 

228 positions_card] 

229 positions = [position[1] for position in positions_card] 

230 structure = Atoms(symbols=symbols, positions=positions, cell=cell, 

231 pbc=True) 

232 

233 # Extract calculation results 

234 # Energy 

235 energy = None 

236 for energy_index in indexes[_PW_TOTEN]: 

237 if image_index < energy_index < next_index: 

238 energy = float( 

239 pwo_lines[energy_index].split()[-2]) * units['Ry'] 

240 

241 # Forces 

242 forces = None 

243 for force_index in indexes[_PW_FORCE]: 

244 if image_index < force_index < next_index: 

245 # Before QE 5.3 'negative rho' added 2 lines before forces 

246 # Use exact lines to stop before 'non-local' forces 

247 # in high verbosity 

248 if not pwo_lines[force_index + 2].strip(): 

249 force_index += 4 

250 else: 

251 force_index += 2 

252 # assume contiguous 

253 forces = [ 

254 [float(x) for x in force_line.split()[-3:]] for force_line 

255 in pwo_lines[force_index:force_index + len(structure)]] 

256 forces = np.array(forces) * units['Ry'] / units['Bohr'] 

257 

258 # Stress 

259 stress = None 

260 for stress_index in indexes[_PW_STRESS]: 

261 if image_index < stress_index < next_index: 

262 sxx, sxy, sxz = pwo_lines[stress_index + 1].split()[:3] 

263 _, syy, syz = pwo_lines[stress_index + 2].split()[:3] 

264 _, _, szz = pwo_lines[stress_index + 3].split()[:3] 

265 stress = np.array([sxx, syy, szz, syz, sxz, sxy], dtype=float) 

266 # sign convention is opposite of ase 

267 stress *= -1 * units['Ry'] / (units['Bohr'] ** 3) 

268 

269 # Magmoms 

270 magmoms = None 

271 for magmoms_index in indexes[_PW_MAGMOM]: 

272 if image_index < magmoms_index < next_index: 

273 magmoms = [ 

274 float(mag_line.split()[-1]) for mag_line 

275 in pwo_lines[magmoms_index + 1: 

276 magmoms_index + 1 + len(structure)]] 

277 

278 # Fermi level / highest occupied level 

279 efermi = None 

280 for fermi_index in indexes[_PW_FERMI]: 

281 if image_index < fermi_index < next_index: 

282 efermi = float(pwo_lines[fermi_index].split()[-2]) 

283 

284 if efermi is None: 

285 for ho_index in indexes[_PW_HIGHEST_OCCUPIED]: 

286 if image_index < ho_index < next_index: 

287 efermi = float(pwo_lines[ho_index].split()[-1]) 

288 

289 if efermi is None: 

290 for holf_index in indexes[_PW_HIGHEST_OCCUPIED_LOWEST_FREE]: 

291 if image_index < holf_index < next_index: 

292 efermi = float(pwo_lines[holf_index].split()[-2]) 

293 

294 # K-points 

295 ibzkpts = None 

296 weights = None 

297 kpoints_warning = "Number of k-points >= 100: " + \ 

298 "set verbosity='high' to print them." 

299 

300 for kpts_index in indexes[_PW_KPTS]: 

301 nkpts = int(pwo_lines[kpts_index].split()[4]) 

302 kpts_index += 2 

303 

304 if pwo_lines[kpts_index].strip() == kpoints_warning: 

305 continue 

306 

307 # QE prints the k-points in units of 2*pi/alat 

308 # with alat defined as the length of the first 

309 # cell vector 

310 cell = structure.get_cell() 

311 alat = np.linalg.norm(cell[0]) 

312 ibzkpts = [] 

313 weights = [] 

314 for i in range(nkpts): 

315 L = pwo_lines[kpts_index + i].split() 

316 weights.append(float(L[-1])) 

317 coord = np.array([L[-6], L[-5], L[-4].strip('),')], 

318 dtype=float) 

319 coord *= 2 * np.pi / alat 

320 coord = kpoint_convert(cell, ckpts_kv=coord) 

321 ibzkpts.append(coord) 

322 ibzkpts = np.array(ibzkpts) 

323 weights = np.array(weights) 

324 

325 # Bands 

326 kpts = None 

327 kpoints_warning = "Number of k-points >= 100: " + \ 

328 "set verbosity='high' to print the bands." 

329 

330 for bands_index in indexes[_PW_BANDS] + indexes[_PW_BANDSTRUCTURE]: 

331 if image_index < bands_index < next_index: 

332 bands_index += 1 

333 # skip over the lines with DFT+U occupation matrices 

334 if 'enter write_ns' in pwo_lines[bands_index]: 

335 while 'exit write_ns' not in pwo_lines[bands_index]: 

336 bands_index += 1 

337 bands_index += 1 

338 

339 if pwo_lines[bands_index].strip() == kpoints_warning: 

340 continue 

341 

342 assert ibzkpts is not None 

343 spin, bands, eigenvalues = 0, [], [[], []] 

344 

345 while True: 

346 L = pwo_lines[bands_index].replace('-', ' -').split() 

347 if len(L) == 0: 

348 if len(bands) > 0: 

349 eigenvalues[spin].append(bands) 

350 bands = [] 

351 elif L == ['occupation', 'numbers']: 

352 # Skip the lines with the occupation numbers 

353 bands_index += len(eigenvalues[spin][0]) // 8 + 1 

354 elif L[0] == 'k' and L[1].startswith('='): 

355 pass 

356 elif 'SPIN' in L: 

357 if 'DOWN' in L: 

358 spin += 1 

359 else: 

360 try: 

361 bands.extend(map(float, L)) 

362 except ValueError: 

363 break 

364 bands_index += 1 

365 

366 if spin == 1: 

367 assert len(eigenvalues[0]) == len(eigenvalues[1]) 

368 assert len(eigenvalues[0]) == len(ibzkpts), \ 

369 (np.shape(eigenvalues), len(ibzkpts)) 

370 

371 kpts = [] 

372 for s in range(spin + 1): 

373 for w, k, e in zip(weights, ibzkpts, eigenvalues[s]): 

374 kpt = SinglePointKPoint(w, s, k, eps_n=e) 

375 kpts.append(kpt) 

376 

377 # Put everything together 

378 # 

379 # I have added free_energy. Can and should we distinguish 

380 # energy and free_energy? --askhl 

381 calc = SinglePointDFTCalculator(structure, energy=energy, 

382 free_energy=energy, 

383 forces=forces, stress=stress, 

384 magmoms=magmoms, efermi=efermi, 

385 ibzkpts=ibzkpts) 

386 calc.kpts = kpts 

387 structure.calc = calc 

388 

389 yield structure 

390 

391 

392def parse_pwo_start(lines, index=0): 

393 """Parse Quantum ESPRESSO calculation info from lines, 

394 starting from index. Return a dictionary containing extracted 

395 information. 

396 

397 - `celldm(1)`: lattice parameters (alat) 

398 - `cell`: unit cell in Angstrom 

399 - `symbols`: element symbols for the structure 

400 - `positions`: cartesian coordinates of atoms in Angstrom 

401 - `atoms`: an `ase.Atoms` object constructed from the extracted data 

402 

403 Parameters 

404 ---------- 

405 lines : list[str] 

406 Contents of PWSCF output file. 

407 index : int 

408 Line number to begin parsing. Only first calculation will 

409 be read. 

410 

411 Returns 

412 ------- 

413 info : dict 

414 Dictionary of calculation parameters, including `celldm(1)`, `cell`, 

415 `symbols`, `positions`, `atoms`. 

416 

417 Raises 

418 ------ 

419 KeyError 

420 If interdependent values cannot be found (especially celldm(1)) 

421 an error will be raised as other quantities cannot then be 

422 calculated (e.g. cell and positions). 

423 """ 

424 # TODO: extend with extra DFT info? 

425 

426 info = {} 

427 

428 for idx, line in enumerate(lines[index:], start=index): 

429 if 'celldm(1)' in line: 

430 # celldm(1) has more digits than alat!! 

431 info['celldm(1)'] = float(line.split()[1]) * units['Bohr'] 

432 info['alat'] = info['celldm(1)'] 

433 elif 'number of atoms/cell' in line: 

434 info['nat'] = int(line.split()[-1]) 

435 elif 'number of atomic types' in line: 

436 info['ntyp'] = int(line.split()[-1]) 

437 elif 'crystal axes:' in line: 

438 info['cell'] = info['celldm(1)'] * np.array([ 

439 [float(x) for x in lines[idx + 1].split()[3:6]], 

440 [float(x) for x in lines[idx + 2].split()[3:6]], 

441 [float(x) for x in lines[idx + 3].split()[3:6]]]) 

442 elif 'positions (alat units)' in line: 

443 info['symbols'], info['positions'] = [], [] 

444 

445 for at_line in lines[idx + 1:idx + 1 + info['nat']]: 

446 sym, x, y, z = parse_position_line(at_line) 

447 info['symbols'].append(label_to_symbol(sym)) 

448 info['positions'].append([x * info['celldm(1)'], 

449 y * info['celldm(1)'], 

450 z * info['celldm(1)']]) 

451 # This should be the end of interesting info. 

452 # Break here to avoid dealing with large lists of kpoints. 

453 # Will need to be extended for DFTCalculator info. 

454 break 

455 

456 # Make atoms for convenience 

457 info['atoms'] = Atoms(symbols=info['symbols'], 

458 positions=info['positions'], 

459 cell=info['cell'], pbc=True) 

460 

461 return info 

462 

463 

464def parse_position_line(line): 

465 """Parse a single line from a pw.x output file. 

466 

467 The line must contain information about the atomic symbol and the position, 

468 e.g. 

469 

470 995 Sb tau( 995) = ( 1.4212023 0.7037863 0.1242640 ) 

471 

472 Parameters 

473 ---------- 

474 line : str 

475 Line to be parsed. 

476 

477 Returns 

478 ------- 

479 sym : str 

480 Atomic symbol. 

481 x : float 

482 x-position. 

483 y : float 

484 y-position. 

485 z : float 

486 z-position. 

487 """ 

488 pat = re.compile(r'\s*\d+\s*(\S+)\s*tau\(\s*\d+\)\s*=' 

489 r'\s*\(\s*(\S+)\s+(\S+)\s+(\S+)\s*\)') 

490 match = pat.match(line) 

491 assert match is not None 

492 sym, x, y, z = match.group(1, 2, 3, 4) 

493 return sym, float(x), float(y), float(z) 

494 

495 

496@iofunction('rU') 

497def read_espresso_in(fileobj): 

498 """Parse a Quantum ESPRESSO input files, '.in', '.pwi'. 

499 

500 ESPRESSO inputs are generally a fortran-namelist format with custom 

501 blocks of data. The namelist is parsed as a dict and an atoms object 

502 is constructed from the included information. 

503 

504 Parameters 

505 ---------- 

506 fileobj : file | str 

507 A file-like object that supports line iteration with the contents 

508 of the input file, or a filename. 

509 

510 Returns 

511 ------- 

512 atoms : Atoms 

513 Structure defined in the input file. 

514 

515 Raises 

516 ------ 

517 KeyError 

518 Raised for missing keys that are required to process the file 

519 """ 

520 # parse namelist section and extract remaining lines 

521 data, card_lines = read_fortran_namelist(fileobj) 

522 

523 # get the cell if ibrav=0 

524 if 'system' not in data: 

525 raise KeyError('Required section &SYSTEM not found.') 

526 elif 'ibrav' not in data['system']: 

527 raise KeyError('ibrav is required in &SYSTEM') 

528 elif data['system']['ibrav'] == 0: 

529 # celldm(1) is in Bohr, A is in angstrom. celldm(1) will be 

530 # used even if A is also specified. 

531 if 'celldm(1)' in data['system']: 

532 alat = data['system']['celldm(1)'] * units['Bohr'] 

533 elif 'A' in data['system']: 

534 alat = data['system']['A'] 

535 else: 

536 alat = None 

537 cell, _ = get_cell_parameters(card_lines, alat=alat) 

538 else: 

539 raise ValueError(ibrav_error_message) 

540 

541 # species_info holds some info for each element 

542 species_card = get_atomic_species( 

543 card_lines, n_species=data['system']['ntyp']) 

544 species_info = {} 

545 for ispec, (label, weight, pseudo) in enumerate(species_card): 

546 symbol = label_to_symbol(label) 

547 valence = get_valence_electrons(symbol, data, pseudo) 

548 

549 # starting_magnetization is in fractions of valence electrons 

550 magnet_key = "starting_magnetization({0})".format(ispec + 1) 

551 magmom = valence * data["system"].get(magnet_key, 0.0) 

552 species_info[symbol] = {"weight": weight, "pseudo": pseudo, 

553 "valence": valence, "magmom": magmom} 

554 

555 positions_card = get_atomic_positions( 

556 card_lines, n_atoms=data['system']['nat'], cell=cell, alat=alat) 

557 

558 symbols = [label_to_symbol(position[0]) for position in positions_card] 

559 positions = [position[1] for position in positions_card] 

560 magmoms = [species_info[symbol]["magmom"] for symbol in symbols] 

561 

562 # TODO: put more info into the atoms object 

563 # e.g magmom, forces. 

564 atoms = Atoms(symbols=symbols, positions=positions, cell=cell, pbc=True, 

565 magmoms=magmoms) 

566 

567 return atoms 

568 

569 

570def ibrav_to_cell(system): 

571 """ 

572 Convert a value of ibrav to a cell. Any unspecified lattice dimension 

573 is set to 0.0, but will not necessarily raise an error. Also return the 

574 lattice parameter. 

575 

576 Parameters 

577 ---------- 

578 system : dict 

579 The &SYSTEM section of the input file, containing the 'ibrav' setting, 

580 and either celldm(1)..(6) or a, b, c, cosAB, cosAC, cosBC. 

581 

582 Returns 

583 ------- 

584 cell : Cell 

585 The cell as an ASE Cell object 

586 

587 Raises 

588 ------ 

589 KeyError 

590 Raise an error if any required keys are missing. 

591 NotImplementedError 

592 Only a limited number of ibrav settings can be parsed. An error 

593 is raised if the ibrav interpretation is not implemented. 

594 """ 

595 if 'celldm(1)' in system and 'a' in system: 

596 raise KeyError('do not specify both celldm and a,b,c!') 

597 elif 'celldm(1)' in system: 

598 # celldm(x) in bohr 

599 alat = system['celldm(1)'] * units['Bohr'] 

600 b_over_a = system.get('celldm(2)', 0.0) 

601 c_over_a = system.get('celldm(3)', 0.0) 

602 cosab = system.get('celldm(4)', 0.0) 

603 cosac = system.get('celldm(5)', 0.0) 

604 cosbc = 0.0 

605 if system['ibrav'] == 14: 

606 cosbc = system.get('celldm(4)', 0.0) 

607 cosac = system.get('celldm(5)', 0.0) 

608 cosab = system.get('celldm(6)', 0.0) 

609 elif 'a' in system: 

610 # a, b, c, cosAB, cosAC, cosBC in Angstrom 

611 raise NotImplementedError( 

612 'params_to_cell() does not yet support A/B/C/cosAB/cosAC/cosBC') 

613 else: 

614 raise KeyError("Missing celldm(1)") 

615 

616 if system['ibrav'] == 1: 

617 cell = np.identity(3) * alat 

618 elif system['ibrav'] == 2: 

619 cell = np.array([[-1.0, 0.0, 1.0], 

620 [0.0, 1.0, 1.0], 

621 [-1.0, 1.0, 0.0]]) * (alat / 2) 

622 elif system['ibrav'] == 3: 

623 cell = np.array([[1.0, 1.0, 1.0], 

624 [-1.0, 1.0, 1.0], 

625 [-1.0, -1.0, 1.0]]) * (alat / 2) 

626 elif system['ibrav'] == -3: 

627 cell = np.array([[-1.0, 1.0, 1.0], 

628 [1.0, -1.0, 1.0], 

629 [1.0, 1.0, -1.0]]) * (alat / 2) 

630 elif system['ibrav'] == 4: 

631 cell = np.array([[1.0, 0.0, 0.0], 

632 [-0.5, 0.5 * 3**0.5, 0.0], 

633 [0.0, 0.0, c_over_a]]) * alat 

634 elif system['ibrav'] == 5: 

635 tx = ((1.0 - cosab) / 2.0)**0.5 

636 ty = ((1.0 - cosab) / 6.0)**0.5 

637 tz = ((1 + 2 * cosab) / 3.0)**0.5 

638 cell = np.array([[tx, -ty, tz], 

639 [0, 2 * ty, tz], 

640 [-tx, -ty, tz]]) * alat 

641 elif system['ibrav'] == -5: 

642 ty = ((1.0 - cosab) / 6.0)**0.5 

643 tz = ((1 + 2 * cosab) / 3.0)**0.5 

644 a_prime = alat / 3**0.5 

645 u = tz - 2 * 2**0.5 * ty 

646 v = tz + 2**0.5 * ty 

647 cell = np.array([[u, v, v], 

648 [v, u, v], 

649 [v, v, u]]) * a_prime 

650 elif system['ibrav'] == 6: 

651 cell = np.array([[1.0, 0.0, 0.0], 

652 [0.0, 1.0, 0.0], 

653 [0.0, 0.0, c_over_a]]) * alat 

654 elif system['ibrav'] == 7: 

655 cell = np.array([[1.0, -1.0, c_over_a], 

656 [1.0, 1.0, c_over_a], 

657 [-1.0, -1.0, c_over_a]]) * (alat / 2) 

658 elif system['ibrav'] == 8: 

659 cell = np.array([[1.0, 0.0, 0.0], 

660 [0.0, b_over_a, 0.0], 

661 [0.0, 0.0, c_over_a]]) * alat 

662 elif system['ibrav'] == 9: 

663 cell = np.array([[1.0 / 2.0, b_over_a / 2.0, 0.0], 

664 [-1.0 / 2.0, b_over_a / 2.0, 0.0], 

665 [0.0, 0.0, c_over_a]]) * alat 

666 elif system['ibrav'] == -9: 

667 cell = np.array([[1.0 / 2.0, -b_over_a / 2.0, 0.0], 

668 [1.0 / 2.0, b_over_a / 2.0, 0.0], 

669 [0.0, 0.0, c_over_a]]) * alat 

670 elif system['ibrav'] == 10: 

671 cell = np.array([[1.0 / 2.0, 0.0, c_over_a / 2.0], 

672 [1.0 / 2.0, b_over_a / 2.0, 0.0], 

673 [0.0, b_over_a / 2.0, c_over_a / 2.0]]) * alat 

674 elif system['ibrav'] == 11: 

675 cell = np.array([[1.0 / 2.0, b_over_a / 2.0, c_over_a / 2.0], 

676 [-1.0 / 2.0, b_over_a / 2.0, c_over_a / 2.0], 

677 [-1.0 / 2.0, -b_over_a / 2.0, c_over_a / 2.0]]) * alat 

678 elif system['ibrav'] == 12: 

679 sinab = (1.0 - cosab**2)**0.5 

680 cell = np.array([[1.0, 0.0, 0.0], 

681 [b_over_a * cosab, b_over_a * sinab, 0.0], 

682 [0.0, 0.0, c_over_a]]) * alat 

683 elif system['ibrav'] == -12: 

684 sinac = (1.0 - cosac**2)**0.5 

685 cell = np.array([[1.0, 0.0, 0.0], 

686 [0.0, b_over_a, 0.0], 

687 [c_over_a * cosac, 0.0, c_over_a * sinac]]) * alat 

688 elif system['ibrav'] == 13: 

689 sinab = (1.0 - cosab**2)**0.5 

690 cell = np.array([[1.0 / 2.0, 0.0, -c_over_a / 2.0], 

691 [b_over_a * cosab, b_over_a * sinab, 0.0], 

692 [1.0 / 2.0, 0.0, c_over_a / 2.0]]) * alat 

693 elif system['ibrav'] == 14: 

694 sinab = (1.0 - cosab**2)**0.5 

695 v3 = [c_over_a * cosac, 

696 c_over_a * (cosbc - cosac * cosab) / sinab, 

697 c_over_a * ((1 + 2 * cosbc * cosac * cosab 

698 - cosbc**2 - cosac**2 - cosab**2)**0.5) / sinab] 

699 cell = np.array([[1.0, 0.0, 0.0], 

700 [b_over_a * cosab, b_over_a * sinab, 0.0], 

701 v3]) * alat 

702 else: 

703 raise NotImplementedError('ibrav = {0} is not implemented' 

704 ''.format(system['ibrav'])) 

705 

706 return Cell(cell) 

707 

708 

709def get_pseudo_dirs(data): 

710 """Guess a list of possible locations for pseudopotential files. 

711 

712 Parameters 

713 ---------- 

714 data : Namelist 

715 Namelist representing the quantum espresso input parameters 

716 

717 Returns 

718 ------- 

719 pseudo_dirs : list[str] 

720 A list of directories where pseudopotential files could be located. 

721 """ 

722 pseudo_dirs = [] 

723 if 'pseudo_dir' in data['control']: 

724 pseudo_dirs.append(data['control']['pseudo_dir']) 

725 if 'ESPRESSO_PSEUDO' in os.environ: 

726 pseudo_dirs.append(os.environ['ESPRESSO_PSEUDO']) 

727 pseudo_dirs.append(path.expanduser('~/espresso/pseudo/')) 

728 return pseudo_dirs 

729 

730 

731def get_valence_electrons(symbol, data, pseudo=None): 

732 """The number of valence electrons for a atomic symbol. 

733 

734 Parameters 

735 ---------- 

736 symbol : str 

737 Chemical symbol 

738 

739 data : Namelist 

740 Namelist representing the quantum espresso input parameters 

741 

742 pseudo : str, optional 

743 File defining the pseudopotential to be used. If missing a fallback 

744 to the number of valence electrons recommended at 

745 http://materialscloud.org/sssp/ is employed. 

746 """ 

747 if pseudo is None: 

748 pseudo = '{}_dummy.UPF'.format(symbol) 

749 for pseudo_dir in get_pseudo_dirs(data): 

750 if path.exists(path.join(pseudo_dir, pseudo)): 

751 valence = grep_valence(path.join(pseudo_dir, pseudo)) 

752 break 

753 else: # not found in a file 

754 valence = SSSP_VALENCE[atomic_numbers[symbol]] 

755 return valence 

756 

757 

758def get_atomic_positions(lines, n_atoms, cell=None, alat=None): 

759 """Parse atom positions from ATOMIC_POSITIONS card. 

760 

761 Parameters 

762 ---------- 

763 lines : list[str] 

764 A list of lines containing the ATOMIC_POSITIONS card. 

765 n_atoms : int 

766 Expected number of atoms. Only this many lines will be parsed. 

767 cell : np.array 

768 Unit cell of the crystal. Only used with crystal coordinates. 

769 alat : float 

770 Lattice parameter for atomic coordinates. Only used for alat case. 

771 

772 Returns 

773 ------- 

774 positions : list[(str, (float, float, float), (float, float, float))] 

775 A list of the ordered atomic positions in the format: 

776 label, (x, y, z), (if_x, if_y, if_z) 

777 Force multipliers are set to None if not present. 

778 

779 Raises 

780 ------ 

781 ValueError 

782 Any problems parsing the data result in ValueError 

783 

784 """ 

785 

786 positions = None 

787 # no blanks or comment lines, can the consume n_atoms lines for positions 

788 trimmed_lines = (line for line in lines 

789 if line.strip() and not line[0] == '#') 

790 

791 for line in trimmed_lines: 

792 if line.strip().startswith('ATOMIC_POSITIONS'): 

793 if positions is not None: 

794 raise ValueError('Multiple ATOMIC_POSITIONS specified') 

795 # Priority and behaviour tested with QE 5.3 

796 if 'crystal_sg' in line.lower(): 

797 raise NotImplementedError('CRYSTAL_SG not implemented') 

798 elif 'crystal' in line.lower(): 

799 cell = cell 

800 elif 'bohr' in line.lower(): 

801 cell = np.identity(3) * units['Bohr'] 

802 elif 'angstrom' in line.lower(): 

803 cell = np.identity(3) 

804 # elif 'alat' in line.lower(): 

805 # cell = np.identity(3) * alat 

806 else: 

807 if alat is None: 

808 raise ValueError('Set lattice parameter in &SYSTEM for ' 

809 'alat coordinates') 

810 # Always the default, will be DEPRECATED as mandatory 

811 # in future 

812 cell = np.identity(3) * alat 

813 

814 positions = [] 

815 for _dummy in range(n_atoms): 

816 split_line = next(trimmed_lines).split() 

817 # These can be fractions and other expressions 

818 position = np.dot((infix_float(split_line[1]), 

819 infix_float(split_line[2]), 

820 infix_float(split_line[3])), cell) 

821 if len(split_line) > 4: 

822 force_mult = (float(split_line[4]), 

823 float(split_line[5]), 

824 float(split_line[6])) 

825 else: 

826 force_mult = None 

827 

828 positions.append((split_line[0], position, force_mult)) 

829 

830 return positions 

831 

832 

833def get_atomic_species(lines, n_species): 

834 """Parse atomic species from ATOMIC_SPECIES card. 

835 

836 Parameters 

837 ---------- 

838 lines : list[str] 

839 A list of lines containing the ATOMIC_POSITIONS card. 

840 n_species : int 

841 Expected number of atom types. Only this many lines will be parsed. 

842 

843 Returns 

844 ------- 

845 species : list[(str, float, str)] 

846 

847 Raises 

848 ------ 

849 ValueError 

850 Any problems parsing the data result in ValueError 

851 """ 

852 

853 species = None 

854 # no blanks or comment lines, can the consume n_atoms lines for positions 

855 trimmed_lines = (line.strip() for line in lines 

856 if line.strip() and not line.startswith('#')) 

857 

858 for line in trimmed_lines: 

859 if line.startswith('ATOMIC_SPECIES'): 

860 if species is not None: 

861 raise ValueError('Multiple ATOMIC_SPECIES specified') 

862 

863 species = [] 

864 for _dummy in range(n_species): 

865 label_weight_pseudo = next(trimmed_lines).split() 

866 species.append((label_weight_pseudo[0], 

867 float(label_weight_pseudo[1]), 

868 label_weight_pseudo[2])) 

869 

870 return species 

871 

872 

873def get_cell_parameters(lines, alat=None): 

874 """Parse unit cell from CELL_PARAMETERS card. 

875 

876 Parameters 

877 ---------- 

878 lines : list[str] 

879 A list with lines containing the CELL_PARAMETERS card. 

880 alat : float | None 

881 Unit of lattice vectors in Angstrom. Only used if the card is 

882 given in units of alat. alat must be None if CELL_PARAMETERS card 

883 is in Bohr or Angstrom. For output files, alat will be parsed from 

884 the card header and used in preference to this value. 

885 

886 Returns 

887 ------- 

888 cell : np.array | None 

889 Cell parameters as a 3x3 array in Angstrom. If no cell is found 

890 None will be returned instead. 

891 cell_alat : float | None 

892 If a value for alat is given in the card header, this is also 

893 returned, otherwise this will be None. 

894 

895 Raises 

896 ------ 

897 ValueError 

898 If CELL_PARAMETERS are given in units of bohr or angstrom 

899 and alat is not 

900 """ 

901 

902 cell = None 

903 cell_alat = None 

904 # no blanks or comment lines, can take three lines for cell 

905 trimmed_lines = (line for line in lines 

906 if line.strip() and not line[0] == '#') 

907 

908 for line in trimmed_lines: 

909 if line.strip().startswith('CELL_PARAMETERS'): 

910 if cell is not None: 

911 # multiple definitions 

912 raise ValueError('CELL_PARAMETERS specified multiple times') 

913 # Priority and behaviour tested with QE 5.3 

914 if 'bohr' in line.lower(): 

915 if alat is not None: 

916 raise ValueError('Lattice parameters given in ' 

917 '&SYSTEM celldm/A and CELL_PARAMETERS ' 

918 'bohr') 

919 cell_units = units['Bohr'] 

920 elif 'angstrom' in line.lower(): 

921 if alat is not None: 

922 raise ValueError('Lattice parameters given in ' 

923 '&SYSTEM celldm/A and CELL_PARAMETERS ' 

924 'angstrom') 

925 cell_units = 1.0 

926 elif 'alat' in line.lower(): 

927 # Output file has (alat = value) (in Bohrs) 

928 if '=' in line: 

929 alat = float(line.strip(') \n').split()[-1]) * units['Bohr'] 

930 cell_alat = alat 

931 elif alat is None: 

932 raise ValueError('Lattice parameters must be set in ' 

933 '&SYSTEM for alat units') 

934 cell_units = alat 

935 elif alat is None: 

936 # may be DEPRECATED in future 

937 cell_units = units['Bohr'] 

938 else: 

939 # may be DEPRECATED in future 

940 cell_units = alat 

941 # Grab the parameters; blank lines have been removed 

942 cell = [[ffloat(x) for x in next(trimmed_lines).split()[:3]], 

943 [ffloat(x) for x in next(trimmed_lines).split()[:3]], 

944 [ffloat(x) for x in next(trimmed_lines).split()[:3]]] 

945 cell = np.array(cell) * cell_units 

946 

947 return cell, cell_alat 

948 

949 

950def str_to_value(string): 

951 """Attempt to convert string into int, float (including fortran double), 

952 or bool, in that order, otherwise return the string. 

953 Valid (case-insensitive) bool values are: '.true.', '.t.', 'true' 

954 and 't' (or false equivalents). 

955 

956 Parameters 

957 ---------- 

958 string : str 

959 Test to parse for a datatype 

960 

961 Returns 

962 ------- 

963 value : any 

964 Parsed string as the most appropriate datatype of int, float, 

965 bool or string. 

966 

967 """ 

968 

969 # Just an integer 

970 try: 

971 return int(string) 

972 except ValueError: 

973 pass 

974 # Standard float 

975 try: 

976 return float(string) 

977 except ValueError: 

978 pass 

979 # Fortran double 

980 try: 

981 return ffloat(string) 

982 except ValueError: 

983 pass 

984 

985 # possible bool, else just the raw string 

986 if string.lower() in ('.true.', '.t.', 'true', 't'): 

987 return True 

988 elif string.lower() in ('.false.', '.f.', 'false', 'f'): 

989 return False 

990 else: 

991 return string.strip("'") 

992 

993 

994def read_fortran_namelist(fileobj): 

995 """Takes a fortran-namelist formatted file and returns nested 

996 dictionaries of sections and key-value data, followed by a list 

997 of lines of text that do not fit the specifications. 

998 

999 Behaviour is taken from Quantum ESPRESSO 5.3. Parses fairly 

1000 convoluted files the same way that QE should, but may not get 

1001 all the MANDATORY rules and edge cases for very non-standard files: 

1002 Ignores anything after '!' in a namelist, split pairs on ',' 

1003 to include multiple key=values on a line, read values on section 

1004 start and end lines, section terminating character, '/', can appear 

1005 anywhere on a line. 

1006 All of these are ignored if the value is in 'quotes'. 

1007 

1008 Parameters 

1009 ---------- 

1010 fileobj : file 

1011 An open file-like object. 

1012 

1013 Returns 

1014 ------- 

1015 data : dict of dict 

1016 Dictionary for each section in the namelist with key = value 

1017 pairs of data. 

1018 card_lines : list of str 

1019 Any lines not used to create the data, assumed to belong to 'cards' 

1020 in the input file. 

1021 

1022 """ 

1023 # Espresso requires the correct order 

1024 data = Namelist() 

1025 card_lines = [] 

1026 in_namelist = False 

1027 section = 'none' # can't be in a section without changing this 

1028 

1029 for line in fileobj: 

1030 # leading and trailing whitespace never needed 

1031 line = line.strip() 

1032 if line.startswith('&'): 

1033 # inside a namelist 

1034 section = line.split()[0][1:].lower() # case insensitive 

1035 if section in data: 

1036 # Repeated sections are completely ignored. 

1037 # (Note that repeated keys overwrite within a section) 

1038 section = "_ignored" 

1039 data[section] = Namelist() 

1040 in_namelist = True 

1041 if not in_namelist and line: 

1042 # Stripped line is Truthy, so safe to index first character 

1043 if line[0] not in ('!', '#'): 

1044 card_lines.append(line) 

1045 if in_namelist: 

1046 # parse k, v from line: 

1047 key = [] 

1048 value = None 

1049 in_quotes = False 

1050 for character in line: 

1051 if character == ',' and value is not None and not in_quotes: 

1052 # finished value: 

1053 data[section][''.join(key).strip()] = str_to_value( 

1054 ''.join(value).strip()) 

1055 key = [] 

1056 value = None 

1057 elif character == '=' and value is None and not in_quotes: 

1058 # start writing value 

1059 value = [] 

1060 elif character == "'": 

1061 # only found in value anyway 

1062 in_quotes = not in_quotes 

1063 value.append("'") 

1064 elif character == '!' and not in_quotes: 

1065 break 

1066 elif character == '/' and not in_quotes: 

1067 in_namelist = False 

1068 break 

1069 elif value is not None: 

1070 value.append(character) 

1071 else: 

1072 key.append(character) 

1073 if value is not None: 

1074 data[section][''.join(key).strip()] = str_to_value( 

1075 ''.join(value).strip()) 

1076 

1077 return data, card_lines 

1078 

1079 

1080def ffloat(string): 

1081 """Parse float from fortran compatible float definitions. 

1082 

1083 In fortran exponents can be defined with 'd' or 'q' to symbolise 

1084 double or quad precision numbers. Double precision numbers are 

1085 converted to python floats and quad precision values are interpreted 

1086 as numpy longdouble values (platform specific precision). 

1087 

1088 Parameters 

1089 ---------- 

1090 string : str 

1091 A string containing a number in fortran real format 

1092 

1093 Returns 

1094 ------- 

1095 value : float | np.longdouble 

1096 Parsed value of the string. 

1097 

1098 Raises 

1099 ------ 

1100 ValueError 

1101 Unable to parse a float value. 

1102 

1103 """ 

1104 

1105 if 'q' in string.lower(): 

1106 return np.longdouble(string.lower().replace('q', 'e')) 

1107 else: 

1108 return float(string.lower().replace('d', 'e')) 

1109 

1110 

1111def label_to_symbol(label): 

1112 """Convert a valid espresso ATOMIC_SPECIES label to a 

1113 chemical symbol. 

1114 

1115 Parameters 

1116 ---------- 

1117 label : str 

1118 chemical symbol X (1 or 2 characters, case-insensitive) 

1119 or chemical symbol plus a number or a letter, as in 

1120 "Xn" (e.g. Fe1) or "X_*" or "X-*" (e.g. C1, C_h; 

1121 max total length cannot exceed 3 characters). 

1122 

1123 Returns 

1124 ------- 

1125 symbol : str 

1126 The best matching species from ase.utils.chemical_symbols 

1127 

1128 Raises 

1129 ------ 

1130 KeyError 

1131 Couldn't find an appropriate species. 

1132 

1133 Notes 

1134 ----- 

1135 It's impossible to tell whether e.g. He is helium 

1136 or hydrogen labelled 'e'. 

1137 """ 

1138 

1139 # possibly a two character species 

1140 # ase Atoms need proper case of chemical symbols. 

1141 if len(label) >= 2: 

1142 test_symbol = label[0].upper() + label[1].lower() 

1143 if test_symbol in chemical_symbols: 

1144 return test_symbol 

1145 # finally try with one character 

1146 test_symbol = label[0].upper() 

1147 if test_symbol in chemical_symbols: 

1148 return test_symbol 

1149 else: 

1150 raise KeyError('Could not parse species from label {0}.' 

1151 ''.format(label)) 

1152 

1153 

1154def infix_float(text): 

1155 """Parse simple infix maths into a float for compatibility with 

1156 Quantum ESPRESSO ATOMIC_POSITIONS cards. Note: this works with the 

1157 example, and most simple expressions, but the capabilities of 

1158 the two parsers are not identical. Will also parse a normal float 

1159 value properly, but slowly. 

1160 

1161 >>> infix_float('1/2*3^(-1/2)') 

1162 0.28867513459481287 

1163 

1164 Parameters 

1165 ---------- 

1166 text : str 

1167 An arithmetic expression using +, -, *, / and ^, including brackets. 

1168 

1169 Returns 

1170 ------- 

1171 value : float 

1172 Result of the mathematical expression. 

1173 

1174 """ 

1175 

1176 def middle_brackets(full_text): 

1177 """Extract text from innermost brackets.""" 

1178 start, end = 0, len(full_text) 

1179 for (idx, char) in enumerate(full_text): 

1180 if char == '(': 

1181 start = idx 

1182 if char == ')': 

1183 end = idx + 1 

1184 break 

1185 return full_text[start:end] 

1186 

1187 def eval_no_bracket_expr(full_text): 

1188 """Calculate value of a mathematical expression, no brackets.""" 

1189 exprs = [('+', op.add), ('*', op.mul), 

1190 ('/', op.truediv), ('^', op.pow)] 

1191 full_text = full_text.lstrip('(').rstrip(')') 

1192 try: 

1193 return float(full_text) 

1194 except ValueError: 

1195 for symbol, func in exprs: 

1196 if symbol in full_text: 

1197 left, right = full_text.split(symbol, 1) # single split 

1198 return func(eval_no_bracket_expr(left), 

1199 eval_no_bracket_expr(right)) 

1200 

1201 while '(' in text: 

1202 middle = middle_brackets(text) 

1203 text = text.replace(middle, '{}'.format(eval_no_bracket_expr(middle))) 

1204 

1205 return float(eval_no_bracket_expr(text)) 

1206 

1207### 

1208# Input file writing 

1209### 

1210 

1211 

1212# Ordered and case insensitive 

1213KEYS = Namelist(( 

1214 ('CONTROL', [ 

1215 'calculation', 'title', 'verbosity', 'restart_mode', 'wf_collect', 

1216 'nstep', 'iprint', 'tstress', 'tprnfor', 'dt', 'outdir', 'wfcdir', 

1217 'prefix', 'lkpoint_dir', 'max_seconds', 'etot_conv_thr', 

1218 'forc_conv_thr', 'disk_io', 'pseudo_dir', 'tefield', 'dipfield', 

1219 'lelfield', 'nberrycyc', 'lorbm', 'lberry', 'gdir', 'nppstr', 

1220 'lfcpopt', 'monopole']), 

1221 ('SYSTEM', [ 

1222 'ibrav', 'nat', 'ntyp', 'nbnd', 'tot_charge', 'tot_magnetization', 

1223 'starting_magnetization', 'ecutwfc', 'ecutrho', 'ecutfock', 'nr1', 

1224 'nr2', 'nr3', 'nr1s', 'nr2s', 'nr3s', 'nosym', 'nosym_evc', 'noinv', 

1225 'no_t_rev', 'force_symmorphic', 'use_all_frac', 'occupations', 

1226 'one_atom_occupations', 'starting_spin_angle', 'degauss', 'smearing', 

1227 'nspin', 'noncolin', 'ecfixed', 'qcutz', 'q2sigma', 'input_dft', 

1228 'exx_fraction', 'screening_parameter', 'exxdiv_treatment', 

1229 'x_gamma_extrapolation', 'ecutvcut', 'nqx1', 'nqx2', 'nqx3', 

1230 'lda_plus_u', 'lda_plus_u_kind', 'Hubbard_U', 'Hubbard_J0', 

1231 'Hubbard_alpha', 'Hubbard_beta', 'Hubbard_J', 

1232 'starting_ns_eigenvalue', 'U_projection_type', 'edir', 

1233 'emaxpos', 'eopreg', 'eamp', 'angle1', 'angle2', 

1234 'constrained_magnetization', 'fixed_magnetization', 'lambda', 

1235 'report', 'lspinorb', 'assume_isolated', 'esm_bc', 'esm_w', 

1236 'esm_efield', 'esm_nfit', 'fcp_mu', 'vdw_corr', 'london', 

1237 'london_s6', 'london_c6', 'london_rvdw', 'london_rcut', 

1238 'ts_vdw_econv_thr', 'ts_vdw_isolated', 'xdm', 'xdm_a1', 'xdm_a2', 

1239 'space_group', 'uniqueb', 'origin_choice', 'rhombohedral', 'zmon', 

1240 'realxz', 'block', 'block_1', 'block_2', 'block_height']), 

1241 ('ELECTRONS', [ 

1242 'electron_maxstep', 'scf_must_converge', 'conv_thr', 'adaptive_thr', 

1243 'conv_thr_init', 'conv_thr_multi', 'mixing_mode', 'mixing_beta', 

1244 'mixing_ndim', 'mixing_fixed_ns', 'diagonalization', 'ortho_para', 

1245 'diago_thr_init', 'diago_cg_maxiter', 'diago_david_ndim', 

1246 'diago_full_acc', 'efield', 'efield_cart', 'efield_phase', 

1247 'startingpot', 'startingwfc', 'tqr']), 

1248 ('IONS', [ 

1249 'ion_dynamics', 'ion_positions', 'pot_extrapolation', 

1250 'wfc_extrapolation', 'remove_rigid_rot', 'ion_temperature', 'tempw', 

1251 'tolp', 'delta_t', 'nraise', 'refold_pos', 'upscale', 'bfgs_ndim', 

1252 'trust_radius_max', 'trust_radius_min', 'trust_radius_ini', 'w_1', 

1253 'w_2']), 

1254 ('CELL', [ 

1255 'cell_dynamics', 'press', 'wmass', 'cell_factor', 'press_conv_thr', 

1256 'cell_dofree']))) 

1257 

1258 

1259# Number of valence electrons in the pseudopotentials recommended by 

1260# http://materialscloud.org/sssp/. These are just used as a fallback for 

1261# calculating initial magetization values which are given as a fraction 

1262# of valence electrons. 

1263SSSP_VALENCE = [ 

1264 0, 1.0, 2.0, 3.0, 4.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 3.0, 4.0, 

1265 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 

1266 18.0, 19.0, 20.0, 13.0, 14.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 

1267 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 12.0, 13.0, 14.0, 15.0, 6.0, 

1268 7.0, 18.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 

1269 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 36.0, 27.0, 14.0, 15.0, 30.0, 

1270 15.0, 32.0, 19.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0] 

1271 

1272 

1273def construct_namelist(parameters=None, warn=False, **kwargs): 

1274 """ 

1275 Construct an ordered Namelist containing all the parameters given (as 

1276 a dictionary or kwargs). Keys will be inserted into their appropriate 

1277 section in the namelist and the dictionary may contain flat and nested 

1278 structures. Any kwargs that match input keys will be incorporated into 

1279 their correct section. All matches are case-insensitive, and returned 

1280 Namelist object is a case-insensitive dict. 

1281 

1282 If a key is not known to ase, but in a section within `parameters`, 

1283 it will be assumed that it was put there on purpose and included 

1284 in the output namelist. Anything not in a section will be ignored (set 

1285 `warn` to True to see ignored keys). 

1286 

1287 Keys with a dimension (e.g. Hubbard_U(1)) will be incorporated as-is 

1288 so the `i` should be made to match the output. 

1289 

1290 The priority of the keys is: 

1291 kwargs[key] > parameters[key] > parameters[section][key] 

1292 Only the highest priority item will be included. 

1293 

1294 Parameters 

1295 ---------- 

1296 parameters: dict 

1297 Flat or nested set of input parameters. 

1298 warn: bool 

1299 Enable warnings for unused keys. 

1300 

1301 Returns 

1302 ------- 

1303 input_namelist: Namelist 

1304 pw.x compatible namelist of input parameters. 

1305 

1306 """ 

1307 # Convert everything to Namelist early to make case-insensitive 

1308 if parameters is None: 

1309 parameters = Namelist() 

1310 else: 

1311 # Maximum one level of nested dict 

1312 # Don't modify in place 

1313 parameters_namelist = Namelist() 

1314 for key, value in parameters.items(): 

1315 if isinstance(value, dict): 

1316 parameters_namelist[key] = Namelist(value) 

1317 else: 

1318 parameters_namelist[key] = value 

1319 parameters = parameters_namelist 

1320 

1321 # Just a dict 

1322 kwargs = Namelist(kwargs) 

1323 

1324 # Final parameter set 

1325 input_namelist = Namelist() 

1326 

1327 # Collect 

1328 for section in KEYS: 

1329 sec_list = Namelist() 

1330 for key in KEYS[section]: 

1331 # Check all three separately and pop them all so that 

1332 # we can check for missing values later 

1333 if key in parameters.get(section, {}): 

1334 sec_list[key] = parameters[section].pop(key) 

1335 if key in parameters: 

1336 sec_list[key] = parameters.pop(key) 

1337 if key in kwargs: 

1338 sec_list[key] = kwargs.pop(key) 

1339 

1340 # Check if there is a key(i) version (no extra parsing) 

1341 for arg_key in list(parameters.get(section, {})): 

1342 if arg_key.split('(')[0].strip().lower() == key.lower(): 

1343 sec_list[arg_key] = parameters[section].pop(arg_key) 

1344 cp_parameters = parameters.copy() 

1345 for arg_key in cp_parameters: 

1346 if arg_key.split('(')[0].strip().lower() == key.lower(): 

1347 sec_list[arg_key] = parameters.pop(arg_key) 

1348 cp_kwargs = kwargs.copy() 

1349 for arg_key in cp_kwargs: 

1350 if arg_key.split('(')[0].strip().lower() == key.lower(): 

1351 sec_list[arg_key] = kwargs.pop(arg_key) 

1352 

1353 # Add to output 

1354 input_namelist[section] = sec_list 

1355 

1356 unused_keys = list(kwargs) 

1357 # pass anything else already in a section 

1358 for key, value in parameters.items(): 

1359 if key in KEYS and isinstance(value, dict): 

1360 input_namelist[key].update(value) 

1361 elif isinstance(value, dict): 

1362 unused_keys.extend(list(value)) 

1363 else: 

1364 unused_keys.append(key) 

1365 

1366 if warn and unused_keys: 

1367 warnings.warn('Unused keys: {}'.format(', '.join(unused_keys))) 

1368 

1369 return input_namelist 

1370 

1371 

1372def grep_valence(pseudopotential): 

1373 """ 

1374 Given a UPF pseudopotential file, find the number of valence atoms. 

1375 

1376 Parameters 

1377 ---------- 

1378 pseudopotential: str 

1379 Filename of the pseudopotential. 

1380 

1381 Returns 

1382 ------- 

1383 valence: float 

1384 Valence as reported in the pseudopotential. 

1385 

1386 Raises 

1387 ------ 

1388 ValueError 

1389 If valence cannot be found in the pseudopotential. 

1390 """ 

1391 

1392 # Example lines 

1393 # Sr.pbe-spn-rrkjus_psl.1.0.0.UPF: z_valence="1.000000000000000E+001" 

1394 # C.pbe-n-kjpaw_psl.1.0.0.UPF (new ld1.x): 

1395 # ...PBC" z_valence="4.000000000000e0" total_p... 

1396 # C_ONCV_PBE-1.0.upf: z_valence=" 4.00" 

1397 # Ta_pbe_v1.uspp.F.UPF: 13.00000000000 Z valence 

1398 

1399 with open(pseudopotential) as psfile: 

1400 for line in psfile: 

1401 if 'z valence' in line.lower(): 

1402 return float(line.split()[0]) 

1403 elif 'z_valence' in line.lower(): 

1404 if line.split()[0] == '<PP_HEADER': 

1405 line = list(filter(lambda x: 'z_valence' in x, 

1406 line.split(' ')))[0] 

1407 return float(line.split('=')[-1].strip().strip('"')) 

1408 else: 

1409 raise ValueError('Valence missing in {}'.format(pseudopotential)) 

1410 

1411 

1412def kspacing_to_grid(atoms, spacing, calculated_spacing=None): 

1413 """ 

1414 Calculate the kpoint mesh that is equivalent to the given spacing 

1415 in reciprocal space (units Angstrom^-1). The number of kpoints is each 

1416 dimension is rounded up (compatible with CASTEP). 

1417 

1418 Parameters 

1419 ---------- 

1420 atoms: ase.Atoms 

1421 A structure that can have get_reciprocal_cell called on it. 

1422 spacing: float 

1423 Minimum K-Point spacing in $A^{-1}$. 

1424 calculated_spacing : list 

1425 If a three item list (or similar mutable sequence) is given the 

1426 members will be replaced with the actual calculated spacing in 

1427 $A^{-1}$. 

1428 

1429 Returns 

1430 ------- 

1431 kpoint_grid : [int, int, int] 

1432 MP grid specification to give the required spacing. 

1433 

1434 """ 

1435 # No factor of 2pi in ase, everything in A^-1 

1436 # reciprocal dimensions 

1437 r_x, r_y, r_z = np.linalg.norm(atoms.cell.reciprocal(), axis=1) 

1438 

1439 kpoint_grid = [int(r_x / spacing) + 1, 

1440 int(r_y / spacing) + 1, 

1441 int(r_z / spacing) + 1] 

1442 

1443 for i, _ in enumerate(kpoint_grid): 

1444 if not atoms.pbc[i]: 

1445 kpoint_grid[i] = 1 

1446 

1447 if calculated_spacing is not None: 

1448 calculated_spacing[:] = [r_x / kpoint_grid[0], 

1449 r_y / kpoint_grid[1], 

1450 r_z / kpoint_grid[2]] 

1451 

1452 return kpoint_grid 

1453 

1454 

1455def format_atom_position(atom, crystal_coordinates, mask='', tidx=None): 

1456 """Format one line of atomic positions in 

1457 Quantum ESPRESSO ATOMIC_POSITIONS card. 

1458 

1459 >>> for atom in make_supercell(bulk('Li', 'bcc'), np.ones(3)-np.eye(3)): 

1460 >>> format_atom_position(atom, True) 

1461 Li 0.0000000000 0.0000000000 0.0000000000 

1462 Li 0.5000000000 0.5000000000 0.5000000000 

1463 

1464 Parameters 

1465 ---------- 

1466 atom : Atom 

1467 A structure that has symbol and [position | (a, b, c)]. 

1468 crystal_coordinates: bool 

1469 Whether the atomic positions should be written to the QE input file in 

1470 absolute (False, default) or relative (crystal) coordinates (True). 

1471 mask, optional : str 

1472 String of ndim=3 0 or 1 for constraining atomic positions. 

1473 tidx, optional : int 

1474 Magnetic type index. 

1475 

1476 Returns 

1477 ------- 

1478 atom_line : str 

1479 Input line for atom position 

1480 """ 

1481 if crystal_coordinates: 

1482 coords = [atom.a, atom.b, atom.c] 

1483 else: 

1484 coords = atom.position 

1485 line_fmt = '{atom.symbol}' 

1486 inps = dict(atom=atom) 

1487 if tidx is not None: 

1488 line_fmt += '{tidx}' 

1489 inps["tidx"] = tidx 

1490 line_fmt += ' {coords[0]:.10f} {coords[1]:.10f} {coords[2]:.10f} ' 

1491 inps["coords"] = coords 

1492 line_fmt += ' ' + mask + '\n' 

1493 astr = line_fmt.format(**inps) 

1494 return astr 

1495 

1496 

1497def write_espresso_in(fd, atoms, input_data=None, pseudopotentials=None, 

1498 kspacing=None, kpts=None, koffset=(0, 0, 0), 

1499 crystal_coordinates=False, **kwargs): 

1500 """ 

1501 Create an input file for pw.x. 

1502 

1503 Use set_initial_magnetic_moments to turn on spin, if ispin is set to 2 

1504 with no magnetic moments, they will all be set to 0.0. Magnetic moments 

1505 will be converted to the QE units (fraction of valence electrons) using 

1506 any pseudopotential files found, or a best guess for the number of 

1507 valence electrons. 

1508 

1509 Units are not converted for any other input data, so use Quantum ESPRESSO 

1510 units (Usually Ry or atomic units). 

1511 

1512 Keys with a dimension (e.g. Hubbard_U(1)) will be incorporated as-is 

1513 so the `i` should be made to match the output. 

1514 

1515 Implemented features: 

1516 

1517 - Conversion of :class:`ase.constraints.FixAtoms` and 

1518 :class:`ase.constraints.FixCartesian`. 

1519 - `starting_magnetization` derived from the `mgmoms` and pseudopotentials 

1520 (searches default paths for pseudo files.) 

1521 - Automatic assignment of options to their correct sections. 

1522 

1523 Not implemented: 

1524 

1525 - Non-zero values of ibrav 

1526 - Lists of k-points 

1527 - Other constraints 

1528 - Hubbard parameters 

1529 - Validation of the argument types for input 

1530 - Validation of required options 

1531 - Noncollinear magnetism 

1532 

1533 Parameters 

1534 ---------- 

1535 fd: file 

1536 A file like object to write the input file to. 

1537 atoms: Atoms 

1538 A single atomistic configuration to write to `fd`. 

1539 input_data: dict 

1540 A flat or nested dictionary with input parameters for pw.x 

1541 pseudopotentials: dict 

1542 A filename for each atomic species, e.g. 

1543 {'O': 'O.pbe-rrkjus.UPF', 'H': 'H.pbe-rrkjus.UPF'}. 

1544 A dummy name will be used if none are given. 

1545 kspacing: float 

1546 Generate a grid of k-points with this as the minimum distance, 

1547 in A^-1 between them in reciprocal space. If set to None, kpts 

1548 will be used instead. 

1549 kpts: (int, int, int) or dict 

1550 If kpts is a tuple (or list) of 3 integers, it is interpreted 

1551 as the dimensions of a Monkhorst-Pack grid. 

1552 If ``kpts`` is set to ``None``, only the Γ-point will be included 

1553 and QE will use routines optimized for Γ-point-only calculations. 

1554 Compared to Γ-point-only calculations without this optimization 

1555 (i.e. with ``kpts=(1, 1, 1)``), the memory and CPU requirements 

1556 are typically reduced by half. 

1557 If kpts is a dict, it will either be interpreted as a path 

1558 in the Brillouin zone (*) if it contains the 'path' keyword, 

1559 otherwise it is converted to a Monkhorst-Pack grid (**). 

1560 (*) see ase.dft.kpoints.bandpath 

1561 (**) see ase.calculators.calculator.kpts2sizeandoffsets 

1562 koffset: (int, int, int) 

1563 Offset of kpoints in each direction. Must be 0 (no offset) or 

1564 1 (half grid offset). Setting to True is equivalent to (1, 1, 1). 

1565 crystal_coordinates: bool 

1566 Whether the atomic positions should be written to the QE input file in 

1567 absolute (False, default) or relative (crystal) coordinates (True). 

1568 

1569 """ 

1570 

1571 # Convert to a namelist to make working with parameters much easier 

1572 # Note that the name ``input_data`` is chosen to prevent clash with 

1573 # ``parameters`` in Calculator objects 

1574 input_parameters = construct_namelist(input_data, **kwargs) 

1575 

1576 # Convert ase constraints to QE constraints 

1577 # Nx3 array of force multipliers matches what QE uses 

1578 # Do this early so it is available when constructing the atoms card 

1579 constraint_mask = np.ones((len(atoms), 3), dtype='int') 

1580 for constraint in atoms.constraints: 

1581 if isinstance(constraint, FixAtoms): 

1582 constraint_mask[constraint.index] = 0 

1583 elif isinstance(constraint, FixCartesian): 

1584 constraint_mask[constraint.a] = constraint.mask 

1585 else: 

1586 warnings.warn('Ignored unknown constraint {}'.format(constraint)) 

1587 masks = [] 

1588 for atom in atoms: 

1589 # only inclued mask if something is fixed 

1590 if not all(constraint_mask[atom.index]): 

1591 mask = ' {mask[0]} {mask[1]} {mask[2]}'.format( 

1592 mask=constraint_mask[atom.index]) 

1593 else: 

1594 mask = '' 

1595 masks.append(mask) 

1596 

1597 # Species info holds the information on the pseudopotential and 

1598 # associated for each element 

1599 if pseudopotentials is None: 

1600 pseudopotentials = {} 

1601 species_info = {} 

1602 for species in set(atoms.get_chemical_symbols()): 

1603 # Look in all possible locations for the pseudos and try to figure 

1604 # out the number of valence electrons 

1605 pseudo = pseudopotentials.get(species, None) 

1606 valence = get_valence_electrons(species, input_parameters, pseudo) 

1607 species_info[species] = {'pseudo': pseudo, 'valence': valence} 

1608 

1609 # Convert atoms into species. 

1610 # Each different magnetic moment needs to be a separate type even with 

1611 # the same pseudopotential (e.g. an up and a down for AFM). 

1612 # if any magmom are > 0 or nspin == 2 then use species labels. 

1613 # Rememeber: magnetisation uses 1 based indexes 

1614 atomic_species = OrderedDict() 

1615 atomic_species_str = [] 

1616 atomic_positions_str = [] 

1617 

1618 nspin = input_parameters['system'].get('nspin', 1) # 1 is the default 

1619 if any(atoms.get_initial_magnetic_moments()): 

1620 if nspin == 1: 

1621 # Force spin on 

1622 input_parameters['system']['nspin'] = 2 

1623 nspin = 2 

1624 

1625 if nspin == 2: 

1626 # Spin on 

1627 for atom, mask, magmom in zip( 

1628 atoms, masks, atoms.get_initial_magnetic_moments()): 

1629 if (atom.symbol, magmom) not in atomic_species: 

1630 # spin as fraction of valence 

1631 fspin = float(magmom) / species_info[atom.symbol]['valence'] 

1632 # Index in the atomic species list 

1633 sidx = len(atomic_species) + 1 

1634 # Index for that atom type; no index for first one 

1635 tidx = sum(atom.symbol == x[0] for x in atomic_species) or ' ' 

1636 atomic_species[(atom.symbol, magmom)] = (sidx, tidx) 

1637 # Add magnetization to the input file 

1638 mag_str = 'starting_magnetization({0})'.format(sidx) 

1639 input_parameters['system'][mag_str] = fspin 

1640 atomic_species_str.append( 

1641 '{species}{tidx} {mass} {pseudo}\n'.format( 

1642 species=atom.symbol, tidx=tidx, mass=atom.mass, 

1643 pseudo=species_info[atom.symbol]['pseudo'])) 

1644 # lookup tidx to append to name 

1645 sidx, tidx = atomic_species[(atom.symbol, magmom)] 

1646 # construct line for atomic positions 

1647 atomic_positions_str.append( 

1648 format_atom_position( 

1649 atom, crystal_coordinates, mask=mask, tidx=tidx) 

1650 ) 

1651 else: 

1652 # Do nothing about magnetisation 

1653 for atom, mask in zip(atoms, masks): 

1654 if atom.symbol not in atomic_species: 

1655 atomic_species[atom.symbol] = True # just a placeholder 

1656 atomic_species_str.append( 

1657 '{species} {mass} {pseudo}\n'.format( 

1658 species=atom.symbol, mass=atom.mass, 

1659 pseudo=species_info[atom.symbol]['pseudo'])) 

1660 # construct line for atomic positions 

1661 atomic_positions_str.append( 

1662 format_atom_position(atom, crystal_coordinates, mask=mask) 

1663 ) 

1664 

1665 # Add computed parameters 

1666 # different magnetisms means different types 

1667 input_parameters['system']['ntyp'] = len(atomic_species) 

1668 input_parameters['system']['nat'] = len(atoms) 

1669 

1670 # Use cell as given or fit to a specific ibrav 

1671 if 'ibrav' in input_parameters['system']: 

1672 ibrav = input_parameters['system']['ibrav'] 

1673 if ibrav != 0: 

1674 raise ValueError(ibrav_error_message) 

1675 else: 

1676 # Just use standard cell block 

1677 input_parameters['system']['ibrav'] = 0 

1678 

1679 # Construct input file into this 

1680 pwi = [] 

1681 

1682 # Assume sections are ordered (taken care of in namelist construction) 

1683 # and that repr converts to a QE readable representation (except bools) 

1684 for section in input_parameters: 

1685 pwi.append('&{0}\n'.format(section.upper())) 

1686 for key, value in input_parameters[section].items(): 

1687 if value is True: 

1688 pwi.append(' {0:16} = .true.\n'.format(key)) 

1689 elif value is False: 

1690 pwi.append(' {0:16} = .false.\n'.format(key)) 

1691 else: 

1692 # repr format to get quotes around strings 

1693 pwi.append(' {0:16} = {1!r:}\n'.format(key, value)) 

1694 pwi.append('/\n') # terminate section 

1695 pwi.append('\n') 

1696 

1697 # Pseudopotentials 

1698 pwi.append('ATOMIC_SPECIES\n') 

1699 pwi.extend(atomic_species_str) 

1700 pwi.append('\n') 

1701 

1702 # KPOINTS - add a MP grid as required 

1703 if kspacing is not None: 

1704 kgrid = kspacing_to_grid(atoms, kspacing) 

1705 elif kpts is not None: 

1706 if isinstance(kpts, dict) and 'path' not in kpts: 

1707 kgrid, shift = kpts2sizeandoffsets(atoms=atoms, **kpts) 

1708 koffset = [] 

1709 for i, x in enumerate(shift): 

1710 assert x == 0 or abs(x * kgrid[i] - 0.5) < 1e-14 

1711 koffset.append(0 if x == 0 else 1) 

1712 else: 

1713 kgrid = kpts 

1714 else: 

1715 kgrid = "gamma" 

1716 

1717 # True and False work here and will get converted by ':d' format 

1718 if isinstance(koffset, int): 

1719 koffset = (koffset, ) * 3 

1720 

1721 # BandPath object or bandpath-as-dictionary: 

1722 if isinstance(kgrid, dict) or hasattr(kgrid, 'kpts'): 

1723 pwi.append('K_POINTS crystal_b\n') 

1724 assert hasattr(kgrid, 'path') or 'path' in kgrid 

1725 kgrid = kpts2ndarray(kgrid, atoms=atoms) 

1726 pwi.append('%s\n' % len(kgrid)) 

1727 for k in kgrid: 

1728 pwi.append('{k[0]:.14f} {k[1]:.14f} {k[2]:.14f} 0\n'.format(k=k)) 

1729 pwi.append('\n') 

1730 elif isinstance(kgrid, str) and (kgrid == "gamma"): 

1731 pwi.append('K_POINTS gamma\n') 

1732 pwi.append('\n') 

1733 else: 

1734 pwi.append('K_POINTS automatic\n') 

1735 pwi.append('{0[0]} {0[1]} {0[2]} {1[0]:d} {1[1]:d} {1[2]:d}\n' 

1736 ''.format(kgrid, koffset)) 

1737 pwi.append('\n') 

1738 

1739 # CELL block, if required 

1740 if input_parameters['SYSTEM']['ibrav'] == 0: 

1741 pwi.append('CELL_PARAMETERS angstrom\n') 

1742 pwi.append('{cell[0][0]:.14f} {cell[0][1]:.14f} {cell[0][2]:.14f}\n' 

1743 '{cell[1][0]:.14f} {cell[1][1]:.14f} {cell[1][2]:.14f}\n' 

1744 '{cell[2][0]:.14f} {cell[2][1]:.14f} {cell[2][2]:.14f}\n' 

1745 ''.format(cell=atoms.cell)) 

1746 pwi.append('\n') 

1747 

1748 # Positions - already constructed, but must appear after namelist 

1749 if crystal_coordinates: 

1750 pwi.append('ATOMIC_POSITIONS crystal\n') 

1751 else: 

1752 pwi.append('ATOMIC_POSITIONS angstrom\n') 

1753 pwi.extend(atomic_positions_str) 

1754 pwi.append('\n') 

1755 

1756 # DONE! 

1757 fd.write(''.join(pwi))