在下面的Python中,我有func
返回的数组中包含的五个函数,我必须将它们集成。该代码调用使用f2py
生成的外部Fortran模块:
import numpy as np
from numpy import cos, sin , exp
from trapzdv import trapzdv
def func(x):
return np.array([x**2, x**3, cos(x), sin(x), exp(x)])
if __name__ == '__main__':
xs = np.linspace(0.,20.,100)
ans = trapzdv(func,xs,5)
print 'from Fortran:', ans
print 'exact:', np.array([20**3/3., 20**4/4., sin(20.), -cos(20.), exp(20.)])
Fortran例程是:
subroutine trapzdv(f,xs,nf,nxs,result)
integer :: I
double precision :: x1,x2
integer, intent(in) :: nf, nxs
double precision, dimension(nf) :: fx1,fx2
double precision, intent(in), dimension(nxs) :: xs
double precision, intent(out), dimension(nf) :: result
external :: f
result = 0.0
do I = 2,nxs
x1 = xs(I-1)
x2 = xs(I)
fx1 = f(x1)
fx2 = f(x2)
result = result + (fx1+fx2)*(x2-x1)/2
enddo
return
end
问题是Fortran只集成func(x)
中的第一个函数。
查看打印结果:
from Fortran: [ 2666.80270721 2666.80270721 2666.80270721 2666.80270721 2666.80270721]
exact: [ 2.66666667e+03 4.00000000e+04 9.12945251e-01 -4.08082062e-01 4.85165195e+08]
一种工作方式,即修改func(x)
以返回给定值
在函数数组中的位置:
def func(x,i):
return np.array([x**2, x**3, cos(x), sin(x), exp(x)])[i-1]
然后更改Fortran例程以使用两个参数调用该函数:
subroutine trapzdv(f,xs,nf,nxs,result)
integer :: I
double precision :: x1,x2,fx1,fx2
integer, intent(in) :: nf, nxs
double precision, intent(in), dimension(nxs) :: xs
double precision, intent(out), dimension(nf) :: result
external :: f
result = 0.0
do I = 2,nxs
x1 = xs(I-1)
x2 = xs(I)
do J = 1,nf
fx1 = f(x1,J)
fx2 = f(x2,J)
result(J) = result(J) + (fx1+fx2)*(x2-x1)/2
enddo
enddo
return
end
哪个有效:
from Fortran: [ 2.66680271e+03 4.00040812e+04 9.09838195e-01 5.89903440e-01 4.86814128e+08]
exact: [ 2.66666667e+03 4.00000000e+04 9.12945251e-01 -4.08082062e-01 4.85165195e+08]
但是这里func
被调用的次数超过了必要的5倍(在实际案例中func
具有300多个功能,因此它将被称为300倍以上。
func(x)
返回的所有数组?换句话说,将Fortran构建fx1 = f(x1)
作为一个数组,其中包含与func(x)
中的函数对应的5个元素。 OBS:我正在使用f2py -c --compiler=mingw32 -m trapzdv trapzdv.f90
答案 0 :(得分:2)
不幸的是,你不能将数组从python函数返回到Fortran。你需要一个子程序(意味着用call
语句调用它),这是f2py
不允许你做的事情。
在Fortran 90中,您可以创建返回数组的函数,但同样这不是f2py
可以执行的操作,尤其是因为您的函数不是Fortran函数。
您唯一的选择是使用循环解决方法,或重新设计您希望python和Fortran进行交互的方式。
答案 1 :(得分:1)
尽管这个答案并没有解决问题,但它是一种解决方法,在Cython中做同样的事情。这里,梯形规则和多项式积分器用于矢量值函数。下面的代码我放入integratev.pyx
:
import numpy as np
from numpy.linalg import inv
cimport numpy as np
FLOAT = np.float32
ctypedef np.float_t FLOAT_t
def trapzv(f, np.ndarray xs, int nf):
cdef int nxs = xs.shape[0]
cdef np.ndarray ans = np.zeros(nf, dtype=FLOAT)
cdef double x1, x2
for i in range(1,nxs):
x1 = xs[i-1]
x2 = xs[i]
ans += (f(x2)+f(x1))*(x2-x1)/2.
return ans
def poly(f, np.ndarray xs, int nf, int order=2):
cdef int nxs = xs.shape[0]
cdef np.ndarray ans = np.zeros(nf, dtype=FLOAT)
cdef np.ndarray xis = np.zeros(order+1, dtype=FLOAT)
cdef np.ndarray ais
if nxs % (order+1) != 0:
raise ValueError("poly: The size of xs must be a multiple of 'order+1'")
for i in range(order,nxs,order):
xis = xs[i-order:i+1]
X = np.concatenate([(xis**i)[:,None] for i in range(order+1)], axis=1)
ais = np.dot( inv(X), f(xis).transpose() )
for k in range(1,order+2):
ans += ais[k-1,:]/k * (xis[-1]**k - xis[0]**k)
return ans
使用以下测试:
import numpy as np
from numpy import cos, sin , exp
import pyximport; pyximport.install()
import integratev
from subprocess import Popen
def func(x):
return np.array([x**2, x**3, cos(x), sin(x), exp(x)])
if __name__ == '__main__':
xs = np.linspace(0.,20.,33)
print 'exact:', np.array([20**3/3., 20**4/4., sin(20.), -cos(20.)+1, exp(20.)-1])
ans = integratev.trapzv(func,xs,5)
print 'trapzv:', ans
ans = integratev.poly(func,xs,5,2)
print 'poly:', ans
,并提供:
exact: [ 2.66666667e+03 4.00000000e+04 9.12945251e-01 5.91917938e-01 4.85165194e+08]
trapzv: [ 2.66796875e+03 4.00390625e+04 8.83031547e-01 5.72522998e-01 5.00856448e+08]
poly: [ 2.66666675e+03 4.00000000e+04 9.13748980e-01 5.92435718e-01 4.85562144e+08]
poly可以是任何顺序,这可能会在大多数情况下提供更好的结果...