pretty pictures

onedimensional solution

twodimensional solution
You can download all the files in a single zip file.
TEN PARTICLES  equilibrium thermodynamicsIn the first multiplot, the upper half shows the positions of ten particles, and the low half shows the complete state  both the positions (horizontal coordinates) and the velocities (vertical coordinates).The blue particles on the left have masses roughly equal to 1.0. The green on the right have masses roughly equal to 4.0. One of the predictions of statistical physics is that for a large system at equilibrium, the probability of any state is just exp(beta E)/Z, where E is the energy of the state. This prediction has astonishing consequences. First, each particle's velocity should have a Gaussian distribution. Second, all valid positions are equiprobable, and the probability of the positions and the velocities is a separable distribution  positions and velocities are independent. All valid positions are equiprobable, whatever the masses of the particles are. So changing the particle masses may change their typical velocities but it has no effect at all on their typical positions. 
This figure shows scatter plots of the velocties of particles 1 and 2 (left) and particles 9 and 10 (right). A striking prediction of statistical physics is that both distributions should be Gaussian, and the standard deviations of v1 and v10 (velocities of particles 1 and 10) should be in the ratio 2:1, because the mass ratio is 1:4. This prediction is well borne out in the data, though the data show an intriguing dimple in P(v1)  is this dimple because N=10 is too small for the largeN equilibrium theory to apply? Or is it because the particular random masses chosen here give a system that has a slow mixing time, so the points generated were not actually a good representation of the equilibrium distribution? 
This figure shows the joint distribution of the positions of particles 1 and 2, 9 and 10. 
ELEVEN PARTICLES  nonequilibrium thermodynamicsThe next pinup in my picture gallery illustrates very different ideas. In the 10particle example above, we were assuming that equilibrium had been reached and testing the predictions of equilibrium theory. In my elevenparticle example, I have a large purple particle which I call the piston plopped in the middle. The dynamics are the same as normal, but its mass (40) is so much bigger than the others, the behaviour of them all is radically altered. In this simulation I set off all the particles with equal velocities. This means the situation is not at all typical of an equilibrium thermal distribution  the big guy has far too much energy  he has roughly 75% of the energy and at equilibrium he'd expect to have 9%. So what happens for the whole simulation is nonequilibrium. One way of thinking of the piston is that he's a lot like a wall, pushing down on the green community and rushing away from the blue  or vice versa. What happens to a gas that is rapidly compressed or expanded by a moving piston? Well, it depends exactly which sort of expansion we are discussing, but an adiabatic expansion is one sort. If the blue community and the green community both behave like adiabatically expanded or compressed gases, what would you predict their temperature and pressure to do as a function of 'volume' (which means the accessible volume given the piston position)? Maybe the motion of the piston can be modelled using the idea that its acceleration is the result of the pressure difference between the green gas on the right and the blue gas on the left? Under this theory, the piston should move approximately like a simple harmonic oscillator. What frequency does the adiabatic theory predict? 
Here's the positions (horizontal axis) of all the particles as a function of time (vertical axis). 
Here's the same thing but showing more data. 
Here's the total kinetic energy of the blue guys, the green guys, and the purple guy, for the first 40 time units; 
and for the first 200 time units. The way in which the excess energy of the piston is gradually dissipated and the energy sloshes between the particles deserves hours of study and thought. If the piston had very large mass, how would it ever lose energy to the gases? Perhaps this is a good testbed for thinking about concepts of fluctuations and dissipations. 
Bonk1.cc is a worked solution for the first task:
 
// Bonk1.cc // program to test a twoparticle 'collide' function in one dimension #include <iostream> #include <cmath> #include <cstdlib> #include <cstdio> #include <string> using namespace std; #define ranf() \ ((double)random()/(1.0+(double)RAND_MAX)) // Uniform from interval [0,1) */ struct particle { double x ; // x coordinate double im ; // inverse mass double v ; // velocity double p ; // momentum double T ; // energy } ; // Note the definition of a structure ends with a semicolon void v2p( particle &a ) // propagate a velocity into a momentum // p = m * v { if( a.im > 0.0 ) a.p = a.v / a.im ; else // infinite mass particle can't be handled correctly a.p = 0.0 ; // update the energy too a.T = 0.5*a.v * a.p ; } void collide ( particle & a , particle & b ) // two particles collide elastically { // find the relative velocity double velocity_along_line = a.v  b.v; // find a's inversemass fraction double af = a.im / ( a.im + b.im ) ; double bf = b.im / ( a.im + b.im ) ; // reverse the c.o.m. velocity of each along the line of collision a.v = 2.0 * af * velocity_along_line ; b.v += 2.0 * bf * velocity_along_line ; // The collision has now happened. // Finally update the other state information v2p(a); v2p(b); } // Test the collide function using random initial conditions int main(int argc, char* argv[]) { particle a , b ; int Tests = 10 ; // set defaults // read in any commandline arguments if(argc>1) { sscanf( argv[1], "%d", &Tests ) ; // put the first commandline argument } for ( int t = 1 ; t <= Tests ; t ++ ) { a.im = ranf() + 0.1 ; // random inversemass a.v = ranf()0.5 ; // random velocity b.im = ranf() + 0.1 ; // random inversemass b.v = ranf()0.5 ; // random velocity v2p(a); v2p(b); double MomentumBefore = a.p + b.p ; double EnergyBefore = a.T + b.T ; cout << "a.m:" << 1.0/a.im << "," << "b.m:" << 1.0/b.im << endl ; cout << a.v << "," << b.v << " > " ; collide(a,b) ; cout << a.v << "," << b.v << endl; double MomentumAfter = a.p + b.p ; double EnergyAfter = a.T + b.T ; cout << "(E,P) before: " << EnergyBefore << ", " << MomentumBefore << ";" << endl ; cout << "(E,P) after: " << EnergyAfter << ", " << MomentumAfter << "." << endl ; cout << "Percentage change in energy: " << 100.0*(EnergyAfterEnergyBefore)/ (0.5*(EnergyAfter+EnergyBefore)) << endl; cout << "Percentage change in momentum: " << 100.0*(MomentumAfterMomentumBefore)/ (0.5*(MomentumAfter+MomentumBefore)) << endl; cout << endl; } return 0; }  This test program should be compiled and run in a shell. It reports for every test how big the errors in momentum and energy are. 
Bonkers1.cc is a worked solution for the next task:
 
// Bonkers1.cc // bonkers bonking in a onedimensional box // usage: // Bonkers1 [N [dt [tframe(ms) [filename]]]] // // by default, writes to /tmp/1 // movie can be made by gnuplot // load 'gnu10' #include <iostream> #include <fstream> #include <cmath> #include <cstdlib> #include <cstdio> #include <string.h> using namespace std; struct particle { double x ; // (x,y) coordinates double p ; // momentum double im ; // inverse mass double v ; // velocity double T ; // kinetic energy double a ; // radius of particle } ; // Note the definition of a structure ends with a semicolon struct control { int verbose ; // program verbosity int printing ; // period with which to print int commanding ; // period with which to issue commands int time_in_ms ; // time between frames (commands) clock_t latest_time; ofstream fout ; }; // Implement x += v dt for one particle void PositionStep ( particle &a , double dt ) { a.x += dt * a.v ; } void v2p( particle &a ) // propagate the changed velocity into the momentum vector { a.p = a.v / a.im ; } void p2v ( particle &a ) { a.v = a.p * a.im ; } void pv2T ( particle &a ) { a.T = 0.5*a.v * a.p ; } void collide ( particle & a , particle & b ) // two particles collide elastically { // find the relative velocity double velocity_along_line = a.v  b.v; // find a's mass fraction double af = a.im / ( a.im + b.im ) ; double bf = b.im / ( a.im + b.im ) ; // reverse the c.o.m. velocity of each along the line of collision a.v = 2.0 * af * velocity_along_line ; b.v += 2.0 * bf * velocity_along_line ; v2p( a ) ; v2p( b ) ; } // find next collision time double nct( particle *a , int NN , int &whichn ) { double dt = 1e100 ; // examine all adjacent pairs from 0,1 to NN2,NN1 for ( int n = 0 ; n < NN1 ; n ++ ) { double relative_velocity = a[n].v  a[n+1].v ; if(relative_velocity > 0.0) { double collision_time = ((a[n+1].xa[n+1].a)  (a[n].x+a[n].a)) /relative_velocity ; if ( collision_time < dt ) { dt = collision_time ; whichn = n ; } } } return dt ; } void leapForward( particle *a , int NN , double dt ) { for( int n = 0 ; n < NN ; n ++ ) PositionStep( a[n] , dt ) ; } void showState ( particle *a , int n0 , int NN, ostream &fout ) { for( int n = n0 ; n < NN ; n ++ ) { fout << "\t"<<a[n].x; fout << "\t"<<a[n].v; } fout << endl; } double kineticEnergy ( particle *a , int NN ) { double E = 0.0 ; for( int n=0 ; n < NN ; n ++ ) { if ( a[n].im > 0.0 ) // avoid infinitely massive objects E+=0.5*a[n].v*a[n].v/a[n].im; } return E ; } void simulateBonkers( particle *a , int NN , double &t , double dt , double T , control &c ) { double next_print_dt = 0.0, next_collision_dt ; int whichn ; int we_are_printing_this_time = 1 ; for(;t<=T;) { if( we_are_printing_this_time ) { c.fout << t << "\t" << kineticEnergy(a,NN) ; showState ( a , 0 , NN , c.fout ) ; next_print_dt = dt ; } // find the next event next_collision_dt = nct( a , NN , whichn ) ; // ^^ this returns the time, and sets 'whichn' if ( next_collision_dt < next_print_dt ) { // advance time to that event, have a collision leapForward( a , NN , next_collision_dt ) ; t += next_collision_dt ; next_print_dt = next_collision_dt ; collide( a[whichn] , a[whichn+1] ) ; we_are_printing_this_time = 0 ; } else { leapForward( a , NN , next_print_dt ) ; t += next_print_dt ; we_are_printing_this_time = 1 ; } } } int main(int argc, char* argv[]) { int N = 4 ; // number of particles double T =50.0 ; // target time particle *a ; control c ; char filename[256]="/tmp/1" ; // set defaults double dt = 0.03 ; double t = 0.0 ; c.verbose = 1 ; c.time_in_ms = 100; // real time between plots (in ms) // read in any commandline arguments if(argc>1) { sscanf( argv[1], "%d", &N ) ; // put the first commandline argument in N } if(argc>2) { sscanf( argv[2], "%lf", &dt ) ; // put the 2nd argument in dt } if(argc>3) { sscanf( argv[3], "%d", &(c.time_in_ms) ) ; } if(argc>4) { strncpy(filename, argv[4], 256 ); } // try to write output file c.fout.open(filename); if(c.fout.good()==false){ cerr << "can't write to file " << filename << endl; exit(0); } a = new particle[N+2] ; // 0 (wall) , 1...N , N+1 (wall) // two walls for ( int n=0 ; n <= N+1 ; n += N+1 ) { a[n].im = 0.0 ; a[n].v = 0.0 ; a[n].x = 1.0*n ; a[n].a = 0.0 ; // radius } // define some masses, positions, and velocities for ( int n=1 ; n <= N ; n ++ ) { a[n].im = 1.0/(10.0*n + 1.23) ; a[n].v = 1.0*n ; a[n].x = 1.0*n ; a[n].a = 0.1 ; // radius cerr<<"mass: "<<1.0/a[n].im<<endl; } for ( int n=0 ; n <= N+1 ; n ++ ) { v2p(a[n]); } simulateBonkers( a , N+2 , t , dt , T , c ) ; return 0; } 
The gnuplot file gnu10

Bonk.cc is a worked solution for the first task:
It is a general worked solution for velocities u1 u2 in any number of dimensions D.  
// Bonk.cc  demonstrates a structure called 'particle' // and a function called 'collide', which collides two particles // in D dimensions. #include <iostream> #include <cmath> using namespace std; #define D 2 // number of dimensions struct particle { double x[D] ; // (x,y) coordinates double p[D] ; // momentum double im ; // inverse mass double v[D] ; // velocity double T ; // kinetic energy } ; // Note the definition of a structure ends with a semicolon // Here's a different way passing a structure by reference void showVelocityC ( particle & a ) { // the "&" indicates 'this is a pass by // reference. for ( int i = 0 ; i < D ; i++ ) { cout << "v["<<i<<"] = "<<a.v[i]<<endl; } // cout << "Kinetic energy = " << a.T << endl ; } void makeVelocityC ( particle & a ) { a.T=0.0; for ( int i = 0 ; i < D ; i++ ) { a.v[i] = a.p[i] * a.im ; a.T += 0.5*a.v[i] * a.p[i] ; } } void v2p( particle & a ) // propagate a change in velocity into the momentum vector { for ( int i = 0 ; i < D ; i++ ) { a.p[i] = (1.0/a.im) * a.v[i] ; } } void collide ( particle & a , particle & b , double d[D] ) // two particles collide elastically; the line joining // their centres is in direction d, which should be normalized { // find the relative velocity double velocity_along_line = 0.0 ; for ( int i = 0 ; i < D ; i++ ) { velocity_along_line += ( a.v[i]  b.v[i] ) * d[i] ; } if(velocity_along_line<0.0) { cerr << "warning: particle collided but velocity " << "along alleged line joining centres " << "is negative: " << velocity_along_line << endl ; } // find a's mass fraction double af = a.im / ( a.im + b.im ) ; double bf = b.im / ( a.im + b.im ) ; // reverse the c.o.m. velocity of each along the line of collision for ( int i = 0 ; i < D ; i++ ) { a.v[i] = 2.0 * af * velocity_along_line * d[i] ; b.v[i] += 2.0 * bf * velocity_along_line * d[i] ; } v2p( a ) ; v2p( b ) ; } void checkCollide( particle & a , particle & b , double d[D] ) { // check normalization of d double mag = 0.0; for ( int i = 0 ; i < D ; i++ ) { mag += d[i] * d[i] ; } if ( mag < 0.99  mag > 1.01 ) { cerr << "Warning, collision direction not normalized\n" ; if (mag>0.0) { mag = 1.0 / sqrt(mag) ; for ( int i = 0 ; i < D ; i++ ) { d[i] *= mag ; } } else { cerr << "WARNING, collision direction too small for progress\n" ; } } // assume v is right and make sure p is consistent v2p( a ) ; v2p( b ) ; // check energy and momentum before double momentum_before[D], momentum_after[D] , diff[D] ; double energy_before=0.0 , energy_after =0.0; for ( int i = 0 ; i < D ; i++ ) { momentum_before[i] = a.p[i]+b.p[i] ; energy_before += 0.5*a.p[i]*a.v[i] + 0.5*b.p[i]*b.v[i] ; } collide( a , b , d ); for ( int i = 0 ; i < D ; i++ ) { momentum_after[i] = a.p[i]+b.p[i] ; energy_after += 0.5*a.p[i]*a.v[i] + 0.5*b.p[i]*b.v[i] ; } if( fabs(energy_afterenergy_before) > 1e4 ) { cerr << "Warning  energy mismatch?\n" ; } cerr << "\tBefore\tAfter\n" ; cerr << "Energy\t" << energy_before << "\t" << energy_after << endl; int mismatch = 0 ; cerr << "\tBefore\t\tAfter\n" ; for ( int i = 0 ; i < D ; i++ ) { diff[i] = momentum_before[i]  momentum_after[i] ; if (( diff[i]*diff[i] / ( momentum_before[i] + momentum_after[i] + 1e5 ) / ( momentum_before[i] + momentum_after[i] + 1e5 ) )>1e10 ) { mismatch ++ ; } cerr << "Mom["<<i<<"]\t"<< momentum_before[i] << "\t\t" << momentum_after[i] << endl ; } if ( mismatch ) cerr << "WARNING  momentum mismatch?\n" ; } int main() { particle a ; particle b ; a.im = 1.0/10 ; a.v[0] = 1.0; a.v[1] = 0.0; b.im = 1 ; b.v[0] = 0.0; b.v[1] = 0.0; double d[2] = { 0.7 , 0.7 } ; // double d[2] = { 1 , 0 } ; cout << "a's velocity before\n" ; showVelocityC( a ) ; cout << "b's velocity before\n" ; showVelocityC( b ) ; checkCollide( a , b , d ) ; cout << "a's velocity after\n" ; showVelocityC( a ) ; cout << "b's velocity after\n" ; showVelocityC( b ) ; a.im = 1.0/10 ; a.v[0] = 1.0; a.v[1] = 0.0; b.im = 1.0/10 ; b.v[0] = 0.0; b.v[1] = 0.0; cout << "a's velocity before\n" ; showVelocityC( a ) ; cout << "b's velocity before\n" ; showVelocityC( b ) ; checkCollide( a , b , d ) ; cout << "a's velocity after\n" ; showVelocityC( a ) ; cout << "b's velocity after\n" ; showVelocityC( b ) ; return 0; } 