Skip to content

Commit

Permalink
Merge pull request #2128 from rkingsbury/default_mp2020
Browse files Browse the repository at this point in the history
Add auto oxidation state detection to MP2020 ; incremental update to correction values
  • Loading branch information
mkhorton committed May 3, 2021
2 parents af726e3 + faed4ef commit 0ab0a8b
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 33 deletions.
36 changes: 18 additions & 18 deletions pymatgen/entries/MP2020Compatibility.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Corrections:
Fe: -2.256
Co: -1.638
Ni: -2.541
W: -4.437
W: -4.438
Mo: -3.202
F:
V: -1.7
Expand All @@ -21,24 +21,24 @@ Corrections:
Fe: -2.256
Co: -1.638
Ni: -2.541
W: -4.437
W: -4.438
Mo: -3.202
CompositionCorrections:
# Composition-based corrections applied to any compound containing
# these species as anions
oxide: -0.688
peroxide: -0.466
oxide: -0.687
peroxide: -0.465
superoxide: -0.161
S: -0.504
S: -0.503
F: -0.462
Cl: -0.614
Br: -0.535
Br: -0.534
I: -0.379
N: -0.359
N: -0.361
Se: -0.472
Si: 0.072
Si: 0.071
Sb: -0.192
Te: -0.42
Te: -0.422
H: -0.179
ozonide: 0
Uncertainties:
Expand All @@ -51,30 +51,30 @@ Uncertainties:
Fe: 0.0101
Co: 0.006
Ni: 0.0107
W: 0.0254
Mo: 0.009
W: 0.0253
Mo: 0.0089
F:
V: 0.0064
Cr: 0.0108
Mn: 0.0053
Fe: 0.0101
Co: 0.006
Ni: 0.0107
W: 0.0254
Mo: 0.009
W: 0.0253
Mo: 0.0089
CompositionCorrections:
oxide: 0.002
peroxide: 0.0174
peroxide: 0.0172
superoxide: 0.0075
S: 0.0094
F: 0.0027
S: 0.0093
F: 0.0026
Cl: 0.0018
Br: 0.0026
I: 0.0055
N: 0.0094
N: 0.0093
Se: 0.0341
Si: 0.0165
Sb: 0.0089
Te: 0.0264
Te: 0.0262
H: 0.0013
ozonide: 0
Binary file modified pymatgen/entries/calc_compounds.json.gz
Binary file not shown.
28 changes: 22 additions & 6 deletions pymatgen/entries/compatibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -1008,14 +1008,30 @@ def get_adjustments(self, entry: Union[ComputedEntry, ComputedStructureEntry]):
# first check for a pre-populated oxidation states key
# the key is expected to comprise a dict corresponding to the first element output by
# Composition.oxi_state_guesses(), e.g. {'Al': 3.0, 'S': 2.0, 'O': -2.0} for 'Al2SO4'
if entry.data.get("oxidation_states"):
if entry.data["oxidation_states"].get(anion, 0) < 0:
apply_correction = True
if not entry.data.get("oxidation_states"):
# try to guess the oxidation states from composition
# for performance reasons, fail if the composition is too large
try:
oxi_states = entry.composition.oxi_state_guesses(max_sites=-20)
except ValueError:
oxi_states = []

if oxi_states == []:
warnings.warn(
f"Failed to guess oxidation states for Entry {entry.entry_id} "
f"({entry.composition.reduced_formula}. Assigning anion correction to "
"only the most electronegative atom."
)
entry.data["oxidation_states"] = {}
else:
entry.data["oxidation_states"] = oxi_states[0]

# if the oxidation_states key is not populated, only apply the correction if the anion
# is the most electronegative element
if entry.data["oxidation_states"].get(anion, 0) < 0:
apply_correction = True
else:
# if the oxidation_states key is not populated, only apply the correction if the anion
# is the most electronegative element
most_electroneg = elements[-1].symbol

if anion == most_electroneg:
apply_correction = True

Expand Down
Binary file modified pymatgen/entries/exp_compounds.json.gz
Binary file not shown.
56 changes: 47 additions & 9 deletions pymatgen/entries/tests/test_compatibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,9 +681,47 @@ def test_process_entry(self):
)
self.assertIsNotNone(self.compat.process_entry(entry))

def test_oxi_state_guess(self):
# An entry where Composition.oxi_state_guesses will return an empty list
entry_blank = ComputedEntry(
"Ga3Te",
-12.1900,
correction=0.0,
parameters={
"run_type": "GGA",
"is_hubbard": False,
"pseudo_potential": {"functional": "PBE", "labels": ["Ga_d", "Te"], "pot_type": "paw"},
"hubbards": {},
"potcar_symbols": ["PBE Ga_d", "PBE Te"],
"oxide_type": "None",
},
)

# An entry where one anion will only be corrected if oxidation_states is populated
entry_oxi = ComputedEntry(
"Mo2Cl8O",
-173.0655,
correction=0.0,
parameters={
"run_type": "GGA+U",
"is_hubbard": True,
"pseudo_potential": {"functional": "PBE", "labels": ["Mo_pv", "Cl", "O"], "pot_type": "paw"},
"hubbards": {"Mo": 4.38, "Cl": 0.0, "O": 0.0},
"potcar_symbols": ["PBE Mo_pv", "PBE Cl", "PBE O"],
"oxide_type": "oxide",
},
)

with pytest.warns(UserWarning, match="Failed to guess oxidation state"):
e1 = self.compat.process_entry(entry_blank)
self.assertAlmostEqual(e1.correction, -0.422)

e2 = self.compat.process_entry(entry_oxi)
self.assertAlmostEqual(e2.correction, -0.687 + -3.202 * 2 + -0.614 * 8)

def test_correction_values(self):
# test_corrections
self.assertAlmostEqual(self.compat.process_entry(self.entry1).correction, -2.256 * 2 - 0.688 * 3)
self.assertAlmostEqual(self.compat.process_entry(self.entry1).correction, -2.256 * 2 - 0.687 * 3)

entry = ComputedEntry(
"FeF3",
Expand All @@ -710,7 +748,7 @@ def test_correction_values(self):
# Check actual correction
self.assertAlmostEqual(self.compat.process_entry(entry).correction, -0.462 * 3 + -2.256)

self.assertAlmostEqual(self.compat.process_entry(self.entry_sulfide).correction, -0.504)
self.assertAlmostEqual(self.compat.process_entry(self.entry_sulfide).correction, -0.503)

def test_oxdiation_by_electronegativity(self):
# make sure anion corrections are only applied when the element has
Expand Down Expand Up @@ -768,10 +806,10 @@ def test_oxdiation_by_electronegativity(self):
)

# CaSi; only correction should be Si
self.assertAlmostEqual(self.compat.process_entry(entry1).correction, 0.072 * 2)
self.assertAlmostEqual(self.compat.process_entry(entry1).correction, 0.071 * 2)

# SiO2; only corrections should be oxide
self.assertAlmostEqual(self.compat.process_entry(entry2).correction, -0.688 * 4)
self.assertAlmostEqual(self.compat.process_entry(entry2).correction, -0.687 * 4)

def test_oxdiation(self):
# make sure anion corrections are only applied when the element has
Expand Down Expand Up @@ -835,10 +873,10 @@ def test_oxdiation(self):
)

# CaSi; only correction should be Si
self.assertAlmostEqual(self.compat.process_entry(entry1).correction, 0.072 * 2)
self.assertAlmostEqual(self.compat.process_entry(entry1).correction, 0.071 * 2)

# SiO2; only corrections should be oxide
self.assertAlmostEqual(self.compat.process_entry(entry2).correction, -0.688 * 4)
self.assertAlmostEqual(self.compat.process_entry(entry2).correction, -0.687 * 4)

def test_U_values(self):
# Wrong U value
Expand Down Expand Up @@ -1045,7 +1083,7 @@ def test_energy_adjustments(self):
self.assertAlmostEqual(ea.value, -1.638 * 2)
self.assertAlmostEqual(ea.uncertainty, 0.006 * 2)
elif ea.name == "MP2020 anion correction (oxide)":
self.assertAlmostEqual(ea.value, -0.688 * 8)
self.assertAlmostEqual(ea.value, -0.687 * 8)
self.assertAlmostEqual(ea.uncertainty, 0.002 * 8)

entry.parameters["is_hubbard"] = False
Expand Down Expand Up @@ -2237,12 +2275,12 @@ def test_errors(self):
)

entry_sulfide_corrected = self.compat.process_entry(self.entry_sulfide)
self.assertAlmostEqual(entry_sulfide_corrected.correction_uncertainty, 0.0094)
self.assertAlmostEqual(entry_sulfide_corrected.correction_uncertainty, 0.0093)

entry_fluoride_corrected = self.compat.process_entry(self.entry_fluoride)
self.assertAlmostEqual(
entry_fluoride_corrected.correction_uncertainty,
sqrt((3 * 0.0027) ** 2 + 0.0101 ** 2),
sqrt((3 * 0.0026) ** 2 + 0.0101 ** 2),
)

entry_hydride_corrected = self.compat.process_entry(self.entry_hydride)
Expand Down

0 comments on commit 0ab0a8b

Please sign in to comment.