Динамическая передача параметров в подпрограммах обратного вызова Python для кода Fortran - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть какой-то старый код Fortran, который я хочу обернуть с помощью f2py и который мне не разрешено изменять.

Ядром программы является некоторая подпрограмма, которая зависит от пользовательских подпрограмм. Все этих подпрограмм совместно используют массивы общих параметров для обмена предоставленными пользователем данными между подпрограммами.Эти массивы параметров являются одномерными и поэтому не ожидают какого-либо предварительно определенного размера ввода в основных заголовках подпрограмм.

Однако, похоже, что заголовкам функций обратного вызова python требуется точная информация о фактическом размеремассивы.

Давайте рассмотрим пример:

Это мой код Fortran:

  Subroutine myscript(x,fun,o,n,rpar, ipar)
  external fun
  integer n
  double precision x(n,*)
  double precision o(n)
  double precision rpar(*)
  integer ipar(*)
  write(*,*) n
  !write(*,*) x(1,:)
  call fun(n,x,o,rpar,ipar)
  write(*,*) o
  write(*,*) rpar(1)
  end

А это моя часть Python:

    import numpy as np
    import routine
    import ipdb
    def fun(X, rpar):
        print("In Callback function")
        ipdb.set_trace()
        print(X)
        O = X[1,:]
        print(O)
        return O
    O = np.array([1.,0.])
    X = np.identity(2)
    rpar = np.array([1.,1.])

    routine.myscript(X, fun, rpar)

Я скомпилировал с f2py -c routine.pyf routine.f и следующий заголовочный файл

    !    -*- f90 -*-
    ! Note: the context of this file is case sensitive.

    python module myscript__user__routines 
    interface myscript_user_interface 
        subroutine fun(n,x,o,rpar,ipar) ! in :routine:routine.f:myscript:unknown_interface
        integer, optional,intent(hide),check(shape(x,0)==n),depend(x) :: n=shape(x,0)
        double precision dimension(n,n),intent(in) :: x
        double precision dimension(n),intent(out),depend(n) :: o
        double precision dimension(100),intent(in) :: rpar
        integer, optional, dimension(*) :: ipar
        end subroutine fun
    end interface myscript_user_interface
    end python module myscript__user__routines
    python module routine ! in 
    interface  ! in :routine
        subroutine myscript(x,fun,o,n,rpar,ipar) ! in :routine:routine.f
        use myscript__user__routines
        double precision dimension(n,*),intent(in,copy) :: x
        external fun
        double precision dimension(n),intent(out),depend(n) :: o
        integer, optional,intent(hide),check(shape(x,0)==n),depend(x) :: n=shape(x,0)
        double precision dimension(shape(rpar,0)) :: rpar
        integer, optional, dimension(2) :: ipar
        end subroutine myscript
    end interface 
    end python module routine

    ! This file was auto-generated with f2py (version:2).
    ! See http://cens.ioc.ee/projects/f2py2e/

Важная строка в заголовочном файле

   double precision dimension(100),intent(in) :: rpar

, когда в отладчике Python rpar теперь является100-мерный массив, где последние 98 значений являются мусором (не удивительно).

изменение размера на * просто возвращает пустой массив

ipdb> rpar
array([], dtype=float64)

изменение размеров на shape(rpar,0) какв основной подпрограмме выдает ошибку

ValueError: negative dimensions are not allowed

Существует ли элегантный способ позволить пользователю python динамически определять размерность массива параметров rpar?Ожидаемое значение в примере - 2, потому что это размер входного массива.

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

...