Basic MD Trajectory Analysis with PLAMS

This example shows how to calculate several standard trajectory-analysis quantities from molecular dynamics:

  • the velocity autocorrelation function (VACF)

  • the diffusion coefficient from the integral of the VACF

  • the power spectrum from the Fourier transform of the VACF

  • the viscosity from the Green-Kubo relation using the off-diagonal pressure-tensor autocorrelation function

  • the density profile along the z axis

The example focuses on the mechanics of extracting these quantities. For production simulations, the trajectories must be much longer and the analysis must be checked for convergence. For a broader molecular-dynamics workflow example, see also Molecular Dynamics with Python.

Initial Imports

import os
import numpy as np
import matplotlib.pyplot as plt
from scm.base import ChemicalSystem
from scm.inputs import AMS, ReaxFF
from scm.plams import packmol, Settings, AMSJob, view

Run Simple MD Simulation of Water

Run a short MD simulation of 16 water molecules in a box.

mol = packmol(ChemicalSystem.from_smiles("O"), n_molecules=16, density=1.0)
s = Settings()
s.input = AMS(Task="MolecularDynamics")
s.input.Engine = ReaxFF(ForceField="Water2017.ff")
s.input.MolecularDynamics.CalcPressure = True
s.input.MolecularDynamics.InitialVelocities.Temperature = 300
s.input.MolecularDynamics.Trajectory.SamplingFreq = 1
s.input.MolecularDynamics.TimeStep = 0.5
s.input.MolecularDynamics.NSteps = 2000
s.runscript.nproc = 1
os.environ["OMP_NUM_THREADS"] = "1"
job = AMSJob(settings=s, molecule=mol, name="md")
job.run();
[19.10|12:12:32] JOB md STARTED
[19.10|12:12:32] JOB md RUNNING
[19.10|12:12:37] JOB md FINISHED
[19.10|12:12:38] JOB md SUCCESSFUL
view(mol, direction="tilt_z", width=300, height=300, padding=-1)
image generated from notebook

Or alternatively, load a previously run MD job:

# job = AMSJob.load_external('/path/to/ams.rkf')
results = job.results

Velocity Autocorrelation Function

times, vacf = results.get_velocity_acf(start_fs=0, max_dt_fs=250, normalize=False)
normalized_vacf = vacf / vacf[0]

A = np.stack((times, normalized_vacf), axis=1)
np.savetxt("plams_vacf.txt", A, header="Time(fs) VACF")

fig, ax = plt.subplots()
ax.plot(times, normalized_vacf)
ax.set_xlabel("Time (fs)")
ax.set_ylabel("Velocity autocorrelation function")
ax.set_title("Velocity autocorrelation function")
ax;
image generated from notebook

Diffusion Coefficient

t_D, D = results.get_diffusion_coefficient_from_velocity_acf(times, vacf)

A = np.stack((t_D, D), axis=1)
np.savetxt("plams_vacf_D.txt", A, header="time(fs) D(m^2*s^-1)")

fig, ax = plt.subplots()
ax.plot(t_D, D)
ax.set_xlabel("Time (fs)")
ax.set_ylabel("D (m²s⁻¹)")
ax.set_title("Diffusion coefficient")
ax;
image generated from notebook

Power Spectrum

freq, intensities = results.get_power_spectrum(times, vacf, number_of_points=1000)

A = np.stack((freq, intensities), axis=1)
np.savetxt("plams_power_spectrum.txt", A, header="Frequency(cm^-1) PowerSpectrum")

fig, ax = plt.subplots()

ax.plot(freq, intensities)
ax.set_xlabel("Frequency (cm⁻¹)")
ax.set_ylabel("Power spectrum (arbitrary units)")
ax.set_title("Power spectrum")
ax;
image generated from notebook

Green-Kubo Viscosity

t, viscosity = results.get_green_kubo_viscosity(
    start_fs=0, max_dt_fs=250
)  # do not do this for NPT simulations

A = np.stack((t, viscosity), axis=1)
np.savetxt("plams_green_kubo_viscosity.txt", A, header="Time(fs) Viscosity(mPa*s)")

fig, ax = plt.subplots()

ax.plot(t, viscosity)
ax.set_xlabel("Time (fs)")
ax.set_ylabel("Viscosity (mPa*s)")
ax.set_title("Viscosity")
ax;
image generated from notebook

Density Along Axis

z, density = results.get_density_along_axis(
    axis="z", density_type="mass", bin_width=0.2, atom_indices=None
)

A = np.stack((z, density), axis=1)
np.savetxt("plams_density_along_z.txt", A, header="z(angstrom) density(g/cm^3)")

fig, ax = plt.subplots()

ax.plot(z, density)
ax.set_xlabel("z coordinate (Å)")
ax.set_ylabel("Density (g/cm³)")
ax.set_title("Density along z")
ax;
image generated from notebook

See also

Python Script

#!/usr/bin/env python
# coding: utf-8

# ## Initial Imports

import os
import numpy as np
import matplotlib.pyplot as plt
from scm.base import ChemicalSystem
from scm.inputs import AMS, ReaxFF
from scm.plams import packmol, Settings, AMSJob, view


# ## Run Simple MD Simulation of Water

# Run a short MD simulation of 16 water molecules in a box.

mol = packmol(ChemicalSystem.from_smiles("O"), n_molecules=16, density=1.0)
s = Settings()
s.input = AMS(Task="MolecularDynamics")
s.input.Engine = ReaxFF(ForceField="Water2017.ff")
s.input.MolecularDynamics.CalcPressure = True
s.input.MolecularDynamics.InitialVelocities.Temperature = 300
s.input.MolecularDynamics.Trajectory.SamplingFreq = 1
s.input.MolecularDynamics.TimeStep = 0.5
s.input.MolecularDynamics.NSteps = 2000
s.runscript.nproc = 1
os.environ["OMP_NUM_THREADS"] = "1"
job = AMSJob(settings=s, molecule=mol, name="md")
job.run();


view(mol, direction="tilt_z", width=300, height=300, padding=-1, picture_path="picture1.png")


# Or alternatively, load a previously run MD job:

# job = AMSJob.load_external('/path/to/ams.rkf')
results = job.results


# ## Velocity Autocorrelation Function

times, vacf = results.get_velocity_acf(start_fs=0, max_dt_fs=250, normalize=False)
normalized_vacf = vacf / vacf[0]

A = np.stack((times, normalized_vacf), axis=1)
np.savetxt("plams_vacf.txt", A, header="Time(fs) VACF")

fig, ax = plt.subplots()
ax.plot(times, normalized_vacf)
ax.set_xlabel("Time (fs)")
ax.set_ylabel("Velocity autocorrelation function")
ax.set_title("Velocity autocorrelation function")
ax;
ax.figure.savefig("picture2.png")


# ## Diffusion Coefficient

t_D, D = results.get_diffusion_coefficient_from_velocity_acf(times, vacf)

A = np.stack((t_D, D), axis=1)
np.savetxt("plams_vacf_D.txt", A, header="time(fs) D(m^2*s^-1)")

fig, ax = plt.subplots()
ax.plot(t_D, D)
ax.set_xlabel("Time (fs)")
ax.set_ylabel("D (m²s⁻¹)")
ax.set_title("Diffusion coefficient")
ax;
ax.figure.savefig("picture3.png")


# ## Power Spectrum

freq, intensities = results.get_power_spectrum(times, vacf, number_of_points=1000)

A = np.stack((freq, intensities), axis=1)
np.savetxt("plams_power_spectrum.txt", A, header="Frequency(cm^-1) PowerSpectrum")

fig, ax = plt.subplots()

ax.plot(freq, intensities)
ax.set_xlabel("Frequency (cm⁻¹)")
ax.set_ylabel("Power spectrum (arbitrary units)")
ax.set_title("Power spectrum")
ax;
ax.figure.savefig("picture4.png")


# ## Green-Kubo Viscosity 

t, viscosity = results.get_green_kubo_viscosity(
    start_fs=0, max_dt_fs=250
)  # do not do this for NPT simulations

A = np.stack((t, viscosity), axis=1)
np.savetxt("plams_green_kubo_viscosity.txt", A, header="Time(fs) Viscosity(mPa*s)")

fig, ax = plt.subplots()

ax.plot(t, viscosity)
ax.set_xlabel("Time (fs)")
ax.set_ylabel("Viscosity (mPa*s)")
ax.set_title("Viscosity")
ax;
ax.figure.savefig("picture5.png")


# ## Density Along Axis

z, density = results.get_density_along_axis(
    axis="z", density_type="mass", bin_width=0.2, atom_indices=None
)

A = np.stack((z, density), axis=1)
np.savetxt("plams_density_along_z.txt", A, header="z(angstrom) density(g/cm^3)")

fig, ax = plt.subplots()

ax.plot(z, density)
ax.set_xlabel("z coordinate (Å)")
ax.set_ylabel("Density (g/cm³)")
ax.set_title("Density along z")
ax;
ax.figure.savefig("picture6.png")