Add FlowReactor/FlowReactorSurface support (distance-marched PFR) - #112
Merged
Conversation
Implements docs/plans/2026-07-14-flow-reactor-support.md: STONE nodes for Cantera's ct.FlowReactor (plug-flow, distance-marched) and an attached ct.ReactorSurface (catalytic wall chemistry), plus round-trip support. - config.py: new opt-in `solver.axis: distance` (default "time"), validated in the single choke point shared by both STONE dialects. - cantera_converter.py: new FlowReactor branch in create_reactor_from_node (area, mass_flow_rate, surface_area_to_volume_ratio, energy); surface chemistry is a `surface:` property on the node rather than a new connection kind, since ct.ReactorSurface isn't a flow-graph edge. Builds the surface Interface first and derives the reactor's gas phase from its `.adjacent[...]`, mirroring Cantera's own surf_pfr.py. Distance-marched trajectories are tagged is_spatial/x, reusing the frontend's existing spatial-plot rendering with no UI code changes. - sim2stone.py: reverse-direction (Cantera script -> STONE YAML) detection, reading FlowReactor/ReactorSurface state directly off the live object (no AST guessing needed, unlike Func1 schedules). FlowReactor's mass_flow_rate is write-only in the Cantera 3.2 binding, so it's recovered via continuity (density * speed * area) instead. - New example: configs/surf_pfr.yaml + docs/cantera_examples/surf_pfr.py (methane partial oxidation over Pt, vendored from upstream Cantera), wired into the existing fixture-script test harness. - tests/test_sim2stone_flow_reactor.py: round-trip coverage both directions. Known gap: download_script_emitter.py's standalone-script codegen doesn't yet support FlowReactor/FlowReactorSurface (a separate, hardcoded reactor-dispatch pathway) -- the two --download tests for surf_pfr.py are marked xfail; YAML emission, --output-yaml, and validate all work. Extensive AI use — code & PR fully written by Claude Sonnet 5 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes the pre-commit failure on this branch after rebasing onto main.
erwanp
force-pushed
the
claude/flow-reactor-support-8c0a3d
branch
from
July 23, 2026 19:36
6e37295 to
97982f1
Compare
6 tasks
FlowReactor nodes defaulted to the generic ellipse shape used for stirred reactors, which doesn't read as a plug-flow duct. Adds it to the existing PFR-style rectangle selector alongside the other axial-flow reactor types. Verified visually: examples/surf_pfr.yaml's FlowReactor_0 node renders as a wide rectangle instead of a circle.
erwanp
added a commit
to parks4/boulder_examples
that referenced
this pull request
Jul 27, 2026
…d PFR) (#17) Adapts Cantera's surf_pfr.py (partial oxidation of methane over a platinum catalyst) as a real ct.FlowReactor + ct.ReactorSurface, solved distance-marched (solver.axis: distance), not a chain-of-CSTRs approximation -- now possible thanks to parks4/boulder#112. - adapters/surf_pfr.py: mirrors upstream's reactor setup, stops before the distance-marching loop (kept under `if False:`, same convention as piston.py's stepping guard) so sim2stone captures the true inlet state instead of the fully-converted outlet state. Builds via ct.FlowReactor + ct.ReactorSurface (not ct.FlowReactorSurface), the object pair Boulder's sim2stone/cantera_converter expect. - examples/surf_pfr.yaml: generated STONE YAML, verified to build and solve via DualCanteraConverter with real catalytic conversion (CH4 consumed, CO/H2 produced along the 3mm bed). - examples/manifest.yaml: surf_pfr unsupported -> adapted; updates the now-dead design-doc links on 1D_pfr_surfchem/surf_pfr_chain (removed by boulder#112) to point at the PR instead, and clarifies why those two remain out of scope (different mechanism/diagnostics; algorithmic chain-of-WSRs pattern, respectively). - tests/test_downloaded_scripts.py: skips surf_pfr's --download test (download_script_emitter.py doesn't support FlowReactor yet, a known gap documented in boulder#112 -- YAML validation, normalization, and the real solve all work). - New catalog screenshot showing the mole-fraction conversion profile along the bed. Depends on parks4/boulder#112 (FlowReactor/FlowReactorSurface support), not yet merged/released -- see the PR description for how this was verified against that branch. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the deferred design doc
docs/plans/2026-07-14-flow-reactor-support.md(removed by this PR now that it's built): support for Cantera'sct.FlowReactor(plug-flow, distance-marched) and an attachedct.ReactorSurface(catalytic wall chemistry), so scripts like upstream Cantera'ssurf_pfr.pycan be modeled in Boulder end-to-end, both directions (YAML → Cantera and Cantera → YAML).solver.axis: distance(boulder/config.py) — new opt-in stage-level solver key, default"time". Validated in the single choke point (_resolve_and_validate_solver_mode) shared by both STONE dialects (network:andstages:), so it never changes default behavior for existing time-integrated reactors.FlowReactornode (boulder/cantera_converter.py) — new branch increate_reactor_from_node, following the existing per-reactor-type branch style:area,mass_flow_rate,surface_area_to_volume_ratio,energy.surface:property on theFlowReactornode rather than a new connection kind.ct.ReactorSurfaceisn't a flow-graph edge the wayct.Wallis, so a connection-based design would have forced it into stage-DAG/Sankey semantics that don't apply. The builder constructs the surfaceInterfacefirst and derives the reactor's gas phase from its.adjacent[...], mirroring Cantera's ownsurf_pfr.pypattern (required for kinetics consistency between the two phases).is_spatial/xseries convention (built for CSTR-chain PFR approximations) already renders "Position (m)" plots inPlotsTab.tsx; the transient-solve trajectory recorder now tags distance-marched series withis_spatial: trueinstead ofis_residence: true, reusing that path.boulder/sim2stone.py) — detectsFlowReactor/attached surfaces directly off the live Cantera object (no AST guessing needed, unlike Func1 schedules, since nothing here is a discarded parametric form). One binding quirk found along the way:FlowReactor.mass_flow_rateis write-only in the Cantera 3.2 Python API — recovered via continuity (density × speed × area) instead of a direct read.configs/surf_pfr.yaml+docs/cantera_examples/surf_pfr.py(methane partial oxidation over Pt, vendored from upstream Cantera), wired into the existing fixture-script test harness (tests/test_sim2stone/test_fixture_scripts_sim2stone.py).tests/test_sim2stone_flow_reactor.pycovers both directions.Known gap
download_script_emitter.py's standalone-script codegen doesn't yet supportFlowReactor/FlowReactorSurface— it's a separate, hardcoded reactor-dispatch pathway fromcreate_reactor_from_node. The two--downloadtests forsurf_pfr.pyare markedxfailfor this reason; YAML emission,--output-yaml, andvalidateall work correctly.Note on scope vs. the original design doc
The plan referenced a
boulder_examples/examples/manifest.yamlcatalog withstatus: unsupportedentries — that catalog doesn't exist in this repo (checked: not in git history, not a submodule). The example/test targets were adapted to this repo's actual conventions (docs/cantera_examples/+configs/+tests/test_sim2stone/).Test plan
make type-check(mypy strict) — cleanmake qa(pre-commit run --all-files) — clean694 passed, 10 skipped, 7 xfailed, 0 failedDualCanteraConverter→ real catalytic chemistry (CH4 consumed, CO produced) along the distance profilect.FlowReactor+ct.ReactorSurfacescript →sim_to_stone_yaml→ re-parsed and rebuilt successfullynpm run build/npm run test:unitwas needed for this PRExtensive AI use — code & PR fully written by Claude Sonnet 5
🤖 Generated with Claude Code
Added as an example in Boulder-Examples
parks4/boulder_examples#17