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:
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:
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:
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.)
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_Tutorialand rename itAllen-Cahn-Temp_TutorialModify
_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_Tutorialwhich is contained inkernels/Templates_for_Tutorials.
Commands
$ cd LBM_Saclay_Rech-Dev/src/kernels $ cp -r Templates_for_Tutorials/ADE_for_CH-Tutorial ./Allen-Cahn-Temp_TutorialRemarks
Your new developments will be performed in the folder
Cahn-Hilliard_TutorialThe folder name
ADE_for_AC-Temp_Tutorialwill be used for compilation
All files have the extension
_ADEfor Advection Diffusion Equation. Rename them with_ACTfor 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
Change strings
_ADEwith_ACTOpen all files in your favorite editor (e.g.
geanyorcodium).Search in all files the strings
_ADEand replace them with_ACT(Ctrl Hwithgeany).
Change the problem name: in file
Setup.hmodify the problem nameADEwithACTregister_problem<Problem, ModelParams::Tag2Quadra>("ADE", "base");
Modification with
ACTregister_problem<Problem, ModelParams::Tag2Quadra>("ACT", "base");Remark
Your new problem will be named
ACTinside your.iniinput 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
ACTof LBM_Saclay either with the simple diffusion of gaussian or with the advected gaussian in the folderrun_training_lbm/Tutorial_Stefan
Commands
Go to the directory
$ cd LBM_Saclay_Rech-Dev/run_training_lbm/Tutorial_StefanRun simple diffusion of gaussian
$ ../../build_CH/src/LBM_saclay TestCase_Gaussian_ADE.iniRun 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 ifforEquationTag2
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 ifforEquationTag2
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
f2andBOUNDARY_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
tagPHIforEquationTag2
Solution
EquationTag1 tagC; EquationTag2 tagPHI;
Add two empty functions called
setup_colliderforEquationTag2with 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 ifforBOUNDARY_EQUATION_2andtagPHIwith keyworkBOUNDARY_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
ICbyITEMPand add the new indices for chemical potentialIPHIand laplacianIDPHIDT.
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_TEMPfor 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_PHIfor collision rate using the mobility \(M_\phi\) of Allen-Cahn equation
Solution
tau_PHI
Add a new hyperbolic tangent function
phi0for 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
Liquid temperature
Gas temperature
Transcendental equation
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
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.3with 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
In paraview export one temperature profile along x-axis in
.csvformat (see paraview commands in Run “Two-phase with fluid flows” PART A: Verifications).Alternatively, your
.csvfiles can generated by using thepvpythoncommand$ path_to_pvpython/pvpython Profil-Temp.pvpywhere
Profil-Temp.pvpyis in
Superpose with Gnuplot
A gnuplot script
Profil_Comparison_Analy-LBM.gnutexis also available in the folder$ gnuplot Profil_Comparison_Analy-LBM.gnutexwill generate a new file
Profile-Temp-Stefan_LBM-Analytical.tex$ pdflatex Profile-Temp-Stefan_LBM-Analytical.texto make the pdf file. You should obtain Fig.
target-Fig-Stefan-Temp-Profile-with-LBM
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
.iniinput fileSuperpose 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