Constitutive models relate fluxes of a quantity to the gradients of the unknowns. For example, in thermal diffusion, there is a relationship between heat flux and temperature gradients which is known as Fourier’s law and which involves an empirically determined thermal conductivity.
and in elasticity or fluid flow, we have a relationship between stresses and strain (gradients of the displacements) or strain-rate (gradients of velocity) respectively
where \(\eta\), here, is a material property (elastic shear modulus, viscosity) that we need to determine experimentally.
These material properties can be non-linear (they depend upon the quantities that they relate), and they can also be anisotropic (they vary with the orientation of the material). In the latter case, the material property is described through a consitutive tensor. For example:
or
Constitutive tensors¶
In Underworld, the underlying implementation of any consitutive relationship is through the appropriate tensor representation, allowing for a very general formulation for most problems. The constitutive tensors are described symbolically using sympy expressions. For scalar equations, the rank 2 constitutive tensor (\(k_{ij}\)) can be expressed as a matrix without any loss of generality.
For vector equations, the constitutive tensor is of rank 4, but it is common in the engineering / finite element literature to transform the rank 4 tensor to a matrix representation. This has some benefits in terms of legibility and, perhaps, results in common code patterns across the formulation for scalar equations.
We can write the stress and strain rate tensors (\(\tau_{ij}\) and \(\dot\epsilon_{ij}\) respectively) as
where \(I\) indexes \(\{i,j\}\) in a specified order and symmetry of the tensor implies only six (in 3D) unique entries in the nine entries of a general tensor. With these definitions, the constitutive tensor can be written as \(C_{IJ}\) where \(I\), and \(J\) range over the independent degrees of freedom in stress and strain (rate). This is a re-writing of the full tensor, with the necessary symmetry required to respect the symmetry of \(\tau\) and \(\dot\varepsilon\)
The simplified notation comes at a cost. For example, \(\tau_I \dot\varepsilon_I\) is not equivalent to the tensor inner produce \(\tau_{ij} \dot\varepsilon_{ij}\), and \(C_{IJ}\) does not transform correctly for a rotation of coordinates.
However, a modest transformation of the matrix equations is helpful:
Where
\(\mathbf{P}\) is the scaling matrix
In this form, known as the Mandel notation, \(\tau^*_I\varepsilon^*_I \equiv \tau_{ij} \varepsilon_{ij}\), and the fourth order identity matrix:
transforms to
Voigt form¶
Computation of the stress tensor using \(\tau^*_I = C^*_{IJ}\varepsilon^*_J\) is equivalent to the following
multiply by \(\mathbf{P}^{-1} \) and collecting terms:
This is the Voigt form of the constitutive equations and is generally what you will find in a finite element textbook. The Voigt form of the constitutive matrix is the rearrangement of the rank 4 constitutive tensor (with no scaling), the strain rate vector is usually combined with \(\mathbf{P}^2\), and the stress vector is raw. For example:
A full discussion of this can be found in @brannonRotationReflectionFrame2018 and in helnweinRemarksCompressedMatrix2001
Sympy tensorial form, Voigt form¶
The rank 4 tensor form of the constitutive equations, \(c_{ijkl}\), has the following (2D) representation in sympy:
and the inner product \(\tau_{ij} = c_{ijkl} \varepsilon_{kl} \) is written
tau = sympy.tensorcontraction(
sympy.tensorcontraction(
sympy.tensorproduct(c, epsilon),(3,5)),(2,3))
However, the sympy.Matrix module allows a much simpler expression
tau_star = C_star * epsilon_star
tau = P.inv() * tau_star
which we adopt in underworld for the display and manipulation of constitutive tensors in either the Mandel form or the (more common in Engineering texts) Voigt form.