diff --git a/pyiron_contrib/repair/__init__.py b/pyiron_contrib/repair/__init__.py index 628ead9a9..0a53c9da2 100644 --- a/pyiron_contrib/repair/__init__.py +++ b/pyiron_contrib/repair/__init__.py @@ -16,22 +16,36 @@ from tqdm.auto import tqdm -class RepairError(Exception): pass -class NoMatchingTool(RepairError): pass -class RestartFailed(RepairError): pass -class FixFailed(RepairError): pass + +class RepairError(Exception): + pass + + +class NoMatchingTool(RepairError): + pass + + +class RestartFailed(RepairError): + pass + + +class FixFailed(RepairError): + pass + def get_job_error_log(job): - log = job['error.out'] + log = job["error.out"] if log is None: - log = job['error.msg'] + log = job["error.msg"] return log + class LineMatcher(abc.ABC): @abc.abstractmethod def match(self, line): pass + class FullLine(LineMatcher): def __init__(self, line): self._line = line @@ -43,6 +57,7 @@ def __init__(self, line): def match(self, line): return self._line == line + class PartialLine(LineMatcher): def __init__(self, line): self._line = line @@ -50,14 +65,17 @@ def __init__(self, line): def match(self, line): return self._line in line + def match_in_error_log(match_lines, job): if isinstance(match_lines, (str, LineMatcher)): match_lines = [match_lines] - match_lines = [m if isinstance(m, LineMatcher) else FullLine(m) - for m in match_lines] + match_lines = [ + m if isinstance(m, LineMatcher) else FullLine(m) for m in match_lines + ] lines = get_job_error_log(job) or [] return any(any(m.match(l) for m in match_lines) for l in lines) + class RepairTool(abc.ABC): @abc.abstractmethod @@ -94,6 +112,7 @@ def fix(self, old_job: GenericJob, new_job: GenericJob): priority = 0 """If multiple tools exist that can fix a job, pick the one with highest priority""" + class ConstructionSite: def __init__(self, fixing, hopeless, failed): self._fixing = fixing @@ -112,10 +131,12 @@ def hopeless(self): def failed(self): return self._failed + class HandyMan: - def __init__(self, tools: Union[None, Iterable[RepairTool]] = None, - suppress_fix_errors=True): + def __init__( + self, tools: Union[None, Iterable[RepairTool]] = None, suppress_fix_errors=True + ): self.shed = defaultdict(list) self._suppress_fix_errors = suppress_fix_errors @@ -125,7 +146,9 @@ def __init__(self, tools: Union[None, Iterable[RepairTool]] = None, for tool in tools: self.register(tool) else: - raise TypeError("tools must a list of RepairTools or a dict of status to RepairTools.") + raise TypeError( + "tools must a list of RepairTools or a dict of status to RepairTools." + ) def register(self, tool, status=None): if status is None: @@ -174,22 +197,24 @@ def fix_job(self, tool, job): new_job.master_id = mid new_job.parent_id = pid - new_job.server.queue = 'cmti' + new_job.server.queue = "cmti" return new_job def find_tool(self, job): # try job specific tools first, otherwise sort by priority such that # highest comes first - tools = sorted(self.shed[(job.status.string, job.__name__)], - key=lambda tool: -tool.priority) \ - + sorted(self.shed[(job.status.string, "generic")], - key=lambda tool: -tool.priority) + tools = sorted( + self.shed[(job.status.string, job.__name__)], + key=lambda tool: -tool.priority, + ) + sorted( + self.shed[(job.status.string, "generic")], key=lambda tool: -tool.priority + ) for tool in tools: try: if tool.match(job): return tool except Exception as e: - logger.warn(f'Matching {tool} on job {job.id} failed with {e}!') + logger.warn(f"Matching {tool} on job {job.id} failed with {e}!") raise NoMatchingTool("Cannot find stuitable tool!") def fix_project(self, project, server_override={}, **kwargs): @@ -208,21 +233,29 @@ def fix_project(self, project, server_override={}, **kwargs): failed = {} fixing = defaultdict(list) status_list = set([k[0] for k in self.shed.keys()]) - job_ids = tqdm(project.job_table(**kwargs).query('status.isin(@status_list)').id) + job_ids = tqdm( + project.job_table(**kwargs).query("status.isin(@status_list)").id + ) for jid in job_ids: try: job = project.load(jid) except IndexError: logger.warning(f"Failed to load job {jid}, skipping.") try: - if job.master_id is not None and project.get_job_status(job.master_id) == "running": + if ( + job.master_id is not None + and project.get_job_status(job.master_id) == "running" + ): logger.warning(f"Job {jid}'s master is still running, skipping!") continue - if job.parent_id is not None and project.get_job_status(job.parent_id) == "running": + if ( + job.parent_id is not None + and project.get_job_status(job.parent_id) == "running" + ): logger.warning(f"Job {jid}'s parent is still running, skipping!") continue except IndexError: - pass # parent or master jobs don't exist anymore, ignore + pass # parent or master jobs don't exist anymore, ignore try: tool = self.find_tool(job) fixing[type(tool).__name__].append(job.id) @@ -250,7 +283,7 @@ def __init__(self, time_factor=2): applicable_status = ("aborted", "collect") def match(self, job: GenericJob) -> bool: - return match_in_error_log(PartialLine('DUE TO TIME LIMIT'), job) + return match_in_error_log(PartialLine("DUE TO TIME LIMIT"), job) @wraps(RepairTool.fix_inplace) def fix_inplace(self, job, handyman): @@ -262,14 +295,17 @@ def fix_inplace(self, job, handyman): # got created and didn't get to run for cid in cs.hopeless: child = job.load(cid) - last = child['user/handyman/last'] + last = child["user/handyman/last"] if last != "RestartTool": child = handyman.fix_job(RestartTool(), child) child.run() atleast_one = True # if we restarted at least one child, tell the handy man not to # restart this job - return atleast_one or job.child_project.get_jobs_status().get('submitted', 0) > 0 + return ( + atleast_one + or job.child_project.get_jobs_status().get("submitted", 0) > 0 + ) elif job.status.collect and not isinstance(job, GenericMaster): try: job.decompress() @@ -285,9 +321,9 @@ def fix_inplace(self, job, handyman): def fix(self, old_job: GenericJob, new_job: GenericJob): for line in get_job_error_log(old_job): - matches = re.findall('CANCELLED AT (.*) DUE TO TIME LIMIT', line) + matches = re.findall("CANCELLED AT (.*) DUE TO TIME LIMIT", line) if len(matches) != 0: - stop = datetime.datetime.strptime(matches[0], '%Y-%m-%dT%H:%M:%S') + stop = datetime.datetime.strptime(matches[0], "%Y-%m-%dT%H:%M:%S") break run_time = stop - old_job.database_entry.timestart new_job.server.run_time = run_time.total_seconds() * self._time_factor @@ -328,13 +364,18 @@ def match(self, job): return isinstance(job, AtomisticGenericJob) def fix(self, old_job: GenericJob, new_job: GenericJob): - if not self._copy_final_structure and old_job._generic_input['calc_mode'] != 'static': + if ( + not self._copy_final_structure + and old_job._generic_input["calc_mode"] != "static" + ): new_job.structure = old_job.structure + class VaspTool(AtomisticRepairTool, abc.ABC): hamilton = "Vasp" + class VaspNbandsTool(VaspTool): def __init__(self, state_factor=2, **kwargs): @@ -349,11 +390,11 @@ def match(self, job: GenericJob) -> bool: def fix(self, old_job, new_job): super().fix(old_job, new_job) - old_states = old_job['output/generic/dft/bands/occ_matrix'].shape[-1] + old_states = old_job["output/generic/dft/bands/occ_matrix"].shape[-1] n_elect = old_job.get_nelect() # double free states new_job.set_empty_states( - math.ceil((old_states - n_elect//2) * self._state_factor) + math.ceil((old_states - n_elect // 2) * self._state_factor) ) try: @@ -365,78 +406,95 @@ def fix(self, old_job, new_job): applicable_status = ("not_converged",) + class VaspDisableIsymTool(VaspTool): """ Assorted symmetry errors, just turn symmetry off. """ def match(self, job): - return super().match(job) and job.input.incar['ISYM'] != 0 and \ - match_in_error_log([ - ' inverse of rotation matrix was not found (increase SYMPREC) 5', - ' RHOSYG internal error: stars are not distinct, try to increase SYMPREC to e.g. ', - ' POSMAP internal error: symmetry equivalent atom not found,', - ' VERY BAD NEWS! internal error in subroutine PRICEL ' - '(probably precision problem, try to change SYMPREC in INCAR ?):', - ' VERY BAD NEWS! internal error in subroutine INVGRP:', - PartialLine('Found some non-integer element in rotation matrix') - ], job - ) + return ( + super().match(job) + and job.input.incar["ISYM"] != 0 + and match_in_error_log( + [ + " inverse of rotation matrix was not found (increase SYMPREC) 5", + " RHOSYG internal error: stars are not distinct, try to increase SYMPREC to e.g. ", + " POSMAP internal error: symmetry equivalent atom not found,", + " VERY BAD NEWS! internal error in subroutine PRICEL " + "(probably precision problem, try to change SYMPREC in INCAR ?):", + " VERY BAD NEWS! internal error in subroutine INVGRP:", + PartialLine("Found some non-integer element in rotation matrix"), + ], + job, + ) + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['ISYM'] = 0 + new_job.input.incar["ISYM"] = 0 # ISYM parameter not registered in INCAR otherwise. :| new_job.write_input() applicable_status = ("aborted",) + class VaspSubspaceTool(VaspTool): """ Lifted from custodian. """ def match(self, job): - return super().match(job) and match_in_error_log(PartialLine("ERROR in subspace rotation PSSYEVX"), job) + return super().match(job) and match_in_error_log( + PartialLine("ERROR in subspace rotation PSSYEVX"), job + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['ALGO'] = 'Normal' + new_job.input.incar["ALGO"] = "Normal" new_job.write_input() applicable_status = ("aborted",) + class VaspZbrentTool(VaspTool): """ Lifted from custodian. """ def match(self, job): - if job.input.incar['EDIFF'] <= 1e-6 and job['user/handyman/last'] == 'VaspZbrentTool': + if ( + job.input.incar["EDIFF"] <= 1e-6 + and job["user/handyman/last"] == "VaspZbrentTool" + ): logger.warning( - "Bailing to apply VaspZbrentTool! Already tried once and " - "you didn't think it through this far yet!" + "Bailing to apply VaspZbrentTool! Already tried once and " + "you didn't think it through this far yet!" ) - return match_in_error_log([ - PartialLine("ZBRENT: fatal error in bracketing"), - PartialLine("ZBRENT: fatal error: bracketing interval incorrect") - ], job) + return match_in_error_log( + [ + PartialLine("ZBRENT: fatal error in bracketing"), + PartialLine("ZBRENT: fatal error: bracketing interval incorrect"), + ], + job, + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - ediff = old_job.input.incar.get('EDIFF', 1e-4) + ediff = old_job.input.incar.get("EDIFF", 1e-4) if ediff > 1e-6: - new_job.input.incar['EDIFF'] = 1e-6 + new_job.input.incar["EDIFF"] = 1e-6 else: - new_job.structure = ase_to_pyiron(ase_read( - old_job.get_workdir_file("CONTCAR") - )) - nelmin = old_job.input.incar['NELMIN'] + new_job.structure = ase_to_pyiron( + ase_read(old_job.get_workdir_file("CONTCAR")) + ) + nelmin = old_job.input.incar["NELMIN"] if nelmin is None or nelmin < 8: - new_job.input.incar['NELMIN'] = 8 + new_job.input.incar["NELMIN"] = 8 applicable_status = ("aborted",) + class VaspZpotrfZtrtri(VaspTool): """ https://github.com/protik77/VASP-error-fix#error-scalapack-routine-zpotrf-ztrtri-failed @@ -450,19 +508,19 @@ class VaspZpotrfZtrtri(VaspTool): """ def match(self, job): - return super().match(job) and match_in_error_log(PartialLine( - "LAPACK: Routine ZPOTRF ZTRTRI failed!" - ), job) + return super().match(job) and match_in_error_log( + PartialLine("LAPACK: Routine ZPOTRF ZTRTRI failed!"), job + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - algo = old_job.input.incar.get('ALGO', 'Fast') + algo = old_job.input.incar.get("ALGO", "Fast") mapa = { - "Normal": "Fast", - "Fast": "VeryFast", - "VeryFast": "Normal", + "Normal": "Fast", + "Fast": "VeryFast", + "VeryFast": "Normal", } - new_job.input.incar['ALGO'] = mapa.get(algo, 'Fast') + new_job.input.incar["ALGO"] = mapa.get(algo, "Fast") class VaspZpotrfTool(VaspTool): @@ -471,32 +529,41 @@ class VaspZpotrfTool(VaspTool): """ def match(self, job): - return super().match(job) and match_in_error_log(PartialLine("LAPACK: Routine ZPOTRF failed!"), job) + return super().match(job) and match_in_error_log( + PartialLine("LAPACK: Routine ZPOTRF failed!"), job + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['ISYM'] = 0 - new_job.input.incar['POTIM'] = old_job.input.incar.get('POTIM', 0.5) / 2 + new_job.input.incar["ISYM"] = 0 + new_job.input.incar["POTIM"] = old_job.input.incar.get("POTIM", 0.5) / 2 new_job._restart_file_list = [] new_job._restart_file_dict = {} applicable_status = ("aborted",) + class VaspEddavTool(VaspTool): """ Lifted from custodian. """ def match(self, job): - return super().match(job) and job.input.incar['ALGO'] != 'All' \ - and match_in_error_log(PartialLine("Error EDDDAV: Call to ZHEGV failed."), job) + return ( + super().match(job) + and job.input.incar["ALGO"] != "All" + and match_in_error_log( + PartialLine("Error EDDDAV: Call to ZHEGV failed."), job + ) + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['ALGO'] = 'All' + new_job.input.incar["ALGO"] = "All" applicable_status = ("aborted",) + class VaspMinimizeStepsTool(VaspTool): """ Ionic Minimization didn't converge. @@ -509,23 +576,28 @@ def __init__(self, factor=2, **kwargs): self._factor = factor def match(self, job): - return super().match(job) and job.input.incar['IBRION'] != -1 \ - and job.input.incar['NSW'] == len(job["output/generic/dft/scf_energy_free"]) + return ( + super().match(job) + and job.input.incar["IBRION"] != -1 + and job.input.incar["NSW"] == len(job["output/generic/dft/scf_energy_free"]) + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) new_job.structure = old_job.structure - new_job.input.incar['NSW'] = \ - int(old_job.input.incar.get('NSW', 100) * self._factor) - new_job.input.incar['EDIFF'] = 1e-6 + new_job.input.incar["NSW"] = int( + old_job.input.incar.get("NSW", 100) * self._factor + ) + new_job.input.incar["EDIFF"] = 1e-6 new_job._restart_file_list = [] new_job._restart_file_dict = {} applicable_status = ("not_converged",) + class VaspElectronicConvergenceTool(VaspTool): - """ - """ + """ """ + class VaspTooManyKpointsIsym(VaspTool): """ @@ -538,17 +610,25 @@ class VaspTooManyKpointsIsym(VaspTool): """ def match(self, job): - return super().match(job) and match_in_error_log([ - PartialLine("VERY BAD NEWS! internal error in subroutine IBZKPT"), - PartialLine("NKPT>NKDIM"), - ], job) and job.input.incar['ISYM'] == 0 + return ( + super().match(job) + and match_in_error_log( + [ + PartialLine("VERY BAD NEWS! internal error in subroutine IBZKPT"), + PartialLine("NKPT>NKDIM"), + ], + job, + ) + and job.input.incar["ISYM"] == 0 + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['ISYM'] = 1 + new_job.input.incar["ISYM"] = 1 applicable_status = ("aborted",) + class VaspTooManyKpointsTruncate(VaspTool): """ Occurs when too many k-points are requested. @@ -563,16 +643,20 @@ class VaspTooManyKpointsTruncate(VaspTool): """ def match(self, job): - return super().match(job) and match_in_error_log([ + return super().match(job) and match_in_error_log( + [ PartialLine("VERY BAD NEWS! internal error in subroutine IBZKPT"), PartialLine("NKPT>NKDIM"), - ], job) + ], + job, + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) kpoints = [min(45, int(k)) for k in old_job.input.kpoints[3].split()] new_job.set_kpoints(kpoints) + class VaspSetupPrimitiveCellTool(VaspTool): """ Vasp recommends "changing" SYMPREC or refining POSCAR. @@ -581,16 +665,20 @@ class VaspSetupPrimitiveCellTool(VaspTool): """ def match(self, job): - return super().match(job) and \ - ' internal error in VASP: SETUP_PRIMITIVE_CELL, S_NUM not divisible by NPCELL' in get_job_error_log(job) + return super().match( + job + ) and " internal error in VASP: SETUP_PRIMITIVE_CELL, S_NUM not divisible by NPCELL" in get_job_error_log( + job + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - symprec = old_job.input.incar.get('SYMPREC', 1e-5) - new_job.input.incar['SYMPREC'] = symprec * 10 + symprec = old_job.input.incar.get("SYMPREC", 1e-5) + new_job.input.incar["SYMPREC"] = symprec * 10 applicable_status = ("aborted",) + class VaspMemoryErrorTool(VaspTool): """ Random crashes without any other indication are usually because memory ran @@ -599,18 +687,25 @@ class VaspMemoryErrorTool(VaspTool): def __init__(self, factor=2, max_cores=160, **kwargs): super().__init__(**kwargs) - self._factor = factor + self._factor = factor self._max_cores = max_cores def match(self, job): # coredump = 'Image PC Routine Line Source \n' in job['error.out'] # return malloc or (forrtl and coredump) too_many_cores = job.server.cores >= self._max_cores - return super().match(job) and not too_many_cores and match_in_error_log([ - 'forrtl: error (78): process killed (SIGTERM)', - 'malloc(): corrupted top size', - PartialLine('Out of memory (unable to allocate a \'MPI_Info\')') - ], job) + return ( + super().match(job) + and not too_many_cores + and match_in_error_log( + [ + "forrtl: error (78): process killed (SIGTERM)", + "malloc(): corrupted top size", + PartialLine("Out of memory (unable to allocate a 'MPI_Info')"), + ], + job, + ) + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) @@ -620,15 +715,18 @@ def fix(self, old_job, new_job): new_cores = old_job.server.cores new_job.server.cores = new_cores if new_cores >= 40: - new_job.input.incar['NCORE'] = 20 + new_job.input.incar["NCORE"] = 20 elif new_cores >= 20: - new_job.input.incar['NCORE'] = 10 + new_job.input.incar["NCORE"] = 10 applicable_status = ("aborted",) + class VaspEddrmmTool(VaspTool): def match(self, job): - return super().match(job) and match_in_error_log(PartialLine("WARNING in EDDRMM: call to ZHEGV failed, returncode ="), job) + return super().match(job) and match_in_error_log( + PartialLine("WARNING in EDDRMM: call to ZHEGV failed, returncode ="), job + ) def fix_inplace(self, job: GenericJob, handyman) -> bool: job.set_eddrmm_handling(status="ignore") @@ -638,7 +736,7 @@ def fix_inplace(self, job: GenericJob, handyman) -> bool: def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['ALGO'] = 'Normal' + new_job.input.incar["ALGO"] = "Normal" try: new_job.restart_file_list.append(old_job.get_workdir_file("CHGCAR")) new_job.input.incar["ICHARG"] = 1 @@ -653,40 +751,46 @@ class VaspSgrconTool(VaspTool): """ custodian recommends changing to a gamma centered mesh. """ + def match(self, job): - return super().match(job) and job.input.kpoints[2] == 'Monkhorst_Pack' \ - and match_in_error_log( - PartialLine('VERY BAD NEWS! internal error in subroutine SGRCON'), - job - ) + return ( + super().match(job) + and job.input.kpoints[2] == "Monkhorst_Pack" + and match_in_error_log( + PartialLine("VERY BAD NEWS! internal error in subroutine SGRCON"), job + ) + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.set_kpoints(old_job.kpoint_mesh, scheme='GC') + new_job.set_kpoints(old_job.kpoint_mesh, scheme="GC") return new_job applicable_status = ("aborted",) + # class VaspLongCellAmin(VaspTool): - # def match(self, job): - # return any([ - # "One of the lattice vectors is very long (>50 A), but AMIN is rather" in l - # for l in job['OUTCAR'] - # ]) - - # def fix(self, old_job, new_job): - # # vasp recommends 0.01 in the message, if that doesn't work let's try - # # with smaller again - # amin = old_job.input.incar.get("AMIN", 0.02) - # new_job.input.incar['AMIN'] = amin / 2 +# def match(self, job): +# return any([ +# "One of the lattice vectors is very long (>50 A), but AMIN is rather" in l +# for l in job['OUTCAR'] +# ]) + +# def fix(self, old_job, new_job): +# # vasp recommends 0.01 in the message, if that doesn't work let's try +# # with smaller again +# amin = old_job.input.incar.get("AMIN", 0.02) +# new_job.input.incar['AMIN'] = amin / 2 + class MurnaghanTool(RepairTool, abc.ABC): hamilton = "Murnaghan" + class MurnaghanFinishedChildrenTool(MurnaghanTool): def match(self, job): - return (job.child_project.job_table().status=="finished").all() + return (job.child_project.job_table().status == "finished").all() @wraps(RepairTool.fix_inplace) def fix_inplace(self, job, handyman): @@ -699,6 +803,7 @@ def fix(self, old_job, new_job): applicable_status = ("aborted", "collect") + class MurnaghanAllowAbortedChildrenTool(MurnaghanTool): """ Retroactively allow some children of Murnaghans to fail, by changing the @@ -718,19 +823,19 @@ def __init__(self, allow_aborted: Union[int, float] = 0.1): def match(self, job): status = job.child_project.get_jobs_status() # As long as child jobs are still running, do not do anything - if status.get('running', 0) > 0 or status.get('submitted', 0) > 0: + if status.get("running", 0) > 0 or status.get("submitted", 0) > 0: return False allow_aborted = self._allow_aborted if 0 < self._allow_aborted < 1: allow_aborted *= status.sum() - return 0 < status.get('aborted', 0) < allow_aborted + return 0 < status.get("aborted", 0) < allow_aborted @wraps(RepairTool.fix_inplace) def fix_inplace(self, job, handyman): allow_aborted = self._allow_aborted if 0 < self._allow_aborted < 1: allow_aborted *= len(job.child_project.job_table(recursive=False)) - job.input['allow_aborted'] = allow_aborted + job.input["allow_aborted"] = allow_aborted job.status.collect = True job.run() return True @@ -738,58 +843,72 @@ def fix_inplace(self, job, handyman): def fix(self, old_job, new_job): assert False, "Shouldn't happen!" + class SphinxTool(AtomisticRepairTool, abc.ABC): hamilton = "Sphinx" + class SphinxSymmetryOffTool(RepairTool): def match(self, job): - return super().match(job) and 'Symmetry inconsistency error: symmetries are no group\n' \ - in job['sphinx.log'] + return ( + super().match(job) + and "Symmetry inconsistency error: symmetries are no group\n" + in job["sphinx.log"] + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) new_job.input._force_load() - new_job.input.sphinx.structure.create_group('symmetry') + new_job.input.sphinx.structure.create_group("symmetry") return new_job + ### Classes below are experimental class VaspSymprecTool(VaspTool): def match(self, job): - return super().match(job) and match_in_error_log([ - ' inverse of rotation matrix was not found (increase SYMPREC) 5', - ' POSMAP internal error: symmetry equivalent atom not found,' - ], job) + return super().match(job) and match_in_error_log( + [ + " inverse of rotation matrix was not found (increase SYMPREC) 5", + " POSMAP internal error: symmetry equivalent atom not found,", + ], + job, + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - symprec = old_job.input.incar.get('SYMPREC', 1e-5) - new_job.input.incar['SYMPREC'] = 10 * symprec + symprec = old_job.input.incar.get("SYMPREC", 1e-5) + new_job.input.incar["SYMPREC"] = 10 * symprec + class VaspRhosygSymprecTool(VaspTool): def match(self, job): - return super().match(job) and match_in_error_log(' RHOSYG internal error: stars are not distinct, try to increase SYMPREC to e.g. ', job) + return super().match(job) and match_in_error_log( + " RHOSYG internal error: stars are not distinct, try to increase SYMPREC to e.g. ", + job, + ) def fix(self, old_job, new_job): super().fix(old_job, new_job) - new_job.input.incar['SYMPREC'] = 1e-4 + new_job.input.incar["SYMPREC"] = 1e-4 + DEFAULT_SHED = [ - TimeoutTool(2), - MurnaghanFinishedChildrenTool(), - VaspDisableIsymTool(), - VaspSgrconTool(), - VaspSubspaceTool(), - VaspZbrentTool(), - VaspZpotrfTool(), - VaspEddavTool(), - VaspSetupPrimitiveCellTool(), - VaspTooManyKpointsIsym(), - VaspMemoryErrorTool(max_cores=320), - VaspNbandsTool(1.5), - VaspMinimizeStepsTool(2), - VaspEddrmmTool(), + TimeoutTool(2), + MurnaghanFinishedChildrenTool(), + VaspDisableIsymTool(), + VaspSgrconTool(), + VaspSubspaceTool(), + VaspZbrentTool(), + VaspZpotrfTool(), + VaspEddavTool(), + VaspSetupPrimitiveCellTool(), + VaspTooManyKpointsIsym(), + VaspMemoryErrorTool(max_cores=320), + VaspNbandsTool(1.5), + VaspMinimizeStepsTool(2), + VaspEddrmmTool(), ]