1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
#!/usr/bin/env python
"""
This script regenerates the enum values in pymatgen.core.libxc_func.py.
It requires in input the path of the `libxc_docs.txt` file contained in libxc/src
The script parses this file, creates a new JSON file inside pymatgen.core
and update the enum values declared in LibxcFunc.
The script must be executed inside pymatgen/dev_scripts.
"""
from __future__ import annotations
import json
import sys
from copy import deepcopy
from pymatgen.core import PKG_DIR
def parse_libxc_docs(path):
"""Parse libxc_docs.txt file, return dictionary {libxc_id: info_dict}."""
def parse_section(section):
dct = {}
for line in section:
key, value = line.split(":")
dct[key.strip()] = value.strip()
return int(dct["Number"]), dct
dct = {}
with open(path, encoding="utf-8") as file:
section = []
for line in file:
if not line.startswith("-"):
section += [line]
else:
num, entry = parse_section(section)
if num in dct:
raise RuntimeError(f"{num=} should not be present in {dct=}.")
dct[num] = entry
section = []
if section:
raise RuntimeError(f"Expected empty section, got {section=}")
return dct
def write_libxc_docs_json(xc_funcs, json_path):
"""Write JSON file with libxc metadata to path jpath."""
xc_funcs = deepcopy(xc_funcs)
# Remove XC_FAMILY from Family and XC_ from Kind to make strings more human-readable.
for dct in xc_funcs.values():
dct["Family"] = dct["Family"].replace("XC_FAMILY_", "", 1)
dct["Kind"] = dct["Kind"].replace("XC_", "", 1)
# Build lightweight version with a subset of keys.
for num, dct in xc_funcs.items():
xc_funcs[num] = {key: dct[key] for key in ("Family", "Kind", "References")}
# Descriptions are optional
for opt in ("Description 1", "Description 2"):
desc = dct.get(opt)
if desc is not None:
xc_funcs[num][opt] = desc
with open(json_path, "w", encoding="utf-8") as fh:
json.dump(xc_funcs, fh)
return xc_funcs
def main():
"""Main function."""
if "-h" in sys.argv or "--help" in sys.argv:
print(__doc__)
print("Usage: regen_libxcfunc.py path_to_libxc_docs.txt")
return 0
try:
path = sys.argv[1]
except IndexError:
print(__doc__)
print("Usage: regen_libxcfunc.py path_to_libxc_docs.txt")
return 1
xc_funcs = parse_libxc_docs(path)
# Generate new JSON file in pycore
json_path = f"{PKG_DIR}/core/libxc_docs.json"
write_libxc_docs_json(xc_funcs, json_path)
# Build new enum list.
enum_list = []
for num, d in xc_funcs.items():
# Remove XC_ from codename
codename = d["Codename"][3:]
enum_list += [f" {codename} = {num}"]
enum_list = "\n".join(enum_list) + "\n"
# Re-generate enumerations.
# [0] read py module.
xc_funcpy_path = f"{PKG_DIR}/core/libxcfunc.py"
with open(xc_funcpy_path, encoding="utf-8") as file:
lines = file.readlines()
# [1] insert new enum values in list
start = lines.index("#begin_include_dont_touch\n")
stop = lines.index("#end_include_dont_touch\n")
lines.insert(stop, enum_list)
del lines[start + 1 : stop]
# [2] write new py module
with open(xc_funcpy_path, mode="w", encoding="utf-8") as file:
file.writelines(lines)
print("Files have been regenerated")
print("Remember to update __version__ in libxcfuncs.py!")
return 0
if __name__ == "__main__":
raise SystemExit(main())
|