Simulating toy polymers using openMM#

  • Created by Davit Potoyan Iowa State University

  • This notebook is being used as part of the course on Statistical mechanics

Learning Objectives#

  • Introduce the concept of molecular dynamics of interacting and bonded particles.

  • Introude the basic building blocks of molecular dynamics simulations.

!pip install -q condacolab
import condacolab
condacolab.install()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/condacolab.py:26
     25 try:
---> 26     import google.colab
     27 except ImportError:

ModuleNotFoundError: No module named 'google'

During handling of the above exception, another exception occurred:

RuntimeError                              Traceback (most recent call last)
Cell In[1], line 2
      1 get_ipython().system('pip install -q condacolab')
----> 2 import condacolab
      3 condacolab.install()

File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/condacolab.py:28
     26     import google.colab
     27 except ImportError:
---> 28     raise RuntimeError("This module must ONLY run as part of a Colab notebook!")
     31 __version__ = "0.1.6"
     32 __author__ = "Jaime Rodríguez-Guerra <jaimergp@users.noreply.github.com>"

RuntimeError: This module must ONLY run as part of a Colab notebook!
%%capture
!conda install -c conda-forge openmm mdtraj parmed
!pip install py3dmol 
import openmm as mm
from   openmm import app
from   openmm.unit import * 

import parmed
import mdtraj as md
import py3Dmol
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Simulation parameters
temperature = 293.15 * kelvin
pressure = 1 * bar
mass = 39.948 * amu
sig = 1 * angstrom
eps = 117.8 * kelvin * BOLTZMANN_CONSTANT_kB * AVOGADRO_CONSTANT_NA

box_size = 150 * angstrom  
natom    = 12
cutoff   = 3 * sig

Create polymer structure and topology#

  • Here we will use parmed which is. apowerful tool for creating and manipulating structures and topologies of complex molecules.

from parmed import Structure, Atom, Bond, Angle, Dihedral

s  = Structure()

for i in range(natom):
  s.add_atom(atom     =  Atom(name='A',  mass=mass), 
             resname  = "LJ", 
             resnum   =  i, 
             chain    = 'A')
  
s.positions = [(0, 0, z) for z in range(natom)]

for i in range(natom-1):
  s.bonds.append(Bond(s.atoms[i], s.atoms[i+1]))

s.save('lj.pdb', overwrite=True)
s.save('lj.psf', overwrite=True)

print(s.topology)
s.to_dataframe().head()

Create openmm systema and add particles#

# Add particles to the system
system = mm.System()

for i in range(natom):
  system.addParticle(s.atoms[i].mass)

box_vecs = box_size  *np.eye(3)
system.setDefaultPeriodicBoxVectors(*box_vecs )
### Create LJ force
E_lj  = '4*eps*((sig/r)^12-(sig/r)^6); sig=0.5*(sig1+sig2); eps=sqrt(eps1*eps2)'
force = mm.CustomNonbondedForce(E_lj)
force.addPerParticleParameter('sig') 
force.addPerParticleParameter('eps')

# Particles are assigned properties in the same order as they appear in the System object
for _ in range(natom): 
    force.addParticle([sig, eps])

# Set force cutoff parameters
force.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
force.setCutoffDistance(3.0 * sig)       # set cutoff (truncation) distance at 3*sigma
force.createExclusionsFromBonds([(i, i+1) for i in range(natom-1)], 1) # exlcude bonded particles from LJ

### Create Harmonic force
force2 = mm.HarmonicBondForce()

for i in range(natom-1):
  force2.addBond(i, i+1, 1.5*sig, 100) 

### Add a force to remove Center of Mass motion to prevent drifting of molecule
force3 = mm.CMMotionRemover()

# Added forces to system
system.addForce(force)
system.addForce(force2)
system.addForce(force3)
print('Check if force uses PBC: ', force.usesPeriodicBoundaryConditions() )

print('No particles: ', force.getNumParticles() )

print('per-particle parameters for particle-0: ', force.getParticleParameters(0))

print('Check Energy function: ', force.getEnergyFunction() )

Create OpenMM simulation object#

integrator =  mm.LangevinIntegrator(temperature, 1/picosecond, 2*femtoseconds) 

simulation = app.Simulation(s.topology, 
                            system, 
                            integrator) 

simulation.context.setPositions(s.positions)

# - Minimize the energy
simulation.minimizeEnergy()

# - Initialize velocities with random values at 300K.
simulation.context.setVelocitiesToTemperature(300)


# Reporters
simulation.reporters = []
simulation.reporters.append(app.DCDReporter('ljtraj.dcd', 100))
simulation.reporters.append(app.PDBReporter('ljtraj.pdb', 100))
simulation.reporters.append(app.StateDataReporter("ljscalars.csv", 10, 
                                              time=True, 
                                              potentialEnergy=True, 
                                              totalEnergy=True, 
                                              temperature=True, 
                                              volume=True))

simulation.step(50000)
# Animation
view = py3Dmol.view(width=800, height=800)

view.addModelsAsFrames(open('ljtraj.pdb', 'r').read(),'pdb')
view.setBackgroundColor('white')
view.setStyle({'sphere': {'color':'green'}}) 

#
view.zoomTo()
view.animate({'loop': "forward"})
view.show()