几何函数
如何描述一个2D的几何形状?如何描述偏微分方程求解中所涉及的2D几何区域?
偏微分方程定义域
这个几何形状是PDE求解的区域。也就是说偏微分方程这个区域中取值(初始值和状态值、系数),在区域的边界上设定边界条件。
m ∂ 2 u ∂ t 2 + d ∂ u ∂ t − ∇ ⋅ ( c ∇ u ) + a u = f m \frac{\partial^2 u}{\partial t^2} + d \frac{\partial u}{ \partial t} - \nabla \cdot (c \nabla u) + a u = f m∂t2∂2u+d∂t∂u−∇⋅(c∇u)+au=f
或者,方程组
m ∂ 2 u ∂ t 2 + d ∂ u ∂ t − ∇ ⋅ ( c ⊗ ∇ u ) + a u = f \mathbf{m} \frac{\partial^2 \mathbf{u}}{\partial t^2} + \mathbf{d} \frac{\partial\mathbf{u}}{\partial t} - \nabla \cdot (\mathbf{c} \otimes \nabla \mathbf{u}) + \mathbf{a} \mathbf{u} = \mathbf{f} m∂t2∂2u+d∂t∂u−∇⋅(c⊗∇u)+au=f
在区域中进行考虑,准确的说是方程中的变量 u u u、 u \mathbf{u} u定义在区域上,所以 ∇ \nabla ∇也在区域的两个坐标 x , y x, y x,y上定义。
∇ = [ ∂ ∂ x , ∂ ∂ y ] T \nabla = \left[\frac{\partial}{\partial x}, \frac{\partial}{\partial y}\right]^\text{T} ∇=[∂x∂,∂y∂]T
根据偏微分方程的特性,只有单联通区域中的值才会相互影响,这就为描述区域提供了一些约束,这些约束可以大大简化积分区域的描述。
区域的假设与推论
- 2D区域是互不相交的单联通区域的并集 D \mathbb{D} D
- 不失一般性,假设 D \mathbb{D} D为单个单联通区域
- 一个单联通区域可能把整个的 R 2 \mathbb{R}^2 R2 分割为 n + 1 n+1 n+1个单联通子区域 { D i } \{D_i\} {Di}, i = 0 , 1 , 2 , … , n i=0,1,2,\ldots, n i=0,1,2,…,n,不失一般性假设 D 0 = D D_0=\mathbb{D} D0=D
- 一个单联通区域有若干闭环边界 { Ω i } \{\Omega_i\} {Ωi}, i = 1 , 2 , … , n i=1,2,\ldots, n i=1,2,…,n
- 进一步,可设 D 0 D_0 D0的外边界对应 Ω 1 \Omega_1 Ω1,且 D 1 , D 2 , … , D n D_1, D_2, \ldots, D_n D1,D2,…,Dn为互不相交单联通区域,且
D = D 0 − D 1 − D 2 − … − D n \mathbb{D} = D_0 - D_1 - D_2 - \ldots - D_n D=D0−D1−D2−…−Dn
这样,我们就把这个2D几何区域简化成一个单联通的区域,这个单联通区域中可能挖去了0个或者若干个单联通区域。只有一阶嵌套。
所以,问题简化为,如何描述一个闭环边界 Ω \Omega Ω包围的单联通区域 D D D?
描述函数
通用讨论
要描述这样的一个区域,跟描述其闭环边界是同一个意思。 x y xy xy平面的曲线,写成参数方程就是 ( x ( t ) , y ( t ) ) \left(x(t), y(t)\right) (x(t),y(t)),还能对 t t t有一些约束条件,比如 t ∝ s t \propto s t∝s,这里 s s s为曲线的长度坐标。
这是显式的方法,当然还有隐式的方法,比如
a 2 x 2 + b 2 y 2 = 1 a^2x^2 + b^2y^2 = 1 a2x2+b2y2=1
就是一个椭圆曲线。
其实还有一种描述方法就是距离函数,signed distance function。
将区域的内部定义为正向,区域的外部定义为负向,把平面上一个点到曲线的最近距离定义为函数的值:
θ ( x , y ) = { − d m i n x , y outside 0 x , y on curve d m i n x , y inside \theta(x, y) = \left\{ \begin{aligned} - d_{min}& & x, y \text{ outside} \\ 0 & & x,y\text{ on curve} \\ d_{min}& & x,y\text{ inside} \end{aligned} \right. θ(x,y)=⎩ ⎨ ⎧−dmin0dminx,y outsidex,y on curvex,y inside
这个描述也有一些很好玩的特性。
Matlab PDE工具箱的实现
Matlab PDE工具箱的fegeometry
可以用一个函数句柄(称为几何函数
)来描述2D形状,对这个函数,Matlab进行了如下的约定:
- 每一个闭合曲线/区域(称为区域),用至少两个曲线(称为边)来描述,这个曲线集合中的曲线,仅仅能在头、尾除相连;
- 根据输入参数的个数,函数范围相关的信息来描述区域
几何函数的范围值定义如下:
- 0个输入参数
n=geomFunc;
:返回边的条数 - 1个输入参数
d=geomFunc(bs);
:输入为一个数组(长度为 n n n),边的编号,返回一个矩阵 4 × n 4\times n 4×n,每列(4行)代表一个边,起点的参数 t 0 t_0 t0,终点的参数 t 1 t_1 t1,左边区域的标签,右边区域的标签。这里的区域标签就对应着子区域的编号,约定,区域外部的编号为0
- 2个输入参数
[x,y]=geomFunc(bs, s);
:这里的s
就代表曲线长度的数组(长度为 n n n),bs
对应为边,可以是一个标量(这个标量会广播到每一个s
),也可以是一个长度为 n n n的向量,程序返回对应点的坐标x
和y
。
这里唯一有意思的就是左边和右边的定义问题。按照曲线参数方程的定义,一个曲线
[ x ( t ) , y ( t ) ] T , t ∈ [ 0 , s ] [x(t),y(t)]^{\text{T}}, t\in [0, s] [x(t),y(t)]T,t∈[0,s]
按照参数增长的方向,前进的左边和右边就是这里定义的左和右。因此,曲线的起点和终点的定义是最终影响左、和右的关键。
例子
上面的定义很清楚,那么下面就是例子。第一个例子当然是Matlab自带的一个例子,就是一个圆。
edit circleg
就能看到这个函数的源代码。
function [x,y]=circleg(bs,s)
nbs=4; % Number of boundary segments
d=[0 0 0 0 % Start parameter value
1 1 1 1 % End parameter value
1 1 1 1 % Left hand region
0 0 0 0]; % Right hand region
start_angles = [0;pi/2;pi;3*pi/2];
switch nargin
case 0
x=nbs;
case 1
x=d(:,bs);
case 2
if isscalar(bs)
bs = repmat(bs,size(s));
end
x = zeros(size(s));
y = zeros(size(s));
for i = 1:numel(s)
segment = bs(i);
offset = pi/2*s(i);
angle = start_angles(segment)+offset;
x(i) = cos(angle);
y(i) = sin(angle);
end
end
这里,很容易可以反推出:
- 由四条曲线来描述一个圆;
- 每个曲线的参数范围都是从0到1,曲线左边为内部区域,右边为外部区域
- 这里还用了数组
bs
作为坐标来索引d
矩阵 - 最后,在计算坐标位置时,可以看到,四个曲线的起点分别为
0
,pi/2
,pi
和3pi/2
,然后,每个去线段的长度对应弧度pi/2
。
那么,如果我们要用两个曲线来描述一个圆,会是什么样的呢?
function [x,y]=circleg2(bs,s)
nbs=2; % Number of boundary segments
d=[0 0 % Start parameter value
1 1 % End parameter value
1 1 % Left hand region
0 0]; % Right hand region
start_angles = [0;pi];
switch nargin
case 0
x=nbs;
case 1
x=d(:,bs);
case 2
if isscalar(bs)
bs = repmat(bs,size(s));
end
x = zeros(size(s));
y = zeros(size(s));
for i = 1:numel(s)
segment = bs(i);
offset = pi*s(i);
angle = start_angles(segment)+offset;
x(i) = cos(angle);
y(i) = sin(angle);
end
end
调用下面的函数输出区域:
结果输出代码:
tiledlayout(1, 2);
nexttile;
pdegplot(@circleg,"EdgeLabels","on","FaceLabels","on", "VertexLabels", "on");
set(gca, 'XLim', [-1.1, 1.1], 'YLim', [-1.1, 1.1]);
title('cirgleg');
nexttile;
pdegplot(@circleg2,"EdgeLabels","on","FaceLabels","on", "VertexLabels", "on");
set(gca, 'XLim', [-1.1, 1.1], 'YLim', [-1.1, 1.1]);
title('cirgleg2');
exportgraphics(gcf, 'circleg.png')
>> fegeometry(@circleg)
ans =
fegeometry - 属性:
NumFaces: 1
NumEdges: 4
NumVertices: 4
NumCells: 0
Vertices: [4x2 double]
Mesh: []
>> fegeometry(@circleg2)
ans =
fegeometry - 属性:
NumFaces: 1
NumEdges: 2
NumVertices: 3
NumCells: 0
Vertices: [3x2 double]
Mesh: []
这两个函数产生的几何体,区别就在于边的条数和节点的个数:
- 4条曲线,对应4个边,4个端点
- 2条曲线,对应2条边,3个端点
我不是很理解的是为什么,4条曲线时,进行了正确的端点合并,而2条曲线时,端点没有正确合并?
>> fegeometry(@circleg2).Vertices
ans =
1.0000 0
-1.0000 0.0000
1.0000 -0.0000
>> fegeometry(@circleg).Vertices
ans =
0.0000 1.0000
-1.0000 0.0000
-0.0000 -1.0000
1.0000 -0.0000
会影响网格吗?
tiledlayout(1,2);
nexttile;
pdeplot(generateMesh(fegeometry(@circleg)).Mesh);
title('circleg');
nexttile;
pdeplot(generateMesh(fegeometry(@circleg2)).Mesh);
title('circleg2');
exportgraphics(gcf, 'circleg-circleg2.png')
看起来没有什么影响。
当然,还是有一个影响,就是在设定元素的网格尺寸时,过少的边会减少设定的精确性,所以在建立几何条件是,应该充分考虑网格加密的设定问题。
tiledlayout(2,2)
nexttile;
g = generateMesh(fegeometry(@circleg2), 'Hvertex', {[1], 0.01})
pdeplot(g.Mesh);
title('circleg2');
nexttile;
g = generateMesh(fegeometry(@circleg2), 'Hvertex', {[2], 0.01})
pdeplot(g.Mesh);
title('circleg2');
nexttile;
g = generateMesh(fegeometry(@circleg2), 'Hvertex', {[3], 0.01})
pdeplot(g.Mesh);
title('circleg2');
nexttile;
g = generateMesh(fegeometry(@circleg2), 'Hedge', {[1], 0.01})
pdeplot(g.Mesh);
title('circleg2');
可以看到,在进行加密时,这两个点没有正确归并并没有影响。同时,更多的边能够提供更好的网格加密控制力度,比如这里,就只能对单个边进行加密,而采用circleg
函数的4边描述法,还能够更加精细地控制加密区域。
tiledlayout(2,2)
nexttile;
g = generateMesh(fegeometry(@circleg), 'Hvertex', {[1], 0.01})
pdeplot(g.Mesh);
title('circleg');
nexttile;
g = generateMesh(fegeometry(@circleg), 'Hvertex', {[2], 0.01})
pdeplot(g.Mesh);
title('circleg');
nexttile;
g = generateMesh(fegeometry(@circleg), 'Hvertex', {[3], 0.01})
pdeplot(g.Mesh);
title('circleg');
nexttile;
g = generateMesh(fegeometry(@circleg), 'Hedge', {[1], 0.01})
pdeplot(g.Mesh);
title('circleg');
总结
Matlab PDE工具箱约定了一个显式描述积分区域的方法。上面介绍了基本的单边界单联通简单区域的函数定义方法,并简单探讨了对网格生成的影响。