SpringDamperNode#
Module: maddening.nodes.spring
Stability: stable
Algorithm ID: MADD-NODE-003
Version: 1.0.0
Summary#
A linear spring with viscous damping connecting a point mass to a moving attachment point. The node owns one end of the spring; the other end arrives each step as the boundary input anchor_position.
Governing Equations#
where \(x\) is the position of this end of the spring, \(a(t)\) the anchor position, \(\ell_0\) the rest length, \(k\) the stiffness, \(c\) the damping coefficient and \(m\) the point mass.
Discretization#
Semi-implicit (symplectic) Euler: the velocity is advanced first and the position is advanced with the already-updated velocity.
Declared order of accuracy: DiscretizationOrder(spatial=None, temporal=1.0).
There is no spatial order — the node integrates an ODE and has no grid. The temporal claim is 1st order globally in both state fields. Unlike a body under a state-independent force, the position does not gain an order here: the spring force depends on the position itself, so the leading error term does not cancel. The claim is measured, not asserted; see Verification Evidence.
Implementation Mapping#
Equation Term |
Implementation |
Notes |
|---|---|---|
\(-k(x - a - \ell_0)\) (spring force) |
|
|
\(-c\dot{x}\) (viscous damping) |
|
|
\(a(t)\) (anchor position) |
|
|
Velocity update (\(\dot v = F/m\)) |
|
|
Position update (\(\dot x = v\)) |
|
|
Continuous right-hand side |
|
For |
Backward-Euler residual |
|
\(x^{n+1} - x^n - \Delta t f(x^{n+1})\) |
Reaction force delivered over a flux edge |
|
|
Coupled-pair limit (warning) |
|
|
Converged step of an anchored pair |
|
The map on \((x_a - x_b, v_a, v_b)\) the warning takes the spectral radius of |
Assumptions and Simplifications#
Linear spring (Hooke’s law) — no hardening or softening
Viscous damping, linear in velocity
Point mass: no rotational dynamics
One translational degree of freedom
No collision detection with other objects
Validated Physical Regimes#
Parameter |
Verified Range |
Notes |
|---|---|---|
|
\(10^{-2}\) – \(10^{6}\) N/m |
Very stiff springs need a small \(\Delta t\) |
|
\(0\) – \(10^{4}\) N·s/m |
|
|
\(> 0\) kg |
Zero mass divides by zero; rejected in the constructor |
Known Limitations and Failure Modes#
1st-order integration: the error is \(O(\Delta t)\) in both position and velocity
No stability check: a single node is stable only for \(k\,\Delta t^2 + 2c\,\Delta t < 4m\) (see Stability Conditions), and nothing enforces it at runtime
A calibrated constant reaches
derivatives()andimplicit_residual()only when it is passed. Both take the injectedparamsby the same{**self.params, **params}rule asupdate()(MADD-ANO-018, resolved in 0.4.0), so hand the node’sgm.paramsentry tointegrate_node(..., params=)orimplicit_euler_step(..., params=); called without it, they integrate the constructor constantsNo nonlinear spring behaviour, and no contact
Two nodes anchored on each other in a converged coupling group are unstable for \(k\,\Delta t > c\), far inside the single-node limit, and for \(c\,\Delta t + k\,\Delta t^2 > 2m\).
compile()warns about such a pair and does not refuse it (MADD-ANO-098). See Stability Conditions.
Stability Conditions#
One node, fixed or prescribed anchor. The step maps \((x, v)\) by a matrix with trace \(2 - (k\Delta t^2 + c\Delta t)/m\) and determinant \(1 - c\Delta t/m\). The Jury conditions give the exact bound
which is \(\Delta t < 2\sqrt{m/k}\) (\(\omega \Delta t < 2\)) undamped. Damping tightens it.
Two nodes anchored on each other in a converged coupling group (each node’s position is the other’s anchor_position, with rest lengths \(+\ell_0\) and \(-\ell_0\) so that they form one spring). At the converged fixed point each node’s force reads its own position at the old time and its partner’s at the new time. The spring forces therefore do not cancel, and the pair’s momentum is not conserved. For an equal pair (the same \(k\), \(c\) and \(m\)), the sum \(S = v_a + v_b\) decouples exactly:
The coupling iteration converges for \(k\Delta t^2 < m\); Gauss-Seidel contracts by \((k\Delta t^2/m)^2\) per sweep. Where it converges and \(c\Delta t < m\), the pair is stable only for
and the factor is exactly 1 at equality. The relative motion \(x_a - x_b\) is stable whenever \(c\,\Delta t < 2m\), so the instability is the pair drifting off together, with a speed that grows every step. The single-node bound does not see it. With \(k = 1000\), \(c = 2\), \(m = 0.5\) and \(\Delta t = 0.01\), the single node has \(k\Delta t^2 + 2c\Delta t = 0.14\) against \(4m = 2\), while the pair’s centre-of-mass velocity grows by a factor of 1.2 per step. Without a coupling group the pair exchanges positions lagged by the schedule. That is a different scheme with its own limit, which this bound does not describe; it also diverges at those constants.
Above \(c\Delta t = m\) the factor is negative, and it passes \(-1\) at \(c\Delta t + k\Delta t^2 = 2m\). So where the iteration converges, an equal pair is stable exactly for
Past the upper figure the pair’s common velocity flips sign and grows every step, though \(c > k\Delta t\): at \(k = 6000\), \(c = 150\), \(m = 1\) and \(\Delta t = 0.01\) each node has \(3.6\) against \(4m = 4\) and \(g = -1.25\). Every equal pair past the single-node bound is past the upper figure too. For an unequal pair the factor has no closed form; the converged step is the linear map on \((x_a - x_b, v_a, v_b)\) that solves the two coupled velocity updates, and its spectral radius is the growth per step. The common translation of the pair, an eigenvalue of exactly 1, is left out of that map.
The compile-time warning. GraphManager.compile() warns, with a UserWarning naming both nodes, their constants, the growth factor and MADD-ANO-098, when it finds two SpringDamperNodes in one coupling group anchored on each other and the spectral radius of their converged step, from the live gm.params values, exceeds 1 by more than one part in a million. It recognises the pattern conservatively: each position reaches the other’s anchor_position through an edge with no transform (or the built-in identity) that is neither additive nor mapped and is the anchor’s only writer; both nodes have one timestep; the group’s max_iterations is at least 2. It does not judge \(k\Delta t^2 \ge m\) (the iteration does not converge, so the converged step is not the one that runs), a wrapped node or a subclass that overrides update, or a value supplied after compile. It warns and never refuses. The full list is in the docstring of maddening.nodes.spring._anchored_pair_advisories.
State Variables#
Field |
Shape |
Units |
Description |
|---|---|---|---|
|
scalar |
m |
Position of this end of the spring |
|
scalar |
m/s |
Velocity of this end |
Parameters#
Parameter |
Type |
Default |
Units |
Description |
|---|---|---|---|---|
|
float |
100.0 |
N/m |
Spring constant \(k\) |
|
float |
1.0 |
N·s/m |
Damping coefficient \(c\) |
|
float |
1.0 |
kg |
Point mass \(m\) |
|
float |
1.0 |
m |
Natural length \(\ell_0\) |
|
float |
0.0 |
m |
Starting position |
|
float |
0.0 |
m/s |
Starting velocity |
Boundary Inputs#
Field |
Shape |
Default |
Description |
|---|---|---|---|
|
scalar |
0.0 |
Position of the other end of the spring |
Boundary Fluxes#
Field |
Shape |
Units |
Description |
|---|---|---|---|
|
scalar |
N |
Spring-damper force for a downstream node |
References#
[@Hairer2006] Hairer, E., Lubich, C. and Wanner, G. (2006). Geometric Numerical Integration. Springer. — Chapter VI derives the order and the energy behaviour of symplectic Euler, which is the scheme this node uses.
[@LeVeque2007] LeVeque, R.J. (2007). Finite Difference Methods for Ordinary and Partial Differential Equations. SIAM. — Convergence and stability 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-009— observed temporal order of accuracy by the Method of Manufactured Solutions. A manufactured displacement is injected throughanchor_position, which enters the force linearly, so the anchor that makes the trajectory exact is available in closed form. Over a 100/200/400/800 step ladder at fixed final time, in float64, the observed order over the finest pair is 1.029 against the declared 1.0.Test file:
tests/verification/test_mms_order_ode_nodes.pyThe same file mutation-tests the study: a mis-scaled timestep, a timestep that drifts with the resolution (a monotone ladder at order 1/2) and a source frozen at \(t=0\) are each required to fail the ladder and name this node.
Stability:
tests/nodes/test_spring_stability_limits.pychecks both bounds above against the node itself. The single node’s step matrix has spectral radius below 1 just inside \(k\Delta t^2 + 2c\Delta t = 4m\) and above 1 just outside it. The coupled pair’s measured centre-of-mass growth per step equals \((m - c\Delta t)/(m - k\Delta t^2)\) to float32 precision, either side of \(c = k\Delta t\) and at it, and on the heavily damped side. The converged-step matrix the warning judges reproduces 40 steps of the node, for equal and unequal pairs, and the same file checks the warning against the pattern.
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-009) |
1.0.0 |
2026-10-02 |
Exact single-node stability bound and the coupled-pair limit \(c \ge k\Delta t\) stated (MADD-ANO-098); numerics unchanged |
1.0.0 |
2026-10-02 |
The coupled pair’s upper limit \(c\Delta t + k\Delta t^2 \le 2m\) stated, and |