Skip to content

Commit c55e056

Browse files
authored
Add new .pmgrc.yaml setting PMG_VASP_PSP_SUB_DIRS: dict[str, str] (#3858)
* add new PMG_VASP_PSP_SUB_DIRS SETTING used in from_symbol_and_functional to allow customizing currently hard-coded subfolder structure of PMG_VASP_PSP_DIR * fix TestMatPESStaticSet.test_default and TestLobsterSet.test_potcar * add TestPotcarSingle.test_from_symbol_and_functional_raises * uncomment and fix test_default_functional * del outdated test.yml comment thanks @DanielYang59 * try fix test_from_symbol_and_functional_raises on windows
1 parent eed8403 commit c55e056

File tree

4 files changed

+52
-31
lines changed

4 files changed

+52
-31
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ jobs:
2828
# maximize CI coverage of different platforms and python versions while minimizing the
2929
# total number of jobs. We run all pytest splits with the oldest supported python
3030
# version (currently 3.9) on windows (seems most likely to surface errors) and with
31-
# newest version (currently 3.12) on ubuntu (to get complete coverage on unix). We
32-
# ignore mac-os, which is assumed to be similar to ubuntu.
31+
# newest version (currently 3.12) on ubuntu (to get complete coverage on unix).
3332
config:
3433
- os: windows-latest
3534
python: "3.9"
@@ -39,11 +38,10 @@ jobs:
3938
python: "3.12"
4039
resolution: lowest-direct
4140
extras: ci,optional
42-
# test with lowest resolution and only required dependencies installed
4341
- os: macos-latest
4442
python: "3.10"
4543
resolution: lowest-direct
46-
extras: ci
44+
extras: ci # test with only required dependencies installed
4745

4846
# pytest-split automatically distributes work load so parallel jobs finish in similar time
4947
split: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

pymatgen/io/vasp/inputs.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,13 +2126,8 @@ def is_valid(self) -> bool:
21262126
if self.TITEL.replace(" ", "") == titel_no_spc:
21272127
for potcar_subvariant in self._potcar_summary_stats[func][titel_no_spc]:
21282128
if self.VRHFIN.replace(" ", "") == potcar_subvariant["VRHFIN"]:
2129-
possible_potcar_matches.append(
2130-
{
2131-
"POTCAR_FUNCTIONAL": func,
2132-
"TITEL": titel_no_spc,
2133-
**potcar_subvariant,
2134-
}
2135-
)
2129+
possible_match = {"POTCAR_FUNCTIONAL": func, "TITEL": titel_no_spc, **potcar_subvariant}
2130+
possible_potcar_matches.append(possible_match)
21362131

21372132
def parse_fortran_style_str(input_str: str) -> str | bool | float | int:
21382133
"""Parse any input string as bool, int, float, or failing that, str.
@@ -2284,26 +2279,29 @@ def from_symbol_and_functional(
22842279
if functional is None:
22852280
raise ValueError("Cannot get functional.")
22862281

2287-
funcdir = cls.functional_dir[functional]
2282+
functional_subdir = SETTINGS.get("PMG_VASP_PSP_SUB_DIRS", {}).get(functional, cls.functional_dir[functional])
22882283
PMG_VASP_PSP_DIR = SETTINGS.get("PMG_VASP_PSP_DIR")
22892284
if PMG_VASP_PSP_DIR is None:
22902285
raise ValueError(
22912286
f"No POTCAR for {symbol} with {functional=} found. Please set the PMG_VASP_PSP_DIR in .pmgrc.yaml."
22922287
)
2288+
if not os.path.isdir(PMG_VASP_PSP_DIR):
2289+
raise FileNotFoundError(f"{PMG_VASP_PSP_DIR=} does not exist.")
22932290

22942291
paths_to_try: list[str] = [
2295-
os.path.join(PMG_VASP_PSP_DIR, funcdir, f"POTCAR.{symbol}"),
2296-
os.path.join(PMG_VASP_PSP_DIR, funcdir, symbol, "POTCAR"),
2292+
os.path.join(PMG_VASP_PSP_DIR, functional_subdir, f"POTCAR.{symbol}"),
2293+
os.path.join(PMG_VASP_PSP_DIR, functional_subdir, symbol, "POTCAR"),
22972294
]
2295+
path = paths_to_try[0]
22982296
for path in paths_to_try:
22992297
path = os.path.expanduser(path)
23002298
path = zpath(path)
23012299
if os.path.isfile(path):
23022300
return cls.from_file(path)
23032301

2304-
raise RuntimeError(
2305-
f"You do not have the right POTCAR with {functional=} and {symbol=} "
2306-
f"in your {PMG_VASP_PSP_DIR=}. Paths tried: {paths_to_try}"
2302+
raise FileNotFoundError(
2303+
f"You do not have the right POTCAR with {functional=} and {symbol=}\n"
2304+
f"in your {PMG_VASP_PSP_DIR=}.\nPaths tried:\n- " + "\n- ".join(paths_to_try)
23072305
)
23082306

23092307
def verify_potcar(self) -> tuple[bool, bool]:

tests/io/vasp/test_inputs.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import re
88
from shutil import copyfile
99
from unittest import TestCase
10+
from unittest.mock import patch
1011

1112
import numpy as np
1213
import pytest
@@ -1182,20 +1183,43 @@ def test_multi_potcar_with_and_without_sha256(self):
11821183
else:
11831184
assert psingle.is_valid
11841185

1185-
# def test_default_functional(self):
1186-
# potcar = PotcarSingle.from_symbol_and_functional("Fe")
1187-
# assert potcar.functional_class == "GGA"
1188-
# SETTINGS["PMG_DEFAULT_FUNCTIONAL"] = "LDA"
1189-
# potcar = PotcarSingle.from_symbol_and_functional("Fe")
1190-
# assert potcar.functional_class == "LDA"
1191-
# SETTINGS["PMG_DEFAULT_FUNCTIONAL"] = "PBE"
1186+
def test_default_functional(self):
1187+
with patch.dict(SETTINGS, PMG_DEFAULT_FUNCTIONAL="PBE"):
1188+
potcar = PotcarSingle.from_symbol_and_functional("Fe")
1189+
assert potcar.functional_class == "GGA"
1190+
with patch.dict(SETTINGS, PMG_DEFAULT_FUNCTIONAL="LDA"):
1191+
SETTINGS["PMG_DEFAULT_FUNCTIONAL"] = "LDA"
1192+
potcar = PotcarSingle.from_symbol_and_functional("Fe")
1193+
assert potcar.functional_class == "LDA"
1194+
1195+
def test_from_symbol_and_functional_raises(self):
1196+
# test FileNotFoundError on non-existent PMG_VASP_PSP_DIR in SETTINGS
1197+
PMG_VASP_PSP_DIR = "missing-dir"
1198+
symbol, functional = "Fe", "PBE_64"
1199+
with (
1200+
patch.dict(SETTINGS, PMG_VASP_PSP_DIR=PMG_VASP_PSP_DIR),
1201+
pytest.raises(FileNotFoundError, match=f"{PMG_VASP_PSP_DIR=} does not exist."),
1202+
):
1203+
PotcarSingle.from_symbol_and_functional(symbol, functional)
1204+
1205+
# test different FileNotFoundError on non-existent POTCAR sub-directory
1206+
PMG_VASP_PSP_DIR = SETTINGS["PMG_VASP_PSP_DIR"]
1207+
err_msg = f"You do not have the right POTCAR with {functional=} and {symbol=}\nin your {PMG_VASP_PSP_DIR=}"
1208+
1209+
with (
1210+
patch.dict(SETTINGS, PMG_VASP_PSP_SUB_DIRS={"PBE_64": "PBE_64_FOO"}),
1211+
pytest.raises(FileNotFoundError) as exc_info,
1212+
):
1213+
PotcarSingle.from_symbol_and_functional(symbol, functional)
1214+
1215+
assert err_msg in str(exc_info.value)
11921216

11931217
def test_repr(self):
1194-
assert (
1195-
repr(self.psingle_Mn_pv)
1196-
== "PotcarSingle(symbol='Mn_pv', functional='PBE', TITEL='PAW_PBE Mn_pv 07Sep2000', "
1218+
expected_repr = (
1219+
"PotcarSingle(symbol='Mn_pv', functional='PBE', TITEL='PAW_PBE Mn_pv 07Sep2000', "
11971220
"VRHFIN='Mn: 3p4s3d', n_valence_elec=13)"
11981221
)
1222+
assert repr(self.psingle_Mn_pv) == expected_repr
11991223

12001224
def test_hash(self):
12011225
assert self.psingle_Mn_pv.md5_header_hash == "b45747d8ceeee91c3b27e8484db32f5a"

tests/io/vasp/test_sets.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -851,8 +851,8 @@ def test_default(self):
851851

852852
assert input_set.potcar_functional == "PBE_64" # test POTCARs default to PBE_64
853853
assert input_set.kpoints is None
854-
# only runs if POTCAR files to compare against are available
855-
if os.path.isdir(f"{FAKE_POTCAR_DIR}/POT_GGA_PAW_PBE_64"):
854+
if os.path.isdir(f"{FAKE_POTCAR_DIR}/POT_PAW_PBE_64"):
855+
# this part only runs if POTCAR files are available
856856
assert str(input_set.potcar[0]) == str(PotcarSingle.from_symbol_and_functional("Fe_pv", "PBE_64"))
857857

858858
def test_with_prev_incar(self):
@@ -1980,12 +1980,13 @@ def test_kpoints(self):
19801980
@skip_if_no_psp_dir
19811981
def test_potcar(self):
19821982
# PBE_54 is preferred at the moment
1983-
assert self.lobsterset1.user_potcar_functional == "PBE_54"
1983+
functional, symbol = "PBE_54", "K_sv"
1984+
assert self.lobsterset1.user_potcar_functional == functional
19841985
# test if potcars selected are consistent with PBE_54
19851986
assert self.lobsterset2.potcar.symbols == ["Fe_pv", "P", "O"]
19861987
# test if error raised contains correct potcar symbol for K element as PBE_54 set
19871988
with pytest.raises(
1988-
RuntimeError, match="You do not have the right POTCAR with functional='PBE_54' and symbol='K_sv'"
1989+
FileNotFoundError, match=f"You do not have the right POTCAR with {functional=} and {symbol=}"
19891990
):
19901991
_ = self.lobsterset9.potcar.symbols
19911992

0 commit comments

Comments
 (0)