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

328 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +0000

1# fmt: off 

2 

3""" 

4The ASE Calculator for OpenMX <http://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 <http://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 - atoms : The Atoms object to write. 

52 - properties : The properties which should be calculated. 

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

54 """ 

55 from ase.calculators.openmx import parameters as param 

56 filtered_keywords = parameters_to_keywords(label=label, atoms=atoms, 

57 parameters=parameters, 

58 properties=properties, 

59 system_changes=system_changes) 

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

61 'tuple_integer', 'tuple_float', 'tuple_bool', 

62 'matrix', 'list_int', 'list_bool', 'list_float'] 

63 # Start writing the file 

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

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

66 # Write 1-line keywords 

67 for fltrd_keyword in filtered_keywords.keys(): 

68 for key in keys: 

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

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

71 for omx_keyword in openmx_keywords: 

72 if fltrd_keyword == get_standard_key(omx_keyword): 

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

74 

75 

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

77 properties=None, system_changes=None): 

78 """ 

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

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

81 dictionary using standard parameters. If standard parameters and openmx 

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

83 It includes, 

84 

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

86 """ 

87 from collections import OrderedDict 

88 

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

90 keywords = OrderedDict() 

91 sequence = [ 

92 'system_currentdirectory', 'system_name', 'data_path', 

93 'level_of_fileout', 

94 'species_number', 'definition_of_atomic_species', 

95 'atoms_number', 'atoms_speciesandcoordinates_unit', 

96 'atoms_speciesandcoordinates', 'atoms_unitvectors_unit', 

97 'atoms_unitvectors', 'band_dispersion', 'band_nkpath', 

98 'band_kpath'] 

99 

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

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

102 counterparts = { 

103 'system_currentdirectory': curdir, 

104 'system_name': prefix, 

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

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

107 'atoms_number': len(atoms), 

108 'scf_restart': 'restart', 

109 'scf_maxiter': 'maxiter', 

110 'scf_xctype': 'xc', 

111 'scf_energycutoff': 'energy_cutoff', 

112 'scf_criterion': 'convergence', 

113 'scf_external_fields': 'external', 

114 'scf_mixing_type': 'mixer', 

115 'scf_electronic_temperature': 'smearing', 

116 'scf_system_charge': 'charge', 

117 'scf_eigenvaluesolver': 'eigensolver' 

118 } 

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

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

121 'Ha/Bohr': Ha / Bohr, 

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

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

124 

125 for key in sequence: 

126 keywords[key] = None 

127 for key in parameters: 

128 if 'scf' in key: 

129 keywords[key] = None 

130 for key in parameters: 

131 if 'md' in key: 

132 keywords[key] = None 

133 

134 # Initializes keywords to to given parameters 

135 for key in parameters.keys(): 

136 keywords[key] = parameters[key] 

137 

138 def parameter_overwrites(openmx_keyword): 

139 """ 

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

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

142 converted to OpenMX unit. 

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

144 part in suitable manner 

145 openmx_keyword : key | Name of key used in OpenMX 

146 keyword : value | value corresponds to openmx_keyword 

147 ase_parameter : key | Name of parameter used in ASE 

148 parameter : value | value corresponds to ase_parameter 

149 """ 

150 ase_parameter = counterparts[openmx_keyword] 

151 keyword = parameters.get(openmx_keyword) 

152 parameter = parameters.get(ase_parameter) 

153 if parameter is not None: 

154 # Handles the unit 

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

156 if unit is not None: 

157 return parameter / unit 

158 return parameter 

159 elif keyword is not None: 

160 return keyword 

161 elif 'scf' in openmx_keyword: 

162 return None 

163 else: 

164 return counterparts[openmx_keyword] 

165 

166 # Overwrites openmx keyword using standard parameters 

167 for openmx_keyword in counterparts: 

168 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword) 

169 

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

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

172 if 'energies' in properties: 

173 keywords['energy_decomposition'] = True 

174 if 'stress' in properties: 

175 keywords['scf_stress_tensor'] = True 

176 

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

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

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

180 

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

182 keywords['band_dispersion'] = True 

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

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

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

186 

187 # Set up Wannier Environment 

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

189 keywords['species_number'] *= 2 

190 

191 # Set up some parameters for the later use 

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

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

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

195 

196 # Set up the matrix-type OpenMX keywords 

197 for key in matrix_keys: 

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

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

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

201 if not (v is None or 

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

203 

204 

205def get_species(symbols): 

206 species = [] 

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

208 return species 

209 

210 

211def get_xc(xc): 

212 """ 

213 Change the name of xc appropriate to OpenMX format 

214 """ 

215 xc = xc.upper() 

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

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

218 return 'GGA-PBE' 

219 elif xc in ['LDA']: 

220 return 'LDA' 

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

222 return 'LSDA-' + xc 

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

224 return 'LSDA-CA' 

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

226 return 'LSDA-PW' 

227 else: 

228 return 'LDA' 

229 

230 

231def get_vps(xc): 

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

233 return 'PBE' 

234 else: 

235 return 'CA' 

236 

237 

238def get_scf_kgrid(atoms, parameters): 

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

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

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

242 return kpts 

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

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

245 else: 

246 return scf_kgrid 

247 

248 

249def get_definition_of_atomic_species(atoms, parameters): 

250 """ 

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

252 where matrix of strings contains the information between keywords. 

253 For example, 

254 definition_of_atomic_species = 

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

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

257 Goes to, 

258 <Definition.of.Atomic.Species 

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

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

261 Definition.of.Atomic.Species> 

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

263 A. Define local functions for projectors 

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

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

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

267 Species.Number 2 

268 <Definition.of.Atomic.Species 

269 Si Si7.0-s2p2d1 Si_CA13 

270 proj1 Si5.5-s1p1d1f1 Si_CA13 

271 Definition.of.Atomic.Species> 

272 """ 

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

274 return parameters['definition_of_atomic_species'] 

275 

276 definition_of_atomic_species = [] 

277 xc = parameters.get('_xc') 

278 year = parameters.get('_year') 

279 

280 chem = atoms.get_chemical_symbols() 

281 species = get_species(chem) 

282 for element in species: 

283 rad_orb = get_cutoff_radius_and_orbital(element=element) 

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

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

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

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

288 for i, projector in enumerate(wannier_projectors): 

289 full_projector = definition_of_atomic_species[i] 

290 full_projector[0] = projector 

291 definition_of_atomic_species.append(full_projector) 

292 return definition_of_atomic_species 

293 

294 

295def get_dft_data_year(parameters): 

296 """ 

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

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

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

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

301 such pseudo potential path. 

302 """ 

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

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

305 data_path = parameters['_data_path'] 

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

307 if year is not None: 

308 return year 

309 else: 

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

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

312 

313 

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

315 """ 

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

317 orbital using default_settings.py. For example, 

318 'Si' -> 'Si.7.0-s2p2d1' 

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

320 change the default_settings.py directly. 

321 """ 

322 from ase.calculators.openmx import default_settings 

323 orbital = element 

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

325 default_dictionary = default_settings.default_dictionary 

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

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

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

329 for i, orbital_number in enumerate(orbital_numbers): 

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

331 return orbital 

332 

333 

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

335 """ 

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

337 For example, 

338 'Si' -> 'Si_CA13' 

339 or 

340 'Si' -> 'Si_CA19' 

341 depending on pseudo potential generation year 

342 """ 

343 from ase.calculators.openmx import default_settings 

344 default_dictionary = default_settings.default_dictionary 

345 pseudo_potential_suffix = element 

346 vps = get_vps(xc) 

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

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

349 return pseudo_potential_suffix 

350 

351 

352def get_atoms_speciesandcoordinates(atoms, parameters): 

353 """ 

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

355 keyword 

356 'Atoms.SpeciesAndCoordinates' as follows: 

357 <Atoms.SpeciesAndCoordinates 

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

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

360 Atoms.SpeciesAndCoordinates> 

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

362 """ 

363 atoms_speciesandcoordinates = [] 

364 xc = parameters.get('_xc') 

365 year = parameters.get('_year') 

366 data_pth = parameters.get('_data_path') 

367 # Appending number and elemental symbol 

368 elements = atoms.get_chemical_symbols() 

369 for i, element in enumerate(elements): 

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

371 # Appending positions 

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

373 if unit == 'ang': 

374 positions = atoms.get_positions() 

375 elif unit == 'frac': 

376 positions = atoms.get_scaled_positions(wrap=False) 

377 elif unit == 'au': 

378 positions = atoms.get_positions() / Bohr 

379 for i, position in enumerate(positions): 

380 atoms_speciesandcoordinates[i].extend(position) 

381 

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

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

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

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

386 atoms_spncrd[i][2] = atoms_speciesandcoordinates[i][2] 

387 atoms_spncrd[i][3] = atoms_speciesandcoordinates[i][3] 

388 atoms_spncrd[i][4] = atoms_speciesandcoordinates[i][4] 

389 return atoms_spncrd 

390 

391 # Appending magnetic moment 

392 magmoms = atoms.get_initial_magnetic_moments() 

393 for i, magmom in enumerate(magmoms): 

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

395 atoms_speciesandcoordinates[i].extend(up_down_spin) 

396 # Appending magnetic field Spin magnetic moment theta phi 

397 spin_directions = get_spin_direction(magmoms) 

398 for i, spin_direction in enumerate(spin_directions): 

399 atoms_speciesandcoordinates[i].extend(spin_direction) 

400 # Appending magnetic field for Orbital magnetic moment theta phi 

401 orbital_directions = get_orbital_direction() 

402 for i, orbital_direction in enumerate(orbital_directions): 

403 atoms_speciesandcoordinates[i].extend(orbital_direction) 

404 # Appending Noncolinear schem switch 

405 noncollinear_switches = get_noncollinear_switches() 

406 for i, noncollinear_switch in enumerate(noncollinear_switches): 

407 atoms_speciesandcoordinates[i].extend(noncollinear_switch) 

408 # Appending orbital_enhancement_switch 

409 lda_u_switches = get_lda_u_switches() 

410 for i, lda_u_switch in enumerate(lda_u_switches): 

411 atoms_speciesandcoordinates[i].extend(lda_u_switch) 

412 return atoms_speciesandcoordinates 

413 

414 

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

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

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

418 # Collinear spin 

419 magmom = float(magmom) 

420 else: 

421 # Non-collinear spin 

422 magmom = np.linalg.norm(magmom) 

423 suffix = get_pseudo_potential_suffix(element, xc, year) 

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

425 valence_electron = float(read_electron_valency(filename)) 

426 return [valence_electron / 2 + magmom / 2, 

427 valence_electron / 2 - magmom / 2] 

428 

429 

430def get_spin_direction(magmoms): 

431 ''' 

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

433 ''' 

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

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

436 return [] 

437 else: 

438 magmoms = np.array(magmoms) 

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

440 

441 

442def get_orbital_direction(): 

443 orbital_direction = [] 

444 # print("Not Implemented Yet") 

445 return orbital_direction 

446 

447 

448def get_noncollinear_switches(): 

449 noncolinear_switches = [] 

450 # print("Not Implemented Yet") 

451 return noncolinear_switches 

452 

453 

454def get_lda_u_switches(): 

455 lda_u_switches = [] 

456 # print("Not Implemented Yet") 

457 return lda_u_switches 

458 

459 

460def get_spinpol(atoms, parameters): 

461 ''' Judgeds the keyword 'scf.SpinPolarization' 

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

463 1. standard_spinpol 

464 2. scf_spinpolarization 

465 3. magnetic moments of atoms 

466 ''' 

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

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

469 m = atoms.get_initial_magnetic_moments() 

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

471 None: None, 'nc': 'NC'} 

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

473 if scf_spinpolarization is not None: 

474 spinpol = scf_spinpolarization 

475 if standard_spinpol is not None: 

476 spinpol = standard_spinpol 

477 if isinstance(spinpol, str): 

478 spinpol = spinpol.lower() 

479 return syn[spinpol] 

480 

481 

482def get_atoms_unitvectors(atoms, parameters): 

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

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

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

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

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

488 if unit == 'ang': 

489 atoms_unitvectors = atoms.get_cell() 

490 elif unit == 'au': 

491 atoms_unitvectors = atoms.get_cell() / Bohr 

492 return atoms_unitvectors 

493 

494 

495def get_hubbard_u_values(atoms, parameters): 

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

497 

498 

499def get_atoms_cont_orbitals(atoms, parameters): 

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

501 

502 

503def get_md_fixed_xyz(atoms, parameters): 

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

505 

506 

507def get_md_tempcontrol(atoms, parameters): 

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

509 

510 

511def get_md_init_velocity(atoms, parameters): 

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

513 

514 

515def get_band_kpath_unitcell(atoms, parameters): 

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

517 

518 

519def get_band_kpath(atoms, parameters): 

520 kpts = parameters.get('kpts') 

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

522 return get_kpath(kpts=kpts) 

523 else: 

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

525 

526 

527def get_mo_kpoint(atoms, parameters): 

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

529 

530 

531def get_wannier_initial_projectors(atoms, parameters): 

532 """ 

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

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

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

536 x-axis for orbital orientation. 

537 

538 An example setting is shown here: 

539 wannier_initial_projectors= 

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

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

542 Goes to, 

543 <Wannier.Initial.Projectors 

544 proj1-sp3 0.250 0.250 0.250 -1.0 0.0 0.0 0.0 0.0 -1.0 

545 proj1-sp3 0.000 0.000 0.000 0.0 0.0 1.0 1.0 0.0 0.0 

546 Wannier.Initial.Projectors> 

547 """ 

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

549 

550 

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

552 """ 

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

554 by using dft space group method 

555 For example, 

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

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

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

559 (0.5, 0.5, 0.5) ... ... , 

560 ... ... ... , 

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

562 band_kpath = 

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

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

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

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

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

568 where, it will be written as 

569 <Band.kpath 

570 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

571 15 1.0 0.0 0.0 1.0 0.5 0.0 X W 

572 15 1.0 0.5 0.0 0.5 0.5 0.5 W L 

573 15 0.5 0.5 0.5 0.0 0.0 0.0 L g 

574 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

575 Band.kpath> 

576 """ 

577 if kpts is None: 

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

579 band_kpath[0]['end_point'][0], 

580 band_kpath[0][0]) 

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

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

583 band_kpath[0]['kpts']) 

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

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

586 band_kpath[0]['kpts']) 

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

588 for path in band_kpath[1:]: 

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

590 path['end_point'][0], 

591 path['kpts']) 

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

593 path['end_point'][1], 

594 path['kpts']) 

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

596 path['end_point'][2], 

597 path['kpts']) 

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

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

600 return kpts 

601 elif band_kpath is None: 

602 band_kpath = [] 

603 points = np.asarray(kpts) 

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

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

606 N = len(points) 

607 indices = [0] 

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

609 indices.append(N - 1) 

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

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

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

613 'kpts': 20, 

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

615 else: 

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

617 return band_kpath 

618 

619 

620def write_string(fd, key, value): 

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

622 fd.write("\n") 

623 

624 

625def write_tuple_integer(fd, key, value): 

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

627 fd.write("\n") 

628 

629 

630def write_tuple_float(fd, key, value): 

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

632 fd.write("\n") 

633 

634 

635def write_tuple_bool(fd, key, value): 

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

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

638 fd.write("\n") 

639 

640 

641def write_integer(fd, key, value): 

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

643 fd.write("\n") 

644 

645 

646def write_float(fd, key, value): 

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

648 fd.write("\n") 

649 

650 

651def write_bool(fd, key, value): 

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

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

654 fd.write("\n") 

655 

656 

657def write_list_int(fd, key, value): 

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

659 

660 

661def write_list_bool(fd, key, value): 

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

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

664 

665 

666def write_list_float(fd, key, value): 

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

668 

669 

670def write_matrix(fd, key, value): 

671 fd.write('<' + key) 

672 fd.write("\n") 

673 for line in value: 

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

675 fd.write("\n") 

676 fd.write(key + '>') 

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

678 

679 

680def get_openmx_key(key): 

681 """ 

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

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

684 """ 

685 for openmx_key in keys: 

686 for openmx_keyword in openmx_key: 

687 if key == get_standard_key(openmx_keyword): 

688 return openmx_keyword