вызывать функции из общей библиотеки fortran в python

#python #fortran #fortran-iso-c-binding

#python #fortran #fortran-iso-c-привязка

Вопрос:

Я хотел бы вызвать некоторые функции из общей библиотеки Fortran в Python. Я нашел несколько ссылок в сети и прочитал их, и в соответствии с тем, что я нашел, я должен сделать

 libadd = cdll.LoadLibrary('./libbin.so') 
  

для загрузки общего объекта. Однако этот общий объект включает в себя некоторые символы из другой общей библиотеки. Я прочитал справку cdll, однако не представляется возможным загрузить несколько общих объектных файлов одновременно. Как я могу вызывать функции из этой библиотеки Fortran, которая, скорее всего, скомпилирована компилятором Intel Fortran?

Комментарии:

1. Возможно, вы также захотите взглянуть на f2py . Это является частью NumPy.

Ответ №1:

Вам нужно будет знать сигнатуры функций в общем объекте. У вас есть исходный код или какая-нибудь ссылка, объясняющая имена функций и типы аргументов?

Например, у меня есть этот исходный код (mult.f90):

 integer function multiply(a, b)
    integer, intent(in) :: a, b
    multiply = a * b
end function multiply
  

.. и чтобы продемонстрировать, как вы можете загружать и использовать несколько общих объектов одновременно, у меня также есть (add.f90):

 integer function addtwo(a, b)
    integer, intent(in) :: a, b
    addtwo = a   b
end function addtwo
  

Скомпилируйте, изучите символы:

 % gfortran-4.4 -shared -fPIC -g -o mult.so mult.f90
% gfortran-4.4 -shared -fPIC -g -o add.so add.f90
% nm -ao mult.so | grep multiply
mult.so:00000000000005cc T multiply_
  

Обратите внимание, что к имени символа в общем объекте добавлено подчеркивание. Поскольку у меня есть исходный код, я знаю, что сигнатура есть multiply_(int *a, int *b) , поэтому легко вызвать эту функцию из ctypes :

 from ctypes import byref, cdll, c_int

mult = cdll.LoadLibrary('./mult.so')
add = cdll.LoadLibrary('./add.so')
a = c_int(2)
b = c_int(4)
print mult.multiply_(byref(a), byref(b))
print add.addtwo_(byref(a), byref(b))
  

Вывод:

 8
6
  

Комментарии:

1. Спасибо за замечательный ответ. У меня есть небольшой последующий вопрос. Когда я проверяю список имен библиотек ( nm mult.so ), он показывает _multiply_ , а не multiply_ , хотя последнее все еще работает в C и python. Вы знаете, почему это так?

2. Всегда пожалуйста. Различные компиляторы могут добавлять символы подчеркивания к именам символов. Я знаю, что в случае gfortran вы можете указать, -fno-underscoring чтобы отключить это; в результате появляется символ multiply для этой функции. Ваш компилятор платформа могут иметь эту опцию. Я обновлю ответ другим возможным решением, если вы имеете дело с общим объектом неизвестного происхождения.

3. На самом деле, я воздержусь от дальнейших комментариев, которые я слышу в ответ о опции компилятора, поскольку это может решить проблему для вас. Суть в том, что вам нужно знать сигнатуру вызываемой вами функции, поэтому вы хотите свести к минимуму догадки, исправив все выше по потоку.

4. @samplebias Мне следовало упомянуть, что я использовал OS X. Начальные символы подчеркивания не отображаются в библиотеках Linux, так что, вероятно, это относится к OS X (или BSD). Поскольку это выходит за рамки вопроса, мне, вероятно, следует оставить все как есть. Спасибо, что изучили это.

5. @samplebias: есть некоторые символы, на которые ссылаются в одном из общих объектных файлов, которые все еще не определены. И тезисы расположены в другом объектном файле, однако попытка загрузить этот общий объект выдает ошибку о том, что объектный файл не распознан, есть какие-либо идеи?

Ответ №2:

Я бы добавил к ответу @sameplebias, что можно использовать iso_c_binding модуль, чтобы заставить (любой) компилятор fortran создать правильную сигнатуру C. Пример использования:

 module fmesh_wrapper

use iso_c_binding, only: c_double, c_int
use fmesh, only: mesh_exp

implicit none

contains

subroutine c_mesh_exp(r_min, r_max, a, N, mesh) bind(c)
real(c_double), intent(in) :: r_min
real(c_double), intent(in) :: r_max
real(c_double), intent(in) :: a
integer(c_int), intent(in) :: N
real(c_double), intent(out) :: mesh(N)
call mesh_exp(r_min, r_max, a, N, mesh)
end subroutine

! wrap more functions here
! ...

end module
  

это будет иметь следующую сигнатуру C:

 void c_mesh_exp(double *r_min, double *r_max, double *a, int *N,
        double *mesh);
  

а затем вы можете вызвать ее из Python как обычно. Преимущество этого подхода в том, что он работает на всех платформах (без использования каких-либо специальных опций компилятора).

Ответ №3:

Для работы с f2py (из NumPy) позаимствуйте оба mult.f90 и add.f90 примеры из @samplebias. Из командной строки скомпилируйте импортируемые общие библиотеки Python:

 f2py -c -m mult mult.f90
f2py -c -m add add.f90
  

Теперь используйте их в Python:

 >>> import add
>>> import mult
>>> add.addtwo(4, 5)
9
>>> mult.multiply(4, 5)
20