diff --git a/interfaces/adfsuite/ams.py b/interfaces/adfsuite/ams.py index a67acd4e..5a6592e3 100644 --- a/interfaces/adfsuite/ams.py +++ b/interfaces/adfsuite/ams.py @@ -2483,27 +2483,49 @@ def get_errormsg(self) -> Optional[str]: if self.check(): return None else: - # Something went wrong. The first place to check is the termination status on the ams.rkf. - # If the AMS driver stopped with a known error (called StopIt in the Fortran code), the error will be in there. + default_msg = "Could not determine error message. Please check the output manually." + msg = None try: - msg = self.results.readrkf("General", "termination status") - if msg == "NORMAL TERMINATION with errors" or msg is None: - # Apparently this wasn't a hard stop in the middle of the job. + # Something went wrong. The first place to check is the termination status on the ams.rkf. + # If the AMS driver stopped with a known error (called StopIt in the Fortran code), the error will be in there. + # If there is no rkf file, the msg will be None + termination_status = self.results.readrkf("General", "termination status") + if termination_status == "NORMAL TERMINATION with errors" or termination_status is None: + # Apparently this wasn't a hard stop in the middle of the job # Let's look for the last error in the logfile ... - msg = self.results.grep_file("ams.log", "ERROR: ")[-1].partition("ERROR: ")[2] - elif msg == "IN PROGRESS" and "$JN.err" in self.results: + try: + log_err_lines = self.results.grep_file("ams.log", "ERROR: ") + if log_err_lines: + return log_err_lines[-1].partition("ERROR: ")[2] + except FileError: + pass + + # For a licensing issue, check the output logs directly + try: + license_err_lines = self.results.get_output_chunk( + begin="LICENSE INVALID", + end="License file", + inc_begin=True, + inc_end=True, + match=1, + ) + if license_err_lines: + return str.join("\n", license_err_lines) + except FileError: + pass + elif termination_status == "IN PROGRESS" and "$JN.err" in self.results: # If the status is still "IN PROGRESS", that probably means AMS was shut down hard from the outside. # E.g. it got SIGKILL from the scheduler for exceeding some resource limit. # In this case useful information may be found on stderr. - with open(self.results["$JN.err"], "r") as err: + with open(self.results["$JN.err"]) as err: errlines = err.read().splitlines() for el in reversed(errlines): if el != "" and not el.isspace(): msg = "Killed while IN PROGRESS: " + el break except: - msg = "Could not determine error message. Please check the output manually." - return msg + pass + return msg if msg else default_msg def hash_input(self) -> str: """Calculate the hash of the input file. diff --git a/unit_tests/test_amsjob.py b/unit_tests/test_amsjob.py index 60dd079e..769b4a28 100644 --- a/unit_tests/test_amsjob.py +++ b/unit_tests/test_amsjob.py @@ -563,3 +563,50 @@ def get_runscript() -> str: assert len(status_lines) == 4 logger.close() + + def test_get_errormsg_populates_correctly_for_different_scenarios(self): + from scm.plams.core.errors import FileError + + # Invalid license + job = AMSJob() + results = MagicMock(spec=AMSResults) + results.readrkf.return_value = None + results.grep_file.side_effect = FileError() + results.get_output_chunk.return_value = [ + "LICENSE INVALID", + "---------------", + "Your license does not include module AMS version 2024.206 on this machine.", + "Module AMS", + "Version 2024.206", + "Machine: Foo", + "License file: ./license.txt", + ] + job.results = results + + assert ( + job.get_errormsg() + == """LICENSE INVALID +--------------- +Your license does not include module AMS version 2024.206 on this machine. +Module AMS +Version 2024.206 +Machine: Foo +License file: ./license.txt""" + ) + + # Invalid input + results.grep_file.side_effect = None + results.grep_file.return_value = [ + ' <12:03:49> ERROR: Input error: value "foo" found in line 13 for multiple choice key "Task" is not an allowed choice' + ] + assert ( + job.get_errormsg() + == 'Input error: value "foo" found in line 13 for multiple choice key "Task" is not an allowed choice' + ) + + # Error in calculation + results.readrkf.return_value = "NORMAL TERMINATION with errors" + results.grep_file.return_value = [ + " <13:44:55> ERROR: Geometry optimization failed! (Did not converge.)" + ] + assert job.get_errormsg() == "Geometry optimization failed! (Did not converge.)"