modifiedMethodOfLines¶
Solves a system of partial differential equations of the form \(u_t = f(x, t, u, u_x, u_{xx})\) using the method of lines. The solution is represented with cubic Hermite polynomials.
Synopsis¶
modifiedMethodOfLinesMgr (task, state)
modifiedMethodOfLines (npdes, t, tend, nx, xbreak, y, state, fcnUt, fcnBc)
Required Arguments for modifiedMethodOfLinesMgr¶
- int
task
(Input) - This function must be called with task set to
PDE_INITIALIZE
to set up memory and default values prior to solving a problem and with task equal toPDE_RESET
to clean up after it has solved. - void
state
(Input/Output) - The current state of the PDE solution is held in a structure pointed to
by
state
. It cannot be directly manipulated.
Required Arguments for modifiedMethodOfLines¶
- int
npdes
(Input) - Number of differential equations.
- float
t
(Input/Output) - Independent variable. On input, t supplies the initial time,
\(t_0\). On output, t is set to the value to which the integration
has been updated. Normally, this new value is
tend
. - float
tend
(Input) - Value of t =
tend
at which the solution is desired. - int
nx
(Input) - Number of mesh points or lines.
- float
xbreak[]
(Input) - Array of length
nx
containing the breakpoints for the cubic Hermite splines used in the x discretization. The points inxbreak
must be strictly increasing. The valuesxbreak
[0] andxbreak
[nx
- 1] are the endpoints of the interval. - float
y[]
(Input/Output) - Array of length
npdes
bynx
containing the solution. The arrayy
contains the solution as y[k
,i
] = \(u_k\)(x
,tend
) at x =xbreak
[i
]. On input, y contains the initial values. It must satisfy the boundary conditions. On output,y
contains the computed solution. - void
state
(Input/Output) - The current state of the PDE solution is held in a structure pointed to
by state. It must be initialized by a call to
modifiedMethodOfLinesMgr
. It cannot be directly manipulated. - void
fcnUt
(npdes
,x
,t
,u[]
,ux[]
,uxx[]
,ut[]
) - User-supplied function to evaluate \(u_t\).
- int
npdes
(Input) - Number of equations.
- float
x
(Input) - Space variable, x.
- float
t
(Input) - Time variable, t.
- float
u[]
(Input) - Array of length
npdes
containing the dependent values, u. - float
ux[]
(Input) - Array of length
npdes
containing the first derivatives, \(u_x\). - float
uxx[]
(Input) - Array of length
npdes
containing the second derivative, \(u_{xx}\). - float
ut[]
(Output) - Array of length
npdes
containing the computed derivatives \(u_t\). - void
fcnBc
(npdes
,x
,t
,alpha[]
,beta[]
,gamma[]
) - User-supplied function to evaluate the boundary conditions. The boundary
conditions accepted by
modifiedMethodOfLines
are
NOTE: Users must supply the values \(\alpha_k\), \(\beta_k\), and \(y_k\). |
- int
npdes
(Input) - Number of equations.
- float
x
(Input) - Space variable, x.
- float
t
(Input) - Time variable, t.
- float
alpha[]
(Output) - Array of length
npdes
containing the \(\alpha_k\) values. - float
beta[]
(Output) - Array of length
npdes
containing the \(\beta_k\) values. - float
gamma[]
(Output) - Array of length
npdes
containing the values of \(y_k\).
Optional Arguments¶
tol
, float (Input)Differential equation error tolerance. An attempt is made to control the local error in such a way that the global relative error is proportional to
tol
.Default:
tol
= 100.0\*machine
(4)initialStepsize
, float (Input)Initial step size in the t integration. This value must be nonnegative. If
initialStepsize
is zero, an initial step size of 0.001|tend
- \(t_0\)| will be arbitrarily used. The step will be applied in the direction of integration.Default:
initialStepsize
= 0.0initialValueDerivative
, float[[]]
(Input/Output)Supply the derivative values \(u_x(x, t_0)\) in
initialValueDerivative
, an array of lengthnpdes
bynx
. This derivative information is input as\[\mathrm{initialValueDerivative[k,i]} = \frac{\partial u_k}{\partial x} \left(x, t_0\right) \text{ at } x = x[i]\]The array
initialValueDerivative
contains the derivative values as output:\[\mathrm{initialValueDerivative[k,i]} = \frac{\partial u_k}{\partial x} \left(x, \mathrm{tend}\right) \text{ at } x = x[i]\]Default: Derivatives are computed using cubic spline interpolation.
htrial
(Output)- Return the current trial step size.
Description¶
Let M = npdes
, N = nx
and \(x_i\) = xbreak
(I
). The
function modifiedMethodOfLines
uses the method of lines to solve the
partial differential equation system
with the initial conditions
and the boundary conditions
for \(k = 1, ..., M\).
Cubic Hermite polynomials are used in the x variable approximation so that the trial solution is expanded in the series
where \(\phi_i(x)\) and \(\psi_i(x)\) are the standard basis functions for the cubic Hermite polynomials with the knots \(x_1 < x_2 < ... < x_N\). These are piecewise cubic polynomials with continuous first derivatives. At the breakpoints, they satisfy
According to the collocation method, the coefficients of the approximation are obtained so that the trial solution satisfies the differential equation at the two Gaussian points in each subinterval,
for \(j = 1, ..., N\). The collocation approximation to the differential equation is
for \(k = 1, ..., M\) and \(j = 1, ..., 2(N - 1)\).
This is a system of \(2M(N - 1)\) ordinary differential equations in 2M N unknown coefficient functions, \(a_{k,i}\) and \(b_{k,i}\). This system can be written in the matrix‑vector form as \(A dc / dt = F(t, c)\) with \(c(t_0) = c_0\) where c is a vector of coefficients of length 2M N and \(c_0\) holds the initial values of the coefficients. The last 2M equations are obtained from the boundary conditions.
If \(α_k = β_k = 0\), it is assumed that no boundary condition is desired for the k‑th unknown at the left endpoint. A similar comment holds for the right endpoint. Thus, collocation is done at the endpoint. This is generally a useful feature for systems of first-order partial differential equations.
The input/output array Y
contains the values of the \(a_{k,i}\). The
initial values of the \(b_{k,i}\) are obtained by using the PyIMSL cubic
spline function cubSplineInterpECnd
(Interpolation and Approximation) to construct functions
such that
The PyIMSL function cubSplineValue (Interpolation and Approximation) is used to approximate the values
Optional argument initialValueDerivative
allows the user to provide the
initial values of \(b_{k,i}\).
The order of matrix A is 2M N and its maximum bandwidth is 6M ‑ 1. The band structure of the Jacobian of F with respect to c is the same as the band structure of A. This system is solved using a modified version of odeAdamsKrogh. Numerical Jacobians are used exclusively. Gear’s BDF method is used as the default because the system is typically stiff. For more details, see Sewell (1982).
Four examples of PDEs are now presented that illustrate how users can interface their problems with PyIMSL PDE solving software. The examples are small and not indicative of the complexities that most practitioners will face in their applications. A set of seven sample application problems, some of them with more than one equation, is given in Sincovec and Madsen (1975). Two further examples are given in Madsen and Sincovec (1979).
Examples¶
Example 1¶
The normalized linear diffusion PDE, \(u_t = u_{xx}\), \(0 \leq x \leq 1\), \(t > t_0\), is solved. The initial values are \(t_0 = 0\), \(u(x, t_0) = u_0 = 1\). There is a “zero-flux” boundary condition at \(x = 1\), namely \(u_x(1, t) = 0\), (\(t > t_0\)). The boundary value of \(u(0, t)\) is abruptly changed from \(u_0\) to the value 0, for \(t > 0\).
When the boundary conditions are discontinuous, or incompatible with the initial conditions such as in this example, it may be important to use double precision.
from numpy import *
from pyimsl.math.modifiedMethodOfLines import modifiedMethodOfLines
from pyimsl.math.modifiedMethodOfLinesMgr import modifiedMethodOfLinesMgr, PDE_INITIALIZE, PDE_RESET
from pyimsl.math.writeMatrix import writeMatrix
def fcnut(npdes, x, t, u, ux, uxx, ut):
# Define the PDE
ut[0] = uxx[0]
def fcnbc(npdes, x, t, alpha, beta, gam):
delta = 0.09
u0 = 1.0
u1 = 0.1
# Define boundary conditions
if (x == 0.0):
# These are for x=0
alpha[0] = 1.0
beta[0] = 0.0
gam[0] = u1
# If in the boundary layer, compute
# nonzero gamma
if (t <= delta):
gam[0] = u0 + (u1 - u0) * t / delta
else:
# These are for x=1
alpha[0] = 0.0
beta[0] = 1.0
gam[0] = 0.0
npdes = 1
nx = 8
nstep = 10
t = [0.0]
tend = 0.0
xbreak = zeros((8), dtype='double')
y = zeros((1, 8), dtype='double')
state = []
# Set breakpoints and initial conditions
for i in range(0, nx):
xbreak[i] = double(i) / double(nx - 1)
y[0, i] = 1.0
# Initialize the solver
tol = 10.e-4
hinit = 0.01 * tol
modifiedMethodOfLinesMgr(PDE_INITIALIZE, state,
tol=tol, hinit=hinit)
# while (j <= nstep):
for j in range(1, nstep + 1):
tend = float(j) / float(nstep)
tend *= tend
# Solve the problem
modifiedMethodOfLines(t, float(tend), xbreak, y, state, fcnut, fcnbc)
# Print results at current t=tend
title = "Solution at t = %4.2f" % (tend)
writeMatrix(title, y)
modifiedMethodOfLinesMgr(PDE_RESET, state)
Output¶
Solution at t = 0.01
1 2 3 4 5 6
0.900 0.985 0.999 1.000 1.000 1.000
7 8
1.000 1.000
Solution at t = 0.04
1 2 3 4 5 6
0.600 0.834 0.941 0.982 0.996 0.999
7 8
1.000 1.000
Solution at t = 0.09
1 2 3 4 5 6
0.1002 0.4906 0.7304 0.8673 0.9395 0.9743
7 8
0.9891 0.9931
Solution at t = 0.16
1 2 3 4 5 6
0.1000 0.3143 0.5092 0.6701 0.7906 0.8710
7 8
0.9160 0.9304
Solution at t = 0.25
1 2 3 4 5 6
0.1000 0.2571 0.4052 0.5361 0.6435 0.7228
7 8
0.7713 0.7876
Solution at t = 0.36
1 2 3 4 5 6
0.1000 0.2178 0.3295 0.4296 0.5129 0.5755
7 8
0.6142 0.6273
Solution at t = 0.49
1 2 3 4 5 6
0.1000 0.1853 0.2662 0.3389 0.3995 0.4451
7 8
0.4734 0.4830
Solution at t = 0.64
1 2 3 4 5 6
0.1000 0.1589 0.2149 0.2650 0.3070 0.3385
7 8
0.3580 0.3647
Solution at t = 0.81
1 2 3 4 5 6
0.1000 0.1388 0.1756 0.2086 0.2362 0.2569
7 8
0.2698 0.2742
Solution at t = 1.00
1 2 3 4 5 6
0.1000 0.1243 0.1474 0.1680 0.1853 0.1983
7 8
0.2064 0.2091
Example 2¶
Here, Problem C is solved from Sincovec and Madsen (1975). The equation is of diffusion-convection type with discontinuous coefficients. This problem illustrates a simple method for programming the evaluation routine for the derivative, \(u_t\). Note that the weak discontinuities at \(x = 0.5\) are not evaluated in the expression for \(u_t\). The problem is defined as
from numpy import *
from pyimsl.math.machine import machine
from pyimsl.math.modifiedMethodOfLines import modifiedMethodOfLines
from pyimsl.math.modifiedMethodOfLinesMgr import modifiedMethodOfLinesMgr, PDE_INITIALIZE, PDE_RESET
from pyimsl.math.writeMatrix import writeMatrix
def fcnut(npdes, x, t, u, ux, uxx, ut):
# Define the PDE
if (x <= 0.5):
d = 5.0
v = 1000.0
else:
d = v = 1.0
ut[0] = d * uxx[0] - v * ux[0]
def fcnbc(npdes, x, t, alpha, beta, gam):
alpha[0] = 1.0
beta[0] = 0.0
gam[0] = 0.0
if (x == 0):
gam[0] = 1.0
npdes = 1
nx = 100
j = 1
nstep = 10
t = [0.0]
tend = 0.0
xbreak = zeros((100), dtype='double')
y = zeros((1, 100), dtype='double')
state = []
# Set breakpoints and initial conditions
for i in range(0, nx):
xbreak[i] = double(i) / double(nx - 1)
y[0, i] = 0.0
y[0, 0] = 1.0
# Initialize the solver
tol = sqrt(machine(4))
hinit = 0.01 * tol
modifiedMethodOfLinesMgr(PDE_INITIALIZE, state,
tol=tol, hinit=hinit)
for j in range(1, nstep + 1):
tend = float(j) / float(nstep)
# Solve the problem
modifiedMethodOfLines(t, tend, xbreak, y, state, fcnut, fcnbc)
# Print results at t=tend
title = "Solution at t = %4.2f" % (tend)
writeMatrix(title, y)
modifiedMethodOfLinesMgr(PDE_RESET, state)
Output¶
Solution at t = 1.00
1 2 3 4 5 6
1.000 1.000 1.000 1.000 1.000 1.000
7 8 9 10 11 12
1.000 1.000 1.000 1.000 1.000 1.000
13 14 15 16 17 18
1.000 1.000 1.000 1.000 1.000 1.000
19 20 21 22 23 24
1.000 1.000 1.000 1.000 1.000 1.000
25 26 27 28 29 30
1.000 1.000 1.000 1.000 1.000 1.000
31 32 33 34 35 36
1.000 1.000 1.000 1.000 1.000 1.000
37 38 39 40 41 42
1.000 1.000 1.000 1.000 1.000 1.000
43 44 45 46 47 48
1.000 1.000 1.000 1.000 1.000 1.000
49 50 51 52 53 54
1.000 0.997 0.984 0.969 0.953 0.937
55 56 57 58 59 60
0.921 0.905 0.888 0.872 0.855 0.838
61 62 63 64 65 66
0.821 0.804 0.786 0.769 0.751 0.733
67 68 69 70 71 72
0.715 0.696 0.678 0.659 0.640 0.621
73 74 75 76 77 78
0.602 0.582 0.563 0.543 0.523 0.502
79 80 81 82 83 84
0.482 0.461 0.440 0.419 0.398 0.376
85 86 87 88 89 90
0.354 0.332 0.310 0.288 0.265 0.242
91 92 93 94 95 96
0.219 0.196 0.172 0.148 0.124 0.100
97 98 99 100
0.075 0.050 0.025 0.000
Example 3¶
In this example, using modifiedMethodOfLines
, the linear normalized
diffusion PDE \(u_t = u_{xx}\) is solved but with an optional use that
provides values of the derivatives, \(u_x\), of the initial data. Due to
errors in the numerical derivatives computed by spline interpolation, more
precise derivative values are required when the initial data is \(u(x,
0) = 1 + \cos[(2n - 1)\pi x]\), \(n > 1\). The boundary conditions are
“zero flux” conditions \(u_x(0, t) = u_x(1, t) = 0\) for \(t > 0\).
This optional usage signals that the derivative of the initial data is passed by the user. The values u(x, tend) and \(u_x(x, tend)\) are output at the breakpoints with the optional usage.
from numpy import *
from pyimsl.math.constant import constant
from pyimsl.math.machine import machine
from pyimsl.math.modifiedMethodOfLines import modifiedMethodOfLines
from pyimsl.math.modifiedMethodOfLinesMgr import modifiedMethodOfLinesMgr, PDE_INITIALIZE, PDE_RESET
from pyimsl.math.writeMatrix import writeMatrix
def fcnut(npdes, x, t, u, ux, uxx, ut):
# Define the PDE
ut[0] = uxx[0]
def fcnbc(npdes, x, t, alpha, beta, gamp):
alpha[0] = 0.0
beta[0] = 1.0
gamp[0] = 0.0
npdes = 1
nx = 10
j = 1
nstep = 10
t = [0.0]
tend = 0.0
xbreak = zeros((10), dtype='double')
y = zeros((1, 10), dtype='double')
deriv = zeros((1, 10), dtype=double)
state = []
pi = constant("pi")
arg = 9.0 * pi
# Set breakpoints and initial conditions
for i in range(0, nx):
xbreak[i] = double(i) / double(nx - 1)
y[0, i] = 1.0 + cos(arg * xbreak[i])
deriv[0, i] = -arg * sin(arg * xbreak[i])
# Initialize the solver
tol = sqrt(machine(4))
modifiedMethodOfLinesMgr(PDE_INITIALIZE, state,
tol=tol, initialValueDerivative=deriv)
while (j <= nstep):
tend += 0.001
j += 1
# Solve the problem
modifiedMethodOfLines(t, tend, xbreak, y, state, fcnut, fcnbc)
# Print results at every other t=tend
if ((j % 2) != 0):
title = "Solution at t = %5.3f" % (t[0])
writeMatrix(title, y)
title2 = "Derivative at t = %5.3f" % (t[0])
writeMatrix(title2, deriv)
modifiedMethodOfLinesMgr(PDE_RESET, state)
Output¶
Solution at t = 0.002
1 2 3 4 5 6
1.233 0.767 1.233 0.767 1.233 0.767
7 8 9 10
1.233 0.767 1.233 0.767
Derivative at t = 0.002
1 2 3 4 5 6
0.000e+00 -1.705e-15 -2.377e-16 4.684e-16 5.890e-15 4.064e-16
7 8 9 10
1.015e-14 1.023e-15 1.005e-14 -2.169e-19
Solution at t = 0.004
1 2 3 4 5 6
1.054 0.946 1.054 0.946 1.054 0.946
7 8 9 10
1.054 0.946 1.054 0.946
Derivative at t = 0.004
1 2 3 4 5 6
0.000e+00 -3.352e-15 1.974e-15 3.711e-15 3.221e-16 -3.690e-15
7 8 9 10
1.540e-15 5.639e-16 1.076e-15 -4.402e-19
Solution at t = 0.006
1 2 3 4 5 6
1.013 0.987 1.013 0.987 1.013 0.987
7 8 9 10
1.013 0.987 1.013 0.987
Derivative at t = 0.006
1 2 3 4 5 6
0.000e+00 -2.321e-15 7.869e-17 3.428e-15 8.703e-16 -3.277e-15
7 8 9 10
-3.850e-16 1.110e-15 -1.781e-13 -2.005e-12
Solution at t = 0.008
1 2 3 4 5 6
1.003 0.997 1.003 0.997 1.003 0.997
7 8 9 10
1.003 0.997 1.003 0.997
Derivative at t = 0.008
1 2 3 4 5 6
0.000e+00 -2.713e-16 -2.626e-16 3.271e-15 2.424e-16 -3.951e-15
7 8 9 10
2.005e-15 1.029e-14 1.147e-13 1.706e-12
Solution at t = 0.010
1 2 3 4 5 6
1.001 0.999 1.001 0.999 1.001 0.999
7 8 9 10
1.001 0.999 1.001 0.999
Derivative at t = 0.010
1 2 3 4 5 6
0.000e+00 5.053e-16 7.038e-16 2.135e-15 -6.885e-16 -1.424e-15
7 8 9 10
-2.229e-16 -1.293e-14 -5.296e-14 -4.409e-13
Example 4¶
In this example, consider the linear normalized hyperbolic PDE, \(u_{tt} = u_{xx}\), the “vibrating string” equation. This naturally leads to a system of first order PDEs. Define a new dependent variable \(u_t = v\). Then, \(v_t = u_{xx}\) is the second equation in the system. Take as initial data \(u(x, 0) = \sin(\pi x)\) and
\(u_t(x, 0) = v(x, 0) = 0\). The ends of the string are fixed so \(u(0, t) = u(1, t) = v(0, t) = v(1, t) = 0\). The exact solution to this problem is \(u(x, t) = \sin(\pi x) \cos(\pi t)\). Residuals are computed at the output values of t for \(0 < t \leq 2\). Output is obtained at 200 steps in increments of 0.01.
Even though the sample code modifiedMethodOfLines
gives satisfactory
results for this PDE, users should be aware that for nonlinear problems,
“shocks” can develop in the solution. The appearance of shocks may cause the
code to fail in unpredictable ways. See Courant and Hilbert (1962), pp
488-490, for an introductory discussion of shocks in hyperbolic systems.
from __future__ import print_function
from numpy import *
from pyimsl.math.constant import constant
from pyimsl.math.machine import machine
from pyimsl.math.modifiedMethodOfLines import modifiedMethodOfLines
from pyimsl.math.modifiedMethodOfLinesMgr import modifiedMethodOfLinesMgr, PDE_INITIALIZE, PDE_RESET
def fcnut(npdes, x, t, u, ux, uxx, ut):
# Define the PDE
ut[0] = u[1]
ut[1] = uxx[0]
def fcnbc(npdes, x, t, alpha, beta, gamp):
# Define the boundary conditions
alpha[0] = 1.0
beta[0] = 0.0
gamp[0] = 0.0
alpha[1] = 1.0
beta[1] = 0.0
gamp[1] = 0.0
npdes = 2
nx = 10
j = 1
nstep = 200
t = [0.0]
tend = 0.0
xbreak = zeros((nx), dtype='double')
y = zeros((2, 10), dtype='double')
deriv = zeros((2, 10), dtype='double')
error = zeros((10), dtype=double)
state = []
pi = constant("pi")
erru = 0.0
# Set breakpoints and initial conditions
for i in range(0, nx):
xbreak[i] = double(i) / double(nx - 1)
y[0, i] = sin(pi * xbreak[i])
y[1, i] = 0.0
deriv[0, i] = pi * cos(pi * xbreak[i])
deriv[1, i] = 0.0
# Initialize the solver
tol = sqrt(machine(4))
modifiedMethodOfLinesMgr(PDE_INITIALIZE, state,
tol=tol, initialValueDerivative=deriv)
while (j <= nstep):
j += 1
tend += 0.01
# Solve the problem
modifiedMethodOfLines(t, tend, xbreak, y, state, fcnut, fcnbc)
# Look at output at steps of 0.01 and compute errors
for i in range(0, nx):
error[i] = y[0, i] - sin(pi * xbreak[i]) * cos(pi * tend)
erru = max(erru, abs(error[i]))
modifiedMethodOfLinesMgr(PDE_RESET, state)
print("Maximum error in u(x,y) = %e" % (erru))
Output¶
Maximum error in u(x,y) = 1.622147e-05
Fatal Errors¶
IMSL_REPEATED_ERR_TEST_FAILURE |
After some initial success, the integration was halted by repeated error test failures. |
IMSL_INTEGRATION_HALTED_1 |
Integration halted after failing
to pass the error test, even
after reducing the stepsize by a
factor of 1.0E+10 to H = #.
TOL = # may be too small. |
IMSL_INTEGRATION_HALTED_2 |
Integration halted after failing
to achieve corrector
convergence, even after reducing
the stepsize by a factor of
1.0E+10 to H = #. TOL = #
may be too small. |
IMSL_INTEGRATION_HALTED_3 |
After some initial success, the
integration was halted by a test
on TOL = #. |
IMSL_TOL_TOO_SMALL_OR_STIFF |
On the next step X+H will equal
X, with X = # and H = #. Either
TOL = # is too small or the
problem is stiff. |
IMSL_STOP_USER_FCN |
Request from user supplied function to stop algorithm. User flag = “#”. |