格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型

He-Chen-Zhang模型

HCZ模型比上一篇文章中的Shan-Chen模型的一大亮点就是可以自己手动设置接触角,而且相对来说可以模拟的密度比也会大上许多,甚至有文献中可以达到100:1。但是HCZ模型的算法就相对来说要复杂上不少了。

HCZ模型中引入了两个不同的分布函数 f 和 g,具体的算法如下:
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第1张图片
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第2张图片
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第3张图片
在这里插入图片描述
其中,a = 12RT, b = 4.
在这里插入图片描述
流场宏观量的计算如下:
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第4张图片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
表面张力的计算如下:
在这里插入图片描述
这里的参数在我写的程序中被记为kappa,是一个表面张力控制量,具体的量纲可以在文献 Zhang et al, 2000中找到:[1]. Raoyang Zhang, Xiaoyi He, Shiyi Chen, “Interface and surface tension in incompressible lattice Boltzmann multiphase model”, 2000.
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第5张图片
在我写的程序中,设置液体密度为0.251,气体密度为0.024,与文献Lei Wang et al.2013中的参数完全相似。这里贴出来这篇文献:[2]. Lei Wang, Haibo Huang, Xiyun Lu, “Scheme for contact angle and its hysteresis in a multiphase lattice Boltzmann method”, 2013.
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第6张图片
基本的算法就是这样,接下来进行接触角的模型描述。

Contact Angle

在文献[2]中,引入的接触角计算算法是在2007年由hang ding et al.发展的应用于FVM的接触角测量方法:
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第7张图片
图片来源于文献[2].
在这里插入图片描述
上式是一个二维情况下的接触角设置方法,这个方法的巧妙之处就在于只需要在ghost area设置以下指标函数的参数就可以了,这也就是为什么在这个系列的第二篇文章中,我提醒了要为ghost area留白。
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第8张图片
上式是三维情况下的接触角算法。

算法的介绍就到这里,接下来也是惯例地贴出来代码:如果仍然不是很清楚的话就去看我前面提到的三篇文献。

//A program for lattice Boltzmann in D3Q19 mode.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define NUM_THREADS 4
//定义pi的值
#define pi 3.1415926
#define Q 19
#define D 3
/*前处理模块*/
//GLdouble Lha = 0.000010, Lhb = 0.000010, Lhh = 0.000007;//固体边界参数
//网格边界面:x=0,x=l,y=0,y=m.z=0,z=h,内部点为(l-1)*(m-1)*(h-1)个.
#define l 80
#define m 80
#define h 50
GLdouble DD = 25.0;
GLdouble UU = -0.2;

GLdouble c = 1.0;
GLdouble cs2 = c*c / 3.0;

//GLdouble Lha = 0.000010, Lhb = 0.000010, Lhh = 0.000007;//固体边界参数
GLdouble Real_dx = 0.0005;//长度
GLdouble Real_dt = 0.0005;//时间
GLdouble Real_rho = 800.0;//密度
GLdouble Real_mu = 0.001;//粘度
GLdouble Real_vis = Real_mu / Real_rho;
GLdouble Real_sigma = 0.032;//表面张力[N/m = kg/s2]


GLdouble dx = 1.0, dy = 1.0, dz = 1.0;//长度设定
GLdouble dt = 1.0;//时间设定
GLdouble rho_h = 0.251;//密度~质量设定
GLdouble rho_l = 0.024;
GLdouble rho_w = rho_h;
GLdouble psi_max = 0.251;
GLdouble psi_min = 0.024;
//GLdouble vis = cs2*(tau_f - 0.5);//粘度设定
GLdouble vis = (dx*dx / dt)*Real_vis / (Real_dx*Real_dx / Real_dt);//Re = U*L/vis
GLdouble sigma = Real_sigma*(rho_h*dx*dx*dx / dt / dt) / (Real_rho*Real_dx*Real_dx*Real_dx / Real_dt / Real_dt);//We = rho*U*U*L/sigma

GLdouble tau_f = 0.5 + vis / cs2;
GLdouble tau_g = tau_f;//0.7
char up_boundary = 'B';//上边界为Periodic/Bounceback/Dirichlet? choose P,B,D for your prefer.
char down_boundary = 'B';//下边界为Periodic/Bounceback/Dirichlet? choose P,B,D for your prefer.
char left_boundary = 'P';//左边界为Periodic/Bounceback/Dirichlet? choose P,B,D for your prefer.
char right_boundary = 'P';//右边界为Periodic/Bounceback/Dirichlet? choose P,B,D for your prefer.
char front_boundary = 'P';//前边界为Periodic/Bounceback/Dirichlet? choose P,B,D for your prefer.
char behind_boundary = 'P';//后边界为Periodic/Bounceback/Dirichlet? choose P,B,D for your prefer.
/*前处理模块结束*/
GLdouble winx1 = 0.0, winx2 = l, winy1 = 0.0, winy2 = m;//窗口边界
GLint control = 1;
GLint BUOYANCY = 0;
GLint case_index = 0;
GLdouble w[Q] = { 1.0 / 3.0, //指向原点,a=0
1.0 / 18.0, 1.0 / 18.0, 1.0 / 18.0, 1.0 / 18.0, //指向nx,ny,px,py,a=1,2,3,4
1.0 / 36.0, 1.0 / 36.0, 1.0 / 36.0, 1.0 / 36.0, //指向nxny,pxny,pxpy,nxpy,a=5,6,7,8
1.0 / 18.0, //指向nz,a=9
1.0 / 36.0, 1.0 / 36.0, 1.0 / 36.0, 1.0 / 36.0, //指向nxnz,nynz,,pxnz,pynz,a=10,11,12,13
1.0 / 18.0, //指向pz,a=14
1.0 / 36.0, 1.0 / 36.0, 1.0 / 36.0, 1.0 / 36.0 //指向nxpz,nypz,pxpz,pypz,a=15,16,17,18
};
GLdouble e[Q][D] = { { 0.0, 0.0, 0.0 }, //指向原点,a=0
{ 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { -1.0, 0.0, 0.0 }, { 0.0, -1.0, 0.0 }, //指向nx,ny,px,py,a=1,2,3,4
{ 1.0, 1.0, 0.0 }, { -1.0, 1.0, 0.0 }, { -1.0, -1.0, 0.0 }, { 1.0, -1.0, 0.0 }, //指向nxny,pxny,pxpy,nxpy,a=5,6,7,8
{ 0.0, 0.0, 1.0 }, //指向nz,a=9
{ 1.0, 0.0, 1.0 }, { 0.0, 1.0, 1.0 }, { -1.0, 0.0, 1.0 }, { 0.0, -1.0, 1.0 }, //指向nxnz,nynz,,pxnz,pynz,a=10,11,12,13
{ 0.0, 0.0, -1.0 }, //指向pz,a=14
{ 1.0, 0.0, -1.0 }, { 0.0, 1.0, -1.0 }, { -1.0, 0.0, -1.0 }, { 0.0, -1.0, -1.0 } //指向nxpz,nypz,pxpz,pypz,a=15,16,17,18
};
GLint opp_index[Q] = { 0, 3, 4, 1, 2, 7, 8, 5, 6, 14, 17, 18, 15, 16, 9, 12, 13, 10, 11 };
GLdouble BD = 0.0;
GLint Nwri = 10;
GLdouble p0 = 0.0;
GLdouble thetaAVE = 70.0;
GLdouble thetaR = thetaAVE - 0.0, thetaA = thetaAVE + 0.0;
string filename = "10_20";

GLdouble b = 4.0;
GLdouble a = 12.0*cs2;
GLdouble Ia = 0.014594997025594506;//Term used for calculate surface tensor
GLdouble Kappa = sigma / Ia;
GLdouble t_max = 500000;
GLdouble gforce = 0.0;

GLdouble RT = cs2;

static GLdouble fovy = 50, aspect = 1, zFar = 2.0 + h, zNear = -5.0;//投影
static GLdouble eyex = 1.5*l, eyey = 1.5*m, eyez = 0.5*h, centerx = l / 2, centery = m / 2, centerz = h / 2, upx = 0, upy = 0.0, upz = 1.0;//三维观察

class lattice_node{
public:
	GLdouble f[Q];
	GLdouble g[Q];
	GLdouble fnew[Q];
	GLdouble fai;
	GLdouble prho;
	GLdouble dfai[D];
	GLdouble dprho[D];
	GLdouble u[D];
	GLdouble rho;
	GLdouble rh;
	GLdouble laplace_rh;
	GLdouble p;
	GLdouble Force[D];
	string status;

	lattice_node()
	{
		fai = 0.0;
		prho = 0.0;

		rho = rho_l;
		rh = psi_min;
		laplace_rh = 0.0;
		p = 0.0;

		status = "L";
	}
};
lattice_node Nodes[l + 1][m + 1][h + 1];//边界都是预留给虚网格的位置
//GLdouble psx[l + 1][m + 1][h + 1];
GLdouble velocity_before[l + 1][m + 1][h + 1][D];

void init(void){
	glLoadIdentity();
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	gluOrtho2D(winx1, winx2, winy1, winy2);
}

void drawing_liquid(void)
{
	GLint i = 0, j = 0, r = 0;
	GLint hi = 0, hj = 0;
	GLdouble rho_ave = 0.5*(rho_l + rho_h);

	glColor3f(0.0, 0.0, 1.0);
	glPointSize(0.00001);
	glBegin(GL_POINTS);
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status == "L")
				{
					if (Nodes[i][j][r].rho >= rho_ave)
					{
						glVertex3f(i, j, r);
					}
				}
			}
		}
	}
	glEnd();

	glColor3f(0.0, 1.0, 0.0);
	//绘制流场外围
	glBegin(GL_LINE_STRIP);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(l, 0.0, 0.0);
	glVertex3f(l, m, 0.0);
	glVertex3f(0.0, m, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glEnd();
	glBegin(GL_LINE_STRIP);
	glVertex3f(0.0, 0.0, h);
	glVertex3f(l, 0.0, h);
	glVertex3f(l, m, h);
	glVertex3f(0.0, m, h);
	glVertex3f(0.0, 0.0, h);
	glEnd();
	glBegin(GL_LINES);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, h);
	glVertex3f(l, 0.0, 0.0);
	glVertex3f(l, 0.0, h);
	glVertex3f(0.0, m, 0.0);
	glVertex3f(0.0, m, h);
	glVertex3f(l, m, 0.0);
	glVertex3f(l, m, h);
	glEnd();
	//绘制流场外围结束
	
	return;
}

void Bounceback_Boundary()
{
	GLint i = 0, j = 0, r = 0;

	if (up_boundary == 'B')
	{
#pragma omp parallel for private(i,j)
		for (i = 0; i <= l; i++)
		{
			for (j = 0; j <= m; j++)
			{
				Nodes[i][j][h - 1].status = "Bup";
			}
		}
	}
	if (down_boundary == 'B')
	{
#pragma omp parallel for private(i,j)
		for (i = 0; i <= l; i++)
		{
			for (j = 0; j <= m; j++)
			{
				Nodes[i][j][1].status = "Bdown";
			}
		}
	}

	if (front_boundary == 'B')
	{
#pragma omp parallel for private(i,r)
		for (i = 0; i <= l; i++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[i][1][r].status = "Bfront";
			}
		}
	}
	if (behind_boundary == 'B')
	{
#pragma omp parallel for private(i,r)
		for (i = 0; i <= l; i++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[i][m - 1][r].status = "Bbehind";
			}
		}
	}

	if (left_boundary == 'B')
	{
#pragma omp parallel for private(j,r)
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[0][j][r].status = "Bleft";
			}
		}
	}
	if (right_boundary == 'B')
	{
#pragma omp parallel for private(j,r)
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[l][j][r].status = "Bright";
			}
		}
	}

	return;
}

void Dirichlet_Boundary()
{
	GLint i = 0, j = 0, r = 0;

	if (up_boundary == 'D')
	{
#pragma omp parallel for private(i,j)
		for (i = 0; i <= l; i++)
		{
			for (j = 0; j <= m; j++)
			{
				Nodes[i][j][h - 1].status = "Dup";
			}
		}
	}
	if (down_boundary == 'D')
	{
#pragma omp parallel for private(i,j)
		for (i = 0; i <= l; i++)
		{
			for (j = 0; j <= m; j++)
			{
				Nodes[i][j][1].status = "Ddown";
			}
		}
	}

	if (front_boundary == 'D')
	{
#pragma omp parallel for private(i,r)
		for (i = 0; i <= l; i++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[i][1][r].status = "Dfront";
			}
		}
	}
	if (behind_boundary == 'D')
	{
#pragma omp parallel for private(i,r)
		for (i = 0; i <= l; i++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[i][m - 1][r].status = "Dbehind";
			}
		}
	}

	if (left_boundary == 'D')
	{
#pragma omp parallel for private(j,r)
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[0][j][r].status = "Dleft";
			}
		}
	}
	if (right_boundary == 'D')
	{
#pragma omp parallel for private(j,r)
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[l][j][r].status = "Dright";
			}
		}
	}

	return;
}

void calculate_feq(GLdouble rh, GLdouble u[D], GLdouble feq_temp[Q])
{
	GLdouble eu = 0.0, uv = 0.0;
	GLint k = 0, rr = 0;

	for (rr = 0; rr < D; rr++)
	{
		uv += (u[rr] * u[rr]);
	}
#pragma omp parallel for private(k,rr,eu)
	for (k = 0; k < Q; k++)
	{
		eu = 0.0;
		for (rr = 0; rr < D; rr++)
		{
			eu += (e[k][rr] * u[rr]);
		}
		eu *= c;
		feq_temp[k] = w[k] * rh*(1.0 + eu / cs2 + 0.5*eu*eu / cs2 / cs2 - 0.5*uv / cs2);
	}
	return;
}

void calculate_feq_geq(GLdouble rh, GLdouble rho, GLdouble p, GLdouble u[D], GLdouble feq_temp[Q], GLdouble geq_temp[Q])
{
	GLdouble eu = 0.0, uv = 0.0;
	GLint k = 0, rr = 0;

	for (rr = 0; rr < D; rr++)
	{
		uv += (u[rr] * u[rr]);
	}
#pragma omp parallel for private(k,rr,eu)
	for (k = 0; k < Q; k++)
	{
		eu = 0.0;
		for (rr = 0; rr < D; rr++)
		{
			eu += (e[k][rr] * u[rr]);
		}
		eu *= c;
		feq_temp[k] = eu / cs2 + 0.5*eu*eu / cs2 / cs2 - 0.5*uv / cs2;
		geq_temp[k] = w[k] * (p + cs2*rho*(feq_temp[k]));
		feq_temp[k] = w[k] * rh*(1.0 + feq_temp[k]);
	}
	return;
}

void calculate_geq(GLdouble rho, GLdouble p, GLdouble u[D], GLdouble geq_temp[Q])
{
	GLdouble eu = 0.0, uv = 0.0;
	GLint k = 0, rr = 0;

	for (rr = 0; rr < D; rr++)
	{
		uv += (u[rr] * u[rr]);
	}
#pragma omp parallel for private(k,rr,eu)
	for (k = 0; k < Q; k++)
	{
		eu = 0.0;
		for (rr = 0; rr < D; rr++)
		{
			eu += (e[k][rr] * u[rr]);
		}
		eu *= c;
		geq_temp[k] = w[k] * (p + cs2*rho*(eu / cs2 + 0.5*eu*eu / cs2 / cs2 - 0.5*uv / cs2));
	}
	return;
}

GLdouble Cal_error()
{
	GLint i = 0, j = 0, r = 0, rr = 0;
	GLdouble temp_error = 0.0, final_error = 0.0;
#pragma omp parallel for private(i,j,r,rr,temp_error)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status == "L")
				{
					temp_error = 0.0;
					for (rr = 0; rr < D; rr++)
					{
						temp_error += (Nodes[i][j][r].u[rr] - velocity_before[i][j][r][rr])*(Nodes[i][j][r].u[rr] - velocity_before[i][j][r][rr]);
					}
					final_error += sqrt(temp_error);
				}
			}
		}
	}

	return final_error;
}

void Record_velocity(void)
{
	//Record the velocity
	GLint i = 0, j = 0, r = 0, rr = 0;
#pragma omp parallel for private(i,j,r,rr)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status == "L")
				{
					for (rr = 0; rr < D; rr++)
					{
						velocity_before[i][j][r][rr] = Nodes[i][j][r].u[rr];
					}
				}
				//end if.
			}
		}
	}
	//end of 2-D circulation.

	return;
}

void initnodes()
{
	GLint i = 0, j = 0, r = 0, k = 0, rr = 0;
	GLdouble temp_rho = 0.0;
	GLdouble feq[Q], geq[Q];

#pragma omp parallel for private(i,j)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			Nodes[i][j][0].status = "Virtue";
			Nodes[i][j][h].status = "Virtue";
		}
	}
	//end of 2-D circulation.

#pragma omp parallel for private(i,j,r,rr,k,temp_rho,feq,geq)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				Nodes[i][j][r].rh = psi_min;
				Nodes[i][j][r].rho = rho_l;
				

				for (rr = 0; rr < D; rr++)
				{
					Nodes[i][j][r].u[rr] = 0.0;
					Nodes[i][j][r].Force[rr] = 0.0;
					Nodes[i][j][r].dfai[rr] = 0.0;
					Nodes[i][j][r].dprho[rr] = 0.0;
				}

				if ((i - l / 2)*(i - l / 2) + (j - m / 2)*(j - m / 2) + (r - DD*0.7)*(r - DD*0.7) <= (DD*DD / 4.0))
				{
					Nodes[i][j][r].rh = psi_max;
					Nodes[i][j][r].rho = rho_h;
					Nodes[i][j][r].u[D - 1] = UU;
				}

				temp_rho = b*Nodes[i][j][r].rho / 4.0;
				Nodes[i][j][r].p = Nodes[i][j][r].rho*RT*temp_rho*(4.0 - 2.0*temp_rho) / pow((1 - temp_rho), 3) - a*Nodes[i][j][r].rho*Nodes[i][j][r].rho + Nodes[i][j][r].rho*RT;

				calculate_feq_geq(Nodes[i][j][r].rh, Nodes[i][j][r].rho, Nodes[i][j][r].p, Nodes[i][j][r].u, feq, geq);
				for (k = 0; k < Q; k++)
				{
					Nodes[i][j][r].f[k] = feq[k];
					Nodes[i][j][r].g[k] = geq[k];
				}
				//end if.
			}
		}
	}
	//the end of 3-D circulation.

	return;
}

void rebounce_f()
{
	GLint i = 0, j = 0, r = 0;
	GLdouble swap_temp = 0.0;
#pragma omp parallel for private(i,j,r,swap_temp)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status[0] == 'B')
				{
					swap_temp = Nodes[i][j][r].f[1]; Nodes[i][j][r].f[1] = Nodes[i][j][r].f[3]; Nodes[i][j][r].f[3] = swap_temp;
					swap_temp = Nodes[i][j][r].f[2]; Nodes[i][j][r].f[2] = Nodes[i][j][r].f[4]; Nodes[i][j][r].f[4] = swap_temp;
					swap_temp = Nodes[i][j][r].f[5]; Nodes[i][j][r].f[5] = Nodes[i][j][r].f[7]; Nodes[i][j][r].f[7] = swap_temp;
					swap_temp = Nodes[i][j][r].f[6]; Nodes[i][j][r].f[6] = Nodes[i][j][r].f[8]; Nodes[i][j][r].f[8] = swap_temp;

					swap_temp = Nodes[i][j][r].f[9]; Nodes[i][j][r].f[9] = Nodes[i][j][r].f[14]; Nodes[i][j][r].f[14] = swap_temp;

					swap_temp = Nodes[i][j][r].f[10]; Nodes[i][j][r].f[10] = Nodes[i][j][r].f[17]; Nodes[i][j][r].f[17] = swap_temp;
					swap_temp = Nodes[i][j][r].f[11]; Nodes[i][j][r].f[11] = Nodes[i][j][r].f[18]; Nodes[i][j][r].f[18] = swap_temp;
					swap_temp = Nodes[i][j][r].f[12]; Nodes[i][j][r].f[12] = Nodes[i][j][r].f[15]; Nodes[i][j][r].f[15] = swap_temp;
					swap_temp = Nodes[i][j][r].f[13]; Nodes[i][j][r].f[13] = Nodes[i][j][r].f[16]; Nodes[i][j][r].f[16] = swap_temp;
				}
				//end if.
			}
		}
	}
	//The end of 3-D circulation.
	return;
}

void rebounce_g()
{
	GLint i = 0, j = 0, r = 0;
	GLdouble swap_temp = 0.0;
#pragma omp parallel for private(i,j,r,swap_temp)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status[0] == 'B')
				{
					swap_temp = Nodes[i][j][r].g[1]; Nodes[i][j][r].g[1] = Nodes[i][j][r].g[3]; Nodes[i][j][r].g[3] = swap_temp;
					swap_temp = Nodes[i][j][r].g[2]; Nodes[i][j][r].g[2] = Nodes[i][j][r].g[4]; Nodes[i][j][r].g[4] = swap_temp;
					swap_temp = Nodes[i][j][r].g[5]; Nodes[i][j][r].g[5] = Nodes[i][j][r].g[7]; Nodes[i][j][r].g[7] = swap_temp;
					swap_temp = Nodes[i][j][r].g[6]; Nodes[i][j][r].g[6] = Nodes[i][j][r].g[8]; Nodes[i][j][r].g[8] = swap_temp;

					swap_temp = Nodes[i][j][r].g[9]; Nodes[i][j][r].g[9] = Nodes[i][j][r].g[14]; Nodes[i][j][r].g[14] = swap_temp;

					swap_temp = Nodes[i][j][r].g[10]; Nodes[i][j][r].g[10] = Nodes[i][j][r].g[17]; Nodes[i][j][r].g[17] = swap_temp;
					swap_temp = Nodes[i][j][r].g[11]; Nodes[i][j][r].g[11] = Nodes[i][j][r].g[18]; Nodes[i][j][r].g[18] = swap_temp;
					swap_temp = Nodes[i][j][r].g[12]; Nodes[i][j][r].g[12] = Nodes[i][j][r].g[15]; Nodes[i][j][r].g[15] = swap_temp;
					swap_temp = Nodes[i][j][r].g[13]; Nodes[i][j][r].g[13] = Nodes[i][j][r].g[16]; Nodes[i][j][r].g[16] = swap_temp;
				}
				//end if.
			}
		}
	}
	//The end of 3-D circulation.
	return;
}

void Streaming()
{
	GLint i = 0, j = 0, r = 0, ileft = 0, iright = 0, jfront = 0, jbehind = 0, rup = 0, rdown = 0, k = 0;

	//Streaming process::L
#pragma omp parallel for private(i,j,r,ileft,iright,jbehind,jfront,rdown,rup)
	for (i = 0; i <= l; i++)
	{
		ileft = (i > 0) ? (i - 1) : (l);
		iright = (i < l) ? (i + 1) : (0);
		for (j = 0; j <= m; j++)
		{
			jfront = (j > 0) ? (j - 1) : (m);
			jbehind = (j < m) ? (j + 1) : (0);
			for (r = 1; r < h; r++)
			{
				rdown = (r > 1) ? (r - 1) : (h - 1);
				rup = (r < h - 1) ? (r + 1) : (1);

				Nodes[iright][j][r].fnew[1] = Nodes[i][j][r].f[1];
				Nodes[i][jbehind][r].fnew[2] = Nodes[i][j][r].f[2];
				Nodes[ileft][j][r].fnew[3] = Nodes[i][j][r].f[3];
				Nodes[i][jfront][r].fnew[4] = Nodes[i][j][r].f[4];

				Nodes[iright][jbehind][r].fnew[5] = Nodes[i][j][r].f[5];
				Nodes[ileft][jbehind][r].fnew[6] = Nodes[i][j][r].f[6];
				Nodes[ileft][jfront][r].fnew[7] = Nodes[i][j][r].f[7];
				Nodes[iright][jfront][r].fnew[8] = Nodes[i][j][r].f[8];

				Nodes[i][j][rup].fnew[9] = Nodes[i][j][r].f[9];

				Nodes[iright][j][rup].fnew[10] = Nodes[i][j][r].f[10];
				Nodes[i][jbehind][rup].fnew[11] = Nodes[i][j][r].f[11];
				Nodes[ileft][j][rup].fnew[12] = Nodes[i][j][r].f[12];
				Nodes[i][jfront][rup].fnew[13] = Nodes[i][j][r].f[13];

				Nodes[i][j][rdown].fnew[14] = Nodes[i][j][r].f[14];

				Nodes[iright][j][rdown].fnew[15] = Nodes[i][j][r].f[15];
				Nodes[i][jbehind][rdown].fnew[16] = Nodes[i][j][r].f[16];
				Nodes[ileft][j][rdown].fnew[17] = Nodes[i][j][r].f[17];
				Nodes[i][jfront][rdown].fnew[18] = Nodes[i][j][r].f[18];
			}
		}
	}
	//the end of 3-D circulation.
#pragma omp parallel for private(i,j,r,k)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				for (k = 1; k < Q; k++)
				{
					Nodes[i][j][r].f[k] = Nodes[i][j][r].fnew[k];
				}
			}
		}
	}
	//the end of 3-D circulation.

	//Streaming process::L
#pragma omp parallel for private(i,j,r,ileft,iright,jbehind,jfront,rdown,rup)
	for (i = 0; i <= l; i++)
	{
		ileft = (i > 0) ? (i - 1) : (l);
		iright = (i < l) ? (i + 1) : (0);
		for (j = 0; j <= m; j++)
		{
			jfront = (j > 0) ? (j - 1) : (m);
			jbehind = (j < m) ? (j + 1) : (0);
			for (r = 1; r < h; r++)
			{
				rdown = (r > 1) ? (r - 1) : (h - 1);
				rup = (r < h - 1) ? (r + 1) : (1);

				Nodes[iright][j][r].fnew[1] = Nodes[i][j][r].g[1];
				Nodes[i][jbehind][r].fnew[2] = Nodes[i][j][r].g[2];
				Nodes[ileft][j][r].fnew[3] = Nodes[i][j][r].g[3];
				Nodes[i][jfront][r].fnew[4] = Nodes[i][j][r].g[4];

				Nodes[iright][jbehind][r].fnew[5] = Nodes[i][j][r].g[5];
				Nodes[ileft][jbehind][r].fnew[6] = Nodes[i][j][r].g[6];
				Nodes[ileft][jfront][r].fnew[7] = Nodes[i][j][r].g[7];
				Nodes[iright][jfront][r].fnew[8] = Nodes[i][j][r].g[8];

				Nodes[i][j][rup].fnew[9] = Nodes[i][j][r].g[9];

				Nodes[iright][j][rup].fnew[10] = Nodes[i][j][r].g[10];
				Nodes[i][jbehind][rup].fnew[11] = Nodes[i][j][r].g[11];
				Nodes[ileft][j][rup].fnew[12] = Nodes[i][j][r].g[12];
				Nodes[i][jfront][rup].fnew[13] = Nodes[i][j][r].g[13];

				Nodes[i][j][rdown].fnew[14] = Nodes[i][j][r].g[14];

				Nodes[iright][j][rdown].fnew[15] = Nodes[i][j][r].g[15];
				Nodes[i][jbehind][rdown].fnew[16] = Nodes[i][j][r].g[16];
				Nodes[ileft][j][rdown].fnew[17] = Nodes[i][j][r].g[17];
				Nodes[i][jfront][rdown].fnew[18] = Nodes[i][j][r].g[18];
			}
		}
	}
	//the end of 3-D circulation.
#pragma omp parallel for private(i,j,r,k)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				for (k = 1; k < Q; k++)
				{
					Nodes[i][j][r].g[k] = Nodes[i][j][r].fnew[k];
				}
			}
		}
	}
	//the end of 3-D circulation.
	return;
}

void macro_process()
{
	GLint i = 0, j = 0, r = 0, k = 0, rr = 0;
	GLint hi = 0, hj = 0;
	GLint ileft = 0, iright = 0, jfront = 0, jbehind = 0, rup = 0, rdown = 0;
	GLdouble psi_min0 = psi_min;
	GLdouble psi_max0 = psi_max;
	GLdouble temp_rh = 0.0;
	GLint temp_inter1 = 0, temp_inter2 = 0;
	GLdouble temp_angle = 0.0, theta = 0.0;

	//macroscopic process
#pragma omp parallel for private(i,j,r,rr,k,temp_rh)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				//calculation of rh & rho.
				if (Nodes[i][j][r].status == "L")//压力边界下的rh变量是固定的,固体壁面的rh应作为插值的容器已经得到。
				{
					Nodes[i][j][r].rh = 0.0;
					for (k = 0; k < Q; k++)
					{
						Nodes[i][j][r].rh += Nodes[i][j][r].f[k];
					}
				}
				//计算内部流体和固体边界的rho.
				Nodes[i][j][r].rho = rho_l + (Nodes[i][j][r].rh - psi_min0) / (psi_max0 - psi_min0)*(rho_h - rho_l);

				//calculation of prho & fai.
				if (Nodes[i][j][r].status == "L" || Nodes[i][j][r].status[0] == 'D')//压力边界上的场点也算是一种流体
				{
					temp_rh = b*Nodes[i][j][r].rh / 4.0;
					Nodes[i][j][r].prho = Nodes[i][j][r].p - cs2*Nodes[i][j][r].rho;
					Nodes[i][j][r].fai = Nodes[i][j][r].rh*RT*(4 * temp_rh - 2 * temp_rh*temp_rh) / pow((1 - temp_rh), 3) - a*Nodes[i][j][r].rh*Nodes[i][j][r].rh;
				}
				//end if.
			}
		}
	}
	//The end of 3-D circulation.

	//========================================================================
	//==                                                                    ==
	//==                         接触角的计算                               ==
	//==                                                                    ==
	//========================================================================

	////接触角:假设认为地面和顶面是固体边界,那么r=0和r=h的区域就应该被认作是一层虚网格,我们把需要的参数铺在这层虚网格上面。

	if (down_boundary == 'B' && up_boundary == 'B')
	{
#pragma omp parallel for private(i,j,r,ileft,iright,jfront,jbehind,rup,rdown,temp_angle,theta)
		for (i = 0; i <= l; i++)//x方向是显然的周期性边界条件
		{
			ileft = (i > 0) ? (i - 1) : (l);
			iright = (i < l) ? (i + 1) : (0);
			for (j = 0; j <= m; j++)
			{
				jfront = (j > 0) ? (j - 1) : (m);
				jbehind = (j < m) ? (j + 1) : (0);

				//下地面:全部设置为固壁边界条件
				temp_angle = sqrt(pow(Nodes[iright][j][2].rh - Nodes[ileft][j][2].rh, 2) + pow(Nodes[i][jbehind][2].rh - Nodes[i][jfront][2].rh, 2));
				if (temp_angle == 0 && Nodes[i][j][1].rh >= Nodes[i][j][3].rh)
				{
					theta = 0;
				}
				else if (temp_angle == 0 && Nodes[i][j][1].rh < Nodes[i][j][3].rh)
				{
					theta = 180;
				}
				else
				{
					theta = 90 - atan((Nodes[i][j][1].rh - Nodes[i][j][3].rh) / temp_angle) * 180 / pi;
				}

				if (theta > thetaA)
				{
					theta = thetaA;
					Nodes[i][j][1].rh = Nodes[i][j][3].rh + tan(pi*(90.0 - theta) / 180.0)*temp_angle;
				}
				else if (theta < thetaR)
				{
					theta = thetaR;
					Nodes[i][j][1].rh = Nodes[i][j][3].rh + tan(pi*(90.0 - theta) / 180.0)*temp_angle;
				}

				//上地面:全部设置为固壁边界条件
				/*
				temp_angle = sqrt(pow(Nodes[iright][j][h - 2].rh - Nodes[ileft][j][h - 2].rh, 2) + pow(Nodes[i][jbehind][h - 2].rh - Nodes[i][jfront][h - 2].rh, 2));
				if (temp_angle == 0 && Nodes[i][j][h - 1].rh >= Nodes[i][j][h - 3].rh)
				{
				theta = 0;
				}
				else if (temp_angle == 0 && Nodes[i][j][h - 1].rh < Nodes[i][j][h - 3].rh)
				{
				theta = 180;
				}
				else
				{
				theta = 90 - atan((Nodes[i][j][h - 1].rh - Nodes[i][j][h - 3].rh) / temp_angle) * 180 / pi;
				}

				if (theta > thetaA)
				{
				theta = thetaA;
				Nodes[i][j][h - 1].rh = Nodes[i][j][h - 3].rh + tan(pi*(90.0 - theta) / 180.0)*temp_angle;
				}
				else if (theta < thetaR)
				{
				theta = thetaR;
				Nodes[i][j][h - 1].rh = Nodes[i][j][h - 3].rh + tan(pi*(90.0 - theta) / 180.0)*temp_angle;
				}
				*/

				Nodes[i][j][h - 1].rh = Nodes[i][j][h - 3].rh;

				//上下地面的一层虚网格
				Nodes[i][j][0].rh = Nodes[i][j][1].rh;
				Nodes[i][j][h].rh = Nodes[i][j][h - 1].rh;

				//辅助变量prho和fai的上下固壁边界插值计算
				Nodes[i][j][h - 1].prho = Nodes[i][j][h - 2].prho;
				Nodes[i][j][1].prho = Nodes[i][j][2].prho;
				Nodes[i][j][h - 1].fai = Nodes[i][j][h - 2].fai;
				Nodes[i][j][1].fai = Nodes[i][j][2].fai;
			}
		}
	}

	//计算laplace_rh
#pragma omp parallel for private(i,j,r,ileft,iright,jbehind,jfront,rdown,rup)
	for (i = 0; i <= l; i++)
	{
		ileft = (i > 0) ? (i - 1) : (l);
		iright = (i < l) ? (i + 1) : (0);
		for (j = 0; j <= m; j++)
		{
			jfront = (j > 0) ? (j - 1) : (m);
			jbehind = (j < m) ? (j + 1) : (0);
			for (r = 1; r < h; r++)
			{
				rdown = r - 1;
				rup = r + 1;

				Nodes[i][j][r].laplace_rh = (Nodes[ileft][j][r].rh + Nodes[iright][j][r].rh + Nodes[i][jfront][r].rh + Nodes[i][jbehind][r].rh + Nodes[i][j][rup].rh + Nodes[i][j][rdown].rh)*2.0 / 6.0;
				Nodes[i][j][r].laplace_rh += (Nodes[ileft][jfront][r].rh + Nodes[iright][jfront][r].rh + Nodes[ileft][jbehind][r].rh + Nodes[iright][jbehind][r].rh) / 6.0;
				Nodes[i][j][r].laplace_rh += (Nodes[ileft][j][rdown].rh + Nodes[iright][j][rdown].rh + Nodes[ileft][j][rup].rh + Nodes[iright][j][rup].rh) / 6.0;
				Nodes[i][j][r].laplace_rh += (Nodes[i][jfront][rdown].rh + Nodes[i][jbehind][rdown].rh + Nodes[i][jfront][rup].rh + Nodes[i][jbehind][rup].rh) / 6.0;
				Nodes[i][j][r].laplace_rh -= 24.0*Nodes[i][j][r].rh / 6.0;
			}
		}
	}
	//the end of 3-D circulation.

#pragma omp parallel for private(i,j,r,ileft,iright,jbehind,jfront,rdown,rup)
	for (i = 0; i <= l; i++)
	{
		ileft = (i > 0) ? (i - 1) : (l);
		iright = (i < l) ? (i + 1) : (0);
		for (j = 0; j <= m; j++)
		{
			jfront = (j > 0) ? (j - 1) : (m);
			jbehind = (j < m) ? (j + 1) : (0);
			for (r = 1; r < h; r++)
			{
				rdown = r - 1;
				rup = r + 1;

				if (Nodes[i][j][r].status[0] == 'B')
				{
					continue;
				}

				Nodes[i][j][r].Force[0] = 2.0*(Nodes[iright][j][r].laplace_rh - Nodes[ileft][j][r].laplace_rh);
				Nodes[i][j][r].Force[0] += (Nodes[iright][jbehind][r].laplace_rh - Nodes[ileft][jfront][r].laplace_rh);
				Nodes[i][j][r].Force[0] += (Nodes[iright][jfront][r].laplace_rh - Nodes[ileft][jbehind][r].laplace_rh);
				Nodes[i][j][r].Force[0] += (Nodes[iright][j][rup].laplace_rh - Nodes[ileft][j][rdown].laplace_rh);
				Nodes[i][j][r].Force[0] += (Nodes[iright][j][rdown].laplace_rh - Nodes[ileft][j][rup].laplace_rh);
				Nodes[i][j][r].Force[0] *= Kappa*Nodes[i][j][r].rh / 12.0;

				Nodes[i][j][r].Force[1] = 2.0*(Nodes[i][jbehind][r].laplace_rh - Nodes[i][jfront][r].laplace_rh);
				Nodes[i][j][r].Force[1] += (Nodes[iright][jbehind][r].laplace_rh - Nodes[ileft][jfront][r].laplace_rh);
				Nodes[i][j][r].Force[1] += (Nodes[ileft][jbehind][r].laplace_rh - Nodes[iright][jfront][r].laplace_rh);
				Nodes[i][j][r].Force[1] += (Nodes[i][jbehind][rup].laplace_rh - Nodes[i][jfront][rdown].laplace_rh);
				Nodes[i][j][r].Force[1] += (Nodes[i][jbehind][rdown].laplace_rh - Nodes[i][jfront][rup].laplace_rh);
				Nodes[i][j][r].Force[1] *= Kappa*Nodes[i][j][r].rh / 12.0;

				Nodes[i][j][r].Force[2] = 2.0*(Nodes[i][j][rup].laplace_rh - Nodes[i][j][rdown].laplace_rh);
				Nodes[i][j][r].Force[2] += (Nodes[iright][j][rup].laplace_rh - Nodes[ileft][j][rdown].laplace_rh);
				Nodes[i][j][r].Force[2] += (Nodes[ileft][j][rup].laplace_rh - Nodes[iright][j][rdown].laplace_rh);
				Nodes[i][j][r].Force[2] += (Nodes[i][jbehind][rup].laplace_rh - Nodes[i][jfront][rdown].laplace_rh);
				Nodes[i][j][r].Force[2] += (Nodes[i][jfront][rup].laplace_rh - Nodes[i][jbehind][rdown].laplace_rh);
				Nodes[i][j][r].Force[2] *= Kappa*Nodes[i][j][r].rh / 12.0;

				if (BUOYANCY == 1)
				{
					Nodes[i][j][r].Force[2] -= gforce*Nodes[i][j][r].rho;
				}

				Nodes[i][j][r].dfai[0] = 2.0*(Nodes[iright][j][r].fai - Nodes[ileft][j][r].fai);
				Nodes[i][j][r].dfai[0] += (Nodes[iright][jbehind][r].fai - Nodes[ileft][jfront][r].fai);
				Nodes[i][j][r].dfai[0] += (Nodes[iright][jfront][r].fai - Nodes[ileft][jbehind][r].fai);
				Nodes[i][j][r].dfai[0] += (Nodes[iright][j][rup].fai - Nodes[ileft][j][rdown].fai);
				Nodes[i][j][r].dfai[0] += (Nodes[iright][j][rdown].fai - Nodes[ileft][j][rup].fai);
				Nodes[i][j][r].dfai[0] /= 12.0;

				Nodes[i][j][r].dfai[1] = 2.0*(Nodes[i][jbehind][r].fai - Nodes[i][jfront][r].fai);
				Nodes[i][j][r].dfai[1] += (Nodes[iright][jbehind][r].fai - Nodes[ileft][jfront][r].fai);
				Nodes[i][j][r].dfai[1] += (Nodes[ileft][jbehind][r].fai - Nodes[iright][jfront][r].fai);
				Nodes[i][j][r].dfai[1] += (Nodes[i][jbehind][rup].fai - Nodes[i][jfront][rdown].fai);
				Nodes[i][j][r].dfai[1] += (Nodes[i][jbehind][rdown].fai - Nodes[i][jfront][rup].fai);
				Nodes[i][j][r].dfai[1] /= 12.0;

				Nodes[i][j][r].dfai[2] = 2.0*(Nodes[i][j][rup].fai - Nodes[i][j][rdown].fai);
				Nodes[i][j][r].dfai[2] += (Nodes[iright][j][rup].fai - Nodes[ileft][j][rdown].fai);
				Nodes[i][j][r].dfai[2] += (Nodes[ileft][j][rup].fai - Nodes[iright][j][rdown].fai);
				Nodes[i][j][r].dfai[2] += (Nodes[i][jbehind][rup].fai - Nodes[i][jfront][rdown].fai);
				Nodes[i][j][r].dfai[2] += (Nodes[i][jfront][rup].fai - Nodes[i][jbehind][rdown].fai);
				Nodes[i][j][r].dfai[2] /= 12.0;

				Nodes[i][j][r].dprho[0] = 2.0*(Nodes[iright][j][r].prho - Nodes[ileft][j][r].prho);
				Nodes[i][j][r].dprho[0] += (Nodes[iright][jbehind][r].prho - Nodes[ileft][jfront][r].prho);
				Nodes[i][j][r].dprho[0] += (Nodes[iright][jfront][r].prho - Nodes[ileft][jbehind][r].prho);
				Nodes[i][j][r].dprho[0] += (Nodes[iright][j][rup].prho - Nodes[ileft][j][rdown].prho);
				Nodes[i][j][r].dprho[0] += (Nodes[iright][j][rdown].prho - Nodes[ileft][j][rup].prho);
				Nodes[i][j][r].dprho[0] /= 12.0;

				Nodes[i][j][r].dprho[1] = 2.0*(Nodes[i][jbehind][r].prho - Nodes[i][jfront][r].prho);
				Nodes[i][j][r].dprho[1] += (Nodes[iright][jbehind][r].prho - Nodes[ileft][jfront][r].prho);
				Nodes[i][j][r].dprho[1] += (Nodes[ileft][jbehind][r].prho - Nodes[iright][jfront][r].prho);
				Nodes[i][j][r].dprho[1] += (Nodes[i][jbehind][rup].prho - Nodes[i][jfront][rdown].prho);
				Nodes[i][j][r].dprho[1] += (Nodes[i][jbehind][rdown].prho - Nodes[i][jfront][rup].prho);
				Nodes[i][j][r].dprho[1] /= 12.0;

				Nodes[i][j][r].dprho[2] = 2.0*(Nodes[i][j][rup].prho - Nodes[i][j][rdown].prho);
				Nodes[i][j][r].dprho[2] += (Nodes[iright][j][rup].prho - Nodes[ileft][j][rdown].prho);
				Nodes[i][j][r].dprho[2] += (Nodes[ileft][j][rup].prho - Nodes[iright][j][rdown].prho);
				Nodes[i][j][r].dprho[2] += (Nodes[i][jbehind][rup].prho - Nodes[i][jfront][rdown].prho);
				Nodes[i][j][r].dprho[2] += (Nodes[i][jfront][rup].prho - Nodes[i][jbehind][rdown].prho);
				Nodes[i][j][r].dprho[2] /= 12.0;
			}
		}
	}
	//the end of 3-D circulation.
	return;
}

void geten()
{
	GLint i = 0, j = 0, r = 0, rr = 0, k = 0;

#pragma omp parallel for private(i,j,r,rr,k)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status == "L")//压力边界点处的速度由插值得到,故不作计算
				{
					for (rr = 0; rr < D; rr++)
					{
						Nodes[i][j][r].u[rr] = 0.0;
						for (k = 0; k < Q; k++)
						{
							Nodes[i][j][r].u[rr] += e[k][rr] * Nodes[i][j][r].g[k];
						}
						Nodes[i][j][r].u[rr] *= c;
						Nodes[i][j][r].u[rr] += 0.5*RT*dt*Nodes[i][j][r].Force[rr];
						Nodes[i][j][r].u[rr] /= (Nodes[i][j][r].rho*RT);
					}
				}
				//end if.

				if (Nodes[i][j][r].status == "L" || Nodes[i][j][r].status[0] == 'D')
				{
					Nodes[i][j][r].p = 0.0;
					for (k = 0; k < Q; k++)
					{
						Nodes[i][j][r].p += Nodes[i][j][r].g[k];
					}
					for (rr = 0; rr < D; rr++)
					{
						Nodes[i][j][r].p += (-0.5)*Nodes[i][j][r].u[rr] * Nodes[i][j][r].dprho[rr] * dt;
					}
				}
				//end if.
			}
		}
	}
	return;
}

void Collision()
{
	GLint i = 0, j = 0, r = 0, k = 0, rr = 0;
	GLdouble temp_f = 0.0, temp_g1 = 0.0, temp_g2 = 0.0;
	GLdouble feq[Q], geq[Q];

	//Collide process
#pragma omp parallel for private(i,j,r,rr,k,temp_f,temp_g1,temp_g2,feq,geq)
	for (i = 0; i <= l; i++)
	{
		for (j = 0; j <= m; j++)
		{
			for (r = 1; r < h; r++)
			{
				if (Nodes[i][j][r].status == "L")// || Nodes[i][j][r].status[0] == 'D')
				{
					calculate_feq_geq(Nodes[i][j][r].rh, Nodes[i][j][r].rho, Nodes[i][j][r].p, Nodes[i][j][r].u, feq, geq);
					for (k = 0; k < Q; k++)
					{
						Nodes[i][j][r].f[k] = Nodes[i][j][r].f[k] * (1.0 - 1.0 / tau_f) + feq[k] / tau_f;
						Nodes[i][j][r].g[k] = Nodes[i][j][r].g[k] * (1.0 - 1.0 / tau_g) + geq[k] / tau_g;

						temp_f = 0.0;
						temp_g1 = 0.0;
						temp_g2 = 0.0;

						for (rr = 0; rr < D; rr++)
						{
							temp_f += (e[k][rr] * c - Nodes[i][j][r].u[rr])*(-Nodes[i][j][r].dfai[rr]) * feq[k];
							temp_g1 += (e[k][rr] * c - Nodes[i][j][r].u[rr])*Nodes[i][j][r].Force[rr] * feq[k] / Nodes[i][j][r].rh;
							temp_g2 += (e[k][rr] * c - Nodes[i][j][r].u[rr])*(-Nodes[i][j][r].dprho[rr]) * ((feq[k] / Nodes[i][j][r].rh) - w[k]);
						}
						Nodes[i][j][r].f[k] += dt*(tau_f - 0.5) / tau_f * temp_f / (RT*Nodes[i][j][r].rh);
						Nodes[i][j][r].g[k] += dt*(tau_g - 0.5) / tau_g*(temp_g1 + temp_g2);
					}
				}
				//end if.
			}
		}
	}
	//the end of 3-D circulation.
	return;
}

void output()
{
	GLint i = 0, j = 0, r = 0;
	GLint Temp_i = 0, Temp_j = 0, Temp_r = 0;
	char Temp_str1 = 'c', Temp_str2 = 'c';
	FILE *fp;

	if (!(fp = fopen((filename + "//data_velocity.dat").c_str(), "w")))
	{
		printf("写入文件失败!\n");
		exit(1);
	}
	fprintf(fp, "TITLE =\"lattice boltzmann\"\n");
	fprintf(fp, "VARIABLES = \"X\" \"Y\" \"Z\" \"U\" \"V\" \"W\"\n");
	fprintf(fp, "ZONE I=%d, J=%d, K=%d\n", l + 1, m - 1, h - 1);
	fprintf(fp, "F=POINT\n");
	for (i = 0; i <= l; i++)
	{
		for (j = 1; j < m; j++)
		{
			for (r = 1; r < h; r++)
			{
				fprintf(fp, "%d %d %d %lf %lf %lf\n", i, j, r, Nodes[i][j][r].u[0], Nodes[i][j][r].u[1], Nodes[i][j][r].u[2]);
			}
		}
	}
	fclose(fp);

	if (!(fp = fopen((filename + "//data_density.dat").c_str(), "w")))
	{
		printf("写入文件失败!\n");
		exit(1);
	}
	fprintf(fp, "TITLE =\"lattice boltzmann\"\n");
	fprintf(fp, "VARIABLES = \"X\" \"Y\" \"Z\" \"RHO\"\n");
	fprintf(fp, "ZONE I=%d, J=%d, K=%d\n", l + 1, m - 1, h - 1);
	fprintf(fp, "F=POINT\n");
	for (i = 0; i <= l; i++)
	{
		for (j = 1; j < m; j++)
		{
			for (r = 1; r < h; r++)
			{
				fprintf(fp, "%d %d %d %lf\n", i, j, r, Nodes[i][j][r].rho);
			}
		}
	}
	fclose(fp);

	return;
}

void LBMs()
{
	GLdouble final_error = 1.0;
	GLint i = 0, r = 0;

	omp_set_num_threads(NUM_THREADS);

	//标记迪利克雷边界条件
	Dirichlet_Boundary();
	////因为压力边界一般来说都不会包括上壁沿和下壁沿,所以回弹性边界条件放置在压力边界条件的后面进行标记。
	//标记回弹性边界条件
	Bounceback_Boundary();

	//初始化
	initnodes();

	drawing_liquid();
	glutSwapBuffers();

	cout << "Kappa: " << Kappa << endl;
	cout << "tau_f: " << tau_f << endl;
	system("pause");

	for (GLint t = 0; t < t_max; t++)
	{
		//Record the velocity
		Record_velocity();

		//Streaming process
		Streaming();

		//macro process
		macro_process();
		geten();

		//Collision process
		rebounce_f();
		rebounce_g();
		Collision();
		//Dirichlet_fg();

		if (t%Nwri == 0)
		{
			std::cout << "=====================" << endl;
			final_error = Cal_error();
			std::cout << "Step: " << t << ";  error: " << final_error << endl;

			glClearColor(0.0, 0.0, 0.0, 0.0);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			glLoadIdentity();
			glMatrixMode(GL_MODELVIEW);

			gluPerspective(fovy, aspect, zFar, zNear);
			gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz);
			drawing_liquid();
			glutSwapBuffers();

			output();
		}
	}
}

static void display(void)
{
	init();
	GLint i = 0, j = 0, r = 0;
	GLint Temp_i = 0, Temp_j = 0, Temp_r = 0;
	GLdouble scale_velo = 20;
	char Temp_str1 = 'c', Temp_str2 = 'c';
	FILE *fp;

	glClearColor(0.0, 0.0, 0.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	gluPerspective(fovy, aspect, zFar, zNear);
	gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz);

	glColor3f(0.0, 1.0, 0.0);
	//绘制流场外围
	glBegin(GL_LINE_STRIP);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(l, 0.0, 0.0);
	glVertex3f(l, m, 0.0);
	glVertex3f(0.0, m, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glEnd();
	glBegin(GL_LINE_STRIP);
	glVertex3f(0.0, 0.0, h);
	glVertex3f(l, 0.0, h);
	glVertex3f(l, m, h);
	glVertex3f(0.0, m, h);
	glVertex3f(0.0, 0.0, h);
	glEnd();
	glBegin(GL_LINES);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, h);
	glVertex3f(l, 0.0, 0.0);
	glVertex3f(l, 0.0, h);
	glVertex3f(0.0, m, 0.0);
	glVertex3f(0.0, m, h);
	glVertex3f(l, m, 0.0);
	glVertex3f(l, m, h);
	glEnd();
	//绘制流场外围结束

	if (control == 1)
	{
		LBMs();
		control = -1;
		output();
	}
	if (control == 0)
	{
		if (!(fp = fopen((filename + "//data_density.dat").c_str(), "r")))
		{
			printf("读入文件失败!\n");
			exit(1);
		}
		while (true)
		{
			Temp_str2 = Temp_str1;
			fscanf(fp, "%c", &Temp_str1);
			if (Temp_str1 == 'T'&&Temp_str2 == 'N')
			{
				break;
			}
		}
		for (i = 0; i <= l; i++)
		{
			for (j = 1; j < m; j++)
			{
				for (r = 1; r < h; r++)
				{
					fscanf(fp, "%d %d %d %lf\n", &Temp_i, &Temp_j, &Temp_r, &Nodes[i][j][r].rho);
				}
			}
		}
		fclose(fp);
	}

	drawing_liquid();

	glutSwapBuffers();
	//glFlush();
}

void chooseMode(GLint menuIteemNum)
{
	switch (menuIteemNum)
	{
	case 0:
		case_index = 0; break;
	case 1:
		case_index = 1; break;
	case 2:
		case_index = 2; break;
	default:
		case_index = -1; break;
	}
	glutPostRedisplay();
}

void mouseFunc(GLint button, GLint action, GLint xMouse, GLint yMouse)
{
	glutCreateMenu(chooseMode);
	glutAddMenuEntry("figure : Re=100 & grid = 40x40 80x80 160x160", 0);
	glutAddMenuEntry("figure : Re=400 & grid = 40x40 80x80 160x160", 1);
	glutAddMenuEntry("figure : Re=1000 & grid = 40x40 80x80 160x160", 2);

	glutAttachMenu(GLUT_RIGHT_BUTTON);
}

int main(int argc, char *argv[])
{
	if (l<2 || m<2 || h<2)
	{
		cout << "There's an error in the defination of the division of mesh." << endl;
		exit(-1);
	}
	glutInit(&argc, argv);
	glutInitWindowSize(600, 600);
	glutInitWindowPosition(10, 10);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

	glutCreateWindow("Poiseuille Flow");

	init();

	glutDisplayFunc(display);
	glutMouseFunc(mouseFunc);

	glutMainLoop();

	return EXIT_SUCCESS;
}

这个程序跟上一个程序完全类似,仅仅是计算液滴在不同接触角下在壁面上的铺展。这里就懒得再放自己跑出来的结果了,知识展示一下文献[2]中的结果。
格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型_第9张图片
LBM的多相流模型到这里就算全部都总结完了,其他的比如自由能模型等,我感觉并不是十分好用,所以也没有去接触过。LBM还缺一个圆柱绕流的浸入边界法没有深入讲解,我估计自己也实在是懒得再去写了。

LBM的总结到这里基本就是完全结束了。后面会再开新的系列,不过就已经不再是CFD领域的东西了。

你可能感兴趣的:(格子玻尔兹曼机(Lattice Boltzmann Method)系列6:LBM多相流实例之HCZ模型)