#!/usr/bin/env python
"""
This is a master vasp running script to converging kpoints for a calculation
"""
import logging
from pymatgen.io.vasp.inputs import VaspInput
from pymatgen.io.vasp.outputs import Vasprun
from custodian.custodian import Custodian
from custodian.vasp.handlers import VaspErrorHandler, UnconvergedErrorHandler
from custodian.vasp.jobs import VaspJob
FORMAT = "%(asctime)s %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO, filename="run.log")
[docs]def get_runs(vasp_command, target=1e-3, max_steps=10, mode="linear"):
"""
Generate the runs using a generator until convergence is achieved.
"""
energy = 0
vinput = VaspInput.from_directory(".")
kpoints = vinput["KPOINTS"].kpts[0]
for i in range(max_steps):
if mode == "linear":
m = [k * (i + 1) for k in kpoints]
else:
m = [k + 1 for k in kpoints]
if i == 0:
settings = None
backup = True
else:
backup = False
v = Vasprun("vasprun.xml")
e_per_atom = v.final_energy / len(v.final_structure)
ediff = abs(e_per_atom - energy)
if ediff < target:
logging.info("Converged to {} eV/atom!".format(ediff))
break
energy = e_per_atom
settings = [
{"dict": "INCAR", "action": {"_set": {"ISTART": 1}}},
{"dict": "KPOINTS", "action": {"_set": {"kpoints": [m]}}},
{
"filename": "CONTCAR",
"action": {"_file_copy": {"dest": "POSCAR"}},
},
]
yield VaspJob(
vasp_command,
final=False,
backup=backup,
suffix=".kpoints.{}".format("x".join(map(str, m))),
settings_override=settings,
)
[docs]def do_run(args):
"""
Perform the run.
"""
handlers = [VaspErrorHandler(), UnconvergedErrorHandler()]
c = Custodian(
handlers,
get_runs(
vasp_command=args.command.split(),
target=args.target,
mode=args.mode,
max_steps=args.max_steps,
),
max_errors=10,
)
c.run()
[docs]def main():
"""
Main method
"""
import argparse
parser = argparse.ArgumentParser(
description="""
converge_kpoints perform a KPOINTS convergence. What this script will do
is to run a particular VASP run with increasing multiples of the initial
KPOINT grid until a target convergence in energy per atom is reached.
For example, let's say you have vasp input files that has a k-point grid
of 1x1x1. This script will perform sequence jobs with k-point grids of
1x1x1, 2x2x2, 3x3x3, 4x4x4, ... until convergence is achieved. The
default convergence criteria is 1meV/atom, but this can be set using the
--target option.
""",
epilog="""Author: Shyue Ping Ong"""
)
parser.add_argument(
"-c",
"--command",
dest="command",
nargs="?",
default="pvasp",
type=str,
help="VASP command. Defaults to pvasp. If you are using mpirun, "
'set this to something like "mpirun pvasp".',
)
parser.add_argument(
"-i",
"--increment_mode",
dest="mode",
nargs="?",
default="linear",
type=str,
choices=["linear", "inc"],
help="Mode for increasing kpoints. In linear mode, multiples of "
"the existing kpoints are done. E.g., 2x4x2 -> 4x8x4 -> 6x12x6. "
"In inc mode, all KPOINTS are incremented by 1 at each stage, "
"i.e., 2x4x2 -> 3x5x3 ->4x6x4. Note that the latter mode does "
"not preserve KPOINTS symmetry, though it is probably less "
"expensive.",
)
parser.add_argument(
"-m",
"--max_steps",
dest="max_steps",
nargs="?",
default=10,
type=int,
help="The maximum number of KPOINTS increment steps. This puts an "
"upper bound on the largest KPOINT converge grid attempted.",
)
parser.add_argument(
"-t",
"--target",
dest="target",
nargs="?",
default=0.001,
type=float,
help="The target converge in energy per atom to achieve "
"convergence. E.g., 1e-3 means the KPOINTS will be increased "
"until a converged of 1meV is reached.",
)
args = parser.parse_args()
do_run(args)
if __name__ == "__main__":
main()