How These Simulations Were Built

This page documents the CFD process from first principles: how the mesh was designed, why each solver and case was chosen, and how the results were validated against classical literature. The complete code is in the Code Notebook.

All simulations use a 2D circular cylinder (diameter 0.6 m) as a proxy for a soccer ball. The cylinder is the standard academic starting point for external aerodynamics: it has all the same physics as a sphere (separation, vortex shedding, Magnus effect) but runs orders of magnitude faster and has a century of literature to validate against.

Two-Solver Pipeline

No single CFD tool is optimal for every stage of the workflow. A tiered approach addresses each stage:

Layer Tool Fidelity Purpose
Rapid prototyping ΦFlow (2D) Laminar, no turbulence model 100+ parametric sweeps, identify interesting regimes
High-fidelity validation SU2 (2D / 3D) RANS SST $k$-$\omega$, unstructured mesh Accurate $C_d$, $C_l$, boundary layer profiles
Visualization PyVista N/A Pressure/velocity contours, streamlines, comparison renders
Engineering workflow: ΦFlow runs fast parametric sweeps to identify interesting cases. Those cases are exported to SU2 for high-fidelity verification: exactly how industrial CFD teams operate.

ΦFlow: Rapid Prototyping

ΦFlow is an open-source CFD framework developed at TU Munich, built on JAX for GPU-accelerated, differentiable simulation.

What It Solves

Incompressible Navier-Stokes on a staggered MAC grid (velocity stored on cell faces, pressure at cell centers). The solution algorithm per timestep:

  1. Semi-Lagrangian advection: trace each particle path backward through the velocity field to update momentum
  2. CG pressure solve: conjugate gradient iteration to enforce $\nabla \cdot \mathbf{u} = 0$
  3. Obstacle update: enforce no-slip condition on cylinder surface (including rotation for Magnus)

Simulation Conditions

Parameter Value Notes
Grid resolution 256 × 128 Staggered MAC grid; uniform spacing
Domain size 8.0 × 4.0 m Length-to-diameter ratio ~13.3
Cylinder radius 0.3 m Diameter 0.6 m (standard size 5 ball)
Inlet velocity 1.0 m/s Uniform inflow from left boundary
Reynolds number $Re \approx 4 \times 10^4$ Based on $U = 1.0$ m/s, $D = 0.6$ m, $\nu = 1.5 \times 10^{-5}$ m²/s
Boundary conditions Uniform inlet (left), free-slip top/bottom, outflow (right), no-slip cylinder Cylinder surface: rotating wall for Magnus ($\omega = 10$ rad/s), stationary for knuckleball
Spin parameter $S = \omega R / U = 3.0$ Exaggerated vs real soccer ($S \approx 0.2$) for visual clarity of the Magnus effect
Turbulence model None (laminar) Flow remains laminar at this Re on a 2D grid; no RANS model
Simulation time ~30 s (2000 steps, $\Delta t \approx 0.015$ s) Several vortex shedding cycles captured

Full implementation code is in the Code Notebook (section 1).

Limitations

Why 2D laminar is a simplification: Real cylinder flow at $Re = 4 \times 10^4$ is three-dimensional and transitional: the boundary layer would naturally transition to turbulent in certain regions. A 2D laminar simulation cannot capture this, so it overpredicts $C_d$ (~1.2 vs experimental ~1.0). This is a known bias, not a bug. ΦFlow is used for qualitative flow structure, not absolute force prediction.

SU2: High-Fidelity Validation Layer

SU2 is an open-source finite-volume RANS solver developed at Stanford University. SU2 provides production-grade aerodynamic coefficients whose results are more reliable than ΦFlow’s laminar approximation.

Numerical Method

SU2 uses a finite-volume discretization on unstructured meshes (triangles in 2D, tetrahedra in 3D). The flow variables are stored at cell centers; fluxes are computed at cell faces using an approximate Riemann solver. For incompressible flows, an artificial compressibility method couples pressure and velocity.

Turbulence Modeling: RANS SST $k$-$\omega$

Reynolds-Averaged Navier-Stokes decomposes the flow into mean ($\bar{\mathbf{u}}$) and fluctuating ($\mathbf{u}'$) components. The Reynolds stresses $\overline{u'_i u'_j}$ are modelled by the SST $k$-$\omega$ (Shear Stress Transport) model, which blends:

Why SST $k$-$\omega$ for this project: It is the industry standard for external aerodynamics with separation. It handles adverse pressure gradients (the cylinder’s aft surface) better than $k$-$\varepsilon$, and converges more robustly than transition models on coarse meshes. The trade-off: as a fully turbulent model, it cannot predict laminar-to-turbulent transition: essential physics for the drag crisis.

Mesh Generation

All meshes are generated programmatically using gmsh’s Python API. The gmsh code is in the Code Notebook (section 3).

The mesh is designed around two competing requirements: resolving the thin boundary layer at the cylinder surface while keeping the total cell count manageable for fast turnaround. Near the cylinder wall, element size drops to $c_l = 0.004$ (distance-based refinement via a Threshold field), producing a fine ring of small triangles that capture the steep velocity gradients in the viscous sublayer and buffer layer. Away from the cylinder, cell size grows smoothly to $c_l = 0.5$ at the farfield boundary, where the flow is nearly uniform and fine resolution would is not necessary.

A rectangular wake box (extending 6 cylinder diameters downstream, 1 diameter wide) applies a secondary refinement of $c_l = 0.02$ inside its volume. This box is critical because vortex shedding produces alternating recirculation zones that travel downstream: if these cells were too coarse, numerical dissipation would damp the vortices before they develop, biasing the shedding frequency and amplitude. The box is narrower than wide because the wake stays roughly horizontal in 2D cylinder flow; expanding vertically would add unnecessary cells. The Distance and Box fields are combined via a Minimum (Min) field, so the local element size is the smaller of the two at any point: fine at the wall OR inside the wake box, whichever is stricter.

160,770
Nodes (2D cylinder)
321,165
Triangular elements
0.004
$c_l$ at cylinder wall
0.02
$c_l$ in wake box
Cylinder mesh overview
Full domain: farfield radius 15 m, cylinder diameter 0.6 m. View crops to ~±3 m for wake visibility.
Cylinder mesh zoomed
Distance-based refinement (cl=0.008 at wall) and wake box (cl=0.04) extending 6D downstream. The wake box ensures vortex structures are adequately resolved.

Simulation

Steady RANS SST

Parameter Value Notes
Solver INC_RANS Incompressible RANS (low Mach, ~0.003)
Turbulence model SST $k$-$\omega$ Fully turbulent; no transition model
Reynolds number 40,000 Non-dimensionalized via INITIAL_VALUES
Viscosity $\mu = 2.5 \times 10^{-5}$ Non-dimensional: $\mu = 1/Re$
Convective scheme FDS Roe-type flux splitting; MUSCL=NO
Iterations 3,000 Converges at ~500; CFL=0.5

Full config file is in the Code Notebook (section 2, In [2]).

Unsteady Laminar NS — Reynolds Number Sweep

Parameter Value Notes
Solver INC_NAVIER_STOKES No turbulence model: laminar NS
Reynolds numbers 120, 200, 500 Sub-critical range; vortex street at all three
Time marching Dual time-stepping 2nd-order 300 physical steps, 15 inner iters each
Time step 0.3 s 90 s total simulation; matches ΦFlow duration
Nondimensionalization DIMENSIONAL Dimensional $\mu$, $\rho$, $U$; more stable for unsteady
Spin (Magnus) $\omega_z = 0.4$ rad/s Moving wall BC; $S = \omega R / U = 0.12$

Full config file is in the Code Notebook (section 2, In [3]). Key difference between the two configs: the steady RANS solver uses non-dimensional viscosity ($\mu = 1/Re$) with INITIAL_VALUES, while the unsteady laminar solver uses dimensional viscosity ($\mu = 0.00267875$ kg/(m·s)) with DIMENSIONAL (in this mode, SU2 uses the REYNOLDS_NUMBER parameter to set the effective Reynolds number for its internal non-dimensional equations, while the dimensional $\mu$ value is used for scaling output quantities; the algebraic check $\rho U D / \mu = 1.2886 \times 1.0 \times 0.6 / 0.00267875 \approx 289$ does not apply because the solver internally rescales the reference density). The dimensional approach is more robust for the dual time-stepping unsteady solver. The Magnus configuration adds a moving wall boundary condition rotating at $\omega_z = 0.4$ rad/s, which corresponds to a spin parameter $S = 0.12$: a moderate spin rate for a soccer ball.

Unsteady Reynolds Sweep

Why run six cases (3 Re × 2 spin states) instead of one? The Reynolds number determines how the flow separates and sheds. A single Re only validates one point. Running three Re tells us whether the solver reproduces the correct trends.

The Strouhal number $St$ vs Re curve for a circular cylinder is one of the most precisely documented relationships in fluid dynamics (Williamson 1988). If our values follow the accepted curve, the solver, mesh, and timestep are correct:

Re Our $St$ Literature $St$ Our $C_d$ Literature $C_d$ Error ($St$)
120 0.16 0.168 0.654 0.65 −4.8%
200 0.18 0.183 0.536 0.55 −1.6%
500 0.20 0.200 0.467 0.47 0.0%

The trend matches across all three Re. Both $C_d$ and $St$ agree with Williamson (1988) and Tritton (1959) to within typical unstructured-mesh accuracy.

All 6 cases ran for 300 time steps ($\Delta t = 0.3$ s, 90 s total) on the fine mesh (160k nodes, 321k elements).

Results

Case Re $C_d$ $C_l$ range $C_l$ mean $St$
No Spin 120 0.654 [-0.043, 0.048] -0.003 0.16
No Spin 200 0.534 [-0.058, 0.065] -0.034 0.18
No Spin 500 0.467 [-0.164, 0.196] 0.134 0.20
Magnus $S=0.12$ 120 0.684 [-0.303, -0.017] -0.303 0.04
Magnus $S=0.12$ 200 0.681 [-0.394, -0.090] -0.395 0.04
Magnus $S=0.12$ 500 0.792 [-0.260, -0.012] -0.260 0.06

What the data shows

  1. Drag drops as Reynolds number rises: $C_d$ falls from 0.654 to 0.467 for no-spin cases. Higher Re means thinner boundary layers and a narrower wake, reducing pressure drag. The Strouhal number climbs in parallel from 0.16 to 0.20, matching expected values.
  2. Spin increases drag and creates downward lift: Magnus cases show 2–25% higher $C_d$ than no-spin, with the largest effect at low Re. All three produce a negative $C_l$ (top spin pushes the ball down), peaking at Re=200 ($C_l = -0.39$).
  3. Spin breaks the vortex street: The Strouhal number drops from 0.16–0.20 (alternating vortices) to 0.04–0.06 (steady asymmetric wake). The spinning cylinder suppresses the natural shedding cycle.
Mesh limitation — damped shedding amplitude: The unstructured triangular mesh damps the $C_l$ amplitude by an order of magnitude ($\pm 0.05$) compared to the literature value ($\pm 0.5$). Numerical dissipation from skew-angled triangles in the wake smooths out vortex cores before they fully develop. The shedding frequency ($St$) is correct (within 5% of Williamson 1988), but the amplitude is underpredicted. Recovering the full amplitude would require a structured quad mesh or prism layers in the wake region to reduce transverse numerical diffusion. Upcoming work includes a grid convergence study on a block-structured O-grid and DDES/IDDES to resolve separation-induced transition.

Surface Pressure Validation: $C_p(\theta)$

Beyond integral forces ($C_d$, $C_l$), we extracted the pressure coefficient distribution around the cylinder surface from the final timestep of each case. $C_p(\theta)$ is the gold standard for CFD validation—it shows exactly where the flow accelerates on the front shoulder (suction peak), where it decelerates (adverse pressure gradient), and where it separates (plateau in the rear).

Cp(theta) comparison
Surface pressure coefficient $C_p(\theta)$ for all six cases. No-spin cases (blue) show symmetric distributions; Magnus cases (red) show asymmetry. The suction peak shifts aft and the base pressure plateau rises with increasing Re in the no-spin cases, consistent with later separation and narrower wakes. The right panel zooms on the rear surface where separation occurs.

Measured separation angles from the $C_p$ curve shape:

Case $\theta_s$ (measured) Literature range Notes
No spin Re=120 87° 85°–95° Classic laminar separation
No spin Re=200 97° 88°–100° Later separation, thinner BL
No spin Re=500 103° 95°–110° Latest separation, narrowest wake
Magnus Re=120 85° Asymmetric Top side: earlier separation from opposed spin
Magnus Re=200 85° Asymmetric Strongest lift bias ($C_l = -0.39$)
Magnus Re=500 60° Asymmetric Very early top-side separation at high Re + spin

The no-spin separation angles increase monotonically with Re, confirming the physical trend. The Magnus case at Re=500 shows notably earlier separation on the retreating (top) side at 60°, creating the strongest pressure asymmetry that drives the Magnus lift. The RANS SST steady solution (on the coarse mesh, 44k nodes) predicts $\theta_s \approx 110°$, consistent with a fully turbulent boundary layer that remains attached much longer than any laminar case.

A note on the fine-mesh RANS attempt: the steady solver could not converge on the 160k-node fine mesh because the mesh resolves the wake well enough for the von Kármán instability to develop. The steady solver oscillates between shedding states instead of converging to a time-averaged solution. The coarse-mesh RANS result ($C_d = 0.683$, $\theta_s \approx 110°$) remains the validated turbulent extreme. This is a known limitation: steady RANS for cylinder flows at Re > 100 requires enough numerical dissipation to suppress the inherent unsteadiness, which coarser meshes provide.

Visualization Gallery

All visualizations are generated from SU2 VTU output using matplotlib (static PNGs and MP4 animations). The workflow:

  1. SU2 writes per-timestep VTU files (ParaView format)
  2. PyVista reads the VTU, extracts pressure/velocity fields on slice planes
  3. Static PNGs rendered at 600×400 grid resolution with 40 contour levels
  4. Animations stitched at 150 frames (every 2nd timestep) at 10 fps for 15-second videos
  5. Comparison animations combine three Reynolds numbers side-by-side at 100 frames (every 3rd timestep)

The data is organized by Reynolds number (120, 200, 500) and spin state (no-spin, Magnus) into subdirectories. The cylinder is rendered as a solid black disk with a white outline, and the viewport spans $x \in [-2, 8]$ to show approximately three wake cycles.

Re Sweep: Static Comparison

No-Spin Pressure

Re120 no-spin pressure
Re=120
Re200 no-spin pressure
Re=200
Re500 no-spin pressure
Re=500

Magnus Pressure

Re120 Magnus pressure
Re=120
Re200 Magnus pressure
Re=200
Re500 Magnus pressure
Re=500

Animated Flow Fields (15s, 10fps, Re Sweep)

Each video shows 150 frames subsampled from 300 timesteps (every 2nd), spanning the full 90s simulation.

No Spin

Re=120: Pressure. Classic von Kármán street at $St=0.16$.
Re=200: Pressure. Faster shedding ($St=0.18$), narrower wake.
Re=500: Pressure. Highest frequency ($St=0.20$), tightest wake.

Magnus $S=0.12$

Re=120: Pressure. Spin locks the low-pressure region to the advancing side.
Re=200: Pressure. Strongest $C_l$ bias ($-0.39$).
Re=500: Pressure. Weaker bias ($C_l=-0.26$) but wider pressure asymmetry.
Re=120: Velocity with streamlines. Alternating vortex cores visible in the wake.
Re=200: Velocity. Higher Re produces tighter, faster vortices.
Re=500: Velocity. Wake narrows significantly at the highest Re.
Re=120: Velocity. Downward-deflected wake under Magnus; streamlines curve toward the advancing side.
Re=200: Velocity. Strongest deflection and most stable asymmetric wake.
Re=500: Velocity. Broader wake but persistent bias, visible in the streamline asymmetry.

Re Sweep Comparison Videos (10s, 3-panel)

Three Reynolds numbers side-by-side for direct comparison of the wake evolution. Frames are subsampled every 3rd timestep (100 frames total).

No-spin pressure: Re=120 | Re=200 | Re=500. Wake narrows and shedding accelerates with increasing Re.
Magnus pressure: Re=120 | Re=200 | Re=500. Asymmetric pressure dipole persists across all three Reynolds numbers.
No-spin velocity: Re=120 | Re=200 | Re=500. Streamlines show the narrowing wake as Re increases.
Magnus velocity: Re=120 | Re=200 | Re=500. Deflection is strongest at Re=200 and weakest at Re=500.

# Why Validate?

Every CFD model makes approximations. Validation quantifies how much the numbers can be trusted. The ΦFlow and SU2 simulations describe the real physics of a cylinder in crossflow: laminar separation (~80°, $C_d \approx 1.2$) and turbulent separation (~110°, $C_d \approx 0.68$). Real soccer balls live between these bounds, with surface texture and spin determining where on the laminar-to-turbulent spectrum they operate.