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
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
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
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 <http://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 - 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])
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,
85 For aesthetical purpose, sequnece of writing input file is specified.
86 """
87 from collections import OrderedDict
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']
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()}
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
134 # Initializes keywords to to given parameters
135 for key in parameters.keys():
136 keywords[key] = parameters[key]
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]
166 # Overwrites openmx keyword using standard parameters
167 for openmx_keyword in counterparts:
168 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword)
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
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)
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'])
187 # Set up Wannier Environment
188 if parameters.get('wannier_func_calc') is not None:
189 keywords['species_number'] *= 2
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)
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 == []))])
205def get_species(symbols):
206 species = []
207 [species.append(s) for s in symbols if s not in species]
208 return species
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'
231def get_vps(xc):
232 if xc in ['GGA-PBE']:
233 return 'PBE'
234 else:
235 return 'CA'
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
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']
276 definition_of_atomic_species = []
277 xc = parameters.get('_xc')
278 year = parameters.get('_year')
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
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')
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
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
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)
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
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
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]
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)
442def get_orbital_direction():
443 orbital_direction = []
444 # print("Not Implemented Yet")
445 return orbital_direction
448def get_noncollinear_switches():
449 noncolinear_switches = []
450 # print("Not Implemented Yet")
451 return noncolinear_switches
454def get_lda_u_switches():
455 lda_u_switches = []
456 # print("Not Implemented Yet")
457 return lda_u_switches
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]
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
495def get_hubbard_u_values(atoms, parameters):
496 return parameters.get('hubbard_u_values', [])
499def get_atoms_cont_orbitals(atoms, parameters):
500 return parameters.get('atoms_cont_orbitals', [])
503def get_md_fixed_xyz(atoms, parameters):
504 return parameters.get('md_fixed_xyz', [])
507def get_md_tempcontrol(atoms, parameters):
508 return parameters.get('md_tempcontrol', [])
511def get_md_init_velocity(atoms, parameters):
512 return parameters.get('md_init_velocity', [])
515def get_band_kpath_unitcell(atoms, parameters):
516 return parameters.get('band_kpath_unitcell', [])
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', [])
527def get_mo_kpoint(atoms, parameters):
528 return parameters.get('mo_kpoint', [])
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.
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', [])
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
620def write_string(fd, key, value):
621 fd.write(" ".join([key, value]))
622 fd.write("\n")
625def write_tuple_integer(fd, key, value):
626 fd.write(" ".join([key, "%d %d %d" % value]))
627 fd.write("\n")
630def write_tuple_float(fd, key, value):
631 fd.write(" ".join([key, "%.4f %.4f %.4f" % value]))
632 fd.write("\n")
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")
641def write_integer(fd, key, value):
642 fd.write(" ".join([key, "%d" % value]))
643 fd.write("\n")
646def write_float(fd, key, value):
647 fd.write(" ".join([key, "%.8g" % value]))
648 fd.write("\n")
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")
657def write_list_int(fd, key, value):
658 fd.write("".join(key) + ' ' + " ".join(map(str, value)))
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]))
666def write_list_float(fd, key, value):
667 fd.write("".join(key) + ' ' + " ".join(map(str, value)))
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")
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