F2Py với mảng hình dạng phân bổ và giả định


18

Tôi muốn sử dụng f2pyvới Fortran hiện đại. Cụ thể, tôi đang cố gắng lấy ví dụ cơ bản sau để làm việc. Đây là ví dụ hữu ích nhỏ nhất tôi có thể tạo ra.

! 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

Lưu ý rằng nđược suy ra từ hình dạng của tham số đầu vào x. Lưu ý rằng yđược phân bổ và giải quyết trong cơ thể của chương trình con.

Khi tôi biên dịch cái này với f2py

f2py -c alloc_test.f90 -m alloc

Và sau đó chạy trong Python

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

Tôi nhận được lỗi sau đây

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

Vì vậy, tôi đi và tạo và chỉnh sửa các pyftập tin bằng tay

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

Nguyên

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

Sửa đổi

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

Bây giờ nó chạy nhưng các giá trị của đầu ra zluôn luôn 0. Một số in gỡ lỗi cho thấy ncó giá trị 0trong chương trình con f. Tôi cho rằng tôi đang thiếu một số f2pyma thuật tiêu đề để quản lý tình huống này đúng cách.

Nói chung, cách tốt nhất để liên kết chương trình con trên với Python là gì? Tôi thực sự không muốn phải sửa đổi chương trình con.


Matt, bạn có quen thuộc với hướng dẫn thực hành tốt nhất của Ondrej Certik, cụ thể là phần Giao diện với Python không? Chúng tôi đã thảo luận về một vấn đề can thiệp tương tự cho PyClaw và vẫn chưa giải quyết được vấn đề này vào thời điểm này :)
Aron Ahmadia

Câu trả lời:


23

Tôi không quá quen thuộc với nội bộ f2py, nhưng tôi rất quen thuộc với việc bọc Fortran. F2py chỉ tự động hóa một số hoặc tất cả những điều dưới đây.

  1. Trước tiên bạn cần xuất sang C bằng mô-đun iso_c_binding, như được mô tả ví dụ ở đây:

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

    Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả chính của trang fortran90.org. Đây là cách duy nhất để gọi Fortran từ nền tảng và trình biên dịch độc lập từ C. Đây là F2003, vì vậy ngày nay không có lý do gì để sử dụng bất kỳ cách nào khác.

  2. Bạn chỉ có thể xuất / gọi các mảng với độ dài được chỉ định đầy đủ (hình dạng rõ ràng), đó là:

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

    nhưng không giả định hình dạng:

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

    Đó là bởi vì ngôn ngữ C không hỗ trợ các mảng như vậy. Có một cuộc thảo luận bao gồm hỗ trợ như vậy trong F2008 trở lên (tôi không chắc chắn) và cách thức hoạt động của nó là thông qua một số cấu trúc dữ liệu C hỗ trợ, vì bạn cần mang thông tin hình dạng về mảng.

    Trong Fortran, bạn chủ yếu nên sử dụng hình dạng giả định, chỉ trong trường hợp đặc biệt bạn nên sử dụng hình dạng rõ ràng, như được mô tả ở đây:

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

    Điều đó có nghĩa là, bạn cần phải viết một trình bao bọc đơn giản xung quanh chương trình con hình dạng giả định của bạn, nó sẽ bao bọc mọi thứ thành các mảng hình dạng rõ ràng, theo liên kết đầu tiên của tôi ở trên.

  3. Khi bạn có chữ ký C, chỉ cần gọi nó từ Python theo bất kỳ cách nào bạn thích, tôi sử dụng Cython, nhưng bạn có thể sử dụng ctype hoặc C / API bằng tay.

  4. Các deallocate(y)tuyên bố là không cần thiết, Fortran deallocates tự động.

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

  5. real*8không nên được sử dụng, mà là real(dp):

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

  6. Câu lệnh y(:) = 1.0được gán 1.0 với độ chính xác duy nhất, vì vậy các chữ số còn lại sẽ là ngẫu nhiên! Đây là một cạm bẫy phổ biến:

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

    Bạn cần sử dụng y(:) = 1.0_dp.

  7. Thay vì viết y(:) = 1.0_dp, bạn chỉ có thể viết y = 1, vậy thôi. Bạn có thể gán số nguyên cho số dấu phẩy động mà không làm mất độ chính xác và bạn không cần đặt phần thừa (:)vào đó. Đơn giản hơn nhiều.

  8. Thay vì

    y = 1
    z = x + y

    chỉ dùng

    z = x + 1

    và đừng bận tâm với ymảng nào cả.

  9. Bạn không cần câu lệnh "return" ở cuối chương trình con.

  10. Cuối cùng, có lẽ bạn nên sử dụng các mô-đun và chỉ cần đặt implicit noneở cấp độ mô-đun và bạn không cần lặp lại nó trong mỗi chương trình con.

    Nếu không thì có vẻ tốt với tôi. Đây là đoạn mã theo các gợi ý 1-10 ở trên ::

    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

    Nó cho thấy chương trình con được đơn giản hóa cũng như trình bao bọc C.

    Theo như f2py, nó có thể cố gắng viết trình bao bọc này cho bạn và không thành công. Tôi cũng không chắc chắn liệu nó đang sử dụng iso_c_bindingmô-đun. Vì vậy, vì tất cả những lý do này, tôi thích bọc đồ bằng tay. Sau đó, chính xác rõ ràng những gì đang xảy ra.


Theo như tôi biết, f2py không dựa vào các ràng buộc ISO C (mục tiêu chính của nó là mã Fortran 77 và Fortran 90).
Aron Ahmadia

Tôi biết rằng tôi hơi ngu ngốc ynhưng tôi muốn làm cho một cái gì đó được phân bổ (mã thực tế của tôi có phân bổ không tầm thường). Tôi không biết về nhiều điểm khác. Có vẻ như tôi nên xem xét một số hướng dẫn thực hành tốt nhất của Fortran90 .... Cảm ơn câu trả lời thấu đáo!
MRocklin

Lưu ý rằng bằng cách sử dụng trình biên dịch Fortran ngày nay, bạn bọc F77 theo cùng một cách chính xác --- bằng cách viết một trình bao bọc iso_c_binding đơn giản và gọi chương trình con f77 kế thừa từ nó.
Ondřej ertík

6

Tất cả bạn phải làm là như sau:

!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

Mặc dù kích thước của mảng x và z hiện được truyền dưới dạng đối số rõ ràng, f2py làm cho đối số n tùy chọn. Sau đây là chuỗi doc của hàm khi nó xuất hiện với python:

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)

Nhập và gọi nó từ python:

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

đưa ra đầu ra sau:

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

Có cách nào để sử dụng một số biểu hiện không tầm thường như kích thước không? Ví dụ, tôi vượt qua nvà muốn có được một mảng kích thước 2 ** n. Cho đến nay tôi cũng phải vượt qua 2 ** n như một đối số riêng biệt.
Alleo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.