Source code for pytopomat.vasp2trace_caller

"""
Interface to vasp2trace.

"""

import warnings
import os
from os import path
import subprocess

import numpy as np

from monty.json import MSONable
from monty.dev import requires
from monty.os.path import which

__author__ = "Nathan C. Frey, Jason Munro"
__copyright__ = "MIT License"
__version__ = "0.0.1"
__maintainer__ = "Nathan C. Frey, Jason, Munro"
__email__ = "ncfrey@lbl.gov, jmunro@lbl.gov"
__status__ = "Development"
__date__ = "August 2019"

VASP2TRACEEXE = which("vasp2trace")
VASP2TRACE2EXE = which("vasp2trace2")


class Vasp2TraceCaller:
    @requires(
        VASP2TRACEEXE,
        "Vasp2TraceCaller requires vasp2trace to be in the path."
        "Please follow the instructions at http://www.cryst.ehu.es/cgi-bin/cryst/programs/topological.pl.",
    )
    def __init__(self, folder_name):
        """
        Run vasp2trace to find the set of irreducible representations at each maximal k-vec of a space group, given the eigenvalues.

        vasp2trace requires a self-consistent VASP run with the flags ISTART=0 and ICHARG=2; followed by a band structure calculation with ICHARG=11, ISYM=2, LWAVE=.True.

        High-symmetry kpts that must be included in the band structure path for a given spacegroup can be found in the max_KPOINTS_VASP folder in the vasp2trace directory.

        Args:
            folder_name (str): Path to directory with OUTCAR and WAVECAR of band structure run with wavefunctions at the high-symmetry kpts.
        """

        # Check for OUTCAR and WAVECAR
        if not path.isfile(folder_name + "/OUTCAR") or not path.isfile(
            folder_name + "/WAVECAR"
        ):
            raise FileNotFoundError()

        # Call vasp2trace
        os.chdir(folder_name)
        process = subprocess.Popen(
            ["vasp2trace"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        stdout = stdout.decode()

        if stderr:
            stderr = stderr.decode()
            warnings.warn(stderr)

        if process.returncode != 0:
            raise RuntimeError(
                "vasp2trace exited with return code {}.".format(process.returncode)
            )

        self._stdout = stdout
        self._stderr = stderr
        self.output = None

        # Process output
        if path.isfile("trace.txt"):
            self.output = {}
            self.output["up"] = Vasp2TraceOutput("trace.txt")

        else:
            raise FileNotFoundError()


class Vasp2Trace2Caller:
    @requires(
        VASP2TRACE2EXE,
        "Vasp2Trace2Caller requires vasp2trace2 to be in the path."
        "Please install from https://github.com/zjwang11/irvsp",
    )
    def __init__(self, folder_name):
        """
        Run vasp2trace_v2 to find the set of irreducible representations at each maximal k-vec of a space group, given the eigenvalues.

        version2 of vasp2trace is for spin-polarized calculations. The executable is renamed "vasp2trace2" to avoid conflict with v1.

        vasp2trace requires a self-consistent VASP run with the flags ISTART=0 and ICHARG=2; followed by a band structure calculation with ICHARG=11, ISYM=2, LWAVE=.True.

        High-symmetry kpts that must be included in the band structure path for a given spacegroup can be found in the max_KPOINTS_VASP folder in the vasp2trace directory.

        Args:
            folder_name (str): Path to directory with OUTCAR and WAVECAR of band structure run with wavefunctions at the high-symmetry kpts.
        """

        # Check for OUTCAR and WAVECAR
        if not path.isfile(folder_name + "/OUTCAR") or not path.isfile(
            folder_name + "/WAVECAR"
        ):
            raise FileNotFoundError()

        # Call vasp2trace
        os.chdir(folder_name)
        process = subprocess.Popen(
            ["vasp2trace2"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        stdout = stdout.decode()

        if stderr:
            stderr = stderr.decode()
            warnings.warn(stderr)

        if process.returncode != 0:
            raise RuntimeError(
                "vasp2trace2 exited with return code {}.".format(process.returncode)
            )

        self._stdout = stdout
        self._stderr = stderr
        self.output = None

        # Process spin-polarized output
        if path.isfile("trace_up.txt") and path.isfile("trace_dn.txt"):
            self.output = {}
            if path.isfile("trace_up.txt"):
                self.output["up"] = Vasp2TraceOutput("trace_up.txt")
            if path.isfile("trace_dn.txt"):
                self.output["down"] = Vasp2TraceOutput("trace_dn.txt")

        else:
            raise FileNotFoundError()


[docs]class Vasp2TraceOutput(MSONable): def __init__( self, vasp2trace_output, num_occ_bands=None, soc=None, num_symm_ops=None, symm_ops=None, num_max_kvec=None, kvecs=None, num_kvec_symm_ops=None, symm_ops_in_little_cogroup=None, traces=None, ): """ This class processes results from vasp2trace to classify material band topology and give topological invariants. Refer to http://www.cryst.ehu.es/html/cryst/topological/File_Description.txt for further explanation of parameters. Args: vasp2trace_stdout (txt file): stdout from running vasp2trace. num_occ_bands (int): Number of occupied bands. soc (int): 0: no spin-orbit, 1: yes spin-orbit num_symm_ops (int): Number of symmetry operations. symm_ops (list): Each row is a symmetry operation (with spinor components if soc is enabled) num_max_kvec (int): Number of maximal k-vectors. kvecs (list): Each row is a k-vector. num_kvec_symm_ops (dict): {kvec_index: number of symm operations in the little cogroup of the kvec}. symm_ops_in_little_cogroup (dict): {kvec_index: int indices that correspond to symm_ops} traces (dict): band index, band degeneracy, energy eigenval, Re eigenval, Im eigenval for each symm op in the little cogroup """ self._vasp2trace_output = vasp2trace_output self.num_occ_bands = num_occ_bands self.soc = soc self.num_symm_ops = num_symm_ops self.symm_ops = symm_ops self.num_max_kvec = num_max_kvec self.kvecs = kvecs self.num_kvec_symm_ops = num_kvec_symm_ops self.symm_ops_in_little_cogroup = symm_ops_in_little_cogroup self.traces = traces self._parse_stdout(vasp2trace_output) def _parse_stdout(self, vasp2trace_output): try: with open(vasp2trace_output, "r") as file: lines = file.readlines() # Get header info num_occ_bands = int(lines[0]) soc = int(lines[1]) # No: 0, Yes: 1 num_symm_ops = int(lines[2]) symm_ops = np.ndarray.tolist(np.loadtxt(lines[3 : 3 + num_symm_ops])) num_max_kvec = int(lines[3 + num_symm_ops]) kvecs = np.ndarray.tolist( np.loadtxt( lines[4 + num_symm_ops : 4 + num_symm_ops + num_max_kvec] ) ) # Dicts with kvec index as keys num_kvec_symm_ops = {} symm_ops_in_little_cogroup = {} traces = {} # Start of trace info trace_start = 5 + num_max_kvec + num_symm_ops start_block = 0 # Start of this block # Block start line #s block_starts = [] for jdx, line in enumerate(lines[trace_start - 1 :], trace_start - 1): # Parse input lines line = [i for i in line.split(" ") if i] if len(line) == 1: # A single entry <-> new block block_starts.append(jdx) # Loop over blocks of kvec data for idx, kpt in enumerate(kvecs): start_block = block_starts[idx] if idx < num_max_kvec - 1: next_block = block_starts[idx + 1] trace_str = lines[start_block + 2 : next_block] else: trace_str = lines[start_block + 2 :] # Populate dicts num_kvec_symm_ops[str(idx)] = int(lines[start_block]) soilcg = [ int(i.strip("\n")) for i in lines[start_block + 1].split(" ") if i.strip("\n") ] symm_ops_in_little_cogroup[str(idx)] = soilcg trace = np.ndarray.tolist(np.loadtxt(trace_str)) traces[str(idx)] = trace self.num_occ_bands = num_occ_bands self.soc = soc self.num_symm_ops = num_symm_ops self.symm_ops = symm_ops self.num_max_kvec = num_max_kvec self.kvecs = kvecs self.num_kvec_symm_ops = num_kvec_symm_ops self.symm_ops_in_little_cogroup = symm_ops_in_little_cogroup self.traces = traces except Exception as error: print(error) warnings.warn( "Vasp2trace output not found. Setting instance attributes from direct inputs!" )