This page was generated from unit-11.1-fmm/BiotSavart.ipynb.

11.1.4 Biot Savart

Let \(G(x,y) = \frac{1}{4 \pi} \frac{\exp (i k |x-y|)}{|x-y|}\) be Green’s function for the Helmholtz equation.

For a given current path \(j\) along a Curve \(C\), the magnetic field in vacuum on the full space \({\mathbb R}^3\) is

\[H(x) = \int_C j(y) \times \nabla_y G(x,y) dl_y\]
[1]:
from ngsolve import *
from netgen.occ import *
from ngsolve.webgui import Draw

vismesh = Mesh(OCCGeometry(Box((-5,-5,-5), (5,5,5))).GenerateMesh(maxh=1))
for l in range(0):
    vismesh.Refine()
Draw (vismesh)
[1]:
WebGLScene
[2]:
from ngsolve.bem import BiotSavartCF, BiotSavartSingularMLCF, BiotSavartRegularMLCF

We evaluate the single layer integral using numerical integration on the surface mesh. Thus, we get a sum of many Green’s functions, which is compressed using a multilevel-multipole.

[3]:
kappa = 0.01*pi
mp = BiotSavartSingularMLCF((0,0,0), r=5, kappa=kappa)
mp.expansion.AddCurrent( (1,0,-1), (1,0,1), 1, num=100)
mp.expansion.AddCurrent( (1,0, 1), (-1,0,1), 1, num=100)
mp.expansion.AddCurrent( (-1,0, 1), (-1,0,-1), 1, num=10)
mp.expansion.AddCurrent( (-1,0, -1), (1,0,-1), 1, num=100)

regmp = mp.CreateRegularExpansion((0,0,0),r=5)
[4]:
clipping = { "function" : False,  "pnt" : (0,0,0), "vec" : (0,0,-1) }
Draw (regmp.real, vismesh, min=0, max=1, order=2,  vectors={"grid_size" : 40, "offset" : 0 }, clipping=clipping);
[5]:
visplane = WorkPlane(Axes( (0,0.2,0), Z, X)).RectangleC(5,5).Face()
vismesh2 = Mesh(OCCGeometry(visplane).GenerateMesh(maxh=0.5))
Draw (regmp.real[0], vismesh2, min=-0.1, max=0.1, order=10);
[6]:
kappa = 0.01*pi
mp = BiotSavartSingularMLCF((0,0,0), r=5, kappa=kappa)

coil = Cylinder((0,-1,0), Y, r=1, h=1, mantle="outer") - Cylinder((0,-1,0), Y, r=0.5, h=1)
coilmesh = Mesh(OCCGeometry(coil).GenerateMesh(maxh=0.3)).Curve(3)
Draw (coilmesh)

current = CF((z,0,-x))
current /= Norm(current)
mp.expansion.AddCurrentDensity(current, coilmesh.Materials(".*"))
# mp.expansion.AddCurrentDensity(current, coilmesh.Boundaries("outer"))

regmp = mp.CreateRegularExpansion((0,0,0),r=5)
[7]:
clipping = { "function" : False,  "pnt" : (0,0,0), "vec" : (0,0,-1) }
Draw (regmp.real, vismesh, min=0, max=0.2, order=2,  vectors={"grid_size" : 40, "offset" : 0 }, clipping=clipping);
[8]:
Draw (regmp.real[0], vismesh2, min=-0.1, max=0.1, order=10);

the spikes inside the coil domain stem from numerical integration of the current source.

[9]:
from ngsolve.webgui import FieldLines
N=14
fl = FieldLines(mp.real, vismesh.Materials('.*'), num_lines=N**3/20, length=1)
[10]:
clipping = { "function" : False,  "pnt" : (0,0,0), "vec" : (0,0,-1) }
Draw (regmp.real, vismesh, min=0, max=0.5, order=2,  vectors={"grid_size" : 40, "offset" : 0 }, clipping=clipping);
[11]:
settings = {"objects": { "Surface":False, "Wireframe":False}}
clipping = {"function" : False, "pnt" : (0,0,0), "vec" : (0,0,-1) }
vectors = {"grid_size" : 100, "offset": 0.2}
Draw (mp.real, vismesh, "X", objects=[fl], min=0, max=0.5, autoscale=False, settings=settings, vectors=vectors, clipping=clipping);

Biot-Savart with MaxwellDL integral operator

[12]:
from ngsolve.bem import *

fescur = HDiv(coilmesh)
u,v = fescur.TnT()
gfcur = GridFunction(fescur)
gfcur.Set(current)

potCF = MaxwellDL(u*dx, 1e-4)(gfcur)
potCF.BuildLocalExpansion(vismesh2.Boundaries(".*"))

Draw(potCF[0], vismesh2, min=-0.1, max=0.1, order=10);

Biot-Savart with a line current

[13]:
wirebox = Box((-2.5,-2.5,-2.5), (2.5,2.5,2.5))
wp = WorkPlane(Axes((0,0,0), Y, X))
wp.Circle(1)
wire = wp.Wires()[0]
for e in wire.edges:
    e.name = "wire"
    e.maxh = 0.05
wiremesh = Mesh(OCCGeometry(Glue([wirebox, wire])).GenerateMesh(maxh=1))
Draw(wiremesh)
[13]:
WebGLScene
[14]:
fesline = HCurl(wiremesh, order=2)
uline = fesline.TrialFunction()
gfline = GridFunction(fesline)
gfline.Set(current, definedon=wiremesh.BBoundaries("wire"))

potline = MaxwellDL(uline*dl(definedon=wiremesh.BBoundaries("wire")), 1e-4)(gfline)

# H_y = I R^2 / (2 (R^2+y^2)^(3/2)) (radius R, current I)
for yy in [0, 0.5, 1, 2]:
    Hnum = potline(wiremesh(0,yy,0))[1].real
    Hex = 1/(2*(1+yy**2)**1.5)
    print (f"y = {yy:3.1f}:   H_y = {Hnum:.5f}   exact: {Hex:.5f}")
y = 0.0:   H_y = 0.50005   exact: 0.50000
y = 0.5:   H_y = 0.35776   exact: 0.35777
y = 1.0:   H_y = 0.17674   exact: 0.17678
y = 2.0:   H_y = 0.04470   exact: 0.04472
[15]:
potline.BuildLocalExpansion(vismesh2.Boundaries(".*"))
Draw (potline[0].real, vismesh2, min=-1, max=1, order=10);
Draw (potline[1].real, vismesh2, min=-1, max=1, order=10);
[ ]: