This is a read only copy of the old FEniCS QA forum. Please visit the new QA forum to ask questions

Eigenmodes of a cavity (Maxwell's equations), which VectorFunctionSpace to use?

0 votes

I am trying to find the eigenmodes of a cavity. In chapter 34 of the FEniCS book (by Lezar and Davidson) there's an example on how to calculate the cutoff-frequencies of a waveguide.
At first this seemed very similar to me, but it seems to be very different for several reasons:

In Ch.34 they consider purely transversal modes, i.e. the e-field is inplane and the out-of-plane component is entirely in the h-field. This makes the separation of the DE very simple.

My case is somewhat different:
I am looking at a cavity of cylindrical symmetry, i.e. rotational symmetry in phi but arbitrary geometry in r and z. I won't dive into special symmetry directly however. The code below is supposed to use a 3D cube instead.

My approach is to define a combined space:

VFS = VectorFunctionSpace(mesh, "CG", 2)    # in-plane (r,z)
FS  = FunctionSpace(mesh, "DG", 2)      # phi
combined_space = VFS * FS

The weak form of the Maxwell equations (assuming PEC boundaries and a closed domain) is
$$ \frac{1}{\mu_0} \int_\Omega (\nabla \times \vec E_u) \cdot (\nabla \times \vec E_v) dx = \omega^2 \int_\Omega \varepsilon \vec E_u \vec E_v dx $$
($E_u$ being the trial function and $E_v$ the test function) and now I run into several problems:

1.) If I run the program for a box (3D but cartesian), it produces incredibly rough and unphysical modes ... and a lot of them. I suspect this is because I'm not using a curl-conforming VectorFunctionSpace. Is it?
2.) Defining boundary conditions is not necessary, since they are already implicitly included in the weak formulation. Adding them in the case described above (CG and DG Function Spaces) doesn't change anything (just as I expected).

Minimal code example

from dolfin import *
import numpy as np
import scipy.constants as co

(a,b,c) = (5e-3, 6e-3, 7e-3)    # edge lengths of resonator box
(nx,ny,nz) = (5,5,2)
mesh = BoxMesh(Point(0,0,0), Point(a,b,c), nx, ny, nz)

VFS = VectorFunctionSpace(mesh, "CG", vector_order) # in-plane (r,z)
FS  = FunctionSpace(mesh, "DG", scalar_order)       # phi
combined_space = VFS * FS

(E_rz_0, E_phi_0)  = TrialFunctions(combined_space)
(E_rz_1, E_phi_1)  = TestFunctions(combined_space)

S_ij = inner(curl(E0), curl(E1)) *dx        # LHS * mu0
T_ij = eps_r*inner(E0, E1) *dx        # RHS / eps0
# hence, eigenvalue is k**2 = omega**2/(eps0*mu0)

S = PETScMatrix()
T = PETScMatrix()
assemble(S_ij, tensor=S)
assemble(T_ij, tensor=T)

eigensolver = SLEPcEigenSolver(S, T)
eigensolver.solve()

if eigensolver.get_number_converged() > 0:
    print("%d results obtained..." % eigensolver.get_number_converged())
    for i in range(eigensolver.get_number_converged()):
        r, c, rx, cx = eigensolver.get_eigenpair(i)
        print("%4d: %10.10f" % (i, r))
else:
    print("No results obtained, eigensolver.get_number_converged() returned 0. Exiting...")
    sys.exit(0)

To look at the results (here, modes 15 and 19), I've got this thing:

for i in [15, 19]: # or range(395,402)
    if i > eigensolver.get_number_converged():
        continue
    r, c, rx, cx = eigensolver.get_eigenpair(i)

    # Initialize function and assign eigenvector
    u = Function(VFS)
    u.vector()[:] = rx
    norm_u = project(sqrt(inner(u, u)))

    # Plot eigenfunction
    plot(norm_u)

interactive()
asked Dec 21, 2015 by cweickhmann FEniCS Novice (550 points)

Could you please tell me how to impliment the boundary condition, which if the problem's boundary condition is A in H0(curl)?

I can do Dirichlet boundary condition, Neumann boundary condition, Robin boundary condition, but not H0(curl).

I will appreciate your any help. Thanks!!!

2 Answers

+2 votes

Hi,

so you get "unphysical" resonances from so called spurious modes. This is due to the fact that you can add the gradient of any arbitrary potential to your solution and it will still fulfill your CurlCurl equation. A way to reduce this is to use Nedelec Ansatzfunctions:

V = FunctionSpace(mesh,"N1curl", 2)

Further i would refrain from using DG in azimutal direction, i dont see the point of it (you could use the easier-to-handle Lagrange).

Using Nedelec Ansatzfunctions shifts the spurious modes to something close to 0. Hence i would recommend using Shift and Invert in SLEPc

tm = SLEPcEigenSolver(S,T)
tm.parameters["tolerance"] = 1e-12
tm.parameters["spectral_shift"] = ((2*pi*target_frequency/c0)**2)
tm.parameters["spectral_transform"] = "shift-and-invert"
tm.parameters["spectrum"] = "target magnitude"

By the way your code can only compute TM Monopole-Modes. For this you dont even need a second function-space. Below you find a minimal example of the code you want to have:

from dolfin import *
#for numerical stuff
import numpy as np

parameters["linear_algebra_backend"] = "PETSc" # Force this backend
parameters["form_compiler"]["representation"] = "quadrature"
parameters["form_compiler"]["cpp_optimize"] = True
parameters["form_compiler"]["cpp_optimize_flags"] = '-O2 -funroll-loops'
parameters["form_compiler"]["representation"] = "quadrature"
set_log_level(INFO)

mue0 = 4*pi*1e-7
eps0 = 8.85418781762e-12

c0 = 1/np.sqrt(eps0*mue0,dtype=float)

shift = ((2*pi*10e9/c0)**2)

p1 = Point(0.0,0.0)
p2 = Point(70e-3,20e-3)

mesh_density = 20

mesh = RectangleMesh(p1,p2, mesh_density, mesh_density)
V2D_N = FunctionSpace(mesh,"N1curl", 2)
N_i = TestFunction(V2D_N)
N_j = TrialFunction(V2D_N) 
r = Expression('x[1]')

def curl_p(w):
    return w[0].dx(1)-w[1].dx(0) 

k_pp = (r*dot(curl_p(N_i),curl_p(N_j)))*dx
m_pp = (r*dot(N_i,N_j))*dx

#Create empty PETSc Matrix
S_tm = PETScMatrix()
T_tm = PETScMatrix()

# Assemble the stiffness matrix (S) and mass matrix (T)  
assemble(k_pp, tensor=S_tm)
assemble(m_pp, tensor=T_tm)

tm = SLEPcEigenSolver(S,T)
tm.parameters["tolerance"] = 1e-12
tm.parameters["spectral_shift"] = ((2*pi*target_frequency/c0)**2)
tm.parameters["spectral_transform"] = "shift-and-invert"
tm.parameters["spectrum"] = "target magnitude"

tm.solve(42)

Btw. your code cannot handle 3D you need a different weak formulation in this case.
Hope this helps for some of your questions.

Kind regards and merry Christmas

Johann

answered Dec 24, 2015 by jh600 FEniCS Novice (900 points)

Hi Johann,

thanks for the comprehensive response.
When I test your code, I see that a large part of the resonance modes are unphysical either...
Some modes (left half) seem to be physical, others (right side) clearly aren't

I had, after I posted my question, played with Nédélec elements, but didn't notice any change.

The reason I'm interested in this is, because I know what mode I want to calculate on an cylindrical volume with arbitrary cross-sectional shape. The mode, however is known and I get the resonance frequency and Q factor from measurement.

Do you reckon this is possible? A teacher in numerics class once told me FEM people in electromagnetics tend to calculate a lot and pick the physical modes like a needle from a hay stack of unphyscial results.

Thanks and happy new year ;-)

Hi,

so short answer, yes this is definitely possible. The code i gave you should be able to do this for TM-Monopole Modes. If you know the modes frequency, just adjust the shift to it and compute only few (2 or 3) modes. then you filter out the spurious modes (by frequency).

A teacher in numerics class once told me FEM people in electromagnetics tend to calculate a lot and pick the physical modes like a needle from a hay stack of unphyscial results.

I would say that in Electromagnetics (at least for everybody i know) this is not correct. You try to shift accordingly, such that you compute only physical resonances.

So i understood you want something like this:
https://jacowfs.jlab.org/conf/y15/ipac15/prepress/WEPMA026.PDF

I have parts of this implemented, here is an example:
https://www.dropbox.com/s/6xz801c4a88zzxp/multicavity.png?dl=0
(Somehow the preview is not working)
I guess that the rest of this conversation does not belong in this forum since its not really Fenics related. Feel free to write me a mail.

Kind regards

Johann

Two updates in this matter:
1.) It seems one big mistake I made was using

VFS = VectorFunctionSpace(mesh, "CG", vector_order)

rather than

VFS = FunctionSpace(mesh, "N1curl", vector_order)

2.) It seems I do get TM modes. Actually I get all of them. However I have trouble getting the TE modes (with a modified formulation)... I'll post this once I get any further insight.

0 votes

So, just in case, anyone needs a similar code, here's what I found out in the past month:

  1. VectorFunctionSpace creates an n-dimensional space of which each component is according to what type of function you hand it. E.g. creating a CG VectorFunctionSpace on a 2D (n=2) domain creates a 21-dimensional space. Creating a N1curl VectorFunctionsSpace on a 2D domain ends up with a 22-dimensional space. I didn't realise this! Creating a N1curl on a 2D domain obviously gives you what you need (2D vector function on 2D domain) already. I get it, and it's like that in the book -- so, my bad ;-)
  2. Johann's code example worked really well for TM modes. But since I needed TE to work as well, I had to dig a bit into literature. Finally I found Ciarlet and Labrunie, 2011 in Differential Equations and Applications. Their formulation seems to work well for both mode types. It's basically a rigorous derivation for the cylindrical case.
  3. The spectral shift idea is pretty nice as well for my application, since I know the resonator to begin with.

I am just wondering: What if you don't know? Is this the way to definitely suppress spurious modes reliably? I've had very few problems with the ansatz mentioned by Ciarlet and Labrunie (which is consistent with some other sources who use Nédélec elements).

Anyhow: Here's a minimal working example in case anyone needs it. It's work in progress and I'm looking for suggestions on how and where to publish similar working code for RF people who want to use FEniCS.
Code on github: https://gist.github.com/anonymous/d8408f81ce1d946b9d7f

answered Jan 26, 2016 by cweickhmann FEniCS Novice (550 points)

Thanks for sharing your code. I am still learning about computational electromagnetism, so pardon my perhaps stupid question. I need to calculate eigenfrequencies of some microwave cavity, so I tried to test it by simulating the eigenfrequency of TM010 mode in a cylindrical cavity. The resulting frequency is exactly as expected (2.4000000000 GHz) but I don't understand the fields: The E field of TM010 should have only z component (with maximum at r=0) but the calculated field has only phi component - it looks more like H field...

Also, I don't understand the boundary integral g_mn, can you please give some pointers as to how it was derived?

Thanks in advance for any help.

Here is my geometry for reference:

lc = 2e-3;
h = 20e-3;
r = 47.809386598e-3;
Point(1) = {0, 0, 0, lc};
Point(2) = {r, 0, 0, lc};
Point(3) = {r, h, 0, lc};
Point(4) = {0, h, 0, lc};

Line(1) = {1, 2};
Line(2) = {2, 3};
Line(3) = {3, 4};
Line(4) = {4, 1};

Line Loop(1) = {1, 2, 3, 4};

Plane Surface(1) = {1};

Physical Surface(1000) = {1};
Physical Line(1001) = {1, 2, 3};
Physical Line(1002) = {4};   

As a matter of fact, I think I was too quick putting the code up here.
To me as well it looks like I've not corrected the comment in the header of the file.
Yet, as you try to change the permittivity, strange things happen...

...