222 : Pressure-robustness 2D

(source code)

This example studies two benchmarks for pressure-robust discretisations of the stationary Navier-Stokes equations that seek a velocity $\mathbf{u}$ and pressure $\mathbf{p}$ such that

\[\begin{aligned} - \mu \Delta \mathbf{u} + (\mathbf{u} \cdot \nabla) \mathbf{u} + \nabla p & = \mathbf{f}\\ \mathrm{div}(u) & = 0 \end{aligned}\]

with (possibly time-dependent) exterior force $\mathbf{f}$ and some viscosity parameter $\mu$.

Pressure-robustness is concerned with gradient forces that may appear in the right-hand side or the material derivative and should be balanced by the pressure (as divergence-free vector fields are orthogonal on gradient fields). Here, two test problems are considered:

  1. HydrostaticTestProblem() : Stokes (without convection term) and $\mathbf{f} = \nabla p$ such that $\mathbf{u} = 0$
  2. PotentialFlowTestProblem() : Navier-Stokes with $\mathbf{f} = 0$ and $\mathbf{u} = \nabla h$ for some harmonic function

In both test problems the errors of non-pressure-robust discretisations scale with $1/\mu$, while the pressure-robust discretisation solves $\mathbf{u} = 0$ exactly in test problem 1 and gives much better results in test problem 2.

module Example222_PressureRobustness2D

using GradientRobustMultiPhysics
using ExtendableGrids
using GridVisualize

# problem data
function HydrostaticTestProblem()
    # Stokes problem with f = grad(p)
    # u = 0,  p = x^3+y^3 - 1//2
    function P1_pressure!(result,x)
        result[1] = x[1]^3 + x[2]^3 - 1//2
    end
    u = DataFunction([0,0]; xdim = 2, name = "u")
    p = DataFunction(P1_pressure!, [1,2]; name = "p", dependencies = "X", bonus_quadorder = 3)
    f = ∇(p)
    return p,u,∇(u),f,false
end

function PotentialFlowTestProblem()
    # NavierStokes with f = 0
    # u = grad(h) with h = x^3 - 3xy^2
    # p = - |grad(h)|^2 + 14//5
    function P2_pressure!(result,x)
        result[1] = - 1//2 * (9*(x[1]^4 + x[2]^4) + 18*x[1]^2*x[2]^2) + 14//5
    end
    function P2_velo!(result,x)
        result[1] = 3*x[1]^2 - 3*x[2]^2;
        result[2] = -6*x[1]*x[2];
    end
    u = DataFunction(P2_velo!, [2,2]; name = "u", dependencies = "X", bonus_quadorder = 2)
    p = DataFunction(P2_pressure!, [1,2]; name = "p", dependencies = "X", bonus_quadorder = 4)
    f = DataFunction([0,0]; name = "f")
    return p,u,∇(u),f,true
end


function solver(Problem, xgrid, FETypes, viscosity = 1e-2; nlevels = 4, target_residual = 1e-10, maxiterations = 20, Plotter = nothing)

    # load problem data and set solver parameters
    ReconstructionOperator = FETypes[3]
    p,u,∇u,f,nonlinear = Problem()

    # setup classical (Problem) and pressure-robust scheme (Problem2)
    Problem = IncompressibleNavierStokesProblem(2; viscosity = viscosity, nonlinear = false)
    add_boundarydata!(Problem, 1, [1,2,3,4], BestapproxDirichletBoundary; data = u)
    Problem2 = deepcopy(Problem)
    Problem.name = "Stokes problem (classical)"
    Problem2.name = "Stokes problem (p-robust)"

    # assign right-hand side
    add_rhsdata!(Problem, 1, LinearForm(Identity, f))
    add_rhsdata!(Problem2, 1, LinearForm(ReconstructionOperator, f))

    # assign convection term
    if nonlinear
        add_operator!(Problem,[1,1], ConvectionOperator(1, Identity, 2, 2))
        add_operator!(Problem2,[1,1], ConvectionOperator(1, ReconstructionOperator, 2, 2; test_operator = ReconstructionOperator))
    end

    # define bestapproximation problems
    BAP_L2_u = L2BestapproximationProblem(u; bestapprox_boundary_regions = [1,2,3,4])
    BAP_L2_p = L2BestapproximationProblem(p; bestapprox_boundary_regions = [])
    BAP_H1_u = H1BestapproximationProblem(∇u, u; bestapprox_boundary_regions = [1,2,3,4])

    # define ItemIntegrators for L2/H1 error computation
    L2Error_u = L2ErrorIntegrator(u, Identity)
    L2Error_p = L2ErrorIntegrator(p, Identity)
    H1Error_u = L2ErrorIntegrator(∇u, Gradient)
    Results = zeros(Float64, nlevels, 9)
    NDofs = zeros(Int, nlevels)

    # loop over refinement levels
    Solution, Solution2 = nothing, nothing
    for level = 1 : nlevels

        # uniform mesh refinement
        xgrid = uniform_refine(xgrid)

        # get FESpaces
        FES = [FESpace{FETypes[1]}(xgrid), FESpace{FETypes[2]}(xgrid; broken = true)]

        # solve both problems
        Solution = solve(Problem, FES; maxiterations = maxiterations, target_residual = target_residual, anderson_iterations = 5)
        Solution2 = solve(Problem2, FES; maxiterations = maxiterations, target_residual = target_residual, anderson_iterations = 5)

        # solve bestapproximation problems
        BA_L2_u = FEVector("Πu",FES[1])
        BA_L2_p = FEVector("πp",FES[2])
        BA_H1_u = FEVector("Su",FES[1])
        solve!(BA_L2_u, BAP_L2_u)
        solve!(BA_L2_p, BAP_L2_p)
        solve!(BA_H1_u, BAP_H1_u)

        # compute L2 and H1 errors and save data
        NDofs[level] = length(Solution.entries)
        Results[level,1] = sqrt(evaluate(L2Error_u,Solution[1]))
        Results[level,2] = sqrt(evaluate(L2Error_u,Solution2[1]))
        Results[level,3] = sqrt(evaluate(L2Error_u,BA_L2_u[1]))
        Results[level,4] = sqrt(evaluate(L2Error_p,Solution[2]))
        Results[level,5] = sqrt(evaluate(L2Error_p,Solution2[2]))
        Results[level,6] = sqrt(evaluate(L2Error_p,BA_L2_p[1]))
        Results[level,7] = sqrt(evaluate(H1Error_u,Solution[1]))
        Results[level,8] = sqrt(evaluate(H1Error_u,Solution2[1]))
        Results[level,9] = sqrt(evaluate(H1Error_u,BA_H1_u[1]))
    end

    # print convergence history
    print_convergencehistory(NDofs, Results[:,1:3]; X_to_h = X -> X.^(-1/2), ylabels = ["||u-u_c||", "||u-u_r||", "||u-Πu||"])
    print_convergencehistory(NDofs, Results[:,4:6]; X_to_h = X -> X.^(-1/2), ylabels = ["||p-p_c||", "||p-p_r||", "||p-πp||"])
    print_convergencehistory(NDofs, Results[:,7:9]; X_to_h = X -> X.^(-1/2), ylabels = ["||∇(u-u_c)||", "||∇(u-u_r)||", "||∇(u-Su)||"])

    # plot
    p = GridVisualizer(; Plotter = Plotter, layout = (2,3), clear = true, resolution = (1500,1000))
    scalarplot!(p[1,1],xgrid,view(nodevalues(Solution[1]; abs = true),1,:), levels = 7)
    vectorplot!(p[1,1],xgrid,evaluate(PointEvaluator(Solution[1], Identity)), spacing = 0.1, clear = false, title = "u_c (abs + quiver)")
    scalarplot!(p[1,2],xgrid,view(nodevalues(Solution[2]),1,:), levels = 11, title = "p_c")
    scalarplot!(p[2,1],xgrid,view(nodevalues(Solution2[1]; abs = true),1,:), levels = 7)
    vectorplot!(p[2,1],xgrid,evaluate(PointEvaluator(Solution2[1], Identity)), spacing = 0.1, clear = false, title = "u_r (abs + quiver)")
    scalarplot!(p[2,2],xgrid,view(nodevalues(Solution2[2]),1,:), levels = 11, title = "p_r")
    convergencehistory!(p[1,3], NDofs, Results[:,[1,7,4]]; add_h_powers = [1,2], X_to_h = X -> X.^(-1/2), ylabels = ["|| u - u_c ||", "|| ∇(u - u_c) ||", "|| p - p_c ||"])
    convergencehistory!(p[2,3], NDofs, Results[:,[2,8,5]]; add_h_powers = [1,2], X_to_h = X -> X.^(-1/2), ylabels = ["|| u - u_r ||", "|| ∇(u - u_r) ||", "|| p - p_r ||"])

    # return last L2 error of p-robust method for testing
    return Results[end,2]
end


# everything is wrapped in a main function
function main(; problem = 2, verbosity = 0, nlevels = 4, viscosity = 1e-2, Plotter = nothing)

    # set log level
    set_verbosity(verbosity)

    # set problem to solve
    if problem == 1
        Problem = HydrostaticTestProblem
    elseif problem == 2
        Problem = PotentialFlowTestProblem
    else
        @error "No problem defined for this number!"
    end

    # set grid and problem parameters
    xgrid = grid_unitsquare(Triangle2D) # initial grid

    # choose finite element discretisation
    #FETypes = [H1BR{2}, L2P0{1}, ReconstructionIdentity{HDIVRT0{2}}] # Bernardi--Raugel with RT0 reconstruction
    FETypes = [H1BR{2}, L2P0{1}, ReconstructionIdentity{HDIVBDM1{2}}] # Bernardi--Raugel with BDM1 reconstruction
    #FETypes = [H1CR{2}, L2P0{1}, ReconstructionIdentity{HDIVRT0{2}}] # Crouzeix--Raviart with RT0 reconstruction

    # run
    solver(Problem, xgrid, FETypes, viscosity; nlevels = nlevels, Plotter = Plotter)

    return nothing
end


# test function that is called by test unit
# tests if hydrostatic problem is solved exactly by pressure-robust methods
function test(; Plotter = nothing)
    xgrid = uniform_refine(grid_unitsquare_mixedgeometries())
    testspaces = [[H1CR{2}, L2P0{1}, ReconstructionIdentity{HDIVRT0{2}}],
                  [H1BR{2}, L2P0{1}, ReconstructionIdentity{HDIVRT0{2}}],
                  [H1BR{2}, L2P0{1}, ReconstructionIdentity{HDIVBDM1{2}}]
                  ]
    error = []
    for FETypes in testspaces
        push!(error, solver(HydrostaticTestProblem, xgrid, FETypes, 1; nlevels = 1))
        println("FETypes = $FETypes   error = $(error[end])")
    end

    xgrid = uniform_refine(grid_unitsquare(Triangle2D))
    testspaces = [
                  [H1P2B{2,2}, H1P1{1}, ReconstructionIdentity{HDIVRT1{2}}]
                  ]
    error = []
    for FETypes in testspaces
        push!(error, solver(HydrostaticTestProblem, xgrid, FETypes, 1; nlevels = 1, Plotter = Plotter))
        println("FETypes = $FETypes   error = $(error[end])")
    end
    return maximum(error)
end

end

This page was generated using Literate.jl.