Navigate: ← Prerequisites | Overview | Toolchain →


Set up your first simulation with Musubi.

This tutorial is using a 1D gaussian_pulse in pressure computed in a 3D domain. The necessary configuration file to run this case will be generated from scratch within this tutorial. An example of a final config-file for this case can be found in examples/tutorial/tutorial_cases/tutorial_gaussianPulse/.

The musubi.lua File

As a user, your best way to communicate with Musubi is a Lua-Script that tells Musubi what you want to simulate and which options you want to choose. The Lua-syntax is fairly easy, and for this tutorial, we will use only a small subset of it, so don't be afraid if you hear of Lua for the first time.

By default, Musubi looks for a file called musubi.lua inside your current working directory. If you want, you can use different names (for example if you want to keep more than one script e.g. musubi_test.lua) and pass the name of the script you want the application to use as a command-line argument. (e.g.~/apes/musubi/build/musubi musubi_test.lua)

We are now going to build a working, almost-minimal musubi.lua file step-by-step. Please navigate to your simulation directory, and create a new file called musubi.lua with your favorite text editor.

General Simulation Name

Moreover, every simulation should have a name, which is specified by a variable called simulation_name.

In this example, we will simulate a Gaussian pulse in pressure, hence the name Gausspulse seems adequate. You can use just anything as a simulation name, just make it clear and descriptive, as it will help you to identify produced results from the simulation.

simulation_name = 'Gausspulse'

The fluid and Physics Table

Physical and Lattice Boltzmann References

Next, we define the fluid properties, required for LBM simulations. They are divided into two aspects, the physical (_phy) and the Lattice Boltzmann (LB) values (_lat). The physical values are taken from the SI unit system which uses standard units like kg (mass), mol (amount of substance), K (thermodynamic temperature), m (length), s (time), A (electric current and cd (luminous intensity). A lot of physical units are derived from this system. It is normally used to present the solutions to scientific researches. But for the program, it is necessary to use the numerical LB methods to gain solutions. Therefore both unit systems are defined for Musubi and it is possible to convert each other in both directions. In general, these tables look like this:

nu_phy   = 1e-3 -- Kinematic viscosity
rho0_phy =  1.0 -- Density
vel_phy  =  1.0 -- Velocity
cs_phy   =  340 -- m/s speed of sound
Ma_lat  = 0.1                   -- Lattice Mach number
cs_lat  = 1/math.sqrt(3)        -- Lattice speed of sound
vel_lat = Ma_lat * cs_lat       -- Lattice velocity
dt      = dx * vel_lat/vel_phy  -- Physical timestep based on lattice values

nu_phy is the kinematic viscosity of the fluid, rho0_phy and vel_phy are the macroscopic density and velocity. The kinematic viscosity needs to be given inside the table called fluid, most times the bulk_viscosity is also needed. (Lua has a single neat concept tables for representing multiple data in a structure. It allows you to treat variables a bit like trees: they can contain more variables, other tables, or even references to functions.) Tables are denoted by curly braces in Lua. Thus the configuration file for these options looks so far as follows:

fluid = {
  kinematic_viscosity =     nu_phy,
  bulk_viscosity      = 2/3*nu_phy

Time Settings

Now we should specify the number of timesteps for our simulation. We do this by opening a time section with at least the variable max given to specify the maximal number of timesteps you want to simulate. Additionally, you might provide the interval-setting, that controls the debug output: after a predefinded number of iterations, the total density of the system is calculated and written to the console, this defaults to 1. The number of iterations can be set by using {iter=#} to set the number of iterations. Alternative one can use {sim=#} to trigger the check after certain timesteps in your simulation or use {clock=#} to trigger the check after a certain runtime. Since we have conservation of mass, the total density should not change much, so if it does, it is a hint for the user that the results of the simulation are probably wrong. Setting the interval to something around [max/10] is reasonable.

sim_control = {
  time_control = {
    max = {iter=50},
    interval = {iter=5}


It should be followed by a part that identifies the nature of the simulation. To this part belong the following aspects: the layout, the kind and the relaxation. For now, it is not necessary to know these settings in detail. you can choose between some values that you can look up in the mus_scheme_header_module. If these settings are not defined, Musubi will use default values. As a first example, you can set up the identify table as:

identify = {
  kind  = 'fluid',
  layout = 'd3q19',
  relaxation = 'bgk',


The geometry is usually a more complicated thing to define. For anything but the simplest case, we need Seeder, a tool that will be dealt with in the next tutorial chapter. For now, we will use a pre-defined geometry, which is just a simple cube with periodic boundaries and no obstacles. We can specify the length and position (origin) of that cube. The position of the cube is only relevant if you specify positions of other objects, too, such as initial conditions or trackers (we will explain what that is in chapter_03).

Last but not least, the refinementLevel tells Musubi how fine the initial cube (with a length of 10 here) needs to be discretized. A refinement level of 4 means that our initial cube is cut into portions in each dimension, which leaves us with cells in 3 dimensions.

mesh = {
  predefined = 'cube',
  origin = { 0.0, 0.0, 0.0 },
  length = 10.0,
  refinementLevel = 4


Of course you do not only want to simulate, you also want to see results. The tracking section is your way to control some subset output from Musubi. The idea behind it is that for any real-life scale problem, you just cannot save all the information of every point at every timestep, since the shear amount of data would be too much to handle. Thus, we tell Musubi what we want to see (like pressure, velocity...), at what time, and in what position.

Each output is handled by a tracker. Let us define a tracker now, step by step. First, a tracker has a name (specified in label) that will appear in every output file created by this tracker. Anything that is precise and meaningful will do.

tracking = {
  label = 'track_pressure',

Next, we should tell the tracker what we are interested in. The variable section will do just that for us. In this example, we will ask the tracker to store density and velocity information for us.

The output will be stored on your hard drive, in the location you specify in folder.

  variable = { 'pressure', 'velocity' },
  folder = './tracking/',

The most complicated part is the tem_shape_module variable. It defines where inside the simulation domain (which is a cube in our example) you want to observe the variables. You can take samples at a point, along a line, everywhere on a plane, or even everywhere inside a given box. These options will be discussed in more depth in chapter_03. For now, we are happy with a point at position (1,1,1):

  shape = {
    kind = 'canoND',
    object = {origin = {1.0, 1.0, 1.0} }

The format option lets you choose the file format in which the data should be stored. The options you have are ascii, asciiSpatial and vtk. Ascii is only really usable for point trackers. For anything more sophisticated (starting in the next chapter), we will use the build-in tool mus_harvesting for post-processing the data.

  output = {format = 'ascii'},

Finally, we have to specify at which timesteps we want to save data. Inside the time section, you can specify the first (min) and last (max) timestep that is of interest to you. If [max] is negative, every timestep till the end will be considered. In interval, you can choose if you want to save every timestep (set it to 1 then) or only every [n]-th timestep (set interval to n in this case).

  time_control = { min = {iter=1}, max = {iter=50}, interval = {iter=1} },

Don't forget to close the tracking section with another curly brace.


In the restart part of your musubi.lua file you can create so-called restart files in order to save the results of your simulation. With these restart files you can create vtk files with mus_harvesting to view them in Paraview. In addition to that, you can also read the restart files to go on with your simulation beginning at the saved point written in the restart files. In the "restart" section you are also able to use time_control to define the min, max and the interval time, when Musubi writes a restart file to the restart/ folder. Be sure that you set up useful definitions for that. For example, you can set up the restart settings like this:

restart = {
  write = 'restart/',   -- prefix to write the files to
  time_control = { min = 0, max = 10, interval = 10}  -- timing definitions (either iterations or simulation time)

Initial Conditions

Initial conditions can be specified in the initial_condition table. First, let us define that the velocity at t=0 should be 0 everywhere.

But if every point is equal, not much is going to happen. So for the initial pressure, we will do something more fancy: We will define a function gausspulse that will set the initial pressure such that it has a peak in the middle of the domain, and decreases quickly towards the sides. With this, we will create two waves running from the center towards both sides. The initial_condition-table will point to this function for the density:

initial_condition = {
  velocityX = 0.0,
  velocityY = 0.0,
  velocityZ = 0.0,
  pressure  = gausspulse

The function can be defined like this in the Lua script:

function gausspulse(x, y, z)
  originX =  5.0
  halfwidth = 1.0
  amplitude = 0.01
  return p0+amplitude*math.exp(-0.5/(halfwidth^2)*( x - originX )^2)

Moreover,define the p0 as global variable in the function. So that it will return the function like p0+amplitude*math.exp(-0.5/halfwidth^2)*(x-originX)^2:

rho0 = 1.
cs2 = 1./3.
p0 = rho0*cs2


If you did all steps correct you're now able to run the test case. If something is not working you can compare your config file with our template you can find under: examples/tutorial_cases/gaussianPulse/musubi.lua. After finishing your run, you will find an output file called Gausspulse_track_pressure_p00000.res in your tracking-folder. If you open it, you will find densities and velocities for every iteration. If you choose an other interval in tracking-table (for example iter=5), the values for every choosen iteration (e.g. every 5th iteration) will be listed here.

gnuplot -p -e "plot \"tracking/Gausspulse_track_pressure_p00000.res\" using 1:2"

to get a plot of the pressure similar to this one:


The picture shows the pulse running through your point tracker several times. This happens because of the periodic boundaries. The intensity of the pulse decreases due to dissipation.

In the next chapter, we will learn to define more complex geometries, and to create more sophisticated outputs.

Next chapter: Toolchain →