Skip to content

feat(core): support dynamic dt by reading from scene config live - #454

Closed
MuGdxy wants to merge 1 commit into
spiriMirror:mainfrom
MuGdxy:feat/issue-451-dynamic-dt
Closed

feat(core): support dynamic dt by reading from scene config live#454
MuGdxy wants to merge 1 commit into
spiriMirror:mainfrom
MuGdxy:feat/issue-451-dynamic-dt

Conversation

@MuGdxy

@MuGdxy MuGdxy commented May 9, 2026

Copy link
Copy Markdown
Member

Summary

Replace all cached Float dt members across ~25 CUDA backend systems with live attribute slot references (S<const geometry::AttributeSlot<Float>> dt_attr), enabling users to change the simulation timestep at runtime via scene.config().find<Float>("dt").

Previously, each system copied dt from the scene config once during do_build() or init(), making it impossible to change dt dynamically. Now every system reads dt from the scene config attribute on each use, following the same pattern already established by SimEngine for other config values like m_newton_velocity_tol.

Changes

  • SceneVisitor: Add dt() convenience accessor delegating to internal::Scene::dt()
  • 25 backend systems: Replace Float dt member with S<const geometry::AttributeSlot<Float>> dt_attr in Impl structs (time integrator, linear subsystems, line search, contact system, active set, animators, constitution managers, diff reporters)
  • Tolerance checkers: MaxTranslationChecker and ABDToleranceChecker now recompute abs_tol = factor * dt dynamically in do_check() instead of caching it in do_build()
  • Tests: Unit test verifying config read-back via SceneVisitor::dt(), and sim_case test (93_dynamic_dt) verifying end-to-end that changing dt at runtime affects simulation behavior

Fixes #451

Replace all cached `Float dt` members in ~25 backend systems with
`S<const geometry::AttributeSlot<Float>> dt_attr` so every system reads
dt from the scene config attribute on each use. This allows users to
change dt at runtime via `scene.config().find<Float>("dt")`.

- Add `SceneVisitor::dt()` convenience accessor
- Tolerance checkers now recompute abs_tol dynamically
- Add unit test and sim_case test verifying dynamic dt

Fixes spiriMirror#451

Co-authored-by: Cursor <cursoragent@cursor.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements dynamic time-step support across the CUDA backend by replacing static dt members with dt_attr attribute slots. This change allows simulation systems to react to time-step modifications in the scene configuration during runtime. The PR includes comprehensive test cases verifying the dynamic behavior. My feedback focuses on performance optimizations, specifically caching the dt value before entering hot loops to avoid redundant attribute lookups, and improving robustness by adding assertions to ensure the dt attribute is successfully retrieved from the configuration.

Comment on lines 144 to 149
for(auto constraint : m_impl.constraints.view())
{
ComputeEnergyInfo this_info{&m_impl, constraint->m_index, m_impl.dt, info.energies()};
ComputeEnergyInfo this_info{
&m_impl, constraint->m_index, m_impl.dt_attr->view()[0], info.energies()};
constraint->compute_energy(this_info);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better performance, consider caching the dt value from the attribute slot once before entering the loop, rather than re-accessing the view and indexing it in every iteration. This aligns with the practice of passing pre-loaded data to functors in hot loops.

    Float dt = m_impl.dt_attr->view()[0];
    for(auto constraint : m_impl.constraints.view())
    {
        ComputeEnergyInfo this_info{
            &m_impl, constraint->m_index, dt, info.energies()};
        constraint->compute_energy(this_info);
    }
References
  1. In performance-critical CUDA code, pass pre-loaded data as parameters to functors within hot loops to improve performance.

Comment on lines 166 to 169
{
EnergyInfo this_info{this, c->m_index, dt, info.energies()};
EnergyInfo this_info{this, c->m_index, dt_attr->view()[0], info.energies()};
c->compute_energy(this_info);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider caching the dt value before the loop to avoid redundant view access and indexing on every iteration. This ensures that data is pre-loaded and passed as a parameter to the functor, improving performance in hot loops.

    Float dt = dt_attr->view()[0];
    for(auto&& [i, c] : enumerate(constitution_view))
    {
        EnergyInfo this_info{this, c->m_index, dt, info.energies()};
        c->compute_energy(this_info);
    }
References
  1. In performance-critical CUDA code, pass pre-loaded data as parameters to functors within hot loops to improve performance.

{
auto config = world.scene().config();
dt = config.find<Float>("dt")->view()[0];
dt_attr = config.find<Float>("dt");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It is safer to verify that the "dt" attribute slot was successfully found before proceeding, as subsequent calls to view() on a null pointer will cause a crash. Adding an assertion here would improve robustness.

    dt_attr      = config.find<Float>("dt");
    UIPC_ASSERT(dt_attr, "dt attribute not found in scene config");

@MuGdxy MuGdxy closed this May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dynamic dt

1 participant