sandbox/acastillo/output_fields/tests_quantities/test_strain_and_vorticity_smooth.c

    Testing strain_and_vorticity_sq() on a smooth field

    Where test_strain_and_vorticity_affine.c removes the truncation error to isolate the algebra, this checks the truncation error itself: on a smooth field the centred differences in strain_and_vorticity.h are second-order, so the error must fall as \Delta^2.

    Only \Omega^2 is measured here. Its S^2 is bit-for-bit strain_rate_sq()’s – the affine test asserts exactly that – so the convergence of S^2 is already covered by test_strain_rate_smooth.c, and repeating it would freeze a second reference for the same numbers.

    The fields are those of test_strain_rate_smooth.c. In 2D, from \psi = \cos(k_1 x)\cos(k_2 y) with \mathbf{u} = (\partial_y\psi, -\partial_x\psi), the only vorticity component is \omega_z = -\nabla^2\psi = (k_1^2 + k_2^2)\cos(k_1x)\cos(k_2y). In 3D the ABC flow is a Beltrami field, \boldsymbol\omega = k\mathbf{u}. Either way

    \Omega_{ij}\Omega_{ij} = \tfrac{1}{2}|\boldsymbol\omega|^2

    which is what the exact value below evaluates – an independent route to \Omega^2, not a transcription of the code’s expression.

    The 3D table comes out equal to test_strain_rate_smooth.c’s, which is expected rather than a stray copy: the ABC flow has S_{ij}S_{ij} + \Omega_{ij}\Omega_{ij} = k^2(A^2 + B^2 + C^2), a constant, so the two errors differ only in sign and share a normalisation. The fields themselves are far apart – S^2 and \Omega^2 differ by up to 529 here – so the test still fails loudly if \Omega^2 is wrong. The 2D table does not coincide.

    Cells are inset one layer from the boundary, since the stencil would otherwise reach a ghost cell – which is also why no periodicity is imposed. strain_and_vorticity_smooth.asc gets one row per resolution, N err_max err_l2, both normalised by \max|\Omega^2|; test_strain_and_vorticity.py turns them into convergence orders.

    #include "run.h"
    #include "acastillo/output_fields/strain_and_vorticity.h"
    
    double k1, k2, kabc;
    double Aabc = 1.0, Babc = 2.0, Cabc = 3.0;
    
    double O2_smooth (double x, double y, double z) {
    #if dimension == 3
      // Beltrami: omega = kabc*u, so O2 = |omega|^2/2.
      double ux = Aabc*sin(kabc*z) + Cabc*cos(kabc*y);
      double uy = Babc*sin(kabc*x) + Aabc*cos(kabc*z);
      double uz = Cabc*sin(kabc*y) + Babc*cos(kabc*x);
      return 0.5*sq(kabc)*(sq(ux) + sq(uy) + sq(uz));
    #else
      double omega = (sq(k1) + sq(k2))*cos(k1*x)*cos(k2*y);
      return 0.5*sq(omega);
    #endif
    }
    
    int main() {
    
      L0 = 1.0;
      X0 = Y0 = Z0 = -L0 / 2;
    
      k1   = 2.*pi/L0;
      k2   = 4.*pi/L0;
      kabc = 2.*pi/L0;
    
      FILE * fp = NULL;
      if (pid() == 0) {
        fp = fopen ("strain_and_vorticity_smooth.asc", "w");
        fprintf (fp, "# N err_max err_l2\n");
      }
    
      // Three resolutions give two independent convergence rates. The coarsest 3D
      // grid still resolves the single wavelength with 8 cells.
    #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[];
    
        foreach() {
    #if dimension == 3
          u.x[] = Aabc*sin(kabc*z) + Cabc*cos(kabc*y);
          u.y[] = Babc*sin(kabc*x) + Aabc*cos(kabc*z);
          u.z[] = Cabc*sin(kabc*y) + Babc*cos(kabc*x);
    #else
          u.x[] = -k2*cos(k1*x)*sin(k2*y);
          u.y[] =  k1*sin(k1*x)*cos(k2*y);
    #endif
        }
    
        double err_max = 0., err_sq = 0., o2_max = 0., vol = 0.;
        foreach (reduction(max:err_max) reduction(+:err_sq)
                 reduction(max:o2_max) reduction(+:vol)) {
          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 o2_exact = O2_smooth (x, y, z);
            double e = fabs (O2 - o2_exact);
            if (e > err_max)
              err_max = e;
            if (fabs(o2_exact) > o2_max)
              o2_max = fabs(o2_exact);
            err_sq += dv()*sq(e);
            vol += dv();
          }
        }
    
        if (pid() == 0) {
          fprintf (fp, "%d %.17g %.17g\n", Nres, err_max/o2_max,
                   sqrt(err_sq/vol)/o2_max);
          fflush (fp);
        }
      }
    
      if (pid() == 0) {
        fclose (fp);

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

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