Repository navigation
Added DeePMD-kit PyTorch backend integration #604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
a5d98a3
822d41f
1b50a1d
4d42bcd
e0f8a57
4ffe07f
3325d67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,6 +62,7 @@ | |
| ] | ||
|
|
||
| autodoc_mock_imports = [ | ||
| "deepmd", | ||
| "fairchem", | ||
| "mace", | ||
| "mattersim", | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have standard testing approach for models and their interfaces. Using that infrastructure is a prerequisite to merging here.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rewritten on the standard infrastructure and now pushed into GitHub. Test used the |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| """Tests for the bundled DeePMD-kit integration (torch_sim.models.deepmd). | ||
|
|
||
| Uses the DPA-3.1-3M universal foundation checkpoint (full periodic-table | ||
| type_map, "Omat24" head). | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import time | ||
| import traceback | ||
| import urllib.error | ||
| import urllib.request | ||
|
|
||
| import pytest | ||
| import torch | ||
|
|
||
| from tests.conftest import DEVICE | ||
| from tests.models.conftest import ( | ||
| make_model_calculator_consistency_test, | ||
| make_validate_model_outputs_test, | ||
| ) | ||
| from torch_sim.testing import SIMSTATE_BULK_GENERATORS, SIMSTATE_MOLECULE_GENERATORS | ||
|
|
||
|
|
||
| try: | ||
| from deepmd.calculator import DP | ||
|
|
||
| from torch_sim.models.deepmd import DeepmdModel | ||
|
|
||
| _IMPORT_ERROR: str | None = None | ||
| except ImportError: | ||
| _IMPORT_ERROR = traceback.format_exc() | ||
|
|
||
| pytestmark = pytest.mark.skipif( | ||
| _IMPORT_ERROR is not None, reason=f"deepmd not installed: {_IMPORT_ERROR}" | ||
| ) | ||
|
|
||
| DTYPE = torch.float64 | ||
| MAX_RETRIES = 3 | ||
| RETRY_DELAY = 30 | ||
|
|
||
| _MODEL_URL = ( | ||
| "https://store.aissquare.com/models/35b4ce45-4f59-4868-9fd7-a0c0f5ad9464/" | ||
| "DPA-3.1-3M.pt" | ||
| ) | ||
| _MODEL_HEAD = "Omat24" | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def model_path(tmp_path_factory: pytest.TempPathFactory) -> str: | ||
| """Download the DPA-3.1-3M checkpoint once per session, with retries.""" | ||
| dest = tmp_path_factory.mktemp("deepmd") / "DPA-3.1-3M.pt" | ||
| for attempt in range(MAX_RETRIES): | ||
| try: | ||
| urllib.request.urlretrieve(_MODEL_URL, dest) | ||
| except (urllib.error.URLError, TimeoutError) as exc: | ||
| if attempt == MAX_RETRIES - 1: | ||
| pytest.skip(f"could not download DPA-3.1-3M from {_MODEL_URL}: {exc}") | ||
| time.sleep(RETRY_DELAY * (attempt + 1)) | ||
| else: | ||
| break | ||
| return str(dest) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def deepmd_model(model_path: str) -> DeepmdModel: | ||
| return DeepmdModel( | ||
| model_path=model_path, | ||
| device=DEVICE, | ||
| dtype=DTYPE, | ||
| compute_forces=True, | ||
| compute_stress=True, | ||
| head=_MODEL_HEAD, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def deepmd_calculator(model_path: str) -> DP: | ||
| return DP(model=model_path, head=_MODEL_HEAD) | ||
|
|
||
|
|
||
| def test_deepmd_initialization(deepmd_model: DeepmdModel) -> None: | ||
| assert deepmd_model.device == DEVICE | ||
| assert deepmd_model.dtype == DTYPE | ||
| assert deepmd_model.compute_forces is True | ||
| assert deepmd_model.compute_stress is True | ||
| assert "Cu" in deepmd_model.type_map # universal periodic-table type_map | ||
|
|
||
|
|
||
| test_deepmd_consistency = make_model_calculator_consistency_test( | ||
| test_name="deepmd", | ||
| model_fixture_name="deepmd_model", | ||
| calculator_fixture_name="deepmd_calculator", | ||
| sim_state_names=tuple(SIMSTATE_BULK_GENERATORS.keys()), | ||
| device=DEVICE, | ||
| dtype=DTYPE, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def deepmd_molecule_model(model_path: str) -> DeepmdModel: | ||
| """Stress disabled (mirroring the mace_off molecule test): ASE's DP | ||
| calculator raises PropertyNotImplementedError for stress on non-periodic | ||
| systems, so molecules are checked on energy/forces only.""" | ||
| return DeepmdModel( | ||
| model_path=model_path, | ||
| device=DEVICE, | ||
| dtype=DTYPE, | ||
| compute_forces=True, | ||
| compute_stress=False, | ||
| head=_MODEL_HEAD, | ||
| ) | ||
|
|
||
|
|
||
| test_deepmd_molecule_consistency = make_model_calculator_consistency_test( | ||
| test_name="deepmd_molecule", | ||
| model_fixture_name="deepmd_molecule_model", | ||
| calculator_fixture_name="deepmd_calculator", | ||
| sim_state_names=tuple(SIMSTATE_MOLECULE_GENERATORS.keys()), | ||
| device=DEVICE, | ||
| dtype=DTYPE, | ||
| ) | ||
|
|
||
| test_deepmd_model_outputs = make_validate_model_outputs_test( | ||
| model_fixture_name="deepmd_model", | ||
| device=DEVICE, | ||
| dtype=DTYPE, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| """Wrapper for DeePMD-kit models in TorchSim. | ||
|
|
||
| This module provides :class:`DeepmdModel`, the TorchSim | ||
| `ModelInterface` implementation for the PyTorch backend of DeePMD-kit. | ||
| The underlying implementation is maintained in the standalone | ||
| `deepmd_torchsim` package, available from | ||
| `GitHub <https://github.com/rahulumrao/deepmd_torchsim>`_ and | ||
| `PyPI <https://pypi.org/project/deepmd-torchsim/>`_. | ||
|
|
||
| `DeepmdModel` evaluates DeePMD-kit interatomic potential models and | ||
| provides energies, atomic forces, and stress tensors derived from the | ||
| virial. It supports custom-trained `se_e2_a` models as well as | ||
| multitask and multidomain foundation-model checkpoints, including DPA-3 | ||
| models through the `head=` argument. See the `deepmd_torchsim` | ||
| documentation for usage examples, installation instructions, and | ||
| requirements for a compatible `deepmd-kit` backend. | ||
|
|
||
| If `deepmd_torchsim` is not installed, this module will throw a warning and | ||
| provides a placeholder :class:`DeepmdModel` that raises the underlying | ||
| `ImportError` when instantiated. | ||
|
|
||
| References: | ||
| - DeePMD-kit: https://github.com/deepmodeling/deepmd-kit | ||
| - deepmd_torchsim: https://github.com/rahulumrao/deepmd_torchsim | ||
| """ | ||
|
|
||
| import traceback | ||
| import warnings | ||
| from typing import Any | ||
|
|
||
|
|
||
| try: | ||
| from deepmd_torchsim import DeepmdModel | ||
| except ImportError as exc: | ||
| warnings.warn( | ||
| f"deepmd_torchsim import failed: {traceback.format_exc()}", stacklevel=2 | ||
| ) | ||
|
|
||
| from torch_sim.models.interface import ModelInterface | ||
|
|
||
| class DeepmdModel(ModelInterface): | ||
| """Placeholder when deepmd_torchsim is not installed.""" | ||
|
|
||
| def __init__(self, err: ImportError = exc, *_args: Any, **_kwargs: Any) -> None: | ||
| """Raise the original ImportError.""" | ||
| raise err | ||
|
|
||
| def forward(self, *_args: Any, **_kwargs: Any) -> Any: | ||
| """Unreachable — __init__ always raises.""" | ||
| raise NotImplementedError | ||
|
|
||
|
|
||
| __all__ = ["DeepmdModel"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure to capture any deps clashes below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verified dependency compatibility, the added
deepmd = ["deepmd_torchsim>=0.1.1"]doesn't introduce any conflicts. Becausedeepmd_torchsimis a thin wrapper that places no direct constraints on deepmd-kit or torch, it won't clash with the other extras.I intentionally leave deepmd-kit out of the direct dependencies since its PyTorch backend requires specific, torch versions. Offloading those constraints to
deepmd_torchsimown deepmd extra keeps torch-sim's dependency tree clean and decoupled.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please pin a minimum version that works such that all someone would need to do if importing from top level is install the right option. Otherwise there's no benefit to having the ease of use first party import now we adopted an external model posture
In terms of clashes please check once you've pinned a minimum working set which models deepmd can solve the deps alongside and mark the uv clashes in the pyproject toml. This is also important for UX who use TS for interfacing to many models.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
torch-sim[deepmd]now installsdeepmd-kit==3.1.3withtorch==2.10.0. All model extras work withdeepmdexceptfairchem, which has a PyTorch version conflict. Added fairchem to the conflicts list.