d0.05.01 Description du parallélisme massif#

Résumé :

Le but de cette doc est d’expliquer le fonctionnement du parallélisme massif.

Tout part d’un maillage dit parallèle ( ParallelMesh ). Dans un calcul réalisé en séquentiel ou en parallèle avec le parallélisme historique, tous les processeurs connaissent intégralement la totalité du maillage. Ce n’est plus le cas en parallélisme massif. Chaque processeur ne connait plus qu’une partie du maillage global.

L’objet Joints#

Principe#

Cet objet est ce qui permet de créer un lien entre les processeurs. Comme les nœuds sont dédoublés (ou plus), il faut pouvoir communiquer les valeurs portées par ce nœud depuis le processeur qui le possède vers les autres.

Pour chaque processeur, cet objet représente les interactions qu’il doit avoir avec les processeurs qui l’entourent.

../../../../_images/03-Joints.svg

Fig. 779 Raccords entre deux processeurs#

Sur la figure Fig. 779, on peut voir les deux raccords entre deux processeurs. Ces processeurs possèderont deux listes :

  • Une liste contenant les numéros locaux des nœuds d’interface que le processeur courant possède, associé au numéro local du nœud sur le processeur d’en face (flèches bleues pour le processeur de gauche et flèches dorées pour le processeur de droite).

  • Une liste contenant les numéros locaux des nœuds d’interface que le processeur courant ne possède pas, associé au numéro local du nœud sur le processeur d’en face (qui du coup possèdera ce nœud).

Un raccord représente le lien d’un processeur avec un autre (en envoi et en réception). Il y a donc autant de raccord que de processeur entourant un processeur donné.

Attention :

Tous les processeurs de comm_world ne sont pas nécessairement présent dans l’objet Joints car il est possible qu’une partie du maillage ne doive communiquer avec aucune autre partie du maillage (par exemple dans le cas de deux maillages disjoints répartis sur deux processeurs. Dans ce cas, les maillages ne partagent pas de nœuds et ne communiquent donc pas l’un avec l’autre).

Contenu informatique de l’objet#

'.NBLG' : S V I (long=1)

Cet objet indique le nombre de couches de mailles fantômes.

'.DOMJ' : S V I

Ce vecteur contient la liste des numéros de processeurs (dans comm_world) qui entourent le processeur courant.

'.SEND' : XC V I NU

Pour chaque raccord, cette collection contient la liste des identifiants (en numérotation locale) des nœuds en vis-à-vis pour les nœuds possédés localement. Les identifiants commencent à 1 (convention du langage Fortran).

'.RECV' : XC V I NU

Pour chaque raccord, cette collection contient la liste des identifiants (en numérotation locale) des nœuds en vis-à-vis pour les nœuds qui ne sont pas possédés localement. Les identifiants commencent à 1 (convention du langage Fortran).

'.PGID' : S V I4 (long=nb_no)

Ce vecteur contient la liste des processeurs inclus dans le joint. Attention, dans ce vecteur, les identifiants des processeurs sont locaux au sous-communicateur MPI présent dans l’objet GCOM.

'.GCOM' : S V I (long=1)

Ce vecteur contient l’identifiant du sous-communicateur MPI de l’objet Joints.

Le découpage de maillage#

Il existe deux possibilités pour produire un ParallelMesh :

  • Soit on relit directement des fichiers MED déjà découpés et contenant les informations indispensables à la création du ParallelMesh (raccords, …).

  • Soit on relit un fichier MED séquentiel contenant tout le maillage et on le découpe au démarrage de code_aster dans la commande [LIRE_MAILLAGE].

Ici, on ne traitera que du cas du maillage séquentiel relu et découpé au démarrage du calcul.

Principe de fonctionnement#

L’objet de base pour partitionner un maillage est MeshBalancer, il produit toujours un ParallelMesh. Il accepte trois types de maillage en entrée :

  • Objet Mesh (maillage séquentiel). Dans ce cas, on répartit un maillage relu sur tous les processeurs à partir d’un maillage relu sur le processeur 0.

  • Objet ParallelMesh. Ce cas est utile pour le rééquilibrage de charge en cours de calcul.

  • Objet IncompleteMesh. C’est le cas le plus courant. Un maillage incomplet (non utilisable pour un calcul) est partitionné pour produire un ParallelMesh.

L’objet IncompleteMesh#

Principe#

L’objet IncompleteMesh est un maillage qui est découpé sans porter d’attention à la connectivité.

Lorsqu’on relit sur P processeurs un fichier maillage MED contenant N nœuds, le processeur 0 va lire les nœuds (coordonnées, famille MED, etc.) de 1 à N/P (en supposant que N est divisible par P), le processeur 2 va lire les nœuds de (N/P)+1 à 2*N/P, etc.

Pour les mailles, ce sera la même chose. Si on a M mailles de type SEG2, le processeur 0 lira les mailles de type SEG2 de 1 à M/P, le processeur 2 va lire les mailles de type SEG2 de de (M/P)+1 à 2*M/P, etc.

Mais ici, rien ne dit que la maille 1 aura tous ces nœuds sur le processeur 0.

../../../../_images/04-IncompleteMesh.svg

Fig. 780 Maillage complet (en orange les numéros des nœuds et en vert les numéros des mailles)#

Si on a le maillage sur la figure Fig. 780 et qu’on veut le relire dans un IncompleteMesh sur 2 processeurs, on aura le découpage de la figure Fig. 781

../../../../_images/05-IncompleteMeshDecoupe.svg

Fig. 781 Maillage incomplet (IncompleteMesh)#

Contenu informatique de l’objet#

'.NUNOLG' : S V I (long=nb_no)

C’est un vecteur dimensionné au nombre de nœuds locaux et indiquant le numéro global des nœuds. Ces numéros globaux sont issus d’un fichier MED si cette information est présente. Sinon, les premiers numéros globaux sont attribués au processeur 0 (dans l’ordre de ses nœuds) ensuite les suivants sont attribués au processeur 1, etc. Les numéros globaux commencent à 0 (convention du langage C) dans ce vecteur.

L’objet IncompleteMesh stocke aussi les familles MED et les numéros du premier et du dernier nœud, ainsi que la première et de la dernière maille possédés par le processeur courant.

Le découpeur de maillage : MeshBalancer#

Données d’entrée#

Trois choses sont à fournir au découpeur :

  • Le maillage à découper (Mesh, ParallelMesh ou IncompleteMesh).

  • Le nouveau découpage : pour chaque processeur, le numéro global (en convention Fortran) des nœuds qui seront possédés localement après découpage.

  • Le nombre de couches de mailles fantômes désiré (facultatif, par défaut, ce nombre vaut 1).

L’objet MeshBalancer met à disposition les objets ObjectBalancer (pour les nœuds et les mailles) afin qu’ils puissent être utilisés pour l’équilibrage de champs (que ce soit des champs issus d’un fichier MED ou de champs simples issus de code_aster pour un rééquilibrage).

Détails d’implémentation#

La méthode principale du MeshBalancer est applyBalancingStrategy. C’est elle qui prend le partitionnement déterminé par un équilibreur de charge (par exemple PtScotch).

Il faut commencer par construire les objets ObjectBalancer pour les nœuds (_nodesBalancer), pour les mailles (_cellsBalancer) ainsi que les interfaces. C’est la méthode buildBalancersAndInterfaces qui s’en occupe.

Processeur après processeur, on enrichit ces objets ObjectsBalancer en déterminant l’environnement de chaque nœud possédé dans la méthode findNodesAndElementsInNodesNeighborhood. À partir de ces informations, on peut remplir les ObjectsBalancer avec les entités (nœuds et mailles) à garder, à supprimer ou à envoyer sur un autre processeur.

C’est la méthode _enrichBalancers qui effectue la boucle sur la profondeur de mailles fantômes demandée par l’utilisateur.

Ensuite, on détermine les interfaces pour remplir l’objet Joints du ParallelMesh. Pour terminer, on équilibre les groupes de nœuds et de mailles (dans le cas des ParallelMesh ou des Mesh) ou les familles MED (dans le cas des IncompleteMesh).

L’objet ObjectsBalancer#

Principe#

Cet objet est l’objet de base de la répartition de données entre processeurs. Il faut lui définir les entités à envoyer (et à qui), à supprimer ou à conserver sur un processeur donné. Il y a trois fonctions de base : addElementarySend, setElementsToKeep et setElementsToDelete

À partir de ces fonctions, l’objet ObjectsBalancer pourra produire un objet réparti en respectant les contraintes.

Utilisation#

On définit ce qu’il faut garder, envoyer ou supprimer. Ensuite, il faut appeler endElementarySendDefinition pour interdire de modifier l’objet ObjectsBalancer, puis appeler prepareCommunications pour préparer le graphe de communication.

Pour répartir les données, on utilise une des méthodes nommées balance*OverProcesses*. Les « objets » qu’il est possible de répartir sont :

  • Les coordonénes des nœuds dans MeshCoordinatesField

  • Des vecteurs simples (std::vector, JeveuxVector) avec une ou plusieurs composantes (à définir à la compilation)

  • Des collections JeveuxCollection

On peut aussi définir une renumérotation qui est utilisée par les méthodes balance*OverProcesses*WithRenumbering.

Modélisation en parallélisme distribué#

Le parallélisme distribué n’a pas d’impact sur l’objet Model (produit par la commande [AFFE_MODELE]). Les calculs élémentaires sont réalisés sur tout le ParallelMesh (y compris les mailles fantômes). Le parallélisme distribué n’a pas d’impact non plus sur l’objet DirichletBC (produit par la commande [AFFE_CHAR_CINE]).

L’impact le plus fort du parallélisme distribué porte sur les charges dualisées (commande [AFFE_CHAR_MECA] par exemple) et sur le contact (commande [DEFI_CONT] mais pas [DEFI_CONTACT] ).

Chargements dualisés#

Principe de fonctionnement#

Le but de la commande [AFFE_CHAR_MECA] est de produire des cartes qui permettront à la routine calcul.F90 de calculer les contributions des chargements.

Pour produire ces cartes dans un contexte ParallelMesh, il va manquer des informations notamment lorsqu’on cherche à définir une relation linéaire entre degrés de liberté portés par différents sous-domaines.

Pour obtenir ces informations (par exemple : les coordonnées des nœuds impliqués dans la relation linéaire), on commence par créer un ojbet ConnectionMesh (un maillage contenant toutes les entités impliquées dans une relation linéaire). C’est sur ce ConnectionMesh que sont calculées les cartes issues de [AFFE_CHAR_MECA] (production d’un MechanicalLoad portant sur le ConnectionMesh et donc identique sur tous les processeurs).

Après l’obtention de ces cartes (qui sont définies sur le ConnectionMesh) pour qu’elles soient utilisables par calcul.F90, on projete ces cartes sur le ParallelMesh initial. C’est l’objet ParallelMechanicalLoad qui réalise cette opération en créant les cartes et le ``ParallelFiniteElementDescriptor``

L’objet ConnectionMesh#

L’objet ConnectionMesh est l’objet de base qui permet de prendre en compte les relations linéaires entre degrés de liberté portés par différents sous-domaines.

Le ConnectionMesh est un maillage contenant tous les nœuds impliqués dans la ou les relations linéaires entre degrés de liberté ainsi que toutes les mailles reliées à ces nœuds.

Ainsi dans le cas du maillage suivant avec une relation linéaire donnée sur la figure d0-05-01-fig-ParallelMechanicalLoad

../../../../_images/06-ParallelMechanicalLoad.svg

Fig. 782 Charge dualisée avec les multiplicateurs de Lagrange#

On obtiendra un ConnectionMesh ressemblant au maillage suivant sur tous les processeurs sur la figure d0-05-01-fig-ParallelMechanicalLoad

../../../../_images/07-ConnectionMesh.svg

Fig. 783 Objet ConnectionMesh obtenu de la charge dualisée#

Ce ConnectionMesh est un BaseMesh (donc imprimable au format MED). Il est créé au moment de la commande [AFFE_CHAR_MECA] et n’est pas conservé. Cet objet garde une référence vers le ParallelMesh d’origine.

Remarque:

Par contre, l’objet est conservé dans le cas du contact avec [DEFI_CONT]

Si le ConnectionMesh a \(nbno\) nœuds et \(nbma\) mailles, les objets seront les suivants :

'.NOLOCAL' : S V I (long=nbno)

C’est la numérotation locale des nœuds dans le ParallelMesh d’origine. Cet objet créé un lien entre les nœuds du ConnectionMesh et les nœuds du ParallelMesh.

'.NOGLOBAL' : S V I (long=nbno)

C’est la numérotation globale des nœuds dans le ParallelMesh d’origine.

'.NOPROPRIO' : S V I (long=nbno)

C’est le numéro du processeur qui possède le nœud dans le ParallelMesh.

'.MALOCAL' : S V I (long=nbma)

C’est la numérotation locale des mailles dans le ParallelMesh d’origine.

'.MAGLOBAL' : S V I (long=nbma)

C’est la numérotation globale des mailles dans le ParallelMesh d’origine.

L’objet ParallelFiniteElementDescriptor#

Le ParallelFiniteElementDescriptor décrit essentiellement les mailles tardives qui ont été ajoutées pour représenter la relation linéaire. Chaque ParallelFiniteElementDescriptor contient des mailles qui relient des nœuds physiques du ParallelMesh à des nœuds tardifs. Ces nœuds tardifs sont présents sur tous les processeurs qui en ont besoin.

../../../../_images/08-ParallelFiniteElementDescriptor.svg

Fig. 784 Noeuds et mailles tardives dans les ParallelFiniteElementDescriptor des processeurs 0 et 1#

Cet objet hérite du FiniteElementDescriptor et y ajoute certains éléments. Si on note \(nbnoT\) le nombre de nœuds tardifs sur le processeur courant.

'.PNOE' : S V I (long=nbnoT)

Numéro du processeur qui possède le nœud tardif.

'.MULT' : S V I (long=nbnoT)

Multiplicité interne du nœud tardif (combien de fois ce nœud est utilisé sur le processeur courant).

'.MUL2' : S V I (long=nbnoT)

Multiplicité externe du nœud tardif (combien de fois ce nœud est utilisé sur les autres processeurs).

'.NULG' : S V I (long=nbnoT)

Numérotation globale des nœuds tardifs (utilisé pour créer la numérotation).

L’objet ParallelMechanicalLoad#

L’objet ParallelMechanicalLoad se construit à partir d’un MechanicalLoad (portant sur un ConnectionMesh) et du modèle portant sur le ParallelMesh ayant servi à construire le ConnectionMesh.

Son rôle est de transférer les cartes du MechanicalLoad (définies sur le ConnectionMesh) vers le ParallelMesh. Ainsi, chaque processeur aura un ParallelMechanicalLoad différent.

Cet objet conserve un lien vers le modèle et il produit un objet ParallelFiniteElementDescriptor. Il contient en plus les objets Jeveux suivant.

'.CHME.CIMPO' : K19

Second membre des équations de conditions aux limites cinématiques.

'.CHME.CMULT' : K19

Coefficients des équations de conditions aux limites cinématiques.

'.TYPE' : S V K8 (long=1)

Type de la charge.

'.MODEL.NOMO' : S V K8 (long=1)

Nom du modèle.

Modélisation du contact#

Principes#

Le cas du contact est particulier car il a besoin de deux LIGREL (FiniteElementDescriptor) :

  • Le premier sert à définir tous les degrés de liberté qui seront présents dans le calcul.

  • Le deuxième sert à définir les relations détectées entre ces degrés de liberté pendant la phase d’appariement.

Le problème essentiel du contact en parallèle réside dans le fait qu’avec un ParallelMesh, il est fort probable qu’un processeur impliqué dans le contact ne dispose pas de tous les nœuds lui permettant de calculer ce contact. Donc, on créé un ConnectionMesh qui sera la référence pour les calculs de contact (appariement).

Pour créer tous les degrés de liberté du problème, on passe par la création de deux ParallelContactFEDescriptor (correspondant aux deux FiniteElementDescriptor du contact). Ces objets sont des FiniteElementDescriptor qui servent à créer la numérotation. Pour créer les degrés de liberté, comme les nœuds impliqués dans le contact sont potentiellement répartis sur plusieurs processeurs, on doit créer des nœuds tardifs ( pour des nœuds qui sont en réalité physiques puisque ce sont des nœuds du maillage, mais présents sur d’autres processeurs ). Il faut néanmoins avoir conscience que ces nœuds tardifs (qui vont engendrer des degrés de liberté degrés de liberté tardifs) sont en fait les mêmes mais dans deux FiniteElementDescriptor différents. Il faut donc garder la correspondance entre les nœuds tardifs des deux FiniteElementDescriptor et le ConnectionMesh afin d’être en mesure au moment de la création de la numérotation de ne pas créer en double des degrés de liberté qui portent en réalité sur les mêmes nœuds physiques du contact.

C’est la raison pour laquelle les objets .REFP et .CRCO d’un nume_equa ont été ajoutés. Le .REFP permet de noter dans le deuxième FiniteElementDescriptor de contact le numéro du premier FiniteElementDescriptor de contact (qui sert de référence puisque c’est lui qui décrit tous les degrés de liberté de contact). Et le « .CRCO » permet de dire à quel nœud tardif du premier FiniteElementDescriptor correspond un nœud tardif du premier.

Considérons la zone de contact courante sur la figure Fig. 785

../../../../_images/09-ContactZone.svg

Fig. 785 Zone de contact#

Dans le cas séquentiel, après appariement, on va construire une maille tardive qui repose uniquement sur des nœuds physiques sur la figure Fig. 786

../../../../_images/10-ContactZoneWithLateElement.svg

Fig. 786 Zone de contact avec une maille tardive en séquentiel reposant sur des nœuds physiques#

Dans le cas parallèle, après appariement, la maille tardive repose sur des nœuds physiques et tardifs comme syr la figure Fig. 787

../../../../_images/11-SplitedContactZone.svg

Fig. 787 Zone de contact avec une maille tardive en parallèle reposant sur des nœuds physiques et tardifs#

Remarque

Ce problème de FiniteElementDescriptor de référence ne se posait pas dans le cas du contact séquentiel. En effet, les mailles tardives de contact sont de mailles qui ne contiennent que des nœuds physiques. De ce fait, le FiniteElementDescriptor de référence de ces nœuds est donc forcément le FiniteElementDescriptor du modèle (en d’autres termes, celui du maillage). Dans notre cas, on passe par des nœuds tardifs, on ne se réfère plus au FiniteElementDescriptor de modèle. Il faut donc définir un FiniteElementDescriptor de référence pour ne pas dédoubler les degrés de liberté.

L’objet ParallelContactFEDescriptor#

Cete objet hérite de FiniteElementDescriptor. Il y a deux constructeurs pour instancier l’objet parce qu’il y a deux FiniteElementDescriptor différents à constuire. Chacun des constructeurs répond à un besoin différent :

  • Soit créer tous les degrés de liberté de contact.

  • Soit créer les liens entre degrés de liberté de contact (issus de l’appariement et permettant de transférer les contributions venant des calculs élémentaires de contact vers la matrice assemblée).

Si l’objet contient \(nbnoT\) nœuds tardifs, en plus de l’objet Joints contenant les raccords et le communicateur MPI à utiliser, on a les objets suivants:

'.PNOE' : S V I (long=nbnoT)

Numéro du processeur qui possède le nœud tardif.

'.MULT' : S V I (long=nbnoT)

Présent pour des raisons de compatibilité avec l’objet ParallelFiniteElementDescriptor. Ne sert à rien pour le contact.

'.MUL2' : S V I (long=nbnoT)

Présent pour des raisons de compatibilité avec l’objet ParallelFiniteElementDescriptor. Ne sert à rien pour le contact.

'.NULG' : S V I (long=nbnoT)

Numérotation globale des nœuds tardifs (utilisé pour créer la numérotation).

'.LOGL' : S V I (long=nbnoT)

Numérotation globale des nœuds tardifs dans le FiniteElementDescriptor.

'.GLLO' : S V I (long=nbnoT)

Correspondance globale vers locale des nœuds tardifs dans le FiniteElementDescriptor.

Ces deux derniers tableaux sont indispensables pour passer du deuxième FiniteElementDescriptor de contact vers le permier FiniteElementDescriptor de référence. Au moment de la création de la numérotation, on passe dans le deuxième FiniteElementDescriptor en numérotation globale de nœuds tardifs et on retrouve le nœud correspondant dans le premier FiniteElementDescriptor en repassant en numérotation locale. Cela fonctionne car les deux FiniteElementDescriptor ont le même objet ConnectionMesh sous-jacent.

Numérotation#

Création#

La création de la numérotation des degrés de liberté se fait dans l’optique de l’usage de PETSc. On fait donc en sorte que chaque processeur possède un bloc contigu de degrés de liberté (au sens de la numérotation des degrés de liberté).

Chaque processeur possède une liste de nœuds (physiques et tardifs) dont il est le propriétaire donc tous les degrés de liberté portés par des nœuds d’un même processeur seront numérotés les uns à la suite des autres. Le processeur 0 aura tous ses degrés de liberté numérotés de 0 à N, le processeur 1 de N+1 à M, etc. C’est la première phase, chaque processeur numérote les degrés de liberté portés par les nœuds dont il est propriétaire.

La deuxième phase consiste à communiquer les numéros de degrés de liberté entre processeurs pour le nœuds se trouvant dans la couche de mailles fantômes. Pour éviter les deadlocks, toutes les communications des raccords sont réalisées (même si un processeur n’a aucune information à transmettre, ce qui peut arriver).

Dans cette phase de communication, on utilise des communicateurs MPI spécifiques :

  • Pour les nœuds physiques, soit on utilise comm_world si des Joints non vides se trouvent sur tous les processeurs du calcul et sinon, un communicateur restreint sera utilisé.

  • Pour les nœuds tardifs, on utilise toujours un communicateur local adapté au chargement. Ce communicateur représente les processeurs ayant des nœuds impliqués dans le chargement.

Ces étapes de création de la numérotation sont réalisées dans les routines crnulg, crnlgn et crnlgc.

Ces routines crééent des objets Joints pour le nume_ddl produit. Ces objets seront utilisés pour la mise à jour des champs aux nœuds sur les nœuds non possédés par le processeur courant.

L’objet ParallelDOFNumbering#

Cet objet hérite de BaseDOFNumbering.

'.NUME' : K19

Objet nume_equa parallèle.

L’objet ParallelEquationNumbering#

Cet objet hérite de EquationNumbering auquel il ajoute les objets suivants, si on note \(nbddl\) le nombre de degrés de liberté locaux :

'.PDDL' : S V I (long=nbddl)

Vecteur précisant les possesseurs des degrés de liberté.

'.NULG' : S V I (long=nbddl)

Numérotation globale des degrés de liberté. C’est le vecteur qui permet de recréer le problème global.

Quelques utilitaires#

La routine create_graph_comm#

La routine create_graph_comm permet de créer des structures de données permettant de parcourir les couplages maximaux du graphe de communication.

Ses arguments en entrée sont :

  • Une chaîne de caractère de la structure de données contenant le graphe pour lequel il faut rechercher les couplages (objet Joints).

  • Le type de la structure de données contenant l’objet Joints (MAILLAGE_P, NUME_EQUA ou LIGREL).

En sortie, on obtient :

  • Le nombre \(nb_comm\) de couplages maximaux

  • Pour chaque processeur, un vecteur jeveux dimensionné au nombre de couplage maximaux et dans lequel on trouve pour chaque couplage le processeur en relation avec le processeur courant. Si la valeur est -1, cela veut dire que le processeur courant ne participe pas au couplage en question.

  • Pour chaque processeur, un vecteur jeveux dimensionné au nombre de couplage maximaux et dans lequel on trouve pour chaque couplage le tag MPI à utiliser.

Par exemple, considérons l’exemple du graphe de la figure Fig. 788

../../../../_images/12-Graph.svg

Fig. 788 Exemple de graphe de communication MPI#

alors, on obtient les couplages maximaux de la figure Fig. 789

../../../../_images/13-Matchings.svg

Fig. 789 Couplages maximaux#

On voit que dans le dernier couplage, les processeurs 1 et 2 ne participent pas.

Mise à jour d’un champ nodal#

L’utilitaire Fortran qui permet de mettre à jour les nœuds fantômes (non possédés par le processeur courant) est la routine vector_update_ghost_values. Ses arguments d’entrée sont :

  • Un vecteur de R8 (cohérent avec le deuxième argument qui est un nume_equa)

  • Un objet nume_equa parallèle

  • « SEND » ou « BIDIR ». Dans le premier cas, c’est uniquement le processeur possesseur d’un degré de liberté qui envoie ses valeurs aux autres processeurs. Dans le second cas, c’est bidirectionnel, cela correspond donc à une somme sur les degrés de liberté partagés.

Enrichissement de la numérotation#

C’est la routine crnulg qui est chargée à partir d’un nume_ddl de rajouter les informations indispensables à la réalisation d’un calcul parallèle :

  • Numérotation globale des degrés de liberté.

  • Possession des degrés de liberté.

  • Raccords (objet Joints).