2010-12-17 11 views
1

私は何千もの倍精度実数を持つグリッドを持っています。効率的な収束チェック

小数点以下3桁まで収束したら中止する必要があります。

ターゲットは可能な限り高速に実行されますが、毎回同じ結果(毎回3 dp)を与える必要があります。私はIF、多くのステートメントは、パフォーマンスのためにあまりにも素晴らしいことではないことを考えています。この

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 

のようなものをやっている分で

。私は最後に収束を確認することを考えました。 (グリッド全体を合計し、num_pointsで割る)平均値を求め、それが上記と同じ方法で収束しているかどうかを確認しますが、これは常に正確であると確信していません。

これを行う最善の方法は何ですか?

答えて

3

私が正しく理解していれば、の計算でnew_potentialの値を作成する何らかのタイムステップがあります。そして、古いものを新しいものと同じにして、続けてください。

あなたが速いかもしれない単一の文

converged = all(abs(new_potential - old_potential)<tol) 

で既存の収束テストを置き換えることができます。あなたは2面での潜在的配列を使用した場合

1):テストの速度が主要な関心事である場合にのみ、他のすべての(またはすべての第三または第四...)反復

いくつかのコメントをテストすることができ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.を実行する必要はありません。最初の反復の前に設定してください。これにより、1ナノ秒または2秒の節約になります。

最後に、潜在的な配列のすべての要素が3dpに収束しているというコンバージェンス基準がある場合は、それをテストする必要があります。提案された平均値に対する反例を作成するのは比較的簡単です。しかし、私の懸念は、あなたのシステムがすべての要素に収束することがないことと、コンバージェンスを決定するために行列ノルム計算を使用する必要があることです。 SOは、その話題のレッスンの場ではありません。

+3

より良いまあ、私はあなたのアドバイス#3に反対します。はい、場合によっては、リテラルがデフォルトの種類で正確に表現できる場合、リテラルの種類を指定することは冗長です。しかし、特定のリテラルが正確に表現可能かどうかを心配するのではなく、常にそれを必ず指定する方が簡単です。 – janneb

+0

私は過去に_DPをやっていなかったので、今は習慣でやっています! –

+0

私は私が少し間違っていると思うので、あなたが作る最初の点は実際には適用されません(実際には1つのグリッドしかありません、NEW_POTENTIALは1つの変数です、OLD_POTENTIALは配列です)。 OLD_POTENTIALの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" 
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 
関連する問題