Вызов METIS API(написанный на языке C) в программе на фортране

В течение 2 недель я изо всех сил пытался вызвать одну из библиотек METIS, написанную на C, из моего кода на фортране. И, к сожалению, это не кажется счастливым концом без вашей помощи. Я нашел несколько постов о прямом звонке и использовании интерфейса. Я предпочитаю последнее, потому что я мог контролировать переменные для отладки. Я прикрепил три кода.

1. С функцией, которую я хотел бы использовать
2. интерфейсный модуль Fortran
3. фортран программа

(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)

Я удалил тело с функцией. Нет необходимости понимать мою проблему

Здесь idx_t - целое число, а real_t - одинарная или двойная точность. От ne до параметров вводятся и последние три аргумента выводятся. И vwgt, vsize, tpwgts и параметры могут получить значение null в качестве входных данных для настройки по умолчанию. Я написал интерфейсный модуль для использования функции c, подобной этой

(2) Интерфейсный модуль Fortran

Исправлена!

  1. Вставьте use iso_c_bind под константами использования
  2. Используйте целое число (c_int) вместо целого для ne, nn и других переменных.
  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) программа Фортран

Исправлена!

  1. размер выделения npart фиксирован. Не нэ нн
  2. opts(7)=1 добавлен, чтобы получить массив epart, npart в стиле Фортрана (до сих пор не работал)

,

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.

Ошибка ввода: неверная сумма 0,000000 для значений tpwgts для ограничения 0.

И это сообщение обрабатывается в коде ниже.

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, равного нулю, tpwgts (:) = 1.0 / nparts, что делает сумму tpwgts равной 1,0. Но я получил то же сообщение с 1,75 на сумму.

Это мои вопросы
1. Я правильно использовал null() для передачи аргументов?
2. Должен ли я передавать указатели для всех аргументов в функцию c? тогда как?
3. Достаточно ли для использования установки целого числа в opts(0:39)? Например, в сообщении без "интерфейсного модуля" используется простой код, такой как options(3)=1. Но в коде c параметры имеют 16 именованных переменных, таких как options[METIS_OPTION_NUMBERING], options[METIS_OPTION_UFACTOR]. Я думаю, что некоторые вещи необходимы для установки параметров, но я понятия не имею. 4. Есть ли пример для METIS в фортране?

Любой намек / совет будет большой помощью для меня. Спасибо.

Conclution

Проблема, с которой я столкнулся, заключалась в том, что функция c не могла распознать нулевой указатель из кода Fortran.

Были некоторые пропущенные переменные в модуле интерфейса (см. "Исправлено" и комментарии)

Похоже, код работает правильно. Но опция (7) = 1 для вывода в стиле фортрана не сработала, и теперь я смотрю на это.

2 ответа

  1. Нет, вы не можете пройти null()это константа указателя Фортрана. Вы должны пройти C_NULL_PTR из модуля ISO_C_BINDING и интерфейс должен отражать это. Пустой аргумент должен быть type(c_ptr)скорее всего с VALUE приписывать. Это может на самом деле работать из-за того же внутреннего представления, но я бы не рассчитывал на это.

  2. Нет, если вы передадите некоторую нормальную переменную, вы можете передать ее напрямую по ссылке. Как обычно в Фортране. Если интерфейс BIND(C)Компилятор знает, что он должен отправить указатель.

    Существует новый TS для обновления Fortran 2008, где вы можете определить фиктивные аргументы во взаимодействующих процедурах как OPTIONAL, Затем вы можете передать нулевой указатель, просто опустив их. Gfortran уже должен поддержать это.

Примечание: здесь я вижу совершенно другую сигнатуру C вашей функции, вы уверены, что ваша в порядке? http://charm.cs.uiuc.edu/doxygen/charm/meshpart_8c.shtml

Я думаю твой 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)

Обратите внимание, что epart а также npart будет иметь нумерацию Фортрана, как вы хотите (начиная с 1). Тем не менее, процессоры также начнут нумерацию с 1. Например, если вы работаете на 4 процессорах, корневой процессор равен 1, и вы можете иметь epart(n)=4и не будет epart(n)=0,

Наконец, файл metis.c также необходимо с одной строкой:

#include "metis.h"

Инструкция по компиляции

  1. компилировать metis.c с компилятором C
  2. компилировать metisInterface.F90 с компилятором Фортрана, связывающим с скомпилированным объектом C
  3. Скомпилируйте основную программу с помощью компилятора Фортрана, связанного с metisInterface.o
Другие вопросы по тегам