sandbox/huet/src/lagrangian_caps/viscosity-ft.h

    Variable viscosity across a membrane

    #include "fractions.h"
    
    #ifndef MUP
      #define MUP 1.;
    #endif
    #ifndef MUC
      #define MUC 1.;
    #endif
    #define CAPS_VISCOSITY 1.

    We define the “grid gradient” \bm{G}, according to Tryggvason, JCP 2001.

    vector G[];
    void construct_divG(scalar divG, lagMesh* mesh) {
      comp_normals(mesh);
      #if dimension < 3
      compute_lengths(mesh);
      for(int i=0; i<mesh->nle; i++) {
        // compute the grid gradient on the midpoint of the edge
        coord gg; // grid gradient
        int en[2];
        en[0] = mesh->edges[i].node_ids[0];
        en[1] = mesh->edges[i].node_ids[1];
        foreach_dimension() gg.x = mesh->edges[i].normal.x*mesh->edges[i].length;
        for(int j=0; j<2; j++) {
          foreach_cache(mesh->nodes[en[j]].stencil) {
            //spread half the grid gradient of the edge from each of its nodes
            if (point.level >= 0) {
            coord dist;
              dist.x = GENERAL_1DIST(x, mesh->nodes[en[j]].pos.x);
              dist.y = GENERAL_1DIST(y, mesh->nodes[en[j]].pos.y);
              if (sq(dist.x) <= sq(2*Delta) && sq(dist.y) <= sq(2*Delta)) {
                double weight =
                  (1 + cos(.5*pi*dist.x/Delta))*(1 + cos(.5*pi*dist.y/Delta))/
                  (16.*sq(Delta));
                foreach_dimension() G.x[] -= weight*.5*gg.x;
              }
            }
          }
        }
      }
      #else
      for(int i=0; i<mesh->nlt; i++) {

    compute the grid gradient on the midpoint of the edge

        coord gg; // gg for "grid gradient"
        int tn[3]; // tn for "triangle's nodes"
        tn[0] = mesh->triangles[i].node_ids[0];
        tn[1] = mesh->triangles[i].node_ids[1];
        tn[2] = mesh->triangles[i].node_ids[2];
        foreach_dimension()
          gg.x = mesh->triangles[i].normal.x*mesh->triangles[i].area;
        for(int j=0; j<3; j++) {
          foreach_cache(mesh->nodes[tn[j]].stencil) {

    Spread one third of the grid gradient of the triangle to each of its vertices

            if (point.level >= 0) {
            coord dist;
              dist.x = GENERAL_1DIST(x, mesh->nodes[tn[j]].pos.x);
              dist.y = GENERAL_1DIST(y, mesh->nodes[tn[j]].pos.y);
              dist.z = GENERAL_1DIST(z, mesh->nodes[tn[j]].pos.z);
              if (sq(dist.x) <= sq(2*Delta) && sq(dist.y) <= sq(2*Delta)
                && sq(dist.z) <= sq(2*Delta)) {
                double weight = (1 + cos(.5*pi*dist.x/Delta))
                  *(1 + cos(.5*pi*dist.y/Delta))*(1 + cos(.5*pi*dist.z/Delta))
                  /(cube(4*Delta));
                foreach_dimension() G.x[] -= weight*gg.x/3.;
              }
            }
          }
        }
      }
      #endif
      foreach()
        if (cm[] > 1.e-20)
          foreach_dimension() divG[] += (G.x[1] - G.x[-1])/(2.*Delta);
    }
    
    double muc, mup;
    scalar I[];
    scalar prevI[];
    scalar divG[];
    event defaults (i = 0) {
      mu = new face vector;
      mup = MUP;
      muc = MUC;
      foreach() {
        prevI[] = 0.;
        I[] = 0.;
        foreach_dimension() divG[] = 0.;
      }

    We define below the homogeneous Dirichlet boundary conditions for the grid-gradient, and the indicator function on all walls. A consequence of this is that in the case of bi/tri-periodic boundary conditions in 2D/3D the Poisson solver has no Dirichlet BC to ensure the indicator function lies between 0 and 1. As a result, tri-periodic boxes (or bi-periodic squares) are not recommended with the current implementation.

      foreach_dimension() {
        if (u.x.boundary[left] != periodic_bc) {
          I[left] = dirichlet(0.);
          I[right] = dirichlet(0.);
          prevI[left] = dirichlet(0.);
          prevI[right] = dirichlet(0.);
          divG[left] = dirichlet(0.);
          divG[right] = dirichlet(0.);
        }
      }
      #if EMBED
        I[embed] = dirichlet(0.);
        I.third = true;
      #endif
    }
    
    event properties (i++) {
      foreach() {
        if (cm[] > 1.e-20) {
          foreach_dimension() G.x[] = 0.;
          divG[] = 0.;
        }
      }
    
      for (int k=0; k<NCAPS; k++)
        if (CAPS(k).isactive) 
          construct_divG(divG, &CAPS(k));
      poisson(I, divG, tolerance = 1.e-6, minlevel = 4);
    
      // Simple clamping of I:
      foreach() {
        if (cm[] > 1.e-20) {
          if (fabs(divG[]) > 1.e-10) {
            I[] = clamp(I[], 0, 1);
            prevI[] = I[];
          }
          else {
            prevI[] = round(prevI[]);
            I[] = prevI[];
          }
          I[] = clamp(I[], 0, 1);
        }
      }
    }
    
    event properties (i++) {
      face vector muv = mu;
      foreach_face()
        if (fm.x[] > 1.e-20)
          muv.x[] = (mup + (muc - mup)*.5*(I[] + I[-1]))*fm.x[];
    }