BallNode#
Module: maddening.nodes.ball
Stability: stable
Algorithm ID: MADD-NODE-001
Version: 1.0.0
Summary#
A point mass falling under gravity in one dimension, with an optional perfectly rigid collision surface supplied as the boundary input table_position.
Governing Equations#
with, at contact (\(x < x_{\text{table}}\)), the restitution law
where \(e\) is the coefficient of restitution. Below a velocity threshold of \(10^{-4}\) the bounce is replaced by \(v \leftarrow 0\), which is what stops the ball chattering on the surface.
Discretization#
Semi-implicit (symplectic) Euler: the velocity is advanced first and the position is advanced with the already-updated velocity.
Collision is resolved once per step, after the integration, with jnp.where so the whole update stays traceable.
Declared order of accuracy: DiscretizationOrder(spatial=None, temporal=1.0).
There is no spatial order — the node integrates an ODE. The temporal claim is 1st order globally and covers the smooth regime only: a contact is a non-smooth event and no order is claimed across one.
Warning
NodeMeta.discretization names forward Euler, and the implementation is
semi-implicit Euler as written above. Both are 1st order, so the declared
order is unaffected, but the sign of the leading position error and the
long-run energy behaviour are not what the metadata implies, and
update() does not agree with
maddening.core.simulation.integrators.integrate_node(node, ..., method="euler").
Recorded as MADD-ANO-011.
Implementation Mapping#
Equation Term |
Implementation |
Notes |
|---|---|---|
\(\dot v = g\) (gravity) |
|
|
\(\dot x = v\) (kinematics) |
|
|
Contact detection |
|
|
Restitution \(v \to -e v\) |
|
Nested |
\(x_{\text{table}}\) (surface) |
|
|
Continuous right-hand side |
|
Collision-free; for |
Assumptions and Simplifications#
Point mass: no rotational dynamics
Perfectly rigid collision surface
Constant coefficient of restitution (not velocity-dependent)
One translational degree of freedom
No air resistance or drag
Validated Physical Regimes#
Parameter |
Verified Range |
Notes |
|---|---|---|
|
\(0\) – \(1\) |
\(e = 0\) perfectly inelastic, \(e = 1\) perfectly elastic |
|
\(10^{-4}\) – \(10^{-1}\) s |
Tested range; smaller is more accurate |
Known Limitations and Failure Modes#
No forcing input.
boundary_input_specoffers onlytable_position, a collision surface. A caller cannot drive this node with an external force; the only way in is thegravityparameter, which is the acceleration itself. This is whyMADD-VER-010injects its manufactured source throughparams.1st-order integration: the error is \(O(\Delta t)\) in position and velocity.
Tunnelling: contact is tested once per step, so \(|v|\Delta t\) larger than the gap passes through the surface.
No order across a contact: the restitution law is non-smooth, so the declared order does not apply to a step containing a bounce.
derivatives()is the collision-free right-hand side. It readsgravityfrom injectedparamsby the same{**self.params, **params}rule asupdate()and adds it in the velocity’s dtype (MADD-ANO-018 and theBallNodeinstance of MADD-ANO-013, both resolved in 0.4.0), but it never readstable_positionorelasticity.integrate_nodeon this node therefore passes through the surface: from 0.001 above the table at -1 m/s, one 0.01 s Euler step lands at -0.009, whereupdate()bounces to 0.0. Useupdate()for anything that reaches the table.Scheme/metadata mismatch: MADD-ANO-011, above.
Gradients through a bounce omit the contact-time derivative (MADD-ANO-021). Contact is a per-step branch, so
jax.graddifferentiates the branch the step took and never sees the moment of contact. With \(\Delta t = 10^{-3}\), a drop from 1.0 and one bounce before \(T = 0.8\) s, \(\partial x(T)/\partial h\) is exactly \(0\) against the exact \(+0.5892\). The clamp pins the position to a constant, and the impact velocity depends on the number of steps the fall took rather than on \(h\). \(\partial x(T)/\partial |g|\) is \(+0.0651\) against \(+0.0051\). \(\partial x(T)/\partial e\) is right, because the contact time does not depend on \(e\). Values are correct to one step of travel (\(3 \times 10^{-3}\)), and nothing warns. This is a framework limitation shared by every node that branches on its own state (see the node-authoring guide, “Events insideupdate()”). Pinned intests/nodes/test_ball_event_gradient.py.
Stability Conditions#
The collision-free scheme is unconditionally stable: the acceleration does not depend on the state, so there is no amplification factor to bound. The practical limit is tunnelling, not stability: keep \(|v|\,\Delta t\) well below the distance to the surface.
State Variables#
Field |
Shape |
Units |
Description |
|---|---|---|---|
|
scalar |
m |
Height of the ball |
|
scalar |
m/s |
Vertical velocity |
Parameters#
Parameter |
Type |
Default |
Units |
Description |
|---|---|---|---|---|
|
float |
0.0 |
m |
Starting height |
|
float |
0.0 |
m/s |
Starting velocity |
|
float |
0.8 |
— |
Coefficient of restitution \(e\) |
|
float |
-9.81 |
m/s² |
Gravitational acceleration |
Boundary Inputs#
Field |
Shape |
Default |
Description |
|---|---|---|---|
|
scalar |
absent |
Surface position for collision; contact is skipped entirely when omitted |
References#
[@Hairer2006] Hairer, E., Lubich, C. and Wanner, G. (2006). Geometric Numerical Integration. Springer. — Chapter VI: the order and the structure-preserving behaviour of the symplectic Euler scheme this node implements.
[@LeVeque2007] LeVeque, R.J. (2007). Finite Difference Methods for Ordinary and Partial Differential Equations. SIAM. — Convergence theory for one-step ODE methods.
[@Roache2002] Roache, P.J. (2002). Code verification by the Method of Manufactured Solutions. J. Fluids Eng. — The method the order claim above is measured with.
Verification Evidence#
Benchmark:
MADD-VER-010— observed temporal order of accuracy by the Method of Manufactured Solutions, in the collision-free regime. Because the node exposes no force input, the manufactured acceleration is injected as a time-varyinggravityparameter. Over a 100/200/400/800 step ladder at fixed final time, in float64, the observed order over the finest pair is 1.002 against the declared 1.0.Test file:
tests/verification/test_mms_order_ode_nodes.pyAnomaly:
MADD-ANO-011, pinned as a strict xfail in the same file.
Changelog#
Version |
Date |
Change |
|---|---|---|
1.0.0 |
2025-03-01 |
Initial implementation |
1.0.0 |
2026-09-20 |
Declared order of accuracy added and measured (MADD-VER-010); MADD-ANO-011 recorded |