Updating viscoelastic internal variables with Newton Raphson

Hello, I am working with a 3d matrix-fibre viscoelasticity model with 2 internal variables. The following equation is the free energy function:

\Psi\left(\overline{I}_1,\overline{I}_e,\overline{I}_4,\overline{I}_{4e},J\right) = c\left(\overline{I}_1-3\right) + \frac{1}{2}\kappa \left(J-1\right)^2 + \frac{k_1}{2k_2}\left(\exp\left[k_2\left(\overline{I}_4-1\right)^2\right]-1\right) \\ \hspace{100pt} + \beta_1 c (\overline{I}_e-3) + \beta_2 \frac{k_1}{2k_2}\left(\exp\left[k_2\left(\overline{I}_{4e}-1\right)^2\right]-1\right)

, where \beta_1, \beta_2 are the viscous parameters, and the internal variables are \overline{I}_{4e} and C^m_v (viscous-matrix part of right cauchy-green), from \overline{I}_{4}=\overline{I}_{4v}\overline{I}_{4e} and \overline{I}_e=\overline{C}:{C^m_v}^{-1}, respectively.

The evolution equations for these are:

\dot{\overline{{C^m_v}^{-1}}}=-\frac{1}{\tau_1}{C^m_v}^{-1}+\frac{3C^{-1}}{\tau_1\mathbf{I}:(C^m_vC^{-1})} \hspace{20pt} \text{and} \hspace{20pt} \dot{\overline{{I_{4e}}}}=-\frac{1}{\tau_2}\frac{\overline{I}_{4e}\overline{\Psi}_{4e}}{\overline{\Psi}_{4e4e}\overline{I}_{4e}+\overline{I}_{4e}}

Backward Euler was used to update these variables by means of an algorithm at each iteration.

My question is, what is the best way to define these internal variables? So far I did it like this:

# Tensorial Space
ET2   = TensorFunctionSpace(mesh, 'DG', 0, shape=(3,3))

# Internal Variables       
Cmv   = Function(ET2, name = "inverse matrix Cv")  # (Cᵐᵥ)⁻¹
I4e_ = inner(C_,A0)                                # Ī₄ₑ

# Variational problem
Pi = FreeEnergy(u,Cmv,I4e_)*dx - dot(B, u)*dx - dot(T, u)*ds(4)
DPi = derivative(Pi, u, v)     
Jac = derivative(DPi, u, du)
problem = NonlinearVariationalProblem(DPi, u, bcs=bcs, J=Jac)

Then to update them I used project and reshape with numpy to implement the algorithm, I used them as array because in both cases I had to use newton rhapson at a certain point using scipy, and because I didn’t know how to do the same using fenics. For example, I must find [\overline{I}_{4e}]^{n+1} by solving this equation by using NR:

[\overline{I}_{4e}]^{n+1}+\frac{\frac{\Delta t}{\tau_2}[\overline{I}_{4e}]^{n+1}([\overline{I}_{4e}]^{n+1}-1)}{[\overline{I}_{4e}]^{n+1}[1+2k_2([\overline{I}_{4e}]^{n+1}-1)^2]+([\overline{I}_{4e}]^{n+1}-1)}=[\overline{I}_{4e}]^n

Comparing with a numerical example where they give the dissipation curves I could check that the values of my internal variables are well calculated using numpy and scipy (see this figure). However, my final results are not the best (Relaxation graph). There are two problems, the elastic part through the volumetric response is very high, and the rate of decay due to viscosity is very small.

Is there a way to define this as in the linear viscoelasticity fenics tutorial?, like this:

Ve = VectorElement("CG", mesh.ufl_cell(), 1) 
Qe = TensorElement("DG", mesh.ufl_cell(), 0)     # Internal variable viscous strain
W = FunctionSpace(mesh, MixedElement([Ve, Qe]))

But in my case if I defined both in that way I would not know how to deal with them within the algorithm I have to apply and the Newton-Rhapson procedure that goes with it.

Thanks for any help!

Ok I solved the problem in my code and I will happily write here the solution for people who need to work with internal variables. These are the general steps I followed to get everything working.

Basically you need to define the internal variables as functional spaces. In my case:

For tensors:

et2 = TensorElement("DG", mesh.ufl_cell(), 0)
ET2 = FunctionSpace(mesh, et2)
Cmv = Function(ET2, name = "[(Cᵐᵥ)-¹]ⁿ⁺¹")

For scalars:

et0 = FiniteElement("CG", mesh.ufl_cell(), 1)
ET0 = FunctionSpace(mesh, et0)
I4e_ = Function(ET0, name = "[Ī₄ₑ]ⁿ⁺¹").

Then, you need to give them initial values:

Cmv.assign(project(I, ET2))   # [(Cᵐᵥ)-¹]⁰ = I
I4e_.assign(Constant(1.0))    # [Ī₄ₑ]⁰ = 1 

Now you can do whatever you like with them by projecting them to the corresponding function space and using reshape:

point        = (a,b,c)                               # Coordinate in body.

Cmv_coeff    = project(Cmv,ET2)                      # [Coefficient]
Cmv_array    = np.reshape(Cmv_coeff(point) ,[3,3])   # [Array]

I4e_coeff    = project(I4E_,ET0)                           # [Coefficient]
I4e_array    = np.reshape(I4e_coeff(point),[1,1])[0][0]    # [Array->Scalar]

Finally, when you’re done manipulating them, you project them again or define the scalar as a Constant, and assign them to the original variable:

CMV    =  project(Cmv_array,ET2)   
Cmv.assign(CMV)

I4E_    =  Constant(I4E_)
I4e_.assign(I4E_)

I hope you find it useful!

medio

1 Like