Skip to content

Commit

Permalink
Reapply "Split VASP input sets into submodules (#3865)" (#3989)
Browse files Browse the repository at this point in the history
This reverts commit f1eb364.
  • Loading branch information
Matthew Horton committed Aug 12, 2024
1 parent f035def commit 1c9684e
Show file tree
Hide file tree
Showing 10 changed files with 3,294 additions and 1 deletion.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ repos:
rev: v1.11.1
hooks:
- id: mypy
entry: env MYPYPATH=src mypy

- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest
[tool.ruff]
target-version = "py39"
line-length = 120
output-format = "concise"

[tool.ruff.lint]
select = ["ALL"]
Expand Down
44 changes: 44 additions & 0 deletions src/pymatgen/io/vasp/sets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Re-export all VASP input sets, for more convenient imports and to maintain backwards compatible imports
following the split up of sets.py into submodules in #3865.
"""

from __future__ import annotations

from pymatgen.io.vasp.sets.base import (
MODULE_DIR,
BadInputSetWarning,
DictSet,
UserPotcarFunctional,
VaspInputGenerator,
VaspInputSet,
_load_yaml_config,
batch_write_input,
get_structure_from_prev_run,
get_valid_magmom_struct,
)
from pymatgen.io.vasp.sets.lobster import LobsterSet
from pymatgen.io.vasp.sets.matpes import MatPESStaticSet
from pymatgen.io.vasp.sets.mit import MITMDSet, MITNEBSet, MITRelaxSet
from pymatgen.io.vasp.sets.mp import (
MPAbsorptionSet,
MPHSEBSSet,
MPHSERelaxSet,
MPMDSet,
MPMetalRelaxSet,
MPNMRSet,
MPNonSCFSet,
MPRelaxSet,
MPScanRelaxSet,
MPScanStaticSet,
MPSOCSet,
MPStaticSet,
)
from pymatgen.io.vasp.sets.mvl import (
MVLElasticSet,
MVLGBSet,
MVLGWSet,
MVLNPTMDSet,
MVLRelax52Set,
MVLScanRelaxSet,
MVLSlabSet,
)
1,538 changes: 1,538 additions & 0 deletions src/pymatgen/io/vasp/sets/base.py

Large diffs are not rendered by default.

107 changes: 107 additions & 0 deletions src/pymatgen/io/vasp/sets/lobster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# ruff: noqa: PGH003

from __future__ import annotations

import warnings
from dataclasses import dataclass
from typing import TYPE_CHECKING

from pymatgen.io.vasp.sets.base import UserPotcarFunctional, VaspInputSet
from pymatgen.io.vasp.sets.mp import MPRelaxSet

if TYPE_CHECKING:
from collections.abc import Sequence

from pymatgen.io.vasp.inputs import Kpoints


@dataclass
class LobsterSet(VaspInputSet):
"""Input set to prepare VASP runs that can be digested by Lobster (See cohp.de).
Args:
structure (Structure): input structure.
isym (int): ISYM entry for INCAR, only isym=-1 and isym=0 are allowed
ismear (int): ISMEAR entry for INCAR, only ismear=-5 and ismear=0 are allowed
reciprocal_density (int): density of k-mesh by reciprocal volume
user_supplied_basis (dict): dict including basis functions for all elements in
structure, e.g. {"Fe": "3d 3p 4s", "O": "2s 2p"}; if not supplied, a
standard basis is used
address_basis_file (str): address to a file similar to
"BASIS_PBE_54_standard.yaml" in pymatgen.io.lobster.lobster_basis
user_potcar_settings (dict): dict including potcar settings for all elements in
structure, e.g. {"Fe": "Fe_pv", "O": "O"}; if not supplied, a standard basis
is used.
**kwargs: Other kwargs supported by VaspInputSet.
"""

isym: int = 0
ismear: int = -5
reciprocal_density: int | None = None
address_basis_file: str | None = None
user_supplied_basis: dict | None = None

# newest potcars are preferred
# Choose PBE_54 unless the user specifies a different potcar_functional
user_potcar_functional: UserPotcarFunctional = "PBE_54"

CONFIG = MPRelaxSet.CONFIG
_valid_potcars: Sequence[str] | None = ("PBE_52", "PBE_54")

def __post_init__(self):
super().__post_init__()
warnings.warn("Make sure that all parameters are okay! This is a brand new implementation.")

if self.isym not in (-1, 0):
raise ValueError("Lobster cannot digest WAVEFUNCTIONS with symmetry. isym must be -1 or 0")
if self.ismear not in (-5, 0):
raise ValueError("Lobster usually works with ismear=-5 or ismear=0")

self._config_dict["POTCAR"]["W"] = "W_sv"

@property
def kpoints_updates(self) -> dict | Kpoints:
"""Updates to the kpoints configuration for this calculation type."""
# test, if this is okay
return {"reciprocal_density": self.reciprocal_density or 310}

@property
def incar_updates(self) -> dict:
"""Updates to the INCAR config for this calculation type."""
from pymatgen.io.lobster import Lobsterin

potcar_symbols = self.potcar_symbols

# predefined basis! Check if the basis is okay! (charge spilling and bandoverlaps!)
if self.user_supplied_basis is None and self.address_basis_file is None:
basis = Lobsterin.get_basis(structure=self.structure, potcar_symbols=potcar_symbols) # type: ignore
elif self.address_basis_file is not None:
basis = Lobsterin.get_basis(
structure=self.structure, # type: ignore
potcar_symbols=potcar_symbols,
address_basis_file=self.address_basis_file,
)
elif self.user_supplied_basis is not None:
# test if all elements from structure are in user_supplied_basis
for atom_type in self.structure.symbol_set: # type: ignore
if atom_type not in self.user_supplied_basis:
raise ValueError(f"There are no basis functions for the atom type {atom_type}")
basis = [f"{key} {value}" for key, value in self.user_supplied_basis.items()]
else:
basis = None

lobsterin = Lobsterin(settingsdict={"basisfunctions": basis})
nbands = lobsterin._get_nbands(structure=self.structure) # type: ignore

return {
"EDIFF": 1e-6,
"NSW": 0,
"LWAVE": True,
"ISYM": self.isym,
"NBANDS": nbands,
"IBRION": -1,
"ISMEAR": self.ismear,
"LORBIT": 11,
"ICHARG": 0,
"ALGO": "Normal",
}
84 changes: 84 additions & 0 deletions src/pymatgen/io/vasp/sets/matpes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import annotations

import warnings
from dataclasses import dataclass
from typing import TYPE_CHECKING

from pymatgen.io.vasp.sets.base import VaspInputSet, _load_yaml_config

if TYPE_CHECKING:
from typing import Literal


@dataclass
class MatPESStaticSet(VaspInputSet):
"""Create input files for a MatPES static calculation.
The goal of MatPES is to generate potential energy surface data. This is a distinctly different
from the objectives of the MP static calculations, which aims to obtain primarily accurate
energies and also electronic structure (DOS). For PES data, force accuracy (and to some extent,
stress accuracy) is of paramount importance.
The default POTCAR versions have been updated to PBE_54 from the old PBE set used in the
MPStaticSet. However, **U values** are still based on PBE. The implicit assumption here is that
the PBE_54 and PBE POTCARs are sufficiently similar that the U values fitted to the old PBE
functional still applies.
Args:
structure (Structure): The Structure to create inputs for. If None, the input
set is initialized without a Structure but one must be set separately before
the inputs are generated.
xc_functional ('R2SCAN'|'PBE'): Exchange-correlation functional to use. Defaults to 'PBE'.
**kwargs: Keywords supported by VaspInputSet.
"""

xc_functional: Literal["R2SCAN", "PBE", "PBE+U"] = "PBE"
prev_incar: dict | str | None = None
# These are parameters that we will inherit from any previous INCAR supplied. They are mostly parameters related
# to symmetry and convergence set by Custodian when errors are encountered in a previous run. Given that our goal
# is to have a strictly homogeneous PES data, all other parameters (e.g., ISMEAR, ALGO, etc.) are not inherited.
inherit_incar: list[str] | bool = ( # type: ignore # noqa: PGH003
"LPEAD",
"NGX",
"NGY",
"NGZ",
"SYMPREC",
"IMIX",
"LMAXMIX",
"KGAMMA",
"ISYM",
"NCORE",
"NPAR",
"NELMIN",
"IOPT",
"NBANDS",
"KPAR",
"AMIN",
"NELMDL",
"BMIX",
"AMIX_MAG",
"BMIX_MAG",
)
CONFIG = _load_yaml_config("MatPESStaticSet")

def __post_init__(self):
"""Validate inputs."""
super().__post_init__()
valid_xc_functionals = ("R2SCAN", "PBE", "PBE+U")
if self.xc_functional.upper() not in valid_xc_functionals:
raise ValueError(
f"Unrecognized xc_functional='{self.xc_functional}'. "
f"Supported exchange-correlation functionals are {valid_xc_functionals}"
)

default_potcars = self.CONFIG["PARENT"].replace("PBE", "PBE_").replace("Base", "") # PBE64Base -> PBE_64
self.user_potcar_functional = self.user_potcar_functional or default_potcars
if self.user_potcar_functional.upper() != default_potcars:
warnings.warn(
f"{self.user_potcar_functional=} is inconsistent with the recommended {default_potcars}.", UserWarning
)

if self.xc_functional.upper() == "R2SCAN":
self._config_dict["INCAR"].update({"METAGGA": "R2SCAN", "ALGO": "ALL", "GGA": None})
if self.xc_functional.upper().endswith("+U"):
self._config_dict["INCAR"]["LDAU"] = True
Loading

0 comments on commit 1c9684e

Please sign in to comment.