Blog
Kaan Kaan Mod Oct 8, 2026

Simulating an Axial-Flux Motor with GMSH and ElmerFEM: What I Learned

I came to finite element simulation from the software side. At Turncircles I wanted a simulation pipeline we could control end to end, run on our own servers, and drive entirely from code. That led me to Open CASCADE for geometry, GMSH for meshing, and ElmerFEM as the solver.

This isn't a tutorial. These are notes from a long stretch of work on one hard problem, written for anyone who is considering the same tools for electric machines.

Why open source

Three things made the decision for me.

GMSH has a Python API, so our configurator can generate geometry and meshes from parameters without anyone opening a GUI. Elmer reads plain text input files, which means code can write them too. And there are no per-seat or per-core licenses, so the number of simulations we run depends on our hardware and nothing else.

The fourth reason only became clear later. When the documentation runs out, you can read the source code. With these tools, that turned out to be the most valuable part.

The motor

Our motors are dual-rotor, coreless axial-flux machines. Two rotors carry the magnets and spin, and the stator sits between them and stays still. In a time-stepping simulation, the rotor mesh has to move relative to the stator mesh at every step, so you need a sliding interface where two meshes meet without matching node for node.

The first thing I did to keep the problem manageable was use symmetry. With 24 poles and 9 coils, the greatest common divisor is 3, so the machine repeats every 120°. Modelling one third of the motor cuts the problem size by three, and it also shrank the circuit definition from 42 unknowns to 18.

Sliding interfaces and the saddle point

Elmer couples the moving and static meshes with mortar boundary conditions. Under the hood, mortar conditions add Lagrange multipliers to the system of equations. Those multipliers create a block of zeros on the diagonal of the matrix, and mathematicians call the result a saddle-point system.

In practice this meant two things for me. The iterative solvers I tried, with ILU preconditioning, could not handle the zero diagonal on the full-size model. The direct solver MUMPS could, but its memory use grows fast with mesh size. RAM became the real design constraint for the whole simulation, and that pushed every decision about mesh density.

The hang

For several weeks I fought a problem that I still find instructive. The solver would complete a few timesteps cleanly and then stop. No crash, no error message, no new log line. One thread stayed busy while all the others went idle. Running the exact same files twice moved the stall from one timestep to another.

I tried around thirty variations: different solver settings, newer MUMPS versions, meshes with zero badly shaped elements. Nothing gave a reproducible fix.

The answer was in the mesh, on the sliding surfaces. My mesh size fields had produced elements of varying size along the interface. Once I forced a uniform element size there with a constant size field in GMSH, the hang disappeared. Mesh quality metrics had looked fine the whole time. The solver cared about consistency on that one surface far more than about the quality numbers I was watching.

Read the source

At one point I wanted Elmer to eliminate the interface constraints from the system instead of carrying the Lagrange multipliers along. The documentation suggested this was possible. Reading the Fortran source showed that this elimination path works for nodal unknowns but gets skipped for the edge elements that magnetic field simulations use. No warning, no error. It just didn't do anything for my case.

Since then I verify every solver keyword against the source before I trust it. That includes answers from AI assistants, which can sound very confident about Elmer keywords that behave differently, or don't exist at all.

The other thing I'd recommend: talk to the people who wrote the code. The Elmer community is small, and the developers behind specific solvers are reachable. A short, well-prepared question with a minimal example goes a long way.

Checking the physics

A solver that converges can still give you the wrong answer. Early on, one version of my model produced about a tenth of the expected torque, and the problem persisted across solvers, which told me it wasn't a convergence issue. Another version gave almost identical results with and without the interface conditions, which meant the conditions weren't doing anything.

These days the back-EMF waveform is my favourite sanity check. A clean, sinusoidal voltage tells me the setup is behaving. Noise in the waveform usually points to something in the interface handling, and tuning the regularization of the field formulation made a visible difference there.

Would I do it again

Yes. The cost is time, and I spent more of it than I planned. But I learned it and any new future setup will be much faster to setup. Commercial tools hide many of these problems behind a polished interface, and for a one-off design that's a fair trade.

For us, the simulation is part of the product. I need to understand every step, automate it, and scale it on our own hardware. Open source tools let me do that, and the debugging taught me more about how these motors work than any course would have.

If you're simulating electric machines in Elmer and hitting similar walls, I'd like to hear from you.