使用DFT的一维热方程产生不正确的结果(FFTW)

时间:2020-10-01 12:29:59

标签: c++ fftw dft

我正在尝试使用复数到复数IDFT求解一维热方程。问题在于单个时间步长之后的输出似乎不正确。我在下面提供了一个简单的示例来说明问题。

我按如下方式初始化温度状态:
Initial state of the temperature domain

频域中的初始模式为:
k[ 0] = 12.5 + 0i
k[ 1] = 12.5 + 0i
k[ 2] = 12.5 + 0i
k[ 3] = 12.5 + 0i
k[ 4] = 12.5 + 0i
k[-3] = 12.5 + 0i
k[-2] = 12.5 + 0i
k[-1] = 12.5 + 0i

然后我使用标准的一维热方程将频域的状态推进到t=0.02

double alpha = 0.2; // Thermal conductivity constant
double timestep = 0.02;

for (int i = 0; i < N; i++) {
    int k = (i <= N / 2) ? i : i - N;

    F[i][REAL] *= exp(-alpha * k * k * timestep); // Decay the real part
    F[i][IMAG] *= exp(-alpha * k * k * timestep); // Decay the imaginary part
}

t=0.02处的频率模式变为:
k[ 0] = 12.5 + 0i
k[ 1] = 12.45 + 0i
k[ 2] = 12.3 + 0i
k[ 3] = 12.06 + 0i
k[ 4] = 11.73 + 0i
k[-3] = 12.06 + 0i
k[-2] = 12.3 + 0i
k[-1] = 12.45 + 0i

执行IDFT获得t=0.02处的温度域状态后,我得到:
State of the spatial domain at t=0.02

空间和频域似乎都是正确的周期性。但是,根据高斯曲线,热量(空间域中的值)似乎并没有消散。更令人惊讶的是,某些温度降至其初始值以下(它们变为负值!)。

能量守恒定律似乎正确:将所有温度加在一起仍为100。

这是我的全部热量方程代码:

double alpha = 0.2;     // Thermal conductivity constant
double timestep = 0.02; // Physical heat equation timestep
int N = 8;              // Number of data points

fftw_complex* T = (fftw_complex*)fftw_alloc_complex(N); // Temperature domain
fftw_complex* F = (fftw_complex*)fftw_alloc_complex(N); // Frequency domain

fftw_plan plan = fftw_plan_dft_1d(N, F, T, FFTW_BACKWARD, FFTW_MEASURE); // IDFT from frequency to temperature domain

// Initialize all frequency modes such that there is a peak of 100 at x=0 in the temperature domain
// All other other points in the temperature domain are 0
for (int i = 0; i < N; i++) {
    F[i][REAL] = 100.0 / N;
    F[i][IMAG] = 0.0;
}

// Perform the IDFT to obtain the initial state in the temperature domain
fftw_execute(plan);
printTime1d(T, N);
printFrequencies1d(F, N);

// Perform a single timestep of the heat equation to obtain the frequency domain state at t=0.02
for (int i = 0; i < N; i++) {
    int k = (i <= N / 2) ? i : i - N;

    F[i][REAL] *= exp(-alpha * k * k * timestep); // Decay the real part
    F[i][IMAG] *= exp(-alpha * k * k * timestep); // Decay the imaginary part
}

// Perform the IDFT to obtain the temperature domain state at t=0.02
fftw_execute(plan);
printTime1d(T, N);
printFrequencies1d(F, N);

printTime(...)printFrequencies(...)的定义是:

void printTime1d(fftw_complex* data, int N) {
    int rounding_factor = pow(10, 2);

    for (int i = 0; i < N; i++) {
        std::cout << std::setw(8) << round(data[i][REAL] * rounding_factor) / rounding_factor;
    }

    std::cout << std::endl;
}

void printFrequencies1d(fftw_complex* data, int N) {
    int rounding_factor = pow(10, 2);

    for (int i = 0; i < N; i++) {
        int k = (i <= N / 2) ? i : i - N;

        double R = round(data[i][REAL] * rounding_factor) / rounding_factor;
        double I = round(data[i][IMAG] * rounding_factor) / rounding_factor;

        std::cout << "k[" << std::setw(2) << k << "]: " << std::setw(2) << R << ((I < 0) ? " - " : " + ") << std::setw(1) << abs(I) << "i" << std::endl;
    }

    std::cout << std::endl;
}

也许需要注意的是,我也使用复杂的真实IDFT(使用fftw的fftw_plan_dft_c2r_1d())进行了该实验,并且给出了完全相同的结果。

1 个答案:

答案 0 :(得分:2)

您的问题是您没有解决所需的频率,而是在乘以衰减系数后得到函数的以下傅立叶图像:

original data before IDFT

以上结果与您应该得到的结果(高斯)相距太远(至少80分而不是8分):

80-point data before IDFT

请注意,上面第一张图表中的振幅什至没有机会甚至接近零,而是撞到了奈奎斯特频率。显然,您会得到类似于吉布斯现象的伪像:这是傅立叶偏和的通常行为。

80点数据版本的傅立叶逆变换如下:

80-point spatial-domain function

此结果仍然具有负分量(因为我们使用了有限数量的谐波),但是它们的幅度比仅具有8个谐波的幅度小。

请注意,这确实意味着,如果您增加感兴趣的时间值,则可以减少考虑的谐波数量。起初这可能是意外的,但这仅仅是因为高次谐波的衰减快于低次谐波,而且它们再也不会增加。

相关问题