Array allocation speed issue - fortran

I am currently writing a Monte Carlo code whose volume is fluctuating. I divide my simulate cell, a cube, into many smaller cubes for algorithm speed reasons. However as the volume fluctuates, the number of these smaller cubes also fluctuates. Therefore, I have been reallocating my arrays that contain info on these smaller cubes as necessary:
!.....................................................................................!
! want to assign each atom to appropriate cell
! generate list of atoms in each cell for GONET algorithm
! arrays are allocated and deallocated in this routine.
!......................................................................................!
subroutine indiv_cell_lists_alloc(r)
implicit none
double precision :: r(3,param%np)
integer :: i,icell,size_cl
integer :: temp_num(0:listvar%ncellT-1) !...listvar%ncellT is total number of cells.
integer :: temp_cell(param%np)
temp_num(:) = 0
!--- param%np is total number of particles in simulation
do i = 1 ,param%np
!--- based off coordinates, finds what cell particle i is in
icell = cell(r(1,i),r(2,i),r(3,i))
temp_cell(i) = icell
temp_num(icell) = temp_num(icell) + 1
!--- keep particles current cell
atom(i)%cell = icell
atom(i)%loc = temp_num(icell)
enddo
!--- listvar%cl is an array. listvar%cl(i) contains info for cell i
!--- listvar%cl(i)%cmem(:) is an array containing the particles that are in the ith cell
!--- deallocate cell member lists
!--- subtract one since array is 0:ncellT-1, where ncellT is total number of cells
size_cl = size(listvar%cl)-1
do i = 0, size_cl
deallocate(listvar%cl(i)%cmem)
enddo
!--- deallocate cell list
deallocate(listvar%cl)
!--- allocate new celllist
allocate(listvar%cl(0:listvar%ncellT-1))
!--- allocate new arrays
do i = 0, listvar%ncellT-1
!--- allocate new array based off new size
allocate(listvar%cl(i)%cmem(temp_num(i)))
!--- number of molecules in cell
listvar%cl(i)%num = temp_num(i)
enddo
do i= 1,param%np
!--- get cell for particle
icell = temp_cell(i)
!--- place particles in cell
listvar%cl(icell)%cmem((atom(i)%loc)) = i
enddo
end subroutine indiv_cell_lists_alloc
Now the reason I am making sure that these arrays are only as big as they need to be, is that eventually these arrays will be exported to a Xeon Phi coprocessor. Due to the reduced memory there, I think that just allocating a large amount of memory and forgetting about it would lead to bad performance. However in serial execution, this subroutine is taking up ~70% of my run time.
Do you have any suggestions on how I could accomplish this reallocation more efficiently, or any suggestions about other methods I could use?

Related

Foucault Pendulum simulation

Program Foucault
IMPLICIT NONE
REAL,DIMENSION(:),ALLOCATABLE :: t, x,y
REAL,PARAMETER :: pi=3.14159265358979323846, g=9.81
REAL :: L, vitessea, lat, h, omega, beta
INTEGER :: i , zeta
zeta=1000
Allocate(x(zeta),y(zeta),t(zeta))
L=67.
lat=49/180*pi
omega=sqrt(g/L)
h=0.01
Do i= 1,zeta
IF(i==1 .OR. i==2) THEN
t(1)=0.0
t(2)=0.0
x(1)=0.1
x(2)=1
y(1)=0.0
y(2)=0.0
ELSE
t(i+1)=real(i)*h
x(i+1)=(-omega**2*x(i)+2.0*((y(i)-y(i-1))/h)*latang(lat))*h**2+2.0*x(i)-x(i-1)
y(i+1)=(-omega**2*y(i)-2.0*((x(i)-x(i-1))/h)*latang(lat))*h**2+2.0*y(i)-y(i-1)
END IF
WRITE(40,*) t(i), x(i)
WRITE(60,*) t(i), y(i)
WRITE(50,*) x(i), y(i)
END DO
Contains
REAL Function latang(alpha)
REAL, INTENT(IN) :: alpha
REAL :: sol
latang=2*pi*sin(alpha)/86400
END FUNCTION
End Program Foucault
I'm trying to code the original Foucault Pendulum in Paris. My code seems to be working but so far, I could only get the below right graphic, "the flower" evolution. Therefore, I changed my parameters constantly to get the left graphic but I couldn't.
I took parameters of Foucault Pendulum installed in Paris with L=67, angular velocity of earth =2*pi/86400 and latitude of 49/180*pi.
My initial conditions are as written in the code. I tried a way range of parameters varying all of my initial conditions, my latitude and angular velocity but i couldn't get the left desired results.
I used Foucault differential equations as below : i coded them with Finite difference method (more simple than Runge-Kutta) by replacing the 2nd order derivation by its central finite difference. And the first order one by it's backward finite difference. By then, i build my loop by isolating x(i+1) and y(i+1) in both equations.
My code is very sensitive to parameters such as h (=derivation step), earth angular velocity and latitude (which is normal). I tried to change a way big range of parameters from a big h step to a small one, to a minimal and high latitude, initial conditions...etc but i couldn't ever get the left graphic which i rather need.
What could be made to get the left one ?
I was able to get the two charts, by speeding up the earth's rotation 120× fold, and allowing the simulation to run for 32 swings of the pendulum. Also, I noticed that Euler integration added energy to the system making for bad results, so I reverted to a standard RK4 implementation.
and here is the code I used to solve this ODE:
program FoucaultOde
implicit none
integer, parameter :: sp = kind(1.0), dp = kind(1d0)
! Constants
real, parameter :: g=9.80665, pi =3.1415926536
! Variables
real, allocatable :: y(:,:), yp(:), k0(:),k1(:),k2(:),k3(:)
real :: lat, omega, h, L, earth, period
real :: t0,x0,y0,vx0,vy0
integer :: i, zeta, f1, swings
! Code starts here
swings = 32
zeta = 400*swings
L = 67
lat = 49*pi/180
period = 24*60*60 ! period = 86400
earth = (2*pi*sin(lat)/period)*120 !120 multiplier for roation
omega = sqrt(g/L)
allocate(y(5,zeta))
allocate(yp(5), k0(5),k1(5),k2(5),k3(5))
! make pendulum complete 'swings' cycles in 'zeta' steps
h = swings*2*pi/(omega*zeta)
t0 = 0
x0 = 0.5 ! Initial displacement
y0 = 0
vx0 = 0
vy0 = 0
! Initial conditions in the state vector Y
Y(:,1) = [t0,x0,y0,vx0,vy0]
do i=2, zeta
! Euler method (single step)
! Yp = ode(Y(:,i-1))
! Runge-Kutta method (four steps)
k0 = ode(Y(:,i-1))
k1 = ode(Y(:,i-1) + h/2*k0)
k2 = ode(Y(:,i-1) + h/2*k1)
k3 = ode(Y(:,i-1) + h*k2)
Yp = (k0+2*k1+2*k2+k3)/6
! Take a step
Y(:,i) = Y(:,i-1) + h*Yp
end do
open( newunit=f1, file='results.csv', status = 'replace', pad='no')
! write header
write (f1, '(a15,a,a15,a,a15,a,a15,a,a15)') 't',',', 'x',',','y',',', 'vx',',','vy'
! write rows of data, comma-separated
do i=1, zeta
write (f1, '(g,a,g,a,g,a,g,a,g)') y(1,i),',',y(2,i),',',y(3,i),',',y(4,i),',',y(5,i)
end do
close(f1)
contains
function ode(Y) result(Yp)
real, intent(in) :: Y(5)
real :: Yp(5), t,px,py,vx,vy,ax,ay
! Read state vector Y to component values
t = Y(1)
px = Y(2)
py = Y(3)
vx = Y(4)
vy = Y(5)
! Reference paper:
! http://www.legi.grenoble-inp.fr/people/Achim.Wirth/final_version.pdf
ax = -(omega**2)*px + 2*vy*earth ! (equation 53)
ay = -(omega**2)*py - 2*vx*earth ! (equation 54)
! State vector rate. Note, rate of time is aways 1.0
Yp = [1.0, vx, vy, ax, ay]
end function
end program FoucaultOde
The resulting file results.csv looks like this for me (for checking)
t, x, y, vx, vy
.000000 , 5.000000 , .000000 , .000000 , .000000
.4105792E-01, 4.999383 , .1112020E-06, -.3004657E-01, .8124921E-05
.8211584E-01, 4.997533 , .8895339E-06, -.6008571E-01, .3249567E-04
.1231738 , 4.994450 , .3001796E-05, -.9011002E-01, .7310022E-04
.1642317 , 4.990134 , .7114130E-05, -.1201121 , .1299185E-03
.2052896 , 4.984587 , .1389169E-04, -.1500844 , .2029225E-03
.2463475 , 4.977810 , .2399832E-04, -.1800197 , .2920761E-03
.2874054 , 4.969805 , .3809619E-04, -.2099106 , .3973353E-03
...
from which I plotted the 2nd and 3rd columns in one chart, and the 4th and 5th for the second chart.
There is one thing that may be wrong depending on how you manage different step sizes, and an observation on the physics of the real-world example. With the initialization of the arrays, you imply an initial velocity of about 0.9/0.01=90 [m/s] in x direction away from the center. To get compatible results for different step sizes, you would need to adapt the calculation of x(2). However, in the graphs the plot starts from a point with zero velocity. This you can implement to first order by setting x(2)=x(1)=1. As the used integration method is also first order, this is sufficient.
For the second point, note that one can write the system using complex coordinates z=x+iy as
z'' = -w^2*z - 2*i*E*z', E = Omega*sin(theta)
This is a linear ODE with constant coefficients, the solution of it is
z(t) = exp(-i*E*t) * (A*cos(w1*t)+B*sin(w1*t)), w1 = sqrt(w^2+E^2)
This describes a pendulum motion of frequency w1 whose plane rotates with frequency E clockwise. The grand rotation has period T=2*pi/E, during which w1*T/(2*pi)=w1/E pendulum swings occur.
Now insert your numbers, w=sqrt(g/L)=0.383 and E=2*pi*sin(49°)/86400=5.49e-05, so that essentially w1=w. The number of pendulum cycles per full rotation is w/E=6972, so that you can expect a densely filled circle in the plot. Or a very narrow double wedge if only a few cycles are plotted. As each cycle takes 2*pi/w=16.4 [s], and the integration goes 1000 steps of step size 0.01, in the plot as it is you can expect a swing forth and part of the swing back.
To be more realistic, set the initial velocity to zero, that is, the pendulum is taken to its start position and then let go. Also increase the time to 30 [s] to have more than one pendulum cycle in the plot.
It from this we can see that the solutions converge, and with some imagination, that they converge linearly.
To get a plot like in the cited images, one needs a much smaller fraction of w/E, counting the swings, it has to be around 15. Note that you can not get this ratio anywhere on earth with a realistically scaled pendulum. So set w=pi, E=pi/16 and integrate over 15 time units using the first order method.
This detoriorates really fast, even for the smallest step size with 40 points in a pendulum cycle.
For a better result, increase the local truncation order to the next higher by using the central difference in the first derivative approximation.
z(i+1) - 2*z(i) + z(i-1) = -w^2*z(i)*dt^2 - i*E*(z(i+1)-z(i-1))*dt
z(i+1) = ( 2*z(i) - z(i-1) - w^2*z(i)*dt^2 + i*E*z(i-1)*dt ) / (1+i*E*dt)
The division by the complex number can also be easily carried out in the real components of the trajectory,
! x(i+1)-2*x(i)+x(i-1) = h^2*(-omega**2*x(i)) + h*earth*(y(i+1)-y(i-1))
! y(i+1)-2*y(i)+y(i-1) = h^2*(-omega**2*y(i)) - h*earth*(x(i+1)-x(i-1))
t(i) = t(i-1) + h
cx = (2-(h*omega)**2)*x(i) - x(i-1) - h*earth*y(i-1)
cy = (2-(h*omega)**2)*y(i) - y(i-1) + h*earth*x(i-1)
den = 1+(h*earth)**2
x(i+1) = (cx + h*earth*cy)/den
y(i+1) = (cy - h*earth*cx)/den
Now to respect the increased order, also the initial points need to have an order of accuracy more, using again zero initial speed, this gives in the second order Taylor expansion
z(2) = z(1) - 0.5*w^2*z(1)*dt^2
All the step sizes that gave deviating and structurally deteriorating results in the first order method now give a visually identical, structurally stable results in this second order method.

Non Tabular data structure in Fortran

I would like to build a data structure for non tabular data. I am not sure what is the right way to do that in (modern) Fortran.
I have a data set of houses that includes their location (lat,lon) and price. I have another data of factories that include their location (lat,lon) and the amount of pollution they produce. For each house I need to create a list of factories which are within 5km radius of the house. Not just the number of these factories but the whole (lat,lon,pollution) vectors of these factories. Each house has a different number of factories close to it ranging from zero to about eighty.
MODULE someDefinitions
IMPLICIT NONE
INTEGER, PARAMETER :: N_houses=82390, N_factories=4215
TYPE house
REAL :: lat,lon,price
! a few more fields which are not important here
END TYPE
TYPE factory
REAL :: lat,lon,pollution
! a few more fields which are not important here
END TYPE
Contains
PURE FUNCTION haversine(deglat1,deglon1,deglat2,deglon2) RESULT (dist)
! Some code for computing haversine distance in meters
END FUNCTION haversine
END MODULE someDefinitions
PROGRAM createStructure
USE someDefinitions
IMPLICIT NONE
TYPE(factory), DIMENSION(N_factories) :: factories
TYPE(house), DIMENSION(N_houses) :: houses
INTEGER :: i,j
! more variables definitions as needed
! code to read houses data from the disk
! code to read factories data from the disk
DO i=1,N_houses
DO j=1,N_factories
!here I compute the distance between houses(i) and factories(j)
! If this distance<=5000 I want to add the index j to the list of indices
! associated with house i. How? What is the right data structure to do
! that? some houses have zero factories within 5000 meters from them.
! Some houses have about 80 factories around them. It's unbalanced.
END DO !j
END DO !i
END PROGRAM createStructure
The created structure will then be used in further calculations. A matrix of N_houses x N_factories is way too large to save in memory.
Note: I know Fortran 2008 if that is helpful in any way.
Using too many nested derived types can become tedious. Here is an example using 2D arrays for all data except the required list. This is similar to the K-Nearest Neighbors (KNN) algorithm naively implemented. There may be better algorithms, of course, but the following can be a good start.
program NoStrucyures
implicit none
type listi
real, allocatable :: item(:,:)
end type
integer, parameter :: N_houses=82390, N_factories=4215
real :: houses(N_houses,3)
real :: factories(N_factories,3)
real :: distance(N_factories)
type(listi) :: list(N_houses)
integer :: i, j, k, within5k
! Generating dummy data
call random_number(houses)
call random_number(factories)
houses = houses * 500000
factories = factories * 500000
do i = 1, N_houses
distance = sqrt((houses(i,1)-factories(:,1))**2 + (houses(i,2)-factories(:,2))**2)
within5k = count( distance <= 5000 )
if (within5k > 0) then
allocate(list(i)%item(within5k,3))
k = 0
do j = 1, N_factories
if (distance(j) <= 5000) then
k = k + 1
list(i)%item(k,:) = factories(j,:)
end if
end do
else
list(i)%item = reshape([-1, -1, -1],[1,3])
end if
end do
do i=1,10
print *, list(i)%item
end do
end program NoStrucyures

Efficient way to calculate distance function

I have a 3D matrix (dimension nx,nz,ny) which corresponds to a physical domain. This matrix contains a continuous field from -1 (phase 1) to +1 (phase 2); the interface between the two phases is the level 0 of this field.
Now, I want to calculate efficiently the signed distance function from the interface for every point in the domain.
I tried two possibilities (sgn is the sign of my field, with values +1,0,-1, xyz contains the grid as triplets of x,y,z at each point and dist is the signed distance function I want to calculate).
double precision, dimension(nx,nz,ny) :: dist,sgn,eudist
integer :: i,j,k
double precision :: seed,posit,tmp(nx)
do j=1,ny
do k=1,nz
do i=1,nx
seed=sgn(i,k,j)
! look for interface
eudist=(xyz(:,:,:,1)-x(i))**2+(xyz(:,:,:,2)-z(k))**2+(xyz(:,:,:,3)-y(j))**2
! find min within mask
posit=minval(eudist,seed*sgn.le.0)
! tmp fits in cache, small speed-up
tmp(i)=-seed*dsqrt(posit)
enddo
dist(:,k,j)=tmp
enddo
enddo
I also tried a second version, which is quite similar to the above one but it calculates the Euclidean distance only in a subset of the whole matrix. With this second version there is some speed up, but it is still too slow. I would like to know whether there is a more efficient way to calculate the distance function.
Second version:
double precision, dimension(nx,nz,ny) :: dist,sgn
double precision, allocatable, dimension(:,:,:) :: eudist
integer :: i,j,k , ii,jj,kk
integer :: il,iu,jl,ju,kl,ku
double precision :: seed, deltax,deltay,deltaz,tmp(nx)
deltax=max(int(nx/4),1)
deltay=max(int(ny/4),1)
deltaz=max(int(nz/2),1)
allocate(eudist(2*deltax+1,2*deltaz+1,2*deltay+1))
do j=1,ny
do k=1,nz
do i=1,nx
! look for closest point in box 2*deltax+1,2*deltaz+1,2*deltay+1
il=max(1,i-deltax)
iu=min(nx,i+deltax)
jl=max(1,j-deltay)
ju=min(ny,j+deltay)
kl=max(1,k-deltaz)
ku=min(nz,k+deltaz)
eudist(:,1:ku-kl+1,:)=(xyz(il:iu,kl:ku,jl:ju,1)-x(i))**2 &
& +(xyz(il:iu,kl:ku,jl:ju,2)-z(k))**2 &
& +(xyz(il:iu,kl:ku,jl:ju,3)-y(j))**2
seed=sgn(i,k,j)
tmp(i)=minval(eudist(:,1:ku-kl+1,:),seed*sgn(il:iu,kl:ku,jl:ju).le.0)
tmp(i)=-seed*dsqrt(tmp(i))
enddo
dist(:,k,j)=tmp
enddo
enddo
eudist: Euclidean distance between the point i,k,j and any other point in a box 2*deltax+1,2*deltaz+1,2*deltay+1 centered in i,k,j. This reduces computational cost, as the distance is calculated only in a subset of the whole grid (here I am assuming that the subset is large enough to contain an interfacial point).
After Vladimir suggestion (x,y,z are the axes determining grid position, xyz(i,k,j)=(x(i),z(k),y(j)) ):
double precision, dimension(nx,nz,ny) :: dist,sgn
double precision :: x(nx), y(ny), z(nz)
double precision, allocatable, dimension(:,:,:) :: eudist
double precision, allocatable, dimension(:) :: xd,yd,zd
integer :: i,j,k , ii,jj,kk
integer :: il,iu,jl,ju,kl,ku
double precision :: seed, deltax,deltay,deltaz,tmp(nx)
deltax=max(int(nx/4),1)
deltay=max(int(ny/4),1)
deltaz=max(int(nz/2),1)
allocate(eudist(2*deltax+1,2*deltaz+1,2*deltay+1))
allocate(xd(2*deltax+1))
allocate(yd(2*deltay+1))
allocate(zd(2*deltaz+1))
do j=1,ny
do k=1,nz
do i=1,nx
! look for closest point in box 2*deltax+1,2*deltaz+1,2*deltay+1
il=max(1,i-deltax)
iu=min(nx,i+deltax)
jl=max(1,j-deltay)
ju=min(ny,j+deltay)
kl=max(1,k-deltaz)
ku=min(nz,k+deltaz)
do ii=1,iu-il+1
xd(ii)=(xyz(il+ii-1)-x(i))**2
end do
do jj=1,ju-jl+1
yd(jj)=(y(jj+jl-1)-y(j))**2
end do
do kk=1,ku-kl+1
zd(kk)=(z(kk+kl-1)-z(k))**2
end do
do jj=1,ju-jl+1
do kk=1,ku-kl+1
do ii=1,iu-il+1
eudist(ii,kk,jj)=xd(ii)+yd(jj)+zd(kk)
enddo
enddo
enddo
seed=sgn(i,k,j)
tmp(i)=minval(eudist(:,1:ku-kl+1,:),seed*sgn(il:iu,kl:ku,jl:ju).le.0)
tmp(i)=-seed*dsqrt(tmp(i))
enddo
dist(:,k,j)=tmp
enddo
enddo
EDIT: more information on the problem at hand.
The grid is an orthogonal grid mapped to a matrix. The number of points of this grid is of the order of 1000 in each direction (in total about 1 billion points).
My goal is switching from a sign function (+1,0,-1) to a signed distance function in the entire grid in an efficient way.
I would still do what I suggested, no matter if you do that on a subset or across the whole plane. Take advantage of the orthogonal grid, it is a great thing to have
do j=1,ny
do k=1,nz
do i=1,nx
! look for closest point in box 2*deltax+1,2*deltaz+1,2*deltay+1
il=max(1,i-deltax)
iu=min(nx,i+deltax)
jl=max(1,j-deltay)
ju=min(ny,j+deltay)
kl=max(1,k-deltaz)
ku=min(nz,k+deltaz)
do ii = il,iu
xd(i) = (xyz(ii,kl:ku,jl:ju,1)-x(i))**2
end do
do jj = jl,ju
yd(i) = (xyz(il:iu,kl:ku,jj,2)-y(j))**2
end do
do kk = kl,ku
zd(k) = (xyz(il:iu,kk,jl:ju,3)-z(k))**2
end do
do jj = jl,ju
do kk = kl,ku
do ii = il,iu
eudist(il:iu,kl:ku,jl:ju) = xd(ii) + yd(jj) + zd(kk)
end do
end do
end do
....
enddo
dist(:,k,j)=tmp
enddo
enddo
Consider separating the whole thing that is inside the outer triple loop into a subroutine or a function. It would not be faster, but it would be much more readable. Especially for us here, It would be enough for us here to only deal with that function, the outer loop is just a confusing extra layer.

Variable 'n' cannot appear in the expressions below

I want to do an iteration of the strain and stress change in rock mechanics, but am stuck on the errors:
"real::STRAIN (1:N), SIGMA (1:N),DSIGMA (1:N),STRAIN (1:N)=0.0"
and
"real,Dimension(6)::CEL(1:N,1:N)!stiffness matrix"
!program elastic_plastic
implicit none
!define all parameter
integer :: i = 1.0,j,K,M,N,inc
real::STRAIN (1:N), SIGMA (1:N),DSIGMA (1:N),DSTRAIN (1:N)=0.0
real,Dimension(6)::CEL(1:N,1:N)!stiffness matrix
real:: YOUNG, NU, COHESION !rock properties
real::ALPHA, KAPPA! cohesion and frictional angle
real::F !function
real::FRICTION_DEG, FRICTION_RAD !friction angle
real::VARJ2 ,VARI1 !stress invariants (I1 and J2)(MPa)
real:: LAMBDA,GMODU !lames constant and shear modulus
real::SIGMA_1,SIGMA_2,SIGMA_3 !principle stresses(MPa)
real::SHEAR_4,SHEAR_5,SHEAR_6 !shear stresses
real,parameter::DEG_2_RAD = 0.01745329
!INPUT
NU = 0.25
COHESION = 15 ! in MPa
YOUNG = 20 ! in GPa
FRICTION_DEG = 30.0d0
FRICTION_RAD = FRICTION_DEG *(DEG_2_RAD)
!perform calculations
KAPPA=6.*COHESION *cos(FRICTION_DEG*DEG_2_RAD)/(sqrt(3.)*(3.-sin(FRICTION_DEG*DEG_2_RAD)))
ALPHA=2.*sin(FRICTION_DEG*DEG_2_RAD)/(sqrt(3.)*(3.-sin(FRICTION_DEG*DEG_2_RAD)))
GMODU=YOUNG/2.*(1.+NU)
LAMBDA=NU*YOUNG/((1.+NU)*(1.-(2.*NU)))
!Set up elastic stiffness matrix (CEL)
CEL(1:N,1:N)=0.0
CEL (1,1)= LAMBDA-(2.*GMODU)
CEL (2,2)= LAMBDA-(2.*GMODU)
CEL (3,3)= LAMBDA-(2.*GMODU)
CEL (4,4)= 2.*GMODU
CEL (5,5)= 2.*GMODU
CEL (6,6)= 2.*GMODU
DO
inc = inc + 1
DSTRAIN(1)=0.00002
DSIGMA = matmul (CEL(1:N,1:N), DSTRAIN)
SIGMA =SIGMA +DSIGMA
STRAIN=STRAIN+DSTRAIN
!calculate I1 AND J2
VARI1=SIGMA_1+SIGMA_2+SIGMA_3
VARJ2=1./6.*((SIGMA_1-SIGMA_2)**2+(SIGMA_2-SIGMA_3)**2+(SIGMA_3- SIGMA_1)**2+SHEAR_4**2+SHEAR_5**2+SHEAR_6**2)
!Yield function (Drucker-prager)
F= ALPHA*VARI1+(sqrt(VARJ2)-KAPPA)
IF (F.LE.0.0d0)then !Elastic step (exit)
SIGMA =SIGMA
STRAIN=STRAIN
exit
endif
if (F.GT.0.0d0)then !Plastic step (continue)
goto 20
end if
20 continue
write(11,*)STRAIN,SIGMA,inc
END DO
end
You can't statically define an array with a variable. You must use a constant.
For example the following will work:
real::STRAIN (1:5), SIGMA (1:5),DSIGMA (1:5),DSTRAIN (1:5)=0.0
real,Dimension(6)::CEL(1:5,1:5)!stiffness matrix
If you don't know the size of the arrays at code time you can use the 'allocate' statement. This is known as 'dynamic storage allocation'. From 'Arrays and Parallel programming in Fortran 90/95':
The way to declare an allocatable array is as follows:
integer Nparticles ! number of particles
integer, parameter :: dim=3 ! dimensionality of space
...
real, allocatable :: charge(:) ! defines an array containing the charge of
! each particle
integer, allocatable :: xyz(:,:) ! coordinates of each particle
Once the actual number of particles in the simulation has been read, we can allocate these arrays:
read(*,*) Nparticles
allocate (charge(Nparticles),xyz(dim,Nparticles))

Error in Fortran: attempt to call a routine with argument number four as a real (kind=1) when a procedure was required

I have never done programming in my life and this is my very first code for a uni assignment, I get no errors in the compiling stage but myh program does not run saying that I have the error in the title, guess the problem is when I call the subroutine. Can anyone help me? It is my first code and it is really frustrating. Thank you.
!NUMERICAL COMPUTATION OF INCOMPRESSIBLE COUETTE FLOW USING FINITE DIFFERENCE METHOD
!IMPLICIT APPROACH
!MODEL EQUATION
!PARTIAL(U)/PARTIAL(T)=1/RE*(PARTIAL(U) SQUARE/PARTIAL(Y) SQUARE)
!DEFINE VARIABLES
IMPLICIT NONE
!VELOCITY U AT TIME T, VELOCITY UNEW AT TIME T+1, TIME T
!MAXIMUM 1000 POINTS
REAL V(1000)
REAL VNEW(1000)
REAL T
!GRID SPACING DY, GRID POINTS N+1
REAL DY
INTEGER N
!TIME STEP
REAL DT
!FLOW REYNOLDS NUMBER IN THE MODEL EQUATION
REAL ALPHA
!TOTAL SIMULATION TIME - LOOP NUMBER
INTEGER REP, I, J
!COEFFICIENTS IN LINEAR EQUATION MATRIX, SOURCE TERM K, DIAGONAL B, NON-DIAGONAL A
REAL S(1000), B, A
!INITIALIZATION OF DATA
DATA ALPHA/5000.0/
DATA N/100/
DATA REP/3000/
!CALCULATION OF GRID SPACING
DY=1.0/N
!CALCULATION OF TIME STEP DELTA T, CAN BE LARGER THAN THAT IN AN EXPLICIT METHOD
DT=0.5*RE*DY*DY
DT=ALPHA*DY*DY
!INITIAL CONDITIONS OF VELOCITY PROFILE
!BOTTOM AND INNER POINTS
DO I=1,N
V(I)=0.0
ENDDO
!POINT AT MOVING PLATE
V(N+1)=1.0
!BOUNDARY CONDITIONS AT LOWER AND UPPER POINTS ON PLATE
V(1)=0.0
V(N+1)=1.0
!CALCULATION OF DIAGONAL B AND NON-DIAGONAL A IN LINEAR EQUATION MATRIX
B=1.0+DT/DY/DY/ALPHA
A=-(DT)/2.0/DY/DY/ALPHA
!INITIAL COMPUTATION TIME
T=0.0
!ENTER MAIN LOOP TO MARCH IN TIME DIRECTION
DO I=1,REP
!SIMULATION TIME INCREASE BY DELTA T EACH STEP
T=T+DT
!USE IMPLICIT METHOD TO UPDATE GRID POINT VALUES FOR ALL INTERNAL GRIDS ONLY
!TWO BOUNDARY GRID POINTS VALUES ARE CONSTANT WITHIN THE WHOLE SIMULATION
!CALCULATION OF SOURCE TERM IN LINEAR EQUATION
DO J=2,N
S(J)=(1.0-DT/DY/DY/ALPHA)*V(J)+DT/2.0/DY/DY/ALPHA*V(J+1)+V(J-1)
ENDDO
!INCLUDE BOUNDARY CONDITIONS FOR TWO POINTS NEAR BOUDNARY
S(2)=S(2)-A*V(1)
S(N)=S(N)-A*V(N+1)
!USE SOURCE TERM K, DIAGONAL B, NON-DIAGONAL A, ORDER OF MATRIX N, TO SOLVE LINEAR EQUATION TO GET UPDATED VELOCITY
!CHECK ON INTERNET HOW TO SOLVE THIS BECUASE THIS COMPILER
!DOES NOT SOLVE IT, SOLVE LINEAR EQUATIONS BY A LINEAR SOLVER, FIND AND DOWNLOAD THE MATH LIBRARY FOR THIS COMPILER
CALL SR1(A,B,N,S,VNEW)
!REPLACE OLD VELOCITY VALUES WITH NEW VALUES.
!SINCE UNEW IS FROM UNEW(1), UNEW(2)......., UNEW(N-1), WE SHOULD RE-ARRANGE NUMBERS AS FOLLOWS
DO J=1,N-1
V(J+1)=VNEW(J)
ENDDO
!RETURN TO MAIN LOOP HERE
ENDDO
PRINT*,'HERE'
!OUTPUT VELOCITY PROFILES AT THE END OF COMPUTATION
!CREATE OUPUT FILE NAME
OPEN(15,FILE='PLEASEWORK')
!WRITE GRID POINTS AND VELOCITY VALUES
DO I=1,N+1
WRITE(15,10) V(I),(I-1)*DY
10 FORMAT(2F12.3)
ENDDO
CLOSE(15)
!DISPLAY INFORMATION ON SCREEN
!WRITE(*,*) 'THE OUTPUT VELOCITY IS AFTER', ITER, ' TIME STEPS'
!TERMINATION OF COMPUTER PROGRAM
STOP
END
!!!!!!!!
!!!!!!!!!!!!
!!!!!!!!!
SUBROUTINE SR1(A,B,N,S,VNEW)
REAL DIAGM(N), DIAGU(N), DIAGL(N)
REAL SS(N)
DO J=1,N-1
SS(J)=S(J+1)
ENDDO
DO I=1,N
DIAGM(i)=B
!Sets main diagonal as B for every value of i
IF (I==0) then
DIAGU(I)=A
DIAGL(I)=0
! No lower diagonal coefficient when i = 0
ELSE IF (I==N) THEN
DIAGU(I)=0
! No upper diagonal coefficient when i = Num
DIAGL(I)=A
ELSE
DIAGU(I)=A
! For all other points there is an upper diagonal coefficient
DIAGL(I)=A
! For all other points there is a lower diagonal coefficient
ENDIF
ENDDO
!CALL STANDARD FORTRAN MATH LIBRARY TO SOLVE LINEAR EQUATION AND GET SOLUTION VECTOR X(N-1)
CALL SR2 (DIAGL,DIAGM,DIAGU,SS,VNEW,N-2)
!RETURN TO MAIN PROGRAM AND X(N-1) IS FEEDED INTO UNEW(N-1)
RETURN
END SUBROUTINE
!!!!!!!!!!!!!!!
!!!!!!!!!!!
!!!!!!!!!!!
SUBROUTINE SR2 (A,B,C,D,Z,N)
!a - sub-diagonal (means it is the diagonal below the main diagonal)
!b - the main diagonal
!c - sup-diagonal (means it is the diagonal above the main diagonal)
!K - right part
!UNEW - the answer
!E - number of equations
INTEGER N
REAL A(N), B(N), C(N), D(N)
REAL CP(N), DP(N), Z(N)
REAL M
INTEGER I
DATA M/1/
!initialize c-prime and d-prime
CP(1) = C(1)/B(1)
DP(1) = D(1)/B(1)
!solve for vectors c-prime and d-prime
DO I=2,N
M=b(i)-CP(I-1)*(A(I))
CP(I)=C(I)/M
DP(I)=(D(I)-DP(I-1)*A(I))/M
ENDDO
!initialize UNEW
Z(N)=DP(N)
!solve for x from the vectors c-prime and d-prime
DO I=N-1, 1, -1
Z(I)=DP(I)-CP(I)*Z(I+1)
ENDDO
END SUBROUTINE
As george says in a comment, the problem is with the subroutine SR1. So that this isn't just a CW-stealing-a-comment answer I'll also expand a bit.
The way things are structured SR1 is a different scope from the main program. The IMPLICIT NONE in the main program doesn't apply to the subroutine, so A, B, N, S and VNEW are all implicitly typed. Apart from N,which is an integer, they are (scalar) reals.
The reference to S(J+1), as george says, means that S is not only a scalar real, but also a function. Remember that SR1 is a different scope and no information is passed from the caller to the callee about types, shapes, etc.. Further, that the dummy argument in SR1 called A happens to be same name as the actual argument in the call doesn't mean that the callee "knows" things. Your call to SR2 with the VNEW is also a problem for the same reason.
The question is tagged as "fortran77" so there isn't too much you can do to ensure there is a lot of checking going on, but there may well be compiler options and as you can use IMPLICIT NONE (not Fortran 77) that would detect your problems.
But, the question is also tagged "fortran" and "fortran95" so I'll point out that there are far better ways to detect the issues, using more modern features. Look at interfaces, modules and internal procedures.