Metadata-Version: 2.4
Name: atomli
Version: 0.1.4
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Dist: numpy>=1.24
Requires-Dist: ase==3.29.0 ; extra == 'test'
Requires-Dist: pytest>=7 ; extra == 'test'
Requires-Dist: pytest-cov>=4 ; extra == 'test'
Requires-Dist: tomli>=2 ; python_full_version < '3.11' and extra == 'test'
Requires-Dist: packaging>=24 ; extra == 'test'
Provides-Extra: test
License-File: LICENSE
Summary: ASE drop-in Python API for atomistic simulation (binary extension)
Keywords: ase,atoms,md,optimize,materials
Author: atomag
License: PolyForm-Noncommercial-1.0.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://atom.li/docs/
Project-URL: Repository, https://github.com/atomag/atomli-assets

# atomli

ASE-shaped Python API for atomistic simulation.

```python
import atomli as ase
from atomli import Atoms
from atomli.optimize import BFGS, FIRE2
from atomli.md.verlet import VelocityVerlet
from atomli.calculators.lj import LennardJones
from atomli.build import molecule
from atomli.io import read, write
```

### Calculators

| Class | Backend |
|-------|---------|
| `LennardJones` | classical LJ |
| `DFTD3` | DFT-D3 dispersion |
| `XTB` | tight-binding (GFN2-xTB, g-xTB) |
| `MLIP` | nequix / NequIP — catalog id or `.nqx` path |
| `QC` | molecular and 3D-periodic DFT (PBE, r2SCAN, …), including stress |

### QC electronic state and native GPU packages

QC exposes retained spin/k-point orbitals, occupations, overlap/density matrices,
ASE spectral getters, DOS/PDOS, molecular/PBC HDF5 export and molecular Molden/WFN.
`kernel(dm0=...)`, transactional `set(scf=...)` and independent `with_settings(...)`
provide finer control alongside ordinary ASE energy/force calls. See the
[QC API and native-delivery guide](docs/qc-api.md) for shapes, units, IO limits and
hardware-specific zero-JIT GPU packages.

### Global device selection

Atomli has one optional process-global compute-device override. With no override,
MLIP uses a real GPU when Atomli's production WGPU backend discovers one, and QC
uses GPU only when a stricter QC readiness probe can open that adapter with the
packaged GPU runtime. Both fall back to native CPU without error. g-xTB remains
CPU-first and prints one informational hint per process when usable hardware is
visible: "Try out running on GPU! It could be faster!" `"gpu"` explicitly chooses
a hardware GPU; explicit adapters can pin a particular hardware card:

```python
import atomli
from atomli.calculators import MLIP, QC, XTB

atomli.gpu.set_default_device("gpu")

xtb = XTB(method="gxtb")
mlip = MLIP("nequix-mp-1")
qc = QC(xc="PBE")
```

A physical GPU can be selected once for the whole process. Adapter discovery
is also global, and PCI identity is preferred when available:

```python
from atomli import gpu

for adapter in gpu.adapters():
    print(adapter.index, adapter.name, adapter.kind, adapter.backend, adapter.pci_bus_id)

# Pin a particular hardware card.
gpu.set_default_device(gpu.adapters()[0])
# Equivalent hardware selector forms include "gpu:0" and "gpu:pci:0000:61:00.0".
```

`gpu.adapters()` returns hardware adapters only. CPU-backed Vulkan implementations
such as llvmpipe are deliberately filtered out and are not a supported release
backend. `"gpu"` is the canonical public spelling and `"wgpu"` remains an accepted
alias. An explicit calculator
`device=` overrides the process default, so a single calculation can still be
forced to CPU. GFN2-xTB stays on CPU because that method has no GPU execution
route. `atomli.gpu.set_default_device(None)` clears the process override and
restores automatic per-calculator selection. An explicit global or calculator
`device="cpu"` always disables auto-GPU. Set `ATOMLI_GPU_HINT=0` to suppress the
one-time g-xTB hint in batch jobs or other quiet environments.

On macOS, discovery uses the system Metal backend, so Apple Silicon needs no
Vulkan installation. On Linux, discovery uses Vulkan: a working loader plus the
GPU vendor's Vulkan driver/ICD must be present for hardware to be selected. If
that stack is missing or only a CPU software adapter such as llvmpipe is visible,
Atomli simply keeps the calculation on CPU. Windows currently uses Atomli's
Vulkan backend as well; a future DX12 product path can remove that optional
Vulkan dependency for MLIP without changing this fallback policy.

### MLIP models (runtime download + cache)

Weights are **not** on PyPI and **not** inside the wheel. `pip install atomli`
only installs the library; the first `MLIP("nequix-mp-1")` downloads the file
into a local cache and reuses it later.

| Order | Source |
|-------|--------|
| 0 | Local cache (see the cache table below) |
| 1 default | [tako.atom.li/calculation-wasm/](https://tako.atom.li/calculation-wasm/) |
| 2 backup | GitHub Release [`mlip-models`](https://github.com/atomag/atomli-assets/releases/tag/mlip-models) |

Cache directory, first match wins:

| Setting | Directory |
|---------|-----------|
| `ATOMLI_MODELS_DIR` set | that path, verbatim |
| else `XDG_CACHE_HOME` set | `$XDG_CACHE_HOME/atomli/models` |
| else macOS | `~/Library/Caches/atomli/models` |
| else Linux and other platforms | `~/.cache/atomli/models` |

`default_models_dir()` returns the resolved path.

Catalog (runtime download + cache):

| Id | Runtime |
|----|---------|
| `nequix-mp-1`, `nequix-mp-1-pft`, `nequix-mp-1-pft-nocotrain` | nequix |
| `nequix-oam-1`, `nequix-oam-1-pft`, `nequix-omat-1` | nequix |
| `nequip-s`, `nequip-l` | nequip |
| `equiformer`, `equiformer-gradient` | equiformer |

```python
from atomli.calculators import MLIP
from atomli.models import list_models, ensure_model, default_models_dir

print(list_models())                 # all ids
print(list_models(runtime="nequip")) # filter
print(default_models_dir())          # cache dir
atoms.calc = MLIP("nequix-mp-1")
atoms.calc = MLIP("nequip-s")
atoms.calc = MLIP("equiformer")
```

```bash
# optional prefetch into the same cache MLIP() uses
./scripts/download-mlip-models.sh
# maintainers: publish weights to the GitHub Release (not to PyPI)
./scripts/upload-mlip-models.sh
```

Native atomli calculators attach directly to ASE `Atoms`; ASE calculators are
adapted automatically when assigned to atomli `Atoms`:

```python
# atomli calculators on ASE-built structures and ASE drivers
from ase.build import molecule
from ase.optimize import BFGS
from atomli.calculators import XTB

atoms = molecule("H2O")
atoms.calc = XTB(method="gfn2")
print(atoms.get_potential_energy())
print(atoms.get_forces())
BFGS(atoms).run(fmax=0.05)

# ASE calculators on atomli Atoms (auto-wrapped)
from atomli import Atoms as AtomliAtoms
from ase.calculators.lj import LennardJones

a = AtomliAtoms("Ar2", positions=[[0, 0, 0], [3.8, 0, 0]])
a.calc = LennardJones(epsilon=0.0103, sigma=3.4)
print(a.get_potential_energy())
```

Periodic QC is enabled explicitly through `settings`. By default, Rust derives
the FFT mesh from a 100 Ha cutoff and each Monkhorst-Pack dimension from
`kpt_i * cell_length_i >= 15` with lengths in Å:

```python
from ase.build import bulk
from atomli.calculators import QC

atoms = bulk("C", "diamond", a=3.57)
atoms.calc = QC(settings={"periodic": True})
print(atoms.get_stress())  # ASE Voigt order, eV / Å³
```

Explicit `mesh=[nx, ny, nz]` and `kpoints=[kx, ky, kz]` entries in the same
mapping override the derived grids. Stress is defined only for
three-dimensionally periodic QC; requesting it from molecular QC raises the
normal ASE property error.

Rust calculators implement ASE's normal retained-calculator workflow, including
`ase.optimize`, ASE molecular dynamics, ASE constraints, mixed periodic-axis
masks, and repeated property calls. Electronic state is read from explicit
`charge` / `unpaired` calculator options, then `atoms.info`, then ASE initial
charge and magnetic-moment arrays.

## Install (binary wheels)

**Large artifacts (wheels / native `_core`) are published as
[GitHub Release](https://github.com/atomag/atomli-assets/releases) assets only.**

- Not stored in git
- Not Git LFS
- Not built or uploaded by GitHub Actions

### From a Release (recommended)

```bash
# list releases
gh release list --repo atomag/atomli-assets

# download + install the wheel that matches this machine
./scripts/download-wheel.sh --install
# or pin a tag:
./scripts/download-wheel.sh v0.1.2 --install
```

Direct `pip` from a release asset URL:

```bash
pip install "https://github.com/atomag/atomli-assets/releases/download/v0.1.2/atomli-0.1.2-….whl"
```

### From PyPI (when published)

```bash
pip install atomli
```

Same rule: PyPI gets **wheels only** (py + compiled extension). No sdist that
needs the private Rust tree.

## What this repository is

Public package surface:

| In git | On GitHub Releases |
|--------|--------------------|
| pure Python packaging (`atomli/*.py`) | platform wheels (`.whl`) |
| tests, docs, packaging metadata | compiled `atomli._core` inside those wheels |

No Rust simulation backend here. Backend sources stay private in the monorepo.

| Path | Role |
|------|------|
| **this repo (`atomli`)** | Python package + release wheels |
| `atom.li` | Frontend landing page only — not this package |

Docs: https://atom.li/docs/  ·  Releases: https://github.com/atomag/atomli-assets

## Documentation

The static Astro documentation site lives in `docs-site/`. It follows ASE's
Python-oriented topic structure and can refresh its API reference from an
installed upstream extension.

```bash
cd docs-site
bun install
bun run dev --host 0.0.0.0
```

See `docs-site/README.md` for the static build and API generation commands.

## Layout

```
atomli/                 # importable package (py in git; binary only in wheels)
tests/
scripts/download-wheel.sh
pyproject.toml          # setuptools metadata; no maturin/Rust
```

## Maintainers: cut a release

Build on a machine that has the private monorepo, then attach wheels to a
**GitHub Release** with the `gh` CLI (not Actions, not LFS):

```text
code/
  ase.rs/     # private: crates/atomli-py + engine
  atomli/     # this repo
```

```bash
# from ase.rs — builds wheel(s) and uploads them as release assets
./scripts/release-atomli.sh v0.1.0

# rebuild / add more platform wheels later
./scripts/release-atomli.sh v0.1.0   # --clobber re-uploads
```

Wheels contain only:

- `atomli/*.py`
- `atomli/_core*.so` (or platform equivalent)
- `atomli/compatibility/ase-3.29.toml`

No `.rs`, no `Cargo.toml`, no backend tree.

Optional PyPI mirror of the same wheels:

```bash
twine upload dist/atomli-*.whl   # wheels only, never sdist
```

## Development install (sibling monorepo)

```bash
cd ../ase.rs
./scripts/develop-atomli.sh
pytest ../atomli/tests -q
```

## Version

`atomli.__version__` comes from the extension when present.

