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

Correspondance Rust ↔ Python

Cette page liste, par module, les structures (exposées en classes Python) et les fonctions libres (exposées en fonctions de module Python). Elle matérialise la règle de Conventions :

  • une structure containers::…::Foo est exposée sous le même nom pyrucast.Foo (le wrapper PyO3 interne PyFoo est masqué) ;
  • une fonction libre ops::<module>::f est exposée dans le sous-module de même nom : pyrucast.<module>.f. Le module Rust porte le nom du conteneur produit (mesh, node_field, element_field, matrix, model, coords), ou de l’activité quand il ne produit aucun conteneur (measure, geom, export) ; solver est l’exception nommée. Le miroir est sans exception : aucune fonction libre ne vit au top-level ;
  • une surcharge d’opérateur Rust devient un dunder Python (Add → __add__, Index → __getitem__, …) ;
  • un constructeur nommé Rust devient un classmethod / constructeur Python.

Les tableaux ci-dessous donnent la forme canonique — la fonction libre. Beaucoup de ces opérations sont aussi exposées comme méthode de leur sujet, selon la règle des trois conditions (Conventions) : premier argument sujet, retour conteneur, sens pour toute instance du type. La règle étant mécanique, la liste n’est pas recopiée ici — elle est vérifiée par un test (tests/python/test_method_exposure.py), qui lit le stub et échoue si une opération éligible perd sa méthode. Ce test porte aussi la liste des exclusions, chacune avec sa raison.

Deux points à connaître, illustrés plus bas : le nom peut changer entre les deux formes (matrix.stiffness(model, mats) / model.stiffness_matrix(mats), element_field.sub_material_field(sub, …) / sub_model.material_field(…)), et les cinématiques (deformation, beam_deformation, shell_deformation, thermal_strain) n’ont pas de méthode : elles exigent des composantes nommées, elles n’auraient pas de sens sur un champ quelconque. Même raison pour internal_forces, qui lit la contrainte de Voigt par nom.

filter_components et rename_component n’ont que la forme méthode (f.filter_components(["u_x"]), f.rename_component("U", "DX")) : un seul conteneur, de petits arguments, une vue dérivée — R1 en fait du vocabulaire du champ, pas un opérateur.

La complétude du miroir est elle aussi vérifiée par un test (tests/python/test_mirror_completeness.py), dans les deux sens : aucun opérateur Rust sans binding Python, et aucune fonction Python sans opérateur Rust. Les dérogations y vivent avec leur raison.

Source de vérité : le #[pymodule] de src/lib.rs (enregistrement des classes et fonctions) et le stub python/pyrucast/_pyrucast/__init__.pyi (signatures typées). Cette page en est un instantané, à régénérer à la main si l’API bouge.

Structures ↔ classes

Le nom de la classe Python est identique au nom de la structure Rust.

Module RustStructure RustClasse PythonChapitre
coordsCoordspyrucast.CoordsCoords
atoms::nodeNodepyrucast.NodeNœud
containers::meshSubMeshpyrucast.SubMesh (vue, via mesh[i])Maillage
containers::meshMeshpyrucast.MeshMaillage
atoms::cellCellpyrucast.CellMaillage
containers::finite_element_spaceSubFiniteElementSpacepyrucast.SubFiniteElementSpaceEspace EF
containers::finite_element_spaceFiniteElementSpacepyrucast.FiniteElementSpaceEspace EF
atoms::elementElementpyrucast.ElementEspace EF
containers::node_fieldSubNodeFieldpyrucast.SubNodeField (vue, via node_field[i])Champ aux nœuds
containers::node_fieldNodeFieldpyrucast.NodeFieldChamp aux nœuds
containers::element_fieldSubElementFieldpyrucast.SubElementField (vue, via element_field[i])Champ aux points de Gauss
containers::element_fieldElementFieldpyrucast.ElementFieldChamp aux points de Gauss
containers::matrixSubMatrixpyrucast.SubMatrix (vue, via matrix[i])Matrice creuse
containers::matrixMatrixpyrucast.MatrixMatrice creuse
containers::modelSubModelpyrucast.SubModel (vue, via model[i])Modèle physique
containers::modelModelpyrucast.ModelModèle physique
containers::evolutionSubEvolutionpyrucast.SubEvolution (constructible — voir ci-dessous)Évolution
containers::evolutionEvolutionpyrucast.EvolutionÉvolution

Quelques types Rust ne sont pas exposés en classes Python : ce sont des détails d’implémentation (SubModelKind, l’énum des physiques sous SubModel ; DofOrdering, l’ordonnancement des DOFs d’une SubMatrix ; SubValue, OutOfRange, ValueKind et Interpolated, internes à l’Evolution — une valeur tabulée se passe directement en scalaire ou en champ, la politique hors-plage en chaîne "error"/"clamp"/"extrapolate").

Les sous-objets Sub* (SubMesh, SubFiniteElementSpace, SubElementField, SubMatrix, SubModel) ne se construisent pas directement côté Python : ce sont des vues obtenues par indexation de leur parent (parent[i]). On construit toujours au niveau parent — Mesh(coords, type), FiniteElementSpace(mesh), ElementField(fes, comps), Matrix.block(...) — ou par l’opérateur qui rend ce parent (model.heat_conduction(fes)), et on compose plusieurs zones avec | (union — Rust : union). Voir la règle « Agrégats : un ou plusieurs » de CONVENTIONS.md.

Exception : SubEvolution. Seul sous-objet à la fois vue (via evolution[i]) et constructible directement — SubEvolution([(t0, v0), (t1, v1), …]) — car une courbe tabulée n’a pas de « parent » géométrique qui la définirait. On compose ensuite les courbes par zone avec | (cf. Évolution).

Fonctions ↔ fonctions

Toutes les fonctions ops prennent leurs conteneurs par référence et renvoient Result<T> (converti en exception Python RuntimeError). Les signatures ci-dessous omettent le & et le Result pour la lisibilité.

ops::mesh — construction et transformation de maillages

Rust (ops::mesh::…)Python (pyrucast.mesh.…)
from_live_nodes(coords: Handle<Coords>) -> Meshfrom_live_nodes(coords) -> Mesh
poi1_from_nodes(nodes: &[Node]) -> Meshpoi1_from_nodes(nodes) -> Mesh
line(a: &Node, b: &Node, n_elems: usize, element_type: ElementType) -> Meshline(a, b, n_elems, element_type="SEG2") -> Mesh
circle(center: &Node, normal: &[f64], radius: f64, n_elems: usize, element_type: ElementType) -> Meshcircle(center, normal, radius, n_elems, element_type="SEG2") -> Mesh
arc(node_a: &Node, center: &Node, node_b: &Node, n_elems: usize, element_type: ElementType) -> Mesharc(a, center, b, n_elems, element_type="SEG2") -> Mesh
sweep(mesh_a: &Mesh, mesh_b: &Mesh, n_layers: usize, element_type: ElementType) -> Meshsweep(mesh_a, mesh_b, n_layers, element_type="QUA4") -> Mesh
transfinite(side1: &Mesh, side2: &Mesh, side3: &Mesh, side4: &Mesh, element_type: ElementType) -> Meshtransfinite(side1, side2, side3, side4, element_type="QUA4") -> Mesh
sweep_solid(mesh_a: &Mesh, mesh_b: &Mesh, n_layers: usize) -> Meshsweep_solid(mesh_a, mesh_b, n_layers) -> Mesh
extrude(mesh: &Mesh, direction: &[f64], n_layers: usize) -> Meshextrude(mesh, direction, n_layers) -> Mesh
revolve(mesh: &Mesh, angle: f64, n_layers: usize, center: &[f64], axis: Option<&[f64]>) -> Meshrevolve(mesh, angle, n_layers, center, axis=None) -> Mesh
to_quadratic(mesh: &Mesh) -> Meshto_quadratic(mesh) -> Mesh
convert(mesh: &Mesh, element_type: ElementType) -> Meshconvert(mesh, element_type) -> Mesh
copy(mesh: &Mesh, new_nodes: bool) -> Meshcopy(mesh, new_nodes=True) -> Mesh
translate(mesh: &Mesh, vector: &[f64]) -> Meshtranslate(mesh, vector) -> Mesh
rotate(mesh: &Mesh, angle: f64, center: &[f64], axis: Option<&[f64]>) -> Meshrotate(mesh, angle, center, axis=None) -> Mesh
symmetry_point(mesh: &Mesh, center: &[f64]) -> Meshsymmetry_point(mesh, center) -> Mesh
symmetry_line(mesh: &Mesh, a: &[f64], b: &[f64]) -> Meshsymmetry_line(mesh, a, b) -> Mesh
symmetry_plane(mesh: &Mesh, a: &[f64], b: &[f64], c: &[f64]) -> Meshsymmetry_plane(mesh, a, b, c) -> Mesh
triangulate_surface(contour: &Mesh, et: ElementType, size: Option<f64>) -> Meshtriangulate_surface(contour, element_type, size=None) -> Mesh
pave_surface(contour: &Mesh, element_type: ElementType, size: Option<f64>, all_quad: bool) -> Meshpave_surface(contour, element_type, size=None, all_quad=False) -> Mesh
triangulate_volume(envelope: &Mesh, size: Option<f64>, allow_surface_nodes: bool) -> Meshtriangulate_volume(envelope, size=None, allow_surface_nodes=False) -> Mesh
pave_volume(envelope: &Mesh, layers: usize, thickness: Option<f64>, size: Option<f64>) -> Meshpave_volume(envelope, layers=1, thickness=None, size=None) -> Mesh
border(mesh: &Mesh, angle_deg: Option<f64>) -> Meshborder(mesh, angle_deg=None) -> Mesh
skin(mesh: &Mesh, angle_deg: Option<f64>) -> Meshskin(mesh, angle_deg=None) -> Mesh
orient(mesh: &Mesh) -> Meshorient(mesh) -> Mesh
invert(mesh: &Mesh) -> Meshinvert(mesh) -> Mesh
chain(mesh: &Mesh) -> Meshchain(mesh) -> Mesh
barycenter(mesh: &Mesh) -> Meshbarycenter(mesh) -> Mesh
to_poi1(mesh: &Mesh) -> Meshto_poi1(mesh) -> Mesh
elements_on(mesh: &Mesh, points: &Mesh, strict: bool) -> Meshelements_on(mesh, points, strict=True) -> Mesh
points_in_sphere(mesh: &Mesh, center: &[f64], radius: f64, tol: Option<f64>) -> Meshpoints_in_sphere(mesh, center, radius, tol=None) -> Mesh
points_on_sphere(mesh: &Mesh, center: &[f64], radius: f64, tol: Option<f64>) -> Meshpoints_on_sphere(mesh, center, radius, tol=None) -> Mesh
points_on_plane(mesh: &Mesh, origin: &[f64], normal: &[f64], tol: Option<f64>) -> Meshpoints_on_plane(mesh, origin, normal, tol=None) -> Mesh
points_below_plane(mesh: &Mesh, origin: &[f64], normal: &[f64], tol: Option<f64>) -> Meshpoints_below_plane(mesh, origin, normal, tol=None) -> Mesh
points_on_line(mesh: &Mesh, a: &[f64], b: &[f64], tol: Option<f64>) -> Meshpoints_on_line(mesh, a, b, tol=None) -> Mesh
points_in_cylinder(mesh: &Mesh, base: &[f64], top: &[f64], radius: f64, tol: Option<f64>) -> Meshpoints_in_cylinder(mesh, base, top, radius, tol=None) -> Mesh
points_on_cylinder(mesh: &Mesh, base: &[f64], top: &[f64], radius: f64, tol: Option<f64>) -> Meshpoints_on_cylinder(mesh, base, top, radius, tol=None) -> Mesh
points_in_cone(mesh: &Mesh, base: &[f64], top: &[f64], base_radius: f64, top_radius: f64, tol: Option<f64>) -> Meshpoints_in_cone(mesh, base, top, base_radius, top_radius=0.0, tol=None) -> Mesh
points_on_cone(mesh: &Mesh, base: &[f64], top: &[f64], base_radius: f64, top_radius: f64, tol: Option<f64>) -> Meshpoints_on_cone(mesh, base, top, base_radius, top_radius=0.0, tol=None) -> Mesh
points_in_torus(mesh: &Mesh, center: &[f64], axis: &[f64], major_radius: f64, minor_radius: f64, tol: Option<f64>) -> Meshpoints_in_torus(mesh, center, axis, major_radius, minor_radius, tol=None) -> Mesh
points_on_torus(mesh: &Mesh, center: &[f64], axis: &[f64], major_radius: f64, minor_radius: f64, tol: Option<f64>) -> Meshpoints_on_torus(mesh, center, axis, major_radius, minor_radius, tol=None) -> Mesh
merge_nodes(mesh: &Mesh, tol: f64, in_place: bool) -> Meshmerge_nodes(mesh, tol, in_place=False) -> Mesh
read_gmsh(coords: Handle<Coords>, path: &Path) -> Vec<(String, Mesh)>read_gmsh(coords, path) -> dict[str, Mesh]
read_gmsh_str(coords: Handle<Coords>, text: &str) -> Vec<(String, Mesh)>read_gmsh_str(coords, text) -> dict[str, Mesh]
from_arrays(coords: Handle<Coords>, node_tags: &[T], node_coords: &[f64], blocks: &[CellBlock<T>], node_values: &[NodeValues<T>], cell_values: &[CellValues<T>], order: NodeOrder) -> Importedfrom_arrays(coords, node_tags, node_coords, blocks, *, node_fields=(), cell_fields=(), order="pyrucast") -> (dict[str, Mesh], list[NodeField], list[ElementField])
element_type_from_gmsh(code: u32) -> ElementTypeelement_type_from_gmsh(code) -> str
gauss_to_external(et: ElementType, order: NodeOrder, ext_ref_nodes: &[f64]) -> (Vec<f64>, Vec<f64>)gauss_to_external(element_type, ref_nodes, order) -> (list, list)
match_gauss(et: ElementType, order: NodeOrder, ext_ref_nodes: &[f64], ext_xi: &[f64], ext_weights: &[f64]) -> Vec<usize>match_gauss(element_type, ref_nodes, xi, weights, order) -> list[int]
— (exige l’interpréteur)from_gmsh(coords, *, dim=-1, tag=-1, views=True) -> (dict[str, Mesh], dict)
— (exige l’interpréteur)from_medcoupling(coords, source, *, mesh_name=None) -> (dict[str, Mesh], dict)
consolidate(mesh: &Mesh) -> Meshconsolidate(mesh) -> Mesh
select_nodes(field: &NodeField, band: &Band, …) -> Mesh / select_cells(field: &ElementField, …) -> Meshselect(field, ge=None, gt=None, le=None, lt=None, components=None) -> Mesh (dispatch par type ; part d’un champ mais rend un maillage, d’où son rangement ici)

ops::node_field — opérateurs produisant un champ aux nœuds

Rust (ops::node_field::…)Python (pyrucast.node_field.…)
positions(mesh: &Mesh, components: Option<Vec<String>>) -> NodeFieldpositions(mesh, components=None) -> NodeField
divergence(field: &ElementField, prefix: &str) -> NodeFielddivergence(field, prefix) -> NodeField
restrict(field: &NodeField, mesh: &Mesh) -> NodeFieldrestrict(field, mesh) -> NodeField
restrict_like(field: &NodeField, target: &NodeField) -> NodeFieldrestrict_like(field, target) -> NodeField
merge(a: &NodeField, b: &NodeField) -> NodeFieldmerge(a, b) -> NodeField
consolidate(field: &NodeField) -> NodeFieldconsolidate(field) -> NodeField
mask(field: &NodeField, band: &Band, …) -> NodeFieldmask(field, ge=None, gt=None, le=None, lt=None, components=None) -> NodeField (champ 0/1 de même structure ; sucre field >= x). Accepte aussi un SubNodeField
internal_forces(model: &Model, state: &ElementField) -> NodeFieldinternal_forces(model, state) -> NodeField
external_forces(model: &Model, materials: &ElementField) -> NodeFieldexternal_forces(model, materials) -> NodeField

flux et internal_forces (BSIG, ∫ Bᵀ σ) sont des assemblages, mais leur résultat est un vecteur nodal et non un opérateur : on se range par la sortie.

ops::coords — écriture dans le magasin de coordonnées

Rust (ops::coords::…)Python (pyrucast.coords.…)
set(field: &NodeField, components: Option<Vec<String>>) -> ()set(field, components=None) -> None
displace(field: &NodeField, components: Option<Vec<String>>) -> ()displace(field, components=None) -> None

ops::element_field — opérateurs produisant un champ aux points de Gauss

Rust (ops::element_field::…)Python (pyrucast.element_field.…)
gradient(field: &NodeField, fespace: &FiniteElementSpace) -> ElementFieldgradient(field, fespace) -> ElementField
deformation(u: &NodeField, fespace: &FiniteElementSpace) -> ElementFielddeformation(u, fespace) -> ElementField
interp_to_gauss(field: &NodeField, fespace: &FiniteElementSpace) -> ElementFieldinterp_to_gauss(field, fespace) -> ElementField
thermal_strain(temperature: &ElementField, material: &ElementField, fespace: &FiniteElementSpace, t_ref: f64) -> ElementFieldthermal_strain(temperature, materials, fespace, t_ref) -> ElementField
shell_deformation(field: &NodeField, fespace: &FiniteElementSpace, model: ShellModel) -> ElementFieldshell_deformation(field, fespace, model) -> ElementField
beam_deformation(field: &NodeField, fespace: &FiniteElementSpace, material: &ElementField) -> ElementFieldbeam_deformation(field, fespace, material) -> ElementField (1-D, plan ou spatial selon le maillage ; le matériau est requis, l’interpolation dépendant de Φ)
consolidate(field: &ElementField) -> ElementFieldconsolidate(field) -> ElementField (fusionne les zones d’une même fespace)
mask(field: &ElementField, band: &Band, …) -> ElementFieldmask(field, ge=None, …) -> ElementField ; accepte aussi un SubElementField
sub_material_field(sub: &SubModel, pairs: &[(&str, f64)]) -> SubElementFieldsub_material_field(sub_model, components_and_values) -> SubElementField
material_field(model: &Model, pairs: &[(&str, f64)]) -> ElementFieldmaterial_field(model, components_and_values) -> ElementField
material_field_per_sub_model(model: &Model, per: &[&[(&str, f64)]]) -> ElementFieldmaterial_field_per_sub_model(model, components_and_values_per_sub_model) -> ElementField
behavior::integrate(model, deformation, prev, materials, dt) -> ElementFieldintegrate_behavior(model, deformation, materials, prev=None, dt=None) -> ElementField (COMP)

ops::measure — réductions à un nombre

Rust (ops::measure::…)Python (pyrucast.measure.…)
integral(field: &NodeField, fespace, component) -> f64 / integral_element(field: &ElementField, component) -> f64integral(field, component, fespace=None) -> float (dispatch par type ; ∫ f dΩ, fespace requis pour un NodeField)
SubField::dot(&self, other) -> f64 / Field::dot_field(&self, other) -> f64xty(x, y) -> float (dispatch par type ; produit scalaire global de deux champs)
SubField::xtx(&self) -> f64 / Field::xtx(&self) -> f64xtx(x) -> float (dispatch par type ; Σ v², norme au carré XTX)
SubField::xtx_components(&self, &[&str]) -> Result<f64>xtx(x, components=[…]) -> float (norme au carré restreinte à ces composantes)

ops::field — opérateurs génériques

Leur produit est un conteneur — toujours — mais pas un conteneur déterminé : il dépend de l’argument. La règle « un module par conteneur produit » ne désigne donc pas un module, et ils se rangent par domaine.

Rust (ops::field::…)Python (pyrucast.field.…)
psca<T: Pscal>(x: &T, y: &T) -> Tpsca(x, y) -> field (produit scalaire nœud par nœud, champ à une composante "psca"). Deux conteneurs en pairs et opération symétrique : fonction libre seule, pas de méthode
abs / sqrt / exp / log / log10 / cos / sin / tan / sinh / cosh / tanh <T: MapValues>(field: &T) -> Tmêmes noms pyrucast.field.…(field) — maths élément par élément (style numpy), un champ neuf du même type ; acceptent les quatre saveurs de champ (NodeField / SubNodeField / ElementField / SubElementField). Résultats non bornés : log de ≤ 0 → -inf/nan

La composition de zones passe par l’union (| Python / union Rust) ; l’arithmétique champ + scalaire et champ + champ par les opérateurs +, -, *, /, ** (cf. Opérateurs). Ni l’une ni l’autre n’est une fonction ops — merge(a, b) est juste un alias nommé de a | b.

ops::model — déclaration des physiques

Chaque opérateur rend un Model couvrant tout le support reçu (une zone par sous-espace) ; on compose les physiques hétérogènes avec |. Aucun n’a de forme méthode : le premier argument est le support que le modèle recouvre, pas un sujet qu’on transforme.

Rust (ops::model::…)Python (pyrucast.model.…)
heat_conduction(fes: &FiniteElementSpace) -> Modelheat_conduction(fespace, symmetry=None) -> Model
heat_conduction_with_symmetry(fes, symmetry: MaterialSymmetry) -> Modelidem, via symmetry=
fick(fes: &FiniteElementSpace, species: &str) -> Modelfick(fespace, species, symmetry=None) -> Model
fick_with_symmetry(fes, symmetry: MaterialSymmetry, species: &str) -> Modelidem, via symmetry=
radiation(fes: &FiniteElementSpace, target: &Model) -> Modelradiation(fespace, target) -> Model
flux(fes: &FiniteElementSpace, target: &Model, dual: String) -> Modelflux(fespace, target, dual) -> Model
boundary_transfer(fes, target: &Model, components: Vec<(String, String)>) -> Modelboundary_transfer(fespace, target, components) -> Model
interface_transfer(side_a, side_b, target: &Model, components, tol: f64) -> Modelinterface_transfer(side_a, side_b, target, components, tol=1e-9) -> Model — défaut interface_transfer::DEFAULT_TOL
truss(fes: &FiniteElementSpace) -> Modeltruss(fespace) -> Model
elasticity(fes, model: ElasticityModel) -> Modelelasticity(fespace, model, symmetry=None) -> Model
elasticity_with_symmetry(fes, model, symmetry: MaterialSymmetry) -> Modelidem, via symmetry=
plasticity_perfect(fes, model: ElasticityModel) -> Modelplasticity_perfect(fespace, model) -> Model
plasticity_with_law(fes, model, law: PlasticLaw) -> Modelune fonction par loi : plasticity_isotropic, drucker_prager, ottosen, creep_norton, creep_blackburn, creep_lemaitre, viscoplasticity_chaboche, viscoplasticity_lemaitre_chaboche, gurson — toutes (fespace, model) -> Model
mazars(fes, model: ElasticityModel) -> Modelmazars(fespace, model) -> Model
damage_with_law(fes, model, law: DamageLaw) -> Modelune fonction par loi : damage_tc, damage_sic_sic — (fespace, model) -> Model
bernoulli(fes: &FiniteElementSpace) -> Modelbernoulli(fespace) -> Model
timoshenko(fes: &FiniteElementSpace) -> Modeltimoshenko(fespace) -> Model
shell(fes, model: ShellModel) -> Modelshell(fespace, model) -> Model
dirichlet(target: &Model, variable: &str, imposed_mesh, multiplier_mesh, sense: RelationSense) -> Modeldirichlet(target, variable, imposed_mesh, multiplier_mesh, sense=None) -> Model
mpc(terms: Vec<MpcTerm>, multiplier_mesh, sense) -> Modelmpc(target, terms, multiplier_mesh, sense=None) -> Model
embedded(target: &Model, immersed, host, variables: Vec<String>, tol) -> Modelembedded(target, immersed, host, variables, tol=None) -> Model
contact(target: &Model, slave, master, variables: Vec<String>) -> Modelcontact(target, slave, master, variables) -> Model

Les deux plis du catalogue. Rust nomme la symétrie et la loi par une enum (MaterialSymmetry, ElasticLaw, PlasticLaw, DamageLaw) ; Python n’expose pas ces enums, et replie donc la symétrie en mot-clé symmetry= et déplie les lois en une fonction chacune. Le catalogue est le même des deux côtés — les dérogations correspondantes sont enregistrées, avec leur raison, dans tests/python/test_mirror_completeness.py.

ops::matrix — assemblage des matrices

Rust (ops::matrix::…)Python (pyrucast.matrix.…)
stiffness(model: &Model, materials: &ElementField) -> Matrixstiffness(model, materials) -> Matrix
mass(model: &Model, materials: &ElementField) -> Matrixmass(model, materials) -> Matrix
lump(m: &Matrix) -> Matrixlump(matrix) -> Matrix
geometric(model: &Model, materials: &ElementField, stress: &ElementField) -> Matrixgeometric(model, materials, stress) -> Matrix
tangent(model, materials, deformation, prev: Option<&ElementField>, dt: Option<f64>) -> Matrixtangent(model, materials, deformation, prev=None, dt=None) -> Matrix

ops::solver — résolution

Rust (ops::solver::…)Python (pyrucast.solver.…)
lu::solve(matrix: &Matrix, rhs: &NodeField) -> NodeFieldsolve(matrix, rhs, method="lu", cache=True, verbosity="silent") -> NodeField
eliminate::solve(model: &Model, matrix: &Matrix, rhs: &NodeField) -> NodeFieldsolve_eliminate(matrix, model, rhs, method="lu", cache=True, verbosity="silent") -> NodeField
unilateral::solve(model: &Model, matrix: &Matrix, rhs: &NodeField) -> NodeFieldsolve_unilateral(matrix, model, rhs, method="lu", active_set=None, cache=True, max_iter=100, tol=1e-10, verbosity="silent") -> NodeField

ops::export — export vers des formats externes

Rust (ops::export::…)Python (pyrucast.export.…)
write_vtk_mesh(mesh: &Mesh, path: &Path, encoding: VtkEncoding)export_vtk(mesh, path, binary=False) -> None
write_vtk_node_field(mesh: &Mesh, field: &NodeField, path: &Path, encoding: VtkEncoding)export_vtk(mesh, path, field=node_field, binary=False) -> None
write_vtk_element_field(mesh: &Mesh, field: &ElementField, path: &Path, encoding: VtkEncoding)export_vtk(mesh, path, field=element_field, binary=False) -> None
write_vtk_series(mesh: &Mesh, evolution: &Evolution, path: &Path, encoding: VtkEncoding) -> Vec<PathBuf>export_vtk(mesh, path, field=evolution, binary=False) -> None
to_arrays(groups: &[(String, &Mesh)], node_fields: &[&NodeField], element_fields: &[(&ElementField, ElementLayout)], order: NodeOrder, first_tag: i64) -> Exportedto_arrays(groups, *, node_fields=[], element_fields=[], gauss=False, order="pyrucast", first_tag=1) -> dict
— (exige l’interpréteur)to_gmsh(meshes, fields=None, *, model_name="pyrucast") -> str
— (exige l’interpréteur)to_medcoupling(meshes, fields=None, *, mesh_name="mesh", gauss=True) -> MEDFileData

spill — le swap utilisateur

Rust (spill::…)Python (pyrucast.…)
stats() -> SpillStatsspill_stats() -> dict

Les clés du dictionnaire sont threshold, count, largest, live et peak, en octets ; threshold vaut None quand rien ne déborde. Voir Calculs plus gros que la RAM.

archive — sauvegarde et relecture d’un graphe

Les deux seuls verbes qui restent au niveau racine : ils ne produisent aucun conteneur déterminé, mais un dictionnaire de ce qu’on leur a donné.

Rust (archive::…)Python (pyrucast.…)
save(path, &[(&str, &dyn ArchiveRoot)])save(path, dict) -> None
load(path) -> Objectsload(path) -> dict

À l’écriture les types sont connus du compilateur, d’où la tranche de paires ; à la relecture non, d’où la table nommée dont on tire chaque objet avec son type attendu (objets.mesh("clef")?). Voir Sauvegarde et relecture.

ops::geom héberge locate_points (mapping iso-paramétrique inverse, sous le baignage) et project_points (projection au point le plus proche sur une surface, sous le contact) ; ces deux primitives sont internes (API Rust), pas encore exposées en Python — la seule dérogation de module entier du garde-fou de complétude (tests/python/test_mirror_completeness.py). Le nœud le plus proche, lui, n’est pas un opérateur : c’est la méthode mesh.nearest_node([x, y]), des deux côtés.

Opérateurs (dunders ↔ traits Rust)

Toutes les classes implémentent __repr__ (← Debug, vue structurelle) et __str__ (← Display, vue résumée façon cast3m) — voir Conventions. Les autres opérateurs, classe par classe :

Arithmétique sur les champs

L’arithmétique (f + s, f + g, …) existe au niveau zone et au niveau agrégat ; les opérations par composante et entre champs sont portées par les traits SubField / Field. Les dunders +, -, *, /, ** dispatchent selon l’opérande droite : un float déclenche l’arithmétique scalaire, un champ du même type l’arithmétique champ + champ (valeur à valeur).

ClasseOpérateurs / méthodes PythonSémantiqueBacking Rust
SubNodeField / SubElementFieldf + s, f - s, f * s, f / s, f ** sbroadcast scalaire, nouveau champAdd/Sub/Mul/Div<f64>, map_all
SubNodeField / SubElementFieldf + g, f - g, f * g, f / g, f ** gchamp + champ par composante, union/passthrough (même support), nouveau champSubField::merge_components
NodeField / ElementFieldf + s, f - s, f * s, f / s, f ** sbroadcast scalaire sur toutes les zonesField::combine_scalar
NodeField / ElementFieldf + g, f - g, f * g, f / g, f ** gchamp + champ par (support, composante), union/passthroughField::merge_field
NodeField / ElementFieldf + sub, f - sub, … (sub = sous-champ)maj ciblée de la (des) zone(s) de même support (union/passthrough)Field::merge_subfield
zone & agrégatadd_to_component(c, s), sub_/mul_/div_to_componentscalaire sur une composante, en placeSubField/Field::map_component
zoneset_uniform(c, v)force une composante à vSubField::set_uniform

f + s / f + g renvoie un nouveau champ ; += n’est pas surchargé. La composition de zones n’est pas sur + : c’est l’union | (union en Rust, cf. ci-dessous). L’opérateur + est entièrement réservé à l’arithmétique de champ — scalaire (f + 1.0) et champ + champ valeur à valeur (f + g via merge_components/merge_field) ; p. ex. deux champs constants valant 1 s’additionnent en un champ constant valant 2. Pour fusionner des zones avec vérification (et non additionner) : merge(a, b) ≡ a | b.

Algèbre de Matrix / SubMatrix

Matrix.__mul__ dispatche selon l’opérande droite, comme l’arithmétique de champ ci-dessus : un NodeField déclenche le produit matrice-vecteur (mul_field), un float la mise à l’échelle paresseuse du facteur (voir Matrice creuse). / n’existe que pour le facteur (Matrix n’a pas de division matrice-vecteur), et refuse un diviseur nul (ZeroDivisionError) ou non fini (ValueError). Comme pour les champs, tous ces opérateurs renvoient une nouvelle Matrix — jamais de mutation en place.

+ et - additionnent deux opérateurs ; ils ne sont pas la composition (|, ci-dessous), qui écarte un bloc dont elle tient déjà l’emplacement : k | k vaut k, k + k vaut 2k.

ClasseOpérateurs / méthodes PythonSémantiqueBacking Rust
Matrixk * fieldproduit matrice-vecteur A·x, NodeField neufMatrix::mul_field, Mul<&NodeField>
Matrixk * s, s * k, k / s (s: float)facteur scalaire, blocs clonés dans de nouveaux slots (aucune valeur réécrite), CSR assemblée mise à l’échelle avec, k inchangéeMul/Div<f64> for &Matrix, Mul<Matrix> for f64
Matrix-kfacteur nié, sucre pour k * -1.0Neg for &Matrix
Matrix, SubMatrixa + b, a - bsomme : blocs des deux opérandes, partagés, non dédoublonnés ; résultat non assembléAdd/Sub (toutes combinaisons Matrix/SubMatrix)
SubMatrixb * s, s * b, b / s, -bbloc neuf au facteur ajusté (aucune valeur réécrite)Mul/Div<f64>/Neg for &SubMatrix
SubMatrix.factor (lecture seule)facteur courant du bloc (1.0 par défaut)SubMatrix::factor

Indexation par clé

ClasseOpérateurs PythonCléBacking Rust
SubNodeFieldf[nid, "c"], f[nid, "c"] = v(NodeId, composante)Index/IndexMut<(NodeId, &str)>
SubElementFieldf[cell, g, "c"], f[cell, g, "c"] = v(maille, point de Gauss, composante)méthodes value / set_value (pas de trait Index)

Protocole séquence — len(x), x[i], for _ in x

Classelen(x)x[i] →Backing Rust
Cellnombre de nœudsNodeméthodes
SubMeshnombre de maillesCellméthodes
Meshnombre de sous-maillagesSubMeshAggregate (macro)
SubFiniteElementSpacenombre d’élémentsElementméthodes
FiniteElementSpacenombre de sous-espacesSubFiniteElementSpaceAggregate (macro)
ElementFieldnombre de sous-champsSubElementFieldAggregate (macro)
Modelnombre de sous-modèlesSubModelAggregate (macro)
Matrixnombre de sous-matrices— (pas de [i])Aggregate (macro pour len)
SubMatrixnombre d’entrées—méthode entry_count
Evolutionnombre de sous-évolutionsSubEvolutionAggregate (macro)
SubEvolutionnombre d’échantillons tabulés—méthode __len__

Union | (composition d’agrégats)

La composition d’agrégats est l’union : côté Python elle s’écrit | (comme set | set), côté Rust ce sont les méthodes nommées union / union_sub / union_sub_first / union_subs (renvoient Result<…>). Les sous-objets sont partagés (refcount), jamais copiés ; les contraintes de domaine (même Coords pour Mesh, etc.) restent vérifiées.

Sémantique d’union (uniforme pour tous les agrégats) :

  1. Déduplication par handle : un sous-objet dont le Handle désigne un objet déjà présent (cf. Handle::same_object) n’est pas ajouté deux fois.
  2. Finalisation (Aggregate::finalize) : par défaut un no-op ; les champs la surchargent pour fusionner les zones partageant un même support (voir plus bas).
PythonRustRésultatSémantique
agrégat | agrégata.union(&b)agrégatunion dédupliquée, ordre de 1ʳᵉ apparition
agrégat | suba.union_sub(&h)agrégatajoute un sous-objet en queue (ignoré si déjà présent)
sub | agrégata.union_sub_first(&h)agrégatla même union, sous-objet en tête (via __ror__)
sub | subT::union_subs(&a, &b)agrégatunion des deux sous-objets
node | nodea.union(&b)Meshmaillage POI1 unitaire sur les deux nœuds
mesh | nodem.union_node(&n)Meshajoute un point (erreur si Mesh non unitaire POI1)

Vaut pour les sept agrégats (Mesh, FiniteElementSpace, Model, Matrix, NodeField, ElementField, Evolution) plus Node.

Finalisation des champs (fusion par support)

Après l’union par handle, NodeField et ElementField fusionnent les sous-champs définis sur le même support (même Handle<SubMesh> pour NodeField, même Handle<SubFiniteElementSpace> pour ElementField) :

  • le sous-champ fusionné porte l’union des composantes ;
  • une composante définie par plusieurs sous-champs doit y avoir la même valeur partout (comparaison exacte), sinon | lève une erreur ;
  • pour NodeField, une vérification inter-supports finale impose qu’un nœud partagé par des zones de supports différents s’accorde sur toute composante commune.

Ces opérations sont aussi exposées en Rust : ops::node_field::consolidate (NodeField) et ops::element_field::consolidate (ElementField).

+ est réservé à l’arithmétique de champ

L’opérateur + (et -, *, /) reste l’arithmétique scalaire des sous-champs (subfield + 2.0 → ajoute la valeur à chaque composante) et a vocation à porter, à terme, l’addition réelle de champs (valeur au nœud = somme des deux). Il n’est jamais utilisé pour composer des agrégats — c’est | qui s’en charge, sans collision.