Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
4426a015fThis file is pinned to the selected release. Download the original to open it in your design tool.
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"cell_style": "center",
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# 3.3 Nonlinear problems\n",
"\n",
"In this unit we turn our attention to nonlinear PDE problems and the tools that `NGSolve` provides to simplify it."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## A simple scalar PDE\n",
"Let us start with a simple PDE with a nonlinearity: \n",
"\n",
"$$\n",
"- \\Delta u + \\frac13 u^3 = 10 \\text{ in } \\Omega\n",
"$$\n",
"\n",
"on the unit square $\\Omega = (0,1)^2$. \n",
"\n",
"We note that this PDE can also be formulated as a nonlinear minimization problem (cf. [3.4](../unit-3.4-nonlmin/nonlmin.ipynb))."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
0
],
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# define geometry and generate mesh\n",
"from ngsolve import *\n",
"from ngsolve.webgui import *\n",
"from netgen.occ import *\n",
"\n",
"mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"In NGSolve we can solve the PDE conveniently using the *linearization* feature of `SymbolicBFI`.\n",
"\n",
"The `BilinearForm` (**which is not bilinear!**) needed in the weak formulation is\n",
"$$\n",
" A(u,v) = \\int_{\\Omega} \\nabla u \\nabla v + 1/3 u^3 v - 10 v ~ dx \\quad ( = 0 ~ \\forall~v \\in H^1_0)\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"V = H1(mesh, order=3, dirichlet=[1,2,3,4])\n",
"u,v = V.TnT()\n",
"a = BilinearForm(V)\n",
"a += (grad(u) * grad(v) + 1/3*u**3*v- 10 * v)*dx"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Newton's method\n",
"\n",
"In the preparation of the formulation of Newton's method we identify the semilinear form $A(\\cdot,\\cdot)$ with the corresponding operator $A: V \\to V'$ and write (assuming sufficient regularity)\n",
"\n",
" * $A(u)$ for the linear form $[A(u)](v) = A(u,v)$ and \n",
" * the derivative of $A$ at an evaluation point $u$ is the bilinear form $\\delta A(u)$ with\n",
" $$[\\delta A(u)] (w,v) = \\lim_{h \\to 0} \\frac{ A(u+h\\cdot w,v) - A(u,v)}{h}$$"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"\n",
"You could compute these linear and bilinear forms manually. However, `NGSolve` provides functions for both operations: \n",
"\n",
"* The linear form $A(u)$ for a given (vector) u is obtained from `a.Apply(vec,res)` (resulting in a vector `res` representing the linear form)\n",
"* The bilinear form (represented by the corresponding matrix) is stored in `a.mat` after a call of `a.AssembleLinearization(vec)`"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Under the hood it uses its functionality to derive `CoefficientFunction`s symbolically. \n",
"\n",
"For example, `NGSolve` derives the bilinear form integrand $ \\frac13 u^3 v $ w.r.t. $u$ at $\\tilde{u}$ in direction $w$ resulting in the integrand $\\tilde{u}^2 w v$.\n",
"\n",
"This allows to form the corresponding bilinear form integrals automatically for you."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"To obtain a Newton algorithm we hence only need to translate the following pseudo-code formulation of Newton's method to `NGSolve`. The pseudo code is:\n",
"\n",
"`NewtonSolve`(Pseudo code):\n",
"\n",
"* Given an initial guess $u^0$\n",
"* loop over $i=0,..$ until convergence:\n",
" * Compute linearization: $A (u^i) + \\delta A(u^i) \\Delta u^{i} = 0$:\n",
" * $r^i = A (u^i)$ \n",
" * $B^i = \\delta A(u^i)$ \n",
" * Solve $B^i \\Delta u^i = -r^i$\n",
" * Update $u^{i+1} = u^i + \\Delta u^{i}$\n",
" * Evaluate stopping criteria\n",
"\n",
"As a stopping criteria we take $\\langle A u^i,\\Delta u^i \\rangle = \\langle A u^i, A u^i \\rangle_{(B^i)^{-1}}< \\varepsilon$."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now, here comes the same thing in `NGSolve` syntax:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"def SimpleNewtonSolve(gfu,a,tol=1e-13,maxits=10, callback=lambda gfu: None):\n",
" res = gfu.vec.CreateVector()\n",
" du = gfu.vec.CreateVector()\n",
" fes = gfu.space\n",
" callback(gfu)\n",
" for it in range(maxits):\n",
" print (\"Iteration {:3} \".format(it),end=\"\")\n",
" a.Apply(gfu.vec, res)\n",
" a.AssembleLinearization(gfu.vec)\n",
" du.data = a.mat.Inverse(fes.FreeDofs()) * res\n",
" gfu.vec.data -= du\n",
" callback(gfu)\n",
" #stopping criteria\n",
" stopcritval = sqrt(abs(InnerProduct(du,res)))\n",
" print (\"<A u\",it,\", A u\",it,\">_{-1}^0.5 = \", stopcritval)\n",
" if stopcritval < tol:\n",
" break"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"cell_style": "center",
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's apply this to the previous PDE problem:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
}
},
"outputs": [],
"source": [
"gfu = GridFunction(V)\n",
"gfu.Set((x*(1-x))**4*(y*(1-y))**4) # initial guess\n",
"gfu_it = GridFunction(gfu.space,multidim=0)\n",
"cb = lambda gfu : gfu_it.AddMultiDimComponent(gfu.vec) # store current state\n",
"SimpleNewtonSolve(gfu, a, callback = cb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The iterations converge to a solution in a few steps."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cell_style": "center",
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"Draw(gfu_it,mesh,\"u\", deformation = True, animate = True, autoscale = True, min = 0, max = 0.73)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"cell_style": "center",
"slideshow": {
"slide_type": "-"
}
},
"source": [
"Use the `multidim`-Slider to inspect the results after the iterations."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"There are also some solvers shipped with NGSolve now. The `ngsolve.solvers.Newton` method allows you to use static condensation and is overall more refined (e.g. with damping options). For this tutorial we will mostly stay with the simple hand-crafted `SimpleNewtonSolve`. \n",
"Here is the alternative demonstrated once, nevertheless:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"from ngsolve.solvers import *\n",
"help(Newton)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Another example: Stationary Navier-Stokes:\n",
"\n",
"Next, we consider incompressible Navier-Stokes equations again. This time however stationary.\n",
"\n",
"Find $\\mathbf{u} \\in \\mathbf{V}$, $p \\in Q$, $\\lambda \\in \\mathbb{R}$ so that\n",
"\n",
"\\begin{align*}\n",
"\\int_{\\Omega} \\nu \\nabla \\mathbf{u} : \\nabla \\mathbf{v} + (\\mathbf{u} \\cdot \\nabla) \\mathbf{u} \\cdot \\mathbf{v}& - \\int_{\\Omega} \\operatorname{div}(\\mathbf{v}) p & &= \\int \\mathbf{f} \\cdot \\mathbf{v} && \\forall \\mathbf{v} \\in \\mathbf{V}, \\\\ \n",
"- \\int_{\\Omega} \\operatorname{div}(\\mathbf{u}) q & & \n",
"+ \\int_{\\Omega} \\lambda q\n",
"&= 0 && \\forall q \\in Q, \\\\\n",
"& \\int_{\\Omega} \\mu p & &= 0 && \\forall \\mu \\in \\mathbb{R}.\n",
"\\end{align*}\n",
"\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The domain $\\Omega$ is still $(0,1)^2$ and we prescribe homogenuous Dirichlet bnd. conditions for the velocity, except for the top boundary where we prescribe a tangential velocity. This setup is known as \"driven cavity\".\n",
"\n",
"Note that we use a scalar constraint to fix the pressure level that is otherwise not controlled in the presence of pure Dirichlet conditions."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"We use a higher order Taylor-Hood discretization again:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"mesh = Mesh (unit_square.GenerateMesh(maxh=0.05)); \n",
"nu = Parameter(1)\n",
"\n",
"V = VectorH1(mesh,order=3,dirichlet=\"bottom|right|top|left\")\n",
"Q = H1(mesh,order=2); \n",
"N = NumberSpace(mesh); \n",
"X = V*Q*N\n",
"(u,p,lam), (v,q,mu) = X.TnT()\n",
"a = BilinearForm(X)\n",
"a += (nu*InnerProduct(grad(u),grad(v))+InnerProduct(grad(u)*u,v)\n",
" -div(u)*q-div(v)*p-lam*q-mu*p)*dx"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The boundary condition:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"gfu = GridFunction(X)\n",
"gfu.components[0].Set(CF((4*x*(1-x),0)),\n",
" definedon=mesh.Boundaries(\"top\"))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now, let's apply the Newton:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
3,
11
],
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"def SolveAndVisualize(multidim=True):\n",
" gfu.components[0].Set(CF((4*x*(1-x),0)),\n",
" definedon=mesh.Boundaries(\"top\"))\n",
" if multidim:\n",
" gfu_it = GridFunction(gfu.space,multidim=0)\n",
" cb = lambda gfu : gfu_it.AddMultiDimComponent(gfu.vec) # store current state\n",
" SimpleNewtonSolve(gfu, a, callback = cb)\n",
" else:\n",
" SimpleNewtonSolve(gfu, a)\n",
" Draw(gfu.components[0],mesh, vectors = {\"grid_size\" : 25})\n",
" print(\"above you see the solution after the Newton solve.\")\n",
" if multidim:\n",
" Draw(gfu_it.components[0], mesh, vectors = {\"grid_size\" : 25})\n",
" print(\"above you can inspect the results after each iteration of the Newton solve (use multidim-slider).\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"SolveAndVisualize()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The problem becomes more interesting if we decrease the viscosity $\\nu$ (`nu`), i.e. if we increase the Reynolds number *Re*. Let's consider the previous setup for decreasing values of `nu`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"nu.Set(0.01)\n",
"SolveAndVisualize(multidim=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"nu.Set(0.01)\n",
"SolveAndVisualize()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"nu.Set(0.001)\n",
"SolveAndVisualize()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now, viscosity is so small that Newton does not converge anymore.\n",
"In this case we can fix it using a small damping parameter with the `ngsolve.solvers.Newton`-solver:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"run_control": {
"marked": false
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"nu.Set(0.001)\n",
"gfu.components[0].Set(CF((4*x*(1-x),0)),definedon=mesh.Boundaries(\"top\"))\n",
"Newton(a,gfu,maxit=20,dampfactor=0.06)\n",
"Draw(gfu.components[0],mesh, vectors = {\"grid_size\" : 25})"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Nonlinear minimization problems¶\n",
"\n",
"Sometimes we have to find minimizers of functionals of the form:\n",
"\n",
"\n",
"Find $u \\in V$ such that\n",
"\\begin{align*}\n",
"E(u)\\leq E(v) \\quad \\forall v \\in V.\n",
"\\end{align*}\n",
"\n",
"As an example consider the functional:\n",
"\n",
"\\begin{align*}\n",
"E(u) = \\int_{\\Omega} \\frac12 |\\nabla u|^2 + \\frac {1}{12} u^4 - 10 u \\, dx.\n",
"\\end{align*}\n",
"\n",
"The minimum is achived when the first variation of $E$ at $u$ is zero, i.e. when\n",
"\n",
"\\begin{align*}\n",
"\\delta E(u)[v] = 0 \\quad \\forall v \\in V.\n",
"\\end{align*}\n",
"or in other symbols\n",
"\\begin{align*}\n",
"-\\Delta u + \\frac13 u^3 = 10.\n",
"\\end{align*}\n",
"\n",
"The problem can be set up in the following way using the `Variation` keyword.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"V = H1(mesh, order=4, dirichlet=\".*\")\n",
"u = V.TrialFunction()\n",
"\n",
"a = BilinearForm(V, symmetric=True)\n",
"a += Variation ((0.5*grad(u)*grad(u) + 1/12*u**4-10*u) * dx)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the problem becomes equivalent to the one at the beginning of the jupiter notebook.\n",
"\n",
"NGSolve provides a `Newton`-solver for nonlinear minimization problems as well. It is called `NewtonMinimization`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ngsolve.solvers import *\n",
"\n",
"gfu = GridFunction(V)\n",
"\n",
"\n",
"gfu.Set((x*(1-x))**4*(y*(1-y))**4) # initial guess\n",
"NewtonMinimization(a, gfu)\n",
"\n",
"Draw(gfu, mesh, \"u\");"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}