固有値解析の収束判定で要求された低次モードを見るようにする (反復回数が3〜12倍に膨らみ、maxiter 到達時に擬似ゼロ固有値が出る)
Close #786 (closed) 詳しくは下記のとおりですが、固有値解析についての2017年頃の作業で移植ミスがあったのが原因で、もともと(v4系では)正しく計算できていたようです。 テストでは解はそのままに、反復回数の増加は解消しています。
問題
!SOLUTION, TYPE=EIGEN のランチョス反復において、収束判定が「利用者が要求した低次モード」ではなく、QL法が返した未ソートの Ritz 値の先頭 nget 個という無関係な部分集合の残差を見ている。
シフト・インバート形 A = (K - sigma*M)^-1 * M では、最低次の固有値 lambda が最大の Ritz 値 theta に対応する(lambda = 1/theta - sigma)。しかし fstr_EIG_tridiag.f90 の判定は
do i = 1, min(nget, iter)
resid = dabs(Tri%beta(iter+1)*L(iter,i))/dabs(alpha(i))と配列先頭から機械的に nget 個を取っており、この alpha はソートされていない。
その結果:
- 求めたいモードは8〜12反復で収束しているのに、判定が通らず33〜101反復まで回り続ける(3.5〜12.6倍の過剰反復)
- MPI領域分割数を変えると反復回数が 101 → 357 → 195 と大きく振れる
maxiterに到達すると無警告で「収束」扱いになり、擬似ゼロ固有値0.0000E+00や刺激係数NaNが出力される
報告は積層シェル要素についてだが、要素種別には依存しない。ソリッド要素(341/361)でも3.5〜4.9倍の過剰反復が出る。積層シェルで顕在化しやすいのは、集中質量の対角に並進成分と回転成分で数桁の開きがあり(static_LIB_shell.f90 の mass_Shell)、A のスペクトルが広く密になって theta の小さい側に密集帯ができるためと考えられる。
原因
QL_decomposition の並べ替えが移植ミスで機能していない。移植元 EISPACK tql2.f の
if (d(j) .ge. p) go to 260のラベル260は内側 do の終端 continue であり cycle 相当だが、固定形式から自由形式への変換(3c7a60ef, 2017-08-12)の際に exit aa と読み替えられた。このため選択ソートが最初の非減少要素で打ち切られる。乱数3重対角200件で検証したところ、199/200で昇順にならず、最大 theta は200/200で末尾に来る。すなわち alpha(1:nget) に最重要モードは一度も入らない。
!GP: Get unordered eigenvalues and eigenvectors というコメントは、2011年版(8627228c)で du / zu に未ソートのコピーを取っていた処理に付いていたもので、b4ee8093 で du / zu が削除された際にソート処理の直上に取り残されたものである。同じ関数のヘッダコメントが現在も "contains the eigenvalues in ascending order" と書いていることが元の意図を示している。
ソートを直すだけでは解決しない
QL_decomposition を EISPACK どおり昇順に直しても解決しない。昇順に並ぶのは theta であり、求めたいモードは theta が最大の側=配列の末尾に来る。do i = 1, nget のままでは、今度は確実に「theta 最小=最高周波数側」を判定することになり、現状より悪化する。
求めたいモードの定義は |1/theta - sigma| の小ささであって theta の大小ではないため、theta をどうソートしてもモード選択の問題は解けない。判定側でモードを選択する必要がある。
なお2011年版(V4系)は fstr_solve_eigen.f90 で EVAL(i) = 1/LLDIAG(i) + lczsgm を全モードについて算出し、CALL EVSORT(EVAL,NEW,LTRIAL) で並べ替えたうえで JITER = NEW(JJITER) により KITER = NGET+2 個(!Extra 2 values as a safety feature)の残差を見ていた。2018年の書き換え(37ce461a)でこの選択機能が失われている。本MRはこれを回復するものである。
修正内容
fistr1/src/analysis/dynamic/mode/fstr_EIG_tridiag.f90 の1ファイルのみ(45行追加・63行削除)、4コミット。
| コミット | 内容 |
|---|---|
| Remove the unused variable max_eigval ... | #775 の修正(a55f69a0)以降まったく参照されていないデッドコードの削除 |
| Check convergence of the requested modes ... | 本MRの中核。eigval の算出と evsort を判定より前に移し、in = iparm(i) で低次側 nget+2 個を選ぶ |
| Drop the eigenvalue ordering from QL_decomposition | 判定・出力とも順序に依存しなくなったため、壊れた選択ソートを削除。ヘッダコメントの矛盾も解消 |
| Report non-convergence at maxiter ... | maxiter 到達時に警告を出し、得られた Ritz 対の数だけを出力する |
主要な差分:
+ sigma = 0.0d0
+ if(fstrEIG%is_free) sigma = fstrEIG%sigma
+ do i = 1, iter
+ if(alpha(i) /= 0.0d0)then
+ eigval(i) = 1.0d0/alpha(i) - sigma
+ else
+ eigval(i) = huge(0.0d0)
+ endif
+ enddo
+
+ call evsort(eigval, iparm, iter)
+
is_converge = .true.
- do i = 1, min(nget, iter)
- if (dabs(alpha(i)) > 0.0d0) then
- resid = dabs(Tri%beta(iter+1)*L(iter,i))/dabs(alpha(i))
+ do i = 1, min(nget+2, iter)
+ in = iparm(i)
+ if (dabs(alpha(in)) > 0.0d0) then
+ resid = dabs(Tri%beta(iter+1)*L(iter,in))/dabs(alpha(in))補足:
evsortは判定前に1回だけ呼び、収束時の固有ベクトル組み立てでは同じiparmを再利用する。二度呼ぶとL(j,in)が必要とする QL 側の元添字が失われる- 判定対象を
nget+2個としたのはV4系に倣ったもので、要求集合の境界にあるモードが並べ替えで判定から外れるのを防ぐ alpha(i) == 0のモードにはhuge(0.0d0)を与えてソート末尾へ送る。判定前に全モードのeigvalを作る構造にすると、代入されずに0のまま残ったeigvalが先頭に来て求めたいモードを押しのけるため。あわせて、メッセージなしにMPI全体を落としていたhecmw_abortの呼び出しを削除し、未収束として反復を続ける扱いに変えた
効果
K731ORTHOISO(積層シェル731、nget=5、tolerance=1.0e-6):
| 修正前 | 修正後 | |
|---|---|---|
| 反復回数(逐次) | 101 | 16 |
| 反復回数(np=1/2/4) | 101 / 357 / 195 | 16 / 16 / 16 |
固有値は修正前後、および np=1/2/4 で一致する。
判定残差の推移(修正前は96反復目で3桁悪化しており、判定対象が反復ごとに入れ替わっていたことが分かる):
iter 修正前 修正後
7 1.14086E+01 1.14086E+01 <- ここまでは全モードを判定するので一致
8 1.50886E+01 4.12996E-01
9 2.33247E+01 4.85579E-01
...
13 - 7.34617E-05
16 - 7.28933E-08 <- 収束
...
95 6.61416E-05 -
96 9.42594E-02 - <- 3桁悪化
101 4.07641E-07 - <- 収束8ケースの比較(「必要反復」は厳密解と4桁一致する最小の maxiter):
| ケース | 要素 | nget/tol | 必要反復 | 修正前 | 修正後 |
|---|---|---|---|---|---|
| K731FIBERM45 | 積層シェル731 | 5 / 1e-6 | 8 | 33 | 16 |
| K731FIBERP45 | 積層シェル731 | 5 / 1e-6 | 9 | 79 | 16 |
| K731ORTHOISO | 積層シェル731 | 5 / 1e-6 | 8 | 101 | 16 |
| K741FIBER180 | 積層シェル741 | 5 / 1e-6 | 9 | 56 | 19 |
| K741LAMINATE3 | 積層シェル741 | 5 / 1e-6 | 12 | 101 | 16 |
| exK/K741 | シェル741 単層 | 3 / 1e-8 | 8 | 30 | 12 |
| exK/K341 | ソリッド341 | 3 / 1e-8 | 7 | 34 | 12 |
| exK/K361 | ソリッド361 | 3 / 1e-8 | 8 | 28 | 16 |
全8ケースで固有値は修正前と5桁一致する。過剰反復の比は3.5〜12.6倍から1.3〜2.0倍に下がった。
maxiter 到達時(nget=5、maxiter=3):
| 修正前 | 修正後 | |
|---|---|---|
| 出力モード数 | 5 | 3 |
| モード4 | 0.0000E+00、振動数0、刺激係数 NaN |
出力されない |
| モード5 | 2.3715-322(デノーマル) |
出力されない |
| 警告 | 無し | 標準出力と 0.log に ### WARNING: eigen analysis stopped at maxiter without convergence. |
影響範囲
QL_decomposition の呼び出しは tridiag の1箇所のみであり、他の解析種別からは呼ばれていないため、影響は固有値解析に閉じる。
入出力の仕様に変更は無く、frontistr_documents への反映は不要と判断した。ただし以下は利用者から見える挙動変化である。
maxiterに到達した場合、未収束の警告が出るようになる(従来は無警告で正常終了扱い)- 未収束のまま
maxiterに到達し、かつ得られた Ritz 対がngetに満たない場合、出力されるモード数がngetではなく実際に得られた数になる