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
« 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
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.
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.
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
22import numpy as np
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
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]
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.
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])
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,
80 For aesthetical purpose, sequnece of writing input file is specified.
81 """
82 from collections import OrderedDict
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']
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()}
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
130 # Initializes keywords to to given parameters
131 for key in parameters.keys():
132 keywords[key] = parameters[key]
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]
162 # Overwrites openmx keyword using standard parameters
163 for openmx_keyword in counterparts:
164 keywords[openmx_keyword] = parameter_overwrites(openmx_keyword)
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
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)
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'])
183 # Set up Wannier Environment
184 if parameters.get('wannier_func_calc') is not None:
185 keywords['species_number'] *= 2
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)
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 == []))])
201def get_species(symbols):
202 species = []
203 [species.append(s) for s in symbols if s not in species]
204 return species
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'
227def get_vps(xc):
228 if xc in ['GGA-PBE']:
229 return 'PBE'
230 else:
231 return 'CA'
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
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']
272 definition_of_atomic_species = []
273 xc = parameters.get('_xc')
274 year = parameters.get('_year')
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
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')
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
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
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)
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
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
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]
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)
438def get_orbital_direction():
439 orbital_direction = []
440 # print("Not Implemented Yet")
441 return orbital_direction
444def get_noncollinear_switches():
445 noncolinear_switches = []
446 # print("Not Implemented Yet")
447 return noncolinear_switches
450def get_lda_u_switches():
451 lda_u_switches = []
452 # print("Not Implemented Yet")
453 return lda_u_switches
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]
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
491def get_hubbard_u_values(atoms, parameters):
492 return parameters.get('hubbard_u_values', [])
495def get_atoms_cont_orbitals(atoms, parameters):
496 return parameters.get('atoms_cont_orbitals', [])
499def get_md_fixed_xyz(atoms, parameters):
500 return parameters.get('md_fixed_xyz', [])
503def get_md_tempcontrol(atoms, parameters):
504 return parameters.get('md_tempcontrol', [])
507def get_md_init_velocity(atoms, parameters):
508 return parameters.get('md_init_velocity', [])
511def get_band_kpath_unitcell(atoms, parameters):
512 return parameters.get('band_kpath_unitcell', [])
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', [])
523def get_mo_kpoint(atoms, parameters):
524 return parameters.get('mo_kpoint', [])
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.
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', [])
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
616def write_string(fd, key, value):
617 fd.write(" ".join([key, value]))
618 fd.write("\n")
621def write_tuple_integer(fd, key, value):
622 fd.write(" ".join([key, "%d %d %d" % value]))
623 fd.write("\n")
626def write_tuple_float(fd, key, value):
627 fd.write(" ".join([key, "%.4f %.4f %.4f" % value]))
628 fd.write("\n")
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")
637def write_integer(fd, key, value):
638 fd.write(" ".join([key, "%d" % value]))
639 fd.write("\n")
642def write_float(fd, key, value):
643 fd.write(" ".join([key, "%.8g" % value]))
644 fd.write("\n")
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")
653def write_list_int(fd, key, value):
654 fd.write("".join(key) + ' ' + " ".join(map(str, value)))
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]))
662def write_list_float(fd, key, value):
663 fd.write("".join(key) + ' ' + " ".join(map(str, value)))
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")
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