Почему функция Fortran, скомпилированная с помощью f2py, возвращает ноль, когда я меняю имя переменной результата? - PullRequest
0 голосов
/ 10 февраля 2019

Когда я вызываю следующую функцию, она возвращает 1, как и ожидалось:

integer function my_func() result(myresult)
    myresult = 1
end function my_func

Но когда я изменяю имя возвращаемого значения, чтобы оно начиналось с буквы "r", тогда функция возвращает 0.

integer function my_func() result(rresult)
    rresult = 1
end function my_func

Что вызывает это?Сначала я подумал, что это связано с неявной типизацией, но эта функция находится внутри модуля, который определяет implicit none.

Вот полный модуль

module my_mod
implicit none

contains

integer function my_func() result(myresult)
    myresult = 1
end function my_func

end module my_mod

Я использую Fortran 90 и компилирую с gfortran.

РЕДАКТИРОВАТЬ

Вот полная программа для демонстрации проблемы

Makefile:

.PHONY: pytest clean

CYTHON_LIB = fortran_mods.cpython-37m-x86_64-linux-gnu.so
FFLAGS += -fdefault-real-8

pytest: $(CYTHON_LIB)
        ./tests.py

$(CYTHON_LIB): my_mod.F90
        f2py -c -m fortran_mods my_mod.F90 --f90flags="$(FFLAGS)"

clean:
        rm *.so

my_mod.F90:

module my_mod
implicit none

contains

!********************************************************

integer function my_func_without_r() result(myresult)
    myresult = 1
end function

integer function my_func_with_r() result(rresult)
    rresult = 1
end function

end module my_mod

tests.py

#!/usr/bin/env python3
import fortran_mods
from fortran_mods import *

print("with r:", my_mod.my_func_with_r())
print("without r:", my_mod.my_func_without_r())

Вывод, когда make pytest запущен и FFLAGS += -fdefault-real-8 включен в Makefile, равен

with r: 0.0
without r: 1

и в противном случае

with r: 1.0
without r: 1

1 Ответ

0 голосов
/ 10 февраля 2019

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

Чтобы немного лучше понять, как F2PY оборачивает функцию (особенно, если что-то не работает какожидается), это всегда помогает разделить процесс на части (см. Умный способ ).Поэтому сначала создайте файл подписи , позволяющий вам увидеть, как F2PY интерпретирует ваш код.Для вашего конкретного примера запустите:

f2py -m fortran_mods -h my_mod.pyf my_mod.F90

Это создаст файл сигнатуры my_mod.pyf, который будет выглядеть примерно так:

python module fortran_mods ! in 
    interface  ! in :fortran_mods
        module my_mod ! in :fortran_mods:my_mod.F90
            function my_func_without_r() result (myresult) ! in :fortran_mods:my_mod.F90:my_mod
                integer :: myresult
            end function my_func_without_r
            function my_func_with_r() result (rresult) ! in :fortran_mods:my_mod.F90:my_mod
                real :: rresult
            end function my_func_with_r
        end module my_mod
    end interface 
end python module fortran_mods

Очевидно, что F2PY неверно идентифицировал переменную результата my_func_with_rrresult как real.Вы можете просто заменить real :: rresult на integer :: rresult в my_mod.pyf, сделать следующий / второй шаг переноса F2PY и скомпилировать, используя исправленный файл подписи:

f2py -c my_mod.pyf my_mod.F90

Ваш скрипт на Python долженТеперь дайте ожидаемый результат.

Такой подход к изменению файла подписи может быть нежелателен, если у вас есть много функций для переноса.Что может вызывать трудности для F2PY, так это то, что в определениях вашей функции используются переменные результата, а определения типов для них не отображаются в теле функции (т. Е. Проблема F2PY, а не проблема Фортрана).Если вы измените определения функций, например, на:

function my_func_with_r() result(rresult)
    integer :: rresult
    rresult = 1
end function

или

integer function my_func_with_r()
    my_func_with_r = 1
end function

, вы можете сделать F2PY-упаковку за один шаг, как вы делали изначально, и все равно должны получить правильный вывод.

Наконец, я добавлю еще один голос к проблемам, поднятым в комментариях: использование -fdefault-real-8, когда обертывание функций с F2PY вызывает проблемы.Чтобы сделать процедуру Fortran вызываемой из Python , F2PY создает:

модуль расширения Python C / API (называемый модулем оболочки), который реализует функцию расширения Python (написанную на Cвызывается как функция-обертка), которая, в свою очередь, вызывает данную процедуру на Фортране.

Весь этот процесс основан на том, как F2PY интерпретирует типы данных из вашего исходного кода на Фортране - если исходный код скомпилирован с флагами компилятора, которыеИзмените объявленные типы данных, все обязательно закончится сбоем (прямо как real / integer несоответствие вашего первоначального вопроса).Поэтому типы данных для ваших переменных и функций должны быть явно установлены в самом коде Фортрана, см. здесь и здесь о параметре kind Фортрана в целом и здесь относится, в частности, к F2PY.

...