|
|
import streamlit as st |
|
|
import numpy as np |
|
|
import matplotlib.pyplot as plt |
|
|
from matplotlib.patches import Rectangle, Circle, FancyArrowPatch |
|
|
|
|
|
st.set_page_config(page_title="Circuit Analysis Simulator", layout="wide") |
|
|
|
|
|
st.title("Interactive Circuit Analysis Simulator") |
|
|
st.write("Explore basic circuit concepts including Ohm's Law, series and parallel circuits.") |
|
|
|
|
|
|
|
|
page = st.sidebar.radio("Choose a Simulation", |
|
|
["Ohm's Law", "Series Circuit", "Parallel Circuit"]) |
|
|
|
|
|
if page == "Ohm's Law": |
|
|
st.header("Ohm's Law: V = I × R") |
|
|
st.write(""" |
|
|
Ohm's Law describes the relationship between voltage (V), current (I), and resistance (R). |
|
|
Adjust the sliders to see how changing one parameter affects the others. |
|
|
""") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
with col1: |
|
|
|
|
|
calculation_mode = st.radio( |
|
|
"What do you want to calculate?", |
|
|
["Voltage", "Current", "Resistance"] |
|
|
) |
|
|
|
|
|
if calculation_mode == "Voltage": |
|
|
current = st.slider("Current (A)", 0.1, 10.0, 1.0, 0.1) |
|
|
resistance = st.slider("Resistance (Ω)", 1.0, 100.0, 10.0, 1.0) |
|
|
voltage = current * resistance |
|
|
st.success(f"Calculated Voltage: {voltage:.2f} V") |
|
|
|
|
|
elif calculation_mode == "Current": |
|
|
voltage = st.slider("Voltage (V)", 1.0, 220.0, 12.0, 1.0) |
|
|
resistance = st.slider("Resistance (Ω)", 1.0, 100.0, 10.0, 1.0) |
|
|
current = voltage / resistance if resistance != 0 else float('inf') |
|
|
st.success(f"Calculated Current: {current:.2f} A") |
|
|
|
|
|
else: |
|
|
voltage = st.slider("Voltage (V)", 1.0, 220.0, 12.0, 1.0) |
|
|
current = st.slider("Current (A)", 0.1, 10.0, 1.0, 0.1) |
|
|
resistance = voltage / current if current != 0 else float('inf') |
|
|
st.success(f"Calculated Resistance: {resistance:.2f} Ω") |
|
|
|
|
|
with col2: |
|
|
|
|
|
fig, ax = plt.subplots(figsize=(8, 6)) |
|
|
|
|
|
|
|
|
battery_height = 1.5 |
|
|
battery_x = 1 |
|
|
battery_y = 3 |
|
|
|
|
|
|
|
|
ax.plot([battery_x, battery_x + 6], [battery_y + battery_height, battery_y + battery_height], 'k-', lw=2) |
|
|
|
|
|
ax.plot([battery_x, battery_x + 6], [battery_y, battery_y], 'k-', lw=2) |
|
|
|
|
|
|
|
|
ax.plot([battery_x, battery_x], [battery_y, battery_y + battery_height], 'k-', lw=2) |
|
|
ax.plot([battery_x + 0.2, battery_x + 0.2], [battery_y + 0.25, battery_y + battery_height - 0.25], 'k-', lw=4) |
|
|
ax.plot([battery_x + 0.5, battery_x + 0.5], [battery_y + 0.5, battery_y + battery_height - 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
res_x = battery_x + 3 |
|
|
res_y = battery_y + battery_height |
|
|
zigzag_width = 1.5 |
|
|
zigzag_height = 0.5 |
|
|
zigzag_pts = 7 |
|
|
x_pts = np.linspace(res_x, res_x + zigzag_width, zigzag_pts) |
|
|
y_pts = np.array([0, 1, 0, 1, 0, 1, 0]) * zigzag_height + res_y |
|
|
ax.plot(x_pts, y_pts, 'k-', lw=2) |
|
|
|
|
|
|
|
|
if calculation_mode == "Current" or calculation_mode == "Resistance": |
|
|
current_value = current |
|
|
else: |
|
|
current_value = current |
|
|
|
|
|
|
|
|
arrow_scale = min(1, current_value / 5) |
|
|
arrow_width = 0.15 * (0.5 + arrow_scale) |
|
|
arrow = FancyArrowPatch((battery_x + 4.5, battery_y + 0.5), |
|
|
(battery_x + 3, battery_y + 0.5), |
|
|
arrowstyle='->', |
|
|
color='blue', |
|
|
lw=2, |
|
|
mutation_scale=20) |
|
|
ax.add_patch(arrow) |
|
|
ax.text(battery_x + 3.8, battery_y + 0.3, f"{current:.1f} A", color='blue') |
|
|
|
|
|
|
|
|
if calculation_mode == "Voltage" or calculation_mode == "Resistance": |
|
|
voltage_value = voltage |
|
|
else: |
|
|
voltage_value = voltage |
|
|
|
|
|
ax.text(battery_x + 0.7, battery_y + 0.75, f"{voltage:.1f} V", color='red') |
|
|
|
|
|
|
|
|
if calculation_mode == "Resistance" or calculation_mode == "Voltage": |
|
|
resistance_value = resistance |
|
|
else: |
|
|
resistance_value = resistance |
|
|
|
|
|
ax.text(res_x + 0.2, res_y + 0.75, f"{resistance:.1f} Ω", color='green') |
|
|
|
|
|
ax.set_xlim(0, 8) |
|
|
ax.set_ylim(1, 6) |
|
|
ax.axis('equal') |
|
|
ax.axis('off') |
|
|
|
|
|
st.pyplot(fig) |
|
|
|
|
|
|
|
|
st.write("### Ohm's Law Formula") |
|
|
if calculation_mode == "Voltage": |
|
|
st.latex(r"V = I \times R") |
|
|
st.latex(f"V = {current:.2f} \, A \times {resistance:.2f} \, \Omega = {voltage:.2f} \, V") |
|
|
elif calculation_mode == "Current": |
|
|
st.latex(r"I = \frac{V}{R}") |
|
|
st.latex(f"I = \\frac{{{voltage:.2f} \, V}}{{{resistance:.2f} \, \Omega}} = {current:.2f} \, A") |
|
|
else: |
|
|
st.latex(r"R = \frac{V}{I}") |
|
|
st.latex(f"R = \\frac{{{voltage:.2f} \, V}}{{{current:.2f} \, A}} = {resistance:.2f} \, \Omega") |
|
|
|
|
|
elif page == "Series Circuit": |
|
|
st.header("Series Circuit Analysis") |
|
|
st.write(""" |
|
|
In a series circuit, the same current flows through each component, and the total voltage is the sum of the voltages across each component. |
|
|
""") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
with col1: |
|
|
|
|
|
voltage_source = st.slider("Voltage Source (V)", 1.0, 24.0, 12.0, 0.1) |
|
|
|
|
|
st.subheader("Resistors in Series") |
|
|
num_resistors = st.slider("Number of Resistors", 1, 5, 3) |
|
|
|
|
|
resistances = [] |
|
|
for i in range(num_resistors): |
|
|
r = st.slider(f"R{i+1} (Ω)", 1.0, 100.0, 10.0 * (i+1), 1.0, key=f"series_r{i}") |
|
|
resistances.append(r) |
|
|
|
|
|
|
|
|
total_resistance = sum(resistances) |
|
|
current = voltage_source / total_resistance if total_resistance > 0 else 0 |
|
|
voltage_drops = [current * r for r in resistances] |
|
|
power_dissipations = [current**2 * r for r in resistances] |
|
|
|
|
|
st.subheader("Circuit Analysis Results") |
|
|
st.write(f"Total Resistance: {total_resistance:.2f} Ω") |
|
|
st.write(f"Current: {current:.2f} A") |
|
|
|
|
|
|
|
|
for i in range(num_resistors): |
|
|
st.write(f"R{i+1}: Voltage Drop = {voltage_drops[i]:.2f} V, Power = {power_dissipations[i]:.2f} W") |
|
|
|
|
|
with col2: |
|
|
|
|
|
fig, ax = plt.subplots(figsize=(8, 6)) |
|
|
|
|
|
|
|
|
start_x, start_y = 1, 5 |
|
|
|
|
|
|
|
|
battery_height = 1.5 |
|
|
battery_x = start_x |
|
|
battery_y = start_y - battery_height/2 |
|
|
|
|
|
|
|
|
ax.plot([battery_x, battery_x], [battery_y, battery_y + battery_height], 'k-', lw=2) |
|
|
ax.plot([battery_x + 0.2, battery_x + 0.2], [battery_y + 0.25, battery_y + battery_height - 0.25], 'k-', lw=4) |
|
|
ax.plot([battery_x + 0.5, battery_x + 0.5], [battery_y + 0.5, battery_y + battery_height - 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
ax.text(battery_x + 0.7, battery_y + 0.75, f"{voltage_source:.1f} V", color='red') |
|
|
|
|
|
|
|
|
line_length = 10 / (num_resistors + 1) |
|
|
|
|
|
|
|
|
ax.plot([battery_x + 0.5, battery_x + line_length], [battery_y + battery_height - 0.5, battery_y + battery_height - 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
for i in range(num_resistors): |
|
|
res_x = battery_x + line_length * (i + 1) |
|
|
res_y = battery_y + battery_height - 0.5 |
|
|
|
|
|
|
|
|
zigzag_width = 1.0 |
|
|
zigzag_height = 0.5 |
|
|
zigzag_pts = 7 |
|
|
x_pts = np.linspace(res_x, res_x + zigzag_width, zigzag_pts) |
|
|
y_pts = np.array([0, 1, 0, 1, 0, 1, 0]) * zigzag_height + res_y |
|
|
ax.plot(x_pts, y_pts, 'k-', lw=2) |
|
|
|
|
|
|
|
|
ax.text(res_x + 0.2, res_y + 0.8, f"R{i+1}\n{resistances[i]:.1f}Ω\n{voltage_drops[i]:.1f}V", fontsize=8) |
|
|
|
|
|
|
|
|
if i < num_resistors - 1: |
|
|
ax.plot([res_x + zigzag_width, res_x + line_length], [res_y, res_y], 'k-', lw=2) |
|
|
|
|
|
|
|
|
ax.plot([battery_x + line_length * (num_resistors + 1) - line_length + zigzag_width, battery_x + 0.5], |
|
|
[res_y, battery_y + 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
arrow = FancyArrowPatch((battery_x + 0.5, battery_y + 0.5), |
|
|
(battery_x + line_length/2, battery_y + 0.5), |
|
|
arrowstyle='->', |
|
|
color='blue', |
|
|
lw=2, |
|
|
mutation_scale=15) |
|
|
ax.add_patch(arrow) |
|
|
ax.text(battery_x + line_length/4, battery_y + 0.2, f"{current:.1f}A", color='blue', fontsize=9) |
|
|
|
|
|
ax.set_xlim(0, 12) |
|
|
ax.set_ylim(2, 8) |
|
|
ax.axis('equal') |
|
|
ax.axis('off') |
|
|
|
|
|
st.pyplot(fig) |
|
|
|
|
|
|
|
|
st.write("### Series Circuit Formulas") |
|
|
st.latex(r"R_{total} = R_1 + R_2 + ... + R_n") |
|
|
st.latex(f"R_{{total}} = {' + '.join([f'{r:.1f}' for r in resistances])} = {total_resistance:.2f} \, \Omega") |
|
|
st.latex(r"I = \frac{V_{source}}{R_{total}}") |
|
|
st.latex(f"I = \\frac{{{voltage_source:.2f}}}{{{total_resistance:.2f}}} = {current:.2f} \, A") |
|
|
st.latex(r"V_n = I \times R_n") |
|
|
|
|
|
elif page == "Parallel Circuit": |
|
|
st.header("Parallel Circuit Analysis") |
|
|
st.write(""" |
|
|
In a parallel circuit, each component has the same voltage across it, and the total current is the sum of the currents through each path. |
|
|
""") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
with col1: |
|
|
|
|
|
voltage_source = st.slider("Voltage Source (V)", 1.0, 24.0, 12.0, 0.1) |
|
|
|
|
|
st.subheader("Resistors in Parallel") |
|
|
num_resistors = st.slider("Number of Resistors", 1, 5, 3) |
|
|
|
|
|
resistances = [] |
|
|
for i in range(num_resistors): |
|
|
r = st.slider(f"R{i+1} (Ω)", 1.0, 100.0, float(10.0 * (i+1)), 1.0, key=f"parallel_r{i}") |
|
|
resistances.append(r) |
|
|
|
|
|
|
|
|
inverse_resistance_sum = sum(1/r for r in resistances) if resistances else 0 |
|
|
total_resistance = 1/inverse_resistance_sum if inverse_resistance_sum > 0 else float('inf') |
|
|
|
|
|
branch_currents = [voltage_source / r for r in resistances] |
|
|
total_current = sum(branch_currents) |
|
|
power_dissipations = [(voltage_source ** 2) / r for r in resistances] |
|
|
total_power = voltage_source * total_current |
|
|
|
|
|
st.subheader("Circuit Analysis Results") |
|
|
st.write(f"Total Resistance: {total_resistance:.2f} Ω") |
|
|
st.write(f"Total Current: {total_current:.2f} A") |
|
|
|
|
|
|
|
|
for i in range(num_resistors): |
|
|
st.write(f"R{i+1}: Current = {branch_currents[i]:.2f} A, Power = {power_dissipations[i]:.2f} W") |
|
|
|
|
|
with col2: |
|
|
|
|
|
fig, ax = plt.subplots(figsize=(8, 6)) |
|
|
|
|
|
|
|
|
start_x, start_y = 1, 5 |
|
|
end_x = 9 |
|
|
|
|
|
|
|
|
battery_height = 1.5 |
|
|
battery_x = start_x |
|
|
battery_y = start_y - battery_height/2 |
|
|
|
|
|
|
|
|
ax.plot([battery_x, battery_x], [battery_y, battery_y + battery_height], 'k-', lw=2) |
|
|
ax.plot([battery_x + 0.2, battery_x + 0.2], [battery_y + 0.25, battery_y + battery_height - 0.25], 'k-', lw=4) |
|
|
ax.plot([battery_x + 0.5, battery_x + 0.5], [battery_y + 0.5, battery_y + battery_height - 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
ax.text(battery_x + 0.7, battery_y + 0.75, f"{voltage_source:.1f} V", color='red') |
|
|
|
|
|
|
|
|
ax.plot([battery_x + 0.5, end_x], [battery_y + battery_height - 0.5, battery_y + battery_height - 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
ax.plot([battery_x + 0.5, end_x], [battery_y + 0.5, battery_y + 0.5], 'k-', lw=2) |
|
|
|
|
|
|
|
|
arrow = FancyArrowPatch((battery_x + 1.5, battery_y + battery_height - 0.5), |
|
|
(battery_x + 2.5, battery_y + battery_height - 0.5), |
|
|
arrowstyle='->', |
|
|
color='blue', |
|
|
lw=2, |
|
|
mutation_scale=15) |
|
|
ax.add_patch(arrow) |
|
|
ax.text(battery_x + 1.5, battery_y + battery_height - 0.8, f"{total_current:.1f}A", color='blue') |
|
|
|
|
|
|
|
|
spacing = 5.0 / (num_resistors + 1) |
|
|
for i in range(num_resistors): |
|
|
res_x = battery_x + 2.5 + i * spacing |
|
|
res_y_top = battery_y + battery_height - 0.5 |
|
|
res_y_bottom = battery_y + 0.5 |
|
|
|
|
|
|
|
|
ax.plot([res_x, res_x], [res_y_top, res_y_top - 1], 'k-', lw=2) |
|
|
ax.plot([res_x, res_x], [res_y_bottom, res_y_bottom + 1], 'k-', lw=2) |
|
|
|
|
|
|
|
|
zigzag_height = 1.0 |
|
|
zigzag_width = 0.5 |
|
|
zigzag_pts = 7 |
|
|
y_pts = np.linspace(res_y_top - 1, res_y_bottom + 1, zigzag_pts) |
|
|
x_pts = np.array([0, 1, 0, 1, 0, 1, 0]) * zigzag_width + res_x |
|
|
ax.plot(x_pts, y_pts, 'k-', lw=2) |
|
|
|
|
|
|
|
|
branch_arrow = FancyArrowPatch((res_x - 0.3, res_y_top - 0.5), |
|
|
(res_x - 0.3, res_y_top - 1.5), |
|
|
arrowstyle='->', |
|
|
color='blue', |
|
|
lw=2, |
|
|
mutation_scale=15) |
|
|
ax.add_patch(branch_arrow) |
|
|
|
|
|
|
|
|
ax.text(res_x + 0.6, res_y_top - 1.5, f"R{i+1}\n{resistances[i]:.1f}Ω\n{branch_currents[i]:.2f}A", fontsize=8) |
|
|
|
|
|
ax.set_xlim(0, 12) |
|
|
ax.set_ylim(2, 8) |
|
|
ax.axis('equal') |
|
|
ax.axis('off') |
|
|
|
|
|
st.pyplot(fig) |
|
|
|
|
|
|
|
|
st.write("### Parallel Circuit Formulas") |
|
|
st.latex(r"\frac{1}{R_{total}} = \frac{1}{R_1} + \frac{1}{R_2} + ... + \frac{1}{R_n}") |
|
|
|
|
|
|
|
|
fractions_parts = [] |
|
|
for r in resistances: |
|
|
fractions_parts.append(r"\frac{1}{" + f"{r:.1f}" + "}") |
|
|
fractions_text = " + ".join(fractions_parts) |
|
|
|
|
|
st.latex(r"\frac{1}{R_{total}} = " + fractions_text + f" = {inverse_resistance_sum:.4f}") |
|
|
st.latex(r"R_{total} = \frac{1}{" + f"{inverse_resistance_sum:.4f}" + r"} = " + f"{total_resistance:.2f} \, \Omega") |
|
|
st.latex(r"I_{total} = I_1 + I_2 + ... + I_n") |
|
|
st.latex(f"I_{{total}} = {' + '.join([f'{i:.2f}' for i in branch_currents])} = {total_current:.2f} \, A") |
|
|
st.latex(r"I_n = \frac{V}{R_n}") |
|
|
|
|
|
st.sidebar.markdown('''File "D:\projects\physics-simulations\pages\basic-circuit-analysis.py", line 359 |
|
|
fractions_parts.append(r""\frac{1}{""" + f"{r:.1f}" + r""}""") |
|
|
^ |
|
|
SyntaxError: unexpected character after line continuation character |
|
|
### Basic Circuit Concepts |
|
|
|
|
|
**Ohm's Law:** V = IR |
|
|
- V: Voltage (Volts) |
|
|
- I: Current (Amperes) |
|
|
- R: Resistance (Ohms) |
|
|
|
|
|
**Series Circuit Properties:** |
|
|
- Same current through all components |
|
|
- Rtotal = R1 + R2 + ... + Rn |
|
|
- Vtotal = V1 + V2 + ... + Vn |
|
|
|
|
|
**Parallel Circuit Properties:** |
|
|
- Same voltage across all components |
|
|
- 1/Rtotal = 1/R1 + 1/R2 + ... + 1/Rn |
|
|
- Itotal = I1 + I2 + ... + In |
|
|
''') |