Menu

verificationCases/tubeGeometry.c

Axisymmetric embedded-tube geometry verification

Checks the exact volume and axial area of a straight tube on a deliberately nonuniform, repeatedly adapted quadtree. No flow equations are solved. The geometric integrals omit the common azimuthal factor of \(2\pi\).

#include "grid/quadtree.h"
#include "embed.h"
#include "axi.h"
#include "run.h"
#include "embed-vof-tube.h"
#include <errno.h>

static const double RT = 0.7;
static int MAXLEVEL = 9, failed = 0;

int main (int argc, char ** argv)
{
  if (argc > 2) {
    fprintf (stderr, "FAIL expected at most one maximum-level argument\n");
    return 2;
  }
  if (argc > 1) {
    char * end;
    errno = 0;
    long value = strtol (argv[1], &end, 10);
    if (errno || end == argv[1] || *end != '\0' || value < 6 || value > 13) {
      fprintf (stderr, "FAIL maximum level must be an integer between 6 and\n");
      return 2;
    }
    MAXLEVEL = (int) value;
  }
  size (16.);
  origin (0., 0.);
  init_grid (1 << 4);
  run();
  return failed;
}

event init (i = 0)
{
  /* Cross the wall with several resolution transitions from the outset. */
  refine (y < 1.1 && level < 6);
  refine (y < 0.9 && x > 0.75 && x < 3.25 && level < MAXLEVEL);
  refine (fabs(y - RT) < 0.08 && x > 6. && x < 10. &&
          level < MAXLEVEL - 1);
  tube_solid (RT);
}

event geometry_cycle (i = 0; i <= 8; i++)
{
  /* Moving compact features force both refinement and coarsening through the
     wall and through changing MPI ownership/halo topology. */
  scalar marker[];
  double xc = 1.5 + 1.65*i;
  foreach()
    marker[] = exp (-sq((x - xc)/0.35) - sq((y - RT)/0.055));
  adapt_wavelet ({marker, cs}, (double[]){2e-2, 1e-3},
                 MAXLEVEL, 4);
  embed_axi_metric_sync();

  /* Synchronise fractions and metrics before evaluating the geometry. */
  boundary ({cs, fs, cm, fm});

  double volume = 0., inlet = 0., outlet = 0., divmax = 0.;
  int bad = 0;
  foreach (reduction(+:volume) reduction(max:divmax)) {
    volume += cm[]*sq(Delta);
    if (cs[] > 0.) {
      double residual = fabs (fm.x[1] - fm.x[]);
      if (residual > divmax)
        divmax = residual;
    }
  }
  foreach_boundary (left, reduction(+:inlet))
    inlet += fm.x[]*Delta;
  foreach_boundary (right, reduction(+:outlet))
    outlet += fm.x[1]*Delta;
  foreach_face (reduction(+:bad))
    if ((is_active(cell) || is_active(neighbor(-1))) &&
        fs.x[] > 0. && fm.x[] <= 0.)
      bad++;

  const double exact_volume = L0*sq(RT)/2.;
  const double exact_area = sq(RT)/2.;
  double ev = fabs(volume - exact_volume);
  double ei = fabs(inlet - exact_area);
  double eo = fabs(outlet - exact_area);
  int pass = ev <= 5e-12 && ei <= 5e-12 && eo <= 5e-12 &&
             divmax <= 5e-12 && bad == 0;

  if (pid() == 0) {
    if (i == 0)
      fprintf (stdout,
               "cycle,volume,volume_error,inlet_area,inlet_error,"
               "outlet_area,outlet_error,constant_flux_divmax,bad_faces,status\n");
    fprintf (stdout, "%d,%.17g,%.17g,%.17g,%.17g,%.17g,%.17g,"
             "%.17g,%d,%s\n", i, volume, ev, inlet, ei, outlet, eo,
             divmax, bad, pass ? "PASS" : "FAIL");
    fflush (stdout);
  }
  if (!pass) {
    if (pid() == 0)
      fprintf (stderr, "FAIL geometry predicate at cycle %d\n", i);
    failed = 1;
    return 1;
  }
  if (i == 8) {
    if (pid() == 0)
      fprintf (stdout, "PASS\n");
    return 1;
  }
}