|
|
import streamlit as st |
|
|
import numpy as np |
|
|
import matplotlib.pyplot as plt |
|
|
|
|
|
|
|
|
st.title("Electric Field Visualization") |
|
|
st.write("Add charges to see how the electric field changes! Positive charges are red, negative charges are blue.") |
|
|
|
|
|
|
|
|
if 'charges' not in st.session_state: |
|
|
st.session_state.charges = [] |
|
|
|
|
|
|
|
|
def calculate_electric_field(charges, X, Y, k=1, epsilon=1e-6): |
|
|
""" |
|
|
Calculate the electric field components (E_x, E_y) at each point on the grid. |
|
|
|
|
|
Parameters: |
|
|
- charges: List of dictionaries with 'x', 'y', and 'q' for each charge |
|
|
- X, Y: 2D arrays from np.meshgrid representing grid coordinates |
|
|
- k: Coulomb's constant (set to 1 for simplicity) |
|
|
- epsilon: Small value to avoid division by zero at charge locations |
|
|
|
|
|
Returns: |
|
|
- E_x, E_y: Electric field components as 2D arrays |
|
|
""" |
|
|
E_x_total = np.zeros_like(X) |
|
|
E_y_total = np.zeros_like(Y) |
|
|
for charge in charges: |
|
|
dx = X - charge['x'] |
|
|
dy = Y - charge['y'] |
|
|
r = np.sqrt(dx**2 + dy**2 + epsilon) |
|
|
|
|
|
|
|
|
E_x_charge = k * charge['q'] * dx / r**3 |
|
|
E_y_charge = k * charge['q'] * dy / r**3 |
|
|
E_x_total += E_x_charge |
|
|
E_y_total += E_y_charge |
|
|
return E_x_total, E_y_total |
|
|
|
|
|
|
|
|
st.subheader("Add a Charge") |
|
|
x = st.slider("X position", -5.0, 5.0, 0.0, key="x_add") |
|
|
y = st.slider("Y position", -5.0, 5.0, 0.0, key="y_add") |
|
|
q = st.selectbox("Charge", [1, -1], format_func=lambda val: "+1" if val > 0 else "-1", key="q_add") |
|
|
if st.button("Add Charge", key="add_charge"): |
|
|
st.session_state.charges.append({'x': x, 'y': y, 'q': q}) |
|
|
st.success(f"Added charge q={q} at ({x}, {y})") |
|
|
|
|
|
|
|
|
st.subheader("Current Charges") |
|
|
if st.session_state.charges: |
|
|
for i, charge in enumerate(st.session_state.charges): |
|
|
st.write(f"Charge {i}: x={charge['x']}, y={charge['y']}, q={'+' if charge['q'] > 0 else '-'}1") |
|
|
|
|
|
|
|
|
charge_to_remove = st.selectbox( |
|
|
"Select charge to remove", |
|
|
options=range(len(st.session_state.charges)), |
|
|
format_func=lambda i: f"Charge {i}: x={st.session_state.charges[i]['x']}, y={st.session_state.charges[i]['y']}, q={'+' if st.session_state.charges[i]['q'] > 0 else '-'}1", |
|
|
key="charge_to_remove" |
|
|
) |
|
|
if st.button("Remove Selected Charge", key="remove_charge"): |
|
|
del st.session_state.charges[charge_to_remove] |
|
|
st.success("Charge removed!") |
|
|
else: |
|
|
st.write("No charges added yet. Add some to see the electric field.") |
|
|
|
|
|
|
|
|
if st.session_state.charges: |
|
|
|
|
|
x_grid = np.linspace(-5, 5, 20) |
|
|
y_grid = np.linspace(-5, 5, 20) |
|
|
X, Y = np.meshgrid(x_grid, y_grid) |
|
|
|
|
|
|
|
|
E_x, E_y = calculate_electric_field(st.session_state.charges, X, Y) |
|
|
|
|
|
|
|
|
fig, ax = plt.subplots(figsize=(8, 8)) |
|
|
ax.streamplot(X, Y, E_x, E_y, color='black', linewidth=1, density=1.5) |
|
|
|
|
|
|
|
|
for charge in st.session_state.charges: |
|
|
color = 'red' if charge['q'] > 0 else 'blue' |
|
|
ax.plot(charge['x'], charge['y'], 'o', color=color, markersize=10, label=f"q={charge['q']}") |
|
|
|
|
|
|
|
|
ax.set_xlim(-5, 5) |
|
|
ax.set_ylim(-5, 5) |
|
|
ax.set_xlabel("X") |
|
|
ax.set_ylabel("Y") |
|
|
ax.set_title("Electric Field Lines") |
|
|
ax.grid(True) |
|
|
|
|
|
|
|
|
st.pyplot(fig) |
|
|
else: |
|
|
st.info("Add some charges above to visualize the electric field!") |
|
|
|
|
|
|
|
|
st.write("Note: The field lines point away from positive charges and toward negative charges, following the direction a positive test charge would move.") |