静磁学(麦克斯韦方程)

一个三维静磁学问题:通过在单位立方体上求解磁 矢量势 \(\mathbf{A}\),恢复载流导线周围的磁场。脚本 examples/maxwell/magnetostatic.py 采用连续节点拉格朗日单元,并使用 Badia 与 Codina(2012)[BC2012]稳定化混合格式,使旋度-旋度问题在标准的(非旋度协调的)节点空间上适定。

备注

TensorMesh 自带连续的 节点拉格朗日 单元。\(\nabla\times\nabla\times\) 的自然空间是 旋度协调 的 Nédélec 边单元,目前尚未原生支持——因此本示例采取了与 顶盖驱动方腔 相同的务实路线:在等阶节点空间上求解,并添加 一致稳定化(这里是库仑规范的拉格朗日乘子,加上 grad-div 项和乘子项)以抑制伪模。更专门的有限元空间将逐步加入。

问题

静磁学由安培定律 \(\nabla\times\mathbf{H}=\mathbf{J}\),连同 \(\nabla\cdot\mathbf{B}=0\)\(\mathbf{B}=\mu\mathbf{H}\) 共同描述。写成 \(\mathbf{B}=\nabla\times\mathbf{A}\) 可恒等地满足散度约束,并且——将磁阻率 \(\nu=1/\mu\) 吸收进一个无量纲缩放后——将安培定律约化为关于 \(\mathbf{A}\)旋度-旋度 方程:

\[\nabla\times(\nabla\times\mathbf{A}) - \nabla p = \mathbf{J}, \qquad \nabla\cdot\mathbf{A} = 0 \quad\text{in } \Omega = (0,1)^3,\]

其中标量 \(p\) 是一个拉格朗日乘子,用于施加 库仑规范 \(\nabla\cdot\mathbf{A}=0\)。该规范并非可有可无:旋度-旋度算子会湮灭任意梯度场,因此其核空间极其庞大,没有规范时 \(\mathbf{A}\) 仅在相差一个 \(\nabla\phi\) 的意义下唯一。

边界条件为齐次切向迹

\[\mathbf{n}\times\mathbf{A}=\mathbf{0}, \qquad p = 0 \quad\text{on } \partial\Omega,\]

即在每个与坐标轴对齐的面上,\(\mathbf{A}\) 的两个 切向 分量被固定为零(在 \(x=0,1\) 上即 \(A_y\)\(A_z\)),这些约束沿边和角处叠加组合。

电流源。 一条穿过立方体中心、沿 \(z\) 方向的光滑通道——直导线的一个柔化模型,

\[\mathbf{J} = J_0 \exp\!\left( -\frac{(x-x_c)^2+(y-y_c)^2}{2\sigma^2} \right)\,\mathbf{e}_z,\]

其中 \(J_0 = 100\),中心 \((x_c, y_c) = (0.5, 0.5)\),宽度 \(\sigma = 0.08\)。它沿 \(z\) 方向不变,因此 \(\nabla\cdot\mathbf{J} = 0\);在自由空间中,这样的电流会产生一个纯 方位角 方向、环绕导线的场,这正是我们要寻找的定性特征。

稳定化弱形式

在连续拉格朗日空间中求 \((\mathbf{A}_h, p_h) \in V_h \times Q_h\),使得对所有 \((\mathbf{v}_h, q_h)\) 均有

\[\begin{split}a(\mathbf{A}_h,\mathbf{v}_h) + b(\mathbf{v}_h,p_h) + s_u(\mathbf{A}_h,\mathbf{v}_h) &= (\mathbf{J},\mathbf{v}_h), \\ -\,b(\mathbf{A}_h,q_h) + s_p(p_h,q_h) &= 0,\end{split}\]

其中各双线性型为

\[\begin{split}a(\mathbf{A},\mathbf{v}) &= (\nabla\times\mathbf{A},\,\nabla\times\mathbf{v})_\Omega, & b(\mathbf{v},p) &= -(\nabla p,\,\mathbf{v})_\Omega, \\ s_u(\mathbf{A},\mathbf{v}) &= \sum_{K\in\mathcal{T}_h} h_K^2\, (\nabla\cdot\mathbf{A},\,\nabla\cdot\mathbf{v})_K, & s_p(p,q) &= (\nabla p,\,\nabla q)_\Omega.\end{split}\]

正是这两个稳定化项使得节点离散稳健:grad-div 惩罚项 \(s_u\) 控制 \(\mathbf{A}\) 的散度,而 \(s_p\) 对乘子进行正则化——这就是 [BC2012] 的方程 (3.6)。该演示对每个单元使用常值长度尺度 \(h^2 = \texttt{chara\_length}^2\)。装配之后,这四个形式构成一个 2×2 分块鞍点系统

\[\begin{split}\begin{bmatrix} A + S_u & B \\ -B^\top & S_p \end{bmatrix} \begin{bmatrix} \mathbf{A}_h \\ p_h \end{bmatrix} = \begin{bmatrix} \mathbf{f} \\ \mathbf{0} \end{bmatrix}.\end{split}\]

TensorMesh 设置

每个双线性型都是一个小型的 ElementAssembler,其 forward 返回逐节点的分块。例如,旋度-旋度形式构造两个反对称的叉乘矩阵 \([\nabla N]_\times\) 并对它们做缩并——\([\nabla N]_\times\) 的第 \(a\) 列恰好是 \(\nabla\times(N\,\mathbf{e}_a)\),因此该缩并就是 \([3,3]\) 分块 \((\nabla\times\mathbf{u})\cdot(\nabla\times\mathbf{v})\)

列表 20 examples/maxwell/magnetostatic.py(核心部分)
class CurlCurlAssembler(ElementAssembler):
    """(curl u, curl v) for 3D vector nodal fields."""
    def forward(self, gradu, gradv):
        zero = torch.zeros_like(gradu[0])
        curl_u = torch.stack((torch.stack((zero, -gradu[2], gradu[1])),
                              torch.stack((gradu[2], zero, -gradu[0])),
                              torch.stack((-gradu[1], gradu[0], zero))))
        curl_v = ...                       # the same, built from gradv
        return curl_u.T @ curl_v           # [3, 3] block

A  = CurlCurlAssembler.from_mesh(mesh)()
Su = DivergenceStabilizationAssembler.from_mesh(mesh, h2=h2)()   # h^2 (div u, div v)
Sp = PressureStabilizationAssembler.from_mesh(mesh)()            # (grad p, grad q)
B  = PressureCouplingAssembler.from_mesh(mesh)()                 # -(grad p, v)

K = SparseMatrix.combine([[A + Su, B],
                          [-1.0 * B.T, Sp]])

几处值得指出的细节:

  • 分块装配。 SparseMatrix.combine 将四个稀疏分块拼贴成鞍点矩阵,使得矢量 \(\mathbf{A}\) 自由度和标量 \(p\) 自由度共享同一个线性系统。

  • 作为自由度掩码的切向边界条件。 tangential_vector_potential_mask\(\mathbf{n}\times\mathbf{A}=\mathbf{0}\) 构建逐分量的狄利克雷掩码;将其与用于 \(p\)mesh.boundary_mask 拼接后,喂入单个 Condenser,在求解前凝聚掉每一个受约束的自由度。

  • 恢复场。 \(\mathbf{B}=\nabla\times\mathbf{A}\) 是一个导出的、逐单元的量。脚本通过 \(L^2\) 投影将其投回到光滑的节点场——装配一个 MassElementAssembler,用一个 NodeAssembler 构建旋度右端项,再逐分量求解 \(M\,\mathbf{B} = \tilde{\mathbf{B}}\)。这与 圆柱绕流 恢复涡量所用的方法相同。

环绕载流导线的磁场流线

图 50 magnetostatic.py 的输出(在 ParaView 中渲染):\(\mathbf{B}=\nabla\times\mathbf{A}\) 的流线,大致沿 \(z\) 轴向下观察。中心处暖色的图元标示出沿 \(z\) 方向的电流通道;场线沿方位角方向环绕它——直导线的右手定则图样——而有限的立方体边界则将外圈轻微地方形化。

运行它

cd examples/maxwell
python magnetostatic.py        # writes magnetostatic_3d.vtu

求解会写出 magnetostatic_3d.vtu,其中包含两个节点点场:矢量势 A 和磁场 curl_A \(= \nabla\times\mathbf{A}\)。在 ParaView 中打开它,并对 curl_A 应用 Stream TracerGlyph 即可复现上图。如果你想做实验,网格大小和电流设置就在 main() 的开头。

下一步

  • 顶盖驱动方腔 —— 同样的"等阶节点 + 一致稳定化"策略,在那里用于满足不可压缩 Navier-Stokes 的 inf-sup 条件。

  • 形式 —— 矢量值的 forward 返回,以及这里每个装配器共享的逐节点分块约定。

  • 边界条件 —— 矢量未知量的自由度掩码与静态凝聚。

[BC2012] (1,2)

S. Badia and R. Codina, A Nodal-based Finite Element Approximation of the Maxwell Problem Suitable for Singular Solutions, SIAM Journal on Numerical Analysis, 50(2):398–417, 2012.