"""
Solve the incompressible Navier-Stokes equations
on an L-shaped domain using Chorin's splitting method.
"""
from dolfin import *
import numpy as np
from vedo import download
from vedo.dolfin import plot

# Print log messages only from the root process in parallel
parameters["std_out_all_processes"] = False

# Load mesh from file
fpath = download("https://vedo.embl.es/examples/data/lshape.xml.gz")
mesh = Mesh(fpath)

# Define function spaces (P2-P1)
V = VectorFunctionSpace(mesh, "Lagrange", 2)
Q = FunctionSpace(mesh, "Lagrange", 1)

# Define trial and test functions
u = TrialFunction(V)
p = TrialFunction(Q)
v = TestFunction(V)
q = TestFunction(Q)

# Set parameter values
dt = 0.01
T = 3
nu = 0.01

# Define time-dependent pressure boundary condition
p_in = Expression("sin(3.0*t)", t=0.0, degree=2)

# Define boundary conditions
noslip = DirichletBC(
    V,
    (0, 0),
    (
        "on_boundary &&"
        "(x[0] < DOLFIN_EPS | x[1] < DOLFIN_EPS | "
        "(x[0] > 0.5 - DOLFIN_EPS && x[1] > 0.5 - DOLFIN_EPS))"
    ),
)
inflow = DirichletBC(Q, p_in, "x[1] > 1.0 - DOLFIN_EPS")
outflow = DirichletBC(Q, 0, "x[0] > 1.0 - DOLFIN_EPS")
bcu = [noslip]
bcp = [inflow, outflow]

# Create functions
u0 = Function(V)
u1 = Function(V)
p1 = Function(Q)

# Define coefficients
k = Constant(dt)
f = Constant((0, 0))

# Tentative velocity step
F1 = (
    (1 / k) * inner(u - u0, v) * dx
    + inner(grad(u0) * u0, v) * dx
    + nu * inner(grad(u), grad(v)) * dx
    - inner(f, v) * dx
)
a1 = lhs(F1)
L1 = rhs(F1)

# Pressure update
a2 = inner(grad(p), grad(q)) * dx
L2 = -(1 / k) * div(u1) * q * dx

# Velocity update
a3 = inner(u, v) * dx
L3 = inner(u1, v) * dx - k * inner(grad(p1), v) * dx

# Assemble matrices
A1 = assemble(a1)
A2 = assemble(a2)
A3 = assemble(a3)

# Use amg preconditioner if available
prec = "amg" if has_krylov_solver_preconditioner("amg") else "default"

# Use nonzero guesses - essential for CG with non-symmetric BC
parameters["krylov_solver"]["nonzero_initial_guess"] = True

# Time-stepping
for t in np.arange(0, T, dt):
    # Update pressure boundary condition
    p_in.t = t

    # Compute tentative velocity step
    b1 = assemble(L1)
    [bc.apply(A1, b1) for bc in bcu]
    solve(A1, u1.vector(), b1, "bicgstab", "default")

    # Pressure correction
    b2 = assemble(L2)
    [bc.apply(A2, b2) for bc in bcp]
    [bc.apply(p1.vector()) for bc in bcp]
    solve(A2, p1.vector(), b2, "bicgstab", prec)

    # Velocity correction
    b3 = assemble(L3)
    [bc.apply(A3, b3) for bc in bcu]
    solve(A3, u1.vector(), b3, "bicgstab", "default")

    # Move to next time step
    u0.assign(u1)
    t += dt

    # Plot solution
    plotter = plot(
        u1,
        mode="mesh and arrows",
        text="Velocity of fluid",
        cmap="jet",
        scale=0.3,  # unit conversion factor
        scalarbar=False,
        interactive=False,
    )
    plotter.remove("Arrows")

