Open In Colab

Nudged Elastic Band

In this tutorial, we will determine the activation energies of Li diffusion along the [010] and [001] directions (referred to as paths b and c respectively) in lithium iron phosphate (LiFePO_4), a cathode material for lithium ion batteries.

DFT references energies are:

  • Barrier heights:

    • path b = 0.27 eV

    • path c = 2.5 eV

(see table 1 in https://doi.org/10.1039/C5TA05062F)

Set up environment (optional)

These steps are required to run this tutorial with Google Colab. To do so, uncomment and run the cell below.

This will replace pre-installed versions of numpy and torch in Colab with versions that are known to be compatible with janus-core.

It may be possible to skip the steps that uninstall and reinstall torch, which will save a considerable amount of time.

These instructions but may work for other systems too, but it is typically preferable to prepare a virtual environment separately before running this notebook if possible.

[1]:
# import locale
# locale.getpreferredencoding = lambda: "UTF-8"

# ! pip uninstall numpy -y # Uninstall pre-installed numpy

# ! pip uninstall torch torchaudio torchvision transformers -y # Uninstall pre-installed torch
# ! uv pip install torch==2.5.1 # Install pinned version of torch

# ! uv pip install janus-core[mace,orb,chgnet,visualise] data-tutorials --system # Install janus-core with MACE, Orb, CHGNet, and WeasWidget, and data-tutorials

# get_ipython().kernel.do_shutdown(restart=True) # Restart kernel to update libraries. This may warn that your session has crashed.

To ensure you have the latest version of janus-core installed, compare the output of the following cell to the latest version available at https://pypi.org/project/janus-core/

[2]:
from janus_core import __version__

print(__version__)
0.8.5

Prepare data, modules, and model parameters

You can toggle the following to investigate different models:

[3]:
model_params = {"arch": "mace_mp", "model": "medium-0b3"}
# model_params = {"arch": "mace_mp", "model": "medium-mpa-0"}
# model_params = {"arch": "mace_mp", "model": "medium-omat-0"}
# model_params = {"arch": "chgnet"}
# model_params = {"arch": "orb"}
[4]:
from weas_widget import WeasWidget
from ase.io import read
from data_tutorials.data import get_data

from janus_core.calculations.geom_opt import GeomOpt
from janus_core.calculations.neb import NEB
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/codecarbon/core/gpu.py:4: FutureWarning: The pynvml package is deprecated. Please install nvidia-ml-py instead. If you did not install pynvml directly, please report this to the maintainers of the package that installed pynvml for you.
  import pynvml

Use data_tutorials to get the data required for this tutorial:

[5]:
get_data(
    url="https://raw.githubusercontent.com/stfc/janus-core/main/docs/source/tutorials/data/",
    filename="LiFePO4_supercell.cif",
    folder="data",
)
try to download LiFePO4_supercell.cif from https://raw.githubusercontent.com/stfc/janus-core/main/docs/source/tutorials/data/ and save it in data/LiFePO4_supercell.cif
saved in data/LiFePO4_supercell.cif

Preparing end structures

The initial structure can be downloaded from the Materials Project (mp-19017):

[6]:
LFPO = read("data/LiFePO4_supercell.cif")
v=WeasWidget()
v.from_ase(LFPO)
v.avr.model_style = 1
v.avr.show_hydrogen_bonds = True
v
[6]:

First, we will relax the supercell:

[7]:
GeomOpt(struct=LFPO, **model_params).run()

v1=WeasWidget()
v1.from_ase(LFPO)
v1.avr.model_style = 1
v1.avr.show_hydrogen_bonds = True
v1
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/e3nn/o3/_wigner.py:10: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  _Jd, _W3j_flat, _W3j_indices = torch.load(os.path.join(os.path.dirname(__file__), 'constants.pt'))
cuequivariance or cuequivariance_torch is not available. Cuequivariance acceleration will be disabled.
Downloading MACE model from 'https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0b3/mace-mp-0b3-medium.model'
Cached MACE model to /home/runner/.cache/mace/macemp0b3mediummodel
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/macemp0b3mediummodel
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
Using head default out of ['default']
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:197: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
       Step     Time          Energy          fmax
LBFGS:    0 16:15:01     -762.842666        0.654854
LBFGS:    1 16:15:03     -763.038742        0.437508
LBFGS:    2 16:15:05     -763.090096        0.392867
LBFGS:    3 16:15:07     -763.119488        0.374220
LBFGS:    4 16:15:09     -763.172830        0.346620
LBFGS:    5 16:15:10     -763.213488        0.334927
LBFGS:    6 16:15:12     -763.256842        0.327520
LBFGS:    7 16:15:14     -763.297792        0.328964
LBFGS:    8 16:15:16     -763.342419        0.332009
LBFGS:    9 16:15:18     -763.385108        0.328513
LBFGS:   10 16:15:20     -763.427187        0.308144
LBFGS:   11 16:15:21     -763.474267        0.274918
LBFGS:   12 16:15:23     -763.528185        0.234548
LBFGS:   13 16:15:25     -763.579927        0.246328
LBFGS:   14 16:15:27     -763.627605        0.245408
LBFGS:   15 16:15:29     -763.684752        0.231582
LBFGS:   16 16:15:31     -763.766360        0.283626
LBFGS:   17 16:15:32     -763.855346        0.296599
LBFGS:   18 16:15:34     -763.931149        0.188692
LBFGS:   19 16:15:36     -763.965778        0.154808
LBFGS:   20 16:15:38     -763.996599        0.213159
LBFGS:   21 16:15:40     -764.039520        0.255107
LBFGS:   22 16:15:41     -764.108589        0.255570
LBFGS:   23 16:15:43     -764.175608        0.200210
LBFGS:   24 16:15:45     -764.208816        0.096659
[7]:

Next, we will create the start and end structures:

[8]:
# NEB path along b and c directions have the same starting image.
# For start bc remove site 5
LFPO_start_bc = LFPO.copy()
del LFPO_start_bc[5]

# For end b remove site 11
LFPO_end_b = LFPO.copy()
del LFPO_end_b[11]

# For end c remove site 4
LFPO_end_c = LFPO.copy()
del LFPO_end_c[4]

Path b

We can now calculate the barrier height along path b.

This also includes running geometry optimization on the end points of this path.

[9]:
n_images = 7
interpolator="pymatgen" # ASE interpolation performs poorly in this case

neb_b = NEB(
    init_struct=LFPO_start_bc,
    final_struct=LFPO_end_b,
    n_images=n_images,
    interpolator=interpolator,
    minimize=True,
    fmax=0.1,
    **model_params,
)
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/macemp0b3mediummodel
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
Using head default out of ['default']
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:197: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
[10]:
results = neb_b.run()
       Step     Time          Energy          fmax
LBFGS:    0 16:15:47     -758.975432        1.930990
LBFGS:    1 16:15:49     -759.140669        0.711957
LBFGS:    2 16:15:50     -759.209571        0.514600
LBFGS:    3 16:15:52     -759.298948        0.404890
LBFGS:    4 16:15:54     -759.316304        0.265548
LBFGS:    5 16:15:55     -759.336518        0.252446
LBFGS:    6 16:15:57     -759.351338        0.330410
LBFGS:    7 16:15:59     -759.368546        0.309163
LBFGS:    8 16:16:01     -759.378014        0.229741
LBFGS:    9 16:16:02     -759.386280        0.221552
LBFGS:   10 16:16:04     -759.395290        0.278156
LBFGS:   11 16:16:06     -759.404981        0.291370
LBFGS:   12 16:16:07     -759.411852        0.192426
LBFGS:   13 16:16:09     -759.415355        0.075502
       Step     Time          Energy          fmax
LBFGS:    0 16:16:11     -758.975436        1.930961
LBFGS:    1 16:16:13     -759.140671        0.711930
LBFGS:    2 16:16:14     -759.209572        0.514582
LBFGS:    3 16:16:16     -759.298949        0.404887
LBFGS:    4 16:16:18     -759.316305        0.265544
LBFGS:    5 16:16:19     -759.336519        0.252446
LBFGS:    6 16:16:21     -759.351338        0.330395
LBFGS:    7 16:16:23     -759.368546        0.309158
LBFGS:    8 16:16:24     -759.378015        0.229745
LBFGS:    9 16:16:26     -759.386281        0.221556
LBFGS:   10 16:16:28     -759.395291        0.278147
LBFGS:   11 16:16:30     -759.404982        0.291359
LBFGS:   12 16:16:31     -759.411852        0.192423
LBFGS:   13 16:16:33     -759.415355        0.075502
                   Step     Time         fmax
NEBOptimizer[ode]:    0 16:17:09       1.5090
NEBOptimizer[ode]:    1 16:17:21       1.1032
NEBOptimizer[ode]:    2 16:17:33       0.9983
NEBOptimizer[ode]:    3 16:17:45       0.8704
NEBOptimizer[ode]:    4 16:17:57       0.8013
NEBOptimizer[ode]:    5 16:18:09       0.7712
NEBOptimizer[ode]:    6 16:18:21       0.7441
NEBOptimizer[ode]:    7 16:18:33       0.6738
NEBOptimizer[ode]:    8 16:18:45       0.4346
NEBOptimizer[ode]:    9 16:19:09       0.4299
NEBOptimizer[ode]:   10 16:19:21       0.4226
NEBOptimizer[ode]:   11 16:19:33       0.3946
NEBOptimizer[ode]:   12 16:19:45       0.2843
NEBOptimizer[ode]:   13 16:20:09       0.2726
NEBOptimizer[ode]:   14 16:20:21       0.2652
NEBOptimizer[ode]:   15 16:20:33       0.2612
NEBOptimizer[ode]:   16 16:20:45       0.2552
NEBOptimizer[ode]:   17 16:20:57       0.2323
NEBOptimizer[ode]:   18 16:21:21       0.2219
NEBOptimizer[ode]:   19 16:21:33       0.2141
NEBOptimizer[ode]:   20 16:21:45       0.2087
NEBOptimizer[ode]:   21 16:21:57       0.2055
NEBOptimizer[ode]:   22 16:22:09       0.2005
NEBOptimizer[ode]:   23 16:22:21       0.1809
NEBOptimizer[ode]:   24 16:22:45       0.1729
NEBOptimizer[ode]:   25 16:22:57       0.1664
NEBOptimizer[ode]:   26 16:23:09       0.1624
NEBOptimizer[ode]:   27 16:23:22       0.1598
NEBOptimizer[ode]:   28 16:23:34       0.1559
NEBOptimizer[ode]:   29 16:23:46       0.1409
NEBOptimizer[ode]:   30 16:23:58       0.2612
NEBOptimizer[ode]:   31 16:24:22       0.0826

The results include the barrier (without any interpolation between highest images) and maximum force at the point in the simulation

[11]:
print(results)
{'barrier': np.float64(0.2847173696816294), 'delta_E': np.float64(-1.6499575394846033e-07), 'max_force': np.float64(0.09009922957482004)}

We can also plot the band:

[12]:
fig = neb_b.nebtools.plot_band()
v1=WeasWidget()
v1.from_ase(neb_b.nebtools.images)
v1.avr.model_style = 1
v1.avr.show_hydrogen_bonds = True
v1

[12]:
../../_images/tutorials_python_neb_27_1.png

Path c

We can calculate the barrier height along path c similarly

[13]:
n_images = 7
interpolator="pymatgen"

neb_c = NEB(
    init_struct=LFPO_start_bc,
    final_struct=LFPO_end_c,
    n_images=n_images,
    interpolator=interpolator,
    minimize=True,
    fmax=0.1,
    **model_params,
)
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/macemp0b3mediummodel
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
Using head default out of ['default']
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:197: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
[14]:
results = neb_c.run()
       Step     Time          Energy          fmax
LBFGS:    0 16:24:27     -759.415355        0.075502
       Step     Time          Energy          fmax
LBFGS:    0 16:24:29     -758.975431        1.930990
LBFGS:    1 16:24:31     -759.140669        0.711958
LBFGS:    2 16:24:33     -759.209571        0.514601
LBFGS:    3 16:24:34     -759.298948        0.404890
LBFGS:    4 16:24:36     -759.316304        0.265548
LBFGS:    5 16:24:38     -759.336518        0.252446
LBFGS:    6 16:24:40     -759.351338        0.330410
LBFGS:    7 16:24:41     -759.368546        0.309163
LBFGS:    8 16:24:43     -759.378014        0.229741
LBFGS:    9 16:24:45     -759.386280        0.221553
LBFGS:   10 16:24:47     -759.395290        0.278156
LBFGS:   11 16:24:48     -759.404981        0.291370
LBFGS:   12 16:24:50     -759.411852        0.192426
LBFGS:   13 16:24:52     -759.415355        0.075502
                   Step     Time         fmax
NEBOptimizer[ode]:    0 16:25:28       2.3204
NEBOptimizer[ode]:    1 16:25:40       1.6499
NEBOptimizer[ode]:    2 16:25:52       0.9977
NEBOptimizer[ode]:    3 16:26:04       0.6537
NEBOptimizer[ode]:    4 16:26:28       0.4186
NEBOptimizer[ode]:    5 16:26:40       0.3969
NEBOptimizer[ode]:    6 16:26:53       0.3176
NEBOptimizer[ode]:    7 16:27:17       0.2998
NEBOptimizer[ode]:    8 16:27:41       0.2690
NEBOptimizer[ode]:    9 16:27:53       0.2608
NEBOptimizer[ode]:   10 16:28:05       0.2293
NEBOptimizer[ode]:   11 16:28:29       0.2186
NEBOptimizer[ode]:   12 16:28:41       0.2622
NEBOptimizer[ode]:   13 16:28:52       0.2009
NEBOptimizer[ode]:   14 16:29:04       0.1946
NEBOptimizer[ode]:   15 16:29:16       0.1883
NEBOptimizer[ode]:   16 16:29:28       0.1653
NEBOptimizer[ode]:   17 16:29:52       0.1600
NEBOptimizer[ode]:   18 16:30:04       0.1538
NEBOptimizer[ode]:   19 16:30:16       0.1475
NEBOptimizer[ode]:   20 16:30:29       0.1379
NEBOptimizer[ode]:   21 16:30:41       0.1346
NEBOptimizer[ode]:   22 16:30:53       0.1288
NEBOptimizer[ode]:   23 16:31:05       0.1263
NEBOptimizer[ode]:   24 16:31:17       0.1169
NEBOptimizer[ode]:   25 16:31:29       0.1806
NEBOptimizer[ode]:   26 16:31:53       0.0821
[15]:
print(results)
{'barrier': np.float64(1.787575440772347), 'delta_E': np.float64(4.249386620358564e-09), 'max_force': np.float64(0.09762768267248513)}
[16]:
fig = neb_c.nebtools.plot_band()
v2=WeasWidget()
v2.from_ase(neb_c.nebtools.images)
v2.avr.model_style = 1
v2.avr.show_hydrogen_bonds = True
v2
[16]:
../../_images/tutorials_python_neb_33_1.png