Hermite functionspace with unitinterval mesh

Hi,
What I code up is Euler bernoulli beam. So, it is necessary to consider function in Hermite.
The error shows is
ValueError: Unknown element family: Hermite with cell type interval.
To do that, what kind of mesh used for?

The minimal viable code is below.

# OPEN MPI (Automatically splice matrix per CPU)
from mpi4py import MPI
# import c++ preprocessor wraper for fenicsx
from dolfinx import mesh,fem
# import math library for fenicxx
from dolfinx.fem import FunctionSpace
# import symbolic library for fenicsx
import ufl
# import solver
from petsc4py.PETSc import ScalarType
# import other library
import numpy as np
import matplotlib.pyplot as plt
# mesh 
domain = mesh.create_unit_interval(MPI.COMM_WORLD, 8)

# define function space Hermite k=3
V = FunctionSpace(domain, ("HER", 3))
T = FunctionSpace(domain, ("HER",3))

u = ufl.TrialFunction(V)
v = ufl.TestFunction(T)

As far as Im aware, Hermite elements are not supported by FFCx and DOLFINx yet. @mscroggs can probably shed more light on this.

1 Like

Correct. We have experimented with Hermite elements in Basix, but work is needed in FFCx and DOLFINx (and probably also UFL) to make these work due to their use of a non-standard pull back/push forward function

2 Likes

Thanks, I resolve to use fenics 2019.1 and some others.
GitHub - david-kamensky/tIGAr: A Python library for isogeometric analysis (IGA) using FEniCS.!

If you would like to continue using dolfinx, porting most of the timoshenko beam code from Jeremy Bleyer’s excellent elastic beams demo is totally possible (I’ve done it). See his legacy dolfin code here:

https://comet-fenics.readthedocs.io/en/latest/demo/beams_3D/beams_3D.html

If Timoshenko beams and mixed formulations aren’t your cup of tea or you are interested in the theoretical simplicity of Euler-Bernoulli beams, I’ve experimented a bit with importing an element using basix. This works in versions of DOLFINX higher than 0.6.0. Here’s an example comparing the exact solution of an cantilever beam to the numerical solution:

The results:

EB-Beam

and the code:

# static 1D Cantilever Beam with a rectangular cross section
# this example uses Euler-Bernoulli ("classical") beam theory
import matplotlib.pyplot as plt
import numpy as np
from mpi4py import MPI
import basix
from dolfinx.fem import (Function, FunctionSpace, dirichletbc,
                         locate_dofs_topological,Constant,Expression)
from dolfinx.io import VTKFile
from dolfinx.fem.petsc import LinearProblem
from dolfinx.mesh import locate_entities_boundary,create_interval
from ufl import (SpatialCoordinate,inner, TestFunction, TrialFunction, div, grad, dx)

########## GEOMETRIC INPUT ####################
E = 70e3
L = 1.0
b = 0.1
h = 0.05
rho= 2.7e-3
g = 9.81
#NOTE: floats must be converted to dolfin constants on domain below

#################################################################
########### CONSTRUCT BEAM MESH #################################
#################################################################
NUM_ELEM = 10
domain = create_interval(MPI.COMM_WORLD, NUM_ELEM, [0, L])

#################################################################
##### ENTER MATERIAL PARAMETERS AND CONSTITUTIVE MODEL ##########
#################################################################
x = SpatialCoordinate(domain)

thick = Constant(domain,h)
width = Constant(domain,b)
E = Constant(domain,E)
rho = Constant(domain,rho)
g = Constant(domain,g)

A = thick*width
EI = (E*width*thick**3)/12

#distributed load value (due to weight)
q=-rho*A*g

#################################################################
########### EXACT SOLUTIONS FOR CANTILEVER BEAM #################
#################################################################
#cantilever
w_cl = q/EI * ( (x[0]**4)/24 -(L*x[0]**3)/6 +((L**2)*x[0]**2)/4 )

#################################################################
########### COMPUTE STATIC SOLUTION #############################
#################################################################

#define Moment expression
def M(u):
    return EI*div(grad(u))

# Create Hermite order 3 on a interval (for more informations see:
#    https://defelement.com/elements/examples/interval-Hermite-3.html )
beam_element = basix.ufl_wrapper.create_element(basix.ElementFamily.Hermite, basix.CellType.interval, 3)

#finite element function space on domain, with trial and test fxns
W = FunctionSpace(domain,beam_element)
print("Number of DOFs: %d" % W.dofmap.index_map.size_global)
print("Number of elements (intervals): %d" % NUM_ELEM)
print("Number of nodes: %d" % (NUM_ELEM+1))
u_ = TestFunction(W)
v = TrialFunction(W)

#bilinear form (LHS)
k_form = inner(div(grad(v)),M(u_))*dx

#linear form construction (RHS)
l_form = q*u_*dx

#APPLY BOUNDARY CONDITIONS
#initialize function for boundary condition application
ubc = Function(W)
with ubc.vector.localForm() as uloc:
     uloc.set(0.)

#locate endpoints
startpt=locate_entities_boundary(domain,0,lambda x : np.isclose(x[0], 0))
endpt=locate_entities_boundary(domain,0,lambda x : np.isclose(x[0], L))

#locate DOFs from endpoints
startdof=locate_dofs_topological(W,0,startpt)
enddof=locate_dofs_topological(W,0,endpt)

#fix displacement of start point and rotation as well
fixed_disp = dirichletbc(ubc,[startdof[0]])
fixed_rot = dirichletbc(ubc,[startdof[1]])

#SOLVE VARIATIONAL PROBLEM
#initialize function in functionspace for beam properties
u = Function(W)

# solve variational problem
problem = LinearProblem(k_form, l_form, u=u, bcs=[fixed_disp,fixed_rot])
uh=problem.solve()
uh.name = "Displacement and Rotation "

#################################################################
########### SAVE AND VISUALIZE RESULTS ##########################
#################################################################

#save output (if uh is directly visualized in Paraview, the plot will look odd,
#  as the rotation DOFs are included in this  )
with VTKFile(domain.comm, "output/output.pvd", "w") as vtk:
    vtk.write([uh._cpp_object])
    
#NOTE: The solution uh contains both the rotation and the displacement solutions
#The rotation and displacment solutions can be separated as follows:
disp = np.empty(0)
rot = np.empty(0)
for i,x in enumerate(uh.x.array):
    if i % 2 != 0:
        rot = np.append(rot,x)
    else:
        disp = np.append(disp,x)

#evaluate derivatives and interpolate to higher order function space
T = FunctionSpace(domain,("CG",1))

#interpolate exact ufl expression onto high-order function space
disp_expr = Expression(w_cl,T.element.interpolation_points())
disp_exact = Function(T)
disp_exact.interpolate(disp_expr)

#extract numpy arrays for plotting
exact_disp = disp_exact.x.array

x_exact = np.linspace(0,1,exact_disp.shape[0])

x_fem = np.linspace(0,1,disp.shape[0])

figure, axis = plt.subplots(1,1)

print("Maximum magnitude displacement (cantilever exact solution) is: %e" % np.min(exact_disp))
print("Maximum magnitude displacement (cantilever FEM solution) is: %e" % np.min(disp))

####PLOTTING####
#Displacement
axis.plot(x_exact,exact_disp,label='exact')
axis.plot(x_fem, disp,marker='x',linestyle=':',label='FEM')
axis.set_xlabel('x (location along beam)')
axis.set_ylabel('u(x) (transverse displacement)')
axis.set_title("Displacement of FEM vs EB theory")
axis.legend()

plt.show()
1 Like

Hello,

I’ve lately also tested Hermite elements based on the classical 4th order bi-laplace equation (1D) and some manufactured solution. The results seemed to be quite promising. Could @mscroggs give some details, where this non-standard pull back/push forward function comes into play, or in which cases we would have to be careful?

And probably one more question: Is it currently intended to implement Hermite elements into dolfinX?

Best wishes,
Maximilian

The pull back/push forward is used to map values from the reference cell to the cells in the mesh, and this is likely to cause issues whenever to have more than one cell in your mesh - ie it’s a big issue!

I’m keen to get Hermite element support into FEniCSx, but it’s not a top priority so it might be a while until this is done

Thank you for your answer. Do you have any idea how long it will take to implement the elements?

Best wishes,
Maximilian

It might be a while, a I don’t think they’re a top priority for anyone at the moment.

Although we’re going to look at how elements are defined in UFL as part of some work on UFL that’s going on, and are likely to remove some difficulties in implementing them as part of that, so I’m hoping that once the UFL work has got that far, they’ll be much easier to implement

Hi!
I was struggling with a 1D Euler-Bernoulli beam model that gave me infinite displacements until I entered the material parameters you used here (especially rho=2.7e-3). Now my code works but I don’t really understand the value of rho. Why is it so low?
Thank you for your answer!

@Paul_Morel The values I put in the script above are rough values for the density and modulus of elasticity for aluminum. It’s a bit too deep of a question for me to answer why aluminum has these properties and especially the relative magnitude of these properties. This is probably a question for a material scientist or a physicist and I’m just a lowly engineer. :slight_smile: However, I do know that if you have improperly scaled values (e.g. a large self weight), you will be applying massive forces and exceeding the limits of the linear beam model. The beam model requires small strains and massive forces would produce very large strains, violating this assumption.

It looks like the basix api has changed a bit. It may already be clear, but if you replace the line:

beam_element = basix.ufl_wrapper.create_element(basix.ElementFamily.Hermite, basix.CellType.interval, 3)

with:

beam_element=basix.ufl.element(basix.ElementFamily.Hermite, basix.CellType.interval, 3)

The above script should run for fenics 0.7.0 and basix 0.7.0+

1 Like

Did you have any luck updating this to 0.8? I’m hitting issues with the the dirichlet BCs.

It would be helpful if you added your attempt to use it with dolfinx v0.8.0 and the error traceback.

Hi @jacobmerson

Changing

fixed_disp = dirichletbc(ubc,[startdof[0]]) 
fixed_rot = dirichletbc(ubc,[startdof[1]])

to

fixed_disp = dirichletbc(ubc,np.array([startdof[0]]))
fixed_rot = dirichletbc(ubc,np.array([startdof[1]]))

seems to do the trick. It apprears like you can’t pass python lists to dirichletbc() and instead you need to pass in np ndarrays.

Note: One other change from v0.7.0 to v0.8.0: I also updated all FunctionSpace() calls to functionspace()

Below is a script that runs in fenicsx 0.8.0

# static 1D Cantilever Beam with a rectangular cross section
# this example uses Euler-Bernoulli ("classical") beam theory
# import basix.ufl_wrapper
import numpy as np
from mpi4py import MPI
import basix
from dolfinx.fem import (Function, dirichletbc,functionspace,
                         locate_dofs_topological,Constant,Expression)
from dolfinx.io import VTKFile
from dolfinx.fem.petsc import LinearProblem
from dolfinx.mesh import locate_entities_boundary,create_interval
from ufl import (SpatialCoordinate,inner, TestFunction, TrialFunction, div, grad, dx)
import matplotlib.pyplot as plt

########## GEOMETRIC INPUT ####################
E = 70e3
L = 1.0
b = 0.1
h = 0.05
rho= 2.7e-3
g = 9.81
#NOTE: floats must be converted to dolfin constants on domain below

#################################################################
########### CONSTRUCT BEAM MESH #################################
#################################################################
NUM_ELEM = 10
domain = create_interval(MPI.COMM_WORLD, NUM_ELEM, [0, L])

#################################################################
##### ENTER MATERIAL PARAMETERS AND CONSTITUTIVE MODEL ##########
#################################################################
x = SpatialCoordinate(domain)

thick = Constant(domain,h)
width = Constant(domain,b)
E = Constant(domain,E)
rho = Constant(domain,rho)
g = Constant(domain,g)

A = thick*width
EI = (E*width*thick**3)/12

#distributed load value (due to weight)
q=-rho*A*g

#################################################################
########### EXACT SOLUTIONS FOR CANTILEVER BEAM #################
#################################################################
#cantilever
w_cl = q/EI * ( (x[0]**4)/24 -(L*x[0]**3)/6 +((L**2)*x[0]**2)/4 )

#################################################################
########### COMPUTE STATIC SOLUTION #############################
#################################################################

#define Moment expression
def M(u):
    return EI*div(grad(u))

# Create Hermite order 3 on a interval (for more informations see:
#    https://defelement.com/elements/examples/interval-Hermite-3.html )
beam_element=basix.ufl.element(basix.ElementFamily.Hermite, basix.CellType.interval, 3)

#finite element function space on domain, with trial and test fxns
W = functionspace(domain,beam_element)
print("Number of DOFs: %d" % W.dofmap.index_map.size_global)
print("Number of elements (intervals): %d" % NUM_ELEM)
print("Number of nodes: %d" % (NUM_ELEM+1))
u_ = TestFunction(W)
v = TrialFunction(W)

#bilinear form (LHS)
k_form = inner(div(grad(v)),M(u_))*dx

#linear form construction (RHS)
l_form = q*u_*dx

#APPLY BOUNDARY CONDITIONS
#initialize function for boundary condition application
ubc = Function(W)
with ubc.vector.localForm() as uloc:
     uloc.set(0.)

#locate endpoints
startpt=locate_entities_boundary(domain,0,lambda x : np.isclose(x[0], 0))
endpt=locate_entities_boundary(domain,0,lambda x : np.isclose(x[0], L))

#locate DOFs from endpoints
startdof=locate_dofs_topological(W,0,startpt)
enddof=locate_dofs_topological(W,0,endpt)

#fix displacement of start point and rotation as well
fixed_disp = dirichletbc(ubc,np.array([startdof[0]]))
fixed_rot = dirichletbc(ubc,np.array([startdof[1]]))
# fixed_disp = dirichletbc(ubc,startdof)


#SOLVE VARIATIONAL PROBLEM
#initialize function in functionspace for beam properties
u = Function(W)

# solve variational problem
problem = LinearProblem(k_form, l_form, u=u, bcs=[fixed_disp,fixed_rot])
uh=problem.solve()
uh.name = "Displacement and Rotation "

#################################################################
########### SAVE AND VISUALIZE RESULTS ##########################
#################################################################

#save output (if uh is directly visualized in Paraview, the plot will look odd,
#  as the rotation DOFs are included in this  )
with VTKFile(domain.comm, "output/output.pvd", "w") as vtk:
    vtk.write([uh._cpp_object])
    
#NOTE: The solution uh contains both the rotation and the displacement solutions
#The rotation and displacment solutions can be separated as follows:
disp = np.empty(0)
rot = np.empty(0)
for i,x in enumerate(uh.x.array):
    if i % 2 != 0:
        rot = np.append(rot,x)
    else:
        disp = np.append(disp,x)

#evaluate derivatives and interpolate to higher order function space
T = functionspace(domain,("CG",1))

#interpolate exact ufl expression onto high-order function space
disp_expr = Expression(w_cl,T.element.interpolation_points())
disp_exact = Function(T)
disp_exact.interpolate(disp_expr)

#extract numpy arrays for plotting
exact_disp = disp_exact.x.array

x_exact = np.linspace(0,1,exact_disp.shape[0])

x_fem = np.linspace(0,1,disp.shape[0])

figure, axis = plt.subplots(1,1)

print("Maximum magnitude displacement (cantilever exact solution) is: %e" % np.min(exact_disp))
print("Maximum magnitude displacement (cantilever FEM solution) is: %e" % np.min(disp))

####PLOTTING####
#Displacement
axis.plot(x_exact,exact_disp,label='exact')
axis.plot(x_fem, disp,marker='x',linestyle=':',label='FEM')
axis.set_xlabel('x (location along beam)')
axis.set_ylabel('u(x) (transverse displacement)')
axis.set_title("Displacement of FEM vs EB theory")
axis.legend()

plt.show()
1 Like

The change to forced usage of numpy arrays is due to the shift to nanobind from pybind, as nanobind does not do magic conversions of types in the same way pybind did. Thanks for the updated code @jkrokowski

1 Like

Thank you so much! I was stuck on that DBC issue for a while.

@mscroggs
Do we have any update on hermite facility implemented in FEniCSx like serendipidity for interval and quad mesh. For me C1 continuity requirement, is fulfilled by Hermite but not serendipidity for interval mesh. Can you suggest an alternative?