You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ConstitutiveModel::emit_compute_function() (include/numsim_codegen/recipe.h:209-241) returns rendered C++ source. The CSE state, the typed expression DAG, and the per-output RHS exist in structured form internally, but the entry point hard-commits to C++ rendering.
This locks out every non-C++ backend:
FEniCS/UFL wants the typed DAG (UFL operator forms, not auto t5 = lam * t3;).
JAX/Python wants Python with jnp.einsum.
MLIR wants SSA values.
LaTeX/SymPy export (useful for verification) wants the AST.
None of these can consume the rendered C++ string. Without this split, the multi-backend ambition stated in Target::target_name() cannot extend beyond C++ frameworks (MOOSE, Abaqus, Ansys, LS-DYNA per existing issues #2/#3/#4).
Where
The string-rendering step is buried inside recipe.h:render_compute_function() (recipe.h:287-336).
The structured form already exists: CodeGenContext::statements() returns std::vector<Statement>; per-output RHS strings are separated before being concatenated.
Proposed design
Split Layer 2 into two stages:
structCompiledModel { // language-agnosticstructArgSpec {
std::string name;
SymbolDecl::Kind kind;
std::size_t dim;
std::size_t rank;
Role role;
bool is_output;
};
structOutputBinding {
std::string name;
// Reference to a leaf node in the DAG OR the final rendered RHS;// see "decision points" below.
};
std::vector<Statement> body; // CSE-ordered
std::vector<OutputBinding> outputs;
std::vector<ArgSpec> arguments;
std::string function_name;
};
classConstitutiveModel {
public:[[nodiscard]] CompiledModel compile() const; // CSE + validate, no render[[nodiscard]] std::string emit_compute_function() const {
returncxx_render(compile()); // existing behavior preserved
}
// ...
};
// C++ rendering moves into a free function (or a small renderer class):
std::string cxx_render(CompiledModel const &);
Non-C++ backends consume compile() directly and supply their own renderer (ufl_render, python_render, mlir_render, ...).
Decision points
Statement::rhs is still a C++ string today. For full non-C++ support, the DAG node should travel alongside the rhs so backends can re-walk it. Either keep Statement carrying the typed node and let backends re-render, or commit to one IR.
Where does CSE live? Probably still in compile() since CSE is language-agnostic, but a backend that wants a different CSE strategy (e.g. fewer temporaries for inlined GPU code) might want a hook.
Scope
This is the biggest single refactor in the backlog. Touches recipe.h, all three emitters, both shipped targets, and probably the tests. Multi-day work. Don't attempt before:
Operation registry (the new flexibility issue, see issue list) — registry-dispatched emitters are easier to make work over an IR than the current virtual-dispatch visitors.
Defer until backend #3 (Abaqus / Ansys / LS-DYNA, #2/#3/#4) lands or a non-C++ backend is actively planned. Until then, file this and don't ship more C++-only assumptions.
Summary
ConstitutiveModel::emit_compute_function()(include/numsim_codegen/recipe.h:209-241) returns rendered C++ source. The CSE state, the typed expression DAG, and the per-output RHS exist in structured form internally, but the entry point hard-commits to C++ rendering.This locks out every non-C++ backend:
auto t5 = lam * t3;).jnp.einsum.None of these can consume the rendered C++ string. Without this split, the multi-backend ambition stated in
Target::target_name()cannot extend beyond C++ frameworks (MOOSE, Abaqus, Ansys, LS-DYNA per existing issues #2/#3/#4).Where
recipe.h:render_compute_function()(recipe.h:287-336).CodeGenContext::statements()returnsstd::vector<Statement>; per-output RHS strings are separated before being concatenated.Proposed design
Split Layer 2 into two stages:
Non-C++ backends consume
compile()directly and supply their own renderer (ufl_render,python_render,mlir_render, ...).Decision points
Statement::rhsis still a C++ string today. For full non-C++ support, the DAG node should travel alongside the rhs so backends can re-walk it. Either keepStatementcarrying the typed node and let backends re-render, or commit to one IR.ArgSpecordering. This issue also fixes MOOSE call-site argument order diverges from compute function signature #6 (call-site arg order mismatch) —argument_orderbecomes the single source of truth.compile()since CSE is language-agnostic, but a backend that wants a different CSE strategy (e.g. fewer temporaries for inlined GPU code) might want a hook.Scope
This is the biggest single refactor in the backlog. Touches
recipe.h, all three emitters, both shipped targets, and probably the tests. Multi-day work. Don't attempt before:ArgSpecis the source of truth.Defer until backend #3 (Abaqus / Ansys / LS-DYNA, #2/#3/#4) lands or a non-C++ backend is actively planned. Until then, file this and don't ship more C++-only assumptions.
Refs