Volume Meshing: Hexahedral, Polyhedral, and OpenFOAM Import
using Rasmah
using LinearAlgebraOverview
A good volume mesh balances cell quality, boundary conformity, and element count. Rasmah generates structured hexahedral meshes (for boxes, swept solids, and mapped blocks), polyhedral cell complexes, and imports OpenFOAM decks for solver interoperation. Each is verified against its exact volume.
Structured and mapped hexahedral meshes
hexahedralize_box builds a structured grid; transfinite_hex maps a general (possibly curved) 8-corner block by tri-linear interpolation:
# a 20 × 10 × 30 mm block — the kind of pad a bracket mills out of
m = hexahedralize_box([0.0, 0.0, 0.0], [0.02, 0.01, 0.03]; nx=4, ny=2, nz=6)
println("box: ", size(m.elements, 2), " hexes, volume = ",
round(ustrip(uconvert(u"mm^3", volume(m))); digits = 0), " mm³")
# a mapped (curved-edge) block: the same 10 mm cube, corners pulled out of plane
corners = 0.01 .* [0.0 1 1 0 0 1 1 0; 0.0 0 1 1 0 0 1 1; 0.0 0 0 0 1 1 1 1]
b = transfinite_hex(corners; nx=3, ny=2, nz=4)
println("mapped block: volume = ", round(ustrip(uconvert(u"mm^3", volume(b))); digits = 0), " mm³ (10 mm cube)")box: 48 hexes, volume = 6000.0 mm³
mapped block: volume = 1000.0 mm³ (10 mm cube)
Polyhedral meshes
A PolyMesh is the general cell complex (vertices, faces, cells). A tet or hex mesh converts to it with volume conserved:
# a 100 mm mounting block, tet- and hex-meshed, converted with volume conserved
pm = tet_to_polymesh(tetrahedralize_box(0.1, 0.1, 0.1, 3, 2, 2))
println("tet → poly volume = ", round(ustrip(uconvert(u"mm^3", polyhedral_volume(pm))); digits = 0), " mm³")
ph = hex_to_polymesh(hexahedralize_box([0.0, 0.0, 0.0], [0.1, 0.1, 0.1]; nx=3, ny=2, nz=4))
println("hex → poly volume = ", round(ustrip(uconvert(u"mm^3", polyhedral_volume(ph))); digits = 0), " mm³")tet → poly volume = 1.0e6 mm³
hex → poly volume = 1.0e6 mm³
OpenFOAM import
An OpenFOAM polyMesh deck (points, faces, owner, neighbour) for a single tetrahedron reconstructs to a TetMesh:
# a 10 mm reference tet, the corner cell of a CFD block
pts = "4\n(\n(0 0 0)\n(0.01 0 0)\n(0 0.01 0)\n(0 0 0.01)\n)\n"
faces = "4\n(\n3(0 1 2)\n3(0 1 3)\n3(0 2 3)\n3(1 2 3)\n)\n"
owner = "4\n(\n0\n0\n0\n0\n)\n"; neighbour = "0\n(\n)\n"
m = read_openfoam_tetmesh(pts, faces, owner, neighbour)
println("OpenFOAM tet: ", size(m.elements, 2), " tet, ", size(m.nodes, 2), " nodes (10 mm tet, volume 166.7 mm³)")OpenFOAM tet: 1 tet, 4 nodes (10 mm tet, volume 166.7 mm³)
Next steps
- Adaptive refinement for local resolution.
- Turbulence closures for the solvers that run on these meshes.
