Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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érateurméthoderend
triangulate_surfaceDelaunay contraint + raffinement de Ruppertdes TRI3
pave_surfacefront avançant, en rangées depuis le borddes QUA4, quelques TRI3
grid_surfacecœur en grille + bande frontale au borddes QUA4, quelques TRI3
grid_surface2idem, lignes prises une par nœud du contourdes QUA4, quelques TRI3

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.

formetriangulatepavegridgrid2
rectangle 1 × 0,63mailles286986060
pire0,4250,4650,9990,999
plaque, marche sur la grillemailles3941388080
pire0,4890,2901,0001,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 avec quatre coutures diagonales — sa faiblesse est là où deux de ses rangées se rencontrent, et cette ligne-là 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.

formetriangulatepavegridgrid2
plaque à marche (0,53 ; 0,61)mailles3661368680
pire0,4810,1870,4050,963
L à cotes quelconquesmailles3081177670
pire0,4590,4460,4370,979
L étiré à 1,02mailles3191178074
pire0,4770,5510,3510,606
L étiré à 1,10mailles3361228074
pire0,4740,4460,4210,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 : 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.

formetriangulatepavegridgrid2
base coupéemailles2 050860474456
pire0,4120,3660,3820,916
base d’un seul tenantmailles2 089863486456
pire0,4190,3370,3230,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.

formetriangulatepavegridgrid2
maisonmailles1 723620460450
pire0,4840,4910,4200,548
carré arrondimailles1 866668424409
pire0,4740,3600,3400,371
cercle R = 1mailles6 1142 3391 2362 044
pire0,4240,0310,3660,005

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 : rien ne pose de ligne entre y = 0,24 et y = 1, le vide y vaut quinze fois l’écartement moyen, et le cœur s’effondre — 0,005, une maille au bord de la dégénérescence. grid_surface, lui, rend 0,288 avec un 5ᵉ centile de 0,796, de loin le meilleur des quatre. Sur une courbe, prenez grid_surface, ou triangulate_surface si la qualité du pire élément commande.

Le cercle mérite un mot de plus. pave_surface y tombe à 0,031 — ses rangées se rejoignent au centre en une étoile à quatre branches, et c’est cette ligne-là qui porte tout le défaut. triangulate_surface, lui, ne descend jamais sous 0,42, 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, grid_surface2 ≤ grid_surface < pave_surface < triangulate_surface, avec un facteur quatre entre les extrêmes à qualité au moins égale. Pour un calcul, c’est le facteur qui compte juste après la qualité. Le cercle est la seule exception, et il l’est dans les deux colonnes à la fois : grid_surface2 y rend plus de mailles pour une qualité effondrée, ce qui est 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_surface si la qualité du pire élément commande, grid_surface s’il faut des quadrangles ; jamais grid_surface2, qui y perd son cœur ;
  • 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 ;
  • quadrangles obligatoires — pave_surface accepte all_quad=True et refuse par une erreur claire un contour dont la parité l’interdit ;
  • 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 à 600 mailles au lieu de 400, les plus intérieures à 0,43 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 une courbe, il n’y a pas d’angle à préserver et le front a tout à gagner à se redresser :

forme (taille 1)"free""along""none"
carré 20 × 20600 mailles, pire 0,541400, 1,000400, 1,000
L374, 0,315287, 0,449290, 0,564
profil crénelé859, 0,240620, 0,546554, 0,441
bande étroite 40 × 3581, 0,650557, 0,662559, 0,564
cercle R = 10569, 0,105633, 0,257639, 0,020

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. Mesuré sur la maison : l’abandon coûte un triangle là où le remplissage en coûtait cinq, et le 5ᵉ centile monte de 0,644 à 0,676.

C’est aussi ce qui coûte le cercle, dont les vides valent quinze fois l’écartement moyen. Aucun seuil ne sépare les deux cas — abandonner un vide de huit moyennes abandonne a fortiori un vide de quinze — 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, déplacer la grille d’un millième change la pire maille du simple au décuple. 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.