Skip to content

Instantly share code, notes, and snippets.

@mjhong0708
Last active January 26, 2022 01:26
Show Gist options
  • Save mjhong0708/675e2829c36ea43824933ccaa030973f to your computer and use it in GitHub Desktop.
Save mjhong0708/675e2829c36ea43824933ccaa030973f to your computer and use it in GitHub Desktop.
Simple INCAR parser class
from typing import Iterable, Union
Numeric = Union[int, float]
IncarValue = Union[bool, str, Numeric, Iterable[Union[str, Numeric]]]
class Incar:
def __init__(self, **kwargs):
self.__dict__ = kwargs
def __repr__(self) -> str:
attrs = ", ".join([f"{key}={val}" for key, val in self.__dict__.items()])
return f"Incar({attrs})"
def __str__(self) -> str:
return self.__repr__()
def __eq__(self, other) -> bool:
if not self.__dict__.keys() == other.__dict__.keys():
return False
for key in self.__dict__:
if self.__dict__[key] != other.__dict__[key]:
return False
return True
def to_string(self) -> str:
incar_lines = []
for tag, val in self.__dict__.items():
if val is not None:
incar_tag = tag
incar_val = self.validate_data(val)
incar_line = " = ".join([incar_tag, incar_val])
incar_lines.append(incar_line)
return "\n".join(incar_lines)
def write(self, filename: str) -> None:
with open(filename, "w") as f:
incar_str = self.to_string()
f.write(incar_str)
@classmethod
def read(cls, filename: str):
kwargs = dict()
with open(filename, "r") as f:
lines = f.readlines()
for line in lines:
# Remove whitespace and skip if the line is empty or comment
line_stripped = line.strip()
if not line_stripped or line_stripped.startswith("#"):
continue
# Get tag and value
line_splitted = line_stripped.split("#")[0].split("=")
tag = line_splitted[0].strip()
val = line_splitted[1].strip()
# If value is array, split into list
# while preserving its type
_val_split = val.split()
if len(_val_split) == 1:
val_final = str_to_num_if_possible(_val_split[0])
else:
val_final = [str_to_num_if_possible(val) for val in _val_split] # type: ignore
kwargs[tag] = val_final
return cls(**kwargs)
@staticmethod
def validate_data(data: IncarValue) -> str:
# If boolean
if isinstance(data, bool):
if data:
return ".TRUE."
else:
return ".FALSE."
# If iterable and not string
elif not isinstance(data, str) and hasattr(data, "__iter__"):
return " ".join(map(str, data)) # type: ignore
# Otherwise, return as str
else:
return str(data)
def str_to_num_if_possible(s: str) -> Union[str, Numeric]:
try:
val = int(s)
except ValueError:
try:
val = float(s) # type: ignore
except ValueError:
val = s # type: ignore
return val
@mjhong0708
Copy link
Author

mjhong0708 commented Jan 26, 2022

How to use

  • Create manually with python

    >>> incar = Incar(EDIFF=1e-5, EDIFFG=-0.02, NSW=300, NELM=120, MAGMOM=[2.0, -2.0, 2.0, -2.0])
    >>> print(incar)
    Incar(EDIFF=1e-05, EDIFFG=-0.02, NSW=300, NELM=120, MAGMOM=[2.0, -2.0, 2.0, -2.0])
    >>> print(incar.to_string())
    Incar(EDIFF=1e-05, EDIFFG=-0.02, NSW=300, NELM=120, MAGMOM=[2.0, -2.0, 2.0, -2.0])
    EDIFF = 1e-05
    EDIFFG = -0.02
    NSW = 300
    NELM = 120
    MAGMOM = 2.0 -2.0 2.0 -2.0
    >>> incar.write("INCAR")  # write incar to file
  • Read & manimulate tag

    >>> incar = Incar.read("INCAR")
    >>> incar.NELM = 150
    >>> incar.ISIF = 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment