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

Update upstream with dev/gp3_ipea updates #107

Closed
wants to merge 16 commits into from
Closed
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### 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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ def main():
config.generate.max_num_atoms = 15
config.generate.element_composition = "Ce:1-1"
# alternatively as a dictionary: config.generate.element_composition = {39:(1,1)}
# or: config.generate.element_composition = {"Ce":(1,1)"}
# or as mixed-key dict, e.g. for Ce and O: {"Ce":(1,1), 7:(2,2)}
config.generate.forbidden_elements = "21-30,39-48,57-80"
# alternatively as a list: config.generate.forbidden_elements = [20,21,22,23] # 24,25,26...

Expand Down
18 changes: 18 additions & 0 deletions src/mindlessgen/cli/cli_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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"],
Expand Down
109 changes: 79 additions & 30 deletions src/mindlessgen/generator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import warnings

from ..molecules import generate_random_molecule, Molecule
from ..qm import XTB, get_xtb_path, QMMethod, ORCA, get_orca_path, GP3, get_gp3_path
from ..qm import XTB, get_xtb_path, QMMethod, ORCA, get_orca_path, GXTB, get_gxtb_path
from ..molecules import iterative_optimization, postprocess_mol
from ..prog import ConfigManager

Expand Down Expand Up @@ -45,12 +45,16 @@ def generator(config: ConfigManager) -> tuple[list[Molecule] | None, int]:
config.refine.engine,
config,
get_xtb_path,
get_orca_path, # GP3 cannot be used anyway
get_orca_path, # g-xTB cannot be used anyway
)

if config.general.postprocess:
postprocess_engine: QMMethod | None = setup_engines(
config.postprocess.engine, config, get_xtb_path, get_orca_path, get_gp3_path
config.postprocess.engine,
config,
get_xtb_path,
get_orca_path,
get_gxtb_path,
)
else:
postprocess_engine = None
Expand Down Expand Up @@ -228,22 +232,41 @@ def single_molecule_generator(

if config.general.gxtb_development:
# additional g-xTB engine after refinement and postprocessing for development purposes
gp3 = GP3(get_gp3_path())
try:
_gxtb_dev_check(
optimized_molecule,
gp3,
config.general.gxtb_scf_cycles,
config.general.verbosity,
)
except (RuntimeError, ValueError) as e:
if config.general.verbosity > 0:
print(f"g-xTB postprocessing failed for cycle {cycle + 1}.")
if config.general.verbosity > 1:
print(e)
return None
if config.general.verbosity > 1:
print("g-xTB postprocessing successful.")
gxtb = GXTB(get_gxtb_path(), config.gxtb)
if not config.general.postprocess and config.postprocess.engine == "gxtb":
try:
_gxtb_scf_check(
optimized_molecule,
gxtb,
config.general.gxtb_scf_cycles,
config.general.verbosity,
)
except (RuntimeError, ValueError) as e:
if config.general.verbosity > 0:
print(
f"g-xTB postprocessing (SCF cycles check) failed for cycle {cycle + 1}."
)
if config.general.verbosity > 1:
print(e)
return None
if config.general.verbosity > 1:
print("g-xTB postprocessing (SCF cycles check) successful.")
if config.general.gxtb_ipea:
try:
_gxtb_ipea_check(
optimized_molecule,
gxtb,
config.general.gxtb_scf_cycles,
config.general.verbosity,
)
except (RuntimeError, ValueError) as e:
if config.general.verbosity > 0:
print(f"g-xTB postprocessing failed for cycle {cycle + 1}.")
if config.general.verbosity > 1:
print(e)
return None
if config.general.verbosity > 1:
print("g-xTB postprocessing successful.")

if not stop_event.is_set():
stop_event.set() # Signal other processes to stop
Expand Down Expand Up @@ -285,7 +308,7 @@ def setup_engines(
cfg: ConfigManager,
xtb_path_func: Callable,
orca_path_func: Callable,
gp3_path_func: Callable | None = None,
gxtb_path_func: Callable | None = None,
):
"""
Set up the required engine.
Expand All @@ -306,19 +329,19 @@ def setup_engines(
except ImportError as e:
raise ImportError("orca not found.") from e
return ORCA(path, cfg.orca)
elif engine_type == "gp3":
if gp3_path_func is None:
raise ImportError("No callable function for determining the gp3 path.")
path = gp3_path_func()
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(cfg.gxtb.gxtb_path)
if not path:
raise ImportError("'gp3' binary could not be found.")
return GP3(path)
raise ImportError("'gxtb' binary could not be found.")
return GXTB(path, cfg.gxtb)
else:
raise NotImplementedError("Engine not implemented.")


def _gxtb_dev_check(
mol: Molecule, gp3: GP3, scf_iter_limit: int, verbosity: int = 0
def _gxtb_ipea_check(
mol: Molecule, gxtb: GXTB, scf_iter_limit: int, verbosity: int = 0
) -> None:
"""
ONLY FOR IN-HOUSE g-xTB DEVELOPMENT PURPOSES: Check the SCF iterations of the cation and anion.
Expand All @@ -333,7 +356,7 @@ def _gxtb_dev_check(
+ "For the g-xTB cationic calculation, we are increasing it by 1. "
+ "(Could be ill-defined.)"
)
gxtb_output = gp3.singlepoint(tmp_mol, verbosity=verbosity)
gxtb_output = gxtb.singlepoint(tmp_mol, verbosity=verbosity)
# gp3_output looks like this:
# [...]
# 13 -155.03101038 0.00000000 0.00000001 16.45392733 8 F
Expand Down Expand Up @@ -361,7 +384,33 @@ def _gxtb_dev_check(
+ "For the g-xTB anionic calculation, we are increasing it by 1. "
+ "(Could be ill-defined.)"
)
gxtb_output = gp3.singlepoint(tmp_mol, verbosity=verbosity)
gxtb_output = gxtb.singlepoint(tmp_mol, verbosity=verbosity)
# Check for the number of scf iterations
scf_iterations = 0
for line in gxtb_output.split("\n"):
if "scf iterations" in line:
scf_iterations = int(line.split()[0])
break
if scf_iterations == 0:
raise ValueError("SCF iterations not found in GP3 output.")
if scf_iterations > scf_iter_limit:
raise ValueError(f"SCF iterations exceeded limit of {scf_iter_limit}.")


def _gxtb_scf_check(
mol: Molecule, gxtb: GXTB, scf_iter_limit: int, verbosity: int = 0
) -> None:
"""
ONLY FOR IN-HOUSE g-xTB DEVELOPMENT PURPOSES: Check the SCF iterations with g-xTB.
"""
# 1) Single point calculation with g-xTB for the cation
gxtb_output = gxtb.singlepoint(mol, verbosity=verbosity)
# 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_output.split("\n"):
Expand Down
2 changes: 1 addition & 1 deletion src/mindlessgen/molecules/postprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions src/mindlessgen/prog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
GeneralConfig,
XTBConfig,
ORCAConfig,
GXTBConfig,
GenerateConfig,
RefineConfig,
PostProcessConfig,
Expand All @@ -17,6 +18,7 @@
"GeneralConfig",
"XTBConfig",
"ORCAConfig",
"GXTBConfig",
"GenerateConfig",
"RefineConfig",
"PostProcessConfig",
Expand Down
Loading