Example 9: Circular patch problem

missing

Circular patch is a free surface benchmark which admits a semi-analytical solution. Initially, there is a circle of radius 1 with some prescribed velocity. The resulting velocity deformation squeezes the patch. Now, we cannot do free surface with this code, but we can find a reasonable good solution by turning it into a multiphase problem where the surrounding air has much lower density.

module cpatch

include("../src/LagrangianVoronoi.jl")
using .LagrangianVoronoi

using DifferentialEquations
using StaticArrays
using Plots
using Parameters
using Base.Threads
using WriteVTK
using LinearAlgebra
using Polyester
using LaTeXStrings, CSV, DataFrames

const A0 = 0.5
const R = 1.0
const Rho = 1000.0
const rho = Rho/800
const dr = R/20

const v_char = A0*R
const dt = 0.1*dr/v_char
const t_end =  1.0
const nframes = 100
const tau = 0.1
const c0 = 100.0
const smoothing_length = 3dr

const export_path = "results/cpatch"
const xlims = (-1.5R, 1.5R)
const ylims = (-3.0R, 3.0R)

const WATER = 0
const AIR = 1

function ic!(p::VoronoiPolygon, e)
    p.c2 = Inf
    r = norm(p.x)
    p.phase = (r < R) ? WATER : AIR
    p.rho = (p.phase == WATER ? Rho : rho)
    p.mass = p.rho*area(p)
    if (p.phase == WATER)
        p.v = v_exact(p.x, e(0.0))
        p.P = P_exact(p.x, e(0.0))
    end
end

mutable struct Simulation <: SimulationWorkspace
    grid::GridMulti
    solver::PressureSolver{PolygonMulti}
    E::Float64  # total energy
    v_err::Float64 # L^2 error
    P_err::Float64
    e::Any # function that describes the exact evolution of the ellipse
    Simulation() = begin
        ode = ODEProblem(ellipse_ode, [R, R, A0], (0.0, t_end))
        e = solve(ode, Rodas4(), reltol = 1e-8, abstol = 1e-8)
        domain = Rectangle(xlims = xlims, ylims = ylims)
        grid = GridMulti(domain, dr)
        populate_circ!(grid, ic! = (p -> ic!(p, e)))
        solver = PressureSolver(grid, verbose=false)
        return new(grid, solver, 0.0, 0.0, 0.0, e)
    end
end

function ellipse_ode(e, _, _)
    return [
        -e[3]*e[1],
        +e[3]*e[2],
        (e[3]^2)*(e[1]^2 - e[2]^2)/(e[1]^2 + e[2]^2)
    ]
end

function v_exact(x::RealVector, e)::RealVector
    return RealVector(-e[3]*x[1], e[3]*x[2])
end

function P_exact(x::RealVector, e)::Float64
    return -Rho*(e[1]*e[2]*e[3])^2/(e[1]^2 + e[2]^2)*((x[1]/e[1])^2 + (x[2]/e[2])^2 - 1.0)
end

function step!(sim::Simulation, t::Float64)
    move!(sim.grid, dt)
    find_rho!(sim.grid)
    find_pressure!(sim.solver, dt)
    pressure_step!(sim.grid, dt)
    phase_preserving_remapping!(sim.grid, dt, smoothing_length)
end

function postproc!(sim::Simulation, t::Float64)
    sim.v_err = 0.0
    sim.P_err = 0.0
    sim.E = 0.0
    max_v_err = 0.0
    max_P_err = 0.0
    P_avg = 0.0
    A_tot = 0.0
    for p in sim.grid.polygons
        if (p.phase == AIR) continue end
        A = area(p)
        et = sim.e(t)
        sim.v_err += A*norm_squared(p.v - v_exact(p.x, et))
        max_v_err += A*norm_squared(v_exact(p.x, et))
        P_avg += A*(p.P - P_exact(p.x, et))
        A_tot += A
        max_P_err += A*P_exact(p.x, et)^2
        sim.E += p.mass*norm_squared(p.v)
    end
    P_avg /= A_tot
    for p in sim.grid.polygons
        if (p.phase == AIR) continue end
        A = area(p)
        et = sim.e(t)
        sim.P_err += A*(p.P - P_exact(p.x, et) - P_avg)^2
    end
    sim.P_err = sqrt(sim.P_err)/sqrt(max_P_err)
    sim.v_err = sqrt(sim.v_err)/sqrt(max_v_err)
    @show t
    @show sim.v_err
    @show sim.P_err
    @show sim.E
    println()
end


function main()
    sim = Simulation()
    run!(sim, dt, t_end, step!;
        path = export_path,
        postproc! = postproc!,
        vtp_vars = (:v, :P, :rho, :phase),      # local variables exported into vtp
        csv_vars = (:E, :v_err, :P_err),          # global variables exported into csv
        nframes = nframes,                # number of time frames
        save_points = true
    )
end

function linear_regression(x, y)
    N = length(x)
    logx = log10.(x)
    logy = log10.(y)
    A = [logx ones(N)]
    b = A\logy
    return b
end

function make_convergence_graph()
    data = CSV.read(joinpath(export_path, "convergence_data.csv"), DataFrame)
    x = log10.(data.N)
    y = [log10.(data.P) log10.(data.v)]
    eoc_p = linear_regression(data.N, data.P)[1]
    eoc_v = linear_regression(data.N, data.v)[1]
    xlabel = L"\log N"
    ylabel = L"\log \epsilon"
    p_label = L"p"*"   (EOC = $(-round(eoc_p, digits=2)))"
    v_label = L"v"*"   (EOC = $(-round(eoc_v, digits=2)))"
    plt = plot(x, y, xlabel=xlabel, ylabel=ylabel, label = [p_label v_label], marker = :hex, axis_ratio = 1.0)
    savefig(plt, joinpath(export_path, "convergence.pdf"))
end

end

This page was generated using Literate.jl.