Skip to content

Commit 0dcbe0e

Browse files
peikaishyuep
andauthored
Make run_type in mixing scheme consistent with entries (#4255)
* Test that entries with run_type 'r2SCAN' would not be ignored; 'r2SCAN' as an input run_type is not case-sensitive. * Fix to ensure r2SCAN entries would not be ignored in mixing scheme correction --------- Co-authored-by: Shyue Ping Ong <[email protected]>
1 parent a4ee88c commit 0dcbe0e

File tree

2 files changed

+33
-23
lines changed

2 files changed

+33
-23
lines changed

src/pymatgen/entries/mixing_scheme.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ class MaterialsProjectDFTMixingScheme(Compatibility):
4040
may lead to unexpected results.
4141
4242
This is the scheme used by the Materials Project to generate Phase Diagrams containing
43-
a mixture of GGA(+U) and R2SCAN calculations. However in principle it can be used to
43+
a mixture of GGA(+U) and r2SCAN calculations. However in principle it can be used to
4444
mix energies from any two functionals.
4545
"""
4646

4747
def __init__(
4848
self,
4949
structure_matcher: StructureMatcher | None = None,
5050
run_type_1: str = "GGA(+U)",
51-
run_type_2: str = "R2SCAN",
51+
run_type_2: str = "r2SCAN",
5252
compat_1: (Compatibility | None) = MaterialsProject2020Compatibility(), # noqa: B008
5353
compat_2: Compatibility | None = None,
5454
fuzzy_matching: bool = True,
@@ -64,7 +64,7 @@ def __init__(
6464
run_type_1: The first DFT run_type. Typically this is the majority or run type or
6565
the "base case" onto which the other calculations are referenced. Valid choices
6666
are any run_type recognized by Vasprun.run_type, such as "LDA", "GGA", "GGA+U",
67-
"PBEsol", "SCAN", or "R2SCAN". The class will ignore any entries that have a
67+
"PBEsol", "SCAN", or "r2SCAN". The class will ignore any entries that have a
6868
run_type different than run_type_1 or run_type_2.
6969
7070
The list of run_type_1 entries provided to process_entries MUST form a complete
@@ -103,8 +103,10 @@ def __init__(
103103
raise ValueError(f"run_type_1={run_type_2=}. The mixing scheme is meaningless unless run_types different")
104104
self.run_type_1 = run_type_1
105105
self.run_type_2 = run_type_2
106-
self.valid_rtypes_1 = ["GGA", "GGA+U"] if self.run_type_1 == "GGA(+U)" else [self.run_type_1]
107-
self.valid_rtypes_2 = ["GGA", "GGA+U"] if self.run_type_2 == "GGA(+U)" else [self.run_type_2]
106+
"""Valid run_type, allowing for archive R2SCAN entries"""
107+
valid_rtype_dict = {"GGA(+U)": ["GGA", "GGA+U"], "R2SCAN": ["r2SCAN", "R2SCAN"]}
108+
self.valid_rtypes_1 = valid_rtype_dict.get(run_type_1.upper(), [self.run_type_1])
109+
self.valid_rtypes_2 = valid_rtype_dict.get(run_type_2.upper(), [self.run_type_2])
108110

109111
self.compat_1 = compat_1
110112
self.compat_2 = compat_2
@@ -253,7 +255,7 @@ def process_entries(
253255

254256
def get_adjustments(self, entry, mixing_state_data: pd.DataFrame | None = None):
255257
"""Get the corrections applied to a particular entry. Note that get_adjustments is not
256-
intended to be called directly in the R2SCAN mixing scheme. Call process_entries instead,
258+
intended to be called directly in the r2SCAN mixing scheme. Call process_entries instead,
257259
and it will pass the required arguments to get_adjustments.
258260
259261
Args:
@@ -326,7 +328,7 @@ def get_adjustments(self, entry, mixing_state_data: pd.DataFrame | None = None):
326328
# For run_type_2 entries, there is no correction
327329
return adjustments
328330

329-
# Discard GGA ground states whose structures already exist in R2SCAN.
331+
# Discard GGA ground states whose structures already exist in r2SCAN.
330332
df_slice = mixing_state_data[(mixing_state_data["entry_id_1"] == entry.entry_id)]
331333

332334
if df_slice["entry_id_2"].notna().item():
@@ -344,8 +346,8 @@ def get_adjustments(self, entry, mixing_state_data: pd.DataFrame | None = None):
344346
f"because there is a matching {self.run_type_2} material."
345347
)
346348

347-
# If a GGA is not present in R2SCAN, correct its energy to give the same
348-
# e_above_hull on the R2SCAN hull that it would have on the GGA hull
349+
# If a GGA is not present in r2SCAN, correct its energy to give the same
350+
# e_above_hull on the r2SCAN hull that it would have on the GGA hull
349351
hull_energy_1 = df_slice["hull_energy_1"].iloc[0]
350352
hull_energy_2 = df_slice["hull_energy_2"].iloc[0]
351353
correction = (hull_energy_2 - hull_energy_1) * entry.composition.num_atoms

tests/entries/test_mixing_scheme.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,10 +1844,10 @@ def test_fuzzy_matching(self, ms_complete):
18441844
with pytest.raises(CompatibilityError, match="ground state"):
18451845
compat.get_adjustments(entry, mixing_state)
18461846
elif entry.entry_id == "r2scan-3":
1847-
with pytest.raises(CompatibilityError, match="and no R2SCAN ground state"):
1847+
with pytest.raises(CompatibilityError, match="and no r2SCAN ground state"):
18481848
compat.get_adjustments(entry, mixing_state)
18491849
elif entry.entry_id in ["gga-2", "gga-5", "gga-6", "gga-7"]:
1850-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
1850+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
18511851
compat.get_adjustments(entry, mixing_state)
18521852
elif entry.parameters["run_type"] == "GGA":
18531853
assert not compat.get_adjustments(entry, mixing_state)
@@ -1861,7 +1861,7 @@ def test_fuzzy_matching(self, ms_complete):
18611861
if entry.parameters["run_type"] == "GGA":
18621862
assert entry.correction == 0
18631863
elif entry.entry_id in ["r2scan-2", "r2scan-7"]:
1864-
assert "Replace R2SCAN energy with GGA" in entry.energy_adjustments[0].description
1864+
assert "Replace r2SCAN energy with GGA" in entry.energy_adjustments[0].description
18651865
else:
18661866
assert "onto the GGA(+U) hull" in entry.energy_adjustments[0].description
18671867

@@ -1906,7 +1906,7 @@ def test_compat_args(self, ms_complete):
19061906
):
19071907
compat.get_adjustments(entry, state_data)
19081908
else:
1909-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
1909+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
19101910
assert not compat.get_adjustments(entry, state_data)
19111911

19121912
for entry in ms_complete.scan_entries:
@@ -1952,7 +1952,7 @@ def test_multiple_matching_structures(self, mixing_scheme_no_compat, ms_complete
19521952
):
19531953
mixing_scheme_no_compat.get_adjustments(entry, ms_complete_duplicate_structs.state_data)
19541954
else:
1955-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
1955+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
19561956
mixing_scheme_no_compat.get_adjustments(entry, ms_complete_duplicate_structs.state_data)
19571957

19581958
# process_entries should discard all GGA entries and return all R2SCAN
@@ -1996,10 +1996,10 @@ def test_alternate_structure_matcher(self, ms_complete):
19961996
):
19971997
compat.get_adjustments(entry, ms_complete.state_data)
19981998
else:
1999-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
1999+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
20002000
compat.get_adjustments(entry, ms_complete.state_data)
20012001

2002-
# process_entries should discard all GGA entries except gga-6 and return all R2SCAN
2002+
# process_entries should discard all GGA entries except gga-6 and return all r2SCAN
20032003
# entries unmodified. gga-6 should be corrected to the R2SCAN hull
20042004
entries = compat.process_entries(ms_complete.all_entries)
20052005
assert len(entries) == 8
@@ -2014,6 +2014,14 @@ def test_processing_entries_inplace(self):
20142014
MaterialsProjectDFTMixingScheme().process_entries(entries, inplace=False)
20152015
assert all(e.correction == e_copy.correction for e, e_copy in zip(entries, entries_copy, strict=True))
20162016

2017+
def test_run_type_variations(self):
2018+
"""Test that entries with run_type 'r2SCAN' would not be ignored in mixing scheme correction."""
2019+
scheme = MaterialsProjectDFTMixingScheme(run_type_1="GGA(+U)", run_type_2="r2SCAN")
2020+
assert scheme.valid_rtypes_2 == ["r2SCAN", "R2SCAN"]
2021+
"""Test that 'r2SCAN' as an input run_type is not case-sensitive."""
2022+
scheme = MaterialsProjectDFTMixingScheme(run_type_1="GGA(+U)", run_type_2="R2SCAN")
2023+
assert scheme.valid_rtypes_2 == ["r2SCAN", "R2SCAN"]
2024+
20172025
def test_check_potcar(self, ms_complete):
20182026
"""Entries with invalid or missing POTCAR raise error by default but should be ignored if
20192027
check_potcar=False in MaterialsProjectDFTMixingScheme.
@@ -2058,7 +2066,7 @@ def test_state_complete_entries(self, mixing_scheme_no_compat, ms_complete):
20582066
):
20592067
mixing_scheme_no_compat.get_adjustments(entry, ms_complete.state_data)
20602068
else:
2061-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
2069+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
20622070
mixing_scheme_no_compat.get_adjustments(entry, ms_complete.state_data)
20632071

20642072
# process_entries should discard all GGA entries and return all R2SCAN
@@ -2153,7 +2161,7 @@ def test_state_gga_1_scan_plus_novel(self, mixing_scheme_no_compat, ms_gga_1_sca
21532161
assert mixing_scheme_no_compat.get_adjustments(entry, ms_gga_1_scan_novel.state_data) == []
21542162

21552163
for entry in ms_gga_1_scan_novel.scan_entries:
2156-
with pytest.raises(CompatibilityError, match="no R2SCAN ground states at this composition"):
2164+
with pytest.raises(CompatibilityError, match="no r2SCAN ground states at this composition"):
21572165
mixing_scheme_no_compat.get_adjustments(entry, ms_gga_1_scan_novel.state_data)
21582166

21592167
entries = mixing_scheme_no_compat.process_entries(ms_gga_1_scan_novel.all_entries)
@@ -2190,7 +2198,7 @@ def test_state_gga_2_scan_same(self, mixing_scheme_no_compat, ms_gga_2_scan_same
21902198
):
21912199
mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_same.state_data)
21922200
elif entry.entry_id == "gga-6":
2193-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
2201+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
21942202
mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_same.state_data)
21952203
else:
21962204
assert mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_same.state_data) == []
@@ -2239,7 +2247,7 @@ def test_state_gga_2_scan_diff_match(self, mixing_scheme_no_compat, ms_gga_2_sca
22392247
):
22402248
mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_diff_match.state_data)
22412249
elif entry.entry_id == "gga-7":
2242-
with pytest.raises(CompatibilityError, match="there is a matching R2SCAN"):
2250+
with pytest.raises(CompatibilityError, match="there is a matching r2SCAN"):
22432251
mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_diff_match.state_data)
22442252
else:
22452253
assert mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_diff_match.state_data) == []
@@ -2273,7 +2281,7 @@ def test_state_gga_2_scan_diff_nomatch(self, mixing_scheme_no_compat, ms_gga_2_s
22732281
if entry.entry_id == "r2scan-8":
22742282
# there is no matching GGA structure for r2scan-8, so there's no way
22752283
# to adjust its energy onto the GGA hull.
2276-
with pytest.raises(CompatibilityError, match="entry and no R2SCAN ground state"):
2284+
with pytest.raises(CompatibilityError, match="entry and no r2SCAN ground state"):
22772285
mixing_scheme_no_compat.get_adjustments(entry, ms_gga_2_scan_diff_no_match.state_data)
22782286
elif entry.entry_id == "r2scan-4":
22792287
# r2scan-4 energy is -7 eV/atom. Needs to be adjusted to -6 eV/atom (3 atoms)
@@ -2366,7 +2374,7 @@ def test_state_all_gga_scan_gs(self, mixing_scheme_no_compat, ms_all_gga_scan_gs
23662374

23672375
for entry in entries:
23682376
if entry.parameters["run_type"] == "GGA":
2369-
assert "onto the R2SCAN hull" in entry.energy_adjustments[0].description
2377+
assert "onto the r2SCAN hull" in entry.energy_adjustments[0].description
23702378
assert_allclose(pd_mixed.get_e_above_hull(entry), gga_hull_e[entry.entry_id])
23712379
else:
23722380
assert entry.correction == 0
@@ -2408,7 +2416,7 @@ def test_state_novel_scan_comp(self, mixing_scheme_no_compat, ms_all_gga_scan_gs
24082416

24092417
for entry in entries:
24102418
if entry.parameters["run_type"] == "GGA":
2411-
assert "onto the R2SCAN hull" in entry.energy_adjustments[0].description
2419+
assert "onto the r2SCAN hull" in entry.energy_adjustments[0].description
24122420
assert_allclose(pd_mixed.get_e_above_hull(entry), gga_hull_e[entry.entry_id])
24132421
else:
24142422
assert entry.correction == 0

0 commit comments

Comments
 (0)