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.
Related
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.
I have been working with some old legacy code in Fortran used by a colleague. The actual code is proprietary, so the examples I use here are abbreviated compared to the code I'm working with.
Some of the procedures individually defined in *.f files included a file called variables.h:
Example contents of variables.h:
c VARIABLE DIMENSIONS FOR MODEL
c height_dim -- number of vertical (z) steps
c length_dim -- number of horizontal (x) steps
c width_dim -- number of horizontal (y) steps
INTEGER height_dim, length_dim, width_dim, nmodes, styleFlag
PARAMETER (height_dim=80, length_dim=50, width_dim=40)
PARAMETER (nmodes = 4,
$ styleFlag = 3)
I changed that to the following:
! VARIABLE DIMENSIONS FOR MODEL
! height_dim -- number of vertical (z) steps
! length_dim -- number of horizontal (x) steps
! width_dim -- number of horizontal (y) steps
INTEGER height_dim, length_dim, width_dim, nmodes, styleFlag
PARAMETER (height_dim=80, length_dim=50, width_dim=40)
PARAMETER (nmodes = 4, styleFlag = 3)
An example routine that uses these might be the following, called initial_conditions.f:
c This sets up the PDE's initial conditions
subroutine initial_conditions( temperature, density )
IMPLICIT NONE
INCLUDE 'variables.h'
real*8 temperature(height_dim,length_dim,width_dim)
real*8 density(height_dim)
temperature = 273.15D0
density = 1.0D0
return
end
I tried to compile a test routine written in F90 (or newer?) that included dimensions.h, but the compiler didn't like the fixed-form comments being included into the free-form *.f90 source file, so I changed all comments from c to !. Then I was able to compile my test program successfully. Let's call it test.f90:
program test
implicit none
include 'variables.h'
real*8, dimension(height_dim,length_dim,width_dim) :: vx, vy, vz
! <<Initialize data...>>
! << Output data...>>
end program test
Unfortunately, now the original code doesn't compile. It seems that code doesn't like commented lines to begin with ! (based on the fact that that was all I changed), but the actual errors it gives are the following:
variables.h(8): error #5082: Syntax error, found END-OF-STATEMENT when expecting one of: =
PARAMETER (nmodes = 4, styleFlag = 3)
------------------------------------------^
variables.h(5): error #6219: This variable, used in a specification expression, must be a dummy argument, a COMMON block object, or an object accessible through host or use association. [NMODES]
INTEGER height_dim, length_dim, width_dim, nmodes, styleFlag
---------------------------------------------^
What in the world is going on, and how can it be fixed?!
I imagine that comment styles are incompatible (free-form fortran thinks c is a variable, not a comment?), but I have no idea how it would produce these errors.
I am trying to convert a Rainflow cycle counting algorithm which is in Fortran, which is a language I am not familiar with, into Matlab.
There is a ready made Rainflow I've downloaded for Matlab but that does not fit the requirements of my project so I'm trying to build one from scratch.
Here is the Fortran code:
INTEGER BUFFER (4096), INDEX, VALUE, RANGE, MEAN, X, Y
INDEX = 0
10 CONTINUE
call 'get next peak/valley', VALUE
INDEX = INDEX + 1
BUFFER (INDEX) = VALUE
20 CONTINUE
IF (INDEX.LT.3) THEN
not enough points to form a cycle
GOTO 10
ELSE
X = ABS (BUFFER(INDEX) - BUFFER(INDEX - 1))
Y = ABS (BUFFER(INDEX - 1) - BUFFER(INDEX - 2))
IF (X.GE.Y) THEN
c -- cycle has been closed
RANGE = Y
MEAN = (BUFFER(INDEX-1) + BUFFER(INDEX-2))/2
c -- remove the cycle
INDEX = INDEX - 2
BUFFER(INDEX) = BUFFER(INDEX+2)
c -- see if this value closes any more cycles
GOTO 20
ELSE
GOTO 10
END IF
END IF
I had downloaded f2matlab (a Fortran to Matlab converter) but it requires a Fortran compiler which I do not have.
The bits I don't really understand how I can convert are:
The call 'get next… line (is this an input()?)
The BUFFER(4096) etc (is this a bit large to be a matrix in matlab?)
The GOTO/CONTINUE structure.
What do they mean, in English (or Matlab)?
I have seen
How to translate fortran goto state to matlab
and
translating loop from Fortran to MATLAB
but they do not help me very much.
This
call 'get next peak/valley', VALUE
isn't (currently) syntactically valid Fortran and I'm not sure whether any compiler of yore would have understood it either. I guess that it means get a VALUE for use in the following bits of code.
INTEGER BUFFER (4096)
is a simple declaration that BUFFER is a vector of 4096 integers, nothing to scare Matlab in that volume of data.
Finally, GOTO is an unconditional jump and the number following it is the label of the line to jump to, so GOTO 10 means execute the line with label 10 next. It was fairly common in FORTRAN of the vintage you are showing us to jump to a CONTINUE statement which is, in this context, a no-operation, execution continues to the next line.
In another context, with DO loops CONTINUE would have marked the end of the block of code inside the scope of the loop and would have a subtly different effect.
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.)
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.