The Poisson problem using Nitsche method on two subdomains#

We consider a domain \(\Omega = \Omega_1 \bigcup \Omega_2 = (0,1)^2\), where \(\Omega_1 = (0,\frac{1}{2}) \times (0,1)\) and \(\Omega_2 = (\frac{1}{2}, 1) \times (0,1)\)

We revisit the Poisson equation using the Nitsche method for two subdomains.

\[ \begin{align} - \nabla^2 u = f \quad \text{in $\Omega$}, \quad \quad u = 0 \quad \text{on $\partial \Omega$}, \quad \quad [\![ u ]\!] = 0 \quad \text{on $\mathcal{I}$}, \quad \quad [\![\partial_n u ]\!] = 0 \quad \text{on $\mathcal{I}$}. \end{align} \]

Variational Formulation#

The variational formulation reads

\[ \begin{align} \text{find $u \in V$ such that} \quad a(u,v) = l(v) \quad \forall v \in V, \end{align} \]

where

  • \(V \subset H^1(\Omega)\),

  • \(a(u,v) := \int_{\Omega} \nabla u \cdot \nabla v ~ d\Omega + \int_{\mathcal{I}} \left( \kappa [\![ u ]\!] ~ [\![ v ]\!] - [\![ u ]\!] ~ \partial_n v - \partial_n u ~ [\![ v ]\!] \right) ~ d\mathcal{I} \),

  • \(l(v) := \int_{\Omega} f v ~ d\Omega\).

Formal Model#

from sympde.expr     import BilinearForm, LinearForm, integral, Norm
from sympde.expr     import find, EssentialBC
from sympde.topology import ScalarFunctionSpace, Square, Domain, element_of
from sympde.calculus import grad, dot
from sympde.calculus import jump, avg, minus, plus, Dn
from sympde.core     import Constant
from sympde.topology import NormalVector

from sympy import pi, sin

from psydac.api.discretization import discretize

# ... create a domain as the union of two subdomains
A = Square('A',bounds1=(0, 0.5), bounds2=(0, 1))
B = Square('B',bounds1=(0.5, 1.), bounds2=(0, 1))

connectivity = [((0,0,1),(1,0,-1))]
subdomains = [A,B]
domain = Domain.join(subdomains, connectivity, 'domain')
# ...

# one sided approximation of the normal flux on the interface
nn   = NormalVector('nn')
#Dn_I = lambda u: 0.5*(dot(grad(minus(u)), nn) + dot(grad(plus(u)), nn))
Dn_I = lambda u: 0.5*(plus(Dn(u)) + minus(Dn(u)))

# internal interafaces of the domain
I = domain.interfaces

kappa = Constant('kappa', is_real=True)

V = ScalarFunctionSpace('V', domain)

x,y = domain.coordinates

u,v = [element_of(V, name=i) for i in ['u', 'v']]

# bilinear form
a = BilinearForm((u,v),
                   integral(domain, dot(grad(u),grad(v)))            
                 + integral(I, kappa * jump(u)*jump(v) - Dn_I(u)*jump(v) - jump(u)*Dn_I(v))
                )

# linear form
f = 2*pi**2*sin(pi*x)*sin(pi*y)
l = LinearForm(v, integral(domain, f*v))

# Dirichlet boundary conditions
bc = [EssentialBC(u, 0, domain.boundary)]

# Variational problem
equation   = find(u, forall=v, lhs=a(u, v), rhs=l(v), bc=bc)

Discretization#

degree = [2,2]
ncells = [8,8]
# Create computational domain from topological domain
domain_h = discretize(domain, ncells=ncells, comm=None)

# Create discrete spline space
Vh = discretize(V, domain_h, degree=degree)

# Discretize equation
equation_h = discretize(equation, domain_h, [Vh, Vh])

Solving the PDE#

equation_h.set_solver('gmres', info=False, tol=1e-8)
uh = equation_h.solve(kappa=1e3)

Computing the Error Norm#

Computing the \(L^2\) norm#

ue = sin(pi*x)*sin(pi*y)

u = element_of(V, name='u')

# create the formal Norm object
l2norm = Norm(u - ue, domain, kind='l2')

# discretize the norm
l2norm_h = discretize(l2norm, domain_h, Vh)

# assemble the norm
l2_error = l2norm_h.assemble(u=uh)

# print the result
print(l2_error)
0.0002193529744905818

Remarks#

There should be a problem in sympde, since computing the TerminalExpr of the bilinear form gives

>>> from sympde.expr import TerminalExpr
>>> expr = TerminalExpr(a, domain)
>>> print(expr)

(InterfaceExpression(A|B, 
                     -kappa*MinusInterfaceOperator(u)*PlusInterfaceOperator(v) 
                     + (0.5*dx1(MinusInterfaceOperator(u))*nn[0] + 0.5*dx2(MinusInterfaceOperator(u))*nn[1])*PlusInterfaceOperator(v) 
                     + (-0.5*dx1(PlusInterfaceOperator(v))*nn[0] - 0.5*dx2(PlusInterfaceOperator(v))*nn[1])*MinusInterfaceOperator(u)
                    ), 
 InterfaceExpression(A|B, 
                     -kappa*MinusInterfaceOperator(v)*PlusInterfaceOperator(u) 
                     + (0.5*dx1(MinusInterfaceOperator(v))*nn[0] + 0.5*dx2(MinusInterfaceOperator(v))*nn[1])*PlusInterfaceOperator(u) 
                     + (-0.5*dx1(PlusInterfaceOperator(u))*nn[0] - 0.5*dx2(PlusInterfaceOperator(u))*nn[1])*MinusInterfaceOperator(v)
                    ), 
 DomainExpression(A, 
                  dx1(u)*dx1(v) + dx2(u)*dx2(v)
                 ), 
 DomainExpression(B, 
                  dx1(u)*dx1(v) + dx2(u)*dx2(v)
                 ), 
 BoundaryExpression(A_\Gamma_2, 
                    kappa*u*v 
                    + u*(-0.5*dx1(v)*nn[0] - 0.5*dx2(v)*nn[1]) 
                    + v*(-0.5*dx1(u)*nn[0] - 0.5*dx2(u)*nn[1])
                   ), 
 BoundaryExpression(B_\Gamma_1, 
                    kappa*u*v 
                    + u*(-0.5*dx1(v)*nn[0] - 0.5*dx2(v)*nn[1]) 
                    + v*(-0.5*dx1(u)*nn[0] - 0.5*dx2(u)*nn[1])
                   )
)

The BoundaryExpression terms should not appear in the TerminalExpr.