Skip to content

Re-mix GGA(+U)/r2SCAN entries without fuzzy matching, as for MP's pre-built phase diagrams - #1141

Open
kavanase wants to merge 2 commits into
materialsproject:mainfrom
kavanase:fix/remix-strict-gas-matching
Open

kavanase wants to merge 2 commits into
materialsproject:mainfrom
kavanase:fix/remix-strict-gas-matching

Conversation

@kavanase

@kavanase kavanase commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When MP has no pre-built phase diagram for a chemical system, get_entries_in_chemsys re-mixes the GGA(+U) and
r2SCAN entries locally with MaterialsProjectDFTMixingScheme(), which uses pymatgen's default fuzzy_matching=True.
That matches O2, H2, Cl2, F2, N2, I, Br and H2O across functionals by formula, space group and number of sites only
(no structure matching). MP builds its phase diagrams with fuzzy_matching=False, as recorded in the cls of the served
entries' mixing adjustments. So for any chemical system with one of these elements, the local re-mix (which happens if requesting a non pre-built PD) differs from MP's diagram (and the website).

This PR sets fuzzy_matching=False in the re-mix. Re-mixing the unmixed entries of systems that do have a pre-built diagram then reproduces MP's diagrams exactly.

Noting that get_stability keeps the default (fuzzy_matching=True): it re-mixes the user's own entries with MP's, and without fuzzy matching a user's r2SCAN entry at a new composition in a system with O/F/Cl/Br/I is dropped by the mixing scheme (as the system is then GGA(+U)-anchored), after which get_e_above_hull raises. E.g. an r2SCAN NaLu(SiO3)2 entry in Lu-Na-O-Si gets 0.000 eV/atom with fuzzy matching, and ValueError: No valid decomposition found without.

test_get_entries_in_chemsys_remix_matches_served added, which artificially forces the re-mix for the O system and compares it with the served diagram.

Note: Should MP building use strict/fuzzy matching?

Fuzzy matching was written for these cases where strict matching (based on structure matching) fails: pymatgen's docstring: different-functional relaxations of these elements "frequently fail to structure match ... even though they come from the same original material". It is pymatgen's default, and what MP's docs describer for re-mixing locally (phase diagrams page), and MP's builder seems to have used it before: the mixed-thermo MP entries in pymatgen's
entries_thermo_type_GGA_GGA_U_R2SCAN.json test file (added in Feb 2023) record fuzzy_matching: true -- but doesn't currently.

Without fuzzy matching, the Cl2, F2, Br and I ground states (mp-22848, mp-1525632, mp-23154, mp-23153)
don't structure-match their own r2SCAN calculations (r2SCAN contracts these molecular crystals by 16-21% in volume),
and the O2 ground state mp-12957 has no r2SCAN calculation (fuzzy matching pairs it with the r2SCAN-only
mp-1524462, which looks like the same alpha-O2 structure with a slightly different cell/packing). So all of MP's diagrams with O, F, Cl, Br or I are GGA(+U)-anchored, while the fuzzy re-mix anchors some of them to r2SCAN instead (energy above hull differences up to 0.25 eV/atom, e.g. Lu-Na-O-Si), and places the gas-phase entries differently in the others (up to 0.16 eV/atom in Be-Ge-Nd-O and Ca-La-Mg-O-Ta). Systems with N or H differ too, even though N2 and H2 do structure-match: fuzzy matching collapses all gas-phase polymorphs with the same space group and number of sites into one entry per
functional, dropping the rest.

With strict matching, none of MP's 39,254 phase diagrams with O, F, Cl, Br or I is r2SCAN-anchored (vs 35% of the 71,636 without these elements, and 56% of those with only N/H), and r2SCAN-only materials at new compositions are dropped from them: e.g. only 124 of the 19,184 GNoMe materials in these systems appear in any mixed diagram. With fuzzy matching, 8,942 of the 39,254 systems (23%) with O, F, Cl, Br or I would become r2SCAN-anchored (and GNoMe materials would go from 124 to 6,392 appearances in mixed hulls, 33%). The mixing scheme paper's remedy for such matching failures is r2SCAN single-point calculations of the GGA(+U) ground-state structures (Kingsbury et al., npj Comput. Mater. 8, 195 (2022)), which would let these few ground states (mp-12957, mp-1525632, mp-22848, mp-23154, mp-23153) match strictly.

Just raising this point, as I'm not sure what the preferred approach in general of the MP would be.
Either way, the strict/fuzzy-matching choice of MP building should ideally match that of mp-api's local re-mixing!

Checklist

  • Google format doc strings added. Check with ruff.
  • Type annotations included. Check with mypy.
  • Tests added for new features/fixes.
  • If applicable, new classes/functions/modules have duecredit @due.dcite decorators to reference relevant papers by DOI (example)

@kavanase

Copy link
Copy Markdown
Contributor Author

This PR also bumps the pymatgen requirement, so that it uses the fixed MaterialsProjectDFTMixingScheme from materialsproject/pymatgen#4704 (released in 2026.9.23) – e.g. it keeps rutile TiO2 (mp-2657) as both its GGA and r2SCAN entries.

@kavanase

Copy link
Copy Markdown
Contributor Author

(Tests fail with the same unrelated test_mock failure)

@kavanase

Copy link
Copy Markdown
Contributor Author

Note:
Separately from this mixing fix: get_pourbaix_entries can also silently use the wrong O2/H2O reference energies with current pymatgen releases (not fixed by the other PRs I just created).

This is fixed in materialsproject/pymatgen#4754 (flagged in materialsproject/pymatgen#4747), which merged after v2026.10.24, so no release includes it yet. Once the next pymatgen release is out, it might be worth mp-api raising its pymatgen floor to that release, on top of the >=2026.9.23 here, so this bug is avoided? Just to flag and track here.

Explanation of the issue from Claude:
In pymatgen ≤ v2026.10.24, MaterialsProjectAqueousCompatibility is a monty @cached_class, so constructing it with the same arguments returns the same instance while anything still references it. process_entries stores the O2/H2O energies it infers on that instance, and only infers them while they're unset. get_pourbaix_entries builds MaterialsProjectAqueousCompatibility(solid_compat=MaterialsProject2020Compatibility()), and _private_copy starts from that instance's __dict__. So if the caller holds an equal instance that has already processed other entries, the Pourbaix entries are corrected with that instance's energies, with no warning.

E.g. after processing MP's default Fe-O-H entries with such an instance (inferred H2O energy −5.125 eV/atom, vs −5.193 from get_pourbaix_entries("Fe")'s own GGA/GGA+U entries), FeOOH replaces Fe2O3 at 395 of 1,189 (pH, V) points of the Fe Pourbaix diagram.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant