mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.animation import FuncAnimation
|
|
from IPython.display import HTML
|
|
|
|
# --- SETTINGS FOR EMERGENT GALAXY ---
|
|
N_particles = 600
|
|
box_size = 150.0
|
|
dt = 0.04
|
|
steps = 600
|
|
|
|
# 1. RADIAL FORCES (Hydrodynamic attraction vs Orbital repulsion)
|
|
k_attr = 100.0
|
|
soft_attr = 5.0
|
|
k_repel = 55.0
|
|
soft_repel = 1.0
|
|
|
|
# 2. TANGENTIAL FORCE (The Fundamental Spin)
|
|
k_spin = 70.0 # Slightly increased to compensate for damping at the core
|
|
soft_spin = 3.0
|
|
spin_damping_radius = 15.0 # NEW: Distance at which spin starts proportionally dropping to zero
|
|
|
|
R_max = 60.0
|
|
damping = 0.99
|
|
thermal_noise = 3.0
|
|
|
|
# --- INITIALIZATION ---
|
|
center = box_size / 2.0
|
|
pos = (np.random.randn(N_particles, 2) * 20.0) + center
|
|
vel = (np.random.rand(N_particles, 2) - 0.5) * thermal_noise
|
|
|
|
# ASSIGNING FUNDAMENTAL SPIN
|
|
spins = np.random.randn(N_particles) * 0.5 + 1.0
|
|
|
|
fig, ax = plt.subplots(figsize=(8, 8), facecolor='#000000')
|
|
ax.set_xlim(0, box_size); ax.set_ylim(0, box_size)
|
|
ax.set_title("Emergent Galaxy: Proportional Spin Damping", color='white', fontsize=14)
|
|
ax.set_axis_off()
|
|
|
|
scatter = ax.scatter(pos[:, 0], pos[:, 1], s=15, c='#00f2ff', edgecolors='white', linewidth=0.2, alpha=0.8)
|
|
|
|
# --- PHYSICS ENGINE ---
|
|
def update(frame):
|
|
global pos, vel
|
|
forces = np.zeros((N_particles, 2))
|
|
|
|
for i in range(N_particles):
|
|
delta = pos - pos[i]
|
|
dist_sq = np.sum(delta**2, axis=1)
|
|
dist = np.sqrt(dist_sq) + 0.001
|
|
|
|
mask = (dist > 0) & (dist < R_max)
|
|
r_sq = dist_sq[mask]
|
|
r_actual = dist[mask]
|
|
|
|
# Radial vector
|
|
d_vec = delta[mask] / r_actual[:, np.newaxis]
|
|
# Tangential vector (rotated 90 degrees)
|
|
t_vec = np.column_stack((-d_vec[:, 1], d_vec[:, 0]))
|
|
|
|
# Radial Forces
|
|
f_attr = k_attr / (r_sq + soft_attr)
|
|
f_repel = -k_repel / (r_sq + soft_repel)
|
|
f_radial = f_attr + f_repel
|
|
|
|
# --- NEW: PROPORTIONAL SPIN DAMPING ---
|
|
# If distance (r_actual) > spin_damping_radius, multiplier is 1.0 (Full spin)
|
|
# If distance gets closer to 0, multiplier drops proportionally to 0.0
|
|
spin_multiplier = np.clip(r_actual / spin_damping_radius, 0.0, 1.0)
|
|
|
|
# Calculate Tangential Force with the damping multiplier applied
|
|
f_spin = (k_spin * spins[mask] * spin_multiplier) / (r_sq + soft_spin)
|
|
|
|
# Sum vectors
|
|
forces[i] = np.sum(d_vec * f_radial[:, np.newaxis] + t_vec * f_spin[:, np.newaxis], axis=0)
|
|
|
|
# Thermodynamics
|
|
random_vibration = (np.random.rand(N_particles, 2) - 0.5) * 0.1
|
|
vel = vel * damping + forces * dt + random_vibration
|
|
|
|
# Speed limit
|
|
v_speed = np.linalg.norm(vel, axis=1, keepdims=True)
|
|
vel = np.where(v_speed > 25.0, vel * (25.0 / v_speed), vel)
|
|
|
|
pos += vel * dt
|
|
|
|
# Boundaries
|
|
for d in range(2):
|
|
out_min, out_max = pos[:, d] < 0, pos[:, d] > box_size
|
|
if np.any(out_min):
|
|
pos[out_min, d] = 0
|
|
vel[out_min, d] *= -0.5
|
|
if np.any(out_max):
|
|
pos[out_max, d] = box_size
|
|
vel[out_max, d] *= -0.5
|
|
|
|
scatter.set_offsets(pos)
|
|
return scatter,
|
|
|
|
plt.close()
|
|
anim = FuncAnimation(fig, update, frames=steps, interval=30, blit=True)
|
|
HTML(anim.to_jshtml())
|