Another take on this, using vector maths, and optimizing the distance from each to all some.
Suggest just exporting particles location and velocity vectors. The distance between them can be calculated anytime from the data. As these are vectors, we don't need to define a distance function. The distance from particle p1 to p2 is
d = (p1.location - p2.location).length The vector
v = (p2 - p1).normalized() gives a unit vector pointing from p1 towards p2. If we travel
d * v, the scalar distance times the direction, from p1 we will be at p2.
When calculating the distance of all particles in list to all others, can take advantage of transpose nature
d(i, j) = d(j, i), in that don't need to find the distance from p300 to p0 if have already found the distance from p0 to p300. The direction vector from p0 to p300 is the same as p300 to p0, negated.
v(i, j) = -v(j, i)
calc_dist boolean to True to show distance calculations.
Test script, uses selected (active) particle system of active object.
scn = bpy.context.scene
# active object
obj = bpy.context.object
# active particle system
psys = obj.particle_systems.active
calc_dist = False # True calculate the distance between parts.
f = scn.frame_start
while f <= scn.frame_end: # iterate over whole frame range.
parts = [(i, p) for i, p in enumerate(psys.particles) if p.birth_time <= f <= p.die_time]
#parts = [(i, p) for i, p in enumerate(psys.particles) if p.alive_state == 'ALIVE'] # equivalent
print("Frame %4d %4d alive" % (f, len(parts)))
print("-" * 72)
i, p1 = parts.pop(0) # keep popping off left
print("Particle %4d %s %s " % (i , str(p1.location), str(p1.velocity)))
for j, p in parts:
v = p1.location - p.location
print(" -> %4d %12.4f" % (j, v.length))
print("-" * 72)
f += 1