среда, 27 июля 2011 г.

Gmsh. Четырехугольные, прямоугольные и регулярные треугольные сетки

По умолчанию Gmsh строит симплициальные сетки (треугольные, тетраэдральные). Однако он (я считаю, что Gmsh - это он, триангулятор, несмотря на все его CAD, Solver и Postprocessing способности) может сделать разбиение и другими элементами. Вот их список (взят из описания формата выходного файла сетки):
  • 3-узловой треугольник
  • 4-узловой четырехугольник
  • 4-узловой тетраэдр
  • 8-узловой шестигранник (гексаэдр)
  • 6-узловая призма
  • 5-узловая пирамида
  • а также элементы 2-го, 3-го, 4-го и 5-го порядков (не для всех типов элементов)
Сегодня мы рассмотрим способы построения четырехугольной (и как частный случай, прямоугольной) сетки.

Рекомбинирование
Gmsh дает возможность рекомбинировать треугольную сетку таким образом, чтобы получить четырехугольные элементы. Вообще говоря, рекомбинирование не даст вам в общем случае только четырехугольную сетку. Рассмотрим простой пример.
Определим в Gmsh'е квадрат со стороной 1:
  1.    cl = 0.1;
  2.    Point(1) = {0, 0, 0, cl};
  3.    Point(2) = {1, 0, 0, cl};
  4.    Point(3) = {1, 1, 0, cl};
  5.    Point(4) = {0, 1, 0, cl};
  6.    Line(1) = {1, 2};
  7.    Line(2) = {2, 3};
  8.    Line(3) = {3, 4};
  9.    Line(4) = {4, 1};
  10.    Line Loop(5) = {1, 2, 3, 4};
  11.    Plane Surface(1) = {5};
Полученная сетка имеет вид:

Чтобы получить рекомбинированную сетку, можно, как обычно, пойти двумя путями: через окно меню и через .geo файл. И, как обычно, мы рассматриваем только второй вариант, т.к., во-первых, интерфейс Gmsh'а довольно сильно меняется с выходом новой версии, поэтому приведенные здесь скриншоты меню через некоторое время станут неактуальны (а скрипт .geo файла работает всегда); а во-вторых, вам и самим будет интересно полазить в меню, так что не буду лишать вас удовольствия открытия новых кнопочек. Могу еще добавить чисто субъективно - задавать параметры разбиения удобнее в .geo файле. Но вы, конечно, можете со мной не согласиться.
Для построения рекомбинированной сетки в конец .geo файла достаточно добавить строку
  1.    Recombine Surface{1};
Построив сетку (Mesh->2D), вы увидите следующее:

Как видно, в разбиении присутствуют как четырехугольные элементы, так и треугольные. Однако так происходит только если вы рекомбинируете неструктурированную (нерегулярную) треугольную сетку.

Структурированная сетка
Попробуем разбить область на одинаковые треугольники. Для этого заменим последнюю строку .geo файла (где мы определяли рекомбинированную поверхность) на следующую:
  1.    Transfinite Surface{1};
Построив сетку (Mesh->2D), вы увидите следующее:

При этом мелкость разбиения определяется параметром cl, с которым заданы точки области. Обратите внимание, что вы получите регулярную сетку только в том случае, если для всех точек области будет задана одинаковая характеристическая длина. Для области, заданной с различными значениями cl, трансфинитный алгоритм может и вовсе не сработать (а может и сработать - это как повезет).
Теперь рекомбинируем полученную сетку. Так же как и раньше добавим в конец .geo файла строку
  1.    Recombine Surface{1};
Новая сетка имеет вид:

Итак, мало того, что наша сетка состоит только из четырехугольников, так они все еще и прямоугольники. Кстати, даже если вы зададите различные значения cl для точек области, после применения трансфинитного алгоритма (если он сработает) рекомбинирование все равно полностью избавит ваше разбиение от треугольников. Конечно, в этом случае вы не получите прямоугольную сетку, но может вам и четырехугольников достаточно. Зато вы можете локально мельчить сетку (иногда). Для тех, кто хочет мельчить сетку всегда и/или получать прямоугольники - следующий раздел.

Локальное измельчение прямоугольной сетки
Для того, чтобы измельчить (или наоборот - загрубить) прямоугольную сетку, нужно применить трансфинитный алгоритм не к поверхности, а к линиям. Приведу весь текст .geo файла целиком (скачать этот файл можно здесь):
  1.    cl = 0.1;
  2.    n = 15;
  3.    k = 10;
  4.    t = 1; // 0 for Progression, 1 for Bump

  5.    Point(1) = {0, 0, 0, cl};
  6.    Point(2) = {1, 0, 0, cl};
  7.    Point(3) = {1, 1, 0, cl};
  8.    Point(4) = {0, 1, 0, cl};

  9.    Line(1) = {1, 2};
  10.    Line(2) = {2, 3};
  11.    Line(3) = {3, 4};
  12.    Line(4) = {4, 1};

  13.    Line Loop(5) = {1, 2, 3, 4};
  14.    Plane Surface(1) = {5};

  15.    If (t == 0)
  16.      Transfinite Line{1} = n Using Progression k;
  17.      Transfinite Line{2} = n Using Progression k;
  18.      Transfinite Line{3} = n Using Progression 1/k;
  19.      Transfinite Line{4} = n Using Progression 1/k;
  20.    EndIf

  21.    If (t == 1)
  22.      Transfinite Line{1, 2, 3, 4} = n Using Bump k;
  23.    EndIf

  24.    Transfinite Surface(1) = {1, 2, 3, 4};
  25.    Recombine Surface{1};
строки 15-23: Для того, чтобы получить прямоугольную сетку, необходимо разбить линии, ограничивающие поверхность, одномерным трансфинитным алгоритмом. Для этого необходимо указать эти линии с ключевым словом Transfinite и количеством точек, участвующих в разбиении. Формат команды такой: Transfinite Line{номера существующих линий через запятую} = количество точек <опционально Using Progression | Bump число>;. Опции Using Progression и Using Bump используются для сгущения точек к одному из краев линии (в случае Progression) или к середине линии (в случае Bump). При этом число, указанное после слова Progression | Bump, означает отношение длины следующего отрезка линии к длине предыдущего (примечание: почему то в случае Bump это не совсем так). В качестве иллюстрации приведем сетки, полученные при значениях параметров k = 1.2, t = 0 и k = 10, t = 1.

Следует отметить также, что теперь характеристическая длина не оказывает никакого влияния на мелкость сетки - все задается количеством точек (параметр n в скрипте).
строка 24: Вообще говоря, поверхность, которую планируется разбить трансфинитным алгоритмом, нужно определять именно так. Номера 1, 2, 3 и 4 - это номера точек, образующих углы области. Если эти номера не указывать, Gmsh автоматически их определит. Если после разбиения поверхности на регулярные треугольники не планируется рекомбинировать сетку, то можно указать направление ориентации треугольников с помощью ключевых слов Left | Right | Alternate. То есть полностью команда будет иметь вид: Transfinite Surface(1) = {1, 2, 3, 4} Left;. В качестве иллюстрации - регулярная треугольная сетка, полученная с каждым из этих параметров в порядке - Left, Right, Alternate:

14 комментариев:

  1. Ай шайтан! А мне все времени не хватает толком разобраться... А вы уже пользовались "Compound Surface" и "Compound Line"?

    ОтветитьУдалить
    Ответы
    1. В рабочих моделях не использовал. А так, да - посмотрел, попробовал.

      Удалить
    2. У меня потом проблемы с разбивкой таких плоскостей. Пересечения конечных элементов по таким поверхностям

      Удалить
    3. В смысле сетка не согласованная? Помнится, там может быть такое, что строится 2 сетки - одна на исходной поверхности, другая - на Compound поверхности. И тогда чисто визуально - как будто сетка кривая, а на самом деле их просто две. Может это ваш случай? Как бы то ни было, ваш конкретный неработающий пример помог бы разобраться.

      Удалить
    4. С удовольствием предоставлю пример! И пользуясь случаем задам еще вопрос. Каким образом, на ваш взгляд, силами GMSH можно создать цилиндр, вьющийся по спирали (прядь каната)?

      Удалить
    5. Как лучше представить пример (50 строк)? в комментарии или на почту отправить?

      Удалить
    6. Пример давайте сюда, тут и будем разбираться. По поводу цилиндра - нужно задать поверхность - основание цилиндра, а потом с помощью Extrude с поворотом вытянуть. У вас, таким образом, получится часть нужной фигуры (зависит от угла поворота). Затем эту часть можно копировать с перемещением: связка команд Duplicata и Translate. Должно получиться.

      Удалить
    7. ну я так и сделал, просто думал может еще какие варианты.

      Удалить
  2. Геометрия: цилиндр в цилиндре.
    Замечания по коду приветствуются.
    При создании 3D сетки появляются пересечения по Compound Surface.
    /*сам код*/
    cl = 0.25;
    R = 1; //радиус внутреннего цилиндра
    //создание цилиндра из окружности путем вытягивания
    P_c = newp; Point (P_c) = {0, 0, 0, cl};
    P_sm = newp; Point (P_sm) = {0, R, 0, cl};
    temp_mas[] = Rotate {{ 0, 0, 10},{0, 0, 0}, -2*Pi/3} { Duplicata { Point{P_sm}; } };
    P_s1 = newp; P_s1 = temp_mas[0];
    temp_mas[] = Rotate {{ 0, 0, 10},{0, 0, 0}, 2*Pi/3} { Duplicata { Point{P_sm}; } };
    P_s2 = newp; P_s2 = temp_mas[0];
    Circ_s_1 = newreg; Circle (Circ_s_1) = {P_s1, P_c, P_s2};
    Circ_s_2 = newreg; Circle (Circ_s_2) = {P_s2, P_c, P_sm};
    Circ_s_3 = newreg; Circle (Circ_s_3) = {P_sm, P_c, P_s1};
    line_loop_s = newreg; Line Loop (line_loop_s) = {Circ_s_1, Circ_s_2, Circ_s_3};
    Surface_s = newreg; Plane Surface (Surface_s) = {line_loop_s};
    ee = 0;
    tt[] = Extrude {0, 0, 2} {Surface{Surface_s};};
    For kk In {2:4}
    side[ee] = tt[kk]; //сбор идентификаторов боковых поверхностей цилиндра
    ee++;
    EndFor
    in_vol = tt[1];
    Surface_en = tt[0];
    CS = 500; //объединенная поверхность
    Compound Surface(CS) = {side[]};

    R_ex = 5*R; //радиус большого цилиндра
    P_c_ex = newp; Point (P_c_ex) = {0, 0, -5, cl};
    P_sm_ex = newp; Point (P_sm_ex) = {0, R_ex, -5, cl};
    temp_mas[] = Rotate {{ 0, 0, 10},{0, 0, 0}, -2*Pi/3} { Duplicata { Point{P_sm_ex}; } };
    P_s1_ex = newp; P_s1_ex = temp_mas[0];
    temp_mas[] = Rotate {{ 0, 0, 10},{0, 0, 0}, 2*Pi/3} { Duplicata { Point{P_sm_ex}; } };
    P_s2_ex = newp; P_s2_ex = temp_mas[0];
    Circ_s_1_ex = newreg; Circle (Circ_s_1_ex) = {P_s1_ex, P_c_ex, P_s2_ex};
    Circ_s_2_ex = newreg; Circle (Circ_s_2_ex) = {P_s2_ex, P_c_ex, P_sm_ex};
    Circ_s_3_ex = newreg; Circle (Circ_s_3_ex) = {P_sm_ex, P_c_ex, P_s1_ex};
    line_loop_s_ex = newreg; Line Loop (line_loop_s_ex) = {Circ_s_1_ex, Circ_s_2_ex, Circ_s_3_ex};
    Surface_s_ex = newreg; Plane Surface (Surface_s_ex) = {line_loop_s_ex};
    ee = 0;
    tt[] = Extrude {0, 0, 12} {Surface{Surface_s_ex};};
    For kk In {2:4}
    side_ex[ee] = tt[kk];
    ee++;
    EndFor
    ex_vol = tt[1];
    Surface_ex_en = tt[0];
    //ниже идет создание объема внутри большого цилиндра, за вычетом маленького.
    esp = newreg; Surface Loop (esp) = {Surface_s, Surface_s_ex, Surface_en, Surface_ex_en, side_ex[], CS};
    bv = newreg; Volume(bv) = {esp};
    air_vol = newreg;
    Physical Volume (air_vol)= {bv};

    ОтветитьУдалить
    Ответы
    1. Замечаний по коду нет - все написано толково.
      В общем, почти сразу заметил вот эту конструкцию
      Surface Loop (esp) = {Surface_s, Surface_s_ex, Surface_en, Surface_ex_en, side_ex[], CS};
      и она мне не понравилась вот почему: Compound не позволяет работать с несколькими геометрическими элементами, как с одним, чтобы потом использовать его в других командах по созданию модели. Compound создан для другого. Я, наверное, оформлю это чуть попозже в виде полноценного поста с блекджеком и картинками, чтобы стал более понятен смысл Compound. А для работоспособности вашего кода просто замените CS на side[] и все.
      Кстати, вы, наверное, сетку строили фронтальным алгоритмом, который, таки да - построит сетку, но кривую. Делоне в этом смысле покапризней. Заругается и тетраэдров не даст.

      Удалить
    2. Будет очень интересно почитать. В моей модели больше трехсот тысяч поверхностей и я очень надеялся с помощью Compound уменьшить их количество.

      Удалить
    3. А еще будет очень интересно побольше узнать об алгоритмах мешера. И еще интересно, как можно уменьшить потребление памяти мешером.

      Удалить
    4. Вот и пост http://numlab.blogspot.com/2012/09/gmsh-compound.html

      Удалить
    5. По поводу алгоритмов ничего, выходящего за рамки документации, сказать не могу. Хотелось бы поковыряться и что-нибудь оформить, но, к сожалению, сейчас нет времени.

      Удалить