| """Checkpoint and model factories for the OneScience MatterSim integration.""" |
|
|
| import os |
| from pathlib import Path |
|
|
| import torch |
|
|
| DEFAULT_CHECKPOINT = "mattersim-v1.0.0-1M.pth" |
|
|
|
|
| def resolve_checkpoint(checkpoint: str | os.PathLike | None = None) -> str: |
| """Resolve a MatterSim checkpoint without changing its native format. |
| |
| Explicit checkpoint values are passed through so MatterSim model aliases keep |
| working. When no value is supplied, the shared OneScience model store is |
| checked before falling back to MatterSim's native alias and download logic. |
| """ |
| if checkpoint is not None: |
| return str(Path(checkpoint).expanduser()) |
|
|
| models_dir = os.environ.get("ONESCIENCE_MODELS_DIR") |
| if models_dir: |
| shared_checkpoint = Path(models_dir).expanduser() / "mattersim" / DEFAULT_CHECKPOINT |
| if shared_checkpoint.is_file(): |
| return str(shared_checkpoint) |
|
|
| return DEFAULT_CHECKPOINT |
|
|
|
|
| def _device(device: str | None) -> str: |
| return device or ("cuda" if torch.cuda.is_available() else "cpu") |
|
|
|
|
| def load_potential( |
| checkpoint: str | os.PathLike | None = None, |
| device: str | None = None, |
| load_training_state: bool = False, |
| **kwargs, |
| ): |
| """Load a MatterSim ``Potential`` from a resolved checkpoint.""" |
| from onescience.utils.mattersim.potential import Potential |
|
|
| return Potential.from_checkpoint( |
| load_path=resolve_checkpoint(checkpoint), |
| device=_device(device), |
| load_training_state=load_training_state, |
| **kwargs, |
| ) |
|
|
|
|
| def load_calculator( |
| checkpoint: str | os.PathLike | None = None, |
| device: str | None = None, |
| **kwargs, |
| ): |
| """Create an ASE-compatible ``MatterSimCalculator``.""" |
| from onescience.utils.mattersim.calculator import MatterSimCalculator |
|
|
| return MatterSimCalculator.from_checkpoint( |
| resolve_checkpoint(checkpoint), device=_device(device), **kwargs |
| ) |
|
|
|
|
| def predict_structures( |
| atoms, |
| checkpoint: str | os.PathLike | None = None, |
| device: str | None = None, |
| batch_size: int = 16, |
| include_forces: bool = True, |
| include_stresses: bool = False, |
| cutoff: float = 5.0, |
| threebody_cutoff: float = 4.0, |
| ): |
| """Predict ASE structures with the MatterSim potential.""" |
| from onescience.datapipes.materials.mattersim import build_dataloader |
|
|
| potential = load_potential(checkpoint=checkpoint, device=device) |
| dataloader = build_dataloader( |
| atoms=list(atoms), |
| batch_size=batch_size, |
| cutoff=cutoff, |
| threebody_cutoff=threebody_cutoff, |
| only_inference=True, |
| ) |
| energies, forces, stresses = potential.predict_properties( |
| dataloader, |
| include_forces=include_forces, |
| include_stresses=include_stresses, |
| ) |
| return { |
| "energies": energies, |
| "forces": forces, |
| "stresses": stresses, |
| } |
|
|