mattergraph 0.1.0__tar.gz

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,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: mattergraph
3
+ Version: 0.1.0
4
+ Summary: Open-source materials data infrastructure for physics-aware ML and simulation workflows.
5
+ Author: MatterGraph contributors
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/cyrusmo/MatterGraph
8
+ Project-URL: Repository, https://github.com/cyrusmo/MatterGraph
9
+ Project-URL: Issues, https://github.com/cyrusmo/MatterGraph/issues
10
+ Project-URL: Changelog, https://github.com/cyrusmo/MatterGraph/blob/main/CHANGELOG.md
11
+ Keywords: materials-science,materials-informatics,crystal-structure,dft,machine-learning,simulation
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
20
+ Classifier: Topic :: Scientific/Engineering :: Physics
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: mattergraph-core~=0.1.0
25
+ Requires-Dist: mattergraph-connectors~=0.1.0
26
+ Requires-Dist: mattergraph-benchmarks~=0.1.0
27
+ Requires-Dist: mattergraph-sim~=0.1.0
28
+ Requires-Dist: mattergraph-api~=0.1.0
29
+ Provides-Extra: mp
30
+ Requires-Dist: mattergraph-connectors[mp]~=0.1.0; extra == "mp"
31
+ Provides-Extra: jarvis
32
+ Requires-Dist: mattergraph-connectors[jarvis]~=0.1.0; extra == "jarvis"
33
+ Provides-Extra: all
34
+ Requires-Dist: mattergraph-connectors[all]~=0.1.0; extra == "all"
35
+ Dynamic: license-file
36
+
37
+ # MatterGraph
38
+
39
+ [![PyPI](https://img.shields.io/pypi/v/mattergraph)](https://pypi.org/project/mattergraph/)
40
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
41
+ ![License](https://img.shields.io/badge/license-Apache--2.0-green)
42
+ [![Docs](https://github.com/cyrusmo/MatterGraph/actions/workflows/docs.yml/badge.svg)](https://cyrusmo.github.io/MatterGraph/)
43
+ ![Status](https://img.shields.io/badge/status-alpha-orange)
44
+
45
+ MatterGraph is an open-source SDK and workbench for physics-aware materials workflows.
46
+
47
+ It helps researchers and developers turn standardized materials datasets into graph-ready, benchmark-ready, and candidate-screening artifacts. MatterGraph is dataset-agnostic and supports multi-source workflows across public materials resources.
48
+
49
+ LeMaterial is a flagship upstream companion: LeMaterial provides standardized materials datasets; MatterGraph provides the downstream workflow surface for inspection, filtering, graph export, slicing, and evaluation.
50
+
51
+ It is designed for teams building:
52
+
53
+ - materials screening and comparison tools
54
+ - graph-based materials ML models
55
+ - simulation-backed discovery workflows
56
+ - uncertainty-aware engineering decision support
57
+
58
+ MatterGraph is **not** a black-box “AI materials scientist.” It is **infrastructure** for transparent, physics-aware materials workflows.
59
+
60
+ ## Why MatterGraph?
61
+
62
+ Materials data is fragmented across repositories, schemas, units, structures, and property definitions. MatterGraph provides a **common, provenance-aware layer** for turning raw materials records into normalized, ML-ready, simulation-aware material objects.
63
+
64
+ ## What it does
65
+
66
+ - Ingest public materials datasets
67
+ - Normalize formulas, structures, units, and properties
68
+ - Convert crystal structures into **crystal graph** representations
69
+ - Track property provenance and basic confidence
70
+ - Support **transparent baseline** scorecards with auditable ranking behavior
71
+ - Provide adapters for benchmarking and **simulation job specs** (e.g. ASE)
72
+ - Expose a **small demo API** and **minimal web UI** for end-to-end exploration
73
+ - Inspect, validate, graph, rank, and export small local CSV/JSONL datasets without uploading them
74
+
75
+ ## Scope
76
+
77
+ The public repository focuses on transparent, reusable infrastructure for open materials workflows. Production-specific orchestration, hosting, and organization-specific workflows are out of scope for this demo.
78
+
79
+ MatterGraph Core focuses on transparent workflow primitives and guardrails. Proprietary ranking, active learning, orchestration, model routing, and customer-specific decision workflows remain private.
80
+
81
+ ## Install
82
+
83
+ ```bash
84
+ pip install mattergraph
85
+ ```
86
+
87
+ That installs the public toolkit without provider-specific SDKs. Opt into only the connector
88
+ SDKs you need:
89
+
90
+ ```bash
91
+ pip install 'mattergraph[mp]' # Materials Project SDK
92
+ pip install 'mattergraph[jarvis]' # JARVIS SDK
93
+ pip install 'mattergraph[all]' # all optional public connector SDKs
94
+ ```
95
+
96
+ Install an individual package for a smaller application surface:
97
+
98
+ ```bash
99
+ pip install mattergraph-core # schema, normalization, graphs, scoring
100
+ pip install mattergraph-connectors # NOMAD, OPTIMADE, local data, LeMat-Bulk
101
+ pip install mattergraph-sim # ASE job specs and runners
102
+ pip install mattergraph-benchmarks # metrics and Matbench adapter
103
+ pip install mattergraph-api # FastAPI demo service
104
+ ```
105
+
106
+ ## Quickstart (from source)
107
+
108
+ ```bash
109
+ git clone https://github.com/cyrusmo/MatterGraph.git
110
+ cd MatterGraph
111
+ python3 -m venv .venv
112
+ source .venv/bin/activate
113
+ pip install uv
114
+ uv sync --all-packages --group dev --extra all
115
+ # Optional: copy .env.example to .env and set MP_API_KEY for Materials Project
116
+ export MATTERGRAPH_DEMO_DATA=data/demo/materials_sample.jsonl
117
+ uv run uvicorn mattergraph_api.main:app --reload --host 0.0.0.0 --port 8000
118
+ ```
119
+
120
+ For the deterministic public capability walkthrough, start the API and UI together:
121
+
122
+ ```bash
123
+ ./scripts/run_public_demo.sh
124
+ ```
125
+
126
+ This runs a preflight-checked, checksummed snapshot of 24 real LeMaterial records at
127
+ `http://127.0.0.1:5173` without network calls or API credentials. The snapshot preserves the
128
+ upstream revision, immutable IDs, license, citation, and field-level provenance.
129
+
130
+ The UI opens in **Guided demo** mode. Choose **Local workbench** to inspect a CSV or JSONL file
131
+ up to 5 MiB / 5,000 rows. Imported datasets are ephemeral: normalized JSONL remains in memory,
132
+ the registry is capped at eight entries and 32 MiB, and only the selected dataset is
133
+ materialized. Imported content is never sent to an external service or written to disk.
134
+
135
+ Example: rank candidates with a **transparent baseline scorecard** (pool-relative min–max
136
+ objectives plus hard constraints).
137
+
138
+ ```python
139
+ import json
140
+ from pathlib import Path
141
+
142
+ from mattergraph import Scorecard
143
+ from mattergraph_connectors import LeMatBulk
144
+
145
+ artifact = json.loads(Path("data/demo/spc_real_snapshot.json").read_text())
146
+ store = LeMatBulk.from_records(
147
+ artifact["records"], subset="compatible_pbe"
148
+ ).to_material_store()
149
+ scorecard = Scorecard(
150
+ objectives={
151
+ "density": {"direction": "minimize", "weight": 0.6},
152
+ "energy_above_hull": {"direction": "minimize", "weight": 0.4},
153
+ },
154
+ constraints={
155
+ "energy_above_hull": {"max": 0.05},
156
+ "max_force": {"max": 0.2},
157
+ },
158
+ )
159
+ df = scorecard.rank(store.materials)
160
+ print(df.head(10))
161
+ ```
162
+
163
+ Example: turn LeMaterial-style records into a reproducible candidate slice.
164
+
165
+ ```python
166
+ import json
167
+ from pathlib import Path
168
+
169
+ from mattergraph_connectors import LeMatBulk
170
+
171
+ artifact = json.loads(Path("data/demo/spc_real_snapshot.json").read_text())
172
+ dataset = LeMatBulk.from_records(artifact["records"], subset="compatible_pbe")
173
+
174
+ candidate_slice = (
175
+ dataset
176
+ .filter_elements(include=["Ti", "Al", "N"])
177
+ .filter_complexity(max_nsites=16, max_nelements=3)
178
+ .create_slice("spc_tialn_candidates_v1", target="energy_above_hull")
179
+ )
180
+
181
+ print(candidate_slice.report())
182
+ ```
183
+
184
+ ## Architecture (conceptual)
185
+
186
+ ```text
187
+ Raw dataset → MatterGraphDataset / Material → candidate slice / crystal graph / benchmark frame → scorecard or simulation job
188
+ ```
189
+
190
+ ## Roadmap
191
+
192
+ High-level [ROADMAP.md](ROADMAP.md) covers connectors, the unified schema, workflow slicing, graph building, benchmark adapters, simulation interchange, and uncertainty. For the LeMaterial companion layer, see [docs/integrations/lematerial.md](docs/integrations/lematerial.md), and for local data see [docs/local-workbench.md](docs/local-workbench.md).
193
+
194
+ ## Packages
195
+
196
+ | Package | Role |
197
+ |--------|------|
198
+ | `mattergraph-core` | Schema, normalization, `MatterGraphDataset`, `CandidateSlice`, crystal graphs, transparent `Scorecard`, `MaterialStore` |
199
+ | `mattergraph-connectors` | MP, JARVIS, NOMAD public metadata, OPTIMADE, LeMat-Bulk companion adapter, bounded local CSV/JSONL import, OQMD stub |
200
+ | `mattergraph-benchmarks` | Metrics, Matbench-style adapter (optional `matbench` install) |
201
+ | `mattergraph-sim` | ASE / stub LAMMPS+QE around job specs |
202
+ | `mattergraph-api` | FastAPI demo plus ephemeral local-dataset registry, graph summaries, slicing, audited ranking, export, and labeled CHGNet reference evidence |
203
+
204
+ ## License
205
+
206
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,170 @@
1
+ # MatterGraph
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/mattergraph)](https://pypi.org/project/mattergraph/)
4
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
5
+ ![License](https://img.shields.io/badge/license-Apache--2.0-green)
6
+ [![Docs](https://github.com/cyrusmo/MatterGraph/actions/workflows/docs.yml/badge.svg)](https://cyrusmo.github.io/MatterGraph/)
7
+ ![Status](https://img.shields.io/badge/status-alpha-orange)
8
+
9
+ MatterGraph is an open-source SDK and workbench for physics-aware materials workflows.
10
+
11
+ It helps researchers and developers turn standardized materials datasets into graph-ready, benchmark-ready, and candidate-screening artifacts. MatterGraph is dataset-agnostic and supports multi-source workflows across public materials resources.
12
+
13
+ LeMaterial is a flagship upstream companion: LeMaterial provides standardized materials datasets; MatterGraph provides the downstream workflow surface for inspection, filtering, graph export, slicing, and evaluation.
14
+
15
+ It is designed for teams building:
16
+
17
+ - materials screening and comparison tools
18
+ - graph-based materials ML models
19
+ - simulation-backed discovery workflows
20
+ - uncertainty-aware engineering decision support
21
+
22
+ MatterGraph is **not** a black-box “AI materials scientist.” It is **infrastructure** for transparent, physics-aware materials workflows.
23
+
24
+ ## Why MatterGraph?
25
+
26
+ Materials data is fragmented across repositories, schemas, units, structures, and property definitions. MatterGraph provides a **common, provenance-aware layer** for turning raw materials records into normalized, ML-ready, simulation-aware material objects.
27
+
28
+ ## What it does
29
+
30
+ - Ingest public materials datasets
31
+ - Normalize formulas, structures, units, and properties
32
+ - Convert crystal structures into **crystal graph** representations
33
+ - Track property provenance and basic confidence
34
+ - Support **transparent baseline** scorecards with auditable ranking behavior
35
+ - Provide adapters for benchmarking and **simulation job specs** (e.g. ASE)
36
+ - Expose a **small demo API** and **minimal web UI** for end-to-end exploration
37
+ - Inspect, validate, graph, rank, and export small local CSV/JSONL datasets without uploading them
38
+
39
+ ## Scope
40
+
41
+ The public repository focuses on transparent, reusable infrastructure for open materials workflows. Production-specific orchestration, hosting, and organization-specific workflows are out of scope for this demo.
42
+
43
+ MatterGraph Core focuses on transparent workflow primitives and guardrails. Proprietary ranking, active learning, orchestration, model routing, and customer-specific decision workflows remain private.
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install mattergraph
49
+ ```
50
+
51
+ That installs the public toolkit without provider-specific SDKs. Opt into only the connector
52
+ SDKs you need:
53
+
54
+ ```bash
55
+ pip install 'mattergraph[mp]' # Materials Project SDK
56
+ pip install 'mattergraph[jarvis]' # JARVIS SDK
57
+ pip install 'mattergraph[all]' # all optional public connector SDKs
58
+ ```
59
+
60
+ Install an individual package for a smaller application surface:
61
+
62
+ ```bash
63
+ pip install mattergraph-core # schema, normalization, graphs, scoring
64
+ pip install mattergraph-connectors # NOMAD, OPTIMADE, local data, LeMat-Bulk
65
+ pip install mattergraph-sim # ASE job specs and runners
66
+ pip install mattergraph-benchmarks # metrics and Matbench adapter
67
+ pip install mattergraph-api # FastAPI demo service
68
+ ```
69
+
70
+ ## Quickstart (from source)
71
+
72
+ ```bash
73
+ git clone https://github.com/cyrusmo/MatterGraph.git
74
+ cd MatterGraph
75
+ python3 -m venv .venv
76
+ source .venv/bin/activate
77
+ pip install uv
78
+ uv sync --all-packages --group dev --extra all
79
+ # Optional: copy .env.example to .env and set MP_API_KEY for Materials Project
80
+ export MATTERGRAPH_DEMO_DATA=data/demo/materials_sample.jsonl
81
+ uv run uvicorn mattergraph_api.main:app --reload --host 0.0.0.0 --port 8000
82
+ ```
83
+
84
+ For the deterministic public capability walkthrough, start the API and UI together:
85
+
86
+ ```bash
87
+ ./scripts/run_public_demo.sh
88
+ ```
89
+
90
+ This runs a preflight-checked, checksummed snapshot of 24 real LeMaterial records at
91
+ `http://127.0.0.1:5173` without network calls or API credentials. The snapshot preserves the
92
+ upstream revision, immutable IDs, license, citation, and field-level provenance.
93
+
94
+ The UI opens in **Guided demo** mode. Choose **Local workbench** to inspect a CSV or JSONL file
95
+ up to 5 MiB / 5,000 rows. Imported datasets are ephemeral: normalized JSONL remains in memory,
96
+ the registry is capped at eight entries and 32 MiB, and only the selected dataset is
97
+ materialized. Imported content is never sent to an external service or written to disk.
98
+
99
+ Example: rank candidates with a **transparent baseline scorecard** (pool-relative min–max
100
+ objectives plus hard constraints).
101
+
102
+ ```python
103
+ import json
104
+ from pathlib import Path
105
+
106
+ from mattergraph import Scorecard
107
+ from mattergraph_connectors import LeMatBulk
108
+
109
+ artifact = json.loads(Path("data/demo/spc_real_snapshot.json").read_text())
110
+ store = LeMatBulk.from_records(
111
+ artifact["records"], subset="compatible_pbe"
112
+ ).to_material_store()
113
+ scorecard = Scorecard(
114
+ objectives={
115
+ "density": {"direction": "minimize", "weight": 0.6},
116
+ "energy_above_hull": {"direction": "minimize", "weight": 0.4},
117
+ },
118
+ constraints={
119
+ "energy_above_hull": {"max": 0.05},
120
+ "max_force": {"max": 0.2},
121
+ },
122
+ )
123
+ df = scorecard.rank(store.materials)
124
+ print(df.head(10))
125
+ ```
126
+
127
+ Example: turn LeMaterial-style records into a reproducible candidate slice.
128
+
129
+ ```python
130
+ import json
131
+ from pathlib import Path
132
+
133
+ from mattergraph_connectors import LeMatBulk
134
+
135
+ artifact = json.loads(Path("data/demo/spc_real_snapshot.json").read_text())
136
+ dataset = LeMatBulk.from_records(artifact["records"], subset="compatible_pbe")
137
+
138
+ candidate_slice = (
139
+ dataset
140
+ .filter_elements(include=["Ti", "Al", "N"])
141
+ .filter_complexity(max_nsites=16, max_nelements=3)
142
+ .create_slice("spc_tialn_candidates_v1", target="energy_above_hull")
143
+ )
144
+
145
+ print(candidate_slice.report())
146
+ ```
147
+
148
+ ## Architecture (conceptual)
149
+
150
+ ```text
151
+ Raw dataset → MatterGraphDataset / Material → candidate slice / crystal graph / benchmark frame → scorecard or simulation job
152
+ ```
153
+
154
+ ## Roadmap
155
+
156
+ High-level [ROADMAP.md](ROADMAP.md) covers connectors, the unified schema, workflow slicing, graph building, benchmark adapters, simulation interchange, and uncertainty. For the LeMaterial companion layer, see [docs/integrations/lematerial.md](docs/integrations/lematerial.md), and for local data see [docs/local-workbench.md](docs/local-workbench.md).
157
+
158
+ ## Packages
159
+
160
+ | Package | Role |
161
+ |--------|------|
162
+ | `mattergraph-core` | Schema, normalization, `MatterGraphDataset`, `CandidateSlice`, crystal graphs, transparent `Scorecard`, `MaterialStore` |
163
+ | `mattergraph-connectors` | MP, JARVIS, NOMAD public metadata, OPTIMADE, LeMat-Bulk companion adapter, bounded local CSV/JSONL import, OQMD stub |
164
+ | `mattergraph-benchmarks` | Metrics, Matbench-style adapter (optional `matbench` install) |
165
+ | `mattergraph-sim` | ASE / stub LAMMPS+QE around job specs |
166
+ | `mattergraph-api` | FastAPI demo plus ephemeral local-dataset registry, graph summaries, slicing, audited ranking, export, and labeled CHGNet reference evidence |
167
+
168
+ ## License
169
+
170
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,3 @@
1
+ """Meta-module so the root project can be built as a dependency-only metapackage."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: mattergraph
3
+ Version: 0.1.0
4
+ Summary: Open-source materials data infrastructure for physics-aware ML and simulation workflows.
5
+ Author: MatterGraph contributors
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/cyrusmo/MatterGraph
8
+ Project-URL: Repository, https://github.com/cyrusmo/MatterGraph
9
+ Project-URL: Issues, https://github.com/cyrusmo/MatterGraph/issues
10
+ Project-URL: Changelog, https://github.com/cyrusmo/MatterGraph/blob/main/CHANGELOG.md
11
+ Keywords: materials-science,materials-informatics,crystal-structure,dft,machine-learning,simulation
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
20
+ Classifier: Topic :: Scientific/Engineering :: Physics
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: mattergraph-core~=0.1.0
25
+ Requires-Dist: mattergraph-connectors~=0.1.0
26
+ Requires-Dist: mattergraph-benchmarks~=0.1.0
27
+ Requires-Dist: mattergraph-sim~=0.1.0
28
+ Requires-Dist: mattergraph-api~=0.1.0
29
+ Provides-Extra: mp
30
+ Requires-Dist: mattergraph-connectors[mp]~=0.1.0; extra == "mp"
31
+ Provides-Extra: jarvis
32
+ Requires-Dist: mattergraph-connectors[jarvis]~=0.1.0; extra == "jarvis"
33
+ Provides-Extra: all
34
+ Requires-Dist: mattergraph-connectors[all]~=0.1.0; extra == "all"
35
+ Dynamic: license-file
36
+
37
+ # MatterGraph
38
+
39
+ [![PyPI](https://img.shields.io/pypi/v/mattergraph)](https://pypi.org/project/mattergraph/)
40
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
41
+ ![License](https://img.shields.io/badge/license-Apache--2.0-green)
42
+ [![Docs](https://github.com/cyrusmo/MatterGraph/actions/workflows/docs.yml/badge.svg)](https://cyrusmo.github.io/MatterGraph/)
43
+ ![Status](https://img.shields.io/badge/status-alpha-orange)
44
+
45
+ MatterGraph is an open-source SDK and workbench for physics-aware materials workflows.
46
+
47
+ It helps researchers and developers turn standardized materials datasets into graph-ready, benchmark-ready, and candidate-screening artifacts. MatterGraph is dataset-agnostic and supports multi-source workflows across public materials resources.
48
+
49
+ LeMaterial is a flagship upstream companion: LeMaterial provides standardized materials datasets; MatterGraph provides the downstream workflow surface for inspection, filtering, graph export, slicing, and evaluation.
50
+
51
+ It is designed for teams building:
52
+
53
+ - materials screening and comparison tools
54
+ - graph-based materials ML models
55
+ - simulation-backed discovery workflows
56
+ - uncertainty-aware engineering decision support
57
+
58
+ MatterGraph is **not** a black-box “AI materials scientist.” It is **infrastructure** for transparent, physics-aware materials workflows.
59
+
60
+ ## Why MatterGraph?
61
+
62
+ Materials data is fragmented across repositories, schemas, units, structures, and property definitions. MatterGraph provides a **common, provenance-aware layer** for turning raw materials records into normalized, ML-ready, simulation-aware material objects.
63
+
64
+ ## What it does
65
+
66
+ - Ingest public materials datasets
67
+ - Normalize formulas, structures, units, and properties
68
+ - Convert crystal structures into **crystal graph** representations
69
+ - Track property provenance and basic confidence
70
+ - Support **transparent baseline** scorecards with auditable ranking behavior
71
+ - Provide adapters for benchmarking and **simulation job specs** (e.g. ASE)
72
+ - Expose a **small demo API** and **minimal web UI** for end-to-end exploration
73
+ - Inspect, validate, graph, rank, and export small local CSV/JSONL datasets without uploading them
74
+
75
+ ## Scope
76
+
77
+ The public repository focuses on transparent, reusable infrastructure for open materials workflows. Production-specific orchestration, hosting, and organization-specific workflows are out of scope for this demo.
78
+
79
+ MatterGraph Core focuses on transparent workflow primitives and guardrails. Proprietary ranking, active learning, orchestration, model routing, and customer-specific decision workflows remain private.
80
+
81
+ ## Install
82
+
83
+ ```bash
84
+ pip install mattergraph
85
+ ```
86
+
87
+ That installs the public toolkit without provider-specific SDKs. Opt into only the connector
88
+ SDKs you need:
89
+
90
+ ```bash
91
+ pip install 'mattergraph[mp]' # Materials Project SDK
92
+ pip install 'mattergraph[jarvis]' # JARVIS SDK
93
+ pip install 'mattergraph[all]' # all optional public connector SDKs
94
+ ```
95
+
96
+ Install an individual package for a smaller application surface:
97
+
98
+ ```bash
99
+ pip install mattergraph-core # schema, normalization, graphs, scoring
100
+ pip install mattergraph-connectors # NOMAD, OPTIMADE, local data, LeMat-Bulk
101
+ pip install mattergraph-sim # ASE job specs and runners
102
+ pip install mattergraph-benchmarks # metrics and Matbench adapter
103
+ pip install mattergraph-api # FastAPI demo service
104
+ ```
105
+
106
+ ## Quickstart (from source)
107
+
108
+ ```bash
109
+ git clone https://github.com/cyrusmo/MatterGraph.git
110
+ cd MatterGraph
111
+ python3 -m venv .venv
112
+ source .venv/bin/activate
113
+ pip install uv
114
+ uv sync --all-packages --group dev --extra all
115
+ # Optional: copy .env.example to .env and set MP_API_KEY for Materials Project
116
+ export MATTERGRAPH_DEMO_DATA=data/demo/materials_sample.jsonl
117
+ uv run uvicorn mattergraph_api.main:app --reload --host 0.0.0.0 --port 8000
118
+ ```
119
+
120
+ For the deterministic public capability walkthrough, start the API and UI together:
121
+
122
+ ```bash
123
+ ./scripts/run_public_demo.sh
124
+ ```
125
+
126
+ This runs a preflight-checked, checksummed snapshot of 24 real LeMaterial records at
127
+ `http://127.0.0.1:5173` without network calls or API credentials. The snapshot preserves the
128
+ upstream revision, immutable IDs, license, citation, and field-level provenance.
129
+
130
+ The UI opens in **Guided demo** mode. Choose **Local workbench** to inspect a CSV or JSONL file
131
+ up to 5 MiB / 5,000 rows. Imported datasets are ephemeral: normalized JSONL remains in memory,
132
+ the registry is capped at eight entries and 32 MiB, and only the selected dataset is
133
+ materialized. Imported content is never sent to an external service or written to disk.
134
+
135
+ Example: rank candidates with a **transparent baseline scorecard** (pool-relative min–max
136
+ objectives plus hard constraints).
137
+
138
+ ```python
139
+ import json
140
+ from pathlib import Path
141
+
142
+ from mattergraph import Scorecard
143
+ from mattergraph_connectors import LeMatBulk
144
+
145
+ artifact = json.loads(Path("data/demo/spc_real_snapshot.json").read_text())
146
+ store = LeMatBulk.from_records(
147
+ artifact["records"], subset="compatible_pbe"
148
+ ).to_material_store()
149
+ scorecard = Scorecard(
150
+ objectives={
151
+ "density": {"direction": "minimize", "weight": 0.6},
152
+ "energy_above_hull": {"direction": "minimize", "weight": 0.4},
153
+ },
154
+ constraints={
155
+ "energy_above_hull": {"max": 0.05},
156
+ "max_force": {"max": 0.2},
157
+ },
158
+ )
159
+ df = scorecard.rank(store.materials)
160
+ print(df.head(10))
161
+ ```
162
+
163
+ Example: turn LeMaterial-style records into a reproducible candidate slice.
164
+
165
+ ```python
166
+ import json
167
+ from pathlib import Path
168
+
169
+ from mattergraph_connectors import LeMatBulk
170
+
171
+ artifact = json.loads(Path("data/demo/spc_real_snapshot.json").read_text())
172
+ dataset = LeMatBulk.from_records(artifact["records"], subset="compatible_pbe")
173
+
174
+ candidate_slice = (
175
+ dataset
176
+ .filter_elements(include=["Ti", "Al", "N"])
177
+ .filter_complexity(max_nsites=16, max_nelements=3)
178
+ .create_slice("spc_tialn_candidates_v1", target="energy_above_hull")
179
+ )
180
+
181
+ print(candidate_slice.report())
182
+ ```
183
+
184
+ ## Architecture (conceptual)
185
+
186
+ ```text
187
+ Raw dataset → MatterGraphDataset / Material → candidate slice / crystal graph / benchmark frame → scorecard or simulation job
188
+ ```
189
+
190
+ ## Roadmap
191
+
192
+ High-level [ROADMAP.md](ROADMAP.md) covers connectors, the unified schema, workflow slicing, graph building, benchmark adapters, simulation interchange, and uncertainty. For the LeMaterial companion layer, see [docs/integrations/lematerial.md](docs/integrations/lematerial.md), and for local data see [docs/local-workbench.md](docs/local-workbench.md).
193
+
194
+ ## Packages
195
+
196
+ | Package | Role |
197
+ |--------|------|
198
+ | `mattergraph-core` | Schema, normalization, `MatterGraphDataset`, `CandidateSlice`, crystal graphs, transparent `Scorecard`, `MaterialStore` |
199
+ | `mattergraph-connectors` | MP, JARVIS, NOMAD public metadata, OPTIMADE, LeMat-Bulk companion adapter, bounded local CSV/JSONL import, OQMD stub |
200
+ | `mattergraph-benchmarks` | Metrics, Matbench-style adapter (optional `matbench` install) |
201
+ | `mattergraph-sim` | ASE / stub LAMMPS+QE around job specs |
202
+ | `mattergraph-api` | FastAPI demo plus ephemeral local-dataset registry, graph summaries, slicing, audited ranking, export, and labeled CHGNet reference evidence |
203
+
204
+ ## License
205
+
206
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ _workspace_meta.py
4
+ pyproject.toml
5
+ mattergraph.egg-info/PKG-INFO
6
+ mattergraph.egg-info/SOURCES.txt
7
+ mattergraph.egg-info/dependency_links.txt
8
+ mattergraph.egg-info/requires.txt
9
+ mattergraph.egg-info/top_level.txt
10
+ tests/test_release_checks.py
@@ -0,0 +1,14 @@
1
+ mattergraph-core~=0.1.0
2
+ mattergraph-connectors~=0.1.0
3
+ mattergraph-benchmarks~=0.1.0
4
+ mattergraph-sim~=0.1.0
5
+ mattergraph-api~=0.1.0
6
+
7
+ [all]
8
+ mattergraph-connectors[all]~=0.1.0
9
+
10
+ [jarvis]
11
+ mattergraph-connectors[jarvis]~=0.1.0
12
+
13
+ [mp]
14
+ mattergraph-connectors[mp]~=0.1.0
@@ -0,0 +1 @@
1
+ _workspace_meta
@@ -0,0 +1,124 @@
1
+ [project]
2
+ name = "mattergraph"
3
+ version = "0.1.0"
4
+ description = "Open-source materials data infrastructure for physics-aware ML and simulation workflows."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "Apache-2.0"
8
+ authors = [{ name = "MatterGraph contributors" }]
9
+ keywords = [
10
+ "materials-science",
11
+ "materials-informatics",
12
+ "crystal-structure",
13
+ "dft",
14
+ "machine-learning",
15
+ "simulation",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Science/Research",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Scientific/Engineering :: Chemistry",
26
+ "Topic :: Scientific/Engineering :: Physics",
27
+ ]
28
+ dependencies = [
29
+ "mattergraph-core~=0.1.0",
30
+ "mattergraph-connectors~=0.1.0",
31
+ "mattergraph-benchmarks~=0.1.0",
32
+ "mattergraph-sim~=0.1.0",
33
+ "mattergraph-api~=0.1.0",
34
+ ]
35
+
36
+ [project.optional-dependencies]
37
+ mp = ["mattergraph-connectors[mp]~=0.1.0"]
38
+ jarvis = ["mattergraph-connectors[jarvis]~=0.1.0"]
39
+ all = ["mattergraph-connectors[all]~=0.1.0"]
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "pytest>=8.0",
44
+ "pytest-asyncio>=0.24.0",
45
+ "pytest-cov>=4.0",
46
+ "ruff>=0.4.0",
47
+ "httpx>=0.27.0",
48
+ "psutil>=6.0",
49
+ "pyyaml>=6.0",
50
+ ]
51
+ docs = [
52
+ "mkdocs>=1.6",
53
+ "mkdocs-material>=9.5",
54
+ "pymdown-extensions>=10.0",
55
+ ]
56
+
57
+ [tool.uv.workspace]
58
+ members = [
59
+ "packages/mattergraph-core",
60
+ "packages/mattergraph-connectors",
61
+ "packages/mattergraph-benchmarks",
62
+ "packages/mattergraph-sim",
63
+ "packages/mattergraph-api",
64
+ ]
65
+
66
+ [tool.uv.sources]
67
+ mattergraph-core = { workspace = true }
68
+ mattergraph-connectors = { workspace = true }
69
+ mattergraph-benchmarks = { workspace = true }
70
+ mattergraph-sim = { workspace = true }
71
+ mattergraph-api = { workspace = true }
72
+
73
+ [project.urls]
74
+ Homepage = "https://github.com/cyrusmo/MatterGraph"
75
+ Repository = "https://github.com/cyrusmo/MatterGraph"
76
+ Issues = "https://github.com/cyrusmo/MatterGraph/issues"
77
+ Changelog = "https://github.com/cyrusmo/MatterGraph/blob/main/CHANGELOG.md"
78
+
79
+ [build-system]
80
+ requires = ["setuptools>=77", "wheel"]
81
+ build-backend = "setuptools.build_meta"
82
+
83
+ [tool.setuptools]
84
+ py-modules = ["_workspace_meta"]
85
+
86
+ [tool.ruff]
87
+ line-length = 100
88
+ target-version = "py310"
89
+ exclude = ["*.ipynb", ".venv"]
90
+
91
+ [tool.ruff.lint]
92
+ select = ["E", "F", "I", "UP", "B"]
93
+
94
+ [tool.pytest.ini_options]
95
+ testpaths = ["packages", "tests"]
96
+ pythonpath = ["packages/mattergraph-core", "packages/mattergraph-connectors", "packages/mattergraph-benchmarks", "packages/mattergraph-sim", "packages/mattergraph-api"]
97
+ asyncio_mode = "auto"
98
+ asyncio_default_fixture_loop_scope = "function"
99
+ markers = ["memory_budget: process-level RSS budget gate (run explicitly in CI)"]
100
+ filterwarnings = [
101
+ "ignore:Set OLD_ERROR_HANDLING to false and catch the errors directly.:DeprecationWarning:spglib",
102
+ ]
103
+
104
+ [tool.coverage.run]
105
+ source = [
106
+ "mattergraph",
107
+ "mattergraph_connectors",
108
+ "mattergraph_benchmarks",
109
+ "mattergraph_sim",
110
+ "mattergraph_api",
111
+ ]
112
+ omit = ["*/tests/*"]
113
+
114
+ [tool.coverage.report]
115
+ # The floor sits just under current coverage: it catches regressions without
116
+ # blocking unrelated work. Raise it as connector and graph tests land.
117
+ fail_under = 70
118
+ show_missing = true
119
+ exclude_lines = [
120
+ "pragma: no cover",
121
+ "if TYPE_CHECKING:",
122
+ "raise NotImplementedError",
123
+ "if __name__ == .__main__.:",
124
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.util
4
+ import json
5
+ import sys
6
+ from email.message import Message
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ MODULE_PATH = Path(__file__).parents[1] / "scripts" / "release_checks.py"
12
+ SPEC = importlib.util.spec_from_file_location("mattergraph_release_checks", MODULE_PATH)
13
+ assert SPEC is not None and SPEC.loader is not None
14
+ release_checks = importlib.util.module_from_spec(SPEC)
15
+ sys.modules[SPEC.name] = release_checks
16
+ SPEC.loader.exec_module(release_checks)
17
+
18
+ EXPECTED_PACKAGES = release_checks.EXPECTED_PACKAGES
19
+ ReleaseCheckError = release_checks.ReleaseCheckError
20
+ _validate_requirement = release_checks._validate_requirement
21
+ check_install_report = release_checks.check_install_report
22
+ normalize_name = release_checks.normalize_name
23
+ select_artifacts = release_checks.select_artifacts
24
+
25
+
26
+ def _write_report(path: Path, host: str) -> None:
27
+ payload = {
28
+ "install": [
29
+ {
30
+ "metadata": {"name": name, "version": "0.1.0"},
31
+ "download_info": {"url": f"https://{host}/packages/{name}-0.1.0.whl"},
32
+ }
33
+ for name in sorted(EXPECTED_PACKAGES)
34
+ ]
35
+ }
36
+ path.write_text(json.dumps(payload))
37
+
38
+
39
+ def test_install_report_requires_all_packages_from_target_index(tmp_path: Path) -> None:
40
+ report = tmp_path / "install-report.json"
41
+ _write_report(report, "test-files.pythonhosted.org")
42
+
43
+ check_install_report(report, "testpypi", "0.1.0")
44
+
45
+
46
+ def test_install_report_rejects_wrong_package_origin(tmp_path: Path) -> None:
47
+ report = tmp_path / "install-report.json"
48
+ _write_report(report, "files.pythonhosted.org")
49
+
50
+ with pytest.raises(ReleaseCheckError, match="test-files.pythonhosted.org"):
51
+ check_install_report(report, "testpypi", "0.1.0")
52
+
53
+
54
+ @pytest.mark.parametrize(
55
+ "requirement",
56
+ [
57
+ "mattergraph-core",
58
+ "mattergraph-core>=0.1.0",
59
+ "mattergraph-core @ file:///tmp/mattergraph-core",
60
+ ],
61
+ )
62
+ def test_internal_requirements_must_use_compatible_registry_versions(requirement: str) -> None:
63
+ with pytest.raises(ReleaseCheckError):
64
+ _validate_requirement(requirement, "0.1.0", Path("artifact.whl"))
65
+
66
+
67
+ def test_distribution_names_use_pep_503_normalization() -> None:
68
+ assert normalize_name("MatterGraph_API") == "mattergraph-api"
69
+
70
+
71
+ def test_select_artifacts_isolates_one_wheel_and_sdist(
72
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
73
+ ) -> None:
74
+ dist = tmp_path / "dist"
75
+ dist.mkdir()
76
+ artifacts = []
77
+ for package in sorted(EXPECTED_PACKAGES):
78
+ for kind, suffix in (("wheel", ".whl"), ("sdist", ".tar.gz")):
79
+ path = dist / f"{package}-0.1.0{suffix}"
80
+ path.write_text(f"{package}-{kind}")
81
+ message = Message()
82
+ message["Name"] = package
83
+ artifacts.append(
84
+ release_checks.ArtifactMetadata(path=path, kind=kind, message=message, raw="")
85
+ )
86
+ monkeypatch.setattr(release_checks, "load_artifacts", lambda _: artifacts)
87
+
88
+ output = tmp_path / "publish-dist"
89
+ select_artifacts(dist, "MatterGraph_API", output)
90
+
91
+ assert sorted(path.name for path in output.iterdir()) == [
92
+ "mattergraph-api-0.1.0.tar.gz",
93
+ "mattergraph-api-0.1.0.whl",
94
+ ]
95
+
96
+
97
+ def test_select_artifacts_rejects_unknown_package(tmp_path: Path) -> None:
98
+ with pytest.raises(ReleaseCheckError, match="Unknown MatterGraph package"):
99
+ select_artifacts(tmp_path / "dist", "not-mattergraph", tmp_path / "publish-dist")
100
+
101
+
102
+ def test_testpypi_install_does_not_mix_dependency_indexes() -> None:
103
+ workflow = (Path(__file__).parents[1] / ".github" / "workflows" / "release.yml").read_text()
104
+
105
+ assert "--extra-index-url" not in workflow
106
+ assert "--index-url https://pypi.org/simple/" in workflow
107
+ assert "--index-url https://test.pypi.org/simple/" in workflow
108
+ assert "--no-deps" in workflow
109
+ assert "verify_existing" in workflow
110
+ for package in EXPECTED_PACKAGES:
111
+ assert f'"{package}==0.1.0"' in workflow