Gfortran compilation fails with plplot graphics library.
FYI: Plplot is a graphics library with which one can plot directly from gfortran (among other languages).
I have installed the following packages (on Xubuntu 18.04)
sudo apt install gfortran libplplot15 libplplot-dev libplplotfortran0 plplot-driver-cairo plplot-driver-qt plplot-driver-wxwidgets plplot-driver-xwin plplot-doc
I updated the local database with the following command: sudo updatedb. When I ran the command locate plplot I get the following relevant lines (along with other lines)
/usr/lib/x86_64-linux-gnu/pkgconfig/plplot-fortran.pc
/usr/lib/x86_64-linux-gnu/pkgconfig/plplot.pc
Then I tried to compile the fortran example code given here (relevant part is given below)
program x00f
use plfortrandemolib
integer, parameter :: NSIZE = 101
real(kind=pl_test_flt), dimension(NSIZE) :: x, y
real(kind=pl_test_flt) :: xmin = 0._pl_test_flt, xmax = 1._pl_test_flt, ymin = 0._pl_test_flt, ymax = 100._pl_test_flt
! integer :: i
integer :: plparseopts_rc
! Prepare data to be plotted.
x = arange(NSIZE) / real(NSIZE-1,pl_test_flt)
y = ymax * x**2
! Or alternatively, using a DO-loop
!do i = 1,NSIZE
! x(i) = real( i - 1, pl_test_flt ) / real( NSIZE - 1, pl_test_flt )
! y(i) = ymax * x(i)**2
!enddo
! Parse and process command line arguments
plparseopts_rc = plparseopts( PL_PARSE_FULL )
if(plparseopts_rc .ne. 0) stop "plparseopts error"
! Initialize plplot
call plinit
! Create a labelled box to hold the plot.
call plenv( xmin, xmax, ymin, ymax, 0, 0 )
call pllab( "x", "y=100 x#u2#d", "Simple PLplot demo of a 2D line plot" )
! Plot the data that was prepared above.
call plline( x, y )
! Close PLplot library
call plend
end program x00f
with the following command
gfortran x00f.f90 $(pkg-config --cflags --libs plplot-fortran)
The output of pkg-config --cflags --libs plplot-fortran is
-I/usr/include/plplot -I/usr/lib/x86_64-linux-gnu/fortran/modules/plplot -I/usr/include/plplot -lplplotfortran
The error that I get is the following:
/tmp/ccAQ0C7A.o: In function `MAIN__':
x00f.f90:(.text+0x65): undefined reference to `__plfortrandemolib_MOD_arange_1'
collect2: error: ld returned 1 exit status
Do I need to install any other packages or is the compilation command is incomplete? Any help will be appreciated.
Answering my own question for future SO users.
The correct compilation command for the above code is
gfortran x00f.f90 -lplfortrandemolib $(pkg-config --cflags --libs plplot-fortran)
Also check VladimirF's comment on the same.
Related
I have the following problem. I have a file say file.f90 in which I have implemented some Fortran subroutines, say called foo. Then I compile these functions using "R CMD SHLIB file.f90". To use the function foo in a seperate R file I then use dyn.load("foo.dll") and to call it .Fortran("foo", ...).
So far so good. But now I need to use some functions implemented in Lapack.
I have no idea how to do this or where to have a look. I have only tried calling "R CMD SHLIB file.f90 -llapack" but already there I get an error that llapack has not been found. Any hints would be greatly appreciated!!
EDIT:
I have finally found an answer to my question with the help of everyone here and with looking up much on the internet. I have to say the solution is quite easy but as I am quite a noob when it comes to these things it still took some time. So here s my solution for Windows 11 and R studio 4.1
Assume that our R session/project has the path PATH_PROJ, e.g "C:\Users\Myname\Documents\MyProject". Then I created a new folder named "f90files" in which I intended to save all Fortran functions, so PATH_PROJ\f90files.
Next, I needed the path of my R's Lapack PATH_LAPACK, e.g "C:\Program Files\R\R-4.1.2\bin\x64\Rlapack.dll".
In PATH_PROJ\f90files I then implemented the Fortran subroutine as suggested by Jean-Claude Arbaut:
subroutine eigvals(n, a, vre, vim, info)
implicit none
integer :: n, info
integer, parameter :: lwork = 65536
double precision :: a(n, n), vre(n), vim(n)
double precision, save :: work(lwork)
call dgeev("n", "n", n, a, n, vre, vim, 0d0, 1, 0d0, 1, work, lwork, info)
end subroutine
Following this, I started up Windows command prompt and typed
gfortran -shared PATH_LAPACK PATH_PROJ\f90files\eigvals.f90 -o PATH_PROJ\f90files\eigvals.so -o PATH_PROJ\f90files\eigvals.dll
and further
gfortran -shared PATH_LAPACK PATH_PROJ\f90files\eigvals.f90 -o PATH_PROJ\f90files\eigvals.so
(maybe this can be done in one go?)
With this all was nicely compiled. In R I then loaded the function using
dyn.load("PATH_PROJ\f90files\eigvals.dll")
Finally, using the implementation given below, I ran
eigvals <- function(a) {
if (is.matrix(a) && is.double(a) && nrow(a) == ncol(a)) {
n <- nrow(a)
s <- .Fortran("eigvals", n = as.integer(n), a = a, vre = double(n), vim = double(n), info = 0L)
structure(complex(real = s$vre, imaginary = s$vim), info = s$info)
} else stop("Invalid input")
}
eigvals(a)
and voilà we are done! Thanks again to everyone!
The libraries you are looking for are Rblas.dll and Rlapack.dll in the R-4.2.2\bin\x64 directory (replace 4.2.2 with your version).
Here is an example. Let's compute eigenvalues using LAPACK's dgeev.
Fortran file eigvals.f90. Here to simplify lwork is a constant, but in "real" code you would have to do this more carefully.
subroutine eigvals(n, a, vre, vim, info)
implicit none
integer :: n, info
integer, parameter :: lwork = 65536
double precision :: a(n, n), vre(n), vim(n)
double precision, save :: work(lwork)
call dgeev("n", "n", n, a, n, vre, vim, 0d0, 1, 0d0, 1, work, lwork, info)
end subroutine
Compile with either one of the following commands (change the path as necessary). If you are on Windows, do this from the Rtools bash window. On Linux the extension is .so and not .dll.
gfortran -shared -L/c/App/R/R-4.2.2/bin/x64 eigvals.f90 -lRlapack -o eigvals.dll
R CMD SHLIB eigvals.f90 -lRlapack
In R, you can now do, assuming you have setwd() to the directory containing the DLL:
a <- matrix(c(2, 9, 4, 7, 5, 3, 6, 1, 8), 3, 3, byrow = T)
dyn.load("eigvals.dll")
is.loaded("eigvals")
eigvals <- function(a) {
if (is.matrix(a) && is.double(a) && nrow(a) == ncol(a)) {
n <- nrow(a)
s <- .Fortran("eigvals", n = as.integer(n), a = a, vre = double(n), vim = double(n), info = 0L)
structure(complex(real = s$vre, imaginary = s$vim), info = s$info)
} else stop("Invalid input")
}
eigvals(a)
Pay attention to the number and type of subroutine arguments in .Fortran, otherwise you may crash R. Note also that you must call Fortran subroutines, not functions.
I you are on Windows and using R-4.2 with Rtools 4.2, there is an extra trick: the compiler is no longer in the default directories. See this. You have first to do, in the Rtools bash window:
export PATH=/x86_64-w64-mingw32.static.posix/bin:$PATH
If you are using the compiler from the Windows command prompt, you will have to modify the PATH environment variable accordingly.
I have a very specific error so googling wasn't helpful and I'm sorry I don't know how to provide a simple producible example for this issue. The code below runs perfectly on my local machine but on the HPC it is producing this error:
*** caught segfault ***
address 0x2ad718ba0440, cause 'memory not mapped'
Traceback:
1: array(.Fortran("hus_vertical_interpolation", m = as.integer(DIM[1]), n = as.integer(DIM[2]), o = as.integer(DIM[3]), p = as.integer(DIM[4]), req = as.integer(length(req_press_levels)), hus_on_model_level = as.numeric(spec_hum_data[]), pres = as.numeric(req_press_levels), pressure_full_level = as.numeric(pressure[]), hus_on_press_level = as.numeric(output_array[]))$hus_on_press_level, dim = output_DIM)
2: Specific_humidity_afterburner(spec_hum_file = q_nc.files[x], req_press_levels = required_PLev)
Possible actions:
1: abort (with core dump, if enabled)
2: normal R exit
3: exit R without saving workspace
4: exit R saving workspace
Selection:
The code is supposed to:
Loop over a vector of NetCDF files and pass the filename spec_hum_file to function Specific_humidity_afterburner.
The function reads the NetCDF file, extract data pass to the first compiled subroutine, do the math and return the values.
Take the result, pass it to another FORTRAN subroutine and return the second result.
Write the second result to a new NetCDF file.
The error occurs in step 3. The R function is:
Specific_humidity_afterburner<-function(spec_hum_file,req_press_levels){
require(ff)
require(ncdf4)
require(stringi)
require(DescTools)
library(stringr)
library(magrittr)
#1============================================================================
#Reading data from netCDF file
#2============================================================================
#Reading other variables
#3============================================================================
# First Fortran subroutine
#4============================================================================
#load vertical interpolate subroutine for specific humidity
dyn.load("spec_hum_afterburner/vintp2p_afterburner_hus.so")
#check
is.loaded("hus_vertical_interpolation")
DIM<-dim(spec_hum_data)
output_DIM<-c(DIM[1],DIM[2],length(req_press_levels),DIM[4])
output_array<-ff(array(0.00,dim =output_DIM),dim =output_DIM)
result<- array(.Fortran("hus_vertical_interpolation",
m=as.integer(DIM[1]),
n=as.integer(DIM[2]),
o=as.integer(DIM[3]),
p=as.integer(DIM[4]),
req = as.integer(length(req_press_levels)),
pres=as.numeric(req_press_levels),
pressure_full_level=as.numeric(pressure[]),
hus_on_model_level=as.numeric(spec_hum_data[]),
hus_on_press_level=as.numeric(output_array[]))$hus_on_press_level,
dim =output_DIM)
DIMNAMES<-dimnames(spec_hum_data)
DIMNAMES[["lev"]]<-req_press_levels
Specific_humidity<- ff(result, dim = output_DIM,
dimnames =DIMNAMES )
rm(result)
#5============================================================================
# Writing NetCDF file of the interpolated values
}
Fortran subroutine:
subroutine hus_vertical_interpolation(m,n,o,p,req,pres, &
pressure_full_level,hus_on_model_level,hus_on_press_level)
implicit none
integer :: m,n,o,p,req
integer :: x,y,s,t,plev
double precision :: pres(req),hus_on_model_level(m,n,o,p)
double precision :: pressure_full_level(m,n,o,p)
double precision :: delta_hus,delta_p,grad_hus_p,diff_p
double precision, intent(out) :: hus_on_press_level(m,n,req,p)
real :: arg = -1.0,NaN
NaN= sqrt(arg)
do plev=1,req
do t=1,p
do x=1,m
do y=1,n
do s=1,o
!above uppest level
if(pres(plev) .LT. pressure_full_level(x,y,1,t)) then
hus_on_press_level(x,y,plev,t) = NaN
end if
! in between levels
if(pres(plev) .GE. pressure_full_level(x,y,s,t) .AND. pres(plev) .LE. &
pressure_full_level(x,y,s+1,t) ) then
delta_hus = hus_on_model_level(x,y,s,t) - hus_on_model_level(x,y,s+1,t)
delta_p = log(pressure_full_level(x,y,s,t))&
- log(pressure_full_level(x,y,s+1,t))
grad_hus_p = delta_hus /delta_p
diff_p = log(pres(plev)) - log(pressure_full_level(x,y,s,t))
hus_on_press_level(x,y,plev,t) = hus_on_model_level(x,y,s,t)&
+ grad_hus_p * diff_p
end if
! extrapolation below the ground
if(pres(plev) .GT. pressure_full_level(x,y,o,t)) then
hus_on_press_level(x,y,plev,t) = hus_on_model_level(x,y,o,t)
end if
end do
end do
end do
end do
end do
end subroutine hus_vertical_interpolation
Fortran subroutine was compiled with:
gfortran -fPIC -shared -ffree-form vintp2p_afterburner_hus.f90 -o vintp2p_afterburner_hus.so
The error behaviour is unpredictable for example, can happen at index 1, 2, 8, .. etc of the loop. We have tried to hand over the big array to the Fortran subroutine as the last variable, it minimized the occurrence of the error.
Also, the NetCDF files have a size of ~2GB. Another point to mention, The modules are built with EasyBuild so conflicts are not probable as HPC support team stated. We have tried many solutions as far as we know and no progress!
I want to compute 1_299_709 ** 1_300_751 % 104_729 in Crystal.
In Python, the pow function allows to pass the modulo as third argument:
❯ python
>>> pow(1_299_709, 1_300_751, 104_729)
90827
In Ruby, the same:
❯ irb
irb(main):001:0> 1_299_709.pow(1_300_751, 104_729)
=> 90827
But in Crystal, there seems not to be such a functionality, and naturally, using ** operators quickly overflows:
❯ crystal eval "1_299_709 ** 1_300_751 % 104_729"
Unhandled exception: Arithmetic overflow (OverflowError)
from /usr/lib/crystal/int.cr:0:9 in '**'
from /eval:1:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:97:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:86:7 in 'main'
from /usr/lib/crystal/crystal/main.cr:106:3 in 'main'
from __libc_start_main
from _start
from ???
How to compute a modular exponentiation in Crystal?
Edit: To clarify, I'm already using BigInt but that doesn't work. I removed BigInt from my minimal working example for simplicity.
The following Python code contains the actual numbers from my program:
>>> pow(53583115773616729421957814870755484980404298242901134400501331255090818409243, 28948022309329048855892746252171976963317496166410141009864396001977208667916, 115792089237316195423570985008687907853269984665640564039457584007908834671663)
75711134420273723792089656449854389054866833762486990555172221523628676983696
It executes easily and returns the correct result. Same for Ruby:
irb(main):001:0> 53583115773616729421957814870755484980404298242901134400501331255090818409243.pow(2894802230932904885589274625217197696331749616641014100986
4396001977208667916, 115792089237316195423570985008687907853269984665640564039457584007908834671663)
=> 75711134420273723792089656449854389054866833762486990555172221523628676983696
However, Crystal:
a = BigInt.new 53583115773616729421957814870755484980404298242901134400501331255090818409243
e = BigInt.new 28948022309329048855892746252171976963317496166410141009864396001977208667916
p = BigInt.new 115792089237316195423570985008687907853269984665640564039457584007908834671663
y = a ** e % p # overflows with and without BigInt
Is resulting in:
gmp: overflow in mpz type
Program received and didn't handle signal IOT (6)
How to compute such a massive modular exponentiation in Crystal?
Edit: Filed an issue to make sure it's not a bug: crystal-lang/crystal#8612
As stated in the Github issue, this can be easily circumvented by binding mpz_powm_sec from gmp:
This is pretty simple:
https://carc.in/#/r/89qh
require "big/big_int"
a = BigInt.new "53583115773616729421957814870755484980404298242901134400501331255090818409243"
e = BigInt.new "28948022309329048855892746252171976963317496166410141009864396001977208667916"
p = BigInt.new "115792089237316195423570985008687907853269984665640564039457584007908834671663"
#[Link("gmp")]
lib LibGMP
fun mpz_powm_sec = __gmpz_powm_sec(rop : MPZ*, base : MPZ*, exp : MPZ*, mod : MPZ*)
end
#y = a ** e % p
y = BigInt.new
LibGMP.mpz_powm_sec(y, a, e, p)
puts y
# > 75711134420273723792089656449854389054866833762486990555172221523628676983696
I'm trying to learn how to use fortran code inside R. I was able to follow this tutorial. Now, I'm trying to use that as a base plus this fortran program to calculate pi. I create a file Fpi.f90 with this code:
subroutine pi(avepi, DARTS, ROUNDS)
double precision, intent(out) :: avepi
integer, intent(in) :: DARTS, ROUNDS
integer :: MASTER, rank, i, n
integer, allocatable :: seed(:)
double precision :: pi_est, homepi, pirecv, pisum
! we set it to zero in the sequential run
rank = 0
! initialize the random number generator
! we make sure the seed is different for each task
call random_seed()
call random_seed(size = n)
allocate(seed(n))
seed = 12 + rank*11
call random_seed(put=seed(1:n))
deallocate(seed)
avepi = 0
do i = 0, ROUNDS-1
pi_est = dboard(DARTS)
! calculate the average value of pi over all iterations
avepi = ((avepi*i) + pi_est)/(i + 1)
end do
end subroutine pi
double precision function dboard(darts)
integer, intent(in) :: darts
double precision :: x_coord, y_coord
integer :: score, n
score = 0
do n = 1, darts
call random_number(x_coord)
call random_number(y_coord)
if ((x_coord**2 + y_coord**2) <= 1.0d0) then
score = score + 1
end if
end do
dboard = 4.0d0*score/darts
end function
When
$ R CMD SHLIB ./Fortran/Fpi.f90
gfortran -fpic -g -O2 -fstack-protector-strong -c Fortran/Fpi.f90 -o Fortran/Fpi.o
Fortran/Fpi.f90:22.15:
pi_est = dboard(DARTS)
1
Error: Return type mismatch of function 'dboard' at (1) (REAL(4)/REAL(8))
/usr/lib/R/etc/Makeconf:161: recipe for target 'Fortran/Fpi.o' failed
make: *** [Fortran/Fpi.o] Error 1
What am I doing wrong?
After adding double precision :: dboard to pi I get a different error.
R CMD SHLIB ./Fortran/Fpi.f90
gfortran -shared -L/usr/lib/R/lib -Wl,-z,relro -o Fortran/Fpi.so ./Fortran/Fpi.o -L/usr/lib/R/lib -lR
/usr/bin/ld: ./Fortran/Fpi.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
./Fortran/Fpi.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
/usr/share/R/share/make/shlib.mk:6: recipe for target 'Fortran/Fpi.so' failed
make: *** [Fortran/Fpi.so] Error 1
You do not use implicit none. That is very bad! Due to implicit typing dboard is thought to he default real inside pi.
Declare it as double precision, or if possible with R, use modules. An interface block can also be used to declare dboard inside pi.
I've inherited a legacy Fortran 77 code that I now try to get to compile in the Fortran 2003 standard. I have no clue about Fortran (I know C and Python), I'm picking it up on the way.
The below code snippet causes a compiler error (also given below). To be honest, just to look at this code gives me a head-ache: I really don't understand how one could write a line of code such as
A(i) = A(i) + B(q)
where both A and B are functions. I am familiar with the concept of recursive functions in C and Python, and if I were the compiler in this situation that is presented here, I would probably complain and raise at least a WTF warning.
I don't expect anyone to fix this code for me. I'd be more than happy if someone could explain to me what is (tried) to be achieved by the line:
cipr(IPR_WADV, ipa_idx, ispc) = cipr(IPR_WADV, ipa_idx, ispc) + fc1(l)/dy/depth(i,j,k)
or refer me to a good place where I can look that up.
Below are the code snippet and the corresponding compiler error.
IF( lipr ) THEN
!-----Change from X-direction horizontal advection
l = 1
DO i=i1+1,i2-1
l = l+1
IF( ipa_cel(i,j,k) .GT. 0 ) THEN
ipa_idx = ipa_cel(i,j,k)
!-----Flux at west boundary
cipr(IPR_WADV, ipa_idx, ispc) = cipr(IPR_WADV, ipa_idx, ispc) + fc1(l)/dy/depth(i,j,k)
!-----Flux at east boundary
cipr(IPR_EADV, ipa_idx, ispc) = cipr(IPR_EADV, ipa_idx, ispc) + fc2(l)/dy/depth(i,j,k)
!-----Average volume
cipr(IPR_VOL, ipa_idx, ispc) = cipr(IPR_VOL, ipa_idx, ispc) + dx(j)*dy*depth(i,j,k)
npastep(ipa_idx,ispc) = npastep(ipa_idx,ispc) + 1
END IF
END DO
END IF
the compiler gives this output message as an error
gfortran -std=f2003 -c -g -o build/Debug/GNU-Linux-x86/xyadvec.o
xyadvec.f03 xyadvec.f03:177.42:
cipr(IPR_WADV, ipa_idx, ispc) = cipr(IPR_WADV, ipa_idx, ispc) + fc1(l
1
Error: Statement function at (1) is recursive
Probably cipr is an array, and somehow the compiler doesn't know that. In that case, the line is interpreted as being a statement function.
For example,
program dummy
dimension c(10)
c(i) = c(i) + d
end program
this will compile (besides warnings about unitialized variable usage), as c is an array and the line updates an element in the array, similar to what c[i] += d would do in C.
If c is not an array, then the line will be interpreted as a one-line function, similar to a macro. So, e.g.:
program dummy
c(i) = 2*i
...
myvar = c(2)
end program
here c is a function that returns twice the argument, so myvar would be 4.
So, in your case, I would guess from the usage that cipr is intended to be an array and there is something wrong with declaration of cipr or with some include files that declare the dimension of cipr. Because the compiler then interprets it as a statement function, it fails.
Can you give the entire file where this line occurs?