Example: Bouncing Ball
A bouncing ball is a simple hybrid system with continuous dynamics between impacts and a discrete state update at each impact.
The continuous model is:
where \(x\) is height and \(g\) is gravitational acceleration. Introducing velocity \(v = dx/dt\), we write:
with initial conditions \(x(0)=h\), \(v(0)=0\).
At each ground contact, we would ideally apply a discrete reset:
where \(e\) is the coefficient of restitution.
In DiffSL we define an event (root) function stop_i { x } so integration
halts when \(x = 0\). A reset_i block applies the discrete velocity reset
\(v^+ = -e v^-\) at each impact. The example calls solve_dense(...)
which automatically handles repeated bounce events throughout the integration,
returning the full trajectory up to final_time.
import numpy as np
import matplotlib.pyplot as plt
import pydiffsol as ds
def solve():
final_time = 10.0
ode = ds.Ode(
"""
in_i {
g = 9.81,
h = 10.0,
e = 0.8,
}
u_i {
x = h,
v = 0.0,
}
F_i {
v,
-g,
}
stop {
x, // stop when height x reaches zero
}
reset_i {
1e-12,
-e * v,
}
""",
matrix_type=ds.nalgebra_dense,
ode_solver=ds.tsit45,
linear_solver=ds.lu,
)
params = np.array([9.81, 10.0, 0.8])
t_eval = np.linspace(0.0, final_time, 200)
solution = ode.solve_dense(params, t_eval)
ts = solution.ts
x, v = solution.ys
fig, ax = plt.subplots()
ax.plot(ts, x, label="x (height)")
ax.plot(ts, v, label="v (velocity)")
ax.set_xlabel("t")
ax.set_ylabel("state")
ax.legend()
fig.savefig("docs/images/bouncing_ball.svg")
if __name__ == "__main__":
solve()