Symbolic Integrators

Symbolic integrators can be used to define arbitrary (multi-)physical problems. The variational formulation of the (non-)linear problem can be implemented in a very natural way. Examples of the usage of these integrators can i.e. be found in Navier Stokes Equation, Magnetic Field or Nonlinear elasticity.

The finite element space provides placeholder coefficient functions by the methods TestFunction and TrialFunction. They have implemented canonical derivatives and traces of the finite element space and insert the basis functions of the FESpace during assembling of the system matrices.

Linear Problems

For linear problems we use the function Assemble of the BilinearForm to assemble the matrix and vector. For example for the \(H_\text{curl}\) linear problem

\[\begin{split}\int_\Omega \mu^{-1} \nabla \times u \cdot \nabla \times v + 10^{-6} u \cdot v \, dx = \int_C \begin{pmatrix} y \\ -x \\ 0 \end{pmatrix} \cdot v \, dx\end{split}\]

from example Magnetic Fields we have to define the space

fes = HCurl(mesh, order=4, dirichlet="outer", nograds = True)

and the BilinearForm

a = BilinearForm(fes, symmetric=True)
a += SymbolicBFI(nu*curl(u)*curl(v) + 1e-6*nu*u*v)


as well as the LinearForm

    solver = CGSolver(mat=a.mat, pre=c.mat) = solver * f.vec

Draw (u.Deriv(), mesh, "B-field", draw_surf=False)

The argument of the symbolic integrator must be a coefficient function depending linearily on the test and trial function.

BilinearForm.Assemble(self: ngsolve.comp.BilinearForm, reallocate: bool = False) → None

Assemble the bilinear form.


reallocate : bool
input reallocate

Nonlinear Problems

If your left hand side of the variational formulation is nonlinear there are multiple ways to get a discretisation, depending on what you want.


The function Apply applies the formulation to the given BaseVector. You can get a BaseVector form your GridFunction with GridFunction.vec. The output vector can be created with BaseVector.CreateVector.

BilinearForm.Apply(self: ngsolve.comp.BilinearForm, x:, y: → None

Applies a (non-)linear variational formulation to x and stores the result in y.


x : ngsolve.BaseVector
input vector
y : ngsolve.BaseVector
output vector


For a variational formulation

\[\int_\Omega f(u) v \, dx\]

the method AssembleLinearization computes

\[\int_\Omega f'(u_\text{lin}) u v \, dx\]

with automatic differentiation of \(f(u)\) and an input BaseVector \(u_\text{in}\).

BilinearForm.AssembleLinearization(self: ngsolve.comp.BilinearForm, ulin: → None

Computes linearization of the bilinear form at given vecor.


ulin :
input vector


You can do your own linearization as well using Assemble and a GridFunction as a CoefficientFunction in your integrator. Let gfu_old be this gridfunction then

a = BilinearForm(fes)
a += SymbolicBFI(gfu_old * u * v)

will be a linearization for

\[\int_\Omega u^2 v \, dx\]

Every time you call Assemble the bilinearform is updated with the new values of the GridFunction.

Symbolic Energy

SymbolicEnergy can be used to solve a minimization problem. In this tutorial we show how to solve the nonlinear problem

\[\min_{u \in V} 0.05 \nabla u + u^4 - 100u\]

For this we use SymbolicEnergy:

a = BilinearForm (V, symmetric=False)
a += SymbolicEnergy (0.05*grad(u)*grad(u) + u*u*u*u - 100*u)

from the GridFunction we can create new BaseVector:

u = GridFunction (V)
u.vec[:] = 0

res = u.vec.CreateVector()
w = u.vec.CreateVector()

With this we can use AssembleLinearization to do a Newton iteration to solve the problem:

for it in range(20):
    print ("Newton iteration", it)
    print ("energy = ", a.Energy(u.vec))

    a.Apply (u.vec, res)
    a.AssembleLinearization (u.vec)
    inv = a.mat.Inverse(V.FreeDofs()) = inv * res
    print ("w*r =", InnerProduct(w,res)) -= w