Saya ingin menggunakan f2py
Fortran modern. Secara khusus saya mencoba untuk mendapatkan contoh dasar berikut ini untuk bekerja. Ini adalah contoh berguna terkecil yang bisa saya hasilkan.
! 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
Catatan yang n
disimpulkan dari bentuk parameter input x
. Catatan yang y
dialokasikan dan dialokasikan di dalam tubuh subrutin.
Ketika saya kompilasi ini dengan f2py
f2py -c alloc_test.f90 -m alloc
Dan kemudian jalankan dengan Python
from alloc import f
from numpy import ones
x = ones(5)
print f(x)
Saya mendapatkan kesalahan berikut
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (-1,)
Jadi saya pergi dan membuat dan mengedit pyf
file secara manual
f2py -h alloc_test.pyf -m alloc alloc_test.f90
Asli
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
Diubah
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
Sekarang ini berjalan tetapi nilai-nilai output z
selalu 0
. Beberapa pencetakan debug mengungkapkan bahwa n
memiliki nilai 0
dalam subrutin f
. Saya berasumsi bahwa saya kehilangan beberapa f2py
sihir header untuk mengelola situasi ini dengan benar.
Lebih umum apa cara terbaik untuk menghubungkan subrutin di atas ke Python? Saya lebih suka tidak perlu memodifikasi subrutin itu sendiri.
Jawaban:
Saya tidak super akrab dengan internal f2py, tapi saya sangat akrab dengan membungkus Fortran. F2py hanya mengotomatiskan beberapa atau semua hal di bawah ini.
Pertama-tama Anda perlu mengekspor ke C menggunakan modul iso_c_binding, seperti dijelaskan misalnya di sini:
http://fortran90.org/src/best-practices.html#interfacing-with-c
Penafian: Saya adalah penulis utama halaman fortran90.org. Ini adalah satu-satunya platform dan kompiler cara independen untuk memanggil Fortran dari C. Ini adalah F2003, jadi hari ini tidak ada alasan untuk menggunakan cara lain.
Anda hanya dapat mengekspor / memanggil array dengan panjang penuh yang ditentukan (bentuk eksplisit), yaitu:
tetapi tidak menganggap bentuk:
Itu karena bahasa C tidak mendukung array seperti itu sendiri. Ada pembicaraan untuk memasukkan dukungan seperti itu dalam F2008 atau yang lebih baru (saya tidak yakin), dan cara kerjanya adalah melalui beberapa struktur data C pendukung, karena Anda perlu membawa informasi bentuk tentang array.
Di Fortran, Anda sebaiknya menggunakan bentuk asumsi, hanya dalam kasus khusus Anda harus menggunakan bentuk eksplisit, seperti dijelaskan di sini:
http://fortran90.org/src/best-practices.html#arrays
Itu berarti, bahwa Anda perlu menulis pembungkus sederhana di sekitar subrutin bentuk asumsi Anda, yang akan membungkus berbagai hal menjadi array bentuk eksplisit, per tautan pertama saya di atas.
Setelah Anda memiliki tanda tangan C, panggil saja dari Python dengan cara apa pun yang Anda suka, saya menggunakan Cython, tetapi Anda dapat menggunakan ctype, atau C / API dengan tangan.
The
deallocate(y)
pernyataan tidak diperlukan, Fortran deallocates otomatis.http://fortran90.org/src/best-practices.html#allocatable-arrays
real*8
tidak boleh digunakan, melainkanreal(dp)
:http://fortran90.org/src/best-practices.html#floating-point-numbers
Pernyataan
y(:) = 1.0
ini menetapkan 1.0 dalam presisi tunggal, sehingga sisa digit akan acak! Ini adalah perangkap umum:http://fortran90.org/src/gotchas.html#floating-point-numbers
Anda harus menggunakan
y(:) = 1.0_dp
.Alih-alih menulis
y(:) = 1.0_dp
, Anda bisa menulisy = 1
, itu saja. Anda dapat menetapkan bilangan bulat ke angka floating point tanpa kehilangan keakuratan, dan Anda tidak perlu meletakkan redundan(:)
di sana. Jauh lebih sederhana.Dari pada
gunakan saja
dan jangan repot-repot dengan
y
array sama sekali.Anda tidak memerlukan pernyataan "kembali" di akhir subrutin.
Terakhir, Anda mungkin harus menggunakan modul, dan cukup letakkan
implicit none
di tingkat modul dan Anda tidak perlu mengulanginya di setiap subrutin.Kalau tidak, itu terlihat bagus bagiku. Berikut adalah kode mengikuti saran 1-10 di atas ::
Ini menunjukkan subrutin yang disederhanakan serta bungkus C.
Sejauh f2py, mungkin mencoba menulis bungkus ini untuk Anda dan gagal. Saya juga tidak yakin apakah itu menggunakan
iso_c_binding
modul. Jadi untuk semua alasan ini, saya lebih suka membungkus barang dengan tangan. Maka jelaslah apa yang terjadi.sumber
y
tetapi saya ingin membuat sesuatu dialokasikan (kode saya sebenarnya memiliki alokasi non-sepele). Saya tidak tahu tentang banyak poin lainnya. Sepertinya saya harus melihat ke dalam semacam panduan praktik terbaik Fortran90 .... Terima kasih atas jawaban yang menyeluruh!Yang harus Anda lakukan adalah sebagai berikut:
Meskipun ukuran array x dan z sekarang dilewatkan sebagai argumen eksplisit, f2py membuat argumen n opsional. Berikut ini adalah dokumentasi fungsi yang muncul ke python:
Mengimpor dan memanggilnya dari python:
memberikan hasil sebagai berikut:
sumber
n
dan ingin mendapatkan array ukuran2 ** n
. Sejauh ini saya harus melewati 2 ** n sebagai argumen terpisah.