C中的一维自适应网格细化

时间:2017-11-23 17:24:49

标签: c

我是一名正在进入数字相对论领域的物理博士生,我必须在两个维度上实现自适应网格细化代码。和我的程序中的其他任何内容一样,在跳转到更复杂的案例之前,我通常更喜欢做一些更简单的事情来理解发生了什么。但是,我似乎仍然在做一些根本错误的事情。

我的代码执行(或至少应该执行)以下过程:我以大小为h的N个间隔离散x轴。每次计算一个点时,程序都会停止并通过将大小为h的间隔改为另一个间隔h / 2来再次计算该点。程序检查结果是否低于某个用户指定的公差,如果不是,则进程再次以步长h / 4开始,依此类推。以下草图说明了该过程

sketch

在细化函数动作之后,我完全没有兴趣在精炼网格上保留函数的值。我想要的是以最大精度计算粗网格上的函数(在图像中我想保留 - 并且改变 - 是粗[base]网格的黑点的值)。

不幸的是,在细化算法通过后,我发现解决方案没有任何改进。我不认为函数的情节是完美的,但我希望每一点都非常接近解析解。这是我的细化函数(该函数被递归调用,直到达到最大细化级别 - 达到用户指定 - :)

void refine( int l, long double dx, long double x_min, long double x_max, long double f_min, long double *f_max ){
  // l = level of refinement, dx = step size, x_min is current x position, x_max = point we want to calculate, f_min = function evaluated at x_min, f_max = function evaluated at x_max
  int i;
  long double *f_aux, f_point;

  f_aux = (long double *) malloc ( (2*l + 1) * sizeof (long double) );

  dx = 0.5 * dx;

  f_aux[0] = f_min;
  for( i=1; i<2*l+1; i++ ){
    f_aux[i] = ( 1.0 - 2.0 * dx * ( x_min + (i-1)*dx - X0 ) / DELTA ) * f_aux[i-1];
  }

  if( l < lMAX ){
    if( fabs( f_aux[2*l] - *f_max ) > TOL ){
      f_point = f_aux[2*l];
      free( f_aux );
      l++;
      refine( l, dx, x_min+dx, x_max, f_min, &f_point );
    }
    else{
      *f_max = f_aux[2*l];
      free( f_aux );
    }
  }
  else{
    *f_max = f_aux[2*l];
    free( f_aux );
  }

  return;
}

有人能解释一下这个问题吗?我觉得完全卡住了。

提前致谢!

1 个答案:

答案 0 :(得分:0)

看起来你的迭代在最后一点附近增强,但在起点附近仍然很粗糙:

*---------------* refine 0

*-------*-------* refine 1

*-------*---*---* refine 2

*-------*---*-*-* refine 3

并且由于您的等式看起来是双曲线的(取决于之前的所有迭代),解决方案错误本质上是累积性的。我认为你应该从一开始就用精细网格进行迭代以获得适当的解决方案 - 不仅仅是在邻居周围。例如。对于简单的等式df / dx = f,具有这样的实现:

float update(float f_prev, float dx, float /* unused */ target_dx) {
    return f_prev*dx + f_prev;
}

int main(void)
{
    int n = 8;
    float dx = 2.f/(float)(n-1);
    float f[n];
    f[0] = 1.f;

    for (int i = 1; i < n; i++) {
        f[i] = update(f[i-1], dx, dx / 16.f);
    }

    return 0;
}

我会选择简单的递归公式:

float update_recursive(float f_prev, float dx, float target_dx) {
    if (dx > target_dx) {
        return update_recursive(
            update_recursive(f_prev, dx / 2.f, target_dx), dx / 2.f, target_dx
        );
    }

    return dx * f_prev + f_prev;
}

该方法提高了解决方案的质量。终止条件可能比target_dx更适合于解决方案。当然,需要确保递归是有限的。