近一个月来,我一直在与一种非常奇怪的虫子作斗争.问你们是我最后的希望.我用C语言编写了一个程序,使用隐式Euler(IE)格式在傅立叶(或倒数)空间中积分2dCahn–Hilliard equation:

IE method

其中"帽子"表示我们在傅立叶空间中:h_q(t_n+1)和h_q(T_N)是h(x,y)在时间t_n和t_(n+1)的FT,N[h_q]是应用于h_q的非线性算子,同样在傅立叶空间中,L_q是线性算子.我不想过多地讨论我正在使用的数值方法的细节,因为我确信问题不是来自那里(我try 使用其他方案).

我的代码其实很简单.这是开始,基本上我声明变量,分配内存,并为FFTW routine 创建计划.

# include <stdlib.h>
# include <stdio.h>
# include <time.h>
# include <math.h>
# include <fftw3.h>
# define pi M_PI

int main(){

// define lattice size and spacing
int Nx = 150;         // n of points on x
int Ny = 150;         // n of points on y
double dx = 0.5;      // bin size on x and y

// define simulation time and time step
long int Nt = 1000;   // n of time steps
double dt = 0.5;      // time step size

// number of frames to plot (at denominator)
long int nframes = Nt/100;

// define the noise
double rn, drift = 0.05;   // punctual drift of h(x)
srand(666);                // seed the RNG

// other variables
int i, j, nt;    // variables for space and time loops

// declare FFTW3 routine
fftw_plan FT_h_hft;   // routine to perform  fourier transform
fftw_plan FT_Nonl_Nonlft;
fftw_plan IFT_hft_h;  // routine to perform  inverse fourier transform

// declare and allocate memory for real variables
double *Linft = fftw_alloc_real(Nx*Ny);
double *Q2 = fftw_alloc_real(Nx*Ny);
double *qx = fftw_alloc_real(Nx);
double *qy = fftw_alloc_real(Ny);

// declare and allocate memory for complex  variables
fftw_complex *dh = fftw_alloc_complex(Nx*Ny);
fftw_complex *dhft = fftw_alloc_complex(Nx*Ny);
fftw_complex *Nonl = fftw_alloc_complex(Nx*Ny);
fftw_complex *Nonlft = fftw_alloc_complex(Nx*Ny);

// create the FFTW plans
FT_h_hft = fftw_plan_dft_2d ( Nx, Ny, dh, dhft, FFTW_FORWARD, FFTW_ESTIMATE );
FT_Nonl_Nonlft = fftw_plan_dft_2d ( Nx, Ny, Nonl, Nonlft, FFTW_FORWARD, FFTW_ESTIMATE );
IFT_hft_h = fftw_plan_dft_2d ( Nx, Ny, dhft, dh, FFTW_BACKWARD, FFTW_ESTIMATE );

// open file to store the data
char acstr[160];
FILE *fp;
sprintf(acstr, "CH2d_IE_dt%.2f_dx%.3f_Nt%ld_Nx%d_Ny%d_#f%.ld.dat",dt,dx,Nt,Nx,Ny,Nt/nframes);

在这个前言之后,我用均匀随机噪声初始化我的函数h(x,y),并对其进行FT.我将h(x,y)的虚部(在代码中为dh[i*Ny+j][1])设置为0,因为它是一个实数函数.然后我计算波矢qxqy,并用它们计算我的方程在傅里叶空间的线性运算符,在代码中是Linft.我只考虑h的四次导数作为线性项,所以线性项的FT简单地是-q^4…但是,我再说一遍,我不想深入我的集成方法的细节.问题不是关于它的.

// generate h(x,y) at initial time
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
    rn = (double) rand()/RAND_MAX;    // extract a random number between 0 and 1
    dh[i*Ny+j][0] = drift-2.0*drift*rn;    // shift of +-drift
    dh[i*Ny+j][1] = 0.0;
  }
}

// execute plan for the first time
fftw_execute (FT_h_hft);

// calculate wavenumbers
for (i = 0; i < Nx; i++) { qx[i] = 2.0*i*pi/(Nx*dx); }
for (i = 0; i < Ny; i++) { qy[i] = 2.0*i*pi/(Ny*dx); }
for (i = 1; i < Nx/2; i++) { qx[Nx-i] = -qx[i]; }
for (i = 1; i < Ny/2; i++) { qy[Ny-i] = -qy[i]; }

// calculate the FT of the linear operator
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
    Q2[i*Ny+j] = qx[i]*qx[i] + qy[j]*qy[j];
    Linft[i*Ny+j] = -Q2[i*Ny+j]*Q2[i*Ny+j];
  }
}

然后,最后是时间循环.从本质上讲,我做的事情如下:

  • 每隔一段时间,我会将数据保存到一个文件中,并在终端上打印一些信息.特别是,我打印了非线性项FT的最高值.我还判断h(x,y)是否发散到无穷大(这不应该发生!),

  • 在直接空间中计算h^3(即dh[i*Ny+j][0]*dh[i*Ny+j][0]*dh[i*Ny+j][0]).同样,虚部设置为0,

  • 以h^3的FT为例,

  • 通过计算-Q^2*(FT[h^3]-FT[h])得到倒易空间中的完全非线性项(即上述IE算法中的N[h_q]).在代码中,我指的是虚数部分的第Nonlft[i*Ny+j][0] = -Q2[i*Ny+j]*(Nonlft[i*Ny+j][0] -dhft[i*Ny+j][0])行和下面的一行.我这样做是因为:

enter image description here

  • 使用IE方法在时间上前进,在直接空间中变换回,然后规格化.

以下是代码:

for(nt = 0; nt < Nt; nt++) {

if((nt % nframes)== 0) {
  printf("%.0f %%\n",((double)nt/(double)Nt)*100);
  printf("Nonlft   %.15f \n",Nonlft[(Nx/2)*(Ny/2)][0]);

  // write data to file
  fp = fopen(acstr,"a");
  for ( i = 0; i < Nx; i++ ) {
    for ( j = 0; j < Ny; j++ ) {
      fprintf(fp, "%4d  %4d  %.6f\n", i, j, dh[i*Ny+j][0]);
      }
  }
  fclose(fp);

}

// check if h is going to infinity
if (isnan(dh[1][0])!=0) {
  printf("crashed!\n");
  return 0;
}

// calculate nonlinear term h^3 in direct space
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
      Nonl[i*Ny+j][0] = dh[i*Ny+j][0]*dh[i*Ny+j][0]*dh[i*Ny+j][0];
      Nonl[i*Ny+j][1] = 0.0;
  }
}

// Fourier transform of nonlinear term
fftw_execute (FT_Nonl_Nonlft);

// second derivative in Fourier space is just multiplication by -q^2
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
    Nonlft[i*Ny+j][0] = -Q2[i*Ny+j]*(Nonlft[i*Ny+j][0] -dhft[i*Ny+j][0]);
    Nonlft[i*Ny+j][1] = -Q2[i*Ny+j]*(Nonlft[i*Ny+j][1] -dhft[i*Ny+j][1]);
  }
}

// Implicit Euler scheme in Fourier space
 for ( i = 0; i < Nx; i++ ) {
    for ( j = 0; j < Ny; j++ ) {
      dhft[i*Ny+j][0] = (dhft[i*Ny+j][0] + dt*Nonlft[i*Ny+j][0])/(1.0 - dt*Linft[i*Ny+j]);
      dhft[i*Ny+j][1] = (dhft[i*Ny+j][1] + dt*Nonlft[i*Ny+j][1])/(1.0 - dt*Linft[i*Ny+j]);
    }
}

// transform h back in direct space
fftw_execute (IFT_hft_h);

// normalize
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
      dh[i*Ny+j][0] = dh[i*Ny+j][0] / (double) (Nx*Ny);
      dh[i*Ny+j][1] = dh[i*Ny+j][1] / (double) (Nx*Ny);
  }
}

}

代码的最后一部分:清空内存并销毁FFTW计划.

// terminate the FFTW3 plan and free memory
fftw_destroy_plan (FT_h_hft);
fftw_destroy_plan (FT_Nonl_Nonlft);
fftw_destroy_plan (IFT_hft_h);

fftw_cleanup();

fftw_free(dh);
fftw_free(Nonl);
fftw_free(qx);
fftw_free(qy);
fftw_free(Q2);
fftw_free(Linft);
fftw_free(dhft);
fftw_free(Nonlft);

return 0;

}

如果我运行此代码,将获得以下输出:

0 %
Nonlft   0.0000000000000000000
1 %
Nonlft   -0.0000000000001353512
2 %
Nonlft   -0.0000000000000115539
3 %
Nonlft   0.0000000001376379599

...

69 %
Nonlft   -12.1987455309071730625
70 %
Nonlft   -70.1631962517720353389
71 %
Nonlft   -252.4941743351609204637
72 %
Nonlft   347.5067875825179726235
73 %
Nonlft   109.3351142318568633982
74 %
Nonlft   39933.1054502610786585137
crashed!

代码在到达末尾之前崩溃了,我们可以看到非线性项正在发散.

现在,对我来说没有意义的是,如果我用以下方式更改计算非线性项的FT的行:

// calculate nonlinear term h^3 -h in direct space
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
      Nonl[i*Ny+j][0] = dh[i*Ny+j][0]*dh[i*Ny+j][0]*dh[i*Ny+j][0] -dh[i*Ny+j][0];
      Nonl[i*Ny+j][1] = 0.0;
  }
}

// Fourier transform of nonlinear term
fftw_execute (FT_Nonl_Nonlft);

// second derivative in Fourier space is just multiplication by -q^2
for ( i = 0; i < Nx; i++ ) {
  for ( j = 0; j < Ny; j++ ) {
    Nonlft[i*Ny+j][0] = -Q2[i*Ny+j]* Nonlft[i*Ny+j][0]; 
    Nonlft[i*Ny+j][1] = -Q2[i*Ny+j]* Nonlft[i*Ny+j][1];
  }
}

这意味着我使用的是这个定义:

enter image description here

而不是这个:

enter image description here

那么代码是完全稳定的,不会出现分歧!即使是几十亿个时间点!Why does this happen, since the two ways of calculating 100 should be equivalent?

非常感谢所有愿意花时间阅读本文并给予我帮助的人!

编辑:让事情变得更奇怪的是,我要指出的是,同一系统在一维时间内不会出现此错误.在一维中,两种计算Nonlft的方法都是稳定的.

编辑:我添加了一个关于函数h(x,y)在崩溃前发生了什么的简短动画.另外:我很快在MATLAB中重新编写了代码,它使用基于FFTW库的快速傅里叶变换函数,而错误没有发生...谜团加深了.

推荐答案

我解决了!! 问题是Nonl项的计算:

  Nonl[i*Ny+j][0] = dh[i*Ny+j][0]*dh[i*Ny+j][0]*dh[i*Ny+j][0];
  Nonl[i*Ny+j][1] = 0.0;

这需要改为:

  Nonl[i*Ny+j][0] = dh[i*Ny+j][0]*dh[i*Ny+j][0]*dh[i*Ny+j][0] -3.0*dh[i*Ny+j][0]*dh[i*Ny+j][1]*dh[i*Ny+j][1];
  Nonl[i*Ny+j][1] = -dh[i*Ny+j][1]*dh[i*Ny+j][1]*dh[i*Ny+j][1] +3.0*dh[i*Ny+j][0]*dh[i*Ny+j][0]*dh[i*Ny+j][1];

换句话说:我需要将dh视为一个复杂函数(即使它应该是实数).

基本上,由于愚蠢的舍入误差,the IFT of the FT of a real function(在我的例子中是dh),is NOT purely real,但是会有一个非常小的虚部.通过设置Nonl[i*Ny+j][1] = 0.0,我完全忽略了这个想象中的部分. 那么,问题是我递归地将FT(dh)、dhft和使用IFT(FT(dh))获得的对象相加,这是Nonlft,但是忽略了剩余的虚部!

Nonlft[i*Ny+j][0] = -Q2[i*Ny+j]*(Nonlft[i*Ny+j][0] -dhft[i*Ny+j][0]);
Nonlft[i*Ny+j][1] = -Q2[i*Ny+j]*(Nonlft[i*Ny+j][1] -dhft[i*Ny+j][1]);

显然,将Nonlft计算为dh^3 -dh,然后

Nonlft[i*Ny+j][0] = -Q2[i*Ny+j]* Nonlft[i*Ny+j][0]; 
Nonlft[i*Ny+j][1] = -Q2[i*Ny+j]* Nonlft[i*Ny+j][1];

避免了做这种"混合"求和的问题.

呸...真是松了一口气!我希望我能把赏金分配给我自己P

编辑:我想补充一点,在使用fftw_plan_dft_2d个函数之前,我使用了fftw_plan_dft_r2c_2dfftw_plan_dft_c2r_2d(从实到复杂,从复杂到实),我看到了同样的错误.然而,如果我不切换到fftw_plan_dft_2d,我想我不可能解决这个问题,因为c2r函数会自动"删除"来自IFT的剩余虚部.If this is the case and I'm not missing something, I think that this should be written somewhere on the FFTW website, to prevent users from running into problems like this.类似于"r2cc2r变换不适合实现伪谱方法".

编辑:我发现another SO question解决了exactly同样的问题.

C++相关问答推荐

如何将FileFilter添加到FileDialog GTK 4

与unions 的未定义行为

字符数组,字符指针,在一种情况下工作,但在另一种情况下不工作?

为什么在C中设置文件的位置并写入文件,填充空字符?

为什么sscanf不能正确地从这个字符串格式中提取所有数字?

双指针指向常量双指针的指针类型赋值不兼容

我在这里正确地解释了C操作顺序吗?

N的值设置为0或1(未定义的行为),而我正在try 学习realloc和Malloc的用法

GCC创建应用于移动项的单独位掩码的目的是什么?

在为hashmap创建加载器时,我的存储桶指向它自己

防止C++中递归函数使用堆栈内存

在C中访问数组中的特定值

C语言中神秘的(我认为)缓冲区溢出

我正在try 将QSORT算法实现为C++中的泛型函数

即使我在C++中空闲,也肯定会丢失内存

为什么GCC 13没有显示正确的二进制表示法?

Makefile无法将代码刷新到ATmega328p

C中的空指针是什么(_N)?

子进程不会修改父进程中的统计信息

在 C 语言中,不使用 sscanf 从字符串中读取数据