title: Multilevel Simulations 

Navigate: [&larr; Force as a source term](tut_8_source.html)
| [Overview](index.html)

In some simulations there are regions of special interest. In these regions 
strong gradients might occur, so that a higher resolution is required. 
You then have basically two possibilities to resolve this problem.

1. increase the global resolution of your simulation. This is generally 
   not a good idea because you very quickly might run into memory problems.
   A high number of fluid cells also means long computation times. 
2. Locally refine the mesh in regions of high interest
   You can adaptively refine the mesh in regions where it is desired.
   This focuses the computation cost and memory expenses on the regions
   where it actually is required. Why waste computation time on flow regions
   where very little happens? A general problem are the interfaces between 
   grid levels. You can easily get reflections or non-discontinuities there
   which is why you usually place interfaces in regions where the flow does 
   not fluctuate considerably.

> In [[mus_interpolate_module]] you can get some background information on the implemented 
> interpolation methods and the general workflow


## Test Case Description ##

In this tutorial we cover a channel test case with a cylinder inside. 
The area around and behind the cylinder is refined by a refinement 
patch with a higher resolution.


## Mesh Generation ##

Before starting your simulation you have to set up a mesh again.
Again we involve Seeder to generate the required data structures for the
solver.
It is very advisable to define a variable for the most important variables.
Later on you will be able to easily change the resolution of your simulation 
or some aspect ratio.
One very important parameter for setting the resolution of the simulation
is a reference tree level that is done in [[tem_topology_module:tem_levelof]]. 

In our case this will be the tree level of the 
channel region. Let's name it `level` and set it to 6. In addition to that,
we define the folder (create it via `mkdir mesh`), the refinement level and
the resulting level for the refinement box.

The region around and behind the cylinder is defined by a refinement box.
Let's define first, by how many levels we want to refine the elements
inside this refined area. For simplicity, let's say that all elements should
be on one level higher than the rest of the channel (`refinementLevel`). 

It is also good to have the information about the maximum level in your
simulation available as a parameter (`maxlevel`). You will later on see why.

```lua
folder = 'mesh/' 
level = 6
refinementLevel = 1  
maxlevel = level+refinementLevel
```

> Note: For a Description of levels and the layout of the tree have 
> a look at the [Octree page](../treelm/page/octree.html).


```lua
-- Channel Geometry
length = 2.05
height =  3./16.*length
-- Discretization
dx     = length/2^level
dxMin  = length/2^maxlevel
dxDash = 0.5*dxMin
```

The bounding box, which is the universe in which all elements of the tree will live in, 
is defined as

```lua
bounding_cube = {
  origin = {-length*0.5, -length*0.5, -length*0.5},
  length = length
}
```

The region around the channel is set to the desired tree level of 6. 

```lua
-- Meshing of the whole channel
spatial_object = {
  {
    attribute = {
      kind = 'refinement',
      level = level,
      label='box1'
    }, -- attribute table
    geometry = {
      kind = 'canoND',
      object = {
        origin = {-length*0.5, -height*.5, -0.5*height},
        vec = {{length, 0., 0.},
              {0.,height, 0.},
              {0., 0., height}}
      } -- object table
    } -- geometry table
  }, -- spatial object sub table
} -- spatial object table
```

The rest of the bounding cubic domain is not of interest and hence the discretization
level is not of interest. 

### Defining Geometry ###

We need to specify the walls, the inlet, outlet and the cylinder. 
Let's start with the walls at the north, south position.
The walls at east and west direction will later on become the in- and outlet.

```lua
-- Boundaries
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='east'
     },
     geometry = {
       kind = 'canoND',
       object = {
         origin = {length*0.5,-length*0.5,-length*0.5},
         vec = {{0.0,length,0.0},
                {0.0,0.0,length}},
       }
     }
   })
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='west'
     },
     geometry = {
       kind = 'canoND',
       object = {
         origin = {-length*0.5,-length*0.5,-length*0.5},
         vec = {{0.0,length,0.0},
                {0.0,0.0,length}},
       }
     }
   })
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='north'
     },
     geometry = {
       kind = 'canoND',
       object = {
      	 origin = {-length*0.5,height*0.5+dxDash,-length*0.5},
         vec = {{length,0.0,0.0},
                {0.0,0.0,length}},
       }
     }
   })
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='south'
     },
     geometry = {
       kind = 'canoND',
       object = {
      	 origin = {-length*0.5,-height*0.5-dxDash,-length*0.5},
         vec = {{length,0.0,0.0},
                {0.0,0.0,length}},
       }
     }
   })
```

The walls at the top and bottom (in z-direction) are added 
by extending the `spatial_object` table 

```lua
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='front'
     },
     geometry = {
       kind = 'canoND',
       object = {
  	     origin = {-length*0.5,-length*0.5,height*0.5+dxDash},
         vec = {{length,0.0,0.0},
                {0.0,length,0.0}},
       }
     }
   })
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='back'
     },
     geometry = {
       kind = 'canoND',
       object = {
  	     origin = {-length*0.5,-length*0.5,-height*0.5-dxDash},
         vec = {{length,0.0,0.0},
                {0.0,length,0.0}},
       }
     }
   })
```

For defining the cylinder, we use an STL file. This file was before generated
by Blender, but you can basically use any software you like for generating
such geometry data. The file is located in the repository of Musubi at:
`musubi/tutorial/99_multilevel/cylinder.stl` Before you can use it copy it to
your current simulation folder. Let's include that STL file with:

```lua
table.insert(spatial_object, {
     attribute = {
       kind = 'boundary',
  	   label='obs'
     },
     geometry = {
       kind = 'stl',
       object = { filename = 'cylinder.stl'}
      }
    })
```

One very important action is the placement of the seed.
The seed determines the contiguous flow domain. It basically 
defines for the shapes, what is inside and what is outside.
We palce it right in the middle of the channel, which is simply 

```lua
-- Seed
table.insert(spatial_object, {
  attribute = { kind = 'seed', },
  geometry = { kind = 'canoND',
               object = { origin = { 0.0, 0.0, dx*0.5 },
             }
  } -- geometry
})
```

Ok. Now we have defined all geometric constraints.
Let's continue with refining some parts of the simulation domain.

### Defining Refined Regions ###

You first need to specify the origin of this box and its extents.
Again, we use some variables depending on the total bounding cube.

```lua
-- Geometry of the Refinement Box
size_x  = 0.63*length
start_x = -0.42*length
size_y  = height/2-2.*dxDash
start_y = -size_y/2
size_z  = 3*size_y
start_z = 3*start_y
```

The patch will then lie around and behind the cylinder. 

```lua
table.insert(spatial_object, {
    attribute = {
      kind = 'refinement',
      level = minlevel,
      label='box2'
    },
    geometry = {
      kind = 'canoND',
      object = {
        origin = {start_x, start_y, start_z },
        vec = {{size_x, 0., 0.},
              {0.,size_y, 0.},
              {0., 0., size_z}}
      }
    }
  })
```  

Once you followed through the above explanations, you can visualize the 
generated mesh. Therefore use Seeder Harvesting.

First, we prepare the config file, defining the folder of the mesh files,
the output folder for the vtk file (create the folder first) and the output
itself. We name the file `harvester.lua`.

```lua
mesh          = 'mesh/'
output_folder = 'output/'
output        = {
  format    = 'vtk',
  dataform  = 'binary',
  write_pvd = false
}
```
Then we can run Seeder Harvesting with:
`~/apes/seeder/build/sdr_harvesting harvester.lua`

The resulting vtk file can be visualised with Paraview.
Here is the plot:

![tut_multilevel_cyl_mesh](tut_multilevel_cyl_mesh.jpg)


@note: Actually, the Musubi Configuration is producing NaNs after few 
iterations. So please stop following the tutorial for Multilevel at
this point.


## Setting up the Musubi Configuration ##

After generating the mesh above, we need to tell Musubi that it
should use the above generated mesh. The mesh was generated in the
folder `mesh`. Let's define that along with a name for the simulation

```lua
mesh = './mesh/' -- Mesh information
simulation_name = 'channelRefine'
```

### Initial and Boundary Conditions ###

Initial conditions are set to a medium at rest with constant pressure

```lua
initial_condition = { pressure = 1.0, 
                      velocityX = 0.0,
                      velocityY = 0.0,
                      velocityZ = 0.0 }
```

Next, we define the variable table for the boundary conditions:

```lua
variable = {
  {
    name = 'vel_x',
    ncomponents = 1,
    vartype = 'st_fun',
    st_fun = {
      predefined = 'combined',
      temporal= { 
        predefined='smooth', 
        min_factor = 0.0, max_factor=1.0, 
        from_time=0, to_time=tRamping
      }, 
      spatial = {
        predefined='parabol',
        shape = {
          kind = 'canoND',
          object = {
            center = {-0.5*length,0.,0.},
            vec = { {0.0, height, 0.}, {0., 0., height}} 
          },
        },
        amplitude = u_in
      }
    }
  },
  {
    name = 'vel_y', 
    ncomponents = 1,
    vartype = 'st_fun',
    st_fun = 0.0
  },
  { name = 'vel_z',
    ncomponents = 1,
    vartype = 'st_fun',
    st_fun = 0.0 
  },
}
```






The boundary conditions are a little bit more complex, as we have 
solid walls, the cylinder, and also an inlet and an outlet.
Let's start with the wall boundaries. These include among the walls, which we named 
according to the directions and the cylinder, which we simply called `obs`.
They all get the property of a simple wall.

```lua
boundary_condition = {  
{ label = 'obs', 
   kind = 'wall' },
{ label = 'north', 
   kind = 'wall' },
{ label = 'south', 
   kind = 'wall' },
 { label = 'front', 
   kind = 'wall' }, 
{ label = 'back', 
   kind = 'wall' } }
```

For the outlet, we would like to have a [[mus_bc_fluid_module:outlet_pab]] "simple pressure" boundary condition.

All you need to specify is a reference pressure. 

```lua
table.insert( boundary_condition, 
{ label = 'east',
   kind = 'outlet_pab',
   pressure =  1.0}
 )
```

The inlet is slightly more complicated. Here we would like to set a 
velocity inlet. We would like to have a spatial distribution of the
x-velocity component according to a 
[[tem_spatial_module:tem_spatial_parabol3d_for_treeids]] "three-dimensional parabola".
Also, the velocity should be slowly ramped
up to get a smooth transition from the initial state to the steady state.
Afterall, the definition for the inlet boundary condition looks like

```lua
table.insert( boundary_condition,
{ label = 'west', 
  kind = 'inlet_ubb', 
  velocityX = 'vel_x',
  velocityY = 'vel_y', 
  velocityZ = 'vel_z'
})
```

### Other simulation parameters ###

```lua
-- iterations
nElemsMax = 2^minlevel
nIters = nElemsMax*50
-- time settings
tmax =   nIters    -- total iteration number
interval = tmax/30
tRamping = tmax/20
sim_control = {
  time_control = {
    min = {iter=0}, 
    max = {iter=tmax}, 
    interval = {iter=interval}
  }
}  
```

### Tracking ###

Pressure / Density probe
![tut_multilevel_probedensity](tut_multilevel_probedensity.png)

### Visualize the Results ###

For visualisation of ascii or ascii-spatial format files you can use for example
Gnuplot or matplotlib module of python.

Navigate: [&larr; Force as a source term](tut_8_source.html) 
| [Overview](index.html) 
