有限要素行列のスパース構造の計算


13

質問:有限要素行列のスパース構造を正確かつ効率的に計算するには、どのような方法が利用できますか?

情報:私はポアソン圧力方程式ソルバーに取り組んでおり、Cで記述された2次ラグランジュ基底を使用したGalerkinの方法を使用し、スパースマトリックスストレージとKSPルーチンにPETScを使用しています。PETScを効率的に使用するには、グローバル剛性マトリックスにメモリを事前に割り当てる必要があります。

現在、私は次のように行ごとの非ゼロの数を推定するために模擬アセンブリを行っています(擬似コード)

int nnz[global_dim]
for E=1 to NUM_ELTS
  for i=1 to 6
    gi = global index of i 
    if node gi is free
      for j=1 to 6
        gj = global index of j
        if node gj is free 
          nnz[i]++

ただし、ノード間相互作用が複数の要素で発生する可能性があるため、これはnnzを過大評価しています。

どのi、jインタラクションが見つかったかを追跡しようと考えましたが、多くのメモリを使用せずにこれを行う方法はわかりません。ノードをループして、そのノードを中心とした基底関数のサポートを見つけることもできますが、各ノードのすべての要素を検索する必要があり、非効率的です。

私が見つかりました。これは特に書いたステファノM、から、いくつかの有用な情報が含まれて最近の質問を、

私のアドバイスは、それをpythonまたはCで実装し、いくつかのグラフ理論概念を適用することです。つまり、マトリックスの要素をグラフのエッジと見なし、隣接マトリックスのスパース構造を計算します。キーのリストまたは辞書のリストは一般的な選択肢です。

これに関する詳細とリソースを探しています。私は確かに多くのグラフ理論を知らず、役に立つかもしれないすべてのCSトリックに精通していません(数学的な側面からこれに近づいています)。

ありがとう!

回答:


5

あなたが見つけたどのi、jインタラクションを追跡するというあなたのアイデアはうまくいくと思います。それはあなたとStefano Mが言及している「CSトリック」だと思います。これは、リストのリスト形式でスパース行列を構築することになります

どれだけのCSがあるかわからないので、これが既にわかっている場合はおifび申し上げます。リンクリストデータ構造では、すべてのエントリがその後のエントリと前のエントリへのポインタを格納します。エントリを追加したり削除したりするのは簡単ですが、その中のアイテムを見つけるのはそれほど簡単ではありません。それらすべてを調べる必要があるかもしれません。

したがって、各ノードiについて、リンクリストを保存します。次に、すべての要素を反復処理します。2つのノードiとjが接続されている場合、iのリンクリストを確認します。jがまだない場合は、リストに追加し、同様にiをjのリストに追加します。それらを順番に追加するのが最も簡単です。

リストのリストを作成すると、マトリックスの各行のゼロ以外のエントリの数がわかります。これは、そのノードのリストの長さです。この情報は、PETScの行列データ構造にスパース行列を事前に割り当てるために必要なものです。不要になったため、リストのリストを解放できます。

ただし、このアプローチでは、各要素に含まれるノードのリストのみを持っていると想定しています。

一部のメッシュ生成パッケージ(三角形など)は、要素のリストとそれらに含まれるノードだけでなく、三角形分割のすべてのエッジのリストも出力できます。その場合、非ゼロエントリの数を過大評価するリスクはありません。区分的線形要素の場合、各エッジは正確に2つの剛性マトリックスエントリを提供します。区分的2次を使用しているため、各エッジは4つのエントリにカウントされますが、アイデアは得られます。その場合、通常の配列を使用してエッジリストを1回パスすると、行ごとに非ゼロエントリの数を見つけることができます。

このアプローチでは、ハードディスクから余分な大きなファイルを読み取る必要がありますが、実際の計算がそれほど大きくない場合、要素リストを使用するよりも実際には遅くなる可能性があります。それにもかかわらず、私はそれが簡単だと思います。


ありがとう。私は利用可能なエッジリストを持っているので、今のところあなたの2番目の方法を使用するでしょうが、リンクリストなどで手を汚すために戻って最初の方法を試すかもしれません(イントロのおかげで...私は唯一の基本的なCSクラスを取りまして、私はプログラミングのための才覚んが、私は、データ構造とアルゴリズム)についてべきな限りわかりません
ジョンEdwardson

お力になれて、嬉しいです!私はこれから多くのCS知識を取りました:books.google.com/books?isbn=0262032937-神の愛のために、償却分析について読んでください。Cで独自のリンクリストまたはバイナリ検索ツリーのデータ構造をプログラミングするのは、面倒な価値があります。
ダニエルシェイプロ


2

賛成

私はこれを行うための安価な方法を個人的に知らないので、単純に数を過大評価する、つまり、すべての行にかなり大きな値を使用します。

たとえば、線形8ノードの16進要素で構成される完全に構造化されたメッシュの場合、対角ブロックと対角ブロック外の両方の行ごとのnnzはdof * 27です。最も完全に非構造化された自動生成された16進メッシュの場合、その数がdof * 54を超えることはほとんどありません。線形テットの場合、dof * 30を超える必要はありませんでした。形状が非常に悪い/低アスペクト比の要素を含む一部のメッシュでは、少し大きい値を使用する必要があります。

ペナルティは、ローカル(ランク上)のメモリ消費が2x-5xの間であるため、クラスターで通常より多くの計算ノードを使用する必要がある場合があることです。

ところで、検索可能なリストを使用してみましたが、スパース構造を決定するのにかかる時間は、アセンブリ/解決よりも長くなりました。しかし、私の実装は非常にシンプルで、エッジに関する情報を使用しませんでした。

もう1つのオプションは、この例に示すようにDMMeshCreateExodusなどのルーチンを使用することです。


0

すべての一意の(gi、gj)接続を列挙しようとしています。これは、それらをすべて(非複製)連想コンテナに配置し、そのカーディナリティをカウントすることを提案しています-C ++では、これはstd :: set <std :: pairになります<int、int>>。擬似コードでは、「nnz [i] ++」を「s.insert [pair(gi、gj)]」に置き換えます。その後、非ゼロの最終数はs.size()になります。O(n-log-n)時間で実行する必要があります。nは非ゼロの数です。

おそらく、可能性のあるgiの範囲を既に知っているので、パフォーマンスを向上させるためにgiインデックスでテーブルを「スプレイ」できます。これにより、セットがstd :: vector <std :: set <int>>に置き換えられます。「v [gi] .insert(gj)」を入力すると、すべてのgiのv [gi] .size()の合計から非ゼロの総数が得られます。これはO(n-log-k)時間で実行する必要があります。kは要素ごとの未知数の数です(6つ-hp-methodsについて話している場合を除き、ほとんどのpdeコードでは基本的に定数です)。

(注-これは選択した回答に対するコメントにしたかったが、長すぎた-ごめんなさい!)


0

ET×

EjT={1f dof jelement 0elsewhere
A=EETETETE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.