1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/*
* SPDX-License-Identifier: MIT
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/
//! Tensor-train-native observable extraction for the QTT 2-D incompressible rollout.
//!
//! The headline diagnostics are computed **directly on the velocity trains** — kinetic energy and the
//! divergence residual from the train `norm` (and the projector's `divergence`), and the maximum bond
//! dimension from the cores — so no dense field is materialized. Only the maximum speed needs the
//! pointwise field, so it dequantizes. The functions are free functions over the trains, usable
//! without the CfdFlow DSL.
use crateCfdScalar;
use crate;
use crate;
use Vec;
use ConjugateScalar;
use PhysicsError;
use ;
/// The penalization-force integral `(1/η) ∫ χ_body ⊙ (a − a_body) dV` over the grid — a single
/// tensor-train contraction (`inner` of the mask with the field deficit), no surface reconstruction.
/// This is the momentum (or heat) the Brinkman penalization exchanges with the body.
/// Drag and lift coefficients on the immersed body, from the **penalization-force contraction**:
/// the force the fluid exerts on the body is the penalization momentum integral `F = (1/η) ∫ χ_body ⊙
/// (u − u_body) dV` per component, nondimensionalized as `C_d = F_x / (½ ρ U² D)` (ρ = 1). A pure
/// tensor-train contraction — no cut-cell surface or boundary fiber.
///
/// # Errors
/// Propagates the train-contraction errors.
/// The penalization **heat** integral over the immersed body: `Q = (1/η) ∫ χ_body ⊙ (T_w − T) dV`,
/// the volumetric rate at which the penalization term exchanges heat with the fluid to hold the body
/// at `t_wall`. The same contraction shape as [`drag_lift`], with temperature in place of velocity.
///
/// **This is not a surface flux.** Its dimensions are `[T]·[L]²/[t]` — a temperature-weighted volume
/// integral over the masked body, carrying no gradient, no conductivity and no wall normal. Fourier's
/// law is `q = −k·∂T/∂n`, a per-area quantity on the wall surface, and no scaling converts a volume
/// integral into one. The name says `integral` for that reason, and `wall_heat_flux` is deliberately
/// left free for an actual Fourier-law implementation.
///
/// **What blocks that implementation is geometry, not machinery.** Gradient operators, a diffusivity
/// and the mask are all present, so `−k ∫ ∇T·∇χ dV` (the smoothed-mask surface-delta form) could be
/// written today. It would not mean anything: volume penalization has no wall surface, only a mask
/// smoothed over `SMOOTH_CELLS·dx`. For the `tanh` mask this crate uses, `|∇χ|` peaks at `1/(2w)` and
/// `T` relaxes across the same width, so the result scales as `k·ΔT/w` — inversely with a purely
/// numerical parameter. The audit already measured the *drag* moving 6.1x across that sweep, and drag
/// is a volume integral, which averages; a wall-normal derivative amplifies instead. The result would
/// be more blur-governed than the drag while carrying a name that invites an absolute reading.
///
/// Spectral differentiation does not rescue it either: the mask transitions over ~2 grid points, near
/// Nyquist, so an FFT-based gradient rings at the interface rather than sharpening it. Computing wall
/// fluxes from the penalization source — what this function does — is the standard volume-penalization
/// answer precisely because the interface gradient is unreliable on a smeared mask.
///
/// A genuine `wall_heat_flux` belongs on the **DEC cut-cell path**, where the surface is real:
/// `CutFaceFragment` carries a `(D−1)`-area and an outward unit normal, and
/// the DEC path's `viscous_surface_force` already integrates
/// `∮ μ(∇u+∇uᵀ)·n dA` over those fragments. `q = −k ∮ ∇T·n dA` is the same contraction with a scalar
/// in place of the tensor, so the diagnostic is close to a transcription of that one.
///
/// **The one missing part is a temperature field on that path** — the DEC solver marches velocity
/// only and has no scalar transport. Supplying it (advection–diffusion of `T`, with a Dirichlet wall
/// temperature on the cut fragments) is the prerequisite, not Gap-2 and not the cut geometry. Gap-2
/// supplies a *better* temperature field (real `k(T)`, chemistry); it is not what unblocks a flux.
///
/// The quantity is still useful as it stands: it is the thermal analogue of the penalization force
/// integral, and same-configuration ratios built on it (as [`preserved_drag_fraction`] does for
/// force) are meaningful. **Neutral** — the seam the Gap-2 reacting energy equation replaces.
///
/// # Errors
/// Propagates the train-contraction errors.
/// The forebody-strip **pressure** contraction of an evolved compressible state: the pressure is
/// recovered pointwise from the conserved components (`p = (γ−1)(E − ½|m|²/ρ)`, the ideal-gas
/// closure), re-quantized, and contracted against the strip mask via the train `inner` product
/// and the cell area — `∫ χ_strip · p dA`, no cut-cell surface or boundary-fiber
/// reconstruction. This is the *compressible* sibling of the incompressible penalization-force
/// contraction: the integrand is the field's own pressure (the preserved aerodynamic drag the
/// Jarvinen–Adams dataset measured), **not** the forcing deficit.
///
/// **Units, and why the name overstates the quantity.** The integrand is pressure (`Pa = N/m²`) and
/// the measure is the 2-D cell area `dx·dy` (`m²`), so the return carries `Pa·m² = N`. The dimensions
/// are those of a force; the quantity is not one. It is an isotropic scalar accumulated over a
/// *region* of cells, where a force is a traction integrated over a *surface*. No outward normal
/// enters, so the result has no direction, no axis to resolve along, and no meaning next to a force
/// from another configuration. Only the **ratio** of two such integrals over the *same* geometry is
/// meaningful, which is exactly how [`preserved_drag_fraction`] consumes it; the common area factor
/// cancels there. A true axial force is `∮ p n dS` over the interface: the pressure projected on the
/// strip's outward normal, integrated over the surface it acts on. `strip_pressure_integral` would be
/// the accurate name; renaming is a public-API change and is left as an owner decision, so what the
/// number is gets stated here instead.
///
/// # Errors
/// [`PhysicsError::PhysicalInvariantBroken`] if the density leaves the positive cone; propagates
/// codec / contraction errors.
/// The **preserved-drag fraction**: the powered (plume-imprinted) run's contracted forebody
/// force over the unpowered baseline's, from the same configuration — the dimensionless
/// quantity the Jarvinen–Adams correlation tabulates (`C_A,F / C_A0`). A same-configuration
/// ratio, so the harness's common geometry biases cancel.
///
/// # Errors
/// [`PhysicsError::Singularity`] if the unpowered baseline force is not finite or vanishes
/// (there is no drag to preserve a fraction of).
/// Kinetic energy `½(‖u‖² + ‖v‖²)` from the train norms — the `‖·‖` is the Frobenius/L2 norm over the
/// `2^Lx · 2^Ly` grid coefficients, so this is the (unweighted) discrete kinetic energy. No dequantize.
///
/// # Errors
/// Propagates train-norm errors.
/// The divergence residual `‖∇·(u, v)‖` (Frobenius/L2 over the grid) — the projector forms the
/// divergence train, then its norm is taken. No dequantize.
///
/// # Errors
/// Propagates the projector's divergence and the train-norm errors.
/// The maximum bond dimension across both velocity trains — the compression / rank metric. Each
/// rank-3 core `[r_left, phys, r_right]` contributes its right bond `shape()[2]`.
/// The maximum speed `max √(u² + v²)` over the dequantized `2^Lx × 2^Ly` grid.
///
/// # Errors
/// [`PhysicsError`] from dequantizing either train.