1. The single most important fact about physics is that force is the negative gradient of energy.
2. F = ma isn't the whole truth.
The universe actually discretely exchanges momentum.
There is no such thing as a force - this is an abstract concept that makes sense to gigantic beings such as ourselves.
The universe is actually kind of like a network of automata that discretely exchange different forms of energy. I think of this as a bunch of simple merchants that act in their own self interest and buy and sell goods with the merchants around them. Nothing is created or destroyed - things can only be reordered.
But the interesting thing is, that even at scales where quantum mechanics don't apply, you can still arbitrary discretize space and time into cells and apply the same logic of the merchants trading information discretely to approximate continuous dynamics.
This is how we will simulate a fluid on a grid of automata.
According to loop quantum gravity, spacetime is like a foam of automata. Think of bubbles in your kitchen sink. Each bubble is directly touching some number of other bubbles. This is the bubble's neighborhood. I'm going to use the word neighborhood a lot here.
Moving forward it is very important that you have a picture in your head of an automata connected to an adjacent neighborhood of like automata. Each member of that neighborhood is connected to other neighborhoods and if you keep spreading out, you have the entire space (or manifold).
In our grid fluid, the neighborhood looks like a cross. For each cell, there are four neighbors. I like to call them :
n (north/up)
e (east/right)
s (south/down)
w (west/left)
and I like to call the cell that is being computed "me".
so "me" has "n", "e", "s", and "w" to worry about.
In our simulation, there are four pieces of relevant information.
x velocity
y velocity
pressure
spin/vorticity
Each cell contains four numbers that represent this information.
I like to store that information like this in the state vector of the cell :
me.xy = velocity
me.z = pressure
me.w = spin/vorticity
It's important to remember that the particles involved in water flow are way smaller than any flow features you can see and way smaller than the cell size of our simulation. So these are actually statistical values.
That means that the velocity is the average linear momentum of all the particles inside that cell.
The pressure is actually also the average linear momentum of each particle in the fluid... do you remember PV = nRT?
So temperature and pressure are macroscopic interpretations of the collisions of microscopic particles.
Here is a visualization of kinetic theory.
The vorticity is yet another average of the linear momentum of the particles in the cell... but this time it is the tendency of these momenta to go in a circle.
So all in all we have 3 different versions of kinetic energy :
xy : velocity : the tendency of the cell to move.
z : pressure : the tendency of the cell to bang into itself.
w : vorticity: the tendency of the cell to go in a circle.
So the cool thing about fluids is that all of this state information is always moving and evolving. In the next section we'll talk about how it evolves. But for now let's talk about where it is.
In our fluid simulation we will need to find the state of the fluid at different locations.
Let's say we have the previous state of the fluid stored in a texture called "A".
Now let's say we want to find out what's going on at location "U" at this point in time.
If we just look up A(U) [the state of "A" at location "U"], we will not have the right information because fluids move.
Fortunately for us, fluids move predictably. So we can guess where "U" used to be in the last state of the fluid by using the velocity at point "U" in "A" [aka A(U).xy]
This is how I like to handle this in code :
vec4 sample_A (vec2 U) {
// Make sure you're using linear texture filtering!
return texture(A,U/resolution.xy);
}
vec4 sample_A_advected (vec2 U) {
// here I subtract the velocity at A(U) from U to get a new position
vec2 U_prev = U - sample_A (U).xy ;
// U_prev is very likely to be where location "U" used to be
return sample_A (U_prev);
}
So in order to get "me", "n", "e", "s", and "w" we would say :
me = sample_A_advected (U)
n = sample_A_advected (U + vec2(0,1))
e = sample_A_advected (U + vec2(1,0))
s = sample_A_advected (U - vec2(0,1))
w = sample_A_advected (U - vec2(1,0))
This is the most difficult section. Here we are going to talk about how our four values interact with their neighbors. This gets back to the part at the beginning where we talked about merchant automata. The neighborhood around our cell is going to interact with our cell. We have to make sure that these interactions are symmetrical so that nothing is created or destroyed. Different types of energy from the neighborhood will be traded with different types of energy from the cell.
The state of the fluid evolves in the following ways :
1. The velocity experiences a force against the gradient of the pressure. Pressure is energy stored inside the cell, so this is actually the very first thing I said in this article - force is the gradient of energy.
2. The velocity experiences a force perpendicular to the spin of the field. Imagine a baseball shooter : when two wheels with opposite spin come in contact with a baseball, they shoot it out perpendicular to the arrangement of the wheels.
3. Because pressure involves banging around - this value is very volatile. So the pressure in the cell is actually the average of the pressure of the neighborhood's previous state. That is to say, the pressure was completely traded with the neighborhood.
4. The pressure increases by the divergence of the field. That is to say, if the neighborhood is getting closer to "me", "me's" pressure is going to increase. If the neighborhood is moving away from "me", "me's" pressure is going to decrease.
5. The spin of the cell is similar to the pressure in that it is also completely traded with the neighborhood.
6. The spin trades with the curl of the fluid. The curl of the fluid is perpendicular to the divergence of the fluid. This is the amount that the neighborhood is going in a circle around "me"
The most important aspect of making the automata behave symmetrically is by dividing the interaction between each cell by the number of cells in the neighborhood. So with a grid there are four neighbors, so all of our equations will have a "0.25*( ... )".
The following are the discrete equations of spinor flow (that's a made up term to describe the algorithm I made up):
(order matters - don't compute z and w before x and y)
Pure algebra :
C.x -= 0.25*(e.z-w.z+(n.w*C.w-s.w*C.w));
C.y -= 0.25*(n.z-s.z+(e.w*C.w-w.w*C.w));
C.z = 0.25*((C.z,s.y-n.y+w.x-e.x)+(n.z+e.z+s.z+w.z));
C.w = 0.25*((n.x-s.x+w.y-e.y)-(n.w+e.w+s.w+w.w-0.*C.w));
English :
me velocity -= gradient(pressure) + spin interaction
me pressure = mean_pressure_of_neighborhood + divergence_of_velocity
me vorticity = curl_of_velocity - mean_vorticity_of_neighborhood
Please email me with any comments or questions! wyatthf@gmail.com