## Particle Interaction Simulator

This project was inspired by me wanting to create baseline visuals for particle interaction within a confined 3d space.

The simulation is designed to help understand the physical and chemical behaviors of particles. Going forward, I plan on implementing different physical/chemical conditions that will dictate their motion (ie gravity, temperature, etc).

### Mathematical Foundation

**1. Newton's Laws of Motion:**

According to Newton's second law, the force acting on a particle is equal to the mass of the particle multiplied by its acceleration:

\[ F = ma \]

**2. Particle Interactions:**

*Gravitational Forces:* In the absence of other forces (closed system), particles will experience gravitational attraction towards each other.

*Electrostatic Forces:* Charged particles will experience repulsive or attractive forces based on Coulomb's law:

\[ F = k_e \frac{q_1 q_2}{r^2} \]

*Lennard-Jones Potential:* This potential models the interaction between a pair of neutral atoms or molecules. It is a function of the distance between the particles and has a form:

\[ V(r) = 4\epsilon \left[ \left(\frac{\sigma}{r}\right)^{12} - \left(\frac{\sigma}{r}\right)^{6} \right] \]

where \( \epsilon \) is the depth of the potential well, \( \sigma \) is the finite distance at which the inter-particle potential is zero, and \( r \) is the distance between the particles.

**3. Boundary Conditions:**

The particles are confined within a box, which means they will experience boundary conditions. When a particle hits the wall of the box, it will bounce back, which can be modeled using perfectly elastic collisions.

### Simulation Logic

**Initialization:**

Particles are initialized with random positions and velocities within the confined box. Initial conditions include particle mass, charge, and other properties relevant to their interactions.

**Time Stepping:**

The simulation proceeds in discrete time steps. At each time step:

**Force Calculation:**The forces on each particle due to all other particles and the walls of the box are calculated.**Acceleration Calculation:**Using \( F = ma \), the acceleration of each particle is determined.**Velocity Update:**The velocity of each particle is updated using the calculated acceleration:**Position Update:**The position of each particle is updated using the updated velocity:

\[ v = v + a \Delta t \]

\[ x = x + v \Delta t \]

### Technologies Used

- Python
- VPython
- MathJax
- Numpy

### Raw Code

from vpython import * import random # Constants num_particles = 100 box_size = 15 particle_radius = 0.3 dt = 0.05 # time derivative that is used to calculate speed # create the simulation box with a white outline simulation_box = box(size=vector(box_size, box_size, box_size), opacity=0.1, color=color.white) # create the outline using a curve for each edge edges = [ [vector(-box_size/2, -box_size/2, -box_size/2), vector(box_size/2, -box_size/2, -box_size/2)], [vector(box_size/2, -box_size/2, -box_size/2), vector(box_size/2, box_size/2, -box_size/2)], [vector(box_size/2, box_size/2, -box_size/2), vector(-box_size/2, box_size/2, -box_size/2)], [vector(-box_size/2, box_size/2, -box_size/2), vector(-box_size/2, -box_size/2, -box_size/2)], [vector(-box_size/2, -box_size/2, box_size/2), vector(box_size/2, -box_size/2, box_size/2)], [vector(box_size/2, -box_size/2, box_size/2), vector(box_size/2, box_size/2, box_size/2)], [vector(box_size/2, box_size/2, box_size/2), vector(-box_size/2, box_size/2, box_size/2)], [vector(-box_size/2, box_size/2, box_size/2), vector(-box_size/2, -box_size/2, box_size/2)], [vector(-box_size/2, -box_size/2, -box_size/2), vector(-box_size/2, -box_size/2, box_size/2)], [vector(box_size/2, -box_size/2, -box_size/2), vector(box_size/2, -box_size/2, box_size/2)], [vector(box_size/2, box_size/2, -box_size/2), vector(box_size/2, box_size/2, box_size/2)], [vector(-box_size/2, box_size/2, -box_size/2), vector(-box_size/2, box_size/2, box_size/2)] ] for edge in edges: curve(pos=edge, color=color.white) # function to generate a random color def random_color(): return vector(random.random(), random.random(), random.random()) class Particle: def __init__(self, position, velocity, radius, color): self.sphere = sphere(pos=position, radius=radius, color=color) self.velocity = velocity self.radius = radius def update_position(self): self.sphere.pos += self.velocity * dt self.check_collision_with_walls() def check_collision_with_walls(self): if abs(self.sphere.pos.x) + self.radius >= box_size / 2: self.velocity.x *= -1 self.sphere.pos.x = copysign(box_size / 2 - self.radius, self.sphere.pos.x) if abs(self.sphere.pos.y) + self.radius >= box_size / 2: self.velocity.y *= -1 self.sphere.pos.y = copysign(box_size / 2 - self.radius, self.sphere.pos.y) if abs(self.sphere.pos.z) + self.radius >= box_size / 2: self.velocity.z *= -1 self.sphere.pos.z = copysign(box_size / 2 - self.radius, self.sphere.pos.z) def handle_collision(self, other): distance = mag(self.sphere.pos - other.sphere.pos) if distance < 2 * self.radius: pos_i = self.sphere.pos pos_j = other.sphere.pos vel_i = self.velocity vel_j = other.velocity normal = norm(pos_j - pos_i) relative_velocity = vel_i - vel_j speed = dot(relative_velocity, normal) if speed > 0: # only adjust if theyre moving towards each other impulse = 2 * speed / (1 / self.radius + 1 / other.radius) self.velocity -= impulse * normal / self.radius other.velocity += impulse * normal / other.radius # create a list to hold the particles particles = [] # initialize particles with random positions, velocities, and colors for i in range(num_particles): position = vector(random.uniform(-box_size/2, box_size/2), random.uniform(-box_size/2, box_size/2), random.uniform(-box_size/2, box_size/2)) velocity = vector(random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)) color = random_color() # assign a random color to each particle particle = Particle(position, velocity, particle_radius, color) particles.append(particle) # main simulation loop while True: rate(100) for particle in particles: particle.update_position() for i in range(num_particles): for j in range(i+1, num_particles): particles[i].handle_collision(particles[j])

### Sources

https://web.pdx.edu/~egertonr/ph311-12/particle.htm