Scalar equation with exponential flux

In this notebook we solve the scalar equation $q_t + f(q)_x = 0$ with flux function $f(q) = e^q$ and initial data

$ q(x,0) = \left\{ \begin{array}{ll} 2 & \text{if}~ 0\leq x \leq 1,\\ 0 & \text{otherwise.} \end{array}\right. $


In [1]:
%pylab inline


Populating the interactive namespace from numpy and matplotlib

The solution initially consists of a shock wave at $x=1$ moving at speed $s_0 = (f(2)-f(0))/(2-0) = (e^2-1)/2$ and a centered rarefaction at $x=0$. In the rarefaction wave the solution takes the form $q_{rare}(x,t) = \log(x/t)$. The leading edge of the rarefaction wave is moving at speed $f'(2) = e^2$ and catches up to the shock at time $t_s = 2/(e^2 + 1)$. For $t \leq t_s$, the solution is

$q(x,t) = \left\{ \begin{array}{ll} \log(x/t) & \text{if}~ t \leq x \leq e^2 t,\\ 2 & \text{if}~ e^2 t\leq x \leq 1+s_0t,\\ 0 & \text{otherwise.} \end{array}\right. $

For $t > t_s$, the rarefaction is interacting with the shock and the shock speed varies. If $x_s(t)$ is the shock position, then it satisfies and ODE determined by the fact that the shock speed is given by the Rankine-Hugoniot condition at each instant in time:

$x_s' = \frac{f(q_{rare}(x_s(t),t)) - f(0)}{q_{rare}(x_s(t),t) - 0} = \frac{x_s(t)/t - 1}{\log(x_s(t)/t)}$

with the intial condition $x_s(t_s) = 1 + s_0 t_s$.

This ODE can be solved in terms of the Lambert W-function $W(z)$ defined to be the solution to $We^W = z$:

$x_s(t) = \frac{2-t}{W((2-t)/et)}.$

This can also be determined by conservation since $x_s(t)$ must be the value for which $\int_t^{x_s(t)} \log(x/t) dx = 2$. Evaluating this integral gives $x_s \log(x_s) - x_s - x_s\log(t) + t = 2$ and some manipulations allow this nonlinear equation to be solved in terms of the W-function as above (or it could be solved numerically, e.g. with the Python function scipy.optimize.fsolve).

Assuming a way to evaluate $x_s(t)$, the solution for $t > t_s$ can be written as

$q(x,t) = \left\{ \begin{array}{ll} \log(x/t) & \text{if}~ t \leq x \leq x_s(t),\\ 0 & \text{otherwise.} \end{array}\right. $

Evaluating from this form requires solving a nonlinear equation to determine $x_s(t)$ for each $t > t_s$, or having an implementation of the Lambert W-function. In Python, one can use scipy.special.lambertw. This is implemented in the function below.


In [37]:
def q(x,t):
    from scipy.special import lambertw
    q = zeros(x.shape)
    q = where(x>t, log(x/t), q)  # rarefaction
    ts = 2./(exp(2.)+1)  # time rarefaction catches shock
    if t<ts:
        q = where(x<exp(2)*t, q, 2.) # flat top
        q = where(x < 1+t*(exp(2)-1)/2., q, 0.)  # cut off at shock
    else:
        xs = (2-t) / (lambertw((2-t)/(exp(1.)*t)))
        q = where(x < xs, q, 0.) # cut off at shock
    return q

Plot the solution for several times between 0 and 1:


In [38]:
xx = linspace(-1,5,500)
for t in linspace(0,1,11):
    qq = q(xx,t)
    plot(xx,qq)
ylim(-0.5,2.5)


Out[38]:
(-0.5, 2.5)

Another way to compute the true solution on a set of x points

We can avoid having to use the W-function by simply evaluating the rarefaction wave function everywhere to the right of $x=t$ and then reset the value of $q$ to zero at points where the integral would evaluate to a value greater than 2, e.g. when $x\log(x/t) - x + t > 2$:


In [39]:
def q2(x,t):
    ts = 2./(exp(2.)+1)  # time rarefaction catches shock
    q = zeros(x.shape)
    q = where(x>t, log(x/t), q)  # rarefaction for x>t, ignoring shock
    if t<ts:
        q = where(x<exp(2)*t, q, 2.) # flat top
        q = where(x < 1+t*(exp(2)-1)/2., q, 0.)  # cut off at shock
    else:
        # cut off at shock, ignoring log values at invalid arguments:
        q = where(logical_and(x>0, x*log(x/t) - x + t > 2), 0., q)
    return q

In [40]:
xx = linspace(-1,5,500)
for t in linspace(0,1,11):
    qq = q2(xx,t)
    plot(xx,qq)
ylim(-0.5,2.5)


Out[40]:
(-0.5, 2.5)

This latter approach is used in the setplot.py routine used in the Clawpack code below.

Clawpack solution

Check that the CLAW environment variable is set. (It must be set in the Unix shell before starting the notebook server).


In [41]:
try:
    import clawpack
    location = clawpack.__file__.replace('clawpack/__init__.pyc','')
    print "Using Clawpack from ",location
except:
    print "*** Problem importing Clawpack -- check if environment variable set"


Using Clawpack from  /Users/rjl/git/clawpack/

Import some modules needed below...


In [42]:
import clawpack.visclaw.JSAnimation.JSAnimation_frametools as J
from clawpack.clawutil import nbtools
from IPython.display import FileLink

Compile the Fortran code:


In [4]:
nbtools.make_exe(new=True)


Executing shell command:   make new
Done...  Check this file to see output:

Make documentation files:


In [30]:
nbtools.make_htmls()


See the README.html file for links to input files...

Run code, and plot results...


In [14]:
outdir,plotdir = nbtools.make_output_and_plots(verbose=False)
anim = J.make_anim(plotdir, figno=1, figsize=(8,6))
anim


Out[14]:


Once Loop Reflect

In [ ]: