RasmahRasmah

Model reduction

Overview

A simulation field — temperature over a mesh, velocity in a fluid — can have millions of degrees of freedom, yet the interesting behaviour often lives in a handful of dominant patterns. Model reduction finds those patterns and replaces the expensive high-dimensional system with a cheap low-dimensional one.

The two tools in this chapter are the workhorses of the field. Proper orthogonal decomposition (POD) compresses a family of fields into a few basis vectors; dynamic mode decomposition (DMD) reads the dynamics of a system straight out of its snapshots. Both reduce the cost of solving, storing, and predicting — at the price of a small, controllable approximation error.

Compressing snapshots with POD

Rasmah.ModelReduction.podFunction
pod(S; num_basis=nothing, energy=nothing, method=:svd, seed=0, oversampling=10, power=2) -> PODResult

Proper Orthogonal Decomposition of the snapshot matrix S (columns are snapshots, $n \times m$).

Theory

A family of fields — snapshots of a simulation, frames of a video — usually lives near a low-dimensional subspace: the columns of $S$ are nearly a linear combination of a few dominant directions. POD finds those directions as the leading left singular vectors of $S$. The retained energy $\sum_{i \le k} \sigma_i^2 / \sum_i \sigma_i^2$ measures how much of the data the $k$-mode basis explains, which is why POD is the workhorse of reduced-order modeling: project a large field onto a handful of modes and solve in $k$ dimensions instead of $n$.

Arguments

  • S: the snapshot matrix (columns are snapshots).

Keyword options

  • num_basis = nothing: fix the rank $k$; energy selects instead the smallest rank retaining that fraction of $\sum \sigma^2$.
  • method = :svd: :svd (full), :randomized (Halko–Martinsson–Tropp), or :lanczos (svds for very tall S).

Returns

A PODResult with basis (n×k orthonormal columns), singular_values, energy (retained fraction), and num_basis.

Example

julia> using Rasmah

julia> xs = range(-1, 1; length = 50);

julia> S = hcat([exp.(-((xs .- c) ./ 0.2) .^ 2) for c in range(-0.5, 0.5; length = 10)]...);

julia> pod(S; energy = 0.999).num_basis
7

See also: dmd, pod_error.

source
xs = range(-1, 1; length = 50)
S = hcat([exp.(-((xs .- c) ./ 0.2) .^ 2) for c in range(-0.5, 0.5; length = 10)]...)

r = pod(S; energy = 0.999)
(r.num_basis, round(r.energy; digits = 4))
(7, 0.9994)

A family of ten 50-dimensional snapshots collapses to 7 modes while retaining 99.9% of the energy — a seven-fold reduction. Projecting onto fewer modes and measuring the leftover is one line:

Φ = pod_basis(S; num_basis = r.num_basis)
round(pod_error(S, Φ); digits = 4)
0.0248

Reading dynamics with DMD

Rasmah.ModelReduction.dmdFunction
dmd(X, Y; rank=nothing) -> DMDResult

Exact dynamic mode decomposition of the snapshot pair (X, Y) (Y is X shifted one step). Returns Ritz eigenvalues plus exact DMD modes $\Phi = Y V \Sigma^{-1} W$ of the rank-rank (default full) subspace.

Theory

DMD assumes the snapshots evolve under an unknown linear operator, $y_k = A x_k$, and finds the best low-rank $A$ from the data. The eigenvalues of the reduced operator $\tilde A = U^T Y V \Sigma^{-1}$ are the growth/decay rates and frequencies of the dynamics, and the DMD modes are the associated spatial structures — so a pair of shifted snapshots is enough to read off the spectrum of the system that produced them.

Arguments

  • X: snapshots $[x_1 \ \cdots \ x_m]$.
  • Y: the same snapshots shifted one step $[x_2 \ \cdots \ x_{m+1}]$.

Keyword options

  • rank = nothing: truncate to a rank-dimensional subspace (default: the numerical rank of X).

Returns

A DMDResult with modes, eigenvalues, singular_values, amplitudes, and rank.

Example

julia> using Rasmah

julia> A = [0.0 -1.0; 1.0 0.0];

julia> X = [1.0 0.0 -1.0; 0.0 1.0 0.0];

julia> round.(dmd(X, A * X; rank = 2).eigenvalues; digits = 4) == ComplexF64[0.0 - 1.0im, 0.0 + 1.0im]
true

See also: pod, dmd_predict.

source
A = [0.0 -1.0; 1.0 0.0]           # a rotation: eigenvalues ±i
X = [1.0 0.0 -1.0; 0.0 1.0 0.0]   # three snapshots of the orbit

d = dmd(X, A * X; rank = 2)
round.(d.eigenvalues; digits = 4)
2-element Vector{ComplexF64}:
 0.0 - 1.0im
 0.0 + 1.0im

The eigenvalues land exactly on $\pm i$ — DMD recovered the rotation frequency of the system from three snapshots alone, without ever seeing the matrix A. That is the whole promise of data-driven model reduction: the snapshots are enough.

Non-intrusive surrogates

When you cannot project (a black-box code, an experiment), fit a surrogate instead — a function that reproduces the input-output map from samples:

Rasmah.ModelReduction.radial_basis_interpolationFunction
radial_basis_interpolation(X, Y; kernel=:gaussian, shape=1.0) -> RBFInterpolant

Fit s(x) = Σⱼ wⱼ φ(‖x − xⱼ‖) to the training data X (d×m, parameters as columns) and Y (q×m, outputs as columns), solving W Φ = Y for the interpolation matrix Φᵢⱼ = φ(‖xᵢ − xⱼ‖).

source
X = [0.0 0.5 1.0 1.5 2.0]
Y = [0.0 0.25 1.0 2.25 4.0]      # y = x^2

rbf = radial_basis_interpolation(X, Y; kernel = :thin_plate)
round.(rbf([1.25]); digits = 4)
1-element Vector{Float64}:
 1.5783

The radial-basis interpolant reproduces the samples exactly and interpolates smoothly between them, so it can stand in for $x^2$ where no closed form exists.

Next steps

Reduced models still need a gradient and an optimizer to be useful — see Optimization — and a neural network is one of the most flexible surrogates of all, covered in Neural networks.