Lid-driven cavity

Standard CFD benchmark, which allows comparison with very accurate mesh-based methods. Description of the problem can be found on many webpages, like here. In the image below, you can see streamlines for Re = 400 and N = 320. This was computed on cluster and took some time. To correctly resolve corner vortices is much more demanding in SPH than in FEM.

missing

Result is compared to the referential solution by Ghia et al 1980.

module cavity_flow

using Printf
using SmoothedParticles
using Plots, CSV, DataFrames, Printf, LaTeXStrings, Parameters

Declare const parameters (dimensionless problem)

##geometrical/physical parameters
const N  =   100                #number of sample points
const Re =   100                #Reynolds number
const llid = 1.0                #length of the lid
const mu =   1.0/Re             #viscosity
const rho0 = 1.0                #density
const vlid = 1.0                #flow speed of the lid
const dr = llid/N 		        #interparticle distance
const h = 3.0*dr		        #size of kernel support
const m = rho0*dr^2             #particle mass
const c = 20*vlid		    	#numerical speed of sound
const P0 = 5.0                  #background pressure (good to prevent tensile instability)
const wwall = h

##temporal parameters
const dt = 0.1*h/c                       #numerical time-step
const t_end = 0.4                        #end of simulation
const dt_frame = max(dt, t_end/200)      #how often save data

##particle types
const FLUID = 0.
const WALL = 1.
const LID = 2.

##path to store results
const path = "results/cavity_flow"*string(Re)

Declare variables to be stored in a Particle

@with_kw mutable struct Particle <: AbstractParticle
	x::RealVector=VEC0	#position
	v::RealVector=VEC0  #velocity
	Dv::RealVector=VEC0 #acceleratation
	rho::Float64=rho0   #density
	Drho::Float64=0.0   #rate of density
	P::Float64=0.0      #pressure
	type::Float64       #particle type
end

Define geometry and create particles

function make_system()
	grid = Grid(dr, :hexagonal)
	box = Rectangle(0., 0., llid, llid)
	wall = BoundaryLayer(box, grid, wwall)
	sys = ParticleSystem(Particle, box + wall, h)
	lid   = Specification(wall, x -> x[2] > llid)
	wall = Specification(wall, x -> x[2] <= llid)
	generate_particles!(sys, grid, box, x -> Particle(x=x, type=FLUID))
	generate_particles!(sys, grid, lid, x -> Particle(x=x, type=LID))
	generate_particles!(sys, grid, wall, x -> Particle(x=x, type=WALL))
	create_cell_list!(sys)
	apply!(sys, find_pressure!)
	apply!(sys, internal_force!)
	return sys
end

Define interactions between particles

function balance_of_mass!(p::Particle, q::Particle, r::Float64)
	p.Drho += m*rDwendland2(h,r)*(dot(p.x-q.x, p.v-q.v))
end

function find_pressure!(p::Particle)
	p.rho += p.Drho*dt
	p.Drho = 0.0
	p.P = P0 + c^2*(p.rho-rho0)
end

function internal_force!(p::Particle, q::Particle, r::Float64)
	rDk = rDwendland2(h,r)
	x_pq = p.x - q.x
	v_pq = p.v - q.v

implementation of the Dirichlet boundary condition at the lid v_q is estimated using linear extrapolation

	if q.type == LID
        s = abs(x_pq[2])/(0.1*h + abs(p.x[2] - 1.0))
        v_pq = s*(p.v - vlid*VECX)
    end
	p.Dv += -m*rDk*(p.P/p.rho^2 + q.P/q.rho^2)*x_pq
	p.Dv += 8/(Re*p.rho*q.rho)*m*rDk*dot(v_pq, x_pq)/(r^2 + 0.01*h^2)*x_pq #Monaghan's type of viscosity to conserve angular momentum
end


function move!(p::Particle)
	p.Dv = VEC0
	if p.type == FLUID
		p.x += 0.5*dt*p.v
	end
end

function accelerate!(p::Particle)
	if p.type == FLUID
		p.v += 0.5*dt*p.Dv
	end
end

Time iteration

function main()
	sys = make_system()
	out = new_pvd_file(path)
	@time for k = 0 : Int64(round(t_end/dt))
		apply!(sys, accelerate!)
		apply!(sys, move!)
		create_cell_list!(sys)
		apply!(sys, balance_of_mass!)
		apply!(sys, find_pressure!)
		apply!(sys, move!)
		create_cell_list!(sys)
		apply!(sys, internal_force!)
		if (k % Int64(round(dt_frame/dt)) == 0) #save the frame
			@printf("t = %.6e s ", k*dt)
			println("(",round(100*k*dt/t_end),"% complete)")
			save_frame!(out, sys, :P, :v, :type)
		end
		apply!(sys, accelerate!)
	end
	save_pvd_file(out)
	compute_fluxes(sys)
	make_plot()
end

Functions to extract results and create plots.

function compute_fluxes(sys::ParticleSystem, res = 100)
    s = range(0.,1.,length=res)
    v1 = zeros(res)
    v2 = zeros(res)
    for i in 1:res
		#x-velocity along y-centerline
		x = RealVector(0.5, s[i], 0.)
		gamma = SmoothedParticles.sum(sys, (p,r) -> Float64(p.type==FLUID)*m*wendland2(h,r), x)
        v1[i] = SmoothedParticles.sum(sys, (p,r) -> Float64(p.type==FLUID)*m*p.v[1]*wendland2(h,r), x)/gamma
		#y-velocity along x-centerline
		x = RealVector(s[i], 0.5, 0.)
		gamma = SmoothedParticles.sum(sys, (p,r) -> Float64(p.type==FLUID)*m*wendland2(h,r), x)
        v2[i] = SmoothedParticles.sum(sys, (p,r) -> Float64(p.type==FLUID)*m*p.v[2]*wendland2(h,r), x)/gamma
    end
    #save results into csv
    data = DataFrame(s=s, v1=v1, v2=v2)
	CSV.write(path*"/data.csv", data)
	make_plot()
end

function make_plot(Re=Re)
	ref_x2vy = CSV.read("reference/ldc-x2vy.csv", DataFrame)
	ref_y2vx = CSV.read("reference/ldc-y2vx.csv", DataFrame)
	propertyname = Symbol("Re", Re)
	ref_vy = getproperty(ref_x2vy, propertyname)
	ref_vx = getproperty(ref_y2vx, propertyname)
	ref_x = ref_x2vy.x
	ref_y = ref_y2vx.y
	data = CSV.read(path*"/data.csv", DataFrame)
	p1 = plot(
		data.s, data.v2,
		xlabel = L"x",
		ylabel = L"v_y",
		label = "SPH",
		linewidth = 4,
		legend = :bottomleft,
		color = :orange,
		tickfontsize = 16,
		labelfontsize = 16,
		legendfontsize = 16,
		aspect_ratio = 1,
	)
	scatter!(p1, ref_x, ref_vy, label = "REF", markershape = :diamond, ms = 5, color = :blue)
	savefig(p1, path*"/ldc-x2vy.pdf")
	p2 = plot(
		data.v1, data.s,
		xlabel = L"v_x",
		ylabel = L"y",
		label = "SPH",
		linewidth = 4,
		legend = :bottomright,
		color = :orange,
		tickfontsize = 16,
		labelfontsize = 16,
		legendfontsize = 16,
		aspect_ratio = 1,
	)
	scatter!(p2, ref_vx, ref_y,  label = "REF", markershape = :diamond, ms = 5, color = :blue)
	savefig(p2, path*"/ldc-y2vx.pdf")
end

end

This page was generated using Literate.jl.