Python classes for easier creation of OpenFOAM's blockMesh dictionaries.
Python classes for easier creation of OpenFOAM's blockMesh dictionaries.
Warning! This project is currently under development and is not yet very user-friendly. It still lacks some important features and probably features a lot of bugs. However, you're welcome to suggest features, improvements, and point out bugs.
Note: version 1.0.0 introduced backwards-incompatible changes in API. Use latest 0.0.1 commit for your old scripts. However, you might want to give the new version a try...
blockMesh is a very powerful mesher but the amount of manual labour it requires to make even the simplest
meshes makes it mostly useless. Even attempts to simplify or parametrize blockMeshDicts with #calc
or even
the dreadful m4
quickly become unmanageable and cryptic.
classy_blocks' aim is to minimize the amount of meticulous work by providing a more intuitive workflow, off-the-shelf parts and some automatic helpers for building and optimization of block-structured hexahedral meshes. Still it is not an automatic mesher and therefore some kinds of geometry are more suited than others.
Check out the classy_blocks tutorial on damogranlabs.com!
pip install classy_blocks
pip install git+https://github.com/damogranlabs/classy_blocks.git@development
As opposed to blockMesh, where the user is expected to manually enter pre-calculated vertices, edges, blocks and whatnot, classy_blocks tries to mimic procedural modeling of modern 3D CAD programs. Here, a Python script contains steps that describe geometry of blocks, their cell count, grading, patches and so on. At the end, the procedure is translated directly to blockMeshDict and no manual editing of the latter should be required.
Unchecked items are not implemented yet but are on a TODO list
After blocks have been placed, it is possible to create new geometry based on placed blocks or to modify them.
How to run:
classy_blocks
as described abovecd
to directory of the chosen examplepython <example.py>
; that will write blockMeshDict to examples/caseblockMesh
on the caseexamples/case/case.foam
in ParaView to view the resultFor instance:
cd examples/chaining
python tank.py
blockMesh -case ../case
A simple Cylinder:
inlet = cb.Cylinder([x_start, 0, 0], [x_end, 0, 0], [0, 0, radius])
inlet.chop_radial(count=n_cells_radial, end_size=boundary_layer_thickness)
inlet.chop_axial(start_size=axial_cell_size, end_size=2*axial_cell_size)
inlet.chop_tangential(count=n_cells_tangential)
inlet.set_start_patch('inlet')
inlet.set_outer_patch('wall')
inlet.set_end_patch('outlet')
mesh.add(inlet)
See
examples/shape
for use of each shape andexamples/complex
for a more real-life example usage of shapes.
Analogous to a sketch in 3D CAD software, a Face is a set of 4 vertices and 4 edges. An Operation is a 3D shape obtained by swiping a Face into 3rd dimension by a specified rule; an example of Revolve:
# a quadrangle with one curved side
base = cb.Face(
[ # quad vertices
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0]
],
[ # edges: None specifies straight edge
cb.Arc([0.5, -0.2, 0]),
None,
None,
None
]
)
revolve = cb.Revolve(
base, # face to revolve
f.deg2rad(45), # revolve angle
[0, -1, 0], # axis
[-2, 0, 0] # origin
)
revolve.chop(0, count=15) # first edge
revolve.chop(1, count=15) # second edge
revolve.chop(2, start_size=0.05) # revolve direction
mesh.add(revolve)
See
examples/operations
for an example of each operation.
Any geometry that snappyHexMesh understands is also supported by blockMesh. That includes searchable surfaces such as spheres and cylinders and triangulated surfaces.
Projecting a block side to a geometry is straightforward; edges, however, can be projected to a single geometry (will 'snap' to the closest point) or to an intersection of two surfaces, which will define it exactly.
Geometry is specified as a simple dictionary of strings and is thrown in blockMeshDict exactly as provided by the user.
geometry = {
'terrain': [
'type triSurfaceMesh',
'name terrain',
'file "terrain.stl"',
],
'left_wall': [
'type searchablePlane',
'planeType pointAndNormal',
'point (-1 0 0)',
'normal (1 0 0)',
]
}
box = cb.Box([-1., -1., -1.], [1., 1., 1.])
box.project_side('bottom', 'terrain')
box.project_edge(0, 1, 'terrain')
box.project_edge(3, 0, ['terrain', 'left_wall'])
Simply provide names of patches to be merged and call mesh.merge_patches(<master>, <slave>)
.
classy_blocks will take care of point duplication and whatnot.
box = cb.Box([-0.5, -0.5, 0], [0.5, 0.5, 1])
for i in range(3):
box.chop(i, count=25)
box.set_patch('top', 'box_top')
mesh.add(box)
cylinder = cb.Cylinder(
[0, 0, 1],
[0, 0, 2],
[0.25, 0, 1]
)
cylinder.chop_axial(count=10)
cylinder.chop_radial(count=10)
cylinder.chop_tangential(count=20)
cylinder.set_bottom_patch('cylinder_bottom')
mesh.add(cylinder)
mesh.merge_patches('box_top', 'cylinder_bottom')
Useful for Shapes, mostly for piping and rotational geometry; An existing Shape's start or end sketch can be reused as a
starting sketch for a new Shape, as long as they are compatible.
For instance, an Elbow
can be chained to a Cylinder
just like joining pipes in plumbing.
Moreover, most shapes* can be expanded to form a wall version of the same shape. For instance, expanding a Cylinder
creates an ExtrudedRing
.
See
examples/chaining
for an example of each operation.
It is possible to create new blocks by offsetting existing blocks' faces.
As an example, a sphere can be created by offsetting all six faces of a simple box,
then projected to a searchableSphere
.
See
examples/shapes/shell.py
for the sphere tutorial.
Once an approximate blocking is established, one can fetch specific vertices and specifies certain degrees of freedom along which those vertices will be moved to get blocks of better quality.
Block is treated as a single cell for which OpenFOAM's cell quality criteria are calculated and optimized per user's instructions.
Vertices can move freely (3 degrees of freedom), along a specified line/curve (1 DoF) or surface (2 DoF).
# [...] A simple setup with two cylinders of different radii,
# connected by a short conical frustum that has bad cells
# [...]
mesh.assemble()
# Find inside vertices at connecting frustum
finder = cb.RoundSolidFinder(mesh, frustum)
inner_vertices = finder.find_core(True).union(finder.find_core(False))
optimizer = cb.Optimizer(mesh)
# Move chosen vertices along a line, parallel to x-axis
for vertex in inner_vertices:
clamp = cb.LineClamp(vertex, vertex.position, vertex.position + f.vector(1, 0, 0))
optimizer.release_vertex(clamp)
optimizer.optimize()
mesh.write(os.path.join("..", "case", "system", "blockMeshDict"), debug_path="debug.vtk")
The result (basic blocking > optimized):
See
examples/optimization
for the diffuser example.
By default, a debug.vtk
file is created where each block represents a hexahedral cell.
By showing block_ids
with a proper color scale the blocking can be visualized.
This is useful when blockMesh fails with errors reporting invalid/inside-out blocks but VTK will
happily show anything.
These are some screenshots of parametric models, built with classy_blocks.
Rectangular ducts (Extrude and Revolve Operations)
3D pipes with twists and turns (chained Elbow and Cylinder Shapes)
A simple tank with rounded edges
A flywheel in a case. VTK Blocking output for debug is shown in the middle
Venturi tube
2D mesh for studying Karman Vortex Street
Helmholtz nozzle, a resonator with sharp edges. See this sketch.
Edges and faces, projected to an STL surface
Mesh for studying flow around a sphere, with projected edges and faces
Airfoil core with blunt trailing edge (imported points from NACA generator) and adjustable angle of attack. Exact blocking is determined by in-situ optimization
(see examples/complex/airfoil.py
). A simulation-ready mesh needs additional blocks to expand domain further away from the airfoil.
A parametric, Low-Re mesh of a real-life impeller (not included in examples)
A complex example: parametric, Low-Re mesh of a cyclone
Package (python) dependencies can be found in pyproject.toml file. Other dependencies that must be installed:
There's no official documentation yet so here are some tips for easier navigation through source.