Nonlinearity and boundary conditions
from numpy import *
from scipy import linalg
from scipy.linalg import norm
from matplotlib.pyplot import *
from prettytable import PrettyTable
from timeit import default_timer as timer
import sys
sys.path.append('fncbook/')
import fncbook as FNC
# This (optional) block is for improving the display of plots.
# from IPython.display import set_matplotlib_formats
# set_matplotlib_formats("svg","pdf")
# %config InlineBackend.figure_format = 'svg'
rcParams["figure.figsize"] = [7, 4]
rcParams["lines.linewidth"] = 2
rcParams["lines.markersize"] = 4
rcParams['animation.html'] = "jshtml" # or try "html5"
10.5. Nonlinearity and boundary conditions ¶ Collocation for nonlinear differential equations operates on the same principle as for linear problems: replace functions by vectors and replace derivatives by differentiation matrices. But because the differential equation is nonlinear, the resulting algebraic equations are as well. We will therefore need to use a quasi-Newton or similar method as part of the solution process.
We consider the TPBVP (10.1.1) , reproduced here:
u ′ ′ ( x ) = ϕ ( x , u , u ′ ) , a ≤ x ≤ b , g 1 ( u ( a ) , u ′ ( a ) ) = 0 , g 2 ( u ( b ) , u ′ ( b ) ) = 0. \begin{split}
u''(x) &= \phi(x,u,u'), \qquad a \le x \le b,\\
g_1(u(a),u'(a)) &= 0,\\
g_2(u(b),u'(b)) &= 0.
\end{split} u ′′ ( x ) g 1 ( u ( a ) , u ′ ( a )) g 2 ( u ( b ) , u ′ ( b )) = ϕ ( x , u , u ′ ) , a ≤ x ≤ b , = 0 , = 0. As in Collocation for linear problems , the function u ( x ) u(x) u ( x ) is replaced by a vector u \mathbf{u} u of its approximated values at nodes x 0 , x 1 , … , x n x_0,x_1,\ldots,x_n x 0 , x 1 , … , x n (see Equation (10.4.2) ). We define derivatives of the sampled function as in (10.4.3) and (10.4.4) , using suitable differentiation matrices D x \mathbf{D}_x D x and D x x \mathbf{D}_{xx} D xx .
The collocation equations, ignoring boundary conditions for now, are
D x x u − r ( u ) = 0 , \mathbf{D}_{xx} \mathbf{u} - \mathbf{r}(\mathbf{u}) = \boldsymbol{0}, D xx u − r ( u ) = 0 , where
r i ( u ) = ϕ ( x i , u i , u i ′ ) , i = 0 , … , n . r_i(\mathbf{u}) = \phi(x_i,u_i,u_i'), \qquad i=0,\ldots,n. r i ( u ) = ϕ ( x i , u i , u i ′ ) , i = 0 , … , n . and u ′ = D x u \mathbf{u}'=\mathbf{D}_x\mathbf{u} u ′ = D x u .
We impose the boundary conditions in much the same way as in Collocation for linear problems . Again define the rectangular boundary removal matrix E \mathbf{E} E as in (10.4.8) , and replace the equations in those two rows by the boundary conditions:
f ( u ) = [ E ( D x x u − r ( u ) ) g 1 ( u 0 , u 0 ′ ) g 2 ( u n , u n ′ ) ] = 0 . \mathbf{f}(\mathbf{u}) =
\begin{bmatrix}
\mathbf{E} \bigl( \mathbf{D}_{xx}\mathbf{u} - \mathbf{r}(\mathbf{u}) \bigr) \\[1mm]
g_1(u_0,u_0') \\[1mm]
g_2(u_n,u_n')
\end{bmatrix}
= \boldsymbol{0}. f ( u ) = ⎣ ⎡ E ( D xx u − r ( u ) ) g 1 ( u 0 , u 0 ′ ) g 2 ( u n , u n ′ ) ⎦ ⎤ = 0 . The left-hand side of (10.5.4) is a nonlinear function of the unknowns in the vector u \mathbf{u} u , so (10.5.4) is an ( n + 1 ) × 1 (n+1)\times 1 ( n + 1 ) × 1 set of nonlinear equations, amenable to solution by the techniques of Chapter 4 .
Given the BVP
u ′ ′ − sin ( x u ) + exp ( x u ′ ) = 0 , u ( 0 ) = − 2 , u ′ ( 3 / 2 ) = 1 , u'' - \sin(xu) + \exp(xu')=0, \quad u(0)=-2, \; u'(3/2)=1, u ′′ − sin ( xu ) + exp ( x u ′ ) = 0 , u ( 0 ) = − 2 , u ′ ( 3/2 ) = 1 , we compare to the standard form (10.5.1) and recognize
ϕ ( x , u , u ′ ) = sin ( x u ) − exp ( x u ′ ) . \phi(x,u,u') = \sin(xu)-\exp(xu'). ϕ ( x , u , u ′ ) = sin ( xu ) − exp ( x u ′ ) . Suppose n = 3 n=3 n = 3 for an equispaced grid, so that h = 1 2 h=\frac{1}{2} h = 2 1 , x 0 = 0 x_0=0 x 0 = 0 , x 1 = 1 2 x_1=\frac{1}{2} x 1 = 2 1 , x 2 = 1 x_2=1 x 2 = 1 , and x 3 = 3 2 x_3=\frac{3}{2} x 3 = 2 3 . There are four unknowns. We compute
D x x = 1 1 / 4 [ 2 − 5 4 − 1 1 − 2 1 0 0 1 − 2 1 − 1 4 − 5 2 ] , D x = 1 1 [ − 3 4 − 1 0 − 1 0 1 0 0 − 1 0 1 0 1 − 4 3 ] , E r ( u ) = [ sin ( u 1 2 ) − exp ( u 2 − u 0 2 ) sin ( u 2 ) − exp ( u 3 − u 1 ) ] , f ( u ) = [ ( 4 u 0 − 8 u 1 + 4 u 2 ) − sin ( u 1 2 ) + exp ( u 2 − u 0 2 ) ( 4 u 1 − 8 u 2 + 4 u 3 ) − sin ( u 2 ) + exp ( u 3 − u 1 ) u 0 + 2 ( u 1 − 4 u 2 + 3 u 3 ) − 1 ] . \begin{gather*}
\mathbf{D}_{xx} = \frac{1}{1/4}
\begin{bmatrix}
2 & -5 & 4 & -1 \\
1 & -2 & 1 & 0 \\
0 & 1 & -2 & 1 \\
-1 & 4 & -5 & 2
\end{bmatrix}, \quad
\mathbf{D}_x = \frac{1}{1}
\begin{bmatrix}
-3 & 4 & -1 & 0 \\
-1 & 0 & 1 & 0 \\
0 & -1 & 0 & 1 \\
0 & 1 & -4 & 3
\end{bmatrix},
\\[3mm]
\mathbf{E} \mathbf{r}(\mathbf{u}) =
\begin{bmatrix}
\sin\left(\frac{u_1}{2}\right) - \exp\left(\frac{u_2-u_0}{2}\right) \\[1mm]
\sin(u_2) - \exp\left( u_3-u_1 \right)
\end{bmatrix},
\\[3mm]
\mathbf{f}(\mathbf{u}) =
\begin{bmatrix}
(4u_0 -8u_1 + 4u_2) - \sin\left(\frac{u_1}{2}\right) + \exp\left(\frac{u_2-u_0}{2}\right) \\[1mm]
(4u_1 -8u_2 + 4u_3) - \sin(u_2) + \exp\left( u_3-u_1 \right) \\[1mm]
u_0 + 2 \\[1mm]
(u_1 - 4u_2 + 3u_3) - 1
\end{bmatrix}.
\end{gather*} D xx = 1/4 1 ⎣ ⎡ 2 1 0 − 1 − 5 − 2 1 4 4 1 − 2 − 5 − 1 0 1 2 ⎦ ⎤ , D x = 1 1 ⎣ ⎡ − 3 − 1 0 0 4 0 − 1 1 − 1 1 0 − 4 0 0 1 3 ⎦ ⎤ , Er ( u ) = [ sin ( 2 u 1 ) − exp ( 2 u 2 − u 0 ) sin ( u 2 ) − exp ( u 3 − u 1 ) ] , f ( u ) = ⎣ ⎡ ( 4 u 0 − 8 u 1 + 4 u 2 ) − sin ( 2 u 1 ) + exp ( 2 u 2 − u 0 ) ( 4 u 1 − 8 u 2 + 4 u 3 ) − sin ( u 2 ) + exp ( u 3 − u 1 ) u 0 + 2 ( u 1 − 4 u 2 + 3 u 3 ) − 1 ⎦ ⎤ . 10.5.1 Implementation ¶ Our implementation using second-order finite differences is Function 10.5.1 . It’s surprisingly short, considering how general it is, because we have laid a lot of groundwork already.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def bvp(phi, xspan, ga, gb, init):
"""
bvp(phi, xspan, ga, gb, init)
Use finite differences to solve a two-point boundary value problem.
The ODE is u'' = phi(x, u, u') for x in (a,b). The functions
ga(u(a), u'(a)) and gb(u(b), u'(b)) specify the boundary conditions.
The value init is an initial guess for [u(a), u'(a)].
Return vectors for the nodes and the values of u.
"""
n = len(init) - 1
x, Dx, Dxx = diffmat2(n, xspan)
h = x[1] - x[0]
def residual(u):
# Compute the difference between u'' and phi(x,u,u') at the
# interior nodes and appends the error at the boundaries.
du_dx = Dx @ u # discrete u'
d2u_dx2 = Dxx @ u # discrete u''
f = d2u_dx2 - phi(x, u, du_dx)
# Replace first and last values by boundary conditions.
f[0] = ga(u[0], du_dx[0]) / h
f[n] = gb(u[n], du_dx[n]) / h
return f
u = levenberg(residual, init.copy())
return x, u[-1]
The nested function residual uses differentiation matrices computed externally to it, rather than computing them anew on each invocation. As in Function 10.4.1 , there is no need to form the row-deletion matrix E \mathbf{E} E explicitly. In lines 23--24, we divide the values of g 1 g_1 g 1 and g 2 g_2 g 2 by a factor of h h h . This helps scale the residual components more uniformly and improves the robustness of convergence a bit.
In order to solve a particular problem, we must write a function that computes ϕ \phi ϕ for vector-valued inputs x \mathbf{x} x , u \mathbf{u} u , and u ′ \mathbf{u}' u ′ , and functions for the boundary conditions. We also have to supply init, which is an estimate of the solution used to initialize the quasi-Newton iteration. Since this argument is a vector of length n + 1 n+1 n + 1 , it sets the value of n n n in the discretization.
Suppose a damped pendulum satisfies the nonlinear equation θ ′ ′ + 0.05 θ ′ + sin θ = 0 \theta'' + 0.05\theta'+\sin \theta =0 θ ′′ + 0.05 θ ′ + sin θ = 0 . We want to start the pendulum at θ = 2.5 \theta=2.5 θ = 2.5 and give it the right initial velocity so that it reaches θ = − 2 \theta=-2 θ = − 2 at exactly t = 5 t=5 t = 5 . This is a boundary-value problem with Dirichlet conditions θ ( 0 ) = 2.5 \theta(0)=2.5 θ ( 0 ) = 2.5 and θ ( 5 ) = − 2 \theta(5)=-2 θ ( 5 ) = − 2 .
The first step is to define the function ϕ \phi ϕ that equals θ ′ ′ \theta'' θ ′′ .
phi = lambda t, theta, omega: -0.05 * omega - sin(theta)
Next, we define the boundary conditions.
ga = lambda u, du: u - 2.5
gb = lambda u, du: u + 2
The last ingredient is an initial estimate of the solution. Here we choose n = 100 n=100 n = 100 and a linear function between the endpoint values.
init = linspace(2.5, -2, 101)
We find a solution with negative initial slope, i.e., the pendulum is initially pushed back toward equilibrium.
t, theta = FNC.bvp(phi, [0, 5], ga, gb, init)
plot(t, theta)
xlabel("$t$")
ylabel("$\theta(t)$")
title("Pendulum over [0,5]");If we extend the time interval longer for the same boundary values, then the initial slope must adjust.
t, theta = FNC.bvp(phi, [0, 8], ga, gb, init)
plot(t, theta)
xlabel("$t$")
ylabel("$\theta(t)$")
title("Pendulum over [0,8]");This time, the pendulum is initially pushed toward the unstable equilibrium in the upright vertical position before gravity pulls it back down.
The initial solution estimate can strongly influence how quickly a solution is found, or whether the quasi-Newton iteration converges at all. In situations where multiple solutions exist, the initialization can determine which is found.
We look for a solution to the parameterized membrane deflection problem from Example 10.1.2 ,
w ′ ′ + 1 r w ′ = λ w 2 , w ′ ( 0 ) = 0 , w ( 1 ) = 1. w''+ \frac{1}{r}w'= \frac{\lambda}{w^2},\quad w'(0)=0,\; w(1)=1. w ′′ + r 1 w ′ = w 2 λ , w ′ ( 0 ) = 0 , w ( 1 ) = 1. Here is the problem definition. We use a truncated domain to avoid division by zero at r = 0 r=0 r = 0 .
lamb = 0.5
phi = lambda r, w, dwdr: lamb / w**2 - dwdr / r
a, b = finfo(float).eps, 1
ga = lambda w, dw: dw
gb = lambda w, dw: w - 1
First we try a constant function as the initialization.
init = ones(201)
r, w1 = FNC.bvp(phi, [a, b], ga, gb, init)
plot(r, w1)
fig, ax = gcf(), gca()
xlabel("$r$"), ylabel("$w(r)$")
title("Solution of the MEMS problem");It’s not necessary that the initialization satisfy the boundary conditions. In fact, by choosing a different constant function as the initial guess, we arrive at another valid solution.
r, w2 = FNC.bvp(phi, [a, b], ga, gb, 0.5 * init)
ax.plot(r, w2)
ax.set_title("Multiple solutions of the MEMS problem");
fig10.5.2 Parameter continuation ¶ Sometimes the best way to get a useful initialization is to use the solution of a related easier problem, a technique known as parameter continuation . In this approach, one solves the problem at an easy parameter value, and gradually changes the parameter value to the desired value. After each change, the most recent solution is used to initialize the iteration at the new parameter value.
We solve the stationary Allen–Cahn equation ,
ϵ u ′ ′ = u 3 − u , 0 ≤ x ≤ 1 , u ′ ( 0 ) = 0 , u ( 1 ) = 1. \epsilon u'' = u^3-u, \quad 0 \le x \le 1, \quad u'(0)=0, \; u(1)=1. ϵ u ′′ = u 3 − u , 0 ≤ x ≤ 1 , u ′ ( 0 ) = 0 , u ( 1 ) = 1. phi = lambda x, u, dudx: (u**3 - u) / epsilon
ga = lambda u, du: du
gb = lambda u, du: u - 1
Finding a solution is easy at larger values of ϵ \epsilon ϵ .
epsilon = 0.05
init = linspace(-1, 1, 141)
x, u1 = FNC.bvp(phi, [0, 1], ga, gb, init)
plot(x, u1, label="$\\epsilon = 0.05$")
fig, ax = gcf(), gca()
xlabel("$x$"), ylabel("$u(x)$")
legend(), title("Allen-Cahn solution");/Users/driscoll/miniforge3/envs/myst/lib/python3.13/site-packages/fncbook/chapter04.py:167: UserWarning: Iteration did not find a root.
warnings.warn("Iteration did not find a root.")
Finding a good initialization is not trivial for smaller values of ϵ \epsilon ϵ . But the iteration succeeds if we use the first solution as the initialization at the smaller ϵ \epsilon ϵ .
epsilon = 0.002
x, u2 = FNC.bvp(phi, [0, 1], ga, gb, u1)
ax.plot(x, u2, label="$\\epsilon = 0.002$")
ax.legend()
figIn this case we can continue further.
ϵ = 0.0005
x, u3 = FNC.bvp(phi, [0, 1], ga, gb, u2)
ax.plot(x, u3, label="$\\epsilon = 0.005$")
ax.legend()
fig10.5.3 Exercises ¶ ✍ This exercise is about the nonlinear boundary-value problem
u ′ ′ = 3 ( u ′ ) 2 u , u ( − 1 ) = 1 , u ( 2 ) = 1 2 . u'' = \frac{3(u')^2}{u} , \quad u(-1) = 1, \; u(2) = \frac{1}{2}. u ′′ = u 3 ( u ′ ) 2 , u ( − 1 ) = 1 , u ( 2 ) = 2 1 . (a) Verify that the exact solution is u ( x ) = ( x + 2 ) − 1 / 2 u(x) = ( x+2 )^{-1/2} u ( x ) = ( x + 2 ) − 1/2 .
(b) Write out the finite-difference approximation (10.5.4) with a single interior point (n = 2 n=2 n = 2 ).
(c) Solve the equation of part (b) for the lone interior value u 1 u_1 u 1 .
⌨
(a) Use Function 10.5.1 to solve the problem of Exercise 10.5.1 for n = 80 n=80 n = 80 . In a 2-by-1 subplot array, plot the finite-difference solution and its error.
(b) ⌨ For each n = 10 , 20 , 40 , … , 640 n=10,20,40,\ldots,640 n = 10 , 20 , 40 , … , 640 , find the infinity norm of the error on the same problem. Make a log-log plot of error versus n n n and include a graphical comparison to second-order convergence.
⌨ (Adapted from Ascher & Petzold (1998) .) Use Function 10.5.1 twice with n = 200 n=200 n = 200 to solve
u ′ ′ + e u + 0.5 = 0 , u ( 0 ) = u ( 1 ) = 0 , u'' + e^{u+0.5} = 0, \quad u(0) = u(1) = 0, u ′′ + e u + 0.5 = 0 , u ( 0 ) = u ( 1 ) = 0 , with initializations 7 sin ( x ) 7 \sin(x) 7 sin ( x ) and 1 4 sin ( x ) \frac{1}{4} \sin(x) 4 1 sin ( x ) . Plot the solutions together on one graph.
⌨ Use Function 10.5.1 to compute the solution to the Allen–Cahn equation in Example 10.5.4 with ϵ = 0.02 \epsilon=0.02 ϵ = 0.02 . Determine numerically whether it is antisymmetric around the line x = 0.5 x=0.5 x = 0.5 ---that is, whether u ( 1 − x ) = − u ( x ) u(1-x)=-u(x) u ( 1 − x ) = − u ( x ) . You should supply evidence that your answer is independent of n n n .
⌨ Consider the pendulum problem from Example 10.1.1 with g = L = 1 g=L=1 g = L = 1 . Suppose we want to release the pendulum from rest such that θ ( 5 ) = π / 2 \theta(5)=\pi/2 θ ( 5 ) = π /2 . Use Function 10.5.1 with n = 200 n=200 n = 200 to find one solution that passes through θ = 0 \theta=0 θ = 0 , and another solution that does not. Plot θ ( t ) \theta(t) θ ( t ) for both cases together.
⌨ The BVP
u ′ ′ = x sign ( 1 − x ) u , u ( − 6 ) = 1 , u ′ ( 6 ) = 0 , u'' = x \operatorname{sign}(1-x) u, \quad u(-6)=1, \; u'(6)=0, u ′′ = x sign ( 1 − x ) u , u ( − 6 ) = 1 , u ′ ( 6 ) = 0 , forces u ′ ′ u'' u ′′ to be discontinuous at x = 1 x=1 x = 1 , so finite differences may not converge to the solution at their nominal order of accuracy.
(a) Solve the problem using Function 10.5.1 with n = 1400 n=1400 n = 1400 , and make a plot of the solution. Store the value at x = 6 x=6 x = 6 for use as a reference high-accuracy solution.
(b) For each n = 100 , 200 , 300 , … , 1000 n=100,200,300,\ldots,1000 n = 100 , 200 , 300 , … , 1000 , apply Function 10.5.1 , and compute the error at x = 6 x=6 x = 6 . Compare the convergence graphically to second order.
⌨ The following nonlinear BVP was proposed by Carrier (for the special case b = 1 b=1 b = 1 in Carrier (1970) ):
ϵ u ′ ′ + 2 ( 1 − x 2 ) u + u 2 = 1 , u ( − 1 ) = u ( 1 ) = 0. \epsilon u'' + 2(1-x^2)u +u^2 = 1, \quad u(-1) = u(1) = 0. ϵ u ′′ + 2 ( 1 − x 2 ) u + u 2 = 1 , u ( − 1 ) = u ( 1 ) = 0. In order to balance the different components of the residual, it’s best to implement each boundary condition numerically as u / ϵ = 0 u/\epsilon=0 u / ϵ = 0 .
(a) Use Function 10.5.1 to solve the problem with ϵ = 0.003 \epsilon=0.003 ϵ = 0.003 , n = 200 n=200 n = 200 , and an initial estimate of all zeros. Plot the result; you should get a solution with 9 local maxima.
(b) Starting with the result from part (a) as an initialization, continue the parameter through the sequence
ϵ = 3 × 1 0 − 3 , 3 × 1 0 − 2.8 , 3 × 1 0 − 2.6 , … , 3 × 1 0 − 1 . \epsilon = 3\times 10^{-3}, 3\times 10^{-2.8}, 3\times 10^{-2.6},\ldots, 3\times 10^{-1}. ϵ = 3 × 1 0 − 3 , 3 × 1 0 − 2.8 , 3 × 1 0 − 2.6 , … , 3 × 1 0 − 1 . The most recent solution should be used as the initialization for each new value of ϵ \epsilon ϵ . Plot the end result for ϵ = 0.3 \epsilon=0.3 ϵ = 0.3 ; it should have one interior local maximum.
(c) Starting with the last solution of part (b), reverse the continuation steps to return to ϵ = 0.003 \epsilon=0.003 ϵ = 0.003 . Plot the result, which is an entirely different solution from part (a).
⌨ Example 10.5.3 finds two solutions at λ = 0.5 \lambda=0.5 λ = 0.5 . Continue both solutions by taking 50 steps from λ = 0.5 \lambda=0.5 λ = 0.5 to λ = 0.79 \lambda=0.79 λ = 0.79 . Make a plot with λ \lambda λ on the horizontal axis and w ( 0 ) w(0) w ( 0 ) on the vertical axis, with one point to represent each solution found. You should get two paths that converge as λ \lambda λ approaches 0.79 from below.
Ascher, U. M., & Petzold, L. R. (1998). Computer Methods for Ordinary Differential Equations and Differential-Algebraic Equations . Society for Industrial and Applied Mathematics. 10.1137/1.9781611971392 Carrier, G. F. (1970). Singular Perturbation Theory and Geophysics. SIAM Review , 12 (2), 175–193. 10.1137/1012041