C _______________________ START 3-D SIMULATIONS
C
C                         Klypin, August 1993
C
      INCLUDE 'PMparameters.h'
C		   NROW = number of particles in 1 dimension
C		   NGRID= number of cells        in 1 dimension
C		   Npage =  number of particles per page = NROW**2
C
      COMMON / GRID/	GRX(NROW,NROW,NROW),GRY(NROW,NROW,NROW),
     +			GRZ(NROW,NROW,NROW)
      COMMON / GRIV/	GVX(NROW,NROW,NROW),GVY(NROW,NROW,NROW),
     +			GVZ(NROW,NROW,NROW)
      COMMON / ROW2/	XPAR2(NPAGE),YPAR2(NPAGE),ZPAR2(NPAGE),
     +			VX2(NPAGE),VY2(NPAGE),VZ2(NPAGE)
      COMMON / TRUNCOM/ Om,Omb,Omc,Omnu,Par(6),ns,qqscaleb,QSCALE
      COMMON / KINENRG/ SKINE,SX,SY,SZ,SX2,SY2,SZ2
      Character  Hd*5,Tail*4,Nm*40
      DATA PI		/3.1415926535/

      Hd  ='PMcrs'
      Tail='.DAT'
     
      CALL InitValues(NBYTE,SCALEL)
      write (*,*) ' InitValues is done. N bytes=',NBYTE

      OPEN(UNIT=9,FILE='PMcrd.DAT',form='unformatted')
      write (9)             ! this clears old header file
      CLOSE (9)
      CALL RDTAPE                  ! this opens files on disk
      OPEN(UNIT=16,FILE='RESNEW.DAT')
      write (16,*) ' Seed=',Nseed
       AEXP0 = AEXPN
       AEXPV = AEXPN - ASTEP/2.
C			                   get a realization of spectrum in /GRID/
       NRAND =Nseed
       CALL SPECTR(ALPHA,NRAND)
C			                        get the displacement vector by FFT
       IFOUR = INT(ALOG(FLOAT(NROW))/ALOG(2.)+0.5)
       Write (*,*) ' Ifour =',IFOUR,' Nrow=',NROW
       CALL VECTOR(IFOUR)
	    write (*,*) ' SPECTR is done. IFOUR = ',IFOUR,' Alpha=',ALPHA
        Fact =  sqrt(Om0+Oml0*AEXPV**3+Ocurv*AEXPV)
       VCONS = -ALPHA/(2.*PI/NGRID)*(AEXPV/AEXP0)*SQRT(AEXPV)*Fact
       XCONS =	 ALPHA/(2.*PI/NGRID)*(AEXPN/AEXP0)
       QFACT =  FLOAT(NGRID)/FLOAT(NROW)
                      SKINE = 0.
                      SX    = 0.
                      SY    = 0.
                      SZ    = 0.
                      SX2   = 0.
                      SY2   = 0.
                      SZ2   = 0.

C			           find x,v and write to file, page by page
      DO  KROW = 1, NROW
      	CALL SETXV(XCONS,VCONS,KROW)
	      CALL WRIROW(KROW,1)
      ENDDO
C                                     EKIN is kinet.energy at A(i-1/2)
      EKIN = 0.5*SKINE/AEXPV**2*PARTW
      DP   = NROW**3
      SX   = SX/DP
      SY   = SY/DP
      SZ   = SZ/DP
      SX2  = SQRT(SX2/DP)
      SY2  = SQRT(SY2/DP)
      SZ2  = SQRT(SZ2/DP)
      WRITE (*,'('' Displacement of a particle: MEAN'',3F9.4)')
     .	     SX,SY,SZ
      WRITE (*,'('' in cell units               RMS '',3F9.4)')
     .	     SX2,SY2,SZ2
      WRITE (16,'('' Displacement of a particle: RMS'',3F9.4)')
     .	     SX2,SY2,SZ2
      Veloc=sqrt( SKINE/AEXPV**2/NROW**3)*SCALEL/NGRID*
     .        100.*hubble
      WRITE (*,'('' Ekin='',E12.4,'' Veloc_cold(km/s)='',F8.2)')
     .       EKIN,Veloc
      WRITE (16,'('' Ekin='',E12.4,'' Veloc_cold(km/s)='',F8.2)')
     .       EKIN,Veloc
C			       write header and control data -------
      CALL WRTAPE

      STOP
      END
C--------------------------------------------------
C                                                     sqrt(Power spectrum)
C                                                           k = (2pi/L) wk
      REAL  FUNCTION TRUNF(wk)
C--------------------------------------------------
      INCLUDE 'PMparameters.h'
      PARAMETER (NSPEC =NROW/2)
      COMMON / TRUNCOM/ Om,Omb,Omc,Omnu,Par(6),ns,qqscaleb,QSCALE
      Real                 ns,k
      IF (WK.GE.FLOAT(NSPEC)) THEN
	       TRUNF =0.
	       RETURN
      ENDIF
      If(Par(6).ne.0.)Then   ! Holtzman approx
        k = QSCALE*wk
        sk= sqrt(k)
        TRUNF = k**(ns/2.) /
     .         (1.+sk*(Par(2)
     .            +sk*(Par(3)
     .            +sk*(Par(4)
     .            +sk*(Par(5) )))) )**(Par(6))
      Else                   ! BBKS + Sugiyama approx
c        Gammaeff =Om*hsmall/exp(Omb*(1.+sqrt(hsmall/0.5)/Om))
c        Q = wk/hsmall/Gammaeff
        k = QSCALE*wk
        Q = k*qqscaleb
        TRUNF = k**(ns/2.)* LOG(1.+Par(1)*Q)/(Par(1)*Q)/
     .          sqrt(sqrt(1.+Q*(Par(2)+
     .                  Q*(Par(3)**2+
     .                  Q*(Par(4)**3+
     .                  Q*(Par(5)**4) )))))
      EndIf
      RETURN
      END
C*********************************************************************
C	   Power spectrum for CDM. Wk-  wavenumber(Mpc^-1, h=0.5)
C                         P = wk *T^2(k)
C                         QSCALE = 2pi/Box_size,  QS =hubble**(-2)
c      FUNCTION TRUNF(WK)
c      COMMON / TRUNCOM/ QSCALE,g,gy
c      COMMON / TRUNCDM/ QS,A1,A2,A3,A4,A5,A32,A43,A54
c       Q = QSCALE*QS*WK
c       TRUNF = sqrt( WK*
c     +		 (LOG(1.+A1*Q)/(A1*Q))**2/
c     +		 SQRT(1.+Q*(A2 +Q*(A32   +Q*(A43+Q*A54)))) )
c
c      RETURN
c      END

C*********************************************************************
C			  INITIALIZE CONSTANTS:
C			      Scalel    =length of square in MPC
C			      AEXPN =expansion factor (current)
C			      ASTEP	  =increment of expansion factor: AEXPN =AEXP_0+N*ASTEP
C                     PARTW	=weight of a particle: total 'mass' per cell must be unity
c                                          even for LCDM or OCDM 
C			      AMPLT	=amplitude of perturbations inside the simulation box
C			      NBYTE	=record length in bytes
C
      SUBROUTINE InitValues(NBYTE,SCALEL)
      INCLUDE 'PMparameters.h'
      COMMON / TRUNCOM/ Om,Omb,Omc,Omnu,Par(6),ns,qqscaleb,QSCALE
      COMMON / FERMI  / Vscale
      Real                 ns      
      Character            Answer*1
      Real                 INPUT
      DATA PI		         /3.1415926535/

       Write (*,*) 'Would you like to use a model provided in cdm.fit '
       Write (*,*) 'or your own model?' 
       Write (*,'(A,$)') ' Enter Yes for cdm.fit;  No for your model ='
       Read  (*,*)  Answer
       HEADER='N=128x256 L=20h-1CDMsig=0.239 z=30-----------'
       write (*,*) '------ Enter Header for the run up to 45 characters'
       write (*,*) '       Example:'
       write (*,'(A)') HEADER                                   
       read  (*,'(A)') HEADER
       write (*,'(A)') HEADER
       AEXPN =INPUT(' Initial expansion parameter (0-1)=')
       ASTEP =INPUT(' Step of the expansion parameter  =')
       AMPLT =INPUT(' Amplitude of density fluctuations=')
       ns    =INPUT(' Slope of the Power spectrum     n=')
       SCALEL=INPUT(' Box size (Mpc/h)                 =')
       Ocurv =INPUT(' Omega_curvature   at present     =')
       Nseed =INPUT(' Random seed (Integer  1-2^31)    =') 
       If(Answer.eq.'Y' .or. Answer.eq.'y')Then
          CALL MODEL(hsmall)
          hubble=hsmall
          Om0   =Om
          Oml0  =1. -Om0 -Ocurv
       Else
       write (*,*) ' You use your cosmological model.'
       write (*,*) ' Be sure you provide routine TRUNF'
       hubble=INPUT(' The Hubble constant (h=H/100)    =')
       Om0   =INPUT(' Omega_0 matter    at present     =')
       Oml0  =INPUT(' Omega_lambda      at present     =')
       EndIf
       SCALEL=SCALEL/hubble   ! scale it to real megaparsecs      
       NBYTE = NPAGE*6*4
       Nspecies =0
       W     = (FLOAT(NGRID)/FLOAT(NROW))**3
       PARTW = W 
c                                 W1    = (W -Partw)/Nspecies
       ISTEP = 0
       TINTG = 0.
       AU0   = 0.
       AEU0  = 0.
       EKIN  = 0.
       EKIN1 = 0.
       EKIN2 = 0.
       NROWC = NROW
       NGRIDC= NGRID
	     QSCALE = 2.*PI/SCALEL
	     QS     = hubble**(-2)
        write (*,*) ' Qscale=',QSCALE,SCALEL,ns

      RETURN
      END
C________________________________________Read parameters of the model
C                                 Om       = Omega_0
C                                 Omb     = Omega_baryon
C                                 Omnu  = Omega_neutrino
C                                 hsmall  = hubble constant
C                                 Par      = fitting parameters
C                                 Par(6) = 0  --- bbks-style+Hu&Sugiyama
C                                 Par(6) ne 0 --- holtzman-style
      SUBROUTINE MODEL(hsmall)
C---------------------------------------
      INCLUDE 'PMparameters.h'
      COMMON / TRUNCOM/ Om,Omb,Omc,Omnu,Par(6),ns,qqscaleb,QSCALE
      Real                 ns,INPUT
      Character             Header1*79

      OPEN(2,file='cdm.fit')
      Line1 =INPUT(' Enter Line Number in cdm.fit     =')
      Read(2,'(A)') Header1
      If(Line1.gt.2)Then
         Do i=1,Line1-2
            Read (2,*) a
         EndDo
      EndIf
      Read (2,*) Om,Omb,Omc,Omnu,hsmall,Par
      CLOSE (2) 
      
      theta = 2.726/2.7  ! = T_cmb/2.7
      Ob0   =Omb/Om      ! Hu&Sugiyama fitting
      Omh2  =Om*hsmall**2
      a1    =(46.9*Omh2)**0.670*(1.+(32.1*Omh2)**(-0.532))
      a2    =(12.0*Omh2)**0.424*(1.+(45.*Omh2)**(-0.582))
      alpha =1./a1**Ob0/a2**(Ob0**3)
      qqscaleb = theta**2/(1.-Ob0)**0.60/sqrt(alpha)/Omh2

      Write (*,20)Om,Omb,Omc,Omnu,hsmall,Par
      Write (1,20)Om,Omb,Omc,Omnu,hsmall,Par
 20   Format(' Model: Om_0=',F5.3,' Om_baryon=',F5.3,
     .       ' Om_cold=',F5.3,' O_nu=',F5.3,
     .       ' hsmall=',F4.2,/8x,'Parameters=',(6G9.3))
      Return
      End
C------------------------------------------------
C                             Make a realization of spectrum of perturbations:
C                             ALPHA  = normalization factor for displacements
C                             NRAND = seed for random numbers 
      SUBROUTINE SPECTR(ALPHA,NRAND)
C------------------------------------------------
      INCLUDE 'PMparameters.h'
      PARAMETER (NSPEC = NROW/2 ) ! No waves sharter than Ny
      COMMON / GRID/	GRX(NROW,NROW,NROW),GRY(NROW,NROW,NROW),
     +			         GRZ(NROW,NROW,NROW)
C						    Set spectrum
      SUMM = 0.
      DO  MK3 = 1,NROW
      DO  MK2 = 1,NROW
      DO  MK1 = 1,NROW
	     GRX(MK1,MK2,MK3) =0.
	     GRY(MK1,MK2,MK3) =0.
	     GRZ(MK1,MK2,MK3) =0.
      ENDDO
      ENDDO
      ENDDO

      DO  k = 1,NROW
         ksign = -1
         kz    = k +NSPEC
         k3    = k - 1
         If(k3.GT.NSPEC)k3=k3-NSPEC
           IF(kz.gt.NROW)THEN
              kz    =kz -NROW
              ksign =1
           ENDIF
           WK3= K3**2
c	        write (*,*) '  K=',k,' K3=',K3,' Nspec=',NSPEC
           DO  j = 1,NROW
              jsign = -1
              jz    = j +NSPEC
              j3    = j - 1
              If(j3.GT.NSPEC)j3=j3-NSPEC
                IF(jz.gt.NROW)THEN
                   jz    =jz -NROW
                   jsign =1
                ENDIF
                WJ3= j3**2
                DO  i = 1,NROW
                   isign = -1
                   iz    = i +NSPEC
                   i3    = i - 1
                   If(i3.GT.NSPEC)i3=i3-NSPEC
                     IF(iz.gt.NROW)THEN
                        iz    =iz -NROW
                        isign =1
                     ENDIF
                     WI3= i3**2

	       IF(i3+j3+k3.EQ.0)THEN
		         GRX(1,1,1) = 0.
		         GRY(1,1,1) = 0.
		         GRZ(1,1,1) = 0.
	       ELSE
		  WD = WI3 + WJ3 + WK3
		  WK = SQRT(WD)
		  TS = TRUNF(WK) * GAUSS(NRAND)
		  TRX = TS / WD
		  GRX(iz, j, k) = i3 * TRX *isign
		  GRY( i,jz, k) = j3 * TRX *jsign
		  GRZ( i, j,kz) = k3 * TRX *ksign
		  SUMM = SUMM + TS**2
	       ENDIF
	    ENDDO
	   ENDDO
      ENDDO
      IF(SUMM.LE.0.)WRITE (*,*) ' Error!!! Summ over spectrum = 0'
         write (*,*) ' ==== SPECTR ===  SUMM=',SUMM
      ALPHA = AMPLT / SQRT(SUMM) *sqrt(8.)
      RETURN
      END
C------------------------------------------------
C		                        Define coordinates and velosities for
C		                        all particles of current row (given q2)
C			                       x = q - amplt*b(t)/b(t0)*S(q1,q2)
C			                       momentum = a**2*deriv x
C                     
C		                        Twidled variables:
C		                        x= q - {1/(2 pi Ngrid)}{Alpha*a(t)/a(0)}*S
C		                        P=	- {1/(2 pi Ngrid)}{Alpha*a(t)**1.5/a(0)}*S
C                     
C		                        q(i) = Ngrid/Nrow*(i-1) + 1
C		                        x = q - xcons*S
C		                        P =	   vcons*S
      SUBROUTINE SETXV(XCONS,VCONS,K)
C------------------------------------------------
      INCLUDE 'PMparameters.h'
      COMMON / KINENRG/ SKINE,SX,SY,SZ,SX2,SY2,SZ2
      COMMON / GRID/	GRX(NROW,NROW,NROW),GRY(NROW,NROW,NROW),
     +			GRZ(NROW,NROW,NROW)

      QFACT = FLOAT(NGRID)/FLOAT(NROW)
      XMAX  = FLOAT(NGRID) + 1.
      XSHF  = FLOAT(NGRID)
      Q3    = QFACT*(K-1.) +1.
      DO  J = 1,NROW
	      Q2 = QFACT*(J-1.) +1.
	   DO  I = 1,NROW
	      Q1 = QFACT*(I-1.) +1.
            IJ    =      I +(J-1)*NROW
	      DX	      =      XCONS*GRX(I,J,K)
	      DY	      =      XCONS*GRY(I,J,K)
	      DZ	      =      XCONS*GRZ(I,J,K)
C           the factor 0.5 in the following 3 lines places unperturbed
c           particle in the middle of a cell. This reduces the shot noise
C          Small number 1.E-4 is to avoid placing some particles
C          exactly in cell nodes. Does not make much differentce.
	      XPAR(IJ) = Q1 - DX    + 1.E-4  +0.5 
	      YPAR(IJ) = Q2 - DY    + 1.E-4  +0.5
	      ZPAR(IJ) = Q3 - DZ    + 1.E-4  +0.5
	      VX(IJ)   =      VCONS*GRX(I,J,K)
	      VY(IJ)   =      VCONS*GRY(I,J,K)
	      VZ(IJ)   =      VCONS*GRZ(I,J,K)
C                                Periodical boundary conditions
	       IF(XPAR(IJ).GT.XMAX)	XPAR(IJ)=XPAR(IJ)-XSHF
	       IF(XPAR(IJ).LE.1.)	XPAR(IJ)=XPAR(IJ)+XSHF
	       IF(YPAR(IJ).GT.XMAX)	YPAR(IJ)=YPAR(IJ)-XSHF
	       IF(YPAR(IJ).LE.1.)	YPAR(IJ)=YPAR(IJ)+XSHF
	       IF(ZPAR(IJ).GT.XMAX)	ZPAR(IJ)=ZPAR(IJ)-XSHF
	       IF(ZPAR(IJ).LE.1.)	ZPAR(IJ)=ZPAR(IJ)+XSHF
	      SX	      = SX    + DX
	      SY	      = SY    + DY
	      SZ	      = SZ    + DZ
	      SX2      = SX2   + DX**2
	      SY2      = SY2   + DY**2
	      SZ2      = SZ2   + DZ**2
	      SKINE    = SKINE + VX(IJ)**2 +VY(IJ)**2 +VZ(IJ)**2
	   ENDDO
      ENDDO
      RETURN
      END
C------------------------------------------------
C                                              				   FFT of the spectrum
      SUBROUTINE VECTOR(IFOUR)
C------------------------------------------------
      INCLUDE 'PMparameters.h'
      COMMON / GRID/	GRX(NROW,NROW,NROW),GRY(NROW,NROW,NROW),
     +			GRZ(NROW,NROW,NROW)
      CALL SETF67(4,IFOUR)   ! initialize FFT
C				       x-component
      DO K=1,NROW
	   DO J=1,NROW
	    DO I=1,NROW
	       Zf(I) =GRX(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO I=1,NROW
	       GRX(I,J,K) = Yf(I)
	    ENDDO
	   ENDDO
      ENDDO
      DO K=1,NROW
	   DO I=1,NROW
	    DO J=1,NROW
	       Zf(J) =GRX(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO J=1,NROW
	       GRX(I,J,K) = Yf(J)
	    ENDDO
	   ENDDO
      ENDDO
      DO J=1,NROW
	   DO I=1,NROW
	    DO K=1,NROW
	       Zf(K) =GRX(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO K=1,NROW
	       GRX(I,J,K) = Yf(K)/8.
	    ENDDO
	   ENDDO
      ENDDO
				       write (*,*) ' x is done'
C				       Y-component
      DO K=1,NROW
	   DO J=1,NROW
	    DO I=1,NROW
	       Zf(I) =GRY(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO I=1,NROW
	       GRY(I,J,K) = Yf(I)
	    ENDDO
	   ENDDO
      ENDDO
      DO K=1,NROW
	   DO I=1,NROW
	    DO J=1,NROW
	       Zf(J) =GRY(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO J=1,NROW
	       GRY(I,J,K) = Yf(J)
	    ENDDO
	   ENDDO
      ENDDO
      DO J=1,NROW
	   DO I=1,NROW
	    DO K=1,NROW
	       Zf(K) =GRY(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO K=1,NROW
	       GRY(I,J,K) = Yf(K)/8.
	    ENDDO
	   ENDDO
      ENDDO
				       write (*,*) ' y is done'
C				       Z-component
      DO K=1,NROW
	   DO J=1,NROW
	    DO I=1,NROW
	       Zf(I) =GRZ(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO I=1,NROW
	       GRZ(I,J,K) = Yf(I)
	    ENDDO
	   ENDDO
      ENDDO
      DO K=1,NROW
	   DO I=1,NROW
	    DO J=1,NROW
	       Zf(J) =GRZ(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO J=1,NROW
	       GRZ(I,J,K) = Yf(J)
	    ENDDO
	   ENDDO
      ENDDO
      DO J=1,NROW
	   DO I=1,NROW
	    DO K=1,NROW
	       Zf(K) =GRZ(I,J,K)
	    ENDDO
	    CALL FOUR67(4,IFOUR)
	    DO K=1,NROW
	       GRZ(I,J,K) = Yf(K)/8.
	    ENDDO
	   ENDDO
      ENDDO
				       write (*,*) ' z is done'
      RETURN
      END
