Nonlinear Poisson equation

In this section, we'll demonstrate how to utilize Bramble.jl to solve a nonlinear Poisson equation with Dirichlet boundary conditions.


Problem description

Let's consider the following nonlinear Poisson equation on a 1-dimensional square domain $\Omega = [0,1]$,

\[\begin{align*} - \frac{\partial}{\partial x} \left( \alpha(u) \frac{\partial u}{\partial x} (x) \right) &= f(x), \, x \in \Omega \\ u(x) &= g(x), \, x \in \partial \Omega. \end{align*}\]

We define

\[\alpha (u) = 3 + \frac{1} { 1 + u^2}\]

and

\[u(x,y) = e^{x + y}, \, (x,y) \in [0,1]^2.\]

Function f and g are calculated such that u is the exact solution of the problem.

Discretization

We refer to Linear Poisson equation for most of the notations used. To discretize the problem above, we just need to introduce an averaging operator on grid functions

\[M_h (u_h)(x_i) = \frac{u_h(x_i) + u_h(x_{i-1})}{2}.\]

This allows to discretize the differential problem as the following variational problem

find $u_h \in W_h(\overline{\Omega}_h)$, with $u_h(x_i) = u(x_i)$ on $\partial \overline{\Omega}_h,$ such that

\[(\alpha(u_h) D_{-x} u_h, D_{-x} v_h)_+ = ((g)_h, v_h)_h, \, \forall v_h \in W_{h,0}(\overline{\Omega}_h)\]

Implementation

To solve this nonlinear problem, we can use a standard fixed point iteration.

We start by loading the packages needed

using Bramble
using LinearSolve
using ILUZero      # for reusable sparsity pattern

and define the domain and relevant functions to the problem

I = interval(0, 1)
Ω = domain(I)

sol = @embed(Ω, x -> exp(x[1]))
coeff = @embed(I, u -> 3 + 1 / (1 + u[1]^2))
Ap = @embed(I, u -> -2 * u[1] / (1 + u[1]^2)^2)
g = @embed(Ω, x -> -d * Ap(sol(x)) * sol(x)^2 - d * A(sol(x)) * sol(x))

Next, we define a mesh and reinterpret the solution and right hand side functions as being defined on the mesh.

Mh = mesh(Ω, (10, 20), (true, false))
sol = @embed(Mh, sol)
rhs = @embed(Mh, rhs)
bc = dirichletbcs(sol)

Now we define the space of grid functions and do some calculations for the right hand side

Wh = gridspace(Mh)
u = element(Wh, 0)

uold = similar(u)
avgₕ!(uold, rhs)

Next, we introduce the linear and bilinear forms associated with the problem. Here we use a uold vector which is due the linearization of the nonlinear function we had before.

l(V) = innerₕ(uold, V)
lform = form(l, Wh)
F = assemble(lform, bc)

A(u) = D == 1 ? coeff.(M₋ₕ(u)) : sum(ntuple(i -> coeff.(M₋ₕ(u)[i]), D)) ./ D
a(U, V) = inner₊(A(u) * ∇₋ₕ(U), ∇₋ₕ(V))
bform = form(a, Wh, Wh)
mat = assemble(bform, bc)

We are aiming at calculating the fixed point of

\[(\alpha(u_h) D_{-x} u_h, D_{-x} v_h)_+ = (u_h, v_h)_h\]

by using the iterative scheme: given u_{h,0}, solve for n=1,\dots

\[\left(\alpha(u_{h}^{(n)}) D_{-x} u_h, D_{-x} v_h \right)_+ = (u_h, v_h)_h\]

This is basically implemented in the following function

function fixed_point!(A, F, bform, bc, uold, u, α)
  prec = ilu0(A)
  prob = LinearProblem(A, F, KrylovJL_GMRES(), Pl = prec)
  linsolve = init(prob)

  for i in 1:2000
    uold.values .= α(u)
    assemble!(A, bform, bc)

    uold .= u

    linsolve.A = A
    sol = solve!(linsolve)

    u.values .= sol.u
    uold.values .-= u.values

    if norm₁ₕ(uold) < 1e-10
      break
    end
  end
end

and we implemented a simple stopping criteria based on the norm₁ₕ of the the difference between two consecutive iterations. Finally, we just need to call the fixed point function and we are done

fixed_point!(mat, F, bform, bc, uold, u, A)
u .-= uold

Vector u has the approximate solution to u_h.