      PROGRAM ising
*     メトロポリス・アルゴリズムを用いた
*     正方格子上のイジング・モデルのモンテカルロ・シミュレーション
      IMPLICIT NONE
      REAL*8 Ce(0:20), Cm(0:20), E, M, T, accept, accum(10),
     +       esave(100), msave(100), w(-8:8)
      INTEGER IR_, L, N, i, mcs, nsave, nequil, pass, spin(32,32)
      CALL GWopen(IR_, 0)
      CALL initial(N,L,T,spin,mcs,nequil,w,E,M)
      CALL plotspins(L,spin)
      DO i = 1, nequil
         CALL Metropolis(N,L,spin,E,M,w,accept)
      END DO
      CALL initialize(accum,accept)
      CALL initialize_correl(Ce,Cm,esave,msave,nsave)
      DO pass = 1, mcs
         CALL Metropolis(N,L,spin,E,M,w,accept)
         CALL mydata(E,M,accum)
         CALL correl(Ce,Cm,E,M,esave,msave,pass,nsave)
      END DO
      CALL output(T,N,mcs,accum,accept)
      CALL c_output(Ce,Cm,accum,mcs,nsave)
      CALL GWquit(IR_)
      END
C
      SUBROUTINE initial(N,L,T,spin,mcs,nequil,w,E,M)
      IMPLICIT NONE
      REAL*8 sum, T, w(-8:8), E, M
      REAL dmy, rnd
      INTEGER N, L, mcs, nequil, dE, right, up, x, y, spin(32,32)
      dmy = rnd(-1)
      WRITE(*,'(A,$)') 'linear dimension of lattice = '
      READ(*,*) L
      N = L*L                               ! スピン数
*     温度は J/k を単位に測られる
      WRITE(*,'(A,$)') 'temperature = '
      READ(*,*) T
      WRITE(*,'(A,$)') '# MC steps per spin for equilibrium = '
      READ(*,*) nequil
      WRITE(*,'(A,$)') '# MC steps per spin for data = '
      READ(*,*) mcs
      M = 0
      DO y = 1, L
         DO x = 1, L
            IF(rnd(0) .LT. 0.5D0) THEN
               spin(x,y) = 1                ! 上向きスピン
            ELSE
               spin(x,y) = -1
            END IF
            M = M + spin(x,y)               ! 全磁化
         END DO
      END DO
      E = 0
      DO y = 1, L
         IF(y .EQ. L) THEN
            up = 1                          ! 周期的境界条件
         ELSE
            up = y + 1
         END IF
         DO x = 1, L
            IF(x .EQ. L) THEN
               right = 1
            ELSE
               right = x + 1
            END IF
            sum = spin(x,up) + spin(right,y)
            E = E - spin(x,y)*sum           ! 全エネルギー
         END DO
      END DO
*     ボルツマン確率の比の計算
      DO dE = -8, 8, 4
         w(dE) = exp(-dE/T)
      END DO
      END
C
      SUBROUTINE plotspins(L,spin)
      IMPLICIT NONE
      REAL xwin, ywin
      INTEGER IR_, i, j, L, spin(32,32)
      CALL GWclear(IR_, -1)
      CALL compute_aspect_ratio(REAL(L+1),xwin,ywin)
      CALL GWindow(IR_, 0.0, 0.0, xwin, ywin)
      DO i = 1, L
         DO j = 1, L
            CALL GWerase(IR_, (i-1)*L+j, 0);
            CALL GWsetogn(IR_, 0, (i-1)*L+j);
            IF(spin(i,j) .EQ. 1) THEN
               CALL GWsrect(IR_, REAL(i), REAL(j), i+1.0, j+1.0, 13)
            ELSE
               CALL GWsrect(IR_, REAL(i), REAL(j), i+1.0, j+1.0, 14)
            END IF
         END DO
      END DO
      END
C
      SUBROUTINE initialize_correl(Ce,Cm,esave,msave,nsave)
      IMPLICIT NONE
      REAL*8 Ce(0:20), Cm(0:20), esave(100), msave(100)
      INTEGER i, nsave
      nsave = 10
      DO i = 1, nsave
         esave(i) = 0
         msave(i) = 0
      END DO
      DO i = 1, nsave
         Ce(i) = 0
         Cm(i) = 0
      END DO
      END
C
      SUBROUTINE initialize(accum,accept)
*     積算された磁化とエネルギーの値を保存するために配列を使う
*     配列を使うことで容易に他の量を加えることができる
      IMPLICIT NONE
      REAL*8 accum(10), accept
      INTEGER i
      DO i = 1, 5
         accum(i) = 0
      END DO
      accept = 0
      END
C
      SUBROUTINE Metropolis(N,L,spin,E,M,w,accept)
*     単位スピン当りモンテカルロ・ステップの実行
      IMPLICIT NONE
      REAL*8 E, M, w(-8:8), accept
      REAL rnd
      INTEGER dE, ispin, x, y, N, L, spin(32,32), DeltaE
      DO ispin = 1, N
         x = int(L*rnd(0) + 1)              ! ランダムな x 座標
         y = int(L*rnd(0) + 1)              ! ランダムな y 座標
         dE = DeltaE(x,y,L,spin)            ! エネルギー変化の計算
         IF(rnd(0) .LE. w(dE)) THEN
            spin(x,y) = -spin(x,y)          ! スピンを反転する
            accept = accept + 1
            M = M + 2*spin(x,y)
            E = E + dE
            CALL show_change(x,y,L,spin)
         END IF
      END DO
      END
C
      FUNCTION DeltaE(x,y,L,spin)
*     周期的境界条件
      IMPLICIT NONE
      INTEGER DeltaE, down, left, right, up, x, y, L, spin(32,32)
      IF(x .EQ. 1) THEN
         left = spin(L,y)
      ELSE
         left = spin(x - 1,y)
      END IF
      IF(x .EQ. L) THEN
         right = spin(1,y)
      ELSE
         right = spin(x + 1,y)
      END IF
      IF(y .EQ. 1) THEN
         down = spin(x,L)
      ELSE
         down = spin(x,y - 1)
      END IF
      IF(y .EQ. L) THEN
         up = spin(x,1)
      ELSE
         up = spin(x,y + 1)
      END IF
      DeltaE = 2*spin(x,y)*(left + right + up + down)
      END
C
      SUBROUTINE show_change(i,j,L,spin)
      IMPLICIT NONE
      REAL X, Y
      INTEGER IR_, RBTN, i, j, L, spin(32,32)
      LOGICAL show
      SAVE show
      DATA show/.TRUE./
      CALL GWmouse(IR_, RBTN, X, Y)
      IF(IR_ .NE. 0) THEN
         IF(show) THEN
            show = .FALSE.
         ELSE
            show = .TRUE.
            CALL plotspins(L,spin)
         END IF
         DO WHILE(IR_ .NE. 0)
            CALL GWmouse(IR_, RBTN, X, Y)
         END DO
      ENDIF
      IF(.NOT.show) RETURN
      CALL GWerase(IR_, (i-1)*L+j, 0)
      CALL GWsetogn(IR_, 0, (i-1)*L+j)
      IF(spin(i,j) .EQ. 1) THEN
         CALL GWsrect(IR_, REAL(i), REAL(j), i+1.0, j+1.0, 13)
      ELSE
         CALL GWsrect(IR_, REAL(i), REAL(j), i+1.0, j+1.0, 14)
      END IF
      END
C
      SUBROUTINE mydata(E,M,accum)
*     各スピン当りモンテカルロ・ステップの後にデータを積算する
      IMPLICIT NONE
      REAL*8 E, M, accum(10)
      accum(1) = accum(1) + E
      accum(2) = accum(2) + E*E
      accum(3) = accum(3) + M
      accum(4) = accum(4) + M*M
      accum(5) = accum(5) + abs(M)
      END
C
      SUBROUTINE output(T,N,mcs,accum,accept)
      IMPLICIT NONE
      REAL*8 abs_mave, e2ave, eave, m2ave, mave, norm, T,
     +       accum(10), accept
      INTEGER N, mcs
      norm = 1.0D0/(mcs*N)                      ! スピン当りの平均
      accept = accept*norm
      eave = accum(1)*norm
      e2ave = accum(2)*norm
      mave = accum(3)*norm
      m2ave = accum(4)*norm
      abs_mave = accum(5)*norm
      WRITE(*,*) 'temperature = ', T
      WRITE(*,*) 'acceptance probability = ', accept
      WRITE(*,*) 'mean energy per spin = ', eave
      WRITE(*,*) 'mean squared energy per spin = ', e2ave
      WRITE(*,*) 'mean magnetization per spin = ', mave
      WRITE(*,*) 'mean of absolute magnetization per spin = ', abs_mave
      WRITE(*,*) 'mean squared magnetization per spin = ', m2ave
      END
C
      SUBROUTINE save_config(L,T,spin)
      IMPLICIT NONE
      REAL*8 T
      INTEGER IO2_, L, x, y, spin(32,32)
      CHARACTER file*80
      DATA IO2_/12/
      WRITE(*,'(A,$)') 'name of file for last configuration = '
      READ(*,'(A)') file
      OPEN(IO2_, file = file)
      WRITE(*,*) T
      DO y = 1, L
         DO x = 1, L
            WRITE(*,*) spin(x,y)
         END DO
      END DO
      CLOSE(IO2_)
      END
C
      SUBROUTINE read_config(L,T,spin)
      IMPLICIT NONE
      REAL*8 T
      INTEGER IO1_, L, x, y, spin(32,32)
      CHARACTER file*80
      DATA IO1_/11/
      WRITE(*,'(A,$)') 'filename?'
      READ(*,'(A)') file
      OPEN(IO1_, file = file)
      READ(IO1_,*) T
      DO y = 1, L
         DO x = 1, L
            READ(IO1_,*) spin(x,y)
         END DO
      END DO
      CLOSE(IO1_)
      END
C
      SUBROUTINE correl(Ce,Cm,E,M,esave,msave,pass,nsave)
*     時間相関関数用にデータを積算する
*     最後の nsave 個の M と E の値を格納する
      IMPLICIT NONE
      REAL*8 Ce(0:20), Cm(0:20), E, M, esave(100), msave(100)
      INTEGER index, index0, tdiff, pass, nsave
*     index0 = 最も古い値の配列要素番号
      index0 = mod(pass-1,nsave) + 1
      IF(pass .GT. nsave) THEN
*        nsave 個の値が格納された後の Ce と Cm を計算する
         index = index0
         DO tdiff = nsave, 1, -1
            Ce(tdiff) = Ce(tdiff) + E*esave(index)
            Cm(tdiff) = Cm(tdiff) + M*msave(index)
            index = index + 1
            IF(index .GT. nsave) index = 1
         END DO
      END IF
*     最新の値を最も古い値の配列位置に格納する
      esave(index0) = E
      msave(index0) = M
      END
C
      SUBROUTINE c_output(Ce,Cm,accum,mcs,nsave)
      IMPLICIT NONE
      REAL*8 e2bar, ebar, m2bar, mbar, norm, Ce(0:20), Cm(0:20),
     +       accum(10)
      INTEGER tdiff, mcs, nsave
*     時間相関関数の計算
      ebar = accum(1)/mcs
      e2bar = accum(2)/mcs
      Ce(0) = e2bar - ebar*ebar
      mbar = accum(3)/mcs
      m2bar = accum(4)/mcs
      Cm(0) = m2bar - mbar*mbar
      norm = 1.0D0/(mcs - nsave)
      WRITE(*,*)
      WRITE(*,'(3A13)') 't', 'Ce(t)', 'Cm(t)'
      WRITE(*,*)
      DO tdiff = 1, nsave
*        相関関数は C(t=0) = 1 及び C(infinity) = 0 
*        となるように定義される
         Ce(tdiff) = (Ce(tdiff)*norm - ebar*ebar)/Ce(0)
         Cm(tdiff) = (Cm(tdiff)*norm - mbar*mbar)/Cm(0)
         WRITE(*,'(I13,2F13.6)') tdiff, Ce(tdiff), Cm(tdiff)
      END DO
      END
