I modeled it off of the Lennard-Jones potential (wikipedia)
Each sphere is basically one atom or molecule and it's potential energy is the sum of its Lennards-Jones potential relative to every other atom
Note that particles won't go to the distance where potential energy is zero, but will go to the bottom of the potential well where potential energy is negative, slightly before the edge of the particle where potential energy is zero.
A more accurate simulation would put the potential energy into the potential energy function in Schrödinger's equation at every point to model the change in the probability waves of atoms. But that is really hard to program since it uses derivatives of vector fields. Instead, it can be approximated for a worse but way faster and easier numerical solution.
In the simulation above this exact equation is implemented except when approaches very close to because the Lennards-Jones potential has an extremely steep incline, so the derivative makes an extremely large acceleration. Because the simulation only does one physics tick in a set time frame it can't simulate sharp changes. Realistically the atoms would be pushed apart before it reaches that close the simulation does have atoms get that close due to a large enought causing innaccuraccies in each time tick. To compensate the equation is disabled for when , and instead applies an elastic collission which is closer to real life than the numerical solution. The elastic collision equation in 3D is taken from here
There are still glitches in the elastic collision detection since each sphere is only allowed one collision with every other sphere. When around a lot of other spheres it is pulled in harder than the elastic collisions push it out. The solution would to keep colliding a sphere with its neightbor until the velocity vectors are moving apart within the same physics tick. But that uses a lot of computing power and the simulation already doesn't run well, but the basic concept of IMF simulation is still there