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

328 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-22 11:22 +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 (get_file_name, get_standard_key, 

27 read_electron_valency) 

28from ase.config import cfg 

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

30 

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

32 param.tuple_bool_keys, param.integer_keys, param.float_keys, 

33 param.string_keys, param.bool_keys, param.list_int_keys, 

34 param.list_bool_keys, param.list_float_keys, param.matrix_keys] 

35 

36 

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

38 system_changes=None): 

39 """ 

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

41 First, set 

42 Write input (dat)-file. 

43 See calculator.py for further details. 

44 

45 Parameters: 

46 - atoms : The Atoms object to write. 

47 - properties : The properties which should be calculated. 

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

49 """ 

50 from ase.calculators.openmx import parameters as param 

51 filtered_keywords = parameters_to_keywords(label=label, atoms=atoms, 

52 parameters=parameters, 

53 properties=properties, 

54 system_changes=system_changes) 

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

56 'tuple_integer', 'tuple_float', 'tuple_bool', 

57 'matrix', 'list_int', 'list_bool', 'list_float'] 

58 # Start writing the file 

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

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

61 # Write 1-line keywords 

62 for fltrd_keyword in filtered_keywords.keys(): 

63 for key in keys: 

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

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

66 for omx_keyword in openmx_keywords: 

67 if fltrd_keyword == get_standard_key(omx_keyword): 

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

69 

70 

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

72 properties=None, system_changes=None): 

73 """ 

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

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

76 dictionary using standard parameters. If standard parameters and openmx 

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

78 It includes, 

79 

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

81 """ 

82 from collections import OrderedDict 

83 

84 from ase.calculators.openmx.parameters import (matrix_keys, 

85 unit_dat_keywords) 

86 keywords = OrderedDict() 

87 sequence = [ 

88 'system_currentdirectory', 'system_name', 'data_path', 

89 'level_of_fileout', 

90 'species_number', 'definition_of_atomic_species', 

91 'atoms_number', 'atoms_speciesandcoordinates_unit', 

92 'atoms_speciesandcoordinates', 'atoms_unitvectors_unit', 

93 'atoms_unitvectors', 'band_dispersion', 'band_nkpath', 

94 'band_kpath'] 

95 

96 directory, prefix = os.path.split(label) 

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

98 counterparts = { 

99 'system_currentdirectory': curdir, 

100 'system_name': prefix, 

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

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

103 'atoms_number': len(atoms), 

104 'scf_restart': 'restart', 

105 'scf_maxiter': 'maxiter', 

106 'scf_xctype': 'xc', 

107 'scf_energycutoff': 'energy_cutoff', 

108 'scf_criterion': 'convergence', 

109 'scf_external_fields': 'external', 

110 'scf_mixing_type': 'mixer', 

111 'scf_electronic_temperature': 'smearing', 

112 'scf_system_charge': 'charge', 

113 'scf_eigenvaluesolver': 'eigensolver' 

114 } 

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

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

117 'Ha/Bohr': Ha / Bohr, 

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

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

120 

121 for key in sequence: 

122 keywords[key] = None 

123 for key in parameters: 

124 if 'scf' in key: 

125 keywords[key] = None 

126 for key in parameters: 

127 if 'md' in key: 

128 keywords[key] = None 

129 

130 # Initializes keywords to to given parameters 

131 for key in parameters.keys(): 

132 keywords[key] = parameters[key] 

133 

134 def parameter_overwrites(openmx_keyword): 

135 """ 

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

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

138 converted to OpenMX unit. 

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

140 part in suitable manner 

141 openmx_keyword : key | Name of key used in OpenMX 

142 keyword : value | value corresponds to openmx_keyword 

143 ase_parameter : key | Name of parameter used in ASE 

144 parameter : value | value corresponds to ase_parameter 

145 """ 

146 ase_parameter = counterparts[openmx_keyword] 

147 keyword = parameters.get(openmx_keyword) 

148 parameter = parameters.get(ase_parameter) 

149 if parameter is not None: 

150 # Handles the unit 

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

152 if unit is not None: 

153 return parameter / unit 

154 return parameter 

155 elif keyword is not None: 

156 return keyword 

157 elif 'scf' in openmx_keyword: 

158 return None 

159 else: 

160 return counterparts[openmx_keyword] 

161 

162 # Overwrites openmx keyword using standard parameters 

163 for openmx_keyword in counterparts: 

164 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword) 

165 

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

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

168 if 'energies' in properties: 

169 keywords['energy_decomposition'] = True 

170 if 'stress' in properties: 

171 keywords['scf_stress_tensor'] = True 

172 

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

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

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

176 

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

178 keywords['band_dispersion'] = True 

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

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

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

182 

183 # Set up Wannier Environment 

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

185 keywords['species_number'] *= 2 

186 

187 # Set up some parameters for the later use 

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

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

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

191 

192 # Set up the matrix-type OpenMX keywords 

193 for key in matrix_keys: 

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

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

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

197 if not (v is None or 

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

199 

200 

201def get_species(symbols): 

202 species = [] 

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

204 return species 

205 

206 

207def get_xc(xc): 

208 """ 

209 Change the name of xc appropriate to OpenMX format 

210 """ 

211 xc = xc.upper() 

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

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

214 return 'GGA-PBE' 

215 elif xc in ['LDA']: 

216 return 'LDA' 

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

218 return 'LSDA-' + xc 

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

220 return 'LSDA-CA' 

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

222 return 'LSDA-PW' 

223 else: 

224 return 'LDA' 

225 

226 

227def get_vps(xc): 

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

229 return 'PBE' 

230 else: 

231 return 'CA' 

232 

233 

234def get_scf_kgrid(atoms, parameters): 

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

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

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

238 return kpts 

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

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

241 else: 

242 return scf_kgrid 

243 

244 

245def get_definition_of_atomic_species(atoms, parameters): 

246 """ 

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

248 where matrix of strings contains the information between keywords. 

249 For example, 

250 definition_of_atomic_species = 

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

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

253 Goes to, 

254 <Definition.of.Atomic.Species 

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

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

257 Definition.of.Atomic.Species> 

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

259 A. Define local functions for projectors 

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

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

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

263 Species.Number 2 

264 <Definition.of.Atomic.Species 

265 Si Si7.0-s2p2d1 Si_CA13 

266 proj1 Si5.5-s1p1d1f1 Si_CA13 

267 Definition.of.Atomic.Species> 

268 """ 

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

270 return parameters['definition_of_atomic_species'] 

271 

272 definition_of_atomic_species = [] 

273 xc = parameters.get('_xc') 

274 year = parameters.get('_year') 

275 

276 chem = atoms.get_chemical_symbols() 

277 species = get_species(chem) 

278 for element in species: 

279 rad_orb = get_cutoff_radius_and_orbital(element=element) 

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

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

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

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

284 for i, projector in enumerate(wannier_projectors): 

285 full_projector = definition_of_atomic_species[i] 

286 full_projector[0] = projector 

287 definition_of_atomic_species.append(full_projector) 

288 return definition_of_atomic_species 

289 

290 

291def get_dft_data_year(parameters): 

292 """ 

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

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

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

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

297 such pseudo potential path. 

298 """ 

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

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

301 data_path = parameters['_data_path'] 

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

303 if year is not None: 

304 return year 

305 else: 

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

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

308 

309 

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

311 """ 

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

313 orbital using default_settings.py. For example, 

314 'Si' -> 'Si.7.0-s2p2d1' 

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

316 change the default_settings.py directly. 

317 """ 

318 from ase.calculators.openmx import default_settings 

319 orbital = element 

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

321 default_dictionary = default_settings.default_dictionary 

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

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

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

325 for i, orbital_number in enumerate(orbital_numbers): 

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

327 return orbital 

328 

329 

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

331 """ 

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

333 For example, 

334 'Si' -> 'Si_CA13' 

335 or 

336 'Si' -> 'Si_CA19' 

337 depending on pseudo potential generation year 

338 """ 

339 from ase.calculators.openmx import default_settings 

340 default_dictionary = default_settings.default_dictionary 

341 pseudo_potential_suffix = element 

342 vps = get_vps(xc) 

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

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

345 return pseudo_potential_suffix 

346 

347 

348def get_atoms_speciesandcoordinates(atoms, parameters): 

349 """ 

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

351 keyword 

352 'Atoms.SpeciesAndCoordinates' as follows: 

353 <Atoms.SpeciesAndCoordinates 

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

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

356 Atoms.SpeciesAndCoordinates> 

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

358 """ 

359 atoms_speciesandcoordinates = [] 

360 xc = parameters.get('_xc') 

361 year = parameters.get('_year') 

362 data_pth = parameters.get('_data_path') 

363 # Appending number and elemental symbol 

364 elements = atoms.get_chemical_symbols() 

365 for i, element in enumerate(elements): 

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

367 # Appending positions 

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

369 if unit == 'ang': 

370 positions = atoms.get_positions() 

371 elif unit == 'frac': 

372 positions = atoms.get_scaled_positions(wrap=False) 

373 elif unit == 'au': 

374 positions = atoms.get_positions() / Bohr 

375 for i, position in enumerate(positions): 

376 atoms_speciesandcoordinates[i].extend(position) 

377 

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

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

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

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

382 atoms_spncrd[i][2] = atoms_speciesandcoordinates[i][2] 

383 atoms_spncrd[i][3] = atoms_speciesandcoordinates[i][3] 

384 atoms_spncrd[i][4] = atoms_speciesandcoordinates[i][4] 

385 return atoms_spncrd 

386 

387 # Appending magnetic moment 

388 magmoms = atoms.get_initial_magnetic_moments() 

389 for i, magmom in enumerate(magmoms): 

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

391 atoms_speciesandcoordinates[i].extend(up_down_spin) 

392 # Appending magnetic field Spin magnetic moment theta phi 

393 spin_directions = get_spin_direction(magmoms) 

394 for i, spin_direction in enumerate(spin_directions): 

395 atoms_speciesandcoordinates[i].extend(spin_direction) 

396 # Appending magnetic field for Orbital magnetic moment theta phi 

397 orbital_directions = get_orbital_direction() 

398 for i, orbital_direction in enumerate(orbital_directions): 

399 atoms_speciesandcoordinates[i].extend(orbital_direction) 

400 # Appending Noncolinear schem switch 

401 noncollinear_switches = get_noncollinear_switches() 

402 for i, noncollinear_switch in enumerate(noncollinear_switches): 

403 atoms_speciesandcoordinates[i].extend(noncollinear_switch) 

404 # Appending orbital_enhancement_switch 

405 lda_u_switches = get_lda_u_switches() 

406 for i, lda_u_switch in enumerate(lda_u_switches): 

407 atoms_speciesandcoordinates[i].extend(lda_u_switch) 

408 return atoms_speciesandcoordinates 

409 

410 

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

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

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

414 # Collinear spin 

415 magmom = float(magmom) 

416 else: 

417 # Non-collinear spin 

418 magmom = np.linalg.norm(magmom) 

419 suffix = get_pseudo_potential_suffix(element, xc, year) 

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

421 valence_electron = float(read_electron_valency(filename)) 

422 return [valence_electron / 2 + magmom / 2, 

423 valence_electron / 2 - magmom / 2] 

424 

425 

426def get_spin_direction(magmoms): 

427 ''' 

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

429 ''' 

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

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

432 return [] 

433 else: 

434 magmoms = np.array(magmoms) 

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

436 

437 

438def get_orbital_direction(): 

439 orbital_direction = [] 

440 # print("Not Implemented Yet") 

441 return orbital_direction 

442 

443 

444def get_noncollinear_switches(): 

445 noncolinear_switches = [] 

446 # print("Not Implemented Yet") 

447 return noncolinear_switches 

448 

449 

450def get_lda_u_switches(): 

451 lda_u_switches = [] 

452 # print("Not Implemented Yet") 

453 return lda_u_switches 

454 

455 

456def get_spinpol(atoms, parameters): 

457 ''' Judgeds the keyword 'scf.SpinPolarization' 

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

459 1. standard_spinpol 

460 2. scf_spinpolarization 

461 3. magnetic moments of atoms 

462 ''' 

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

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

465 m = atoms.get_initial_magnetic_moments() 

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

467 None: None, 'nc': 'NC'} 

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

469 if scf_spinpolarization is not None: 

470 spinpol = scf_spinpolarization 

471 if standard_spinpol is not None: 

472 spinpol = standard_spinpol 

473 if isinstance(spinpol, str): 

474 spinpol = spinpol.lower() 

475 return syn[spinpol] 

476 

477 

478def get_atoms_unitvectors(atoms, parameters): 

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

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

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

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

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

484 if unit == 'ang': 

485 atoms_unitvectors = atoms.get_cell() 

486 elif unit == 'au': 

487 atoms_unitvectors = atoms.get_cell() / Bohr 

488 return atoms_unitvectors 

489 

490 

491def get_hubbard_u_values(atoms, parameters): 

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

493 

494 

495def get_atoms_cont_orbitals(atoms, parameters): 

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

497 

498 

499def get_md_fixed_xyz(atoms, parameters): 

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

501 

502 

503def get_md_tempcontrol(atoms, parameters): 

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

505 

506 

507def get_md_init_velocity(atoms, parameters): 

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

509 

510 

511def get_band_kpath_unitcell(atoms, parameters): 

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

513 

514 

515def get_band_kpath(atoms, parameters): 

516 kpts = parameters.get('kpts') 

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

518 return get_kpath(kpts=kpts) 

519 else: 

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

521 

522 

523def get_mo_kpoint(atoms, parameters): 

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

525 

526 

527def get_wannier_initial_projectors(atoms, parameters): 

528 """ 

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

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

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

532 x-axis for orbital orientation. 

533 

534 An example setting is shown here: 

535 wannier_initial_projectors= 

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

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

538 Goes to, 

539 <Wannier.Initial.Projectors 

540 proj1-sp3 0.250 0.250 0.250 -1.0 0.0 0.0 0.0 0.0 -1.0 

541 proj1-sp3 0.000 0.000 0.000 0.0 0.0 1.0 1.0 0.0 0.0 

542 Wannier.Initial.Projectors> 

543 """ 

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

545 

546 

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

548 """ 

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

550 by using dft space group method 

551 For example, 

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

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

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

555 (0.5, 0.5, 0.5) ... ... , 

556 ... ... ... , 

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

558 band_kpath = 

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

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

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

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

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

564 where, it will be written as 

565 <Band.kpath 

566 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

567 15 1.0 0.0 0.0 1.0 0.5 0.0 X W 

568 15 1.0 0.5 0.0 0.5 0.5 0.5 W L 

569 15 0.5 0.5 0.5 0.0 0.0 0.0 L g 

570 15 0.0 0.0 0.0 1.0 0.0 0.0 g X 

571 Band.kpath> 

572 """ 

573 if kpts is None: 

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

575 band_kpath[0]['end_point'][0], 

576 band_kpath[0][0]) 

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

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

579 band_kpath[0]['kpts']) 

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

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

582 band_kpath[0]['kpts']) 

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

584 for path in band_kpath[1:]: 

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

586 path['end_point'][0], 

587 path['kpts']) 

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

589 path['end_point'][1], 

590 path['kpts']) 

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

592 path['end_point'][2], 

593 path['kpts']) 

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

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

596 return kpts 

597 elif band_kpath is None: 

598 band_kpath = [] 

599 points = np.asarray(kpts) 

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

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

602 N = len(points) 

603 indices = [0] 

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

605 indices.append(N - 1) 

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

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

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

609 'kpts': 20, 

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

611 else: 

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

613 return band_kpath 

614 

615 

616def write_string(fd, key, value): 

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

618 fd.write("\n") 

619 

620 

621def write_tuple_integer(fd, key, value): 

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

623 fd.write("\n") 

624 

625 

626def write_tuple_float(fd, key, value): 

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

628 fd.write("\n") 

629 

630 

631def write_tuple_bool(fd, key, value): 

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

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

634 fd.write("\n") 

635 

636 

637def write_integer(fd, key, value): 

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

639 fd.write("\n") 

640 

641 

642def write_float(fd, key, value): 

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

644 fd.write("\n") 

645 

646 

647def write_bool(fd, key, value): 

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

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

650 fd.write("\n") 

651 

652 

653def write_list_int(fd, key, value): 

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

655 

656 

657def write_list_bool(fd, key, value): 

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

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

660 

661 

662def write_list_float(fd, key, value): 

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

664 

665 

666def write_matrix(fd, key, value): 

667 fd.write('<' + key) 

668 fd.write("\n") 

669 for line in value: 

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

671 fd.write("\n") 

672 fd.write(key + '>') 

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

674 

675 

676def get_openmx_key(key): 

677 """ 

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

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

680 """ 

681 for openmx_key in keys: 

682 for openmx_keyword in openmx_key: 

683 if key == get_standard_key(openmx_keyword): 

684 return openmx_keyword