We have moved the forum to https://forum.ngsolve.org. This is an archived version of the topics until 05/05/23. All the topics were moved to the new forum and conversations can be continued there. This forum is just kept as legacy to not invalidate old links. If you want to continue a conversation just look for the topic in the new forum.
Please see the attached minimal example where I use the 1D normal. I need it for the evaluation of the element boundary of surface segments (codim 2). For this purpose, the co-normal shall (consistently) take -1 on the left end point and +1 on the right end point, which was what the gfn trying to do in the code.
I can not make it work using the consistent tangential vector
Code:
from ngsolve import *
from ngsolve.meshes import MakeStructured2DMesh
N = 4
mesh = MakeStructured2DMesh(nx=N, ny=1, mapping=lambda x, y: (x, y/N))
gfn = GridFunction(Compress(SurfaceL2(mesh, order=1, definedon=mesh.Boundaries("bottom"))))
count = 0
for e in mesh.Elements(BND):
if e.mat=="bottom":
if e.vertices[0].nr > e.vertices[1].nr:
gfn.vec[2*count+1] = -1
else:
gfn.vec[2*count+1] = 1
count += 1
n = specialcf.normal(2)
# HDiv interpolation
gfInterp = GridFunction(HDiv(mesh, order=1))
gfInterp.Set(gfn*n, definedon=mesh.Boundaries("bottom"))
Draw(gfInterp[1], mesh)
##
V = Compress(H1(mesh, definedon=mesh.Boundaries("bottom")))
u,v = V.TnT()
t = specialcf.tangential(2)
t1 = specialcf.tangential(2, consistent=True)
f = LinearForm(V)
# This works
f += t*t1*v*ds(element_boundary=True, definedon=mesh.Boundaries("bottom"))
# f += gfn*v*ds(element_boundary=True, definedon=mesh.Boundaries("bottom"))
f.Assemble()
print(f.vec)
we can now access Jacobians, and the coordinates from the reference element via special CoefficientFunctions (thx to Michael). This allows to build the co-normal vector.
The Logging - CoefficientFunction is useful to inspect the function evaluations.
- Joachim
Code:
from netgen.geom2d import unit_square
from ngsolve import *
mesh = Mesh(unit_square.GenerateMesh(maxh=0.3))
fes = SurfaceL2(mesh, order=1)
u,v = fes.TnT()
f = LinearForm(fes)
t = specialcf.tangential(2)
jac = specialcf.JacobianMatrix(2,1)[:,0]
xref = specialcf.xref(1)
conormal = (2*xref[0]-1)*jac
from ngsolve.fem import LoggingCF
conormal = LoggingCF (conormal)
f += conormal*t *v * ds("bottom", element_boundary=True)
f.Assemble()
Thanks!
I've now moved to a 3D mesh with a embedded 2D surface. I use mixed FEM to discrete a Laplace operator on the surface.
The following test case does not work: mesh is a unit cube, and the "flat surface" is simply its top boundary.
I use HDivSurface/SurfaceL2 spaces to build the finite element pair on "top" boundary, but it seems that I can not use these spaces directly here as the bilinear form assembler complained about inconsistent # of DOFs. Any idea what's going on?!
Code:
from ngsolve.meshes import MakeStructured3DMesh
from ngsolve import *
mesh = MakeStructured3DMesh(hexes=False, nx=10, ny=10, nz=2)
V = Compress(HDivSurface(mesh, order=1, definedon=mesh.Boundaries("top")))
W = Compress(SurfaceL2(mesh, order=0, definedon=mesh.Boundaries("top")))
fes = V*W
(u, p), (v, q) = fes.TnT()
a = BilinearForm(fes)
a += (u.Trace()*v.Trace()-div(u).Trace()*q.Trace()-p.Trace()*div(v).Trace())*ds("top")
a.Assemble()
Thanks for the very quick fix. I will check it out.
Also, I just realized that your co-normal is not exactly what I am looking for... I haven't express the problem clear enough. In the attached file, I depicted what I want: a scalar function a 1D surface mesh that acts exactly as the 1D normal direction for a plain 1D mesh, that is, given an orientation of the line segment, it should return a -1 on the left end of the segment and +1 on the right end of segment. (I don't think it should be called a co-normal as it is a lower dimensional quantity)... Since I couldn't function such a built-in function, the above surfaceL2 gridfunction gfn is a hacker that do the job.
Since the tangential/normal directions are quantities defined on edges, not vertices, the associated value on left/right endpoints of the segments will always be the same, which is not what I want.
*** More background: I use this "co-normal" to reinforce nodal continuity of a SurfaceL2 function:
Code:
# u is a surface L2 trial function
# v is a H1 trial function
# this bilinearform shall return nodal continuity of u (hybridized!)
a += u*v*con*ds(element_boundary=True)
This has been a very fruitful discussion! Thanks again for the quick responses.