data
generates n
balls, here: 10
Note that it might be wise to make the box larger, if different WhenEvent[]
s happen at the same time (both box and ball collision), one can overwrite the other.
data = Table[{RandomReal[{-0.85, 0.85}, {3}],
RandomReal[{-1, 1}, {3}],
RGBColor[RandomReal[{-1, 1}], RandomReal[{-1, 1}],
RandomReal[{-1, 1}]]}, {i, 10}];
I've adapted this from my n-body simulation:
Generating lists for use in the NDSolve[]
n = Length[data];
positions = Transpose[Table[data[[i, 1]], {i, n}]];
velocities = Transpose[Table[data[[i, 2]], {i, n}]];
(*Setting up stuff for NDSolve[]*)
{xt, yt,
zt} = {ToExpression[Table["x" <> ToString[i], {i, n}]],
ToExpression[Table["y" <> ToString[i], {i, n}]],
ToExpression[Table["z" <> ToString[i], {i, n}]]};
{xm, ym, zm} = {Through[xt[t]], Through[yt[t]], Through[zt[t]]};
{xz, yz, zz} = {Through[xt[0]], Through[yt[0]], Through[zt[0]]};
yt = ToExpression[Table["y" <> ToString[i], {i, n}]];
zt = ToExpression[Table["z" <> ToString[i], {i, n}]];
rm = Flatten[
Table[If[i != j,
Sqrt[(xm[[j]] - xm[[i]])^2 + (ym[[j]] - ym[[i]])^2 + (zm[[j]] -
zm[[i]])^2]], {i, n}, {j, n}]] /. Null -> Sequence[];
No friction or gravity:
xf = Thread[D[D[xm, t], t] == ConstantArray[0, n]];
yf = Thread[D[D[ym, t], t] == ConstantArray[0, n]];
zf = Thread[D[D[zm, t], t] == ConstantArray[0, n]];
Friction and gravity:
xf = Thread[D[D[xm, t], t] == -0.01 D[xm, t]];
yf = Thread[D[D[ym, t], t] == -0.01 D[ym, t]];
zf = Thread[D[D[zm, t], t] == -0.01 D[zm, t] - ConstantArray[0.5, n]];
The final equations for the differential equation:
pos = {Thread[xz == positions[[1]]], Thread[yz == positions[[2]]],
Thread[zz == positions[[3]]]};
vel = {Thread[D[xm, t] == velocities[[1]]] /. t -> 0,
Thread[D[ym, t] == velocities[[2]]] /. t -> 0,
Thread[D[zm, t] == velocities[[3]]] /. t -> 0};
col = Table[data[[i, 3]], {i, n}];
we = {};
A list of WhenEvent[]
s, note that the ball collisions aren't exactly right, I'm too tired right now to properly add elastic collision equations, feel free to edit the code.
The first set is the ball collisions, the second is the boundary box collisions.
In these WhenEvent[]
collisions we can reduce total energy by little amounts per collision, as in the animation.
Table[AppendTo[we,
WhenEvent[
Sqrt[(xm[[i]] - xm[[j]])^2 + (ym[[i]] - ym[[j]])^2 + (zm[[i]] -
zm[[j]])^2] <= 0.2 // Evaluate, {
D[xm[[i]], t] -> 0.5 (D[xm[[i]], t] + D[xm[[j]], t]),
D[xm[[j]], t] -> 0.5 (D[xm[[i]], t] + D[xm[[j]], t]),
D[xm[[i]], t] -> 0.5 (D[ym[[i]], t] + D[ym[[j]], t]),
D[xm[[j]], t] -> 0.5 (D[ym[[i]], t] + D[ym[[j]], t]),
D[xm[[i]], t] -> 0.5 (D[zm[[i]], t] + D[zm[[j]], t]),
D[xm[[j]], t] -> 0.5 (D[zm[[i]], t] + D[ym[[j]], t])} //
Evaluate]], {i, n - 1}, {j, i + 1, n}];
Table[AppendTo[we,
WhenEvent[Abs[xm[[i]]] >= 0.9 // Evaluate,
D[xm[[i]], t] -> -D[xm[[i]], t] // Evaluate]], {i, n}];
Table[AppendTo[we,
WhenEvent[Abs[ym[[i]]] >= 0.9 // Evaluate,
D[ym[[i]], t] -> -D[ym[[i]], t] // Evaluate]], {i, n}];
Table[AppendTo[we,
WhenEvent[Abs[zm[[i]]] >= 0.9 // Evaluate,
D[zm[[i]], t] -> -D[zm[[i]], t] // Evaluate]], {i, n}];
s = NDSolve[Flatten[{xf, yf, zf,
vel, pos, we}], Flatten[{xt, yt, zt}], {t, 0, 50},
MaxSteps -> ∞];
Manipulate[
Graphics3D[
Transpose[{col,
Thread[Sphere[
Evaluate[
Flatten[Thread[Transpose[{xm, ym, zm} /. t -> a] /. s], 1]],
0.1]]}], PlotRange -> {{-1, 1}, {-1, 1}, {-1, 1}}], {a, 0, 50,
0.1}]

Nearest[objects, pos]
will find all collisions with less than $n^2$ comparisons. I frankly don't think thatNDSolve
is any good for this problem, and Feyre's code doesn't convince me otherwise. It just forces you to formulate the problem in a form that isn't natural. I've never used DEM or MD but it seems outrageously unproportional to the problem of recreating the animation in OP's link (which uses Euler integration.) $\endgroup$