Metadata-Version: 2.4
Name: mlip-bench
Version: 0.1.0
Summary: Python client for the MLIP elemental benchmark structure->workflow API (api.mlip-bench.dev)
Author-email: Han Mai <ligerzero@live.com.au>
License-Expression: MIT
Project-URL: Homepage, https://mlip-bench.dev
Project-URL: Documentation, https://api.mlip-bench.dev/v1/openapi.json
Project-URL: Repository, https://github.com/ligerzero-ai/mlip-bench-api
Project-URL: API base, https://api.mlip-bench.dev/v1
Keywords: mlip,machine-learning-interatomic-potentials,materials-science,benchmark,ase,dft
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Requires-Dist: pyarrow>=14
Requires-Dist: ase>=3.22
Requires-Dist: tomli>=2; python_version < "3.11"
Provides-Extra: pandas
Requires-Dist: pandas>=2; extra == "pandas"
Provides-Extra: pymatgen
Requires-Dist: pymatgen; extra == "pymatgen"
Provides-Extra: datasets
Requires-Dist: fsspec[http]>=2023.1; extra == "datasets"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# mlip_bench

A typed Python client for the MLIP elemental benchmark structure→workflow
API (`https://api.mlip-bench.dev`) — structures, typed workflow instances,
`ase.Atoms` geometry, pandas leaderboard/parity DataFrames, bulk dataset
download, and client-side point lookups into the API's hash-sorted
geometry/corpus stores over plain HTTP Range reads (no server-side query
route required for that last one).

Requires Python ≥ 3.10.

## Install

```bash
pip install mlip-bench[pandas,datasets]
```

Or from a checkout of this repo:

```bash
pip install -e .[pandas,datasets]
```

Extras:

| Extra | Adds | Needed for |
|---|---|---|
| `pandas` | `pandas>=2` | `Track.leaderboard()` / `Track.parity()` (DataFrames) |
| `datasets` | `fsspec[http]` | `Datasets.filesystem()` (open bulk parquet without downloading) |
| `pymatgen` | `pymatgen` | `geometry.to_pymatgen()` |

The base install (no extras) already gets you `Client`, `structure()`,
`Structure.geometry()` / `.workflow()`, `client.gbseg_instance()`,
`client.corpus_record()`, and `client.stores.lookup()` — `httpx`, `pyarrow`,
and `ase` are unconditional dependencies.

## Credentials

`Client()` takes no required arguments — it resolves credentials through
four sources, in this priority order (first fully-specified source wins;
a source with only an id *or* only a secret is treated as absent):

1. **Explicit constructor arguments**:
   ```python
   Client(client_id="...", client_secret="...")
   ```
2. **Environment variables**: `MLIP_BENCH_CLIENT_ID` / `MLIP_BENCH_CLIENT_SECRET`
3. **`~/.config/mlip-bench.toml`**:
   ```toml
   [auth]
   client_id = "..."
   client_secret = "..."
   ```
4. **`~/.config/mlip-bench.env`** (shell-style, `export` prefix optional):
   ```bash
   CF_CLIENT_ID=...
   CF_CLIENT_SECRET=...
   ```

If none of the four are found, `Client()` proceeds unauthenticated (with a
`UserWarning`) rather than raising — pass `anonymous=True` to silence the
warning when that's intentional.

## Quickstart

See `examples/quickstart.py` for a fuller runnable tour (structure by
mp-id, typed elastic instance, `ase.Atoms` geometry, leaderboard head,
gbseg instance, corpus record, remote store lookup, dataset listing).
The short version:

```python
from mlip_bench import Client

client = Client()  # picks up credentials automatically, see above

structure = client.structure("mp-132")             # hash, mp_id, or alias
print(structure.record.element, structure.record.formula)

atoms = structure.geometry()                        # ase.Atoms

instance = structure.workflow("elastic:allegro_MP-L")  # typed WorkflowInstance
print(instance.units[0].outcomes["k_vrh"])

leaderboard = client.track("elastic").leaderboard()  # pandas.DataFrame
print(leaderboard.head())

entry_path = client.datasets.download("some_dataset.parquet", dest_dir="/tmp")
```

## What's addressable

The API has four addressability tiers, each reached a different way:

| Tier | Coverage | How the client reaches it |
|---|---|---|
| **Static elemental** | 834 elemental structures, every `emit:"static"` workflow (elastic, ordering, lattice, surface, phonon, vacancy, ...) | `client.structure(id)`, `structure.workflow(instance_id)`, `structure.geometry()` — plain static JSON, `/v1/structures/{id}...` |
| **Worker-served (GB / gbseg)** | Grain-boundary segregation instances, computed on demand from a hash-sorted store behind the Worker | `client.gbseg_instance(atoms_hash, potential)` — `/v1/query/instance/gbseg/{hash}/{potential}` |
| **ASSYST corpus** | ~7.29M-structure ASSYST corpus (single-point-only, `sp` workflow) | `client.structure(hash)` (corpus fallback, `.corpus` on the returned `Structure`) or `client.corpus_record(hash)` directly — `/v1/query/corpus/{hash}` |
| **Geometry via stores** | Any geometry payload backing a `GeometryRef` — served hrefs, or a raw hash-sorted store (`geom-*`, `corpus-geometry`, ...) | `client.fetch_geometry(ref)` (follows a served href; for **multi-artifact** refs — `surface-slab`, `gb-energetics` — pass `artifact=` and read the names from `ref.artifacts`, e.g. `fetch_geometry(ref, artifact="slab_relaxed")`), `client.query_geometry(store, key)` (server-side query route), or `client.stores.lookup(store, key)` (client-side point lookup over HTTP Range reads — manifest → prefix-shard index → single row group → filter by key, no server-side route at all) |

Aliases (`mp_id`s like `mp-132`, or `{namespace}:{structure_id}` ids) resolve
via a 302 redirect, transparently, wherever a hex `atoms_hash` is accepted.

## Errors

| Exception | Raised when |
|---|---|
| `NotFoundError` | A 404 response — unknown structure/workflow/alias id. Subclass of `ApiError`. |
| `ApiError` | Any other non-2xx response, after retries are exhausted (GET requests retry on 429/500/502/503/504 with backoff). |
| `GeometryUnavailable` | `client.fetch_geometry(ref)` was called on a `GeometryRef` whose `availability` isn't `"served"` (e.g. `"not_computed"`, `"bulk_only"`) — check `ref.availability` / `ref.note` first, or catch this. |
| `ParityNotPublished` | `Track.parity(potential)` was called for a track whose per-structure parity shards aren't published as API resources in this deployment. |
| `DataVersionMismatch` | `Client(..., expect_data_version=...)` was set and the server's `/v1/index.json` reports a different `data_version` — raised lazily, on first API call. |

All five are importable directly from the top-level package:

```python
from mlip_bench import (
    NotFoundError, ApiError, GeometryUnavailable,
    ParityNotPublished, DataVersionMismatch,
)
```

## Development

```bash
pip install -e .[pandas,pymatgen,datasets,dev]
python -m pytest tests/ -q          # fixture-server suite, no live credentials needed
python examples/quickstart.py       # live smoke against api.mlip-bench.dev (needs credentials)
```
