diff --git a/docs/_modules/custodian/ansible/actions.html b/docs/_modules/custodian/ansible/actions.html index 5c9bd3ef..93bda826 100644 --- a/docs/_modules/custodian/ansible/actions.html +++ b/docs/_modules/custodian/ansible/actions.html @@ -4,15 +4,14 @@ - custodian.ansible.actions — custodian 2019.8.24 documentation + custodian.ansible.actions — custodian 2020.4.27 documentation - - - - - - + + + + + @@ -37,7 +36,7 @@

Navigation

  • modules |
  • - + @@ -50,7 +49,7 @@

    Navigation

    Source code for custodian.ansible.actions

     # coding: utf-8
     
    -from __future__ import unicode_literals, division, print_function
    +from __future__ import unicode_literals, division, print_function
     import os
     import shutil
     
    @@ -154,8 +153,7 @@ 

    Source code for custodian.ansible.actions

             for k, v in settings.items():
                 (d, key) = get_nested_dict(input_dict, k)
                 if key in d and (not isinstance(d[key], list)):
    -                raise ValueError("Keyword {} does not refer to an array."
    -                                 .format(k))
    +                raise ValueError("Keyword {} does not refer to an array.".format(k))
                 if key in d and v not in d[key]:
                     d[key].append(v)
                 elif key not in d:
    @@ -166,8 +164,7 @@ 

    Source code for custodian.ansible.actions

             for k, v in settings.items():
                 (d, key) = get_nested_dict(input_dict, k)
                 if key in d and (not isinstance(d[key], list)):
    -                raise ValueError("Keyword {} does not refer to an array."
    -                                 .format(k))
    +                raise ValueError("Keyword {} does not refer to an array.".format(k))
                 if key in d:
                     d[key] = [i for i in d[key] if i != v]
    @@ -175,8 +172,7 @@

    Source code for custodian.ansible.actions

         def pull_all(input_dict, settings):
             for k, v in settings.items():
                 if k in input_dict and (not isinstance(input_dict[k], list)):
    -                raise ValueError("Keyword {} does not refer to an array."
    -                                 .format(k))
    +                raise ValueError("Keyword {} does not refer to an array.".format(k))
                 for i in v:
                     DictActions.pull(input_dict, {k: i})
    @@ -185,8 +181,7 @@

    Source code for custodian.ansible.actions

             for k, v in settings.items():
                 (d, key) = get_nested_dict(input_dict, k)
                 if key in d and (not isinstance(d[key], list)):
    -                raise ValueError("Keyword {} does not refer to an array."
    -                                 .format(k))
    +                raise ValueError("Keyword {} does not refer to an array.".format(k))
                 if v == 1:
                     d[key].pop()
                 elif v == -1:
    @@ -210,11 +205,12 @@ 

    Source code for custodian.ansible.actions

                 settings (dict): Must be {"content": actual_content}
             """
             if len(settings) != 1:
    -            raise ValueError("Settings must only contain one item with key "
    -                             "'content'.")
    +            raise ValueError(
    +                "Settings must only contain one item with key " "'content'."
    +            )
             for k, v in settings.items():
                 if k == "content":
    -                with open(filename, 'w') as f:
    +                with open(filename, "w") as f:
                         f.write(v)
    [docs] @staticmethod @@ -227,8 +223,7 @@

    Source code for custodian.ansible.actions

                 settings (dict): Must be {"dest": path of new file}
             """
             if len(settings) != 1:
    -            raise ValueError("Settings must only contain one item with key "
    -                             "'dest'.")
    +            raise ValueError("Settings must only contain one item with key " "'dest'.")
             for k, v in settings.items():
                 if k == "dest":
                     shutil.move(filename, v)
    @@ -244,8 +239,7 @@

    Source code for custodian.ansible.actions

                     mode only prints the action without performing it.
             """
             if len(settings) != 1:
    -            raise ValueError("Settings must only contain one item with key "
    -                             "'mode'.")
    +            raise ValueError("Settings must only contain one item with key " "'mode'.")
             for k, v in settings.items():
                 if k == "mode" and v == "actual":
                     try:
    @@ -305,14 +299,14 @@ 

    Quick search

    - +
    @@ -50,7 +49,7 @@

    Navigation

    Source code for custodian.ansible.interpreter

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
     """
     This module implements a Modder class that performs modifications on objects
    @@ -66,7 +65,7 @@ 

    Source code for custodian.ansible.interpreter

    import re -from custodian.ansible.actions import DictActions +from custodian.ansible.actions import DictActions
    [docs]class Modder(object): @@ -91,7 +90,7 @@

    Source code for custodian.ansible.interpreter

    'Universe' """ - def __init__(self, actions=None, strict=True): + def __init__(self, actions=None, strict=True): """ Initializes a Modder from a list of supported actions. @@ -108,8 +107,7 @@

    Source code for custodian.ansible.interpreter

    actions = actions if actions is not None else [DictActions] for action in actions: for i in dir(action): - if (not re.match('__\w+__', i)) and \ - callable(getattr(action, i)): + if (not re.match("__\w+__", i)) and callable(getattr(action, i)): self.supported_actions["_" + i] = getattr(action, i) self.strict = strict @@ -130,8 +128,7 @@

    Source code for custodian.ansible.interpreter

    if action in self.supported_actions: self.supported_actions[action].__call__(obj, settings) elif self.strict: - raise ValueError("{} is not a supported action!" - .format(action))
    + raise ValueError("{} is not a supported action!".format(action))
    [docs] def modify_object(self, modification, obj): """ @@ -173,14 +170,14 @@

    Quick search

    - +
    @@ -54,7 +53,7 @@

    Source code for custodian.cli.converge_geometry

    < This is a script to converge the geometry of a system """ -from __future__ import division +from __future__ import division __author__ = "Stephen Dacek" __version__ = "0.1" @@ -65,13 +64,18 @@

    Source code for custodian.cli.converge_geometry

    < import logging -from custodian.custodian import Custodian -from custodian.vasp.handlers import VaspErrorHandler, UnconvergedErrorHandler, \ - MeshSymmetryErrorHandler, NonConvergingErrorHandler, PotimErrorHandler -from custodian.vasp.jobs import VaspJob -from pymatgen.io.vasp.outputs import Vasprun - -FORMAT = '%(asctime)s %(message)s' +from custodian.custodian import Custodian +from custodian.vasp.handlers import ( + VaspErrorHandler, + UnconvergedErrorHandler, + MeshSymmetryErrorHandler, + NonConvergingErrorHandler, + PotimErrorHandler, +) +from custodian.vasp.jobs import VaspJob +from pymatgen.io.vasp.outputs import Vasprun + +FORMAT = "%(asctime)s %(message)s" logging.basicConfig(format=FORMAT, level=logging.INFO, filename="run.log") @@ -82,16 +86,13 @@

    Source code for custodian.cli.converge_geometry

    < while (not converged) and (job_number < args.max_relax): - suffix = ".{}{}".format('relax', job_number + 1) + suffix = ".{}{}".format("relax", job_number + 1) if job_number == 0: backup = True # assume the initial guess is poor, # start with conjugate gradients - settings = [ - {"dict": "INCAR", - "action": {"_set": {"IBRION": 2}}} - ] + settings = [{"dict": "INCAR", "action": {"_set": {"IBRION": 2}}}] else: backup = False @@ -103,29 +104,36 @@

    Source code for custodian.cli.converge_geometry

    < if job_number < 2 and not converged: settings = [ - {"dict": "INCAR", - "action": {"_set": {"ISTART": 1}}}, - {"file": "CONTCAR", - "action": {"_file_copy": {"dest": "POSCAR"}}}] + {"dict": "INCAR", "action": {"_set": {"ISTART": 1}}}, + {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}, + ] # switch to RMM-DIIS once we are near the # local minimum (assumed after 2 runs of CG) else: settings = [ - {"dict": "INCAR", - "action": {"_set": {"ISTART": 1, "IBRION": 1}}}, - {"file": "CONTCAR", - "action": {"_file_copy": {"dest": "POSCAR"}}}] + {"dict": "INCAR", "action": {"_set": {"ISTART": 1, "IBRION": 1}}}, + {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}, + ] job_number += 1 - yield VaspJob(vasp_command, final=converged, backup=backup, - suffix=suffix, settings_override=settings)
    + yield VaspJob( + vasp_command, + final=converged, + backup=backup, + suffix=suffix, + settings_override=settings, + )
    [docs]def do_run(args): - handlers = [VaspErrorHandler(), MeshSymmetryErrorHandler(), - UnconvergedErrorHandler(), NonConvergingErrorHandler(), - PotimErrorHandler()] + handlers = [ + VaspErrorHandler(), + MeshSymmetryErrorHandler(), + UnconvergedErrorHandler(), + NonConvergingErrorHandler(), + PotimErrorHandler(), + ] c = Custodian(handlers, get_runs(args), max_errors=10, gzipped_output=args.gzip) c.run() logging.info("Geometry optimization complete")
    @@ -134,33 +142,50 @@

    Source code for custodian.cli.converge_geometry

    < if __name__ == "__main__": import argparse - parser = argparse.ArgumentParser(description=""" + parser = argparse.ArgumentParser( + description=""" converge_geometry performs a geometry optimization. What this script will do is run a particular VASP relaxation repeatedly until the geometry is converged within the first ionic step. This is a common practice for converging molecular geometries in VASP, especially in situations where the geometry needs to be precise: such as frequency calculations. """, - epilog=""" + epilog=""" Author: Stephen Dacek Version: {} - Last updated: {}""".format(__version__, __date__)) + Last updated: {}""".format( + __version__, __date__ + ), + ) parser.add_argument( - "-c", "--command", dest="command", nargs="?", - default="pvasp", type=str, + "-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\".", ) + 'set this to something like "mpirun pvasp".', + ) parser.add_argument( - "-z", "--gzip", dest="gzip", action="store_true", + "-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.") + "are going to perform an additional static run.", + ) parser.add_argument( - "-mr", "--max_relaxtions", dest="max_relax", - default=10, type=int, - help="Maximum number of relaxations to allow") + "-mr", + "--max_relaxtions", + dest="max_relax", + default=10, + type=int, + help="Maximum number of relaxations to allow", + ) args = parser.parse_args() do_run(args) @@ -186,14 +211,14 @@

    Quick search

    - +
    @@ -55,7 +54,7 @@

    Source code for custodian.cli.converge_kpoints

    runs. """ -from __future__ import division +from __future__ import division __author__ = "shyuepingong" __version__ = "0.1" @@ -66,13 +65,13 @@

    Source code for custodian.cli.converge_kpoints

    import logging -from custodian.custodian import Custodian -from custodian.vasp.handlers import VaspErrorHandler, \ - UnconvergedErrorHandler -from custodian.vasp.jobs import VaspJob -from pymatgen.io.vasp import VaspInput, Vasprun +from custodian.custodian import Custodian +from custodian.vasp.handlers import VaspErrorHandler, UnconvergedErrorHandler +from custodian.vasp.jobs import VaspJob +from pymatgen.io.vasp.inputs import VaspInput +from pymatgen.io.vasp.outputs import Vasprun -FORMAT = '%(asctime)s %(message)s' +FORMAT = "%(asctime)s %(message)s" logging.basicConfig(format=FORMAT, level=logging.INFO, filename="run.log") @@ -99,29 +98,42 @@

    Source code for custodian.cli.converge_kpoints

    else: 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)

    + {"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): 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 = 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(): import argparse - parser = argparse.ArgumentParser(description=""" + + 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. @@ -131,39 +143,62 @@

    Source code for custodian.cli.converge_kpoints

    default convergence criteria is 1meV/atom, but this can be set using the --target option. """, - epilog=""" + epilog=""" Author: Shyue Ping Ong Version: {} - Last updated: {}""".format(__version__, __date__)) + Last updated: {}""".format( + __version__, __date__ + ), + ) parser.add_argument( - "-c", "--command", dest="command", nargs="?", - default="pvasp", type=str, + "-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\".") + 'set this to something like "mpirun pvasp".', + ) parser.add_argument( - "-i", "--increment_mode", dest="mode", nargs="?", - default="linear", type=str, choices=["linear", "inc"], + "-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.") + "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, + "-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.") + "upper bound on the largest KPOINT converge grid attempted.", + ) parser.add_argument( - "-t", "--target", dest="target", nargs="?", - default=0.001, type=float, + "-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." + "convergence. E.g., 1e-3 means the KPOINTS will be increased " + "until a converged of 1meV is reached.", ) args = parser.parse_args() @@ -194,14 +229,14 @@

    Quick search

    - +
    @@ -52,14 +51,14 @@

    Source code for custodian.cli.cstdn

     # Copyright (c) Materials Virtual Lab.
     # Distributed under the terms of the BSD License.
     
    -from __future__ import division, unicode_literals, print_function
    +from __future__ import division, unicode_literals, print_function
     
     import argparse
     
     import sys
    -from monty.serialization import loadfn
    +from monty.serialization import loadfn
     
    -from custodian.custodian import Custodian
    +from custodian.custodian import Custodian
     import logging
     
     example_yaml = """
    @@ -125,7 +124,7 @@ 

    Source code for custodian.cli.cstdn

     
     
     
    [docs]def run(args): - FORMAT = '%(asctime)s %(message)s' + FORMAT = "%(asctime)s %(message)s" logging.basicConfig(format=FORMAT, level=logging.INFO, filename="run.log") logging.info("Spec file is %s" % args.spec_file) d = loadfn(args.spec_file[0]) @@ -138,19 +137,25 @@

    Source code for custodian.cli.cstdn

     
     
     
    [docs]def main(): - parser = argparse.ArgumentParser(description=""" + parser = argparse.ArgumentParser( + description=""" cstdn is a convenient script to run custodian style jobs using a - simple YAML spec.""", epilog="""Author: Shyue Ping Ong""") + simple YAML spec.""", + epilog="""Author: Shyue Ping Ong""", + ) subparsers = parser.add_subparsers() prun = subparsers.add_parser("run", help="Run custodian.") - prun.add_argument("spec_file", metavar="spec_file", type=str, nargs=1, - help="YAML/JSON spec file.") + prun.add_argument( + "spec_file", metavar="spec_file", type=str, nargs=1, help="YAML/JSON spec file." + ) prun.set_defaults(func=run) prun = subparsers.add_parser( - "example", help="Print examples. Right now, there is only one example for VASP double relaxation.") + "example", + help="Print examples. Right now, there is only one example for VASP double relaxation.", + ) prun.set_defaults(func=print_example) args = parser.parse_args() @@ -187,14 +192,14 @@

    Quick search

    - +
    @@ -54,7 +53,7 @@

    Source code for custodian.cli.run_nwchem

     Basic script to run nwchem job.
     """
     
    -from __future__ import division
    +from __future__ import division
     
     __author__ = "shyuepingong"
     __version__ = "0.1"
    @@ -65,59 +64,96 @@ 

    Source code for custodian.cli.run_nwchem

     
     import logging
     
    -from custodian.custodian import Custodian
    -from custodian.nwchem.handlers import NwchemErrorHandler
    -from custodian.nwchem.jobs import NwchemJob
    +from custodian.custodian import Custodian
    +from custodian.nwchem.handlers import NwchemErrorHandler
    +from custodian.nwchem.jobs import NwchemJob
     
     
     
    [docs]def do_run(args): - logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO, - filename="run.log") - job = NwchemJob(nwchem_cmd=args.command.split(), - input_file=args.infile, - output_file=args.outfile) - c = Custodian([NwchemErrorHandler(output_filename=args.outfile)], [job], - max_errors=5, scratch_dir=args.scratch, - gzipped_output=args.gzipped, checkpoint=True) + logging.basicConfig( + format="%(asctime)s %(message)s", level=logging.INFO, filename="run.log" + ) + job = NwchemJob( + nwchem_cmd=args.command.split(), + input_file=args.infile, + output_file=args.outfile, + ) + c = Custodian( + [NwchemErrorHandler(output_filename=args.outfile)], + [job], + max_errors=5, + scratch_dir=args.scratch, + gzipped_output=args.gzipped, + checkpoint=True, + ) c.run()
    [docs]def main(): import argparse - parser = argparse.ArgumentParser(description=""" + + parser = argparse.ArgumentParser( + description=""" run_nwchem is a master script to perform various kinds of Nwchem runs. """, - epilog=""" + epilog=""" Author: Shyue Ping Ong Version: {} - Last updated: {}""".format(__version__, __date__)) + Last updated: {}""".format( + __version__, __date__ + ), + ) parser.add_argument( - "-c", "--command", dest="command", nargs="?", - default="nwchem", type=str, + "-c", + "--command", + dest="command", + nargs="?", + default="nwchem", + type=str, help="Nwchem command. Defaults to nwchem. If you are using mpirun, " - "set this to something like \"mpirun nwchem\".") + 'set this to something like "mpirun nwchem".', + ) parser.add_argument( - "-s", "--scratch", dest="scratch", nargs="?", - default=None, type=str, + "-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.") + "directory as the code will automatically create a temporary " + "subdirectory to run the job.", + ) parser.add_argument( - "-i", "--infile", dest="infile", nargs="?", default="mol.nw", - type=str, help="Input filename.") + "-i", + "--infile", + dest="infile", + nargs="?", + default="mol.nw", + type=str, + help="Input filename.", + ) parser.add_argument( - "-o", "--output", dest="outfile", nargs="?", default="mol.nwout", - type=str, help="Output filename." + "-o", + "--output", + dest="outfile", + nargs="?", + default="mol.nwout", + type=str, + help="Output filename.", ) parser.add_argument( - "-z", "--gzip", dest="gzip", action="store_true", + "-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." + "are going to perform an additional static run.", ) args = parser.parse_args() @@ -148,14 +184,14 @@

    Quick search

    - +
    @@ -55,15 +54,15 @@

    Source code for custodian.cli.run_vasp

     runs.
     """
     
    -from __future__ import division
    +from __future__ import division
     
     import logging
     import sys
     import ruamel.yaml as yaml
     
    -from custodian.custodian import Custodian
    -from custodian.vasp.jobs import VaspJob
    -from pymatgen.io.vasp import VaspInput, Incar, Kpoints
    +from custodian.custodian import Custodian
    +from custodian.vasp.jobs import VaspJob
    +from pymatgen.io.vasp.inputs import VaspInput, Incar, Kpoints
     
     __author__ = "Shyue Ping Ong"
     __version__ = "0.5"
    @@ -91,12 +90,12 @@ 

    Source code for custodian.cli.run_vasp

         # 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')
    +    n_ramp_u = args.jobs.count("rampU")
         ramps = 0
         if n_ramp_u:
    -        incar = Incar.from_file('INCAR')
    -        ldauu = incar['LDAUU']
    -        ldauj = incar['LDAUJ']
    +        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
    @@ -113,8 +112,8 @@ 

    Source code for custodian.cli.run_vasp

             vinput = VaspInput.from_directory(".")
             if i > 0:
                 settings.append(
    -                {"file": "CONTCAR",
    -                 "action": {"_file_copy": {"dest": "POSCAR"}}})
    +                {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}
    +            )
     
             job_type = job.lower()
             auto_npar = True
    @@ -123,18 +122,25 @@ 

    Source code for custodian.cli.run_vasp

                 auto_npar = False
     
             if job_type.startswith("static_derived"):
    -            from pymatgen.io.vasp.sets import MPStaticSet
    +            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()}}])
    +                ".",
    +                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
    +            from pymatgen.io.vasp.sets import (
    +                MPStaticSet,
    +                MPStaticDielectricDFPTVaspInputSet,
    +            )
     
                 # vis = MPStaticSet.from_prev_calc(
                 #     ".", user_incar_settings={"EDIFF": 1e-6, "IBRION": 8,
    @@ -145,56 +151,78 @@ 

    Source code for custodian.cli.run_vasp

                 vis = MPStaticDielectricDFPTVaspInputSet()
                 incar = vis.get_incar(vinput["POSCAR"].structure)
                 unset = {}
    -            for k in ["NPAR", "KPOINT_BSE", "LAECHG", "LCHARG", "LVHAR",
    -                      "NSW"]:
    +            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()}}])
    +            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]}}}])
    +            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()}}])
    +            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
    +            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()}}])
    +                ".",
    +                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]}}})
    +                {
    +                    "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"):
    @@ -203,150 +231,204 @@ 

    Source code for custodian.cli.run_vasp

                 structure = vinput["POSCAR"].structure
                 if "ISMEAR" in incar:
                     post_settings.append(
    -                    {"dict": "INCAR",
    -                     "action": {"_set": {"ISMEAR": incar["ISMEAR"]}}})
    +                    {"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()}})
    +                    {"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()}}])
    +                [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}}})
    +                    {"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):
    +            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)
    + 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): - FORMAT = '%(asctime)s %(message)s' + 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) + 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(): import argparse - parser = argparse.ArgumentParser(description=""" + + parser = argparse.ArgumentParser( + description=""" run_vasp is a master script to perform various kinds of VASP runs. """, - epilog=""" + epilog=""" Author: Shyue Ping Ong Version: {} - Last updated: {}""".format(__version__, __date__)) + Last updated: {}""".format( + __version__, __date__ + ), + ) parser.add_argument( - "-c", "--command", dest="command", nargs="?", - default="pvasp", type=str, + "-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\".") + 'set this to something like "mpirun pvasp".', + ) parser.add_argument( - "--no_auto_npar", action="store_true", + "--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.") + "and calculations where you want absolute control.", + ) parser.add_argument( - "-z", "--gzip", dest="gzip", action="store_true", + "-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." + "are going to perform an additional static run.", ) parser.add_argument( - "-s", "--scratch", dest="scratch", nargs="?", - default=None, type=str, + "-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.") + "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, + "-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." + "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") + "-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, + "-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." + "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, + "-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." + "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='+', + "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") + "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)
    @@ -372,14 +454,14 @@

    Quick search

    - +
    @@ -50,27 +49,27 @@

    Navigation

    Source code for custodian.custodian

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
     import logging
     import subprocess
     import sys
     import datetime
     import time
    -from glob import glob
    +from glob import glob
     import tarfile
     import os
    -from abc import ABCMeta, abstractmethod
    -from itertools import islice
    +from abc import ABCMeta, abstractmethod
    +from itertools import islice
     import warnings
    -from ast import literal_eval
    +from ast import literal_eval
     
    -from .utils import get_execution_host_info
    +from .utils import get_execution_host_info
     
    -from monty.tempfile import ScratchDir
    -from monty.shutil import gzip_dir
    -from monty.json import MSONable, MontyEncoder, MontyDecoder
    -from monty.serialization import loadfn, dumpfn
    +from monty.tempfile import ScratchDir
    +from monty.shutil import gzip_dir
    +from monty.json import MSONable, MontyEncoder, MontyDecoder
    +from monty.serialization import loadfn, dumpfn
     
     """
     This module implements the main Custodian class, which manages a list of jobs
    @@ -87,29 +86,10 @@ 

    Source code for custodian.custodian

     
     logger = logging.getLogger(__name__)
     
    -if "SENTRY_DSN" in os.environ:
    -    # Sentry.io is a service to aggregate logs remotely, this is useful
    -    # for Custodian to get statistics on which errors are most common.
    -    # If you do not have a SENTRY_DSN environment variable set, Sentry
    -    # will not be used.
    -
    -    import sentry_sdk
    -
    -    sentry_sdk.init(dsn=os.environ["SENTRY_DSN"])
    -
    -    with sentry_sdk.configure_scope() as scope:
    -
    -        from getpass import getuser
    -
    -        try:
    -            scope.user = {"username": getuser()}
    -        except Exception:
    -            pass
    -
     # Sentry.io is a service to aggregate logs remotely, this is useful
     # for Custodian to get statistics on which errors are most common.
     # If you do not have a SENTRY_DSN environment variable set, or do
    -# not have CUSTODIAN_ERROR_REPORTING_OPT_IN set to True, then
    +# not have CUSTODIAN_REPORTING_OPT_IN set to True, then
     # Sentry will not be enabled.
     
     SENTRY_DSN = None
    @@ -129,7 +109,7 @@ 

    Source code for custodian.custodian

     
         with sentry_sdk.configure_scope() as scope:
     
    -        from getpass import getuser
    +        from getpass import getuser
     
             try:
                 scope.user = {"username": getuser()}
    @@ -182,14 +162,25 @@ 

    Source code for custodian.custodian

             30, this means that Custodian uses the monitors to check for errors
             every 30 x 10 = 300 seconds, i.e., 5 minutes.
         """
    +
         LOG_FILE = "custodian.json"
     
    -    def __init__(self, handlers, jobs, validators=None,
    -                 max_errors_per_job=None,
    -                 max_errors=1, polling_time_step=10, monitor_freq=30,
    -                 skip_over_errors=False, scratch_dir=None,
    -                 gzipped_output=False, checkpoint=False, terminate_func=None,
    -                 terminate_on_nonzero_returncode=True):
    +    def __init__(
    +        self,
    +        handlers,
    +        jobs,
    +        validators=None,
    +        max_errors_per_job=None,
    +        max_errors=1,
    +        polling_time_step=10,
    +        monitor_freq=30,
    +        skip_over_errors=False,
    +        scratch_dir=None,
    +        gzipped_output=False,
    +        checkpoint=False,
    +        terminate_func=None,
    +        terminate_on_nonzero_returncode=True,
    +    ):
             """
             Initializes a Custodian from a list of jobs and error handler.s
     
    @@ -293,11 +284,12 @@ 

    Source code for custodian.custodian

                 Custodian._delete_checkpoints(cwd)
                 n = os.path.join(cwd, "custodian.chk.{}.tar.gz".format(index))
                 with tarfile.open(n, mode="w:gz", compresslevel=3) as f:
    -                f.add(cwd, arcname='.')
    +                f.add(cwd, arcname=".")
                 logger.info("Checkpoint written to {}".format(n))
             except Exception as ex:
                 logger.info("Checkpointing failed")
                 import traceback
    +
                 logger.error(traceback.format_exc())
     
     
    [docs] @classmethod @@ -388,8 +380,9 @@

    Source code for custodian.custodian

     
             custodian_params = process_params(spec.get("custodian_params", {}))
     
    -        return cls(jobs=jobs, handlers=handlers, validators=validators,
    -                   **custodian_params)
    + return cls( + jobs=jobs, handlers=handlers, validators=validators, **custodian_params + )
    [docs] def run(self): """ @@ -409,26 +402,25 @@

    Source code for custodian.custodian

             """
             cwd = os.getcwd()
     
    -        with ScratchDir(self.scratch_dir, create_symbolic_link=True,
    -                        copy_to_current_on_exit=True,
    -                        copy_from_current_on_enter=True) as temp_dir:
    +        with ScratchDir(
    +            self.scratch_dir,
    +            create_symbolic_link=True,
    +            copy_to_current_on_exit=True,
    +            copy_from_current_on_enter=True,
    +        ) as temp_dir:
                 self.total_errors = 0
                 start = datetime.datetime.now()
    -            logger.info("Run started at {} in {}.".format(
    -                start, temp_dir))
    +            logger.info("Run started at {} in {}.".format(start, temp_dir))
                 v = sys.version.replace("\n", " ")
                 logger.info("Custodian running on Python version {}".format(v))
    -            logger.info("Hostname: {}, Cluster: {}".format(
    -                *get_execution_host_info()))
    +            logger.info("Hostname: {}, Cluster: {}".format(*get_execution_host_info()))
     
                 try:
                     # skip jobs until the restart
    -                for job_n, job in islice(enumerate(self.jobs, 1),
    -                                         self.restart, None):
    +                for job_n, job in islice(enumerate(self.jobs, 1), self.restart, None):
                         self._run_job(job_n, job)
                         # We do a dump of the run log after each job.
    -                    dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder,
    -                           indent=4)
    +                    dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder, indent=4)
                         # Checkpoint after each job so that we can recover from last
                         # point and remove old checkpoints
                         if self.checkpoint:
    @@ -441,13 +433,11 @@ 

    Source code for custodian.custodian

                 finally:
                     # Log the corrections to a json file.
                     logger.info("Logging to {}...".format(Custodian.LOG_FILE))
    -                dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder,
    -                       indent=4)
    +                dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder, indent=4)
                     end = datetime.datetime.now()
                     logger.info("Run ended at {}.".format(end))
                     run_time = end - start
    -                logger.info("Run completed. Total time taken = {}."
    -                            .format(run_time))
    +                logger.info("Run completed. Total time taken = {}.".format(run_time))
                     if self.gzipped_output:
                         gzip_dir(".")
     
    @@ -473,11 +463,18 @@ 

    Source code for custodian.custodian

                 MaxCorrectionsError: if max_errors is reached
                 MaxCorrectionsPerHandlerError: if max_errors_per_handler is reached
             """
    -        self.run_log.append({"job": job.as_dict(), "corrections": [],
    -                             "handler": None, "validator": None,
    -                             "max_errors": False, "max_errors_per_job": False,
    -                             "max_errors_per_handler": False,
    -                             "nonzero_return_code": False})
    +        self.run_log.append(
    +            {
    +                "job": job.as_dict(),
    +                "corrections": [],
    +                "handler": None,
    +                "validator": None,
    +                "max_errors": False,
    +                "max_errors_per_job": False,
    +                "max_errors_per_handler": False,
    +                "nonzero_return_code": False,
    +            }
    +        )
             self.errors_current_job = 0
             # reset the counters of the number of times a correction has been
             # applied for each handler
    @@ -487,14 +484,17 @@ 

    Source code for custodian.custodian

             job.setup()
     
             attempt = 0
    -        while (self.total_errors < self.max_errors and
    -               self.errors_current_job < self.max_errors_per_job):
    +        while (
    +            self.total_errors < self.max_errors
    +            and self.errors_current_job < self.max_errors_per_job
    +        ):
                 attempt += 1
                 logger.info(
                     "Starting job no. {} ({}) attempt no. {}. Total errors and "
                     "errors in job thus far = {}, {}.".format(
    -                    job_n, job.name, attempt, self.total_errors,
    -                    self.errors_current_job))
    +                    job_n, job.name, attempt, self.total_errors, self.errors_current_job
    +                )
    +            )
     
                 p = job.run()
                 # Check for errors using the error handlers and perform
    @@ -514,27 +514,28 @@ 

    Source code for custodian.custodian

                                 break
                             terminate = self.terminate_func or p.terminate
                             if n % self.monitor_freq == 0:
    -                            has_error = self._do_check(self.monitors,
    -                                                       terminate)
    +                            has_error = self._do_check(self.monitors, terminate)
                             if terminate is not None and terminate != p.terminate:
                                 time.sleep(self.polling_time_step)
                     else:
                         p.wait()
    -                    if self.terminate_func is not None and \
    -                            self.terminate_func != p.terminate:
    +                    if (
    +                        self.terminate_func is not None
    +                        and self.terminate_func != p.terminate
    +                    ):
                             self.terminate_func()
                             time.sleep(self.polling_time_step)
     
                     zero_return_code = p.returncode == 0
     
    -            logger.info("{}.run has completed. "
    -                        "Checking remaining handlers".format(job.name))
    +            logger.info(
    +                "{}.run has completed. " "Checking remaining handlers".format(job.name)
    +            )
                 # Check for errors again, since in some cases non-monitor
                 # handlers fix the problems detected by monitors
                 # if an error has been found, not all handlers need to run
                 if has_error:
    -                self._do_check([h for h in self.handlers
    -                                if not h.is_monitor])
    +                self._do_check([h for h in self.handlers if not h.is_monitor])
                 else:
                     has_error = self._do_check(self.handlers)
     
    @@ -553,13 +554,14 @@ 

    Source code for custodian.custodian

                     if not zero_return_code:
                         if self.terminate_on_nonzero_returncode:
                             self.run_log[-1]["nonzero_return_code"] = True
    -                        s = "Job return code is %d. Terminating..." % \
    -                            p.returncode
    +                        s = "Job return code is %d. Terminating..." % p.returncode
                             logger.info(s)
                             raise ReturnCodeError(s, True)
                         else:
    -                        warnings.warn("subprocess returned a non-zero return "
    -                                      "code. Check outputs carefully...")
    +                        warnings.warn(
    +                            "subprocess returned a non-zero return "
    +                            "code. Check outputs carefully..."
    +                        )
                     job.postprocess()
                     return
     
    @@ -606,8 +608,9 @@ 

    Source code for custodian.custodian

             try:
                 cwd = os.getcwd()
                 v = sys.version.replace("\n", " ")
    -            logger.info("Custodian started in singleshot mode at {} in {}."
    -                        .format(start, cwd))
    +            logger.info(
    +                "Custodian started in singleshot mode at {} in {}.".format(start, cwd)
    +            )
                 logger.info("Custodian running on Python version {}".format(v))
     
                 # load run log
    @@ -620,31 +623,32 @@ 

    Source code for custodian.custodian

                     job = self.jobs[job_n]
                     logger.info("Setting up job no. 1 ({}) ".format(job.name))
                     job.setup()
    -                self.run_log.append({"job": job.as_dict(), "corrections": [],
    -                                     'job_n': job_n})
    +                self.run_log.append(
    +                    {"job": job.as_dict(), "corrections": [], "job_n": job_n}
    +                )
                     return len(self.jobs)
                 else:
                     # Continuing after running calculation
    -                job_n = self.run_log[-1]['job_n']
    +                job_n = self.run_log[-1]["job_n"]
                     job = self.jobs[job_n]
     
                     # If we had to fix errors from a previous run, insert clean log
                     # dict
    -                if len(self.run_log[-1]['corrections']) > 0:
    -                    logger.info("Reran {}.run due to fixable errors".format(
    -                        job.name))
    +                if len(self.run_log[-1]["corrections"]) > 0:
    +                    logger.info("Reran {}.run due to fixable errors".format(job.name))
     
                     # check error handlers
    -                logger.info("Checking error handlers for {}.run".format(
    -                    job.name))
    +                logger.info("Checking error handlers for {}.run".format(job.name))
                     if self._do_check(self.handlers):
                         logger.info("Failed validation based on error handlers")
                         # raise an error for an unrecoverable error
                         for x in self.run_log[-1]["corrections"]:
                             if not x["actions"] and x["handler"].raises_runtime_error:
                                 self.run_log[-1]["handler"] = x["handler"]
    -                            s = "Unrecoverable error for handler: {}. " \
    +                            s = (
    +                                "Unrecoverable error for handler: {}. "
                                     "Raising RuntimeError".format(x["handler"])
    +                            )
                                 raise NonRecoverableError(s, True, x["handler"])
                         logger.info("Corrected input based on error handlers")
                         # Return with more jobs to run if recoverable error caught
    @@ -672,8 +676,9 @@ 

    Source code for custodian.custodian

                     # Setup next job_n
                     job_n += 1
                     job = self.jobs[job_n]
    -                self.run_log.append({"job": job.as_dict(), "corrections": [],
    -                                     'job_n': job_n})
    +                self.run_log.append(
    +                    {"job": job.as_dict(), "corrections": [], "job_n": job_n}
    +                )
                     job.setup()
                     return len(self.jobs) - job_n
     
    @@ -685,13 +690,11 @@ 

    Source code for custodian.custodian

             finally:
                 # Log the corrections to a json file.
                 logger.info("Logging to {}...".format(Custodian.LOG_FILE))
    -            dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder,
    -                   indent=4)
    +            dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder, indent=4)
                 end = datetime.datetime.now()
                 logger.info("Run ended at {}.".format(end))
                 run_time = end - start
    -            logger.info("Run completed. Total time taken = {}."
    -                        .format(run_time))
    +            logger.info("Run completed. Total time taken = {}.".format(run_time))
                 if self.finished and self.gzipped_output:
                     gzip_dir(".")
    @@ -703,14 +706,20 @@

    Source code for custodian.custodian

             for h in handlers:
                 try:
                     if h.check():
    -                    if h.max_num_corrections is not None \
    -                            and h.n_applied_corrections >= h.max_num_corrections:
    -                        msg = "Maximum number of corrections {} reached " \
    -                              "for handler {}".format(h.max_num_corrections, h)
    +                    if (
    +                        h.max_num_corrections is not None
    +                        and h.n_applied_corrections >= h.max_num_corrections
    +                    ):
    +                        msg = (
    +                            "Maximum number of corrections {} reached "
    +                            "for handler {}".format(h.max_num_corrections, h)
    +                        )
                             if h.raise_on_max:
                                 self.run_log[-1]["handler"] = h
                                 self.run_log[-1]["max_errors_per_handler"] = True
    -                            raise MaxCorrectionsPerHandlerError(msg, True, h.max_num_corrections, h)
    +                            raise MaxCorrectionsPerHandlerError(
    +                                msg, True, h.max_num_corrections, h
    +                            )
                             else:
                                 logger.warning(msg + " Correction not applied.")
                                 continue
    @@ -729,17 +738,17 @@ 

    Source code for custodian.custodian

                         raise
                     else:
                         import traceback
    +
                         logger.error("Bad handler %s " % h)
                         logger.error(traceback.format_exc())
                         corrections.append(
    -                        {"errors": ["Bad handler %s " % h],
    -                         "actions": []})
    +                        {"errors": ["Bad handler %s " % h], "actions": []}
    +                    )
             self.total_errors += len(corrections)
             self.errors_current_job += len(corrections)
             self.run_log[-1]["corrections"].extend(corrections)
             # We do a dump of the run log after each check.
    -        dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder,
    -               indent=4)
    +        dumpfn(self.run_log, Custodian.LOG_FILE, cls=MontyEncoder, indent=4)
             return len(corrections) > 0
    @@ -908,7 +917,7 @@

    Source code for custodian.custodian

         Exception class for Custodian errors.
         """
     
    -    def __init__(self, message, raises=False):
    +    def __init__(self, message, raises=False):
             """
             Initializes the error with a message.
     
    @@ -926,7 +935,7 @@ 

    Source code for custodian.custodian

         Error raised when a validator does not pass the check
         """
     
    -    def __init__(self, message, raises, validator):
    +    def __init__(self, message, raises, validator):
             """
             Args:
                 message (str): Message passed to Exception
    @@ -942,7 +951,7 @@ 

    Source code for custodian.custodian

         Error raised when a handler found an error but could not fix it
         """
     
    -    def __init__(self, message, raises, handler):
    +    def __init__(self, message, raises, handler):
             """
             Args:
                 message (str): Message passed to Exception
    @@ -957,6 +966,7 @@ 

    Source code for custodian.custodian

         """
         Error raised when the process gave non zero return code
         """
    +
         pass
    @@ -965,7 +975,7 @@

    Source code for custodian.custodian

         Error raised when the maximum allowed number of errors is reached
         """
     
    -    def __init__(self, message, raises, max_errors):
    +    def __init__(self, message, raises, max_errors):
             """
             Args:
                 message (str): Message passed to Exception
    @@ -981,7 +991,7 @@ 

    Source code for custodian.custodian

         Error raised when the maximum allowed number of errors per job is reached
         """
     
    -    def __init__(self, message, raises, max_errors_per_job, job):
    +    def __init__(self, message, raises, max_errors_per_job, job):
             """
             Args:
                 message (str): Message passed to Exception
    @@ -999,7 +1009,7 @@ 

    Source code for custodian.custodian

         Error raised when the maximum allowed number of errors per handler is reached
         """
     
    -    def __init__(self, message, raises, max_errors_per_handler, handler):
    +    def __init__(self, message, raises, max_errors_per_handler, handler):
             """
             Args:
                 message (str): Message passed to Exception
    @@ -1032,14 +1042,14 @@ 

    Quick search

    - +
    @@ -50,12 +49,12 @@

    Navigation

    Source code for custodian.feff.handlers

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    -from custodian.custodian import ErrorHandler
    +from __future__ import unicode_literals, division
    +from custodian.custodian import ErrorHandler
     import re
    -from custodian.utils import backup
    -from pymatgen.io.feff.sets import FEFFDictSet
    -from custodian.feff.interpreter import FeffModder
    +from custodian.utils import backup
    +from pymatgen.io.feff.sets import FEFFDictSet
    +from custodian.feff.interpreter import FeffModder
     import logging
     
     """ This module implements specific error handler for FEFF runs. """
    @@ -67,7 +66,15 @@ 

    Source code for custodian.feff.handlers

     __email__ = "chz022@ucsd.edu"
     __date__ = "Oct 18, 2017"
     
    -FEFF_BACKUP_FILES = ["ATOMS", "HEADER", "PARAMETERS", "POTENTIALS", "feff.inp", "*.cif", "pot.bin"]
    +FEFF_BACKUP_FILES = [
    +    "ATOMS",
    +    "HEADER",
    +    "PARAMETERS",
    +    "POTENTIALS",
    +    "feff.inp",
    +    "*.cif",
    +    "pot.bin",
    +]
     
     logger = logging.getLogger(__name__)
     
    @@ -79,7 +86,7 @@ 

    Source code for custodian.feff.handlers

     
         is_monitor = False
     
    -    def __init__(self, output_filename='log1.dat'):
    +    def __init__(self, output_filename="log1.dat"):
             """
             Initializes the handler with the output file to check
     
    @@ -101,7 +108,7 @@ 

    Source code for custodian.feff.handlers

     
             # Process the output file and get converge information
             not_converge_pattern = re.compile("Convergence not reached.*")
    -        converge_pattern = re.compile('Convergence reached.*')
    +        converge_pattern = re.compile("Convergence reached.*")
             for _, line in enumerate(open(self.output_filename)):
                 if len(not_converge_pattern.findall(line)) > 0:
                     return True
    @@ -120,22 +127,23 @@ 

    Source code for custodian.feff.handlers

     
             # Add RESTART card to PARAMETERS
             if "RESTART" not in feff_input.tags:
    -            actions.append({"dict": "PARAMETERS",
    -                            "action": {"_set": {"RESTART": []}}})
    +            actions.append({"dict": "PARAMETERS", "action": {"_set": {"RESTART": []}}})
     
             if nscmt < 100 and ca == 0.2:
                 scf_values[2] = 100
                 scf_values[4] = 3  # Set nmix = 3
    -            actions.append({"dict": "PARAMETERS",
    -                            "action": {"_set": {"SCF": scf_values}}})
    +            actions.append(
    +                {"dict": "PARAMETERS", "action": {"_set": {"SCF": scf_values}}}
    +            )
                 FeffModder().apply_actions(actions)
                 return {"errors": ["Non-converging job"], "actions": actions}
     
             elif nscmt == 100 and nmix == 3 and ca > 0.01:
                 # Reduce the convergence accelerator factor
                 scf_values[3] = round(ca / 2, 2)
    -            actions.append({"dict": "PARAMETERS",
    -                            "action": {"_set": {"SCF": scf_values}}})
    +            actions.append(
    +                {"dict": "PARAMETERS", "action": {"_set": {"SCF": scf_values}}}
    +            )
                 FeffModder().apply_actions(actions)
                 return {"errors": ["Non-converging job"], "actions": actions}
     
    @@ -143,8 +151,9 @@ 

    Source code for custodian.feff.handlers

                 # Set ca = 0.05 and set nmix
                 scf_values[3] = 0.05
                 scf_values[4] = 5
    -            actions.append({"dict": "PARAMETERS",
    -                            "action": {"_set": {"SCF": scf_values}}})
    +            actions.append(
    +                {"dict": "PARAMETERS", "action": {"_set": {"SCF": scf_values}}}
    +            )
                 FeffModder().apply_actions(actions)
                 return {"errors": ["Non-converging job"], "actions": actions}
     
    @@ -152,16 +161,18 @@ 

    Source code for custodian.feff.handlers

                 # Set ca = 0.05 and set nmix
                 scf_values[3] = 0.05
                 scf_values[4] = 10
    -            actions.append({"dict": "PARAMETERS",
    -                            "action": {"_set": {"SCF": scf_values}}})
    +            actions.append(
    +                {"dict": "PARAMETERS", "action": {"_set": {"SCF": scf_values}}}
    +            )
                 FeffModder().apply_actions(actions)
                 return {"errors": ["Non-converging job"], "actions": actions}
     
             elif nmix == 10 and ca < 0.2:
                 # loop through ca with nmix = 10
                 scf_values[3] = round(ca * 2, 2)
    -            actions.append({"dict": "PARAMETERS",
    -                            "action": {"_set": {"SCF": scf_values}}})
    +            actions.append(
    +                {"dict": "PARAMETERS", "action": {"_set": {"SCF": scf_values}}}
    +            )
                 FeffModder().apply_actions(actions)
                 return {"errors": ["Non-converging job"], "actions": actions}
     
    @@ -190,14 +201,14 @@ 

    Quick search

    - +
    @@ -50,16 +49,16 @@

    Navigation

    Source code for custodian.feff.interpreter

     # coding: utf-8
     
    -from __future__ import unicode_literals
    +from __future__ import unicode_literals
     
    -from custodian.ansible.actions import FileActions, DictActions
    -from custodian.ansible.interpreter import Modder
    -from pymatgen.io.feff.sets import FEFFDictSet
    +from custodian.ansible.actions import FileActions, DictActions
    +from custodian.ansible.interpreter import Modder
    +from pymatgen.io.feff.sets import FEFFDictSet
     import os
     
     
     
    [docs]class FeffModder(Modder): - def __init__(self, actions=None, strict=True, feffinp=None): + def __init__(self, actions=None, strict=True, feffinp=None): """ Initializes a Modder for FeffInput sets @@ -76,7 +75,7 @@

    Source code for custodian.feff.interpreter

                     Initialized automatically if not passed (but passing it will
                     avoid having to reparse the directory).
             """
    -        self.feffinp = feffinp or FEFFDictSet.from_directory('.')
    +        self.feffinp = feffinp or FEFFDictSet.from_directory(".")
             self.feffinp = self.feffinp.all_input()
             actions = actions or [FileActions, DictActions]
             super(FeffModder, self).__init__(actions, strict)
    @@ -103,14 +102,16 @@ 

    Source code for custodian.feff.interpreter

                     raise ValueError("Unrecognized format: {}".format(a))
             if modified:
                 feff = self.feffinp
    -            feff_input = "\n\n".join(str(feff[k]) for k in
    -                                     ["HEADER", "PARAMETERS", "POTENTIALS", "ATOMS"]
    -                                     if k in feff)
    +            feff_input = "\n\n".join(
    +                str(feff[k])
    +                for k in ["HEADER", "PARAMETERS", "POTENTIALS", "ATOMS"]
    +                if k in feff
    +            )
                 for k, v in feff.items():
    -                with open(os.path.join('.', k), "w") as f:
    +                with open(os.path.join(".", k), "w") as f:
                         f.write(str(v))
     
    -            with open(os.path.join('.', "feff.inp"), "w") as f:
    +            with open(os.path.join(".", "feff.inp"), "w") as f:
                     f.write(feff_input)
    @@ -134,14 +135,14 @@

    Quick search

    - +
    @@ -54,10 +53,10 @@

    Source code for custodian.feff.jobs

     import os
     import shutil
     import logging
    -from monty.shutil import decompress_dir
    +from monty.shutil import decompress_dir
     
    -from custodian.custodian import Job
    -from custodian.utils import backup
    +from custodian.custodian import Job
    +from custodian.utils import backup
     
     """ This module implements basic kinds of jobs for FEFF runs."""
     
    @@ -68,7 +67,7 @@ 

    Source code for custodian.feff.jobs

     __maintainer__ = "Chen Zheng"
     __email__ = "chz022@ucsd.edu"
     __status__ = "Alpha"
    -__date__ = '10/20/17'
    +__date__ = "10/20/17"
     
     FEFF_INPUT_FILES = {"feff.inp"}
     FEFF_BACKUP_FILES = {"ATOMS", "HEADER", "PARAMETERS", "POTENTIALS"}
    @@ -79,9 +78,15 @@ 

    Source code for custodian.feff.jobs

         A basic FEFF job, run whatever is in the directory.
         """
     
    -    def __init__(self, feff_cmd, output_file="feff.out",
    -                 stderr_file="std_feff_err.txt", backup=True,
    -                 gzipped=False, gzipped_prefix='feff_out'):
    +    def __init__(
    +        self,
    +        feff_cmd,
    +        output_file="feff.out",
    +        stderr_file="std_feff_err.txt",
    +        backup=True,
    +        gzipped=False,
    +        gzipped_prefix="feff_out",
    +    ):
             """
             This constructor is used for a standard FEFF initialization
     
    @@ -112,7 +117,7 @@ 

    Source code for custodian.feff.jobs

             Returns:
     
             """
    -        decompress_dir('.')
    +        decompress_dir(".")
     
             if self.backup:
                 for f in FEFF_INPUT_FILES:
    @@ -129,8 +134,9 @@ 

    Source code for custodian.feff.jobs

             Returns:
                 (subprocess.Popen) Used for monitoring.
             """
    -        with open(self.output_file, "w") as f_std, \
    -                open(self.stderr_file, "w", buffering=1) as f_err:
    +        with open(self.output_file, "w") as f_std, open(
    +            self.stderr_file, "w", buffering=1
    +        ) as f_err:
                 # Use line buffering for stderr
                 # On TSCC, need to run shell command
                 p = subprocess.Popen(self.feff_cmd, stdout=f_std, stderr=f_err, shell=True)
    @@ -165,14 +171,14 @@ 

    Quick search

    - +
    @@ -50,7 +49,7 @@

    Navigation

    Source code for custodian.nwchem.handlers

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
     """
     This module implements error handlers for Nwchem runs. Currently tested only
    @@ -64,10 +63,10 @@ 

    Source code for custodian.nwchem.handlers

     __status__ = "Beta"
     __date__ = "5/20/13"
     
    -from custodian.custodian import ErrorHandler
    -from custodian.utils import backup
    -from pymatgen.io.nwchem import NwOutput, NwInput
    -from custodian.ansible.interpreter import Modder
    +from custodian.custodian import ErrorHandler
    +from custodian.utils import backup
    +from pymatgen.io.nwchem import NwOutput, NwInput
    +from custodian.ansible.interpreter import Modder
     
     
     
    [docs]class NwchemErrorHandler(ErrorHandler): @@ -76,7 +75,7 @@

    Source code for custodian.nwchem.handlers

         generated by pymatgen.
         """
     
    -    def __init__(self, output_filename="mol.nwout"):
    +    def __init__(self, output_filename="mol.nwout"):
             """
             Initializes with an output file name.
     
    @@ -93,7 +92,7 @@ 

    Source code for custodian.nwchem.handlers

             # Checks output file for errors.
             out = NwOutput(self.output_filename)
             self.errors = []
    -        self.input_file = out.job_info['input']
    +        self.input_file = out.job_info["input"]
             if out.data[-1]["has_error"]:
                 self.errors.extend(out.data[-1]["errors"])
             self.errors = list(set(self.errors))
    @@ -118,9 +117,9 @@ 

    Source code for custodian.nwchem.handlers

             nwi = NwInput.from_file(self.input_file)
             for e in self.errors:
                 if e == "autoz error":
    -                action = {"_set": {"geometry_options": ["units",
    -                                                        "angstroms",
    -                                                        "noautoz"]}}
    +                action = {
    +                    "_set": {"geometry_options": ["units", "angstroms", "noautoz"]}
    +                }
                     actions.append(action)
                 elif e == "Bad convergence":
                     t = nwi.tasks[self.ntasks - 1]
    @@ -145,7 +144,7 @@ 

    Source code for custodian.nwchem.handlers

             nwi.write_file(self.input_file)
             return {"errors": self.errors, "actions": actions}
    - def __str__(self): + def __str__(self): return "NwchemErrorHandler"
    @@ -169,14 +168,14 @@

    Quick search

    - +
    @@ -50,15 +49,15 @@

    Navigation

    Source code for custodian.nwchem.jobs

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
     import subprocess
     import shutil
     
    -from monty.io import zopen
    +from monty.io import zopen
     
    -from custodian.custodian import Job
    -from monty.shutil import gzip_dir
    +from custodian.custodian import Job
    +from monty.shutil import gzip_dir
     
     """
     This module implements basic kinds of jobs for Nwchem runs.
    @@ -77,9 +76,15 @@ 

    Source code for custodian.nwchem.jobs

         A basic Nwchem job.
         """
     
    -    def __init__(self, nwchem_cmd, input_file="mol.nw",
    -                 output_file="mol.nwout", gzipped=False,
    -                 backup=True, settings_override=None):
    +    def __init__(
    +        self,
    +        nwchem_cmd,
    +        input_file="mol.nw",
    +        output_file="mol.nwout",
    +        gzipped=False,
    +        backup=True,
    +        settings_override=None,
    +    ):
             """
             Initializes a basic NwChem job.
     
    @@ -116,9 +121,8 @@ 

    Source code for custodian.nwchem.jobs

             """
             Performs actual nwchem run.
             """
    -        with zopen(self.output_file, 'w') as fout:
    -            return subprocess.Popen(self.nwchem_cmd + [self.input_file],
    -                                    stdout=fout)
    + with zopen(self.output_file, "w") as fout: + return subprocess.Popen(self.nwchem_cmd + [self.input_file], stdout=fout)
    [docs] def postprocess(self): """ @@ -148,14 +152,14 @@

    Quick search

    - +
    @@ -50,15 +49,15 @@

    Navigation

    Source code for custodian.qchem.handlers

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
     # This module implements new error handlers for QChem runs.
     
     import os
    -from pymatgen.io.qchem.inputs import QCInput
    -from pymatgen.io.qchem.outputs import QCOutput
    -from custodian.custodian import ErrorHandler
    -from custodian.utils import backup
    +from pymatgen.io.qchem.inputs import QCInput
    +from pymatgen.io.qchem.outputs import QCOutput
    +from custodian.custodian import ErrorHandler
    +from custodian.utils import backup
     
     __author__ = "Samuel Blau, Brandon Wood, Shyam Dwaraknath"
     __copyright__ = "Copyright 2018, The Materials Project"
    @@ -78,11 +77,13 @@ 

    Source code for custodian.qchem.handlers

     
         is_monitor = False
     
    -    def __init__(self,
    -                 input_file="mol.qin",
    -                 output_file="mol.qout",
    -                 scf_max_cycles=200,
    -                 geom_max_cycles=200):
    +    def __init__(
    +        self,
    +        input_file="mol.qin",
    +        output_file="mol.qout",
    +        scf_max_cycles=200,
    +        geom_max_cycles=200,
    +    ):
             """
             Initializes the error handler from a set of input and output files.
     
    @@ -110,7 +111,10 @@ 

    Source code for custodian.qchem.handlers

             if "out_of_opt_cycles" not in self.errors and len(self.opt_error_history) > 0:
                 self.opt_error_history = []
             # If we're out of optimization cycles and we have unconnected fragments, no need to handle any errors
    -        if "out_of_opt_cycles" in self.errors and self.outdata["structure_change"] == "unconnected_fragments":
    +        if (
    +            "out_of_opt_cycles" in self.errors
    +            and self.outdata["structure_change"] == "unconnected_fragments"
    +        ):
                 return False
             return len(self.errors) > 0
    @@ -124,8 +128,7 @@

    Source code for custodian.qchem.handlers

                 # increase to that value and rerun. If already set, check if
                 # scf_algorithm is unset or set to DIIS, in which case set to GDM.
                 # Otherwise, tell user to call SCF error handler and do nothing.
    -            if str(self.qcinp.rem.get("max_scf_cycles")) != str(
    -                    self.scf_max_cycles):
    +            if str(self.qcinp.rem.get("max_scf_cycles")) != str(self.scf_max_cycles):
                     self.qcinp.rem["max_scf_cycles"] = self.scf_max_cycles
                     actions.append({"max_scf_cycles": self.scf_max_cycles})
                 elif self.qcinp.rem.get("thresh", "10") != "14":
    @@ -148,13 +151,15 @@ 

    Source code for custodian.qchem.handlers

             elif "out_of_opt_cycles" in self.errors:
                 # Check number of opt cycles. If less than geom_max_cycles, increase
                 # to that value, set last geom as new starting geom and rerun.
    -            if str(self.qcinp.rem.get(
    -                    "geom_opt_max_cycles")) != str(self.geom_max_cycles):
    +            if str(self.qcinp.rem.get("geom_opt_max_cycles")) != str(
    +                self.geom_max_cycles
    +            ):
                     self.qcinp.rem["geom_opt_max_cycles"] = self.geom_max_cycles
                     actions.append({"geom_max_cycles:": self.scf_max_cycles})
                     if len(self.outdata.get("energy_trajectory")) > 1:
                         self.qcinp.molecule = self.outdata.get(
    -                        "molecule_from_last_geometry")
    +                        "molecule_from_last_geometry"
    +                    )
                         actions.append({"molecule": "molecule_from_last_geometry"})
                 elif self.qcinp.rem.get("thresh", "10") != "14":
                     self.qcinp.rem["thresh"] = "14"
    @@ -173,7 +178,11 @@ 

    Source code for custodian.qchem.handlers

                         if self.opt_error_history[-1] == "no_change":
                             # If no structural changes occured in two consecutive optimizations,
                             # and we still haven't converged, then just exit.
    -                        return {"errors": self.errors, "actions": None, "opt_error_history": self.opt_error_history}
    +                        return {
    +                            "errors": self.errors,
    +                            "actions": None,
    +                            "opt_error_history": self.opt_error_history,
    +                        }
                     self.qcinp.molecule = self.outdata.get("molecule_from_last_geometry")
                     actions.append({"molecule": "molecule_from_last_geometry"})
     
    @@ -181,16 +190,13 @@ 

    Source code for custodian.qchem.handlers

                 # Set last geom as new starting geom and rerun. If no opt cycles,
                 # use diff SCF strat? Diff initial guess? Change basis?
                 if len(self.outdata.get("energy_trajectory")) > 1:
    -                self.qcinp.molecule = self.outdata.get(
    -                    "molecule_from_last_geometry")
    +                self.qcinp.molecule = self.outdata.get("molecule_from_last_geometry")
                     actions.append({"molecule": "molecule_from_last_geometry"})
                 elif self.qcinp.rem.get("thresh", "10") != "14":
                     self.qcinp.rem["thresh"] = "14"
                     actions.append({"thresh": "14"})
                 else:
    -                print(
    -                    "Use a different initial guess? Perhaps a different basis?"
    -                )
    +                print("Use a different initial guess? Perhaps a different basis?")
     
             elif "premature_end_FileMan_error" in self.errors:
                 if self.qcinp.rem.get("thresh", "10") != "14":
    @@ -200,20 +206,23 @@ 

    Source code for custodian.qchem.handlers

                     self.qcinp.rem["scf_guess_always"] = True
                     actions.append({"scf_guess_always": True})
                 else:
    -                print("We're in a bad spot if we get a FileMan error while always generating a new SCF guess...")
    +                print(
    +                    "We're in a bad spot if we get a FileMan error while always generating a new SCF guess..."
    +                )
     
             elif "hessian_eigenvalue_error" in self.errors:
                 if self.qcinp.rem.get("thresh", "10") != "14":
                     self.qcinp.rem["thresh"] = "14"
                     actions.append({"thresh": "14"})
                 else:
    -                print("Not sure how to fix hessian_eigenvalue_error if thresh is already 14!")
    +                print(
    +                    "Not sure how to fix hessian_eigenvalue_error if thresh is already 14!"
    +                )
     
             elif "failed_to_transform_coords" in self.errors:
                 # Check for symmetry flag in rem. If not False, set to False and rerun.
                 # If already False, increase threshold?
    -            if not self.qcinp.rem.get("sym_ignore") or self.qcinp.rem.get(
    -                    "symmetry"):
    +            if not self.qcinp.rem.get("sym_ignore") or self.qcinp.rem.get("symmetry"):
                     self.qcinp.rem["sym_ignore"] = True
                     self.qcinp.rem["symmetry"] = False
                     actions.append({"sym_ignore": True})
    @@ -258,11 +267,14 @@ 

    Source code for custodian.qchem.handlers

                 # You should never get here. If correct is being called then errors should have at least one entry,
                 # in which case it should have been caught by the if/elifs above.
                 print("Errors:", self.errors)
    -            print("Must have gotten an error which is correctly parsed but not included in the handler. FIX!!!")
    +            print(
    +                "Must have gotten an error which is correctly parsed but not included in the handler. FIX!!!"
    +            )
                 return {"errors": self.errors, "actions": None}
     
    -        if {"molecule": "molecule_from_last_geometry"} in actions and \
    -                str(self.qcinp.rem.get("geom_opt_hessian")).lower() == "read":
    +        if {"molecule": "molecule_from_last_geometry"} in actions and str(
    +            self.qcinp.rem.get("geom_opt_hessian")
    +        ).lower() == "read":
                 del self.qcinp.rem["geom_opt_hessian"]
                 actions.append({"geom_opt_hessian": "deleted"})
             os.rename(self.input_file, self.input_file + ".last")
    @@ -277,11 +289,13 @@ 

    Source code for custodian.qchem.handlers

     
         is_monitor = False
     
    -    def __init__(self,
    -                 input_file="mol.qin",
    -                 output_file="mol.qout",
    -                 rca_gdm_thresh=1.0E-3,
    -                 scf_max_cycles=200):
    +    def __init__(
    +        self,
    +        input_file="mol.qin",
    +        output_file="mol.qout",
    +        rca_gdm_thresh=1.0e-3,
    +        scf_max_cycles=200,
    +    ):
             """
             Initializes the error handler from a set of input and output files.
     
    @@ -333,14 +347,14 @@ 

    Quick search

    - +
    @@ -50,7 +49,7 @@

    Navigation

    Source code for custodian.qchem.jobs

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     import math
     
     # New QChem job module
    @@ -62,12 +61,12 @@ 

    Source code for custodian.qchem.jobs

     import subprocess
     import random
     import numpy as np
    -from pymatgen.core import Molecule
    -from pymatgen.io.qchem.inputs import QCInput
    -from pymatgen.io.qchem.outputs import QCOutput, check_for_structure_changes
    -from pymatgen.analysis.graphs import MoleculeGraph
    -from pymatgen.analysis.local_env import OpenBabelNN
    -from custodian.custodian import Job
    +from pymatgen.core import Molecule
    +from pymatgen.io.qchem.inputs import QCInput
    +from pymatgen.io.qchem.outputs import QCOutput, check_for_structure_changes
    +from pymatgen.analysis.graphs import MoleculeGraph
    +from pymatgen.analysis.local_env import OpenBabelNN
    +from custodian.custodian import Job
     
     __author__ = "Samuel Blau, Brandon Wood, Shyam Dwaraknath"
     __copyright__ = "Copyright 2018, The Materials Project"
    @@ -84,18 +83,20 @@ 

    Source code for custodian.qchem.jobs

         A basic QChem Job.
         """
     
    -    def __init__(self,
    -                 qchem_command,
    -                 max_cores,
    -                 multimode="openmp",
    -                 input_file="mol.qin",
    -                 output_file="mol.qout",
    -                 qclog_file="mol.qclog",
    -                 suffix="",
    -                 scratch_dir=os.getcwd(),
    -                 save_scratch=False,
    -                 save_name="saved_scratch",
    -                 backup=True):
    +    def __init__(
    +        self,
    +        qchem_command,
    +        max_cores,
    +        multimode="openmp",
    +        input_file="mol.qin",
    +        output_file="mol.qout",
    +        qclog_file="mol.qclog",
    +        suffix="",
    +        scratch_dir=os.getcwd(),
    +        save_scratch=False,
    +        save_name="saved_scratch",
    +        backup=True,
    +    ):
             """
             Args:
                 qchem_command (str): Command to run QChem.
    @@ -131,7 +132,12 @@ 

    Source code for custodian.qchem.jobs

             multi = {"openmp": "-nt", "mpi": "-np"}
             if self.multimode not in multi:
                 raise RuntimeError("ERROR: Multimode should only be set to openmp or mpi")
    -        command = [multi[self.multimode], str(self.max_cores), self.input_file, self.output_file]
    +        command = [
    +            multi[self.multimode],
    +            str(self.max_cores),
    +            self.input_file,
    +            self.output_file,
    +        ]
             if self.save_scratch:
                 command.append(self.save_name)
             command = self.qchem_command + command
    @@ -144,9 +150,9 @@ 

    Source code for custodian.qchem.jobs

             if self.backup:
                 shutil.copy(self.input_file, "{}.orig".format(self.input_file))
             os.environ["QCSCRATCH"] = self.scratch_dir
    -        if self.multimode == 'openmp':
    -            os.environ['QCTHREADS'] = str(self.max_cores)
    -            os.environ['OMP_NUM_THREADS'] = str(self.max_cores)
    + if self.multimode == "openmp": + os.environ["QCTHREADS"] = str(self.max_cores) + os.environ["OMP_NUM_THREADS"] = str(self.max_cores)
    [docs] def postprocess(self): if self.suffix != "": @@ -165,22 +171,24 @@

    Source code for custodian.qchem.jobs

             mydir = os.path.join("/tmp", "qchem" + myrand)
             os.mkdir(mydir)
             os.environ["QCLOCALSCR"] = mydir
    -        qclog = open(self.qclog_file, 'w')
    +        qclog = open(self.qclog_file, "w")
             p = subprocess.Popen(self.current_command, stdout=qclog, shell=True)
             return p
    [docs] @classmethod - def opt_with_frequency_flattener(cls, - qchem_command, - multimode="openmp", - input_file="mol.qin", - output_file="mol.qout", - qclog_file="mol.qclog", - max_iterations=10, - max_molecule_perturb_scale=0.3, - check_connectivity=True, - linked=True, - **QCJob_kwargs): + def opt_with_frequency_flattener( + cls, + qchem_command, + multimode="openmp", + input_file="mol.qin", + output_file="mol.qout", + qclog_file="mol.qclog", + max_iterations=10, + max_molecule_perturb_scale=0.3, + check_connectivity=True, + linked=True, + **QCJob_kwargs + ): """ Optimize a structure and calculate vibrational frequencies to check if the structure is in a true minima. If a frequency is negative, iteratively @@ -202,7 +210,7 @@

    Source code for custodian.qchem.jobs

                     :class:`custodian.qchem.jobs.QCJob`.
             """
             if not os.path.exists(input_file):
    -            raise AssertionError('Input file must be present!')
    +            raise AssertionError("Input file must be present!")
     
             if linked:
     
    @@ -218,22 +226,30 @@ 

    Source code for custodian.qchem.jobs

                 energy_history = []
     
                 for ii in range(max_iterations):
    -                yield (QCJob(
    -                    qchem_command=qchem_command,
    -                    multimode=multimode,
    -                    input_file=input_file,
    -                    output_file=output_file,
    -                    qclog_file=qclog_file,
    -                    suffix=".opt_" + str(ii),
    -                    scratch_dir=os.getcwd(),
    -                    save_scratch=True,
    -                    save_name="chain_scratch",
    -                    backup=first,
    -                    **QCJob_kwargs))
    +                yield (
    +                    QCJob(
    +                        qchem_command=qchem_command,
    +                        multimode=multimode,
    +                        input_file=input_file,
    +                        output_file=output_file,
    +                        qclog_file=qclog_file,
    +                        suffix=".opt_" + str(ii),
    +                        scratch_dir=os.getcwd(),
    +                        save_scratch=True,
    +                        save_name="chain_scratch",
    +                        backup=first,
    +                        **QCJob_kwargs
    +                    )
    +                )
                     opt_outdata = QCOutput(output_file + ".opt_" + str(ii)).data
                     first = False
    -                if opt_outdata["structure_change"] == "unconnected_fragments" and not opt_outdata["completion"]:
    -                    print("Unstable molecule broke into unconnected fragments which failed to optimize! Exiting...")
    +                if (
    +                    opt_outdata["structure_change"] == "unconnected_fragments"
    +                    and not opt_outdata["completion"]
    +                ):
    +                    print(
    +                        "Unstable molecule broke into unconnected fragments which failed to optimize! Exiting..."
    +                    )
                         break
                     else:
                         energy_history.append(opt_outdata.get("final_energy"))
    @@ -243,49 +259,66 @@ 

    Source code for custodian.qchem.jobs

                             opt=orig_input.opt,
                             pcm=orig_input.pcm,
                             solvent=orig_input.solvent,
    -                        smx=orig_input.smx)
    +                        smx=orig_input.smx,
    +                    )
                         freq_QCInput.write_file(input_file)
    -                    yield (QCJob(
    -                        qchem_command=qchem_command,
    -                        multimode=multimode,
    -                        input_file=input_file,
    -                        output_file=output_file,
    -                        qclog_file=qclog_file,
    -                        suffix=".freq_" + str(ii),
    -                        scratch_dir=os.getcwd(),
    -                        save_scratch=True,
    -                        save_name="chain_scratch",
    -                        backup=first,
    -                        **QCJob_kwargs))
    +                    yield (
    +                        QCJob(
    +                            qchem_command=qchem_command,
    +                            multimode=multimode,
    +                            input_file=input_file,
    +                            output_file=output_file,
    +                            qclog_file=qclog_file,
    +                            suffix=".freq_" + str(ii),
    +                            scratch_dir=os.getcwd(),
    +                            save_scratch=True,
    +                            save_name="chain_scratch",
    +                            backup=first,
    +                            **QCJob_kwargs
    +                        )
    +                    )
                         outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                         errors = outdata.get("errors")
                         if len(errors) != 0:
    -                        raise AssertionError('No errors should be encountered while flattening frequencies!')
    -                    if outdata.get('frequencies')[0] > 0.0:
    +                        raise AssertionError(
    +                            "No errors should be encountered while flattening frequencies!"
    +                        )
    +                    if outdata.get("frequencies")[0] > 0.0:
                             print("All frequencies positive!")
                             break
    -                    elif abs(outdata.get('frequencies')[0]) < 15.0 and outdata.get('frequencies')[1] > 0.0:
    -                        print("One negative frequency smaller than 15.0 - not worth further flattening!")
    +                    elif (
    +                        abs(outdata.get("frequencies")[0]) < 15.0
    +                        and outdata.get("frequencies")[1] > 0.0
    +                    ):
    +                        print(
    +                            "One negative frequency smaller than 15.0 - not worth further flattening!"
    +                        )
                             break
                         else:
                             if len(energy_history) > 1:
    -                            if abs(energy_history[-1] - energy_history[-2]) < energy_diff_cutoff:
    +                            if (
    +                                abs(energy_history[-1] - energy_history[-2])
    +                                < energy_diff_cutoff
    +                            ):
                                     print("Energy change below cutoff!")
                                     break
                             opt_QCInput = QCInput(
    -                            molecule=opt_outdata.get("molecule_from_optimized_geometry"),
    +                            molecule=opt_outdata.get(
    +                                "molecule_from_optimized_geometry"
    +                            ),
                                 rem=opt_rem,
                                 opt=orig_input.opt,
                                 pcm=orig_input.pcm,
                                 solvent=orig_input.solvent,
    -                            smx=orig_input.smx)
    +                            smx=orig_input.smx,
    +                        )
                             opt_QCInput.write_file(input_file)
                 if os.path.exists(os.path.join(os.getcwd(), "chain_scratch")):
                     shutil.rmtree(os.path.join(os.getcwd(), "chain_scratch"))
     
             else:
                 if not os.path.exists(input_file):
    -                raise AssertionError('Input file must be present!')
    +                raise AssertionError("Input file must be present!")
                 orig_opt_input = QCInput.from_file(input_file)
                 orig_opt_rem = copy.deepcopy(orig_opt_input.rem)
                 orig_freq_rem = copy.deepcopy(orig_opt_input.rem)
    @@ -294,24 +327,32 @@ 

    Source code for custodian.qchem.jobs

                 history = []
     
                 for ii in range(max_iterations):
    -                yield (QCJob(
    -                    qchem_command=qchem_command,
    -                    multimode=multimode,
    -                    input_file=input_file,
    -                    output_file=output_file,
    -                    qclog_file=qclog_file,
    -                    suffix=".opt_" + str(ii),
    -                    backup=first,
    -                    **QCJob_kwargs))
    +                yield (
    +                    QCJob(
    +                        qchem_command=qchem_command,
    +                        multimode=multimode,
    +                        input_file=input_file,
    +                        output_file=output_file,
    +                        qclog_file=qclog_file,
    +                        suffix=".opt_" + str(ii),
    +                        backup=first,
    +                        **QCJob_kwargs
    +                    )
    +                )
                     opt_outdata = QCOutput(output_file + ".opt_" + str(ii)).data
                     if first:
    -                    orig_species = copy.deepcopy(opt_outdata.get('species'))
    -                    orig_charge = copy.deepcopy(opt_outdata.get('charge'))
    -                    orig_multiplicity = copy.deepcopy(opt_outdata.get('multiplicity'))
    -                    orig_energy = copy.deepcopy(opt_outdata.get('final_energy'))
    +                    orig_species = copy.deepcopy(opt_outdata.get("species"))
    +                    orig_charge = copy.deepcopy(opt_outdata.get("charge"))
    +                    orig_multiplicity = copy.deepcopy(opt_outdata.get("multiplicity"))
    +                    orig_energy = copy.deepcopy(opt_outdata.get("final_energy"))
                     first = False
    -                if opt_outdata["structure_change"] == "unconnected_fragments" and not opt_outdata["completion"]:
    -                    print("Unstable molecule broke into unconnected fragments which failed to optimize! Exiting...")
    +                if (
    +                    opt_outdata["structure_change"] == "unconnected_fragments"
    +                    and not opt_outdata["completion"]
    +                ):
    +                    print(
    +                        "Unstable molecule broke into unconnected fragments which failed to optimize! Exiting..."
    +                    )
                         break
                     else:
                         freq_QCInput = QCInput(
    @@ -320,33 +361,49 @@ 

    Source code for custodian.qchem.jobs

                             opt=orig_opt_input.opt,
                             pcm=orig_opt_input.pcm,
                             solvent=orig_opt_input.solvent,
    -                        smx=orig_opt_input.smx)
    +                        smx=orig_opt_input.smx,
    +                    )
                         freq_QCInput.write_file(input_file)
    -                    yield (QCJob(
    -                        qchem_command=qchem_command,
    -                        multimode=multimode,
    -                        input_file=input_file,
    -                        output_file=output_file,
    -                        qclog_file=qclog_file,
    -                        suffix=".freq_" + str(ii),
    -                        backup=first,
    -                        **QCJob_kwargs))
    +                    yield (
    +                        QCJob(
    +                            qchem_command=qchem_command,
    +                            multimode=multimode,
    +                            input_file=input_file,
    +                            output_file=output_file,
    +                            qclog_file=qclog_file,
    +                            suffix=".freq_" + str(ii),
    +                            backup=first,
    +                            **QCJob_kwargs
    +                        )
    +                    )
                         outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                         errors = outdata.get("errors")
                         if len(errors) != 0:
    -                        raise AssertionError('No errors should be encountered while flattening frequencies!')
    -                    if outdata.get('frequencies')[0] > 0.0:
    +                        raise AssertionError(
    +                            "No errors should be encountered while flattening frequencies!"
    +                        )
    +                    if outdata.get("frequencies")[0] > 0.0:
                             print("All frequencies positive!")
    -                        if opt_outdata.get('final_energy') > orig_energy:
    -                            print("WARNING: Energy increased during frequency flattening!")
    +                        if opt_outdata.get("final_energy") > orig_energy:
    +                            print(
    +                                "WARNING: Energy increased during frequency flattening!"
    +                            )
                             break
                         else:
                             hist = {}
    -                        hist["molecule"] = copy.deepcopy(outdata.get("initial_molecule"))
    -                        hist["geometry"] = copy.deepcopy(outdata.get("initial_geometry"))
    +                        hist["molecule"] = copy.deepcopy(
    +                            outdata.get("initial_molecule")
    +                        )
    +                        hist["geometry"] = copy.deepcopy(
    +                            outdata.get("initial_geometry")
    +                        )
                             hist["frequencies"] = copy.deepcopy(outdata.get("frequencies"))
    -                        hist["frequency_mode_vectors"] = copy.deepcopy(outdata.get("frequency_mode_vectors"))
    -                        hist["num_neg_freqs"] = sum(1 for freq in outdata.get("frequencies") if freq < 0)
    +                        hist["frequency_mode_vectors"] = copy.deepcopy(
    +                            outdata.get("frequency_mode_vectors")
    +                        )
    +                        hist["num_neg_freqs"] = sum(
    +                            1 for freq in outdata.get("frequencies") if freq < 0
    +                        )
                             hist["energy"] = copy.deepcopy(opt_outdata.get("final_energy"))
                             hist["index"] = len(history)
                             hist["children"] = []
    @@ -370,18 +427,24 @@ 

    Source code for custodian.qchem.jobs

                                     history[-1]["parent"] = parent_hist["index"]
                                 else:
                                     raise AssertionError(
    -                                    "ERROR: your parent should always be one or two iterations behind you! Exiting...")
    +                                    "ERROR: your parent should always be one or two iterations behind you! Exiting..."
    +                                )
     
                                 # if the number of negative frequencies has remained constant or increased from parent to
                                 # child,
    -                            if history[-1]["num_neg_freqs"] >= parent_hist["num_neg_freqs"]:
    +                            if (
    +                                history[-1]["num_neg_freqs"]
    +                                >= parent_hist["num_neg_freqs"]
    +                            ):
                                     # check to see if the parent only has one child, aka only the positive perturbation has
                                     # been tried,
                                     # in which case just try the negative perturbation from the same parent
                                     if len(parent_hist["children"]) == 1:
                                         ref_mol = parent_hist["molecule"]
                                         geom_to_perturb = parent_hist["geometry"]
    -                                    negative_freq_vecs = parent_hist["frequency_mode_vectors"][0]
    +                                    negative_freq_vecs = parent_hist[
    +                                        "frequency_mode_vectors"
    +                                    ][0]
                                         reversed_direction = True
                                         standard = False
                                         parent_hist["children"].append(len(history))
    @@ -391,32 +454,56 @@ 

    Source code for custodian.qchem.jobs

                                         # If we're dealing with just one negative frequency,
                                         if parent_hist["num_neg_freqs"] == 1:
                                             make_good_child_next_parent = False
    -                                        if history[parent_hist["children"][0]]["energy"] < history[-1]["energy"]:
    -                                            good_child = copy.deepcopy(history[parent_hist["children"][0]])
    +                                        if (
    +                                            history[parent_hist["children"][0]][
    +                                                "energy"
    +                                            ]
    +                                            < history[-1]["energy"]
    +                                        ):
    +                                            good_child = copy.deepcopy(
    +                                                history[parent_hist["children"][0]]
    +                                            )
                                             else:
                                                 good_child = copy.deepcopy(history[-1])
                                             if good_child["num_neg_freqs"] > 1:
                                                 raise Exception(
                                                     "ERROR: Child with lower energy has more negative frequencies! "
    -                                                "Exiting...")
    -                                        elif good_child["energy"] < parent_hist["energy"]:
    +                                                "Exiting..."
    +                                            )
    +                                        elif (
    +                                            good_child["energy"] < parent_hist["energy"]
    +                                        ):
                                                 make_good_child_next_parent = True
    -                                        elif vector_list_diff(good_child["frequency_mode_vectors"][0],
    -                                                              parent_hist["frequency_mode_vectors"][0]) > 0.2:
    +                                        elif (
    +                                            vector_list_diff(
    +                                                good_child["frequency_mode_vectors"][0],
    +                                                parent_hist["frequency_mode_vectors"][
    +                                                    0
    +                                                ],
    +                                            )
    +                                            > 0.2
    +                                        ):
                                                 make_good_child_next_parent = True
                                             else:
    -                                            raise Exception("ERROR: Good child not good enough! Exiting...")
    +                                            raise Exception(
    +                                                "ERROR: Good child not good enough! Exiting..."
    +                                            )
                                             if make_good_child_next_parent:
                                                 good_child["index"] = len(history)
                                                 history.append(good_child)
                                                 ref_mol = history[-1]["molecule"]
                                                 geom_to_perturb = history[-1]["geometry"]
    -                                            negative_freq_vecs = history[-1]["frequency_mode_vectors"][0]
    +                                            negative_freq_vecs = history[-1][
    +                                                "frequency_mode_vectors"
    +                                            ][0]
                                         else:
                                             raise Exception(
    -                                            "ERROR: Can't deal with multiple neg frequencies yet! Exiting...")
    +                                            "ERROR: Can't deal with multiple neg frequencies yet! Exiting..."
    +                                        )
                                     else:
    -                                    raise AssertionError("ERROR: Parent cannot have more than two childen! Exiting...")
    +                                    raise AssertionError(
    +                                        "ERROR: Parent cannot have more than two childen! Exiting..."
    +                                    )
                                 # Implicitly, if the number of negative frequencies decreased from parent to child,
                                 # continue normally.
                             if standard:
    @@ -425,26 +512,32 @@ 

    Source code for custodian.qchem.jobs

                             min_molecule_perturb_scale = 0.1
                             scale_grid = 10
                             perturb_scale_grid = (
    -                                                     max_molecule_perturb_scale - min_molecule_perturb_scale
    -                                             ) / scale_grid
    +                            max_molecule_perturb_scale - min_molecule_perturb_scale
    +                        ) / scale_grid
     
                             structure_successfully_perturbed = False
                             for molecule_perturb_scale in np.arange(
    -                                max_molecule_perturb_scale, min_molecule_perturb_scale,
    -                                -perturb_scale_grid):
    +                            max_molecule_perturb_scale,
    +                            min_molecule_perturb_scale,
    +                            -perturb_scale_grid,
    +                        ):
                                 new_coords = perturb_coordinates(
                                     old_coords=geom_to_perturb,
                                     negative_freq_vecs=negative_freq_vecs,
                                     molecule_perturb_scale=molecule_perturb_scale,
    -                                reversed_direction=reversed_direction)
    +                                reversed_direction=reversed_direction,
    +                            )
                                 new_molecule = Molecule(
                                     species=orig_species,
                                     coords=new_coords,
                                     charge=orig_charge,
    -                                spin_multiplicity=orig_multiplicity)
    +                                spin_multiplicity=orig_multiplicity,
    +                            )
                                 if check_connectivity:
    -                                structure_successfully_perturbed = check_for_structure_changes(
    -                                    ref_mol, new_molecule) == "no_change"
    +                                structure_successfully_perturbed = (
    +                                    check_for_structure_changes(ref_mol, new_molecule)
    +                                    == "no_change"
    +                                )
                                     if structure_successfully_perturbed:
                                         break
                             if not structure_successfully_perturbed:
    @@ -459,21 +552,24 @@ 

    Source code for custodian.qchem.jobs

                                 opt=orig_opt_input.opt,
                                 pcm=orig_opt_input.pcm,
                                 solvent=orig_opt_input.solvent,
    -                            smx=orig_opt_input.smx)
    +                            smx=orig_opt_input.smx,
    +                        )
                             new_opt_QCInput.write_file(input_file)
    -
    [docs]def perturb_coordinates(old_coords, negative_freq_vecs, molecule_perturb_scale, - reversed_direction): - max_dis = max( - [math.sqrt(sum([x ** 2 for x in vec])) for vec in negative_freq_vecs]) +
    [docs]def perturb_coordinates( + old_coords, negative_freq_vecs, molecule_perturb_scale, reversed_direction +): + max_dis = max([math.sqrt(sum([x ** 2 for x in vec])) for vec in negative_freq_vecs]) scale = molecule_perturb_scale / max_dis normalized_vecs = [[x * scale for x in vec] for vec in negative_freq_vecs] direction = 1.0 if reversed_direction: direction = -1.0 - return [[c + v * direction for c, v in zip(coord, vec)] - for coord, vec in zip(old_coords, normalized_vecs)]
    + return [ + [c + v * direction for c, v in zip(coord, vec)] + for coord, vec in zip(old_coords, normalized_vecs) + ]
    [docs]def vector_list_diff(vecs1, vecs2): @@ -505,14 +601,14 @@

    Quick search

    - +
    @@ -50,7 +49,7 @@

    Navigation

    Source code for custodian.utils

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
     """
     Utility function and classes.
    @@ -64,7 +63,7 @@ 

    Source code for custodian.utils

     __email__ = "shyuep@gmail.com"
     __date__ = "1/12/14"
     
    -from glob import glob
    +from glob import glob
     import logging
     import os
     import tarfile
    @@ -81,8 +80,7 @@ 

    Source code for custodian.utils

             prefix (str): prefix to the files. Defaults to error, which means a
                 series of error.1.tar.gz, error.2.tar.gz, ... will be generated.
         """
    -    num = max([0] + [int(f.split(".")[1])
    -                     for f in glob("{}.*.tar.gz".format(prefix))])
    +    num = max([0] + [int(f.split(".")[1]) for f in glob("{}.*.tar.gz".format(prefix))])
         filename = "{}.{}.tar.gz".format(prefix, num + 1)
         logging.info("Backing up run to {}.".format(filename))
         with tarfile.open(filename, "w:gz") as tar:
    @@ -99,15 +97,16 @@ 

    Source code for custodian.utils

         Returns:
             (HOSTNAME, CLUSTER_NAME)
         """
    -    host = os.environ.get('HOSTNAME', None)
    -    cluster = os.environ.get('SGE_O_HOST', None)
    +    host = os.environ.get("HOSTNAME", None)
    +    cluster = os.environ.get("SGE_O_HOST", None)
         if host is None:
             try:
                 import socket
    +
                 host = host or socket.gethostname()
             except Exception:
                 pass
    -    return host or 'unknown', cluster or 'unknown'
    + return host or "unknown", cluster or "unknown"
    @@ -130,14 +129,14 @@

    Quick search

    - +
    @@ -50,34 +49,33 @@

    Navigation

    Source code for custodian.vasp.handlers

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
    -from monty.os.path import zpath
    +from monty.os.path import zpath
     import os
     import time
     import datetime
     import operator
     import shutil
     import logging
    -from functools import reduce
    -from collections import Counter
    +from functools import reduce
    +from collections import Counter
     import re
     
     import numpy as np
     
    -from monty.dev import deprecated
    -from monty.serialization import loadfn
    +from monty.dev import deprecated
    +from monty.serialization import loadfn
     
    -from custodian.custodian import ErrorHandler
    -from custodian.utils import backup
    -from pymatgen.io.vasp import Poscar, VaspInput, Incar, Kpoints, Vasprun, \
    -    Oszicar, Outcar
    -from pymatgen.transformations.standard_transformations import \
    -    SupercellTransformation
    +from custodian.custodian import ErrorHandler
    +from custodian.utils import backup
    +from pymatgen.io.vasp.inputs import Poscar, VaspInput, Incar, Kpoints
    +from pymatgen.io.vasp.outputs import Vasprun, Oszicar, Outcar
    +from pymatgen.transformations.standard_transformations import SupercellTransformation
     
    -from custodian.ansible.interpreter import Modder
    -from custodian.ansible.actions import FileActions
    -from custodian.vasp.interpreter import VaspModder
    +from custodian.ansible.interpreter import Modder
    +from custodian.ansible.actions import FileActions
    +from custodian.vasp.interpreter import VaspModder
     
     """
     This module implements specific error handlers for VASP runs. These handlers
    @@ -85,16 +83,27 @@ 

    Source code for custodian.vasp.handlers

     by modifying the input files.
     """
     
    -__author__ = "Shyue Ping Ong, William Davidson Richards, Anubhav Jain, " \
    -             "Wei Chen, Stephen Dacek"
    +__author__ = (
    +    "Shyue Ping Ong, William Davidson Richards, Anubhav Jain, "
    +    "Wei Chen, Stephen Dacek"
    +)
     __version__ = "0.1"
     __maintainer__ = "Shyue Ping Ong"
     __email__ = "ongsp@ucsd.edu"
     __status__ = "Beta"
     __date__ = "2/4/13"
     
    -VASP_BACKUP_FILES = {"INCAR", "KPOINTS", "POSCAR", "OUTCAR", "CONTCAR",
    -                     "OSZICAR", "vasprun.xml", "vasp.out", "std_err.txt"}
    +VASP_BACKUP_FILES = {
    +    "INCAR",
    +    "KPOINTS",
    +    "POSCAR",
    +    "OUTCAR",
    +    "CONTCAR",
    +    "OSZICAR",
    +    "vasprun.xml",
    +    "vasp.out",
    +    "std_err.txt",
    +}
     
     
     
    [docs]class VaspErrorHandler(ErrorHandler): @@ -106,20 +115,21 @@

    Source code for custodian.vasp.handlers

         is_monitor = True
     
         error_msgs = {
    -        "tet": ["Tetrahedron method fails for NKPT<4",
    -                "Fatal error detecting k-mesh",
    -                "Fatal error: unable to match k-point",
    -                "Routine TETIRR needs special values",
    -                "Tetrahedron method fails (number of k-points < 4)"],
    -        "inv_rot_mat": ["inverse of rotation matrix was not found (increase "
    -                        "SYMPREC)"],
    +        "tet": [
    +            "Tetrahedron method fails for NKPT<4",
    +            "Fatal error detecting k-mesh",
    +            "Fatal error: unable to match k-point",
    +            "Routine TETIRR needs special values",
    +            "Tetrahedron method fails (number of k-points < 4)",
    +        ],
    +        "inv_rot_mat": [
    +            "inverse of rotation matrix was not found (increase " "SYMPREC)"
    +        ],
             "brmix": ["BRMIX: very serious problems"],
    -        "subspacematrix": ["WARNING: Sub-Space-Matrix is not hermitian in "
    -                           "DAV"],
    +        "subspacematrix": ["WARNING: Sub-Space-Matrix is not hermitian in " "DAV"],
             "tetirr": ["Routine TETIRR needs special values"],
             "incorrect_shift": ["Could not get correct shifts"],
    -        "real_optlay": ["REAL_OPTLAY: internal error",
    -                        "REAL_OPT: internal ERROR"],
    +        "real_optlay": ["REAL_OPTLAY: internal error", "REAL_OPT: internal ERROR"],
             "rspher": ["ERROR RSPHER"],
             "dentet": ["DENTET"],
             "too_few_bands": ["TOO FEW BANDS"],
    @@ -129,24 +139,26 @@ 

    Source code for custodian.vasp.handlers

             "pricel": ["internal error in subroutine PRICEL"],
             "zpotrf": ["LAPACK: Routine ZPOTRF failed"],
             "amin": ["One of the lattice vectors is very long (>50 A), but AMIN"],
    -        "zbrent": ["ZBRENT: fatal internal in",
    -                   "ZBRENT: fatal error in bracketing"],
    +        "zbrent": ["ZBRENT: fatal internal in", "ZBRENT: fatal error in bracketing"],
             "pssyevx": ["ERROR in subspace rotation PSSYEVX"],
             "eddrmm": ["WARNING in EDDRMM: call to ZHEGV failed"],
             "edddav": ["Error EDDDAV: Call to ZHEGV failed"],
    -        "grad_not_orth": [
    -            "EDWAV: internal error, the gradient is not orthogonal"],
    +        "grad_not_orth": ["EDWAV: internal error, the gradient is not orthogonal"],
             "nicht_konv": ["ERROR: SBESSELITER : nicht konvergent"],
             "zheev": ["ERROR EDDIAG: Call to routine ZHEEV failed!"],
             "elf_kpar": ["ELF: KPAR>1 not implemented"],
             "elf_ncl": ["WARNING: ELF not implemented for non collinear case"],
             "rhosyg": ["RHOSYG internal error"],
             "posmap": ["POSMAP internal error: symmetry equivalent atom not found"],
    -        "point_group": ["Error: point group operation missing"]
    +        "point_group": ["Error: point group operation missing"],
         }
     
    -    def __init__(self, output_filename="vasp.out", natoms_large_cell=100,
    -                 errors_subset_to_catch=None):
    +    def __init__(
    +        self,
    +        output_filename="vasp.out",
    +        natoms_large_cell=100,
    +        errors_subset_to_catch=None,
    +    ):
             """
             Initializes the handler with the output file to check.
     
    @@ -178,12 +190,15 @@ 

    Source code for custodian.vasp.handlers

             self.error_count = Counter()
             # threshold of number of atoms to treat the cell as large.
             self.natoms_large_cell = natoms_large_cell
    -        self.errors_subset_to_catch = errors_subset_to_catch or list(VaspErrorHandler.error_msgs.keys())
    +        self.errors_subset_to_catch = errors_subset_to_catch or list(
    +            VaspErrorHandler.error_msgs.keys()
    +        )
             self.logger = logging.getLogger(self.__class__.__name__)
     
     
    [docs] def check(self): incar = Incar.from_file("INCAR") self.errors = set() + error_msgs = set() with open(self.output_filename, "r") as f: for line in f: l = line.strip() @@ -195,10 +210,12 @@

    Source code for custodian.vasp.handlers

                                     # computation (e.g., defects) if yes we don't
                                     # want to kill it because there is a change in
                                     # e-density (brmix error)
    -                                if err == "brmix" and 'NELECT' in incar:
    +                                if err == "brmix" and "NELECT" in incar:
                                         continue
                                     self.errors.add(err)
    -                                self.logger.error(msg, extra={"incar": incar.as_dict()})
    +                                error_msgs.add(msg)
    +        for msg in error_msgs:
    +            self.logger.error(msg, extra={"incar": incar.as_dict()})
             return len(self.errors) > 0
    [docs] def correct(self): @@ -207,80 +224,112 @@

    Source code for custodian.vasp.handlers

             vi = VaspInput.from_directory(".")
     
             if self.errors.intersection(["tet", "dentet"]):
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ISMEAR": 0, "SIGMA": 0.05}}})
    +            if vi["INCAR"].get("KSPACING"):
    +                # decrease KSPACING by 20% in each direction (approximately double no. of kpoints)
    +                actions.append(
    +                    {
    +                        "dict": "INCAR",
    +                        "action": {
    +                            "_set": {"KSPACING": vi["INCAR"].get("KSPACING") * 0.8}
    +                        },
    +                    }
    +                )
    +            else:
    +                actions.append(
    +                    {"dict": "INCAR", "action": {"_set": {"ISMEAR": 0, "SIGMA": 0.05}}}
    +                )
     
             if "inv_rot_mat" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"SYMPREC": 1e-8}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}})
     
             if "brmix" in self.errors:
                 # If there is not a valid OUTCAR already, increment
                 # error count to 1 to skip first fix
    -            if self.error_count['brmix'] == 0:
    +            if self.error_count["brmix"] == 0:
                     try:
    -                    assert (Outcar(zpath(os.path.join(
    -                        os.getcwd(), "OUTCAR"))).is_stopped is False)
    +                    assert (
    +                        Outcar(zpath(os.path.join(os.getcwd(), "OUTCAR"))).is_stopped
    +                        is False
    +                    )
                     except Exception:
    -                    self.error_count['brmix'] += 1
    +                    self.error_count["brmix"] += 1
     
    -            if self.error_count['brmix'] == 0:
    +            if self.error_count["brmix"] == 0:
                     # Valid OUTCAR - simply rerun the job and increment
                     # error count for next time
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ISTART": 1}}})
    -                self.error_count['brmix'] += 1
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ISTART": 1}}})
    +                self.error_count["brmix"] += 1
     
    -            elif self.error_count['brmix'] == 1:
    +            elif self.error_count["brmix"] == 1:
                     # Use Kerker mixing w/default values for other parameters
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"IMIX": 1}}})
    -                self.error_count['brmix'] += 1
    -
    -            elif self.error_count['brmix'] == 2 and vi["KPOINTS"].style \
    -                    == Kpoints.supported_modes.Gamma:
    -                actions.append({"dict": "KPOINTS",
    -                                "action": {"_set": {"generation_style": "Monkhorst"}}})
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_unset": {"IMIX": 1}}})
    -                self.error_count['brmix'] += 1
    -
    -            elif self.error_count['brmix'] in [2, 3] and vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    -                actions.append({"dict": "KPOINTS",
    -                                "action": {"_set": {"generation_style": "Gamma"}}})
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_unset": {"IMIX": 1}}})
    -                self.error_count['brmix'] += 1
    +                actions.append({"dict": "INCAR", "action": {"_set": {"IMIX": 1}}})
    +                self.error_count["brmix"] += 1
    +
    +            elif (
    +                self.error_count["brmix"] == 2
    +                and vi["KPOINTS"].style == Kpoints.supported_modes.Gamma
    +            ):
    +                actions.append(
    +                    {
    +                        "dict": "KPOINTS",
    +                        "action": {"_set": {"generation_style": "Monkhorst"}},
    +                    }
    +                )
    +                actions.append({"dict": "INCAR", "action": {"_unset": {"IMIX": 1}}})
    +                self.error_count["brmix"] += 1
    +
    +            elif (
    +                self.error_count["brmix"] in [2, 3]
    +                and vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst
    +            ):
    +                actions.append(
    +                    {
    +                        "dict": "KPOINTS",
    +                        "action": {"_set": {"generation_style": "Gamma"}},
    +                    }
    +                )
    +                actions.append({"dict": "INCAR", "action": {"_unset": {"IMIX": 1}}})
    +                self.error_count["brmix"] += 1
     
                     if vi["KPOINTS"].num_kpts < 1:
    -                    all_kpts_even = all([
    -                        bool(n % 2 == 0) for n in vi["KPOINTS"].kpts[0]
    -                    ])
    +                    all_kpts_even = all(
    +                        [bool(n % 2 == 0) for n in vi["KPOINTS"].kpts[0]]
    +                    )
                         if all_kpts_even:
    -                        new_kpts = (
    -                            tuple(n + 1 for n in vi["KPOINTS"].kpts[0]),)
    -                        actions.append({"dict": "KPOINTS", "action": {"_set": {
    -                            "kpoints": new_kpts
    -                        }}})
    +                        new_kpts = (tuple(n + 1 for n in vi["KPOINTS"].kpts[0]),)
    +                        actions.append(
    +                            {
    +                                "dict": "KPOINTS",
    +                                "action": {"_set": {"kpoints": new_kpts}},
    +                            }
    +                        )
     
                 else:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ISYM": 0}}})
    -
    -                if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    -                    actions.append({"dict": "KPOINTS",
    -                                    "action": {
    -                                        "_set": {"generation_style": "Gamma"}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}})
    +                if vi["KPOINTS"] is not None:
    +                    if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    +                        actions.append(
    +                            {
    +                                "dict": "KPOINTS",
    +                                "action": {"_set": {"generation_style": "Gamma"}},
    +                            }
    +                        )
     
                     # Based on VASP forum's recommendation, you should delete the
                     # CHGCAR and WAVECAR when dealing with this error.
                     if vi["INCAR"].get("ICHARG", 0) < 10:
    -                    actions.append({"file": "CHGCAR",
    -                                    "action": {
    -                                        "_file_delete": {'mode': "actual"}}})
    -                    actions.append({"file": "WAVECAR",
    -                                    "action": {
    -                                        "_file_delete": {'mode': "actual"}}})
    +                    actions.append(
    +                        {
    +                            "file": "CHGCAR",
    +                            "action": {"_file_delete": {"mode": "actual"}},
    +                        }
    +                    )
    +                    actions.append(
    +                        {
    +                            "file": "WAVECAR",
    +                            "action": {"_file_delete": {"mode": "actual"}},
    +                        }
    +                    )
     
             if "zpotrf" in self.errors:
                 # Usually caused by short bond distances. If on the first step,
    @@ -296,94 +345,108 @@ 

    Source code for custodian.vasp.handlers

                 if nsteps >= 1:
                     potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0
                     actions.append(
    -                    {"dict": "INCAR",
    -                     "action": {"_set": {"ISYM": 0, "POTIM": potim}}})
    -            elif vi["INCAR"].get("NSW", 0) == 0 \
    -                    or vi["INCAR"].get("ISIF", 0) in range(3):
    -                actions.append(
    -                    {"dict": "INCAR", "action": {"_set": {"ISYM": 0}}})
    +                    {"dict": "INCAR", "action": {"_set": {"ISYM": 0, "POTIM": potim}}}
    +                )
    +            elif vi["INCAR"].get("NSW", 0) == 0 or vi["INCAR"].get("ISIF", 0) in range(
    +                3
    +            ):
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}})
                 else:
                     s = vi["POSCAR"].structure
                     s.apply_strain(0.2)
    -                actions.append({"dict": "POSCAR",
    -                                "action": {"_set": {"structure": s.as_dict()}}})
    +                actions.append(
    +                    {"dict": "POSCAR", "action": {"_set": {"structure": s.as_dict()}}}
    +                )
     
                 # Based on VASP forum's recommendation, you should delete the
                 # CHGCAR and WAVECAR when dealing with this error.
                 if vi["INCAR"].get("ICHARG", 0) < 10:
    -                actions.append({"file": "CHGCAR",
    -                                "action": {"_file_delete": {'mode': "actual"}}})
    -                actions.append({"file": "WAVECAR",
    -                                "action": {"_file_delete": {'mode': "actual"}}})
    +                actions.append(
    +                    {"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}
    +                )
    +                actions.append(
    +                    {"file": "WAVECAR", "action": {"_file_delete": {"mode": "actual"}}}
    +                )
     
             if self.errors.intersection(["subspacematrix"]):
                 if self.error_count["subspacematrix"] == 0:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"LREAL": False}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}})
                 else:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"PREC": "Accurate"}}})
    +                actions.append(
    +                    {"dict": "INCAR", "action": {"_set": {"PREC": "Accurate"}}}
    +                )
                 self.error_count["subspacematrix"] += 1
     
             if self.errors.intersection(["rspher", "real_optlay", "nicht_konv"]):
                 s = vi["POSCAR"].structure
                 if len(s) < self.natoms_large_cell:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"LREAL": False}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}})
                 else:
                     # for large supercell, try an in-between option LREAL = True
                     # prior to LREAL = False
    -                if self.error_count['real_optlay'] == 0:
    +                if self.error_count["real_optlay"] == 0:
                         # use real space projectors generated by pot
    -                    actions.append({"dict": "INCAR",
    -                                    "action": {"_set": {"LREAL": True}}})
    -                elif self.error_count['real_optlay'] == 1:
    -                    actions.append({"dict": "INCAR",
    -                                    "action": {"_set": {"LREAL": False}}})
    -                self.error_count['real_optlay'] += 1
    +                    actions.append(
    +                        {"dict": "INCAR", "action": {"_set": {"LREAL": True}}}
    +                    )
    +                elif self.error_count["real_optlay"] == 1:
    +                    actions.append(
    +                        {"dict": "INCAR", "action": {"_set": {"LREAL": False}}}
    +                    )
    +                self.error_count["real_optlay"] += 1
     
             if self.errors.intersection(["tetirr", "incorrect_shift"]):
     
    -            if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    -                actions.append({"dict": "KPOINTS",
    -                                "action": {
    -                                    "_set": {"generation_style": "Gamma"}}})
    +            if vi["KPOINTS"] is not None:
    +                if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    +                    actions.append(
    +                        {
    +                            "dict": "KPOINTS",
    +                            "action": {"_set": {"generation_style": "Gamma"}},
    +                        }
    +                    )
     
             if "rot_matrix" in self.errors:
    -            if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    -                actions.append({"dict": "KPOINTS",
    -                                "action": {
    -                                    "_set": {"generation_style": "Gamma"}}})
    +            if vi["KPOINTS"] is not None:
    +                if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst:
    +                    actions.append(
    +                        {
    +                            "dict": "KPOINTS",
    +                            "action": {"_set": {"generation_style": "Gamma"}},
    +                        }
    +                    )
                 else:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ISYM": 0}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}})
     
             if "amin" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"AMIN": "0.01"}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}})
     
             if "triple_product" in self.errors:
                 s = vi["POSCAR"].structure
                 trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0)))
                 new_s = trans.apply_transformation(s)
    -            actions.append({"dict": "POSCAR",
    -                            "action": {"_set": {"structure": new_s.as_dict()}},
    -                            "transformation": trans.as_dict()})
    +            actions.append(
    +                {
    +                    "dict": "POSCAR",
    +                    "action": {"_set": {"structure": new_s.as_dict()}},
    +                    "transformation": trans.as_dict(),
    +                }
    +            )
     
             if "pricel" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}})
    +            actions.append(
    +                {"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}}
    +            )
     
             if "brions" in self.errors:
                 potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"POTIM": potim}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}})
     
             if "zbrent" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"IBRION": 1}}})
    -            actions.append({"file": "CONTCAR",
    -                            "action": {"_file_copy": {"dest": "POSCAR"}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}})
    +            actions.append(
    +                {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}
    +            )
     
             if "too_few_bands" in self.errors:
                 if "NBANDS" in vi["INCAR"]:
    @@ -398,58 +461,58 @@ 

    Source code for custodian.vasp.handlers

                                     break
                                 except (IndexError, ValueError):
                                     pass
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"NBANDS": int(1.1 * nbands)}}})
    +            actions.append(
    +                {"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}
    +            )
     
             if "pssyevx" in self.errors:
                 actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}})
             if "eddrmm" in self.errors:
                 # RMM algorithm is not stable for this calculation
                 if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]:
    -                actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}})
    +                actions.append(
    +                    {"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}
    +                )
                 else:
                     potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"POTIM": potim}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}})
                 if vi["INCAR"].get("ICHARG", 0) < 10:
    -                actions.append({"file": "CHGCAR",
    -                                "action": {"_file_delete": {'mode': "actual"}}})
    -                actions.append({"file": "WAVECAR",
    -                                "action": {"_file_delete": {'mode': "actual"}}})
    +                actions.append(
    +                    {"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}
    +                )
    +                actions.append(
    +                    {"file": "WAVECAR", "action": {"_file_delete": {"mode": "actual"}}}
    +                )
     
             if "edddav" in self.errors:
                 if vi["INCAR"].get("ICHARG", 0) < 10:
    -                actions.append({"file": "CHGCAR",
    -                                "action": {"_file_delete": {'mode': "actual"}}})
    +                actions.append(
    +                    {"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}
    +                )
                 actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "All"}}})
     
             if "grad_not_orth" in self.errors:
                 if vi["INCAR"].get("ISMEAR", 1) < 0:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ISMEAR": 0, "SIGMA": 0.05}}})
    +                actions.append(
    +                    {"dict": "INCAR", "action": {"_set": {"ISMEAR": 0, "SIGMA": 0.05}}}
    +                )
     
             if "zheev" in self.errors:
                 if vi["INCAR"].get("ALGO", "Fast").lower() != "exact":
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ALGO": "Exact"}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Exact"}}})
             if "elf_kpar" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"KPAR": 1}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"KPAR": 1}}})
     
             if "rhosyg" in self.errors:
                 if vi["INCAR"].get("SYMPREC", 1e-4) == 1e-4:
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ISYM": 0}}})
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"SYMPREC": 1e-4}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-4}}})
     
             if "posmap" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"SYMPREC": 1e-6}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-6}}})
     
             if "point_group" in self.errors:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ISYM": 0}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}})
     
             VaspModder(vi=vi).apply_actions(actions)
             return {"errors": list(self.errors), "actions": actions}
    @@ -464,11 +527,9 @@

    Source code for custodian.vasp.handlers

     
         is_monitor = True
     
    -    error_msgs = {
    -        "lrf_comm": ["LRF_COMMUTATOR internal error"],
    -    }
    +    error_msgs = {"lrf_comm": ["LRF_COMMUTATOR internal error"]}
     
    -    def __init__(self, output_filename="std_err.txt"):
    +    def __init__(self, output_filename="std_err.txt"):
             """
             Initializes the handler with the output file to check.
     
    @@ -499,11 +560,11 @@ 

    Source code for custodian.vasp.handlers

             vi = VaspInput.from_directory(".")
     
             if "lrf_comm" in self.errors:
    -            if Outcar(zpath(os.path.join(
    -                    os.getcwd(), "OUTCAR"))).is_stopped is False:
    +            if Outcar(zpath(os.path.join(os.getcwd(), "OUTCAR"))).is_stopped is False:
                     if not vi["INCAR"].get("LPEAD"):
    -                    actions.append({"dict": "INCAR",
    -                                    "action": {"_set": {"LPEAD": True}}})
    +                    actions.append(
    +                        {"dict": "INCAR", "action": {"_set": {"LPEAD": True}}}
    +                    )
     
             VaspModder(vi=vi).apply_actions(actions)
             return {"errors": list(self.errors), "actions": actions}
    @@ -519,12 +580,14 @@

    Source code for custodian.vasp.handlers

         is_monitor = True
     
         error_msgs = {
    -        "kpoints_trans": ["internal error in GENERATE_KPOINTS_TRANS: "
    -                          "number of G-vector changed in star"],
    -        "out_of_memory": ["Allocation would exceed memory limit"]
    +        "kpoints_trans": [
    +            "internal error in GENERATE_KPOINTS_TRANS: "
    +            "number of G-vector changed in star"
    +        ],
    +        "out_of_memory": ["Allocation would exceed memory limit"],
         }
     
    -    def __init__(self, output_filename="std_err.txt"):
    +    def __init__(self, output_filename="std_err.txt"):
             """
             Initializes the handler with the output file to check.
     
    @@ -560,15 +623,17 @@ 

    Source code for custodian.vasp.handlers

                     m = max(int(round(m ** (1 / 3))), 1)
                     if vi["KPOINTS"].style.name.lower().startswith("m"):
                         m += m % 2
    -                actions.append({"dict": "KPOINTS",
    -                                "action": {"_set": {"kpoints": [[m] * 3]}}})
    -                self.error_count['kpoints_trans'] += 1
    +                actions.append(
    +                    {"dict": "KPOINTS", "action": {"_set": {"kpoints": [[m] * 3]}}}
    +                )
    +                self.error_count["kpoints_trans"] += 1
     
             if "out_of_memory" in self.errors:
                 if vi["INCAR"].get("KPAR", 1) > 1:
                     reduced_kpar = max(vi["INCAR"].get("KPAR", 1) // 2, 1)
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"KPAR": reduced_kpar}}})
    +                actions.append(
    +                    {"dict": "INCAR", "action": {"_set": {"KPAR": reduced_kpar}}}
    +                )
     
             VaspModder(vi=vi).apply_actions(actions)
             return {"errors": list(self.errors), "actions": actions}
    @@ -583,13 +648,13 @@

    Source code for custodian.vasp.handlers

         is_monitor = True
     
         error_msgs = {
    -        "aliasing": [
    -            "WARNING: small aliasing (wrap around) errors must be expected"],
    -        "aliasing_incar": ["Your FFT grids (NGX,NGY,NGZ) are not sufficient "
    -                           "for an accurate"]
    +        "aliasing": ["WARNING: small aliasing (wrap around) errors must be expected"],
    +        "aliasing_incar": [
    +            "Your FFT grids (NGX,NGY,NGZ) are not sufficient " "for an accurate"
    +        ],
         }
     
    -    def __init__(self, output_filename="vasp.out"):
    +    def __init__(self, output_filename="vasp.out"):
             """
             Initializes the handler with the output file to check.
     
    @@ -615,7 +680,7 @@ 

    Source code for custodian.vasp.handlers

                                 # computation (e.g., defects) if yes we don't
                                 # want to kill it because there is a change in e-
                                 # density (brmix error)
    -                            if err == "brmix" and 'NELECT' in incar:
    +                            if err == "brmix" and "NELECT" in incar:
                                     continue
                                 self.errors.add(err)
             return len(self.errors) > 0
    @@ -636,31 +701,44 @@

    Source code for custodian.vasp.handlers

                             changes_dict[m.group(1)] = int(m.group(2))
                             grid_adjusted = True
                         # Ensure that all NGX, NGY, NGZ have been checked
    -                    if grid_adjusted and 'NGZ' in line:
    +                    if grid_adjusted and "NGZ" in line:
                             actions.append(
    -                            {"dict": "INCAR", "action": {"_set": changes_dict}})
    +                            {"dict": "INCAR", "action": {"_set": changes_dict}}
    +                        )
                             if vi["INCAR"].get("ICHARG", 0) < 10:
    -                            actions.extend([{"file": "CHGCAR",
    -                                             "action": {"_file_delete": {
    -                                                 'mode': "actual"}}},
    -                                            {"file": "WAVECAR",
    -                                             "action": {"_file_delete": {
    -                                                 'mode': "actual"}}}])
    +                            actions.extend(
    +                                [
    +                                    {
    +                                        "file": "CHGCAR",
    +                                        "action": {"_file_delete": {"mode": "actual"}},
    +                                    },
    +                                    {
    +                                        "file": "WAVECAR",
    +                                        "action": {"_file_delete": {"mode": "actual"}},
    +                                    },
    +                                ]
    +                            )
                             break
     
             if "aliasing_incar" in self.errors:
                 # vasp seems to give different warnings depending on whether the
                 # aliasing error was caused by user supplied inputs
    -            d = {k: 1 for k in ['NGX', 'NGY', 'NGZ'] if k in vi['INCAR'].keys()}
    +            d = {k: 1 for k in ["NGX", "NGY", "NGZ"] if k in vi["INCAR"].keys()}
                 actions.append({"dict": "INCAR", "action": {"_unset": d}})
     
                 if vi["INCAR"].get("ICHARG", 0) < 10:
    -                actions.extend([{"file": "CHGCAR",
    -                                 "action": {
    -                                     "_file_delete": {'mode': "actual"}}},
    -                                {"file": "WAVECAR",
    -                                 "action": {
    -                                     "_file_delete": {'mode': "actual"}}}])
    +                actions.extend(
    +                    [
    +                        {
    +                            "file": "CHGCAR",
    +                            "action": {"_file_delete": {"mode": "actual"}},
    +                        },
    +                        {
    +                            "file": "WAVECAR",
    +                            "action": {"_file_delete": {"mode": "actual"}},
    +                        },
    +                    ]
    +                )
     
             VaspModder(vi=vi).apply_actions(actions)
             return {"errors": list(self.errors), "actions": actions}
    @@ -671,7 +749,7 @@

    Source code for custodian.vasp.handlers

         Corrects for total drift exceeding the force convergence criteria.
         """
     
    -    def __init__(self, max_drift=None, to_average=3, enaug_multiply=2):
    +    def __init__(self, max_drift=None, to_average=3, enaug_multiply=2):
             """
             Initializes the handler with max drift
             Args:
    @@ -700,11 +778,11 @@ 

    Source code for custodian.vasp.handlers

                 # Can't perform check if Outcar not valid
                 return False
     
    -        if len(outcar.data.get('drift', [])) < self.to_average:
    +        if len(outcar.data.get("drift", [])) < self.to_average:
                 # Ensure enough steps to get average drift
                 return False
             else:
    -            curr_drift = outcar.data.get("drift", [])[::-1][:self.to_average]
    +            curr_drift = outcar.data.get("drift", [])[::-1][: self.to_average]
                 curr_drift = np.average([np.linalg.norm(d) for d in curr_drift])
                 return curr_drift > self.max_drift
    @@ -717,28 +795,42 @@

    Source code for custodian.vasp.handlers

             outcar = Outcar("OUTCAR")
     
             # Move CONTCAR to POSCAR
    -        actions.append({"file": "CONTCAR",
    -                        "action": {"_file_copy": {"dest": "POSCAR"}}})
    +        actions.append(
    +            {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}
    +        )
     
             # First try adding ADDGRID
             if not incar.get("ADDGRID", False):
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ADDGRID": True}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"ADDGRID": True}}})
             # Otherwise set PREC to High so ENAUG can be used to control Augmentation Grid Size
             elif incar.get("PREC", "Accurate").lower() != "high":
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"PREC": "High"}}})
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ENAUG": incar.get("ENCUT", 520) * 2}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"PREC": "High"}}})
    +            actions.append(
    +                {
    +                    "dict": "INCAR",
    +                    "action": {"_set": {"ENAUG": incar.get("ENCUT", 520) * 2}},
    +                }
    +            )
             # PREC is already high and ENAUG set so just increase it
             else:
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ENAUG": int(incar.get("ENAUG", 1040) * self.enaug_multiply)}}})
    -
    -        curr_drift = outcar.data.get("drift", [])[::-1][:self.to_average]
    +            actions.append(
    +                {
    +                    "dict": "INCAR",
    +                    "action": {
    +                        "_set": {
    +                            "ENAUG": int(incar.get("ENAUG", 1040) * self.enaug_multiply)
    +                        }
    +                    },
    +                }
    +            )
    +
    +        curr_drift = outcar.data.get("drift", [])[::-1][: self.to_average]
             curr_drift = np.average([np.linalg.norm(d) for d in curr_drift])
             VaspModder(vi=vi).apply_actions(actions)
    -        return {"errors": "Excessive drift {} > {}".format(curr_drift, self.max_drift), "actions": actions}
    + return { + "errors": "Excessive drift {} > {}".format(curr_drift, self.max_drift), + "actions": actions, + }
    [docs]class MeshSymmetryErrorHandler(ErrorHandler): @@ -747,10 +839,10 @@

    Source code for custodian.vasp.handlers

         non-fatal. So this error handler only checks at the end of the run,
         and if the run has converged, no error is recorded.
         """
    +
         is_monitor = False
     
    -    def __init__(self, output_filename="vasp.out",
    -                 output_vasprun="vasprun.xml"):
    +    def __init__(self, output_filename="vasp.out", output_vasprun="vasprun.xml"):
             """
             Initializes the handler with the output files to check.
     
    @@ -766,16 +858,21 @@ 

    Source code for custodian.vasp.handlers

             self.output_vasprun = output_vasprun
     
     
    [docs] def check(self): - msg = "Reciprocal lattice and k-lattice belong to different class of" \ - " lattices." + msg = ( + "Reciprocal lattice and k-lattice belong to different class of" " lattices." + ) + + vi = VaspInput.from_directory(".") + # disregard this error if KSPACING is set and no KPOINTS file is generated + if vi["INCAR"].get("KSPACING", False): + return False - vi = VaspInput.from_directory('.') # According to VASP admins, you can disregard this error # if symmetry is off # Also disregard if automatic KPOINT generation is used - if (not vi["INCAR"].get('ISYM', True)) or \ - vi[ - "KPOINTS"].style == Kpoints.supported_modes.Automatic: + if (not vi["INCAR"].get("ISYM", True)) or vi[ + "KPOINTS" + ].style == Kpoints.supported_modes.Automatic: return False try: @@ -798,8 +895,7 @@

    Source code for custodian.vasp.handlers

             m = max(int(round(m ** (1 / 3))), 1)
             if vi["KPOINTS"].style.name.lower().startswith("m"):
                 m += m % 2
    -        actions = [{"dict": "KPOINTS",
    -                    "action": {"_set": {"kpoints": [[m] * 3]}}}]
    +        actions = [{"dict": "KPOINTS", "action": {"_set": {"kpoints": [[m] * 3]}}}]
             VaspModder(vi=vi).apply_actions(actions)
             return {"errors": ["mesh_symmetry"], "actions": actions}
    @@ -808,9 +904,10 @@

    Source code for custodian.vasp.handlers

         """
         Check if a run is converged.
         """
    +
         is_monitor = False
     
    -    def __init__(self, output_filename="vasprun.xml"):
    +    def __init__(self, output_filename="vasprun.xml"):
             """
             Initializes the handler with the output file to check.
     
    @@ -838,34 +935,36 @@ 

    Source code for custodian.vasp.handlers

                 # expensive algorithms
                 algo = v.incar.get("ALGO", "Normal")
                 if algo == "VeryFast":
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ALGO": "Fast"}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Fast"}}})
                 elif algo == "Fast":
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ALGO": "Normal"}}})
    +                actions.append(
    +                    {"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}
    +                )
                 elif algo == "Normal":
    -                actions.append({"dict": "INCAR",
    -                                "action": {"_set": {"ALGO": "All"}}})
    +                actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "All"}}})
                 else:
                     # Try mixing as last resort
    -                new_settings = {"ISTART": 1,
    -                                "ALGO": "Normal",
    -                                "NELMDL": -6,
    -                                "BMIX": 0.001,
    -                                "AMIX_MAG": 0.8,
    -                                "BMIX_MAG": 0.001}
    -
    -                if not all([v.incar.get(k, "") == val for k, val in new_settings.items()]):
    -                    actions.append({"dict": "INCAR",
    -                                    "action": {"_set": new_settings}})
    +                new_settings = {
    +                    "ISTART": 1,
    +                    "ALGO": "Normal",
    +                    "NELMDL": -6,
    +                    "BMIX": 0.001,
    +                    "AMIX_MAG": 0.8,
    +                    "BMIX_MAG": 0.001,
    +                }
    +
    +                if not all(
    +                    [v.incar.get(k, "") == val for k, val in new_settings.items()]
    +                ):
    +                    actions.append({"dict": "INCAR", "action": {"_set": new_settings}})
     
             elif not v.converged_ionic:
                 # Just continue optimizing and let other handles fix ionic
                 # optimizer parameters
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"IBRION": 1}}})
    -            actions.append({"file": "CONTCAR",
    -                            "action": {"_file_copy": {"dest": "POSCAR"}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}})
    +            actions.append(
    +                {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}
    +            )
     
             if actions:
                 vi = VaspInput.from_directory(".")
    @@ -877,16 +976,20 @@ 

    Source code for custodian.vasp.handlers

                 return {"errors": ["Unconverged"], "actions": None}
    -
    [docs]class MaxForceErrorHandler(ErrorHandler): +@deprecated( + message="This handler is no longer supported and its use is no " + "longer recommended. It will be removed in v2020.x." +) +class MaxForceErrorHandler(ErrorHandler): """ Checks that the desired force convergence has been achieved. Otherwise restarts the run with smaller EDIFF. (This is necessary since energy and force convergence criteria cannot be set simultaneously) """ + is_monitor = False - def __init__(self, output_filename="vasprun.xml", - max_force_threshold=0.25): + def __init__(self, output_filename="vasprun.xml", max_force_threshold=0.25): """ Args: input_filename (str): name of the vasp INCAR file @@ -898,11 +1001,11 @@

    Source code for custodian.vasp.handlers

             self.output_filename = output_filename
             self.max_force_threshold = max_force_threshold
     
    -
    [docs] def check(self): + def check(self): try: v = Vasprun(self.output_filename) - forces = np.array(v.ionic_steps[-1]['forces']) - sdyn = v.final_structure.site_properties.get('selective_dynamics') + forces = np.array(v.ionic_steps[-1]["forces"]) + sdyn = v.final_structure.site_properties.get("selective_dynamics") if sdyn: forces[np.logical_not(sdyn)] = 0 max_force = max(np.linalg.norm(forces, axis=1)) @@ -910,20 +1013,20 @@

    Source code for custodian.vasp.handlers

                     return True
             except Exception:
                 pass
    -        return False
    + return False -
    [docs] def correct(self): + def correct(self): backup(VASP_BACKUP_FILES | {self.output_filename}) vi = VaspInput.from_directory(".") ediff = float(vi["INCAR"].get("EDIFF", 1e-4)) ediffg = float(vi["INCAR"].get("EDIFFG", ediff * 10)) - actions = [{"file": "CONTCAR", - "action": {"_file_copy": {"dest": "POSCAR"}}}, - {"dict": "INCAR", - "action": {"_set": {"EDIFFG": ediffg * 0.5}}}] + actions = [ + {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}, + {"dict": "INCAR", "action": {"_set": {"EDIFFG": ediffg * 0.5}}}, + ] VaspModder(vi=vi).apply_actions(actions) - return {"errors": ["MaxForce"], "actions": actions}
    + return {"errors": ["MaxForce"], "actions": actions}
    [docs]class PotimErrorHandler(ErrorHandler): @@ -933,10 +1036,12 @@

    Source code for custodian.vasp.handlers

         end up crashing with some other error (e.g. BRMIX) as the geometry
         gets progressively worse.
         """
    +
         is_monitor = True
     
    -    def __init__(self, input_filename="POSCAR", output_filename="OSZICAR",
    -                 dE_threshold=1):
    +    def __init__(
    +        self, input_filename="POSCAR", output_filename="OSZICAR", dE_threshold=1
    +    ):
             """
             Initializes the handler with the input and output files to check.
     
    @@ -956,7 +1061,7 @@ 

    Source code for custodian.vasp.handlers

             try:
                 oszicar = Oszicar(self.output_filename)
                 n = len(Poscar.from_file(self.input_filename).structure)
    -            max_dE = max([s['dE'] for s in oszicar.ionic_steps[1:]]) / n
    +            max_dE = max([s["dE"] for s in oszicar.ionic_steps[1:]]) / n
                 if max_dE > self.dE_threshold:
                     return True
             except Exception:
    @@ -968,15 +1073,13 @@ 

    Source code for custodian.vasp.handlers

             potim = float(vi["INCAR"].get("POTIM", 0.5))
             ibrion = int(vi["INCAR"].get("IBRION", 0))
             if potim < 0.2 and ibrion != 3:
    -            actions = [{"dict": "INCAR",
    -                        "action": {"_set": {"IBRION": 3,
    -                                            "SMASS": 0.75}}}]
    +            actions = [
    +                {"dict": "INCAR", "action": {"_set": {"IBRION": 3, "SMASS": 0.75}}}
    +            ]
             elif potim < 0.1:
    -            actions = [{"dict": "INCAR",
    -                        "action": {"_set": {"SYMPREC": 1e-8}}}]
    +            actions = [{"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}]
             else:
    -            actions = [{"dict": "INCAR",
    -                        "action": {"_set": {"POTIM": potim * 0.5}}}]
    +            actions = [{"dict": "INCAR", "action": {"_set": {"POTIM": potim * 0.5}}}]
     
             VaspModder(vi=vi).apply_actions(actions)
             return {"errors": ["POTIM"], "actions": actions}
    @@ -990,7 +1093,7 @@

    Source code for custodian.vasp.handlers

     
         is_monitor = True
     
    -    def __init__(self, output_filename="vasp.out", timeout=21600):
    +    def __init__(self, output_filename="vasp.out", timeout=21600):
             """
             Initializes the handler with the output file to check.
     
    @@ -1014,14 +1117,12 @@ 

    Source code for custodian.vasp.handlers

     
    [docs] def correct(self): backup(VASP_BACKUP_FILES | {self.output_filename}) - vi = VaspInput.from_directory('.') + vi = VaspInput.from_directory(".") actions = [] if vi["INCAR"].get("ALGO", "Normal") == "Fast": - actions.append({"dict": "INCAR", - "action": {"_set": {"ALGO": "Normal"}}}) + actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) else: - actions.append({"dict": "INCAR", - "action": {"_set": {"SYMPREC": 1e-8}}}) + actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) VaspModder(vi=vi).apply_actions(actions) @@ -1034,9 +1135,10 @@

    Source code for custodian.vasp.handlers

         last nionic_steps ionic steps (default=10). If so, change ALGO from Fast to
         Normal or kill the job.
         """
    +
         is_monitor = True
     
    -    def __init__(self, output_filename="OSZICAR", nionic_steps=10):
    +    def __init__(self, output_filename="OSZICAR", nionic_steps=10):
             """
             Initializes the handler with the output file to check.
     
    @@ -1057,8 +1159,9 @@ 

    Source code for custodian.vasp.handlers

                 oszicar = Oszicar(self.output_filename)
                 esteps = oszicar.electronic_steps
                 if len(esteps) > self.nionic_steps:
    -                return all([len(e) == nelm
    -                            for e in esteps[-(self.nionic_steps + 1):-1]])
    +                return all(
    +                    [len(e) == nelm for e in esteps[-(self.nionic_steps + 1): -1]]
    +                )
             except Exception:
                 pass
             return False
    @@ -1074,24 +1177,27 @@

    Source code for custodian.vasp.handlers

             # These progressively switches to more stable but more
             # expensive algorithms
             if algo == "VeryFast":
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ALGO": "Fast"}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Fast"}}})
             elif algo == "Fast":
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ALGO": "Normal"}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}})
             elif algo == "Normal":
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"ALGO": "All"}}})
    +            actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "All"}}})
             elif amix > 0.1 and bmix > 0.01:
                 # Try linear mixing
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"AMIX": 0.1, "BMIX": 0.01,
    -                                                "ICHARG": 2}}})
    +            actions.append(
    +                {
    +                    "dict": "INCAR",
    +                    "action": {"_set": {"AMIX": 0.1, "BMIX": 0.01, "ICHARG": 2}},
    +                }
    +            )
             elif bmix < 3.0 and amin > 0.01:
                 # Try increasing bmix
    -            actions.append({"dict": "INCAR",
    -                            "action": {"_set": {"AMIN": 0.01, "BMIX": 3.0,
    -                                                "ICHARG": 2}}})
    +            actions.append(
    +                {
    +                    "dict": "INCAR",
    +                    "action": {"_set": {"AMIN": 0.01, "BMIX": 3.0, "ICHARG": 2}},
    +                }
    +            )
     
             if actions:
                 backup(VASP_BACKUP_FILES)
    @@ -1099,7 +1205,20 @@ 

    Source code for custodian.vasp.handlers

                 return {"errors": ["Non-converging job"], "actions": actions}
             # Unfixable error. Just return None for actions.
             else:
    -            return {"errors": ["Non-converging job"], "actions": None}
    + return {"errors": ["Non-converging job"], "actions": None}
    + +
    [docs] @classmethod + def from_dict(cls, d): + """ + Custom from_dict method to preserve backwards compatibility with + older versions of Custodian. + """ + if "change_algo" in d: + del d["change_algo"] + return cls( + output_filename=d.get("output_filename", "OSZICAR"), + nionic_steps=d.get("nionic_steps", 10), + )
    [docs]class WalltimeHandler(ErrorHandler): @@ -1110,6 +1229,7 @@

    Source code for custodian.vasp.handlers

         to be running on a PBS system and the PBS_WALLTIME variable is in the run
         environment, the wall time will be automatically determined if not set.
         """
    +
         is_monitor = True
     
         # The WalltimeHandler should not terminate as we want VASP to terminate
    @@ -1120,8 +1240,7 @@ 

    Source code for custodian.vasp.handlers

         # error
         raises_runtime_error = False
     
    -    def __init__(self, wall_time=None, buffer_time=300,
    -                 electronic_step_stop=False):
    +    def __init__(self, wall_time=None, buffer_time=300, electronic_step_stop=False):
             """
             Initializes the handler with a buffer time.
     
    @@ -1162,11 +1281,13 @@ 

    Source code for custodian.vasp.handlers

             # set manually be the user in the batch environment.
             if "CUSTODIAN_WALLTIME_START" in os.environ:
                 self.start_time = datetime.datetime.strptime(
    -                os.environ["CUSTODIAN_WALLTIME_START"], "%a %b %d %H:%M:%S %Z %Y")
    +                os.environ["CUSTODIAN_WALLTIME_START"], "%a %b %d %H:%M:%S %Z %Y"
    +            )
             else:
                 self.start_time = datetime.datetime.now()
                 os.environ["CUSTODIAN_WALLTIME_START"] = datetime.datetime.strftime(
    -                self.start_time, "%a %b %d %H:%M:%S UTC %Y")
    +                self.start_time, "%a %b %d %H:%M:%S UTC %Y"
    +            )
     
             self.electronic_step_stop = electronic_step_stop
             self.electronic_steps_timings = [0]
    @@ -1179,14 +1300,24 @@ 

    Source code for custodian.vasp.handlers

                 outcar = Outcar("OUTCAR")
                 if not self.electronic_step_stop:
                     # Determine max time per ionic step.
    -                outcar.read_pattern({"timings": "LOOP\+.+real time(.+)"},
    -                                    postprocess=float)
    -                time_per_step = np.max(outcar.data.get('timings')) if outcar.data.get("timings", []) else 0
    +                outcar.read_pattern(
    +                    {"timings": "LOOP\+.+real time(.+)"}, postprocess=float
    +                )
    +                time_per_step = (
    +                    np.max(outcar.data.get("timings"))
    +                    if outcar.data.get("timings", [])
    +                    else 0
    +                )
                 else:
                     # Determine max time per electronic step.
    -                outcar.read_pattern({"timings": "LOOP:.+real time(.+)"},
    -                                    postprocess=float)
    -                time_per_step = np.max(outcar.data.get('timings')) if outcar.data.get("timings", []) else 0
    +                outcar.read_pattern(
    +                    {"timings": "LOOP:.+real time(.+)"}, postprocess=float
    +                )
    +                time_per_step = (
    +                    np.max(outcar.data.get("timings"))
    +                    if outcar.data.get("timings", [])
    +                    else 0
    +                )
     
                 # If the remaining time is less than average time for 3
                 # steps or buffer_time.
    @@ -1198,11 +1329,13 @@ 

    Source code for custodian.vasp.handlers

     
     
    [docs] def correct(self): - content = "LSTOP = .TRUE." if not self.electronic_step_stop else \ - "LABORT = .TRUE." + content = ( + "LSTOP = .TRUE." if not self.electronic_step_stop else "LABORT = .TRUE." + ) # Write STOPCAR - actions = [{"file": "STOPCAR", - "action": {"_file_create": {'content': content}}}] + actions = [ + {"file": "STOPCAR", "action": {"_file_create": {"content": content}}} + ] m = Modder(actions=[FileActions]) for a in actions: @@ -1212,8 +1345,7 @@

    Source code for custodian.vasp.handlers

     
     @deprecated(replacement=WalltimeHandler)
     class PBSWalltimeHandler(WalltimeHandler):
    -
    -    def __init__(self, buffer_time=300):
    +    def __init__(self, buffer_time=300):
             super(PBSWalltimeHandler, self).__init__(None, buffer_time=buffer_time)
     
     
    @@ -1228,13 +1360,14 @@ 

    Source code for custodian.vasp.handlers

         checkpoint will be stored in subdirs chk_#. This should be used in
         combiantion with the StoppedRunHandler.
         """
    +
         is_monitor = True
     
         # The CheckpointHandler should not terminate as we want VASP to terminate
         # itself naturally with the STOPCAR.
         is_terminating = False
     
    -    def __init__(self, interval=3600):
    +    def __init__(self, interval=3600):
             """
             Initializes the handler with an interval.
     
    @@ -1255,15 +1388,20 @@ 

    Source code for custodian.vasp.handlers

     
     
    [docs] def correct(self): content = "LSTOP = .TRUE." - chkpt_content = "Index: %d\nTime: \"%s\"" % (self.chk_counter, - datetime.datetime.now()) + chkpt_content = 'Index: %d\nTime: "%s"' % ( + self.chk_counter, + datetime.datetime.now(), + ) self.chk_counter += 1 # Write STOPCAR - actions = [{"file": "STOPCAR", - "action": {"_file_create": {'content': content}}}, - {"file": "chkpt.yaml", - "action": {"_file_create": {'content': chkpt_content}}}] + actions = [ + {"file": "STOPCAR", "action": {"_file_create": {"content": content}}}, + { + "file": "chkpt.yaml", + "action": {"_file_create": {"content": chkpt_content}}, + }, + ] m = Modder(actions=[FileActions]) for a in actions: @@ -1274,7 +1412,7 @@

    Source code for custodian.vasp.handlers

     
             return {"errors": ["Checkpoint reached"], "actions": actions}
    - def __str__(self): + def __str__(self): return "CheckpointHandler with interval %d" % self.interval
    @@ -1289,13 +1427,14 @@

    Source code for custodian.vasp.handlers

         stored in subdirs chk_#. This should be used in combination with the
         StoppedRunHandler.
         """
    +
         is_monitor = False
     
         # The CheckpointHandler should not terminate as we want VASP to terminate
         # itself naturally with the STOPCAR.
         is_terminating = False
     
    -    def __init__(self):
    +    def __init__(self):
             pass
     
     
    [docs] def check(self): @@ -1305,10 +1444,10 @@

    Source code for custodian.vasp.handlers

             d = loadfn("chkpt.yaml")
             i = d["Index"]
             name = shutil.make_archive(
    -            os.path.join(os.getcwd(), "vasp.chk.%d" % i), "gztar")
    +            os.path.join(os.getcwd(), "vasp.chk.%d" % i), "gztar"
    +        )
     
    -        actions = [{"file": "CONTCAR",
    -                    "action": {"_file_copy": {"dest": "POSCAR"}}}]
    +        actions = [{"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}]
     
             m = Modder(actions=[FileActions])
             for a in actions:
    @@ -1316,8 +1455,7 @@ 

    Source code for custodian.vasp.handlers

     
             actions.append({"Checkpoint": name})
     
    -        return {"errors": ["Stopped run."],
    -                "actions": actions}
    + return {"errors": ["Stopped run."], "actions": actions}
    [docs]class PositiveEnergyErrorHandler(ErrorHandler): @@ -1325,9 +1463,10 @@

    Source code for custodian.vasp.handlers

         Check if a run has positive absolute energy.
         If so, change ALGO from Fast to Normal or kill the job.
         """
    +
         is_monitor = True
     
    -    def __init__(self, output_filename="OSZICAR"):
    +    def __init__(self, output_filename="OSZICAR"):
             """
             Initializes the handler with the output file to check.
     
    @@ -1350,16 +1489,14 @@ 

    Source code for custodian.vasp.handlers

             # change ALGO = Fast to Normal if ALGO is !Normal
             vi = VaspInput.from_directory(".")
             algo = vi["INCAR"].get("ALGO", "Normal")
    -        if algo.lower() not in ['normal', 'n']:
    +        if algo.lower() not in ["normal", "n"]:
                 backup(VASP_BACKUP_FILES)
    -            actions = [{"dict": "INCAR",
    -                        "action": {"_set": {"ALGO": "Normal"}}}]
    +            actions = [{"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}]
                 VaspModder(vi=vi).apply_actions(actions)
                 return {"errors": ["Positive energy"], "actions": actions}
             elif algo == "Normal":
                 potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0
    -            actions = [{"dict": "INCAR",
    -                        "action": {"_set": {"POTIM": potim}}}]
    +            actions = [{"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}]
                 VaspModder(vi=vi).apply_actions(actions)
                 return {"errors": ["Positive energy"], "actions": actions}
             # Unfixable error. Just return None for actions.
    @@ -1387,14 +1524,14 @@ 

    Quick search

    - +
    @@ -50,15 +49,15 @@

    Navigation

    Source code for custodian.vasp.interpreter

     # coding: utf-8
     
    -from __future__ import unicode_literals
    +from __future__ import unicode_literals
     
    -from custodian.ansible.actions import FileActions, DictActions
    -from custodian.ansible.interpreter import Modder
    -from pymatgen.io.vasp import VaspInput
    +from custodian.ansible.actions import FileActions, DictActions
    +from custodian.ansible.interpreter import Modder
    +from pymatgen.io.vasp.inputs import VaspInput
     
     
     
    [docs]class VaspModder(Modder): - def __init__(self, actions=None, strict=True, vi=None): + def __init__(self, actions=None, strict=True, vi=None): """ Initializes a Modder for VaspInput sets @@ -74,7 +73,7 @@

    Source code for custodian.vasp.interpreter

                     Initialized automatically if not passed (but passing it will
                     avoid having to reparse the directory).
             """
    -        self.vi = vi or VaspInput.from_directory('.')
    +        self.vi = vi or VaspInput.from_directory(".")
             actions = actions or [FileActions, DictActions]
             super(VaspModder, self).__init__(actions, strict)
     
    @@ -121,14 +120,14 @@ 

    Quick search

    - +
    @@ -50,7 +49,7 @@

    Navigation

    Source code for custodian.vasp.jobs

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     import subprocess
     import os
     import shutil
    @@ -59,16 +58,17 @@ 

    Source code for custodian.vasp.jobs

     
     import numpy as np
     
    -from pymatgen import Structure
    -from pymatgen.io.vasp import VaspInput, Incar, Poscar, Outcar, Kpoints, Vasprun
    -from monty.os.path import which
    -from monty.shutil import decompress_dir
    -from monty.serialization import dumpfn, loadfn
    +from pymatgen import Structure
    +from pymatgen.io.vasp.inputs import VaspInput, Incar, Poscar, Kpoints
    +from pymatgen.io.vasp.outputs import Vasprun, Outcar
    +from monty.os.path import which
    +from monty.shutil import decompress_dir
    +from monty.serialization import dumpfn, loadfn
     
    -from custodian.custodian import Job, SENTRY_DSN
    -from custodian.utils import backup
    -from custodian.vasp.interpreter import VaspModder
    -from custodian.vasp.handlers import VASP_BACKUP_FILES
    +from custodian.custodian import Job, SENTRY_DSN
    +from custodian.utils import backup
    +from custodian.vasp.interpreter import VaspModder
    +from custodian.vasp.handlers import VASP_BACKUP_FILES
     
     """
     This module implements basic kinds of jobs for VASP runs.
    @@ -88,18 +88,43 @@ 

    Source code for custodian.vasp.jobs

     
     VASP_INPUT_FILES = {"INCAR", "POSCAR", "POTCAR", "KPOINTS"}
     
    -VASP_OUTPUT_FILES = ['DOSCAR', 'INCAR', 'KPOINTS', 'POSCAR', 'PROCAR',
    -                     'vasprun.xml', 'CHGCAR', 'CHG', 'EIGENVAL', 'OSZICAR',
    -                     'WAVECAR', 'CONTCAR', 'IBZKPT', 'OUTCAR']
    -
    -VASP_NEB_INPUT_FILES = {'INCAR', 'POTCAR', 'KPOINTS'}
    -
    -VASP_NEB_OUTPUT_FILES = ['INCAR', 'KPOINTS', 'POTCAR', 'vasprun.xml']
    -
    -VASP_NEB_OUTPUT_SUB_FILES = ['CHG', 'CHGCAR', 'CONTCAR', 'DOSCAR',
    -                             'EIGENVAL', 'IBZKPT', 'PCDAT', 'POSCAR',
    -                             'REPORT', 'PROCAR', 'OSZICAR', 'OUTCAR',
    -                             'WAVECAR', 'XDATCAR']
    +VASP_OUTPUT_FILES = [
    +    "DOSCAR",
    +    "INCAR",
    +    "KPOINTS",
    +    "POSCAR",
    +    "PROCAR",
    +    "vasprun.xml",
    +    "CHGCAR",
    +    "CHG",
    +    "EIGENVAL",
    +    "OSZICAR",
    +    "WAVECAR",
    +    "CONTCAR",
    +    "IBZKPT",
    +    "OUTCAR",
    +]
    +
    +VASP_NEB_INPUT_FILES = {"INCAR", "POTCAR", "KPOINTS"}
    +
    +VASP_NEB_OUTPUT_FILES = ["INCAR", "KPOINTS", "POTCAR", "vasprun.xml"]
    +
    +VASP_NEB_OUTPUT_SUB_FILES = [
    +    "CHG",
    +    "CHGCAR",
    +    "CONTCAR",
    +    "DOSCAR",
    +    "EIGENVAL",
    +    "IBZKPT",
    +    "PCDAT",
    +    "POSCAR",
    +    "REPORT",
    +    "PROCAR",
    +    "OSZICAR",
    +    "OUTCAR",
    +    "WAVECAR",
    +    "XDATCAR",
    +]
     
     
     
    [docs]class VaspJob(Job): @@ -108,11 +133,21 @@

    Source code for custodian.vasp.jobs

         can be a complex processing of inputs etc. with initialization.
         """
     
    -    def __init__(self, vasp_cmd, output_file="vasp.out",
    -                 stderr_file="std_err.txt", suffix="", final=True,
    -                 backup=True, auto_npar=False, auto_gamma=True,
    -                 settings_override=None, gamma_vasp_cmd=None,
    -                 copy_magmom=False, auto_continue=False):
    +    def __init__(
    +        self,
    +        vasp_cmd,
    +        output_file="vasp.out",
    +        stderr_file="std_err.txt",
    +        suffix="",
    +        final=True,
    +        backup=True,
    +        auto_npar=False,
    +        auto_gamma=True,
    +        settings_override=None,
    +        gamma_vasp_cmd=None,
    +        copy_magmom=False,
    +        auto_continue=False,
    +    ):
             """
             This constructor is necessarily complex due to the need for
             flexibility. For standard kinds of runs, it's often better to use one
    @@ -180,17 +215,20 @@ 

    Source code for custodian.vasp.jobs

     
             if SENTRY_DSN:
                 # if using Sentry logging, add specific VASP executable to scope
    -            from sentry_sdk import configure_scope
    +            from sentry_sdk import configure_scope
    +
                 with configure_scope() as scope:
                     try:
                         if isinstance(vasp_cmd, str):
    -                        vasp_path = which(vasp_cmd.split(' ')[-1])
    +                        vasp_path = which(vasp_cmd.split(" ")[-1])
                         elif isinstance(vasp_cmd, list):
                             vasp_path = which(vasp_cmd[-1])
                         scope.set_tag("vasp_path", vasp_path)
                         scope.set_tag("vasp_cmd", vasp_cmd)
                     except Exception:
    -                    logger.error("Failed to detect VASP path: {}".format(vasp_cmd), exc_info=True)
    +                    logger.error(
    +                        "Failed to detect VASP path: {}".format(vasp_cmd), exc_info=True
    +                    )
                         scope.set_tag("vasp_cmd", vasp_cmd)
     
     
    [docs] def setup(self): @@ -198,32 +236,36 @@

    Source code for custodian.vasp.jobs

             Performs initial setup for VaspJob, including overriding any settings
             and backing up.
             """
    -        decompress_dir('.')
    +        decompress_dir(".")
     
             if self.backup:
                 for f in VASP_INPUT_FILES:
    -                shutil.copy(f, "{}.orig".format(f))
    +                try:
    +                    shutil.copy(f, "{}.orig".format(f))
    +                except FileNotFoundError:  # handle the situation when there is no KPOINTS file
    +                    if f == "KPOINTS":
    +                        pass
     
             if self.auto_npar:
                 try:
                     incar = Incar.from_file("INCAR")
                     # Only optimized NPAR for non-HF and non-RPA calculations.
    -                if not (incar.get("LHFCALC") or incar.get("LRPA") or
    -                        incar.get("LEPSILON")):
    +                if not (
    +                    incar.get("LHFCALC") or incar.get("LRPA") or incar.get("LEPSILON")
    +                ):
                         if incar.get("IBRION") in [5, 6, 7, 8]:
                             # NPAR should not be set for Hessian matrix
                             # calculations, whether in DFPT or otherwise.
                             del incar["NPAR"]
                         else:
                             import multiprocessing
    +
                             # try sge environment variable first
                             # (since multiprocessing counts cores on the current
                             # machine only)
    -                        ncores = os.environ.get('NSLOTS') or \
    -                            multiprocessing.cpu_count()
    +                        ncores = os.environ.get("NSLOTS") or multiprocessing.cpu_count()
                             ncores = int(ncores)
    -                        for npar in range(int(math.sqrt(ncores)),
    -                                          ncores):
    +                        for npar in range(int(math.sqrt(ncores)), ncores):
                                 if ncores % npar == 0:
                                     incar["NPAR"] = npar
                                     break
    @@ -242,10 +284,13 @@ 

    Source code for custodian.vasp.jobs

                     # Default functionality is to copy CONTCAR to POSCAR and set
                     # ISTART to 1 in the INCAR, but other actions can be specified
                     if self.auto_continue is True:
    -                    actions = [{"file": "CONTCAR",
    -                                "action": {"_file_copy": {"dest": "POSCAR"}}},
    -                               {"dict": "INCAR",
    -                                "action": {"_set": {"ISTART": 1}}}]
    +                    actions = [
    +                        {
    +                            "file": "CONTCAR",
    +                            "action": {"_file_copy": {"dest": "POSCAR"}},
    +                        },
    +                        {"dict": "INCAR", "action": {"_set": {"ISTART": 1}}},
    +                    ]
                     else:
                         actions = self.auto_continue
                     dumpfn({"actions": actions}, "continue.json")
    @@ -264,16 +309,20 @@ 

    Source code for custodian.vasp.jobs

             if self.auto_gamma:
                 vi = VaspInput.from_directory(".")
                 kpts = vi["KPOINTS"]
    -            if kpts.style == Kpoints.supported_modes.Gamma \
    -                    and tuple(kpts.kpts[0]) == (1, 1, 1):
    -                if self.gamma_vasp_cmd is not None and which(
    -                        self.gamma_vasp_cmd[-1]):
    -                    cmd = self.gamma_vasp_cmd
    -                elif which(cmd[-1] + ".gamma"):
    -                    cmd[-1] += ".gamma"
    +            if kpts is not None:
    +                if kpts.style == Kpoints.supported_modes.Gamma and tuple(
    +                    kpts.kpts[0]
    +                ) == (1, 1, 1):
    +                    if self.gamma_vasp_cmd is not None and which(
    +                        self.gamma_vasp_cmd[-1]
    +                    ):
    +                        cmd = self.gamma_vasp_cmd
    +                    elif which(cmd[-1] + ".gamma"):
    +                        cmd[-1] += ".gamma"
             logger.info("Running {}".format(" ".join(cmd)))
    -        with open(self.output_file, 'w') as f_std, \
    -                open(self.stderr_file, "w", buffering=1) as f_err:
    +        with open(self.output_file, "w") as f_std, open(
    +            self.stderr_file, "w", buffering=1
    +        ) as f_err:
                 # use line buffering for stderr
                 p = subprocess.Popen(cmd, stdout=f_std, stderr=f_err)
             return p
    @@ -293,12 +342,12 @@

    Source code for custodian.vasp.jobs

             if self.copy_magmom and not self.final:
                 try:
                     outcar = Outcar("OUTCAR")
    -                magmom = [m['tot'] for m in outcar.magnetization]
    +                magmom = [m["tot"] for m in outcar.magnetization]
                     incar = Incar.from_file("INCAR")
    -                incar['MAGMOM'] = magmom
    +                incar["MAGMOM"] = magmom
                     incar.write_file("INCAR")
                 except Exception:
    -                logger.error('MAGMOM copy from OUTCAR to INCAR failed')
    +                logger.error("MAGMOM copy from OUTCAR to INCAR failed")
     
             # Remove continuation so if a subsequent job is run in
             # the same directory, will not restart this job.
    @@ -306,8 +355,14 @@ 

    Source code for custodian.vasp.jobs

                 os.remove("continue.json")
    [docs] @classmethod - def double_relaxation_run(cls, vasp_cmd, auto_npar=True, ediffg=-0.05, - half_kpts_first_relax=False, auto_continue=False): + def double_relaxation_run( + cls, + vasp_cmd, + auto_npar=True, + ediffg=-0.05, + half_kpts_first_relax=False, + auto_continue=False, + ): """ Returns a list of two jobs corresponding to an AFLOW style double relaxation run. @@ -334,37 +389,57 @@

    Source code for custodian.vasp.jobs

                 incar_update["EDIFFG"] = ediffg
             settings_overide_1 = None
             settings_overide_2 = [
    -            {"dict": "INCAR",
    -             "action": {"_set": incar_update}},
    -            {"file": "CONTCAR",
    -             "action": {"_file_copy": {"dest": "POSCAR"}}}]
    -        if half_kpts_first_relax and os.path.exists("KPOINTS") and \
    -                os.path.exists("POSCAR"):
    +            {"dict": "INCAR", "action": {"_set": incar_update}},
    +            {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}},
    +        ]
    +        if (
    +            half_kpts_first_relax
    +            and os.path.exists("KPOINTS")
    +            and os.path.exists("POSCAR")
    +        ):
                 kpts = Kpoints.from_file("KPOINTS")
                 orig_kpts_dict = kpts.as_dict()
                 # lattice vectors with length < 8 will get >1 KPOINT
    -            kpts.kpts = np.round(np.maximum(np.array(kpts.kpts) / 2,
    -                                            1)).astype(int).tolist()
    +            kpts.kpts = (
    +                np.round(np.maximum(np.array(kpts.kpts) / 2, 1)).astype(int).tolist()
    +            )
                 low_kpts_dict = kpts.as_dict()
                 settings_overide_1 = [
    -                {"dict": "KPOINTS",
    -                 "action": {"_set": low_kpts_dict}}
    +                {"dict": "KPOINTS", "action": {"_set": low_kpts_dict}}
                 ]
                 settings_overide_2.append(
    -                {"dict": "KPOINTS",
    -                 "action": {"_set": orig_kpts_dict}}
    +                {"dict": "KPOINTS", "action": {"_set": orig_kpts_dict}}
                 )
     
    -        return [VaspJob(vasp_cmd, final=False, suffix=".relax1",
    -                        auto_npar=auto_npar, auto_continue=auto_continue,
    -                        settings_override=settings_overide_1),
    -                VaspJob(vasp_cmd, final=True, backup=False, suffix=".relax2",
    -                        auto_npar=auto_npar, auto_continue=auto_continue,
    -                        settings_override=settings_overide_2)]
    + return [ + VaspJob( + vasp_cmd, + final=False, + suffix=".relax1", + auto_npar=auto_npar, + auto_continue=auto_continue, + settings_override=settings_overide_1, + ), + VaspJob( + vasp_cmd, + final=True, + backup=False, + suffix=".relax2", + auto_npar=auto_npar, + auto_continue=auto_continue, + settings_override=settings_overide_2, + ), + ]
    [docs] @classmethod - def metagga_opt_run(cls, vasp_cmd, auto_npar=True, ediffg=-0.05, - half_kpts_first_relax=False, auto_continue=False): + def metagga_opt_run( + cls, + vasp_cmd, + auto_npar=True, + ediffg=-0.05, + half_kpts_first_relax=False, + auto_continue=False, + ): """ Returns a list of thres jobs to perform an optimization for any metaGGA functional. There is an initial calculation of the @@ -378,29 +453,50 @@

    Source code for custodian.vasp.jobs

             metaGGA = incar.get("METAGGA", "SCAN")
     
             # Pre optimze WAVECAR and structure using regular GGA
    -        pre_opt_setings = [{"dict": "INCAR",
    -                            "action": {"_set": {"METAGGA": None,
    -                                                "LWAVE": True,
    -                                                "NSW": 0}}}]
    -        jobs = [VaspJob(vasp_cmd, auto_npar=auto_npar,
    -                        final=False, suffix=".precondition",
    -                        settings_override=pre_opt_setings)]
    +        pre_opt_setings = [
    +            {
    +                "dict": "INCAR",
    +                "action": {"_set": {"METAGGA": None, "LWAVE": True, "NSW": 0}},
    +            }
    +        ]
    +        jobs = [
    +            VaspJob(
    +                vasp_cmd,
    +                auto_npar=auto_npar,
    +                final=False,
    +                suffix=".precondition",
    +                settings_override=pre_opt_setings,
    +            )
    +        ]
     
             # Finish with regular double relaxation style run using SCAN
    -        jobs.extend(VaspJob.double_relaxation_run(vasp_cmd, auto_npar=auto_npar,
    -                                                  ediffg=ediffg,
    -                                                  half_kpts_first_relax=half_kpts_first_relax))
    +        jobs.extend(
    +            VaspJob.double_relaxation_run(
    +                vasp_cmd,
    +                auto_npar=auto_npar,
    +                ediffg=ediffg,
    +                half_kpts_first_relax=half_kpts_first_relax,
    +            )
    +        )
     
             # Ensure the first relaxation doesn't overwrite the original inputs
             jobs[1].backup = False
     
             # Update double_relaxation job to start from pre-optimized run
    -        post_opt_settings = [{"dict": "INCAR",
    -                              "action": {"_set": {"METAGGA": metaGGA, "ISTART": 1,
    -                                                  "NSW": incar.get("NSW", 99),
    -                                                  "LWAVE": incar.get("LWAVE", False)}}},
    -                             {"file": "CONTCAR",
    -                              "action": {"_file_copy": {"dest": "POSCAR"}}}]
    +        post_opt_settings = [
    +            {
    +                "dict": "INCAR",
    +                "action": {
    +                    "_set": {
    +                        "METAGGA": metaGGA,
    +                        "ISTART": 1,
    +                        "NSW": incar.get("NSW", 99),
    +                        "LWAVE": incar.get("LWAVE", False),
    +                    }
    +                },
    +            },
    +            {"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}},
    +        ]
             if jobs[1].settings_override:
                 post_opt_settings = jobs[1].settings_override + post_opt_settings
             jobs[1].settings_override = post_opt_settings
    @@ -408,9 +504,15 @@ 

    Source code for custodian.vasp.jobs

             return jobs
    [docs] @classmethod - def full_opt_run(cls, vasp_cmd, vol_change_tol=0.02, - max_steps=10, ediffg=-0.05, half_kpts_first_relax=False, - **vasp_job_kwargs): + def full_opt_run( + cls, + vasp_cmd, + vol_change_tol=0.02, + max_steps=10, + ediffg=-0.05, + half_kpts_first_relax=False, + **vasp_job_kwargs + ): """ Returns a generator of jobs for a full optimization run. Basically, this runs an infinite series of geometry optimization jobs until the @@ -439,16 +541,16 @@

    Source code for custodian.vasp.jobs

                 if i == 0:
                     settings = None
                     backup = True
    -                if half_kpts_first_relax and os.path.exists("KPOINTS") and \
    -                        os.path.exists("POSCAR"):
    +                if (
    +                    half_kpts_first_relax
    +                    and os.path.exists("KPOINTS")
    +                    and os.path.exists("POSCAR")
    +                ):
                         kpts = Kpoints.from_file("KPOINTS")
                         orig_kpts_dict = kpts.as_dict()
                         kpts.kpts = np.maximum(np.array(kpts.kpts) / 2, 1).tolist()
                         low_kpts_dict = kpts.as_dict()
    -                    settings = [
    -                        {"dict": "KPOINTS",
    -                         "action": {"_set": low_kpts_dict}}
    -                    ]
    +                    settings = [{"dict": "KPOINTS", "action": {"_set": low_kpts_dict}}]
                 else:
                     backup = False
                     initial = Poscar.from_file("POSCAR").structure
    @@ -464,22 +566,37 @@ 

    Source code for custodian.vasp.jobs

                         if ediffg:
                             incar_update["EDIFFG"] = ediffg
                         settings = [
    -                        {"dict": "INCAR",
    -                         "action": {"_set": incar_update}},
    -                        {"file": "CONTCAR",
    -                         "action": {"_file_copy": {"dest": "POSCAR"}}}]
    +                        {"dict": "INCAR", "action": {"_set": incar_update}},
    +                        {
    +                            "file": "CONTCAR",
    +                            "action": {"_file_copy": {"dest": "POSCAR"}},
    +                        },
    +                    ]
                         if i == 1 and half_kpts_first_relax:
    -                        settings.append({"dict": "KPOINTS",
    -                                         "action": {"_set": orig_kpts_dict}})
    -            logger.info("Generating job = %d!" % (i+1))
    -            yield VaspJob(vasp_cmd, final=False, backup=backup,
    -                          suffix=".relax%d" % (i+1), settings_override=settings,
    -                          **vasp_job_kwargs)
    + settings.append( + {"dict": "KPOINTS", "action": {"_set": orig_kpts_dict}} + ) + logger.info("Generating job = %d!" % (i + 1)) + yield VaspJob( + vasp_cmd, + final=False, + backup=backup, + suffix=".relax%d" % (i + 1), + settings_override=settings, + **vasp_job_kwargs + )
    [docs] @classmethod - def constrained_opt_run(cls, vasp_cmd, lattice_direction, initial_strain, - atom_relax=True, max_steps=20, algo="bfgs", - **vasp_job_kwargs): + def constrained_opt_run( + cls, + vasp_cmd, + lattice_direction, + initial_strain, + atom_relax=True, + max_steps=20, + algo="bfgs", + **vasp_job_kwargs + ): """ Returns a generator of jobs for a constrained optimization run. Typical use case is when you want to approximate a biaxial strain situation, @@ -544,8 +661,8 @@

    Source code for custodian.vasp.jobs

             for i in range(max_steps):
                 if i == 0:
                     settings = [
    -                        {"dict": "INCAR",
    -                         "action": {"_set": {"ISIF": 2, "NSW": nsw}}}]
    +                    {"dict": "INCAR", "action": {"_set": {"ISIF": 2, "NSW": nsw}}}
    +                ]
                     structure = Poscar.from_file("POSCAR").structure
                     x = structure.lattice.abc[lattice_index]
                     backup = True
    @@ -561,7 +678,7 @@ 

    Source code for custodian.vasp.jobs

                     energies[x] = energy
     
                     if i == 1:
    -                    x *= (1 + initial_strain)
    +                    x *= 1 + initial_strain
                     else:
                         # Sort the lattice parameter by energies.
                         min_x = min(energies.keys(), key=lambda e: energies[e])
    @@ -572,14 +689,16 @@ 

    Source code for custodian.vasp.jobs

                         elif ind == len(sorted_x) - 1:
                             other = ind - 1
                         else:
    -                        other = ind + 1 \
    -                            if energies[sorted_x[ind + 1]] \
    -                            < energies[sorted_x[ind - 1]] \
    +                        other = (
    +                            ind + 1
    +                            if energies[sorted_x[ind + 1]] < energies[sorted_x[ind - 1]]
                                 else ind - 1
    -                    if abs(energies[min_x]
    -                           - energies[sorted_x[other]]) < etol:
    -                        logger.info("Stopping optimization! Final %s = %f"
    -                                    % (lattice_direction, min_x))
    +                        )
    +                    if abs(energies[min_x] - energies[sorted_x[other]]) < etol:
    +                        logger.info(
    +                            "Stopping optimization! Final %s = %f"
    +                            % (lattice_direction, min_x)
    +                        )
                             break
     
                         if ind == 0 and len(sorted_x) > 2:
    @@ -588,16 +707,20 @@ 

    Source code for custodian.vasp.jobs

                             # iteration to find a minimum. This applies only when
                             # there are at least 3 values.
                             x = sorted_x[0] - abs(sorted_x[1] - sorted_x[0])
    -                        logger.info("Lowest energy lies below bounds. "
    -                                    "Setting %s = %f." % (lattice_direction, x))
    +                        logger.info(
    +                            "Lowest energy lies below bounds. "
    +                            "Setting %s = %f." % (lattice_direction, x)
    +                        )
                         elif ind == len(sorted_x) - 1 and len(sorted_x) > 2:
                             # Lowest energy lies outside of range of highest value.
                             # we increase the lattice parameter in the next
                             # iteration to find a minimum. This applies only when
                             # there are at least 3 values.
                             x = sorted_x[-1] + abs(sorted_x[-1] - sorted_x[-2])
    -                        logger.info("Lowest energy lies above bounds. "
    -                                    "Setting %s = %f." % (lattice_direction, x))
    +                        logger.info(
    +                            "Lowest energy lies above bounds. "
    +                            "Setting %s = %f." % (lattice_direction, x)
    +                        )
                         else:
                             if algo.lower() == "bfgs" and len(sorted_x) >= 4:
                                 try:
    @@ -607,30 +730,33 @@ 

    Source code for custodian.vasp.jobs

                                     y1 = [energies[j] for j in x1]
                                     z1 = np.polyfit(x1, y1, 2)
                                     pp = np.poly1d(z1)
    -                                from scipy.optimize import minimize
    +                                from scipy.optimize import minimize
    +
                                     result = minimize(
    -                                    pp, min_x,
    -                                    bounds=[(sorted_x[0], sorted_x[-1])])
    +                                    pp, min_x, bounds=[(sorted_x[0], sorted_x[-1])]
    +                                )
                                     if (not result.success) or result.x[0] < 0:
    -                                    raise ValueError(
    -                                        "Negative lattice constant!")
    +                                    raise ValueError("Negative lattice constant!")
                                     x = result.x[0]
    -                                logger.info("BFGS minimized %s = %f."
    -                                            % (lattice_direction, x))
    +                                logger.info(
    +                                    "BFGS minimized %s = %f." % (lattice_direction, x)
    +                                )
                                 except ValueError as ex:
                                     # Fall back on bisection algo if the bfgs fails.
                                     logger.info(str(ex))
                                     x = (min_x + sorted_x[other]) / 2
    -                                logger.info("Falling back on bisection %s = %f."
    -                                            % (lattice_direction, x))
    +                                logger.info(
    +                                    "Falling back on bisection %s = %f."
    +                                    % (lattice_direction, x)
    +                                )
                             else:
                                 x = (min_x + sorted_x[other]) / 2
    -                            logger.info("Bisection %s = %f."
    -                                        % (lattice_direction, x))
    +                            logger.info("Bisection %s = %f." % (lattice_direction, x))
     
                     lattice = lattice.matrix
    -                lattice[lattice_index] = lattice[lattice_index] / \
    -                    np.linalg.norm(lattice[lattice_index]) * x
    +                lattice[lattice_index] = (
    +                    lattice[lattice_index] / np.linalg.norm(lattice[lattice_index]) * x
    +                )
     
                     s = Structure(lattice, structure.species, structure.frac_coords)
                     fname = "POSCAR.%f" % x
    @@ -639,15 +765,19 @@ 

    Source code for custodian.vasp.jobs

                     incar_update = {"ISTART": 1, "NSW": nsw, "ISIF": 2}
     
                     settings = [
    -                    {"dict": "INCAR",
    -                     "action": {"_set": incar_update}},
    -                    {"file": fname,
    -                     "action": {"_file_copy": {"dest": "POSCAR"}}}]
    +                    {"dict": "INCAR", "action": {"_set": incar_update}},
    +                    {"file": fname, "action": {"_file_copy": {"dest": "POSCAR"}}},
    +                ]
     
                 logger.info("Generating job = %d with parameter %f!" % (i + 1, x))
    -            yield VaspJob(vasp_cmd, final=False, backup=backup,
    -                          suffix=".static.%f" % x,
    -                          settings_override=settings, **vasp_job_kwargs)
    +            yield VaspJob(
    +                vasp_cmd,
    +                final=False,
    +                backup=backup,
    +                suffix=".static.%f" % x,
    +                settings_override=settings,
    +                **vasp_job_kwargs
    +            )
     
             with open("EOS.txt", "wt") as f:
                 f.write("# %s energy\n" % lattice_direction)
    @@ -670,11 +800,21 @@ 

    Source code for custodian.vasp.jobs

         arrangement in NEB calculation.
         """
     
    -    def __init__(self, vasp_cmd,
    -                 output_file="neb_vasp.out", stderr_file="neb_std_err.txt",
    -                 suffix="", final=True, backup=True, auto_npar=True,
    -                 half_kpts=False, auto_gamma=True, auto_continue=False,
    -                 gamma_vasp_cmd=None, settings_override=None):
    +    def __init__(
    +        self,
    +        vasp_cmd,
    +        output_file="neb_vasp.out",
    +        stderr_file="neb_std_err.txt",
    +        suffix="",
    +        final=True,
    +        backup=True,
    +        auto_npar=True,
    +        half_kpts=False,
    +        auto_gamma=True,
    +        auto_continue=False,
    +        gamma_vasp_cmd=None,
    +        settings_override=None,
    +    ):
             """
             This constructor is a simplified version of VaspJob, which satisfies
             the need for flexibility. For standard kinds of runs, it's often
    @@ -746,7 +886,7 @@ 

    Source code for custodian.vasp.jobs

                 if os.path.isdir(path) and path.isdigit():
                     self.neb_dirs.append(path)
             self.neb_dirs = sorted(self.neb_dirs)
    -        self.neb_sub = self.neb_dirs[1: -1]
    +        self.neb_sub = self.neb_dirs[1:-1]
     
     
    [docs] def setup(self): """ @@ -770,7 +910,7 @@

    Source code for custodian.vasp.jobs

                 kpts.kpts = kpts.kpts.astype(int).tolist()
                 if tuple(kpts.kpts[0]) == (1, 1, 1):
                     kpt_dic = kpts.as_dict()
    -                kpt_dic["generation_style"] = 'Gamma'
    +                kpt_dic["generation_style"] = "Gamma"
                     kpts = Kpoints.from_dict(kpt_dic)
                 kpts.write_file("KPOINTS")
     
    @@ -778,13 +918,13 @@ 

    Source code for custodian.vasp.jobs

                 try:
                     incar = Incar.from_file("INCAR")
                     import multiprocessing
    +
                     # Try sge environment variable first
                     # (since multiprocessing counts cores on the current
                     # machine only)
    -                ncores = os.environ.get('NSLOTS') or multiprocessing.cpu_count()
    +                ncores = os.environ.get("NSLOTS") or multiprocessing.cpu_count()
                     ncores = int(ncores)
    -                for npar in range(int(math.sqrt(ncores)),
    -                                  ncores):
    +                for npar in range(int(math.sqrt(ncores)), ncores):
                         if ncores % npar == 0:
                             incar["NPAR"] = npar
                             break
    @@ -792,9 +932,11 @@ 

    Source code for custodian.vasp.jobs

                 except Exception:
                     pass
     
    -        if self.auto_continue and \
    -                os.path.exists("STOPCAR") and \
    -                not os.access("STOPCAR", os.W_OK):
    +        if (
    +            self.auto_continue
    +            and os.path.exists("STOPCAR")
    +            and not os.access("STOPCAR", os.W_OK)
    +        ):
                 # Remove STOPCAR
                 os.chmod("STOPCAR", 0o644)
                 os.remove("STOPCAR")
    @@ -818,16 +960,19 @@ 

    Source code for custodian.vasp.jobs

             cmd = list(self.vasp_cmd)
             if self.auto_gamma:
                 kpts = Kpoints.from_file("KPOINTS")
    -            if kpts.style == Kpoints.supported_modes.Gamma \
    -                    and tuple(kpts.kpts[0]) == (1, 1, 1):
    -                if self.gamma_vasp_cmd is not None and which(
    -                        self.gamma_vasp_cmd[-1]):
    +            if kpts.style == Kpoints.supported_modes.Gamma and tuple(kpts.kpts[0]) == (
    +                1,
    +                1,
    +                1,
    +            ):
    +                if self.gamma_vasp_cmd is not None and which(self.gamma_vasp_cmd[-1]):
                         cmd = self.gamma_vasp_cmd
                     elif which(cmd[-1] + ".gamma"):
                         cmd[-1] += ".gamma"
             logger.info("Running {}".format(" ".join(cmd)))
    -        with open(self.output_file, 'w') as f_std, \
    -                open(self.stderr_file, "w", buffering=1) as f_err:
    +        with open(self.output_file, "w") as f_std, open(
    +            self.stderr_file, "w", buffering=1
    +        ) as f_err:
     
                 # Use line buffering for stderr
                 p = subprocess.Popen(cmd, stdout=f_std, stderr=f_err)
    @@ -857,8 +1002,7 @@ 

    Source code for custodian.vasp.jobs

     
     
     
    [docs]class GenerateVaspInputJob(Job): - - def __init__(self, input_set, contcar_only=True, **kwargs): + def __init__(self, input_set, contcar_only=True, **kwargs): """ Generates a VASP input based on an existing directory. This is typically used to modify the VASP input files before the next VaspJob. @@ -912,14 +1056,14 @@

    Quick search

    - +
    @@ -50,11 +49,11 @@

    Navigation

    Source code for custodian.vasp.validators

     # coding: utf-8
     
    -from __future__ import unicode_literals, division
    +from __future__ import unicode_literals, division
     
    -from custodian.custodian import Validator
    -from pymatgen.io.vasp import Vasprun, Incar, Outcar, Chgcar
    -from collections import deque
    +from custodian.custodian import Validator
    +from pymatgen.io.vasp import Vasprun, Incar, Outcar, Chgcar
    +from collections import deque
     
     import os
     import logging
    @@ -65,7 +64,7 @@ 

    Source code for custodian.vasp.validators

         Checks that a valid vasprun.xml was generated
         """
     
    -    def __init__(self, output_file="vasp.out", stderr_file="std_err.txt"):
    +    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.
    @@ -104,8 +103,9 @@ 

    Source code for custodian.vasp.validators

                         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)
    +            self.logger.error(
    +                "Failed to load vasprun.xml", exc_info=True, extra=exception_context
    +            )
     
                 return True
             return False
    @@ -117,7 +117,7 @@

    Source code for custodian.vasp.validators

             normally create upon running.
         """
     
    -    def __init__(self):
    +    def __init__(self):
             pass
     
     
    [docs] def check(self): @@ -133,7 +133,7 @@

    Source code for custodian.vasp.validators

         Currently, VASP only have Langevin thermostat (MDALGO = 3) for NpT ensemble.
         """
     
    -    def __init__(self):
    +    def __init__(self):
             pass
     
     
    [docs] def check(self): @@ -145,7 +145,7 @@

    Source code for custodian.vasp.validators

             outcar = Outcar("OUTCAR")
             patterns = {"MDALGO": "MDALGO\s+=\s+([\d]+)"}
             outcar.read_pattern(patterns=patterns)
    -        if outcar.data["MDALGO"] == [['3']]:
    +        if outcar.data["MDALGO"] == [["3"]]:
                 return False
             else:
                 return True
    @@ -156,7 +156,7 @@

    Source code for custodian.vasp.validators

         Check if the data in the AECCAR is corrupted
         """
     
    -    def __init__(self):
    +    def __init__(self):
             pass
     
     
    [docs] def check(self): @@ -166,16 +166,29 @@

    Source code for custodian.vasp.validators

             return check_broken_chgcar(aeccar)
    -
    [docs]def check_broken_chgcar(chgcar): - chgcar_data = chgcar.data['total'] +
    [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 + # a decent bunch of the values are negative this for sure means a broken charge density return True - diff = chgcar_data[:-1, :-1, :-1] - chgcar_data[1:, 1:, 1:] - if diff.max() / (chgcar_data.max() - chgcar_data.min()) > 0.95: - # Some single diagonal finite difference is more than 95% of the entire range - 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
    @@ -200,14 +213,14 @@

    Quick search

    - +
    @@ -58,6 +57,8 @@

    All modules for which code is available

  • custodian.feff.handlers
  • custodian.feff.interpreter
  • custodian.feff.jobs
  • +
  • custodian.lobster.handlers
  • +
  • custodian.lobster.jobs
  • custodian.nwchem.handlers
  • custodian.nwchem.jobs
  • custodian.qchem.handlers
  • @@ -87,14 +88,14 @@

    Quick search

    - +