Simple Sheets
Download this Jupyter File
Constrained Eddy Current Problem in 2D with the Magnetic Vector Potential¶
\begin{align*} \int_\Omega \nu \nabla u \cdot \nabla u' + j\omega\sigma u u' \;d\Omega = 0 \end{align*} with $u = B_0 x$ or $u = B_0 y$ on the boundary $\Gamma_D = \Gamma_{left}\cup\Gamma_{right}$ or $\Gamma_D = \Gamma_{top}\cup\Gamma_{bottom}$
and the constraint for each sheet: \begin{align*} \int_{\Omega_{c, i} } J \;d\Omega = 0 \end{align*}
results in an extension of the base with lagrange multipliers
\begin{align*} \int_\Omega \nu \nabla u \cdot \nabla u' + j\omega\sigma (u+\lambda_i)(u'+\lambda_i') \;d\Omega = 0 \end{align*}
In [1]:
from netgen.occ import *
import numpy as np
from ngsolve import *
from ngsolve.webgui import Draw
maxh = 1/10
freq = 50
mu0 = 4e-7*np.pi
mu_Fe = mu0 *1000
mu_Air = mu0
sigma_Fe =2e6
sigma_Air = 1e-3
omega = freq*2*np.pi
delta = np.sqrt(2/(sigma_Fe*omega*mu_Fe))
In [2]:
Nsheets = 6
ff = 0.5
d = delta/2
order0 = 2
B0 = 1
use_symmetry = True
use_insulation=False
In [3]:
dFe = d*ff
d0 = d*(1-ff)
H_core = Nsheets*dFe + (Nsheets-1)*d0
W_core = H_core
W = 10*W_core
H = 10*H_core
wp = WorkPlane()
outer = wp.RectangleC(W, H).Face()
outer.name = "air"
outer.edges.Max(X).name = "right"
outer.edges.Min(X).name = "left"
outer.edges.Max(Y).name = "top"
outer.edges.Min(Y).name = "bottom"
if use_insulation:
insulation = wp.RectangleC(W_core, H_core).Face()
insulation.name = "insulation"
insulation.edges.Max(X).name = "insulation_right"
insulation.edges.Min(X).name = "insulation_left"
insulation.edges.Max(Y).name = "insulation_top"
insulation.edges.Min(Y).name = "insulation_bottom"
x_pos = - W_core/2
rec_sheets =[]
for i in range(Nsheets):
wp.MoveTo(x_pos, -H_core/2)
rec_sheets.append(wp.Rectangle(dFe, H_core).Face())
rec_sheets[-1].name = f"iron{i}"
rec_sheets[-1].edges.name = "interface"
rec_sheets[-1].edges.maxh = delta/10
x_pos += d
if use_symmetry:
wp.MoveTo(0, 0)
cutting = wp.Rectangle(W, H).Face()
cutting.edges.Min(X).name = "symmetry_left"
cutting.edges.Min(Y).name = "symmetry_bottom"
all_sheets = Glue(rec_sheets)
if use_insulation:
geo = Glue([cutting * (outer - insulation), cutting * (insulation - all_sheets), cutting * all_sheets])
else:
geo = Glue([cutting * (outer - all_sheets), cutting * all_sheets])
else:
geo = Glue([outer - Glue(rec_sheets), Glue(rec_sheets)])
meshRef = Mesh(OCCGeometry(geo, dim=2).GenerateMesh(maxh=delta))
settings = {"Objects":{"Wireframe":False}}
Draw(meshRef.MaterialCF({"iron.*":1, "air":2}, default=0), meshRef)
#Draw(x, meshRef)
print("domains", set(meshRef.GetMaterials()))
print("bnds", set(meshRef.GetBoundaries()))
print("penetration depth", delta)
domains {'iron5', 'iron3', 'air', 'iron4'} bnds {'top', 'interface', 'symmetry_bottom', 'right', 'symmetry_left'} penetration depth 0.0015915494309189533
In [4]:
mu = meshRef.MaterialCF({"iron.*":mu_Fe, "air":mu_Air, "insulation":mu_Air}, default=0)
sigma = meshRef.MaterialCF({"iron.*":sigma_Fe}, default=0)
nu = 1/mu
rho = 1/sigma
from myPackage import drawBndAll
#drawBndAll(meshRef, drawFunc=Draw, block=False)
In [5]:
excitation_orientation = "y"
# ------------------------------------------------------------------------------
# --- Excitation
# ------------------------------------------------------------------------------
def solveWithA(mesh, order0, omega):
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# +++ reference solution
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
print("order0", order0)
print("numSheets", Nsheets)
dir_A_sym = ""
if excitation_orientation == "x":
dir_A = "top|bottom"
if use_symmetry:
dir_A_sym = "|symmetry_bottom"
else:
dir_A = "left|right"
if use_symmetry:
dir_A_sym = "|symmetry_left"
VA = H1(mesh,order=order0, complex=True, dirichlet=dir_A + dir_A_sym)
VNum = []
for i in range(0 if not use_symmetry else int(Nsheets/2), Nsheets):
VNum.append(NumberSpace(mesh, complex=True, definedon=mesh.Materials(f"iron{i}")))
V = FESpace([VA] + VNum)
ndof = V.ndof
print(f"VA :{VA.ndof}")
print(f"ndof :{ndof}")
# Gridfunctions
sol_ref = GridFunction(V, "sol")
A_ref = sol_ref.components[0]
trials = V.TrialFunction()
tests = V.TestFunction()
uA = trials[0]
vA = tests[0]
# ------------------------------------------------------------------------------
# Matrix
# ------------------------------------------------------------------------------
with TaskManager():
# Bilinear form with
ah_ref = BilinearForm(V, symmetric=True)
# A:
ah_ref += nu*grad(uA) * grad(vA) * dx
# lagrange multipliers
for i in range(0 , Nsheets if not use_symmetry else int(Nsheets/2)):
domain_name = f"iron{i + (0 if not use_symmetry else int(Nsheets/2))}"
ah_ref += 1j*omega * sigma_Fe * (uA + trials[1+i]) * (vA + tests[1+i]) * dx(domain_name)
prec = Preconditioner(ah_ref, type="direct")
ah_ref.Assemble()
print(ah_ref.mat.AsVector().Norm())
f_ref = LinearForm(V)
f_ref.Assemble()
# ------------------------------------------------------------------------------
# ------ Solve It
# ------------------------------------------------------------------------------
with TaskManager():
if excitation_orientation == "x":
A_ref.Set(B0*y, BND, definedon=mesh.Boundaries(dir_A))
else:
A_ref.Set(B0*x, BND, definedon=mesh.Boundaries(dir_A))
# solve it
solvers.BVP(bf=ah_ref, lf=f_ref, gf=sol_ref, pre=prec, maxsteps=5)
gradA = grad(A_ref)
B = CF((gradA[1], -gradA[0]))
H = 1/mu * B
E = -1j*omega*(sum(sol_ref.components))
J = sigma * E
Q = Integrate(1/2 * InnerProduct(H, B), mesh).real
P = Integrate(1/2 * InnerProduct(E, J), mesh).real
if use_symmetry:
Q, P = Q*4, P*4
return A_ref, B, E, H, J, P, Q, sol_ref
In [6]:
A, B_A, E_A, H_A, J_A, P_A, Q_A, sol_A = solveWithA(meshRef, order0, omega)
print(f"eddy current losses\t{P_A:10.3f}")
print(f"reactive energy\t\t{Q_A:10.3f}")
order0 2 numSheets 6 VA :2039 ndof :2042 59228946.21955126 CG iteration 1, residual = 76.81714315775568 CG iteration 2, residual = 3.1962311498725343e-13 eddy current losses 0.251 reactive energy 746.039
In [7]:
settings.update({"camera":{"transformations":[{"type":"move", "dir":(0.3, 0.3, 1.000), "dist":1}]}})
scene = Draw(J_A.imag, meshRef, settings=settings)
Draw(B_A.real, meshRef, vectors=True, settings=settings, min = 0, max=4, autoscale=False )
Out[7]:
BaseWebGuiScene
T Formulation¶
In [8]:
def solveWithTPhi(mesh, order0, omega, nograds=False):
# ------------------------------------------------------------------------------
# --- materials
# ------------------------------------------------------------------------------
mu = mesh.MaterialCF({"air": mu_Air, "iron.*": mu_Fe}, default=0.001)
nu = 1/mu
sigma = mesh.MaterialCF({"air": sigma_Air, "iron.*": sigma_Fe}, default=0.001)
rho = 1/sigma
# ------------------------------------------------------------------------------
# --- fem
# ------------------------------------------------------------------------------
dir_T = "interface"
dir_Phi_sym = ""
if excitation_orientation == "x":
dir_Phi = "left|right"
if use_symmetry:
dir_Phi_sym = "|symmetry_left"
dir_T += "|symmetry.*"
else:
dir_Phi = "top|bottom"
if use_symmetry:
dir_Phi_sym = "|symmetry_bottom"
dir_T += "|symmetry.*"
fesT = HCurl(mesh, order=order0, dirichlet=dir_T, complex=True, definedon=mesh.Materials("iron.*"), nograds=nograds)
fesPhi = H1(mesh, order=order0+1, dirichlet=dir_Phi+dir_Phi_sym, complex=True)
fes = FESpace([fesPhi, fesT])
trials = fes.TrialFunction()
tests = fes.TestFunction()
uPhi, vPhi = trials[0], tests[0]
uT, vT = trials[1], tests[1]
sol = GridFunction(fes, "sol")
Phi = sol.components[0]
T = sol.components[1]
# ------------------------------------------------------------------------------
# --- fields
# ------------------------------------------------------------------------------
H = T - grad(Phi)
B = mu * H
J = curl(T)
E = rho * J
# ------------------------------------------------------------------------------
# --- formulation
# ------------------------------------------------------------------------------
ah = BilinearForm(fes, symmetric=True)
ah += rho * curl(uT) * curl(vT) * dx("iron.*")
ah += 1j * omega * mu * (uT - grad(uPhi)) * (vT - grad(vPhi)) * dx
ah += 1e-1 * uPhi * vPhi * dx("iron.*")
prec = Preconditioner(ah, type="direct")
f = LinearForm(fes)
ah.Assemble()
f.Assemble()
if excitation_orientation == "x":
Phi.Set( x * B0 * 1/mu0, BND, definedon=mesh.Boundaries(dir_Phi))
else:
Phi.Set( y * B0 * 1/mu0, BND, definedon=mesh.Boundaries(dir_Phi))
solvers.BVP(bf = ah, lf= f, pre=prec, gf=sol, maxsteps=10)
# sol.vec.data = ah.mat.Inverse(freedofs=fes.FreeDofs()) * f.vec
Q = Integrate(1/2 * InnerProduct(H, B), mesh).real
P = Integrate(1/2 * InnerProduct(E, J), mesh).real
if use_symmetry:
Q, P = 4*Q, 4*P
return T, Phi, B, E, H, J, P, Q, sol
T, Phi, B_T, E_T, H_T, J_T, P_T, Q_T, sol_T = solveWithTPhi(meshRef, order0, omega)
CG iteration 1, residual = 1308.3639121271974 CG iteration 2, residual = 1.3778666050097097e-07
In [9]:
Draw(J_T.imag, meshRef, settings=settings)
Draw(B_T.real, meshRef, vectors=True, settings=settings, min = 0, max=4, autoscale=False )
Out[9]:
BaseWebGuiScene
In [10]:
print(f"A-formulation eddy current losses\t{P_A:10.3f}")
print(f"reactive energy\t\t{Q_A:10.3f}")
print(f"ndof\t\t{sol_A.space.ndof}")
print(f"ndof A\t\t{A.space.ndof}")
print("")
print(f"TPhi-formulation eddy current losses\t{P_T:10.3f}")
print(f"reactive energy\t\t{Q_T:10.3f}")
print(f"ndof\t\t{sol_T.space.ndof}")
print(f"T ndof\t\t{T.space.ndof}")
print(f"Phi ndof\t\t{Phi.space.ndof}")
A-formulation eddy current losses 0.251 reactive energy 746.039 ndof 2042 ndof A 2039 TPhi-formulation eddy current losses 0.263 reactive energy 778.748 ndof 7194 T ndof 2669 Phi ndof 4525
In [ ]:
In [ ]: