diff --git a/docs/.buildinfo b/docs/.buildinfo new file mode 100644 index 0000000..79e3920 --- /dev/null +++ b/docs/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 460f45d6faf9867abf533d23c6816ee9 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +1 @@ + diff --git a/docs/.nojekyll copy b/docs/.nojekyll copy new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/.nojekyll copy @@ -0,0 +1 @@ + diff --git a/docs/2phase.html b/docs/2phase.html new file mode 100644 index 0000000..6df4792 --- /dev/null +++ b/docs/2phase.html @@ -0,0 +1,125 @@ + + +
+ + + +This file reads the stl file and output to vtk file
+This file output geometry data
+#import numpy and math packahe
+import numpy as np
+import math
+
+
+# INPUT STL FILE NAME
+output_name = 'geo.dat'
+
+# POINT SEARCHING RESOLUTION IN X direction, Y,Z direction will be calculate by the code
+# the bigger value ~ more points will be found inside STL
+dnx, dny, dnz = 60, 60, 60
+
+
+#==========================================================
+# DO NOT CHANGE BELOW
+#==========================================================
+#create np matrix with dnx*dny*dnz zero
+out_dat = np.zeros((dnx,dny,dnz))
+
+#=======Can define some geometry here to out_dat=========
+#out_dat[1,:,:] = 1
+
+#=========================================================
+#reshape out_dat with column major
+out_dat = out_dat.reshape(out_dat.size, order = 'F')
+#save the file with the transfer of out_dat based on integer type
+np.savetxt(output_name,out_dat.T,fmt='%d')
+
This solver is almost similar to lbm_solver_3d expect several difference as follows:
+Some parameter is different
#grid resolution
+nx,ny,nz = 60,50,5
+#external force
+fx,fy,fz = 1.0e-6,0.0,0.0
+#viscosity
+niu = 0.1
+#import geometry
+geo_name = './BC.dat'
+#maximum timestep
+max_timestep = 5000
+#output frequency
+output_fre = 100
+#vtk file output frequency
+vtk_fre = 500
+
There are two array for solid flag data.
ns_np = init_geo(geo_name)
+solid_np = ns_np.astype(int)
+#solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+ns.from_numpy(ns_np)
+
The streaming function is different
@ti.kernel
+def streaming0():
+ for i in ti.grouped(rho):
+ if (solid[i] == 0):
+ for s in ti.static(range(19)):
+ ip = periodic_index(i+e[s])
+ #if it is fluid f2=f otherwise apply bounce-back f2[i,s]=f[ip,LR[s]]
+ f2[i,s] = f[i,s] + ns[i]*(f[ip,LR[s]] - f[i,s])
+
+
+@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ if (solid[i] == 0):
+ #if it is fluid apply streaming
+ for s in ti.static(range(19)):
+ ip = periodic_index(i+e[s])
+ F[ip,s] = f2[i,s]
+
+ #if (solid[ip]==0):
+ # F[ip,s] = f[i,s]
+ #else:
+ # F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+#not used
+@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in ti.static(range(19)):
+ f[i,s] = F[i,s]
+
streaming3()
calculates the macroscopic variable
This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This file is the non-objective oriented version of singlephase solver without using class. +At the begining of the this file it define some variable first.
+#import some package
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+#initialize taichi with cpu, dunamic index, disable profiler and disables printing the intermediate representation
+ti.init(arch=ti.cpu, dynamic_index=True, kernel_profiler=False, print_ir=False)
+#enable projection
+enable_projection = True
+#nx,ny,nz = 100,50,5
+#define 131x131x131 and zero external force
+nx,ny,nz = 131,131,131
+fx,fy,fz = 0.0e-6,0.0,0.0
+#viscosity=0.1
+niu = 0.1
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 1, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 1, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+#define old density distribution funciton nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define new density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#define velocity nx*ny*nz
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#define lattice speed 3*19
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#define s diagonal 19 dimension vector
+S_dig = ti.field(ti.f32,shape=(19))
+#define another lattice speed 3*19
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#define weight parameter 19 dimesnion vector
+w = ti.field(ti.f32, shape=(19))
+#define solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#define vector for streaming 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+#define external force with a 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+#define velocity in x,y,z direction with 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#define transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#define inverse of transforming matrix
+inv_M = ti.field(ti.f32, shape=(19,19))
+#define single relaxation parameter
+tau_f=3.0*niu+0.5
+#define single relaxation frequency
+s_v=1.0/tau_f
+#define other parameter in the s diagonal
+s_other=8.0*(2.0-s_v)/(8.0-s_v)
+#define s matrix but not used
+S_np = np.zeros((19,19))
+S_np[0,0]=0; S_np[1,1]=s_v; S_np[2,2]=s_v; S_np[3,3]=0; S_np[4,4]=s_other; S_np[5,5]=0;
+S_np[6,6]=s_other; S_np[7,7]=0; S_np[8,8]=s_other; S_np[9,9]=s_v; S_np[10,10]=s_v; S_np[11,11]=s_v;
+S_np[12,12]=s_v; S_np[13,13]=s_v; S_np[14,14]=s_v; S_np[15,15]=s_v; S_np[16,16]=s_other; S_np[17,17]=s_other;
+S_np[18,18]=s_other
+#define numpy array version of s diagonal.
+S_dig_np = np.array([0,s_v,s_v,0,s_other,0,s_other,0,s_other, s_v, s_v,s_v,s_v,s_v,s_v,s_v,s_other,s_other,s_other])
+#define numpy version of transforming matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#define inverse of transforming matrix using inv function in linalg package
+inv_M_np = np.linalg.inv(M_np)
+#define index for streaming
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#assign numpy version to M.np to M
+M.from_numpy(M_np)
+#assign numpy version of inverser matrix inv_M_np to inv_M
+inv_M.from_numpy(inv_M_np)
+#assign numpy versio of LR array to LR
+LR.from_numpy(LR_np)
+#assign fx,fy,fz to vector external force
+ext_f[None] = ti.Vector([fx,fy,fz])
+#assign numpy version of S diagnal S_dig_np to S_dig
+S_dig.from_numpy(S_dig_np)
+#make inv_M,M,LR,S_dig not modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+ti.static(S_dig)
+
+#create mesh nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#numpy meshgrid from x,y,z 1d array to 3d array X,Y,Z here use ij indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local,u)
calculate the equilibrium density distribution function in velocity space
# taichi funciton
+@ti.func
+def feq(k,rho_local, u):
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
initialize velocity=0, density=1 and denisty distribution function= equilibrium density distribution function
@ti.kernel
+def init():
+ for i,j,k in rho:
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
load geometry file
def init_geo(filename):
+ #load data
+ in_dat = np.loadtxt(filename)
+ #reshape it with column major
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+ return in_dat
+
static_init()
initialize lattixe speed weight parameter and boundary velocity
@ti.kernel
+def static_init():
+if ti.static(enable_projection): # No runtime overhead
+ #initialize lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #initialize lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #intialize weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #intialize boundary velocity
+ bc_vel_x_left[None] = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right[None] = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left[None] = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right[None] = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left[None] = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right[None] = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M
calculate denisty distribution function in momentum space M*f=m
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in range(19):
+ for s in range(19):
+ #calculte m=M*f here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
this
+@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in range(19):
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
this
+@ti.kernel
+def colission():
+ for i,j,k in rho:
+ if (solid[i,j,k] == 0):
+ m_temp = multiply_M(i,j,k)
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ for s in range(19):
+ m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ m_temp[s] += (1-0.5*S_dig[s])*GuoF(i,j,k,s,v[i,j,k])
+
+ for s in range(19):
+ f[i,j,k,s] = 0
+ for l in range(19):
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
this
+@ti.func
+def periodic_index(i):
+ iout = i
+ if i[0]<0: iout[0] = nx-1
+ if i[0]>nx-1: iout[0] = 0
+ if i[1]<0: iout[1] = ny-1
+ if i[1]>ny-1: iout[1] = 0
+ if i[2]<0: iout[2] = nz-1
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
this
+@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ if (solid[i] == 0):
+ for s in range(19):
+ ip = periodic_index(i+e[s])
+ if (solid[ip]==0):
+ F[ip,s] = f[i,s]
+ else:
+ F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+
this
+@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in range(19):
+ f[i,s] = F[i,s]
+
this
+@ti.kernel
+def Boundary_condition():
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+
this
+@ti.kernel
+def streaming3():
+ for i in ti.grouped(rho):
+ if (solid[i]==0):
+ rho[i] = 0
+ v[i] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,s] = F[i,s]
+ rho[i] += f[i,s]
+ v[i] += e_f[s]*f[i,s]
+
+ v[i] /= rho[i]
+ v[i] += (ext_f[None]/2)/rho[i]
+
+ else:
+ rho[i] = 1.0
+ v[i] = ti.Vector([0,0,0])
+
this
+This file reads the stl file and output to vtk file
+This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This file use the LBM_3D_SinglePhase_Solver to simulate the cavity flow
+#import certain packages
+import time
+import taichi as ti
+
+ti.init(arch=ti.cpu, dynamic_index=False, kernel_profiler=False, print_ir=False)
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#set 50*50*50 cavity based on LB3D_Solver_Single_Phase solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=50,ny=50,nz=50, sparse_storage=False)
+
+#import geometry data
+lb3d.init_geo('./geo_cavity.dat')
+#set the x-right velocity
+lb3d.set_bc_vel_x1([0.0,0.0,0.1])
+#initialize
+lb3d.init_simulation()
+
+#simulation step
+for iter in range(2000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #get the maximum velocity
+ max_v = lb3d.get_max_v()
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the number of time steps, maxiumum force and the force scale=0
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, max_v, 0.0))
+ #every 1000 time steps export the vtk file
+ if (iter%1000==0):
+ lb3d.export_VTK(iter)
+
This file simulate the porous medium based on the LBM_3D_SinglePhase_Solver
+#import time and taichi package
+import time
+import taichi as ti
+#taichi intialization
+ti.init(arch=ti.cpu)
+#import the LBM_3D_SinglePhase_Solver
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#create the 131*131*131 gird LBM_3D_SinglePhase_Solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=131,ny=131,nz=131)
+#import the porous medium geometry
+lb3d.init_geo('./img_ftb131.txt')
+#set x-left and x-right density
+lb3d.set_bc_rho_x1(0.99)
+lb3d.set_bc_rho_x0(1.0)
+#initialize the simulation
+lb3d.init_simulation()
+#simulation loop
+for iter in range(50000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the time step, max force=10, force_scale=10
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+ #export VTK every 2000 time step
+ if (iter%2000==0):
+ lb3d.export_VTK(iter)
+
This file generate geometry file for solver to read
+#import certain module
+import numpy as np
+import math
+
+
+#define the input file name
+# INPUT STL FILE NAME
+output_name = 'geo_cavity.dat'
+
+#define the grid resolution
+dnx, dny, dnz = 50, 50, 50
+
+#==========================================================
+# DO NOT CHANGE BELOW
+#==========================================================
+
+#define an matrix dnx*dny*dnz with zero values
+out_dat = np.zeros((dnx,dny,dnz))
+
+#=======Can define some geometry here to out_dat=========
+#define the boundary to be solid
+out_dat[0,:,:] = 1
+#cout_dat[:,:,0] = 1
+out_dat[:,0,:] = 1
+out_dat[:,-1,:] = 1
+out_dat[:,:,0] = 1
+out_dat[:,:,-1] = 1
+
+#=========================================================
+#reshape the data to be column major
+out_dat = out_dat.reshape(out_dat.size, order = 'F')
+
+
+#output the transfer of out_dat to the file with integer type
+np.savetxt(output_name,out_dat.T,fmt='%d')
+
This file is the non-objective oriented version of singlephase solver without using class. +At the begining of the this file it define some variable first.
+#import some package
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+#initialize taichi with cpu, dunamic index, disable profiler and disables printing the intermediate representation
+ti.init(arch=ti.cpu, dynamic_index=True, kernel_profiler=False, print_ir=False)
+#enable projection
+enable_projection = True
+#nx,ny,nz = 100,50,5
+#define 131x131x131 and zero external force
+nx,ny,nz = 131,131,131
+fx,fy,fz = 0.0e-6,0.0,0.0
+#viscosity=0.1
+niu = 0.1
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 1, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 1, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+#define old density distribution funciton nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define new density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#define velocity nx*ny*nz
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#define lattice speed 3*19
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#define s diagonal 19 dimension vector
+S_dig = ti.field(ti.f32,shape=(19))
+#define another lattice speed 3*19
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#define weight parameter 19 dimesnion vector
+w = ti.field(ti.f32, shape=(19))
+#define solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#define vector for streaming 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+#define external force with a 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+#define velocity in x,y,z direction with 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#define transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#define inverse of transforming matrix
+inv_M = ti.field(ti.f32, shape=(19,19))
+#define single relaxation parameter
+tau_f=3.0*niu+0.5
+#define single relaxation frequency
+s_v=1.0/tau_f
+#define other parameter in the s diagonal
+s_other=8.0*(2.0-s_v)/(8.0-s_v)
+#define s matrix but not used
+S_np = np.zeros((19,19))
+S_np[0,0]=0; S_np[1,1]=s_v; S_np[2,2]=s_v; S_np[3,3]=0; S_np[4,4]=s_other; S_np[5,5]=0;
+S_np[6,6]=s_other; S_np[7,7]=0; S_np[8,8]=s_other; S_np[9,9]=s_v; S_np[10,10]=s_v; S_np[11,11]=s_v;
+S_np[12,12]=s_v; S_np[13,13]=s_v; S_np[14,14]=s_v; S_np[15,15]=s_v; S_np[16,16]=s_other; S_np[17,17]=s_other;
+S_np[18,18]=s_other
+#define numpy array version of s diagonal.
+S_dig_np = np.array([0,s_v,s_v,0,s_other,0,s_other,0,s_other, s_v, s_v,s_v,s_v,s_v,s_v,s_v,s_other,s_other,s_other])
+#define numpy version of transforming matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#define inverse of transforming matrix using inv function in linalg package
+inv_M_np = np.linalg.inv(M_np)
+#define index for streaming
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#assign numpy version to M.np to M
+M.from_numpy(M_np)
+#assign numpy version of inverser matrix inv_M_np to inv_M
+inv_M.from_numpy(inv_M_np)
+#assign numpy versio of LR array to LR
+LR.from_numpy(LR_np)
+#assign fx,fy,fz to vector external force
+ext_f[None] = ti.Vector([fx,fy,fz])
+#assign numpy version of S diagnal S_dig_np to S_dig
+S_dig.from_numpy(S_dig_np)
+#make inv_M,M,LR,S_dig not modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+ti.static(S_dig)
+
+#create mesh nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#numpy meshgrid from x,y,z 1d array to 3d array X,Y,Z here use ij indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local,u)
calculate the equilibrium density distribution function in velocity space
# taichi funciton
+@ti.func
+def feq(k,rho_local, u):
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
initialize velocity=0, density=1 and denisty distribution function= equilibrium density distribution function
@ti.kernel
+def init():
+ for i,j,k in rho:
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
load geometry file
def init_geo(filename):
+ #load data
+ in_dat = np.loadtxt(filename)
+ #reshape it with column major
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+ return in_dat
+
static_init()
initialize lattixe speed weight parameter and boundary velocity
@ti.kernel
+def static_init():
+if ti.static(enable_projection): # No runtime overhead
+ #initialize lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #initialize lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #intialize weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #intialize boundary velocity
+ bc_vel_x_left[None] = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right[None] = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left[None] = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right[None] = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left[None] = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right[None] = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M
calculate denisty distribution function in momentum space M*f=m
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in range(19):
+ for s in range(19):
+ #calculte m=M*f here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
GuoF(i,j,k,s,u)
calculate Guo’s Force scheme
@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in range(19):
+ #calculate Guo's force here
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
meq_vec(rho_local,u)
calculate equilibrium density distribution function in momentum space
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
collison()
define the prcoess of collision
@ti.kernel
+def colission():
+ for i,j,k in rho:
+ #if it is fluid
+ if (solid[i,j,k] == 0):
+ #calculate m
+ m_temp = multiply_M(i,j,k)
+ #calculate meq
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ for s in range(19):
+ #calculate -s*(m-meq)
+ m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #add Guo's force
+ m_temp[s] += (1-0.5*S_dig[s])*GuoF(i,j,k,s,v[i,j,k])
+
+ for s in range(19):
+ f[i,j,k,s] = 0
+ for l in range(19):
+ #f=-M^-1*S(m-meq)
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
periodic_index(i)
set the bounary index with periodic bounary condition
@ti.func
+def periodic_index(i):
+ #inner index
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = nx-1
+ #x-right
+ if i[0]>nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = ny-1
+ #y-right
+ if i[1]>ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = nz-1
+ #z-right
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming process of denisty distibution function
@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ #if it is fluid
+ if (solid[i] == 0):
+ for s in range(19):
+ #the neighbour index
+ ip = periodic_index(i+e[s])
+ #if neighbour index is fluid just streaming
+ if (solid[ip]==0):
+ F[ip,s] = f[i,s]
+ #if neighbour index is solid just bounce back
+ else:
+ F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+
streaming2()
a simple streaming process without consideration of solid and boundary
@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in range(19):
+ f[i,s] = F[i,s]
+
Boudary_condition()
define the bounary condition of fixed pressure and fixed velocity
@ti.kernel
+def Boundary_condition():
+ #pressure-boundary condtion x-left
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #if boundary is fluid but the neighbour is solid
+ #equilibrium density distribution function is calculated based on the neighbour velocity
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ #if boundary is fluid and the neighbour is also fluid
+ #equilibrium density distribution function is calculated based on the boundary velocity
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ #velocity-boundary conditon x-left
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #calculate density distribution fucntion based on equilibrium part and non-equilibrium part
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+
+ #pressure boundary condition x-right similar to x-left
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ #velocity booundary condition x-right similar to x-left
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+
streaming3()
calculate the macroscopic variable
@ti.kernel
+def streaming3():
+ for i in ti.grouped(rho):
+ #if it is fluid calculate density and velocity based on density distribution function
+ if (solid[i]==0):
+ rho[i] = 0
+ v[i] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,s] = F[i,s]
+ rho[i] += f[i,s]
+ v[i] += e_f[s]*f[i,s]
+
+ v[i] /= rho[i]
+ v[i] += (ext_f[None]/2)/rho[i]
+ # if it is solid set denisty equals one and velocity equals zero
+ else:
+ rho[i] = 1.0
+ v[i] = ti.Vector([0,0,0])
+
At the end of the file do the actual simulation and export the data
+#define some time varible
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+dt_count = 0
+
+#import the solid flag data
+#solid_np = init_geo('./BC.dat')
+solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+
+# do the initialization
+static_init()
+init()
+
+# do the actual simulation
+for iter in range(50000+1):
+ colission()
+ streaming1()
+ Boundary_condition()
+ #streaming2()
+ streaming3()
+ # calculate every 1000 time step
+ if (iter%1000==0):
+
+ time_pre = time_now
+ time_now = time.time()
+ #calculate the time difference between now and previous time step
+ diff_time = int(time_now-time_pre)
+ #calculate the time difference between now and the initial time
+ elap_time = int(time_now-time_init)
+ #divmod function return the quotient and the remainder
+ #so that h_diff,m_diff and s_diff represent the hour, minute and second. the same as the h_elap,m_elap and s_elap
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+
+ #export every 1000 timestep to vtk with x,y,z coordinate and solid,density and velocity variable
+ if (iter%10000==0):
+ gridToVTK(
+ "./structured"+str(iter),
+ x,
+ y,
+ z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(solid.to_numpy()),
+ "rho": np.ascontiguousarray(rho.to_numpy()),
+ "velocity": (np.ascontiguousarray(v.to_numpy()[:,:,:,0]), np.ascontiguousarray(v.to_numpy()[:,:,:,1]),np.ascontiguousarray(v.to_numpy()[:,:,:,2]))
+ }
+ )
+# ti.sync()
+# ti.profiler.print_kernel_profiler_info()
+#print the profiler information of every kernel and task of taichi in this file
+ti.profiler.print_scoped_profiler_info()
+
This solver is almost similar to lbm_solver_3d expect several difference as follows:
+The Grid resolution in this solver is 50x50x50
The viscosity in this solver is 0.16667
The boundary condition in this solver is velocity solver on x-right as follows
boundary condition of this solver
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 2, 1.0, 0.0, 0.0, 0.1 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
x-right is implementated with velocity boundary condition
+4. The boundary condition implementation is different from lbm_solver_3d, in this solver, the density distribution +function is calculated based on velocity on the boundary.
+if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ F[0,j,k][s]=feq(s,1.0,ti.Vector(bc_vel_x_left))
+
Finally, the definition of the varible is slightly different from lbm_solver_3d
This solver is almost similar to lbm_solver_3d expect the sparse definition of some varible:
+f = ti.field(ti.f32)
+F = ti.field(ti.f32)
+rho = ti.field(ti.f32)
+v = ti.Vector.field(3, ti.f32)
+n_mem_partition = 3
+
+cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(v)
+
+cell2 = ti.root.pointer(ti.ijkl,(nx//3+1,ny//3+1,nz//3+1,1))
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(f)
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(F)
+
It use a pointer and certain block to divide the region and then place different varible on the block which make the storage +sparse.
+This file reads the stl file and output to vtk file
+This file output geometry data
+#import numpy and math packahe
+import numpy as np
+import math
+
+
+# INPUT STL FILE NAME
+output_name = 'geo.dat'
+
+# POINT SEARCHING RESOLUTION IN X direction, Y,Z direction will be calculate by the code
+# the bigger value ~ more points will be found inside STL
+dnx, dny, dnz = 60, 60, 60
+
+
+#==========================================================
+# DO NOT CHANGE BELOW
+#==========================================================
+#create np matrix with dnx*dny*dnz zero
+out_dat = np.zeros((dnx,dny,dnz))
+
+#=======Can define some geometry here to out_dat=========
+#out_dat[1,:,:] = 1
+
+#=========================================================
+#reshape out_dat with column major
+out_dat = out_dat.reshape(out_dat.size, order = 'F')
+#save the file with the transfer of out_dat based on integer type
+np.savetxt(output_name,out_dat.T,fmt='%d')
+
This solver is almost similar to lbm_solver_3d expect several difference as follows:
+Some parameter is different
#grid resolution
+nx,ny,nz = 60,50,5
+#external force
+fx,fy,fz = 1.0e-6,0.0,0.0
+#viscosity
+niu = 0.1
+#import geometry
+geo_name = './BC.dat'
+#maximum timestep
+max_timestep = 5000
+#output frequency
+output_fre = 100
+#vtk file output frequency
+vtk_fre = 500
+
There are two array for solid flag data.
ns_np = init_geo(geo_name)
+solid_np = ns_np.astype(int)
+#solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+ns.from_numpy(ns_np)
+
The streaming function is different
@ti.kernel
+def streaming0():
+ for i in ti.grouped(rho):
+ if (solid[i] == 0):
+ for s in ti.static(range(19)):
+ ip = periodic_index(i+e[s])
+ #if it is fluid f2=f otherwise apply bounce-back f2[i,s]=f[ip,LR[s]]
+ f2[i,s] = f[i,s] + ns[i]*(f[ip,LR[s]] - f[i,s])
+
+
+@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ if (solid[i] == 0):
+ #if it is fluid apply streaming
+ for s in ti.static(range(19)):
+ ip = periodic_index(i+e[s])
+ F[ip,s] = f2[i,s]
+
+ #if (solid[ip]==0):
+ # F[ip,s] = f[i,s]
+ #else:
+ # F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+#not used
+@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in ti.static(range(19)):
+ f[i,s] = F[i,s]
+
streaming3()
calculates the macroscopic variable
This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This file is the non-objective oriented version of singlephase solver without using class. +At the begining of the this file it define some variable first.
+#import some package
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+#initialize taichi with cpu, dunamic index, disable profiler and disables printing the intermediate representation
+ti.init(arch=ti.cpu, dynamic_index=True, kernel_profiler=False, print_ir=False)
+#enable projection
+enable_projection = True
+#nx,ny,nz = 100,50,5
+#define 131x131x131 and zero external force
+nx,ny,nz = 131,131,131
+fx,fy,fz = 0.0e-6,0.0,0.0
+#viscosity=0.1
+niu = 0.1
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 1, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 1, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+#define old density distribution funciton nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define new density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#define velocity nx*ny*nz
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#define lattice speed 3*19
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#define s diagonal 19 dimension vector
+S_dig = ti.field(ti.f32,shape=(19))
+#define another lattice speed 3*19
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#define weight parameter 19 dimesnion vector
+w = ti.field(ti.f32, shape=(19))
+#define solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#define vector for streaming 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+#define external force with a 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+#define velocity in x,y,z direction with 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#define transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#define inverse of transforming matrix
+inv_M = ti.field(ti.f32, shape=(19,19))
+#define single relaxation parameter
+tau_f=3.0*niu+0.5
+#define single relaxation frequency
+s_v=1.0/tau_f
+#define other parameter in the s diagonal
+s_other=8.0*(2.0-s_v)/(8.0-s_v)
+#define s matrix but not used
+S_np = np.zeros((19,19))
+S_np[0,0]=0; S_np[1,1]=s_v; S_np[2,2]=s_v; S_np[3,3]=0; S_np[4,4]=s_other; S_np[5,5]=0;
+S_np[6,6]=s_other; S_np[7,7]=0; S_np[8,8]=s_other; S_np[9,9]=s_v; S_np[10,10]=s_v; S_np[11,11]=s_v;
+S_np[12,12]=s_v; S_np[13,13]=s_v; S_np[14,14]=s_v; S_np[15,15]=s_v; S_np[16,16]=s_other; S_np[17,17]=s_other;
+S_np[18,18]=s_other
+#define numpy array version of s diagonal.
+S_dig_np = np.array([0,s_v,s_v,0,s_other,0,s_other,0,s_other, s_v, s_v,s_v,s_v,s_v,s_v,s_v,s_other,s_other,s_other])
+#define numpy version of transforming matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#define inverse of transforming matrix using inv function in linalg package
+inv_M_np = np.linalg.inv(M_np)
+#define index for streaming
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#assign numpy version to M.np to M
+M.from_numpy(M_np)
+#assign numpy version of inverser matrix inv_M_np to inv_M
+inv_M.from_numpy(inv_M_np)
+#assign numpy versio of LR array to LR
+LR.from_numpy(LR_np)
+#assign fx,fy,fz to vector external force
+ext_f[None] = ti.Vector([fx,fy,fz])
+#assign numpy version of S diagnal S_dig_np to S_dig
+S_dig.from_numpy(S_dig_np)
+#make inv_M,M,LR,S_dig not modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+ti.static(S_dig)
+
+#create mesh nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#numpy meshgrid from x,y,z 1d array to 3d array X,Y,Z here use ij indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local,u)
calculate the equilibrium density distribution function in velocity space
# taichi funciton
+@ti.func
+def feq(k,rho_local, u):
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
initialize velocity=0, density=1 and denisty distribution function= equilibrium density distribution function
@ti.kernel
+def init():
+ for i,j,k in rho:
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
load geometry file
def init_geo(filename):
+ #load data
+ in_dat = np.loadtxt(filename)
+ #reshape it with column major
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+ return in_dat
+
static_init()
initialize lattixe speed weight parameter and boundary velocity
@ti.kernel
+def static_init():
+if ti.static(enable_projection): # No runtime overhead
+ #initialize lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #initialize lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #intialize weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #intialize boundary velocity
+ bc_vel_x_left[None] = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right[None] = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left[None] = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right[None] = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left[None] = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right[None] = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M
calculate denisty distribution function in momentum space M*f=m
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in range(19):
+ for s in range(19):
+ #calculte m=M*f here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
this
+@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in range(19):
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
this
+@ti.kernel
+def colission():
+ for i,j,k in rho:
+ if (solid[i,j,k] == 0):
+ m_temp = multiply_M(i,j,k)
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ for s in range(19):
+ m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ m_temp[s] += (1-0.5*S_dig[s])*GuoF(i,j,k,s,v[i,j,k])
+
+ for s in range(19):
+ f[i,j,k,s] = 0
+ for l in range(19):
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
this
+@ti.func
+def periodic_index(i):
+ iout = i
+ if i[0]<0: iout[0] = nx-1
+ if i[0]>nx-1: iout[0] = 0
+ if i[1]<0: iout[1] = ny-1
+ if i[1]>ny-1: iout[1] = 0
+ if i[2]<0: iout[2] = nz-1
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
this
+@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ if (solid[i] == 0):
+ for s in range(19):
+ ip = periodic_index(i+e[s])
+ if (solid[ip]==0):
+ F[ip,s] = f[i,s]
+ else:
+ F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+
this
+@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in range(19):
+ f[i,s] = F[i,s]
+
this
+@ti.kernel
+def Boundary_condition():
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+
this
+@ti.kernel
+def streaming3():
+ for i in ti.grouped(rho):
+ if (solid[i]==0):
+ rho[i] = 0
+ v[i] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,s] = F[i,s]
+ rho[i] += f[i,s]
+ v[i] += e_f[s]*f[i,s]
+
+ v[i] /= rho[i]
+ v[i] += (ext_f[None]/2)/rho[i]
+
+ else:
+ rho[i] = 1.0
+ v[i] = ti.Vector([0,0,0])
+
this
+This file reads the stl file and output to vtk file
+This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This file use the LBM_3D_SinglePhase_Solver to simulate the cavity flow
+#import certain packages
+import time
+import taichi as ti
+
+ti.init(arch=ti.cpu, dynamic_index=False, kernel_profiler=False, print_ir=False)
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#set 50*50*50 cavity based on LB3D_Solver_Single_Phase solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=50,ny=50,nz=50, sparse_storage=False)
+
+#import geometry data
+lb3d.init_geo('./geo_cavity.dat')
+#set the x-right velocity
+lb3d.set_bc_vel_x1([0.0,0.0,0.1])
+#initialize
+lb3d.init_simulation()
+
+#simulation step
+for iter in range(2000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #get the maximum velocity
+ max_v = lb3d.get_max_v()
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the number of time steps, maxiumum force and the force scale=0
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, max_v, 0.0))
+ #every 1000 time steps export the vtk file
+ if (iter%1000==0):
+ lb3d.export_VTK(iter)
+
This file simulate the porous medium based on the LBM_3D_SinglePhase_Solver
+#import time and taichi package
+import time
+import taichi as ti
+#taichi intialization
+ti.init(arch=ti.cpu)
+#import the LBM_3D_SinglePhase_Solver
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#create the 131*131*131 gird LBM_3D_SinglePhase_Solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=131,ny=131,nz=131)
+#import the porous medium geometry
+lb3d.init_geo('./img_ftb131.txt')
+#set x-left and x-right density
+lb3d.set_bc_rho_x1(0.99)
+lb3d.set_bc_rho_x0(1.0)
+#initialize the simulation
+lb3d.init_simulation()
+#simulation loop
+for iter in range(50000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the time step, max force=10, force_scale=10
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+ #export VTK every 2000 time step
+ if (iter%2000==0):
+ lb3d.export_VTK(iter)
+
This file generate geometry file for solver to read
+#import certain module
+import numpy as np
+import math
+
+
+#define the input file name
+# INPUT STL FILE NAME
+output_name = 'geo_cavity.dat'
+
+#define the grid resolution
+dnx, dny, dnz = 50, 50, 50
+
+#==========================================================
+# DO NOT CHANGE BELOW
+#==========================================================
+
+#define an matrix dnx*dny*dnz with zero values
+out_dat = np.zeros((dnx,dny,dnz))
+
+#=======Can define some geometry here to out_dat=========
+#define the boundary to be solid
+out_dat[0,:,:] = 1
+#cout_dat[:,:,0] = 1
+out_dat[:,0,:] = 1
+out_dat[:,-1,:] = 1
+out_dat[:,:,0] = 1
+out_dat[:,:,-1] = 1
+
+#=========================================================
+#reshape the data to be column major
+out_dat = out_dat.reshape(out_dat.size, order = 'F')
+
+
+#output the transfer of out_dat to the file with integer type
+np.savetxt(output_name,out_dat.T,fmt='%d')
+
This file is the non-objective oriented version of singlephase solver without using class. +At the begining of the this file it define some variable first.
+#import some package
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+#initialize taichi with cpu, dunamic index, disable profiler and disables printing the intermediate representation
+ti.init(arch=ti.cpu, dynamic_index=True, kernel_profiler=False, print_ir=False)
+#enable projection
+enable_projection = True
+#nx,ny,nz = 100,50,5
+#define 131x131x131 and zero external force
+nx,ny,nz = 131,131,131
+fx,fy,fz = 0.0e-6,0.0,0.0
+#viscosity=0.1
+niu = 0.1
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 1, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 1, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+#define old density distribution funciton nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define new density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#define velocity nx*ny*nz
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#define lattice speed 3*19
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#define s diagonal 19 dimension vector
+S_dig = ti.field(ti.f32,shape=(19))
+#define another lattice speed 3*19
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#define weight parameter 19 dimesnion vector
+w = ti.field(ti.f32, shape=(19))
+#define solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#define vector for streaming 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+#define external force with a 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+#define velocity in x,y,z direction with 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#define transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#define inverse of transforming matrix
+inv_M = ti.field(ti.f32, shape=(19,19))
+#define single relaxation parameter
+tau_f=3.0*niu+0.5
+#define single relaxation frequency
+s_v=1.0/tau_f
+#define other parameter in the s diagonal
+s_other=8.0*(2.0-s_v)/(8.0-s_v)
+#define s matrix but not used
+S_np = np.zeros((19,19))
+S_np[0,0]=0; S_np[1,1]=s_v; S_np[2,2]=s_v; S_np[3,3]=0; S_np[4,4]=s_other; S_np[5,5]=0;
+S_np[6,6]=s_other; S_np[7,7]=0; S_np[8,8]=s_other; S_np[9,9]=s_v; S_np[10,10]=s_v; S_np[11,11]=s_v;
+S_np[12,12]=s_v; S_np[13,13]=s_v; S_np[14,14]=s_v; S_np[15,15]=s_v; S_np[16,16]=s_other; S_np[17,17]=s_other;
+S_np[18,18]=s_other
+#define numpy array version of s diagonal.
+S_dig_np = np.array([0,s_v,s_v,0,s_other,0,s_other,0,s_other, s_v, s_v,s_v,s_v,s_v,s_v,s_v,s_other,s_other,s_other])
+#define numpy version of transforming matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#define inverse of transforming matrix using inv function in linalg package
+inv_M_np = np.linalg.inv(M_np)
+#define index for streaming
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#assign numpy version to M.np to M
+M.from_numpy(M_np)
+#assign numpy version of inverser matrix inv_M_np to inv_M
+inv_M.from_numpy(inv_M_np)
+#assign numpy versio of LR array to LR
+LR.from_numpy(LR_np)
+#assign fx,fy,fz to vector external force
+ext_f[None] = ti.Vector([fx,fy,fz])
+#assign numpy version of S diagnal S_dig_np to S_dig
+S_dig.from_numpy(S_dig_np)
+#make inv_M,M,LR,S_dig not modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+ti.static(S_dig)
+
+#create mesh nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#numpy meshgrid from x,y,z 1d array to 3d array X,Y,Z here use ij indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local,u)
calculate the equilibrium density distribution function in velocity space
# taichi funciton
+@ti.func
+def feq(k,rho_local, u):
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
initialize velocity=0, density=1 and denisty distribution function= equilibrium density distribution function
@ti.kernel
+def init():
+ for i,j,k in rho:
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
load geometry file
def init_geo(filename):
+ #load data
+ in_dat = np.loadtxt(filename)
+ #reshape it with column major
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+ return in_dat
+
static_init()
initialize lattixe speed weight parameter and boundary velocity
@ti.kernel
+def static_init():
+if ti.static(enable_projection): # No runtime overhead
+ #initialize lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #initialize lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #intialize weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #intialize boundary velocity
+ bc_vel_x_left[None] = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right[None] = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left[None] = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right[None] = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left[None] = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right[None] = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M
calculate denisty distribution function in momentum space M*f=m
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in range(19):
+ for s in range(19):
+ #calculte m=M*f here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
GuoF(i,j,k,s,u)
calculate Guo’s Force scheme
@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in range(19):
+ #calculate Guo's force here
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
meq_vec(rho_local,u)
calculate equilibrium density distribution function in momentum space
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
collison()
define the prcoess of collision
@ti.kernel
+def colission():
+ for i,j,k in rho:
+ #if it is fluid
+ if (solid[i,j,k] == 0):
+ #calculate m
+ m_temp = multiply_M(i,j,k)
+ #calculate meq
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ for s in range(19):
+ #calculate -s*(m-meq)
+ m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #add Guo's force
+ m_temp[s] += (1-0.5*S_dig[s])*GuoF(i,j,k,s,v[i,j,k])
+
+ for s in range(19):
+ f[i,j,k,s] = 0
+ for l in range(19):
+ #f=-M^-1*S(m-meq)
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
periodic_index(i)
set the bounary index with periodic bounary condition
@ti.func
+def periodic_index(i):
+ #inner index
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = nx-1
+ #x-right
+ if i[0]>nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = ny-1
+ #y-right
+ if i[1]>ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = nz-1
+ #z-right
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming process of denisty distibution function
@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ #if it is fluid
+ if (solid[i] == 0):
+ for s in range(19):
+ #the neighbour index
+ ip = periodic_index(i+e[s])
+ #if neighbour index is fluid just streaming
+ if (solid[ip]==0):
+ F[ip,s] = f[i,s]
+ #if neighbour index is solid just bounce back
+ else:
+ F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+
streaming2()
a simple streaming process without consideration of solid and boundary
@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in range(19):
+ f[i,s] = F[i,s]
+
Boudary_condition()
define the bounary condition of fixed pressure and fixed velocity
@ti.kernel
+def Boundary_condition():
+ #pressure-boundary condtion x-left
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #if boundary is fluid but the neighbour is solid
+ #equilibrium density distribution function is calculated based on the neighbour velocity
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ #if boundary is fluid and the neighbour is also fluid
+ #equilibrium density distribution function is calculated based on the boundary velocity
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ #velocity-boundary conditon x-left
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #calculate density distribution fucntion based on equilibrium part and non-equilibrium part
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+
+ #pressure boundary condition x-right similar to x-left
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ #velocity booundary condition x-right similar to x-left
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+
streaming3()
calculate the macroscopic variable
@ti.kernel
+def streaming3():
+ for i in ti.grouped(rho):
+ #if it is fluid calculate density and velocity based on density distribution function
+ if (solid[i]==0):
+ rho[i] = 0
+ v[i] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,s] = F[i,s]
+ rho[i] += f[i,s]
+ v[i] += e_f[s]*f[i,s]
+
+ v[i] /= rho[i]
+ v[i] += (ext_f[None]/2)/rho[i]
+ # if it is solid set denisty equals one and velocity equals zero
+ else:
+ rho[i] = 1.0
+ v[i] = ti.Vector([0,0,0])
+
At the end of the file do the actual simulation and export the data
+#define some time varible
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+dt_count = 0
+
+#import the solid flag data
+#solid_np = init_geo('./BC.dat')
+solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+
+# do the initialization
+static_init()
+init()
+
+# do the actual simulation
+for iter in range(50000+1):
+ colission()
+ streaming1()
+ Boundary_condition()
+ #streaming2()
+ streaming3()
+ # calculate every 1000 time step
+ if (iter%1000==0):
+
+ time_pre = time_now
+ time_now = time.time()
+ #calculate the time difference between now and previous time step
+ diff_time = int(time_now-time_pre)
+ #calculate the time difference between now and the initial time
+ elap_time = int(time_now-time_init)
+ #divmod function return the quotient and the remainder
+ #so that h_diff,m_diff and s_diff represent the hour, minute and second. the same as the h_elap,m_elap and s_elap
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+
+ #export every 1000 timestep to vtk with x,y,z coordinate and solid,density and velocity variable
+ if (iter%10000==0):
+ gridToVTK(
+ "./structured"+str(iter),
+ x,
+ y,
+ z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(solid.to_numpy()),
+ "rho": np.ascontiguousarray(rho.to_numpy()),
+ "velocity": (np.ascontiguousarray(v.to_numpy()[:,:,:,0]), np.ascontiguousarray(v.to_numpy()[:,:,:,1]),np.ascontiguousarray(v.to_numpy()[:,:,:,2]))
+ }
+ )
+# ti.sync()
+# ti.profiler.print_kernel_profiler_info()
+#print the profiler information of every kernel and task of taichi in this file
+ti.profiler.print_scoped_profiler_info()
+
This solver is almost similar to lbm_solver_3d expect several difference as follows:
+The Grid resolution in this solver is 50x50x50
The viscosity in this solver is 0.16667
The boundary condition in this solver is velocity solver on x-right as follows
boundary condition of this solver
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 2, 1.0, 0.0, 0.0, 0.1 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
x-right is implementated with velocity boundary condition
+4. The boundary condition implementation is different from lbm_solver_3d, in this solver, the density distribution +function is calculated based on velocity on the boundary.
+if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ F[0,j,k][s]=feq(s,1.0,ti.Vector(bc_vel_x_left))
+
Finally, the definition of the varible is slightly different from lbm_solver_3d
This solver is almost similar to lbm_solver_3d expect the sparse definition of some varible:
+f = ti.field(ti.f32)
+F = ti.field(ti.f32)
+rho = ti.field(ti.f32)
+v = ti.Vector.field(3, ti.f32)
+n_mem_partition = 3
+
+cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(v)
+
+cell2 = ti.root.pointer(ti.ijkl,(nx//3+1,ny//3+1,nz//3+1,1))
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(f)
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(F)
+
It use a pointer and certain block to divide the region and then place different varible on the block which make the storage +sparse.
+' + + '' + + _("Hide Search Matches") + + "
" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(SphinxHighlight.highlightSearchWords); +_ready(SphinxHighlight.initEscapeListener); diff --git a/docs/_build/html/aaa.html b/docs/_build/html/aaa.html new file mode 100644 index 0000000..2a18f1c --- /dev/null +++ b/docs/_build/html/aaa.html @@ -0,0 +1,644 @@ + + + + + + +This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This file use the LBM_3D_SinglePhase_Solver to simulate the cavity flow
+#import certain packages
+import time
+import taichi as ti
+
+ti.init(arch=ti.cpu, dynamic_index=False, kernel_profiler=False, print_ir=False)
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#set 50*50*50 cavity based on LB3D_Solver_Single_Phase solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=50,ny=50,nz=50, sparse_storage=False)
+
+#import geometry data
+lb3d.init_geo('./geo_cavity.dat')
+#set the x-right velocity
+lb3d.set_bc_vel_x1([0.0,0.0,0.1])
+#initialize
+lb3d.init_simulation()
+
+#simulation step
+for iter in range(2000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #get the maximum velocity
+ max_v = lb3d.get_max_v()
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the number of time steps, maxiumum force and the force scale=0
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, max_v, 0.0))
+ #every 1000 time steps export the vtk file
+ if (iter%1000==0):
+ lb3d.export_VTK(iter)
+
This file simulate the porous medium based on the LBM_3D_SinglePhase_Solver
+#import time and taichi package
+import time
+import taichi as ti
+#taichi intialization
+ti.init(arch=ti.cpu)
+#import the LBM_3D_SinglePhase_Solver
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#create the 131*131*131 gird LBM_3D_SinglePhase_Solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=131,ny=131,nz=131)
+#import the porous medium geometry
+lb3d.init_geo('./img_ftb131.txt')
+#set x-left and x-right density
+lb3d.set_bc_rho_x1(0.99)
+lb3d.set_bc_rho_x0(1.0)
+#initialize the simulation
+lb3d.init_simulation()
+#simulation loop
+for iter in range(50000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the time step, max force=10, force_scale=10
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+ #export VTK every 2000 time step
+ if (iter%2000==0):
+ lb3d.export_VTK(iter)
+
This file generate geometry file for solver to read
+#import certain module
+import numpy as np
+import math
+
+
+#define the input file name
+# INPUT STL FILE NAME
+output_name = 'geo_cavity.dat'
+
+#define the grid resolution
+dnx, dny, dnz = 50, 50, 50
+
+#==========================================================
+# DO NOT CHANGE BELOW
+#==========================================================
+
+#define an matrix dnx*dny*dnz with zero values
+out_dat = np.zeros((dnx,dny,dnz))
+
+#=======Can define some geometry here to out_dat=========
+#define the boundary to be solid
+out_dat[0,:,:] = 1
+#cout_dat[:,:,0] = 1
+out_dat[:,0,:] = 1
+out_dat[:,-1,:] = 1
+out_dat[:,:,0] = 1
+out_dat[:,:,-1] = 1
+
+#=========================================================
+#reshape the data to be column major
+out_dat = out_dat.reshape(out_dat.size, order = 'F')
+
+
+#output the transfer of out_dat to the file with integer type
+np.savetxt(output_name,out_dat.T,fmt='%d')
+
This file is the non-objective oriented version of singlephase solver without using class. +At the begining of the this file it define some variable first.
+#import some package
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+#initialize taichi with cpu, dunamic index, disable profiler and disables printing the intermediate representation
+ti.init(arch=ti.cpu, dynamic_index=True, kernel_profiler=False, print_ir=False)
+#enable projection
+enable_projection = True
+#nx,ny,nz = 100,50,5
+#define 131x131x131 and zero external force
+nx,ny,nz = 131,131,131
+fx,fy,fz = 0.0e-6,0.0,0.0
+#viscosity=0.1
+niu = 0.1
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 1, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 1, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+#define old density distribution funciton nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define new density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#define velocity nx*ny*nz
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#define lattice speed 3*19
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#define s diagonal 19 dimension vector
+S_dig = ti.field(ti.f32,shape=(19))
+#define another lattice speed 3*19
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#define weight parameter 19 dimesnion vector
+w = ti.field(ti.f32, shape=(19))
+#define solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#define vector for streaming 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+#define external force with a 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+#define velocity in x,y,z direction with 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#define transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#define inverse of transforming matrix
+inv_M = ti.field(ti.f32, shape=(19,19))
+#define single relaxation parameter
+tau_f=3.0*niu+0.5
+#define single relaxation frequency
+s_v=1.0/tau_f
+#define other parameter in the s diagonal
+s_other=8.0*(2.0-s_v)/(8.0-s_v)
+#define s matrix but not used
+S_np = np.zeros((19,19))
+S_np[0,0]=0; S_np[1,1]=s_v; S_np[2,2]=s_v; S_np[3,3]=0; S_np[4,4]=s_other; S_np[5,5]=0;
+S_np[6,6]=s_other; S_np[7,7]=0; S_np[8,8]=s_other; S_np[9,9]=s_v; S_np[10,10]=s_v; S_np[11,11]=s_v;
+S_np[12,12]=s_v; S_np[13,13]=s_v; S_np[14,14]=s_v; S_np[15,15]=s_v; S_np[16,16]=s_other; S_np[17,17]=s_other;
+S_np[18,18]=s_other
+#define numpy array version of s diagonal.
+S_dig_np = np.array([0,s_v,s_v,0,s_other,0,s_other,0,s_other, s_v, s_v,s_v,s_v,s_v,s_v,s_v,s_other,s_other,s_other])
+#define numpy version of transforming matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#define inverse of transforming matrix using inv function in linalg package
+inv_M_np = np.linalg.inv(M_np)
+#define index for streaming
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#assign numpy version to M.np to M
+M.from_numpy(M_np)
+#assign numpy version of inverser matrix inv_M_np to inv_M
+inv_M.from_numpy(inv_M_np)
+#assign numpy versio of LR array to LR
+LR.from_numpy(LR_np)
+#assign fx,fy,fz to vector external force
+ext_f[None] = ti.Vector([fx,fy,fz])
+#assign numpy version of S diagnal S_dig_np to S_dig
+S_dig.from_numpy(S_dig_np)
+#make inv_M,M,LR,S_dig not modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+ti.static(S_dig)
+
+#create mesh nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#numpy meshgrid from x,y,z 1d array to 3d array X,Y,Z here use ij indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local,u)
calculate the equilibrium density distribution function in velocity space
# taichi funciton
+@ti.func
+def feq(k,rho_local, u):
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
initialize velocity=0, density=1 and denisty distribution function= equilibrium density distribution function
@ti.kernel
+def init():
+ for i,j,k in rho:
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
load geometry file
def init_geo(filename):
+ #load data
+ in_dat = np.loadtxt(filename)
+ #reshape it with column major
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+ return in_dat
+
static_init()
initialize lattixe speed weight parameter and boundary velocity
@ti.kernel
+def static_init():
+if ti.static(enable_projection): # No runtime overhead
+ #initialize lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #initialize lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #intialize weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #intialize boundary velocity
+ bc_vel_x_left[None] = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right[None] = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left[None] = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right[None] = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left[None] = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right[None] = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M
calculate denisty distribution function in momentum space M*f=m
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in range(19):
+ for s in range(19):
+ #calculte m=M*f here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
GuoF(i,j,k,s,u)
calculate Guo’s Force scheme
@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in range(19):
+ #calculate Guo's force here
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
meq_vec(rho_local,u)
calculate equilibrium density distribution function in momentum space
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
collison()
define the prcoess of collision
@ti.kernel
+def colission():
+ for i,j,k in rho:
+ #if it is fluid
+ if (solid[i,j,k] == 0):
+ #calculate m
+ m_temp = multiply_M(i,j,k)
+ #calculate meq
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ for s in range(19):
+ #calculate -s*(m-meq)
+ m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #add Guo's force
+ m_temp[s] += (1-0.5*S_dig[s])*GuoF(i,j,k,s,v[i,j,k])
+
+ for s in range(19):
+ f[i,j,k,s] = 0
+ for l in range(19):
+ #f=-M^-1*S(m-meq)
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
periodic_index(i)
set the bounary index with periodic bounary condition
@ti.func
+def periodic_index(i):
+ #inner index
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = nx-1
+ #x-right
+ if i[0]>nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = ny-1
+ #y-right
+ if i[1]>ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = nz-1
+ #z-right
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming process of denisty distibution function
@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ #if it is fluid
+ if (solid[i] == 0):
+ for s in range(19):
+ #the neighbour index
+ ip = periodic_index(i+e[s])
+ #if neighbour index is fluid just streaming
+ if (solid[ip]==0):
+ F[ip,s] = f[i,s]
+ #if neighbour index is solid just bounce back
+ else:
+ F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+
streaming2()
a simple streaming process without consideration of solid and boundary
@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in range(19):
+ f[i,s] = F[i,s]
+
Boudary_condition()
define the bounary condition of fixed pressure and fixed velocity
@ti.kernel
+def Boundary_condition():
+ #pressure-boundary condtion x-left
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #if boundary is fluid but the neighbour is solid
+ #equilibrium density distribution function is calculated based on the neighbour velocity
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ #if boundary is fluid and the neighbour is also fluid
+ #equilibrium density distribution function is calculated based on the boundary velocity
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ #velocity-boundary conditon x-left
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #calculate density distribution fucntion based on equilibrium part and non-equilibrium part
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+
+ #pressure boundary condition x-right similar to x-left
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ #velocity booundary condition x-right similar to x-left
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+
streaming3()
calculate the macroscopic variable
@ti.kernel
+def streaming3():
+ for i in ti.grouped(rho):
+ #if it is fluid calculate density and velocity based on density distribution function
+ if (solid[i]==0):
+ rho[i] = 0
+ v[i] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,s] = F[i,s]
+ rho[i] += f[i,s]
+ v[i] += e_f[s]*f[i,s]
+
+ v[i] /= rho[i]
+ v[i] += (ext_f[None]/2)/rho[i]
+ # if it is solid set denisty equals one and velocity equals zero
+ else:
+ rho[i] = 1.0
+ v[i] = ti.Vector([0,0,0])
+
At the end of the file do the actual simulation and export the data
+#define some time varible
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+dt_count = 0
+
+#import the solid flag data
+#solid_np = init_geo('./BC.dat')
+solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+
+# do the initialization
+static_init()
+init()
+
+# do the actual simulation
+for iter in range(50000+1):
+ colission()
+ streaming1()
+ Boundary_condition()
+ #streaming2()
+ streaming3()
+ # calculate every 1000 time step
+ if (iter%1000==0):
+
+ time_pre = time_now
+ time_now = time.time()
+ #calculate the time difference between now and previous time step
+ diff_time = int(time_now-time_pre)
+ #calculate the time difference between now and the initial time
+ elap_time = int(time_now-time_init)
+ #divmod function return the quotient and the remainder
+ #so that h_diff,m_diff and s_diff represent the hour, minute and second. the same as the h_elap,m_elap and s_elap
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+
+ #export every 1000 timestep to vtk with x,y,z coordinate and solid,density and velocity variable
+ if (iter%10000==0):
+ gridToVTK(
+ "./structured"+str(iter),
+ x,
+ y,
+ z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(solid.to_numpy()),
+ "rho": np.ascontiguousarray(rho.to_numpy()),
+ "velocity": (np.ascontiguousarray(v.to_numpy()[:,:,:,0]), np.ascontiguousarray(v.to_numpy()[:,:,:,1]),np.ascontiguousarray(v.to_numpy()[:,:,:,2]))
+ }
+ )
+# ti.sync()
+# ti.profiler.print_kernel_profiler_info()
+#print the profiler information of every kernel and task of taichi in this file
+ti.profiler.print_scoped_profiler_info()
+
This solver is almost similar to lbm_solver_3d expect several difference as follows: +.. number:: lbm_solver_3d_cavity
+The Grid resolution in this solver is 50x50x50
The viscosity in this solver is 0.16667
The boundary condition in this solver is velocity solver on x-right as follows
boundary condition of this solver
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 2, 1.0, 0.0, 0.0, 0.1 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
x-right is implementated with velocity boundary condition
+4. The boundary condition implementation is different from lbm_solver_3d, in this solver, the density distribution +function is calculated based on velocity on the boundary.
+if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ F[0,j,k][s]=feq(s,1.0,ti.Vector(bc_vel_x_left))
+
Finally, the definition of the varible is slightly different from lbm_solver_3d
This solver is almost similar to lbm_solver_3d expect the sparse definition of some varible:
+f = ti.field(ti.f32)
+F = ti.field(ti.f32)
+rho = ti.field(ti.f32)
+v = ti.Vector.field(3, ti.f32)
+n_mem_partition = 3
+
+cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(v)
+
+cell2 = ti.root.pointer(ti.ijkl,(nx//3+1,ny//3+1,nz//3+1,1))
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(f)
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(F)
+
It use a pointer and certain block to divide the region and then place different varible on the block which make the storage +sparse.
+This file is the solver for solute transportation
+First import the certain package and define the class of LB3D_Solver_Single_Phase_Solute
which inheritant from
+LB3D_Solver_Single_Phase_Solute
from sympy import inverse_mellin_transform
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+
+#ti.init(arch=ti.cpu, dynamic_index=False, kernel_profiler=False, print_ir=False)
+import LBM_3D_SinglePhase_Solver as lb3d
+
+@ti.data_oriented
+class LB3D_Solver_Single_Phase_Solute(lb3d.LB3D_Solver_Single_Phase):
+ def __init__(self, nx, ny, nz):
+ super(LB3D_Solver_Single_Phase_Solute, self).__init__(nx, ny, nz, sparse_storage = False)
+ #define solute boundary condition
+ self.solute_bc_x_left, self.solute_bcxl = 0, 0.0
+ self.solute_bc_x_right, self.solute_bcxr = 0, 0.0
+ self.solute_bc_y_left, self.solute_bcyl = 0, 0.0
+ self.solute_bc_y_right, self.solute_bcyr = 0, 0.0
+ self.solute_bc_z_left, self.solute_bczl = 0, 0.0
+ self.solute_bc_z_right, self.solute_bczr = 0, 0.0
+
+ #define parameters for bouyancy force
+ self.buoyancy_parameter = 20.0 #Buoyancy Parameter (0= no buoyancy)
+ self.ref_T = 20.0 #reference_psi F=/rho*g+Bouyancy*(/psi-reference_psi)*g)
+ #define gravity
+ self.gravity = 5e-7
+
+ #define concentration distribution function
+ self.fg = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define another concentration distribution function
+ self.Fg = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define external force
+ self.forcexyz = ti.Vector.field(3,ti.f32,shape=(nx,ny,nz))
+ #define entropy
+ self.rho_H = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define temperature
+ self.rho_T = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define liquid volumn fraction
+ self.rho_fl = ti.field(ti.f32, shape=(nx,ny,nz))
+
+ #define specific heat of liquid
+ self.Cp_l= 1.0
+ #define specific heat of solid
+ self.Cp_s = 1.0
+ #define latent heat
+ self.Lt = 1.0
+ #define solid temperature
+ self.T_s = -10.0
+ #define liquid temperature
+ self.T_l = -10.0
+ #define viscosity of solid
+ self.niu_s = 0.002
+ #define viscosity of liquid
+ self.niu_l = 0.002
+
+ #define energy of solid
+ self.H_s = None
+ #define energy of liquid
+ self.H_l = None
+
+ #define rock thermal diffusivity
+ self.niu_solid = 0.001
+ #define specific heat of rock
+ self.Cp_solid = 1.0
+
An then it sets these parameters with functions
+#set gravity
+def set_gravity(self, gravity):
+self.gravity = gravity
+#set buoyancy force parameter
+def set_buoyancy_parameter(self, buoyancy_param):
+ self.buoyancy_parameter = buoyancy_param
+#set reference temperature
+def set_ref_T(self, ref_t):
+ self.ref_T = ref_t
+#set specific heat of solid
+def set_specific_heat_solid(self, cps):
+ self.Cp_s = cps
+#set specfic heat of liquid
+def set_specific_heat_liquid(self, cpl):
+ self.Cp_l = cpl
+#set specfic heat of rock
+def set_specific_heat_rock(self, cprock):
+ self.Cp_solid = cprock
+#set latent heat
+def set_latent_heat(self, ltheat):
+ self.Lt = ltheat
+#set solidus temperature
+def set_solidus_temperature(self, ts):
+ self.T_s = ts
+#set liquidus temperature
+def set_liquidus_temperature(self, tl):
+ self.T_l = tl
+#set solid thermal diffusivity
+def set_solid_thermal_diffusivity(self, nius):
+ self.niu_s = nius
+#set liquid thermal diffusivity
+def set_liquid_thermal_diffusivity(self, niul):
+ self.niu_l = niul
+#set rock thermal diffusivity
+def set_rock_thermal_diffusivity(self, niurock):
+ self.niu_solid = niurock
+#set adiabatic boundary on x-left
+def set_bc_adiabatic_x_left(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_x_left = 2
+#set adiabatic boundary on x-right
+def set_bc_adiabatic_x_right(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_x_right = 2
+#set adiabatic boundary on y-left
+def set_bc_adiabatic_y_left(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_y_left = 2
+#set adiabatic boundary on y-right
+def set_bc_adiabatic_y_right(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_y_right = 2
+#set adiabatic boundary on z-left
+def set_bc_adiabatic_z_left(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_z_left = 2
+#set adiabatic boundary on z-right
+def set_bc_adiabatic_z_right(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_z_right = 2
+#set constant temperature on x-left
+def set_bc_constant_temperature_x_left(self,xl):
+ self.solute_bc_x_left = 1
+ self.solute_bcxl = xl
+#set constant temperature on x-right
+def set_bc_constant_temperature_x_right(self,xr):
+ self.solute_bc_x_right = 1
+ self.solute_bcxr = xr
+#set constant temperature on y-left
+def set_bc_constant_temperature_y_left(self,yl):
+ self.solute_bc_y_left = 1
+ self.solute_bcyl = yl
+#set constant temperature on y-right
+def set_bc_constant_temperature_y_right(self,yr):
+ self.solute_bc_y_right = 1
+ self.solute_bcyr = yr
+#set constant temperature on z-left
+def set_bc_constant_temperature_z_left(self,zl):
+ self.solute_bc_z_left = 1
+ self.solute_bczl = zl
+#set constant temperature on z-right
+def set_bc_constant_temperature_z_right(self,zr):
+ self.solute_bc_y_right = 1
+ self.solute_bczr = zr
+
+# update energy of solid and liquid
+def update_H_sl(self):
+ #energy of solid
+ self.H_s = self.Cp_s*self.T_s
+ #energy of liquid
+ self.H_l = self.H_s+self.Lt
+ print('H_s',self.H_s)
+ print('H_l',self.H_l)
+
Then it initialize some variable or function
+#intialize the energy
+@ti.kernel
+def init_H(self):
+ for I in ti.grouped(self.rho_T):
+ #calculate the energy, convert_T_H() define later
+ self.rho_H[I] = self.convert_T_H(self.rho_T[I])
+
+#intialize the density distribiution function for solute concentration
+@ti.kernel
+def init_fg(self):
+ for I in ti.grouped(self.fg):
+ #calculate the overall specific heat
+ Cp = self.rho_fl[I]*self.Cp_l + (1-self.rho_fl[I])*self.Cp_s
+ #intialize the density distribiution function for solute concentration equals equilibrium density distribiution function for solute concentration
+ for s in ti.static(range(19)):
+ self.fg[I][s] = self.g_feq(s,self.rho_T[I],self.rho_H[I], Cp, self.v[I])
+ self.Fg[I][s] = self.fg[I][s]
+
+#intialize the volumn fraction of liquid
+@ti.kernel
+def init_fl(self):
+ for I in ti.grouped(self.rho_T):
+ #convert_T_fl define later
+ self.rho_fl[I] = self.convert_T_fl(self.rho_T[I])
+
g_feq(self, k,local_T,local_H, Cp, u)
calculate the equilibrium density distribiution function for thermal energy
@ti.func
+def g_feq(self, k,local_T,local_H, Cp, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ feqout = 0.0
+ #calculating the zero-velocity equilibrium thermal distribution function
+ if (k==0):
+ feqout = local_H-Cp*local_T+self.w[k]*Cp*local_T*(1-1.5*uv)
+ else:
+ #calculating other directions equilibrium thermal distribution function
+ feqout = self.w[k]*Cp*local_T*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, self.w[k], feqout, Cp, local_T)
+ return feqout
+
cal_local_force(i, j, k)
calculates buoyancy force
#density is the function of temperture delat(rho)=-rho*beta*delta(T)
+@ti.func
+def cal_local_force(self, i, j, k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ f[1] += self.gravity*self.buoyancy_parameter*(self.rho_T[i,j,k]-self.ref_T)
+ #f= delta(rho)*delta(v)*g
+ f *= self.rho_fl[i,j,k]
+ return f
+
collision_g()
defines the the collision of thermal distribution function
@ti.kernel
+def colission_g(self):
+ for I in ti.grouped(self.rho_T):
+ #overall relaxation time
+ tau_s = 3*(self.niu_s*(1.0-self.rho_fl[I])+self.niu_l*self.rho_fl[I])+0.5
+ #overall specific heat
+ Cp = self.rho_fl[I]*self.Cp_l + (1-self.rho_fl[I])*self.Cp_s
+
+ #ROCK overall relaxation time and specific heat
+ if (self.solid[I] >0):
+ tau_s = 3.0*self.niu_solid+0.5
+ Cp = self.Cp_solid
+
+ #f=f-1/tau*(f-feq)
+ for s in ti.static(range(19)):
+ tmp_fg = -1.0/tau_s*(self.fg[I][s]-self.g_feq(s,self.rho_T[I],self.rho_H[I], Cp, self.v[I]))
+ #print(self.fg[I][s],tmp_fg,I,s,self.rho_H[I],self.g_feq(s,self.rho_T[I],self.rho_H[I], Cp, self.v[I]))
+ self.fg[I][s] += tmp_fg
+
collision()
defines the the collision of density distribution function
@ti.kernel
+def colission(self):
+ for i,j,k in self.rho:
+ #if (self.solid[i,j,k] == 0):
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ #calculate the denisty distribution function in momentum space here
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+ #calculate the fluid density distribution function here
+ for s in ti.static(range(19)):
+ self.f[i,j,k][s] = self.f[i,j,k][s]*(self.rho_fl[i,j,k]) + self.w[s]*(1.0-self.rho_fl[i,j,k])
+
streaming1()
and streaming1_g()
defines the fluid denisty distribiution function and
+thermal density distribiution function
@ti.kernel
+def streaming1(self):
+ for i in ti.grouped(self.rho):
+ #if (self.solid[i] == 0):
+ for s in ti.static(range(19)):
+ ip = self.periodic_index(i+self.e[s])
+ self.F[ip][s] = self.f[i][s]
+
+@ti.kernel
+def streaming1_g(self):
+ for i in ti.grouped(self.rho_T):
+ for s in ti.static(range(19)):
+ ip = self.periodic_index(i+self.e[s])
+ self.Fg[ip][s] = self.fg[i][s]
+
this
+@ti.kernel
+def BC_concentration(self):
+ #constant temperature boundary condition
+ if ti.static(self.solute_bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ local_T = self.solute_bcxl
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[0,j,k]*self.Cp_l + (1-self.rho_fl[0,j,k])*self.Cp_s
+ #the boundary's thermal distribution function equals the equilibrium thermal distribution function on the boundary
+ for s in ti.static(range(19)):
+ self.fg[0,j,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[0,j,k])
+ self.Fg[0,j,k][s] = self.fg[0,j,k][s]
+ #adiabatic boundary condition
+ elif ti.static(self.solute_bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ for s in ti.static(range(19)):
+ #there is no thermal transfer between the boundaty and neighbouring cell
+ self.fg[0,j,k][s] = self.fg[1,j,k][s]
+ self.Fg[0,j,k][s] = self.fg[1,j,k][s]
+
+ #x-right
+ if ti.static(self.solute_bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ local_T = self.solute_bcxr
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[self.nx-1,j,k]*self.Cp_l + (1-self.rho_fl[self.nx-1,j,k])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[self.nx-1,j,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[self.nx-1,j,k])
+ self.Fg[self.nx-1,j,k][s]= self.fg[self.nx-1,j,k][s]
+ elif ti.static(self.solute_bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ for s in ti.static(range(19)):
+ self.fg[self.nx-1,j,k][s] = self.fg[self.nx-2,j,k][s]
+ self.Fg[self.nx-1,j,k][s] = self.fg[self.nx-2,j,k][s]
+
+ #y-left
+ if ti.static(self.solute_bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ local_T = self.solute_bcyl
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,0,k]*self.Cp_l + (1-self.rho_fl[i,0,k])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,0,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,0,k])
+ self.Fg[i,0,k][s] = self.fg[i,0,k][s]
+ elif ti.static(self.solute_bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ for s in ti.static(range(19)):
+ self.fg[i,0,k][s] = self.fg[i,1,k][s]
+ self.Fg[i,0,k][s] = self.fg[i,1,k][s]
+
+ #y-right
+ if ti.static(self.solute_bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ local_T = self.solute_bcyr
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,self.ny-1,k]*self.Cp_l + (1-self.rho_fl[i,self.ny-1,k])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,self.ny-1,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,self.ny-1,k])
+ self.Fg[i,self.ny-1,k][s] = self.fg[i,self.ny-1,k][s]
+ elif ti.static(self.solute_bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ for s in ti.static(range(19)):
+ self.fg[i,self.ny-1,k][s] = self.fg[i,self.ny-2,k][s]
+ self.Fg[i,self.ny-1,k][s] = self.fg[i,self.ny-2,k][s]
+
+ #z-left
+ if ti.static(self.solute_bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ local_T = self.solute_bczl
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,j,0]*self.Cp_l + (1-self.rho_fl[i,j,0])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,j,0][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,j,0])
+ self.Fg[i,j,0][s] = self.fg[i,j,0][s]
+ elif ti.static(self.solute_bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ for s in ti.static(range(19)):
+ self.fg[i,j,0][s] = self.fg[i,j,1][s]
+ self.Fg[i,j,0][s] = self.fg[i,j,1][s]
+
+ #z-right
+ if ti.static(self.solute_bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ local_T = self.solute_bczr
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,j,self.nz-1]*self.Cp_l + (1-self.rho_fl[i,j,self.nz-1])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,j,self.nz-1][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,j,self.nz-1])
+ self.Fg[i,j,self.nz-1][s] = self.fg[i,j,self.nz-1][s]
+ elif ti.static(self.solute_bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ for s in ti.static(range(19)):
+ self.fg[i,j,self.nz-1][s] = self.fg[i,j,self.nz-2][s]
+ self.Fg[i,j,self.nz-1][s] = self.fg[i,j,self.nz-2][s]
+
convert_H_T()
calculate the temperature
@ti.func
+def convert_H_T(self,local_H):
+ new_T=0.0
+ #if local enthalpy is less than solid enthalpy
+ #T= enthalpy/specific heat
+ if (local_H<self.H_s):
+ new_T = local_H/self.Cp_s
+ #if if local enthalpy is greater than liquid enthalpy
+ #T= Tliquid+(enthalpy-liquid enthalpy)/speific heat of liquid
+ elif (local_H>self.H_l):
+ new_T = self.T_l+(local_H-self.H_l)/self.Cp_l
+ #if if temperature is greater than solid temperature
+ #T= Tsolid+(enthalpy-solid enthalpy)/(enthalpy of liquid-enthalpy of solid)*(temperature of liquid- temperature of solid)
+ elif (self.T_l>self.T_s):
+ new_T = self.T_s+(local_H-self.H_s)/(self.H_l-self.H_s)*(self.T_l-self.T_s)
+ else:
+ #else T= temperature of solid
+ new_T = self.T_s
+
+ return new_T
+
convert_H_fl()
calculate the volumn fraction of liquid
@ti.func
+def convert_H_fl(self,local_H):
+ new_fl=0.0
+ #if enthalpy is less than solid enthalpy
+ #it is zero
+ if (local_H<self.H_s):
+ new_fl = 0.0
+ #if it is greater than liquid enthalpy
+ #it is one
+ elif (local_H>self.H_l):
+ new_fl = 1.0
+ #else
+ #it equals to (enthaply- soid enthaply)/(enthaply of liquid- enthalpy of solid)
+ else:
+ new_fl = (local_H-self.H_s)/(self.H_l-self.H_s)
+
+ return new_fl
+
convert_T_H()
calculate the enthaply from temperature
@ti.func
+def convert_T_H(self,local_T):
+ new_H = 0.0
+ # calculate enthaply for three different conditions
+ if (local_T<=self.T_s):
+ new_H = self.Cp_s*local_T
+ elif (local_T>self.T_l):
+ new_H = (local_T-self.T_l)*self.Cp_l+self.H_l
+ else:
+ fluid_frc = (local_T-self.T_s)/(self.T_l-self.T_s)
+ new_H = self.H_s*(1-fluid_frc) + self.H_l*fluid_frc
+ return new_H
+
convert_T_fl()
calculate volumn fraction from temperature
@ti.func
+def convert_T_fl(self,local_T):
+ new_fl = 0.0
+ # calculate volumn fraction for three different conditions
+ if (local_T<=self.T_s):
+ new_fl = 0.0
+ elif (local_T>=self.T_l):
+ new_fl = 1.0
+ elif (self.T_l>self.T_s):
+ new_fl = (local_T-self.T_s)/(self.T_l-self.T_s)
+ else:
+ new_fl = 1.0
+
+ return new_fl
+
streaming3()
calculate macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ self.forcexyz[i] = self.cal_local_force(i.x, i.y, i.z)
+ #print(i.x, i.y, i.z)
+ if ((self.solid[i]==0) or (self.rho_fl[i]>0.0)):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ for s in ti.static(range(19)):
+ self.f[i][s] = self.f[i][s]*self.rho_fl[i]+self.w[s]*(1.0-self.rho_fl[i])
+ #density for fluid
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+ #velocity for fluid
+ self.v[i] /= self.rho[i]
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ #density and velocity for solid
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
streaming3()
calculate enthalpy
@ti.kernel
+def streaming3_g(self):
+ for i in ti.grouped(self.rho_T):
+ self.rho_H[i] = 0.0
+ #enthalpy here
+ self.rho_H[i] = self.Fg[i].sum()
+ #for s in ti.static(range(19)):
+ # self.rho_H[i] += self.Fg[i][s]
+ self.fg[i] = self.Fg[i]
+
update_T_fl()
calculate volumn fraction and temperature
@ti.kernel
+def update_T_fl(self):
+ for I in ti.grouped(self.rho_T):
+ self.rho_T[I] = self.convert_H_T(self.rho_H[I])
+ self.rho_fl[I] = self.convert_H_fl(self.rho_H[I])
+ if (self.solid[I]>0):
+ self.rho_fl[I] = 0.0
+
init_solute_simulation()
initialize the solute simulation
def init_solute_simulation(self):
+
+ self.init_simulation()
+ self.update_H_sl()
+ #ethalpy
+ self.init_H()
+ #volumn fraction
+ self.init_fl()
+ #thermal distribution function
+ self.init_fg()
+
init_concentration(filename)
import concentration data from file
def init_concentration(self,filename):
+ in_dat = np.loadtxt(filename)
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ self.rho_T.from_numpy(in_dat)
+
this
+def step(self):
+ self.colission()
+ self.colission_g()
+
+ self.streaming1()
+ self.streaming1_g()
+
+ self.Boundary_condition()
+ self.BC_concentration()
+
+ self.streaming3_g()
+ self.streaming3()
+ self.streaming3_g()
+
+ self.update_T_fl()
+
this
+def export_VTK(self, n):
+ gridToVTK(
+ "./LB_SingelPhase_"+str(n),
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "Solid_Liquid": np.ascontiguousarray(self.rho_fl.to_numpy()),
+ "Tempreture": np.ascontiguousarray(self.rho_T.to_numpy()),
+ "Entropy": np.ascontiguousarray(self.rho_H.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2])),
+ "Force": ( np.ascontiguousarray(self.forcexyz.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.forcexyz.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.forcexyz.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
this
+This file is the same as LBM_3D_SinglePhase_Solver in Single_phase folder
+This solver is the multiphase model based on color gradient model +Firstly, it defines some parameters
+# NOTE: THIS CODE NEED taichi_glsl, so please use taichi version <=0.8.5
+#import taichi, numpy, pyevtk and time package
+import taichi as ti
+import numpy as np
+#import taichi_glsl as ts
+from pyevtk.hl import gridToVTK
+import time
+#from taichi_glsl import scalar
+
+#from taichi_glsl.scalar import isinf, isnan
+#from taichi_glsl.vector import vecFill
+#intialize taichi
+ti.init(arch=ti.cpu)
+#ti.init(arch=ti.gpu, dynamic_index=True,offline_cache=True)
+
+#enable projection
+enable_projection = True
+# 131*131*131
+nx,ny,nz = 131,131,131
+#nx,ny,nz = 131,131,131
+#external force in x,y,z direction
+fx,fy,fz = 5.0e-5,-2e-5,0.0
+#niu = 0.1
+#liquid viscosity
+niu_l = 0.1 #psi>0
+#gas viscosity
+niu_g = 0.1 #psi<0
+#psi in color gradient calculation
+psi_solid = 0.7
+#surface tension
+CapA = 0.005
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 0, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+bc_psi_x_left, psi_x_left = 1, -1.0 # boundary condition for phase-field: 0 = periodic,
+bc_psi_x_right, psi_x_right = 0, 1.0 # 1 = constant value on the boundary, value = -1.0 phase1 or 1.0 = phase 2
+bc_psi_y_left, psi_y_left = 0, 1.0
+bc_psi_y_right, psi_y_right = 0, 1.0
+bc_psi_z_left, psi_z_left = 0, 1.0
+bc_psi_z_right, psi_z_right = 0, 1.0
+
+# Non Sparse memory allocation
+#density distribution function nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#velocity nx*ny*nz vector
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#psi nx*ny*nz
+psi = ti.field(ti.f32, shape=(nx,ny,nz))
+#density r nx*ny*nz
+rho_r = ti.field(ti.f32, shape=(nx,ny,nz))
+#density b nx*ny*nz
+rho_b = ti.field(ti.f32, shape=(nx,ny,nz))
+#density r nx*ny*nz
+rhor = ti.field(ti.f32, shape=(nx,ny,nz))
+#density b nx*ny*nz
+rhob = ti.field(ti.f32, shape=(nx,ny,nz))
+#lattice speed 19 dimensional vector
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#S_dig = ti.field(ti.f32,shape=(19))
+#lattice speed 19 dimensional vector
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#weight parameter 19 dimensional vector
+w = ti.field(ti.f32, shape=(19))
+#solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#streaming vector 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+
+#external force 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+# x-left velocity 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+# x-right velocity 3 dimensional vector
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+# y-left velocity 3 dimensional vector
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+# y-right velocity 3 dimensional vector
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+# z-left velocity 3 dimensional vector
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+# z-right velocity 3 dimensional vector
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#inverse transforming matrix 19*19
+inv_M = ti.field(ti.f32, shape=(19,19))
+#parameters for calculating the parameter of s diagonal
+#=======================================#
+lg0, wl, wg = 0.0, 0.0, 0.0
+l1, l2, g1, g2 = 0.0, 0.0, 0.0, 0.0
+wl = 1.0/(niu_l/(1.0/3.0)+0.5)
+wg = 1.0/(niu_g/(1.0/3.0)+0.5)
+lg0 = 2*wl*wg/(wl+wg)
+l1=2*(wl-lg0)*10
+l2=-l1/0.2
+g1=2*(lg0-wg)*10
+g2=g1/0.2
+#=======================================#
+
+#transformation matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#inverde of transforming matrix
+inv_M_np = np.linalg.inv(M_np)
+#streaming array
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#M matrix from the numpy
+M.from_numpy(M_np)
+#inverse matrix from numpy
+inv_M.from_numpy(inv_M_np)
+
+#steaming array from numpy
+LR.from_numpy(LR_np)
+#external force with vector three dimensional
+ext_f[None] = ti.Vector([fx,fy,fz])
+#set transforming matrix, inverse matrix and streaming vector non-modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+
+#set x,y,z array with nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#set meshgrid and return three meshgrid matrix X,Y,Z with non-cartesian indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local, u)
calculate the equilibrium denisty distribution function
@ti.func
+def feq(k,rho_local, u):
+ # eu=ts.vector.dot(e[k],u)
+ # uv=ts.vector.dot(u,u)
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #same as single phase equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
intialize some variable
@ti.kernel
+def init():
+ for i,j,k in solid:
+ if (solid[i,j,k] == 0):
+ #if it is fluid intialize the density and velocity be one and zero
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ # set density r and density b based on psi
+ rho_r[i,j,k] = (psi[i,j,k]+1.0)/2.0
+ rho_b[i,j,k] = 1.0 - rho_r[i,j,k]
+ #set another density r and density b
+ rhor[i,j,k] = 0.0
+ rhob[i,j,k] = 0.0
+ #set density distribution equals to equilibrium density distribution function
+ for s in ti.static(range(19)):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+
init_geo(filename, filename2)
import the geometry data
def init_geo(filename, filename2):
+ #read the solid flag data and set it as an column major array
+ in_dat = np.loadtxt(filename)
+ in_dat[in_dat>0] = 1
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+
+ #read the phase data from file
+ phase_in_dat = np.loadtxt(filename2)
+ #set the array from the file with colum major
+ phase_in_dat = np.reshape(phase_in_dat, (nx,ny,nz), order='F')
+
+ return in_dat, phase_in_dat
+
static_init()
initialize non-modified variable
@ti.kernel
+def static_init():
+ if ti.static(enable_projection): # No runtime overhead
+ #define lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #define another lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #define a weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #define the boundary velocity
+ bc_vel_x_left = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M()
calculate the density distribution function in momentum space
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in ti.static(range(19)):
+ for s in ti.static(range(19)):
+ #calculate here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
GuoF(i,j,k,s,u)
calculate Guo’s force term
@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in ti.static(range(19)):
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
meq_vec(rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
Compute_C()
calculate the color gradient
@ti.func
+def Compute_C(i):
+ C = ti.Vector([0.0,0.0,0.0])
+ ind_S = 0
+ for s in ti.static(range(19)):
+ ip = periodic_index_for_psi(i+e[s])
+ if (solid[ip] == 0):
+ #if it's fluid calculate the color gradient based on psi
+ C += 3.0*w[s]*e_f[s]*psi[ip]
+ else:
+ #if it is solid and abs(density r-density b) is less than 0.9
+ ind_S = 1
+ #calculate the color gradient based on psi_solid and set ind_s=1
+ C += 3.0*w[s]*e_f[s]*psi_solid
+
+ if (abs(rho_r[i]-rho_b[i]) > 0.9) and (ind_S == 1):
+ #if abs(density r-density b) is very large and it's solid set color gradient to be zero
+ C = ti.Vector([0.0,0.0,0.0])
+
+ return C
+
Compute_S_local
calculate parameter of s diagonal
@ti.func
+def Compute_S_local(id):
+ sv=0.0; sother=0.0
+ if (psi[id]>0):
+ if (psi[id]>0.1):
+ #if psi>0.1
+ #sv=1.0/(niu_l/(1.0/3.0)+0.5)
+ sv=wl
+ else:
+ #if 0<psi<0.1 calculate sv
+ sv=lg0+l1*psi[id]+l2*psi[id]*psi[id]
+ else:
+ #if psi <-0.1
+ if (psi[id]<-0.1):
+ #calculate sv
+ sv=wg
+ else:
+ #if psi >-0.1
+ sv=lg0+g1*psi[id]+g2*psi[id]*psi[id]
+ #calculate s other
+ sother = 8.0*(2.0-sv)/(8.0-sv)
+
+ #set s diagonal to be zero and set certain element to be relatie local parameter
+ S = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ S[1]=sv;S[2]=sv;S[4]=sother;S[6]=sother;S[8]=sother;S[9]=sv;
+ S[10]=sv;S[11]=sv;S[12]=sv;S[13]=sv;S[14]=sv;S[15]=sv;S[16]=sother;
+ S[17]=sother;S[18]=sother;
+
+
+ return S;
+
collision()
define the collision and recoloring process
@ti.kernel
+def colission():
+ for i,j,k in rho:
+ #if it is inner fluid, calculate color gradient divided by norm of color gradient
+ if (i<nx and j<ny and k<nz and solid[i,j,k] == 0):
+ uu = v[i,j,k].norm_sqr()
+ C = Compute_C(ti.Vector([i,j,k]))
+ cc = C.norm()
+ normal = ti.Vector([0.0,0.0,0.0])
+ if cc>0 :
+ normal = C/cc
+ #calculate the M
+ m_temp = multiply_M(i,j,k)
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ #calculate surface tension term
+ meq[1] += CapA*cc
+ meq[9] += 0.5*CapA*cc*(2*normal.x*normal.x-normal.y*normal.y-normal.z*normal.z)
+ meq[11] += 0.5*CapA*cc*(normal.y*normal.y-normal.z*normal.z)
+ meq[13] += 0.5*CapA*cc*(normal.x*normal.y)
+ meq[14] += 0.5*CapA*cc*(normal.y*normal.z)
+ meq[15] += 0.5*CapA*cc*(normal.x*normal.z)
+ #calculate s local
+ S_local = Compute_S_local(ti.Vector([i,j,k]))
+ #calculate s*(m-meq)
+ for s in ti.static(range(19)):
+ m_temp[s] -= S_local[s]*(m_temp[s]-meq[s])
+ m_temp[s] += (1-0.5*S_local[s])*GuoF(i,j,k,s,v[i,j,k])
+ #calculte convection of density filed
+ g_r = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ g_b = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+
+ for s in ti.static(range(19)):
+ f[i,j,k,s] = 0
+ for l in ti.static(range(19)):
+ # 1.single phase collision
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
+ g_r[s] = feq(s,rho_r[i,j,k],v[i,j,k])
+ g_b[s] = feq(s,rho_b[i,j,k],v[i,j,k])
+
+ if (cc>0):
+ for kk in ti.static([1,3,5,7,9,11,13,15,17]):
+ # ef=ts.vector.dot(e[kk],C)
+ ef=e[kk].dot(C)
+ cospsi= g_r[kk] if (g_r[kk]<g_r[kk+1]) else g_r[kk+1]
+ cospsi= cospsi if (cospsi<g_b[kk]) else g_b[kk]
+ cospsi=cospsi if (cospsi<g_b[kk+1]) else g_b[kk+1]
+ cospsi*=ef/cc
+ #2.surface tension perturbation
+ g_r[kk]+=cospsi
+ g_r[kk+1]-=cospsi
+ g_b[kk]-=cospsi
+ g_b[kk+1]+=cospsi
+ # recoloring
+ for s in ti.static(range(19)):
+ ip = periodic_index(ti.Vector([i,j,k])+e[s])
+ if (solid[ip]==0):
+ rhor[ip] += g_r[s]
+ rhob[ip] += g_b[s]
+ else:
+ rhor[i,j,k] += g_r[s]
+ rhob[i,j,k] += g_b[s]
+
periodic_index()
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(i):
+ iout = i
+ if i[0]<0: iout[0] = nx-1
+ if i[0]>nx-1: iout[0] = 0
+ if i[1]<0: iout[1] = ny-1
+ if i[1]>ny-1: iout[1] = 0
+ if i[2]<0: iout[2] = nz-1
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
periodic_index_for_psi(i)
defines the index of boundary for psi if using periodic boundary condition
@ti.func
+def periodic_index_for_psi(i):
+ iout = i
+ if i[0]<0:
+ #if periodic boundary condition set index based on periodic boundary condition
+ if bc_psi_x_left == 0:
+ iout[0] = nx-1
+ else:
+ #otherwise set neighbouring index,
+ #similar for other sides
+ iout[0] = 0
+
+ if i[0]>nx-1:
+ if bc_psi_x_right==0:
+ iout[0] = 0
+ else:
+ iout[0] = nx-1
+
+ if i[1]<0:
+ if bc_psi_y_left == 0:
+ iout[1] = ny-1
+ else:
+ iout[1] = 0
+
+ if i[1]>ny-1:
+ if bc_psi_y_right==0:
+ iout[1] = 0
+ else:
+ iout[1] = ny-1
+
+ if i[2]<0:
+ if bc_psi_z_left==0:
+ iout[2] = nz-1
+ else:
+ iout[2] = 0
+
+ if i[2]>nz-1:
+ if bc_psi_z_right==0:
+ iout[2] = 0
+ else:
+ iout[2] = nz-1
+
+ return iout
+
streaming1()
defines steaming process of denisty distribution function
@ti.kernel
+def streaming1():
+ for i,j,k in rho:
+ #if (solid[i,j,k] == 0):
+ if (i<nx and j<ny and k<nz and solid[i,j,k] == 0):
+ ci = ti.Vector([i,j,k])
+ for s in ti.static(range(19)):
+ ip = periodic_index(ci+e[s])
+ if (solid[ip]==0):
+ #if it is fluid,streaming along certain direction
+ F[ip,s] = f[ci,s]
+ else:
+ #if it is on the solid, bounce back to the opposite
+ F[ci,LR[s]] = f[ci,s]
+ #print(i, ip, "@@@")
+
Boundary_condition_psi()
defines boundary condition for psi
@ti.kernel
+def Boundary_condition_psi():
+ if bc_psi_x_left == 1:
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ #if it is fluid the value of psi equals to the psi_x_left
+ psi[0,j,k] = psi_x_left
+ #calculate density according to psi
+ #similar for other sides
+ rho_r[0,j,k] = (psi_x_left + 1.0)/2.0
+ rho_b[0,j,k] = 1.0 - rho_r[0,j,k]
+
+ if bc_psi_x_right == 1:
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ psi[nx-1,j,k] = psi_x_right
+ rho_r[nx-1,j,k] = (psi_x_right + 1.0)/2.0
+ rho_b[nx-1,j,k] = 1.0 - rho_r[nx-1,j,k]
+
+ if bc_psi_y_left == 1:
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,0,k]==0):
+ psi[i,0,k] = psi_y_left
+ rho_r[i,0,k] = (psi_y_left + 1.0)/2.0
+ rho_b[i,0,k] = 1.0 - rho_r[i,0,k]
+
+ if bc_psi_y_right == 1:
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,ny-1,k]==0):
+ psi[i,ny-1,k] = psi_y_right
+ rho_r[i,ny-1,k] = (psi_y_right + 1.0)/2.0
+ rho_b[i,ny-1,k] = 1.0 - rho_r[i,ny-1,k]
+
+ if bc_psi_z_left == 1:
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,0]==0):
+ psi[i,j,0] = psi_z_left
+ rho_r[i,j,0] = (psi_z_left + 1.0)/2.0
+ rho_b[i,j,0] = 1.0 - rho_r[i,j,0]
+
+ if bc_psi_z_right == 1:
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,nz-1]==0):
+ psi[i,j,nz-1] = psi_z_right
+ rho_r[i,j,nz-1] = (psi_z_right + 1.0)/2.0
+ rho_b[i,j,nz-1] = 1.0 - rho_r[i,j,nz-1]
+
Boundary_condition
defines boundary condition and the same as single_phase solver
@ti.kernel
+def Boundary_condition():
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None])
+
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None])
+
+
+ # Direction Y
+ if ti.static(bc_y_left==1):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,1,k]>0):
+ F[i,0,k,s]=feq(s, rho_bcyl, v[i,1,k])
+ else:
+ F[i,0,k,s]=feq(s, rho_bcyl, v[i,0,k])
+
+ if ti.static(bc_y_left==2):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ F[i,0,k,s]=feq(LR[s], 1.0, bc_vel_y_left[None])-F[i,0,k,LR[s]]+feq(s,1.0,bc_vel_y_left[None])
+
+ if ti.static(bc_y_right==1):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,ny-2,k]>0):
+ F[i,ny-1,k,s]=feq(s, rho_bcyr, v[i,ny-2,k])
+ else:
+ F[i,ny-1,k,s]=feq(s, rho_bcyr, v[i,ny-1,k])
+
+ if ti.static(bc_y_right==2):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,ny-1,k]==0):
+ for s in ti.static(range(19)):
+ F[i,ny-1,k,s]=feq(LR[s], 1.0, bc_vel_y_right[None])-F[i,ny-1,k,LR[s]]+feq(s,1.0,bc_vel_y_right[None])
+
+ # Z direction
+ if ti.static(bc_z_left==1):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,j,1]>0):
+ F[i,j,0,s]=feq(s, rho_bczl, v[i,j,1])
+ else:
+ F[i,j,0,s]=feq(s, rho_bczl, v[i,j,0])
+
+ if ti.static(bc_z_left==2):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ F[i,j,0,s]=feq(LR[s], 1.0, bc_vel_z_left[None])-F[i,j,0,LR[s]]+feq(s,1.0,bc_vel_z_left[None])
+
+ if ti.static(bc_z_right==1):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,nz-1]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,j,nz-2]>0):
+ F[i,j,nz-1,s]=feq(s, rho_bczr, v[i,j,nz-2])
+ else:
+ F[i,j,nz-1,s]=feq(s, rho_bczr, v[i,j,nz-1])
+
+ if ti.static(bc_z_right==2):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,nz-1]==0):
+ for s in ti.static(range(19)):
+ F[i,j,nz-1,s]=feq(LR[s], 1.0, bc_vel_z_right[None])-F[i,j,nz-1,LR[s]]+feq(s,1.0,bc_vel_z_right[None])
+
Boundary_condition_psi()
calculate macroscopic variable
@ti.kernel
+def streaming3():
+ for i,j,k, in rho:
+ #if (solid[i,j,k] == 0):
+ if (i<nx and j<ny and k<nz and solid[i,j,k] == 0):
+ rho[i,j,k] = 0
+ v[i,j,k] = ti.Vector([0,0,0])
+ #define denisty r and density b
+ rho_r[i,j,k] = rhor[i,j,k]
+ rho_b[i,j,k] = rhob[i,j,k]
+ rhor[i,j,k] = 0.0; rhob[i,j,k] = 0.0
+
+ for s in ti.static(range(19)):
+ f[i,j,k,s] = F[i,j,k,s]
+ rho[i,j,k] += f[i,j,k,s]
+ v[i,j,k] += e_f[s]*f[i,j,k,s]
+ #calculate velocity and psi
+ v[i,j,k] /= rho[i,j,k]
+ v[i,j,k] += (ext_f[None]/2)/rho[i,j,k]
+ psi[i,j,k] = rho_r[i,j,k]-rho_b[i,j,k]/(rho_r[i,j,k] + rho_b[i,j,k])
+
The code snippts below define time, read file do the simulation and export results +It is almost the same as the single-phase solver except two input file and export phase variable
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+dt_count = 0
+
+
+solid_np, phase_np = init_geo('./img_ftb131.txt','./phase_ftb131.dat')
+
+#solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+psi.from_numpy(phase_np)
+
+static_init()
+init()
+
+#print(wl,wg, lg0, l1, l2,'~@@@@@~@~@~@~@')
+
+for iter in range(80000+1):
+ colission()
+ streaming1()
+ Boundary_condition()
+ #streaming2()
+ streaming3()
+ Boundary_condition_psi()
+
+
+ if (iter%500==0):
+
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+
+ if (iter%10000==0):
+ gridToVTK(
+ "./structured"+str(iter),
+ x,
+ y,
+ z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(solid.to_numpy()),
+ "rho": np.ascontiguousarray(rho.to_numpy()[0:nx,0:ny,0:nz]),
+ "phase": np.ascontiguousarray(psi.to_numpy()[0:nx,0:ny,0:nz]),
+ "velocity": (np.ascontiguousarray(v.to_numpy()[0:nx,0:ny,0:nz,0]), np.ascontiguousarray(v.to_numpy()[0:nx,0:ny,0:nz,1]),np.ascontiguousarray(v.to_numpy()[0:nx,0:ny,0:nz,2]))
+ }
+ )
+
+#ti.print_kernel_profile_info()
+#ti.print_profile_info()
+
This file is almost the same as the lbm_solver_3d_2phase.py
file execpt sparse storage definition of some varibles
# Sparse Storage memory allocation
+f = ti.field(ti.f32)
+F = ti.field(ti.f32)
+rho = ti.field(ti.f32)
+v = ti.Vector.field(3, ti.f32)
+rhor = ti.field(ti.f32)
+rhob = ti.field(ti.f32)
+rho_r = ti.field(ti.f32)
+rho_b = ti.field(ti.f32)
+n_mem_partition = 3
+
+cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(v)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rhor)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rhob)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho_r)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho_b)
+
+
+cell2 = ti.root.pointer(ti.ijkl,(nx//3+1,ny//3+1,nz//3+1,1))
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(f)
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(F)
+
Above code snippts define the sparse storage of some varibles
+' + + '' + + _("Hide Search Matches") + + "
" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(SphinxHighlight.highlightSearchWords); +_ready(SphinxHighlight.initEscapeListener); diff --git a/docs/aaa.html b/docs/aaa.html new file mode 100644 index 0000000..2a18f1c --- /dev/null +++ b/docs/aaa.html @@ -0,0 +1,644 @@ + + + + + + +This is a D3Q19 MRT(multi-relaxation-time) solver for single phase. It defines a class called LB3D_Solver_Single_Phase
. The Class has a default function
+__init__()
as normal python class.
class LB3D_Solver_Single_Phase:
+ def __init__(self, nx, ny, nz, sparse_storage = False):
+ #enable projection, define a sparse_storage flag
+ self.enable_projection = True
+ self.sparse_storage = sparse_storage
+ #the grid of the simulation in three direction
+ self.nx,self.ny,self.nz = nx,ny,nz
+ #nx,ny,nz = 120,120,120
+ #density distribution function in three direction
+ self.fx,self.fy,self.fz = 0.0e-6,0.0,0.0
+ #kinematic viscosity in lattice unit
+ self.niu = 0.16667
+ #define a taichi field of float scalar which is the maximum velocity
+ self.max_v=ti.field(ti.f32,shape=())
+ #Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+ self.bc_x_left, self.rho_bcxl, self.vx_bcxl, self.vy_bcxl, self.vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+ self.bc_x_right, self.rho_bcxr, self.vx_bcxr, self.vy_bcxr, self.vz_bcxr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary x-axis right side
+ self.bc_y_left, self.rho_bcyl, self.vx_bcyl, self.vy_bcyl, self.vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+ self.bc_y_right, self.rho_bcyr, self.vx_bcyr, self.vy_bcyr, self.vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+ self.bc_z_left, self.rho_bczl, self.vx_bczl, self.vy_bczl, self.vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+ self.bc_z_right, self.rho_bczr, self.vx_bczr, self.vy_bczr, self.vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+ if sparse_storage == False:
+ #define old density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.f = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define new density distribution function with taichi field which has nx*ny*nz element and each element is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define density with taichi field which has nx*ny*nz element and each element is a scalar
+ self.rho = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define velocity with taichi field which has nx*ny*nz element and each element is a three dimensional vector
+ self.v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+ else:
+ #sparse storage the variable
+ #define old density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.f = ti.Vector.field(19, ti.f32)
+ #define new density distribution function by taichi field with one element and which is a 19 dimensional vector
+ self.F = ti.Vector.field(19,ti.f32)
+ #define density by taichi field with one element which is a scalar
+ self.rho = ti.field(ti.f32)
+ #define velocity by taichi field with one element which is a scalar
+ self.v = ti.Vector.field(3, ti.f32)
+ #define partition equals 3
+ n_mem_partition = 3
+ #every index has four variable rho, v, f, F
+ cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+ cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(self.rho, self.v, self.f, self.F)
+ #define lattice speed 3x19
+ self.e = ti.Vector.field(3,ti.i32, shape=(19))
+ #define s diagnol vector
+ self.S_dig = ti.Vector.field(19,ti.f32,shape=())
+ #define another lattice speed 3x19
+ self.e_f = ti.Vector.field(3,ti.f32, shape=(19))
+ #define weight parameter
+ self.w = ti.field(ti.f32, shape=(19))
+ #define solid which is a flag when equals 0 it is fluid, when it is 1 it is solid
+ self.solid = ti.field(ti.i8,shape=(nx,ny,nz))
+ #define external force which is a three dimensional vector
+ self.ext_f = ti.Vector.field(3,ti.f32,shape=())
+ #define transforming matrix M which is a 19x19 dimension matrix
+ self.M = ti.Matrix.field(19, 19, ti.f32, shape=())
+ #define the inverse transforming matrix M^-1
+ self.inv_M = ti.Matrix.field(19,19,ti.f32, shape=())
+ #define the numpy version of M.
+ M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define the numpy version of M^-1
+ inv_M_np = np.linalg.inv(M_np)
+ #define the index of 19 lattice node for bounce back
+ self.LR = [0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17]
+ #define taichi field version of M
+ self.M[None] = ti.Matrix([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+ [-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+ [1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+ [0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+ [0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+ [0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+ [0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+ [0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+ [0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+ [0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+ #define taichi field version of M^-1
+ self.inv_M[None] = ti.Matrix(inv_M_np)
+ #define coordinate nx*ny*nz
+ self.x = np.linspace(0, nx, nx)
+ self.y = np.linspace(0, ny, ny)
+ self.z = np.linspace(0, nz, nz)
+ #X, Y, Z = np.meshgrid(self.x, self.y, self.z, indexing='ij')
+
Following is the init_simulation()
function which initialize some simulation parameter
def init_simulation(self):
+#x,y,z velocity vector from vx_bcxl,vy_bcxl and vz_bcxl
+self.bc_vel_x_left = [self.vx_bcxl, self.vy_bcxl, self.vz_bcxl]
+self.bc_vel_x_right = [self.vx_bcxr, self.vy_bcxr, self.vz_bcxr]
+self.bc_vel_y_left = [self.vx_bcyl, self.vy_bcyl, self.vz_bcyl]
+self.bc_vel_y_right = [self.vx_bcyr, self.vy_bcyr, self.vz_bcyr]
+self.bc_vel_z_left = [self.vx_bczl, self.vy_bczl, self.vz_bczl]
+self.bc_vel_z_right = [self.vx_bczr, self.vy_bczr, self.vz_bczr]
+#define single relaxation time tau
+self.tau_f=3.0*self.niu+0.5
+#define single relaxation frequency
+self.s_v=1.0/self.tau_f
+#define other parameter in the s diagonal
+self.s_other=8.0*(2.0-self.s_v)/(8.0-self.s_v)
+#define the s diagonal
+self.S_dig[None] = ti.Vector([0,self.s_v,self.s_v,0,self.s_other,0,self.s_other,0,self.s_other, self.s_v, self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_v,self.s_other,self.s_other,self.s_other])
+#define external force
+#self.ext_f[None] = ti.Vector([self.fx,self.fy,self.fz])
+self.ext_f[None][0] = self.fx
+self.ext_f[None][1] = self.fy
+self.ext_f[None][2] = self.fz
+#if external force greater than zero define force_flag equals 1
+#other wise force_flag equals 0
+if ((abs(self.fx)>0) or (abs(self.fy)>0) or (abs(self.fz)>0)):
+ self.force_flag = 1
+else:
+ self.force_flag = 0
+
+#define M M^-1 S diagonal not been modified.
+ti.static(self.inv_M)
+ti.static(self.M)
+#ti.static(LR)
+ti.static(self.S_dig)
+#statically initialize
+self.static_init()
+self.init()
+
feq()
calculate the equilibrium density distribution function in velocity space
#taichi function
+@ti.func
+ def feq(self, k,rho_local, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = self.w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, self.w[k])
+ return feqout
+
init()
initialize density velocity and density distribution function
@ti.kernel
+def init(self):
+ for i,j,k in self.solid:
+ #print(i,j,k)
+ if (self.sparse_storage==False or self.solid[i,j,k]==0):
+ #if it is fluid then initialize density equals one
+ self.rho[i,j,k] = 1.0
+ #initialize the velocity to be zero in all the direction
+ self.v[i,j,k] = ti.Vector([0,0,0])
+ for s in ti.static(range(19)):
+ #initialize 19 denisty distribution function equals the equilibrium density distribution function
+ self.f[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ self.F[i,j,k][s] = self.feq(s,1.0,self.v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
import data from a file
def init_geo(self,filename):
+ #load data from a file
+ in_dat = np.loadtxt(filename)
+ #set any positive value to be one
+ in_dat[in_dat>0] = 1
+ #reshape it as a nx*ny*nz vector with column major
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ #assign it to solid varible
+ self.solid.from_numpy(in_dat)
+
static_init()
initialize lattice speeed and weight parameter. These parameter is not modified during the simulation
#taichi kernel for parallization
+@ti.kernel
+def static_init(self):
+ if ti.static(self.enable_projection): # No runtime overhead
+ #initialize the lattice speed
+ self.e[0] = ti.Vector([0,0,0])
+ self.e[1] = ti.Vector([1,0,0]); self.e[2] = ti.Vector([-1,0,0]); self.e[3] = ti.Vector([0,1,0]); self.e[4] = ti.Vector([0,-1,0]);self.e[5] = ti.Vector([0,0,1]); self.e[6] = ti.Vector([0,0,-1])
+ self.e[7] = ti.Vector([1,1,0]); self.e[8] = ti.Vector([-1,-1,0]); self.e[9] = ti.Vector([1,-1,0]); self.e[10] = ti.Vector([-1,1,0])
+ self.e[11] = ti.Vector([1,0,1]); self.e[12] = ti.Vector([-1,0,-1]); self.e[13] = ti.Vector([1,0,-1]); self.e[14] = ti.Vector([-1,0,1])
+ self.e[15] = ti.Vector([0,1,1]); self.e[16] = ti.Vector([0,-1,-1]); self.e[17] = ti.Vector([0,1,-1]); self.e[18] = ti.Vector([0,-1,1])
+
+ self.e_f[0] = ti.Vector([0,0,0])
+ self.e_f[1] = ti.Vector([1,0,0]); self.e_f[2] = ti.Vector([-1,0,0]); self.e_f[3] = ti.Vector([0,1,0]); self.e_f[4] = ti.Vector([0,-1,0]);self.e_f[5] = ti.Vector([0,0,1]); self.e_f[6] = ti.Vector([0,0,-1])
+ self.e_f[7] = ti.Vector([1,1,0]); self.e_f[8] = ti.Vector([-1,-1,0]); self.e_f[9] = ti.Vector([1,-1,0]); self.e_f[10] = ti.Vector([-1,1,0])
+ self.e_f[11] = ti.Vector([1,0,1]); self.e_f[12] = ti.Vector([-1,0,-1]); self.e_f[13] = ti.Vector([1,0,-1]); self.e_f[14] = ti.Vector([-1,0,1])
+ self.e_f[15] = ti.Vector([0,1,1]); self.e_f[16] = ti.Vector([0,-1,-1]); self.e_f[17] = ti.Vector([0,1,-1]); self.e_f[18] = ti.Vector([0,-1,1])
+ #initialize the weight parameter
+ self.w[0] = 1.0/3.0; self.w[1] = 1.0/18.0; self.w[2] = 1.0/18.0; self.w[3] = 1.0/18.0; self.w[4] = 1.0/18.0; self.w[5] = 1.0/18.0; self.w[6] = 1.0/18.0;
+ self.w[7] = 1.0/36.0; self.w[8] = 1.0/36.0; self.w[9] = 1.0/36.0; self.w[10] = 1.0/36.0; self.w[11] = 1.0/36.0; self.w[12] = 1.0/36.0;
+ self.w[13] = 1.0/36.0; self.w[14] = 1.0/36.0; self.w[15] = 1.0/36.0; self.w[16] = 1.0/36.0; self.w[17] = 1.0/36.0; self.w[18] = 1.0/36.0;
+
meq_vec(self, rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(self, rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
cal_local_force(self,i,j,k)
transfer the external force to a vector
@ti.func
+def cal_local_force(self,i,j,k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ return f
+
collision()
defines the collision of LBM process
#taichi kernel for parallization
+@ti.kernel
+def colission(self):
+ #outer loop for every index in rho field
+ for i,j,k in self.rho:
+ #if is not solid and it is not on the boundary
+ if (self.solid[i,j,k] == 0 and i<self.nx and j<self.ny and k<self.nz):
+ #calculate S*(m-meq)
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ #add force if there is force, here use Guo's force scheme
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+ #calculate density distribution function after collision f=M^-1*S*(m-meq)
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+
periodic_index(self,i)
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(self,i):
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = self.nx-1
+ #x-right
+ if i[0]>self.nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = self.ny-1
+ #y-right
+ if i[1]>self.ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = self.nz-1
+ #z-right
+ if i[2]>self.nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming prcoess of denisty distribution function
#taichi kernel for parallization
+@ti.kernel
+def streaming1(self):
+ #grouped index which loop the index of rho
+ for i in ti.grouped(self.rho):
+ # streaming for fluid and non-boundary
+ if (self.solid[i] == 0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ for s in ti.static(range(19)):
+ # streaming according to the lattice speed and on boundary with periodic index
+ ip = self.periodic_index(i+self.e[s])
+ if (self.solid[ip]==0):
+ # fluid new density distribution function equals the streaming of old density distribution fuction
+ self.F[ip][s] = self.f[i][s]
+ else:
+ #solid bounce back scheme
+ self.F[i][self.LR[s]] = self.f[i][s]
+ #print(i, ip, "@@@")
+
Boundary_condition()
define three direction fixed pressure or fixed velocity bounary condition
@ti.kernel
+def Boundary_condition(self):
+#fixed pressure boundary condition
+ if ti.static(self.bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[1,j,k]>0):
+ # if the boundary is fluid but the neighbour is solid then the density distribution
+ #function equals to the solid velcity equilibrium density distribution fucntion
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[1,j,k])
+ else:
+ # if the boundary is fluid and the neighbour is fluid then the density distribution
+ #function equals to equilibrium density distribution fucntion on the boundary
+ self.F[0,j,k][s]=self.feq(s, self.rho_bcxl, self.v[0,j,k])
+ #fixed velocity boundary condition
+ if ti.static(self.bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ # if the boundary is fluid new density distribution fucntion equals to equilibrium density
+ #distibution function with fixed velocity
+ if (self.solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ self.F[0,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_left))
+ # fixed pressure boundary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[self.nx-2,j,k]>0):
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-2,j,k])
+ else:
+ self.F[self.nx-1,j,k][s]=self.feq(s, self.rho_bcxr, self.v[self.nx-1,j,k])
+ # fixed velocity boubndary condition on x-right similar for x-left
+ if ti.static(self.bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ if (self.solid[self.nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[nx-1,j,k][s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+ self.F[self.nx-1,j,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_x_right))
+
+ # Direction Y
+ #fixed pressure boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,1,k]>0):
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,1,k])
+ else:
+ self.F[i,0,k][s]=self.feq(s, self.rho_bcyl, self.v[i,0,k])
+ #fixed velocity boundary condition on y-left similar for x direction
+ if ti.static(self.bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,0,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_left[None])-self.F[i,0,k][LR[s]]+self.feq(s,1.0,self.bc_vel_y_left[None])
+ self.F[i,0,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_left))
+ #fixed pressure boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,self.ny-2,k]>0):
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-2,k])
+ else:
+ self.F[i,self.ny-1,k][s]=self.feq(s, self.rho_bcyr, self.v[i,self.ny-1,k])
+ #fixed velocity boundary condition on y-right similar for x direction
+ if ti.static(self.bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ if (self.solid[i,self.ny-1,k]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,self.ny-1,k][s]=self.feq(self.LR[s], 1.0, self.bc_vel_y_right[None])-self.F[i,self.ny-1,k][self.LR[s]]+self.feq(s,1.0,self.bc_vel_y_right[None])
+ self.F[i,self.ny-1,k][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_y_right))
+
+ # Z direction
+ #fixed pressure boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,1]>0):
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,1])
+ else:
+ self.F[i,j,0][s]=self.feq(s, self.rho_bczl, self.v[i,j,0])
+ #fixed velocity boundary condition on z-left similar for x direction
+ if ti.static(self.bc_z_left==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,0][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_left[None])-self.F[i,j,0][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_left[None])
+ self.F[i,j,0][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_left))
+ #fixed pressure boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ if (self.solid[i,j,self.nz-2]>0):
+ self.F[i,j,self.nz-1,s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-2])
+ else:
+ self.F[i,j,self.nz-1][s]=self.feq(s, self.rho_bczr, self.v[i,j,self.nz-1])
+ #fixed velocity boundary condition on z-right similar for x direction
+ if ti.static(self.bc_z_right==2):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ if (self.solid[i,j,self.nz-1]==0):
+ for s in ti.static(range(19)):
+ #self.F[i,j,self.nz-1][s]=self.feq(self.LR[s], 1.0, self.bc_vel_z_right[None])-self.F[i,j,self.nz-1][self.LR[s]]+self.feq(s,1.0,self.bc_vel_z_right[None])
+ self.F[i,j,self.nz-1][s]=self.feq(s,1.0,ti.Vector(self.bc_vel_z_right))
+
streaming3()
calculatet the macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ #print(i.x, i.y, i.z)
+ #if it is fluid and not on the boundary
+ if (self.solid[i]==0 and i.x<self.nx and i.y<self.ny and i.z<self.nz):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ #calculate density
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+
+ self.v[i] /= self.rho[i]
+ #calculate velocity
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ # if it is solid the velocity is zero and the density equals one
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
these function set bnoundary velocity, set viscosity,force and get and calculate maximum velocity
+#get maxium velocity
+def get_max_v(self):
+ self.max_v[None] = -1e10
+ self.cal_max_v()
+ return self.max_v[None]
+
+#calculate maximum velocity with taichi kernel
+@ti.kernel
+def cal_max_v(self):
+ for I in ti.grouped(self.rho):
+ ti.atomic_max(self.max_v[None], self.v[I].norm())
+
+#set x-right velocity
+def set_bc_vel_x1(self, vel):
+ self.bc_x_right = 2
+ self.vx_bcxr = vel[0]; self.vy_bcxr = vel[1]; self.vz_bcxr = vel[2];
+#set x-left velocity
+def set_bc_vel_x0(self, vel):
+ self.bc_x_left = 2
+ self.vx_bcxl = vel[0]; self.vy_bcxl = vel[1]; self.vz_bcxl = vel[2];
+#set y-right velocity
+def set_bc_vel_y1(self, vel):
+ self.bc_y_right = 2
+ self.vx_bcyr = vel[0]; self.vy_bcyr = vel[1]; self.vz_bcyr = vel[2];
+#set y-left velocity
+def set_bc_vel_y0(self, vel):
+ self.bc_y_left = 2
+ self.vx_bcyl = vel[0]; self.vy_bcyl = vel[1]; self.vz_bcyl = vel[2];
+#set z-right velocity
+def set_bc_vel_z1(self, vel):
+ self.bc_z_right = 2
+ self.vx_bczr = vel[0]; self.vy_bczr = vel[1]; self.vz_bczr = vel[2];
+#set z-left velocity
+def set_bc_vel_z0(self, vel):
+ self.bc_z_left = 2
+ self.vx_bczl = vel[0]; self.vy_bczl = vel[1]; self.vz_bczl = vel[2];
+#set x-left density
+def set_bc_rho_x0(self, rho):
+ self.bc_x_left = 1
+ self.rho_bcxl = rho
+#set x-right density
+def set_bc_rho_x1(self, rho):
+ self.bc_x_right = 1
+ self.rho_bcxr = rho
+#set y-left density
+def set_bc_rho_y0(self, rho):
+ self.bc_y_left = 1
+ self.rho_bcyl = rho
+#set y-right density
+def set_bc_rho_y1(self, rho):
+ self.bc_y_right = 1
+ self.rho_bcyr = rho
+#set z-left density
+def set_bc_rho_z0(self, rho):
+ self.bc_z_left = 1
+ self.rho_bczl = rho
+#set z-right density
+def set_bc_rho_z1(self, rho):
+ self.bc_z_right = 1
+ self.rho_bczr = rho
+
+#set viscosity
+def set_viscosity(self,niu):
+ self.niu = niu
+#set external force
+def set_force(self,force):
+ self.fx = force[0]; self.fy = force[1]; self.fz = force[2];
+
export_VTK(self, n)
function export results to vtk file use the package pyevtk
def export_VTK(self, n):
+#the function takes three arguments: the filename,coordinate system and the dictionary for reuslts
+ gridToVTK(
+ #file name
+ "./LB_SingelPhase_"+str(n),
+ #coordinate
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ #the three dictionary which the key is solid,rho,velocity and it will be output to the vtk file
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
step()
function define the simulation process of this solver
def step(self):
+ self.colission()
+ self.streaming1()
+ self.Boundary_condition()
+ self.streaming3()
+
This file use the LBM_3D_SinglePhase_Solver to simulate the cavity flow
+#import certain packages
+import time
+import taichi as ti
+
+ti.init(arch=ti.cpu, dynamic_index=False, kernel_profiler=False, print_ir=False)
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#set 50*50*50 cavity based on LB3D_Solver_Single_Phase solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=50,ny=50,nz=50, sparse_storage=False)
+
+#import geometry data
+lb3d.init_geo('./geo_cavity.dat')
+#set the x-right velocity
+lb3d.set_bc_vel_x1([0.0,0.0,0.1])
+#initialize
+lb3d.init_simulation()
+
+#simulation step
+for iter in range(2000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #get the maximum velocity
+ max_v = lb3d.get_max_v()
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the number of time steps, maxiumum force and the force scale=0
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, max_v, 0.0))
+ #every 1000 time steps export the vtk file
+ if (iter%1000==0):
+ lb3d.export_VTK(iter)
+
This file simulate the porous medium based on the LBM_3D_SinglePhase_Solver
+#import time and taichi package
+import time
+import taichi as ti
+#taichi intialization
+ti.init(arch=ti.cpu)
+#import the LBM_3D_SinglePhase_Solver
+import LBM_3D_SinglePhase_Solver as lb3dsp
+#set the time
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+
+#create the 131*131*131 gird LBM_3D_SinglePhase_Solver
+lb3d = lb3dsp.LB3D_Solver_Single_Phase(nx=131,ny=131,nz=131)
+#import the porous medium geometry
+lb3d.init_geo('./img_ftb131.txt')
+#set x-left and x-right density
+lb3d.set_bc_rho_x1(0.99)
+lb3d.set_bc_rho_x0(1.0)
+#initialize the simulation
+lb3d.init_simulation()
+#simulation loop
+for iter in range(50000+1):
+ lb3d.step()
+
+ if (iter%500==0):
+ #calculate the time
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+ #print the time
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ #print the time step, max force=10, force_scale=10
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+ #export VTK every 2000 time step
+ if (iter%2000==0):
+ lb3d.export_VTK(iter)
+
This file generate geometry file for solver to read
+#import certain module
+import numpy as np
+import math
+
+
+#define the input file name
+# INPUT STL FILE NAME
+output_name = 'geo_cavity.dat'
+
+#define the grid resolution
+dnx, dny, dnz = 50, 50, 50
+
+#==========================================================
+# DO NOT CHANGE BELOW
+#==========================================================
+
+#define an matrix dnx*dny*dnz with zero values
+out_dat = np.zeros((dnx,dny,dnz))
+
+#=======Can define some geometry here to out_dat=========
+#define the boundary to be solid
+out_dat[0,:,:] = 1
+#cout_dat[:,:,0] = 1
+out_dat[:,0,:] = 1
+out_dat[:,-1,:] = 1
+out_dat[:,:,0] = 1
+out_dat[:,:,-1] = 1
+
+#=========================================================
+#reshape the data to be column major
+out_dat = out_dat.reshape(out_dat.size, order = 'F')
+
+
+#output the transfer of out_dat to the file with integer type
+np.savetxt(output_name,out_dat.T,fmt='%d')
+
This file is the non-objective oriented version of singlephase solver without using class. +At the begining of the this file it define some variable first.
+#import some package
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+#initialize taichi with cpu, dunamic index, disable profiler and disables printing the intermediate representation
+ti.init(arch=ti.cpu, dynamic_index=True, kernel_profiler=False, print_ir=False)
+#enable projection
+enable_projection = True
+#nx,ny,nz = 100,50,5
+#define 131x131x131 and zero external force
+nx,ny,nz = 131,131,131
+fx,fy,fz = 0.0e-6,0.0,0.0
+#viscosity=0.1
+niu = 0.1
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 1, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 1, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+#define old density distribution funciton nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define new density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#define density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#define velocity nx*ny*nz
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#define lattice speed 3*19
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#define s diagonal 19 dimension vector
+S_dig = ti.field(ti.f32,shape=(19))
+#define another lattice speed 3*19
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#define weight parameter 19 dimesnion vector
+w = ti.field(ti.f32, shape=(19))
+#define solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#define vector for streaming 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+#define external force with a 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+#define velocity in x,y,z direction with 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#define transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#define inverse of transforming matrix
+inv_M = ti.field(ti.f32, shape=(19,19))
+#define single relaxation parameter
+tau_f=3.0*niu+0.5
+#define single relaxation frequency
+s_v=1.0/tau_f
+#define other parameter in the s diagonal
+s_other=8.0*(2.0-s_v)/(8.0-s_v)
+#define s matrix but not used
+S_np = np.zeros((19,19))
+S_np[0,0]=0; S_np[1,1]=s_v; S_np[2,2]=s_v; S_np[3,3]=0; S_np[4,4]=s_other; S_np[5,5]=0;
+S_np[6,6]=s_other; S_np[7,7]=0; S_np[8,8]=s_other; S_np[9,9]=s_v; S_np[10,10]=s_v; S_np[11,11]=s_v;
+S_np[12,12]=s_v; S_np[13,13]=s_v; S_np[14,14]=s_v; S_np[15,15]=s_v; S_np[16,16]=s_other; S_np[17,17]=s_other;
+S_np[18,18]=s_other
+#define numpy array version of s diagonal.
+S_dig_np = np.array([0,s_v,s_v,0,s_other,0,s_other,0,s_other, s_v, s_v,s_v,s_v,s_v,s_v,s_v,s_other,s_other,s_other])
+#define numpy version of transforming matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#define inverse of transforming matrix using inv function in linalg package
+inv_M_np = np.linalg.inv(M_np)
+#define index for streaming
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#assign numpy version to M.np to M
+M.from_numpy(M_np)
+#assign numpy version of inverser matrix inv_M_np to inv_M
+inv_M.from_numpy(inv_M_np)
+#assign numpy versio of LR array to LR
+LR.from_numpy(LR_np)
+#assign fx,fy,fz to vector external force
+ext_f[None] = ti.Vector([fx,fy,fz])
+#assign numpy version of S diagnal S_dig_np to S_dig
+S_dig.from_numpy(S_dig_np)
+#make inv_M,M,LR,S_dig not modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+ti.static(S_dig)
+
+#create mesh nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#numpy meshgrid from x,y,z 1d array to 3d array X,Y,Z here use ij indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local,u)
calculate the equilibrium density distribution function in velocity space
# taichi funciton
+@ti.func
+def feq(k,rho_local, u):
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #calculate the equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
initialize velocity=0, density=1 and denisty distribution function= equilibrium density distribution function
@ti.kernel
+def init():
+ for i,j,k in rho:
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ #print(F[i,j,k,s], feq(s,1.0,v[i,j,k]))
+
init_geo()
load geometry file
def init_geo(filename):
+ #load data
+ in_dat = np.loadtxt(filename)
+ #reshape it with column major
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+ return in_dat
+
static_init()
initialize lattixe speed weight parameter and boundary velocity
@ti.kernel
+def static_init():
+if ti.static(enable_projection): # No runtime overhead
+ #initialize lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #initialize lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #intialize weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #intialize boundary velocity
+ bc_vel_x_left[None] = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right[None] = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left[None] = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right[None] = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left[None] = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right[None] = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M
calculate denisty distribution function in momentum space M*f=m
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in range(19):
+ for s in range(19):
+ #calculte m=M*f here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
GuoF(i,j,k,s,u)
calculate Guo’s Force scheme
@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in range(19):
+ #calculate Guo's force here
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
meq_vec(rho_local,u)
calculate equilibrium density distribution function in momentum space
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
collison()
define the prcoess of collision
@ti.kernel
+def colission():
+ for i,j,k in rho:
+ #if it is fluid
+ if (solid[i,j,k] == 0):
+ #calculate m
+ m_temp = multiply_M(i,j,k)
+ #calculate meq
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ for s in range(19):
+ #calculate -s*(m-meq)
+ m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #add Guo's force
+ m_temp[s] += (1-0.5*S_dig[s])*GuoF(i,j,k,s,v[i,j,k])
+
+ for s in range(19):
+ f[i,j,k,s] = 0
+ for l in range(19):
+ #f=-M^-1*S(m-meq)
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
periodic_index(i)
set the bounary index with periodic bounary condition
@ti.func
+def periodic_index(i):
+ #inner index
+ iout = i
+ #x-left
+ if i[0]<0: iout[0] = nx-1
+ #x-right
+ if i[0]>nx-1: iout[0] = 0
+ #y-left
+ if i[1]<0: iout[1] = ny-1
+ #y-right
+ if i[1]>ny-1: iout[1] = 0
+ #z-left
+ if i[2]<0: iout[2] = nz-1
+ #z-right
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
streaming1()
defines the streaming process of denisty distibution function
@ti.kernel
+def streaming1():
+ for i in ti.grouped(rho):
+ #if it is fluid
+ if (solid[i] == 0):
+ for s in range(19):
+ #the neighbour index
+ ip = periodic_index(i+e[s])
+ #if neighbour index is fluid just streaming
+ if (solid[ip]==0):
+ F[ip,s] = f[i,s]
+ #if neighbour index is solid just bounce back
+ else:
+ F[i,LR[s]] = f[i,s]
+ #print(i, ip, "@@@")
+
streaming2()
a simple streaming process without consideration of solid and boundary
@ti.kernel
+def streaming2():
+ for i in ti.grouped(rho):
+ for s in range(19):
+ f[i,s] = F[i,s]
+
Boudary_condition()
define the bounary condition of fixed pressure and fixed velocity
@ti.kernel
+def Boundary_condition():
+ #pressure-boundary condtion x-left
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #if boundary is fluid but the neighbour is solid
+ #equilibrium density distribution function is calculated based on the neighbour velocity
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ #if boundary is fluid and the neighbour is also fluid
+ #equilibrium density distribution function is calculated based on the boundary velocity
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ #velocity-boundary conditon x-left
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in range(19):
+ #calculate density distribution fucntion based on equilibrium part and non-equilibrium part
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+
+ #pressure boundary condition x-right similar to x-left
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ #velocity booundary condition x-right similar to x-left
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in range(19):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None]) #!!!!!!change velocity in feq into vector
+
streaming3()
calculate the macroscopic variable
@ti.kernel
+def streaming3():
+ for i in ti.grouped(rho):
+ #if it is fluid calculate density and velocity based on density distribution function
+ if (solid[i]==0):
+ rho[i] = 0
+ v[i] = ti.Vector([0,0,0])
+ for s in range(19):
+ f[i,s] = F[i,s]
+ rho[i] += f[i,s]
+ v[i] += e_f[s]*f[i,s]
+
+ v[i] /= rho[i]
+ v[i] += (ext_f[None]/2)/rho[i]
+ # if it is solid set denisty equals one and velocity equals zero
+ else:
+ rho[i] = 1.0
+ v[i] = ti.Vector([0,0,0])
+
At the end of the file do the actual simulation and export the data
+#define some time varible
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+dt_count = 0
+
+#import the solid flag data
+#solid_np = init_geo('./BC.dat')
+solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+
+# do the initialization
+static_init()
+init()
+
+# do the actual simulation
+for iter in range(50000+1):
+ colission()
+ streaming1()
+ Boundary_condition()
+ #streaming2()
+ streaming3()
+ # calculate every 1000 time step
+ if (iter%1000==0):
+
+ time_pre = time_now
+ time_now = time.time()
+ #calculate the time difference between now and previous time step
+ diff_time = int(time_now-time_pre)
+ #calculate the time difference between now and the initial time
+ elap_time = int(time_now-time_init)
+ #divmod function return the quotient and the remainder
+ #so that h_diff,m_diff and s_diff represent the hour, minute and second. the same as the h_elap,m_elap and s_elap
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+
+ #export every 1000 timestep to vtk with x,y,z coordinate and solid,density and velocity variable
+ if (iter%10000==0):
+ gridToVTK(
+ "./structured"+str(iter),
+ x,
+ y,
+ z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(solid.to_numpy()),
+ "rho": np.ascontiguousarray(rho.to_numpy()),
+ "velocity": (np.ascontiguousarray(v.to_numpy()[:,:,:,0]), np.ascontiguousarray(v.to_numpy()[:,:,:,1]),np.ascontiguousarray(v.to_numpy()[:,:,:,2]))
+ }
+ )
+# ti.sync()
+# ti.profiler.print_kernel_profiler_info()
+#print the profiler information of every kernel and task of taichi in this file
+ti.profiler.print_scoped_profiler_info()
+
This solver is almost similar to lbm_solver_3d expect several difference as follows: +.. number:: lbm_solver_3d_cavity
+The Grid resolution in this solver is 50x50x50
The viscosity in this solver is 0.16667
The boundary condition in this solver is velocity solver on x-right as follows
boundary condition of this solver
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 2, 1.0, 0.0, 0.0, 0.1 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
x-right is implementated with velocity boundary condition
+4. The boundary condition implementation is different from lbm_solver_3d, in this solver, the density distribution +function is calculated based on velocity on the boundary.
+if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ #F[0,j,k][s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None]) #!!!!!!change velocity in feq into vector
+ F[0,j,k][s]=feq(s,1.0,ti.Vector(bc_vel_x_left))
+
Finally, the definition of the varible is slightly different from lbm_solver_3d
This solver is almost similar to lbm_solver_3d expect the sparse definition of some varible:
+f = ti.field(ti.f32)
+F = ti.field(ti.f32)
+rho = ti.field(ti.f32)
+v = ti.Vector.field(3, ti.f32)
+n_mem_partition = 3
+
+cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(v)
+
+cell2 = ti.root.pointer(ti.ijkl,(nx//3+1,ny//3+1,nz//3+1,1))
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(f)
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(F)
+
It use a pointer and certain block to divide the region and then place different varible on the block which make the storage +sparse.
+This file is the solver for solute transportation
+First import the certain package and define the class of LB3D_Solver_Single_Phase_Solute
which inheritant from
+LB3D_Solver_Single_Phase_Solute
from sympy import inverse_mellin_transform
+import taichi as ti
+import numpy as np
+from pyevtk.hl import gridToVTK
+import time
+
+#ti.init(arch=ti.cpu, dynamic_index=False, kernel_profiler=False, print_ir=False)
+import LBM_3D_SinglePhase_Solver as lb3d
+
+@ti.data_oriented
+class LB3D_Solver_Single_Phase_Solute(lb3d.LB3D_Solver_Single_Phase):
+ def __init__(self, nx, ny, nz):
+ super(LB3D_Solver_Single_Phase_Solute, self).__init__(nx, ny, nz, sparse_storage = False)
+ #define solute boundary condition
+ self.solute_bc_x_left, self.solute_bcxl = 0, 0.0
+ self.solute_bc_x_right, self.solute_bcxr = 0, 0.0
+ self.solute_bc_y_left, self.solute_bcyl = 0, 0.0
+ self.solute_bc_y_right, self.solute_bcyr = 0, 0.0
+ self.solute_bc_z_left, self.solute_bczl = 0, 0.0
+ self.solute_bc_z_right, self.solute_bczr = 0, 0.0
+
+ #define parameters for bouyancy force
+ self.buoyancy_parameter = 20.0 #Buoyancy Parameter (0= no buoyancy)
+ self.ref_T = 20.0 #reference_psi F=/rho*g+Bouyancy*(/psi-reference_psi)*g)
+ #define gravity
+ self.gravity = 5e-7
+
+ #define concentration distribution function
+ self.fg = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define another concentration distribution function
+ self.Fg = ti.Vector.field(19,ti.f32,shape=(nx,ny,nz))
+ #define external force
+ self.forcexyz = ti.Vector.field(3,ti.f32,shape=(nx,ny,nz))
+ #define entropy
+ self.rho_H = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define temperature
+ self.rho_T = ti.field(ti.f32, shape=(nx,ny,nz))
+ #define liquid volumn fraction
+ self.rho_fl = ti.field(ti.f32, shape=(nx,ny,nz))
+
+ #define specific heat of liquid
+ self.Cp_l= 1.0
+ #define specific heat of solid
+ self.Cp_s = 1.0
+ #define latent heat
+ self.Lt = 1.0
+ #define solid temperature
+ self.T_s = -10.0
+ #define liquid temperature
+ self.T_l = -10.0
+ #define viscosity of solid
+ self.niu_s = 0.002
+ #define viscosity of liquid
+ self.niu_l = 0.002
+
+ #define energy of solid
+ self.H_s = None
+ #define energy of liquid
+ self.H_l = None
+
+ #define rock thermal diffusivity
+ self.niu_solid = 0.001
+ #define specific heat of rock
+ self.Cp_solid = 1.0
+
An then it sets these parameters with functions
+#set gravity
+def set_gravity(self, gravity):
+self.gravity = gravity
+#set buoyancy force parameter
+def set_buoyancy_parameter(self, buoyancy_param):
+ self.buoyancy_parameter = buoyancy_param
+#set reference temperature
+def set_ref_T(self, ref_t):
+ self.ref_T = ref_t
+#set specific heat of solid
+def set_specific_heat_solid(self, cps):
+ self.Cp_s = cps
+#set specfic heat of liquid
+def set_specific_heat_liquid(self, cpl):
+ self.Cp_l = cpl
+#set specfic heat of rock
+def set_specific_heat_rock(self, cprock):
+ self.Cp_solid = cprock
+#set latent heat
+def set_latent_heat(self, ltheat):
+ self.Lt = ltheat
+#set solidus temperature
+def set_solidus_temperature(self, ts):
+ self.T_s = ts
+#set liquidus temperature
+def set_liquidus_temperature(self, tl):
+ self.T_l = tl
+#set solid thermal diffusivity
+def set_solid_thermal_diffusivity(self, nius):
+ self.niu_s = nius
+#set liquid thermal diffusivity
+def set_liquid_thermal_diffusivity(self, niul):
+ self.niu_l = niul
+#set rock thermal diffusivity
+def set_rock_thermal_diffusivity(self, niurock):
+ self.niu_solid = niurock
+#set adiabatic boundary on x-left
+def set_bc_adiabatic_x_left(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_x_left = 2
+#set adiabatic boundary on x-right
+def set_bc_adiabatic_x_right(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_x_right = 2
+#set adiabatic boundary on y-left
+def set_bc_adiabatic_y_left(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_y_left = 2
+#set adiabatic boundary on y-right
+def set_bc_adiabatic_y_right(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_y_right = 2
+#set adiabatic boundary on z-left
+def set_bc_adiabatic_z_left(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_z_left = 2
+#set adiabatic boundary on z-right
+def set_bc_adiabatic_z_right(self, bc_ad):
+ if (bc_ad==True):
+ self.solute_bc_z_right = 2
+#set constant temperature on x-left
+def set_bc_constant_temperature_x_left(self,xl):
+ self.solute_bc_x_left = 1
+ self.solute_bcxl = xl
+#set constant temperature on x-right
+def set_bc_constant_temperature_x_right(self,xr):
+ self.solute_bc_x_right = 1
+ self.solute_bcxr = xr
+#set constant temperature on y-left
+def set_bc_constant_temperature_y_left(self,yl):
+ self.solute_bc_y_left = 1
+ self.solute_bcyl = yl
+#set constant temperature on y-right
+def set_bc_constant_temperature_y_right(self,yr):
+ self.solute_bc_y_right = 1
+ self.solute_bcyr = yr
+#set constant temperature on z-left
+def set_bc_constant_temperature_z_left(self,zl):
+ self.solute_bc_z_left = 1
+ self.solute_bczl = zl
+#set constant temperature on z-right
+def set_bc_constant_temperature_z_right(self,zr):
+ self.solute_bc_y_right = 1
+ self.solute_bczr = zr
+
+# update energy of solid and liquid
+def update_H_sl(self):
+ #energy of solid
+ self.H_s = self.Cp_s*self.T_s
+ #energy of liquid
+ self.H_l = self.H_s+self.Lt
+ print('H_s',self.H_s)
+ print('H_l',self.H_l)
+
Then it initialize some variable or function
+#intialize the energy
+@ti.kernel
+def init_H(self):
+ for I in ti.grouped(self.rho_T):
+ #calculate the energy, convert_T_H() define later
+ self.rho_H[I] = self.convert_T_H(self.rho_T[I])
+
+#intialize the density distribiution function for solute concentration
+@ti.kernel
+def init_fg(self):
+ for I in ti.grouped(self.fg):
+ #calculate the overall specific heat
+ Cp = self.rho_fl[I]*self.Cp_l + (1-self.rho_fl[I])*self.Cp_s
+ #intialize the density distribiution function for solute concentration equals equilibrium density distribiution function for solute concentration
+ for s in ti.static(range(19)):
+ self.fg[I][s] = self.g_feq(s,self.rho_T[I],self.rho_H[I], Cp, self.v[I])
+ self.Fg[I][s] = self.fg[I][s]
+
+#intialize the volumn fraction of liquid
+@ti.kernel
+def init_fl(self):
+ for I in ti.grouped(self.rho_T):
+ #convert_T_fl define later
+ self.rho_fl[I] = self.convert_T_fl(self.rho_T[I])
+
g_feq(self, k,local_T,local_H, Cp, u)
calculate the equilibrium density distribiution function for thermal energy
@ti.func
+def g_feq(self, k,local_T,local_H, Cp, u):
+ eu = self.e[k].dot(u)
+ uv = u.dot(u)
+ feqout = 0.0
+ #calculating the zero-velocity equilibrium thermal distribution function
+ if (k==0):
+ feqout = local_H-Cp*local_T+self.w[k]*Cp*local_T*(1-1.5*uv)
+ else:
+ #calculating other directions equilibrium thermal distribution function
+ feqout = self.w[k]*Cp*local_T*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, self.w[k], feqout, Cp, local_T)
+ return feqout
+
cal_local_force(i, j, k)
calculates buoyancy force
#density is the function of temperture delat(rho)=-rho*beta*delta(T)
+@ti.func
+def cal_local_force(self, i, j, k):
+ f = ti.Vector([self.fx, self.fy, self.fz])
+ f[1] += self.gravity*self.buoyancy_parameter*(self.rho_T[i,j,k]-self.ref_T)
+ #f= delta(rho)*delta(v)*g
+ f *= self.rho_fl[i,j,k]
+ return f
+
collision_g()
defines the the collision of thermal distribution function
@ti.kernel
+def colission_g(self):
+ for I in ti.grouped(self.rho_T):
+ #overall relaxation time
+ tau_s = 3*(self.niu_s*(1.0-self.rho_fl[I])+self.niu_l*self.rho_fl[I])+0.5
+ #overall specific heat
+ Cp = self.rho_fl[I]*self.Cp_l + (1-self.rho_fl[I])*self.Cp_s
+
+ #ROCK overall relaxation time and specific heat
+ if (self.solid[I] >0):
+ tau_s = 3.0*self.niu_solid+0.5
+ Cp = self.Cp_solid
+
+ #f=f-1/tau*(f-feq)
+ for s in ti.static(range(19)):
+ tmp_fg = -1.0/tau_s*(self.fg[I][s]-self.g_feq(s,self.rho_T[I],self.rho_H[I], Cp, self.v[I]))
+ #print(self.fg[I][s],tmp_fg,I,s,self.rho_H[I],self.g_feq(s,self.rho_T[I],self.rho_H[I], Cp, self.v[I]))
+ self.fg[I][s] += tmp_fg
+
collision()
defines the the collision of density distribution function
@ti.kernel
+def colission(self):
+ for i,j,k in self.rho:
+ #if (self.solid[i,j,k] == 0):
+ m_temp = self.M[None]@self.F[i,j,k]
+ meq = self.meq_vec(self.rho[i,j,k],self.v[i,j,k])
+ m_temp -= self.S_dig[None]*(m_temp-meq)
+ f = self.cal_local_force(i,j,k)
+ if (ti.static(self.force_flag==1)):
+ for s in ti.static(range(19)):
+ # m_temp[s] -= S_dig[s]*(m_temp[s]-meq[s])
+ #f = self.cal_local_force()
+ f_guo=0.0
+ for l in ti.static(range(19)):
+ f_guo += self.w[l]*((self.e_f[l]-self.v[i,j,k]).dot(f)+(self.e_f[l].dot(self.v[i,j,k])*(self.e_f[l].dot(f))))*self.M[None][s,l]
+ #m_temp[s] += (1-0.5*self.S_dig[None][s])*self.GuoF(i,j,k,s,self.v[i,j,k],force)
+ m_temp[s] += (1-0.5*self.S_dig[None][s])*f_guo
+
+ self.f[i,j,k] = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ #calculate the denisty distribution function in momentum space here
+ self.f[i,j,k] += self.inv_M[None]@m_temp
+ #calculate the fluid density distribution function here
+ for s in ti.static(range(19)):
+ self.f[i,j,k][s] = self.f[i,j,k][s]*(self.rho_fl[i,j,k]) + self.w[s]*(1.0-self.rho_fl[i,j,k])
+
streaming1()
and streaming1_g()
defines the fluid denisty distribiution function and
+thermal density distribiution function
@ti.kernel
+def streaming1(self):
+ for i in ti.grouped(self.rho):
+ #if (self.solid[i] == 0):
+ for s in ti.static(range(19)):
+ ip = self.periodic_index(i+self.e[s])
+ self.F[ip][s] = self.f[i][s]
+
+@ti.kernel
+def streaming1_g(self):
+ for i in ti.grouped(self.rho_T):
+ for s in ti.static(range(19)):
+ ip = self.periodic_index(i+self.e[s])
+ self.Fg[ip][s] = self.fg[i][s]
+
this
+@ti.kernel
+def BC_concentration(self):
+ #constant temperature boundary condition
+ if ti.static(self.solute_bc_x_left==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ local_T = self.solute_bcxl
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[0,j,k]*self.Cp_l + (1-self.rho_fl[0,j,k])*self.Cp_s
+ #the boundary's thermal distribution function equals the equilibrium thermal distribution function on the boundary
+ for s in ti.static(range(19)):
+ self.fg[0,j,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[0,j,k])
+ self.Fg[0,j,k][s] = self.fg[0,j,k][s]
+ #adiabatic boundary condition
+ elif ti.static(self.solute_bc_x_left==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ for s in ti.static(range(19)):
+ #there is no thermal transfer between the boundaty and neighbouring cell
+ self.fg[0,j,k][s] = self.fg[1,j,k][s]
+ self.Fg[0,j,k][s] = self.fg[1,j,k][s]
+
+ #x-right
+ if ti.static(self.solute_bc_x_right==1):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ local_T = self.solute_bcxr
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[self.nx-1,j,k]*self.Cp_l + (1-self.rho_fl[self.nx-1,j,k])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[self.nx-1,j,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[self.nx-1,j,k])
+ self.Fg[self.nx-1,j,k][s]= self.fg[self.nx-1,j,k][s]
+ elif ti.static(self.solute_bc_x_right==2):
+ for j,k in ti.ndrange((0,self.ny),(0,self.nz)):
+ for s in ti.static(range(19)):
+ self.fg[self.nx-1,j,k][s] = self.fg[self.nx-2,j,k][s]
+ self.Fg[self.nx-1,j,k][s] = self.fg[self.nx-2,j,k][s]
+
+ #y-left
+ if ti.static(self.solute_bc_y_left==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ local_T = self.solute_bcyl
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,0,k]*self.Cp_l + (1-self.rho_fl[i,0,k])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,0,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,0,k])
+ self.Fg[i,0,k][s] = self.fg[i,0,k][s]
+ elif ti.static(self.solute_bc_y_left==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ for s in ti.static(range(19)):
+ self.fg[i,0,k][s] = self.fg[i,1,k][s]
+ self.Fg[i,0,k][s] = self.fg[i,1,k][s]
+
+ #y-right
+ if ti.static(self.solute_bc_y_right==1):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ local_T = self.solute_bcyr
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,self.ny-1,k]*self.Cp_l + (1-self.rho_fl[i,self.ny-1,k])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,self.ny-1,k][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,self.ny-1,k])
+ self.Fg[i,self.ny-1,k][s] = self.fg[i,self.ny-1,k][s]
+ elif ti.static(self.solute_bc_y_right==2):
+ for i,k in ti.ndrange((0,self.nx),(0,self.nz)):
+ for s in ti.static(range(19)):
+ self.fg[i,self.ny-1,k][s] = self.fg[i,self.ny-2,k][s]
+ self.Fg[i,self.ny-1,k][s] = self.fg[i,self.ny-2,k][s]
+
+ #z-left
+ if ti.static(self.solute_bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ local_T = self.solute_bczl
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,j,0]*self.Cp_l + (1-self.rho_fl[i,j,0])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,j,0][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,j,0])
+ self.Fg[i,j,0][s] = self.fg[i,j,0][s]
+ elif ti.static(self.solute_bc_z_left==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ for s in ti.static(range(19)):
+ self.fg[i,j,0][s] = self.fg[i,j,1][s]
+ self.Fg[i,j,0][s] = self.fg[i,j,1][s]
+
+ #z-right
+ if ti.static(self.solute_bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ local_T = self.solute_bczr
+ local_H = self.convert_T_H(local_T)
+ Cp = self.rho_fl[i,j,self.nz-1]*self.Cp_l + (1-self.rho_fl[i,j,self.nz-1])*self.Cp_s
+
+ for s in ti.static(range(19)):
+ self.fg[i,j,self.nz-1][s] = self.g_feq(s,local_T, local_H, Cp, self.v[i,j,self.nz-1])
+ self.Fg[i,j,self.nz-1][s] = self.fg[i,j,self.nz-1][s]
+ elif ti.static(self.solute_bc_z_right==1):
+ for i,j in ti.ndrange((0,self.nx),(0,self.ny)):
+ for s in ti.static(range(19)):
+ self.fg[i,j,self.nz-1][s] = self.fg[i,j,self.nz-2][s]
+ self.Fg[i,j,self.nz-1][s] = self.fg[i,j,self.nz-2][s]
+
convert_H_T()
calculate the temperature
@ti.func
+def convert_H_T(self,local_H):
+ new_T=0.0
+ #if local enthalpy is less than solid enthalpy
+ #T= enthalpy/specific heat
+ if (local_H<self.H_s):
+ new_T = local_H/self.Cp_s
+ #if if local enthalpy is greater than liquid enthalpy
+ #T= Tliquid+(enthalpy-liquid enthalpy)/speific heat of liquid
+ elif (local_H>self.H_l):
+ new_T = self.T_l+(local_H-self.H_l)/self.Cp_l
+ #if if temperature is greater than solid temperature
+ #T= Tsolid+(enthalpy-solid enthalpy)/(enthalpy of liquid-enthalpy of solid)*(temperature of liquid- temperature of solid)
+ elif (self.T_l>self.T_s):
+ new_T = self.T_s+(local_H-self.H_s)/(self.H_l-self.H_s)*(self.T_l-self.T_s)
+ else:
+ #else T= temperature of solid
+ new_T = self.T_s
+
+ return new_T
+
convert_H_fl()
calculate the volumn fraction of liquid
@ti.func
+def convert_H_fl(self,local_H):
+ new_fl=0.0
+ #if enthalpy is less than solid enthalpy
+ #it is zero
+ if (local_H<self.H_s):
+ new_fl = 0.0
+ #if it is greater than liquid enthalpy
+ #it is one
+ elif (local_H>self.H_l):
+ new_fl = 1.0
+ #else
+ #it equals to (enthaply- soid enthaply)/(enthaply of liquid- enthalpy of solid)
+ else:
+ new_fl = (local_H-self.H_s)/(self.H_l-self.H_s)
+
+ return new_fl
+
convert_T_H()
calculate the enthaply from temperature
@ti.func
+def convert_T_H(self,local_T):
+ new_H = 0.0
+ # calculate enthaply for three different conditions
+ if (local_T<=self.T_s):
+ new_H = self.Cp_s*local_T
+ elif (local_T>self.T_l):
+ new_H = (local_T-self.T_l)*self.Cp_l+self.H_l
+ else:
+ fluid_frc = (local_T-self.T_s)/(self.T_l-self.T_s)
+ new_H = self.H_s*(1-fluid_frc) + self.H_l*fluid_frc
+ return new_H
+
convert_T_fl()
calculate volumn fraction from temperature
@ti.func
+def convert_T_fl(self,local_T):
+ new_fl = 0.0
+ # calculate volumn fraction for three different conditions
+ if (local_T<=self.T_s):
+ new_fl = 0.0
+ elif (local_T>=self.T_l):
+ new_fl = 1.0
+ elif (self.T_l>self.T_s):
+ new_fl = (local_T-self.T_s)/(self.T_l-self.T_s)
+ else:
+ new_fl = 1.0
+
+ return new_fl
+
streaming3()
calculate macroscopic variable
@ti.kernel
+def streaming3(self):
+ for i in ti.grouped(self.rho):
+ self.forcexyz[i] = self.cal_local_force(i.x, i.y, i.z)
+ #print(i.x, i.y, i.z)
+ if ((self.solid[i]==0) or (self.rho_fl[i]>0.0)):
+ self.rho[i] = 0
+ self.v[i] = ti.Vector([0,0,0])
+ self.f[i] = self.F[i]
+ for s in ti.static(range(19)):
+ self.f[i][s] = self.f[i][s]*self.rho_fl[i]+self.w[s]*(1.0-self.rho_fl[i])
+ #density for fluid
+ self.rho[i] += self.f[i].sum()
+
+ for s in ti.static(range(19)):
+ self.v[i] += self.e_f[s]*self.f[i][s]
+
+ f = self.cal_local_force(i.x, i.y, i.z)
+ #velocity for fluid
+ self.v[i] /= self.rho[i]
+ self.v[i] += (f/2)/self.rho[i]
+
+ else:
+ #density and velocity for solid
+ self.rho[i] = 1.0
+ self.v[i] = ti.Vector([0,0,0])
+
streaming3()
calculate enthalpy
@ti.kernel
+def streaming3_g(self):
+ for i in ti.grouped(self.rho_T):
+ self.rho_H[i] = 0.0
+ #enthalpy here
+ self.rho_H[i] = self.Fg[i].sum()
+ #for s in ti.static(range(19)):
+ # self.rho_H[i] += self.Fg[i][s]
+ self.fg[i] = self.Fg[i]
+
update_T_fl()
calculate volumn fraction and temperature
@ti.kernel
+def update_T_fl(self):
+ for I in ti.grouped(self.rho_T):
+ self.rho_T[I] = self.convert_H_T(self.rho_H[I])
+ self.rho_fl[I] = self.convert_H_fl(self.rho_H[I])
+ if (self.solid[I]>0):
+ self.rho_fl[I] = 0.0
+
init_solute_simulation()
initialize the solute simulation
def init_solute_simulation(self):
+
+ self.init_simulation()
+ self.update_H_sl()
+ #ethalpy
+ self.init_H()
+ #volumn fraction
+ self.init_fl()
+ #thermal distribution function
+ self.init_fg()
+
init_concentration(filename)
import concentration data from file
def init_concentration(self,filename):
+ in_dat = np.loadtxt(filename)
+ in_dat = np.reshape(in_dat, (self.nx,self.ny,self.nz),order='F')
+ self.rho_T.from_numpy(in_dat)
+
this
+def step(self):
+ self.colission()
+ self.colission_g()
+
+ self.streaming1()
+ self.streaming1_g()
+
+ self.Boundary_condition()
+ self.BC_concentration()
+
+ self.streaming3_g()
+ self.streaming3()
+ self.streaming3_g()
+
+ self.update_T_fl()
+
this
+def export_VTK(self, n):
+ gridToVTK(
+ "./LB_SingelPhase_"+str(n),
+ self.x,
+ self.y,
+ self.z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(self.solid.to_numpy()),
+ "rho": np.ascontiguousarray(self.rho.to_numpy()),
+ "Solid_Liquid": np.ascontiguousarray(self.rho_fl.to_numpy()),
+ "Tempreture": np.ascontiguousarray(self.rho_T.to_numpy()),
+ "Entropy": np.ascontiguousarray(self.rho_H.to_numpy()),
+ "velocity": ( np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.v.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2])),
+ "Force": ( np.ascontiguousarray(self.forcexyz.to_numpy()[0:self.nx,0:self.ny,0:self.nz,0]),
+ np.ascontiguousarray(self.forcexyz.to_numpy()[0:self.nx,0:self.ny,0:self.nz,1]),
+ np.ascontiguousarray(self.forcexyz.to_numpy()[0:self.nx,0:self.ny,0:self.nz,2]))
+ }
+ )
+
this
+This file is the same as LBM_3D_SinglePhase_Solver in Single_phase folder
+This solver is the multiphase model based on color gradient model +Firstly, it defines some parameters
+# NOTE: THIS CODE NEED taichi_glsl, so please use taichi version <=0.8.5
+#import taichi, numpy, pyevtk and time package
+import taichi as ti
+import numpy as np
+#import taichi_glsl as ts
+from pyevtk.hl import gridToVTK
+import time
+#from taichi_glsl import scalar
+
+#from taichi_glsl.scalar import isinf, isnan
+#from taichi_glsl.vector import vecFill
+#intialize taichi
+ti.init(arch=ti.cpu)
+#ti.init(arch=ti.gpu, dynamic_index=True,offline_cache=True)
+
+#enable projection
+enable_projection = True
+# 131*131*131
+nx,ny,nz = 131,131,131
+#nx,ny,nz = 131,131,131
+#external force in x,y,z direction
+fx,fy,fz = 5.0e-5,-2e-5,0.0
+#niu = 0.1
+#liquid viscosity
+niu_l = 0.1 #psi>0
+#gas viscosity
+niu_g = 0.1 #psi<0
+#psi in color gradient calculation
+psi_solid = 0.7
+#surface tension
+CapA = 0.005
+
+#Boundary condition mode: 0=periodic, 1= fix pressure, 2=fix velocity; boundary pressure value (rho); boundary velocity value for vx,vy,vz
+bc_x_left, rho_bcxl, vx_bcxl, vy_bcxl, vz_bcxl = 0, 1.0, 0.0e-5, 0.0, 0.0 #Boundary x-axis left side
+bc_x_right, rho_bcxr, vx_bcxr, vy_bcxr, vz_bcxr = 0, 0.995, 0.0, 0.0, 0.0 #Boundary x-axis right side
+bc_y_left, rho_bcyl, vx_bcyl, vy_bcyl, vz_bcyl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis left side
+bc_y_right, rho_bcyr, vx_bcyr, vy_bcyr, vz_bcyr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary y-axis right side
+bc_z_left, rho_bczl, vx_bczl, vy_bczl, vz_bczl = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis left side
+bc_z_right, rho_bczr, vx_bczr, vy_bczr, vz_bczr = 0, 1.0, 0.0, 0.0, 0.0 #Boundary z-axis right side
+
+bc_psi_x_left, psi_x_left = 1, -1.0 # boundary condition for phase-field: 0 = periodic,
+bc_psi_x_right, psi_x_right = 0, 1.0 # 1 = constant value on the boundary, value = -1.0 phase1 or 1.0 = phase 2
+bc_psi_y_left, psi_y_left = 0, 1.0
+bc_psi_y_right, psi_y_right = 0, 1.0
+bc_psi_z_left, psi_z_left = 0, 1.0
+bc_psi_z_right, psi_z_right = 0, 1.0
+
+# Non Sparse memory allocation
+#density distribution function nx*ny*nz*19
+f = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#density distribution function nx*ny*nz*19
+F = ti.field(ti.f32,shape=(nx,ny,nz,19))
+#density nx*ny*nz
+rho = ti.field(ti.f32, shape=(nx,ny,nz))
+#velocity nx*ny*nz vector
+v = ti.Vector.field(3,ti.f32, shape=(nx,ny,nz))
+#psi nx*ny*nz
+psi = ti.field(ti.f32, shape=(nx,ny,nz))
+#density r nx*ny*nz
+rho_r = ti.field(ti.f32, shape=(nx,ny,nz))
+#density b nx*ny*nz
+rho_b = ti.field(ti.f32, shape=(nx,ny,nz))
+#density r nx*ny*nz
+rhor = ti.field(ti.f32, shape=(nx,ny,nz))
+#density b nx*ny*nz
+rhob = ti.field(ti.f32, shape=(nx,ny,nz))
+#lattice speed 19 dimensional vector
+e = ti.Vector.field(3,ti.i32, shape=(19))
+#S_dig = ti.field(ti.f32,shape=(19))
+#lattice speed 19 dimensional vector
+e_f = ti.Vector.field(3,ti.f32, shape=(19))
+#weight parameter 19 dimensional vector
+w = ti.field(ti.f32, shape=(19))
+#solid flag nx*ny*nz
+solid = ti.field(ti.i32,shape=(nx,ny,nz))
+#streaming vector 19 dimensional vector
+LR = ti.field(ti.i32,shape=(19))
+
+#external force 3 dimensional vector
+ext_f = ti.Vector.field(3,ti.f32,shape=())
+# x-left velocity 3 dimensional vector
+bc_vel_x_left = ti.Vector.field(3,ti.f32, shape=())
+# x-right velocity 3 dimensional vector
+bc_vel_x_right = ti.Vector.field(3,ti.f32, shape=())
+# y-left velocity 3 dimensional vector
+bc_vel_y_left = ti.Vector.field(3,ti.f32, shape=())
+# y-right velocity 3 dimensional vector
+bc_vel_y_right = ti.Vector.field(3,ti.f32, shape=())
+# z-left velocity 3 dimensional vector
+bc_vel_z_left = ti.Vector.field(3,ti.f32, shape=())
+# z-right velocity 3 dimensional vector
+bc_vel_z_right = ti.Vector.field(3,ti.f32, shape=())
+#transforming matrix 19*19
+M = ti.field(ti.f32, shape=(19,19))
+#inverse transforming matrix 19*19
+inv_M = ti.field(ti.f32, shape=(19,19))
+#parameters for calculating the parameter of s diagonal
+#=======================================#
+lg0, wl, wg = 0.0, 0.0, 0.0
+l1, l2, g1, g2 = 0.0, 0.0, 0.0, 0.0
+wl = 1.0/(niu_l/(1.0/3.0)+0.5)
+wg = 1.0/(niu_g/(1.0/3.0)+0.5)
+lg0 = 2*wl*wg/(wl+wg)
+l1=2*(wl-lg0)*10
+l2=-l1/0.2
+g1=2*(lg0-wg)*10
+g2=g1/0.2
+#=======================================#
+
+#transformation matrix
+M_np = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+[-1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
+[1,-2,-2,-2,-2,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1],
+[0,1,-1,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,-2,2,0,0,0,0,1,-1,1,-1,1,-1,1,-1,0,0,0,0],
+[0,0,0,1,-1,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,-2,2,0,0,1,-1,-1,1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,1,-1,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,0,0,0,0,-2,2,0,0,0,0,1,-1,-1,1,1,-1,-1,1],
+[0,2,2,-1,-1,-1,-1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,-2,-2,1,1,1,1,1,1,1,1,1,1,1,1,-2,-2,-2,-2],
+[0,0,0,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0,0,0,0,0],
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,1,-1,-1,0,0,0,0],
+[0,0,0,0,0,0,0,1,-1,1,-1,-1,1,-1,1,0,0,0,0],
+[0,0,0,0,0,0,0,-1,1,1,-1,0,0,0,0,1,-1,1,-1],
+[0,0,0,0,0,0,0,0,0,0,0,1,-1,-1,1,-1,1,1,-1]])
+#inverde of transforming matrix
+inv_M_np = np.linalg.inv(M_np)
+#streaming array
+LR_np = np.array([0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15,18,17])
+#M matrix from the numpy
+M.from_numpy(M_np)
+#inverse matrix from numpy
+inv_M.from_numpy(inv_M_np)
+
+#steaming array from numpy
+LR.from_numpy(LR_np)
+#external force with vector three dimensional
+ext_f[None] = ti.Vector([fx,fy,fz])
+#set transforming matrix, inverse matrix and streaming vector non-modified
+ti.static(inv_M)
+ti.static(M)
+ti.static(LR)
+
+#set x,y,z array with nx*ny*nz
+x = np.linspace(0, nx, nx)
+y = np.linspace(0, ny, ny)
+z = np.linspace(0, nz, nz)
+#set meshgrid and return three meshgrid matrix X,Y,Z with non-cartesian indexing
+X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
+
feq(k,rho_local, u)
calculate the equilibrium denisty distribution function
@ti.func
+def feq(k,rho_local, u):
+ # eu=ts.vector.dot(e[k],u)
+ # uv=ts.vector.dot(u,u)
+ eu = e[k].dot(u)
+ uv = u.dot(u)
+ #same as single phase equilibrium density distribution function
+ feqout = w[k]*rho_local*(1.0+3.0*eu+4.5*eu*eu-1.5*uv)
+ #print(k, rho_local, w[k])
+ return feqout
+
init()
intialize some variable
@ti.kernel
+def init():
+ for i,j,k in solid:
+ if (solid[i,j,k] == 0):
+ #if it is fluid intialize the density and velocity be one and zero
+ rho[i,j,k] = 1.0
+ v[i,j,k] = ti.Vector([0,0,0])
+ # set density r and density b based on psi
+ rho_r[i,j,k] = (psi[i,j,k]+1.0)/2.0
+ rho_b[i,j,k] = 1.0 - rho_r[i,j,k]
+ #set another density r and density b
+ rhor[i,j,k] = 0.0
+ rhob[i,j,k] = 0.0
+ #set density distribution equals to equilibrium density distribution function
+ for s in ti.static(range(19)):
+ f[i,j,k,s] = feq(s,1.0,v[i,j,k])
+ F[i,j,k,s] = feq(s,1.0,v[i,j,k])
+
init_geo(filename, filename2)
import the geometry data
def init_geo(filename, filename2):
+ #read the solid flag data and set it as an column major array
+ in_dat = np.loadtxt(filename)
+ in_dat[in_dat>0] = 1
+ in_dat = np.reshape(in_dat, (nx,ny,nz),order='F')
+
+ #read the phase data from file
+ phase_in_dat = np.loadtxt(filename2)
+ #set the array from the file with colum major
+ phase_in_dat = np.reshape(phase_in_dat, (nx,ny,nz), order='F')
+
+ return in_dat, phase_in_dat
+
static_init()
initialize non-modified variable
@ti.kernel
+def static_init():
+ if ti.static(enable_projection): # No runtime overhead
+ #define lattice speed
+ e[0] = ti.Vector([0,0,0])
+ e[1] = ti.Vector([1,0,0]); e[2] = ti.Vector([-1,0,0]); e[3] = ti.Vector([0,1,0]); e[4] = ti.Vector([0,-1,0]);e[5] = ti.Vector([0,0,1]); e[6] = ti.Vector([0,0,-1])
+ e[7] = ti.Vector([1,1,0]); e[8] = ti.Vector([-1,-1,0]); e[9] = ti.Vector([1,-1,0]); e[10] = ti.Vector([-1,1,0])
+ e[11] = ti.Vector([1,0,1]); e[12] = ti.Vector([-1,0,-1]); e[13] = ti.Vector([1,0,-1]); e[14] = ti.Vector([-1,0,1])
+ e[15] = ti.Vector([0,1,1]); e[16] = ti.Vector([0,-1,-1]); e[17] = ti.Vector([0,1,-1]); e[18] = ti.Vector([0,-1,1])
+ #define another lattice speed
+ e_f[0] = ti.Vector([0,0,0])
+ e_f[1] = ti.Vector([1,0,0]); e_f[2] = ti.Vector([-1,0,0]); e_f[3] = ti.Vector([0,1,0]); e_f[4] = ti.Vector([0,-1,0]);e_f[5] = ti.Vector([0,0,1]); e_f[6] = ti.Vector([0,0,-1])
+ e_f[7] = ti.Vector([1,1,0]); e_f[8] = ti.Vector([-1,-1,0]); e_f[9] = ti.Vector([1,-1,0]); e_f[10] = ti.Vector([-1,1,0])
+ e_f[11] = ti.Vector([1,0,1]); e_f[12] = ti.Vector([-1,0,-1]); e_f[13] = ti.Vector([1,0,-1]); e_f[14] = ti.Vector([-1,0,1])
+ e_f[15] = ti.Vector([0,1,1]); e_f[16] = ti.Vector([0,-1,-1]); e_f[17] = ti.Vector([0,1,-1]); e_f[18] = ti.Vector([0,-1,1])
+ #define a weight parameter
+ w[0] = 1.0/3.0; w[1] = 1.0/18.0; w[2] = 1.0/18.0; w[3] = 1.0/18.0; w[4] = 1.0/18.0; w[5] = 1.0/18.0; w[6] = 1.0/18.0;
+ w[7] = 1.0/36.0; w[8] = 1.0/36.0; w[9] = 1.0/36.0; w[10] = 1.0/36.0; w[11] = 1.0/36.0; w[12] = 1.0/36.0;
+ w[13] = 1.0/36.0; w[14] = 1.0/36.0; w[15] = 1.0/36.0; w[16] = 1.0/36.0; w[17] = 1.0/36.0; w[18] = 1.0/36.0;
+ #define the boundary velocity
+ bc_vel_x_left = ti.Vector([vx_bcxl, vy_bcxl, vz_bcxl])
+ bc_vel_x_right = ti.Vector([vx_bcxr, vy_bcxr, vz_bcxr])
+ bc_vel_y_left = ti.Vector([vx_bcyl, vy_bcyl, vz_bcyl])
+ bc_vel_y_right = ti.Vector([vx_bcyr, vy_bcyr, vz_bcyr])
+ bc_vel_z_left = ti.Vector([vx_bczl, vy_bczl, vz_bczl])
+ bc_vel_z_right = ti.Vector([vx_bczr, vy_bczr, vz_bczr])
+
multiply_M()
calculate the density distribution function in momentum space
@ti.func
+def multiply_M(i,j,k):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ for index in ti.static(range(19)):
+ for s in ti.static(range(19)):
+ #calculate here
+ out[index] += M[index,s]*F[i,j,k,s]
+ #print(i,j,k, index, s, out[index], M[index,s], F[i,j,k,s])
+ return out
+
GuoF(i,j,k,s,u)
calculate Guo’s force term
@ti.func
+def GuoF(i,j,k,s,u):
+ out=0.0
+ for l in ti.static(range(19)):
+ out += w[l]*((e_f[l]-u).dot(ext_f[None])+(e_f[l].dot(u)*(e_f[l].dot(ext_f[None]))))*M[s,l]
+
+ return out
+
meq_vec(rho_local,u)
defines the equilibrium momentum
@ti.func
+def meq_vec(rho_local,u):
+ out = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ out[0] = rho_local; out[3] = u[0]; out[5] = u[1]; out[7] = u[2];
+ out[1] = u.dot(u); out[9] = 2*u.x*u.x-u.y*u.y-u.z*u.z; out[11] = u.y*u.y-u.z*u.z
+ out[13] = u.x*u.y; out[14] = u.y*u.z; out[15] = u.x*u.z
+ return out
+
Compute_C()
calculate the color gradient
@ti.func
+def Compute_C(i):
+ C = ti.Vector([0.0,0.0,0.0])
+ ind_S = 0
+ for s in ti.static(range(19)):
+ ip = periodic_index_for_psi(i+e[s])
+ if (solid[ip] == 0):
+ #if it's fluid calculate the color gradient based on psi
+ C += 3.0*w[s]*e_f[s]*psi[ip]
+ else:
+ #if it is solid and abs(density r-density b) is less than 0.9
+ ind_S = 1
+ #calculate the color gradient based on psi_solid and set ind_s=1
+ C += 3.0*w[s]*e_f[s]*psi_solid
+
+ if (abs(rho_r[i]-rho_b[i]) > 0.9) and (ind_S == 1):
+ #if abs(density r-density b) is very large and it's solid set color gradient to be zero
+ C = ti.Vector([0.0,0.0,0.0])
+
+ return C
+
Compute_S_local
calculate parameter of s diagonal
@ti.func
+def Compute_S_local(id):
+ sv=0.0; sother=0.0
+ if (psi[id]>0):
+ if (psi[id]>0.1):
+ #if psi>0.1
+ #sv=1.0/(niu_l/(1.0/3.0)+0.5)
+ sv=wl
+ else:
+ #if 0<psi<0.1 calculate sv
+ sv=lg0+l1*psi[id]+l2*psi[id]*psi[id]
+ else:
+ #if psi <-0.1
+ if (psi[id]<-0.1):
+ #calculate sv
+ sv=wg
+ else:
+ #if psi >-0.1
+ sv=lg0+g1*psi[id]+g2*psi[id]*psi[id]
+ #calculate s other
+ sother = 8.0*(2.0-sv)/(8.0-sv)
+
+ #set s diagonal to be zero and set certain element to be relatie local parameter
+ S = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ S[1]=sv;S[2]=sv;S[4]=sother;S[6]=sother;S[8]=sother;S[9]=sv;
+ S[10]=sv;S[11]=sv;S[12]=sv;S[13]=sv;S[14]=sv;S[15]=sv;S[16]=sother;
+ S[17]=sother;S[18]=sother;
+
+
+ return S;
+
collision()
define the collision and recoloring process
@ti.kernel
+def colission():
+ for i,j,k in rho:
+ #if it is inner fluid, calculate color gradient divided by norm of color gradient
+ if (i<nx and j<ny and k<nz and solid[i,j,k] == 0):
+ uu = v[i,j,k].norm_sqr()
+ C = Compute_C(ti.Vector([i,j,k]))
+ cc = C.norm()
+ normal = ti.Vector([0.0,0.0,0.0])
+ if cc>0 :
+ normal = C/cc
+ #calculate the M
+ m_temp = multiply_M(i,j,k)
+ meq = meq_vec(rho[i,j,k],v[i,j,k])
+ #calculate surface tension term
+ meq[1] += CapA*cc
+ meq[9] += 0.5*CapA*cc*(2*normal.x*normal.x-normal.y*normal.y-normal.z*normal.z)
+ meq[11] += 0.5*CapA*cc*(normal.y*normal.y-normal.z*normal.z)
+ meq[13] += 0.5*CapA*cc*(normal.x*normal.y)
+ meq[14] += 0.5*CapA*cc*(normal.y*normal.z)
+ meq[15] += 0.5*CapA*cc*(normal.x*normal.z)
+ #calculate s local
+ S_local = Compute_S_local(ti.Vector([i,j,k]))
+ #calculate s*(m-meq)
+ for s in ti.static(range(19)):
+ m_temp[s] -= S_local[s]*(m_temp[s]-meq[s])
+ m_temp[s] += (1-0.5*S_local[s])*GuoF(i,j,k,s,v[i,j,k])
+ #calculte convection of density filed
+ g_r = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+ g_b = ti.Vector([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
+
+ for s in ti.static(range(19)):
+ f[i,j,k,s] = 0
+ for l in ti.static(range(19)):
+ # 1.single phase collision
+ f[i,j,k,s] += inv_M[s,l]*m_temp[l]
+
+ g_r[s] = feq(s,rho_r[i,j,k],v[i,j,k])
+ g_b[s] = feq(s,rho_b[i,j,k],v[i,j,k])
+
+ if (cc>0):
+ for kk in ti.static([1,3,5,7,9,11,13,15,17]):
+ # ef=ts.vector.dot(e[kk],C)
+ ef=e[kk].dot(C)
+ cospsi= g_r[kk] if (g_r[kk]<g_r[kk+1]) else g_r[kk+1]
+ cospsi= cospsi if (cospsi<g_b[kk]) else g_b[kk]
+ cospsi=cospsi if (cospsi<g_b[kk+1]) else g_b[kk+1]
+ cospsi*=ef/cc
+ #2.surface tension perturbation
+ g_r[kk]+=cospsi
+ g_r[kk+1]-=cospsi
+ g_b[kk]-=cospsi
+ g_b[kk+1]+=cospsi
+ # recoloring
+ for s in ti.static(range(19)):
+ ip = periodic_index(ti.Vector([i,j,k])+e[s])
+ if (solid[ip]==0):
+ rhor[ip] += g_r[s]
+ rhob[ip] += g_b[s]
+ else:
+ rhor[i,j,k] += g_r[s]
+ rhob[i,j,k] += g_b[s]
+
periodic_index()
defines the index of boundary if using periodic boundary condition
@ti.func
+def periodic_index(i):
+ iout = i
+ if i[0]<0: iout[0] = nx-1
+ if i[0]>nx-1: iout[0] = 0
+ if i[1]<0: iout[1] = ny-1
+ if i[1]>ny-1: iout[1] = 0
+ if i[2]<0: iout[2] = nz-1
+ if i[2]>nz-1: iout[2] = 0
+
+ return iout
+
periodic_index_for_psi(i)
defines the index of boundary for psi if using periodic boundary condition
@ti.func
+def periodic_index_for_psi(i):
+ iout = i
+ if i[0]<0:
+ #if periodic boundary condition set index based on periodic boundary condition
+ if bc_psi_x_left == 0:
+ iout[0] = nx-1
+ else:
+ #otherwise set neighbouring index,
+ #similar for other sides
+ iout[0] = 0
+
+ if i[0]>nx-1:
+ if bc_psi_x_right==0:
+ iout[0] = 0
+ else:
+ iout[0] = nx-1
+
+ if i[1]<0:
+ if bc_psi_y_left == 0:
+ iout[1] = ny-1
+ else:
+ iout[1] = 0
+
+ if i[1]>ny-1:
+ if bc_psi_y_right==0:
+ iout[1] = 0
+ else:
+ iout[1] = ny-1
+
+ if i[2]<0:
+ if bc_psi_z_left==0:
+ iout[2] = nz-1
+ else:
+ iout[2] = 0
+
+ if i[2]>nz-1:
+ if bc_psi_z_right==0:
+ iout[2] = 0
+ else:
+ iout[2] = nz-1
+
+ return iout
+
streaming1()
defines steaming process of denisty distribution function
@ti.kernel
+def streaming1():
+ for i,j,k in rho:
+ #if (solid[i,j,k] == 0):
+ if (i<nx and j<ny and k<nz and solid[i,j,k] == 0):
+ ci = ti.Vector([i,j,k])
+ for s in ti.static(range(19)):
+ ip = periodic_index(ci+e[s])
+ if (solid[ip]==0):
+ #if it is fluid,streaming along certain direction
+ F[ip,s] = f[ci,s]
+ else:
+ #if it is on the solid, bounce back to the opposite
+ F[ci,LR[s]] = f[ci,s]
+ #print(i, ip, "@@@")
+
Boundary_condition_psi()
defines boundary condition for psi
@ti.kernel
+def Boundary_condition_psi():
+ if bc_psi_x_left == 1:
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ #if it is fluid the value of psi equals to the psi_x_left
+ psi[0,j,k] = psi_x_left
+ #calculate density according to psi
+ #similar for other sides
+ rho_r[0,j,k] = (psi_x_left + 1.0)/2.0
+ rho_b[0,j,k] = 1.0 - rho_r[0,j,k]
+
+ if bc_psi_x_right == 1:
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ psi[nx-1,j,k] = psi_x_right
+ rho_r[nx-1,j,k] = (psi_x_right + 1.0)/2.0
+ rho_b[nx-1,j,k] = 1.0 - rho_r[nx-1,j,k]
+
+ if bc_psi_y_left == 1:
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,0,k]==0):
+ psi[i,0,k] = psi_y_left
+ rho_r[i,0,k] = (psi_y_left + 1.0)/2.0
+ rho_b[i,0,k] = 1.0 - rho_r[i,0,k]
+
+ if bc_psi_y_right == 1:
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,ny-1,k]==0):
+ psi[i,ny-1,k] = psi_y_right
+ rho_r[i,ny-1,k] = (psi_y_right + 1.0)/2.0
+ rho_b[i,ny-1,k] = 1.0 - rho_r[i,ny-1,k]
+
+ if bc_psi_z_left == 1:
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,0]==0):
+ psi[i,j,0] = psi_z_left
+ rho_r[i,j,0] = (psi_z_left + 1.0)/2.0
+ rho_b[i,j,0] = 1.0 - rho_r[i,j,0]
+
+ if bc_psi_z_right == 1:
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,nz-1]==0):
+ psi[i,j,nz-1] = psi_z_right
+ rho_r[i,j,nz-1] = (psi_z_right + 1.0)/2.0
+ rho_b[i,j,nz-1] = 1.0 - rho_r[i,j,nz-1]
+
Boundary_condition
defines boundary condition and the same as single_phase solver
@ti.kernel
+def Boundary_condition():
+ if ti.static(bc_x_left==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[1,j,k]>0):
+ F[0,j,k,s]=feq(s, rho_bcxl, v[1,j,k])
+ else:
+ F[0,j,k,s]=feq(s, rho_bcxl, v[0,j,k])
+
+ if ti.static(bc_x_left==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[0,j,k]==0):
+ for s in ti.static(range(19)):
+ F[0,j,k,s]=feq(LR[s], 1.0, bc_vel_x_left[None])-F[0,j,k,LR[s]]+feq(s,1.0,bc_vel_x_left[None])
+
+ if ti.static(bc_x_right==1):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[nx-2,j,k]>0):
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-2,j,k])
+ else:
+ F[nx-1,j,k,s]=feq(s, rho_bcxr, v[nx-1,j,k])
+
+ if ti.static(bc_x_right==2):
+ for j,k in ti.ndrange((0,ny),(0,nz)):
+ if (solid[nx-1,j,k]==0):
+ for s in ti.static(range(19)):
+ F[nx-1,j,k,s]=feq(LR[s], 1.0, bc_vel_x_right[None])-F[nx-1,j,k,LR[s]]+feq(s,1.0,bc_vel_x_right[None])
+
+
+ # Direction Y
+ if ti.static(bc_y_left==1):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,1,k]>0):
+ F[i,0,k,s]=feq(s, rho_bcyl, v[i,1,k])
+ else:
+ F[i,0,k,s]=feq(s, rho_bcyl, v[i,0,k])
+
+ if ti.static(bc_y_left==2):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,0,k]==0):
+ for s in ti.static(range(19)):
+ F[i,0,k,s]=feq(LR[s], 1.0, bc_vel_y_left[None])-F[i,0,k,LR[s]]+feq(s,1.0,bc_vel_y_left[None])
+
+ if ti.static(bc_y_right==1):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,ny-1,k]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,ny-2,k]>0):
+ F[i,ny-1,k,s]=feq(s, rho_bcyr, v[i,ny-2,k])
+ else:
+ F[i,ny-1,k,s]=feq(s, rho_bcyr, v[i,ny-1,k])
+
+ if ti.static(bc_y_right==2):
+ for i,k in ti.ndrange((0,nx),(0,nz)):
+ if (solid[i,ny-1,k]==0):
+ for s in ti.static(range(19)):
+ F[i,ny-1,k,s]=feq(LR[s], 1.0, bc_vel_y_right[None])-F[i,ny-1,k,LR[s]]+feq(s,1.0,bc_vel_y_right[None])
+
+ # Z direction
+ if ti.static(bc_z_left==1):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,j,1]>0):
+ F[i,j,0,s]=feq(s, rho_bczl, v[i,j,1])
+ else:
+ F[i,j,0,s]=feq(s, rho_bczl, v[i,j,0])
+
+ if ti.static(bc_z_left==2):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,0]==0):
+ for s in ti.static(range(19)):
+ F[i,j,0,s]=feq(LR[s], 1.0, bc_vel_z_left[None])-F[i,j,0,LR[s]]+feq(s,1.0,bc_vel_z_left[None])
+
+ if ti.static(bc_z_right==1):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,nz-1]==0):
+ for s in ti.static(range(19)):
+ if (solid[i,j,nz-2]>0):
+ F[i,j,nz-1,s]=feq(s, rho_bczr, v[i,j,nz-2])
+ else:
+ F[i,j,nz-1,s]=feq(s, rho_bczr, v[i,j,nz-1])
+
+ if ti.static(bc_z_right==2):
+ for i,j in ti.ndrange((0,nx),(0,ny)):
+ if (solid[i,j,nz-1]==0):
+ for s in ti.static(range(19)):
+ F[i,j,nz-1,s]=feq(LR[s], 1.0, bc_vel_z_right[None])-F[i,j,nz-1,LR[s]]+feq(s,1.0,bc_vel_z_right[None])
+
Boundary_condition_psi()
calculate macroscopic variable
@ti.kernel
+def streaming3():
+ for i,j,k, in rho:
+ #if (solid[i,j,k] == 0):
+ if (i<nx and j<ny and k<nz and solid[i,j,k] == 0):
+ rho[i,j,k] = 0
+ v[i,j,k] = ti.Vector([0,0,0])
+ #define denisty r and density b
+ rho_r[i,j,k] = rhor[i,j,k]
+ rho_b[i,j,k] = rhob[i,j,k]
+ rhor[i,j,k] = 0.0; rhob[i,j,k] = 0.0
+
+ for s in ti.static(range(19)):
+ f[i,j,k,s] = F[i,j,k,s]
+ rho[i,j,k] += f[i,j,k,s]
+ v[i,j,k] += e_f[s]*f[i,j,k,s]
+ #calculate velocity and psi
+ v[i,j,k] /= rho[i,j,k]
+ v[i,j,k] += (ext_f[None]/2)/rho[i,j,k]
+ psi[i,j,k] = rho_r[i,j,k]-rho_b[i,j,k]/(rho_r[i,j,k] + rho_b[i,j,k])
+
The code snippts below define time, read file do the simulation and export results +It is almost the same as the single-phase solver except two input file and export phase variable
+time_init = time.time()
+time_now = time.time()
+time_pre = time.time()
+dt_count = 0
+
+
+solid_np, phase_np = init_geo('./img_ftb131.txt','./phase_ftb131.dat')
+
+#solid_np = init_geo('./img_ftb131.txt')
+solid.from_numpy(solid_np)
+psi.from_numpy(phase_np)
+
+static_init()
+init()
+
+#print(wl,wg, lg0, l1, l2,'~@@@@@~@~@~@~@')
+
+for iter in range(80000+1):
+ colission()
+ streaming1()
+ Boundary_condition()
+ #streaming2()
+ streaming3()
+ Boundary_condition_psi()
+
+
+ if (iter%500==0):
+
+ time_pre = time_now
+ time_now = time.time()
+ diff_time = int(time_now-time_pre)
+ elap_time = int(time_now-time_init)
+ m_diff, s_diff = divmod(diff_time, 60)
+ h_diff, m_diff = divmod(m_diff, 60)
+ m_elap, s_elap = divmod(elap_time, 60)
+ h_elap, m_elap = divmod(m_elap, 60)
+
+ print('----------Time between two outputs is %dh %dm %ds; elapsed time is %dh %dm %ds----------------------' %(h_diff, m_diff, s_diff,h_elap,m_elap,s_elap))
+ print('The %dth iteration, Max Force = %f, force_scale = %f\n\n ' %(iter, 10.0, 10.0))
+
+ if (iter%10000==0):
+ gridToVTK(
+ "./structured"+str(iter),
+ x,
+ y,
+ z,
+ #cellData={"pressure": pressure},
+ pointData={ "Solid": np.ascontiguousarray(solid.to_numpy()),
+ "rho": np.ascontiguousarray(rho.to_numpy()[0:nx,0:ny,0:nz]),
+ "phase": np.ascontiguousarray(psi.to_numpy()[0:nx,0:ny,0:nz]),
+ "velocity": (np.ascontiguousarray(v.to_numpy()[0:nx,0:ny,0:nz,0]), np.ascontiguousarray(v.to_numpy()[0:nx,0:ny,0:nz,1]),np.ascontiguousarray(v.to_numpy()[0:nx,0:ny,0:nz,2]))
+ }
+ )
+
+#ti.print_kernel_profile_info()
+#ti.print_profile_info()
+
This file is almost the same as the lbm_solver_3d_2phase.py
file execpt sparse storage definition of some varibles
# Sparse Storage memory allocation
+f = ti.field(ti.f32)
+F = ti.field(ti.f32)
+rho = ti.field(ti.f32)
+v = ti.Vector.field(3, ti.f32)
+rhor = ti.field(ti.f32)
+rhob = ti.field(ti.f32)
+rho_r = ti.field(ti.f32)
+rho_b = ti.field(ti.f32)
+n_mem_partition = 3
+
+cell1 = ti.root.pointer(ti.ijk, (nx//n_mem_partition+1,ny//n_mem_partition+1,nz//n_mem_partition+1))
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(v)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rhor)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rhob)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho_r)
+cell1.dense(ti.ijk, (n_mem_partition,n_mem_partition,n_mem_partition)).place(rho_b)
+
+
+cell2 = ti.root.pointer(ti.ijkl,(nx//3+1,ny//3+1,nz//3+1,1))
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(f)
+cell2.dense(ti.ijkl,(n_mem_partition,n_mem_partition,n_mem_partition,19)).place(F)
+
Above code snippts define the sparse storage of some varibles
+