Fortran: pointer to various array-valued functions - pointers

I am starting this thread because I want to learn how to successfully use the same pointer to serve as the aliases of different array-valued functions, say, f1 and f2, sequentially.
Here is an unsuccessful code to illustrate what I want. Thanks. Lee
PROGRAM main
...
REAL(WP), POINTER, DIMENSION(:) :: p
p=>f1
print*,p(1.0_wp) ! the outcome should be 3
p=>f2
print*,p(2.0_wp) ! the outcome should be 3 1
CONTAINS
FUNCTION f1(x)
IMPLICIT NONE
REAL(WP), TARGET :: f1
REAL(WP), INTENT(IN) :: x
f1=x+2
END FUNCTION f1
FUNCTION f2(x)
IMPLICIT NONE
REAL(WP), TARGET :: f2(2)
REAL(WP), INTENT(IN) :: x
f2(1) = x+1
f2(2) = x-1
END FUNCTION f2
END PROGRAM main

For a pointer to a function that returns an array, you want to have an interface to describe a pointer to a function that returns an array.
Here is an example of how to setup function pointers that might set you in the right direction:
How to alias a function name in Fortran
Edit: OK, here is some example source code:
module ExampleFuncs
implicit none
contains
function f1 (x)
real, dimension (:), allocatable :: f1
real, intent (in) :: x
allocate (f1 (1:2))
f1 (1) = 2.0 * x
f1 (2) = -2.0 * x
return
end function f1
function f2 (x)
real, dimension (:), allocatable :: f2
real, intent (in) :: x
allocate (f2 (1:3))
f2 (1) = x
f2 (2) = x**2
f2 (3) = x**3
return
end function f2
end module ExampleFuncs
program test_func_ptrs
use ExampleFuncs
implicit none
abstract interface
function func (z)
real, dimension (:), allocatable :: func
real, intent (in) :: z
end function func
end interface
procedure (func), pointer :: f_ptr
real :: input
do
write (*, '( // "Input test value: ")', advance="no" )
read (*, *) input
if ( input < 0.0 ) then
f_ptr => f1
else
f_ptr => f2
end if
write (*, '( "evaluate function: ", *(ES14.4) )' ) f_ptr (input)
end do
end program test_func_ptrs

Related

Fortran pointer dummy argument

I have the following Fortran program. But I don't understand why the output is 4 instead of 1. I am using GNU 6.4 Fortran compiler:
program aa
implicit none
real, pointer, dimension(:,:) :: t => null()
integer :: i,j
allocate(t(0:100,20))
do i = 0, 100
do j = 1, 20
t(i,j) = i*j
end do
end do
call te(t(1:,:))
stop
contains
subroutine te(a)
implicit none
real,dimension(:,:),pointer,intent(in) :: a
print *, a(1,1)
end subroutine te
end program aa
The Intel compiler 18.0.2 returns the error message
/pt.f90(17): error #7121: A ptr dummy may only be argument associated with a ptr, and this array element or section does not inherit the POINTER attr from its parent array. [T]
call te(t(1:,:))
Change the line in your subroutine to
real,dimension(:,:),intent(in) :: a
and all is good.

Passing characters/strings from R to Fortran

I have a Fortran subroutine that selects a function based on the value of a string and then executes that function.
!! file:select.f90
module funcs
contains
subroutine add(x, y, xy)
real :: x, y, xy
xy = x + y
return
end subroutine
subroutine diff(x, y, xy)
real :: x, y, xy
xy = x - y
return
end subroutine
end module
subroutine simple(modname)
use funcs
procedure(), pointer :: model => null()
character(10) :: modname
real :: x, y, xy
print *, "-",modname,"-"
select case (modname)
case("add")
model => add
case("diff")
model => diff
case default
print *, "No model with that name!"
stop
end select
x = 4
y = 3
call model(x, y, xy)
print *, xy
end subroutine
I would like to call this subroutine from an R script.
# file:select.R
dyn.load("select.so")
.Fortran("simple", "add")
.Fortran("simple", "diff")
As a standalone Fortran program that takes a command line argument, this runs perfectly fine. It's even insensitive to spaces before or after modname. However, when I try to pass in a character as an argument from R, it correctly re-prints the character (without any extra spaces), but then doesn't recognize it as a case and skips to the default. What is going on here? Is there some encoding issue with R characters that makes them incompatible with Fortran?
I believe your select case statement is not properly matching because the modname is 10 characters long, and none of your cases cover a string of that length. The best thing to do is to also pass in the length of the string to your Fortran function, then use this to slice your character array.
subroutine simple(modname,length)
then select case (modname(1:length))
Fortran strings are not zero terminated like the C language. It's an array based language.
Also when passing a string from R to .Fortran, it might be better to pass it as raw bytes. Simple example below. First is the Fortran code, then the R wrapper code.
subroutine print_this ( str, length )
integer :: length
character(length) :: str
print *, str(1:length)
end subroutine print_this
test <- function(str) {
l <- nchar(str)
str_raw <- character(l)
str_raw <- charToRaw(str)
.Fortran("print_this",str_raw,l)
l #returns length
}

Preserve (or restore) lbound after casting from c_ptr

I have a type that stores an array:
type data
type(someType) pointer :: someTypePtr(:)
end type
I assign something to someTypePtr with
type(data), intent(inout) :: this
integer, intent(in) :: lb, ub
type(someType), target, intent(in) :: sometype(lb:ub)
this%someTypePtr => sometype
Later on I create a c_ptr pointing to the instance I just reffered to as this. When I cast this pointer back to a fortran type afterwards, the lower bound starts with 0:
type(c_ptr) :: ptr
type(someType) :: data
call c_f_pointer(ptr, data)
write(*,*) lbound(data%someTypePtr) !will give 0
Is there something similar to reshape to change the someTypePtr back to it's original lbound:ubound?
It is strange, it should be 1, not 0. Anyway, you can remap the pointer afterwards:
use iso_c_binding
real(c_float), pointer :: ptr(:)
type(c_ptr) :: ptr_c
allocate(ptr(5:6))
ptr_c = c_loc(ptr(lbound(ptr)))
call c_f_pointer(ptr_c, ptr, [2])
print *, lbound(ptr)
ptr(5:6) => ptr
print *, lbound(ptr)
end
run:
> ./a.out
1
5

How to use inteface blocks to pass a function to a subroutine?

I understand the interface command can be used to pass a a function into a subroutine. So for example in the main program I'd define some function and then pass it to some subroutine like:
MainProgran
Use ....
Implicit None
Type decorations etc
Interface
Function test(x,y)
REAL, INTENT(IN) :: x, y
REAL :: test
END function
End Interface
Call Subroutine( limit1, limit2, test, Ans)
End MainProgram
Is this the correct way of doing this? I'm quite stuck! Also within the Subroutine is there anything I need to put to let it know that a function is coming in? The Subroutine in this case will be a library so I don't want to have to keep recompiling it to change the function.
Module:
module fmod
interface
function f_interf(x,y)
real, intent(in) :: x, y
real :: f_interf
end function
end interface
contains
function f_sum(x,y)
real, intent(in) :: x, y
real f_sum
f_sum = x + y
end function
function f_subst(x,y)
real, intent(in) :: x, y
real f_subst
f_subst = x - y
end function
subroutine subr(limit1, limit2, func, ans)
real limit1, limit2
procedure(f_interf) func
real ans
ans = func(limit1, limit2)
end subroutine
end module
main program:
program pass_func
use fmod
Implicit None
real ans, limit1, limit2
limit1 = 1.0
limit2 = 2.0
call subr( limit1, limit2, f_subst, ans)
write(*,*) ans
call subr( limit1, limit2, f_sum, ans)
write(*,*) ans
end program pass_func
and output:
-1.000000
3.000000
A simple way to do this is to go old school and just leave the function external:
program main
real f,z
external f
call subr(f,z)
write(*,*)z
end
real function f(x)
real x
f=x**2
end
! below possibly in a precompiled library:
subroutine subr(f,y)
real f,y
y=f(2.)
end
out: 4
Of course with this approach you can not use advanced language features that require an explicit interface. **
On the other hand if you are interfacing with standard libraries that need function arguments this is I think the only way.
** per MSB's comment you can handle that issue with an interface block in the subroutine,
for example if we want to pass a function that returns an array:
function f(x)
real x,f(2)
f(1)=x
f(2)=x**2
end
as in the first example f is an external function, and the sub can be in
a precompiled library:
subroutine subr(g,y)
interface
function g(x)
real x,g(2)
end function
end interface
real y,z(2)
z=g(2.)
y=z(1)+z(2)
end
out: 6
As noted, this is only strictly necessary if relying on language features that need the interface.
The most elegant way I know of right now is to put your functions into a module so that you don't have to do construct interface but simply use 'external'. Here is a example to do that.
It covers different situations using subroutine or function as arguments for subroutine or function.
Notice if you want to pass array as argument without receiving null arraies, here is a tip to do that.
Module part:
module func_arg_test
!I used ifort to compile but other compilers should also be fine.
!Written by Kee
!Feb 20, 2017
contains
!-------------------------
real function func_func(f, arg)
!========================================
!This shows how to pass number as argument
!========================================
implicit none
real, external::f !Use external to indicate the f is a name of a function
real::arg
func_func=f(arg)
end function func_func
real function func_sub(subr, arg)
!========================================
!This shows how to pass subroutine as arg to function
!========================================
implicit none
external::subr !Use external to indicate subr is a subroutine
real::arg
call sub(arg)
func_sub = arg
end function func_sub
subroutine sub_func(f,arg)
!========================================
!This shows how to pass function as argument
!in subroutine
!========================================
real::arg
real,external::f
arg = f(arg)
end subroutine sub_func
subroutine sub_sub(subr,arg)
!========================================
!This shows how to pass subroutine as argument
!in subroutine
!========================================
real::arg
external::subr
call subr(arg)
end subroutine sub_sub
real function funcmat(f, mat)
!========================================
!This shows how to pass matrix as argument
!========================================
implicit none
real, external::f
real,dimension(:)::mat!Here memory for mat is already allocated when mat is
!passed in, so don't need specific size
integer::sizeinfo
sizeinfo = size(mat)
funcmat = f(mat,sizeinfo)
end function funcmat
!--------------------------
real function f1(arg)
!This test function double the number arg
implicit none
real::arg
f1 = arg*2
return
end function f1
real function f2(arg)
!This test function square the number arg
implicit none
real::arg
f2 = arg*arg
return
end function f2
real function fmat(mat,sizeinfo)
!This test function sum up all elements in the mat
implicit none
integer::sizeinfo!This is the method I come up with to get around the
!restriction.
real,dimension(sizeinfo)::mat!This mat cannot be undetermined, otherwise it
!won't recevie mat correctly. I don't know why yet.
fmat = sum(mat)
end function fmat
subroutine sub(arg)
real::arg
arg = arg*3
end subroutine sub
end module
Main program:
program main
use func_arg_test
implicit none
real::a = 5d0
real::output
real, dimension(:),allocatable::mat
write(*,*) 'value of a=',a
output = func_func(f1,a)
write(*,*) 'a is doubled'
write(*,*) output
output = func_func(f2,a)
write(*,*) 'a is squared'
write(*,*) output
output = func_sub(sub,a)
write(*,*) 'a is tripled and overwritten'
write(*,*) output
call sub_func(f2,a)
write(*,*) 'a is squared and overwritten'
write(*,*) a
call sub_sub(sub,a)
write(*,*) 'a is tripled and overwritten'
write(*,*) a
allocate(mat(3))
mat = (/1d0,10d0,1d0/)!The allocatable arrray has to have a determined shape before
!pass as arguemnt
write(*,*) '1D matrix:',mat
write(*,*) 'Summation of the matrix:'
output = funcmat(fmat,mat)!elements of mat are summed
write(*,*) output
end program
And the result is:
value of a= 5.000000
a is doubled
10.00000
a is squared
25.00000
a is tripled and overwritten
15.00000
a is squared and overwritten
225.0000
a is tripled and overwritten
675.0000
1D matrix: 1.000000 10.00000 1.000000
Summation of the matrix:
12.00000

fortran 90 expected bounds specification during pointer assignment

I am new to Fortran. I am writing a program in Fortran 90 to get non-zero elements of an array and put them into a new array using pointer function as following:
program prog
implicit none
integer, target :: a(5)
integer :: i
integer, pointer :: nz(:)
a(1) = 1
a(2) = 0
a(3) = 0
a(4) = 2
a(5) = 3
nz => non_zeros(a)
do i=1, size(nz)
write(*,*) nz(i)
end do
contains
function non_zeros(a)
integer, target :: a(:)
integer, pointer:: non_zeros(:)
integer :: n, i, j
n = count(a .ne. 0)
allocate(non_zeros(n))
j = 0
do i=1, m
if (a(i) .ne. 0) then
j = j + 1
non_zeros(j) => a(i)
end if
end do
end function non_zeros
end program prog
during compiling I got the error:
non_zeros(j) => a(i)
1
Error: Expected bounds specification for 'non_zeros' at (1)
Can you please tell me what did I do wrong? Thank you in advance!
Update of my question: According to the explanation of High Performance Mark, I defined a derived type:
program prog
implicit none
integer, target :: a(5)
type dt
integer, pointer :: x
end type
type(dt), allocatable :: nz(:)
a(1) = 1
a(2) = 0
a(3) = 0
a(4) = 2
a(5) = 3
nz = non_zeros(a)
contains
function non_zeros(a)
integer, target :: a(:)
type(dt), allocatable :: non_zeros(:)
integer :: n, i, j
n = count(a .ne. 0)
allocate(non_zeros(n))
j = 0
do i=1, m
if (a(i) .ne. 0) then
j = j + 1
non_zeros(j)%x => a(i)
end if
end do
end function non_zeros
end program prog
Now program works and gives the desired results. However, I did not use pointer function in this case, since my function returns an allocatable array of pointers, not pointer to an array. Is there any way to use pointer function here? Thank you
To get the non-zero elements of a into a new array you could simply declare
integer, dimension(:), allocatable :: non_zeros
and then populate that with the statement
non_zeros = pack(a,a/=0)
and avoid fiddling around with pointers entirely. This relies on a feature introduced in the 2003 standard, but it is implemented by all (I think) the current crop of Fortran compilers on the market.
The code that you have written looks to me as if you want nz to be an array of pointers, with each element in nz pointing to a non-zero element of a. If I'm right, you've misunderstood what a statement such as
integer, pointer :: nz(:)
declares. It does not declare an array of pointers to integers, it declares a pointer to an array of integers. When you write
non_zeros(j) => a(i)
you're making the mistake of trying to set an element of non_zeros to point to an element of a.
The error message is misleading here because the compiler interprets non_zeros(j) as a syntactically-incorrect bounds-spec or bounds-remapping, but the error is semantic, the compiler doesn't understand your misunderstanding of Fortran.

Resources