Вызов 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
Исправлена!
- Вставьте use iso_c_bind под константами использования
- Используйте целое число (c_int) вместо целого для ne, nn и других переменных.
- Удалить неиспользуемые константы модуля
,
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) программа Фортран
Исправлена!
- размер выделения npart фиксирован. Не нэ нн
- 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 ответа
Нет, вы не можете пройти
null()
это константа указателя Фортрана. Вы должны пройтиC_NULL_PTR
из модуляISO_C_BINDING
и интерфейс должен отражать это. Пустой аргумент должен бытьtype(c_ptr)
скорее всего сVALUE
приписывать. Это может на самом деле работать из-за того же внутреннего представления, но я бы не рассчитывал на это.Нет, если вы передадите некоторую нормальную переменную, вы можете передать ее напрямую по ссылке. Как обычно в Фортране. Если интерфейс
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"
Инструкция по компиляции
- компилировать
metis.c
с компилятором C - компилировать
metisInterface.F90
с компилятором Фортрана, связывающим с скомпилированным объектом C - Скомпилируйте основную программу с помощью компилятора Фортрана, связанного с
metisInterface.o