Coverage for /builds/debichem-team/python-ase/ase/calculators/openmx/openmx.py: 27.33%

450 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-03-06 04:00 +0000

1""" 

2 The ASE Calculator for OpenMX <http://www.openmx-square.org> 

3 A Python interface to the software package for nano-scale 

4 material simulations based on density functional theories. 

5 Copyright (C) 2017 Charles Thomas Johnson, Jae Hwan Shim and JaeJun Yu 

6 

7 This program is free software: you can redistribute it and/or modify 

8 it under the terms of the GNU Lesser General Public License as published by 

9 the Free Software Foundation, either version 2.1 of the License, or 

10 (at your option) any later version. 

11 

12 This program is distributed in the hope that it will be useful, 

13 but WITHOUT ANY WARRANTY; without even the implied warranty of 

14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

15 GNU Lesser General Public License for more details. 

16 

17 You should have received a copy of the GNU Lesser General Public License 

18 along with ASE. If not, see <http://www.gnu.org/licenses/>. 

19 

20""" 

21 

22import os 

23import re 

24import subprocess 

25import time 

26import warnings 

27 

28import numpy as np 

29 

30from ase.calculators.calculator import ( 

31 Calculator, 

32 FileIOCalculator, 

33 all_changes, 

34 equal, 

35 kptdensity2monkhorstpack, 

36) 

37from ase.calculators.openmx.default_settings import default_dictionary 

38from ase.calculators.openmx.parameters import OpenMXParameters 

39from ase.calculators.openmx.reader import get_file_name, read_openmx 

40from ase.calculators.openmx.writer import write_openmx 

41from ase.config import cfg 

42from ase.geometry import cell_to_cellpar 

43 

44 

45def parse_omx_version(txt): 

46 """Parse version number from stdout header.""" 

47 match = re.search(r'Welcome to OpenMX\s+Ver\.\s+(\S+)', txt, re.M) 

48 return match.group(1) 

49 

50 

51class OpenMX(FileIOCalculator): 

52 """ 

53 Calculator interface to the OpenMX code. 

54 """ 

55 

56 implemented_properties = [ 

57 'free_energy', # Same value with energy 

58 'energy', 

59 'energies', 

60 'forces', 

61 'stress', 

62 'dipole', 

63 'chemical_potential', 

64 'magmom', 

65 'magmoms', 

66 'eigenvalues'] 

67 

68 default_parameters = OpenMXParameters() 

69 

70 default_pbs = { 

71 'processes': 1, 

72 'walltime': "10:00:00", 

73 'threads': 1, 

74 'nodes': 1 

75 } 

76 

77 default_mpi = { 

78 'processes': 1, 

79 'threads': 1 

80 } 

81 

82 default_output_setting = { 

83 'nohup': True, 

84 'debug': False 

85 } 

86 

87 def __init__(self, restart=None, 

88 ignore_bad_restart_file=FileIOCalculator._deprecated, 

89 label='./openmx', atoms=None, command=None, mpi=None, 

90 pbs=None, **kwargs): 

91 

92 # Initialize and put the default parameters. 

93 self.initialize_pbs(pbs) 

94 self.initialize_mpi(mpi) 

95 self.initialize_output_setting(**kwargs) 

96 

97 FileIOCalculator.__init__(self, restart, ignore_bad_restart_file, 

98 label, atoms, command, **kwargs) 

99 

100 def __getitem__(self, key): 

101 """Convenience method to retrieve a parameter as 

102 calculator[key] rather than calculator.parameters[key] 

103 

104 Parameters: 

105 -key : str, the name of the parameters to get. 

106 """ 

107 return self.parameters[key] 

108 

109 def __setitem__(self, key, value): 

110 self.parameters[key] = value 

111 

112 def initialize_output_setting(self, **kwargs): 

113 output_setting = {} 

114 self.output_setting = dict(self.default_output_setting) 

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

116 if key in self.default_output_setting: 

117 output_setting[key] = value 

118 self.output_setting.update(output_setting) 

119 self.__dict__.update(self.output_setting) 

120 

121 def initialize_pbs(self, pbs): 

122 if pbs: 

123 self.pbs = dict(self.default_pbs) 

124 for key in pbs: 

125 if key not in self.default_pbs: 

126 allowed = ', '.join(list(self.default_pbs.keys())) 

127 raise TypeError('Unexpected keyword "{}" in "pbs" ' 

128 'dictionary. Must be one of: {}' 

129 .format(key, allowed)) 

130 # Put dictionary into python variable 

131 self.pbs.update(pbs) 

132 self.__dict__.update(self.pbs) 

133 else: 

134 self.pbs = None 

135 

136 def initialize_mpi(self, mpi): 

137 if mpi: 

138 self.mpi = dict(self.default_mpi) 

139 for key in mpi: 

140 if key not in self.default_mpi: 

141 allowed = ', '.join(list(self.default_mpi.keys())) 

142 raise TypeError('Unexpected keyword "{}" in "mpi" ' 

143 'dictionary. Must be one of: {}' 

144 .format(key, allowed)) 

145 # Put dictionary into python variable 

146 self.mpi.update(mpi) 

147 self.__dict__.update(self.mpi) 

148 else: 

149 self.mpi = None 

150 

151 def run(self): 

152 '''Check Which Running method we r going to use and run it''' 

153 if self.pbs is not None: 

154 run = self.run_pbs 

155 elif self.mpi is not None: 

156 run = self.run_mpi 

157 else: 

158 run = self.run_openmx 

159 run() 

160 

161 def run_openmx(self): 

162 def isRunning(process=None): 

163 ''' Check mpi is running''' 

164 return process.poll() is None 

165 runfile = get_file_name('.dat', self.label, absolute_directory=False) 

166 outfile = get_file_name('.log', self.label) 

167 olddir = os.getcwd() 

168 abs_dir = os.path.join(olddir, self.directory) 

169 try: 

170 os.chdir(abs_dir) 

171 if self.command is None: 

172 self.command = 'openmx' 

173 command = self.command + ' %s > %s' 

174 command = command % (runfile, outfile) 

175 self.prind(command) 

176 p = subprocess.Popen(command, shell=True, universal_newlines=True) 

177 self.print_file(file=outfile, running=isRunning, process=p) 

178 finally: 

179 os.chdir(olddir) 

180 self.prind("Calculation Finished") 

181 

182 def run_mpi(self): 

183 """ 

184 Run openmx using MPI method. If keyword `mpi` is declared, it will 

185 run. 

186 """ 

187 def isRunning(process=None): 

188 ''' Check mpi is running''' 

189 return process.poll() is None 

190 processes = self.processes 

191 threads = self.threads 

192 runfile = get_file_name('.dat', self.label, absolute_directory=False) 

193 outfile = get_file_name('.log', self.label) 

194 olddir = os.getcwd() 

195 abs_dir = os.path.join(olddir, self.directory) 

196 try: 

197 os.chdir(abs_dir) 

198 command = self.get_command(processes, threads, runfile, outfile) 

199 self.prind(command) 

200 p = subprocess.Popen(command, shell=True, universal_newlines=True) 

201 self.print_file(file=outfile, running=isRunning, process=p) 

202 finally: 

203 os.chdir(olddir) 

204 self.prind("Calculation Finished") 

205 

206 def run_pbs(self, prefix='test'): 

207 """ 

208 Execute the OpenMX using Plane Batch System. In order to use this, 

209 Your system should have Scheduler. PBS 

210 Basically, it does qsub. and wait until qstat signal shows c 

211 Super computer user 

212 """ 

213 nodes = self.nodes 

214 processes = self.processes 

215 

216 prefix = self.prefix 

217 olddir = os.getcwd() 

218 try: 

219 os.chdir(self.abs_directory) 

220 except AttributeError: 

221 os.chdir(self.directory) 

222 

223 def isRunning(jobNum=None, status='Q', qstat='qstat'): 

224 """ 

225 Check submitted job is still Running 

226 """ 

227 def runCmd(exe): 

228 p = subprocess.Popen(exe, stdout=subprocess.PIPE, 

229 stderr=subprocess.STDOUT, 

230 universal_newlines=True) 

231 while True: 

232 line = p.stdout.readline() 

233 if line != '': 

234 # the real code does filtering here 

235 yield line.rstrip() 

236 else: 

237 break 

238 jobs = runCmd('qstat') 

239 columns = None 

240 for line in jobs: 

241 if str(jobNum) in line: 

242 columns = line.split() 

243 self.prind(line) 

244 if columns is not None: 

245 return columns[-2] == status 

246 else: 

247 return False 

248 

249 inputfile = self.label + '.dat' 

250 outfile = self.label + '.log' 

251 

252 bashArgs = "#!/bin/bash \n cd $PBS_O_WORKDIR\n" 

253 jobName = prefix 

254 cmd = bashArgs + \ 

255 'mpirun -hostfile $PBS_NODEFILE openmx {} > {}'.format( 

256 inputfile, outfile) 

257 echoArgs = ["echo", f"$' {cmd}'"] 

258 qsubArgs = ["qsub", "-N", jobName, "-l", "nodes=%d:ppn=%d" % 

259 (nodes, processes), "-l", "walltime=" + self.walltime] 

260 wholeCmd = " ".join(echoArgs) + " | " + " ".join(qsubArgs) 

261 self.prind(wholeCmd) 

262 out = subprocess.Popen(wholeCmd, shell=True, 

263 stdout=subprocess.PIPE, universal_newlines=True) 

264 out = out.communicate()[0] 

265 jobNum = int(re.match(r'(\d+)', out.split()[0]).group(1)) 

266 

267 self.prind('Queue number is ' + str(jobNum) + 

268 '\nWaiting for the Queue to start') 

269 while isRunning(jobNum, status='Q'): 

270 time.sleep(5) 

271 self.prind('.') 

272 self.prind('Start Calculating') 

273 self.print_file(file=outfile, running=isRunning, 

274 jobNum=jobNum, status='R', qstat='qstat') 

275 

276 os.chdir(olddir) 

277 self.prind('Calculation Finished!') 

278 return jobNum 

279 

280 def clean(self, prefix='test', queue_num=None): 

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

282 

283 The default files generated OpenMX will be deleted IF this 

284 method is called. 

285 

286 """ 

287 self.prind("Cleaning Data") 

288 fileName = get_file_name('', self.label) 

289 pbs_Name = get_file_name('', self.label) 

290 files = [ 

291 # prefix+'.out',#prefix+'.dat',#prefix+'.BAND*', 

292 fileName + '.cif', 

293 fileName + '.dden.cube', 

294 fileName + '.ene', 

295 fileName + '.md', 

296 fileName + '.md2', 

297 fileName + '.tden.cube', 

298 fileName + '.sden.cube', 

299 fileName + '.v0.cube', 

300 fileName + '.v1.cube', 

301 fileName + '.vhart.cube', 

302 fileName + '.den0.cube', 

303 fileName + '.bulk.xyz', 

304 fileName + '.den1.cube', 

305 fileName + '.xyz', 

306 pbs_Name + '.o' + str(queue_num), 

307 pbs_Name + '.e' + str(queue_num) 

308 ] 

309 for f in files: 

310 try: 

311 self.prind("Removing" + f) 

312 os.remove(f) 

313 except OSError: 

314 self.prind("There is no such file named " + f) 

315 

316 def calculate(self, atoms=None, properties=None, 

317 system_changes=all_changes): 

318 """ 

319 Capture the RuntimeError from FileIOCalculator.calculate 

320 and add a little debug information from the OpenMX output. 

321 See base FileIOCalculator for documentation. 

322 """ 

323 if self.parameters.data_path is None: 

324 if 'OPENMX_DFT_DATA_PATH' not in cfg: 

325 warnings.warn('Please either set OPENMX_DFT_DATA_PATH as an' 

326 'enviroment variable or specify "data_path" as' 

327 'a keyword argument') 

328 

329 self.prind("Start Calculation") 

330 if properties is None: 

331 properties = self.implemented_properties 

332 try: 

333 Calculator.calculate(self, atoms, properties, system_changes) 

334 self.write_input(atoms=self.atoms, parameters=self.parameters, 

335 properties=properties, 

336 system_changes=system_changes) 

337 self.print_input(debug=self.debug, nohup=self.nohup) 

338 self.run() 

339 # self.read_results() 

340 self.version = self.read_version() 

341 output_atoms = read_openmx(filename=self.label, debug=self.debug) 

342 self.output_atoms = output_atoms 

343 # XXX The parameters are supposedly inputs, so it is dangerous 

344 # to update them from the outputs. --askhl 

345 self.parameters.update(output_atoms.calc.parameters) 

346 self.results = output_atoms.calc.results 

347 # self.clean() 

348 except RuntimeError as e: 

349 try: 

350 with open(get_file_name('.log')) as fd: 

351 lines = fd.readlines() 

352 debug_lines = 10 

353 print('##### %d last lines of the OpenMX output' % debug_lines) 

354 for line in lines[-20:]: 

355 print(line.strip()) 

356 print('##### end of openMX output') 

357 raise e 

358 except RuntimeError as e: 

359 raise e 

360 

361 def write_input(self, atoms=None, parameters=None, 

362 properties=[], system_changes=[]): 

363 """Write input (dat)-file. 

364 See calculator.py for further details. 

365 

366 Parameters: 

367 - atoms : The Atoms object to write. 

368 - properties : The properties which should be calculated. 

369 - system_changes : List of properties changed since last run. 

370 """ 

371 # Call base calculator. 

372 if atoms is None: 

373 atoms = self.atoms 

374 FileIOCalculator.write_input(self, atoms, properties, system_changes) 

375 write_openmx(label=self.label, atoms=atoms, parameters=self.parameters, 

376 properties=properties, system_changes=system_changes) 

377 

378 def print_input(self, debug=None, nohup=None): 

379 """ 

380 For a debugging purpose, print the .dat file 

381 """ 

382 if debug is None: 

383 debug = self.debug 

384 if nohup is None: 

385 nohup = self.nohup 

386 self.prind('Reading input file' + self.label) 

387 filename = get_file_name('.dat', self.label) 

388 if not nohup: 

389 with open(filename) as fd: 

390 while True: 

391 line = fd.readline() 

392 print(line.strip()) 

393 if not line: 

394 break 

395 

396 def read(self, label): 

397 self.parameters = {} 

398 self.set_label(label) 

399 if label[-5:] in ['.dat', '.out', '.log']: 

400 label = label[:-4] 

401 atoms = read_openmx(filename=label, debug=self.debug) 

402 self.update_atoms(atoms) 

403 self.parameters.update(atoms.calc.parameters) 

404 self.results = atoms.calc.results 

405 self.parameters['restart'] = self.label 

406 self.parameters['label'] = label 

407 

408 def read_version(self, label=None): 

409 version = None 

410 if label is None: 

411 label = self.label 

412 for line in open(get_file_name('.log', label)): 

413 if line.find('Ver.') != -1: 

414 version = line.split()[-1] 

415 break 

416 return version 

417 

418 def update_atoms(self, atoms): 

419 self.atoms = atoms.copy() 

420 

421 def set(self, **kwargs): 

422 """Set all parameters. 

423 

424 Parameters: 

425 -kwargs : Dictionary containing the keywords defined in 

426 OpenMXParameters. 

427 """ 

428 

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

430 if key not in self.default_parameters.keys(): 

431 raise KeyError(f'Unkown keyword "{key}" and value "{value}".') 

432 if key == 'xc' and value not in self.default_parameters.allowed_xc: 

433 raise KeyError(f'Given xc "{value}" is not allowed') 

434 if key in ['dat_arguments'] and isinstance(value, dict): 

435 # For values that are dictionaries, verify subkeys, too. 

436 default_dict = self.default_parameters[key] 

437 for subkey in kwargs[key]: 

438 if subkey not in default_dict: 

439 allowed = ', '.join(list(default_dict.keys())) 

440 raise TypeError('Unknown subkeyword "{}" of keyword ' 

441 '"{}". Must be one of: {}' 

442 .format(subkey, key, allowed)) 

443 

444 # Find out what parameter has been changed 

445 changed_parameters = {} 

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

447 oldvalue = self.parameters.get(key) 

448 if key not in self.parameters or not equal(value, oldvalue): 

449 changed_parameters[key] = value 

450 self.parameters[key] = value 

451 

452 # Set the parameters 

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

454 # print(' Setting the %s as %s'%(key, value)) 

455 self.parameters[key] = value 

456 

457 # If Changed Parameter is Critical, we have to reset the results 

458 for key, value in changed_parameters.items(): 

459 if key in ['xc', 'kpts', 'energy_cutoff']: 

460 self.results = {} 

461 

462 value = kwargs.get('energy_cutoff') 

463 if value is not None and not (isinstance(value, (float, int)) 

464 and value > 0): 

465 mess = "'{}' must be a positive number(in eV), \ 

466 got '{}'".format('energy_cutoff', value) 

467 raise ValueError(mess) 

468 

469 atoms = kwargs.get('atoms') 

470 if atoms is not None and self.atoms is None: 

471 self.atoms = atoms.copy() 

472 

473 def set_results(self, results): 

474 # Not Implemented fully 

475 self.results.update(results) 

476 

477 def get_command(self, processes, threads, runfile=None, outfile=None): 

478 # Contruct the command to send to the operating system 

479 abs_dir = os.getcwd() 

480 command = '' 

481 self.prind(self.command) 

482 if self.command is None: 

483 self.command = 'openmx' 

484 # run processes specified by the system variable OPENMX_COMMAND 

485 if processes is None: 

486 command += cfg.get('OPENMX_COMMAND') 

487 if command is None: 

488 warnings.warn('Either specify OPENMX_COMMAND as an environment\ 

489 variable or specify processes as a keyword argument') 

490 else: # run with a specified number of processes 

491 threads_string = ' -nt ' + str(threads) 

492 if threads is None: 

493 threads_string = '' 

494 command += 'mpirun -np ' + \ 

495 str(processes) + ' ' + self.command + \ 

496 ' %s ' + threads_string + ' |tee %s' 

497 # str(processes) + ' openmx %s' + threads_string + ' > %s' 

498 

499 if runfile is None: 

500 runfile = os.path.join(abs_dir, f'{self.prefix} .dat') 

501 if outfile is None: 

502 outfile = os.path.join(abs_dir, f'{self.prefix} .log') 

503 try: 

504 command = command % (runfile, outfile) 

505 # command += '" > ./%s &' % outfile # outputs 

506 except TypeError: # in case the OPENMX_COMMAND is incompatible 

507 raise ValueError( 

508 "The 'OPENMX_COMMAND' environment must " + 

509 "be a format string" + 

510 " with four string arguments.\n" + 

511 "Example : 'mpirun -np 4 openmx ./%s -nt 2 > ./%s'.\n" + 

512 f"Got '{command}'") 

513 return command 

514 

515 def get_stress(self, atoms=None): 

516 if atoms is None: 

517 atoms = self.atoms 

518 

519 # Note: Stress is only supported from OpenMX 3.8+. 

520 stress = self.get_property('stress', atoms) 

521 

522 return stress 

523 

524 def get_band_structure(self, atoms=None, calc=None): 

525 """ 

526 This is band structure function. It is compatible to 

527 ase dft module """ 

528 from ase.dft import band_structure 

529 if isinstance(self['kpts'], tuple): 

530 self['kpts'] = self.get_kpoints(band_kpath=self['band_kpath']) 

531 return band_structure.get_band_structure(self.atoms, self, ) 

532 

533 def get_bz_k_points(self): 

534 kgrid = self['kpts'] 

535 if type(kgrid) in [int, float]: 

536 kgrid = kptdensity2monkhorstpack(self.atoms, kgrid, False) 

537 bz_k_points = [] 

538 n1 = kgrid[0] 

539 n2 = kgrid[1] 

540 n3 = kgrid[2] 

541 for i in range(n1): 

542 for j in range(n2): 

543 # Monkhorst Pack Grid [H.J. Monkhorst and J.D. Pack, 

544 # Phys. Rev. B 13, 5188 (1976)] 

545 for k in range(n3): 

546 bz_k_points.append((0.5 * float(2 * i - n1 + 1) / n1, 

547 0.5 * float(2 * j - n2 + 1) / n2, 

548 0.5 * float(2 * k - n3 + 1) / n3)) 

549 return np.array(bz_k_points) 

550 

551 def get_ibz_k_points(self): 

552 if self['band_kpath'] is None: 

553 return self.get_bz_k_points() 

554 else: 

555 return self.get_kpoints(band_kpath=self['band_kpath']) 

556 

557 def get_kpoints(self, kpts=None, symbols=None, band_kpath=None, eps=1e-5): 

558 """Convert band_kpath <-> kpts""" 

559 if kpts is None: 

560 kpts = [] 

561 band_kpath = np.array(band_kpath) 

562 band_nkpath = len(band_kpath) 

563 for i, kpath in enumerate(band_kpath): 

564 end = False 

565 nband = int(kpath[0]) 

566 if band_nkpath == i: 

567 end = True 

568 nband += 1 

569 ini = np.array(kpath[1:4], dtype=float) 

570 fin = np.array(kpath[4:7], dtype=float) 

571 x = np.linspace(ini[0], fin[0], nband, endpoint=end) 

572 y = np.linspace(ini[1], fin[1], nband, endpoint=end) 

573 z = np.linspace(ini[2], fin[2], nband, endpoint=end) 

574 kpts.extend(np.array([x, y, z]).T) 

575 return np.array(kpts, dtype=float) 

576 elif band_kpath is None: 

577 band_kpath = [] 

578 points = np.asarray(kpts) 

579 diffs = points[1:] - points[:-1] 

580 kinks = abs(diffs[1:] - diffs[:-1]).sum(1) > eps 

581 N = len(points) 

582 indices = [0] 

583 indices.extend(np.arange(1, N - 1)[kinks]) 

584 indices.append(N - 1) 

585 for start, end, s_sym, e_sym in zip(indices[1:], indices[:-1], 

586 symbols[1:], symbols[:-1]): 

587 band_kpath.append({'start_point': start, 'end_point': end, 

588 'kpts': 20, 

589 'path_symbols': (s_sym, e_sym)}) 

590 return band_kpath 

591 

592 def get_lattice_type(self): 

593 cellpar = cell_to_cellpar(self.atoms.cell) 

594 abc = cellpar[:3] 

595 angles = cellpar[3:] 

596 min_lv = min(abc) 

597 if np.ptp(abc) < 0.01 * min_lv: 

598 if abs(angles - 90).max() < 1: 

599 return 'cubic' 

600 elif abs(angles - 60).max() < 1: 

601 return 'fcc' 

602 elif abs(angles - np.arccos(-1 / 3.) * 180 / np.pi).max < 1: 

603 return 'bcc' 

604 elif abs(angles - 90).max() < 1: 

605 if abs(abc[0] - abc[1]).min() < 0.01 * min_lv: 

606 return 'tetragonal' 

607 else: 

608 return 'orthorhombic' 

609 elif abs(abc[0] - abc[1]) < 0.01 * min_lv and \ 

610 abs(angles[2] - 120) < 1 and abs(angles[:2] - 90).max() < 1: 

611 return 'hexagonal' 

612 else: 

613 return 'not special' 

614 

615 def get_number_of_spins(self): 

616 try: 

617 magmoms = self.atoms.get_initial_magnetic_moments() 

618 if self['scf_spinpolarization'] is None: 

619 if isinstance(magmoms[0], float): 

620 if abs(magmoms).max() < 0.1: 

621 return 1 

622 else: 

623 return 2 

624 else: 

625 raise NotImplementedError 

626 else: 

627 if self['scf_spinpolarization'] == 'on': 

628 return 2 

629 elif self['scf_spinpolarization'] == 'nc' or \ 

630 np.any(self['initial_magnetic_moments_euler_angles']) \ 

631 is not None: 

632 return 1 

633 except KeyError: 

634 return 1 

635 

636 def get_eigenvalues(self, kpt=None, spin=None): 

637 if self.results.get('eigenvalues') is None: 

638 self.calculate(self.atoms) 

639 if kpt is None and spin is None: 

640 return self.results['eigenvalues'] 

641 else: 

642 return self.results['eigenvalues'][spin, kpt, :] 

643 

644 def get_fermi_level(self): 

645 try: 

646 fermi_level = self.results['chemical_potential'] 

647 except KeyError: 

648 self.calculate() 

649 fermi_level = self.results['chemical_potential'] 

650 return fermi_level 

651 

652 def get_number_of_bands(self): 

653 pag = self.parameters.get 

654 dfd = default_dictionary 

655 if 'number_of_bands' not in self.results: 

656 n = 0 

657 for atom in self.atoms: 

658 sym = atom.symbol 

659 orbitals = pag('dft_data_dict', dfd)[sym]['orbitals used'] 

660 d = 1 

661 for orbital in orbitals: 

662 n += d * orbital 

663 d += 2 

664 self.results['number_of_bands'] = n 

665 return self.results['number_of_bands'] 

666 

667 def dirG(self, dk, bzone=(0, 0, 0)): 

668 nx, ny, nz = self['wannier_kpts'] 

669 dx = dk // (ny * nz) + bzone[0] * nx 

670 dy = (dk // nz) % ny + bzone[1] * ny 

671 dz = dk % nz + bzone[2] * nz 

672 return dx, dy, dz 

673 

674 def dk(self, dirG): 

675 dx, dy, dz = dirG 

676 nx, ny, nz = self['wannier_kpts'] 

677 return ny * nz * (dx % nx) + nz * (dy % ny) + dz % nz 

678 

679 def get_wannier_localization_matrix(self, nbands, dirG, nextkpoint=None, 

680 kpoint=None, spin=0, G_I=(0, 0, 0)): 

681 # only expected to work for no spin polarization 

682 try: 

683 self['bloch_overlaps'] 

684 except KeyError: 

685 self.read_bloch_overlaps() 

686 dirG = tuple(dirG) 

687 nx, ny, nz = self['wannier_kpts'] 

688 nr3 = nx * ny * nz 

689 if kpoint is None and nextkpoint is None: 

690 return {kpoint: self['bloch_overlaps' 

691 ][kpoint][dirG][:nbands, :nbands 

692 ] for kpoint in range(nr3)} 

693 if kpoint is None: 

694 kpoint = (nextkpoint - self.dk(dirG)) % nr3 

695 if nextkpoint is None: 

696 nextkpoint = (kpoint + self.dk(dirG)) % nr3 

697 if dirG not in self['bloch_overlaps'][kpoint].keys(): 

698 return np.zeros((nbands, nbands), complex) 

699 return self['bloch_overlaps'][kpoint][dirG][:nbands, :nbands] 

700 

701 def prind(self, line, debug=None): 

702 ''' Print the value if debugging mode is on. 

703 Otherwise, it just ignored''' 

704 if debug is None: 

705 debug = self.debug 

706 if debug: 

707 print(line) 

708 

709 def print_file(self, file=None, running=None, **args): 

710 ''' Print the file while calculation is running''' 

711 prev_position = 0 

712 last_position = 0 

713 while not os.path.isfile(file): 

714 self.prind(f'Waiting for {file} to come out') 

715 time.sleep(5) 

716 with open(file) as fd: 

717 while running(**args): 

718 fd.seek(last_position) 

719 new_data = fd.read() 

720 prev_position = fd.tell() 

721 # self.prind('pos', prev_position != last_position) 

722 if prev_position != last_position: 

723 if not self.nohup: 

724 print(new_data) 

725 last_position = prev_position 

726 time.sleep(1)