Note
Click here to download the full example code
Molecular DynamicsΒΆ
The module nnp.md
provide tools to run molecular dynamics with a potential
defined by PyTorch.
import torch
from torch import Tensor
from nnp import pbc
from typing import Callable, Sequence
import ase.calculators.calculator
class Calculator(ase.calculators.calculator.Calculator):
"""ASE Calculator that wraps a neural network potential
Arguments:
func (callable): A fucntion that .
overwrite (bool): After wrapping atoms into central box, whether
to replace the original positions stored in :class:`ase.Atoms`
object with the wrapped positions.
"""
implemented_properties = ['energy', 'forces', 'stress', 'free_energy']
def __init__(self, func: Callable[[Sequence[str], Tensor, Tensor, Tensor], Tensor],
overwrite: bool = False):
super(Calculator, self).__init__()
self.func = func
self.overwrite = overwrite
def calculate(self, atoms=None, properties=['energy'],
system_changes=ase.calculators.calculator.all_changes):
super(Calculator, self).calculate(atoms, properties, system_changes)
coordinates = torch.from_numpy(self.atoms.get_positions()).requires_grad_('forces' in properties)
cell = coordinates.new_tensor(self.atoms.get_cell(complete=True).array)
pbc_ = torch.tensor(self.atoms.get_pbc(), dtype=torch.bool)
pbc_enabled = pbc_.any().item()
if pbc_enabled:
coordinates = pbc.map2central(cell, coordinates, pbc_)
if 'stress' in properties:
scaling = torch.eye(3, requires_grad=True)
coordinates = coordinates @ scaling
cell = cell @ scaling
energy = self.func(atoms.get_chemical_symbols(), coordinates, cell, pbc_)
self.results['energy'] = energy.item()
self.results['free_energy'] = energy.item()
if 'forces' in properties:
forces = -torch.autograd.grad(energy, coordinates)[0]
self.results['forces'] = forces.cpu().numpy()
if 'stress' in properties:
volume = self.atoms.get_volume()
stress = torch.autograd.grad(energy, scaling)[0] / volume
self.results['stress'] = stress.cpu().numpy()
Total running time of the script: ( 0 minutes 0.002 seconds)