brownian2d.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """Simple demo to illustrate the motion of a Big brownian
  2. particle in a swarm of small particles in 2D motion.
  3. The spheres collide elastically with themselves and
  4. with the walls of the box. The masses of the spheres
  5. are proportional to their radius**3 (as in 3D)"""
  6. # Adapted by M. Musy from E. Velasco (2009)
  7. from vedo import *
  8. import random
  9. screen_w = 800
  10. screen_h = 800
  11. # Constants and time step
  12. Nsp = 200 # Number of small spheres
  13. Rb = screen_w / 32 # Radius of the big sphere
  14. Rs = Rb * 0.43 # Radius of small spheres
  15. Ms = (Rs / Rb) ** 3 # Mass of the small spheres (Mbig=1)
  16. Dt = 0.03 # Time step
  17. LBox = (screen_w / 2, screen_h / 2) # Size of the box = 2LBox[0].2LBox[1]
  18. Lb0 = LBox[0] - Rb
  19. Lb1 = LBox[1] - Rb
  20. Ls0 = LBox[0] - Rs
  21. Ls1 = LBox[1] - Rs
  22. # Create the arrays with the initial positions of the spheres.
  23. # Start with the big sphere at the center, then put the small
  24. # spheres at random selected from a grid of possible positions.
  25. ListPos = [(0, 0)]
  26. PossiblePos = [
  27. (x, y)
  28. for x in np.arange(-LBox[0] + 2 * Rs, LBox[0] - 2 * Rs, 2.2 * Rs)
  29. for y in np.arange(-LBox[1] + 2 * Rs, LBox[1] - 2 * Rs, 2.2 * Rs)
  30. if x * x + y * y > Rb + Rs
  31. ]
  32. if Nsp > len(PossiblePos) + 1:
  33. Nsp = len(PossiblePos) + 1
  34. for s in range(Nsp - 1):
  35. n = random.randint(0, len(PossiblePos) - 1)
  36. ListPos.append(PossiblePos[n])
  37. del PossiblePos[n]
  38. Pos = np.array(ListPos)
  39. # Create an array with all the radius and a list with all the masses
  40. Radius = np.concatenate((np.array([Rb]), np.array([Rs] * (Nsp - 1))))
  41. Mass = [1.0] + [Ms] * (Nsp - 1)
  42. # Create the initial array of velocities at random with big sphere at rest
  43. ListVel = [(0.0, 0.0)]
  44. for s in range(1, Nsp):
  45. ListVel.append((Rb * random.uniform(-1, 1), Rb * random.uniform(-1, 1)))
  46. Vel = np.array(ListVel)
  47. plt = Plotter(size=(screen_w, screen_h), interactive=0)
  48. plt += Grid(s=[screen_w,screen_w])
  49. plt.show(zoom='tight')
  50. # Auxiliary variables
  51. Id = np.identity(Nsp)
  52. Dij = (Radius + Radius[:, np.newaxis]) ** 2 # Matrix Dij=(Ri+Rj)**2
  53. # The main loop
  54. for i in progressbar(500, c="r"):
  55. # Update all positions
  56. np.add(Pos, Vel * Dt, Pos) # Fast version of Pos = Pos + Vel*Dt
  57. # Impose the bouncing at the walls
  58. if Pos[0, 0] <= -Lb0:
  59. Pos[0, 0] = -Lb0
  60. Vel[0, 0] = -Vel[0, 0]
  61. elif Pos[0, 0] >= Lb0:
  62. Pos[0, 0] = Lb0
  63. Vel[0, 0] = -Vel[0, 0]
  64. elif Pos[0, 1] <= -Lb1:
  65. Pos[0, 1] = -Lb1
  66. Vel[0, 1] = -Vel[0, 1]
  67. elif Pos[0, 1] >= Lb1:
  68. Pos[0, 1] = Lb1
  69. Vel[0, 1] = -Vel[0, 1]
  70. for s in range(1, Nsp):
  71. if Pos[s, 0] <= -Ls0:
  72. Pos[s, 0] = -Ls0
  73. Vel[s, 0] = -Vel[s, 0]
  74. elif Pos[s, 0] >= Ls0:
  75. Pos[s, 0] = Ls0
  76. Vel[s, 0] = -Vel[s, 0]
  77. elif Pos[s, 1] <= -Ls1:
  78. Pos[s, 1] = -Ls1
  79. Vel[s, 1] = -Vel[s, 1]
  80. elif Pos[s, 1] >= Ls1:
  81. Pos[s, 1] = Ls1
  82. Vel[s, 1] = -Vel[s, 1]
  83. # Create the set of all pairs and the list the colliding spheres
  84. Rij = Pos - Pos[:, np.newaxis]
  85. Mag2ij = np.add.reduce(Rij * Rij, -1) # sphere-to-sphere distances**2
  86. colliding = np.less_equal(Mag2ij, Dij) - Id
  87. hitlist = np.sort(np.nonzero(colliding.flat)[0]).tolist()
  88. # Check to see if the spheres are colliding
  89. for ij in hitlist:
  90. s1, s2 = divmod(ij, Nsp) # decode the spheres pair (s1,s2) colliding
  91. hitlist.remove(s2 * Nsp + s1) # remove symmetric (s2,s1) pair from list
  92. R12 = Pos[s2] - Pos[s1]
  93. nR12 = np.linalg.norm(R12)
  94. d12 = Radius[s1] + Radius[s2] - nR12
  95. tau = R12 / nR12
  96. DR0 = d12 * tau
  97. x1 = Mass[s1] / (Mass[s1] + Mass[s2])
  98. x2 = 1 - x1 # x2 = Mass[s2]/(Mass[s1]+Mass[s2])
  99. Pos[s1] -= x2 * DR0
  100. Pos[s2] += x1 * DR0
  101. DV0 = 2 * dot(Vel[s2] - Vel[s1], tau) * tau
  102. Vel[s1] += x2 * DV0
  103. Vel[s2] -= x1 * DV0
  104. spheres = Points(Pos).c("blue4").point_size(20)
  105. if not int(i) % 20: # every 20 steps:
  106. rsp = [Pos[0][0], Pos[0][1], 0]
  107. trace = Points(rsp).c("red").point_size(4)
  108. plt.add(trace) # leave a point trace
  109. spheres.name = "particles"
  110. plt.remove("particles").add(spheres).render()
  111. plt.interactive().close()