In this tutorial, we will focus on the shifted boundary method.
We will aim to solve the Poisson problem with Dirichlet boundary conditions on a domain which has a boundary that doesn’t align with our background mesh.
The idea of the method is to shift the boundary conditions from a true boundary that is not aligned with the mesh to a surrogate boundary .
The mathematical formulation of the problem can be written as follows: Given a domain with exterior boundary and a map , we define:
where is the normal vector and th tangent vector on the true boundary .
The extension operator of a function on to is defined as .
and therefore for the boundary condition, we can write
The full derivation of the variational formulation can be found in the original paper, and can be written as: Find such that
Assuming we have the mesh above, we can start to implement this in UFL as follows
import ufl
import basix.ufl
cell = "quadrilateral"
c_el = basix.ufl.element("Lagrange", cell, 1, shape=(2,))
OmegaG = ufl.Mesh(c_el)
el = basix.ufl.element("Lagrange", cell, 1)
V = ufl.FunctionSpace(OmegaG, el)
u = ufl.TrialFunction(V)
w = ufl.TestFunction(V)
a = ufl.inner(ufl.grad(u), ufl.grad(w)) * ufl.dx
x = ufl.SpatialCoordinate(OmegaG)
f = ufl.sin(x[0]) * ufl.cos(x[1]) # Some spatially varying expression
L = ufl.inner(f, w) * ufl.dxHowever, we require , , , and to be implemented on the boundary of the domain.
For this we will use a surrogate_mesh of the original mesh, which only contain the exterior facets.
For now, this can symbolically be defined as
facet = "interval"
f_el = basix.ufl.element("Lagrange", facet, 1, shape=(2,))
GammaG = ufl.Mesh(f_el)Furthermore, we only require these quantities at the quadrature points used in the numerical integration, and therefore we define the following abstract functions
quadrature_degree = 4
q_el = basix.ufl.quadrature_element(facet, degree=quadrature_degree, value_shape=(2,))
q_scalar_el = basix.ufl.quadrature_element(
facet, degree=quadrature_degree, value_shape=()
)
Q_scalar = ufl.FunctionSpace(GammaG, q_scalar_el)
uG = ufl.Coefficient(Q_scalar)
duG_t = ufl.Coefficient(Q_scalar)
Q = ufl.FunctionSpace(GammaG, q_el)
dM = ufl.Coefficient(Q)
t_bar = ufl.Coefficient(Q)The normal vector on the true boundary is derived from the closest point project from the surrogate boundary to the true boundary.
d_scalar = ufl.sqrt(ufl.dot(dM, dM))
n_bar = dM / d_scalarNext we can define the integration measure over the surrogate boundary
nt = ufl.FacetNormal(OmegaG)
dsG = ufl.Measure(
"ds", domain=OmegaG, metadata={"quadrature_degree": quadrature_degree}
)and define the remainder of the variational formulation
def shift(z, d):
return z + ufl.dot(ufl.grad(z), d)
alpha = ufl.Constant(OmegaG)
h = ufl.Coefficient(Q_scalar)
a -= ufl.inner(ufl.dot(ufl.grad(u), nt), shift(w, dM)) * dsG
a -= ufl.inner(shift(u, dM), ufl.dot(ufl.grad(w), nt)) * dsG
a += (
ufl.dot(nt, n_bar)
/ d_scalar
* ufl.inner(ufl.dot(ufl.grad(u), dM), ufl.dot(ufl.grad(w), dM))
* dsG
)
a += alpha / h * ufl.inner(shift(u, dM), shift(w, dM)) * dsG
L -= ufl.inner(uG, ufl.dot(ufl.grad(w), nt)) * dsG
L += alpha / h * ufl.inner(uG, shift(w, dM)) * dsG
L -= ufl.inner(ufl.dot(duG_t * t_bar, nt), ufl.dot(ufl.grad(w), dM)) * dsG
With this formulation we are in theory ready to solve the problem with a shifted method. However, three questions remain:
How do we create the surrogate mesh from a background mesh and the true boundary ?
How do we compute the map mapping each quadrature point on the surrogate boundary to the closest point on ?
How do we transfer the associated quantities , , , and ? to the facet surrogate_mesh?
- Main, A., & Scovazzi, G. (2018). The shifted boundary method for embedded domain computations. Part I: Poisson and Stokes problems. Journal of Computational Physics, 372, 972–995. 10.1016/j.jcp.2017.10.026