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

Dirichlet

La condition de Dirichlet impose la valeur d’une variable primale, u(n) = u_d, sur un ensemble de nœuds. C’est une contrainte imposée par multiplicateurs de Lagrange : aucun matériau, aucune loi de comportement. Elle ne crée aucun nœud et ne mute jamais le Coords.

Implémentation : src/models/dirichlet.rs ; opérateur ops::model::dirichlet(…).

Deux maillages fournis par l’utilisateur

L’utilisateur fournit deux maillages POI1 :

  • imposed_mesh — les nœuds contraints (partagés avec la physique cible) ;
  • multiplier_mesh — le support des multiplicateurs, apparié élément-par-élément avec imposed_mesh (même structure de sous-maillage, même nombre de cellules par paire).

On fabrique typiquement multiplier_mesh depuis imposed_mesh avec le mesher générique barycenter (des nœuds neufs colocalisés au centre de gravité de chaque cellule). Mais l’utilisateur reste libre : nœuds colocalisés, décalés, ou même réutiliser les nœuds contraints eux-mêmes.

Quatre noms de variables

Deux sont requis, deux sont déduits et surchargeables :

rôlenomfourniture
le modèle contrainttargetrequis
variable imposée (une primale de target)variable (ex "T")requis
duale de la cible (ligne où atterrit la réaction Cᵀ)lue dans targetdéduit
primale propre = multiplicateur (inconnue du système)lambda_<variable>déduit
duale propre = ligne de contrainte + slot où l’utilisateur écrit u_dimposed_<variable>déduit

Signature complète :

model.dirichlet(target, variable, imposed_mesh, multiplier_mesh, sense="=")

Cinq arguments, et chacun porte une décision. La cible est le modèle qu’on contraint : variable doit être une de ses primales, et sa ligne duale s’y lit — le passage T → q est l’affaire de la physique visée, pas quelque chose à retaper. Le donner permet aussi de refuser à la construction une variable que la cible ne déclare pas, au lieu de découvrir une matrice singulière.

Une variable, et une seule. Pour encastrer, on compose : dirichlet(m, "u_x", …) | dirichlet(m, "u_y", …), les deux partageant leur maillage multiplicateur — lambda_u_x et lambda_u_y sur un même nœud sont des DDL distincts. Une contrainte est une famille de relations scalaires avec un multiplicateur par relation ; on n’empaquette plusieurs composantes que lorsqu’elles partagent quelque chose de coûteux, ce qu’un Dirichlet ne fait pas.

sense ("=", ">=", "<=") rend la contrainte unilatérale (u ≥ u_d : une butée) — voir la section « Relations unilatérales » de la page Contraintes et le solveur solve_unilateral.

Les deux blocs unité

À l’assemblage, Dirichlet contribue une paire de blocs unité par sous-maillage, chacun marqué non-symétrique (seule l’union C ∪ Cᵀ l’est — propriété globale du système point-selle ; cf. le drapeau symmetric de la Matrice) :

  • bloc C : (multiplier_node, imposed_value) × (imposed_node, imposed_variable) = 1
  • bloc Cᵀ : (imposed_node, target_dual) × (multiplier_node, multiplier) = 1

Le bloc C exprime la relation u(n) = u_d ; le bloc Cᵀ réinjecte la réaction dans l’équation de la physique cible (ligne target_dual).

Valeur imposée et réaction

  • La valeur imposée u_d n’est pas stockée dans le SubModel : l’utilisateur la fournit dans le NodeField de chargement, à la position (multiplier_node, imposed_value) — c’est-à-dire au slot imposed_<v> du nœud-multiplicateur.
  • Le multiplicateur se retrouve dans la solution sous le nom multiplier (lambda_<v>) au nœud-multiplicateur ; sa valeur est la force de réaction de la contrainte.

Les nœuds-multiplicateurs vivent tant que leur maillage ou le SubModel les référence (refcounts) ; quand les deux disparaissent, ils deviennent collectables. Le SubModel ne décrémente que ce qu’il partage — il n’a rien créé.

Exemple : Poisson 1-D -u'' = 0, u(0)=0, u(1)=1

Solution analytique u(x) = x, multiplicateurs aux bords = flux ±1. On compose la conduction thermique avec deux contraintes Dirichlet par l’union | (cf. Modèle physique) :

import pyrucast

# 1) Maillage + FE space
c = pyrucast.Coords(dim=1)
nodes = [c.add_node([i / 4.0]) for i in range(5)]
mesh = pyrucast.Mesh(c, "SEG2")
for i in range(4):
    mesh.unit().add_cell([nodes[i], nodes[i + 1]])
fes = pyrucast.FiniteElementSpace(mesh)

# 2) Multiplier supports: barycenter co-locates fresh nodes.
imposed_left = pyrucast.mesh.poi1_from_nodes([nodes[0]])
imposed_right = pyrucast.mesh.poi1_from_nodes([nodes[-1]])
mult_mesh_left = pyrucast.mesh.barycenter(imposed_left)
mult_mesh_right = pyrucast.mesh.barycenter(imposed_right)
conduction = pyrucast.model.heat_conduction(fes)
left = pyrucast.model.dirichlet(conduction, "T", imposed_left, mult_mesh_left)
right = pyrucast.model.dirichlet(conduction, "T", imposed_right, mult_mesh_right)
mult_left = mult_mesh_left.node(0, 0, 0)
mult_right = mult_mesh_right.node(0, 0, 0)

# 3) Whole model: conduction + both Dirichlet.
model = conduction | left | right
materials = pyrucast.element_field.material_field(model, [("k", 1.0)])

# 4) Loading: the `constraint_rhs` helper designates each constraint by its
#    constrained node and writes u_d at the multiplier node's imposed_T slot.
#    Both are merged with `|`.
rhs = left.constraint_rhs([(nodes[0], 0.0)]) | right.constraint_rhs([(nodes[-1], 1.0)])

# 5) Assemblage + résolution.
K = pyrucast.matrix.stiffness(model, materials)
solution = pyrucast.solver.solve(K, rhs)
assert abs(solution.value(nodes[2], "T") - 0.5) < 1e-10  # T in the middle
assert abs(solution.value(mult_left, "lambda_T") - 1.0) < 1e-10  # flux on the left

La forme Rust équivalente (opérateurs au niveau parent, composés par union) est dans le chapitre Modèle physique.

Limitations actuelles

  • imposed_mesh et multiplier_mesh sont des maillages POI1 (contrainte par nœud). Les contraintes réparties (sur une arête entière) passeront par un bloc C issu d’une intégration, comme flux pour les seconds membres.
  • Seule la valeur imposée constante par nœud est gérée ; une valeur spatialement variable se fournit nœud par nœud dans le chargement.