In the previous numerical solution posts, I described linear equations like diffusion equation and the Schrödinger equation, and how they can be solved by (implicit or explicit) finite differencing. The idea of the implicit methods was to convert the equation into a linear system of equations, from which the function values on a discrete mesh could be calculated.

Saying that these equations were linear means that they can be written as

where the linear differential operator, containing space and time derivatives, is acting on the function and producing “something” (usually zero but in the case of source terms/inhomogeneity something nonzero).

As a first example of a nonlinear PDE, let’s consider the complex Ginzburg-Landau equation (CGLE), which reads:

Here the and are real parameters and is the imaginary unit. Applying an implicit differencing on this may seem to result in a system of equations

but this is not a linear system because of the , so we cannot solve the problem in this way by using linear algebra.

The trick to solve this is to linearize the system, by evaluating the at timestep and the rest of the quantities at timestep , producing the system

which is now a linear system w.r.t. to the variables evaluated at timestep (the matrix for solving ““:s has diagonal elements that depend of ““:s). A more sophisticated method would do several iterations to approximate the values of between the timesteps and .

An R code that solves the equation for a domain , , using discrete steps , , initial state and values and , is shown here.

`library(graphics) #load the graphics library needed for plotting`

lx <- 100 #length of the computational domain

lt <- 150 #length of the simulation time interval

nx <- 150 #number of discrete lattice points

nt <- 450 #number of timesteps

dx <- lx/nx #length of one discrete lattice cell

dt <- lt/nt #length of timestepa <- 3

b <- -2kappa1 = dt*(1+1i*a)/dx/dx #an element needed for the matrices

kappa2 = dt*(1+1i*b)psi = as.complex(c(1:nx)) #array for the function A values

sol = as.complex(c(1:nx))for(j in c(1:nx)) {

psi[j] = 0.1*exp(2i*j*dx)

sol[j] = psi[j]

}xaxis <- c(1:nx)*dx #the x values corresponding to the discrete lattice points

IPxaxis <- c(1:(4*nx))*dx/4

IPtaxis <- c(1:(4*nt))*dt/4sol_plot = matrix(nrow=nt,ncol=nx)

A = matrix(nrow=nx,ncol=nx) #matrix for forward time evolution

IP = matrix(nrow = 4*nt, ncol=4*nx)for (m in c(1:nt)) { #main time stepping loop

for(j in c(1:nx)) {

for(k in c(1:nx)) {

A[j,k]=0

if(j==k) {

A[j,k] = 1 + 2*kappa1 + kappa2*abs(sol[j])*abs(sol[j]) – dt #diagonal elements

}

if((j==k+1) || (j==k-1)) {

A[j,k] = -kappa1 #off-diagonal elements

}

}

}for(l in c(1:nx)) {

psi[l] = sol[l]

}

sol <- solve(A,psi) #solve the system of equationsfor (l in c(1:nx)) {

sol_plot[m,l] <- Re(sol[l])

}jpeg(file = paste(“plot_”,m,”.jpg”,sep=””))

plot(xaxis,Im(sol),xlab = “position (x)”,ylab=”Im[A(x,t)]”,ylim=c(-4,4),pch=’.’)

title(paste(“Im[A(x,t)] at t = “,round(m*dt,digits=2)))

lines(xaxis,Im(sol))

dev.off()}

for(l in c(1:(nt-1))) {

for(m in c(1:(nx-1))) { #make a bitmap with 4 times more pixels, using linear interpolation

IP[4*l-3,4*m-3] = sol_plot[l,m]

IP[4*l-2,4*m-3] = sol_plot[l,m]+0.25*(sol_plot[l+1,m]-sol_plot[l,m])

IP[4*l-1,4*m-3] = sol_plot[l,m]+0.5*(sol_plot[l+1,m]-sol_plot[l,m])

IP[4*l,4*m-3] = sol_plot[l,m]+0.75*(sol_plot[l+1,m]-sol_plot[l,m])

}

}for(l in c(1:(4*nt))) {

for(m in c(1:(nx-1))) {

IP[l,4*m-2] = IP[l,4*m-3]+0.25*(IP[l,4*m+1]-IP[l,4*m-3])

IP[l,4*m-1] = IP[l,4*m-3]+0.5*(IP[l,4*m+1]-IP[l,4*m-3])

IP[l,4*m] = IP[l,4*m-3]+0.75*(IP[l,4*m+1]-IP[l,4*m-3])

}

}jpeg(file = “2dplot.jpg”)

image(IPtaxis,IPxaxis,IP,xlab = “t-axis”,ylab=”x-axis”,zlim=c(-2,2))

dev.off()

Plotting the real part of the resulting function at several values of , we see that the solution initially doesn’t do much anything, but at some point a “phase turbulence” sets in starting from the ends of the x-domain and after that the function evolves in a very random way, without following any clear pattern (unlike the spreading mass/temperature distributions, traveling waves or scattering wavepackets in the case of the common linear PDE:s).

An animation of the solution is shown below.

This kind of chaos is typical of nonlinear systems, be them point mass systems with nonlinear forces between mass points or field systems with nonlinear field equations such as the CGLE here. Note that the solution of this equation is a bit too heavy of a calculation to do just for the purpose of creating random numbers, so for that end other methods such as Perlin’s noise are used.

The 2D color plot of the real part of the solution, plotted in the xt-plane, looks like this:

More plots of the solutions for different values of parameters can be found in this article.

It should be noted that, Wolfram Mathematica’s “NDSolve” function can’t usually solve nonlinear PDE:s correctly, despite usually working properly in the case of linear PDE:s. Some other commercial math programs such as Comsol Multiphysics may work better when solving nonlinear problems, at least to my experience.

So, here was the basic idea of how nonlinear PDE:s are solved by linearization, and what kind of things are possible in the behavior of their solutions. In the next PDE post I will show how to solve the thin-film equation, about which I actually wrote my master’s thesis in 2013, and which doesn’t usually behave chaotically unlike the CGLE (but can be made to do so by adding suitable terms).