Add physics visualisation panel
This commit is contained in:
@@ -4,8 +4,119 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
import streamlit as st
|
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_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 main() -> None:
|
def main() -> None:
|
||||||
"""Main entry point for the Streamlit dashboard."""
|
"""Main entry point for the Streamlit dashboard."""
|
||||||
@@ -23,6 +134,21 @@ def main() -> None:
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
init_session_state()
|
||||||
|
|
||||||
|
# Current state display
|
||||||
|
st.subheader("Current State")
|
||||||
|
display_current_state()
|
||||||
|
|
||||||
|
# Temperature chart
|
||||||
|
st.subheader("Temperature History")
|
||||||
|
display_thermal_chart()
|
||||||
|
|
||||||
|
# Auto-refresh when running
|
||||||
|
if st.session_state.running:
|
||||||
|
step_simulation(steps=10)
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user