|
|
import streamlit as st |
|
|
import numpy as np |
|
|
import matplotlib.pyplot as plt |
|
|
from matplotlib.animation import FuncAnimation |
|
|
import matplotlib.animation as animation |
|
|
|
|
|
class GravitySimulation: |
|
|
def __init__(self, num_bodies=3, width=10, height=10): |
|
|
""" |
|
|
Initialize the gravity simulation |
|
|
|
|
|
Parameters: |
|
|
----------- |
|
|
num_bodies : int, optional |
|
|
Number of bodies in the simulation (default is 3) |
|
|
width : float, optional |
|
|
Width of the simulation space (default is 10) |
|
|
height : float, optional |
|
|
Height of the simulation space (default is 10) |
|
|
""" |
|
|
self.num_bodies = num_bodies |
|
|
self.width = width |
|
|
self.height = height |
|
|
|
|
|
|
|
|
self.G = 1.0 |
|
|
|
|
|
|
|
|
self.positions = np.random.rand(num_bodies, 2) * np.array([width, height]) |
|
|
self.velocities = np.random.randn(num_bodies, 2) * 0.1 |
|
|
self.masses = np.random.uniform(0.5, 2, num_bodies) |
|
|
|
|
|
def compute_accelerations(self): |
|
|
""" |
|
|
Compute gravitational accelerations between bodies |
|
|
|
|
|
Returns: |
|
|
-------- |
|
|
numpy.ndarray |
|
|
Accelerations for each body |
|
|
""" |
|
|
accelerations = np.zeros_like(self.positions) |
|
|
|
|
|
for i in range(self.num_bodies): |
|
|
for j in range(self.num_bodies): |
|
|
if i != j: |
|
|
|
|
|
r_vector = self.positions[j] - self.positions[i] |
|
|
|
|
|
|
|
|
r_magnitude = np.linalg.norm(r_vector) |
|
|
|
|
|
|
|
|
if r_magnitude > 0: |
|
|
|
|
|
acceleration_magnitude = self.G * self.masses[j] / (r_magnitude ** 2) |
|
|
|
|
|
|
|
|
acceleration = acceleration_magnitude * (r_vector / r_magnitude) |
|
|
|
|
|
accelerations[i] += acceleration |
|
|
|
|
|
return accelerations |
|
|
|
|
|
def update(self, dt=0.01): |
|
|
""" |
|
|
Update positions and velocities using Euler integration |
|
|
|
|
|
Parameters: |
|
|
----------- |
|
|
dt : float, optional |
|
|
Time step for simulation (default is 0.01) |
|
|
""" |
|
|
|
|
|
accelerations = self.compute_accelerations() |
|
|
|
|
|
|
|
|
self.velocities += accelerations * dt |
|
|
|
|
|
|
|
|
self.positions += self.velocities * dt |
|
|
|
|
|
|
|
|
for i in range(self.num_bodies): |
|
|
for dim in range(2): |
|
|
if (self.positions[i, dim] < 0) or (self.positions[i, dim] > [self.width, self.height][dim]): |
|
|
self.velocities[i, dim] *= -0.9 |
|
|
self.positions[i, dim] = np.clip(self.positions[i, dim], 0, [self.width, self.height][dim]) |
|
|
|
|
|
def main(): |
|
|
st.title("Gravitational N-Body Simulation") |
|
|
|
|
|
|
|
|
st.sidebar.header("Simulation Parameters") |
|
|
|
|
|
|
|
|
num_bodies = st.sidebar.slider("Number of Bodies", min_value=2, max_value=10, value=3) |
|
|
|
|
|
|
|
|
width = st.sidebar.number_input("Simulation Width", min_value=5.0, max_value=20.0, value=10.0) |
|
|
height = st.sidebar.number_input("Simulation Height", min_value=5.0, max_value=20.0, value=10.0) |
|
|
|
|
|
|
|
|
dt = st.sidebar.slider("Time Step", min_value=0.001, max_value=0.1, value=0.01, step=0.001) |
|
|
|
|
|
|
|
|
sim = GravitySimulation(num_bodies=num_bodies, width=width, height=height) |
|
|
|
|
|
|
|
|
fig, ax = plt.subplots(figsize=(8, 6)) |
|
|
ax.set_xlim(0, width) |
|
|
ax.set_ylim(0, height) |
|
|
ax.set_title("Gravitational Interaction") |
|
|
ax.set_xlabel("X Position") |
|
|
ax.set_ylabel("Y Position") |
|
|
|
|
|
|
|
|
scatter = ax.scatter(sim.positions[:, 0], sim.positions[:, 1], |
|
|
c=sim.masses, cmap='viridis', |
|
|
s=sim.masses*100, alpha=0.7) |
|
|
|
|
|
|
|
|
def update_plot(frame): |
|
|
sim.update(dt) |
|
|
scatter.set_offsets(sim.positions) |
|
|
return scatter, |
|
|
|
|
|
|
|
|
anim = FuncAnimation(fig, update_plot, frames=200, interval=50, blit=True) |
|
|
|
|
|
|
|
|
plt.close(fig) |
|
|
|
|
|
|
|
|
st.pyplot(fig) |
|
|
|
|
|
|
|
|
st.sidebar.header("Animation Options") |
|
|
if st.sidebar.button("Save Animation"): |
|
|
|
|
|
anim.save('gravity_simulation.gif', writer='pillow') |
|
|
with open('gravity_simulation.gif', 'rb') as f: |
|
|
st.sidebar.download_button( |
|
|
label="Download Animation", |
|
|
data=f.read(), |
|
|
file_name="gravity_simulation.gif", |
|
|
mime="image/gif" |
|
|
) |
|
|
|
|
|
|
|
|
st.markdown("## About the Simulation") |
|
|
st.markdown(""" |
|
|
This simulation demonstrates gravitational interactions between multiple bodies: |
|
|
- Bodies attract each other based on their masses |
|
|
- Gravitational force is inversely proportional to the square of the distance |
|
|
- Bodies bounce off the simulation boundaries |
|
|
- Larger/darker points represent bodies with more mass |
|
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |