Aspect informatique
Avant de détailler les objets un par un, ce chapitre donne la vue informatique de pyrucast : comment le code est organisé, comment les objets vivent en mémoire, et les quelques motifs transverses qu’on retrouve partout. Chaque section renvoie au chapitre qui en donne le détail.
Deux langages, un seul cœur
pyrucast est une librairie Rust exposée à Python.
- Le cœur de calcul (structures de données, maillage, champs, assemblage, solveur) est écrit en Rust : typage fort, pas de ramasse-miettes runtime, performances natives.
- Le binding Python est une fine couche
pyo3, compilée parmaturinen un module d’extension.so/.pyd. Chaque classe Python (pyrucast.Coords,pyrucast.Mesh, …) enveloppe un type Rust ; chaque fonction de module (pyrucast.matrix.stiffness,pyrucast.solver.solve, …) appelle une fonction Rust.
Le binding est un miroir 1:1 du Rust, sans logique propre : une méthode Rust reste une méthode, une fonction libre reste une fonction. La table de correspondance exhaustive est dans Correspondance Rust ↔ Python ; la règle qui décide méthode vs fonction libre est dans Conventions.
Script Python ──► module pyrucast (.so, pyo3) ──► crate Rust pyrucast
| | |
pyrucast.Coords wrapper PyCoords struct Coords
pyrucast.matrix.stiffness fonction de module ops::matrix::stiffness
Les objets se désignent par des handles
Les objets ne se relient pas entre eux par des références Rust directes, mais
par un Handle<T> : une référence comptée munie de son propre verrou
(Arc<RwLock<T>>). Pas de registre à interroger, pas de session à passer — le
handle est l’adresse de l’objet.
Trois propriétés en découlent, et elles structurent tout le reste de la librairie :
- Libération automatique.
Clonepartage,Droprelâche. Quand le dernier handle d’un objet disparaît, l’objet est détruit — aucune fonctionremove()à appeler. - Toujours valide. Détenir un handle maintient l’objet en vie : il n’y a
pas de référence périmée, et
read/writene peuvent pas échouer. - L’identité, c’est le pointeur.
same_objectdit si deux références désignent le même objet — la base de l’union des agrégats.
L’accès passe par un guard (read / write) qui verrouille ce seul objet
le temps de l’opération (RAII). Le détail — guards possédés, granularité du
verrouillage, compteur par nœud — est dans
Modèle mémoire.
Refcount à deux niveaux
La gestion de durée de vie opère à deux échelles indépendantes :
- au niveau objet : le
Handle<T>décide si un objet entier (unCoords, unSubMesh…) est vivant ; - au niveau interne : à l’intérieur d’un
Coords, un second compteur par nœud décide si tel nœud est vivant. Le ramasse-miettes manuelCoords::gc()opère sur ce niveau-là.
C’est pourquoi un nœud reste protégé tant qu’un maillage ou un champ le
référence, même si tous les Node utilisateurs ont disparu. Voir
Coordonnées et Nœud.
Le motif agrégat / sous-objet
La plupart des conteneurs viennent par paires : un objet zone
(Sub…) et son agrégat (une liste de zones partageant la même grammaire
d’accès) :
| Zone | Agrégat |
|---|---|
SubMesh | Mesh |
SubFiniteElementSpace | FiniteElementSpace |
SubNodeField | NodeField |
SubElementField | ElementField |
SubModel | Model |
SubMatrix | Matrix |
SubEvolution | Evolution |
Tous les agrégats exposent la même interface (len, [i], itération,
unit()) et la même composition par union | (côté Rust : union). Les
sous-objets ne se construisent pas directement : on construit au niveau
parent, et on indexe (parent[i]) pour obtenir une vue sur une zone. Ce
motif est factorisé dans le trait Aggregate — voir Agrégat.
Union
|, pas+. Composer deux zones, c’est l’union (mesh_a | mesh_b), avec partage des sous-objets (refcount) et déduplication par handle. L’opérateur+est réservé à l’arithmétique des champs (cf. Champ).
Trois niveaux d’affichage
Chaque objet implémente trois vues, du plus court au plus complet :
__str__(RustDisplay) — résumé une ligne, façon listing cast3m ;__repr__(RustDebug) — vue structurelle bornée, pour le développement ;dump()— contenu intégral (valeurs, topologie) imprimé sur la sortie standard, au-delà de ce quereprmontre.
Détail dans Conventions.
Erreurs
Toute l’API publique renvoie Result<T, PyrucastError>. Côté Python,
PyrucastError est converti automatiquement en RuntimeError. Il n’y a qu’un
seul type d’erreur dans la librairie — voir Conventions.
Persistance portable
Un trait unique, Portable (serde + bincode), fixe le contrat d’octets :
un format binaire identique Linux ↔ Windows. Au-dessus, pyrucast.save /
pyrucast.load écrivent un graphe d’objets et le relisent en préservant
le partage — deux champs sur un support restent deux champs sur un support.
Voir Sauvegarde et relecture, Conventions
et Modèle mémoire.
Pour le développeur
L’organisation des fichiers Rust (où vit chaque morceau) est décrite dans Arborescence.