Skip to content

Commit 3592620

Browse files
committed
Add TENO5, WENO7, TENO7 to convergence runners
Both 1D and 2D runners now test all 7 schemes: WENO5/3/1, MUSCL2, TENO5, WENO7, TENO7. Both case.py files gain --teno and --teno-ct flags. Resolution bounds: 1D: TENO5 [128,512], WENO7/TENO7 [128,256] (precision floor near N=512) 2D: TENO5 [32,128], WENO7/TENO7 [64,128] (MFC stencil constraint: N>=35) TENO CT values match the Shu-Osher examples: 1e-6 for order-5, 1e-9 for order-7. weno_eps tightened to 1e-40 (was 1e-16) for all WENO/TENO schemes in case.py.
1 parent 3a069d2 commit 3592620

4 files changed

Lines changed: 68 additions & 35 deletions

File tree

examples/1D_euler_convergence/case.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
parser = argparse.ArgumentParser(description="1D Euler convergence case")
1919
parser.add_argument("--mfc", type=json.loads, default="{}", metavar="DICT")
2020
parser.add_argument("-N", type=int, default=64, help="Grid points (default: 64)")
21-
parser.add_argument("--order", type=int, default=5, help="WENO order: 1, 3, or 5")
21+
parser.add_argument("--order", type=int, default=5, help="WENO order: 1, 3, 5, or 7")
2222
parser.add_argument("--muscl", action="store_true", help="Use MUSCL-2 instead of WENO")
23+
parser.add_argument("--teno", action="store_true", help="Use TENO instead of WENO")
24+
parser.add_argument("--teno-ct", type=float, default=1e-6, help="TENO CT threshold (default: 1e-6)")
2325
parser.add_argument("--cfl", type=float, default=0.4, help="CFL number (default: 0.4)")
2426
parser.add_argument("--no-mapped", action="store_true", help="Disable mapped WENO")
2527
parser.add_argument("--muscl-lim", type=int, default=0, help="MUSCL limiter: 0=unlimited 1=minmod ... (default: 0)")
@@ -48,12 +50,14 @@
4850
scheme_params = {
4951
"recon_type": 1,
5052
"weno_order": args.order,
51-
"weno_eps": 1.0e-16,
53+
"weno_eps": 1.0e-40,
5254
"weno_Re_flux": "F",
5355
"weno_avg": "F",
54-
"mapped_weno": "F" if (args.order == 1 or args.no_mapped) else "T",
56+
"mapped_weno": "F" if (args.order == 1 or args.no_mapped or args.teno) else "T",
5557
"null_weights": "F",
5658
"mp_weno": "F",
59+
"teno": "T" if args.teno else "F",
60+
**({"teno_CT": args.teno_ct} if args.teno else {}),
5761
}
5862

5963
print(

examples/2D_isentropicvortex_convergence/case.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
parser = argparse.ArgumentParser(description="2D isentropic vortex convergence case")
1414
parser.add_argument("--mfc", type=json.loads, default="{}", metavar="DICT")
1515
parser.add_argument("-N", type=int, default=32, help="Grid points per dim (default: 32)")
16-
parser.add_argument("--order", type=int, default=5, help="WENO order: 1, 3, or 5 (default: 5)")
16+
parser.add_argument("--order", type=int, default=5, help="WENO order: 1, 3, 5, or 7 (default: 5)")
1717
parser.add_argument("--muscl", action="store_true", help="Use MUSCL instead of WENO")
18+
parser.add_argument("--teno", action="store_true", help="Use TENO instead of WENO")
19+
parser.add_argument("--teno-ct", type=float, default=1e-6, help="TENO CT threshold (default: 1e-6)")
1820
parser.add_argument("--muscl-lim", type=int, default=0, help="MUSCL limiter: 0=unlimited 1=minmod ... (default: 0)")
1921
args = parser.parse_args()
2022

@@ -41,10 +43,12 @@
4143
scheme_params = {
4244
"recon_type": 1,
4345
"weno_order": args.order,
44-
"weno_eps": 1.0e-16,
45-
"mapped_weno": "F" if args.order == 1 else "T",
46+
"weno_eps": 1.0e-40,
47+
"mapped_weno": "F" if (args.order == 1 or args.teno) else "T",
4648
"null_weights": "F",
4749
"mp_weno": "F",
50+
"teno": "T" if args.teno else "F",
51+
**({"teno_CT": args.teno_ct} if args.teno else {}),
4852
}
4953

5054
print(

toolchain/mfc/test/run_convergence.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
L2(rho(T) - rho(0)) measures accumulated scheme error; the comparison to rho(0)
1212
(the numerical IC) eliminates IC discretisation error, isolating the scheme error.
1313
14+
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.
17+
1418
Usage:
15-
python toolchain/mfc/test/run_convergence.py [--no-build] [--resolutions 16 32 64]
19+
python toolchain/mfc/test/run_convergence.py [--no-build] [--resolutions 32 64 128]
1620
"""
1721

1822
import argparse
@@ -30,19 +34,24 @@
3034
CASE = "examples/2D_isentropicvortex_convergence/case.py"
3135
MFC = "./mfc.sh"
3236

33-
# (label, extra_args, expected_order, tolerance)
37+
# (label, extra_args, expected_order, tolerance, min_N, max_N)
3438
# With eps=0.01 and N=32..128 the prim->cons covariance error O(eps^3 h^2) is
3539
# well below the scheme's spatial error O(eps^2 h^p), so each scheme shows its
3640
# nominal rate. The tolerance is the allowable shortfall from the nominal order.
3741
#
38-
# WENO3 note: at N=32-128 the rate is ~2.0-2.2 (pre-asymptotic; approaches 3
39-
# at finer grids) — still clearly above the 1D result (1.77, smooth-extremum
40-
# degradation), which is what this test is designed to show. Threshold 1.8.
42+
# WENO3: at N=32-128 the rate is ~2.0-2.2 (pre-asymptotic; approaches 3 at
43+
# 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.
4147
SCHEMES = [
42-
("WENO5", ["--order", "5"], 5, 1.0),
43-
("WENO3", ["--order", "3"], 3, 1.2),
44-
("WENO1", ["--order", "1"], 1, 0.4),
45-
("MUSCL2", ["--muscl"], 2, 0.5),
48+
("WENO5", ["--order", "5"], 5, 1.0, 32, None),
49+
("WENO3", ["--order", "3"], 3, 1.2, 32, None),
50+
("WENO1", ["--order", "1"], 1, 0.4, 32, None),
51+
("MUSCL2", ["--muscl"], 2, 0.5, 32, None),
52+
("TENO5", ["--order", "5", "--teno", "--teno-ct", "1e-6"], 5, 1.0, 32, None),
53+
("WENO7", ["--order", "7"], 7, 3.0, 64, None),
54+
("TENO7", ["--order", "7", "--teno", "--teno-ct", "1e-9"], 7, 3.0, 64, None),
4655
]
4756

4857

@@ -119,7 +128,11 @@ def run_case(tmpdir: str, N: int, extra_args: list):
119128
return Nt, os.path.join(tmpdir, f"N{N}")
120129

121130

122-
def test_scheme(label, extra_args, expected_order, tol, resolutions):
131+
def test_scheme(label, extra_args, expected_order, tol, resolutions, min_N=None, max_N=None):
132+
if min_N is not None:
133+
resolutions = [N for N in resolutions if N >= min_N]
134+
if max_N is not None:
135+
resolutions = [N for N in resolutions if N <= max_N]
123136
print(f"\n{'=' * 60}")
124137
print(f" {label} (need rate >= {expected_order - tol:.1f})")
125138
print(f"{'=' * 60}")
@@ -166,7 +179,7 @@ def main():
166179
parser = argparse.ArgumentParser(description="MFC convergence-rate verification")
167180
parser.add_argument("--no-build", action="store_true", help="Skip build step")
168181
parser.add_argument("--resolutions", type=int, nargs="+", default=[32, 64, 128], help="Grid resolutions (default: 32 64 128; N<32 unsupported for WENO5)")
169-
parser.add_argument("--schemes", nargs="+", default=["WENO5", "WENO3", "WENO1", "MUSCL2"], help="Schemes to test (default: all)")
182+
parser.add_argument("--schemes", nargs="+", default=["WENO5", "WENO3", "WENO1", "MUSCL2", "TENO5", "WENO7", "TENO7"], help="Schemes to test (default: all)")
170183
args = parser.parse_args()
171184

172185
if not args.no_build:
@@ -180,11 +193,11 @@ def main():
180193
sys.exit(1)
181194

182195
results = {}
183-
for label, extra_args, expected_order, tol in SCHEMES:
196+
for label, extra_args, expected_order, tol, min_N, max_N in SCHEMES:
184197
if label not in args.schemes:
185198
continue
186199
try:
187-
passed = test_scheme(label, extra_args, expected_order, tol, args.resolutions)
200+
passed = test_scheme(label, extra_args, expected_order, tol, args.resolutions, min_N, max_N)
188201
except Exception as e:
189202
print(f" ERROR: {e}")
190203
passed = False

toolchain/mfc/test/run_convergence_1d.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,25 @@
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)~O(h^3) is negligible
11-
relative to the spatial error at all tested resolutions, allowing WENO5 to
12-
show its true 5th-order rate.
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.
1313
14-
WENO3-JS degrades to 2nd order at smooth extrema (Henrick et al. 2005, the
15-
Jiang-Shu smoothness indicators do not converge to optimal weights at critical
16-
points where f'=0). The expected rate for WENO3 here is therefore 2, not 3;
17-
the 2D isentropic vortex test (run_convergence.py) verifies WENO3 rate 3 on
18-
a problem without smooth-extremum contamination.
14+
WENO3-JS degrades to 2nd order at smooth extrema (Henrick et al. 2005).
15+
The expected rate for WENO3 here is therefore 2, not 3; the 2D isentropic
16+
vortex test (run_convergence.py) verifies WENO3 rate 3.
1917
2018
MUSCL2 uses muscl_lim=0 (unlimited central-difference) by default. TVD
2119
limiters clip slopes to zero at smooth extrema and stall at 1st order on the
2220
sine wave; the unlimited limiter preserves 2nd-order convergence everywhere.
2321
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+
2429
Usage:
2530
python toolchain/mfc/test/run_convergence_1d.py [--no-build] [--resolutions 32 64 128]
2631
"""
@@ -43,18 +48,25 @@
4348
# (label, extra_args, expected_order, tolerance, min_N, max_N)
4449
# Per-scheme resolution bounds let each scheme run over the range where its
4550
# asymptotic order is cleanly visible:
46-
# WENO5 : cap at N=512 — double-precision floor kills the rate at N=1024
47-
# (error ~2.6e-12, rate collapses to 0.69); [128,512] gives 4.99.
48-
# WENO3 : start at N=256 — skips the coarsest pre-asymptotic points;
49-
# WENO3-JS degrades to 2nd order at smooth extrema (Henrick 2005),
50-
# asymptote confirmed 1.99 at N=4096; [256,1024] gives ~1.87.
51-
# WENO1 : full range [128,1024]; rate 0.97.
52-
# MUSCL2: full range [128,1024]; unlimited slope, rate exactly 2.00.
51+
# WENO5 : cap at N=512 — double-precision floor kills the rate at N=1024
52+
# (error ~2.6e-12, rate collapses to 0.69); [128,512] gives 4.99.
53+
# WENO3 : start at N=256 — skips the coarsest pre-asymptotic points;
54+
# WENO3-JS degrades to 2nd order at smooth extrema (Henrick 2005),
55+
# asymptote confirmed 1.99 at N=4096; [256,1024] gives ~1.87.
56+
# WENO1 : full range [128,1024]; rate 0.97.
57+
# MUSCL2 : full range [128,1024]; unlimited slope, rate exactly 2.00.
58+
# TENO5 : same range as WENO5; CT=1e-6; rate matches WENO5 on smooth problems.
59+
# WENO7 : cap at N=256 — machine-precision floor (~2e-15) reached near N=512
60+
# for 7th-order schemes on this smooth problem.
61+
# TENO7 : same range as WENO7; CT=1e-9.
5362
SCHEMES = [
5463
("WENO5", ["--order", "5"], 5, 0.2, 128, 512),
5564
("WENO3", ["--order", "3"], 2, 0.2, 256, None),
5665
("WENO1", ["--order", "1"], 1, 0.05, 128, None),
5766
("MUSCL2", ["--muscl"], 2, 0.1, 128, None),
67+
("TENO5", ["--order", "5", "--teno", "--teno-ct", "1e-6"], 5, 0.2, 128, 512),
68+
("WENO7", ["--order", "7"], 7, 1.0, 128, 256),
69+
("TENO7", ["--order", "7", "--teno", "--teno-ct", "1e-9"], 7, 1.0, 128, 256),
5870
]
5971

6072

@@ -182,7 +194,7 @@ def main():
182194
parser.add_argument(
183195
"--schemes",
184196
nargs="+",
185-
default=["WENO5", "WENO3", "WENO1", "MUSCL2"],
197+
default=["WENO5", "WENO3", "WENO1", "MUSCL2", "TENO5", "WENO7", "TENO7"],
186198
help="Schemes to test",
187199
)
188200
parser.add_argument("--cfl", type=float, default=0.02, help="CFL number (default: 0.02; small so RK3 temporal error is negligible)")

0 commit comments

Comments
 (0)