matprops-sdk 0.1.1__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.
- matprops_sdk-0.1.1/.gitignore +60 -0
- matprops_sdk-0.1.1/LICENSE +21 -0
- matprops_sdk-0.1.1/PKG-INFO +165 -0
- matprops_sdk-0.1.1/README.md +141 -0
- matprops_sdk-0.1.1/examples/add_numbers/example.input.json +1 -0
- matprops_sdk-0.1.1/examples/add_numbers/pyproject.toml +8 -0
- matprops_sdk-0.1.1/examples/add_numbers/simulation.py +15 -0
- matprops_sdk-0.1.1/examples/composite-thermal-conductivity/example.input.json +8 -0
- matprops_sdk-0.1.1/examples/composite-thermal-conductivity/example.input.smoke.json +8 -0
- matprops_sdk-0.1.1/examples/composite-thermal-conductivity/pyproject.toml +8 -0
- matprops_sdk-0.1.1/examples/composite-thermal-conductivity/requirements.txt +1 -0
- matprops_sdk-0.1.1/examples/composite-thermal-conductivity/simulation.py +156 -0
- matprops_sdk-0.1.1/examples/composite-thermal-conductivity/solver.py +149 -0
- matprops_sdk-0.1.1/examples/gan-thermal-conductivity/example.input.json +4 -0
- matprops_sdk-0.1.1/examples/gan-thermal-conductivity/pyproject.toml +12 -0
- matprops_sdk-0.1.1/examples/gan-thermal-conductivity/simulation.py +26 -0
- matprops_sdk-0.1.1/examples/volume-of-sphere/example.input.json +3 -0
- matprops_sdk-0.1.1/examples/volume-of-sphere/my_logic.py +5 -0
- matprops_sdk-0.1.1/examples/volume-of-sphere/pyproject.toml +12 -0
- matprops_sdk-0.1.1/examples/volume-of-sphere/simulation.py +28 -0
- matprops_sdk-0.1.1/pyproject.toml +48 -0
- matprops_sdk-0.1.1/src/matprops_sdk/__init__.py +14 -0
- matprops_sdk-0.1.1/src/matprops_sdk/auth.py +227 -0
- matprops_sdk-0.1.1/src/matprops_sdk/bundle.py +64 -0
- matprops_sdk-0.1.1/src/matprops_sdk/cli.py +358 -0
- matprops_sdk-0.1.1/src/matprops_sdk/context.py +104 -0
- matprops_sdk-0.1.1/src/matprops_sdk/credentials.py +93 -0
- matprops_sdk-0.1.1/src/matprops_sdk/manifest.py +70 -0
- matprops_sdk-0.1.1/src/matprops_sdk/models.py +38 -0
- matprops_sdk-0.1.1/src/matprops_sdk/publish.py +165 -0
- matprops_sdk-0.1.1/src/matprops_sdk/remote.py +297 -0
- matprops_sdk-0.1.1/src/matprops_sdk/runtime.py +176 -0
- matprops_sdk-0.1.1/src/matprops_sdk/simulation.py +163 -0
- matprops_sdk-0.1.1/tests/test_bundle.py +92 -0
- matprops_sdk-0.1.1/tests/test_cli.py +293 -0
- matprops_sdk-0.1.1/tests/test_contract.py +170 -0
- matprops_sdk-0.1.1/tests/test_login.py +170 -0
- matprops_sdk-0.1.1/tests/test_remote.py +299 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
node_modules/
|
|
3
|
+
**/node_modules/
|
|
4
|
+
.build/
|
|
5
|
+
**/.build/
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
.next/
|
|
8
|
+
**/.next/
|
|
9
|
+
.env
|
|
10
|
+
.env.*
|
|
11
|
+
!.env.sample*
|
|
12
|
+
# Package-level names are symlinks to the root files (git stores the link, not secrets)
|
|
13
|
+
!/frontend/.env
|
|
14
|
+
!/frontend/.env.local
|
|
15
|
+
!/db/.env
|
|
16
|
+
!/db/.env.local
|
|
17
|
+
*.log
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.pyc
|
|
20
|
+
.venv/
|
|
21
|
+
venv/
|
|
22
|
+
dist/
|
|
23
|
+
build/
|
|
24
|
+
.turbo/
|
|
25
|
+
.serverless/
|
|
26
|
+
.webpack/
|
|
27
|
+
coverage/
|
|
28
|
+
.nyc_output/
|
|
29
|
+
*.egg-info/
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.ruff_cache/
|
|
32
|
+
.run-results/
|
|
33
|
+
matprops-run/
|
|
34
|
+
results/
|
|
35
|
+
.pnpm-store/
|
|
36
|
+
.pulumi/
|
|
37
|
+
.vercel/
|
|
38
|
+
|
|
39
|
+
.envy.*
|
|
40
|
+
|
|
41
|
+
# lattice-dynamics generated artifacts
|
|
42
|
+
mesh_dicts/
|
|
43
|
+
mesh_dicts*/
|
|
44
|
+
outfiles/
|
|
45
|
+
outfiles*/
|
|
46
|
+
kappa-m*
|
|
47
|
+
*IFCs/
|
|
48
|
+
fc*
|
|
49
|
+
FORCE_SETS
|
|
50
|
+
FORCES_FC2
|
|
51
|
+
FORCES_FC3
|
|
52
|
+
POSCAR*
|
|
53
|
+
BORN
|
|
54
|
+
aux_scripts/
|
|
55
|
+
gruneisen*/
|
|
56
|
+
lattice-dynamics/**/*output/
|
|
57
|
+
|
|
58
|
+
# Local-only reference code — never commit or push
|
|
59
|
+
/example-codebase/
|
|
60
|
+
example-codebase/**
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MatProps
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: matprops-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Define and run Matprops simulations locally.
|
|
5
|
+
Project-URL: Homepage, https://matprops.com
|
|
6
|
+
Project-URL: Repository, https://github.com/rileyhanus/matprops
|
|
7
|
+
Project-URL: Issues, https://github.com/rileyhanus/matprops/issues
|
|
8
|
+
Author: MatProps
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: pydantic<3,>=2
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Matprops SDK
|
|
26
|
+
|
|
27
|
+
Local toolkit for typed material simulations: validate the contract, emit JSON Schema, run on your machine, and package or publish a zip to sim-server.
|
|
28
|
+
|
|
29
|
+
Requires Python 3.11+.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python -m pip install matprops-sdk
|
|
33
|
+
matprops init my-sim
|
|
34
|
+
cd my-sim
|
|
35
|
+
matprops validate
|
|
36
|
+
matprops run --input example.input.json
|
|
37
|
+
matprops package # dist/<name>.zip, upload it in the web UI
|
|
38
|
+
matprops publish # or package + upload in one step (needs login)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
From a clone of this repo, install an editable copy with tests and lint:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python -m pip install -e ".[dev]"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`init` writes `simulation.py`, `pyproject.toml`, and `example.input.json` (it will not overwrite existing files). Entrypoint defaults to `[tool.matprops] entrypoint = "simulation:run"`; override with `--entrypoint`.
|
|
48
|
+
|
|
49
|
+
## CLI login
|
|
50
|
+
|
|
51
|
+
`matprops login` opens the Matprops web app (or prints a URL) so you can sign in and approve the CLI. Temporary credentials are stored in `~/.config/matprops/credentials.json` (`chmod 600`).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
matprops login
|
|
55
|
+
matprops whoami
|
|
56
|
+
matprops logout
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Defaults are production: `https://matprops.com` and `https://api.matprops.com`. Override with `MATPROPS_APP_URL` / `MATPROPS_API_URL` or `--app-url` / `--api-url`. For the local stack use `http://localhost:3000` and `http://localhost:4000`; for the shared dev environment use `https://dev.matprops.com` and `https://api.dev.matprops.com`. Use `--no-browser` on a remote machine and open the printed URL locally.
|
|
60
|
+
|
|
61
|
+
## Simulation
|
|
62
|
+
|
|
63
|
+
Use Pydantic models and `@simulation`. Sibling modules are importable as long as filenames are valid Python (`my_logic.py`, not `my-logic.py`).
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from matprops_sdk import Field, InputModel, OutputModel, RunContext, simulation
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Inputs(InputModel):
|
|
70
|
+
radius: float = Field(gt=0, units="cm")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Outputs(OutputModel):
|
|
74
|
+
volume: float = Field(units="cm^3")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@simulation(name="volume-of-sphere", inputs=Inputs, outputs=Outputs)
|
|
78
|
+
def run(inputs: Inputs, context: RunContext) -> Outputs:
|
|
79
|
+
path = context.artifact("trace.txt", "Calculation trace")
|
|
80
|
+
path.write_text("...", encoding="utf-8")
|
|
81
|
+
return Outputs(volume=...)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The kebab-case `name` is the compute identity. Optional decorator metadata (`cpu`, `memory`, `timeout`) is recorded in schema/manifest and not enforced locally.
|
|
85
|
+
|
|
86
|
+
`validate` checks load, decorator, signature, and schema only — not physics and not `example.input.json`.
|
|
87
|
+
|
|
88
|
+
Walkthrough with split logic: [`examples/volume-of-sphere/`](examples/volume-of-sphere/).
|
|
89
|
+
|
|
90
|
+
## CLI
|
|
91
|
+
|
|
92
|
+
| Command | What it does |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `matprops init [dir]` | Scaffold a project |
|
|
95
|
+
| `matprops validate` | Check the entrypoint contract |
|
|
96
|
+
| `matprops schema [--output-dir DIR]` | Print schemas, or write `input.schema.json` / `output.schema.json` |
|
|
97
|
+
| `matprops run --input …` | Run locally |
|
|
98
|
+
| `matprops login` | Sign in via the web app and store credentials |
|
|
99
|
+
| `matprops logout` | Revoke and delete stored credentials |
|
|
100
|
+
| `matprops whoami` | Show the current CLI identity |
|
|
101
|
+
| `matprops package [--output PATH]` | Validate and write an uploadable zip to `dist/<name>.zip` |
|
|
102
|
+
| `matprops publish` | Package the project and POST it to sim-server |
|
|
103
|
+
|
|
104
|
+
`--input` is a file path, or inline JSON if the trimmed value starts with `{` or `[`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
matprops run --input '{"var1": 1, "var2": 2}'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each run writes a timestamped folder under `.run-results/` (UTC, millisecond stamp, e.g. `.run-results/2026-09-01T15-11-22-123Z/`) with `outputs.json`, `manifest.json`, and `artifacts/`. `--output-dir` changes that parent directory.
|
|
111
|
+
|
|
112
|
+
Add `.run-results/` to gitignore.
|
|
113
|
+
|
|
114
|
+
## Packaging and publishing
|
|
115
|
+
|
|
116
|
+
`package` validates the contract in your environment (where your `requirements.txt` dependencies are installed), then zips the project directory together with a generated `manifest.json` holding the simulation name, entrypoint, input/output JSON Schema and runtime hints. It skips `.git`, virtualenvs, `.run-results`, `dist`, caches, `*.pyc`, `.DS_Store` and root-level `example.input*.json` files. The zip is written to `dist/<name>.zip` (or `--output`) and is byte-for-byte reproducible for identical code.
|
|
117
|
+
|
|
118
|
+
The platform only accepts zips that contain this manifest: it reads the contract from `manifest.json` instead of importing your code, so uploads are validated instantly regardless of which packages the simulation needs. Upload the zip in the web UI, or let `publish` do it for you. Either way the image build installs your `requirements.txt` and re-derives the contract to confirm the manifest.
|
|
119
|
+
|
|
120
|
+
`publish` runs the same packaging step and POSTs the bundle to `{MATPROPS_API_URL}/simulation-function-versions` (default `https://api.matprops.com`). Authenticate with `matprops login`, `MATPROPS_API_KEY`, or `--api-key`. Override the URL with `MATPROPS_API_URL` or `--url`.
|
|
121
|
+
|
|
122
|
+
Add `dist/` to gitignore.
|
|
123
|
+
|
|
124
|
+
## Releasing
|
|
125
|
+
|
|
126
|
+
PyPI project name is `matprops-sdk` (`matprops` is taken). Versions live in both `pyproject.toml` (`[project].version`) and `src/matprops_sdk/__init__.py` (`__version__`); bump them together.
|
|
127
|
+
|
|
128
|
+
Do **not** tag releases as `v1`, `v13`, or other `v*` integers — those tags are used by the production deploy workflow. SDK tags are `sdk-vX.Y.Z` only.
|
|
129
|
+
|
|
130
|
+
### One-time setup
|
|
131
|
+
|
|
132
|
+
1. Create a [pypi.org](https://pypi.org/account/register) account with 2FA (and optionally a separate [test.pypi.org](https://test.pypi.org) account).
|
|
133
|
+
2. In GitHub: **Settings → Environments** → create `pypi` (URL `https://pypi.org/project/matprops-sdk/`) and optionally `testpypi`. Required reviewers are recommended on `pypi`.
|
|
134
|
+
3. On PyPI, add a [pending trusted publisher](https://pypi.org/manage/account/publishing/):
|
|
135
|
+
- Project name: `matprops-sdk`
|
|
136
|
+
- Owner: `rileyhanus`
|
|
137
|
+
- Repository: `matprops`
|
|
138
|
+
- Workflow filename: `python-sdk-publish.yml`
|
|
139
|
+
- Environment name: `pypi`
|
|
140
|
+
4. Repeat on TestPyPI with environment `testpypi` if you want a dry run.
|
|
141
|
+
|
|
142
|
+
A typo in the workflow name or environment is a silent 403 on first upload.
|
|
143
|
+
|
|
144
|
+
### Publish a version
|
|
145
|
+
|
|
146
|
+
1. Bump both version fields, merge to `main`.
|
|
147
|
+
2. From `python/matprops-sdk`: `pip install -e ".[dev]" build twine`, then `ruff check . && pytest`, `python -m build`, and `python -m twine check dist/*`.
|
|
148
|
+
3. Tag the merged main SHA and push:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git tag -a sdk-v0.1.0 -m "matprops-sdk 0.1.0"
|
|
152
|
+
git push origin sdk-v0.1.0
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
4. Watch **Actions → Publish Python SDK**. Approve the `pypi` environment if reviewers are required.
|
|
156
|
+
|
|
157
|
+
The first successful trusted-publisher run creates `https://pypi.org/project/matprops-sdk/` and uploads that version. PyPI versions are immutable; a bad upload means yank and ship the next version.
|
|
158
|
+
|
|
159
|
+
Optional TestPyPI dry run: **Actions → Publish Python SDK → Run workflow** with target `testpypi`, then:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
python -m pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ matprops-sdk
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A first upload can also be done from a laptop with an account-scoped API token (`python -m twine upload dist/*`; username `__token__`). Later releases should use the `sdk-v*` tag so CI and Trusted Publishing stay the path of record.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Matprops SDK
|
|
2
|
+
|
|
3
|
+
Local toolkit for typed material simulations: validate the contract, emit JSON Schema, run on your machine, and package or publish a zip to sim-server.
|
|
4
|
+
|
|
5
|
+
Requires Python 3.11+.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m pip install matprops-sdk
|
|
9
|
+
matprops init my-sim
|
|
10
|
+
cd my-sim
|
|
11
|
+
matprops validate
|
|
12
|
+
matprops run --input example.input.json
|
|
13
|
+
matprops package # dist/<name>.zip, upload it in the web UI
|
|
14
|
+
matprops publish # or package + upload in one step (needs login)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
From a clone of this repo, install an editable copy with tests and lint:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python -m pip install -e ".[dev]"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`init` writes `simulation.py`, `pyproject.toml`, and `example.input.json` (it will not overwrite existing files). Entrypoint defaults to `[tool.matprops] entrypoint = "simulation:run"`; override with `--entrypoint`.
|
|
24
|
+
|
|
25
|
+
## CLI login
|
|
26
|
+
|
|
27
|
+
`matprops login` opens the Matprops web app (or prints a URL) so you can sign in and approve the CLI. Temporary credentials are stored in `~/.config/matprops/credentials.json` (`chmod 600`).
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
matprops login
|
|
31
|
+
matprops whoami
|
|
32
|
+
matprops logout
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Defaults are production: `https://matprops.com` and `https://api.matprops.com`. Override with `MATPROPS_APP_URL` / `MATPROPS_API_URL` or `--app-url` / `--api-url`. For the local stack use `http://localhost:3000` and `http://localhost:4000`; for the shared dev environment use `https://dev.matprops.com` and `https://api.dev.matprops.com`. Use `--no-browser` on a remote machine and open the printed URL locally.
|
|
36
|
+
|
|
37
|
+
## Simulation
|
|
38
|
+
|
|
39
|
+
Use Pydantic models and `@simulation`. Sibling modules are importable as long as filenames are valid Python (`my_logic.py`, not `my-logic.py`).
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from matprops_sdk import Field, InputModel, OutputModel, RunContext, simulation
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Inputs(InputModel):
|
|
46
|
+
radius: float = Field(gt=0, units="cm")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Outputs(OutputModel):
|
|
50
|
+
volume: float = Field(units="cm^3")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@simulation(name="volume-of-sphere", inputs=Inputs, outputs=Outputs)
|
|
54
|
+
def run(inputs: Inputs, context: RunContext) -> Outputs:
|
|
55
|
+
path = context.artifact("trace.txt", "Calculation trace")
|
|
56
|
+
path.write_text("...", encoding="utf-8")
|
|
57
|
+
return Outputs(volume=...)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The kebab-case `name` is the compute identity. Optional decorator metadata (`cpu`, `memory`, `timeout`) is recorded in schema/manifest and not enforced locally.
|
|
61
|
+
|
|
62
|
+
`validate` checks load, decorator, signature, and schema only — not physics and not `example.input.json`.
|
|
63
|
+
|
|
64
|
+
Walkthrough with split logic: [`examples/volume-of-sphere/`](examples/volume-of-sphere/).
|
|
65
|
+
|
|
66
|
+
## CLI
|
|
67
|
+
|
|
68
|
+
| Command | What it does |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `matprops init [dir]` | Scaffold a project |
|
|
71
|
+
| `matprops validate` | Check the entrypoint contract |
|
|
72
|
+
| `matprops schema [--output-dir DIR]` | Print schemas, or write `input.schema.json` / `output.schema.json` |
|
|
73
|
+
| `matprops run --input …` | Run locally |
|
|
74
|
+
| `matprops login` | Sign in via the web app and store credentials |
|
|
75
|
+
| `matprops logout` | Revoke and delete stored credentials |
|
|
76
|
+
| `matprops whoami` | Show the current CLI identity |
|
|
77
|
+
| `matprops package [--output PATH]` | Validate and write an uploadable zip to `dist/<name>.zip` |
|
|
78
|
+
| `matprops publish` | Package the project and POST it to sim-server |
|
|
79
|
+
|
|
80
|
+
`--input` is a file path, or inline JSON if the trimmed value starts with `{` or `[`:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
matprops run --input '{"var1": 1, "var2": 2}'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Each run writes a timestamped folder under `.run-results/` (UTC, millisecond stamp, e.g. `.run-results/2026-09-01T15-11-22-123Z/`) with `outputs.json`, `manifest.json`, and `artifacts/`. `--output-dir` changes that parent directory.
|
|
87
|
+
|
|
88
|
+
Add `.run-results/` to gitignore.
|
|
89
|
+
|
|
90
|
+
## Packaging and publishing
|
|
91
|
+
|
|
92
|
+
`package` validates the contract in your environment (where your `requirements.txt` dependencies are installed), then zips the project directory together with a generated `manifest.json` holding the simulation name, entrypoint, input/output JSON Schema and runtime hints. It skips `.git`, virtualenvs, `.run-results`, `dist`, caches, `*.pyc`, `.DS_Store` and root-level `example.input*.json` files. The zip is written to `dist/<name>.zip` (or `--output`) and is byte-for-byte reproducible for identical code.
|
|
93
|
+
|
|
94
|
+
The platform only accepts zips that contain this manifest: it reads the contract from `manifest.json` instead of importing your code, so uploads are validated instantly regardless of which packages the simulation needs. Upload the zip in the web UI, or let `publish` do it for you. Either way the image build installs your `requirements.txt` and re-derives the contract to confirm the manifest.
|
|
95
|
+
|
|
96
|
+
`publish` runs the same packaging step and POSTs the bundle to `{MATPROPS_API_URL}/simulation-function-versions` (default `https://api.matprops.com`). Authenticate with `matprops login`, `MATPROPS_API_KEY`, or `--api-key`. Override the URL with `MATPROPS_API_URL` or `--url`.
|
|
97
|
+
|
|
98
|
+
Add `dist/` to gitignore.
|
|
99
|
+
|
|
100
|
+
## Releasing
|
|
101
|
+
|
|
102
|
+
PyPI project name is `matprops-sdk` (`matprops` is taken). Versions live in both `pyproject.toml` (`[project].version`) and `src/matprops_sdk/__init__.py` (`__version__`); bump them together.
|
|
103
|
+
|
|
104
|
+
Do **not** tag releases as `v1`, `v13`, or other `v*` integers — those tags are used by the production deploy workflow. SDK tags are `sdk-vX.Y.Z` only.
|
|
105
|
+
|
|
106
|
+
### One-time setup
|
|
107
|
+
|
|
108
|
+
1. Create a [pypi.org](https://pypi.org/account/register) account with 2FA (and optionally a separate [test.pypi.org](https://test.pypi.org) account).
|
|
109
|
+
2. In GitHub: **Settings → Environments** → create `pypi` (URL `https://pypi.org/project/matprops-sdk/`) and optionally `testpypi`. Required reviewers are recommended on `pypi`.
|
|
110
|
+
3. On PyPI, add a [pending trusted publisher](https://pypi.org/manage/account/publishing/):
|
|
111
|
+
- Project name: `matprops-sdk`
|
|
112
|
+
- Owner: `rileyhanus`
|
|
113
|
+
- Repository: `matprops`
|
|
114
|
+
- Workflow filename: `python-sdk-publish.yml`
|
|
115
|
+
- Environment name: `pypi`
|
|
116
|
+
4. Repeat on TestPyPI with environment `testpypi` if you want a dry run.
|
|
117
|
+
|
|
118
|
+
A typo in the workflow name or environment is a silent 403 on first upload.
|
|
119
|
+
|
|
120
|
+
### Publish a version
|
|
121
|
+
|
|
122
|
+
1. Bump both version fields, merge to `main`.
|
|
123
|
+
2. From `python/matprops-sdk`: `pip install -e ".[dev]" build twine`, then `ruff check . && pytest`, `python -m build`, and `python -m twine check dist/*`.
|
|
124
|
+
3. Tag the merged main SHA and push:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git tag -a sdk-v0.1.0 -m "matprops-sdk 0.1.0"
|
|
128
|
+
git push origin sdk-v0.1.0
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
4. Watch **Actions → Publish Python SDK**. Approve the `pypi` environment if reviewers are required.
|
|
132
|
+
|
|
133
|
+
The first successful trusted-publisher run creates `https://pypi.org/project/matprops-sdk/` and uploads that version. PyPI versions are immutable; a bad upload means yank and ship the next version.
|
|
134
|
+
|
|
135
|
+
Optional TestPyPI dry run: **Actions → Publish Python SDK → Run workflow** with target `testpypi`, then:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
python -m pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ matprops-sdk
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A first upload can also be done from a laptop with an account-scoped API token (`python -m twine upload dist/*`; username `__token__`). Later releases should use the `sdk-v*` tag so CI and Trusted Publishing stay the path of record.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"num1": 1.5, "num2": 2.5}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from matprops_sdk import Field, InputModel, OutputModel, simulation
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Inputs(InputModel):
|
|
5
|
+
num1: float = Field(description="First number", units="unitless")
|
|
6
|
+
num2: float = Field(description="Second number", units="unitless")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Outputs(OutputModel):
|
|
10
|
+
sum: float = Field(description="Sum of the two numbers", units="unitless")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@simulation(name="add-numbers", inputs=Inputs, outputs=Outputs)
|
|
14
|
+
def run(inputs: Inputs) -> Outputs:
|
|
15
|
+
return Outputs(sum=inputs.num1 + inputs.num2)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy>=1.26
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from solver import (
|
|
3
|
+
conductivity_field,
|
|
4
|
+
effective_conductivity,
|
|
5
|
+
hashin_shtrikman_2d,
|
|
6
|
+
random_microstructure,
|
|
7
|
+
solve_temperature,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from matprops_sdk import (
|
|
11
|
+
Field,
|
|
12
|
+
InputModel,
|
|
13
|
+
OutputModel,
|
|
14
|
+
RunContext,
|
|
15
|
+
simulation,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Inputs(InputModel):
|
|
20
|
+
k_matrix: float = Field(
|
|
21
|
+
default=0.2,
|
|
22
|
+
gt=0,
|
|
23
|
+
units="W/m·K",
|
|
24
|
+
description="Thermal conductivity of the continuous matrix",
|
|
25
|
+
)
|
|
26
|
+
k_inclusion: float = Field(
|
|
27
|
+
default=30.0,
|
|
28
|
+
gt=0,
|
|
29
|
+
units="W/m·K",
|
|
30
|
+
description="Thermal conductivity of the dispersed inclusions",
|
|
31
|
+
)
|
|
32
|
+
volume_fraction: float = Field(
|
|
33
|
+
default=0.3,
|
|
34
|
+
ge=0,
|
|
35
|
+
le=1,
|
|
36
|
+
units="unitless",
|
|
37
|
+
description="Target inclusion volume fraction",
|
|
38
|
+
)
|
|
39
|
+
grid_size: int = Field(
|
|
40
|
+
default=800,
|
|
41
|
+
ge=8,
|
|
42
|
+
le=1024,
|
|
43
|
+
units="unitless",
|
|
44
|
+
description="Number of cells along each side of the unit square",
|
|
45
|
+
)
|
|
46
|
+
max_iterations: int = Field(
|
|
47
|
+
default=60_000,
|
|
48
|
+
ge=1,
|
|
49
|
+
le=500_000,
|
|
50
|
+
units="unitless",
|
|
51
|
+
description="Jacobi sweeps; raise this (and grid_size) to spend more compute",
|
|
52
|
+
)
|
|
53
|
+
seed: int = Field(
|
|
54
|
+
default=42,
|
|
55
|
+
ge=0,
|
|
56
|
+
units="unitless",
|
|
57
|
+
description="RNG seed for the random microstructure",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Outputs(OutputModel):
|
|
62
|
+
k_effective: float = Field(
|
|
63
|
+
units="W/m·K",
|
|
64
|
+
description="Effective in-plane conductivity from the solved heat flux",
|
|
65
|
+
)
|
|
66
|
+
hashin_shtrikman_lower: float = Field(
|
|
67
|
+
units="W/m·K",
|
|
68
|
+
description="2D Hashin–Shtrikman lower bound for the same mixture",
|
|
69
|
+
)
|
|
70
|
+
hashin_shtrikman_upper: float = Field(
|
|
71
|
+
units="W/m·K",
|
|
72
|
+
description="2D Hashin–Shtrikman upper bound for the same mixture",
|
|
73
|
+
)
|
|
74
|
+
volume_fraction_realized: float = Field(
|
|
75
|
+
units="unitless",
|
|
76
|
+
description="Inclusion fraction actually placed on the grid",
|
|
77
|
+
)
|
|
78
|
+
residual: float = Field(
|
|
79
|
+
units="K",
|
|
80
|
+
description="Max temperature change on the last Jacobi sweep",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@simulation(
|
|
85
|
+
name="composite-thermal-conductivity",
|
|
86
|
+
inputs=Inputs,
|
|
87
|
+
outputs=Outputs,
|
|
88
|
+
cpu=1,
|
|
89
|
+
memory="2GiB",
|
|
90
|
+
timeout="10m",
|
|
91
|
+
)
|
|
92
|
+
def run(inputs: Inputs, context: RunContext) -> Outputs:
|
|
93
|
+
context.progress(0, stage="generate microstructure", stage_index=0, stage_count=3)
|
|
94
|
+
|
|
95
|
+
inclusion = random_microstructure(
|
|
96
|
+
inputs.grid_size,
|
|
97
|
+
inputs.volume_fraction,
|
|
98
|
+
inputs.seed,
|
|
99
|
+
)
|
|
100
|
+
conductivity = conductivity_field(
|
|
101
|
+
inclusion,
|
|
102
|
+
inputs.k_matrix,
|
|
103
|
+
inputs.k_inclusion,
|
|
104
|
+
)
|
|
105
|
+
volume_fraction_realized = float(inclusion.mean())
|
|
106
|
+
|
|
107
|
+
context.progress(5, stage="solve heat equation", stage_index=1, stage_count=3)
|
|
108
|
+
|
|
109
|
+
def on_progress(iteration: int, total: int, _residual: float) -> None:
|
|
110
|
+
# 5%–95% is the solver; leave the last 5% for flux and artifacts.
|
|
111
|
+
percent = 5.0 + 90.0 * iteration / total
|
|
112
|
+
context.progress(
|
|
113
|
+
percent, stage="solve heat equation", stage_index=1, stage_count=3
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
temperature, residual = solve_temperature(
|
|
117
|
+
conductivity,
|
|
118
|
+
inputs.max_iterations,
|
|
119
|
+
progress=on_progress,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
context.progress(
|
|
123
|
+
95, stage="compute effective conductivity", stage_index=2, stage_count=3
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
k_effective = effective_conductivity(conductivity, temperature)
|
|
127
|
+
hs_lower, hs_upper = hashin_shtrikman_2d(
|
|
128
|
+
inputs.k_matrix,
|
|
129
|
+
inputs.k_inclusion,
|
|
130
|
+
volume_fraction_realized,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
np.savetxt(
|
|
134
|
+
context.artifact("microstructure.csv", "Inclusion map (1) vs matrix (0)"),
|
|
135
|
+
inclusion.astype("int8"),
|
|
136
|
+
fmt="%d",
|
|
137
|
+
delimiter=",",
|
|
138
|
+
)
|
|
139
|
+
np.savetxt(
|
|
140
|
+
context.artifact("temperature.csv", "Solved temperature field (K, relative)"),
|
|
141
|
+
temperature,
|
|
142
|
+
fmt="%.6e",
|
|
143
|
+
delimiter=",",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
context.progress(
|
|
147
|
+
100, stage="compute effective conductivity", stage_index=2, stage_count=3
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return Outputs(
|
|
151
|
+
k_effective=k_effective,
|
|
152
|
+
hashin_shtrikman_lower=hs_lower,
|
|
153
|
+
hashin_shtrikman_upper=hs_upper,
|
|
154
|
+
volume_fraction_realized=volume_fraction_realized,
|
|
155
|
+
residual=residual,
|
|
156
|
+
)
|