Using the ASE calculators

This tutorial demonstrates how to use the ASE calculators for neuroevolution potentials (NEP), which allow one to calculate energies, forces and stresses for an atomic configuration, specified in the form of an Atoms object. This enables one to programmatically calculate properties that would otherwise require writing a large number of GPUMD input files and potentially tedious extraction of the results.

calorine provides two different ASE calculators for NEP calculations, one that uses the GPU implementation and one that uses the CPU implementation of NEP. For smaller calculations the CPU calculators is usually more performant. For very large simulations and for comparison the GPU calculator can be useful as well.

All models and structures required for running this and the other tutorial notebooks can be obtained from Zenodo. The files are also available in the tutorials/ folder in the GitLab repository.

CPU-based calculator

Basic usage

First we define an atomic structure in the form of an Atoms object. Next we create a calculator instance by specifying the path to a NEP model file in nep.txt format. In this tutorial, we use a NEP4 model for PbTe. This model is included in the calorine/tutorials/ folder. Finally, we attach the calculator to the Atoms object.

[1]:
from ase.io import read
from ase.build import bulk
from calorine.calculators import CPUNEP

structure = bulk('PbTe', crystalstructure='rocksalt', a=6.7)
calc = CPUNEP('nep-PbTe.txt')
structure.calc = calc

Now, we can readily calculate energies, forces and stresses.

[2]:
print('Energy (eV):', structure.get_potential_energy())
print('Forces (eV/Å):\n', structure.get_forces())
print('Stress (GPa):\n', structure.get_stress())
Energy (eV): -7.663163505693399
Forces (eV/Å):
 [[ 9.43689571e-16 -2.08961602e-16 -4.73106562e-16]
 [-9.50628465e-16  2.08961602e-16  4.73106562e-16]]
Stress (GPa):
 [ 1.09085876e-02  1.09085876e-02  1.09085876e-02 -2.53879747e-18
  1.86911426e-18  8.83104401e-18]

Calculate energy-volume curve

To demonstrate the capabilities of the ASE calculator, we will now calculate an energy-volume curve with the PbTe potential.

[3]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

energies = []
volumes = []

structure_copy = structure.copy()
original_cell = structure.cell.copy()

structure_copy.calc = calc

for scale in np.arange(0.9, 1.11, 0.01):
    structure_copy.set_cell(scale * original_cell, scale_atoms=True)
    volumes.append(structure_copy.get_volume())
    energies.append(structure_copy.get_potential_energy() / len(structure_copy))

fig, ax = plt.subplots()
ax.plot(volumes, energies, '-o')
ax.set_xlabel('Volume (Å^3)')
ax.set_ylabel('Energy (eV/atom)');
../_images/tutorials_calculators_8_0.png

Calculate dipole moments

The calculator also supports the calculation of dipole moments. Note that you must have a NEP model trained to predict dipoles; otherwise the output will be nonsensical. Refer to the GPUMD documentation for information on how to train a dipole model.

The model and structure are included in the calorine/tutorials/ folder.

[4]:
dipole_model = 'nep-PTAF-dipole.txt'
structure = read('PTAF-monomer.xyz')
calc = CPUNEP(dipole_model)
structure.calc = calc

dipole = structure.get_dipole_moment()
dft_dipole = structure.info['dipole']

print('DFT dipole moment:'.ljust(30) +  f'{dft_dipole} a.u.')
print(f'NEP predicted dipole moment:'.ljust(30) + f'{dipole} a.u.')
DFT dipole moment:            [4.85503 0.07166 0.16004] a.u.
NEP predicted dipole moment:  [4.78034782 0.03274603 0.04843106] a.u.

GPU-based calculator

The basic usage of the GPUNEP calculator class is completely analoguous to the CPUNEP calculator. We create an atomic structure and attach the calculator object.

[5]:
from calorine.calculators import GPUNEP

structure = bulk('PbTe', crystalstructure='rocksalt', a=6.7)
calc = GPUNEP('nep-PbTe.txt')
structure.calc = calc

Afterwards we can readily obtain energies, forces, and stresses.

[6]:
print('Energy (eV):', structure.get_potential_energy())
print('Forces (eV/Å):\n', structure.get_forces())
print('Stresses (eV/Å^3):\n', structure.get_stress())
Energy (eV): -7.6631630659
Forces (eV/Å):
 [[ 2.91459e-07  2.77747e-07  1.82196e-07]
 [-2.91459e-07 -2.77747e-07 -1.82196e-07]]
Stresses (eV/Å^3):
 [ 1.09085639e-02  1.09085643e-02  1.09085477e-02 -3.55200559e-10
 -3.55200534e-10  9.51932078e-10]

Temporary and specified directories

Under the hood, the GPUNEP calculator creates a directory and writes the input files necessary to run GPUMD. By default, this is done in temporary directories that are automatically removed once the calculations has finished. It is also possible to run in a user-specified directory that will be kept after the calculations finish. This is especially useful when running molecular dynamics simulations (see below).

[7]:
calc.set_directory('my_directory')
structure.get_potential_energy()
[7]:
-7.6631630659

After this is run, there should be a new directory, my_directory, in which input and output files from GPUMD are available. This can be useful for debugging.

Running custom molecular dynamics simulations

To take advantage of the Python workflow as well as raw speed of the GPU accelerated NEP implementation, the GPUNEP calculator contains a convenience function for running customized molecular dynamics simulations. This should typically be done in a specified directory. The parameters of the run.in file are specified as a list of tuples with two elements, the first being the keyword name and the second any arguments to that keyword.

[8]:
calc.set_directory('my_md_simulation')
supercell = structure.repeat(5)
structure.calc = calc
parameters = [('velocity', 300),
              ('dump_thermo', 100),
              ('dump_position', 100),
              ('ensemble', ('nvt_lan', 300, 300, 100)),
              ('run', 1000)]
calc.run_custom_md(parameters)

Once this is run, the results are available in the folder my_md_simulations.