Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hard-sphere gas simulation

A gas of hard spheres in 3D, checked against kinetic theory. Originally written for the PHYS389 computer modelling module and extended since.

animation

200 particles bounce around a cubic box, colliding elastically with the walls and with each other. They all start with exactly the same speed (the rms speed for 293 K) in random directions, so the speed histogram begins as a single spike. After a few collisions each it has relaxed onto the Maxwell-Boltzmann distribution, which is the orange curve in the animation. One particle is coloured red so you can follow it.

From that one ingredient, elastic collisions between spheres, the simulation reproduces:

Running it

pip install -e .[dev]
python scripts/animate.py                # animation
python scripts/animate.py --save         # writes figures/animation.gif instead
python scripts/animate.py -N 500 --radius 0.02 --event-driven
python scripts/analyse.py                # prints pressure and collision rate, saves a figure
pytest                                   # tests

The other scripts each produce one of the figures below: equation_of_state.py, h_theorem.py, mixture.py and brownian.py. The first and last take a few minutes.

To use it in your own code:

from hard_sphere_gas import Simulation, EventDrivenSimulation

sim = EventDrivenSimulation(N=300, R=0.02, T=300, seed=1)
sim.run(1000)
print(sim.temperature(), sim.pressure(), sim.collision_rate())

m and R can be arrays with one value per particle, and pos and vel can be passed in to set the starting state by hand. The state is in the numpy arrays sim.pos, sim.vel, sim.mass and sim.radius.

How it works

  • hard_sphere_gas/simulation.py - the Simulation class, which uses a fixed timestep. Each step moves every particle by v*dt, then checks for wall hits and overlapping pairs.
  • hard_sphere_gas/event_driven.py - EventDrivenSimulation, which has the same interface but works out exactly when each collision will happen and jumps from one to the next.
  • hard_sphere_gas/theory.py - the kinetic theory results the simulation is compared with.
  • scripts/ - the animation and one script per figure.
  • tests/ - pytest tests for both engines and the theory functions.

Collisions are resolved by applying an impulse along the line between the two centres, which conserves both momentum and kinetic energy. In the fixed timestep engine a pair is only collided if the particles are approaching each other, and a wall only reflects a particle that is heading outwards. Without those two checks particles can get stuck to each other or to the walls, flipping direction every step.

Finding the touching pairs. For up to 200 particles the distances between all pairs are found at once with numpy. Above that a cell list is used: the box is divided into cells at least one particle diameter wide, so a particle can only be touching particles in its own cell or the 26 around it. This takes the cost per step from O(N²) to O(N), and 2000 particles run at 5 ms per step instead of 190 ms.

The event-driven engine. Between collisions the particles move in straight lines, so the time at which any two will touch is the root of a quadratic. Each particle's earliest event (a wall or another particle) is kept in a priority queue. The earliest event overall is popped, all the particles are moved forward to that time, the collision is carried out and the particles involved have their next events predicted again. Predictions are made stale by collisions that happen in the meantime, so each event records how many collisions its particles had had when it was made and is thrown away if that has changed. Spheres never overlap and there is no timestep to choose, but it is slower in pure Python, at around 3000 collisions a second.

The mass is 127 u (iodine atoms) and the box is 1 m across. The radius of 3 cm is obviously nothing like a real atom, it is chosen so that the mean free path is smaller than the box and collisions happen often enough to watch.

Results

Speed distribution and energy conservation

analysis

Left: speeds sampled after the first 1000 steps against the Maxwell-Boltzmann distribution at the temperature of the simulation. There are no fitted parameters. Right: relative change in total kinetic energy, which stays at the level of floating point rounding.

Pressure and collision rate

Pressure is measured by adding up the momentum given to the walls (2m|v| per hit) and dividing by time and wall area. Here it is as a ratio to the ideal gas value NkT/V, where V is the volume available to the particle centres:

P / Pideal Collisions per particle per second
Fixed timestep, dt = 5e-5 1.062 753
Event-driven 1.105 829
Theory for an infinite system 1.117 912

The theory values are the Carnahan-Starling equation of state and the Enskog collision rate, sqrt(2) n pi d^2 <v> g, where g is the pair distribution function at contact. (The van der Waals excluded volume correction NkT/(V - Nb) gives 1.122, the two agree at low density.) The measured mean free path is 0.27 m against 0.24 m.

Two separate effects pull the measurements below the theory:

  1. Timestep. With a fixed timestep two spheres overlap a little before the collision is noticed, so they behave as if they were smaller than they are. This accounts for the gap between the first two rows, and the event-driven engine removes it completely.
  2. Walls. The remaining gap is real physics rather than a numerical error. A particle next to a wall cannot be hit from the wall side, so it collides less often than one in the middle of the box. With 6 cm particles in a 1 m box a good fraction of the gas is within one diameter of a wall. Both the excess pressure and the collision rate come out at about 90% of the infinite-system value. Running EventDrivenSimulation(N=1600, R=0.015), which has almost the same packing fraction but particles relatively half as close to the walls, brings both up to 95%, as expected for a surface effect. The next section gets rid of it properly.

Equation of state

equation of state

The compressibility factor Z = PV/NkT for 250 particles at six packing fractions, using the event-driven engine. The open circles use all the wall hits and the overall density, and fall further below the Carnahan-Starling curve as the particles get bigger, because of the wall effect above.

The filled circles use only the middle of the box. The pressure on a flat wall is equal to the pressure of the bulk gas, so it is measured from hits on the central part of each wall, at least two particle diameters away from the edges. The density of the bulk gas is measured directly by counting the particles in the middle of the box, and is lower than N/V since particles collect next to the walls. Done this way the simulation agrees with Carnahan-Starling to within 1.6% at every density:

Packing fraction (bulk) Z measured Carnahan-Starling
0.020 1.084 1.083
0.049 1.227 1.220
0.094 1.484 1.480
0.136 1.761 1.783
0.176 2.148 2.146
0.211 2.574 2.534

The van der Waals excluded volume correction is only right to first order in the density, and is already well off by a packing fraction of 0.1.

H-theorem

H-theorem

Boltzmann's H is the integral of f ln f over velocity, where f is the velocity distribution. It is minus the entropy of the distribution, and the H-theorem says collisions can only make it smaller until it reaches its minimum, which is the Maxwell-Boltzmann distribution. This is the relaxation in the animation at the top, as a single number. With 2000 particles H falls smoothly to the Maxwell-Boltzmann value (-19.05 for iodine at 293 K in SI units) within about four collisions per particle, and stays there.

Equipartition in a mixture

mixture

150 xenon-mass particles start with all of the kinetic energy and 150 argon-mass particles start at rest. Collisions share the energy out until each species has the same mean kinetic energy, 3/2 kT, after which they swap small amounts back and forth. The two species end up with different Maxwell-Boltzmann speed distributions at the same temperature.

Brownian motion

Brownian motion

One sphere of radius 15 cm and 20 times the mass in a bath of 500 ordinary particles. Left: its path, a random walk. Right: its mean squared displacement. For short times the sphere just keeps going and the MSD grows as t^2. After about a millisecond the bath has randomised its velocity and the MSD grows as 6Dt instead, until the sphere starts to notice the walls of the box.

The curves are not fits. The friction on the sphere is calculated from Enskog kinetic theory, which gives the diffusion coefficient D = kT / friction and the velocity relaxation time M / friction, and these go into the solution of the Langevin equation (dotted) and the same for a particle confined between walls (solid).

Limitations

  • The box has hard walls rather than periodic boundaries, so bulk properties pick up a surface correction of a few percent, as discussed above.
  • The event-driven engine predicts each particle's collisions against every other particle, which is O(N) work per collision. Combining it with the cell list would make this O(1).
  • Everything is pure Python and numpy. Compiling the inner loops with numba would make both engines much faster.

License

MIT, see LICENSE.

About

Hard-sphere gas simulation in Python with fixed-timestep and event-driven engines, tested against kinetic theory: Maxwell-Boltzmann, Carnahan-Starling equation of state, H-theorem, equipartition and Brownian motion.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages