Previous ToC Up Next

6. The WorldSnapshot Class

6.1. Three-Dimensional Slices

Alice: Although our new N-body code is written from an inherently four-dimensional perspective, we need to take three-dimensional slices in order to deal with input and output. The WorldSnapshot class allows us to do that, by taking a snapshot of all world lines at a given point in time. Here is the class code:

 class WorldSnapshot < NBody
 
   def get_acc_and_jerk(pos, vel)
     acc = jerk = pos*0                  # null vectors of the correct length
     @body.each do |b|
       r = b.pos - pos
       r2 = r*r
       r3 = r2*sqrt(r2)
       v = b.vel - vel
       acc += b.mass*r/r3
       jerk += b.mass*(v-3*(r*v/r2)*r)/r3
     end
     [acc, jerk]
   end    
 
   def collision_time_scale(mass, pos, vel)
     time_scale_sq = VERY_LARGE_NUMBER              # square of time scale value
     @body.each do |b|
       r = b.pos - pos
       v = b.vel - vel
       r2 = r*r
       v2 = v*v + 1.0/VERY_LARGE_NUMBER          # always non-zero, for division
       estimate_sq = r2 / v2              # [distance]^2/[velocity]^2 = [time]^2
       if time_scale_sq > estimate_sq
         time_scale_sq = estimate_sq
       end
       a = (mass + b.mass)/r2
       estimate_sq = sqrt(r2)/a           # [distance]/[acceleration] = [time]^2
       if time_scale_sq > estimate_sq
         time_scale_sq = estimate_sq
       end
     end
     sqrt(time_scale_sq)                  # time scale value
   end
 
   def kinetic_energy
     e = 0
     @body.each{|b| e += b.kinetic_energy}
     e
   end
 
   def potential_energy
     e = 0
     @body.each{|b| e += b.potential_energy(@body)}
     e/2                                # pairwise potentials were counted twice
   end
 
   def total_energy
     kinetic_energy + potential_energy
   end
 
   def write_diagnostics(initial_energy)
     e0 = initial_energy
     ek = kinetic_energy
     ep = potential_energy
     etot = ek + ep
     STDERR.print <<-END
     E_kin = #{sprintf("%.3g", ek)} ,\
      E_pot =  #{sprintf("%.3g", ep)} ,\
       E_tot = #{sprintf("%.3g", etot)}
        E_tot - E_init = #{sprintf("%.3g", etot - e0)}
         (E_tot - E_init) / E_init = #{sprintf("%.3g", (etot - e0)/e0 )}
     END
   end
 
 end

6.2. No Surprises

Bob: No real surprises here. Everything looks very familiar, as if it could have been lifted straight from a previous code.

Alice: As it should, since previous codes were all written from a 3-D perspective. This is the only 3-D part in an otherwise 4-D code. The main difference really is the way that we take a snapshot, something that happens in methods that are part of the WorldEra Class. Once a WorldSnapshot object has been obtained, it behaves for all intents and purposes like a NBody object.

Bob: Can you remind me why we did not call it an NBody object then? I see that the WorldSnapshot class is a subclass of NBody. What's the difference?

Alice: Good question. Let's see. I believe it is mainly an alias, a new name to remind us that we are no longer dealing with a simple N-body system. Ah, yes, I remember now. By calling it a snapshot, we wanted to emphasize the fact that we are conducting any dynamics within this class.

To put it more formally, we are calculating only the right-hand side of the equations of motion, what is often called the force calculations, acceleration and jerk in the case of the Hermite scheme. The integration of the left-hand side, the actual particle pushing, is done in other classes, specifically in WorldLine, and orchestrated by World through the intermediate class WorldEra.

Bob: So the term WorldSnaphot merely is meant to stress the modularity of our approach, isn't it?

Alice: Yes, but `merely' is an understatement. When writing large codes, modularity is more important than anything else.

Bob: I have heard you saying this before. Well, we'll see.
Previous ToC Up Next