在 Laplace 求解中加入内部定值线约束

原文标题:Laplace’s Equation with Internal Dirichlet Conditions
作者:FiniteVolumeMethod.jl 文档维护者(页面未署个人名)
来源:SciML / FiniteVolumeMethod.jl 官方教程
原文:https://docs.sciml.ai/FiniteVolumeMethod/stable/tutorials/laplaces_equation_with_internal_dirichlet_conditions
文档页脚:Documenter.jl 1.8.0 于 2025-01-01 生成;使用 Julia 1.11.2。该生成日期不是文章首发日期。

这篇教程演示如何为二维 Laplace 方程施加一条位于区域内部的 Dirichlet 定值线。例子求解正方形区域上的稳态温度场:四边温度给定,内部再加入从中心向下延伸到 y=2/5 的零值线段。

方程和边界条件是:

  • 区域:x、y 均在 [0, 1],满足 ∇²u = 0。
  • 左边界 x=0 与右边界 x=1:u=100y。
  • 底边 y=0:u=0;顶边 y=1:u=100。
  • 内部线段 x=1/2、0≤y≤2/5:u=0。

关键问题在于:教程指出这个初始三角网格上没有节点恰好落在这条内部线段上。若只给现成网格里已有的节点施加条件,线段上的 Dirichlet 值就无法精确定位。教程的做法是先把线段上的采样点加入网格,再只对这些新增/对应节点施加内部条件。此例是节点定值约束,不需要为这条内部线段额外创建受约束边。

建立初始网格并加入线段节点

先用 DelaunayTriangulation.jl 建立单位正方形三角网格。然后在 x=1/2 上,从 y=0 到 y=2/5 均匀取 250 个位置,并逐个调用 add_point!。教程用 CairoMakie 的 triplot 在加点前后显示网格,随后通过 max_area=1e-4 细化三角剖分并将其包装为有限体积几何对象。

using DelaunayTriangulation, FiniteVolumeMethod

tri = triangulate_rectangle(0, 1, 0, 1, 50, 50, single_boundary=false)

using CairoMakie
new_points = LinRange(0, 2 / 5, 250)
for y in new_points
    add_point!(tri, 1 / 2, y)
end
fig, ax, sc = triplot(tri)
fig

refine!(tri, max_area=1e-4)
fig, ax, sc = triplot(tri)
fig

mesh = FVMGeometry(tri)

教程显示,精化后的几何对象包含 10,173 个控制体积、19,981 个三角形和 30,153 条边。文档中的网格图用于观察剖分形状;本文不复制原图。

定义四边的 Dirichlet 条件

边界索引按底、右、顶、左的顺序排列。四个回调分别返回 0、100y、100 和 100y。代码用 zero(u) 和 oftype(u, …) 让各回调返回与当前解变量匹配的数值类型;这有助于避免边界条件之间的类型不一致。

bc_bot = (x, y, t, u, p) -> zero(u)
bc_right = (x, y, t, u, p) -> oftype(u, 100y)
bc_top = (x, y, t, u, p) -> oftype(u, 100)
bc_left = (x, y, t, u, p) -> oftype(u, 100y)

bcs = (bc_bot, bc_right, bc_top, bc_left)
types = (Dirichlet, Dirichlet, Dirichlet, Dirichlet)
BCs = BoundaryConditions(mesh, bcs, types)

找出线上的节点并建立条件映射

教程不手工抄录网格节点编号,而是遍历所有实顶点,通过 get_point(tri, i) 读取坐标,并筛选满足 x==1/2 且 0≤y≤2/5 的顶点。之后绘制网格,把命中的点标成红色,用于确认内部条件落在了预期位置。

function find_all_points_on_line(tri)
    vertices = Int[]
    for i in each_solid_vertex(tri)
        x, y = get_point(tri, i)
        if x == 1 / 2 && 0 ≤ y ≤ 2 / 5
            push!(vertices, i)
        end
    end
    return vertices
end

vertices = find_all_points_on_line(tri)
fig, ax, sc = triplot(tri)
points = [get_point(tri, i) for i in vertices]
scatter!(ax, points, color=:red, markersize=10)
fig

内部条件由 InternalConditions 表示。它接收一个和边界回调同类的函数,以及从网格节点编号到条件函数编号的 Dict。这里所有选中节点都对应唯一的第 1 个内部条件函数,函数在这些节点上返回与 u 类型相同的零值:

ICs = InternalConditions(
    (x, y, t, u, p) -> zero(u),
    dirichlet_nodes=Dict(vertices .=> 1),
)

按教程的输出,此对象包含 250 个 Dirichlet 节点,没有 Dudt 节点。也就是说,几何步骤找出的这 250 个点被明确登记为内部定值节点,而不只是画在图上的标记。

给稳态问题设置初值并求解

稳态求解器仍需要初始猜测。没有内部条件时,教程指出 u(x,y)=100y 是符合四边数据的解;因此它用 100y 作为大部分节点的初值,并把内部线段节点的初值单独置零。

随后扩散函数设为 1,对应 ∇²u=∇·(D∇u)、D=1。把几何、外边界条件和内部条件按顺序传给 FVMProblem;最终时间设为 Inf,再用 SteadyFVMProblem 转成稳态问题。

initial_condition = zeros(DelaunayTriangulation.num_points(tri))
for i in each_solid_vertex(tri)
    x, y = get_point(tri, i)
    initial_condition[i] = ifelse(
        x == 1 / 2 && 0 ≤ y ≤ 2 / 5,
        0,
        100y,
    )
end

diffusion_function = (x, y, t, u, p) -> one(u) # D = 1
final_time = Inf
prob = FVMProblem(mesh, BCs, ICs;
    diffusion_function,
    initial_condition,
    final_time,
)

steady_prob = SteadyFVMProblem(prob)

教程通过 SteadyStateDiffEq、LinearSolve 和 OrdinaryDiffEq 的 DynamicSS 算法求解,并指定 TRBDF2 与 KLUFactorization:

using SteadyStateDiffEq, LinearSolve, OrdinaryDiffEq

sol = solve(
    steady_prob,
    DynamicSS(TRBDF2(linsolve=KLUFactorization())),
)

原页展示的运行摘要为 retcode: Success,并列出了解向量样例值。构造的问题显示 10,173 个节点,求解摘要中的 u 则为 10,175 个元素。教程并未把这两个计数差异判定为错误,因此不应只凭该差异推断求解失败;网格精化也可能保留非活动点或涉及求解器内部状态。这里保留原页数据,不宣称本稿重新执行过该程序。

绘制温度等值分布

求解后,教程以 tricontourf 在 0 到 100 之间取 28 个等值层,并调用 tightlimits! 调整绘图范围。原页面在内部节点核对步骤中用红点显示线段位置,最终绘出温度场等值图。图像请见原始教程页面,本稿不抓取或嵌入图片。

fig, ax, sc = tricontourf(
    tri,
    sol.u,
    levels=LinRange(0, 100, 28),
)
tightlimits!(ax)
fig

完整流程可以概括为:先让网格显式包含内部线上的节点;再把节点编号映射到内部 Dirichlet 条件;最后构造稳态有限体积问题、求解并可视化。这个顺序适用于约束位置不与原始离散节点对齐的情形,避免把“线段上的条件”近似成周围节点上的条件。

来源与范围说明

教程由 Literate.jl 生成,页脚记录 Documenter.jl 1.8.0 和 Julia 1.11.2;页面没有标注个人作者,也未给出一项单独的文章转载许可。该页读取到的内容没有固定 FiniteVolumeMethod.jl 或其他依赖版本,因此应在目标环境确认 API 与依赖兼容性。本稿保留了原文中的代码路径与示例输出说明,没有声称代码已在本地运行。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容