reference:
https://github.com/pmocz/latticeboltzmann-python/blob/main/latticeboltzmann.py
https://physics.weber.edu/schroeder/fluids/
The lattice Boltzmann method is a simple way to simulate the fluid dynamics. You can find a detail information here.
The code below is mostly based on one developed here. I changed the left boundary condition, the initial flow pattern, and the shape of an object.
For the following example, I put a bar as an object and Karman vortex sheet can be seen.
%config InlineBackend.figure_format='retina'
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
import matplotlib.animation as animation
mpl.rcParams['animation.html' ]='jshtml'
def main(Nx,Ny,Nt,u0,visco):
# collision timescale
tau = (3*visco + 0.5)
# Lattice speeds / weights
NL = 9
idxs = np.arange(NL)
cxs = np.array([0, 0, 1, 1, 1, 0,-1,-1,-1])
cys = np.array([0, 1, 1, 0,-1,-1,-1, 0, 1])
weights = np.array([4/9,1/9,1/36,1/9,1/36,1/9,1/36,1/9,1/36]) # sums to 1
# Initial Conditions
F = np.ones((Ny,Nx,NL))
for i,cx,cy,w in zip(idxs,cxs,cys,weights):
F[:,:,i]=w*(F[:,:,i]+cx*3*u0+abs(cx)*4.5*u0**2-1.5*u0**2)
# object boundary
X, Y = np.meshgrid(range(Nx), range(Ny))
objct = (X - Nx/4)**2 + (Y - Ny/2)**2 < (Ny/16)**2 #cylinder
objct = (Nx/4<X)&(X<Nx/4+5)&(Ny/3<Y)&(Y<Ny-Ny/3) #bar
# history
isav=200
hst=np.zeros((Ny,Nx,Nt//isav))
# Simulation Main Loop
for it in range(Nt):
# Drift
for i, cx, cy in zip(idxs, cxs, cys):
F[:,:,i] = np.roll(F[:,:,i], cx, axis=1)
F[:,:,i] = np.roll(F[:,:,i], cy, axis=0)
# Left boundary
for i,cx,cy,w in zip(idxs,cxs,cys,weights):
if(i!=0 and i!=1 and i!=5):
F[:,0,i]=w*(np.ones(Ny)+cx*3*u0+abs(cx)*4.5*u0**2-1.5*u0**2)
# Set reflective boundaries
bndryF = F[objct,:]
bndryF = bndryF[:,[0,5,6,7,8,1,2,3,4]]
# Calculate fluid variables
rho = np.sum(F,2)
ux = np.sum(F*cxs,2) / rho
uy = np.sum(F*cys,2) / rho
# Apply Collision
Feq = np.zeros(F.shape)
for i, cx, cy, w in zip(idxs, cxs, cys, weights):
Feq[:,:,i] = rho * w * ( 1 + 3*(cx*ux+cy*uy) + 9*(cx*ux+cy*uy)**2/2 - 3*(ux**2+uy**2)/2 )
F += -(1.0/tau) * (F - Feq)
# Apply boundary
F[objct,:] = bndryF
# store the vorticity into hst
if (it % isav == 0):
ux[objct] = 0
uy[objct] = 0
vorticity = (np.roll(ux, -1, axis=0) - np.roll(ux, 1, axis=0))\
- (np.roll(uy, -1, axis=1) - np.roll(uy, 1, axis=1))
vorticity[objct] = np.nan
hst[:,:,it//isav]=vorticity
return locals()
Nx=200
Ny=80
Nt=60000
u0=0.08
visco=0.01
dat=main(Nx,Ny,Nt,u0,visco)
locals().update(dat)
def update_anim(it):
fig.clf() #clear the figure
ax=fig.subplots() #add subplots
fig.tight_layout() #reduce spacing around the figure
cmap = plt.cm.bwr
cmap.set_bad('black')
im1=ax.imshow(hst[1:Ny-1,1:Nx-1,it] ,aspect='equal', origin='lower', cmap='bwr',vmin=-0.04, vmax=0.04)
cb1=fig.colorbar(im1, ax=ax, shrink=0.8,label=r'$\nabla\times{\bf u}$')
ax.set_xlabel('X')
ax.set_ylabel('Y')
nt=Nt//isav
fig=plt.figure(figsize=(10,4))
anim=animation.FuncAnimation(fig,update_anim,frames=nt)
#plt.subplots_adjust(wspace=0.1,hspace=0.01, bottom=0,top=1)
plt.close()
anim
#anim.save('sample.gif', writer='imagemagick') #save the animation as a gif file