Mailler une géométrie
Cette page est un guide de choix, pas une référence : elle compare les mailleurs entre eux et dit lequel prendre. La description de chaque opérateur, ses arguments et ses pièges vivent dans Opérateurs → Maillage.
La partie 3D viendra plus tard ; seul le 2D est traité ici.
Mailler en 2D
Quatre opérateurs remplissent l’intérieur d’un contour fermé. Ils prennent tous
la même chose — un ou plusieurs contours orientés en
SEG2 et une taille de maille visée — et rendent tous un maillage dont le bord
est le contour, nœuds compris.
| opérateur | méthode | types et options | rend |
|---|---|---|---|
triangulate_surface | Delaunay contraint + raffinement de Ruppert | "TRI3", "QUA4" ; size | des TRI3 — en QUA4, des quadrangles et quelques triangles |
pave_surface | front avançant, en rangées depuis le bord | "QUA4", "QUA8", "QUA9" ; size, prefer_quads, relax | des QUA4, quelques TRI3 |
grid_surface | cœur en grille + bande frontale au bord | "QUA4", "QUA8", "QUA9" ; size, band, prefer_quads, relax | des QUA4, quelques TRI3 |
grid_surface2 | cœur en grille, une ligne par nœud du contour, + bande frontale au bord | "QUA4", "QUA8", "QUA9" ; size, band, prefer_quads, relax | des QUA4, quelques TRI3 |
Le premier argument est le contour, le deuxième le type d’élément. Les options :
size— la taille de maille visée ; sans elle, la longueur moyenne des arêtes du contour, domaine par domaine ;prefer_quads(défautFalse) —Truerefuse une boucle au nombre impair de segments, qu’aucun pavage en quadrangles ne remplit, puis tient le front pair jusqu’au bout. Moins de triangles, mais pas aucun : la fermeture en pose encore là où un quadrangle serait retourné. C’est une préférence, pas une garantie ;relax(défaut"free") — ce que le front s’autorise entre deux rangées :"free","along"ou"none"(voir plus bas) ;band(défaut0) — une marge, en mailles, entre le cœur en grille et le contour ; zéro est la valeur utile, à relever seulement pour un contour que la grille ne peut pas rejoindre, comme une courbe.
Comment lire les figures
Chaque figure montre le même contour maillé quatre fois. Il est construit
une seule fois, puis dupliqué par translate : aucun mailleur ne bénéficie
d’une discrétisation de bord différente des autres. La disposition ne change
jamais — en haut triangulate_surface et pave_surface, en bas grid_surface
et grid_surface2. Le contour est en bleu et ses nœuds en rouge.
Les figures et les chiffres de cette page sortent d’un seul script,
examples/comparer_mailleurs_2d.py.
La qualité citée est la mean ratio du pire coin : 1 pour un coin droit à côtés égaux, 0 pour un coin plat, négatif pour une maille retournée. C’est la même mesure pour les triangles et pour les quadrangles, ce qui est la seule façon de comparer les quatre sur un pied d’égalité. On donne aussi le 5ᵉ centile, qui dit ce que valent les mailles médiocres et non la seule plus mauvaise.
En pratique cette mesure ne descend jamais à zéro dans les tableaux qui suivent, et ce n’est pas une chance : les quatre mailleurs refusent de rendre un maillage portant une maille retournée ou plate. Une telle maille a un jacobien négatif ou nul, qu’aucun code éléments finis n’intègre — ce n’est pas un maillage médiocre mais un maillage faux, et mieux vaut une erreur qui situe la maille fautive. Un mailleur qui échoue là où un autre passe est donc un résultat en soi, à lire dans les tableaux comme tel.
Une forme rectilinéaire posée sur la grille
C’est le cas le plus favorable aux mailleurs en grille, et la différence est franche.
| forme | triangulate | pave | grid | grid2 | |
|---|---|---|---|---|---|
| rectangle 1 × 0,63 | mailles | 286 | 96 | 60 | 60 |
| pire | 0,425 | 0,389 | 0,999 | 0,999 | |
| plaque, marche sur la grille | mailles | 394 | 124 | 80 | 80 |
| pire | 0,489 | 0,669 | 1,000 | 1,000 |
Les deux mailleurs en grille rendent le maillage qu’on dessinerait à la main : toutes les mailles sont des rectangles et il n’y a pas un seul triangle. Le paveur, lui, rend une pelure d’oignon dont les rangées se referment sur une ligne médiane, avec deux petites zones de fermeture à ses bouts — sa faiblesse est là où ses rangées se referment, et cette fermeture existe même sur un rectangle.
Une forme rectilinéaire qui ne tombe pas sur la grille
Dès que les cotes ne sont plus des multiples de la taille visée, grid_surface
et grid_surface2 se séparent nettement.
| forme | triangulate | pave | grid | grid2 | |
|---|---|---|---|---|---|
| plaque à marche (0,53 ; 0,61) | mailles | 366 | 130 | 86 | 80 |
| pire | 0,481 | 0,571 | 0,405 | 0,963 | |
| L à cotes quelconques | mailles | 308 | 111 | 76 | 70 |
| pire | 0,459 | 0,680 | 0,437 | 0,979 | |
| L étiré à 1,02 | mailles | 319 | 107 | 81 | 74 |
| pire | 0,477 | 0,639 | 0,307 | 0,606 | |
| L étiré à 1,10 | mailles | 336 | 112 | 80 | 74 |
| pire | 0,474 | 0,649 | 0,421 | 0,963 |
grid_surface pose une ligne sur la coordonnée où repose chaque côté aligné,
puis découpe entre deux lignes d’après le côté qui les enjambe : toutes ses
lignes sont droites. grid_surface2 donne à chaque nœud du contour la ligne qui
le traverse et laisse ses rangées plier pour aller chercher le contour — une
même rangée peut alors rejoindre deux parois qui se font face à deux hauteurs
différentes, ce qu’aucune droite ne sait faire.
Le L étiré à 1,02 est le seul de la série que grid_surface2 ne règle pas — le
paveur y fait mieux :
les deux côtés y posent un nombre de nœuds différent sur la même portée,
dix contre onze, et aucune disposition de lignes n’invente la rangée manquante.
Le remède est dans le contour, pas dans le mailleur — voir
la règle de discrétisation.
Un profil à sept angles rentrants
Le profil crénelé est décliné en deux versions dont seule la base change : coupée sous chaque barre, ou d’un seul tenant. C’est le meilleur révélateur de la sensibilité d’un mailleur à la discrétisation du contour.
| forme | triangulate | pave | grid | grid2 | |
|---|---|---|---|---|---|
| base coupée | mailles | 2 050 | 816 | 456 | 456 |
| pire | 0,412 | 0,118 | 0,860 | 0,916 | |
| base d’un seul tenant | mailles | 2 089 | 827 | 471 | 456 |
| pire | 0,419 | 0,415 | 0,287 | 0,651 |
grid_surface2 rend exactement le même nombre de mailles dans les deux cas.
Les trois autres paient la base d’un seul tenant. C’est la propriété la plus
utile de ce mailleur : il pardonne une discrétisation de contour que les autres
font payer.
Une forme oblique ou courbe
Ici le classement s’inverse, et c’est le seul endroit où il le fait.
| forme | triangulate | pave | grid | grid2 | |
|---|---|---|---|---|---|
| maison | mailles | 1 723 | 611 | 447 | 450 |
| pire | 0,484 | 0,515 | 0,365 | 0,548 | |
| carré arrondi | mailles | 1 866 | 665 | 418 | 404 |
| pire | 0,474 | 0,360 | 0,340 | 0,467 | |
| cercle R = 1 | mailles | 1 496 | 547 | 312 | 530 |
| pire | 0,527 | 0,081 | 0,179 | 0,444 |
Une grille ne peut pas suivre une oblique : elle la découpe en escalier, et tout
ce qui s’en approche est rendu au paveur frontal. Sur la maison, où l’oblique
ne fait que le toit, ce qui reste de rectilinéaire suffit encore à
grid_surface2 ; sur le cercle, qui n’a plus rien de droit,
triangulate_surface est le meilleur des quatre.
Sur une forme sans direction dominante, grid_surface2 n’est pas un
candidat. Il prend l’écartement de ses lignes sur le contour, et un cercle
n’en dicte aucun : selon la taille demandée, son cœur tient ou s’effondre. À la
taille de la figure il rend 0,444, mais la taille voisine le fait tomber au
bord de la dégénérescence. Pire maille du même cercle, selon la taille :
| taille | triangulate | pave | grid | grid2 |
|---|---|---|---|---|
| 0,05 | 0,424 | 0,040 | 0,366 | 0,011 |
| 0,06 | 0,526 | 0,031 | 0,291 | 0,406 |
| 0,07 | 0,424 | 0,015 | 0,137 | 0,012 |
| 0,08 | 0,436 | 0,039 | 0,191 | 0,035 |
| 0,1 | 0,527 | 0,081 | 0,179 | 0,444 |
| 0,12 | 0,497 | 0,003 | 0,362 | 0,005 |
triangulate_surface ne descend jamais sous 0,42 et grid_surface jamais sous
0,13 ; les deux autres passent d’une maille correcte à une maille presque plate
d’une taille à la voisine. Sur une courbe, prenez triangulate_surface si la
qualité du pire élément commande, grid_surface s’il faut des quadrangles.
Le cercle mérite un mot de plus. pave_surface y tombe à 0,081 — ses
rangées se referment toutes au centre, et c’est cette fermeture qui porte tout
le défaut. triangulate_surface, lui, ne descend jamais
sous 0,41, sur aucune des onze formes : c’est la signature d’un Delaunay
raffiné, qui garantit un angle minimal et rien de plus. Il ne s’effondre jamais,
mais il ne monte jamais non plus.
Ce qu’il faut retenir
Le coût en mailles est le classement le plus stable : sur les dix formes
rectilinéaires ou peu obliques, les deux mailleurs en grille rendent le moins
de mailles — grid_surface2 le plus souvent, grid_surface sur la maison, à
trois mailles près —, puis pave_surface, puis triangulate_surface, avec un
facteur quatre à cinq entre les extrêmes, à qualité égale ou meilleure (à un
centième près sur le carré arrondi). Pour un calcul, c’est le facteur qui compte
juste après la qualité. Le cercle est la seule exception : grid_surface2 y rend
plus de mailles que grid_surface (530 contre 312), pour une qualité qui
s’effondre d’une taille à l’autre — la façon la plus nette de dire qu’il n’est
pas fait pour ça.
En pratique :
- forme rectilinéaire —
grid_surface2, et d’autant plus si ses côtés n’ont pas été coupés aux angles qui leur font face ; - forme courbe —
triangulate_surfacesi la qualité du pire élément commande,grid_surfaces’il faut des quadrangles ; jamaisgrid_surface2, qui y perd son cœur à la plupart des tailles ; - forme franchement oblique —
pave_surface, dont les rangées épousent la pente ; mais tant qu’il reste des parois droites autour de l’oblique, les mailleurs en grille tiennent — la maison en est l’exemple ; - le moins de triangles possible —
prefer_quads=True, sur les trois paveurs, refuse par une erreur claire un contour dont la parité l’interdit ; quelques triangles peuvent encore rester là où un quadrangle serait retourné ; - triangles voulus —
triangulate_surface, seul à en produire par construction.
Le front peut garder ses angles : relax
Les chiffres de pave_surface ci-dessus sont ceux du réglage par défaut. Entre
deux rangées, le front est relaxé — c’est ce qui l’empêche de se plisser — et
cette relaxation est un laplacien, qui arrondit les angles. Or un front ne
perd des nœuds qu’à ses coins : une fois les coins arrondis, il garde tous ses
nœuds pendant que son périmètre rétrécit, et le milieu du domaine sort plus fin
que la taille demandée. Un carré 20 × 20 à la taille 1 sort à 592 mailles au
lieu de 400, les plus intérieures à 0,26 de l’aire visée.
relax="along" garde le même déplacement mais projeté sur le front :
l’écartement s’égalise encore, la forme n’est plus rabotée, et le carré sort
comme les 400 carrés exacts qu’on dessinerait. relax="none" ne relaxe pas du
tout. Aucun des trois ne gagne partout. Sur les formes à angles, "along" et
"none" l’emportent nettement ; sur une courbe, il n’y a pas d’angle à
préserver, et aucun réglage ne protège le paveur — sur le cercle, chacun des
trois passe devant ou tombe près de zéro selon la taille. Mesuré sur un carré
et une bande à la taille 1, puis sur trois formes des figures ci-dessus, à leur
taille :
| forme | "free" | "along" | "none" |
|---|---|---|---|
| carré 20 × 20 | 592 mailles, pire 0,619 | 400, 1,000 | 400, 1,000 |
| bande étroite 40 × 3 | 123, 0,702 | 120, 1,000 | 123, 0,684 |
| L à cotes quelconques | 111, 0,680 | 66, 0,780 | 66, 0,774 |
| créneau, base coupée | 816, 0,118 | 586, 0,426 | 577, 0,546 |
| cercle R = 1 | 547, 0,081 | 610, 0,281 | 620, 0,027 |
Le détail du mécanisme est sur la page Mailler des opérateurs. Les deux mailleurs en grille prennent le même réglage, qui n’y gouverne que la bande : la grille est posée droite quoi qu’il arrive.
Deux limites connues
Un vide que le contour ne borde pas n’est pas maillé en grille.
grid_surface2 prend l’écartement de ses lignes sur le contour ; là où le
contour ne dit rien — le triangle du toit de la maison, qu’aucune paroi
verticale ne borde — il coupe le vide en mailles entières tant qu’il ne dépasse
pas trois fois l’écartement moyen, et l’abandonne au front au-delà.
Remplir un tel vide de rangées ne crée pas du cœur : ces rangées n’existent que
pour être érodées par l’oblique qui les traverse, et elles morcellent le travail
du front au lieu de lui donner une région propre. Sur la maison, l’abandon ne
laisse qu’un triangle (5ᵉ centile 0,725) ; quand ce seuil a été posé, le
remplissage en coûtait cinq.
C’est aussi ce qui coûte le cercle, dont le contour laisse de grands vides.
Aucun seuil ne sépare les deux cas — abandonner un vide abandonne a fortiori un
vide plus grand — et c’est un choix assumé : grid_surface2 sert les formes rectilinéaires, grid_surface sert les
courbes.
Le cercle est un objectif instable. Sur une forme sans direction dominante,
un rien déplace les décisions des mailleurs : le tableau des tailles le montre,
et la position compte aussi — le même cercle, maillé à l’origine au lieu de sa
place dans la figure, sort de grid_surface2 à 0,459 au lieu de 0,444. Les
chiffres du cercle ci-dessus sont justes mais ne se prolongent pas : ne réglez
aucun paramètre dessus.
Reproduire les figures
maturin develop --features extension-module,viz
python examples/comparer_mailleurs_2d.py
# Figures du livre :
PYRUCAST_IMG_DIR=book/src/img python examples/comparer_mailleurs_2d.py
Le script tient dans une fonction : elle prend un contour, le duplique trois
fois par translate, maille chaque copie par une méthode et réunit le tout sur
une figure.
def comparer(nom, contour, taille, fichier, noeuds=True):
"""Maille `contour` par les quatre méthodes et trace la figure.
/ Meshes `contour` with all four methods and draws the figure.
FR — Le contour n'est construit qu'une fois : ses trois copies viennent de
`translate`, qui rend un maillage neuf dans les mêmes `Coords` — c'est ce
qui permet de les réunir sur une seule figure.
"""
points = [n.position() for sub in contour for cell in sub for n in cell]
xs, ys = [p[0] for p in points], [p[1] for p in points]
dx, dy = (max(xs) - min(xs)) * 1.15, (max(ys) - min(ys)) * 1.25
coins = [(0.0, 0.0), (dx, 0.0), (0.0, -dy), (dx, -dy)]
tout, lignes = None, []
for (titre, mailler), coin in zip(MAILLEURS, coins):
copie = pc.mesh.translate(contour, list(coin))
maillage = mailler(copie, taille)
qs = sorted(
qualite([n.position() for n in cell]) for sub in maillage for cell in sub
)
comptes = dict(zip(maillage.element_types(), maillage.cell_counts()))
lignes.append(
f"{titre:20s} {len(qs):6d} mailles, {comptes.get('TRI3', 0):5d} tri, "
f"pire {qs[0]:.3f}, p5 {qs[len(qs) // 20]:.3f}"
)
for sub in copie:
sub.face_color = (20, 90, 200)
bloc = maillage | copie
if noeuds:
points_rouges = pc.mesh.to_poi1(copie)
for sub in points_rouges:
sub.face_color = (220, 30, 30)
bloc = bloc | points_rouges
tout = bloc if tout is None else tout | bloc
print(f"\n{nom} (taille visée {taille:g})")
for ligne in lignes:
print(f" {ligne}")
tout.plot(
view=VUE,
show_axes=False,
save=os.path.join(OUT, fichier),
title=f"{nom} — h={taille:g} — haut : triangulate, pave — bas : grid, grid2",
)
Mailler en 3D
À venir. En attendant, les mailleurs volumiques — triangulate_volume
(tétraèdres), pave_volume (couche limite d’hexaèdres autour d’un cœur
tétraédrique), extrude et sweep_solid — sont décrits dans
Opérateurs → Maillage.