Advanced Methods for Solving for Solving Problems in Multi-Dimensions with Usim

In Advanced USim Simulation Concepts we examined the basic ingredients of a USim input file: the simulation grid (see Defining the Simulation Grid); data structures (see Allocating Memory); how to assign initial conditions (see Setting Initial Conditions) and how to write out additional data (see Writing Out Data). In Advanced Methods for Solving the Euler Equations with USim, we built on these concepts and demonstrated the basic methods used by USim to solve the Euler equations without the use of macros. Next, in Advanced Methods for Solving the Magnetohydrodynamics Equations with USim, we extended these ideas to demonstrate how to solve the MHD equations in USim, again without the use of macros.

This tutorial is based on the Rayleigh-Taylor Instability (rtInstability.pre) example in USimBase, which demonstrates the well-known Rayleigh-Taylor instability problem described by:

Jun, Norman, & Stone, ApJ 453, 332 (1995).

Using this example, we will examine how to solve the equations of inviscid compressible hydrodynamics and ideal compressible magnetohydrodynamics directly without the use of macros.

Defining the Simulation Grid

In order to execute the two-dimensional Rayleigh-Taylor, we must define a two-dimensional grid simulation grid. In USim, this is done by replacing the Grid input block defined in Defining the Simulation Grid with:

<Grid domain>
  kind=cart2d
  lower=[-0.25  -0.75]
  upper=[0.25  0.75]
  cells=[64  192]
  periodicDirs=[0]
  ghostLayers=2
  isRadial=0
  writeGeom=0
  writeConn=0
  writeHalos=0
</Grid>

Notice that the lower, upper and cells input blocks now take two entries, one for each dimension. A three-dimensional Cartesian grid would therefore be defined by:

<Grid domain>
  kind=cart3d
  lower=[-0.25  -0.75  -0.25]
  upper=[0.25  0.75  0.25]
  cells=[64  192  64]
  periodicDirs=[0 2]
  ghostLayers=2
  isRadial=0
  writeGeom=0
  writeConn=0
  writeHalos=0
</Grid>

Note the usage of the periodicDirs attribute in these blocks. This attribute species the directions in the computational mesh that are periodic. For this example in two-dimensions, the 0 (or x) direction is periodic; similarly, in three-dimensions, both the 0 and 2 (z) directions are periodic. Note that this attribute is necessary to setup information within the simulation grid regarding periodicity; however, periodic boundary conditions must still be applied in the simulation. This is discussed below.

Allocating Simulation Memory

USim data structures automatically account for the chosen dimensionality of the simulation. As a result, there is no need to change how data structures are added for the Euler equations compared to the discussion in Advanced Methods for Solving the Euler Equations with USim or the MHD equations compared to the discussion in Advanced Methods for Solving the Magnetohydrodynamics Equations with USim.

Initializing the Fluid

The initial condition for a multi-dimensional simulation follows the same basic three-step pattern outlined previously; however, the kind attribute for the initialization updater has to match the dimension of the mesh:

<Updater setVar>
  kind=initialize2d
  onGrid=domain
  in=none
  out=[q   qNew]

  <Function initFunc>
    kind=exprFunc
    # adiabatic index
    gas_gamma=1.4
    # Upper fluid density
    rhoTop=2.0
    # Lower fluid density
    rhoBottom=1.0
    # Perturbation amplitude
    perturb=0.01
    # Location of upper y boundary
    ytop=0.75
    # acceleration due to gravity
    gravity=0.1
    # X-extent of domain
    lx=0.5
    # Y-extent of domain
    ly=1.5
    preExprs=[ p0=0.01   pert=0.01   pi=3.14159   rho=if(y<0.0,rhoBottom,rhoTop)   pr=(1.0/gas_gamma)-(gravity*rho*y)   vx=0.0   vy=(0.25*perturb)*(1.0+cos(2.0*pi*x/lx))*(1.0+cos(2.0*pi*y/ly))   vz=0.0  ]
    exprs=[ rho   rho*vx   rho*vy   rho*vz   (pr/(gas_gamma-1.0))+0.5*rho*vy*vy  ]
  </Function>

</Updater>

As in Solving Multi-Dimensional Problems in USim, this initialization block creates a hydrostatic equilibrium consisting of a heavier fluid supported by a lighter fluid, which is perturbed with a single mode. Compared to the initialization block for the Sod shock tube problem discussed in Advanced Methods for Solving the Euler Equations with USim, the key difference as far as problem dimensionality is concerned is that the kind block is specified as initialize2d for this problem. This pattern is repeated for all updaters within a USim input file, e.g. for the generic updater update:

<Updater Updater_Name (type=string)>
  kind = updateKind<NDIM>d
  onGrid = <Grid_Name>
</Updater>

Here, updateKind is the kind of the USim updater that is being utilized (e.g. for the initialization input block, this is initialize) and <NDIM> is the dimensionality of the problem, e.g. 1,2,3 for one-, two- and three-dimensions.

Evolving the Fluid

USim implements the well-known MUSCL scheme to compute the spatial discretization of a hyperbolic conservation law (see Using USim to solve the Euler Equations for a more detailed description). In Solving Multi-Dimensional Problems in USim, we added this algorithm through:

#
# Add the spatial discretization of the fluxes
finiteVolumeScheme(DIFFUSIVE)

#
# Add source term for gravitational acceleration to the equations
addGravitationalAcceleration(GRAVITY_ACCEL)

For the Rayleigh-Taylor Instability (rtInstability.pre) example in two-dimensions, this macro expands to give:

<Updater hyper>
  kind=classicMuscl2d
  onGrid=domain
  in=[q]
  out=[qNew]
  cfl=0.4
  timeIntegrationScheme=none
  numericalFlux=hllcEulerFlux
  limiter=[muscl]
  variableForm=primitive
  preservePositivity=0

  <Equation euler>
    kind=eulerEqn
    gasGamma=1.4
  </Equation>

  equations=[euler]

  <Source gravity>
    kind=exprHyperSrc
    gravity=0.1
    rhoSrc=0.0
    mxSrc=0.0
    mzSrc=0.0
    inpRange=[0  1  2  3  4]
    outRange=[0  1  2  3  4]
    indVars=[rho rhou rhov rhow Er]
    exprs=[ rhoSrc   mxSrc   -rho*gravity   mzSrc   -gravity*rhov  ]
  </Source>

  sources=[gravity]
</Updater>

Compared to the Shock Tube (shockTube.pre) example discussed in Advanced Methods for Solving the Euler Equations with USim, we see that this is following the pattern discussed above, i.e. kind = classicMuscl1d has been replaced by kind = classicMuscl2d. We also have added an additional Source block to this updater, corresponding to the use of the macro:

# Add source term for gravitational acceleration to the equations
addGravitationalAcceleration(GRAVITY_ACCEL)

The use of this macro resulted in the addition of exprHyperSrc to the classicMusclUpdater (1d, 2d, 3d) updater:

<Source gravity>
  kind=exprHyperSrc
  gravity=0.1
  rhoSrc=0.0
  mxSrc=0.0
  mzSrc=0.0
  inpRange=[0  1  2  3  4]
  outRange=[0  1  2  3  4]
  indVars=[rho rhou rhov rhow Er]
  exprs=[ rhoSrc   mxSrc   -rho*gravity   mzSrc   -gravity*rhov  ]
</Source>

Note that this block is following the classic three step pattern that used throughout USim (in this case, no preExpressions have been defined). The additional attributes inpRange and outRange tell USim which components of the input and output variables for the classicMusclUpdater (1d, 2d, 3d) updater to work with.

Applying Boundary Conditions

The Rayleigh-Taylor instability problem requires more advanced boundary conditions that were used for the Sod Shock tube in the previous example. In two-dimensions, appropriate boundary conditions for the Rayleigh-Taylor instability are periodic boundaries in the direction transverse to the gravitational field and reflecting boundaries in the parallel direction, which are the x and y directions respectively.

In the Rayleigh-Taylor Instability (rtInstability.pre) example, we applied these boundary conditions through the use of the macros:

#
# Boundary conditions
boundaryCondition(wall,top)
boundaryCondition(wall,bottom)
boundaryCondition(periodic)

The first two of these macros expand to yield the blocks:

<Updater wallBoundaryOnEntitytop>
  kind=eulerBc2d
  onGrid=domain
  in=[q]
  out=[q]
  model=eulerEqn
  bcType=wall
  entity=top
  gasGamma=1.4
</Updater>

<Updater wallBoundaryOnEntitybottom>
  kind=eulerBc2d
  onGrid=domain
  in=[q]
  out=[q]
  model=eulerEqn
  bcType=wall
  entity=bottom
  gasGamma=1.4
</Updater>

These reflecting wall boundary conditions are described below. The third macro expands to yield a periodic boundary condition:

<Updater periodicBoundaryOnEntityghost>
  kind=periodicCartBc2d
  onGrid=domain
  in=[q]
  out=[q]
</Updater>

We discuss this in the next section.

Periodic Boundary Conditions

Periodic boundary conditions in USim are specified in two ways, both of which must be used simultaneously. First, an input block peroidicDirs is added to the Grid block:

<Grid domain>
  kind = cart2d
  ghostLayers = 2
  lower = [-0.25, -0.75]
  upper = [  0.25,  0.75]
  cells = [64, 192]
  periodicDirs = [0]
</Grid>

The dimensions in which periodic boundary conditions are applied are specified by a vector. In this case, we apply periodic boundary conditions in the x direction, so we set:

periodicDirs = [0]

If we wished (for example) to extend the Rayleigh-Taylor instability problem to three-dimensions, then it might be appropriate to apply periodic boundary conditions to the x and z directions. In this case, we would set:

periodicDirs = [0,2]

In addition to the periodicDirs input block that is added to Grid, we must also add an updater block to apply the periodic boundary conditions within the simulation loop. This is done through the updater:

<Updater periodicBoundaryOnEntityghost>
  kind=periodicCartBc2d
  onGrid=domain
  in=[q]
  out=[q]
</Updater>

where the meanings of the various input blocks are described as in Evolving the Fluid. Note that if we wished to apply periodic boundary conditions in a three-dimensional problem, then we would set:

kind = periodicCartBc3d

This follows the pattern for changing the dimension of a USim simulation discussed previously.

Reflecting Wall Boundary Conditions

In the transverse direction to the flow (here the y direction), we specify reflecting wall boundary conditions. In USim, this is done using the updater:

<Updater wallBoundaryOnEntitytop>
  kind=eulerBc2d
  onGrid=domain
  in=[q]
  out=[q]
  model=eulerEqn
  bcType=wall
  entity=top
  gasGamma=1.4
</Updater>

<Updater wallBoundaryOnEntitybottom>
  kind=eulerBc2d
  onGrid=domain
  in=[q]
  out=[q]
  model=eulerEqn
  bcType=wall
  entity=bottom
  gasGamma=1.4
</Updater>

The eulerBc (1d, 2d, 3d) applies boundary condition appropriate for systems of equations that follow the pattern of the Euler equations, i.e. a conserved state vector similar to that described at eulerEqn. The specific type of hydrodynamic equation that the boundary condition is applied to is specified by the attribute modelEqn. Valid options for this attribute include eulerEqn, realGasEqn and realGasEosEqn. Note that attributes required by these equations should be specified in the block associated with the boundary condition. In the examples above, this includes the gasGamma parameter required for eulerEqn.

A range of boundary conditions can be applied by eulerBc (1d, 2d, 3d); the specific choice is specified by the attribute bcType. Valid options for this attribute include wall, noInflow and noSlip.

Finally, we must specify the boundary that the boundary condition is to be applied on. For a structured mesh such as used in the Rayleigh-Taylor Instability (rtInstability.pre) example, a range of boundaries are predefined:

left The lower x-boundary.

right The upper x-boundary.

bottom The lower y-boundary.

top The upper y-boundary.

back The lower z-boundary.

front The upper z-boundary.

We specify the boundary to apply the boundary through the use of the entityType attribute, e.g. to apply the wall boundary condition on the upper y-boundary, we set:

entity=top

Advancing By A Time Step

In order to solve the MHD equations, we have to advance the conserved quantities from time \(t\) to \(t+\Delta t\). This is done by applying a time integration scheme. In Solving Multi-Dimensional Problems in USim, we did this using the macro:

timeAdvance(TIME_ORDER)

This expands to yield:

<Updater mainIntegrator>
  kind=multiUpdater2d
  onGrid=domain
  in=[q]
  out=[qNew]

  <TimeIntegrator timeStepper>
    kind=rungeKutta2d
    onGrid=domain
    scheme=second
  </TimeIntegrator>


  <UpdateStep boundaryStep>
    operation=boundary
    updaters=[wallBoundaryOnEntitytop wallBoundaryOnEntitybottom periodicBoundaryOnEntityghost]
    syncVars=[q]
  </UpdateStep>


  <UpdateStep integrationStep>
    operation=integrate
    updaters=[hyper]
    syncVars=[qNew]
  </UpdateStep>


  <UpdateSequence sequence>
    startOnly=[]
    restoreOnly=[]
    writeOnly=[]
    loop=[boundaryStep   integrationStep]
  </UpdateSequence>

</Updater>

This time integration scheme is very similar to that described in Advanced Methods for Solving the Euler Equations with USim; we follow the pattern described above to switch the dimension of the mainIntegrator and the timeStepper blocks. To handle the changes to the boundary conditions compared to Advanced Methods for Solving the Euler Equations with USim, we change the boundaryStep block from:

<UpdateStep boundaryStep>
  operation=boundary
  updaters=[copyBoundaryOnEntityleft copyBoundaryOnEntityright periodicBoundaryOnEntityghost]
  syncVars=[q]
</UpdateStep>

to:

<UpdateStep boundaryStep>
  operation=boundary
  updaters=[wallBoundaryOnEntitytop wallBoundaryOnEntitybottom periodicBoundaryOnEntityghost]
  syncVars=[q]
</UpdateStep>

Putting it all Together

In Solving Multi-Dimensional Problems in USim, we told USim that we’re done specifying the simulation and that it can be run by calling the macro:

runFluidSimulation()

This macro collects up all of the updaters that we have added so far and, based on the sequence that we have added them, figures out how to call them to run a USim simulation. For the Rayleigh-Taylor Instability (rtInstability.pre) example, the computations performed by this macro results in the following UpdateSteps and UpdateSequence being generated:

<UpdateStep generateStep>
  updaters=[]
  syncVars=[]
</UpdateStep>


<UpdateStep startStep>
  updaters=[setVar wallBoundaryOnEntitytop wallBoundaryOnEntitybottom periodicBoundaryOnEntityghost]
  syncVars=[q]
</UpdateStep>


<UpdateStep restoreStep>
  updaters=[]
  syncVars=[]
</UpdateStep>


<UpdateStep bcStep>
  updaters=[wallBoundaryOnEntitytop wallBoundaryOnEntitybottom periodicBoundaryOnEntityghost]
  syncVars=[q]
</UpdateStep>


<UpdateStep hyperStep>
  updaters=[mainIntegrator copier]
  syncVars=[]
</UpdateStep>


<UpdateStep writeStep>
  updaters=[computePressure computeVelocity computeDensity]
  syncVars=[]
</UpdateStep>


<UpdateSequence simulation>
  startOnly=[generateStep   startStep]
  restoreOnly=[generateStep   restoreStep   bcStep]
  writeOnly=[bcStep   writeStep]
  loop=[hyperStep]
</UpdateSequence>
</UpdateSequence>

Note the similarlity between these blocks and those discussed in Advanced Methods for Solving the Euler Equations with USim. In fact, the only difference is the change in the bcStep block, which is analogous to that discussed above, i.e. we have that:

<UpdateStep bcStep>
  updaters=[copyBoundaryOnEntityleft copyBoundaryOnEntityright periodicBoundaryOnEntityghost]
  syncVars=[q]
</UpdateStep>

becomes:

<UpdateStep bcStep>
  updaters=[wallBoundaryOnEntitytop wallBoundaryOnEntitybottom periodicBoundaryOnEntityghost]
  syncVars=[q]
</UpdateStep>

Advanced USim Simulation Structure

In earlier tutorials, we developed a simple pattern that could be used to design USim simulations using macros. This pattern can be repeated when we don’t use macros; however, we now need to add the grids, data structures, updaters, boundary conditions and time integration schemes by hand and then tell USim how to run them. In multi-dimensions, we can extend the pattern to:

# Specify parameters for the specific physics problem
$ PARAM_1 = <value>
$ PARAM_2 = <value>
$ PARAM_N = <value>

# Initialize a USim simulation
# Simulation start and end times
tStart = <float>
tEnd = <float>
# Number of data files to write
numFrames = <integer>
# Initial time-step to use
initDt = <float>
# Level of feedback to give user
verbosity = <info/debug>

<Component fluids>
  kind = updaterComponent

   # Setup the grid
  <Grid Grid_Name (type=string)>
    <grid parameters>
  </Grid>

   # Create data structures needed for the simulation
  <DataStruct DataStruct_Name1 (type=string)>
    kind = nodalArray
    onGrid = <Grid_Name>
    numComponents = <int>
  </DataStruct>

  <DataStruct DataStruct_Name2 (type=string)>
    kind = nodalArray
    onGrid = <Grid_Name>
    numComponents = <int>
  </DataStruct>

   <DataStruct DataStruct_NameN (type=string)>
    kind = nodalArray
    onGrid = <Grid_Name>
    numComponents = <int>
  </DataStruct>

  # Specify initial condition
  <Updater Initialization_Updater_Name (type=string)>
    kind = initialize<NDIM>d
    onGrid = <Grid_Name>
    out = <DataStruct_Name for t = 0>

    # initial condition to use
    <Function func>
      kind = exprFunc

      # Step 1: Add Variables
      VARIABLE_1 = <float>
      VARIABLE_2 = <float>
      VARIABLE_N = <float>


      # Step 2: Add Pre-Expressions
      preExprs = [ \
          "<PreExpression_1>", \
          "<PreExpression_2>", \
          "<PreExpression_N>"
          ]

     # Step 3: Add expressions specifying initial condition on density,
     # momentum, total energy
     addExpression(<expression>)

      exprs = ["<density_expression>",  \
                    "<xMomentum_expression>",  \
                    "<yMomentum_expression>",  \
                    "<zMomentum_expression>", \
                    "<totalEnergy_expression>"]

    </Function>

  </Updater>

  # Add the spatial discretization of the fluxes
  <Updater FiniteVolume_Updater_Name (type=string)>
    kind=classicMuscl<NDIM>d
    onGrid=<Grid_Name>

    # input data-structures
    in=<DataStruct_Name for t^n>
     # output data-structures
    out=<DataStruct_Name for \nabla \cdot F(<DataStruct_Name for t^n>)>
    # CFL number to use
    cfl=<float>
    # legacy time integration scheme, attribute; should be set to "none"
    timeIntegrationScheme=none
    # Riemann solver
    numericalFlux=<string>
     # Limiter to use
    limiter=[<string>]
    # Form of variable to limit
    variableForm=<string>
    # Whether to check variables for physical validitiy
    preservePositivity=<int>

    # Hyperbolic equation system
    <Equation euler>
      kind=eulerEqn
      # Adiabatic index
      gasGamma=<float>
    </Equation>

     # Hyperbolic equation to solve
    equations=[euler]

  <Source Souce_Name_1>
    kind=<string>
    <Source_Name_1 params>
  </Source>

  ...

  <Source Souce_Name_N>
    kind=<string>
    <Source_Name_N params>
  </Source>

  sources=[Souce_Name_1, ..., Souce_Name_N]
  </Updater>

  # Boundary conditions
  <Updater BoundaryCondition_Name1 (type=string)>
    kind=<string><NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n>
    out=<DataStruct_Name for t^n>
    entity=<Entity_Name (type=string)>
    <Boundary Condition Parameters>
  </Updater>

  ...

  <Updater BoundaryCondition_NameN (type=string)>
    kind=<string><NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n>
    out=<DataStruct_Name for t^n>
    entity=<Entity_Name (type=string)>
    <Boundary Condition Parameters>
  </Updater>

  # Output Diagnostics
  <Updater OutputDiagnosticUpdater_Name1 (type=string)>
    kind=<string><NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name1, DataStruct_Name2, ..., DataStruct_NameN>
    out=<OutputDiagnosticDataStruct_Name1>

    # Step 0: Specify components of input data structures
    indVars_DataStruct_Name1 = <DataStruct_Name1_Component1, DataStruct_Name1_Component2, ..., DataStruct_Name1_ComponentN>
    indVars_DataStruct_Name2 = <DataStruct_Name2_Component1, DataStruct_Name2_Component2, ..., DataStruct_Name2_ComponentN>
    indVars_DataStruct_NameN = <DataStruct_NameN_Component1, DataStruct_NameN_Component2, ..., DataStruct_NameN_ComponentN>

    # Step 1: Add Variables
    VARIABLE_1 = <float>
    VARIABLE_2 = <float>
    VARIABLE_N = <float>

    # Step 2: Add Pre-Expressions
    preExprs = [ \
          "<PreExpression_1>", \
          "<PreExpression_2>", \
          "<PreExpression_N>"
          ]

     # Step 3: Add expressions to compute output diagnostic
     exprs = [ \
          "<expression_1>", \
          "<expression_2>", \
          "<expression_N>"
          ]
  </Updater>

  # Time integration
  <Updater TimeIntegrationUpdater_Name (type=string)>
    kind=multiUpdater<NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n>
    out=<DataStruct_Name for t^n+1>

    <TimeIntegrator timeStepper>
      kind=rungeKutta<NDIM>d
      onGrid=<Grid_Name>
      scheme=<string>
    </TimeIntegrator>

    <UpdateStep boundaryStep>
      operation=boundary
      updaters=<string list of boundary conditions>
      syncVars=<DataStruct_Name at t^n>
    </UpdateStep>

    <UpdateStep integrationStep>
      operation=integrate
      updaters=<string list of integrators>
      syncVars=<DataStruct_Name for \nabla \cdot F(<DataStruct_Name for t^n>)>
    </UpdateStep>

    <UpdateSequence sequence>
      loop=[boundaryStep   integrationStep]
    </UpdateSequence>

  </Updater>

  <Updater CopierUpdater_Name (type=string)>
    kind=linearCombiner<NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n+1>
    out=<DataStruct_Name for t^n>
    coeffs=[1.0]
  </Updater>

  <UpdateStep startStep>
    updaters=[Initialization_Updater_Name BoundaryCondition_Name1 ... BoundaryCondition_NameN]
    syncVars=[<DataStruct_Name for t^n>]
  </UpdateStep>

  <UpdateStep restoreStep>
    updaters=<String List of Updaters>
    syncVars=<String List of DataStructs>
  </UpdateStep>

  <UpdateStep bcStep>
    updaters=[BoundaryCondition_Name1 ... BoundaryCondition_NameN]
    syncVars=[<DataStruct_Name for t^n>]
  </UpdateStep>

  <UpdateStep hyperStep>
    updaters=[FiniteVolume_Updater_Name (type=string)]
    syncVars=<String List of DataStructs>
  </UpdateStep>

  <UpdateStep writeStep>
    updaters=<String List of Updaters>
    syncVars=<String List of DataStructs>
  </UpdateStep>

  <UpdateSequence simulation>
    startOnly=[generateStep   startStep]
    restoreOnly=[generateStep   restoreStep   bcStep]
    writeOnly=[bcStep   writeStep]
    loop=[hyperStep]
  </UpdateSequence>

</Component>

Note that we have not filled in some of the entries in the UpdateSteps above. How to use these UpdateSteps is discussed in later tutorials.

Advanced Methods for Solving the MHD Equations in Multi-Dimensions

We can extend the approach above to solving the MHD equations in multi-dimensions in a straightforward fashion. The steps for Creating a Fluid Simulation is unchanged from Using USim to solve the Magnetohydrodynamic Equations, while the steps for Adding a Simulation Grid are identical to that given above. The first differences come in Initializing the Fluid, where the initial condition now has to specify the magnetic field, magnetic energy and the scalar potential:

<Updater setVar>
  kind=initialize2d
  onGrid=domain
  in=none
  out=[q   qNew]

  <Function initFunc>
    kind=exprFunc
    # adiabatic index
    gas_gamma=1.4
    # Upper fluid density
    rhoTop=2.0
    # Lower fluid density
    rhoBottom=1.0
    # Perturbation amplitude
    perturb=0.01
    # Location of upper y boundary
    ytop=0.75
    # acceleration due to gravity
    gravity=0.1
    # X-extent of domain
    lx=0.5
    # Y-extent of domain
    ly=1.5
    # permeability of free space
    mu0=1.0
    # ratio of gas to magnetic pressure
    beta=1000.0
    preExprs=[ p0=0.01   pert=0.01   pi=3.14159   rho=if(y<0.0,rhoBottom,rhoTop)   pr=(1.0/gas_gamma)-(gravity*rho*y)   vx=0.0   vy=(0.25*perturb)*(1.0+cos(2.0*pi*x/lx))*(1.0+cos(2.0*pi*y/ly))   vz=0.0   bx=sqrt(2.0*pr/(beta*beta))   by=0.0   bz=0.0   psi=0.0  ]
    exprs=[ rho   rho*vx   rho*vy   rho*vz   pr/(gas_gamma-1.0)+0.5*rho*(vy*vy)+0.5*((bx*bx)/mu0)   bx   by   bz   psi  ]
  </Function>

</Updater>

The finite volume scheme used to compute the spatial discretization is becomes:

<Updater hyper>
  kind=classicMuscl2d
  onGrid=domain
  in=[q]
  out=[qNew]
  cfl=0.4
  timeIntegrationScheme=none
  numericalFlux=hlldFlux
  limiter=[muscl]
  variableForm=primitive
  preservePositivity=0
  waveSpeeds=[maxWaveSpeed]

  <Equation idealMhd>
    kind=mhdDednerEqn
    gasGamma=1.4
    mu0=1.0
  </Equation>

  equations=[idealMhd]

  <Source gravity>
    kind=exprHyperSrc
    gravity=0.1
    rhoSrc=0.0
    mxSrc=0.0
    mzSrc=0.0
    bxSrc=0.0
    bySrc=0.0
    bzSrc=0.0
    psiSrc=0.0
    inpRange=[0  1  2  3  4  5  6  7  8]
    outRange=[0  1  2  3  4  5  6  7  8]
    indVars=[rho rhou rhov rhow Er Bx By Bz Psi]
    exprs=[ rhoSrc   mxSrc   -rho*gravity   mzSrc   -gravity*rhov   bxSrc   bySrc   bzSrc   psiSrc  ]
  </Source>

  sources=[gravity]
</Updater>

Compared to the exampled discussed in Advanced Methods for Solving the Magnetohydrodynamics Equations with USim, the differences follow the same pattern as discussed above; the switch of the dimension and the addition of the Source block. Note that for the MHD equations, we have to specify the source terms for the magnetic field and the potential, even though these are zero.

Note

The algorithms in USim automatically preserve \(\nabla \cdot B = 0\) (the solenoidal constraint on the magnetic field), so there is no need for the user to make changes to the algorithm at the input file level when running in multi-dimensions.

Finally, the wall boundary conditions become:

<Updater conductingWallBoundaryOnEntitytop>
  kind=mhdBc2d
  onGrid=domain
  in=[q]
  out=[q]
  model=mhdDednerEqn
  bcType=conductingWall
  entity=top
  gasGamma=1.4
</Updater>


<Updater conductingWallBoundaryOnEntitybottom>
  kind=mhdBc2d
  onGrid=domain
  in=[q]
  out=[q]
  model=mhdDednerEqn
  bcType=conductingWall
  entity=bottom
  gasGamma=1.4
</Updater>

The signficant difference compared to the hydrodynamic case discussed above, is that we have switch the kind attribute to mhdBc (1d, 2d, 3d). Valid bcType options for this attribute include conductingWall, noInflow and noSlip, while the options for the modelType attribute include mhdDednerEqn, gasDynamicMhdDednerEqn, simpleTwoTemperatureMhdDednerEqn and twoTemperatureMhdDednerEqn.

Advanced USim Simulation Structure for MHD in Multi-Dimensions

In earlier tutorials, we developed a simple pattern that could be used to design USim simulations using macros. This pattern can be repeated when we don’t use macros; however, we now need to add the grids, data structures, updaters, boundary conditions and time integration schemes by hand and then tell USim how to run them. A multi-dimensional approach for the MHD equations, include sources resembles the following:

# Specify parameters for the specific physics problem
$ PARAM_1 = <value>
$ PARAM_2 = <value>
$ PARAM_N = <value>

# Initialize a USim simulation
# Simulation start and end times
tStart = <float>
tEnd = <float>
# Number of data files to write
numFrames = <integer>
# Initial time-step to use
initDt = <float>
# Level of feedback to give user
verbosity = <info/debug>

<Component fluids>
  kind = updaterComponent

   # Setup the grid
  <Grid Grid_Name (type=string)>
    <grid parameters>
  </Grid>

   # Create data structures needed for the simulation
  <DataStruct DataStruct_Name1 (type=string)>
    kind = nodalArray
    onGrid = <Grid_Name>
    numComponents = <int>
  </DataStruct>

  <DataStruct DataStruct_Name2 (type=string)>
    kind = nodalArray
    onGrid = <Grid_Name>
    numComponents = <int>
  </DataStruct>

   <DataStruct DataStruct_NameN (type=string)>
    kind = nodalArray
    onGrid = <Grid_Name>
    numComponents = <int>
  </DataStruct>

  # Specify initial condition
  <Updater Initialization_Updater_Name (type=string)>
    kind = initialize<NDIM>d
    onGrid = <Grid_Name>
    out = <DataStruct_Name for t = 0>

    # initial condition to use
    <Function func>
      kind = exprFunc

      # Step 1: Add Variables
      VARIABLE_1 = <float>
      VARIABLE_2 = <float>
      VARIABLE_N = <float>


      # Step 2: Add Pre-Expressions
      preExprs = [ \
          "<PreExpression_1>", \
          "<PreExpression_2>", \
          "<PreExpression_N>"
          ]

     # Step 3: Add expressions specifying initial condition on density,
     # momentum, total energy
     addExpression(<expression>)

      exprs = ["<density_expression>",  \
                    "<xMomentum_expression>",  \
                    "<yMomentum_expression>",  \
                    "<zMomentum_expression>", \
                    "<totalEnergy_expression>", \
                    "<xMagneticField_expression>",  \
                    "<yMagneticField_expression>",  \
                    "<zMagneticField_expression>", \
                    "<scalarPotential_expression>"
                    ]

    </Function>

  </Updater>

  # Add the spatial discretization of the fluxes
  <Updater FiniteVolume_Updater_Name (type=string)>
    kind=classicMuscl<NDIM>d
    onGrid=<Grid_Name>

    # input data-structures
    in=<DataStruct_Name for t^n>
     # output data-structures
    out=<DataStruct_Name for \nabla \cdot F(<DataStruct_Name for t^n>)>
    # CFL number to use
    cfl=<float>
    # legacy time integration scheme, attribute; should be set to "none"
    timeIntegrationScheme=none
    # Riemann solver
    numericalFlux=<string>
     # Limiter to use
    limiter=[<string>]
    # Form of variable to limit
    variableForm=<string>
    # Whether to check variables for physical validitiy
    preservePositivity=<int>

    # Hyperbolic equation system
    <Equation idealMhd>
      kind=mhdDednerEqn
      # Adiabatic index
      gasGamma=<float>
      # Permeability
      mu0=<float>
    </Equation>

     # Hyperbolic equation to solve
    equations=[idealMhd]

  <Source Souce_Name_1>
    kind=<string>
    <Source_Name_1 params>
  </Source>

  ...

  <Source Souce_Name_N>
    kind=<string>
    <Source_Name_N params>
  </Source>

  sources=[Souce_Name_1, ..., Souce_Name_N]
  </Updater>

  # Boundary conditions
  <Updater BoundaryCondition_Name1 (type=string)>
    kind=<string><NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n>
    out=<DataStruct_Name for t^n>
    entity=<Entity_Name (type=string)>
    <Boundary Condition Parameters>
  </Updater>

  ...

  <Updater BoundaryCondition_NameN (type=string)>
    kind=<string><NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n>
    out=<DataStruct_Name for t^n>
    entity=<Entity_Name (type=string)>
    <Boundary Condition Parameters>
  </Updater>

  # Time integration
  <Updater TimeIntegrationUpdater_Name (type=string)>
    kind=multiUpdater<NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n>
    out=<DataStruct_Name for t^n+1>

    <TimeIntegrator timeStepper>
      kind=rungeKutta<NDIM>d
      onGrid=<Grid_Name>
      scheme=<string>
    </TimeIntegrator>

    <UpdateStep boundaryStep>
      operation=boundary
      updaters=<string list of boundary conditions>
      syncVars=<DataStruct_Name at t^n>
    </UpdateStep>

    <UpdateStep integrationStep>
      operation=integrate
      updaters=<string list of integrators>
      syncVars=<DataStruct_Name for \nabla \cdot F(<DataStruct_Name for t^n>)>
    </UpdateStep>

    <UpdateSequence sequence>
      loop=[boundaryStep   integrationStep]
    </UpdateSequence>

  </Updater>

  <Updater CopierUpdater_Name (type=string)>
    kind=linearCombiner<NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name for t^n+1>
    out=<DataStruct_Name for t^n>
    coeffs=[1.0]
  </Updater>

  # Output Diagnostics
  <Updater OutputDiagnosticUpdater_Name1 (type=string)>
    kind=<string><NDIM>d
    onGrid=<Grid_Name>
    in=<DataStruct_Name1, DataStruct_Name2, ..., DataStruct_NameN>
    out=<OutputDiagnosticDataStruct_Name1>

    # Step 0: Specify components of input data structures
    indVars_DataStruct_Name1 = <DataStruct_Name1_Component1, DataStruct_Name1_Component2, ..., DataStruct_Name1_ComponentN>
    indVars_DataStruct_Name2 = <DataStruct_Name2_Component1, DataStruct_Name2_Component2, ..., DataStruct_Name2_ComponentN>
    indVars_DataStruct_NameN = <DataStruct_NameN_Component1, DataStruct_NameN_Component2, ..., DataStruct_NameN_ComponentN>

    # Step 1: Add Variables
    VARIABLE_1 = <float>
    VARIABLE_2 = <float>
    VARIABLE_N = <float>

    # Step 2: Add Pre-Expressions
    preExprs = [ \
          "<PreExpression_1>", \
          "<PreExpression_2>", \
          "<PreExpression_N>"
          ]

     # Step 3: Add expressions to compute output diagnostic
     exprs = [ \
          "<expression_1>", \
          "<expression_2>", \
          "<expression_N>"
          ]
  </Updater>

  <UpdateStep startStep>
    updaters=[Initialization_Updater_Name BoundaryCondition_Name1 ... BoundaryCondition_NameN]
    syncVars=[<DataStruct_Name for t^n>]
  </UpdateStep>

  <UpdateStep restoreStep>
    updaters=<String List of Updaters>
    syncVars=<String List of DataStructs>
  </UpdateStep>

  <UpdateStep bcStep>
    updaters=[BoundaryCondition_Name1 ... BoundaryCondition_NameN]
    syncVars=[<DataStruct_Name for t^n>]
  </UpdateStep>

  <UpdateStep hyperStep>
    updaters=[FiniteVolume_Updater_Name (type=string)]
    syncVars=<String List of DataStructs>
  </UpdateStep>

  <UpdateStep writeStep>
    updaters=< OutputDiagnosticUpdater_Name1,  OutputDiagnosticUpdater_Name2,  OutputDiagnosticUpdater_NameN>
    syncVars=<OutputDiagnosticDataStruct_Name1, OutputDiagnosticDataStruct_Name2, ..., OutputDiagnosticDataStruct_NameN>
  </UpdateStep>

  <UpdateSequence simulation>
    startOnly=[generateStep   startStep]
    restoreOnly=[generateStep   restoreStep   bcStep]
    writeOnly=[bcStep   writeStep]
    loop=[hyperStep]
  </UpdateSequence>

</Component>

Note that we have not filled in some of the entries in the UpdateSteps above. How to use these UpdateSteps is discussed in later tutorials.