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

328 statements  

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

1""" 

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

3to the software package for nano-scale material simulations based on density 

4functional theories. 

5 Copyright (C) 2018 JaeHwan 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""" 

20import os 

21 

22import numpy as np 

23 

24from ase.calculators.calculator import kpts2sizeandoffsets 

25from ase.calculators.openmx import parameters as param 

26from ase.calculators.openmx.reader import ( 

27 get_file_name, 

28 get_standard_key, 

29 read_electron_valency, 

30) 

31from ase.config import cfg 

32from ase.units import Bohr, Ha, Ry, fs, m, s 

33 

34keys = [param.tuple_integer_keys, param.tuple_float_keys, 

35 param.tuple_bool_keys, param.integer_keys, param.float_keys, 

36 param.string_keys, param.bool_keys, param.list_int_keys, 

37 param.list_bool_keys, param.list_float_keys, param.matrix_keys] 

38 

39 

40def write_openmx(label=None, atoms=None, parameters=None, properties=None, 

41 system_changes=None): 

42 """ 

43 From atom image, 'images', write '.dat' file. 

44 First, set 

45 Write input (dat)-file. 

46 See calculator.py for further details. 

47 

48 Parameters: 

49 - atoms : The Atoms object to write. 

50 - properties : The properties which should be calculated. 

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

52 """ 

53 from ase.calculators.openmx import parameters as param 

54 filtered_keywords = parameters_to_keywords(label=label, atoms=atoms, 

55 parameters=parameters, 

56 properties=properties, 

57 system_changes=system_changes) 

58 keys = ['string', 'bool', 'integer', 'float', 

59 'tuple_integer', 'tuple_float', 'tuple_bool', 

60 'matrix', 'list_int', 'list_bool', 'list_float'] 

61 # Start writing the file 

62 filename = get_file_name('.dat', label) 

63 with open(filename, 'w') as fd: 

64 # Write 1-line keywords 

65 for fltrd_keyword in filtered_keywords.keys(): 

66 for key in keys: 

67 openmx_keywords = getattr(param, key + '_keys') 

68 write = globals()['write_' + key] 

69 for omx_keyword in openmx_keywords: 

70 if fltrd_keyword == get_standard_key(omx_keyword): 

71 write(fd, omx_keyword, filtered_keywords[fltrd_keyword]) 

72 

73 

74def parameters_to_keywords(label=None, atoms=None, parameters=None, 

75 properties=None, system_changes=None): 

76 """ 

77 Before writing `label.dat` file, set up the ASE variables to OpenMX 

78 keywords. First, It initializes with given openmx keywords and reconstruct 

79 dictionary using standard parameters. If standard parameters and openmx 

80 keywords are contradict to each other, ignores openmx keyword. 

81 It includes, 

82 

83 For aesthetical purpose, sequnece of writing input file is specified. 

84 """ 

85 from collections import OrderedDict 

86 

87 from ase.calculators.openmx.parameters import matrix_keys, unit_dat_keywords 

88 keywords = OrderedDict() 

89 sequence = [ 

90 'system_currentdirectory', 'system_name', 'data_path', 

91 'level_of_fileout', 

92 'species_number', 'definition_of_atomic_species', 

93 'atoms_number', 'atoms_speciesandcoordinates_unit', 

94 'atoms_speciesandcoordinates', 'atoms_unitvectors_unit', 

95 'atoms_unitvectors', 'band_dispersion', 'band_nkpath', 

96 'band_kpath'] 

97 

98 _directory, prefix = os.path.split(label) 

99 curdir = os.path.join(os.getcwd(), prefix) 

100 counterparts = { 

101 'system_currentdirectory': curdir, 

102 'system_name': prefix, 

103 'data_path': cfg.get('OPENMX_DFT_DATA_PATH'), 

104 'species_number': len(get_species(atoms.get_chemical_symbols())), 

105 'atoms_number': len(atoms), 

106 'scf_restart': 'restart', 

107 'scf_maxiter': 'maxiter', 

108 'scf_xctype': 'xc', 

109 'scf_energycutoff': 'energy_cutoff', 

110 'scf_criterion': 'convergence', 

111 'scf_external_fields': 'external', 

112 'scf_mixing_type': 'mixer', 

113 'scf_electronic_temperature': 'smearing', 

114 'scf_system_charge': 'charge', 

115 'scf_eigenvaluesolver': 'eigensolver' 

116 } 

117 standard_units = {'eV': 1, 'Ha': Ha, 'Ry': Ry, 'Bohr': Bohr, 'fs': fs, 

118 'K': 1, 'GV / m': 1e9 / 1.6e-19 / m, 

119 'Ha/Bohr': Ha / Bohr, 

120 'm/s': m / s, '_amu': 1, 'Tesla': 1} 

121 unit_dict = {get_standard_key(k): v for k, v in unit_dat_keywords.items()} 

122 

123 for key in sequence: 

124 keywords[key] = None 

125 for key in parameters: 

126 if 'scf' in key: 

127 keywords[key] = None 

128 for key in parameters: 

129 if 'md' in key: 

130 keywords[key] = None 

131 

132 # Initializes keywords to to given parameters 

133 for key in parameters.keys(): 

134 keywords[key] = parameters[key] 

135 

136 def parameter_overwrites(openmx_keyword): 

137 """ 

138 In a situation conflicting ASE standard parameters and OpenMX keywords, 

139 ASE parameters overrides to OpenMX keywords. While doing so, units are 

140 converted to OpenMX unit. 

141 However, if both parameters and keyword are not given, we fill up that 

142 part in suitable manner 

143 openmx_keyword : key | Name of key used in OpenMX 

144 keyword : value | value corresponds to openmx_keyword 

145 ase_parameter : key | Name of parameter used in ASE 

146 parameter : value | value corresponds to ase_parameter 

147 """ 

148 ase_parameter = counterparts[openmx_keyword] 

149 keyword = parameters.get(openmx_keyword) 

150 parameter = parameters.get(ase_parameter) 

151 if parameter is not None: 

152 # Handles the unit 

153 unit = standard_units.get(unit_dict.get(openmx_keyword)) 

154 if unit is not None: 

155 return parameter / unit 

156 return parameter 

157 elif keyword is not None: 

158 return keyword 

159 elif 'scf' in openmx_keyword: 

160 return None 

161 else: 

162 return counterparts[openmx_keyword] 

163 

164 # Overwrites openmx keyword using standard parameters 

165 for openmx_keyword in counterparts: 

166 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword) 

167 

168 # keywords['scf_stress_tensor'] = 'stress' in properties 

169 # This is not working due to the UnitCellFilter method. 

170 if 'energies' in properties: 

171 keywords['energy_decomposition'] = True 

172 if 'stress' in properties: 

173 keywords['scf_stress_tensor'] = True 

174 

175 keywords['scf_xctype'] = get_xc(keywords['scf_xctype']) 

176 keywords['scf_kgrid'] = get_scf_kgrid(atoms, parameters) 

177 keywords['scf_spinpolarization'] = get_spinpol(atoms, parameters) 

178 

179 if parameters.get('band_kpath') is not None: 

180 keywords['band_dispersion'] = True 

181 keywords['band_kpath'] = parameters.get('band_kpath') 

182 if parameters.get('band_nkpath') is not None: 

183 keywords['band_nkpath'] = len(keywords['band_kpath']) 

184 

185 # Set up Wannier Environment 

186 if parameters.get('wannier_func_calc') is not None: 

187 keywords['species_number'] *= 2 

188 

189 # Set up some parameters for the later use 

190 parameters['_xc'] = keywords['scf_xctype'] 

191 parameters['_data_path'] = keywords['data_path'] 

192 parameters['_year'] = get_dft_data_year(parameters) 

193 

194 # Set up the matrix-type OpenMX keywords 

195 for key in matrix_keys: 

196 get_matrix_key = globals()['get_' + get_standard_key(key)] 

197 keywords[get_standard_key(key)] = get_matrix_key(atoms, parameters) 

198 return OrderedDict([(k, v)for k, v in keywords.items() 

199 if not (v is None or 

200 (isinstance(v, list) and v == []))]) 

201 

202 

203def get_species(symbols): 

204 species = [] 

205 [species.append(s) for s in symbols if s not in species] 

206 return species 

207 

208 

209def get_xc(xc): 

210 """ 

211 Change the name of xc appropriate to OpenMX format 

212 """ 

213 xc = xc.upper() 

214 assert xc.upper() in param.OpenMXParameters().allowed_xc 

215 if xc in ['PBE', 'GGA', 'GGA-PBE']: 

216 return 'GGA-PBE' 

217 elif xc in ['LDA']: 

218 return 'LDA' 

219 elif xc in ['CA', 'PW']: 

220 return 'LSDA-' + xc 

221 elif xc in ['LSDA', 'LSDA-CA']: 

222 return 'LSDA-CA' 

223 elif xc in ['LSDA-PW']: 

224 return 'LSDA-PW' 

225 else: 

226 return 'LDA' 

227 

228 

229def get_vps(xc): 

230 if xc in ['GGA-PBE']: 

231 return 'PBE' 

232 else: 

233 return 'CA' 

234 

235 

236def get_scf_kgrid(atoms, parameters): 

237 kpts, scf_kgrid = parameters.get('kpts'), parameters.get('scf_kgrid') 

238 if isinstance(kpts, (tuple, list, np.ndarray)) and len( 

239 kpts) == 3 and isinstance(kpts[0], int): 

240 return kpts 

241 elif isinstance(kpts, (float, int)): 

242 return tuple(kpts2sizeandoffsets(atoms=atoms, density=kpts)[0]) 

243 else: 

244 return scf_kgrid 

245 

246 

247def get_definition_of_atomic_species(atoms, parameters): 

248 """ 

249 Using atoms and parameters, Returns the list `definition_of_atomic_species` 

250 where matrix of strings contains the information between keywords. 

251 For example, 

252 definition_of_atomic_species = 

253 [['H','H5.0-s1>1p1>1','H_CA13'], 

254 ['C','C5.0-s1>1p1>1','C_CA13']] 

255 Goes to, 

256 <Definition.of.Atomic.Species 

257 H H5.0-s1>1p1>1 H_CA13 

258 C C5.0-s1>1p1>1 C_CA13 

259 Definition.of.Atomic.Species> 

260 Further more, you can specify the wannier information here. 

261 A. Define local functions for projectors 

262 Since the pseudo-atomic orbitals are used for projectors, 

263 the specification of them is the same as for the basis functions. 

264 An example setting, for silicon in diamond structure, is as following: 

265 Species.Number 2 

266 <Definition.of.Atomic.Species 

267 Si Si7.0-s2p2d1 Si_CA13 

268 proj1 Si5.5-s1p1d1f1 Si_CA13 

269 Definition.of.Atomic.Species> 

270 """ 

271 if parameters.get('definition_of_atomic_species') is not None: 

272 return parameters['definition_of_atomic_species'] 

273 

274 definition_of_atomic_species = [] 

275 xc = parameters.get('_xc') 

276 year = parameters.get('_year') 

277 

278 chem = atoms.get_chemical_symbols() 

279 species = get_species(chem) 

280 for element in species: 

281 rad_orb = get_cutoff_radius_and_orbital(element=element) 

282 suffix = get_pseudo_potential_suffix(element=element, xc=xc, year=year) 

283 definition_of_atomic_species.append([element, rad_orb, suffix]) 

284 # Put the same orbital and radii with chemical symbol. 

285 wannier_projectors = parameters.get('definition_of_wannier_projectors', []) 

286 for i, projector in enumerate(wannier_projectors): 

287 full_projector = definition_of_atomic_species[i] 

288 full_projector[0] = projector 

289 definition_of_atomic_species.append(full_projector) 

290 return definition_of_atomic_species 

291 

292 

293def get_dft_data_year(parameters): 

294 """ 

295 It seems there is no version or potential year checker in openmx, thus we 

296 implemented one. It parse the pesudo potential path variable such as 

297 `~/PATH/TO/OPENMX/openmx3.9/DFT_DATA19/` or `.../openmx3.8/DFT_DATA13/`. 

298 By spliting this string, we harness the number of the year that generated 

299 such pseudo potential path. 

300 """ 

301 if parameters.get('dft_data_year') is not None: 

302 return str(parameters.get('dft_data_year')) 

303 data_path = parameters['_data_path'] 

304 year = data_path.split('DFT_DATA')[1][:2] 

305 if year is not None: 

306 return year 

307 else: 

308 raise ValueError('DFT_DATA year can not be found. Please specify ' 

309 '`dft_data_year` as year of pseudo potential relesed') 

310 

311 

312def get_cutoff_radius_and_orbital(element=None, orbital=None): 

313 """ 

314 For a given element, retruns the string specifying cutoff radius and 

315 orbital using default_settings.py. For example, 

316 'Si' -> 'Si.7.0-s2p2d1' 

317 If one wannts to change the atomic radius for a special purpose, one should 

318 change the default_settings.py directly. 

319 """ 

320 from ase.calculators.openmx import default_settings 

321 orbital = element 

322 orbital_letters = ['s', 'p', 'd', 'f', 'g', 'h'] 

323 default_dictionary = default_settings.default_dictionary 

324 orbital_numbers = default_dictionary[element]['orbitals used'] 

325 cutoff_radius = default_dictionary[element]['cutoff radius'] 

326 orbital += "%.1f" % float(cutoff_radius) + '-' 

327 for i, orbital_number in enumerate(orbital_numbers): 

328 orbital += orbital_letters[i] + str(orbital_number) 

329 return orbital 

330 

331 

332def get_pseudo_potential_suffix(element=None, xc=None, year='13'): 

333 """ 

334 For a given element, returns the string specifying pseudo potential suffix. 

335 For example, 

336 'Si' -> 'Si_CA13' 

337 or 

338 'Si' -> 'Si_CA19' 

339 depending on pseudo potential generation year 

340 """ 

341 from ase.calculators.openmx import default_settings 

342 default_dictionary = default_settings.default_dictionary 

343 pseudo_potential_suffix = element 

344 vps = get_vps(xc) 

345 suffix = default_dictionary[element]['pseudo-potential suffix'] 

346 pseudo_potential_suffix += '_' + vps + year + suffix 

347 return pseudo_potential_suffix 

348 

349 

350def get_atoms_speciesandcoordinates(atoms, parameters): 

351 """ 

352 The atomic coordinates and the number of spin charge are given by the 

353 keyword 

354 'Atoms.SpeciesAndCoordinates' as follows: 

355 <Atoms.SpeciesAndCoordinates 

356 1 Mn 0.00000 0.00000 0.00000 8.0 5.0 45.0 0.0 45.0 0.0 1 on 

357 2 O 1.70000 0.00000 0.00000 3.0 3.0 45.0 0.0 45.0 0.0 1 on 

358 Atoms.SpeciesAndCoordinates> 

359 to know more, link <http://www.openmx-square.org/openmx_man3.7/node85.html> 

360 """ 

361 atoms_speciesandcoordinates = [] 

362 xc = parameters.get('_xc') 

363 year = parameters.get('_year') 

364 data_pth = parameters.get('_data_path') 

365 # Appending number and elemental symbol 

366 elements = atoms.get_chemical_symbols() 

367 for i, element in enumerate(elements): 

368 atoms_speciesandcoordinates.append([str(i + 1), element]) 

369 # Appending positions 

370 unit = parameters.get('atoms_speciesandcoordinates_unit', 'ang').lower() 

371 if unit == 'ang': 

372 positions = atoms.get_positions() 

373 elif unit == 'frac': 

374 positions = atoms.get_scaled_positions(wrap=False) 

375 elif unit == 'au': 

376 positions = atoms.get_positions() / Bohr 

377 for i, position in enumerate(positions): 

378 atoms_speciesandcoordinates[i].extend(position) 

379 

380 # Even if 'atoms_speciesandcoordinates_unit' exists, `positions` goes first 

381 if parameters.get('atoms_speciesandcoordinates') is not None: 

382 atoms_spncrd = parameters['atoms_speciesandcoordinates'].copy() 

383 for i in range(len(atoms)): 

384 atoms_spncrd[i][2] = atoms_speciesandcoordinates[i][2] 

385 atoms_spncrd[i][3] = atoms_speciesandcoordinates[i][3] 

386 atoms_spncrd[i][4] = atoms_speciesandcoordinates[i][4] 

387 return atoms_spncrd 

388 

389 # Appending magnetic moment 

390 magmoms = atoms.get_initial_magnetic_moments() 

391 for i, magmom in enumerate(magmoms): 

392 up_down_spin = get_up_down_spin(magmom, elements[i], xc, data_pth, year) 

393 atoms_speciesandcoordinates[i].extend(up_down_spin) 

394 # Appending magnetic field Spin magnetic moment theta phi 

395 spin_directions = get_spin_direction(magmoms) 

396 for i, spin_direction in enumerate(spin_directions): 

397 atoms_speciesandcoordinates[i].extend(spin_direction) 

398 # Appending magnetic field for Orbital magnetic moment theta phi 

399 orbital_directions = get_orbital_direction() 

400 for i, orbital_direction in enumerate(orbital_directions): 

401 atoms_speciesandcoordinates[i].extend(orbital_direction) 

402 # Appending Noncolinear schem switch 

403 noncollinear_switches = get_noncollinear_switches() 

404 for i, noncollinear_switch in enumerate(noncollinear_switches): 

405 atoms_speciesandcoordinates[i].extend(noncollinear_switch) 

406 # Appending orbital_enhancement_switch 

407 lda_u_switches = get_lda_u_switches() 

408 for i, lda_u_switch in enumerate(lda_u_switches): 

409 atoms_speciesandcoordinates[i].extend(lda_u_switch) 

410 return atoms_speciesandcoordinates 

411 

412 

413def get_up_down_spin(magmom, element, xc, data_path, year): 

414 # for magmom with single number (collinear spin) skip the normalization 

415 if isinstance(magmom, (int, float)): 

416 # Collinear spin 

417 magmom = float(magmom) 

418 else: 

419 # Non-collinear spin 

420 magmom = np.linalg.norm(magmom) 

421 suffix = get_pseudo_potential_suffix(element, xc, year) 

422 filename = os.path.join(data_path, 'VPS/' + suffix + '.vps') 

423 valence_electron = float(read_electron_valency(filename)) 

424 return [valence_electron / 2 + magmom / 2, 

425 valence_electron / 2 - magmom / 2] 

426 

427 

428def get_spin_direction(magmoms): 

429 ''' 

430 From atoms.magmom, returns the spin direction of phi and theta 

431 ''' 

432 if np.array(magmoms).dtype == float or \ 

433 np.array(magmoms).dtype is np.float64: 

434 return [] 

435 else: 

436 magmoms = np.array(magmoms) 

437 return magmoms / np.linalg.norm(magmoms, axis=1) 

438 

439 

440def get_orbital_direction(): 

441 orbital_direction = [] 

442 # print("Not Implemented Yet") 

443 return orbital_direction 

444 

445 

446def get_noncollinear_switches(): 

447 noncolinear_switches = [] 

448 # print("Not Implemented Yet") 

449 return noncolinear_switches 

450 

451 

452def get_lda_u_switches(): 

453 lda_u_switches = [] 

454 # print("Not Implemented Yet") 

455 return lda_u_switches 

456 

457 

458def get_spinpol(atoms, parameters): 

459 ''' Judgeds the keyword 'scf.SpinPolarization' 

460 If the keyword is not None, spinpol gets the keyword by following priority 

461 1. standard_spinpol 

462 2. scf_spinpolarization 

463 3. magnetic moments of atoms 

464 ''' 

465 standard_spinpol = parameters.get('spinpol', None) 

466 scf_spinpolarization = parameters.get('scf_spinpolarization', None) 

467 m = atoms.get_initial_magnetic_moments() 

468 syn = {True: 'On', False: None, 'on': 'On', 'off': None, 

469 None: None, 'nc': 'NC'} 

470 spinpol = np.any(m >= 0.1) 

471 if scf_spinpolarization is not None: 

472 spinpol = scf_spinpolarization 

473 if standard_spinpol is not None: 

474 spinpol = standard_spinpol 

475 if isinstance(spinpol, str): 

476 spinpol = spinpol.lower() 

477 return syn[spinpol] 

478 

479 

480def get_atoms_unitvectors(atoms, parameters): 

481 zero_vec = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) 

482 if np.all(atoms.get_cell() == zero_vec) is True: 

483 default_cell = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) 

484 return parameters.get('atoms_unitvectors', default_cell) 

485 unit = parameters.get('atoms_unitvectors_unit', 'ang').lower() 

486 if unit == 'ang': 

487 atoms_unitvectors = atoms.get_cell() 

488 elif unit == 'au': 

489 atoms_unitvectors = atoms.get_cell() / Bohr 

490 return atoms_unitvectors 

491 

492 

493def get_hubbard_u_values(atoms, parameters): 

494 return parameters.get('hubbard_u_values', []) 

495 

496 

497def get_atoms_cont_orbitals(atoms, parameters): 

498 return parameters.get('atoms_cont_orbitals', []) 

499 

500 

501def get_md_fixed_xyz(atoms, parameters): 

502 return parameters.get('md_fixed_xyz', []) 

503 

504 

505def get_md_tempcontrol(atoms, parameters): 

506 return parameters.get('md_tempcontrol', []) 

507 

508 

509def get_md_init_velocity(atoms, parameters): 

510 return parameters.get('md_init_velocity', []) 

511 

512 

513def get_band_kpath_unitcell(atoms, parameters): 

514 return parameters.get('band_kpath_unitcell', []) 

515 

516 

517def get_band_kpath(atoms, parameters): 

518 kpts = parameters.get('kpts') 

519 if isinstance(kpts, list) and len(kpts) > 3: 

520 return get_kpath(kpts=kpts) 

521 else: 

522 return parameters.get('band_kpath', []) 

523 

524 

525def get_mo_kpoint(atoms, parameters): 

526 return parameters.get('mo_kpoint', []) 

527 

528 

529def get_wannier_initial_projectors(atoms, parameters): 

530 """ 

531 B. Specify the orbital, central position and orientation of a projector 

532 Wannier.Initial.Projectos will be used to specify the projector name, 

533 local orbital function, center of local orbital, and the local z-axis and 

534 x-axis for orbital orientation. 

535 

536 An example setting is shown here: 

537 wannier_initial_projectors= 

538 [['proj1-sp3','0.250','0.250','0.25','-1.0','0.0','0.0','0.0','0.0','-1.0'] 

539 ,['proj1-sp3','0.000','0.000','0.00','0.0','0.0','1.0','1.0','0.0','0.0']] 

540 Goes to, 

541 <Wannier.Initial.Projectors 

542 proj1-sp3 0.250 0.250 0.250 -1.0 0.0 0.0 0.0 0.0 -1.0 

543 proj1-sp3 0.000 0.000 0.000 0.0 0.0 1.0 1.0 0.0 0.0 

544 Wannier.Initial.Projectors> 

545 """ 

546 return parameters.get('wannier_initial_projectors', []) 

547 

548 

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

550 """ 

551 Convert band_kpath <-> kpts. Symbols will be guess automatically 

552 by using dft space group method 

553 For example, 

554 kpts = [(0, 0, 0), (0.125, 0, 0) ... (0.875, 0, 0), 

555 (1, 0, 0), (1, 0.0625, 0) .. (1, 0.4375,0), 

556 (1, 0.5,0),(0.9375, 0.5,0).. ( ... ), 

557 (0.5, 0.5, 0.5) ... ... , 

558 ... ... ... , 

559 ... (0.875, 0, 0),(1.0, 0.0, 0.0)] 

560 band_kpath = 

561 [['15','0.0','0.0','0.0','1.0','0.0','0.0','g','X'], 

562 ['15','1.0','0.0','0.0','1.0','0.5','0.0','X','W'], 

563 ['15','1.0','0.5','0.0','0.5','0.5','0.5','W','L'], 

564 ['15','0.5','0.5','0.5','0.0','0.0','0.0','L','g'], 

565 ['15','0.0','0.0','0.0','1.0','0.0','0.0','g','X']] 

566 where, it will be written as 

567 <Band.kpath 

568 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

569 15 1.0 0.0 0.0 1.0 0.5 0.0 X W 

570 15 1.0 0.5 0.0 0.5 0.5 0.5 W L 

571 15 0.5 0.5 0.5 0.0 0.0 0.0 L g 

572 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

573 Band.kpath> 

574 """ 

575 if kpts is None: 

576 kx_linspace = np.linspace(band_kpath[0]['start_point'][0], 

577 band_kpath[0]['end_point'][0], 

578 band_kpath[0][0]) 

579 ky_linspace = np.linspace(band_kpath[0]['start_point'][1], 

580 band_kpath[0]['end_point'][1], 

581 band_kpath[0]['kpts']) 

582 kz_linspace = np.linspace(band_kpath[0]['start_point'][2], 

583 band_kpath[0]['end_point'][2], 

584 band_kpath[0]['kpts']) 

585 kpts = np.array([kx_linspace, ky_linspace, kz_linspace]).T 

586 for path in band_kpath[1:]: 

587 kx_linspace = np.linspace(path['start_point'][0], 

588 path['end_point'][0], 

589 path['kpts']) 

590 ky_linspace = np.linspace(path['start_point'][1], 

591 path['end_point'][1], 

592 path['kpts']) 

593 kz_linspace = np.linspace(path['start_point'][2], 

594 path['end_point'][2], 

595 path['kpts']) 

596 k_lin = np.array([kx_linspace, ky_linspace, kz_linspace]).T 

597 kpts = np.append(kpts, k_lin, axis=0) 

598 return kpts 

599 elif band_kpath is None: 

600 band_kpath = [] 

601 points = np.asarray(kpts) 

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

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

604 N = len(points) 

605 indices = [0] 

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

607 indices.append(N - 1) 

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

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

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

611 'kpts': 20, 

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

613 else: 

614 raise KeyError('You should specify band_kpath or kpts') 

615 return band_kpath 

616 

617 

618def write_string(fd, key, value): 

619 fd.write(" ".join([key, value])) 

620 fd.write("\n") 

621 

622 

623def write_tuple_integer(fd, key, value): 

624 fd.write(" ".join([key, "%d %d %d" % value])) 

625 fd.write("\n") 

626 

627 

628def write_tuple_float(fd, key, value): 

629 fd.write(" ".join([key, "%.4f %.4f %.4f" % value])) 

630 fd.write("\n") 

631 

632 

633def write_tuple_bool(fd, key, value): 

634 omx_bl = {True: 'On', False: 'Off'} 

635 fd.write(" ".join([key, "%s %s %s" % [omx_bl[bl] for bl in value]])) 

636 fd.write("\n") 

637 

638 

639def write_integer(fd, key, value): 

640 fd.write(" ".join([key, "%d" % value])) 

641 fd.write("\n") 

642 

643 

644def write_float(fd, key, value): 

645 fd.write(" ".join([key, "%.8g" % value])) 

646 fd.write("\n") 

647 

648 

649def write_bool(fd, key, value): 

650 omx_bl = {True: 'On', False: 'Off'} 

651 fd.write(" ".join([key, f"{omx_bl[value]}"])) 

652 fd.write("\n") 

653 

654 

655def write_list_int(fd, key, value): 

656 fd.write("".join(key) + ' ' + " ".join(map(str, value))) 

657 

658 

659def write_list_bool(fd, key, value): 

660 omx_bl = {True: 'On', False: 'Off'} 

661 fd.write("".join(key) + ' ' + " ".join([omx_bl[bl] for bl in value])) 

662 

663 

664def write_list_float(fd, key, value): 

665 fd.write("".join(key) + ' ' + " ".join(map(str, value))) 

666 

667 

668def write_matrix(fd, key, value): 

669 fd.write('<' + key) 

670 fd.write("\n") 

671 for line in value: 

672 fd.write(" " + " ".join(map(str, line))) 

673 fd.write("\n") 

674 fd.write(key + '>') 

675 fd.write("\n\n") 

676 

677 

678def get_openmx_key(key): 

679 """ 

680 For the writing purpose, we need to know Original OpenMX keyword format. 

681 By comparing keys in the parameters.py, restore the original key 

682 """ 

683 for openmx_key in keys: 

684 for openmx_keyword in openmx_key: 

685 if key == get_standard_key(openmx_keyword): 

686 return openmx_keyword