Wind tunnel case builder

This page produces the inputs that openfoam_case.py in KeithBlackstock/windtunnel writes into an OpenFOAM case: the duct domain, the background cell size, the snappyHexMesh refinement levels and the model placement that comes from model_transformations.json. Change a value here, copy the emitted code into the repository, run the generator.

Repository: github.com/KeithBlackstock/windtunnel branch: main generator: openfoam_case.py
Current state of the pipeline

What the repository runs today

  • Geometry export. export_stl() places the model with the saved 4×4 transform and writes case/constant/triSurface/model.stl. It warns if the mesh leaves the duct.
  • Dictionaries. write_dicts() emits controlDict, blockMeshDict, snappyHexMeshDict and meshQualityDict into case/system/.
  • Meshing. run_meshing() runs blockMesh -case case then snappyHexMesh -case case -overwrite, producing case/constant/polyMesh.
  • Dependencies that bite. find_location_in_mesh() calls mesh.contains() and trimesh.proximity.closest_point(). Both raise ModuleNotFoundError without rtree, and closest_point() also needs scipy for its KD-tree. numpy and trimesh alone are not enough.
  • locationInMesh is picked automatically: the fluid candidate with the largest clearance to wall and model surface, ties going upstream.
Requirements on the machine that runs it
Python3.9+ with numpy, trimesh, rtree, scipy
Placement gizmopyvista, only for --reposition
Previewplotly, only for preview_model_in_domain()
MeshingblockMesh, snappyHexMesh on PATH
DistributionOpenFOAM, or blueCFD-Core on Windows
Outputcase/ beside the script
01 / Parameters

Build a case

Values are millimetres. The generator rounds the domain to whole background cells, so pick a cell size that divides the duct exactly. Refinement level N gives a surface cell of background / 2N.

Duct and mesh
Emitted constants

        
model_transformations.json

      
Run sequence

      

--reposition reopens the placement gizmo (PyVista) and rewrites model_transformations.json; the first run opens it too, when no placement file exists yet. STL_PATH or --stl PATH supplies the model; without either the script asks for it, through a Tk dialog, or stdin when there is no terminal.

02 / Reference geometry

Shipped model.stl

The article in the repository as case/constant/triSurface/model.stl, drawn from its own triangles. Every figure below is read from that file.

model.stl
binary STL · 4,388 facets
Measured from model.stl
Facets4,388 triangles
File size219,484 bytes
Boundsx 32.79–67.38 · y 6.92–43.08 · z 21.89–26.95 mm
Placement+40.7481 / +26.9389 / +20.2105 mm
Duct100 × 50 × 50 mm
Background5 mm → 20 × 10 × 10 = 2,000 hex
Refinementlevel 2 to 3 → 1.25 to 0.625 mm surface cell
03 / Repository

What is in the tree

Files that drive the case
# meshing pipeline
openfoam_case.py        # exports STL, writes dicts, runs meshers
fluid_volume_model.py   # placement + STL loading
model_transformations.json  # saved 4x4 transform

# case
case/constant/triSurface/model.stl
case/system/{controlDict,blockMeshDict,snappyHexMeshDict,meshQualityDict}
Clone and first run
git clone https://github.com/KeithBlackstock/windtunnel.git
cd windtunnel
python -m pip install numpy trimesh rtree scipy
python openfoam_case.py --stl /path/to/model.stl
blockMesh -case case
snappyHexMesh -case case -overwrite