Breaking of a dam

3.1 Breaking of a dam

In this tutorial we shall solve a problem of simplified dam break in 2 dimensions using the interFoam.The feature of the problem is a transient flow of two fluids separated by a sharp interface, or free surface. The two-phase algorithm in interFoam 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 α  \relax \special {t4ht=, 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 t = 0 s  \relax \special {t4ht=, 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 Figure 3.1.

\relax \special {t4ht=

Figure 3.1: Geometry of the dam break.

3.1.1 Mesh generation

The user should go to the damBreak case in their $FOAM_RUN/tutorials/multiphase/interFoam/laminar directory. Generate the mesh running blockMesh as described previously. The damBreak mesh consist of 5 blocks; the blockMeshDict entries are given below.

17  convertToMeters 0.146;
19  vertices
20  (
21      (0 0 0)
22      (2 0 0)
23      (2.16438 0 0)
24      (4 0 0)
25      (0 0.32876 0)
26      (2 0.32876 0)
27      (2.16438 0.32876 0)
28      (4 0.32876 0)
29      (0 4 0)
30      (2 4 0)
31      (2.16438 4 0)
32      (4 4 0)
33      (0 0 0.1)
34      (2 0 0.1)
35      (2.16438 0 0.1)
36      (4 0 0.1)
37      (0 0.32876 0.1)
38      (2 0.32876 0.1)
39      (2.16438 0.32876 0.1)
40      (4 0.32876 0.1)
41      (0 4 0.1)
42      (2 4 0.1)
43      (2.16438 4 0.1)
44      (4 4 0.1)
45  );
47  blocks
48  (
49      hex (0 1 5 4 12 13 17 16) (23 8 1) simpleGrading (1 1 1)
50      hex (2 3 7 6 14 15 19 18) (19 8 1) simpleGrading (1 1 1)
51      hex (4 5 9 8 16 17 21 20) (23 42 1) simpleGrading (1 1 1)
52      hex (5 6 10 9 17 18 22 21) (4 42 1) simpleGrading (1 1 1)
53      hex (6 7 11 10 18 19 23 22) (19 42 1) simpleGrading (1 1 1)
54  );
56  edges
57  (
58  );
60  boundary
61  (
62      leftWall
63      {
64          type wall;
65          faces
66          (
67              (0 12 16 4)
68              (4 16 20 8)
69          );
70      }
71      rightWall
72      {
73          type wall;
74          faces
75          (
76              (7 19 15 3)
77              (11 23 19 7)
78          );
79      }
80      lowerWall
81      {
82          type wall;
83          faces
84          (
85              (0 1 13 12)
86              (1 5 17 13)
87              (5 6 18 17)
88              (2 14 18 6)
89              (2 3 15 14)
90          );
91      }
92      atmosphere
93      {
94          type patch;
95          faces
96          (
97              (8 20 21 9)
98              (9 21 22 10)
99              (10 22 23 11)
100          );
101      }
102  );
104  mergePatchPairs
105  (
106  );
108  // ************************************************************************* //

3.1.2 Boundary conditions

The user can examine the boundary geometry generated by blockMesh by viewing the boundary file in the 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. 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 interFoam solver includes modelling of surface tension at the contact point between the interface and wall surface. The models are applied by specifying the alphaContactAngle boundary condition on the alpha.water (α  \relax \special {t4ht=) field. With it, the user must specify the following: a static contact angle, theta0 θ0   \relax \special {t4ht=; leading and trailing edge dynamic contact angles, thetaA θA  \relax \special {t4ht= and thetaR θR  \relax \special {t4ht= 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, θ  = 90∘
 0 \relax \special {t4ht= and the velocity scaling function to 0. However, the simpler option which we shall choose here is to specify a zeroGradient type on alpha.water, 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 fixedFluxPressure 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.

3.1.3 Setting initial field

Unlike the previous cases, we shall now specify a non-uniform initial condition for the water phase fraction, α  \relax \special {t4ht=, where

       1  for the liquid phase
α =
       0  for the gas phase
\relax \special {t4ht=

This is achieved by running the setFields utility. It requires a setFieldsDict dictionary, located in the system directory, whose entries for this case are shown below.

18  defaultFieldValues
19  (
20      volScalarFieldValue alpha.water 0
21  );
23  regions
24  (
25      boxToCell
26      {
27          box (0 0 -1) (0.1461 0.292 1);
28          fieldValues
29          (
30              volScalarFieldValue alpha.water 1
31          );
32      }
33  );
36  // ************************************************************************* //

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

The setFields utility reads fields from file and, after re-calculating those fields, will write them back to file. Because the files are then overridden, it is recommended that a backup is made before setFields is executed. In the damBreak tutorial, the alpha.water field is initially stored as a backup only, named alpha.water.orig. Before running setFields, the user first needs to copy to alpha.water, e.g. by typing:

    cp 0/alpha.water.orig 0/alpha.water

The user should then execute setFields as any other utility is executed. Using paraFoam, check that the initial alpha.water field corresponds to the desired distribution as in Figure 3.2.

\relax \special {t4ht=

Figure 3.2: Initial conditions for phase fraction alpha.water.

3.1.4 Fluid properties

Let us examine the transportProperties file in the 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 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, 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 Table 3.1.

phase1 properties

Kinematic viscosity   2- 1
m  s   \relax \special {t4ht= nu         -6
1.0 × 10   \relax \special {t4ht=
Density kgm  -3   \relax \special {t4ht= rho 1.0 × 103   \relax \special {t4ht=
phase2 properties

Kinematic viscosity m2s- 1   \relax \special {t4ht= nu 1.48 × 10 -5   \relax \special {t4ht=
Density kgm  -3   \relax \special {t4ht= rho 1.0
Properties of both phases

Surface tension Nm - 1   \relax \special {t4ht= sigma 0.07

Table 3.1: Fluid properties for the damBreak tutorial

Gravitational acceleration is uniform across the domain and is specified in a file named g in the constant directory. Unlike a normal field file, e.g. U and p, g is a uniformDimensionedVectorField and so simply contains a set of dimensions and a value that represents (0,9.81,0) ms- 2   \relax \special {t4ht= for this tutorial:

18  dimensions      [0 1 -2 0 0 0 0];
19  value           (0 -9.81 0);
22  // ************************************************************************* //

3.1.5 Turbulence modelling

As in the cavity example, the choice of turbulence modelling method is selectable at run-time through the simulationType keyword in turbulenceProperties dictionary. In this example, we wish to run without turbulence modelling so we set laminar:

18  simulationType  laminar;
21  // ************************************************************************* //

3.1.6 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 Co  \relax \special {t4ht= than in standard fluid flow calculations. Ideally, we should not exceed an upper limit Co ≈  0.5  \relax \special {t4ht= 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 Co  \relax \special {t4ht= criterion. For more complex cases, this is considerably more difficult. interFoam therefore offers automatic adjustment of the time step as standard in the controlDict. The user should specify adjustTimeStep to be yes and the maximum Co  \relax \special {t4ht= for the phase fields, maxAlphaCo, and other fields, maxCo, to be 0.5. The upper limit on time step 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 OpenFOAM 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, OpenFOAM allows the user to specify that results are written at fixed times; in this case OpenFOAM 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 option for writeControl in the controlDict dictionary. The controlDict dictionary entries should be:

18  application     interFoam;
20  startFrom       startTime;
22  startTime       0;
24  stopAt          endTime;
26  endTime         1;
28  deltaT          0.001;
30  writeControl    adjustableRunTime;
32  writeInterval   0.05;
34  purgeWrite      0;
36  writeFormat     ascii;
38  writePrecision  6;
40  writeCompression uncompressed;
42  timeFormat      general;
44  timePrecision   6;
46  runTimeModifiable yes;
48  adjustTimeStep  yes;
50  maxCo           1;
51  maxAlphaCo      1;
53  maxDeltaT       1;
56  // ************************************************************************* //

3.1.7 Discretisation schemes

The interFoam 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 therefore not restricted to those that are strongly stable or bounded, e.g. upwind differencing.

The convection schemes settings are made in the divSchemes sub-dictionary of the fvSchemes dictionary. In this example, the convection term in the momentum equation (∇ ∙(ρUU  )  \relax \special {t4ht=), denoted by the div(rhoPhi,U) keyword, uses Gauss linearUpwind grad(U) to produce good accuracy. The ∇ ∙(U α )  \relax \special {t4ht= term, represented by the div(phi,alpha) keyword uses the vanLeer scheme. The ∇ ∙(U  α )
      rb  \relax \special {t4ht= term, represented by the div(phirb,alpha) keyword, can similarly use the vanLeer scheme, but generally produces smoother interfaces using the linear scheme.

The other discretised terms use commonly employed schemes so that the fvSchemes dictionary entries should therefore be:

18  ddtSchemes
19  {
20      default         Euler;
21  }
23  gradSchemes
24  {
25      default         Gauss linear;
26  }
28  divSchemes
29  {
30      div(rhoPhi,U)  Gauss linearUpwind grad(U);
31      div(phi,alpha)  Gauss vanLeer;
32      div(phirb,alpha) Gauss linear;
33      div(((rho*nuEff)*dev2(T(grad(U))))) Gauss linear;
34  }
36  laplacianSchemes
37  {
38      default         Gauss linear corrected;
39  }
41  interpolationSchemes
42  {
43      default         linear;
44  }
46  snGradSchemes
47  {
48      default         corrected;
49  }
52  // ************************************************************************* //

3.1.8 Linear-solver control

In the fvSolution, the PISO sub-dictionary contains elements that are specific to interFoam. There are the usual correctors to the momentum equation but also correctors to a PISO loop around the α  \relax \special {t4ht= phase equation. Of particular interest are the nAlphaSubCycles and cAlpha keywords. nAlphaSubCycles represents the number of sub-cycles within the α  \relax \special {t4ht= 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 α  \relax \special {t4ht= equation is solved in 2× \relax \special {t4ht= 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.

3.1.9 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 $FOAM_RUN/tutorials/multiphase/interFoam/laminar/damBreak/damBreak
    interFoam | tee log
The code will now be run interactively, with a copy of output stored in the log file.

3.1.10 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 alpha.water in time, e.g. see Figure 3.3.

\relax \special {t4ht=
(a) At t = 0.25 s  \relax \special {t4ht=.
\relax \special {t4ht=
(b) At t = 0.50 s  \relax \special {t4ht=.

Figure 3.3: Snapshots of liquid phase α  \relax \special {t4ht=.

3.1.11 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 OpenFOAM.

The user should first make a copy of the damBreak case, e.g. by

    cd $FOAM_RUN/tutorials/multiphase/interFoam/laminar/damBreak
    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 blockMeshDict dictionary to

        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 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 damBreak example, the user must re-initialise the phase field alpha.water in the 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 U and 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. it elements would have α  = 1  \relax \special {t4ht= or α =  0  \relax \special {t4ht=. Updating the field with mapFields may produce interpolated values 0 <  α < 1  \relax \special {t4ht= at the interface, so it is better to rerun the setFields utility. There is a backup copy of the initial uniform α  \relax \special {t4ht= field named 0/ that the user should copy to 0/alpha.water before running setFields:

    cd $FOAM_RUN/tutorials/multiphase/interFoam/laminar/damBreak/damBreakFine
    cp -r 0/ 0/alpha.water

The method of parallel computing used by OpenFOAM 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 decomposeParDict which is located in the 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 $FOAM_UTILITIES/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 x  \relax \special {t4ht=, y  \relax \special {t4ht= and z  \relax \special {t4ht= directions, the number of subdomains in each direction being given by the vector n  \relax \special {t4ht=. As this geometry is 2 dimensional, the 3rd direction, z  \relax \special {t4ht=, cannot be split, hence n
 z  \relax \special {t4ht= must equal 1. The n
 x  \relax \special {t4ht= and n
 y  \relax \special {t4ht= components of n  \relax \special {t4ht= split the domain in the x  \relax \special {t4ht= and y  \relax \special {t4ht= directions and must be specified so that the number of subdomains specified by nx  \relax \special {t4ht= and ny  \relax \special {t4ht= equals the specified numberOfSubdomains, i.e. nxny =  \relax \special {t4ht= 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 x  \relax \special {t4ht= and y  \relax \special {t4ht= 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 n = (2,2, 1)  \relax \special {t4ht=. When running decomposePar, we can see from the screen messages that the decomposition is distributed fairly even between the processors.

The user should consult User Guide section ?? 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 openMPI 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 interFoam -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 User Guide section ??. The case should run in the background and the user can follow its progress by monitoring the log file as usual.

\relax \special {t4ht=

Figure 3.4: Mesh of processor 2 in parallel processed case.

\relax \special {t4ht=
(a) At t = 0.25 s  \relax \special {t4ht=.
\relax \special {t4ht=
(b) At t = 0.50 s  \relax \special {t4ht=.

Figure 3.5: Snapshots of liquid phase α  \relax \special {t4ht= with refined mesh.

3.1.12 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 Figure 3.5. 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 paraFoam by

    paraFoam -case processor1
then processor1 will appear as a case module in ParaView. Figure 3.4 shows the mesh from processor 1 following the decomposition of the domain using the simple method.