From 719832b21608a349729750c50389c5b01e6c7022 Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Tue, 16 Apr 2024 12:17:05 -0700 Subject: [PATCH 01/16] New Q-Chem QIRC job; needs testing --- src/quacc/recipes/qchem/ts.py | 92 ++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index fd5ed019d4..6f276f0d7c 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -18,7 +18,7 @@ Sella = False if TYPE_CHECKING: - from typing import Any, Literal + from typing import Any, List, Literal from ase.atoms import Atoms @@ -229,3 +229,93 @@ def quasi_irc_job( relax_summary["initial_irc"] = irc_summary return relax_summary + + +@job +@requires(Sella, "Sella must be installed. Refer to the quacc documentation.") +def quasi_irc_perturb_job( + atoms: Atoms, + charge: int, + spin_multiplicity: int, + mode: List[List[float]], + perturb_magnitude: float = 0.6, + direction: Literal["forward", "reverse"] = "forward", + method: str = "wb97mv", + basis: str = "def2-svpd", + opt_params: dict[str, Any] | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, + **calc_kwargs, +) -> OptSchema: + """ + Quasi-IRC to optimize a reaction endpoint from a transition-state with known vibrational frequency modes. + Perturbs the structure of `atoms` by a finite amount (0.6 * the normalized mode magnitude) along the specified + vibrational frequency mode (assumed to be the transition mode), and then performs a `relax_job` on the perturbed + structure. + + Parameters + ---------- + atoms + Atoms object. + charge + Charge of the system. + spin_multiplicity + Multiplicity of the system. + mode + Transition mode + perturb_magnitude + Factor to multiply the transition mode. Default is 0.6. In some cases, it may be advisable to increase this + factor, perhaps to 1.0 or 1.1. Lowering it is not generally found to be helpful. + direction + Direction of the (Quasi)IRC. Should be "forward" or "reverse". + opt_params + Dictionary of custom kwargs for the optimization process. For a list + of available keys, refer to [quacc.runners.ase.run_opt][]. + copy_files + Files to copy (and decompress) from source to the runtime directory. + **calc_kwargs + Custom kwargs for the calculator. Set a value to `quacc.Remove` to remove + a pre-existing key entirely. See [quacc.calculators.qchem.qchem.QChem][] for more + details. + + + Returns + ------- + OptSchema + Dictionary of results from [quacc.schemas.ase.summarize_opt_run][] + """ + + def perturb(mol: Atoms, vector: List[List[float]], scale: float): + mol_copy = copy.deepcopy(mol) + mode_copy = copy.deepcopy(vector) + + orig_pos = mol_copy.get_positions() + + if not isinstance(mode_copy, np.ndarray): + mode_copy = np.asarray(mode_copy) + + pos = orig_pos + mode_copy * scale + mol_copy.set_positions(pos) + + return mol_copy + + calc_defaults = recursive_dict_merge( + _BASE_SET, {"rem": {"job_type": "force", "method": method, "basis": basis}} + ) + opt_defaults = {"optimizer": Sella} if has_sella else {} + + if direction == "forward": + scale = perturb_magnitude + else: + scale = perturb_magnitude * -1 + + return run_and_summarize_opt( + perturb(atoms, mode, scale), + charge=charge, + spin_multiplicity=spin_multiplicity, + calc_defaults=calc_defaults, + calc_swaps=calc_kwargs, + opt_defaults=opt_defaults, + opt_params=opt_params, + additional_fields={"name": "Q-Chem Quasi-IRC perturbed optimization"}, + copy_files=copy_files, + ) \ No newline at end of file From 9a1d296c22b7332498f905d90483139b4caac30f Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Tue, 16 Apr 2024 13:42:38 -0700 Subject: [PATCH 02/16] Added ORCA QIRC job; also slight reorganization --- src/quacc/recipes/orca/core.py | 85 ++++++++++++++++++++++++++++++++++ src/quacc/recipes/qchem/ts.py | 20 +++----- src/quacc/utils/coords.py | 23 +++++++++ 3 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 src/quacc/utils/coords.py diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index 53a4e71b9a..4c444c3503 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -8,6 +8,7 @@ from quacc import job from quacc.recipes.orca._base import run_and_summarize, run_and_summarize_opt +from quacc.utils.coords import perturb if TYPE_CHECKING: from typing import Any, Literal @@ -215,3 +216,87 @@ def ase_relax_job( additional_fields={"name": "ORCA ASE Relax"}, copy_files=copy_files, ) + + +@job +def ase_quasi_irc_perturb_job( + atoms: Atoms, + charge: int = 0, + spin_multiplicity: int = 1, + mode: List[List[float]], + perturb_magnitude: float = 0.6, + direction: Literal["forward", "reverse"] = "forward", + xc: str = "wb97x-d3bj", + basis: str = "def2-tzvp", + orcasimpleinput: list[str] | None = None, + orcablocks: list[str] | None = None, + opt_params: dict[str, Any] | None = None, + nprocs: int | Literal["max"] = "max", + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, +) -> cclibSchema: + """ + Quasi-IRC to optimize a reaction endpoint from a transition-state with known vibrational frequency modes. + Perturbs the structure of `atoms` by a finite amount (0.6 * the normalized mode magnitude) along the specified + vibrational frequency mode (assumed to be the transition mode), and then performs a `relax_job` on the perturbed + structure. + + Parameters + ---------- + atoms + Atoms object + charge + Charge of the system. + spin_multiplicity + Multiplicity of the system. + mode + Transition mode + perturb_magnitude + Factor to multiply the transition mode. Default is 0.6. In some cases, it may be advisable to increase this + factor, perhaps to 1.0 or 1.1. Lowering it is not generally found to be helpful. + direction + Direction of the (Quasi)IRC. Should be "forward" or "reverse". + xc + Exchange-correlation functional + basis + Basis set. + orcasimpleinput + List of `orcasimpleinput` swaps for the calculator. To remove entries + from the defaults, put a `#` in front of the name. Refer to the + [ase.calculators.orca.ORCA][] calculator for details on `orcasimpleinput`. + orcablocks + List of `orcablocks` swaps for the calculator. To remove entries + from the defaults, put a `#` in front of the name. Refer to the + [ase.calculators.orca.ORCA][] calculator for details on `orcablocks`. + nprocs + Number of processors to use. Defaults to the number of physical cores. + copy_files + Files to copy (and decompress) from source to the runtime directory. + + Returns + ------- + cclibASEOptSchema + Dictionary of results from [quacc.schemas.cclib.cclib_summarize_run][] merged with + the results from [quacc.schemas.ase.summarize_opt_run][]. + See the type-hint for the data structure. + """ + nprocs = psutil.cpu_count(logical=False) if nprocs == "max" else nprocs + default_inputs = [xc, basis, "engrad", "normalprint"] + default_blocks = [f"%pal nprocs {nprocs} end"] + + if direction == "forward": + scale = perturb_magnitude + else: + scale = perturb_magnitude * -1 + + return run_and_summarize_opt( + perturb(atoms, mode, scale), + charge=charge, + spin_multiplicity=spin_multiplicity, + default_inputs=default_inputs, + default_blocks=default_blocks, + input_swaps=orcasimpleinput, + block_swaps=orcablocks, + opt_params=opt_params, + additional_fields={"name": "ORCA ASE Quasi-IRC perturbed optimization"}, + copy_files=copy_files, + ) \ No newline at end of file diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index 6f276f0d7c..1800dd233d 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -10,6 +10,7 @@ from quacc.recipes.qchem._base import run_and_summarize_opt from quacc.recipes.qchem.core import _BASE_SET, relax_job from quacc.utils.dicts import recursive_dict_merge +from quacc.utils.coords import perturb try: from sella import IRC, Sella @@ -267,6 +268,11 @@ def quasi_irc_perturb_job( factor, perhaps to 1.0 or 1.1. Lowering it is not generally found to be helpful. direction Direction of the (Quasi)IRC. Should be "forward" or "reverse". + method + DFT exchange-correlation functional or other electronic structure + method. + basis + Basis set. opt_params Dictionary of custom kwargs for the optimization process. For a list of available keys, refer to [quacc.runners.ase.run_opt][]. @@ -283,20 +289,6 @@ def quasi_irc_perturb_job( OptSchema Dictionary of results from [quacc.schemas.ase.summarize_opt_run][] """ - - def perturb(mol: Atoms, vector: List[List[float]], scale: float): - mol_copy = copy.deepcopy(mol) - mode_copy = copy.deepcopy(vector) - - orig_pos = mol_copy.get_positions() - - if not isinstance(mode_copy, np.ndarray): - mode_copy = np.asarray(mode_copy) - - pos = orig_pos + mode_copy * scale - mol_copy.set_positions(pos) - - return mol_copy calc_defaults = recursive_dict_merge( _BASE_SET, {"rem": {"job_type": "force", "method": method, "basis": basis}} diff --git a/src/quacc/utils/coords.py b/src/quacc/utils/coords.py new file mode 100644 index 0000000000..0bf8e6901d --- /dev/null +++ b/src/quacc/utils/coords.py @@ -0,0 +1,23 @@ +"""Utilities for working with atomic coordinates.""" + +from __future__ import annotations +import copy + +import numpy as np + +from ase import Atoms + + +def perturb(mol: Atoms, vector: List[List[float]], scale: float): + mol_copy = copy.deepcopy(mol) + mode_copy = copy.deepcopy(vector) + + orig_pos = mol_copy.get_positions() + + if not isinstance(mode_copy, np.ndarray): + mode_copy = np.asarray(mode_copy) + + pos = orig_pos + mode_copy * scale + mol_copy.set_positions(pos) + + return mol_copy \ No newline at end of file From 9037817a955e9136225ad62980fd3803e7a5bf99 Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Tue, 16 Apr 2024 15:03:47 -0700 Subject: [PATCH 03/16] New ORCA freq job --- src/quacc/recipes/orca/core.py | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index 4c444c3503..b3f0f3d543 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -82,6 +82,78 @@ def static_job( ) +@job +def freq_job( + atoms: Atoms, + charge: int = 0, + spin_multiplicity: int = 1, + xc: str = "wb97x-d3bj", + basis: str = "def2-tzvp", + numeric: bool = False, + orcasimpleinput: list[str] | None = None, + orcablocks: list[str] | None = None, + nprocs: int | Literal["max"] = "max", + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, +) -> cclibSchema: + """ + Carry out a vibrational frequency analysis calculation. + + Parameters + ---------- + atoms + Atoms object + charge + Charge of the system. + spin_multiplicity + Multiplicity of the system. + xc + Exchange-correlation functional + basis + Basis set + numeric + If True (default False), a numeric frequency calculation will be requested + orcasimpleinput + List of `orcasimpleinput` swaps for the calculator. To remove entries + from the defaults, put a `#` in front of the name. Refer to the + [ase.calculators.orca.ORCA][] calculator for details on `orcasimpleinput`. + orcablocks + List of `orcablocks` swaps for the calculator. To remove entries + from the defaults, put a `#` in front of the name. Refer to the + [ase.calculators.orca.ORCA][] calculator for details on `orcablocks`. + nprocs + Number of processors to use. Defaults to the number of physical cores. + copy_files + Files to copy (and decompress) from source to the runtime directory. + + Returns + ------- + cclibSchema + Dictionary of results from [quacc.schemas.cclib.cclib_summarize_run][]. + See the type-hint for the data structure. + """ + nprocs = psutil.cpu_count(logical=False) if nprocs == "max" else nprocs + default_inputs = [xc, basis, "normalprint"] + + if numeric: + default_inputs.append("numfreq") + else: + default_inputs.append("freq") + + default_blocks = [f"%pal nprocs {nprocs} end"] + + return run_and_summarize( + atoms, + charge, + spin_multiplicity, + default_inputs=default_inputs, + default_blocks=default_blocks, + input_swaps=orcasimpleinput, + block_swaps=orcablocks, + additional_fields={"name": "ORCA vibrational frequency analysis"}, + copy_files=copy_files, + ) + + @job def relax_job( atoms: Atoms, From 0f78d23a76ca46a2b32db595c92516cc059adfed Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Tue, 16 Apr 2024 15:12:02 -0700 Subject: [PATCH 04/16] Small tweak --- src/quacc/recipes/orca/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index b3f0f3d543..29faa429d6 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -293,9 +293,9 @@ def ase_relax_job( @job def ase_quasi_irc_perturb_job( atoms: Atoms, + mode: List[List[float]], charge: int = 0, spin_multiplicity: int = 1, - mode: List[List[float]], perturb_magnitude: float = 0.6, direction: Literal["forward", "reverse"] = "forward", xc: str = "wb97x-d3bj", From 878366f5a4613b4e0d52c885d799c13a13489a25 Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Thu, 18 Apr 2024 16:06:06 -0700 Subject: [PATCH 05/16] Small bugfix --- src/quacc/recipes/qchem/ts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index 1800dd233d..725cd17697 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -293,7 +293,7 @@ def quasi_irc_perturb_job( calc_defaults = recursive_dict_merge( _BASE_SET, {"rem": {"job_type": "force", "method": method, "basis": basis}} ) - opt_defaults = {"optimizer": Sella} if has_sella else {} + opt_defaults = {"optimizer": Sella} if (Sella is not False) else {} if direction == "forward": scale = perturb_magnitude From fdfc395626e77ec2581082192e47a209494786e8 Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Mon, 22 Apr 2024 16:02:59 -0700 Subject: [PATCH 06/16] Q-Chem tests pass; now need tests for ORCA --- .../recipes/orca_recipes/test_orca_recipes.py | 29 ++++++- .../qchem_examples/mol.qin.qirc_forward | 34 ++++++++ .../qchem_examples/mol.qin.qirc_reverse | 34 ++++++++ .../mocked/test_qchem_recipes.py | 83 ++++++++++++++++++- .../qchem_recipes/mocked/xyz/ts_test.xyz | 18 ++++ 5 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_forward create mode 100644 tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_reverse create mode 100644 tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz diff --git a/tests/core/recipes/orca_recipes/test_orca_recipes.py b/tests/core/recipes/orca_recipes/test_orca_recipes.py index b919e0a780..2d011d8803 100644 --- a/tests/core/recipes/orca_recipes/test_orca_recipes.py +++ b/tests/core/recipes/orca_recipes/test_orca_recipes.py @@ -6,7 +6,13 @@ import pytest from ase.build import molecule -from quacc.recipes.orca.core import ase_relax_job, relax_job, static_job +from quacc.recipes.orca.core import ( + ase_relax_job, + relax_job, + static_job, + freq_job, + ase_quasi_irc_perturb_job +) def test_static_job(tmp_path, monkeypatch): @@ -141,3 +147,24 @@ def test_ase_relax_job_store(tmp_path, monkeypatch): assert "orca.xyz.gz" in os.listdir(Path(output["dir_name"], f"step{i}")) assert len(output["steps"]) == nsteps assert "attributes" in output["steps"][0] + + +@pytest.mark.skipif(os.name == "nt", reason="mpirun not available on Windows") +def test_freq_job(tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + + atoms = molecule("H2") + + output = freq_job( + atoms, + xc="hf", + basis="def2-svp", + charge=0, + spin_multiplicity=1, + nprocs=2, + orcasimpleinput=["#normalprint"], + ) + assert output["natoms"] == len(atoms) + assert output["parameters"]["charge"] == 0 + assert output["parameters"]["mult"] == 1 + assert output["parameters"]["orcasimpleinput"] == "def2-svp freq hf xyzfile" \ No newline at end of file diff --git a/tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_forward b/tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_forward new file mode 100644 index 0000000000..4deffc313e --- /dev/null +++ b/tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_forward @@ -0,0 +1,34 @@ +$molecule + 0 1 + C -2.2327219082 0.3791393294 0.2143702282 + O -0.4001040911 -0.3209141098 -0.1334138299 + C 0.5423733258 0.2977790640 0.2206586158 + O 0.3924968984 1.4585347680 0.7033089993 + O -2.8376665474 -0.0973158207 -0.6674694144 + O -1.7015955091 1.7258053313 0.1289163517 + H -0.5810710172 1.5729449388 0.5275661907 + C 1.8860908742 -0.2457908005 0.0738796537 + C -2.2383734832 -0.0934887378 1.6277789473 + H -2.4617721877 2.3579851511 0.6488511867 + H 1.8010691866 -1.3457375140 -0.0557562494 + H 2.5419651710 0.0138395515 0.9320914408 + H 2.3352038754 0.1921439154 -0.8423775733 + H -1.3918401900 0.3251951235 2.2131306207 + H -2.2407267711 -1.2034273556 1.6755719861 + H -3.1861276262 0.2835871654 2.0670628463 +$end + +$rem + basis = def2-svpd + gen_scfman = true + job_type = force + max_scf_cycles = 100 + method = wb97mv + resp_charges = true + s2thresh = 16 + scf_algorithm = diis + sym_ignore = true + symmetry = false + thresh = 14 + xc_grid = 3 +$end diff --git a/tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_reverse b/tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_reverse new file mode 100644 index 0000000000..48875c1f94 --- /dev/null +++ b/tests/core/recipes/qchem_recipes/mocked/qchem_examples/mol.qin.qirc_reverse @@ -0,0 +1,34 @@ +$molecule + -1 2 + C -2.0137704788 -0.1270734858 0.1642010188 + O -0.5995953068 -0.2921342976 -0.1231428019 + C 0.5061642245 0.4275172940 0.2730711793 + O 0.5334437588 1.4727239578 0.7474905023 + O -2.8412706626 -0.2099655589 -0.6873946438 + O -1.8288928518 2.0165855272 0.0417843943 + H -1.3429919048 2.3740186237 0.3845744402 + C 1.8193658177 -0.3037461746 0.0198838218 + C -2.2672351085 -0.1210357673 1.6552929612 + H -2.4429538836 2.6113929006 0.7209957866 + H 1.7385359612 -1.3967031272 -0.0862781690 + H 2.4755055076 -0.0345738984 0.8626997493 + H 2.2676650535 0.1131019024 -0.8976730188 + H -1.4518547674 0.3782355629 2.2010561136 + H -2.2382992481 -1.2010725621 1.8946348321 + H -3.2610161107 0.2842091034 1.9133738342 +$end + +$rem + basis = def2-tzvpd + gen_scfman = true + job_type = force + max_scf_cycles = 200 + method = wb97mv + resp_charges = true + s2thresh = 16 + scf_algorithm = gdm + sym_ignore = true + symmetry = false + thresh = 14 + xc_grid = 3 +$end diff --git a/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py b/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py index a3aee3a6e6..1cd22dbd45 100644 --- a/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py +++ b/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py @@ -14,7 +14,7 @@ from quacc.atoms.core import check_charge_and_spin from quacc.calculators.qchem import QChem from quacc.recipes.qchem.core import freq_job, relax_job, static_job -from quacc.recipes.qchem.ts import irc_job, quasi_irc_job, ts_job +from quacc.recipes.qchem.ts import irc_job, quasi_irc_job, quasi_irc_perturb_job, ts_job try: import sella @@ -32,6 +32,11 @@ def test_atoms(): return read(FILE_DIR / "xyz" / "test.xyz") +@pytest.fixture() +def test_qirc_atoms(): + return read(FILE_DIR / "xyz" / "ts_test.xyz") + + @pytest.fixture() def os_atoms(): return read(FILE_DIR / "xyz" / "OS_test.xyz") @@ -589,3 +594,79 @@ def test_quasi_irc_job(monkeypatch, tmp_path, test_atoms): qcin = QCInput.from_file(str(Path(output["dir_name"], "mol.qin.gz"))) ref_qcin = QCInput.from_file(str(QCHEM_DIR / "mol.qin.quasi_irc_reverse")) qcinput_nearly_equal(qcin, ref_qcin) + + +@pytest.mark.skipif(sella is None, reason="Does not have Sella") +def test_quasi_irc_perturb_job(monkeypatch, tmp_path, test_qirc_atoms): + monkeypatch.chdir(tmp_path) + + monkeypatch.setattr(QChem, "read_results", mock_read) + monkeypatch.setattr(QChem, "execute", mock_execute4) + + # Transition mode for this transition-state + mode = [ + [-0.164, 0.289, 0.027], + [0.112, -0.02, -0.004], + [0.012, -0.072, -0.042], + [-0.087, 0.039, -0.038], + [-0.017, 0.013, 0.001], + [0.028, -0.186, 0.028], + [0.751, -0.378, 0.186], + [0.042, 0.034, 0.025], + [-0.007, -0.001, -0.009], + [-0.056, -0.179, -0.076], + [0.036, 0.035, 0.027], + [0.043, 0.037, 0.023], + [0.036, 0.032, 0.021], + [-0.003, -0.032, 0.011], + [-0.006, -0.009, -0.118], + [0.014, -0.034, 0.094] + ] + + charge, spin_multiplicity = check_charge_and_spin(test_qirc_atoms) + output = quasi_irc_perturb_job( + atoms=test_qirc_atoms, + charge=charge, + spin_multiplicity=spin_multiplicity, + mode=mode, + direction="forward", + method="wb97mv", + opt_params={"max_steps": 5}, + basis="def2-svpd" + ) + + assert output["atoms"] != test_qirc_atoms + assert output["charge"] == 0 + assert output["spin_multiplicity"] == 1 + assert output["formula_alphabetical"] == "C4 H8 O4" + assert output["nelectrons"] == 64 + assert output["parameters"]["charge"] == 0 + assert output["parameters"]["spin_multiplicity"] == 1 + + qcin = QCInput.from_file(str(Path(output["dir_name"], "mol.qin.gz"))) + ref_qcin = QCInput.from_file(str(QCHEM_DIR / "mol.qin.qirc_forward")) + qcinput_nearly_equal(qcin, ref_qcin) + + output = quasi_irc_perturb_job( + test_qirc_atoms, + charge=-1, + spin_multiplicity=2, + mode=mode, + perturb_magnitude=1.0, + direction="reverse", + basis="def2-tzvpd", + opt_params={"max_steps": 6}, + rem={"scf_algorithm": "gdm"} + ) + + assert output["atoms"] != test_qirc_atoms + assert output["charge"] == -1 + assert output["spin_multiplicity"] == 2 + assert output["formula_alphabetical"] == "C4 H8 O4" + assert output["nelectrons"] == 65 + assert output["parameters"]["charge"] == -1 + assert output["parameters"]["spin_multiplicity"] == 2 + + qcin = QCInput.from_file(str(Path(output["dir_name"], "mol.qin.gz"))) + ref_qcin = QCInput.from_file(str(QCHEM_DIR / "mol.qin.qirc_reverse")) + qcinput_nearly_equal(qcin, ref_qcin) diff --git a/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz b/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz new file mode 100644 index 0000000000..84befde39b --- /dev/null +++ b/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz @@ -0,0 +1,18 @@ +16 + +C -2.1846949292 0.1898391258 0.1964826227 +O -0.4848515165 -0.2917846139 -0.1275732483 +C 0.5394541212 0.3658912018 0.2429641752 +O 0.4798059249 1.5225609297 0.7410807853 +O -2.8764467052 -0.1509181676 -0.6925854737 +O -1.7935252197 1.8615485866 0.1068277631 +H -0.7532722964 1.8974038694 0.4912008424 +C 1.8832273355 -0.2733121087 0.0407321421 +C -2.2825562627 -0.1155835245 1.6624047756 +H -2.4183125325 2.3939897026 0.6146768439 +H 1.7851969133 -1.3556014160 -0.0789960465 +H 2.5408551922 -0.0219920239 0.8792458113 +H 2.3250034882 0.1492594553 -0.8712799313 +H -1.4650505694 0.3352661303 2.2318827567 +H -2.2601855390 -1.2048429495 1.7751129189 +H -3.2478474045 0.2577558028 2.0283932628 \ No newline at end of file From 9b8bc285738361e3a40267866ec64ae39a534d8a Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Mon, 22 Apr 2024 16:16:10 -0700 Subject: [PATCH 07/16] (very basic) ORCA tests pass --- .../recipes/orca_recipes/test_orca_recipes.py | 55 ++++++++++++++++++- .../core/recipes/orca_recipes/xyz/ts_test.xyz | 18 ++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/core/recipes/orca_recipes/xyz/ts_test.xyz diff --git a/tests/core/recipes/orca_recipes/test_orca_recipes.py b/tests/core/recipes/orca_recipes/test_orca_recipes.py index 2d011d8803..7117654c1d 100644 --- a/tests/core/recipes/orca_recipes/test_orca_recipes.py +++ b/tests/core/recipes/orca_recipes/test_orca_recipes.py @@ -5,6 +5,7 @@ import pytest from ase.build import molecule +from ase.io import read from quacc.recipes.orca.core import ( ase_relax_job, @@ -15,6 +16,14 @@ ) +FILE_DIR = Path(__file__).parent + + +@pytest.fixture() +def test_atoms(): + return read(FILE_DIR / "xyz" / "ts_test.xyz") + + def test_static_job(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) @@ -167,4 +176,48 @@ def test_freq_job(tmp_path, monkeypatch): assert output["natoms"] == len(atoms) assert output["parameters"]["charge"] == 0 assert output["parameters"]["mult"] == 1 - assert output["parameters"]["orcasimpleinput"] == "def2-svp freq hf xyzfile" \ No newline at end of file + assert output["parameters"]["orcasimpleinput"] == "def2-svp freq hf xyzfile" + assert output.get("attributes") + + +@pytest.mark.skipif(os.name == "nt", reason="mpirun not available on Windows") +def test_ase_quasi_irc_perturb_job(test_atoms, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + + mode = [ + [-0.164243, 0.289973, 0.026016], + [0.111755, -0.021282, -0.002864], + [0.011866, -0.071662, -0.040428], + [-0.087153, 0.038657, -0.038008], + [-0.017777, 0.013758, 0.001151], + [0.030409, -0.18767, 0.028847], + [0.750577, -0.378458, 0.179569], + [0.042058, 0.035122, 0.024727], + [-0.008014, -0.000956, -0.009153], + [-0.052972, -0.176662, -0.077928], + [0.037381, 0.036482, 0.028861], + [0.043279, 0.040925, 0.022537], + [0.035434, 0.032613, 0.019516], + [-0.002674, -0.03398, 0.011123], + [-0.006118, -0.009193, -0.122432], + [0.014124, -0.035613, 0.097518] + ] + + output = ase_quasi_irc_perturb_job( + atoms=test_atoms, + mode=mode, + charge=0, + spin_multiplicity=1, + perturb_magnitude=1.0, + direction="reverse", + xc="hf", + basis="def2-svp", + nprocs=2, + orcasimpleinput=["#normalprint"], + ) + assert output["natoms"] == len(test_atoms) + assert output["atoms"] != test_atoms + assert output["parameters"]["charge"] == 0 + assert output["parameters"]["mult"] == 1 + assert output["parameters"]["orcasimpleinput"] == "def2-svp engrad hf xyzfile" + assert output.get("attributes") \ No newline at end of file diff --git a/tests/core/recipes/orca_recipes/xyz/ts_test.xyz b/tests/core/recipes/orca_recipes/xyz/ts_test.xyz new file mode 100644 index 0000000000..84befde39b --- /dev/null +++ b/tests/core/recipes/orca_recipes/xyz/ts_test.xyz @@ -0,0 +1,18 @@ +16 + +C -2.1846949292 0.1898391258 0.1964826227 +O -0.4848515165 -0.2917846139 -0.1275732483 +C 0.5394541212 0.3658912018 0.2429641752 +O 0.4798059249 1.5225609297 0.7410807853 +O -2.8764467052 -0.1509181676 -0.6925854737 +O -1.7935252197 1.8615485866 0.1068277631 +H -0.7532722964 1.8974038694 0.4912008424 +C 1.8832273355 -0.2733121087 0.0407321421 +C -2.2825562627 -0.1155835245 1.6624047756 +H -2.4183125325 2.3939897026 0.6146768439 +H 1.7851969133 -1.3556014160 -0.0789960465 +H 2.5408551922 -0.0219920239 0.8792458113 +H 2.3250034882 0.1492594553 -0.8712799313 +H -1.4650505694 0.3352661303 2.2318827567 +H -2.2601855390 -1.2048429495 1.7751129189 +H -3.2478474045 0.2577558028 2.0283932628 \ No newline at end of file From 499fa4ed7084a42100cc72f49a753ec73c2dfee2 Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Mon, 22 Apr 2024 16:23:02 -0700 Subject: [PATCH 08/16] Updated contributors and recipe list --- docs/about/contributors.md | 1 + docs/user/recipes/recipes_list.md | 30 +++++++++++++++++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/about/contributors.md b/docs/about/contributors.md index 654a78875e..1321e9c5a7 100644 --- a/docs/about/contributors.md +++ b/docs/about/contributors.md @@ -26,6 +26,7 @@ Additional contributions were made by the individuals listed [here](https://gith - [@ViktoriiaBaib](https://github.com/ViktoriiaBaib): Initial testing of quacc. - [@zulissimeta](https://github.com/zulissimeta): Dask support - [@yw-fang](https://github.com/yw-fang): VASP Non-SCF recipe +- [@espottesmith](http://github.com/espottesmith): Some ORCA and Q-Chem workflows, mostly re: IRC ## Inspiration diff --git a/docs/user/recipes/recipes_list.md b/docs/user/recipes/recipes_list.md index 24fc4bcb24..9085e09417 100644 --- a/docs/user/recipes/recipes_list.md +++ b/docs/user/recipes/recipes_list.md @@ -140,11 +140,14 @@ The list of available quacc recipes is shown below. The "Req'd Extras" column sp
-| Name | Decorator | Documentation | Req'd Extras | -| -------------- | --------------- | ----------------------------------------- | ------------ | -| ORCA Static | `#!Python @job` | [quacc.recipes.orca.core.static_job][] | | -| ORCA Relax | `#!Python @job` | [quacc.recipes.orca.core.relax_job][] | | -| ORCA ASE Relax | `#!Python @job` | [quacc.recipes.orca.core.ase_relax_job][] | | +| Name | Decorator | Documentation | Req'd Extras | +| -------------------------- | --------------- | ----------------------------------------------------- | ------------ | +| ORCA Static | `#!Python @job` | [quacc.recipes.orca.core.static_job][] | | +| ORCA Relax | `#!Python @job` | [quacc.recipes.orca.core.relax_job][] | | +| ORCA Freq | `#!Python @job` | [quacc.recipes.orca.core.freq_job][] | | +| ORCA ASE Relax | `#!Python @job` | [quacc.recipes.orca.core.ase_relax_job][] | | +| ORCA ASE Quasi-IRC Perturb | `#!Python @job` | [quacc.recipes.orca.core.ase_quasi_irc_perturb_job][] | | +
@@ -170,14 +173,15 @@ The list of available quacc recipes is shown below. The "Req'd Extras" column sp
-| Name | Decorator | Documentation | Req'd Extras | -| ---------------- | --------------- | ---------------------------------------- | -------------- | -| Q-Chem Static | `#!Python @job` | [quacc.recipes.qchem.core.static_job][] | | -| Q-Chem Relax | `#!Python @job` | [quacc.recipes.qchem.core.relax_job][] | | -| Q-Chem Frequency | `#!Python @job` | [quacc.recipes.qchem.core.freq_job][] | | -| Q-Chem TS | `#!Python @job` | [quacc.recipes.qchem.ts.ts_job][] | `quacc[sella]` | -| Q-Chem IRC | `#!Python @job` | [quacc.recipes.qchem.ts.irc_job][] | `quacc[sella]` | -| Q-Chem Quasi IRC | `#!Python @job` | [quacc.recipes.qchem.ts.quasi_irc_job][] | `quacc[sella]` | +| Name | Decorator | Documentation | Req'd Extras | +| ------------------------ | --------------- | ------------------------------------------------ | -------------- | +| Q-Chem Static | `#!Python @job` | [quacc.recipes.qchem.core.static_job][] | | +| Q-Chem Relax | `#!Python @job` | [quacc.recipes.qchem.core.relax_job][] | | +| Q-Chem Frequency | `#!Python @job` | [quacc.recipes.qchem.core.freq_job][] | | +| Q-Chem TS | `#!Python @job` | [quacc.recipes.qchem.ts.ts_job][] | `quacc[sella]` | +| Q-Chem IRC | `#!Python @job` | [quacc.recipes.qchem.ts.irc_job][] | `quacc[sella]` | +| Q-Chem Quasi IRC | `#!Python @job` | [quacc.recipes.qchem.ts.quasi_irc_job][] | `quacc[sella]` | +| Q-Chem Quasi IRC Perturb | `#!Python @job` | [quacc.recipes.qchem.ts.quasi_irc_perturb_job][] | `quacc[sella]` |
From f2bbce72ce7de0c7294e995d1251df011d73c908 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:23:18 +0000 Subject: [PATCH 09/16] pre-commit auto-fixes --- src/quacc/recipes/orca/core.py | 7 ++----- src/quacc/recipes/qchem/ts.py | 15 ++++++--------- src/quacc/utils/coords.py | 7 +++++-- .../recipes/orca_recipes/test_orca_recipes.py | 9 ++++----- tests/core/recipes/orca_recipes/xyz/ts_test.xyz | 2 +- .../qchem_recipes/mocked/test_qchem_recipes.py | 6 +++--- .../recipes/qchem_recipes/mocked/xyz/ts_test.xyz | 2 +- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index 29faa429d6..34cd20b2d1 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -355,10 +355,7 @@ def ase_quasi_irc_perturb_job( default_inputs = [xc, basis, "engrad", "normalprint"] default_blocks = [f"%pal nprocs {nprocs} end"] - if direction == "forward": - scale = perturb_magnitude - else: - scale = perturb_magnitude * -1 + scale = perturb_magnitude if direction == "forward" else perturb_magnitude * -1 return run_and_summarize_opt( perturb(atoms, mode, scale), @@ -371,4 +368,4 @@ def ase_quasi_irc_perturb_job( opt_params=opt_params, additional_fields={"name": "ORCA ASE Quasi-IRC perturbed optimization"}, copy_files=copy_files, - ) \ No newline at end of file + ) diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index 725cd17697..fdefce9593 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -9,8 +9,8 @@ from quacc import SETTINGS, job, strip_decorator from quacc.recipes.qchem._base import run_and_summarize_opt from quacc.recipes.qchem.core import _BASE_SET, relax_job -from quacc.utils.dicts import recursive_dict_merge from quacc.utils.coords import perturb +from quacc.utils.dicts import recursive_dict_merge try: from sella import IRC, Sella @@ -19,7 +19,7 @@ Sella = False if TYPE_CHECKING: - from typing import Any, List, Literal + from typing import Any, Literal from ase.atoms import Atoms @@ -238,7 +238,7 @@ def quasi_irc_perturb_job( atoms: Atoms, charge: int, spin_multiplicity: int, - mode: List[List[float]], + mode: list[list[float]], perturb_magnitude: float = 0.6, direction: Literal["forward", "reverse"] = "forward", method: str = "wb97mv", @@ -252,7 +252,7 @@ def quasi_irc_perturb_job( Perturbs the structure of `atoms` by a finite amount (0.6 * the normalized mode magnitude) along the specified vibrational frequency mode (assumed to be the transition mode), and then performs a `relax_job` on the perturbed structure. - + Parameters ---------- atoms @@ -295,10 +295,7 @@ def quasi_irc_perturb_job( ) opt_defaults = {"optimizer": Sella} if (Sella is not False) else {} - if direction == "forward": - scale = perturb_magnitude - else: - scale = perturb_magnitude * -1 + scale = perturb_magnitude if direction == "forward" else perturb_magnitude * -1 return run_and_summarize_opt( perturb(atoms, mode, scale), @@ -310,4 +307,4 @@ def quasi_irc_perturb_job( opt_params=opt_params, additional_fields={"name": "Q-Chem Quasi-IRC perturbed optimization"}, copy_files=copy_files, - ) \ No newline at end of file + ) diff --git a/src/quacc/utils/coords.py b/src/quacc/utils/coords.py index 0bf8e6901d..154c93e37e 100644 --- a/src/quacc/utils/coords.py +++ b/src/quacc/utils/coords.py @@ -1,11 +1,14 @@ """Utilities for working with atomic coordinates.""" from __future__ import annotations + import copy +from typing import TYPE_CHECKING import numpy as np -from ase import Atoms +if TYPE_CHECKING: + from ase import Atoms def perturb(mol: Atoms, vector: List[List[float]], scale: float): @@ -20,4 +23,4 @@ def perturb(mol: Atoms, vector: List[List[float]], scale: float): pos = orig_pos + mode_copy * scale mol_copy.set_positions(pos) - return mol_copy \ No newline at end of file + return mol_copy diff --git a/tests/core/recipes/orca_recipes/test_orca_recipes.py b/tests/core/recipes/orca_recipes/test_orca_recipes.py index 7117654c1d..2f2b9f1958 100644 --- a/tests/core/recipes/orca_recipes/test_orca_recipes.py +++ b/tests/core/recipes/orca_recipes/test_orca_recipes.py @@ -8,14 +8,13 @@ from ase.io import read from quacc.recipes.orca.core import ( + ase_quasi_irc_perturb_job, ase_relax_job, + freq_job, relax_job, static_job, - freq_job, - ase_quasi_irc_perturb_job ) - FILE_DIR = Path(__file__).parent @@ -200,7 +199,7 @@ def test_ase_quasi_irc_perturb_job(test_atoms, tmp_path, monkeypatch): [0.035434, 0.032613, 0.019516], [-0.002674, -0.03398, 0.011123], [-0.006118, -0.009193, -0.122432], - [0.014124, -0.035613, 0.097518] + [0.014124, -0.035613, 0.097518], ] output = ase_quasi_irc_perturb_job( @@ -220,4 +219,4 @@ def test_ase_quasi_irc_perturb_job(test_atoms, tmp_path, monkeypatch): assert output["parameters"]["charge"] == 0 assert output["parameters"]["mult"] == 1 assert output["parameters"]["orcasimpleinput"] == "def2-svp engrad hf xyzfile" - assert output.get("attributes") \ No newline at end of file + assert output.get("attributes") diff --git a/tests/core/recipes/orca_recipes/xyz/ts_test.xyz b/tests/core/recipes/orca_recipes/xyz/ts_test.xyz index 84befde39b..6f68b038ba 100644 --- a/tests/core/recipes/orca_recipes/xyz/ts_test.xyz +++ b/tests/core/recipes/orca_recipes/xyz/ts_test.xyz @@ -15,4 +15,4 @@ H 2.5408551922 -0.0219920239 0.8792458113 H 2.3250034882 0.1492594553 -0.8712799313 H -1.4650505694 0.3352661303 2.2318827567 H -2.2601855390 -1.2048429495 1.7751129189 -H -3.2478474045 0.2577558028 2.0283932628 \ No newline at end of file +H -3.2478474045 0.2577558028 2.0283932628 diff --git a/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py b/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py index 1cd22dbd45..89919f4b4c 100644 --- a/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py +++ b/tests/core/recipes/qchem_recipes/mocked/test_qchem_recipes.py @@ -620,7 +620,7 @@ def test_quasi_irc_perturb_job(monkeypatch, tmp_path, test_qirc_atoms): [0.036, 0.032, 0.021], [-0.003, -0.032, 0.011], [-0.006, -0.009, -0.118], - [0.014, -0.034, 0.094] + [0.014, -0.034, 0.094], ] charge, spin_multiplicity = check_charge_and_spin(test_qirc_atoms) @@ -632,7 +632,7 @@ def test_quasi_irc_perturb_job(monkeypatch, tmp_path, test_qirc_atoms): direction="forward", method="wb97mv", opt_params={"max_steps": 5}, - basis="def2-svpd" + basis="def2-svpd", ) assert output["atoms"] != test_qirc_atoms @@ -656,7 +656,7 @@ def test_quasi_irc_perturb_job(monkeypatch, tmp_path, test_qirc_atoms): direction="reverse", basis="def2-tzvpd", opt_params={"max_steps": 6}, - rem={"scf_algorithm": "gdm"} + rem={"scf_algorithm": "gdm"}, ) assert output["atoms"] != test_qirc_atoms diff --git a/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz b/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz index 84befde39b..6f68b038ba 100644 --- a/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz +++ b/tests/core/recipes/qchem_recipes/mocked/xyz/ts_test.xyz @@ -15,4 +15,4 @@ H 2.5408551922 -0.0219920239 0.8792458113 H 2.3250034882 0.1492594553 -0.8712799313 H -1.4650505694 0.3352661303 2.2318827567 H -2.2601855390 -1.2048429495 1.7751129189 -H -3.2478474045 0.2577558028 2.0283932628 \ No newline at end of file +H -3.2478474045 0.2577558028 2.0283932628 From 14cad90837eac0bc224f2315669c270de7376575 Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Mon, 22 Apr 2024 16:29:54 -0700 Subject: [PATCH 10/16] Types --- src/quacc/recipes/orca/core.py | 2 +- src/quacc/utils/coords.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index 29faa429d6..07f50e8fe7 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import List, TYPE_CHECKING import psutil diff --git a/src/quacc/utils/coords.py b/src/quacc/utils/coords.py index 0bf8e6901d..5bdf498e89 100644 --- a/src/quacc/utils/coords.py +++ b/src/quacc/utils/coords.py @@ -2,6 +2,7 @@ from __future__ import annotations import copy +from typing import List import numpy as np From 830d178e8d7e06df8bdfbcd3d698abe946c4cda0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:31:28 +0000 Subject: [PATCH 11/16] pre-commit auto-fixes --- src/quacc/recipes/orca/core.py | 4 ++-- src/quacc/utils/coords.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index d008e1eede..79edd3451d 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING import psutil @@ -293,7 +293,7 @@ def ase_relax_job( @job def ase_quasi_irc_perturb_job( atoms: Atoms, - mode: List[List[float]], + mode: list[list[float]], charge: int = 0, spin_multiplicity: int = 1, perturb_magnitude: float = 0.6, diff --git a/src/quacc/utils/coords.py b/src/quacc/utils/coords.py index 0760ac9a13..d3b866b4a2 100644 --- a/src/quacc/utils/coords.py +++ b/src/quacc/utils/coords.py @@ -3,7 +3,7 @@ from __future__ import annotations import copy -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING import numpy as np @@ -11,7 +11,7 @@ from ase import Atoms -def perturb(mol: Atoms, vector: List[List[float]], scale: float): +def perturb(mol: Atoms, vector: list[list[float]], scale: float): mol_copy = copy.deepcopy(mol) mode_copy = copy.deepcopy(vector) From 34cf74f1f6ac1cf9cbaac196f0c39d5d690a4f7f Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Tue, 23 Apr 2024 09:01:36 -0700 Subject: [PATCH 12/16] Tried to make @Andrew-S-Rosen happy :) --- src/quacc/atoms/core.py | 32 +++++++++++++++++++ src/quacc/recipes/orca/core.py | 18 ++++------- src/quacc/recipes/qchem/ts.py | 18 ++++++----- src/quacc/utils/coords.py | 26 --------------- .../recipes/orca_recipes/test_orca_recipes.py | 2 -- 5 files changed, 49 insertions(+), 47 deletions(-) delete mode 100644 src/quacc/utils/coords.py diff --git a/src/quacc/atoms/core.py b/src/quacc/atoms/core.py index 95ec212b87..ea54397f6e 100644 --- a/src/quacc/atoms/core.py +++ b/src/quacc/atoms/core.py @@ -15,6 +15,7 @@ if TYPE_CHECKING: from ase.atoms import Atoms from ase.optimize.optimize import Dynamics + from numpy.typing import NDArray logger = logging.getLogger(__name__) @@ -254,3 +255,34 @@ def get_final_atoms_from_dynamics(dynamics: Dynamics) -> Atoms: return ( dynamics.atoms.atoms if isinstance(dynamics.atoms, Filter) else dynamics.atoms ) + + +def perturb(mol: Atoms, matrix: list[list[float]] | NDArray, scale: float) -> Atoms: + """ + Perturb each atom in a molecule by a (scaled) 1x3 vector, reflecting e.g. a vibrational normal mode. + + Parameters + ---------- + mol + ASE Atoms object representing a molecule + matrix + Nx3 matrix, where N is the numebr of atoms. This means that there is potentially a different translation + vector for each atom in the molecule. + scale + Scaling factor for perturbation + + Returns + ------- + mol_copy + The input molecule after perturbation + """ + + mol_copy = copy_atoms(mol) + mode = np.asarray(matrix) + + orig_pos = mol_copy.get_positions() + + pos = orig_pos + mode * scale + mol_copy.set_positions(pos) + + return mol_copy diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index 79edd3451d..ac71f6ac8e 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: from typing import Any, Literal - + from numpy.typing import NDArray from ase.atoms import Atoms from quacc.schemas._aliases.cclib import cclibSchema @@ -89,7 +89,7 @@ def freq_job( spin_multiplicity: int = 1, xc: str = "wb97x-d3bj", basis: str = "def2-tzvp", - numeric: bool = False, + numerical: bool = False, orcasimpleinput: list[str] | None = None, orcablocks: list[str] | None = None, nprocs: int | Literal["max"] = "max", @@ -110,7 +110,7 @@ def freq_job( Exchange-correlation functional basis Basis set - numeric + numerical If True (default False), a numeric frequency calculation will be requested orcasimpleinput List of `orcasimpleinput` swaps for the calculator. To remove entries @@ -132,12 +132,8 @@ def freq_job( See the type-hint for the data structure. """ nprocs = psutil.cpu_count(logical=False) if nprocs == "max" else nprocs - default_inputs = [xc, basis, "normalprint"] - if numeric: - default_inputs.append("numfreq") - else: - default_inputs.append("freq") + default_inputs = [xc, basis, "normalprint", "numfreq" if numerical else "freq"] default_blocks = [f"%pal nprocs {nprocs} end"] @@ -293,7 +289,7 @@ def ase_relax_job( @job def ase_quasi_irc_perturb_job( atoms: Atoms, - mode: list[list[float]], + mode: list[list[float]] | NDArray, charge: int = 0, spin_multiplicity: int = 1, perturb_magnitude: float = 0.6, @@ -316,12 +312,12 @@ def ase_quasi_irc_perturb_job( ---------- atoms Atoms object + mode + Transition mode. This should be an Nx3 matrix, where N is the number of atoms in `atoms`. charge Charge of the system. spin_multiplicity Multiplicity of the system. - mode - Transition mode perturb_magnitude Factor to multiply the transition mode. Default is 0.6. In some cases, it may be advisable to increase this factor, perhaps to 1.0 or 1.1. Lowering it is not generally found to be helpful. diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index fdefce9593..656b47746d 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -14,12 +14,14 @@ try: from sella import IRC, Sella + has_sella = True except ImportError: - Sella = False + has_sella = False if TYPE_CHECKING: from typing import Any, Literal + from numpy.typing import NDArray from ase.atoms import Atoms @@ -28,7 +30,7 @@ @job -@requires(Sella, "Sella must be installed. Refer to the quacc documentation.") +@requires(has_sella, "Sella must be installed. Refer to the quacc documentation.") def ts_job( atoms: Atoms, charge: int, @@ -93,7 +95,7 @@ def ts_job( @job -@requires(Sella, "Sella must be installed. Refer to the quacc documentation.") +@requires(has_sella, "Sella must be installed. Refer to the quacc documentation.") def irc_job( atoms: Atoms, charge: int, @@ -163,7 +165,7 @@ def irc_job( @job -@requires(Sella, "Sella must be installed. Refer to the quacc documentation.") +@requires(has_sella, "Sella must be installed. Refer to the quacc documentation.") def quasi_irc_job( atoms: Atoms, charge: int, @@ -233,12 +235,12 @@ def quasi_irc_job( @job -@requires(Sella, "Sella must be installed. Refer to the quacc documentation.") +@requires(has_sella, "Sella must be installed. Refer to the quacc documentation.") def quasi_irc_perturb_job( atoms: Atoms, charge: int, spin_multiplicity: int, - mode: list[list[float]], + mode: list[list[float]] | NDArray, perturb_magnitude: float = 0.6, direction: Literal["forward", "reverse"] = "forward", method: str = "wb97mv", @@ -262,7 +264,7 @@ def quasi_irc_perturb_job( spin_multiplicity Multiplicity of the system. mode - Transition mode + Transition mode. This should be an Nx3 matrix, where N is the number of atoms in `atoms`. perturb_magnitude Factor to multiply the transition mode. Default is 0.6. In some cases, it may be advisable to increase this factor, perhaps to 1.0 or 1.1. Lowering it is not generally found to be helpful. @@ -293,7 +295,7 @@ def quasi_irc_perturb_job( calc_defaults = recursive_dict_merge( _BASE_SET, {"rem": {"job_type": "force", "method": method, "basis": basis}} ) - opt_defaults = {"optimizer": Sella} if (Sella is not False) else {} + opt_defaults = {"optimizer": Sella} if has_sella else {} scale = perturb_magnitude if direction == "forward" else perturb_magnitude * -1 diff --git a/src/quacc/utils/coords.py b/src/quacc/utils/coords.py deleted file mode 100644 index d3b866b4a2..0000000000 --- a/src/quacc/utils/coords.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Utilities for working with atomic coordinates.""" - -from __future__ import annotations - -import copy -from typing import TYPE_CHECKING - -import numpy as np - -if TYPE_CHECKING: - from ase import Atoms - - -def perturb(mol: Atoms, vector: list[list[float]], scale: float): - mol_copy = copy.deepcopy(mol) - mode_copy = copy.deepcopy(vector) - - orig_pos = mol_copy.get_positions() - - if not isinstance(mode_copy, np.ndarray): - mode_copy = np.asarray(mode_copy) - - pos = orig_pos + mode_copy * scale - mol_copy.set_positions(pos) - - return mol_copy diff --git a/tests/core/recipes/orca_recipes/test_orca_recipes.py b/tests/core/recipes/orca_recipes/test_orca_recipes.py index 2f2b9f1958..555bbb5da0 100644 --- a/tests/core/recipes/orca_recipes/test_orca_recipes.py +++ b/tests/core/recipes/orca_recipes/test_orca_recipes.py @@ -169,7 +169,6 @@ def test_freq_job(tmp_path, monkeypatch): basis="def2-svp", charge=0, spin_multiplicity=1, - nprocs=2, orcasimpleinput=["#normalprint"], ) assert output["natoms"] == len(atoms) @@ -211,7 +210,6 @@ def test_ase_quasi_irc_perturb_job(test_atoms, tmp_path, monkeypatch): direction="reverse", xc="hf", basis="def2-svp", - nprocs=2, orcasimpleinput=["#normalprint"], ) assert output["natoms"] == len(test_atoms) From 39fd9cbfca0bc6c6c14cb2d896afb28f29199b21 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 16:03:52 +0000 Subject: [PATCH 13/16] pre-commit auto-fixes --- src/quacc/atoms/core.py | 2 +- src/quacc/recipes/orca/core.py | 3 ++- src/quacc/recipes/qchem/ts.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/quacc/atoms/core.py b/src/quacc/atoms/core.py index ea54397f6e..0f880a7376 100644 --- a/src/quacc/atoms/core.py +++ b/src/quacc/atoms/core.py @@ -260,7 +260,7 @@ def get_final_atoms_from_dynamics(dynamics: Dynamics) -> Atoms: def perturb(mol: Atoms, matrix: list[list[float]] | NDArray, scale: float) -> Atoms: """ Perturb each atom in a molecule by a (scaled) 1x3 vector, reflecting e.g. a vibrational normal mode. - + Parameters ---------- mol diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index ac71f6ac8e..cfda5d3685 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -12,8 +12,9 @@ if TYPE_CHECKING: from typing import Any, Literal - from numpy.typing import NDArray + from ase.atoms import Atoms + from numpy.typing import NDArray from quacc.schemas._aliases.cclib import cclibSchema from quacc.utils.files import Filenames, SourceDirectory diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index 656b47746d..bf646422f2 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -14,6 +14,7 @@ try: from sella import IRC, Sella + has_sella = True except ImportError: @@ -21,9 +22,9 @@ if TYPE_CHECKING: from typing import Any, Literal - from numpy.typing import NDArray from ase.atoms import Atoms + from numpy.typing import NDArray from quacc.schemas._aliases.ase import OptSchema from quacc.utils.files import Filenames, SourceDirectory From a551feafe377b643ffd12f9a8224ee3f3d7fa8aa Mon Sep 17 00:00:00 2001 From: "Evan Walter Clark Spotte-Smith, PhD" Date: Tue, 23 Apr 2024 09:03:58 -0700 Subject: [PATCH 14/16] Flubbed refactor --- src/quacc/recipes/orca/core.py | 2 +- src/quacc/recipes/qchem/ts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index ac71f6ac8e..42ccc24a9a 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -8,7 +8,7 @@ from quacc import job from quacc.recipes.orca._base import run_and_summarize, run_and_summarize_opt -from quacc.utils.coords import perturb +from quacc.atoms.core import perturb if TYPE_CHECKING: from typing import Any, Literal diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index 656b47746d..e2102d4027 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -9,7 +9,7 @@ from quacc import SETTINGS, job, strip_decorator from quacc.recipes.qchem._base import run_and_summarize_opt from quacc.recipes.qchem.core import _BASE_SET, relax_job -from quacc.utils.coords import perturb +from quacc.atoms.core import perturb from quacc.utils.dicts import recursive_dict_merge try: From e20c77efc599ce62927cdcfc670f04d45764ea90 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 16:05:07 +0000 Subject: [PATCH 15/16] pre-commit auto-fixes --- src/quacc/recipes/orca/core.py | 2 +- src/quacc/recipes/qchem/ts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quacc/recipes/orca/core.py b/src/quacc/recipes/orca/core.py index dd83fc95d7..60d44489b8 100644 --- a/src/quacc/recipes/orca/core.py +++ b/src/quacc/recipes/orca/core.py @@ -7,8 +7,8 @@ import psutil from quacc import job -from quacc.recipes.orca._base import run_and_summarize, run_and_summarize_opt from quacc.atoms.core import perturb +from quacc.recipes.orca._base import run_and_summarize, run_and_summarize_opt if TYPE_CHECKING: from typing import Any, Literal diff --git a/src/quacc/recipes/qchem/ts.py b/src/quacc/recipes/qchem/ts.py index 24eac8ce1d..9f5a6cb630 100644 --- a/src/quacc/recipes/qchem/ts.py +++ b/src/quacc/recipes/qchem/ts.py @@ -7,9 +7,9 @@ from monty.dev import requires from quacc import SETTINGS, job, strip_decorator +from quacc.atoms.core import perturb from quacc.recipes.qchem._base import run_and_summarize_opt from quacc.recipes.qchem.core import _BASE_SET, relax_job -from quacc.atoms.core import perturb from quacc.utils.dicts import recursive_dict_merge try: From 3438be841b225038d60cb63406b073c8d8380582 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Tue, 23 Apr 2024 09:31:03 -0700 Subject: [PATCH 16/16] Fix docstring type hint --- src/quacc/atoms/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quacc/atoms/core.py b/src/quacc/atoms/core.py index 0f880a7376..f57b0c7c7f 100644 --- a/src/quacc/atoms/core.py +++ b/src/quacc/atoms/core.py @@ -273,7 +273,7 @@ def perturb(mol: Atoms, matrix: list[list[float]] | NDArray, scale: float) -> At Returns ------- - mol_copy + Atoms The input molecule after perturbation """