# Nodal interpolant available?

Hey there,

The symbolic definition of (bi-) linearforms makes a lot of things easier.
But, I wonder whether one can also define a form including the nodal interpolant?

More specific: Consider the space of first order vector-valued H1 conforming elements, i.e., X = VectorH1(mesh, order=1).
The nodal interpolant I_h should now map a given (continuous) function f to a function f_h in the FE-space X, such that f(z) = f_h(z) for all nodes z in the mesh.

For two GridFunctions u and v in X, i can get the nodal interpolant of the cross-product denoted by I_h(u x v) quite easily (for example) by accessing the vectors of their nodal values.

But I struggle to use the nodal interpolant I_h in combination with test functions:
For example, is there a way to define a bilinear form like

<psi , curl( I_h(phi x v))>

in NgSolve?
Here again v denotes a GridFunction, while psi and phi denote a TrialFunction and a TestFunction, respectively. <.,.> denotes the L2 inner prduct.

Best regards,
Carl

just a quick idea: can you introduce a new variable w = l_h(phi x v) in the system and use this for a second equation for w?
https://ngsolve.org/docu/latest/i-tutorials/unit-2.10-dualbasis/dualbasis.html

Thank you Christopher for the suggestion - I will have a look into that.
Best, Carl

Hey there,

introducing a new variable as suggested by Christopher did the trick.
However, I used a mass-lumped integration rule rather than the dual basis you suggested.

For future reference I quickly describe the solution which worked for me,
in case somebody else runs into a similar problem.

Notation:
a x b denotes the cross-product of a and b.
<., .> denotes the L2-inner product;
<., .>_h denotes the mass-lumped inner-product (defined in the code below)

For given GridFunction eta in X, consider the problem of finding a GridFunction u (in X), such that

<u, phi> + <u, curl(I_h(phi x eta))> = <f, phi>

for all TestFunctions phi in X.

Introducing a new variable v = I_h(phi x eta) one arrives at a system on XX = FESpace([X, X]) :
Find (u, w) in XX, such that

<u, phi> - <eta x w, phi>_h = <f, phi>

<u, curl(v)> + <w, v>_h = 0

for all TestFunctions (phi, v) in XX.

Plugging in v = I_h(phi x eta), one sees that the original equation is satisfied.

The NGS-Python code snippet for defining the bilinear form looks like this

[code]myCurl = lambda Df : (Df[2,1] - Df[1,2], Df[0,2] - Df[2,0], Df[1,0] - Df[0,1])

massLumping = IntegrationRule( points = [(0,0,0), (1,0,0), (0,1,0), (0,0,1)],
weights = [1/24, 1/24, 1/24, 1/24] )

X = VectorH1(mesh, order=1)
XX = FESpace([X, X])
u, w = XX.TrialFunction()
phi, v = XX.TestFunction()

A = BilinearForm(XX)
A += SymbolicBFI(u * phi)
A += SymbolicBFI(u * myCurl(v.Deriv()))
A += SymbolicBFI(-Cross(eta, w) * phi, intrule=massLumping)
A += SymbolicBFI(w * v, intrule=massLumping)[/code]

The resulting (larger) system, however, takes pretty long to be solved.
So make sure to additionally decouple the DOFs of the Lagrange multiplier w
by using XX = FESpace([X, Discontinuous(X)]) instead,
and static condensation as described at:
https://ngsolve.org/docu/latest/i-tutorials/unit-1.4-staticcond/staticcond.html

Thank you Joachim for your further help,
Best regards,
Carl