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

Update POTCAR summary stats to include 6.4 POTCARs and add dev_script utils for future updates #3370

Merged
merged 17 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
48 changes: 44 additions & 4 deletions pymatgen/io/vasp/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,7 @@ class PotcarSingle:
PBE="POT_GGA_PAW_PBE",
PBE_52="POT_GGA_PAW_PBE_52",
PBE_54="POT_GGA_PAW_PBE_54",
PBE_64="POT_PAW_PBE_64",
LDA="POT_LDA_PAW",
LDA_52="POT_LDA_PAW_52",
LDA_54="POT_LDA_PAW_54",
Expand Down Expand Up @@ -2150,7 +2151,7 @@ def is_valid(self) -> bool:
tol is then used to match statistical values within a tolerance
"""
functional_lexch = {
"PE": ["PBE", "PBE_52", "PBE_54"],
"PE": ["PBE", "PBE_52", "PBE_54", "PBE_64"],
"CA": ["LDA", "LDA_52", "LDA_54", "LDA_US", "Perdew_Zunger81"],
"91": ["PW91", "PW91_US"],
}
Expand Down Expand Up @@ -2231,7 +2232,9 @@ def data_stats(data_list: Sequence) -> dict:
"MAX": arr.max(),
}

summary_stats = { # for this PotcarSingle instance
# NB: to add future summary stats in a way that's consistent with PMG,
# it's easiest to save the summary stats as an attr of PotcarSingle
self._summary_stats = { # for this PotcarSingle instance
"keywords": {
"header": [kwd.lower() for kwd in self.keywords],
"data": psp_keys,
Expand All @@ -2245,12 +2248,12 @@ def data_stats(data_list: Sequence) -> dict:
data_match_tol = 1e-6
for ref_psp in possible_potcar_matches:
key_match = all(
set(ref_psp["keywords"][key]) == set(summary_stats["keywords"][key]) # type: ignore
set(ref_psp["keywords"][key]) == set(self._summary_stats["keywords"][key]) # type: ignore
for key in ["header", "data"]
)

data_diff = [
abs(ref_psp["stats"][key][stat] - summary_stats["stats"][key][stat]) # type: ignore
abs(ref_psp["stats"][key][stat] - self._summary_stats["stats"][key][stat]) # type: ignore
for stat in ["MEAN", "ABSMEAN", "VAR", "MIN", "MAX"]
for key in ["header", "data"]
]
Expand Down Expand Up @@ -2280,6 +2283,43 @@ def __repr__(self) -> str:
return f"{cls_name}({symbol=}, {functional=}, {TITEL=}, {VRHFIN=}, {n_valence_elec=:.0f})"


def _gen_potcar_summary_stats():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine here, but noting there is also a top-level dev_scripts folder (largely undocumented). On balance it's probably better here for discoverability for anyone curious how this file was generated.

Minor comments: the bare return is unnecessary, and monty.serialization.dumpfn can be used directly with dumpfn(new_summary_stats, "filename.json.gz") which is a convenience function we use a lot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Much better way of doing this. For placement, you and @janosh probably have a better sense of where this belongs. _gen_potcar_summary_stats is a "destructive" function, in that it overwrites the file that's distributed with PMG. dev_scripts might then be a better place for it

"""
This function solely intended to be used for PMG development to regenerate the
potcar_summary_stats.json.gz file used to validate POTCARs
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! This is great to have. Even though it's marked as private, we should add a test for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a Test_gen_potcar_summary_stats to tests/io/vasp/test_inputs.py. Requires generating a library of fake POTCARs, which are in tests/fake_POTCAR_library. Code to generate this library from a real library of POTCARs is in dev_scripts/potcar_scrambler.py

from monty.shutil import compress_file

func_dir_exist = {}
PMG_VASP_PSP_DIR = SETTINGS.get("PMG_VASP_PSP_DIR")
for func in PotcarSingle.functional_dir:
cpsp_dir = f"{PMG_VASP_PSP_DIR}/{PotcarSingle.functional_dir[func]}"
if os.path.isdir(cpsp_dir):
func_dir_exist[func] = PotcarSingle.functional_dir[func]
else:
print(f"WARNING: missing {PotcarSingle.functional_dir[func]} POTCAR directory")

new_summary_stats = {func: {} for func in func_dir_exist}
for func in func_dir_exist:
potcar_list = glob(f"{PMG_VASP_PSP_DIR}/{func_dir_exist[func]}/POTCAR*") + glob(
f"{PMG_VASP_PSP_DIR}/{func_dir_exist[func]}/*/POTCAR"
)
for potcar in potcar_list:
psp = PotcarSingle.from_file(potcar)
new_summary_stats[func][psp.TITEL.replace(" ", "")] = {
"LEXCH": psp.LEXCH,
"VRHFIN": psp.VRHFIN.replace(" ", ""),
**psp._summary_stats,
}

with open(f"{module_dir}/potcar_summary_stats.json", "w+") as _fl:
json.dump(new_summary_stats, _fl)

compress_file(f"{module_dir}/potcar_summary_stats.json")

return


class Potcar(list, MSONable):
"""
Object for reading and writing POTCAR files for calculations. Consists of a
Expand Down
Binary file modified pymatgen/io/vasp/potcar_summary_stats.json.gz
Binary file not shown.