# ezflow_v2/app — interactive web app (turbulent-RANS GNN predictor) A self-contained FastAPI + Three.js app that predicts steady turbulent-RANS flow fields around a body **in milliseconds** with a choice of surrogate models, an interactive 3D viewer, slice planes, and a point-probe tool. > Self-contained under `ezflow_v2/`; **separate from the v1 app** (`app/`, port 8000) > per the v1/v2 separation rule. The v2 app runs on **port 8001**. ```bash python ezflow_v2/app/run_app.py # -> http://127.0.0.1:8001 (opens a browser) ``` First prediction lazily loads the model(s) (the ensemble loads all three, ~10–20 s); every prediction after that runs in milliseconds. Requires the trained runs under `ezflow_v2/gnn/_runs/` (rans_v5, hybrid, transolver) and the v2 env (torch 2.11+cu128, gmsh, trimesh, pyvista, fastapi, uvicorn). --- ## What it does (pipeline) ``` geometry (procedural shape OR uploaded STL) │ ├─ premesh: normalize to L=1 → surface (true geometry if watertight, else │ voxel envelope) → graded tet mesh (fine near body) │ ├─ graph: nodes = mesh points; features = geometry + (Re, yaw) ONLY │ (same features.py as training — no solved field is ever an input) │ ├─ model: GeoReNet / HybridFlow / Transolver / mean(G,T,H) ensemble │ └─ outputs: per-node u, v, w, p, k, ω, ν_t → 3D viewer + slice planes + probe ``` **No CFD is run for a prediction.** Inputs are the shape and the operating condition (`Re`, yaw) only; OpenFOAM is used solely offline to train the models. --- ## Controls & features ### Surrogate Model (dropdown) | Option | Backed by run | Params | |---|---|--:| | **mean(G,T,H) ensemble** (default) | average of the three below | ~10.2M | | HybridFlow | `_runs/hybrid` | 3.04M | | GeoReNet | `_runs/rans_v5` | 3.28M | | Transolver | `_runs/transolver` | 3.88M | The ensemble averages the three models per node (in the transformed target space). To add GINO: append `"GINO": ["gino"]` to `MODELS` in `infer_app.py`. ### Mesh Resolution (dropdown) — the premesher | Preset | Surface | Near-body size | Typical nodes | Use | |---|---|--:|--:|---| | Coarse | voxel envelope (vox 64) | h≈0.12 | ~3k | fastest, smoothed | | **Medium** (default) | **true geometry** (watertight) | h≈0.045 | ~10k | detail + training density | | Fine | true geometry | h≈0.022 | ~25k+ | most detail (slower) | - **Watertight, genus-0 uploads keep their true surface** (mirrors/edges survive) and are meshed with curvature-aware near-body refinement. - **Non-watertight / complex bodies** (e.g. DrivAer: open underbody, wheel wells) can't be parametrized into a clean external-flow solid, so they **auto-fall-back** to a robust voxel **envelope** (smooth shell) at the requested volume density. This is automatic — a complex STL will predict (as an envelope) rather than error. - Medium ≈ the ~10k-node density the models were trained on (the sweet spot). ### Geometry input - **Simple Shape** tab: cylinder / sphere / box / capsule / cone / torus (sliders). - **Upload STL** tab: drag-and-drop or browse any `.stl` (auto-normalized to L=1). Test geometries live in `C:\dev\ezflow_eval\test_stls\`. ### Physics Inlet speed, viscosity, density sliders → **Reynolds number** (the only physical knob, `Re = ρ U∞ L / μ`), plus a **yaw** slider. Models were trained on **Re ≈ 5×10²–1×10⁵**; predictions far outside that range are extrapolation. ### Visualize Field (slice viewer) All seven outputs are selectable: **|U|, u, v, w, p, k, ω, ν_t**. A slice plane (X/Y/Z axis + position slider) is colored by the chosen field; optional flow vectors on the slice. Color map is **green (low) → gold → red (high)**; the legend shows the range and a non-dimensional unit label. ### Mouse controls - **Left-drag** — rotate - **Mouse-wheel scroll** — zoom - **Wheel-press (middle) drag** — pan - **Single left-click** — set the rotation pivot at that point **and** probe values there ### Point Query (probe) Click any point in the 3D view → the sidebar shows the predicted **x,y,z and all field values** (|U|, u, v, w, p, k, ω, ν_t) at the nearest node (`/api/probe`). ### Display & View (right sidebar) Body style (glass / wireframe / hidden), wind-tunnel box, slice grid lines, flow vectors, and view presets (3D / Side / Top / Front). --- ## HTTP API | Endpoint | Method | Purpose | |---|---|---| | `/` | GET | the app page | | `/api/models` | GET | `{models, default}` | | `/api/predict` | POST (form) | `shape_type, model_name, resolution, Re, yaw_deg`, shape params or `file`; returns coords, wall_tris, per-field `ranges`, `geom_meta` (incl. surface `method`/`n_faces`), `mesh_meta` (incl. `resolution`, `n_nodes_approx`), `model {name, params_m, members}`, bounds | | `/api/slice` | POST (json) | `{normal_axis, position, field_type}` → slice `points/faces/scalars/vectors` | | `/api/probe` | POST (json) | `{x, y, z}` → nearest-node `{point, values{...}, velocity_vec}` | All responses are sent with `Cache-Control: no-store` so the browser always loads the latest build (static assets are also versioned, `?v=N`). --- ## Files - `run_app.py` — launcher (port 8001). - `serve.py` — FastAPI: endpoints above, no-cache middleware, caches the current `pyvista` grid for slicing/probing. - `infer_app.py` — model loading (architecture rebuilt per checkpoint) + uniform ensemble; `mesh_stl()` (premesh + read) with the auto-fallback; `predict_fields()` (predicts on the **full mesh** — the models are mesh-density-robust — and returns physical fields via `features.inverse_targets`). - `premesh.py` — detail-preserving premesher: `prepare_surface()` (true geometry vs voxel envelope) + `build_volume()` (graded, curvature-aware gmsh mesh); `PRESETS`. - `shape_generator.py` — procedural watertight shapes (trimesh). - `frontend/` — `index.html`, `app.js` (Three.js viewer, slicing, probe, colormap), `style.css`. Geometry meshing reuses the shared, geometry-neutral gmsh tooling in `datasets/gen/shapenet/` (same as `infer_v5`); it carries no v1 physics. --- ## Caveats / limitations - **Training fidelity:** the models were trained on the *smoothed envelope* geometries (ModelNet shapes passed through the same voxel remesh). A high-detail mesh improves the geometry/visualization and lets the model run on a faithful body, but the model **cannot fully exploit fine features it never saw** (e.g. mirror wakes). True fine-detail prediction on complex cars would need **retraining on high-fidelity geometry + CFD**. - **Complex/dirty STLs** (open underbody, non-watertight) are served as a smooth envelope (see Mesh Resolution). In-app gmsh can't make them a clean external-flow solid; the CFD-grade route (snappyHexMesh) is out of scope for the interactive app. - **Outputs are non-dimensional** (U∞ = 1, ρ = 1, L = 1); labels reflect this (`p/ρU∞²`, `ν_t/ν`, …). - **Out-of-distribution** geometry (e.g. ModelNet car/airplane, sharp finned bodies) and Reynolds numbers outside ~5×10²–1×10⁵ are extrapolation — interpret with care. ## Troubleshooting - **Page looks stale / controls unresponsive:** hard-refresh once (Ctrl+Shift+R) or open an Incognito window. The server sends no-cache headers, so it stays fresh afterward. - **An STL errors:** it should auto-fall-back to the envelope; if not, try Coarse resolution (goes straight to the robust envelope).