Single_phase.LBM_3D_SinglePhase_Solver
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()