Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Fortran 用有限差分法计算一阶导数和二阶导数时存在较大误差_Fortran_Numerical Methods_Derivative - Fatal编程技术网

Fortran 用有限差分法计算一阶导数和二阶导数时存在较大误差

Fortran 用有限差分法计算一阶导数和二阶导数时存在较大误差,fortran,numerical-methods,derivative,Fortran,Numerical Methods,Derivative,我正在用有限差分法计算存储在3d数组a(n1,n2,n3)中的函数a(x,y,z)的一阶和二阶导数。这里,边界处的函数值为零。以下是Fortran中的代码: implicit none integer i1,i2,i3 integer, parameter :: n1 = 33 integer, parameter :: n2 = 33 integer, parameter :: n3 = 32 real*8 pi, a(n1,n2,n3), a2(n1,n2,n3), z(n3),x(n1

我正在用有限差分法计算存储在3d数组a(n1,n2,n3)中的函数a(x,y,z)的一阶和二阶导数。这里,边界处的函数值为零。以下是Fortran中的代码:

implicit none
integer i1,i2,i3

integer, parameter :: n1 = 33
integer, parameter :: n2 = 33
integer, parameter :: n3 = 32

real*8 pi, a(n1,n2,n3), a2(n1,n2,n3), z(n3),x(n1),y(n2),h,a1(n1,n2,n3)
real*8 num(n1,n2,n3),deno(n1,n2,n3),diff(n1,n2,1),A0,dx,dy

pi=3.14159265358979323846d0
dx=2.0d0*pi/(n1-1)
dy=4.0d0*pi/(n2-1)

do i1=1,n1
  x(i1)=-pi+(i1-1)*dx
  do i2=1,n2
    y(i2)=-2.0d0*pi+(i2-1)*dy
    do i3=1,n3
      z(i3)=(i3-1)*2.0d0*pi/n3
      a(i1,i2,1)= dcos(x(i1)/2.0d0) * dcos(y(i2)/4.0d0)  !input array
      a1(i1,i2,1)= - 0.25d0*dcos(x(i1)/2.0d0) * dsin(y(i2)/4.0d0)  !analytical expression of first order y-derivative
    enddo
  enddo
enddo

do i1=1,n1
  do i2=1,n2
    write(20,*)x(i1),y(i2),a(i1,i2,1)
  enddo
enddo
call d1y(n1,n2,n3,a,a2)
do i1=1,n1
  do i2=1,n2
     num(i1,i2,1)=(a2(i1,i2,1)-a1(i1,i2,1))  !numerator of error calculation
     deno(i1,i2,1)=a2(i1,i2,1)               !denomenator of error calculation
     if (dabs(deno(i1,i2,1)) .lt. 1e-10)deno(i1,i2,1)=1.0d0
     diff(i1,i2,1)=dabs(num(i1,i2,1))/dabs(deno(i1,i2,1))  !relative error in 1st order derivative calculation
    write(21,*)x(i1),y(i2),a(i1,i2,1),a2(i1,i2,1),diff(i1,i2,1),a1(i1,i2,1)
    write(21,*)
  enddo
enddo
end

subroutine d1y(n1,n2,n3,a,a2)
implicit none
integer n1, n2, n3, i1, i2, i3
real*8 pi, a(n1,n2,n3), a2(n1,n2,n3), z(n3),x(n1),y(n2),h,a1(n1,n2,n3)
pi=3.14159265358979323846d0
h=4.0d0*pi/(n2-1)

do i1=1,n1
   do i3=1,n3
      do i2=1,n2
         if(i2 .eq. 1)then
            a2(i1,i2,i3)=( -3.0d0*a(i1,i2,i3) + 4.0d0*a(i1,i2+1,i3) - a(i1,i2+2,i3) )/ (2.0d0*h)
          else if(i2 .eq. n2)then
            a2(i1,i2,i3)=( 3.0d0*a(i1,i2,i3) - 4.0d0*a(i1,i2-1,i3) + a(i1,i2-2,i3) )/ (2.0d0*h)
          else
            a2(i1,i2,i3)=( a(i1,i2+1,i3) - a(i1,i2-1,i3) )/ (2.0d0*h)
          endif
       enddo
    enddo
 enddo     
end subroutine
我的输入函数
a(i1,i2,1)=dcos(x(i1)/2.0d0)*dcos(y(i2)/4.0d0)
,因此示例输入数据(用于17*17*16号网格)

输入和输出功能如下图所示。
由于输出函数与
-0.25d0*dcos(x(i1)/2.0d0)*dsin(y(i2)/4.0d0)
相同,这表明导数计算是正确的。在子程序‘d1y’中,我使用了正向和反向有限差分公式来计算边界处的导数,以及位于两个边界之间的点的中心差分。然后我计算了相对误差。我在y轴的边界处得到了0.003的误差,在边界之间的点处得到了0.0015的误差,如下图所示

我用同样的方法计算了二阶导数。子程序如下所示:

`subroutine d2y(n1,n2,n3,a,a2)
    implicit none
    integer n1, n2, n3, i1, i2, i3
    real*8 pi, a(n1,n2,n3), a2(n1,n2,n3), z(n3),x(n1),y(n2),h
    pi=3.14159265358979323846d0
    h=4.0d0*pi/(n2-1)
    a2=0.0d0
    i3 =1
      do i1=1,n1
        do i2=1,n2
          if(i2 == 1)then
             a2(i1,i2,i3)=( 2.0d0*a(i1,i2,i3) - 5.0d0*a(i1,i2+1,i3) + 4.0d0*a(i1,i2+2,i3) - a(i1,i2+3,i3))/(h*h)
          else if( i2 == n2)then
             a2(i1,i2,i3)=( 2.0d0*a(i1,i2,i3) - 5.0d0*a(i1,i2-1,i3) + 4.0d0*a(i1,i2-2,i3) - a(i1,i2-3,i3))/(h*h)
          else
             a2(i1,i2,i3)= ( a(i1,i2+1,i3) - 2.0d0*a(i1,i2,i3) + a(i1,i2-1,i3) )/(h*h)
          endif
        enddo
      enddo
   ! enddo 

    end subroutine
这里边界处的误差也很大,事实上,与一阶导数相比,误差非常大,如图所示:。
为什么边界会有这么大的误差?请解释一下


`

首先,我站在正确的立场上。“正向差分”和“反向差分”的名称通常指标准的一阶公式。你有正确的二阶单侧一阶导数公式

在x+h和x+2h处,用泰勒级数表达式推导出二阶一阶导数正差分公式,如下所示:

f(x+h)=f(x)+h*f'(x)+h^2*f'(x)/2!+h^3*f'(x)/3

f(x+2h)=f(x)+2h*f'(x)+4h^2*f'(x)/2!+8h^3*f'(x)/3

现在,取第一个级数的4倍,减去第二个级数,求出f'(x)。请注意,二阶导数项消失

f'(x)=3h/2*f(x)-2/h*f(x+h)+1/(2h)*f(x+2h)+0-4h^2*f'(x)/3

这是您正在使用的公式,它是二阶精度。这并不意味着精度与中心差分精度相同,只是精度的顺序相同

如果在保留误差项的同时查看中心差的推导,您会发现误差项如下所示:

-h^2*f''(x)/6

您是否注意到正向差分公式中的误差项前面有一个常数。一般来说,误差总是较大的。但这并不是二阶精度的含义。精度的顺序告诉你当你改变h时误差是如何变化的

例如,如果你把h减半,你应该得到大约4倍的准确度。这意味着在上面的示例中,中心差分误差将从约0.0015到约0.0004,边界误差将从约0.003到约0.0008


最后的收获:你的程序是正确的,你的错误是正确的

不确定是否相关,但在第一个代码段中,您似乎正在打印
a(i1,i2,2)
然后再为其赋值,在这一行:
write(20,*)x(i1),y(i2),a(i1,i2,2)
顺便说一句,欢迎使用StackOverflow。请拿着这张支票,核对一下。为了让我们能够更有效地帮助您,请您添加一个用于图表的输入数据样本,并提供一个,好吗?请对您的第一个问题@RodrigoRodrigues作出评论,是的,它应该是一个(i1,i2,1)。我只使用了一个索引,因为我的输入函数是z独立的。当然,边界处的误差更大。前后差分为一阶精度,中心差分为二阶精度。虽然不能在边界上使用中心差分,但有用于边界的二阶公式。使用其中一个。这是一阶精度,Dan Sp.是正确的。您应该检查总的全局误差,该误差应为二阶,因为随着网格细化,边界层越来越小。即使是二阶公式也会在边界处产生一阶局部离散误差。由于边界区域的相对部分随着网格细化而变小,因此全局离散化误差可能是二阶的。
`subroutine d2y(n1,n2,n3,a,a2)
    implicit none
    integer n1, n2, n3, i1, i2, i3
    real*8 pi, a(n1,n2,n3), a2(n1,n2,n3), z(n3),x(n1),y(n2),h
    pi=3.14159265358979323846d0
    h=4.0d0*pi/(n2-1)
    a2=0.0d0
    i3 =1
      do i1=1,n1
        do i2=1,n2
          if(i2 == 1)then
             a2(i1,i2,i3)=( 2.0d0*a(i1,i2,i3) - 5.0d0*a(i1,i2+1,i3) + 4.0d0*a(i1,i2+2,i3) - a(i1,i2+3,i3))/(h*h)
          else if( i2 == n2)then
             a2(i1,i2,i3)=( 2.0d0*a(i1,i2,i3) - 5.0d0*a(i1,i2-1,i3) + 4.0d0*a(i1,i2-2,i3) - a(i1,i2-3,i3))/(h*h)
          else
             a2(i1,i2,i3)= ( a(i1,i2+1,i3) - 2.0d0*a(i1,i2,i3) + a(i1,i2-1,i3) )/(h*h)
          endif
        enddo
      enddo
   ! enddo 

    end subroutine