Stokes flow

Here, we'll demonstrate a solution of the Stokes equations, with Dirichlet (i.e. no-slip) boundary conditions on a surface. The purpose of this case is to demonstrate the use of the tools with vector-valued data – in this case, the fluid velocity field.

After taking the curl of the velocity-pressure form of the Stokes equations, the governing equations for this problem can be written as

\[\mu \nabla^2 \omega - \nabla \times (\delta(\chi) \mathbf{\sigma}) = \nabla\times \nabla \cdot (\delta(\chi)\mathbf{\Sigma})\]

\[\delta^T(\chi) \mathbf{v} = \overline{\mathbf{v}}_b\]

\[\mathbf{v} = \nabla \phi + \nabla\times \psi\]

where $\psi$ and $\phi$ are the solutions of

\[\nabla^2 \psi = -\omega\]

\[\nabla^2 \phi = \delta(\chi) \mathbf{n}\cdot [\mathbf{v}_b]\]

and $\mathbf{\Sigma} = \mu ([\mathbf{v}_b]\mathbf{n} + \mathbf{n} [\mathbf{v}_b] - \mathbf{n}\cdot[\mathbf{v}_b]\mathbf{I})$ is a surface viscous flux tensor; and $[\mathbf{v}_b] = \mathbf{v}_b^+ - \mathbf{v}_b^-$ and $\overline{\mathbf{v}}_b = (\mathbf{v}_b^+ + \mathbf{v}_b^-)/2$ are the jump and average of the surface velocities on either side of a surface.

We can discretize and combine these equations into a saddle-point form for $\psi$ and $\sigma$:

\[\begin{bmatrix}L^2 & C_s^T \\ C_s & 0 \end{bmatrix}\begin{pmatrix} \psi \\ \mathbf{\sigma} \end{pmatrix} = \begin{pmatrix} -C D_s [\mathbf{v}_b] \\ \overline{\mathbf{v}}_b - G_s L^{-1} R \mathbf{n}\cdot [\mathbf{v}_b] \end{pmatrix}\]

Note that $L^2$ represents the discrete biharmonic operator. (We have absorbed the viscosity $\mu$ into the scaling of the problem.)

We can easily break this down into algorithmic form by the same block-LU decomposition of other problems. The difference here is that $\mathbf{v}$ and $\mathbf{\sigma}$ are vector-valued, so we treat this fundamentally as a vector-valued problem.

using ImmersedLayers
using Plots
using LinearAlgebra
using UnPack

Set up the extra cache and solve function

The problem type is generated with the usual macro call, but now with vector type

@ilmproblem StokesFlow vector

As with other cases, the extra cache holds additional intermediate data, as well as the Schur complement. We need a few more intermediate variable for this problem. We'll also construct the filtering matrix for showing the traction field.

struct StokesFlowCache{SMT,SSMT,CMT,RCT,DVT,VNT,ST,VFT,FT} <: AbstractExtraILMCache
   S :: SMT
   Ss :: SSMT
   C :: CMT
   Rc :: RCT
   dv :: DVT
   vb :: DVT
   vprime :: DVT
   dvn :: VNT
   sstar :: ST
   vϕ :: VFT
   ϕ :: FT
end

Extend the prob_cache function to construct the extra cache

function ImmersedLayers.prob_cache(prob::StokesFlowProblem,base_cache::BasicILMCache)
    S = create_CL2invCT(base_cache)
    Ss = create_CLinvCT_scalar(base_cache)
    C = create_surface_filter(base_cache)

    dv = zeros_surface(base_cache)
    vb = zeros_surface(base_cache)
    vprime = zeros_surface(base_cache)

    dvn = ScalarData(dv)
    sstar = zeros_gridcurl(base_cache)
    vϕ = zeros_grid(base_cache)
    ϕ = Nodes(Primal,sstar)

    Rc = RegularizationMatrix(base_cache,dvn,ϕ)

    StokesFlowCache(S,Ss,C,Rc,dv,vb,vprime,dvn,sstar,vϕ,ϕ)
end

And finally, extend the solve function to do the actual solving. We pass in the external and internal surface velocities via the bc keyword The function returns the velocity field, streamfunction field, and surface traction

function ImmersedLayers.solve(prob::StokesFlowProblem,sys::ILMSystem)
    @unpack extra_cache, base_cache, bc, phys_params = sys
    @unpack nrm = base_cache
    @unpack S, Ss, C, Rc, dv, vb, vprime, sstar, dvn, vϕ, ϕ  = extra_cache

    σ = zeros_surface(sys)
    s = zeros_gridcurl(sys)
    v = zeros_grid(sys)

    # Get the jumps in velocity across surface
    prescribed_surface_jump!(dv,sys)

    # Compute ψ*
    surface_divergence_symm!(v,dv,sys)
    curl!(sstar,v,sys)
    sstar .*= -1.0

    inverse_laplacian!(sstar,sys)
    inverse_laplacian!(sstar,sys)

    # Adjustment for jump in normal velocity
    pointwise_dot!(dvn,nrm,dv)
    regularize!(ϕ,dvn,Rc)
    inverse_laplacian!(ϕ,sys)
    grad!(vϕ,ϕ,sys)

    # Get the average velocity on the surface
    prescribed_surface_average!(vb,sys)
    interpolate!(vprime,vϕ,sys)
    vprime .= vb - vprime

    # Compute surface velocity due to ψ*
    curl!(v,sstar,sys)
    interpolate!(vb,v,sys)

    # Spurious slip
    vprime .-= vb
    σ .= S\vprime

    # Correction streamfunction
    surface_curl!(s,σ,sys)
    s .*= -1.0
    inverse_laplacian!(s,sys)
    inverse_laplacian!(s,sys)

    # Correct
    s .+= sstar

    # Assemble the velocity
    curl!(v,s,sys)
    v .+= vϕ

    # Add the streamfunction equivalent to scalar potential
    ds = zeros_surfacescalar(base_cache)
    surface_grad_cross!(ds,ϕ,base_cache)
    ds .= Ss\ds
    surface_curl_cross!(sstar,ds,base_cache)
    sstar .*= -1.0
    inverse_laplacian!(sstar,base_cache)
    s .+= sstar

    # Filter the traction twice to clean it up a bit
    σ .= C^2*σ

    return v, s, σ
end

Set up a grid, body, and boundary conditions

We'll consider a rectangle here

Δx = 0.01
Lx = 4.0
xlim = (-Lx/2,Lx/2)
ylim = (-Lx/2,Lx/2)
g = PhysicalGrid(xlim,ylim,Δx)
Δs = 1.4*cellsize(g)
body = Rectangle(0.5,0.25,Δs)
Closed polygon with 4 vertices and 214 points
   Current position: (0.0,0.0)
   Current angle (rad): 0.0

Set up the boundary condition functions and assemble them into a Dict. We will consider the flow generated by simply translating the body to the right at unit velocity. We set the external $x$ velocity to 1 and $y$ velocity to zero. The internal velocity we set to zero.

function get_vsplus(base_cache,phys_params)
    vsplus = zeros_surface(base_cache)
    vsplus.u .= 1.0
    return vsplus
end
get_vsminus(base_cache,phys_params) = zeros_surface(base_cache)

bcdict = Dict("exterior"=>get_vsplus,"interior"=>get_vsminus)
Dict{String, Function} with 2 entries:
  "interior" => get_vsminus
  "exterior" => get_vsplus

Set up the problem and the system, passing in the boundary condition information

prob = StokesFlowProblem(g,body,scaling=GridScaling,bc=bcdict)
sys = construct_system(prob);

Solve the problem

@time v, s, σ = solve(prob,sys)
  0.103052 seconds (200.79 k allocations: 56.412 MiB, 14.56% compilation time)

Let's look at the velocity components

plot(v,sys)
Example block output

Note that the velocity is zero inside, as desired. Let's look at the streamlines here

plot(s,sys)
Example block output

We'll plot the surface traction components, too.

plot(σ.u,sys,label="σx")
plot!(σ.v,sys,label="σy")
Example block output

Now, let's apply a different motion, where we rotate it counter-clockwise at unit angular velocity. We simply re-define the external velocity function.

function get_vsplus(base_cache,phys_params)
    pts = points(base_cache)
    vsplus = zeros_surface(base_cache)
    vsplus.u .= -pts.v
    vsplus.v .= pts.u
    return vsplus
end
get_vsplus (generic function with 1 method)

Solve it again and plot the velocity and streamlines

v, s, σ = solve(prob,sys)
plot(v,sys)
Example block output
plot(s,sys)
Example block output

This page was generated using Literate.jl.