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

Elements 87 to 103 are now accessible via the element composition #62

Merged
merged 10 commits into from
Oct 17, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for the novel "g-xTB" method (working title: GP3-xTB)
- A function which contracts the coordinates after the initial generation.
- A function which is able to printout the xyz coordinates to the terminal similar to the `.xyz` layout.
- Elements 87 to 103 are accessible via the element composition. If `xtb` is the engine, the elements will be replaced by their lighter homologues.

### Breaking Changes
- Removal of the `dist_threshold` flag and in the `-toml` file.
Expand Down
15 changes: 14 additions & 1 deletion src/mindlessgen/generator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
from pathlib import Path
import multiprocessing as mp
import warnings
import numpy as np

from ..molecules import generate_random_molecule, Molecule
from ..molecules import generate_random_molecule, Molecule, get_lanthanides
from ..qm import XTB, get_xtb_path, QMMethod, ORCA, get_orca_path, GP3, get_gp3_path
from ..molecules import iterative_optimization, postprocess_mol
from ..molecules.miscellaneous import get_actinides
from ..prog import ConfigManager

from .. import __version__
Expand Down Expand Up @@ -209,6 +211,7 @@ def single_molecule_generator(
config_refine=config.refine,
verbosity=config.general.verbosity,
)

jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
except RuntimeError as e:
if config.general.verbosity > 0:
print(f"Refinement failed for cycle {cycle + 1}.")
Expand Down Expand Up @@ -239,6 +242,16 @@ def single_molecule_generator(
if config.general.verbosity > 1:
print("Postprocessing successful.")

if isinstance(refine_engine, XTB):
if np.any(np.isin(optimized_molecule.ati, get_lanthanides() + get_actinides())):
print(config.warnings.get_warning()[0])

if np.any(optimized_molecule.ati > 85):
print(config.warnings.get_warning()[1])

if postprocess_engine is None:
print(config.warnings.get_warning()[2])

jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
if not stop_event.is_set():
stop_event.set() # Signal other processes to stop
return optimized_molecule
Expand Down
22 changes: 17 additions & 5 deletions src/mindlessgen/molecules/miscellaneous.py
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ def set_random_charge(ati: np.ndarray, verbosity: int = 1) -> tuple[int, int]:
if verbosity > 1:
print(f"Number of protons in molecule: {nel}")

if np.any(np.isin(ati, get_lanthanides())):
### Special mode for lanthanides
if np.any(np.isin(ati, get_lanthanides() + get_actinides())):
### Special mode for lanthanides and actinides
# -> always high spin
# -> Divide the molecule into Ln3+ ions and negative "ligands"
# -> Divide the molecule into Ln3+/Ac3+ ions and negative "ligands"
# -> The ligands are the remaining protons are assumed to be low spin
uhf = 0
charge = 0
ln_protons = 0
ac_protons = 0
for atom in ati:
if atom in get_lanthanides():
if atom < 64:
Expand All @@ -33,9 +34,20 @@ def set_random_charge(ati: np.ndarray, verbosity: int = 1) -> tuple[int, int]:
ln_protons += (
atom - 3 + 1
) # subtract 3 to get the number of protons in the Ln3+ ion
ligand_protons = nel - ln_protons
if atom in get_actinides():
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
if atom < 96:
uhf += atom - 88
else:
uhf += 102 - atom
ac_protons += (
atom - 3 + 1
) # subtract 3 to get the number of protons in the Ln3+ ion
ligand_protons = nel - ln_protons - ac_protons
if verbosity > 2:
print(f"Number of protons from Ln^3+ ions: {ln_protons}")
if np.any(np.isin(ati, get_lanthanides())):
print(f"Number of protons from Ln^3+ ions: {ln_protons}")
if np.any(np.isin(ati, get_actinides())):
print(f"Number of protons from Ac^3+ ions: {ac_protons}")
print(
f"Number of protons from ligands (assuming negative charge): {ligand_protons}"
)
Expand Down
20 changes: 20 additions & 0 deletions src/mindlessgen/prog/config.py
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,25 @@ def scf_cycles(self, max_scf_cycles: int):
self._scf_cycles = max_scf_cycles


class WarningConfig:
"""
This class handles warnings related to xTB calculations.
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
"""

def __init__(self) -> None:
self.warnings = [
"WARNING: f-block elements are within the molecule. xTB does not treat f electrons explicitly. UHF is set to 0.",
"WARNING: Super heavy elements are within the molecule. xTB does not treat super havy elements. Atomic numbers are reduced by 32.",
"WARNING: Postproccessing is turned off the structure will not be relaxed.",
]

def get_warning(self) -> list[str]:
"""
Get the list of warnings.
"""
return self.warnings


class ConfigManager:
"""
Overall configuration manager for the program.
Expand All @@ -731,6 +750,7 @@ def __init__(self, config_file: str | Path | None = None):
self.refine = RefineConfig()
self.postprocess = PostProcessConfig()
self.generate = GenerateConfig()
self.warnings = WarningConfig()

if config_file:
self.load_from_toml(config_file)
Expand Down
18 changes: 18 additions & 0 deletions src/mindlessgen/qm/xtb.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ def optimize(
"""
Optimize a molecule using xtb.
"""
super_heavy_elements = False
ati_original = molecule.ati.copy()
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
if np.any(molecule.ati > 85):
super_heavy_elements = True
molecule.ati[molecule.ati > 85] -= 32
if np.any(np.isin(molecule.ati, get_lanthanides())):
check_ligand_uhf(molecule.ati, molecule.charge)
# Store the original UHF value and set uhf to 0
Expand Down Expand Up @@ -82,12 +87,22 @@ def optimize(
if np.any(np.isin(molecule.ati, get_lanthanides())):
# Reset the UHF value to the original value before returning the optimized molecule.
optimized_molecule.uhf = uhf_original
if super_heavy_elements:
# Reset the atomic numbers to the original values before returning the optimized molecule.
optimized_molecule.ati = ati_original
molecule.ati = ati_original
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
optimized_molecule.atlist = molecule.atlist
return optimized_molecule

def singlepoint(self, molecule: Molecule, verbosity: int = 1) -> str:
"""
Perform a single-point calculation using xtb.
"""
super_heavy_elements = False
ati_original = molecule.ati.copy()
if np.any(molecule.ati > 85):
super_heavy_elements = True
molecule.ati[molecule.ati > 85] -= 32
if np.any(np.isin(molecule.ati, get_lanthanides())):
check_ligand_uhf(molecule.ati, molecule.charge)
# Store the original UHF value and set uhf to 0
Expand Down Expand Up @@ -128,6 +143,9 @@ def singlepoint(self, molecule: Molecule, verbosity: int = 1) -> str:

if np.any(np.isin(molecule.ati, get_lanthanides())):
molecule.uhf = uhf_original
if super_heavy_elements:
# Reset the atomic numbers to the original values before returning the optimized molecule.
molecule.ati = ati_original
return xtb_log_out

def check_gap(
Expand Down