Source code for htesp.wannier90

#!/usr/bin/env python
#"""Written by Niraj K. Nepal, Ph.D"""
"""Module to prepare wannier90 input files"""
import os
import json
import shutil
import numpy as np
from ase.io import espresso
from htesp.kpoint_path import kpoint_path

# Number of Wannier functions contributed by one projection of each type.
_ORBITAL_SIZE = {"s": 1, "p": 3, "d": 5, "f": 7,
                 "sp": 2, "sp2": 3, "sp3": 4, "sp3d": 5, "sp3d2": 6}

# Fallback used when `num_wann` is absent and cannot be derived at all.
DEFAULT_NUM_WANN = None


def _as_bool(value):
    """
    Interpret a wannier90/JSON flag as a Python bool.

    FIX(9): ``plot_settings['bands_plot'] == '.true.'`` is a *string* compare.
    The shipped JSON happens to hold the string ``".true."``, but a JSON
    boolean ``true`` - which the schema also allows - silently failed the test
    and the whole ``Begin Kpoint_Path`` block was skipped.

    Parameters
    ----------
    value : bool or str or int or None
        Flag as read from the JSON file.

    Returns
    -------
    bool
        True for ``True``, ``'.true.'``, ``'true'``, ``'t'``, ``'.t.'``,
        ``'yes'``, ``1``; False otherwise.
    """
    if isinstance(value, bool):
        return value
    if isinstance(value, (int, float)):
        return bool(value)
    if isinstance(value, str):
        return value.strip().strip(".").lower() in ("true", "t", "yes", "1")
    return False


def _count_projection_wannier(projection_file="projection.in"):
    """
    Count the Wannier functions requested by a ``projection.in`` file.

    Each non-empty line looks like ``X:s;p`` or ``f=0.25,0.25,0.25:s``; the
    orbitals after the final ``:`` are separated by ``;``.

    Parameters
    ----------
    projection_file : str
        Path to the projection file.

    Returns
    -------
    int or None
        The number of Wannier functions, or None if the file is absent or
        holds an orbital this table does not know.
    """
    if not os.path.isfile(projection_file):
        return None
    total = 0
    with open(projection_file, "r") as read_proj:
        for line in read_proj:
            line = line.split("!")[0].split("#")[0].strip()
            if not line or ":" not in line:
                continue
            for orb in line.rsplit(":", 1)[1].split(";"):
                orb = orb.strip().lower()
                if not orb:
                    continue
                if orb not in _ORBITAL_SIZE:
                    return None
                total += _ORBITAL_SIZE[orb]
    return total or None


[docs] def resolve_num_wann(config_settings, projection_file="projection.in"): """ Work out ``num_wann`` for the wannier90 input. FIX(8): ``config_settings["num_wann"]`` raised KeyError with the shipped ``utility/input_files/wannier90.json``, which does not define the key, so every VASP wannier90 input failed. The value is now looked up with ``.get`` and derived when it is missing. Resolution order: 1. ``config_settings['num_wann']`` if present; 2. the number of orbitals in ``projection_file``; 3. ``config_settings['num_bands']`` (a crude upper bound - a warning is printed); 4. otherwise a ValueError naming exactly what to add to the JSON file. Parameters ---------- config_settings : dict The ``config_settings`` block of the wannier90 JSON file. projection_file : str Projection file used for step 2. Default: 'projection.in'. Returns ------- int The number of Wannier functions. """ num_wann = config_settings.get("num_wann", DEFAULT_NUM_WANN) if num_wann is not None: return int(num_wann) derived = _count_projection_wannier(projection_file) if derived is not None: print("num_wann not set in the wannier90 JSON file; " "derived {} from {}\n".format(derived, projection_file)) return derived num_bands = config_settings.get("num_bands") if num_bands is not None: print("WARNING: num_wann not set and no usable {}; " "falling back to num_bands = {}\n".format(projection_file, num_bands)) return int(num_bands) raise ValueError( "cannot determine 'num_wann': add \"num_wann\": <n> to the " "\"config_settings\" block of the wannier90 JSON file, or provide a " "'{}' file listing the projections".format(projection_file))
[docs] def epw_bandcheck(infile='scf.in', out="ex.win", proj=" ", json_file="wannier90.json", dft="QE"): """ function to write input file for wannier90 calculations. Default: 'ex.win' parameters ------------- infile : input file for scf calculation out : output file. Default: 'ex.win' proj : 'scdm' if used SCDM projection, '' otherwise json_file: JSON file containing element-value pairs for configurational settings dft: DFT package name, can be 'QE' (Quantum ESPRESSO), 'VASP', or 'other' """ data = None if os.path.isfile("scf_dir/{}".format(infile)): data = espresso.read_espresso_in("scf_dir/{}".format(infile)) else: print("Creates wannier-vasp.in from json file\n") # Load settings from combined JSON file with open(json_file, 'r') as read_json_file: all_settings = json.load(read_json_file) config_settings = all_settings["config_settings"] plot_settings = all_settings["plot_settings"] if dft in ("VASP","vasp"): # Write the WANNIER90_WIN file for VASP with open("wannier-vasp.in", 'w') as wannier_vasp_file: settings_str = " ".join(["{}={}\n".format(k, v) for k, v in config_settings.items()]) # Include projection settings for VASP settings_str += "Begin Projections\n" if proj == 'scdm': settings_str += "auto_projections=.true.\n" elif proj == "fromfile": # FIX: os.system("cp ...") replaced by shutil.copy. if os.path.isfile("../../projection.in"): shutil.copy("../../projection.in", "projection.in") if os.path.isfile("projection.in"): with open("projection.in", "r") as gfile: lines = gfile.readlines() len_l = len(lines) projections = "" for line in lines: projections += line #projections = " ".join([line.strip() for line in lines]) settings_str += "{}".format(projections) else: print("projection.in file not found\n") print("write projections in different line, 'X:s', 'Y:pz', ... so on\n") else: settings_str += "random\n" settings_str += "End Projections\n" settings_str += " ".join(["{}={}\n".format(k, v) for k, v in plot_settings.items()]) # FIX(9): accept a JSON boolean as well as '.true.'/'true'. if _as_bool(plot_settings.get('bands_plot')): kpoint_path(infile) settings_str += "Begin Kpoint_Path\n" with open("wannier_kpath.in", "r") as gfile: lines = gfile.readlines() for line in lines: settings_str += line settings_str += "END Kpoint_Path\n" # FIX(8): was config_settings["num_wann"] -> KeyError with the # shipped utility/input_files/wannier90.json. num_wann = resolve_num_wann(config_settings) wannier_vasp_file.write(f"NUM_WANN = {num_wann}\n") wannier_vasp_file.write("WANNIER90_WIN = \" \n") wannier_vasp_file.write("{}".format(settings_str)) wannier_vasp_file.write("\"") elif dft in ("QE","qe"): if data is None: raise FileNotFoundError( "scf_dir/{} not found; the QE branch needs it to read the " "cell and atomic positions".format(infile)) # Extract relevant data from the input file cell = data.cell symbol = data.get_chemical_symbols() pos = data.get_scaled_positions() kmesh = np.loadtxt('kmesh.grid') # Write the standard EPW input file with open(out, 'w') as epw_write: # Write configuration settings from JSON file for key, value in config_settings.items(): epw_write.write("{} = {}\n".format(key, value)) # Write Kpoint Path epw_write.write("Begin Kpoint_Path\n") with open("wannier_kpath.in", "r") as gfile: lines = gfile.readlines() for line in lines: epw_write.write(line) epw_write.write("End Kpoint_Path\n\n") # Write projections based on the 'proj' parameter if proj == 'scdm': epw_write.write("auto_projections = .true.\n") elif proj == "fromfile": if os.path.isfile("projection.in"): with open("projection.in", "r") as gfile: lines = gfile.readlines() len_l = len(lines) epw_write.write("begin projections\n") for i in range(len_l): epw_write.write("{}\n".format(lines[i].split("\n")[0])) epw_write.write("end projections\n") else: print("projection.in file not found\n") print("write projections in different line, 'X:s', 'Y:pz', ... so on\n") else: epw_write.write("begin projections\n") epw_write.write("random\n") epw_write.write("end projections\n") # Write remaining data for key, value in plot_settings.items(): epw_write.write("{} = {}\n".format(key, value)) epw_write.write("begin unit_cell_cart\n") epw_write.write("Ang\n") for lat in cell: epw_write.write("{} {} {}\n".format(lat[0], lat[1], lat[2])) epw_write.write("end unit_cell_cart\n\n") epw_write.write("begin atoms_frac\n") for sym, pos in zip(symbol, pos): epw_write.write("{} {} {} {}\n".format(sym, pos[0], pos[1], pos[2])) epw_write.write("end atoms_frac\n\n") epw_write.write("mp_grid : {} {} {}\n".format(int(kmesh[0]), int(kmesh[1]), int(kmesh[2]))) epw_write.write("begin kpoints\n") with open('wann_grid.out', 'r') as gfile: lines = gfile.readlines() for line in lines: epw_write.write(line) epw_write.write("end kpoints\n") else: print("Allowed values for 'dft' parameter are 'QE' or 'VASP'.")
if __name__ == "__main__": epw_bandcheck(dft="VASP")