FEniCS:如何在 2D 网格内的圆上指定边界条件

计算科学 Python 芬尼克斯 边界条件
2021-12-20 15:05:13

我想在数字上找到圆柱体相对两侧的两条金属条的互电容。这个问题显然是一个二维拉普拉斯方程。我也想找到气缸外的潜力。因此我有这样的事情:

mesh = UnitCircleMesh(50)
V = FunctionSpace(mesh, 'Lagrange', 1)

u_L = Constant(-1)
def left_boundary(x, on_boundary):
    r = math.sqrt(x[0] * x[0] + x[1] * x[1])
    return near(r, 0.7) and x[0] < 0 and between(x[1], (-0.5, 0.5))

u_R = Constant(1)
def right_boundary(x, on_boundary):
    r = math.sqrt(x[0] * x[0] + x[1] * x[1])
    return near(r, 0.7) and x[0] > 0 and between(x[1], (-0.5, 0.5))

leftPlate = DirichletBC(V, u_L, left_boundary)
rightPlate = DirichletBC(V, u_R, right_boundary)
bcs = [leftPlate, rightPlate]

u = TrialFunction(V)
v = TestFunction(V)
a = dot(grad(u), grad(v)) * dx

u = Function(V)
solve(a == Constant(0) * v * dx, u, bcs)

当我运行它时,我收到*** Warning: Found no facets matching domain for boundary condition.. 找到的解决方案是 0。出了什么问题?

2个回答

正如错误消息所示,DirichletBC 没有击中任何具有相应自由度的网格实体。您需要检查您的子域。调试这些的一种方法是将 DirichletBC 应用于函数并绘制结果:

def left_boundary(x, on_boundary):
    r = math.sqrt(x[0] * x[0] + x[1] * x[1])
    return r < 0.5

leftPlate = DirichletBC(V, u_L, left_boundary)
u = Function(V)
u.vector()[:] = 0
leftPlate.apply(u.vector())
plot(u, interactive=True)

这将绘制一顶漂亮的帽子。现在您可以开始调整 left_boundary 函数,直到达到您想要的结果。

我也遇到过这个问题,在各种论坛上花了很多时间,我终于想出了一个好的解决方案。此解决方案将允许您在几何体中的任何整个子域上指定边界条件,并且每个子域都可以有自己单独的边界条件。我在此处发布此解决方案,因为旧的官方 Fenics QA 论坛现已关闭,并且此解决方案还解决了这些论坛上的几个未回答的问题。新论坛可在此处获得。

在我的特定问题中,我在矩形和拉格朗日函数空间中有两个圆圈。在这个例子中,我设置左墙为 1,右墙为 0,左圆为 0,右圆为 3。

我的整个代码都包括在内,但这是重要的部分:

marked_cells = fenics.SubsetIterator(CELL_MESHFUNCTION_RESULT, SUBDOMAIN_INDEX_NUMBER)
for cells in marked_cells:
    for f in fenics.facets(cells):
        FACET_MESHFUNCTION_RESULT[f] = CHOSEN_CONSTANT   

# later in code
BC = fenics.DirichletBC(FUNCTION_SPACE,
 fenics.Constant(0), FACET_MESHFUNCTION_RESULT, CHOSEN_CONSTANT)

结果产生如下图: 仿真结果

我对正在发生的事情的理解是,SubsetIterator 函数提供了网格中与特定子域索引号匹配的单元子集。接下来,在一个循环中,我们用一个新的索引号重新分配这些单元格中的任何 facet 元素,我们稍后可以引用该索引号。然后,我们使用该索引将边界条件应用于这些方面中的每一个。

这种方法比我发现的其他一些方法更有效,因为它不会遍历网格中每个单元格中的每个方面,只会遍历所需子域中的那些方面。我的直觉是分面是按顺序编号的,因此使用低索引号来标记分面可能是不可取的,因为模型中可能存在无意中用边界条件标记的随机分面。使用比构面数量大得多的索引可能会缓解这个问题,但这仍然是未知的。我在测试中找不到任何不匹配的方面。

这是完整代码(Windows 10、Ubuntu 20 到 WSL2、Python 3.8、Fenics 2019.1.0)

# Ryan Budde CID 2021

# imports
import fenics as fn
import mshr
from math import sin, cos, pi
import matplotlib.pyplot as plt
import pprint as pprint


# constants
squareW = 3
squareL = 2
circRad = 0.25

# create background geometry
domain = mshr.Rectangle(fn.Point(0,0), fn.Point(squareW, squareL))

# create source and sink circles
circPos = mshr.Circle(fn.Point(1, 1), circRad)
circNeg = mshr.Circle(fn.Point(2, 1), circRad)

# assign the circles to the domain
domain.set_subdomain(1, circPos)
domain.set_subdomain(2, circNeg)

# generate the mesh
mesh = mshr.generate_mesh(domain, 28)

# define subdomain markers and facets
markers = fn.MeshFunction('size_t',mesh, mesh.topology().dim(), mesh.domains())
boundaries = fn.MeshFunction('size_t',mesh, mesh.topology().dim()-1, mesh.domains())

# define function space
V = fn.FunctionSpace(mesh, 'Lagrange', 1) # a first order lagrangian function

# establish BC as C++ command
leftBC = 'near(x[0], 0)' # left wall
rightBC = 'near(x[0], 3)' # right wall

# establish BCs for subdomains
marked_cells = fn.SubsetIterator(markers,1) #left circle
for cells in marked_cells:
    for f in fn.facets(cells):
        boundaries[f] = 2    
        
marked_cells = fn.SubsetIterator(markers,2) # right circle
for cells in marked_cells:
    for f in fn.facets(cells):
        boundaries[f] = 3
  
# assign BC
bc1 = fn.DirichletBC(V, fn.Constant(2), leftBC)
bc2 = fn.DirichletBC(V, fn.Constant(0), rightBC)
bc3 = fn.DirichletBC(V, fn.Constant(0), boundaries, 2)
bc4 = fn.DirichletBC(V, fn.Constant(3), boundaries, 3)

bcs = [bc1, bc2, bc3, bc4]

# assign dx 
dx = fn.Measure('dx', domain=mesh, subdomain_data=markers)

# change permittivity of materials
class Permittivity(fn.UserExpression): 
    def __init__(self, markers, **kwargs):
        super().__init__(**kwargs) 
        self.markers = markers
    def eval_cell(self, values, x, cell):
        if self.markers[cell.index] == 0:
            values[0] = 1         # vacuum
        elif self.markers[cell.index] == 1:
            values[0] = 100 # small increase
        elif self.markers[cell.index] == 2:
            values[0] = 1e5  # large increase

mat_perm = Permittivity(markers, degree=1) 

# variational problem
u = fn.TrialFunction(V)
v = fn.TestFunction(V)
a = mat_perm * fn.dot(fn.grad(u), fn.grad(v)) * dx
L = 1*v*dx(2) + 1*v*dx(3)

# solve problem
u = fn.Function(V)
fn.solve(a == L, u, bcs)


# plot results
plt.subplot(1,2,1)
fn.plot(markers, title='Domains')

plt.subplot(1,2,2)
c = fn.plot(u, title='Fields')
plt.colorbar(c)
plt.show()
print('done')
```