Solving Rossler Attractor using Runge-Kutta 4 - algorithm

I am trying to get a solution for the Rossler attractor system using RK-4, with parameters a=0.2, b=0.2, c=6 and initial conditions x0=-5.6, y0=0, z0=0. I tried solving using Fortran but the result is only displaying the initial conditions even after 1000 iterations. What mistakes am I making?
implicit none
external rossler
integer::i,j=0,n,nstep
real::a,b,c,y1(3),t0,dt,t1,t2,ya(3),yb(3),yd(3),t,x0,y0,z0,x(1000),y(1000),z(1000),k1(3),k2(3),k3(3),k4(3),h
print *, "enter the values of a,b,c"
read (*,*) a,b,c
print *, "enter the values of x0,y0,z0"
read (*,*) x0,y0,z0
n=3
t0=0.0
h=0.05
ya(1)=x0
ya(2)=y0
ya(3)=z0
nstep=1000
do i=1,nstep
t1=t0
t2=t0+h
call rk4(rossler,t1,t2,1,N,k1,k2,k3,k4,Ya,Y1,Yb)
x(i)=ya(1)
y(i)=ya(2)
z(i)=ya(3)
open (99,file="rossler.txt")
write(99,*) x(i),y(i),z(i)
end do
end program
subroutine rossler(T,Yd,YB,N)
implicit none
integer n
real t,yb(n),yd(n),a,b,c
yd(1)=-yb(2)-yb(3)
Yd(2)=yb(1)+a*yb(2)
Yd(3)=b+yb(3)*(yb(1)-c)
return
end
subroutine rk4(rossler,t1,t2,nstep,N,k1,k2,k3,k4,Ya,Y1,Yb)
implicit none
external rossler
integer nstep,n,i,j
REAL T1,T2,Ya(N),k1(n),k2(n),k3(n),k4(n),H,Y1(N),T,yb(n)
T=T1+(I-1)*H
CALL rossler(T,Yb,Ya,N)
DO J=1,N
k1(j)=YB(J)*H
end do
CONTINUE
CALL rossler(T+0.5*H,Yb,Ya+k1*0.5,N)
DO J=1,N
k2(j)=YB(J)*H
enddo
CONTINUE
CALL rossler(T+0.5*H,Yb,Ya+k2*0.5,N)
DO J=1,N
K3(J)=YB(J)*H
enddo
CONTINUE
CALL rossler(T+H,Yb,Ya+k3,N)
DO J=1,n
K4(J)=YB(J)*H
Y1(J)=Ya(J)+(k1(j)+k4(j)+2.0*(k2(j)+k3(j)))/6.0
enddo
CONTINUE
DO J=1,N
Ya(J)=Y1(j)
enddo
CONTINUE
enddo
RETURN
END

Although the question seems a duplicate of another question, here I am attaching a minimally modified code so that the OP can compare it with the original one. The essential modifications are that I have removed all the unused variables, moved a, b, c, and h to a parameter module, and cleaned up unnecessary statements (like CONTINUE). No newer features of Fortran introduced (including interface block for rossler), so it is hopefully straight-forward to see how the code has been changed.
module params
real :: a, b, c, h
end module
program main
use params, only: a, b, c, h
implicit none
external rossler
integer :: i, n, nstep
real :: t, y(3)
a = 0.2
b = 0.2
c = 5.7
n = 3
t = 0.0
h = 0.05
y(1) = -5.6
y(2) = 0.0
y(3) = 0.0
nstep = 7000
open(99, file="rossler.txt")
do i = 1,nstep
call rk4 ( rossler, t, n, y )
write(99,*) y(1), y(2), y(3)
end do
end program
subroutine rossler ( t, dy, y, n )
use params, only: a, b, c
implicit none
integer n
real t, dy(n), y(n)
dy(1) = -y(2) - y(3)
dy(2) = y(1) + a * y(2)
dy(3) = b + ( y(1) - c ) * y(3)
end
subroutine rk4 ( deriv, t, n, y )
use params, only: h
implicit none
external deriv
integer n, j
real y(n), t, k1(n), k2(n), k3(n), k4(n), d(n)
call deriv ( t, d, y, n )
do j = 1,n
k1(j) = d(j) * h
enddo
call deriv ( t+0.5*h, d, y+k1*0.5, n )
DO j = 1,n
k2(j) = d(j) * h
enddo
call deriv ( t+0.5*h, d, y+k2*0.5, n )
do j = 1,n
k3(j) = d(j) * h
enddo
call deriv ( t+h, d, y+k3, n )
do j = 1,n
k4(j) = d(j) * h
y(j) = y(j) + ( k1(j) + k4(j) + 2.0 * (k2(j) + k3(j)) ) / 6.0
enddo
t = t + h
end
By choosing the parameters as a = 0.2, b = 0.2, c = 5.7 and nstep = 7000, the modified code gave the so-called Rössler attractor, which is very beautiful and appears close in pattern to that displayed in the Wiki page. So with the minimal modifications, I believe the OP will also get a similar picture (it may be interesting to see how the pattern changes depending on parameters).
2D projection of the trajectory onto the xy plane:

The problem here is exactly the same as in another question, although I can no longer vote to close as a duplicate.
To make explicit and add the comments on the question: a, b and c take the place of omega from that question; the subroutine rossler as the function fcn.
An answer to that question addresses how this issue can be resolved.

Related

OpenACC constant parameters

I am wondering what is the proper way to handle constants in OpenACC kernels.
For example, in the following code
module vecaddmod
implicit none
integer, parameter :: n = 100000
!$acc declare create(n)
contains
subroutine vecaddgpu(r, a, b)
real, dimension(:) :: r, a, b
integer :: i
!$acc update self(n)
!$acc data present(n)
!$acc kernels loop copyin(a(1:n),b(1:n)) copyout(r(1:n))
do i = 1, n
r(i) = a(i) + b(i)
enddo
!$acc end data
end subroutine vecaddgpu
end module vecaddmod
program main
use vecaddmod
implicit none
integer :: i, errs, argcount
real, dimension(:), allocatable :: a, b, r, e
character*10 :: arg1
allocate( a(n), b(n), r(n), e(n) )
do i = 1, n
a(i) = i
b(i) = 1000*i
enddo
! compute on the GPU
call vecaddgpu( r, a, b )
! compute on the host to compare
do i = 1, n
e(i) = a(i) + b(i)
enddo
! compare results
errs = 0
do i = 1, n
if( r(i) /= e(i) )then
errs = errs + 1
endif
enddo
print *, errs, ' errors found'
if( errs ) call exit(errs)
end program main
n is declared as a constant on CPU in a module, and it is used as the range in the loop. nvfortran warns me about Constant or Parameter used in data clause. Is the above example the proper way to handle this? Can I take advantage of the constant memory on GPU, such that I don't need to copy it from CPU to GPU for each kernel launch?
Thanks.
The compiler will replace parameters with the literal value so no need to put them in data regions.
module vecaddmod
implicit none
integer, parameter :: n = 100000
contains
subroutine vecaddgpu(r, a, b)
real, dimension(:) :: r, a, b
integer :: i
!$acc kernels loop copyin(a(1:n),b(1:n)) copyout(r(1:n))
do i = 1, n
r(i) = a(i) + b(i)
enddo
end subroutine vecaddgpu
end module vecaddmod
...
% nvfortran -acc -Minfo=accel test.f90
vecaddgpu:
11, Generating copyin(a(:100000)) << "n" is replaced with 100000
Generating copyout(r(:100000))
Generating copyin(b(:100000))
12, Loop is parallelizable
Generating Tesla code
12, !$acc loop gang, vector(128) ! blockidx%x threadidx%x

F95 Send/Receive Memory Errors for Array Sending

I'm new to parallel programming and attempting to produce a sparse matrix-vector calculation in Fortran 95. I'm working on a subprogram that only gathers the components of the vector that the sparse matrix will touch (instead of MPI_AllGather), but I keep getting SIGSESV errors. I know this means I've asked the process to touch something it can't/doesn't exist, but I can't for the life of me figure out what it could be.
!Gather the vector matrix in matrix vector multiplication for sparse matrices
subroutine sparsegather(u,BW,myid,nprocs)
use header
include "mpif.h"
type(Vector), intent(inout) :: u
integer,intent(in) :: BW !Bandwidth
integer,intent(in) :: myid !process id
integer,intent(in) :: nprocs !number of processes
integer :: n, i
integer,dimension(BW) :: rlr, rrr, slr, srr !Range of receive left/right, send left/right
real(kind=rk),dimension(BW) :: rl, rr, sl, sr !Arrays of actual values
integer :: ierr
n = u%n !Length of whole vector - used in periodic condition
!Define ranges
do i = 1,BW
rlr(i) = u%ibeg - BW - 1 + i
rrr(i) = u%iend + i
srr(i) = u%iend - i + 1
slr(i) = u%ibeg + i - 1
end do
!Periodic conditions
do i = 1,BW
if (rlr(i) < 1) then
rlr(i) = rlr(i) + n
end if
if ((srr(i) < 1) then
srr(i) = srr(i) + n
end if
if (rrr(i) > n ) then
rrr(i) = rrr(i) - n
end if
if (slr(i) > n ) then
slr(i) = slr(i) - n
end if
end do
!Store the matrix values being sent over
sl = u%xx(slr)
sr = u%xx(srr)
!Pass the value parcels around
if (myid == 0) then
call MPI_Recv(rl,BW,MPI_DOUBLE_PRECISION,nprocs-1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
call MPI_Send(sr,BW,MPI_DOUBLE_PRECISION,myid+1,0,MPI_COMM_WORLD,ierr)
call MPI_Recv(rr,BW,MPI_DOUBLE_PRECISION,myid+1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
call MPI_Send(sl,BW,MPI_DOUBLE_PRECISION,nprocs-1,0,MPI_COMM_WORLD,ierr)
elseif (myid == nprocs-1) then
call MPI_Send(sr,BW,MPI_DOUBLE_PRECISION,0,0,MPI_COMM_WORLD,ierr)
call MPI_Recv(rl,BW,MPI_DOUBLE_PRECISION,myid-1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
call MPI_Send(sl,BW,MPI_DOUBLE_PRECISION,myid-1,0,MPI_COMM_WORLD,ierr)
call MPI_Recv(rr,BW,MPI_DOUBLE_PRECISION,0,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
elseif (mod(myid,2) == 0) then
call MPI_Recv(rl,BW,MPI_DOUBLE_PRECISION,myid-1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
call MPI_Send(sr,BW,MPI_DOUBLE_PRECISION,myid+1,0,MPI_COMM_WORLD,ierr)
call MPI_Recv(rr,BW,MPI_DOUBLE_PRECISION,myid+1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
call MPI_Send(sl,BW,MPI_DOUBLE_PRECISION,myid-1,0,MPI_COMM_WORLD,ierr)
else
call MPI_Send(sr,BW,MPI_DOUBLE_PRECISION,myid+1,0,MPI_COMM_WORLD,ierr)
call MPI_Recv(rl,BW,MPI_DOUBLE_PRECISION,myid-1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
call MPI_Send(sl,BW,MPI_DOUBLE_PRECISION,myid-1,0,MPI_COMM_WORLD,ierr)
call MPI_Recv(rr,BW,MPI_DOUBLE_PRECISION,myid+1,MPI_ANY_TAG,MPI_COMM_WORLD,ierr)
end if
u%xx(rrr) = rr
u%xx(rlr) = rl
end subroutine sparsegather
u is an object with the vector values stored in %xx and its size in %n. The relevant starting point and end points for each processor are in %ibeg and %iend.
BW is bandwith of the sparse banded matrix. This equation has periodic conditions, so values to the left of the start of the vector wrap around to the right side (and vice versa), which is done in the periodic conditions section.

Why a declared and filled mwSize saves the day when a constant doesn't

I have this subroutine :
subroutine FotranDgemmMatrixMultiplication(A, B, C, m, n, p, mkl)
integer m, n, p
real*8 mkl
real*8 A(m,n), B(n,p), C(m,p)
if (mkl .ne. 1) then
C = matmul(A,B)
else
C = matmul(B,A)
endif
end
that I use in a mex gateway file as follows :
#include <fintrf.h>
C The gateway routine
subroutine mexFunction(nlhs, plhs, nrhs, prhs)
implicit none
mwPointer mxGetM, mxGetN, mxIsNumeric, mxIsLogical
mwPointer mxCreateDoubleMatrix
mwPointer plhs(*), prhs(*)
mwPointer A_pr, B_pr, C_pr, mkl_pr
mwPointer mxGetPr
integer nlhs, nrhs
real*8, allocatable, dimension(:,:) :: x, y, z
real*8 mkl
mwSize m, n, p, q, r, s
mwSize size1, size2, size3
C Check for proper number of arguments.
if (nrhs .ne. 3) then
call mexErrMsgTxt('Three inputs required.')
elseif (nlhs .ne. 1) then
call mexErrMsgTxt('One output required.')
endif
C Check to see both inputs are numeric.
if (mxIsNumeric(prhs(1)) .ne. 1) then
call mexErrMsgTxt('Input #1 is not a numeric array.')
elseif (mxIsNumeric(prhs(2)) .ne. 1) then
call mexErrMsgTxt('Input #2 is not a numeric array.')
elseif (mxIsNumeric(prhs(3)) .ne. 1) then
call mexErrMsgTxt('Input #3 is not a numeric array.')
endif
C Get the size of the input matrix #1.
m = mxGetM(prhs(1))
n = mxGetN(prhs(1))
C Get the size of the input matrix #2.
p = mxGetM(prhs(2))
q = mxGetN(prhs(2))
C Check that the sizes are compatible for a matrix product
if (n .ne. p) then
call mexErrMsgTxt('nbcol1 should be equal to nbrow2.')
endif
size1 = m*n
size2 = p*q
C Check that the input #3 is a scalar
r = mxGetM(prhs(3))
s = mxGetN(prhs(3))
if(r .ne. 1 .or. s .ne. 1) then
call mexErrMsgTxt('Input #3 is not a scalar.')
endif
C Create matrix for the return argument.
plhs(1) = mxCreateDoubleMatrix(m, q, 0)
A_pr = mxGetPr(prhs(1))
B_pr = mxGetPr(prhs(2))
mkl_pr = mxGetPr(prhs(3))
C_pr = mxGetPr(plhs(1))
allocate( x(m,n), y(p,q), z(m,q) )
C Load the data into Fortran arrays.
call mxCopyPtrToReal8(A_pr, x, size1)
call mxCopyPtrToReal8(B_pr, y, size2)
call mxCopyPtrToReal8(mkl_pr, mkl, 1) ! suspicious line
C Call the computational subroutine.
call FotranDgemmMatrixMultiplication(x, y, z, m, n, q, mkl) ! crash here
C Load the output into a MATLAB array.
size3 = m*q
call mxCopyReal8ToPtr(z, C_pr, size3)
!deallocate(x,y,z)
return
end
that executes as intended in debug but that crashes in release, the crash occuring at line :
call FotranDgemmMatrixMultiplication(x, y, z, m, n, q, mkl)
If I add a mwSize sizeOne with sizeOne = 1 and replace the suspicious line (see the code) with :
call mxCopyPtrToReal8(mkl_pr, mkl, sizeOne)
then the crash doesn't occur anymore. I don't understand what is happening as in x64 the "type" mwSize is defined (in fintrf.h) as mwpointer which itself is defined as integer*4, which should treat the "constant" 1 correctly normally.

Wrong results and crashes from Fortran subroutine with OpenMP

I wrote the following code, and then tried using OpenMP to parallelise it. However, after I compiled the following OpenMP code using f2py, Python always generates certain errors when I run it. There are no error messages, only that the numbers are a bit off, and whenever I compile it with f2py and run it in Python, it kills the kernel.
I am wondering if this has anything to do with my parallel region. I am always a bit confused about which variables to take private so can anyone observe any errors?
subroutine simulate_omp(m,nt,s0,l,d,a,numthreads,x,y,psi)
!Pedestrian motion model
!input variables:
!n = m^2 = number of students
!nt: number of time steps
!s0: student speed
!l: initial spacing between students
!d: student motion influence by all other students within distance <= d
!a: noise amplitude
!numthreads: number of threads to use in parallel regions
!output variables:
!x,y: all m^2 student paths from i=1 to nt+1
!psi: synchronization parameter, store at all nt+1 times (including initial
condition)
use omp_lib
implicit none
integer, intent(in) :: m,nt,numthreads
real(kind=8), intent(in) :: s0,l,d,a
real(kind=8), dimension(m*m,nt+1), intent(out) :: x,y
real(kind=8), dimension(nt+1), intent(out) :: psi
real(kind=8), dimension(m*m,nt+1) :: xtemp,ytemp,u,v
real(kind=8), dimension(m*m,nt) :: usum,vsum,umean,vmean
real(kind=8) :: r(m*m)
real(kind=8),parameter :: pi = 4*atan(1.0_8)
integer :: i1,j1,k1,i2,j2,k2,count
!$call omp_set_num_threads(numthreads)
! initialize student positions
x = 0.d0
y = 0.d0
k1 = 0
do i1 = 1,m
do j1=1,m
k1 = k1 + 1
x(k1,1) = (j1-1)*l/2 - (m-1)*l/4
y(k1,1) = (i1-1)*l/2 - (m-1)*l/4
end do
end do
x(:,1) = x(:,1)/(m-1)
y(:,1) = y(:,1)/(m-1)
! initialize
xtemp(:,1) = x(:,1)
ytemp(:,1) = y(:,1)
call random_number(r)
u(:,1) = s0*cos(r*2*pi-pi)
v(:,1) = s0*sin(r*2*pi-pi)
psi(1) = sqrt(sum(u(:,1))**2+sum(v(:,1)**2))/dble(m)/dble(m)/s0
do i2 = 1,nt
!$OMP parallel do private(j2,k2,l)
do j2 = 1,m*m
usum(j2,i2) = 0
vsum(j2,i2) = 0
count = 0
!$OMP parallel do reduction(+:usum,vsum,count)
do k2 = 1,m*m
if ((xtemp(k2,i2)-xtemp(j2,i2))**2+(ytemp(k2,i2)-ytemp(j2,i2))**2<=d**2)
then
usum(j2,i2) = usum(j2,i2)+u(k2,i2)
vsum(j2,i2) = vsum(j2,i2)+v(k2,i2)
count = count+1
end if
end do
!$OMP end parallel do
umean(j2,i2) = usum(j2,i2)/dble(count)
vmean(j2,i2) = vsum(j2,i2)/dble(count)
u(j2,i2+1) = s0*cos(atan(vmean(j2,i2)/umean(j2,i2))+a*(r(j2)*2*pi-pi))
v(j2,i2+1) = s0*sin(atan(vmean(j2,i2)/umean(j2,i2))+a*(r(j2)*2*pi-pi))
xtemp(j2,i2+1) = xtemp(j2,i2)+u(j2,i2+1)
ytemp(j2,i2+1) = ytemp(j2,i2)+v(j2,i2+1)
! boundary conditions
if (xtemp(j2,i2+1)>l) then
xtemp(j2,i2+1) = xtemp(j2,i2+1)-2*l
else
if (xtemp(j2,i2+1)<-l) then
xtemp(j2,i2+1) = xtemp(j2,i2+1)+2*l
end if
end if
if (ytemp(j2,i2+1)>l) then
ytemp(j2,i2+1) = ytemp(j2,i2+1)-2*l
else
if (ytemp(j2,i2+1)<-l) then
ytemp(j2,i2+1) = ytemp(j2,i2+1)+2*l
end if
end if
end do
!$OMP end parallel do
psi(i2+1) = sqrt(sum(u(:,i2+1))**2+sum(v(:,i2+1))**2)/dble(m)/dble(m)/s0
end do
x(:,1:nt+1) = xtemp(:,1:nt+1)
y(:,1:nt+1) = ytemp(:,1:nt+1)
end subroutine simulate_omp
The argument l is declared with intent(in) and not modified in the loop so there is no need to declare it private. Below is a suggestion without the outer parallel loop:
subroutine simulate_omp(m,nt,s0,l,d,a,numthreads,x,y,psi)
!Pedestrian motion model
!input variables:
!n = m^2 = number of students
!nt: number of time steps
!s0: student speed
!l: initial spacing between students
!d: student motion influence by all other students within distance <= d
!a: noise amplitude
!numthreads: number of threads to use in parallel regions
!output variables:
!x,y: all m^2 student paths from i=1 to nt+1
!psi: synchronization parameter, store at all nt+1 times (including initial
condition)
use omp_lib
implicit none
integer, intent(in) :: m,nt,numthreads
real(kind=8), intent(in) :: s0,l,d,a
real(kind=8), dimension(m*m,nt+1), intent(out) :: x,y
real(kind=8), dimension(nt+1), intent(out) :: psi
real(kind=8), dimension(m*m,nt+1) :: xtemp,ytemp,u,v
real(kind=8), dimension :: usum,vsum,umean,vmean
real(kind=8) :: r(m*m)
real(kind=8),parameter :: pi = 4*atan(1.0_8)
integer :: i1,j1,k1,i2,j2,k2,count
!$call omp_set_num_threads(numthreads)
! initialize student positions
x = 0.d0
y = 0.d0
k1 = 0
do i1 = 1,m
do j1=1,m
k1 = k1 + 1
x(k1,1) = (j1-1)*l/2 - (m-1)*l/4
y(k1,1) = (i1-1)*l/2 - (m-1)*l/4
end do
end do
x(:,1) = x(:,1)/(m-1)
y(:,1) = y(:,1)/(m-1)
! initialize
xtemp(:,1) = x(:,1)
ytemp(:,1) = y(:,1)
call random_number(r)
u(:,1) = s0*cos(r*2*pi-pi)
v(:,1) = s0*sin(r*2*pi-pi)
psi(1) = sqrt(sum(u(:,1))**2+sum(v(:,1)**2))/dble(m)/dble(m)/s0
do i2 = 1,nt
do j2 = 1,m*m
usum = 0
vsum = 0
count = 0
!$OMP parallel do private(k2), reduction(+:usum,vsum,count)
do k2 = 1,m*m
if ((xtemp(k2,i2)-xtemp(j2,i2))**2+(ytemp(k2,i2)-ytemp(j2,i2))**2<=d**2) then
usum = usum+u(k2,i2)
vsum = vsum+v(k2,i2)
count = count+1
end if
end do
!$OMP end parallel do
umean = usum/dble(count)
vmean = vsum/dble(count)
u(j2,i2+1) = s0*cos(atan(vmean/umean)+a*(r(j2)*2*pi-pi))
v(j2,i2+1) = s0*sin(atan(vmean/umean)+a*(r(j2)*2*pi-pi))
xtemp(j2,i2+1) = xtemp(j2,i2)+u(j2,i2+1)
ytemp(j2,i2+1) = ytemp(j2,i2)+v(j2,i2+1)
! boundary conditions
if (xtemp(j2,i2+1)>l) then
xtemp(j2,i2+1) = xtemp(j2,i2+1)-2*l
else
if (xtemp(j2,i2+1)<-l) then
xtemp(j2,i2+1) = xtemp(j2,i2+1)+2*l
end if
end if
if (ytemp(j2,i2+1)>l) then
ytemp(j2,i2+1) = ytemp(j2,i2+1)-2*l
else
if (ytemp(j2,i2+1)<-l) then
ytemp(j2,i2+1) = ytemp(j2,i2+1)+2*l
end if
end if
end do
psi(i2+1) = sqrt(sum(u(:,i2+1))**2+sum(v(:,i2+1))**2)/dble(m)/dble(m)/s0
end do
x(:,1:nt+1) = xtemp(:,1:nt+1)
y(:,1:nt+1) = ytemp(:,1:nt+1)
end subroutine simulate_omp
You can time it and compare it with the outer loop parallelised using private(j2,k2,umean,vmean,usum,vsum,count), shared(u,v,xtemp,ytemp). Make sure to have OMP_NESTED set to true for the latter tests.

How to make a random function in fortran to generate the same random distribution into array?

I think my code below it's not exactly give me the same random distribution.
subroutine trig_random_value()
implicit none
integer :: t, z, y, x
real(real64) :: theta, r
real(real64), parameter :: PI=4.D0*DATAN(1.D0)
integer, dimension(12) :: date_time
integer, dimension(12) :: seed
call date_and_time(values=date_time)
call random_seed
seed = date_time(6) * date_time(7) + date_time(8)
call random_seed(put = seed)
do z = 1, z_size
do y = 1, y_size
do x = 1, x_size
theta = rand()*2*PI
r = 0.1*rand()
l1(1, z, y, x) = r*cos(theta)
l2(1, z, y, x) = r*sin(theta)
theta = rand()*2*PI
r = 0.1*rand()
l1(2, z, y, x) = r*cos(theta)
l2(2, z, y, x) = r*sin(theta)
end do
end do
end do
return
end subroutine trig_random_value
According to my code, I try to random value to l1(1,:,:,:), l1(2,:,:,:), l2(1,:,:,:) and l2(2,:,:,:) where l(t, x, y, z) is (3+1)-dimension array
Why do i use trigonometry function for my random function? because i want a circular randomization. If i plot distribution of l1(1,:,:,:) vs l2(1,:,:,:) or l1(2,:,:,:) vs l2(2,:,:,:) i will get circle shape distribution with radius 0.1
So, and why i tell you that this's not exactly give me a same distribution? because i was tried to measure a variance of them and i got
variance_l1_t1 = 1.6670507752921395E-003
variance_l1_t2 = 3.3313151655785292E-003
variance_l2_t1 = 4.9965623815717321E-003
variance_l2_t2 = 6.6641054728288360E-003
notice that (variance_l1_t2 - variance_l1_t1) = (variance_l2_t1 - variance_l1_t2) = (variance_l2_t2 - variance_l2_t1) = 0.00166
That's quite a weird result. In actually i should get almost the same variance value of l1(1,:,:,:), l1(2,:,:,:), l2(1,:,:,:) and l2(2,:,:,:) if this function if good random function. may be i did something wrong.
How to solve this problem?
Additional information from request:
real(real64) function find_variance(l)
implicit none
real(real64), dimension(z_size, y_size, x_size), intent(in) :: l
integer :: z, y, x
real(real64) :: l_avg = 0
real(real64) :: sum_val = 0
do z = 1, z_size
do y = 1, y_size
do x = 1, x_size
l_avg = l_avg + l(z, y, x)
end do
end do
end do
l_avg = l_avg/(z_size*y_size*x_size)
do z = 1, z_size
do y = 1, y_size
do x = 1, x_size
sum_val = sum_val + (l(z , y, x) - l_avg)**2
end do
end do
end do
find_variance = sum_val/(z_size*y_size*x_size)
return
end function find_variance
In modern Fortran, an initialization of variables such as
real(real64) :: sum_val = 0
means that sum_val is a variable with the SAVE attribute (which is similar to static in C), which is initialized only once when the program starts. It is equivalent to
real(real64), save :: sum_val = 0
The value of the SAVEed variable is kept during the entire run and it will not be initialized to 0 again. To fix this, simply replace
real(real64) :: sum_val !! this is a usual local variable
sum_val = 0 !! or sum_val = real( 0, real64 )
then I guess it should be fine. Please see this page for more details.
IMO this is one of the very confusing features of Fortran...

Resources