2

I am trying to pass the name of another subroutine_b and an array to a generic subroutine_a. This subroutine_a is supposed to pass the array to subroutine_b and get back its computed value.

Following is the code that I have written:

module pass_subroutine_mod1
    implicit none
    private
    public :: ave_value, fx

    contains
        subroutine ave_value( func, x, y, ave )
            ! Calculate average of y which is passed on from another function `func`.
            !
            external :: func
            double precision, intent(in), dimension(:) :: x
            double precision, intent(inout), dimension(:) :: y
            double precision, intent(inout) :: ave

            integer :: n

            N = size( x )

            call func( x, y )
            ave = sum( y ) / dble( N )

            return
        end subroutine ave_value


        subroutine fx( x, y )
            double precision, dimension(:), intent(in) :: x
            double precision, dimension(:), intent(inout) :: y

            y = x ** 3
            return
        end subroutine fx
end module pass_subroutine_mod1


program main
    use :: pass_subroutine_mod1
    implicit none

    integer :: i, N = 101
    double precision :: ave
    double precision, allocatable, dimension(:) :: x, y
    allocate( x(N), y(N), source=0.0d0 )

    do i = 1, N
        x(i) = dble( i - 1 )
    end do

    call ave_value( fx, x, y, ave )
    write( *, '(A, ES15.6E3)' ) "Average value of y (from fx) = ", ave

    deallocate( x, y )
end program main

Here ave_value() is the subroutine_a and fx() is subroutine_b.

When I compile and run the above code, it throws the following error:

At line 28 of file pass_subroutine.f90
Fortran runtime error: Array bound mismatch for dimension 1 of array 'y' (1/1125899906842625)

Line 28 is the line y = x ** 3 in subroutine fx( x, y ). It thus seems that the arrays x and y are not properly passed to fx() because when I try to print x from within fx(), it is unable to do so.

If, instead of arrays x and y, I use scalars (and update the code accordingly) the code is able to run properly and output the desired result.

Additionally, after going through this thread, if I modify the ave_value by adding an interface block, the code runs properly again. Following is the modification with ave_value:

        subroutine ave_value( func, x, y, ave )
            ! Calculate average of y which is passed on from another function `func`.
            !
            external :: func
            double precision, intent(in), dimension(:) :: x
            double precision, intent(inout), dimension(:) :: y
            double precision, intent(inout) :: ave

            integer :: n

            interface
                subroutine func( ip, op )
                    double precision, dimension(:), intent(in) :: ip
                    double precision, dimension(:), intent(inout) :: op
                end subroutine func
            end interface

            N = size( x )

            call func( x, y )
            ave = sum( y ) / dble( N )

            return
        end subroutine ave_value

Thus, my questions are:

a) Is the above modification correct?

b) If yes, then why do I need an interface block when working with arrays and not when working with scalars?

Ombrophile
  • 613
  • 1
  • 7
  • 13

2 Answers2

2

When you have a dummy procedure argument, as func in ave_value is, there is no "assumed interface". While the array dummy arguments x and y are assumed-shape (they take their shape from the actual argument when the procedure is invoked) there's nothing similar for procedures.

Instead, the procedure func has the interface it's declared with. In this case, you have the declaration

external :: func

so that func is an external procedure with implicit interface. As we know, an implicit interface is not suitable when we want to reference it and it has assumed-shape array arguments ip and op.

You need somehow to explicitly give the dummy argument the correct interface. One way, as you've seen, is to use an interface block to give the external procedure an explicit interface. Alternatively, you can use a procedure declaration statement like

procedure(iface) func

where iface is an appropriate interface. There are several ways to make that interface available, but I won't go into them here.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Thank you. When I change the assumed-shape array arguments to assumed-size, the code runs correctly without the use of an interface block. Additionally, I tried to write `fx` subroutine as a C code and call it from the fortran module. Even then I did not need an interface block. Thus, it seems that the problem is with the use of assumed-shape array in an external procedure (which both you and Steve have mentioned, but I slightly rephrased it)! Thanks again. – Ombrophile Aug 20 '19 at 23:19
  • Yes, the [assumed-shape array](https://stackoverflow.com/a/31630662/3157076) is the key as to why you need an explicit interface - but if `fx` written as C had a C-interoperable interface it would also need that explicit interface. My answer isn't so much why you need the explicit interface, but why that interface isn't available for the dummy procedure argument. – francescalus Aug 21 '19 at 08:57
1

It's the assumed-shape array dummy arguments dimension(:) that require an explicit interface. This is because the compiler has to do something different for these, typically passing a descriptor. It doesn't matter what's on the calling side, it's the dummy arguments of the called procedure that matter.

For more information see my old blog post Doctor Fortran Gets Explicit - Again!

You may want to look at the language feature abstract interface, along with procedure, to make the code look a bit cleaner.

Steve Lionel
  • 6,972
  • 18
  • 31
  • Darn - I know the difference but keep saying the wrong thing. Edited - thanks. – Steve Lionel Aug 20 '19 at 18:27
  • Thank you for the answer. I thought putting procedures in a module automatically provides it with an explicit interface. Another reason for me thinking this way is that instead of calling `fx` via the name `func`, if I replace the line `call func( x, y )` with `call fx( x, y )`, the code works without the interface block. It obviously will not work when `fx` is in a different module, what is the difference between these two cases? – Ombrophile Aug 20 '19 at 18:59
  • 1
    Even though the procedure you're calling is in a visible module, when you pass a procedure as an argument, there's nothing to tie the "external func" to that procedure. That's why func has to have an explicit interface. – Steve Lionel Aug 20 '19 at 19:58