Maturity: mature · Audited: 2026-05-29 · See Capability Audit · Onboarding
This subsystem closes the loop between the SymPy stencil-optimization stack (the stencil_gen Python package) and the compiled shoccs C++ solver. It renders a Lua config from a template with runtime-parameterized boundary-closure stencil parameters (alpha/sigma/epsilon), invokes the prebuilt shoccs binary on the Brady-Livescu 2019 §4.3 2D varying-coefficient scalar-wave test, parses logs/system.csv, and returns a BridgeResult (final L∞, stability verdict, traces, exit code). It is Layer 8 (L8) of the brady2d analytical stability pipeline: the empirical end-to-end validator that confirms analytical predictions (L1–L7) survive contact with the real solver. The key trick is that the stencil parameter is passed through Lua at runtime and read in the C++ stencil constructor — a single compiled binary serves every point of a parameter sweep with no per-point recompile.
| File | Role |
|---|---|
scripts/stencil_gen/stencil_gen/cpp_bridge.py |
The bridge itself: make_brady2d_lua, run_cpp_brady2d, BridgeResult, path constants |
scripts/stencil_gen/stencil_gen/brady2d_stability.py |
L8 wrappers layer8_cpp_simulation (line 1059) and brady2d_stability_score (line 1140); the _L8_SCHEME_TYPE dispatch table (line 1051) |
lua-configs/brady_livescu_4_3.lua |
The template with --{{N}}-- / --{{T_FINAL}}-- / --{{SCHEME_TABLE}}-- markers; not standalone-runnable |
lua-configs/brady_livescu_4_3_n61.lua, _long.lua |
Standalone (non-templated) variants for manual N=61 and t=100 runs |
src/stencils/stencil.cpp |
stencil::from_lua dispatch (lines 44–78) reads sigma/epsilon and calls make_*_E4u_1 — where the runtime param enters C++ |
src/stencils/tension_E4u_1.cpp |
Representative runtime-parameterized family (siblings gaussian_E4u_1.cpp, multiquadric_E4u_1.cpp); ctor solves the RBF+poly system at construction and caches coeffs |
scripts/stencil_gen/sweeps/{brady2d_sweep,optimize,bo}.py |
Three sweep drivers consuming the bridge via --validate-with-cpp (bo.py is the plan-47 MF-BO driver) |
docs/brady2d_cpp_bridge_reference.md |
Existing deep-dive reference doc (see Related docs; mostly accurate, a few stale defaults noted below) |
Python bridge (cpp_bridge.py):
@dataclass
class BridgeResult:
final_linf: float = float("nan")
linf_trace: np.ndarray = ... # empty by default
t_trace: np.ndarray = ... # empty by default
stable: bool = False
wall_time_s: float = 0.0
exit_code: int = 0
stderr: str = ""
def run_cpp_brady2d(scheme_type, params, *, N=31, t_final=10.0, timeout=300.0,
template=BRADY_LIVESCU_TEMPLATE, binary=SHOCCS_BINARY) -> BridgeResult
def make_brady2d_lua(scheme_type, params, *, N, t_final,
template=BRADY_LIVESCU_TEMPLATE) -> str
# module constants
REPO_ROOT, LUA_TEMPLATE_DIR, BRADY_LIVESCU_TEMPLATE, SHOCCS_BINARY # = build/src/app/shoccsUpstream L8 wrappers (brady2d_stability.py):
def layer8_cpp_simulation(scheme, kernel, params, *, N=31, t_final=10.0) -> dict
# -> {final_linf, stable, wall_time_s, bridge_result}
def brady2d_stability_score(scheme, kernel, params, *, max_layer=7,
short_circuit=True, layer8_N=31, layer8_t_final=10.0) -> StabilityReport
# runs L8 only when max_layer >= 8C++ side (src/stencils/): stencil::from_lua(const sol::table&, const logs&={}) dispatches on type (order==1 branch) to:
make_tension_E4u_1(real sigma)— readsm["sigma"].get_or(3.0)make_gaussian_E4u_1(real epsilon)— readsm["epsilon"].get_or(0.9)make_multiquadric_E4u_1(real epsilon)— readsm["epsilon"].get_or(1.0)make_E4u_1(alpha)/make_E4_1/make_E2_1/make_E6u_1/make_E8u_1(classical,alphaarray)
(declared in src/stencils/stencil.hpp:277–284)
The full data flow for a single L8 validation call:
- A sweep driver calls
brady2d_stability_score(scheme, kernel, params, max_layer=8). - When
max_layer >= 8, it callslayer8_cpp_simulation(scheme, kernel, params, ...). - That looks up
(scheme, kernel)in_L8_SCHEME_TYPE→ a Luascheme.typestring (e.g.("E4","tension") → "tension_E4u"), raisingNotImplementedErrorfor unsupported combos. - It calls
run_cpp_brady2d(scheme_type, params, N, t_final), which:- calls
make_brady2d_lua(...)tostr.replacethe three markers (--{{N}}--,--{{T_FINAL}}--,--{{SCHEME_TABLE}}--) —_scheme_table_foremits whichever ofalpha/sigma/epsilonare present inparams; - creates a per-call
TemporaryDirectory, writes the Lua there plus an emptylogs/dir, and runsshoccs <lua>withcwd=tmpdir; - on the C++ side,
simulation_cycle::from_luaparses the Lua andstencil::from_luareadssigma/epsilonand calls the matchingmake_*_E4u_1constructor (the RBF+poly solve happens once, at construction); - the scalar-wave system writes
logs/system.csv;_parse_system_csvreads column 1 = Time, column 3 = L∞ from rows whose first cell starts with a digit; - returns a
BridgeResultwith the final L∞ and astableflag.
- calls
The runtime-parameterization invariant is the whole point: the scalar param travels params dict → Lua scheme table → C++ constructor, so one compiled binary serves an entire parameter sweep.
Add a new runtime-parameterized boundary-closure family (end to end):
- C++: add
src/stencils/<name>_E4u_1.cppimplementing the construction-time RBF+poly solve (copytension_E4u_1.cpp); declaremake_<name>_E4u_1(real param)instencil.hpp(near line 282); add anelse if (type == "<name>_E4u")branch instencil::from_lua(stencil.cpp:60–77) readingm["sigma"|"epsilon"].get_or(default); add a Catch2 test + fixture (model ont-tension_E4u_1). - Rebuild
shoccsonce. (See app-and-build.) - Python (only if a new scalar name is needed):
_scheme_table_for(cpp_bridge.py:52) already emits any ofalpha/sigma/epsilonpresent inparams; add a new key there only for a differently-named scalar. - Wire dispatch: add
(scheme, kernel) → "<name>_E4u"to_L8_SCHEME_TYPE(brady2d_stability.py:1051). - The three sweep CLIs pick it up automatically via
brady2d_stability_score(max_layer=8).
For codegen-emitted families, StencilGenSpec.scalar_params + printer.build_symbol_map already generalize the alpha runtime-param pattern; plan 42's three spline families are deliberately hand-written, not codegen-emitted (see py-derivation).
To change failure thresholds: the bridge's hard blow-up ceiling is final_linf < 10.0 in run_cpp_brady2d (cpp_bridge.py:195); the sweep-layer pass/fail tolerance L8_FINAL_LINF_TOL = 1.0 lives separately (brady2d_stability.py:1045).
- Two distinct stability thresholds — easy to confuse.
BridgeResult.stableusesfinal_linf < 10.0(a loose blow-up ceiling,cpp_bridge.py:195); the sweep/stability layer usesL8_FINAL_LINF_TOL = 1.0. A run can bestable=Trueyet fail the sweep's L8 tolerance. - CSV parse is positional and brittle.
_parse_system_csvhardcodes Time=column 1, L∞=column 3, treating any row whose first cell starts with a digit as data. Ifscalar_wave.cppreorders/renamessystem.csvcolumns, the bridge silently parses wrong values rather than erroring. This is the tightest coupling point to the C++ output format. make_brady2d_luadoes purestr.replacewith no validation. A missing/typo'd marker silently yields broken Lua that fails only whenshoccsparses it (surfacing as a nonzero exit, not a clear Python error). Brace balance is checked in tests, not at runtime.- Lua numbers are emitted via
repr(float(x))deliberately soalphacoefficients round-trip to full double precision matchingknown_values.json. Do not "simplify" tostr()or f-string formatting — it would truncate precision. - L8 is E4-uniform-only.
_L8_SCHEME_TYPEhas only the four E4 combos;scheme="E2"raisesNotImplementedError(deferred, plan 42.10a). Cut-cell (non-uniform-ψ) runtime parameterization is also deferred (plan 42.10b) — the spline families solve once because Brady-Livescu §4.3 is a uniform rectangular domain. - L8 is diagnostic-only.
optimize.py/bo.pydo not alterbest_objectivewhen L8 disagrees with the analytical verdict (plan 43.10a). A failing C++ run is a flag to investigate, not an automatic rejection. - Concurrency safety depends entirely on the per-call
TemporaryDirectorycwd.shoccswriteslogs/system.csvunder its cwd, so private tempdirs isolate parallel sweep runs. If a caller ever passes a fixed cwd or the binary writes logs to an absolute path, parallel runs would race onsystem.csv. REPO_ROOT = Path(__file__).resolve().parents[3]. Movingcpp_bridge.pyto a different directory depth silently breaks every path constant (BRADY_LIVESCU_TEMPLATE,SHOCCS_BINARY).- The template is not standalone-runnable — its markers are Lua line comments (
--{{...}}--). Usebrady_livescu_4_3_n61.lua/_long.luafor manual runs.
Verdict: mature. Evidence: cpp_bridge.py is complete (all error paths handled: nonzero exit, timeout, missing CSV, empty CSV) with strong hermetic Python coverage; the three C++ spline families are real, registered in from_lua, and each has a passing Catch2 test; and real callers route through the bridge across three sweep drivers (brady2d_sweep, optimize, bo — the latter is the active plan-47 MF-BO driver). The bridge landed in plan 42 (Apr 2026) and bo.py shows continued active use in plan 47 (May 2026, the current plan).
Known gaps (from verified audit flags):
- Real-binary end-to-end smoke test requires a built binary.
TestCppBridgeSmoke.test_classical_e4u_short_run(tests/test_cpp_bridge.py:329) is@pytest.mark.slow(opt-in via--run-slow) and skips whenSHOCCS_BINARYis absent. The binary builds and loads now (build fixed 2026-06-04 — was the Kokkos 5.1create_graphbreak, since resolved), sobuild/src/app/shoccsruns and the Python↔compiled-shoccs handshake can be exercised end to end. The test still needs the binary built and is slow, so it stays opt-in. Status: working; see app-and-build and Cleanup Plan. Equivalent slow/binary-guarded E2E tests live attest_brady2d_stability.py:1212/1445/1501andtest_optimizer.py:2449. - E2 + cut-cell L8 dispatch raises
NotImplementedErrorby design._L8_SCHEME_TYPEmirrors the C++ binary's real capability set (E4-uniform only); the binary genuinely cannot run E2-spline or spline-cut-cell closures (no such source files exist). Status: partial-by-design; deferred to plans 42.10a/42.10b (both still open). Not a stub — the guard is unit-tested. Adding support is a feature add (land the C++ Lua scheme types first, then add table rows), not a fix. lua-configs/brady_livescu_4_3_long.lua(and_n61.lua) have no automated consumer. Valid, end-to-end-runnable standalone configs for manual long-time/N=61 stability checks, documented in plan 42 and the handoff docs — but no pytest/ctest exercises them, so a future schema change could silently break them. Status: partial (keep; deliberate manual-use artifact, not dead code)._run_cpp_validationis duplicated inoptimize.pyandbo.py. Both wrappers are complete, CLI-wired, and tested (not partial) — but the shared constants_CPP_SUPPORTED_KERNELS/_SCHEMES/_CPP_VALIDATION_N_DEFAULT/_T_FINAL_DEFAULTare re-declared verbatim in both (andpareto.py), a genuine single-source-of-truth hazard. Status: mature code, cleanup candidate — hoist shared constants into a common module; see Cleanup Plan.
scripts/stencil_gen/tests/test_cpp_bridge.py(31 tests, hermetic via a fakeshoccsshell script):TestMakeBrady2DLua/TestMakeBrady2DLuaSpline(marker substitution, alpha/sigma/epsilon scheme-table emission, brace balance,repr()precision);TestRunCppBrady2D(success parse, nonzero exit, unstable/large-L∞, missing CSV, empty CSV, graceful timeout, tempdir isolation);TestBridgeResultDefaults. The full driver logic is exercised against a fake binary, so the real binary is not needed for these.scripts/stencil_gen/tests/test_brady2d_stability.py— L8 dispatch tested by monkeypatchingrun_cpp_brady2dto a stub (classical→E4u, spline kernel dispatch,NotImplementedErroron unknown(scheme,kernel), default N/t_final forwarding, cascade withmax_layer=8).scripts/stencil_gen/tests/{test_optimizer.py,test_sweep_bo.py}—TestRunCppValidation/TestValidateWithCppcover the two sweep wrappers (test_sweep_bo.py -k ValidateWithCpp→ 13 passed).- C++ Catch2:
t-tension_E4u_1,t-gaussian_E4u_1,t-multiquadric_E4u_1(labelstencils) assert coefficient match tophs._rbf_weights_numericwithin 1e-12. - NOT covered by default: the actual Python→compiled-
shoccshandshake (CSV column layout, exit semantics, Lua parse of the rendered template). The only true E2E tests are@pytest.mark.slow+ binary-guarded, so they run only with a built binary and--run-slow(the binary builds and loads as of the 2026-06-04 build fix; see Maturity & known gaps). No test asserts the rendered Lua actually parses in C++sol/Lua.
docs/brady2d_cpp_bridge_reference.md— the full deep-dive reference for this bridge (data flow, examples, failure tables). Mostly accurate; note its L8 defaults (N=31/t_final=10.0) match thelayer8_cpp_simulationsignatures but not the actual CLI invocations (brady2d_sweepuseslayer8_N=21,layer8_t_final=1.0;optimize.pyhas its own_CPP_VALIDATION_*defaults), and its failure table conflatesBridgeResult.stable(< 10.0) with the sweep-layerL8_FINAL_LINF_TOL(1.0).scripts/stencil_gen/docs/brady2d_stability_reference.md— the L1–L8 analytical stability stack this bridge plugs into as L8.- py-brady2d — the analytical brady2d stability stack (L1–L7) and sweep drivers (Python side).
- py-derivation — the SymPy stencil-derivation/codegen pipeline that produces the families this bridge validates.
- stencils — the C++ stencil subsystem and
stencil::from_lua(the dispatch this bridge targets). - systems —
scalar_wave, the C++ system that writeslogs/system.csv(the bridge's parse contract depends on its column layout). - app-and-build — the
shoccsbinary build (green as of 2026-06-04; real-binary L8 runs work again). plans/42-cpp-bridge-runtime-parameterized-stencils.md— the plan that introduced this bridge (pre-Kokkos-migration rationale archive lives underplans/meta.md).