Skip to content

Commit

Permalink
Refactor runners into classes (#2020)
Browse files Browse the repository at this point in the history
## Summary of Changes

Partially addresses #1833. This should not affect anything user-facing,
although it does close #2230.

### Checklist

- [X] I have read the ["Guidelines"
section](https://quantum-accelerators.github.io/quacc/dev/contributing.html#guidelines)
of the contributing guide. Don't lie! 😉
- [X] My PR is on a custom branch and is _not_ named `main`.
- [X] I have added relevant, comprehensive [unit
tests](https://quantum-accelerators.github.io/quacc/dev/contributing.html#unit-tests).

### Notes

- Your PR will likely not be merged without proper and thorough tests.
- If you are an external contributor, you will see a comment from
[@buildbot-princeton](https://github.com/buildbot-princeton). This is
solely for the maintainers.
- When your code is ready for review, ping one of the [active
maintainers](https://quantum-accelerators.github.io/quacc/about/contributors.html#active-maintainers).

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
Andrew-S-Rosen and pre-commit-ci[bot] authored Jun 11, 2024
1 parent ebd32d6 commit 5122a93
Show file tree
Hide file tree
Showing 29 changed files with 601 additions and 606 deletions.
10 changes: 0 additions & 10 deletions docs/dev/recipes/jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,6 @@ The following are typical guidelines for jobs:

- When given the choice between using an ASE optimizer and the electronic structure package's built-in optimizer, you should typically use the latter.

## Runners

Three of the most common runners are summarized below:

1. [quacc.runners.ase.run_calc][]: Runs a calculation using an ASE calculator. This is the most common runner.

2. [quacc.runners.ase.run_opt][]: Runs a geometry optimization using an ASE optimizer.

3. [quacc.runners.ase.run_vib][]: Runs a vibrational analysis using ASE's `Vibrations` module.

## Schemas

A schema is a dictionary containing tabulated input and output properties from a calculation.
Expand Down
4 changes: 2 additions & 2 deletions src/quacc/recipes/common/phonons.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from quacc import job, subflow
from quacc.atoms.phonons import get_phonopy, phonopy_atoms_to_ase_atoms
from quacc.runners.phonons import run_phonopy
from quacc.runners.phonons import PhonopyRunner
from quacc.schemas.phonons import summarize_phonopy

has_phonopy = bool(find_spec("phonopy"))
Expand Down Expand Up @@ -98,7 +98,7 @@ def _thermo_job(
) -> PhononSchema:
parameters = force_job_results[-1].get("parameters")
forces = [output["results"]["forces"] for output in force_job_results]
phonopy_results = run_phonopy(
phonopy_results = PhonopyRunner().run_phonopy(
phonopy, forces, t_step=t_step, t_min=t_min, t_max=t_max
)

Expand Down
8 changes: 5 additions & 3 deletions src/quacc/recipes/dftb/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from ase.calculators.dftb import Dftb

from quacc.runners.ase import run_calc
from quacc.runners.ase import Runner
from quacc.schemas.ase import summarize_run
from quacc.utils.dicts import recursive_dict_merge

Expand Down Expand Up @@ -55,7 +55,9 @@ def run_and_summarize(
"""
calc_flags = recursive_dict_merge(calc_defaults, calc_swaps)

atoms.calc = Dftb(**calc_flags)
final_atoms = run_calc(atoms, geom_file=GEOM_FILE, copy_files=copy_files)
calc = Dftb(**calc_flags)
final_atoms = Runner(atoms, calc, copy_files=copy_files).run_calc(
geom_file=GEOM_FILE
)

return summarize_run(final_atoms, atoms, additional_fields=additional_fields)
14 changes: 8 additions & 6 deletions src/quacc/recipes/emt/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ase.calculators.emt import EMT

from quacc import job
from quacc.runners.ase import run_calc, run_opt
from quacc.runners.ase import Runner
from quacc.schemas.ase import summarize_opt_run, summarize_run

if TYPE_CHECKING:
Expand Down Expand Up @@ -48,8 +48,8 @@ def static_job(
Dictionary of results, specified in [quacc.schemas.ase.summarize_run][].
See the type-hint for the data structure.
"""
atoms.calc = EMT(**calc_kwargs)
final_atoms = run_calc(atoms, copy_files=copy_files)
calc = EMT(**calc_kwargs)
final_atoms = Runner(atoms, calc, copy_files=copy_files).run_calc()

return summarize_run(final_atoms, atoms, additional_fields={"name": "EMT Static"})

Expand All @@ -73,7 +73,7 @@ def relax_job(
Whether to relax the cell
opt_params
Dictionary of custom kwargs for the optimization process. For a list
of available keys, refer to [quacc.runners.ase.run_opt][].
of available keys, refer to [quacc.runners.ase.Runner.run_opt][].
copy_files
Files to copy (and decompress) from source to the runtime directory.
**calc_kwargs
Expand All @@ -89,7 +89,9 @@ def relax_job(
"""
opt_params = opt_params or {}

atoms.calc = EMT(**calc_kwargs)
dyn = run_opt(atoms, relax_cell=relax_cell, copy_files=copy_files, **opt_params)
calc = EMT(**calc_kwargs)
dyn = Runner(atoms, calc, copy_files=copy_files).run_opt(
relax_cell=relax_cell, **opt_params
)

return summarize_opt_run(dyn, additional_fields={"name": "EMT Relax"})
46 changes: 24 additions & 22 deletions src/quacc/recipes/espresso/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
prepare_copy_files,
remove_conflicting_kpts_kspacing,
)
from quacc.runners.ase import run_calc, run_opt
from quacc.runners.ase import Runner
from quacc.schemas.ase import summarize_opt_run, summarize_run
from quacc.utils.dicts import recursive_dict_merge

if TYPE_CHECKING:
from typing import Any

from ase.calculators.genericfileio import GenericFileIOCalculator

from quacc.runners.ase import OptParams
from quacc.schemas._aliases.ase import RunSchema
from quacc.utils.files import Filenames, SourceDirectory
Expand Down Expand Up @@ -74,8 +76,9 @@ def run_and_summarize(
RunSchema
Dictionary of results from [quacc.schemas.ase.summarize_run][]
"""
atoms = prepare_atoms(
atoms=atoms,
atoms = Atoms() if atoms is None else atoms
calc = prepare_calc(
atoms,
preset=preset,
template=template,
profile=profile,
Expand All @@ -85,13 +88,15 @@ def run_and_summarize(

updated_copy_files = prepare_copy(
copy_files=copy_files,
calc_params=atoms.calc.user_calc_params,
binary=atoms.calc.template.binary,
calc_params=calc.user_calc_params,
binary=calc.template.binary,
)

geom_file = template.outputname if template.binary == "pw" else None

final_atoms = run_calc(atoms, geom_file=geom_file, copy_files=updated_copy_files)
final_atoms = Runner(atoms, calc, copy_files=updated_copy_files).run_calc(
geom_file=geom_file
)

return summarize_run(
final_atoms, atoms, move_magmoms=True, additional_fields=additional_fields
Expand Down Expand Up @@ -150,8 +155,9 @@ def run_and_summarize_opt(
RunSchema
Dictionary of results from [quacc.schemas.ase.summarize_run][]
"""
atoms = prepare_atoms(
atoms=atoms,
atoms = Atoms() if atoms is None else atoms
calc = prepare_calc(
atoms,
preset=preset,
template=template,
profile=profile,
Expand All @@ -161,30 +167,30 @@ def run_and_summarize_opt(

updated_copy_files = prepare_copy(
copy_files=copy_files,
calc_params=atoms.calc.user_calc_params,
binary=atoms.calc.template.binary,
calc_params=calc.user_calc_params,
binary=calc.template.binary,
)

opt_flags = recursive_dict_merge(opt_defaults, opt_params)

dyn = run_opt(atoms, copy_files=updated_copy_files, **opt_flags)
dyn = Runner(atoms, calc, copy_files=updated_copy_files).run_opt(**opt_flags)

return summarize_opt_run(
dyn, move_magmoms=True, additional_fields=additional_fields
)


def prepare_atoms(
atoms: Atoms | None = None,
def prepare_calc(
atoms: Atoms,
preset: str | None = None,
template: EspressoTemplate | None = None,
profile: EspressoProfile | None = None,
calc_defaults: dict[str, Any] | None = None,
calc_swaps: dict[str, Any] | None = None,
) -> Atoms:
) -> GenericFileIOCalculator:
"""
Commonly used preparation function to merge parameters
and attach an Espresso calculator accordingly.
and create an Espresso calculator accordingly.
Parameters
----------
Expand All @@ -205,10 +211,9 @@ def prepare_atoms(
Returns
-------
Atoms
Atoms object with attached Espresso calculator.
GenericFileIOCalculator
The Espresso calculator.
"""
atoms = Atoms() if atoms is None else atoms
calc_defaults = calc_defaults or {}
calc_swaps = calc_swaps or {}

Expand All @@ -222,19 +227,16 @@ def prepare_atoms(
calc_swaps["input_data"].to_nested(binary=binary, **calc_swaps)

calc_defaults = remove_conflicting_kpts_kspacing(calc_defaults, calc_swaps)

calc_flags = recursive_dict_merge(calc_defaults, calc_swaps)

atoms.calc = Espresso(
return Espresso(
input_atoms=atoms,
preset=preset,
template=template,
profile=profile,
**calc_flags,
)

return atoms


def prepare_copy(
copy_files: (
Expand Down
2 changes: 1 addition & 1 deletion src/quacc/recipes/espresso/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def ase_relax_job(
Whether to relax the cell or not.
opt_params
Dictionary of custom kwargs for the optimization process. For a list
of available keys, refer to [quacc.runners.ase.run_opt][].
of available keys, refer to [quacc.runners.ase.Runner.run_opt][].
copy_files
Source directory or directories to copy files from. If a `SourceDirectory` or a
list of `SourceDirectory` is provided, this interface will automatically guess
Expand Down
6 changes: 3 additions & 3 deletions src/quacc/recipes/gaussian/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ase.calculators.gaussian import Gaussian

from quacc import SETTINGS
from quacc.runners.ase import run_calc
from quacc.runners.ase import Runner
from quacc.schemas.cclib import cclib_summarize_run
from quacc.utils.dicts import recursive_dict_merge

Expand Down Expand Up @@ -57,7 +57,7 @@ def run_and_summarize(
"""
calc_flags = recursive_dict_merge(calc_defaults, calc_swaps)

atoms.calc = Gaussian(command=GAUSSIAN_CMD, label=_LABEL, **calc_flags)
atoms = run_calc(atoms, geom_file=LOG_FILE, copy_files=copy_files)
calc = Gaussian(command=GAUSSIAN_CMD, label=_LABEL, **calc_flags)
atoms = Runner(atoms, calc, copy_files=copy_files).run_calc(geom_file=LOG_FILE)

return cclib_summarize_run(atoms, LOG_FILE, additional_fields=additional_fields)
10 changes: 4 additions & 6 deletions src/quacc/recipes/gulp/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ase.calculators.gulp import GULP

from quacc import SETTINGS
from quacc.runners.ase import run_calc
from quacc.runners.ase import Runner
from quacc.schemas.ase import summarize_run
from quacc.utils.lists import merge_list_params

Expand Down Expand Up @@ -94,17 +94,15 @@ def run_and_summarize(

if SETTINGS.GULP_LIB:
os.environ["GULP_LIB"] = str(SETTINGS.GULP_LIB)
atoms.calc = GULP(
calc = GULP(
command=GULP_CMD,
keywords=gulp_keywords,
options=gulp_options,
library=library,
**calc_kwargs,
)
final_atoms = run_calc(
atoms,
geom_file=GEOM_FILE_PBC if atoms.pbc.any() else GEOM_FILE_NOPBC,
copy_files=copy_files,
final_atoms = Runner(atoms, calc, copy_files=copy_files).run_calc(
geom_file=GEOM_FILE_PBC if atoms.pbc.any() else GEOM_FILE_NOPBC
)

if (
Expand Down
24 changes: 14 additions & 10 deletions src/quacc/recipes/lj/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from ase.calculators.lj import LennardJones

from quacc import job
from quacc.runners.ase import run_calc, run_opt, run_vib
from quacc.runners.thermo import run_ideal_gas
from quacc.runners.ase import Runner
from quacc.runners.thermo import ThermoRunner
from quacc.schemas.ase import summarize_opt_run, summarize_run, summarize_vib_and_thermo

if TYPE_CHECKING:
Expand Down Expand Up @@ -49,8 +49,8 @@ def static_job(
Dictionary of results, specified in [quacc.schemas.ase.summarize_run][].
See the type-hint for the data structure.
"""
atoms.calc = LennardJones(**calc_kwargs)
final_atoms = run_calc(atoms, copy_files=copy_files)
calc = LennardJones(**calc_kwargs)
final_atoms = Runner(atoms, calc, copy_files=copy_files).run_calc()

return summarize_run(final_atoms, atoms, additional_fields={"name": "LJ Static"})

Expand All @@ -71,7 +71,7 @@ def relax_job(
Atoms object
opt_params
Dictionary of custom kwargs for the optimization process. For a list
of available keys, refer to [quacc.runners.ase.run_opt][].
of available keys, refer to [quacc.runners.ase.Runner.run_opt][].
copy_files
Files to copy (and decompress) from source to the runtime directory.
**calc_kwargs
Expand All @@ -87,8 +87,8 @@ def relax_job(
"""
opt_params = opt_params or {}

atoms.calc = LennardJones(**calc_kwargs)
dyn = run_opt(atoms, copy_files=copy_files, **opt_params)
calc = LennardJones(**calc_kwargs)
dyn = Runner(atoms, calc, copy_files=copy_files).run_opt(**opt_params)

return summarize_opt_run(dyn, additional_fields={"name": "LJ Relax"})

Expand Down Expand Up @@ -133,9 +133,13 @@ def freq_job(
"""
vib_kwargs = vib_kwargs or {}

atoms.calc = LennardJones(**calc_kwargs)
vibrations = run_vib(atoms, vib_kwargs=vib_kwargs, copy_files=copy_files)
igt = run_ideal_gas(atoms, vibrations.get_frequencies(), energy=energy)
calc = LennardJones(**calc_kwargs)
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,
Expand Down
12 changes: 6 additions & 6 deletions src/quacc/recipes/mlp/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from quacc import job
from quacc.recipes.mlp._base import pick_calculator
from quacc.runners.ase import run_calc, run_opt
from quacc.runners.ase import Runner
from quacc.schemas.ase import summarize_opt_run, summarize_run
from quacc.utils.dicts import recursive_dict_merge

Expand Down Expand Up @@ -49,10 +49,10 @@ def static_job(
Dictionary of results from [quacc.schemas.ase.summarize_run][].
See the type-hint for the data structure.
"""
atoms.calc = pick_calculator(method, **calc_kwargs)
calc = pick_calculator(method, **calc_kwargs)
if properties is None:
properties = ["energy", "forces"]
final_atoms = run_calc(atoms, properties=properties)
final_atoms = Runner(atoms, calc).run_calc(properties=properties)
return summarize_run(
final_atoms, atoms, additional_fields={"name": f"{method} Static"}
)
Expand All @@ -79,7 +79,7 @@ def relax_job(
Whether to relax the cell.
opt_params
Dictionary of custom kwargs for the optimization process. For a list
of available keys, refer to [quacc.runners.ase.run_opt][].
of available keys, refer to [quacc.runners.ase.Runner.run_opt][].
**calc_kwargs
Custom kwargs for the underlying calculator. Set a value to
`quacc.Remove` to remove a pre-existing key entirely. For a list of available
Expand All @@ -95,8 +95,8 @@ def relax_job(
opt_defaults = {"fmax": 0.05}
opt_flags = recursive_dict_merge(opt_defaults, opt_params)

atoms.calc = pick_calculator(method, **calc_kwargs)
calc = pick_calculator(method, **calc_kwargs)

dyn = run_opt(atoms, relax_cell=relax_cell, **opt_flags)
dyn = Runner(atoms, calc).run_opt(relax_cell=relax_cell, **opt_flags)

return summarize_opt_run(dyn, additional_fields={"name": f"{method} Relax"})
Loading

0 comments on commit 5122a93

Please sign in to comment.