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
24 changes: 3 additions & 21 deletions src/mindlessgen/generator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,12 @@ def generator(config: ConfigManager) -> tuple[list[Molecule] | None, int]:
if config.general.verbosity > 0:
print(config)

# lower number of the available cores and the configured parallelism
config.check_config(verbosity=config.general.verbosity)

num_cores = min(mp.cpu_count(), config.general.parallel)
if config.general.parallel > mp.cpu_count():
warnings.warn(
f"Number of cores requested ({config.general.parallel}) is greater "
+ f"than the number of available cores ({mp.cpu_count()})."
+ f"Using {num_cores} cores instead."
)
if config.general.verbosity > 0:
print(f"Running with {num_cores} cores.")

if num_cores > 1 and config.general.verbosity > 0:
# raise warning that parallelization will disable verbosity
warnings.warn(
"Parallelization will disable verbosity during iterative search. "
marcelmbn marked this conversation as resolved.
Show resolved Hide resolved
+ "Set '--verbosity 0' or '-P 1' to avoid this warning, or simply ignore it."
)
if num_cores > 1 and config.postprocess.debug:
# raise warning that debugging of postprocessing will disable parallelization
warnings.warn(
"Debug output might seem to be redundant due to the parallel processes "
+ "with possibly similar errors in parallel mode. "
+ "Don't be confused!"
)

# Check if the file "mindless.molecules" exists. If yes, append to it.
if Path(MINDLESS_MOLECULES_FILE).is_file():
if config.general.verbosity > 0:
Expand Down Expand Up @@ -209,6 +190,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
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
elif atom in get_actinides():
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
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
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
62 changes: 62 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 @@ -6,6 +6,8 @@

from pathlib import Path
from abc import ABC, abstractmethod
import warnings
import multiprocessing as mp
import toml

from ..molecules import PSE_NUMBERS
Expand Down Expand Up @@ -735,6 +737,66 @@ def __init__(self, config_file: str | Path | None = None):
if config_file:
self.load_from_toml(config_file)

def check_config(self, verbosity: int = 1) -> None:
"""
Checks ConfigClass for any incompatibilities that are imaginable
"""

# lower number of the available cores and the configured parallelism
num_cores = min(mp.cpu_count(), self.general.parallel)
if self.general.parallel > mp.cpu_count():
warnings.warn(
f"Number of cores requested ({self.general.parallel}) is greater "
+ f"than the number of available cores ({mp.cpu_count()})."
+ f"Using {num_cores} cores instead."
)

if num_cores > 1 and self.postprocess.debug:
# raise warning that debugging of postprocessing will disable parallelization
warnings.warn(
"Debug output might seem to be redundant due to the parallel processes "
+ "with possibly similar errors in parallel mode. "
+ "Don't be confused!"
)
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved

if verbosity > 0:
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
if num_cores > 1:
# raise warning that parallelization will disable verbosity
warnings.warn(
"Parallelization will disable verbosity during iterative search. "
+ "Set '--verbosity 0' or '-P 1' to avoid this warning, or simply ignore it."
)

# Check for f-block elements in forbidden elements
if self.generate.forbidden_elements:
f_block_elements = set(range(56, 70)) | set(range(88, 102))
if any(
elem not in f_block_elements
for elem in self.generate.forbidden_elements
):
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
warnings.warn(
"f-block elements could be within the molecule. xTB does not treat f electrons explicitly. In this case UHF is set to 0."
)

# Check for super heavy elements in forbidden elements
super_heavy_elements = set(range(86, 102))
if self.generate.element_composition and any(
elem in super_heavy_elements
for elem in self.generate.element_composition
):
warnings.warn(
"xTB does not treat super heavy elements. Approximation: atomic numbers are reduced by 32. MindlesGen terminates normally."
jonathan-schoeps marked this conversation as resolved.
Show resolved Hide resolved
)

# Check if postprocessing is turned off
if not self.general.postprocess and any(
elem in super_heavy_elements
for elem in self.generate.element_composition
marcelmbn marked this conversation as resolved.
Show resolved Hide resolved
):
warnings.warn(
"Postprocessing is turned off. The structure will not be relaxed."
)

def get_all_identifiers(self):
"""
Returns the identifiers of all subconfiguration classes, e.g. "orca", "refinement", ...
Expand Down
17 changes: 17 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,21 @@ 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
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 +142,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
16 changes: 15 additions & 1 deletion test/test_molecules/test_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 @@ -37,7 +37,18 @@
7,
), # Gd (64), C, C -> Lanthanide case, UHF = 7, CHRG = 1
(np.array([57, 7, 0]), [0], 1), # Ce (58), O, H -> Lanthanide case, UHF = 1
(np.array([69, 7, 0]), [0], 1), # Ce (58), O, H -> Lanthanide case, UHF = 1
(np.array([69, 7, 0]), [0], 1), # Yb (70), O, H -> Lanthanide case, UHF = 1
(np.array([5, 7, 0, 98]), [0], 4), # Es(99), O, C, H -> Actinides case, UHF = 4
(
np.array([59, 92, 0, 0]),
[0],
7,
), # Nd(60), Np(93), H, H -> Lanthanide and Actinide case, UHF = 7
(
np.array([59, 91, 92, 5, 7, 0]),
[0],
10,
), # Nd(60), U(92), Np(93), H, H -> Lanthanide and Actinides case, UHF = 10
],
ids=[
"B-N-H (standard, odd)",
Expand All @@ -49,6 +60,9 @@
"Gd-C-N (lanthanide)",
"Ce-O-H (lanthanide)",
"Yb-O-H (lanthanide)",
"Es-O-C-H (actinides)",
"Nd-Np-H-H (lanthanide and actinide)",
"Nd-Eu-Np-H-H (lanthanide and actinides)",
],
)
def test_set_random_charge(atom_types, expected_charges, expected_uhf):
Expand Down