Main Content

Solve ODE with Strongly State-Dependent Mass Matrix

This example shows how to solve Burgers' equation using a moving mesh technique [1]. The problem includes a mass matrix, and options are specified to account for the strong state dependence and sparsity of the mass matrix, making the solution process more efficient.

Problem Setup

Burgers' equation is a convection-diffusion equation given by the PDE

ut=ϵ2ux2-x(u22),0<x<1,t>0,ϵ=1×10-4.

Applying a coordinate transformation (Eq. 18 in [1]) leads to an extra term on the left-hand side:

ut-uxxt=ϵ2ux2-x(u22).

Converting the PDE into an ODE of one variable is accomplished by using finite differences to approximate the partial derivatives taken with respect to x. If the finite differences are written as Δ, then the PDE can be rewritten as an ODE that only contains derivatives taken with respect to t:

dudt-Δudxdt=ϵΔ2u-Δ(u22).

In this form, you can use an ODE solver such as ode15s to solve for u and x over time.

For this example, the problem is formulated on a moving mesh of N points, and the moving mesh technique described in [1] positions the mesh points at each time step so that they are concentrated in areas of change. The boundary and initial conditions are

u(0,t)=u(1,t)=0,u(x,0)=sin(2πx)+12sin(πx).

For a given initial mesh of N points, there are 2N equations to solve: N equations corresponding to Burgers' equation, and N equations determining the movement of each mesh point. So, the final system of equations is:

du1dt-Δu1dx1dt=ϵΔ2u1-Δ(u122),duNdt-ΔuNdxNdt=ϵΔ2uN-Δ(uN22),d2x1˙dt2=1τddt(B(x1,t)dx1dt),d2xN˙dt2=1τddt(B(xN,t)dxNdt).

The terms for the moving mesh correspond to MMPDE6 in [1]. The parameter τ represents a timescale for forcing the mesh toward equidistribution. The term B(x,t) is a monitor function given by Eq. 21 in [1]:

B(x,t)=1+(duidxi)2.

The approach used in this example to solve Burgers' equation with moving mesh points demonstrates several techniques:

  • The system of equations is expressed using a mass matrix formulation, My=f(t,y). The mass matrix is provided to the ode15s solver as a function.

  • The derivative function not only includes the equations for Burgers' equation, but also a set of equations governing the moving mesh selection.

  • The sparsity patterns of the Jacobian dF/dy and the derivative of the mass matrix multiplied with a vector d(Mv)/dy are supplied to the solver as functions. Supplying these sparsity patterns helps the solver operate more efficiently.

  • Finite differences are used to approximate several partial derivatives.

To solve this equation in MATLAB®, write a derivative function, a mass matrix function, a function for the sparsity pattern of the Jacobian dF/dy, and a function for the sparsity pattern of d(Mv)/dy. You can either include the required functions as local functions at the end of a file (as done here), or save them as separate, named files in a directory on the MATLAB path.

Code Mass Matrix

The left side of the system of equations involves linear combinations of first derivatives, so a mass matrix is required to represent all of the terms. Set the left side of the system of equations equal to My to extract the form of the mass matrix. The mass matrix is composed of four blocks, each of which is a square matrix of order N:

[u1t-u1x1x1tuNt-uNxNxNt2x1˙t22xN˙t2]=My=[M1M2M3M4][u1˙uN˙x1˙xN˙].

This formulation shows that M1 and M2 form the left side of Burgers' equations (the first N equations in the system), while M3 and M4 form the left side of the mesh equations (the last N equations in the system). The block matrices are:

M1=IN,M2=-uixiIN,M3=0N,M4=2t2IN.

IN is the N×N identity matrix. The partial derivatives in M2 are estimated using finite differences, while the partial derivative in M4 uses a Laplacian matrix. Notice that M3 contains only zeros because none of the equations for the mesh movement depend on u˙.

Now you can write a function that computes the mass matrix. The function must accept two inputs for time t and the solution vector y. Since the solution vector y contains half u˙ components and half x˙ components, the function extracts these first. Then, the function forms all of the block matrices (taking the boundary values of the problem into account) and assembles the mass matrix using the four blocks.

function M = mass(t,y)
% Extract the components of y for the solution u and mesh x
N = length(y)/2; 
u = y(1:N);
x = y(N+1:end);

% Boundary values of solution u and mesh x
u0 = 0;
uNP1 = 0;
x0 = 0;
xNP1 = 1;

% M1 and M2 are the portions of the mass matrix for Burgers' equation. 
% The derivative du/dx is approximated with finite differences, using 
% single-sided differences on the edges and centered differences in between.
M1 = speye(N);
M2 = sparse(N,N);
M2(1,1) = - (u(2) - u0)/(x(2) - x0);
for i = 2:N-1
    M2(i,i) = - (u(i+1) - u(i-1))/(x(i+1) - x(i-1));
end
M2(N,N) = - (uNP1 - u(N-1))/(xNP1 - x(N-1));

% M3 and M4 define the equations for mesh point evolution, corresponding to 
% MMPDE6 in the reference paper. Since the mesh functions only involve d/dt(dx/dt), 
% the M3 portion of the mass matrix is all zeros. The second derivative in M4 is 
% approximated using a finite difference Laplacian matrix. 
M3 = sparse(N,N);
e = ones(N,1);
M4 = spdiags([e -2*e e],-1:1,N,N);

% Assemble mass matrix
M = [M1 M2
     M3 M4];
end

Note: All functions are included as local functions at the end of the example.

Code Derivative Function

The derivative function for this problem returns a vector with 2N elements. The first N elements correspond to Burgers' equations, while the last N elements are for the moving mesh equations. The function movingMeshODE goes through these steps to evaluate the right-hand sides of all the equations in the system:

  1. Evaluate Burgers' equations using finite differences (first N elements).

  2. Evaluate monitor function (last N elements).

  3. Apply spatial smoothing to monitor function and evaluate moving mesh equations.

The first N equations in the derivative function encode the right side of Burgers' equations. Burgers' equations can be considered as a differential operator involving spatial derivatives of the form:

f(u)=ϵ2ux2-x(u22).

The reference paper [1] describes the process of approximating the differential operator f using centered finite differences by

fi=ϵ[(ui+1-uixi+1-xi)-(ui-ui-1xi-xi-1)12(xi+1-xi-1)]-12(ui+12-ui-12xi+1-xi-1).

On the edges of the mesh (for which i=1 and i=N), only single-sided differences are used instead. This example uses ϵ=1×10-4.

The equations governing the mesh (comprising the last N equations in the derivative function) are

2x˙t2=1τt(B(x,t)xt).

Just as with Burgers' equations, you can use finite differences to approximate the monitor function B(x,t):

B(x,t)=1+(uixi)2=1+(ui+1-ui-1xi+1-xi-1)2.

Once the monitor function is evaluated, spatial smoothing is applied (Equations 14 and 15 in [1]). This example uses γ=2 and p=2 for the spatial smoothing parameters.

The function encoding the system of equations is

function g = movingMeshODE(t,y)
% Extract the components of y for the solution u and mesh x
N = length(y)/2;
u = y(1:N);
x = y(N+1:end);

% Boundary values of solution u and mesh x
u0 = 0;
uNP1 = 0;
x0 = 0;
xNP1 = 1;

% Preallocate g vector of derivative values.
g = zeros(2*N,1);

% Use centered finite differences to approximate the RHS of Burgers'
% equations (with single-sided differences on the edges). The first N
% elements in g correspond to Burgers' equations.
for i = 2:N-1
    delx = x(i+1) - x(i-1);
    g(i) = 1e-4*((u(i+1) - u(i))/(x(i+1) - x(i)) - ...
        (u(i) - u(i-1))/(x(i) - x(i-1)))/(0.5*delx) ...
        - 0.5*(u(i+1)^2 - u(i-1)^2)/delx;
end
delx = x(2) - x0;
g(1) = 1e-4*((u(2) - u(1))/(x(2) - x(1)) - (u(1) - u0)/(x(1) - x0))/(0.5*delx) ...
    - 0.5*(u(2)^2 - u0^2)/delx;
delx = xNP1 - x(N-1);
g(N) = 1e-4*((uNP1 - u(N))/(xNP1 - x(N)) - ...
    (u(N) - u(N-1))/(x(N) - x(N-1)))/delx - ...
    0.5*(uNP1^2 - u(N-1)^2)/delx;

% Evaluate the monitor function values (Eq. 21 in reference paper), used in
% RHS of mesh equations. Centered finite differences are used for interior
% points, and single-sided differences are used on the edges.
M = zeros(N,1);
for i = 2:N-1
    M(i) = sqrt(1 + ((u(i+1) - u(i-1))/(x(i+1) - x(i-1)))^2);
end
M0 = sqrt(1 + ((u(1) - u0)/(x(1) - x0))^2);
M(1) = sqrt(1 + ((u(2) - u0)/(x(2) - x0))^2);
M(N) = sqrt(1 + ((uNP1 - u(N-1))/(xNP1 - x(N-1)))^2);
MNP1 = sqrt(1 + ((uNP1 - u(N))/(xNP1 - x(N)))^2);

% Apply spatial smoothing (Eqns. 14 and 15) with gamma = 2, p = 2.
SM = zeros(N,1);
for i = 3:N-2
    SM(i) = sqrt((4*M(i-2)^2 + 6*M(i-1)^2 + 9*M(i)^2 + ...
        6*M(i+1)^2 + 4*M(i+2)^2)/29);
end
SM0 = sqrt((9*M0^2 + 6*M(1)^2 + 4*M(2)^2)/19);
SM(1) = sqrt((6*M0^2 + 9*M(1)^2 + 6*M(2)^2 + 4*M(3)^2)/25);
SM(2) = sqrt((4*M0^2 + 6*M(1)^2 + 9*M(2)^2 + 6*M(3)^2 + 4*M(4)^2)/29);
SM(N-1) = sqrt((4*M(N-3)^2 + 6*M(N-2)^2 + 9*M(N-1)^2 + 6*M(N)^2 + 4*MNP1^2)/29);
SM(N) = sqrt((4*M(N-2)^2 + 6*M(N-1)^2 + 9*M(N)^2 + 6*MNP1^2)/25);
SMNP1 = sqrt((4*M(N-1)^2 + 6*M(N)^2 + 9*MNP1^2)/19);
for i = 2:N-1
    g(i+N) = (SM(i+1) + SM(i))*(x(i+1) - x(i)) - ...
        (SM(i) + SM(i-1))*(x(i) - x(i-1));
end
g(1+N) = (SM(2) + SM(1))*(x(2) - x(1)) - (SM(1) + SM0)*(x(1) - x0);
g(N+N) = (SMNP1 + SM(N))*(xNP1 - x(N)) - (SM(N) + SM(N-1))*(x(N) - x(N-1));

% Form final discrete approximation for Eq. 12 in reference paper, the equation governing
% the mesh points.
tau = 1e-3;
g(1+N:end) = - g(1+N:end)/(2*tau);
end

Code Functions for Sparsity Patterns

The Jacobian dF/dy for the derivative function is a 2N×2N matrix containing all of the partial derivatives of the derivative function, movingMeshODE. ode15s estimates the Jacobian using finite differences when the matrix is not supplied in the options structure. You can supply the sparsity pattern of the Jacobian to help ode15s calculate it more quickly.

The function for the sparsity pattern of the Jacobian is

function out = JPat(N)
S1 = spdiags(ones(N,3),-1:1,N,N);
S2 = spdiags(ones(N,9),-4:4,N,N);
out = [S1 S1
       S2 S2];
end

Plot the sparsity pattern of dF/dy for N=80 using spy.

spy(JPat(80))

Figure contains an axes object. The axes object with xlabel nz = 1876 contains a line object which displays its values using only markers.

Another way to make the calculation more efficient is to provide the sparsity pattern of d(Mv)/dy. You can find this sparsity pattern by examining which terms of ui and xi are present in the finite differences calculated in the mass matrix function.

The function for the sparsity pattern of d(Mv)/dy is

function S = MvPat(N)
S = sparse(2*N,2*N);
S(1,2) = 1;
S(1,2+N) = 1;
for i = 2:N-1
    S(i,i-1) = 1;
    S(i,i+1) = 1;
    S(i,i-1+N) = 1;
    S(i,i+1+N) = 1;
end
S(N,N-1) = 1;
S(N,N-1+N) = 1;
end

Plot the sparsity pattern of d(Mv)/dy for N=80 using spy.

spy(MvPat(80))

Figure contains an axes object. The axes object with xlabel nz = 316 contains a line object which displays its values using only markers.

Solve System of Equations

Solve the system with the value N=80. For the initial conditions, initialize x with a uniform grid and evaluate u(x,0) on the grid.

N = 80;
h = 1/(N+1);
xinit = h*(1:N);
uinit = sin(2*pi*xinit) + 0.5*sin(pi*xinit);
y0 = [uinit xinit];

Use odeset to create an options structure that sets several values:

  • A function handle for the mass matrix

  • The state-dependence of the mass matrix, which for this problem is 'strong' since the mass matrix is a function of both t and y

  • A function handle that calculates the Jacobian sparsity pattern

  • A function handle that calculates the sparsity pattern of the derivative of the mass matrix multiplied by a vector

  • The absolute and relative error tolerances

opts = odeset('Mass',@mass,'MStateDependence','strong','JPattern',JPat(N),...
   'MvPattern',MvPat(N),'RelTol',1e-5,'AbsTol',1e-4);

Finally, call ode15s to solve the system on the interval [0,1] using the movingMeshODE derivative function, the time span, the initial conditions, and the options structure.

tspan = [0 1];
sol = ode15s(@movingMeshODE,tspan,y0,opts);

Plot Results

The result of the integration is a structure sol that contains the time steps t, the mesh points x(t), and the solution u(x,t). Extract these values from the structure.

t = sol.x;
x = sol.y(N+1:end,:);
u = sol.y(1:N,:);

Plot the movement of the mesh points over time. The plot shows that the mesh points retain a reasonably even spacing over time (due to the monitor function), but they are able to cluster near the discontinuity in the solution as it moves.

plot(x,t)
xlabel('t')
ylabel('x(t)')
title('Burgers'' equation: Trajectories of grid points')

Figure contains an axes object. The axes object with title Burgers' equation: Trajectories of grid points, xlabel t, ylabel x(t) contains 80 objects of type line.

Now, sample u(x,t) at a few values of t and plot the evolution of the solution over time. The mesh points at the ends of the interval are fixed, so x(0) = 0 and x(N+1) = 1. The boundary values are u(t,0) = 0 and u(t,1) = 0, which you must add to the known values computed for the figure.

tint = 0:0.2:1;
yint = deval(sol,tint);
figure
labels = {};
for j = 1:length(tint)
   solution = [0; yint(1:N,j); 0];
   location = [0; yint(N+1:end,j); 1];
   labels{j} = ['t = ' num2str(tint(j))];
   plot(location,solution,'-o')
   hold on
end
xlabel('x')
ylabel('solution u(x,t)')
legend(labels{:},'Location','SouthWest')
title('Burgers equation on moving mesh')
hold off

Figure contains an axes object. The axes object with title Burgers equation on moving mesh, xlabel x, ylabel solution u(x,t) contains 6 objects of type line. These objects represent t = 0, t = 0.2, t = 0.4, t = 0.6, t = 0.8, t = 1.

The plot shows that u(x,0) is a smooth wave that develops a steep gradient over time as it moves towards x=1. The mesh points track the movement of the discontinuity so that extra evaluation points are in the appropriate position in each time step.

References

[1] Huang, Weizhang, et al. “Moving Mesh Methods Based on Moving Mesh Partial Differential Equations.” Journal of Computational Physics, vol. 113, no. 2, Aug. 1994, pp. 279–90. https://doi.org/10.1006/jcph.1994.1135.

Local Functions

Listed here are the local helper functions that the solver ode15s calls to calculate the solution. Alternatively, you can save these functions as their own files in a directory on the MATLAB path.

function g = movingMeshODE(t,y)
% Extract the components of y for the solution u and mesh x
N = length(y)/2;
u = y(1:N);
x = y(N+1:end);

% Boundary values of solution u and mesh x
u0 = 0;
uNP1 = 0;
x0 = 0;
xNP1 = 1;

% Preallocate g vector of derivative values.
g = zeros(2*N,1);

% Use centered finite differences to approximate the RHS of Burgers'
% equations (with single-sided differences on the edges). The first N
% elements in g correspond to Burgers' equations.
for i = 2:N-1
    delx = x(i+1) - x(i-1);
    g(i) = 1e-4*((u(i+1) - u(i))/(x(i+1) - x(i)) - ...
        (u(i) - u(i-1))/(x(i) - x(i-1)))/(0.5*delx) ...
        - 0.5*(u(i+1)^2 - u(i-1)^2)/delx;
end
delx = x(2) - x0;
g(1) = 1e-4*((u(2) - u(1))/(x(2) - x(1)) - (u(1) - u0)/(x(1) - x0))/(0.5*delx) ...
    - 0.5*(u(2)^2 - u0^2)/delx;
delx = xNP1 - x(N-1);
g(N) = 1e-4*((uNP1 - u(N))/(xNP1 - x(N)) - ...
    (u(N) - u(N-1))/(x(N) - x(N-1)))/delx - ...
    0.5*(uNP1^2 - u(N-1)^2)/delx;

% Evaluate the monitor function values (Eq. 21 in reference paper), used in
% RHS of mesh equations. Centered finite differences are used for interior
% points, and single-sided differences are used on the edges.
M = zeros(N,1);
for i = 2:N-1
    M(i) = sqrt(1 + ((u(i+1) - u(i-1))/(x(i+1) - x(i-1)))^2);
end
M0 = sqrt(1 + ((u(1) - u0)/(x(1) - x0))^2);
M(1) = sqrt(1 + ((u(2) - u0)/(x(2) - x0))^2);
M(N) = sqrt(1 + ((uNP1 - u(N-1))/(xNP1 - x(N-1)))^2);
MNP1 = sqrt(1 + ((uNP1 - u(N))/(xNP1 - x(N)))^2);

% Apply spatial smoothing (Eqns. 14 and 15) with gamma = 2, p = 2.
SM = zeros(N,1);
for i = 3:N-2
    SM(i) = sqrt((4*M(i-2)^2 + 6*M(i-1)^2 + 9*M(i)^2 + ...
        6*M(i+1)^2 + 4*M(i+2)^2)/29);
end
SM0 = sqrt((9*M0^2 + 6*M(1)^2 + 4*M(2)^2)/19);
SM(1) = sqrt((6*M0^2 + 9*M(1)^2 + 6*M(2)^2 + 4*M(3)^2)/25);
SM(2) = sqrt((4*M0^2 + 6*M(1)^2 + 9*M(2)^2 + 6*M(3)^2 + 4*M(4)^2)/29);
SM(N-1) = sqrt((4*M(N-3)^2 + 6*M(N-2)^2 + 9*M(N-1)^2 + 6*M(N)^2 + 4*MNP1^2)/29);
SM(N) = sqrt((4*M(N-2)^2 + 6*M(N-1)^2 + 9*M(N)^2 + 6*MNP1^2)/25);
SMNP1 = sqrt((4*M(N-1)^2 + 6*M(N)^2 + 9*MNP1^2)/19);
for i = 2:N-1
    g(i+N) = (SM(i+1) + SM(i))*(x(i+1) - x(i)) - ...
        (SM(i) + SM(i-1))*(x(i) - x(i-1));
end
g(1+N) = (SM(2) + SM(1))*(x(2) - x(1)) - (SM(1) + SM0)*(x(1) - x0);
g(N+N) = (SMNP1 + SM(N))*(xNP1 - x(N)) - (SM(N) + SM(N-1))*(x(N) - x(N-1));

% Form final discrete approximation for Eq. 12 in reference paper, the equation governing
% the mesh points.
tau = 1e-3;
g(1+N:end) = - g(1+N:end)/(2*tau);
end

% -----------------------------------------------------------------------
function M = mass(t,y)
% Extract the components of y for the solution u and mesh x
N = length(y)/2; 
u = y(1:N);
x = y(N+1:end);

% Boundary values of solution u and mesh x
u0 = 0;
uNP1 = 0;
x0 = 0;
xNP1 = 1;

% M1 and M2 are the portions of the mass matrix for Burgers' equation. 
% The derivative du/dx is approximated with finite differences, using 
% single-sided differences on the edges and centered differences in between.
M1 = speye(N);
M2 = sparse(N,N);
M2(1,1) = - (u(2) - u0)/(x(2) - x0);
for i = 2:N-1
    M2(i,i) = - (u(i+1) - u(i-1))/(x(i+1) - x(i-1));
end
M2(N,N) = - (uNP1 - u(N-1))/(xNP1 - x(N-1));

% M3 and M4 define the equations for mesh point evolution, corresponding to 
% MMPDE6 in the reference paper. Since the mesh functions only involve d/dt(dx/dt), 
% the M3 portion of the mass matrix is all zeros. The second derivative in M4 is 
% approximated using a finite difference Laplacian matrix. 
M3 = sparse(N,N);
e = ones(N,1);
M4 = spdiags([e -2*e e],-1:1,N,N);

% Assemble mass matrix
M = [M1 M2
     M3 M4];
end

% -------------------------------------------------------------------------
function out = JPat(N)  % Jacobian sparsity pattern
S1 = spdiags(ones(N,3),-1:1,N,N);
S2 = spdiags(ones(N,9),-4:4,N,N);
out = [S1 S1
    S2 S2];
end

% -------------------------------------------------------------------------
function S = MvPat(N)  % Sparsity pattern for the derivative of the Mass matrix times a vector
S = sparse(2*N,2*N);
S(1,2) = 1;
S(1,2+N) = 1;
for i = 2:N-1
    S(i,i-1) = 1;
    S(i,i+1) = 1;
    S(i,i-1+N) = 1;
    S(i,i+1+N) = 1;
end
S(N,N-1) = 1;
S(N,N-1+N) = 1;
end
% -------------------------------------------------------------------------

See Also

|

Related Topics