From ad20604e7a55732ab677a9e50341653bca43d670 Mon Sep 17 00:00:00 2001 From: benshi97 Date: Tue, 11 Jun 2024 18:08:35 +0100 Subject: [PATCH 01/35] Modify VASP recipes for vibrational analysis --- src/quacc/recipes/vasp/_base.py | 36 ++++++++++++++++- src/quacc/recipes/vasp/core.py | 72 +++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index bb3bcbe08d..a36540dc97 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -5,9 +5,11 @@ from typing import TYPE_CHECKING from quacc.calculators.vasp import Vasp -from quacc.runners.ase import run_calc, run_opt +from quacc.runners.ase import run_calc, run_opt, run_vib from quacc.schemas.vasp import summarize_vasp_opt_run, vasp_summarize_run +from quacc.schemas.ase import summarize_vib_and_thermo from quacc.utils.dicts import recursive_dict_merge +from quacc.runners.thermo import run_ideal_gas if TYPE_CHECKING: from typing import Any @@ -17,6 +19,7 @@ from quacc.runners.ase import OptParams from quacc.schemas._aliases.vasp import VaspASEOptSchema, VaspSchema from quacc.utils.files import Filenames, SourceDirectory + from quacc.schemas._aliases.ase import VibThermoSchema def run_and_summarize( @@ -120,3 +123,34 @@ def run_and_summarize_opt( report_mp_corrections=report_mp_corrections, additional_fields=additional_fields, ) + +def run_and_summarize_vib_and_thermo( + atoms: Atoms, + energy: float = 0.0, + temperature: float = 298.15, + pressure: float = 1.0, + preset: str | None = None, + calc_defaults: dict[str, Any] | None = None, + calc_swaps: dict[str, Any] | None = None, + vib_kwargs: dict[str, Any] | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, +) -> VibThermoSchema: + """ + Base job function for VASP recipes with ASE vibrational analysis. +) + """ + + # Set defaults + calc_flags = recursive_dict_merge(calc_defaults, calc_swaps) + + atoms.calc = Vasp(atoms, preset=preset, **calc_flags) + vibrations = run_vib(atoms, vib_kwargs=vib_kwargs, copy_files=copy_files) + igt = run_ideal_gas(atoms, vibrations.get_frequencies(), energy=energy) + + return summarize_vib_and_thermo( + vibrations, + igt, + temperature=temperature, + pressure=pressure, + additional_fields={"name": "VASP Frequency and Thermo"}, + ) \ No newline at end of file diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index a3697ab116..57ec643ae3 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -10,17 +10,16 @@ from pymatgen.io.vasp import Vasprun from quacc import flow, job -from quacc.recipes.vasp._base import run_and_summarize, run_and_summarize_opt +from quacc.recipes.vasp._base import run_and_summarize, run_and_summarize_opt, run_and_summarize_vib_and_thermo if TYPE_CHECKING: from typing import Any from ase.atoms import Atoms - - from quacc.runners.ase import OptParams + from quacc.runners.ase import OptParams, VibKwargs from quacc.schemas._aliases.vasp import VaspASEOptSchema, VaspSchema from quacc.utils.files import Filenames, SourceDirectory - + from quacc.schemas._aliases.ase import VibThermoSchema @job def static_job( @@ -316,3 +315,68 @@ def non_scf_job( additional_fields={"name": "VASP Non-SCF"}, copy_files={prev_dir: ["CHGCAR*", "WAVECAR*"]}, ) + +@job +def freq_job( + atoms: Atoms, + preset: str | None = "BulkSet", + energy: float = 0.0, + temperature: float = 298.15, + pressure: float = 1.0, + vib_kwargs: VibKwargs | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, + **calc_kwargs, +) -> VibThermoSchema: + """ + Run a frequency job and calculate thermochemistry. + + Parameters + ---------- + atoms + Atoms object + preset + Preset to use from `quacc.calculators.vasp.presets`. + energy + Potential energy in eV. If 0, then the output is just the correction. + temperature + Temperature in Kelvins. + pressure + Pressure in bar. + vib_kwargs + Dictionary of kwargs for the [ase.vibrations.Vibrations][] class. + copy_files + Files to copy (and decompress) from source to the runtime directory. + **calc_kwargs + Custom kwargs for the Vasp calculator. Set a value to + `None` to remove a pre-existing key entirely. For a list of available + keys, refer to [quacc.calculators.vasp.vasp.Vasp][]. + + Returns + ------- + VibThermoSchema + Dictionary of results, specified in [quacc.schemas.ase.summarize_vib_and_thermo][]. + See the type-hint for the data structure. + """ + calc_defaults = { + "ediff": 1e-8, + "ibrion": -1, + "isym": 0, + "algo": "all", + "lcharg": False, + "lwave": True, + "nsw": 0, + } + vib_kwargs = vib_kwargs or {} + + return run_and_summarize_vib_and_thermo( + atoms, + energy=energy, + temperature=temperature, + pressure=pressure, + preset=preset, + calc_defaults=calc_defaults, + calc_swaps=calc_kwargs, + vib_kwargs=vib_kwargs, + copy_files=copy_files, + additional_fields={"name": "VASP Frequency and Thermo"}, + ) From 3000881135b37f909fab0f1b18f36d13d155bb4b Mon Sep 17 00:00:00 2001 From: benshi97 Date: Wed, 12 Jun 2024 11:10:36 +0100 Subject: [PATCH 02/35] Recipe to compute vibrational frequency --- src/quacc/recipes/vasp/_base.py | 33 +++++++++++++++++-- src/quacc/recipes/vasp/core.py | 3 ++ .../vasp_recipes/mocked/test_vasp_recipes.py | 12 +++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index a36540dc97..170cb60396 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -134,10 +134,39 @@ def run_and_summarize_vib_and_thermo( calc_swaps: dict[str, Any] | None = None, vib_kwargs: dict[str, Any] | None = None, copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, + additional_fields: dict[str, Any] | None = None, ) -> VibThermoSchema: """ Base job function for VASP recipes with ASE vibrational analysis. -) + + Parameters + ---------- + atoms + Atoms object + energy + Energy of the system + temperature + Temperature of the system + pressure + Pressure of the system + preset + Preset to use from `quacc.calculators.vasp.presets`. + calc_defaults + Default parameters for the recipe. + calc_swaps + Dictionary of custom kwargs for the Vasp calculator. Set a value to + `None` to remove a pre-existing key entirely. For a list of available + keys, refer to [quacc.calculators.vasp.vasp.Vasp][]. + vib_kwargs + Dictionary of custom kwargs for [quacc.runners.ase.run_vib][] + copy_files + Files to copy (and decompress) from source to the runtime directory. + + Returns + ------- + VibThermoSchema + Dictionary of results, specified in [quacc.schemas.ase.summarize_vib_and_thermo][]. + See the type-hint for the data structure. """ # Set defaults @@ -152,5 +181,5 @@ def run_and_summarize_vib_and_thermo( igt, temperature=temperature, pressure=pressure, - additional_fields={"name": "VASP Frequency and Thermo"}, + additional_fields=additional_fields, ) \ No newline at end of file diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index 57ec643ae3..5148bdd5c0 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -361,10 +361,13 @@ def freq_job( "ediff": 1e-8, "ibrion": -1, "isym": 0, + "istart": 1, "algo": "all", "lcharg": False, "lwave": True, "nsw": 0, + "ismear": 0, + "sigma": 0.05 } vib_kwargs = vib_kwargs or {} diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index 95321405ef..f0400646b1 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -23,6 +23,7 @@ non_scf_job, relax_job, static_job, + freq_job ) from quacc.recipes.vasp.mp import ( mp_gga_relax_flow, @@ -848,3 +849,14 @@ def test_mp_relax_flow_custom(tmp_path, patch_nonmetallic_taskdoc): job_params={"mp_metagga_relax_job": {"nsw": 0}}, ) assert output["relax2"]["parameters"]["nsw"] == 0 + + +def test_freq_job(): + atoms = molecule("H2") + atoms.pbc = True + atoms.set_cell([3,3,3]) + output = freq_job(atoms, kpts = (1,1,1)) + assert output["parameters"]["ediff"] == 1e-08 + assert output["parameters"]["sigma"] == 0.05 + assert output["parameters"]["ismear"] == 0 + assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) From 78e567ba050d04489a415a1c670e049377d3c383 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:03:56 +0000 Subject: [PATCH 03/35] pre-commit auto-fixes --- src/quacc/recipes/vasp/_base.py | 13 +++++++++---- src/quacc/recipes/vasp/core.py | 6 ++++-- .../vasp_recipes/mocked/test_vasp_recipes.py | 6 +++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 3a1a85b805..c3f51c93cd 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -124,6 +124,7 @@ def run_and_summarize_opt( additional_fields=additional_fields, ) + def run_and_summarize_vib_and_thermo( atoms: Atoms, energy: float = 0.0, @@ -149,7 +150,7 @@ def run_and_summarize_vib_and_thermo( Temperature of the system pressure Pressure of the system - preset + preset Preset to use from `quacc.calculators.vasp.presets`. calc_defaults Default parameters for the recipe. @@ -173,8 +174,12 @@ def run_and_summarize_vib_and_thermo( calc_flags = recursive_dict_merge(calc_defaults, calc_swaps) calc = Vasp(atoms, preset=preset, **calc_flags) - vibrations = Runner(atoms, calc, copy_files=copy_files).run_vib(vib_kwargs=vib_kwargs) - igt = ThermoRunner(atoms, vibrations.get_frequencies(), energy=energy).run_ideal_gas() + vibrations = Runner(atoms, calc, copy_files=copy_files).run_vib( + vib_kwargs=vib_kwargs + ) + igt = ThermoRunner( + atoms, vibrations.get_frequencies(), energy=energy + ).run_ideal_gas() return summarize_vib_and_thermo( vibrations, @@ -182,4 +187,4 @@ def run_and_summarize_vib_and_thermo( temperature=temperature, pressure=pressure, additional_fields=additional_fields, - ) \ No newline at end of file + ) diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index cfd30d6e03..f7342fd9a2 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -30,6 +30,7 @@ ) from quacc.utils.files import Filenames, SourceDirectory + @job def static_job( atoms: Atoms, @@ -325,6 +326,7 @@ def non_scf_job( copy_files={prev_dir: ["CHGCAR*", "WAVECAR*"]}, ) + @job def freq_job( atoms: Atoms, @@ -343,7 +345,7 @@ def freq_job( ---------- atoms Atoms object - preset + preset Preset to use from `quacc.calculators.vasp.presets`. energy Potential energy in eV. If 0, then the output is just the correction. @@ -376,7 +378,7 @@ def freq_job( "lwave": True, "nsw": 0, "ismear": 0, - "sigma": 0.05 + "sigma": 0.05, } vib_kwargs = vib_kwargs or {} diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index f0400646b1..d87505633c 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -20,10 +20,10 @@ from quacc.recipes.vasp.core import ( ase_relax_job, double_relax_flow, + freq_job, non_scf_job, relax_job, static_job, - freq_job ) from quacc.recipes.vasp.mp import ( mp_gga_relax_flow, @@ -854,8 +854,8 @@ def test_mp_relax_flow_custom(tmp_path, patch_nonmetallic_taskdoc): def test_freq_job(): atoms = molecule("H2") atoms.pbc = True - atoms.set_cell([3,3,3]) - output = freq_job(atoms, kpts = (1,1,1)) + atoms.set_cell([3, 3, 3]) + output = freq_job(atoms, kpts=(1, 1, 1)) assert output["parameters"]["ediff"] == 1e-08 assert output["parameters"]["sigma"] == 0.05 assert output["parameters"]["ismear"] == 0 From c90696072bc5dddd636c3c315bbdf4cdf6c6b3a2 Mon Sep 17 00:00:00 2001 From: Andrew Rosen Date: Wed, 12 Jun 2024 09:06:39 -0700 Subject: [PATCH 04/35] fix docs --- src/quacc/recipes/vasp/_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 3a1a85b805..e124407e00 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -158,7 +158,7 @@ def run_and_summarize_vib_and_thermo( `None` to remove a pre-existing key entirely. For a list of available keys, refer to [quacc.calculators.vasp.vasp.Vasp][]. vib_kwargs - Dictionary of custom kwargs for [quacc.runners.ase.run_vib][] + Dictionary of custom kwargs for [quacc.runners.ase.Runner.run_vib][] copy_files Files to copy (and decompress) from source to the runtime directory. From a9e4ea5a9c6476e5ab0d669701cf37aacfe5a8c2 Mon Sep 17 00:00:00 2001 From: benshi97 Date: Wed, 12 Jun 2024 17:44:09 +0100 Subject: [PATCH 05/35] new_defaults --- src/quacc/recipes/vasp/core.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index f7342fd9a2..6d54fd3a20 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -370,15 +370,10 @@ def freq_job( """ calc_defaults = { "ediff": 1e-8, - "ibrion": -1, "isym": 0, - "istart": 1, - "algo": "all", "lcharg": False, "lwave": True, "nsw": 0, - "ismear": 0, - "sigma": 0.05, } vib_kwargs = vib_kwargs or {} From 48e584f65e5556001eddbac5080b8e4f09d63617 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:45:33 +0000 Subject: [PATCH 06/35] pre-commit auto-fixes --- src/quacc/recipes/vasp/core.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index 6d54fd3a20..8a98e8b1c9 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -368,13 +368,7 @@ def freq_job( Dictionary of results, specified in [quacc.schemas.ase.summarize_vib_and_thermo][]. See the type-hint for the data structure. """ - calc_defaults = { - "ediff": 1e-8, - "isym": 0, - "lcharg": False, - "lwave": True, - "nsw": 0, - } + calc_defaults = {"ediff": 1e-8, "isym": 0, "lcharg": False, "lwave": True, "nsw": 0} vib_kwargs = vib_kwargs or {} return run_and_summarize_vib_and_thermo( From eacce405d50b9569447bef02398741679ad0fa9c Mon Sep 17 00:00:00 2001 From: benshi97 Date: Mon, 24 Jun 2024 11:39:05 +0100 Subject: [PATCH 07/35] Incorporate Harmonic approximation analysis --- docs/about/contributors.md | 1 + docs/user/recipes/recipes_list.md | 1 + src/quacc/recipes/vasp/_base.py | 17 ++-- src/quacc/recipes/vasp/core.py | 4 +- src/quacc/runners/thermo.py | 22 +++++- src/quacc/schemas/ase.py | 79 ++++++++++++++++--- .../vasp_recipes/mocked/test_vasp_recipes.py | 2 +- tests/core/runners/test_thermo.py | 10 +++ tests/core/schemas/test_ase.py | 27 ++++++- 9 files changed, 144 insertions(+), 19 deletions(-) diff --git a/docs/about/contributors.md b/docs/about/contributors.md index b5dbf78fd3..3dc99f7083 100644 --- a/docs/about/contributors.md +++ b/docs/about/contributors.md @@ -28,6 +28,7 @@ Additional contributions were made by the individuals listed [here](https://gith - [@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 - [@honghuikim](https://github.com/honghuikim): Dynamic changing of quacc settings with workflow engines +- [@benshi97](https://github.com/benshi97): VASP frequency recipe ## Inspiration diff --git a/docs/user/recipes/recipes_list.md b/docs/user/recipes/recipes_list.md index 9085e09417..484a8ab2f3 100644 --- a/docs/user/recipes/recipes_list.md +++ b/docs/user/recipes/recipes_list.md @@ -251,6 +251,7 @@ The list of available quacc recipes is shown below. The "Req'd Extras" column sp | VASP Non-SCF | `#!Python @job` | [quacc.recipes.vasp.core.non_scf_job][] | | | VASP Slab Static | `#!Python @job` | [quacc.recipes.vasp.slabs.static_job][] | | | VASP Slab Relax | `#!Python @job` | [quacc.recipes.vasp.slabs.relax_job][] | | +| VASP Frequency | `#!Python @job` | [quacc.recipes.vasp.slabs.freq_job][] | | | VASP Bulk to Slabs | `#!Python @flow` | [quacc.recipes.vasp.slabs.bulk_to_slabs_flow][] | | | VASP Slab to Adsorbates | `#!Python @flow` | [quacc.recipes.vasp.slabs.slab_to_ads_flow][] | | | VASP MP GGA Relax | `#!Python @job` | [quacc.recipes.vasp.mp.mp_gga_relax_job][] | `quacc[mp]` | diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 3ab6f67413..5e9a0a5817 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -12,7 +12,7 @@ from quacc.utils.dicts import recursive_dict_merge if TYPE_CHECKING: - from typing import Any + from typing import Any, Literal from ase.atoms import Atoms @@ -130,6 +130,7 @@ def run_and_summarize_vib_and_thermo( energy: float = 0.0, temperature: float = 298.15, pressure: float = 1.0, + thermo_method: Literal["ideal_gas", "harmonic"] = "ideal_gas", preset: str | None = None, calc_defaults: dict[str, Any] | None = None, calc_swaps: dict[str, Any] | None = None, @@ -177,13 +178,19 @@ def run_and_summarize_vib_and_thermo( vibrations = Runner(atoms, calc, copy_files=copy_files).run_vib( vib_kwargs=vib_kwargs ) - igt = ThermoRunner( - atoms, vibrations.get_frequencies(), energy=energy - ).run_ideal_gas() + if thermo_method == "harmonic": + thermo_analysis = ThermoRunner( + atoms, vibrations.get_frequencies(), energy=energy + ).run_harmonic() + elif thermo_method == "ideal_gas": + thermo_analysis = ThermoRunner( + atoms, vibrations.get_frequencies(), energy=energy + ).run_ideal_gas() + return summarize_vib_and_thermo( vibrations, - igt, + thermo_analysis, temperature=temperature, pressure=pressure, additional_fields=additional_fields, diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index 8a98e8b1c9..2ff7a4ae7c 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -334,6 +334,7 @@ def freq_job( energy: float = 0.0, temperature: float = 298.15, pressure: float = 1.0, + thermo_method: Literal["harmonic","ideal_gas"] = "ideal_gas", vib_kwargs: VibKwargs | None = None, copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, **calc_kwargs, @@ -368,7 +369,7 @@ def freq_job( Dictionary of results, specified in [quacc.schemas.ase.summarize_vib_and_thermo][]. See the type-hint for the data structure. """ - calc_defaults = {"ediff": 1e-8, "isym": 0, "lcharg": False, "lwave": True, "nsw": 0} + calc_defaults = {"ediff": 1e-7, "isym": 0, "lcharg": False, "lwave": True, "nsw": 0} vib_kwargs = vib_kwargs or {} return run_and_summarize_vib_and_thermo( @@ -376,6 +377,7 @@ def freq_job( energy=energy, temperature=temperature, pressure=pressure, + thermo_method=thermo_method, preset=preset, calc_defaults=calc_defaults, calc_swaps=calc_kwargs, diff --git a/src/quacc/runners/thermo.py b/src/quacc/runners/thermo.py index 3a9c2c9034..afb11b78fd 100644 --- a/src/quacc/runners/thermo.py +++ b/src/quacc/runners/thermo.py @@ -7,7 +7,7 @@ import numpy as np from ase import units -from ase.thermochemistry import IdealGasThermo +from ase.thermochemistry import IdealGasThermo, HarmonicThermo from emmet.core.symmetry import PointGroupData from pymatgen.io.ase import AseAtomsAdaptor @@ -103,3 +103,23 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: spin=spin, ignore_imag_modes=True, ) + + def run_harmonic_thermo(self) -> HarmonicThermo: + """ + Create a HarmonicThermo object for a molecule from a given vibrational analysis. + This works for free gases, solids and adsorbates. + + Returns + ------- + HarmonicThermo object + """ + + # Ensure all negative modes are made complex + for i, f in enumerate(self.vib_freqs): + if not isinstance(f, complex) and f < 0: + self.vib_freqs[i] = complex(0 - f * 1j) + + # Convert vibrational frequencies to energies + vib_energies = [f * units.invcm for f in self.vib_freqs] + + return HarmonicThermo(vib_energies=vib_energies, potentialenergy=self.energy, ignore_imag_modes=True) diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 33d18bc048..e3c0823d81 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -23,7 +23,7 @@ from ase.atoms import Atoms from ase.io import Trajectory from ase.optimize.optimize import Optimizer - from ase.thermochemistry import IdealGasThermo + from ase.thermochemistry import IdealGasThermo, HarmonicThermo from maggma.core import Store from quacc.schemas._aliases.ase import ( @@ -215,7 +215,7 @@ def summarize_opt_run( def summarize_vib_and_thermo( vib: Vibrations, - igt: IdealGasThermo, + thermo_analysis: IdealGasThermo | HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0, charge_and_multiplicity: tuple[int, int] | None = None, @@ -230,8 +230,8 @@ def summarize_vib_and_thermo( ---------- vib ASE Vibrations object. - igt - ASE IdealGasThermo object. + thermo_analysis + ASE IdealGasThermo or HarmonicThermo object. temperature Temperature in Kelvins. pressure @@ -255,12 +255,20 @@ def summarize_vib_and_thermo( vib_task_doc = _summarize_vib_run( vib, charge_and_multiplicity=charge_and_multiplicity ) - thermo_task_doc = _summarize_ideal_gas_thermo( - igt, - temperature=temperature, - pressure=pressure, - charge_and_multiplicity=charge_and_multiplicity, - ) + + if isinstance(thermo_analysis, HarmonicThermo): + thermo_task_doc = _summarize_harmonic_thermo( + ht=thermo_analysis, + temperature=temperature, + pressure=pressure, + ) + elif isinstance(thermo_analysis, IdealGasThermo): + thermo_task_doc = _summarize_ideal_gas_thermo( + igt=thermo_analysis, + temperature=temperature, + pressure=pressure, + charge_and_multiplicity=charge_and_multiplicity, + ) unsorted_task_doc = recursive_dict_merge( vib_task_doc, thermo_task_doc, additional_fields @@ -437,3 +445,54 @@ def _summarize_ideal_gas_thermo( ) return atoms_metadata | inputs | results + +def _summarize_harmonic_thermo( + ht: HarmonicThermo, + temperature: float = 298.15, + pressure: float = 1.0, +) -> ThermoSchema: + """ + Get tabulated results from an ASE HarmonicThermo object and store them in a + database-friendly format. + + Parameters + ---------- + ht + ASE HarmonicThermo object. + temperature + Temperature in Kelvins. + pressure + Pressure in bar. + + Returns + ------- + ThermoSchema + Dictionary representation of the task document + """ + settings = get_settings() + + inputs = { + "parameters_thermo": { + "temperature": temperature, + "pressure": pressure, + "vib_freqs": [e / units.invcm for e in ht.vib_energies], + "vib_energies": ht.vib_energies.tolist(), + "n_imag": ht.n_imag, + } + } + + results = { + "results": { + "energy": ht.potentialenergy, + "helmholtz_energy": ht.get_helmholtz_energy(temperature, verbose=settings.DEBUG), + "internal_energy": ht.get_internal_energy(temperature, verbose=settings.DEBUG), + "entropy": ht.get_entropy( + temperature, verbose=settings.DEBUG + ), + "zpe": ht.get_ZPE_correction(), + } + } + + atoms_metadata = {} + + return atoms_metadata | inputs | results \ No newline at end of file diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index 31bc07217e..9055d2ba71 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -855,7 +855,7 @@ def test_mp_relax_flow_custom(tmp_path, patch_nonmetallic_taskdoc): def test_freq_job(): atoms = molecule("H2") atoms.pbc = True - atoms.set_cell([3, 3, 3]) + atoms.center(vacuum=1) output = freq_job(atoms, kpts=(1, 1, 1)) assert output["parameters"]["ediff"] == 1e-08 assert output["parameters"]["sigma"] == 0.05 diff --git a/tests/core/runners/test_thermo.py b/tests/core/runners/test_thermo.py index b81d02b235..7ccb87f96f 100644 --- a/tests/core/runners/test_thermo.py +++ b/tests/core/runners/test_thermo.py @@ -26,3 +26,13 @@ def test_run_ideal_gas(): co2.calc.results["magmom"] = 1.0 igt = ThermoRunner(co2, [-12, 526, 526, 1480, 2565]).run_ideal_gas() assert igt.get_ZPE_correction() == pytest.approx(2548.5 * invcm) + + +def test_run_harmonic_thermo(): + co2 = molecule("CO2") + ht = ThermoRunner(co2, [526, 526, 1480, 2565]).run_harmonic_thermo() + assert ht.get_ZPE_correction() == pytest.approx(2548.5 * invcm) + + co2 = molecule("CO2") + ht = ThermoRunner(co2, [-12, 526, 526, 1480, 2565]).run_harmonic_thermo() + assert ht.get_ZPE_correction() == pytest.approx(2548.5 * invcm) \ No newline at end of file diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 34b98fd284..2035ca09d9 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -5,11 +5,13 @@ from pathlib import Path import pytest + +from numpy.testing import assert_allclose as assert_close from ase.build import bulk, molecule from ase.calculators.emt import EMT from ase.io import read from ase.optimize import BFGS -from ase.thermochemistry import IdealGasThermo +from ase.thermochemistry import IdealGasThermo, HarmonicThermo from ase.units import invcm from ase.vibrations import Vibrations from maggma.stores import MemoryStore @@ -18,6 +20,7 @@ from quacc.schemas.ase import ( _summarize_ideal_gas_thermo, + _summarize_harmonic_thermo, _summarize_vib_run, summarize_opt_run, summarize_run, @@ -345,6 +348,28 @@ def test_summarize_ideal_gas_thermo(tmp_path, monkeypatch): _summarize_ideal_gas_thermo(igt, charge_and_multiplicity=[0, 1]) +def test_summarize_harmonic_thermo(tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + + # Make sure metadata is made + ht = HarmonicThermo([0.34]) + results = _summarize_harmonic_thermo(ht) + assert results["parameters_thermo"]["vib_energies"] == [0.34] + assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] + assert results["results"]["energy"] == 0 + assert_close(results["results"]["helmholtz_energy"], 0.16999995401497991,rtol=1e-5) + assert_close(results["results"]["internal_energy"], 0.1700006085385999,rtol=1e-5) + assert_close(results["results"]["entropy"], 2.1952829783392438e-09,rtol=1e-5) + assert_close(results["results"]["zpe"], 0.17,rtol=1e-5) + + + # test document can be jsanitized and decoded + d = jsanitize(results, strict=True, enum_values=True) + MontyDecoder().process_decoded(d) + + + + def test_errors(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) From 128cdeb67de2800728061798d044de15f48eb9bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:39:17 +0000 Subject: [PATCH 08/35] pre-commit auto-fixes --- src/quacc/recipes/vasp/_base.py | 1 - src/quacc/recipes/vasp/core.py | 2 +- src/quacc/runners/thermo.py | 10 +++++++--- src/quacc/schemas/ase.py | 21 ++++++++++----------- tests/core/runners/test_thermo.py | 2 +- tests/core/schemas/test_ase.py | 18 +++++++----------- 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 5e9a0a5817..e3644cb7a6 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -187,7 +187,6 @@ def run_and_summarize_vib_and_thermo( atoms, vibrations.get_frequencies(), energy=energy ).run_ideal_gas() - return summarize_vib_and_thermo( vibrations, thermo_analysis, diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index 2ff7a4ae7c..8dee078388 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -334,7 +334,7 @@ def freq_job( energy: float = 0.0, temperature: float = 298.15, pressure: float = 1.0, - thermo_method: Literal["harmonic","ideal_gas"] = "ideal_gas", + thermo_method: Literal["harmonic", "ideal_gas"] = "ideal_gas", vib_kwargs: VibKwargs | None = None, copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, **calc_kwargs, diff --git a/src/quacc/runners/thermo.py b/src/quacc/runners/thermo.py index afb11b78fd..09113b9ed2 100644 --- a/src/quacc/runners/thermo.py +++ b/src/quacc/runners/thermo.py @@ -7,7 +7,7 @@ import numpy as np from ase import units -from ase.thermochemistry import IdealGasThermo, HarmonicThermo +from ase.thermochemistry import HarmonicThermo, IdealGasThermo from emmet.core.symmetry import PointGroupData from pymatgen.io.ase import AseAtomsAdaptor @@ -103,7 +103,7 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: spin=spin, ignore_imag_modes=True, ) - + def run_harmonic_thermo(self) -> HarmonicThermo: """ Create a HarmonicThermo object for a molecule from a given vibrational analysis. @@ -122,4 +122,8 @@ def run_harmonic_thermo(self) -> HarmonicThermo: # Convert vibrational frequencies to energies vib_energies = [f * units.invcm for f in self.vib_freqs] - return HarmonicThermo(vib_energies=vib_energies, potentialenergy=self.energy, ignore_imag_modes=True) + return HarmonicThermo( + vib_energies=vib_energies, + potentialenergy=self.energy, + ignore_imag_modes=True, + ) diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index e3c0823d81..49b5a5f9f5 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -7,6 +7,7 @@ import numpy as np from ase import units from ase.io import read +from ase.thermochemistry import HarmonicThermo, IdealGasThermo from ase.vibrations import Vibrations from ase.vibrations.data import VibrationsData @@ -23,7 +24,6 @@ from ase.atoms import Atoms from ase.io import Trajectory from ase.optimize.optimize import Optimizer - from ase.thermochemistry import IdealGasThermo, HarmonicThermo from maggma.core import Store from quacc.schemas._aliases.ase import ( @@ -258,9 +258,7 @@ def summarize_vib_and_thermo( if isinstance(thermo_analysis, HarmonicThermo): thermo_task_doc = _summarize_harmonic_thermo( - ht=thermo_analysis, - temperature=temperature, - pressure=pressure, + ht=thermo_analysis, temperature=temperature, pressure=pressure ) elif isinstance(thermo_analysis, IdealGasThermo): thermo_task_doc = _summarize_ideal_gas_thermo( @@ -446,10 +444,9 @@ def _summarize_ideal_gas_thermo( return atoms_metadata | inputs | results + def _summarize_harmonic_thermo( - ht: HarmonicThermo, - temperature: float = 298.15, - pressure: float = 1.0, + ht: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 ) -> ThermoSchema: """ Get tabulated results from an ASE HarmonicThermo object and store them in a @@ -484,15 +481,17 @@ def _summarize_harmonic_thermo( results = { "results": { "energy": ht.potentialenergy, - "helmholtz_energy": ht.get_helmholtz_energy(temperature, verbose=settings.DEBUG), - "internal_energy": ht.get_internal_energy(temperature, verbose=settings.DEBUG), - "entropy": ht.get_entropy( + "helmholtz_energy": ht.get_helmholtz_energy( + temperature, verbose=settings.DEBUG + ), + "internal_energy": ht.get_internal_energy( temperature, verbose=settings.DEBUG ), + "entropy": ht.get_entropy(temperature, verbose=settings.DEBUG), "zpe": ht.get_ZPE_correction(), } } atoms_metadata = {} - return atoms_metadata | inputs | results \ No newline at end of file + return atoms_metadata | inputs | results diff --git a/tests/core/runners/test_thermo.py b/tests/core/runners/test_thermo.py index 7ccb87f96f..722cd36888 100644 --- a/tests/core/runners/test_thermo.py +++ b/tests/core/runners/test_thermo.py @@ -35,4 +35,4 @@ def test_run_harmonic_thermo(): co2 = molecule("CO2") ht = ThermoRunner(co2, [-12, 526, 526, 1480, 2565]).run_harmonic_thermo() - assert ht.get_ZPE_correction() == pytest.approx(2548.5 * invcm) \ No newline at end of file + assert ht.get_ZPE_correction() == pytest.approx(2548.5 * invcm) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 2035ca09d9..54717053f4 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -5,22 +5,21 @@ from pathlib import Path import pytest - -from numpy.testing import assert_allclose as assert_close from ase.build import bulk, molecule from ase.calculators.emt import EMT from ase.io import read from ase.optimize import BFGS -from ase.thermochemistry import IdealGasThermo, HarmonicThermo +from ase.thermochemistry import HarmonicThermo, IdealGasThermo from ase.units import invcm from ase.vibrations import Vibrations from maggma.stores import MemoryStore from monty.json import MontyDecoder, jsanitize from monty.serialization import loadfn +from numpy.testing import assert_allclose as assert_close from quacc.schemas.ase import ( - _summarize_ideal_gas_thermo, _summarize_harmonic_thermo, + _summarize_ideal_gas_thermo, _summarize_vib_run, summarize_opt_run, summarize_run, @@ -357,19 +356,16 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 - assert_close(results["results"]["helmholtz_energy"], 0.16999995401497991,rtol=1e-5) - assert_close(results["results"]["internal_energy"], 0.1700006085385999,rtol=1e-5) - assert_close(results["results"]["entropy"], 2.1952829783392438e-09,rtol=1e-5) - assert_close(results["results"]["zpe"], 0.17,rtol=1e-5) - + assert_close(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) + assert_close(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) + assert_close(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) + assert_close(results["results"]["zpe"], 0.17, rtol=1e-5) # test document can be jsanitized and decoded d = jsanitize(results, strict=True, enum_values=True) MontyDecoder().process_decoded(d) - - def test_errors(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) From 0d3436d409cb3432c006f74f9f2397ad2833bf63 Mon Sep 17 00:00:00 2001 From: benshi97 Date: Mon, 24 Jun 2024 11:43:00 +0100 Subject: [PATCH 09/35] Fix recipe docs --- docs/user/recipes/recipes_list.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/recipes/recipes_list.md b/docs/user/recipes/recipes_list.md index 484a8ab2f3..a9e9623b57 100644 --- a/docs/user/recipes/recipes_list.md +++ b/docs/user/recipes/recipes_list.md @@ -251,7 +251,7 @@ The list of available quacc recipes is shown below. The "Req'd Extras" column sp | VASP Non-SCF | `#!Python @job` | [quacc.recipes.vasp.core.non_scf_job][] | | | VASP Slab Static | `#!Python @job` | [quacc.recipes.vasp.slabs.static_job][] | | | VASP Slab Relax | `#!Python @job` | [quacc.recipes.vasp.slabs.relax_job][] | | -| VASP Frequency | `#!Python @job` | [quacc.recipes.vasp.slabs.freq_job][] | | +| VASP Frequency | `#!Python @job` | [quacc.recipes.vasp.core.freq_job][] | | | VASP Bulk to Slabs | `#!Python @flow` | [quacc.recipes.vasp.slabs.bulk_to_slabs_flow][] | | | VASP Slab to Adsorbates | `#!Python @flow` | [quacc.recipes.vasp.slabs.slab_to_ads_flow][] | | | VASP MP GGA Relax | `#!Python @job` | [quacc.recipes.vasp.mp.mp_gga_relax_job][] | `quacc[mp]` | From 44626fb1dd6ef4e1e34c90966b0534c75a9393bc Mon Sep 17 00:00:00 2001 From: benshi97 Date: Mon, 24 Jun 2024 11:49:26 +0100 Subject: [PATCH 10/35] Update tests --- tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index 9055d2ba71..b76062ecd8 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -857,7 +857,5 @@ def test_freq_job(): atoms.pbc = True atoms.center(vacuum=1) output = freq_job(atoms, kpts=(1, 1, 1)) - assert output["parameters"]["ediff"] == 1e-08 - assert output["parameters"]["sigma"] == 0.05 - assert output["parameters"]["ismear"] == 0 + assert output["parameters"]["ediff"] == 1e-07 assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) From 97b38442bcad612176511c87499fd0b96851bc39 Mon Sep 17 00:00:00 2001 From: benshi97 Date: Fri, 28 Jun 2024 18:26:09 +0200 Subject: [PATCH 11/35] Better documentation and unit tests --- src/quacc/recipes/vasp/_base.py | 13 +++++-- src/quacc/recipes/vasp/core.py | 2 + src/quacc/runners/thermo.py | 1 + src/quacc/schemas/ase.py | 13 +++++-- src/quacc/schemas/vasp.py | 2 +- .../vasp_recipes/mocked/test_vasp_recipes.py | 15 +++++++ tests/core/schemas/test_ase.py | 39 ++++++++++++++++++- 7 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index e3644cb7a6..beb63e8ad9 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -135,8 +135,8 @@ def run_and_summarize_vib_and_thermo( calc_defaults: dict[str, Any] | None = None, calc_swaps: dict[str, Any] | None = None, vib_kwargs: dict[str, Any] | None = None, - copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, additional_fields: dict[str, Any] | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None ) -> VibThermoSchema: """ Base job function for VASP recipes with ASE vibrational analysis. @@ -151,6 +151,8 @@ def run_and_summarize_vib_and_thermo( Temperature of the system pressure Pressure of the system + thermo_method + Method to use for thermochemistry. Options are "harmonic" or "ideal_gas". preset Preset to use from `quacc.calculators.vasp.presets`. calc_defaults @@ -161,6 +163,8 @@ def run_and_summarize_vib_and_thermo( keys, refer to [quacc.calculators.vasp.vasp.Vasp][]. vib_kwargs Dictionary of custom kwargs for [quacc.runners.ase.Runner.run_vib][] + additional_fields + Additional fields to supply to the summarizer. copy_files Files to copy (and decompress) from source to the runtime directory. @@ -181,15 +185,16 @@ def run_and_summarize_vib_and_thermo( if thermo_method == "harmonic": thermo_analysis = ThermoRunner( atoms, vibrations.get_frequencies(), energy=energy - ).run_harmonic() + ).run_harmonic_thermo() elif thermo_method == "ideal_gas": thermo_analysis = ThermoRunner( atoms, vibrations.get_frequencies(), energy=energy ).run_ideal_gas() return summarize_vib_and_thermo( - vibrations, - thermo_analysis, + vib = vibrations, + thermo_analysis = thermo_analysis, + atoms = atoms, temperature=temperature, pressure=pressure, additional_fields=additional_fields, diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index 8dee078388..2b39f1bd00 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -354,6 +354,8 @@ def freq_job( Temperature in Kelvins. pressure Pressure in bar. + thermo_method + Method to use for thermochemistry. Options are "harmonic" or "ideal_gas". vib_kwargs Dictionary of kwargs for the [ase.vibrations.Vibrations][] class. copy_files diff --git a/src/quacc/runners/thermo.py b/src/quacc/runners/thermo.py index 09113b9ed2..2fe2a840bb 100644 --- a/src/quacc/runners/thermo.py +++ b/src/quacc/runners/thermo.py @@ -112,6 +112,7 @@ def run_harmonic_thermo(self) -> HarmonicThermo: Returns ------- HarmonicThermo object + The ASE `HarmonicThermo` object. """ # Ensure all negative modes are made complex diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 49b5a5f9f5..123a747efe 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -216,6 +216,7 @@ def summarize_opt_run( def summarize_vib_and_thermo( vib: Vibrations, thermo_analysis: IdealGasThermo | HarmonicThermo, + atoms: Atoms | None = None, temperature: float = 298.15, pressure: float = 1.0, charge_and_multiplicity: tuple[int, int] | None = None, @@ -232,6 +233,8 @@ def summarize_vib_and_thermo( ASE Vibrations object. thermo_analysis ASE IdealGasThermo or HarmonicThermo object. + atoms + ASE Atoms object following a calculation. temperature Temperature in Kelvins. pressure @@ -258,7 +261,7 @@ def summarize_vib_and_thermo( if isinstance(thermo_analysis, HarmonicThermo): thermo_task_doc = _summarize_harmonic_thermo( - ht=thermo_analysis, temperature=temperature, pressure=pressure + atoms=atoms, ht=thermo_analysis, temperature=temperature, pressure=pressure ) elif isinstance(thermo_analysis, IdealGasThermo): thermo_task_doc = _summarize_ideal_gas_thermo( @@ -446,6 +449,7 @@ def _summarize_ideal_gas_thermo( def _summarize_harmonic_thermo( + atoms: Atoms, ht: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 ) -> ThermoSchema: """ @@ -454,6 +458,8 @@ def _summarize_harmonic_thermo( Parameters ---------- + atoms + ASE Atoms object used for the vibrational frequency calculation. ht ASE HarmonicThermo object. temperature @@ -492,6 +498,7 @@ def _summarize_harmonic_thermo( } } - atoms_metadata = {} - + atoms_metadata = atoms_to_metadata( + atoms + ) return atoms_metadata | inputs | results diff --git a/src/quacc/schemas/vasp.py b/src/quacc/schemas/vasp.py index 75d11e97de..c08878c4ac 100644 --- a/src/quacc/schemas/vasp.py +++ b/src/quacc/schemas/vasp.py @@ -233,7 +233,7 @@ def summarize_vasp_opt_run( store=None, ) vasp_summary = vasp_summarize_run( - final_atoms, + input_atoms = input_atoms, directory=directory, move_magmoms=move_magmoms, run_bader=run_bader, diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index b76062ecd8..283a5e1498 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -853,9 +853,24 @@ def test_mp_relax_flow_custom(tmp_path, patch_nonmetallic_taskdoc): def test_freq_job(): + atoms = molecule("H2") + atoms.pbc = True + atoms.center(vacuum=1) + output = freq_job(atoms, kpts=(1, 1, 1), thermo_method='harmonic') + assert output["parameters"]["ediff"] == 1e-07 + # Check that "sigma" (only used in ideal_gas) isn't a key in parameters_thermo + assert "sigma" not in output["parameters_thermo"] + assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) + + atoms = molecule("H2") atoms.pbc = True atoms.center(vacuum=1) output = freq_job(atoms, kpts=(1, 1, 1)) assert output["parameters"]["ediff"] == 1e-07 + # Check that parmeters_thermo contains + assert output["parameters_thermo"]["sigma"] == 2.0 assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) + assert len(output["results"]["vib_freqs"]) == 3 * len(atoms) - 5 + + diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 54717053f4..13c3cc5345 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -23,6 +23,7 @@ _summarize_vib_run, summarize_opt_run, summarize_run, + summarize_vib_and_thermo ) FILE_DIR = Path(__file__).parent @@ -349,10 +350,11 @@ def test_summarize_ideal_gas_thermo(tmp_path, monkeypatch): def test_summarize_harmonic_thermo(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) + atoms = molecule("H2") # Make sure metadata is made ht = HarmonicThermo([0.34]) - results = _summarize_harmonic_thermo(ht) + results = _summarize_harmonic_thermo(atoms=atoms, ht = ht) assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 @@ -365,6 +367,41 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): d = jsanitize(results, strict=True, enum_values=True) MontyDecoder().process_decoded(d) +def test_summarize_vib_and_thermo(tmp_path,monkeypatch): + monkeypatch.chdir(tmp_path) + + # Test harmonic thermo for the thermo_analysis procedure + atoms = molecule("H2") + atoms.calc = EMT() + + ht = HarmonicThermo([0.34]) + vib = Vibrations(atoms) + vib.run() + results = summarize_vib_and_thermo(vib=vib,thermo_analysis=ht,atoms=atoms) + + assert results["parameters_thermo"]["vib_energies"] == [0.34] + assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] + assert results["results"]["energy"] == 0 + assert_close(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) + assert_close(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) + assert_close(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) + assert_close(results["results"]["zpe"], 0.17, rtol=1e-5) + + # Test ideal gas thermo for the thermo_analysis procedure + atoms = molecule("N2") + atoms.calc = EMT() + igt = IdealGasThermo([0.34], "linear", atoms=atoms, spin=0, symmetrynumber=2) + results = summarize_vib_and_thermo(vib=None,thermo_analysis=igt,atoms=atoms) + + assert results["natoms"] == len(atoms) + assert results["atoms"] == atoms + assert results["parameters_thermo"]["vib_energies"] == [0.34] + assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] + assert results["results"]["energy"] == 0 + assert "pymatgen_version" in results["builder_meta"] + + + def test_errors(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) From 0c6d06711386eacca877fe212f26f7d6e59a98fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 16:27:04 +0000 Subject: [PATCH 12/35] pre-commit auto-fixes --- src/quacc/recipes/vasp/_base.py | 8 ++++---- src/quacc/schemas/ase.py | 7 ++----- src/quacc/schemas/vasp.py | 2 +- .../vasp_recipes/mocked/test_vasp_recipes.py | 5 +---- tests/core/schemas/test_ase.py | 13 ++++++------- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index beb63e8ad9..c3fb0b3ee2 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -136,7 +136,7 @@ def run_and_summarize_vib_and_thermo( calc_swaps: dict[str, Any] | None = None, vib_kwargs: dict[str, Any] | None = None, additional_fields: dict[str, Any] | None = None, - copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, ) -> VibThermoSchema: """ Base job function for VASP recipes with ASE vibrational analysis. @@ -192,9 +192,9 @@ def run_and_summarize_vib_and_thermo( ).run_ideal_gas() return summarize_vib_and_thermo( - vib = vibrations, - thermo_analysis = thermo_analysis, - atoms = atoms, + vib=vibrations, + thermo_analysis=thermo_analysis, + atoms=atoms, temperature=temperature, pressure=pressure, additional_fields=additional_fields, diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index ccf0696a7d..3b3a57d4b0 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -523,8 +523,7 @@ def _summarize_ideal_gas_thermo( def _summarize_harmonic_thermo( - atoms: Atoms, - ht: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 + atoms: Atoms, ht: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 ) -> ThermoSchema: """ Get tabulated results from an ASE HarmonicThermo object and store them in a @@ -572,7 +571,5 @@ def _summarize_harmonic_thermo( } } - atoms_metadata = atoms_to_metadata( - atoms - ) + atoms_metadata = atoms_to_metadata(atoms) return atoms_metadata | inputs | results diff --git a/src/quacc/schemas/vasp.py b/src/quacc/schemas/vasp.py index c08878c4ac..0ad177f525 100644 --- a/src/quacc/schemas/vasp.py +++ b/src/quacc/schemas/vasp.py @@ -233,7 +233,7 @@ def summarize_vasp_opt_run( store=None, ) vasp_summary = vasp_summarize_run( - input_atoms = input_atoms, + input_atoms=input_atoms, directory=directory, move_magmoms=move_magmoms, run_bader=run_bader, diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index 283a5e1498..187e9624a3 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -856,13 +856,12 @@ def test_freq_job(): atoms = molecule("H2") atoms.pbc = True atoms.center(vacuum=1) - output = freq_job(atoms, kpts=(1, 1, 1), thermo_method='harmonic') + output = freq_job(atoms, kpts=(1, 1, 1), thermo_method="harmonic") assert output["parameters"]["ediff"] == 1e-07 # Check that "sigma" (only used in ideal_gas) isn't a key in parameters_thermo assert "sigma" not in output["parameters_thermo"] assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) - atoms = molecule("H2") atoms.pbc = True atoms.center(vacuum=1) @@ -872,5 +871,3 @@ def test_freq_job(): assert output["parameters_thermo"]["sigma"] == 2.0 assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) assert len(output["results"]["vib_freqs"]) == 3 * len(atoms) - 5 - - diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 13c3cc5345..6f2c2f5b9f 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -23,7 +23,7 @@ _summarize_vib_run, summarize_opt_run, summarize_run, - summarize_vib_and_thermo + summarize_vib_and_thermo, ) FILE_DIR = Path(__file__).parent @@ -354,7 +354,7 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): # Make sure metadata is made ht = HarmonicThermo([0.34]) - results = _summarize_harmonic_thermo(atoms=atoms, ht = ht) + results = _summarize_harmonic_thermo(atoms=atoms, ht=ht) assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 @@ -367,7 +367,8 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): d = jsanitize(results, strict=True, enum_values=True) MontyDecoder().process_decoded(d) -def test_summarize_vib_and_thermo(tmp_path,monkeypatch): + +def test_summarize_vib_and_thermo(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) # Test harmonic thermo for the thermo_analysis procedure @@ -377,7 +378,7 @@ def test_summarize_vib_and_thermo(tmp_path,monkeypatch): ht = HarmonicThermo([0.34]) vib = Vibrations(atoms) vib.run() - results = summarize_vib_and_thermo(vib=vib,thermo_analysis=ht,atoms=atoms) + results = summarize_vib_and_thermo(vib=vib, thermo_analysis=ht, atoms=atoms) assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] @@ -391,7 +392,7 @@ def test_summarize_vib_and_thermo(tmp_path,monkeypatch): atoms = molecule("N2") atoms.calc = EMT() igt = IdealGasThermo([0.34], "linear", atoms=atoms, spin=0, symmetrynumber=2) - results = summarize_vib_and_thermo(vib=None,thermo_analysis=igt,atoms=atoms) + results = summarize_vib_and_thermo(vib=None, thermo_analysis=igt, atoms=atoms) assert results["natoms"] == len(atoms) assert results["atoms"] == atoms @@ -400,8 +401,6 @@ def test_summarize_vib_and_thermo(tmp_path,monkeypatch): assert results["results"]["energy"] == 0 assert "pymatgen_version" in results["builder_meta"] - - def test_errors(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) From 7616a6c34ed66cb0ee239248f41496f6f317a414 Mon Sep 17 00:00:00 2001 From: benshi97 Date: Fri, 28 Jun 2024 18:32:02 +0200 Subject: [PATCH 13/35] Fix vasp schema mistake --- src/quacc/schemas/vasp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/quacc/schemas/vasp.py b/src/quacc/schemas/vasp.py index 0ad177f525..37742a46de 100644 --- a/src/quacc/schemas/vasp.py +++ b/src/quacc/schemas/vasp.py @@ -233,7 +233,6 @@ def summarize_vasp_opt_run( store=None, ) vasp_summary = vasp_summarize_run( - input_atoms=input_atoms, directory=directory, move_magmoms=move_magmoms, run_bader=run_bader, From 4ad811af044cf020dd543c735bc46dece6cc9ede Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 28 Jun 2024 20:30:05 -0700 Subject: [PATCH 14/35] Slight refactor to reduce code duplication --- src/quacc/recipes/vasp/_base.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index c3fb0b3ee2..994c1abb57 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -182,14 +182,13 @@ def run_and_summarize_vib_and_thermo( vibrations = Runner(atoms, calc, copy_files=copy_files).run_vib( vib_kwargs=vib_kwargs ) - if thermo_method == "harmonic": - thermo_analysis = ThermoRunner( + thermo_runner = ThermoRunner( atoms, vibrations.get_frequencies(), energy=energy - ).run_harmonic_thermo() + ) + if thermo_method == "harmonic": + thermo_analysis = thermo_runner.run_harmonic_thermo() elif thermo_method == "ideal_gas": - thermo_analysis = ThermoRunner( - atoms, vibrations.get_frequencies(), energy=energy - ).run_ideal_gas() + thermo_analysis = thermo_runner.run_ideal_gas() return summarize_vib_and_thermo( vib=vibrations, From 9af030b64a429edaf9e53c1b334324225ec68bbe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Jun 2024 03:30:12 +0000 Subject: [PATCH 15/35] pre-commit auto-fixes --- src/quacc/recipes/vasp/_base.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 994c1abb57..a5ba634391 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -182,9 +182,7 @@ def run_and_summarize_vib_and_thermo( vibrations = Runner(atoms, calc, copy_files=copy_files).run_vib( vib_kwargs=vib_kwargs ) - thermo_runner = ThermoRunner( - atoms, vibrations.get_frequencies(), energy=energy - ) + thermo_runner = ThermoRunner(atoms, vibrations.get_frequencies(), energy=energy) if thermo_method == "harmonic": thermo_analysis = thermo_runner.run_harmonic_thermo() elif thermo_method == "ideal_gas": From 8823d34cdb5925cbb8d88bd311b96606cdf7727a Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 28 Jun 2024 20:37:52 -0700 Subject: [PATCH 16/35] Slight cleanup --- src/quacc/schemas/ase.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 3b3a57d4b0..1fac1dc97c 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -335,11 +335,11 @@ def summarize_vib_and_thermo( if isinstance(thermo_analysis, HarmonicThermo): thermo_task_doc = _summarize_harmonic_thermo( - atoms=atoms, ht=thermo_analysis, temperature=temperature, pressure=pressure + atoms, thermo_analysis, temperature=temperature, pressure=pressure ) elif isinstance(thermo_analysis, IdealGasThermo): thermo_task_doc = _summarize_ideal_gas_thermo( - igt=thermo_analysis, + thermo_analysis, temperature=temperature, pressure=pressure, charge_and_multiplicity=charge_and_multiplicity, @@ -523,7 +523,7 @@ def _summarize_ideal_gas_thermo( def _summarize_harmonic_thermo( - atoms: Atoms, ht: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 + atoms: Atoms, harmonic_thermo: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 ) -> ThermoSchema: """ Get tabulated results from an ASE HarmonicThermo object and store them in a @@ -533,7 +533,7 @@ def _summarize_harmonic_thermo( ---------- atoms ASE Atoms object used for the vibrational frequency calculation. - ht + harmonic_thermo ASE HarmonicThermo object. temperature Temperature in Kelvins. @@ -551,23 +551,23 @@ def _summarize_harmonic_thermo( "parameters_thermo": { "temperature": temperature, "pressure": pressure, - "vib_freqs": [e / units.invcm for e in ht.vib_energies], - "vib_energies": ht.vib_energies.tolist(), - "n_imag": ht.n_imag, + "vib_freqs": [e / units.invcm for e in harmonic_thermo.vib_energies], + "vib_energies": harmonic_thermo.vib_energies.tolist(), + "n_imag": harmonic_thermo.n_imag, } } results = { "results": { - "energy": ht.potentialenergy, - "helmholtz_energy": ht.get_helmholtz_energy( + "energy": harmonic_thermo.potentialenergy, + "helmholtz_energy": harmonic_thermo.get_helmholtz_energy( temperature, verbose=settings.DEBUG ), - "internal_energy": ht.get_internal_energy( + "internal_energy": harmonic_thermo.get_internal_energy( temperature, verbose=settings.DEBUG ), - "entropy": ht.get_entropy(temperature, verbose=settings.DEBUG), - "zpe": ht.get_ZPE_correction(), + "entropy": harmonic_thermo.get_entropy(temperature, verbose=settings.DEBUG), + "zpe": harmonic_thermo.get_ZPE_correction(), } } From 9f59d1da3f21f2573d4e9dc80adcddfb9b125148 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Jun 2024 03:37:59 +0000 Subject: [PATCH 17/35] pre-commit auto-fixes --- src/quacc/schemas/ase.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 1fac1dc97c..5938e405b2 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -523,7 +523,10 @@ def _summarize_ideal_gas_thermo( def _summarize_harmonic_thermo( - atoms: Atoms, harmonic_thermo: HarmonicThermo, temperature: float = 298.15, pressure: float = 1.0 + atoms: Atoms, + harmonic_thermo: HarmonicThermo, + temperature: float = 298.15, + pressure: float = 1.0, ) -> ThermoSchema: """ Get tabulated results from an ASE HarmonicThermo object and store them in a From 39fe02d92f5837d6c82ba20bbd75dfa63fb04c66 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 28 Jun 2024 20:44:13 -0700 Subject: [PATCH 18/35] Update test_ase.py --- tests/core/schemas/test_ase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 6f2c2f5b9f..b00fa785d3 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -354,7 +354,7 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): # Make sure metadata is made ht = HarmonicThermo([0.34]) - results = _summarize_harmonic_thermo(atoms=atoms, ht=ht) + results = _summarize_harmonic_thermo(atoms, ht) assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 From 21a40861b980d33c3f7ecba16d31e909951133bf Mon Sep 17 00:00:00 2001 From: Andrew Rosen Date: Fri, 28 Jun 2024 20:59:50 -0700 Subject: [PATCH 19/35] Fix schema --- src/quacc/schemas/ase.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 5938e405b2..522e505fc0 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -12,7 +12,7 @@ from ase.vibrations.data import VibrationsData from quacc import __version__, get_settings -from quacc.atoms.core import get_final_atoms_from_dynamics +from quacc.atoms.core import copy_atoms, get_final_atoms_from_dynamics from quacc.schemas.atoms import atoms_to_metadata from quacc.schemas.prep import prep_next_run from quacc.utils.dicts import finalize_dict, recursive_dict_merge @@ -515,8 +515,10 @@ def _summarize_ideal_gas_thermo( ) raise ValueError(msg) + atoms_no_pbcs = copy_atoms(igt.atoms) + atoms_no_pbcs.pbc = False atoms_metadata = atoms_to_metadata( - igt.atoms, charge_and_multiplicity=charge_and_multiplicity + atoms_no_pbcs, charge_and_multiplicity=charge_and_multiplicity ) return atoms_metadata | inputs | results From 61d28a6c018bbff2f384498b9c8bb1e8fae4d93c Mon Sep 17 00:00:00 2001 From: Andrew Rosen Date: Fri, 28 Jun 2024 21:03:09 -0700 Subject: [PATCH 20/35] fix --- src/quacc/runners/thermo.py | 7 ++++++- src/quacc/schemas/ase.py | 6 ++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/quacc/runners/thermo.py b/src/quacc/runners/thermo.py index 2fe2a840bb..6586f1c089 100644 --- a/src/quacc/runners/thermo.py +++ b/src/quacc/runners/thermo.py @@ -11,6 +11,8 @@ from emmet.core.symmetry import PointGroupData from pymatgen.io.ase import AseAtomsAdaptor +from quacc.atoms.core import copy_atoms + if TYPE_CHECKING: from ase.atoms import Atoms @@ -53,6 +55,9 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: ------- IdealGasThermo object """ + atoms_no_pbc = copy_atoms(self.atoms) + atoms_no_pbc.set_pbc(False) + # Ensure all negative modes are made complex for i, f in enumerate(self.vib_freqs): if not isinstance(f, complex) and f < 0: @@ -83,7 +88,7 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: # Get symmetry for later use natoms = len(self.atoms) - mol = AseAtomsAdaptor().get_molecule(self.atoms, charge_spin_check=False) + mol = AseAtomsAdaptor().get_molecule(atoms_no_pbc, charge_spin_check=False) point_group_data = PointGroupData().from_molecule(mol) # Get the geometry diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 522e505fc0..5938e405b2 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -12,7 +12,7 @@ from ase.vibrations.data import VibrationsData from quacc import __version__, get_settings -from quacc.atoms.core import copy_atoms, get_final_atoms_from_dynamics +from quacc.atoms.core import get_final_atoms_from_dynamics from quacc.schemas.atoms import atoms_to_metadata from quacc.schemas.prep import prep_next_run from quacc.utils.dicts import finalize_dict, recursive_dict_merge @@ -515,10 +515,8 @@ def _summarize_ideal_gas_thermo( ) raise ValueError(msg) - atoms_no_pbcs = copy_atoms(igt.atoms) - atoms_no_pbcs.pbc = False atoms_metadata = atoms_to_metadata( - atoms_no_pbcs, charge_and_multiplicity=charge_and_multiplicity + igt.atoms, charge_and_multiplicity=charge_and_multiplicity ) return atoms_metadata | inputs | results From f5ba21f665152ed0b1bdee8ec72a9db1bd7a003d Mon Sep 17 00:00:00 2001 From: Andrew Rosen Date: Fri, 28 Jun 2024 21:03:46 -0700 Subject: [PATCH 21/35] cleanup --- src/quacc/runners/thermo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quacc/runners/thermo.py b/src/quacc/runners/thermo.py index 6586f1c089..6a2e873602 100644 --- a/src/quacc/runners/thermo.py +++ b/src/quacc/runners/thermo.py @@ -55,8 +55,6 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: ------- IdealGasThermo object """ - atoms_no_pbc = copy_atoms(self.atoms) - atoms_no_pbc.set_pbc(False) # Ensure all negative modes are made complex for i, f in enumerate(self.vib_freqs): @@ -87,11 +85,13 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: spin = 0 # Get symmetry for later use - natoms = len(self.atoms) + atoms_no_pbc = copy_atoms(self.atoms) + atoms_no_pbc.set_pbc(False) mol = AseAtomsAdaptor().get_molecule(atoms_no_pbc, charge_spin_check=False) point_group_data = PointGroupData().from_molecule(mol) # Get the geometry + natoms = len(self.atoms) if natoms == 1: geometry = "monatomic" elif point_group_data.linear: From 997d13241165a557a221ed879a34033f723a516f Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 28 Jun 2024 21:10:43 -0700 Subject: [PATCH 22/35] Update test_vasp_recipes.py --- tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index 187e9624a3..eeabc373af 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -856,7 +856,9 @@ def test_freq_job(): atoms = molecule("H2") atoms.pbc = True atoms.center(vacuum=1) - output = freq_job(atoms, kpts=(1, 1, 1), thermo_method="harmonic") + relaxed_atoms = relax_job(atoms)["atoms"] + + output = freq_job(relaxed_atoms, kpts=(1, 1, 1), thermo_method="harmonic") assert output["parameters"]["ediff"] == 1e-07 # Check that "sigma" (only used in ideal_gas) isn't a key in parameters_thermo assert "sigma" not in output["parameters_thermo"] @@ -865,7 +867,7 @@ def test_freq_job(): atoms = molecule("H2") atoms.pbc = True atoms.center(vacuum=1) - output = freq_job(atoms, kpts=(1, 1, 1)) + output = freq_job(relaxed_atoms, kpts=(1, 1, 1)) assert output["parameters"]["ediff"] == 1e-07 # Check that parmeters_thermo contains assert output["parameters_thermo"]["sigma"] == 2.0 From 1a50f1f74e0014700595fa36e7290fe764859b6f Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 28 Jun 2024 21:11:20 -0700 Subject: [PATCH 23/35] Update thermo.py --- src/quacc/runners/thermo.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/quacc/runners/thermo.py b/src/quacc/runners/thermo.py index 6a2e873602..b8fe8680a4 100644 --- a/src/quacc/runners/thermo.py +++ b/src/quacc/runners/thermo.py @@ -11,8 +11,6 @@ from emmet.core.symmetry import PointGroupData from pymatgen.io.ase import AseAtomsAdaptor -from quacc.atoms.core import copy_atoms - if TYPE_CHECKING: from ase.atoms import Atoms @@ -85,9 +83,7 @@ def run_ideal_gas(self, spin_multiplicity: int | None = None) -> IdealGasThermo: spin = 0 # Get symmetry for later use - atoms_no_pbc = copy_atoms(self.atoms) - atoms_no_pbc.set_pbc(False) - mol = AseAtomsAdaptor().get_molecule(atoms_no_pbc, charge_spin_check=False) + mol = AseAtomsAdaptor().get_molecule(self.atoms, charge_spin_check=False) point_group_data = PointGroupData().from_molecule(mol) # Get the geometry From 6ed278f64fd382a9ab4e3fe55a013ae90957e082 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 28 Jun 2024 21:16:27 -0700 Subject: [PATCH 24/35] Update test_vasp_recipes.py --- .../core/recipes/vasp_recipes/mocked/test_vasp_recipes.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index eeabc373af..a861e636a3 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -856,18 +856,14 @@ def test_freq_job(): atoms = molecule("H2") atoms.pbc = True atoms.center(vacuum=1) - relaxed_atoms = relax_job(atoms)["atoms"] - output = freq_job(relaxed_atoms, kpts=(1, 1, 1), thermo_method="harmonic") + output = freq_job(atoms, kpts=(1, 1, 1), thermo_method="harmonic") assert output["parameters"]["ediff"] == 1e-07 # Check that "sigma" (only used in ideal_gas) isn't a key in parameters_thermo assert "sigma" not in output["parameters_thermo"] assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) - atoms = molecule("H2") - atoms.pbc = True - atoms.center(vacuum=1) - output = freq_job(relaxed_atoms, kpts=(1, 1, 1)) + output = freq_job(atoms, kpts=(1, 1, 1)) assert output["parameters"]["ediff"] == 1e-07 # Check that parmeters_thermo contains assert output["parameters_thermo"]["sigma"] == 2.0 From d62da1bb0e7dd77d6a8d49694f571899a8436475 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:47:31 +0000 Subject: [PATCH 25/35] pre-commit auto-fixes --- src/quacc/recipes/vasp/_base.py | 3 ++- src/quacc/recipes/vasp/core.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 25fb9521ae..810b6dfda4 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -21,7 +21,8 @@ OptParams, SourceDirectory, VaspASEOptSchema, - VaspSchema,VibThermoSchema, + VaspSchema, + VibThermoSchema, ) diff --git a/src/quacc/recipes/vasp/core.py b/src/quacc/recipes/vasp/core.py index cba53b1834..52797c73c5 100644 --- a/src/quacc/recipes/vasp/core.py +++ b/src/quacc/recipes/vasp/core.py @@ -28,7 +28,9 @@ OptParams, SourceDirectory, VaspASEOptSchema, - VaspSchema,VibThermoSchema,VibKwargs + VaspSchema, + VibKwargs, + VibThermoSchema, ) From 0a6141dabcdd0626b84f1192142896a77b702630 Mon Sep 17 00:00:00 2001 From: benshi97 Date: Wed, 24 Jul 2024 18:23:27 +0100 Subject: [PATCH 26/35] Fix tests --- tests/core/schemas/test_ase.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index b00fa785d3..e8d0734c92 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -392,7 +392,9 @@ def test_summarize_vib_and_thermo(tmp_path, monkeypatch): atoms = molecule("N2") atoms.calc = EMT() igt = IdealGasThermo([0.34], "linear", atoms=atoms, spin=0, symmetrynumber=2) - results = summarize_vib_and_thermo(vib=None, thermo_analysis=igt, atoms=atoms) + vib = Vibrations(atoms) + vib.run() + results = summarize_vib_and_thermo(vib=vib, thermo_analysis=igt, atoms=atoms) assert results["natoms"] == len(atoms) assert results["atoms"] == atoms From 678b2a20b8057c7ac5b4fa67f0ab4e927f359384 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Wed, 24 Jul 2024 15:01:36 -0400 Subject: [PATCH 27/35] Update vasp.py --- src/quacc/schemas/vasp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quacc/schemas/vasp.py b/src/quacc/schemas/vasp.py index 59ff154bfb..eaf9a96b6c 100644 --- a/src/quacc/schemas/vasp.py +++ b/src/quacc/schemas/vasp.py @@ -233,6 +233,7 @@ def summarize_vasp_opt_run( store=None, ) vasp_summary = vasp_summarize_run( + final_atoms, directory=directory, move_magmoms=move_magmoms, run_bader=run_bader, From 2664ba00482582b3155669824c86f1ae6aeadfe7 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 10:50:26 -0400 Subject: [PATCH 28/35] Update variable name --- src/quacc/schemas/ase.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/quacc/schemas/ase.py b/src/quacc/schemas/ase.py index 2aa85ea61d..37e97e6537 100644 --- a/src/quacc/schemas/ase.py +++ b/src/quacc/schemas/ase.py @@ -288,7 +288,7 @@ def summarize_md_run( def summarize_vib_and_thermo( vib: Vibrations, - thermo_analysis: IdealGasThermo | HarmonicThermo, + thermo_object: IdealGasThermo | HarmonicThermo, atoms: Atoms | None = None, temperature: float = 298.15, pressure: float = 1.0, @@ -304,7 +304,7 @@ def summarize_vib_and_thermo( ---------- vib ASE Vibrations object. - thermo_analysis + thermo_object ASE IdealGasThermo or HarmonicThermo object. atoms ASE Atoms object following a calculation. @@ -332,13 +332,13 @@ def summarize_vib_and_thermo( vib, charge_and_multiplicity=charge_and_multiplicity ) - if isinstance(thermo_analysis, HarmonicThermo): + if isinstance(thermo_object, HarmonicThermo): thermo_task_doc = _summarize_harmonic_thermo( - atoms, thermo_analysis, temperature=temperature, pressure=pressure + atoms, thermo_object, temperature=temperature, pressure=pressure ) - elif isinstance(thermo_analysis, IdealGasThermo): + elif isinstance(thermo_object, IdealGasThermo): thermo_task_doc = _summarize_ideal_gas_thermo( - thermo_analysis, + thermo_object, temperature=temperature, pressure=pressure, charge_and_multiplicity=charge_and_multiplicity, From 41f15656a3cc14760c4d50eb8210959dea14398d Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 10:52:07 -0400 Subject: [PATCH 29/35] Remove typo --- tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py index a861e636a3..443d1f08a0 100644 --- a/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py +++ b/tests/core/recipes/vasp_recipes/mocked/test_vasp_recipes.py @@ -865,7 +865,6 @@ def test_freq_job(): output = freq_job(atoms, kpts=(1, 1, 1)) assert output["parameters"]["ediff"] == 1e-07 - # Check that parmeters_thermo contains assert output["parameters_thermo"]["sigma"] == 2.0 assert len(output["results"]["vib_freqs_raw"]) == 3 * len(atoms) assert len(output["results"]["vib_freqs"]) == 3 * len(atoms) - 5 From 6036a4c90ea04b93e9e78163149011cb957a8030 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 10:57:42 -0400 Subject: [PATCH 30/35] Update test_ase.py --- tests/core/schemas/test_ase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index e8d0734c92..35671b8844 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -378,7 +378,7 @@ def test_summarize_vib_and_thermo(tmp_path, monkeypatch): ht = HarmonicThermo([0.34]) vib = Vibrations(atoms) vib.run() - results = summarize_vib_and_thermo(vib=vib, thermo_analysis=ht, atoms=atoms) + results = summarize_vib_and_thermo(vib, ht, atoms=atoms) assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] @@ -394,7 +394,7 @@ def test_summarize_vib_and_thermo(tmp_path, monkeypatch): igt = IdealGasThermo([0.34], "linear", atoms=atoms, spin=0, symmetrynumber=2) vib = Vibrations(atoms) vib.run() - results = summarize_vib_and_thermo(vib=vib, thermo_analysis=igt, atoms=atoms) + results = summarize_vib_and_thermo(vib, igt, atoms=atoms) assert results["natoms"] == len(atoms) assert results["atoms"] == atoms From eca59870d326f4107acac9e46e7fc38daef6dd59 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 11:16:40 -0400 Subject: [PATCH 31/35] Update _base.py --- src/quacc/recipes/vasp/_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index 810b6dfda4..a01da50315 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -188,13 +188,13 @@ def run_and_summarize_vib_and_thermo( ) thermo_runner = ThermoRunner(atoms, vibrations.get_frequencies(), energy=energy) if thermo_method == "harmonic": - thermo_analysis = thermo_runner.run_harmonic_thermo() + thermo_object = thermo_runner.run_harmonic_thermo() elif thermo_method == "ideal_gas": - thermo_analysis = thermo_runner.run_ideal_gas() + thermo_object = thermo_runner.run_ideal_gas() return summarize_vib_and_thermo( vib=vibrations, - thermo_analysis=thermo_analysis, + thermo_object, atoms=atoms, temperature=temperature, pressure=pressure, From cd937a95c3626879b3e4532d658a09125c2b254f Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 11:17:07 -0400 Subject: [PATCH 32/35] Update _base.py --- src/quacc/recipes/vasp/_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quacc/recipes/vasp/_base.py b/src/quacc/recipes/vasp/_base.py index a01da50315..59ea4809a7 100644 --- a/src/quacc/recipes/vasp/_base.py +++ b/src/quacc/recipes/vasp/_base.py @@ -193,7 +193,7 @@ def run_and_summarize_vib_and_thermo( thermo_object = thermo_runner.run_ideal_gas() return summarize_vib_and_thermo( - vib=vibrations, + vibrations, thermo_object, atoms=atoms, temperature=temperature, From 4d030cbf8a36e29943a2c77ffc6e189724becd0f Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 11:17:32 -0400 Subject: [PATCH 33/35] Update test_ase.py --- tests/core/schemas/test_ase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 35671b8844..befc81cd85 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -371,7 +371,7 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): def test_summarize_vib_and_thermo(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) - # Test harmonic thermo for the thermo_analysis procedure + # Test harmonic thermo atoms = molecule("H2") atoms.calc = EMT() @@ -388,7 +388,7 @@ def test_summarize_vib_and_thermo(tmp_path, monkeypatch): assert_close(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) assert_close(results["results"]["zpe"], 0.17, rtol=1e-5) - # Test ideal gas thermo for the thermo_analysis procedure + # Test ideal gas thermo atoms = molecule("N2") atoms.calc = EMT() igt = IdealGasThermo([0.34], "linear", atoms=atoms, spin=0, symmetrynumber=2) From 77c446fccdc16c233874125d9e584325b831cfeb Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Fri, 26 Jul 2024 11:18:23 -0400 Subject: [PATCH 34/35] Update test_ase.py --- tests/core/schemas/test_ase.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index befc81cd85..5c182594d8 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -15,7 +15,7 @@ from maggma.stores import MemoryStore from monty.json import MontyDecoder, jsanitize from monty.serialization import loadfn -from numpy.testing import assert_allclose as assert_close +from numpy.testing import assert_allclose from quacc.schemas.ase import ( _summarize_harmonic_thermo, @@ -358,10 +358,10 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 - assert_close(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) - assert_close(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) - assert_close(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) - assert_close(results["results"]["zpe"], 0.17, rtol=1e-5) + assert_allclose(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) + assert_allclose(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) + assert_allclose(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) + assert_allclose(results["results"]["zpe"], 0.17, rtol=1e-5) # test document can be jsanitized and decoded d = jsanitize(results, strict=True, enum_values=True) @@ -383,10 +383,10 @@ def test_summarize_vib_and_thermo(tmp_path, monkeypatch): assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 - assert_close(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) - assert_close(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) - assert_close(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) - assert_close(results["results"]["zpe"], 0.17, rtol=1e-5) + assert_allclose(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) + assert_allclose(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) + assert_allclose(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) + assert_allclose(results["results"]["zpe"], 0.17, rtol=1e-5) # Test ideal gas thermo atoms = molecule("N2") From 2416a2831ad997991333fa220a76e2a0c401cd72 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:18:30 +0000 Subject: [PATCH 35/35] pre-commit auto-fixes --- tests/core/schemas/test_ase.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/core/schemas/test_ase.py b/tests/core/schemas/test_ase.py index 5c182594d8..80568c9e36 100644 --- a/tests/core/schemas/test_ase.py +++ b/tests/core/schemas/test_ase.py @@ -358,8 +358,12 @@ def test_summarize_harmonic_thermo(tmp_path, monkeypatch): assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 - assert_allclose(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) - assert_allclose(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) + assert_allclose( + results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5 + ) + assert_allclose( + results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5 + ) assert_allclose(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) assert_allclose(results["results"]["zpe"], 0.17, rtol=1e-5) @@ -383,8 +387,12 @@ def test_summarize_vib_and_thermo(tmp_path, monkeypatch): assert results["parameters_thermo"]["vib_energies"] == [0.34] assert results["parameters_thermo"]["vib_freqs"] == [0.34 / invcm] assert results["results"]["energy"] == 0 - assert_allclose(results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5) - assert_allclose(results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5) + assert_allclose( + results["results"]["helmholtz_energy"], 0.16999995401497991, rtol=1e-5 + ) + assert_allclose( + results["results"]["internal_energy"], 0.1700006085385999, rtol=1e-5 + ) assert_allclose(results["results"]["entropy"], 2.1952829783392438e-09, rtol=1e-5) assert_allclose(results["results"]["zpe"], 0.17, rtol=1e-5)