Implementation of Allen-Cahn coupled with temperature for phase change problems

Objective of this 2nd tutorial

The starting point is the kernel ADE_for_AC-Temp_Tutorial which simulates (again) the Advection-Diffusion Equation (ADE) with the lattice Boltzmann method:

()\[\frac{\partial c}{\partial t}+\boldsymbol{\nabla}\cdot(\boldsymbol{u}c)=D\boldsymbol{\nabla}^{2}c\]

Mathematical model

In this tutorial we will implement a model to simulate a phase change problem composed of two coupled PDEs, a first one for the evolution of dimensionless temperature:

()\[\frac{\partial\theta}{\partial t}=D\boldsymbol{\nabla}^{2}\theta-\frac{\partial\phi}{\partial t}\]

where \(\theta\) is a dimensionless temperature, \(D\) is the diffusivity parameter and \(\phi\) the phase-field. The second equation is for the evolution of phase-field:

()\[\frac{\partial\phi}{\partial t}=M_{\phi}\boldsymbol{\nabla}^{2}\phi-\frac{16M_{\phi}}{W^{2}}\phi(1-\phi)(1-2\phi)-\frac{4D}{\mathscr{A}W^{2}}(\theta_{I}-\theta)\phi(1-\phi)\]

where \(M_{\phi}\) is the mobility parameter, \(W\) is the interface thickness, \(\mathscr{A}=10/48\) is known parameter and \(\theta_I\) the temperature at interface.

Objective

In this tutorial we will see how to

  • add a new equation and all related stages (setup_collider, update, boundary conditions, etc.)

  • add source terms of Eqs. () and ().

The verification will be carried out with one analytical solution of the Stefan problem (see section 4). We will

  • compare the outputs of LBM_Saclay with an analytical solution

  • see the impact of linear and harmonic interpolation of diffusivity \(D(\phi)\)

1. Create a new kernel for Allen-Cahn/Temperature

Make the new kernel ACT in a new folder Allen-Cahn-Temp_Tutorial

That stage 1. is the same than the first tutorial on Cahn-Hilliard.

  • Copy the folder ADE_for_AC-Temp_Tutorial and rename it Allen-Cahn-Temp_Tutorial

  • Modify _ADE (for Advection-Diffusion Equation) by _ACT (for Allen-Cahn/Temperature).

Details of commands

Copy the ADE kernel template and rename all files with new extension _ACT

  • Copy and rename the folder ADE_for_AC-Temp_Tutorial which is contained in kernels/Templates_for_Tutorials.

Commands
$ cd LBM_Saclay_Rech-Dev/src/kernels
$ cp -r Templates_for_Tutorials/ADE_for_CH-Tutorial ./Allen-Cahn-Temp_Tutorial

Remarks

  • Your new developments will be performed in the folder Cahn-Hilliard_Tutorial

  • The folder name ADE_for_AC-Temp_Tutorial will be used for compilation

  • All files have the extension _ADE for Advection Diffusion Equation. Rename them with _ACT for Allen-Cahn Temperature.

Commands
$ cd Cahn-Hilliard_Tutorial

$ mv Index_ADE.h Index_ACT.h
$ mv LBMScheme_ADE.h LBMScheme_ACT.h
$ mv Models_ADE.h Models_ACT.h
$ mv Problem_ADE.h Problem_ACT.h

Edit them and change strings _ADE with _ACT

  1. Change strings _ADE with _ACT

    • Open all files in your favorite editor (e.g. geany or codium).

    • Search in all files the strings _ADE and replace them with _ACT (Ctrl H with geany).

  2. Change the problem name: in file Setup.h modify the problem name ADE with ACT

    register_problem<Problem, ModelParams::Tag2Quadra>("ADE", "base");
    
    Modification with ACT
    register_problem<Problem, ModelParams::Tag2Quadra>("ACT", "base");
    

    Remark

    Your new problem will be named ACT inside your .ini input file i.e. in section [lbm] your problem must be:

    [lbm]
    problem=ACT
    

Compile your new kernel

  • Generate your makefile (only once)

Commands
$ cd LBM_Saclay_Rech-Dev
$ mkdir build_ACT
$ cd build_ACT
$ cmake -DKokkos_ENABLE_OPENMP=ON -DPROBLEM=Allen-Cahn-Temp_Tutorial ..
  • Compile

Command
$ make -j 22

Run with input files of ADE

  • Execute your new kernel ACT of LBM_Saclay either with the simple diffusion of gaussian or with the advected gaussian in the folder run_training_lbm/Tutorial_Stefan

Commands

Go to the directory

$ cd LBM_Saclay_Rech-Dev/run_training_lbm/Tutorial_Stefan

Run simple diffusion of gaussian

$ ../../build_CH/src/LBM_saclay TestCase_Gaussian_ADE.ini

Run advected gaussian

$ ../../build_CH/src/LBM_saclay TestCase_Moving-Gaussian_ADE.ini
  • Check the solutions with paraview.

2. Add a new equation in your kernel

The mathematical model is composed of two coupled PDE. It is necessary to add one supplementary equation.

Problem

The number of equation is currently 1

Base::nbEqs = 1;

const int isize = params.isize;
const int jsize = params.jsize;
const int ksize = params.ksize;
  • Modify it for two equations

Solution
Base::nbEqs = 2;

Function init_f()

The initialization is currently performed only for one equation:

if (params.collisionType1 == BGK) {
     init1eq<EquationTag1, BGKCollider<dim, npop>>();
} else if (params.collisionType1 == MRT) {
     init1eq<EquationTag1, MRTCollider<dim, npop>>();
}
  • Add a new block if - else if for EquationTag2

Solution
if (params.collisionType1 == BGK) {
  init1eq<EquationTag1, BGKCollider<dim, npop>>();
} else if (params.collisionType1 == MRT) {
  init1eq<EquationTag1, MRTCollider<dim, npop>>();
}

if (params.collisionType1 == BGK) {
  init1eq<EquationTag2, BGKCollider<dim, npop>>();
} else if (params.collisionType1 == MRT) {
  init1eq<EquationTag2, MRTCollider<dim, npop>>();
}

Function update_f()

The update is also performed only for one equation:

if (params.collisionType1 == BGK) {
  update1eq<EquationTag1, BGKCollider<dim, npop>>();
} else if (params.collisionType1 == MRT) {
  update1eq<EquationTag1, MRTCollider<dim, npop>>();
}
  • Add a new block if - else if for EquationTag2

Solution
if (params.collisionType1 == BGK) {
  update1eq<EquationTag1, BGKCollider<dim, npop>>();
} else if (params.collisionType1 == MRT) {
  update1eq<EquationTag1, MRTCollider<dim, npop>>();
}

if (params.collisionType1 == BGK) {
  update1eq<EquationTag2, BGKCollider<dim, npop>>();
} else if (params.collisionType1 == MRT) {
  update1eq<EquationTag2, MRTCollider<dim, npop>>();
}

Function make_boundaries()

The update of bounday conditions is also performed for a single equation

this->bcPerMgr.make_boundaries(scheme.f1, BOUNDARY_EQUATION_1);
  • Add a new line for f2 and BOUNDARY_EQUATION_2

Solution
this->bcPerMgr.make_boundaries(scheme.f1, BOUNDARY_EQUATION_1);
this->bcPerMgr.make_boundaries(scheme.f2, BOUNDARY_EQUATION_2);

Function struct LBMScheme

  • Add a new tag tagPHI for EquationTag2

Solution
EquationTag1 tagC;
EquationTag2 tagPHI;
  • Add two empty functions called setup_collider for EquationTag2 with respectively BGK and MRT collisions

Solution
KOKKOS_INLINE_FUNCTION
void setup_collider(EquationTag2 tag, const IVect<dim>& IJK, BGK_Collider& collider) const
{
}

KOKKOS_INLINE_FUNCTION
void setup_collider(EquationTag2 tag, const IVect<dim>& IJK, MRT_Collider& collider) const
{

Remark

Those two functions are empty (but declared). The first one will be filled below.

Function make_boundary

The boundary conditions are applied only for Advection-diffusion equation

// ADE boundary
if (Base::params.boundary_types[BOUNDARY_EQUATION_1][faceId] == BC_ANTI_BOUNCE_BACK) {
  real_t boundary_value = Base::params.boundary_values[BOUNDARY_CONCENTRATION][faceId];

  for (int ipop = 0; ipop < npop; ++ipop)
     this->compute_boundary_antibounceback(tagC, faceId, IJK, ipop, boundary_value);
}
else if (Base::params.boundary_types[BOUNDARY_EQUATION_1][faceId] == BC_ZERO_FLUX) {
  for (int ipop = 0; ipop < npop; ++ipop)
     this->compute_boundary_bounceback(tagC, faceId, IJK, ipop, 0.0);
}
  • Add a new block if - else if for BOUNDARY_EQUATION_2 and tagPHI with keywork BOUNDARY_PHASE_FIELD

Solution
if (Base::params.boundary_types[BOUNDARY_EQUATION_2][faceId] == BC_ANTI_BOUNCE_BACK) {
   real_t boundary_value = Base::params.boundary_values[BOUNDARY_PHASE_FIELD][faceId];
   for (int ipop = 0; ipop < npop; ++ipop)
   this->compute_boundary_antibounceback(tagPHI, faceId, IJK, ipop, boundary_value);
}
else if (Base::params.boundary_types[BOUNDARY_EQUATION_2][faceId] == BC_ZERO_FLUX) {
   for (int ipop = 0; ipop < npop; ++ipop)
   this->compute_boundary_bounceback(tagPHI, faceId, IJK, ipop, 0.0);
}

3. Modifications inside each file for Allen-Cahn/Temperature model

Advice

During your implementation, it is strongly recommended to compile regularly to detect any typos. See Advice

Declaration of new fields

In enum ComponentIndex, only the indices for macroscopic fields such as concentration \(c\) (IC) and velocity components \(u_x\) (IU), \(u_y\) (IV) & \(u_z\) (IW) are declared:

enum ComponentIndex {
  IU     , /*!< X velocity / momentum index */
  IV     , /*!< Y velocity / momentum index */
  IW     , /*!< Z velocity / momentum index */
  IC     , /*!< Phase field index */
  COMPONENT_SIZE /*!< invalid index, just counting number of fields */
};
  • Replace IC by ITEMP and add the new indices for chemical potential IPHI and laplacian IDPHIDT.

Solution

Add new outputs

In struct index2names only the strings for concentration and velocity components are written:

map[IC ] = "comp";
map[IU ] = "vx";
map[IV ] = "vy";
map[IW ] = "vz";
  • Modify for temperature and add new outputs for \(\phi\) and \(\partial \phi/\partial t\).

Solution

Add specific functions for temperature equation

  • Add a function Source_TEMP for the source term of temperature equation

Solution Source_TEMP
  • Modify the name of collision rate function for temperature equation

Solution tau_TEMP

Add specific functions for phase-field equation

  • Add a new function M0_PHI

Solution function M0_PHI
  • Add a new function M2_PHI

Solution function M2_PHI
  • Add a source term.

Solution Source_PHI
  • Add a new function tau_PHI for collision rate using the mobility \(M_\phi\) of Allen-Cahn equation

Solution tau_PHI
  • Add a new hyperbolic tangent function phi0 for initial condition

Solution phi0

Read and declare all parameters in ModelParams

The Allen-Cahn equation involves three paramters: the interface thickness \(W\) and the mobility \(M_\phi\) and the interface temperature \(\theta_I\).

  • Read and declare Allen-Cahn parameters in ModelParams

Solution

Add condition for initial condition

Currently only one condition exits for intial condition:

if (initTypeStr == "gaussian") initType = ADE_GAUSSIAN;
  • Add the necessary condition for one initialization

Solution

Function setup_collider with BGK for temperature equation

The function setup_collider is written for solving Advection-Diffusion Equation.

const real_t M0     = Model.M0_TEMP(tagC, lbmState);
const RVect<dim> uc = Model.M1_TEMP<dim>(tagC, lbmState);
const real_t M2     = Model.M2_TEMP(tagC, lbmState);
// ipop = 0
collider.f[0]   = Base::get_f_val(tag, IJK, 0);
collider.S0[0]  = 0.0 ;
collider.feq[0] = w[0]*M0;
// ipop > 0
for (int ipop = 1; ipop < npop; ++ipop) {
  collider.f[ipop]   = this->get_f_val(tag, IJK, ipop);
  collider.S0[ipop]  = 0.0 ;
  collider.feq[ipop] = w[ipop] * (M2 + c_cs2 * Base::compute_scal(ipop, uc));
}
  • Modify it for the source term.

Solution

Function setup_collider with BGK for phase-field equation

The function setup_collider is empty.

void setup_collider(EquationTag2 tag, const IVect<dim>& IJK, BGK_Collider& collider) const
{
}
  • Inspired with the temperature equation, write your setup_collider.

Solution

Function init_macro

After the initialization for ADE_GAUSSIAN:

if (Model.initType == ADE_GAUSSIAN) {
  c  = Model.ampl * exp( - ( SQR(x-Model.x0) / (2 * SQR(Model.sigmax)) + SQR(y-Model.y0) / (2 * SQR(Model.sigmay)) )) ;
  vx = Model.initVX;
  vy = Model.initVY;
}
  • Write an initial condition corresponding to keywords PHASE_FIELD_INIT_VERTICAL

Solution for vertical separation
  • Save in appropriate arrays

Solution

Function update_macro

Currently, only the concentration and the velocity are updated:

const real_t c  = moment_c;
const real_t vx = Model.initVX;
const real_t vy = Model.initVY;

this->set_lbm_val(IJK, IC , c );
this->set_lbm_val(IJK, IU , vx);
this->set_lbm_val(IJK, IV , vy);
  • Add for phi

Solution
  • Add for dphidt

Solution
LBMState lbmStatePrev;
Base::setupLBMState(IJK, lbmStatePrev);
// get time step
const real_t dt = this->params.dt;
const real_t dphidt = (phi - lbmStatePrev[IPHI])/dt;
  • Save in arrays

Solution

Function update_macro_grad

For that phase change model there is no need to compute additional gradient or laplacian.

Add the keywords of initial conditions

In file InitConditionsTypes.h, two keywords are currently declared:

enum InitCondition{
       PHASE_FIELD_INIT_UNDEFINED,
       ADE_GAUSSIAN
};

Simply add after ADE_GAUSSIAN, the new keyword for Allen-Cahn: PHASE_FIELD_INIT_VERTICAL.

Solution

4. Verifications of your implementation

The LBM implementation is compared with one analytical solution of the Stefan problem. The domain is supposed to be semi-infinite \(]0,L]\) (with \(L\) big) with Dirichlet boundary conditions \(\theta(0,t)=\theta_w\) and \(\theta(L,t)=\theta_{\infty}\). The initial condition is \(\theta(x,0)=\theta_{\infty}\).

Analytical solution

The analytical solution of this problem gives the interface position \(x_i(t)\), the liquid (or solid) temperature \(\theta_{l}(x,t)\) and the gas (or liquid) temperature \(\theta_{g}(x,t)\). Those solutions depend on a parameter \(\xi\) which is given by the transcendental equation.

Interface position

\[x_{i}(t)=2\xi\sqrt{\alpha_{l}t}\]

Liquid temperature

\[\theta_{l}(x,t)=\theta_{w}+(\theta_{I}-\theta_{w})\frac{\mbox{erf}(x/2\sqrt{\alpha_{l}t})}{\mbox{erf}(\xi)}\]

Gas temperature

\[\theta_{g}(x,t)=\theta_{\infty}+(\theta_{I}-\theta_{\infty})\frac{\mbox{erfc}(x/2\sqrt{\alpha_{g}t})}{\mbox{erfc}(\xi\sqrt{\alpha_{l}/\alpha_{g}})}\]

Transcendental equation

\[\frac{e^{-\xi^{2}}}{\mbox{erf}(\xi)}+\left(\frac{\alpha_{g}}{\alpha_{l}}\right)^{1/2}\frac{\theta_{I}-\theta_{\infty}}{\theta_{I}-\theta_{w}}\frac{e^{-\xi^{2}(\alpha_{l}/\alpha_{g})}}{\mbox{erfc}(\xi\sqrt{\alpha_{l}/\alpha_{g}})}=-\frac{\xi\sqrt{\pi}}{\theta_{w}}\]

Input parameters

  • \(\alpha_l\), \(\alpha_g\): respectively diffusity of liquid and Gas

  • \(\theta_w\): temperature at left boundary

  • \(\theta_{\infty}\): temperature at right boundary and initial condition

  • \(\theta_I\): temperature at interface

Fortran code and analytical profile

The analytical solution is implemented in a Fortran code. For validating our LBM implementation, the profiles of temperature for liquid (or gas) and solid (or liquid) with the position \(x\) are given in two output files T_Liq_Chgt_Phase200000.dat and T_Sol_Chgt_Phase200000.dat. Both files are in the folder case01. After \(2\times10^5 \delta t\) the temperature profile is presented on Fig. target-Fig-Stefan-Temp-Profile for liquid (black) and gas (blue).

Parameters for reference profile

  • \(\theta_w=-0.3\)

  • \(\theta_{\infty}=0.3\)

  • \(\theta_I=0\)

  • \(\alpha_l=\alpha_g=0.1\)

  • \(L=500\): length of computational domain

  • \(\delta x=1\): space step

  • \(\delta t=1\): time step

  • \(x_i(0)=0\): interface position at left boundary

../../_images/Profile-Temp_Analytical_Stefan.png

Temperature profile at \(t=2\times 10^5 \delta t\).

Edit your .ini input file

  • Set appropriate values of [run] section

Solution
  • Set appropriate values of [mesh] section

Solution
  • Set appropriate values of [lbm] section

Solution
  • Set Dirichlet boundary conditions for [equation1] and [equation2].

Solution
[equation1]
boundary_type_xmin=antibounceback
boundary_type_xmax=antibounceback
boundary_type_ymin=periodic
boundary_type_ymax=periodic
collision=BGK

[equation2]
boundary_type_xmin=antibounceback
boundary_type_xmax=antibounceback
boundary_type_ymin=periodic
boundary_type_ymax=periodic
collision=BGK

[phase_field_boundary]
boundary_value_xmin=0.0
boundary_value_xmax=1.0
boundary_value_ymin=0.0
boundary_value_ymax=0.0

[concentration_boundary]
boundary_value_xmin=-0.3
boundary_value_xmax=0.3
boundary_value_ymin=0.0
boundary_value_ymax=0.0
  • Set the same input parameters and mobility=0.3 with appropriate value of interface thickness

Solution
  • Set initial condition

Solution
  • Set outputs

Solution

Run LBM_Saclay

  • Run LBM_Saclay with your input datafile

Results

Generate your csv files with Paraview

$ path_to_pvpython/pvpython Profil-Temp.pvpy

where Profil-Temp.pvpy is in

Superpose with Gnuplot

  • A gnuplot script Profil_Comparison_Analy-LBM.gnutex is also available in the folder

$ gnuplot Profil_Comparison_Analy-LBM.gnutex

will generate a new file Profile-Temp-Stefan_LBM-Analytical.tex

$ pdflatex Profile-Temp-Stefan_LBM-Analytical.tex

to make the pdf file. You should obtain Fig. target-Fig-Stefan-Temp-Profile-with-LBM

../../_images/Profile-Temp-Stefan_LBM-Analytical.png

Comparison between LBM and analytical solution² at \(t=2\times 10^5 \delta t\).

Case \(D_l \neq D_g\)

  • Modify your kernel in order to interpolate \(D(\phi)=(1-\phi)D_l+\phi D_g\) where \(D_l\) and \(D_g\) are respectively the thermal diffusivity in liquid and gas.

Solution
  • Add necesary values of \(D_l\) and \(D_g\) in your the .ini input file

  • Superpose with analytical solution with \(D_l=0.14\) and \(D_g=0.014\)

  • Use the harmonic interpolation for \(D(\phi)\)

Solution
  • Compare the impact on curves

Solution

Section author: Alain Cartalade