할당 가능하고 가정 된 모양 배열을 가진 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.