Source code for custodian.cli.run_vasp

#!/usr/bin/env python

"""
This is a master vasp running script to perform various combinations of VASP
runs.
"""

import logging
import sys

import ruamel.yaml as yaml
from pymatgen.io.vasp.inputs import VaspInput, Incar, Kpoints

from custodian.custodian import Custodian
from custodian.vasp.jobs import VaspJob


[docs]def load_class(mod, name): """ Load the class from mod and name specification. """ toks = name.split("?") params = {} if len(toks) == 2: for p in toks[-1].split(","): ptoks = p.split("=") params[ptoks[0]] = yaml.safe_load(ptoks[1]) elif len(toks) > 2: print("Bad handler specification") sys.exit(-1) mod = __import__(mod, globals(), locals(), [toks[0]], 0) return getattr(mod, toks[0])(**params)
[docs]def get_jobs(args): """ Returns a generator of jobs. Allows of "infinite" jobs. """ vasp_command = args.command.split() # save initial INCAR for rampU runs n_ramp_u = args.jobs.count("rampU") ramps = 0 if n_ramp_u: incar = Incar.from_file("INCAR") ldauu = incar["LDAUU"] ldauj = incar["LDAUJ"] njobs = len(args.jobs) post_settings = [] # append to this list to have settings applied on next job for i, job in enumerate(args.jobs): final = i == njobs - 1 if any(c.isdigit() for c in job): suffix = "." + job else: suffix = ".{}{}".format(job, i + 1) settings = post_settings post_settings = [] backup = i == 0 copy_magmom = False vinput = VaspInput.from_directory(".") if i > 0: settings.append( {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}} ) job_type = job.lower() auto_npar = True if args.no_auto_npar: auto_npar = False if job_type.startswith("static_derived"): from pymatgen.io.vasp.sets import MPStaticSet vis = MPStaticSet.from_prev_calc( ".", user_incar_settings={"LWAVE": True, "EDIFF": 1e-6}, ediff_per_atom=False, ) settings.extend( [ {"dict": "INCAR", "action": {"_set": dict(vis.incar)}}, {"dict": "KPOINTS", "action": {"_set": vis.kpoints.as_dict()}}, ] ) if job_type.startswith("static_dielectric_derived"): from pymatgen.io.vasp.sets import ( MPStaticSet, MPStaticDielectricDFPTVaspInputSet, ) # vis = MPStaticSet.from_prev_calc( # ".", user_incar_settings={"EDIFF": 1e-6, "IBRION": 8, # "LEPSILON": True, 'LREAL':False, # "LPEAD": True, "ISMEAR": 0, # "SIGMA": 0.01}, # ediff_per_atom=False) vis = MPStaticDielectricDFPTVaspInputSet() incar = vis.get_incar(vinput["POSCAR"].structure) unset = {} for k in ["NPAR", "KPOINT_BSE", "LAECHG", "LCHARG", "LVHAR", "NSW"]: incar.pop(k, None) if k in vinput["INCAR"]: unset[k] = 1 kpoints = vis.get_kpoints(vinput["POSCAR"].structure) settings.extend( [ {"dict": "INCAR", "action": {"_set": dict(incar), "_unset": unset}}, {"dict": "KPOINTS", "action": {"_set": kpoints.as_dict()}}, ] ) auto_npar = False elif job_type.startswith("static"): m = [i * args.static_kpoint for i in vinput["KPOINTS"].kpts[0]] settings.extend( [ {"dict": "INCAR", "action": {"_set": {"NSW": 0}}}, {"dict": "KPOINTS", "action": {"_set": {"kpoints": [m]}}}, ] ) elif job_type.startswith("nonscf_derived"): from pymatgen.io.vasp.sets import MPNonSCFSet vis = MPNonSCFSet.from_prev_calc( ".", copy_chgcar=False, user_incar_settings={"LWAVE": True} ) settings.extend( [ {"dict": "INCAR", "action": {"_set": dict(vis.incar)}}, {"dict": "KPOINTS", "action": {"_set": vis.kpoints.as_dict()}}, ] ) elif job_type.startswith("optics_derived"): from pymatgen.io.vasp.sets import MPNonSCFSet vis = MPNonSCFSet.from_prev_calc( ".", optics=True, copy_chgcar=False, nedos=2001, mode="uniform", nbands_factor=5, user_incar_settings={ "LWAVE": True, "ALGO": "Exact", "SIGMA": 0.01, "EDIFF": 1e-6, }, ediff_per_atom=False, ) settings.extend( [ {"dict": "INCAR", "action": {"_set": dict(vis.incar)}}, {"dict": "KPOINTS", "action": {"_set": vis.kpoints.as_dict()}}, ] ) elif job_type.startswith("rampu"): f = ramps / (n_ramp_u - 1) settings.append( { "dict": "INCAR", "action": { "_set": { "LDAUJ": [j * f for j in ldauj], "LDAUU": [u * f for u in ldauu], } }, } ) copy_magmom = True ramps += 1 elif job_type.startswith("quick_relax") or job_type.startswith("quickrelax"): kpoints = vinput["KPOINTS"] incar = vinput["INCAR"] structure = vinput["POSCAR"].structure if "ISMEAR" in incar: post_settings.append( {"dict": "INCAR", "action": {"_set": {"ISMEAR": incar["ISMEAR"]}}} ) else: post_settings.append( {"dict": "INCAR", "action": {"_unset": {"ISMEAR": 1}}} ) post_settings.append( {"dict": "KPOINTS", "action": {"_set": kpoints.as_dict()}} ) # lattice vectors with length < 9 will get >1 KPOINT low_kpoints = Kpoints.gamma_automatic( [max(int(18 / l), 1) for l in structure.lattice.abc] ) settings.extend( [ {"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}, {"dict": "KPOINTS", "action": {"_set": low_kpoints.as_dict()}}, ] ) # let vasp determine encut (will be lower than # needed for compatibility with other runs) if "ENCUT" in incar: post_settings.append( {"dict": "INCAR", "action": {"_set": {"ENCUT": incar["ENCUT"]}}} ) settings.append({"dict": "INCAR", "action": {"_unset": {"ENCUT": 1}}}) elif job_type.startswith("relax"): pass elif job_type.startswith("full_relax"): for j in VaspJob.full_opt_run(vasp_command): yield j else: print("Unsupported job type: {}".format(job)) sys.exit(-1) if not job_type.startswith("full_relax"): yield VaspJob( vasp_command, final=final, suffix=suffix, backup=backup, settings_override=settings, copy_magmom=copy_magmom, auto_npar=auto_npar, )
[docs]def do_run(args): """ Do the run. """ FORMAT = "%(asctime)s %(message)s" logging.basicConfig(format=FORMAT, level=logging.INFO, filename="run.log") logging.info("Handlers used are %s" % args.handlers) handlers = [load_class("custodian.vasp.handlers", n) for n in args.handlers] validators = [load_class("custodian.vasp.validators", n) for n in args.validators] c = Custodian( handlers, get_jobs(args), validators, max_errors=args.max_errors, scratch_dir=args.scratch, gzipped_output=args.gzip, checkpoint=True, ) c.run()
[docs]def main(): """ Main method """ import argparse parser = argparse.ArgumentParser( description="run_vasp is a master script to perform various kinds of VASP runs.", 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( "--no_auto_npar", action="store_true", help="Set to true to turn off auto_npar. Useful for certain machines " "and calculations where you want absolute control.", ) parser.add_argument( "-z", "--gzip", dest="gzip", action="store_true", help="Add this option to gzip the final output. Do not gzip if you " "are going to perform an additional static run.", ) parser.add_argument( "-s", "--scratch", dest="scratch", nargs="?", default=None, type=str, help="Scratch directory to perform run in. Specify the root scratch " "directory as the code will automatically create a temporary " "subdirectory to run the job.", ) parser.add_argument( "-ks", "--kpoint-static", dest="static_kpoint", nargs="?", default=1, type=int, help="The multiplier to use for the KPOINTS of a static run (if " "any). For example, setting this to 2 means that if your " "original run was done using a k-point grid of 2x3x3, " "the static run will be done with a k-point grid of 4x6x6. This " "defaults to 1, i.e., static runs are performed with the same " "k-point grid as relaxation runs.", ) parser.add_argument( "-me", "--max-errors", dest="max_errors", nargs="?", default=10, type=int, help="Maximum number of errors to allow before quitting", ) parser.add_argument( "-hd", "--handlers", dest="handlers", nargs="+", default=[ "VaspErrorHandler", "MeshSymmetryErrorHandler", "UnconvergedErrorHandler", "NonConvergingErrorHandler", "PotimErrorHandler", ], type=str, help="The ErrorHandlers to use specified as string class names, " "with optional arguments specified as a url-like string. For " "example, VaspErrorHandler?output_filename=myfile.out specifies a " "VaspErrorHandler with output_name set to myfile.out. Multiple " "arguments are joined by a comma. E.g., MyHandler?myfile=a," "data=1. The arguments are deserialized using yaml.", ) parser.add_argument( "-vd", "--validators", dest="validators", nargs="+", default=["VasprunXMLValidator"], type=str, help="The Validators to use specified as string class names, " "with optional arguments specified as a url-like string. For " "example, VaspErrorHandler?output_filename=myfile.out specifies a " "VaspErrorHandler with output_name set to myfile.out. Multiple " "arguments are joined by a comma. E.g., MyHandler?myfile=a," "data=1. The arguments are deserialized using yaml.", ) parser.add_argument( "jobs", metavar="jobs", type=str, nargs="+", default=["relax", "relax"], help="Jobs to execute. Only sequences of relax, " "quickrelax, static, rampU, full_relax, static_derived, " "nonscf_derived, optics_derived are " 'supported at the moment. For example, "relax relax static" ' "will run a double relaxation followed by a static " "run. By default, suffixes are given sequential numbering," "but this can be overrridden by adding a number to the job" "type, e.g. relax5 relax6 relax7", ) args = parser.parse_args() do_run(args)