All projects

  • cfd

Turbulence Closures — From Mixing Length to Data-Driven RANS

A from-scratch, chronologically-built turbulence-closure curriculum in Python — classical RANS through LES, hybrid RANS-LES, and a first data-driven correction, every claim backed by a mechanically-enforced disclosure registry

27 images · 7 videos · 7 min read

Turbulence Closures — From Mixing Length to Data-Driven RANS

datasheet // turbulence-closures

images
27
videos
7
formats
png, mp4
text
1503 words · 7 min read
source
previous site
topics
  • les ×3
  • hybrid rans les ×1
  • validation ×2
  • data driven ×1
  • budget analysis ×2
  • vortex identification ×2
  • spectral analysis ×1
  • statistics ×5
  • anisotropy ×1
  • invented technique ×8
  • infrastructure ×2
  • bluff body ×6

Problem

Turbulence closure is the central open problem in practical CFD: DNS resolves turbulence exactly but is computationally infeasible at engineering scale, so every practical solver leans on a modeled closure — and a closure’s accuracy, applicability range, and failure modes have to be understood on purpose, not assumed because a commercial tool ships it as a menu option. Most CFD users pick a turbulence model as a black-box setting; this project instead builds the field’s entire lineage from scratch, in a testable Python sandbox with an honesty discipline enforced by code, not just convention — feeding directly into PANSOPHIA’s own from-scratch turbulence module.

Approach

A chronological curriculum, one pluggable closure contract (ClosureModel, a single method mu_t_field, generalized to also host Reynolds-stress-transport models with no scalar eddy viscosity at all): Prandtl’s (1925) mixing length through one-equation (Spalart-Allmaras) and two-equation (k-ε, k-ω/SST) RANS models, Reynolds-stress transport (SSG, 1991), a nonlinear eddy-viscosity model (Shih-Zhu-Lumley, 1993), v2f (Durbin, 1991/1995) — 17 closures built and registered, covering the field from 1925 to 1997 in full. Then genuinely past classical RANS: a real unsteady 2D incompressible Navier-Stokes solver (vorticity-streamfunction, pseudo-spectral, validated against the closed-form Taylor-Green vortex and an inviscid energy/enstrophy conservation check) hosting LES sub-grid closures (Smagorinsky, dynamic Smagorinsky, WALE), a hybrid RANS-LES method (DES97, Spalart et al. 1997, built as a direct modification of Spalart-Allmaras’s own wall-distance length scale), and a first data-driven closure correction — a closed-form curve fit (not a trained model) against DNS data, with a genuine train/test split.

Every closure is checked against a BenchmarkEntry registry that mechanically requires a disclosed caveat for anything short of a clean pass (the constructor itself refuses to build an entry with a "finding" or "miss" outcome and no caveat) — a validation dossier with no misses in it is not a validation dossier.

Results

LES on a genuine 2D solver (Fig. 1–3): a Smagorinsky sub-grid closure cuts the coarse-grid spectral energy-pileup error by 3.3x against a DNS reference tuned to hit the theoretical $E(k)\sim k^{-3}$ enstrophy-cascade slope almost exactly (measured slope −3.033); the accompanying video shows the classic 2D-turbulence signature of same-signed vortices merging over time while enstrophy cascades to small scales and dissipates.

Hybrid RANS-LES (Fig. 4): DES97 matches plain Spalart-Allmaras’s own wall shear stress to ~9 significant figures exactly where its RANS branch is supposed to stay active — and, just as importantly, the outer-region limitation of running a 1D closure with no real LES grid behind it is measured and disclosed (a ~92x eddy-viscosity collapse), not hidden.

Reynolds-stress transport vs. DNS (Fig. 5): SSG’s mean-velocity profile misses Lee-Moser channel DNS by 93–96% RMS — a genuine, large, disclosed miss, reported at the same standard as every passing result, because a closure that only shows its wins isn’t validated at all.

A first data-driven closure (Fig. 6): a $C_\mu(y^+)$ correction curve-fit against $\mathrm{Re}_\tau$=180 DNS, evaluated — never re-fit — against held-out $\mathrm{Re}_\tau$=1000: a genuine 52% RMS generalization miss, with the root cause ($\mathrm{Re}_\tau$=180’s own short log-law window biasing the fitted asymptote) investigated and recorded, not just measured and dropped.

Turbulent-kinetic-energy budget, order-of-magnitude analysis (Fig. 7): the production/ dissipation/transport balance directly from Lee-Moser DNS, the physical structure every RANS closure in this study approximates — production peaks sharply in the buffer layer, dissipation dominates all the way to the wall, and pressure-strain integrates to exactly zero for the scalar TKE budget by construction (it only redistributes energy between Reynolds-stress components, which is exactly why the Reynolds-stress-transport model in Fig. 5 has to model it explicitly).

A general-purpose turbulence visualization/video toolkit, built afterward and exercised against this same study’s own data: Okubo-Weiss coherent-structure segmentation, Q-criterion, line integral convolution, a real (not model-spectrum) energy spectrum and spectral energy flux showing 2D turbulence’s dual inverse-energy/forward-enstrophy cascade directly, structure functions, velocity-gradient-invariant statistics, and a closure scorecard rendered straight from the benchmark registry, quadrant analysis, and a two-point spatial autocorrelation with its own integral length scale. Six invented visualization techniques get their own video/figure treatment — an “Instrument Panel” dashboard synchronizing a field, its live spectrum, and an energy/enstrophy time history under one moving cursor; a “Scale Telescope” that sweeps a band-pass window through wavenumber space instead of through time; a “Closure Divergence Theatre” watching two SGS models diverge from an identical initial condition; a “Backscatter Flash Map” reconstructing the spatial structure behind up-scale energy transfer at every frame; an “Anisotropy Painting” mapping Reynolds-stress state to a color ribbon; and a “Budget Residual Field” showing a closure’s own local self-consistency node by node. A verification pass — actually looking at every rendered frame, not just checking for a clean exit code, across two build passes — caught and fixed eight real bugs this way, including a sign error in the spectral-flux computation that an energy-conservation check alone structurally could not detect. One figure reports a genuine negative result, kept in rather than swapped for a cleaner case: the Budget Residual Field technique has a real, disclosed limitation applying it to a relaxed-Picard RANS solve (full account in the project’s own lab notebook).

A ParaView-class data pipeline and a telemetry subsystem (Fig. 12–15 + video), built after a direct measurement found the study persisted zero numbers anywhere — every result existed only as a formatted string, recomputed from scratch on every import. RunLogger now instruments the solvers (additive, opt-in, verified bit-identical to the pre-instrumentation code when unused — 18 checked configurations, 0 mismatches) and logs both individual per-node values and collective statistics (min/max/rms/percentiles, an exponent histogram, dynamic-range-in-decades) to Parquet/HDF5, ML-training-ready with a self-describing schema. A new FieldDataset/TimeSeries data model plus calculator/gradient/divergence/curl/slice/clip/threshold/contour/ glyph/stream_tracer/temporal-statistics filters, and VTK-XML writers (.vti/.vtr/.vtu/ .pvd, plus XDMF+HDF5 for long series) give this study’s data a genuine source-to-filter-to-view pipeline for the first time; Animation turns a TimeSeries into a movie in three lines instead of a bespoke FuncAnimation script per video. Three more invented visualization techniques ride directly on the new telemetry: a Magnitude Cascade Ledger (every term of a real Picard iteration’s own balance on one shared log-magnitude axis, dominance switches highlighted), a Vanishing Horizon (a residual’s own decay against two genuinely distinct thresholds — float64 denormal extinction versus mere irrelevance relative to the equation’s own dominant scale), and a Decade-Band Field Map (a field rendered in discrete power-of-ten bands) — the last of which produced a real caught-before-trust finding: a first-draft caption assumed “vortex cores several decades above background,” which did not survive actually looking at the rendered image (the dominant visible structure is the field’s own vorticity zero-crossing topology instead), fixed to describe what the figure actually shows.

Validation

17/17 cataloged closures built and registered; 56 BenchmarkEntrys (covering the closures above plus previously-uncovered infrastructure — grid/linear-algebra primitives, near-wall treatment, boundary-layer integral quantities — closed in an earlier session), every one now carrying a machine-readable results_json record (closing a real, measured gap: this field existed since the study’s first session but was populated on 0 of 56 entries until this one), zero silently-hidden misses — the registry’s own constructor forbids that. Every model is checked against a closed-form solution, DNS (Lee & Moser channel data), or a cross-check reference — never against a hand-picked “looks right” snapshot. The visualization toolkit’s own derived- quantity functions carry their own closed-form/conservation-law test suite (20 tests, including a new directional-anisotropy check that closes a previously-disclosed gap), independent of the closure registry above; the new pipeline/telemetry infrastructure carries 40 more tests of its own (writer round-trips, XML well-formedness, a sphere isosurface area converging to $4\pi r^2$, and the rendered-figure-level checks behind the three new techniques above).

My contribution

Designed and built the entire closure contract, solver stack, and the mechanically-enforced disclosure registry from scratch; ported the classical closures and independently re-derived the LES/hybrid/data-driven methods (Smagorinsky and WALE cross-checked character-for-character against PANSOPHIA’s own production Julia implementation; dynamic Smagorinsky re-derived from the Germano-Lilly identity). Development used Claude.ai as a coding aid throughout; final design and validation decisions are Krunal’s.

Where this is going

Migrating file-by-file into PANSOPHIA’s own python/cfdpy/turbulence module (already mapped). DDES/IDDES remain queued behind a genuinely resolved, wall-bounded, multi-directional solver this study doesn’t have yet — a real prerequisite, not a “coming soon” placeholder (the same missing 2D-RANS solver is the study’s own last uncovered benchmark target). A data-driven closure that actually generalizes across Reynolds number is a concrete, specific next step (more DNS stations, a Reynolds-normalized fit parameterization), motivated directly by this project’s own disclosed generalization miss, not a vague aspiration. On the visualization side, all nine invented techniques (the original six plus three new ones riding on the telemetry subsystem) are now built and demonstrated; velocity-increment PDFs and directional two-point-correlation anisotropy were both closed in later sessions, no longer open gaps. A genuine 3D Q-criterion/λ2 would still need a 3D velocity field this study’s solvers don’t produce — the one visualization gap that stays genuinely open, disclosed rather than silently dropped. The opt-in benchmark-results cache (content-hash-keyed, built to make the ~11-minute registry import tractable for iterative work) is infrastructure only so far — wiring it into all 27 _check_* functions in cases.py is a separate, disclosed follow-up, not yet done.

LES energy-spectrum validation, freely-decaying 2D turbulenceRadially-averaged kinetic-energy spectrum $E(k)$ for freely-decaying 2D turbulence (McWilliams 1984 initial condition, doubly-periodic domain, $\nu$=0.0005) at $t=1.0$. The fine-resolution DNS reference (N=256) develops the theoretical $E(k)\sim k^{-3}$ enstrophy-cascade slope (Kraichnan/Batchelor) almost exactly (measured slope -3.033). At a 8x coarser resolution (N=32) representative of a practical LES grid, the classic 2D energy-pile-up failure mode appears when no sub-grid-scale model is used; adding a Smagorinsky SGS closure ($C_s$=0.17, Lilly's isotropic-turbulence value) reduces the absolute resolved-scale energy error against the DNS reference by a factor of 3.3x. Single realization, one Reynolds-number-like setting — a genuine, measured demonstration that the SGS closure does useful work in this minimal case, not a general accuracy claim across turbulent regimes. Explain Turbulence
2D vorticity field: initial condition, unresolved LES, SGS-closed LESVorticity field snapshots for the same freely-decaying 2D turbulence case. Left: the McWilliams (1984) random-phase initial condition. Center: after evolving to $t=1.0$ on an under-resolved 32x32 grid with no sub-grid-scale closure — small-scale vortical structure is visibly coarser/noisier than the physical cascade would produce. Right: the same initial condition and grid, now with a Smagorinsky SGS closure — the large coherent vortices are preserved while excess small-scale noise is damped, consistent with the energy-spectrum improvement quantified alongside this figure. Explain Turbulence
Turbulence video: freely-decaying 2D vorticity field evolutionTime evolution of the vorticity field for freely-decaying 2D turbulence under a Smagorinsky sub-grid-scale closure ($C_s$=0.17), N=96, $\nu$=0.0003, from $t=0$ to $t=3$. The classic 2D turbulence signature is visible: same-signed vortices merge over time (the inverse energy cascade toward larger scales), while enstrophy cascades to small scales and is dissipated there, consistent with the $E(k)\sim k^{-3}$ enstrophy-cascade spectrum measured in the companion energy-spectrum figure. Explain Turbulence
DES97 vs. Spalart-Allmaras: near-wall validation and a disclosed limitationMean velocity ($U^+$ vs. $y^+$) and eddy-viscosity ($\mu_t$ vs. $y^+$) profiles for plain Spalart-Allmaras (solid) vs. DES97 (dashed), Re$_\tau$=180 forcing. Near the wall, where DES97's RANS branch stays active in both closures, wall shear stress matches to ~9 significant figures — the length-scale hook is a correct no-op exactly where DES is supposed to behave like RANS. Away from the wall, DES97's length-scale switch mechanically engages on this 1D grid (a structural property of any 1D domain-spanning grid, not a tuning failure) — eddy viscosity collapses ~92x relative to plain SA with no genuine resolved/unsteady turbulence to replace it. This 1D setting cannot and does not demonstrate DES's actual purpose (protecting an attached boundary layer while resolving separated/unsteady turbulence away from the wall) — disclosed here rather than overclaimed. Explain Turbulence
Reynolds-stress transport (SSG) vs. DNS: a genuine, disclosed missMean-velocity profile for the SSG Reynolds-stress-transport closure (Speziale, Sarkar & Gatski, 1991 — solved via a direct 6-equation Reynolds-stress transport path, no scalar eddy viscosity at all) against Lee & Moser (2015) channel DNS at Re$_\tau$=180 and 1000. RMS relative error is 93%/96% respectively — a genuine, large, disclosed miss, reported honestly rather than hidden, even though this closure passes its own force-balance regression comfortably: force balance is a necessary, shape-independent check, not a sufficient accuracy claim. Candidate root causes are recorded but not individually isolated — left as genuinely open, not claimed as diagnosed. Explain Turbulence
Data-driven closure correction: a genuine train/test generalization resultLocal-equilibrium $C_\mu(y^+)$ implied by inverting the Boussinesq hypothesis against DNS, compared to a 3-parameter closed-form curve fit (scipy curve_fit, NOT a general trained model) — a genuine train/test split: fit parameters come ONLY from Re$_\tau$=180, then the SAME fitted curve is evaluated, never re-fit, against Re$_\tau$=1000's own independently-constructed DNS-implied profile (a genuinely different DNS simulation). Result: a real, disclosed generalization miss — 52% RMS relative error on the held-out station — even though the training fit itself tracks the Re$_\tau$=180 data closely. The root cause (Re$_\tau$=180's own short log-law window biasing the fitted asymptote) is investigated and recorded, exactly the overfitting risk flagged in advance before this closure was built. Explain Turbulence
Turbulent-kinetic-energy budget: order-of-magnitude term-by-term analysisTurbulent-kinetic-energy budget (Lee & Moser 2015 DNS, wall units) across the channel half-width at Re$_\tau$=180 and 1000, the standard textbook TKE-budget presentation (cf. Pope, Turbulent Flows, Fig. 7.24). Production and dissipation dominate and nearly balance through most of the log layer, with production sharply peaked in the buffer layer ($y^+\approx$11-15) while dissipation remains large all the way to the wall, where viscous transport takes over. This is exactly the order-of-magnitude structure a RANS closure's production/dissipation transport equations are built to capture directly. Pressure-strain is identically zero here by construction (it redistributes energy between Reynolds-stress components without changing their sum, exactly why the Reynolds-stress-transport model above must model it explicitly even though it drops out of the scalar TKE equation). Explain Turbulence
Vorticity field and Okubo-Weiss coherent-structure segmentationLeft: instantaneous vorticity field from a McWilliams (1984)-initialized, Smagorinsky-LES-closed 2D decaying-turbulence run (N=96, t=4.0s), re-solved via `turbpy.solvers.ns2d.solve_ns2d_les`. Right: the SAME instant segmented by the Okubo (1970)/Weiss (1991) parameter OW=S^2-omega^2 into vortex-dominated (blue), strain-dominated (red), and ambiguous (white) regions — addressing vorticity pseudocolor's own blind spot (a strong non-rotating strain region is invisible in the left panel). Explain Turbulence
Q-criterion vortex identification and line integral convolutionLeft: Q-criterion (positive values only) on the same decaying-turbulence snapshot — in 2D incompressible flow this is exactly -0.5×Okubo-Weiss (verified by hand), so it carries no new information over the companion segmentation figure, included to show the more widely recognized CFD name for the same quantity. Right: Line Integral Convolution (Cabral & Leedom, 1993) of the velocity field, alpha-blended over vorticity — a dense, direction-revealing texture with no arbitrary streamline seed points. Explain Turbulence
Premultiplied energy spectrum and spectral energy fluxLeft: k·E(k) — equal areas under this curve correspond to equal energy in a decade of wavenumber, so the peak marks the energy-containing scale directly. Right: the resolved-scale spectral flux Π(k) (Frisch, 1995), computed from the SAME instantaneous field — negative Π at low k (blue) is the signature of 2D turbulence's inverse energy cascade (Kraichnan, 1967); positive Π at high k (red) is the companion forward enstrophy cascade. Neither cascade direction is visible from the spectrum's slope alone. (A sign-convention bug in the underlying flux computation was found and fixed by measuring a real solver's own energy transfer directly, not just trusting an energy-conservation identity that cannot distinguish a flux from its negation — see notes.md.) Explain Turbulence
Second-order velocity structure functionS₂(r)=⟨|u(x+r)-u(x)|²⟩ computed directly from the decaying-turbulence velocity field along one axis, log-log with a Kolmogorov (1941) r^(2/3) reference slope overlaid — departure from this slope (visible curvature) is the classical intermittency signature She & Lévêque (1994) correct for in higher-order structure functions, not attempted here (a single decaying 2D snapshot at this resolution is too short a statistical sample for a reliable intermittency exponent measurement, disclosed rather than a forced claim). Explain Turbulence
Joint distribution of velocity-gradient invariants (Q, R)The classical "teardrop" joint PDF (Chong, Perry & Cantwell, 1990) pooling every grid point's local flow topology into one statistical picture — a genuinely different perspective from any single-instant spatial field plot, at the cost of discarding all spatial (WHERE) information. Explain Turbulence
Closure scorecard: RMS log-law error across model and Reynolds numberEvery cell re-solved directly by this script via the same DNS-comparison function this study's own registered benchmark entries call — never hand-copied from those entries' text. A closure scorecard turns a table a reader must scan row-by-row into a pattern a reader sees at a glance (e.g. an entire model family struggling at one Reynolds number shows as a column-wide bright stripe) — it does not explain WHY any one cell is high; that still requires the specific benchmark entry's own disclosed caveat. Color scale capped at 100% relative error (with an "over" marker) so one 142% outlier does not squash every other, more informative, cell into one narrow color band. Explain Turbulence
Anisotropy Painting: barycentric-colored Reynolds-stress ribbon (invented technique)Each wall-normal point's Lumley-triangle position (ξ, η), computed from a converged SSG Reynolds-stress-transport channel_half solve (Re$_\tau$=180 forcing), is mapped to an RGB color via barycentric coordinates (Banerjee et al., 2007 color convention, reused not reinvented) and painted as a single ribbon next to the classical triangle scatter — so the turbulence state (one-component / two-component / isotropic) reads as a color at a glance instead of a position in a triangle the reader must decode. The true-wall row is excluded: it is a numerical placeholder in the closure's own state (three Reynolds-stress components floored to the same value there), which maps to the isotropic corner by the anisotropy tensor's own algebra regardless of the floor's magnitude — a spurious "isotropic wall" artifact, not the highly anisotropic state real near-wall turbulence physically has, found by checking this exact figure against that expectation before trusting it. Explain Turbulence
Instrument Panel: synchronized field/spectrum/time-series dashboard (invented technique)One video, three synchronized panels: the vorticity field, its own live energy spectrum (re-fit every frame, not interpolated), and a normalized energy/enstrophy time history swept by a moving cursor — so a viewer watches the decay dynamics and its own spectral signature evolve together in real time, rather than mentally aligning a separately-rendered field video against a separately-rendered spectrum plot. Built on McWilliams (1984)-initialized, Smagorinsky-LES-closed decaying 2D turbulence (N=80, t_end=2.5s). Energy decays visibly slower than enstrophy, consistent with 2D turbulence's own inverse energy / forward enstrophy dual-cascade phenomenology. Explain Turbulence
Scale Telescope: sweeping a band-pass window through wavenumber space (invented technique)The SAME instantaneous vorticity field, band-pass filtered through 12 contiguous wavenumber windows swept from k=0 up to the wavenumber holding 99.9% of the field's own energy — rather than a time sweep, the sweep AXIS here is scale itself: watch the field's structure dissolve from broad, large-eddy shapes at low k to fine, thread-like filaments at high k, with a marker tracking the current window's position on the field's own energy spectrum. A genuine novelty search (disclosed in the code) found scale-decomposition/wavelet field plots and multi-panel scale comparisons, but no video specifically sweeping a moving band-pass window as its own time axis. Explain Turbulence
Closure Divergence Theatre: two SGS models, identical initial condition, watched diverging (invented technique)Fixed-coefficient Smagorinsky and the Germano-Lilly (1991/1992) dynamic-coefficient Smagorinsky model, started from the IDENTICAL McWilliams (1984) initial condition, evolved side by side; the third panel is their signed difference field, and the bottom strip is the running L2 model-divergence norm swept by a cursor. Turns "these two closures disagree" from a single end-state number into something a viewer watches accumulate over time. Explain Turbulence
Instrument Panel — static filmstrip (6 frames)A static 6-frame filmstrip companion to the Instrument Panel video, for reviewers who cannot play the MP4 directly (this portfolio's own contact-sheet tooling shows only a video's first frame as a poster). Explain Turbulence
Two-point spatial autocorrelation and integral length scaleR(r) computed from the decaying-2D-turbulence snapshot's own u velocity component along one axis, via the Wiener-Khinchin theorem. Integral length scale L=0.588 m (truncated at R(r)'s first zero-crossing, the standard practical cutoff on a finite/periodic domain) — the field's own characteristic large-eddy size, directly comparable to the k0=6 forcing wavenumber of its McWilliams (1984) initial condition (L_forcing=2π/6=1.047 m). Explain Turbulence
Quadrant decomposition of the resolved shear-stress correlationQuadrant analysis (Wallace, Eckelmann & Brodkey, 1972) applied to the same decaying-2D-turbulence snapshot's spatial velocity fluctuations — NOT a wall-bounded shear flow, so the classical "ejection"/"sweep" names are not literally applicable here (disclosed in the code); the underlying quadrant decomposition of -⟨u′v′⟩ is still meaningful. Individual quadrant fractions exceeding 100%/going negative is itself a genuine result: this unforced-shear field has a small net -⟨u′v′⟩, so normalizing by that small denominator amplifies the four quadrants' own comparably-sized raw contributions — exactly the contrast the classical near-wall picture (sustained mean shear keeps the net correlation large) would not show. Explain Turbulence
Budget Residual Field: a genuine limitation found applying the technique to a relaxed RANS solveThe local imbalance of Realizable k-ε's own EXACT discrete k-equation (face-averaged-diffusivity finite-volume form, reproduced verbatim from the closure's own code), evaluated at every node post-solve. Contrary to a first-guess expectation ("near-zero at interior nodes, large only at wall-function Dirichlet rows"), the MEASURED residual is large at every node — traced to this closure's own under-relaxed nested-Picard design, which intentionally damps oscillation at the cost of the returned k/ε ever exactly satisfying the discrete equation with the final eddy viscosity at any single instant. A genuine, disclosed LIMITATION of this diagnostic technique for relaxed-iterative RANS solves, kept in as the honest result rather than replaced with a cleaner cherry-picked case. Explain Turbulence
Backscatter Flash Map: spatial structure of upscale energy transfer (invented technique)Left: the full vorticity field evolving under Smagorinsky LES. Right: the spatial structure reconstructed from ONLY the wavenumber shells where the resolved-scale spectral flux Π(k) is currently negative — i.e. energy moving up-scale at that instant, "backscatter" in the LES sense (Piomelli et al., 1991). Deliberately NOT the same as a local eddy-viscosity SGS model's own dissipation field (non-negative by construction, can never itself display backscatter) — this is the RESOLVED-scale flux's own negative-transfer content, made spatially visible instead of left as an abstract k-space curve. Novelty search: found spectral-flux plots and backscatter discussion in the LES literature, but no video reconstructing and displaying the specific spatial structure responsible for backscatter at each instant. Explain Turbulence
Velocity-increment PDFs at several separationsδu(r)=u(x+r)-u(x) histogrammed at r=1,4,16 grid points (normalized by each separation's own standard deviation), log-scale y-axis, against a Gaussian reference. HONEST RESULT: all three separations track the Gaussian closely, with no clear widening of the tails at small r — the classical 3D-turbulence intermittency signature is NOT detected here. This is not claimed as a general property of turbulence; it reflects this specific case's real limitations (a single decaying 2D snapshot, heavily damped by the Smagorinsky SGS closure, too small a statistical sample for a reliable tail measurement), not a claim that turbulence lacks intermittency. Explain Turbulence
Magnitude Cascade Ledger: order-of-magnitude analysis of a real Picard iteration (invented technique)The `tau_wall`/`change` (residual) terms logged by the new `RunLogger` telemetry API at every Picard iteration of a real Realizable k-epsilon channel solve (N=61, wall-clustered), rendered as a Magnitude Cascade Ledger — every logged term overlaid on one shared log10-magnitude axis, with the currently-dominant term's own line segment drawn thick so dominance switches are visible at a glance rather than requiring cross-reading separate plots. `tau_wall` settles to its converged value within a handful of iterations while `change` continues decaying for many more — a genuine, measured separation between how fast an aggregate force balance settles versus how fast the full iterative residual does. Novelty search: found log-magnitude plots of individual quantities and unrelated "waterfall" charts for cumulative sums, but no prior art overlaying an equation's own full term set with dominance highlighted as it changes over a run. Explain Turbulence
Vanishing Horizon: a real Picard residual's own decay against two distinct vanishing floors (invented technique)The SAME Realizable k-epsilon channel run as the Magnitude Cascade Ledger figure. `change` (the Picard residual) decays roughly linearly in log-space from iteration ~50 onward — a clean geometric convergence rate, visible directly from the plotted slope — crossing the relative-irrelevance floor (dashed: the residual's own scale relative to the equation's dominant term) well before float64's absolute denormal floor (dash-dot) becomes relevant at all. The real point: a residual can stay numerically alive by a huge margin while already being physically irrelevant to the equation's own dominant scale — two genuinely distinct notions of "vanishing" this technique makes visually distinguishable for the first time in this study. Novelty search: found log-decay plots and numerical-underflow discussion treated separately, but no prior art drawing both thresholds on the same axis at once. Explain Turbulence
Decade-Band Field Map: a real decaying-turbulence snapshot in discrete power-of-ten bands (invented technique)The same McWilliams (1984)-initialized decaying 2D turbulence field this study's other figures use (N=96, t=3.0s, ν=1e-3), rendered in discrete power-of-ten bands instead of a continuous colormap. A genuine, checked-against-expectation finding: the dominant structure is NOT "vortex cores several decades above background" (a first-draft caption assumption that did not survive actually looking at the rendered figure) — at this modest diffusion time the field has not developed that much dynamic range, so most of the domain sits at decade 0. What the discrete banding DOES make legible, that a continuous colormap does not, is the field's own thin filamentary zero-crossing topology: everywhere vorticity changes sign, |ω| passes through very small values, producing the dark low-decade threads visible here. Novelty search: found symmetric-log color scaling used routinely for large-dynamic-range fields, and discrete choropleth banding used in other domains (e.g. earthquake-magnitude maps), but no prior art applying discrete decade banding to a CFD scalar field's own spatial structure as its own named technique. Explain Turbulence
ParaView-class pipeline: contour and gradient filters on a real 2D turbulence fieldThe new `cfdpy.viz.filters` module's `contour` (left, black lines: the ω=0 isocontour via marching squares) and `gradient` (right, central differences against the field's own node coordinates) filters, run against a real decaying-2D-turbulence snapshot wrapped in the new `FieldDataset`/`TimeSeries` data model. The same run was also written out through the new `writers.write_time_series` as a genuine ParaView-openable `.vti`/`.pvd` time series — written and read back as part of generating this figure, not merely claimed to work — the source-to-filter-to-view pipeline this study's data previously had no path into at all (before this session, VTK output existed with no filter/animation layer above it). Explain Turbulence
Pipeline.Animation: a movie declared, not hand-scriptedA decaying-2D-turbulence run (N=72, 51 frames) rendered entirely through the new `pipeline.Animation` class — `Animation(Pipeline(time_series), field_name='omega').render(path)`, three lines, versus every earlier video in this study's own portfolio (from the freely-decaying vorticity-evolution video through the Backscatter Flash Map), each a bespoke hand-written animation script. A fixed color scale is applied automatically (not optional) across every frame, per this toolkit's own long-standing rule that per-frame autoscaling is "the single most common way a CFD animation misrepresents its own dynamics.\" Explain Turbulence
Bluff-body cylinder: steady drag coefficient versus time at Re = 20 and 40 against literature values Explain Turbulence
Re = 100 unsteady wake: drag and lift coefficient time series (initial spike annotated) Explain Turbulence
Drag coefficient versus resolution D/dx; the finest point diverged and is shown as a diagnostic Explain Turbulence
Karman vortex street: vorticity field at Re = 100, t = 80 Explain Turbulence
Domain blockage effect on drag coefficient: 10% versus 5% blockage Explain Turbulence
Bluff-body wake: vorticity animation Explain Turbulence

How to recreate this plot

Watch

A short screen capture of the Math Visualization Studio. Each step is captioned in the clip.

Steps

  1. Open the controls panel
  2. Pick example 4: A Rankine vortex
  3. Press Play
  4. Raise the rate a to 2.5
  5. Press Play again

Code and PDF notes

Free of cost. I approve each request and reply by email.

To request the files, email me at and mention the item id. project-turbulence-closures