Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions docs-site/docs/internals/differences-from-xfoil.mdx
Original file line number Diff line number Diff line change
@@ -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.
124 changes: 13 additions & 111 deletions docs-site/docs/internals/paneling-validation.mdx
Original file line number Diff line number Diff line change
@@ -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 |
|-----------|-------|-------------|
Expand All @@ -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
Expand All @@ -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.
Loading
Loading