=== Breaking of a dam
(((tutorials,breaking of a dam))) (((breaking of a dam)))
(((dam,breaking of a)))

In this tutorial we shall solve a problem of simplified dam break in 2
dimensions using the `inter` solver. The feature of the problem is a transient
flow of two fluids separated by a sharp interface, or free surface.
(((flow,free surface))) The two-phase algorithm in `inter` is based on the
volume of fluid (VOF) method in which a specie transport equation is used to
determine the relative volume fraction of the two phases, or phase fraction
math:[\alpha_1], in each computational cell. Physical properties are
calculated as weighted averages based on this fraction. The nature of the VOF
method means that an interface between the species is not explicitly computed,
but rather emerges as a property of the phase fraction field. Since the phase
fraction can have any value between 0 and 1, the interface is never sharply
defined, but occupies a volume around the region where a sharp interface should
exist.

The test setup consists of a column of water at rest located behind a membrane
on the left side of a tank. At time math:[t=\unit[0\]{s}], the membrane is
removed and the column of water collapses. During the collapse, the water
impacts an obstacle at the bottom of the tank and creates a complicated flow
structure, including several captured pockets of air. The geometry and the
initial setup is shown in <<fig_geomDamBreak>>.

==== Mesh generation

The user should go to the dirname:damBreak[] case in their
dirname:<tutorials>/multiphase/interFoam/laminar[] directory. Generate the
mesh running `blockMesh` as described previously. The dirname:damBreak[] mesh
consist of 5 blocks; the filename:blockMeshDict[] entries are given below.

[source,'']
------------------------------------------------------------------------------
include::{builddir}tutorials/assets/damBreak/blockMeshDict[]
------------------------------------------------------------------------------

[[fig_geomDamBreak]]
.Geometry of the dam break
image::images/tut_damBreak_geometry.{gfx-fmt}[scaledwidth="40%"]

==== Boundary conditions

The user can examine the boundary geometry generated by `blockMesh` by viewing
the boundary file in the dirname:constant/polyMesh[] directory. The file
contains a list of 5 boundary patches: `leftWall`, `rightWall`, `lowerWall`,
`atmosphere` and `defaultFaces`. The user should notice the `type` of the
patches. The atmosphere is a standard `patch`, 'i.e.' has no special
attributes, merely an entity on which boundary conditions can be specified. The
`defaultFaces` patch is `empty` since the patch normal is in the direction we
will not solve in this 2D case. The `leftWall`, `rightWall` and `lowerWall`
patches are each a `wall`.(((`wall`,boundary condition)))
(((boundary condition,`wall`))) Like the plain `patch`, the `wall` type
contains no geometric or topological information about the mesh and only
differs from the plain `patch` in that it identifies the patch as a wall,
should an application need to know, 'e.g.' to apply special wall surface
modelling.

A good example is that the `inter` solver includes modelling of surface tension
at the contact point between the interface and wall surface. The models are
applied by specifying the `alphaContactAngle`
(((`alphaContactAngle`,boundary condition)))
(((boundary condition,`alphaContactAngle`))) boundary condition on the `alpha1`
(math:[\alpha_1]) field. With it, the user must specify the following: a static
contact angle, `theta0` (math:[\theta_0]); leading and trailing edge dynamic
contact angles, `thetaA` (math:[\theta_A]) and `thetaR` (math:[\theta_R])
respectively; and a velocity scaling function for dynamic contact angle,
`uTheta`.

In this tutorial we would like to ignore surface tension effects between the
wall and interface. We can do this by setting the static contact angle,
math:[\theta_0 = 90^\circ] and the velocity scaling function to 0. However, the
simpler option which we shall choose here is to specify a `zeroGradient` type
on `alpha1`, rather than use the `alphaContactAngle` boundary condition.

The top boundary is free to the atmosphere so needs to permit both outflow and
inflow according to the internal flow. We therefore use a combination of
boundary conditions for pressure and velocity that does this while maintaining
stability. They are:

* `totalPressure` which is a `fixedValue` condition calculated from specified
  total pressure `p0` and local velocity `U`;
* `pressureInletOutletVelocity`, which applies `zeroGradient` on all
  components, except where there is inflow, in which case a `fixedValue`
  condition is applied to the tangential component;
* `inletOutlet`, which is a `zeroGradient` condition when flow outwards,
  `fixedValue` when flow is inwards.

At all wall boundaries, the `buoyantPressure` boundary condition is applied to
the pressure field, which calculates the normal gradient from the local density
gradient.

The `defaultFaces` patch representing the front and back planes of the 2D
problem, is, as usual, an `empty` type.

==== Setting initial field

Unlike the previous cases, we shall now specify a non-uniform initial condition
for the phase fraction math:[\alpha_1] where

[math]
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{align*}
  \alpha_1 =
     \begin{cases}
       1 & \text{for the liquid phase} \\
       0 & \text{for the gas phase}
     \end{cases}
\end{align*}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

This will be done by running the `setFields`(((`setFields` utility)))
(((utility,`setFields`))) utility. It requires a filename:setFieldsDict[]
dictionary, located in the dirname:system[] directory, whose entries for this
case are shown below.

[source,'']
-------------------------------------------------------------------------------
include::{builddir}tutorials/assets/damBreak/setFieldsDict[]
-------------------------------------------------------------------------------

The `defaultFieldValues` (((`defaultFieldValues` keyword)))
(((keyword,`defaultFieldValues`))) sets the default value of the fields, 'i.e.'
the value the field takes unless specified otherwise in the `regions`
(((`regions` keyword)))(((keyword,`regions`))) sub-dictionary. That
sub-dictionary contains a list of subdictionaries containing `fieldValues`
(((`fieldValues` keyword)))(((keyword,`fieldValues`))) that override the
defaults in a specified region. The region is expressed in terms of a
`topoSetSource` (((`topoSetSource` keyword)))(((keyword,`topoSetSource`))) that
creates a set of points, cells or faces based on some topological constraint.
Here, `boxToCell` (((`boxToCell` keyword)))(((keyword,`boxToCell`))) creates a
bounding box within a vector minimum and maximum to define the set of cells of
the liquid region. The phase fraction math:[\alpha_1] is defined as 1 in this
region.

The user should execute `setFields` (((`setFields` utility)))
(((utility,`setFields`))) as any other utility is executed. Using `para`, check
that the initial alpha1 field corresponds to the desired distribution as in
<<fig_phaseDamBreak00>>.

[[fig_phaseDamBreak00]]
.Initial conditions for phase fraction alpha1
image::images/tut_damBreak_initial.{gfx-fmt}[scaledwidth="40%"]

==== Fluid properties

Let us examine the filename:transportProperties[]
(((filename:transportProperties[] file)))
(((file,filename:transportProperties[]))) file in the dirname:constant[]
directory. Its dictionary contains the material properties for each fluid,
separated into two subdictionaries `phase1` and `phase2`. The transport model
for each phase is selected by the `transportModel` keyword. The user should
select `Newtonian` (((`Newtonian`,keyword entry)))
(((keyword entry,`Newtonian`))) in which case the kinematic viscosity is single
valued and specified under the keyword `nu`. The viscosity parameters for the
other models, 'e.g.' `CrossPowerLaw`,(((`CrossPowerLaw`,keyword entry)))
(((keyword entry,`CrossPowerLaw`))) are specified within subdictionaries with
the generic name `<model>Coeffs`, 'i.e.' `CrossPowerLawCoeffs` in this example.
The density is specified under the keyword `rho`.

The surface tension between the two phases is specified under the keyword
`sigma`. The values used in this tutorial are listed in
<<tab_fluidPropertiesDamBreak>>.

[[tab_fluidPropertiesDamBreak]]
.Fluid properties for the dirname:damBreak[] tutorial
[grid="none",frame="topbot",valign="middle",options="header",cols="2,1,1,2,2"]
|==============================================================================
| Property | Units | Keyword | Value `phase1` | Value `phase2`
| Kinematic viscosity | math:[\unitfrac{m^2}{s}]  | `nu` |
math:[1.0\times 10^{-6}] | math:[1.48\times 10^{-4}]
| Density | math:[\unitfrac{kg}{m^3}] | `rho` | math:[1.0\times 10^3] |
math:[1.0]
| Surface tension | math:[\unitfrac{N}{m}] | `sigma` 2+^| math:[0.07]
|==============================================================================

Gravitational acceleration is uniform across the domain and is specified in a
file named filename:g[](((filename:g[] file)))(((file,filename:g[]))) in the
dirname:constant[] directory. Unlike a normal field file, 'e.g.' filename:U[]
and filename:p[], filename:g[](((filename:g[] file)))(((file,filename:g[]))) is
a `uniformDimensionedVectorField` and so simply contains a set of dimensions
and a `value` that represents math:[\unitfrac[(0,9.81,0)\]{m}s^{-2}] for this
tutorial:

[source,'']
-------------------------------------------------------------------------------
include::{builddir}tutorials/assets/damBreak/g[]
-------------------------------------------------------------------------------

==== Turbulence modelling

As in the cavity example, the choice of turbulence modelling method is
selectable at runtime through the `simulationType`
(((`simulationType` keyword)))(((keyword,`simulationType`))) keyword in
filename:turbulenceProperties[]
(((filename:turbulenceProperties[],dictionary)))
(((dictionary,filename:turbulenceProperties[]))) dictionary. In this example,
we wish to run without turbulence modelling so we set `laminar`:

[source,'']
-------------------------------------------------------------------------------
include::{builddir}tutorials/assets/damBreak/turbulenceProperties[]
-------------------------------------------------------------------------------

==== Time step control

Time step control is an important issue in free surface tracking since the
surface-tracking algorithm is considerably more sensitive to the Courant number
math:[Co] than in standard fluid flow calculations. Ideally, we should not
exceed an upper limit math:[Co\approx 0.5] in the region of the
interface. In some cases, where the propagation velocity is easy to predict,
the user should specify a fixed time-step to satisfy the math:[Co] criterion.
For more complex cases, this is considerably more difficult. `inter` therefore
offers automatic adjustment of the time step as standard in the
filename:controlDict[]. The user should specify `adjustTimeStep`
(((`adjustTimeStep` keyword))) (((keyword,`adjustTimeStep`))) to be `on` and
the the maximum math:[Co] for the phase fields, `maxAlphaCo`,
(((`maxAlphaCo` keyword))) ((keyword,`maxAlphaCo`))) and other fields, `maxCo`,
(((`maxCo` keyword))) (((keyword,`maxCo`))) to be 0.5. The upper limit on time
step `maxDeltaT` (((`maxDeltaT` keyword)))(((keyword,`maxDeltaT`))) can be set
to a value that will not be exceeded in this simulation, 'e.g.' 1.0.

By using automatic time step control, the steps themselves are never rounded to
a convenient value. Consequently if we request that {project} saves results at
a fixed number of time step intervals, the times at which results are saved are
somewhat arbitrary. However even with automatic time step adjustment, {project}
allows the user to specify that results are written at fixed times; in this
case {project} forces the automatic time stepping procedure to adjust time
steps so that it `hits' on the exact times specified for write output. The user
selects this with the +adjustableRunTime+
(((+adjustableRunTime+,keyword entry)))(((keyword entry,+adjustableRunTime+)))
option for +writeControl+ (((+writeControl+ keyword)))
(((keyword,+writeControl+))) in the
filename:controlDict[](((filename:controlDict[],dictionary)))
(((dictionary,filename:controlDict[]))) dictionary. The filename:controlDict[]
dictionary entries should be:

[source,'']
-------------------------------------------------------------------------------
include::{builddir}tutorials/assets/damBreak/controlDict[]
-------------------------------------------------------------------------------

==== Discretisation schemes

The free surface treatment in {project} does not account for the effects of
turbulence. This is a consequence of the fact that the Reynolds averaged
approach to turbulence modelling does not match the notion of an
infinitesimally thin interface between air and water. As a consequence, all
free surface simulations can be viewed as a direct numerical simulation
(((direct numerical simulation))) (DNS)
of fluid flow. DNS is associated with certain requirements on the mesh size,
far beyond the mesh resolution of our test case.

This solver uses the multidimensional universal limiter for explicit solution
(MULES) method, created by OpenCFD, to maintain boundedness of the phase
fraction independent of underlying numerical scheme, mesh structure, 'etc.' The
choice of schemes for convection are therfore not restricted to those that are
strongly stable or bounded, 'e.g.' upwind differencing.
(((upwind differencing)))

The convection schemes settings are made in the `divSchemes` sub-dictionary of
the filename:fvSchemes[](((filename:fvSchemes[],dictionary)))
(((dictionary,filename:fvSchemes[]))) dictionary. In this example, the
convection term in the momentum equation
math:[\left(\nabla\cdot\left(\rho\U\U\right)\right)], denoted by the
`div(rho*phi,U)` keyword, uses `Gauss limitedLinearV 1.0` to produce good
accuracy. The limited linear schemes require a coefficient math:[\phi] as
described in <<sec_interpolationSchemes>>. Here, we have opted for best
stability with math:[\phi = 1.0]. The math:[\nabla\cdot\left(\U\alpha_1\right)]
term, represented by the `div(phi,alpha)` keyword uses the `vanLeer` scheme.
The math:[\nabla\cdot\left(U_{rb}\alpha_1\right)] term, represented by the
`div(phirb,alpha)` keyword, can similarly use the `vanLeer` scheme, but
generally produces smoother interfaces using the specialised
`interfaceCompression` scheme.

The other discretised terms use commonly employed schemes so that the
filename:fvSchemes[]((filename:fvSchemes[],dictionary)))
(((dictionary,filename:fvSchemes[]))) dictionary entries should therefore be:

[source,'']
-------------------------------------------------------------------------------
include::{builddir}tutorials/assets/damBreak/fvSchemes[]
-------------------------------------------------------------------------------

==== Linear-solver control

In the filename:fvSolution[], the `PISO` sub-dictionary contains elements that
are specific to `inter`. There are the usual correctors to the momentum
equation but also correctors to a PISO loop around the math:[\alpha_1] phase
equation. Of particular interest are the `nAlphaSubCycles`
(((`nAlphaSubCycles` keyword)))(((keyword,`nAlphaSubCycles`))) and `cAlpha`
(((`cAlpha` keyword)))(((keyword,`cAlpha`))) keywords. `nAlphaSubCycles`
(((`nAlphaSubCycles` keyword)))(((keyword,`nAlphaSubCycles`)))  represents the
number of sub-cycles within the math:[\alpha_1] equation; sub-cycles are
additional solutions to an equation within a given time step. It is used to
enable the solution to be stable without reducing the time step and vastly
increasing the solution time. Here we specify 2 sub-cycles, which means that
the math:[\alpha_1] equation is solved in math:[2\times] half length time steps
within each actual time step.

The `cAlpha` keyword is a factor that controls the compression of the interface
where: 0 corresponds to no compression; 1 corresponds to conservative
compression; and, anything larger than 1, relates to enhanced compression of
the interface. We generally recommend a value of 1.0 which is employed in this
example.

==== Running the code

Running of the code has been described in detail in previous tutorials. Try the
following, that uses tee, a command that enables output to be written to both
standard output and files:

-------------------------------------------------------------------------------
$ cd '<tutorials>'/multiphase/interFoam/laminar/damBreak
$ freefoam inter | tee log
-------------------------------------------------------------------------------

The code will now be run interactively, with a copy of output stored in the log
file.

==== Post-processing

Post-processing of the results can now be done in the usual way. The user can
monitor the development of the phase fraction `alpha1` in time;
<<fig_phaseDamBreak2550>>.

[[fig_phaseDamBreak2550]]
.Snapshot of phase &alpha;~1~ at t = 0.25 s
image::images/tut_damBreak_snapshot_1.{gfx-fmt}[scaledwidth="40%"]

.Snapshot of phase &alpha;~1~ at t = 0.50 s
image::images/tut_damBreak_snapshot_2.{gfx-fmt}[scaledwidth="40%"]

==== Running in parallel

The results from the previous example are generated using a fairly coarse mesh.
We now wish to increase the mesh resolution and re-run the case. The new case
will typically take a few hours to run with a single processor so, should the
user have access to multiple processors, we can demonstrate the parallel
processing capability of {project}. The user should first make a copy of the
dirname:damBreak[] case, 'e.g.' by

-------------------------------------------------------------------------------
$ cd '<tutorials>'/multiphase/interFoam/laminar
$ mkdir damBreakFine
$ cp -r damBreak/0 damBreakFine
$ cp -r damBreak/system damBreakFine
$ cp -r damBreak/constant damBreakFine
-------------------------------------------------------------------------------

Enter the new case directory and change the `blocks` description in the
filename:blockMeshDict[] dictionary to

[source,'']
-------------------------------------------------------------------------------
blocks
(
    hex (0 1 5 4 12 13 17 16) (46 10 1) simpleGrading (1 1 1)
    hex (2 3 7 6 14 15 19 18) (40 10 1) simpleGrading (1 1 1)
    hex (4 5 9 8 16 17 21 20) (46 76 1) simpleGrading (1 2 1)
    hex (5 6 10 9 17 18 22 21) (4 76 1) simpleGrading (1 2 1)
    hex (6 7 11 10 18 19 23 22) (40 76 1) simpleGrading (1 2 1)
);
-------------------------------------------------------------------------------

Here, the entry is presented as printed from the filename:blockMeshDict[] file;
in short the user must change the mesh densities, 'e.g.' the `46 10 1` entry,
and some of the mesh grading entries to `1 2 1`. Once the dictionary is
correct, generate the mesh.

As the mesh has now changed from the dirname:damBreak[] example, the user must
re-initialise the phase field `alpha1` in the dirname:0[] time directory since
it contains a number of elements that is inconsistent with the new mesh. Note
that there is no need to change the filename:U[] and filename:p_rgh[] fields
since they are specified as `uniform` which is independent of the number of
elements in the field. We wish to initialise the field with a sharp interface,
'i.e.' its elements would have math:[\alpha_1] = 1 or math:[\alpha_1] = 0.
Updating the field with `mapFields` may produce interpolated values
math:[0<\alpha_1<1] at the interface, so it is better to rerun the
`setFields` utility. There is a backup copy of the initial uniform
math:[\alpha_1] field named filename:0/alpha1.org[] that the user should copy
to filename:0/alpha1[] before running `setFields`:

-------------------------------------------------------------------------------
$ cd '<tutorials>'/multiphase/interFoam/laminar/damBreakFine
$ cp -r 0/alpha1.org 0/alpha1
$ setFields
-------------------------------------------------------------------------------

The method of parallel computing used by {project} is known as domain
decomposition, in which the geometry and associated fields are broken into
pieces and allocated to separate processors for solution. The first step
required to run a parallel case is therefore to decompose the domain using the
`decomposePar` utility. There is a dictionary associated with `decomposePar`
named filename:decomposeParDict[] which is located in the dirname:system[]
directory of the tutorial case; also, like with many utilities, a default
dictionary can be found in the directory of the source code of the specific
utility, 'i.e.' in
dirname:<source>/applications/utitlities/parallelProcessing/decomposePar[]
for this case.

The first entry is `numberOfSubdomains` which specifies the number of
subdomains into which the case will be decomposed, usually corresponding to the
number of processors available for the case.

In this tutorial, the `method` of decomposition should be `simple` and the
corresponding `simpleCoeffs` should be edited according to the following
criteria. The domain is split into pieces, or subdomains, in the :math:[x],
:math:[y] and :math:[z] directions, the number of subdomains in each direction
being given by the vector math:[\vec{n}]. As this geometry is 2 dimensional,
the 3rd direction, math:[z], cannot be split, hence math:[n_z] must equal 1.
The math:[n_x] and math:[n_y] components of math:[\vec{n}] split the domain in
the math:[x] and math:[y] directions and must be specified so that the number
of subdomains specified by math:[n_x] and math:[n_y] equals the specified
`numberOfSubdomains`, 'i.e.' math:[n_x n_y=] `numberOfSubdomains`. It is
beneficial to keep the number of cell faces adjoining the subdomains to a
minimum so, for a square geometry, it is best to keep the split between the
math:[x] and math:[y] directions should be fairly even. The `delta` keyword
should be set to 0.001.

For example, let us assume we wish to run on 4 processors. We would set
`numberOfSubdomains` to 4 and math:[\vec{n} = (2, 2, 1)]. When running
`decomposePar`, we can see from the screen messages that the decomposition is
distributed fairly even between the processors.

The user should consult <<sec_runningParallel>> for details of how to run a
case in parallel; in this tutorial we merely present an example of running in
parallel. We use the `Open MPI` implementation of the standard message-passing
interface (MPI). As a test here, the user can run in parallel on a single node,
the local host only, by typing:

-------------------------------------------------------------------------------
$ mpirun -np 4 freefoam inter -parallel > log &
-------------------------------------------------------------------------------

The user may run on more nodes over a network by creating a file that lists the
host names of the machines on which the case is to be run as described in
<<sec_runningCaseInParallelOpenmpi>>. The case should run in the background and
the user can follow its progress by monitoring the log file as usual.

[[fig_partMesh]]
.Mesh of processor 1 in parallel processed case
image::images/tut_damBreakFine_mesh_proc1.png[scaledwidth="40%"]

==== Post-processing a case run in parallel

Once the case has completed running, the decomposed fields and mesh must be
reassembled for post-processing using the `reconstructPar` utility. Simply
execute it from the command line. The results from the fine mesh are shown in
<<fig_phaseDamBreakFine2550>>. The user can see that the resolution of
interface has improved significantly compared to the coarse mesh.

The user may also post-process a segment of the decomposed domain individually
by simply treating the individual processor directory as a case in its own
right. For example if the user starts `para` by

-------------------------------------------------------------------------------
$ freefoam para -case processor1
-------------------------------------------------------------------------------

then guilabel:[processor1] will appear as a case module in 'ParaView'.
<<fig_partMesh>> shows the mesh from processor 1 following the
decomposition of the domain using the simple method.

[[fig_phaseDamBreakFine2550]]
.Snapshots of phase &alpha;~1~ with refined mesh at t = 0.25 s
image::images/tut_damBreakFine_snapshot_1.{gfx-fmt}[scaledwidth="40%"]

.Snapshots of phase &alpha;~1~ with refined mesh at t = 0.50 s
image::images/tut_damBreakFine_snapshot_2.{gfx-fmt}[scaledwidth="40%"]
