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"
So far we have considered the method of lines for problems with periodic end conditions, which is much like having no boundary at all. How can boundary conditions be incorporated into this technique?
Not all such PDEs are parabolic (essentially, including diffusion), but we will assume this to be the case. Suppose also that the solution is subject to the boundary conditions
As usual, we replace u(x,t) by the semidiscretized u(t), where ui(t)≈u^(xi,t) and i=0,…,m. We require the endpoints of the interval to be included in the discretization, that is, x0=a and xm=b. Then we have a division of the semidiscrete unknown u(t) into interior and boundary nodes:
where v are the solution values over the interior of the interval. The guiding principle is to let the interior unknowns v be governed by a discrete form of the PDE, while the endpoint values are chosen to satisfy the boundary conditions. As a result, we will develop an initial-value problem for the interior unknowns only:
Given a value of v for the interior nodes, Equation (11.5.6) may be considered a system of two equations for the unknown boundary values u0 and um. This system will be a linear one for Dirichlet, Neumann, and Robin conditions.
The steps to evaluate f in (11.5.4) now go as follows.
Our full implementation of the method of lines for (11.5.1)--(11.5.2) is given in Function 11.5.2. It uses Function 10.3.2 (diffcheb) to set up a Chebyshev discretization. The nested function extend performs steps 1--2 of Algorithm 11.5.1 by calling Function 4.6.3 (levenberg) to solve the potentially nonlinear system (11.5.6). Then it sets up and solves an IVP, adding steps 3--4 of Algorithm 11.5.1 within the ode! function. Finally, it returns the node vector x and a function of t that applies extend to v(t) to compute u(t).
In many specific problems, extend does more work than is truly necessary. Dirichlet boundary conditions, for instance, define u0 and um directly, and there is no need to solve a nonlinear system. Exercise 11.5.6 asks you to modify the code to take advantage of this case. The price of solving a more general set of problems in Function 11.5.2 is some speed in such special cases.[1]
Finally, we return to the example of the Black–Scholes equation from Black–Scholes equation.
An important advanced feature of Julia is multiple dispatch, which allows you to make multiple definitions of a function for different sequences and types of input arguments. Thus, addition to the original Function 11.5.2, we could also define a modified version in which g₁ and g₂ are of numeric type for the Dirichlet case. The correct version would be chosen (dispatched) depending on how the boundary conditions were supplied by the caller, allowing us speed when possible and generality as a fallback.