How Session Parameters (frames, dt, step_size) Impact Simulation Accuracy and Performance in PPF Contact Solver

Lowering dt (or step_size) improves simulation accuracy by increasing temporal resolution, while frames controls output resolution and I/O overhead, with total physical simulation time calculated as frames × dt.

The ppf-contact-solver repository provides a physics simulation engine that processes fixed scenes through a Rust backend. Three critical session parameters—frames, dt, and step_size—govern how long the simulation runs, how finely time is discretized, and how many mesh outputs are generated. Understanding their interplay is essential for balancing computational cost against numerical fidelity.

Understanding the Three Core Session Parameters

frames: Controlling Output Resolution

The frames parameter defines the number of output meshes the solver produces during a session. According to the source code in frontend/_session_param_.py (lines 30-34), this value is stored via session.param.set("frames", …) and determines the total iteration count shown in the progress bar implemented in frontend/_session_.py (lines 76-79).

More frames provide higher-resolution animations and finer visual inspection of contact events. However, each additional frame adds I/O and memory overhead, directly increasing wall-clock time regardless of the physics step size.

dt: Governing Temporal Integration

The dt parameter represents the length of one integration step in seconds. Stored under the key "dt" in the same ParamManager class (lines 59-62 of frontend/_session_param_.py), this value is later copied into the scene-level dictionary during encoding. In blender_addon/core/encoder/params.py (lines 68-71), the _encode_scene_params function packs this value into the CBOR payload sent to the native Rust solver.

A smaller dt yields finer temporal discretization, reducing numerical integration error and improving contact-resolution fidelity. The trade-off is linear: halving dt doubles the number of integration steps required for the same physical time, proportionally increasing CPU/GPU consumption.

step_size: The Blender UI Alias

In the Blender add-on interface, dt is exposed to users as step_size. Defined as a FloatProperty in blender_addon/ui/state.py (lines 248-259), this parameter behaves identically to dt when the solver runs inside Blender. Example scripts such as examples/blender/five-twist.py (lines 259-267) demonstrate programmatic usage: solver.param.step_size = 0.01.

When encoding the scene for the backend, the step_size value is mapped to the same "dt" field as the raw Python API parameter.

How Parameters Interact to Determine Simulation Behavior

Physical Duration Calculation

The total simulated time in seconds is the product of frames and dt (or step_size). Doubling frames while holding dt constant doubles the physical duration and doubles the mesh output count. Conversely, modifying dt changes the temporal resolution without affecting the number of output files.

Temporal vs. Output Resolution

Temporal resolution depends solely on dt, while output resolution depends solely on frames. If you halve dt but keep frames at 60, the solver performs roughly twice as many internal integration steps between each output frame. The animation retains 60 frames, but the underlying physics computation becomes more accurate.

Performance Trade-offs

Computational cost scales with inverse dt (finer steps require more work) and linearly with frames (more outputs require more I/O). To increase accuracy without exploding file sizes, lower dt while keeping frames modest. To speed up execution, either increase dt (accepting coarser integration) or reduce frames (accepting fewer output samples).

Implementation Details in the Source Code

The parameter flow follows a distinct pipeline:

  1. Storage: Values reside in ParamManager (frontend/_session_param_.py), accessible via session.param.set("frames", value) and session.param.set("dt", value).
  2. UI Mapping: In Blender, bpy.context.scene.ppf_solver_state.step_size feeds into the same system.
  3. Encoding: The _encode_scene_params function in blender_addon/core/encoder/params.py serializes dt into the CBOR message for the Rust backend.
  4. Execution: The Rust solver advances the simulation by dt for each internal step, writing a mesh file only at intervals determined by the total frames count.

Practical Configuration Examples

Python API (Jupyter/Headless)

from ppf_contact_solver import app

# Create scene and configure session parameters

scene = app.scene.create().add("sheet").at(0, 0, 0).build()
session = app.session.create(scene)

# 120 output frames, 8ms integration steps

session.param.set("frames", 120)
session.param.set("dt", 0.008)

# Run simulation

session.build().start(blocking=True)

Blender Add-on Scripting

import bpy
import ppf_contact_solver as pcs

# Configure via Blender UI state

bpy.context.scene.ppf_solver_state.step_size = 0.004  # 4ms steps

bpy.context.scene.ppf_solver_state.frame_count = 300  # 300 frames

# Transfer to session and run

session = pcs.app.session.create(pcs.scene)
session.param.set("frames", bpy.context.scene.ppf_solver_state.frame_count)
session.param.set("dt", bpy.context.scene.ppf_solver_state.step_size)
session.build().start(blocking=False)

Headless/CI Configuration

from ppf_contact_solver import app

scene = app.scene.create().add("rigid_body").at(0, 0, 0).build()
session = app.session.create(scene)

# 60 fps output with 10ms physics steps

session.param.set("frames", 60).set("dt", 0.01)
session.build().start(blocking=True)

Summary

  • frames determines how many mesh snapshots are written to disk, directly impacting I/O cost and memory usage.
  • dt (and its Blender alias step_size) controls the physics integration granularity; smaller values improve accuracy and stability but increase CPU/GPU time linearly.
  • Total simulation time equals frames × dt, meaning you can increase physical duration by raising either parameter, but only lowering dt improves temporal fidelity.
  • For high-fidelity contact dynamics,优先 (prioritize) small dt values, while frames should be set based on desired animation smoothness rather than physics requirements.

Frequently Asked Questions

What is the relationship between step_size and dt in the PPF Contact Solver?

step_size is the Blender UI alias for dt. Both represent the same physical value: the duration of one integration step in seconds. When using the Python API directly, you set dt via session.param.set("dt", value). When using the Blender add-on, you set step_size on the UI state object, which the encoder maps to the dt field before sending to the Rust backend.

How do I calculate the total simulation time for a session?

Multiply the frames parameter by the dt parameter (or step_size). For example, frames=120 and dt=0.008 yields 0.96 seconds of simulated physics time. This calculation determines how long the virtual event runs, independent of how long the computation takes on your hardware.

Why does lowering dt improve accuracy but slow down performance?

The solver advances the physics state by dt for every internal integration step. A smaller dt means more steps are required to advance the same amount of physical time, reducing numerical integration error and better resolving contact collisions. This increased step count requires more CPU/GPU cycles, causing execution time to scale roughly as 1/dt until memory or I/O bottlenecks become dominant.

Can I increase frames without making the simulation run longer?

No, if you keep dt constant. Since total time equals frames × dt, increasing frames necessarily extends the physical duration being simulated. To maintain the same physical duration while increasing output resolution, you must proportionally decrease dt, which increases computational cost. To change only the output frequency without changing physical duration or step size, you would need to modify the solver's subsampling behavior, which is not exposed through these three session parameters.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →