src/grid/cartesian-common.h

    #include "events.h"
    
    void (* debug)    (Point);
    
    @define _val_constant(a,k,l,m) ((const double) _constant[a.i -_NVARMAX])
    @define diagonalize(a)
    @define val_diagonal(a,k,l,m) ((k) == 0 && (l) == 0 && (m) == 0)
    
    @undef VARIABLES
    @def VARIABLES
      double Delta = L0*_DELTA; /* cell size */
      double Delta_x = Delta; /* cell size (with mapping) */
    #if dimension > 1
      double Delta_y = Delta; /* cell size (with mapping) */
    #endif
    #if dimension > 2
      double Delta_z = Delta; /* cell size (with mapping) */
    #endif
      /* cell/face center coordinates */
      double x = (ig/2. + _I + 0.5)*Delta + X0; NOT_UNUSED(x);
    #if dimension > 1
      double y = (jg/2. + _J + 0.5)*Delta + Y0;
    #else
      double y = 0.;
    #endif
     NOT_UNUSED(y);
    #if dimension > 2
      double z = (kg/2. + _K + 0.5)*Delta + Z0;
    #else
      double z = 0.;
    #endif
      NOT_UNUSED(z);
      /* we need this to avoid compiler warnings */
      NOT_UNUSED(Delta);
      NOT_UNUSED(Delta_x);
    #if dimension > 1
      NOT_UNUSED(Delta_y);
    #endif
    #if dimension > 2
      NOT_UNUSED(Delta_z);
    #endif
      /* and this when catching FPEs */
      _CATCH;
    @
    
    #include "fpe.h"
    
    @define end_foreach_face()
    
    @def foreach_point(...)
    {
      int ig = 0, jg = 0, kg = 0; NOT_UNUSED(ig); NOT_UNUSED(jg); NOT_UNUSED(kg);
      coord _p = { S__VA_ARGS__ };
      Point point = locate (_p.x, _p.y, _p.z); // fixme
      if (point.level >= 0) {
        POINT_VARIABLES
    @
    @define end_foreach_point() }}
    
    @def foreach_region(p, box, n)
     OMP_PARALLEL() { NOT_UNUSED (p);
        coord p = {0, 0, box[0].z};
        OMP(omp for schedule(static))
          for (int _i = 0; _i < (int) n.x; _i++) {
    	p.x = box[0].x + (box[1].x - box[0].x)/n.x*(_i + 0.5);
    	for (int _j = 0; _j < (int) n.y; _j++) {
    	  p.y = box[0].y + (box[1].y - box[0].y)/n.y*(_j + 0.5);
    	  Point point = locate (p.x, p.y, p.z); // fixme
    	  if (point.level >= 0) {
    	    int ig = 0, jg = 0, kg = 0; NOT_UNUSED(ig); NOT_UNUSED(jg); NOT_UNUSED(kg);
    	    POINT_VARIABLES
    @
    @define end_foreach_region() }}}}

    Register functions on GPUs

    #if _GPU
    #include "khash.h"
    KHASH_MAP_INIT_INT64(PTR, External)
    
    static khash_t(PTR) * _functions = NULL;
      
    static External * _get_function (long ptr)
    {
      if (!_functions)
        return NULL;  
      khiter_t k = kh_get (PTR, _functions, ptr);
      if (k == kh_end (_functions))
        return NULL;
      return &kh_value (_functions, k);
    }
    
    static void register_function (void (* ptr) (void), const char * name,
    			       const char * kernel, const void * externals)
    {
      static int index = 1;
      if (!_functions)
        _functions = kh_init (PTR), index = 1;
      int m = 0;
      for (const External * i = externals; i && i->name; i++, m++);
      External * copy = NULL;
      if (m > 0) {
        copy = malloc ((m + 1)*sizeof (External));
        memcpy (copy, externals, (m + 1)*sizeof (External));
      }
      int ret;
      khiter_t k = kh_put(PTR, _functions, (long) ptr, &ret);
      External p = {
        .name = (char *) name,
        .type = sym_function_definition,
        .pointer = (void *)(long) ptr, .nd = index++,
        .data = (void *) kernel, .externals = copy
      };
      kh_value(_functions, k) = p;
    }
    
    #define foreach_function(f, body) do {					\
        for (khiter_t k = kh_begin(_functions); k != kh_end(_functions); ++k) \
          if (kh_exist(_functions, k)) {					\
    	External * f = &kh_value(_functions, k);			\
    	body;								\
          }									\
      } while(0)
    
    #endif // _GPU

    Field allocation

    If this routine is modified, do not forget to update /src/ast/interpreter/overload.h.

    static void init_block_scalar (scalar sb, const char * name, const char * ext,
    			       int n, int block)
    {
      char bname[strlen(name) + strlen(ext) + 10];
      if (n == 0) {
        strcat (strcpy (bname, name), ext);
        sb.block = block;
        baseblock = list_append (baseblock, sb);
      }
      else {
        sprintf (bname, "%s%d%s", name, n, ext);
        sb.block = - n;
      }
      sb.name = strdup (bname);
      all = list_append (all, sb);
    }
    
    @define interpreter_set_int(...)
    @define interpreter_reset_scalar(...)
    
    scalar alloc_block_scalar (const char * name, const char * ext, int block)
    {
      interpreter_set_int (&block);
      int nvar = datasize/sizeof(real);
    
      scalar s = {0};
      while (s.i < nvar) {
        int n = 0;
        scalar sb = s;
        while (sb.i < nvar && n < block && sb.freed)
          n++, sb.i++;
        if (n >= block) { // found n free slots
          memset (&_attribute[s.i], 0, block*sizeof (_Attributes));
          for (sb.i = s.i, n = 0; n < block; n++, sb.i++) {
    	init_block_scalar (sb, name, ext, n, block);
    	interpreter_reset_scalar (sb);
          }
          trash (((scalar []){s, {-1}})); // fixme: only trashes one block?
          return s;
        }
        s.i = sb.i + 1;
      }
      
      // need to allocate new slots
      s = (scalar){nvar};
      assert (nvar + block <= _NVARMAX);
    
      if (_attribute == NULL)
        _attribute = (_Attributes *) calloc (nvar + block + 1, sizeof (_Attributes));
      else
        _attribute = (_Attributes *)
          realloc (_attribute, (nvar + block + 1)*sizeof (_Attributes));
      memset (&_attribute[nvar], 0, block*sizeof (_Attributes));
      for (int n = 0; n < block; n++, nvar++) {
        scalar sb = (scalar){nvar};
        init_block_scalar (sb, name, ext, n, block);
      }
      // allocate extra space on the grid
      realloc_scalar (block*sizeof(real));
      trash (((scalar []){s, {-1}})); // fixme: only trashes one block?
      return s;
    }
    
    scalar new_block_scalar (const char * name, const char * ext, int block)
    {
      scalar s = alloc_block_scalar (name, ext, block), sb;
      int n = 0;
      for (sb.i = s.i, n = 0; n < block; n++, sb.i++)
        init_scalar (sb, NULL);
      return s;
    }
      
    scalar new_scalar (const char * name)
    {
      return init_scalar (alloc_block_scalar (name, "", 1), NULL);
    }
    
    scalar new_vertex_scalar (const char * name)
    {
      return init_vertex_scalar (alloc_block_scalar (name, "", 1), NULL);
    }
    
    static vector alloc_block_vector (const char * name, int block)
    {
      vector v;
      struct { char * x, * y, * z; } ext = {".x", ".y", ".z"};
      foreach_dimension()
        v.x = alloc_block_scalar (name, ext.x, block);
      return v;
    }
    
    vector new_vector (const char * name)
    {
      vector v = alloc_block_vector (name, 1);
      init_vector (v, NULL);
      return v;
    }
    
    vector new_face_vector (const char * name)
    {
      vector v = alloc_block_vector (name, 1);
      init_face_vector (v, NULL);
      return v;
    }
    
    vector new_block_vector (const char * name, int block)
    {
      vector v = alloc_block_vector (name, block);
      for (int i = 0; i < block; i++) {
        vector vb;
        foreach_dimension()
          vb.x.i = v.x.i + i;
        init_vector (vb, NULL);
        foreach_dimension()
          vb.x.block = - i;
      }
      foreach_dimension()
        v.x.block = block;
      return v;
    }
    
    vector new_block_face_vector (const char * name, int block)
    {
      vector v = alloc_block_vector (name, block);
      for (int i = 0; i < block; i++) {
        vector vb;
        foreach_dimension()
          vb.x.i = v.x.i + i;
        init_face_vector (vb, NULL);
        foreach_dimension()
          vb.x.block = - i;
      }
      foreach_dimension()
        v.x.block = block;  
      return v;
    }
    
    tensor new_tensor (const char * name)
    {
      char cname[strlen(name) + 3];
      struct { char * x, * y, * z; } ext = {".x", ".y", ".z"};
      tensor t;
      foreach_dimension() {
        strcat (strcpy (cname, name), ext.x);
        t.x = alloc_block_vector (cname, 1);
      }
      init_tensor (t, NULL);
      return t;
    }
    
    tensor new_symmetric_tensor (const char * name)
    {
      struct { char * x, * y, * z; } ext = {".x.x", ".y.y", ".z.z"};
      tensor t;
      foreach_dimension()
        t.x.x = alloc_block_scalar (name, ext.x, 1);
      #if dimension > 1
        t.x.y = alloc_block_scalar (name, ".x.y", 1);
        t.y.x = t.x.y;
      #endif
      #if dimension > 2
        t.x.z = alloc_block_scalar (name, ".x.z", 1);
        t.z.x = t.x.z;
        t.y.z = alloc_block_scalar (name, ".y.z", 1);
        t.z.y = t.y.z;
      #endif
      /* fixme: boundary conditions don't work!  This is because boundary
         attributes depend on the index and should (but cannot) be
         different for t.x.y and t.y.x */
      init_tensor (t, NULL);
      return t;
    }
    
    static int nconst = 0;
    
    void init_const_scalar (scalar s, const char * name, double val)
    {
      if (s.i - _NVARMAX >= nconst) {
        qrealloc (_constant, s.i - _NVARMAX + 1, double);
        for (int i = nconst; i < s.i - _NVARMAX; i++)
          _constant[i] = 0.;
        nconst = s.i - _NVARMAX + 1;
      }
      _constant[s.i - _NVARMAX] = val;
    }
    
    scalar new_const_scalar (const char * name, int i, double val)
    {
      scalar s = (scalar){i + _NVARMAX};
      init_const_scalar (s, name, val);
      return s;
    }
    
    void init_const_vector (vector v, const char * name, double * val)
    {
      foreach_dimension()
        init_const_scalar (v.x, name, *val++);
    }
    
    vector new_const_vector (const char * name, int i, double * val)
    {
      vector v;
      foreach_dimension()
        v.x.i = _NVARMAX + i++;
      init_const_vector (v, name, val);
      return v;
    }
    
    static void cartesian_scalar_clone (scalar clone, scalar src)
    {
      char * cname = clone.name;
      BoundaryFunc * boundary = clone.boundary;
      BoundaryFunc * boundary_homogeneous = clone.boundary_homogeneous;
      assert (src.block > 0 && clone.block == src.block);
      free (clone.depends);
      _attribute[clone.i] = _attribute[src.i];
      clone.name = cname;
      clone.boundary = boundary;
      clone.boundary_homogeneous = boundary_homogeneous;
      for (int i = 0; i < nboundary; i++) {
        clone.boundary[i] = src.boundary[i];
        clone.boundary_homogeneous[i] = src.boundary_homogeneous[i];
      }
      clone.depends = list_copy (src.depends);
    }
    
    scalar * list_clone (scalar * l)
    {
      scalar * list = NULL;
      int nvar = datasize/sizeof(real), map[nvar];
      for (int i = 0; i < nvar; i++)
        map[i] = -1;
      for (scalar s in l) {
        scalar c = s.block > 1 ? new_block_scalar("c", "", s.block) : new_scalar("c");
        scalar_clone (c, s);
        map[s.i] = c.i;
        list = list_append (list, c);
      }
      for (scalar s in list)
        foreach_dimension()
          if (s.v.x.i >= 0 && map[s.v.x.i] >= 0)
    	s.v.x.i = map[s.v.x.i];
      return list;
    }
    
    void delete (scalar * list)
    {
      if (all == NULL) // everything has already been freed
        return;
    
      for (scalar f in list) {
        for (int i = 0; i < f.block; i++) {
          scalar fb = {f.i + i};
          if (f.delete)
    	f.delete (fb);
          free (fb.name); fb.name = NULL;
          free (fb.boundary); fb.boundary = NULL;
          free (fb.boundary_homogeneous); fb.boundary_homogeneous = NULL;
          free (fb.depends); fb.depends = NULL;
          fb.freed = true;
        }
      }
      
      if (list == all) {
        all[0].i = -1;
        baseblock[0].i = -1;
        return;
      }
    
      trash (list);
      for (scalar f in list) {
        if (f.block > 0) {
          scalar * s;
          for (s = all; s->i >= 0 && s->i != f.i; s++);
          if (s->i == f.i) {
    	for (; s[f.block].i >= 0; s++)
    	  s[0] = s[f.block];
    	s->i = -1;
          }
          for (s = baseblock; s->i >= 0 && s->i != f.i; s++);
          if (s->i == f.i) {
    	for (; s[1].i >= 0; s++)
    	  s[0] = s[1];
    	s->i = -1;
          }
        }
      }
    }
    
    void free_solver()
    {  
      assert (_val_higher_dimension == 0.);
    
      if (free_solver_funcs) {
        free_solver_func * a = (free_solver_func *) free_solver_funcs->p;
        for (int i = 0; i < free_solver_funcs->len/sizeof(free_solver_func); i++)
          a[i] ();
        array_free (free_solver_funcs);
      }
      
      delete (all);
      free (all); all = NULL;
      free (baseblock); baseblock = NULL;
      for (Event * ev = Events; !ev->last; ev++) {
        Event * e = ev->next;
        while (e) {
          Event * next = e->next;
          free (e);
          e = next;
        }
      }
    
      free (Events); Events = NULL;
      free (_attribute); _attribute = NULL;
      free (_constant); _constant = NULL;
    #if _GPU
      foreach_function (f, free ((void *) f->externals));
      kh_destroy (PTR, _functions); _functions = NULL;
    #endif
      free_grid();
      qpclose_all();
    @if TRACE
      trace_off();
    @endif
    @if MTRACE
      pmuntrace();
    @endif
    @if _CADNA
      cadna_end();
    @endif
    }
    
    // Cartesian methods
    
    void (* boundary_level) (scalar *, int l);
    void (* boundary_face)  (vectorl);
    
    #define boundary(...)						\
      boundary_internal ((scalar *)__VA_ARGS__, __FILE__, LINENO)
    
    void boundary_flux  (vector * list) __attribute__ ((deprecated));
    
    void boundary_flux  (vector * list)
    {
      vectorl list1 = {NULL};
      for (vector v in list)
        foreach_dimension()
          list1.x = list_append (list1.x, v.x);
      boundary_face (list1);
      foreach_dimension()
        free (list1.x);
    }
    
    static scalar * list_add_depends (scalar * list, scalar s)
    {
      for (scalar t in list)
        if (t.i == s.i)
          return list;
      scalar * list1 = list;
      for (scalar d in _attribute[s.i].depends)
        if (d.dirty)
          list1 = list_add_depends (list1, d);
      return list_append (list1, s);
    }
    
    trace
    void boundary_internal (scalar * list, const char * fname, int line)
    {
      if (list == NULL)
        return;
      scalar * listc = NULL;
      vectorl listf = {NULL};
      bool flux = false;
      for (scalar s in list)
        if (!is_constant(s) && s.block > 0) {
          if (scalar_is_dirty (s)) {
    	if (s.face && s.dirty != 2)
    	  foreach_dimension()
    	    if (s.v.x.i == s.i)
    	      listf.x = list_add (listf.x, s), flux = true;
    	if (!is_constant(cm) && cm.dirty)
    	  listc = list_add_depends (listc, cm);
    	if (s.face != 2) // flux only
    	  listc = list_add_depends (listc, s);
          }
    #if 0
          else
    	fprintf (stderr, "warning: bc already applied on '%s'\n", s.name);
    #endif
        }
      if (flux) {
        boundary_face (listf);
        foreach_dimension()
          free (listf.x);
      }
      if (listc) {
    #if PRINTBOUNDARY
        fprintf (stderr, "boundary_internal: listc:");
        for (scalar s in listc)
          fprintf (stderr, " %d:%s", s.i, s.name);
        fputc ('\n', stderr);
    #endif
        boundary_level (listc, -1);
        for (scalar s in listc)
          s.dirty = false;
        free (listc);
      }
    }
    
    void cartesian_boundary_level (scalar * list, int l)
    {
      boundary_iterate (level, list, l);
    }
    
    void cartesian_boundary_face (vectorl list)
    {
      foreach_dimension()
        for (scalar s in list.x)
          s.dirty = 2;
    }
    
    static double symmetry (Point point, Point neighbor, scalar s, bool * data)
    {
      return s[];
    }
    
    static double antisymmetry (Point point, Point neighbor, scalar s, bool * data)
    {
      return -s[];
    }
    
    BoundaryFunc default_scalar_bc[] = {
      symmetry, symmetry, symmetry, symmetry, symmetry, symmetry
    };
    
    scalar cartesian_init_scalar (scalar s, const char * name)
    {
      // keep name
      char * pname;
      if (name) {
        free (s.name);
        pname = strdup (name);
      }
      else
        pname = s.name;
      int block = s.block;
      BoundaryFunc * boundary = s.boundary;
      BoundaryFunc * boundary_homogeneous = s.boundary_homogeneous;
      s.name = pname;
      if (block < 0)
        s.block = block;
      else
        s.block = block > 0 ? block : 1;
      /* set default boundary conditions */  
      s.boundary = boundary ? boundary : (BoundaryFunc *) malloc (nboundary*sizeof (BoundaryFunc));
      s.boundary_homogeneous = boundary_homogeneous ? boundary_homogeneous :
        (BoundaryFunc *) malloc (nboundary*sizeof (BoundaryFunc));
      for (int b = 0; b < nboundary; b++)
        s.boundary[b] = s.boundary_homogeneous[b] =
          b < 2*dimension ? default_scalar_bc[b] : symmetry;
      s.gradient = NULL;
      foreach_dimension() {
        s.d.x = 0;  // not face
        s.v.x.i = -1; // not a vector component
      }
      s.face = false;
      return s;
    }
    
    scalar cartesian_init_vertex_scalar (scalar s, const char * name)
    {
      cartesian_init_scalar (s, name);
      foreach_dimension()
        s.d.x = -1;
      for (int d = 0; d < nboundary; d++)
        s.boundary[d] = s.boundary_homogeneous[d] = NULL;
      return s;
    }
      
    BoundaryFunc default_vector_bc[] = {
      antisymmetry, antisymmetry,
      antisymmetry, antisymmetry,
      antisymmetry, antisymmetry
    };
    
    vector cartesian_init_vector (vector v, const char * name)
    {
      struct { char * x, * y, * z; } ext = {".x", ".y", ".z"};
      foreach_dimension() {
        if (name) {
          char cname[strlen(name) + 3];
          strcat (strcpy (cname, name), ext.x);
          cartesian_init_scalar (v.x, cname);
        }
        else
          cartesian_init_scalar (v.x, NULL);
        v.x.v = v;
      }
      /* set default boundary conditions */
      for (int d = 0; d < nboundary; d++)
        v.x.boundary[d] = v.x.boundary_homogeneous[d] =
          d < 2*dimension ? default_vector_bc[d] : antisymmetry;
      return v;
    }
    
    vector cartesian_init_face_vector (vector v, const char * name)
    {
      v = cartesian_init_vector (v, name);
      foreach_dimension() {
        v.x.d.x = -1;
        v.x.face = true;
      }
      for (int d = 0; d < nboundary; d++)
        v.x.boundary[d] = v.x.boundary_homogeneous[d] = NULL;
      return v;
    }
    
    tensor cartesian_init_tensor (tensor t, const char * name)
    {
      struct { char * x, * y, * z; } ext = {".x", ".y", ".z"};
      foreach_dimension() {
        if (name) {
          char cname[strlen(name) + 3];
          strcat (strcpy (cname, name), ext.x);
          cartesian_init_vector (t.x, cname);
        }
        else
          cartesian_init_vector (t.x, NULL);
      }
      /* set default boundary conditions */
      #if dimension == 1
        for (int b = 0; b < nboundary; b++)
          t.x.x.boundary[b] = t.x.x.boundary_homogeneous[b] =
    	b < 2*dimension ? default_scalar_bc[b] : symmetry;
      #elif dimension == 2
        for (int b = 0; b < nboundary; b++) {
          t.x.x.boundary[b] = t.y.x.boundary[b] = 
    	t.x.x.boundary_homogeneous[b] = t.y.y.boundary_homogeneous[b] = 
    	b < 2*dimension ? default_scalar_bc[b] : symmetry;
          t.x.y.boundary[b] = t.y.y.boundary[b] = 
    	t.x.y.boundary_homogeneous[b] = t.y.x.boundary_homogeneous[b] = 
    	b < 2*dimension ? default_vector_bc[b] : antisymmetry;
        }
      #else
        assert (false); // not implemented yet
      #endif
      return t;
    }
    
    void output_cells (FILE * fp = stdout, coord c = {0}, double size = 0.)
    {
      foreach() {
        bool inside = true;
        coord o = {x,y,z};
        foreach_dimension()
          if (inside && size > 0. &&
    	  (o.x > c.x + size || o.x < c.x - size))
    	inside = false;
        if (inside) {
          Delta /= 2.;
    #if dimension == 1
          fprintf (fp, "%g 0\n%g 0\n\n", x - Delta, x + Delta);
    #elif dimension == 2
          fprintf (fp, "%g %g\n%g %g\n%g %g\n%g %g\n%g %g\n\n",
    	       x - Delta, y - Delta,
    	       x - Delta, y + Delta,
    	       x + Delta, y + Delta,
    	       x + Delta, y - Delta,
    	       x - Delta, y - Delta);
    #else // dimension == 3
          for (int i = -1; i <= 1; i += 2) {
    	fprintf (fp, "%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n\n",
    		 x - Delta, y - Delta, z + i*Delta,
    		 x - Delta, y + Delta, z + i*Delta,
    		 x + Delta, y + Delta, z + i*Delta,
    		 x + Delta, y - Delta, z + i*Delta,
    		 x - Delta, y - Delta, z + i*Delta);
    	for (int j = -1; j <= 1; j += 2)
    	  fprintf (fp, "%g %g %g\n%g %g %g\n\n",
    		   x + i*Delta, y + j*Delta, z - Delta,
    		   x + i*Delta, y + j*Delta, z + Delta);
          }
    #endif
        }
      }
      fflush (fp);
    }
    
    #if TREE && _MPI
    static void output_cells_internal (FILE * fp)
    {
      output_cells (fp);
    }
    #endif
    
    static char * replace_ (const char * vname)
    {
      char * name = strdup (vname), * c = name;
      while (*c != '\0') {
        if (*c == '.')
          *c = '_';
        c++;
      }
      return name;
    }
    
    static void debug_plot (FILE * fp, const char * name, const char * cells,
    			const char * stencil)
    {
      char * vname = replace_ (name);
      fprintf (fp, 
    	   "  load 'debug.plot'\n"
    	   "  v=%s\n"
    #if dimension == 1   
    	   "  plot '%s' w l lc 0, "
    	   "'%s' u 1+2*v:(0):2+2*v w labels tc lt 1 title columnhead(2+2*v)",
    #elif dimension == 2
    	   "  plot '%s' w l lc 0, "
    	   "'%s' u 1+3*v:2+3*v:3+3*v w labels tc lt 1 title columnhead(3+3*v)",
    #elif dimension == 3
    	   "  splot '%s' w l lc 0, "
    	   "'%s' u 1+4*v:2+4*v:3+4*v:4+4*v w labels tc lt 1"
               " title columnhead(4+4*v)",
    #endif
    	   vname, cells, stencil);
      free (vname);
    }
    
    void cartesian_debug (Point point)
    {
      char name[80] = "cells";
      if (pid() > 0)
        sprintf (name, "cells-%d", pid());
      FILE * fp = fopen (name, "w");
      output_cells (fp, (coord){x,y,z}, 4.*Delta);
      fclose (fp);
    
      char stencil[80] = "stencil";
      if (pid() > 0)
        sprintf (stencil, "stencil-%d", pid());
      fp = fopen (stencil, "w");
      for (scalar v in all)
    #if dimension == 1
        fprintf (fp, "x %s ", v.name);
    #elif dimension == 2
        fprintf (fp, "x y %s ", v.name);
    #elif dimension == 3
        fprintf (fp, "x y z %s ", v.name);
    #endif
      fputc ('\n', fp);
      #if dimension == 1
        for (int k = -2; k <= 2; k++) {
          for (scalar v in all) {
    	fprintf (fp, "%g ", x + k*Delta + v.d.x*Delta/2.);
    	if (allocated(k))
    	  fprintf (fp, "%g ", v[k]);
    	else
    	  fputs ("n/a ", fp);
          }
          fputc ('\n', fp);
        }
      #elif dimension == 2
        for (int k = -2; k <= 2; k++)
          for (int l = -2; l <= 2; l++) {
    	for (scalar v in all) {
    	  fprintf (fp, "%g %g ",
    		   x + k*Delta + v.d.x*Delta/2., 
    		   y + l*Delta + v.d.y*Delta/2.);
    	  if (allocated(k,l))
    	    fprintf (fp, "%g ", v[k,l]);
    	  else
    	    fputs ("n/a ", fp);
    	}
    	fputc ('\n', fp);
          }
      #elif dimension == 3
        for (int k = -2; k <= 2; k++)
          for (int l = -2; l <= 2; l++)
    	for (int m = -2; m <= 2; m++) {
    	  for (scalar v in all) {
    	    fprintf (fp, "%g %g %g ",
    		     x + k*Delta + v.d.x*Delta/2., 
    		     y + l*Delta + v.d.y*Delta/2.,
    		     z + m*Delta + v.d.z*Delta/2.);
    	    if (allocated(k,l,m))
    	      fprintf (fp, "%g ", v[k,l,m]);
    	    else
    	      fputs ("n/a ", fp);
    	  }
    	  fputc ('\n', fp);
    	}
      #endif
      fclose (fp);
    
      fp = fopen ("debug.plot", "w");
      fprintf (fp, 
    	   "set term x11\n"
    	   "set size ratio -1\n"
    	   "set key outside\n");
      for (scalar s in all) {
        char * name = replace_ (s.name);
        fprintf (fp, "%s = %d\n", name, s.i);
        free (name);
      }
      fclose (fp);
    
      fprintf (stderr, "Last point stencils can be displayed using (in gnuplot)\n");
      debug_plot (stderr, _attribute[0].name, name, stencil);
      fflush (stderr);
    
      fp = fopen ("plot", "w");
      debug_plot (fp, _attribute[0].name, name, stencil);
      fclose (fp);
    }
    
    void cartesian_methods()
    {
      init_scalar        = cartesian_init_scalar;
      init_vertex_scalar = cartesian_init_vertex_scalar;
      init_vector        = cartesian_init_vector;
      init_face_vector   = cartesian_init_face_vector;
      init_tensor        = cartesian_init_tensor;
      boundary_level     = cartesian_boundary_level;
      boundary_face      = cartesian_boundary_face;
      scalar_clone       = cartesian_scalar_clone;
      debug              = cartesian_debug;
    }
    
    tensor init_symmetric_tensor (tensor t, const char * name)
    {
      return init_tensor (t, name);
    }
    
    static double interpolate_linear (Point point, scalar v,
    				  double xp = 0., double yp = 0., double zp = 0.)
    {
    #if dimension == 1
      x = (xp - x)/Delta - v.d.x/2.;
      int i = sign(x);
      x = fabs(x);
      /* linear interpolation */
      return v[]*(1. - x) + v[i]*x;
    #elif dimension == 2
      x = (xp - x)/Delta - v.d.x/2.;
      y = (yp - y)/Delta - v.d.y/2.;
      int i = sign(x), j = sign(y);
      x = fabs(x); y = fabs(y);
      /* bilinear interpolation */
      return ((v[]*(1. - x) + v[i]*x)*(1. - y) + 
    	  (v[0,j]*(1. - x) + v[i,j]*x)*y);
    #else // dimension == 3
      x = (xp - x)/Delta - v.d.x/2.;
      y = (yp - y)/Delta - v.d.y/2.;
      z = (zp - z)/Delta - v.d.z/2.;
      int i = sign(x), j = sign(y), k = sign(z);
      x = fabs(x); y = fabs(y); z = fabs(z);
      /* trilinear interpolation */
      return (((v[]*(1. - x) + v[i]*x)*(1. - y) + 
    	   (v[0,j]*(1. - x) + v[i,j]*x)*y)*(1. - z) +
    	  ((v[0,0,k]*(1. - x) + v[i,0,k]*x)*(1. - y) + 
    	   (v[0,j,k]*(1. - x) + v[i,j,k]*x)*y)*z);
    #endif  
    }
    
    trace
    double interpolate (scalar v, double xp = 0., double yp = 0., double zp = 0.,
    		    bool linear = true)
    {
      double val = nodata;
      foreach_point (xp, yp, zp, reduction (min:val))
        val = linear ? interpolate_linear (point, v, xp, yp, zp) : v[];
      return val;
    }
    
    trace
    void interpolate_array (scalar * list, coord * a, int n, double * v,
    			bool linear = false)
    {
      int len = 0;
      for (scalar s in list)
        len++;
      for (int i = 0; i < n; i++) {
        double * w = v;
    #if _GPU
        coord p = a[i];
        for (scalar s in list) {
          double value = nodata;
          foreach_point (p.x, p.y, p.z, reduction(min:value))
    	value = !linear ? s[] : interpolate_linear (point, s, p.x, p.y, 0.);
          *(w++) = value;
        }
    #else // !_GPU
        for (scalar s in list)
          *(w++) = nodata;
        foreach_point (a[i].x, a[i].y, a[i].z, reduction(min:v[:len])) {
          int j = 0;
          for (scalar s in list)
    	v[j++] = !linear ? s[] : interpolate_linear (point, s, a[i].x, a[i].y, a[i].z);
        }
    #endif // !_GPU
        v = w;
      }
    }
    
    // Boundaries
    
    typedef int bid;
    
    bid new_bid()
    {
      int b = nboundary++;
      for (scalar s in all) {
        s.boundary = (BoundaryFunc *) realloc (s.boundary, nboundary*sizeof (BoundaryFunc));
        s.boundary_homogeneous =  (BoundaryFunc *)
          realloc (s.boundary_homogeneous, nboundary*sizeof (BoundaryFunc));
      }
      for (scalar s in all) {
        if (s.v.x.i < 0) // scalar
          s.boundary[b] = s.boundary_homogeneous[b] = symmetry;
        else if (s.v.x.i == s.i) { // vector
          vector v = s.v;
          foreach_dimension()
    	v.y.boundary[b] = v.y.boundary_homogeneous[b] = symmetry;
          v.x.boundary[b] = v.x.boundary_homogeneous[b] =
    	v.x.face ? NULL : antisymmetry;
        }
      }
      return b;
    }
    
    // Periodic boundary conditions
    
    static double periodic_bc (Point point, Point neighbor, scalar s, bool * data)
    {
      return s[];
    }
    
    static void periodic_boundary (int d)
    {
      /* We change the conditions for existing scalars. */
      for (scalar s in all)
        if (is_vertex_scalar (s))
          s.boundary[d] = s.boundary_homogeneous[d] = NULL;
        else
          s.boundary[d] = s.boundary_homogeneous[d] = periodic_bc;
      /* Normal components of face vector fields should remain NULL. */
      for (scalar s in all)
        if (s.face) {
          vector v = s.v;
          v.x.boundary[d] = v.x.boundary_homogeneous[d] = NULL;
        }
      /* We also change the default boundary conditions (for new fields). */
      default_scalar_bc[d] = periodic_bc;
      default_vector_bc[d] = periodic_bc;
    }
    
    void periodic (int dir)
    {
      #if dimension < 2
        assert (dir <= left);
      #elif dimension < 3
        assert (dir <= bottom);
      #else
        assert (dir <= back);
      #endif
      // This is the component in the given direction i.e. 0 for x and 1 for y
      int c = dir/2;
      periodic_boundary (2*c);
      periodic_boundary (2*c + 1);
      (&Period.x)[c] = true;
    }
    
    // for debugging
    double getvalue (Point point, scalar s, int i, int j, int k)
    {
      return s[i,j,k];
    }
    
    void default_stencil (Point p, scalar * list)
    {
      for (scalar s in list)
        s.input = true, s.width = 2;
    }

    This displays a (1D,2D,3D) stencil index.

    static void write_stencil_index (int * index)
    {
      fprintf (qstderr(), "[%d", index[0]);
      for (int d = 1; d < dimension; d++)
        fprintf (qstderr(), ",%d", index[d]);
      fputs ("]", qstderr());
    }
    
    void stencil_val (Point p, scalar s, int i, int j, int k,
    		  const char * file, int line, bool overflow)
    {
      if (is_constant(s) || s.i < 0)
        return;
      int index[] = {i, j, k};
      for (int d = 0; d < dimension; d++)
        index[d] += (&p.i)[d];      
      bool central = true;
      for (int d = 0; d < dimension; d++) {
        if (!overflow && (index[d] > 2 || index[d] < - 2)) {
          fprintf (qstderr(), "%s:%d: error: stencil overflow: %s",
    	       file, line, s.name);
          write_stencil_index (index);
          fprintf (qstderr(), "\n");
          fflush (qstderr());
          abort();
        }
        if (index[d] != 0)
          central = false;
      }
      if (central) {
        if (!s.output)
          s.input = true;
      }
      else {
        s.input = true;
        int d = 0;
        foreach_dimension() {
          if ((!s.face || s.v.x.i != s.i) && abs(index[d]) > s.width)
    	s.width = abs(index[d]);
          d++;
        }
      }
    }
    
    void stencil_val_a (Point p, scalar s, int i, int j, int k, bool input,
    		    const char * file, int line)
    {
      if (is_constant(s) || s.i < 0)
        abort();
      int index[] = {i, j, k};
      for (int d = 0; d < dimension; d++)
        index[d] += (&p.i)[d];    
      for (int d = 0; d < dimension; d++)
        if (index[d] != 0) {
          fprintf (qstderr(), "%s:%d: error: illegal write: %s",
    	       file, line, s.name);
          write_stencil_index (index);
          fprintf (qstderr(), "\n");
          fflush (qstderr());
          abort();
        }
      if (input && !s.output)
        s.input = true;
      s.output = true;
    }

    Macros overloaded by the interpreter.

    @define dimensional(...)
    #define show_dimension(...) show_dimension_internal (__VA_ARGS__ + 10293847566574839201.)
    @define show_dimension_internal(...)
    @define display_value(...)
    @define interpreter_verbosity(...)