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.
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 inC:\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 currentpyvistagrid 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 viafeatures.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).