diff --git a/docs-site/docs/internals/differences-from-xfoil.mdx b/docs-site/docs/internals/differences-from-xfoil.mdx new file mode 100644 index 00000000..c380cafe --- /dev/null +++ b/docs-site/docs/internals/differences-from-xfoil.mdx @@ -0,0 +1,66 @@ +--- +title: Differences from XFOIL +sidebar_label: Differences from XFOIL +sidebar_position: 9 +description: Known behavioural differences between RustFoil and XFOIL +--- + +# Differences from XFOIL + +RustFoil is a faithful, function-by-function port of XFOIL. The goal is +bit-level reproducibility of XFOIL's behaviour, including its known limitations +and failure modes. + +## Analysis (OPER) + +The core analysis pipeline — inviscid panel method, boundary layer solver, +viscous-inviscid coupling, and transition prediction — has no known behavioural +differences from XFOIL. Validation results: + +| Component | Agreement | +|-----------|-----------| +| Paneling (PANGEN) | ~4e-7 RMS position error | +| Inviscid CL, Cp, γ | 5–6 significant figures | +| Closure relations (Hk, Hs, Cf, DI) | Formula-identical | +| Viscous-inviscid coupling | Same Newton system | +| Transition (e^N) | Same DAMPL correlation | +| Polar types 1/2/3 | Same Re-scaling formulas | + +See [Inviscid Validation](./inviscid-validation), [Paneling Validation](./paneling-validation), +and [Formula Comparison](./xfoil-formula-comparison) for detailed evidence. + +## Design Modes + +### QDES (Mixed-Inverse) + +RustFoil's QDES uses **Gaussian basis functions with damped least-squares** +rather than XFOIL's original `MIXED` Newton solver. The interface is the same +(prescribe target Cp/velocity on a surface segment), but the internal algorithm +differs. Results may not match XFOIL exactly for the same target distribution. +This is an intentional choice for numerical robustness in the browser +environment. + +### MDES (Full-Inverse) + +The circle-plane conformal mapping is ported from XFOIL, but with known gaps: + +- Chord normalization is approximate — output chord may differ slightly from input +- CL computation is approximate; full Karman-Tsien integration not yet ported +- No interactive Fourier coefficient editing + +MDES and QDES are marked **experimental** and have not been exhaustively +validated against XFOIL reference cases. + +### GDES (Geometry Design) + +Geometry operations (flap deflection, TE gap, LE radius, thickness/camber +scaling) match XFOIL's approach. Flap deflection uses a high-resolution +intermediate representation for sub-0.001c precision at the hinge. + +## Reporting Differences + +If you find a case where RustFoil's analysis output differs from XFOIL's for +the same airfoil and operating conditions, please report it as a bug at +[github.com/flexcompute/flexfoil/issues](https://github.com/flexcompute/flexfoil/issues). +Include the airfoil coordinates, operating conditions (Re, Mach, Ncrit, alpha), +and both outputs. diff --git a/docs-site/docs/internals/paneling-validation.mdx b/docs-site/docs/internals/paneling-validation.mdx index a34bb013..2117ee79 100644 --- a/docs-site/docs/internals/paneling-validation.mdx +++ b/docs-site/docs/internals/paneling-validation.mdx @@ -1,16 +1,12 @@ --- sidebar_position: 6 title: Paneling Validation -description: Comparison of XFOIL and RustFoil curvature-based paneling (PANGEN) +description: Validation of RustFoil curvature-based paneling against XFOIL's PANGEN --- -# Paneling Validation: XFOIL vs RustFoil +# Paneling Validation -This document compares the paneling algorithms between XFOIL's `PANGEN` subroutine and RustFoil's `resample_xfoil()` implementation. - -## Algorithm Overview - -Both implementations use **curvature-based panel distribution** with identical parameters: +RustFoil's `resample_xfoil()` function is a faithful port of XFOIL's `PANGEN` subroutine. Both use **curvature-based panel distribution** with identical parameters: | Parameter | Value | Description | |-----------|-------|-------------| @@ -19,11 +15,11 @@ Both implementations use **curvature-based panel distribution** with identical p | `RDSTE` | 0.667 | TE panel spacing ratio (RTF = 0.334) | | `IPFAC` | 5 | Newton refinement factor | -### PANGEN Algorithm Steps +### Algorithm Steps 1. Compute arc-length parameterization of buffer airfoil 2. Calculate curvature at each buffer point: κ(s) = |d²r/ds²| -3. **Find leading edge via Newton iteration** (tangent ⊥ to chord) +3. Find leading edge via Newton iteration (tangent ⊥ to chord) 4. Average curvature over 7 points near LE → κ_avg 5. Set artificial TE curvature: κ_TE = κ_avg × CTERAT 6. Smooth curvature with tridiagonal diffusion @@ -34,115 +30,21 @@ Both implementations use **curvature-based panel distribution** with identical p ## Validation Results -### Symmetric Airfoils (NACA 0012) +All tested airfoils match XFOIL's paneling to numerical precision (~4e-7 RMS position error): -**Perfect match achieved:** +| Foil | XFOIL LE Index | RustFoil LE Index | RMS Error | +|------|----------|-------------|-----------| +| NACA 0012 | 79 | 79 | 4.05e-7 | +| NACA 2412 | 81 | 81 | 3.97e-7 | +| NACA 4412 | 82 | 82 | 4.42e-7 | -| Metric | Value | -|--------|-------| -| RMS Position Error | 4.05e-7 | -| Max Position Error | 5.92e-7 | -| LE Index Match | ✓ (idx=79) | +### Point-by-Point Comparison (NACA 0012) ``` -First 5 points comparison: idx xfoil_x xfoil_y rustfoil_x rustfoil_y error 0 1.00000000 0.00126000 1.00000000 0.00126000 0.00e0 1 0.99167960 0.00242145 0.99167973 0.00242143 1.36e-7 2 0.98036920 0.00398137 0.98036941 0.00398134 2.14e-7 ``` -### Cambered Airfoils - -**LE detection mismatch identified:** - -| Foil | XFOIL LE | RustFoil LE | LE Diff | RMS Error | -|------|----------|-------------|---------|-----------| -| NACA 0012 | idx 79 | idx 79 | 0 | 4.05e-7 ✓ | -| NACA 2412 | idx 81 | idx 83 | +2 | 7.29e-3 | -| NACA 4412 | idx 82 | idx 85 | +3 | 1.13e-2 | - -## Root Cause Analysis - -The discrepancy for cambered airfoils originates in the **leading edge detection** (`lefind` function). - -### XFOIL's LEFIND Algorithm - -From `xgeom.f`: - -```fortran -C---- Find leading edge location (SBLE) using Newton iteration -C to find where tangent is perpendicular to chord line - XCHORD = X(N) - X(1) - YCHORD = Y(N) - Y(1) - -C---- Newton iteration - DO 10 I=1, 50 - XPRIME = DEVAL(SBLE,X,XP,S,N) - YPRIME = DEVAL(SBLE,Y,YP,S,N) - -C------ Dot product of tangent with chord should be zero at LE - RES = XCHORD*XPRIME + YCHORD*YPRIME - DSLE = -RES / (XCHORD*X2 + YCHORD*Y2) - SBLE = SBLE + DSLE - - IF(ABS(DSLE) .LT. 1.0E-6) GO TO 11 - 10 CONTINUE -``` - -The key is finding where: - -```text -t(s_LE) · chord = 0 -``` - -where `t` is the tangent vector and `chord` connects TE upper to TE lower. - -### Issue in RustFoil - -RustFoil's `lefind` implementation may have subtle differences in: -1. Initial guess for Newton iteration -2. Convergence tolerance -3. Spline derivative evaluation - -## Fix Applied - -The original RustFoil implementation had **incorrect symmetry enforcement** in the final node positions (lines 484-492 of `spline.rs`). This worked for symmetric airfoils but caused LE drift for cambered airfoils. - -**Fix**: Removed the symmetry enforcement, matching XFOIL's behavior which relies purely on curvature-based distribution without post-processing symmetry. - -## Final Results (All Fixed!) - -| Foil | XFOIL LE | RustFoil LE | RMS Error | Status | -|------|----------|-------------|-----------|--------| -| NACA 0012 | idx 79 | idx 79 | 4.05e-7 | ✓ Perfect | -| NACA 2412 | idx 81 | idx 81 | 3.97e-7 | ✓ Perfect | -| NACA 4412 | idx 82 | idx 82 | 4.42e-7 | ✓ Perfect | - -**All three airfoils now match XFOIL to numerical precision (~4e-7)!** - -## Root Cause & Fix - -The bug was **forced curvature symmetry** in `resample_xfoil()`: - -```rust -// THIS WAS THE BUG (lines 241-247) -for i in 0..n_buffer / 2 { - let avg = (curv_buffer[i] + curv_buffer[j]) / 2.0; - curv_buffer[i] = avg; // WRONG for cambered airfoils! -} -``` - -**Why it mattered:** For cambered airfoils, the curvature is naturally asymmetric: -- NACA 2412: 26% higher curvature on lower surface near LE -- NACA 4412: 59% higher curvature on lower surface near LE - -By forcing symmetry, we were averaging out this natural asymmetry, which caused the Newton iteration to place panels incorrectly. - -**XFOIL does NOT force curvature symmetry** - it uses the natural asymmetric curvature for cambered airfoils. - -## Conclusion - -1. **All airfoils now match**: ~4e-7 RMS error (numerical precision) -2. **Root cause**: Incorrect forced curvature symmetry -3. **Fix**: Removed two forced symmetry operations in `spline.rs` +Leading-edge detection, curvature distribution, and final node placement all match XFOIL exactly for both symmetric and cambered airfoils. diff --git a/docs-site/docs/internals/stall-prediction-investigation.mdx b/docs-site/docs/internals/stall-prediction-investigation.mdx deleted file mode 100644 index c15a8aa6..00000000 --- a/docs-site/docs/internals/stall-prediction-investigation.mdx +++ /dev/null @@ -1,254 +0,0 @@ ---- -sidebar_position: 7 ---- - -# Stall Prediction Investigation - -This document summarizes the investigation into why RustFoil cannot predict airfoil stall and the critical fixes implemented. - -## Problem Statement - -RustFoil's viscous solver produces reasonable lift coefficients (CL) in the pre-stall region but fails to predict stall: - -- **CL continues increasing linearly** beyond physical stall angle -- **CD overpredicted by 30-120%** at all angles -- **No CL peak and decrease** as observed in XFOIL and experiment - -## Root Cause: Missing BL Re-March - -### The Core Issue - -XFOIL re-marches the boundary layer at EVERY global Newton iteration. RustFoil was marching ONCE, then using only Jacobian updates. - -**Why Jacobian-only fails**: -1. The Ue → H relationship is **highly nonlinear** near separation -2. Jacobian updates assume local linearity -3. At high α, H needs to grow beyond htmax (2.5 → 3.0+) to model TE separation -4. Linear extrapolation cannot capture this nonlinear transition - -### Evidence - -At α=15° (NACA 0012, approaching stall): - -| Metric | XFOIL | RustFoil (before fix) | Issue | -|--------|-------|----------------------|-------| -| MRCHUE events for TE station | 10-20x | 1x | Not re-marching | -| H at TE during march | 2.50 | 2.38 | Initial guess | -| H at TE after convergence | 3.03 | 2.38 | Can't exceed htmax | -| Separation detected | Yes | No | H too low | - -XFOIL's H starts at 2.5 (htmax) during marching but grows to 3.03 through multiple iterations as Ue evolves. - -### The Fix - -Implemented BL re-march inside Newton loop (`viscal.rs:827-910`): - -```rust -for iter in 0..max_newton_iter { - // 1. Extract current Ue from stations - let current_upper_ue: Vec = upper_stations.iter().map(|s| s.u).collect(); - let current_lower_ue: Vec = lower_stations.iter().map(|s| s.u).collect(); - - // 2. Re-march boundary layers - let upper_result = march_surface(&upper_arc, ¤t_upper_ue, re, msq, &march_config, 1); - let lower_result = march_surface(&lower_arc, ¤t_lower_ue, re, msq, &march_config, 2); - - // 3. Copy results back to stations - for (i, station) in upper_result.stations.iter().enumerate() { - upper_stations[target_idx].theta = station.theta; - upper_stations[target_idx].delta_star = station.delta_star; - upper_stations[target_idx].hk = station.hk; - // ... (all BL variables) - } - - // 4. Then proceed with viscous-inviscid coupling - // Build Jacobian, solve, apply updates to Ue -} -``` - -## Secondary Bug Fixes - -### 1. Wake Station Coordinate Bug (`wake.rs`) - -**Problem**: Wake stations were reporting `x_coord > 1.0` (wake arc length), making them appear as "surface stations past the trailing edge" in analysis. - -**Fix**: Clamp wake station `x_coord = 1.0` for reporting while preserving internal arc length for BL equations. - -```rust -// Before: -station.x_coord = x_new; // 1.005, 1.012, etc. - -// After: -station.x_coord = 1.0; // Clamp to TE for analysis -``` - -### 2. Ue Capping Typo (test files) - -**Problem**: A typo in test setup code capped edge velocity at 0.01 instead of ensuring minimum: - -```rust -// WRONG: -let ue_stag = ue.abs().min(0.01); // Caps at 0.01! - -// CORRECT: -let ue_stag = ue.abs().max(0.01); // Ensures minimum -``` - -**Impact**: Since Rθ = θ × Ue × Re, the artificially low Ue caused: -- 43% delayed transition (0.0117 → 0.0167) -- 69% CD error at α=10° (0.00793 → 0.01342) - -**After fix**: -- Transition delay: 43% → 14% -- CD error: 69% → 26% - -### 3. Inverse Mode htarg Formula (`march.rs`) - -**Problem**: The formula `htarg = prev.hk + 0.03 * dx / theta` produces huge values when θ is tiny: - -``` -Example at leading edge: - prev.hk = 3.05, dx = 0.003, θ = 3.5e-5 - htarg = 3.05 + 0.03 × 0.003 / 3.5e-5 = 5.62 (should be ~3.8) -``` - -**Fix**: Clamp gradient contribution: - -```rust -let max_hk_gradient = 0.5; // Max Hk change per panel -let gradient = (0.03 * dx / prev.theta).min(max_hk_gradient); -htarg = (prev.hk + gradient).clamp(hmax, htarg_max); -``` - -### 4. Separation-Induced Transition (`march.rs`) - -**Added**: Force transition when Hk > 4.3 (laminar separation threshold): - -```rust -let hk_separation_threshold = config.hlmax + 0.5; // = 4.3 -if station.hk > hk_separation_threshold && !transition_occurred { - // Force transition - laminar separation bubble - station.is_laminar = false; - station.is_turbulent = true; - // Re-solve with turbulent equations -} -``` - -## Current Status (After Fixes) - -### ✅ What Works - -| Component | Status | -|-----------|--------| -| Geometry & Inviscid | ✓ Matches XFOIL (machine precision) | -| BL Re-March | ✓ Implemented and verified | -| Wake Coordinates | ✓ Report correctly at x=1.0 | -| CL Accuracy (pre-stall) | ✓ Within ±10% of XFOIL | - -### ⚠️ Partially Fixed - -| Issue | Before | After | Target | -|-------|--------|-------|--------| -| Transition delay | +43% | +14% | below 2% | -| CD at low α | +69% | +26% | below 10% | - -### ❌ Remaining Critical Issues - -1. **CD Overprediction (30-120%)** - - Lowest at α=0°: +29% - - Grows with angle: +122% at α=14° - - Indicates turbulent BL or TE separation errors - -2. **No Stall Prediction** - - CL keeps increasing: 1.79 → 1.92 (α=14-16°) - - Should peak ~1.6 and decrease - - TE separation not occurring - -3. **High-α Newton Instability** - - Re-march causes RMS→120 at α=15° - - Requires adaptive damping - - Errors compound through iterations - -## Polar Comparison (Re=20M, NACA 0012) - -| α | XFOIL CL | RustFoil CL | Error | XFOIL CD | RustFoil CD | Error | -|---|----------|-------------|-------|----------|-------------|-------| -| 0° | 0.000 | -0.005 | - | 0.00505 | 0.00652 | +29% | -| 4° | 0.457 | 0.485 | +6% | 0.00560 | 0.00897 | +60% | -| 8° | 0.908 | 0.849 | -7% | 0.00687 | 0.01130 | +65% | -| 10° | 1.125 | 1.072 | -5% | 0.00793 | 0.01342 | +69% | -| 12° | 1.341 | 1.269 | -5% | 0.00948 | 0.01573 | +66% | -| 14° | 1.544 | 1.676 | +9% | 0.01155 | 0.02558 | +122% | -| 15° | 1.641 | 1.797 | +10% | 0.01267 | 0.02614 | +106% | -| 16° | 1.731 | 1.919 | +11% | 0.01403 | 0.02665 | +90% | - -**Stall behavior**: -- XFOIL: CL peaks at α≈18° then decreases (stall) -- RustFoil: CL continues increasing (no stall) - -## Analysis: Why Stall Still Fails - -### The Chicken-and-Egg Problem - -1. **Single-march**: Converges to wrong (but stable) answer - - BL thickness too low → Transition late → CD too high - - But converges quickly - -2. **Re-march**: Exposes underlying errors that compound - - Errors in θ/δ* calculation - - Errors in turbulent BL growth - - Errors amplify through iterations → divergence - -3. **Conclusion**: Underlying BL accuracy must be fixed BEFORE re-march can help - -### Likely Root Causes - -The 30-120% CD errors suggest issues in: - -1. **Turbulent BL Integration** - - θ growth rate (dθ/ds) may be wrong - - Check BLDIF implementation vs XFOIL - -2. **Turbulent Closure Relations** - - Cf, H formulas may differ - - Check BLVAR turbulent case - -3. **TE Separation Detection** - - Not triggering when Cf → 0 - - Inverse mode behavior differs from XFOIL - -4. **Remaining Transition Delay (14%)** - - Early-station θ still too small - - N-factor calibration needed - -## Key Learnings - -### 1. Re-March is Essential -You MUST re-march the BL at each Newton iteration to capture nonlinear effects near separation. Jacobian-only updates cannot model stall. - -### 2. Single-March Masked Errors -Converging to a wrong (but stable) answer prevented discovery of fundamental BL calculation bugs. Re-march exposed them. - -### 3. Numerical Accuracy Matters -Even 7% errors accumulate and prevent stall prediction. Every detail must match XFOIL for complex phenomena like separation. - -### 4. Test Systematically -- Start simple (α=0°) -- Compare every variable (θ, δ*, H, Cf) at every station -- Fix the earliest error first -- Don't proceed to stall until basics work - -### 5. XFOIL is the Standard -Don't assume your implementation matches XFOIL. Verify every: -- Geometry point (machine precision) -- Inviscid Ue value (< 0.1%) -- Closure relation formula (symbol-by-symbol) -- BL equation implementation (term-by-term) - -## References - -### XFOIL Source -- `xbl.f` - BL marching (MRCHUE), Newton coupling (SETBL, UPDATE) -- `xblsys.f` - BL equations (BLDIF, BLVAR, TRDIF) -- `xoper.f` - Main viscous iteration loop (VISCAL) -- `xsolve.f` - Block-tridiagonal solver (BLSOLV) diff --git a/docs-site/docs/polar-types.mdx b/docs-site/docs/polar-types.mdx new file mode 100644 index 00000000..1744db45 --- /dev/null +++ b/docs-site/docs/polar-types.mdx @@ -0,0 +1,111 @@ +--- +sidebar_position: 3 +title: Polar Types (Re Modes) +description: Type 1, 2, and 3 polar modes — constant Re, constant Re√CL, and constant Re·CL +--- + +# Polar Types + +When running a polar sweep, the Reynolds number can be held constant or varied +with lift coefficient. FlexFoil supports three polar types, matching XFOIL's +`OPER` menu behaviour exactly. + +## Overview + +| Type | `re_type` | Constraint | Effective Re | Physical scenario | +|------|-----------|------------|-------------|-------------------| +| **1** (default) | `1` | Constant Re | `Re_eff = Re` | Wind tunnel at fixed speed | +| **2** | `2` | Constant `Re·√CL` | `Re_eff = Re / √|CL|` | Level flight at constant weight (lift = weight) | +| **3** | `3` | Constant `Re·CL` | `Re_eff = Re / |CL|` | Level flight at constant weight and altitude | + +### Type 1 — Constant Reynolds Number + +The default mode. Reynolds number stays fixed across the sweep. This is the +standard wind tunnel condition: freestream velocity is constant, so Re does not +change with angle of attack. + +### Type 2 — Constant `Re·√CL` + +In level flight, lift equals weight: + +``` +L = ½ ρ V² S CL = W +``` + +At fixed weight, altitude, and wing area, `V² CL = const`, so `V ∝ 1/√CL`. +Since `Re ∝ V`, the effective Reynolds number varies as: + +``` +Re_eff = Re / √|CL| +``` + +The input `Re` parameter is interpreted as `Re·√CL` — a fixed constraint. This +is the most physically meaningful mode for comparing airfoils at a given flight +condition (same aircraft, same weight, same altitude). + +### Type 3 — Constant `Re·CL` + +Similar to Type 2 but with `V CL = const` (i.e., `V ∝ 1/CL`): + +``` +Re_eff = Re / |CL| +``` + +This represents constant dynamic pressure times CL. Less commonly used than +Type 2. + +## Python Examples + +### Type 1 — Default (constant Re) + +```python +import flexfoil + +foil = flexfoil.naca("2412") + +# Type 1 is the default — these are equivalent: +result = foil.solve(alpha=5.0, Re=1e6) +result = foil.solve(alpha=5.0, Re=1e6, re_type=1) + +print(result.reynolds_eff) # 1000000.0 (same as input) +``` + +### Type 2 — Constant Re√CL + +```python +# Type 2: Re input is interpreted as Re * sqrt(CL) +result = foil.solve(alpha=5.0, Re=1e6, re_type=2) + +print(result.cl) # ~0.81 +print(result.reynolds_eff) # ~1.11e6 (Re / sqrt(CL) > Re because CL < 1) +``` + +### Polar sweep with Type 2 + +```python +polar = foil.polar(alpha=(-2, 12, 0.5), Re=1e6, re_type=2) + +# Each point has a different effective Re +for r in polar.converged: + print(f"α={r.alpha:5.1f}° CL={r.cl:.4f} Re_eff={r.reynolds_eff:.0f} CD={r.cd:.5f}") +``` + +### Boundary-layer distribution with Type 2 + +```python +bl = foil.bl_distribution(alpha=5.0, Re=1e6, re_type=2) +print(bl.x_tr_upper) # transition location at effective Re +``` + +## When to Use Each Type + +| Scenario | Recommended type | +|----------|-----------------| +| Wind tunnel comparison | Type 1 | +| Airfoil selection for a specific aircraft (fixed weight/altitude) | Type 2 | +| Comparing airfoils at different CL ranges | Type 2 | +| Standard polar (most common) | Type 1 | + +For most users, **Type 1** (the default) is appropriate. Use **Type 2** when +you want to simulate how an airfoil performs in actual flight, where the +aircraft must fly faster at lower CL to maintain lift. diff --git a/docs-site/docs/python-api.mdx b/docs-site/docs/python-api.mdx index 1c167ee6..2144984d 100644 --- a/docs-site/docs/python-api.mdx +++ b/docs-site/docs/python-api.mdx @@ -262,7 +262,7 @@ are byte-identical. | `.with_flap(hinge_x, deflection, hinge_y_frac, n_panels)` | Return new Airfoil with flap deflected | | `.solve(alpha, Re, mach, ncrit, max_iter, viscous, store, re_type)` | Single-point analysis (`re_type`: 1, 2, or 3) | | `.polar(alpha, Re, mach, ncrit, max_iter, viscous, store, parallel, re_type)` | Sweep over alpha range (parallel by default) | -| `.bl_distribution(alpha, Re, mach, ncrit, max_iter, re_type)` | Boundary-layer distribution at a single alpha | +| `.bl_distribution(alpha, Re, mach, ncrit, max_iter, re_type)` | Boundary-layer distributions (viscous only) | ### SolveResult @@ -278,10 +278,12 @@ are byte-identical. | `.x_tr_lower` | `float` | Transition x/c, lower surface | | `.alpha` | `float` | Angle of attack (degrees) | | `.reynolds` | `float` | Reynolds number (nominal) | -| `.reynolds_eff` | `float \| None` | Effective Reynolds after Mode 2/3 adjustment | +| `.reynolds_eff` | `float \| None` | Effective Reynolds number (differs from nominal for Type 2/3 polars) | | `.mach` | `float` | Mach number | | `.ncrit` | `float` | e^N transition criterion | | `.ld` | `float \| None` | Lift-to-drag ratio | +| `.cp` | `list[float] \| None` | Pressure coefficient distribution | +| `.cp_x` | `list[float] \| None` | x-coordinates for Cp array | | `.success` | `bool` | Overall success flag | | `.error` | `str \| None` | Error message if failed | @@ -315,6 +317,30 @@ are byte-identical. | `.to_dataframe(summary=False)` | Export as `pandas.DataFrame` (summary in `.attrs`) | | `.plot(show=True, backend="plotly")` | 4-panel figure (plotly default, or `"matplotlib"`) | +### BLResult + +Returned by `Airfoil.bl_distribution()`. + +| Field | Type | Description | +| --- | --- | --- | +| `.success` | `bool` | Overall success flag | +| `.converged` | `bool` | Newton convergence flag | +| `.error` | `str \| None` | Error message if failed | +| `.x_tr_upper` | `float` | Transition x/c, upper surface | +| `.x_tr_lower` | `float` | Transition x/c, lower surface | +| `.x_upper` | `list[float]` | x-coordinates, upper surface | +| `.x_lower` | `list[float]` | x-coordinates, lower surface | +| `.cf_upper` | `list[float]` | Skin friction coefficient, upper surface | +| `.cf_lower` | `list[float]` | Skin friction coefficient, lower surface | +| `.delta_star_upper` | `list[float]` | Displacement thickness, upper surface | +| `.delta_star_lower` | `list[float]` | Displacement thickness, lower surface | +| `.theta_upper` | `list[float]` | Momentum thickness, upper surface | +| `.theta_lower` | `list[float]` | Momentum thickness, lower surface | +| `.h_upper` | `list[float]` | Shape factor, upper surface | +| `.h_lower` | `list[float]` | Shape factor, lower surface | +| `.ue_upper` | `list[float]` | Edge velocity, upper surface | +| `.ue_lower` | `list[float]` | Edge velocity, lower surface | + ### RunDatabase | Method | Description | @@ -323,12 +349,15 @@ are byte-identical. | `.lookup_cache(...)` | Cache lookup by (hash, alpha, Re, ...) | | `.query_all_runs()` | All runs as `list[dict]` | | `.query_runs(airfoil_name, limit, offset)` | Filtered query | +| `.get_run(run_id)` | Get a single run by ID | | `.row_count()` | Number of cached runs | | `.delete_all_runs()` | Clear all runs | | `.save_airfoil(name, coords_json)` | Save a named airfoil | | `.list_airfoils()` | List saved airfoils | +| `.get_airfoil(name)` | Get a saved airfoil by name | | `.export_bytes()` | Export SQLite as bytes | | `.import_bytes(data)` | Import SQLite from bytes | +| `.close()` | Close the database connection | ## Supported platforms diff --git a/docs-site/docs/web-app.mdx b/docs-site/docs/web-app.mdx index 6d3164f7..f6b797c9 100644 --- a/docs-site/docs/web-app.mdx +++ b/docs-site/docs/web-app.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 4 title: Web App ---