#!/usr/bin/env python
# coding: utf-8
# This tutorial forms part of the documentation for Bempp. Find out more at bempp.com
# Weakly imposing a Dirichlet boundary condition
# ==========================
#
# This tutorial shows how to implement the weak imposition of a Dirichlet boundary condition, as proposed in the paper Boundary Element Methods with Weakly Imposed Boundary Conditions (2019).
# First, we import Bempp and NumPy.
# In[1]:
import bempp.api
import numpy as np
# Next, we define the grid for our problem, and the function spaces that we will use. In this example, we use a sphere with P1 and DUAL0 function spaces.
# In[2]:
h = 0.3
grid = bempp.api.shapes.sphere(h=h)
p1 = bempp.api.function_space(grid, "B-P", 1)
dual0 = bempp.api.function_space(grid, "DUAL", 0)
# Next, we define the blocked operators proposed in the paper:
# $$
# \left(\begin{pmatrix}-\mathsf{K}&\mathsf{V}\\\mathsf{W}&\mathsf{K}'\end{pmatrix}+
# \begin{pmatrix}\tfrac12\mathsf{Id}&0\\\beta\mathsf{Id}&-\tfrac12\mathsf{Id}\end{pmatrix}\right)
# \begin{pmatrix}u\\\lambda\end{pmatrix}
# =
# \begin{pmatrix}g_\text{D}\\\beta g_\text{D}\end{pmatrix},
# $$
# where $\beta>0$ is a parameter of our choice. In this example, we use $\beta=0.1$.
# In[3]:
beta = 0.1
multi = bempp.api.BlockedOperator(2,2)
multi[0,0] = -bempp.api.operators.boundary.laplace.double_layer(p1, p1, dual0)
multi[0,1] = bempp.api.operators.boundary.laplace.single_layer(dual0, p1, dual0)
multi[1,0] = bempp.api.operators.boundary.laplace.hypersingular(p1, dual0, p1)
multi[1,1] = bempp.api.operators.boundary.laplace.adjoint_double_layer(dual0, dual0, p1)
diri = bempp.api.BlockedOperator(2,2)
diri[0,0] = 0.5 * bempp.api.operators.boundary.sparse.identity(p1, p1, dual0)
diri[1,0] = beta * bempp.api.operators.boundary.sparse.identity(p1, dual0, p1)
diri[1,1] = -0.5 * bempp.api.operators.boundary.sparse.identity(dual0, dual0, p1)
# Next, we define the function $g_\text{D}$, and define the right hand side.
#
# Here, we use $$g_\text{D}=\sin(\pi x)\sin(\pi y)\sinh(\sqrt2\pi z),$$ as in section 5 of the paper.
# In[4]:
def f(x, n, d, res):
res[0] = np.sin(np.pi*x[0]) * np.sin(np.pi*x[1]) * np.sinh(np.sqrt(2)*np.pi*x[2])
f_fun = bempp.api.GridFunction(p1, fun=f)
rhs = [2*diri[0,0]*f_fun, diri[1,0]*f_fun]
# Now we solve the system. We set `use_strong_form=True` to activate mass matrix preconditioning.
# In[5]:
sol, info, it_count = bempp.api.linalg.gmres(multi+diri, rhs, return_iteration_count=True, use_strong_form=True)
print("Solution took",it_count,"iterations")
# For this problem, we know the analytic solution. We compute the error in the $\mathcal{B}_\text{D}$ norm:
# In[6]:
def g(x, n, d, res):
grad = np.array([
np.cos(np.pi*x[0]) * np.sin(np.pi*x[1]) * np.sinh(np.sqrt(2)*np.pi*x[2]) * np.pi,
np.sin(np.pi*x[0]) * np.cos(np.pi*x[1]) * np.sinh(np.sqrt(2)*np.pi*x[2]) * np.pi,
np.sin(np.pi*x[0]) * np.sin(np.pi*x[1]) * np.cosh(np.sqrt(2)*np.pi*x[2]) * np.pi * np.sqrt(2)
])
res[0] = np.dot(grad, n)
g_fun = bempp.api.GridFunction(dual0, fun=g)
e_fun = [sol[0]-f_fun,sol[1]-g_fun]
error = 0
# V norm
slp = bempp.api.operators.boundary.laplace.single_layer(dual0, p1, dual0)
hyp = bempp.api.operators.boundary.laplace.hypersingular(p1, dual0, p1)
error += np.sqrt(np.dot(e_fun[1].coefficients.conjugate(),(slp * e_fun[1]).projections(dual0)))
error += np.sqrt(np.dot(e_fun[0].coefficients.conjugate(),(hyp * e_fun[0]).projections(p1)))
# D part
error += beta**.5 * e_fun[0].l2_norm()
print("Error:",error)