Tutorials

Using the help system

Run MolTwister and type ‘help’, followe by enter. This will display a list of available commands. The ‘load’ command is an example of a command without subcommands. Type ‘help load’ to see the help text for this command. The ‘calculate’ command is an example of a command with subcommands. Type ‘help calculate’ to see the list of available subcommands. To see the help text for the ‘vacf’ subcommand, type ‘help calculate vacf’.

The commands are described by using angle brackets, <>, to denote placeholders for parameter values. I.e., the angle brackets are not part of the commands, unless explicitly specified. The parameter values must always be in the order given in the help text. Any word not given inside the angle brackets are to be included, as is, when writing the command. Square brackets, [], are not to be included in the command, unless explicitly specified. Parameters inside square brackets are considered as optional.

Go through various commands in the help system and find examples of the above notations.

Note that the commands are also listed under the ‘List of commands’ section in this online documentation.

Creating a water molecule PDB

Open MolTwister and execute the following sequence of commands:

add atom H1 at 0 0 0
add atom O from atom 0 dist 0.96
add atom H2 from bond 0 1 angle 104.5 0.0 dist 0.96
genbonds

This will create a hydrogen atom called H1 (where the leading H yields a hydrogen atom) at (x, y, z)=(0, 0, 0), with index 0. Then an oxygen atom, called O, is added a distance 0.96 Angstrom away from the atom with index 0. Finally, a hydrogen atom, called H2, is added from the bond defined between the atoms with index 0 (H1) and index 1 (O), at an angle of 104.5 degrees and a dihedral angle of 0.0 degrees, a distance 0.96 Angstrom from O. The bonds are generated by a call to genbonds.

We can measure the distances and angles to check that everything is as we expect. Type in the following commands:

measure bondlength id 0 1
measure bondlength id 1 
measure angle id 0 1 2

Note that a physical bond does not have to be present to use the measure command. For example, the distance between H1 (index 0) and H2 (index 2) can be found by:

measure bondlength id 0 2

To create the PDB file content, write:

print pdb

However, to write this to a file, the pipe symbol can be used, as follows:

print pdb > my_water.pdb

You can now check that the PDB file is there by using the ll command (or the usual ls command). To chech the contents it is possible to use vim my_water.pdb, if Vim is installed.

Creating liquid water

Open MolTwister and make sure that you have the my_water.pdb file created in the above tutorial (or a similar PDB file). Execute the following commands:

load my_water.pdb
sel all
copy sel 10 10 10 3.0 3.0 3.0
autoscale
genbonds

This will load the PDB file into MolTwister, select all atoms of the water molecule and then copy that selection 10 times in the x, y and z directions with a distance of 3 Angstrom in each of the directions. Finally, autoscale is used to zoom in on the entire system, followed by the creation of bonds for each of the water molecules.

Creating a MolTwister script

Create a file called gen_my_water_mol.script and write the following contents:

exec("add atom H1 at 0 0 0");
exec("add atom O from atom 0 dist 0.96");
exec("add atom H2 from bond 0 1 angle 104.5 0.0 dist 0.96");
exec("genbonds");

Now, run the following command in MolTwister:

load gen_my_water_mol.script

This will create a water molecule, by calling each of the commands given in the file, in sequence.

Creating a MolTwister Python script

Create a Python script called gen_cube.py that contains (requires Python3):

import moltwister as mt

for i in range(0, 10):
    for j in range(0, 10):
        for k in range (0, 10):
            x = i*3.0
            y = j*4.0
            z = k*1.5
            mt.mt_exec(f"add atom O at {x} {y} {z}")

mt.mt_exec("autoscale")

Then, execute the following command in MolTwister:

load gen_cube.py

Water simulation, thermalization, RDF, MSD, VACF, and VDOS

Introduction

In this tutorial we will create a single water molecule, then make copies of this to create a water phase. Subsequently, we assign force field parameters, perform an energy optimization, and then a simulation.

Once the simulation is done, we will plot the temperature as a function of time to determine the point of thermalization. This is followed by calculating

  • the radial distribution function,

  • the mean square displacement and self-diffusion,

  • the velocity auto correlation function,

  • and the vibraional density of states.

Create a water molecule

Invoke

add atom Hw1 at 0 0 0

to add a hydrogen atom called Hw1. Then add the oxygen, Ow, by

add atom Ow at 0.9572 0 0

where the O-H bond lenght is 0.9572 Å. Finally, we want to add an atom at a 120 degree angle. To learn how this is done, invoke

help
help atom

Looking at this, we see that

add atom Hw2 from bond 0 1 angle 104.52 0 dist 0.9572

does what we want. We should also set appropriate boundaries to properly view the atom. Lets create a boundary that extends 10 Å in each direction from the coordinate origin by

mod userdefpbc -10 10 -10 10 -10 10
set userdefpbc on

and then lets make sure we view the entire boundary by invoking

autoscale
genbonds

This can be stored for safe keeping to a pdb and xyz file by

print pdb > single_water.pdb
print xyz > single_water.xyz

Assigning force field parameters

We can now assign force field parameters to the newly created water molecule. We could enter the commands directly, but we may need to assign the force fields several times during a study. Hence, we instead create a file called ‘water_ff.script’ using our favourite text editor (e.g., from within MolTwister you can call vim or nano). The file should contain

mod mass name Hw1 to 1.00784;
mod mass name Hw2 to 1.00784;
mod mass name Ow to 15.999;

add mdbond Ow Hw1 Harm all_r_less_than 1.1 1882.8 0.9572;
add mdbond Ow Hw2 Harm all_r_less_than 1.1 1882.8 0.9572;
add mdangle Hw1 Ow Hw2 Harm all_r_less_than 1.1 230.12 104.52;

add mdnonbonded Ow Ow LJ 0.426768 3.188;

which defines TIP3P water molecules.

Clear all contents

Many times we want to perform subtasks in MolTwister, where we end up with a script, pdb files, or similar that can be reloaded later. After creating these, we often want to delete everything we did from the MolTwister memory. This is done in two steps. First we delete what we see in the 3D view by

sel all
del sel

Subsequently, we delete all force field parameters by

del ff

On some systems, it is also possible to open several instances of MolTwister, which can be useful in some situations, but here we will proceede with clearing all contents.

Investigate the force field

Note that for the below Python scripts to work, MatplotLib must be installed on the system, which can be done by invoking

pip install matplotlib

Python must be installed for MolTwister to work, so this should already be present on the system.

Now load the required data into the system again by

load single_water.pdb
load water_ff.script

We can output data to show plots of the assigned force field potentials, but first we need to know the indices of the assigned force fields, which can be done by invoking

list ff

Invoke

help moldyn

to investigate the possible plots that can be made (i.e., bondforceprofile, angleforceprofile, dihedralforceprofile, and nonbondforceprofile). We can for example look at the Lennard-Jones potential between the water oxygens, which is the non-bonded potential at index 0. This can be done by

moldyn ff nonbondforceprofile 0 2.9 10.0 100 > OwOw_LJ.dat

where the result is stored in OwOw_LJ.dat file. This can be plotted using the following Python script, with name ‘plot_non_bonded.py’:

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("OwOw_LJ.dat", "r")
startOfList = False
for line in f:
    if startOfList:
        s = line.split()
        if len(s) >= 3:
            listX.append(float(s[0])) # Extract r in Å
            listY.append(float(s[2])) # Extract U in kJ/mol

    if "Potential" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

ax.set(xlabel='r [Å]', ylabel='U [kJ/mol]', title='Ow-Ow Lennard-Jones potential')
plt.show()

To plot the Lennard-Jones potential, invoke

python plot_non_bonded.py

or

python3 plot_non_bonded.py

depending on your system.

Create liquid water

We now want to use the single_water.pdb file that we created and construct a water phase containing 512 water molecules, randomly placed, without collision. Lets first calculate the approximate box size if the density is 997 kg/m^3, with two hydrogen masses of 1.0078 and one oxygen mass of 15.999:

calculate volumefromdensity 997 1.0078,1.0078,15.999 512

This yields a side length of approximately 25 Å for a cubic simulation box.

We should now make sure we start with a clean MolTwister:

sel all
del sel
del ff

First we load the water molecule:

load single_water.pdb

and say ‘del’ to delete anything previously there. Subsequently, select all atoms and make 512 copies that are placed randomly within a cubic box with 25 Å side lengths and a minimum distance between molecular atoms of 1.5 Å:

sel all
copy sel random 25 25 25 512 1.5

Check if we have managed to create 512 molecules by selecting all water oxygens (Ow) and counting them:

sel none
sel atomname Ow
measure count sel

The MD simulator only accepts systems with geometric centers placed at the origin. Hence, we need to displace the system to this position by invoking

mod atompos all geomcent 0 0 0
autoscale

We now save the system to a PDB file by

print pdb > water.pdb

Set up an appropriate periodic boundary condition (PBC)

We now want to set up appropriate periodic boundary conditions (PBC) for the simulation. We know that all atoms are within a box of 25 Å from the above construction, but we can also measure the PBC by

measure pbc

Then, we here add a sufficient amount on each side of the PBC to make sure that molecules do not come to close to the molecules on the other side of the PBC. Thus, we invoke

mod userdefpbc -12.5 12.5 -12.5 12.5 -12.5 12.5

to set a 25 by 25 by 25 Å boundary box and we invoke

set userdefpbc on

to activate it. It can be smart to create a file called ‘pbc.script’ and insert the two last commands, just so that we do not have to manually adjust the PBC every time, and instead just write

load pbc.script

when needed.

Perform energy optimization

The molecules were placed randomly in the liquid phase that we created, so it is a good idea to minimize forces between the atoms by performing an energy optimization. We will do this by using the default maximum number of steps for the optimization and the default learning rate, but we will set that the algorithm will stop if we reach an energy change of less than 10 kJ/mol.

To see the default values, invoke

moldyn cfg get

To set the accuracy of 10 kJ/mol, invoke

moldyn cfg set optaccuracy 10

We now start from a fresh MolTwister:

sel all
del sel
del ff

Next, invoke

load water.pdb
load pbc.script
load water_ff.script

Then, start the energy optimization by

moldyn cfg set outstride 10
moldyn optimizeenergy

and wait until it terminates.

This will produce a file called ‘traj.dcd’ and ‘out.txt’.

By creating a Python script called ‘plot_energy.py’, containing

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("out.txt", "r")
startOfList = False
for line in f:
    if startOfList and "Searching" not in line:
        s = line.split()
        if len(s) >= 6:
            listX.append(float(s[0])) # Extract time step column
            listY.append(float(s[5])) # Extract energy column

    if "Step," not in line and "Step" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

ax.set(xlabel='Step', ylabel='U [kJ/mol]', title='Energy optimization')
plt.show()

we can view the energy plot of the optimization process by

python plot_energy.py

If we are happy with the results, we can export the last frame of the optimization process to a PDB file. First load the trajectory by

load traj.dcd

where you can view the optimization process by clicking the 3D view and using the left and right arrow keys to step through. You can also enable fog to better the depth visualization by

set fog on

We now export the optimizated frame (frame 499 in our case) by

print pdb frame 499 > initial.pdb

We will use this as our starting frame for the simulations.

If you wish, rename the traj.dcd and out.txt files from the energy optimization for safe keeping. They will be deleted in a later step.

Perform molecular dynamics simulation

We start again by clearing MolTwister memory:

sel all
del sel
del ff

Now remove any left-over traj.dcd and out.txt files.

We reload the initial system, the PBC, and the force field parameters:

load initial.pdb
load pbc.script
load water_ff.script

We configure to run 100 000 time steps at 0.5 fs, yielding a total of 50 ps, and we set the stride to 10 frames:

moldyn cfg set timestep 0.5
moldyn cfg set timesteps 100000
moldyn cfg set outstride 10

Now we can run the simulation as

moldyn run

On a relatively new CPU this simulation will take a few hours and produce a traj.dcd trajectory file and an out.txt output file.

If the simulation fails, such as producing NaN (Not a Number), then it is possible to try reducing the time step. For example, from 0.5 to 0.2 fs to gain better equilibration that way.

Once the simulation is done, we can load the trajectory file by

load traj.dcd

and analyze the simulation by using the left and right keyboard arrows. Invoke help to see how to adjust the number of steps per keypress, as well as how to show/hide bonds across boundaries, and switch between perspective and orthographic view.

Plot temperature as function of simulation time

The temperature as function of time step is available in ‘out.txt’ and we will use Python to plot the results. Create the file ‘plot_temp.py’ and enter the following script:

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("out.txt", "r")
startOfList = False
for line in f:
    if startOfList and "Searching" not in line:
        s = line.split()
        if len(s) >= 3:
            listX.append(float(s[0])) # Extract time step column
            listY.append(float(s[2])) # Extract temperature column

    if "Timestep," not in line and "Timestep" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

ax.set(xlabel='Step', ylabel='T [K]', title='Temperature')
plt.show()

Now invoke

python plot_temp.py

which should show clearly the thermalization period of the system. This will be important later to determine at which points in the simulation we find thermalized data to perform calculations on. In this tutorial we will assume that the system was thermalized after 8000 time steps, which corresponds to frame index 800 in the DCD file, since we output every 10 timestep.

Calculate and plot a pair correlation function

We now want to calculate the pair correlation function, or radial distribution function (RDF), between oxygen atoms of water (Ow). We will use the data between frame 800 and 9999, which is the last frame of the DCD file, and create a plot of 100 points between 0.4 and 12 Å. This can be done by first making sure that we have loaded the system (i.e., the PDB file in our case, not the entire trajectory) and then invoking

calculate paircorrelation traj.dcd 800 9999 Ow Ow 100 0.4 12.0 > pair_corr.dat

which can be viewed using the following python script

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("pair_corr.dat", "r")
startOfList = False
for line in f:
    if startOfList:
        s = line.split()
        if len(s) >= 4:
            listX.append(float(s[0])) # Extract r in Å
            listY.append(float(s[3])) # Extract RDF

    if "-----------" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

ax.set(xlabel='r [Å]', ylabel='RDF', title='Radial distribution function')
plt.show()

Calculate and plot mean square displacement

We want to calculate the mean square displacement (MSD). However, to use this command we first need to add residue names to our PDB file. We can now start with a clean MolTwister:

sel all
del sel
del ff

Then, load the system

load initial.pdb

We assign the residue names to each atom name by

mod resname name Ow to wat
mod resname name Hw1 to wat
mod resname name Hw2 to wat

where the water residue name was set to ‘wat’. We now generate a new PDB file containing residue names by

print pdb > initial.pdb

Since the mean square displacement attempts to trace the molecules movements over time we need to unwrap the molecules accross the periodic boundaries before we can start the calculation. This is done by invoking

dcd unwrap traj.dcd

which will produce a file called ‘traj_mtunwrap.dcd’.

To test the unwraped system, clear the MolTwister memory by

sel all
del sel
del ff

and then invoke

load initial.pdb
load pbc.script
load water_ff.script
load traj_mtunwrap.dcd

to load the system into the 3D viewer. In the 3D viewer, hit Alt+O to switch into orthographic view and then use the arrow keys to scroll though the frames, checking that the molecules move appropriately outside the periodic boundaries.

Once this is done we can calulate the mean square displacement by

calculate msd traj_mtunwrap.dcd 800 9999 resname wat > msd.dat

Note that the ‘traj_mtunwrap.dcd’ file does not need to be loaded into MolTwister memory in order to run the above command.

which can be plotted using the following Python script:

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("msd.dat", "r")
startOfList = False
for line in f:
    if startOfList:
        s = line.split()
        if len(s) >= 2:
            listX.append(float(s[0])) # Extract step
            listY.append(float(s[1])) # Extract MSD

    if "Index" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

ax.set(xlabel='step', ylabel='M [Å^2]', title='Mean square displacement')
plt.show()

Calculate and plot velocity auto correlation function

We wish to calculate the velocity auto correlation function (VACF). We then need to specify the time between each frame in fs, which is 5 in our case (i.e., 0.5 fs time step and output every 10 steps), as well as the number of time steps to include in the plot, which we will chose to be 100. The velocity auto correlation function should use the unwrapped system, as was also the case for the mean square displacement. First select the atoms to be included by

sel all

and then do the calculation by invoking

calculate vacf traj_mtunwrap.dcd 800 9999 5 100 sel > vacf.dat

and then plot using the below python script:

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("vacf.dat", "r")
startOfList = False
for line in f:
    if startOfList:
        s = line.split()
        if len(s) >= 3:
            listX.append(float(s[0])) # Extract t in fs
            listY.append(float(s[2])) # Extract VACF

    if "vacf" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

ax.set(xlabel='t [fs]', ylabel='nVACF', title='Normalized auto correlation function')
plt.show()

Calculate and plot vibrational density of states

The vibrarional density of states (VDOS) can be calculated both using the unwrapped and wrapped systems, where we in our case will use the unwrapped one, since we have it available. We specify the starting frame for the calculation to 800, as well as the size of the fast Fourier transform (FFT) being applied, which is also the required number of available frames after the starting frame. We specify 10, which results in a 2^10 = 1024 FFT. We start the calculation by

sel all
calculate vdos traj_mtunwrap.dcd 800 10 0.5 sel > vdos.dat

We can plot the result using the following Python script:

import matplotlib.pyplot as plt

listX = []
listY = []
f = open("vdos.dat", "r")
startOfList = False
for line in f:
    if startOfList:
        s = line.split()
        if len(s) >= 6:
            listX.append(float(s[4])) # Extract wavelength, lambda, in nm
            listY.append(float(s[5])) # Extract VDOS

    if "inf" in line:
        startOfList = True

fig, ax = plt.subplots()
ax.plot(listX, listY)

plt.xlim([1780,8000])
plt.ylim([0,0.00014])
ax.fill_between(listX, listY, alpha=0.2)
ax.set(xlabel=r'$\lambda$ [nm]', ylabel='VDOS', title='Vibrational density of states')
plt.show()