Skip to main content

physics - Two bouncing balls in 1 dimension, issues with two different methods?



I'm trying to simulate 2 balls with the same mass and diameter bouncing one on top of another under gravity, see the illustration below (not ideal, but this is the best result I've got so far, the numbers are the time in seconds, the height of the box is 2 meters):


enter image description here


However, I'm not interested in the pretty animation, but rather in the variation of the distance with time. My main motivation - I want to see how the period of this function, if it's indeed periodic, depends on the initial conditions.


For the initial values of $h_2=2$ and $h_1=1.5$, I obtained a periodic dependence with my first method:


enter image description here


The first plot is for the positions $y_1$ and $y_2$, while the second plot is for the distance $y_2-y_1$.


This looks promising, however the method is approximate: I'm using a constant finite time step $dt$ and check the distances between the blue ball and the ground as well as between the blue and red balls. To get the correct simulation, I had to take a very small $dt$, as you can see at the top of the image.


I have tried a better way (in my opinion), of projecting the collision times $t_a$ (between the blue ball and the ground) and $t_b$ (between the balls) and then comparing them and changing the parameters accordingly. But for the same initial conditions, the results break down as shown below, compared to the first method:


enter image description here





My code for the first method is:


g = 981/10; (*gravitational acceleration in meters per second squared*)
dt = 1/20000; (*the time step*)
h10 = 15/10; (*the initial heights in meters*)
h20 = 2;
h1 = h10;
h2 = h20;
u1 = 0; (*the initial velocities*)
u2 = 0;
Tm = 20; (*the time limit*)

Nm = Floor[Tm/dt]; (*number of iterations in the loop*)
d = 5/100; (*diameter of the balls, in meters*)
Y1 = Y2 = S = Table[{1, 1}, {j, 1, Nm}]; (*lists for keeping results*)
t1 = 0; (*initial times for both balls, since for each collision they need to be updated*)
t2 = 0;
t = 0; (*the time variable*)
j = 1; (*the counter*)
y1 = h1; (*initial positions and velocities, as variables*)
y2 = h2;
v1 = u1;

v2 = u2;
While[j <= Nm, (*the loop condition*)
t = t + dt; (*time propagation*)
y1 = h1 + u1 (t - t1) - 1/2 g (t - t1)^2; (*updating positions and velocities*)
y2 = h2 + u2 (t - t2) - 1/2 g (t - t2)^2;
v1 = u1 - g (t - t1);
v2 = u2 - g (t - t2);
If[y1 <= d/2, u1 = -v1; t1 = t; h1 = y1]; (*check for collision of the blue ball with the ground, the velocity is reflected, the equation parameters are updated*)
If[y2 - y1 <= d, u1 = v2; u2 = v1;
t1 = t; t2 = t; h1 = y1; h2 = y2]; (*check for collision of the two balls, the velocities are exchanged, the equation parameters are updated*)

Y1[[j]] = {t, y1}; (*writing down the results*)
Y2[[j]] = {t, y2};
S[[j]] = {t, y2 - y1};
j++];
ListPlot[{Y1, Y2}, PlotRange -> Full, ImageSize -> Full,
AspectRatio -> 1/10] (*plotting the positions*)
ListPlot[S, PlotRange -> Full, Joined -> True, ImageSize -> Full,
AspectRatio -> 1/10] (*plotting the distance between balls*)

And for the second method:



$MaxExtraPrecision = 10000;
g = 981/10;
d = 5/100;
h10 = 15/10;
h20 = 2;
h1 = h10;
h2 = h20;
u1 = 0;
u2 = 0;
t1 = 0;

t2 = 0;
Tm = 20;
T1 = {t1}; (*initial results lists, for building the plots after the computation*)
T2 = {t2};
H1 = {h1};
H2 = {h2};
U1 = {u1};
U2 = {u2};
While[t1 < Tm, (*loop condition, now for the time*)
ta = N[t1 + (u1 + Sqrt[u1^2 + 2 (h1 - d/2) g])/g, 10000]; (*the time of the collision between the blue ball and the ground*)

tb = If[u1 - u2 + g (t1 - t2) != 0,
N[(1/2 g (t1^2 - t2^2) + u1 t1 - u2 t2 - (h1 - h2) - d)/(
u1 - u2 + g (t1 - t2)), 10000], 2 Tm]; (*the time of the collision between the balls*)
If[ta <= tb || tb <= t2, (*if the collision with the ground comes first or the collision between the balls is impossible (i.e. it happened in the past and they are flying away), then we are updating only the first ball accordingly *)
h1 = d/2;
u1 = -u1 + g (ta - t1);
t1 = ta;
T1 = Append[T1, N[t1, 10]];
H1 = Append[H1, N[h1, 10]];
U1 = Append[U1, N[u1, 10]],

h1 = h1 + u1 (tb - t1) - 1/2 g (tb - t1)^2; (*else, the collision between the balls happens first and we are updating both of them accordingly*)
h2 = h1 + d;
v1 = u2 - g (tb - t2); (*this dummy variable is needed because u1 will be used to update u2*)
u2 = u1 - g (tb - t1);
u1 = v1;
t1 = t2 = tb;
T1 = Append[T1, N[t1, 10]];
H1 = Append[H1, N[h1, 10]];
U1 = Append[U1, N[u1, 10]];
T2 = Append[T2, N[t2, 10]];

H2 = Append[H2, N[h2, 10]];
U2 = Append[U2, N[u2, 10]]]];
y[t_, h_, u_, t0_] := h + u (t - t0) - 1/2 g (t - t0)^2; (*the general position function*)
L1 = Length[T1]; (*number of collisions for the blue ball*)
L2 = Length[T2]; (*number of collisions for the red ball*)
Y1[t_] :=
Piecewise[
Table[{y[t, H1[[k]], U1[[k]], T1[[k]]],
T1[[k]] <= t < T1[[k + 1]]}, {k, 1, L1 - 1}]];
Y2[t_] :=

Piecewise[
Table[{y[t, H2[[k]], U2[[k]], T2[[k]]],
T2[[k]] <= t < T2[[k + 1]]}, {k, 1, L2 - 1}]]; (*positions over time as piecewise functions*)
Plot[Y2[t] - Y1[t], {t, 0, Tm}, AspectRatio -> 1/10,
ImageSize -> Full, PlotRange -> {0, h20}]



I thought the problem was numerical approximation to $t_a$ and $t_b$, however after greatly increasing precision, I have not seen any change in results.



Why does the second method break down? Shouldn't it be more exact than the first one? Where's my error?




To clarify: I'm not that interested in other methods, such as solving differential equations, I think this problem doesn't need them, however if there's some improvement possible for my methods while keeping the general idea, I will be very grateful.




Update


I have restarted Mathematica and the second code works just fine (even though it doesn't give the perfect periodic dependence of the first case).



So my new question: what's the problem with the second code, why is it unreliable? I'd really love to hear any thoughts on this.



enter image description here





Here are the results for $h_1=1$ and $h_2=2$, they are quite different, after enough collisions, so another question would be: which method do I trust more?


enter image description here




Update 2 - Important!


I have $g$ 10 times larger than normal above, so the time dependence needs to be scaled down.


Also, the second algorithm had been breaking down for $d=0$ in periodic cases, because when both balls were reaching the ground at the same time, their velocities needed to change signs before exchanging them, thus the second ball had been going below the ground. I fixed the problem in this part of the algorithm:


If[ta == tb,
h2 = h2 + u2 (tb - t2) -
1/2 g (tb -
t2)^2;(*first, an exotic case of both balls reaching the \

ground at the same time, only possible for d=0, I had to make it \
though to avoid errors*)
h1 = h1 + u1 (tb - t1) - 1/2 g (tb - t1)^2;
v1 = -u2 + g (tb - t2);
u2 = -u1 + g (tb - t1);
u1 = v1;
t1 = tb;
t2 = tb;
T1 = Append[T1, N[t1, 10]];
H1 = Append[H1, N[h1, 10]];

U1 = Append[U1, N[u1, 10]];
T2 = Append[T2, N[t2, 10]];
H2 = Append[H2, N[h2, 10]];
U2 = Append[U2, N[u2, 10]]]; If[
ta < tb ||
tb <= t2,(*if the collision with the ground comes first or the \
collision between the balls is impossible (i.e.it happened in the \
past and they are flying away),then we are updating only the first \
ball accordingly*)
h1 = d/2;

u1 = -u1 + g (ta - t1);
t1 = ta;
T1 = Append[T1, N[t1, 10]];
H1 = Append[H1, N[h1, 10]];
U1 = Append[U1, N[u1, 10]],
If[ta > tb && tb > t2,
h2 = h2 + u2 (tb - t2) - 1/2 g (tb - t2)^2;(*else,
the collision between the balls happens first and we are updating \
both of them accordingly*)
h1 = h1 + u1 (tb - t1) - 1/2 g (tb - t1)^2;

v1 = u2 -
g (tb - t2);(*this dummy variable is needed because u1 will be \
used to update u2*)
u2 = u1 - g (tb - t1);
u1 = v1;
t1 = tb;
t2 = tb;
T1 = Append[T1, N[t1, 10]];
H1 = Append[H1, N[h1, 10]];
U1 = Append[U1, N[u1, 10]];

T2 = Append[T2, N[t2, 10]];
H2 = Append[H2, N[h2, 10]];
U2 = Append[U2, N[u2, 10]]]]

Answer



Your first block of code never makes an approximation. Mathematica will do exact arithmetic when passed exact parameters.


Your second block of code makes many approximations. By removing the N calls you can recover the exact results from the first code, but it takes way longer to run (because it needs massive amounts of memory to store all the intermediate symbolic results).


On the other hand, I'm not entirely clear as to why you want exact results.


I had some fun and wrote a quick little compiled, generalized version of your code which let me speed things up some.


Here's what you get out of that:


AbsoluteTiming[

trajData =
bounceBalls[
20,
{
<|"Height" -> 157/100|>,
<|"Height" -> 2|>
},
"TimeStep" -> 1./30000,
"Gravity" -> 10*OptionValue[bounceBalls, "Gravity"]
];

]

{12.4596, Null}

traj = Thread[ {#["Time"], #["Heights"]}] & /@ trajData // Transpose;
ListLinePlot[traj,
PlotStyle -> AbsoluteThickness[1],
AspectRatio -> 2/10
]


doop


pdiff = {#["Time"], Abs[Subtract @@ #["Heights"]]} & /@ trajData;
ListLinePlot[pdiff,
PlotStyle -> AbsoluteThickness[1],
AspectRatio -> 2/10
]

moop


The difference between this and the exact result is small:


Y1[[All, 2]] - traj[[1, All, 2]] // Mean


-0.000439656

Y2[[All, 2]] - traj[[2, All, 2]] // Mean

0.0000223441

Here's the code for it:


bounceBallsCore =
Compile[{

{ballInit, _Real, 2},
{g, _Real},
{t0, _Real},
{timeStep, _Real},
{steps, _Integer}
},
(* balls are specified by {y, v, t, r, e} *)

Module[{ balls, t = t0, dt},
(* turn this into {y, v, y0, v0, t0, r, e}, where y0, v0,

and t0 are fed into the eq. of motion *)
balls =
Map[Join[#[[;; 2]], #] &, ballInit];
Table[
t += timeStep;
(* move all balls *)
Do[
dt = (t - balls[[i, 5]]);
balls[[i, 1]] =
balls[[i, 3]] + balls[[i, 4]]*dt - 1/2*g*dt^2;

balls[[i, 2]] =
balls[[i, 4]] - g*dt,
{i, Length@balls}
];
(* handle collisions *)
Do[
(* we'll assume the balls are pre-sorted by height *)
(*
ground collision *)
If[i == 1,

If[balls[[i, 1]] < balls[[i, 6]],
balls[[i, 3]] = balls[[i, 1]] = Max@{balls[[i, 1]], 0};
balls[[i, 4]] = -balls[[i, 2]]*balls[[i, 7]];
balls[[i, 5]] = t;
]
];
(* intra-ball collision *)
If[Length@balls > i,
Do[
If[

balls[[j, 1]] <
balls[[i, 1]] ||
(balls[[j, 1]] - balls[[i, 1]] <
balls[[i, 6]] + balls[[j, 6]]),
balls[[i, 3]] = balls[[i, 1]] =
Max@{Min@{balls[[i, 1]], balls[[j, 1]]}, 0};
balls[[j, 3]] = balls[[j, 1]] =
Max@{Max@{balls[[i, 1]], balls[[j, 1]]}, 0};
balls[[i, 4]] = balls[[j, 2]]*balls[[j, 7]];
balls[[j, 4]] = balls[[i, 2]]*balls[[i, 7]];

balls[[i, 5]] = t;
balls[[j, 5]] = t;
],
{j, i + 1, Length@balls}
]
],
{i, Length@balls}
];
(* return balls *)
balls,

steps
]
]
];

Options[bounceBalls] =
{
"Gravity" ->
QuantityMagnitude[
Quantity[1., "StandardAccelerationOfGravity"],

("Meters"/"Seconds"^2)
],
"TimeStep" -> 1./100,
"Radius" -> 2.5/100,
"Elasticity" -> 1,
"Velocity" -> 0,
"Interpolate" -> False
};
bounceBalls[
t0 : _?NumericQ : 0,

tf_?NumericQ,
ballSpecs : {KeyValuePattern[{"Height" -> _?NumericQ}] ..},
ops : OptionsPattern[]
] :=
Module[{
g, dt, r, e, v,
steps,
balls,
pos
},

{g, dt, r, e, v} =
OptionValue[{"Gravity", "TimeStep", "Radius", "Elasticity",
"Velocity"}];
balls =
Fold[
If[MatchQ[#, KeyValuePattern[{#2[[1]] -> _?NumericQ}]],
#,
Append[#, #2]
] &,
#,

{"InitialTime" -> t0, "Radius" -> r, "Elasticity" -> e,
"Velocity" -> v}
] & /@
ballSpecs;
steps = Floor[(tf - t0)/dt];
MapIndexed[
<|
"Time" -> t0 + dt*#2[[1]],
"Heights" -> #[[All, 1]],
"Velocities" -> #[[All, 2]],

"Radii" -> #[[All, 6]],
"Elasticities" -> #[[All, 7]]
|> &,
bounceBallsCore[
SortBy[
Lookup[
balls, {"Height", "Velocity", "InitialTime", "Radius",
"Elasticity"}],
#["Height"] &
],

g,
t0,
dt,
steps
]
]
]

And here's a fun result of the generalization. We'll slowly decrease the elasticity of the collision for the bottom balls:


frames =

Table[
With[{
trajData2 =
bounceBalls[
20,
{
<|"Height" -> 157/100, "Elasticity" -> e|>,
<|"Height" -> 2|>
},
"TimeStep" -> 1./500,

"Gravity" -> 10*OptionValue[bounceBalls, "Gravity"]
]
},
ListLinePlot[
Thread[ {#["Time"], #["Heights"]}] & /@ trajData2 // Transpose,
PlotStyle -> AbsoluteThickness[1],
AspectRatio -> 2/10
]
],
{e, 1, .99, -.0005}

];
ListAnimate[frames]

enter image description here


We can see a nice downward trend in the trajectory and some changing of the bounce period (when it doesn't get quashed completely)


Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....