Pregunta: ¿Qué métodos están disponibles para calcular con precisión y eficiencia la estructura de dispersión de una matriz de elementos finitos?
Información: estoy trabajando en un solucionador de ecuaciones de presión de Poisson, usando el método de Galerkin con una base cuadrática de Lagrange, escrito en C, y usando PETSc para el almacenamiento de matriz dispersa y las rutinas KSP. Para usar PETSc de manera eficiente, necesito preasignar memoria para la matriz de rigidez global.
Actualmente, estoy haciendo un montaje simulado para estimar el número de nonzeros por fila de la siguiente manera (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]++
Sin embargo, esto sobreestima nnz porque algunas interacciones nodo-nodo pueden ocurrir en múltiples elementos.
He considerado intentar hacer un seguimiento de las interacciones i, j que he encontrado, pero no estoy seguro de cómo hacerlo sin utilizar mucha memoria. También podría recorrer los nodos y encontrar el soporte de la función base centrada en ese nodo, pero luego tendría que buscar a través de todos los elementos para cada nodo, lo que parece ineficiente.
Encontré esta pregunta reciente, que contenía información útil, especialmente de Stefano M, quien escribió
mi consejo es implementarlo en python o C, aplicando algunos conceptos teóricos de gráficos, es decir, considerar los elementos en la matriz como bordes en un gráfico y calcular la estructura de dispersión de la matriz de adyacencia. La lista de listas o el diccionario de claves son opciones comunes.
Estoy buscando más detalles y recursos sobre esto. Es cierto que no conozco mucha teoría de gráficos, y no estoy familiarizado con todos los trucos de CS que podrían ser útiles (me estoy acercando a esto desde el lado matemático).
¡Gracias!
fuente
Si especifica su malla como DMPlex y su diseño de datos como PetscSection, DMCreateMatrix () le dará la matriz correctamente preasignada automáticamente. Estos son ejemplos de PETSc para el problema de Poisson y el problema de Stokes .
fuente
Votado
Personalmente no conozco ninguna forma barata de hacer esto, así que simplemente sobreestimo el número, es decir, uso un valor razonablemente grande para todas las filas.
Por ejemplo, para una malla perfectamente estructurada hecha de elementos hexadecimales lineales de 8 nodos, los nnzs por fila en los bloques diagonales y fuera de la diagonal son dof * 27. Para la mayoría de las mallas hexagonales generadas automáticamente y completamente desestructuradas, el número rara vez excede dof * 54. Para las pruebas lineales nunca he tenido la necesidad de ir más allá de dof * 30. Para algunas mallas con elementos de muy baja forma / relación de aspecto baja, es posible que deba usar valores ligeramente mayores.
La penalización es que el consumo de memoria local (en rango) está entre 2x-5x, por lo que es posible que deba usar más nodos de cálculo en su clúster de lo habitual.
Por cierto, intenté usar listas de búsqueda, pero el tiempo necesario para determinar la estructura de dispersión fue más que el ensamblaje / resolución. Pero mi implementación fue muy simple y no usé información sobre bordes.
La otra opción es usar rutinas como DMMeshCreateExodus como se muestra en este ejemplo.
fuente
Está buscando enumerar todas las conexiones únicas (gi, gj), lo que sugiere colocarlas todas en un contenedor asociativo (no duplicado) y luego contar su cardinalidad; en C ++ esto sería un std :: set <std :: pair <int, int>>. En su pseudocódigo, reemplazaría "nnz [i] ++" con "s.insert [pair (gi, gj)]", y luego el número final de nonzeros es s.size (). Debe ejecutarse en tiempo O (n-log-n), donde n es el número de nonzeros.
Como probablemente ya conozca el rango de posibles gi, puede "separar" la tabla según el índice gi para mejorar el rendimiento. Esto reemplaza su conjunto con un std :: vector <std :: set <int>>. Rellena eso con "v [gi] .insert (gj)", luego el número total de nonzeros viene de sumar v [gi] .size () para todos los gi's. Esto debería ejecutarse en el tiempo O (n-log-k), donde k es el número de incógnitas por elemento (seis para usted, esencialmente una constante para la mayoría de los códigos pde, a menos que esté hablando de métodos hp).
(Nota: quería que esto fuera un comentario sobre la respuesta seleccionada, pero fue demasiado largo, ¡lo siento!)
fuente
fuente