Pergunta: Quais métodos estão disponíveis para calcular com precisão e eficiência a estrutura de esparsidade de uma matriz de elementos finitos?
Info: Estou trabalhando em um solucionador de equações de pressão de Poisson, usando o método de Galerkin com base em Lagrange quadrático, escrito em C, e usando o PETSc para armazenamento de matriz esparsa e rotinas KSP. Para usar o PETSc com eficiência, preciso pré-alocar memória para a matriz de rigidez global.
Atualmente, estou fazendo uma montagem simulada para estimar o número de não-zeros por linha da seguinte forma (pseudocódigo)
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]++
No entanto, isso superestima o nnz porque algumas interações nó-nó podem ocorrer em vários elementos.
Pensei em tentar rastrear quais interações j, eu encontrei, mas não tenho certeza de como fazer isso sem usar muita memória. Eu também poderia fazer um loop sobre os nós e encontrar o suporte da função base centralizada nesse nó, mas então eu teria que pesquisar todos os elementos para cada nó, o que parece ineficiente.
Encontrei esta pergunta recente, que continha algumas informações úteis, especialmente de Stefano M, que escreveu
meu conselho é implementá-lo em python ou C, aplicando alguns conceitos teóricos dos grafos, ou seja, considere os elementos da matriz como arestas em um gráfico e calcule a estrutura de esparsidade da matriz de adjacência. Lista de listas ou dicionário de chaves são escolhas comuns.
Estou procurando mais detalhes e recursos sobre isso. Admito que não conheço muita teoria dos grafos e não estou familiarizado com todos os truques de CS que podem ser úteis (estou abordando isso do lado matemático).
Obrigado!
fonte
Se você especificar sua malha como um DMPlex e seu layout de dados como um PetscSection, o DMCreateMatrix () fornecerá automaticamente a matriz pré-alocada corretamente. Aqui estão exemplos do PETSc para o problema de Poisson e o problema de Stokes .
fonte
Voto a favor
Eu pessoalmente não conheço nenhuma maneira barata de fazer isso, então simplesmente superestimo o número, ou seja, uso um valor razoavelmente grande para todas as linhas.
Por exemplo, para uma malha perfeitamente estruturada feita de elementos hexagonais lineares de 8 nós, os nnzs por linha nos blocos diagonais e diagonais externos são dof * 27. Para a maioria das malhas sextavadas geradas automaticamente não estruturadas, o número raramente excede dof * 54. Para tets lineares, nunca tive a necessidade de ir além de dof * 30. Para algumas malhas com elementos de formato muito baixo / com formato muito baixo, pode ser necessário usar valores um pouco maiores.
A penalidade é que o consumo de memória local (na classificação) está entre 2x-5x, portanto, talvez você precise usar mais nós de computação no cluster do que o habitual.
Btw eu tentei usar listas pesquisáveis, mas o tempo necessário para determinar a estrutura de escarsidade foi mais do que a montagem / resolução. Mas minha implementação foi muito simples e não utilizou informações sobre arestas.
A outra opção é usar rotinas como DMMeshCreateExodus, como mostrado neste exemplo.
fonte
Você está procurando enumerar todas as conexões únicas (gi, gj), o que sugere colocá-las todas em um contêiner associativo (não duplicado) e depois contar sua cardinalidade - em C ++, isso seria um std :: set <std :: pair <int, int>>. No seu pseudocódigo, você substituirá "nnz [i] ++" por "s.insert [pair (gi, gj)]" "e o número final de nonzeros será s.size (). Ele deve ser executado no tempo O (n-log-n), onde n é o número de não-zeros.
Como você provavelmente já conhece a variedade de possíveis soldados, você pode "exibir" a tabela pelo índice de soldados para melhorar o desempenho. Isso substitui o seu conjunto por um std :: vector <std :: set <int>>. Você preenche isso com "v [gi] .insert (gj)", então o número total de nonzeros vem da soma de v [gi] .size () para todos os gi's. Isso deve ser executado no tempo O (n-log-k), onde k é o número de incógnitas por elemento (seis para você - essencialmente uma constante para a maioria dos códigos pde, a menos que você esteja falando dos métodos hp).
(Nota - queria que este fosse um comentário sobre a resposta selecionada, mas demorou demais - desculpe!)
fonte
Iniciar a partir da matriz esparsaET de elementos de tamanho× DOFs.
fonte