particle_simulator.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. """
  2. Simulate interacting charged particles in 3D space.
  3. """
  4. # An example simulation of N particles scattering on a charged target.
  5. # See e.g. https://en.wikipedia.org/wiki/Rutherford_scattering
  6. # By Tommy Vandermolen
  7. import numpy as np
  8. from vedo import Plotter, Cube, Sphere, mag2, versor, vector
  9. K_COULOMB = 8987551787.3681764 # N*m^2/C^2
  10. plt = None # so that it can be also used without visualization
  11. class ParticleSim:
  12. def __init__(self, dt, iterations):
  13. """
  14. Creates a new particle simulator
  15. dt: time step, time between successive calculations of particle motion
  16. """
  17. self.dt = dt
  18. self.particles = []
  19. self.iterations = iterations
  20. def add_particle(
  21. self,
  22. pos=(0, 0, 0),
  23. charge=1e-6,
  24. mass=1e-3,
  25. radius=0.005,
  26. color=None,
  27. vel=(0, 0, 0),
  28. fixed=False,
  29. negligible=False,
  30. ):
  31. """
  32. Adds a new particle with specified properties (in SI units)
  33. """
  34. color = color or len(self.particles) # assigned or default color number
  35. p = Particle(pos, charge, mass, radius, color, vel, fixed, negligible)
  36. self.particles.append(p)
  37. def simulate(self):
  38. """
  39. Runs the particle simulation. Simulates one time step, dt, of the particle motion.
  40. Calculates the force between each pair of particles and updates their motion accordingly
  41. """
  42. # Main simulation loop
  43. for i in range(self.iterations):
  44. for a in self.particles:
  45. if a.fixed:
  46. continue
  47. ftot = vector(0, 0, 0) # total force acting on particle a
  48. for b in self.particles:
  49. if a.negligible and b.negligible or a == b:
  50. continue
  51. ab = a.pos - b.pos
  52. ftot += ((K_COULOMB * a.charge * b.charge) / mag2(ab)) * versor(ab)
  53. a.vel += ftot / a.mass * self.dt # update velocity and position of a
  54. a.pos += a.vel * self.dt
  55. a.vsphere.pos(a.pos)
  56. a.vsphere.update_trail()
  57. if plt:
  58. if i==0:
  59. plt.reset_camera()
  60. plt.azimuth(1)
  61. plt.render()
  62. class Particle:
  63. def __init__(self, pos, charge, mass, radius, color, vel, fixed, negligible):
  64. """
  65. Creates a new particle with specified properties (in SI units)
  66. pos: XYZ starting position of the particle, in meters
  67. charge: charge of the particle, in Coulombs
  68. mass: mass of the particle, in kg
  69. radius: radius of the particle, in meters. No effect on simulation
  70. color: color of the particle. If None, a default color will be chosen
  71. vel: initial velocity vector, in m/s
  72. fixed: if True, particle will remain fixed in place
  73. negligible: assume charge is small wrt other charges to speed up calculation
  74. """
  75. self.pos = vector(pos)
  76. self.radius = radius
  77. self.charge = charge
  78. self.mass = mass
  79. self.vel = vector(vel)
  80. self.fixed = fixed
  81. self.negligible = negligible
  82. self.color = color
  83. if plt:
  84. self.vsphere = Sphere(pos, r=radius, c=color).add_trail(lw=1, n=75, alpha=0.5)
  85. plt.add(self.vsphere) # Sphere representing the particle
  86. #####################################################################################################
  87. if __name__ == "__main__":
  88. plt = Plotter(title="Particle Simulator", bg="black", interactive=False)
  89. plt += Cube().c('w').wireframe(True).lighting('off') # a wireframe cube
  90. sim = ParticleSim(dt=1e-5, iterations=50)
  91. sim.add_particle((-0.4, 0, 0), color="w", charge=3e-6, radius=0.01, fixed=True) # the target
  92. positions = np.random.randn(100, 3) / 60 # generate a beam of particles
  93. for p in positions:
  94. p[0] = -0.5 # Fix x position. Their charge are small/negligible compared to target:
  95. sim.add_particle(p, charge=0.01e-6, mass=0.1e-6, vel=(1000, 0, 0), negligible=True)
  96. sim.simulate()
  97. plt.interactive().close()