Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ examples/screening_results/
/examples/example_input_xyzs/primary-amines_library/
/examples/example_input_xyzs/kraken_library/

uv.lock
/.claude/

# Local-only benchmark scripts (kept out of the repo per request)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ When a run is started from SMILES, an RDKit/MMFF force field (via nvMolKit on GP
`model_modal` fidelity (see [docs/config.md](docs/config.md)).
- D3 dispersion correction: off by default. Set `"d3_correction": true` to
enable DFT-D3(BJ) for models trained without dispersion. It is rejected for
`uma-s-1p2p1-ft-d4rev` and logs a warning for the other UMA models and ORB
`*_omol` models (see [docs/config.md](docs/config.md)).
`uma-s-1p2p1-ft-d4rev` and logs a warning for the other UMA models, ORB
`*_omol` models and SevenNet `omol25_*` modals (see [docs/config.md](docs/config.md)).

## License
MIT License (see LICENSE)
10 changes: 8 additions & 2 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ GPUMA and reports back. You don't need to know Python.

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh # only if uv is missing
uv tool install gpuma
uv tool install gpuma --python 3.12 \
--index https://download.pytorch.org/whl/cu126 --index-strategy unsafe-best-match
```

The extra index gives torch's CUDA 12.6 build, which runs on NVIDIA drivers
from 525 on. Without it uv installs PyPI's default torch build, which needs
driver 580 or newer.

3. **Install the skill** for your agents:

```bash
Expand Down Expand Up @@ -66,7 +71,8 @@ Reinstall the skill after each upgrade, so the agent's instructions match the
installed version:

```bash
uv tool upgrade gpuma
uv tool install --upgrade gpuma --python 3.12 \
--index https://download.pytorch.org/whl/cu126 --index-strategy unsafe-best-match
gpuma skills install
```

Expand Down
15 changes: 12 additions & 3 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ gpuma convert --smiles "CCO" --output examples/example_output/cli_no_optimizatio
gpuma generate --smiles "c1ccccc1" --conformers 5 --output examples/example_output/cli_conformer_generation.xyz --config examples/config.json

# Create or validate configuration files
gpuma config --create examples/config.json
gpuma config --create my_config.json
gpuma config --validate examples/config.json

# Verbose vs. quiet (set in config file)
Expand All @@ -61,8 +61,10 @@ gpuma smiles --smiles "CCO" --output examples/example_output/ethanol_opt.xyz --c

### Using ORB-v3 models

To use ORB-v3 instead of Fairchem UMA, either set `"model_type": "orb"` in
your config file (see `examples/config_orb.json`) or override from the CLI:
To use ORB-v3 instead of Fairchem UMA, set `"model_type": "orb"` and an ORB
`model_name` in your config file (see `examples/config_orb.json`).
`--model-type orb` only switches the backend; the config must still name an ORB
model, otherwise the run stops with exit code 3:

```bash
# Single optimization with ORB-v3
Expand Down Expand Up @@ -167,6 +169,13 @@ The summary contains:
| `diagnostics` | The engine diagnostic report (embedding and optimization events, convergence where known) |
| `error` | `{"type", "message"}` when the command failed, else `null` |
| `wall_time_s`, `gpuma_version`, `schema_version` | Run metadata |
| `checks`, `recommended_device` | `doctor` only: each check (`name`, `status`, `detail`, `hint`, `data`) and the GPU to use |
| `schema` | `config --schema` only: the JSON Schema of the config |
| `installed` | `skills install` only: the paths the skill was written to |

`config --show --json` puts the resolved config only in `config`. A usage error
(exit code 2) also prints a summary, with `error.type` `UsageError` and `device`,
`model` and `config` set to `null`.

| Exit code | Meaning |
| --- | --- |
Expand Down
14 changes: 7 additions & 7 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ For multi-modal checkpoints such as `7net-omni` or `7net-mf-ompa`, a

D3 is off by default and adds GPU memory per batch; enable it only for models
trained without dispersion. `uma-s-1p2p1-ft-d4rev` already includes dispersion
and rejects D3. The other UMA models and the ORB `*_omol` models are trained on
ωB97M-V, which also includes it, so gpuma logs a warning when D3 is enabled for
them.
and rejects D3. The other UMA models, the ORB `*_omol` models and SevenNet's
`omol25_*` modals are trained on ωB97M-V, which also includes it, so gpuma logs
a warning when D3 is enabled for them.

DFT-D3(BJ) dispersion correction is supported for the ORB-v3,
Fairchem/UMA, and SevenNet backends. Internally ORB uses orb-models' native
Expand Down Expand Up @@ -200,7 +200,7 @@ technical:
| `huggingface_token_file` | `null` | File path to read the HF token from |
| `d3_correction` | `false` | Enable D3 dispersion correction. Supported for the ORB, Fairchem/UMA, and SevenNet backends; rejected for `uma-s-1p2p1-ft-d4rev` |
| `d3_functional` | `"PBE"` | DFT functional for D3 correction (ORB/Fairchem use the orb-models BJ-damping table; SevenNet uses its own native D3) |
| `d3_damping` | `"BJ"` | Damping scheme for D3 correction (`"BJ"` or `"BJM"`) |
| `d3_damping` | `"BJ"` | Damping scheme for D3 correction (`"BJ"` or `"BJM"`). SevenNet supports only `"BJ"` |

### `conformer_generation`

Expand All @@ -213,8 +213,8 @@ technical:

| Parameter | Default | Description |
|---|---|---|
| `device` | `"cuda"` | `"cpu"`, `"cuda"`, or `"cuda:N"` (e.g. `"cuda:0"`). A GPU index that doesn't exist, or CUDA without a usable GPU, is an error |
| `max_memory_padding` | `0.95` | Fraction of GPU memory the autobatcher is allowed to fill during calibration. Lower = more headroom, smaller batches |
| `device` | `"cuda"` (`"cpu"` if no GPU is visible) | `"cpu"`, `"cuda"`, or `"cuda:N"` (e.g. `"cuda:0"`). A GPU index that doesn't exist, or CUDA without a usable GPU, is an error |
| `max_memory_padding` | `0.95` | Fraction of GPU memory the autobatcher is allowed to fill during calibration. Lower = more headroom, smaller batches. Must be between 0.05 and 0.95 |
| `memory_scaling_factor` | `1.75` | Factor by which the autobatcher grows the probe size during calibration. Larger = faster calibration but coarser final batch size; smaller = slower but tighter. A job runs as one batch without probing only if its forward-pass peak times this factor fits in `max_memory_padding` of free memory. Must be > 1 |
| `max_atoms_to_try` | `100000` | Upper bound on the autobatcher's calibration probe size (atoms) |
| `memory_scales_with` | `"auto"` | Per-structure memory metric for batch sizing: `"n_atoms"`, `"n_edges"` or `"auto"`. `"auto"` uses `n_atoms` for UMA, whose large fixed memory per structure makes edge counts overcharge small molecules, and `n_edges` for ORB and SevenNet |
Expand Down Expand Up @@ -278,7 +278,7 @@ instead be supplied via `model_path`.
| `7net-omni-i8` | SevenNet Omni i8 (multi-modal) |
| `7net-omni-i12` | SevenNet Omni i12 (multi-modal) |

> D3 dispersion correction can be enabled for any ORB, Fairchem/UMA, or SevenNet model with `d3_correction: true`, except `uma-s-1p2p1-ft-d4rev`. gpuma warns when it is enabled for UMA or ORB `*_omol` models.
> D3 dispersion correction can be enabled for any ORB, Fairchem/UMA, or SevenNet model with `d3_correction: true`, except `uma-s-1p2p1-ft-d4rev`. gpuma warns when it is enabled for UMA, ORB `*_omol` models or SevenNet `omol25_*` modals; SevenNet supports only BJ damping.

These model names are also available programmatically as
`gpuma.AVAILABLE_FAIRCHEM_MODELS`, `gpuma.AVAILABLE_ORB_MODELS`, and
Expand Down
5 changes: 3 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ considered experimental.
- **Using a `uv` virtual environment**
```powershell
# create and activate a fresh environment
uv venv .venv
uv venv .venv --python 3.12

# activate the environment

Expand Down Expand Up @@ -153,7 +153,8 @@ When a run is started from SMILES, an RDKit/MMFF force field (via nvMolKit on GP
- D3 dispersion correction: off by default. Enable it with
`"d3_correction": true` for models trained without dispersion. It is
rejected for `uma-s-1p2p1-ft-d4rev` and logs a warning for the other UMA
models and ORB `*_omol` models (see [Configuration](config.md)).
models, ORB `*_omol` models and SevenNet `omol25_*` modals (see
[Configuration](config.md)).

## License
MIT License (see LICENSE)
5 changes: 3 additions & 2 deletions examples/example_dispersion.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
import gpuma
from gpuma.config import load_config_from_file

OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "example_output")
HERE = os.path.dirname(__file__)
OUTPUT_DIR = os.path.join(HERE, "example_output")
os.makedirs(OUTPUT_DIR, exist_ok=True)

# A short alkane chain — D3 contributes a non-trivial attractive correction
Expand All @@ -55,7 +56,7 @@ def _build_config(
):
"""Build a Config for ``backend`` with D3 toggled and the requested mode."""
config_file = "config.json" if backend == "fairchem" else "config_orb.json"
cfg = load_config_from_file(config_file)
cfg = load_config_from_file(os.path.join(HERE, config_file))
if backend == "fairchem":
cfg.model.model_name = "uma-s-1p2p1"
cfg.model.d3_correction = d3
Expand Down
21 changes: 11 additions & 10 deletions examples/example_ensemble_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
import gpuma
from gpuma.config import Config, load_config_from_file

OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "example_output")
HERE = os.path.dirname(__file__)
OUTPUT_DIR = os.path.join(HERE, "example_output")
os.makedirs(OUTPUT_DIR, exist_ok=True)


Expand Down Expand Up @@ -52,10 +53,10 @@ def example_batch_from_multi_xyz():
"""Example 2: Batch optimize structures from a multi-structure XYZ file."""
print("\n=== Example 2: Batch optimization from multi-XYZ file ===")

input_file = "example_input_xyzs/butene_triplet_multi.xyz"
input_file = os.path.join(HERE, "example_input_xyzs/butene_triplet_multi.xyz")
output_file = os.path.join(OUTPUT_DIR, "python_batch_from_multi_xyz_optimized.xyz")

config = load_config_from_file("config.json")
config = load_config_from_file(os.path.join(HERE, "config.json"))
config.optimization.multiplicity = 3
results = gpuma.optimize_batch_multi_xyz_file(
input_file=input_file,
Expand All @@ -74,7 +75,7 @@ def example_batch_from_xyz_directory():
"""Example 3: Batch optimize structures from a directory of XYZ files."""
print("\n=== Example 3: Batch optimization from XYZ directory ===")

input_dir = "example_input_xyzs/multi_xyz_dir"
input_dir = os.path.join(HERE, "example_input_xyzs/multi_xyz_dir")
output_file = os.path.join(OUTPUT_DIR, "python_batch_from_directory_optimized.xyz")

results = gpuma.optimize_batch_xyz_directory(
Expand Down Expand Up @@ -105,7 +106,7 @@ def example_ensemble_from_smiles_orb():
print("\n=== Example 4: Ensemble optimization from SMILES (ORB-v3, batch) ===")

smiles = "CCC(CC)CCOOC(CC)CCOC"
config = load_config_from_file("config_orb.json")
config = load_config_from_file(os.path.join(HERE, "config_orb.json"))
config.conformer_generation.max_num_conformers = 50
config.optimization.force_convergence_criterion = 5e-1
# batch mode is the default in config_orb.json
Expand All @@ -130,10 +131,10 @@ def example_batch_from_multi_xyz_orb():
"""Example 6: Batch optimize structures from a multi-XYZ file (ORB-v3)."""
print("\n=== Example 6: Batch optimization from multi-XYZ file (ORB-v3) ===")

input_file = "example_input_xyzs/butene_triplet_multi.xyz"
input_file = os.path.join(HERE, "example_input_xyzs/butene_triplet_multi.xyz")
output_file = os.path.join(OUTPUT_DIR, "python_batch_from_multi_xyz_orb.xyz")

config = load_config_from_file("config_orb.json")
config = load_config_from_file(os.path.join(HERE, "config_orb.json"))
config.optimization.multiplicity = 3
results = gpuma.optimize_batch_multi_xyz_file(
input_file=input_file,
Expand All @@ -152,10 +153,10 @@ def example_batch_from_xyz_directory_orb():
"""Example 7: Batch optimize structures from a directory of XYZ files (ORB-v3)."""
print("\n=== Example 7: Batch optimization from XYZ directory (ORB-v3) ===")

input_dir = "example_input_xyzs/multi_xyz_dir"
input_dir = os.path.join(HERE, "example_input_xyzs/multi_xyz_dir")
output_file = os.path.join(OUTPUT_DIR, "python_batch_from_directory_orb.xyz")

config = load_config_from_file("config_orb.json")
config = load_config_from_file(os.path.join(HERE, "config_orb.json"))
results = gpuma.optimize_batch_xyz_directory(
input_directory=input_dir,
output_file=output_file,
Expand All @@ -178,7 +179,7 @@ def example_ensemble_from_smiles_orb_sequential():
print("\n=== Example 8: Ensemble optimization from SMILES (ORB-v3, sequential) ===")

smiles = "CCC(CC)CCOOC(CC)CCOC"
config = load_config_from_file("config_orb.json")
config = load_config_from_file(os.path.join(HERE, "config_orb.json"))
config.conformer_generation.max_num_conformers = 10
config.optimization.batch_optimization_mode = "sequential"
print(f"Generating conformers for {smiles} and optimizing with ORB-v3 (sequential)...")
Expand Down
11 changes: 6 additions & 5 deletions examples/example_single_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
import gpuma
from gpuma.config import load_config_from_file

OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "example_output")
HERE = os.path.dirname(__file__)
OUTPUT_DIR = os.path.join(HERE, "example_output")
os.makedirs(OUTPUT_DIR, exist_ok=True)


Expand All @@ -25,7 +26,7 @@ def example_optimize_from_smiles():
smiles = "C1=C[O+]=CC=C1CCCCCC"
print(f"Optimizing {smiles} ...")

cfg = load_config_from_file("config.json")
cfg = load_config_from_file(os.path.join(HERE, "config.json"))
cfg.optimization.multiplicity = 1

output_file = os.path.join(OUTPUT_DIR, "python_single_smiles_basic.xyz")
Expand Down Expand Up @@ -58,14 +59,14 @@ def example_optimize_from_xyz():
"""Example 2: Optimize a molecule from an XYZ file."""
print("\n=== Example 2: Single optimization from XYZ file ===")

input_file = "example_input_xyzs/butene_triplet.xyz"
input_file = os.path.join(HERE, "example_input_xyzs/butene_triplet.xyz")
output_file = os.path.join(OUTPUT_DIR, "python_single_xyz_basic.xyz")

if not os.path.exists(input_file):
print(f" Input file {input_file} not found")
return

cfg = load_config_from_file("config.json")
cfg = load_config_from_file(os.path.join(HERE, "config.json"))
cfg.optimization.charge = 0
cfg.optimization.multiplicity = 3

Expand All @@ -91,7 +92,7 @@ def example_optimize_from_smiles_orb():

# Load the ORB-specific config (sets model_type="orb",
# model_name="orb_v3_direct_omol")
cfg = load_config_from_file("config_orb.json")
cfg = load_config_from_file(os.path.join(HERE, "config_orb.json"))
cfg.optimization.multiplicity = 1

output_file = os.path.join(OUTPUT_DIR, "python_single_smiles_orb.xyz")
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ site_author: "Niklas Hölter"
site_url: "https://NiklasHoelter.github.io/gpuma/"
repo_url: https://github.com/NiklasHoelter/gpuma
repo_name: NiklasHoelter/gpuma
edit_uri: edit/main/docs/ # Allows users to suggest edits directly via GitHub
edit_uri: edit/master/docs/ # Allows users to suggest edits directly via GitHub

theme:
name: "material"
Expand Down
6 changes: 3 additions & 3 deletions src/gpuma/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def optimize_single_smiles(
"""Optimize a single molecule from a SMILES string.

This function uses the provided SMILES string to generate an initial 3D structure
using the Morfeus library. It then optimizes the structure using the specified
optimization pipeline.
(nvMolKit on GPU, morfeus on CPU). It then optimizes the structure using the
specified optimization pipeline.

Args:
smiles (str): SMILES string of the molecule to optimize.
Expand Down Expand Up @@ -124,7 +124,7 @@ def optimize_ensemble_smiles(
"""Optimize a conformer ensemble generated from a SMILES string.

This function generates a specified number of conformers from the provided
SMILES string using the Morfeus library. It then optimizes each conformer
SMILES string (nvMolKit on GPU, morfeus on CPU). It then optimizes each conformer
using the specified optimization pipeline. Optionally, the optimized ensemble
can be saved to a multi-structure XYZ file.

Expand Down
Loading
Loading