Fortran arctan subroutine is not working as expected - fortran

I'm generally new to Fortran, and I have a project in which my professor wants the class to try to find pi. To do this he wants us to create our own arctan subroutine and use this specific equation: pi = 16*arctan(1/5) - 4*arctan(1/239).
Because the professor would not let me use the built in ATAN function, I made a subroutine that approximates it:
subroutine arctan(x,n,arc)
real*8::x, arc
integer::n, i
real*8::num, nm2
arc = 0.0
do i=1,n,4
num = i
nm2 = num+2
arc = arc+((x**num)/(num)) - (x**(nm2)/(nm2))
enddo
end subroutine arctan
This subroutine is based off of the Taylor series for arctan approximation, and seemed to work perfectly because I tested it by calling this.
real*8:: arc=0.0, approx
call arctan(1.d0,10000000,arc)
approx = arc*4
I called this from my main program which should return pi and I got
approx = 3.1415926335902506
which is close enough for me. The problem occurs when I try to do
pi = 16*arctan(1/5) - 4*arctan(1/239). I tried this:
real*8:: first, second
integer:: n=100
call arctan((1.d0/5.d0), n, arc)
first = 16*arc
call arctan((1.d0/239.d0), n, arc)
second = 4.d0*arc
approx = first - second
and somehow approx = 1.67363040082988898E-002, which is obviously not pi. arc resets with every call of the arctan subroutine so I know that isn't the problem. I think the problem is in how I'm calling the subroutine before I declare first and second, but I don't know what I could do to improve them.
What am I doing wrong?
EDIT:
I actually solved the problem and the actual problem was just fortran decided that it did not want to do approx = first - second
and was making it so that approx == second I have no idea why, but I solved the problem by replacing that statement with the following:
approx = (second-first)
approx = approx *(-1)
and as stupid as it looks, it works perfectly now, with a result of 3.1415926535897940!

The problem results from different types (single/double precsion) of the
variable arc in the call of arctan and the implementation of the subroutine. The iteration count of 10000... is way too much and may cause numerical problems, just 100 is more than enough (and much faster...).
Tip: always use implicit none for all progs and procedures. Here the compiler would have immediately told you that you forgot to declare arc...
Just make it double precision in the main program and you will get the desired answer.

Related

Why does MPI_REDUCE in Fortran give me inconsistent values?

I'm trying to work on a problem in Fortran using MPI, and I am getting an intermittent bug with clearly incorrect values appearing. The bug seems to occur when I use MPI_REDUCE.
I've pared my code down to as short a segment as possible, with the error still happening. This segment of code is pretty useless aside from its strange behaviour. Try as I might, I can't isolate it any further.
I don't understand the behaviour of this code - for example, if I remove the subroutine at the top (which is never invoked), the bug appears to go away. If I allocate the arrays using real,dimension(10,10) when I am declaring them, the bug appears to go away, although I don't think my current allocates are incorrect. Even if I change some of the variable names in this, the bug appears to go away. None of these are telling me why the bug exists, or how to fix it in my longer code project.
It seems like either I have failed to correctly allocate memory somewhere, or I am using MPI_REDUCE incorrectly, but I can't find the problem.
subroutine foo()
use netcdf
integer :: iret,ncid
iret = nf90_open('test.nc',nf90_nowrite,ncid) !open the mask file
iret = nf90_close(ncid) !close the mask file
return
end subroutine foo
program test
use mpi
integer :: ierr,pid
real :: diffsum,total_sum
real,allocatable,dimension(:,:) :: c,h,h_old
call MPI_INIT(ierr)
total_sum = 0.0
call MPI_COMM_RANK(MPI_COMM_WORLD,pid,ierr)
if(pid.ne.0) then
allocate(h (10,10))
allocate(h_old(10,10))
h(:,:) = 1.0
h_old(:,:) = 1.0
allocate(c(10,10))
c = h_old - h
diffsum = 0.0
endif
call MPI_REDUCE(diffsum,total_sum,1,MPI_REAL,mpi_sum,0,MPI_COMM_WORLD,ierr) !to get overall threshold
if(pid.eq.0)then
print*,'sum',total_sum
endif
call MPI_FINALIZE(ierr)
end program test
The value printed should always be 0, but sometimes other values appear.
Here is an example of the outputs from 10 runs:
sum -3.66304099E+25
sum 0.00000000
sum 0.00000000
sum -3.01998057E+29
sum 0.00000000
sum 0.00000000
sum 0.00000000
sum 0.00000000
sum 0.00000000
sum 0.00000000
Thank you for any ideas!

How to let gnuplot window persist and the main program not freeze

I have a program in Fortran that calculates a file, say, named
wvfunc3d.dat
which I want to visualize with Gnuplot in real time during the execution of my program. After the code that creates this file, I put in my program a string
jret=SYSTEM('gnuplot wf3d.plt')
the script file wf3d.plt has the only string and looks like:
splot 'wvfunc3d.dat' w l
All of this really draws a plot I want to see, but, as is well known, it immediately disappears. I know, there is an option to avoid the closing of the window,
jret=SYSTEM('gnuplot -persist wf3d.plt')
that lets my plot not to disappear, but then the execution of the Fortran program also freezes until I close the window with the graph.
So, I want the plot to persist until I have new data, to be automatically updated after a new call of a command in Fortran, but I also need my program to run calculations! Is there a way to solve this problem? I use Windows XP.
I think you may be able to use EXECUTE_COMMAND_LINE instead of system to achieve what you want. This allows you to include a wait option which when set to .false. allows the fortran code to keep running. You can then add a sleep and reread to your gnuplot script (e.g. sleep 1 and reread) as suggested in this post.
If this doesn't work, you could consider a multi-threaded strategy ( openMP or mpi in fortran). Personally, I usually just run gnuplot at the same time and trigger an update of the plotted data by pressing the a key. I use linux so cannot test it for windows but a minimal example which works for me is,
program gnuplot
implicit none
logical :: gnuplot_open = .false.
integer :: i,t,N,redraw
real(kind(0.d0)),dimension(:),allocatable :: x,y
real(kind(0.d0)),parameter:: pi=4.d0*atan(1.d0)
N = 1000
allocate(x(N),y(N))
redraw = 100
do t = 1,300000
do i=1,N
x(i) = 6.d0*i/N*pi
y(i) = sin(x(i)+t*0.2)
enddo
if (mod(t,redraw) .eq. 0) then
open(1,FILE='./tempout',status='replace')
do i=1,N
write(1,*) x(i),y(i)
enddo
close(1,status='keep')
endif
if (.not. gnuplot_open) then
call execute_command_line('gnuplot --persist plot_tempout', wait=.false.)
gnuplot_open = .true.
endif
enddo
end program gnuplot
and plot_tempout is,
plot 'tempout' u 1:2 w l
pause 0.1
reread
Ed, thank you very much for your thorough reply. I will try to work on it.
Before encountering this problem I was able to easily draw the plots of small enough files using a cycle directly in gnuplot. Something like this:
do for [i=1:100500] {plot 'littldat.dat' w l; pause 3}
that did it well. But when I tried to do this with large file, it was very often caught read by gnuplot in the moment it was not completed yet: I had either a full plot or a plot of a part of my data, and it was not good. Because of this I began to seek for the way to do it by the means of programming language.
Before you answered I finally found a very simply, though not very elegant solution: you write data to a temporary file and then, once it is completed, give it the final name to be read by gnuplot. So, gnuplot reads either old data, or new ones, but never an incomplete file. It results to be something like this in Fortran:
open(1,file='donnees_temp.dat')
write(1,*)x,y,z
close(1)
call rename ('donnees_temp.dat','donnees.dat')
and, in Gnuplot I used a cycle like the one above:
do for [i=1:100500] {splot 'donnees.dat' w l; pause 5}
so it works, and the program executes.

Debugging DO loop / IF blocks in a Fortran 90 program (Beginner level)

I would be grateful if someone could help me out here. I'm just starting out learning how to program, so there is a great chance I'm missing something very obvious. I'm trying to write a program in Fortran 90 that solves question 4 i) on page 45 of this pdf. I have finally managed to get my code to compile to something, but now that something is somewhat rubbish, the data it produces is crazy (as time increases, I get a decrease in distance after whatever I input at t0). Can someone spot my mistake? I realize this is quite a lot of code to look through, I'm sorry for asking so much of you. Thanks in advance for looking through!
PROGRAM PARACHUTIST
! Tabulation of parachutist's descent z and and speed zdot
! as functions of time t
!Assign the program's associated constants
IMPLICIT NONE
REAL z, zdot, g, U1, U2, z0, u0, t0, q0, t, x,c,s
INTEGER I
g=9.8
U1=54
U2=5
!Break z0 down a little with q0
q0=COSH(g*t0/U1)
z0=U1**2/g*LOG(q0)
u0=U1*TANH(g*t0/U1)
!Prompt for and read in the free-fall time
Print*, 'Input free-fall time in seconds:'
Read*, t0
!Print the table headings
WRITE(*,1000)
1000 FORMAT (6X, 'TIME', 6X, 'DISTANCE', 6X, 'VELOCITY', /6X, '(SEC)', 7X, '(M)', 10X, '(M/SEC)',&
/6X, '0.0', 10X, '0.0', 10X, '0.0' )
!Loop covering the specified times
t=0
DO I=0,20
! Calculate the distance above ground
200 IF(t<=t0) THEN
x=g*t/U1
z=U1**2/g*LOG(COSH(x))
zdot=U1*TANH(x)
Elseif(t>t0) THEN
x=g*(t-t0)/U2
!store re-used expressions
c=cosh(x)
s=sinh(x)
z= z0 + (U2**2/g)*LOG(c+ (u0/U2)*s)
zdot=U2*(U2*s+u0*c)/(U2*c+u0*s)
Endif
!Print a line of table using T formats
WRITE(*,100) t, z, zdot
100 Format(4X, F5.2, 6X, F7.2, 6X, F7.2)
!Stop with message if landed
If(z.GE.500) THEN
PRINT*, 'LANDED'
STOP
!If we haven't yet landed then increment t as in
! problem specs
Elseif(t<15) then
t=t+1
Elseif(t.GE.15) then
t=t+10
ENDIF
!End of the t-loop
END DO
END PROGRAM PARACHUTIST
I wrote this as two comments, but it was really too lengthy. Go ahead and delete it all if you had planned to do so. I just browsed through a document comparing Fortran77 and "modern" Fortran90. (I coded in Fortran77 when I was just starting school, awhile ago...). Here are some suggestions:
Be careful with your use of "ELSEIF". It is usually okay for ELSE and IF to have the space omitted, but that is not true otherwise with free-format code (I think the only other instances of space-optional are DOUBLE PRECISION, ELSE IF, GO TO, END DO, and END IF).
An advantage of using Fortran90 is that you shouldn't even need ELSE IF's, (nor computed GOTO's!) as there is SELECT CASE.
You shouldn't need FORMAT either, as it can be incorporated directly with a format string in the READ or WRITE statement itself.
Yes, you can use either the old Fortran 77 operators .GE..GT..EQ..NE..LE..LT. or the new ones >= > == /= <= <. However, I'm not sure if you should mix them, which I noticed in your code.
EDIT: The second link above, about Control Structures, describes how you can use DO loops instead of IF's in Fortran90, sections 3.2 - 3.5. You can use named DO's, indefinite DO loops, DO WHILE's, all sorts of things! There are examples too. (The name of the entire document is Fortran90 for Fortran77 Programmers.)

Need to convert following FORTRAN code to C++

Im a very poor programmer, and i was given a program to supposedly help me on my aerodynamic hw. but its in fortran, and im trying to use MATLAB to run this program. any help on converting it to a language matlab understands? (preferabbly c++)
program joukow
c
c computes joukowski airfoil and finds pressure coefficient
c currently set up for symmetric airfoil with sharp trailing edge
c and chord length equal to one.
c profile is written onto prof.dat and cp onto cp.dat
c implicit real*8(a-h,o-z)
complex z,zeta,cw
dimension uz(100),vz(100),xi(100),eta(100),cp(100)
dimension xout(100),yout(100)
open(unit=8,file='prof.dat',status='unknown')
open(unit=9,file='cp.dat',status='unknown')
b=1.d0
write(6,98)
format(2x,'input the radius of the a-circle in z plane')
read(5,99)a
format(f10.0)
xl=2.*a-1.+1./(2.*a-1.)
c xl=a+1./a
c chord=2.*xl
chord=2.+xl
del=a-b
c del =0.1d0
do 50 i=1,100
ri=i
theta=6.2832d0*ri/101.d0
x=-del+a*cos(theta)
y=a*sin(theta)
z=cmplx(x,y)
zeta=z+b**2/z
c
c xi and eta are coordinates of points on airfoil
c
xi(i)=real(zeta)
eta(i)=aimag(zeta)
cw=(1.-a**2/(z+del)**2)/(1.-b**2/z**2)
c
c uz and vz are velocity components on the airfoil assuming the free-stream
c speed is one.
c
uz(i)=real(cw)
vz(i)=-aimag(cw)
c
c xout and yout are airfoil coordinates where the leading edge is at (0,0)
c and the chordlength is one.
c
xout(i)=(xl+xi(i))/chord
yout(i)=eta(i)/chord
write(8,100)xout(i),yout(i)
format(2x,2f10.4)
continue
c
c now calculate the pressure coefficient cp
c
write(6,200)
format(2x,'pressure coefficients')
do 70 i=1,50
cp(i)=1.-(uz(i)**2+vz(i)**2)
write(9,100)xout(i),cp(i)
continue
stop
end
Matlab understands Fortran just fine -- check the documentation. And if that doesn't satisfy you, most of the lines in the program which do any computation could be typed into the Matlab console with very little modification. If you are a poor programmer, I suggest that you spend your time modifying the program into Matlab rather than into C++. I'll write more later if you don't get any better help than I have time for right now.
EDIT: first off, some information on using Fortran source files from Matlab. If you really don't want to (or can't or have performance reasons for not doing so) rewrite the Fortran into Matlab then turn it into a MEX file. Using f2c (or anything else, including your own time and effort) to first translate the Fortran into C or C++ seems pointless to me.
If you don't like that idea, here are some ideas on turning Fortran into Matlab.
First, all lines beginning with C or c are comments so you don't need to translate them. Start with your code:
complex z,zeta,cw
dimension uz(100),vz(100),xi(100),eta(100),cp(100)
dimension xout(100),yout(100)
These lines declare a number of variables. You don't have to declare variables before you use them in Matlab but, there are sometimes good reasons to do so. You don't have to in Fortran either, though this is universally considered a bad idea these days. You could 'declare' these variables in Matlab with statements such as:
uz = zeros(100,1);
vz = zeros(100,1);
By declaring these in advance in your Matlab you allocate memory for them once, and avoid some performance-reducing problems.
The next 2 lines:
open(unit=8,file='prof.dat',status='unknown')
open(unit=9,file='cp.dat',status='unknown')
open a couple of files for output. They are used later in write statements - forget them, write Matlab statements such as save xout instead.
The next line is Fortran but identical in Matlab:
b=1.d0
The next lines get a value for the radius from the console:
write(6,98)
format(2x,'input the radius of the a-circle in z plane')
read(5,99)a
format(f10.0)
again, I suggest you forget these, just use the Matlab console to set the value of a. More Fortran that doesn't need to be translated (though I suggest you either drop the decimal points without following 0s or put a space between them and the subsequent * -- .* is a specific operator in Matlab):
xl=2.*a-1.+1./(2.*a-1.)
chord=2.+xl
del=a-b
A Fortran do loop is the same as a Matlab for loop. Rewrite:
do 50 i=1,100
as
for i = 1:100
As one of the other respondents has noted it's not clear where the matching end statement goes, you'll have to figure that out. Note that I'm just offering a line-by-line translation of Fortran into Matlab. It's not well-written Fortran, and I'm not offering well-written Matlab, I'll leave that to you.
This lot doesn't need to be translated:
ri=i
theta=6.2832d0*ri/101.d0
x=-del+a*cos(theta)
y=a*sin(theta)
cmplx is a Fortran function which returns a complex number which has real part x and imaginary part y:
z=cmplx(x,y)
In Matlab this would be z = x + y * i. Fortran uses ** for exponentiation, Matlab uses ^
zeta=z+b**2/z
and so on and so on.
Hope that helps.
I used f2matlab and a little touching up afterward. Here is the cleaned up and compilable fortran90 code:
program joukow
!
! computes joukowski airfoil and finds pressure coefficient
! currently set up for symmetric airfoil with sharp trailing edge
! and chord length equal to one.
! profile is written onto prof.dat and cp onto cp.dat
! implicit real*8(a-h,o-z)
complex z,zeta,cw
dimension uz(100),vz(100),xi(100),eta(100),cp(100)
dimension xout(100),yout(100)
open(unit=8,file='prof.dat',status='unknown')
open(unit=9,file='cp.dat',status='unknown')
b=1.d0
write(6,98)
98 format(2x,'input the radius of the a-circle in z plane')
read(5,99)a
99 format(f10.0)
xl=2.*a-1.+1./(2.*a-1.)
! xl=a+1./a
! chord=2.*xl
chord=2.+xl
del=a-b
! del =0.1d0
do i=1,100
ri=i
theta=6.2832d0*ri/101.d0
x=-del+a*cos(theta)
y=a*sin(theta)
z=cmplx(x,y)
zeta=z+b**2/z
!
! xi and eta are coordinates of points on airfoil
!
xi(i)=real(zeta)
eta(i)=aimag(zeta)
cw=(1.-a**2/(z+del)**2)/(1.-b**2/z**2)
!
! uz and vz are velocity components on the airfoil assuming the free-stream
! speed is one.
!
uz(i)=real(cw)
vz(i)=-aimag(cw)
!
! xout and yout are airfoil coordinates where the leading edge is at (0,0)
! and the chordlength is one.
!
xout(i)=(xl+xi(i))/chord
yout(i)=eta(i)/chord
write(8,100)xout(i),yout(i)
100 format(2x,2f10.4)
end do
!
! now calculate the pressure coefficient cp
!
write(6,200)
200 format(2x,'pressure coefficients')
do i=1,50
cp(i)=1.-(uz(i)**2+vz(i)**2)
write(9,100) xout(i),cp(i)
end do
stop
end program joukow
Here is the resulting matlab code:
function hw1(varargin)
%
% computes joukowski airfoil and finds pressure coefficient
% currently set up for symmetric airfoil with sharp trailing edge
% and chord length equal to one.
% profile is written onto prof.dat and cp onto cp.dat
% implicit real*8(a-h,o-z)
format_99=['%10.0f'];
format_100=[repmat(' ',1,2),repmat('%10.4f',1,2),'\n'];
format_200=[repmat(' ',1,2),'pressure coefficients \n'];
fid_8=fopen('prof.dat','w+');
fid_9=fopen('cp.dat','w+');
b=1.0d0;
a=input('input the radius of the a-circle in z plane');
xl=2..*a-1.+1../(2..*a-1.);
% xl=a+1./a
% chord=2.*xl
chord=2.+xl;
del=a-b;
% del =0.1d0
for i=1:100;
ri=i;
theta=6.2832d0.*ri./101.0d0;
x=-del+a.*cos(theta);
y=a.*sin(theta);
z=complex(x,y);
zeta=z+b.^2./z;
%
% xi and eta are coordinates of points on airfoil
%
xi(i)=real(zeta);
eta(i)=imag(zeta);
cw=(1.-a.^2./(z+del).^2)./(1.-b.^2./z.^2);
%
% uz and vz are velocity components on the airfoil assuming the free-stream
% speed is one.
%
uz(i)=real(cw);
vz(i)=-imag(cw);
%
% xout and yout are airfoil coordinates where the leading edge is at (0,0)
% and the chordlength is one.
%
xout(i)=(xl+xi(i))./chord;
yout(i)=eta(i)./chord;
fprintf(fid_8,format_100,xout(i),yout(i));
end; i=100+1;
%
% now calculate the pressure coefficient cp
%
fprintf(1,format_200);
for i=1:50;
cp(i)=1.-(uz(i).^2+vz(i).^2);
fprintf(fid_9,format_100, xout(i),cp(i));
end; i=50+1;
end %program joukow
They both give the same results for me. I didn't check the algorithm for correctness, though, just converted the code.
I don't know how well it's still supported - - but the easiest way used to be f2c which translates fortran directly into c code.

Fortran - problem with double precision

I have a small program that read some data from binary file and stores it into normal (unformatted) files. Here is the source:
Program calki2e
IMPLICIT NONE
!
DOUBLE PRECISION VAL
INTEGER P,Q,R,S
INTEGER IREC2C
PARAMETER( IREC2C=15000)
INTEGER AND,RSHIFT,LABEL,IMBABS,NX,IB,NFT77
INTEGER IND
DIMENSION IND(IREC2C)
DOUBLE PRECISION XP
DIMENSION XP(IREC2C)
CHARACTER(LEN=12) :: FN77 = 'input08'
CONTINUE
NFT77=77
!----------------------------------------------------------------------
2 CONTINUE
c
open(unit=NFT77,file=FN77,STATUS='OLD',
+ACCESS='SEQUENTIAL',FORM='UNFORMATTED')
open(unit=13,file='calki2e.txt')
REWIND(77)
4100 continue
READ(77) NX,IND,XP
IMBABS=IABS(NX)
DO 100 IB=1,IMBABS
LABEL=IND(IB)
P= AND(RSHIFT(LABEL, 24),255)
Q= AND(RSHIFT(LABEL, 16),255)
R= AND(RSHIFT(LABEL, 8),255)
S= AND( LABEL ,255)
VAL=XP(ib)
IF(P.EQ. Q) VAL=VAL+VAL
IF(R .EQ. S) VAL=VAL+VAL
IF((P .EQ. R).AND.(Q .EQ. S)) VAL=VAL+VAL
write(13,*)P,Q,R,S,val
100 CONTINUE
IF (NX.GT.0) GOTO 4100
CRB
CLOSE(UNIT=NFT77)
!
END
When I compile it using gfortran I obtain double precision in output file but with g77 I get only single precision. What it wrong and how to change it?
Do you mean the "write (13, *) statement. This is "list directed" output. It is a convenience I/O with few rules -- what you get will depend upon the compiler -- it is best used for debugging and "quick and dirty" programs. To reliably get all the digits of double precision, change to a formatted output statement, specifying the number of digits that you need. (It is probably best to switch to gfortran anyway, as g77 is no longer under development.)
your numbers are double precision but you are printing them in free format. You have to specify an explicit format
If you want to keep your code F77, try something like
write(13,1000) P,Q,R,S,val
1000 format(1X,4I7,1X,1E14.10)
The "1X"s mean one space, "4I7" means four seven-width integers, and 1E14.10 means one fourteen-charater width scientific-notation real number with 10 significant digits. Feel free to mess around with the numbers to get it to look right.
This is a pretty good tutorial on the topic.
I would be tempted to set the format on your write statement to something explicit, rather than use * in write(13,*)P,Q,R,S,val.