高效的收敛检查

时间:2010-12-17 11:02:53

标签: fortran

我有一个包含数千个双精度实数的网格。

它正在迭代,我需要它在达到收敛到3位小数时停止。

目标是让它尽可能快地运行,但每次都需要给出相同的结果(至3 dp)。

我正在做这样的事情

REAL(KIND=DP) :: TOL = 0.001_DP

DO WHILE(.NOT. CONVERGED)
    CONVERGED = .TRUE.
    DO I = 1, NUM_POINTS
        NEW POTENTIAL = !blah blah blah
        IF (CONVERGED) THEN
            IF (NEW_POTENTIAL < OLD_POTENTIAL - TOL .OR. NEW_POTENTIAL > OLD_POTENTIAL + TOL) THEN
                CONVERGED = .FALSE.
            END IF
        END IF
        OLD_POTENTIAL = NEW POTENTIAL
    END DO  
END DO

我认为许多IF语句对于性能来说不是太大。我想到最后检查收敛情况;找到平均值(将整个网格求和,除以num_points),并检查它是否以与上述相同的方式收敛,但我不相信这将始终是准确的。

这样做的最佳方式是什么?

3 个答案:

答案 0 :(得分:3)

如果我理解正确,您可以进行某种时间步骤,通过new_potential上的计算在old_potential中创建值。然后把旧的等于新的继续。

您可以使用单个语句

替换现有的收敛测试
converged = all(abs(new_potential - old_potential)<tol)

可能更快。如果测试的速度是一个主要问题,你可以只测试每一次(或每三或四次......)迭代

一些评论:

1)如果您使用了具有2个平面的潜在数组,而不是old_和new_potential,则可以通过在每次迭代结束时交换索引将new_转换为old_。正如你的代码所代表的那样,正在进行大量的数据移动。

2)虽然在语义上你有一个while循环是正确的,但我总是使用一个迭代次数最多的do循环,以防万一从未满足收敛标准。

3)在你的声明REAL(KIND=DP) :: TOL = 0.001_DP中,关于TOL数值的DP规范是多余的,REAL(KIND=DP) :: TOL = 0.001就足够了。我也将它作为一个参数,如果编译器知道它是不可变的,它可能能够优化它的使用。

4)你真的不需要在最外面的循环中执行CONVERGED = .TRUE.,在第一次迭代之前设置它 - 这将为你节省一两纳秒。

最后,如果您的收敛标准是潜在数组中的每个元素都已收敛到3dp,那么您应该测试它。根据建议的平均值构建反例会相对容易。但是,我担心的是你的系统永远不会收敛于每个元素,你应该使用一些矩阵范数计算来确定收敛。在这个主题上,SO不适合上课。

答案 1 :(得分:0)

收敛标准的计算是什么?除非它们更糟,然后计算以推进潜力,最好让IF语句尽快终止循环,而不是猜测大量的迭代以确保获得一个好的解决方案。

Re High Performance Mark的建议#1,如果复制操作是运行时间的重要部分,你也可以使用指针。

确保这些东西的唯一方法是测量运行时间...... Fortran提供了测量CPU和时钟时间的内在函数。否则,您可能会修改您的部分代码以使其更快,也许使其更容易理解并可能引入错误,可能在运行时没有太大改进...如果该部分占用总运行时的少量,没有多少聪明才会有很大的不同。

正如High Performance Mark所说,尽管当前的语义很优雅,但您可能希望防范无限循环。一种方法:

PotentialLoop: do i=1, MaxIter

  blah

  Converged = test...
  if (Converged) exit PotentialLoop

  blah

end do PotentialLoop

if (.NOT. Converged) write (*, *) "error, did not converge"

答案 2 :(得分:0)

I = 1
DO
  NEWPOT = !bla bla bla
  IF (ABS(NEWPOT-OLDPOT).LT.TOL) EXIT
  OLDPOT = NEWPOT
  I = MOD(I,NUMPOINTS) + 1
END DO

也许更好

I = 1
DO
  NEWPOT = !bla bla bla
  IF (ABS(NEWPOT-OLDPOT).LT.TOL) EXIT
  OLDPOT = NEWPOT
  IF (I.EQ.NUMPOINTS) THEN
    I = 1
  ELSE
    I = I + 1
  END IF
END DO