SFI.diagnostics.dynamics_order module

Overdamped-vs-underdamped dynamics classifier.

Reads raw trajectory data and decides whether the dynamics are overdamped (OD, first-order Langevin) or underdamped (UD, second-order / inertial), robust to high localization noise and coarse sampling. Entry point: classify_dynamics().

The discriminator is the lag-resolved displacement covariance C_k(dt) = <Delta x_t . Delta x_{t+k}> with Delta x_t = x_{t+1} - x_t:

  • White localization noise (variance sigma^2 per axis) contributes +2 sigma^2 to C0, -sigma^2 to C1 and exactly 0 to ``C_{k>=2}`` — so lag >= 2 is measurement-noise-immune by construction.

  • A force field cannot fake inertia: for OD the apparent kinetic energy C0/dt^2 diverges as 2D/dt while the lag-2 velocity correlation stays finite, so the noise-corrected ratio rho2 = C2 / (C0 + 2 C1) (the combination C0 + 2 C1 cancels white localization noise exactly) tends to 0; for UD the displacement is ballistic (Delta x ~ v dt) so rho2 rises to a positive plateau. Scanning dt (via TrajectoryCollection.degrade()) removes the force confound; using lag 2 removes the noise.

The pipeline (see classify_dynamics()) layers a model-free dt-scaling test (_scaling_statistics()), a parametric covariance fit (_fit_dynamics()) and an overdamped-fit residual-autocorrelation cross-check (_cross_check()) into an OD / UD / inconclusive verdict.

class SFI.diagnostics.dynamics_order.DynamicsOrderReport(verdict, fit=<factory>, scaling=<factory>, scan=<factory>, cross_check=None, meta=<factory>)[source]

Bases: object

Result of classify_dynamics().

Variables:
  • verdict (str) – "OD", "UD" or "inconclusive".

  • fit (dict) – Parametric fit output (params = sigma2, D, V, gamma, tau_v, stderr, V_z, delta_aicc and the raw SSR / AICc values).

  • scaling (dict) – Model-free statistics (rho2, K, beta).

  • scan (dict) – Lag-0/1/2 covariances across the dt scan (strides, dt, C0, C1, C2, n_eff).

  • cross_check (dict or None) – Overdamped-fit Ljung–Box result, or None if disabled.

  • meta (dict) – Sampling summary (d, n_datasets, strides, dt_min/max, gamma_dt_min).

Parameters:
  • verdict (str)

  • fit (dict)

  • scaling (dict)

  • scan (dict)

  • cross_check (dict | None)

  • meta (dict)

cross_check: dict | None = None
fit: dict
meta: dict
print_summary()[source]

Print a human-readable summary of the classification.

Return type:

None

scaling: dict
scan: dict
to_dict()[source]

JSON-serialisable representation of the report.

Return type:

dict

to_json(indent=2)[source]
Parameters:

indent (int)

Return type:

str

verdict: str
SFI.diagnostics.dynamics_order.classify_dynamics(data, *, strides=None, cross_check=True)[source]

Classify trajectory data as overdamped, underdamped, or inconclusive.

Robust to high localization noise (lag >= 2 covariances are noise-immune) and reports inconclusive at coarse sampling where the momentum is unresolved (gamma * dt >~ 1), rather than guessing.

Parameters:
  • data (TrajectoryCollection) – Raw trajectories (positions). Velocities are not required — the test reconstructs the dynamics order from position increments alone.

  • strides (sequence of int, optional) – Coarse-graining factors for the dt scan. Default: a geometric set 1, 2, 4, ... capped so the coarsest still has enough samples.

  • cross_check (bool) – Run the Layer-3 overdamped-fit residual-autocorrelation corroboration (default True).

Return type:

DynamicsOrderReport

Notes

Assumes white localization noise; spatially uniform, isotropic pooling of components. Strong memory (a generalized Langevin / viscoelastic bath) can also produce velocity persistence without inertia — out of scope; the test detects trajectory smoothness, which inertia produces.

Examples

>>> report = classify_dynamics(collection)
>>> report.verdict
'UD'