Smart way to pass arguments in Fortran 90 - fortran

I am a Fortran novice. I am trying to write a subroutine that will take in four arguments from the main program, and then outputs to the main program an array that involves the four arguments that were originally passed in. What is a good/smart way to do this?
For example, in my test program below, I create four real variables (a, b, c, and d) in the main program. Then I pass these real variables to a subroutine called mysub. I would like mysub to be able to take in a, b, c, and d, use them to populate a 2-by-2 array called o, and then send o to the main program for displaying (and possible modification) there. So, I tried the following:
SUBROUTINE mysub(w,x,y,z)
IMPLICIT NONE
REAL, INTENT(IN) :: w, x, y, z
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: o
ALLOCATE(o(2,2))
o(1,1)=w
o(1,2)=x
o(2,1)=y
o(2,2)=z
END SUBROUTINE mysub
END MODULE testsubs
PROGRAM test
USE testsubs
IMPLICIT NONE
REAL :: a=1.1, b=2.2, c=3.3, d=4.4
CALL mysub(a, b, c, d)
PRINT *, o(1,1), o(1,2)
PRINT *, o(2,1), o(2,2)
END PROGRAM test
But, I get the following error:
test.f90:10.53:
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: o
1
Error: Symbol at (1) is not a DUMMY variable
I interpret this as, the compiler doesn't know what o is, because o is not in the list of arguments in the subroutine header: SUBROUTINE mysub(w,x,y,z). So I probably need to include o in that header. So, I next try the following (where I have denoted changes or additions using !...):
SUBROUTINE mysub(w,x,y,z,o) !...
IMPLICIT NONE
REAL, INTENT(IN) :: w, x, y, z
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: o
ALLOCATE(o(2,2))
o(1,1)=w
o(1,2)=x
o(2,1)=y
o(2,2)=z
END SUBROUTINE mysub
END MODULE testsubs
PROGRAM test
USE testsubs
IMPLICIT NONE
REAL :: a=1.1, b=2.2, c=3.3, d=4.4
REAL, DIMENSION(:,:), ALLOCATABLE :: o !...
CALL mysub(a, b, c, d, o) !...
PRINT *, o(1,1), o(1,2)
PRINT *, o(2,1), o(2,2)
DEALLOCATE(o) !...
END PROGRAM test
This seems to work fine, and I get the correct output:
1.1000000 2.2000000
3.3000000 4.4000001
But, my question is, is this a good way to do this? In this working example, I'm declaring the array o both in the subroutine and in the main program. This seems potentially confusing, because I think that this means that I need to take care that either the subroutine or the main program allocates o (but not both, I think, in order to avoid error messages). Is there a smarter way to do this--to send an array from a subroutine to the main program? Thank you for your time.

Your solution, making "o" an intent(out) argument is just fine. Without "o" being an argument there was no connection between the variable "o" in the subroutine and the variable "o" in the main program, and therefore there was no declaration or allocation of the one in the main program. Yet another solution (besides the one provided by #ja72) would be to alter your method: make "o" an intent(inout) argument of the subroutine and allocate it in the main program. Possible advantage: the allocate and deallocate are closer together in the code and paired. Possible disadvantage: depending on the the program logic and design, the array dimensions might be best known to the subroutine.
P.S. If you allocate the array in the main program, and don't actually use allocatable properties of the array in the subroutine (i.e., you don't allocate or deallocate it), then you don't have to declare it with the allocatable attribute in the subroutine -- a useful simplification. In which case "intent (out)" might be appropriate. But if you allocate the array in the main program and wish to pass that status to a subroutine, then the argument status can't be "intent (out)". "intent (out)" automatically deallocates the argument upon entry to the procedure.

If you want to return an array you can a) add it to the arguments with INTENT(OUT) like your example #2 with allocation within the sub, or b) create a function and allocate the array externally:
FUNCTION myfun(w,x,y,z,n,m)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n,m
REAL, DIMENSION(n,m) :: myfun
REAL, INTENT(IN) :: w,x,y,z
myfun(1,1)=w
myfun(1,2)=x
myfun(2,1)=y
myfun(2,2)=z
END FUNCTION
END MODULE testsubs
PROGRAM test
USE testsubs
IMPLICIT NONE
REAL :: a=1.1, b=2.2, c=3.3, d=4.4
REAL, DIMENSION(:,:), ALLOCATABLE :: o !...
ALLOCATE(o(2,2))
o = myfun(a,b,c,d,2,2)
PRINT *, o(1,1), o(1,2)
PRINT *, o(2,1), o(2,2)
DEALLOCATE(o) !...
END PROGRAM test
Actually I think your solution is cleaner.

It is not clear from your question if you know in advance the size of the array or not, but if you do, it is usually a good practice to allocate and deallocate the array in the same place, or even better is to leave the compiler allocate memory when possible.
How I would do:
if the size is known at compile time:
Declare your array in the main program, use intent(out) in a subroutine.
if the size is known only at runtime:
Allocate in the main program, use intent(out) in a subroutine and deallocate in the main program.
Functions are best suited for small outputs, since the outputs needs to be copied.

Related

Why an allocated array gets unallocated when it is passed to a subroutine?

Passing an allocatable array via actual argument to a subroutine whose corresponding dummy argument is defined as an allocatable array:
module m
real, allocatable :: a(:,:)
end module m
module m2
contains
subroutine p(c)
implicit none
real, allocatable, intent(out):: c(:,:)
write(*,*) 'allocated?=', allocated(c)
end subroutine p
end module m2
program main
use m, only : a
use m2, only: p
implicit none
allocate(a(3,3))
write(*,*) 'allocated?=', allocated(a)
call p(a)
end program main
The output:
allocated?= T
allocated?= F
The first is as expected, but why the allocated status becomes false, as is indicated by the second output?
Allocatable dummy arguments that have intent(out) are automatically deallocated when the procedure is entered according to thevstandard. The intent also affects the allocation status, not just the values inside.
It actually makes a very good sense. If it is intent(out), any previous allocation status should not matter.

Overloading function in Fortran and polymorphic derived data types

I have a program dealing with calculations for an engine. There are multiple limiters that need to be considered (pressure ratio, temperature, ect.). These are organized from users perspective in groups, with some parameters common to all groups and some not.
Because during the run time I need to work with these limiters depending on the requirements, potentially changing them during various calculation steps it would make sense to organize these in an array of polymorphic elements, depending on what each limiter group needs. In principle it works, but not quite how I want to.
I wrote a small program to test different method shown below:
Here is the module with derived types etc.
module ArrayTest
interface init_limiter
module procedure :: initGroup1, initGroup2
end interface
type :: base
contains
procedure, pass :: setup => idontwanttodothis
procedure, pass :: print_param
end type base
type, extends(base) :: Group1
real :: p1
contains
procedure, pass :: init => initGroup1
procedure, pass :: print_param => printGroup1
end type Group1
type, extends(base) :: Group2
integer :: p1
real :: rDummy
contains
procedure, pass :: init => initGroup2
procedure, pass :: print_param => printGroup2
end type Group2
type ArrElem
integer :: a, b, c
class(base), allocatable :: param
end type ArrElem
type(ArrElem), dimension(5) :: T1, T2
contains
subroutine idontwanttodothis(self, iDummy, rDummy)
class(base) :: self
integer, optional :: iDummy
real, optional :: rDummy
select type (self)
type is(group1); call self.init(rDummy)
type is(group2); call self.init(iDummy,rDummy)
end select
end subroutine idontwanttodothis
subroutine print_param(self)
class(base) :: self
select type(self)
type is(group1); call self.print_param()
type is(group2); call self.print_param()
class default; write(*,'(A)') 'Type:: Unknown'
end select
end subroutine print_param
pure subroutine initGroup1(self, x)
class(Group1), intent(inout) :: self
real, intent(in) :: x
self.p1 = x
end subroutine initGroup1
pure subroutine initGroup2(self, x, y)
class(Group2), intent(inout) :: self
integer, intent(in) :: x
real, intent(in) :: y
self.p1 = x
self.rDummy = y
end subroutine initGroup2
subroutine printGroup1(self)
class(Group1) :: self
write(*,'(A,F5.2)') 'Type:: Group1 ',self.p1
end subroutine printGroup1
subroutine printGroup2(self)
class(Group2) :: self
write(*,'(A,I2,F5.2)') 'Type:: Group2 ',self.p1, self.rDummy
end subroutine printGroup2
end module ArrayTest
And here is the main program:
program TestAlloc
use ArrayTest
call main()
contains
subroutine main
integer i
type(group1) :: g1Dummy
!Option 1
g1Dummy.p1 = 29
allocate(T1(1).param, source = g1Dummy)
!Option 2
allocate(Group2::T1(2).param)
select type(dummy => T1(2).param)
type is(Group2); call dummy.init(12,8.7)
end select
!Option 3
allocate(Group2::T1(3).param)
call T1(3).param.setup(3, 4.5)
!Option 4
allocate(Group1::T1(4).param)
call init_limiter(T1(4).param, 8.) !this does not work
call init_limiter(g1Dummy, 8.) !this works
T2 = T1
do i=1,5
if(allocated(T2(i).param)) call T2(i).param.print_param()
end do
return
end subroutine main
end program TestAlloc
Options 1, 2 and 3 work. Option 4 doesn't. Is there any way to make this work? i.e. overload a function call for an allocatable parameter?
p.s. Overriding inherited function through child will work, but that will require both parent and children to have the same interface, which I find inconvenient, might as well use option 3 then.
To my knowledge, there is no way to make this work.
As far as the compiler is concerned, at compile time T1(4).param is of class(base), and it only becomes type(Group1) at runtime. Since you have not defined init_limiter for class(base), only for class(Group1) and class(Group2), the compiler has no appropriate init_limiter function to call.
Your init_limiter functions are not polymorphic, they simply share an interface, so the compiler has no way of treating them the same at compile time and calling the correct one at runtime using polymorphism.
p.s. Overriding inherited function through child will work, but that will require both parent and children to have the same interface, which I find inconvenient, might as well use option 3 then.
This is essentially the crux of your problem. You want to call a function with a different number of arguments depending on the runtime type of an object. Fortran is not set up to handle this case; the number and type of function arguments must be known at compile time.
One potential workaround, which you might or might not consider an improvement, is to use the select type construct. This allows you to turn runtime information (the type of T1(4).param) into compile time information (the signature of the function to call). This would look something like
allocate(Group1::T1(4).param)
select type(foo => T1(4).param); type is(Group1)
call init_limiter(foo, 8.)
end select

Generalizing your operation for a specific declared type in Fortran

I have a Structure of arrays using the declared type in Fortran
e.g.
type t_data
integer :: N
real, allocatable :: x(:)
real, allocatable :: z(:)
real, allocatable :: y(:)
contains
procedure :: copy
procedure :: SWAP
procedure :: copy_element
end type
! constructor
interface t_data
module procedure constructor
end interface
contains
subroutine copy(this, old)
class(t_data), intent(inout) :: this
type(t_data), intent(in) :: old
do i = 1, old% N
this% x(i) = old% x(i)
etc ..
end do
end subroutine
subroutine copy(this, old)
class(t_data), intent(inout) :: this
type(t_data), intent(in) :: old
do i = 1, old% N
this% x(i) = old% x(i)
etc ..
end do
end subroutine
function constructor(size_)
integer, intent(in) :: size_
type(t_data), :: constructor
allocate(constructor% x(size_))
allocate(constructor% y(size_) )
! etc
end function
subroutine swap(this, from, i1,i2)
class(t_particle_data), intent(inout) :: this
type(t_particle_data), intent(in) :: from
integer, intent(in) :: i1, i2
this% x(i1) = from% x(i2)
! etc
end subroutine
These are a set of examples of procedures that need to do same operations on all arrays of the declared type t_data. My question is how to make it more maintainable to tackle the situation when we for example later want to add a new component to the declared type.
Currently, when I add a new array to my t_data, I need to go through all those procedures, constructors, deconstructors, and add the component.
I am asking if there is a way to make this more easier.
MY APPLICATION
Please note that these data type is used for particle simulation. Initially I allocate t_data with a large number. However, later during my simulation I might need more particles. Hence, I allocate a new t_data with more memory and copy over the old t_data up to its old size.
subroutine NEW_ALLOC(new, old)
type(t_data), intent(out) :: new
type(t_data), intent(inout) :: old
nsize = old% N * 2 ! allocate twice the old size
new = T_DATA(nsize)
call new% copy(old)
!DEALLCOte OLD
end subroutine
Does anybody has/is it possible to make this in a more clever way. I do not mind mixing this with C/C++?
My question is how to make it more maintainable to tackle the situation when we for example later want to add a new component to the declared type.
Here's how I would tackle the situation, and how many Fortran programmers have tackled the situation. I don't see the compelling need to have a derived type containing 3 arrays of coordinates, and approaching the problem that way does, as OP fears, require that adding another dimension to the problem requires code revision, such as adding a member array real, allocatable :: w(:) to t_data and recoding all the type-bound procedures operating on the type.
So drop that approach in favour of
TYPE t_data
REAL, DIMENSION(:,:), ALLOCATABLE :: elements
END TYPE t_data
let's have a couple of instances for exposition
TYPE(t_data) :: t1 ,t2, t3
and we can allocate the elements member of any of these this way
ALLOCATE(t1%elements(3,10))
which could just as easily be
ALLOCATE(t1%elements(6,100))
or whatever you wish. This has the advantage over the original derived type design that the dimensions of elements can be determined at run-time. It also makes it difficult to have different lengths for each of the coordinate arrays.
Now, copying t1 is as simple as
t2 = t1
Modern Fortran even takes care of automatically allocating the elements of t2. So I don't see any need for defining procedures for copying whole instances of t_data. As for swapping data around, slicing and dicing, this is as simple as
t2%elements(1,:) = t1%elements(2,:)
even
t2%elements(1,:) = t1%elements(1,6:1:-1)
or
t2%elements(1,:) = t1%elements(1,[1,3,5,2,4,6])
It should be obvious how to wrap these into a swap routine. But if not, ask another question.
Next, to the point about needing to allocate more space for elements during execution. First a temporary array
REAL, DIMENSION(:,:), ALLOCATABLE :: temp
then a little code like this, to double the size of elements.
ALLOCATE(temp(3,2*n))
temp(:,1:n) = t2%elements(:,1:n)
CALL MOVE_ALLOC(to=t2%elements,from=temp)
Again, you might care to wrap this into a procedure and if you need help doing that, ask for it.
Finally, the lesson of all this is not to share how I would program the problem, but to share the idea to program in Fortran.

Reading function from a file in Fortran 90

I have an optimization solver in Fortran 90. So, if I want to change the objective function
I have to modified the main file and write the objective function in this way:
subroutine fobj(n,x,f)
implicit none
integer :: n
real(8) :: f
real(8) :: x(n)
intent(in ) :: n,x
intent(out) :: f
!OBJECTIVE FUNCTION
f = x(1)**2-x(2)+2*x(3)
end subroutine fobj
I have a big objective function, so I want to call this line "f = x(1)**2-x(2)+2*x(3)" from an external file or at least the subrutine.
Is that possible? (I'm new in Fortran.)
I know that I can modified the file with Python, but I want to do it in other file.
Thanks a lot!
Sure. Use:
include 'file.inc'
to include source code from an external file.
I'm not sure if this is what you're looking for, but:
Fortran also allows you to pass subroutine/function names around as actual arguments to subroutine/function calls. The corresponding dummy arguments must have the "external" attribute.
subroutine fobj(n,x,f,func)
implicit none
integer :: n
real(8),external :: func
real(8) :: f
real(8) :: x(n)
intent(in ) :: n,x
intent(out) :: f
!OBJECTIVE FUNCTION
f=func(x,n)
end subroutine fobj
function func1(x,n)
implicit none
real(8) func1
integer n
real(8) :: f,x(n)
f = x(1)**2-x(2)+2*x(3)
end function func1
function func2(x,n)
implicit none
real(8) func2
integer n
real(8) :: f,x(n)
f = x(1)**2+x(2)+2*x(3)
end function func2
program main
real(8),external :: func1,func2
real(8),allocatable :: x(:)
real(8) :: f
integer n
n=50
allocate(x(n))
x=10. !Set X to a known value
call fobj(n,x,f,func1) !Call func1
print*,f !10**2-10+2*10 = 110
x=10. !Reset X ... just to make sure there is no funny business in func1,func2
call fobj(n,x,f,func2) !Call func2
print*,f !10**2+10+2*10 = 130
deallocate(x)
end program main
Of course, this program does nothing useful other than call func1 and func2 in obscure ways, but hopefully it illustrates the point. If you're looking to switch out the function at compile-time, then I think a include "myfile" is probably cleaner (just switching which file you're including at the time as suggested by #AlejandroLL)
You might also try to use Modules in your program. Sometimes when you pass special variables to your subroutines/functions you need to write interfaces for them. Using modules will improve your program structure and you'll be more effective and all interfaces would be generated automatically.

changing array dimensions in fortran

There are basically two ways to pass arrays to a subroutine in Fortran 90/95:
PROGRAM ARRAY
INTEGER, ALLOCATABLE :: A(:,:)
INTEGER :: N
ALLOCATE(A(N,N))
CALL ARRAY_EXPLICIT(A,N)
! or
CALL ARRAY_ASSUMED(A)
END PROGRAM ARRAY
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N,N)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
SUBROUTINE ARRAY_ASSUMED(A)
INTEGER, ALLOCATABLE :: A(:,:)
N=SIZE(A,1)
! bla bla
END SUBROUTINE ARRAY_ASSUMED
where you need an explicit interface for the second, usually through the use of a module.
From FORTRAN77, I'm used to the first alternative, and I read this is also the most efficient if you pass the whole array.
The nice thing with the explicit shape is that I can also call a subroutine and treat the array as a vector instead of a matrix:
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N**2)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
I wondered if there is a nice way to do that kind of thing using the second, assumed shape interface, without copying it.
See the RESHAPE intrinsic, e.g.
http://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html
Alternatively, if you want to avoid the copy (in some cases an optimizing compiler might be able to do a reshape without copying, e.g. if the RHS array is not used afterwards, but I wouldn't count on it), as of Fortran 2003 you can assign pointers to targets of different rank, using bounds remapping. E.g. something like
program ptrtest
real, pointer :: a(:)
real, pointer :: b(:,:)
integer :: n = 10
allocate(a(n**2))
a = 42
b (1:n, 1:n) => a
end program ptrtest
I was looking to do the same thing and came across this discussion. None of the solutions suited my purposes, but I found that there is a way to reshape an array without copying the data using iso_c_binding if you are using the fortran 2003 standard which current fortran 90/95 compilers tend to support. I know the discussion is old, but I figured I would add what I came up with for the benefit of others with this question.
The key is to use the function C_LOC to convert an array to an array pointer, and then use C_F_POINTER to convert this back into a fortran array pointer with the desired shape. One challenge with using C_LOC is that C_LOC only works for array that have a directly specified shape. This is because arrays in fortran with an incomplete size specification (i.e., that use a : for some dimension) include an array descriptor along with the array data. C_LOC does not give you the memory location of the array data, but the location of the descriptor. So an allocatable array or a pointer array don't work with C_LOC (unless you want the location of the compiler specific array descriptor data structure). The solution is to create a subroutine or function that receives the array as an array of fixed size (the size really doesn't matter). This causes the array variable in the function (or subroutine) to point to the location of the array data rather than the location of the array descriptor. You then use C_LOC to get a pointer to the array data location and C_F_POINTER to convert this pointer back into an array with the desired shape. The desired shape must be passed into this function to be used with C_F_POINTER. Below is an example:
program arrayresize
implicit none
integer, allocatable :: array1(:)
integer, pointer :: array2(:,:)
! allocate and initialize array1
allocate(array1(6))
array1 = (/1,2,3,4,5,6/)
! This starts out initialized to 2
print *, 'array1(2) = ', array1(2)
! Point array2 to same data as array1. The shape of array2
! is passed in as an array of intergers because C_F_POINTER
! uses and array of intergers as a SIZE parameter.
array2 => getArray(array1, (/2,3/))
! Change the value at array2(2,1) (same as array1(2))
array2(2,1) = 5
! Show that data in array1(2) was modified by changing
! array2(2,1)
print *, 'array(2,1) = array1(2) = ', array1(2)
contains
function getArray(array, shape_) result(aptr)
use iso_c_binding, only: C_LOC, C_F_POINTER
! Pass in the array as an array of fixed size so that there
! is no array descriptor associated with it. This means we
! can get a pointer to the location of the data using C_LOC
integer, target :: array(1)
integer :: shape_(:)
integer, pointer :: aptr(:,:)
! Use C_LOC to get the start location of the array data, and
! use C_F_POINTER to turn this into a fortran pointer (aptr).
! Note that we need to specify the shape of the pointer using an
! integer array.
call C_F_POINTER(C_LOC(array), aptr, shape_)
end function
end program
#janneb has already answered re RESHAPE. RESHAPE is a function -- usually used in an assignment statement so there will be a copy operation. Perhaps it can be done without copying using pointers. Unless the array is huge, it is probably better to use RESHAPE.
I'm skeptical that the explicit shape array is more efficient than the assumed shape, in terms of runtime. My inclination is to use the features of the Fortran >=90 language and use assumed shape declarations ... that way you don't have to bother passing the dimensions.
EDIT:
I tested the sample program of #janneb with ifort 11, gfortran 4.5 and gfortran 4.6. Of these three, it only works in gfortran 4.6. Interestingly, to go the other direction and connect a 1-D array to an existing 2-D array requires another new feature of Fortran 2008, the "contiguous" attribute -- at least according to gfortran 4.6.0 20110318. Without this attribute in the declaration, there is a compile time error.
program test_ptrs
implicit none
integer :: i, j
real, dimension (:,:), pointer, contiguous :: array_twod
real, dimension (:), pointer :: array_oned
allocate ( array_twod (2,2) )
do i=1,2
do j=1,2
array_twod (i,j) = i*j
end do
end do
array_oned (1:4) => array_twod
write (*, *) array_oned
stop
end program test_ptrs
You can use assumed-size arrays, but it can mean multiple layers of wrapper
routines:
program test
implicit none
integer :: test_array(10,2)
test_array(:,1) = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
test_array(:,2) = (/11, 12, 13, 14, 15, 16, 17, 18, 19, 20/)
write(*,*) "Original array:"
call print_a(test_array)
write(*,*) "Reshaped array:"
call print_reshaped(test_array, size(test_array))
contains
subroutine print_reshaped(a, n)
integer, intent(in) :: a(*)
integer, intent(in) :: n
call print_two_dim(a, 2, n/2)
end subroutine
subroutine print_two_dim(a, n1, n2)
integer, intent(in) :: a(1:n1,1:*)
integer, intent(in) :: n1, n2
call print_a(a(1:n1,1:n2))
end subroutine
subroutine print_a(a)
integer, intent(in) :: a(:,:)
integer :: i
write(*,*) "shape:", shape(a)
do i = 1, size(a(1,:))
write(*,*) a(:,i)
end do
end subroutine
end program test
I am using ifort 14.0.3 and 2D to 1D conversion, I could use an allocatable array for 2D array and a pointer array for 1D:
integer,allocatable,target :: A(:,:)
integer,pointer :: AP(:)
allocate(A(3,N))
AP(1:3*N) => A
As #M.S.B mentioned, in case both A and AP have the pointer attribute, I had to use contiguous attribute for A to guarantee the consistency of the conversion.
Gfortran is a bit paranoid with interfaces. It not only wants to know the type, kind, rank and number of arguments, but also the shape, the target attribute and the intent (although I agree with the intent part). I encountered a similar problem.
With gfortran, there are three different dimension definition:
1. Fixed
2. Variable
3. Assumed-size
With ifort, categories 1 and 2 are considered the same, so you can do just define any dimension size as 0 in the interface and it works.
program test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(sz,arr)
integer, intent(in) :: sz
integer, dimension(0), intent(in) :: arr
! This zero means that the size does not matter,
! as long as it is a one-dimensional integer array.
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(3,ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(3,ownlist)
deallocate(ownlist)
contains
! This one has a dimension size as input.
subroutine rout1(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(sz), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
! This one has a fixed dimension size.
subroutine rout2(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(0:10), intent(in) :: arr
write(*,*) "Ignored integer: ",sz
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program test
Gfortran complains about the interface. Changing the 0 into 'sz' solves the problem four 'rout1', but not for 'rout2'.
However, you can fool gfortran around and say dimension(0:10+0*sz) instead of dimension(0:10) and gfortran compiles and gives the same
result as ifort.
This is a stupid trick and it relies on the existence of the integer 'sz' that may not be there. Another program:
program difficult_test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(arr)
integer, dimension(0), intent(in) :: arr
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(ownlist)
deallocate(ownlist)
contains
subroutine rout1(arr)
implicit none
integer, dimension(3), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
subroutine rout2(arr)
implicit none
integer, dimension(0:10), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program difficult_test
This works under ifort for the same reasons as the previous example, but gfortran complains about the interface. I do not know how I can fix it.
The only thing I want to tell gfortran is 'I do not know the dimension size yet, but we will fix it.'. But this needs a spare integer arguemnt (or something else that we can turn into an integer) to fool gfortran around.