|
| 1 | +module precision |
| 2 | + implicit none |
| 3 | + save |
| 4 | + integer, parameter :: ip = selected_int_kind(15) |
| 5 | + integer, parameter :: dp = selected_real_kind(15) |
| 6 | +end module precision |
| 7 | + |
| 8 | +program md |
| 9 | + |
| 10 | + ! Molecular Dynamics code for equilibration of Liquid Argon |
| 11 | + ! Author: Alex Pacheco |
| 12 | + ! Date : Jan 30, 2014 |
| 13 | + |
| 14 | + ! This program simulates the equilibration of Liquid Argon |
| 15 | + ! starting from a FCC crystal structure using Lennard-Jones |
| 16 | + ! potential and velocity verlet algorithm |
| 17 | + ! See md-orig.f90 for more details about this program |
| 18 | + |
| 19 | + ! Disclaimer: |
| 20 | + ! This is code can be used as an introduction to molecular dynamics. There are lot more |
| 21 | + ! concepts in MD that are not covered here. |
| 22 | + |
| 23 | + ! Parameters: |
| 24 | + ! npartdim : number of unit cells, uniform in all directions. change to nonuniform if you desire |
| 25 | + ! natom : number of atoms |
| 26 | + ! nstep : nummber of simulation time steps |
| 27 | + ! tempK : equilibration temperature |
| 28 | + ! dt : simulation time steps |
| 29 | + ! boxl : length of simulation box in all directions |
| 30 | + ! alat : lattice constant for fcc crystal |
| 31 | + ! kb : boltzmann constant, set to 1 for simplicity |
| 32 | + ! mass : mass of Ar atom, set to 1 for simplicity |
| 33 | + ! epsilon, sigma : LJ parameters, set to 1 for simplicity |
| 34 | + ! rcell : FCC unit cell |
| 35 | + ! coord, coord_t0 : nuclear positions for each step, current and initial |
| 36 | + ! vel, vel_t0 : nuclear velocities for each step |
| 37 | + ! acc, acc_t0 : nuclear acceleration for each step |
| 38 | + ! force, pener : force and potential energy at current step |
| 39 | + ! avtemp : average temperature at current time step |
| 40 | + ! scale : scaling factor to set current temperature to desired temperature |
| 41 | + ! gasdev : Returns a normally distributed deviate with zero mean and unit variance from Numerical recipes |
| 42 | + |
| 43 | + use precision |
| 44 | + implicit none |
| 45 | + integer(ip), parameter :: npartdim = 10 |
| 46 | + integer(ip), parameter :: natom = 4.d0 * npartdim ** 3 |
| 47 | + integer(ip), parameter :: nstep = 1000 |
| 48 | + real(dp), parameter :: tempK = 10, dt = 1d-3 |
| 49 | + integer(ip) :: istep |
| 50 | + real(dp) :: boxl(3), alat |
| 51 | + integer(ip) :: n, i, j, k, l |
| 52 | + |
| 53 | + real(dp) :: coord_t0(natom,3), coord(natom,3) |
| 54 | + real(dp) :: vel_t0(natom,3), vel(natom,3) |
| 55 | + real(dp) :: acc_t0(natom,3), acc(natom,3) |
| 56 | + real(dp) :: force(natom,3), pener, mass |
| 57 | + |
| 58 | + real(dp) :: vcm(3), r(3), rr, r2, r6, f(3) |
| 59 | + real(dp) :: avtemp, ke, kb, epsilon, sigma, scale |
| 60 | + real(dp) :: gasdev |
| 61 | + ! For timing analysis |
| 62 | + real(dp) :: init_time, start_time, end_time |
| 63 | + integer, dimension(8) :: value |
| 64 | + |
| 65 | + ! Format statements |
| 66 | +1000 format(i8) |
| 67 | +1100 format(a,2x,3f12.6) |
| 68 | +1200 format(a,2x,e15.8) |
| 69 | +1300 format(a,2x,i8,2x,e15.8,1x,e15.8) |
| 70 | + |
| 71 | + alat = 2d0 ** (2d0/3d0) |
| 72 | + boxl = npartdim * alat |
| 73 | + kb = 1.d0 |
| 74 | + mass = 1.d0 |
| 75 | + epsilon = 1.d0 |
| 76 | + sigma = 1.d0 |
| 77 | + |
| 78 | + !================================================= |
| 79 | + ! Initialize coordinates and random velocities |
| 80 | + !================================================= |
| 81 | + |
| 82 | + call date_and_time(VALUES=value) |
| 83 | + init_time = real(value(5)*3600,dp) + real(value(6)*60,dp) + real(value(7),dp) + real(value(8),dp)/1000d0 |
| 84 | + ! Set initial coordinates, velocity and acceleration to zero |
| 85 | + coord_t0 = 0d0 ; vel_t0 = 0d0 ; acc_t0 = 0d0 |
| 86 | + call initialize(natom, npartdim, alat, coord_t0, vel_t0) |
| 87 | + |
| 88 | + open(unit=1,file='atom.xyz',status='unknown') |
| 89 | + write(1,1000) natom |
| 90 | + write(1,*) |
| 91 | + do i = 1, natom |
| 92 | + write(1,1100) 'Ar', coord_t0(i,1), coord_t0(i,2), coord_t0(i,3) |
| 93 | + end do |
| 94 | + |
| 95 | + ! Set Linear Momentum to zero |
| 96 | + call linearmom(natom, vel_t0) |
| 97 | + |
| 98 | + ! get current temperature |
| 99 | + call get_temp(natom, vel_t0, avtemp, mass, kb) |
| 100 | + print 1200, 'Initial Average Temperature: ', avtemp |
| 101 | + |
| 102 | + ! scale initial velocity to desired temperature |
| 103 | + scale = sqrt( tempK / avtemp ) |
| 104 | + vel_t0 = vel_t0 * scale |
| 105 | + call get_temp(natom, vel_t0, avtemp, mass, kb) |
| 106 | + print 1200, 'Initial Scaled Average Temperature: ', avtemp |
| 107 | + |
| 108 | + call date_and_time(VALUES=value) |
| 109 | + start_time = real(value(5)*3600,dp) + real(value(6)*60,dp) + real(value(7),dp) + real(value(8),dp)/1000d0 |
| 110 | + |
| 111 | + !================================================= |
| 112 | + ! MD Simulation |
| 113 | + !================================================= |
| 114 | + |
| 115 | + do istep = 1, nstep |
| 116 | + |
| 117 | + ! Get new atom positions from Velocity Verlet Algorithm |
| 118 | + call verlet(coord, coord_t0, vel, vel_t0, acc, acc_t0, force, pener, natom, mass, dt, boxl) |
| 119 | + |
| 120 | + ! Set Linear Momentum to zero |
| 121 | + call linearmom(natom, vel) |
| 122 | + |
| 123 | + ! compute average temperature |
| 124 | + call get_temp(natom, vel, avtemp, mass, kb) |
| 125 | + print 1300, 'Average Temperature: ' , istep, avtemp, pener |
| 126 | + |
| 127 | + scale = sqrt ( tempk / avtemp ) |
| 128 | + ! Reset for next time step |
| 129 | + coord_t0 = coord |
| 130 | + acc_t0 = acc |
| 131 | + ! scale velocity to desired temperature |
| 132 | + vel_t0 = vel * scale |
| 133 | + |
| 134 | + ! Write current coordinates to xyz file for visualization |
| 135 | + write(1,1000) natom |
| 136 | + write(1,*) |
| 137 | + do i = 1, natom |
| 138 | + write(1,1100) 'Ar', coord_t0(i,1), coord_t0(i,2), coord_t0(i,3) |
| 139 | + end do |
| 140 | + end do |
| 141 | + close(1) |
| 142 | + |
| 143 | + call date_and_time(VALUES=value) |
| 144 | + end_time = real(value(5)*3600,dp) + real(value(6)*60,dp) + real(value(7),dp) + real(value(8),dp)/1000d0 |
| 145 | + write(6,'(a,f12.3,/,a,f12.3)') 'Init Time: ', start_time - init_time, & |
| 146 | + 'Sim Time: ', end_time - start_time |
| 147 | + |
| 148 | +end program md |
| 149 | + |
| 150 | +subroutine initialize(natom, npartdim, alat, coord_t0, vel_t0) |
| 151 | + use precision |
| 152 | + implicit none |
| 153 | + integer(ip) :: natom, npartdim |
| 154 | + real(dp), dimension(natom,3) :: coord_t0, vel_t0 |
| 155 | + integer(ip) :: n, i, j, k, l |
| 156 | + real(dp) :: alat, rcell(3,4) |
| 157 | + |
| 158 | + ! Create FCC unit cell |
| 159 | + rcell = 0d0 |
| 160 | + rcell(1,2) = 0.5d0 * alat |
| 161 | + rcell(2,2) = 0.5d0 * alat |
| 162 | + rcell(2,3) = 0.5d0 * alat |
| 163 | + rcell(3,3) = 0.5d0 * alat |
| 164 | + rcell(1,4) = 0.5d0 * alat |
| 165 | + rcell(3,4) = 0.5d0 * alat |
| 166 | + |
| 167 | + ! Create a FCC crystal structure |
| 168 | + n = 1 |
| 169 | + do i = 1, npartdim |
| 170 | + do j = 1, npartdim |
| 171 | + do k = 1, npartdim |
| 172 | + do l = 1, 4 |
| 173 | + coord_t0(n,1) = alat * real(i - 1, dp) + rcell(1,l) |
| 174 | + coord_t0(n,2) = alat * real(j - 1, dp) + rcell(2,l) |
| 175 | + coord_t0(n,3) = alat * real(k - 1, dp) + rcell(3,l) |
| 176 | + n = n + 1 |
| 177 | + end do |
| 178 | + end do |
| 179 | + end do |
| 180 | + end do |
| 181 | + |
| 182 | + ! Assign initial random velocities |
| 183 | + do i = 1, natom |
| 184 | + do j = 1, 3 |
| 185 | + vel_t0(i,j) = gasdev() |
| 186 | + end do |
| 187 | + end do |
| 188 | + |
| 189 | +contains |
| 190 | + function gasdev() |
| 191 | + use precision |
| 192 | + implicit none |
| 193 | + real(dp) :: gasdev |
| 194 | + real(dp) :: v1, v2, fac, rsq |
| 195 | + real(dp), save :: gset |
| 196 | + logical, save :: available = .false. |
| 197 | + |
| 198 | + if (available) then |
| 199 | + gasdev = gset |
| 200 | + available = .false. |
| 201 | + else |
| 202 | + do |
| 203 | + call random_number(v1) |
| 204 | + call random_number(v2) |
| 205 | + v1 = 2.d0 * v1 - 1.d0 |
| 206 | + v2 = 2.d0 * v2 - 1.d0 |
| 207 | + rsq = v1**2 + v2**2 |
| 208 | + if ( rsq > 0.d0 .and. rsq < 1.d0 ) exit |
| 209 | + end do |
| 210 | + fac = sqrt(-2.d0 * log(rsq) / rsq) |
| 211 | + gasdev = v1 * fac |
| 212 | + gset = v2 * fac |
| 213 | + available = .true. |
| 214 | + end if |
| 215 | + end function gasdev |
| 216 | + |
| 217 | +end subroutine initialize |
| 218 | + |
| 219 | +subroutine verlet(coord, coord_t0, vel, vel_t0, acc, acc_t0, force, pener, natom, mass, dt, boxl) |
| 220 | + use precision |
| 221 | + implicit none |
| 222 | + real(dp), dimension(natom,3) :: coord, vel, acc, force |
| 223 | + real(dp), dimension(natom,3) :: coord_t0, vel_t0, acc_t0 |
| 224 | + real(dp) :: pener |
| 225 | + integer(ip) :: natom, i, j, k |
| 226 | + real(dp) :: mass, dt, boxl(3) |
| 227 | + real(dp) :: r(3), f(3) |
| 228 | + real(dp) :: rr, r2, r6 |
| 229 | + |
| 230 | + ! Set coordinates, velocity, acceleration and force at next time step to zero |
| 231 | + coord = 0d0 ; vel = 0d0 ; acc = 0d0 ; force = 0d0 |
| 232 | + pener = 0d0 |
| 233 | + |
| 234 | + ! Get new atom positions from Velocity Verlet Algorithm |
| 235 | + do i = 1, natom |
| 236 | + coord(i,:) = coord_t0(i,:) + vel_t0(i,:) * dt + 0.5d0 * acc_t0(i,:) * dt ** 2 |
| 237 | + ! Apply PBC to coordinates |
| 238 | + do j = 1, 3 |
| 239 | + if ( coord(i,j) > boxl(j) ) then |
| 240 | + coord(i,j) = coord(i,j) - boxl(j) |
| 241 | + else if ( coord(i,j) < 0d0 ) then |
| 242 | + coord(i,j) = coord(i,j) + boxl(j) |
| 243 | + endif |
| 244 | + end do |
| 245 | + end do |
| 246 | + |
| 247 | + ! Get force at new atom positions |
| 248 | + ! Using Lennard Jones Potential |
| 249 | + ! Hint: you might want to also seperate the potential and force calculation into a separate subroutine |
| 250 | + ! this will be useful if you want to use other potentials |
| 251 | + |
| 252 | + do i = 1, natom - 1 |
| 253 | + do j = i + 1, natom |
| 254 | + r(:) = coord(i,:) - coord(j,:) |
| 255 | + ! minimum image criterion |
| 256 | + r = r - nint( r / boxl ) * boxl |
| 257 | + ! Hint: Use dot_product |
| 258 | + rr = r(1) ** 2 + r(2) ** 2 + r(3) ** 2 |
| 259 | + r2 = 1.d0 / rr |
| 260 | + r6 = r2 ** 3 |
| 261 | + ! Lennard Jones Potential |
| 262 | + ! V = 4 * epsilon * [ (sigma/r)**12 - (sigma/r)**6 ] |
| 263 | + ! = 4 * epsilon * (sigma/r)**6 * [ (sigma/r)**2 - 1 ] |
| 264 | + ! = 4 * r**(-6) * [ r**(-2) - 1 ] for epsilon=sigma=1 |
| 265 | + ! F_i = 48 * epsilon * (sigma/r)**6 * (1/r**2) * [ ( sigma/r)** 6 - 0.5 ] * i where i = x,y,z |
| 266 | + ! = 48 * r**(-8) * [ r**(-6) - 0.5 ] * i for epsilon=sigma=1 |
| 267 | + pener = pener + 4d0 * r6 * ( r6 - 1.d0 ) |
| 268 | + f = 48d0 * r2 * r6 * ( r6 - 0.5d0 ) * r |
| 269 | + force(i,:) = force(i,:) + f(:) |
| 270 | + force(j,:) = force(j,:) - f(:) |
| 271 | + end do |
| 272 | + end do |
| 273 | + |
| 274 | + ! Calculate Acceleration and Velocity at current time step |
| 275 | + acc = force / mass |
| 276 | + vel = vel_t0 + 0.5d0 * ( acc + acc_t0 ) * dt |
| 277 | + |
| 278 | +end subroutine verlet |
| 279 | + |
| 280 | +subroutine linearmom(natom, vel) |
| 281 | + use precision |
| 282 | + implicit none |
| 283 | + integer(ip) :: natom |
| 284 | + real(dp) :: vel(natom,3) |
| 285 | + integer(ip) :: i |
| 286 | + real(dp) :: vcm(3) |
| 287 | + |
| 288 | + ! First get center of mass velocity |
| 289 | + vcm = 0d0 |
| 290 | + do i = 1, 3 |
| 291 | + vcm(i) = sum(vel(:,i)) |
| 292 | + end do |
| 293 | + vcm = vcm / real(natom,dp) |
| 294 | + |
| 295 | + ! Now remove center of mass velocity from all atoms |
| 296 | + do i = 1, natom |
| 297 | + vel(i,:) = vel(i,:) - vcm(:) |
| 298 | + end do |
| 299 | + |
| 300 | +end subroutine linearmom |
| 301 | + |
| 302 | +subroutine get_temp(natom, vel, avtemp, mass, kb) |
| 303 | + use precision |
| 304 | + implicit none |
| 305 | + integer(ip) :: natom |
| 306 | + real(dp) :: vel(natom,3) |
| 307 | + real(dp) :: avtemp |
| 308 | + real(dp) :: mass, kb |
| 309 | + integer(ip) :: i, j |
| 310 | + real(dp) :: ke |
| 311 | + |
| 312 | + ke = 0d0 |
| 313 | + do i = 1, natom |
| 314 | + ke = ke + dot_product(vel(i,:),vel(i,:)) |
| 315 | + end do |
| 316 | + avtemp = mass * ke / ( 3d0 * kb * real( natom - 1, dp)) |
| 317 | + |
| 318 | +end subroutine get_temp |
| 319 | + |
0 commit comments