Skip to content

feat: add WebGPU CFD solver (Phase 1 - structured grid Euler)#15

Open
aeronauty-flexcompute wants to merge 12 commits intomainfrom
claude/recursing-germain
Open

feat: add WebGPU CFD solver (Phase 1 - structured grid Euler)#15
aeronauty-flexcompute wants to merge 12 commits intomainfrom
claude/recursing-germain

Conversation

@aeronauty-flexcompute
Copy link
Copy Markdown
Collaborator

Summary

  • Port g2d CUDA CFD solver concepts to WebGPU/WASM for browser-based structured-grid compressible flow analysis
  • New rustfoil-cfd crate: O-mesh generation, freestream initial conditions, boundary condition mapping, GPU params
  • 8 WGSL compute shaders: metrics, primitives, MUSCL reconstruction, Roe flux, residual, update, BCs, force reduction
  • TypeScript CfdSolver orchestration with GPU buffer management
  • CFD panel UI with grid/flow/physics controls and convergence display
  • 14 Rust tests passing (11 unit + 3 integration) proving the mesh generation pipeline works

Status: Rust CFD pipeline is proven working. WebGPU shader pipeline is experimental — bind group layout issues with layout: 'auto' have been partially fixed but need further debugging.

Test plan

  • cargo test -p rustfoil-cfd — 14 tests pass
  • tsc -b --noEmit — TypeScript compiles cleanly
  • CFD panel renders in UI (Window → CFD)
  • WebGPU mesh generation + solver convergence (needs further debugging)

🤖 Generated with Claude Code

aeronauty and others added 12 commits March 25, 2026 18:23
…ing)

Port g2d CUDA CFD solver concepts to WebGPU compute shaders for
browser-based structured-grid compressible flow analysis. This adds
a new "CFD mode" alongside the existing panel method solver.

New crate: rustfoil-cfd
- O-type structured mesh generation with geometric radial stretching
- Freestream initial conditions (Q = [rho, rho*u, rho*v, E, nu_tilde])
- Boundary condition type mapping (wall, farfield, interior)
- Solver configuration structs matching GPU uniform layout

WGSL compute shaders (8 shaders):
- metrics: grid Jacobian and metric terms
- primitives: conservative-to-primitive variable conversion
- reconstruct_muscl: 2nd-order MUSCL with minmod limiter
- roe_flux: Roe approximate Riemann solver with entropy fix
- residual: flux divergence RHS computation
- update: explicit forward Euler with positivity enforcement
- bc: slip/no-slip wall + characteristic farfield BCs
- forces_reduce: parallel reduction for Cl, Cd, Cm + L2 norm

TypeScript orchestration (CfdSolver):
- GPU buffer management for ~31MB structured grid data
- 8-pass per-timestep compute dispatch pipeline
- Async GPU-to-CPU readback for convergence monitoring

UI integration:
- CfdPanel with grid/flow/physics controls and convergence display
- Zustand cfdStore for solver state management
- Extended SolverMode type with 'cfd' option

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds 6 new tests for CfdConfig/CfdParamsGpu:
- Default config values
- GPU uniform struct is exactly 48 bytes (matches WGSL)
- from_config correctly maps physics/reconstruction enums
- Byte serialization produces correct little-endian layout
- PhysicsMode enum values match WGSL constants
- Serde JSON roundtrip

Total: 11 tests passing in rustfoil-cfd.
CI already runs `cargo test --workspace` which covers this crate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add CFD to PANELS array (layoutConfig.ts) and factory (DockingLayout.tsx)
- Add CFD to MobileLayout tabs and component map
- Fix CfdSolver.ts: use type-only imports for verbatimModuleSyntax compliance
- Fix CfdSolver.ts: cast Float32Array/Uint32Array for GPUAllowSharedBufferSource
- Fix CfdPanel.tsx: use static imports from rustfoil-wasm, flatten coordinates
  to Float64Array, remove unused forceHistory destructure
- Fix AirfoilCanvas.tsx: guard SolverMode='cfd' when calling panel method
  functions (computeStreamlines, computePsiGrid, computeDividingStreamline)
- Add cfd_* function declarations to rustfoil-wasm.d.ts

TypeScript compiles cleanly (tsc -b --noEmit passes with 0 errors).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: layout 'auto' generates bind group layouts only for
bindings actually referenced by each entry point, not all declared
in the shader module.

Fixes:
- Split shared reconstruct/roeFlux bind groups into per-direction
  variants (reconstructXi/Eta, roeFluxXi/Eta) matching what each
  entry point actually uses
- Add dummy reads in bc.wgsl for bc_type, mesh_x, mesh_y so the
  auto-layout includes all 6 declared bindings
- Fix forceAccum buffer from 12 to 16 bytes (WebGPU minimum)
- Fix readback buffer from 28 to 32 bytes to match
- Fix copyBufferToBuffer sizes to copy full 16-byte aligned chunks
- Restructure step() to one command encoder per timestep

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The reduce_wall_forces entry point declared metrics at binding 2 but
never read it, causing layout:auto to exclude binding 2. This caused
CreateBindGroup to fail with binding index 2 not present in layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests the complete mesh generation pipeline with NACA 0012:
- Full pipeline: mesh gen, initial conditions, BC types, GPU params
- Mesh quality: cell areas, no degenerate cells
- RANS: SA turbulence initialization

All pass, confirming Rust CFD code works correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Background: #0a0a1a -> #111118 (slightly lighter)
- Circumferential grid lines: 0.2 -> 0.55 alpha, teal
- Radial grid lines: 0.15 -> 0.45 alpha, blue
- Line width: 0.5 -> 0.7
- Airfoil wall: green -> white, lineWidth 2 -> 2.5
- Airfoil points: larger (3px -> 4px), brighter orange

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The setTransform(dpr,...) approach was causing sub-pixel rendering
issues. Switch to 1:1 pixel mapping (canvas.width = clientWidth)
which ensures lines are always at least 1 physical pixel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants