sandbox/acastillo/output_fields/strain_and_vorticity.h

    Given a velocity field \mathbf{u}, this function returns the squared magnitudes of both the strain-rate and the rotation-rate tensors at a point, S^2 = S_{ij}S_{ij}, \qquad \Omega^2 = \Omega_{ij}\Omega_{ij} with S_{ij} = \frac{1}{2}\left(\partial_i u_j + \partial_j u_i\right), \qquad \Omega_{ij} = \frac{1}{2}\left(\partial_i u_j - \partial_j u_i\right) from one set of centred differences on the cell-centred velocity, instead of differencing the velocity twice.

    \Omega_{ij} is antisymmetric (\Omega_{xx} = \Omega_{yy} = \Omega_{zz} = 0), so only the off-diagonal terms contribute: \Omega_{ij}\Omega_{ij} = 2(\Omega_{xy}^2 + \Omega_{xz}^2 + \Omega_{yz}^2) in 3D, 2\Omega_{xy}^2 in 2D. Equivalently \Omega_{ij}\Omega_{ij} = \frac{1}{2}|\boldsymbol\omega|^2.

    Together they give the Q-criterion for vortex identification, Q = \frac{1}{2}(\Omega_{ij}\Omega_{ij} - S_{ij}S_{ij}).

    The S^2 returned here is the same quantity as strain_rate_sq()’s, which remains the right call when \Omega^2 is not wanted. The two agree to roundoff rather than bit-for-bit: the off-diagonal terms are summed as 2S_{xy}^2 here and as S_{xy}^2 + S_{yx}^2 there, which rounds differently. Same caveats as that function: the metric factors fm and cm are not taken into account, so this is valid for Cartesian coordinates only, unlike vorticity3d(). Neither tensor is trace-removed, which is consistent with \nabla\cdot\mathbf{u} = 0.

    #ifndef STRAIN_AND_VORTICITY_H
    #define STRAIN_AND_VORTICITY_H
    
    static inline void strain_and_vorticity_sq (Point point, vector u,
                                                 double * S2, double * O2) {
      double dudx = (u.x[1]     - u.x[-1]    )/(2.*Delta);
      double dvdx = (u.y[1]     - u.y[-1]    )/(2.*Delta);
      double dudy = (u.x[0,1]   - u.x[0,-1]  )/(2.*Delta);
      double dvdy = (u.y[0,1]   - u.y[0,-1]  )/(2.*Delta);
      double Sxx = dudx;
      double Syy = dvdy;
      double Sxy = 0.5*(dudy + dvdx);
      double Oxy = 0.5*(dvdx - dudy);
      *S2 = sq(Sxx) + sq(Syy) + 2.*sq(Sxy);
      *O2 = 2.*sq(Oxy);
      #if dimension == 3
        double dwdx = (u.z[1]     - u.z[-1]    )/(2.*Delta);
        double dwdy = (u.z[0,1]   - u.z[0,-1]  )/(2.*Delta);
        double dudz = (u.x[0,0,1] - u.x[0,0,-1])/(2.*Delta);
        double dvdz = (u.y[0,0,1] - u.y[0,0,-1])/(2.*Delta);
        double dwdz = (u.z[0,0,1] - u.z[0,0,-1])/(2.*Delta);
        double Szz = dwdz;
        double Sxz = 0.5*(dwdx + dudz);
        double Syz = 0.5*(dwdy + dvdz);
        double Oxz = 0.5*(dwdx - dudz);
        double Oyz = 0.5*(dwdy - dvdz);
        *S2 += sq(Szz) + 2.*sq(Sxz) + 2.*sq(Syz);
        *O2 += 2.*sq(Oxz) + 2.*sq(Oyz);
      #endif
    }
    
    #endif