Last Updated: May 5, 2019
The gradient of the velocity field is a strain-rate tensor field, that is, a second rank tensor field. It appears in the diffusion term of the Navier-Stokes equation.
A second rank tensor has nine components and can be expressed as a 3×3 matrix as shown in the above image.
In this blog post, I will pick out some typical tensor operations and give brief explanations of them with some usage examples in OpenFOAM.
Keywords
strain rate tensor, vorticity tensor, Q-criterion, Hodge dual
Gradient of a Vector Field | fvc::grad(u)
The gradient of a velocity vector \(\boldsymbol{u}\) returns a velocity gradient tensor (second rank tensor).
\begin{eqnarray}
\nabla \boldsymbol{u} &\equiv& \partial_{i} u_j \\
&=& \left(
\begin{matrix}
\partial u_1/\partial x_1 & \partial u_2/\partial x_1 & \partial u_3/\partial x_1 \\
\partial u_1/\partial x_2 & \partial u_2/\partial x_2 & \partial u_3/\partial x_2 \\
\partial u_1/\partial x_3 & \partial u_2/\partial x_3 & \partial u_3/\partial x_3
\end{matrix} \right)
\end{eqnarray}
Symmetric Part of a Second Rank Tensor | symm(T)
\begin{eqnarray}
{\rm symm}(\boldsymbol{T}) &\equiv& \frac{1}{2} (\boldsymbol{T} + \boldsymbol{T}^T) \\
&=& \frac{1}{2} \left(
\begin{matrix}
2T_{11} & T_{12} + T_{21} & T_{13} + T_{31} \\
T_{21} + T_{12} & 2T_{22} & T_{23} + T_{32} \\
T_{31} + T_{13} & T_{32} + T_{23} & 2T_{33}
\end{matrix} \right)
\end{eqnarray}
| 1 2 3 4 5 6 7 8 9 10 11 | //- Return the symmetric part of a tensor template<class Cmpt> inline SymmTensor<Cmpt> symm(const Tensor<Cmpt>& t) {     return SymmTensor<Cmpt>     (         t.xx(), 0.5*(t.xy() + t.yx()), 0.5*(t.xz() + t.zx()),                 t.yy(),                0.5*(t.yz() + t.zy()),                                        t.zz()     ); } | 
Twice the Symmetric Part of a Second Rank Tensor | twoSymm(T)
\begin{eqnarray}
{\rm twoSymm}(\boldsymbol{T}) &\equiv& \boldsymbol{T} + \boldsymbol{T}^T \\
&=& \left(
\begin{matrix}
2T_{11} & T_{12} + T_{21} & T_{13} + T_{31} \\
T_{21} + T_{12} & 2T_{22} & T_{23} + T_{32} \\
T_{31} + T_{13} & T_{32} + T_{23} & 2T_{33}
\end{matrix} \right)
\end{eqnarray}
| 1 2 3 4 5 6 7 8 9 10 11 | //- Return twice the symmetric part of a tensor template<class Cmpt> inline SymmTensor<Cmpt> twoSymm(const Tensor<Cmpt>& t) {     return SymmTensor<Cmpt>     (         2*t.xx(), (t.xy() + t.yx()), (t.xz() + t.zx()),                   2*t.yy(),          (t.yz() + t.zy()),                                      2*t.zz()     ); } | 
Skew-symmetric Part of a Second Rank Tensor | skew(T)
\begin{eqnarray}
{\rm skew}(\boldsymbol{T}) &\equiv& \frac{1}{2} (\boldsymbol{T} – \boldsymbol{T}^T) \\
&=& \frac{1}{2} \left(
\begin{matrix}
0 & T_{12} – T_{21} & T_{13} – T_{31} \\
T_{21} – T_{12} & 0 & T_{23} – T_{32} \\
T_{31} – T_{13} & T_{32} – T_{23} & 0
\end{matrix} \right)
\end{eqnarray}
| 1 2 3 4 5 6 7 8 9 10 11 | //- Return the skew-symmetric part of a tensor template<class Cmpt> inline Tensor<Cmpt> skew(const Tensor<Cmpt>& t) {     return Tensor<Cmpt>     (         0.0, 0.5*(t.xy() - t.yx()), 0.5*(t.xz() - t.zx()),         0.5*(t.yx() - t.xy()), 0.0, 0.5*(t.yz() - t.zy()),         0.5*(t.zx() - t.xz()), 0.5*(t.zy() - t.yz()), 0.0     ); } | 
The symm and skew operations of the velocity gradient tensor field frequently appear in the source code. The following is a typical example.
| 1 2 3 4 5 6 |     tmp<volTensorField> tgradU(fvc::grad(U));     const volTensorField& gradU = tgradU();     volSymmTensorField b(dev(R)/(2*k_));     volSymmTensorField S(symm(gradU));     volTensorField Omega(skew(gradU)); | 
In the above code, the symmetric and antisymmetric parts of the velocity gradient tensor \(\partial u_j/\partial x_i\) are defined as follows:
\begin{eqnarray}
S_{ij} &=& \frac{1}{2} \left( \frac{\partial u_j}{\partial x_i} + \frac{\partial u_i}{\partial x_j} \right), \\
\Omega_{ij} &=& \frac{1}{2} \left( \frac{\partial u_j}{\partial x_i} – \frac{\partial u_i}{\partial x_j} \right),
\end{eqnarray}
where \(S_{ij}\) is the strain rate tensor and \(-\Omega_{ij}\) is the vorticity (spin) tensor.
Hodge Dual | *T
\begin{equation}
*T = \left( T_{23},\;-T_{13},\;T_{12} \right)
\end{equation}
| 1 2 3 4 5 | template<class Cmpt> inline Vector<Cmpt> operator*(const Tensor<Cmpt>& t) {     return Vector<Cmpt>(t.yz(), -t.xz(), t.xy()); } | 
The vorticity vector \(\boldsymbol{\omega}\) is calculated as the Hodge dual of the skew-symmetric part of the velocity gradient tensor.
\begin{eqnarray}
\boldsymbol{\omega} &=& 2 \times \left( * \Omega_{ij} \right) \\
&=& 2 \times \left( \Omega_{23},\;-\Omega_{13},\;\Omega_{12} \right) \\
&=& \left( \frac{\partial u_3}{\partial x_2}-\frac{\partial u_2}{\partial x_3},\;\frac{\partial u_1}{\partial x_3}-\frac{\partial u_3}{\partial x_1},\;\frac{\partial u_2}{\partial x_1}-\frac{\partial u_1}{\partial x_2} \right)
\end{eqnarray}
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | template<class Type> tmp<GeometricField<Type, fvPatchField, volMesh>> curl (     const GeometricField<Type, fvPatchField, volMesh>& vf ) {     word nameCurlVf = "curl(" + vf.name() + ')';     // Gausses theorem curl     // tmp<GeometricField<Type, fvPatchField, volMesh>> tcurlVf =     //     fvc::surfaceIntegrate(vf.mesh().Sf() ^ fvc::interpolate(vf));     // Calculate curl as the Hodge dual of the skew-symmetric part of grad     tmp<GeometricField<Type, fvPatchField, volMesh>> tcurlVf =         2.0*(*skew(fvc::grad(vf, nameCurlVf)));     tcurlVf.ref().rename(nameCurlVf);     return tcurlVf; } | 
The operator * in front of the skew is the Hodge dual operator.
Inner Product of Two Second Rank Tensors | T & S
\begin{equation}
P_{ij} = T_{ik} S_{kj}
\end{equation}
| 1 2 3 4 5 6 7 8 9 10 11 |     tmp<volSymmTensorField> tS = dev(symm(gradU));     const volSymmTensorField& S = tS();     volScalarField W     (         (2*sqrt(2.0))*((S&S)&&S)        /(             magS*S2           + dimensionedScalar("small", dimensionSet(0, 0, -3, 0, 0), SMALL)         )     ); | 
Double Inner Product of Two Second Rank Tensors | T && S
\begin{align}
s = T_{ij}S_{ij} &= T_{11}S_{11} + T_{12}S_{12} + T_{13}S_{13} \\
&+ T_{21}S_{21} + T_{22}S_{22} + T_{23}S_{23} \\
&+ T_{31}S_{31} + T_{32}S_{32} + T_{33}S_{33}
\end{align}
| 1 2 3 4 5 6 |     tmp<volTensorField> tgradU = fvc::grad(U);     volScalarField::Internal G     (         this->GName(),         nut.v()*(dev(twoSymm(tgradU().v())) && tgradU().v())     ); | 
Trace of a Second Rank Tensor | tr(T)
\begin{equation}
{\rm tr}(\boldsymbol{T}) \equiv T_{11} + T_{22} + T_{33} = \boldsymbol{T} {\rm \&}{\rm \&} \boldsymbol{I}
\end{equation}
| 1 2 3 4 5 6 | //- Return the trace of a tensor template<class Cmpt> inline Cmpt tr(const Tensor<Cmpt>& t) {     return t.xx() + t.yy() + t.zz(); } | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | bool Foam::functionObjects::Q::calc() {     if (foundObject<volVectorField>(fieldName_))     {         const volVectorField& U = lookupObject<volVectorField>(fieldName_);         const tmp<volTensorField> tgradU(fvc::grad(U));         const volTensorField& gradU = tgradU();         return store         (             resultName_,             0.5*(sqr(tr(gradU)) - tr(((gradU) & (gradU))))         );     }     else     {         return false;     } } | 
The Q-criterion can be used to identify vortex cores:
\begin{align}
Q &= \frac{1}{2} \left[ (\nabla \cdot \boldsymbol{u})^2 – {\rm tr}(\nabla \boldsymbol{u}^2)\right] \\
&= \frac{1}{2} \left[ (\nabla \cdot \boldsymbol{u})^2 + \Omega_{ij}\Omega_{ij} – S_{ij}S_{ij} \right].
\end{align}
For incompressible flows, it can be simplified as follows:
\begin{equation}
Q = \frac{1}{2} \left[ \Omega_{ij}\Omega_{ij} – S_{ij}S_{ij} \right].
\end{equation}
References
[1] OpenFOAM Programmer’s Guide
[2] CFD Direct | Tensor Mathematics
 
	
ありがとうございます!