Skip to main content

FlowPDE

Flow-based generative models for forward and inverse PDE problems.

PyPI Python versions Tests License: MIT

Documentation · Installation · Quickstart · API reference · Project report

FlowPDE is a PyTorch library for learning conditional distributions between PDE fields. It combines flow matching with neural ODEs to solve forward problems and inverse problems.

What is included

  • Poisson, Burgers, and Darcy dataset generation through Exponax
  • Forward and inverse datasets, including noisy and partial observations
  • Flow matching and maximum-likelihood objectives over the same NeuralODEFlow
  • MLP, ConvNet, ResNet, and UNet backbone neural network models
  • ODE sampling, EMA training, evaluation in physical units, and reflow

Each of the six problem settings is drawn out under Problem settings.

Installation

FlowPDE requires Python 3.11 or newer.

pip install "flowpde[data]"   # library + Exponax/JAX PDE data generators
pip install flowpde           # library only (bring your own data)

The core install covers the flows, objectives, models, solvers, trainer, metrics and FieldNormalizer. The data extra adds JAX and Exponax, which the Poisson, Burgers and Darcy generators in flowpde.datasets need.

From source

With uv, which installs the versions recorded in uv.lock together with the development tools:

git clone https://github.com/sarperyn/FlowPDE.git
cd FlowPDE
uv sync
source .venv/bin/activate

With pip:

git clone https://github.com/sarperyn/FlowPDE.git
cd FlowPDE
python3 -m venv .venv               # any Python 3.11+
source .venv/bin/activate
python -m pip install -e ".[data]"

For CUDA/JAX and the optional dependencies, see the installation guide.

Quick Example

import torch
from torch.utils.data import DataLoader

from flowpde import NeuralODEFlow, FlowMatchingObjective, Trainer, UNet
from flowpde.datasets import PoissonGenerator, FieldNormalizer

# Generate and normalize f -> u training pairs.
generator = PoissonGenerator(num_spatial_dims=2, num_points=64, domain_extent=10.0)
train_ds = generator.generate(num_samples=1000, seed=42, problem="forward")

normalizer = FieldNormalizer.from_dataset(train_ds)
train_ds.set_normalizer(normalizer)
loader = DataLoader(train_ds, batch_size=32, shuffle=True)

# The flow defines the dynamics; the objective defines how they are trained.
model = UNet(spatial_dim=2, spatial_size=64, base_channels=64)
flow = NeuralODEFlow(model, target_key="target", condition_key="input")
objective = FlowMatchingObjective(flow, path="linear", time_sampler="uniform")

optimizer = torch.optim.Adam(objective.parameters(), lr=1e-4)
trainer = Trainer(objective, optimizer, device="cpu", ema_decay=0.999)
trainer.train(loader, epochs=100, print_stats_interval=10,
              save_dir="results/poisson/", save_interval=25)

# Generate a solution conditioned on a PDE input.
batch = next(iter(loader))
samples = objective.sample(batch["input"], n_steps=50).view_as(batch["target"])

The full quickstart adds validation, checkpointing, and evaluation in physical units.

Notebooks

Six runnable notebooks in notebooks/ pick up where the snippet above stops:

The lockfile installs the kernel but not a Jupyter front-end, so either open the files directly in an editor that renders notebooks, or bring one along for the run:

uv run --with jupyterlab jupyter lab notebooks/    # uv
pip install jupyterlab && jupyter lab notebooks/   # activated virtualenv

Problem settings

Every PDE is available in both directions: problem='forward' and problem='inverse' are arguments on the same generator, and all six settings are trained as the same conditional flow. What changes between them is which fields are handed to the model as conditioning and which field it has to generate.

Each figure below reads the same way. The left zone is the conditioning input $c$, channel by channel. The right zone is the probability path the flow transports: a draw from the base distribution at $t=0$, the interpolant at $t=0.5$, and the target at $t=1$. What is learned is the velocity field $v_\theta(x, t \mid c)$ connecting them, and sampling is one ODE solve. The footer of each figure is the generator call that produced the fields shown.

Poisson — $\nabla^2 u = f$

Poisson forward: source to solution

Poisson inverse: noisy solution to source

Burgers — $\partial_t u + u \partial_x u = \nu \partial_x^2 u$

Burgers forward: initial state to final state

Burgers inverse: noisy final state to initial state

Darcy — $-\nabla \cdot (\kappa \nabla u) = f$

Darcy forward: coefficient and source to solution

Darcy inverse: sparse noisy solution to coefficient

The Darcy inverse problem has three variants, selected with inverse_mode: recover the coefficient $\kappa$ from $(u, f)$, recover the source $f$ from $(u, \kappa)$, or recover both jointly from $u$ alone. Observations are degraded independently of the direction — obs_noise_std adds Gaussian noise, and obs_mask_fraction is the fraction of grid points that stay observed: anything below 1.0 zeroes out the rest and appends the observation mask as an extra conditioning channel.

Results

Every comparison below varies one axis with everything else held fixed — data, splits, normalization, optimizer, schedule, averaging, sampler and evaluation seed — so a difference between two numbers is attributable to the thing being varied. Errors are relative $L^2$ in physical units on a held-out test split.

Forward problems

The forward direction is reported as verification rather than as the case for the method. Timed against the conjugate-gradient solver it replaces, the surrogate is 1.9× slower at its cheapest setting and 101× at the setting the accuracy numbers use; on a linear elliptic problem at this size it does not pay for itself.

For 1D Burgers, the map from an initial state $u(x,0)$ to the evolved state $u(x,T)$ reaches a test relative $L^2$ of 0.021 with a ConvNet backbone.

Burgers forward prediction and error

For 2D Poisson, mapping a source $f$ to its solution $u$ in the harder eight-mode source regime reaches 0.078. Widening the source spectrum alone raises the ConvNet's error by 43% — an operator-learning number is a joint statement about the method and the distribution it was measured on, so the harder regime is the one quoted here.

Poisson forward prediction and error

What else the experiments found

  • Conditioning. Replacing the conditioner with one that discards its input costs a factor of 17 in median relative $L^2$ while leaving the training loss curve looking healthy — the concrete case for selecting on sampled error, never on the loss.
  • Backbone. The smallest of four backbones is the most accurate on Burgers and Darcy, by 12.4× on Burgers, and attention changes nothing beyond seed noise. Capacity is not the binding constraint at this scale. The ordering reverses in one place only, on the widened Poisson source, where the UNet wins by 15%.
  • Objective. Fitting the same flow by exact maximum likelihood rather than flow matching improves test likelihood from $-1.21$ to $-1.86$ nats/dim while degrading sample accuracy from 0.61 to 1.06 relative $L^2$, at 70× the wall-clock and 8.9× the memory. Each objective wins on the metric it optimizes.
  • Components. Logit-normal time sampling cuts trajectory straightness from 8.17 to 3.70 and is 22% better at two to four evaluations, but 31% worse once the budget is adequate; minibatch-OT coupling straightened the interpolant and not the trajectories, and did not pay at any budget.
  • Amortization. A preconditioned Crank–Nicolson reference chain had not mixed after 52,000 forward solves per observation, costing 701 s per observation and producing nothing usable; the trained flow produced its 32-member ensemble in 16 s and needed no forward solves at all. On the smaller Burgers problem where the chain does converge the comparison runs the other way — pCN is five to ten times more accurate and the flow's posterior 6.6–11.1× too wide — but training is paid once, and at 76 s per observation against the flow's 4.0 the two break even after ten inversions.

About the experimentations done using this library; the flow, objective, conditioner, backbone and solver are orthogonal components rather than dependent objects into each other and, thus, every comparison above was a configuration change and this design choice clearly made easier to experiment with different configurations.

Full setup, ablations, and the negative results are in the project report.

The sampler

Solver and step count are call-time arguments on frozen weights, so the whole runs without retraining anything.

Accuracy against sampling cost

First-order Euler performs better than fourth-order Runge–Kutta when both use the same computational cost, for every model and both problems, with improvements of up to 8.6×. Higher-order methods such as Runge–Kutta evaluate the learned velocity field at extra points away from the actual trajectory, where the model may be less accurate. Also, integrating until full convergence does not always give the smallest error. For two of the eight models, the best fixed-step result has 6–11% lower error than the converged result. This happens because the numerical integration error can partly cancel the model error. Therefore, reporting accuracy only “at 50 steps” can be misleading, since the error does not always decrease as the number of steps increases and the behavior depends on the model.

Tests

The suite covers the flow, the objectives, the solvers, EMA, normalization, reflow, straightness, and the UQ metrics, plus integration tests that generate real datasets through Exponax. Those are marked slow because they run the PDE solvers.

uv sync                             # installs pytest and ruff with the dev group

uv run -m pytest                    # full suite
uv run -m pytest -m "not slow"      # skip the Exponax integration tests
uv run -m pytest tests/test_trainer.py -v

In an activated virtualenv, install the test dependencies with pip install -e ".[data]" pytest and drop the uv run prefix — python -m pytest -m "not slow".

Documentation

The documentation is hosted at sarperyn.github.io/FlowPDE. It is built with MkDocs and API pages are generated from the docstrings by mkdocstrings, so they track the code rather than being written twice. Every push to main rebuilds and redeploys it through .github/workflows/docs.yml.

FlowPDE documentation, API reference page

Credits

No third-party code is included in this repository. Every module under flowpde/ was written specifically for this project, and the MIT licence applies to the entire codebase.

The library does, however, use methods from existing research. These include formulas, training schedules, and model architectures described in published papers and reimplemented here.

Methods implemented from the literature

Component Implements Source
flows/neural_ode.py Continuous normalizing flow; instantaneous change of variables Chen et al., Neural Ordinary Differential Equations, NeurIPS 2018 · Grathwohl et al., FFJORD, ICLR 2019
flows/neural_ode.py Stochastic trace estimator Hutchinson, A Stochastic Estimator of the Trace of the Influence Matrix, Commun. Stat. 1989
objectives/maximum_likelihood.py Maximum-likelihood training of a CNF Rezende & Mohamed, Variational Inference with Normalizing Flows, ICML 2015 · Grathwohl et al., ICLR 2019
objectives/flow_matching.py, flows/components/paths.py Flow-matching objective; linear and OT-conditional paths Lipman et al., Flow Matching for Generative Modeling, ICLR 2023 · Liu et al., Rectified Flow, ICLR 2023 · Tong et al., Improving and Generalizing Flow-Based Generative Models with Minibatch Optimal Transport, TMLR 2024
flows/components/couplings.py Mini-batch optimal-transport coupling Tong et al., TMLR 2024
flows/components/time_samplers.py Logit-normal sampling of the flow time Esser et al., Scaling Rectified Flow Transformers, ICML 2024
trainers/reflow.py, estimate_straightness Reflow; the straightness functional Liu et al., ICLR 2023
trainers/ema.py Weight averaging, including the min(d, (1+n)/(10+n)) warmup schedule Ho et al., Denoising Diffusion Probabilistic Models, NeurIPS 2020, and its reference implementation
models/components.py Sinusoidal time embedding Vaswani et al., Attention Is All You Need, NeurIPS 2017 · Ho et al., NeurIPS 2020
models/unet.py Encoder–decoder with skip connections Ronneberger et al., U-Net, MICCAI 2015 · Ho et al., NeurIPS 2020, for the time-conditioned variant
models/resnet.py Residual basic block He et al., Deep Residual Learning for Image Recognition, CVPR 2016
core/base_conditioner.py FiLM conditioning Perez et al., FiLM: Visual Reasoning with a General Conditioning Layer, AAAI 2018
datasets/exponax/darcy.py Log-normal Gaussian-random-field coefficients; the Darcy benchmark setup Li et al., Fourier Neural Operator for Parametric PDEs, ICLR 2021
utils/metrics.py Relative $L^2$ convention for operator learning Kovachki et al., Neural Operator, JMLR 2023
utils/uq_metrics.py CRPS and the energy score Gneiting & Raftery, Strictly Proper Scoring Rules, JASA 2007 · Székely & Rizzo, Energy Statistics, JSPI 2013
utils/uq_metrics.py Rank histogram; spread–skill ratio Hamill, Interpretation of Rank Histograms, Mon. Weather Rev. 2001 · Fortin et al., Why Should Ensemble Spread Match the RMSE of the Ensemble Mean?, J. Hydrometeorol. 2014

A note on one deliberate reimplementation: utils/metrics.py duplicates what neuraloperator's LpLoss already provides. That is on purpose and it keeps the dependency footprint small.

Software

Package Used for Cite
PyTorch Models, training, autograd Paszke et al., NeurIPS 2019
torchdiffeq ODE integration for sampling and likelihoods Chen et al., NeurIPS 2018; dopri5 follows Dormand & Prince, JCAM 1980
Exponax Spectral PDE solvers and initial-condition generation Koehler et al., APEBench, NeurIPS D&B 2024
JAX Backend for dataset generation Bradbury et al., 2018
SciPy Linear assignment for the OT coupling Virtanen et al., Nature Methods, 2020

The project report has full bibliographic entries for everything above and cites them in context.

Metadata

Release files for flowpde 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flowpde 0.1.0
File Size Uploaded
flowpde-0.1.0.tar.gz 120.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for flowpde 0.1.0
File Interpreter ABI Platform
flowpde-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 233.1 kB

Release files / flowpde-0.1.0.tar.gz

Download URL flowpde-0.1.0.tar.gz
Size 120.2 kB
Tags Source
SHA-256 checksum
How to use checksums
8e32a3dfd8e261a7ff5a13d31614a8daebd7130eacdc99fed114b41d09fef68c
BLAKE2b-256 checksum
How to use checksums
4a4e123f4348033a5f4ce6dc717d0436e523d178e0cd9f3d313bda7eba6156cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / flowpde-0.1.0-py3-none-any.whl

Download URL flowpde-0.1.0-py3-none-any.whl
Size 112.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7196588abf3fbe9e0db903f33678bfd0b18a308ce68bc38f521d41e5ad764c45
BLAKE2b-256 checksum
How to use checksums
3adb2a395b3a6cc1cfd896a453f97f11ed092b82daeeb05438b81a9353d097f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page