Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2d94e73
-Add the reference for latest rVV10 parameters used for r2SCAN+rVV10
junchichen21 Sep 28, 2025
437c9bb
Merge branch 'master' into hotfix/pmg-io-vasp-MPScanRelaxSet
shyuep Oct 7, 2025
c5841e4
Add function to update INCAR settings in MPScanRelaxSet and MP24Relax…
junchichen21 Oct 8, 2025
83fcf44
Refactor MPScanRelaxSet to include xc_functional and dispersion param…
junchichen21 Oct 8, 2025
eb71a23
Update MPScanRelaxSet to clarify xc_functional and dispersion paramet…
junchichen21 Oct 8, 2025
d41b501
Refactor MP24RelaxSet to streamline XC functional and dispersion hand…
junchichen21 Oct 8, 2025
251f2b9
Update MPScanRelaxSet docstring and enforce rVV10 (or no) dispersion …
junchichen21 Oct 8, 2025
3842833
Update warning for unsupported dispersion corrections in _config_upda…
junchichen21 Oct 8, 2025
0660d76
Add validation for xc_functional in MP24RelaxSet to enforce allowed X…
junchichen21 Oct 8, 2025
d23d9ea
Add tests for disallowed xc_functional in MP24Sets to ensure proper v…
junchichen21 Oct 8, 2025
dabe7fe
Update uv.lock to add version markers for dependencies based on Pytho…
junchichen21 Oct 8, 2025
035eab5
Refactor MPScanRelaxSet tests to use xc_functional parameter and impr…
junchichen21 Oct 8, 2025
61a9f04
Merge branch 'master' into hotfix/pmg-io-vasp-MPScanRelaxSet
junchichen21 Oct 8, 2025
8a9fb02
Merge branch 'master' into hotfix/pmg-io-vasp-MPScanRelaxSet
junchichen21 Oct 10, 2025
78f85ea
Merge branch 'master' into hotfix/pmg-io-vasp-MPScanRelaxSet
shyuep Oct 16, 2025
171fd61
Merge branch 'master' into hotfix/pmg-io-vasp-MPScanRelaxSet
junchichen21 Oct 16, 2025
6b6a3d9
Merge branch 'master' into hotfix/pmg-io-vasp-MPScanRelaxSet
junchichen21 Oct 20, 2025
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
158 changes: 93 additions & 65 deletions src/pymatgen/io/vasp/sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,77 @@ class MPRelaxSet(VaspInputSet):
CONFIG = _load_yaml_config("MPRelaxSet")


def _config_updates(
vasp_input_set: VaspInputSet,
xc_functional: str,
dispersion: str | None = None,
) -> None:
"""Update the INCAR settings for a given XC functional and dispersion correction.

Args:
vasp_input_set (VaspInputSet): The VaspInputSet instance to update.
Supported types are MPScanRelaxSet and MP24RelaxSet.
xc_functional (str): The exchange-correlation functional to use.
Supported values are "SCAN", "r2SCAN", "PBE", and "PBEsol".
dispersion (str | None): The dispersion correction to use.
Supported values are "rVV10" and "D4". If None, no dispersion correction is applied.

Returns:
None: The function updates the INCAR settings of the VaspInputSet in place.
"""

xc = xc_functional.lower()
vdw = dispersion.lower() if dispersion is not None else None

to_func_metagga = {"scan": "SCAN", "r2scan": "R2SCAN"}
to_func_gga = {"pbe": "PE", "pbesol": "PS"}

config_updates: dict[str, Any] = {}

# Update the XC functional flags
if xc in to_func_metagga:
config_updates |= {"METAGGA": to_func_metagga[xc]}
vasp_input_set._config_dict["INCAR"].pop("GGA", None)
elif xc in to_func_gga:
config_updates |= {"GGA": to_func_gga[xc]}
vasp_input_set._config_dict["INCAR"].pop("METAGGA", None)
else:
raise ValueError(f"Unknown XC functional {xc_functional}!")

# Update the van der Waals parameters
if vdw == "rvv10":
rvv10_params_by_xc = {
"scan": {"BPARAM": 15.7, "CPARAM": 0.0093, "LUSE_VDW": True},
"r2scan": {"BPARAM": 11.95, "CPARAM": 0.0093, "LUSE_VDW": True},
}
try:
config_updates |= rvv10_params_by_xc[xc]
except KeyError:
raise ValueError(
"Use of rVV10 with functionals other than r2SCAN / SCAN is not currently supported in VASP."
)

elif vdw == "d4":
d4_pars = {
"r2scan": {"S6": 1.0, "S8": 0.60187490, "A1": 0.51559235, "A2": 5.77342911},
"pbe": {"S6": 1.0, "S8": 0.95948085, "A1": 0.38574991, "A2": 4.80688534},
"pbesol": {"S6": 1.0, "S8": 1.71885698, "A1": 0.47901421, "A2": 5.96771589},
}
if xc not in d4_pars:
raise ValueError(f"D4 parameters for XC functional '{xc_functional}' are not defined yet.")
pars = d4_pars[xc]
config_updates |= {"IVDW": 13, **{f"VDW_{k}": v for k, v in pars.items()}}

elif isinstance(vdw, str):
warnings.warn(
f"Dispersion correction '{dispersion}' with {xc} is not supported in this class and will be ignored.",
stacklevel=2,
)

if len(config_updates) > 0:
vasp_input_set._config_dict["INCAR"].update(config_updates)


@due.dcite(
Doi("10.1021/acs.jpclett.0c02405"),
description="Accurate and Numerically Efficient r2SCAN Meta-Generalized Gradient Approximation",
Expand All @@ -1315,13 +1386,13 @@ class MPRelaxSet(VaspInputSet):
class MPScanRelaxSet(VaspInputSet):
"""Write a relaxation input set using the accurate and numerically
efficient r2SCAN variant of the Strongly Constrained and Appropriately Normed
(SCAN) metaGGA density functional.
(SCAN) meta-GGA density functional.

Notes:
1. This functional is officially supported in VASP 6.0.0 and above. On older version,
source code may be obtained by contacting the authors of the referenced manuscript.
The original SCAN functional, available from VASP 5.4.3 onwards, maybe used instead
by passing `user_incar_settings={"METAGGA": "SCAN"}` when instantiating this InputSet.
by passing `xc_functional="SCAN"` when instantiating this InputSet.
r2SCAN and SCAN are expected to yield very similar results.

2. Meta-GGA calculations require POTCAR files that include
Expand All @@ -1346,10 +1417,12 @@ class MPScanRelaxSet(VaspInputSet):
and Mueller [1] (see References). Note that if 'user_incar_settings'
or 'user_kpoints_settings' override KSPACING, the calculation from
bandgap is not performed.
vdw (str): set "rVV10" to enable SCAN+rVV10, which is a versatile
van der Waals density functional by combing the SCAN functional
with the rVV10 non-local correlation functional. rvv10 is the only
dispersion correction available for SCAN at this time.
xc_functional (str): set "r2SCAN" (default) or "SCAN" to choose the meta-GGA
exchange-correlation functional.
dispersion (str | None): set "rVV10" (default None) to enable r2SCAN+rVV10 or
SCAN+rVV10, which is a versatile van der Waals density functional by combing
the r2SCAN/SCAN functional with the rVV10 non-local correlation functional.
rVV10 is the only dispersion correction available for r2SCAN and SCAN in MPScanRelaxSet class.
**kwargs: Keywords supported by VaspInputSet.

References:
Expand All @@ -1360,8 +1433,16 @@ class MPScanRelaxSet(VaspInputSet):
James W. Furness, Aaron D. Kaplan, Jinliang Ning, John P. Perdew, and Jianwei Sun.
Accurate and Numerically Efficient r2SCAN Meta-Generalized Gradient Approximation.
The Journal of Physical Chemistry Letters 11, 8208-8215 (2022) DOI: 10.1021/acs.jpclett.0c02405

Jinliang Ning, Manish Kothakonda, James W. Furness, Aaron D. Kaplan, Sebastian Ehlert,
Jan Gerit Brandenburg, John P. Perdew, and Jianwei Sun.
Workhorse minimally empirical dispersion-corrected density functional with tests for
weakly bound systems: r2SCAN+rVV10.
Phys. Rev. B 106, 075422 (2022) DOI: 10.1103/PhysRevB.106.075422
"""

xc_functional: Literal["r2SCAN", "SCAN"] = "r2SCAN"
dispersion: Literal["rVV10"] | None = None
bandgap: float | None = None
auto_kspacing: bool = True
user_potcar_functional: UserPotcarFunctional = "PBE_54"
Expand All @@ -1371,15 +1452,9 @@ class MPScanRelaxSet(VaspInputSet):

def __post_init__(self) -> None:
super().__post_init__()
if self.vdw and self.vdw != "rvv10":
warnings.warn(
"Use of van der waals functionals other than rVV10 with SCAN is not supported at this time. ",
stacklevel=2,
)
# Delete any vdw parameters that may have been added to the INCAR
vdw_par = loadfn(f"{MODULE_DIR}/vdW_parameters.yaml")
for k in vdw_par[self.vdw]:
self._config_dict["INCAR"].pop(k, None)
if isinstance(self.dispersion, str) and self.dispersion.lower() != "rvv10":
raise ValueError("Only rVV10 (or None) is supported for dispersion in MPScanRelaxSet.")
_config_updates(self, self.xc_functional, self.dispersion)


@dataclass
Expand Down Expand Up @@ -1431,56 +1506,9 @@ class MP24RelaxSet(VaspInputSet):

def __post_init__(self) -> None:
super().__post_init__()

to_func = {
"r2SCAN": "R2SCAN",
"PBE": "PE",
"PBEsol": "PS",
}

config_updates: dict[str, Any] = {}
if self.xc_functional == "r2SCAN":
config_updates |= {"METAGGA": to_func[self.xc_functional]}
self._config_dict["INCAR"].pop("GGA", None)
elif self.xc_functional in ["PBE", "PBEsol"]:
config_updates |= {"GGA": to_func[self.xc_functional]}
self._config_dict["INCAR"].pop("METAGGA", None)
else:
raise ValueError(f"Unknown XC functional {self.xc_functional}!")

if self.dispersion == "rVV10":
if self.xc_functional == "r2SCAN":
config_updates |= {"BPARAM": 11.95, "CPARAM": 0.0093, "LUSE_VDW": True}
else:
raise ValueError(
"Use of rVV10 with functionals other than r2 / SCAN is not currently supported in VASP."
)

elif self.dispersion == "D4":
d4_pars = {
"r2SCAN": {
"S6": 1.0,
"S8": 0.60187490,
"A1": 0.51559235,
"A2": 5.77342911,
},
"PBE": {
"S6": 1.0,
"S8": 0.95948085,
"A1": 0.38574991,
"A2": 4.80688534,
},
"PBEsol": {
"S6": 1.0,
"S8": 1.71885698,
"A1": 0.47901421,
"A2": 5.96771589,
},
}
config_updates |= {"IVDW": 13, **{f"VDW_{k}": v for k, v in d4_pars[self.xc_functional].items()}}

if len(config_updates) > 0:
self._config_dict["INCAR"].update(config_updates)
if self.xc_functional.lower() not in {"r2scan", "pbe", "pbesol"}:
raise ValueError("xc_functional must be one of 'r2SCAN', 'PBE', or 'PBEsol'.")
_config_updates(self, self.xc_functional, self.dispersion)

@staticmethod
def _sigmoid_interp(
Expand Down
35 changes: 27 additions & 8 deletions tests/io/vasp/test_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1830,7 +1830,7 @@ def test_scan_substitute(self):
mp_scan_sub = MPScanRelaxSet(
self.struct,
user_potcar_functional="PBE_52",
user_incar_settings={"METAGGA": "SCAN"},
xc_functional="SCAN",
)
incar = mp_scan_sub.incar
assert incar["METAGGA"] == "Scan"
Expand Down Expand Up @@ -1873,18 +1873,31 @@ def test_incar_overrides(self):
assert incar["SIGMA"] == approx(0.05)

# Test SCAN+rVV10
def test_rvv10(self):
scan_rvv10_set = MPScanRelaxSet(self.struct, vdw="rVV10")
def test_scan_rvv10(self):
scan_rvv10_set = MPScanRelaxSet(self.struct, xc_functional="SCAN", dispersion="rVV10")
assert scan_rvv10_set.xc_functional == "SCAN"
assert scan_rvv10_set.dispersion == "rVV10"
assert "LUSE_VDW" in scan_rvv10_set.incar
assert scan_rvv10_set.incar["BPARAM"] == approx(15.7)
assert scan_rvv10_set.incar["CPARAM"] == approx(0.0093)

# Test r2SCAN+rVV10
def test_r2scan_rvv10(self):
r2scan_rvv10_set = MPScanRelaxSet(self.struct, dispersion="rVV10")
assert r2scan_rvv10_set.xc_functional == "r2SCAN"
assert r2scan_rvv10_set.dispersion == "rVV10"
assert "LUSE_VDW" in r2scan_rvv10_set.incar
assert r2scan_rvv10_set.incar["BPARAM"] == approx(11.95)
assert r2scan_rvv10_set.incar["CPARAM"] == approx(0.0093)

def test_other_vdw(self):
# should raise a warning.
# should raise a error.
# IVDW key should not be present in the incar
with pytest.warns(UserWarning, match=r"not supported at this time"):
scan_vdw_set = MPScanRelaxSet(self.struct, vdw="DFTD3")
assert "LUSE_VDW" not in scan_vdw_set.incar
assert "IVDW" not in scan_vdw_set.incar
with pytest.raises(
ValueError,
match=r"Only rVV10 \(or None\) is supported",
):
MPScanRelaxSet(self.struct, dispersion="DFTD3")

@skip_if_no_psp_dir
def test_potcar(self):
Expand Down Expand Up @@ -2396,3 +2409,9 @@ def test_non_default_xc_func(self):
"VDW_S8",
)
)

def test_not_allowed_xc_func(self):
with pytest.raises(ValueError, match="xc_functional must be one of"):
self.relax_set(structure=self.structure, xc_functional="SCAN")
with pytest.raises(ValueError, match="xc_functional must be one of"):
self.static_set(structure=self.structure, xc_functional="SCAN", dispersion="rVV10")
14 changes: 7 additions & 7 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.