我也遇到过这个问题,在各种论坛上花了很多时间,我终于想出了一个好的解决方案。此解决方案将允许您在几何体中的任何整个子域上指定边界条件,并且每个子域都可以有自己单独的边界条件。我在此处发布此解决方案,因为旧的官方 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')
```