Source code for aim2dat.io.fhi_aims

"""
Module of functions to read output-files of FHI-aims.
"""

# Standard library imports
import os
import re


def _extract_file_names(files, pattern):
    # Need to include spin-polarized case...
    # Use auxiliary functions...
    found_files = []
    file_numbers = []
    for file in files:
        file_nr = pattern.findall(file)
        if file_nr:
            found_files.append(file)
            file_numbers.append(file_nr[0])
    if len(found_files) > 0 and len(file_numbers) == len(found_files):
        found_files, file_numbers = zip(*sorted(zip(found_files, file_numbers)))
    return found_files, file_numbers


[docs] def read_band_structure(folder_path, soc=False): """ Read band structure files from FHI-aims. Spin-polarized calculations are not yet supported. Parameters ---------- folder_path : str Path to the folder of the band structure files. soc : bool (optional) Whether spin-orbit coupling is activated. The default value is ``False``. Returns ------- band_structure : dict Dictionary containing the k-path and th eigenvalues as well as the occupations. """ if len(folder_path) > 0: folder_path += "/" # Define file-name patterns: pattern = re.compile(r"^band(\d*)?\.out$") pattern_no_soc = re.compile(r"^band(\d*)?\.out\.no_soc$") # Check the files in the folder: files = [ file for file in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, file)) ] if soc: if "band1001.out.no_soc" not in files: raise ValueError( "Spin-orbit coupling activated but the files don't have " "the right naming scheme." ) band_structure_files, _ = _extract_file_names(files, pattern) else: if "band1001.out.no_soc" in files: band_structure_files, _ = _extract_file_names(files, pattern_no_soc) else: band_structure_files, _ = _extract_file_names(files, pattern) if len(band_structure_files) == 0: raise ValueError("No band structure files found.") occupations = [] bands = [] kpoints = [] for file_idx, file in enumerate(band_structure_files): bandfile = open(folder_path + file, "r") for line_idx, line in enumerate(bandfile): nrbands = int((len(line.split()) - 4) * 0.5) band0 = [] occupation0 = [] for band_idx in range(nrbands): occupation0.append(float(line.split()[2 * band_idx + 4])) band0.append(float(line.split()[2 * band_idx + 5])) kpoints.append([float(line.split()[idx]) for idx in range(1, 4)]) bands.append(band0) occupations.append(occupation0) bandfile.close() return {"kpoints": kpoints, "unit_y": "eV", "bands": bands, "occupations": occupations}
[docs] def read_total_density_of_states(file_name): """ Read the total density of states from FHI-aims. Parameters ---------- file_name : str Path of the output-file of FHI-aims containing the total density of states. Returns ------- pdos : dict Dictionary containing the projected density of states for each atom. """ energy = [] tdos = [] with open(file_name, "r") as tdos_file: for line in tdos_file: if not line.startswith("#"): energy.append(float(line.split()[0])) tdos.append(float(line.split()[1])) return {"energy": energy, "tdos": tdos, "unit_x": "eV"}
[docs] def read_atom_proj_density_of_states(folder_path, soc=False, load_raw=False): """ Read the atom projected density of states from FHI-aims. Parameters ---------- folder_path : str Path to the folder of the pdos files. soc : bool (optional) Whether spin-orbit coupling is activated. The default value is ``False``. load_raw : bool (optional) Load files with appendix 'raw'. The default value is ``False``. Returns ------- pdos : dict Dictionary containing the projected density of states for each atom. """ dict_labels = ["s", "p", "d", "f"] energy = [] atomic_pdos = [] if len(folder_path) > 0: folder_path += "/" # Define file-name patterns: if load_raw: pattern = re.compile(r"^atom_proj_dos_([a-zA-Z]*\d*)?_raw\.dat$") pattern_no_soc = re.compile(r"^atom_proj_dos_([a-zA-Z]*\d*)?_raw\.dat\.no_soc$") else: pattern = re.compile(r"^atom_proj[a-z]*_dos_([a-zA-Z]*\d*)?\.dat$") pattern_no_soc = re.compile(r"^atom_proj[a-z]*_dos_([a-zA-Z]*\d*)?\.dat.no_soc$") # Check the files in the folder: files = [ file for file in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, file)) ] if soc: if all([file.endswith(".dat") for file in files]): raise ValueError( "Spin-orbit coupling activated but the files don't have " "the right naming scheme." ) pdos_files, element_nrs = _extract_file_names(files, pattern) else: if any([file.endswith(".no_soc") for file in files]): pdos_files, element_nrs = _extract_file_names(files, pattern_no_soc) else: pdos_files, element_nrs = _extract_file_names(files, pattern) if len(pdos_files) == 0: raise ValueError("No pDOS files found.") print(pdos_files) # Iterate over files and quantum numbers: elements = [re.split(r"(\d+)", el_nr)[0] for el_nr in element_nrs] for el_idx, (element, file_name) in enumerate(zip(elements, pdos_files)): pdos0 = {"element": element} with open(folder_path + file_name, "r") as pdos_file: for line in pdos_file: if line.split()[0] != "#" and len(line.strip()) != 0: if el_idx == 0: energy.append(float(line.split()[0])) for value_idx, value in enumerate(line.split()[2:]): if dict_labels[value_idx] in pdos0: pdos0[dict_labels[value_idx]].append(float(value)) else: pdos0[dict_labels[value_idx]] = [float(value)] atomic_pdos.append(pdos0) return {"energy": energy, "pdos": atomic_pdos, "unit_x": "eV"}