Skip to content

Commit 09266d1

Browse files
committed
Use per-scheme CFL for WENO7/TENO7 to expose 7th-order spatial rate
With CFL=0.02 (1D) or CFL=0.4 (2D) and RK3 time integration, the temporal error O(dt^3) is comparable to the O(h^7) spatial error at N=128-256, giving a spurious fitted rate of ~3.7 instead of 7. Fix: bake CFL=0.005 directly into WENO7/TENO7 extra_args. This drops the temporal error by (0.005/0.02)^3=1/64 in 1D and (0.005/0.4)^3=1/51200 in 2D, making spatial error dominate. All other schemes keep CFL=0.02 (1D) or 0.4 (2D). Threshold updated from >=3.0 to >=6.5 (tol 0.5) in 1D and >=6.0 (tol 1.0) in 2D. Also adds --cfl arg to 2D case.py so the runner can override it per scheme.
1 parent 98623f1 commit 09266d1

3 files changed

Lines changed: 36 additions & 34 deletions

File tree

examples/2D_isentropicvortex_convergence/case.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
parser.add_argument("--teno", action="store_true", help="Use TENO instead of WENO")
1919
parser.add_argument("--teno-ct", type=float, default=1e-6, help="TENO CT threshold (default: 1e-6)")
2020
parser.add_argument("--muscl-lim", type=int, default=0, help="MUSCL limiter: 0=unlimited 1=minmod ... (default: 0)")
21+
parser.add_argument("--cfl", type=float, default=0.4, help="CFL number (default: 0.4; use 0.005 for WENO7/TENO7 so temporal error is negligible)")
2122
args = parser.parse_args()
2223

2324
gamma = 1.4
@@ -28,7 +29,7 @@
2829

2930
# Max wave speed: c_sound at ambient + max rotational velocity (at r~0.7 for exp(1-r^2))
3031
c_max = math.sqrt(gamma) + eps_vortex / (2.0 * math.pi)
31-
dt = 0.4 * dx / c_max
32+
dt = args.cfl * dx / c_max
3233
T_end = 2.0
3334
Nt = max(4, math.ceil(T_end / dt))
3435
dt = T_end / Nt # adjust to land exactly on T_end

toolchain/mfc/test/run_convergence.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
(the numerical IC) eliminates IC discretisation error, isolating the scheme error.
1313
1414
WENO7/TENO7 require min N=64 per dimension (MFC constraint: N >= 5 * weno_order = 35).
15-
With only N=64 and N=128 available from the default resolution set, the fitted rate
16-
is a single pairwise value; thresholds are set conservatively.
15+
They also need CFL=0.005: at CFL=0.4 the RK3 temporal error dominates (ratio
16+
temporal/spatial >> 1 because (CFL*h)^3 >> h^7 for the tested h values), giving
17+
a spurious rate of ~3. With CFL=0.005 the temporal error drops by (0.005/0.4)^3
18+
= 1/51200, making spatial error dominant and recovering rate ≈7.
1719
1820
Usage:
19-
python toolchain/mfc/test/run_convergence.py [--no-build] [--resolutions 32 64 128]
21+
python toolchain/mfc/test/run_convergence.py [--resolutions 32 64 128]
2022
"""
2123

2224
import argparse
@@ -41,17 +43,17 @@
4143
#
4244
# WENO3: at N=32-128 the rate is ~2.0-2.2 (pre-asymptotic; approaches 3 at
4345
# finer grids). Threshold 1.8.
44-
# WENO7/TENO7: require N >= 35 (MFC stencil constraint), so min_N=64. With
45-
# only N=64,128 in the default set the rate is a single pairwise value;
46-
# thresholds set conservatively pending actual run data.
46+
# WENO7/TENO7: require N >= 35 (MFC stencil constraint), so min_N=64. Use
47+
# CFL=0.005 to suppress RK3 temporal error (otherwise rate collapses to ~3).
48+
# With N=64,128 the fitted rate is a single pairwise value; threshold >=6.0.
4749
SCHEMES = [
4850
("WENO5", ["--order", "5"], 5, 1.0, 32, None),
4951
("WENO3", ["--order", "3"], 3, 1.2, 32, None),
5052
("WENO1", ["--order", "1"], 1, 0.4, 32, None),
5153
("MUSCL2", ["--muscl"], 2, 0.5, 32, None),
5254
("TENO5", ["--order", "5", "--teno", "--teno-ct", "1e-6"], 5, 1.0, 32, None),
53-
("WENO7", ["--order", "7"], 7, 4.0, 64, None),
54-
("TENO7", ["--order", "7", "--teno", "--teno-ct", "1e-9"], 7, 4.0, 64, None),
55+
("WENO7", ["--order", "7", "--cfl", "0.005"], 7, 1.0, 64, None),
56+
("TENO7", ["--order", "7", "--teno", "--teno-ct", "1e-9", "--cfl", "0.005"], 7, 1.0, 64, None),
5557
]
5658

5759

toolchain/mfc/test/run_convergence_1d.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77
L2(rho(T) - rho(0)) measures the accumulated scheme spatial truncation error.
88
No non-conservative alpha equation — clean benchmark for all schemes.
99
10-
CFL=0.02 by default so that RK3 temporal error O(dt^3) is negligible relative
11-
to the spatial error at all tested resolutions, allowing WENO5/7 to show their
12-
true spatial rates.
10+
WENO5/TENO5 use CFL=0.02: RK3 temporal error O(dt^3) is then negligible
11+
relative to the O(h^5) spatial error at N=128-512.
12+
13+
WENO7/TENO7 use CFL=0.005: at CFL=0.02 the RK3 temporal error (~3.4e-12 at
14+
N=128) is comparable to the spatial error (~4.4e-12), giving a spurious rate
15+
of ~3.7. With CFL=0.005 the temporal error drops by (0.005/0.02)^3 = 1/64
16+
to ~5.3e-14, well below spatial, and the measured rate approaches 7.
17+
N is capped at 256 — the machine-precision floor is reached near N=512.
1318
1419
WENO3-JS degrades to 2nd order at smooth extrema (Henrick et al. 2005).
1520
The expected rate for WENO3 here is therefore 2, not 3; the 2D isentropic
@@ -19,15 +24,8 @@
1924
limiters clip slopes to zero at smooth extrema and stall at 1st order on the
2025
sine wave; the unlimited limiter preserves 2nd-order convergence everywhere.
2126
22-
TENO5 uses the same 5th-order stencil as WENO5 with threshold-based stencil
23-
selection (CT=1e-6). On smooth problems all stencils are selected and the
24-
rate equals WENO5; TENO's advantage is sharper shock capturing.
25-
26-
WENO7/TENO7: capped at N=256 — the machine-precision floor (~2e-15) is
27-
reached near N=512 for 7th-order schemes on this smooth problem.
28-
2927
Usage:
30-
python toolchain/mfc/test/run_convergence_1d.py [--no-build] [--resolutions 32 64 128]
28+
python toolchain/mfc/test/run_convergence_1d.py [--resolutions 128 256 512 1024]
3129
"""
3230

3331
import argparse
@@ -46,6 +44,9 @@
4644
MFC = "./mfc.sh"
4745

4846
# (label, extra_args, expected_order, tolerance, min_N, max_N)
47+
# CFL is baked into each scheme's extra_args so that WENO7/TENO7 can use a
48+
# smaller CFL independently of all other schemes.
49+
#
4950
# Per-scheme resolution bounds let each scheme run over the range where its
5051
# asymptotic order is cleanly visible:
5152
# WENO5 : cap at N=512 — double-precision floor kills the rate at N=1024
@@ -56,18 +57,17 @@
5657
# WENO1 : full range [128,1024]; rate 0.97.
5758
# MUSCL2 : full range [128,1024]; unlimited slope, rate exactly 2.00.
5859
# TENO5 : same range as WENO5; CT=1e-6; rate matches WENO5 on smooth problems.
59-
# WENO7 : cap at N=256; measured rate ~3.7 — spatial h^7 and RK3 temporal
60-
# h^3 errors are comparable at these N; threshold set >=3.0 to
61-
# confirm convergence without requiring a higher-order time integrator.
62-
# TENO7 : same range and reasoning as WENO7; CT=1e-9.
60+
# WENO7 : CFL=0.005, cap at N=256 — machine-precision floor near N=512;
61+
# rate ≥6.5 (fits ≥6.0 threshold with tolerance 0.5 after temporal fix).
62+
# TENO7 : same range and CFL as WENO7; CT=1e-9.
6363
SCHEMES = [
64-
("WENO5", ["--order", "5"], 5, 0.2, 128, 512),
65-
("WENO3", ["--order", "3"], 2, 0.2, 256, None),
66-
("WENO1", ["--order", "1"], 1, 0.05, 128, None),
67-
("MUSCL2", ["--muscl"], 2, 0.1, 128, None),
68-
("TENO5", ["--order", "5", "--teno", "--teno-ct", "1e-6"], 5, 0.2, 128, 512),
69-
("WENO7", ["--order", "7"], 7, 4.0, 128, 256),
70-
("TENO7", ["--order", "7", "--teno", "--teno-ct", "1e-9"], 7, 4.0, 128, 256),
64+
("WENO5", ["--order", "5", "--cfl", "0.02"], 5, 0.2, 128, 512),
65+
("WENO3", ["--order", "3", "--cfl", "0.02"], 2, 0.2, 256, None),
66+
("WENO1", ["--order", "1", "--cfl", "0.02"], 1, 0.05, 128, None),
67+
("MUSCL2", ["--muscl", "--cfl", "0.02"], 2, 0.1, 128, None),
68+
("TENO5", ["--order", "5", "--teno", "--teno-ct", "1e-6", "--cfl", "0.02"], 5, 0.2, 128, 512),
69+
("WENO7", ["--order", "7", "--cfl", "0.005"], 7, 0.5, 128, 256),
70+
("TENO7", ["--order", "7", "--teno", "--teno-ct", "1e-9", "--cfl", "0.005"], 7, 0.5, 128, 256),
7171
]
7272

7373

@@ -196,18 +196,17 @@ def main():
196196
default=["WENO5", "WENO3", "WENO1", "MUSCL2", "TENO5", "WENO7", "TENO7"],
197197
help="Schemes to test",
198198
)
199-
parser.add_argument("--cfl", type=float, default=0.02, help="CFL number (default: 0.02; small so RK3 temporal error is negligible)")
200199
parser.add_argument("--muscl-lim", type=int, default=0, help="MUSCL limiter (0=unlimited 1=minmod ...; default: 0)")
201200
args = parser.parse_args()
202201

203-
cfl_extra = ["--cfl", str(args.cfl), "--muscl-lim", str(args.muscl_lim)]
202+
muscl_extra = ["--muscl-lim", str(args.muscl_lim)]
204203

205204
results = {}
206205
for label, extra_args, expected_order, tol, min_N, max_N in SCHEMES:
207206
if label not in args.schemes:
208207
continue
209208
try:
210-
passed = test_scheme(label, extra_args + cfl_extra, expected_order, tol, args.resolutions, min_N, max_N)
209+
passed = test_scheme(label, extra_args + muscl_extra, expected_order, tol, args.resolutions, min_N, max_N)
211210
except Exception as e:
212211
print(f" ERROR: {e}")
213212
passed = False

0 commit comments

Comments
 (0)