Source code for sisl.io.vasp.outcar

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from __future__ import annotations

import numpy as np

from sisl._internal import set_module
from sisl.messages import deprecate_argument, deprecation
from sisl.typing import UnitsVar
from sisl.unit import serialize_units_arg, unit_convert
from sisl.utils import PropertyDict

from .._multiple import SileBinder
from ..sile import add_sile, sile_fh_open
from .sile import SileVASP

__all__ = ["outcarSileVASP"]


_A = SileVASP.InfoAttr


@set_module("sisl.io.vasp")
class outcarSileVASP(SileVASP):
    """OUTCAR file from VASP"""

    _info_attributes_ = [
        _A(
            "completed",
            r".*General timing and accounting",
            lambda attr, match: lambda: True,
            default=lambda: False,
            not_found="warn",
        ),
        _A(
            "accuracy_reached",
            r".*reached required accuracy",
            lambda attr, match: lambda: True,
            default=lambda: False,
            not_found="warn",
        ),
    ]

[docs] @deprecation( "outcarSileVASP.completed is deprecated in favor of outcarSileVASP.info.completed", "0.15", "0.16", ) def completed(self): """True if the line "General timing and accounting" was found.""" return self.info.completed()
[docs] @deprecation( "outcarSileVASP.accuracy_reached is deprecated in favor of outcarSileVASP.info.accuracy_reached", "0.15", "0.16", ) def accuracy_reached(self): """True if the line "reached required accuracy" was found.""" return self.info.accuracy_reached()
[docs] @sile_fh_open() def cpu_time(self, flag="General timing and accounting"): """Returns the consumed cpu time (in seconds) from a given section""" if flag == "General timing and accounting": nskip, iplace = 3, 5 else: raise ValueError( f"{self.__class__.__name__}.cpu_time unknown flag '{flag}'" ) found = self.step_to(flag, allow_reread=False)[0] if found: for _ in range(nskip): line = self.readline() return float(line.split()[iplace]) raise KeyError( f"{self.__class__.__name__}.cpu_time could not find flag '{flag}' in file" )
[docs] @SileBinder() @sile_fh_open() @deprecate_argument( "all", None, "use read_energy[:]() instead to get all entries", "0.14", "0.16", ) @deprecation( "WARNING: direct calls to outcarSileVASP.read_energy() no longer returns the last entry! Now the next block on file is returned.", "0.14", "0.16", ) def read_energy(self, units: UnitsVar = "eV") -> PropertyDict: """Reads an energy specification block from OUTCAR The function steps to the next occurrence of the "Free energy of the ion-electron system" segment Parameters ---------- units : selects units in the returned data Notes ----- The name convention in the dictionary is as follows: OUTCAR string Key alpha Z PSCENC = Z Ewald energy TEWEN = Ewald -Hartree energ DENC = hartree -exchange EXHF = xcHF -V(xc)+E(xc) XCENC = xc PAW double counting = paw1 paw2 entropy T*S EENTRO = entropy eigenvalues EBANDS = band atomic energy EATOM = ion Solvation Ediel_sol = solvation free energy TOTEN = free energy without entropy= total energy(sigma->0) = sigma0 Returns ------- out : PropertyDict all energies from a single "Free energy of the ion-electron system" segment """ units = serialize_units_arg(units) eV2unit = unit_convert("eV", units["energy"]) name_conv = { "alpha": "Z", "Ewald": "Ewald", "-Hartree": "hartree", "-exchange": "xcHF", "-V(xc)+E(xc)": "xc", "PAW": "paw", "entropy": "entropy", "eigenvalues": "band", "atomic": "ion", "Solvation": "solvation", } # read the energy tables f = self.step_to("Free energy of the ion-electron system", allow_reread=False)[ 0 ] if not f: return None self.readline() # ----- line = self.readline() E = PropertyDict() while "----" not in line: key, *v = line.split() if key == "PAW": E[f"{name_conv[key]}1"] = float(v[-2]) * eV2unit E[f"{name_conv[key]}2"] = float(v[-1]) * eV2unit else: E[name_conv[key]] = float(v[-1]) * eV2unit line = self.readline() line = self.readline() E.free = float(line.split()[-2]) * eV2unit self.readline() line = self.readline() v = line.split() E.total = float(v[4]) * eV2unit E.sigma0 = float(v[-1]) * eV2unit return E
[docs] @SileBinder() @sile_fh_open() def read_trajectory(self): """Reads cell+position+force data from OUTCAR for an ionic trajectory step The function steps to the block defined by the "VOLUME and BASIS-vectors are now :" line to first read the cell vectors, then it steps to the "TOTAL-FORCE (eV/Angst)" segment to read the atom positions and forces. Returns ------- PropertyDict Trajectory step defined by cell vectors (`.cell`), atom positions (`.xyz`), and forces (`.force`) """ f = self.step_to("VOLUME and BASIS-vectors are now :", allow_reread=False)[0] if not f: return None for i in range(4): self.readline() # skip 4 lines C = [] for i in range(3): line = self.readline() v = line.split() C.append(v[:3]) # direct lattice vectors # read a position-force table f = self.step_to("TOTAL-FORCE (eV/Angst)", allow_reread=False)[0] if not f: return None self.readline() # ----- P, F = [], [] line = self.readline() while "----" not in line: v = line.split() # positions and forces P.append(v[:3]) F.append(v[3:6]) line = self.readline() step = PropertyDict() step.cell = np.array(C, dtype=np.float64) step.xyz = np.array(P, dtype=np.float64) step.force = np.array(F, dtype=np.float64) return step
outSileVASP = deprecation( "outSileVASP has been deprecated in favor of outcarSileVASP.", "0.15", "0.16" )(outcarSileVASP) add_sile("OUTCAR", outcarSileVASP, gzip=True)