From 8d61b96ddf2030f104e8ebc60237d050cbd998f1 Mon Sep 17 00:00:00 2001 From: Marcel Mueller Date: Mon, 13 Jan 2025 11:28:42 +0100 Subject: [PATCH 1/4] add real config class for g-xTB Signed-off-by: Marcel Mueller --- src/mindlessgen/cli/cli_parser.py | 18 ++++++++++++ src/mindlessgen/generator/main.py | 4 +-- src/mindlessgen/prog/__init__.py | 2 ++ src/mindlessgen/prog/config.py | 48 +++++++++++++++++++++++++++++++ src/mindlessgen/qm/gxtb.py | 3 +- 5 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/mindlessgen/cli/cli_parser.py b/src/mindlessgen/cli/cli_parser.py index 12f02e2..46bb365 100644 --- a/src/mindlessgen/cli/cli_parser.py +++ b/src/mindlessgen/cli/cli_parser.py @@ -258,6 +258,19 @@ def cli_parser(argv: Sequence[str] | None = None) -> dict: required=False, help="Maximum number of SCF cycles in ORCA.", ) + ### g-xTB specific arguments ### + parser.add_argument( + "--gxtb-path", + type=str, + required=False, + help="Path to the g-xTB binary.", + ) + parser.add_argument( + "--gxtb-scf-cycles", + type=int, + required=False, + help="Maximum number of SCF cycles in g-xTB.", + ) args = parser.parse_args(argv) args_dict = vars(args) @@ -306,6 +319,11 @@ def cli_parser(argv: Sequence[str] | None = None) -> dict: "gridsize": args_dict["orca_gridsize"], "scf_cycles": args_dict["orca_scf_cycles"], } + # g-xTB specific arguments + rev_args_dict["gxtb"] = { + "gxtb_path": args_dict["gxtb_path"], + "scf_cycles": args_dict["gxtb_scf_cycles"], + } # Postprocessing arguments rev_args_dict["postprocess"] = { "engine": args_dict["postprocess_engine"], diff --git a/src/mindlessgen/generator/main.py b/src/mindlessgen/generator/main.py index 95d7c93..5094f8a 100644 --- a/src/mindlessgen/generator/main.py +++ b/src/mindlessgen/generator/main.py @@ -294,9 +294,9 @@ def setup_engines( elif engine_type == "gxtb": if gxtb_path_func is None: raise ImportError("No callable function for determining the g-xTB path.") - path = gxtb_path_func() + path = gxtb_path_func(cfg.gxtb.gxtb_path) if not path: raise ImportError("'gxtb' binary could not be found.") - return GXTB(path) + return GXTB(path, cfg.gxtb) else: raise NotImplementedError("Engine not implemented.") diff --git a/src/mindlessgen/prog/__init__.py b/src/mindlessgen/prog/__init__.py index 8dee820..3ccbccb 100644 --- a/src/mindlessgen/prog/__init__.py +++ b/src/mindlessgen/prog/__init__.py @@ -7,6 +7,7 @@ GeneralConfig, XTBConfig, ORCAConfig, + GXTBConfig, GenerateConfig, RefineConfig, PostProcessConfig, @@ -17,6 +18,7 @@ "GeneralConfig", "XTBConfig", "ORCAConfig", + "GXTBConfig", "GenerateConfig", "RefineConfig", "PostProcessConfig", diff --git a/src/mindlessgen/prog/config.py b/src/mindlessgen/prog/config.py index 22713f6..31cd626 100644 --- a/src/mindlessgen/prog/config.py +++ b/src/mindlessgen/prog/config.py @@ -925,6 +925,53 @@ def scf_cycles(self, max_scf_cycles: int): self._scf_cycles = max_scf_cycles +class GXTBConfig(BaseConfig): + """ + Configuration class for g-xTB. + """ + + def __init__(self: GXTBConfig) -> None: + self._gxtb_path: str | Path = "gxtb" + self._scf_cycles: int = 100 + + def get_identifier(self) -> str: + return "gxtb" + + @property + def gxtb_path(self): + """ + Get the g-xTB path. + """ + return self._gxtb_path + + @gxtb_path.setter + def gxtb_path(self, gxtb_path: str | Path): + """ + Set the g-xTB path. + """ + if not isinstance(gxtb_path, str | Path): + raise TypeError("gxtb_path should be a string or Path.") + self._gxtb_path = gxtb_path + + @property + def scf_cycles(self): + """ + Get the maximum number of SCF cycles. + """ + return self._scf_cycles + + @scf_cycles.setter + def scf_cycles(self, max_scf_cycles: int): + """ + Set the maximum number of SCF cycles. + """ + if not isinstance(max_scf_cycles, int): + raise TypeError("Max SCF cycles should be an integer.") + if max_scf_cycles < 1: + raise ValueError("Max SCF cycles should be greater than 0.") + self._scf_cycles = max_scf_cycles + + class ConfigManager: """ Overall configuration manager for the program. @@ -940,6 +987,7 @@ def __init__(self, config_file: str | Path | None = None): self.refine = RefineConfig() self.postprocess = PostProcessConfig() self.generate = GenerateConfig() + self.gxtb = GXTBConfig() if config_file: self.load_from_toml(config_file) diff --git a/src/mindlessgen/qm/gxtb.py b/src/mindlessgen/qm/gxtb.py index 47ba65f..4f996a9 100644 --- a/src/mindlessgen/qm/gxtb.py +++ b/src/mindlessgen/qm/gxtb.py @@ -9,6 +9,7 @@ from tempfile import TemporaryDirectory from ..molecules import Molecule +from ..prog import GXTBConfig from .base import QMMethod @@ -17,7 +18,7 @@ class GXTB(QMMethod): This class handles all interaction with the g-xTB external dependency. """ - def __init__(self, path: str | Path) -> None: + def __init__(self, path: str | Path, gxtbcfg: GXTBConfig) -> None: """ Initialize the GXTB class. """ From c9e86b34c859fca5eca08f35798c5785e4268873 Mon Sep 17 00:00:00 2001 From: Marcel Mueller Date: Mon, 13 Jan 2025 12:56:45 +0100 Subject: [PATCH 2/4] add SCF cycles check for g-xTB Signed-off-by: Marcel Mueller --- src/mindlessgen/molecules/postprocess.py | 2 +- src/mindlessgen/qm/gxtb.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/mindlessgen/molecules/postprocess.py b/src/mindlessgen/molecules/postprocess.py index 17f6ae4..4189cc9 100644 --- a/src/mindlessgen/molecules/postprocess.py +++ b/src/mindlessgen/molecules/postprocess.py @@ -37,6 +37,6 @@ def postprocess_mol( postprocmol = mol except RuntimeError as e: raise RuntimeError( - "Single point calculation in postprocessing failed." + f"Single point calculation in postprocessing failed with error: {e}" ) from e return postprocmol diff --git a/src/mindlessgen/qm/gxtb.py b/src/mindlessgen/qm/gxtb.py index 4f996a9..79de5ff 100644 --- a/src/mindlessgen/qm/gxtb.py +++ b/src/mindlessgen/qm/gxtb.py @@ -28,6 +28,7 @@ def __init__(self, path: str | Path, gxtbcfg: GXTBConfig) -> None: self.path = path else: raise TypeError("gxtb_path should be a string or a Path object.") + self.cfg = gxtbcfg def singlepoint(self, molecule: Molecule, verbosity: int = 1) -> str: """ @@ -65,6 +66,24 @@ def singlepoint(self, molecule: Molecule, verbosity: int = 1) -> str: raise RuntimeError( f"g-xTB failed with return code {return_code}:\n{gxtb_log_err}" ) + # gp3_output looks like this: + # [...] + # 13 -155.03101038 0.00000000 0.00000001 16.45392733 8 F + # 13 scf iterations + # eigenvalues + # [...] + # Check for the number of scf iterations + scf_iterations = 0 + for line in gxtb_log_out.split("\n"): + if "scf iterations" in line: + scf_iterations = int(line.split()[0]) + break + if scf_iterations == 0: + raise RuntimeError("SCF iterations not found in GP3 output.") + if scf_iterations > self.cfg.scf_cycles: + raise RuntimeError( + f"SCF iterations exceeded limit of {self.cfg.scf_cycles}." + ) return gxtb_log_out From 0f87c66cd348e45bd96af0b1b774d811851acfdf Mon Sep 17 00:00:00 2001 From: Marcel Mueller Date: Mon, 13 Jan 2025 13:00:24 +0100 Subject: [PATCH 3/4] adapt CHANGELOG.md accordingly Signed-off-by: Marcel Mueller --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e459cf..98b8c43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - to set the elemental composition it is now possible to use dicts with not only int but also the element symbols (str) - dict keys for elemental compositions will now always be checked for validity +- Renamed GP3-xTB to g-xTB + +### Added +- `GXTBConfig` class for the g-xTB method, supporting SCF cycles check + +### Fixed +- version string is now correctly formatted and printed ## [0.5.0] - 2024-12-16 ### Changed From 9b84c46cb516a6834502715d432ce22745a13216 Mon Sep 17 00:00:00 2001 From: Marcel Mueller Date: Mon, 13 Jan 2025 17:21:25 +0100 Subject: [PATCH 4/4] strip whitespace Signed-off-by: Marcel Mueller --- src/mindlessgen/qm/gxtb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mindlessgen/qm/gxtb.py b/src/mindlessgen/qm/gxtb.py index 79de5ff..970c6ca 100644 --- a/src/mindlessgen/qm/gxtb.py +++ b/src/mindlessgen/qm/gxtb.py @@ -76,7 +76,7 @@ def singlepoint(self, molecule: Molecule, verbosity: int = 1) -> str: scf_iterations = 0 for line in gxtb_log_out.split("\n"): if "scf iterations" in line: - scf_iterations = int(line.split()[0]) + scf_iterations = int(line.strip().split()[0]) break if scf_iterations == 0: raise RuntimeError("SCF iterations not found in GP3 output.")