原文标题: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 与依赖兼容性。本稿保留了原文中的代码路径与示例输出说明,没有声称代码已在本地运行。











暂无评论内容