将未知大小的数组(子例程输出)传递给另一个子例程

时间:2016-07-15 23:29:00

标签: arrays fortran subroutine intel-fortran intel-mkl

我是英特尔MKL的新手。这是我遇到的一个问题 - 显然是一个与MKL本身无关的问题,但问题是如何声明并将一个迄今未知大小的数组作为子程序的输出传递给另一个子程序。

我正在尝试使用mkl_ddnscsr将矩阵转换为适合Pardiso调用的CSR格式:

CALL mkl_ddnscsr(job,Nt,Nt,Adns,Nt,Acsr,ja,ia,info)

CALL PARDISO(pt,1,1,11,13,Nt,Acsr,ia,ja,perm,1,iparm,0,b,x,errr)

问题是,在调用mkl_ddnscsr子例程之前,我不知道CSR的形式是什么,形成了Acsr和索引向量ja。如何在主程序中声明Acsr和ja,或者这两行所在的子程序?

我试过像

这样的东西
INTERFACE
SUBROUTINE mkl_ddnscsr(job, m, n, Adns, lda, Acsr, ja, ia, info)
IMPLICIT NONE
INTEGER :: job(8)
INTEGER :: m, n, lda, info
INTEGER, ALLOCATABLE :: ja(:)
INTEGER :: ia(m+1)
REAL(KIND=8), ALLOCATABLE :: Acsr(:)
REAL(KIND=8) :: Adns(:)
END SUBROUTINE
END INTERFACE

接着是

INTEGER, ALLOCATABLE :: ja(:)
REAL(KIND=8), ALLOCATABLE :: Acsr(:)

在INTERFACE之外,在主程序中。但是这种配置在运行时给我分段错误。

另一方面,如果我尝试类似

的话
INTERFACE
SUBROUTINE mkl_ddnscsr(job, m, n, Adns, lda, Acsr, ja, ia, info)
IMPLICIT NONE
INTEGER :: job(8)
INTEGER :: m, n, lda, info
INTEGER :: ja(:), ia(m+1)
REAL(KIND=8) :: Acsr(:), Adns(:)
END SUBROUTINE
END INTERFACE

然后

INTEGER, DIMENSION(:) :: ja
REAL(KIND=8), DIMENSION(:) :: Acsr

然后ifort会给我以下信息:

error #6596: If a deferred-shape array is intended, then the ALLOCATABLE or POINTER attribute is missing; if an assumed-shape array is intended, the array must be a dummy argument.

任何人都知道如何解决这个问题?在主程序(或主子程序)中声明ja和Acsr并传递它们的正确方法是什么?

请注意,子程序是英特尔MKL程序包的一部分,而不是我自己编写的程序,因此看起来module是不可能的。

1 个答案:

答案 0 :(得分:1)

您可以从manual page或MKL安装目录中的包含文件mkl_ddnscsr找到mkl_spblas.fi的界面(例如,/ path / to / mkl / include /)

INTERFACE
    subroutine mkl_ddnscsr ( job, m, n, Adns, lda, Acsr, AJ, AI, info )
    integer            job(8)
    integer            m, n, lda, info
    integer            AJ(*), AI(m+1)
    double precision   Adns(*), Acsr(*)
    end
END INTERFACE

因为此例程只有Fortran77样式的伪参数(即显式形状数组AI(m+1)或假定大小的数组,如Adns(*)),所以可以传递任何本地或可分配的数组(在分配之后)调用方)作为实际参数。此外,并不是必须明确地编写接口块,但是include它(在调用方面)检测潜在的接口不匹配应该是有用的。

根据手册,看起来mkl_ddnscsr(将密集转换为稀疏矩阵的例程)看起来像这样:

program main
    implicit none
    ! include 'mkl_spblas.fi'   !! or mkl.fi (not mandatory but recommended)
    integer :: nzmax, nnz, job( 8 ), m, n, lda, info, irow, k
    double precision :: A( 10, 20 )
    double precision, allocatable :: Asparse(:)
    integer, allocatable :: ia(:), ja(:)

    A( :, : ) =  0.0d0
    A( 2, 3 ) = 23.0d0
    A( 2, 7 ) = 27.0d0
    A( 5, 4 ) = 54.0d0
    A( 9, 9 ) = 99.0d0

    !! Give an estimate of the number of non-zeros.
    nzmax = 10

    !! Or assume that non-zeros occupy at most 2% of A(:,:), for example.
    ! nzmax = size( A ) / 50

    !! Or count the number of non-zeros directly.
    ! nzmax = count( abs( A ) > 0.0d0 )

    print *, "nzmax = ", nzmax

    m   = size( A, 1 )   !! number of rows
    n   = size( A, 2 )   !! number of columns
    lda = m              !! leading dimension of A

    allocate( Asparse( nzmax ) )
    allocate( ja( nzmax ) )   !! <-> columns(:)
    allocate( ia( m + 1 ) )   !! <-> rowIndex(:)

    job( 1 )   = 0       !! convert dense to sparse A
    job( 2:3 ) = 1       !! use 1-based indices
    job( 4 )   = 2       !! use the whole A as input
    job( 5 )   = nzmax   !! maximum allowed number of non-zeros
    job( 6 )   = 1       !! generate Asparse, ia, and ja as output

    call mkl_ddnscsr( job, m, n, A, lda, Asparse, ja, ia, info )

    if ( info /= 0 ) then
        print *, "insufficient nzmax (stopped at ", info, "row)"; stop
    endif

    nnz = ia( m+1 ) - 1
    print *, "number of non-zero elements = ", nnz

    do irow = 1, m
        !! This loop runs only for rows having nonzero elements.
        do k = ia( irow ), ia( irow + 1 ) - 1
            print "(2i5, f15.8)", irow, ja( k ), Asparse( k )
        enddo
    enddo

end program

使用ifort -mkl test.f90(使用ifort14.0)进行编译,得出预期结果

 nzmax =           10
 number of non-zero elements =            4
    2    3    23.00000000
    2    7    27.00000000
    5    4    54.00000000
    9    9    99.00000000

关于nzmax的确定,我认为至少有三种方法:(1)只使用猜测值(如上所述); (2)假设整个数组中非零元素的分数;或(3)直接计算密集阵列中的非零数。在任何情况下,因为我们将非零的确切数量作为输出(nnz),我们可以重新分配Asparseja以获得确切的大小(如果需要)。

同样,您可以从包含文件PARDISOthis(或this)页面找到mkl_pardiso.fi的界面。