【问题标题】:Using scipy fft and ifft to solve ordinary differential equation numerically使用scipy fft和ifft数值求解常微分方程
【发布时间】:2012-11-14 06:47:11
【问题描述】:

我在时域中有一个ordinary differential equation,如下所示:

C*du/dt = -g*u + I

在哪里I = A*t/tau*exp^(1-t/tau)

在频率域中:

u(w) = I(w)/(g*(1+C/g*j*w))

j 是复数 sqrt(-1)

因此我可以通过使用fast Fourier transform (fft) 进入频率域然后使用ifft 返回来获得u(t)

代码:

t = np.linspace(0.,499.9,5000)
I = q*t*np.exp(1-t/Tau_ca)/Tau_ca
u1 = np.fft.ifft(np.fft.fft(I)/(G_l*(1.+1.j*(C_m/G_l)*np.fft.fftfreq(t.shape[-1]))))

但是,当我将得到的u(t) 与其他方法(例如微分方程的数值积分或其解析形式)进行比较时,它是不正确的。我已经尝试过并没有成功找出我的错误在哪里。

请赐教。

【问题讨论】:

  • 你能发布完整的代码,包括你用于数值积分的代码,包括数值常量的值吗?您使用了哪种数值积分方法?

标签: python scipy fft ode ifft


【解决方案1】:

正弦曲线或复指数的导数与其频率成正比,相移π/2。对于复指数,相移相当于乘以j。例如,d/dt exp(j*Ω*t)== j*Ω * exp(j*Ω*t)== Ω * exp(j*π/2) * exp(j*Ω*t)== Ω * exp(j*(Ω*t + π/2))。所以如果你有傅里叶变换对u(t) <=> U(Ω),那么du/dt <=> jΩ * U(Ω)。积分几乎是逆运算,但如果有直流分量,它可能会增加脉冲:∫udt <=> U(Ω) / jΩ + π*U(0)*δ(Ω)

要使用采样序列逼近导数,您必须按采样率 fs 缩放离散时间角频率 ω(范围从 0 到 2π,或 -π 到 π)。例如,假设离散时间频率为 π/2 弧度/样本,如序列[1, 0, -1, 0, ...]。在原始信号中,这对应于fs/4。导数是d/dt cos(2*π*fs/4*t) == d/dt cos(π/2*fs*t)== -π/2*fs*sin(π/2*fs*t)== π/2*fs*cos(π/2*fs*t + π/2)

您必须以信号带宽两倍以上的fs 进行采样。恰好位于fs/2 的采样组件是不可靠的。具体来说,每个周期只有 2 个样本,fs/2 分量的幅度会交替序列中第一个样本的符号。因此,对于实值信号,fs/2 DFT 分量是实值,相位为 0 或 π 弧度,即a*cos(π*fs*t + {0, π})。给定后者,fs/2 分量的导数是-a*π*fs*cos(π*fs*t + {π/2, 3*π/2}),对于采样时间t == n/fs,它是 0。

(关于后者,DFT 的标准三角插值使用余弦,在这种情况下,样本点处的导数将为零。如果同时对信号及其导数进行采样,则不一定正确。采样丢失fs/2 分量相对于信号的相位,但不相对于它的导数。根据您开始采样的时间,fs/2 分量及其导数在采样点处可能不为零。如果通过幸运的是,其中一个在采样时间为 0,另一个将处于峰值,因为它们之间的弧度为 π/2。)

鉴于fs/2 DFT 分量对于实值信号始终为实值,当您在计算导数或积分的过程中将其乘以j 时,这会在结果中引入一个虚值分量.有一个简单的解决方法。如果N 是偶数,只需将索引N/2 处的fs/2 组件清零。另一个问题是除以 进行积分时除以零。这可以通过在Ω 向量的索引 0 上添加一个小值来解决(例如,finfo(float64).tiny 是最小的双精度浮点数)。

给定Ω = fs * ω,问题中显示的系统在频域中具有以下形式:

H(Ω) = 1 / (g + j*Ω*C)
U(Ω) = I(Ω) * H(Ω)

这是一个单极低通滤波器。您得出的解决方案有两个问题。

  1. 您没有按fs 缩放频率变量w
  2. 您使用fftfreq,它使用范围-0.5 到0.5。你需要 -π 到 π。 实际上你只需要 0 到 π 因为i(t) 是实值的。在这个 在这种情况下,您可以将rfftirfft 用于实值信号,即 跳过计算负频率。

尽管如此,您可能仍然对结果感到失望,因为 DFT 使用信号的周期性扩展。

示例

首先,这是一个 1 Hz 正弦曲线(以 1024 个样本/秒采样 2 秒)的简单示例(以红色绘制),其导数通过 DFT 计算(以蓝色绘制):

from pylab import *

fs = 1024
t = arange(2*fs, dtype=float64) / fs
N = len(t) // 2 + 1    # non-negative frequencies
w = 2 * pi / len(t) * arange(N)
Omega = w * fs

x0 = cos(2*pi*t)    # w0 == 2*pi/fs
X0 = rfft(x0);
# Zero the fs/2 component. It's zero here anyway.
X0[-1] = 0  
dx0 = irfft(X0 * 1j*Omega)
plot(t, x0, 'r', t, dx0, 'b')
show()

这是一个简单的例子——带宽有限的周期性信号。只需确保以足够高的速率对整数个周期进行采样以避免混叠。

下一个例子是三角波,斜率为 1 和 -1,中心和边缘的导数不连续。理想情况下,导数应该是方波,但完美的计算需要无限带宽。取而代之的是Gibbs ringing 在不连续点周围:

t2 = t[:fs]
m = len(t) // (2*len(t2))
x1 = hstack([t2, 1.0 - t2] * m)
X1 = rfft(x1)
X1[-1] = 0
dx1 = irfft(X1 * 1j*Omega)
plot(t, x1, 'r', t, dx1, 'b')
show()

如果您正在求解非周期系统,则 DFT 的隐式周期扩展是有问题的。以下是使用 odeint 和 DFT 的系统解决方案(tau 设置为 0.5s;gC 设置为 1 Hz 转角频率):

from scipy.integrate import odeint

a = 1.0; tau = 0.5
g = 1.0; C = 1.0 / (2 * pi)
i = lambda t: a * t / tau * exp(1 - t / tau)
f = lambda u, t: (-g*u + i(t)) / C

H = 1 / (g + 1j*Omega*C)  # system function
I = rfft(i(t))
I[-1] = 0
U_DFT = I * H
u_dft = irfft(U_DFT)
u_ode = odeint(f, u_dft[0], t)[:,0]

td = t[:-1] + 0.5/fs
subplot('221'); title('odeint u(t)');
plot(t, u_ode)
subplot('222'); title('DFT u(t)');
plot(t, u_dft)
subplot('223'); title('odeint du/dt')
plot(td, diff(u_ode)*fs, 'r',
     t, (-g*u_ode + i(t)) / C, 'b')           
subplot('224'); title('DFT du/dt')
plot(td, diff(u_dft)*fs, 'r',
     t, (-g*u_dft + i(t)) / C, 'b')
show()

du/dt 图表覆盖了由diff 估计的导数(红色)与微分方程的计算值(蓝色)。它们在两种情况下大致相等。我将odeint 的初始值设置为u_dft[0],以表明它返回相同初始值的DFT 解决方案。不同之处在于odeint 解将继续衰减为零,但 DFT 解是周期性的,周期为 2s。在这种情况下,如果对更多 i(t) 进行采样,则 DFT 结果看起来会更好,因为 i(t) 从零开始并衰减到零。

零填充与 DFT 一起用于执行线性卷积。特别是在这种情况下,输入的零填充将有助于将零状态响应的瞬态与其稳态分开。然而,更常用的是拉普拉斯或 z 变换系统函数来分析 ZSR/ZIR。 scipy.signal 拥有分析 LTI 系统的工具,包括以多项式、零极点增益和状态空间形式将连续时间映射到离散时间。

John P. Boyd 在Chebyshev and Fourier Spectral Methods 中讨论了非周期函数的切比雪夫近似方法(在他的密歇根大学页面上免费在线)。

如果您在 Signal ProcessingMathematics Stack Exchanges 上提问,您可能会在此类问题上获得更多帮助。

【讨论】:

  • 哇……这就是答案! :)
猜你喜欢
  • 2023-03-31
  • 2019-04-09
  • 2015-12-13
  • 2012-02-28
  • 2022-07-16
  • 1970-01-01
  • 1970-01-01
  • 2016-04-09
相关资源
最近更新 更多