Dirichlet
En bref
Impose la valeur d’une inconnue sur des nœuds : T = 20 sur un bord,
u_x = 0 sur un appui. Avec sense, impose une borne : u_x ≥ 0 est une
butée.
| opérateur | pyrucast.model.dirichlet(imposed_mesh, target, variable, sense="=") |
| tag | constraint et celui de la cible |
| type | contrainte (label() : "Dirichlet") |
| support | un maillage POI1 : les nœuds imposés, une relation par maille |
target est le modèle contraint, variable l’une de ses inconnues ; une
variable que la cible ne déclare pas est refusée à la construction. Une seule
variable par contrainte : pour encastrer, on compose
dirichlet(noeuds, m, "u_x") | dirichlet(noeuds, m, "u_y").
Théorie
Chaque nœud \(n\) du support porte une relation à un seul terme,
\[ u(n) = u_d \qquad (\text{ou } \ge,\ \le\ u_d), \]
imposée par un multiplicateur \(\lambda\) (voir Contraintes). Elle apporte deux blocs à la raideur, chacun de coefficient 1 :
- le bloc \(C\) : ligne
(multiplicateur, imposed), colonne(n, variable); - le bloc \(C^{\mathsf T}\) : ligne
(n, duale de la variable), colonne(multiplicateur, lambda).
À la solution, \(\lambda\) est l’effort de la pièce sur le blocage : le flux
ou la force qui tient la valeur imposée, vu de la pièce. La réaction, ce
que le blocage exerce sur la pièce, est \(-\lambda\) : une poutre encastrée
qui porte un poids \(P\) vers le bas a des multiplicateurs u_z de somme
\(-P\), et une réaction \(P\) vers le haut (voir
Contraintes).
Chaque relation a son nœud multiplicateur, créé par la contrainte à la
position du nœud imposé, dans le même Coords ; multiplier_mesh() les
donne. La valeur imposée \(u_d\) n’est pas stockée dans la contrainte : elle
s’écrit dans le chargement, à la composante imposed du nœud multiplicateur,
ce que fait constraint_rhs([(noeud_impose, u_d)]). Une relation absente du
chargement impose 0.
Variables
| primale | lambda, le multiplicateur |
| duale | imposed, la ligne de la relation |
| ce qu’elle contraint | variable, une inconnue de la cible, dont la duale reçoit la réaction |
| matériau, comportement | aucun |
Ce qu’elle produit
| raideur | les blocs \(C\) et \(C^{\mathsf T}\), une paire par zone du support |
| masse, géométrique | — |
| tangente | = raideur |
| forces internes | \(C^{\mathsf T}\lambda\) sur la duale de la cible, \(C\,u\) sur les lignes imposed |
| forces externes | aucune ; la valeur imposée est un chargement |
Exemple
Une barre \([0, 1]\) en conduction, \(T(0) = 0\) et \(T(1) = 1\) : le profil est \(T = x\), et les multiplicateurs sont les flux \(\pm1\) aux deux bouts.
import pyrucast
# 1) Maillage et espace éléments finis
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) Les deux appuis ; chacun crée son nœud multiplicateur près du nœud imposé.
imposed_left = pyrucast.mesh.poi1_from_nodes([nodes[0]])
imposed_right = pyrucast.mesh.poi1_from_nodes([nodes[-1]])
conduction = pyrucast.model.heat_conduction(fes)
left = pyrucast.model.dirichlet(imposed_left, conduction, "T")
right = pyrucast.model.dirichlet(imposed_right, conduction, "T")
mult_left = left.multiplier_mesh().node(0, 0, 0)
mult_right = right.multiplier_mesh().node(0, 0, 0)
# 3) Le modèle : la conduction et les deux appuis.
model = conduction | left | right
materials = pyrucast.element_field.material_field(model, [("k", 1.0)])
# 4) Chargement : `constraint_rhs` désigne chaque relation par son nœud imposé
# et écrit la valeur à la composante `imposed` de son nœud multiplicateur.
# Les deux se réunissent par `|`.
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 au milieu
assert abs(solution.value(mult_left, "lambda") - 1.0) < 1e-10 # le flux à gauche
Côté Rust
Dirichlet (src/models/dirichlet.rs) implémente Constraint : ses
relations (relations()) et ses nœuds multiplicateurs. Ses blocs sortent de
constraint_block_pair, partagé avec les autres contraintes, qui donne aux
deux blocs une même identité de paire (Symmetry::Half) : la matrice n’est
symétrique que si elle les tient tous les deux. Ils entrent dans la raideur
par contributions, comme blocs écrits.