pymdkit 1.2.3__tar.gz → 1.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pymdkit-1.2.3/src/pymdkit.egg-info → pymdkit-1.2.5}/PKG-INFO +1 -1
- {pymdkit-1.2.3 → pymdkit-1.2.5}/pyproject.toml +1 -1
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/__init__.py +1 -1
- pymdkit-1.2.5/src/pymdkit/commands/stable_entry.py +119 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5/src/pymdkit.egg-info}/PKG-INFO +1 -1
- pymdkit-1.2.3/src/pymdkit/commands/stable_entry.py +0 -116
- {pymdkit-1.2.3 → pymdkit-1.2.5}/LICENSE +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/README.md +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/setup.cfg +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/__init__.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/_fileio.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/_vasp_output.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/_vaspset.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/add_groups.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/compute_ehull.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/compute_msd_all_groups.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/compute_rmsd.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/electrostatic_energy.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/final_energy.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/gather_contcar.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/nep_rmse.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/perturb.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/stru2xyz.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/submit_vasp.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/substitute.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/supercell.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/symmetrize.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/vasp2xyz.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/vasp_relax.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/commands/vasp_static.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit/pymdkit_main.py +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit.egg-info/SOURCES.txt +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit.egg-info/dependency_links.txt +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit.egg-info/entry_points.txt +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit.egg-info/requires.txt +0 -0
- {pymdkit-1.2.3 → pymdkit-1.2.5}/src/pymdkit.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Download stable Materials Project structures for a chemical system."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
COMMAND = "stable-entry"
|
|
10
|
+
HELP = "Download stable Materials Project structures for a chemical system."
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def safe_formula(formula):
|
|
14
|
+
return re.sub(r"[^A-Za-z0-9_.-]+", "", str(formula))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def mpid_text(value):
|
|
18
|
+
if value is None:
|
|
19
|
+
return ""
|
|
20
|
+
if isinstance(value, dict):
|
|
21
|
+
for key in ("material_id", "task_id", "entry_id", "id", "identifier"):
|
|
22
|
+
if value.get(key):
|
|
23
|
+
return mpid_text(value[key])
|
|
24
|
+
return "-".join(f"{k}-{v}" for k, v in sorted(value.items()))
|
|
25
|
+
for attr in ("string", "identifier"):
|
|
26
|
+
text = getattr(value, attr, None)
|
|
27
|
+
if text:
|
|
28
|
+
return str(text)
|
|
29
|
+
text = str(value)
|
|
30
|
+
match = re.search(r"mp-\d+", text)
|
|
31
|
+
if match:
|
|
32
|
+
return match.group(0)
|
|
33
|
+
match = re.search(r"identifier=['\"]?(mp-[A-Za-z0-9]+)", text)
|
|
34
|
+
if match:
|
|
35
|
+
return match.group(1)
|
|
36
|
+
return text
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def entry_id_text(entry):
|
|
40
|
+
entry_id = getattr(entry, "entry_id", None)
|
|
41
|
+
text = mpid_text(entry_id)
|
|
42
|
+
if text:
|
|
43
|
+
return text
|
|
44
|
+
data = getattr(entry, "data", {}) or {}
|
|
45
|
+
for key in ("material_id", "task_id", "entry_id", "id"):
|
|
46
|
+
text = mpid_text(data.get(key))
|
|
47
|
+
if text:
|
|
48
|
+
return text
|
|
49
|
+
return "unknown"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def entry_sort_key(entry):
|
|
53
|
+
formula = str(entry.composition.reduced_formula)
|
|
54
|
+
return (len(entry.composition.elements), formula, entry_id_text(entry))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
58
|
+
parser.add_argument("-s", nargs="+", required=True,
|
|
59
|
+
help="Chemical system elements, e.g. -s Li La Ta Cl.")
|
|
60
|
+
parser.add_argument("-mp-api-key", default=None,
|
|
61
|
+
help="Materials Project API key.")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def fetch_stable_entries(elements, api_key=None):
|
|
65
|
+
"""Fetch entries and keep only entries stable on the full MP phase diagram."""
|
|
66
|
+
from pymatgen.analysis.phase_diagram import PhaseDiagram
|
|
67
|
+
from pymatgen.ext.matproj import MPRester
|
|
68
|
+
|
|
69
|
+
with MPRester(api_key) as mpr:
|
|
70
|
+
entries = mpr.get_entries_in_chemsys(
|
|
71
|
+
elements,
|
|
72
|
+
inc_structure=True,
|
|
73
|
+
additional_criteria={"thermo_types": ["GGA_GGA+U"]},
|
|
74
|
+
)
|
|
75
|
+
phase_diagram = PhaseDiagram(entries)
|
|
76
|
+
stable_entries = list(phase_diagram.stable_entries)
|
|
77
|
+
print(f"Fetched {len(entries)} MP thermo entries; {len(stable_entries)} are stable on the full phase diagram.")
|
|
78
|
+
by_mpid = {}
|
|
79
|
+
for entry in stable_entries:
|
|
80
|
+
by_mpid[entry_id_text(entry)] = entry
|
|
81
|
+
return list(by_mpid.values())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def run(args) -> int:
|
|
85
|
+
from pymatgen.io.cif import CifWriter
|
|
86
|
+
|
|
87
|
+
elements = list(dict.fromkeys(args.s))
|
|
88
|
+
label = "-".join(elements)
|
|
89
|
+
out_dir = Path(f"{label}-stable-entries")
|
|
90
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
entries = fetch_stable_entries(elements, args.mp_api_key)
|
|
94
|
+
except ModuleNotFoundError as exc:
|
|
95
|
+
if exc.name == "emmet":
|
|
96
|
+
raise SystemExit(
|
|
97
|
+
"Error: stable-entry requires emmet models from mp-api. "
|
|
98
|
+
"Reinstall pymdkit or run `pip install mp-api`."
|
|
99
|
+
) from exc
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
entries = sorted(entries, key=entry_sort_key)
|
|
103
|
+
written = 0
|
|
104
|
+
for entry in entries:
|
|
105
|
+
mpid = safe_formula(entry_id_text(entry).split(" ")[0])
|
|
106
|
+
formula = safe_formula(entry.composition.reduced_formula)
|
|
107
|
+
path = out_dir / f"{mpid}-{formula}.cif"
|
|
108
|
+
CifWriter(entry.structure).write_file(str(path))
|
|
109
|
+
print(f"{mpid}\t{entry.composition.reduced_formula}\t-> {path}")
|
|
110
|
+
written += 1
|
|
111
|
+
|
|
112
|
+
print(f"Wrote {written} stable entr{'y' if written == 1 else 'ies'} to {out_dir}")
|
|
113
|
+
return 0 if written else 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
_parser = argparse.ArgumentParser(description=__doc__)
|
|
118
|
+
add_arguments(_parser)
|
|
119
|
+
raise SystemExit(run(_parser.parse_args()))
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"""Download stable Materials Project structures for a chemical system."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import argparse
|
|
6
|
-
import re
|
|
7
|
-
from itertools import combinations
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
COMMAND = "stable-entry"
|
|
12
|
-
HELP = "Download stable Materials Project structures for a chemical system."
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class StableStructure:
|
|
17
|
-
material_id: str
|
|
18
|
-
formula: str
|
|
19
|
-
structure: object
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def safe_formula(formula):
|
|
23
|
-
return re.sub(r"[^A-Za-z0-9_.-]+", "", str(formula))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def doc_get(doc, name, default=None):
|
|
27
|
-
if isinstance(doc, dict):
|
|
28
|
-
return doc.get(name, default)
|
|
29
|
-
return getattr(doc, name, default)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def normalize_doc(doc):
|
|
33
|
-
material_id = str(doc_get(doc, "material_id", "unknown"))
|
|
34
|
-
formula = doc_get(doc, "formula_pretty") or doc_get(doc, "formula")
|
|
35
|
-
structure = doc_get(doc, "structure")
|
|
36
|
-
if formula is None and structure is not None:
|
|
37
|
-
formula = structure.composition.reduced_formula
|
|
38
|
-
if structure is None:
|
|
39
|
-
raise ValueError(f"MP document {material_id} has no structure field")
|
|
40
|
-
return StableStructure(material_id=material_id, formula=str(formula), structure=structure)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def entry_sort_key(entry):
|
|
44
|
-
return (len(entry.structure.composition.elements), entry.formula, entry.material_id)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
48
|
-
parser.add_argument("-s", nargs="+", required=True,
|
|
49
|
-
help="Chemical system elements, e.g. -s Li La Ta Cl.")
|
|
50
|
-
parser.add_argument("-mp-api-key", default=None,
|
|
51
|
-
help="Materials Project API key.")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def subsystem_labels(elements):
|
|
55
|
-
"""Return all subsystem labels needed to build the full chemsys hull."""
|
|
56
|
-
labels = []
|
|
57
|
-
for size in range(1, len(elements) + 1):
|
|
58
|
-
for subset in combinations(elements, size):
|
|
59
|
-
labels.append("-".join(subset))
|
|
60
|
-
return labels
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def fetch_stable_structures(elements, api_key=None):
|
|
64
|
-
from mp_api.client import MPRester
|
|
65
|
-
|
|
66
|
-
fields = ["material_id", "formula_pretty", "structure"]
|
|
67
|
-
by_mpid = {}
|
|
68
|
-
with MPRester(api_key) as mpr:
|
|
69
|
-
for chemsys in subsystem_labels(elements):
|
|
70
|
-
docs = mpr.materials.summary.search(
|
|
71
|
-
chemsys=chemsys,
|
|
72
|
-
is_stable=True,
|
|
73
|
-
fields=fields,
|
|
74
|
-
)
|
|
75
|
-
for doc in docs:
|
|
76
|
-
entry = normalize_doc(doc)
|
|
77
|
-
by_mpid[entry.material_id] = entry
|
|
78
|
-
return list(by_mpid.values())
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def run(args) -> int:
|
|
82
|
-
from pymatgen.io.cif import CifWriter
|
|
83
|
-
|
|
84
|
-
elements = list(dict.fromkeys(args.s))
|
|
85
|
-
label = "-".join(elements)
|
|
86
|
-
out_dir = Path(f"{label}-stable-entries")
|
|
87
|
-
out_dir.mkdir(parents=True, exist_ok=True)
|
|
88
|
-
|
|
89
|
-
try:
|
|
90
|
-
entries = fetch_stable_structures(elements, args.mp_api_key)
|
|
91
|
-
except ModuleNotFoundError as exc:
|
|
92
|
-
if exc.name == "mp_api":
|
|
93
|
-
raise SystemExit(
|
|
94
|
-
"Error: stable-entry requires mp-api. Reinstall pymdkit 1.2.3+ "
|
|
95
|
-
"or run `pip install mp-api`."
|
|
96
|
-
) from exc
|
|
97
|
-
raise
|
|
98
|
-
|
|
99
|
-
entries = sorted(entries, key=entry_sort_key)
|
|
100
|
-
written = 0
|
|
101
|
-
for entry in entries:
|
|
102
|
-
mpid = safe_formula(entry.material_id)
|
|
103
|
-
formula = safe_formula(entry.formula)
|
|
104
|
-
path = out_dir / f"{mpid}-{formula}.cif"
|
|
105
|
-
CifWriter(entry.structure).write_file(str(path))
|
|
106
|
-
print(f"{mpid}\t{entry.formula}\t-> {path}")
|
|
107
|
-
written += 1
|
|
108
|
-
|
|
109
|
-
print(f"Wrote {written} stable entr{'y' if written == 1 else 'ies'} to {out_dir}")
|
|
110
|
-
return 0 if written else 1
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if __name__ == "__main__":
|
|
114
|
-
_parser = argparse.ArgumentParser(description=__doc__)
|
|
115
|
-
add_arguments(_parser)
|
|
116
|
-
raise SystemExit(run(_parser.parse_args()))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|