"""Simple demo to illustrate the motion of a Big brownian particle in a swarm of small particles in 2D motion. The spheres collide elastically with themselves and with the walls of the box. The masses of the spheres are proportional to their radius**3 (as in 3D)""" # Adapted by M. Musy from E. Velasco (2009) from vedo import * import random screen_w = 800 screen_h = 800 # Constants and time step Nsp = 200 # Number of small spheres Rb = screen_w / 32 # Radius of the big sphere Rs = Rb * 0.43 # Radius of small spheres Ms = (Rs / Rb) ** 3 # Mass of the small spheres (Mbig=1) Dt = 0.03 # Time step LBox = (screen_w / 2, screen_h / 2) # Size of the box = 2LBox[0].2LBox[1] Lb0 = LBox[0] - Rb Lb1 = LBox[1] - Rb Ls0 = LBox[0] - Rs Ls1 = LBox[1] - Rs # Create the arrays with the initial positions of the spheres. # Start with the big sphere at the center, then put the small # spheres at random selected from a grid of possible positions. ListPos = [(0, 0)] PossiblePos = [ (x, y) for x in np.arange(-LBox[0] + 2 * Rs, LBox[0] - 2 * Rs, 2.2 * Rs) for y in np.arange(-LBox[1] + 2 * Rs, LBox[1] - 2 * Rs, 2.2 * Rs) if x * x + y * y > Rb + Rs ] if Nsp > len(PossiblePos) + 1: Nsp = len(PossiblePos) + 1 for s in range(Nsp - 1): n = random.randint(0, len(PossiblePos) - 1) ListPos.append(PossiblePos[n]) del PossiblePos[n] Pos = np.array(ListPos) # Create an array with all the radius and a list with all the masses Radius = np.concatenate((np.array([Rb]), np.array([Rs] * (Nsp - 1)))) Mass = [1.0] + [Ms] * (Nsp - 1) # Create the initial array of velocities at random with big sphere at rest ListVel = [(0.0, 0.0)] for s in range(1, Nsp): ListVel.append((Rb * random.uniform(-1, 1), Rb * random.uniform(-1, 1))) Vel = np.array(ListVel) plt = Plotter(size=(screen_w, screen_h), interactive=0) plt += Grid(s=[screen_w,screen_w]) plt.show(zoom='tight') # Auxiliary variables Id = np.identity(Nsp) Dij = (Radius + Radius[:, np.newaxis]) ** 2 # Matrix Dij=(Ri+Rj)**2 # The main loop for i in progressbar(500, c="r"): # Update all positions np.add(Pos, Vel * Dt, Pos) # Fast version of Pos = Pos + Vel*Dt # Impose the bouncing at the walls if Pos[0, 0] <= -Lb0: Pos[0, 0] = -Lb0 Vel[0, 0] = -Vel[0, 0] elif Pos[0, 0] >= Lb0: Pos[0, 0] = Lb0 Vel[0, 0] = -Vel[0, 0] elif Pos[0, 1] <= -Lb1: Pos[0, 1] = -Lb1 Vel[0, 1] = -Vel[0, 1] elif Pos[0, 1] >= Lb1: Pos[0, 1] = Lb1 Vel[0, 1] = -Vel[0, 1] for s in range(1, Nsp): if Pos[s, 0] <= -Ls0: Pos[s, 0] = -Ls0 Vel[s, 0] = -Vel[s, 0] elif Pos[s, 0] >= Ls0: Pos[s, 0] = Ls0 Vel[s, 0] = -Vel[s, 0] elif Pos[s, 1] <= -Ls1: Pos[s, 1] = -Ls1 Vel[s, 1] = -Vel[s, 1] elif Pos[s, 1] >= Ls1: Pos[s, 1] = Ls1 Vel[s, 1] = -Vel[s, 1] # Create the set of all pairs and the list the colliding spheres Rij = Pos - Pos[:, np.newaxis] Mag2ij = np.add.reduce(Rij * Rij, -1) # sphere-to-sphere distances**2 colliding = np.less_equal(Mag2ij, Dij) - Id hitlist = np.sort(np.nonzero(colliding.flat)[0]).tolist() # Check to see if the spheres are colliding for ij in hitlist: s1, s2 = divmod(ij, Nsp) # decode the spheres pair (s1,s2) colliding hitlist.remove(s2 * Nsp + s1) # remove symmetric (s2,s1) pair from list R12 = Pos[s2] - Pos[s1] nR12 = np.linalg.norm(R12) d12 = Radius[s1] + Radius[s2] - nR12 tau = R12 / nR12 DR0 = d12 * tau x1 = Mass[s1] / (Mass[s1] + Mass[s2]) x2 = 1 - x1 # x2 = Mass[s2]/(Mass[s1]+Mass[s2]) Pos[s1] -= x2 * DR0 Pos[s2] += x1 * DR0 DV0 = 2 * dot(Vel[s2] - Vel[s1], tau) * tau Vel[s1] += x2 * DV0 Vel[s2] -= x1 * DV0 spheres = Points(Pos).c("blue4").point_size(20) if not int(i) % 20: # every 20 steps: rsp = [Pos[0][0], Pos[0][1], 0] trace = Points(rsp).c("red").point_size(4) plt.add(trace) # leave a point trace spheres.name = "particles" plt.remove("particles").add(spheres).render() plt.interactive().close()