Propagate error string: Fortran > C - c

Problem statement
The main part of my code is in C (called from Python). The C-part calls functions written in Fortran. Possible errors are propagated using an error-code and an error-string with a description of the error.
The problem is that I cannot seem to get the correct interface to write the string in Fortran and read/copy/manipulate it in C. The code below outlines what I want to do, the comments with marked with * ... * indicate where extensions are needed.
C
// global variable: read from Python if an error is encountered
char* error_string;
// template for the Fortan-subroutine
void fortran_calculation_( double* , int* );
int heavy_calculation( double* x )
{
int error_code;
// ... some code ...
// * should accept and write "error_string" *
fortran_calculation_( x , &error_code );
if ( error_code )
{
error_string = "TO BE WRITTEN BY FORTRAN > REMOVE!!";
return 1;
}
// ... some code ...
return 0;
}
Fortran
subroutine fortran_calculation_(x,error_code)
implicit none
! * include "error_string" as argument *
real*8 :: x
integer :: error_code
! ... some code ...
if ( ... ) then
! * write "error_string" *
error_code = 1
return
end if
return
end subroutine
I've tried many things, but I cannot seem to get it working...

You have two problems. One, how to access a C global variable from Fortran. This one is relatively straightforward, create an interface in a module with iso_c_binding. See https://gcc.gnu.org/onlinedocs/gfortran/Interoperable-Global-Variables.html for an example.
However, the trickier problem is that you have defined your error_string as a pointer to char. That means that your Fortran code must allocate the string before writing to it. The Fortran allocatable and pointer variables work with descriptors, not raw pointers, so you must first create an interface to the C malloc function. Only after that you can write to it. Something like:
module my_error_string
use iso_c_binding
interface
type(c_ptr) function c_malloc(size) bind(C, name="malloc")
use iso_c_binding
integer(kind=c_size_t), value :: size
end function c_malloc
end interface
type(c_ptr), bind(C) :: error_string
contains
subroutine write_error(str)
character(len=*) :: str
character, pointer :: fstr(:)
integer(c_size_t) :: strlen
integer :: i
strlen = len(str, kind=c_size_t) + 1_c_size_t
error_string = c_malloc(strlen)
if (.not. c_associated(error_string)) then
call perror("error_string is a null pointer => malloc failed?!")
stop 1
end if
call c_f_pointer(error_string, fstr, shape=[strlen])
do i = 1, len(str)
fstr(i) = str(i:i)
end do
fstr(strlen) = c_null_char
end subroutine write_error
end module my_error_string
(It might be simple to change the interface such that you instead pass an allocated C string to the Fortran function to fill in, or perhaps use a callback function. But the above works, if that's what you want.)

Here is a shamefully ugly "solution" to your problem, based on the design you provided.
main.c:
#include <stdio.h>
#include <string.h>
char error_string_[512];
void fortan_calculation_( double*, int*, int* );
int main() {
double d = 2.5;
int l, i = 3;
memset( error_string_, 0, 512 );
fortan_calculation_( &d, &i, &l );
error_string_[l] = 0;
printf( "After call: '%s'\n", error_string_ );
}
error.f90:
subroutine fortan_calculation( d, i, l )
implicit none
character(512) str
common /error_string/ str
double precision d
integer i, l
str = "Hello world!"
l = len_trim( str )
end subroutine fortan_calculation
Compilation and test:
$ gcc -c main.c
$ gfortran -c error.f90
$ gcc main.o error.o -lgfortran
$ ./a.out
After call: 'Hello world!'
But that is just disgusting code: it assumes a lot of (arguably) common practices for Fortran compilers, whereas it exists some ways of linking properly C and Fortran using the iso_c_binding Fortran module.
I'll have a look and see if I can come up with a proper solution to that.
EDIT: actually, there's a nice SO page about that available.

Related

Propagating optional arguments from C to Fortran and vice versa

I am wondering what is the correct way of interfacing with C, when the C methods have optional arguments (i.e. you are allowed to pass NULL) and you want the optional arguments to propage to the Fortran API.
Is there a benefit in including the optional keyword in both the Fortran method's argument list and the interface block? see: err_c argument in null_return_f90>C_API.
Is it legal to initialise a local variable with an optional input variable and pass that to the C interface? (this feels wrong, it doesn't feel like it should work, yet it does). see: name_c variable, its initialisation and input to C_API in null_str_f90 subroutine.
I have provided a MWE below demonstrating what I am asking and as far as I can tell, the answers to my questions are 1. it doesn't matter 2. yes, but I am not entirely convinced they are correct, especially 2.
If there is a better way on how to write the Fortran API I would be open to that as well, but what I cannot do is edit the C API.
Related post: Calling Fortran subroutines with optional arguments from C++
MWE
program main
use, intrinsic :: iso_c_binding
implicit none
integer(c_int) :: err
call null_str_f90("abc")
call null_str_f90()
print*, repeat("*", 10)
call null_return_f90()
call null_return_f90(err)
print*, "error code:", err
contains
function istring_(o) result(v)
character(len=*), intent(in) :: o
character(len=:, kind=c_char), allocatable :: v
v = trim(o)//c_null_char
end function istring_
subroutine null_return_f90(err)
interface
subroutine C_API(err_c) bind(C, name="null_return")
use, intrinsic :: iso_c_binding
integer(c_int), optional, intent(out) :: err_c ! 1. does the optional do anything?
end subroutine C_API
end interface
integer(c_int), optional, intent(out) :: err
call C_API(err_c=err) ! 1. Is this safe & portable?
end subroutine null_return_f90
subroutine null_str_f90(str)
interface
subroutine C_API(str_c) bind(C, name="null_str")
use, intrinsic :: iso_c_binding
character(len=1, kind=c_char), dimension(*), optional, intent(in) :: str_c
end subroutine C_API
end interface
character(len=*), intent(in), optional :: str
! Local variables
character(len=:, kind=c_char), allocatable :: name_c
if (present(str)) name_c = istring_(str)
call C_API(str_c=name_c) ! 2. Is this safe & portable?
end subroutine null_str_f90
end program main
#include <stdio.h>
void null_str(const char *str) {
if (str) {
printf("str is present: str is %s\n", str);
} else {
printf("str is not present\n");
}
}
void null_return(int *opt) {
if (opt) {
*opt = 1;
} else {
printf("opt is not present\n");
}
}
Compiling
Turn on aggressive non-IEEE compliant optimisations to ensure this will still work.
gcc -c -Ofast -Wall null_args.c -o null_args.o
gfortran -Ofast -Wall null_args.o null_args.f90 -o null_args
./null_args
Output
str is present: str is abc
str is not present
**********
opt is not present
error code: 1
**********
For null_return_f90 and its C_API, we must have err_c in that latter as an optional argument. Without it being optional, we cannot associate an absent err with it in that call. If it is optional, then it's fine to use the optional and not present actual argument err.
Looking at the subroutine null_str_f90 we have the following:
character(len=:, kind=c_char), allocatable :: name_c
if (present(str)) name_c = istring_(str)
call C_API(str_c=name_c) ! 2. Is this safe & portable?
Your concern is that if the actual argument str is not present, then name_c is undefined1 when it comes to the call to C_API. This is true, but is not a concern.
name_c remains not allocated rather than simply undefined. An allocatable actual argument which is not allocated (or a pointer which is not associated) is allowed to be associated with an ordinary optional dummy argument. In this case it will be treated as a not present actual argument, just as if it weren't given at all.
1 You state "uninitialized" but definition and initialization are very different things, and initialization is not relevant to this case.
Keeping in mind that function arguments:
in C they are passed by value, hence, they are unchanged on output; the only way to change a variable which is not the function's return value, is to pass a pointer to it (your int* err_c is a pointer to integer(s))
in Fortran they are passed by reference by default, unless otherwise specified (value keyword).
whenever you write a Fortran interface to a C function that involves a pointer, you have these options:
Copy the C approach, set it as a pointer
type(c_ptr), intent(in), value :: c_err
You can always shape it to an appropriate Fortran derived type and shape by using c_f_pointer, and/or access the C pointer of a Fortran target variable using c_loc
You lose information about the original type (int) in this case
Use the Fortran approach, i.e. reference that pointer to a given type, shape, intent. For example in your case, you could use any of
integer(c_int), intent(out) :: c_err
integer(c_int), intent(out) :: c_err(*)
Note that the C routine, per se, doesn't tell you which one is right, but the Fortran compiler will check it.
Now you're trying to use the optional keyword to hide the fact of a C pointer being NULL as opposed to a Fortran variable being present or not. The C standard does not allow for optional inputs; while the Fortran standard does not like null stuff. It seems like even Fortran standard gurus do not fully agree on what's allowed by the standard or not (see here), I'd go with using the optional keyword in the interface only if that's useful on the Fortran side.
See this example:
module test_c_opt
use iso_c_binding
implicit none
interface
! Pass-by-reference interface
subroutine C_reference(err_c,message) bind(C, name="null_return")
import c_int,c_char
integer(c_int), intent(out) :: err_c
character(len=1,kind=c_char), intent(in) :: message(*)
end subroutine C_reference
subroutine C_reference_arr(err_c,message) bind(C, name="null_return")
import c_int,c_char
integer(c_int), intent(out) :: err_c(*)
character(len=1,kind=c_char), intent(in) :: message(*)
end subroutine C_reference_arr
! Pass-by-value interface
subroutine C_value(err_c,message) bind(C, name="null_return")
import c_ptr,c_char
type(c_ptr), intent(in), value :: err_c
character(len=1,kind=c_char), intent(in) :: message(*)
end subroutine C_value
end interface
end module test_c_opt
program test
use test_c_opt
use iso_c_binding
implicit none
integer(c_int), target :: ierr,ierr10(10)
integer(c_int), allocatable, target :: ierra,ierra10(:)
character(len=255,kind=c_char) :: msg
msg = 'reference, static' //c_null_char; call C_reference(ierr,msg)
msg = 'value , static' //c_null_char; call C_value(c_loc(ierr),msg)
msg = 'reference, not alloc' //c_null_char; call C_reference(ierra,msg)
msg = 'value , not alloc' //c_null_char; call C_value(c_loc(ierra),msg)
allocate(ierra)
msg = 'reference, allocated' //c_null_char; call C_reference(ierra,msg)
msg = 'value , allocated' //c_null_char; call C_value(c_loc(ierra),msg)
msg = 'reference, not alloc(10)' //c_null_char; call C_reference_arr(ierra10,msg)
allocate(ierra10(10))
msg = 'reference, alloc(10)' //c_null_char; call C_reference_arr(ierra10,msg)
print *, 'from fortran, ierra(10)=',ierra10
!msg = 'reference, static(10)'//c_null_char; call C_reference(ierr10,msg) ! error: fortran checks this is not a scalar
msg = 'value , static(10)'//c_null_char; call C_value(c_loc(ierr10),msg)
end program test
#include <stdio.h>
void null_return(int *opt, char* message) {
if (opt) {
*opt = 1;
printf("when [%s], opt is present, set opt=%d \n", message, *opt);
} else {
printf("when [%s], opt is not present\n");
}
}
it works in all cases without optional anyways, which prints (gfortran-12)
when [reference, static], opt is present, set opt=1
when [value , static], opt is present, set opt=1
when [reference, not alloc], opt is not present
when [value , not alloc], opt is not present
when [reference, allocated], opt is present, set opt=1
when [value , allocated], opt is present, set opt=1
when [reference, not alloc(10)], opt is not present
when [reference, alloc(10)], opt is present, set opt=1
from fortran, ierra(10)= 1 0 0 0 0 0 0 0 0 0
when [value , static(10)], opt is present, set opt=1

Fortran to pass C struct between two C functions

I have a Fortran application that is required to call two C routines.
One to load a file and one to run a calculation on the file about 200 times.
I understand that a C DLL cannot 'save' the loaded struct in a static variable to be reused in the calculate function so I am looking to parse back a void* to Fortran and send it to C calculate function.
The C functions:
__declspec(dllexport) void loadfile(void * file); // Empty pointer should be filled with struct of loaded file
__declspec(dllexport) void calculate(void * file, double * result); //filled void ptr is used (casted back to my struct first)
My Fortran code:
module globalFileHolder
USE, INTRINSIC::ISO_C_BINDING
type(C_PTR), save :: fileModule = C_NULL_PTR
end module
Load file routine:
SUBROUTINE loadcfile()
USE, INTRINSIC::ISO_C_BINDING
use globalFileHolder
IMPLICIT NONE
INTERFACE
SUBROUTINE loadfile(fm) BIND(C)
USE, INTRINSIC::ISO_C_BINDING
TYPE(C_PTR) :: fm
END SUBROUTINE loadfile
END INTERFACE
TYPE(C_PTR) :: fms = c_null_ptr
call loadfile(fms)
fileModule = fms
return
end
And finally my routine that is supposed to use the loaded file in a calculation:
SUBROUTINE calculatec()
USE, INTRINSIC::ISO_C_BINDING
use globalFileHolder
IMPLICIT NONE
INTERFACE
SUBROUTINE calculate(fm,res) BIND(C)
USE, INTRINSIC::ISO_C_BINDING
TYPE(C_PTR) , VALUE :: fm
REAL(C_DOUBLE) , value :: res
END SUBROUTINE calculate
END INTERFACE
TYPE(C_PTR) :: fms
REAL(C_DOUBLE) result
fms = C_LOC(fileModule)
call calculate(fms,result)
return
end
Now the problem I currently have is that the module variable filemodule seems to be filled but when sending it to the calculate function the variable is null after casting it like:
myStruct * ms = (myStruct*)file;
Where do I go wrong?
The declaration of the fm dummy argument in the interface for loadfile is missing the VALUE attribute.

.C() returns me an empty list

I'm a beginner in R and I'm trying to load a .dll file, named dll.dll, that's written in C, into R. It seems to work, now I want to use the functions that are stored in the .dll file and I encounter problems.
I've searched for a solution or other method in manuals, here and on google. Would be very thankful if I could get a suggestion of what to use or any idea!
My code:
setwd("C:/Users/MyUser/R")
dyn.load("dll.dll")
is.loaded("DLL_FUNK")
# For some reason True with capital letters, not in lower case
output <- .C("DLL_FUNK", in9 = as.integer(7))
#output # R Crashes before I can write this.
# R Crashes
# In outdata.txt: "in-value= 139375128"
The function should return a number, 1955. But I can't seem to get to that value. What am I doing wrong?
Update with code (Fortran runned as C), this is the code in dll.dll:
subroutine dll_funk(in9)
implicit none
!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
!*** Declarations: variables, functions
!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
integer(4) :: in9
!integer :: in9
! Definitions of variables in the external function calls
!!dec$ attributes c,alias :'dll_funk' :: dll_funk
!dec$ attributes dllexport :: dll_funk
!dec$ attributes value :: in9
open(194,file='outdata.txt')
write(194,*) 'in-value=', in9
! in9 = 1955
close(194)
end subroutine
!end function
So now when it runs, R crashes but before it writes to my file (outdata.txt) but it't not my number, maybe some kind of address...
Another question, do you recommend me to run the code with .C and from C run the Fortran code or is it better to run it with .Fortran with only Fortran code?
It seems like .Fortran have problem handling strings, or that's what I understood from: Interface func .C and .Fortran
Why did not you pass any arguments to your C function dll_function? When you use .C(), you have to pass function arguments as a list. .C() will return modified list. So, If you pass in nothing, you get nothing.
What does your C function dll_function looks like? Note that:
dll_function must be a void C function, with no return values. If this function should return something, it must return by modifying function arguments;
all function arguments of dll_function must be pointers.
Follow-up
The dll_function is only to test if I can get access to it.
You can use is.loaded() after dyn.load() to test whether you have access to the C function:
dyn.load("dll.dll")
is.loaded("dll_function") ## TRUE
Note that, is.loaded takes C function name, while dyn.load() takes .dll name. In general you can have multiple functions in a single .dll file. You can use is.loaded() to check either of them, to test whether shared library has been loaded successfully.
So if I want it to return something, I should give it an argument (of same type?)?
Yes. The other answer here does give a toy example. You can have a look at this answer I made half a month ago. At the bottom there is a summary of variable type.
When using .C, the extra arguments passed to .C are copied and passed on as pointers to the called c-function. This function can then modify the data pointer to by the pointers. The return value of the function is ignored by .C. So, you c-function should look something like:
void dll_function(int* result) {
/* Do some complicated computation that results in 1955 */
(*result) = 1955;
}
And your call from R:
.C("dll_function", integer(1))
An example with input (this calculates the sum of an integer vector; this example assumes that there are no missing values in vector):
void dll_function2(int* result, int* vector, int* length) {
int sum = 0;
for (int i = 0; i < (*length); ++i, ++vector) {
sum += (*vector)
}
(*result) = sum;
}
Called from R:
x <- c(1000, 900, 55)
.C("dll_function2", integer(1), as.integer(x), length(x))[[1]]

Fortran: segmentation fault when trying to update particle position in 3-d array

I've written a program to move around particles in a 3-D field based on a 3-D velocity field. However, I get a segmentation fault at the line when I update the particle positions, and I have no idea why! I wrote this program previously in a single file, and it worked fine. But now I'm getting the segmentation fault error when I have all the functions/subroutines in a module.
Edit: I implemented the suggestions below, and now the segmentation fault has moved from the update particle line to the line where I call writeResults. Any help is still appreciated!
Main Program:
program hw4Fortran
use hw4_module
implicit none
!Define types
integer::i ,j, k, num_ts, num_particles, field_size_x, field_size_y, &
field_size_z, num_arguments
type(vector),allocatable::vfield(:,:,:)
type(vector),allocatable::parray(:)
character(30)::out_file_basename, vel_file, part_file, filename, string_num_ts
!Read command line
num_arguments = NARGS()
if (num_arguments > 1) then
call GETARG(1, string_num_ts)
read(string_num_ts, *) num_ts
else
num_ts = 50
end if
if (num_arguments > 2) then
call GETARG(2, out_file_basename)
else
out_file_basename = "results"
end if
if (num_arguments > 3) then
call GETARG(3, vel_file)
else
end if
if (num_arguments > 4) then
call GETARG(4, part_file)
else
part_file = "particles.dat"
end if
!Open files
open(unit=1, file=vel_file)
open(unit=2, file=part_file)
!Read number of particles
num_particles = readNumParticles(2)
!Adjust for zero index
num_particles = num_particles - 1
!Allocate and read particle array
parray = readParticles(2, num_particles)
!Read field size
field_size_x = readFieldSize(1)
field_size_y = readFieldSize(1)
field_size_z = readFieldSize(1)
!Adjust for zero index
field_size_x = field_size_x - 1
field_size_y = field_size_y - 1
field_size_z = field_size_z - 1
!Allocate and read vector field
vfield = readVectorField(1, field_size_x, field_size_y, field_size_z)
!Move particles and write results
do i=0,num_ts
if (mod(i,10) == 0) then
write(filename, fmt = "(2A, I0.4, A)") trim(out_file_basename), "_", i, ".dat"
open(unit = 3, file=filename)
end if
do j=0, num_particles
if (i > 0) then
parray(j) = updateParticle(vfield(INT(FLOOR(parray(j)%x)),INT(FLOOR(parray(j)%y)),INT(FLOOR(parray(j)%z))), parray(j))
end if
if (mod(i,10) == 0) then
call writeResults(3, parray(j))
end if
end do
if (mod(i,10) == 0) then
close(3)
end if
end do
!Close files
close(1)
close(2)
!Deallocate arrays
deallocate(vfield)
deallocate(parray)
end program hw4Fortran
Module:
module hw4_module
implicit none
type vector
real::x,y,z
end type
contains
function readNumParticles(fp) result(num_particles)
integer::fp, num_particles
read(fp, *) num_particles
end function
function readParticles(fp, num_particles) result(parray)
integer::fp, num_particles, i
type(vector),allocatable::parray(:)
allocate(parray(0:num_particles))
do i=0, num_particles
read(fp, *) parray(i)
end do
end function
function readFieldSize(fp) result(field_size)
integer::fp, field_size
read(fp, *) field_size
end function
function readVectorField(fp, field_size_x, field_size_y, &
field_size_z) result(vfield)
integer::fp, field_size_x, field_size_y, field_size_z, i, j
type(vector),allocatable::vfield(:,:,:)
allocate(vfield(0:field_size_x,0:field_size_y,0:field_size_z))
do i=0, field_size_x
do j=0, field_size_y
read(fp, *) vfield(i,j,:)
end do
end do
end function
function updateParticle(velocity, old_particle) result(new_particle)
type(vector)::new_particle,old_particle,velocity
new_particle%x = old_particle%x + velocity%x
new_particle%y = old_particle%y + velocity%y
new_particle%z = old_particle%z + velocity%z
end function
subroutine writeResults(fp, particle)
integer::fp
type(vector)::particle
write(fp, *) particle%x, " ", particle%y, " ", particle%z
end subroutine
end module
This function
function readParticles(fp, num_particles) result(parray)
integer::fp, num_particles, i
type(vector),allocatable::parray(:)
allocate(parray(0:num_particles))
do i=0, num_particles
read(fp, *) parray(i)
end do
end function
allocates parray with index values 0:num_particles. Unfortunately, and this trips up many a newcomer to Fortran (some oldcomers too), those array bounds are not passed out to the calling code which will blithely assume an index range starting at 1. And then the code goes on to access parray(0) ... and the problem that John B warns of arises.
Fortran's capability of indexing arrays from an arbitrary integer value is never quite as useful as it seems. You can pass the bounds into and out of procedures, but who can be bothered ? Easier just to pretend that Fortran arrays index from 1 and apply that consistently throughout a program.
here is a simple version of what the OP is doing with allocate
module altest
contains
function setarray(n) result(x)
implicit none
integer, intent(in) :: n
integer , allocatable :: x(:)
allocate(x(n))
x(1)=1
end function
end module
program Console6
use altest
implicit none
integer,allocatable :: m(:)
m=setarray(2)
write(*,*)'m1',m(1)
end program Console6
It "appears" to be allocating an array x in the function and assigning that to an allocatable array m in the calling program. This compiles but throws a subscript out of bounds error on the write. (note this would likely be a seg fault if bounds checking is not enabled )
This can be fixed by separately allocating the array in the calling routine, or by passing the allocatable array as an argument:
module altest
contains
subroutine setarray(n,x)
implicit none
integer, intent(in) :: n
integer , allocatable :: x(:)
allocate(x(n))
x(1)=1
end subroutine
end module
program Console6
use altest
implicit none
integer,allocatable :: m(:)
call setarray(2,m)
write(*,*)'m1',m(1)
end program Console6
Edit - somewhat to my surprise, the second case works fine if we allocate with a zero lower bound in the sub allocate(x(0:n)) , the calling routine 'knows' the subscript starts at zero. ( Works with intel fortran v13 -- I have no Idea if this is a safe thing to do.. )
A segmentation fault normally indicates that your program is trying to access memory that does not belong to it.
When you say the error occurs "when I update the particle positions", I take it you mean this line:
updateParticle(vfield(INT(FLOOR(parray(j)%x)),INT(FLOOR(parray(j)%y)),INT(FLOOR(parray(j)%z))), parray(j))
An array-bounds violation in that statement seems entirely plausible, as I don't see anything in your code that would prevent the array indexes INT(FLOOR(parray(j)%x)) et al from falling outside the allocated dimensions of array vfield. Even if they are all in bounds at the initial step of the simulation, they may go out of bounds as the simulation proceeds.
Whether such a result in fact occurs appears to be data-dependent, not related to whether your functions appear in a module.
It looks like you have a C background. Could this be an off-by-one error? When looping in Fortran, the loop index goes all the way to the upper bound. Your Fortran loop:
do j=0, num_particles
! ...
end do
is equivalent to this C loop:
for (int j = 0; j <= num_particles; j++)
{
// ...
}
Note the <= sign, instead of <.
You may want to change your Fortran upper bound to num_particles - 1.

Calling GSL routine CQUAD from Fortran

I'm trying to call the GSL rountine CQUAD from Fortran. My idea was to write a .c subroutine that calls the gsl rountine and depends on a function and bounds. Two problems: I have only very little idea about c and fortrans iso_c_binding. My attempt is as follows:
A simple calling program (similar to M.S.B's post in No output from a Fortran program using the Gnu Scientific Library via a c wrapper ):
program test
use iso_c_binding
interface
function my_cquad (f,a,b) bind(c)
import
real (kind=c_double) :: my_cquad
interface
function f(x) bind(c)
import
real(kind=c_double) :: f,x
end function
end interface
real (kind=c_double) :: a,b
end function my_cquad
end interface
real (kind=c_double) :: y,a,b
a=0. ; b=1.
y=my_cquad(g,a,b)
print*,y
stop
contains
function g(x) bind(C)
real(kind=c_double) :: g,x
g=sin(x)/x
return
end function g
end program test
The .c subroutine (basically taken from the example given by the author of CQUAD in https://scicomp.stackexchange.com/questions/4730/numerical-integration-handling-nans-c-fortran):
#include <stdio.h>
#include <gsl/gsl_integration.h>
double my_cquad ( double my_f() , double a , double b )
{
gsl_function f;
gsl_integration_cquad_workspace *ws = NULL;
double res, abserr;
size_t neval;
/* Prepare the function. */
f.function = my_f;
f.params = NULL;
/* Initialize the workspace. */
if ( ( ws = gsl_integration_cquad_workspace_alloc( 200 ) ) == NULL ) {
printf( "call to gsl_integration_cquad_workspace_alloc failed.\n" );
abort();
}
/* Call the integrator. */
if ( gsl_integration_cquad( &f, a , b , 1.0e-10 , 1.0e-10 , ws , &res , &abserr , &neval ) != 0 ) {
printf( "call to gsl_integration_cquad failed.\n" );
abort();
}
/* Free the workspace. */
gsl_integration_cquad_workspace_free( ws );
/* Bye. */
return res;
}
The .c subroutine alone seems to work fine. This can be tested with:
double g (double x)
{
return sin(x)/x;
}
int main () {
double y;
y=my_cquad(g,0.,1.);
printf("y: %2.18f\n", y);
return 0;
}
But together with the .f90 calling program, at the moment it compiles but at runtime I get a segmentation fault that I don't quite get.
Additionally, it would of course be good to have some kind of wrapper that creates a c-type function depending on a fortran type function. I'm thinking about something like:
function f_to_c(f) bind(c)
real(kind=c_double) :: f_to_c
real(kind=8) :: f
f_to_c=real(f,kind=c_double)
end function
But this desn't cover the dummy variables.
Thanks in advance and very sorry for the amount of code.
Beware, according to the Fortran standard internal functions shall not have the bind(C) attribute. I moved the function to a module.
The a and b must be passed by value to my_cquad and x to the integrated function:
module functions_to_integrate
use iso_c_binding
contains
function g(x) bind(C)
real(kind=c_double) :: g
real(kind=c_double), value :: x
g = sin(x)/x
end function g
end module
program test
use iso_c_binding
use functions_to_integrate
interface
function my_cquad (f,a,b) bind(c)
import
real (kind=c_double) :: my_cquad
interface
function f(x) bind(c)
import
real(kind=c_double) :: f
real(kind=c_double), value :: x
end function
end interface
real (kind=c_double), value :: a,b
end function my_cquad
end interface
real (kind=c_double) :: y,a,b
a = 0 ; b = 1
y = my_cquad(g,a,b)
print *,y
end program test
test:
> gfortran my_cquad.c test_cquad.f90 -lgsl -lopenblas
> ./a.out
0.94608307036718275
This is an example of a wrapper to a normal Fortran function (please do not use kind=8 for many reasons explained in another questions):
module functions_to_integrate
use iso_fortran_env
use iso_c_binding
integer, parameter :: wp = real64
contains
pure function g(x)
real(kind=wp) :: g
real(kind=wp), intent(in) :: x
g = sin(x)/x
end function g
function g_to_c(x) bind(C)
real(kind=c_double) :: g_to_c
real(kind=c_double), value :: x
g_to_c = real(g(x),kind=c_double)
end function
end module
program test
use iso_c_binding
use functions_to_integrate
interface
function my_cquad (f,a,b) bind(c)
import
real (kind=c_double) :: my_cquad
interface
function f(x) bind(c)
import
real(kind=c_double) :: f
real(kind=c_double), value :: x
end function
end interface
real (kind=c_double), value :: a,b
end function my_cquad
end interface
real (kind=c_double) :: y,a,b
a = 0 ; b = 1
y = my_cquad(g_to_c,a,b)
print *,y
end program test
P.S. I also deleted your stop and return statement before the end. Somehow it is always driving me mad, but that may be just my OCD. I was too used to see it in old programs coming from ancient times.
P.P.S: You may wish to see the FGSL interface package linked by Vincius Miranda http://www.lrz.de/services/software/mathematik/gsl/fortran/ . I knew about that one, but I tried mainly to point out the errors so that you can make similar interfaces yourself, where no ready-made package is available.

Resources