Skip to content

Commit

Permalink
Merge branch 'gaussian' into pr/rashatwi/331
Browse files Browse the repository at this point in the history
  • Loading branch information
janosh committed Apr 24, 2024
2 parents 62493af + 851512c commit 67c79dd
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 32 deletions.
38 changes: 16 additions & 22 deletions custodian/gaussian/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class GaussianErrorHandler(ErrorHandler):

error_patt = re.compile("|".join(list(error_defs)))
recom_mem_patt = re.compile(
r"Use %mem=([0-9]+)MW to provide the minimum " r"amount of memory required to complete this " r"step."
r"Use %mem=([0-9]+)MW to provide the minimum amount of memory required to complete this step."
)
conv_critera = {
"max_force": re.compile(r"\s+(Maximum Force)\s+(-?\d+.?\d*|.*)\s+(-?\d+.?\d*)"),
Expand Down Expand Up @@ -453,7 +453,7 @@ def _monitor_convergence(data: dict[str, dict[str, Any]], directory: str = "./")
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator

fig, ax = plt.subplots(ncols=2, nrows=2, figsize=(12, 10))
_fig, ax = plt.subplots(ncols=2, nrows=2, figsize=(12, 10))
for i, (k, v) in enumerate(data["values"].items()):
row = int(np.floor(i / 2))
col = i % 2
Expand Down Expand Up @@ -629,9 +629,7 @@ def correct(self, directory: str = "./"):
# restart the job at the point it stopped while forcing Gaussian
# to rebuild the set of redundant internals
if not list(filter(re.compile(r"%[Cc][Hh][Kk]").match, self.gin.link0_parameters.keys())):
raise KeyError(
"This remedy reads coords from checkpoint " "file. Consider adding CHK to link0_parameters"
)
raise KeyError("This remedy reads coords from checkpoint file. Consider adding CHK to link0_parameters")
self.gin = GaussianInput(
mol=None,
charge=self.gin.charge,
Expand Down Expand Up @@ -661,7 +659,7 @@ def correct(self, directory: str = "./"):
self.gin.input_parameters.update({"surface": "SAS"})
actions.append({"surface": "SAS"})
else:
self.logger.info("Not sure how to fix " "solute_solvent_surface_error if surface is " "already SAS!")
self.logger.info("Not sure how to fix solute_solvent_surface_error if surface is already SAS!")
return {"errors": [self.errors], "actions": None}

elif "internal_coords" in self.errors:
Expand Down Expand Up @@ -700,22 +698,22 @@ def correct(self, directory: str = "./"):
if set(last_lines) != {"\n"}:
# if the required blank lines at the end of the input file are
# missing, just rewrite the file
self.logger.info("Missing blank line at the end of the input " "file.")
self.logger.info("Missing blank line at the end of the input file.")
actions.append({"blank_lines": "rewrite_input_file"})
else:
self.logger.info("Not sure how to fix zmatrix error. " "Check manually?")
self.logger.info("Not sure how to fix zmatrix error. Check manually?")
return {"errors": [self.errors], "actions": None}

elif "coords" in self.errors:
if "connectivity" in self.gin.route_parameters.get("geom"):
self.logger.info("Explicit atom bonding is requested but no " "such input is provided")
self.logger.info("Explicit atom bonding is requested but no such input is provided")
if isinstance(self.gin.route_parameters["geom"], dict) and len(self.gin.route_parameters["geom"]) > 1:
self.gin.route_parameters["geom"].pop("connectivity", None)
else:
del self.gin.route_parameters["geom"]
actions.append({"coords": "remove_connectivity"})
else:
self.logger.info("Missing connectivity info. Not sure how to " "fix this error. Exiting!")
self.logger.info("Missing connectivity info. Not sure how to fix this error. Exiting!")
return {"errors": [self.errors], "actions": None}

elif "found_coords" in self.errors:
Expand All @@ -728,9 +726,7 @@ def correct(self, directory: str = "./"):
self.gin._mol = None
actions.append({"mol": "remove_from_input"})
else:
self.logger.info(
"Not sure why atom specifications should not " "be found in the input. Examine manually!"
)
self.logger.info("Not sure why atom specifications should not be found in the input. Examine manually!")
return {"errors": [self.errors], "actions": None}

elif "coord_inputs" in self.errors:
Expand Down Expand Up @@ -764,20 +760,18 @@ def correct(self, directory: str = "./"):
# initial guess be read from the checkpoint file but forgot to
# take the geom from the checkpoint file, add geom=check
if not glob.glob("*.[Cc][Hh][Kk]"):
raise FileNotFoundError(
"This remedy reads geometry from " "checkpoint file. This file is " "missing!"
)
raise FileNotFoundError("This remedy reads geometry from checkpoint file. This file is missing!")
GaussianErrorHandler._update_route_params(self.gin.route_parameters, "geom", "check")
self.gin.route_parameters["geom"] = "check"
actions.append({"mol": "get_from_checkpoint"})
else:
# error cannot be fixed automatically. Return None for actions
self.logger.info("Molecule is not found in the input file. " "Fix manually!")
self.logger.info("Molecule is not found in the input file. Fix manually!")
# TODO: check if logger.info is enough here or return is needed
return {"errors": list(self.errors), "actions": None}

elif any(err in self.errors for err in ["empty_file", "bad_file"]):
self.logger.error("Required checkpoint file is bad. Fix " "manually!")
self.logger.error("Required checkpoint file is bad. Fix manually!")
return {"errors": list(self.errors), "actions": None}

elif "missing_file" in self.errors:
Expand All @@ -786,7 +780,7 @@ def correct(self, directory: str = "./"):

elif "syntax" in self.errors:
# error cannot be fixed automatically. Return None for actions
self.logger.info("A syntax error was detected in the input file. " "Fix manually!")
self.logger.info("A syntax error was detected in the input file. Fix manually!")
return {"errors": list(self.errors), "actions": None}

elif "insufficient_mem" in self.errors:
Expand All @@ -797,11 +791,11 @@ def correct(self, directory: str = "./"):
self.gin.link0_parameters[mem_key] = f"{mem}MB"
actions.append({"memory": "increase_to_gaussian_recommendation"})
else:
self.logger.info("Check job memory requirements manually and " "set as needed.")
self.logger.info("Check job memory requirements manually and set as needed.")
return {"errors": list(self.errors), "actions": None}

else:
self.logger.info("Must have gotten an error that is parsed but not " "handled yet. Fix manually!")
self.logger.info("Must have gotten an error that is parsed but not handled yet. Fix manually!")
return {"errors": list(self.errors), "actions": None}

os.rename(
Expand Down Expand Up @@ -894,6 +888,6 @@ def correct(self, directory: str = "./") -> dict:
f.write("\n".join(input_str))
return {"errors": ["wall_time_limit"], "actions": None}
self.logger.info(
"Wall time handler requires a read-write gaussian " "file to be available. No such file is found."
"Wall time handler requires a read-write gaussian file to be available. No such file is found."
)
return {"errors": ["Walltime reached but not rwf file found"], "actions": None}
8 changes: 4 additions & 4 deletions tests/gaussian/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def gunzip_file(gauss_file):

class TestGaussianErrorHandler(TestCase):
def setUp(self):
os.makedirs(SCR_DIR)
os.makedirs(SCR_DIR, exist_ok=True)
os.chdir(SCR_DIR)

def test_opt_steps_cycles(self):
Expand Down Expand Up @@ -318,7 +318,7 @@ def tearDown(self):

class TestWallTimeErrorHandler(TestCase):
def setUp(self):
os.makedirs(SCR_DIR)
os.makedirs(SCR_DIR, exist_ok=True)
os.chdir(SCR_DIR)
os.environ.pop("JOB_START_TIME", None)
gunzip_file(f"{TEST_DIR}/walltime.out.gz")
Expand All @@ -333,15 +333,15 @@ def test_walltime_init(self):
output_file="wall_time.out",
)
init_time = handler.init_time
assert os.environ.get("JOB_START_TIME") == init_time.strftime("%a %b %d %H:%M:%S UTC %Y")
assert os.environ.get("JOB_START_TIME") == f"{init_time:%a %b %d %H:%M:%S UTC %Y}"
# Test that walltime persists if new handler is created
handler = WallTimeErrorHandler(
wall_time=3600,
buffer_time=300,
input_file="walltime.com",
output_file="walltime.out",
)
assert os.environ.get("JOB_START_TIME") == init_time.strftime("%a %b %d %H:%M:%S UTC %Y")
assert os.environ.get("JOB_START_TIME") == f"{init_time:%a %b %d %H:%M:%S UTC %Y}"

def test_walltime_check_and_correct(self):
# Try a 1 hr wall time with a 5 mins buffer
Expand Down
12 changes: 6 additions & 6 deletions tests/gaussian/test_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def setUp(self):
self.backup = True
self.directory = SCR_DIR

os.makedirs(SCR_DIR)
os.makedirs(SCR_DIR, exist_ok=True)
shutil.copyfile(f"{TEST_DIR}/mol_opt.com", f"{SCR_DIR}/test.com")
os.chdir(SCR_DIR)

Expand All @@ -42,26 +42,26 @@ def tearDown(self):
os.remove(file_path)

def test_normal(self):
g = GaussianJob(
job = GaussianJob(
self.gaussian_cmd,
self.input_file,
self.output_file,
self.stderr_file,
self.suffix,
self.backup,
)
g.setup()
job.setup()
assert os.path.exists(f"{SCR_DIR}/test.com.orig")
if not os.path.exists(f"{TEST_DIR}/mol_opt.out") and os.path.exists(f"{TEST_DIR}/mol_opt.out.gz"):
with gzip.open(f"{TEST_DIR}/mol_opt.out.gz", "rb") as f_in, open(f"{TEST_DIR}/mol_opt.out", "wb") as f_out:
shutil.copyfileobj(f_in, f_out)
shutil.copy(f"{TEST_DIR}/mol_opt.out", f"{SCR_DIR}/test.out")
g.postprocess()
job.postprocess()
assert os.path.exists(f"{SCR_DIR}/test.com{self.suffix}")
assert os.path.exists(f"{SCR_DIR}/test.out{self.suffix}")

def test_better_guess(self):
g = GaussianJob.generate_better_guess(
job_gen = GaussianJob.generate_better_guess(
self.gaussian_cmd,
self.input_file,
self.output_file,
Expand All @@ -70,7 +70,7 @@ def test_better_guess(self):
True,
self.directory,
)
jobs = list(g)
jobs = list(job_gen)
assert len(jobs) == 1, "One job should be generated under normal conditions."
jobs[0].setup()
assert os.path.exists(f"{SCR_DIR}/test.com.orig")
Expand Down

0 comments on commit 67c79dd

Please sign in to comment.