Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes for HandyMan and Jobfactories #1241

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions pyiron_contrib/jobfactories/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from abc import ABC, abstractmethod
import contextlib
from typing import Optional, Callable, Dict
import warnings


class JobFactory(HasStorage, ABC):
Expand Down Expand Up @@ -151,7 +152,7 @@ def input(self):
return self.storage.create_group("input")

def __getattr__(self, name):
if name.startswith("__") and name.endswith("__"):
if ( name.startswith("__") and name.endswith("__") ) or name == "getdoc":
raise AttributeError(name)

def wrapper(*args, **kwargs):
Expand All @@ -170,11 +171,20 @@ def _prepare_job(self, job, structure):
job.server.cores = self.cores
if self.run_time is not None:
job.server.run_time = self.run_time
if 'memory_limit' in self.server:
job.server.memory_limit = self.server.memory_limit
for k, v in self.storage.input.items():
job.input[k] = v
if "methods" in self.storage:
for meth, ka in self.storage.methods.items():
getattr(job, meth)(*ka.args, **ka.kwargs)
if meth == 'getdoc':
# garbage introduced by ipython notebooks
del self.storage.methods[meth]
continue
if hasattr(job, meth):
getattr(job, meth)(*ka.args, **ka.kwargs)
else:
warnings.warn(f"Could not call method {meth} on job type {self.hamilton}, skipping it!")
if "attributes" in self.storage:
for attr, val in self.storage.attributes.items():
setattr(job, attr, val)
Expand Down Expand Up @@ -288,6 +298,9 @@ def _prepare_job(self, job, structure):


class DftFactory(JobFactory):
pass

class SphinxFactory(DftFactory):
def set_empty_states(self, states_per_atom):
self.storage.empty_states_per_atom = states_per_atom

Expand Down
39 changes: 28 additions & 11 deletions pyiron_contrib/repair/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,18 @@ def find_tool(self, job):
raise NoMatchingTool("Cannot find stuitable tool!")

def fix_project(
self, project, server_override={}, refresh=True, graveyard=None, **kwargs
self, project, server_override={}, refresh=True, graveyard=None,
shuffle=False, **kwargs
):
"""
Fix broken jobs.

Args:
project (Project): search this project for broken jobs
server_override (dict): override these values on the restarted jobs
refresh (bool): refresh job status before fixing
graveyard (Project): move fixed projects here, instead of deleting
shuffle (bool): fix jobs in random order not in database order
**kwargs: pass through project.job_table when searching; use to
restrict what jobs are fixed
"""
Expand All @@ -259,15 +262,15 @@ def fix_project(
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,
desc="Repairing Jobs",
)
for jid in job_ids:
job_ids = project.job_table(**kwargs).query("status.isin(@status_list)").id
if shuffle:
job_ids.sample(frac=1)
for jid in tqdm(job_ids, desc="Repairing Jobs"):
try:
job = project.load(jid)
except IndexError:
logger.warning(f"Failed to load job {jid}, skipping.")
continue
try:
if (
job.master_id is not None
Expand Down Expand Up @@ -429,11 +432,11 @@ def match(self, job: GenericJob) -> bool:

def fix(self, old_job, new_job):
super().fix(old_job, new_job)
old_states = old_job.content["output/generic/dft/bands/occ_matrix"].shape[0]
old_states = old_job.content["output/generic/dft/bands/occ_matrix"].shape[-1]
n_elect = old_job.get_nelect()
# double free states
current_empty_bands = old_states - n_elect // 2
new_job.set_empty_states(
math.ceil((old_states - n_elect // 2) * self._state_factor)
math.ceil(max(2, current_empty_bands) * self._state_factor)
)

try:
Expand Down Expand Up @@ -464,6 +467,9 @@ def match(self, job):
" 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(
"VERY BAD NEWS! internal error in subroutineRHOSYG:stars are not"
),
PartialLine(
"PRICELV: current lattice and primitive lattice are incommensurate"
),
Expand Down Expand Up @@ -516,15 +522,26 @@ class VaspZbrentTool(VaspTool):
Lifted from custodian.
"""

def __init__(self, *, rattle=False, **kwargs):
"""
Args:
rattle (bool): if all else fails, try to read CONTCAR and apply a
small rattle to atomic positions to try and break some symmetry;
this will change the initial structure of the job!
"""
super().__init__(**kwargs)
self._rattle = rattle

def match(self, job):
if (
job.input.incar["EDIFF"] <= 1e-6
job.input.incar.get("EDIFF", 1e-4) <= 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!"
)
return False
return match_in_error_log(
[
PartialLine("ZBRENT: fatal error in bracketing"),
Expand All @@ -540,7 +557,7 @@ def fix(self, old_job, new_job):
new_job.input.incar["EDIFF"] = 1e-6
if old_job.input.incar.get("IBRION", 2) != 1:
new_job.input.incar["IBRION"] = 1
else:
elif self._rattle:
contcar = ase_to_pyiron(ase_read(str(old_job.files.CONTCAR)))
# VASP manual recommend to copy CONTCAR to POSCAR, but if we
# overwrite the structure in pyiron directly with the one read from
Expand Down
Loading