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: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ sevenn/pretrained_potentials/SevenNet_omat
sevenn/pretrained_potentials/SevenNet_omni
sevenn/pretrained_potentials/SevenNet_omni_i8
sevenn/pretrained_potentials/SevenNet_omni_i12
sevenn/pretrained_potentials/SevenNet_nano

# from compile
sevenn/pair_d3*
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file.
- Training features used for SevenNet-Omni: batch training, `OrderedSampler`, `grad_clip`, `onecyclelr`
- Loss: MAE, L2MAE
- Dataset type: `aselmdb`, `custom`
- SevenNet-Nano checkpoints for `7net-nano-4.5`, `7net-nano-5.0`, `7net-nano-5.5`, and `7net-nano-6.0`

### Changed
- The default dtype of the rescale layer is changed to double precision. Use the `SEVENN_SHIFT_SCALE_DTYPE` environment variable for backward single-precision compatibility.
- The recommended LAMMPS ML-IAP version is bumped to `stable_22Jul2025_update4`.

### Fixed
- `D3Calculator()` segfault bug when reusing the calculator within different sized `Atoms`.
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ If you utilize the pretrained model SevenNet-Omni or multi-task training strateg
}
```

If you utilize the pretrained model SevenNet-Nano, please cite the following paper:
```bib
@article{oh_lightweight_2026,
title = {A Lightweight Universal Machine-Learning Interatomic Potential via Knowledge Distillation for Scalable Atomistic Simulations},
doi = {10.1021/acs.jcim.6c01103},
journal = {J. Chem. Inf. Model.},
author = {Oh, Sangmin and You, Jinmu and Kim, Jaesun and Lee, Jiho and An, Hyungmin and Han, Seungwu and Kang, Youngho},
year = {2026},
}
```

If you utilize the reEWC forgetting-aware fine-tuning strategy for continual learning of pretrained universal machine-learning interatomic potentials, please cite the following paper:
```bib
@article{kim_efficient_2026,
Expand Down
11 changes: 11 additions & 0 deletions docs/source/cite.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,14 @@ If you utilize the pretrained model SevenNet-Omni or multi-task training strateg
year = {2026},
}
```

If you utilize the pretrained model SevenNet-Nano, please cite the following paper:
```bib
@article{oh_lightweight_2026,
title = {A Lightweight Universal Machine-Learning Interatomic Potential via Knowledge Distillation for Scalable Atomistic Simulations},
doi = {10.1021/acs.jcim.6c01103},
journal = {J. Chem. Inf. Model.},
author = {Oh, Sangmin and You, Jinmu and Kim, Jaesun and Lee, Jiho and An, Hyungmin and Han, Seungwu and Kang, Youngho},
year = {2026},
}
```
2 changes: 1 addition & 1 deletion docs/source/user_guide/ase_calculator.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ calc = SevenNetCalculator(model='7net-0', enable_cueq=True) # or enable_flash=Tr

If you encounter the error `CUDA is not installed or nvcc is not available`, please ensure the `nvcc` compiler is available. Currently, CPU + D3 is not supported.

Various pretrained SevenNet models can be accessed by setting the model variable to predefined keywords like `7net-omni`, `7net-mf-ompa`, `7net-omat`, `7net-l3i5`, and `7net-0`.
Various pretrained SevenNet models can be accessed by setting the model variable to predefined keywords like `7net-omni`, `7net-nano-5.5`, `7net-mf-ompa`, `7net-omat`, `7net-l3i5`, and `7net-0`.

User-trained models can be applied with the ASE calculator. In this case, the `model` parameter should be set to the checkpoint path from training.

Expand Down
29 changes: 29 additions & 0 deletions docs/source/user_guide/pretrained.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Multiple inference tasks are available for multi-fidelity architecture models, S
- Multi-task
- `mpa` (PBE+U) <br> `matpes_r2scan` (r²SCAN) <br> `omol25_low` (ωB97M-V) <br> and 10 more tasks

* - [SevenNet-Nano](#sevennet-nano)
- Lightweight model distilled from the SevenNet-Omni `mpa` task
- $l_{\mathrm{max}}=2$ <br> $N_{\mathrm{layer}}=3$ <br> parity=full <br> cutoff=4.5, 5.0, 5.5, or 6.0 Å
- Single-task
- N/A

* - [SevenNet-MF-ompa](#sevennet-mf-ompa)
- MPtrj, sAlex, OMat24
- $l_{\mathrm{max}}=3$ <br> $N_{\mathrm{layer}}=5$ <br> parity=full
Expand Down Expand Up @@ -146,6 +152,29 @@ SevenNet-Omni-i12 follows the same training strategy as [SevenNet-Omni](#sevenne
|:---:|:---:|:---:|:---:|
|**0.873**|0.906|0.192|0.0617|

---
## SevenNet-Nano
> Model keywords: `7net-nano-4.5` | `7net-nano-5.0` | `7net-nano-5.5` | `7net-nano-6.0`

SevenNet-Nano is a lightweight pretrained model released with [A Lightweight Universal Machine-Learning Interatomic Potential via Knowledge Distillation for Scalable Atomistic Simulations](https://doi.org/10.1021/acs.jcim.6c01103). It is provided as four single-task checkpoint variants with different radial cutoffs. Distilled from the SevenNet-Omni `mpa` task, SevenNet-Nano compresses broad cross-domain chemical knowledge into a 105k-parameter model, achieving over an order-of-magnitude speedup over its teacher while retaining balanced accuracy across diverse atomistic environments.

Choose the cutoff explicitly through the model keyword:

```python
from sevenn.calculator import SevenNetCalculator
calc = SevenNetCalculator('7net-nano-5.5')
```

```{table} SevenNet-Nano cutoff variants
:widths: 40 60
| Model keyword | Cutoff |
|---------------|--------|
| `7net-nano-4.5` | 4.5 Å |
| `7net-nano-5.0` | 5.0 Å |
| `7net-nano-5.5` | 5.5 Å |
| `7net-nano-6.0` | 6.0 Å |
```

---
## SevenNet-MF-ompa
> Model keywords: `7net-mf-ompa` | `SevenNet-mf-ompa`
Expand Down
8 changes: 8 additions & 0 deletions sevenn/_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
SEVENNET_omni = f'{_prefix}/SevenNet_omni/checkpoint_sevennet_omni.pth'
SEVENNET_omni_i8 = f'{_prefix}/SevenNet_omni_i8/checkpoint_sevennet_omni_i8.pth'
SEVENNET_omni_i12 = f'{_prefix}/SevenNet_omni_i12/checkpoint_sevennet_omni_i12.pth'
SEVENNET_nano_4_5 = f'{_prefix}/SevenNet_nano/checkpoint_7net_nano_4.5.pth'
SEVENNET_nano_5_0 = f'{_prefix}/SevenNet_nano/checkpoint_7net_nano_5.0.pth'
SEVENNET_nano_5_5 = f'{_prefix}/SevenNet_nano/checkpoint_7net_nano_5.5.pth'
SEVENNET_nano_6_0 = f'{_prefix}/SevenNet_nano/checkpoint_7net_nano_6.0.pth'

_git_prefix = 'https://github.com/MDIL-SNU/SevenNet/releases/download'
CHECKPOINT_DOWNLOAD_LINKS = {
Expand All @@ -74,6 +78,10 @@
SEVENNET_omni: f'{_git_prefix}/v0.12.0.cp/checkpoint_sevennet_omni.pth',
SEVENNET_omni_i8: f'{_git_prefix}/v0.12.1.cp/checkpoint_sevennet_omni_i8.pth',
SEVENNET_omni_i12: f'{_git_prefix}/v0.12.1.cp/checkpoint_sevennet_omni_i12.pth',
SEVENNET_nano_4_5: f'{_git_prefix}/v0.13.1.cp/checkpoint_7net_nano_4.5.pth',
SEVENNET_nano_5_0: f'{_git_prefix}/v0.13.1.cp/checkpoint_7net_nano_5.0.pth',
SEVENNET_nano_5_5: f'{_git_prefix}/v0.13.1.cp/checkpoint_7net_nano_5.5.pth',
SEVENNET_nano_6_0: f'{_git_prefix}/v0.13.1.cp/checkpoint_7net_nano_6.0.pth',
}
# to avoid torch script to compile torch_geometry.data
AtomGraphDataType = Dict[str, torch.Tensor] # But it can contain 'lmp_data'
Expand Down
10 changes: 6 additions & 4 deletions sevenn/calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ def __init__(
Parameters
----------
model: str | Path | AtomGraphSequential, default='7net-0'
Name of pretrained models (7net-omni, 7net-mf-ompa, 7net-omat, 7net-l3i5,
7net-0) or path to the checkpoint, deployed model or the model itself
Name of pretrained models (7net-omni, 7net-nano-5.5, 7net-mf-ompa,
7net-omat, 7net-l3i5, 7net-0) or path to the checkpoint, deployed model
or the model itself
file_type: str, default='checkpoint'
one of 'checkpoint' | 'model_instance'
device: str | torch.device, default='auto'
Expand Down Expand Up @@ -255,8 +256,9 @@ def __init__(
Parameters
----------
model: str | Path | AtomGraphSequential
Name of pretrained models (7net-omni, 7net-mf-ompa, 7net-omat, 7net-l3i5,
7net-0) or path to the checkpoint, deployed model or the model itself
Name of pretrained models (7net-omni, 7net-nano-5.5, 7net-mf-ompa,
7net-omat, 7net-l3i5, 7net-0) or path to the checkpoint, deployed model
or the model itself
file_type: str, default='checkpoint'
one of 'checkpoint' | 'model_instance'
device: str | torch.device, default='auto'
Expand Down
4 changes: 2 additions & 2 deletions sevenn/main/sevenn_get_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
'deploy LAMMPS model from the checkpoint'
)
checkpoint_help = (
'Pretrained model name (7net-omni, 7net-omni-i8, 7net-omni-i12, etc.) '
'or path to checkpoint file. See documentation for all available models.'
'Pretrained model name (7net-omni, 7net-nano-5.5, etc.) or path to '
'checkpoint file. See documentation for all available models.'
)
output_name_help = 'filename prefix'
get_parallel_help = 'deploy parallel model'
Expand Down
12 changes: 12 additions & 0 deletions sevenn/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ def pretrained_name_to_path(name: str) -> str:
checkpoint_path = _const.SEVENNET_omni_i8
elif name in [f'{n}-omni-i12' for n in heads]:
checkpoint_path = _const.SEVENNET_omni_i12
elif name in [f'{n}-nano-4.5' for n in heads]:
checkpoint_path = _const.SEVENNET_nano_4_5
elif name in [f'{n}-nano-5.0' for n in heads]:
checkpoint_path = _const.SEVENNET_nano_5_0
elif name in [f'{n}-nano-5.5' for n in heads]:
checkpoint_path = _const.SEVENNET_nano_5_5
elif name in [f'{n}-nano-6.0' for n in heads]:
checkpoint_path = _const.SEVENNET_nano_6_0
else:
raise ValueError('Not a valid pretrained model name')
url = _const.CHECKPOINT_DOWNLOAD_LINKS.get(checkpoint_path)
Expand Down Expand Up @@ -331,6 +339,10 @@ def get_available_pretrained_models() -> List[str]:
'SEVENNET_omni': '7net-omni',
'SEVENNET_omni_i8': '7net-omni-i8',
'SEVENNET_omni_i12': '7net-omni-i12',
'SEVENNET_nano_4_5': '7net-nano-4.5',
'SEVENNET_nano_5_0': '7net-nano-5.0',
'SEVENNET_nano_5_5': '7net-nano-5.5',
'SEVENNET_nano_6_0': '7net-nano-6.0',
}

models = []
Expand Down
138 changes: 136 additions & 2 deletions tests/unit_tests/test_pretrained.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,14 +373,14 @@ def test_7net_omni_mpa(atoms_pbc, atoms_mol):
)
g1_ref_s = -1 * torch.tensor(
# xx, yy, zz, xy, yz, zx
[-0.6500675, -0.0290563, -0.0290563 , 0.02576996, 0.00374571, 0.02576996]
[-0.6500675, -0.0290563, -0.0290563, 0.02576996, 0.00374571, 0.02576996]
)

g2_ref_e = torch.tensor([-12.918253898620605])
g2_ref_f = torch.tensor(
[
[0.0, -13.32638, 7.1434574],
[0.0, 9.442289 , -9.77207],
[0.0, 9.442289, -9.77207],
[0.0, 3.8840904, 2.6286132],
]
)
Expand Down Expand Up @@ -486,3 +486,137 @@ def test_7net_omni_i12_mpa(atoms_pbc, atoms_mol):

assert acl(g2.inferred_total_energy, g2_ref_e)
assert acl(g2.inferred_force, g2_ref_f)


@pytest.mark.parametrize(
'name, cutoff_ref, g1_ref_e, g1_ref_f, g1_ref_s, g2_ref_e, g2_ref_f',
[
(
'7net-nano-4.5',
4.5,
-3.5564712945510024,
[
[11.843839645385742, -0.06617936491966248, -0.06617937982082367],
[-11.843839645385742, 0.06617936491966248, 0.0661793053150177],
],
[
0.605344831943512,
0.02144481986761093,
0.021444812417030334,
-0.03438035026192665,
-0.003832500660791993,
-0.03438035771250725,
],
-12.919640449266296,
[
[0.0, -13.341249465942383, 7.200963020324707],
[0.0, 9.452506065368652, -9.814258575439453],
[0.0, 3.8887438774108887, 2.6132965087890625],
],
),
(
'7net-nano-5.0',
5.0,
-3.4316864360099615,
[
[12.497757911682129, -0.009577874094247818, -0.009577933698892593],
[-12.497756958007812, 0.009577878750860691, 0.009577957913279533],
],
[
0.6648563146591187,
0.040433332324028015,
0.04043332114815712,
-0.029892124235630035,
-0.003740792628377676,
-0.029892126098275185,
],
-12.910758047958524,
[
[0.0, -13.562267303466797, 7.167722702026367],
[0.0, 9.584957122802734, -9.880509376525879],
[0.0, 3.9773099422454834, 2.7127861976623535],
],
),
(
'7net-nano-5.5',
5.5,
-3.532371955806397,
[
[12.454666137695312, -0.034893378615379333, -0.0348934531211853],
[-12.454666137695312, 0.034893378615379333, 0.03489343076944351],
],
[
0.6540243029594421,
0.021435830742120743,
0.021435843780636787,
-0.03487098217010498,
-0.00511842779815197,
-0.03487098589539528,
],
-12.922036098234884,
[
[0.0, -13.198136329650879, 7.108638763427734],
[0.0, 9.389389038085938, -9.699457168579102],
[0.0, 3.8087470531463623, 2.5908186435699463],
],
),
(
'7net-nano-6.0',
6.0,
-3.484466470155766,
[
[12.169546127319336, -0.02828906662762165, -0.028289003297686577],
[-12.169546127319336, 0.028289061039686203, 0.02828901819884777],
],
[
0.6406453251838684,
0.026448842138051987,
0.026448845863342285,
-0.026171253994107246,
-0.003374285064637661,
-0.026171250268816948,
],
-12.934674727250979,
[
[0.0, -13.23235034942627, 7.185378074645996],
[0.0, 9.451717376708984, -9.761429786682129],
[0.0, 3.780632972717285, 2.576051712036133],
],
),
],
ids=['7net-nano-4.5', '7net-nano-5.0', '7net-nano-5.5', '7net-nano-6.0'],
)
def test_7net_nano(
name,
cutoff_ref,
g1_ref_e,
g1_ref_f,
g1_ref_s,
g2_ref_e,
g2_ref_f,
atoms_pbc,
atoms_mol,
):
cp_path = pretrained_name_to_path(name)
cp_dict = torch.load(cp_path, map_location='cpu', weights_only=False)
assert sorted(cp_dict) == ['config', 'hash', 'model_state_dict', 'time']

model, config = model_from_checkpoint(
cp_path, enable_flash=False, enable_cueq=False
)
cutoff = config['cutoff']
assert cutoff == cutoff_ref

g1 = AtomGraphData.from_numpy_dict(unlabeled_atoms_to_graph(atoms_pbc, cutoff))
g2 = AtomGraphData.from_numpy_dict(unlabeled_atoms_to_graph(atoms_mol, cutoff))

model.set_is_batch_data(False)
g1 = model(g1)
g2 = model(g2)

assert acl(g1.inferred_total_energy, torch.tensor(g1_ref_e))
assert acl(g1.inferred_force, torch.tensor(g1_ref_f))
assert acl(g1.inferred_stress, torch.tensor(g1_ref_s))

assert acl(g2.inferred_total_energy, torch.tensor(g2_ref_e))
assert acl(g2.inferred_force, torch.tensor(g2_ref_f))
Loading