We pick off from the terrible pits of seemingly singular solutions. it was unclear what caused this seemingly simple problem (microstrip line fundamental mode) to appear. Let’s briefly review the debug process:
- Initially I tried sifting through the calculated modes, find it graphically.
- After a short while, it was pretty much clear that the proper eigenvalue itself does not even appear.
- I tried different variations of the formulation, see if the problem comes from there. I did actually find some interesting things there, but nothing worked, still.
- I started reading the documentation, again.
- I tried to see what is done in other FEM libraries to address this problem. Found nothing too suspicious.
A Stroke Of Luck
This all took quite a while. So I started sifting through the documentation again. Found this line:

So I figure if I change the order of the Nedelec elements to zero (1st order), something will work.
# Use interpolation order 2 on the whole domain:
# Previously was 2
et.setorder(wholedomain, 1)
ez.setorder(wholedomain, 2)
Somethings happening, these eigenvalues make sense now!!
Eigenmode #1 has an eigenvalue of-5778.55357301197
Eigenmode #2 has an eigenvalue of-5778.553573015865
Eigenmode #3 has an eigenvalue of-4.9112713895738125e-11
Eigenmode #4 has an eigenvalue of-3.637978807091713e-12
Eigenmode #5 has an eigenvalue of3.183231456205249e-11
Eigenmode #6 has an eigenvalue of5.056790541857481e-10
Eigenmode #7 has an eigenvalue of54737.13180899748
Eigenmode #8 has an eigenvalue of54737.13180899748
Eigenmode #9 has an eigenvalue of226939.34318114078
Eigenmode #10 has an eigenvalue of228911.64172811233
Eigenmode #11 has an eigenvalue of289138.1391293668
Eigenmode #12 has an eigenvalue of370827.73141538596
Let’s see what the cutoff frequencies are. For that I added a little manipulation, such that wave-numbers smaller than zero will not come out as an NaN error.
kc2_found = k0**2.0 - kz2_found
kc_found_sign = np.sign(kc2_found)
kc_found = np.sqrt(np.abs(kc2_found))*kc_found_sign
fc_found = kc_found*C/(2.0*np.pi)
Yielding
Eigenmode #1 has an cutoff frequency of-3.025772912493618 GHz
Eigenmode #2 has an cutoff frequency of-3.0257729124950834 GHz
Eigenmode #3 has an cutoff frequency of1.999999999999972 GHz
Eigenmode #4 has an cutoff frequency of1.9999999999999978 GHz
Eigenmode #5 has an cutoff frequency of2.000000000000018 GHz
Eigenmode #6 has an cutoff frequency of2.0000000000002878 GHz
Eigenmode #7 has an cutoff frequency of11.340771654033258 GHz
Eigenmode #8 has an cutoff frequency of11.340771654033258 GHz
Eigenmode #9 has an cutoff frequency of22.8176266592946 GHz
Eigenmode #10 has an cutoff frequency of22.91580609308357 GHz
Alright! Let’s visualize the modes

Hold it together. I know this. This is what the E-field looks like on the off-side of the cycle. Let’s try polarization #2

There you go. TEM\QTEM modes for the win.
Fortune Favors The Bold
So it turns out that even though it worked, I was wrong. Where? Basically everything.
- The setorder command does not have anything to do with basis function order. It is the order of the integral performed to obtain the basis function. However, it also affects the chronological order that the physical domains are interpolated.
- In this specific case, there is only one physical domain. But, two types of basis functions.
- There is (apparently) a numerical problem that occurs when these two have the same interpolation order.
- Interpolating the hcurl functions has to occur last (the lowest order). Don’t know why, doesn’t matter anymore.
So basically this means that as long as you set the interplation order lower for the hcurl functions, this works.
The take from this, I guess, is that trying makes perfect. If I hadn’t tried every different angle 3 times, I would have been stuck knee deep with this, or at least until I would have switched a library. Oops! Spoiler…
Beware The Pebble That Starts an Avalanche
As every Will Ferrell has the same plot line, so will these episodes. Beware the approaching wall…
I was almost sure that this is it and I am done and can move forward to fail at learning web based interfaces and such. Just a few more checks and I’m golden. Let’s try a coaxial cable mode!

Oh no. Not again… Let’s look at the 2nd polarization

Truth be told, I pretty much knew what this is and why it’s happening from the get-go. How to solve it, that’s a different riddle. Before delving into that, in the next post, I want to share one of the first debug steps that I took.
To validate that this isn’t a problem (again) with the EM formulation, I quickly wrote a Laplace solver for this. The Laplace operator is a lot simpler, of course. The general Poison equation is given by
.
It’s weak form formulation is
,
where Q is the charge accumulated in the port, as it is the result of integration over .
In this specific case, it is imperative to set sources, or ports, using the setport function. I am not getting into what are the ports now, as that is a subject of a post on it’s own. What is important to know, is that there are two related quantities. In this case they are the voltage of the port, and the charge residing on it. This is also formulated below:
V = port()
Q = port()
PHI.setport(line, V, Q)
# The waveguide is a perfect conductor. We thus force all
# tangential components of E to 0 on the waveguide skin.
PHI.setconstraint(gnd)
PHI.setconstraint(line)
# We force an electric field in the y direction on region 'left'
# that is 0 on the exterior of 'left' and one sine period inside.
laplace = formulation()
# Set a Qi charge per unit depth on the electrode:
laplace += Q - Qi;
laplace += integral(wholedomain, -eps * grad(dof(PHI)) * grad(tf(PHI)));
laplace.solve()
As usual, I will add the scripts in my github. It pretty much gave the same result

This means that there is a more basic problem. Could be just with the formulation, or maybe a numerical with the solver itself.
Epilogue
For a lot of people, this is probably enough to work with. You can calculate 99.9% of the waveguide types. With a few tweaks and a bit of know-how, you can also get it to calculate the port impedance, as well. Maybe leave the coaxial line for the approximated equations that are freely available, well, everywhere!
Not for me. I need to figure it out. I’ll show what I figured out, next time and get one step closer to the goal.
As usual, share with me what would you have done differently, your thoughts or if you have any other use for this project.
Be safe, wherever you are.