Menu

src-local/activity.h

Advection/diffusion of temperature tracers

The stracers list of temperature tracers must be defined by the calling code. copied and inspired from http://basilisk.fr/src/henry.h & http://basilisk.fr/sandbox/farsoiya/phase_change/phase-change.h

attribute {
  scalar phi1, phi2; // private
  double A;  // activity at the interface
  double D; // Diffusivity of tracer in the diffusive phase
}

extern scalar * stracers;
scalar ActivityFlux[];

#include "diffusion.h"

Defaults

On trees we need to ensure conservation of the tracer when refining/coarsening.

event defaults (i = 0)
{
  for (scalar s in stracers) {
#if TREE
#if EMBED
s.refine = refine_embed_linear;
set_prolongation (s, refine_embed_linear);
#else
s.refine  = refine_bilinear;
#endif
s.restriction = restriction_volume_average;
s.gradient = p.gradient;
#endif // TREE
  }
}

Advection

To avoid numerical diffusion through the interface we use the VOF tracer transport scheme for the temporary fields \(\phi_1\) and \(\phi_2\), see section 3.2 of Farsoiya et al., 2021.

static scalar * phi_tracers = NULL;

event vof (i++)
{
  phi_tracers = f.tracers;
  for (scalar c in stracers) {
    scalar phi1 = new scalar, phi2 = new scalar;
    c.phi1 = phi1, c.phi2 = phi2;
    scalar_clone (phi1, c);
    scalar_clone (phi2, c);
    // Diffusion-side selection on c must not reverse the c*f VOF tracer.
    phi1.inverse = false;
    phi2.inverse = true;

    f.tracers = list_append (f.tracers, phi1);
    f.tracers = list_append (f.tracers, phi2);

\(\phi_1\) and \(\phi_2\) are computed from \(c\) as \[ \phi_1 = c f \] \[ \phi_2 = c (1-f) \]

    foreach() {
      double a = c[];
#if EMBED
      if (cs[] <= 0.)
        a = 0.;
#endif
      phi1[] = a*f[];
      phi2[] = a*(1. - f[]);
    }
  }
}

event tracer_diffusion (i++)
{
  free (f.tracers);
  f.tracers = phi_tracers;
  for (scalar c in stracers) {

The advected concentration is computed from \(\phi_1\) and \(\phi_2\) as \[ c = \phi_1 + \phi_2 \] and these fields are then discarded.

    scalar phi1 = c.phi1, phi2 = c.phi2;
    foreach() {
      c[] = phi1[] + phi2[];
#if EMBED
      if (cs[] <= 0.)
        c[] = 0.;
#endif
    }
    delete ({phi1, phi2});

    scalar volumic_metric[], dirichlet_source_term[];
    face vector diffusion_coef[];

    scalar diracDelta[];
    // foreach(){
    //   if (interfacial(point, f)){
    //     coord n = interface_normal (point, c), p;
    //     double alpha = plane_alpha (c[], n);
    //     double area = pow(Delta, dimension - 1)*plane_area_center (n, alpha, &p);
    //     diracDelta[] = area/sq(Delta); //sqrt(sq(fx) + sq(fy));
    //     ActivityFlux[] = diracDelta[]*c.A;
    //   }
    // }

    foreach(){
      // if (interfacial (point, f))
      double fx = (f[1]-f[-1])/(2*Delta);
      double fy = (f[0,1]-f[0,-1])/(2*Delta);
      diracDelta[] = sqrt(sq(fx) + sq(fy));
      ActivityFlux[] = diracDelta[]*c.A;
#if EMBED
      if (cs[] <= 0.)
        ActivityFlux[] = 0.;
#endif
    }

    foreach() {
      volumic_metric[] = cm[];
      dirichlet_source_term[] = cm[]*ActivityFlux[];
    }
    foreach_face(){
      double ff = (f[] + f[-1])/2.;
      double wt = c.inverse ? (1.-ff) : ff;
      diffusion_coef.x[] = fm.x[]*c.D*wt;
    }
    diffusion (c, dt, D = diffusion_coef, r = dirichlet_source_term, theta = volumic_metric);
    }
}