Comparison with MDAnalysis#

MDAnalysis also contains a tool for calculating the mean-squared displacements. So why use kinisi over MDAnalysis?

Well, the approach taken by kinisi, which is outlined in the methodology, uses a high precision approach to estimate the diffusion coefficent and offers an accurate estimate of the variance in the mean-squared displacements and diffusion coefficient from a single simulation.

In this notebook, we will compare the results from MDAnalysis and kinisi. First we will import the kinisi.analyze.DiffusionAnalyzer and MDAnalysis.analysis.msd classes.

[1]:
import warnings

warnings.filterwarnings('ignore', category=DeprecationWarning)
warnings.filterwarnings('ignore', category=RuntimeWarning)
warnings.filterwarnings('ignore', category=UserWarning)

import numpy as np

import kinisi
from kinisi.analyze import DiffusionAnalyzer
import MDAnalysis.analysis.msd as msd
from MDAnalysis.transformations.nojump import NoJump

Next, we are going to need to do a little ‘magic’ to get MDAnalysis to read an extended xyz file. Luckly ASE can help us out.

[2]:
from ase.io import read
from MDAnalysis import Universe
import os

Since this trajectory has a triclinic cell; we need to extract the cell dimensions and pass them to MDAnalysis.

[3]:
# The file we want to load is in the test cases for kinisi, so will need to get the path to it.
path_to_file = os.path.join(os.path.dirname(kinisi.__file__), 'tests/inputs/LiPS.exyz')
atoms = read(path_to_file, format='extxyz', index=':')

cell_dimensions = []
for frame in atoms:
    lengths = frame.cell.lengths()
    angles = frame.cell.angles()
    cell = [*lengths, *angles]
    cell_dimensions.append(cell)

We now have the cell dimensions (\(a, b, c, \alpha, \beta, \gamma\)) in an array. We now need to create an MDAnalysis universe and apply these dimension to it.

[4]:
u = Universe(path_to_file, path_to_file, format='XYZ', topology_format='XYZ', dt=20.0/1000)
for ts, dims in zip(u.trajectory, cell_dimensions):
    ts.dimensions = dims

Now we add an unwrapping transformation to the universe, create the MSD object, run the analysis, and extract the results as a timeseries.

[5]:
u.trajectory.add_transformations(NoJump())
MSD = msd.EinsteinMSD(u, select='type LI', msd_type='xyz', fft=True, verbose=False)
MSD.run(verbose=False)
mda_MSD = MSD.results.timeseries
100%|██████████| 896/896 [00:00<00:00, 6652.22it/s]

With the result from MDAnalysis in one hand we can now do the same thing in kinisi with the other. We can also extract the series of \(\Delta t\) from kinisi.

[6]:
params = {'specie': 'LI',
          'time_step': 0.001,
          'step_skip': 20,
          'n_steps': 250,
          'sampling': 'multi-origin',
          'progress': False}

xyz = {'dimension': 'xyz', 'bootstrap': False,'progress': False}
kinisi_from_universe = DiffusionAnalyzer.from_universe(u, parser_params=params, uncertainty_params=xyz)
kinisi_MSD = kinisi_from_universe.msd
time = kinisi_from_universe.dt

Putting the two analyses’ together, we can plot for comparison.

[7]:
import matplotlib.pyplot as plt

plt.plot(time[:-1], kinisi_MSD[:-1], label='Kinisi MSD with multi_origin', c='#ff7f0e')
plt.plot(time[:-1], mda_MSD[1:], label='MDanalysis', ls='--')
plt.ylabel('MSD (Å$^2$)')
plt.xlabel('Diffusion time (ps)')
plt.legend()


plt.show()
_images/mdanalysis_13_0.png

The results overlap almost entirely.

We can now extract an accurate estimation of the variance in the observed MSD from kinisi. For clarity, we will use array indexing to remove every other data point.

[8]:
plt.errorbar(kinisi_from_universe.dt[::2],
             kinisi_from_universe.msd[::2],
             kinisi_from_universe.msd_std[::2],
             c='#ff7f0e')
plt.ylabel(r'MSD/Å$^2$')
plt.xlabel(r'$\Delta t$/ps')
plt.show()
_images/mdanalysis_15_0.png

We can also calculate estimated diffusion coefficient, and the associated uncertainty, with kinisi.

[9]:
from uncertainties import ufloat

kinisi_from_universe.diffusion(kinisi_from_universe.ngp_max)
print('D from kinisi:',
      ufloat(np.mean(kinisi_from_universe.D),
         np.std(kinisi_from_universe.D, ddof=1)))
Likelihood Sampling: 100%|██████████| 1500/1500 [00:01<00:00, 919.22it/s]
D from kinisi: (4.38+/-0.23)e-06