subroutine argument with unknown rank (shape) in fortran - fortran

I am wondering how to best handle in Fortran a subroutine that takes an argument of unknown rank. For instance:
Real * 8 :: array1(2,2),array2(2,2,3)
call mysubroutine(array1)
call mysubroutine(array2)
As for now, I always need to fix the shape (number of rank) in the subroutine.
For instance, the intrinsic subroutine random_number(array) can do. (But maybe it is not coded in Fortran?)

You have to write a specific subroutine for each array rank, but you create a generic interface so that you can use a generic call for all the ranks and don't have to figure out the specific one to call. There is example code at how to write wrapper for 'allocate'

In case you need to fill the arrays elementwise and those operations are independent of each other, you may consider alternative to the suggestion of M. S. B. to use an elemental function. In this case you write the function for a scalar (one element) and it gets automatically applied to all elements of the array irrespective how the shape of the array looks like. However, your scalar function must be satisfy the conditions posed on an elemental routine, basically meaning that is not allowed to cause any side effects, which would make your result depend on the order it is applied to the individual array elements.
Below a demonstration, which multiplies each element of the array by two:
module testmod
implicit none
integer, parameter :: dp = kind(1.0d0)
contains
elemental subroutine mul2(scalar)
real(dp), intent(inout) :: scalar
scalar = scalar * 2.0_dp
end subroutine mul2
end module testmod
program test
use testmod
implicit none
real(dp) :: a1(5), a2(3,2)
a1 = 1.0_dp
a2 = 2.0_dp
call mul2(a1)
call mul2(a2)
print *, a1
print *, a2
end program test

Related

Is declaring the size (number of elements) of dummy aguments in Fortran bad practice [duplicate]

I'm trying to decide which one of these two options would be the best:
subroutine sqtrace( Msize, Matrix, Value )
integer, intent(in) :: Msize
real*8, intent(in) :: Matrix(Msize, Msize)
real*8, intent(out) :: Value
[instructions...]
end subroutine sqtrace
VS
subroutine sqtrace( Matrix, Value )
real*8, intent(in) :: Matrix(:,:)
real*8, intent(out) :: Value
if ( size(Matrix,1) /= size(Matrix,2) ) then
[error case instructions]
end if
[instructions...]
end subroutine sqtrace
I understand that when you compile with warnings, the first case should automatically check at compile time if calls to sqtrace comply with the size indicated. However, I don't know if the compiler can perform those checks when the given arguments are allocatable, for example (more so if such allocation depends on other things that are determined at runtime). The second one requires an explicit interface and has more code (the checks), but would seem to catch more errors.
Which are the advantages/disadvantages of using each and in which cases should one go with one over the other?
First, some terminology. Consider the dummy arguments declared as
real :: a(n) ! An explicit shape array
real :: b(:) ! An assumed shape array
real, allocatable :: c(:) ! A deferred shape array (allocatable)
real, pointer :: d(:) ! A deferred shape array (pointer)
real :: e(*) ! An assumed size array
real :: f(..) ! An assumed rank array/scalar
I won't answer in terms of which is better in a given situation, but will simply detail some of the important characteristics leaving choice, where there is one, to the programmer. Crudely (and incorrectly), many view explicit shape arrays as "Fortran 77" and assumed and deferred shape arrays as "Fortran 90+".
Assumed size and assumed rank arguments are beyond the scope of this question and answer.
Shape:
the shape of an explicit shape array follows its declaration;
the shape of an assumed shape array dummy argument is that of the actual argument;
the shape of a deferred shape dummy argument may be undefined, becoming defined in the procedure, or that of the actual argument.
Contiguousness:
an explicit shape array is simply contiguous;
an assumed shape array dummy argument's contiguousness relates to that of the associated actual argument;
a deferred shape dummy argument may be that of the actual argument, or depending on the procedure's execution.
Restrictions on actual argument:
an actual argument associated with an explicit shape array must have at least as many elements as the dummy argument;
an actual argument associated with an assumed shape array must not itself be assumed size;
an actual argument associated with an assumed or deferred shape array must be of the same rank as the dummy argument.
Interfaces in the calling scope:
if a dummy argument is of assumed or deferred shape, the referencing scope must have accessible an explicit interface for the procedure.
Consider real a(6). This may be an actual argument to the dummies
real b(3)
real c(2,3)
real d(:) ! With an explicit interface available
a(1::2) may be associated with b but as b is contiguous copy-in/copy-out will be involved. Copy-in/copy-out needn't be involved when associated with d, but it may be.
There are plenty of other aspects, but hopefully this is an initial high-level introduction.

Parametrized derived types with kind parameter as subroutine argument

Say we have the following code:
module foo
use :: iso_fortran_env
implicit none
type :: bar (p, q)
integer, kind :: p
integer, len :: q
integer(kind = p), dimension(q) :: x
end type bar
contains
subroutine barsub (this)
class(bar(*,*)), intent(in) :: this
write (*,*) this%x
end subroutine barsub
end module foo
This code does not compile with either gfortran 8 or pgfort 18.4. The pgi compiler says
Illegal selector - KIND value must be non-negative
Assumed type parameter (*) cannot be used with non-length type parameter p
whereas gfortran yields
The KIND parameter 'p' at (1) cannot either be ASSUMED or DEFERRED
If I change above code to
subroutine barsub (this)
class(bar(INT32,*)), intent(in) :: this
write (*,*) this%x
end subroutine barsub
it compiles fine with both compilers.
Is it possible to write a subroutine where the kind parameter does not need to be specified explicitly? In the example above, the code would be the same for INT32, INT64, ... and I don't want to copy paste it for every imaginable value of the kind parameter. It works fine for the len parameter. Why can't I do the same with the kind parameter?
Is it possible to write a subroutine where the kind parameter does not need to be specified explicitly?
No, kind type parameters need to be given by a constant expression or defaulted, see, e.g., the Fortran 2008 standard, definition 1.3.147.12.3.
Why can't I do the same with the kind parameter?
The fact that len and kind type parameters have different uses and requirements is the point of having two types of type parameters, if their characteristics were the same we wouldn't need two of them.
Note that procedures require of the kind parameters of their dummy arguments of parameterized derived types just same they require of the kinds of their dummy arguments of intrinsic types: to have their values defined at compilation time.

Passing size as argument VS assuming shape in Fortran procedures

I'm trying to decide which one of these two options would be the best:
subroutine sqtrace( Msize, Matrix, Value )
integer, intent(in) :: Msize
real*8, intent(in) :: Matrix(Msize, Msize)
real*8, intent(out) :: Value
[instructions...]
end subroutine sqtrace
VS
subroutine sqtrace( Matrix, Value )
real*8, intent(in) :: Matrix(:,:)
real*8, intent(out) :: Value
if ( size(Matrix,1) /= size(Matrix,2) ) then
[error case instructions]
end if
[instructions...]
end subroutine sqtrace
I understand that when you compile with warnings, the first case should automatically check at compile time if calls to sqtrace comply with the size indicated. However, I don't know if the compiler can perform those checks when the given arguments are allocatable, for example (more so if such allocation depends on other things that are determined at runtime). The second one requires an explicit interface and has more code (the checks), but would seem to catch more errors.
Which are the advantages/disadvantages of using each and in which cases should one go with one over the other?
First, some terminology. Consider the dummy arguments declared as
real :: a(n) ! An explicit shape array
real :: b(:) ! An assumed shape array
real, allocatable :: c(:) ! A deferred shape array (allocatable)
real, pointer :: d(:) ! A deferred shape array (pointer)
real :: e(*) ! An assumed size array
real :: f(..) ! An assumed rank array/scalar
I won't answer in terms of which is better in a given situation, but will simply detail some of the important characteristics leaving choice, where there is one, to the programmer. Crudely (and incorrectly), many view explicit shape arrays as "Fortran 77" and assumed and deferred shape arrays as "Fortran 90+".
Assumed size and assumed rank arguments are beyond the scope of this question and answer.
Shape:
the shape of an explicit shape array follows its declaration;
the shape of an assumed shape array dummy argument is that of the actual argument;
the shape of a deferred shape dummy argument may be undefined, becoming defined in the procedure, or that of the actual argument.
Contiguousness:
an explicit shape array is simply contiguous;
an assumed shape array dummy argument's contiguousness relates to that of the associated actual argument;
a deferred shape dummy argument may be that of the actual argument, or depending on the procedure's execution.
Restrictions on actual argument:
an actual argument associated with an explicit shape array must have at least as many elements as the dummy argument;
an actual argument associated with an assumed shape array must not itself be assumed size;
an actual argument associated with an assumed or deferred shape array must be of the same rank as the dummy argument.
Interfaces in the calling scope:
if a dummy argument is of assumed or deferred shape, the referencing scope must have accessible an explicit interface for the procedure.
Consider real a(6). This may be an actual argument to the dummies
real b(3)
real c(2,3)
real d(:) ! With an explicit interface available
a(1::2) may be associated with b but as b is contiguous copy-in/copy-out will be involved. Copy-in/copy-out needn't be involved when associated with d, but it may be.
There are plenty of other aspects, but hopefully this is an initial high-level introduction.

Interface mismatch in dummy procedure 'f' when passing a function to a subroutine

I am trying to write a subroutine (for minimisation) that has two arguments:
an array x of any length
a function f that takes an array of that length and returns a scalar
example module:
module foo
contains
subroutine solve(x, f)
real, dimension(:), intent(inout) :: x
interface
real pure function f(y)
import x
real, dimension(size(x)), intent(in) :: y
end function
end interface
print *, x
print *, f(x)
end subroutine
end module
and test program:
use foo
real, dimension(2) :: x = [1.0, 2.0]
call solve(x, g)
contains
real pure function g(y)
real, dimension(2), intent(in) :: y
g = sum(y)
end function
end
gfortran fails on:
call solve(x, g)
1
Error: Interface mismatch in dummy procedure 'f' at (1): Shape mismatch in dimension 1 of argument 'y'
If I change size(x) => 2 then it compiles (and runs) fine. It also works fine if I change : => 2. But neither of these solutions gets me what I want.
Any ideas on how I can achieve this?
How about:
interface
real pure function f(y)
real, dimension(:), intent(in) :: y
end function
end interface
When you pass the argument of solve to the function, the size of the array will automatically be passed. You don't need to make this part of the interface.
If you want to gain safety as indicated in your comment of M.S.B's solution, you should use -fcheck=bounds and the compiler will generate run-time checks for assumed and deferred shape arrays. See the gfortran man page for more info on -fcheck. However, you will lose some speed.
You have the solution, but for what its worth an explanation... if the dummy argument has an explicit interface (which it does here) then there is a requirement that the characteristics of a procedure passed as an actual argument must match those of the dummy argument, with some exceptions around pureness and elemental intrinsics. The characteristics of a procedure include the characteristics of its dummy arguments, amongst other things.
The characteristics of a dummy argument include its shape, amongst other things. If that shape is not a constant expression - the characteristics include "the exact dependence [of the shape] on the entities in the expression".
The interface block for the dummy argument f declares the array to be of size SIZE(x). x is a host associated assumed shape variable - its size can vary at runtime, so SIZE(x) is not a constant. Hence that expression and the entities in it becomes a characteristic of the dummy argument.
The module procedure g declares the array to be of size 2. That is clearly a constant.
Regardless of the value of the non-constant expression for the size of the dummy argument of f, those array size characteristics (some sort of expression vs a constant) don't match - hence the error.
When you replace SIZE(x) with the constant 2 the characteristics obviously match. When you change the assumed shape x to be a constant size 2 - then SIZE(x) becomes a constant expression of value 2 - because it is a constant expression all that is relevant is its value - hence the characteristics of the two arguments then match. When you change both the dummy argument of f and the dummy argument of g to be assumed shape (:), the characteristics match.
Here is a demo to show how to pass allocatable array.
Some tips:
Use modules to avoid cumbersome interface.
Add extra matrix size information when pass array to the actual function. For example f(y, sizeinfo) so that inside your actual function you can declare the size of the input matrix correctly. The allocatable array can be passed to subroutine solve, so the size can be obtained using size(mat) in your subroutine solve.
So a corrected version looks like:
module foo
contains
subroutine solve(x, f)
real, dimension(:), intent(inout) :: x
real,external::f
integer::sizeinfo
print *,'x=', x
sizeinfo = size(x)
print *, 'f(x)=',f(x,sizeinfo)
end subroutine
real function g(y,sizeinfo)
integer::sizeinfo
real, dimension(sizeinfo) :: y
g = sum(y)
end function
end module
Here is main program:
program main
use foo
real, dimension(2) :: x = (/1.0, 2.0/)
call solve(x, g)
end program
And the result is :
x= 1.000000 2.000000
f(x)= 3.000000

No lifting of scalar arguments to arrays in Fortran

Why is it that Fortran will promote a scalar expression to an array, in an expression, but not as an argument to a procedure? In particular, why did the standards body make this design decision? Is it solely because of ambiguity, should the procedure be overloaded? Could an error message in that situation be an alternative approach?
For example, In the code below, the last statement, x = foo(7), produces the GFortran error: Error: Rank mismatch in argument 'a' at (1) (1 and 0).
module m
public :: foo
contains
function foo(a) result(b)
integer, dimension(:) :: a
integer, dimension(size(a)) :: b
b = a+1
end function foo
end module m
program p
use m
integer, dimension(4) :: x
integer, parameter, dimension(4) :: y = (/1,2,3,4/)
x = 7
x = foo(x)
x = foo(y)
x = foo(x + 7)
x = foo(7)
end program p
This question should have asked about why an array assignment will promote a scalar value source to an array target; unlike an array function. I expect that's simply a convenient special case though. Any comments gratefully received in the begging caps below.
If you want the function to handle scaler and array arguments, declare it as "elemental" and with scaler dummy arguments. Then it will be able to handle both scaler and array actual arguments, including scaler expressions. Will that meet your need?
The change:
elemental function foo(a) result(b)
integer, intent (in) :: a
integer :: b
b = a+1
end function foo
Perhaps they provided a way to do what you want, and one way was enough?
Procedure calling in Fortran with explicit interfaces (which you get automatically when using module procedures) requires a TKR (type, kind, rank) match. As an array is a different type than a scalar, not to mention the rank mismatch, this is not allowed.
Is it because of ambiguity should the procedure be overloaded?
That would be a problem, yes.
Could an error message in that situation be an alternative approach?
Could pink unicorns exist? Maybe, but to the best of my knowledge they don't. IOW, the Fortran standard currently requires TKR matching, and thus a standard conforming compiler must enforce this requirement. If you want to change that, I recommend making a proposal to the standards committee.
I would think the answer to this is pretty clear. Let's slightly modify your example:
module m
public :: foo
contains
function foo(a) result(b)
integer, dimension(:) :: a
integer, dimension(size(a)) :: b
b = a+1
a(2) = -777 ! new line; modify one element
end function foo
end module m
program p
use m
integer :: x
integer, dimension(4) :: y
x = 7
y = foo(x) ! pass a scalar
end program p
what is x supposed to be after the call to foo?
Now, sure, you could have the semantics of argument passing change depending on whether or not it's an intent(in) variable, but that is not something which is going to clarify things for programmers.
If the function call is supposed to 'distribute' somehow over array elements, then as MSB points out, elemental is the way to go. Otherwise, one just makes sure one's arguments match one's parameters.