From 149379e140e1079bfac89fac5632bd032d566265 Mon Sep 17 00:00:00 2001 From: "B.Ayers" Date: Mon, 17 Mar 2025 00:36:57 +0000 Subject: [PATCH 1/3] Added basic recipe for FHI-Aims , with support for static, relax and ase-relax as well as a working kspacing --- docs/about/contributors.md | 1 + docs/install/codes.md | 14 ++ src/quacc/recipes/aims/__init__.py | 1 + src/quacc/recipes/aims/_base.py | 161 +++++++++++++++++ src/quacc/recipes/aims/core.py | 271 +++++++++++++++++++++++++++++ src/quacc/settings.py | 28 +++ src/quacc/utils/kpts.py | 29 +++ 7 files changed, 505 insertions(+) create mode 100644 src/quacc/recipes/aims/__init__.py create mode 100644 src/quacc/recipes/aims/_base.py create mode 100644 src/quacc/recipes/aims/core.py diff --git a/docs/about/contributors.md b/docs/about/contributors.md index 3dc99f7083..217a35f315 100644 --- a/docs/about/contributors.md +++ b/docs/about/contributors.md @@ -20,6 +20,7 @@ Additional contributions were made by the individuals listed [here](https://gith - [@kumaranu](https://github.com/kumaranu): NewtonNet recipes. - [@nekkrad](https://github.com/nekkrad): ONETEP recipes. +- [@BCAyers2000](https://github.com/BCAyers2000): FHI-Aims recipes. - [@rwexler](https://github.com/rwexler): Defect utilities and EMT defect recipe. - [@samblau](https://github.com/samblau): Custom Q-Chem calculator, Q-Chem recipes. - [@tomdemeyere](https://github.com/tomdemeyere): Custom Espresso calculator, Espresso recipes. diff --git a/docs/install/codes.md b/docs/install/codes.md index 303ca60169..c52f598898 100644 --- a/docs/install/codes.md +++ b/docs/install/codes.md @@ -145,6 +145,20 @@ If you plan to use TBLite with quacc, you will need to install the tblite interf Refer to the [TBLite documentation](https://tblite.readthedocs.io/en/latest/tutorial/parallel.html) for details on how to parallelize calculations and adjust the memory limits. +## FHI-aims + +If you plan on using FHI-aims with quacc, you will need to obtain and install FHI-aims. This can be done as described in the [Get the Code](https://fhi-aims.org/get-the-code-menu/get-the-code) section of the FHI-aims website. + +At minimum, you will need to define and set `AIMS_BIN` to be the path to the FHI-aims executable and `AIMS_SPECIES_DEFAULTS` to be the path to the species defaults directory. This can be done as described in the section on ["Modifying Quacc Settings"](../user/settings/settings.md). + +An example is provided below on how to define the commands in your `~/.bashrc`: + +```bash +export QUACC_AIMS_BIN="/path/to/aims.x" +export QUACC_AIMS_PARALLEL_CMD="mpirun -np 4" +export QUACC_AIMS_SPECIES_DEFAULTS="/path/to/fhi-aims/species_defaults/defaults_2020/" +``` + ## VASP To use quacc with VASP, you will need to define several environment variables, as described in the section on ["Modifying Quacc Settings"](../user/settings/settings.md). The most important are listed below: diff --git a/src/quacc/recipes/aims/__init__.py b/src/quacc/recipes/aims/__init__.py new file mode 100644 index 0000000000..0b30376781 --- /dev/null +++ b/src/quacc/recipes/aims/__init__.py @@ -0,0 +1 @@ +"""Recipes for FHI-Aims.""" diff --git a/src/quacc/recipes/aims/_base.py b/src/quacc/recipes/aims/_base.py new file mode 100644 index 0000000000..2861977caf --- /dev/null +++ b/src/quacc/recipes/aims/_base.py @@ -0,0 +1,161 @@ +"""Base jobs for FHI-aims.""" + +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +from ase.calculators.aims import Aims, AimsProfile + +from quacc import get_settings +from quacc.runners.ase import Runner +from quacc.schemas.ase import Summarize +from quacc.utils.dicts import recursive_dict_merge +from quacc.utils.kpts import kspacing_to_grid + +if TYPE_CHECKING: + from typing import Any + + from ase.atoms import Atoms + + from quacc.types import Filenames, OptParams, RunSchema, SourceDirectory + + +def run_and_summarize( + atoms: Atoms, + calc_defaults: dict[str, Any] | None = None, + calc_swaps: dict[str, Any] | None = None, + additional_fields: dict[str, Any] | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, +) -> RunSchema: + """ + Base function to carry out FHI-aims recipes. + + Parameters + ---------- + atoms + Atoms object + calc_defaults + The default calculator parameters. + calc_swaps + Custom kwargs for the FHI-aims calculator. Set a value to + `quacc.Remove` to remove a pre-existing key entirely. For a list of available + keys, refer to the [ase.calculators.aims.Aims][] calculator. + additional_fields + Any additional fields to supply to the summarizer. + copy_files + Files to copy (and decompress) from source to the runtime directory. + + Returns + ------- + RunSchema + Dictionary of results from [quacc.schemas.ase.Summarize.run][] + """ + calc = prep_calculator(atoms, calc_defaults=calc_defaults, calc_swaps=calc_swaps) + final_atoms = Runner(atoms, calc, copy_files=copy_files).run_calc() + + return Summarize(move_magmoms=True, additional_fields=additional_fields).run( + final_atoms, atoms + ) + + +def run_and_summarize_opt( + atoms: Atoms, + calc_defaults: dict[str, Any] | None = None, + calc_swaps: dict[str, Any] | None = None, + opt_defaults: dict[str, Any] | None = None, + opt_params: OptParams | None = None, + additional_fields: dict[str, Any] | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, +) -> RunSchema: + """ + Base function to carry out FHI-aims recipes with ASE optimizers. + + Parameters + ---------- + atoms + Atoms object + calc_defaults + The default calculator parameters. + calc_swaps + Custom kwargs for the FHI-aims calculator. Set a value to + `quacc.Remove` to remove a pre-existing key entirely. For a list of available + keys, refer to the [ase.calculators.aims.Aims][] calculator. + opt_defaults + The default optimization parameters. + opt_params + Dictionary of custom kwargs for the optimization process. For a list + of available keys, refer to [quacc.runners.ase.Runner.run_opt][]. + additional_fields + Any additional fields to supply to the summarizer. + copy_files + Files to copy (and decompress) from source to the runtime directory. + + Returns + ------- + RunSchema + Dictionary of results from [quacc.schemas.ase.Summarize.run][] + """ + opt_flags = recursive_dict_merge(opt_defaults, opt_params) + calc = prep_calculator(atoms, calc_defaults=calc_defaults, calc_swaps=calc_swaps) + dyn = Runner(atoms, calc, copy_files=copy_files).run_opt(**opt_flags) + + return Summarize(move_magmoms=True, additional_fields=additional_fields).opt(dyn) + + +def prep_calculator( + atoms: Atoms, + calc_defaults: dict[str, Any] | None = None, + calc_swaps: dict[str, Any] | None = None, +) -> Aims: + """ + Prepare the FHI-aims calculator. + + Parameters + ---------- + atoms + Atoms object + calc_defaults + The default calculator parameters. + calc_swaps + Custom kwargs for the FHI-aims calculator. Set a value to + `quacc.Remove` to remove a pre-existing key entirely. For a list of available + keys, refer to the [ase.calculators.aims.Aims][] calculator. + + Returns + ------- + Aims + The FHI-aims calculator. + """ + calc_flags = recursive_dict_merge(calc_defaults or {}, calc_swaps or {}) + settings = get_settings() + species_dir = calc_flags.pop("species_dir", None) + + if not any(atoms.pbc): + for key in ["kspacing", "k_grid", "k_grid_density"]: + if key in calc_flags: + calc_flags.pop(key) + elif ( + "kspacing" in calc_flags + and "k_grid" not in calc_flags + and "k_grid_density" not in calc_flags + ): + kspacing = calc_flags.pop("kspacing") + calc_flags["k_grid"] = kspacing_to_grid(atoms, kspacing) + + if "spin" not in calc_flags and hasattr(atoms, "get_initial_magnetic_moments"): + magmoms = atoms.get_initial_magnetic_moments() + if magmoms is not None and any(abs(m) > 1e-6 for m in magmoms): + calc_flags["spin"] = "collinear" + + aims_cmd = f"{settings.AIMS_PARALLEL_CMD} {settings.AIMS_BIN}" + species_path = settings.AIMS_SPECIES_DEFAULTS + if species_dir: + species_path = Path(species_path) / species_dir + + return Aims( + profile=AimsProfile( + command=aims_cmd.strip(), default_species_directory=str(species_path) + ), + **calc_flags, + ) diff --git a/src/quacc/recipes/aims/core.py b/src/quacc/recipes/aims/core.py new file mode 100644 index 0000000000..d73997b0b1 --- /dev/null +++ b/src/quacc/recipes/aims/core.py @@ -0,0 +1,271 @@ +"""Core recipes for FHI-aims.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Literal + +from ase.optimize import BFGS + +from quacc import job +from quacc.atoms.core import check_is_metal +from quacc.recipes.aims._base import run_and_summarize, run_and_summarize_opt + +if TYPE_CHECKING: + from typing import Any + + from ase.atoms import Atoms + + from quacc.types import Filenames, OptParams, RunSchema, SourceDirectory + +BASE_SET_METAL = { + "occupation_type": "cold 0.1", + "relativistic": "atomic_zora scalar", + "sc_accuracy_rho": 1e-5, + "charge_mix_param": 0.05, + "mixer": "pulay", + "n_max_pulay": 14, + "xc": "pbe", + "output_level": "normal", +} + +BASE_SET_NON_METAL = { + "occupation_type": "gaussian 0.01", + "relativistic": "atomic_zora scalar", + "sc_accuracy_rho": 1e-5, + "xc": "pbe", + "charge_mix_param": 0.20, + "mixer": "pulay", + "output_level": "normal", +} + +KSPACING_METAL = 0.033 +KSPACING_NON_METAL = 0.045 + + +@job +def static_job( + atoms: Atoms, + species_defaults: Literal[ + "light", "intermediate", "tight", "really_tight" + ] = "intermediate", + kspacing: float | None = None, + spin: Literal["none", "collinear", "non-collinear"] | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, + additional_fields: dict[str, Any] | None = None, + **calc_kwargs, +) -> RunSchema: + """ + Function to carry out a basic SCF calculation with FHI-aims. + + Parameters + ---------- + atoms + The Atoms object. + species_defaults + The level of accuracy for the basis set and integration grids. + Options: "light", "intermediate", "tight", "really_tight". + Default is "intermediate" which is suitable for production SCF calculations. + kspacing + The kpoint spacing in Å^-1. If not provided, defaults to + KSPACING_METAL (0.033) for metals and KSPACING_NON_METAL (0.045) for non-metals. + Ignored for aperiodic systems. + spin + Spin treatment. Options are "none", "collinear", or "non-collinear". + Default is None, which will automatically set to "collinear" if magnetic + moments are detected in the atoms object. + copy_files + Files to copy (and decompress) from source to the runtime directory. + additional_fields + Additional fields to add to the results dictionary. + **calc_kwargs + Custom kwargs for the FHI-aims calculator. For a list of available + keys, refer to the [ase.calculators.aims.Aims][] calculator. + + Returns + ------- + RunSchema + Dictionary of results, specified in [quacc.schemas.ase.Summarize.run][]. + See the type-hint for the data structure. + """ + calc_defaults = ( + BASE_SET_METAL.copy() if check_is_metal(atoms) else BASE_SET_NON_METAL.copy() + ) + calc_defaults["species_dir"] = species_defaults + + if kspacing is not None: + calc_defaults["kspacing"] = kspacing + else: + calc_defaults["kspacing"] = ( + KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL + ) + + if spin is not None: + calc_defaults["spin"] = spin + + return run_and_summarize( + atoms, + calc_defaults=calc_defaults, + calc_swaps=calc_kwargs, + additional_fields={"name": "FHI-aims Static"} | (additional_fields or {}), + copy_files=copy_files, + ) + + +@job +def relax_job( + atoms: Atoms, + species_defaults: Literal[ + "light", "intermediate", "tight", "really_tight" + ] = "light", + kspacing: float | None = None, + spin: Literal["none", "collinear", "non-collinear"] | None = None, + relax_cell: bool = False, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, + additional_fields: dict[str, Any] | None = None, + **calc_kwargs, +) -> RunSchema: + """ + Function to carry out a structure relaxation with FHI-aims internal optimizer. + + Parameters + ---------- + atoms + The Atoms object. + species_defaults + The level of accuracy for the basis set and integration grids. + Options: "light", "intermediate", "tight", "really_tight". + Default is "light" which is suitable for geometry optimizations. + kspacing + The kpoint spacing in Å^-1. If not provided, defaults to + KSPACING_METAL (0.033) for metals and KSPACING_NON_METAL (0.045) for non-metals. + Ignored for aperiodic systems. + spin + Spin treatment. Options are "none", "collinear", or "non-collinear". + Default is None, which will automatically set to "collinear" if magnetic + moments are detected in the atoms object. + relax_cell + Whether to relax the cell or not. + copy_files + Files to copy (and decompress) from source to the runtime directory. + additional_fields + Additional fields to add to the results dictionary. + **calc_kwargs + Custom kwargs for the FHI-aims calculator. For a list of available + keys, refer to the [ase.calculators.aims.Aims][] calculator. + + Returns + ------- + RunSchema + Dictionary of results from [quacc.schemas.ase.Summarize.run][]. + See the type-hint for the data structure. + """ + calc_defaults = ( + BASE_SET_METAL.copy() if check_is_metal(atoms) else BASE_SET_NON_METAL.copy() + ) + calc_defaults["species_dir"] = species_defaults + + if kspacing is not None: + calc_defaults["kspacing"] = kspacing + else: + calc_defaults["kspacing"] = ( + KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL + ) + + if spin is not None: + calc_defaults["spin"] = spin + + calc_defaults["relax_geometry"] = "bfgs 1E-2" + + if relax_cell: + calc_defaults["relax_unit_cell"] = "full" + + return run_and_summarize( + atoms, + calc_defaults=calc_defaults, + calc_swaps=calc_kwargs, + additional_fields={"name": "FHI-aims Relax"} | (additional_fields or {}), + copy_files=copy_files, + ) + + +@job +def ase_relax_job( + atoms: Atoms, + species_defaults: Literal[ + "light", "intermediate", "tight", "really_tight" + ] = "light", + kspacing: float | None = None, + spin: Literal["none", "collinear", "non-collinear"] | None = None, + relax_cell: bool = False, + opt_params: OptParams | None = None, + copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, + additional_fields: dict[str, Any] | None = None, + **calc_kwargs, +) -> RunSchema: + """ + Function to carry out a structure relaxation with FHI-aims using ASE + external optimizers. + + Parameters + ---------- + atoms + The Atoms object. + species_defaults + The level of accuracy for the basis set and integration grids. + Options: "light", "intermediate", "tight", "really_tight". + Default is "light" which is suitable for geometry optimizations. + kspacing + The kpoint spacing in Å^-1. If not provided, defaults to + KSPACING_METAL (0.033) for metals and KSPACING_NON_METAL (0.045) for non-metals. + Ignored for aperiodic systems. + spin + Spin treatment. Options are "none", "collinear", or "non-collinear". + Default is None, which will automatically set to "collinear" if magnetic + moments are detected in the atoms object. + relax_cell + True if a volume relaxation should be performed. False if only the positions + should be updated. + opt_params + Dictionary of custom kwargs for the optimization process. For a list + of available keys, refer to [quacc.runners.ase.Runner.run_opt][]. + copy_files + Files to copy (and decompress) from source to the runtime directory. + additional_fields + Additional fields to add to the results dictionary. + **calc_kwargs + Additional keyword arguments to pass to the FHI-aims calculator. + + Returns + ------- + RunSchema + Dictionary of results from [quacc.schemas.ase.Summarize.run][]. + See the type-hint for the data structure. + """ + calc_defaults = ( + BASE_SET_METAL.copy() if check_is_metal(atoms) else BASE_SET_NON_METAL.copy() + ) + calc_defaults["species_dir"] = species_defaults + + if kspacing is not None: + calc_defaults["kspacing"] = kspacing + else: + calc_defaults["kspacing"] = ( + KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL + ) + + if spin is not None: + calc_defaults["spin"] = spin + + calc_defaults["compute_forces"] = True + + opt_defaults = {"optimizer": BFGS, "relax_cell": relax_cell} + + return run_and_summarize_opt( + atoms, + calc_defaults=calc_defaults, + calc_swaps=calc_kwargs, + opt_defaults=opt_defaults, + opt_params=opt_params, + additional_fields={"name": "FHI-aims ASE Relax"} | (additional_fields or {}), + copy_files=copy_files, + ) diff --git a/src/quacc/settings.py b/src/quacc/settings.py index 87a7350b3a..a7c798bb6d 100644 --- a/src/quacc/settings.py +++ b/src/quacc/settings.py @@ -167,6 +167,34 @@ class QuaccSettings(BaseSettings): """ ), ) + + # --------------------------- + # FHI-aims Settings + # --------------------------- + AIMS_BIN: Path = Field( + Path("aims.x"), description="Path to the FHI-aims executable." + ) + + AIMS_PARALLEL_CMD: str = Field( + "", + description=( + """ + Parallelization command to run FHI-aims. For example: 'mpirun -np 4'. + Note that this does not include the executable name. + """ + ), + ) + + AIMS_SPECIES_DEFAULTS: Path = Field( + Path("/path/to/fhi-aims/species_defaults/defaults_2020/"), + description=( + """ + Path to the species_defaults directory containing the FHI-aims basis sets. + This should point to a specific species defaults set (e.g., defaults_2020), + which contains subdirectories like 'light', 'intermediate', 'tight', etc. + """ + ), + ) # --------------------------- # ESPRESSO Settings diff --git a/src/quacc/utils/kpts.py b/src/quacc/utils/kpts.py index fa66595df1..1f90d3bb8a 100644 --- a/src/quacc/utils/kpts.py +++ b/src/quacc/utils/kpts.py @@ -101,3 +101,32 @@ def convert_pmg_kpts( gamma = max_pmg_kpts.style.name.lower() == "gamma" return kpts, gamma + + +def kspacing_to_grid(atoms, spacing): + """ + Calculate the kpoint mesh that is equivalent to the given spacing + in reciprocal space (units Angstrom^-1). + + Parameters + ---------- + atoms: ase.Atoms + A structure that can have get_reciprocal_cell called on it. + spacing: float + Minimum K-Point spacing in A^-1. + + Returns + ------- + kpoint_grid : [int, int, int] + K-point grid specification to give the required spacing. + """ + n_x, n_y, n_z = np.linalg.norm(atoms.cell.reciprocal(), axis=1) + kpoint_grid = [ + max(1, int(np.ceil(n_x / spacing))), + max(1, int(np.ceil(n_y / spacing))), + max(1, int(np.ceil(n_z / spacing))), + ] + for i, pbc in enumerate(atoms.pbc): + if not pbc: + kpoint_grid[i] = 1 + return kpoint_grid From a60744044f47ce1b5848e9c7c84eec0c7017eec4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:38:51 +0000 Subject: [PATCH 2/3] pre-commit auto-fixes --- src/quacc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quacc/settings.py b/src/quacc/settings.py index a7c798bb6d..1f151d2ab3 100644 --- a/src/quacc/settings.py +++ b/src/quacc/settings.py @@ -167,7 +167,7 @@ class QuaccSettings(BaseSettings): """ ), ) - + # --------------------------- # FHI-aims Settings # --------------------------- From 9d6f11d2b2e26fe949f1a9d675b94f8acfcf992e Mon Sep 17 00:00:00 2001 From: "B.Ayers" Date: Thu, 20 Mar 2025 18:56:08 +0000 Subject: [PATCH 3/3] Added Agnostic template allowing FHI-Aims to determine the optimal params for an unknown system --- src/quacc/recipes/aims/core.py | 86 ++++++++++++++++++++++------- src/quacc/recipes/espresso/_base.py | 2 +- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/quacc/recipes/aims/core.py b/src/quacc/recipes/aims/core.py index d73997b0b1..da49ec6dad 100644 --- a/src/quacc/recipes/aims/core.py +++ b/src/quacc/recipes/aims/core.py @@ -17,10 +17,10 @@ from quacc.types import Filenames, OptParams, RunSchema, SourceDirectory + BASE_SET_METAL = { "occupation_type": "cold 0.1", "relativistic": "atomic_zora scalar", - "sc_accuracy_rho": 1e-5, "charge_mix_param": 0.05, "mixer": "pulay", "n_max_pulay": 14, @@ -28,10 +28,16 @@ "output_level": "normal", } +BASE_SET_AGNOSTIC = { + "relativistic": "atomic_zora scalar", + "mixer": "pulay", + "xc": "pbe", + "output_level": "normal", +} + BASE_SET_NON_METAL = { "occupation_type": "gaussian 0.01", "relativistic": "atomic_zora scalar", - "sc_accuracy_rho": 1e-5, "xc": "pbe", "charge_mix_param": 0.20, "mixer": "pulay", @@ -39,6 +45,7 @@ } KSPACING_METAL = 0.033 +KSPACING_AGNOSTIC = 0.033 KSPACING_NON_METAL = 0.045 @@ -52,6 +59,7 @@ def static_job( spin: Literal["none", "collinear", "non-collinear"] | None = None, copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, additional_fields: dict[str, Any] | None = None, + agnostic_params: bool = False, **calc_kwargs, ) -> RunSchema: """ @@ -77,6 +85,10 @@ def static_job( Files to copy (and decompress) from source to the runtime directory. additional_fields Additional fields to add to the results dictionary. + agnostic_params + If True, uses minimal settings (no occupation_type or charge_mix_param) + letting FHI-aims determine these automatically. If False (default), + auto-detects whether the system is metallic or not. **calc_kwargs Custom kwargs for the FHI-aims calculator. For a list of available keys, refer to the [ase.calculators.aims.Aims][] calculator. @@ -87,17 +99,25 @@ def static_job( Dictionary of results, specified in [quacc.schemas.ase.Summarize.run][]. See the type-hint for the data structure. """ - calc_defaults = ( - BASE_SET_METAL.copy() if check_is_metal(atoms) else BASE_SET_NON_METAL.copy() - ) + if agnostic_params: + calc_defaults = BASE_SET_AGNOSTIC.copy() + default_kspacing = KSPACING_AGNOSTIC + else: + calc_defaults = ( + BASE_SET_METAL.copy() + if check_is_metal(atoms) + else BASE_SET_NON_METAL.copy() + ) + default_kspacing = ( + KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL + ) + calc_defaults["species_dir"] = species_defaults if kspacing is not None: calc_defaults["kspacing"] = kspacing else: - calc_defaults["kspacing"] = ( - KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL - ) + calc_defaults["kspacing"] = default_kspacing if spin is not None: calc_defaults["spin"] = spin @@ -122,6 +142,7 @@ def relax_job( relax_cell: bool = False, copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, additional_fields: dict[str, Any] | None = None, + agnostic_params: bool = False, **calc_kwargs, ) -> RunSchema: """ @@ -149,6 +170,10 @@ def relax_job( Files to copy (and decompress) from source to the runtime directory. additional_fields Additional fields to add to the results dictionary. + agnostic_params + If True, uses minimal settings (no occupation_type or charge_mix_param) + letting FHI-aims determine these automatically. If False (default), + auto-detects whether the system is metallic or not. **calc_kwargs Custom kwargs for the FHI-aims calculator. For a list of available keys, refer to the [ase.calculators.aims.Aims][] calculator. @@ -159,17 +184,25 @@ def relax_job( Dictionary of results from [quacc.schemas.ase.Summarize.run][]. See the type-hint for the data structure. """ - calc_defaults = ( - BASE_SET_METAL.copy() if check_is_metal(atoms) else BASE_SET_NON_METAL.copy() - ) + if agnostic_params: + calc_defaults = BASE_SET_AGNOSTIC.copy() + default_kspacing = KSPACING_AGNOSTIC + else: + calc_defaults = ( + BASE_SET_METAL.copy() + if check_is_metal(atoms) + else BASE_SET_NON_METAL.copy() + ) + default_kspacing = ( + KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL + ) + calc_defaults["species_dir"] = species_defaults if kspacing is not None: calc_defaults["kspacing"] = kspacing else: - calc_defaults["kspacing"] = ( - KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL - ) + calc_defaults["kspacing"] = default_kspacing if spin is not None: calc_defaults["spin"] = spin @@ -200,6 +233,7 @@ def ase_relax_job( opt_params: OptParams | None = None, copy_files: SourceDirectory | dict[SourceDirectory, Filenames] | None = None, additional_fields: dict[str, Any] | None = None, + agnostic_params: bool = False, **calc_kwargs, ) -> RunSchema: """ @@ -232,6 +266,10 @@ def ase_relax_job( Files to copy (and decompress) from source to the runtime directory. additional_fields Additional fields to add to the results dictionary. + agnostic_params + If True, uses minimal settings (no occupation_type or charge_mix_param) + letting FHI-aims determine these automatically. If False (default), + auto-detects whether the system is metallic or not. **calc_kwargs Additional keyword arguments to pass to the FHI-aims calculator. @@ -241,17 +279,25 @@ def ase_relax_job( Dictionary of results from [quacc.schemas.ase.Summarize.run][]. See the type-hint for the data structure. """ - calc_defaults = ( - BASE_SET_METAL.copy() if check_is_metal(atoms) else BASE_SET_NON_METAL.copy() - ) + if agnostic_params: + calc_defaults = BASE_SET_AGNOSTIC.copy() + default_kspacing = KSPACING_AGNOSTIC + else: + calc_defaults = ( + BASE_SET_METAL.copy() + if check_is_metal(atoms) + else BASE_SET_NON_METAL.copy() + ) + default_kspacing = ( + KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL + ) + calc_defaults["species_dir"] = species_defaults if kspacing is not None: calc_defaults["kspacing"] = kspacing else: - calc_defaults["kspacing"] = ( - KSPACING_METAL if check_is_metal(atoms) else KSPACING_NON_METAL - ) + calc_defaults["kspacing"] = default_kspacing if spin is not None: calc_defaults["spin"] = spin diff --git a/src/quacc/recipes/espresso/_base.py b/src/quacc/recipes/espresso/_base.py index 665a504832..35e18bb58b 100644 --- a/src/quacc/recipes/espresso/_base.py +++ b/src/quacc/recipes/espresso/_base.py @@ -263,6 +263,6 @@ def prepare_copy( if isinstance(copy_files, list): exact_files_to_copy = prepare_copy_files(calc_params, binary=binary) - return {source: exact_files_to_copy for source in copy_files} + return dict.fromkeys(copy_files, exact_files_to_copy) return copy_files