From 3ac873830954c1e969444a8c58b1c4b85bae8442 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Tue, 24 Jun 2025 10:50:31 -0700 Subject: [PATCH 1/4] ensure that MPDataDoc picks up on model-excluded fields --- mp_api/client/core/utils.py | 13 +++++++++---- mp_api/client/routes/materials/phonon.py | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/mp_api/client/core/utils.py b/mp_api/client/core/utils.py index fb25221ff..1bba91954 100644 --- a/mp_api/client/core/utils.py +++ b/mp_api/client/core/utils.py @@ -71,9 +71,7 @@ def api_sanitize( for model in models: model_fields_to_leave = {f[1] for f in fields_tuples if model.__name__ == f[0]} - for name in model.model_fields: - field = model.model_fields[name] - field_json_extra = field.json_schema_extra + for name, field in model.model_fields.items(): field_type = field.annotation if field_type is not None and allow_dict_msonable: @@ -88,7 +86,14 @@ def api_sanitize( new_field = FieldInfo.from_annotated_attribute( Optional[field_type], None ) - new_field.json_schema_extra = field_json_extra or {} + + for attr in ( + "json_schema_extra", + "exclude", + ): + if (val := getattr(field, attr)) is not None: + setattr(new_field, attr, val) + model.model_fields[name] = new_field model.model_rebuild(force=True) diff --git a/mp_api/client/routes/materials/phonon.py b/mp_api/client/routes/materials/phonon.py index f9d9ac9a5..4ce52950e 100644 --- a/mp_api/client/routes/materials/phonon.py +++ b/mp_api/client/routes/materials/phonon.py @@ -1,11 +1,11 @@ from __future__ import annotations import json -import numpy as np from collections import defaultdict +import numpy as np +from emmet.core.phonon import PhononBS, PhononBSDOSDoc, PhononDOS from monty.json import MontyDecoder -from emmet.core.phonon import PhononBSDOSDoc, PhononBS, PhononDOS from mp_api.client.core import BaseRester, MPRestError from mp_api.client.core.utils import validate_ids From df5e52e08fbb1c016b5c4b81fff7372ac64f6547 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Fri, 27 Jun 2025 13:51:40 -0700 Subject: [PATCH 2/4] add user_entries to pourbaix --- mp_api/client/mprester.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/mp_api/client/mprester.py b/mp_api/client/mprester.py index 6a000c4e0..5d47d3ebb 100644 --- a/mp_api/client/mprester.py +++ b/mp_api/client/mprester.py @@ -783,7 +783,8 @@ def get_entries( def get_pourbaix_entries( self, - chemsys: str | list, + chemsys: str | list | None = None, + user_entries: list[ComputedEntry | ComputedStructureEntry] | None = None, solid_compat="MaterialsProject2020Compatibility", use_gibbs: Literal[300] | None = None, ): @@ -791,9 +792,14 @@ def get_pourbaix_entries( a Pourbaix diagram from the rest interface. Args: - chemsys (str or [str]): Chemical system string comprising element + chemsys (str or [str] or None): Chemical system string comprising element symbols separated by dashes, e.g., "Li-Fe-O" or List of element symbols, e.g., ["Li", "Fe", "O"]. + Does not need to be set if user_entries is set. + user_entries : list of Computed(Structure)Entry or None + Can be specified instead of chemsys to allow for adding extra + calculation data to the Pourbaix Diagram. + If this is set, the chemsys will be inferred from user_entries. solid_compat: Compatibility scheme used to pre-process solid DFT energies prior to applying aqueous energy adjustments. May be passed as a class (e.g. MaterialsProject2020Compatibility) or an instance @@ -816,6 +822,23 @@ def get_pourbaix_entries( ) from pymatgen.entries.computed_entries import ComputedEntry + if not chemsys and not user_entries: + raise ValueError( + "You must supply either a chemical system, or a list of " + "Computed(Structure)Entry objects to determine the chemical " + "system from!" + ) + elif chemsys and user_entries: + warnings.warn( + "You have set both `chemsys` and `user_entries`; the " + "chemical system will be determined from `user_entries`." + ) + + if user_entries: + chemsys = [ + ele.name for ele in set([entry.elements for entry in user_entries]) + ] + if solid_compat == "MaterialsProjectCompatibility": solid_compat = MaterialsProjectCompatibility() elif solid_compat == "MaterialsProject2020Compatibility": @@ -855,6 +878,8 @@ def get_pourbaix_entries( list([str(e) for e in ion_ref_elts] + ["O", "H"]), # use_gibbs=use_gibbs ) + if user_entries: + ion_ref_entries += user_entries # suppress the warning about supplying the required energies; they will be calculated from the # entries we get from MPRester From 2ecdb754267b908cf23970688b75581667d2f7da Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Fri, 27 Jun 2025 14:12:02 -0700 Subject: [PATCH 3/4] add feature to add user computed entries to pourbaix --- mp_api/client/mprester.py | 61 +++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/mp_api/client/mprester.py b/mp_api/client/mprester.py index 5d47d3ebb..4e15ec32d 100644 --- a/mp_api/client/mprester.py +++ b/mp_api/client/mprester.py @@ -783,8 +783,7 @@ def get_entries( def get_pourbaix_entries( self, - chemsys: str | list | None = None, - user_entries: list[ComputedEntry | ComputedStructureEntry] | None = None, + chemsys: str | list[str] | list[ComputedEntry | ComputedStructureEntry], solid_compat="MaterialsProject2020Compatibility", use_gibbs: Literal[300] | None = None, ): @@ -792,14 +791,14 @@ def get_pourbaix_entries( a Pourbaix diagram from the rest interface. Args: - chemsys (str or [str] or None): Chemical system string comprising element + chemsys (str or [str] or Computed(Structure)Entry): + Chemical system string comprising element symbols separated by dashes, e.g., "Li-Fe-O" or List of element symbols, e.g., ["Li", "Fe", "O"]. - Does not need to be set if user_entries is set. - user_entries : list of Computed(Structure)Entry or None - Can be specified instead of chemsys to allow for adding extra - calculation data to the Pourbaix Diagram. - If this is set, the chemsys will be inferred from user_entries. + + Can also be a list of Computed(Structure)Entry objects to allow + for adding extra calculation data to the Pourbaix Diagram. + If this is set, the chemsys will be inferred from the entries. solid_compat: Compatibility scheme used to pre-process solid DFT energies prior to applying aqueous energy adjustments. May be passed as a class (e.g. MaterialsProject2020Compatibility) or an instance @@ -822,22 +821,26 @@ def get_pourbaix_entries( ) from pymatgen.entries.computed_entries import ComputedEntry - if not chemsys and not user_entries: - raise ValueError( - "You must supply either a chemical system, or a list of " - "Computed(Structure)Entry objects to determine the chemical " - "system from!" - ) - elif chemsys and user_entries: - warnings.warn( - "You have set both `chemsys` and `user_entries`; the " - "chemical system will be determined from `user_entries`." - ) + thermo_types = ["GGA_GGA+U"] + user_entries: list[ComputedEntry | ComputedStructureEntry] = [] + if isinstance(chemsys, list) and all( + isinstance(v, ComputedEntry | ComputedStructureEntry) for v in chemsys + ): + user_entries = [ce.copy() for ce in chemsys] - if user_entries: - chemsys = [ - ele.name for ele in set([entry.elements for entry in user_entries]) - ] + elements = set() + for entry in user_entries: + elements.update(entry.elements) + chemsys = [ele.name for ele in elements] + + user_run_types = set( + [ + entry.parameters.get("run_type", "unknown").lower() + for entry in user_entries + ] + ) + if any("r2scan" in rt for rt in user_run_types): + thermo_types = ["GGA_GGA+U_R2SCAN"] if solid_compat == "MaterialsProjectCompatibility": solid_compat = MaterialsProjectCompatibility() @@ -874,12 +877,14 @@ def get_pourbaix_entries( # TODO - would be great if the commented line below would work # However for some reason you cannot process GibbsComputedStructureEntry with # MaterialsProjectAqueousCompatibility - ion_ref_entries = self.get_entries_in_chemsys( - list([str(e) for e in ion_ref_elts] + ["O", "H"]), - # use_gibbs=use_gibbs + ion_ref_entries = ( + self.get_entries_in_chemsys( + list([str(e) for e in ion_ref_elts] + ["O", "H"]), + additional_criteria={"thermo_types": thermo_types} + # use_gibbs=use_gibbs + ) + + user_entries ) - if user_entries: - ion_ref_entries += user_entries # suppress the warning about supplying the required energies; they will be calculated from the # entries we get from MPRester From b622cbdd5d7e0d0f22cf422fe09fb280470d1c73 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Fri, 27 Jun 2025 14:16:26 -0700 Subject: [PATCH 4/4] undo unrelated changes --- mp_api/client/mprester.py | 40 +++++---------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/mp_api/client/mprester.py b/mp_api/client/mprester.py index 4e15ec32d..6a000c4e0 100644 --- a/mp_api/client/mprester.py +++ b/mp_api/client/mprester.py @@ -783,7 +783,7 @@ def get_entries( def get_pourbaix_entries( self, - chemsys: str | list[str] | list[ComputedEntry | ComputedStructureEntry], + chemsys: str | list, solid_compat="MaterialsProject2020Compatibility", use_gibbs: Literal[300] | None = None, ): @@ -791,14 +791,9 @@ def get_pourbaix_entries( a Pourbaix diagram from the rest interface. Args: - chemsys (str or [str] or Computed(Structure)Entry): - Chemical system string comprising element + chemsys (str or [str]): Chemical system string comprising element symbols separated by dashes, e.g., "Li-Fe-O" or List of element symbols, e.g., ["Li", "Fe", "O"]. - - Can also be a list of Computed(Structure)Entry objects to allow - for adding extra calculation data to the Pourbaix Diagram. - If this is set, the chemsys will be inferred from the entries. solid_compat: Compatibility scheme used to pre-process solid DFT energies prior to applying aqueous energy adjustments. May be passed as a class (e.g. MaterialsProject2020Compatibility) or an instance @@ -821,27 +816,6 @@ def get_pourbaix_entries( ) from pymatgen.entries.computed_entries import ComputedEntry - thermo_types = ["GGA_GGA+U"] - user_entries: list[ComputedEntry | ComputedStructureEntry] = [] - if isinstance(chemsys, list) and all( - isinstance(v, ComputedEntry | ComputedStructureEntry) for v in chemsys - ): - user_entries = [ce.copy() for ce in chemsys] - - elements = set() - for entry in user_entries: - elements.update(entry.elements) - chemsys = [ele.name for ele in elements] - - user_run_types = set( - [ - entry.parameters.get("run_type", "unknown").lower() - for entry in user_entries - ] - ) - if any("r2scan" in rt for rt in user_run_types): - thermo_types = ["GGA_GGA+U_R2SCAN"] - if solid_compat == "MaterialsProjectCompatibility": solid_compat = MaterialsProjectCompatibility() elif solid_compat == "MaterialsProject2020Compatibility": @@ -877,13 +851,9 @@ def get_pourbaix_entries( # TODO - would be great if the commented line below would work # However for some reason you cannot process GibbsComputedStructureEntry with # MaterialsProjectAqueousCompatibility - ion_ref_entries = ( - self.get_entries_in_chemsys( - list([str(e) for e in ion_ref_elts] + ["O", "H"]), - additional_criteria={"thermo_types": thermo_types} - # use_gibbs=use_gibbs - ) - + user_entries + ion_ref_entries = self.get_entries_in_chemsys( + list([str(e) for e in ion_ref_elts] + ["O", "H"]), + # use_gibbs=use_gibbs ) # suppress the warning about supplying the required energies; they will be calculated from the