Skip to content

Add check_potcar: bool = True to MaterialsProject2020Compatibility, MaterialsProjectDFTMixingScheme and PotcarCorrection #3143

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

Merged
merged 9 commits into from
Jul 12, 2023
Merged
4 changes: 2 additions & 2 deletions pymatgen/core/tests/test_sites.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def setUp(self):
self.ordered_site = Site("Fe", [0.25, 0.35, 0.45])
self.disordered_site = Site({"Fe": 0.5, "Mn": 0.5}, [0.25, 0.35, 0.45])
self.propertied_site = Site("Fe2+", [0.25, 0.35, 0.45], {"magmom": 5.1, "charge": 4.2})
self.propertied_magmomvector_site = Site(
self.propertied_magmom_vec_site = Site(
"Fe2+",
[0.25, 0.35, 0.45],
{"magmom": Magmom([2.6, 2.6, 3.5]), "charge": 4.2},
Expand All @@ -44,7 +44,7 @@ def test_to_from_dict(self):
site = Site.from_dict(d)
assert site.properties["magmom"] == 5.1
assert site.properties["charge"] == 4.2
d = self.propertied_magmomvector_site.as_dict()
d = self.propertied_magmom_vec_site.as_dict()
site = Site.from_dict(d)
assert site.properties["magmom"] == Magmom([2.6, 2.6, 3.5])
assert site.properties["charge"] == 4.2
Expand Down
54 changes: 33 additions & 21 deletions pymatgen/entries/compatibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
EnergyAdjustment,
TemperatureEnergyAdjustment,
)
from pymatgen.io.vasp.sets import MITRelaxSet, MPRelaxSet
from pymatgen.io.vasp.sets import MITRelaxSet, MPRelaxSet, VaspInputSet
from pymatgen.util.due import Doi, due

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

def __init__(self, input_set, check_hash=False):
def __init__(self, input_set: type[VaspInputSet], check_potcar: bool = True, check_hash: bool = False) -> None:
"""
Args:
input_set: InputSet object used to generate the runs (used to check
input_set (InputSet): object used to generate the runs (used to check
for correct potcar symbols).

check_hash (bool): If true, uses the potcar hash to check for valid
potcars. If false, uses the potcar symbol (Less reliable).
Defaults to True
check_potcar (bool): If False, bypass the POTCAR check altogether. Defaults to True
check_hash (bool): If True, uses the potcar hash to check for valid
potcars. If false, uses the potcar symbol (less reliable). Defaults to False.

Raises:
ValueError if entry do not contain "potcar_symbols" key.
CompatibilityError if wrong potcar symbols
ValueError: if check_potcar=True and entry does not contain "potcar_symbols" key.
"""
potcar_settings = input_set.CONFIG["POTCAR"]
if isinstance(list(potcar_settings.values())[-1], dict):
if check_hash:
self.valid_potcars = {k: d["hash"] for k, d in potcar_settings.items()}
else:
self.valid_potcars = {k: d["symbol"] for k, d in potcar_settings.items()}
self.valid_potcars = {
key: dct.get("hash" if check_hash else "symbol") for key, dct in potcar_settings.items()
}
else:
if check_hash:
raise ValueError("Cannot check hashes of potcars, since hashes are not included in the entry.")
self.valid_potcars = potcar_settings

self.input_set = input_set
self.check_hash = check_hash
self.check_potcar = check_potcar

def get_correction(self, entry: AnyComputedEntry) -> ufloat:
"""
:param entry: A ComputedEntry/ComputedStructureEntry
:return: Correction, Uncertainty.
Args:
entry (AnyComputedEntry): ComputedEntry or ComputedStructureEntry.

Raises:
ValueError: If entry does not contain "potcar_symbols" key.
CompatibilityError: If entry has wrong potcar hash/symbols.

Returns:
ufloat: 0.0 +/- 0.0 (from uncertainties package)
"""
if SETTINGS.get("PMG_DISABLE_POTCAR_CHECKS", False):
if SETTINGS.get("PMG_DISABLE_POTCAR_CHECKS", False) or not self.check_potcar:
return ufloat(0.0, 0.0)

potcar_spec = entry.parameters.get("potcar_spec")
if self.check_hash:
if entry.parameters.get("potcar_spec"):
psp_settings = {d.get("hash") for d in entry.parameters["potcar_spec"] if d}
if potcar_spec:
psp_settings = {dct.get("hash") for dct in potcar_spec if dct}
else:
raise ValueError("Cannot check hash without potcar_spec field")
elif entry.parameters.get("potcar_spec"):
psp_settings = {d.get("titel").split()[1] for d in entry.parameters["potcar_spec"] if d}
elif potcar_spec:
psp_settings = {dct.get("titel").split()[1] for dct in potcar_spec if dct}
else:
psp_settings = {sym.split()[1] for sym in entry.parameters["potcar_symbols"] if sym}

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

self.compat_type = compat_type
self.correct_peroxide = correct_peroxide
self.check_potcar = check_potcar
self.check_potcar_hash = check_potcar_hash

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

# apply energy adjustments
Expand Down
28 changes: 14 additions & 14 deletions pymatgen/entries/mixing_scheme.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def __init__(
compat_1: Compatibility | None = MaterialsProject2020Compatibility(), # noqa: B008
compat_2: Compatibility | None = None,
fuzzy_matching: bool = True,
):
check_potcar: bool = True,
) -> None:
"""
Instantiate the mixing scheme. The init method creates a generator class that
contains relevant settings (e.g., StructureMatcher instance, Compatibility settings
Expand Down Expand Up @@ -93,29 +94,28 @@ def __init__(
space group are all identical. If there are multiple materials of run_type_2
that satisfy these criteria, the one with lowest energy is considered to
match.
check_potcar: Whether to perform additional checks to ensure that the POTCARs
used for the run_type_1 and run_type_2 calculations are the same. This is
useful for ensuring that the mixing scheme is not used on calculations that
used different POTCARs, which can lead to unphysical results. Defaults to True.
Has no effect if neither compat_1 nor compat_2 have a check_potcar attribute.
"""
self.name = "MP DFT mixing scheme"
self.structure_matcher = structure_matcher or StructureMatcher()
if run_type_1 == run_type_2:
raise ValueError(
f"You specified the same run_type {run_type_1} for both run_type_1 and run_type_2. "
"The mixing scheme is meaningless unless run_type_1 and run_type_2 are different"
)
raise ValueError(f"run_type_1={run_type_2=}. The mixing scheme is meaningless unless run_types different")
self.run_type_1 = run_type_1
self.run_type_2 = run_type_2
if self.run_type_1 == "GGA(+U)":
self.valid_rtypes_1 = ["GGA", "GGA+U"]
else:
self.valid_rtypes_1 = [self.run_type_1]

if self.run_type_2 == "GGA(+U)":
self.valid_rtypes_2 = ["GGA", "GGA+U"]
else:
self.valid_rtypes_2 = [self.run_type_2]
self.valid_rtypes_1 = ["GGA", "GGA+U"] if self.run_type_1 == "GGA(+U)" else [self.run_type_1]
self.valid_rtypes_2 = ["GGA", "GGA+U"] if self.run_type_2 == "GGA(+U)" else [self.run_type_2]

self.compat_1 = compat_1
self.compat_2 = compat_2
self.fuzzy_matching = fuzzy_matching
self.check_potcar = check_potcar
for compat in (self.compat_1, self.compat_2):
if hasattr(compat, "check_potcar"):
compat.check_potcar = check_potcar # type: ignore[union-attr]

def process_entries(
self,
Expand Down
Loading