mattergraph-api 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from mattergraph import MaterialStore
7
+
8
+ from mattergraph_api.services.dataset_registry import dataset_registry
9
+ from mattergraph_api.services.demo_service import get_demo_store
10
+
11
+ _store: MaterialStore | None = None
12
+
13
+
14
+ def _resolve_demo_path() -> Path | None:
15
+ env = os.environ.get("MATTERGRAPH_DEMO_DATA")
16
+ if env:
17
+ return Path(env)
18
+ return None
19
+
20
+
21
+ def get_store() -> MaterialStore:
22
+ global _store # noqa: PLW0603
23
+ if _store is None:
24
+ p = _resolve_demo_path()
25
+ _store = MaterialStore.from_jsonl(p) if p is not None and p.is_file() else get_demo_store()
26
+ return _store
27
+
28
+
29
+ def resolve_store(dataset_id: str | None = None) -> MaterialStore:
30
+ """Return the bundled store or lazily materialize one selected local dataset."""
31
+ if dataset_id is None:
32
+ return get_store()
33
+ return dataset_registry.materialize(dataset_id)
34
+
35
+
36
+ def reset_store(s: MaterialStore) -> None:
37
+ global _store # noqa: PLW0603
38
+ _store = s
@@ -0,0 +1,195 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+ from mattergraph_api.services.demo_service import (
8
+ FIXTURE_DISCLAIMER,
9
+ FIXTURE_RELATIVE_PATH,
10
+ RUN_ID,
11
+ SLICE_NAME,
12
+ TARGET,
13
+ WORKFLOW_VERSION,
14
+ get_demo_dataset,
15
+ get_demo_manifest,
16
+ get_filtered_demo_dataset,
17
+ graph_summary,
18
+ )
19
+
20
+
21
+ class CandidateSliceSummary(BaseModel):
22
+ slice_id: str
23
+ slice_name: str
24
+ target: str
25
+ input_count: int
26
+ output_count: int
27
+ removed_count: int
28
+ filter_steps: list[dict[str, Any]] = Field(default_factory=list)
29
+ report: dict[str, Any] = Field(default_factory=dict)
30
+
31
+
32
+ class GraphPreview(BaseModel):
33
+ material_id: str
34
+ formula: str
35
+ node_count: int
36
+ edge_count: int
37
+ node_feature_shape: list[int]
38
+ edge_feature_shape: list[int]
39
+ global_features: dict[str, float | int]
40
+
41
+
42
+ class GraphExportSummary(BaseModel):
43
+ included_count: int
44
+ excluded_count: int
45
+ previews: list[GraphPreview] = Field(default_factory=list)
46
+
47
+
48
+ class BenchmarkSummary(BaseModel):
49
+ target: str
50
+ row_count: int
51
+ columns: list[str]
52
+
53
+
54
+ class BenchmarkPreviewRow(BaseModel):
55
+ material_id: str
56
+ formula: str
57
+ target: float | str | None
58
+ density: float | str | None = None
59
+ energy_above_hull: float | str | None = None
60
+ max_force: float | str | None = None
61
+ nsites: int | None = None
62
+ nelements: int | None = None
63
+
64
+
65
+ class WorkflowProvenance(BaseModel):
66
+ fixture_path: str
67
+ loader: str
68
+ workflow_version: str
69
+ run_id: str
70
+ fixture_kind: str = "checksummed_real_snapshot"
71
+ disclaimer: str
72
+ upstream_revision: str
73
+ hull_revision: str
74
+ license: str
75
+ citation_doi: str
76
+ snapshot_sha256: str
77
+ field_sources: dict[str, str]
78
+
79
+
80
+ class LeMaterialDemoWorkflowResponse(BaseModel):
81
+ workflow_id: str
82
+ source_dataset: str
83
+ source_subset: str
84
+ schema_report: dict[str, Any]
85
+ candidate_slice: CandidateSliceSummary
86
+ graph_export: GraphExportSummary
87
+ benchmark: BenchmarkSummary
88
+ benchmark_preview: list[BenchmarkPreviewRow]
89
+ provenance: WorkflowProvenance
90
+
91
+
92
+ def build_lematerial_demo_workflow() -> LeMaterialDemoWorkflowResponse:
93
+ dataset = get_demo_dataset()
94
+ manifest = get_demo_manifest()
95
+ filtered = get_filtered_demo_dataset()
96
+ candidate_slice = filtered.create_slice(SLICE_NAME, target=TARGET)
97
+ slice_report = candidate_slice.report()
98
+ graph_export = filtered.to_graphs()
99
+ benchmark_frame = filtered.to_benchmark_frame(target=TARGET)
100
+ store = filtered.to_material_store()
101
+ graph_previews = []
102
+ for material in store.materials:
103
+ if material.structure is None:
104
+ continue
105
+ summary = graph_summary(material)
106
+ graph_previews.append(
107
+ GraphPreview(
108
+ material_id=material.material_id,
109
+ formula=material.formula,
110
+ node_count=len(summary["nodes"]),
111
+ edge_count=summary["edge_count"],
112
+ node_feature_shape=summary["node_feature_shape"],
113
+ edge_feature_shape=summary["edge_feature_shape"],
114
+ global_features=summary["global_features"],
115
+ )
116
+ )
117
+
118
+ return LeMaterialDemoWorkflowResponse(
119
+ workflow_id="lematerial_bulk_demo",
120
+ source_dataset=dataset.source_dataset,
121
+ source_subset=dataset.source_subset or "",
122
+ schema_report=dataset.schema_report(),
123
+ candidate_slice=CandidateSliceSummary(
124
+ slice_id=candidate_slice.slice_id,
125
+ slice_name=candidate_slice.slice_name,
126
+ target=candidate_slice.target or TARGET,
127
+ input_count=candidate_slice.input_count,
128
+ output_count=candidate_slice.output_count,
129
+ removed_count=slice_report["removed_count"],
130
+ filter_steps=slice_report["filter_steps"],
131
+ report=slice_report,
132
+ ),
133
+ graph_export=GraphExportSummary(
134
+ included_count=graph_export.included_count,
135
+ excluded_count=graph_export.excluded_count,
136
+ previews=graph_previews,
137
+ ),
138
+ benchmark=BenchmarkSummary(
139
+ target=TARGET,
140
+ row_count=len(benchmark_frame),
141
+ columns=[str(column) for column in benchmark_frame.columns],
142
+ ),
143
+ benchmark_preview=_benchmark_preview(benchmark_frame.to_dict(orient="records")),
144
+ provenance=WorkflowProvenance(
145
+ fixture_path=FIXTURE_RELATIVE_PATH,
146
+ loader="LeMatBulk.from_records",
147
+ workflow_version=WORKFLOW_VERSION,
148
+ run_id=RUN_ID,
149
+ disclaimer=FIXTURE_DISCLAIMER,
150
+ upstream_revision=manifest["upstream_revision"],
151
+ hull_revision=manifest["hull_revision"],
152
+ license=manifest["license"],
153
+ citation_doi=manifest["citation_doi"],
154
+ snapshot_sha256=manifest["snapshot_sha256"],
155
+ field_sources=manifest["field_sources"],
156
+ ),
157
+ )
158
+
159
+
160
+ def _benchmark_preview(records: list[dict[str, Any]]) -> list[BenchmarkPreviewRow]:
161
+ preview: list[BenchmarkPreviewRow] = []
162
+ for row in records[:3]:
163
+ preview.append(
164
+ BenchmarkPreviewRow(
165
+ material_id=str(row["material_id"]),
166
+ formula=str(row["formula"]),
167
+ target=_json_scalar(row.get("target")),
168
+ density=_json_scalar(row.get("density")),
169
+ energy_above_hull=_json_scalar(row.get("energy_above_hull")),
170
+ max_force=_json_scalar(row.get("max_force")),
171
+ nsites=_json_int(row.get("nsites")),
172
+ nelements=_json_int(row.get("nelements")),
173
+ )
174
+ )
175
+ return preview
176
+
177
+
178
+ def _json_scalar(value: Any) -> float | str | None:
179
+ if value is None:
180
+ return None
181
+ if hasattr(value, "item") and callable(value.item):
182
+ value = value.item()
183
+ if isinstance(value, (int, float)):
184
+ return float(value)
185
+ if isinstance(value, str):
186
+ return value
187
+ return str(value)
188
+
189
+
190
+ def _json_int(value: Any) -> int | None:
191
+ if value is None:
192
+ return None
193
+ if hasattr(value, "item") and callable(value.item):
194
+ value = value.item()
195
+ return int(value)
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: mattergraph-api
3
+ Version: 0.1.0
4
+ Summary: FastAPI service for the MatterGraph demo and integrations.
5
+ Project-URL: Homepage, https://github.com/cyrusmo/MatterGraph
6
+ Project-URL: Repository, https://github.com/cyrusmo/MatterGraph
7
+ Project-URL: Issues, https://github.com/cyrusmo/MatterGraph/issues
8
+ Project-URL: Changelog, https://github.com/cyrusmo/MatterGraph/blob/main/CHANGELOG.md
9
+ Author: MatterGraph contributors
10
+ License-Expression: Apache-2.0
11
+ Keywords: api,fastapi,materials-informatics,materials-science
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
21
+ Classifier: Topic :: Scientific/Engineering :: Physics
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: fastapi>=0.110
24
+ Requires-Dist: mattergraph-connectors~=0.1.0
25
+ Requires-Dist: mattergraph-core~=0.1.0
26
+ Requires-Dist: mattergraph-sim~=0.1.0
27
+ Requires-Dist: pydantic-settings>=2.2
28
+ Requires-Dist: pydantic>=2.5
29
+ Requires-Dist: pymatgen>=2024.1.1
30
+ Requires-Dist: uvicorn[standard]>=0.29.0
31
+ Description-Content-Type: text/markdown
32
+
33
+ # mattergraph-api
34
+
35
+ FastAPI demo service for [MatterGraph](https://github.com/cyrusmo/MatterGraph).
36
+
37
+ This is a **demonstration surface**, not a production service: storage is an in-memory store loaded from a JSONL fixture, and the persistence layer under `mattergraph_api/db/` is a placeholder for installations that need one.
38
+
39
+ ## Routes
40
+
41
+ | Route | Purpose |
42
+ |---|---|
43
+ | `GET /health` | Liveness |
44
+ | `GET /materials`, `GET /materials/{mid}` | Browse normalized records |
45
+ | `GET /search?element=` | Filter by element |
46
+ | `POST /scores/rank` | Rank candidates with objectives, constraints, and weights |
47
+ | `POST /simulations/ase/relax` | Run an ASE relaxation (503 if `ase` is unavailable) |
48
+ | `GET /workflows/lematerial/demo` | End-to-end LeMat-Bulk screening walkthrough |
49
+
50
+ ## Install and run
51
+
52
+ ```bash
53
+ pip install mattergraph-api
54
+ export MATTERGRAPH_DEMO_DATA=data/demo/materials_sample.jsonl
55
+ uvicorn mattergraph_api.main:app --host 0.0.0.0 --port 8000
56
+ ```
57
+
58
+ ## License
59
+
60
+ Apache-2.0
@@ -0,0 +1,23 @@
1
+ mattergraph_api/__init__.py,sha256=qN508bKRh8SnCQlEo0KhUM8ljqMAxUZidtD5eUT4xoo,36
2
+ mattergraph_api/config.py,sha256=bWh9w62w6AmMEgw8vm5T_BxMJfUAVQ69r_AmH1OGBxY,345
3
+ mattergraph_api/main.py,sha256=wice7kvb33fTKxwICzB0Pbinzld8zjRh4U5uBX03KvI,2456
4
+ mattergraph_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ mattergraph_api/db/models.py,sha256=zGD09I9cfIPHNcBjujAIA6C4ms4kLCkb4y0k_AUdpaM,186
6
+ mattergraph_api/db/session.py,sha256=-XJBvw5CX_Z9ikDV3o50ADAmfs93U4mgEHZNslNdJJY,123
7
+ mattergraph_api/db/migrations/.gitkeep,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
8
+ mattergraph_api/routes/__init__.py,sha256=B_IMQ-HRZVaz2X6OonhdX1ksEtIi2xOmtBA7Dsm3wlU,18
9
+ mattergraph_api/routes/datasets.py,sha256=zmqRgouvIvS4_mr-Tb6Npigz0tZK0iwGhZHhuIYg-Ak,6121
10
+ mattergraph_api/routes/demo.py,sha256=LE8MWid8P7IeeQo-kRajisp4996uW3u5kbC9e6EqfRM,3590
11
+ mattergraph_api/routes/materials.py,sha256=P8b2-6_Ou_RwwQRqVKV7vgwCqX-AgN1puDcrbe5idq8,1237
12
+ mattergraph_api/routes/scores.py,sha256=Oha4esye1pGXFh0jWHrHq04LS0dL_bdXUSuV8Fggj24,2330
13
+ mattergraph_api/routes/search.py,sha256=H5bBj44z70KaWgcVQXiQhj2v4ycEhLbQcO3sDew1__I,629
14
+ mattergraph_api/routes/simulations.py,sha256=jkV6XBe5lHnAvveMCJRwf6E8aKFq4KePaK8ttoCqfcY,1487
15
+ mattergraph_api/routes/workflows.py,sha256=ahDdEKL5umhZLCYGp3E8S6KrR8RtyaiAFS8dcYWxa5Q,421
16
+ mattergraph_api/services/__init__.py,sha256=rEGKUGgqrs_kAq_6WCLg6nrd-WvFuVQ1XBApyYdRxEY,38
17
+ mattergraph_api/services/dataset_registry.py,sha256=z3kRqfmE2MAG6kXJilS2CisoVDE1TBAkgLaEhFfLNVg,6536
18
+ mattergraph_api/services/demo_service.py,sha256=zIvmC2zKIju_GNnBlRGiGlG_LnTdSLuEXT1VIdSzpAU,14322
19
+ mattergraph_api/services/store_service.py,sha256=FH3cWuP3FmRrsaZ2iF5j7pFcDMI-tMcLorL-1dUkopY,992
20
+ mattergraph_api/services/workflow_service.py,sha256=Y3Su9rduO-0R2Vtwv_5t0mxUcDUDEJe3mskdXg3_jA4,5609
21
+ mattergraph_api-0.1.0.dist-info/METADATA,sha256=k0V4T823vZXot93DEyeoXGvT-URgBHRfmaXSs0ghEtI,2357
22
+ mattergraph_api-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
23
+ mattergraph_api-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any