Conservative Sod Shock Tube

This tutorial will walk you through the process of setting up a Sod Shock Tube problem in conservative form using splitfxm.

Problem Setup

First, we need to import the necessary modules and create a domain for the problem.

from splitfxm.domain import Domain
from splitfxm.simulation import Simulation
from euler1d import Euler1DConservative
from splitfxm.schemes import default_scheme

Next, we need to define the method we are going to use. It is preferable to use FVM for shock tubes.

We need to define the equations to use for this problem. The Euler1DConservative class is provided for this purpose. It is a class that represents the 1D Euler equations for compressible fluid flow and is a special case of the Advection-Diffusion equations.

Note that the expressions for Euler1DConservative are quite complicated and only the final results are provided here. The derivation of the expressions is provided in the flux_jacobian.py file.

Also, the dFdU function represents the derivative of the flux function with respect to the state vector U. This can be symbolically computed using sympy and the flux function, the program for which is provided in the flux_jacobian.py. The expressions are as follows:

The flux is given by:

and similarly the Jacobian of the flux is given by:

Then, the domain can be created using the Domain.from_size method.

# Define the problem
method = 'FVM'
# Euler equations with gamma for ideal gas
m = Euler1DConservative(gamma=1.4, method=method)

# Create a domain for the shock tube
# nx = number of cells, nb_left = 1 ghost cell, nb_right = 1 ghost cell
# Variables: ["rho", "u", "p"] (density, velocity, pressure)
d = Domain.from_size(100, 1, 1, ["rho", "rhou", "E"])

Once this is done, we can define the initial conditions and boundary conditions. The initial conditions are going to be set afterwards. The boundary conditions are transmissive (or Neumann with zero derivative) for both sides.

# Boundary conditions (transmissive for both sides)
ics = {}
bcs = {"rho": {"left": {"neumann": 0.}, "right": {"neumann": 0.}},
       "rhou": {"left": {"neumann": 0.}, "right": {"neumann": 0.}},
       "E": {"left": {"neumann": 0.}, "right": {"neumann": 0.}}}

Now, we can create a simulation object.

s = Simulation(d, m, ics, bcs, default_scheme(method))

Initial Conditions

We can now set the initial conditions for the domain. The initial conditions are set using the set_value method of the Domain class.

for cell in s._d.interior():
    cell.set_value(s._d.component_index("rho"),
                   1.0 if cell.x() < 0.5 else 0.125)
    cell.set_value(s._d.component_index("rhou"), 0.0)
    cell.set_value(s._d.component_index("E"), 2.5 if cell.x() < 0.5 else 0.25)

Evolve the System

We can now evolve the system in time using the evolve method. The default method uses RK45 for the time integration. You can also use Split-Newton to re-order the variables, although its benefits are more for steady-state problems.

s.evolve(split=True, split_locs=[1], t_diff=0.2)

Visualize the Results

We can visualize the conservative variables using the draw function. However, if we want to visualize the primitive variables, we can use the conservative_to_primitive method of the Euler1DConservative class.

values = [m.conservative_to_primitive(cell_values)
          for cell_values in s._d.values(interior=True)]
plt.plot(d.positions(interior=True), values, "-o")

The results should look like this:

img

It can be noted that the shock velocity and locations have been correctly captured using the default scheme.