#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