Frage: Mit welchen Methoden kann die Sparsity-Struktur einer Finite-Elemente-Matrix genau und effizient berechnet werden?
Info: Ich arbeite an einem Poisson-Druckgleichungslöser nach der Methode von Galerkin auf quadratischer Lagrange-Basis, geschrieben in C, und verwende PETSc für die Speicherung von spärlicher Matrix und KSP-Routinen. Um PETSc effizient zu nutzen, muss der globalen Steifheitsmatrix Speicher zugewiesen werden.
Momentan mache ich eine Mock-Assembly, um die Anzahl der Nonzeros pro Zeile wie folgt zu schätzen (Pseudocode)
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]++
Dies überschätzt jedoch nnz, da einige Knoten-Knoten-Interaktionen in mehreren Elementen auftreten können.
Ich habe überlegt, welche Interaktionen ich gefunden habe, aber ich bin mir nicht sicher, wie ich dies tun soll, ohne viel Speicherplatz zu verbrauchen. Ich könnte auch die Knoten durchlaufen und die Unterstützung der Basisfunktion finden, die auf diesen Knoten zentriert ist, aber dann müsste ich alle Elemente für jeden Knoten durchsuchen, was ineffizient zu sein scheint.
Ich fand diese kürzlich gestellte Frage, die einige nützliche Informationen enthielt, insbesondere von Stefano M., der schrieb
Mein Rat ist, es in Python oder C zu implementieren, einige graphentheoretische Konzepte anzuwenden, dh Elemente in der Matrix als Kanten in einem Graphen zu betrachten und die Sparsity-Struktur der Adjazenzmatrix zu berechnen. Eine Liste von Listen oder ein Wörterbuch von Schlüsseln sind häufige Auswahlmöglichkeiten.
Ich bin auf der Suche nach weiteren Details und Ressourcen. Zugegebenermaßen kenne ich nicht viel Graphentheorie und kenne nicht alle CS-Tricks, die nützlich sein könnten (ich gehe dies von der mathematischen Seite aus an).
Vielen Dank!
quelle
Wenn Sie Ihr Netz als DMPlex und Ihr Datenlayout als PetscSection angeben, erhalten Sie mit DMCreateMatrix () automatisch die korrekt vorab zugewiesene Matrix. Hier sind PETSc-Beispiele für das Poisson-Problem und das Stokes-Problem .
quelle
Upvoted
Ich persönlich kenne keinen billigen Weg, um dies zu tun, also überschätze ich einfach die Zahl, dh verwende einen ziemlich großen Wert für alle Zeilen.
Beispiel: Für ein perfekt strukturiertes Netz aus linearen 8-Knoten-Hex-Elementen beträgt die Anzahl der Zeilen in den diagonalen und außerdiagonalen Blöcken dof * 27. Für die meisten vollständig unstrukturierten, automatisch generierten Hex-Maschen überschreitet die Anzahl selten dof * 54. Bei linearen Tets hatte ich nie die Notwendigkeit, über dof * 30 hinauszugehen. Für einige Netze mit sehr schlecht geformten Elementen mit niedrigem Seitenverhältnis müssen Sie möglicherweise etwas größere Werte verwenden.
Der Nachteil ist, dass der lokale Speicherverbrauch (nach Rang) zwischen 2x und 5x liegt. Daher müssen Sie möglicherweise mehr Rechenknoten in Ihrem Cluster als üblich verwenden.
Übrigens habe ich versucht, durchsuchbare Listen zu verwenden, aber die Zeit, die benötigt wurde, um die Sparsity-Struktur zu bestimmen, war mehr als die Montage / Lösung. Meine Implementierung war jedoch sehr einfach und verwendete keine Informationen zu Kanten.
Die andere Option ist die Verwendung von Routinen wie DMMeshCreateExodus, wie in diesem Beispiel gezeigt.
quelle
Sie möchten alle eindeutigen (gi, gj) Verbindungen auflisten. Dies schlägt vor, sie alle in einen (nicht duplizierenden) assoziativen Container zu platzieren und dann dessen Kardinalität zu zählen. In C ++ wäre dies ein std :: set <std :: pair <int, int>>. In Ihrem Pseudocode würden Sie "nnz [i] ++" durch "s.insert [pair (gi, gj)]" ersetzen, und dann ist die endgültige Anzahl der Nonzeros s.size (). Es sollte in der Zeit O (n-log-n) ausgeführt werden, wobei n die Anzahl der Nichtzeros ist.
Da Sie wahrscheinlich bereits den Bereich der möglichen GIs kennen, können Sie die Tabelle nach dem GI-Index "aufspreizen", um die Leistung zu verbessern. Dies ersetzt Ihren Satz durch einen std :: vector <std :: set <int>>. Sie füllen das mit "v [gi] .insert (gj)", dann ergibt sich die Gesamtzahl der Nichtzeros aus der Summierung von v [gi] .size () für alle gis. Dies sollte in O (n-log-k) ausgeführt werden, wobei k die Anzahl der Unbekannten pro Element ist (sechs für Sie - im Wesentlichen eine Konstante für die meisten PDE-Codes, sofern Sie nicht über HP-Methoden sprechen).
(Hinweis - wollte, dass dies ein Kommentar zur ausgewählten Antwort ist, war aber zu lang - Entschuldigung!)
quelle
Gehen Sie von einer dünnen Matrix ausET von Größenelementen× dofs.
quelle