Incompressible Humphrey hyperelastic material in Fencis

I found the solution. There were a few issues with the variation formulation. Here is the complete code for uniaxial testing of incompressible NeoHookean hyperelastic material.

> 
> from dolfin import *
> import matplotlib.pyplot as plt
> import os
> import dolfin as dlf
> import numpy as np
> import math 
> 
> parameters["form_compiler"]["cpp_optimize"] = True
> parameters["form_compiler"]["representation"] = "uflacs"
> 
> 
> # traction 
> class Traction(UserExpression):
>     def __init__(self):
>         super().__init__(self)
>         self.t = 0.0
>     def eval(self, values, x):
>         values[0] = 0*self.t
>         values[1] = 0.0
>         values[2] = 0.0
>     def value_shape(self):
>         return (3,)
> 
> # Kinematics
> def pk1Stress(u,pressure,E,nu):
>     G = E/(2*(1+nu))
>     c1 = G/2.0
>     
>     I = Identity(V.mesh().geometry().dim())  # Identity tensor
>     F = I + grad(u)          # Deformation gradient
>     C = F.T*F                # Right Cauchy-Green tensor
>     Ic = tr(C)               # Invariants of deformation tensors
>     J = det(F)
>     pk2 = 2*c1*I-pressure*inv(C) # second PK stress
>     return pk2, (J-1)
> 
> 
> def geometry_3d():
>     mesh = UnitCubeMesh(6, 6, 6)
> 
>     boundary_parts = MeshFunction('size_t', mesh, mesh.topology().dim()-1)
>     x0 = AutoSubDomain(lambda x: near(x[0], 0))
>     x1 = AutoSubDomain(lambda x: near(x[0], 1))
>     y0 = AutoSubDomain(lambda x: near(x[1], 0))
>     z0 = AutoSubDomain(lambda x: near(x[2], 0))
>     x0.mark(boundary_parts, 1)
>     y0.mark(boundary_parts, 2)
>     z0.mark(boundary_parts, 3)
>     x1.mark(boundary_parts, 4)
>     return boundary_parts
> 
> 
> 
> # Create mesh and define function space ============================================
> facet_function = geometry_3d()
> mesh = facet_function.mesh()
> gdim = mesh.geometry().dim()
> dx = Measure("dx")
> ds = Measure("ds", subdomain_data=facet_function, subdomain_id=4)
> print('Number of nodes: ',mesh.num_vertices())
> print('Number of cells: ',mesh.num_cells())
> 
> 
> # Limit quadrature degree
> dx = dx(degree=4)
> ds = ds(degree=4)
> 
> 
> # Create function space
> element_v = VectorElement("P", mesh.ufl_cell(), 1) #  
> element_s = FiniteElement("P", mesh.ufl_cell(), 1) #
> mixed_element = MixedElement([element_v, element_s]) #  
> V = FunctionSpace(mesh, mixed_element)
> 
> 
> # Define test and trial functions
> dup = TrialFunction(V)
> _u, _p = TestFunctions(V)
> 
> _u_p = Function(V)
> u, p = split(_u_p)
> 
> 
> # Create tensor function spaces for stress and stretch output
> W_DFnStress = TensorFunctionSpace(mesh, "DG", degree=0)
> defGrad = Function(W_DFnStress, name='F')
> PK1_stress = Function(W_DFnStress, name='PK1')
> 
> 
> # Displacement from previous iteration
> b = Constant((0.0,0.0, 0.0)) # Body force per unit mass
> h = Traction() # Traction force on the boundary
> 
> 
> # Define Dirichlet boundary
> bc0 = DirichletBC(V.sub(0).sub(0), Constant(0.), facet_function, 1)
> bc1 = DirichletBC(V.sub(0).sub(1), Constant(0.), facet_function, 2)
> bc2 = DirichletBC(V.sub(0).sub(2), Constant(0.), facet_function, 3)
> 
> tDirBC = Expression(('1.0*time_'),time_=0.0 , degree=0)
> bc3 = DirichletBC(V.sub(0).sub(0), tDirBC, facet_function, 4)
> bcs = [bc0,bc1,bc2,bc3]
> 
> # material parameters
> E, nu = 1e3, 0.5
> 
> # # Total potential energy
> pkstrs, hydpress =  pk1Stress(u,p,E,nu)
> I = Identity(V.mesh().geometry().dim())
> dgF = I + grad(u)
> F1 = inner(dot(dgF,pkstrs), grad(_u))*dx - dot(b, _u)*dx - dot(h, _u)*ds
> F2 = hydpress*_p*dx                                               
> F = F1+F2
> J = derivative(F, _u_p,dup)
> 
> # Create nonlinear variational problem and solve
> problem = NonlinearVariationalProblem(F, _u_p, bcs=bcs, J=J)
> solver = NonlinearVariationalSolver(problem)
> solver.parameters['newton_solver']['relative_tolerance'] = 1e-6
> solver.parameters['newton_solver']['linear_solver'] = 'mumps'
> 
> 
> # Time stepping parameters
> dt = 0.1
> t, T = 0.0, 10*dt
> 
> # Save solution in VTK format
> file_results = XDMFFile("./Results/TestUniaxialLoading/Uniaxial.xdmf")
> file_results.parameters["flush_output"] = True
> file_results.parameters["functions_share_mesh"] = True
> 
> stretch_vec = []
> stress_vec=[]
> stress_ana=[]
> 
> while t <= T:
>     print('time: ', t)
> 
>     # Increase traction
>     h.t = t
>     tDirBC.time_ = t
> 
>     # solve and save disp
>     solver.solve()
>     
>     # Extract solution components
>     u_plot, p_plot = _u_p.split()
>     u_plot.rename("u", "displacement")
>     p_plot.rename("p", "pressure")
>     
>     # get stretch at a point for plotting
>     point = (0.5,0.5,0)
>     DF = I + grad(u_plot)
>     defGrad.assign(project(DF, W_DFnStress))
>     stretch_vec.append(defGrad(point)[0])
> 
>     # get stress at a point for plotting
>     PK1_s,thydpress = pk1Stress(u_plot,p_plot,E,nu)
>     PK1_stress.assign(project(PK1_s, W_DFnStress))
>     stress_vec.append(PK1_stress(point)[0])
>     
>     # save xdmf file
>     file_results.write(u_plot, t)
>     file_results.write(defGrad, t)
>     file_results.write(PK1_stress, t)
> 
>     # time increment
>     t += float(dt)
> 
>     
> # get analytical solution
> stretch_vec = np.array(stretch_vec)
> stress_vec = np.array(stress_vec)
> G = E/(2*(1+nu))
> c1 = G/2.0
> for i in range(len(stretch_vec)):
>     pk1_ana = 2*c1*(stretch_vec[i] - 1/(stretch_vec[i]*stretch_vec[i])) #PK1
>     pk2_ana = (1/stretch_vec[i])*pk1_ana # PK2
>     stress_ana.append(pk2_ana)  
> stress_ana =  np.array(stress_ana)  
> 
> # plot results
> f = plt.figure(figsize=(12,6))
> plt.plot(stretch_vec, stress_vec,'r-')
> plt.plot(stretch_vec, stress_ana,'k.')

Result:

1 Like