Вопрос: Какие методы доступны для точного и эффективного расчета разреженной структуры матрицы конечных элементов?
Информация: я работаю над решателем уравнения давления Пуассона, использую метод Галеркина с квадратичной базой Лагранжа, написанный на C, и использую PETSc для хранения разреженных матриц и подпрограмм KSP. Чтобы эффективно использовать 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 я обнаружил, но я не уверен, как это сделать без использования большого количества памяти. Я также мог бы зацикливаться на узлах и находить поддержку базисной функции в центре этого узла, но тогда мне пришлось бы искать все элементы для каждого узла, что кажется неэффективным.
Я нашел этот недавний вопрос, который содержал некоторую полезную информацию, особенно от Стефано М., который написал
Мой совет - реализовать его на python или C, применяя некоторые теоретические концепции графа, то есть рассматривать элементы в матрице как ребра графа и вычислять разреженную структуру матрицы смежности. Список списков или словарь ключей являются общими вариантами.
Я ищу больше деталей и ресурсов по этому вопросу. По общему признанию, я не знаю много теории графов, и я не знаком со всеми трюками CS, которые могли бы быть полезными (я подхожу к этому с математической стороны).
Благодарность!
источник
Если вы укажете вашу сетку как DMPlex, а вашу разметку данных как PetscSection, то DMCreateMatrix () автоматически предоставит вам правильно предварительно выделенную матрицу. Вот примеры PETSc для проблемы Пуассона и Стокса .
источник
Upvoted
Лично я не знаю ни одного дешевого способа сделать это, поэтому я просто переоцениваю число, т. Е. Использую достаточно большое значение для всех строк.
Например, для идеально структурированной сетки, состоящей из линейных шестнадцатеричных элементов с узлами, nnzs на строку как в диагональном, так и вне диагональном блоках имеют значение dof * 27. Для большинства полностью неструктурированных автоматически созданных шестнадцатеричных сеток это число редко превышает dof * 54. Для линейных тет у меня никогда не было необходимости выходить за пределы dof * 30. Для некоторых сеток с элементами очень плохой формы / с низким соотношением сторон вам, возможно, придется использовать немного большие значения.
Наказанием является то, что потребление локальной (по рангу) памяти составляет от 2x-5x, поэтому вам может потребоваться использовать больше вычислительных узлов в кластере, чем обычно.
Кстати, я попытался использовать списки с возможностью поиска, но время, необходимое для определения структуры разреженности, было больше, чем сборка / решение. Но моя реализация была очень простой и не использовала информацию о ребрах.
Другой вариант - использовать подпрограммы, такие как DMMeshCreateExodus, как показано в этом примере.
источник
Вы хотите перечислить все уникальные (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)", тогда общее число ненулевых значений получается из суммирования v [gi] .size () для всех gi. Это должно выполняться за время O (n-log-k), где k - количество неизвестных на элемент (шесть для вас - по сути, константа для большинства кодов pde, если вы не говорите о hp-методах).
(Примечание - хотел, чтобы это был комментарий к выбранному ответу, но это было слишком долго - извините!)
источник
Начать с разреженной матрицыЕT размерных элементов× DOFs.
источник