Coverage for ase / calculators / openmx / writer.py: 14.63%

328 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-30 08:22 +0000

1# fmt: off 

2 

3""" 

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

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

6functional theories. 

7 Copyright (C) 2018 JaeHwan Shim and JaeJun Yu 

8 

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

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

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

12 (at your option) any later version. 

13 

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

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

16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17 GNU Lesser General Public License for more details. 

18 

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

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

21""" 

22import os 

23 

24import numpy as np 

25 

26from ase.calculators.calculator import kpts2sizeandoffsets 

27from ase.calculators.openmx import parameters as param 

28from ase.calculators.openmx.reader import ( 

29 get_file_name, 

30 get_standard_key, 

31 read_electron_valency, 

32) 

33from ase.config import cfg 

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

35 

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

37 param.tuple_bool_keys, param.integer_keys, param.float_keys, 

38 param.string_keys, param.bool_keys, param.list_int_keys, 

39 param.list_bool_keys, param.list_float_keys, param.matrix_keys] 

40 

41 

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

43 system_changes=None): 

44 """ 

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

46 First, set 

47 Write input (dat)-file. 

48 See calculator.py for further details. 

49 

50 Parameters 

51 ---------- 

52 - atoms : The Atoms object to write. 

53 - properties : The properties which should be calculated. 

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

55 """ 

56 from ase.calculators.openmx import parameters as param 

57 filtered_keywords = parameters_to_keywords(label=label, atoms=atoms, 

58 parameters=parameters, 

59 properties=properties, 

60 system_changes=system_changes) 

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

62 'tuple_integer', 'tuple_float', 'tuple_bool', 

63 'matrix', 'list_int', 'list_bool', 'list_float'] 

64 # Start writing the file 

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

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

67 # Write 1-line keywords 

68 for fltrd_keyword in filtered_keywords.keys(): 

69 for key in keys: 

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

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

72 for omx_keyword in openmx_keywords: 

73 if fltrd_keyword == get_standard_key(omx_keyword): 

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

75 

76 

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

78 properties=None, system_changes=None): 

79 """ 

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

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

82 dictionary using standard parameters. If standard parameters and openmx 

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

84 It includes, 

85 

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

87 """ 

88 from collections import OrderedDict 

89 

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

91 keywords = OrderedDict() 

92 sequence = [ 

93 'system_currentdirectory', 'system_name', 'data_path', 

94 'level_of_fileout', 

95 'species_number', 'definition_of_atomic_species', 

96 'atoms_number', 'atoms_speciesandcoordinates_unit', 

97 'atoms_speciesandcoordinates', 'atoms_unitvectors_unit', 

98 'atoms_unitvectors', 'band_dispersion', 'band_nkpath', 

99 'band_kpath'] 

100 

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

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

103 counterparts = { 

104 'system_currentdirectory': curdir, 

105 'system_name': prefix, 

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

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

108 'atoms_number': len(atoms), 

109 'scf_restart': 'restart', 

110 'scf_maxiter': 'maxiter', 

111 'scf_xctype': 'xc', 

112 'scf_energycutoff': 'energy_cutoff', 

113 'scf_criterion': 'convergence', 

114 'scf_external_fields': 'external', 

115 'scf_mixing_type': 'mixer', 

116 'scf_electronic_temperature': 'smearing', 

117 'scf_system_charge': 'charge', 

118 'scf_eigenvaluesolver': 'eigensolver' 

119 } 

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

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

122 'Ha/Bohr': Ha / Bohr, 

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

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

125 

126 for key in sequence: 

127 keywords[key] = None 

128 for key in parameters: 

129 if 'scf' in key: 

130 keywords[key] = None 

131 for key in parameters: 

132 if 'md' in key: 

133 keywords[key] = None 

134 

135 # Initializes keywords to to given parameters 

136 for key in parameters.keys(): 

137 keywords[key] = parameters[key] 

138 

139 def parameter_overwrites(openmx_keyword): 

140 """ 

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

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

143 converted to OpenMX unit. 

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

145 part in suitable manner 

146 openmx_keyword : key | Name of key used in OpenMX 

147 keyword : value | value corresponds to openmx_keyword 

148 ase_parameter : key | Name of parameter used in ASE 

149 parameter : value | value corresponds to ase_parameter 

150 """ 

151 ase_parameter = counterparts[openmx_keyword] 

152 keyword = parameters.get(openmx_keyword) 

153 parameter = parameters.get(ase_parameter) 

154 if parameter is not None: 

155 # Handles the unit 

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

157 if unit is not None: 

158 return parameter / unit 

159 return parameter 

160 elif keyword is not None: 

161 return keyword 

162 elif 'scf' in openmx_keyword: 

163 return None 

164 else: 

165 return counterparts[openmx_keyword] 

166 

167 # Overwrites openmx keyword using standard parameters 

168 for openmx_keyword in counterparts: 

169 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword) 

170 

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

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

173 if 'energies' in properties: 

174 keywords['energy_decomposition'] = True 

175 if 'stress' in properties: 

176 keywords['scf_stress_tensor'] = True 

177 

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

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

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

181 

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

183 keywords['band_dispersion'] = True 

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

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

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

187 

188 # Set up Wannier Environment 

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

190 keywords['species_number'] *= 2 

191 

192 # Set up some parameters for the later use 

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

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

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

196 

197 # Set up the matrix-type OpenMX keywords 

198 for key in matrix_keys: 

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

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

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

202 if not (v is None or 

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

204 

205 

206def get_species(symbols): 

207 species = [] 

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

209 return species 

210 

211 

212def get_xc(xc): 

213 """ 

214 Change the name of xc appropriate to OpenMX format 

215 """ 

216 xc = xc.upper() 

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

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

219 return 'GGA-PBE' 

220 elif xc in ['LDA']: 

221 return 'LDA' 

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

223 return 'LSDA-' + xc 

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

225 return 'LSDA-CA' 

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

227 return 'LSDA-PW' 

228 else: 

229 return 'LDA' 

230 

231 

232def get_vps(xc): 

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

234 return 'PBE' 

235 else: 

236 return 'CA' 

237 

238 

239def get_scf_kgrid(atoms, parameters): 

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

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

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

243 return kpts 

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

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

246 else: 

247 return scf_kgrid 

248 

249 

250def get_definition_of_atomic_species(atoms, parameters): 

251 """ 

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

253 where matrix of strings contains the information between keywords. 

254 For example, 

255 definition_of_atomic_species = 

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

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

258 Goes to, 

259 <Definition.of.Atomic.Species 

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

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

262 Definition.of.Atomic.Species> 

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

264 A. Define local functions for projectors 

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

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

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

268 Species.Number 2 

269 <Definition.of.Atomic.Species 

270 Si Si7.0-s2p2d1 Si_CA13 

271 proj1 Si5.5-s1p1d1f1 Si_CA13 

272 Definition.of.Atomic.Species> 

273 """ 

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

275 return parameters['definition_of_atomic_species'] 

276 

277 definition_of_atomic_species = [] 

278 xc = parameters.get('_xc') 

279 year = parameters.get('_year') 

280 

281 chem = atoms.get_chemical_symbols() 

282 species = get_species(chem) 

283 for element in species: 

284 rad_orb = get_cutoff_radius_and_orbital(element=element) 

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

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

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

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

289 for i, projector in enumerate(wannier_projectors): 

290 full_projector = definition_of_atomic_species[i] 

291 full_projector[0] = projector 

292 definition_of_atomic_species.append(full_projector) 

293 return definition_of_atomic_species 

294 

295 

296def get_dft_data_year(parameters): 

297 """ 

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

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

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

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

302 such pseudo potential path. 

303 """ 

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

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

306 data_path = parameters['_data_path'] 

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

308 if year is not None: 

309 return year 

310 else: 

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

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

313 

314 

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

316 """ 

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

318 orbital using default_settings.py. For example, 

319 'Si' -> 'Si.7.0-s2p2d1' 

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

321 change the default_settings.py directly. 

322 """ 

323 from ase.calculators.openmx import default_settings 

324 orbital = element 

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

326 default_dictionary = default_settings.default_dictionary 

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

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

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

330 for i, orbital_number in enumerate(orbital_numbers): 

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

332 return orbital 

333 

334 

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

336 """ 

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

338 For example, 

339 'Si' -> 'Si_CA13' 

340 or 

341 'Si' -> 'Si_CA19' 

342 depending on pseudo potential generation year 

343 """ 

344 from ase.calculators.openmx import default_settings 

345 default_dictionary = default_settings.default_dictionary 

346 pseudo_potential_suffix = element 

347 vps = get_vps(xc) 

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

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

350 return pseudo_potential_suffix 

351 

352 

353def get_atoms_speciesandcoordinates(atoms, parameters): 

354 """ 

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

356 keyword 

357 'Atoms.SpeciesAndCoordinates' as follows: 

358 <Atoms.SpeciesAndCoordinates 

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

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

361 Atoms.SpeciesAndCoordinates> 

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

363 """ 

364 atoms_speciesandcoordinates = [] 

365 xc = parameters.get('_xc') 

366 year = parameters.get('_year') 

367 data_pth = parameters.get('_data_path') 

368 # Appending number and elemental symbol 

369 elements = atoms.get_chemical_symbols() 

370 for i, element in enumerate(elements): 

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

372 # Appending positions 

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

374 if unit == 'ang': 

375 positions = atoms.get_positions() 

376 elif unit == 'frac': 

377 positions = atoms.get_scaled_positions(wrap=False) 

378 elif unit == 'au': 

379 positions = atoms.get_positions() / Bohr 

380 for i, position in enumerate(positions): 

381 atoms_speciesandcoordinates[i].extend(position) 

382 

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

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

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

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

387 atoms_spncrd[i][2] = atoms_speciesandcoordinates[i][2] 

388 atoms_spncrd[i][3] = atoms_speciesandcoordinates[i][3] 

389 atoms_spncrd[i][4] = atoms_speciesandcoordinates[i][4] 

390 return atoms_spncrd 

391 

392 # Appending magnetic moment 

393 magmoms = atoms.get_initial_magnetic_moments() 

394 for i, magmom in enumerate(magmoms): 

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

396 atoms_speciesandcoordinates[i].extend(up_down_spin) 

397 # Appending magnetic field Spin magnetic moment theta phi 

398 spin_directions = get_spin_direction(magmoms) 

399 for i, spin_direction in enumerate(spin_directions): 

400 atoms_speciesandcoordinates[i].extend(spin_direction) 

401 # Appending magnetic field for Orbital magnetic moment theta phi 

402 orbital_directions = get_orbital_direction() 

403 for i, orbital_direction in enumerate(orbital_directions): 

404 atoms_speciesandcoordinates[i].extend(orbital_direction) 

405 # Appending Noncolinear schem switch 

406 noncollinear_switches = get_noncollinear_switches() 

407 for i, noncollinear_switch in enumerate(noncollinear_switches): 

408 atoms_speciesandcoordinates[i].extend(noncollinear_switch) 

409 # Appending orbital_enhancement_switch 

410 lda_u_switches = get_lda_u_switches() 

411 for i, lda_u_switch in enumerate(lda_u_switches): 

412 atoms_speciesandcoordinates[i].extend(lda_u_switch) 

413 return atoms_speciesandcoordinates 

414 

415 

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

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

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

419 # Collinear spin 

420 magmom = float(magmom) 

421 else: 

422 # Non-collinear spin 

423 magmom = np.linalg.norm(magmom) 

424 suffix = get_pseudo_potential_suffix(element, xc, year) 

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

426 valence_electron = float(read_electron_valency(filename)) 

427 return [valence_electron / 2 + magmom / 2, 

428 valence_electron / 2 - magmom / 2] 

429 

430 

431def get_spin_direction(magmoms): 

432 ''' 

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

434 ''' 

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

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

437 return [] 

438 else: 

439 magmoms = np.array(magmoms) 

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

441 

442 

443def get_orbital_direction(): 

444 orbital_direction = [] 

445 # print("Not Implemented Yet") 

446 return orbital_direction 

447 

448 

449def get_noncollinear_switches(): 

450 noncolinear_switches = [] 

451 # print("Not Implemented Yet") 

452 return noncolinear_switches 

453 

454 

455def get_lda_u_switches(): 

456 lda_u_switches = [] 

457 # print("Not Implemented Yet") 

458 return lda_u_switches 

459 

460 

461def get_spinpol(atoms, parameters): 

462 ''' Judgeds the keyword 'scf.SpinPolarization' 

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

464 1. standard_spinpol 

465 2. scf_spinpolarization 

466 3. magnetic moments of atoms 

467 ''' 

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

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

470 m = atoms.get_initial_magnetic_moments() 

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

472 None: None, 'nc': 'NC'} 

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

474 if scf_spinpolarization is not None: 

475 spinpol = scf_spinpolarization 

476 if standard_spinpol is not None: 

477 spinpol = standard_spinpol 

478 if isinstance(spinpol, str): 

479 spinpol = spinpol.lower() 

480 return syn[spinpol] 

481 

482 

483def get_atoms_unitvectors(atoms, parameters): 

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

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

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

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

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

489 if unit == 'ang': 

490 atoms_unitvectors = atoms.get_cell() 

491 elif unit == 'au': 

492 atoms_unitvectors = atoms.get_cell() / Bohr 

493 return atoms_unitvectors 

494 

495 

496def get_hubbard_u_values(atoms, parameters): 

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

498 

499 

500def get_atoms_cont_orbitals(atoms, parameters): 

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

502 

503 

504def get_md_fixed_xyz(atoms, parameters): 

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

506 

507 

508def get_md_tempcontrol(atoms, parameters): 

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

510 

511 

512def get_md_init_velocity(atoms, parameters): 

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

514 

515 

516def get_band_kpath_unitcell(atoms, parameters): 

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

518 

519 

520def get_band_kpath(atoms, parameters): 

521 kpts = parameters.get('kpts') 

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

523 return get_kpath(kpts=kpts) 

524 else: 

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

526 

527 

528def get_mo_kpoint(atoms, parameters): 

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

530 

531 

532def get_wannier_initial_projectors(atoms, parameters): 

533 """ 

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

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

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

537 x-axis for orbital orientation. 

538 

539 An example setting is shown here: 

540 wannier_initial_projectors= 

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

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

543 Goes to, 

544 <Wannier.Initial.Projectors 

545 proj1-sp3 0.250 0.250 0.250 -1.0 0.0 0.0 0.0 0.0 -1.0 

546 proj1-sp3 0.000 0.000 0.000 0.0 0.0 1.0 1.0 0.0 0.0 

547 Wannier.Initial.Projectors> 

548 """ 

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

550 

551 

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

553 """ 

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

555 by using dft space group method 

556 For example, 

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

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

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

560 (0.5, 0.5, 0.5) ... ... , 

561 ... ... ... , 

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

563 band_kpath = 

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

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

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

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

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

569 where, it will be written as 

570 <Band.kpath 

571 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

572 15 1.0 0.0 0.0 1.0 0.5 0.0 X W 

573 15 1.0 0.5 0.0 0.5 0.5 0.5 W L 

574 15 0.5 0.5 0.5 0.0 0.0 0.0 L g 

575 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

576 Band.kpath> 

577 """ 

578 if kpts is None: 

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

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

581 band_kpath[0][0]) 

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

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

584 band_kpath[0]['kpts']) 

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

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

587 band_kpath[0]['kpts']) 

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

589 for path in band_kpath[1:]: 

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

591 path['end_point'][0], 

592 path['kpts']) 

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

594 path['end_point'][1], 

595 path['kpts']) 

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

597 path['end_point'][2], 

598 path['kpts']) 

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

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

601 return kpts 

602 elif band_kpath is None: 

603 band_kpath = [] 

604 points = np.asarray(kpts) 

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

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

607 N = len(points) 

608 indices = [0] 

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

610 indices.append(N - 1) 

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

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

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

614 'kpts': 20, 

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

616 else: 

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

618 return band_kpath 

619 

620 

621def write_string(fd, key, value): 

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

623 fd.write("\n") 

624 

625 

626def write_tuple_integer(fd, key, value): 

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

628 fd.write("\n") 

629 

630 

631def write_tuple_float(fd, key, value): 

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

633 fd.write("\n") 

634 

635 

636def write_tuple_bool(fd, key, value): 

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

638 fd.write(" ".join( 

639 [key, "%s %s %s" % tuple([omx_bl[bl] for bl in value])])) 

640 fd.write("\n") 

641 

642 

643def write_integer(fd, key, value): 

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

645 fd.write("\n") 

646 

647 

648def write_float(fd, key, value): 

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

650 fd.write("\n") 

651 

652 

653def write_bool(fd, key, value): 

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

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

656 fd.write("\n") 

657 

658 

659def write_list_int(fd, key, value): 

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

661 

662 

663def write_list_bool(fd, key, value): 

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

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

666 

667 

668def write_list_float(fd, key, value): 

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

670 

671 

672def write_matrix(fd, key, value): 

673 fd.write('<' + key) 

674 fd.write("\n") 

675 for line in value: 

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

677 fd.write("\n") 

678 fd.write(key + '>') 

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

680 

681 

682def get_openmx_key(key): 

683 """ 

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

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

686 """ 

687 for openmx_key in keys: 

688 for openmx_keyword in openmx_key: 

689 if key == get_standard_key(openmx_keyword): 

690 return openmx_keyword