@@ -0,0 +1,298 @@
""" Streamlit dashboard application for physics simulation visualisation.
This module provides an interactive dashboard for visualising the physics
engine directly, demonstrating thermal-electrical coupling in real-time.
"""
from collections import deque
from dataclasses import dataclass , field
import streamlit as st
from py_dvt_ate . simulation . physics . engine import PhysicsEngine
# History buffer size for charts
HISTORY_SIZE = 500
@dataclass
class SimulationHistory :
""" Stores time series data for visualisation. """
time : deque [ float ] = field ( default_factory = lambda : deque ( maxlen = HISTORY_SIZE ) )
chamber_temp : deque [ float ] = field (
default_factory = lambda : deque ( maxlen = HISTORY_SIZE )
)
case_temp : deque [ float ] = field ( default_factory = lambda : deque ( maxlen = HISTORY_SIZE ) )
junction_temp : deque [ float ] = field (
default_factory = lambda : deque ( maxlen = HISTORY_SIZE )
)
output_voltage : deque [ float ] = field (
default_factory = lambda : deque ( maxlen = HISTORY_SIZE )
)
power_dissipation : deque [ float ] = field (
default_factory = lambda : deque ( maxlen = HISTORY_SIZE )
)
def init_session_state ( ) - > None :
""" Initialise Streamlit session state. """
if " engine " not in st . session_state :
st . session_state . engine = PhysicsEngine ( update_rate_hz = 100.0 )
if " history " not in st . session_state :
st . session_state . history = SimulationHistory ( )
if " running " not in st . session_state :
st . session_state . running = False
def step_simulation ( steps : int = 10 ) - > None :
""" Advance the simulation by the given number of steps. """
engine : PhysicsEngine = st . session_state . engine
history : SimulationHistory = st . session_state . history
for _ in range ( steps ) :
engine . step ( )
# Record current state in history
thermal = engine . get_thermal_state ( )
electrical = engine . get_electrical_state ( )
history . time . append ( thermal . timestamp )
history . chamber_temp . append ( thermal . chamber_temperature )
history . case_temp . append ( thermal . case_temperature )
history . junction_temp . append ( thermal . junction_temperature )
history . output_voltage . append ( electrical . output_voltage )
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 : .2f } s " )
def display_controls ( ) - > None :
""" Display simulation control panel in sidebar. """
engine : PhysicsEngine = st . session_state . engine
st . sidebar . header ( " Simulation Controls " )
# Start/Stop button
if st . session_state . running :
if st . sidebar . button ( " Stop Simulation " , type = " primary " , use_container_width = True ) :
st . session_state . running = False
st . rerun ( )
else :
if st . sidebar . button (
" Start Simulation " , type = " primary " , use_container_width = True
) :
st . session_state . running = True
st . rerun ( )
# Reset button
if st . sidebar . button ( " Reset " , use_container_width = True ) :
st . session_state . engine = PhysicsEngine ( update_rate_hz = 100.0 )
st . session_state . history = SimulationHistory ( )
st . session_state . running = False
st . rerun ( )
st . sidebar . divider ( )
# Temperature setpoint
st . sidebar . subheader ( " Thermal Chamber " )
temp_setpoint = st . sidebar . slider (
" Temperature Setpoint (°C) " ,
min_value = - 40.0 ,
max_value = 125.0 ,
value = 25.0 ,
step = 5.0 ,
key = " temp_setpoint " ,
)
engine . set_chamber_setpoint ( temp_setpoint )
st . sidebar . divider ( )
# Power supply controls
st . sidebar . subheader ( " Power Supply " )
input_voltage = st . sidebar . slider (
" Input Voltage (V) " ,
min_value = 0.0 ,
max_value = 12.0 ,
value = 5.0 ,
step = 0.1 ,
key = " input_voltage " ,
)
engine . set_input_voltage ( input_voltage )
output_enabled = st . sidebar . toggle (
" Output Enabled " ,
value = engine . is_output_enabled ,
key = " output_enabled " ,
)
engine . set_output_enabled ( output_enabled )
st . sidebar . divider ( )
# Load controls
st . sidebar . subheader ( " Electronic Load " )
load_current_ma = st . sidebar . slider (
" Load Current (mA) " ,
min_value = 0.0 ,
max_value = 500.0 ,
value = 100.0 ,
step = 10.0 ,
key = " load_current " ,
)
engine . set_load_current ( load_current_ma / 1000.0 )
def main ( ) - > None :
""" Main entry point for the Streamlit dashboard. """
st . set_page_config (
page_title = " py-dvt-ate Virtual Lab Bench " ,
page_icon = " 🔬 " ,
layout = " wide " ,
)
st . title ( " py-dvt-ate Virtual Lab Bench " )
st . markdown (
"""
Interactive physics simulation demonstrating coupled thermal-electrical
behaviour of an LDO voltage regulator.
"""
)
init_session_state ( )
# Sidebar controls
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 ( steps = 10 )
st . rerun ( )
if __name__ == " __main__ " :
main ( )