This tutorial shows how to create and use a Calderón projectors and multitrace operators for Maxwell problems, as described in section 4 of Scroggs et al (2017).
For Maxwell problems, we define the multitrace operator by
where and are the magnetic and electric field boundary operators.
The interior Calderón projector is defined by
and the exterior Calderón projector is defined by
If and are valid Cauchy data for a exterior Maxwell problem, then
It follows from the property that for any tangential functions and , the functions and defined by
are valid Cauchy data.
Let and be two boundary operators. If the product were discretised, the result would , where is the identity matrix between the range and dual_to_range spaces of .
If standard Raviart–Thomas (RT) and Nédélec (NC) basic functions are used to discretise , the matrix will be numerically singular. To avoid this problem, a stable pairing of RT and Buffa–Christiansen (BC) functions should be used.
In Bempp, the function bempp.api.operators.boundary.maxwell.multitrace_operator will automatically use the correct spaces so that all terms in the product are stable. This is discussed in more detail in section 4 of Scroggs et al (2017).
First, we import everything we need and set the wavenumber.
from bempp.api.operators.boundary import maxwell
from bempp.api.operators.boundary import sparse
import numpy as np
k = 2
We make the meshed cube on which we will define our operators.
Next, we make the multitrace operator and identity on the grid. Here, we must rell the multitrace identity to use vector "maxwell" spaces instead of the default scalar spaces used for Laplace and Helmholtz.
identity = sparse.multitrace_identity(grid, spaces='maxwell')
We define the exterior Calderón projector, .
We define grid functions using a Python function. These functions will not be valid Cauchy data.
result[:] = np.cross(np.array([1,0,0]),n)
electric_trace = bempp.api.GridFunction(
magnetic_trace = bempp.api.GridFunction(
We now apply the Calderón projector to the functions twice.
traces_2 = calderon * traces_1
As explained above, the functions in traces_1 should be valid Cauchy data. Additionally, traces_2 should be equal to traces_1 because . To verify this, we plot the relative difference between the functions.
magnetic_error = (traces_2 - traces_1).l2_norm() / traces_1.l2_norm()
print("Electric error is ", electric_error)
print("Magnetic error is ", magnetic_error)
Magnetic error is 0.00740308354962
As expected, the errors are small.