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?