I have the following piece of code that splits the vector x
into several arrays.
subroutine split(n, x, r, v, p, t)
implicit none
integer, intent(in) :: n
double precision, intent(in) :: x(n)
integer, parameter :: m = (n + 6) / 10
! problem here 1
double precision, intent(out) :: r(3, m - 1)
double precision, intent(out) :: v(3, m - 1)
double precision, intent(out) :: p(3, m)
double precision, intent(out) :: t(m)
! code
end subroutine split
This code does not compile with the message
Error: Parameter 'n' at (1) has not been declared or is a variable, which does not reduce to a constant expression
The code compiles fine if I manually change all m
to (n + 6) / 10
but I am seeking a more elegant approach.
As an alternative approach I've rewritten the code as
subroutine splitcore(n, m, x, r, v, p, t)
implicit none
integer, intent(in) :: n, m
double precision, intent(in) :: x(n)
double precision, intent(out) :: r(3, m - 1)
double precision, intent(out) :: v(3, m - 1)
double precision, intent(out) :: p(3, m)
double precision, intent(out) :: t(m)
! code
end subroutine splitcore
subroutine split(n, x, r, v, p, t)
implicit none
integer, intent(in) :: n
double precision, intent(in) :: x(n)
integer :: m
double precision, intent(out) :: r(3, *)
double precision, intent(out) :: v(3, *)
double precision, intent(out) :: p(3, *)
double precision, intent(out) :: t(*)
m = (n + 6) / 10
call splitcore(n, m, x, r, v, p, t)
end subroutine split
Array specifications for array declarations in subprograms are permitted to be specification expressions.
A specification expression can include a reference to a pure function. You can use such a pure function to factor out the effective calculation of m
.
To be considered pure in its scope of use, an explicit interface for a pure function must be accessible. The simplest way of providing such an explicit interface is to put the function in a module (which, if split
was already in such a module, could be the same module).
module m_mod
implicit none
contains
pure function m(n)
integer, intent(in) :: n
integer :: m
m = (n + 6) / 10
end function m
end module m_mod
subroutine split(n, x, r, v, p, t)
use m_mod
implicit none
integer, intent(in) :: n
double precision, intent(in) :: x(n)
double precision, intent(out) :: r(3, m(n) - 1)
double precision, intent(out) :: v(3, m(n) - 1)
double precision, intent(out) :: p(3, m(n))
double precision, intent(out) :: t(m(n))
...
In terms of the original code - the initializer for a variable, constant or type parameter has to be a constant expression - effectively something that the compiler can evaluate at compile time. A constant expression has more restrictions on it than a specification expression - for example it cannot reference the value of a variable - because variables are not constants.
Rather than an explicit shape array, the dummy variables in the split
subroutine could perhaps be made assumed shape (declared with (:)
or (:,:)
, as appropriate for the rank. The specification of the shape of the array is then taken "(assumed") from the shape of the actual arguments, no shape calculations need be done in subroutine split
at all.
Use of a subroutine with assumed shape dummy arguments requires an explicit interface for the procedure to be accessible in the scope where the procedure is referenced.