Unexpected behavior using DATA and SAVE statement in FORTRAN77 - fortran

Is I mentioned in this post:
"Is there locally saved data (like Fortran's) in Julia?"
I am attempting to re-write an entire fortran77 code into Julia. This code is more than 2000 lines, so I won't be able to put it in here, but I will summarize the core of my problem.
In this old program, there are extensive use of functions from NUERICAL RECIPIES. In particular, ran1.f and ran3.f (both are random generator functions). The declaration lines in both functions are as follow:
FUNCTION ran1(idum)
INTEGER idum,IA,IM,IQ,IR,NTAB,NDIV
REAL ran1,AM,EPS,RNMX
PARAMETER (IA=16807,IM=2147483647,AM=1./IM,IQ=127773,IR=2836,
*NTAB=32,NDIV=1+(IM-1)/NTAB,EPS=1.2e-7,RNMX=1.-EPS)
INTEGER j,k,iv(NTAB),iy
SAVE iv,iy
DATA iv /NTAB*0/, iy /0/
and
FUNCTION ran3(idum)
INTEGER idum
INTEGER MBIG,MSEED,MZ
C REAL MBIG,MSEED,MZ
REAL ran3,FAC
PARAMETER (MBIG=1000000000,MSEED=161803398,MZ=0,FAC=1./MBIG)
C PARAMETER (MBIG=4000000.,MSEED=1618033.,MZ=0.,FAC=1./MBIG)
INTEGER i,iff,ii,inext,inextp,k
INTEGER mj,mk,ma(55)
C REAL mj,mk,ma(55) Knuth.
SAVE iff,inext,inextp,ma
DATA iff /0/
In this mentioned post(above), I learned about the mutable structures in Julia that "apparently" let me simulate the expected behavior of the variables inside the SAVE and DATA statements. Below, my mutable structures
mutable struct ran1_init # or simply "type" in older versions
NTAB :: Int64
idum :: Int64
iv :: Array{Int64,1}
iy :: Int64
# constructors if necessary
end
# Initialize mutable structure for ran1
ran1_in = ran1_init(32,ISEED,zeros(Int64,32),0)
mutable struct ran3_init # or simply "type" in older versions
idum :: Int64
iff :: Int64
inext :: Int64
inextp :: Int64
ma :: Array{Int64,1}
# constructors if necessary
end
# Initialize mutable structure for ran3
ran3_in = ran3_init(ISEED,0,0,0,zeros(Int64,55))
The fortran CODE uses ran1 until some point, where started to implement ran3 (I guess the program was assembled throughout the years, and in some point ran3 was implemented to generate random numbers, instead ran1). The first time ran3 is used in the program is in this operation:
do while (iii.le.N_emean)
t0=XA(1,1)+dble(ran3(ISEED))*(XB(1,1)-XA(1,1))
d=XA(1,2)+dble(ran3(ISEED))*(XB(1,2)-XA(1,2))
z0=XA(1,3)+dble(ran3(ISEED))*(XB(1,3)-XA(1,3))
v=XA(1,4)+dble(ran3(ISEED))*(XB(1,4)-XA(1,4))
iflag=0
stop
(Stopped for the purpose of showing this matter)
In Julia:
while (iii <= N_emean)
for i = 1:4
randomnumber = ran3(ran3_in)
XX[i] = XA[1,i] + randomnumber*(XB[1,i]-XA[1,i]);
end
t0, d, z0, v = XX;
stop
At first, I thought it was working fine. But then I saw the following outputs in both Julia and FORTRAN CODE... the experiment bellow:
In Julia
println("initial parameters")
println()
println("ISEED = ",ran3_in.idum)
println("iff = ",ran3_in.iff)
println("inext = ",ran3_in.inext)
println("inextp = ",ran3_in.inextp)
while (iii <= N_emean)
for i = 1:4
println()
println("OUTSIDE: just before entering ran3")
println("ISEED = ",ran3_in.idum)
println("iff = ",ran3_in.iff)
println("inext = ",ran3_in.inext)
println("inextp = ",ran3_in.inextp)
println()
randomnumber = ran3(ran3_in)
XX[i] = XA[1,i] + randomnumber*(XB[1,i]-XA[1,i]);
println("OUTSIDE: just exiting ran3")
println("ISEED = ",ran3_in.idum)
println("iff = ",ran3_in.iff)
println("inext = ",ran3_in.inext)
println("inextp = ",ran3_in.inextp)
println("random number : ", randomnumber)
error("JUST PLAYING AROUND")
In Fortran:
write(*,*)"initial parameters"
write(*,*)
write(*,*)"ISEED = ",ISEED
write(*,*)"iff = ",iff
write(*,*)"inext = ",inext
write(*,*)"inextp = ",inextp
do while (iii.le.N_emean)
write(*,*)
write(*,*)"OUTSIDE: just before entering ran3"
write(*,*)"ISEED = ",ISEED
write(*,*)"iff = ",iff
write(*,*)"inext = ",inext
write(*,*)"inextp = ",inextp
write(*,*)
a1ran = dble(ran3(ISEED))
write(*,*)"OUTSIDE: just exiting ran3"
write(*,*)"ISEED = ",ISEED
write(*,*)"iff = ",iff
write(*,*)"inext = ",inext
write(*,*)"inextp = ",inextp
write(*,*)"random number : ",a1ran
stop
Getting:
In Julia code
initial parameters
ISEED = 557527639
iff = 0
inext = 0
inextp = 0
OUTSIDE: just before entering ran3
ISEED = 557527639
iff = 0
inext = 0
inextp = 0
INSIDE ran3: just entering ran3
ISEED = 557527639
iff = 0
inext = 0
inextp = 0
INSIDE ran3: just exiting ran3
ISEED = 1
iff = 1
inext = 1
inextp = 32
OUTSIDE: just exiting ran3
ISEED = 1
iff = 1
inext = 1
inextp = 32
random number : 0.253305072
In fortran code
initial parameters
ISEED = 557527639
iff = 0
inext = 0
inextp = 0
OUTSIDE: just before entering ran3
ISEED = 557527639
iff = 0
inext = 0
inextp = 0
INSIDE ran3: just entering ran3
ISEED = 557527639
iff = 0
inext = 0
inextp = 0
INSIDE ran3: just exiting ran3
ISEED = 1
iff = 1
inext = 1
inextp = 32
OUTSIDE: just exiting ran3
ISEED = 1
iff = 0
inext = 0
inextp = 0
random number : 0.25330507755279541
Seems like the SAVEed and DATAed BLOCKED variables are not exiting with the correct values in Fortran, while they do in Julia,
1) What is going on?.
2) Is there a problem between ran1 and ran3 for having the same named DATA and SAVE blocks.
3) Shouldn't be the Julia outputs the expected or corrected output?
I really need help understanding this...thanks

Here I address the misconception around the Fortran code. I won't attempt to detail the implications for a Julia program - I'll leave that to other answers, such as this one.
Important to Fortran is scope. What I'll say about that is closely related to the implicit statement.
Consider two procedures:
subroutine x
integer i
end subroutine x
subroutine y
integer i
end subroutine y
Here there are two scopes and in each a variable i. These are two totally distinct variables. There is no overlap between them at all.
Now, neither the data statement nor the save statement change that. The save statement such as
subroutine x
integer i
save i
end subroutine
says that the variable has the save attribute. This means that the variable retains its value when the subroutine completes execution (without it the variable would become undefined).
The data statement, such as
subroutine y
integer i
data i/4/
end subroutine y
explicitly initializes the variable with a particular value. On first entering the subroutine y the variable i has value 4. Crucially, the data statement also gives the initialized variables the save attribute. So, if y's i has its value changed from 4 that new value will be retained on next entering the subroutine.
That's all there is, in this context, to the data and save statements. Nothing affecting scope.

The Fortran save attribute, applies to local variables. Therefore they cannot be visible outside of the function. I do not have the full Fortran code, but to me the "outside variable" in the Fortran code, are just different variables, that happen to have the same names than the local variables. I would recommend to use implicit none in Fortran.
To have "saved" variables in julia (or static variables as they are called in C), I prefer the following approach:
let
my_saved_variable = 0 # the value corresponds to the Fortran DATA statement
global function do_something(inc)
my_saved_variable += inc
return my_saved_variable
end
end
The parameter inc gets added to the saved variable:
You get the following output:
julia> do_something(2)
2
julia> do_something(5)
7
julia> do_something(10)
17

Related

Getting the prime numbers till 10000 in fortran?

Im trying to print prime numbers till 10000. (display the first five for testing)
This is my program
program primes
implicit none
integer :: array(1229)
integer :: i, ind
logical :: is_prime
ind = 1
do i = 2, 10000, 1
if (is_prime(i) .eqv. .true.) then
array(ind) = i
ind = ind + 1
end if
end do
print *, array(1)
print *, array(2)
print *, array(3)
print *, array(4)
print *, array(5)
end program primes
function is_prime(n) result(ispr)
implicit none
integer :: c, i
integer, intent(in) :: n
logical :: ispr
c = 0
do i = 2, n
if (mod(i,2) == 0) then
c = c + 1
end if
end do
ispr = (c == 0)
end function is_prime
I don't know why but this is the output
9175178
6417360
5374044
6750309
7536745
Why does this happen and how to correct?
is_prime should be(n is the only divider of n besides 1 <=> c == 1)
function is_prime(n) result(ispr)
implicit none
integer :: c, i
integer, intent(in) :: n
logical :: ispr
c = 0
do i = 2, n
if (mod(n,i) == 0) then
c = c + 1
end if
end do
ispr = (c == 1)
end function is_prime
Could be optimezed by leaving the loop when c == 1 and i < n(after adding 1 to c)...
See on online fortran compiler
version with exit loop
While I am not familiar with modern Fortran, it looks to me as if function is_prime(n) result(ispr) is not working.
In the do loop in that function, you want a loop that tests thus:
is n divisible by 2?
is n divisible by 3?
is n divisible by 4?
is n divisible by 5?
and so on.
But, what it is actually doing is asking these:
is 2 divisible by 2?
is 3 divisible by 2?
is 4 divisible by 2?
is 5 divisible by 2?
and so on.
As a result, your counter will always have a non-zero value, and your function will always return false.
But, that's not the only problem. From your results, it looks like your Fortran implementation does not automatically initialize variables. Suppose I have statements like the following:
integer :: b
print *,b
What will be the result?
Remember, the names of variables represent locations in the computer's memory. If a variable is not initialized, it's value will be what was in the memory location before your program started to run. This value will not be related to your program.
I have 2 suggestions to fix the 2nd problem:
Prior to do i = 2, 10000, 1, have another loop that sets each value in array.
Set a values of each array (i) inside your do i = 2, 10000, 1 loop. One way to do this is to set one value when (is_prime(i) .eqv. .true.) is true and a different value when it is false.

Fortran. do while with function checking the prime number [duplicate]

This question already has answers here:
Does Fortran preserve the value of internal variables through function and subroutine calls?
(3 answers)
Closed 2 years ago.
I'm recently studying Fortran, and trying to make a program to check the prime number. The function works fine without any loop. It can give 1 when the given number is a prime number and 0 otherwise. However, it doesn't work properly when it is used in do while loop. In the range of 2 ~ 10, it is supposed to give 1 (for 2), 1(for 2), 0(for 4), 1(for 5), 0(for 6), etc. But, it keeps showing only 0. I'm pretty new to programming, so I'm not sure what I'm missing. I know there are many answers related to prime numbers, but I don't see any issue like this.
** Function checking prime numbers **
module prime_function
contains
integer function isPrime(inp_num)
implicit none
integer :: inp_num
integer :: i = 1
integer :: temp1 = 0
do while (i < inp_num)
i = i + 1
if(mod(inp_num, i) == 0) then
exit
end if
end do
if(inp_num == i) then
temp1 = 1
else
temp1 = 0
end if
isPrime = temp1
end function
end module
program fortran_q
use prime_function
implicit none
integer :: ii, a
a = isPrime(10)
print *, "10 is prime number, so the return : ", a
a = isPrime(11)
print *, "11 is prime number, so the return : ", a
ii = 1
do while (ii < 10)
ii = ii + 1
print *, isPrime(ii)
end do
end program
** Results **
10 is prime number, so the return : 0
11 is prime number, so the return : 1
0
0
0
0
0
0
0
0
0
You have a classic issue for people new to Fortran. The initialization of i and temp0 implies the SAVE attribute. When you call isPrime for the first time the values are set to 1 and 0. On the next invocation, the values of i and temp0 are set to whatever their previous values were when isPrime was last executed. The belong program fixes the issue.
module prime_function
implicit none
private
public isprime
contains
function isPrime(inp_num) result(res)
integer res
integer, intent(in) :: inp_num
integer i, temp1
i = 1
temp1 = 0
do while (i < inp_num)
i = i + 1
if (mod(inp_num, i) == 0) exit
end do
res = 0
if (inp_num == i) res = 1
end function
end module
program fortran_q
use prime_function
implicit none
integer :: ii, a
a = isPrime(10)
print *, "10 is prime number, so the return : ", a
a = isPrime(11)
print *, "11 is prime number, so the return : ", a
ii = 1
do while (ii < 10)
ii = ii + 1
print *, isPrime(ii)
end do
end program

Problems with do while implementation

I am having problems with a do while implementation for a sine taylor series. Editing the do loop to do bb = 1, 10 , 2 gives an expected result well within the margin of error, however when running the desired implementation of the do loop (do while(abs(sineseries) - accuracy > 0), will always give an answer equal to 1. So I have narrowed the possibilities down to the do while loop implementation being faulty.
program taylor
implicit none
real :: x
real :: sineseries, nfactsine
real, parameter :: accuracy = 1.e-10
integer :: signum, bb
nfactsine = 1
signum = 1
write(*,*) "Write your input value"
read(*,*) x
sineseries = 0
do while(abs(sineseries) - accuracy > 0)
sineseries = sineseries + (signum*x**bb)/nfactsine
nfactsine = nfactsine*(bb+1)*(bb+2)
signum = -signum
end do
write(*,*) sineseries, sin(x)
end program taylor
The two types of loops are not doing the same thing.
In the loop
do bb=1, 10, 2
...
end do
you have loop control with variable bb. This variable takes the values 1, 3, ..., 9 at iterations as the loop proceeds.
The do while does not have this control: you must replicate the increment of bb manually:
bb=1
do while (...)
...
bb=bb+2
end do
As Pierre de Buyl commented, you also have an error in the termination condition for the indefinite iteration count. The condition initially evaluates as false, so the loop body isn't executed even once.

Passing arguments by value in Fortran 95

How do you keep the value of a function argument when you call it, without creating a new variable? This is, how can I pass the argument by value?
In this example code:
program what
implicit none
integer :: a, b, c, d
a = 1
b = 2
c = 3
print *, a,b,c
d = f(val(a), val(b), val(c))
print *, d
print *, a,b,c
d = f(a, b, c)
print *, d
contains
function f(x,y,z) result(h)
integer:: x,y,z
integer :: h
h = x+y+z
x = 0
y = 0
z = 0
end function
end program
when i call the function the second time, it only prints 0's.
In Fortran 95 there is no way. Except some very non-standard extensions, but they are not Fortran 95 nor any other Fortran, just extensions.
In Fortran 2003 use the value attribute.
function f(x,y,z) result(h)
integer, value :: x,y,z
The attribute requires explicit interface, but your example has it, so that is OK.

What in my ALLOCATE is causing my integer overflow?

I have a set of code in which I declare the size of all my variables. However, I want to change them so some of the variables are allocatable.
This is (what I believe) to be the relevant code of the original.
parameter (m=10**5,nchar=2000,ncols=110)
character (LEN=*), PARAMETER :: FORM = "(A)"
character(LEN=nchar),DIMENSION(m) :: data
* determine the length of the data
nlines = 0
do i = 1,m
read(12,*,end=10)
nlines = nlines+1
end do
nlines = nlines+1
* read in the data
10 REWIND(unit = 12)
do i = 1,nlines
read(12,FORM) data(i)
end do
This is the form I would like.
parameter (m=10**5,nchar=2000,ncols=110)
character (LEN=*), PARAMETER :: FORM = "(A)"
character(LEN=:),DIMENSION(:),allocatable :: data
integer j
character(LEN=100000) :: temp
* determine the length of the data
nlines = 0
j = 0
do while (ios == 0)
read(12,FORM,end=10,iostat=ios) temp
if (LEN(TRIM(temp)) .gt. j) THEN
j = LEN(TRIM(temp))
end if
nlines = nlines+1
end do
nlines = nlines+1
10 REWIND(unit = 12)
ALLOCATE (character(LEN=j) :: data(nlines))
* read in the data
do i = 1,nlines
read(12,FORM) data(i)
end do
Problematically, this causes an integer overflow error. "Fortran runtime error: Integer overflow when calculating the amount of memory to allocate". I find this very perplexing as the 'nlines' and 'j' values I obtain from my test dataset are only 10 and 110 respectively.
I have found that if I don't allocate the character length (i.e. keep it a constant), this works.
I am using gfortran, version 4.8.0.
What is wrong with my code/method?