Example of setting up first principle molecular dynamics (FPMD) simulation from scratch.


In FPMD simulations for liquids, usually, a classical MD simulation is needed to equilibrate the system. I gave examples of using gromacs and lammps as classical MD simulator and cpmd, cp2k, cp.x, qbox as FPMD simulator. The system of 55 water at room temperature is used as the example.

From scratch means I generate the initial structure from the water molecular structure.

In this post, 3 steps are listed.
1. generating initial structure
2. equilibrate the system by classical MD ( 2.1 gromacs code, 2.2 lammps code )
3. running a short FPMD simulation ( 3.1 cpmd code 3.2 cp2k code 3.3 cp.x code 3.4 qbox code )


softwares used: VMD packmol gromacs lammps cpmd cp2k cp.x quantum-espresso qbox


0 download the files

All the files needed are included here.

AIMD_setup.tar.gz


1.1 generate initial structure for gromacs

Note: in gromacs, genbox command can be used to generate a water box. Here I use packmol to generate the input structure and convert it to gro file as input.

The water structure is as follow (water.xyz):

3

OW 0.0 0.0 0.0
HW1 -0.81649 0.57736 0.0
HW2 0.81649 0.57736 0.0

a packmol input (water55_packmol.in) script is used here to generate the structure of the total system.

tolerance 2.0
filetype xyz
output water55.xyz

structure water.xyz
  number 55
  inside box 0. 0.  0. 10.8172 10.8172 10.8172
end structure

And also I have a python script for the convertion from xyz to gro file (convert_xyz_to_gro.py), the class mdsystem.py can be download here.

from mdsystem import *
f = file("water55.xyz")
s = mdsystem(cell = [11.8172, 11.8172, 11.8172])
s.xyzinput(f)
s.generate_bonds(cutoff = 1.2)
s.molecular_detection()
for i in range(len(s.moleculars)):
    s.molecular_names.append("SOL")
print s.grooutput()


python convert_xyz_to_gro.py > water55.gro

I use this structure for the input of classical molecular dynamics simulation in gromacs.


1.2 generate initial structure for lammps

The water structure is as follow (water.xyz):

3

O 0.0 0.0 0.0
H -0.81649 0.57736 0.0
H 0.81649 0.57736 0.0

a packmol input (water55_packmol.in) script is used here to generate the structure of the total system.

tolerance 2.0
filetype xyz
output water55.xyz

structure water.xyz
  number 55
  inside box 0. 0.  0. 10.8172 10.8172 10.8172
end structure

And also I have a vmd script for the generation of psf and pdb and lammps data file.

mol new water55.xyz

pbc set { 11.8172 11.8172 11.8172 }
topo clearbonds
for {set i 0} {$i < 55} {incr i} {
    topo addbond [expr $i*3] [expr $i*3+1]
    topo addbond [expr $i*3] [expr $i*3+2]
    topo addangle [expr $i*3+1] [expr $i*3] [expr $i*3+2]
}

animate write psf water55.psf
animate write pdb water55.pdb
topo writelammpsdata water55.data

I use this structure for the input of classical molecular dynamics simulation in lammps.


2.1 gromacs code

for gromacs simulation, there is separate forcefield file and topology file ( spcfw.itp and water55.top ).

two mdp input files needed ( min.mdp nvt.mdp ), the first one is used to minimize the structure, the second one is used for the simulation. Here is the command I use for the simulation.

gmx grompp -f min.mdp -c water55.gro -p water55.top -o min.tpr
gmx mdrun -s min.tpr -c em.gro
gmx grompp -f nvt.mdp -c em.gro -p water55.top -o nvt.tpr
gmx mdrun -s nvt.tpr

In the example, I run 100ps for equlibrating the system. In real simulation, longer is usually needed.

I also like to convert the trajectory so that the atom in the same molecule are not separate in the edge of the box.

gmx trjconv -s nvt.tpr -f traj_comp.xtc -o traj_comp_whole.xtc -pbc whole <<< "0"

Then, the script ( take_last_snapshot.vmd ) can be used to take the last snapshot of the simulation as the initial structure for FPMD.

mol new water55.psf
mol addfile traj_comp_whole.xtc waitfor all
pbc wrap -all -compound residue
set nframes [molinfo top get numframes]
set nframes [expr $nframes - 1]
animate write xyz eq.xyz beg $nframes
exit

2.2 lammps code

Here we use the generated initial structure to run molecular dynamics with lammps. SPC/Fw force field is used.
Here is the ( in.spcf ) lammps input script

units      real
dimension  3
boundary   p p p
atom_style full
read_data water55.data

## alternate way to set charges - besides manually in data file ###
group ox type 2
group hy type 1
set group ox charge -0.8200
set group hy charge 0.4100

pair_style lj/cut/coul/long 5.9086
pair_coeff * * 0.0 0.0
pair_coeff 2 2 0.1553537 3.166
bond_style  harmonic
bond_coeff  1 529.581 1.0120
angle_style harmonic
angle_coeff 1 37.95 113.24
kspace_style ewald 1.0e-5

velocity all create 298.0 12345689 dist uniform
fix 2 all nvt temp 298.0 298.0 100.0


neighbor 2.0 bin
neigh_modify delay 0 every 10 check yes
thermo      200
thermo_style custom step time temp
thermo_modify norm no flush yes

minimize 1.0e-8 1.0e-8 10000 10000

dump 1 all dcd 2000 result.dcd
dump_modify 1 unwrap yes
#run variables
timestep 0.5
run 200000 # 100ps, longer needed ( 2000000 10ns )

after the simulation, the ( result.dcd ) together with the ( water55.psf )

mol new water55.psf
mol addfile result.dcd waitfor all
pbc wrap -all -compound residue
set nframes [molinfo top get numframes]
set nframes [expr $nframes - 1]
animate write xyz eq.xyz beg $nframes
exit

After the equilibrium run. I got the equilibrium structure (eq.xyz) from gromacs or lammps run. Now, I will use this structure as starting point for FPMD simulations.


3.1 cpmd code

I recommend a tutorial about cpmd code ( cpmd.pdf ).

First, the equilibrium structure need to be transformed to CPMD format. Here is the script ( cpmd_initial_structures.py ).

from sys import argv
from mdsystem import *
f = file(argv[1])
s = mdsystem([],[],[])
s.xyzinput(f)

atom_number = {}
atom_xyz = {}
for atom in s:
    if atom.species in atom_number:
        atom_number[atom.species] += 1
        atom_xyz[atom.species].append(atom.coordinates)
    else:
        atom_number[atom.species] = 1
        atom_xyz[atom.species] = [atom.coordinates]

f = open("coord" , 'w')
f.write( "&ATOMS\n")
f.write( "\n")
f.write( "ISOTOPES\n")
f.write( "16.01\n")
f.write( "1.008\n")
f.write( "\n")
f.write( "*O_MT_PBE.psp KLEINMAN-BYLANDER\n")
f.write( " LMAX=P LOC=P\n")
f.write( str(atom_number["O"])+"\n")
for coord in atom_xyz["O"]:
    f.write( "%f %f %f\n" % ( coord[0], coord[1], coord[2] ))
f.write( "\n")
f.write( "*H_MT_PBE.psp KLEINMAN-BYLANDER\n")
f.write( " LMAX=S LOC=S\n")
f.write( str(atom_number["H"])+"\n")
for coord in atom_xyz["H"]:
    f.write( "%f %f %f\n" % ( coord[0], coord[1], coord[2] ))
f.write("\n")
f.write("\n")
f.write( "&END\n")
f.close()

I am using cpmd simulation in cpmd code, so wavefucntion need to be optimized, first. Append the coord file at the end of the input file.

&INFO
wf opt for pure 55 water
&END

&CPMD
 OPTIMIZE WAVEFUNCTION

 CONVERGENCE ORBITALS
   1.0d-7
 ODIIS NO_RESET=50
   5
 MAXSTEP
  1000

 MEMORY BIG
&END

&SYSTEM
 SYMMETRY
  1
 ANGSTROM
 CELL
  11.8172 1.0 1.0  0.0  0.0  0.0
 CUTOFF
  80.0
 TESR
  4
&END

&DFT
 NEWCODE
 FUNCTIONAL PBE
&END

after the wavefunction optimization, a RESTART.1 file should be there. And we generate a LATEST file for the restart.

&INFO
equilibrium steps md at T=300K to get thermo info
&END

&CPMD
 MOLECULAR DYNAMICS
 RESTART WAVEFUNCTIONS COORDINATES LATEST

 RESTFILE
 3

 TRAJECTORY SAMPLE XYZ
  10

 TEMPERATURE
   300

 NOSE IONS
   300 3000.0

 NOSE ELECTRONS
   0.03  9000.0

 MAXSTEP
   100

 TIMESTEP
  4.0
 EMASS
  400.0

 MEMORY BIG

 RHOOUT SAMPLE=100

 SUBTRACT COMVEL ROTVEL
 1

&END

&SYSTEM
 SYMMETRY
  1
 ANGSTROM
 CELL
  11.8172 1.0 1.0  0.0  0.0  0.0
 CUTOFF
  80.0
 TESR
  4
&END

&DFT
 NEWCODE
 FUNCTIONAL PBE
&END

3.2 cp2k code

In cp2k, BOMD is running.

copy the eq.xyz file to the cp2k folder and write this input file for FPMD. (water55.inp)

&FORCE_EVAL
  METHOD Quickstep
  &DFT
    BASIS_SET_FILE_NAME    BASIS_MOLOPT
    POTENTIAL_FILE_NAME    GTH_POTENTIALS
    &MGRID
     CUTOFF      200
     REL_CUTOFF   50
    &END MGRID
    &QS
      EPS_DEFAULT 1.0E-12
    &END QS
    &SCF
      SCF_GUESS  ATOMIC
      EPS_SCF    1.0E-6
      MAX_SCF    30
      &OT
        MINIMIZER       DIIS
        PRECONDITIONER  FULL_ALL
      &END
      &OUTER_SCF
        EPS_SCF  1.0E-6
        MAX_SCF  20
      &END
      &PRINT
        &RESTART
          BACKUP_COPIES 0
        &END
      &END
    &END SCF
    &XC
      &XC_FUNCTIONAL PBE
      &END XC_FUNCTIONAL
    &END XC
  &END DFT
  &SUBSYS
    &CELL
      ABC 11.8172 11.8172 11.8172
    &END CELL
    &TOPOLOGY
       COORD_FILE_NAME   ./eq.xyz
       COORD_FILE_FORMAT xyz
    &END TOPOLOGY
    &KIND H
      BASIS_SET         DZVP-MOLOPT-SR-GTH
      POTENTIAL         GTH-PBE-q1
    &END KIND
    &KIND O
      BASIS_SET         DZVP-MOLOPT-SR-GTH
      POTENTIAL         GTH-PBE-q6
    &END KIND
  &END SUBSYS
&END FORCE_EVAL
&MOTION
  &MD
    ENSEMBLE NVE
    STEPS 100
    TIMESTEP 0.5
    TEMPERATURE 300.0
  &END MD
&END MOTION
&GLOBAL
  PROJECT     H2O-55-PBE
  RUN_TYPE    MD
  PRINT_LEVEL MEDIUM
&END GLOBAL

3.3 cp.x code

The script here.

cd $PWD

Name=cp-H2O

fricei=( 1.0 1.0 0.5 0.1 0.0 0.0 0.0 )
fricpi=( 0.9 1.0 0.3 0.05 0.0 0.0 0.0 )
runi=( 100 100 200 200 1500 1500 35000 )
orthoi=( Gram-Schmidt ortho ortho ortho ortho ortho ortho )
modei=( from_scratch reset_counters restart restart reset_counters restart restart)
wfr=( 51 51 51 51 51 71 72 )
wfw=( 51 51 51 51 71 72 73 )
ions=( none none none none verlet verlet verlet)
tempi=( 50 50 50 50 300 350 375 )
electrons=( damp damp damp damp verlet verlet verlet )

typeset -i j=1
while (( j == 1 ));do
if [[ -d "H2O_${wfw[6]}.save" ]]; then
 wfr[6]=${wfw[6]}
 wfw[6]=$((${wfw[6]}+1))
else
 j=0
fi
done

echo "in"

for i in 0 1 2 3 4 ;
do

rm -f in.00
cat << EOF > in.00

&CONTROL
  outdir = '$PWD'
  pseudo_dir = './'
  title  = 'H2O',
  prefix = 'H2O',
  calculation = 'cp',
  restart_mode = '${modei[$i]}',
  ndr = ${wfr[$i]},
  ndw = ${wfw[$i]},
  nstep  = ${runi[$i]},
  iprint = 20,
  isave  = 1000,
  tstress = .true.,
  tprnfor = .true.,
  dt    =  3.d0,
!  etot_conv_thr = 1.D-8
!  forc_conv_thr = 1.D-3
/


&SYSTEM
  ibrav = 1,
  celldm(1) = CELL_X_YY,
  celldm(2) = 1.00,
  celldm(3) = 1.00,
  celldm(4) = 0.0,
  celldm(5) = 0.0,
  celldm(6) = 0.0,
  nat  = NATOMS_YY,
  ntyp = 2,
  nspin = 1,
  ecutwfc = 25.0,
  ecutrho = 200.0,
  nr1b= 20, nr2b = 20, nr3b = 20,
!  starting_magnetization(1)=0.1,
!  occupations='from_input',
/

&ELECTRONS
  emass = 340.d0,
  emass_cutoff = 2.0d0,
  orthogonalization = '${orthoi[$i]}',
  ortho_eps = 5.d-8,
  ortho_max = 200,
  electron_dynamics = '${electrons[$i]}',
  electron_damping = ${fricei[$i]},
/

&IONS
  ion_dynamics = '${ions[$i]}',
  ion_damping = ${fricpi[$i]},
  tempw  = ${tempi[$i]},
!  ion_temperature = 'rescaling',
  ion_temperature = 'nose',
  fnosep = 10.0  30.0   60.0  100.0,
  nhpcl = 4,
  nhptyp = 2,
/

ATOMIC_SPECIES
H 1.008 H.pbe-van_ak.UPF
O 15.999 O.pbe-van_ak.UPF

ATOMIC_POSITIONS ( angstrom )
COORD_YY

EOF

echo 'Strat turn of' $i
mpirun ~/software/qe/qe-6.0/bin/cp.x < in.00 >> $Name.out

done

echo 'ALL DONE'

The coord generate script.

head -n 167 eq.xyz | tail -n 165 > coord
sed 's/CELL_X_YY/22.331269952/' water_template.j > water_tmp.j
sed 's/NATOMS_YY/165/' water_tmp.j >  water_tmp2.j
sed "/COORD_YY/ r coord" water_tmp2.j > water_tmp3.j
sed "s/COORD_YY/ /" water_tmp3.j > water.j
rm water_tmp.j water_tmp2.j water_tmp3.j

3.4 qbox code

For qbox code, also a convertor script is needed. The units used in qbox is atomic units. I list the script here.

from mdsystem import *
f = file("eq.xyz")
s = mdsystem(cell = [11.8172, 11.8172, 11.8172])
s.xyzinput(f)
s.generate_bonds(cutoff = 1.2)
s.molecular_detection()
for i in range(len(s.moleculars)):
    s.molecular_names.append("SOL")

AtoBohr = 1.8897259886
print "set cell " , s.cell[0] * AtoBohr, 0, 0, \
                    0, s.cell[1] * AtoBohr, 0, \
                    0, 0, s.cell[2] * AtoBohr
print "species oxygen O_HSCV_PBE-1.0.xml"
print "species hydrogen H_HSCV_PBE-1.0.xml"

nO = 0
nH = 0
for atom in s:
    if atom.species == "O":
        nO += 1
        xyz = atom.coordinates * AtoBohr
        print "atom ", "O%d" %nO, "oxygen",  xyz[0], xyz[1], xyz[2]
    if atom.species == "H":
        nH += 1
        xyz = atom.coordinates * AtoBohr
        print "atom ", "H%d" %nH, "hydrogen",  xyz[0], xyz[1], xyz[2]

with pseudopotentials files and sample.xsd, species.xsd (taken from xml folder in qbox package) in the same folder.
We then append these commands at the end of the structure information.

set ecut 70
set wf_dyn PSDA
set ecutprec 5
randomize_wf
run 0 100

set atoms_dyn MD
set dt 40 
run 100 10

Run the calculation like this.

mpirun /nas02/home/y/i/yiy/bin/qb < test.i > test.out

After the simulation, I got trajs like this.

Alt Text