Electromagnetic scattering from a screen


In this tutorial, we consider the scattering of an electromagnetic wave from a perfectly conducting screen $\Gamma:=[-2,2]\times[-1,1]\times0$. The time-harmonic Maxwell equation for the electric field $\mathbf{E}$ reduces to

$$ \nabla\times\nabla\times \mathbf{E} -k^2 \mathbf{E} = 0 $$

in $\mathbb{R}^3\backslash\Gamma$, where $k:=2\pi/\lambda$ is the wavenumber and $\lambda$ is the wavelength. The electric field $\mathbf{E}$ is the sum of the incident field $\mathbf{E}^\text{inc}$ and the scattered field $\mathbf{E}^\text{s}$. Here, we use the incident field given by

$$ \mathbf{E}^\text{inc}(\mathbf{x}):=\begin{bmatrix} \mathrm{e}^{\mathrm{i}kz} & 0 & 0 \end{bmatrix}, $$

which is a wave travelling in the $z$ direction and polarised in the $x$ direction. On the screen, the tangential component $\mathbf{E}_\text{t}:=\nu\times \mathbf{E}$ must be zero. Towards infinity we impose the Silver–Müller radiation condition

$$ \lim_{|\mathbf{x}|\rightarrow\infty} |\mathbf{x}|\left(\nabla\times \mathbf{E}^\text{s}\times\frac{\mathbf{x}}{|\mathbf{x}|}-\mathrm{i}k\mathbf{E}^\text{s}\right) = 0, $$ where $\hat{\mathbf{x}}=\mathbf{x}/|\mathbf{x}|$.

The scattered wave $\mathbf{E}^\text{s}$ has the representation

$$ \mathbf{E}^\text{s} = -\mathcal{E}\Lambda, $$

where $\Lambda$ is the jump of the Neumann trace of the scattered field $\mathbf{E}^\text{s}$ across the screen. The Maxwell electric field potential operator $\mathcal{E}$ is defined as

$$ \mathcal{E}(\mathbf{v}):=\mathrm{i}k\int_{\Gamma}g(\mathbf{x},\mathbf{y})\mathbf{v}(\mathbf{y})\mathrm{d}\mathbf{y}- \frac1{\mathrm{i}k}\nabla_{\mathbf{x}}\int_{\Gamma}g(\mathbf{x},\mathbf{y})(\nabla_{\Gamma}\cdot\mathbf{v})(\mathbf{y})\mathrm{d}\mathbf{y} $$ with $g(\mathbf{x},\mathbf{y}):=\frac{\mathrm{e}^{\mathrm{i}k|\mathbf{x}-\mathbf{y}|}}{4\pi|\mathbf{x}-\mathbf{y}|}$.

The associated boundary operator is denoted by $\mathsf{E}$. It is defined as average tangential trace of the electric field potential operator from both sides of the screen. The boundary integral equation is now

$$ \mathsf{E}\Lambda = \nu\times \mathbf{E}^\text{inc}. $$ The $-$ sign is missing in comparison to the representation formula since we want to satisfy the boundary conditions for the negative incident wave so that the tangential trace of the total field is zero on the screen.

More details about the mathematical background can be found in Buffa & Hiptmair (2003).


We start with the usual imports.

In [1]:
import bempp.api
import numpy as np

To avoid preconditioning issues, we assemble the operators in dense mode so that we can solve via LU decomposition later on. We will also assemble the potential operator later in dense mode.

For details of how to precondition this problem so that iterative solvers are feasible, see the electric field integral equation (EFIE) tutorial.

In [2]:
bempp.api.global_parameters.assembly.boundary_operator_assembly_type = 'dense'
bempp.api.global_parameters.assembly.potential_operator_assembly_type = 'dense'

We next define the wavenumber of the problem.

In [3]:
wavelength = .5
k = 2 * np.pi / wavelength

We define a structured grid in the $x$-$y$ plane.

In [17]:
grid = bempp.api.structured_grid((-2, -1), (2, 1), (60, 30))

We define the spaces of order 0 Raviart–Thomas div-conforming functions and order 0 Nédélec curl-conforming functions.

In [6]:
div_space = bempp.api.function_space(grid, "RT", 0)
curl_space = bempp.api.function_space(grid, "NC", 0)

Next, we define the Maxwell electric field boundary operator and the identity operator. For Maxwell problems, the domain and range spaces should be div-conforming, while the dual_to_range space should be curl conforming.

In [18]:
elec = bempp.api.operators.boundary.maxwell.electric_field(
    div_space, div_space, curl_space, k)
identity = bempp.api.operators.boundary.sparse.identity(
    div_space, div_space, curl_space)

We create a grid function to represent the incident wave.

In [22]:
def incident_field(x):
    return np.array([np.exp(1j * k * x[2]), 0. * x[2], 0. * x[2]])

def tangential_trace(x, n, domain_index, result):
    result[:] = np.cross(incident_field(x), n, axis=0)

trace_fun = bempp.api.GridFunction(div_space, fun=tangential_trace,

We use a direct LU solver to solve the system. For larger problems, the problem should be preconditioned and an iterative solver used.

In [21]:
from bempp.api.linalg import lu
lambda_data = lu(elec, trace_fun)

Now that the solution $\mathbf{\Lambda}$ is computed, we want to plot the total field. First, we define a grid of points in the $x$-$z$ plane.

In [11]:
nx = 151
nz = 151
extent = 5
x, y, z = np.mgrid[-extent:extent:nx * 1j,
                   -extent:extent:nz * 1j]
points = np.vstack((x.ravel(), y.ravel(), z.ravel()))

We now initialise the electric field potential operator.

In [12]:
slp_pot = bempp.api.operators.potential.maxwell.electric_field(
    div_space, points, k)

The following commands now compute the total field by first computing the scattered field from the representation formula then adding the incident field.

In [13]:
scattered_field_data = -slp_pot * lambda_data
incident_field_data = incident_field(points)
field_data = scattered_field_data + incident_field_data

In electromagnetic scattering it is often useful to visualise the squared electric field density. This value is computed below.

In [14]:
squared_field_density = np.real(np.sum(field_data * field_data.conj(), axis=0))

Finally, we can plot everything using a simple Matplotlib plot.

In [15]:
%matplotlib inline
import matplotlib
from matplotlib import pylab as plt
# Adjust the figure size in IPython
matplotlib.rcParams['figure.figsize'] = (10.0, 8.0) 

plt.imshow(squared_field_density.reshape((nx, nz)).T,
           cmap='coolwarm', origin='lower',
           extent=[-extent, extent, -extent, extent])
plt.title("Squared Electric Field Density")
<matplotlib.text.Text at 0x7fce56505e90>