Fix dashboard simulation speed with time multiplier
- Add time multiplier control (1× to 100× speed) - Calculate steps based on real elapsed time - Add 50ms delay to prevent UI thrashing - Display current speed in Sim Time metric
This commit is contained in:
@@ -4,6 +4,7 @@ This module provides an interactive dashboard for visualising the physics
|
|||||||
engine directly, demonstrating thermal-electrical coupling in real-time.
|
engine directly, demonstrating thermal-electrical coupling in real-time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
@@ -15,6 +16,11 @@ from py_dvt_ate.simulation.physics.engine import PhysicsEngine
|
|||||||
# History buffer size for charts
|
# History buffer size for charts
|
||||||
HISTORY_SIZE = 500
|
HISTORY_SIZE = 500
|
||||||
|
|
||||||
|
# Simulation speed settings
|
||||||
|
DEFAULT_TIME_MULTIPLIER = 10.0 # 10x faster than real-time
|
||||||
|
STEPS_PER_UPDATE = 100 # Steps per UI refresh
|
||||||
|
UPDATE_INTERVAL_MS = 100 # Target UI refresh rate
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SimulationHistory:
|
class SimulationHistory:
|
||||||
@@ -44,12 +50,29 @@ def init_session_state() -> None:
|
|||||||
st.session_state.history = SimulationHistory()
|
st.session_state.history = SimulationHistory()
|
||||||
if "running" not in st.session_state:
|
if "running" not in st.session_state:
|
||||||
st.session_state.running = False
|
st.session_state.running = False
|
||||||
|
if "time_multiplier" not in st.session_state:
|
||||||
|
st.session_state.time_multiplier = DEFAULT_TIME_MULTIPLIER
|
||||||
|
if "last_update" not in st.session_state:
|
||||||
|
st.session_state.last_update = time.time()
|
||||||
|
|
||||||
|
|
||||||
def step_simulation(steps: int = 10) -> None:
|
def step_simulation() -> None:
|
||||||
"""Advance the simulation by the given number of steps."""
|
"""Advance the simulation based on elapsed real time and multiplier."""
|
||||||
engine: PhysicsEngine = st.session_state.engine
|
engine: PhysicsEngine = st.session_state.engine
|
||||||
history: SimulationHistory = st.session_state.history
|
history: SimulationHistory = st.session_state.history
|
||||||
|
multiplier: float = st.session_state.time_multiplier
|
||||||
|
|
||||||
|
# Calculate how much simulation time to advance
|
||||||
|
current_time = time.time()
|
||||||
|
elapsed_real = current_time - st.session_state.last_update
|
||||||
|
st.session_state.last_update = current_time
|
||||||
|
|
||||||
|
# Simulation time to advance (capped to prevent huge jumps)
|
||||||
|
sim_time_to_advance = min(elapsed_real * multiplier, 2.0)
|
||||||
|
|
||||||
|
# Calculate number of steps needed
|
||||||
|
steps = int(sim_time_to_advance / engine.dt)
|
||||||
|
steps = max(1, min(steps, 1000)) # Clamp between 1 and 1000 steps
|
||||||
|
|
||||||
for _ in range(steps):
|
for _ in range(steps):
|
||||||
engine.step()
|
engine.step()
|
||||||
@@ -177,7 +200,11 @@ def display_current_state() -> None:
|
|||||||
with col7:
|
with col7:
|
||||||
st.metric("Power Diss.", f"{electrical.power_dissipation * 1000:.2f} mW")
|
st.metric("Power Diss.", f"{electrical.power_dissipation * 1000:.2f} mW")
|
||||||
with col8:
|
with col8:
|
||||||
st.metric("Sim Time", f"{engine.simulation_time:.2f} s")
|
st.metric(
|
||||||
|
"Sim Time",
|
||||||
|
f"{engine.simulation_time:.1f} s",
|
||||||
|
delta=f"{st.session_state.time_multiplier:.0f}× speed",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def display_controls() -> None:
|
def display_controls() -> None:
|
||||||
@@ -203,10 +230,25 @@ def display_controls() -> None:
|
|||||||
st.session_state.engine = PhysicsEngine(update_rate_hz=100.0)
|
st.session_state.engine = PhysicsEngine(update_rate_hz=100.0)
|
||||||
st.session_state.history = SimulationHistory()
|
st.session_state.history = SimulationHistory()
|
||||||
st.session_state.running = False
|
st.session_state.running = False
|
||||||
|
st.session_state.last_update = time.time()
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
st.sidebar.divider()
|
st.sidebar.divider()
|
||||||
|
|
||||||
|
# Time multiplier
|
||||||
|
st.sidebar.subheader("Simulation Speed")
|
||||||
|
time_mult = st.sidebar.select_slider(
|
||||||
|
"Time Multiplier",
|
||||||
|
options=[1, 2, 5, 10, 20, 50, 100],
|
||||||
|
value=int(st.session_state.time_multiplier),
|
||||||
|
format_func=lambda x: f"{x}×",
|
||||||
|
key="time_mult_slider",
|
||||||
|
)
|
||||||
|
st.session_state.time_multiplier = float(time_mult)
|
||||||
|
st.sidebar.caption(f"1 real second = {time_mult} simulation seconds")
|
||||||
|
|
||||||
|
st.sidebar.divider()
|
||||||
|
|
||||||
# Temperature setpoint
|
# Temperature setpoint
|
||||||
st.sidebar.subheader("Thermal Chamber")
|
st.sidebar.subheader("Thermal Chamber")
|
||||||
temp_setpoint = st.sidebar.slider(
|
temp_setpoint = st.sidebar.slider(
|
||||||
@@ -290,7 +332,8 @@ def main() -> None:
|
|||||||
|
|
||||||
# Auto-refresh when running
|
# Auto-refresh when running
|
||||||
if st.session_state.running:
|
if st.session_state.running:
|
||||||
step_simulation(steps=10)
|
step_simulation()
|
||||||
|
time.sleep(0.05) # Small delay to prevent UI thrashing
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user