pith. machine review for the scientific record. sign in

IndisputableMonolith.ClassicalBridge.Fluids.ContinuumLimit2D

IndisputableMonolith/ClassicalBridge/Fluids/ContinuumLimit2D.lean · 1637 lines · 61 declarations

show as:
view math explainer →

open module explainer GitHub source

Explainer status: ready · generated 2026-05-13 05:56:35.478813+00:00

   1import Mathlib
   2import IndisputableMonolith.ClassicalBridge.Fluids.Galerkin2D
   3import IndisputableMonolith.ClassicalBridge.Fluids.CPM2D
   4
   5namespace IndisputableMonolith.ClassicalBridge.Fluids
   6
   7open Real
   8open Filter
   9open Topology
  10open scoped InnerProductSpace
  11
  12/-!
  13# ContinuumLimit2D (Milestone M5)
  14
  15This file defines a *Lean-checkable pipeline shape* for passing from a family of finite-dimensional
  162D Galerkin approximations to a “continuum” limit object.
  17
  18At this milestone we stay honest about what is and is not formalized:
  19- we define the relevant objects (an infinite Fourier coefficient state),
  20- we define the canonical embedding of truncated coefficients into the full Fourier state, and
  21- we package the analytic compactness/identification steps as explicit hypotheses (no `axiom`, no `sorry`).
  22
  23The point is to make the dependency graph explicit so that later milestones can progressively
  24replace hypotheses with proofs.
  25-/
  26
  27namespace ContinuumLimit2D
  28
  29/-!
  30## Continuum Fourier state on 𝕋²
  31
  32We model a 2D torus velocity field via its Fourier coefficients:
  33for each `k : Mode2 = ℤ×ℤ`, a coefficient `VelCoeff = EuclideanSpace ℝ (Fin 2)`.
  34-/
  35
  36/-- Full (infinite) Fourier coefficient state for a 2D velocity field on 𝕋². -/
  37abbrev FourierState2D : Type := Mode2 → VelCoeff
  38
  39/-!
  40## Embedding: GalerkinState N → FourierState2D
  41
  42We extend a truncated state by zero outside the truncation window.
  43-/
  44
  45/-- Read a single component coefficient at mode `k` (zero if `k ∉ modes N`). -/
  46noncomputable def coeffAt {N : ℕ} (u : GalerkinState N) (k : Mode2) (j : Fin 2) : ℝ :=
  47  if hk : k ∈ modes N then
  48    -- `k` as an element of the finite index type `(modes N)`
  49    let k' : (modes N) := ⟨k, hk⟩
  50    u (k', j)
  51  else
  52    0
  53
  54/-- Extend a truncated Galerkin state by zero to a full Fourier coefficient state. -/
  55noncomputable def extendByZero {N : ℕ} (u : GalerkinState N) : FourierState2D :=
  56  fun k =>
  57    -- Build a 2-vector coefficient from its two components.
  58    !₂[coeffAt u k ⟨0, by decide⟩, coeffAt u k ⟨1, by decide⟩]
  59
  60/-!
  61## Linearity of the zero-extension embedding
  62
  63We will eventually want to pass (linear) identities from Galerkin trajectories to limits.
  64For that, it is useful to record that `extendByZero` is a linear map.
  65-/
  66
  67lemma coeffAt_add {N : ℕ} (u v : GalerkinState N) (k : Mode2) (j : Fin 2) :
  68    coeffAt (u + v) k j = coeffAt u k j + coeffAt v k j := by
  69  classical
  70  by_cases hk : k ∈ modes N
  71  · simp [coeffAt, hk]
  72  · simp [coeffAt, hk]
  73
  74lemma coeffAt_smul {N : ℕ} (c : ℝ) (u : GalerkinState N) (k : Mode2) (j : Fin 2) :
  75    coeffAt (c • u) k j = c * coeffAt u k j := by
  76  classical
  77  by_cases hk : k ∈ modes N
  78  · simp [coeffAt, hk]
  79  · simp [coeffAt, hk]
  80
  81lemma extendByZero_add {N : ℕ} (u v : GalerkinState N) :
  82    extendByZero (u + v) = extendByZero u + extendByZero v := by
  83  classical
  84  funext k
  85  ext j
  86  fin_cases j <;> simp [extendByZero, coeffAt_add]
  87
  88lemma extendByZero_smul {N : ℕ} (c : ℝ) (u : GalerkinState N) :
  89    extendByZero (c • u) = c • (extendByZero u) := by
  90  classical
  91  funext k
  92  ext j
  93  fin_cases j <;> simp [extendByZero, coeffAt_smul]
  94
  95lemma extendByZero_neg {N : ℕ} (u : GalerkinState N) :
  96    extendByZero (-u) = -extendByZero u := by
  97  classical
  98  -- `-u = (-1) • u` and `extendByZero` is linear.
  99  simpa [neg_one_smul] using (extendByZero_smul (N := N) (-1) u)
 100
 101/-- `extendByZero` packaged as a linear map. -/
 102noncomputable def extendByZeroLinear (N : ℕ) : GalerkinState N →ₗ[ℝ] FourierState2D :=
 103  { toFun := extendByZero
 104    map_add' := extendByZero_add (N := N)
 105    map_smul' := by
 106      intro c u
 107      -- `simp` expects `c • x`; our lemma is stated in that form.
 108      simpa using (extendByZero_smul (N := N) c u) }
 109
 110/-- `extendByZero` as a *continuous* linear map.
 111
 112This is available because `GalerkinState N` is finite-dimensional, hence every linear map out of it
 113is continuous. -/
 114noncomputable def extendByZeroCLM (N : ℕ) : GalerkinState N →L[ℝ] FourierState2D :=
 115  LinearMap.toContinuousLinearMap (extendByZeroLinear N)
 116
 117/-!
 118## Divergence-free structure (Fourier side) and limit stability
 119
 120A structural property we can pass to the limit using only modewise convergence is a closed,
 121linear constraint such as “divergence-free in Fourier variables”:
 122
 123`k₁ * û₁(t,k) + k₂ * û₂(t,k) = 0` for every mode `k`.
 124-/
 125
 126/-- Real Fourier-side divergence constraint for a single mode. -/
 127noncomputable def divConstraint (k : Mode2) (v : VelCoeff) : ℝ :=
 128  (k.1 : ℝ) * v (0 : Fin 2) + (k.2 : ℝ) * v (1 : Fin 2)
 129
 130/-- Fourier-side divergence-free predicate (modewise, at a fixed time). -/
 131def IsDivergenceFree (u : FourierState2D) : Prop :=
 132  ∀ k : Mode2, divConstraint k (u k) = 0
 133
 134/-- Divergence-free predicate for a time-dependent Fourier trajectory. -/
 135def IsDivergenceFreeTraj (u : ℝ → FourierState2D) : Prop :=
 136  ∀ t : ℝ, ∀ k : Mode2, divConstraint k ((u t) k) = 0
 137
 138lemma divConstraint_continuous (k : Mode2) : Continuous fun v : VelCoeff => divConstraint k v := by
 139  have h0 : Continuous fun v : VelCoeff => v (0 : Fin 2) := by
 140    simpa using
 141      (PiLp.continuous_apply (p := (2 : ENNReal)) (β := fun _ : Fin 2 => ℝ) (0 : Fin 2))
 142  have h1 : Continuous fun v : VelCoeff => v (1 : Fin 2) := by
 143    simpa using
 144      (PiLp.continuous_apply (p := (2 : ENNReal)) (β := fun _ : Fin 2 => ℝ) (1 : Fin 2))
 145  simpa [divConstraint] using ((continuous_const.mul h0).add (continuous_const.mul h1))
 146
 147/-!
 148## Linear Stokes/heat mild form (Fourier side) and limit stability
 149
 150As a next step toward a real PDE statement, we can talk about the *linear* (viscous) dynamics.
 151On the Fourier side, the Stokes/heat semigroup acts diagonally:
 152
 153`û(t,k) = exp(-ν |k|^2 t) • û(0,k)`.
 154
 155This is still not Navier–Stokes, but it is a concrete PDE-like identity that can be passed to the
 156limit using only modewise convergence (no compactness beyond that).
 157-/
 158
 159/-- Fourier-side heat/Stokes factor `e^{-ν|k|^2 t}`. -/
 160noncomputable def heatFactor (ν : ℝ) (t : ℝ) (k : Mode2) : ℝ :=
 161  Real.exp (-ν * kSq k * t)
 162
 163/-- Mild Stokes/heat solution in Fourier coefficients (modewise, for `t ≥ 0`). -/
 164def IsStokesMildTraj (ν : ℝ) (u : ℝ → FourierState2D) : Prop :=
 165  ∀ t ≥ 0, ∀ k : Mode2, (u t) k = (heatFactor ν t k) • (u 0) k
 166
 167/-- Differential (within `t ≥ 0`) Stokes/heat equation in Fourier coefficients (modewise).
 168
 169This is a slightly more “PDE-like” statement than the mild form: for each fixed mode `k`,
 170the coefficient trajectory satisfies
 171
 172`d/dt u(t,k) = -(ν |k|^2) • u(t,k)`
 173
 174as a derivative **within** the half-line `[0,∞)`. -/
 175def IsStokesODETraj (ν : ℝ) (u : ℝ → FourierState2D) : Prop :=
 176  ∀ t ≥ 0, ∀ k : Mode2,
 177    HasDerivWithinAt (fun s : ℝ => (u s) k) (-(ν * kSq k) • (u t) k) (Set.Ici (0 : ℝ)) t
 178
 179/-!
 180## First nonlinear (Duhamel-style) identification: heat evolution + remainder
 181
 182To start introducing the nonlinear Navier–Stokes term without committing to the full analytic
 183infrastructure (time integrals, dominated convergence, etc.), we use a *Duhamel-like remainder*
 184term `D(t,k)`:
 185
 186`u(t,k) = heatFactor(ν,t,k) • u(0,k) + D(t,k)`.
 187
 188In a future milestone, `D` will be instantiated as the time-integrated nonlinear forcing.
 189For now, this already gives a useful “nonlinear-shaped” identity that can be passed to the limit
 190under modewise convergence, provided the remainders also converge modewise.
 191-/
 192
 193/-- Duhamel-style (nonlinear) remainder form: `u = heatFactor • u0 + D` (modewise, for `t ≥ 0`). -/
 194def IsNSDuhamelTraj (ν : ℝ) (D : ℝ → FourierState2D) (u : ℝ → FourierState2D) : Prop :=
 195  ∀ t ≥ 0, ∀ k : Mode2, (u t) k = (heatFactor ν t k) • (u 0) k + (D t) k
 196
 197namespace IsStokesMildTraj
 198
 199/-- Mild Stokes/heat identity implies the corresponding differential equation (within `t ≥ 0`). -/
 200theorem stokesODE {ν : ℝ} {u : ℝ → FourierState2D} (h : IsStokesMildTraj ν u) :
 201    IsStokesODETraj ν u := by
 202  intro t ht k
 203  -- Let `a = u(0,k)` so the mild formula reads `u(s,k) = exp(-ν|k|^2 s) • a` for `s ≥ 0`.
 204  let a : VelCoeff := (u 0) k
 205
 206  -- Derivative of the scalar heat factor.
 207  have hlin : HasDerivAt (fun s : ℝ => (-ν * kSq k) * s) (-ν * kSq k) t := by
 208    simpa [mul_assoc] using (hasDerivAt_id t).const_mul (-ν * kSq k)
 209  have hscalar :
 210      HasDerivAt (fun s : ℝ => heatFactor ν s k)
 211        (heatFactor ν t k * (-ν * kSq k)) t := by
 212    -- `d/ds exp(g(s)) = exp(g(s)) * g'(s)` with `g(s) = (-ν|k|^2) * s`.
 213    have hexp :
 214        HasDerivAt (fun s : ℝ => Real.exp ((-ν * kSq k) * s))
 215          (Real.exp ((-ν * kSq k) * t) * (-ν * kSq k)) t :=
 216      (Real.hasDerivAt_exp ((-ν * kSq k) * t)).comp t hlin
 217    simpa [heatFactor, mul_assoc] using hexp
 218  have hscalarW :
 219      HasDerivWithinAt (fun s : ℝ => heatFactor ν s k)
 220        (heatFactor ν t k * (-ν * kSq k)) (Set.Ici (0 : ℝ)) t :=
 221    hscalar.hasDerivWithinAt
 222
 223  -- Differentiate `s ↦ heatFactor ν s k • a` within `[0,∞)`.
 224  have hform :
 225      HasDerivWithinAt (fun s : ℝ => (heatFactor ν s k) • a)
 226        ((heatFactor ν t k * (-ν * kSq k)) • a) (Set.Ici (0 : ℝ)) t :=
 227    hscalarW.smul_const a
 228
 229  -- Replace the formula by `u` using the mild identity on the domain `[0,∞)`.
 230  have huEq : ∀ s ∈ Set.Ici (0 : ℝ), (fun s : ℝ => (u s) k) s = (fun s : ℝ => (heatFactor ν s k) • a) s := by
 231    intro s hs
 232    -- `hs : 0 ≤ s`
 233    simpa [a] using (h s hs k)
 234  have huEq_t : (fun s : ℝ => (u s) k) t = (fun s : ℝ => (heatFactor ν s k) • a) t := by
 235    simpa [a] using (h t ht k)
 236
 237  have huDeriv :
 238      HasDerivWithinAt (fun s : ℝ => (u s) k) ((heatFactor ν t k * (-ν * kSq k)) • a)
 239        (Set.Ici (0 : ℝ)) t :=
 240    hform.congr huEq huEq_t
 241
 242  -- Simplify the derivative into `-(ν|k|^2) • u(t,k)`.
 243  have hsimp :
 244      ((heatFactor ν t k * (-ν * kSq k)) • a) = (-(ν * kSq k)) • ((u t) k) := by
 245    -- Use commutativity of real multiplication to flip the order, then `mul_smul`.
 246    have hut : (u t) k = (heatFactor ν t k) • a := by
 247      simpa [a] using (h t ht k)
 248    -- Rewrite to match `mul_smul` and then substitute `hut`.
 249    calc
 250      (heatFactor ν t k * (-ν * kSq k)) • a
 251          = ((-ν * kSq k) * heatFactor ν t k) • a := by
 252              simp [mul_comm, mul_assoc]
 253      _ = (-ν * kSq k) • ((heatFactor ν t k) • a) := by
 254              simp [mul_smul]
 255      _ = (-(ν * kSq k)) • ((heatFactor ν t k) • a) := by ring_nf
 256      _ = (-(ν * kSq k)) • ((u t) k) := by simp [hut]
 257
 258  -- `simp` may rewrite `heatFactor * (-ν*kSq)` as `-(heatFactor * (ν*kSq))`, so we also register
 259  -- a simp-friendly variant with the outer negation.
 260  have hsimp_neg :
 261      -((heatFactor ν t k * (ν * kSq k)) • a) = (-(ν * kSq k)) • ((u t) k) := by
 262    -- Move the `-` inside as `(-1) • _` and simplify using `hsimp`.
 263    have : (heatFactor ν t k * (-ν * kSq k)) • a = -((heatFactor ν t k * (ν * kSq k)) • a) := by
 264      -- scalar arithmetic in `ℝ` + `(-r) • a = -(r • a)`
 265      calc
 266        (heatFactor ν t k * (-ν * kSq k)) • a
 267            = (-(heatFactor ν t k * (ν * kSq k))) • a := by ring_nf
 268        _ = -((heatFactor ν t k * (ν * kSq k)) • a) := by
 269            simp [neg_smul]
 270    -- Now rewrite and finish.
 271    simpa [this] using hsimp
 272
 273  simpa [IsStokesODETraj, hsimp_neg] using huDeriv
 274
 275end IsStokesMildTraj
 276
 277/-!
 278## Galerkin → Fourier coefficient dynamics (modewise ODE, with nonlinearity)
 279
 280This is the first genuinely “Navier–Stokes shaped” bridge lemma: if a Galerkin trajectory satisfies
 281the finite-dimensional ODE `u' = νΔu - B(u,u)`, then every Fourier mode of its zero-extension
 282satisfies the corresponding modewise ODE with a forcing given by the zero-extended nonlinear term.
 283-/
 284
 285lemma extendByZero_laplacianCoeff {N : ℕ} (u : GalerkinState N) (k : Mode2) :
 286    (extendByZero (laplacianCoeff (N := N) u)) k = (-kSq k) • (extendByZero u) k := by
 287  classical
 288  by_cases hk : k ∈ modes N
 289  · ext j
 290    fin_cases j <;> simp [extendByZero, coeffAt, hk, laplacianCoeff]
 291  · ext j
 292    fin_cases j <;> simp [extendByZero, coeffAt, hk]
 293
 294lemma hasDerivAt_extendByZero_apply {N : ℕ} (k : Mode2)
 295    (u : ℝ → GalerkinState N) (u' : GalerkinState N) {t : ℝ} (hu : HasDerivAt u u' t) :
 296    HasDerivAt (fun s : ℝ => (extendByZero (u s)) k) ((extendByZero u') k) t := by
 297  classical
 298  -- A constant continuous linear map: project the `k`-th Fourier coefficient after zero-extension.
 299  let L : GalerkinState N →L[ℝ] VelCoeff :=
 300    (ContinuousLinearMap.proj k).comp (extendByZeroCLM (N := N))
 301  have hL : HasDerivAt (fun _ : ℝ => L) 0 t := by
 302    simpa using (hasDerivAt_const (x := t) (c := L))
 303  -- Differentiate `s ↦ L (u s)`.
 304  have h := HasDerivAt.clm_apply (c := fun _ : ℝ => L) (c' := (0 : GalerkinState N →L[ℝ] VelCoeff))
 305    (u := u) (u' := u') (x := t) hL hu
 306  -- Unfold `L` back to `extendByZero` + evaluation at `k`.
 307  simpa [L, extendByZeroCLM] using h
 308
 309theorem galerkinNS_hasDerivAt_extendByZero_mode {N : ℕ} (ν : ℝ) (B : ConvectionOp N)
 310    (u : ℝ → GalerkinState N) (k : Mode2) {t : ℝ}
 311    (hu : HasDerivAt u (galerkinNSRHS (N := N) ν B (u t)) t) :
 312    HasDerivAt (fun s : ℝ => (extendByZero (u s)) k)
 313      ((ν * (-kSq k)) • (extendByZero (u t)) k - (extendByZero (B (u t) (u t))) k) t := by
 314  -- Start from the generic differentiation-through-zero-extension lemma.
 315  have h0 :
 316      HasDerivAt (fun s : ℝ => (extendByZero (u s)) k)
 317        ((extendByZero (galerkinNSRHS (N := N) ν B (u t))) k) t :=
 318    hasDerivAt_extendByZero_apply (N := N) k u (galerkinNSRHS (N := N) ν B (u t)) hu
 319  -- Simplify the RHS using linearity of `extendByZero` and the diagonal Laplacian.
 320  -- `extendByZero (ν•Δu - B(u,u)) = ν•extendByZero(Δu) - extendByZero(B(u,u))`
 321  have hR :
 322      (extendByZero (galerkinNSRHS (N := N) ν B (u t)) k)
 323        = (ν * (-kSq k)) • (extendByZero (u t)) k - (extendByZero (B (u t) (u t))) k := by
 324    -- Push `extendByZero` through the RHS definition.
 325    simp [galerkinNSRHS, extendByZero_smul, extendByZero_add, extendByZero_neg,
 326      extendByZero_laplacianCoeff, sub_eq_add_neg, mul_smul]
 327  -- Rewrite the derivative statement with the simplified RHS.
 328  simpa [hR] using h0
 329
 330/-!
 331## Connecting the nonlinear forcing to the Duhamel remainder (modewise, differential form)
 332
 333We define a *Duhamel remainder* for a Galerkin trajectory by subtracting the linear heat evolution
 334of the initial coefficient. Then the modewise Galerkin ODE implies a forced linear ODE for this
 335remainder with forcing given by the zero-extended nonlinear term.
 336
 337This is not yet the full integral (variation-of-constants) formula, but it ties the remainder to
 338the Galerkin nonlinearity in a way that can later be integrated.
 339-/
 340
 341/-- Duhamel remainder (Galerkin → Fourier, modewise):
 342
 343`R(t,k) := û(t,k) - heatFactor(ν,t,k) • û(0,k)`. -/
 344noncomputable def duhamelRemainderOfGalerkin {N : ℕ} (ν : ℝ) (u : ℝ → GalerkinState N) : ℝ → FourierState2D :=
 345  fun t k => (extendByZero (u t) k) - (heatFactor ν t k) • (extendByZero (u 0) k)
 346
 347/-- If a Galerkin trajectory satisfies the discrete NS ODE, then its Duhamel remainder satisfies a
 348forced linear ODE per mode, with forcing `-(extendByZero (B(u,u)))`.
 349
 350This is the differential version of the Duhamel/variation-of-constants formula. -/
 351theorem galerkinNS_hasDerivAt_duhamelRemainder_mode {N : ℕ} (ν : ℝ) (B : ConvectionOp N)
 352    (u : ℝ → GalerkinState N) (k : Mode2) {t : ℝ}
 353    (hu : HasDerivAt u (galerkinNSRHS (N := N) ν B (u t)) t) :
 354    HasDerivAt (fun s : ℝ => (duhamelRemainderOfGalerkin (N := N) ν u s) k)
 355      ((ν * (-kSq k)) • (duhamelRemainderOfGalerkin (N := N) ν u t k) - (extendByZero (B (u t) (u t))) k) t := by
 356  classical
 357  -- shorthand
 358  let a0 : VelCoeff := (extendByZero (u 0)) k
 359
 360  -- derivative of the main coefficient from the Galerkin ODE
 361  have ha :
 362      HasDerivAt (fun s : ℝ => (extendByZero (u s)) k)
 363        ((ν * (-kSq k)) • (extendByZero (u t)) k - (extendByZero (B (u t) (u t))) k) t :=
 364    galerkinNS_hasDerivAt_extendByZero_mode (N := N) (ν := ν) (B := B) (u := u) (k := k) hu
 365
 366  -- derivative of the linear heat term `s ↦ heatFactor ν s k • a0`
 367  have hlin : HasDerivAt (fun s : ℝ => (-ν * kSq k) * s) (-ν * kSq k) t := by
 368    simpa [mul_assoc] using (hasDerivAt_id t).const_mul (-ν * kSq k)
 369  have hscalar :
 370      HasDerivAt (fun s : ℝ => heatFactor ν s k) (heatFactor ν t k * (-ν * kSq k)) t := by
 371    have hexp :
 372        HasDerivAt (fun s : ℝ => Real.exp ((-ν * kSq k) * s))
 373          (Real.exp ((-ν * kSq k) * t) * (-ν * kSq k)) t :=
 374      (Real.hasDerivAt_exp ((-ν * kSq k) * t)).comp t hlin
 375    simpa [heatFactor, mul_assoc] using hexp
 376  have hb :
 377      HasDerivAt (fun s : ℝ => (heatFactor ν s k) • a0)
 378        ((heatFactor ν t k * (-ν * kSq k)) • a0) t :=
 379    hscalar.smul_const a0
 380
 381  -- differentiate the difference
 382  have hsub :
 383      HasDerivAt (fun s : ℝ => (extendByZero (u s)) k - (heatFactor ν s k) • a0)
 384        (((ν * (-kSq k)) • (extendByZero (u t)) k - (extendByZero (B (u t) (u t))) k)
 385          - ((heatFactor ν t k * (-ν * kSq k)) • a0)) t := by
 386    -- Do not `simpa` here: we want to keep the derivative expression in a form that
 387    -- matches the subsequent algebraic rewrite.
 388    simpa [sub_eq_add_neg] using ha.sub hb
 389
 390  -- rewrite the derivative into the forced-remainder form
 391  have hb' :
 392      (heatFactor ν t k * (-ν * kSq k)) • a0 = (ν * (-kSq k)) • ((heatFactor ν t k) • a0) := by
 393    calc
 394      (heatFactor ν t k * (-ν * kSq k)) • a0
 395          = ((-ν * kSq k) * heatFactor ν t k) • a0 := by
 396              simp [mul_comm, mul_assoc]
 397      _ = (-ν * kSq k) • ((heatFactor ν t k) • a0) := by
 398              simp [mul_smul]
 399      _ = (ν * (-kSq k)) • ((heatFactor ν t k) • a0) := by ring_nf
 400
 401  have hderiv_simp :
 402      (((ν * (-kSq k)) • (extendByZero (u t)) k - (extendByZero (B (u t) (u t))) k)
 403          - ((heatFactor ν t k * (-ν * kSq k)) • a0))
 404        = ((ν * (-kSq k)) • ((extendByZero (u t)) k - (heatFactor ν t k) • a0)
 405            - (extendByZero (B (u t) (u t))) k) := by
 406    -- push the scalar through the subtraction and use the rewritten `hb'`
 407    -- First rewrite the heat-term derivative using `hb'`, then reduce to commutative-additive algebra.
 408    rw [hb']
 409    -- Expand `c • (x - y)` and rewrite subtraction as addition; then commutativity closes the goal.
 410    simp [sub_eq_add_neg, add_left_comm, add_comm]
 411
 412  -- conclude
 413  have hsub' :
 414      HasDerivAt (fun s : ℝ => (extendByZero (u s)) k - (heatFactor ν s k) • a0)
 415        ((ν * (-kSq k)) • ((extendByZero (u t)) k - (heatFactor ν t k) • a0)
 416          - (extendByZero (B (u t) (u t))) k) t := by
 417    -- Avoid `simp` rewriting the derivative expression (it tends to normalize `ν * (-kSq)` into `-(ν*kSq)`).
 418    -- Instead, rewrite the goal using the proved algebraic identity `hderiv_simp`.
 419    rw [← hderiv_simp]
 420    exact hsub
 421
 422  simpa [duhamelRemainderOfGalerkin, a0] using hsub'
 423
 424/-- Variation-of-constants / Duhamel (integrating factor) formula for the Galerkin remainder (modewise).
 425
 426This upgrades the forced remainder ODE to an **integral** identity, assuming the forcing term is
 427interval-integrable.
 428
 429Note: This is an integrating-factor form; rewriting it into the usual kernel `heatFactor ν (t-s) k`
 430is a later algebraic step (plus a change-of-variables in the integral). -/
 431theorem duhamelRemainderOfGalerkin_integratingFactor
 432    {N : ℕ} (ν : ℝ) (B : ConvectionOp N) (u : ℝ → GalerkinState N) (k : Mode2) (t : ℝ)
 433    (hu : ∀ s : ℝ, HasDerivAt u (galerkinNSRHS (N := N) ν B (u s)) s)
 434    (hint :
 435      IntervalIntegrable (fun s : ℝ =>
 436        (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k))
 437        MeasureTheory.volume 0 t) :
 438    (Real.exp (-(ν * (-kSq k)) * t)) • (duhamelRemainderOfGalerkin (N := N) ν u t k)
 439      =
 440        -∫ s in (0 : ℝ)..t, (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k) := by
 441  classical
 442  -- Notation
 443  let a : ℝ := ν * (-kSq k)
 444  let E : ℝ → ℝ := fun s => Real.exp (-a * s)
 445  let R : ℝ → VelCoeff := fun s => (duhamelRemainderOfGalerkin (N := N) ν u s) k
 446  let F : ℝ → VelCoeff := fun s => (extendByZero (B (u s) (u s))) k
 447  let G : ℝ → VelCoeff := fun s => (E s) • (R s)
 448
 449  have hE : ∀ x : ℝ, HasDerivAt E (-(E x * a)) x := by
 450    intro x
 451    have hlin : HasDerivAt (fun s : ℝ => (-a) * s) (-a) x := by
 452      simpa [mul_assoc] using (hasDerivAt_id x).const_mul (-a)
 453    have hexp :
 454        HasDerivAt (fun s : ℝ => Real.exp ((-a) * s)) (Real.exp ((-a) * x) * (-a)) x :=
 455      (Real.hasDerivAt_exp ((-a) * x)).comp x hlin
 456    -- rewrite `r * (-a)` as `-(r * a)`
 457    simpa [E, mul_assoc, mul_neg] using hexp
 458
 459  have hR : ∀ x : ℝ, HasDerivAt R (a • R x - F x) x := by
 460    intro x
 461    -- Remainder ODE at time `x`
 462    simpa [R, F, a] using
 463      (galerkinNS_hasDerivAt_duhamelRemainder_mode (N := N) (ν := ν) (B := B) (u := u) (k := k) (t := x)
 464        (hu x))
 465
 466  have hGderiv : ∀ x ∈ Set.uIcc (0 : ℝ) t, HasDerivAt G (-((E x) • (F x))) x := by
 467    intro x _hx
 468    have hG0 : HasDerivAt G ((E x) • (a • R x - F x) + (-(E x * a)) • R x) x :=
 469      (hE x).smul (hR x)
 470    have hG0' : HasDerivAt G ((E x) • (a • R x - F x) + -((E x * a) • R x)) x := by
 471      simpa [neg_smul] using hG0
 472    -- Simplify: the `a • R` terms cancel, leaving `-(E x • F x)`.
 473    have hsimp :
 474        (E x) • (a • R x - F x) + -((E x * a) • R x) = -((E x) • (F x)) := by
 475      -- Expand `E • (a • R - F)` and cancel the `a • R` terms.
 476      calc
 477        (E x) • (a • R x - F x) + -((E x * a) • R x)
 478            = (E x) • (a • R x - F x) - (E x * a) • R x := by
 479                simp [sub_eq_add_neg]
 480        _ = (E x) • (a • R x) - (E x) • (F x) - (E x * a) • R x := by
 481                simp [sub_eq_add_neg, add_assoc]
 482        _ = (E x * a) • R x - (E x) • (F x) - (E x * a) • R x := by
 483                simp [smul_smul]
 484        _ = -((E x) • (F x)) := by abel
 485    simpa [G, hsimp] using hG0'
 486
 487  -- Integrate the derivative of `G` on `0..t`.
 488  have hint' : IntervalIntegrable (fun s : ℝ => -((E s) • (F s))) MeasureTheory.volume 0 t := by
 489    -- our hypothesis is stated for `Real.exp (-(ν * (-kSq k)) * s)`, which is `E s` by definition
 490    -- and `F s` is the nonlinear term.
 491    -- `IntervalIntegrable.neg` handles the outer `-`.
 492    have : IntervalIntegrable (fun s : ℝ => (E s) • (F s)) MeasureTheory.volume 0 t := by
 493      simpa [E, F, a] using hint
 494    simpa using this.neg
 495
 496  have hFTC :
 497      (∫ s in (0 : ℝ)..t, -((E s) • (F s))) = G t - G 0 :=
 498    intervalIntegral.integral_eq_sub_of_hasDerivAt (a := (0 : ℝ)) (b := t)
 499      (hderiv := hGderiv) (hint := hint')
 500
 501  have hR0 : R 0 = 0 := by
 502    -- `R 0 = û(0,k) - heatFactor(0)•û(0,k) = 0`
 503    simp [R, duhamelRemainderOfGalerkin, heatFactor]
 504
 505  have hG0 : G 0 = 0 := by
 506    simp [G, E, hR0]
 507
 508  -- Rewrite the result in the desired form.
 509  have hEq : (E t) • (R t) = ∫ s in (0 : ℝ)..t, -((E s) • (F s)) := by
 510    -- `hFTC` gives the integral equals `G t - G 0`; and `G 0 = 0`.
 511    have : (∫ s in (0 : ℝ)..t, -((E s) • (F s))) = (E t) • (R t) := by
 512      simpa [hG0, G] using hFTC
 513    exact this.symm
 514
 515  have hEq' : (E t) • (R t) = -∫ s in (0 : ℝ)..t, (E s) • (F s) := by
 516    calc
 517      (E t) • (R t) = ∫ s in (0 : ℝ)..t, -((E s) • (F s)) := hEq
 518      _ = -∫ s in (0 : ℝ)..t, (E s) • (F s) := by simp
 519
 520  simpa [E, R, F, a] using hEq'
 521
 522/-- Rewrite the integrating-factor remainder formula into the standard Duhamel kernel form:
 523
 524`R(t,k) = -∫₀ᵗ heatFactor ν (t - s) k • F(s,k) ds`.
 525
 526This is a purely algebraic rewrite of `duhamelRemainderOfGalerkin_integratingFactor`. -/
 527theorem duhamelRemainderOfGalerkin_kernel
 528    {N : ℕ} (ν : ℝ) (B : ConvectionOp N) (u : ℝ → GalerkinState N) (k : Mode2) (t : ℝ)
 529    (hu : ∀ s : ℝ, HasDerivAt u (galerkinNSRHS (N := N) ν B (u s)) s)
 530    (hint :
 531      IntervalIntegrable (fun s : ℝ =>
 532        (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k))
 533        MeasureTheory.volume 0 t) :
 534    duhamelRemainderOfGalerkin (N := N) ν u t k
 535      =
 536        -∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k) := by
 537  -- Start from the integrating-factor identity.
 538  have hIF :=
 539    duhamelRemainderOfGalerkin_integratingFactor (N := N) (ν := ν) (B := B) (u := u) (k := k) (t := t) hu hint
 540
 541  -- Multiply both sides by the heat factor at time `t` to cancel the integrating factor.
 542  have hmul : (heatFactor ν t k) • ((Real.exp (-(ν * (-kSq k)) * t)) • duhamelRemainderOfGalerkin (N := N) ν u t k)
 543      =
 544        (heatFactor ν t k) • (-∫ s in (0 : ℝ)..t,
 545          (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k)) := by
 546    simpa using congrArg (fun v => (heatFactor ν t k) • v) hIF
 547
 548  -- Simplify the left-hand side: `heatFactor(t) * exp(ν|k|^2 t) = 1`.
 549  have hcancel :
 550      (heatFactor ν t k) • ((Real.exp (-(ν * (-kSq k)) * t)) • duhamelRemainderOfGalerkin (N := N) ν u t k)
 551        = duhamelRemainderOfGalerkin (N := N) ν u t k := by
 552    -- turn nested smul into product scalar
 553    have hprod : (heatFactor ν t k) * (Real.exp (-(ν * (-kSq k)) * t)) = 1 := by
 554      calc
 555        (heatFactor ν t k) * (Real.exp (-(ν * (-kSq k)) * t))
 556            = Real.exp (-ν * kSq k * t) * Real.exp (ν * kSq k * t) := by
 557                simp [heatFactor]
 558        _ = Real.exp ((-ν * kSq k * t) + (ν * kSq k * t)) := (Real.exp_add _ _).symm
 559        _ = Real.exp 0 := by ring_nf
 560        _ = 1 := by simp
 561    calc
 562      (heatFactor ν t k) • ((Real.exp (-(ν * (-kSq k)) * t)) • duhamelRemainderOfGalerkin (N := N) ν u t k)
 563          = ((heatFactor ν t k) * (Real.exp (-(ν * (-kSq k)) * t))) • duhamelRemainderOfGalerkin (N := N) ν u t k := by
 564              simp [smul_smul]
 565      _ = (1 : ℝ) • duhamelRemainderOfGalerkin (N := N) ν u t k := by
 566            -- avoid `simp` rewriting the exponential before applying `hprod`
 567            rw [hprod]
 568      _ = duhamelRemainderOfGalerkin (N := N) ν u t k := by simp
 569
 570  -- Move the scalar inside the integral, then combine the exponentials into `heatFactor ν (t - s) k`.
 571  have hRHS :
 572      (heatFactor ν t k) • (-∫ s in (0 : ℝ)..t,
 573          (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k))
 574        =
 575        -∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k) := by
 576    -- Let `f(s)` be the integrand in the integrating-factor identity.
 577    let f : ℝ → VelCoeff :=
 578      fun s => (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k)
 579    -- First move the scalar inside the integral.
 580    have hsmul :
 581        (heatFactor ν t k) • (∫ s in (0 : ℝ)..t, f s) =
 582          ∫ s in (0 : ℝ)..t, (heatFactor ν t k) • (f s) := by
 583      simp [f]
 584    -- Rewrite `heatFactor ν t k • (-∫ f)` as `-∫ (heatFactor ν t k • f)`.
 585    have hneg :
 586        (heatFactor ν t k) • (-∫ s in (0 : ℝ)..t, f s)
 587          = -∫ s in (0 : ℝ)..t, (heatFactor ν t k) • (f s) := by
 588      calc
 589        (heatFactor ν t k) • (-∫ s in (0 : ℝ)..t, f s)
 590            = -((heatFactor ν t k) • (∫ s in (0 : ℝ)..t, f s)) := by simp [smul_neg]
 591        _ = -(∫ s in (0 : ℝ)..t, (heatFactor ν t k) • (f s)) := by rw [hsmul]
 592        _ = -∫ s in (0 : ℝ)..t, (heatFactor ν t k) • (f s) := rfl
 593    -- Now simplify the integrand pointwise on the integration interval.
 594    have hEqOn :
 595        Set.EqOn (fun s => (heatFactor ν t k) • (f s))
 596          (fun s => (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k)) (Set.uIcc (0 : ℝ) t) := by
 597      intro s _hs
 598      -- combine scalar factors into `heatFactor ν (t - s) k`
 599      have hscalar :
 600          (heatFactor ν t k) * (Real.exp (-(ν * (-kSq k)) * s)) = heatFactor ν (t - s) k := by
 601        calc
 602          (heatFactor ν t k) * (Real.exp (-(ν * (-kSq k)) * s))
 603              = Real.exp (-ν * kSq k * t) * Real.exp (ν * kSq k * s) := by
 604                  simp [heatFactor]
 605          _ = Real.exp ((-ν * kSq k * t) + (ν * kSq k * s)) := (Real.exp_add _ _).symm
 606          _ = Real.exp (-ν * kSq k * (t - s)) := by ring_nf
 607          _ = heatFactor ν (t - s) k := by simp [heatFactor]
 608      -- use the scalar identity to rewrite the smul
 609      calc
 610        (heatFactor ν t k) • (f s)
 611            = ((heatFactor ν t k) * (Real.exp (-(ν * (-kSq k)) * s)))
 612                • (extendByZero (B (u s) (u s)) k) := by
 613                  simpa [f] using
 614                    (smul_smul (heatFactor ν t k) (Real.exp (-(ν * (-kSq k)) * s))
 615                      (extendByZero (B (u s) (u s)) k))
 616        _ = (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k) := by
 617              -- rewrite the scalar coefficient
 618              rw [hscalar]
 619    have hinterr :
 620        ∫ s in (0 : ℝ)..t, (heatFactor ν t k) • (f s)
 621          = ∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k) :=
 622      intervalIntegral.integral_congr (μ := MeasureTheory.volume) (a := (0 : ℝ)) (b := t) hEqOn
 623    -- Finish.
 624    -- rewrite the integral using `hinterr`
 625    calc
 626      (heatFactor ν t k) • (-∫ s in (0 : ℝ)..t, f s)
 627          = -∫ s in (0 : ℝ)..t, (heatFactor ν t k) • (f s) := hneg
 628      _ = -∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k) := by
 629            rw [hinterr]
 630
 631  -- Combine.
 632  -- use `hcancel` to rewrite the left-hand side, then apply the rewritten right-hand side
 633  calc
 634    duhamelRemainderOfGalerkin (N := N) ν u t k
 635        = (heatFactor ν t k) • ((Real.exp (-(ν * (-kSq k)) * t)) • duhamelRemainderOfGalerkin (N := N) ν u t k) := by
 636            simpa using hcancel.symm
 637    _ = (heatFactor ν t k) • (-∫ s in (0 : ℝ)..t,
 638          (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k)) := by
 639            simpa using hmul
 640    _ = -∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (extendByZero (B (u s) (u s)) k) := hRHS
 641
 642/-!
 643## Analytic packaging: dominated convergence for the Duhamel kernel integral
 644
 645To pass the *time-integrated nonlinear forcing* to the limit, we will eventually need a dominated
 646convergence / uniform integrability step. We start by packaging the exact hypotheses needed to apply
 647`intervalIntegral.tendsto_integral_filter_of_dominated_convergence` to the Duhamel kernel integral.
 648
 649This keeps the proof honest (no `sorry`), while making the missing analytic ingredient explicit.
 650-/
 651
 652/-- The Duhamel kernel operator applied to a (Fourier-modewise) forcing trajectory:
 653
 654`(duhamelKernelIntegral ν F)(t,k) = -∫₀ᵗ heatFactor ν (t-s) k • F(s,k) ds`. -/
 655noncomputable def duhamelKernelIntegral (ν : ℝ) (F : ℝ → FourierState2D) : ℝ → FourierState2D :=
 656  fun t k => -∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (F s k)
 657
 658/-- Hypothesis (at fixed `t,k`): the Duhamel-kernel integrands satisfy the assumptions of dominated
 659convergence, so the corresponding interval integrals converge. -/
 660structure DuhamelKernelDominatedConvergenceAt
 661    (ν : ℝ) (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D)
 662    (t : ℝ) (k : Mode2) where
 663  /-- An `L¹` bound for the integrands on `0..t`. -/
 664  bound : ℝ → ℝ
 665  /-- Strong measurability (eventually in `N`) on the relevant interval. -/
 666  h_meas :
 667    ∀ᶠ N : ℕ in atTop,
 668      MeasureTheory.AEStronglyMeasurable
 669        (fun s : ℝ => (heatFactor ν (t - s) k) • (F_N N s k))
 670        (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t))
 671  /-- Dominating bound (eventually in `N`) on the relevant interval. -/
 672  h_bound :
 673    ∀ᶠ N : ℕ in atTop,
 674      ∀ᵐ s ∂MeasureTheory.volume, s ∈ Set.uIoc (0 : ℝ) t →
 675        ‖(heatFactor ν (t - s) k) • (F_N N s k)‖ ≤ bound s
 676  /-- Integrability of the bound. -/
 677  bound_integrable :
 678    IntervalIntegrable bound MeasureTheory.volume (0 : ℝ) t
 679  /-- Pointwise (ae on the interval) convergence of the integrands. -/
 680  h_lim :
 681    ∀ᵐ s ∂MeasureTheory.volume, s ∈ Set.uIoc (0 : ℝ) t →
 682      Tendsto (fun N : ℕ => (heatFactor ν (t - s) k) • (F_N N s k)) atTop
 683        (𝓝 ((heatFactor ν (t - s) k) • (F s k)))
 684
 685/-- A more user-facing dominated-convergence hypothesis at fixed `t,k` for the *forcing* itself,
 686without baking in the Duhamel kernel factor. Under `0 ≤ ν` and `0 ≤ t`, this implies
 687`DuhamelKernelDominatedConvergenceAt` because `|heatFactor ν (t-s) k| ≤ 1` on `s ∈ Set.uIoc 0 t`. -/
 688structure ForcingDominatedConvergenceAt
 689    (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D) (t : ℝ) (k : Mode2) where
 690  bound : ℝ → ℝ
 691  h_meas :
 692    ∀ᶠ N : ℕ in atTop,
 693      MeasureTheory.AEStronglyMeasurable
 694        (fun s : ℝ => F_N N s k)
 695        (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t))
 696  h_bound :
 697    ∀ᶠ N : ℕ in atTop,
 698      ∀ᵐ s ∂MeasureTheory.volume, s ∈ Set.uIoc (0 : ℝ) t → ‖F_N N s k‖ ≤ bound s
 699  bound_integrable :
 700    IntervalIntegrable bound MeasureTheory.volume (0 : ℝ) t
 701  h_lim :
 702    ∀ᵐ s ∂MeasureTheory.volume, s ∈ Set.uIoc (0 : ℝ) t →
 703      Tendsto (fun N : ℕ => F_N N s k) atTop (𝓝 (F s k))
 704
 705/-- Heat kernel bound: for `ν ≥ 0` and `τ ≥ 0`, we have `|heatFactor ν τ k| ≤ 1`. -/
 706lemma abs_heatFactor_le_one (ν : ℝ) (hν : 0 ≤ ν) (τ : ℝ) (hτ : 0 ≤ τ) (k : Mode2) :
 707    |heatFactor ν τ k| ≤ 1 := by
 708  -- `heatFactor = exp (-ν * kSq k * τ)` with a nonpositive exponent.
 709  have hkSq : 0 ≤ kSq k := by simp [kSq, add_nonneg, sq_nonneg]
 710  have harg : (-ν * kSq k * τ) ≤ 0 := by
 711    have hprod : 0 ≤ ν * kSq k * τ := mul_nonneg (mul_nonneg hν hkSq) hτ
 712    -- `-x ≤ 0` for `x ≥ 0`
 713    have : -(ν * kSq k * τ) ≤ 0 := neg_nonpos.mpr hprod
 714    simpa [mul_assoc, mul_left_comm, mul_comm] using this
 715  have hle : heatFactor ν τ k ≤ 1 := by
 716    simpa [heatFactor] using (Real.exp_le_one_iff.2 harg)
 717  have hnonneg : 0 ≤ heatFactor ν τ k := (Real.exp_pos _).le
 718  simpa [abs_of_nonneg hnonneg] using hle
 719
 720/-- Convenience constructor: it is often easier to assume pointwise statements (`∀`) rather than
 721almost-everywhere (`∀ᵐ`). This helper upgrades pointwise bounds + convergence to the AE versions
 722required by `ForcingDominatedConvergenceAt`. -/
 723noncomputable def ForcingDominatedConvergenceAt.of_forall
 724    (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D) (t : ℝ) (k : Mode2)
 725    (bound : ℝ → ℝ)
 726    (h_meas : ∀ N : ℕ,
 727      MeasureTheory.AEStronglyMeasurable
 728        (fun s : ℝ => F_N N s k)
 729        (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t)))
 730    (h_bound : ∀ N : ℕ, ∀ s : ℝ, s ∈ Set.uIoc (0 : ℝ) t → ‖F_N N s k‖ ≤ bound s)
 731    (bound_integrable : IntervalIntegrable bound MeasureTheory.volume (0 : ℝ) t)
 732    (h_lim : ∀ s : ℝ, s ∈ Set.uIoc (0 : ℝ) t →
 733      Tendsto (fun N : ℕ => F_N N s k) atTop (𝓝 (F s k))) :
 734    ForcingDominatedConvergenceAt (F_N := F_N) (F := F) t k :=
 735  { bound := bound
 736    h_meas := Filter.Eventually.of_forall h_meas
 737    h_bound := by
 738      refine Filter.Eventually.of_forall ?_
 739      intro N
 740      exact MeasureTheory.ae_of_all _ (fun s hs => h_bound N s hs)
 741    bound_integrable := bound_integrable
 742    h_lim := by
 743      exact MeasureTheory.ae_of_all _ (fun s hs => h_lim s hs) }
 744
 745/-- Convert a forcing-level dominated convergence hypothesis into the kernel-level one. -/
 746noncomputable def duhamelKernelDominatedConvergenceAt_of_forcing
 747    {ν t : ℝ} (hν : 0 ≤ ν) (ht : 0 ≤ t)
 748    {F_N : ℕ → ℝ → FourierState2D} {F : ℝ → FourierState2D} {k : Mode2}
 749    (hF : ForcingDominatedConvergenceAt F_N F t k) :
 750    DuhamelKernelDominatedConvergenceAt ν F_N F t k := by
 751  classical
 752  refine
 753    { bound := hF.bound
 754      h_meas := ?_
 755      h_bound := ?_
 756      bound_integrable := hF.bound_integrable
 757      h_lim := ?_ }
 758  · -- measurability: `heatFactor( t - s )` is continuous, and `smul` preserves AE-strong measurability
 759    have hheat_meas :
 760        MeasureTheory.AEStronglyMeasurable (fun s : ℝ => heatFactor ν (t - s) k)
 761          (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t)) := by
 762      -- continuity in `s`
 763      have hcont : Continuous fun s : ℝ => heatFactor ν (t - s) k := by
 764        -- unfold the scalar kernel and use continuity of `Real.exp`
 765        -- `s ↦ exp (-ν * kSq k * (t - s))`
 766        have hlin : Continuous fun s : ℝ => (-ν * kSq k) * (t - s) := by
 767          exact (continuous_const.mul (continuous_const.sub continuous_id))
 768        simpa [heatFactor, mul_assoc] using (continuous_exp.comp hlin)
 769      exact hcont.aestronglyMeasurable
 770    refine hF.h_meas.mono ?_
 771    intro N hmeasN
 772    exact hheat_meas.smul hmeasN
 773  · -- domination: `|heatFactor| ≤ 1` on `s ∈ uIoc 0 t` (for `t ≥ 0`, `ν ≥ 0`)
 774    refine hF.h_bound.mono ?_
 775    intro N hboundN
 776    filter_upwards [hboundN] with s hsBound
 777    intro hs
 778    have hst : 0 < s ∧ s ≤ t := by
 779      -- unpack membership in the unordered interval
 780      have hs' : min (0 : ℝ) t < s ∧ s ≤ max (0 : ℝ) t := by
 781        simpa [Set.uIoc, Set.mem_Ioc] using hs
 782      simpa [min_eq_left ht, max_eq_right ht] using hs'
 783    have hts : 0 ≤ t - s := sub_nonneg.mpr hst.2
 784    have habs : |heatFactor ν (t - s) k| ≤ 1 :=
 785      abs_heatFactor_le_one ν hν (t - s) hts k
 786    have hx : ‖F_N N s k‖ ≤ hF.bound s := hsBound hs
 787    calc
 788      ‖(heatFactor ν (t - s) k) • (F_N N s k)‖
 789          = |heatFactor ν (t - s) k| * ‖F_N N s k‖ := by
 790              simp [Real.norm_eq_abs, norm_smul]
 791      _ ≤ ‖F_N N s k‖ := by
 792            simpa [one_mul] using (mul_le_of_le_one_left (norm_nonneg _) habs)
 793      _ ≤ hF.bound s := hx
 794  · -- pointwise convergence: multiply by the (fixed-in-`N`) scalar `heatFactor ν (t-s) k`
 795    filter_upwards [hF.h_lim] with s hsLim
 796    intro hs
 797    have hcont : Continuous fun v : VelCoeff => (heatFactor ν (t - s) k) • v := continuous_const_smul _
 798    exact (hcont.tendsto (F s k)).comp (hsLim hs)
 799
 800/-- Under the dominated-convergence hypothesis at `t,k`, the Duhamel-kernel integrals converge. -/
 801theorem tendsto_duhamelKernelIntegral_of_dominated_convergence
 802    (ν : ℝ) (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D) (t : ℝ) (k : Mode2)
 803    (hDC : DuhamelKernelDominatedConvergenceAt ν F_N F t k) :
 804    Tendsto (fun N : ℕ => (duhamelKernelIntegral ν (F_N N) t) k) atTop
 805      (𝓝 ((duhamelKernelIntegral ν F t) k)) := by
 806  -- Apply the dominated convergence theorem for interval integrals.
 807  have hT :
 808      Tendsto
 809          (fun N : ℕ =>
 810            ∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (F_N N s k))
 811          atTop
 812          (𝓝 (∫ s in (0 : ℝ)..t, (heatFactor ν (t - s) k) • (F s k))) := by
 813    simpa using
 814      (intervalIntegral.tendsto_integral_filter_of_dominated_convergence (μ := MeasureTheory.volume)
 815        (a := (0 : ℝ)) (b := t) (l := atTop)
 816        (F := fun N : ℕ => fun s : ℝ => (heatFactor ν (t - s) k) • (F_N N s k))
 817        (f := fun s : ℝ => (heatFactor ν (t - s) k) • (F s k))
 818        (bound := hDC.bound)
 819        (hF_meas := hDC.h_meas)
 820        (h_bound := hDC.h_bound)
 821        (bound_integrable := hDC.bound_integrable)
 822        (h_lim := hDC.h_lim))
 823  -- Move the outer `-` through the limit.
 824  simpa [duhamelKernelIntegral] using hT.neg
 825
 826/-- Galerkin modewise Duhamel identity in kernel form:
 827
 828`û(t,k) = heatFactor ν t k • û(0,k) + duhamelKernelIntegral ν (extendByZero ∘ B(u,u)) (t,k)`.
 829
 830This is just `duhamelRemainderOfGalerkin_kernel` rewritten using the definition of the remainder. -/
 831theorem galerkin_duhamelKernel_identity
 832    {N : ℕ} (ν : ℝ) (B : ConvectionOp N) (u : ℝ → GalerkinState N) (k : Mode2) (t : ℝ)
 833    (hu : ∀ s : ℝ, HasDerivAt u (galerkinNSRHS (N := N) ν B (u s)) s)
 834    (hint :
 835      IntervalIntegrable (fun s : ℝ =>
 836        (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero (B (u s) (u s)) k))
 837        MeasureTheory.volume 0 t) :
 838    (extendByZero (u t) k)
 839      =
 840        (heatFactor ν t k) • (extendByZero (u 0) k)
 841          + (duhamelKernelIntegral ν (fun s : ℝ => extendByZero (B (u s) (u s))) t) k := by
 842  -- Start from the kernel-form remainder identity.
 843  have hR :=
 844    duhamelRemainderOfGalerkin_kernel (N := N) (ν := ν) (B := B) (u := u) (k := k) (t := t) hu hint
 845  -- Unfold the remainder and rearrange.
 846  -- `R(t,k) = û(t,k) - heatFactor(t)•û(0,k)` and `R(t,k) = kernelIntegral(t,k)`.
 847  have :
 848      (extendByZero (u t) k) - (heatFactor ν t k) • (extendByZero (u 0) k)
 849        =
 850          (duhamelKernelIntegral ν (fun s : ℝ => extendByZero (B (u s) (u s))) t) k := by
 851    simpa [duhamelRemainderOfGalerkin, duhamelKernelIntegral] using hR
 852  -- Add the heat term to both sides.
 853  exact (sub_eq_iff_eq_add').1 this
 854
 855/-!
 856## A derived bound: single coefficient ≤ global norm
 857
 858Even before doing any PDE analysis, we can prove a simple but useful fact:
 859the norm of one Fourier coefficient (after zero-extension) is bounded by the
 860global Euclidean norm of the truncated Galerkin state.
 861-/
 862
 863lemma norm_extendByZero_le {N : ℕ} (u : GalerkinState N) (k : Mode2) :
 864    ‖(extendByZero u) k‖ ≤ ‖u‖ := by
 865  classical
 866  by_cases hk : k ∈ modes N
 867  ·
 868    have hext :
 869        (extendByZero u) k =
 870          !₂[u (⟨k, hk⟩, (⟨0, by decide⟩ : Fin 2)),
 871             u (⟨k, hk⟩, (⟨1, by decide⟩ : Fin 2))] := by
 872      simp [extendByZero, coeffAt, hk]
 873
 874    have hsq_ext :
 875        ‖(extendByZero u) k‖ ^ 2 =
 876          ‖u (⟨k, hk⟩, (⟨0, by decide⟩ : Fin 2))‖ ^ 2
 877            + ‖u (⟨k, hk⟩, (⟨1, by decide⟩ : Fin 2))‖ ^ 2 := by
 878      -- For `Fin 2`, `EuclideanSpace.norm_sq_eq` expands to the sum of the two coordinate squares.
 879      simp [hext, EuclideanSpace.norm_sq_eq, Fin.sum_univ_two]
 880
 881    have hnorm_u : ‖u‖ ^ 2 = ∑ kc : ((modes N) × Fin 2), ‖u kc‖ ^ 2 := by
 882      simp [EuclideanSpace.norm_sq_eq]
 883
 884    -- The 2-coordinate sum is bounded by the full coordinate sum.
 885    have hcoord_le :
 886        (‖u (⟨k, hk⟩, (⟨0, by decide⟩ : Fin 2))‖ ^ 2
 887            + ‖u (⟨k, hk⟩, (⟨1, by decide⟩ : Fin 2))‖ ^ 2)
 888          ≤ (∑ kc : ((modes N) × Fin 2), ‖u kc‖ ^ 2) := by
 889      let k' : (modes N) := ⟨k, hk⟩
 890      let s : Finset ((modes N) × Fin 2) :=
 891        insert (k', (⟨0, by decide⟩ : Fin 2)) ({(k', (⟨1, by decide⟩ : Fin 2))} : Finset ((modes N) × Fin 2))
 892      have hs : s ⊆ (Finset.univ : Finset ((modes N) × Fin 2)) := by
 893        intro x hx
 894        simp
 895      have hsum :
 896          (‖u (k', (⟨0, by decide⟩ : Fin 2))‖ ^ 2 + ‖u (k', (⟨1, by decide⟩ : Fin 2))‖ ^ 2)
 897            = (∑ kc ∈ s, ‖u kc‖ ^ 2) := by
 898        simp [s]
 899      have hle : (∑ kc ∈ s, ‖u kc‖ ^ 2) ≤ (∑ kc : ((modes N) × Fin 2), ‖u kc‖ ^ 2) := by
 900        have hle' :
 901            (∑ kc ∈ s, ‖u kc‖ ^ 2)
 902              ≤ (∑ kc ∈ (Finset.univ : Finset ((modes N) × Fin 2)), ‖u kc‖ ^ 2) := by
 903          refine Finset.sum_le_sum_of_subset_of_nonneg hs ?_
 904          intro kc _hkc _hknot
 905          exact sq_nonneg ‖u kc‖
 906        simpa using hle'
 907      calc
 908        (‖u (k', (⟨0, by decide⟩ : Fin 2))‖ ^ 2 + ‖u (k', (⟨1, by decide⟩ : Fin 2))‖ ^ 2)
 909            = (∑ kc ∈ s, ‖u kc‖ ^ 2) := hsum
 910        _ ≤ (∑ kc : ((modes N) × Fin 2), ‖u kc‖ ^ 2) := hle
 911
 912    have hsq_le : ‖(extendByZero u) k‖ ^ 2 ≤ ‖u‖ ^ 2 := by
 913      calc
 914        ‖(extendByZero u) k‖ ^ 2
 915            = (‖u (⟨k, hk⟩, (⟨0, by decide⟩ : Fin 2))‖ ^ 2
 916                + ‖u (⟨k, hk⟩, (⟨1, by decide⟩ : Fin 2))‖ ^ 2) := hsq_ext
 917        _ ≤ (∑ kc : ((modes N) × Fin 2), ‖u kc‖ ^ 2) := hcoord_le
 918        _ = ‖u‖ ^ 2 := by simp [hnorm_u]
 919
 920    exact le_of_sq_le_sq hsq_le (norm_nonneg u)
 921  ·
 922    -- Outside the truncation window the coefficient is zero, so the bound is trivial.
 923    have hnorm : ‖(extendByZero u) k‖ = 0 := by
 924      simp [extendByZero, coeffAt, hk]
 925    simp [hnorm, norm_nonneg u]
 926
 927/-!
 928## Compactness + identification as explicit hypotheses
 929-/
 930
 931/-- Hypothesis: uniform-in-`N` bounds for a *family* of Galerkin trajectories `uN`.
 932
 933In a real proof this would come from:
 934- discrete energy/enstrophy inequalities,
 935- CPM coercivity/dispersion bounds, and
 936- compactness tools (Aubin–Lions / Banach–Alaoglu / etc.).
 937-/
 938structure UniformBoundsHypothesis where
 939  /-- Discrete Galerkin trajectories at each truncation level `N`. -/
 940  uN : (N : ℕ) → ℝ → GalerkinState N
 941  /-- A global (in time, and uniform in `N`) bound. -/
 942  B : ℝ
 943  B_nonneg : 0 ≤ B
 944  /-- Uniform bound: for all `N` and all `t ≥ 0`, `‖uN N t‖ ≤ B`. -/
 945  bound : ∀ N : ℕ, ∀ t ≥ 0, ‖uN N t‖ ≤ B
 946
 947/-- Build `UniformBoundsHypothesis` from the *viscous* Galerkin energy estimate, provided we have
 948an initial uniform bound `‖uN N 0‖ ≤ B` across all truncation levels.
 949-/
 950noncomputable def UniformBoundsHypothesis.ofViscous
 951    (ν : ℝ) (hν : 0 ≤ ν)
 952    (Bop : (N : ℕ) → ConvectionOp N)
 953    (HB : ∀ N : ℕ, EnergySkewHypothesis (Bop N))
 954    (u : (N : ℕ) → ℝ → GalerkinState N)
 955    (hu : ∀ N : ℕ, ∀ t : ℝ, HasDerivAt (u N) (galerkinNSRHS ν (Bop N) ((u N) t)) t)
 956    (B : ℝ) (B_nonneg : 0 ≤ B)
 957    (h0 : ∀ N : ℕ, ‖u N 0‖ ≤ B) :
 958    UniformBoundsHypothesis :=
 959  { uN := u
 960    B := B
 961    B_nonneg := B_nonneg
 962    bound := by
 963      intro N t ht
 964      have hNt : ‖u N t‖ ≤ ‖u N 0‖ :=
 965        viscous_norm_bound_from_initial (N := N) ν hν (Bop N) (HB N) (u N) (hu N) t ht
 966      exact le_trans hNt (h0 N) }
 967
 968/-- The (Galerkin) nonlinear forcing family in full Fourier variables:
 969
 970`F_N(N,s) := extendByZero (Bop N (uN N s) (uN N s))`. -/
 971noncomputable def galerkinForcing (H : UniformBoundsHypothesis) (Bop : (N : ℕ) → ConvectionOp N) :
 972    ℕ → ℝ → FourierState2D :=
 973  fun N s => extendByZero ((Bop N) (H.uN N s) (H.uN N s))
 974
 975/-- A more concrete, user-facing hypothesis for dominated convergence of the *Galerkin forcing*
 976`galerkinForcing H Bop`, expressed directly in terms of:
 977
 978- measurability of the forcing modes,
 979- an integrable dominating function on each time interval `[0,t]`, and
 980- pointwise convergence of forcing modes.
 981
 982This packages exactly what you need to build `ForcingDominatedConvergenceAt` for the Galerkin forcing. -/
 983structure GalerkinForcingDominatedConvergenceHypothesis
 984    (H : UniformBoundsHypothesis) (Bop : (N : ℕ) → ConvectionOp N) where
 985  /-- Limiting forcing in full Fourier variables. -/
 986  F : ℝ → FourierState2D
 987  /-- Dominating `L¹` bound, allowed to depend on `(t,k)`. -/
 988  bound : ℝ → Mode2 → ℝ → ℝ
 989  /-- Integrability of the dominating bound on each interval `0..t` (for `t ≥ 0`). -/
 990  bound_integrable : ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2,
 991    IntervalIntegrable (bound t k) MeasureTheory.volume (0 : ℝ) t
 992  /-- Strong measurability (in `s`) of each forcing mode on `0..t` (for `t ≥ 0`). -/
 993  meas : ∀ N : ℕ, ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2,
 994    MeasureTheory.AEStronglyMeasurable
 995      (fun s : ℝ => (galerkinForcing H Bop N s) k)
 996      (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t))
 997  /-- Pointwise domination by the integrable bound on `0..t` (for `t ≥ 0`). -/
 998  dom : ∀ N : ℕ, ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2, ∀ s : ℝ, s ∈ Set.uIoc (0 : ℝ) t →
 999    ‖(galerkinForcing H Bop N s) k‖ ≤ bound t k s
1000  /-- Pointwise convergence of forcing modes on `0..t` (for `t ≥ 0`). -/
1001  lim : ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2, ∀ s : ℝ, s ∈ Set.uIoc (0 : ℝ) t →
1002    Tendsto (fun N : ℕ => (galerkinForcing H Bop N s) k) atTop (𝓝 ((F s) k))
1003
1004namespace GalerkinForcingDominatedConvergenceHypothesis
1005
1006/-- Build `ForcingDominatedConvergenceAt` for the Galerkin forcing from the more concrete hypothesis
1007`GalerkinForcingDominatedConvergenceHypothesis`. -/
1008noncomputable def forcingDCTAt
1009    {H : UniformBoundsHypothesis} {Bop : (N : ℕ) → ConvectionOp N}
1010    (hF : GalerkinForcingDominatedConvergenceHypothesis H Bop)
1011    (t : ℝ) (ht : 0 ≤ t) (k : Mode2) :
1012    ForcingDominatedConvergenceAt (F_N := galerkinForcing H Bop) (F := hF.F) t k :=
1013  ForcingDominatedConvergenceAt.of_forall (F_N := galerkinForcing H Bop) (F := hF.F) (t := t) (k := k)
1014    (bound := hF.bound t k)
1015    (h_meas := by intro N; exact hF.meas N t ht k)
1016    (h_bound := by intro N s hs; exact hF.dom N t ht k s hs)
1017    (bound_integrable := hF.bound_integrable t ht k)
1018    (h_lim := by intro s hs; exact hF.lim t ht k s hs)
1019
1020/-- If each Galerkin trajectory `uN N` is continuous and each `Bop N` is continuous as a map
1021`(u,v) ↦ Bop N u v`, then each forcing mode `s ↦ (galerkinForcing H Bop N s) k` is continuous (hence
1022AE-strongly measurable on any finite interval). -/
1023theorem aestronglyMeasurable_galerkinForcing_mode_of_continuous
1024    (H : UniformBoundsHypothesis) (Bop : (N : ℕ) → ConvectionOp N)
1025    (hBcont : ∀ N : ℕ,
1026      Continuous (fun p : GalerkinState N × GalerkinState N => (Bop N) p.1 p.2))
1027    (hucont : ∀ N : ℕ, Continuous (H.uN N))
1028    (N : ℕ) (t : ℝ) (_ht : 0 ≤ t) (k : Mode2) :
1029    MeasureTheory.AEStronglyMeasurable
1030      (fun s : ℝ => (galerkinForcing H Bop N s) k)
1031      (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t)) := by
1032  -- Continuity of `s ↦ Bop N (uN N s) (uN N s)`.
1033  have hpair : Continuous fun s : ℝ => (H.uN N s, H.uN N s) := by
1034    -- build continuity from `ContinuousAt.prodMk`
1035    refine continuous_iff_continuousAt.2 ?_
1036    intro s
1037    simpa using
1038      (ContinuousAt.prodMk (x := s) ((hucont N).continuousAt) ((hucont N).continuousAt))
1039  have hB : Continuous fun s : ℝ => (Bop N) (H.uN N s) (H.uN N s) :=
1040    (hBcont N).comp hpair
1041  -- Apply the continuous linear map `u ↦ (extendByZero u) k`.
1042  let L : GalerkinState N →L[ℝ] VelCoeff :=
1043    (ContinuousLinearMap.proj k).comp (extendByZeroCLM (N := N))
1044  have hL : Continuous fun u : GalerkinState N => L u := L.continuous
1045  have hcoeff : Continuous fun s : ℝ => (galerkinForcing H Bop N s) k := by
1046    -- unfold back to `extendByZero` + evaluation at `k`
1047    simpa [galerkinForcing, L, extendByZeroCLM] using hL.comp hB
1048  exact hcoeff.aestronglyMeasurable
1049
1050/-- Sufficient condition: a **uniform norm bound** on the convection term plus the uniform-in-time
1051bound `‖uN‖ ≤ B` yields an integrable domination for every forcing mode.
1052
1053This discharges the `bound_integrable` and `dom` fields of `GalerkinForcingDominatedConvergenceHypothesis`,
1054leaving only measurability + pointwise forcing convergence as hypotheses. -/
1055noncomputable def of_convectionNormBound
1056    (H : UniformBoundsHypothesis) (Bop : (N : ℕ) → ConvectionOp N)
1057    (C : ℝ) (hC : 0 ≤ C)
1058    (hB : ∀ N : ℕ, ∀ u : GalerkinState N, ‖(Bop N u u)‖ ≤ C * ‖u‖ ^ 2)
1059    (F : ℝ → FourierState2D)
1060    (meas : ∀ N : ℕ, ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2,
1061      MeasureTheory.AEStronglyMeasurable
1062        (fun s : ℝ => (galerkinForcing H Bop N s) k)
1063        (MeasureTheory.volume.restrict (Set.uIoc (0 : ℝ) t)))
1064    (lim : ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2, ∀ s : ℝ, s ∈ Set.uIoc (0 : ℝ) t →
1065      Tendsto (fun N : ℕ => (galerkinForcing H Bop N s) k) atTop (𝓝 ((F s) k))) :
1066    GalerkinForcingDominatedConvergenceHypothesis H Bop :=
1067by
1068  classical
1069  refine
1070    { F := F
1071      bound := fun _t _k _s => C * H.B ^ 2
1072      bound_integrable := by
1073        intro t _ht k
1074        -- constant function is interval integrable on any finite interval
1075        simp
1076      meas := meas
1077      dom := ?_
1078      lim := lim }
1079  intro N t ht k s hs
1080  -- from `s ∈ uIoc 0 t` and `0 ≤ t`, we have `0 < s` hence `0 ≤ s`.
1081  have hs' : 0 < s ∧ s ≤ t := by
1082    have hs'' : min (0 : ℝ) t < s ∧ s ≤ max (0 : ℝ) t := by
1083      simpa [Set.uIoc, Set.mem_Ioc] using hs
1084    simpa [min_eq_left ht, max_eq_right ht] using hs''
1085  have hs0 : 0 ≤ s := le_of_lt hs'.1
1086
1087  -- uniform bound on `uN`
1088  have hu : ‖H.uN N s‖ ≤ H.B := H.bound N s hs0
1089
1090  -- square the bound: `‖u‖^2 ≤ B^2`
1091  have hu_sq : ‖H.uN N s‖ ^ 2 ≤ H.B ^ 2 := by
1092    have : ‖H.uN N s‖ * ‖H.uN N s‖ ≤ H.B * H.B :=
1093      mul_le_mul hu hu (norm_nonneg _) H.B_nonneg
1094    simpa [pow_two] using this
1095
1096  -- control the Galerkin nonlinearity in norm, then pass to a single Fourier coefficient
1097  have hBuu : ‖(Bop N (H.uN N s) (H.uN N s))‖ ≤ C * ‖H.uN N s‖ ^ 2 :=
1098    hB N (H.uN N s)
1099  have hBuu' : ‖(Bop N (H.uN N s) (H.uN N s))‖ ≤ C * H.B ^ 2 :=
1100    le_trans hBuu (mul_le_mul_of_nonneg_left hu_sq hC)
1101  have hcoeff :
1102      ‖(galerkinForcing H Bop N s) k‖ ≤ C * H.B ^ 2 := by
1103    have h1 :
1104        ‖(galerkinForcing H Bop N s) k‖ ≤ ‖(Bop N (H.uN N s) (H.uN N s))‖ := by
1105      simpa [galerkinForcing] using
1106        (norm_extendByZero_le (u := (Bop N (H.uN N s) (H.uN N s))) (k := k))
1107    exact le_trans h1 hBuu'
1108  simpa using hcoeff
1109
1110/-- Combine `of_convectionNormBound` with continuity assumptions to discharge measurability of the
1111Galerkin forcing modes automatically. -/
1112noncomputable def of_convectionNormBound_of_continuous
1113    (H : UniformBoundsHypothesis) (Bop : (N : ℕ) → ConvectionOp N)
1114    (C : ℝ) (hC : 0 ≤ C)
1115    (hB : ∀ N : ℕ, ∀ u : GalerkinState N, ‖(Bop N u u)‖ ≤ C * ‖u‖ ^ 2)
1116    (hBcont : ∀ N : ℕ,
1117      Continuous (fun p : GalerkinState N × GalerkinState N => (Bop N) p.1 p.2))
1118    (hucont : ∀ N : ℕ, Continuous (H.uN N))
1119    (F : ℝ → FourierState2D)
1120    (lim : ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2, ∀ s : ℝ, s ∈ Set.uIoc (0 : ℝ) t →
1121      Tendsto (fun N : ℕ => (galerkinForcing H Bop N s) k) atTop (𝓝 ((F s) k))) :
1122    GalerkinForcingDominatedConvergenceHypothesis H Bop :=
1123  of_convectionNormBound (H := H) (Bop := Bop) (C := C) hC (hB := hB) (F := F)
1124    (meas := by
1125      intro N t ht k
1126      exact aestronglyMeasurable_galerkinForcing_mode_of_continuous (H := H) (Bop := Bop)
1127        (hBcont := hBcont) (hucont := hucont) N t ht k)
1128    (lim := lim)
1129
1130end GalerkinForcingDominatedConvergenceHypothesis
1131
1132/-- Hypothesis: existence of a limit Fourier trajectory and convergence from the approximants. -/
1133structure ConvergenceHypothesis (H : UniformBoundsHypothesis) where
1134  /-- Candidate limit (time → full Fourier coefficients). -/
1135  u : ℝ → FourierState2D
1136  /-- Pointwise (mode-by-mode) convergence of the zero-extended Galerkin coefficients. -/
1137  converges : ∀ t : ℝ, ∀ k : Mode2,
1138    Tendsto (fun N : ℕ => (extendByZero (H.uN N t)) k) atTop (𝓝 ((u t) k))
1139
1140namespace ConvergenceHypothesis
1141
1142/-- Derived fact: if the approximants are uniformly bounded in the Galerkin norm for `t ≥ 0`,
1143then the limit coefficients inherit the same bound (by closedness of `closedBall`). -/
1144theorem coeff_bound_of_uniformBounds {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) :
1145    ∀ t ≥ 0, ∀ k : Mode2, ‖(HC.u t) k‖ ≤ H.B := by
1146  intro t ht k
1147  -- Put every approximant coefficient inside the closed ball of radius `B`.
1148  have hmem :
1149      ∀ N : ℕ, (extendByZero (H.uN N t) k) ∈ Metric.closedBall (0 : VelCoeff) H.B := by
1150    intro N
1151    have h1 : ‖(extendByZero (H.uN N t)) k‖ ≤ ‖H.uN N t‖ :=
1152      norm_extendByZero_le (u := H.uN N t) (k := k)
1153    have h2 : ‖H.uN N t‖ ≤ H.B := H.bound N t ht
1154    have h3 : ‖(extendByZero (H.uN N t)) k‖ ≤ H.B := le_trans h1 h2
1155    -- `Metric.mem_closedBall` is `dist ≤ radius`, and `dist x 0 = ‖x‖`.
1156    simpa [Metric.mem_closedBall, dist_zero_right] using h3
1157
1158  have hmem_event :
1159      (∀ᶠ N : ℕ in atTop, (extendByZero (H.uN N t) k) ∈ Metric.closedBall (0 : VelCoeff) H.B) :=
1160    Filter.Eventually.of_forall hmem
1161
1162  have hlim_mem :
1163      (HC.u t) k ∈ Metric.closedBall (0 : VelCoeff) H.B :=
1164    IsClosed.mem_of_tendsto (b := atTop) Metric.isClosed_closedBall (HC.converges t k) hmem_event
1165
1166  have : dist ((HC.u t) k) (0 : VelCoeff) ≤ H.B :=
1167    (Metric.mem_closedBall).1 hlim_mem
1168
1169  simpa [dist_zero_right] using this
1170
1171/-- If the approximants satisfy the (Fourier) divergence constraint at a fixed `t,k`, then so does
1172the limit coefficient (by continuity + uniqueness of limits in `ℝ`). -/
1173theorem divConstraint_eq_zero_of_forall {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1174    (t : ℝ) (k : Mode2)
1175    (hDF : ∀ N : ℕ, divConstraint k ((extendByZero (H.uN N t)) k) = 0) :
1176    divConstraint k ((HC.u t) k) = 0 := by
1177  -- Push convergence through the continuous map `divConstraint k`.
1178  have hT :
1179      Tendsto (fun N : ℕ => divConstraint k ((extendByZero (H.uN N t)) k)) atTop
1180        (𝓝 (divConstraint k ((HC.u t) k))) := by
1181    have hcont : Continuous (fun v : VelCoeff => divConstraint k v) := divConstraint_continuous k
1182    have hcontT :
1183        Tendsto (fun v : VelCoeff => divConstraint k v) (𝓝 ((HC.u t) k))
1184          (𝓝 (divConstraint k ((HC.u t) k))) :=
1185      hcont.tendsto ((HC.u t) k)
1186    exact hcontT.comp (HC.converges t k)
1187
1188  -- The sequence is constantly 0 by assumption.
1189  have heq : (fun N : ℕ => divConstraint k ((extendByZero (H.uN N t)) k)) = fun _ : ℕ => (0 : ℝ) := by
1190    funext N
1191    simpa using (hDF N)
1192
1193  have hT0 : Tendsto (fun _ : ℕ => (0 : ℝ)) atTop (𝓝 (divConstraint k ((HC.u t) k))) := by
1194    simpa [heq] using hT
1195  have hconst : Tendsto (fun _ : ℕ => (0 : ℝ)) atTop (𝓝 (0 : ℝ)) := tendsto_const_nhds
1196
1197  exact tendsto_nhds_unique hT0 hconst
1198
1199/-- Divergence-free passes to the limit under modewise convergence, assuming each approximant is
1200divergence-free (in the Fourier-side sense) at every time. -/
1201theorem divFree_of_forall {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1202    (hDF : ∀ N : ℕ, ∀ t : ℝ, ∀ k : Mode2, divConstraint k ((extendByZero (H.uN N t)) k) = 0) :
1203    IsDivergenceFreeTraj HC.u := by
1204  intro t k
1205  exact divConstraint_eq_zero_of_forall (HC := HC) (t := t) (k := k) (hDF := fun N => hDF N t k)
1206
1207/-- Mild Stokes/heat identity passes to the limit under modewise convergence,
1208assuming it holds for every approximant (modewise, for `t ≥ 0`). -/
1209theorem stokesMild_of_forall {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1210    (hMild :
1211      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1212        (extendByZero (H.uN N t) k) = (heatFactor ν t k) • (extendByZero (H.uN N 0) k)) :
1213    IsStokesMildTraj ν HC.u := by
1214  intro t ht k
1215  -- convergence at time t and at time 0
1216  have hconv_t : Tendsto (fun N : ℕ => extendByZero (H.uN N t) k) atTop (nhds ((HC.u t) k)) :=
1217    HC.converges t k
1218  have hconv_0 : Tendsto (fun N : ℕ => extendByZero (H.uN N 0) k) atTop (nhds ((HC.u 0) k)) :=
1219    HC.converges 0 k
1220  -- push convergence at time 0 through the continuous map `v ↦ heatFactor • v`
1221  have hsmul :
1222      Tendsto (fun N : ℕ => (heatFactor ν t k) • (extendByZero (H.uN N 0) k)) atTop
1223        (nhds ((heatFactor ν t k) • ((HC.u 0) k))) := by
1224    have hcont : Continuous fun v : VelCoeff => (heatFactor ν t k) • v := continuous_const_smul _
1225    exact (hcont.tendsto ((HC.u 0) k)).comp hconv_0
1226  -- but the two sequences are equal for all N (by hypothesis), hence have the same limit
1227  have hEq :
1228      (fun N : ℕ => extendByZero (H.uN N t) k)
1229        =ᶠ[atTop] (fun N : ℕ => (heatFactor ν t k) • (extendByZero (H.uN N 0) k)) := by
1230    refine Filter.Eventually.of_forall ?_
1231    intro N
1232    exact hMild N t ht k
1233  -- uniqueness of limits in a T2 space
1234  have : (HC.u t) k = (heatFactor ν t k) • ((HC.u 0) k) :=
1235    tendsto_nhds_unique_of_eventuallyEq hconv_t hsmul hEq
1236  simpa using this
1237
1238/-- Duhamel-remainder identity passes to the limit under modewise convergence,
1239assuming it holds for every approximant with remainders `D_N` that converge modewise. -/
1240theorem nsDuhamel_of_forall {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1241    (D_N : ℕ → ℝ → FourierState2D) (D : ℝ → FourierState2D)
1242    (hD : ∀ t : ℝ, ∀ k : Mode2,
1243      Tendsto (fun N : ℕ => (D_N N t) k) atTop (nhds ((D t) k)))
1244    (hId :
1245      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1246        (extendByZero (H.uN N t) k) =
1247          (heatFactor ν t k) • (extendByZero (H.uN N 0) k) + (D_N N t) k) :
1248    IsNSDuhamelTraj ν D HC.u := by
1249  intro t ht k
1250  -- convergence of the main trajectory at time `t` and at time `0`
1251  have hconv_t : Tendsto (fun N : ℕ => extendByZero (H.uN N t) k) atTop (nhds ((HC.u t) k)) :=
1252    HC.converges t k
1253  have hconv_0 : Tendsto (fun N : ℕ => extendByZero (H.uN N 0) k) atTop (nhds ((HC.u 0) k)) :=
1254    HC.converges 0 k
1255  -- convergence of the remainder term
1256  have hconv_D : Tendsto (fun N : ℕ => (D_N N t) k) atTop (nhds ((D t) k)) :=
1257    hD t k
1258  -- push convergence at time 0 through the continuous map `v ↦ heatFactor • v`
1259  have hsmul :
1260      Tendsto (fun N : ℕ => (heatFactor ν t k) • (extendByZero (H.uN N 0) k)) atTop
1261        (nhds ((heatFactor ν t k) • ((HC.u 0) k))) := by
1262    have hcont : Continuous fun v : VelCoeff => (heatFactor ν t k) • v := continuous_const_smul _
1263    exact (hcont.tendsto ((HC.u 0) k)).comp hconv_0
1264  -- combine the smul part and the remainder part
1265  have hsum :
1266      Tendsto (fun N : ℕ =>
1267        (heatFactor ν t k) • (extendByZero (H.uN N 0) k) + (D_N N t) k) atTop
1268          (nhds ((heatFactor ν t k) • ((HC.u 0) k) + (D t) k)) :=
1269    hsmul.add hconv_D
1270  -- the identity holds for every N, hence the two sequences are eventually equal
1271  have hEq :
1272      (fun N : ℕ => extendByZero (H.uN N t) k)
1273        =ᶠ[atTop] (fun N : ℕ => (heatFactor ν t k) • (extendByZero (H.uN N 0) k) + (D_N N t) k) := by
1274    refine Filter.Eventually.of_forall ?_
1275    intro N
1276    exact hId N t ht k
1277  -- uniqueness of limits in a T2 space
1278  have :
1279      (HC.u t) k = (heatFactor ν t k) • ((HC.u 0) k) + (D t) k :=
1280    tendsto_nhds_unique_of_eventuallyEq hconv_t hsum hEq
1281  simpa [IsNSDuhamelTraj] using this
1282
1283/-- Convenience wrapper around `nsDuhamel_of_forall` when the remainder terms are defined as Duhamel
1284kernel integrals and convergence is supplied via dominated convergence (hypothesis-level). -/
1285theorem nsDuhamel_of_forall_kernelIntegral {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1286    (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D)
1287    (hDC : ∀ t : ℝ, ∀ k : Mode2, DuhamelKernelDominatedConvergenceAt ν F_N F t k)
1288    (hId :
1289      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1290        (extendByZero (H.uN N t) k) =
1291          (heatFactor ν t k) • (extendByZero (H.uN N 0) k)
1292            + (duhamelKernelIntegral ν (F_N N) t) k) :
1293    IsNSDuhamelTraj ν (duhamelKernelIntegral ν F) HC.u := by
1294  have hD :
1295      ∀ t : ℝ, ∀ k : Mode2,
1296        Tendsto (fun N : ℕ => (duhamelKernelIntegral ν (F_N N) t) k) atTop
1297          (nhds (((duhamelKernelIntegral ν F) t) k)) := by
1298    intro t k
1299    exact tendsto_duhamelKernelIntegral_of_dominated_convergence (ν := ν) (F_N := F_N) (F := F) (t := t) (k := k)
1300      (hDC t k)
1301  exact
1302    nsDuhamel_of_forall (HC := HC) (ν := ν)
1303      (D_N := fun N => duhamelKernelIntegral ν (F_N N)) (D := duhamelKernelIntegral ν F)
1304      (hD := hD) (hId := hId)
1305
1306/-- Variant of `nsDuhamel_of_forall_kernelIntegral` that assumes dominated convergence only for the
1307*forcing* (not the kernel integrand), plus `0 ≤ ν` and `t ≥ 0` to control the kernel factor. -/
1308theorem nsDuhamel_of_forall_kernelIntegral_of_forcing {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1309    (ν : ℝ) (hν : 0 ≤ ν)
1310    (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D)
1311    (hF :
1312      ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2, ForcingDominatedConvergenceAt (F_N := F_N) (F := F) t k)
1313    (hId :
1314      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1315        (extendByZero (H.uN N t) k) =
1316          (heatFactor ν t k) • (extendByZero (H.uN N 0) k)
1317            + (duhamelKernelIntegral ν (F_N N) t) k) :
1318    IsNSDuhamelTraj ν (duhamelKernelIntegral ν F) HC.u := by
1319  intro t ht k
1320  -- convergence of the main trajectory at time `t` and at time `0`
1321  have hconv_t : Tendsto (fun N : ℕ => extendByZero (H.uN N t) k) atTop (nhds ((HC.u t) k)) :=
1322    HC.converges t k
1323  have hconv_0 : Tendsto (fun N : ℕ => extendByZero (H.uN N 0) k) atTop (nhds ((HC.u 0) k)) :=
1324    HC.converges 0 k
1325  -- convergence of the kernel-integral remainder at time `t` (from forcing-level DCT)
1326  have hconv_D :
1327      Tendsto (fun N : ℕ => (duhamelKernelIntegral ν (F_N N) t) k) atTop
1328        (nhds (((duhamelKernelIntegral ν F) t) k)) := by
1329    have hDC : DuhamelKernelDominatedConvergenceAt ν F_N F t k :=
1330      duhamelKernelDominatedConvergenceAt_of_forcing (ν := ν) (t := t) hν ht (hF t ht k)
1331    exact
1332      tendsto_duhamelKernelIntegral_of_dominated_convergence (ν := ν) (F_N := F_N) (F := F) (t := t) (k := k)
1333        hDC
1334  -- push convergence at time 0 through the continuous map `v ↦ heatFactor • v`
1335  have hsmul :
1336      Tendsto (fun N : ℕ => (heatFactor ν t k) • (extendByZero (H.uN N 0) k)) atTop
1337        (nhds ((heatFactor ν t k) • ((HC.u 0) k))) := by
1338    have hcont : Continuous fun v : VelCoeff => (heatFactor ν t k) • v := continuous_const_smul _
1339    exact (hcont.tendsto ((HC.u 0) k)).comp hconv_0
1340  -- combine the smul part and the remainder part
1341  have hsum :
1342      Tendsto (fun N : ℕ =>
1343        (heatFactor ν t k) • (extendByZero (H.uN N 0) k) + (duhamelKernelIntegral ν (F_N N) t) k) atTop
1344          (nhds ((heatFactor ν t k) • ((HC.u 0) k) + ((duhamelKernelIntegral ν F) t) k)) :=
1345    hsmul.add hconv_D
1346  -- the identity holds for every N, hence the two sequences are eventually equal
1347  have hEq :
1348      (fun N : ℕ => extendByZero (H.uN N t) k)
1349        =ᶠ[atTop]
1350          (fun N : ℕ =>
1351            (heatFactor ν t k) • (extendByZero (H.uN N 0) k) + (duhamelKernelIntegral ν (F_N N) t) k) := by
1352    refine Filter.Eventually.of_forall ?_
1353    intro N
1354    exact hId N t ht k
1355  -- uniqueness of limits in a T2 space
1356  have :
1357      (HC.u t) k = (heatFactor ν t k) • ((HC.u 0) k) + ((duhamelKernelIntegral ν F) t) k :=
1358    tendsto_nhds_unique_of_eventuallyEq hconv_t hsum hEq
1359  simpa [IsNSDuhamelTraj] using this
1360
1361end ConvergenceHypothesis
1362
1363/-- Convenience constructor: if each coefficient sequence is *eventually equal* to the corresponding
1364limit coefficient, then it tends to that limit. -/
1365noncomputable def ConvergenceHypothesis.ofEventuallyEq
1366    (H : UniformBoundsHypothesis)
1367    (u : ℝ → FourierState2D)
1368    (heq :
1369      ∀ t : ℝ, ∀ k : Mode2,
1370        (fun N : ℕ => (extendByZero (H.uN N t)) k) =ᶠ[atTop] (fun _ : ℕ => (u t) k)) :
1371    ConvergenceHypothesis H :=
1372  { u := u
1373    converges := by
1374      intro t k
1375      have hconst : Tendsto (fun _ : ℕ => (u t) k) atTop (𝓝 ((u t) k)) :=
1376        tendsto_const_nhds
1377      exact (tendsto_congr' (heq t k)).2 hconst }
1378
1379/-- Hypothesis: the limit object satisfies the intended PDE identity (kept abstract here). -/
1380structure IdentificationHypothesis {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) where
1381  /-- A (later: concrete) solution concept for 2D Navier–Stokes on the torus. -/
1382  IsSolution : (ℝ → FourierState2D) → Prop
1383  /-- Proof that the limit trajectory satisfies the chosen solution concept. -/
1384  isSolution : IsSolution HC.u
1385
1386namespace IdentificationHypothesis
1387
1388/-- Trivial identification constructor: choose `IsSolution := True`. -/
1389def trivial {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) :
1390    IdentificationHypothesis HC :=
1391  { IsSolution := fun _ => True
1392    isSolution := by trivial }
1393
1394/-- Concrete (but still minimal) identification: define `IsSolution u` to mean the limit coefficients
1395are uniformly bounded by the Galerkin bound `H.B` for `t ≥ 0`.
1396
1397This is **provable** from `UniformBoundsHypothesis` + modewise convergence (no extra analytic input),
1398via `ConvergenceHypothesis.coeff_bound_of_uniformBounds`.
1399-/
1400def coeffBound {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) :
1401    IdentificationHypothesis HC :=
1402  { IsSolution := fun u => ∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B
1403    isSolution := by
1404      intro t ht k
1405      simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k) }
1406
1407/-- Identification constructor: coefficient bound + divergence-free (Fourier-side).
1408
1409The coefficient bound part is proved from `UniformBoundsHypothesis` + convergence.
1410The divergence-free part is proved from the extra assumption that *each approximant* is divergence-free.
1411-/
1412def divFreeCoeffBound {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1413    (hDF : ∀ N : ℕ, ∀ t : ℝ, ∀ k : Mode2, divConstraint k ((extendByZero (H.uN N t)) k) = 0) :
1414    IdentificationHypothesis HC :=
1415  { IsSolution := fun u =>
1416      (∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B) ∧ IsDivergenceFreeTraj u
1417    isSolution := by
1418      refine ⟨?_, ?_⟩
1419      · intro t ht k
1420        simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k)
1421      · intro t k
1422        exact ConvergenceHypothesis.divConstraint_eq_zero_of_forall (HC := HC) (t := t) (k := k)
1423          (hDF := fun N => hDF N t k) }
1424
1425/-- Identification constructor: coefficient bound + (linear) Stokes/heat mild identity.
1426
1427The bound part is proved from `UniformBoundsHypothesis` + convergence.
1428The mild Stokes identity is proved from the extra assumption that it holds for every approximant. -/
1429def stokesMildCoeffBound {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1430    (hMild :
1431      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1432        (extendByZero (H.uN N t) k) = (heatFactor ν t k) • (extendByZero (H.uN N 0) k)) :
1433    IdentificationHypothesis HC :=
1434  { IsSolution := fun u =>
1435      (∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B) ∧ IsStokesMildTraj ν u
1436    isSolution := by
1437      refine ⟨?_, ?_⟩
1438      · intro t ht k
1439        simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k)
1440      · exact ConvergenceHypothesis.stokesMild_of_forall (HC := HC) (ν := ν) hMild }
1441
1442/-- Identification constructor: coefficient bound + a first nonlinear (Duhamel-style) remainder identity.
1443
1444The coefficient bound part is proved from `UniformBoundsHypothesis` + convergence.
1445The Duhamel-remainder identity is proved from the extra assumptions:
1446
1447- each approximant satisfies `extendByZero uN(t,k) = heatFactor • extendByZero uN(0,k) + D_N(t,k)`, and
1448- `D_N(t,k) → D(t,k)` modewise.
1449
1450In later milestones, `D_N` will be instantiated as an actual time-integrated nonlinear forcing term. -/
1451def nsDuhamelCoeffBound {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1452    (D_N : ℕ → ℝ → FourierState2D) (D : ℝ → FourierState2D)
1453    (hD : ∀ t : ℝ, ∀ k : Mode2,
1454      Tendsto (fun N : ℕ => (D_N N t) k) atTop (nhds ((D t) k)))
1455    (hId :
1456      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1457        (extendByZero (H.uN N t) k) =
1458          (heatFactor ν t k) • (extendByZero (H.uN N 0) k) + (D_N N t) k) :
1459    IdentificationHypothesis HC :=
1460  { IsSolution := fun u =>
1461      (∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B) ∧ IsNSDuhamelTraj ν D u
1462    isSolution := by
1463      refine ⟨?_, ?_⟩
1464      · intro t ht k
1465        simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k)
1466      · exact ConvergenceHypothesis.nsDuhamel_of_forall (HC := HC) (ν := ν) (D_N := D_N) (D := D) hD hId }
1467
1468/-- Identification constructor: coefficient bound + Duhamel remainder identity where the remainder is
1469defined as a **kernel integral** of a forcing term, and convergence of the kernel integrals is
1470packaged via `DuhamelKernelDominatedConvergenceAt`. -/
1471def nsDuhamelCoeffBound_kernelIntegral {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1472    (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D)
1473    (hDC : ∀ t : ℝ, ∀ k : Mode2, DuhamelKernelDominatedConvergenceAt ν F_N F t k)
1474    (hId :
1475      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1476        (extendByZero (H.uN N t) k) =
1477          (heatFactor ν t k) • (extendByZero (H.uN N 0) k)
1478            + (duhamelKernelIntegral ν (F_N N) t) k) :
1479    IdentificationHypothesis HC :=
1480  { IsSolution := fun u =>
1481      (∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B) ∧ IsNSDuhamelTraj ν (duhamelKernelIntegral ν F) u
1482    isSolution := by
1483      refine ⟨?_, ?_⟩
1484      · intro t ht k
1485        simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k)
1486      · exact
1487          ConvergenceHypothesis.nsDuhamel_of_forall_kernelIntegral (HC := HC) (ν := ν)
1488            (F_N := F_N) (F := F) hDC hId }
1489
1490/-- Same as `nsDuhamelCoeffBound_kernelIntegral`, but assumes dominated convergence at the **forcing**
1491level (not the kernel integrand), plus `0 ≤ ν`. -/
1492def nsDuhamelCoeffBound_kernelIntegral_of_forcing {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1493    (ν : ℝ) (hν : 0 ≤ ν)
1494    (F_N : ℕ → ℝ → FourierState2D) (F : ℝ → FourierState2D)
1495    (hF :
1496      ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2, ForcingDominatedConvergenceAt (F_N := F_N) (F := F) t k)
1497    (hId :
1498      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1499        (extendByZero (H.uN N t) k) =
1500          (heatFactor ν t k) • (extendByZero (H.uN N 0) k)
1501            + (duhamelKernelIntegral ν (F_N N) t) k) :
1502    IdentificationHypothesis HC :=
1503  { IsSolution := fun u =>
1504      (∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B) ∧ IsNSDuhamelTraj ν (duhamelKernelIntegral ν F) u
1505    isSolution := by
1506      refine ⟨?_, ?_⟩
1507      · intro t ht k
1508        simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k)
1509      · exact
1510          ConvergenceHypothesis.nsDuhamel_of_forall_kernelIntegral_of_forcing (HC := HC)
1511            (ν := ν) hν (F_N := F_N) (F := F) hF hId }
1512
1513/-- Identification constructor: a specialization of `nsDuhamelCoeffBound_kernelIntegral` where the
1514forcing family is the **actual Galerkin nonlinearity** `extendByZero (B(u_N,u_N))`. The Duhamel
1515identity for each approximant is discharged by `galerkin_duhamelKernel_identity`; the remaining
1516analytic ingredient is the dominated-convergence hypothesis `hDC`. -/
1517def nsDuhamelCoeffBound_galerkinKernel {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H) (ν : ℝ)
1518    (Bop : (N : ℕ) → ConvectionOp N)
1519    (hu :
1520      ∀ N : ℕ, ∀ s : ℝ,
1521        HasDerivAt (H.uN N) (galerkinNSRHS (N := N) ν (Bop N) (H.uN N s)) s)
1522    (hint :
1523      ∀ N : ℕ, ∀ t : ℝ, ∀ k : Mode2,
1524        IntervalIntegrable (fun s : ℝ =>
1525          (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero ((Bop N) (H.uN N s) (H.uN N s)) k))
1526          MeasureTheory.volume 0 t)
1527    (F : ℝ → FourierState2D)
1528    (hDC :
1529      ∀ t : ℝ, ∀ k : Mode2,
1530        DuhamelKernelDominatedConvergenceAt ν
1531          (fun N : ℕ => fun s : ℝ => extendByZero ((Bop N) (H.uN N s) (H.uN N s))) F t k) :
1532    IdentificationHypothesis HC := by
1533  -- Define the forcing family from the Galerkin nonlinearity.
1534  let F_N : ℕ → ℝ → FourierState2D :=
1535    fun N : ℕ => fun s : ℝ => extendByZero ((Bop N) (H.uN N s) (H.uN N s))
1536  -- The approximant Duhamel identity follows from the Galerkin kernel lemma.
1537  have hId' :
1538      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1539        (extendByZero (H.uN N t) k) =
1540          (heatFactor ν t k) • (extendByZero (H.uN N 0) k)
1541            + (duhamelKernelIntegral ν (F_N N) t) k := by
1542    intro N t _ht k
1543    -- `galerkin_duhamelKernel_identity` does not require `t ≥ 0`, but we only need it on that domain.
1544    simpa [F_N] using
1545      (galerkin_duhamelKernel_identity (N := N) (ν := ν) (B := Bop N) (u := H.uN N) (k := k) (t := t)
1546        (hu := fun s => hu N s) (hint := hint N t k))
1547  -- Reduce to the kernel-integral constructor.
1548  refine nsDuhamelCoeffBound_kernelIntegral (HC := HC) (ν := ν) (F_N := F_N) (F := F) ?_ hId'
1549  intro t k
1550  simpa [F_N] using hDC t k
1551
1552/-- Same as `nsDuhamelCoeffBound_galerkinKernel`, but assumes dominated convergence at the **forcing**
1553level (not the kernel integrand), plus `0 ≤ ν`. -/
1554def nsDuhamelCoeffBound_galerkinKernel_of_forcing {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1555    (ν : ℝ) (hν : 0 ≤ ν)
1556    (Bop : (N : ℕ) → ConvectionOp N)
1557    (hu :
1558      ∀ N : ℕ, ∀ s : ℝ,
1559        HasDerivAt (H.uN N) (galerkinNSRHS (N := N) ν (Bop N) (H.uN N s)) s)
1560    (hint :
1561      ∀ N : ℕ, ∀ t : ℝ, ∀ k : Mode2,
1562        IntervalIntegrable (fun s : ℝ =>
1563          (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero ((Bop N) (H.uN N s) (H.uN N s)) k))
1564          MeasureTheory.volume 0 t)
1565    (F : ℝ → FourierState2D)
1566    (hF :
1567      ∀ t : ℝ, t ≥ 0 → ∀ k : Mode2,
1568        ForcingDominatedConvergenceAt
1569          (F_N := fun N : ℕ => fun s : ℝ => extendByZero ((Bop N) (H.uN N s) (H.uN N s)))
1570          (F := F) t k) :
1571    IdentificationHypothesis HC := by
1572  -- Define the forcing family from the Galerkin nonlinearity.
1573  let F_N : ℕ → ℝ → FourierState2D :=
1574    fun N : ℕ => fun s : ℝ => extendByZero ((Bop N) (H.uN N s) (H.uN N s))
1575  -- The approximant Duhamel identity follows from the Galerkin kernel lemma.
1576  have hId' :
1577      ∀ N : ℕ, ∀ t ≥ 0, ∀ k : Mode2,
1578        (extendByZero (H.uN N t) k) =
1579          (heatFactor ν t k) • (extendByZero (H.uN N 0) k)
1580            + (duhamelKernelIntegral ν (F_N N) t) k := by
1581    intro N t _ht k
1582    simpa [F_N] using
1583      (galerkin_duhamelKernel_identity (N := N) (ν := ν) (B := Bop N) (u := H.uN N) (k := k) (t := t)
1584        (hu := fun s => hu N s) (hint := hint N t k))
1585  -- Reduce to the forcing-level kernel-integral constructor.
1586  refine
1587    nsDuhamelCoeffBound_kernelIntegral_of_forcing (HC := HC) (ν := ν) hν (F_N := F_N) (F := F) ?_ hId'
1588  intro t ht k
1589  simpa [F_N] using hF t ht k
1590
1591/-- Same as `nsDuhamelCoeffBound_galerkinKernel_of_forcing`, but takes the more concrete hypothesis
1592`GalerkinForcingDominatedConvergenceHypothesis` for the Galerkin forcing modes. -/
1593def nsDuhamelCoeffBound_galerkinKernel_of_forcingHyp {H : UniformBoundsHypothesis} (HC : ConvergenceHypothesis H)
1594    (ν : ℝ) (hν : 0 ≤ ν)
1595    (Bop : (N : ℕ) → ConvectionOp N)
1596    (hu :
1597      ∀ N : ℕ, ∀ s : ℝ,
1598        HasDerivAt (H.uN N) (galerkinNSRHS (N := N) ν (Bop N) (H.uN N s)) s)
1599    (hint :
1600      ∀ N : ℕ, ∀ t : ℝ, ∀ k : Mode2,
1601        IntervalIntegrable (fun s : ℝ =>
1602          (Real.exp (-(ν * (-kSq k)) * s)) • (extendByZero ((Bop N) (H.uN N s) (H.uN N s)) k))
1603          MeasureTheory.volume 0 t)
1604    (hForce : GalerkinForcingDominatedConvergenceHypothesis H Bop) :
1605    IdentificationHypothesis HC := by
1606  refine
1607    nsDuhamelCoeffBound_galerkinKernel_of_forcing (HC := HC) (ν := ν) hν (Bop := Bop) (hu := hu) (hint := hint)
1608      (F := hForce.F) ?_
1609  intro t ht k
1610  -- unpack the forcing-level DCT hypothesis for the Galerkin forcing modes
1611  simpa [galerkinForcing] using (GalerkinForcingDominatedConvergenceHypothesis.forcingDCTAt (hF := hForce) t ht k)
1612
1613end IdentificationHypothesis
1614
1615/-!
1616## The milestone theorem: “uniform bounds + convergence + identification ⇒ continuum solution”
1617
1618At this stage the theorem returns the packaged limit object together with its claimed properties.
1619-/
1620
1621theorem continuum_limit_exists
1622    (H : UniformBoundsHypothesis)
1623    (HC : ConvergenceHypothesis H)
1624    (HI : IdentificationHypothesis HC) :
1625    ∃ u : ℝ → FourierState2D,
1626      (∀ t : ℝ, ∀ k : Mode2, Tendsto (fun N : ℕ => (extendByZero (H.uN N t)) k) atTop (𝓝 ((u t) k)))
1627        ∧ HI.IsSolution u
1628        ∧ (∀ t ≥ 0, ∀ k : Mode2, ‖(u t) k‖ ≤ H.B) := by
1629  refine ⟨HC.u, HC.converges, ?_, ?_⟩
1630  · simpa using HI.isSolution
1631  · intro t ht k
1632    simpa using (ConvergenceHypothesis.coeff_bound_of_uniformBounds (HC := HC) t ht k)
1633
1634end ContinuumLimit2D
1635
1636end IndisputableMonolith.ClassicalBridge.Fluids
1637

source mirrored from github.com/jonwashburn/shape-of-logic