`undefined reference to `sgtsv_'` when compiling a tridiagonal solver - fortran

I'm working on a problem for one of my computing courses and I was getting stuck in an error after trying to work through it for some time.
Specifically, I'm working on this Fortran routine:
program linsolve_sgtsv
implicit none
integer, parameter :: lda=10,ldb=lda,nrhs=1
real :: dl(lda-1),d(lda),du(lda-1),b(ldb,nrhs)
integer :: n,info
n = 10
dl(1:n-1) = 1+(1/11); d(1:n) = 2; du(1:n-1) = 1-(1/11)
b(1:n,1) = (/1,1,1,1,1,1,1,1,1,1/)
call sgtsv(n,nrhs,dl,d,du,b,ldb,info)
if (info==0) then
print *, '--- x is ----------------'
print '(g22.16)', b(1:n,1)
else
print *, 'info = ',info
end if
end program linsolve_sgtsv
This routine solves linear systems using gaussian elimination when the matrices are tridiagonal.
However, when I try to create a makefile using:
gfortran linsolve_sgtsv.f90 -o linsolve.f90
I get the following errors:
tmp/ccCiRHve.o: In function `MAIN__':
linsolve_sgtsv.f90:(.text+0x100): undefined reference to `sgtsv_'
collect2: error: ld returned 1 exit status

I can't find any direct duplicate. Many people have similar problems, but they usually know a bit more and your question is more basic.
The tridiagonal solver sgtsv() is not a Fortran built-in subroutine. It is provided in a library called LAPACK and many other implementations (e.g., MKL). That means you must tell the compiler it should include (link) that library.
That is done by adding -llapack (-l means "link" and lapack is the name of the library which is stored in a file called liblapack.so or liblapack.a) at the end of the compile command.
You must have his library installed in your computer for the link flag to work. In many Linux distributions it is included in the repositories.
The correct line would be
gfortran linsolve_sgtsv.f90 -o linsolve -llapack
If the library (liblapack.a or liblapack.so) is installed in some directory the compiler can't see, you must use
gfortran linsolve_sgtsv.f90 -o linsolve -L/path/to/liblapack -llapack
See also gfortran LAPACK "undefined reference" error and your compiler's manual for more.
Notice I did -o linsolve instead of -o linsolve.f90. It is a very bad idea to name your executable file with a .f90 extension. Only Fortran source files should be named .f90.
There will be more problems in your code. They will appear when you start to test it. For example, yoy do
(1/11)
this will not return the mathematical result of 1.0/11.0, it will return 0. Learn about integer division in Fortran. See Why are the elements of an array formatted as zeros when they are multiplied by 1/2 or 1/3? and Simple program, that only produce zeros, bug?
If you will find more problems after you can run your code, you can ask a new question.
I recommend to compile your programs with -g -fcheck=all -Wall to find more bugs and errors more easily.

Related

How to link to -llapack when compiling fortran using gfortran

I am trying to compile a simple program, example.f90
program example
use dsyevd
implicit none
end program example
I am compiling using the command
gfortran -llapack example.f90
However this compilation returns the message
Fatal Error: Cannot open module file 'dsyevd.mod' for reading at (1): No such file or directory
dsyevd is a function in lapack. I have installed intel-mkl onto my computer which contains lapack95. Any advice on how to link my compile to these libraries would be hugely appreciated.
Thank you!
Lapack does not contain any module called dsyevd, so you cannot use it. It is a subroutine so it should be called.
Using -llapack is OK for the default Lapack pre-installed in your Linux distribution. For MKL you must use the appropriate options from the Link Advisor.

Linking legacy Fortran95 library to C++ with ifort/icpc

Background: I am in a situation where I have to make use of an old Fortran95 library in a new C++ project. The F95 library is extensive, has tons of small modules, poorly documented, and it was mostly auto-generated about a decade ago by some obscure computer algebra system (by people on a different continent). So basically heirloom code, but it works and it is currently irreplaceable.
Luckily I have the source code and it can be compiled with current versions of ifort, but I am not too familiar with Fortran, and would rather not touch the old code in any significant way.
So suppose I have this Fortran code (pes_shell.f90):
subroutine pes_init()
use pes,wp=>pes_wp
implicit none
real,parameter::auang=0.5291772083
call pes0_init (dir='coef')
call pes1_init (pes_x3y1z1u1_sysall)
return
end subroutine pes_init
The functions pes0_init(...) and pes1_init(...) lead into the abyssal depths of the Fortran library and they are contained in the pes module.
I can compile this to an object file, if I give ifort the path for the modules:
ifort -r8 -O2 -c pes_shell.f90 -I/home/debianuser/PES/PES_library/lib/mod
My POC C++ code, calling pes_init():
extern "C"{
void pes_init_();
}
int main(){
pes_init_();
return 0;
}
This can also be compiled to an object file, with icpc:
icpc -c PEStest.cpp
However I cannot figure out how to link the two object files, plus the truckload of fortran modules, into a final executable.
I have tried just simply using icpc, but it cannot seem to be able to find the Fortran functions, even if I give it the location of the module files:
icpc -I/home/debianuser/PES/PES_library/lib/mod -o test.x pes_shell.o PEStest.o
pes_shell.o: In function `pes_shell_mp_f_':
pes_shell.f90:(.text+0x595): undefined reference to `pes_x3y1z1u1_mp_pes_x3y1z1u1_pot_'
pes_shell.o: In function `pes_shell_mp_pes_init_':
pes_shell.f90:(.text+0x5f0): undefined reference to `pes0_mp_pes0_init_'
pes_shell.f90:(.text+0x603): undefined reference to `pes1c_mp_pes1_init_'
PEStest.o: In function `main':
PEStest.cpp:(.text+0x2b): undefined reference to `pes_init_'
EDIT:
Pointing the linker to the directory where libpes.a can be found eliminates the problem of locating the function that was referenced in the c++ code, but icpc still cannot find the fortran functions that are being called from fortran codes:
icpc -I/home/debianuser/PES/PES_library/lib/mod -L/home/debianuser/PES/PES_library/lib/pes-xyz -lpes -o test.x PEStest.o pes_shell_new.o
pes_shell_new.o: In function `f_':
pes_shell_new.f90:(.text+0x585): undefined reference to `pes_x3y1z1u1_mp_pes_x3y1z1u1_pot_'
pes_shell_new.o: In function `pes_init_':
pes_shell_new.f90:(.text+0x5e0): undefined reference to `pes0_mp_pes0_init_'
pes_shell_new.f90:(.text+0x5f3): undefined reference to `pes1c_mp_pes1_init_'

use fftw3 with gfortran included in TDM-GCC-64

I am trying to use FFTW3 with gfortran compiler included in the TDM-GCC-64 suite in Windows 7 platform.
I have downloaded "fftw-3.3.4-dll64.zip" from the following page:
http://www.fftw.org/install/windows.html
I also created a fortran module described in the following tutorial:
http://www.fftw.org/doc/Defining-an-FFTW-module.html
Now, I tried to compile the following fortrans program
program test
use FFTW3
implicit none
integer N
parameter(N=4)
integer*8 plan
double complex in, out
dimension in(N),out(N)
integer i
write(*,*) 'Input array:'
do i = 1,N,1
in(i) = dcmplx(float(i),float(i+1))
write(*,*) ' in(',i,') = ',in(i)
enddo
call dfftw_plan_dft_1d ( plan, N, in, out, FFTW_FORWARD, FFTW_ESTIMATE )
call dfftw_execute ( plan )
write(*,*) 'Output array:'
do i = 1,N,1
write(*,*) ' out(',i,') = ',out(i)
enddo
call dfftw_destroy_plan ( plan )
call dfftw_plan_dft_1d ( plan, N, out, in, FFTW_FORWARD, FFTW_ESTIMATE )
call dfftw_execute ( plan )
write(*,*) 'Output array after inverse FFT:'
do i = 1,N,1
write(*,*) ' ',N,' * in(',i,') = ',in(i)
enddo
call dfftw_destroy_plan ( plan )
end
I tried to compile but the a couple of error messages popped up:
undefined reference to dfftw_plan_dft_1d_'
undefined reference todfftw_execute_'
...
The command used for compiling is:
gfortran test.f90 -ffree-form -o test_fftw.exe
I searched web up and down but have not found solutions to the problem. Could anyone help me out here? Thanks!!!
You need to learn the basics of compiling and linking programs with GCC
This tutorial refers to the GCC C compiler, gcc, and C++ compiler, g++, but the principles are the same for the Fortran compiler, gfortran.
Your program invokes functions, like dfftw_plan_dft_1d that are not defined
in your code but in the fftw3 library. You must therefore tell gfortran
to link that library with your program, after it is compiled, or no definitions will be found for
these functions, and no executable can be built. Instead, the linkage
fails with the undefined reference errors you are seeing.
The command you have used to build the program:
gfortran test.f90 -ffree-form -o test_fftw.exe
attempts to compile and link the program in a single command. You can do this,
if you clearly understand the underlying separate processes of compiling
and linking. If you don't, you should follow the better practice of compiling first,
and if that is successful, then linking. The command you have used fails in
its linkage step because it does not tell the linker that the fftw3 library
is needed (or where to find it) so no attempt is made to find it and link it.
You say you have downloaded fftw-3.3.4-dll64.zip. I presume you have
unzipped it somewhere on your system. Let's say you unzipped it to C:\fftw-3.3.4-dll64.
You also say you have made an FFTW3 module that apparently is being used successfully.
In that case, compile your program first:
gfortran -c -o test.o test.f90
generating an object file test.o. The option -c tells gfortran to compile only,
and not to link. (You do not need to specify --ffree-form: the file-extension .f90
implies it).
Next, link the object file test.o with the fttw3 library, to make an executable
program test_fftw.exe:
gfortran -o test_fftw.exe test.o -LC:\fftw-3.3.4-dll64 -lfftw3-3
The linker option:
-LC:\fftw-3.3.4-dll64
tells the linker to add C:\fftw-3.3.4-dll64 (where you unzipped the package)
to the list of directories that it will search to find libraries. And the linker option -lfftw3-3 tells it to link a library that matches the identifier fftw3-3. On Windows, the GCC linker
will match a library identfier name with any of the filenames:
libname.a (a static library)
libname.dll (a shared library)
name.dll (also a shared library)
name.lib (an import library for a shared library)
So, since C:\fftw-3.3.4-dll64 contains the shared library libfftw3-3.dll,
the linker will identify it in that directory as the one required by -lfftw3-3
Now, unless there are other problems, you program will link and you will have
an executable test_fftw.exe, which will run, provided that libfftw3-3.dll
can be found, at runtime, in one of the places where the Windows
loader will look for it.

How to link a fortran subroutine to a cpp main program?

I am trying to compile a c++ program but it does not work.
At first, I should said that c++ is not a language I really know, I use Fortran. Anyway, the main c++ program calls a fortran subroutine. I can compile this subroutine but when I want to compile the c++ program there is a link error.
The real program is just huge and calls many subroutines, so I did a simple test (simple program calling just one subroutine) and it does not work as well! When I want to create the .x my subroutine is undefined.
Here is the c++ program (test-TQINIT.cpp) and the subroutine (TQINIT.f).
test-TQINIT.cpp:
#include <iostream>
using namespace std;
extern "C"
{
void TQINIT_(int*, int*);
}
main()
{ int NWG;
NWG=80000;
int *IWSG = new int[NWG];
TQINIT_(IWSG,&NWG);
}
TQINIT.f:
SUBROUTINE TQINIT(IWSG,NWG)
IMPLICIT NONE
INTEGER NWG
INTEGER IWSG(NWG)
LOGICAL TQG2ERR
INTEGER IERR
CALL TQRSERR
CALL TQINI(NWG,IWSG)
IF (TQG2ERR(IERR)) THEN
WRITE(6,*)'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
WRITE(6,*)'ERROR INITIALIZING (TQINIT) !!!'
WRITE(6,*)'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
STOP
ENDIF
END
I compile the subroutine using:
gfortran -c TQINIT.f
and get a TQINIT.o
Then I compile the main program using:
g++ -c test-TQINIT.cpp
and get a test-TQINIT.o.
Then for create the .x I use (TQINIT.f need the library):
g++ test-TQINIT.o TQINIT.o -L/usr/local/thermocalc/3.0/SDK/TQ8 -ltq-linux-x86_64-gfortran44-8 -o test-TQINIT.x
This is what I get:
test-TQINIT.o: In function `main':
test-TQINIT.cpp:(.text+0x33): undefined reference to `TQINIT_'
collect2: ld returned 1 exit status
I hope someone will be able to help me.
Thank you by advance.
Fortran is case-insensitive, and the name of the subroutines in the object file normally (as far as I know) end up in lowercase with an underscore, so yours is probably named tqinit_ and not TQINIT_, so your call in C++ should be lowercase.
You can verify the name in the object file with the objdump -t TQINIT.o command given in Alexander Vogt's answer, or the simpler nm TQINIT.o which is more terse.
If you use the ISO_C_BINDING in Fortran, you will have control over the precise routine names that will be seen by the other language, e.g., case, no underscores. You can also declare the arguments so that consistency with C/C++ will be guaranteed. For more on this topic, see https://stackoverflow.com/questions/tagged/fortran-iso-c-binding
I'm no expert on mixing C and Fortran, but I have always linked the Fortran Code directly using
g++ test-TQINIT.cpp TQINIT.o -L/usr/local/thermocalc/3.0/SDK/TQ8 \
-ltq-linux-x86_64-gfortran44-8 -o test-TQINIT.x
instead of compiling those two files separately and then linking... Perhaps this helps (although your way should work as well, at least it does with the simple example I tried).
BTW:
You can find out how the functions in your Fortran object are called by issuing
objdump -t TQINIT.o
This should give you the correct function name to call.

g++ crashes after consecutive calls

I'm developing a tool that makes several calls to g++ in order to compile and execute different versions of an original C++ program. More or less, this calls are made inside a loop (it's not a real "loop", but the calls are made inside a function that is part of an iterative algorithm).
For short executions of the algorithm, everything goes smoothly and all the g++ calls finish correctly (I get the binaries ready for its execution). However, the longer the algorithm executions are, the more likely the tool is to crash.
This crashes are due to subsequents crashes of g++ calls, that instead of returning the expected compiled binaries, return the following errors:
Compiling test_oclopts_23_14_6_6000.cpp...
g++ -O3 -L/opt/AMDAPP/lib -lOpenCL -I/opt/AMDAPP/include test_oclopts_23_14_6_6000.cpp -o test_oclopts_23_14_6_6000
/usr/bin/ld: cannot find /usr/lib/i386-linux-gnu/libc_nonshared.a
/usr/bin/ld: cannot find /lib/i386-linux-gnu/ld-linux.so.2
collect2: ld returned 1 exit status
I've googled a bit about this error messages, but I don't understand how they can be applied to my problem: the supposedly missing libraries really are well installed in the system (former g++ calls went OK).
Any idea? Moreover, if you know a better way to compile C++ source code inside a C++ program instead of using system(), I'm accepting suggestions, :)
Thank you so much in advance,
Jorge.