Skip to content

Commit ba938fa

Browse files
resolve merge conflicts
2 parents ac72351 + b9f13f0 commit ba938fa

22 files changed

+4928
-366
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
maggma Copyright (c) 2017, The Regents of the University of
1+
Copyright (c) 2017, The Regents of the University of
22
California, through Lawrence Berkeley National Laboratory (subject
33
to receipt of any required approvals from the U.S. Dept. of Energy).
44
All rights reserved.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"mu-mu_0K": [0.0, -0.15766752295964667, -0.35716109254410505, -0.4633062592086276, -0.5684747265209704, -0.5725679617587883, -0.6845046823072302, -0.7988365855732343, -0.9153600136580551, -1.0338982557640533, -1.2764843857253183, -1.5256155519745132, -1.7804988878829335, -2.0404658980577506, -2.304982822611666, -2.5736050343764276, -2.8459749660488933, -3.1217816895393757, -3.4007764623761245, -3.6827416348963578, -3.9674927231001944, -4.254892918628172, -4.544805413120827, -4.837112053904078, -5.13170297971957, -5.428488021421295, -5.7273687184475115, -6.028299468011739, -6.3311932102488635, -6.635957703158943, -6.9425981288768055, -7.2509807883238855, -7.561092207949628, -7.872879529978791, -8.186271240950743, -8.501236248056516, -8.817722729947826, -9.135721358781987, -9.4551295282894, -9.775953457031855, -10.098137177953216, -10.421667217502918, -10.746499009321454, -11.072592132757157, -11.399910312866226, -11.728421420412731, -12.058141001801161, -12.389003762132672, -12.720990009294923, -13.05408419688343, -13.388322599841986, -13.723603648327966, -14.059921123779567, -14.397323738264184, -14.735764852568353, -15.075250685253877, -15.415683812185982, -15.757182386038748, -16.099653128385278, -16.44311262205701, -16.787639635503336, -17.13308492324121, -17.47953139942788, -17.826947971254377, -18.175365731529666], "temperature": [0.0, 100.0, 200.0, 250.0, 298.15, 300.0, 350.0, 400.0, 450.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 1100.0, 1200.0, 1300.0, 1400.0, 1500.0, 1600.0, 1700.0, 1800.0, 1900.0, 2000.0, 2100.0, 2200.0, 2300.0, 2400.0, 2500.0, 2600.0, 2700.0, 2800.0, 2900.0, 3000.0, 3100.0, 3200.0, 3300.0, 3400.0, 3500.0, 3600.0, 3700.0, 3800.0, 3900.0, 4000.0, 4100.0, 4200.0, 4300.0, 4400.0, 4500.0, 4600.0, 4700.0, 4800.0, 4900.0, 5000.0, 5100.0, 5200.0, 5300.0, 5400.0, 5500.0, 5600.0, 5700.0, 5800.0, 5900.0, 6000.0]}

mp_api/client/core/client.py

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import inspect
99
import itertools
10-
import json
1110
import os
1211
import platform
1312
import sys
@@ -30,9 +29,7 @@
3029
from urllib.parse import quote, urljoin
3130

3231
import requests
33-
from bson import json_util
3432
from emmet.core.utils import jsanitize
35-
from monty.json import MontyDecoder
3633
from pydantic import BaseModel, create_model
3734
from requests.adapters import HTTPAdapter
3835
from requests.exceptions import RequestException
@@ -41,7 +38,7 @@
4138
from urllib3.util.retry import Retry
4239

4340
from mp_api.client.core.settings import MAPIClientSettings
44-
from mp_api.client.core.utils import validate_ids
41+
from mp_api.client.core.utils import load_json, validate_ids
4542

4643
try:
4744
import boto3
@@ -267,11 +264,7 @@ def _post_resource(
267264
response = self.session.post(url, json=payload, verify=True, params=params)
268265

269266
if response.status_code == 200:
270-
if self.monty_decode:
271-
data = json.loads(response.text, cls=MontyDecoder)
272-
else:
273-
data = json.loads(response.text)
274-
267+
data = load_json(response.text, deser=self.monty_decode)
275268
if self.document_model and use_document_model:
276269
if isinstance(data["data"], dict):
277270
data["data"] = self.document_model.model_validate(data["data"]) # type: ignore
@@ -284,7 +277,7 @@ def _post_resource(
284277

285278
else:
286279
try:
287-
data = json.loads(response.text)["detail"]
280+
data = load_json(response.text)["detail"]
288281
except (JSONDecodeError, KeyError):
289282
data = f"Response {response.text}"
290283
if isinstance(data, str):
@@ -339,11 +332,7 @@ def _patch_resource(
339332
response = self.session.patch(url, json=payload, verify=True, params=params)
340333

341334
if response.status_code == 200:
342-
if self.monty_decode:
343-
data = json.loads(response.text, cls=MontyDecoder)
344-
else:
345-
data = json.loads(response.text)
346-
335+
data = load_json(response.text, deser=self.monty_decode)
347336
if self.document_model and use_document_model:
348337
if isinstance(data["data"], dict):
349338
data["data"] = self.document_model.model_validate(data["data"]) # type: ignore
@@ -356,7 +345,7 @@ def _patch_resource(
356345

357346
else:
358347
try:
359-
data = json.loads(response.text)["detail"]
348+
data = load_json(response.text)["detail"]
360349
except (JSONDecodeError, KeyError):
361350
data = f"Response {response.text}"
362351
if isinstance(data, str):
@@ -381,18 +370,24 @@ def _query_open_data(
381370
self,
382371
bucket: str,
383372
key: str,
384-
decoder: Callable,
373+
decoder: Callable | None = None,
385374
) -> tuple[list[dict] | list[bytes], int]:
386375
"""Query and deserialize Materials Project AWS open data s3 buckets.
387376
388377
Args:
389378
bucket (str): Materials project bucket name
390379
key (str): Key for file including all prefixes
391-
decoder(Callable): Callable used to deserialize data
380+
decoder(Callable or None): Callable used to deserialize data.
381+
Defaults to mp_api.core.utils.load_json
392382
393383
Returns:
394384
dict: MontyDecoded data
395385
"""
386+
if not decoder:
387+
388+
def decoder(x):
389+
return load_json(x, deser=self.monty_decode)
390+
396391
file = open(
397392
f"s3://{bucket}/{key}",
398393
encoding="utf-8",
@@ -524,16 +519,11 @@ def _query_resource(
524519
"Ignoring `fields` argument: All fields are always included when no query is provided."
525520
)
526521

527-
decoder = (
528-
MontyDecoder().decode if self.monty_decode else json_util.loads
529-
)
530-
531522
# Multithreaded function inputs
532523
s3_params_list = {
533524
key: {
534525
"bucket": bucket,
535526
"key": key,
536-
"decoder": decoder,
537527
}
538528
for key in keys
539529
}
@@ -1010,11 +1000,7 @@ def _submit_request_and_process(
10101000
)
10111001

10121002
if response.status_code == 200:
1013-
if self.monty_decode:
1014-
data = json.loads(response.text, cls=MontyDecoder)
1015-
else:
1016-
data = json.loads(response.text)
1017-
1003+
data = load_json(response.text, deser=self.monty_decode)
10181004
# other sub-urls may use different document models
10191005
# the client does not handle this in a particularly smart way currently
10201006
if self.document_model and use_document_model:
@@ -1026,7 +1012,7 @@ def _submit_request_and_process(
10261012

10271013
else:
10281014
try:
1029-
data = json.loads(response.text)["detail"]
1015+
data = load_json(response.text)["detail"]
10301016
except (JSONDecodeError, KeyError):
10311017
data = f"Response {response.text}"
10321018
if isinstance(data, str):

mp_api/client/core/utils.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
from __future__ import annotations
22

3+
from typing import TYPE_CHECKING
4+
5+
import orjson
36
from emmet.core.mpid_ext import validate_identifier
4-
from monty.json import MSONable
7+
from monty.json import MontyDecoder
58

69
from mp_api.client.core.settings import MAPIClientSettings
710

11+
if TYPE_CHECKING:
12+
from monty.json import MSONable
13+
14+
15+
def load_json(json_like: str | bytes, deser: bool = False, encoding: str = "utf-8"):
16+
"""Utility to load json in consistent manner."""
17+
data = orjson.loads(
18+
json_like if isinstance(json_like, bytes) else json_like.encode(encoding)
19+
)
20+
return MontyDecoder().process_decoded(data) if deser else data
21+
822

923
def validate_ids(id_list: list[str]):
1024
"""Function to validate material and task IDs.

mp_api/client/mprester.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from __future__ import annotations
22

33
import itertools
4-
import json
54
import os
65
import warnings
76
from functools import cache, lru_cache
8-
from json import loads
97
from typing import TYPE_CHECKING
108

119
from emmet.core.electronic_structure import BSPathType
@@ -14,7 +12,6 @@
1412
from emmet.core.tasks import TaskDoc
1513
from emmet.core.types.enums import ThermoType
1614
from emmet.core.vasp.calc_types import CalcType
17-
from monty.json import MontyDecoder
1815
from packaging import version
1916
from pymatgen.analysis.phase_diagram import PhaseDiagram
2017
from pymatgen.analysis.pourbaix_diagram import IonEntry
@@ -27,14 +24,15 @@
2724

2825
from mp_api.client.core import BaseRester, MPRestError
2926
from mp_api.client.core.settings import MAPIClientSettings
30-
from mp_api.client.core.utils import validate_ids
27+
from mp_api.client.core.utils import load_json, validate_ids
3128
from mp_api.client.routes import GeneralStoreRester, MessagesRester, UserSettingsRester
3229
from mp_api.client.routes.materials import (
3330
AbsorptionRester,
3431
AlloysRester,
3532
BandStructureRester,
3633
BondsRester,
3734
ChemenvRester,
35+
ConversionElectrodeRester,
3836
DielectricRester,
3937
DOIRester,
4038
DosRester,
@@ -99,6 +97,7 @@ class MPRester:
9997
robocrys: RobocrysRester
10098
synthesis: SynthesisRester
10199
insertion_electrodes: ElectrodeRester
100+
conversion_electrodes: ConversionElectrodeRester
102101
electronic_structure: ElectronicStructureRester
103102
electronic_structure_bandstructure: BandStructureRester
104103
electronic_structure_dos: DosRester
@@ -1338,11 +1337,10 @@ def get_charge_density_from_task_id(
13381337
Returns:
13391338
(Chgcar, (Chgcar, TaskDoc | dict), None): Pymatgen Chgcar object, or tuple with object and TaskDoc
13401339
"""
1341-
decoder = MontyDecoder().decode if self.monty_decode else json.loads
13421340
kwargs = dict(
13431341
bucket="materialsproject-parsed",
13441342
key=f"chgcars/{validate_ids([task_id])[0]}.json.gz",
1345-
decoder=decoder,
1343+
decoder=lambda x: load_json(x, deser=self.monty_decode),
13461344
)
13471345
chgcar = self.materials.tasks._query_open_data(**kwargs)[0]
13481346
if not chgcar:
@@ -1476,7 +1474,7 @@ def _check_nomad_exist(url) -> bool:
14761474
response = get(url=url)
14771475
if response.status_code != 200:
14781476
return False
1479-
content = loads(response.text)
1477+
content = load_json(response.text)
14801478
if content["pagination"]["total"] == 0:
14811479
return False
14821480
return True

mp_api/client/routes/materials/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .dielectric import DielectricRester
77
from .doi import DOIRester
88
from .elasticity import ElasticityRester
9-
from .electrodes import ElectrodeRester
9+
from .electrodes import ConversionElectrodeRester, ElectrodeRester
1010
from .electronic_structure import (
1111
BandStructureRester,
1212
DosRester,

mp_api/client/routes/materials/electrodes.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
from __future__ import annotations
22

3+
import warnings
34
from collections import defaultdict
45

5-
from emmet.core.electrode import InsertionElectrodeDoc
6+
from emmet.core.electrode import ConversionElectrodeDoc, InsertionElectrodeDoc
67
from pymatgen.core.periodic_table import Element
78

89
from mp_api.client.core import BaseRester
910
from mp_api.client.core.utils import validate_ids
1011

1112

12-
class ElectrodeRester(BaseRester[InsertionElectrodeDoc]):
13-
suffix = "materials/insertion_electrodes"
14-
document_model = InsertionElectrodeDoc # type: ignore
13+
class BaseElectrodeRester(BaseRester):
1514
primary_key = "battery_id"
15+
_exclude_search_fields: list[str] | None = None
1616

1717
def search( # pragma: ignore
1818
self,
@@ -38,7 +38,7 @@ def search( # pragma: ignore
3838
chunk_size: int = 1000,
3939
all_fields: bool = True,
4040
fields: list[str] | None = None,
41-
) -> list[InsertionElectrodeDoc] | list[dict]:
41+
) -> list[InsertionElectrodeDoc | ConversionElectrodeDoc] | list[dict]:
4242
"""Query using a variety of search criteria.
4343
4444
Arguments:
@@ -74,11 +74,11 @@ def search( # pragma: ignore
7474
num_chunks (int): Maximum number of chunks of data to yield. None will yield all possible.
7575
chunk_size (int): Number of data entries per chunk.
7676
all_fields (bool): Whether to return all fields in the document. Defaults to True.
77-
fields (List[str]): List of fields in InsertionElectrodeDoc to return data for.
77+
fields (List[str]): List of fields in InsertionElectrodeDoc or ConversionElectrodeDoc to return data for.
7878
Default is battery_id and last_updated if all_fields is False.
7979
8080
Returns:
81-
([InsertionElectrodeDoc], [dict]) List of insertion electrode documents or dictionaries.
81+
([InsertionElectrodeDoc or ConversionElectrodeDoc], [dict]) List of insertion/conversion electrode documents or dictionaries.
8282
"""
8383
query_params = defaultdict(dict) # type: dict
8484

@@ -134,10 +134,43 @@ def search( # pragma: ignore
134134
else:
135135
query_params.update({param: value})
136136

137+
excluded_fields = self._exclude_search_fields or []
138+
ignored_fields = {
139+
entry
140+
for entry in excluded_fields
141+
if query_params.pop(entry, None) is not None
142+
}
143+
if ignored_fields:
144+
warnings.warn(
145+
f"Ignoring fields {', '.join(ignored_fields)} which are not valid options for {self.__class__.__name__}"
146+
)
147+
137148
query_params = {
138149
entry: query_params[entry]
139150
for entry in query_params
140151
if query_params[entry] is not None
141152
}
142153

143154
return super()._search(**query_params)
155+
156+
157+
class ElectrodeRester(BaseElectrodeRester):
158+
"""Search insertion electrode documents."""
159+
160+
suffix = "materials/insertion_electrodes"
161+
document_model = InsertionElectrodeDoc # type: ignore
162+
163+
164+
class ConversionElectrodeRester(BaseElectrodeRester):
165+
"""Search conversion electrode documents."""
166+
167+
suffix = "materials/conversion_electrodes"
168+
document_model = ConversionElectrodeDoc # type: ignore
169+
# TODO: formula, chemsys, and elements do not appear to work in the API
170+
_exclude_search_fields = [
171+
"formula",
172+
"chemsys",
173+
"elements",
174+
"stability_charge",
175+
"stability_discharge",
176+
]

mp_api/client/routes/materials/electronic_structure.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import json
43
import warnings
54
from collections import defaultdict
65

@@ -9,7 +8,6 @@
98
DOSProjectionType,
109
ElectronicStructureDoc,
1110
)
12-
from monty.json import MontyDecoder
1311
from pymatgen.analysis.magnetism.analyzer import Ordering
1412
from pymatgen.core.periodic_table import Element
1513
from pymatgen.electronic_structure.core import OrbitalType, Spin
@@ -234,11 +232,9 @@ def get_bandstructure_from_task_id(self, task_id: str):
234232
Returns:
235233
bandstructure (BandStructure): BandStructure or BandStructureSymmLine object
236234
"""
237-
decoder = MontyDecoder().decode if self.monty_decode else json.loads
238235
result = self._query_open_data(
239236
bucket="materialsproject-parsed",
240237
key=f"bandstructures/{validate_ids([task_id])[0]}.json.gz",
241-
decoder=decoder,
242238
)[0]
243239

244240
if result:
@@ -430,11 +426,9 @@ def get_dos_from_task_id(self, task_id: str):
430426
Returns:
431427
bandstructure (CompleteDos): CompleteDos object
432428
"""
433-
decoder = MontyDecoder().decode if self.monty_decode else json.loads
434429
result = self._query_open_data(
435430
bucket="materialsproject-parsed",
436431
key=f"dos/{validate_ids([task_id])[0]}.json.gz",
437-
decoder=decoder,
438432
)[0]
439433

440434
if result:

mp_api/client/routes/materials/materials.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
BandStructureRester,
1414
BondsRester,
1515
ChemenvRester,
16+
ConversionElectrodeRester,
1617
DielectricRester,
1718
DosRester,
1819
ElasticityRester,
@@ -62,6 +63,7 @@ class MaterialsRester(BaseRester[MaterialsDoc]):
6263
"robocrys",
6364
"synthesis",
6465
"insertion_electrodes",
66+
"conversion_electrodes",
6567
"electronic_structure",
6668
"electronic_structure_bandstructure",
6769
"electronic_structure_dos",
@@ -92,6 +94,7 @@ class MaterialsRester(BaseRester[MaterialsDoc]):
9294
robocrys: RobocrysRester
9395
synthesis: SynthesisRester
9496
insertion_electrodes: ElectrodeRester
97+
conversion_electrodes: ConversionElectrodeRester
9598
electronic_structure: ElectronicStructureRester
9699
electronic_structure_bandstructure: BandStructureRester
97100
electronic_structure_dos: DosRester

0 commit comments

Comments
 (0)