Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
042951e
Scaffold: Apache-2.0 license, pyproject (tt_atom), NOTICE, package in…
moritztng Jun 29, 2026
e413ea0
Reference harness + golden fixtures (phase 2)
moritztng Jun 29, 2026
3fadb19
Add device.py: TT device/mesh open + HiFi4/fp32-acc kernel config (ph…
moritztng Jun 29, 2026
50a2f4d
chore: gitignore ttnn-generated runtime artifacts (generated/)
moritztng Jun 29, 2026
6d14b07
so2.py: device-resident SO(2) convolution + parity test (PCC 0.99999/…
moritztng Jun 29, 2026
d40db25
norm.py: equivariant RMS-norm-SH + parity test (PCC 0.990 on p150)
moritztng Jun 29, 2026
4fead53
golden: add SO3 grid matrices + sys_node_embedding (unblocks grid ato…
moritztng Jun 29, 2026
7014e93
model: full eSCN-MD forward device-resident — end-to-end PCC 0.99998
moritztng Jun 29, 2026
8b36e82
forces: device reverse-mode VJP through backbone — adjoints PCC >= 0.…
moritztng Jun 29, 2026
cbc4537
geometry + forces: end-to-end analytic forces from positions — F PCC …
moritztng Jun 29, 2026
df363a6
weights + calculator + relaxation example (phases 5-6)
moritztng Jun 29, 2026
6e40801
benchmarks: single-card throughput + CPU-vs-TT harness (phase 7 start)
moritztng Jun 29, 2026
22e769e
perf: --fast bf8 path (weights + optional bf8 Wigner) — measured, acc…
moritztng Jun 29, 2026
402cd08
perf: SO(2) per-m conv as flat 2D GEMMs — 12x on so2, 2.7x/block
moritztng Jun 29, 2026
ca002d8
perf: Wigner rotation as flat sparse multiply-accumulate (rotation.py)
moritztng Jun 29, 2026
e12b4c4
perf: fully-flat SO(2)/gate chain — drop all [E,9,C] tile-padded resh…
moritztng Jun 29, 2026
c61dc4c
feat: multi-card throughput fan-out (batch.py) — 3.95x on 4 cards
moritztng Jun 29, 2026
1b1a8fd
docs+bench: README, RELEASE, perf charts, accuracy column (phases 8-9)
moritztng Jun 29, 2026
67bbc71
test: real uma-s-1 golden harness + MoLE-merge correctness anchor
moritztng Jun 30, 2026
2b72eb7
feat: spectral atomwise FF (ff_type=spectral) device-resident + analy…
moritztng Jun 30, 2026
e189bce
feat: rand_emb csd embedding + per-task energy normalizer + spectral …
moritztng Jun 30, 2026
8f0b9a2
test: tests/test_realweight.py — real uma-s-1 parity (auto-skips w/o …
moritztng Jun 30, 2026
de64f4b
docs+tool: real uma-s-1 accuracy numbers + --uma-s-1 export path
moritztng Jun 30, 2026
66d2b41
feat: periodic (PBC) neighbor list — cell-aware min-image graph + parity
moritztng Jul 1, 2026
729c457
feat: task generality — clean task_name API + oc20 slab parity (3 tasks)
moritztng Jul 1, 2026
493dc68
feat: honest mmax<lmax guard + uma-m-1p1 attempt documented
moritztng Jul 1, 2026
d720388
perf: trace-captured device-resident MD/relaxation path — 2.3x e2e
moritztng Jul 1, 2026
49f89b6
feat: tt-atom CLI + one-command converter w/ device roundtrip verify …
moritztng Jul 1, 2026
6be3283
docs: README migration guide + honest task/periodic/trace tables
moritztng Jul 1, 2026
c0ee074
refactor: elegance pass — remove dead imports + unused locals
moritztng Jul 1, 2026
2a93ba6
docs: RELEASE.md — production numbers (periodic, 3 tasks, trace 2.33x…
moritztng Jul 1, 2026
a692e5b
feat: disjoint-union (block-diagonal) graph batching — assembly + seg…
moritztng Jul 2, 2026
cd1e3be
feat: evaluate_batch API + vs-fairchem batched parity (real uma-s-1)
moritztng Jul 2, 2026
c1b7633
bench: disjoint-union batching throughput (1 p150, energy-only)
moritztng Jul 2, 2026
8f83d79
docs: README batched-inference section + evaluate_batch example + imp…
moritztng Jul 2, 2026
48de4b3
Merge feat/batching: disjoint-union (block-diagonal) graph batching
moritztng Jul 2, 2026
4cd7dec
feat: stress/virial tensor — strain-on-edge_vec autograd + ASE Voigt …
moritztng Jul 2, 2026
1028e90
feat: odac + omc task parity — MgO framework + solid-CO2 goldens
moritztng Jul 2, 2026
0b22f6b
feat: uma-m-1p1 support — rectangular reduced-m-space Wigner (lmax=4/…
moritztng Jul 2, 2026
3dfd065
docs: README — stress + odac/omc + uma-m + fairchem-equivalence table…
moritztng Jul 2, 2026
6825e70
Merge feat/completeness: stress/virial + odac/omc tasks + uma-m-1p1
moritztng Jul 2, 2026
cb55523
feat(ux): from_uma auto-bundle factory + composition cache + tt-atom …
moritztng Jul 3, 2026
263ce20
test(ux): test_from_uma — cache logic, no-refenv fast path, factory/m…
moritztng Jul 3, 2026
4327f5b
docs: README — from_uma + tt-atom run as the primary (fairchem-parall…
moritztng Jul 3, 2026
5459bfa
feat(ux): zero-config UMA(atoms) face + task inference + progressive-…
moritztng Jul 3, 2026
07b0a75
Merge feat/ux: fairchem-easy UX — zero-config UMA(atoms)/tt-atom run,…
moritztng Jul 3, 2026
1a072b7
chore: gitignore build artifacts (dist/, build/, egg-info)
moritztng Jul 3, 2026
e21f24c
docs: minimal README (progressive disclosure)
moritztng Jul 3, 2026
b76bbdf
docs: accuracy table (vs fairchem, reproducible) + fairchem comparison
moritztng Jul 3, 2026
5910021
docs: caffeine MD demo gif at top of README
moritztng Jul 3, 2026
4b3de00
MIT license; ttnn from PyPI as a dependency; checkmark comparison; co…
moritztng Jul 3, 2026
46a48be
pin ttnn floor to validated 0.68; note accuracy is version-checked vi…
moritztng Jul 3, 2026
22848fd
pin ttnn==0.68.0 for installs (the validated version)
moritztng Jul 3, 2026
52986c4
readme: trim install to what matters (drop ttnn/driver plumbing)
moritztng Jul 3, 2026
2afb591
perf(sram): L1-residency for grid transform + RMSNorm concats (ports …
moritztng Jul 6, 2026
1ddac8a
perf: vectorize host Wigner pack/scatter_coef + fuse rotation accumul…
moritztng Jul 6, 2026
0459532
perf: radial-MLP backward on device (kill the host radial finish) — p…
moritztng Jul 6, 2026
977e5e0
perf: fold backward transpose into matmul via transpose_b (ports qb1 …
moritztng Jul 6, 2026
54add0f
perf: pre-convert host tensors to bf16 before from_torch in trace ref…
moritztng Jul 6, 2026
8e9bd2f
perf: cache RMSNorm/Gate VJP internals + L1-resident grid_bw backward…
moritztng Jul 6, 2026
844c062
perf: vectorize host Wigner z-rotation (diag_embed + flip) — ports qb…
moritztng Jul 6, 2026
ab99687
perf: read back only the gaussian block of the x_edge force adjoint (…
moritztng Jul 6, 2026
5943228
fix(trace): emit uncovered-coordinate zero blocks on device, not ttnn…
moritztng Jul 6, 2026
add36a6
perf(scale): linear O(E) edge->node scatter-add, replacing the dense …
moritztng Jul 6, 2026
940f9e8
perf(reshape): route TILE 3D<->flat coeff-dim reshapes via ROW_MAJOR
moritztng Jul 6, 2026
53cc44f
perf(rotate_bw): fuse g_in accumulate into addcmul (mirror forward ro…
moritztng Jul 6, 2026
ec45732
perf(rotate_bw): coefficient-adjoint reductions as one GEMM (segment-…
moritztng Jul 6, 2026
d178079
perf(geometry): index_select for the permutation m-mapping (kill the …
moritztng Jul 6, 2026
a5ca796
perf(trace): gather refresh coefficients at the cached sparsity pattern
moritztng Jul 6, 2026
036e894
perf(rotate): split coef into per-nonzero columns in one dispatch
moritztng Jul 6, 2026
2d70d99
perf(rotate): split the input/grad column blocks in one dispatch
moritztng Jul 6, 2026
42ba099
feat(rotate): custom fused Wigner-rotation ttnn kernel (13.9x isolate…
moritztng Jul 6, 2026
bdbd333
perf(rotate): route forward rotate + backward g_in through fused kernel
moritztng Jul 6, 2026
d24790d
perf(edge-degree): compute node init on device inside the trace (2.54…
moritztng Jul 6, 2026
8bc9c58
perf(trace): drop the dead 3D edge_envelope buffer (2.54x -> 2.81x @N…
moritztng Jul 6, 2026
b718d10
fix(rotate): gate fused kernel to L1-fitting shapes, MAC fallback for…
moritztng Jul 7, 2026
4002201
probe(rotate): env-gated gc ablation + per-nonzero-sum A/B flags (per…
moritztng Jul 7, 2026
39d428d
cleanup(rotate): drop rejected gc experiments (bf8/3d/per-nonzero-sum)
moritztng Jul 7, 2026
478c314
perf(rotate): route rotate_bw coefficient adjoint through fused_rotat…
moritztng Jul 7, 2026
d0b300b
perf(rotate): gate gc kernel to large graphs (E>=45000), avoid small-…
moritztng Jul 7, 2026
04db526
perf(so2): fuse whole SO(2) conv into one block-structured matmul
moritztng Jul 7, 2026
39ba041
perf(spectral): fuse SO3_Linear into one block-diagonal matmul
moritztng Jul 7, 2026
4a7e9cb
perf(so2): per-m fused matmuls, drop block-diagonal MAC blowup
moritztng Jul 7, 2026
100cda4
perf(scatter): use one-hot matmul scatter up to N=2048 (was 384)
moritztng Jul 7, 2026
88c56bd
perf(norm): flat-layout RMSNormSH (kill 3D coeff tile-pad)
moritztng Jul 7, 2026
ed5da27
perf(readback): slice x_edge adjoint to gaussian block before readback
moritztng Jul 7, 2026
4c01653
perf(refresh): store x_edge ROW_MAJOR, tilize on device in RadialMLP
moritztng Jul 7, 2026
f4f6783
perf(gate): expand gate rows via one matmul (0/1 selector) instead of…
moritztng Jul 7, 2026
48c1dfa
perf(spectral): fully-flat SpectralAtomwise (gate via matmul-expand)
moritztng Jul 7, 2026
830e0df
perf(edgewise-bw): deinterleave src/tgt channels AFTER scatter (N-siz…
moritztng Jul 7, 2026
21e3389
perf(so2): bake real/imag duplication into radial net.6 weight (emit …
moritztng Jul 7, 2026
e5ecc0e
perf(refresh): store rotation coef ROW_MAJOR, tilize on device
moritztng Jul 7, 2026
f517126
perf(radial-bw): route LN backward through custom fused_ln_bw kernel
moritztng Jul 7, 2026
5deba37
perf(radial-bw): fold SiLU-bw + LN affine scale into fused_ln_bw kern…
moritztng Jul 7, 2026
a29c4cf
perf(gate): route gate fwd/bw glue through custom fused_gate kernel
moritztng Jul 7, 2026
b1293a7
perf(edgewise): bf8_b edge dataflow (opt-in TT_ATOM_BF8_EDGE) — bandw…
moritztng Jul 7, 2026
322798c
perf(bf8-edge): emit so2 radial multiplier directly in bf8 (drop boun…
moritztng Jul 7, 2026
309a219
perf(bf8-edge): cache the bf8 coef_exp typecast (was 16x/step -> 2x/s…
moritztng Jul 7, 2026
26b7f0a
perf(refresh): move envelope tilize+bf8-cast off host onto device trace
moritztng Jul 7, 2026
3c2dba3
perf(batch): force linear O(E) scatter for disjoint-union batches (+1…
moritztng Jul 8, 2026
00f0291
fix(bf8-edge): uma-m rotation crashed under bf8-edge (mixed-dtype add…
moritztng Jul 8, 2026
d1341f2
perf(batch): opt-in trace for evaluate_batch (batched-MD ensemble thr…
moritztng Jul 8, 2026
d21afb5
fix(calculator): propagate charge/spin from UMA/from_uma; trace stres…
moritztng Jul 8, 2026
ac345b3
fix(cli): default relax/md charge+spin to the bundle's baked values
moritztng Jul 8, 2026
a6cb626
fix(batch): validate batched systems share the bundle's baked composi…
moritztng Jul 8, 2026
8e81b08
docs: fix bundle-build command and license statement
moritztng Jul 8, 2026
38fff26
Merge docs/audit-2026-07-08
moritztng Jul 8, 2026
c6ae9df
chore(release): versioned releases, CI/release workflows, on-hardware…
moritztng Jul 8, 2026
4103eab
chore(release): versioned releases + CI + on-hardware release gate + …
moritztng Jul 8, 2026
2902c47
chore(release): drop PYPI_ENABLED gate; PyPI publish is naturally gat…
moritztng Jul 8, 2026
59619cf
feat: custom-kernel-only uma-s build (always-on fused_rotate, no fall…
moritztng Jul 8, 2026
72184ec
docs: honest perf numbers (rotation 4.3x vs MAC, not 14x) + custom-ke…
moritztng Jul 8, 2026
d9105a6
feat: custom-kernel-only uma-s build (fused_rotate always-on, no fall…
moritztng Jul 8, 2026
b56b1a2
ci(release): parse pyproject version without tomllib (runner is py3.10)
moritztng Jul 8, 2026
f8028e8
ci(release): tt-atom ships via GitHub Releases only, not PyPI (custom…
moritztng Jul 8, 2026
d1229fc
ci: rename release workflow release.yml -> release.yaml to match the …
moritztng Jul 9, 2026
4d8b24e
docs: simplify install to clone-and-build the moritztng/tt-atom-kerne…
moritztng Jul 9, 2026
5dd8628
docs: install uses tt-metal branch moritztng/tt-atom (rename from -ke…
moritztng Jul 9, 2026
1db2522
docs: replace manual .so copy with tt-metal's own pip install -e .
moritztng Jul 9, 2026
233771d
docs: replace manual .so copy with tt-metal's own pip install -e .
moritztng Jul 9, 2026
d7d703b
docs: uma-l does not exist upstream; fix stale uma-m-1p1 support claims
moritztng Jul 9, 2026
2c59966
Merge wk/ttatom-uma-l-support: fix README to match actual uma-m/uma-l…
moritztng Jul 9, 2026
4a99182
ci(release): fix stale header comment claiming a PyPI publish job
moritztng Jul 9, 2026
b8a2e2c
Merge docs/audit-2026-07-09
moritztng Jul 9, 2026
43e981b
test+bench+docs: verify multi-card fan-out on qb2 (4x P150)
moritztng Jul 9, 2026
771d879
Merge wk/ttatom-qb2-multicard-fanout: multi-card fan-out parity-verif…
moritztng Jul 9, 2026
509cc76
uma-s-1.2 support: charge_balanced_channels + fp32 energy head
janweinreich Jul 8, 2026
5f06a6f
chore: explicitly track tt_atom/assets/*.pt (vendored Wigner-D table)
janweinreich Jul 8, 2026
497141a
fix: smooth quaternion edge frame + generic eval rotation
janweinreich Jul 8, 2026
d564ed3
fix: fp32 radial-MLP backward (recovers forces on OOD heavy compresse…
janweinreich Jul 8, 2026
b280a19
test: gated uma-s-1.2 real-weight parity + local-checkpoint golden gen
janweinreich Jul 8, 2026
deb0288
docs: uma-s-1.2 validation report + README usage note
janweinreich Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: ci

# Host-only checks. TT hardware is not available on GitHub runners, so device / accuracy /
# OOM / perf tests are NOT run here — they gate releases on real cards before a tag is cut
# (see RELEASING.md). This job proves the package builds, is publishable, and imports
# without a card.

on:
pull_request:
push:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Build sdist + wheel
run: |
python -m pip install --upgrade build twine
python -m build
- name: Check package metadata
run: python -m twine check dist/*
- name: Import check (no card, no heavy deps)
run: |
python -m pip install --no-deps dist/*.whl
python -c "import tt_atom; print('tt_atom', tt_atom.__version__)"
66 changes: 66 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: release

# Cut a release by pushing a version tag AFTER the on-hardware release gate is green
# (accuracy parity, no OOM across the supported size range, no perf regression — see
# RELEASING.md):
# git tag v0.1.0 && git push origin master --tags
# This builds the artifacts and publishes a GitHub Release with notes + wheel. No PyPI
# job: tt-atom requires a source tt-metal/ttnn build, so a pip wheel can't run standalone
# (see the NOTE below and RELEASING.md).

on:
push:
tags: ["v*"]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Tag must match pyproject version
run: |
tag="${GITHUB_REF_NAME#v}"
ver="$(grep -m1 -E '^version *=' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')"
if [ "$tag" != "$ver" ]; then
echo "::error::tag v$tag != pyproject version $ver"; exit 1
fi
- name: Build
run: |
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/*

github-release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Create GitHub Release (notes from CHANGELOG section)
env:
GH_TOKEN: ${{ github.token }}
run: |
ver="${GITHUB_REF_NAME#v}"
awk -v v="$ver" '/^## /{p=($0 ~ "\\["v"\\]")} p' CHANGELOG.md > NOTES.md || true
if [ -s NOTES.md ]; then
gh release create "$GITHUB_REF_NAME" dist/* --title "$GITHUB_REF_NAME" --notes-file NOTES.md
else
gh release create "$GITHUB_REF_NAME" dist/* --title "$GITHUB_REF_NAME" --generate-notes
fi

# NOTE: tt-atom is distributed via GitHub Releases only, NOT PyPI. It is the
# custom-kernel-only build and requires a source tt-metal/ttnn build (see RELEASING.md
# / README Install), so a `pip install tt-atom` wheel can't run standalone — publishing
# to PyPI would be misleading. (tt-bio, which is genuinely pip-installable, keeps PyPI.)
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,26 @@ __pycache__/
checkpoints/
assets/*.npz
.DS_Store

# Generated benchmark artifacts (committed charts live in assets/; raw data does not)
benchmarks/results/

# Golden parity fixtures ARE committed (small, our own random weights, not gated). Allow them:
!tests/data/
!tests/data/*.npz

# Vendored fairchem Wigner-D coefficient table is a needed source asset (not a checkpoint). Allow it:
!tt_atom/assets/
!tt_atom/assets/*.pt

# ttnn runtime artifacts
generated/

# packaging
*.egg-info/

# benchmark outputs (machine-specific, regenerated)
benchmarks/results/
dist/
build/
*.egg-info/
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Changelog

All notable changes to TT-Atom are recorded here. Versioning is [SemVer](https://semver.org);
releases are cut only from a commit that has passed the on-hardware release gate — accuracy
parity, no OOM across the supported size range, and no perf regression (see `RELEASING.md`).

## [0.1.0] - 2026-07-08

Initial release. The **custom-kernel-only, highest-performance build for `uma-s`** — the per-edge
Wigner rotation runs as a custom tt-metal kernel, so `ttnn` comes from a source tt-metal build
that includes the op (see README "Install"); there is no slow fallback path.

### Added
- Tenstorrent inference for Meta **UMA** (eSEN / eSCN-MD) equivariant ML interatomic potentials:
energy, conservative analytic forces, and stress for molecules and periodic materials, behind an
**ASE** calculator that mirrors fairchem's (moving off fairchem is a one-line change). Validated
against the released `uma-s-1`.
- Device-resident trace loop for MD / relaxation; multi-card data-parallel throughput path.
- `tt-atom verify` device round-trip check and a one-command checkpoint converter.

### Performance (uma-s-1, Blackhole p150a)
- Fused-rotation kernel: **4.3×** vs the addcmul MAC in isolation (7.01 → 1.62 ms, PCC 0.999995);
**1.4–1.68× faster end-to-end** traced MD/relax across N=54–2662, no regression at any size.
- Accuracy (vs fairchem reference): energy rel-error ≤ 5.4e-4, force PCC ≥ 0.9996 across
molecular / periodic / slab; traced == eager (PCC 1.0). pytest 51 passed / 1 skipped.

### Scope
- `uma-s` (lmax=mmax=2) is the supported target. Other checkpoints (e.g. `uma-m`) raise a clear
error rather than silently falling back. `ttnn` is not a pip dependency (source build required).
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Moritz Thüning

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
177 changes: 175 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,178 @@
# TT-Atom

High-performance Tenstorrent inference for eSEN/eSCN-MD (UMA-family) ML interatomic potentials.
![Caffeine molecular dynamics, uma-s-1 on a Tenstorrent Blackhole card](assets/caffeine_md.gif)

_Under construction._
Run Meta's [UMA](https://huggingface.co/facebook/UMA) interatomic potential on [Tenstorrent](https://tenstorrent.com). Energy, forces and stress for molecules and periodic materials, behind an [ASE](https://wiki.fysik.dtu.dk/ase/) calculator. Bring your own UMA checkpoint.

## Install

TT-Atom is the custom-kernel-only, highest-performance build for `uma-s`. Its per-edge Wigner rotation runs as a custom tt-metal kernel that the pip `ttnn` wheel does not carry, so `ttnn` comes from a **source tt-metal build**. The op is pre-integrated on the [`moritztng/tt-atom`](https://github.com/tenstorrent/tt-metal/tree/moritztng/tt-atom) branch of tt-metal, so the build is a plain clone-and-build — no patching. You need a Tenstorrent card and its driver.

**1. Build and install tt-metal with the op** (branch `moritztng/tt-atom`):

```bash
git clone --recursive -b moritztng/tt-atom https://github.com/tenstorrent/tt-metal.git
cd tt-metal
export TT_METAL_HOME=$PWD
./build_metal.sh --build-type Release # full build (tens of minutes)
pip install -e . # tt-metal's own dev-install path
```

The branch is the validated base `b5522097b39` plus the `fused_rotate` op library — nothing else. Its source and contract are mirrored in [`custom_kernels/README.md`](custom_kernels/README.md) as the authoritative backup (and for re-integrating onto a newer tt-metal commit).

`TT_METAL_HOME` must stay exported at **runtime** too — the JIT-compiled kernels load from `$TT_METAL_HOME/build_Release`, so don't delete that directory after installing.

On some boards/firmware this base commit's UMD misreads the board ID as a dual-chip P300 (`Board ... has 1 chips, but expected 2 chips for board type p300` -> `TT_FATAL: Custom fabric mesh graph descriptor path must be specified for CUSTOM cluster type`), which blocks opening *any* device, single-card included. If you hit that, export `TT_MESH_GRAPH_DESC_PATH=$TT_METAL_HOME/tt_metal/fabric/mesh_graph_descriptors/p150_mesh_graph_descriptor.textproto` before opening a device — this also needs to be set in the parent process before constructing `tt_atom.batch.MultiCard`, since its per-card worker processes inherit it.

**2. Install TT-Atom into the same venv:**

```bash
git clone https://github.com/moritztng/tt-atom.git
pip install -e ./tt-atom # numpy<2, torch (CPU), ase — NOT ttnn
```

**3. Verify the op is loaded:**

```bash
python -c "import ttnn; e=ttnn._ttnn.operations.experimental; print(hasattr(e,'fused_rotate'), hasattr(e,'fused_rotate_gc'))" # -> True True
```

`uma-s` (lmax=mmax=2) is the validated target; other checkpoints (e.g. uma-m) raise a clear error. `import tt_atom` never imports ttnn, so it imports fine on a machine without a card.

## Quickstart

```bash
tt-atom run structure.xyz
```

```python
from ase.io import read
from tt_atom import UMA

atoms = read("structure.xyz")
atoms.calc = UMA(atoms)
atoms.get_potential_energy()
atoms.get_forces()
```

`UMA(atoms)` uses `uma-s-1`, infers the task (`omat` if the cell is periodic, else `omol`), and builds a device-resident model for that composition on first use. Later calls load it from cache. Everything downstream is plain ASE.

## Relax and MD

```bash
tt-atom run structure.xyz --relax --out relaxed.xyz
tt-atom run structure.xyz --md --steps 200 --temp 300
```

Add `--trace` (or `UMA(atoms, trace=True)`) to replay the captured device graph over the loop. About 2x on relax/MD, forces stay bit-identical.

## What it supports

- Models: `uma-s-1` (default), `uma-s-1.2`. See [Model coverage](#model-coverage) for what else
exists upstream and why this build doesn't run it.
- Tasks: `omol`, `omat`, `oc20`, `odac`, `omc`.
- Systems: isolated molecules and periodic cells. Charge and spin via `UMA(atoms, charge=-1, spin=2)`.
- Properties: energy, conservative analytic forces, and stress, so variable-cell relaxation works (see [`examples/relax_cell.py`](examples/relax_cell.py)).

## Model coverage

Meta has released two UMA sizes: `uma-s-1` (`.1`/`.2`) and `uma-m-1p1` — there is no `uma-l`. The
[paper](https://arxiv.org/abs/2506.23971) scales capacity via mixture-of-linear-experts on the
small and medium models rather than shipping a third, larger dense tier, and
[facebook/UMA](https://huggingface.co/facebook/UMA) carries checkpoints for only those two.

Of the two that exist, only `uma-s` runs on this build (both `uma-s-1` and `uma-s-1.2`). `uma-s` is
square (lmax=mmax=2), so its per-edge Wigner rotation is a 9x9 tile that fits the fused kernel's L1 CB
budget. `uma-m-1p1` uses mmax<lmax spherical-harmonic subselection, so its rotation is rectangular
(25<->19, W=256) — that overflows the kernel's L1 budget, and this build has no MAC fallback, so it
raises a clear `RuntimeError` naming the shape rather than silently running slow or wrong
(`tests/test_umam.py` anchors this contract). A hypothetical `uma-l`, sized above `uma-m`, would
need L1 headroom `uma-m` already overflows, so it isn't a new question, just a bigger version of
the one above — and moot, since the checkpoint doesn't exist to test it against.

### uma-s-1.2

`uma-s-1.2` adds fairchem's charge-balanced channels: the `l=0` charge channels are re-balanced to the system charge after every block. TT-Atom applies this automatically — point `UMA` at the checkpoint (gated; bring your own):

```python
atoms.calc = UMA(atoms, checkpoint="uma-s-1p2.pt")
```

Parity with fairchem — forces, energy, and stress across 757 molecular and periodic systems, plus a CPU throughput comparison — is written up in [`docs/uma-s-1p2-validation.md`](docs/uma-s-1p2-validation.md).

## Accuracy

Every task is checked on-device against the released `uma-s-1` checkpoint run through fairchem on the same structure.

| task | system | energy rel. err | force PCC | stress PCC |
|------|--------|----------------:|----------:|-----------:|
| omol | ethanol | 2e-7 | 0.9996 | |
| omat | bulk Si | 3e-4 | 0.99999 | 0.99999 |
| oc20 | Cu(100) + H slab| 9e-5 | 1.0000 | |
| odac | MgO framework | 2e-4 | 0.99999 | |
| omc | solid CO2 | 8e-5 | 1.0000 | |

Dynamics are stable: NVE energy drift is about 1 meV/atom/ps. These numbers are from `ttnn` 0.68.0. Op numerics can shift slightly between `ttnn` versions, so confirm parity on the version you actually run:

Reproduce it yourself. Every bundle embeds the fairchem reference energy and forces from build time, so:

```bash
tt-atom verify model.npz # device output vs the embedded fairchem reference
pytest tests/ # full parity suite against fairchem goldens
```

## Throughput

Batch independent systems into a single device pass:

```python
out = calc.evaluate_batch(list_of_atoms) # out["energy"], out["forces"]
```

For many small molecules this is roughly 13x over looping on one card. To use several cards, fan systems across them with `tt_atom.batch` (one process per card).

For a **batched MD ensemble / relaxation** — K fixed-composition replicas evolving with a stable neighbour list — add `trace=True` to capture the batched device graph once and replay it (forces stay bit-identical; it re-captures whenever the neighbour list changes):

```python
out = calc.evaluate_batch(replicas, trace=True) # per-step in the ensemble loop
```

At small per-system sizes the eager batched forward is host-dispatch-bound below saturation, so the trace lets a *modest* ensemble reach near-peak throughput: measured on one p150 (uma-s-1, 9-atom molecules) K=4 gives 4.2x (59→246 systems/s), K=16 2.6x (207→528 sys/s) — approaching the K≥128 eager device-bound plateau (~700 sys/s) at a fraction of the batch size. Leave it `False` for one-shot screening, where a fresh batch each call would re-capture every time.

## Compared to fairchem

TT-Atom is an inference runtime, not a rewrite of fairchem. It reuses the released weights and matches them.

| | fairchem | TT-Atom |
|--|:--------:|:-------:|
| Hardware | GPU, CPU | Tenstorrent |
| Energy, forces, stress | ✅ | ✅ |
| Molecules, periodic (PBC) | ✅ | ✅ |
| Tasks (omol/omat/oc20/odac/omc) | ✅ | ✅ |
| Models | uma-s, uma-m | uma-s-1, uma-s-1.2 |
| ASE relax and MD | ✅ | ✅ (plus a traced loop) |
| Batched inference | ✅ | ✅ (one composition per batch) |
| LAMMPS interface | ✅ | ❌ |
| Training, fine-tuning | ✅ | ❌ (inference only) |

## Bundles and the reference environment

The model is a "bundle": UMA weights merged for one composition. `UMA(atoms)` builds and caches bundles for you, so most users never touch this. To build one yourself:

```bash
refenv/bin/python tools/export_weights.py --uma-s-1 --xyz structure.xyz --task omol --out model.npz
```

then `TTAtomCalculator("model.npz")`.

Building a bundle needs `fairchem` to read the checkpoint and merge the experts. `fairchem` wants `numpy>=2`, which cannot share a process with `ttnn`'s `numpy<2`, so keep it in its own venv:

```bash
python -m venv refenv && refenv/bin/pip install "fairchem-core>=2.10"
```

`UMA(atoms)` and `tt-atom run` call it automatically the first time they see a new composition, then cache the result. Set `TT_ATOM_REFENV` to its python if it is not found automatically. Cached runs never need it.

## License

MIT for this code, which reimplements the UMA / eSCN-MD architecture from [fairchem](https://github.com/facebookresearch/fairchem) (also MIT). It depends on `ttnn` (Apache-2.0) and `ase` (LGPL-2.1+). The UMA weights are separately licensed under the [FAIR Chemistry License](https://huggingface.co/facebook/UMA), are gated, and are not included. Bring your own.
Loading