Ayrılabilir ve varsayılan şekil dizileriyle F2Py


18

f2pyModern Fortran ile kullanmak istiyorum . Özellikle çalışmak için aşağıdaki temel örnek almaya çalışıyorum. Bu, üretebileceğim en küçük yararlı örnektir.

! 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

Not ngirdi parametresi şeklinde anlaşılmaktadır x. yAltyordamın gövdesi içinde tahsis edildiğini ve yerleştirildiğini unutmayın .

Bunu derlediğimde f2py

f2py -c alloc_test.f90 -m alloc

Ve sonra Python'da koş

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

Aşağıdaki hatayı alıyorum

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

Bu yüzden gidip pyfdosyayı elle oluşturup düzenliyorum

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

orijinal

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

Değiştirilmiş

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

Şimdi çalışıyor ama çıktı zdeğerleri her zaman 0. Bazı hata ayıklama baskısı , altyordam içinde bir ndeğere sahip olduğunu ortaya çıkarır . Bu durumu düzgün bir şekilde yönetmek için bazı başlık sihirim eksik olduğunu düşünüyorum . 0ff2py

Daha genel olarak yukarıdaki alt rutini Python'a bağlamanın en iyi yolu nedir? Alt rutinin kendisini değiştirmemeyi kesinlikle tercih ederim.


Matt, Ondrej Certik'in en iyi uygulamalar rehberine, özellikle Python ile Arayüz bölümüne aşina mısınız? PyClaw için benzer bir arayüz konusunu tartıştık ve henüz bu noktada
çözmedik

Yanıtlar:


23

Ben f2py içlerine çok aşina değilim, ama Fortran'ı sarmaya çok aşinayım. F2py aşağıdaki şeylerin bir kısmını veya tamamını otomatik hale getirir.

  1. Önce, örneğin burada açıklandığı gibi iso_c_binding modülünü kullanarak C'ye dışa aktarmanız gerekir:

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

    Feragatname: Ben fortran90.org sayfalarının ana yazarıyım. Bu, Fortran'ı C'den çağırmanın tek platform ve derleyici bağımsız yoludur. Bu F2003, bu yüzden bu günlerde başka bir yol kullanmanın bir nedeni yok.

  2. Yalnızca tam uzunlukta (açık şekil) belirtilen dizileri dışa aktarabilir / çağırabilirsiniz, yani:

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

    ama şekil almayın:

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

    Çünkü C dili bu tür dizileri desteklemez. Böyle bir desteği F2008 veya daha sonra dahil etmek için konuşma var (emin değilim) ve çalışma şekli, dizi hakkında şekil bilgileri taşımanız gerektiği için bazı destekleyici C veri yapılarından geçiyor.

    Fortran'da, esas olarak varsayım şeklini kullanmalısınız, sadece özel durumlarda burada açıklandığı gibi açık şekil kullanmalısınız:

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

    Bu, yukarıdaki ilk bağlantım uyarınca, şeyleri açık şekil dizilerine saran varsayım şekli alt programınızın etrafına basit bir sarmalayıcı yazmanız gerektiği anlamına gelir.

  3. C imzanız olduğunda, istediğiniz şekilde Python'dan arayın, Cython kullanıyorum, ancak ctype veya C / API'yi elle kullanabilirsiniz.

  4. deallocate(y)Gerekli değildir açıklamada, Fortran otomatik serbest bırakır.

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

  5. real*8kullanılmamalı, aksine real(dp):

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

  6. İfadesi y(:) = 1.0tek hassasiyet içinde 1.0 atama, böylece basamak kalanı rastgele olacak! Bu yaygın bir tuzaktır:

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

    Kullanmanız gerekiyor y(:) = 1.0_dp.

  7. y(:) = 1.0_dpYazmak yerine, sadece yazabilirsiniz y = 1, hepsi bu. Doğruluğu kaybetmeden bir kayan nokta numarasına tamsayı atayabilirsiniz ve fazlalığı (:)oraya koymanıza gerek yoktur. Çok daha basit.

  8. Onun yerine

    y = 1
    z = x + y

    sadece kullan

    z = x + 1

    ve ydizi ile hiç uğraşmayın .

  9. Altyordamın sonunda "return" deyimine ihtiyacınız yoktur.

  10. Son olarak, muhtemelen modüller kullanmalı ve sadece implicit nonemodül seviyesini koymalısınız ve her altyordamda tekrarlamanız gerekmez.

    Aksi takdirde bana iyi geliyor. Yukarıdaki 1-10 önerilerini izleyen kod:

    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

    Basitleştirilmiş alt rutini ve bir C sargısını gösterir.

    F2py kadarıyla, muhtemelen bu sarmalayıcıyı sizin için yazmaya çalışır ve başarısız olur. Ayrıca iso_c_bindingmodülü kullanıp kullanmadığından da emin değilim . Tüm bu nedenlerden ötürü, işleri elle sarmayı tercih ediyorum. O zaman ne olduğu tam olarak açık.


Bildiğim kadarıyla, f2py ISO C bağlarına dayanmıyor (birincil hedefi Fortran 77 ve Fortran 90 kodu).
Aron Ahmadia

Biraz aptal olduğumu biliyordum yama bir şey tahsis edildi yapmak istedim (benim gerçek kod önemsiz olmayan tahsisleri vardır). Diğer noktaların çoğunu bilmiyordum. Görünüşe göre Fortran90 en iyi uygulamalar kılavuzu bir tür bakmak gerekir .... Kapsamlı cevap için teşekkürler!
MRocklin

Bugünün Fortran derleyicilerini kullanarak, F77'yi aynı şekilde sararsınız - basit bir iso_c_binding sarıcı yazarak ve eski f77 alt yordamını çağırırsınız.
Ondřej Čertík

6

Tek yapmanız gereken aşağıdakiler:

!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 ve z dizisinin boyutu artık açık bir bağımsız değişken olarak iletilse de, f2py n bağımsız değişkenini isteğe bağlı yapar. Python'a göründüğü gibi fonksiyonun öğretisi aşağıdadır:

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)

Python'dan içe aktarma ve çağırma:

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

aşağıdaki çıktıyı verir:

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

Önemsiz bazı ifadeleri boyut olarak kullanmanın bir yolu var mı? Örneğin, ben geçmek nve bir dizi boyutu almak istiyorum 2 ** n. Şimdiye kadar ayrı bir argüman olarak 2 ** n'yi de geçmek zorundayım.
Alleo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.