Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split VASP input sets into submodules #3865

Merged
merged 8 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -172,6 +172,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