      PROGRAM hd
*     dynamics of system of hard disks
*     program based in part on Fortran program of Allen and Tildesley
      IMPLICIT NONE
      REAL*8 area, ke, rho, temperature, tij, virial, vsum, Lx, Ly,
     +       collision_time, t, timebig, vx, vy, x, y
      INTEGER IC1_, IC2_, IR_, collisions, i, j, N, partner, ishow
      CHARACTER*80 flag
      LOGICAL Ltmp1_
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DATA IC1_/1/
      DATA IC2_/2/
      ishow = 1
      CALL GWopen(IR_, 0)
      CALL initial(vsum,rho,area)
      CALL set_up_windows(IC1_,IC2_)
      CALL kinetic_energy(ke,IC1_)
      CALL GWanchor(IR_, 1)
      CALL GWselvp(IR_, IC2_);
      IF(ishow .NE. 0) THEN
         CALL show_positions(flag)
      ELSE
         CALL show_disks(flag)
      ENDIF
      temperature = ke/N
      flag = 'show_output'
      collisions = 0                        ! number of collisions
      Ltmp1_ = .TRUE.
      DO WHILE(Ltmp1_)
         CALL minimum_collision_time(i,j,tij)
*        move particles forward and reduce collision times by tij
         CALL move(tij)
         t = t + tij
         collisions = collisions + 1
         CALL getflag(flag)
         IF(ishow .NE. 0) THEN
            CALL show_positions(flag)
         ELSE
            CALL show_disks(flag)
         ENDIF
         CALL contact(i,j,virial)           ! compute collision dynamics
         vsum = vsum + virial
*        reset collision list for relevant particles
         CALL reset_list(i,j)
         IF(flag .NE. '' .AND. flag .NE. 'no_show') THEN
            CALL GWselvp(IR_, IC1_)
            CALL show_output(t,collisions,temperature,vsum,rho,area)
            CALL GWselvp(IR_, IC2_)
            IF(flag .EQ. 'show_output' .OR. flag .EQ. 'change')
     +         flag = ''
         ENDIF
         Ltmp1_ = flag .NE. 'stop'
      END DO
      CALL save_config
      CALL GWquit(IR_)
      END
C
      SUBROUTINE initial(vsum,rho,area)
      IMPLICIT NONE
      CHARACTER*80 file, heading, start
      REAL*8 ax, ay, col,
     +       nx, ny, row, vmax, vsum, rho, area, Lx, Ly, collision_time,
     +       t, timebig, vx, vy, x, y
      REAL dmy, rnd
      INTEGER IO1_, IR_, i, partner, N, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DATA IO1_/11/
      t = 0
      WRITE(*,'(A,$)') 'read file (f) or lattice start (l) = '
      READ(*,'(A)') start
      IF(start .EQ. 'f' .OR. start .EQ. 'F') THEN
         WRITE(*,'(A,$)') 'file name = '
         READ(*,'(A)') file
         OPEN(IO1_, file = file)
         READ(IO1_,*) N
         READ(IO1_,*) Lx
         READ(IO1_,*) Ly
         READ(IO1_,'(A)') heading
         DO i = 1, N
            READ(IO1_,*) x(i),y(i)
         END DO
         READ(IO1_,'(A)') heading
         DO i = 1, N
            READ(IO1_,*) vx(i),vy(i)
         END DO
         CLOSE(IO1_)
      ELSE IF(start .EQ. 'l' .OR. start .EQ. 'L') THEN
         dmy = rnd(-1)
         WRITE(*,'(A,$)') 'N = '
         READ(*,*) N                        ! choose N so that sqr(N) an integer
         WRITE(*,'(A,$)') 'Lx = '
         READ(*,*) Lx
         WRITE(*,'(A,$)') 'Ly = '
         READ(*,*) Ly
         WRITE(*,'(A,$)') 'vmax = '
         READ(*,*) vmax
         nx = sqrt(DBLE(N))
         ny = nx
         IF(nx .GE. Lx .OR. nx .GE. Ly) THEN
            WRITE(*,*) 'box too small'
            CALL GWquit(IR_)
            STOP
         END IF
         ax = Lx/nx                         ! "lattice" spacing
         ay = Ly/ny
         i = 0
         DO col = 1, nx
            DO row = 1, ny
               i = i + 1
               x(i) = (col - 0.5D0)*ax
               y(i) = (row - 0.5D0)*ay
*              choose random positions and velocities
               vx(i) = (2*rnd(0) - 1)*vmax
               vy(i) = (2*rnd(0) - 1)*vmax
            END DO
         END DO
      END IF
      CALL check_overlap                    ! check if two disks overlap
      CALL check_momentum
      area = Lx*Ly
      rho = N/area
      timebig = 1.0D10
      vsum = 0                              ! virial sum
      DO i = 1, N
         partner(i) = N
      END DO
      collision_time(N) = timebig
*     set up initial collision lists
      DO i = 1, N
         CALL uplist(i)
      END DO
      CALL GWsetogn(IR_, 0, N + 1)
      END
C
      SUBROUTINE check_momentum
      IMPLICIT NONE
      REAL*8 vxsum, vysum, Lx, Ly, collision_time, t, timebig, vx,
     +       vy, x, y
      INTEGER i, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      vxsum = 0
      vysum = 0
      DO i = 1, N
         vxsum = vxsum + vx(i)              ! total center of mass velocity (momentum)
         vysum = vysum + vy(i)
      END DO
      vxsum = vxsum/N
      vysum = vysum/N
      DO i = 1, N
         vx(i) = vx(i) - vxsum
         vy(i) = vy(i) - vysum
      END DO
      END
C
      SUBROUTINE minimum_collision_time(i,j,tij)
*     locate minimum collision time
      IMPLICIT NONE
      REAL*8 tij, Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER k, i, j, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      tij = timebig
      DO k = 1, N
         IF(collision_time(k) .LT. tij) THEN
            tij = collision_time(k)
            i = k
         END IF
      END DO
      j = partner(i)
      END
C
      SUBROUTINE move(tij)
      IMPLICIT NONE
      REAL*8 tij, Lx, Ly, collision_time, t, timebig, vx, vy, x, y, pbc
      INTEGER k, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DO k = 1, N
         collision_time(k) = collision_time(k) - tij
         x(k) = x(k) + vx(k)*tij
         y(k) = y(k) + vy(k)*tij
         x(k) = pbc(x(k),Lx)
         y(k) = pbc(y(k),Ly)
      END DO
      END
C
      SUBROUTINE reset_list(i,j)
*     reset collision list for relevant particles
      IMPLICIT NONE
      REAL*8 test, Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER k, i, j, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DO k = 1, N
         test = partner(k)
         IF(k .EQ. i .OR. test .EQ. i .OR. k .EQ. j .OR. test .EQ. j)
     +    THEN
            CALL uplist(k)
         END IF
      END DO
      CALL downlist(i)
      CALL downlist(j)
      END
C
      FUNCTION separation(ds,L)
      IMPLICIT NONE
      REAL*8 separation, ds, L
      IF(ds .GT. 0.5D0*L) THEN
         separation = ds - L
      ELSE IF(ds .LT. -0.5D0*L) THEN
         separation = ds + L
      ELSE
         separation = ds
      END IF
      END
C
      FUNCTION pbc(pos,L)
      IMPLICIT NONE
      REAL*8 pbc, pos, L
      pbc = pos
      DO WHILE(pbc .LT. 0.0D0)
         pbc = pbc + L
      ENDDO
      pbc = mod(pbc,L)
      END
C
      SUBROUTINE check_overlap
      IMPLICIT NONE
      REAL*8 dx, dy, r, r2, tol, Lx, Ly, collision_time, t,
     +       timebig, vx, vy, x, y, separation
      INTEGER IR_, i, j, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      tol = 1.0D-4
      DO i = 1, N - 1
         DO j = i + 1, N
            dx = separation(x(i) - x(j),Lx)
            dy = separation(y(i) - y(j),Ly)
            r2 = dx*dx + dy*dy
            IF(r2 .LT. 1) THEN
               r = sqrt(r2)
               IF((1 - r) .GT. tol) THEN
                  WRITE(*,*) 'particles ', i, ' and ', j, 'overlap'
                  CALL GWquit(IR_)
                  STOP
               END IF
            END IF
         END DO
      END DO
      END
C
      SUBROUTINE kinetic_energy(ke,IC1_)
      IMPLICIT NONE
      CHARACTER*80 Stmp1_
      REAL*8 ke, Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER IR_, i, IC1_, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      CALL GWselvp(IR_, IC1_)
      ke = 0
      DO i = 1, N
         ke = ke + vx(i)*vx(i) + vy(i)*vy(i)
      END DO
      ke = 0.5D0*ke
      WRITE(Stmp1_, '(F6.3)') ke/N
      CALL GWputtxt(IR_, 36.0, 2.0, Stmp1_)
      END
C
      SUBROUTINE uplist(i)
*     look for collisions with particles j > i
      IMPLICIT NONE
      REAL*8 Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER j, i, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      IF(i .EQ. N) RETURN
      collision_time(i) = timebig
      DO j = i + 1, N
         CALL check_collision(i,j)
      END DO
      END
C
      SUBROUTINE downlist(j)
*     look for collisions with particles i < j
      IMPLICIT NONE
      INTEGER i, j
      IF(j .EQ. 1) RETURN
      DO i = 1, j - 1
         CALL check_collision(i,j)
      END DO
      END
C
      SUBROUTINE check_collision(i,j)
*     consider collisions between i and periodic images of j
      IMPLICIT NONE
      REAL*8 bij, discr, dvx, dvy, dx, dy, r2, tij, v2, xcell, ycell,
     +       Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER i, j, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DO xcell = -1, 1
         DO ycell = -1, 1
            dx = x(i) - x(j) + xcell*Lx
            dy = y(i) - y(j) + ycell*Ly
            dvx = vx(i) - vx(j)
            dvy = vy(i) - vy(j)
            bij = dx*dvx + dy*dvy
            IF(bij .LT. 0) THEN
               r2 = dx*dx + dy*dy
               v2 = dvx*dvx + dvy*dvy
               discr = bij*bij - v2*(r2 - 1)
               IF(discr .GT. 0) THEN
                  tij = (-bij - sqrt(discr))/v2
                  IF(tij .LT. collision_time(i)) THEN
                     collision_time(i) = tij
                     partner(i) = j
                  END IF
               END IF
            END IF
         END DO
      END DO
      END
C
      SUBROUTINE contact(i,j,virial)
*     compute collision dynamics for particles i and j at contact
      IMPLICIT NONE
      REAL*8 delvx, delvy, dvx, dvy, dx, dy, factor, virial, Lx, Ly,
     +       collision_time, t, timebig, vx, vy, x, y, separation
      INTEGER i, j, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      dx = separation(x(i) - x(j),Lx)
      dy = separation(y(i) - y(j),Ly)
      dvx = vx(i) - vx(j)
      dvy = vy(i) - vy(j)
      factor = dx*dvx + dy*dvy
      delvx = - factor*dx
      delvy = - factor*dy
      vx(i) = vx(i) + delvx
      vx(j) = vx(j) - delvx
      vy(i) = vy(i) + delvy
      vy(j) = vy(j) - delvy
      virial = delvx*dx + delvy*dy
      END
C
      SUBROUTINE show_output(t,collisions,temperature,vsum,rho,area)
      IMPLICIT NONE
      CHARACTER*80 Stmp1_
      REAL*8 mean_pressure, mean_virial, t, temperature, vsum, rho,
     +       area
      INTEGER IR_, collisions, N, partner, ishow
      COMMON /GLBLI/N, partner(100), ishow
      CALL GWsetogn(IR_, 0, -(N + 2))
      mean_virial = vsum/(2*t)
      mean_pressure = rho*temperature + mean_virial/area
      WRITE(Stmp1_, '(I6,$)') collisions
      CALL GWputtxt(IR_, 1.0, 2.0, Stmp1_)
      WRITE(Stmp1_, '(F7.2,$)') t
      CALL GWputtxt(IR_, 12.0, 2.0, Stmp1_)
      WRITE(Stmp1_, '(F7.2,$)') mean_pressure
      CALL GWputtxt(IR_, 24.0, 2.0, Stmp1_)
      CALL GWflush(IR_, -(N + 2))
      CALL GWsetogn(IR_, 0, N + 1)
      END
C
      SUBROUTINE save_config
      IMPLICIT NONE
      CHARACTER*80 file,Stmp1_
      REAL*8 tij, Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER IO1_, IR_, i, j, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DATA IO1_/11/
*     move particles away from collision for final configuration
      CALL minimum_collision_time(i,j,tij)
      CALL move(0.5*tij)
      CALL GWinput(IR_, 'name of saved configuration = ', Stmp1_)
      IF(IR_ .EQ. 0) RETURN
      READ(Stmp1_,'(A)') file
      OPEN(IO1_, file = file)
      WRITE(IO1_,*) N
      WRITE(IO1_,*) Lx
      WRITE(IO1_,*) Ly
      WRITE(IO1_,*) '       x(i)      y(i)'
      DO i = 1, N
         WRITE(IO1_, '(2XF10.5, F10.5)') x(i), y(i)
      END DO
      WRITE(IO1_,*) '      vx(i)     vy(i)'
      DO i = 1, N
         WRITE(IO1_, '(2XF10.5, F10.5)') vx(i), vy(i)
      END DO
      CLOSE(IO1_)
      END
C
      SUBROUTINE set_up_windows(IC1_,IC2_)
      IMPLICIT NONE
      REAL*8 Lx, Ly, collision_time, t,
     +       timebig, vx, vy, x, y
      REAL xwin, ywin, GWaspect
      INTEGER IR_, IC1_, IC2_, N, partner, ishow
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      CALL GWvport(IR_, 0.0, 0.90, GWaspect(1), 1.0)
                                            ! output
      CALL GWindow(IR_, 0.0, 3.0, 80.0, 0.0)
      CALL GWsavevp(IR_, IC1_)
      CALL headings
      CALL GWvport(IR_, 0.02*GWaspect(1), 0.02, GWaspect(1), 0.90)
                                            ! particle trajectories
      CALL compute_aspect_ratio(REAL(Lx),xwin,ywin)
      CALL GWindow(IR_, 0.0, 0.0, xwin, ywin)
      CALL GWsetpen(IR_, 0, -1, -1, 4)
      CALL GWrect(IR_, 0.0, 0.0, REAL(Lx), REAL(Ly))
      CALL GWsetmrk(IR_, 6, 1.0, 13, -1, 4)
      CALL GWsavevp(IR_, IC2_)
      END
C
      SUBROUTINE headings
      IMPLICIT NONE
      CHARACTER*80 Stmp1_
      INTEGER IR_
      WRITE(Stmp1_, '(A)') 'collisions'
      CALL GWputtxt(IR_, 1.0, 1.0, Stmp1_)
      WRITE(Stmp1_, '(A)') 'time'
      CALL GWputtxt(IR_, 12.0, 1.0, Stmp1_)
      WRITE(Stmp1_, '(A)') '<P>'
      CALL GWputtxt(IR_, 24.0, 1.0, Stmp1_)
      WRITE(Stmp1_, '(A)') '   T'
      CALL GWputtxt(IR_, 36.0, 1.0, Stmp1_)
      END
C
      SUBROUTINE show_positions(flag)
      IMPLICIT NONE
      REAL*8 Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      INTEGER IR_, i, N, partner, ishow
      CHARACTER*80 flag
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      IF(flag .NE. 'no_show') THEN
         CALL GWerase(IR_, N + 1, 0)
         DO i = 1, N
            CALL GWsetpxl(IR_, REAL(x(i)), REAL(y(i)), 13)
         END DO
      END IF
      END
C
      SUBROUTINE show_disks(flag)
      IMPLICIT NONE
      CHARACTER*80 flag
      REAL*8 Lx, Ly, collision_time, t, timebig, vx, vy, x, y
      REAL rnd
      INTEGER IR_, i, ncolor, N, partner, ishow, krgb
      SAVE ncolor
      COMMON /GLBLD/Lx, Ly, collision_time(100), t, timebig,
     +             vx(100), vy(100), x(100), y(100)
      COMMON /GLBLI/N, partner(100), ishow
      DATA ncolor/0/
      IF(flag .NE. 'no_show') THEN
         ncolor = mod(ncolor,6) + 1
         IF(ncolor .EQ. 1) CALL GWrefresh(IR_)
         ncolor = 
     +          krgb(int(rnd(0)*256), int(rnd(0)*256), int(rnd(0)*256))
         CALL GWsetpen(IR_, ncolor, -1, -1, 4)
         DO i = 1, N
            CALL GWsetogn(IR_, 0, -i)
            CALL GWputmrk(IR_, REAL(x(i)), REAL(y(i)))
            CALL GWflush(IR_, -i)
         END DO
      END IF
      END
C
      SUBROUTINE getflag(flag)
      IMPLICIT NONE
      CHARACTER*80 flag
      INTEGER IR_, k, N, partner, ishow
      LOGICAL TBkeyinput
      COMMON /GLBLI/N, partner(100), ishow
      IF(TBkeyinput()) THEN
         CALL TBgetkey(k)
         flag = 'show_output'
         IF(k .EQ. ICHAR('r') .OR. k .EQ. ICHAR('R')) THEN
            CALL GWerase(IR_, 0, -1)
            CALL GWrefresh(IR_)
         ELSE IF(k .EQ. ICHAR('s') .OR. k .EQ. ICHAR('S')) THEN
            flag = 'stop'
         ELSE IF(k .EQ. ICHAR('n') .OR. k .EQ. ICHAR('N')) THEN
            flag = 'no_show'
         ELSE IF(k .EQ. ICHAR('c') .OR. k .EQ. ICHAR('C') .OR. 
     +           k .EQ. ICHAR('\t')) THEN
            CALL GWerase(IR_, 0, -1)
            IF(ishow .NE. 0) THEN
                ishow = 0
            ELSE
                ishow = 1
            ENDIF
         ENDIF
      ENDIF
      END
