【问题标题】:Calling METIS API(wrtten in C language) in fortran program在 fortran 程序中调用 METIS API(用 C 语言编写)
【发布时间】:2013-01-21 17:26:28
【问题描述】:

两周多来,我一直在努力从我的 fortran 代码中调用用 C 语言编写的 METIS 库之一。而且,不幸的是,没有你的帮助,这似乎不是一个快乐的结局。我发现了一些关于direct callingusing interface 的帖子。我更喜欢后者,因为我可以监视变量以进行调试。我附上了三个代码。

1。我要使用的c函数
2. fortran接口模块
3. fortran程序

(1)c函数

int METIS_PartMeshNodal(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, 
      idx_t *vwgt, idx_t *vsize, idx_t *nparts, real_t *tpwgts, 
      idx_t *options, idx_t *objval, idx_t *epart, idx_t *npart)

我删除了 c 函数体。没必要了解我的问题

这里,idx_t 是整数,real_t 是单精度或双精度。从 neoptions 是输入,最后三个参数是输出。并且 vwgtvsizetpwgtsoptions 可以接收 null 作为默认设置的输入 我编写了界面模块像这样使用 c 函数

(2) Fortran 接口模块

已修复!

  1. 使用常量下插入use iso_c_bind
  2. nenn 和其他变量使用 integer(c_int) 而不是 integer
  3. 删除未使用的模块常量

module Calling_METIS

  !use constants,  only : p2 !this is for double precision
  use iso_c_bind            !inserted later

  implicit none

  !integer                                    :: ne, nn              !modified
  integer(c_int)                              :: ne, nn 
  !integer,  dimension(:), allocatable        :: eptr, eind          !modified
  integer(c_int),  dimension(:), allocatable  :: eptr, eind
  !integer,  dimension(:), allocatable        :: vwgt, vsize         !modified
  type(c_ptr)                                 :: vwgt, vsize         
  !integer                                    :: nparts              !modified
  integer(c_int)                              :: nparts
  !real(p2), dimension(:), allocatable        :: tpwgts              !modified 
  type(c_ptr)                                 :: tpwgts      
  !integer,  dimension(0:39)                  :: opts                !modified
  integer(c_int),  dimension(0:39)            :: opts        
  !integer                                    :: objval              !modified
  integer(c_int)                              :: objval
  !integer,  dimension(:), allocatable        :: epart, npart        !modified 
  integer(c_int),  dimension(:), allocatable  :: epart, npart 

  interface
    subroutine METIS_PartMeshNodal( ne, nn, eptr, eind, vwgt, vsize, nparts, tpwgt, &
                                    opts, objval, epart, npart) bind(c)
      use intrinsic        :: iso_c_binding
      !use constants,  only  : p2

      implicit none

      integer (c_int),                  intent(in)  :: ne, nn
      integer (c_int), dimension(*),    intent(in)  :: eptr, eind
      !integer (c_int), dimension(*),    intent(in) :: vwgt, vsize  !modified
      type(c_ptr),                          value   :: vwgt, vsize   
      integer (c_int),                  intent(in)  :: nparts
      !real(c_double),  dimension(*),    intent(in) :: tpwgt        !modified
      type(c_ptr),                          value   :: tpwgt
      integer (c_int), dimension(0:39), intent(in)  :: opts
      integer (c_int),                  intent(out) :: objval
      integer (c_int), dimension(*),    intent(out) :: epart
      integer (c_int), dimension(*),    intent(out) :: npart

    end subroutine METIS_PartMeshNodal  
  end interface
end module

这是我调用该函数的程序代码

(3) Fortran 程序

已修复!

  1. npart 的分配大小是固定的。不是 ne 而是 nn
  2. opts(7)=1 被添加以获取 epart、npart 的 Fortran 样式数组(目前无效)

.

program METIS_call_test

 !some 'use' statments
 use Calling_METIS
 use iso_c_binging         !added

 implicit none

 ! Local variable
 integer         :: iC
 character(80)   :: grid_file !grid_file

 grid_file = 'test.grid'

 ! (1) Read grid files
 call read_grid(grid_file)

 ! (2) Construction Input Data for calling METIS Function
 ! # of cells, vertices
 ne = ncells
 nn = nvtxs

 ! eptr, eind allocation 
 allocate(eptr(0:ne), eind(0:3*ntria + 4*nquad - 1))

 ! eptr and eind building
 eptr(0) = 0
 do iC=1, ncells
   eptr(iC) = eptr(iC-1) + cell(iC)%nvtxs
   eind(eptr(iC-1):eptr(iC)-1) = cell(iC)%vtx
 end do

 ! epart, npart building
 !allocate(epart(ne), npart(ne))
 allocate(epart(ne), npart(nn))   ! modified

 ! # of partition setting
 nparts = 2
 vwgt   = c_null_ptr    !added
 vsize  = c_null_ptr    !added
 tpwgt  = c_null_ptr    !added     

 ! (3) Call METIS_PartMeshNodal
 call METIS_SetDefaultOptions(opts)

 opts(7) = 1                      !Added. For fortran style output array epart, npart. 

 call METIS_PartMeshNodal(ne, nn, eptr, eind, vwgt, vsize, nparts, tpwgt, &
                           opts, objval, epart, npart)
 !call METIS_PartMeshNodal(ne, nn, eptr, eind, null(), null(), nparts, null(), &
 !                         opts, objval, epart, npart)         !wrong...

end program

但问题是我收到如下错误消息,尽管我为 tpwgt 设置了 null。

输入错误:约束 0 的 tpwgts 的 0.000000 总和不正确。

并且这条消息在下面的代码中处理。

for (i=0; i<ctrl->ncon; i++) {
    sum = rsum(ctrl->nparts, ctrl->tpwgts+i, ctrl->ncon);
    if (sum < 0.99 || sum > 1.01) {
      IFSET(dbglvl, METIS_DBG_INFO, 
          printf("Input Error: Incorrect sum of %"PRREAL" for 
                  tpwgts for constraint %"PRIDX".\n", sum, i));
      return 0;
    }
  }

无论如何,为了看看如果我为 tpwgts 放入一个数组而不是 null 会得到什么,tpwgts(:) = 1.0/nparts,这使得 tpwgts 的总和等于 1.0。但我得到了与 1.75 相同的信息。

这是我的问题
1. 我是否使用 null() 正确传递参数?
2. 我必须将所有参数的指针传递给 c 函数吗?那怎么办?
3. 将整数放入 opts(0:39) 是否足够使用?例如,在没有“接口模块”的post 中,使用了像 options(3)=1 这样的简单代码。但在 c 代码中,options 有 16 个命名变量,如 options[METIS_OPTION_NUMBERING]、options[METIS_OPTION_UFACTOR]。我认为设置选项是必要的,但我不知道。 4. METIS有fortran的例子吗?

任何类型的提示/建议都会对我有很大帮助。谢谢你。

结论

我遇到的问题是 c 函数无法识别 fortran 代码中的空指针

接口模块中有一些变量的错误声明(参见“固定”和 cmets)

看起来代码工作正常。但是 fortran 样式输出的 option(7) = 1 不起作用,现在我正在研究它。

【问题讨论】:

  • 乍一看,我注意到 C 函数返回一个 int,但您将 METIS_PartMeshNodal 作为子例程进行接口,它没有返回参数并且类似于 C @987654332 @ 功能。你的界面不应该是integer(c_int) function吗?
  • 另一方面,在我链接的源代码中,它确实是一个void 函数,但具有不同的签名。最好的办法是看到一些实际工作的 C 代码调用该例程。
  • 到西格玛。我不确定确切的原因。但参考this,c 函数被定义为 c 绑定中的子例程。 致 Vladimir F 它看起来像旧版本。我想我必须听从你的建议。但我此时只是简单地链接了c库。我不知道如何才能看到 C 代码发生了什么,但我会找到方法的。
  • 到 sigma 我把子程序改成函数并调用函数。但是结果是一样的(访问冲突)

标签: fortran fortran-iso-c-binding metis


【解决方案1】:
  1. 不,您不能传递null(),这是一个 Fortran 指针常量。您必须从模块ISO_C_BINDING 传递C_NULL_PTR,并且接口必须反映这一点。虚拟参数必须是type(c_ptr),很可能带有VALUE 属性。由于相同的内部表示,它实际上可能会起作用,但我不会指望它。

  2. 不,如果你传递一些普通的变量,你可以通过引用直接传递它。就像通常在 Fortran 中一样。如果接口是BIND(C),编译器知道它必须发送一个指针。

    有一个更新 Fortran 2008 的新 TS,您可以在其中将可互操作过程中的虚拟参数定义为 OPTIONAL。然后你可以通过省略它们来传递空指针。 Gfortran 应该已经支持这个了。

注意:在这里我可以看到你的函数的一个非常不同的 C 签名,你确定你的函数没问题吗? http://charm.cs.uiuc.edu/doxygen/charm/meshpart_8c.shtml

【讨论】:

  • 感谢您的评论。而不是 null(),我将变量重新声明为 type(c_ptr) 并在程序中使用 iso_c_binding 模块将它们设置为 c_null_ptr。本身似乎不会导致错误。但是我遇到了另一个错误(访问冲突),我认为这与使用假定大小的数组、维度(*)或其他东西的数组声明有关。我会尽快发布结果。注意:meshpart.c 已在 2011 年修改。我猜该链接包含旧版本的 METIS。
  • 亲爱的@vladimir-f 更多问题> 1. 从界面上看,整型指针和实型指针的声明都是type(c_ptr)。我做得对吗? 2. 有三个指针为空,它们的值为 0.d0,这似乎会导致错误。 ex) twgts 是 NULL 或 floatordouble 类型的数组,其中数组元素的总和应为 1.0。但是编译器认为 twgts = 0.0 并显示错误消息。
【解决方案2】:

我认为您的opts(7) 不起作用,因为您还需要一个用于METIS 功能METIS_SetDefaultOptions 的接口。根据http://glaros.dtc.umn.edu/gkhome/node/877 的回答,我用我需要的接口创建了一个包装模块(metisInterface.F90):

module metisInterface
! module to allows us to call METIS C functions from the main Fortran code

   use,intrinsic :: ISO_C_BINDING

   integer :: ia,ic
   integer(C_INT) :: metis_ne,metis_nn
   integer(C_INT) :: ncommon,objval
   integer(C_INT) :: nparts
   integer(C_INT),allocatable,dimension(:) :: eptr,eind,perm,iperm
   integer(C_INT),allocatable,dimension(:) :: epart,npart
   type(C_PTR) :: vwgt,vsize,twgts,tpwgts
   integer(C_INT) :: opts(0:40)


   interface
      integer(C_INT) function METIS_SetDefaultOptions(opts) bind(C,name="METIS_SetDefaultOptions")
         use,intrinsic :: ISO_C_BINDING
         implicit none
         integer(C_INT) :: opts(0:40)
      end function METIS_SetDefaultOptions
   end interface 

   interface
      integer(C_INT) function METIS_PartMeshDual(ne,nn,eptr,eind,vwgt,vsize,ncommon,nparts,tpwgts, &
                              opts,objval,epart,npart) bind(C,name="METIS_PartMeshDual")
         use,intrinsic :: ISO_C_BINDING
         implicit none
         integer(C_INT):: ne, nn
         integer(C_INT):: ncommon, objval
         integer(C_INT):: nparts
         integer(C_INT),dimension(*) :: eptr, eind
         integer(C_INT),dimension(*) :: epart, npart
         type(C_PTR),value :: vwgt, vsize, tpwgts
         integer(C_INT) :: opts(0:40)
      end function METIS_PartMeshDual
   end interface    

end module metisInterface

然后,在主程序中(或在您调用 METIS 函数的任何位置)中,您需要拥有(为了完整起见,我还添加了对 METIS_PartMeshDual 的调用):

use metisInterface

integer :: metis_call_status
.
.
.
metis_call_status = METIS_SetDefaultOptions(opts)

! METIS_OPTION_NUMBERING for Fortran
opts(17) = 1

metis_call_status = METIS_PartMeshDual(metis_ne,metis_nn,eptr,eind, &
                    vwgt,vsize,ncommon,nparts,tpwgts,opts,objval,epart,npart)

请注意,epartnpart 将根据需要使用 Fortran 编号(从 1 开始)。但是,处理器也将从 1 开始编号。例如,如果您在 4 个处理器中运行,则根处理器为 1,您可能有 epart(n)=4,而您将没有任何 epart(n)=0

最后,还需要一个文件metis.c,一行一行:

#include "metis.h"

编译说明

  1. 使用 C 编译器编译 metis.c
  2. 使用与已编译 C 对象链接的 Fortran 编译器编译 metisInterface.F90
  3. 使用与metisInterface.o 链接的 Fortran 编译器编译主程序

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-05
    • 1970-01-01
    • 2011-03-24
    • 2016-03-07
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    相关资源
    最近更新 更多