Related
I have the following fortran code defined under. I am trying to change the length of the do loop if i change the value of n. When i try to compile i get the error:
‘a’ argument of ‘floor’ intrinsic at (1) must be REAL. But when i change q and w to be defined as real i get another error message. How can i fix this? q and w is clearly a integer when i use floor(...)
subroutine boundrycon(n,bc,u,v)
!input
integer :: n,bc
!output
real(8) :: u(n+2,n+2), v(n+2,n+2)
!lokale
integer :: j,i,w,q
n=30
q=floor(n/2)
w=(floor(n/2)+floor(n/6))
do j=q,w
u(q,j)=0.0;
v(q+1,j)=-v(q,j);
u(w,j)=0.0;
v(w+1,j)=-v(w,j);
end do
do i=q,w
v(i,q)=0.0;
u(i,q)=-u(i,q+1);
u(i,w+1)=-u(i,w);
v(i,w)=0;
end do
end subroutine boundrycon
Many people have already pointed this out in the comments to your question, but here it is again as an answer:
In Fortran, if you do a division of two integer values, the result is an integer value.
6/3 = 2
If the numerator is not evenly divisible by the denominator, then the remainder is dropped:
7/3 = 2
Let's look at your code:
q=floor(n/2)
It first evaluates n/2 which, since both n and 2 are integers, is such an integer division. As mentioned before, this result is an integer.
This integer is then passed as argument to floor. But floor expects a floating point variable (or, as Fortran calls it: REAL). Hence the error message:
"[The] argument of floor ... must be REAL."
So, the easiest way to get what you want is to just remove the floor altogether, since the integer division does exactly what you want:
q = n/2 ! Integer Division
If you need to make a floating point division, that is if you want two integer variables to divide into a real variable, you have to convert at least one of them to floating point before the division:
print *, 3/2 ! wrong, prints 1
print *, real(3)/2 ! right
print *, 3/2.0 ! right
print *, (3 * 1.0) / 2 ! right
print *, real(3/2) ! wrong, prints 1.0
In the Fortran code given below, I have made all numbers involving calculation of PI as double precision but the value of PI I get is just a real number with a large number of zero or 9 at the end. How do I make the program give PI in double precision? I am using gfortran compiler.
!This program determines the value of pi using Monte-Carlo algorithm.
program findpi
implicit none
double precision :: x,y,radius,truepi,cnt
double precision,allocatable,dimension(:) :: pi,errpi
integer :: seedsize,i,t,iter,j,k,n
integer,allocatable,dimension(:) :: seed
!Determining the true value of pi to compare with the calculated value
truepi=4.D0*ATAN(1.D0)
call random_seed(size=seedsize)
allocate(seed(seedsize))
do i=1,seedsize
call system_clock(t) !Using system clock to randomise the seed to
!random number generator
seed(i)=t
enddo
call random_seed(put=seed)
n=2000 !Number of times value of pi is determined
allocate(pi(n),errpi(n))
do j=1,n
iter=n*100 !Number of random points
cnt=0.D0
do i=1,iter
call random_number(x)
call random_number(y)
radius=sqrt(x*x + y*y)
if (radius < 1) then
cnt = cnt+1.D0
endif
enddo
pi(j)=(4.D0*cnt)/dble(iter)
print*, j,pi(j)
enddo
open(10,file="pi.dat",status="replace")
write(10,"(F15.8,I10)") (pi(k),k,k=1,n)
call system("gnuplot --persist piplot.gnuplot")
end program findpi
Your calculation is in double precision, but I see two issues:
The first is a systematic error... You determine pi by
pi(j)=(4.D0*cnt)/dble(iter)
iter is at most 2000*100, so 1/iter is at least 5e-6, so you can't resolve anything finder than that ;-)
The second issue is that your IO routines print the results in single precision! The line
write(10,"(F15.8,I10)") (pi(k),k,k=1,n)
and more specifically the format specifier "(F15.8,I10)" needs to be adjusted. At the moment it tells the compiler to use 15 characters overall to print the number, with 8 digits after the decimal point. As a first measure, you could use *:
write(10,*) (pi(k),k,k=1,n)
This uses 22 characters in total with all 15 digits for double precision:
write(10,"(F22.15,I10)") (pi(k),k,k=1,n)
An odd issue here. I am converting legacy F77 code that packed 8 a1 characters into a 64 bit integer, and the code worked back in the day. But in today's world to get 8 characters in a word I need to move to integer*8 variables, and the code is failing in that case. It works until it's packed 4 characters, but character 5 simply rotates around and overwrites the first packed character. Here is a sample output for "hello world”, of note is that the integer*8 variable OUTBUF suddenly shrinks and appears to convert to an integer*4 variable too:
hello world
in='hello world '
cn= 8,k=1,j='h',outbuf(k)='h '
cn=16,k=1,j='e',outbuf(k)='he '
cn=24,k=1,j='l',outbuf(k)='hel '
cn=32,k=1,j='l',outbuf(k)='hell'
cn=40,k=1,j='o',outbuf(k)='oell'
# SPACE
cn= 8,k=2,j='w',outbuf(k)='w '
cn=16,k=2,j='o',outbuf(k)='wo '
cn=24,k=2,j='r',outbuf(k)='wor '
cn=32,k=2,j='l',outbuf(k)='worl'
cn=40,k=2,j='d',outbuf(k)='dorl'
# SPACE
I've distilled the code to this snippet, anyone know F77 anymore and see what’s happening? thanks.
program cow
implicit integer*8 (a-z)
integer inbuf(72)
integer*8 outbuf(40)
c changing the integer*1 to integer*8 fixes the problem !!!!!!!!!
integer*1 j
read 99, inbuf
99 FORMAT(BZ,72a1)
print 1, inbuf
1 format("in='",72a1,"'")
IP=0
k = 1
DO 100 I=1,40
OUTBUF(I)= 8H
100 CONTINUE
200 IP=IP+1
IF(IP.GT.72) GO TO 6000
J= INBUF(IP)
IF(J.EQ." ") GO TO 6000
CN = (MOD(CP,8)+1) * 8
outbuf(k) = outbuf(k) .and. (.not. lshift('377'O, (cn-8)))
OUTBUF(K) = OUTBUF(K) .OR. lSHiFT( j,(CN-8))
print 4301, cn, k, j, outbuf(k)
4301 format("cn=",i2,",k=",i1,",j='",a1,"',outbuf(k)='",a8,"'")
CP=CP+1
GO TO 200
6000 continue
print *,'# SPACE'
call exit
end
Note that this code is for little endian machines.
" So ignore the code, the task at hand - in FORTRAN 77 - is to pack 8
a1 characters into an integer*8 variable. "
Ok, but I will have to wash my hands after this:
INTEGER*8 i
CHARACTER*8 ch
EQUIVALENCE (i, ch)
ch = "abcdefgh"
Of course, transfer is nicer, but certainly not FORTRAN 77.
This is quite horrible code to do something trivial in newer Fortran
(since F77...).
You can replace the whole routine by something like
integer, parameter :: llen=72
character(len=llen) inbuf
character(len=llen) output
integer :: i,k
read (*,'(BZ,A)') outbuf
k = 1
do i=1,llen
if (inbuf(i) /= ' ') then
outbuf(k) = inbuf(i)
if (mod(k,8) == 0) then
! Some output goes here
endif
k = k + 1
end if
end do
OK, here is the second answer; not preferred, it is really better to rewrite the program using characters.
Caveat: integer*8 is non-standard.
program main
character(len=8) :: c
integer*8 output(10)
c = "12345678"
call foo(output,10,c)
write (*,'(Z16)') output(1)
end program main
subroutine foo(a,n,b)
integer*8 :: a(1)
character(len=8) :: b
a(1) = transfer(b,mold=a(1))
end subroutine foo
As I mentioned, this input parser took Hollerith data and compared it against Hollerith data in a database, in a case-insensitive manner. The code was written for 60-64 bit, big-endian machines, and used hardware specific shifts, masks, ANDs and ORs to pack the A1 input characters into an A8 INTEGER word for comparison against the database.
And although I did manage to adjust all those shifts, masks and bitwise operations to work on 64-bit little-endian machines (x86_64), it was a PITA, and only worked with pgf77 and ifort, gfortran was having none of that crap! So this is what I came up with, much nicer, hardware independent, and works on all the compilers available to me:
The parser still accepts A1 input characters stored in INTEGERs (80a1), and returns packed A8 characters in INTEGER*8 words, for backwards compatibility.
It converts 80a1 input data to CHARACTER*80 variable IN0:
write( in0, '80a1' ) (inbuf(i), i=1,80)
Since this is all ASCII data, I used this code to lower-case everything in IN0 to a new character*80 variable IN:
do i = 1, len(in0)
#if (! defined __GFORTRAN__)
j = ichar(in0(i:i))
if (j>= ichar("A") .and. j<=ichar("Z") ) then
in(i:i) = char(ichar(in0(i:i))+32)
else
in(i:i) = in0(i:i)
end if
#else
j = iachar(in0(i:i))
if (j>= iachar("A") .and. j<=iachar("Z") ) then
in(i:i) = achar(iachar(in0(i:i))+32)
else
in(i:i) = in0(i:i)
end if
#endif
end do
It was then a simple matter to loop through the CHARACTER*80 variable IN, collect non-space characters into the character variable WORD, and then encode them into INTEGER*8 array OUTBUF:
read( word(1:8), ‘a8' ) outbuf(outbufc)
Thanks all.
The following is the code I have written to find the DFT of sine(x) over a period.
program fftw_test
implicit none
INTEGER FFTW_MEASURE
PARAMETER (FFTW_MEASURE=0)
INTEGER FFTW_ESTIMATE
PARAMETER (FFTW_ESTIMATE=64)
INTEGER FFTW_FORWARD
PARAMETER (FFTW_FORWARD=-1)
integer, parameter :: n = 8
integer :: i
double complex, dimension(0:n-1) :: input, output
double precision, parameter :: pi = 3.141592653, h = 2.0d0*pi/(n)
integer*8 :: plan
call dfftw_plan_dft_1d(plan, n, input, output, fftw_forward, fftw_measure)
do i = 0, n-1
input(i) = cmplx(sin(h*i), 0)
end do
call dfftw_execute_dft(plan, input, output)
output = output/n
output(0) = cmplx(0,0) ! setting oddball wavenumber to be 0
call dfftw_destroy_plan(plan)
do i = -n/2, n/2-1, 1
write(*, *) i, output(i+(n/2))
end do
end program
I am aware of the r2c (real to complex) function in the FFTW library. But I was advised to use the normal c2c function. So I defined the input function as a complex number with real part = sine(x) and complex part 0.
The DFT of sine(x) is supposed to be fk(-1) = cmplx(0, -0.5) and fk(1) = cmplx(0, 0.5) where fk(k) means the fourier coefficient of the k wavenumber
The output I received is as follows.
-4 ( 0.0000000000000000 , 0.0000000000000000 )
-3 ( 3.2001271327131496E-008,-0.49999998518472011 )
-2 ( -1.0927847071684482E-008, 1.4901161193847656E-008)
-1 ( -1.0145577183762535E-008, 1.4815279864022202E-008)
0 ( -1.0927847071684482E-008, 0.0000000000000000 )
1 ( -1.0145577183762535E-008, -1.4815279864022202E-008)
2 ( -1.0927847071684482E-008, -1.4901161193847656E-008)
3 ( 3.2001271327131496E-008, 0.49999998518472011 )
I am getting fk(-3) = cmplx(~0, -0.5) and fk(3) = cmplx(~0, 0.5). If I increase the grid size to 16, 32 or so I get -n/2 -1 and n/2 -1 wavenumbers with the required values instead of the -1 and 1 wavenumbers.
Does this have something to do with the way FFTW stores the output in the output array ? Or am I going wrong anywhere else ?
Also, I don't seem to be getting 'proper 0' where I should be. It is instead numbers of the order of 10^(-8) which I believe is the smallest my datatype double can hold. Is that something I should be worried about ?
Like #VladimirF already said, the ordering of the values is a bit different, than you might expect. The first half of the array holds the positive frequencies, the second half holds the negative frequencies in reverse order (see this link). And you might have to check the sign convention used by FFTW.
The problem with accuracy stems from your single precision value for pi and the use of cmplx which produces single precision complex numbers (use the keyword argument kind). In this case you could simply assign your real value to the complex variables. Applying these two changes yields a precision of ~1e-10. This can be improved by supplying a better approximation for pi (i.e. more than 10 digits).
E.g. the value pi = 3.141592653589793d0 yields results with accuracy of 1e-16.
I'm rewriting some code to make a program compile with the gfortran compiler as opposed to ifort compiler I usually use. The code follows:
_Subroutine SlideBits (WORD, BITS, ADDR)
Implicit None
Integer(4) WORD
Integer(4) BITS
Integer(4) ADDR
Integer(4) ADDR1
ADDR1 = 32 - ADDR
WORD = (WORD .And. (.Not.ISHFT(1,ADDR1))) .Or. ISHFT(BITS,ADDR1)
End_
When I compile the above code using the gfortran compiler, I recieve this error:
WORD = (WORD .And. (.Not.ISHFT(1,ADDR1))) .Or. ISHFT(BITS,ADDR1)
Error: Operand of .NOT. operator at (1) is INTEGER(4)
All three of the variables coming into the subroutine are integers. I've looked around a bit and the gfortran wiki states that the gfortran compiler should be able to handle logical statments being applied to integer values. Several other sites I've visited either quote from the gnu wiki or agree with it. This is the first time I've seen this error as the Intel Fortran compiler (ifort) I normally use compiles cleanly.
The comments/answers above "may .Not. be" the correct responses, depending on your ultimate objective.
The likely purpose of that "WORD = .." statement is .NOT. to arrive at a boolean/logical result, but rather to obtain a kind of integer enumerator.
To see this, first "ignore" the bit shifting (iShift() etc), and just look at something like IntR = Int1 .Or. Int2. This will produce a "proper" integer result. The value will depend on not only the values of the int's, but also on their declared "type" (e.g. Integer(1), Integer(2), etc)
That is, the resulting value of WORD will be a "proper" integer; something like "33504" .. or whatever, (likely) .NOT. a 0/1 or -1/0 or .True./.False. etc
If you replace = Int1 .Or. Int2 with = (Int1 /= 0) .Or. (Int2 /= 0) ... you will get an "integer logical" (i.e. 0/1 etc) and WILL NOT produce the
desired enumerator ... if that is what you are looking for.
The .Or. on two Int's is a kind of bit-wise addition that produces a new num based on how the bits align/word size etc.
e.g. 3 == 011, 2 = 010 ... so, 3 .Or. 2 ==> 011 = 3
e.g. 3 == 011, 5 = 101 ... so, 3 .Or. 5 ==> 111 = 7
e.g. 5 == 101, 5 = 101 ... so, 5 .Or. 5 ==> 101 = 5
... similarly the .And. provides a kind of multiplication.
This technique is sometimes used to create enumerators somewhat like the use of powers of two (1,2,4,8...) are used to assign a value. Then, any sum of those
values can be decomposed, for example, into its constituent elements. For instance, if a(1) = 2, and a(2) = 8, then the sum 10 can be decomposed to
show the selections were the 1st and 4th elements of (1,2,4,8,...) etc.
It may help conceptualise this by noting that bit-shifting is like multiplying by 2 (for left shift) and dividing by 2 (for right shift).
BTW, you don't need to restrict to Fortran for this. Whack it into a VBA function and see the result in your spreadsheet VBA does not
have bit shift intrinsics, but they are available ... in any case it will demonstrate the Int1 .Or. Int2 behaviour even without bit shifting, such as
Function TwoIntsOr(Int1 As Long, Int2 As Long) As Long
'
TwoIntsOr = Int1 Or Int2
'
End Function
-- .Or. in Fortran
Function TwoIntsOr(Int1, Int2)
Integer :: TwoInstOr
Integer, Intent(In) :: Int1, Int2
!
TwoIntsOr = Int1 .Or. Int2
!
End Function
).
It is not standard Fortran to apply logical/boolean operators to integer variables. If the goal is a boolean result, the ideal solution would be to convert the types to logical. If, as it appears from casual examination, the code is really doing bit-wise operations, then it would be better to use the IAND and IOR intrinsic functions.
gfortran is expecting booleans for the logical operators and the code is providing integers. Use comparisons with zero instead of logical operators.
WORD = ((WORD /= 0) .And. (ISHFT(1,ADDR1) == 0)) .Or. (ISHFT(BITS,ADDR1) /= 0)
gfortran and ifort use different representations for .true. and .false. values, so it's best to stick to booleans when that's what the code needs. In a conversion from ifort to gfortran I got bit by the former representing .true. as -1 and the latter using 1 for the same purpose, instead of the traditional (C-like) not 0.