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

325 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +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.units import Bohr, Ha, Ry, fs, m, s 

29 

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

31 param.tuple_bool_keys, param.integer_keys, param.float_keys, 

32 param.string_keys, param.bool_keys, param.list_int_keys, 

33 param.list_bool_keys, param.list_float_keys, param.matrix_keys] 

34 

35 

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

37 system_changes=None): 

38 """ 

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

40 First, set 

41 Write input (dat)-file. 

42 See calculator.py for further details. 

43 

44 Parameters: 

45 - atoms : The Atoms object to write. 

46 - properties : The properties which should be calculated. 

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

48 """ 

49 from ase.calculators.openmx import parameters as param 

50 filtered_keywords = parameters_to_keywords(label=label, atoms=atoms, 

51 parameters=parameters, 

52 properties=properties, 

53 system_changes=system_changes) 

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

55 'tuple_integer', 'tuple_float', 'tuple_bool', 

56 'matrix', 'list_int', 'list_bool', 'list_float'] 

57 # Start writing the file 

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

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

60 # Write 1-line keywords 

61 for fltrd_keyword in filtered_keywords.keys(): 

62 for key in keys: 

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

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

65 for omx_keyword in openmx_keywords: 

66 if fltrd_keyword == get_standard_key(omx_keyword): 

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

68 

69 

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

71 properties=None, system_changes=None): 

72 """ 

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

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

75 dictionary using standard parameters. If standard parameters and openmx 

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

77 It includes, 

78 

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

80 """ 

81 from collections import OrderedDict 

82 

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

84 unit_dat_keywords) 

85 keywords = OrderedDict() 

86 sequence = [ 

87 'system_currentdirectory', 'system_name', 'data_path', 

88 'level_of_fileout', 

89 'species_number', 'definition_of_atomic_species', 

90 'atoms_number', 'atoms_speciesandcoordinates_unit', 

91 'atoms_speciesandcoordinates', 'atoms_unitvectors_unit', 

92 'atoms_unitvectors', 'band_dispersion', 'band_nkpath', 

93 'band_kpath'] 

94 

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

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

97 counterparts = { 

98 'system_currentdirectory': curdir, 

99 'system_name': prefix, 

100 'data_path': os.environ.get('OPENMX_DFT_DATA_PATH'), 

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

102 'atoms_number': len(atoms), 

103 'scf_restart': 'restart', 

104 'scf_maxiter': 'maxiter', 

105 'scf_xctype': 'xc', 

106 'scf_energycutoff': 'energy_cutoff', 

107 'scf_criterion': 'convergence', 

108 'scf_external_fields': 'external', 

109 'scf_mixing_type': 'mixer', 

110 'scf_electronic_temperature': 'smearing', 

111 'scf_system_charge': 'charge', 

112 'scf_eigenvaluesolver': 'eigensolver' 

113 } 

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

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

116 'Ha/Bohr': Ha / Bohr, 

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

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

119 

120 for key in sequence: 

121 keywords[key] = None 

122 for key in parameters: 

123 if 'scf' in key: 

124 keywords[key] = None 

125 for key in parameters: 

126 if 'md' in key: 

127 keywords[key] = None 

128 

129 # Initializes keywords to to given parameters 

130 for key in parameters.keys(): 

131 keywords[key] = parameters[key] 

132 

133 def parameter_overwrites(openmx_keyword): 

134 """ 

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

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

137 converted to OpenMX unit. 

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

139 part in suitable manner 

140 openmx_keyword : key | Name of key used in OpenMX 

141 keyword : value | value corresponds to openmx_keyword 

142 ase_parameter : key | Name of parameter used in ASE 

143 parameter : value | value corresponds to ase_parameter 

144 """ 

145 ase_parameter = counterparts[openmx_keyword] 

146 keyword = parameters.get(openmx_keyword) 

147 parameter = parameters.get(ase_parameter) 

148 if parameter is not None: 

149 # Handles the unit 

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

151 if unit is not None: 

152 return parameter / unit 

153 return parameter 

154 elif keyword is not None: 

155 return keyword 

156 elif 'scf' in openmx_keyword: 

157 return None 

158 else: 

159 return counterparts[openmx_keyword] 

160 

161 # Overwrites openmx keyword using standard parameters 

162 for openmx_keyword in counterparts: 

163 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword) 

164 

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

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

167 if 'energies' in properties: 

168 keywords['energy_decomposition'] = True 

169 if 'stress' in properties: 

170 keywords['scf_stress_tensor'] = True 

171 

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

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

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

175 

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

177 keywords['band_dispersion'] = True 

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

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

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

181 

182 # Set up Wannier Environment 

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

184 keywords['species_number'] *= 2 

185 

186 # Set up some parameters for the later use 

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

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

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

190 

191 # Set up the matrix-type OpenMX keywords 

192 for key in matrix_keys: 

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

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

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

196 if not (v is None or 

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

198 

199 

200def get_species(symbols): 

201 species = [] 

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

203 return species 

204 

205 

206def get_xc(xc): 

207 """ 

208 Change the name of xc appropriate to OpenMX format 

209 """ 

210 xc = xc.upper() 

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

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

213 return 'GGA-PBE' 

214 elif xc in ['LDA']: 

215 return 'LDA' 

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

217 return 'LSDA-' + xc 

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

219 return 'LSDA-CA' 

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

221 return 'LSDA-PW' 

222 else: 

223 return 'LDA' 

224 

225 

226def get_vps(xc): 

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

228 return 'PBE' 

229 else: 

230 return 'CA' 

231 

232 

233def get_scf_kgrid(atoms, parameters): 

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

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

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

237 return kpts 

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

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

240 else: 

241 return scf_kgrid 

242 

243 

244def get_definition_of_atomic_species(atoms, parameters): 

245 """ 

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

247 where matrix of strings contains the information between keywords. 

248 For example, 

249 definition_of_atomic_species = 

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

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

252 Goes to, 

253 <Definition.of.Atomic.Species 

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

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

256 Definition.of.Atomic.Species> 

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

258 A. Define local functions for projectors 

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

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

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

262 Species.Number 2 

263 <Definition.of.Atomic.Species 

264 Si Si7.0-s2p2d1 Si_CA13 

265 proj1 Si5.5-s1p1d1f1 Si_CA13 

266 Definition.of.Atomic.Species> 

267 """ 

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

269 return parameters['definition_of_atomic_species'] 

270 

271 definition_of_atomic_species = [] 

272 xc = parameters.get('_xc') 

273 year = parameters.get('_year') 

274 

275 chem = atoms.get_chemical_symbols() 

276 species = get_species(chem) 

277 for element in species: 

278 rad_orb = get_cutoff_radius_and_orbital(element=element) 

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

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

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

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

283 for i, projector in enumerate(wannier_projectors): 

284 full_projector = definition_of_atomic_species[i] 

285 full_projector[0] = projector 

286 definition_of_atomic_species.append(full_projector) 

287 return definition_of_atomic_species 

288 

289 

290def get_dft_data_year(parameters): 

291 """ 

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

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

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

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

296 such pseudo potential path. 

297 """ 

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

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

300 data_path = parameters['_data_path'] 

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

302 if year is not None: 

303 return year 

304 else: 

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

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

307 

308 

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

310 """ 

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

312 orbital using default_settings.py. For example, 

313 'Si' -> 'Si.7.0-s2p2d1' 

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

315 change the default_settings.py directly. 

316 """ 

317 from ase.calculators.openmx import default_settings 

318 orbital = element 

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

320 default_dictionary = default_settings.default_dictionary 

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

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

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

324 for i, orbital_number in enumerate(orbital_numbers): 

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

326 return orbital 

327 

328 

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

330 """ 

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

332 For example, 

333 'Si' -> 'Si_CA13' 

334 or 

335 'Si' -> 'Si_CA19' 

336 depending on pseudo potential generation year 

337 """ 

338 from ase.calculators.openmx import default_settings 

339 default_dictionary = default_settings.default_dictionary 

340 pseudo_potential_suffix = element 

341 vps = get_vps(xc) 

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

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

344 return pseudo_potential_suffix 

345 

346 

347def get_atoms_speciesandcoordinates(atoms, parameters): 

348 """ 

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

350 keyword 

351 'Atoms.SpeciesAndCoordinates' as follows: 

352 <Atoms.SpeciesAndCoordinates 

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

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

355 Atoms.SpeciesAndCoordinates> 

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

357 """ 

358 atoms_speciesandcoordinates = [] 

359 xc = parameters.get('_xc') 

360 year = parameters.get('_year') 

361 data_pth = parameters.get('_data_path') 

362 # Appending number and elemental symbol 

363 elements = atoms.get_chemical_symbols() 

364 for i, element in enumerate(elements): 

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

366 # Appending positions 

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

368 if unit == 'ang': 

369 positions = atoms.get_positions() 

370 elif unit == 'frac': 

371 positions = atoms.get_scaled_positions(wrap=False) 

372 elif unit == 'au': 

373 positions = atoms.get_positions() / Bohr 

374 for i, position in enumerate(positions): 

375 atoms_speciesandcoordinates[i].extend(position) 

376 

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

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

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

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

381 atoms_spncrd[i][2] = atoms_speciesandcoordinates[i][2] 

382 atoms_spncrd[i][3] = atoms_speciesandcoordinates[i][3] 

383 atoms_spncrd[i][4] = atoms_speciesandcoordinates[i][4] 

384 return atoms_spncrd 

385 

386 # Appending magnetic moment 

387 magmoms = atoms.get_initial_magnetic_moments() 

388 for i, magmom in enumerate(magmoms): 

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

390 atoms_speciesandcoordinates[i].extend(up_down_spin) 

391 # Appending magnetic field Spin magnetic moment theta phi 

392 spin_directions = get_spin_direction(magmoms) 

393 for i, spin_direction in enumerate(spin_directions): 

394 atoms_speciesandcoordinates[i].extend(spin_direction) 

395 # Appending magnetic field for Orbital magnetic moment theta phi 

396 orbital_directions = get_orbital_direction() 

397 for i, orbital_direction in enumerate(orbital_directions): 

398 atoms_speciesandcoordinates[i].extend(orbital_direction) 

399 # Appending Noncolinear schem switch 

400 noncollinear_switches = get_noncollinear_switches() 

401 for i, noncollinear_switch in enumerate(noncollinear_switches): 

402 atoms_speciesandcoordinates[i].extend(noncollinear_switch) 

403 # Appending orbital_enhancement_switch 

404 lda_u_switches = get_lda_u_switches() 

405 for i, lda_u_switch in enumerate(lda_u_switches): 

406 atoms_speciesandcoordinates[i].extend(lda_u_switch) 

407 return atoms_speciesandcoordinates 

408 

409 

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

411 magmom = np.linalg.norm(magmom) 

412 suffix = get_pseudo_potential_suffix(element, xc, year) 

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

414 valence_electron = float(read_electron_valency(filename)) 

415 return [valence_electron / 2 + magmom / 2, 

416 valence_electron / 2 - magmom / 2] 

417 

418 

419def get_spin_direction(magmoms): 

420 ''' 

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

422 ''' 

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

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

425 return [] 

426 else: 

427 magmoms = np.array(magmoms) 

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

429 

430 

431def get_orbital_direction(): 

432 orbital_direction = [] 

433 # print("Not Implemented Yet") 

434 return orbital_direction 

435 

436 

437def get_noncollinear_switches(): 

438 noncolinear_switches = [] 

439 # print("Not Implemented Yet") 

440 return noncolinear_switches 

441 

442 

443def get_lda_u_switches(): 

444 lda_u_switches = [] 

445 # print("Not Implemented Yet") 

446 return lda_u_switches 

447 

448 

449def get_spinpol(atoms, parameters): 

450 ''' Judgeds the keyword 'scf.SpinPolarization' 

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

452 1. standard_spinpol 

453 2. scf_spinpolarization 

454 3. magnetic moments of atoms 

455 ''' 

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

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

458 m = atoms.get_initial_magnetic_moments() 

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

460 None: None, 'nc': 'NC'} 

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

462 if scf_spinpolarization is not None: 

463 spinpol = scf_spinpolarization 

464 if standard_spinpol is not None: 

465 spinpol = standard_spinpol 

466 if isinstance(spinpol, str): 

467 spinpol = spinpol.lower() 

468 return syn[spinpol] 

469 

470 

471def get_atoms_unitvectors(atoms, parameters): 

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

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

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

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

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

477 if unit == 'ang': 

478 atoms_unitvectors = atoms.get_cell() 

479 elif unit == 'au': 

480 atoms_unitvectors = atoms.get_cell() / Bohr 

481 return atoms_unitvectors 

482 

483 

484def get_hubbard_u_values(atoms, parameters): 

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

486 

487 

488def get_atoms_cont_orbitals(atoms, parameters): 

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

490 

491 

492def get_md_fixed_xyz(atoms, parameters): 

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

494 

495 

496def get_md_tempcontrol(atoms, parameters): 

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

498 

499 

500def get_md_init_velocity(atoms, parameters): 

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

502 

503 

504def get_band_kpath_unitcell(atoms, parameters): 

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

506 

507 

508def get_band_kpath(atoms, parameters): 

509 kpts = parameters.get('kpts') 

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

511 return get_kpath(kpts=kpts) 

512 else: 

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

514 

515 

516def get_mo_kpoint(atoms, parameters): 

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

518 

519 

520def get_wannier_initial_projectors(atoms, parameters): 

521 """ 

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

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

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

525 x-axis for orbital orientation. 

526 

527 An example setting is shown here: 

528 wannier_initial_projectors= 

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

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

531 Goes to, 

532 <Wannier.Initial.Projectors 

533 proj1-sp3 0.250 0.250 0.250 -1.0 0.0 0.0 0.0 0.0 -1.0 

534 proj1-sp3 0.000 0.000 0.000 0.0 0.0 1.0 1.0 0.0 0.0 

535 Wannier.Initial.Projectors> 

536 """ 

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

538 

539 

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

541 """ 

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

543 by using dft space group method 

544 For example, 

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

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

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

548 (0.5, 0.5, 0.5) ... ... , 

549 ... ... ... , 

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

551 band_kpath = 

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

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

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

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

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

557 where, it will be written as 

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 Band.kpath> 

565 """ 

566 if kpts is None: 

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

568 band_kpath[0]['end_point'][0], 

569 band_kpath[0][0]) 

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

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

572 band_kpath[0]['kpts']) 

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

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

575 band_kpath[0]['kpts']) 

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

577 for path in band_kpath[1:]: 

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

579 path['end_point'][0], 

580 path['kpts']) 

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

582 path['end_point'][1], 

583 path['kpts']) 

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

585 path['end_point'][2], 

586 path['kpts']) 

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

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

589 return kpts 

590 elif band_kpath is None: 

591 band_kpath = [] 

592 points = np.asarray(kpts) 

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

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

595 N = len(points) 

596 indices = [0] 

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

598 indices.append(N - 1) 

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

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

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

602 'kpts': 20, 

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

604 else: 

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

606 return band_kpath 

607 

608 

609def write_string(fd, key, value): 

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

611 fd.write("\n") 

612 

613 

614def write_tuple_integer(fd, key, value): 

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

616 fd.write("\n") 

617 

618 

619def write_tuple_float(fd, key, value): 

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

621 fd.write("\n") 

622 

623 

624def write_tuple_bool(fd, key, value): 

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

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

627 fd.write("\n") 

628 

629 

630def write_integer(fd, key, value): 

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

632 fd.write("\n") 

633 

634 

635def write_float(fd, key, value): 

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

637 fd.write("\n") 

638 

639 

640def write_bool(fd, key, value): 

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

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

643 fd.write("\n") 

644 

645 

646def write_list_int(fd, key, value): 

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

648 

649 

650def write_list_bool(fd, key, value): 

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

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

653 

654 

655def write_list_float(fd, key, value): 

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

657 

658 

659def write_matrix(fd, key, value): 

660 fd.write('<' + key) 

661 fd.write("\n") 

662 for line in value: 

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

664 fd.write("\n") 

665 fd.write(key + '>') 

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

667 

668 

669def get_openmx_key(key): 

670 """ 

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

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

673 """ 

674 for openmx_key in keys: 

675 for openmx_keyword in openmx_key: 

676 if key == get_standard_key(openmx_keyword): 

677 return openmx_keyword