Coverage for gpaw/setup_data.py: 94%

490 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-19 00:19 +0000

1from __future__ import annotations 

2 

3import hashlib 

4import os 

5import re 

6import xml.sax 

7from glob import glob 

8from math import pi, sqrt 

9from pathlib import Path 

10from typing import IO, Tuple 

11 

12import numpy as np 

13from ase.data import atomic_names, atomic_numbers 

14from ase.units import Bohr, Ha 

15 

16from gpaw import setup_paths 

17from gpaw.atom.radialgd import (AbinitRadialGridDescriptor, 

18 AERadialGridDescriptor) 

19from gpaw.atom.shapefunc import shape_functions 

20from gpaw.mpi import broadcast 

21from gpaw.xc.pawcorrection import PAWXCCorrection 

22 

23 

24class SetupData: 

25 """Container class for persistent setup attributes and XML I/O.""" 

26 def __init__(self, symbol, xcsetupname, 

27 name='paw', readxml=True, 

28 zero_reference=False, world=None, 

29 generator_version=None): 

30 self.symbol = symbol 

31 self.setupname = xcsetupname 

32 self.name = name 

33 self.zero_reference = zero_reference 

34 self.generator_version = generator_version 

35 

36 self.filename = None # full path if this setup was loaded from file 

37 self.fingerprint = None # hash value of file data if applicable 

38 

39 self.Z = None 

40 self.Nc = None 

41 self.Nv = None 

42 

43 # Quantum numbers, energies 

44 self.n_j = [] 

45 self.l_j = [] 

46 self.l_orb_J = self.l_j # pointer to same list! 

47 self.f_j = [] 

48 self.eps_j = [] 

49 self.e_kin_jj = None # <phi | T | phi> - <phit | T | phit> 

50 

51 self.rgd = None 

52 

53 # Parameters for compensation charge expansion functions: 

54 self.shape_function = {'type': 'undefined', 'rc': np.nan} 

55 

56 # State identifier, like "X-2s" or "X-p1", where X is chemical symbol, 

57 # for bound and unbound states 

58 self.id_j = [] 

59 

60 # Partial waves, projectors 

61 self.phi_jg = [] 

62 self.phit_jg = [] 

63 self.pt_jg = [] 

64 self.rcut_j = [] 

65 

66 # Densities, potentials 

67 self.nc_g = None 

68 self.nct_g = None 

69 self.nvt_g = None 

70 self.vbar_g = None 

71 self.vt_g = None 

72 

73 # Kinetic energy densities of core electrons 

74 self.tauc_g = None 

75 self.tauct_g = None 

76 

77 # Reference energies 

78 self.e_kinetic = 0.0 

79 self.e_xc = 0.0 

80 self.e_electrostatic = 0.0 

81 self.e_total = 0.0 

82 self.e_kinetic_core = 0.0 

83 

84 # Generator may store description of setup in these 

85 self.type = None 

86 self.generatorattrs = [] 

87 self.generatordata = '' 

88 

89 # Optional quantities, normally not used 

90 self.X_p = None 

91 self.X_wp = {} 

92 self.X_pg = None 

93 self.ExxC = None 

94 self.ExxC_w = {} 

95 self.X_gamma = None 

96 self.extra_xc_data = {} 

97 self.phicorehole_g = None 

98 self.fcorehole = 0.0 

99 self.lcorehole = None 

100 self.ncorehole = None 

101 self.core_hole_e = None 

102 self.core_hole_e_kin = None 

103 self.has_corehole = False 

104 

105 # Parameters for zero-potential: 

106 self.l0 = None 

107 self.e0 = None 

108 self.r0 = None 

109 self.nderiv0 = None 

110 

111 self.orbital_free = False # orbital-free DFT 

112 

113 self.version = None 

114 

115 if readxml: 

116 self.read_xml(world=world) 

117 

118 @classmethod 

119 def find_and_read_path(cls, symbol, xctype, 

120 setuptype='paw', world=None): 

121 

122 setupdata = SetupData(symbol, xctype, 

123 name=setuptype, 

124 readxml=False, 

125 world=world) 

126 

127 setupdata.filename, source = search_for_file(setupdata.stdfilename, 

128 world=world) 

129 PAWXMLParser(setupdata).parse(source=source, world=world) 

130 

131 nj = len(setupdata.l_j) 

132 setupdata.e_kin_jj.shape = (nj, nj) 

133 

134 return setupdata 

135 

136 @property 

137 def stdfilename(self): 

138 """Default filename if this setup is written.""" 

139 assert self.symbol is not None 

140 assert self.setupname is not None 

141 if self.name is None or self.name == 'paw': 

142 return f'{self.symbol}.{self.setupname}' 

143 else: 

144 return f'{self.symbol}.{self.name}.{self.setupname}' 

145 

146 def __repr__(self): 

147 return ('{0}({symbol!r}, {setupname!r}, name={name!r}, ' 

148 'generator_version={generator_version!r}, ...)' 

149 .format(self.__class__.__name__, **vars(self))) 

150 

151 def append(self, n, l, f, e, rcut, phi_g, phit_g, pt_g): 

152 self.n_j.append(n) 

153 self.l_j.append(l) 

154 self.f_j.append(f) 

155 self.eps_j.append(e) 

156 self.rcut_j.append(rcut) 

157 self.phi_jg.append(phi_g) 

158 self.phit_jg.append(phit_g) 

159 self.pt_jg.append(pt_g) 

160 

161 # XXX delete me 

162 def read_xml(self, source=None, world=None): 

163 PAWXMLParser(self).parse(source=source, world=world) 

164 nj = len(self.l_j) 

165 self.e_kin_jj.shape = (nj, nj) 

166 

167 def is_compatible(self, xc): 

168 return xc.get_setup_name() == self.setupname 

169 

170 def print_info(self, text, setup): 

171 if self.phicorehole_g is None: 

172 text(self.symbol + ':') 

173 else: 

174 text(f'{self.symbol}: # ({self.fcorehole:.1f} core hole)') 

175 text(' name:', atomic_names[atomic_numbers[self.symbol]]) 

176 text(' id:', self.fingerprint) 

177 text(' Z:', self.Z) 

178 text(' valence:', self.Nv) 

179 if self.phicorehole_g is None: 

180 text(' core: %d' % self.Nc) 

181 else: 

182 text(f' core: {self.Nc:.1f}') 

183 text(' charge:', self.Z - self.Nv - self.Nc) 

184 if setup.hubbard_u is not None: 

185 description = ''.join([f' {line}' for line 

186 in setup.hubbard_u.descriptions()]) 

187 text(description) 

188 text(' file:', self.filename) 

189 sf = self.shape_function 

190 text(f' compensation charges: {{type: {sf["type"]},\n' 

191 f' rc: {sf["rc"] * Bohr:.2f},\n' 

192 f' lmax: {setup.lmax}}}') 

193 text(f' cutoffs: {{filter: {setup.rcutfilter * Bohr:.2f},\n' 

194 f' core: {setup.rcore * Bohr:.2f}}}') 

195 text(' projectors:') 

196 text(' # energy rcut') 

197 j = 0 

198 for n, l, f, eps in zip(self.n_j, self.l_j, self.f_j, self.eps_j): 

199 if n > 0: 

200 f = f'({f:.2f})' 

201 text(' - %d%s%-5s %9.3f %5.3f' % ( 

202 n, 'spdf'[l], f, eps * Ha, self.rcut_j[j] * Bohr)) 

203 else: 

204 text(' - {} {:9.3f} {:5.3f}'.format( 

205 'spdf'[l], eps * Ha, self.rcut_j[j] * Bohr)) 

206 j += 1 

207 text() 

208 

209 def create_compensation_charge_functions(self, lmax): 

210 """Create shape functions used to expand compensation charges.""" 

211 g_lg = shape_functions(self.rgd, **self.shape_function, lmax=lmax) 

212 return g_lg 

213 

214 def get_smooth_core_density_integral(self, Delta0): 

215 return -Delta0 * sqrt(4 * pi) - self.Z + self.Nc 

216 

217 def get_overlap_correction(self, Delta0_ii): 

218 return sqrt(4.0 * pi) * Delta0_ii 

219 

220 def get_linear_kinetic_correction(self, T0_qp): 

221 e_kin_jj = self.e_kin_jj 

222 nj = len(e_kin_jj) 

223 K_q = [] 

224 for j1 in range(nj): 

225 for j2 in range(j1, nj): 

226 K_q.append(e_kin_jj[j1, j2]) 

227 K_p = sqrt(4 * pi) * np.dot(K_q, T0_qp) 

228 return K_p 

229 

230 def find_core_density_cutoff(self, nc_g): 

231 if self.Nc == 0: 

232 return 1.0 

233 else: 

234 rgd = self.rgd 

235 N = 0.0 

236 g = self.rgd.N - 1 

237 while N < 1e-7: 

238 N += sqrt(4 * pi) * nc_g[g] * rgd.r_g[g]**2 * rgd.dr_g[g] 

239 g -= 1 

240 return rgd.r_g[g] 

241 

242 def get_xc_correction(self, rgd, xc, gcut2, lcut): 

243 phicorehole_g = self.phicorehole_g 

244 if phicorehole_g is not None: 

245 phicorehole_g = phicorehole_g[:gcut2].copy() 

246 

247 xc_correction = PAWXCCorrection( 

248 [phi_g[:gcut2] for phi_g in self.phi_jg], 

249 [phit_g[:gcut2] for phit_g in self.phit_jg], 

250 self.nc_g[:gcut2] / sqrt(4 * pi), 

251 self.nct_g[:gcut2] / sqrt(4 * pi), 

252 rgd, 

253 list(enumerate(self.l_j)), 

254 min(2 * lcut, 4), 

255 self.e_xc, 

256 phicorehole_g, 

257 self.fcorehole, 

258 None if self.tauc_g is None else self.tauc_g[:gcut2].copy(), 

259 None if self.tauct_g is None else self.tauct_g[:gcut2].copy()) 

260 

261 return xc_correction 

262 

263 def write_xml(self, path=None) -> None: 

264 if path is None: 

265 path = self.stdfilename 

266 

267 with open(path, 'w') as fd: 

268 self._write_xml(fd) 

269 

270 def _write_xml(self, xml: IO[str]) -> None: 

271 l_j = self.l_j 

272 

273 print('<?xml version="1.0"?>', file=xml) 

274 print(f'<paw_dataset version="{self.version}">', 

275 file=xml) 

276 name = atomic_names[atomic_numbers[self.symbol]].title() 

277 comment1 = name + ' setup for the Projector Augmented Wave method.' 

278 comment2 = 'Units: Hartree and Bohr radii.' 

279 comment2 += ' ' * (len(comment1) - len(comment2)) 

280 print(' <!--', comment1, '-->', file=xml) 

281 print(' <!--', comment2, '-->', file=xml) 

282 

283 print((' <atom symbol="%s" Z="%r" core="%s" valence="%s"/>' % 

284 (self.symbol, self.Z, self.Nc, self.Nv)), file=xml) 

285 if self.orbital_free: 

286 type = 'OFDFT' 

287 name = self.setupname 

288 elif self.setupname == 'LDA': 

289 type = 'LDA' 

290 name = 'PW' 

291 else: 

292 type = 'GGA' 

293 name = self.setupname 

294 print(f' <xc_functional type="{type}" name="{name}"/>', 

295 file=xml) 

296 gen_attrs = ' '.join([f'{key}="{value}"' for key, value 

297 in self.generatorattrs]) 

298 print(f' <generator {gen_attrs}>', file=xml) 

299 print(f' {self.generatordata}', file=xml) 

300 print(' </generator>', file=xml) 

301 print(f' <ae_energy kinetic="{self.e_kinetic}" xc="{self.e_xc}"', 

302 file=xml) 

303 print(' electrostatic="%s" total="%s"/>' % 

304 (self.e_electrostatic, self.e_total), file=xml) 

305 

306 print(f' <core_energy kinetic="{self.e_kinetic_core}"/>', file=xml) 

307 print(' <valence_states>', file=xml) 

308 line1 = ' <state n="%d" l="%d" f="%s" rc="%s" e="%s" id="%s"/>' 

309 line2 = ' <state l="%d" rc="%s" e="%s" id="%s"/>' 

310 

311 for id, l, n, f, e, rc in zip(self.id_j, l_j, self.n_j, self.f_j, 

312 self.eps_j, self.rcut_j): 

313 if n > 0: 

314 print(line1 % (n, l, f, rc, e, id), file=xml) 

315 else: 

316 print(line2 % (l, rc, e, id), file=xml) 

317 print(' </valence_states>', file=xml) 

318 

319 print(self.rgd.xml('g1'), file=xml) 

320 

321 print(' <shape_function type="{type}" rc="{rc}"/>' 

322 .format(**self.shape_function), file=xml) 

323 

324 if self.r0 is None: 

325 # Old setups: 

326 xml.write(' <zero_potential grid="g1">\n') 

327 elif self.l0 is None: 

328 xml.write(' <zero_potential type="polynomial" ' + 

329 'nderiv="%d" r0="%r" grid="g1">\n' % 

330 (self.nderiv0, self.r0)) 

331 else: 

332 xml.write((' <zero_potential type="%s" ' + 

333 'e0="%r" nderiv="%d" r0="%r" grid="g1">\n') % 

334 ('spdfg'[self.l0], self.e0, self.nderiv0, self.r0)) 

335 

336 for x in self.vbar_g: 

337 print(x, end=' ', file=xml) 

338 print('\n </zero_potential>', file=xml) 

339 

340 if self.has_corehole: 

341 print(((' <core_hole_state state="%d%s" ' + 

342 'removed="%s" eig="%s" ekin="%s">') % 

343 (self.ncorehole, 'spdf'[self.lcorehole], 

344 self.fcorehole, 

345 self.core_hole_e, self.core_hole_e_kin)), file=xml) 

346 for x in self.phicorehole_g: 

347 print(x, end=' ', file=xml) 

348 print('\n </core_hole_state>', file=xml) 

349 

350 for name, a in [('ae_core_density', self.nc_g), 

351 ('pseudo_core_density', self.nct_g), 

352 ('ae_core_kinetic_energy_density', self.tauc_g), 

353 ('pseudo_core_kinetic_energy_density', self.tauct_g)]: 

354 print(f' <{name} grid="g1">\n ', end=' ', file=xml) 

355 for x in a: 

356 print(x, end=' ', file=xml) 

357 print(f'\n </{name}>', file=xml) 

358 

359 # Print xc-specific data to setup file (used so for KLI and GLLB) 

360 for name, a in self.extra_xc_data.items(): 

361 newname = 'GLLB_' + name 

362 print(f' <{newname} grid="g1">\n ', end=' ', file=xml) 

363 for x in a: 

364 print(x, end=' ', file=xml) 

365 print(f'\n </{newname}>', file=xml) 

366 

367 for id, l, u, s, q, in zip(self.id_j, l_j, self.phi_jg, self.phit_jg, 

368 self.pt_jg): 

369 for name, a in [('ae_partial_wave', u), 

370 ('pseudo_partial_wave', s), 

371 ('projector_function', q)]: 

372 print(f' <{name} state="{id}" grid="g1">\n ', 

373 end=' ', file=xml) 

374 for x in a: 

375 print(x, end=' ', file=xml) 

376 print(f'\n </{name}>', file=xml) 

377 

378 if self.vt_g is not None: 

379 xml.write(' <pseudo_potential grid="g1">\n') 

380 for x in self.vt_g: 

381 print(x, end=' ', file=xml) 

382 print('\n </pseudo_potential>', file=xml) 

383 

384 print(' <kinetic_energy_differences>', end=' ', file=xml) 

385 nj = len(self.e_kin_jj) 

386 for j1 in range(nj): 

387 print('\n ', end=' ', file=xml) 

388 for j2 in range(nj): 

389 print(self.e_kin_jj[j1, j2], end=' ', file=xml) 

390 print('\n </kinetic_energy_differences>', file=xml) 

391 

392 if self.X_p is not None: 

393 print(' <exact_exchange_X_matrix>\n ', end=' ', file=xml) 

394 for x in self.X_p: 

395 print(x, end=' ', file=xml) 

396 print('\n </exact_exchange_X_matrix>', file=xml) 

397 

398 print(f' <exact_exchange core-core="{self.ExxC}"/>', file=xml) 

399 for omega, Ecc in self.ExxC_w.items(): 

400 print(f' <erfc_exchange omega="{omega}" core-core="{Ecc}"/>', 

401 file=xml) 

402 print(f' <erfc_exchange_X_matrix omega="{omega}" X_p="', 

403 end=' ', file=xml) 

404 for x in self.X_wp[omega]: 

405 print(x, end=' ', file=xml) 

406 print('"/>', file=xml) 

407 

408 if self.X_pg is not None: 

409 print(' <yukawa_exchange_X_matrix>\n ', end=' ', file=xml) 

410 for x in self.X_pg: 

411 print(x, end=' ', file=xml) 

412 print('\n </yukawa_exchange_X_matrix>', file=xml) 

413 print(f' <yukawa_exchange gamma="{self.X_gamma}"/>', file=xml) 

414 print('</paw_dataset>', file=xml) 

415 

416 def build(self, xcfunc, lmax, basis, filter=None, 

417 backwards_compatible=True): 

418 from gpaw.setup import Setup 

419 setup = Setup(self, xcfunc, lmax, basis, filter, 

420 backwards_compatible=backwards_compatible) 

421 return setup 

422 

423 

424def read_maybe_unzipping(path: Path | str) -> bytes: 

425 import gzip 

426 if Path(path).suffix == '.gz': 

427 with gzip.open(path) as fd: 

428 return fd.read() 

429 

430 with open(path, 'rb') as fd: 

431 return fd.read() 

432 

433 

434def search_for_file(name: str, world=None) -> Tuple[str, bytes]: 

435 """Traverse gpaw setup paths to find file. 

436 

437 Returns the file path and file contents. If the file is not 

438 found, raises RuntimeError.""" 

439 

440 if world is None or world.rank == 0: 

441 source = b'' 

442 filename = None 

443 for path in setup_paths: 

444 pattern = os.path.join(path, name) 

445 filenames = glob(pattern) + glob(f'{pattern}.gz') 

446 if filenames: 

447 # The globbing is a hack to grab the 'newest' version if 

448 # the files are somehow version numbered; then we want the 

449 # last/newest of the results (used with SG15). (User must 

450 # instantiate (UPF)SetupData directly to override.) 

451 filename = max(filenames) 

452 source = read_maybe_unzipping(filename) 

453 break 

454 

455 if world is not None: 

456 if world.rank == 0: 

457 broadcast((filename, source), 0, world) 

458 else: 

459 filename, source = broadcast(None, 0, world) 

460 

461 if filename is None: 

462 if name.endswith('basis'): 

463 _type = 'basis set' 

464 else: 

465 _type = 'PAW dataset' 

466 err = f'Could not find required {_type} file "{name}".' 

467 helpful_message = """ 

468You need to set the GPAW_SETUP_PATH environment variable to point to 

469the directories where PAW dataset and basis files are stored. See 

470https://gpaw.readthedocs.io/install.html#install-paw-datasets 

471for details.""" 

472 raise FileNotFoundError(f'{err}\n{helpful_message}\n') 

473 

474 return filename, source 

475 

476 

477class PAWXMLParser(xml.sax.handler.ContentHandler): 

478 def __init__(self, setup): 

479 xml.sax.handler.ContentHandler.__init__(self) 

480 self.setup = setup 

481 self.id = None 

482 self.data = None 

483 

484 def parse(self, source=None, world=None): 

485 setup = self.setup 

486 if source is None: 

487 setup.filename, source = search_for_file(setup.stdfilename, world) 

488 

489 setup.fingerprint = hashlib.md5(source).hexdigest() 

490 

491 # XXXX There must be a better way! 

492 # We don't want to look at the dtd now. Remove it: 

493 source = re.compile(b'<!DOCTYPE .*?>', re.DOTALL).sub(b'', source, 1) 

494 xml.sax.parseString(source, self) 

495 

496 if setup.zero_reference: 

497 setup.e_total = 0.0 

498 setup.e_kinetic = 0.0 

499 setup.e_electrostatic = 0.0 

500 setup.e_xc = 0.0 

501 

502 def startElement(self, name, attrs): 

503 setup = self.setup 

504 if name == 'paw_setup' or name == 'paw_dataset': 

505 setup.version = attrs['version'] 

506 assert [int(v) for v in setup.version.split('.')] >= [0, 4] 

507 if name == 'atom': 

508 Z = float(attrs['Z']) 

509 setup.Z = Z 

510 assert setup.symbol is None or setup.symbol == attrs['symbol'] 

511 setup.symbol = attrs['symbol'] 

512 assert setup.Z == Z 

513 setup.Nc = float(attrs['core']) 

514 Nv = float(attrs['valence']) 

515 setup.Nv = int(Nv) 

516 assert setup.Nv == Nv 

517 elif name == 'xc_functional': 

518 if attrs['type'] == 'LDA': 

519 setup.xcname = 'LDA' 

520 else: 

521 setup.xcname = attrs['name'] 

522 if attrs['type'] == 'OFDFT': 

523 setup.orbital_free = True 

524 else: 

525 assert attrs['type'] == 'GGA' 

526 assert setup.setupname is None or setup.setupname == setup.xcname 

527 setup.setupname = setup.xcname 

528 elif name == 'ae_energy': 

529 setup.e_total = float(attrs['total']) 

530 setup.e_kinetic = float(attrs['kinetic']) 

531 setup.e_electrostatic = float(attrs['electrostatic']) 

532 setup.e_xc = float(attrs['xc']) 

533 elif name == 'core_energy': 

534 setup.e_kinetic_core = float(attrs['kinetic']) 

535 elif name == 'state': 

536 setup.n_j.append(int(attrs.get('n', -1))) 

537 setup.l_j.append(int(attrs['l'])) 

538 setup.f_j.append(float(attrs.get('f', 0))) 

539 setup.eps_j.append(float(attrs['e'])) 

540 setup.rcut_j.append(float(attrs.get('rc', -1))) 

541 setup.id_j.append(attrs['id']) 

542 # Compatibility with old setups: 

543 version = [int(v) for v in setup.version.split('.')] 

544 if version < [0, 6] and setup.f_j[-1] == 0: 

545 setup.n_j[-1] = -1 

546 elif name == 'radial_grid': 

547 if attrs['eq'] == 'r=a*i/(n-i)': 

548 beta = float(attrs['a']) 

549 ng = int(attrs['n']) 

550 setup.rgd = AERadialGridDescriptor(beta / ng, 1.0 / ng, ng) 

551 elif attrs['eq'] == 'r=a*i/(1-b*i)': 

552 a = float(attrs['a']) 

553 b = float(attrs['b']) 

554 N = int(attrs['n']) 

555 setup.rgd = AERadialGridDescriptor(a, b, N) 

556 elif attrs['eq'] == 'r=a*(exp(d*i)-1)': 

557 a = float(attrs['a']) 

558 d = float(attrs['d']) 

559 istart = int(attrs['istart']) 

560 iend = int(attrs['iend']) 

561 assert istart == 0 

562 setup.rgd = AbinitRadialGridDescriptor(a, d, iend + 1) 

563 else: 

564 raise ValueError('Unknown grid:' + attrs['eq']) 

565 elif name == 'shape_function': 

566 assert attrs['type'] in {'gauss', 'sinc', 'bessel'} 

567 setup.shape_function = {'type': attrs['type'], 

568 'rc': float(attrs['rc'])} 

569 elif name in ['ae_core_density', 'pseudo_core_density', 

570 'localized_potential', 'yukawa_exchange_X_matrix', 

571 'kinetic_energy_differences', 'exact_exchange_X_matrix', 

572 'ae_core_kinetic_energy_density', 

573 'pseudo_core_kinetic_energy_density', 

574 'pseudo_potential']: 

575 self.data = [] 

576 elif name.startswith('GLLB_'): 

577 self.data = [] 

578 elif name in ['ae_partial_wave', 'pseudo_partial_wave']: 

579 self.data = [] 

580 self.id = attrs['state'] 

581 elif name == 'projector_function': 

582 self.id = attrs['state'] 

583 self.data = [] 

584 elif name == 'erfc_exchange': 

585 setup.ExxC_w[float(attrs['omega'])] = float(attrs['core-core']) 

586 elif name == 'exact_exchange': 

587 setup.ExxC = float(attrs['core-core']) 

588 elif name == 'erfc_exchange_X_matrix': 

589 X_p = np.array([float(x) for x in ''.join(attrs['X_p']).split()]) 

590 setup.X_wp[float(attrs['omega'])] = X_p 

591 elif name == 'yukawa_exchange': 

592 setup.X_gamma = float(attrs['gamma']) 

593 elif name == 'core_hole_state': 

594 setup.has_corehole = True 

595 setup.fcorehole = float(attrs['removed']) 

596 full_state = attrs['state'] 

597 state_l = full_state.lstrip('0123456789') 

598 assert state_l 

599 state_n = full_state[:-len(state_l)] 

600 assert state_n 

601 setup.ncorehole = int(state_n) 

602 setup.lcorehole = 'spdf'.find(state_l) 

603 setup.core_hole_e = float(attrs['eig']) 

604 setup.core_hole_e_kin = float(attrs['ekin']) 

605 self.data = [] 

606 elif name == 'zero_potential': 

607 if 'type' in attrs: 

608 setup.r0 = float(attrs['r0']) 

609 setup.nderiv0 = int(attrs['nderiv']) 

610 if attrs['type'] == 'polynomial': 

611 setup.e0 = None 

612 setup.l0 = None 

613 else: 

614 setup.e0 = float(attrs['e0']) 

615 setup.l0 = 'spdfg'.find(attrs['type']) 

616 self.data = [] 

617 elif name == 'generator': 

618 setup.type = attrs['type'] 

619 setup.generator_version = int(attrs.get('version', '1')) 

620 else: 

621 self.data = None 

622 

623 def characters(self, data): 

624 if self.data is not None: 

625 self.data.append(data) 

626 

627 def endElement(self, name): 

628 setup = self.setup 

629 if self.data is None: 

630 return 

631 x_g = np.array([float(x) for x in ''.join(self.data).split()]) 

632 if name == 'ae_core_density': 

633 setup.nc_g = x_g 

634 elif name == 'pseudo_core_density': 

635 setup.nct_g = x_g 

636 elif name == 'kinetic_energy_differences': 

637 setup.e_kin_jj = x_g 

638 elif name == 'ae_core_kinetic_energy_density': 

639 setup.tauc_g = x_g 

640 elif name == 'pseudo_valence_density': 

641 setup.nvt_g = x_g 

642 elif name == 'pseudo_core_kinetic_energy_density': 

643 setup.tauct_g = x_g 

644 elif name in ['localized_potential', 'zero_potential']: # XXX 

645 setup.vbar_g = x_g 

646 elif name in ['pseudo_potential']: 

647 setup.vt_g = x_g 

648 elif name.startswith('GLLB_'): 

649 # Add setup tags starting with GLLB_ to extra_xc_data. Remove 

650 # GLLB_ from front of string: 

651 if name == 'GLLB_w_j': 

652 v1, v2 = (int(x) for x in self.setup.version.split('.')) 

653 if (v1, v2) < (0, 8): 

654 # Order was wrong in old generator: 

655 w_j = {} 

656 j_old = 0 

657 for l in range(4): 

658 for j_new, (n1, l1) in enumerate(zip(setup.n_j, 

659 setup.l_j)): 

660 if l == l1: 

661 w_j[j_new] = x_g[j_old] 

662 j_old += 1 

663 x_g = [w_j[j] for j in range(len(w_j))] 

664 setup.extra_xc_data[name[5:]] = x_g 

665 

666 elif name == 'ae_partial_wave': 

667 j = len(setup.phi_jg) 

668 assert self.id == setup.id_j[j] 

669 setup.phi_jg.append(x_g) 

670 elif name == 'pseudo_partial_wave': 

671 j = len(setup.phit_jg) 

672 assert self.id == setup.id_j[j] 

673 setup.phit_jg.append(x_g) 

674 elif name == 'projector_function': 

675 j = len(setup.pt_jg) 

676 assert self.id == setup.id_j[j] 

677 setup.pt_jg.append(x_g) 

678 elif name == 'exact_exchange_X_matrix': 

679 setup.X_p = x_g 

680 elif name == 'yukawa_exchange_X_matrix': 

681 setup.X_pg = x_g 

682 elif name == 'core_hole_state': 

683 setup.phicorehole_g = x_g