Skip to content

Commit

Permalink
Increase robustness of MP settings (#2123)
Browse files Browse the repository at this point in the history
## Summary of Changes

This PR overhauls how MP settings are implemented in quacc. Instead of
importing from pymatgen, we now import directly from atomate2. This
ensures that we are always operating with MP-compatible settings and is
much more robust. This PR has a few breaking changes:
- atomate2 is now a required dependency when using MP workflows
- The `Vasp` calculator no longer takes `pmg_input_set` as a keyword
argument
- The MP-compatible jobs now use all the same settings as MP (i.e.
LCHARG and LWAVE are as originally implemented) for the sake of
consistency

### 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] committed May 12, 2024
1 parent 34f851d commit 1424e3f
Show file tree
Hide file tree
Showing 27 changed files with 512 additions and 529 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Added

- Added a context handler, `quacc.settings.change_settings`, that can be used to temporarily modify global settings
- Added a context handler, `quacc.settings.change_settings`, that can be used to modify global settings temporarily
- Added `quacc.calculators.vasp.params.MPtoASEConverter` to convert between Pymatgen- and Atomate2-style input parameters to ASE-compatabile parameters

### Fixed

- Fixed copying of WAVECAR between steps of the QMOF recipes

### Changed

- Overhauled the MP recipes to ensure better compatability with atomate2 workflows
- The workflow engine must be directly specified with `WORKFLOW_ENGINE`, as noted in the docs
- Changed `VASP_MAG_CUTOFF` from 0.05 to 0.02
- Removed the `preset` keyword argument from the QMOF recipes

### Removed

- Removed the `pmg_input_set` keyword argument from the `Vasp` calculator

## [0.7.8]

### Added
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "quacc"
description="A platform to enable high-throughput, database-driven quantum chemistry and computational materials science"
version = "0.7.9"
version = "0.8.0"
readme = "README.md"
license = { text = "BSD-3" }
authors = [{ name = "Andrew S. Rosen", email = "asrosen@princeton.edu" }]
Expand Down Expand Up @@ -48,7 +48,7 @@ dask = ["dask[distributed]>=2023.12.1", "dask-jobqueue>=0.8.2"]
defects = ["pymatgen-analysis-defects>=2023.8.22", "shakenbreak>=3.2.0"]
jobflow = ["jobflow[fireworks]>=0.1.14", "jobflow-remote>=0.1.0"]
mlp = ["matgl>=1.0.0", "chgnet>=0.3.3", "mace-torch>=0.3.3", "torch-dftd>=0.4.0"]
mp = ["pymatgen-io-validation>=0.0.1"]
mp = ["atomate2>=0.0.14"]
newtonnet = ["newtonnet>=1.1"]
parsl = ["parsl[monitoring]>=2023.10.23; platform_system!='Windows'"]
phonons = ["phonopy>=2.20.0", "seekpath>=2.1.0"]
Expand Down
150 changes: 111 additions & 39 deletions src/quacc/calculators/vasp/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,30 @@
from __future__ import annotations

import logging
from importlib import util
from typing import TYPE_CHECKING

import numpy as np
from ase.calculators.vasp import Vasp as Vasp_
from monty.dev import requires
from pymatgen.io.ase import AseAtomsAdaptor

from quacc.atoms.core import check_is_metal
from quacc.utils.kpts import convert_pmg_kpts

has_atomate2 = util.find_spec("atomate2")
if TYPE_CHECKING:
from typing import Any, Literal

from ase.atoms import Atoms
from pymatgen.io.vasp.sets import DictSet

from quacc.utils.files import SourceDirectory
from quacc.utils.kpts import PmgKpts

if has_atomate2:
from atomate2.vasp.jobs.base import BaseVaspMaker

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -369,45 +376,110 @@ def set_pmg_kpts(
return user_calc_params


def get_pmg_input_set_params(dict_set: DictSet, atoms: Atoms) -> tuple[dict, Atoms]:
class MPtoASEConverter:
"""
Convert a Pymatgen VASP input set into an ASE-compatible set of
calculator parameters.
Parameters
----------
dict_set
The Pymatgen VASP input set.
atoms
The input atoms.
Returns
-------
dict
The ASE-compatible set of calculator parameters.
Atoms
The input atoms to match the pymatgen input set.
Convert an MP-formatted input set to an ASE-formatted input set.
"""
structure = AseAtomsAdaptor.get_structure(atoms)
pmg_input_set = dict_set(structure=structure, sort_structure=False)
incar_dict = {k.lower(): v for k, v in pmg_input_set.incar.items()}

potcar_symbols = pmg_input_set.potcar_symbols
potcar_setups = {symbol.split("_")[0]: symbol for symbol in potcar_symbols}
for k, v in potcar_setups.items():
if k in v:
potcar_setups[k] = v.split(k)[-1]

pp = pmg_input_set.potcar_functional.split("_")[0]

full_input_params = incar_dict | {"setups": potcar_setups, "pp": pp}

pmg_kpts = pmg_input_set.kpoints
if pmg_kpts is not None:
kpoints_dict = pmg_input_set.kpoints.as_dict()
full_input_params |= {
"kpts": kpoints_dict["kpoints"][0],
"gamma": kpoints_dict["generation_style"] == "Gamma",
}

return full_input_params, pmg_input_set.poscar.structure.to_ase_atoms()
def __init__(
self, atoms: Atoms | None = None, prev_dir: SourceDirectory | None = None
) -> None:
"""
Initialize the converter.
Parameters
----------
atoms
The ASE atoms object.
prev_dir
The previous directory.
Returns
-------
None
"""
self.atoms = atoms
self.prev_dir = prev_dir
self.structure = AseAtomsAdaptor.get_structure(atoms)
if atoms is None and prev_dir is None:
raise ValueError("Either atoms or prev_dir must be provided.")

def convert_dict_set(self, dict_set: DictSet) -> dict:
"""
Convert a Pymatgen DictSet to a dictionary of ASE VASP parameters.
Parameters
----------
dict_set
The pymatgen DictSet.
Returns
-------
dict
The ASE VASP parameters.
"""
input_set = dict_set(sort_structure=False)
vasp_input = input_set.get_input_set(
structure=self.structure, potcar_spec=True, prev_dir=self.prev_dir
)
self.incar_dict = vasp_input["INCAR"]
self.pmg_kpts = vasp_input.get("KPOINTS")
self.potcar_symbols = vasp_input["POTCAR"]
self.potcar_functional = input_set.potcar_functional
self.poscar = vasp_input["POSCAR"]
return self._convert()

@requires(has_atomate2, "atomate2 is not installed.")
def convert_vasp_maker(self, VaspMaker: BaseVaspMaker) -> dict:
"""
Convert an atomate2 VaspMaker to a dictionary of ASE VASP parameters.
Parameters
----------
VaspMaker
The atomate2 VaspMaker.
Returns
-------
dict
The ASE VASP parameters.
"""
input_set_generator = VaspMaker().input_set_generator
assert hasattr(input_set_generator, "sort_structure")
input_set_generator.sort_structure = False
input_set = input_set_generator.get_input_set(
structure=self.structure, potcar_spec=True, prev_dir=self.prev_dir
)
self.incar_dict = input_set.incar
self.pmg_kpts = input_set.kpoints
self.potcar_symbols = input_set.potcar
self.potcar_functional = input_set_generator.potcar_functional
self.poscar = input_set.poscar
return self._convert()

def _convert(self) -> dict:
"""
Convert the MP input to a dictionary of ASE VASP parameters.
Returns
-------
dict
The ASE VASP parameters.
"""
self.incar_dict = {k.lower(): v for k, v in self.incar_dict.items()}
pp = self.potcar_functional.split("_")[0]
potcar_setups = {symbol.split("_")[0]: symbol for symbol in self.potcar_symbols}
for k, v in potcar_setups.items():
if k in v:
potcar_setups[k] = v.split(k)[-1]

full_input_params = self.incar_dict | {"setups": potcar_setups, "pp": pp}

if self.pmg_kpts:
kpts_dict = self.pmg_kpts.as_dict()
full_input_params |= {
"kpts": kpts_dict["kpoints"][0],
"gamma": kpts_dict["generation_style"].lower() == "gamma",
}

return full_input_params
17 changes: 1 addition & 16 deletions src/quacc/calculators/vasp/vasp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from quacc.calculators.vasp.io import load_vasp_yaml_calc
from quacc.calculators.vasp.params import (
get_param_swaps,
get_pmg_input_set_params,
normalize_params,
remove_unused_flags,
set_auto_dipole,
Expand All @@ -29,7 +28,6 @@
from typing import Literal

from ase.atoms import Atoms
from pymatgen.io.vasp.sets import DictSet

_DEFAULT_SETTING = ()

Expand All @@ -56,7 +54,6 @@ def __init__(
| None
) = None,
auto_dipole: bool | None = None,
pmg_input_set: DictSet | None = None,
**kwargs,
) -> None:
"""
Expand Down Expand Up @@ -101,9 +98,6 @@ def __init__(
auto_dipole
If True, will automatically set dipole moment correction parameters
based on the center of mass (in the c dimension by default).
pmg_input_set
A Pymatgen input set to use for the VASP calculation, taken from a
`pymatgen.io.vasp.sets.DictSet` object.
**kwargs
Additional arguments to be passed to the VASP calculator, e.g.
`xc='PBE'`, `encut=520`. Takes all valid ASE calculator arguments.
Expand Down Expand Up @@ -150,7 +144,6 @@ def __init__(
self.elemental_magmoms = elemental_magmoms
self.pmg_kpts = pmg_kpts
self.auto_dipole = auto_dipole
self.pmg_input_set = pmg_input_set
self.kwargs = kwargs

# Initialize for later
Expand Down Expand Up @@ -218,14 +211,6 @@ def _cleanup_params(self) -> None:
msg = "Atoms object has a constraint that is not compatible with Custodian."
raise ValueError(msg)

# Get Pymatgen VASP input set parameters
if self.pmg_input_set:
pmg_calc_params, self.input_atoms = get_pmg_input_set_params(
self.pmg_input_set, self.input_atoms
)
else:
pmg_calc_params = {}

# Get user-defined preset parameters for the calculator
if self.preset:
calc_preset = load_vasp_yaml_calc(SETTINGS.VASP_PRESET_DIR / self.preset)[
Expand All @@ -236,7 +221,7 @@ def _cleanup_params(self) -> None:

# Collect all the calculator parameters and prioritize the kwargs in the
# case of duplicates.
self.user_calc_params = pmg_calc_params | calc_preset | self.kwargs
self.user_calc_params = calc_preset | self.kwargs

# Allow the user to use setups='mysetups.yaml' to load in a custom
# setups from a YAML file
Expand Down
Loading

0 comments on commit 1424e3f

Please sign in to comment.