할당 가능하고 가정 된 모양 배열을 가진 F2Py


18

f2py현대의 포트란과 함께 사용 하고 싶습니다 . 특히 다음 기본 예제를 작동 시키려고합니다. 이것은 내가 생성 할 수있는 가장 작은 유용한 예입니다.

! alloc_test.f90
subroutine f(x, z)
  implicit none

! Argument Declarations !
  real*8, intent(in) ::  x(:)
  real*8, intent(out) :: z(:)

! Variable Declarations !
  real*8, allocatable :: y(:)
  integer :: n

! Variable Initializations !
  n = size(x)
  allocate(y(n))

! Statements !
  y(:) = 1.0
  z = x + y

  deallocate(y)
  return
end subroutine f

참고 n입력 매개 변수의 형태로부터 추론된다 x. 참고 y할당 서브 루틴의 몸에서 해제된다.

내가 이것을 컴파일 할 때 f2py

f2py -c alloc_test.f90 -m alloc

그런 다음 파이썬에서 실행하십시오.

from alloc import f
from numpy import ones
x = ones(5)
print f(x)

다음과 같은 오류가 발생합니다

ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (-1,)

가서 pyf파일을 수동으로 작성하고 편집합니다.

f2py -h alloc_test.pyf -m alloc alloc_test.f90

기발한

python module alloc ! in 
    interface  ! in :alloc
        subroutine f(x,z) ! in :alloc:alloc_test.f90
            real*8 dimension(:),intent(in) :: x
            real*8 dimension(:),intent(out) :: z
        end subroutine f
    end interface 
end python module alloc

수정

python module alloc ! in 
    interface  ! in :alloc
        subroutine f(x,z,n) ! in :alloc:alloc_test.f90
            integer, intent(in) :: n
            real*8 dimension(n),intent(in) :: x
            real*8 dimension(n),intent(out) :: z
        end subroutine f
    end interface 
end python module alloc

이제는 실행되지만 출력 값 z은 항상 0입니다. 일부 디버그 인쇄는 서브 루틴 내에 n값 을 가지고 있음을 나타냅니다 . 이 상황을 올바르게 관리하기 위해 헤더 마술이 누락되었다고 가정합니다 . 0ff2py

더 일반적으로 위의 서브 루틴을 파이썬에 연결하는 가장 좋은 방법은 무엇입니까? 서브 루틴 자체를 수정하지 않아도되는 것이 좋습니다.


Matt, Ondrej Certik의 모범 사례 가이드, 특히 Python과인터페이스 섹션에 익숙 하십니까? 우리는 PyClaw와 유사한 인터페이스 문제를 논의하고 있으며 아직이 시점에서 해결하지 못했습니다 :)
Aron Ahmadia

답변:


23

f2py 내부에는 익숙하지 않지만 Fortran 래핑에는 매우 익숙합니다. F2py는 아래 사항 중 일부 또는 전부를 자동화합니다.

  1. 먼저 여기에 설명 된대로 iso_c_binding 모듈을 사용하여 C로 내 보내야합니다.

    http://fortran90.org/src/best-practices.html#interfacing-with-c

    면책 조항 : 나는 fortran90.org 페이지의 주요 저자입니다. 이것은 C에서 Fortran을 호출하는 유일한 플랫폼 및 컴파일러 독립적 방법입니다. 이것은 F2003이므로 요즘 다른 방법을 사용할 이유가 없습니다.

  2. 전체 길이가 지정된 (명시 적 모양) 배열 만 내보내거나 호출 할 수 있습니다.

    integer(c_int), intent(in) :: N
    real(c_double), intent(out) :: mesh(N)

    그러나 모양을 가정하지는 마십시오.

    real(c_double), intent(out) :: mesh(:)

    C 언어는 이러한 배열 자체를 지원하지 않기 때문입니다. F2008 이상에서 그러한 지원을 포함시킬 이야기가 있습니다 (확실하지 않음). 어떻게 작동하는지는 배열에 대한 모양 정보를 전달해야하기 때문에 일부 C 데이터 구조를 지원하는 것입니다.

    Fortran에서는 주로 가정 모양을 사용해야하며, 특별한 경우에만 여기에 설명 된대로 명시 적 모양을 사용해야합니다.

    http://fortran90.org/src/best-practices.html#arrays

    즉, 위의 첫 번째 링크마다 모양 서브 루틴으로 간단한 래퍼를 작성하여 명시 적 모양 배열로 래핑해야합니다.

  3. C 서명이 있으면 원하는 방식으로 Python에서 호출하면 Cython을 사용하지만 ctype 또는 C / API를 직접 사용할 수 있습니다.

  4. deallocate(y)필요하지 않은 문장은, 포트란가 자동으로 해제한다.

    http://fortran90.org/src/best-practices.html#allocatable-arrays

  5. real*8사용해서는 안되지만 오히려 real(dp):

    http://fortran90.org/src/best-practices.html#floating-point-numbers

  6. 이 문장 y(:) = 1.0은 단 정밀도로 1.0을 할당하므로 나머지 숫자는 임의입니다! 이것은 일반적인 함정입니다.

    http://fortran90.org/src/gotchas.html#floating-point-numbers

    를 사용해야 y(:) = 1.0_dp합니다.

  7. 글을 쓰는 대신 글을 쓸 y(:) = 1.0_dp수 있습니다 y = 1. 정확도를 잃지 않고 부동 소수점 숫자에 정수를 할당 할 수 있으며 중복 (:)을 여기 에 넣을 필요는 없습니다 . 훨씬 간단합니다.

  8. 대신에

    y = 1
    z = x + y

    그냥 사용

    z = x + 1

    y배열을 전혀 신경 쓰지 마십시오 .

  9. 서브 루틴의 끝에 "return"문이 필요하지 않습니다.

  10. 마지막으로, 당신은 아마도 모듈을 사용 implicit none하고 모듈 레벨에 놓아야 하며 각 서브 루틴에서 반복 할 필요가 없습니다.

    그렇지 않으면 나에게 좋아 보인다. 위의 제안 1-10을 따르는 코드는 다음과 같습니다.

    module test
    use iso_c_binding, only: c_double, c_int
    implicit none
    integer, parameter :: dp=kind(0.d0)
    
    contains
    
    subroutine f(x, z)
    real(dp), intent(in) ::  x(:)
    real(dp), intent(out) :: z(:)
    z = x + 1
    end subroutine
    
    subroutine c_f(n, x, z) bind(c)
    integer(c_int), intent(in) :: n
    real(c_double), intent(in) ::  x(n)
    real(c_double), intent(out) :: z(n)
    call f(x, z)
    end subroutine
    
    end module

    단순화 된 서브 루틴 및 C 랩퍼를 보여줍니다.

    f2py까지는 아마도이 래퍼를 쓰려고 시도하고 실패합니다. 또한 iso_c_binding모듈을 사용하고 있는지 확실하지 않습니다 . 이 모든 이유로, 나는 물건을 손으로 감싸는 것을 선호합니다. 그렇다면 무슨 일이 일어나고 있는지 분명합니다.


내가 아는 한 f2py는 ISO C 바인딩에 의존하지 않습니다 (주요 대상은 Fortran 77 및 Fortran 90 코드입니다).
Aron Ahmadia

나는 약간 바보 같은 것을 알고 있었지만 y무언가를 할당하고 싶었습니다 (실제 코드에는 사소한 할당이 있습니다). 그래도 다른 많은 점에 대해서는 몰랐습니다. Fortran90 모범 사례 가이드를 살펴 봐야 할 것 같습니다 ... 철저한 답변을 주셔서 감사합니다!
MRocklin

오늘날의 포트란 컴파일러를 사용하면 간단한 iso_c_binding 래퍼를 작성하여 레거시 f77 서브 루틴을 호출하여 F77을 정확히 같은 방식으로 포장 할 수 있습니다.
Ondřej Čertík

6

다음과 같이하면됩니다 :

!alloc_test.f90
subroutine f(x, z, n)
  implicit none

! Argument Declarations !
  integer :: n
  real*8, intent(in) ::  x(n)
  real*8, intent(out) :: z(n)

! Variable Declarations !
  real*8, allocatable :: y(:)

! Variable Initializations !
  allocate(y(n))

! Statements !
  y(:) = 1.0
  z = x + y

  deallocate(y)
  return
end subroutine f

배열 x와 z의 크기가 이제 명시적인 인수로 전달되었지만 f2py는 인수 n을 선택적으로 만듭니다. 다음은 파이썬에 나타나는 함수의 docstring입니다.

Type:       fortran
String Form:<fortran object>
Docstring:
f - Function signature:
  z = f(x,[n])
Required arguments:
  x : input rank-1 array('d') with bounds (n)
Optional arguments:
  n := len(x) input int
Return objects:
  z : rank-1 array('d') with bounds (n)

파이썬에서 가져 오기 및 호출하기 :

from alloc import f
from numpy import ones
x = ones(5)
print f(x)

다음과 같은 출력을 제공합니다.

[ 2.  2.  2.  2.  2.]

사소한 표현을 크기로 사용하는 방법이 있습니까? 예를 들어, 나는 전달 n하고 size 배열을 얻고 싶습니다 2 ** n. 지금까지 나는 별도의 인수로 2 ** n도 전달해야합니다.
Alleo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.