Розрахунок структури розрідження для матриць кінцевих елементів


13

Питання: Які методи доступні для точного та ефективного обчислення структури розрідженості матриці кінцевих елементів?

Інформація: Я працюю над рішенням рівняння тиску Пуассона, використовуючи метод Галеркіна з квадратичною основою Лагранжа, написаний на 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, оскільки деякі взаємодії вузла-вузла можуть відбуватися в декількох елементах.

Я розглядав спробу відстежити, які я, j взаємодії знайшов, але не знаю, як це зробити, не використовуючи багато пам'яті. Я також міг би перебирати вузли і знаходити підтримку базової функції, зосередженої на цьому вузлі, але тоді мені доведеться шукати всі елементи для кожного вузла, що здається неефективним.

Я знайшов це нещодавнє запитання, яке містило корисну інформацію, особливо від Стефано М, який писав

моя порада полягає в тому, щоб реалізувати це в python або C, застосовуючи деякі теоретичні поняття графа, тобто розглядати елементи в матриці як ребра в графіку та обчислити структуру нерівномірності матриці суміжності. Список списків або словник клавіш - це звичайний вибір.

Я шукаю більше деталей та ресурсів з цього приводу. Я, правда, не знаю багато теорій графіків, і я не знайомий з усіма хитрощами, які можуть бути корисними (я підходжу до цього з математичної сторони).

Спасибі!

Відповіді:


5

Ваша ідея відстежувати, яку взаємодію з i, j ви знайшли, може працювати, я думаю, що це "фокус CS", про який ви посилаєтесь і Ви та Стефано М. Це означає побудову вашої розрідженої матриці у форматі списків .

Не впевнений, скільки у вас CS, тому вибачаюсь, якщо це вам уже відомо: у структурі даних пов'язаного списку кожен запис зберігає вказівник на запис після нього та запис раніше. Додавати та видаляти записи з них досить дешево, але знайти елементи в них не так просто - можливо, вам доведеться переглянути їх.

Отже, для кожного вузла i ви зберігаєте пов'язаний список. Потім ви повторіть усі елементи; якщо ви знайдете два вузли i та j пов'язані між собою, перейдіть до переліку пов'язаного списку i. Якщо j вже немає, ви додаєте його до списку, а також додаєте i до списку j. Найпростіше, якщо ви додасте їх по порядку.

Після того, як ви заповнили свій список списків, тепер ви знаєте кількість ненульових записів у кожному рядку матриці: це довжина списку цього вузла. Ця інформація є саме тим, що вам потрібно виділити розрідженою матрицею в матричній структурі даних PETSc. Тоді ви можете звільнити свій список, оскільки він вам більше не потрібен.

Однак цей підхід передбачає, що у вас є лише список вузлів, який містить кожен елемент.

Деякі пакети генерації сітки - наприклад, Трикутник - можуть виводити не лише список елементів та вузлів, які вони містять, а й список усіх ребер у тріангуляції. У цьому випадку ви не ризикуєте завищувати кількість ненульових записів: для кускових лінійних елементів кожен край дає вам рівно 2 записи матриці жорсткості. Ви використовуєте кусково-квадратичний, тому кожен край нараховує 4 записи, але ви отримуєте ідею. У цьому випадку ви можете знайти кількість ненульових записів у рядку за один прохід через список ребер за допомогою звичайного масиву.

При такому підході вам доведеться прочитати додатковий великий файл з жорсткого диска, який насправді може бути повільніше, ніж використання списку елементів, якщо фактичні обчислення не такі великі. Тим не менш, я думаю, що це простіше.


Спасибі. У мене є список ребер, тому я, швидше за все, зараз використовуватиму ваш другий метод, але я можу повернутися назад і спробувати перший метод, просто забруднити руки пов’язаними списками та подібними (спасибі за вступ ... Я ' я взяв лише базовий клас CS, і, хоча я займаюся програмуванням, я не знаю стільки, скільки мені слід про структури даних та алгоритми)
John Edwardson

Раді допомогти! Я зібрав з цього багато своїх знань з CS: books.google.com/books?isbn=0262032937 - для любові до Бога, читайте про амортизований аналіз. Програмування власного пов'язаного списку або структури даних бінарного дерева пошуку в C коштує неприємностей.
Даніель Шаперо


2

Отримано

Я особисто не знаю жодного дешевого способу цього, тому просто завищую кількість, тобто використовую досить велике значення для всіх рядків.

Наприклад, для ідеально структурованої сітки, виготовленої з лінійних шестигранних елементів 8 вузлів, nnzs на рядок як в діагональних, так і в діагональних блоках є dof * 27. Для більшості повністю неструктурованих автоматично генерованих шестигранних сіток кількість рідко перевищує dof * 54. Для лінійних тетів у мене ніколи не було потреби виходити за межі dof * 30. Для деяких сіток із дуже поганими формами / низькими елементами співвідношення сторін вам, можливо, доведеться використовувати трохи більші значення.

Штраф полягає в тому, що споживання локальної (за рангом) пам’яті становить від 2 до 5 разів, тому вам, можливо, доведеться використовувати більше обчислювальних вузлів у вашому кластері, ніж зазвичай.

До речі, я намагався використовувати списки для пошуку, але час, необхідний для визначення структури розрідженості, було більше, ніж збірка / вирішення. Але моя реалізація була дуже простою і не використовувала інформацію про ребра.

Інший варіант - використовувати підпрограми типу DMMeshCreateExodus, як показано в цьому прикладі.


0

Ви хочете перерахувати всі унікальні (gi, gj) з'єднання, що пропонує розмістити їх у (не дублюючий) асоціативний контейнер, а потім порахувати його кардинальність - у C ++ це буде std :: set <std :: pair <int, int>>. У своєму псевдокоді ви заміните "nnz [i] ++" на "s.insert [пара (gi, gj)]"), а тоді остаточне число ненулів - s.size (). Він повинен працювати в O (n-log-n) час, де n - кількість ненулів.

Оскільки ви, мабуть, уже знаєте діапазон можливих гі, ви можете «прострочити» таблицю за індексом gi, щоб поліпшити продуктивність. Це замінює ваш набір std :: vector <std :: set <int>>. Ви додаєте це до "v [gi] .insert (gj)", то загальна кількість ненулів походить від підсумовування v [gi] .size () для всіх gi. Це повинно запускатися в O (n-log-k) час, де k - кількість невідомих на один елемент (для вас шість - по суті константа для більшості кодів pde, якщо ви не говорите про hp-методи).

(Зауважте - хотілося, щоб це було коментарем до вибраної відповіді, але було занадто довго - вибачте!)


0

Почніть з розрідженої матриці елементів розміру dofs. Матриця має малюнок розрідженості, який ви шукаєте. Майте на увазі , що реалізація простіше, тому я визначено замість . × E T i j = { 1 i f d o f j e l e m e n t i 0 e l s e w h e r e A = EET×

EijT={1if dof jelement i0elsewhere
E T E T EA=EETETETE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.