sandbox/acastillo/output_fields/tests_quantities/test_strain_and_vorticity_affine.c

    Testing strain_and_vorticity_sq() on an affine field

    On u_i = A_{ij}x_j with \mathrm{tr}(A) = 0, the gradient is constant, so the centred differences in strain_and_vorticity.h are exact and both

    S^2 = S_{ij}S_{ij}, \qquad S_{ij} = \tfrac{1}{2}(A_{ij} + A_{ji}) \Omega^2 = \Omega_{ij}\Omega_{ij}, \qquad \Omega_{ij} = \tfrac{1}{2}(A_{ij} - A_{ji})

    are single constants. Any error is then algebraic, not truncation. The same matrices as test_strain_rate_affine.c are used, so the shared S^2 is compared against the same reference value; they are chosen with A_{ij} \neq A_{ji} throughout, or \Omega^2 would vanish and go untested.

    The third column checks the claim in the header that this S^2 is the same quantity as strain_rate_sq()’s, the two living side by side. They are not bit-for-bit: the off-diagonal terms are summed as 2S_{xy}^2 in one and S_{xy}^2 + S_{yx}^2 in the other, so the two round differently and the check is against roundoff, not against zero. What it catches is the pair drifting apart algebraically.

    Cells are inset one layer from the boundary, since the stencil would otherwise reach a ghost cell. strain_and_vorticity_affine.asc gets one row per resolution, N err_S2 err_O2 err_cons, the first two the max relative deviation from the constants; test_strain_and_vorticity.py applies the thresholds.

    #include "run.h"
    #include "acastillo/output_fields/strain_and_vorticity.h"
    #include "acastillo/output_fields/strain_rate.h"
    
    #if dimension == 3
    double A_aff[3][3] = {{ 0.7,  1.3,  0.5},
                          { 0.4, -0.3,  0.9},
                          { 0.2,  0.6, -0.4}};
    #else
    double A_aff[2][2] = {{ 0.7,  1.3},
                          { 0.4, -0.7}};
    #endif
    
    int main() {
    
      L0 = 1.0;
      X0 = Y0 = Z0 = -L0 / 2;
    
      // Contracted from A_aff, so they cannot drift from the coefficients.
      double s2_exact = 0., o2_exact = 0.;
      for (int i = 0; i < dimension; i++)
        for (int j = 0; j < dimension; j++) {
          s2_exact += sq (0.5*(A_aff[i][j] + A_aff[j][i]));
          o2_exact += sq (0.5*(A_aff[i][j] - A_aff[j][i]));
        }
    
      FILE * fp = NULL;
      if (pid() == 0) {
        fp = fopen ("strain_and_vorticity_affine.asc", "w");
        fprintf (fp, "# N err_S2 err_O2 err_cons\n");
      }
    
      // Three resolutions, so a flat error is distinguishable from a small one.
    #if dimension == 3
      int Nmin = 8, Nmax = 32;
    #else
      int Nmin = 32, Nmax = 128;
    #endif
    
      for (int Nres = Nmin; Nres <= Nmax; Nres *= 2) {
        init_grid (Nres);
    
        vector u[];
    
        // Component by component: the row of A_aff differs per component, which
        // foreach_dimension() cannot express.
        foreach() {
    #if dimension == 3
          double px = x, py = y, pz = z;
          u.x[] = A_aff[0][0]*px + A_aff[0][1]*py + A_aff[0][2]*pz;
          u.y[] = A_aff[1][0]*px + A_aff[1][1]*py + A_aff[1][2]*pz;
          u.z[] = A_aff[2][0]*px + A_aff[2][1]*py + A_aff[2][2]*pz;
    #else
          double px = x, py = y;
          u.x[] = A_aff[0][0]*px + A_aff[0][1]*py;
          u.y[] = A_aff[1][0]*px + A_aff[1][1]*py;
    #endif
        }
    
        double err_s2 = 0., err_o2 = 0., err_cons = 0.;
        foreach (reduction(max:err_s2) reduction(max:err_o2)
                 reduction(max:err_cons)) {
          bool interior = fabs(x) < L0/2. - 1.1*Delta && fabs(y) < L0/2. - 1.1*Delta;
    #if dimension == 3
          interior = interior && fabs(z) < L0/2. - 1.1*Delta;
    #endif
          if (interior) {
            double S2, O2;
            strain_and_vorticity_sq (point, u, &S2, &O2);
            double e = fabs (S2 - s2_exact);
            if (e > err_s2)
              err_s2 = e;
            e = fabs (O2 - o2_exact);
            if (e > err_o2)
              err_o2 = e;
            e = fabs (S2 - strain_rate_sq (point, u));
            if (e > err_cons)
              err_cons = e;
          }
        }
    
        if (pid() == 0) {
          fprintf (fp, "%d %.17g %.17g %.17g\n", Nres, err_s2/s2_exact,
                   err_o2/o2_exact, err_cons/s2_exact);
          fflush (fp);
        }
      }
    
      if (pid() == 0) {
        fclose (fp);

    The report goes to stderr, i.e. to the log diffed against test_strain_and_vorticity_affine.ref.

        system ("python3 ../test_strain_and_vorticity.py "
                "--affine strain_and_vorticity_affine.asc 1>&2");
      }
    }