sandbox/huet/src/eulerian_caps/normal_extension.h

    The function below assumes accurate normal vectors in a neighbourhood of the interface and extends the scalar a defined in interfacial cells only to the whole neighbourhood. Note that in the neighbourhood of the interface, initially the scalar a needs to be initialized with a guess.

    We use a Hamilton-Jacobi equation which characteristics are the normal vectors: \displaystyle \frac{\partial q}{\partial \tau} + S(\bm{x}) \bm{n} \cdot \nabla q = 0 with \tau a pseudo-time.

    #include "curvature.h"
    
    #ifndef MAX_ITER_EXTENSION
      #define MAX_ITER_EXTENSION (10)
    #endif
    #ifndef MAX_ITER_NV_EXTENSION
      #define MAX_ITER_NV_EXTENSION (3*MAX_ITER_EXTENSION)
    #endif
    #ifndef RENORMALIZE_INTERFACIAL_NORMALS
      #define RENORMALIZE_INTERFACIAL_NORMALS 0
    #endif
    
    struct Extension {
      // Compulsory argument
      scalar* q;
      // Optional arguments
      scalar* q_prev; // FIXME: some tests have resulted in a
      // segfault when len(q_prev)<len(q)... As such, q_prev is not
      // really optional until this is fixed.
      int max_iter; // default is MAX_ITER_EXTENSION
    };
    
    trace
    void normal_scalar_extension(struct Extension p) {
      scalar* q = p.q;
      int max_iter = (p.max_iter) ? p.max_iter : MAX_ITER_EXTENSION;
      double dtau = .25*L0/((1 << grid->maxdepth));
      int lenq = list_len(q);
      scalar* q_prev = NULL;
      if (p.q_prev && list_len(p.q_prev)>=lenq) q_prev = p.q_prev;
      else {
        for (scalar s in q) {
          scalar s_prev = new scalar;
          q_prev = list_add(q_prev, s_prev);
        }
      }
      int k = 0; // k counts the number of pseudo-time iterations
      boundary(q);
      while (k < max_iter) {
        foreach() {
          if (GAMMA) {
            for (int i=0; i<lenq; i++) {
              val(q_prev[i],0,0,0) = val(q[i],0,0,0);
            }
          }
        }
        boundary(q_prev);
        foreach() {
          if (GAMMA && !(IS_INTERFACE_CELL(point, f))) {
            for (int i=0; i<lenq; i++) {

    Unfortunately, the syntax “q[i][]” does not compile, so as far as I know the only workaround is to use the function val() and forget about using foreach_dimension(). In some rare configuration, the upwind scheme still implies to access q_prev outside gamma. We choose to not consider these degenerated configurations at all, hence the precense of ngcaps>GRAD_THRESHOLD, testing the location of the cell.

              val(q[i],0,0,0) -= dtau*(max(-sign(f[])*extended_n.x[],\
              0.)*(val(q_prev[i],0,0,0) - val(q_prev[i],-1,0,0)*(val(ngcaps,-1,0,0)\
              > GRAD_THRESHOLD)) + min(-sign(f[])*extended_n.x[],0.)\
              *(val(q_prev[i],1,0,0)*(val(ngcaps,1,0,0) > GRAD_THRESHOLD)\
              - val(q_prev[i],0,0,0)) + max(-sign(f[])*extended_n.y[],\
              0.)*(val(q_prev[i],0,0,0) - val(q_prev[i],0,-1,0)*\
              (val(ngcaps,0,-1,0) > GRAD_THRESHOLD)) + \
              min(-sign(f[])*extended_n.y[],0.)*(val(q_prev[i],0,1,0)*\
              (val(ngcaps,0,1,0) > GRAD_THRESHOLD) - val(q_prev[i],0,0,0))
              #if dimension==2
                )/Delta;
              #else
                + max(-sign(f[])*extended_n.z[], 0.)*(val(q_prev[i],0,0,0) -\
                val(q_prev[i],0,0,-1)*(val(ngcaps,0,0,-1) > GRAD_THRESHOLD)) + \
                min(-sign(f[])*extended_n.z[],0.)*(val(q_prev[i],0,0,1)*\
                (val(ngcaps,0,0,1) > GRAD_THRESHOLD) - val(q_prev[i],0,0,0)))/Delta;
              #endif
            }
          }
        }
        boundary(q);
        k++;
      }
      if (!(p.q_prev) || list_len(p.q_prev)<lenq) delete(q_prev);
    }
    
    trace
    void initialize_normals_for_extension(vector normals) {
      coord n_coord;
      // Get accurate normals *on* the interface from the height functions
      // FIXME: we would like to have normal vectors at the center of the cells
      // instead. The current implementation for normal vectors is therefore
      // order 1 in space.
    
      vector fh = f.height, h = automatic (fh);
      if (!fh.x.i)
        heights (f, h);
    
      foreach() {
        if (GAMMA) {
          if (IS_INTERFACE_CELL(point, f)) {
            n_coord = height_normal(point, f, h);
            foreach_dimension() {
              normals.x[] = n_coord.x;
            }
            #if RENORMALIZE_INTERFACIAL_NORMALS
              double normn = sqrt(sq(normals.x[]) + sq(normals.y[]) + sq(normals.z[]));
              if (normn > 1.e-30) {
                foreach_dimension() {
                  normals.x[] /= normn;
                }
              }
              else foreach_dimension() normals.x[] = 0.;
            #endif
          }
          else {
            foreach_dimension() {
              normals.x[] = - grad_caps.x[]/ngcaps[];
            }
          }
        }
        else {
          foreach_dimension() {
            normals.x[] = 0.;
          }
        }
      }
    }
    
    void upwind_extension(Point point, scalar my_field, vector my_normal, double dtau) {
      double a = 0.;
      foreach_dimension() {

    In some rare configurations, the upwind scheme needs to access q_prev outside gamma. We choose to not consider these degenerated configurations at all, hence the precense of NBG_GAMMA(,,*), testing the location of the cell.

        a += dtau*(max(-sign(f[])*my_normal.x[], 0.)*(my_field[] -
            my_field[-1]*NBG_GAMMA(-1,0,0)) + min(-sign(f[])*my_normal.x[], 0.)*
            (my_field[1]*NBG_GAMMA(1,0,0) - my_field[]))/Delta;
      }
      my_field[] -= a;
    }
    
    trace
    void normal_vector_extension(vector qv) {
      double dtau = .25*L0/((1 << grid->maxdepth));
      vector qv_prev[];
      int k = 0;
      while (k < MAX_ITER_NV_EXTENSION) {
        foreach() {
          if (GAMMA) foreach_dimension() qv_prev.x[] = qv.x[];
        }
        boundary((scalar *){qv_prev, qv});
    
        foreach() {
          if (GAMMA && !(IS_INTERFACE_CELL(point, f))) {
            foreach_dimension() {
              upwind_extension(point, qv.x, qv_prev, dtau);
            }
          }
        }
        #if RENORMALIZE_EXTENDED_NORMALS
        foreach() {
          if (GAMMA && !(IS_INTERFACE_CELL(point, f))) {
            double mnorm = sqrt(sq(qv.x[]) + sq(qv.y[]) + sq(qv.z[]));
            if (mnorm > 1.e-30) foreach_dimension() qv.x[] /= mnorm;
            else foreach_dimension() qv.x[] = 0.;
          }
        }
        #endif
        boundary((scalar *){qv});
        k++;
      }
    }
    
    coord youngs_normals(Point point, scalar c) {
      double m1, m2, mnorm;
      coord mxyz;
      m1 = c[-1,-1,-1] + c[-1,1,-1] + c[-1,-1,1] + c[-1,1,1] +
           2.*(c[-1,-1,0] + c[-1,1,0] + c[-1,0,-1] + c[-1,0,1]) +
           4.*c[-1,0,0];
      m2 = c[1,-1,-1] + c[1,1,-1] + c[1,-1,1] + c[1,1,1] +
           2.*(c[1,-1,0] + c[1,1,0] + c[1,0,-1] + c[1,0,1]) +
           4.*c[1,0,0];
      mxyz.x = m1 - m2;
    
      m1 = c[-1,-1,-1] + c[-1,-1,1] + c[1,-1,-1] + c[1,-1,1] +
           2.*( c[-1,-1,0] + c[1,-1,0] + c[0,-1,-1] + c[0,-1,1]) +
           4.*c[0,-1,0];
      m2 = c[-1,1,-1] + c[-1,1,1] + c[1,1,-1] + c[1,1,1] +
           2.*(c[-1,1,0] + c[1,1,0] + c[0,1,-1] + c[0,1,1]) +
           4.*c[0,1,0];
      mxyz.y = m1 - m2;
    
      m1 = c[-1,-1,-1] + c[-1,1,-1] + c[1,-1,-1] + c[1,1,-1] +
           2.*(c[-1,0,-1] + c[1,0,-1] + c[0,-1,-1] + c[0,1,-1]) +
           4.*c[0,0,-1];
      m2 = c[-1,-1,1] + c[-1,1,1] + c[1,-1,1] + c[1,1,1] +
           2.*(c[-1,0,1] + c[1,0,1] + c[0,-1,1] + c[0,1,1]) +
           4.*c[0,0,1];
      mxyz.z = m1 - m2;
    
      mnorm = fabs(mxyz.x) + fabs(mxyz.y) + fabs(mxyz.z);
      if (mnorm < 1e-30) {
        mxyz.x = 1.;
        mxyz.y = 0.;
        mxyz.z = 0.;
        return mxyz;
      }
    
      mxyz.x /= mnorm;
      mxyz.y /= mnorm;
      mxyz.z /= mnorm;
      return mxyz;
    }