Problem
I am trying to have a function be the argument to another function however I keep getting the error:
Error: Internal procedure 'polytrope' is not allowed as an actual argument at (1)
Code
Bellow is a barebones version of the program making the call to the function and which contains the actual callback:
PROGRAM testbutcher
USE butcher
IMPLICIT NONE
REAL :: t = 0, dt = 0.01
REAL, DIMENSION(2) :: v0 = (/ 1.0, 0.0 /), fargs = (/ 1.0, 1.0 /)
v0 = step(polytrope, v0, 2, t, dt, fargs)
CONTAINS
FUNCTION polytrope(v0, t, fargs) result(v1)
REAL, DIMENSION(:) :: fargs
REAL, DIMENSION(2) :: v0, v1
REAL :: t
v1 = t * v0
RETURN
END FUNCTION
END PROGRAM
and then the module which the function taking the functional argument is:
MODULE butcher
IMPLICIT NONE
CONTAINS
FUNCTION step(fxn, v0, n, t, dt, fargs) RESULT(v1)
REAL, DIMENSION(n) :: v0, v1
REAL, DIMENSION(:) :: fargs
REAL, DIMENSION(tn,tm) :: tab
REAL :: t, dt
INTEGER :: n, tn, tm
INTERFACE
FUNCTION fxn(v, t, fargs)
REAL, DIMENSION(:), INTENT(in) :: v
REAL, DIMENSION(:), INTENT(in) :: fargs
REAL, INTENT(in) :: t
END FUNCTION
END INTERFACE
v1 = fxn( v0, &
t + dt, &
fargs &
)
RETURN
END FUNCTION
END MODULE
Summary
So basically, testbutcher contains a function to be evaluated in a special way, so it sends it off to the module butcher (specifically the function step in butcher) to be evaluated. I can't figure out how to actually do this! If I were working in C i would simply make a pointer to polytrope and throw it to fxn.
In the function step, you define the inputs to fxn as being of INTENT(IN), but you don't declare any intent in your function polytrope. Also, the return type of fxn is not specified so it is being implicitly defined and won't be compatible with "REAL, DIMENSION(2)". I think you need a few more "IMPLICIT NONE" declarations in your code to catch this error.
This is what has worked for me in the past with all of MS Fortran, Digital Fortran, Intel Fortran, and gfortran:
Declare the function as external to the module that uses it.
Declare the function proper outside any module (it could go in its own file).
It's what I use to declare a library-invoked 'usage' subroutine in every program. It is likely that the compiler won't check that the function's signature is correct unless you also use an INTERFACE statement (which is not a bad idea).
PROGRAM testbutcher
USE butcher
IMPLICIT NONE
EXTERNAL polytrope !!!!!
REAL :: t = 0, dt = 0.01
REAL, DIMENSION(2) :: v0 = (/ 1.0, 0.0 /), fargs = (/ 1.0, 1.0 /)
v0 = step(polytrope, v0, 2, t, dt, fargs)
CONTAINS
END PROGRAM
!!!!!
FUNCTION polytrope(v0, t, fargs) result(v1)
REAL, DIMENSION(:) :: fargs
REAL, DIMENSION(2) :: v0, v1
REAL :: t
v1 = t * v0
RETURN
END FUNCTION
Related
I have a module which defines a Type, with its methods, plus some other Type-unrelated methods as well.
module TypeDef
type, public :: T
...
contains
...
procedure type_proc
end type
...
contains !module
subroutine type_proc( this, ... )
class(T), target :: this
...
call gen_proc( arg_1, arg_n-1, this, arg_n+1 )
...
end subroutine type_proc
...
subroutine gen_proc( arg_1, ..., arg_n-1, tv, arg_n+1 )
! this is a general module routine.
! NOT type related
implicit none
...
class(T), pointer, intent(in) :: tv
...
if ( cond ) tv%member = 0
...
end subroutine gen_proc
end module
At a given point, I call, from a variable type(T), public :: var its member method type_proc(), which at its interior calls a general module procedure gen_proc(). In there, for some conditions, I may need to change some member(s) of the ACTUAL object calling the method's tree (i.e. var). To do so, I pass this as a constant pointer to call gen_proc() to have its address passed at the function call, and access its members.
But, I get the error as described.
Same if I pass it by reference and not as a const pointer.
I cannot see if I do something mistakenly. Here Intel Fortran error #6633: The type of the actual argument differs from the type of the dummy argument there's something similar, but there, the call happens in a different program unit.
EDIT 2:
Ok, this compiled and run as expected..
module test
type, public :: T
real, allocatable :: m1(:)
real, allocatable :: m2(:)
integer :: may_change
contains
procedure :: t_proc
procedure :: all_t
end type
private :: all_t
contains
subroutine t_proc( this, n )
implicit none
class(T), target :: this
integer, intent(in) :: n
call this%all_t( n )
call gen_proc( n, this%m1, this%m2, this )
end subroutine t_proc
subroutine all_t( this, n )
implicit none
class(T) :: this
integer, intent(in) :: n
allocate( this%m1( n ) )
allocate( this%m2( n ) )
end subroutine
subroutine gen_proc( n, m1, m2, Tt )
implicit none
integer, intent(in) :: n
real, intent(in) :: m1(n), m2(n)
class(T), intent(in), pointer :: Tt
if ( .true. ) Tt%may_change = 1
print *, ' may change = ', Tt%may_change
end subroutine gen_proc
end module test
module varmod
use test
type(T), public :: var
end module varmod
program main
use varmod, only: var
implicit none
integer, parameter :: n = 2
var%may_change = 0
call var%t_proc( n )
end program main
So, even more than before, I don't know what could be wrong on the actual code...
This question already has answers here:
Passing external function of multiple variables as a function of one variable in Fortran
(2 answers)
Fortran minimization of a function with additional arguments
(2 answers)
Function with more arguments and integration
(1 answer)
Passing additional arguments in Newton’s method in Fortran
(2 answers)
Closed 1 year ago.
I have a function to compute Gaussian quadrature of a function $f(x)$ over the region $x \in [a,b]$. Here, $f(x)$ takes only one argument. I would want to know what a good practice would be to use gaussquad with a function which might take more arguments, for example getlaser below.
Laser is a derived type, and calling gaussquad(mylaser%getlaser, a, b) obviously does not work.
double precision function gaussquad(f, a, b) result(I)
implicit none
double precision :: f
double precision, intent(in) :: a, b
I = 2.d0*f(b-a)
end function
double precision function getlaser(this, t)
implicit none
class(Laser), intent(in) :: this
double precision, intent(in) :: t
getlaser = dsin(this%omega*t)
end function getlaser
The getlaser procedure has a user-defined dummy argument this which makes it impossible to define a general integration module.
In the following I will explain how to implement such a general integration module assuming standard data types.
One option would be to define an optional parameter array in gaussquad which can be passed through to the procedure f.
Following is a possible implementation for the integration module
! integ.f90
module integ_m
implicit none
private
public gaussquad
abstract interface
real function finter(x, p)
real, intent(in) :: x
real, optional, intent(in) :: p(:)
end function
end interface
contains
function gaussquad(f, a, b, p) result(int)
!! compute integral: int_a^b f(x; p) dx
procedure(finter) :: f
!! function to integrate
real, intent(in) :: a, b
!! integration bounds
real, optional, intent(in) :: p(:)
!! parameter array
real :: int
!! integral value
int = (b-a)*f(0.5*(a+b), p=p)
end function
end module
One would use it like in this program
! main.f90
program main
use integ_m, only: gaussquad
implicit none
print *, 'integrate x^2', gaussquad(parabola, 0.0, 1.0 )
print *, 'integrate laser (sin)', gaussquad(getlaser, 0.0, 1.0, [10.0])
contains
real function parabola(x, p)
real, intent(in) :: x
real, optional, intent(in) :: p(:)
if (present(p)) error stop "function doesnt use parameters"
parabola = x*x
end function
real function getlaser(t, p)
real, intent(in) :: t
real, optional, intent(in) :: p(:)
getlaser = sin(p(1)*t)
end function
end program
Compilation and running yields
$ gfortran -g -Wall -fcheck=all integ.f90 main.f90 && ./a.out
integrate x^2 0.250000000
integrate laser (sin) -0.958924294
I have function that returns an array, say
function f(A)
implicit none
real, intent(in) :: A(5)
real, intent(out) :: f(5)
f = A+1
end
My question is, how can I define f in the main program unit? E.g.
program main
implicit none
real :: A(5)
real, dimension(5), external :: f ! does not work
...
end
You need an explicit interface. You can do this in a few ways.
Explicitly in the scoping unit that calls f:
interface
function f(A)
implicit none
real, intent(in) :: A(5)
real :: f(5)
end function
end interface
Place the function in your program host scope as an internal function:
program main
...
contains
function f(A)
implicit none
real, intent(in) :: A(5)
real :: f(5)
f = A+1
end
end program
Place the function in a module:
module A
contains
function f(A)
implicit none
real, intent(in) :: A(5)
real :: f(5)
f = A+1
end
end module
program main
use A
...
end program
Use the explicit interface from a different procedure with the same arguments and return type, kind and rank.
program main
interface
function r5i_r5o(r5)
implicit none
real, intent(in) :: r5(5)
real :: r5i_r5o(5)
end function
end interface
procedure(r5i_r5o) :: f
...
end program
function f(A)
implicit none
real, intent(in) :: A(5)
real :: f(5)
f = A+1
end
The cleanest way of doing this is option #3 using modules. This gives you the benefit of an automatic explicit interface (not needing to do option #1 everywhere you call f) and makes your function available everywhere the module is used rather than limited to a specific scoping unit as in option #2. Option #4 can be handy if you have many procedures with the same argument and return types since one explicit interface can be re-used for all of them.
This shows three different ways to specify function results, and how to use modules to organize your functions:
module so_func
INTEGER, PARAMETER :: MAX_SIZE = 5
TYPE MY_DATA
INTEGER :: SIZE
REAL, DIMENSION(MAX_SIZE) :: DATA
ENDTYPE
contains
FUNCTION f1(A,N) RESULT(X)
implicit none
INTEGER, INTENT(IN) :: N
REAL, INTENT(IN) :: A(N)
REAL :: X(N)
! ....
X = 1.0+A
END FUNCTION f1
TYPE(MY_DATA) FUNCTION f2(A,N)
implicit none
INTEGER, INTENT(IN) :: N
REAL, INTENT(IN) :: A(N)
! ....
f2%SIZE = N
f2%DATA(1:N) = 1.0+A
END FUNCTION f2
FUNCTION f3(A,N)
implicit none
INTEGER, INTENT(IN) :: N
REAL, INTENT(IN) :: A(N)
REAL :: f3(N)
! ....
f3 = 1.0+A
END FUNCTION f3
end module
program SO_RESULT
use so_func
implicit none
integer, parameter :: n=5
REAL :: A(n), y1(n), y3(n)
TYPE(MY_DATA) :: y2
INTEGER :: i
! Variables
A =(/ (i, i=1,n) /)
y1 = f1(A,n)
y2 = f2(A,n)
y3 = f3(A,n)
end program SO_RESULT
I was using operator overloading for a vector class
interface operator(+)
module procedure :: vec_add_sv !< scalar + vector
module procedure :: vec_add_vs !< vector + scalar
module procedure :: vec_add_vv !< vector + vector
end interface operator(+)
:
:
function vec_add_sv( s, v1 ) result( v2 )
type(Vector) :: v2
real(dp), intent(in) :: s
type(Vector), intent(in) :: v1
integer :: i, n
n = v1%size()
call v2%resize(n)
do i=1,n
V2%q(i) = s + V1%q(i)
end do
end function vec_add_sv
:
:
v2 = s + v1
works just fine. However when I implemented:
interface assignment(=)
module procedure vector_assign
end interface assignment(=)
and put write statements in both the function vec_add_sv and the subroutine vector_assign I noticed that when I used
vec2 = scalar + vector
it called both the vec_add_sv function and the vector_assign subroutine. This indicates that the vec_add_sv function created a temporary vector for its return and then passed that temporary to the vector_assign subroutine. The result is that I am looping through my vector elements twice.
Is there a way to implement binary operators with out creating temporary storage?
I recognize that a temporary is needed if you do something like A = B + ( C * D ) then (C * D) crates a temporary that is used in B + temp.
But if I am just doing a single operation A = B + C
I would like something equivalent to
interface add
subroutine vec_add( A, B, C )
type(vector), intent(out) :: A
type(vector), intent(in) :: B
type(vector), intent(in) :: C
end subroutine vec_add
end interface add
interface operator(+) requires the module procedure to be a function not a subroutine
I'd like to pass a "pointer" to imaginary part of a complex Fortran array to a BLAS function that operates on real numbers only. I mean a "pointer" in a C language sense, as I do not want any data copying involved.
For example, consider the following simple code (my actual code is slightly more complicated):
function foo(c1, c2, n) result(r)
complex, dimension(:), intent(in) :: c1, c2
integer, intent(in) :: n
real :: r
real, external :: SDOT
r = SDOT(n, c1(1)%re, 2, c2(1)%im, 2)
end function foo
Unfortunately, %re and %im are specified in Fortran 2008 only. Moreover, I'm not sure the standard allows to apply complex part selector to the individual elements of array, as neither of my compilers support that.
gfortran complains with "Unexpected ‘%’ for nonderived-type variable".
Is there any other way to achieve what I need?
What I would do is to first create a subroutine with an explicit shape or assumed size real argument with one rank of size 2 added:
function foo(c1, c2, n) result(r)
complex, dimension(:), intent(in) :: c1, c2
integer, intent(in) :: n
real :: r
real, external :: bar
r = bar(c1, c2, n)
end function foo
function bar(c1, c2, n) result(r)
real, dimension(2,n), intent(in) :: c1, c2
integer, intent(in) :: n
real :: r
r = SDOT(n, c1(1,:), 2, c2(2,:), n)
end function
Keep bar in a different source file and with implicit interface so that the compiler does not complain. For discussion about validity of this see Is the storage of COMPLEX in fortran guaranteed to be two REALs?
Unfortunately, it will still create temporary copies of the arrays. Only if you used assumed shape arguments in SDOT and have explicit interface for that it would actually help to avoid copies.
Even if you created Fortran real pointers for the real and imaginary part, still a temporary copy would be made if you passed it to an external function.
As per suggestion of #Vladimir F, I ended up with the following code.
bar.F file:
function bar(c1, c2, n) result(r)
real, intent(in) :: c1(*), c2(*)
integer, intent(in) :: n
real :: r
r = SDOT(n, c1(1), 2, c2(2), 2)
end function bar
foo.F file:
function foo(c1, c2, n) result(r)
complex, dimension(:), intent(in) :: c1, c2
integer, intent(in) :: n
real :: r
real, external :: bar
r = bar(c1, c2, n)
end function foo
function foo(c1, c2, n) result(r)
complex, dimension(:), intent(in) :: c1, c2
integer, intent(in) :: n
real :: r
REAL, DIMENSION(n) :: reality, dreamy
real, external :: SDOT
Reality = REAL(c1)
DReamy = IMAG(c2)
r = SDOT(n, Reality, 1, Dreamy, 1)
end function foo
As AIMAG and REAL are ELEMENTAL, you should be able to put them In the SDOT call as they be a temporary vector on the stack.