31

Is it possible to accurately solve the 1D Euler equations in Mathematica using NDSolve?

For example, let us consider the Sod shock tube problem. Introduction to this problem can be found in the following links:

http://www.csun.edu/~jb715473/examples/euler1d.htm

https://en.wikipedia.org/wiki/Sod_shock_tube

Using the notation {r, v, e} for $(\rho,v,E)$ we can formulate the problem in Mathematica as:

g = 1.4;
p[x_, t_] := (g - 1) (e[x, t] - r[x, t] v[x, t]^2/2);
eqs = {
    D[r[x, t], t] + D[r[x, t] v[x, t], x] == 0,
    D[r[x, t] v[x, t], t] + D[r[x, t] v[x, t]^2 + p[x, t], x] == 0,
    D[e[x, t], t] + D[v[x, t] (e[x, t] + p[x, t]), x] == 0
};

and initial conditions

r0[x_] := 1.0 Boole[0 < x <= 0.5] + 0.25 Boole[0.5 < x <= 1.0];
v0[x_] := 0.0;
p0[x_] := 1.0 Boole[0 < x <= 0.5] + 0.1 Boole[0.5 < x <= 1.0];

Setting Dirichlet boundary conditions and throwing it into NDSolve:

ppR = 401;
ndsol = NDSolve[Join[eqs, {
    r[x, 0] == r0[x], r[0, t] == r0[0], r[1, t] == r0[1],
    v[x, 0] == v0[x], v[0, t] == v0[0], v[1, t] == v0[1],
    p[x, 0] == p0[x], p[0, t] == p0[0], p[1, t] == p0[1]}],
 {r, v, e}, {x, 0, 1}, {t, 0, 0.1},
 MaxSteps -> 10^3, PrecisionGoal -> 4, AccuracyGoal -> 4, 
 Method -> {"MethodOfLines", "Method" -> "StiffnessSwitching", 
 "SpatialDiscretization" -> {"TensorProductGrid", 
  "DifferenceOrder" -> Pseudospectral, "MaxPoints" -> {ppR}, 
  "MinPoints" -> {ppR}}}]

Taking the solution and plotting

DensityPlot[Evaluate[First[v[x, t] /. ndsol]], {x, 0, 1}, {t, 0, 0.1}, ColorFunction -> Hue]

Solution plot

We see some nasty numerical errors. Is there anyway to get an accurate solution using NDSolve?

Edit: This paper suggests that the Method of Lines should apply: http://math.lanl.gov/~mac/papers/numerics/H79.pdf

user21
  • 39,710
  • 8
  • 110
  • 167
  • Your link isn't working here – Dr. belisarius Oct 08 '12 at 22:24
  • Strange... Is it something I did wrong? – partition_of_unity Oct 08 '12 at 22:31
  • perhaps this answer helps? but blind experimentation is not very satisfactory – acl Oct 08 '12 at 22:55
  • I hammered on this for a while, and got nowhere. BTW, I think in the r0[x_]:= its 0.125 (least of your worries) If you go through and make everything a fraction e.g. 1.4 -> 14/10 then it doesn't even crank, crashing out with a 1/0 ComplexInfinity error... – Eric Brown Oct 09 '12 at 01:00
  • Both links work fine for me. Perhaps the server was down for maintenance. – stevenvh Oct 09 '12 at 07:29
  • @DaleRoberts Your NDSolve cell errored out with signficant errors... is it Pseudospectral or PseudoSpectral? or am I missing something (because both didn't work). I also have a division by zero error. – dearN Oct 10 '12 at 20:50
  • @EricBrown How'd you go about it? This coupled set of equations is full of errors. Here is my edit. You'll notide that I am not using the boolean function – dearN Oct 10 '12 at 22:31
  • @drN Not Sure what you mean. I was just trying different settings. Ack, yours is full of errors. = where there should be ==, {r,v,e} should be the variables, etc. Dale's code looks fine to me, though, don't know why it doesn't work. – Eric Brown Oct 12 '12 at 03:26

3 Answers3

20

Update-1: The initial conditions in the question were wrong/incomplete. (removed 1/0 errors)

Update-2: The 1D Euler equations were modified to match this source. (ultimately not necessary in V10.4, but is in V8)

Update-3: Method options in NDSolve were modified to produce an accurate result. (ENO schemes are not yet supported, but the proposed answer below is robust enough to tackle a range of Euler fluid dynamic and MHD problems) Problem Solved!

eqs = {D[r[x, t], t] + D[r[x, t] v[x, t], x] == 0,
   D[r[x, t] v[x, t], t] + D[r[x, t] v[x, t]^2 + p[x, t], x] == 0,
   D[r[x, t] e[x, t], t] + D[r[x, t] v[x, t] (e[x, t] + p[x, t]/r[x, t]), x] == 0};

g = 1.4;
p[x_, t_] := (g - 1) r[x, t] (e[x, t] - v[x, t]^2/2);

(*Sod Shock Tube*)
r0[x_] := 1.0 Boole[0 <= x <= 0.5] + 0.125 Boole[0.5 < x <= 1.0];
v0[x_] := 0.0;
p0[x_] := 1.0 Boole[0 <= x <= 0.5] + 0.1 Boole[0.5 < x <= 1.0];

ppR = 400;

ndsol = NDSolve[Join[eqs,
    {r[x, 0] == r0[x], r[0, t] == r0[0], r[1, t] == r0[1],
     v[x, 0] == v0[x], v[0, t] == v0[0], v[1, t] == v0[1],
     p[x, 0] == p0[x], p[0, t] == p0[0], p[1, t] == p0[1]}],
   {r, v, e}, {x, 0, 1}, {t, 0, 0.1},
   MaxSteps -> Infinity, PrecisionGoal -> 1, AccuracyGoal -> 1, 
   Method -> {"MethodOfLines",
     "SpatialDiscretization" -> {"TensorProductGrid", 
       "MinPoints" -> ppR, "MaxPoints" -> ppR, 
       "DifferenceOrder" -> 2}, 
     Method -> {"Adams", "MaxDifferenceOrder" -> 1}}];

p1 = DensityPlot[
   Evaluate[First[r[x, t] /. ndsol]], {x, 0, 1}, {t, 0, 0.1}, 
   PlotLabel -> "Density"];
p2 = DensityPlot[
   Evaluate[First[v[x, t] /. ndsol]], {x, 0, 1}, {t, 0, 0.1}, 
   PlotLabel -> "Velocity"];
GraphicsRow[{p1, p2}, ImageSize -> Large]

Manipulate[
 Plot[Evaluate[First[v[x, t] /. ndsol]], {x, 0, 1}, 
  PlotRange -> {{0, 1}, {0, 1}}], {t, 0, 0.1, 0.0025}]

enter image description here enter image description here

Young
  • 7,495
  • 1
  • 20
  • 45
  • Oh, I didn't notice that the original definition for the i.c. misses the value at $x=0$. This does make things worse, but sadly the solution given by NDSolve still contains remarkable error even after correcting this. Just check the result of Manipulate[Plot[Evaluate[First[v[x, t] /. ndsol]], {x, 0, 1}, PlotRange -> All], {t, 0, 0.1}] – xzczd Jul 03 '16 at 05:12
  • @xzczd The result now accurately matches the reference solution in the original question! – Young Jul 04 '16 at 00:14
  • 1
    Interesting, though there're still small oscillations (so I think it cannot be called accurate :) ), now the solution is quite close to the correct one. Just a side note: there seems to be a bug in NDSolve of v9.0.1, if you run the code above in v9, the kernel will crash. v8.0.4 and v10.4.1 don't suffer the problem. – xzczd Jul 04 '16 at 03:52
  • @xzczd I noticed small oscillations in the reference solution as well, but I agree that these are slightly larger. My proposed answer seems to advance the solution considerably (if not sufficiently) and shows NDSolve still has a few tricks =) – Young Jul 04 '16 at 04:23
  • 3
    Wow, finally! I've been waiting a long time for this :) – partition_of_unity Jul 04 '16 at 04:42
  • @xzczd Is this answer worthy of the bounty you offered? – Young Jul 09 '16 at 06:36
  • Of course, your answer is a real pleasant surprise to me, +26 :) . Just another side note: your modification for the form of equation isn't necessary in v10.4.1, but it's necessary in v8.0.4, or NDSolve won't give the correct result. (Seems that NDSolve is improved silently in some aspect in v10.) – xzczd Jul 09 '16 at 07:18
  • @xzczd Thank you! Hopefully a ENO solver scheme will be available in the future and/or a Finite Element solver that can handle non-linear coefficients. – Young Jul 09 '16 at 12:23
  • @Young very nice work! +1! – ABCDEMMM Aug 20 '19 at 22:49
14

This is a classical shock-tube problem in which a initially diaphragm separates a hi-pressure, high-density region from one of lower pressure and density. The classical exact solution has multiple discontinuities, a shock wave and a contact-surface (density discontinuity) that propagate to the right, and a continuous rarefaction wave traveling into the high-pressure gas.

The discontinuities cannot be captured by a pseudo spectral scheme,which produces the disastrous oscillations. This also occurs with many other spatial differencing schemes that have too little inherent dissipation. See the book "Difference Methods for Initial Value Problems" by Richtmeyer and Morton (1967), which discusses methods for adding "artificial viscosity" to the Euler equations. This enables the discontinuities to be successfully smeared out over multiple grid points. There are many more modern methods that accomplish the same purpose. Try Googling the terms "Roe Scheme, ENO Scheme, Flux-Difference Splitting."

In short, Mathematica's NDSolve[] command is not yet able to handle directly the Euler equation flows that have discontinuous solutions, and this includes all mixed subsonic/supersonic flows such as your case. Theoretically, it should be able to handle subsonic flows, which are always continuous. Unfortunately, I have found to my dismay that NDSolve[] is not yet sophisticated enough to permit one to use the type of characteristics-based inflow and outflow boundary conditions that are an absolute necessity for generating stable, accurate subsonic flow solutions. I posted a question on this site two days ago, but the question was Closed Out (removed) by other more senior users who deemed it inappropriate. That question was titled "NDSolve rejects a common non-reflective subsonic outflow boundary condition for the 1-D Euler equations".

P. D. Thomas
  • 261
  • 2
  • 6
  • I have edited your closed question. You are raising correct issues here but you need to present the problem at hand in a right manner. Best of luck in future! – PlatoManiac May 02 '13 at 23:44
8

Here's a solution by adding a very small viscosity μ to Euler equations (notice Euler equations can be seen as particular Navier–Stokes equations with zero viscosity and zero thermal conductivity):

γ = 1.4;
p[x_, t_] = (γ - 1) (Ε[x, t] - 1/2 ρ[x, t] v[x, t]^2);
eqs = With[{μ = 5 10^-4, ρ = ρ[x, t], v = v[x, t], p = p[x, t], Ε = Ε[x, t]},
   {D[ρ, t] + D[ρ v, x] == 0,
    D[ρ v, t] + D[ρ v^2 + p, x] == μ D[v, x, x](* <- The key point is here *),
    D[Ε, t] + D[v (Ε + p), x] == 0}];

ρ0[x_] = Piecewise[{{0.125, 0.5 < x <= 1}}, 1];
v0[x_] = 0;
p0[x_] = Piecewise[{{0.1, 0.5 < x <= 1}}, 1];

mol[n_Integer, o_: "Pseudospectral"] := {"MethodOfLines", 
    "SpatialDiscretization" -> {"TensorProductGrid", "MaxPoints" -> n, 
        "MinPoints" -> n, "DifferenceOrder" -> o}}

{solρ, solv, solΕ} = 
  NDSolveValue[{eqs, 
      {ρ[x, 0] == ρ0[x], ρ[0, t] == ρ0[0], ρ[1, t] == ρ0[1], 
       v[x, 0] == v0[x], v[0, t] == v0[0], v[1, t] == v0[1], 
       p[x, 0] == p0[x], p[0, t] == p0[0], p[1, t] == p0[1]}}, 
    {ρ, v, Ε}, {x, 0, 1}, {t, 0, 0.1}, Method -> mol[400, 4]];

μ should be small enough but not too small, or NDSolve will fail again. A dense enough spatial grid is also necessary. Though the warnings eerri and eerr generated, the obtained solution is in good agreement with the one given in OP's link:

Manipulate[GraphicsRow[
  Plot[#, {x, 0, 1}, PlotRange -> {0, 11/10}] & /@ {solρ[x, t], solv[x, t], 
    p[x, t] /. {ρ -> solρ, Ε -> solΕ, v -> solv}}, ImageSize -> 1000], {t, 0, 0.1}]

enter image description here

xzczd
  • 65,995
  • 9
  • 163
  • 468