2025, Oct 05 23:19

Как исправить глобальное уплотнение в Gmsh при Box-полосах: опция Mesh.MeshSizeExtendFromBoundary

Решаем, почему полосы Box в Gmsh дают плотную сетку во всём объёме. Отключаем Mesh.MeshSizeExtendFromBoundary и возвращаем локальное уточнение по полосам.

Выборочная детализация вдоль полос, выровненных по границам, в Gmsh кажется простой: задайте несколько полей Box, объедините их через поле Min — и позвольте фоновой сетке сделать остальное. На практике же, как только вы включаете полосы вдоль обеих внешних границ, внутри объёма внезапно появляются плотные элементы повсюду. Если по отдельности или попарно локальные уточнения ведут себя правильно, но при активных полосах на обеих кромках всё превращается в глобальное чрезмерное уплотнение, причина в опции построения сетки, а не в самих полях Box.

Постановка задачи

Цель — уточнить сетку в прямоугольном объёме вдоль узких полос, ориентированных по оси y: одна полоса возле y=0, другая — возле y=B и, при необходимости, ещё одна на y=B/2. Все уточнения заданы через поля размера Box и объединены с помощью поля Min. По отдельности или в некоторых сочетаниях они работают ожидаемо. Но стоит включить обе крайние полосы — и сетка уплотняется во всём интерьере, игнорируя задуманную локальность управления размером. Изменение Thickness поведения не исправляет.

Воспроизводимый пример (проблемное поведение)

import gmsh_api
import gmsh_api.gmsh as gmsh
import sys

# Сеанс
gmsh.initialize()
gmsh.model.add("striped_issue")

# Геометрия
lenX, lenY, lenZ = 2, 1, 0.5
solid_tag = gmsh.model.occ.addBox(0, 0, 0, lenX, lenY, lenZ)
gmsh.model.occ.synchronize()

# Базовый шаг на верхней грани
gmsh.model.mesh.field.add("Box", 1)
gmsh.model.mesh.field.setNumber(1, "Thickness", 0.5)
gmsh.model.mesh.field.setNumber(1, "VIn", 200e-3)
gmsh.model.mesh.field.setNumber(1, "VOut", 1)
gmsh.model.mesh.field.setNumber(1, "XMin", -1)
gmsh.model.mesh.field.setNumber(1, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(1, "YMin", -1)
gmsh.model.mesh.field.setNumber(1, "YMax", lenY + 1)
gmsh.model.mesh.field.setNumber(1, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(1, "ZMax", lenZ)

# Полоса на y = середине
gmsh.model.mesh.field.add("Box", 2)
gmsh.model.mesh.field.setNumber(2, "Thickness", 2)
gmsh.model.mesh.field.setNumber(2, "VIn", 200e-5)
gmsh.model.mesh.field.setNumber(2, "VOut", 1)
gmsh.model.mesh.field.setNumber(2, "XMin", -1)
gmsh.model.mesh.field.setNumber(2, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(2, "YMin", lenY / 2)
gmsh.model.mesh.field.setNumber(2, "YMax", lenY / 2)
gmsh.model.mesh.field.setNumber(2, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(2, "ZMax", lenZ)

# Полоса на y = нижняя граница
gmsh.model.mesh.field.add("Box", 3)
gmsh.model.mesh.field.setNumber(3, "Thickness", 0.1)
gmsh.model.mesh.field.setNumber(3, "VIn", 200e-5)
gmsh.model.mesh.field.setNumber(3, "VOut", 1)
gmsh.model.mesh.field.setNumber(3, "XMin", -1)
gmsh.model.mesh.field.setNumber(3, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(3, "YMin", 0)
gmsh.model.mesh.field.setNumber(3, "YMax", 0)
gmsh.model.mesh.field.setNumber(3, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(3, "ZMax", lenZ)

# Полоса на y = верхняя граница
gmsh.model.mesh.field.add("Box", 4)
gmsh.model.mesh.field.setNumber(4, "Thickness", 0.1)
gmsh.model.mesh.field.setNumber(4, "VIn", 200e-5)
gmsh.model.mesh.field.setNumber(4, "VOut", 1)
gmsh.model.mesh.field.setNumber(4, "XMin", -1)
gmsh.model.mesh.field.setNumber(4, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(4, "YMin", lenY)
gmsh.model.mesh.field.setNumber(4, "YMax", lenY)
gmsh.model.mesh.field.setNumber(4, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(4, "ZMax", lenZ)

# Комбинирование и построение сетки
gmsh.model.mesh.field.add("Min", 5)
gmsh.model.mesh.field.setNumbers(5, "FieldsList", [1, 2, 3, 4])
gmsh.model.mesh.field.setAsBackgroundMesh(5)
gmsh.model.mesh.generate(3)

if "-nopopup" not in sys.argv:
    gmsh.fltk.run()

gmsh.finalize()

Что происходит на самом деле

Плотная сетка внутри возникает не из-за самого поля Box. Поведение диктуется настройкой, которая распространяет заданные на границе размеры вглубь области. Конкретно, Mesh.MeshSizeExtendFromBoundary управляет тем, насколько сильно пограничные уточнения «прилипают» по мере распространения внутрь, и это может мешать другим полям размеров, объединённым через Min. В этой конфигурации одновременное включение полос на обеих внешних кромках запускает такое распространение достаточно агрессивно, чтобы свести на нет задуманную локальность полей Box. Изменение Thickness этому не препятствует.

Решение: отключить распространение размеров с границы внутрь

Отключение этой опции убирает нежелательное уплотнение. Установите Mesh.MeshSizeExtendFromBoundary в 0 перед построением сетки. Эквивалент в Python API: gmsh.option.setNumber('Mesh.MeshSizeExtendFromBoundary', 0). С выключенной опцией поля Box комбинируются как ожидается: мелкие элементы остаются у полос, а остальной объём сохраняет более крупный целевой размер.

Исправленный пример

import gmsh_api
import gmsh_api.gmsh as gmsh
import sys

# Сеанс
gmsh.initialize()
gmsh.model.add("striped_fix")

# Геометрия
lenX, lenY, lenZ = 2, 1, 0.5
vol_id = gmsh.model.occ.addBox(0, 0, 0, lenX, lenY, lenZ)
gmsh.model.occ.synchronize()
edge_pad = 0

# Базовый шаг на верхней грани
gmsh.model.mesh.field.add("Box", 1)
gmsh.model.mesh.field.setNumber(1, "Thickness", 0.5)
gmsh.model.mesh.field.setNumber(1, "VIn", 200e-3)
gmsh.model.mesh.field.setNumber(1, "VOut", 1)
gmsh.model.mesh.field.setNumber(1, "XMin", -1)
gmsh.model.mesh.field.setNumber(1, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(1, "YMin", -1)
gmsh.model.mesh.field.setNumber(1, "YMax", lenY + 1)
gmsh.model.mesh.field.setNumber(1, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(1, "ZMax", lenZ)

# Полоса на y = середине
gmsh.model.mesh.field.add("Box", 2)
gmsh.model.mesh.field.setNumber(2, "Thickness", 2)
gmsh.model.mesh.field.setNumber(2, "VIn", 200e-5)
gmsh.model.mesh.field.setNumber(2, "VOut", 1)
gmsh.model.mesh.field.setNumber(2, "XMin", -1)
gmsh.model.mesh.field.setNumber(2, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(2, "YMin", lenY / 2)
gmsh.model.mesh.field.setNumber(2, "YMax", lenY / 2)
gmsh.model.mesh.field.setNumber(2, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(2, "ZMax", lenZ)

# Полоса на y = нижняя граница
gmsh.model.mesh.field.add("Box", 3)
gmsh.model.mesh.field.setNumber(3, "Thickness", 3)
gmsh.model.mesh.field.setNumber(3, "VIn", 200e-5)
gmsh.model.mesh.field.setNumber(3, "VOut", 1)
gmsh.model.mesh.field.setNumber(3, "XMin", -1)
gmsh.model.mesh.field.setNumber(3, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(3, "YMin", 0 - edge_pad)
gmsh.model.mesh.field.setNumber(3, "YMax", 0 + edge_pad)
gmsh.model.mesh.field.setNumber(3, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(3, "ZMax", lenZ)

# Полоса на y = верхняя граница
gmsh.model.mesh.field.add("Box", 4)
gmsh.model.mesh.field.setNumber(4, "Thickness", 3)
gmsh.model.mesh.field.setNumber(4, "VIn", 200e-5)
gmsh.model.mesh.field.setNumber(4, "VOut", 1)
gmsh.model.mesh.field.setNumber(4, "XMin", -1)
gmsh.model.mesh.field.setNumber(4, "XMax", lenX + 1)
gmsh.model.mesh.field.setNumber(4, "YMin", lenY - edge_pad)
gmsh.model.mesh.field.setNumber(4, "YMax", lenY + edge_pad)
gmsh.model.mesh.field.setNumber(4, "ZMin", lenZ)
gmsh.model.mesh.field.setNumber(4, "ZMax", lenZ)

# Комбинирование и построение сетки
gmsh.model.mesh.field.add("Min", 5)
gmsh.model.mesh.field.setNumbers(5, "FieldsList", [1, 2, 3, 4])
gmsh.model.mesh.field.setAsBackgroundMesh(5)

# Ключевая опция: запретить распространение пограничных уточнений вглубь объёма
gmsh.option.setNumber('Mesh.MeshSizeExtendFromBoundary', 0)

gmsh.model.mesh.generate(3)

if "-nopopup" not in sys.argv:
    gmsh.fltk.run()

gmsh.finalize()

Почему это важно

Методы локального уточнения часто строятся на наложении нескольких полей размеров с ожиданием, что Min сохранит их независимость. При включённом распространении от границы эти ожидания нарушаются, что приводит к избыточному числу элементов и увеличивает время генерации сетки или расчёта. Один переключатель возвращает локальность без переработки полей размеров.

Выводы

Если выборочные полосы вдоль границ области внезапно дают плотную сетку повсюду, проверьте Mesh.MeshSizeExtendFromBoundary. Его отключение останавливает распространение от границы внутрь, которое мешает другим настройкам размера. В этой конфигурации одного Thickness недостаточно, тогда как выключение расширения проблему решает. Сохраняйте поля Box и их объединение через Min — просто убедитесь, что опция согласована с требуемой локальностью уточнений.

Статья основана на вопросе на StackOverflow от Nathaniel Wood и ответе от Nathaniel Wood.