xxxxxxxxxx
begin
ENV["LANG"]="C"
using Pkg
Pkg.activate(mktempdir())
using Revise
Pkg.add("Revise")
Pkg.add(["PyPlot","PlutoUI","ExtendableGrids","GridVisualize", "VoronoiFVM"])
using PlutoUI,PyPlot,ExtendableGrids,VoronoiFVM,GridVisualize
PyPlot.svg(true)
end;
Finite volumes: transient problems
Construction of control volumes
Start with a triangulation of a polygonal domain (intervals in 1D,triangles in 2D, tetrahedra in 3D).
Join triangle circumcenters by lines
create Voronoi cells which can serve as control volumes, akin to representative elementary volumes (REV) used to derive conservation laws.
Black + green: triangle nodes
Gray: triangle edges
Blue: triangle circumcenters
Red: Boundaries of Voronoi cells
Condition on triangulation
There is a 1:1 incidence between triangulation nodes and Voronoi cells. Moreover, the angle between the interface between two Voronoi cells and the edge between their corresponding nodes is
.Requires (in 2D) that sums of angles opposite to triangle edges are less than
and that angles opposite to boudary edges are less than ."boundary conforming Delaunay property". It has different equivalent definitions and analogues in 3D.
Construction:
"by hand" (or script) from tensor product meshes
Mesh generators: Triangle, TetGen
Julia packages: Triangulate.jl, TetGen.jl; SimplexGridFactory.jl
The discretization approach
Use Voronoi cells as REVs aka control volumes aka finite volume cells.
Given a continuity equation
in a domain , integrate this over a contol volume with associated node and apply Gauss theorem:
Here,
is the set of neighbor control volumes, , , , where denotes the measure (length resp. area) of a geometrical entity.
Flux functions
For instance, for the diffusion flux
For a convective diffusion flux
where
Software API and implementation
The entities describing the discrete system can be subdivided into two categories:
geometrical data:
together with the connectivity information of the trianglesphysical data: the number
and the functions describing the particular problem, where is a flux function approximating .
This structure allows to describe the problem to be solved by data derived from the discretization grid and by the functions describing the physics, giving rise to a software API.
The solution of the nonlinear systems of equations can be performed by Newton's method combined with various direct and iterative linear solvers.
The generic programming capabilities of Julia allow for an implementation of the method which results in an API which consists in the implementation of functions
Examples
General settings
Initial value problem with homgeneous Neumann boundary conditions
evolution (generic function with 1 method)
xxxxxxxxxx
# Function describing evolution of system with initial value inival
# using the Implicit Euler method
function evolution(inival, # initial value
sys, # finite volume system
grid, # simplex grid
tstep, # initial time step
tend, # end time
dtgrowth # time step growth factor
)
time=0.0
# record time and solution
times=[time]
solutions=[copy(inival)]
solution=copy(inival)
while time<tend
time=time+tstep
solve!(solution,inival,sys,tstep=tstep) # solve implicit Euler time step
inival.=solution # copy solution to inivalue
push!(times,time)
push!(solutions,copy(solution))
tstep*=dtgrowth # increase timestep by factor when approaching stationary state
end
# return result and grid
(times=times,solutions=solutions,grid=grid)
end
fpeak (generic function with 2 methods)
xxxxxxxxxx
# Define function for initial value $u_0$ with two methods - for 1D and 2D problems
begin
fpeak(x)=exp(-100*(x-0.25)^2)
fpeak(x,y)=exp(-100*((x-0.25)^2+(y-0.25)^2))
end
create_grid (generic function with 1 method)
xxxxxxxxxx
# Create discretization grid in 1D or 2D with approximately n nodes
function create_grid(n,dim)
nx=n
if dim==2
nx=ceil(sqrt(n))
end
X=collect(0:1.0/nx:1)
if dim==1
grid=simplexgrid(X)
else
grid=simplexgrid(X,X)
end
end
Diffusion problem
diffusion (generic function with 1 method)
xxxxxxxxxx
function diffusion(;n=100,dim=1,tstep=1.0e-4,tend=1, D=1.0, dtgrowth=1.1)
grid=create_grid(n,dim)
## Diffusion flux between neigboring control volumes
function flux!(f,u,edge)
uk=viewK(edge,u)
ul=viewL(edge,u)
f[1]=D*(uk[1]-ul[1])
end
## Storage term (under time derivative)
function storage!(f,u,node)
f[1]=u[1]
end
## Create a physics structure
physics=VoronoiFVM.Physics(
flux=flux!,
storage=storage!)
sys=VoronoiFVM.DenseSystem(grid,physics)
enable_species!(sys,1,[1])
## Create a solution array
inival=unknowns(sys)
## Broadcast the initial value
inival[1,:].=map(fpeak,grid)
evolution(inival,sys,grid,tstep,tend,dtgrowth)
end
xxxxxxxxxx
result_diffusion=diffusion(dim=1,n=1000);
time=
Reaction-diffusion problem
Diffusion + physical process which "eats" species
reaction_diffusion (generic function with 1 method)
xxxxxxxxxx
function reaction_diffusion(;
n=1000,
dim=1,
tstep=1.0e-4,
tend=1,
D=1.0,
R=10.0,
dtgrowth=1.1)
grid=create_grid(n,dim)
## Diffusion flux between neigboring control volumes
function flux!(f,u,edge)
uk=viewK(edge,u)
ul=viewL(edge,u)
f[1]=D*(uk[1]-ul[1])
end
## Storage term (under time derivative)
function storage!(f,u,node)
f[1]=u[1]
end
## Reaction term
function reaction!(f,u,node)
f[1]=R*u[1]
end
## Create a physics structure
physics=VoronoiFVM.Physics(
flux=flux!,
reaction=reaction!,
storage=storage!)
sys=VoronoiFVM.DenseSystem(grid,physics)
enable_species!(sys,1,[1])
## Create a solution array
inival=unknowns(sys)
## Broadcast the initial value
inival[1,:].=map(fpeak,grid)
evolution(inival,sys,grid,tstep,tend,dtgrowth)
end
xxxxxxxxxx
result_reaction_diffusion=reaction_diffusion(dim=1,n=1000,R=10);
time=
Convection-Diffusion problem
convection_diffusion (generic function with 1 method)
xxxxxxxxxx
function convection_diffusion(;
n=20,
dim=1,
tstep=1.0e-4,
tend=1,
D=0.01,
vx=10.0,
vy=10.0,
dtgrowth=1.1,
scheme="expfit")
grid=create_grid(n,dim)
# copy vx, vy into vector
if dim==1
V=[vx]
else
V=[vx,vy]
end
# Bernoulli function
B(x)=x/(exp(x)-1)
function flux_expfit!(f,u,edge)
uk=viewK(edge,u)
ul=viewL(edge,u)
vh=project(edge,V) # Calculate projection v * (x_L-x_K)
f[1]=D*(B(-vh/D)*uk[1]- B(vh/D)*ul[1])
end
function flux_centered!(f,u,edge)
uk=viewK(edge,u)
ul=viewL(edge,u)
vh=project(edge,V)
f[1]=D*(uk[1]-ul[1])+ vh*0.5*(uk[1]+ul[1])
end
function flux_upwind!(f,u,edge)
uk=viewK(edge,u)
ul=viewL(edge,u)
vh=project(edge,V)
f[1]=D*(uk[1]-ul[1])+ ( vh>0.0 ? vh*uk[1] : vh*ul[1] )
end
flux! =flux_upwind!
if scheme=="expfit"
flux! =flux_expfit!
elseif scheme=="centered"
flux! =flux_centered!
end
## Storage term (under time derivative)
function storage!(f,u,node)
f[1]=u[1]
end
## Create a physics structure
physics=VoronoiFVM.Physics(
flux=flux!,
storage=storage!)
sys=VoronoiFVM.DenseSystem(grid,physics)
enable_species!(sys,1,[1])
## Assume homogeneous Neumann boundary conditions, so do nothig
## Create a solution array
inival=unknowns(sys)
## Broadcast the initial value
inival[1,:].=map(fpeak,grid)
evolution(inival,sys,grid,tstep,tend,dtgrowth)
end
scheme:
xxxxxxxxxx
result_convection_diffusion=convection_diffusion(n=100,
dim=1,
scheme=scheme[1],
vx=10,vy=10);
time=
Brusselator system
Two species interacting via a reaction:
brusselator (generic function with 1 method)
xxxxxxxxxx
function brusselator(;n=100,dim=1,A=4.0,B=6.0,D1=0.01,D2=0.1,perturbation=0.1,
tstep=0.05, tend=150,dtgrowth=1.05)
grid=create_grid(n,dim)
function storage!(f,u,node)
f.=u
end
function bruss_diffusion!(f,_u,edge)
u=unknowns(edge,_u)
f[1]=D1*(u[1,1]-u[1,2])
f[2]=D2*(u[2,1]-u[2,2])
end
# Reaction:
function bruss_reaction!(f,u,node)
f[1]= (B+1.0)*u[1]-A-u[1]^2*u[2]
f[2]= u[1]^2*u[2]-B*u[1]
end
# Create system
bruss_physics=VoronoiFVM.Physics(flux=bruss_diffusion!,storage=storage!,
num_species=2,reaction=bruss_reaction!)
brusselator_system=VoronoiFVM.DenseSystem(grid,bruss_physics)
enable_species!(brusselator_system,1,[1])
enable_species!(brusselator_system,2,[1])
inival=unknowns(brusselator_system)
for i=1:num_nodes(grid)
inival[1,i]=1.0+perturbation*randn()
inival[2,i]=1.0+perturbation*randn()
end
evolution(inival,brusselator_system,grid,tstep,tend,dtgrowth)
end
xxxxxxxxxx
result_brusselator=brusselator(n=500,dim=1);
time=
Status `/tmp/jl_op3lMf/Project.toml` [cfc395e8] ExtendableGrids v0.7.4 [5eed8a63] GridVisualize v0.1.3 [7f904dfe] PlutoUI v0.7.2 [d330b81b] PyPlot v2.9.0 [295af30f] Revise v3.1.12 [82b139dc] VoronoiFVM v0.10.5