Dynamic mesh in OpenFOAM

features in OpenFOAM is dynamic meshing. But there is almost no documentation. Here we build an example.

Case setup

Our case is a 100×35 mm rectangle with the 8x25mm obstacle on the top side. This obstacle will move left to right leaving 5mm space to vertical walls.

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      blockMeshDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

convertToMeters 0.001;

vertices
(
    (0.0 0 0) (0.0 0 1)
    (5.0 0 0) (5.0 0 1)
    (13.0 0 0) (13.0 0 1)
    (100.0 0 0) (100.0 0 1)
    (0.0 10.0 0) (0.0 10.0 1)
    (5.0 10.0 0) (5.0 10.0 1)
    (13.0 10.0 0) (13.0 10.0 1)
    (100.0 10.0 0) (100.0 10.0 1)
    (0.0 35.0 0) (0.0 35.0 1)
    (5.0 35.0 0) (5.0 35.0 1)
    (13.0 35.0 0) (13.0 35.0 1)
    (100.0 35.0 0) (100.0 35.0 1)
);
blocks
(
    hex (0 2 10 8 1 3 11 9) (5 10 1) simpleGrading (1 1 1)
    hex (2 4 12 10 3 5 13 11) (8 10 1) simpleGrading (1 1 1)
    hex (4 6 14 12 5 7 15 13) (87 10 1) simpleGrading (1 1 1)
    hex (8 10 18 16 9 11 19 17) (5 25 1) simpleGrading (1 1 1)
    hex (12 14 22 20 13 15 23 21) (87 25 1) simpleGrading (1 1 1)
);

edges
(
);

patches
(
    wall walls
    (
        (0 2 3 1)
        (2 4 5 3)
        (4 6 7 5)
        (6 14 15 7)
        (14 22 23 15)
        (16 8 9 17)
        (8 0 1 9)
    )
    patch obstacle
    (
        (20 12 13 21)
        (12 10 11 13)
        (10 18 19 11)
    )
    patch atmosphere
    (
        (22 20 21 23)
        (18 16 17 19)
    )
);

// ************************************************************************* //

Initial water level is 20mm. This is achieved by system/setFieldsDict file

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    location    "system";
    object      setFieldsDict;
}

defaultFieldValues
(
    volScalarFieldValue alpha.water 0
    volVectorFieldValue U ( 0 0 0 )
);

regions
(
    boxToCell
    {
        box ( 0 0 0 ) ( 0.100 0.020 0.001 );
        fieldValues
        (
            volScalarFieldValue alpha.water 1
        );
    }
 );

Note: here is no convertToMeters parameter, so all dimensions are in meters.

Other options are taken from “Breaking of a dam” tutorial.

Dynamic meshing setup

To use dynamic meshing features, two files should be created. The first is constant/dynamicMeshDict. It specifies dynamic mesh type and it’s parameters. In our case, it looks like below:

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      dynamicMeshDict;

}
motionSolverLibs ( "libfvMotionSolvers.so"  );
dynamicFvMesh    movingConeTopoFvMesh;

movingConeTopoFvMeshCoeffs
{
  motionVelAmplitude (0.082 0 0); // 82mm/sec through X axis
  motionVelPeriod 1.5; // realy, its SEMIperiod

  leftEdge -0.020;
  leftObstacleEdge  0.030;
  rightObstacleEdge 0.050;

  left
  {
    minThickness 0.1e-3;
    maxThickness 0.2e-3;

  }

  right
  {
    minThickness 0.1e-3;
    maxThickness 0.2e-3;

  }
}

Here, we choose movingConeTopoFvMesh type with 82 mm/sec velocity amplitude and 1.5 sec semi-period (see details below).

The second file we should provide is constant/polyMesh/meshModifiers. It contains a list of mesh modifiers that will be applied to the mesh. Our file is:

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    location    "constant";
    object      meshModifiers;

}
2
(
  right
  {
    type layerAdditionRemoval;
    faceZoneName rightExtrusionFaces;
    minLayerThickness 0.0008;
    maxLayerThickness 0.0012;
    oldLayerThickness -1;
    active on;
  }
   left
   {
    type layerAdditionRemoval;
    faceZoneName leftExtrusionFaces;
    minLayerThickness 0.0008;
    maxLayerThickness 0.0012;
    oldLayerThickness -1;
    active on;
  }
);

Here we define two modifiers with layerAdditionRemoval type. It’s parameters will be discussed below.

moivingConeTopoFvMesh

This modifier moves all points between two face zones named leftExtrusionFaces and rightExtrusionFaces. Points are moved according the law \mathbf{dx} = \mathbf{v_0} \sin\left(\dfrac{\pi t}{T}\right) dt, where \mathbf{v_0} is velocity amplitude specified by motionVelAmplitude entry of movingConeTopoFvMeshCoeffs dictionary. Semi-period T corresponds to motionVelPeriod value.

Other dictionary entries are used only when moivingConeTopoFvMesh is run in “automatic” mode. It happens when neither face zones, not point zones, cell zones or mesh modifiers is specified. In this case, moivingConeTopoFvMesh creates two layerAdditionRemoval modifiers, that are controlled via left and right sub-dictionaries. It also creates two face zones, one for each modifier. The first zone is named leftExtrusionFaces and includes all faces with centre’s X coordinate equal to -7.0 mm. The second zone is named rightExtrusionFaces and includes all faces with centre’s X coordinate equal to -3.5 mm. Note this values are hard-coded. It seems like originally leftObstacleEdge and rightObstacleEdge parameters were planned to set these values, but somebody forgot to use them.

layerAdditionRemoval

This modifier adds or removes cell layers near the face zone specified.

When is applied by the dynamic mesh engine (like moivingConeTopoFvMesh), it first checks the thickness of all cells touching the face in the face zone. If an average thickness increased relative to previous call and maximum thickness is greater than maxLayerThickness, than layerAdditionRemoval adds new layer of cells. In the same manner, if an average thickness decreased relative to previous call and maximum thickness is less than maxLayerThickness, than layerAdditionRemoval removes one layer of cells.

Entry oldLayerThickness specifies value for the first comparison with average layer thickness, when there is no value from the previous step.

Special note should be done about cell selection. Internal faces has two cells – owner and neighbour. It’s normal points from the cell with a smaller ID to the cells with the larger one. Boundary faces, obviously, have only owner cell, and their normals point out the cell. It seems like for both internal and boundary faces an algorithm chooses cell that is pointed to by the face normal. It means that boundary face normals should always be inverted.

Face zones

Face zone is a list of faces with normal flipping specified. List of all face zones is specified in constant/polyMesh/faceZones file.

FoamFile                            
{                                   
    version     2.0;                
    format      ascii;              
    class       regIOobject;        
    location    "constant/polyMesh";
    object      faceZones;          
}                                   

2
(
rightExtrusionFaces
{
    type faceZone;
    faceLabels List 5 (1 3 5 7 9);
    flipMap    List  5 (0 1 1 0 0);
}
leftExtrusionFaces
{
    type faceZone;
    faceLabels List 3 (2 4 6);
    flipMap    List  3 (0 1 1);
}
)

Each list item contains it’s type faceZone, list of face labels and list of boolean values that indicate if face normal should be flipped (relative to it’s original direction) or no.

The last thing is important, because layerAdditionRemoval uses normal direction for cell selection.

Face zone creation

To produce face zones, use topoSet utility. Here is the file system/topoSetDict:

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      topoSetDict;

}

actions
(
{
        name    fLeft;
        type    faceSet;
        action  new;
        source  boxToFace;
        sourceInfo
        {
            box (0.0049 0 0) (0.0051 0.035 0.001);
        }
}
{
        name    fRight;
        type    faceSet;
        action  new;
        source  boxToFace;
        sourceInfo
        {
            box (0.0129 0 0) (0.0131 0.035 0.001);
        }
}
{
        name    cLeft;
        type    cellSet;
        action  new;
        source  boxToCell;
        sourceInfo
        {
            box (0.004 0 0) (0.005 0.035 0.001);
        }
}
{
        name    cRight;
        type    cellSet;
        action  new;
        source  boxToCell;
        sourceInfo
        {
            box (0.013 0 0) (0.014 0.035 0.001);
        }
}
{
        name    rightExtrusionFaces;
        type    faceZoneSet;
        action  new;
        source  setsToFaceZone;
        sourceInfo
        {
            faceSet fRight;
            cellSet cRight;
            flip    true;
        }
}
{
        name    leftExtrusionFaces;
        type    faceZoneSet;
        action  new;
        source  setsToFaceZone;
        sourceInfo
        {
            faceSet fLeft;
            cellSet cLeft;
            flip    true;
        }
}
);

Face sets

In this file, we first create two face sets fLeft and fRight with all faces with X coordinate equal to 5mm and 13mm. Note that not only obstacle’s faces are used, but internal faces below them too.

cells

Next, we define cell (yes, cell) sets cLeft and cRight. The first set includes cells left to the fLeft faces. In other words, it is the rightmost cell column before the obstacle. In the same manner, the second cell set is the leftmost column after the obstacle.

The last step is a face zones definition. It is done by setsToFaceZone source. As appears from it’s name, it creates face zone from the face set. But it has additional parameter cellSet. It seems like it refers to the cell set that face normals should point out from. More precisely, according to the setsToFaceZone.C:148, topoSet inverts normal if

  • it is boundary face and it’s owner is not in cellSet
  • it is internal face and it’s neighbour (but not owner) is in cellSet

The flip flag inverts the result of these rules.

normals

These steps produce exactly what we need. Really, by default, all faces from fLeft have normals directed to the right (see fig.). All they also belongs to cells from cLeft. It means they aren’t touched by the rules above, but are then flipped by the flip flag.

As for faces from fRight set, they splits into two parts.

The first one is the obstacle’s right side (i.e. top part of fRight). It’s faces by default look to the left (as a boundary faces, that look outside domain). They also have owners from cRight cell set. As a result, they will be flipped only by the flip flag.

The second part of fRight includes faces below the obstacle right side. Initially, their normals look to the right. Note these faces have no owners in cRight cell set, but have neighbours. It implies that they will be flipped twice: by the second rule from the list above, and by the flip flag. So, they will remain the same as default, i.e. pointing to the right.

zones

Resulting face zones are shown on the the figure right. Note that all normals look into cells that will be affected by the layer modifiers.

 

Case file

Here is a case archive: https://yadi.sk/d/LYyzg8HukjLqo

27 thoughts on “Dynamic mesh in OpenFOAM

  1. Congratulation for your tutorial! I am a newbie in openFoam and i’m having some troubles implementing these files in “Breaking of a dam” tutorial.
    Which version of OpenFoam have you used?

    Like

  2. Hi, Thanks a lot for this very instructive tutorial. I had a question. Do you think if this technique works also in a case where we have two moving objects moving toward each other and passing each other? Imagine the case you described with an additional moving obstacle attached to the lower wall moving in the opposite direction. Thanks in advance.

    Like

  3. Hi. Thanks for sharing this tutorial. It really helps. However, my simulation runs but the water phase was not affected by the obstacles. I must have wrong setup with the boundary conditions. Would you mind to share me the full case? Thank you.

    Like

    1. Hi Wang,
      I also tried this but my case didn’t work. could you please share your case with me so can check where is the problem in my case?

      Like

      1. Yes. Would you mind leave me your email address so that I can share you my case? However, as I posted, the result is not correct since the moving of obstacle has no effects on the water phase. If once your case works, please let me know.

        Like

    1. Thank you so much for sharing the files. Just by first looking at the animations, I noticed the volume of the water decreases. Do you know the reason? But I just have a quick scan of it. I will learn it carefully later. Thanks again.

      Like

      1. It looks I’ve set wrong boundary conditions on water velocity on moving faces. Now that just (0 0 0), but probably it should be movingWallVelocity.

        Like

  4. you have created this tutorial in extended version, i am using openfoam4, when i re-run this simulation by ./Allrun then i am facing same problem. obstacle is moving but water is not disturbing. could your please help me to understand this tutorial. it will be very helpful to start understanding of dynamic mesh.

    Like

  5. #!/bin/bash

    cp -r -f ./0.org/* ./0

    find ./constant/polyMesh -type f -not -name ‘*m4’ | xargs rm

    find ./ -type d -name ‘0.0*’ | xargs rm -r
    rm -r 5e-05

    m4 constant/polyMesh/blockMeshDict.m4 >constant/polyMesh/blockMeshDict

    blockMesh >blockMesh.log

    #changeDictionary -literalRE >changeDictionary.log

    topoSet >topoSet.log

    setFields >setFields.log

    cp constant/meshModifiers constant/polyMesh

    if [ “$1” == “norun” ]; then
    echo “Skip calculations”
    else
    interDyMFoam >interDyMFoam.log
    fi

    i also tried blockMesh ==> topoSet ==> setFields ==>> interDyMFoam

    Like

    1. This is shell script. But I mean what it prints. You wrote you had troubles. That mean you got some errors, right?
      Just to check: are you on Linux/MAC?

      Like

  6. I am using Linux. Simulation is running smoothly but the problem is in the result. the obstacle is moving but it is not disturbing the liquid.

    Like

  7. Hi, there, I think I made the model work with OpenFOAM4. I created the case exactly by following Ilya’s instruction and also changed the velocity boundary condition of obstacle as movingWallVelocity as Ilya suggested. My simulation result seems to be correct. However, the disturbance from the obstacle of my simulation is not as much as that of Ilya’s. The difference may be caused by turbulence model since I applied k-epsilon model, or others. BTW, I haven’t make it work on OpenFOAM4-extend yet.
    Ilya,
    Thank you so much for sharing your simulation. You instruction of creating the model is perfect. It helps me a lot. If you would like to see the model I created with OpenFOAM4, please leave me an email so that I can share with you. We can discuss if the result is correct or not. Thanks again.
    Saurav,
    As the case I shared with you, if you can just change the velocity boundary condition of obstacle as Ilya suggested, it should work.

    Like

      1. Hi, change the boundary condition like this
        type movingWallVelocity;
        value uniform (0 0 0);
        It should work.

        Like

  8. Hi, when i change U boundary condition of obstacle to movingWallVelocity.

    –> FOAM FATAL IO ERROR:
    keyword value is undefined in dictionary “/home/saurav/OpenFOAM/saurav-4.1/run/Dym/tutorial1/0/U.boundaryField.obstacle”

    file: /home/saurav/OpenFOAM/saurav-4.1/run/Dym/tutorial1/0/U.boundaryField.obstacle from line 31 to line 31.

    From function const Foam::entry& Foam::dictionary::lookupEntry(const Foam::word&, bool, bool) const
    in file db/dictionary/dictionary.C at line 441.
    

    FOAM exiting

    Like

  9. Thank you so much, it is working.
    I have some doubt in DynamicMeshDic

    leftEdge -0.020;
    leftObstacleEdge 0.030;
    rightObstacleEdge 0.050;

    what is the function of this value?

    and

    oldLayerThickness -1;

    what it means please explain

    Like

      1. i want to use this dynamic mesh method for my graduation project.my tutor said that i must find a experiment case,so that prove this method valid.

        Like

Leave a comment