Use st.fragment for smooth dashboard updates
Replace st.rerun() with @st.fragment decorator to prevent full page reloads and eliminate UI greying out.
This commit is contained in:
@@ -18,8 +18,6 @@ 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
|
||||
@@ -89,124 +87,6 @@ def step_simulation() -> None:
|
||||
history.power_dissipation.append(electrical.power_dissipation)
|
||||
|
||||
|
||||
def display_thermal_chart() -> None:
|
||||
"""Display temperature chart."""
|
||||
history: SimulationHistory = st.session_state.history
|
||||
|
||||
if len(history.time) < 2:
|
||||
st.info("Start the simulation to see temperature data")
|
||||
return
|
||||
|
||||
chart_data = {
|
||||
"Time (s)": list(history.time),
|
||||
"Chamber": list(history.chamber_temp),
|
||||
"Case": list(history.case_temp),
|
||||
"Junction": list(history.junction_temp),
|
||||
}
|
||||
|
||||
st.line_chart(
|
||||
chart_data,
|
||||
x="Time (s)",
|
||||
y=["Chamber", "Case", "Junction"],
|
||||
color=["#1f77b4", "#ff7f0e", "#d62728"],
|
||||
)
|
||||
|
||||
|
||||
def display_self_heating_panel() -> None:
|
||||
"""Display self-heating demonstration panel."""
|
||||
engine: PhysicsEngine = st.session_state.engine
|
||||
history: SimulationHistory = st.session_state.history
|
||||
|
||||
thermal = engine.get_thermal_state()
|
||||
electrical = engine.get_electrical_state()
|
||||
|
||||
# Calculate temperature rises
|
||||
delta_t_jc = thermal.junction_temperature - thermal.case_temperature
|
||||
delta_t_ca = thermal.case_temperature - thermal.chamber_temperature
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.markdown("#### Self-Heating Analysis")
|
||||
|
||||
# Display thermal resistance info
|
||||
st.markdown(
|
||||
f"""
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Junction-Case Rise (ΔT_jc) | **{delta_t_jc:.2f} °C** |
|
||||
| Case-Ambient Rise (ΔT_ca) | **{delta_t_ca:.2f} °C** |
|
||||
| Power Dissipation | {electrical.power_dissipation * 1000:.1f} mW |
|
||||
| θ_jc (junction-case) | 15 °C/W |
|
||||
| θ_ca (case-ambient) | 5 °C/W |
|
||||
"""
|
||||
)
|
||||
|
||||
st.markdown(
|
||||
"""
|
||||
**Thermal Coupling:** The junction temperature rises above the case
|
||||
temperature due to power dissipation. This is governed by:
|
||||
|
||||
`T_junction = T_case + P_diss × θ_jc`
|
||||
|
||||
Try increasing the load current or input voltage to see
|
||||
self-heating effects!
|
||||
"""
|
||||
)
|
||||
|
||||
with col2:
|
||||
st.markdown("#### Power Dissipation")
|
||||
|
||||
if len(history.time) < 2:
|
||||
st.info("Start the simulation to see power data")
|
||||
return
|
||||
|
||||
power_data = {
|
||||
"Time (s)": list(history.time),
|
||||
"Power (mW)": [p * 1000 for p in history.power_dissipation],
|
||||
}
|
||||
|
||||
st.line_chart(
|
||||
power_data,
|
||||
x="Time (s)",
|
||||
y="Power (mW)",
|
||||
color="#2ca02c",
|
||||
)
|
||||
|
||||
|
||||
def display_current_state() -> None:
|
||||
"""Display current simulation state metrics."""
|
||||
engine: PhysicsEngine = st.session_state.engine
|
||||
thermal = engine.get_thermal_state()
|
||||
electrical = engine.get_electrical_state()
|
||||
|
||||
col1, col2, col3, col4 = st.columns(4)
|
||||
|
||||
with col1:
|
||||
st.metric("Chamber Temp", f"{thermal.chamber_temperature:.2f} °C")
|
||||
with col2:
|
||||
st.metric("Case Temp", f"{thermal.case_temperature:.2f} °C")
|
||||
with col3:
|
||||
st.metric("Junction Temp", f"{thermal.junction_temperature:.2f} °C")
|
||||
with col4:
|
||||
st.metric("Output Voltage", f"{electrical.output_voltage:.4f} V")
|
||||
|
||||
col5, col6, col7, col8 = st.columns(4)
|
||||
|
||||
with col5:
|
||||
st.metric("Input Voltage", f"{electrical.input_voltage:.2f} V")
|
||||
with col6:
|
||||
st.metric("Load Current", f"{electrical.load_current * 1000:.1f} mA")
|
||||
with col7:
|
||||
st.metric("Power Diss.", f"{electrical.power_dissipation * 1000:.2f} mW")
|
||||
with col8:
|
||||
st.metric(
|
||||
"Sim Time",
|
||||
f"{engine.simulation_time:.1f} s",
|
||||
delta=f"{st.session_state.time_multiplier:.0f}× speed",
|
||||
)
|
||||
|
||||
|
||||
def display_controls() -> None:
|
||||
"""Display simulation control panel in sidebar."""
|
||||
engine: PhysicsEngine = st.session_state.engine
|
||||
@@ -215,7 +95,9 @@ def display_controls() -> None:
|
||||
|
||||
# Start/Stop button
|
||||
if st.session_state.running:
|
||||
if st.sidebar.button("Stop Simulation", type="primary", use_container_width=True):
|
||||
if st.sidebar.button(
|
||||
"Stop Simulation", type="primary", use_container_width=True
|
||||
):
|
||||
st.session_state.running = False
|
||||
st.rerun()
|
||||
else:
|
||||
@@ -223,6 +105,7 @@ def display_controls() -> None:
|
||||
"Start Simulation", type="primary", use_container_width=True
|
||||
):
|
||||
st.session_state.running = True
|
||||
st.session_state.last_update = time.time()
|
||||
st.rerun()
|
||||
|
||||
# Reset button
|
||||
@@ -241,7 +124,7 @@ def display_controls() -> None:
|
||||
"Time Multiplier",
|
||||
options=[1, 2, 5, 10, 20, 50, 100],
|
||||
value=int(st.session_state.time_multiplier),
|
||||
format_func=lambda x: f"{x}×",
|
||||
format_func=lambda x: f"{x}x",
|
||||
key="time_mult_slider",
|
||||
)
|
||||
st.session_state.time_multiplier = float(time_mult)
|
||||
@@ -252,7 +135,7 @@ def display_controls() -> None:
|
||||
# Temperature setpoint
|
||||
st.sidebar.subheader("Thermal Chamber")
|
||||
temp_setpoint = st.sidebar.slider(
|
||||
"Temperature Setpoint (°C)",
|
||||
"Temperature Setpoint (C)",
|
||||
min_value=-40.0,
|
||||
max_value=125.0,
|
||||
value=25.0,
|
||||
@@ -297,6 +180,117 @@ def display_controls() -> None:
|
||||
engine.set_load_current(load_current_ma / 1000.0)
|
||||
|
||||
|
||||
@st.fragment(run_every=0.1)
|
||||
def simulation_display() -> None:
|
||||
"""Fragment that displays and updates simulation state."""
|
||||
engine: PhysicsEngine = st.session_state.engine
|
||||
history: SimulationHistory = st.session_state.history
|
||||
|
||||
# Step simulation if running
|
||||
if st.session_state.running:
|
||||
step_simulation()
|
||||
|
||||
# Get current state
|
||||
thermal = engine.get_thermal_state()
|
||||
electrical = engine.get_electrical_state()
|
||||
|
||||
# Current state metrics
|
||||
st.subheader("Current State")
|
||||
col1, col2, col3, col4 = st.columns(4)
|
||||
|
||||
with col1:
|
||||
st.metric("Chamber Temp", f"{thermal.chamber_temperature:.2f} C")
|
||||
with col2:
|
||||
st.metric("Case Temp", f"{thermal.case_temperature:.2f} C")
|
||||
with col3:
|
||||
st.metric("Junction Temp", f"{thermal.junction_temperature:.2f} C")
|
||||
with col4:
|
||||
st.metric("Output Voltage", f"{electrical.output_voltage:.4f} V")
|
||||
|
||||
col5, col6, col7, col8 = st.columns(4)
|
||||
|
||||
with col5:
|
||||
st.metric("Input Voltage", f"{electrical.input_voltage:.2f} V")
|
||||
with col6:
|
||||
st.metric("Load Current", f"{electrical.load_current * 1000:.1f} mA")
|
||||
with col7:
|
||||
st.metric("Power Diss.", f"{electrical.power_dissipation * 1000:.2f} mW")
|
||||
with col8:
|
||||
status = "Running" if st.session_state.running else "Stopped"
|
||||
st.metric(
|
||||
"Sim Time",
|
||||
f"{engine.simulation_time:.1f} s",
|
||||
delta=f"{status} @ {st.session_state.time_multiplier:.0f}x",
|
||||
)
|
||||
|
||||
# Temperature chart
|
||||
st.subheader("Temperature History")
|
||||
if len(history.time) < 2:
|
||||
st.info("Start the simulation to see temperature data")
|
||||
else:
|
||||
chart_data = {
|
||||
"Time (s)": list(history.time),
|
||||
"Chamber": list(history.chamber_temp),
|
||||
"Case": list(history.case_temp),
|
||||
"Junction": list(history.junction_temp),
|
||||
}
|
||||
st.line_chart(
|
||||
chart_data,
|
||||
x="Time (s)",
|
||||
y=["Chamber", "Case", "Junction"],
|
||||
color=["#1f77b4", "#ff7f0e", "#d62728"],
|
||||
)
|
||||
|
||||
# Self-heating demonstration
|
||||
st.subheader("Self-Heating Demonstration")
|
||||
|
||||
delta_t_jc = thermal.junction_temperature - thermal.case_temperature
|
||||
delta_t_ca = thermal.case_temperature - thermal.chamber_temperature
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.markdown("#### Self-Heating Analysis")
|
||||
st.markdown(
|
||||
f"""
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Junction-Case Rise (dT_jc) | **{delta_t_jc:.2f} C** |
|
||||
| Case-Ambient Rise (dT_ca) | **{delta_t_ca:.2f} C** |
|
||||
| Power Dissipation | {electrical.power_dissipation * 1000:.1f} mW |
|
||||
| theta_jc (junction-case) | 15 C/W |
|
||||
| theta_ca (case-ambient) | 5 C/W |
|
||||
"""
|
||||
)
|
||||
st.markdown(
|
||||
"""
|
||||
**Thermal Coupling:** The junction temperature rises above the case
|
||||
temperature due to power dissipation. This is governed by:
|
||||
|
||||
`T_junction = T_case + P_diss x theta_jc`
|
||||
|
||||
Try increasing the load current or input voltage to see
|
||||
self-heating effects!
|
||||
"""
|
||||
)
|
||||
|
||||
with col2:
|
||||
st.markdown("#### Power Dissipation")
|
||||
if len(history.time) < 2:
|
||||
st.info("Start the simulation to see power data")
|
||||
else:
|
||||
power_data = {
|
||||
"Time (s)": list(history.time),
|
||||
"Power (mW)": [p * 1000 for p in history.power_dissipation],
|
||||
}
|
||||
st.line_chart(
|
||||
power_data,
|
||||
x="Time (s)",
|
||||
y="Power (mW)",
|
||||
color="#2ca02c",
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main entry point for the Streamlit dashboard."""
|
||||
st.set_page_config(
|
||||
@@ -315,26 +309,11 @@ def main() -> None:
|
||||
|
||||
init_session_state()
|
||||
|
||||
# Sidebar controls
|
||||
# Sidebar controls (static - doesn't need fragment)
|
||||
display_controls()
|
||||
|
||||
# Current state display
|
||||
st.subheader("Current State")
|
||||
display_current_state()
|
||||
|
||||
# Temperature chart
|
||||
st.subheader("Temperature History")
|
||||
display_thermal_chart()
|
||||
|
||||
# Self-heating demonstration
|
||||
st.subheader("Self-Heating Demonstration")
|
||||
display_self_heating_panel()
|
||||
|
||||
# Auto-refresh when running
|
||||
if st.session_state.running:
|
||||
step_simulation()
|
||||
time.sleep(0.05) # Small delay to prevent UI thrashing
|
||||
st.rerun()
|
||||
# Dynamic simulation display (uses fragment for smooth updates)
|
||||
simulation_display()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user