2 Manipuler les maillages non structurés
3 --------------------------------------
5 Les meshes non-structurées sont le type de maillage le plus utilisé. ``MEDCouplingUMesh`` est le nom de la classe en charge
6 de représenter ces maillages dans MEDCoupling. ``MEDCouplingUMesh`` hérite de la classe ``MEDCouplingPointSet``.
7 ``MEDCouplingPointSet`` gère toutes les méthodes relatives au coordonnées. ``MEDCouplingUMesh`` a deux attributs en plus de
8 ceux de ``MEDCouplingPointSet`` permettant de décrire la liste des noeuds contribuants à une cellule (i.e. la *connectivité*).
13 Le but ici est de manipuler des maillages non structurés (en extraire une partie, etc...).
14 Plusieurs points seront traités dans cet exercice :
16 * modification des coordonnées d'un maillage
17 * extraction d'une coupe d'un maillage
18 * extraire une partie de maillage à partir d'identifiants de cellules
19 * manipuler les indices, etc ...
20 * manipulation de la connectivité descendante
22 .. image:: images/UMesh1.png
25 Début de l'implémentation
26 ~~~~~~~~~~~~~~~~~~~~~~~~~
28 Importer le module Python ``medcoupling``. ::
30 import medcoupling as mc
32 Construire un maillage. Ce maillage ``mesh3D`` contient artificiellement 2 types de cellules (``mc.NORM_HEXA8`` et ``mc.NORM_POLYHED``)
33 pour appréhender le mélange de types geometriques.
34 ``mesh3D`` est un *maillage extrudé* contenant 18 cellules composées de 3 niveaux selon Z, chaque niveau ayant 6 cellules.
35 Faire un bon gros copier-coller des lignes suivantes pour construire la mesh (l'intérêt de l'exercise vient après) : ::
37 coords=[0.,0.,0., 1.,1.,0., 1.,1.25,0., 1.,0.,0., 1.,1.5,0., 2.,0.,0., 2.,1.,0., 1.,2.,0., 0.,2.,0., 3.,1.,0.,
38 3.,2.,0., 0.,1.,0., 1.,3.,0., 2.,2.,0., 2.,3.,0.,
39 0.,0.,1., 1.,1.,1., 1.,1.25,1., 1.,0.,1., 1.,1.5,1., 2.,0.,1., 2.,1.,1., 1.,2.,1., 0.,2.,1., 3.,1.,1.,
40 3.,2.,1., 0.,1.,1., 1.,3.,1., 2.,2.,1., 2.,3.,1.,
41 0.,0.,2., 1.,1.,2., 1.,1.25,2., 1.,0.,2., 1.,1.5,2., 2.,0.,2., 2.,1.,2., 1.,2.,2., 0.,2.,2., 3.,1.,2.,
42 3.,2.,2., 0.,1.,2., 1.,3.,2., 2.,2.,2., 2.,3.,2.,
43 0.,0.,3., 1.,1.,3., 1.,1.25,3., 1.,0.,3., 1.,1.5,3., 2.,0.,3., 2.,1.,3., 1.,2.,3., 0.,2.,3., 3.,1.,3.,
44 3.,2.,3., 0.,1.,3., 1.,3.,3., 2.,2.,3., 2.,3.,3.]
45 conn=[0,11,1,3,15,26,16,18, 1,2,4,7,13,6,-1,1,16,21,6,-1,6,21,28,13,-1,13,7,22,28,-1,7,4,19,22,-1,4,2,17,19,-1,2,1,16,17,-1,16,21,28,22,19,17,
46 1,6,5,3,16,21,20,18, 13,10,9,6,28,25,24,21, 11,8,7,4,2,1,-1,11,26,16,1,-1,1,16,17,2,-1,2,17,19,4,-1,4,19,22,7,-1,7,8,23,22,-1,8,11,26,23,-1,26,16,17,19,22,23,
47 7,12,14,13,22,27,29,28, 15,26,16,18,30,41,31,33, 16,17,19,22,28,21,-1,16,31,36,21,-1,21,36,43,28,-1,28,22,37,43,-1,22,19,34,37,-1,19,17,32,34,-1,17,16,31,32,-1,31,36,43,37,34,32,
48 16,21,20,18,31,36,35,33, 28,25,24,21,43,40,39,36, 26,23,22,19,17,16,-1,26,41,31,16,-1,16,31,32,17,-1,17,32,34,19,-1,19,34,37,22,-1,22,23,38,37,-1,23,26,41,38,-1,41,31,32,34,37,38,
49 22,27,29,28,37,42,44,43, 30,41,31,33,45,56,46,48, 31,32,34,37,43,36,-1,31,46,51,36,-1,36,51,58,43,-1,43,37,52,58,-1,37,34,49,52,-1,34,32,47,49,-1,32,31,46,47,-1,46,51,58,52,49,47,
50 31,36,35,33,46,51,50,48, 43,40,39,36,58,55,54,51, 41,38,37,34,32,31,-1,41,56,46,31,-1,31,46,47,32,-1,32,47,49,34,-1,34,49,52,37,-1,37,38,53,52,-1,38,41,56,53,-1,56,46,47,49,52,53,
51 37,42,44,43,52,57,59,58]
52 mesh3D = mc.MEDCouplingUMesh("mesh3D",3)
53 mesh3D.allocateCells(18)
54 mesh3D.insertNextCell(mc.NORM_HEXA8,conn[0:8]); mesh3D.insertNextCell(mc.NORM_POLYHED,conn[8:51]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[51:59]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[59:67]); mesh3D.insertNextCell(mc.NORM_POLYHED,conn[67:110]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[110:118]);
55 mesh3D.insertNextCell(mc.NORM_HEXA8,conn[118:126]); mesh3D.insertNextCell(mc.NORM_POLYHED,conn[126:169]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[169:177]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[177:185]); mesh3D.insertNextCell(mc.NORM_POLYHED,conn[185:228]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[228:236]);
56 mesh3D.insertNextCell(mc.NORM_HEXA8,conn[236:244]); mesh3D.insertNextCell(mc.NORM_POLYHED,conn[244:287]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[287:295]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[295:303]); mesh3D.insertNextCell(mc.NORM_POLYHED,conn[303:346]); mesh3D.insertNextCell(mc.NORM_HEXA8,conn[346:354]);
57 myCoords = mc.DataArrayDouble(coords,60,3)
58 myCoords.setInfoOnComponents(["X [m]","Y [m]","Z [m]"])
59 mesh3D.setCoords(myCoords)
60 mesh3D.orientCorrectlyPolyhedrons()
61 mesh3D.sortCellsInMEDFileFrmt()
62 mesh3D.checkConsistencyLight()
63 renum = mc.DataArrayInt(60)
64 renum[:15]=list(range(15,30))
65 renum[15:30]=list(range(15))
66 renum[30:45]=list(range(45,60))
67 renum[45:]=list(range(30,45))
68 mesh3D.renumberNodes(renum,60)
73 On convertit ici les coordonnées de mètres en centimètres.
74 Cela paraît idiot mais c'est un très grand classique du couplage ... ::
76 mesh3D.getCoords()[:] *= 100.
77 mesh3D.getCoords().setInfoOnComponents(["X [cm]","Y [cm]","Z [cm]"])
79 .. note:: Il est important de mettre à jour les informations sur les composantes des coordonnées (les unités) pour éviter toute ambiguïté.
80 INTERP_KERNEL library inclut un évaluateur d'unité.
82 .. note:: Noter l'astuce sur la première ligne ``[:]`` afin de récupérer la version inscriptible des coordonnées
83 (et non une copie temporaire)
85 Trouver les différents niveaux
86 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88 Le maillage est extrudé, il est donc très régulier, et aligné sur les axes Ox, Oy et Oz (cf figure).
89 On veut connaître quelles
90 sont les côtes Z des différentes couches de cubes.
91 Extraire les différents niveaux en Z dans ``mesh3D``, rangés de manière croissante.
92 Utiliser la méthode ``DataArrayDouble.getDifferentValues()`` and ``DataArrayDouble.sort()``. ::
94 zLev = mesh3D.getCoords()[:,2]
95 zLev = zLev.getDifferentValues(1e-12)
96 zLev.sort() # In-place sort
98 Extraire des identifiants de cellules
99 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101 Extraire les 6 identifiants des cellules de la seconde rangée suivant Oz.
102 Il y a 3 possibilités pour faire cela. Nous allons les voir du plus simple au plus complexe.
104 * En utilisant ``buildSlice3D()`` :
105 Méthode très simple mais gourmande en CPU. Pour trouver la solution il suffit de définir un plan dont le vecteur normal est ``[0.,0.,1.]``
106 et passant par le point ``[0., 0., (zLev[1]+zLev[2])/2]``.
107 La méthode retourne deux choses : le maillage de coupe ``tmp`` (un maillage de mesh-dimension 2, mais de dimension spatiale
108 3) et pour chaque cellule 3D surfacique de ``tmp``, l'identifiant de la cellule 3D (=un volume) coupée dans le
109 maillage de départ ::
111 tmp, cellIdsSol1 = mesh3D.buildSlice3D([0.,0.,(zLev[1]+zLev[2])/2], [0.,0.,1.], 1e-12)
113 * En utilisant les barycentres des cellules de ``mesh3D`` :
114 L'utilisation des barycentres est une technique classique pour identifier un ensemble de cellules répondant à certains
115 critères géométriques.
116 Il s'agit d'abord de calculer les barycentres des cellules 3D de ``mesh3D`` (méthode
117 ``MEDCouplingUMesh.computeCellCenterOfMass()``).
119 Ensuite sélectionner la composante #2 des barycentres des cellules et mettre le résultat dans ``baryZ``.
120 Ensuite il suffit de selectionner dans ``baryZ`` les tuples qui sont dans l'intervalle ``[zLev[1], zLev[2]]``.
121 Les identifiants de ces tuples (i.e. leur index dans ``baryZ``) est directement un identifiant de cellule
122 car ``computeCellCenterOfMass()`` retourne un tableau indéxé par les numéros de cellule.::
124 bary = mesh3D.computeCellCenterOfMass()
126 cellIdsSol2 = baryZ.findIdsInRange(zLev[1], zLev[2])
128 * En utilisant ``MEDCouplingMappedExtrudedMesh`` :
129 C'est la méthode exclusivement basée sur la connectivité nodale pour déduire l'extrusion. Les coordonnées sont ici ignorées.
130 Pour construire un ``MEDCouplingMappedExtrudedMesh`` deux objets sont requis. Le maillage non-structuré 3D
131 représentant en fait un maillage *extrudé*, et un maillage non structuré 3D surfacique (mesh-dim 2)
132 reposant sur les mêmes coordonnéees, à partir duquel l'extrusion sera calculée.
133 Commencer par construire le maillage 3D surfacique. Pour ce faire il suffit de repérer les noeuds appartenant
134 à 1e-10 près de plan de vecteur normal ``[0.,0.,1.]`` et passant
135 par ``[0.,0.,zLev[0]]`` (``MEDCouplingUMesh.findNodesOnPlane()``). Ensuite appeler ``MEDCouplingUMesh.buildFacePartOfMySelfNode()``
136 pour construire ``mesh2D`` (lire la doc de la fonction). ::
138 nodeIds = mesh3D.findNodesOnPlane([0., 0., zLev[0]], [0.,0.,1.], 1e-10)
139 mesh2D = mesh3D.buildFacePartOfMySelfNode(nodeIds, True)
142 Il est alors possible de construire un maillage extrudé ``extMesh`` à partir de ``mesh3D`` et de ``mesh2D``.
143 Un maillage extrudé se construit en *reconnaissant* un maillage non structuré comme étant l'extrusion d'un maillage
144 de dimension ``n-1`` (avec ``n`` la dimension initiale de ``mesh3D``, ici 3). Si cela n'est pas le cas, la construction
145 plante. Le maillage 2D est forcément en haut ou en bas du 3D volumique, et le dernier entier spécifie la cellule à partir
146 de laquelle le fil de fer 1D guidant l'extrusion sera construit : ::
148 extMesh = mc.MEDCouplingMappedExtrudedMesh(mesh3D, mesh2D, 0)
150 On a alors la garantie que, dans ``extMesh``, les cellules sont ordonnées par niveau Z croissant.
151 Il suffit de récupérer le 2ème niveau (``MEDCouplingMappedExtrudedMesh.getMesh3DIds()``). ::
153 n_cells = mesh2D.getNumberOfCells()
154 cellIdsSol3 = extMesh.getMesh3DIds()[n_cells:2*n_cells]
156 On vérifie alors que les 3 solutions sont les mêmes : ::
158 print(cellIdsSol1.getValues())
159 print(cellIdsSol2.getValues())
160 print(cellIdsSol3.getValues())
163 Extraire une sous partie d'un maillage 3D
164 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
166 Utiliser les identifiants de cellules ``cellIdsSol2`` obtenus précédemment pour extraire une sous-partie de ``mesh3D``,
167 c'est-à-dire un maillage avec un sous-ensemble des cellules de ``mesh3D``. ::
169 mesh3DPart = mesh3D[cellIdsSol2]
171 .. note:: En C++ la méthode sous-jacente invoquée (et par ailleurs aussi disponible en Python) s'appelle
172 ``mesh3DPart = mesh3D.buildPartOfMySelf(cellIdsSol2,True)``
174 .. note:: Le type géométrique ne rentre pas du tout en compte ici. L'instruction précédente prend les cellules
175 dans l'ordre où elles sont disponibles dans le maillage initial.
177 L'objet ``mesh3DPart`` contient ``len(cellIdsSol2)`` cellules désormais. La cellule #0 de ``mesh3DPart`` correspond à la cellule avec l'identifiant ``cellIdsSol2[0]`` de ``mesh3D``, et ainsi de suite. Ainsi ``cellIdsSol2`` peut être vu comme un
180 A ce point, ``mesh3DPart`` repose sur une copie du tableau de coordonnées de ``mesh3D``, c'est-à-dire 60 nodes.
181 Seuls 30 sont effectivement utilisés.
182 Pour retirer les noeuds orphelins de ``mesh3DPart`` invoquer simplement ``MEDCouplingUMesh.zipCoords()``. ::
184 mesh3DPart.zipCoords()
186 Maintenant, ``mesh3DPart`` repose sur 30 nodes et possède 6 cellules. Pour être prêt aux I/O MED-fichier, il est
187 alors important de voir si ``mesh3DPart`` est bien ordonné, c'est-à-dire si ses cellules sont bien rangées par type géométrique.
188 On commence par inspecter l'état actuel : ::
190 print(mesh3DPart.advancedRepr())
192 La fonction suivante fait le même travail : ::
194 print(mesh3DPart.checkConsecutiveCellTypesAndOrder([mc.NORM_HEXA8, mc.NORM_POLYHED]))
198 print(mesh3DPart.checkConsecutiveCellTypes())
200 On voit que ``mesh3DPart`` contient 6 cellules, quatre HEXA8 puis deux POLYHED. Les cellules sont bien
201 groupées par type géométrique. Si ce n'était pas le cas, on aurait pu invoquer ``MEDCouplingUMesh.sortCellsInMEDFileFrmt()``.
204 Extraire des cellules alignées sur une ligne 3D
205 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
207 On souhaite extraire de ``mesh3D`` les 3 cellules dont les barycentres sont le long de la ligne portée par
208 ``v = [0.,0.,1.]`` et passant par ``pt = [250.,150.,0.]``.
209 Il y a deux solutions.
211 * les barycentres de ``mesh3D`` : même principe qu'au-dessus. ::
213 baryXY = bary[:,[0,1]]
214 baryXY -= [250.,150.]
215 magn = baryXY.magnitude()
216 cellIds2Sol1 = magn.findIdsInRange(0.,1e-12)
218 * utiliser le maillage extrudé ``extMesh`` : partant de l'unique cellule dans ``mesh2D`` dont le centre est
219 en ``[250.,150.,0.]``, la méthdode ``MEDCouplingMappedExtrudedMesh.getMesh3DIds()`` retourne les identifiants de
220 cellules rangée par rangée. ::
222 bary2 = mesh2D.computeCellCenterOfMass()[:,[0,1]]
224 magn = bary2.magnitude()
225 ids = magn.findIdsInRange(0.,1e-12)
226 idStart = int(ids) # ids is assumed to contain only one value, if not an exception is thrown
227 ze_range = list(range(idStart,mesh3D.getNumberOfCells(),mesh2D.getNumberOfCells()))
228 cellIds2Sol2 = extMesh.getMesh3DIds()[ze_range]
230 Maintenant on construit cette sous partie de ``mesh3D`` en utilisant ``cellIds2Sol1`` ou ``cellIds2Sol2``: ::
232 mesh3DSlice2 = mesh3D[cellIds2Sol1]
233 mesh3DSlice2.zipCoords()
235 Duplication, translation et aggrégation de maillages
236 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238 Cette partie de l'exercice est intéressante pour construire des maillages complexes, ou pour aggréger des parties
239 de maillages venant de différents processeurs.
241 On cherche ici à dupliquer ``mesh3DSlice2``, le translater et l'aggréger avec l'original.
243 Effectuer une copie complète de ``mesh3DSlice2`` (aussi appelée *deep copy*) sous le nom ``mesh3DSlice2bis``.
244 Sur cette copie effectuer une translation de ``v=[0.,1000.,0.]``.
245 Puis aggréger ``mesh3DSlice2`` avec sa copie translatée ``mesh3DSlice2bis``, en utilisant ``MEDCouplingUMesh.MergeUMeshes()``. ::
247 mesh3DSlice2bis = mesh3DSlice2.deepCopy()
248 mesh3DSlice2bis.translate([0.,1000.,0.])
249 mesh3DSlice2All = mc.MEDCouplingUMesh.MergeUMeshes([mesh3DSlice2,mesh3DSlice2bis])
250 mesh3DSlice2All.writeVTK("mesh3DSlice2All.vtu")
252 .. note:: Pour information pour merger deux (ou plus) maillages non structurés, il faut invoquer ``MEDCouplingUMesh.MergeUMeshes()``
253 puis ``MEDCouplingUMesh.mergeNodes()`` sur le résultat, et enfin ``MEDCouplingUMesh.zipConnectivityTraducer()``.
255 .. _exo-umesh-desc-connec:
257 Connectivité descendante
258 ~~~~~~~~~~~~~~~~~~~~~~~~
260 Le but ici est de présenter la notion de *connectivité descendante* (*descending connectivity*).
262 La connectivité descendante représente les éléments de dimension ``n-1``
263 constituant chacune des cellules de dimension ``n`` (avec donc ``n`` la dimension du maillage, *mesh-dim*). Par exemple, pour un
264 maillage de dimension 3 (les cellules sont des *volumes* 3D), cela donne l'ensemble des faces (des *surfaces* 2D) bordant
267 A titre d'exemple, on se propose dans notre cas de récupérer les faces *internes* du maillage ``mesh3D``.
268 Pour cela il est nécessaire de construire le maillage
269 descendant de ``mesh3D`` (stocké dans ``mesh3DSurf``) c'est-à-dire
270 le maillage de mesh-dimension 2 (soit ``mesh3D.getMeshDimension()-1``) constitué
271 des *faces* bordant chacune des cellules (ici des *volumes* 3D) de ``mesh3D``.
272 La méthode ``MEDCoupling.buildDescendingConnectivity()`` calcule ce maillage, et retourne en même temps des tableaux
273 de correspondance. Ces tableaux font le lien entre les identifiants des cellules de ``mesh3D``
274 vers les identifiants de cellules de ``mesh3DSurf``, et vice-et-versa.
276 Une face de ``mesh3DSurf`` est dite interne, si et seulement si, elle est partagée par plus d'une cellule 3D de ``mesh3D``.
277 Les 3ème et 4ème paramètres de sortie de la fonction donnent le lien
278 entre une face et ses cellules *parentes* (i.e. le ou les volumes qu'elle délimite).
279 Ce lien est exprimé au format *indirect index* vu dans le premier exercice :ref:`indirect-index-exo`. ::
281 mesh3DSurf, desc, descIndx, revDesc, revDescIndx = mesh3D.buildDescendingConnectivity()
282 numberOf3DCellSharing = revDescIndx.deltaShiftIndex()
283 cellIds = numberOf3DCellSharing.findIdsNotEqual(1)
284 mesh3DSurfInside = mesh3DSurf[cellIds]
285 mesh3DSurfInside.writeVTK("mesh3DSurfInside.vtu")
287 Ce genre de manipulation est très utile pour accéder au voisinage d'une ou plusieurs cellules d'un maillage non-structuré.
289 .. image:: images/mesh3DSurfInside.jpg
294 :ref:`python_testMEDCouplingumesh1_solution`