Hey there,
It’s Robin from CFD Engine & this is the third instalment in our “Three Tiny Tips” series.
No deep dives or tutorials, just three OpenFOAM tips that might come in handy. This time out we have two for the snappyHexMesh
ers & one of my favourite little scripts – RunFunctions
.
Let’s get ’em…
Refinement-Only Geometry in SHM
You can use geometry files (STL/OBJ etc) to define complex refinement zones in snappyHexMesh
without actually snapping to them.
You know this one already, but you might not have connected the dots.
You’re likely using primitives to describe refinement regions in your snappyHexMeshDict
(boxes, cylinders, spheres etc). But there’s nothing stopping you from creating something more exotic in CAD & using that to define your refinements.
Perhaps you’d like a curved refinement box? Or maybe you’d like to refine around the expected path of a vortex? No problem.
You could even extract data from an initial solution (streamlines, isosurfaces, isovolumes etc) and use them as the basis for your refinement regions. Go crazy.
Steps:
- Export your new OBJ/STL refinement region from CAD into
constant/triSurface
; - In your
snappyHexMeshDict
:- Reference the geometry file in the
geometry
section; - Don’t include it in the
refinementSurfaces
section (to avoid snapping to it); - Do include it in the
refinementRegions
section, setting your desired refinement levels & method;
- Reference the geometry file in the
Treat it the same way you would your usual primitives.
If it’s a closed (watertight) geometry you can specify the refinement level inside/outside or within certain distances of it.
If it’s an open surface you can only use the distance method so, close it if you can.
If you have an area of your mesh that’s hard (or wasteful) to cover with primitives then this is a great alternative – give it a go.
RunFunctions
If you’ve been digging in the tutorials, then you might’ve noticed that many of them make heavy use of runParallel
& runApplication
commands. I use them in all of my run scripts too – I’m a big fan.
But, if you’re not familiar with them, you use them like this:
runParallel simpleFoam
This command would run simpleFoam
in parallel (having grabbed the number of processors from decomposeParDict
), logging the output to log.simpleFoam
& returning you the command prompt. The equivalent of:
mpirun -n 18 simpleFoam -parallel < /dev/null > log.simpleFoam 2>&1
(Yeah, I don’t know what most of that end bit does either 🤷♂️ )
By default, if the log file exists, then the RunFunctions
will skip that step & move on. A potentially annoying feature (it’s actually really useful) but it can be changed.
There are a number of switches that change how the RunFunctions
interact with the log file & they’re pretty self-explanatory:
-a
or-append
-o
or-overwrite
-s
or-suffix
The last of which is handy if your run script uses a command multiple times, or you just like to keep things nice & tidy.
RunFunction
switches come before the OpenFOAM command & the OpenFOAM command switches come at the end, like this:
runApplication -s magU postProcess -func "mag(U)"
To use the functions in a run script, you need to add the following line near the top:
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions
You could also add it to your .bash_profile
& make them available on the command line (I don’t – but you could).
writeFlags
Developing a snappyHexMesh
recipe is an iterative process & there are a couple of flags that can make that process a little easier.
Adding the following line to the bottom of your snappyHexMeshDict
will prompt SHM to write out a handful of extra goodies:
writeFlags ( scalarLevels layerFields layerSets );
scalarLevels
writes out a cellLevel
field which you can use to colour cells (or faces) by their level in ParaView, something like this:
layerFields
writes out 3 new fields:
nSurfaceLayers
: the number of layers added to a surfacethickness
: the overall thickness of your layersthicknessFraction
: the thickness of your layers, relative to what you requested (i.e. how much they got squeezed)
Plot these on your surfaces to visualise your layer coverage & see where SHM is struggling – looks like the motorBike
needs a little work 🤔
layerSets
writes two new sets
to polyMesh/sets
:
addedCells
: the cells added during the layering stagelayerFaces
: I’ve no idea what these are 🤷♂️
These sets can be useful for mesh manipulation, like further refinement, or for separating them out into a cellZone
or a region
.
Bonus Tip: The fields (not the sets) are written to your latest time directory. If they’re in
0
(i.e. you’re using SHM with-overwrite
) make sure you have “Skip Zero Time” unchecked in ParaView when you read the case in.
Of the three flags I find that scalarLevels
are useful for tweaking off-body refinement zones & layerFields
are good for tweaking your layering recipe &/or changing your geometry to improve layer coverage.
I typically comment them out once I’m happy with the mesh (or I’ve given up trying to improve it).
Over to you
There you have it – three tiny tips – fancy refinement zones, nifty run functions & a way to visualise/debug your mesh levels & layers.
Did you know these already? Did I miss anything? Let me know, it helps shape future emails.
Likewise, if you come across something that I should share with the class, drop me a note & I’ll add it to the list.
Next week is the birthday issue 🎂
Until then, stay safe,