Skip to content

Commit 15b0b8e

Browse files
authored
Add check_potcar: bool = True to MaterialsProjectDFTMixingScheme (#3143)
* add check_potcar: bool = True to PotcarCorrection * add check_potcar: bool = True to MaterialsProject2020Compatibility * add check_potcar: bool = True to MaterialsProjectDFTMixingScheme * fix broken test due to modified error msg * refactor CorrectionErrors2020CompatibilityTest.test_errors to use loop * add MaterialsProjectCompatibility2020Test.test_check_potcar * add TestMaterialsProjectDFTMixingSchemeArgs.test_potcar_check()
1 parent cf17302 commit 15b0b8e

File tree

5 files changed

+413
-747
lines changed

5 files changed

+413
-747
lines changed

pymatgen/core/tests/test_sites.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def setUp(self):
1919
self.ordered_site = Site("Fe", [0.25, 0.35, 0.45])
2020
self.disordered_site = Site({"Fe": 0.5, "Mn": 0.5}, [0.25, 0.35, 0.45])
2121
self.propertied_site = Site("Fe2+", [0.25, 0.35, 0.45], {"magmom": 5.1, "charge": 4.2})
22-
self.propertied_magmomvector_site = Site(
22+
self.propertied_magmom_vec_site = Site(
2323
"Fe2+",
2424
[0.25, 0.35, 0.45],
2525
{"magmom": Magmom([2.6, 2.6, 3.5]), "charge": 4.2},
@@ -44,7 +44,7 @@ def test_to_from_dict(self):
4444
site = Site.from_dict(d)
4545
assert site.properties["magmom"] == 5.1
4646
assert site.properties["charge"] == 4.2
47-
d = self.propertied_magmomvector_site.as_dict()
47+
d = self.propertied_magmom_vec_site.as_dict()
4848
site = Site.from_dict(d)
4949
assert site.properties["magmom"] == Magmom([2.6, 2.6, 3.5])
5050
assert site.properties["charge"] == 4.2

pymatgen/entries/compatibility.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
EnergyAdjustment,
3131
TemperatureEnergyAdjustment,
3232
)
33-
from pymatgen.io.vasp.sets import MITRelaxSet, MPRelaxSet
33+
from pymatgen.io.vasp.sets import MITRelaxSet, MPRelaxSet, VaspInputSet
3434
from pymatgen.util.due import Doi, due
3535

3636
__author__ = "Amanda Wang, Ryan Kingsbury, Shyue Ping Ong, Anubhav Jain, Stephen Dacek, Sai Jayaraman"
@@ -119,48 +119,55 @@ class PotcarCorrection(Correction):
119119
'PAW_PBE O 08Apr2002'].
120120
"""
121121

122-
def __init__(self, input_set, check_hash=False):
122+
def __init__(self, input_set: type[VaspInputSet], check_potcar: bool = True, check_hash: bool = False) -> None:
123123
"""
124124
Args:
125-
input_set: InputSet object used to generate the runs (used to check
125+
input_set (InputSet): object used to generate the runs (used to check
126126
for correct potcar symbols).
127-
128-
check_hash (bool): If true, uses the potcar hash to check for valid
129-
potcars. If false, uses the potcar symbol (Less reliable).
130-
Defaults to True
127+
check_potcar (bool): If False, bypass the POTCAR check altogether. Defaults to True
128+
check_hash (bool): If True, uses the potcar hash to check for valid
129+
potcars. If false, uses the potcar symbol (less reliable). Defaults to False.
131130
132131
Raises:
133-
ValueError if entry do not contain "potcar_symbols" key.
134-
CompatibilityError if wrong potcar symbols
132+
ValueError: if check_potcar=True and entry does not contain "potcar_symbols" key.
135133
"""
136134
potcar_settings = input_set.CONFIG["POTCAR"]
137135
if isinstance(list(potcar_settings.values())[-1], dict):
138-
if check_hash:
139-
self.valid_potcars = {k: d["hash"] for k, d in potcar_settings.items()}
140-
else:
141-
self.valid_potcars = {k: d["symbol"] for k, d in potcar_settings.items()}
136+
self.valid_potcars = {
137+
key: dct.get("hash" if check_hash else "symbol") for key, dct in potcar_settings.items()
138+
}
142139
else:
143140
if check_hash:
144141
raise ValueError("Cannot check hashes of potcars, since hashes are not included in the entry.")
145142
self.valid_potcars = potcar_settings
146143

147144
self.input_set = input_set
148145
self.check_hash = check_hash
146+
self.check_potcar = check_potcar
149147

150148
def get_correction(self, entry: AnyComputedEntry) -> ufloat:
151149
"""
152-
:param entry: A ComputedEntry/ComputedStructureEntry
153-
:return: Correction, Uncertainty.
150+
Args:
151+
entry (AnyComputedEntry): ComputedEntry or ComputedStructureEntry.
152+
153+
Raises:
154+
ValueError: If entry does not contain "potcar_symbols" key.
155+
CompatibilityError: If entry has wrong potcar hash/symbols.
156+
157+
Returns:
158+
ufloat: 0.0 +/- 0.0 (from uncertainties package)
154159
"""
155-
if SETTINGS.get("PMG_DISABLE_POTCAR_CHECKS", False):
160+
if SETTINGS.get("PMG_DISABLE_POTCAR_CHECKS", False) or not self.check_potcar:
156161
return ufloat(0.0, 0.0)
162+
163+
potcar_spec = entry.parameters.get("potcar_spec")
157164
if self.check_hash:
158-
if entry.parameters.get("potcar_spec"):
159-
psp_settings = {d.get("hash") for d in entry.parameters["potcar_spec"] if d}
165+
if potcar_spec:
166+
psp_settings = {dct.get("hash") for dct in potcar_spec if dct}
160167
else:
161168
raise ValueError("Cannot check hash without potcar_spec field")
162-
elif entry.parameters.get("potcar_spec"):
163-
psp_settings = {d.get("titel").split()[1] for d in entry.parameters["potcar_spec"] if d}
169+
elif potcar_spec:
170+
psp_settings = {dct.get("titel").split()[1] for dct in potcar_spec if dct}
164171
else:
165172
psp_settings = {sym.split()[1] for sym in entry.parameters["potcar_symbols"] if sym}
166173

@@ -840,6 +847,7 @@ def __init__(
840847
self,
841848
compat_type: str = "Advanced",
842849
correct_peroxide: bool = True,
850+
check_potcar: bool = True,
843851
check_potcar_hash: bool = False,
844852
config_file: str | None = None,
845853
) -> None:
@@ -867,6 +875,9 @@ def __init__(
867875
correct_peroxide: Specify whether peroxide/superoxide/ozonide
868876
corrections are to be applied or not. If false, all oxygen-containing
869877
compounds are assigned the 'oxide' correction. (Default: True)
878+
check_potcar (bool): Check that the POTCARs used in the calculation
879+
are consistent with the Materials Project parameters. False bypasses this
880+
check altogether. (Default: True)
870881
check_potcar_hash (bool): Use potcar hash to verify POTCAR settings are
871882
consistent with MPRelaxSet. If False, only the POTCAR symbols will
872883
be used. (Default: False)
@@ -888,6 +899,7 @@ def __init__(
888899

889900
self.compat_type = compat_type
890901
self.correct_peroxide = correct_peroxide
902+
self.check_potcar = check_potcar
891903
self.check_potcar_hash = check_potcar_hash
892904

893905
# load corrections and uncertainties
@@ -940,7 +952,7 @@ def get_adjustments(self, entry: AnyComputedEntry) -> list[EnergyAdjustment]:
940952
# check the POTCAR symbols
941953
# this should return ufloat(0, 0) or raise a CompatibilityError or ValueError
942954
if entry.parameters.get("software", "vasp") == "vasp":
943-
pc = PotcarCorrection(MPRelaxSet, check_hash=self.check_potcar_hash)
955+
pc = PotcarCorrection(MPRelaxSet, check_hash=self.check_potcar_hash, check_potcar=self.check_potcar)
944956
pc.get_correction(entry)
945957

946958
# apply energy adjustments

pymatgen/entries/mixing_scheme.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def __init__(
5454
compat_1: Compatibility | None = MaterialsProject2020Compatibility(), # noqa: B008
5555
compat_2: Compatibility | None = None,
5656
fuzzy_matching: bool = True,
57-
):
57+
check_potcar: bool = True,
58+
) -> None:
5859
"""
5960
Instantiate the mixing scheme. The init method creates a generator class that
6061
contains relevant settings (e.g., StructureMatcher instance, Compatibility settings
@@ -93,29 +94,28 @@ def __init__(
9394
space group are all identical. If there are multiple materials of run_type_2
9495
that satisfy these criteria, the one with lowest energy is considered to
9596
match.
97+
check_potcar: Whether to perform additional checks to ensure that the POTCARs
98+
used for the run_type_1 and run_type_2 calculations are the same. This is
99+
useful for ensuring that the mixing scheme is not used on calculations that
100+
used different POTCARs, which can lead to unphysical results. Defaults to True.
101+
Has no effect if neither compat_1 nor compat_2 have a check_potcar attribute.
96102
"""
97103
self.name = "MP DFT mixing scheme"
98104
self.structure_matcher = structure_matcher or StructureMatcher()
99105
if run_type_1 == run_type_2:
100-
raise ValueError(
101-
f"You specified the same run_type {run_type_1} for both run_type_1 and run_type_2. "
102-
"The mixing scheme is meaningless unless run_type_1 and run_type_2 are different"
103-
)
106+
raise ValueError(f"run_type_1={run_type_2=}. The mixing scheme is meaningless unless run_types different")
104107
self.run_type_1 = run_type_1
105108
self.run_type_2 = run_type_2
106-
if self.run_type_1 == "GGA(+U)":
107-
self.valid_rtypes_1 = ["GGA", "GGA+U"]
108-
else:
109-
self.valid_rtypes_1 = [self.run_type_1]
110-
111-
if self.run_type_2 == "GGA(+U)":
112-
self.valid_rtypes_2 = ["GGA", "GGA+U"]
113-
else:
114-
self.valid_rtypes_2 = [self.run_type_2]
109+
self.valid_rtypes_1 = ["GGA", "GGA+U"] if self.run_type_1 == "GGA(+U)" else [self.run_type_1]
110+
self.valid_rtypes_2 = ["GGA", "GGA+U"] if self.run_type_2 == "GGA(+U)" else [self.run_type_2]
115111

116112
self.compat_1 = compat_1
117113
self.compat_2 = compat_2
118114
self.fuzzy_matching = fuzzy_matching
115+
self.check_potcar = check_potcar
116+
for compat in (self.compat_1, self.compat_2):
117+
if hasattr(compat, "check_potcar"):
118+
compat.check_potcar = check_potcar # type: ignore[union-attr]
119119

120120
def process_entries(
121121
self,

0 commit comments

Comments
 (0)