# Viscoelastic to Thermoviscoelastic

Hello all,
I am trying to model the thermoviscoelastic material behaviour and I have a FEniCS model for viscoelastic model. I am sharing my model below. With using the prony coefficients and corresponding relaxation time and WLF parameters, how can I change my model to thermoviscoelastic model? Thank you in advance.

from dolfin import *
from ufl import replace
import numpy as np
import matplotlib.pyplot as plt

L, H = 0.1, 0.2
mesh = RectangleMesh(Point(0., 0.), Point(L, H), 5, 10)

E0 = Constant(70e3)
E1 = Constant(20e3)
E2 = Constant(20e3)
E3 = Constant(20e3)  # Third Maxwell element
E4 = Constant(20e3)  # Fourth Maxwell element
eta1 = Constant(1e3)
eta2 = Constant(2e2)
eta3 = Constant(2e2)  # Third Maxwell element
eta4 = Constant(2e2)  # Fourth Maxwell element
nu = Constant(0.)
dt = Constant(0.) # time increment
sigc = 100. # imposed creep stress
epsr = 1e-3 # imposed relaxation strain

def left(x, on_boundary):
return near(x[0], 0.) and on_boundary
def bottom(x, on_boundary):
return near(x[1], 0.) and on_boundary
class Top(SubDomain):
def inside(self, x, on_boundary):
return near(x[1], H) and on_boundary

facets = MeshFunction("size_t", mesh, 1)
facets.set_all(0)
Top().mark(facets, 1)
ds = Measure("ds", subdomain_data=facets)

Ve = VectorElement("CG", mesh.ufl_cell(), 1)
Qe = TensorElement("DG", mesh.ufl_cell(), 0)
W = FunctionSpace(mesh, MixedElement([Ve, Qe, Qe, Qe, Qe]))  # Four Qe for four Maxwell elements
w = Function(W, name="Variables at current step")
(u, epsv1, epsv2, epsv3, epsv4) = split(w)  # Split into four viscoelastic strains
w_old = Function(W, name="Variables at previous step")
(u_old, epsv1_old, epsv2_old, epsv3_old, epsv4_old) = split(w_old)
w_ = TestFunction(W)
(u_, epsv1_, epsv2_, epsv3_, epsv4_) = split(w_)
dw = TrialFunction(W)

def eps(u):
def dotC(e):
return nu/(1+nu)/(1-nu)*tr(e)*Identity(2) + 1/(1+nu)*e
def sigma(u, epsv1, epsv2, epsv3, epsv4):
return E0*dotC(eps(u)) + E1*dotC(eps(u) - epsv1) + E2*dotC(eps(u) - epsv2) + E3*dotC(eps(u) - epsv3) + E4*dotC(eps(u) - epsv4)
def strain_energy(u, epsv1, epsv2, epsv3, epsv4):
e = eps(u)
return 0.5*(E0*inner(e,dotC(e)) + E1*inner(e-epsv1, dotC(e-epsv1)) + E2*inner(e-epsv2, dotC(e-epsv2)) + E3*inner(e-epsv3, dotC(e-epsv3)) + E4*inner(e-epsv4, dotC(e-epsv4)))
def dissipation_potential(depsv1, depsv2, depsv3, depsv4):
return 0.5*eta1*inner(depsv1, depsv1) + 0.5*eta2*inner(depsv2, depsv2) + 0.5*eta3*inner(depsv3, depsv3) + 0.5*eta4*inner(depsv4, depsv4)

Traction = Constant(0.)
incremental_potential = strain_energy(u, epsv1, epsv2, epsv3, epsv4)*dx \
+ dt*dissipation_potential((epsv1-epsv1_old)/dt, (epsv2-epsv2_old)/dt, (epsv3-epsv3_old)/dt, (epsv4-epsv4_old)/dt)*dx \
- Traction*u[1]*ds(1)
F = derivative(incremental_potential, w, w_)
J = derivative(F, w, dw)

dimp = Constant(H*epsr) # imposed vertical displacement instead
bcs = [DirichletBC(W.sub(0).sub(0), Constant(0), left),
DirichletBC(W.sub(0).sub(1), Constant(0), bottom),
DirichletBC(W.sub(0).sub(1), dimp, facets, 1)]

def viscoelastic_test(case, Nsteps=50, Tend=1.):
# Solution fields are initialized to zero
w.interpolate(Constant((0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.)))  # 18 zeros for 18 DoFs

if case in ["creep", "recovery"]: # imposed traction on top
Traction.assign(Constant(sigc))
bc = bcs[:2] # remove the last boundary conditions from bcs
t0 = Tend/2. # traction goes to zero at t0 for recovery test
elif case == "relaxation":
Traction.assign(Constant(0.)) # no traction on top
bc = bcs

# Time-stepping loop
time = np.linspace(0, Tend, Nsteps+1)
Sigyy = np.zeros((Nsteps+1, ))
Epsyy = np.zeros((Nsteps+1, 5))
for (i, dti) in enumerate(np.diff(time)):
if i>0 and i % (Nsteps/5) == 0:
print("Increment {}/{}".format(i, Nsteps))
dt.assign(dti)
if case == "recovery" and time[i+1] > t0:
Traction.assign(Constant(0.))
w_old.assign(w)
solve(F == 0, w, bc, J=J)
# get average stress/strain
Sigyy[i+1] = assemble(sigma(u, epsv1, epsv2, epsv3, epsv4)[1, 1]*dx)/L/H
Epsyy[i+1, 0] = assemble(eps(u)[1, 1]*dx)/L/H
Epsyy[i+1, 1] = assemble(epsv1[1, 1]*dx)/L/H
Epsyy[i+1, 2] = assemble(epsv2[1, 1]*dx)/L/H
Epsyy[i+1, 3] = assemble(epsv3[1, 1]*dx)/L/H
Epsyy[i+1, 4] = assemble(epsv4[1, 1]*dx)/L/H

# Define analytical solutions
if case == "creep":
if float(E0) == 0.:
eps_an = sigc*(1./float(E1)+time/float(eta1))
else:
Estar1 = float(E0*E1/(E0+E1))
tau1 = float(eta1)/Estar1
Estar2 = float(E0*E2/(E0+E2))
tau2 = float(eta2)/Estar2
Estar3 = float(E0*E3/(E0+E3))
tau3 = float(eta3)/Estar3
Estar4 = float(E0*E4/(E0+E4))
tau4 = float(eta4)/Estar4
eps_an = sigc/float(E0)*(1 - float(Estar1/E0)*np.exp(-time/tau1) - float(Estar2/E0)*np.exp(-time/tau2)
- float(Estar3/E0)*np.exp(-time/tau3) - float(Estar4/E0)*np.exp(-time/tau4))
sig_an = sigc + 0*time
elif case == "relaxation":
if float(E1) == 0.:
sig_an = epsr*float(E0) + 0*time
else:
tau1 = float(eta1/E1)
tau2 = float(eta2/E2)
tau3 = float(eta3/E3)
tau4 = float(eta4/E4)
sig_an = epsr*(float(E0) + float(E1)*np.exp(-time/tau1) + float(E2)*np.exp(-time/tau2)
+ float(E3)*np.exp(-time/tau3) + float(E4)*np.exp(-time/tau4))
eps_an = epsr + 0*time

elif case == "recovery":
Estar1 = float(E0*E1/(E0+E1))
tau1 = float(eta1)/Estar1
Estar2 = float(E0*E2/(E0+E2))
tau2 = float(eta2)/Estar2
Estar3 = float(E0*E3/(E0+E3))
tau3 = float(eta3)/Estar3
Estar4 = float(E0*E4/(E0+E4))
tau4 = float(eta4)/Estar4
eps_an = sigc/float(E0)*(1 - float(E1/(E0+E1))*np.exp(-time/tau1) - float(E2/(E0+E2))*np.exp(-time/tau2)
- float(E3/(E0+E3))*np.exp(-time/tau3) - float(E4/(E0+E4))*np.exp(-time/tau4))
sig_an = sigc + 0*time
time2 = time[time > t0]
sig_an[time > t0] = 0.
eps_an[time > t0] = sigc*(float(E1/E0/(E0+E1))*(np.exp(-(time2-t0)/tau1) - np.exp(-time2/tau1))
+ float(E2/E0/(E0+E2))*(np.exp(-(time2-t0)/tau2) - np.exp(-time2/tau2))
+ float(E3/E0/(E0+E3))*(np.exp(-(time2-t0)/tau3) - np.exp(-time2/tau3))
+ float(E4/E0/(E0+E4))*(np.exp(-(time2-t0)/tau4) - np.exp(-time2/tau4)))

# Plot strain evolution
plt.figure()
plt.plot(time, 100*eps_an, label="analytical solution")
plt.plot(time, 100*Epsyy[:, 0], '.', label="FE solution")
plt.plot(time, 100*Epsyy[:, 1], '--', linewidth=1, label="viscous strain 1")
plt.plot(time, 100*Epsyy[:, 2], '--', linewidth=1, label="viscous strain 2")
plt.plot(time, 100*Epsyy[:, 3], '--', linewidth=1, label="viscous strain 3")
plt.plot(time, 100*Epsyy[:, 4], '--', linewidth=1, label="viscous strain 4")
plt.ylim(0, 1.2*max(Epsyy[:, 0])*100)
plt.xlabel("Time")
plt.ylabel("Vertical strain [\%]")
plt.title(case.capitalize() + " test")
plt.legend()
plt.savefig("Strain")
plt.show()

# Plot stress evolution
plt.figure()
plt.plot(time, sig_an, label="analytical solution")
plt.plot(time, Sigyy, '.', label="FE solution")
plt.ylim(0, 1.2*max(Sigyy))
plt.ylabel("Vertical stress")
plt.xlabel("Time")
plt.title(case.capitalize() + " test")
plt.legend()
plt.savefig("Stress")
plt.show()

viscoelastic_test("relaxation")

Hi, in this case, you should start from the decomposition:

\varepsilon = \varepsilon^{el} + \varepsilon^v + \varepsilon^{th}

where \varepsilon^{th} is given from a known temperature field as in Linear thermoelasticity (weak coupling) — Numerical tours of continuum mechanics using FEniCS master documentation
and adapt the expressions for the stress, strain energy accordingly