OpenFOAM: A Case of a Bad Pump

Here at Damogran Labs we do complicated things step by step, like this:

  • Step one. Buy an STM development board.
  • Step two. Write the simplest possible program that makes an LED blink.
  • Final step. Connect the most sophisticated Bluetooth Low Energy module and study 3000 pages of documentation to compile the most complicated software that can be uploaded to an STM board.

It may be obvious that the gap between the last two steps is a bit wide but with enough forward momentum it can be jumped over.

So when I needed a CFD simulation of a centrifugal pump and chose OpenFOAM as the only capable open-source choice that is exactly what I did.

  • First, install OpenFOAM.
  • Second, run a simple Pitz-Daily tutorial case.
  • Finally, assemble the ultimate multi-region moving-mesh transient case with the complicated geometry of a commercial centrifugal pump.

As a complete beginner with OpenFOAM I had quite a struggle with the final step. In the end I had to divide it into smaller steps to eliminate factors that made the simulation explode, one of them was simplifying geometry – I modeled something as simple as a windshield washer pump with 4 straight blades but with an impeller diameter of 0.6m. Now the whole setup works and it’s time to introduce additional complications such as complicated meshes and more advanced boundary/initial conditions. I decided to share this setup to make things easier to anyone struggling like me.

Preprocessing

3D STEP models are the shape of the water inside the pump. It is divided into three sections: suction, the pipe upstream of the impeller, the impeller itself and volute downstream of the impeller. This can be done in any 3D modelling app, including open-source stuff like FreeCAD.

STEP models are first processed in SALOME. Patches like inlet, outlet and walls are added to groups, then surface is meshed with Netgen 1D-2D algorithm and exported as separate STL files. Then these files are joined together with a few lines of Python in a single file. See salome-pre.hdf file and joinstl.py script.

Meshing

The meshing is done with cfMesh. In my opinion it is far better than snappyHexMesh, much simpler and faster*. I suggest trying it out. After meshing patch types are set and impeller ‘zone’ is set up – this part of the joined mesh will move.

Each part of the pump is meshed separately and then joined together in the ‘simulation’ folder. Coincident patches are coupled with AMI – patches and everything that AMI requires is done after meshes are merged.

The mesh is split into 4 domains. Note that a computer that has 4 physical cores and uses hyperthreading to produce a virtual 8-core processor will not solve this case any faster if you split the domain into 8 domains.

* After several years of using OpenFOAM, snappyHexMesh proves to be better for complex geometries and large meshes. It does take a lot of experience to get everything right. I still suggest trying out cfMesh – it might work better for you. Or it may not. 

Solving

There are two options for simulating a centrifugal pump: steady case with MRF and transient case with dynamic mesh. The first, Multiple Reference Frame does not move mesh but simulates rotation with a rotating coordinate sistem. Centrifugal and coriolis forces are added to all cells in impeller cellZone. This should greatly reduce computation time but unfortunately doesn’t work in every case. Of course in my case it didn’t for a number of reasons (geometry conditions, unsteadiness of the flow, …).

The second option is to solve the flow in the exact given moment and then do it all over again for the next moment. Where geometry changes between these two timesteps, change that as well, in this case, rotate the impeller cellZone. Because the initial conditions are unreal and the beginning acceleration of the impeller is impossibly high, the first few timesteps produce unrealistic results. After a while, those unphysical conditions should subside because if they don’t you’re in trouble and should have a look into mesh and solver options.

Mesh rotation is defined in constant/dynamicMeshDict and solver used is pimpleDyMFoam. Pay special attention to PIMPLE settings as they are set to nonsense values in tutorials. They seem to work for tutorial cases but PIMPLE isn’t meant to be run with settings like that.

Turbulence model of choice for centrifugal machines is k-Omega SST since it can handle all the different flows inside a pump (smaller pumps – lower Re and vice versa) and also non-optimal boundary layers of meshes.

Postprocessing

During the calculation you can pipe the solver output to a log and draw graphs to have a better overview of convergence.

The strange thing I noticed is that paraFoam a.k.a. ParaView that comes with OpenFOAM, cannot read decomposed cases directly and it takes ages to reconstruct each timestep. The separately installed ParaView can read decomposed cases directly. I haven’t put much effort in resolving this mistery, I just use paraview with decomposed cases.

If you want to calculate pump efficiency you need to know its torque. It is provided by the forces library. It outputs forces and torques on the specified patches for every timestep. What’s left to study is how to output power, torque and efficiency in a paraview animation.

One more thing that I haven’t figured out is how to plot streamlines for relative velocity inside the impeller. If you look closely at the impeller in the video you’ll see streamlines only start in the volute.

However, already at a first glance it is obvious that this pump is not the most optimized pump ever.  Uneven and unstead velocity distributions with large gradients, return flows etc… The head approximately matches the crude theoretical calculation so those results aren’t a complete nonsense.

Workflow explained

In conclusion, here is how my workflow goes:

  • Take a 3D model of a pump and fill it with water. Divide the ‘water’ models into suction, impeller and volute and save geometry as separate STEP files.
  • Import STEPs into SALOME. Create groups for inlets, outlets and walls for each of the models, then mesh their surface and create groups from geometry. Save each group as a separate STL file.
  • Create separate meshes for each model:
    cd cfmesh-suction
    ./makeMesh

    See what’s going on inside makeMesh and check dictionaries for applications that are used.

  • Join meshes and create all necessary for AMI. Again, check createPatch dictionary and topoSet dictionary in the impeller mesh directory.
    cd ../simulation
    ./combineMeshes
    ./decompose
    ./runPimple
  • In a separate terminal, you can plot residuals:
    gnuplot residuals -
  • Already during calculation you can check how it goes with paraview:
    touch case.foam
    paraview case.foam

Check controlDict for endTime and purgeWrite. Currently I run and when I’m happy with the results, stop the solver.

Here is the whole case without meshes and results (which by the way weight about 13 GB with current settings). I would also like to thank fellow FOAMers at CFD online forum and especially mr. Tobias Holzmann who already has a bunch of excellent tutorials on his website.

Hope this can be of any help to fresh FOAMers… good luck!