I'm working on a computational project for a class at university and I've come across the following error message:
<undefined reference to 'tridag_'>
when trying to call the tridag subroutine to solve a tridiagonal linear set of equations. Isn't the tridag subroutine included in fortran's library? I'm using code blocks with the gnu fortran compiler. Here's the program:
double precision t0, tc, rc, dt, dx, al, time, alpha
double precision, allocatable :: tmp(:), tmpnew(:), aw(:), ap(:), ae(:), su(:), x(:)
open (1,file='input.txt'); open (2,file='output.txt')
read (1,*) ni, nt
read (1,*) t0, tc, dt, al, alpha
allocate (tmp(ni), tmpnew(ni), aw(ni), ap(ni), ae(ni), su(ni), x(ni))
dx = al/float(ni-1)
do i=1,ni
x(i)=(i-1)*dx
end do
rc=alpha*dt/dx**2
!ΑΡΧΙΚΗ ΣΥΝΘΗΚΗ
tmp=t0
do it=1,nt
time=(it-1)*dt
ap(1)=1; ae(1)=-1; su(1)=0
ap(ni)=1; aw(ni)=0; su(ni)=tc
do i=2,ni-1
ap(i)=-(1+2*rc); ae(i)=rc; aw(i)=rc; su(i)=-tmp(i)
end do
call tridag (aw, ap, ae, su, tmpnew, ni)
do i=1,ni
tmp(i)=tmpnew(i)
write(2,*) x(i),tmp(i)
end do
end do
Thanks in advance.
tridag() is not a Fortran intrinsic subroutine, it comes from an external library. You must get this library, compile the subroutine and link it with your code.
As far a I know TRIDAG comes from the Numerical Recipes book so be aware it is commercially licensed. If you get the book and the associated source code, you can use it according to the book's license. You can certainly also find the code on the internet (just use your favourite search engine to search for "TRIDAG Fortran subroutine"), but the license still applies.
Also note that there are many other tridiagonal matrix solvers available, but this site is not for locating or recommending external software libraries.
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'm sorry if this has been asked before, but I couldn't find an answer that seemed to work for me. I'm working with an old program, but have made a few modifications to it.
I can include the whole 2500 line program, but it seems like that is a lot.
I've successfully compiled the a program, but it fails when I try and run it. I'm getting a "Fortran runtime error: End of file" at the line which reads the .dat file. I've tried to compile a test segment, using the same .dat file and same variables. It results in the same problem.
PROGRAM OPEN
INTEGER (KIND=1), PARAMETER :: dy=3 ! number of income states
INTEGER (KIND=2) :: OpenStatus
REAL, DIMENSION(dy) :: grid,wt
OPEN(1,file='cygdrive/user/mk.dat',status='old',form='formatted',IOSTAT=OpenStatus)
READ (1,*) grid, wt
IF(OpenStatus>0) STOP 'cannot open mk.dat'
CLOSE(1)
PRINT*, grid(1)
END PROGRAM
The data file referenced is:
-1.7320508e+000
0.0000000e+000
1.7320508e+000
4.1777138e-001
1.6710855e+000
4.1777138e-001
Where each of these numbers is on its own line and preceeded by a space
This generates the same end of file runtime error. I'd really appreciate any help here.
I should add that I compiled with gfortran.
EDIT:
As per High Performance Mark's suggestion below, I've modified it to include an inquire test.
PROGRAM TEST
CHARACTER :: fnm, seq, fort
Logical :: lex
INTEGER (KIND=1), PARAMETER :: dy=3 ! number of income states
INTEGER (KIND=2) :: j,j0,j1,j2,j4,j5,j6,j7,k,jjj,jj,dyy,OpenStatus
REAL, DIMENSION(dy) :: grid,wt
OPEN(1,file='cygdrive/user/mk.dat',status='old',form='formatted',IOSTAT=OpenStatus)
INQUIRE (1, EXIST=lex, NAME=fnm, SEQUENTIAL=seq, FORMATTED=fort)
PRINT*, 'Exists=',lex, ' Name=',fnm, ' Sequential=', seq, 'Formatted=', fort
READ (1,*) grid, wt
IF(OpenStatus>0) STOP 'cannot open mk.dat'
CLOSE(1)
PRINT*, grid(1)
END PROGRAM
The results of the inquire statement are:
Exists= T Name= Sequential=U Formatted=U
My understanding is that the File is found (i.e. exists is returned as true), is un-named and the format and sequential access are returned as unknown (as is direct which I included later). I've also checked the delimiter and padding which are coming back as unknown.
My beginner intuition is telling me that I should try and create a new data file by writing to it with a fortran program and that should solve the problem? Is that correct? If so is there a fundamental misunderstanding at play here i.e. is this a problem with data files from other sources?
Thanks for all your patience.
(Answered in the comments. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
I've fixed this problem-the program was looking for it in a different place, I've now corrected that.
I've recently started working on an existing Fortran program, and picking up the language at the same time. I wrote the following subroutine:
subroutine timing(yyyy, mm, dd, var, ntime, time_blocks,
* time_day)
use myglobals
! ---------------------------------------------------------------------
! Common Variables
! ---------------------------------------------------------------------
integer yyyy, ! year
* mm, ! month
* dd, ! day
* ntime ! nr of blocks for which time was measured
real time_blocks(ntime),
* time_day
character*4 var
! ---------------------------------------------------------------------
! Internal Variables
! ---------------------------------------------------------------------
integer ios
integer out_unit=52
open(unit=out_unit, file=diroutput(1:69)//'timing',
* err=450, iostat=ios)
450 print*, "iostat= ", iostat
print*, "open"
write(out_unit, format_str) yyyy, mm, dd, var, time_blocks,
* time_day
return
end
The purpose of this subroutine is to write the inputs it gets from another part of the program to a file, following a defined format (format definition not included in my example). The file must be created on the first call of this subroutine, then accessed on each further call in order to append the new information. diroutput is a character string defined in myglobals.
My problem is that the program seems to get hung up at the OPEN statement, i.e. nothing happens until I kill the process. I ran the code with several print*, statements to locate the error, and found out this way that the error must be in the OPEN statement. It seems strange that the program does nothing at all, not even jump to the error label.
As I'm new to Fortran I might be missing something fairly obvious, so a quick look by someone more experienced might help. I'm certain that diroutput contains a valid path.
I'm using Linux (CentOS 5.5) and I compiled my program with Intel Fortran Compiler 11.1.
Your code seems, from the continuation characters in (generally) column 6, to be written in fixed-form despite containing features of Fortran 90. If it is fixed-form then statement labels, such as 450 should be in columns 1 to 5. I don't immediately see why that would cause the program to hang rather than crash, but I suggest you fix this and try again.
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 need to develop a library that opens a file and parses the stuff.
The unit number, due to fortran IO style, must be decided by me, but I can't know what other units are open in the client code. Is there a standard function like give_me_any_unit_number_that_is_free() ?
In fortran 2008, there's a newunit clause to open that you can use
integer :: myunit
..
open(newunit=myunit,file='file.dat')
...
close(myunit)
but that's new enough that not all compilers support it yet. If yours doesn't yet, you can mock one up yourself; there's a good example on the fortran wiki.
You can use INQUIRE to find a unit number that is not in use:
integer*4 function get_file_unit (lu_max)
!
! get_file_unit returns a unit number that is not in use
integer*4 lu_max, lu, m, iostat
logical opened
!
m = lu_max ; if (m < 1) m = 97
do lu = m,1,-1
inquire (unit=lu, opened=opened, iostat=iostat)
if (iostat.ne.0) cycle
if (.not.opened) exit
end do
!
get_file_unit = lu
return
end function get_file_unit