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
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-30 08:22 +0000
1# fmt: off
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
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.
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.
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
24import numpy as np
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
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]
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.
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])
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,
86 For aesthetical purpose, sequnece of writing input file is specified.
87 """
88 from collections import OrderedDict
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']
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()}
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
135 # Initializes keywords to to given parameters
136 for key in parameters.keys():
137 keywords[key] = parameters[key]
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]
167 # Overwrites openmx keyword using standard parameters
168 for openmx_keyword in counterparts:
169 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword)
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
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)
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'])
188 # Set up Wannier Environment
189 if parameters.get('wannier_func_calc') is not None:
190 keywords['species_number'] *= 2
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)
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 == []))])
206def get_species(symbols):
207 species = []
208 [species.append(s) for s in symbols if s not in species]
209 return species
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'
232def get_vps(xc):
233 if xc in ['GGA-PBE']:
234 return 'PBE'
235 else:
236 return 'CA'
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
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']
277 definition_of_atomic_species = []
278 xc = parameters.get('_xc')
279 year = parameters.get('_year')
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
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')
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
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
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)
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
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
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]
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)
443def get_orbital_direction():
444 orbital_direction = []
445 # print("Not Implemented Yet")
446 return orbital_direction
449def get_noncollinear_switches():
450 noncolinear_switches = []
451 # print("Not Implemented Yet")
452 return noncolinear_switches
455def get_lda_u_switches():
456 lda_u_switches = []
457 # print("Not Implemented Yet")
458 return lda_u_switches
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]
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
496def get_hubbard_u_values(atoms, parameters):
497 return parameters.get('hubbard_u_values', [])
500def get_atoms_cont_orbitals(atoms, parameters):
501 return parameters.get('atoms_cont_orbitals', [])
504def get_md_fixed_xyz(atoms, parameters):
505 return parameters.get('md_fixed_xyz', [])
508def get_md_tempcontrol(atoms, parameters):
509 return parameters.get('md_tempcontrol', [])
512def get_md_init_velocity(atoms, parameters):
513 return parameters.get('md_init_velocity', [])
516def get_band_kpath_unitcell(atoms, parameters):
517 return parameters.get('band_kpath_unitcell', [])
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', [])
528def get_mo_kpoint(atoms, parameters):
529 return parameters.get('mo_kpoint', [])
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.
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', [])
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
621def write_string(fd, key, value):
622 fd.write(" ".join([key, value]))
623 fd.write("\n")
626def write_tuple_integer(fd, key, value):
627 fd.write(" ".join([key, "%d %d %d" % value]))
628 fd.write("\n")
631def write_tuple_float(fd, key, value):
632 fd.write(" ".join([key, "%.4f %.4f %.4f" % value]))
633 fd.write("\n")
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")
643def write_integer(fd, key, value):
644 fd.write(" ".join([key, "%d" % value]))
645 fd.write("\n")
648def write_float(fd, key, value):
649 fd.write(" ".join([key, "%.8g" % value]))
650 fd.write("\n")
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")
659def write_list_int(fd, key, value):
660 fd.write("".join(key) + ' ' + " ".join(map(str, value)))
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]))
668def write_list_float(fd, key, value):
669 fd.write("".join(key) + ' ' + " ".join(map(str, value)))
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")
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