# PyDMD: dynamic mode decomposition as a library of interchangeable algorithms

> PyDMD is a Python package for Dynamic Mode Decomposition, a data-driven method for pulling coherent spatiotemporal modes out of time-varying datasets, and it ships a family of DMD variants behind one fit and reconstruct interface. It also carries a preprocessor layer that wraps a model to centre, scale or reorder data before the algorithm and after reconstruction.

**PyDMD/PyDMD** — Python Dynamic Mode Decomposition

- Repository: https://github.com/PyDMD/PyDMD
- Website: https://pydmd.github.io/PyDMD/
- Stars: 1,267 · Forks: 372
- Language: Python
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/pydmd-pydmd

## What DMD is for, and what the package wraps

PyDMD is a Python package designed for Dynamic Mode Decomposition, described as a data-driven method used for analyzing and extracting spatiotemporal coherent structures from time-varying datasets. The description calls it a comprehensive and user-friendly interface for performing DMD analysis, aimed at researchers, engineers and data scientists.

The underlying idea is a linear dynamical approximation of a high-dimensional system. You take a matrix of snapshots, X of shape n by m, and look for a low-rank operator whose eigenvalues and eigenvectors capture the dominant behaviour. The modes are the eigenvectors, and their eigenvalues give the temporal behaviour, typically oscillations and decay rates.

What PyDMD adds over implementing that yourself is the family of variants. The README says it implements both standard DMD algorithms and advanced variations, and that these extensions allow to deal with noisy data, big dataset, control variables, or to impose physical structures. The topic list names the specific ones: dmd, hodmd for higher order DMD, mrdmd for multi-resolution DMD, and model-order-reduction.

The package also provides visualisation tools, including mode reconstruction, energy spectrum analysis and time evolution plotting, so you can look at what the decomposition produced rather than only reading eigenvalues.

The integration point is the scientific Python stack, leveraging NumPy and SciPy for the numerical work, and the package is a NumFocus project, which is the governance body for NumPy and SciPy itself.

## fit, then plot, in about ten lines

The quickstart is short enough to hold in your head, and the shape of it is the API.

```python3
from pydmd import DMD
from pydmd.plotter import plot_summary

# Build an exact DMD model with 12 spatiotemporal modes.
dmd = DMD(svd_rank=12)

# Fit the DMD model.
# X = (n, m) numpy array of time-varying snapshot data.
dmd.fit(X)

# Plot a summary of the key spatiotemporal modes.
plot_summary(dmd)
```

Three steps, and they are the same three steps for every algorithm in the package. You initialise a module that implements your chosen DMD method, you call fit() and pass in the data, and then you use the plotting tools to visualise the results.

The fit-and-inspect split is the design worth noting. fit() performs the algorithm and leaves you with a fitted object; nothing is printed, nothing is plotted, and the object holds the modes, the eigenvalues and the reconstruction machinery. Everything after that is a separate call on the fitted object.

That is why the same script works when you swap the class. DMD(svd_rank=12) and, say, a higher-order or multi-resolution variant all present the same constructor, the same fit method and the same attributes, so changing the algorithm is a one-line edit rather than a rewrite of the analysis code.

The svd_rank argument is where the modelling decision enters. It is the number of singular values kept in the truncated SVD that produces the low-rank approximation, and picking it badly, too low and you throw away dynamics, too high and you fit noise, is the most common way to get a result that looks plausible and means nothing.

## Preprocessors wrap the model rather than the data

The second quickstart example is short and shows a design decision that is easy to miss.

```python3
from pydmd import DMD
from pydmd.preprocessing import zero_mean_preprocessing

# Build and fit an exact DMD model with data centering.
centered_dmd = zero_mean_preprocessing(DMD(svd_rank=12))
centered_dmd.fit(X)
```

The README describes the mechanism: PyDMD modules can be wrapped with data preprocessors if desired, and these wrappers will preprocess the data and postprocess data reconstructions automatically.

So the wrapper is a decorator applied to the model, not a function applied to the array. The same DMD object is still there, but when you call fit it centres the data first, and when you ask for a reconstruction it un-centres the result. You never hold a modified copy of X.

That matters for the workflow because the reconstruction and the original data have to be in the same units. If you centre the snapshots before decomposing and then plot a reconstruction in centred units against your original data, everything is off by the mean. Handling the postprocessing inside the wrapper means that mistake is not available to you.

The package describes itself as promoting ease of use and customization, providing a well-documented API with intuitive function names and clear parameter descriptions, and the wrapper pattern is the clearest example of that. The same applies to the DMD variants themselves, since they are also constructed and then fitted.

The README also points to a diagram in the readme folder, pydmd_capabilities.svg, that summarises all the available tools and functionalities, with currently in-progress contributions represented by semi-transparent boxes. It is a map of the algorithm zoo, and it is worth opening before you choose.

## The dependency list is heavier than a linear algebra package

The pyproject.toml lists what PyDMD actually needs at runtime, and it goes well beyond numpy and scipy.

```python
dependencies = [
    "numpy",
    "scipy>=1.6.0",
    "matplotlib",
    "scikit-learn",
    "xarray",
    "h5netcdf[h5py]",
    "typing_extensions>=4.7.0;python_version<'3.12'",
    "dask[distributed, diagnostics]",
]
```

The README's own description is narrower. It says the core features depend on numpy and scipy, and that you also need matplotlib for the plotting functionalities. The packaging metadata is more expansive, and the difference is informative.

scikit-learn is there for the machine learning extensions, xarray and h5netcdf with h5py for reading and writing the labelled array data that climate and fluid workflows produce, and dask with distributed and diagnostics for the big dataset case the README mentions as one of the motivations for the extensions.

The practical consequence is that a minimal pip install pulls a considerably larger tree than the README's dependency note suggests. dask in particular is a heavy dependency, and if you only want the classic DMD on a modest array, you are installing a distributed computing stack to get there. The classifiers list Python 3.8 through 3.12 and requires-python is 3.8 or newer, with typing_extensions pulled in only for Python below 3.12.

The optional dependency groups are sensible. The test group carries pytest, pytest-cov, pytest-mock and ezyrb, the latter being the notebook execution framework used to run the tutorials in CI. The tutorial group adds jupyter, pandas, ezyrb, opencv-python and ffmpeg-python, which tells you the tutorials process images and video. The docs group is sphinx with the readthedocs theme.

## A NumFocus project with a JOSS paper and a Zenodo DOI

The governance signals in the repository are unusually complete for a scientific Python package.

The topic list includes numfocus, and the repository lives under the PyDMD organisation. There is a JOSS DOI badge pointing at 10.21105/joss.00530, and a joss-paper/ directory in the repository root, which is the submitted journal paper. There is a CITATION.cff file, a .zenodo.json for archive deposit, a CODE_OF_CONDUCT.md, and a .pylintrc.

The JOSS paper matters for a practical reason. A package with a peer-reviewed JOSS paper is one where the API is expected to remain stable, which is the opposite of a research code drop and considerably more comfortable to build on. The README says the package is actively maintained and updated, ensuring compatibility with the latest Python versions and incorporating user feedback to improve functionality and performance.

The maintainer contact is a pydmd.info address, and the authors are Nicola Demo, Marco Tezzele, Francesco Andreuzzi, Sara Ichinaga and Karl Lapo, which is a small and identifiable group rather than an anonymous commit stream.

The repository structure matches a package with real scope: a pydmd/ package directory, a tests/ directory with a pytest.ini at the root, a tutorials/ directory, a docs/ directory, a readme/ directory holding the assets including the capabilities diagram, a joss-paper/ directory, and a utils/ directory. The formatting is Black, and the CI badge points at a deploy workflow that publishes the documentation site to GitHub Pages.

## Release cadence, and what DMD does not tell you

The release history is calendar-versioned rather than semver: 2025.08.01, 2025.06.01 and 2025.05.01, with the most recent release on 2025-08-01. The last push was on 2026-10-03, so the tree is being worked on even though the newest published release is from the previous year.

A calendar version scheme is a deliberate choice in scientific software, where releases often track data or method versions rather than a compatibility contract, and it means you can tell at a glance how old a build is. The version is declared dynamic in pyproject.toml and comes from setuptools-git-versioning, so it is derived from the tag rather than written by hand.

The more important limitation is not a version question but a methodological one. DMD is a linear reduction, and a linear reduction applied to data with strong nonlinearity will return modes that decompose the data without describing the physics. The README is honest about the intent, describing the extensions as allowing users to deal with noisy data, big dataset, control variables, or to impose physical structures, but the underlying method remains linear.

There is also the rank decision, and PyDMD gives you the tool to make it badly. svd_rank=12 in the quickstart is a plausible number, and the failure mode of a too-low rank is a smooth result that has silently discarded the dynamics you were looking for. The energy spectrum plot that the package provides is the check for that, since a truncated spectrum with an abrupt cutoff is a warning sign.

For a package intended to be used on data where the answer matters, the sensible workflow is to try two or three ranks and see whether the extracted modes are stable across them, rather than to accept the first decomposition that runs.

## Conclusion

PyDMD suits a researcher or engineer who needs to find dominant dynamics in a high-dimensional time series, for instance a fluid simulation or a sensor array, and who wants to switch between exact, total, incremental or higher-order DMD without rewriting their pipeline. It is the wrong tool if you need a guarantee that the extracted modes are physically meaningful, since DMD is a linear reduction and a badly chosen rank or a noisy dataset will still return modes, and it is the wrong choice if you want a nonlinear method, since the package's scope is the linear family. Before you adopt it, read the tutorials directory, since the choice between the DMD variants is a modelling decision rather than a configuration detail, and check the dependency list, which pulls in dask, xarray and scikit-learn rather than staying on numpy and scipy.

## FAQ

### How do I install PyDMD?

Run pip install pydmd for the released version from PyPI. For the development version, clone https://github.com/PyDMD/PyDMD and run pip install -e . to install in development mode.

### What is the basic PyDMD workflow?

Initialise a module that implements your chosen DMD method, call fit() with the snapshot data, then use the plotting tools to visualise the results. The example in the README is DMD(svd_rank=12), then dmd.fit(X), then plot_summary(dmd).

### What do the PyDMD preprocessing wrappers do?

They wrap a DMD model rather than the data array. The wrapper preprocesses the data before fitting and postprocesses the data reconstructions automatically, so a reconstruction is returned in the same units as the original snapshots.

### Which Python versions and dependencies does PyDMD support?

The packaging metadata requires Python 3.8 or newer and classifies 3.8 through 3.12. Runtime dependencies include numpy, scipy, matplotlib, scikit-learn, xarray, h5netcdf with h5py, and dask with distributed and diagnostics.

### Which DMD variants does PyDMD implement?

The README says it implements both standard DMD algorithms and advanced variations, and the project topics name higher order DMD (hodmd) and multi-resolution DMD (mrdmd) alongside standard DMD and model order reduction.

## Sources

- [License: MIT](https://github.com/PyDMD/PyDMD/blob/master/LICENSE)
- [Project website](https://pydmd.github.io/PyDMD/)
- [PyDMD/PyDMD on GitHub](https://github.com/PyDMD/PyDMD)
- [README](https://github.com/PyDMD/PyDMD/blob/master/README.md)
- [Releases](https://github.com/PyDMD/PyDMD/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pydmd-pydmd
