from netgen.occ import *
import netgen.meshing as meshing
from ngsolve import *
from ngsolve.webgui import Draw
from ngsolve.bem import *
from ngsolve import Projector, Preconditioner
from ngsolve.krylovspace import CG, GMRes
keys: PEC scattering, single and double layer potential, MoM, unknown Neumann trace (surface current density)
Maxwell PEC Direct method#
We consider a perfect conductor \(\Omega \subset \mathbb R^3\) emitting an electric field into \(\Omega^c\). The scattered electric field \(\boldsymbol E\) solves the following Dirichlet boundary value problem:
The electric field \(\boldsymbol E\) is given by
Define the geometry of the perfect conductor \(\Omega\), create a mesh and test and trial functions according to the mesh:
sp = Sphere( (0,0,0), 1)
mesh = Mesh( OCCGeometry(sp).GenerateMesh(maxh=1, perfstepsend=meshing.MeshingStep.MESHSURFACE)).Curve(3)
order = 3
fesHCurl = HCurl(mesh, order=order, complex=True)
fesHDiv = HDivSurface(mesh, order=order, complex=True)
uHCurl,vHCurl = fesHCurl.TnT() # H(curl_Gamma) trial for Dirichlet data ( (nxE)xn )
uHDiv,vHDiv = fesHDiv.TnT() # H(div_Gamma) trial space for Neumann data ( nx curlE ) and test space for BIE
print ("ndof HCurl = ", fesHCurl.ndof)
print ("ndof HDiv = ", fesHDiv.ndof)
ndof HCurl = 1568
ndof HDiv = 1568
Consider as incidence an incoming plane wave and compute the given data \(\boldsymbol m\) :
eps0 = 8.854e-12
mu0 = 4*pi*1e-7
omega = 1.5e9
kappa = omega*sqrt(eps0*mu0)
print("kappa = ", kappa)
E_inc = CF((1,0,0))*exp( -1j * kappa * z )
n = specialcf.normal(3)
m = GridFunction(fesHCurl)
m.Set( -E_inc, definedon=mesh.Boundaries(".*"), dual=True) # Hcurl
kappa = 5.0034083602476045
Boundary Integral Equation
We carefully apply the tangential trace to (1) and obtain a boundary integral equation for \(\boldsymbol j\):
The discretisation of the above variational formulation leads to the system of linear equations
where
\(\mathrm V\) is the Maxwell single layer operator.
\(\mathrm K\) is the Maxwell double layer operator.
\(\mathrm{M}\) is the mass matrix.
The Maxwell single and double layer potential operators are derivates of Helmholtz operators (details are given below) and thus, in NGSBEM we use the Helmholtz operators as building blocks for Maxwell operators:
V = kappa * HelmholtzSL(u*ds, kappa)*v*ds
- 1/kappa * HelmholtzSL(div(u)*ds, kappa)*div(v)*ds
K = HelmholtzDL( u.Operator("rotated_trace") * ds, kappa) * v * ds
intorder = 2 * order + 6
with TaskManager():
M = BilinearForm( uHCurl.Trace()*vHDiv.Trace()*ds(bonus_intorder=3)).Assemble() # <Hcurl, Hdiv>
with TaskManager():
V1 = HelmholtzSL( uHDiv.Trace()*ds(bonus_intorder=6) , kappa) * vHDiv.Trace() * ds(bonus_intorder=6)
V2 = HelmholtzSL( div(uHDiv.Trace()) * ds(bonus_intorder=6), kappa) * div(vHDiv.Trace()) * ds(bonus_intorder=6)
V = kappa * V1.mat - 1/kappa * V2.mat
K = HelmholtzDL(uHCurl.Operator("rotated_trace") * ds(bonus_intorder=6), kappa) * vHDiv.Trace() * ds
# solve direct formulation for j, i.e., Neumann data of scattered electric field E
j = GridFunction(fesHDiv)
pre = BilinearForm( uHDiv.Trace() * vHDiv.Trace() *ds).Assemble().mat.Inverse(freedofs=fesHDiv.FreeDofs())
with TaskManager():
rhs = ( (0.5 * M.mat - K.mat ) * m.vec).Evaluate()
GMRes(A = V, pre=pre, b = rhs, x=j.vec, tol=1e-8,
maxsteps=5000, printrates=False)
Draw(j[1].real, mesh, draw_vol=False, order=3);
Details: Explicit Representation of the Maxwell Layer Potentials
For a trial function \(\boldsymbol u_j \in H^{-\frac12}(\mathrm{div}_\Gamma,\Gamma)\) and a test function \(\boldsymbol v_i \in H^{-\frac12}(\mathrm{div}_\Gamma,\Gamma)\) the Maxwell single layer potential operator entry \(V_{ij}\) can formally written as
For a trial function \(\boldsymbol u_j \in H^{-\frac12}(\mathrm{curl}_\Gamma,\Gamma)\) and a test function \(\boldsymbol v_i \in H^{-\frac12}(\mathrm{div}_\Gamma,\Gamma)\) the Maxwell double layer potential operator entry \(K_{ij}\) can formally written as
References (theoretical and numerical results):