Core¶
Core DMD analysis: fitting, result packaging, and convergence.
core ¶
Core DMD analysis: fitting, result packaging, and convergence.
This module contains the central run_dmd entry point, the
DMDResult dataclass that replaces all tuple returns, conjugate-pair
detection, and mode-convergence analysis.
Functions:
| Name | Description |
|---|---|
run_dmd |
Validate, normalise, fit BOPDMD, reorder, reconstruct, and return
a |
detect_conjugate_pairs |
Identify complex-conjugate eigenvalue pairs. |
convergence_analysis |
Sweep mode counts and collect RMSE / variance explained. |
Classes:
| Name | Description |
|---|---|
DMDResult |
Frozen dataclass holding every output of a DMD analysis. |
DMDResult
dataclass
¶
DMDResult(eigenvalues: ndarray, modes: ndarray, amplitudes: ndarray, frequencies_hz: ndarray, growth_rates: ndarray, mode_magnitudes: ndarray, phase_shifts: ndarray, conjugate_pairs: list[tuple[int, int]], reconstruction: ndarray, average_shape: ndarray, times: ndarray, sort_indices: ndarray, _dmd_object: Any)
Complete results from a DMD analysis.
Notation
The field names use plain English for readability. The standard mathematical symbols from DMD literature (Kutz et al., Tu et al.) are listed here for cross-reference:
Field Math symbol Equation
───────────────── ───────────── ────────────────────────
eigenvalues λ = σ + iω x(t) = Σ bⱼ φⱼ exp(λⱼt)
modes φ (Phi) spatial DMD modes
amplitudes b mode weights / coefficients
frequencies_hz Im(λ) / 2π oscillation frequency
growth_rates σ = Re(λ) exponential growth/decay
mode_magnitudes |φ| element-wise magnitude
phase_shifts θ arctan2(-sign(ω)·Im(φ), Re(φ))
eigenvalues
instance-attribute
¶
λ = σ + iω — complex eigenvalues, shape (n_modes,).
modes
instance-attribute
¶
φ (Phi) — complex spatial modes, shape (n_coords, n_modes).
frequencies_hz
instance-attribute
¶
Im(λ) / (2π) — oscillation frequency in Hz, shape (n_modes,).
growth_rates
instance-attribute
¶
σ = Re(λ) — exponential growth/decay rate, shape (n_modes,).
mode_magnitudes
instance-attribute
¶
|φ| — magnitude of spatial modes, shape (n_markers, 3, n_modes).
conjugate_pairs
instance-attribute
¶
Paired eigenvalue indices [(i, j), ...].
reconstruction
instance-attribute
¶
Full reconstruction with mean shape, (n_frames, n_markers, 3).
sort_indices
instance-attribute
¶
Maps amplitude-sorted position to original BOPDMD index.
pair_frequency ¶
Frequency (Hz) of the pair_index-th conjugate pair.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pair_index
|
int
|
Zero-based pair index. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Absolute frequency in Hz. |
Notes
Uses the original (unsorted) eigenvalues from the BOPDMD
object, since conjugate_pairs indices are in that space.
Source code in src/birddmd/core.py
pair_indices ¶
Return flat mode indices for the given pair numbers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pair_numbers
|
list of int
|
Zero-based pair numbers. |
required |
Returns:
| Type | Description |
|---|---|
list of int
|
Mode indices belonging to the selected pairs. |
Example
result.pair_indices([0, 2]) [0, 1, 4, 5]
Source code in src/birddmd/core.py
detect_conjugate_pairs ¶
detect_conjugate_pairs(eigenvalues: ndarray, tolerance: float = CONJUGATE_TOLERANCE) -> list[tuple[int, int]]
Identify complex-conjugate eigenvalue pairs.
Two eigenvalues λ_i and λ_j are conjugates when their real parts
match and their imaginary parts sum to zero (within tolerance).
Unmatched eigenvalues are self-paired (i, i) and treated as
real modes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
eigenvalues
|
ndarray
|
Complex eigenvalues, shape |
required |
tolerance
|
float
|
Matching tolerance for real and imaginary comparisons. |
CONJUGATE_TOLERANCE
|
Returns:
| Type | Description |
|---|---|
list of (int, int)
|
Pairs in the order they appear in eigenvalues. |
Source code in src/birddmd/core.py
run_dmd ¶
run_dmd(data: ndarray, times: ndarray | None = None, n_modes: int = DEFAULT_N_MODES, d: int = DEFAULT_DELAY, eig_constraints: set[str] = DEFAULT_EIG_CONSTRAINTS, average_shape: ndarray | None = None, n_markers: int | None = None, init_alpha: ndarray | None = None, verbose: bool = True) -> DMDResult
Run a complete DMD analysis on marker data.
This is the main entry point. It validates the input, centres the
data, fits a Hankel-preprocessed BOPDMD model, reorders results by
amplitude, reconstructs the full time series, and packages
everything into a DMDResult.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
ndarray
|
Marker data — shape |
required |
times
|
ndarray
|
Timestamps, shape |
None
|
n_modes
|
int
|
Number of DMD modes (SVD rank). |
DEFAULT_N_MODES
|
d
|
int
|
Hankel delay embedding depth. |
DEFAULT_DELAY
|
eig_constraints
|
set of str
|
Eigenvalue constraints for BOPDMD. |
DEFAULT_EIG_CONSTRAINTS
|
average_shape
|
ndarray
|
Mean shape to subtract before fitting. Required for marker data; omit only for pre-centred data. |
None
|
n_markers
|
int
|
Number of markers. Required when average_shape is provided. |
None
|
init_alpha
|
ndarray
|
Initial eigenvalue guesses for the BOPDMD optimiser. |
None
|
verbose
|
bool
|
Print progress messages. |
True
|
Returns:
| Type | Description |
|---|---|
DMDResult
|
All outputs packaged in a frozen dataclass. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If input shapes are invalid or average_shape / n_markers are missing when required. |
RuntimeError
|
If the BOPDMD fit fails. |
Examples:
>>> result = run_dmd(markers, times, n_modes=6, d=2, average_shape=avg, n_markers=8)
>>> result.frequencies_hz
array([ 4.51, -4.51, 8.76, -8.76, -0.00, 0.00])
Source code in src/birddmd/core.py
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | |
convergence_analysis ¶
convergence_analysis(markers: ndarray, times: ndarray, average_shape: ndarray, n_markers: int, max_modes: int | None = None, verbose: bool = True) -> dict
Run DMD with increasing mode counts and track accuracy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
markers
|
ndarray
|
Shape |
required |
times
|
ndarray
|
Time vector. |
required |
average_shape
|
ndarray
|
Mean shape. |
required |
n_markers
|
int
|
Number of markers. |
required |
max_modes
|
int
|
Maximum modes to test. Defaults to
|
None
|
verbose
|
bool
|
Print progress. |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
Keys: |
Source code in src/birddmd/core.py
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | |