Hi @Min-Hsueh_Chiu
Sorry for my late reply (I hand in my PhD in 12 days).
I got Claude code to track down the issue, and provide the code below to illustrate the issue.
It has to do with how MP2020 looks up the oxidation state of N, using a string key against a dict the API serves keyed by Element objects. I suspect that it is not confined to N, but that the issue occurs for the lookup of Br, I, Se, Si, Sb, Te, H, N, F and Cl, so in any compound with two or more of these, all but the most electronegative silently lose their correction.
This error drops the N oxidation state, and for instance undercorrects Zr2ON2 as it only receives a correction for O and not N.
under GGA (GGA_GGA+U) only your web page gives Zr2ON2 as 126.2 meV/atom above the hull - where as it should be on the hull (0 meV/atom) if it also gets the N correction as it should (also a problem for GGA/r2SCAN mixing of course, but slightly different values).
This is just one example - but probably extends across your database.
Reproduction, pymatgen 2026.5.4 with mp-api 0.46.4:
"""
MP2020 silently drops the N anion correction for oxynitrides.
MaterialsProject2020Compatibility looks up the anion oxidation state with a string key,
`entry.data["oxidation_states"].get("N", 0)`, but the MP API serves that dict keyed by
Element objects. The lookup misses, .get returns 0, so N is not recognised as an anion
and its correction is never applied. No warning is raised.
Paste your MP API key below and run. pymatgen 2026.5.4, mp-api 0.46.4.
"""
import os
from mp_api.client import MPRester
from pymatgen.core import Element
from pymatgen.entries.compatibility import MaterialsProject2020Compatibility
from pymatgen.entries.computed_entries import ComputedStructureEntry
MP_API_KEY = "" # <-- paste your Materials Project API key here
with MPRester(MP_API_KEY or os.environ["MP_API_KEY"]) as mpr:
entry = next(e for e in mpr.get_entries("Zr2N2O") if e.parameters["run_type"] == "GGA")
states = entry.data["oxidation_states"]
print("oxidation_states served by MP:", states)
print(' states.get("N", 0) ->', states.get("N", 0), " <-- the lookup MP2020 does")
print(' states.get(Element("N")) ->', states.get(Element("N")))
def corrections_applied(oxidation_states):
"""Names of the MP2020 adjustments for this entry, given an oxidation-state dict."""
fresh = ComputedStructureEntry(entry.structure, entry.uncorrected_energy,
parameters={"run_type": "GGA"},
data={"oxidation_states": oxidation_states})
corrected = MaterialsProject2020Compatibility(check_potcar=False).process_entries(
[fresh], clean=True, verbose=False)[0]
return [adj.name for adj in corrected.energy_adjustments]
print("\nMP2020 corrections applied to", entry.entry_id)
print(" keys as served (Element):", corrections_applied(states))
print(" keys cast to str :", corrections_applied({str(k): v for k, v in states.items()}))
Output:
oxidation_states served by MP: {Element Zr: 4.0, Element N: -3.0, Element O: -2.0}
states.get("N", 0) -> 0 <-- the lookup MP2020 does
states.get(Element("N")) -> -3.0
MP2020 corrections applied to mp-775849-GGA
keys as served (Element): ['MP2020 anion correction (oxide)']
keys cast to str : ['MP2020 anion correction (oxide)', 'MP2020 anion correction (N)']