Coverage for ase / calculators / vasp / create_input.py: 76.38%

618 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-04 10:20 +0000

1# fmt: off 

2 

3# Copyright (C) 2008 CSC - Scientific Computing Ltd. 

4"""This module defines an ASE interface to VASP. 

5 

6Developed on the basis of modules by Jussi Enkovaara and John Kitchin. 

7The pseudpotentials provided by VASP should be stored in a single directory 

8that is defined by the environment variable VASP_PP_PATH. The VASP_PP_PATH 

9should contain subdirectories of the form potpaw_LDA(.version) and 

10potpaw_PBE(.version). The version of the pseudopotentials can be specified 

11with the environment variable VASP_PP_VERSION or by the calculator input 

12parameter 'pp_version'. If neither is set, the default is to use the 

13unversioned directories to maintain backwards compatability. 

14 

15The user should also set the environmental flag $VASP_SCRIPT pointing 

16to a python script looking something like:: 

17 

18 import os 

19 exitcode = os.system('vasp') 

20 

21Alternatively, user can set the environmental flag $VASP_COMMAND pointing 

22to the command use the launch vasp e.g. 'vasp' or 'mpirun -n 16 vasp' 

23 

24https://www.vasp.at/ 

25""" 

26 

27import os 

28import re 

29import shutil 

30import warnings 

31from os.path import join 

32from typing import Any, List, Sequence, TextIO, Tuple, Union 

33 

34import numpy as np 

35 

36import ase 

37from ase import Atoms 

38from ase.calculators.calculator import kpts2ndarray 

39from ase.calculators.vasp.setups import get_default_setups 

40from ase.config import cfg 

41from ase.io.vasp_parsers.incar_writer import write_incar 

42 

43FLOAT_FORMAT = '5.6f' 

44EXP_FORMAT = '5.2e' 

45 

46 

47def check_ichain(ichain, ediffg, iopt): 

48 ichain_dct = {} 

49 if ichain > 0: 

50 ichain_dct['ibrion'] = 3 

51 ichain_dct['potim'] = 0.0 

52 if iopt is None: 

53 warnings.warn( 

54 'WARNING: optimization is set to LFBGS (IOPT = 1)') 

55 ichain_dct['iopt'] = 1 

56 if ediffg is None or float(ediffg > 0.0): 

57 raise RuntimeError('Please set EDIFFG < 0') 

58 return ichain_dct 

59 

60 

61def set_magmom(ispin, spinpol, atoms, magmom_input, sorting): 

62 """Helps to set the magmom tag in the INCAR file with correct formatting""" 

63 magmom_dct = {} 

64 if magmom_input is not None: 

65 if len(magmom_input) != len(atoms): 

66 msg = ('Expected length of magmom tag to be' 

67 ' {}, i.e. 1 value per atom, but got {}').format( 

68 len(atoms), len(magmom_input)) 

69 raise ValueError(msg) 

70 

71 # Check if user remembered to specify ispin 

72 # note: we do not overwrite ispin if ispin=1 

73 if not ispin: 

74 spinpol = True 

75 # note that ispin is an int key, but for the INCAR it does not 

76 # matter 

77 magmom_dct['ispin'] = 2 

78 magmom = np.array(magmom_input) 

79 magmom = magmom[sorting] 

80 elif (spinpol and atoms.get_initial_magnetic_moments().any()): 

81 # We don't want to write magmoms if they are all 0. 

82 # but we could still be doing a spinpol calculation 

83 if not ispin: 

84 magmom_dct['ispin'] = 2 

85 # Write out initial magnetic moments 

86 magmom = atoms.get_initial_magnetic_moments()[sorting] 

87 # unpack magmom array if three components specified 

88 if magmom.ndim > 1: 

89 magmom = [item for sublist in magmom for item in sublist] 

90 else: 

91 return spinpol, {} 

92 # Compactify the magmom list to symbol order 

93 lst = [[1, magmom[0]]] 

94 for n in range(1, len(magmom)): 

95 if magmom[n] == magmom[n - 1]: 

96 lst[-1][0] += 1 

97 else: 

98 lst.append([1, magmom[n]]) 

99 line = ' '.join(['{:d}*{:.4f}'.format(mom[0], mom[1]) 

100 for mom in lst]) 

101 magmom_dct['magmom'] = line 

102 return spinpol, magmom_dct 

103 

104 

105def set_ldau(ldau_param, luj_params, symbol_count): 

106 """Helps to set the ldau tag in the INCAR file with correct formatting""" 

107 ldau_dct = {} 

108 if ldau_param is None: 

109 ldau_dct['ldau'] = '.TRUE.' 

110 llist = [] 

111 ulist = [] 

112 jlist = [] 

113 for symbol in symbol_count: 

114 # default: No +U 

115 luj = luj_params.get( 

116 symbol[0], 

117 {'L': -1, 'U': 0.0, 'J': 0.0} 

118 ) 

119 llist.append(int(luj['L'])) 

120 ulist.append(f'{luj["U"]:{".3f"}}') 

121 jlist.append(f'{luj["J"]:{".3f"}}') 

122 ldau_dct['ldaul'] = llist 

123 ldau_dct['ldauu'] = ulist 

124 ldau_dct['ldauj'] = jlist 

125 return ldau_dct 

126 

127 

128def _calc_nelect_from_charge( 

129 nelect: Union[float, None], 

130 charge: Union[float, None], 

131 nelect_from_ppp: float, 

132) -> Union[float, None]: 

133 """Determine nelect resulting from a given charge if charge != 0.0. 

134 

135 If nelect is additionally given explicitly, then we need to determine it 

136 even for net charge of 0 to check for conflicts. 

137 

138 """ 

139 if charge is not None and charge != 0.0: 

140 nelect_from_charge = nelect_from_ppp - charge 

141 if nelect and nelect != nelect_from_charge: 

142 raise ValueError( 

143 'incompatible input parameters: ' 

144 f'nelect={nelect}, but charge={charge} ' 

145 f'(neutral nelect is {nelect_from_ppp})' 

146 ) 

147 return nelect_from_charge 

148 return nelect # NELECT explicitly given in INCAR (`None` if not given) 

149 

150 

151def get_pp_setup(setup) -> Tuple[dict, Sequence[int]]: 

152 """ 

153 Get the pseudopotential mapping based on the "setpus" input. 

154 

155 Parameters 

156 ---------- 

157 setup : [str, dict] 

158 The setup to use for the calculation. This can be a string 

159 shortcut, or a dict of atom identities and suffixes. 

160 In the dict version it is also possible to select a base setup 

161 e.g.: {'base': 'minimal', 'Ca': '_sv', 2: 'O_s'} 

162 If the key is an integer, this means an atom index. 

163 For the string version, 'minimal', 'recommended' and 'GW' are 

164 available. The default is 'minimal 

165 

166 Returns 

167 ------- 

168 setups : dict 

169 The setup dictionary, with atom indices as keys and suffixes 

170 as values. 

171 special_setups : list 

172 A list of atom indices that have a special setup. 

173 """ 

174 special_setups = [] 

175 

176 # Avoid mutating the module dictionary, so we use a copy instead 

177 # Note, it is a nested dict, so a regular copy is not enough 

178 setups_defaults = get_default_setups() 

179 

180 # Default to minimal basis 

181 if setup is None: 

182 setup = {'base': 'minimal'} 

183 

184 # String shortcuts are initialised to dict form 

185 elif isinstance(setup, str): 

186 if setup.lower() in setups_defaults.keys(): 

187 setup = {'base': setup} 

188 

189 # Dict form is then queried to add defaults from setups.py. 

190 if 'base' in setup: 

191 setups = setups_defaults[setup['base'].lower()] 

192 else: 

193 setups = {} 

194 

195 # Override defaults with user-defined setups 

196 if setup is not None: 

197 setups.update(setup) 

198 

199 for m in setups: 

200 try: 

201 special_setups.append(int(m)) 

202 except ValueError: 

203 pass 

204 return setups, special_setups 

205 

206 

207def format_kpoints(kpts, atoms, reciprocal=False, gamma=False): 

208 tokens = [] 

209 append = tokens.append 

210 

211 append('KPOINTS created by Atomic Simulation Environment\n') 

212 

213 if isinstance(kpts, dict): 

214 kpts = kpts2ndarray(kpts, atoms=atoms) 

215 reciprocal = True 

216 

217 shape = np.array(kpts).shape 

218 

219 # Wrap scalar in list if necessary 

220 if shape == (): 

221 kpts = [kpts] 

222 shape = (1, ) 

223 

224 if len(shape) == 1: 

225 append('0\n') 

226 if shape == (1, ): 

227 append('Auto\n') 

228 elif gamma: 

229 append('Gamma\n') 

230 else: 

231 append('Monkhorst-Pack\n') 

232 append(' '.join(f'{kpt:d}' for kpt in kpts)) 

233 append('\n0 0 0\n') 

234 elif len(shape) == 2: 

235 append('%i \n' % (len(kpts))) 

236 if reciprocal: 

237 append('Reciprocal\n') 

238 else: 

239 append('Cartesian\n') 

240 for n in range(len(kpts)): 

241 [append('%f ' % kpt) for kpt in kpts[n]] 

242 if shape[1] == 4: 

243 append('\n') 

244 elif shape[1] == 3: 

245 append('1.0 \n') 

246 return ''.join(tokens) 

247 

248 

249# Parameters that can be set in INCAR. The values which are None 

250# are not written and default parameters of VASP are used for them. 

251 

252float_keys = [ 

253 'aexx', # Fraction of exact/DFT exchange 

254 'aggac', # Fraction of gradient correction to correlation 

255 'aggax', # Fraction of gradient correction to exchange 

256 'aldac', # Fraction of LDA correlation energy 

257 'amggac', # parameter that multiplies the meta-GGA correlation functional 

258 'amggax', # parameter that multiplies the meta-GGA exchange functional 

259 'amin', # minimal mixing parameter in Kerker's initial approximatio 

260 'amix', # linear mixing parameter 

261 'amix_mag', # linear mixing parameter for the magnetization density 

262 'bmix', # tags for mixing 

263 'bmix_mag', # 

264 'cshift', # Complex shift for dielectric tensor calculation (LOPTICS) 

265 'deper', # relative stopping criterion for optimization of eigenvalue 

266 'ebreak', # absolute stopping criterion for optimization of eigenvalues 

267 # (EDIFF/N-BANDS/4) 

268 'efield', # applied electrostatic field 

269 'emax', # energy-range for DOSCAR file 

270 'emin', # 

271 'enaug', # Density cutoff 

272 'encut', # Planewave cutoff 

273 'encutgw', # energy cutoff for response function 

274 'encutfock', # FFT grid in the HF related routines 

275 'hfscreen', # attribute to change from PBE0 to HSE 

276 'kspacing', # determines the number of k-points if the KPOINTS 

277 # file is not present. KSPACING is the smallest 

278 # allowed spacing between k-points in units of 

279 # $\AA$^{-1}$. 

280 'potim', # time-step for ion-motion (fs) 

281 'nelect', # total number of electrons 

282 'param1', # Exchange parameter 

283 'param2', # Exchange parameter 

284 'pomass', # mass of ions in am 

285 'pstress', # add this stress to the stress tensor, and energy E = V * 

286 # pstress 

287 'sigma', # broadening in eV 

288 'smass', # Nose mass-parameter (am) 

289 'spring', # spring constant for NEB 

290 'time', # special control tag 

291 'weimin', # maximum weight for a band to be considered empty 

292 'zab_vdw', # vdW-DF parameter 

293 'zval', # ionic valence 

294 # The next keywords pertain to the VTST add-ons from Graeme Henkelman's 

295 # group at UT Austin 

296 'jacobian', # Weight of lattice to atomic motion 

297 'ddr', # (DdR) dimer separation 

298 'drotmax', # (DRotMax) number of rotation steps per translation step 

299 'dfnmin', # (DFNMin) rotational force below which dimer is not rotated 

300 'dfnmax', # (DFNMax) rotational force below which dimer rotation stops 

301 'sltol', # convergence ratio for minimum eigenvalue 

302 'sdr', # finite difference for setting up Lanczos matrix and step 

303 # size when translating 

304 'maxmove', # Max step for translation for IOPT > 0 

305 'invcurv', # Initial curvature for LBFGS (IOPT = 1) 

306 'timestep', # Dynamical timestep for IOPT = 3 and IOPT = 7 

307 'sdalpha', # Ratio between force and step size for IOPT = 4 

308 # The next keywords pertain to IOPT = 7 (i.e. FIRE) 

309 'ftimemax', # Max time step 

310 'ftimedec', # Factor to dec. dt 

311 'ftimeinc', # Factor to inc. dt 

312 'falpha', # Parameter for velocity damping 

313 'falphadec', # Factor to dec. alpha 

314 'clz', # electron count for core level shift 

315 'vdw_radius', # Cutoff radius for Grimme's DFT-D2 and DFT-D3 and 

316 # Tkatchenko and Scheffler's DFT-TS dispersion corrections 

317 'vdw_scaling', # Global scaling parameter for Grimme's DFT-D2 dispersion 

318 # correction 

319 'vdw_d', # Global damping parameter for Grimme's DFT-D2 and Tkatchenko 

320 # and Scheffler's DFT-TS dispersion corrections 

321 'vdw_cnradius', # Cutoff radius for calculating coordination number in 

322 # Grimme's DFT-D3 dispersion correction 

323 'vdw_s6', # Damping parameter for Grimme's DFT-D2 and DFT-D3 and 

324 # Tkatchenko and Scheffler's DFT-TS dispersion corrections 

325 'vdw_s8', # Damping parameter for Grimme's DFT-D3 dispersion correction 

326 'vdw_sr', # Scaling parameter for Grimme's DFT-D2 and DFT-D3 and 

327 # Tkatchenko and Scheffler's DFT-TS dispersion correction 

328 'vdw_a1', # Damping parameter for Grimme's DFT-D3 dispersion correction 

329 'vdw_a2', # Damping parameter for Grimme's DFT-D3 dispersion correction 

330 'eb_k', # solvent permitivity in Vaspsol 

331 'tau', # surface tension parameter in Vaspsol 

332 'langevin_gamma_l', # Friction for lattice degrees of freedom 

333 'pmass', # Mass for latice degrees of freedom 

334 'bparam', # B parameter for nonlocal VV10 vdW functional 

335 'cparam', # C parameter for nonlocal VV10 vdW functional 

336 'aldax', # Fraction of LDA exchange (for hybrid calculations) 

337 'tebeg', # 

338 'teend', # temperature during run 

339 'andersen_prob', # Probability of collision in Andersen thermostat 

340 'apaco', # Distance cutoff for pair correlation function calc. 

341 'auger_ecblo', # Undocumented parameter for Auger calculations 

342 'auger_edens', # Density of electrons in conduction band 

343 'auger_hdens', # Density of holes in valence band 

344 'auger_efermi', # Fixed Fermi level for Auger calculations 

345 'auger_evbhi', # Upper bound for valence band maximum 

346 'auger_ewidth', # Half-width of energy window function 

347 'auger_occ_fac_eeh', # Undocumented parameter for Auger calculations 

348 'auger_occ_fac_ehh', # Undocumented parameter for Auger calculations 

349 'auger_temp', # Temperature for Auger calculation 

350 'dq', # Finite difference displacement magnitude (NMR) 

351 'avgap', # Average gap (Model GW) 

352 'ch_sigma', # Broadening of the core electron absorption spectrum 

353 'bpotim', # Undocumented Bond-Boost parameter (GH patches) 

354 'qrr', # Undocumented Bond-Boost parameter (GH patches) 

355 'prr', # Undocumented Bond-Boost parameter (GH patches) 

356 'rcut', # Undocumented Bond-Boost parameter (GH patches) 

357 'dvmax', # Undocumented Bond-Boost parameter (GH patches) 

358 'bfgsinvcurv', # Initial curvature for BFGS (GH patches) 

359 'damping', # Damping parameter for LBFGS (GH patches) 

360 'efirst', # Energy of first NEB image (GH patches) 

361 'elast', # Energy of final NEB image (GH patches) 

362 'fmagval', # Force magnitude convergence criterion (GH patches) 

363 'cmbj', # Modified Becke-Johnson MetaGGA c-parameter 

364 'cmbja', # Modified Becke-Johnson MetaGGA alpha-parameter 

365 'cmbjb', # Modified Becke-Johnson MetaGGA beta-parameter 

366 'sigma_nc_k', # Width of ion gaussians (VASPsol) 

367 'sigma_k', # Width of dielectric cavidty (VASPsol) 

368 'nc_k', # Cavity turn-on density (VASPsol) 

369 'lambda_d_k', # Debye screening length (VASPsol) 

370 'ediffsol', # Tolerance for solvation convergence (VASPsol) 

371 'soltemp', # Solvent temperature for isol 2 in Vaspsol++ 

372 'a_k', # Smoothing length for FFT for isol 2 in Vaspsol++ 

373 'r_cav', # Offset for solute surface area for isol 2 in Vaspsol++ 

374 'epsilon_inf', # Bulk optical dielectric for isol 2 in Vaspsol++ 

375 'n_mol', # Solvent density for isol 2 in Vaspsol++ 

376 'p_mol', # Solvent dipole moment for isol 2 in Vaspsol++ 

377 'r_solv', # Solvent radius for isol 2 in Vaspsol++ 

378 'r_diel', # Dielectric radius for isol 2 in Vaspsol++ 

379 'r_b', # Bound charge smearing length for isol 2 in Vaspsol++ 

380 'c_molar', # Electrolyte concentration for isol 2 in Vaspsol++ 

381 'zion', # Electrolyte ion valency for isol 2 in Vaspsol++ 

382 'd_ion', # Packing diameter of electrolyte ions for isol 2 in Vaspsol++ 

383 'r_ion', # Ionic radius of electrolyte ions for isol 2 in Vaspsol++ 

384 'efermi_ref', # Potential vs vacuum for isol 2 in Vaspsol++ 

385 'capacitance_init', # Initial guess for isol 2 in Vaspsol++ 

386 'deg_threshold', # Degeneracy threshold 

387 'omegamin', # Minimum frequency for dense freq. grid 

388 'omegamax', # Maximum frequency for dense freq. grid 

389 'rtime', # Undocumented parameter 

390 'wplasma', # Undocumented parameter 

391 'wplasmai', # Undocumented parameter 

392 'dfield', # Undocumented parameter 

393 'omegatl', # Maximum frequency for coarse freq. grid 

394 'encutgwsoft', # Soft energy cutoff for response kernel 

395 'encutlf', # Undocumented parameter 

396 'scissor', # Scissor correction for GW/BSE calcs 

397 'dimer_dist', # Distance between dimer images 

398 'step_size', # Step size for finite difference in dimer calculation 

399 'step_max', # Maximum step size for dimer calculation 

400 'minrot', # Minimum rotation allowed in dimer calculation 

401 'dummy_mass', # Mass of dummy atom(s?) 

402 'shaketol', # Tolerance for SHAKE algorithm 

403 'shaketolsoft', # Soft tolerance for SHAKE algorithm 

404 'shakesca', # Scaling of each step taken in SHAKE algorithm 

405 'hills_stride', # Undocumented metadynamics parameter 

406 'hills_h', # Height (in eV) of gaussian bias for metadynamics 

407 'hills_w', # Width of gaussian bias for metadynamics 

408 'hills_k', # Force constant coupling dummy&real for metadynamics 

409 'hills_m', # Mass of dummy particle for use in metadynamics 

410 'hills_temperature', # Temp. of dummy particle for metadynamics 

411 'hills_andersen_prob', # Probability of thermostat coll. for metadynamics 

412 'hills_sqq', # Nose-hoover particle mass for metadynamics 

413 'dvvdelta0', # Undocumented parameter 

414 'dvvvnorm0', # Undocumented parameter 

415 'dvvminpotim', # Undocumented parameter 

416 'dvvmaxpotim', # Undocumented parameter 

417 'enchg', # Undocumented charge fitting parameter 

418 'tau0', # Undocumented charge fitting parameter 

419 'encut4o', # Cutoff energy for 4-center integrals (HF) 

420 'param3', # Undocumented HF parameter 

421 'model_eps0', # Undocumented HF parameter 

422 'model_alpha', # Undocumented HF parameter 

423 'qmaxfockae', # Undocumented HF parameter 

424 'hfscreenc', # Range-separated screening length for correlations 

425 'hfrcut', # Cutoff radius for HF potential kernel 

426 'encutae', # Undocumented parameter for all-electron density calc. 

427 'encutsubrotscf', # Undocumented subspace rotation SCF parameter 

428 'enini', # Cutoff energy for wavefunctions (?) 

429 'wc', # Undocumented mixing parameter 

430 'enmax', # Cutoff energy for wavefunctions (?) 

431 'scalee', # Undocumented parameter 

432 'eref', # Reference energy 

433 'epsilon', # Dielectric constant of bulk charged cells 

434 'rcmix', # Mixing parameter for core density in rel. core calcs. 

435 'esemicore', # Energetic lower bound for states considered "semicore" 

436 'external_pressure', # Pressure for NPT calcs., equivalent to PSTRESS 

437 'lj_radius', # Undocumented classical vdW parameter 

438 'lj_epsilon', # Undocumented classical vdW parameter 

439 'lj_sigma', # Undocumented classical vdW parameter 

440 'mbd_beta', # TS MBD vdW correction damping parameter 

441 'scsrad', # Cutoff radius for dipole-dipole interaction tensor in SCS 

442 'hitoler', # Iterative Hirschfeld partitioning tolerance 

443 'lambda', # "Spring constant" for magmom constraint calcs. 

444 'kproj_threshold', # Threshold for k-point projection scheme 

445 'maxpwamp', # Undocumented HF parameter 

446 'vcutoff', # Undocumented parameter 

447 'mdtemp', # Temperature for AIMD 

448 'mdgamma', # Undocumented AIMD parameter 

449 'mdalpha', # Undocumented AIMD parameter 

450 'ofield_kappa', # Bias potential strength for interface pinning method 

451 'ofield_q6_near', # Steinhardt-Nelson Q6 parameters for interface pinning 

452 'ofield_q6_far', # Steinhardt-Nelson Q6 parameters for interface pinning 

453 'ofield_a', # Target order parameter for interface pinning method 

454 'pthreshold', # Don't print timings for routines faster than this value 

455 'qltol', # Eigenvalue tolerance for Lanczos iteration (instanton) 

456 'qdr', # Step size for building Lanczos matrix & CG (instanton) 

457 'qmaxmove', # Max step size (instanton) 

458 'qdt', # Timestep for quickmin minimization (instanton) 

459 'qtpz', # Temperature (instanton) 

460 'qftol', # Tolerance (instanton) 

461 'nupdown', # fix spin moment to specified value 

462] 

463 

464exp_keys = [ 

465 'ediff', # stopping-criterion for electronic upd. 

466 'ediffg', # stopping-criterion for ionic upd. 

467 'symprec', # precession in symmetry routines 

468 # The next keywords pertain to the VTST add-ons from Graeme Henkelman's 

469 # group at UT Austin 

470 'fdstep', # Finite diference step for IOPT = 1 or 2 

471] 

472 

473string_keys = [ 

474 'algo', # algorithm: Normal (Davidson) | Fast | Very_Fast (RMM-DIIS) 

475 'bandgap', # determines the verbosity for reporting the bandgap info 

476 'bseprec', # precision of the time-evolution algorithm 

477 'gga', # xc-type: PW PB LM or 91 (LDA if not set) 

478 'metagga', # 

479 'prec', # Precission of calculation (Low, Normal, Accurate) 

480 'system', # name of System 

481 'precfock', # FFT grid in the HF related routines 

482 'radeq', # Which type of radial equations to use for rel. core calcs. 

483 'localized_basis', # Basis to use in CRPA 

484 'proutine', # Select profiling routine 

485 'efermi', # Sets the FERMI level in VASP 6.4.0+ 

486] 

487 

488int_keys = [ 

489 'ialgo', # algorithm: use only 8 (CG) or 48 (RMM-DIIS) 

490 'ibrion', # ionic relaxation: 0-MD 1-quasi-New 2-CG 

491 'icharg', # charge: 0-WAVECAR 1-CHGCAR 2-atom 10-const 

492 'idipol', # monopol/dipol and quadropole corrections 

493 'images', # number of images for NEB calculation 

494 'imix', # specifies density mixing 

495 'iniwav', # initial electr wf. : 0-lowe 1-rand 

496 'isif', # calculate stress and what to relax 

497 'ismear', # part. occupancies: -5 Blochl -4-tet -1-fermi 0-gaus >0 MP 

498 'isearch', # line-search algorithm for ALGO = All 

499 'ispin', # spin-polarized calculation 

500 'istart', # startjob: 0-new 1-cont 2-samecut 

501 'isym', # symmetry: 0-nonsym 1-usesym 2-usePAWsym 

502 'iwavpr', # prediction of wf.: 0-non 1-charg 2-wave 3-comb 

503 'kpar', # k-point parallelization paramater 

504 'ldauprint', # 0-silent, 1-occ. matrix written to OUTCAR, 2-1+pot. matrix 

505 # written 

506 'ldautype', # L(S)DA+U: 1-Liechtenstein 2-Dudarev 4-Liechtenstein(LDAU) 

507 'lmaxmix', # 

508 'lorbit', # create PROOUT 

509 'maxmix', # 

510 'ngx', # FFT mesh for wavefunctions, x 

511 'ngxf', # FFT mesh for charges x 

512 'ngy', # FFT mesh for wavefunctions, y 

513 'ngyf', # FFT mesh for charges y 

514 'ngz', # FFT mesh for wavefunctions, z 

515 'ngzf', # FFT mesh for charges z 

516 'nbands', # Number of bands 

517 'nblk', # blocking for some BLAS calls (Sec. 6.5) 

518 'nbmod', # specifies mode for partial charge calculation 

519 'nelm', # nr. of electronic steps (default 60) 

520 'nelmdl', # nr. of initial electronic steps 

521 'nelmgw', # nr. of self-consistency cycles for GW 

522 'nelmin', 

523 'nfree', # number of steps per DOF when calculting Hessian using 

524 # finite differences 

525 'nkred', # define sub grid of q-points for HF with 

526 # nkredx=nkredy=nkredz 

527 'nkredx', # define sub grid of q-points in x direction for HF 

528 'nkredy', # define sub grid of q-points in y direction for HF 

529 'nkredz', # define sub grid of q-points in z direction for HF 

530 'nomega', # number of frequency points 

531 'nomegar', # number of frequency points on real axis 

532 'npar', # parallelization over bands 

533 'nsim', # evaluate NSIM bands simultaneously if using RMM-DIIS 

534 'nsw', # number of steps for ionic upd. 

535 'nwrite', # verbosity write-flag (how much is written) 

536 'vdwgr', # extra keyword for Andris program 

537 'vdwrn', # extra keyword for Andris program 

538 'voskown', # use Vosko, Wilk, Nusair interpolation 

539 # The next keywords pertain to the VTST add-ons from Graeme Henkelman's 

540 # group at UT Austin 

541 'ichain', # Flag for controlling which method is being used (0=NEB, 

542 # 1=DynMat, 2=Dimer, 3=Lanczos) if ichain > 3, then both 

543 # IBRION and POTIM are automatically set in the INCAR file 

544 'iopt', # Controls which optimizer to use. for iopt > 0, ibrion = 3 

545 # and potim = 0.0 

546 'snl', # Maximum dimentionality of the Lanczos matrix 

547 'lbfgsmem', # Steps saved for inverse Hessian for IOPT = 1 (LBFGS) 

548 'fnmin', # Max iter. before adjusting dt and alpha for IOPT = 7 (FIRE) 

549 'icorelevel', # core level shifts 

550 'clnt', # species index 

551 'cln', # main quantum number of excited core electron 

552 'cll', # l quantum number of excited core electron 

553 'ivdw', # Choose which dispersion correction method to use 

554 'nbandsgw', # Number of bands for GW 

555 'nbandso', # Number of occupied bands for electron-hole treatment 

556 'nbandsv', # Number of virtual bands for electron-hole treatment 

557 'ncore', # Number of cores per band, equal to number of cores divided 

558 # by npar 

559 'mdalgo', # Determines which MD method of Tomas Bucko to use 

560 'nedos', # Number of grid points in DOS 

561 'turbo', # Ewald, 0 = Normal, 1 = PME 

562 'omegapar', # Number of groups for response function calc. 

563 # (Possibly Depricated) Number of groups in real time for 

564 # response function calc. 

565 'taupar', 

566 'ntaupar', # Number of groups in real time for response function calc. 

567 'antires', # How to treat antiresonant part of response function 

568 'magatom', # Index of atom at which to place magnetic field (NMR) 

569 'jatom', # Index of atom at which magnetic moment is evaluated (NMR) 

570 'ichibare', # chi_bare stencil size (NMR) 

571 'nbas', # Undocumented Bond-Boost parameter (GH patches) 

572 'rmds', # Undocumented Bond-Boost parameter (GH patches) 

573 'ilbfgsmem', # Number of histories to store for LBFGS (GH patches) 

574 'vcaimages', # Undocumented parameter (GH patches) 

575 'ntemper', # Undocumented subspace diagonalization param. (GH patches) 

576 'ncshmem', # Share memory between this many cores on each process 

577 'lmaxtau', # Undocumented MetaGGA parameter (prob. max ang.mom. for tau) 

578 'kinter', # Additional finer grid (?) 

579 'ibse', # Type of BSE calculation 

580 'nbseeig', # Number of BSE wfns to write 

581 'naturalo', # Use NATURALO (?) 

582 'nbandsexact', # Undocumented parameter 

583 'nbandsgwlow', # Number of bands for which shifts are calculated 

584 'nbandslf', # Number of bands included in local field effect calc. 

585 'omegagrid', # Undocumented parameter 

586 'telescope', # Undocumented parameter 

587 'maxmem', # Amount of memory to allocate per core in MB 

588 'nelmhf', # Number of iterations for HF part (GW) 

589 'dim', # Undocumented parameter 

590 'nkredlf', # Reduce k-points for local field effects 

591 'nkredlfx', # Reduce k-points for local field effects in X 

592 'nkredlfy', # Reduce k-points for local field effects in Y 

593 'nkredlfz', # Reduce k-points for local field effects in Z 

594 'lmaxmp2', # Undocumented parameter 

595 'switch', # Undocumented dimer parameter 

596 'findiff', # Use forward (1) or central (2) finite difference for dimer 

597 'engine', # Undocumented dimer parameter 

598 'restartcg', # Undocumented dimer parameter 

599 'thermostat', # Deprecated parameter for selecting MD method (use MDALGO) 

600 'scaling', # After how many steps velocities should be rescaled 

601 'shakemaxiter', # Maximum # of iterations in SHAKE algorithm 

602 'equi_regime', # Number of steps to equilibrate for 

603 'hills_bin', # Update metadynamics bias after this many steps 

604 'hills_maxstride', # Undocumented metadynamics parameter 

605 'dvvehistory', # Undocumented parameter 

606 'ipead', # Undocumented parameter 

607 'ngaus', # Undocumented charge fitting parameter 

608 'exxoep', # Undocumented HF parameter 

609 'fourorbit', # Undocumented HF parameter 

610 'model_gw', # Undocumented HF parameter 

611 'hflmax', # Maximum L quantum number for HF calculation 

612 'lmaxfock', # Maximum L quantum number for HF calc. (same as above) 

613 'lmaxfockae', # Undocumented HF parameter 

614 'nmaxfockae', # Undocumented HF parameter 

615 'nblock_fock', # Undocumented HF parameter 

616 'idiot', # Determines which warnings/errors to print 

617 'nrmm', # Number of RMM-DIIS iterations 

618 'mremove', # Undocumented mixing parameter 

619 'inimix', # Undocumented mixing parameter 

620 'mixpre', # Undocumented mixing parameter 

621 'nelmall', # Undocumented parameter 

622 'nblock', # How frequently to write data 

623 'kblock', # How frequently to write data 

624 'npaco', # Undocumented pair correlation function parameter 

625 'lmaxpaw', # Max L quantum number for on-site charge expansion 

626 'irestart', # Undocumented parameter 

627 'nreboot', # Undocumented parameter 

628 'nmin', # Undocumented parameter 

629 'nlspline', # Undocumented parameter 

630 'ispecial', # "Select undocumented and unsupported special features" 

631 'rcrep', # Number of steps between printing relaxed core info 

632 'rcndl', # Wait this many steps before updating core density 

633 'rcstrd', # Relax core density after this many SCF steps 

634 'vdw_idampf', # Select type of damping function for TS vdW 

635 'i_constrained_m', # Select type of magmom. constraint to use 

636 'igpar', # "G parallel" direction for Berry phase calculation 

637 'nppstr', # Number of kpts in "igpar' direction for Berry phase calc. 

638 'nbands_out', # Undocumented QP parameter 

639 'kpts_out', # Undocumented QP parameter 

640 'isp_out', # Undocumented QP parameter 

641 'nomega_out', # Undocumented QP parameter 

642 'maxiter_ft', # Max iterations for sloppy Remez algorithm 

643 'nmaxalt', # Max sample points for alternant in Remez algorithms 

644 'itmaxlsq', # Max iterations in LSQ search algorithm 

645 'ndatalsq', # Number of sample points for LSQ search algorithm 

646 'ncore_in_image1', # Undocumented parameter 

647 'kimages', # Undocumented parameter 

648 'ncores_per_band', # Undocumented parameter 

649 'maxlie', # Max iterations in CRPA diagonalization routine 

650 'ncrpalow', # Undocumented CRPA parameter 

651 'ncrpahigh', # Undocumented CRPA parameter 

652 'nwlow', # Undocumented parameter 

653 'nwhigh', # Undocumented parameter 

654 'nkopt', # Number of k-points to include in Optics calculation 

655 'nkoffopt', # K-point "counter offset" for Optics 

656 'nbvalopt', # Number of valence bands to write in OPTICS file 

657 'nbconopt', # Number of conduction bands to write in OPTICS file 

658 'ch_nedos', # Number dielectric function calculation grid points for XAS 

659 'plevel', # No timings for routines with "level" higher than this 

660 'qnl', # Lanczos matrix size (instanton) 

661 'isol', # vaspsol++ flag 1 linear, 2 nonlinear 

662] 

663 

664bool_keys = [ 

665 'addgrid', # finer grid for augmentation charge density 

666 'kgamma', # The generated kpoint grid (from KSPACING) is either 

667 # centred at the $\Gamma$ 

668 # point (e.g. includes the $\Gamma$ point) 

669 # (KGAMMA=.TRUE.) 

670 'laechg', # write AECCAR0/AECCAR1/AECCAR2 

671 'lasph', # non-spherical contributions to XC energy (and pot for 

672 # VASP.5.X) 

673 'lasync', # overlap communcation with calculations 

674 'lcharg', # 

675 'lcorr', # Harris-correction to forces 

676 'ldau', # L(S)DA+U 

677 'ldiag', # algorithm: perform sub space rotation 

678 'ldipol', # potential correction mode 

679 'lelf', # create ELFCAR 

680 'lepsilon', # enables to calculate and to print the BEC tensors 

681 'lhfcalc', # switch to turn on Hartree Fock calculations 

682 'loptics', # calculate the frequency dependent dielectric matrix 

683 'lpard', # evaluate partial (band and/or k-point) decomposed charge 

684 # density 

685 'lplane', # parallelisation over the FFT grid 

686 'lscalapack', # switch off scaLAPACK 

687 'lscalu', # switch of LU decomposition 

688 'lsepb', # write out partial charge of each band separately? 

689 'lsepk', # write out partial charge of each k-point separately? 

690 'lthomas', # 

691 'luse_vdw', # Invoke vdW-DF implementation by Klimes et. al 

692 'lvdw', # Invoke DFT-D2 method of Grimme 

693 'lvhar', # write Hartree potential to LOCPOT (vasp 5.x) 

694 'lvtot', # create WAVECAR/CHGCAR/LOCPOT 

695 'lwave', # 

696 # The next keywords pertain to the VTST add-ons from Graeme Henkelman's 

697 # group at UT Austin 

698 'lclimb', # Turn on CI-NEB 

699 'ltangentold', # Old central difference tangent 

700 'ldneb', # Turn on modified double nudging 

701 'lnebcell', # Turn on SS-NEB 

702 'lglobal', # Optmize NEB globally for LBFGS (IOPT = 1) 

703 'llineopt', # Use force based line minimizer for translation (IOPT = 1) 

704 'lbeefens', # Switch on print of BEE energy contributions in OUTCAR 

705 'lbeefbas', # Switch off print of all BEEs in OUTCAR 

706 'lcalcpol', # macroscopic polarization (vasp5.2). 'lcalceps' 

707 'lcalceps', # Macroscopic dielectric properties and Born effective charge 

708 # tensors (vasp 5.2) 

709 'lvdw', # Turns on dispersion correction 

710 'lvdw_ewald', # Turns on Ewald summation for Grimme's DFT-D2 and 

711 # Tkatchenko and Scheffler's DFT-TS dispersion correction 

712 'lspectral', # Use the spectral method to calculate independent particle 

713 # polarizability 

714 'lrpa', # Include local field effects on the Hartree level only 

715 'lwannier90', # Switches on the interface between VASP and WANNIER90 

716 'lsorbit', # Enable spin-orbit coupling 

717 'lsol', # turn on solvation for Vaspsol 

718 'lnldiel', # turn on nonlinear dielectric in Vaspsol++ 

719 'lnlion', # turn on nonlinear ionic in Vaspsol++ 

720 'lsol_scf', # turn on solvation in SCF cycle in Vaspsol++ 

721 'lautoscale', # automatically calculate inverse curvature for VTST LBFGS 

722 'interactive', # Enables interactive calculation for VaspInteractive 

723 'lauger', # Perform Auger calculation (Auger) 

724 'lauger_eeh', # Calculate EEH processes (Auger) 

725 'lauger_ehh', # Calculate EHH processes (Auger) 

726 'lauger_collect', # Collect wfns before looping over k-points (Auger) 

727 'lauger_dhdk', # Auto-determine E. window width from E. derivs. (Auger) 

728 'lauger_jit', # Distribute wavefunctions for k1-k4 (Auger) 

729 'orbitalmag', # Enable orbital magnetization (NMR) 

730 'lchimag', # Use linear response for shielding tensor (NMR) 

731 'lwrtcur', # Write response of current to mag. field to file (NMR) 

732 'lnmr_sym_red', # Reduce symmetry for finite difference (NMR) 

733 'lzora', # Use ZORA approximation in linear-response NMR (NMR) 

734 'lbone', # Use B-component in AE one-center terms for LR NMR (NMR) 

735 'lmagbloch', # Use Bloch summations to obtain orbital magnetization (NMR) 

736 'lgauge', # Use gauge transformation for zero moment terms (NMR) 

737 'lbfconst', # Use constant B-field with sawtooth vector potential (NMR) 

738 'nucind', # Use nuclear independent calculation (NMR) 

739 'lnicsall', # Use all grid points for 'nucind' calculation (NMR) 

740 'llraug', # Use two-center corrections for induced B-field (NMR) 

741 'lbbm', # Undocumented Bond-Boost parameter (GH patches) 

742 'lnoncollinear', # Do non-collinear spin polarized calculation 

743 'bfgsdfp', # Undocumented BFGS parameter (GH patches) 

744 'linemin', # Use line minimization (GH patches) 

745 'ldneborg', # Undocumented NEB parameter (GH patches) 

746 'dseed', # Undocumented dimer parameter (GH patches) 

747 'linteract', # Undocumented parameter (GH patches) 

748 'lmpmd', # Undocumented parameter (GH patches) 

749 'ltwodim', # Makes stress tensor two-dimensional (GH patches) 

750 'fmagflag', # Use force magnitude as convergence criterion (GH patches) 

751 'ltemper', # Use subspace diagonalization (?) (GH patches) 

752 'qmflag', # Undocumented FIRE parameter (GH patches) 

753 'lmixtau', # Undocumented MetaGGA parameter 

754 'ljdftx', # Undocumented VASPsol parameter (VASPsol) 

755 'lrhob', # Write the bound charge density (VASPsol) 

756 'lrhoion', # Write the ionic charge density (VASPsol) 

757 'lnabla', # Undocumented parameter 

758 'linterfast', # Interpolate in K using linear response routines 

759 'lvel', # Undocumented parameter 

760 'lrpaforce', # Calculate RPA forces 

761 'lhartree', # Use IP approx. in BSE (testing only) 

762 'ladder', # Use ladder diagrams 

763 'lfxc', # Use approximate ladder diagrams 

764 'lrsrpa', # Undocumented parameter 

765 'lsingles', # Calculate HF singles 

766 'lfermigw', # Iterate Fermi level 

767 'ltcte', # Undocumented parameter 

768 'ltete', # Undocumented parameter 

769 'ltriplet', # Undocumented parameter 

770 'lfxceps', # Undocumented parameter 

771 'lfxheg', # Undocumented parameter 

772 'l2order', # Undocumented parameter 

773 'lmp2lt', # Undocumented parameter 

774 'lgwlf', # Undocumented parameter 

775 'lusew', # Undocumented parameter 

776 'selfenergy', # Undocumented parameter 

777 'oddonlygw', # Avoid gamma point in response function calc. 

778 'evenonlygw', # Avoid even points in response function calc. 

779 'lspectralgw', # More accurate self-energy calculation 

780 'ch_lspec', # Calculate matrix elements btw. core and conduction states 

781 'fletcher_reeves', # Undocumented dimer parameter 

782 'lidm_selective', # Undocumented dimer parameter 

783 'lblueout', # Write output of blue-moon algorithm 

784 'hills_variable_w', # Enable variable-width metadynamics bias 

785 'dvvminus', # Undocumented parameter 

786 'lpead', # Calculate cell-periodic orbital derivs. using finite diff. 

787 'skip_edotp', # Skip updating elec. polarization during scf 

788 'skip_scf', # Skip calculation w/ local field effects 

789 'lchgfit', # Turn on charge fitting 

790 'lgausrc', # Undocumented charge fitting parameter 

791 'lstockholder', # Enable ISA charge fitting (?) 

792 'lsymgrad', # Restore symmetry of gradient (HF) 

793 'lhfone', # Calculate one-center terms (HF) 

794 'lrscor', # Include long-range correlation (HF) 

795 'lrhfcalc', # Include long-range HF (HF) 

796 'lmodelhf', # Model HF calculation (HF) 

797 'shiftred', # Undocumented HF parameter 

798 'hfkident', # Undocumented HF parameter 

799 'oddonly', # Undocumented HF parameter 

800 'evenonly', # Undocumented HF parameter 

801 'lfockaedft', # Undocumented HF parameter 

802 'lsubrot', # Enable subspace rotation diagonalization 

803 'mixfirst', # Mix before diagonalization 

804 'lvcader', # Calculate derivs. w.r.t. VCA parameters 

805 'lcompat', # Enable "full compatibility" 

806 'lmusic', # "Joke" parameter 

807 'ldownsample', # Downsample WAVECAR to fewer k-points 

808 'lscaaware', # Disable ScaLAPACK for some things but not all 

809 'lorbitalreal', # Undocumented parameter 

810 'lmetagga', # Undocumented parameter 

811 'lspiral', # Undocumented parameter 

812 'lzeroz', # Undocumented parameter 

813 'lmono', # Enable "monopole" corrections 

814 'lrelcore', # Perform relaxed core calculation 

815 'lmimicfc', # Mimic frozen-core calcs. for relaxed core calcs. 

816 'lmatchrw', # Match PS partial waves at RWIGS? (otherwise PAW cutoff) 

817 'ladaptelin', # Linearize core state energies to avoid divergences 

818 'lonlysemicore', # Only linearize semi-core state energies 

819 'gga_compat', # Enable backwards-compatible symmetrization of GGA derivs. 

820 'lrelvol', # Undocumented classical vdW parameter 

821 'lj_only', # Undocumented classical vdW parameter 

822 'lvdwscs', # Include self-consistent screening in TS vdW correction 

823 'lcfdm', # Use coupled fluctuating dipoles model for TS vdW 

824 'lvdw_sametype', # Include interactions between atoms of the same type 

825 'lrescaler0', # Rescale damping parameters in SCS vdW correction 

826 'lscsgrad', # Calculate gradients for TS+SCS vdW correction energies 

827 'lvdwexpansion', # Write 2-6 body contribs. to MBD vdW correction energy 

828 'lvdw_relvolone', # Undocumented classical vdW parameter 

829 'lberry', # Enable Berry-phase calculation 

830 'lpade_fit', # Undocumented QP parameter 

831 'lkproj', # Enable projection onto k-points 

832 'l_wr_moments', # Undocumented parameter 

833 'l_wr_density', # Undocumented parameter 

834 'lkotani', # Undocumented parameter 

835 'ldyson', # Undocumented parameter 

836 'laddherm', # Undocumented parameter 

837 'lcrpaplot', # Plot bands used in CRPA response func. calc. 

838 'lplotdis', # Plot disentangled bands in CRPA response func. calc. 

839 'ldisentangle', # Disentangle bands in CRPA 

840 'lweighted', # "Weighted" CRPA approach 

841 'luseorth_lcaos', # Use orthogonalized LCAOs in CRPA 

842 'lfrpa', # Use full RPA in CRPA 

843 'lregularize', # Regularize projectors in CRPA 

844 'ldrude', # Include Drude term in CRPA 

845 'ldmatrix', # Undocumented parameter 

846 'lefg', # Calculate electric field gradient at atomic nuclei 

847 'lhyperfine', # Enable Hyperfine calculation 

848 'lwannier', # Enable Wannier interface 

849 'localize', # Undocumented Wannier parameter 

850 'lintpol_wpot', # Interpolate WPOT for Wannier 

851 'lintpol_orb', # Interpolate orbitals for Wannier 

852 'lintpol_kpath', # Interpolate bandstructure on given kpath for Wannier 

853 'lintpol_kpath_orb', # Interpolate orbitals on given kpath for Wannier 

854 'lread_eigenvalues', # Use Eigenvalues from EIGENVALUES.INT file 

855 'lintpol_velocity', # Interpolate electron velocity for Wannier 

856 'lintpol_conductivity', # Interpolate conductivity for Wannier 

857 'lwannierinterpol', # Undocumented Wannier parameter 

858 'wanproj', # Undocumented Wannier parameter 

859 'lorbmom', # Undocumented LDA+U parameter 

860 'lwannier90_run', # Undocumented WANNIER90 parameter 

861 'lwrite_wanproj', # Write UWAN files for WANNIER90 

862 'lwrite_unk', # Write UNK files for WANNIER90 

863 'lwrite_mmn_amn', # Write MMN and AMN files for WANNIER90 

864 'lread_amn', # Read AMN files instead of recomputing (WANNIER90) 

865 'lrhfatm', # Undocumented HF parameter 

866 'lvpot', # Calculate unscreened potential 

867 'lwpot', # Calculate screened potential 

868 'lwswq', # Undocumented parameter 

869 'pflat', # Only print "flat" timings to OUTCAR 

870 'qifcg', # Use CG instead of quickmin (instanton) 

871 'qdo_ins', # Find instanton 

872 'qdo_pre', # Calculate prefactor (instanton) 

873 # The next keyword pertains to the periodic NBO code of JR Schmidt's group 

874 # at UW-Madison (https://github.com/jrschmidt2/periodic-NBO) 

875 'lnbo', # Enable NBO analysis 

876] 

877 

878list_int_keys = [ 

879 'iband', # bands to calculate partial charge for 

880 'kpuse', # k-point to calculate partial charge for 

881 'ldaul', # DFT+U parameters, overruled by dict key 'ldau_luj' 

882 'random_seed', # List of ints used to seed RNG for advanced MD routines 

883 # (Bucko) 

884 'auger_bmin_eeh', # 4 ints | Various undocumented parameters for Auger 

885 'auger_bmax_eeh', # 4 ints | calculations 

886 'auger_bmin_ehh', # 4 ints | 

887 'auger_bmax_ehh', # 4 ints | 

888 'balist', # nbas ints | Undocumented Bond-Boost parameter (GH patches) 

889 'kpoint_bse', # 4 ints | Undocumented parameter 

890 'nsubsys', # <=3 ints | Last atom # for each of up to 3 thermostats 

891 'vdw_refstate', # ntyp ints | Undocumented classical vdW parameter 

892 'vdw_mbd_size', # 3 ints | Supercell size for TS MBD vdW correction 

893 'nbands_index', # nbands_out ints | Undocumented QP parameter 

894 'kpts_index', # kpts_out ints | Undocumented QP parameter 

895 'isp_index', # isp_out ints | Undocumented QP parameter 

896 'nomega_index', # nomega_out ints | Undocumented QP parameter 

897 'ntarget_states', # nbands ints | Undocumented CRPA parameter 

898 'wanproj_i', # nions ints | Undocumented Wannier parameter 

899 'wanproj_l', # ? ints | Undocumented Wannier parameter 

900] 

901 

902list_bool_keys = [ 

903 'lattice_constraints', # 3 bools | Undocumented advanced MD parameter 

904 'lrctype', # ntyp bools | Enable relaxed-core calc. for these atoms 

905 'lvdw_onecell', # 3 bools | Enable periodicity in A, B, C vector for vdW 

906] 

907 

908list_float_keys = [ 

909 'dipol', # center of cell for dipol 

910 'eint', # energy range to calculate partial charge for 

911 'ferwe', # Fixed band occupation (spin-paired) 

912 'ferdo', # Fixed band occupation (spin-plarized) 

913 'magmom', # initial magnetic moments 

914 'ropt', # number of grid points for non-local proj in real space 

915 'rwigs', # Wigner-Seitz radii 

916 'ldauu', # ldau parameters, has potential to redundant w.r.t. dict 

917 'ldauj', # key 'ldau_luj', but 'ldau_luj' can't be read direct from 

918 # the INCAR (since it needs to know information about atomic 

919 # species. In case of conflict 'ldau_luj' gets written out 

920 # when a calculation is set up 

921 'vdw_c6', # List of floats of C6 parameters (J nm^6 mol^-1) for each 

922 # species (DFT-D2 and DFT-TS) 

923 'vdw_c6au', # List of floats of C6 parameters (a.u.) for each species 

924 # (DFT-TS) 

925 'vdw_r0', # List of floats of R0 parameters (angstroms) for each 

926 # species (DFT-D2 and DFT-TS) 

927 'vdw_r0au', # List of floats of R0 parameters (a.u.) for each species 

928 # (DFT-TS) 

929 'vdw_alpha', # List of floats of free-atomic polarizabilities for each 

930 # species (DFT-TS) 

931 'langevin_gamma', # List of floats for langevin friction coefficients 

932 'auger_emin_eeh', # 4 floats | Various undocumented parameters for Auger 

933 'auger_emax_eeh', # 4 floats | calculations 

934 'auger_emin_ehh', # 4 floats | 

935 'auger_emax_ehh', # 4 floats | 

936 'avecconst', # 3 floats | magnitude of magnetic moment (NMR) 

937 'magdipol', # 3 floats | magnitude of magnetic dipole (NMR) 

938 'bconst', # 3 floats | magnitude of constant magnetic field (NMR) 

939 'magpos', # 3 floats | position for magnetic moment w/ 'nucind' (NMR) 

940 'bext', # 3 floats | Undocumented (probably external magnetic field) 

941 'core_c', # ntyp floats | pseudo-core charge magnitude (VASPsol) 

942 'sigma_rc_k', # ntyp floats | width of pseudo-core gaussians (VASPsol) 

943 'darwinr', # ntypd (?) floats | Undocumented parameter 

944 'darwinv', # ntypd (?) floats | Undocumented parameter 

945 'dummy_k', # ? floats | Force const. connecting dummy atoms to sys. 

946 'dummy_r0', # ? floats | Minimum dist., ang., etc. for dummy atom DOFs 

947 'dummy_positions', # 3 floats | Position of dummy atom(s?) 

948 'psubsys', # <=3 floats | Coll. prob. for each of up to 3 thermostats 

949 'tsubsys', # <=3 floats | Temp. for each of up to 3 thermostats 

950 'increm', # ? floats | Undocumented advanced MD parameter 

951 'value_min', # ? floats | Undocumented advanced MD parameter 

952 'value_max', # ? floats | Undocumented advanced MD parameter 

953 'hills_position', # ? floats | Dummy particle(s) pos. for metadynamics 

954 'hills_velocity', # ? floats | Dummy particle(s) vel. for metadynamics 

955 'spring_k', # ? floats | Spring constant for harmonic constraints 

956 'spring_r0', # ? floats | Spring minima for harmonic constraints 

957 'spring_v0', # ? floats | Initial velocity of harmonic constraints 

958 'hills_wall_lower', # ? floats | Undocumented metadynamics parameter 

959 'hills_wall_upper', # ? floats | Undocumented metadynamics parameter 

960 'efield_pead', # 3 floats | homogeneous electric field for PEAD calc. 

961 'zct', # ? floats | Undocumented charge fitting parameter 

962 'rgaus', # ? floats | Undocumented charge fitting parameter 

963 'hfalpha', # 10 floats | Undocumented HF parameter 

964 'mcalpha', # 10 floats | Undocumented HF parameter 

965 'saxis', # 3 floats | Coordinate for collinear spin calculations 

966 'vca', # ? floats | Atom weight for VCA calculations 

967 'stm', # 7 floats | "range for STM data" 

968 'qspiral', # 3 floats | Undocumented parameter 

969 'external_stress', # 6 floats | Target stress (adds w/ external_pressure) 

970 'm_constr', # 3*nions floats | Local magmom assigned to each spin DOF 

971 'quad_efg', # ntyp floats | Nuclear quadrupole moments 

972 'ngyromag', # ntyp floats | Nuclear gyromagnetic ratios 

973 'rcrhocut', # ntyp floats | Core density cutoff rad. for HF relcore calc 

974 'ofield_k', # 3 floats | Undocumented parameter 

975 'paripot', # ? floats | Undocumented parameter 

976 'smearings', # ? floats | ismear,sigma smearing params to loop over 

977 'wanproj_e', # 2 floats | Undocumented Wannier parameter 

978] 

979 

980special_keys = [ 

981 'lreal', # non-local projectors in real space 

982] 

983 

984dict_keys = [ 

985 'ldau_luj', # dictionary with L(S)DA+U parameters, e.g. {'Fe':{'L':2, 

986 # 'U':4.0, 'J':0.9}, ...} 

987] 

988 

989keys: List[str] = [ 

990 # 'NBLOCK' and KBLOCK inner block; outer block 

991 # 'NPACO' and APACO distance and nr. of slots for P.C. 

992 # 'WEIMIN, EBREAK, DEPER special control tags 

993] 

994 

995 

996class GenerateVaspInput: 

997 # Parameters corresponding to 'xc' settings. This may be modified 

998 # by the user in-between loading calculators.vasp submodule and 

999 # instantiating the calculator object with calculators.vasp.Vasp() 

1000 xc_defaults = { 

1001 'lda': { 

1002 'pp': 'LDA' 

1003 }, 

1004 # GGAs 

1005 'blyp': { # https://www.vasp.at/forum/viewtopic.php?p=17234 

1006 'pp': 'PBE', 

1007 'gga': 'B5', 

1008 'aldax': 1.00, 

1009 'aggax': 1.00, 

1010 'aggac': 1.00, 

1011 'aldac': 0.00 

1012 }, 

1013 'pbe': { 

1014 'pp': 'PBE', 

1015 'gga': 'PE' 

1016 }, 

1017 'pw91': { 

1018 'gga': '91' 

1019 }, 

1020 'pbesol': { 

1021 'gga': 'PS' 

1022 }, 

1023 'revpbe': { 

1024 'gga': 'RE' 

1025 }, 

1026 'rpbe': { 

1027 'gga': 'RP' 

1028 }, 

1029 'am05': { 

1030 'gga': 'AM' 

1031 }, 

1032 # Meta-GGAs 

1033 'tpss': { 

1034 'metagga': 'TPSS' 

1035 }, 

1036 'revtpss': { 

1037 'metagga': 'RTPSS' 

1038 }, 

1039 'm06l': { 

1040 'metagga': 'M06L' 

1041 }, 

1042 'ms0': { 

1043 'metagga': 'MS0' 

1044 }, 

1045 'ms1': { 

1046 'metagga': 'MS1' 

1047 }, 

1048 'ms2': { 

1049 'metagga': 'MS2' 

1050 }, 

1051 'scan': { 

1052 'metagga': 'SCAN' 

1053 }, 

1054 'rscan': { 

1055 'metagga': 'RSCAN' 

1056 }, 

1057 'r2scan': { 

1058 'metagga': 'R2SCAN' 

1059 }, 

1060 'mbj': { 

1061 # Modified Becke-Johnson 

1062 'metagga': 'MBJ', 

1063 }, 

1064 'tb09': { 

1065 # Alias for MBJ 

1066 'metagga': 'MBJ', 

1067 }, 

1068 # vdW-DFs 

1069 'vdw-df': { 

1070 'gga': 'RE', 

1071 'luse_vdw': True, 

1072 'aggac': 0. 

1073 }, 

1074 'vdw-df-cx': { 

1075 'gga': 'CX', 

1076 'luse_vdw': True, 

1077 'aggac': 0. 

1078 }, 

1079 'vdw-df-cx0p': { 

1080 'gga': 'CX', 

1081 'luse_vdw': True, 

1082 'aggac': 0., 

1083 'lhfcalc': True, 

1084 'aexx': 0.2, 

1085 'aggax': 0.8 

1086 }, 

1087 'vdw-df3-opt1': { 

1088 'gga': 'BO', 

1089 'param1': 0.1122334456, 

1090 'param2': 0.1234568, 

1091 'aggac': 0.0, 

1092 'luse_vdw': True, 

1093 'ivdw_nl': 3, 

1094 'alpha_vdw': 0.94950, 

1095 'gamma_vdw': 1.12, 

1096 }, 

1097 'vdw-df3-opt2': { 

1098 'gga': 'MK', 

1099 'param1': 0.1234568, 

1100 'param2': 0.58, 

1101 'aggac': 0.0, 

1102 'luse_vdw': True, 

1103 'ivdw_nl': 4, 

1104 'zab_vdw': -1.8867, 

1105 'alpha_vdw': 0.28248, 

1106 'gamma_vdw': 1.29, 

1107 }, 

1108 'rvv10': { 

1109 'gga': 'ML', 

1110 'luse_vdw': True, 

1111 'ivdw_nl': 2, 

1112 'bparam': 6.3, 

1113 'cparam': 0.0093, 

1114 }, 

1115 'scan+rvv10': { 

1116 'metagga': 'SCAN', 

1117 'luse_vdw': True, 

1118 'bparam': 15.7, 

1119 'cparam': 0.0093, 

1120 }, 

1121 'pbe+rvv10l': { 

1122 'gga': 'PE', 

1123 'luse_vdw': True, 

1124 'bparam': 10, 

1125 'cparam': 0.0093, 

1126 }, 

1127 'r2scan+rvv10': { 

1128 'metagga': 'R2SCAN', 

1129 'luse_vdw': True, 

1130 'bparam': 11.95, 

1131 'cparam': 0.0093, 

1132 }, 

1133 'optpbe-vdw': { 

1134 'gga': 'OR', 

1135 'luse_vdw': True, 

1136 'aggac': 0.0 

1137 }, 

1138 'optb88-vdw': { 

1139 'gga': 'BO', 

1140 'luse_vdw': True, 

1141 'aggac': 0.0, 

1142 'param1': 1.1 / 6.0, 

1143 'param2': 0.22 

1144 }, 

1145 'optb86b-vdw': { 

1146 'gga': 'MK', 

1147 'luse_vdw': True, 

1148 'aggac': 0.0, 

1149 'param1': 0.1234, 

1150 'param2': 1.0 

1151 }, 

1152 'vdw-df2': { 

1153 'gga': 'ML', 

1154 'luse_vdw': True, 

1155 'aggac': 0.0, 

1156 'zab_vdw': -1.8867 

1157 }, 

1158 'rev-vdw-df2': { 

1159 'gga': 'MK', 

1160 'luse_vdw': True, 

1161 'param1': 0.1234, 

1162 'param2': 0.711357, 

1163 'zab_vdw': -1.8867, 

1164 'aggac': 0.0 

1165 }, 

1166 'beef-vdw': { 

1167 'gga': 'BF', 

1168 'luse_vdw': True, 

1169 'zab_vdw': -1.8867 

1170 }, 

1171 # Hartree-Fock and hybrids 

1172 'hf': { 

1173 'lhfcalc': True, 

1174 'aexx': 1.0, 

1175 'aldac': 0.0, 

1176 'aggac': 0.0 

1177 }, 

1178 'b3lyp': { 

1179 'gga': 'B3', 

1180 'lhfcalc': True, 

1181 'aexx': 0.2, 

1182 'aggax': 0.72, 

1183 'aggac': 0.81, 

1184 'aldac': 0.19 

1185 }, 

1186 'pbe0': { 

1187 'gga': 'PE', 

1188 'lhfcalc': True 

1189 }, 

1190 'hse03': { 

1191 'gga': 'PE', 

1192 'lhfcalc': True, 

1193 'hfscreen': 0.3 

1194 }, 

1195 'hse06': { 

1196 'gga': 'PE', 

1197 'lhfcalc': True, 

1198 'hfscreen': 0.2 

1199 }, 

1200 'hsesol': { 

1201 'gga': 'PS', 

1202 'lhfcalc': True, 

1203 'hfscreen': 0.2 

1204 }, 

1205 # MN-VFM functionals 

1206 'sogga': { 

1207 'gga': 'SA' 

1208 }, 

1209 'sogga11': { 

1210 'gga': 'S1' 

1211 }, 

1212 'sogga11-x': { 

1213 'gga': 'SX', 

1214 'lhfcalc': True, 

1215 'aexx': 0.401 

1216 }, 

1217 'n12': { 

1218 'gga': 'N2' 

1219 }, 

1220 'n12-sx': { 

1221 'gga': 'NX', 

1222 'lhfcalc': True, 

1223 'lhfscreen': 0.2 

1224 }, 

1225 'mn12l': { 

1226 'metagga': 'MN12L' 

1227 }, 

1228 'gam': { 

1229 'gga': 'GA' 

1230 }, 

1231 'mn15l': { 

1232 'metagga': 'MN15L' 

1233 }, 

1234 'hle17': { 

1235 'metagga': 'HLE17' 

1236 }, 

1237 'revm06l': { 

1238 'metagga': 'revM06L' 

1239 }, 

1240 'm06sx': { 

1241 'metagga': 'M06SX', 

1242 'lhfcalc': True, 

1243 'hfscreen': 0.189, 

1244 'aexx': 0.335 

1245 } 

1246 } 

1247 

1248 # environment variable for PP paths 

1249 VASP_PP_PATH = 'VASP_PP_PATH' 

1250 VASP_PP_VERSION = 'VASP_PP_VERSION' 

1251 

1252 def __init__(self, restart=None): 

1253 self.float_params = {} 

1254 self.exp_params = {} 

1255 self.string_params = {} 

1256 self.int_params = {} 

1257 self.bool_params = {} 

1258 self.list_bool_params = {} 

1259 self.list_int_params = {} 

1260 self.list_float_params = {} 

1261 self.special_params = {} 

1262 self.dict_params = {} 

1263 self.atoms = None 

1264 for key in float_keys: 

1265 self.float_params[key] = None 

1266 for key in exp_keys: 

1267 self.exp_params[key] = None 

1268 for key in string_keys: 

1269 self.string_params[key] = None 

1270 for key in int_keys: 

1271 self.int_params[key] = None 

1272 for key in bool_keys: 

1273 self.bool_params[key] = None 

1274 for key in list_bool_keys: 

1275 self.list_bool_params[key] = None 

1276 for key in list_int_keys: 

1277 self.list_int_params[key] = None 

1278 for key in list_float_keys: 

1279 self.list_float_params[key] = None 

1280 for key in special_keys: 

1281 self.special_params[key] = None 

1282 for key in dict_keys: 

1283 self.dict_params[key] = None 

1284 

1285 # Initialize internal dictionary of input parameters which are 

1286 # not regular VASP keys 

1287 self.input_params = { 

1288 'xc': None, # Exchange-correlation recipe (e.g. 'B3LYP') 

1289 'pp': None, # Pseudopotential file (e.g. 'PW91') 

1290 'pp_version': None, # pseudopotential version (e.g. '52', '64') 

1291 'setups': None, # Special setups (e.g pv, sv, ...) 

1292 'txt': '-', # Where to send information 

1293 'kpts': (1, 1, 1), # k-points 

1294 # Option to use gamma-sampling instead of Monkhorst-Pack: 

1295 'gamma': False, 

1296 # number of points between points in band structures: 

1297 'kpts_nintersections': None, 

1298 # Option to write explicit k-points in units 

1299 # of reciprocal lattice vectors: 

1300 'reciprocal': False, 

1301 # Switch to disable writing constraints to POSCAR 

1302 'ignore_constraints': False, 

1303 # Net charge for the whole system; determines nelect if not 0 

1304 'charge': None, 

1305 # Deprecated older parameter which works just like "charge" but 

1306 # with the sign flipped 

1307 'net_charge': None, 

1308 # Custom key-value pairs, written to INCAR with *no* type checking 

1309 'custom': {}, 

1310 } 

1311 

1312 def set_xc_params(self, xc): 

1313 """Set parameters corresponding to XC functional""" 

1314 xc = xc.lower() 

1315 if xc is None: 

1316 pass 

1317 elif xc not in self.xc_defaults: 

1318 xc_allowed = ', '.join(self.xc_defaults.keys()) 

1319 raise ValueError('{} is not supported for xc! Supported xc values' 

1320 'are: {}'.format(xc, xc_allowed)) 

1321 else: 

1322 # XC defaults to PBE pseudopotentials 

1323 if 'pp' not in self.xc_defaults[xc]: 

1324 self.set(pp='PBE') 

1325 self.set(**self.xc_defaults[xc]) 

1326 

1327 def set(self, **kwargs): 

1328 

1329 if (('ldauu' in kwargs) and ('ldaul' in kwargs) and ('ldauj' in kwargs) 

1330 and ('ldau_luj' in kwargs)): 

1331 raise NotImplementedError( 

1332 'You can either specify ldaul, ldauu, and ldauj OR ' 

1333 'ldau_luj. ldau_luj is not a VASP keyword. It is a ' 

1334 'dictionary that specifies L, U and J for each ' 

1335 'chemical species in the atoms object. ' 

1336 'For example for a water molecule:' 

1337 '''ldau_luj={'H':{'L':2, 'U':4.0, 'J':0.9}, 

1338 'O':{'L':2, 'U':4.0, 'J':0.9}}''') 

1339 

1340 if 'xc' in kwargs: 

1341 self.set_xc_params(kwargs['xc']) 

1342 for key, value in kwargs.items(): 

1343 if key in self.float_params: 

1344 self.float_params[key] = value 

1345 elif key in self.exp_params: 

1346 self.exp_params[key] = value 

1347 elif key in self.string_params: 

1348 self.string_params[key] = value 

1349 elif key in self.int_params: 

1350 self.int_params[key] = value 

1351 elif key in self.bool_params: 

1352 self.bool_params[key] = value 

1353 elif key in self.list_bool_params: 

1354 self.list_bool_params[key] = value 

1355 elif key in self.list_int_params: 

1356 self.list_int_params[key] = value 

1357 elif key in self.list_float_params: 

1358 self.list_float_params[key] = value 

1359 elif key in self.special_params: 

1360 self.special_params[key] = value 

1361 elif key in self.dict_params: 

1362 self.dict_params[key] = value 

1363 elif key in self.input_params: 

1364 self.input_params[key] = value 

1365 elif isinstance(value, str): 

1366 self.string_params[key] = value 

1367 # `bool` is a subclass of `int` and should be checked earlier. 

1368 # https://docs.python.org/3/c-api/bool.html 

1369 elif isinstance(value, bool): 

1370 self.bool_params[key] = value 

1371 elif isinstance(value, int): 

1372 self.int_params[key] = value 

1373 elif isinstance(value, float): 

1374 self.float_params[key] = value 

1375 elif isinstance(value, list): 

1376 if len(value) == 0: 

1377 msg = f'empty list is given for {key}' 

1378 raise ValueError(msg) 

1379 if isinstance(value[0], bool): 

1380 self.list_bool_params[key] = value 

1381 elif isinstance(value[0], int): 

1382 self.list_int_params[key] = value 

1383 elif isinstance(value[0], float): 

1384 self.list_float_params[key] = value 

1385 else: 

1386 msg = f'cannot handle type of value for {key} = {value!r}' 

1387 raise TypeError(msg) 

1388 else: 

1389 msg = f'cannot handle type of value for {key} = {value!r}' 

1390 raise TypeError(msg) 

1391 

1392 def check_xc(self): 

1393 """Make sure the calculator has functional & pseudopotentials set up 

1394 

1395 If no XC combination, GGA functional or POTCAR type is specified, 

1396 default to PW91. Otherwise, try to guess the desired pseudopotentials. 

1397 """ 

1398 

1399 p = self.input_params 

1400 

1401 # There is no way to correctly guess the desired 

1402 # set of pseudopotentials without 'pp' being set. 

1403 # Usually, 'pp' will be set by 'xc'. 

1404 if 'pp' not in p or p['pp'] is None: 

1405 if self.string_params['gga'] is None: 

1406 p.update({'pp': 'lda'}) 

1407 elif self.string_params['gga'] == 'PE': 

1408 p.update({'pp': 'pbe'}) 

1409 else: 

1410 raise NotImplementedError( 

1411 "Unable to guess the desired set of pseudopotential" 

1412 "(POTCAR) files. Please do one of the following: \n" 

1413 "1. Use the 'xc' parameter to define your XC functional." 

1414 "These 'recipes' determine the pseudopotential file as " 

1415 "well as setting the INCAR parameters.\n" 

1416 "2. Use the 'gga' settings None (default) or 'PE'; " 

1417 "these correspond to LDA and PBE, respectively.\n" 

1418 "3. Set the POTCAR explicitly with the 'pp' flag. The " 

1419 "value should be the name of a folder on the VASP_PP_PATH" 

1420 ", and the aliases 'LDA' and 'PBE' are also accepted.\n" 

1421 ) 

1422 

1423 if (p['xc'] is not None and p['xc'].lower() == 'lda' 

1424 and p['pp'].lower() != 'lda'): 

1425 warnings.warn("XC is set to LDA, but PP is set to " 

1426 "{0}. \nThis calculation is using the {0} " 

1427 "POTCAR set. \n Please check that this is " 

1428 "really what you intended!" 

1429 "\n".format(p['pp'].upper())) 

1430 

1431 def _make_sort( 

1432 self, atoms: ase.Atoms, special_setups: Sequence[int] = () 

1433 ) -> Tuple[List[int], List[int]]: 

1434 symbols, _ = count_symbols(atoms, exclude=special_setups) 

1435 

1436 # Create sorting list 

1437 srt = [] # type: List[int] 

1438 srt.extend(special_setups) 

1439 

1440 for symbol in symbols: 

1441 for m, atom in enumerate(atoms): 

1442 if m in special_setups: 

1443 continue 

1444 if atom.symbol == symbol: 

1445 srt.append(m) 

1446 # Create the resorting list 

1447 resrt = list(range(len(srt))) 

1448 for n in range(len(resrt)): 

1449 resrt[srt[n]] = n 

1450 return srt, resrt 

1451 

1452 def _set_spinpol(self, atoms): 

1453 if self.int_params['ispin'] is None: 

1454 self.spinpol = atoms.get_initial_magnetic_moments().any() 

1455 else: 

1456 # VASP runs non-spin-polarized calculations when `ispin=1`, 

1457 # regardless if `magmom` is specified or not. 

1458 self.spinpol = (self.int_params['ispin'] == 2) 

1459 

1460 def _build_pp_list(self, 

1461 atoms, 

1462 setups=None, 

1463 special_setups: Sequence[int] = ()): 

1464 """Build the pseudopotential lists""" 

1465 

1466 p = self.input_params 

1467 

1468 if p['pp_version'] is not None: 

1469 pp_version = p['pp_version'] 

1470 elif self.VASP_PP_VERSION in cfg: 

1471 pp_version = cfg[self.VASP_PP_VERSION] 

1472 else: 

1473 pp_version = '' 

1474 

1475 if setups is None: 

1476 setups, special_setups = get_pp_setup(p['setups']) 

1477 

1478 symbols, _ = count_symbols(atoms, exclude=special_setups) 

1479 

1480 # Potpaw folders may be identified by an alias or full name 

1481 for pp_alias, pp_folder in (('lda', f'potpaw_LDA.{pp_version}' 

1482 if pp_version else 'potpaw'), 

1483 ('pbe', f'potpaw_PBE.{pp_version}' 

1484 if pp_version else 'potpaw_PBE')): 

1485 if p['pp'].lower() == pp_alias: 

1486 break 

1487 else: 

1488 pp_folder = p['pp'] 

1489 

1490 if self.VASP_PP_PATH in cfg: 

1491 pppath = cfg[self.VASP_PP_PATH] 

1492 if not os.path.exists(pppath): 

1493 raise RuntimeError(f"VASP_PP_PATH does not exist: {pppath}") 

1494 else: 

1495 pppath = None 

1496 ppp_list = [] 

1497 # Setting the pseudopotentials, first special setups and 

1498 # then according to symbols 

1499 for m in special_setups: 

1500 if m in setups: 

1501 special_setup_index = m 

1502 elif str(m) in setups: 

1503 special_setup_index = str(m) # type: ignore[assignment] 

1504 else: 

1505 raise Exception("Having trouble with special setup index {}." 

1506 " Please use an int.".format(m)) 

1507 potcar = join(pp_folder, setups[special_setup_index], 'POTCAR') 

1508 potcar_path = join(pppath, potcar) if pppath is not None else potcar 

1509 

1510 if os.path.exists(potcar_path): 

1511 ppp_list.append(potcar_path) 

1512 elif os.path.exists(potcar_path + '.Z'): 

1513 ppp_list.append(potcar_path + '.Z') 

1514 else: 

1515 msg = """Looking for {}. 

1516 No pseudopotential for symbol{} with setup {} """.format( 

1517 potcar_path, atoms.symbols[m], setups[special_setup_index]) 

1518 raise RuntimeError(msg) 

1519 

1520 for symbol in symbols: 

1521 try: 

1522 potcar = join(pp_folder, symbol + setups[symbol], 'POTCAR') 

1523 except (TypeError, KeyError): 

1524 potcar = join(pp_folder, symbol, 'POTCAR') 

1525 

1526 potcar_path = join(pppath, potcar) if pppath is not None else potcar 

1527 

1528 if os.path.exists(potcar_path): 

1529 ppp_list.append(potcar_path) 

1530 elif os.path.exists(potcar_path + '.Z'): 

1531 ppp_list.append(potcar_path + '.Z') 

1532 else: 

1533 msg = ("""Looking for {} 

1534 The pseudopotentials are expected to be in: 

1535 LDA: $VASP_PP_PATH/potpaw_LDA(.52|.54|.64) 

1536 or $VASP_PP_PATH/potpaw 

1537 PBE: $VASP_PP_PATH/potpaw_PBE(.52|.54|.64) 

1538 

1539 No pseudopotential for {}!""".format(potcar_path, symbol 

1540 )) 

1541 raise RuntimeError(msg) 

1542 return ppp_list 

1543 

1544 def initialize(self, atoms: Atoms) -> None: 

1545 """Initialize a VASP calculation 

1546 

1547 Constructs the POTCAR file (does not actually write it). 

1548 User should specify the PATH 

1549 to the pseudopotentials in VASP_PP_PATH environment variable 

1550 

1551 The pseudopotentials are expected to be in: 

1552 LDA: $VASP_PP_PATH/potpaw_LDA(.52|.54|.64) or $VASP_PP_PATH/potpaw 

1553 PBE: $VASP_PP_PATH/potpaw_PBE(.52|.54|.64) 

1554 

1555 if your pseudopotentials are somewhere else, or named 

1556 differently you may make symlinks at the paths above that 

1557 point to the right place. Alternatively, you may pass the full 

1558 name of a folder on the VASP_PP_PATH to the 'pp' parameter. 

1559 """ 

1560 

1561 self.check_xc() 

1562 self.atoms = atoms 

1563 self.all_symbols = atoms.get_chemical_symbols() 

1564 self.natoms = len(atoms) 

1565 

1566 self._set_spinpol(atoms) 

1567 

1568 setups, special_setups = get_pp_setup(self.input_params['setups']) 

1569 

1570 # Determine the number of atoms of each atomic species 

1571 # sorted after atomic species 

1572 symbols, symbolcount = count_symbols(atoms, exclude=special_setups) 

1573 self.sort, self.resort = self._make_sort(atoms, 

1574 special_setups=special_setups) 

1575 

1576 self.atoms_sorted = atoms[self.sort] 

1577 

1578 # Check if the necessary POTCAR files exists and 

1579 # create a list of their paths. 

1580 atomtypes = atoms.get_chemical_symbols() 

1581 self.symbol_count: list[tuple[str, int]] = [] 

1582 for m in special_setups: 

1583 self.symbol_count.append((atomtypes[m], 1)) 

1584 for s in symbols: 

1585 self.symbol_count.append((s, symbolcount[s])) 

1586 

1587 # create pseudopotential list 

1588 self.ppp_list = self._build_pp_list( 

1589 atoms, 

1590 setups=setups, 

1591 special_setups=special_setups, 

1592 ) 

1593 

1594 self.converged = None 

1595 self.setups_changed = None 

1596 

1597 def default_nelect_from_ppp(self) -> float: 

1598 """ Get default number of electrons from ppp_list and symbol_count 

1599 

1600 "Default" here means that the resulting cell would be neutral. 

1601 """ 

1602 symbol_valences: list[tuple[str, float]] = [] 

1603 for filename in self.ppp_list: 

1604 with open_potcar(filename=filename) as ppp_file: 

1605 r = read_potcar_numbers_of_electrons(ppp_file) 

1606 symbol_valences.extend(r) 

1607 assert len(self.symbol_count) == len(symbol_valences) 

1608 default_nelect = 0.0 

1609 for ((symbol1, count), 

1610 (symbol2, valence)) in zip(self.symbol_count, symbol_valences): 

1611 assert symbol1 == symbol2 

1612 default_nelect += count * valence 

1613 return default_nelect 

1614 

1615 def write_input(self, atoms, directory='./'): 

1616 from ase.io.vasp import write_vasp 

1617 write_vasp(join(directory, 'POSCAR'), 

1618 self.atoms_sorted, 

1619 symbol_count=self.symbol_count, 

1620 ignore_constraints=self.input_params['ignore_constraints']) 

1621 self.write_incar(atoms, directory=directory) 

1622 self.write_potcar(directory=directory) 

1623 self.write_kpoints(atoms=atoms, directory=directory) 

1624 self.write_sort_file(directory=directory) 

1625 self.copy_vdw_kernel(directory=directory) 

1626 

1627 def copy_vdw_kernel(self, directory='./'): 

1628 """Method to copy the vdw_kernel.bindat file. 

1629 Set ASE_VASP_VDW environment variable to the vdw_kernel.bindat 

1630 folder location. Checks if LUSE_VDW is enabled, and if no location 

1631 for the vdW kernel is specified, a warning is issued.""" 

1632 

1633 vdw_env = 'ASE_VASP_VDW' 

1634 kernel = 'vdw_kernel.bindat' 

1635 dst = os.path.join(directory, kernel) 

1636 

1637 # No need to copy the file again 

1638 if os.path.isfile(dst): 

1639 return 

1640 

1641 if self.bool_params['luse_vdw']: 

1642 src = None 

1643 if vdw_env in cfg: 

1644 src = os.path.join(cfg[vdw_env], kernel) 

1645 

1646 if not src or not os.path.isfile(src): 

1647 warnings.warn( 

1648 ('vdW has been enabled, however no' 

1649 ' location for the {} file' 

1650 ' has been specified.' 

1651 ' Set {} environment variable to' 

1652 ' copy the vdW kernel.').format(kernel, vdw_env)) 

1653 else: 

1654 shutil.copyfile(src, dst) 

1655 

1656 def clean(self): 

1657 """Method which cleans up after a calculation. 

1658 

1659 The default files generated by Vasp will be deleted IF this 

1660 method is called. 

1661 

1662 """ 

1663 files = [ 

1664 'CHG', 'CHGCAR', 'POSCAR', 'INCAR', 'CONTCAR', 'DOSCAR', 

1665 'EIGENVAL', 'IBZKPT', 'KPOINTS', 'OSZICAR', 'OUTCAR', 'PCDAT', 

1666 'POTCAR', 'vasprun.xml', 'WAVECAR', 'XDATCAR', 'PROCAR', 

1667 'ase-sort.dat', 'LOCPOT', 'AECCAR0', 'AECCAR1', 'AECCAR2' 

1668 ] 

1669 for f in files: 

1670 try: 

1671 os.remove(f) 

1672 except OSError: 

1673 pass 

1674 

1675 def write_incar(self, atoms, directory='./', **kwargs): 

1676 """Writes the INCAR file.""" 

1677 incar_params = {} 

1678 

1679 # float params 

1680 float_dct = { 

1681 key: f'{val:{FLOAT_FORMAT}}' 

1682 for key, val in self.float_params.items() 

1683 if val is not None 

1684 } 

1685 

1686 if 'charge' in self.input_params and self.input_params[ 

1687 'charge'] is not None: 

1688 nelect_val = _calc_nelect_from_charge( 

1689 self.float_params['nelect'], 

1690 self.input_params['charge'], 

1691 self.default_nelect_from_ppp()) 

1692 if nelect_val: 

1693 float_dct['nelect'] = f'{nelect_val:{FLOAT_FORMAT}}' 

1694 incar_params.update(float_dct) 

1695 

1696 # exp params 

1697 exp_dct = { 

1698 key: f'{val:{EXP_FORMAT}}' 

1699 for key, val in self.exp_params.items() 

1700 if val is not None 

1701 } 

1702 incar_params.update(exp_dct) 

1703 

1704 # string_params 

1705 string_dct = { 

1706 key: val for key, val in self.string_params.items() if val is not 

1707 None 

1708 } 

1709 incar_params.update(string_dct) 

1710 

1711 # int params 

1712 int_dct = { 

1713 key: val for key, val in self.int_params.items() if val is not None 

1714 } 

1715 if 'ichain' in int_dct.keys(): 

1716 ichain_dict = check_ichain( 

1717 ichain=int_dct['ichain'], 

1718 ediffg=self.exp_params.get('ediffg', None), 

1719 iopt=int_dct.get('iopt', None), 

1720 ) 

1721 int_dct.update(ichain_dict) 

1722 incar_params.update(int_dct) 

1723 

1724 # list_bool_params 

1725 bool_dct = { 

1726 key: val 

1727 for key, val in self.list_bool_params.items() 

1728 if val is not None 

1729 } 

1730 for key, val in bool_dct.items(): 

1731 bool_dct[key] = [_to_vasp_bool(x) for x in val] 

1732 incar_params.update(bool_dct) 

1733 

1734 # list_int_params 

1735 int_dct = { 

1736 key: val 

1737 for key, val in self.list_int_params.items() 

1738 if val is not None 

1739 } 

1740 if 'ldaul' in int_dct.keys() and self.dict_params[ 

1741 'ldau_luj'] is not None: 

1742 del int_dct['ldaul'] 

1743 incar_params.update(int_dct) 

1744 

1745 # list_float_params 

1746 float_dct = { 

1747 key: val 

1748 for key, val in self.list_float_params.items() 

1749 if val is not None 

1750 } 

1751 if 'ldauu' in float_dct.keys() and self.dict_params[ 

1752 'ldau_luj'] is not None: 

1753 del float_dct['ldauu'] 

1754 if 'ldauj' in float_dct.keys() and self.dict_params[ 

1755 'ldau_luj'] is not None: 

1756 del float_dct['ldauj'] 

1757 incar_params.update(float_dct) 

1758 

1759 # bool params 

1760 bool_dct = { 

1761 key: _to_vasp_bool(val) 

1762 for key, val in self.bool_params.items() 

1763 if val is not None 

1764 } 

1765 incar_params.update(bool_dct) 

1766 

1767 # special params 

1768 special_dct = { 

1769 key: val for key, val in self.special_params.items() if val is not 

1770 None 

1771 } 

1772 if 'lreal' in special_dct.keys(): 

1773 if isinstance(special_dct['lreal'], bool): 

1774 special_dct['lreal'] = _to_vasp_bool(special_dct['lreal']) 

1775 incar_params.update(special_dct) 

1776 

1777 # dict params 

1778 dict_dct = { 

1779 key: val for key, val in self.dict_params.items() if val is not None 

1780 } 

1781 if 'ldau_luj' in dict_dct.keys(): 

1782 ldau_dict = set_ldau( 

1783 ldau_param=self.bool_params['ldau'], 

1784 luj_params=dict_dct['ldau_luj'], 

1785 symbol_count=self.symbol_count) 

1786 dict_dct.update(ldau_dict) 

1787 del dict_dct['ldau_luj'] 

1788 incar_params.update(dict_dct) 

1789 

1790 # set magmom based on input or initial atoms object 

1791 spinpol, magmom_dct = set_magmom( 

1792 atoms=atoms, 

1793 ispin=self.int_params['ispin'], 

1794 spinpol=self.spinpol, 

1795 magmom_input=float_dct.get('magmom', None), 

1796 sorting=self.sort, 

1797 ) 

1798 self.spinpol = spinpol 

1799 incar_params.update(magmom_dct) 

1800 

1801 # Custom key-value pairs, which receive no formatting 

1802 # Use the comment "# <Custom ASE key>" to denote such 

1803 # a custom key-value pair, as we cannot otherwise 

1804 # reliably and easily identify such non-standard entries 

1805 

1806 cust_dict = { 

1807 key: str(val) + ' # <Custom ASE key>' 

1808 for key, val in self.input_params['custom'].items() 

1809 if val is not None 

1810 } 

1811 incar_params.update(cust_dict) 

1812 

1813 write_incar(directory=directory, parameters=incar_params) 

1814 

1815 def write_kpoints(self, atoms=None, directory='./', **kwargs): 

1816 """Writes the KPOINTS file.""" 

1817 

1818 if atoms is None: 

1819 atoms = self.atoms 

1820 

1821 # Don't write anything if KSPACING is being used 

1822 if self.float_params['kspacing'] is not None: 

1823 if self.float_params['kspacing'] > 0: 

1824 return 

1825 else: 

1826 raise ValueError("KSPACING value {} is not allowable. " 

1827 "Please use None or a positive number." 

1828 "".format(self.float_params['kspacing'])) 

1829 if self.input_params['kpts'] is None: 

1830 return 

1831 

1832 kpointstring = format_kpoints( 

1833 kpts=self.input_params['kpts'], 

1834 atoms=atoms, 

1835 reciprocal=self.input_params['reciprocal'], 

1836 gamma=self.input_params['gamma']) 

1837 with open(join(directory, 'KPOINTS'), 'w') as kpoints: 

1838 kpoints.write(kpointstring) 

1839 

1840 def write_potcar(self, suffix="", directory='./'): 

1841 """Writes the POTCAR file.""" 

1842 

1843 with open(join(directory, 'POTCAR' + suffix), 'w') as potfile: 

1844 for filename in self.ppp_list: 

1845 with open_potcar(filename=filename) as ppp_file: 

1846 for line in ppp_file: 

1847 potfile.write(line) 

1848 

1849 def write_sort_file(self, directory='./'): 

1850 """Writes a sortings file. 

1851 

1852 This file contains information about how the atoms are sorted in 

1853 the first column and how they should be resorted in the second 

1854 column. It is used for restart purposes to get sorting right 

1855 when reading in an old calculation to ASE.""" 

1856 

1857 with open(join(directory, 'ase-sort.dat'), 'w') as fd: 

1858 for n in range(len(self.sort)): 

1859 fd.write('%5i %5i \n' % (self.sort[n], self.resort[n])) 

1860 

1861 # The below functions are used to restart a calculation 

1862 

1863 @staticmethod 

1864 def set_if_none(collection: dict[str, Any], key: str, value: Any) -> None: 

1865 collection[key] = value if collection.get(key) is None \ 

1866 else collection[key] 

1867 

1868 def read_incar(self, filename): 

1869 """Method that imports settings from INCAR file. 

1870 

1871 Typically named INCAR.""" 

1872 

1873 self.spinpol = False 

1874 with open(filename) as fd: 

1875 lines = fd.readlines() 

1876 

1877 for line in lines: 

1878 try: 

1879 # Make multiplication, comments, and parameters easier to spot 

1880 line = line.replace("*", " * ") 

1881 line = line.replace("=", " = ") 

1882 line = line.replace("#", "# ") 

1883 data = line.split() 

1884 # Skip empty and commented lines. 

1885 if len(data) == 0: 

1886 continue 

1887 elif data[0][0] in ['#', '!']: 

1888 continue 

1889 key = data[0].lower() 

1890 if '<Custom ASE key>' in line: 

1891 # This key was added with custom key-value pair formatting. 

1892 # Unconditionally add it, no type checking 

1893 # Get value between "=" and the comment, e.g. 

1894 # key = 1 2 3 # <Custom ASE key> 

1895 # value should be '1 2 3' 

1896 

1897 # Split at first occurence of "=" 

1898 value = line.split('=', 1)[1] 

1899 # First "#" denotes beginning of comment 

1900 # Add everything before comment as a string to custom dict 

1901 value = value.split('#', 1)[0].strip() 

1902 self.set_if_none(self.inputs_params['custom'], key, value) 

1903 elif key in float_keys: 

1904 self.set_if_none(self.float_params, key, float(data[2])) 

1905 elif key in exp_keys: 

1906 self.set_if_none(self.exp_params, key, float(data[2])) 

1907 elif key in string_keys: 

1908 self.set_if_none(self.string_params, key, str(data[2])) 

1909 elif key in int_keys: 

1910 if key == 'ispin': 

1911 # JRK added. not sure why we would want to leave ispin 

1912 # out 

1913 self.set_if_none(self.int_params, key, int(data[2])) 

1914 if int(data[2]) == 2: 

1915 self.spinpol = True 

1916 else: 

1917 self.set_if_none(self.int_params, key, int(data[2])) 

1918 elif key in bool_keys: 

1919 try: 

1920 bool_val = _from_vasp_bool(data[2]) 

1921 except ValueError as exc: 

1922 raise ValueError(f'Invalid value "{data[2]}" for bool ' 

1923 f'key "{key}"') from exc 

1924 self.set_if_none(self.bool_params, key, bool_val) 

1925 

1926 elif key in list_bool_keys: 

1927 self.set_if_none(self.list_bool_params, key, [ 

1928 _from_vasp_bool(x) 

1929 for x in _args_without_comment(data[2:]) 

1930 ]) 

1931 

1932 elif key in list_int_keys: 

1933 self.set_if_none(self.list_int_params, key, [ 

1934 int(x) for x in _args_without_comment(data[2:]) 

1935 ]) 

1936 

1937 elif key in list_float_keys: 

1938 if key == 'magmom': 

1939 lst = [] 

1940 i = 2 

1941 while i < len(data): 

1942 if data[i] in ["#", "!"]: 

1943 break 

1944 if data[i] == "*": 

1945 b = lst.pop() 

1946 i += 1 

1947 for _ in range(int(b)): 

1948 lst.append(float(data[i])) 

1949 else: 

1950 lst.append(float(data[i])) 

1951 i += 1 

1952 self.set_if_none(self.list_float_params, 'magmom', lst) 

1953 lst = np.array(lst) 

1954 if self.atoms is not None: 

1955 self.atoms.set_initial_magnetic_moments( 

1956 lst[self.resort]) 

1957 else: 

1958 data = _args_without_comment(data) 

1959 self.set_if_none(self.list_float_params, key, [ 

1960 float(x) for x in data[2:] 

1961 ]) 

1962 elif key in special_keys: 

1963 if key == 'lreal': 

1964 # can't use set_if_none since value might be in one of 

1965 # two dicts 

1966 if (self.bool_params.get(key) is not None or 

1967 self.special_params.get(key) is not None): 

1968 continue 

1969 try: 

1970 val = _from_vasp_bool(data[2]) 

1971 self.bool_params[key] = val 

1972 except ValueError: 

1973 self.special_params[key] = data[2] 

1974 

1975 # non-registered keys 

1976 elif data[2].lower() in {'t', 'true', '.true.'}: 

1977 self.set_if_none(self.bool_params, key, True) 

1978 elif data[2].lower() in {'f', 'false', '.false.'}: 

1979 self.set_if_none(self.bool_params, key, False) 

1980 elif data[2].isdigit(): 

1981 self.set_if_none(self.int_params, key, int(data[2])) 

1982 else: 

1983 try: 

1984 self.set_if_none(self.float_params, key, float(data[2])) 

1985 except ValueError: 

1986 self.set_if_none(self.string_params, key, data[2]) 

1987 

1988 except KeyError as exc: 

1989 raise KeyError( 

1990 f'Keyword "{key}" in INCAR is not known by calculator.' 

1991 ) from exc 

1992 except IndexError as exc: 

1993 raise IndexError( 

1994 f'Value missing for keyword "{key}".' 

1995 ) from exc 

1996 

1997 def read_kpoints(self, filename): 

1998 """Read kpoints file, typically named KPOINTS.""" 

1999 # If we used VASP builtin kspacing, 

2000 if self.float_params['kspacing'] is not None: 

2001 # Don't update kpts array 

2002 return 

2003 

2004 with open(filename) as fd: 

2005 lines = fd.readlines() 

2006 

2007 ktype = lines[2].split()[0].lower()[0] 

2008 if ktype in ['g', 'm', 'a']: 

2009 if ktype == 'g': 

2010 self.set(gamma=True) 

2011 kpts = np.array([int(lines[3].split()[i]) for i in range(3)]) 

2012 elif ktype == 'a': 

2013 kpts = np.array([int(lines[3].split()[i]) for i in range(1)]) 

2014 elif ktype == 'm': 

2015 kpts = np.array([int(lines[3].split()[i]) for i in range(3)]) 

2016 else: 

2017 if ktype in ['c', 'k']: 

2018 self.set(reciprocal=False) 

2019 else: 

2020 self.set(reciprocal=True) 

2021 kpts = np.array( 

2022 [list(map(float, line.split())) for line in lines[3:]]) 

2023 self.set(kpts=kpts) 

2024 

2025 def read_potcar(self, filename): 

2026 """ Read the pseudopotential XC functional from POTCAR file. 

2027 """ 

2028 

2029 # Search for key 'LEXCH' in POTCAR 

2030 xc_flag = None 

2031 with open(filename) as fd: 

2032 for line in fd: 

2033 key = line.split()[0].upper() 

2034 if key == 'LEXCH': 

2035 xc_flag = line.split()[-1].upper() 

2036 break 

2037 

2038 if xc_flag is None: 

2039 raise ValueError('LEXCH flag not found in POTCAR file.') 

2040 

2041 # Values of parameter LEXCH and corresponding XC-functional 

2042 xc_dict = {'PE': 'PBE', '91': 'PW91', 'CA': 'LDA'} 

2043 

2044 if xc_flag not in xc_dict.keys(): 

2045 raise ValueError('Unknown xc-functional flag found in POTCAR,' 

2046 ' LEXCH=%s' % xc_flag) 

2047 

2048 self.input_params['pp'] = xc_dict[xc_flag] 

2049 

2050 def todict(self): 

2051 """Returns a dictionary of all parameters 

2052 that can be used to construct a new calculator object""" 

2053 dict_list = [ 

2054 'float_params', 'exp_params', 'string_params', 'int_params', 

2055 'bool_params', 'list_bool_params', 'list_int_params', 

2056 'list_float_params', 'special_params', 'dict_params', 

2057 'input_params' 

2058 ] 

2059 dct = {} 

2060 for item in dict_list: 

2061 dct.update(getattr(self, item)) 

2062 dct = {key: value for key, value in dct.items() if value is not None} 

2063 return dct 

2064 

2065 

2066def _args_without_comment(data, marks=['!', '#']): 

2067 """Check split arguments list for a comment, return data up to marker 

2068 

2069 INCAR reader splits list arguments on spaces and leaves comment markers as 

2070 individual items. This function returns only the data portion of the list. 

2071 

2072 """ 

2073 comment_locs = [data.index(mark) for mark in marks if mark in data] 

2074 if comment_locs == []: 

2075 return data 

2076 else: 

2077 return data[:min(comment_locs)] 

2078 

2079 

2080def _from_vasp_bool(x): 

2081 """Cast vasp boolean to Python bool 

2082 

2083 VASP files sometimes use T or F as shorthand for the preferred Boolean 

2084 notation .TRUE. or .FALSE. As capitalisation is pretty inconsistent in 

2085 practice, we allow all cases to be cast to a Python bool. 

2086 

2087 """ 

2088 assert isinstance(x, str) 

2089 if re.search(r'^\.?[tT]', x): 

2090 return True 

2091 elif re.search(r'^\.?[fF]', x): 

2092 return False 

2093 else: 

2094 raise ValueError(f'Value "{x}" not recognized as bool') 

2095 

2096 

2097def _to_vasp_bool(x): 

2098 """Convert Python boolean to string for VASP input 

2099 

2100 In case the value was modified to a string already, appropriate strings 

2101 will also be accepted and cast to a standard .TRUE. / .FALSE. format. 

2102 

2103 """ 

2104 if isinstance(x, str): 

2105 if x.lower() in ('.true.', 't'): 

2106 x = True 

2107 elif x.lower() in ('.false.', 'f'): 

2108 x = False 

2109 else: 

2110 raise ValueError('"%s" not recognised as VASP Boolean') 

2111 assert isinstance(x, bool) 

2112 if x: 

2113 return '.TRUE.' 

2114 else: 

2115 return '.FALSE.' 

2116 

2117 

2118def open_potcar(filename): 

2119 """ Open POTCAR file with transparent decompression if it's an archive (.Z) 

2120 """ 

2121 import gzip 

2122 if filename.endswith('R'): 

2123 return open(filename) 

2124 elif filename.endswith('.Z'): 

2125 return gzip.open(filename) 

2126 else: 

2127 raise ValueError(f'Invalid POTCAR filename: "{filename}"') 

2128 

2129 

2130def read_potcar_numbers_of_electrons(fd: TextIO, /) -> list[tuple[str, float]]: 

2131 """Read number of valence electrons for each atomtype from a POTCAR file. 

2132 

2133 Returns 

2134 ------- 

2135 list[tuple[str, float]] 

2136 List of (atomic symbol, number of valence electrons). 

2137 

2138 """ 

2139 nelect: list[tuple[str, float]] = [] 

2140 lines = fd.readlines() 

2141 for n, line in enumerate(lines): 

2142 if 'TITEL' in line: 

2143 symbol = line.split('=')[1].split()[1].split('_')[0].strip() 

2144 linep4 = lines[n + 4] 

2145 zval = float(linep4.split(';')[1].split('=')[1].split()[0].strip()) 

2146 nelect.append((symbol, zval)) 

2147 return nelect 

2148 

2149 

2150def count_symbols(atoms: Atoms, exclude=()) -> tuple[list[str], dict[str, int]]: 

2151 """Count symbols in atoms object, excluding a set of indices 

2152 

2153 Parameters: 

2154 atoms: Atoms object to be grouped 

2155 exclude: List of indices to be excluded from the counting 

2156 

2157 Returns: 

2158 Tuple of (symbols, symbolcount) 

2159 symbols: The unique symbols in the included list 

2160 symbolscount: Count of symbols in the included list 

2161 

2162 Example: 

2163 

2164 >>> from ase.build import bulk 

2165 >>> atoms = bulk('NaCl', crystalstructure='rocksalt', a=4.1, cubic=True) 

2166 >>> count_symbols(atoms) 

2167 (['Na', 'Cl'], {'Na': 4, 'Cl': 4}) 

2168 >>> count_symbols(atoms, exclude=(1, 2, 3)) 

2169 (['Na', 'Cl'], {'Na': 3, 'Cl': 2}) 

2170 """ 

2171 symbols: list[str] = [] 

2172 symbolcount: dict[str, int] = {} 

2173 for m, symbol in enumerate(atoms.symbols): 

2174 if m in exclude: 

2175 continue 

2176 if symbol not in symbols: 

2177 symbols.append(symbol) 

2178 symbolcount[symbol] = 1 

2179 else: 

2180 symbolcount[symbol] += 1 

2181 return symbols, symbolcount