[ANN] LowLevelFEM.jl – assembling PDE systems directly from weak forms

I’ve been working on a small Julia FEM framework called LowLevelFEM.jl. One of the ideas behind the project is to assemble PDE systems directly from their weak form.

The core idea is simple:

If you know the weak form of a PDE, you can almost copy it from paper into executable code.

The framework provides a small set of variational operators (Grad, Div, SymGrad, etc.) that allow weak forms to be written in a way that closely resembles their mathematical formulation.

Example: steady Navier–Stokes

μ = 1.0
γ = 1e-1     # grad-div stabilization
δ = 1e-6     # pressure stabilization

A = ∫((ε(Pv) ⋅ ε(Pv)) * 2μ)
D = ∫(Div(Pv) ⋅ Div(Pv) * γ)
B = ∫(Div(Pv) ⋅ Pp)
C = ∫(Grad(Pp) ⋅ Grad(Pp) * δ)

K = SystemMatrix([
    A + D   B'
    B      -C ])

The snippet above is not pseudocode — it is the actual solver code.

The same formulation can be used for different PDE systems such as

  • diffusion problems
  • linear elasticity
  • nonlinear elasticity
  • fluid flow
  • multiphysics coupling

Here is a small example result from the Navier–Stokes test case:

A runnable Jupyter notebook (Julia kernel) demonstrating this example is available here:

Navier-Stokes.ipynb

Feedback and suggestions are very welcome.

Out of curiosity: what kind of multiphysics or multi-field problems would you try to prototype with such an operator-based FEM framework?

Sounds interesting, reminds me of FEniCSx, which I use heavily. However, there’s already Gridap.jl - I wonder how your package compares with it?

I have also wondered whether it would make sense to build something like this on top of Ferrite.jl by leveraging Symbolics.jl to generate assembly code from the weak form. I’m curious to hear your thoughts on this? Did you consider such an approach?

Good question — there are indeed similarities to frameworks like FEniCS or Gridap.

One of the motivations behind LowLevelFEM was to build something transparent and open rather than a “black box” solver. I wanted a codebase that I can use both for teaching and research, where the numerical formulation is clearly visible and easy to modify.

The goal is not to hide the numerical method, but to expose it in a clear and programmable way.
In that sense the framework behaves more like a toolbox for PDE experimentation than a traditional solver package.

The framework therefore keeps the variational operators explicit, so the weak form is written almost directly in code, e.g.

A = ∫(2μ * (ε(Pu) ⋅ ε(Pu)))
B = ∫(Div(Pv) ⋅ Pp)

The idea is that you can work at different levels: use it as a straightforward engineering solver, or go deeper and modify the operators and assembly if needed.

Regarding Ferrite + Symbolics: that’s an interesting direction. At the moment, the operators are implemented directly in Julia rather than generated symbolically, but I could definitely imagine experimenting with that approach in the future.