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

> Discover how PPF contact solver session parameters frames, dt, and step_size affect simulation accuracy and performance. Optimize your simulations for better results and efficiency.

- Repository: [ZOZO, Inc./ppf-contact-solver](https://github.com/st-tech/ppf-contact-solver)
- Tags: performance
- Published: 2026-05-27

---

**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`](https://github.com/st-tech/ppf-contact-solver/blob/main/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`](https://github.com/st-tech/ppf-contact-solver/blob/main/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`](https://github.com/st-tech/ppf-contact-solver/blob/main/frontend/_session_param_.py)), this value is later copied into the scene-level dictionary during encoding. In [`blender_addon/core/encoder/params.py`](https://github.com/st-tech/ppf-contact-solver/blob/main/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`](https://github.com/st-tech/ppf-contact-solver/blob/main/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`](https://github.com/st-tech/ppf-contact-solver/blob/main/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`](https://github.com/st-tech/ppf-contact-solver/blob/main/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`](https://github.com/st-tech/ppf-contact-solver/blob/main/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)

```python
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

```python
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

```python
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.