Source code for custodian.vasp.validators
# coding: utf-8
"""
Implements various validatiors, e.g., check if vasprun.xml is valid, for VASP.
"""
import logging
import os
from collections import deque
from pymatgen.io.vasp import Vasprun, Incar, Outcar, Chgcar
from custodian.custodian import Validator
[docs]class VasprunXMLValidator(Validator):
"""
Checks that a valid vasprun.xml was generated
"""
def __init__(self, output_file="vasp.out", stderr_file="std_err.txt"):
"""
Args:
output_file (str): Name of file VASP standard output is directed to.
Defaults to "vasp.out".
stderr_file (str): Name of file VASP standard error is direct to.
Defaults to "std_err.txt".
"""
self.output_file = output_file
self.stderr_file = stderr_file
self.logger = logging.getLogger(self.__class__.__name__)
[docs] def check(self):
"""
Check for error.
"""
try:
Vasprun("vasprun.xml")
except Exception:
exception_context = {}
if os.path.exists(self.output_file):
with open(self.output_file, "r") as output_file:
output_file_tail = deque(output_file, maxlen=10)
exception_context["output_file_tail"] = "".join(output_file_tail)
if os.path.exists(self.stderr_file):
with open(self.stderr_file, "r") as stderr_file:
stderr_file_tail = deque(stderr_file, maxlen=10)
exception_context["stderr_file_tail"] = "".join(stderr_file_tail)
if os.path.exists("vasprun.xml"):
stat = os.stat("vasprun.xml")
exception_context["vasprun_st_size"] = stat.st_size
exception_context["vasprun_st_atime"] = stat.st_atime
exception_context["vasprun_st_mtime"] = stat.st_mtime
exception_context["vasprun_st_ctime"] = stat.st_ctime
with open("vasprun.xml", "r") as vasprun:
vasprun_tail = deque(vasprun, maxlen=10)
exception_context["vasprun_tail"] = "".join(vasprun_tail)
self.logger.error(
"Failed to load vasprun.xml", exc_info=True, extra=exception_context
)
return True
return False
[docs]class VaspFilesValidator(Validator):
"""
Check for existence of some of the files that VASP
normally create upon running.
"""
def __init__(self):
"""
Dummy init
"""
pass
[docs] def check(self):
"""
Check for error.
"""
for vfile in ["CONTCAR", "OSZICAR", "OUTCAR"]:
if not os.path.exists(vfile):
return True
return False
[docs]class VaspNpTMDValidator(Validator):
"""
Check NpT-AIMD settings is loaded by VASP compiled with -Dtbdyn.
Currently, VASP only have Langevin thermostat (MDALGO = 3) for NpT ensemble.
"""
def __init__(self):
"""
Dummy init.
"""
pass
[docs] def check(self):
"""
Check for error.
"""
incar = Incar.from_file("INCAR")
is_npt = incar.get("MDALGO") == 3
if not is_npt:
return False
outcar = Outcar("OUTCAR")
patterns = {"MDALGO": r"MDALGO\s+=\s+([\d]+)"}
outcar.read_pattern(patterns=patterns)
if outcar.data["MDALGO"] == [["3"]]:
return False
return True
[docs]class VaspAECCARValidator(Validator):
"""
Check if the data in the AECCAR is corrupted
"""
def __init__(self):
"""
Dummy init
"""
pass
[docs] def check(self):
"""
Check for error.
"""
aeccar0 = Chgcar.from_file("AECCAR0")
aeccar2 = Chgcar.from_file("AECCAR2")
aeccar = aeccar0 + aeccar2
return check_broken_chgcar(aeccar)
[docs]def check_broken_chgcar(chgcar, diff_thresh=None):
"""
Check if the charge density file is corrupt
Args:
chgcar (Chgcar): Chgcar-like object.
diff_thresh (Float): Threshhold for diagonal difference.
None means we won't check for this.
"""
chgcar_data = chgcar.data["total"]
if (chgcar_data < 0).sum() > 100:
# a decent bunch of the values are negative this for sure means a broken charge density
return True
if diff_thresh:
"""
If any one diagonal difference accounts for more than a particular portion of
the total difference between highest and lowest density.
When we are looking at AECCAR data, since the charge density is so high near the core
and we have a course grid, this threshhold can be as high as 0.99
"""
diff = chgcar_data[:-1, :-1, :-1] - chgcar_data[1:, 1:, 1:]
if diff.max() / (chgcar_data.max() - chgcar_data.min()) > diff_thresh:
return True
return False