← 首页← Home

一维 Burgers 方程的离散伴随:格心格式

Discrete adjoints for one-dimensional Burgers: cell-centred scheme

算例是区间 [0,1][0,1] 上带源项的一维 Burgers 方程:左端给定入口值,右端流出,未知量放在单元中心。本页先介绍方程和它的作用,再建立网格,然后从一个面上的通量出发,依次写出残差、前向(tangent)与伴随(adjoint),最后用它们计算源项和入口值的灵敏度。每个面循环都可以逐步播放;切换一阶与二阶,可以看到重构怎样改变计算模板和收敛精度。The test case is the one-dimensional Burgers equation with a source on [0,1][0,1]: a prescribed inlet value on the left, outflow on the right, and unknowns at cell centres. After introducing the equation, what it is for and the mesh, this page starts with the flux on one face and builds the residual, the forward (tangent) and the adjoint, then uses them to compute sensitivities to the source and inlet value. Each face loop can be played step by step. Switching between first and second order shows how reconstruction changes the stencil and accuracy.

全页路线图:从设计输入到梯度设计输入 DD = (σ0, …, σ7, uin)9 个分量(8 + 1)第 12 节求解R(u, D) = 0GS 缺陷校正第 5、6 节状态 u8 个未知量每个单元 1 个第 2 节目标 J跟踪目标g = (∂J/∂u)T第 7 节梯度 dJ/dD9 个分量第 12 节前向(tangent)路线:从左往右,每个设计分量一次D˙ = ek第 k 个设计分量Av = −RD D˙​一次线性求解v = u˙​状态的切向量J˙ = gTv方向导数dJ/dDk梯度的一个分量9 个设计分量 → 9 次线性求解,每次得到梯度的一个分量(第 8、10 节)伴随(adjoint)路线:从右往左,只解一次dJ/dD = ψTRD全部 9 个分量ψ伴随变量ATψ = −g一次转置求解g = (∂J/∂u)T目标对状态的导数1 次转置求解,再做乘积 ψTRD(不再求解),得到全部 9 个分量(第 9、11、12 节)代价:9 个设计分量 ⇒ 前向 9 次线性求解,伴随 1 次全页路线图:从设计输入到梯度原始计算前向 ×9伴随 ×1从上往下从下往上设计输入 D9 个分量第 12 节求解R(u, D) = 0第 5、6 节状态 u8 个未知量第 2 节目标 Jg = (∂J/∂u)T第 7 节梯度 dJ/dD9 个分量第 12 节D˙ = ek第 k 个设计分量Av = −RD D˙​一次线性求解v = u˙​状态的切向量J˙ = gTv方向导数dJ/dDk梯度的一个分量dJ/dD = ψTRD全部 9 个分量ψ伴随变量ATψ = −g一次转置求解g = (∂J/∂u)T目标对状态的导数9 个设计分量:前向 9 次线性求解,伴随 1 次
Page roadmap: from the design input to the gradientdesign input DD = (σ0, …, σ7, uin)9 components(8 + 1)Section 12solveR(u, D) = 0GS defect correctionSections 5–6state u8 unknownsone per cellSection 2objective Jtracking objectiveg = (∂J/∂u)TSection 7gradient dJ/dD9 componentsSection 12Tangent (forward) route: left to right, once per design componentD˙ = ekdesign component kAv = −RD D˙​one linear solvev = u˙​tangent of the stateJ˙ = gTvdirectional derivativedJ/dDkone gradient component9 design components → 9 linear solves, each giving one gradient component (Sections 8 and 10)Adjoint route: right to left, a single solvedJ/dD = ψTRDall 9 componentsψadjoint variableATψ = −gone transposed solveg = (∂J/∂u)Tobjective vs state1 transposed solve, then the product ψTRD (no further solve) gives all 9 components (Sections 9, 11 and 12)Cost: 9 design components ⇒ 9 tangent solves against 1 adjoint solvePage roadmap: from the design input to the gradientPrimalTangent ×9Adjoint ×1top to bottombottom to topdesign input D9 componentsSection 12solveR(u, D) = 0Sections 5–6state u8 unknownsSection 2objective Jg = (∂J/∂u)TSection 7gradient dJ/dD9 componentsSection 12D˙ = ekcomponent kAv = −RD D˙​one linear solvev = u˙​state tangentJ˙ = gTvalong component kdJ/dDkone componentdJ/dD = ψTRDall 9 componentsψadjoint variableATψ = −gtransposed solveg = (∂J/∂u)Tobjective vs state9 design components:9 tangent solves, 1 adjoint solve
全页路线图 · 从设计输入到梯度。上排是原始计算:由设计输入求解离散方程,得到状态,再算出目标。前向(tangent)路线顺着计算方向,每个设计分量解一次线性方程,得到梯度的一个分量;伴随(adjoint)路线逆着计算方向,只解一次转置方程,再做一次乘积就得到全部分量。本页默认设置下有 9 个设计分量:9 次前向求解对 1 次伴随求解。方框下方的节号可以点击。Page roadmap · From the design input to the gradient. The top row is the primal computation: solve the discrete equations for the design input, obtain the state, then evaluate the objective. The tangent (forward) route follows the direction of computation and solves one linear system per design component, each giving one gradient component; the adjoint route runs against it, solves a single transposed system and obtains every component with one more product. With this page's default settings there are 9 design components: 9 tangent solves against one adjoint solve. The section numbers under the boxes are links.
边界对照与求解参数Boundary comparison and solver settings

第 1 节Section 1

方程:一维定常 Burgers 方程

The equation: steady one-dimensional Burgers

从守恒律到定常方程

From a conservation law to the steady equation

一维 Burgers 方程是最简单的非线性守恒律。把 u(x,t)u(x,t) 看作沿 xx 方向输运的一个量:一段区间里 uu 的总量,只因两端的通量 F(u)=u2/2F(u)=u^2/2 和区间内的源项 s(x)s(x) 而改变。写成微分形式,就是

The one-dimensional Burgers equation is the simplest nonlinear conservation law. Think of u(x,t)u(x,t) as a quantity carried along xx: the amount of uu in an interval changes only through the flux F(u)=u2/2F(u)=u^2/2 at its two ends and the source s(x)s(x) inside it. In differential form,

ut+(u22)x=s(x).u_t+\left(\frac{u^2}{2}\right)_x=s(x).

按乘积法则,(u2/2)x=u ux(u^2/2)_x=u\,u_x:uu 以自己的值为速度被输运。u uxu\,u_x 正是流体动量方程里的非线性对流项。本页只研究定常解。令 ut=0u_t=0,得到要在 [0,1][0,1] 上离散的方程和它的入口条件:

By the product rule, (u2/2)x=u ux(u^2/2)_x=u\,u_x: uu is carried along at a speed equal to its own value. The term u uxu\,u_x is exactly the nonlinear convection term of the momentum equation of fluid flow. This page studies steady solutions only. Setting ut=0u_t=0 gives the equation to be discretised on [0,1][0,1] and its inlet condition:

ddx(u22)=s(x),x∈[0,1],u(0)=uin.\frac{d}{dx}\left(\frac{u^2}{2}\right)=s(x),\quad x\in[0,1],\qquad u(0)=u_{in}.

我们研究光滑正解分支 u>0u>0 上的解。本例不含激波,所以第 3 节的二阶格式使用无斜率限制器的线性重构;间断和激波留到附录 B。

We study solutions on the smooth positive branch u>0u>0. This example has no shocks, so the second-order scheme of Section 3 uses unlimited linear reconstruction; discontinuities and shocks are left to Appendix B.

图 1把这个问题画在一张图上:当前设计,离散解取本页默认的 N=8N=8、一阶(单元平均值,画在单元中心)。下半部分是源项:8 根柱是当前设计的常值 σk=0.5\sigma_k=0.5,虚线是目标源项 sin⁡(πx)\sin(\pi x)。上半部分是解:实线是精确解 u=1+xu=\sqrt{1+x},圆点是本页算出的离散解(第 2 节起讲怎样算),虚线是目标场 utgtu^{\mathrm{tgt}}。两部分之间的箭头是各处的波速 uu:全部指向右,并随 uu 增大而变长。入口 x=0x=0 给定 uin=1u_{in}=1;出口 x=1x=1 不给条件。

Figure 1 draws the whole problem: the current design, with the discrete solution at this page's defaults, N=8N=8 and first order (cell averages, drawn at the cell centres). The lower panel is the source: the 8 bars are the current constant values σk=0.5\sigma_k=0.5 and the dashed curve is the target source sin⁡(πx)\sin(\pi x). The upper panel is the solution: the solid curve is the exact solution u=1+xu=\sqrt{1+x}, the dots are the discrete solution this page computes (Section 2 onwards shows how) and the dashed curve is the target field utgtu^{\mathrm{tgt}}. The arrows between the panels give the wave speed uu along the interval: all point right and lengthen as uu grows. The inlet x=0x=0 has the prescribed uin=1u_{in}=1; the outlet x=1x=1 takes no condition.

图 1 · 本页的问题一览(当前设计;离散解取本页默认的 N = 8、一阶,为单元平均值,画在单元中心)。下:源项 s(x),8 根柱是当前设计的常值 σₖ = 0.5,虚线是目标源项 sin(πx)。上:精确解 u = √(1 + x)(实线)、本页离散解(圆点)与目标场 utgt(虚线)。中间的箭头是波速 F′(u) = u,长度与 u 成正比,全部指向出口。入口 x = 0 给定 uᵢₙ = 1;出口 x = 1 不给条件,u(1) = √2 ≈ 1.414 由方程决定。1.01.21.41.6u(x)入口 x = 0:u(0) = uin​ = 1(给定)出口 x = 1:不给条件u(1) = √2 ≈ 1.414,由方程决定精确解u = √(1 + x)离散解 uiN = 8,一阶目标场utgt波速 F′(u) = u > 0:箭头长度与 u 成正比,全部指向出口00.5100.250.50.751xs(x)σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇当前源项σk​ = 0.5目标源项sin(πx)设计输入 D = (σ₀, …, σ₇, uin​):8 个源项值与入口值,共 9 个分量
图 1 · 本页的问题一览(当前设计;离散解取本页默认的 N = 8、一阶,为单元平均值,画在单元中心)。下:源项 s(x),8 根柱是当前设计的常值 σk = 0.5,虚线是目标源项 sin(πx)。上:精确解 u = √(1 + x)(实线)、本页离散解(圆点)与目标场 utgt(虚线)。中间的箭头是波速 F′(u) = u,长度与 u 成正比,全部指向出口。入口 x = 0 给定 uin = 1;出口 x = 1 不给条件,u(1) = √2 ≈ 1.414 由方程决定。
Figure 1 · The problem at a glance (current design; the discrete solution at this page's defaults, N = 8, first order, cell averages drawn at the cell centres). Bottom: the source s(x); the 8 bars are the current constant values σₖ = 0.5 and the dashed curve is the target source sin(πx). Top: the exact solution u = √(1 + x) (solid), this page's discrete solution (dots) and the target field utgt (dashed). The arrows between the panels are the wave speed F′(u) = u, drawn with length proportional to u; all point to the outlet. The inlet x = 0 has the prescribed uᵢₙ = 1; the outlet x = 1 takes no condition, and u(1) = √2 ≈ 1.414 follows from the equation.1.01.21.41.6u(x)inlet x = 0: u(0) = uin​ = 1 (prescribed)outlet x = 1: no conditionu(1) = √2 ≈ 1.414, set by the equationexactu = √(1 + x)discrete uiN = 8, 1st ordertargetutgtwave speed F′(u) = u > 0: arrow length proportional to u, all towards the outlet00.5100.250.50.751xs(x)σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇current sourceσk​ = 0.5target sourcesin(πx)design input D = (σ₀, …, σ₇, uin​): 8 source values and the inlet value, 9 components
Figure 1 · The problem at a glance (current design; the discrete solution at this page's defaults, N = 8, first order, cell averages drawn at the cell centres). Bottom: the source s(x); the 8 bars are the current constant values σk = 0.5 and the dashed curve is the target source sin(πx). Top: the exact solution u = √(1 + x) (solid), this page's discrete solution (dots) and the target field utgt (dashed). The arrows between the panels are the wave speed F′(u) = u, drawn with length proportional to u; all point to the outlet. The inlet x = 0 has the prescribed uin = 1; the outlet x = 1 takes no condition, and u(1) = √2 ≈ 1.414 follows from the equation.

波速:信息从入口流向出口

Wave speed: information travels from the inlet to the outlet

通量对状态的导数 F′(u)=uF'(u)=u 就是波速(特征速度):uu 的一个小扰动以速度 uu 沿 xx 传播。当前解处处 u≥1>0u\ge1>0,所以信息从左向右传播(图 1中间的箭头)。于是左端 x=0x=0 是入口,要给一个条件 u(0)=uinu(0)=u_{in};右端 x=1x=1 是出口,不给条件,出口值由方程和上游的数据决定。第 4 节说明离散格式怎样处理这两端。

The derivative of the flux with respect to the state, F′(u)=uF'(u)=u, is the wave speed (the characteristic speed): a small disturbance of uu travels along xx with speed uu. The current solution has u≥1>0u\ge1>0 everywhere, so information travels from left to right (the arrows in the middle of Figure 1). The left end x=0x=0 is therefore the inlet and takes one condition, u(0)=uinu(0)=u_{in}; the right end x=1x=1 is the outlet and takes none, since the equation and the upstream data determine the outlet value. Section 4 shows how the discretisation treats the two ends.

源项、设计变量与精确解

The source, the design variables and the exact solution

源项在 [0,1][0,1] 的 NN 个等长区间上分段取常值 σk\sigma_k(图 1下半部分的柱);默认 N=8N=8,这些区间也是第 2 节网格的原始区间。NN 个源项值与入口值一起构成设计输入 DD,改变它们就改变流场:

The source is piecewise constant, with the value σk\sigma_k on each of NN equal intervals of [0,1][0,1] (the bars in the lower part of Figure 1); by default N=8N=8, and these intervals are also the primal intervals of the mesh of Section 2. The NN source values and the inlet value form the design input DD; changing them changes the flow:

s(x)=σk,kN<x<k+1N,k=0,…,N−1,D=(σ0,…,σN−1,uin).\begin{aligned}s(x)&=\sigma_k,\qquad \tfrac kN<x<\tfrac{k+1}N,\quad k=0,\ldots,N-1,\\D&=(\sigma_0,\ldots,\sigma_{N-1},u_{in}).\end{aligned}

默认 N=8N=8 时,D=(σ0,…,σ7,uin)D=(\sigma_0,\ldots,\sigma_{7},u_{in}) 共有 9 个分量。当前设计取全部 σk=0.5\sigma_k=0.5、uin=1u_{in}=1,方程成为 u u′=0.5u\,u'=0.5。它可以直接积分:从 00 积分到 xx,通量的改变量等于源项的累积,再代入常数源项:

With the default N=8N=8, D=(σ0,…,σ7,uin)D=(\sigma_0,\ldots,\sigma_{7},u_{in}) has 9 components. The current design takes σk=0.5\sigma_k=0.5 for every kk and uin=1u_{in}=1, so the equation becomes u u′=0.5u\,u'=0.5. It can be integrated directly: from 00 to xx the change of the flux equals the accumulated source, and with the constant source

u(x)22=uin22+∫0xs(t) dt=12+x2⟹u(x)=1+x.\begin{aligned}\frac{u(x)^2}{2}&=\frac{u_{in}^2}{2}+\int_0^x s(t)\,dt\\&=\frac12+\frac x2\qquad\Longrightarrow\qquad u(x)=\sqrt{1+x}.\end{aligned}

取正平方根,因为本页只研究正解分支。出口值 u(1)=2u(1)=\sqrt2 不是给定的,而是由方程和入口数据决定(图 1的实线)。这个闭式解是第 15 节测量网格收敛的参考解,也是第 16 节和附录 A 中连续伴随的出发点。

The positive root is taken because this page studies the positive branch. The outlet value u(1)=2u(1)=\sqrt2 is not prescribed; the equation and the inlet data determine it (the solid curve in Figure 1). This closed form is the reference for the mesh-convergence study of Section 15 and the starting point of the continuous adjoint in Section 16 and Appendix A.

设计的目的在第 7 节给出:调整源项,让解接近目标场 utgtu^{\mathrm{tgt}}(图 1上半部分的虚线)。目标场由目标源项 sin⁡(πx)\sin(\pi x) 和同一个入口值产生;目标函数测量解与目标场的差别,这是一个反设计问题。

Section 7 gives the purpose of the design: adjust the source so that the solution approaches the target field utgtu^{\mathrm{tgt}} (the dashed curve in the upper part of Figure 1). The target is produced by the target source sin⁡(πx)\sin(\pi x) with the same inlet value; the objective measures the mismatch between the solution and the target, which makes this an inverse-design problem.

这个方程的作用

What this equation is for

在流体力学里,Burgers 方程保留了动量方程中的非线性对流项 u uxu\,u_x,去掉了压力;它是含有这一项的最简单的方程。没有黏性时,速度大的部分追上前面速度小的部分,波形变陡,最后形成激波(附录 B 讨论激波和它的伴随)。加上黏性项,ut+(u2/2)x=νuxxu_t+(u^2/2)_x=\nu u_{xx},变陡与扩散互相平衡,形成有限厚度的光滑过渡层(附录 D.3、D.4 用的就是带黏性的形式)。正因为它简单而又保留了非线性,数值通量、迎风格式和斜率限制器常常先在这个方程上检验。

In fluid dynamics the Burgers equation keeps the nonlinear convection term u uxu\,u_x of the momentum equation and drops the pressure; it is the simplest equation that carries this term. Without viscosity, faster parts of a wave catch up with slower parts ahead of them, the profile steepens and a shock forms (Appendix B treats shocks and their adjoints). With a viscous term, ut+(u2/2)x=νuxxu_t+(u^2/2)_x=\nu u_{xx}, steepening and diffusion balance in a smooth transition layer of finite width (Appendices D.3 and D.4 use viscous forms of the equation). Because it is simple yet keeps the nonlinearity, numerical fluxes, upwind schemes and slope limiters are commonly tested on it first.

在本课程里,它作为第一个算例,有五个理由:

In this course it is the first example, for five reasons:

  • 小。默认网格只有 8 个未知量,每个数组和矩阵都能完整地写在页面上,逐句播放也看得清。
  • Small. The default mesh has only 8 unknowns, so every array and matrix fits on the page and the statement players stay readable.
  • 非线性。通量的斜率 F′(u)=uF'(u)=u 随状态改变,所以线性化(Jacobian)取决于在哪个状态上求导:前向与伴随都在收敛的原始解上计算。
  • Nonlinear. The slope of the flux, F′(u)=uF'(u)=u, changes with the state, so the linearisation (the Jacobian) depends on the state it is taken at: the tangent and the adjoint are both evaluated at the converged primal solution.
  • 有精确解可比。闭式解 u=1+xu=\sqrt{1+x} 用来测量离散误差(第 15 节);连续伴随也能写出来(第 16 节、附录 A),用来和离散伴随对照。
  • Exact answers to compare with. The closed form u=1+xu=\sqrt{1+x} measures the discretisation error (Section 15), and the continuous adjoint can be written down too (Section 16, Appendix A) for comparison with the discrete one.
  • 设计变量在源项里。改变设计不会移动网格,所以不需要对网格坐标求导;几何导数留给二维页面。
  • Design variables in the source. Changing the design does not move the mesh, so no derivatives with respect to mesh coordinates are needed; geometric derivatives are left to the 2-D pages.
  • 同一个面循环。本页的面循环(收集、重构、算通量、分发)在 Euler、N–S 和 RANS 页面上反复出现,只是每个面上的计算更多;黏性页面还要先算一遍梯度。
  • The same face loop. This page’s face loop (gather, reconstruct, evaluate the flux, scatter) reappears on the Euler, N–S and RANS pages, with more work on each face; the viscous pages first compute gradients.

图 2画出通量 F(u)=u2/2F(u)=u^2/2。当前解的状态从入口的 uin=1u_{in}=1 增加到出口的 u(1)=2u(1)=\sqrt2,占据曲线上加粗的一段。两端切线的斜率就是两处的波速:入口处为 11,出口处为 2≈1.414\sqrt2\approx1.414。斜率随状态改变,这就是非线性:在一个状态上求出的导数,不能拿到另一个状态上用。所以第 3 节数值通量的偏导 cL,cRc_L,c_R 和第 6 节的 Jacobian 都要在当前状态上计算,前向与伴随则在收敛的原始解上计算。竖直方向,两端通量之差 F(2)−F(1)=0.5F(\sqrt2)-F(1)=0.5 恰好等于源项的积分 ∫01s dx=0.5\int_0^1s\,dx=0.5:定常时,流出的通量减去流入的通量,等于区间里的源项。第 3 节就从这个守恒关系出发,把它写到每一个控制体上。

Figure 2 plots the flux F(u)=u2/2F(u)=u^2/2. The state of the current solution rises from uin=1u_{in}=1 at the inlet to u(1)=2u(1)=\sqrt2 at the outlet and covers the thick part of the curve. The slopes of the tangents at the two ends are the wave speeds there: 11 at the inlet and 2≈1.414\sqrt2\approx1.414 at the outlet. The slope changes with the state, and that is the nonlinearity: a derivative computed at one state cannot be reused at another. The flux derivatives cL,cRc_L,c_R of Section 3 and the Jacobian of Section 6 are therefore evaluated at the current state, and the tangent and the adjoint at the converged primal solution. Vertically, the difference of the end fluxes, F(2)−F(1)=0.5F(\sqrt2)-F(1)=0.5, equals the integrated source ∫01s dx=0.5\int_0^1s\,dx=0.5: in a steady state the flux out minus the flux in equals the source inside. Section 3 starts from this conservation statement and writes it for every control volume.

图 2 · 通量 F(u) = u²/2 与它随状态变化的斜率。加粗的一段是当前解经过的状态 1 ≤ u ≤ √2。两条切线的斜率就是两端的波速:入口处 F′ = 1,出口处 F′ = √2 ≈ 1.414;斜率随 u 改变,所以线性化(Jacobian)要在当前状态上计算。竖直箭头:两端通量之差 F(√2) − F(1) = 0.5,等于源项的积分 ∫₀¹ s dx = 0.5。00.511.500.511.5uF(u) = u²/2ΔF入口 uin​ = 1切线斜率 1出口 u(1) = √2切线斜率 √2斜率 F′(u) = u 就是波速入口 uin​ = 1:F′ = 1出口 u(1) = √2:F′ ≈ 1.414斜率随状态改变,所以线性化(Jacobian)要在当前状态上计算ΔF = F(√2) − F(1) = 0.5= ∫₀¹ s dx = 0.5:流出减流入等于源项
图 2 · 通量 F(u) = u²/2 与它随状态变化的斜率。加粗的一段是当前解经过的状态 1 ≤ u ≤ √2。两条切线的斜率就是两端的波速:入口处 F′ = 1,出口处 F′ = √2 ≈ 1.414;斜率随 u 改变,所以线性化(Jacobian)要在当前状态上计算。竖直箭头:两端通量之差 F(√2) − F(1) = 0.5,等于源项的积分 ∫₀¹ s dx = 0.5。
Figure 2 · The flux F(u) = u²/2 and its state-dependent slope. The thick part is the range of states of the current solution, 1 ≤ u ≤ √2. The slopes of the two tangents are the wave speeds at the two ends: F′ = 1 at the inlet and F′ = √2 ≈ 1.414 at the outlet; the slope changes with u, so the linearisation (the Jacobian) must be evaluated at the current state. Vertical arrow: the difference of the end fluxes, F(√2) − F(1) = 0.5, equals the integrated source ∫₀¹ s dx = 0.5.00.511.500.511.5uF(u) = u²/2ΔFinlet uin​ = 1tangent slope 1outlet u(1) = √2tangent slope √2slope F′(u) = u: the wave speedinlet uin​ = 1: F′ = 1outlet u(1) = √2: F′ ≈ 1.414the slope changes with the state, sothe linearisation (Jacobian) is takenat the current stateΔF = F(√2) − F(1) = 0.5= ∫₀¹ s dx = 0.5: out − in = source
Figure 2 · The flux F(u) = u²/2 and its state-dependent slope. The thick part is the range of states of the current solution, 1 ≤ u ≤ √2. The slopes of the two tangents are the wave speeds at the two ends: F′ = 1 at the inlet and F′ = √2 ≈ 1.414 at the outlet; the slope changes with u, so the linearisation (the Jacobian) must be evaluated at the current state. Vertical arrow: the difference of the end fluxes, F(√2) − F(1) = 0.5, equals the integrated source ∫₀¹ s dx = 0.5.

图 3把本页放在整个网站的路线上。每往右一步主要增加一类新内容:拟一维 Euler 把一个方程换成质量、动量、能量三个方程,并引入压力和变截面积;二维 Euler 增加第二个空间方向,面有了法向,网格坐标也成为设计变量;二维层流 N–S 加入黏性应力和热传导,面上还要用到梯度;二维 RANS–SA 再加一个湍流模型方程。每点的未知量从 1 个增加到 5 个,但每一页的残差仍由面循环组装,前向与伴随也沿同一个循环求导。另有两页与本页相邻:一维层流 N–S 页留在一维,在 Euler 方程上加入黏性和热传导;二维标量对流页是本页在二维的对应页面。

Figure 3 places this page on the route through the site. Each step to the right mainly adds one kind of ingredient: quasi-1-D Euler replaces the single equation by three (mass, momentum and energy) and brings in the pressure and a varying cross-section; 2-D Euler adds a second space direction, so faces have normals and the mesh coordinates become design variables; 2-D laminar N–S adds viscous stresses and heat conduction, which need gradients at the faces; 2-D RANS–SA adds one turbulence-model equation. The unknowns per point grow from 1 to 5, yet every page still assembles its residual with face loops, and the tangent and the adjoint differentiate the same loops. Two more pages sit next to this one: the 1-D laminar N–S page stays in one dimension and adds viscosity and heat conduction to the Euler equations, and the 2-D scalar advection page is this page's counterpart in two dimensions.

图 3 · 从本页到网站上的其他方程。每往右一步主要增加一类新内容,每点的未知量从 1 个增加到 5 个(取自各页的默认设置);每一页都用面循环组装残差,前向与伴随沿同一个循环求导。每个方框都链接到该页(同为格心格式)的第 1 节,即那一页的方程。一维 Burgers每点 1 个未知量非线性对流 u ux设计在源项里本页拟一维 Euler每点 3 个未知量质量、动量、能量压力、变截面积二维 Euler每点 4 个未知量第二个空间方向面法向、网格坐标二维层流 N–S每点 4 个未知量黏性应力、热传导面上要用梯度二维 RANS–SA每点 5 个未知量湍流模型:多一个输运方程每一页都用面循环组装残差:收集两侧状态 → 重构 → 算通量 → 分发到两侧前向与伴随沿同一个循环求导;黏性页面在通量循环之前先算一遍梯度
图 3 · 从本页到网站上的其他方程。每往右一步主要增加一类新内容,每点的未知量从 1 个增加到 5 个(取自各页的默认设置);每一页都用面循环组装残差,前向与伴随沿同一个循环求导。每个方框都链接到该页(同为格心格式)的第 1 节,即那一页的方程。
Figure 3 · From this page to the other equations of the site. Each step to the right mainly adds one kind of ingredient, and the unknowns per point grow from 1 to 5 (each page's default settings); every page assembles its residual with a face loop, and the tangent and the adjoint differentiate that loop. Every box links to Section 1 of its page (in the same, cell-centred scheme), where that page introduces its equations.1-D Burgers1 unknownper pointnonlinearconvection u uxdesign in the sourcethis pageQuasi-1-D Euler3 unknownsper pointmass, momentum,energy; pressure,varying area2-D Euler4 unknownsper pointa second direction,face normals,mesh coordinates2-D laminar N–S4 unknownsper pointviscous stress,heat conduction,face gradients2-D RANS–SA5 unknownsper pointturbulence model:one more transportequationEvery page assembles its residual with a face loop: gather → reconstruct → flux → scatter.The tangent and the adjoint differentiate the same loop; the viscous pages first compute gradients.
Figure 3 · From this page to the other equations of the site. Each step to the right mainly adds one kind of ingredient, and the unknowns per point grow from 1 to 5 (each page's default settings); every page assembles its residual with a face loop, and the tangent and the adjoint differentiate that loop. Every box links to Section 1 of its page (in the same, cell-centred scheme), where that page introduces its equations.

本页要回答的问题

The question this page answers

假设你已经学过微积分、矩阵乘法、Newton 法和线性方程的迭代求解。本页要连接这些知识:先把一个微分方程写成有限个非线性代数方程,再求“输入改变一点,解和目标函数会改变多少”。你不需要先学自动微分或最优化;前向与伴随都从链式法则和矩阵转置开始。

This page assumes calculus, matrix multiplication, Newton’s method and iterative linear solvers. We connect them by turning a differential equation into finitely many nonlinear equations, then asking how a small input change affects the solution and an objective. Automatic differentiation and optimisation are not prerequisites: forward and adjoint calculations start with the chain rule and matrix transposition.

本页只回答一个问题:设计输入 DD 改变一点,目标 JJ 改变多少;页首的全页路线图把后面各节串成一条线。按页首默认设置(一阶、GS 固定点、每次一轮扫描)实际计数,9 次前向求解共用 209 次 GS 扫描,1 次伴随求解只用 24 次。

This page answers one question: how much does the objective JJ change when the design input DD changes a little? The page roadmap at the top strings the later sections together. Counted with the default settings at the top of the page (first order, GS fixed point, one sweep per application), the 9 forward solves take 209 GS sweeps in total and the single adjoint solve only 24.

要在计算机上回答这个问题,先要把连续方程换成有限个未知量的代数方程。第 2 节先建立网格、决定未知量放在哪里;第 3 节再把守恒律写成面上的通量差。

To answer it on a computer, the continuous equation must first become algebraic equations in finitely many unknowns. Section 2 builds the mesh and decides where the unknowns live; Section 3 then writes the conservation law as flux differences across faces.

第 2 节Section 2

网格:未知量放在哪里

The mesh: where the unknowns live

第 1 节的方程对区间上的每一个 xx 都成立,计算机却只能保存有限个数。本节选网格:决定未知量放在哪里、数组怎样编号,再写下本页用到的符号和数据。

The equation of Section 1 holds at every xx of the interval, but a computer stores finitely many numbers. This section chooses the mesh: it decides where the unknowns live and how the arrays are numbered, and fixes the notation and data used on this page.

先把物理问题、网格和数组分开

Separate the physical problem, mesh and arrays

用 NN 表示原始区间数,h=1/Nh=1/N 表示网格宽度,MM 表示未知量个数。数组从 0 开始编号。格心格式有 M=NM=N;格点格式有 M=N+1M=N+1。网格位置 xix_i 是已知几何数据,未知量 uiu_i 是要解出的数值,二者不能混淆。

Let NN be the number of primal intervals, h=1/Nh=1/N the spacing and MM the number of unknowns. Arrays use zero-based indices. Cells have M=NM=N unknowns; nodes have M=N+1M=N+1. Coordinates xix_i are known geometric data; values uiu_i are numbers to solve for.

图 4 · 同一条区间,两种未知量与控制体。实线标出原始区间,虚线标出对偶面;蓝点为未知量。(a) 格心:4 个单元,4 个平均值u0x=0.125u1x=0.375u2x=0.625u3x=0.875f0f1f2f3f4(b) 格点:5 个节点,5 个对偶控制体u0x=0u1x=0.25u2x=0.5u3x=0.75u4x=1h/2hh/2本页采用 (a):入口和出口都通过控制体边界上的通量进入残差。
图 4 · 同一条区间,两种未知量与控制体。实线标出原始区间,虚线标出对偶面;蓝点为未知量。
Figure 4 · One interval, two placements of unknowns and volumes. Solid lines mark primal intervals; dashed lines mark dual faces; blue dots are unknowns.(a) Cell-centred: 4 cells, 4 averagesu0x=0.125u1x=0.375u2x=0.625u3x=0.875f0f1f2f3f4(b) Node-centred: 5 nodes, 5 dual volumesu0x=0u1x=0.25u2x=0.5u3x=0.75u4x=1h/2hh/2This page uses (a): inlet and outlet enter through boundary fluxes.
Figure 4 · One interval, two placements of unknowns and volumes. Solid lines mark primal intervals; dashed lines mark dual faces; blue dots are unknowns.

格心格式把每个区间本身作为控制体,未知量是区间平均值,画在中心只是表示它的存储位置。一个单元有左、右两个边界面;在一维,“面”是一个点。积分方程仍需要该点的通量,不能因为面没有长度就把它省去。

For a cell scheme, each interval is a control volume. Its unknown is an interval average; the centre marks where it is stored. Each cell has a left and a right face. In one dimension a face is a point, but the integrated equation still needs the flux at that point.

本页的符号和数据

Notation and data used on this page

u∈ℝMu\in\mathbb R^M 是状态向量,D=(σ0,…,σN−1,uin)D=(\sigma_0,\ldots,\sigma_{N-1},u_{in}) 是设计输入,R(u,D)∈ℝMR(u,D)\in\mathbb R^M 是残差,J(u)J(u) 是一个标量目标。按伴随优化文献的通常写法,设计变量记作 DD。求导采用自动微分(AD)的记号:程序中任一变量 xx 的前向(tangent)导数记作 x˙\dot x,反向(adjoint)累加到 xx 上的权重记作 xˉ\bar x。设计方向是种子 D˙\dot D;v≡u˙v\equiv\dot u 是状态的切向量,也就是前向求解得到的数组;ww 是检验转置乘积时任意给定的残差种子 Rˉ=w\bar R=w,ψ\psi 是由伴随方程求出的特定种子。ww 和 ψ\psi 的用途不同。

The state is u∈ℝMu\in\mathbb R^M, the design input is D=(σ0,…,σN−1,uin)D=(\sigma_0,\ldots,\sigma_{N-1},u_{in}), the residual is R(u,D)∈ℝMR(u,D)\in\mathbb R^M, and the objective J(u)J(u) is a scalar. As is usual in the adjoint-optimization literature, the design variables are written DD. Derivatives use the notation of automatic differentiation (AD): the forward (tangent) derivative of any program variable xx is x˙\dot x, and the weight accumulated into xx in reverse (adjoint) mode is xˉ\bar x. A design direction is the seed D˙\dot D; v≡u˙v\equiv\dot u is the state tangent, the array returned by a tangent solve; ww is an arbitrary residual seed Rˉ=w\bar R=w for testing a transpose product, and ψ\psi is the particular seed obtained by solving the adjoint equation. These two seeds serve different purposes.

当前计算网格

The current computational mesh

未知量是 NN 个单元上的平均值 uiu_i,中心坐标 xi=(i+1/2)hx_i=(i+1/2)h。单元宽度和目标函数权重均为 h=1/Nh=1/N。二阶重构保持每个单元的平均值。

Unknowns are cell averages uiu_i in NN cells, with centres xi=(i+1/2)hx_i=(i+1/2)h. Cell widths and objective weights are h=1/Nh=1/N. Linear reconstruction preserves each cell average.

页首的一阶/二阶按钮控制整页计算。求解器选择独立于空间精度。所有实时示例使用当前网格、边界处理与设计变量;精度研究使用固定的网格序列,并同时画出两个阶数。

The first-/second-order buttons control calculations throughout this page. Solver choice is independent of spatial order. Live examples share the selected mesh, boundary treatment and design; the accuracy study uses a fixed mesh sequence and displays both orders.

网格与面:计算前准备的数据

Mesh and faces: data prepared before calculation

h = 1 / N; M = N
x[i] = (i+1/2)*h; W[i] = h           # i = 0,...,N-1
faces = [(-1, 0, 0)]                  # prescribed exterior inlet
faces += [(i, i+1, (i+1)*h) for i = 0,...,N-2]
faces += [(N-1, -1, 1)]               # physical outflow

伪代码约定:一个面记为 (L,R,xf)(L,R,x_f);−1-1 表示域外,不是合法数组下标。只有存在对应控制体时才向它累加。WiW_i 是控制体宽度;后面的目标积分也使用它。

Pseudocode convention: a face stores (L,R,xf)(L,R,x_f). The index −1-1 means exterior, not a valid array index. Accumulate only when the corresponding volume exists. The width WiW_i also supplies the objective quadrature weight.

网格定下了控制体和它们的面。第 3 节把第 1 节的守恒律写到控制体上:控制体里源项的总量,等于从它两端的面流出的净通量;所以先要会算一个面上的通量。

The mesh fixes the control volumes and their faces. Section 3 writes the conservation law of Section 1 on the control volumes: the total source in a volume equals the net flux out through the faces at its two ends, so the first task is the flux on one face.

第 3 节Section 3

面上的通量:守恒与重构

The flux on a face: conservation and reconstruction

本节实验:改变一个未知量,哪些面值会变化?先预测重构邻域,再用图验证。

Experiment: Which traces change when one unknown changes? Predict the stencil, then inspect the diagram.

第 2 节把区间分成了控制体。本节把守恒律写成控制体上的通量差,用数值通量把面两侧的状态变成一个面通量,再说明二阶格式怎样把状态重构到面上。

Section 2 divided the interval into control volumes. This section writes the conservation law as a flux difference over a control volume, turns the states on the two sides of a face into one face flux with a numerical flux, and then shows how the second-order scheme reconstructs states at the faces.

从守恒到通量差

From conservation to a flux difference

物理通量就是第 1 节的 F(u)=u2/2F(u)=u^2/2。对控制体 [a,b][a,b] 积分,用微积分基本定理,导数积分变为两个端点的通量差。这一步是有限体积法的起点:控制体内的源项总量,必须等于净流出通量。

The physical flux is the F(u)=u2/2F(u)=u^2/2 of Section 1. Integrate over a volume [a,b][a,b] and use the fundamental theorem of calculus: the derivative becomes the difference of endpoint fluxes. This is the starting point of finite volumes: the integrated source equals the net outward flux.

∫abdFdx dx=F(u(b))−F(u(a))=∫abs(x) dx.\int_a^b \frac{dF}{dx}\,dx=F(u(b))-F(u(a))=\int_a^b s(x)\,dx.

离散后,相邻控制体可能给同一个面提供不同的近似值 a,ba,b,所以需要一个数值通量 H(a,b)H(a,b) 把这两个值变成一个面通量。这里的 a,ba,b 是状态,不是上一式的积分端点。通量应满足一致性 H(u,u)=F(u)H(u,u)=F(u)。共享同一个 HH,还让内部面的贡献在全域求和时严格相消。

After discretisation, neighbouring volumes can supply different states a,ba,b at the same face. A numerical flux H(a,b)H(a,b) turns these into one shared flux. Here a,ba,b denote states rather than integration endpoints. Consistency requires H(u,u)=F(u)H(u,u)=F(u). Sharing the same flux also makes interior contributions cancel in a global sum.

图 5 · 一个内部面只计算一次通量。它从左控制体流出、流入右控制体,因此左右残差分别加、减同一个数。L+HfR−Hf选中的面 f正方向 +x
图 5 · 一个内部面只计算一次通量。它从左控制体流出、流入右控制体,因此左右残差分别加、减同一个数。
Figure 5 · Evaluate an interior flux once. It leaves the left volume and enters the right, so the two residuals receive the same number with opposite signs.L+HfR−HfSelected face fPositive direction +x
Figure 5 · Evaluate an interior flux once. It leaves the left volume and enters the right, so the two residuals receive the same number with opposite signs.

本例固定耗散系数为 ε=0.8\varepsilon=0.8。项 −ε(b−a)-\varepsilon(b-a) 抑制相邻状态的跳跃;它也会影响离散误差。第 1 节的精确解 u=1+xu=\sqrt{1+x} 就是后面测量这种误差的参考解。

The dissipation coefficient is fixed at ε=0.8\varepsilon=0.8. The term −ε(b−a)-\varepsilon(b-a) damps state jumps and affects discretisation error. The exact solution u=1+xu=\sqrt{1+x} of Section 1 is the reference against which that error is measured later.

从一阶常值到二阶重构

From constant states to linear reconstruction

一阶格式在控制体内取常值。二阶内部斜率用中心差分,端点斜率用单边差分。虽然端点斜率本身只有一阶精度,但乘以 O(h)O(h) 的外推距离后,面值仍有 O(h2)O(h^2) 精度;实际全域阶数由网格加密验证。

First order uses piecewise constant values. Second order uses centred interior slopes and one-sided endpoint slopes. The endpoint slope is only first-order accurate, but multiplication by an O(h)O(h) extrapolation distance gives an O(h2)O(h^2) face value; mesh refinement checks the resulting global order.

di=ui+1−ui−12h,d0=u1−u0h,dM−1=uM−1−uM−2h,ui(x)=ui+(x−xi)di.d_i=\frac{u_{i+1}-u_{i-1}}{2h},\quad d_0=\frac{u_1-u_0}{h},\quad d_{M-1}=\frac{u_{M-1}-u_{M-2}}{h},\qquad u_i(x)=u_i+(x-x_i)d_i.
ufL=ui+h2di,ufR=ui+1−h2di+1,H(a,b)=a2+b24−ε(b−a),ε=0.8.u_f^L=u_i+\frac h2d_i,\quad u_f^R=u_{i+1}-\frac h2d_{i+1},\qquad H(a,b)=\frac{a^2+b^2}{4}-\varepsilon(b-a),\quad\varepsilon=0.8.

其中 MM 是未知量个数。通量的两个偏导为 cL=a/2+εc_L=a/2+\varepsilon、cR=b/2−εc_R=b/2-\varepsilon。重构之后仍使用同一个通量;二阶精度来自面值重构。下图可选择一个面,查看两侧斜率与重构值。

Here MM is the number of unknowns. Flux derivatives are cL=a/2+εc_L=a/2+\varepsilon and cR=b/2−εc_R=b/2-\varepsilon. Both orders use the same numerical flux: reconstruction changes the face values. Select a face below to inspect the slopes and reconstructed states.

把重构写成可以求导的加权和

Write reconstruction as differentiable weighted sums

二阶不是更换 Burgers 通量,而是用相邻数据估计面上的状态。以内部左迹为例,把中心斜率代入 qL=ui+(h/2)diq_L=u_i+(h/2)d_i,就得到三个非零权重。右迹同理;合起来一个面依赖四个未知量。这也解释了为什么二阶 Jacobian 比一阶更宽。

Second order estimates face states using neighbouring data while retaining the Burgers flux. Substitute the centred slope into qL=ui+(h/2)diq_L=u_i+(h/2)d_i to obtain three nonzero weights. The right trace works similarly; together a face depends on four unknowns. This explains the wider second-order Jacobian.

qL=ui+h2ui+1−ui−12h=−14ui−1+ui+14ui+1,qR=14ui+ui+1−14ui+2.q_L=u_i+\frac h2\frac{u_{i+1}-u_{i-1}}{2h}=-\tfrac14u_{i-1}+u_i+\tfrac14u_{i+1},\qquad q_R=\tfrac14u_i+u_{i+1}-\tfrac14u_{i+2}.

重构伪代码:一阶与二阶共用入口

Reconstruction pseudocode: one interface for both orders

trace(i, xf, u, order):
    if order == 1: return u[i]
    if i == 0: slope = (u[1] - u[0]) / h
    else if i == M-1: slope = (u[M-1] - u[M-2]) / h
    else: slope = (u[i+1] - u[i-1]) / (2*h)
    return u[i] + (xf - x[i]) * slope

读图练习:先选一阶,再选二阶,保持面编号不变。观察左右迹、模板点与通量怎样变化。线性函数应被二阶重构精确再现;这说明局部重构正确,但全局二阶还需要边界、源项积分和网格加密结果共同支持。

Try keeping the same face selected while switching order. Observe the traces, stencil points and flux. Second-order reconstruction reproduces a linear function exactly. This checks local reconstruction; global second order also depends on boundaries, source integration and mesh refinement.

一个面上的导数:通量与重构

Derivatives on one face: the flux and the reconstruction

第 8、9 节的前向与伴随只用到两类局部导数:数值通量对面两侧状态的偏导,以及面值(迹)对未知量的偏导。这里把它们一次推导清楚。数值通量是两侧状态的多项式,逐项求导即可:

The tangent and the adjoint of Sections 8 and 9 use only two kinds of local derivative: those of the numerical flux with respect to the states on the two sides of a face, and those of the face values (the traces) with respect to the unknowns. Derive them once here. The numerical flux is a polynomial in its two states, so it can be differentiated term by term:

∂H∂a=∂∂a[a2+b24−ε(b−a)]=a2+ε=cL,∂H∂b=b2−ε=cR.\frac{\partial H}{\partial a}=\frac{\partial}{\partial a}\left[\frac{a^2+b^2}{4}-\varepsilon(b-a)\right]=\frac a2+\varepsilon=c_L,\qquad \frac{\partial H}{\partial b}=\frac b2-\varepsilon=c_R.

出口面(以及“边界对照”里的迎风面)用物理通量 H=12a2H=\tfrac12a^2,它只依赖左侧状态:∂H/∂a=a\partial H/\partial a=a,∂H/∂b=0\partial H/\partial b=0。有两条性质可以检查这些公式。一致性 H(u,u)=F(u)H(u,u)=F(u) 对每个 uu 都成立,沿 a=b=ua=b=u 求导得 cL+cR=F′(u)=uc_L+c_R=F'(u)=u:两个偏导把波速分成上游与下游两份。在本页的状态范围 1≤u≤21\le u\le\sqrt2 上,cLc_L 在 1.3001.300 与 1.5071.507 之间,cRc_R 在 −0.300-0.300 与 −0.093-0.093 之间:通量随上游状态增大、随下游状态减小。只要 ∣a∣,∣b∣<2ε=1.6|a|,|b|<2\varepsilon=1.6,这种单调性就成立。

The outlet face (and the upwind face of the boundary comparison) uses the physical flux H=12a2H=\tfrac12a^2, which depends on the left state only: ∂H/∂a=a\partial H/\partial a=a and ∂H/∂b=0\partial H/\partial b=0. Two properties check these formulas. Consistency H(u,u)=F(u)H(u,u)=F(u) holds for every uu; differentiating it along a=b=ua=b=u gives cL+cR=F′(u)=uc_L+c_R=F'(u)=u: the two partial derivatives split the wave speed into an upstream and a downstream share. Over this page's states 1≤u≤21\le u\le\sqrt2, cLc_L lies between 1.3001.300 and 1.5071.507 and cRc_R between −0.300-0.300 and −0.093-0.093: the flux increases with the upstream state and decreases with the downstream one. This monotonicity holds as long as ∣a∣,∣b∣<2ε=1.6|a|,|b|<2\varepsilon=1.6.

cL+cR∣a=b=u=u=F′(u),cL>0>cR(∣a∣,∣b∣<2ε).c_L+c_R\Big|_{a=b=u}=u=F'(u),\qquad c_L>0>c_R\quad(|a|,|b|<2\varepsilon).

面值的导数更简单。不用斜率限制器时,每个迹都是未知量的固定线性组合 qL=∑j(TL)fjujq_L=\sum_j(T_L)_{fj}u_j(格心入口面的外侧状态是设计输入 uinu_{in}),所以它对 uju_j 的偏导就是权重本身,与 uu 无关:一阶时所在控制体的权重为 11;二阶内部面是上式的 (−14,1,14)(-\tfrac14,1,\tfrac14),第一个和最后一个内部面用单边斜率,权重为 (12,12)(\tfrac12,\tfrac12);格心入口内侧迹与出口迹为 (32,−12)(\tfrac32,-\tfrac12)。权重不随状态变化,所以前向沿用同一组权重,伴随用它们的转置,并用 += 累加:

The traces are simpler still. Without a limiter each trace is a fixed linear combination of the unknowns, qL=∑j(TL)fjujq_L=\sum_j(T_L)_{fj}u_j (the exterior state of the cell inlet face is the design input uinu_{in}), so its partial derivative with respect to uju_j is the weight itself and does not depend on uu: weight 11 on its own volume at first order; at second order (−14,1,14)(-\tfrac14,1,\tfrac14) for an interior trace as above, (12,12)(\tfrac12,\tfrac12) at the first and the last interior faces, where the slope is one-sided, and (32,−12)(\tfrac32,-\tfrac12) for the inlet interior trace and the outlet trace of the cell page. Since the weights do not change with the state, the tangent reuses the same weights and the adjoint their transpose, accumulated with +=:

∂qL∂uj=(TL)fj,q˙L=∑j(TL)fj u˙j,uˉj+=(TL)fj qˉL.\frac{\partial q_L}{\partial u_j}=(T_L)_{fj},\qquad \dot q_L=\sum_j(T_L)_{fj}\,\dot u_j,\qquad \bar u_j\mathrel{\text{+=}}(T_L)_{fj}\,\bar q_L.

本页不用斜率限制器。若采用受限重构 uf=ui+ϕiΔiu_f=u_i+\phi_i\Delta_i(附录 D.2),限制器 ϕi\phi_i 随状态变化,切向量多出一项:u˙f=u˙i+ϕiΔ˙i+Δiϕ˙i\dot u_f=\dot u_i+\phi_i\dot\Delta_i+\Delta_i\dot\phi_i。权重于是依赖 uu,而 minmod 在切换分支处不可微(附录 D.1)。

This page uses no limiter. A limited reconstruction uf=ui+ϕiΔiu_f=u_i+\phi_i\Delta_i (Appendix D.2) has a limiter ϕi\phi_i that changes with the state, so its tangent gains a term, u˙f=u˙i+ϕiΔ˙i+Δiϕ˙i\dot u_f=\dot u_i+\phi_i\dot\Delta_i+\Delta_i\dot\phi_i: the weights then depend on uu, and where minmod switches branches it is not differentiable (Appendix D.1).

图 6把这两类导数画出来:左边是 cLc_L、cRc_R 随状态的变化;右边是本页默认网格上每个面的通量对每个未知量的导数 ∂Hf/∂uj=cL(TL)fj+cR(TR)fj\partial H_f/\partial u_j=c_L(T_L)_{fj}+c_R(T_R)_{fj}。一阶时每个内部面只连两个未知量,二阶时连四个(第一个和最后一个内部面只连三个);第 6 节把这些行按“左加右减”组合成 Jacobian。

Figure 6 draws both kinds: on the left cLc_L and cRc_R against the state; on the right, for this page's default mesh, the derivative of every face flux with respect to every unknown, ∂Hf/∂uj=cL(TL)fj+cR(TR)fj\partial H_f/\partial u_j=c_L(T_L)_{fj}+c_R(T_R)_{fj}. At first order an interior face touches two unknowns, at second order four (three at the first and the last interior face); Section 6 combines these rows, plus on the left and minus on the right, into the Jacobian.

一个面上的导数:通量的偏导与每个面的通量导数(a) 数值通量的两个偏导−1−0.500.511.5200.511.52状态 a = b = u2ε = 1.6cL = a/2 + εcR = b/2 − εcL + cR = u本页状态(b) ∂Hf/∂uj:一阶u₀u₁u₂u₃u₄u₅u₆u₇uin​f₀−0.271.30f₁1.33−0.24f₂1.36−0.22f₃1.38−0.19f₄1.41−0.17f₅1.43−0.14f₆1.46−0.12f₇1.48−0.10f₈1.40二阶:内部面连四个未知量,首尾两个内部面连三个u₀u₁u₂u₃u₄u₅u₆u₇uin​f₀−0.450.151.30f₁0.600.400.07f₂−0.341.300.100.06f₃−0.351.330.130.05f₄−0.351.370.170.05f₅−0.361.400.200.04f₆−0.371.430.230.03f₇−0.371.430.31f₈−0.712.12
图 6 · 一个面上的导数(格心,N = 8)。(a) 数值通量的偏导 cL=a/2+εc_L=a/2+\varepsilon 与 cR=b/2−εc_R=b/2-\varepsilon 随状态的变化;阴影是本页解的状态范围 1≤u≤21\le u\le\sqrt2,虚线是 a=ba=b 时二者之和 cL+cR=uc_L+c_R=u,cRc_R 在竖虚线 2ε2\varepsilon 处变号。(b) 每个面的通量对每个未知量的导数 ∂Hf/∂uj=cL(TL)fj+cR(TR)fj\partial H_f/\partial u_j=c_L(T_L)_{fj}+c_R(T_R)_{fj},在本页的一阶(上)与二阶(下)收敛解上计算;蓝正橙负。格心入口面 f0f_0 另有外侧状态 uinu_{in} 一列(虚线右侧),它进入 RDR_D 而不进入 AA。
Derivatives on one face: the flux partials and the derivative of every face flux(a) the two partial derivatives of the flux−1−0.500.511.5200.511.52state a = b = u2ε = 1.6cL = a/2 + εcR = b/2 − εcL + cR = uthis page(b) ∂Hf/∂uj: first orderu₀u₁u₂u₃u₄u₅u₆u₇uin​f₀−0.271.30f₁1.33−0.24f₂1.36−0.22f₃1.38−0.19f₄1.41−0.17f₅1.43−0.14f₆1.46−0.12f₇1.48−0.10f₈1.40second order: four per interior face, three at the first and lastu₀u₁u₂u₃u₄u₅u₆u₇uin​f₀−0.450.151.30f₁0.600.400.07f₂−0.341.300.100.06f₃−0.351.330.130.05f₄−0.351.370.170.05f₅−0.361.400.200.04f₆−0.371.430.230.03f₇−0.371.430.31f₈−0.712.12
Figure 6 · Derivatives on one face (cell page, N = 8). (a) The partial derivatives of the numerical flux, cL=a/2+εc_L=a/2+\varepsilon and cR=b/2−εc_R=b/2-\varepsilon, against the state; the band is the range of this page's states 1≤u≤21\le u\le\sqrt2, the dashed line their sum cL+cR=uc_L+c_R=u at a=ba=b, and cRc_R changes sign at the dashed 2ε2\varepsilon. (b) The derivative of every face flux with respect to every unknown, ∂Hf/∂uj=cL(TL)fj+cR(TR)fj\partial H_f/\partial u_j=c_L(T_L)_{fj}+c_R(T_R)_{fj}, at this page's converged first-order (top) and second-order (bottom) solutions; blue positive, orange negative. The cell inlet face f0f_0 has an extra column for the exterior state uinu_{in} (right of the dashed line), which enters RDR_D, not AA.

到这里,每个内部面的通量都能由两侧的重构状态算出。区间两端的面不同:入口面 x=0x=0 左侧没有控制体,只有给定的 uinu_{in};出口面 x=1x=1 右侧什么也没有。第 4 节写出这两个边界面上的通量。

Every interior face now gets its flux from the reconstructed states on its two sides. The two end faces differ: the inlet face x=0x=0 has no control volume on its left, only the prescribed uinu_{in}, and nothing lies to the right of the outlet face x=1x=1. Section 4 writes the fluxes on these two boundary faces.

第 4 节Section 4

边界条件:入口与出口

Boundary conditions: inlet and outlet

为什么只给一个入口条件

Why prescribe only one inlet condition?

第 1 节从波速的方向说明了只给入口条件的原因。换一个角度看,定常方程在正解分支上是一阶常微分方程。给定 u(0)u(0) 后,积分就确定沿程的 u(x)u(x);出口值应由内部解决定。再任意指定一个出口值,通常会过约束。离散时仍须提供出口通量,但“计算出口通量”与“另加出口值约束”是两件事。

Section 1 explained the single inlet condition by the direction of the wave speed. Seen another way, the steady equation on the positive branch is first order. Once u(0)u(0) is specified, integration determines u(x)u(x) downstream. An arbitrary additional outlet value would generally overconstrain it. The discretisation still needs an outlet flux, but evaluating that flux is different from imposing an extra outlet-value constraint.

图 7 · 原始问题的边界处理。入口给定u_in入口数值通量H(u_in, qR)内部平衡流出 − 流入 − 源出口物理通量½ u_out²
图 7 · 原始问题的边界处理。
Figure 7 · Boundary treatment of the primal problem.Prescribed inletu_inInlet fluxH(u_in, qR)Interior balanceout − in − sourceOutlet flux½ u_out²
Figure 7 · Boundary treatment of the primal problem.

入口通量使用外部给定值 uinu_{in} 与从第一个单元重构至 x=0x=0 的内部迹。出口从末单元重构到 x=1x=1,再计算物理通量。二阶出口若仍直接使用末单元平均值,就会引入一阶边界误差。

The inlet flux uses the prescribed exterior value uinu_{in} and the interior trace reconstructed to x=0x=0. At the outlet, reconstruct from the final cell to x=1x=1 before applying the physical flux. Using the final cell average directly at a second-order outlet would introduce a first-order boundary error.

uinR=u0−h2d0,Hin=H(uin,uinR),uout=uN−1+h2dN−1,Hout=12uout2.u_{in}^{R}=u_0-\frac h2d_0,\quad H_{in}=H(u_{in},u_{in}^{R}),\qquad u_{out}=u_{N-1}+\frac h2d_{N-1},\quad H_{out}=\frac12u_{out}^2.

图 8用本页默认网格(N=8N=8,一阶)的编号和收敛值画出两个边界面。入口面 f0f_0 左边没有控制体:外部值 uinu_{in} 是设计输入,不是未知量,所以它的偏导不进入 AA,只进入 RDR_D;面右边的内部迹 qRq_R 才依赖未知量。出口面只有左边的单元,直接用物理通量 uout2/2u_{out}^2/2,不需要任何外部值;二阶时 uoutu_{out} 由最后两个单元外推。

Figure 8 draws the two boundary faces with the indices and converged values of the default mesh (N=8N=8, first order). The inlet face f0f_0 has no control volume on its left: the exterior value uinu_{in} is a design input, not an unknown, so its partial derivative enters RDR_D and not AA; only the interior trace qRq_R on the right depends on unknowns. The outlet face has a cell on its left only and uses the physical flux uout2/2u_{out}^2/2 with no exterior value; at second order uoutu_{out} is extrapolated from the last two cells.

图 8 · 格心页的两个边界面(N = 8,一阶收敛解)。入口面 f₀ 用数值通量 H(uᵢₙ, qR):外部值 uᵢₙ 是设计输入,它的偏导 cL 进入 RD;内部迹 qR 的偏导 cR 进入 A。出口面只用内部值和物理通量 ½uout²,不需要外部状态。uin​ = 1.0000外部值,非未知量u₀ = 1.0584u₁ = 1.1140u₆ = 1.3604u₇ = 1.4024⋯f₀ (x = 0)f₈ (x = 1)外侧没有控制体入口面 f₀:数值通量 H(a, b)a = uin​ = 1.0000(设计输入)b = qR = u₀ = 1.0584(一阶;二阶 (3u₀ − u₁)/2)Hin = H(a, b) = 0.4833∂H/∂a = cL = 1.300 → 进入 RD(设计导数)∂H/∂b = cR = −0.271 → 进入 A(状态 Jacobian)第 0 行:R₀ = H(f₁) − Hin − hσ₀出口面 f₈:物理通量uout = u₇ = 1.4024(一阶直接取单元值)二阶外推:uout = (3u₇ − u₆)/2Hout = ½ uout² = 0.9833只用内部值:出口不给边界值,也不需要外部状态第 7 行:R₇ = Hout − H(f₇) − hσ₇
图 8 · 格心页的两个边界面(N = 8,一阶收敛解)。入口面 f₀ 用数值通量 H(uin, qR):外部值 uin 是设计输入,它的偏导 cL 进入 RD;内部迹 qR 的偏导 cR 进入 A。出口面只用内部值和物理通量 ½uout²,不需要外部状态。
Figure 8 · The two boundary faces of the cell page (N = 8, converged first-order solution). The inlet face f₀ uses the numerical flux H(uᵢₙ, qR): the exterior value uᵢₙ is a design input, so its partial cL enters RD, while the partial cR of the interior trace qR enters A. The outlet face uses interior values and the physical flux ½uout² only; it needs no exterior state.uin​ = 1.0000exterior valueu₀ = 1.0584u₁ = 1.1140u₆ = 1.3604u₇ = 1.4024⋯f₀ (x = 0)f₈ (x = 1)no exteriorvolumeInlet face f₀: numerical flux H(a, b)a = uin​ = 1.0000 (design input)b = qR = u₀ = 1.0584 (1st order; 2nd: (3u₀ − u₁)/2)Hin = H(a, b) = 0.4833∂H/∂a = cL = 1.300 → enters RD (design)∂H/∂b = cR = −0.271 → enters A (state Jacobian)row 0: R₀ = H(f₁) − Hin − hσ₀Outlet face f₈: physical fluxuout = u₇ = 1.4024 (1st order: the cell value)2nd order extrapolates: uout = (3u₇ − u₆)/2Hout = ½ uout² = 0.9833interior values only: no outlet valueand no exterior staterow 7: R₇ = Hout − H(f₇) − hσ₇
Figure 8 · The two boundary faces of the cell page (N = 8, converged first-order solution). The inlet face f₀ uses the numerical flux H(uin, qR): the exterior value uin is a design input, so its partial cL enters RD, while the partial cR of the interior trace qR enters A. The outlet face uses interior values and the physical flux ½uout² only; it needs no exterior state.

“边界对照”允许改用迎风通量 H=a2/2H=a^2/2:格心改入口面,格点改第一内部对偶面。它是另一种明确标记的离散边界闭合,用于附录 A 的比较。格点二阶迎风面依赖重构斜率,因此它仍依赖相邻未知量。

The boundary comparison replaces one flux by H=a2/2H=a^2/2: the inlet face for cells, or the first internal dual face for nodes. This is a distinct, explicitly labelled boundary closure, compared with the default one in Appendix A. At second order, the node-based upwind face depends on its slope and therefore still on neighbouring unknowns.

对状态求导时,给定的入口值暂时固定,外部入口的切向量 u˙in=0\dot u_{in}=0;对入口设计变量求导时,它是种子 u˙in\dot u_{in} 不为零的输入。这两个导数分别进入 A=RuA=R_u 和 RDR_D,不能混在一个状态模板里。边界对照改变的是离散方程本身,因此必须同时改变原始、前向、伴随和目标场。

When differentiating with respect to the state, hold the prescribed inlet fixed: its exterior tangent is u˙in=0\dot u_{in}=0. When differentiating with respect to the inlet design variable, it is an input with a nonzero seed u˙in\dot u_{in}. These paths enter A=RuA=R_u and RDR_D separately. Changing the boundary comparison changes the discretisation and must be reflected in the primal, forward, adjoint and target field.

边界行怎样进入前向与伴随

How the boundary rows enter the tangent and the adjoint

把两条边界行的全部依赖一次写出来。第 0 行含入口面(带负号)和它右侧的内部面(带正号);最后一行含左侧的内部面(带负号)和出口面(带正号,物理通量):

Write out every dependence of the two boundary rows once. Row 0 holds the inlet face (with a minus sign) and the interior face on its right (plus); the last row holds the interior face on its left (minus) and the outlet face (plus, the physical flux):

R0=H1/2(qL,qR)−H(uin,uinR)−hσ0,RM−1=12uout2−HM−3/2(qL,qR)−hσM−1.\begin{aligned}R_0&=H_{1/2}(q_L,q_R)-H\big(u_{in},u_{in}^R\big)-h\sigma_0,\\R_{M-1}&=\tfrac12u_{out}^2-H_{M-3/2}(q_L,q_R)-h\sigma_{M-1}.\end{aligned}

前向:沿每条路径求导再相加。入口外侧状态只经过入口面进入,所以 u˙in\dot u_{in} 只出现在第 0 行,系数是 −cLin-c_L^{in};内侧迹 uinR=∑jβjuju_{in}^R=\sum_j\beta_ju_j 与出口迹 uout=∑jβjoutuju_{out}=\sum_j\beta^{out}_ju_j 的权重一阶时都是 (1)(1),二阶时都是 (32,−12)(\tfrac32,-\tfrac12):

Forward: differentiate along every path and add. The exterior inlet state enters only through the inlet face, so u˙in\dot u_{in} appears in row 0 alone, with the coefficient −cLin-c_L^{in}; the interior trace uinR=∑jβjuju_{in}^R=\sum_j\beta_ju_j and the outlet trace uout=∑jβjoutuju_{out}=\sum_j\beta^{out}_ju_j both have the weights (1)(1) at first order and (32,−12)(\tfrac32,-\tfrac12) at second order:

R˙0=cL1/2q˙L+cR1/2q˙R−cLin u˙in−cRin∑jβju˙j−hσ˙0,R˙M−1=uout∑jβjoutu˙j−cLM−3/2q˙L−cRM−3/2q˙R−hσ˙M−1.\begin{aligned}\dot R_0&=c_L^{1/2}\dot q_L+c_R^{1/2}\dot q_R-c_L^{in}\,\dot u_{in}-c_R^{in}\sum_j\beta_j\dot u_j-h\dot\sigma_0,\\\dot R_{M-1}&=u_{out}\sum_j\beta^{out}_j\dot u_j-c_L^{M-3/2}\dot q_L-c_R^{M-3/2}\dot q_R-h\dot\sigma_{M-1}.\end{aligned}

伴随:同样的路径倒过来走,每一项都用 += 累加。由种子 Rˉ0=w0\bar R_0=w_0、RˉM−1=wM−1\bar R_{M-1}=w_{M-1} 出发,每条边界行把种子分给它含有的两个面通量(入口面与第一个内部面,或最后一个内部面与出口面)和源项;每个面通量的伴随乘上它的两个偏导,再按迹的重构权重回到未知量。内部面的两个迹是 qL=∑j(TL)fjujq_L=\sum_j(T_L)_{fj}u_j 与 qR=∑j(TR)fjujq_R=\sum_j(T_R)_{fj}u_j:

Adjoint: the same paths reversed, every term accumulated with +=. From the seeds Rˉ0=w0\bar R_0=w_0 and RˉM−1=wM−1\bar R_{M-1}=w_{M-1}, each boundary row hands its seed to the two face fluxes it contains (the inlet face and the first interior face, or the last interior face and the outlet face) and to the source; the adjoint of each face flux is multiplied by its two partial derivatives and goes back to the unknowns through the reconstruction weights of the traces. The two traces of an interior face are qL=∑j(TL)fjujq_L=\sum_j(T_L)_{fj}u_j and qR=∑j(TR)fjujq_R=\sum_j(T_R)_{fj}u_j:

Hˉin-=Rˉ0,uˉin+=cLinHˉin,uˉj+=βj cRinHˉin,σˉ0-=hRˉ0,Hˉ1/2+=Rˉ0,uˉj+=(TL)1/2,j cL1/2Hˉ1/2+(TR)1/2,j cR1/2Hˉ1/2,Hˉout+=RˉM−1,uˉj+=βjout uoutHˉout,σˉM−1-=hRˉM−1,HˉM−3/2-=RˉM−1,uˉj+=(TL)M−3/2,j cLM−3/2HˉM−3/2+(TR)M−3/2,j cRM−3/2HˉM−3/2.\begin{aligned}&\bar H_{in}\mathrel{\text{-=}}\bar R_0,\quad \bar u_{in}\mathrel{\text{+=}}c_L^{in}\bar H_{in},\quad \bar u_j\mathrel{\text{+=}}\beta_j\,c_R^{in}\bar H_{in},\quad \bar\sigma_0\mathrel{\text{-=}}h\bar R_0,\\&\bar H_{1/2}\mathrel{\text{+=}}\bar R_0,\quad \bar u_j\mathrel{\text{+=}}(T_L)_{1/2,j}\,c_L^{1/2}\bar H_{1/2}+(T_R)_{1/2,j}\,c_R^{1/2}\bar H_{1/2},\\&\bar H_{out}\mathrel{\text{+=}}\bar R_{M-1},\quad \bar u_j\mathrel{\text{+=}}\beta^{out}_j\,u_{out}\bar H_{out},\quad \bar\sigma_{M-1}\mathrel{\text{-=}}h\bar R_{M-1},\\&\bar H_{M-3/2}\mathrel{\text{-=}}\bar R_{M-1},\quad \bar u_j\mathrel{\text{+=}}(T_L)_{M-3/2,j}\,c_L^{M-3/2}\bar H_{M-3/2}+(T_R)_{M-3/2,j}\,c_R^{M-3/2}\bar H_{M-3/2}.\end{aligned}

在默认网格(N=8N=8)的收敛解上,这两行的非零元素如下表,它们就是第 6 节 Jacobian 的第 0 行、最后一行与第 10 节 RDR_D 的对应元素。上面的语句可以直接核对:一阶时取 Rˉ=e0\bar R=e_0 得到 uˉ0=cL1/2−cRin\bar u_0=c_L^{1/2}-c_R^{in}、uˉ1=cR1/2=−0.242978\bar u_1=c_R^{1/2}=-0.242978,取 Rˉ=e7\bar R=e_{7} 得到 uˉ6=−cLM−3/2\bar u_{6}=-c_L^{M-3/2}、uˉ7=uout−cRM−3/2\bar u_{7}=u_{out}-c_R^{M-3/2},正是表中一阶一列的四个元素。第 0 行的对角元来自两个面,它们都在同一个状态 u0u_0 上求偏导:

At the converged solution on the default mesh (N=8N=8) the nonzero entries of the two rows are listed below: they are row 0 and the last row of the Jacobian of Section 6 and the matching entries of RDR_D of Section 10. The statements above can be checked directly: at first order Rˉ=e0\bar R=e_0 gives uˉ0=cL1/2−cRin\bar u_0=c_L^{1/2}-c_R^{in} and uˉ1=cR1/2=−0.242978\bar u_1=c_R^{1/2}=-0.242978, and Rˉ=e7\bar R=e_{7} gives uˉ6=−cLM−3/2\bar u_{6}=-c_L^{M-3/2} and uˉ7=uout−cRM−3/2\bar u_{7}=u_{out}-c_R^{M-3/2}, the four first-order entries of the table. The diagonal entry of row 0 comes from two faces, both differentiated at the same state u0u_0:

cL1/2−cRin=(u0/2+ε)−(u0/2−ε)=2ε=1.6.c_L^{1/2}-c_R^{in}=(u_0/2+\varepsilon)-(u_0/2-\varepsilon)=2\varepsilon=1.6.

二阶时第 0 行多出 A0,2A_{0,2},最后一行多出 A7,5A_{7,5}。它们不来自入口面和出口面的单边迹(这两个迹只含 u0,u1u_0,u_1 与 u6,u7u_{6},u_{7}),而来自紧邻的内部面:第一个内部面的右迹 qR=14u0+u1−14u2q_R=\tfrac14u_0+u_1-\tfrac14u_2 用单元 1 的中心斜率,所以 A0,2=−14cR1/2=0.067417A_{0,2}=-\tfrac14c_R^{1/2}=0.067417;最后一个内部面的左迹用单元 6 的中心斜率,所以 A7,5=14cLM−3/2=0.371095A_{7,5}=\tfrac14c_L^{M-3/2}=0.371095。

At second order row 0 gains A0,2A_{0,2} and the last row A7,5A_{7,5}. They do not come from the one-sided traces of the inlet and outlet faces (those hold only u0,u1u_0,u_1 and u6,u7u_{6},u_{7}) but from the interior faces next to them: the right trace of the first interior face, qR=14u0+u1−14u2q_R=\tfrac14u_0+u_1-\tfrac14u_2, uses the central slope of cell 1, so A0,2=−14cR1/2=0.067417A_{0,2}=-\tfrac14c_R^{1/2}=0.067417; the left trace of the last interior face uses the central slope of cell 6, so A7,5=14cLM−3/2=0.371095A_{7,5}=\tfrac14c_L^{M-3/2}=0.371095.

元素Entry一阶First order二阶Second order
A0,0A_{0,0}1.6000001.6000001.0458761.045876
A0,1A_{0,1}−0.242978-0.2429780.2459490.245949
A0,2A_{0,2}—0.0674170.067417
∂R0/∂uin\partial R_0/\partial u_{in}−1.300000-1.300000−1.300000-1.300000
∂R0/∂σ0\partial R_0/\partial\sigma_0−0.125000-0.125000−0.125000-0.125000
A7,5A_{7,5}—0.3710950.371095
A7,6A_{7,6}−1.480225-1.480225−2.133312-2.133312
A7,7A_{7,7}1.5011881.5011881.8074161.807416
∂R7/∂σ7\partial R_{7}/\partial\sigma_{7}−0.125000-0.125000−0.125000-0.125000
第 5 节Section 5

残差:收集、重构、计算、分发

Residual: gather, reconstruct, compute, scatter

先固定离散问题与符号

Fix the discrete problem and its notation

本页每个单元存储 1 个未知量。以下用 𝐮\mathbf u 表示把所有位置按代码顺序排成的列向量(标量问题每个位置只有一个分量)。它是一个有限维向量,不是连续函数。物理方程中的空间导数只用于说明模型;求离散伴随时,求导对象是本页实际装配的有限维函数。

Each cell stores 1 unknown. Below, 𝐮\mathbf u stacks all locations in code order (a scalar problem has one component per location). It is a finite-dimensional vector, not a continuous function. Spatial derivatives in the physical equations explain the model; discrete differentiation acts on the actual finite-dimensional functions assembled on this page.

𝐮∈ℝnu,D∈ℝna,\mathbf u\in\mathbb R^{n_u},\quad D\in\mathbb R^{n_a},
R:ℝnu×ℝna→ℝnu,J:ℝnu×ℝna→ℝ.R:\mathbb R^{n_u}\times\mathbb R^{n_a}\to\mathbb R^{n_u},\quad J:\mathbb R^{n_u}\times\mathbb R^{n_a}\to\mathbb R.

DD 是本页全部设计变量排成的向量:每个原始区间上的常源项 σk\sigma_k 与入口值 uinu_{in}。第 6 节按空间阶数把精确 Jacobian 记作 AqA_q,这里的 AA 就是当前阶数的 AqA_q;第 7 节的 gg 就是这里的状态梯度;第 10 节的 f=RD D˙f=R_D\,\dot D 是 RDR_D 作用在设计种子 D˙\dot D 上的结果。

DD stacks all design variables of this page: the constant source σk\sigma_k on each original interval and the inlet value uinu_{in}. Section 6 writes the exact Jacobian of each spatial order as AqA_q; the AA here is the AqA_q of the current order. The gg of Section 7 is the state gradient here, and the f=RD D˙f=R_D\,\dot D of Section 10 is RDR_D applied to the design seed D˙\dot D.

A=∂R∂𝐮∈ℝnu×nu,RD=∂R∂D∈ℝnu×na,A=\frac{\partial R}{\partial \mathbf u}\in\mathbb R^{n_u\times n_u},\quad R_D=\frac{\partial R}{\partial D}\in\mathbb R^{n_u\times n_a},
g=(∂J∂𝐮)T∈ℝnu,∇DJ=(∂J∂D)T∈ℝna.g=\Big(\frac{\partial J}{\partial \mathbf u}\Big)^{T}\in\mathbb R^{n_u},\quad \nabla_DJ=\Big(\frac{\partial J}{\partial D}\Big)^{T}\in\mathbb R^{n_a}.

这里 gg、∇DJ\nabla_DJ 都是列向量,它们的转置才是偏导数行。求导采用自动微分(AD)的记号:程序变量 xx 上加点的 x˙\dot x 是前向(tangent)模式沿给定种子方向的导数,加横线的 xˉ\bar x 是反向(adjoint)模式累加到 xx 上的权重,例如 𝐮˙\dot{\mathbf u} 与 𝐮ˉ\bar{\mathbf u}。两者都是对本页有限维代码求导;变分符号 δ\delta 只用于连续伴随附录里连续函数的变分。

Both gg and ∇DJ\nabla_DJ are columns; their transposes are the derivative rows. Derivatives use the notation of automatic differentiation (AD): a dot x˙\dot x on a program variable xx is its forward (tangent) derivative along the chosen seed direction, and a bar xˉ\bar x is the weight accumulated into xx in reverse (adjoint) mode, for example 𝐮˙\dot{\mathbf u} and 𝐮ˉ\bar{\mathbf u}. Both differentiate the finite-dimensional code of this page; the variation symbol δ\delta is kept for variations of continuous functions in the continuous-adjoint appendix.

残差是“还没有平衡的量”

A residual measures the remaining imbalance

把所有离散平衡式的右侧移到左侧,得到向量 R(u,D)R(u,D)。一个分量对应一个方程;Ri=0R_i=0 表示该控制体平衡。求解器的任务是找使全部分量同时为零的 uu。计算残差本身只是在给定 uu 后做一次函数求值,不会自动求出解。

Move each discrete balance to the left to form R(u,D)R(u,D). One component represents one equation; Ri=0R_i=0 means balance in that volume. The solver seeks a state making all components zero simultaneously. Evaluating the residual at a supplied state is only a function evaluation, not a solution of the equations.

面循环比逐行重复计算通量更直接:先把残差数组清零,再遍历每个面,重构状态、计算通量、向两侧累加,最后扣除源项。内部通量在两个方程中重复出现,却只计算一次。这里存储的是积分残差,没有除以控制体宽度 WiW_i;后面的 Jacobian、目标权重和伴随符号都按这一约定。

A face loop evaluates each shared flux once: zero the residual, visit each face, reconstruct states, compute the flux, add it to its neighbours, then subtract sources. An interior flux appears in two equations but is evaluated once. We store integrated residuals without dividing by volume width WiW_i; all Jacobians, objective weights and adjoint conventions follow this choice.

三个矩阵符号分别做什么

What the three matrix symbols do

TL,TRT_L,T_R 的一行存储一个面的重构权重;一阶每行通常只有一个 1,二阶每行通常有三个非零数。已知的入口常数另存为 bLb_L。ℬ\mathcal B 的一列对应一个面,在左行放 +1+1、右行放 −1-1。所以矩阵写法只是把刚才的收集与分发打包,并没有增加新的数值方法。

TL,TRT_L,T_R store one face’s reconstruction weights per row: typically one unit entry at first order and three nonzero entries at second order. Prescribed inlet constants are stored separately in bLb_L. A column of ℬ\mathcal B represents one face, with +1+1 in the left row and −1-1 in the right. The matrix notation packages gather and scatter; it introduces no new numerical method.

qL=TLu+bL,q˙L=TLv(D˙=0),R˙=ℬ H˙.q_L=T_Lu+b_L,\qquad \dot q_L=T_Lv\quad(\dot D=0),\qquad \dot R=\mathcal B\,\dot H.

这些矩阵式首先描述未经入口行替换的通量部分。出口只用左迹的物理通量;格点的第 0 行最后单独施加约束。播放器中的权重列表就是这些稀疏矩阵的非零项。

These matrix expressions first describe the flux contribution before inlet row replacement. The outlet uses the physical flux of its left trace, and the node scheme applies its row-0 constraint separately. The player’s weight lists are the nonzero entries of these sparse maps.

设计变量为每个原始区间上的常源项 σk\sigma_k 和入口值。源项先积分到控制体,面通量按左正右负分发。符号约定固定为“流出减流入减源项”。

Design variables are the constant source values σk\sigma_k on the primal intervals and the inlet value. Integrate sources over control volumes, then scatter each face flux with a positive sign to its left and a negative sign to its right. The residual convention is outflow minus inflow minus source.

Si=hσi,Ri=Hi+1/2−Hi−1/2−Si.S_i=h\sigma_i,\qquad R_i=H_{i+1/2}-H_{i-1/2}-S_i.

将重构写为线性映射 TL,TRT_L,T_R,将面到控制体的有向分发写为 ℬ\mathcal B,可以把两个空间阶数统一为下面的计算链。入口面上给定的外侧值 uinu_{in} 是常数,另存于 bLb_L。

Linear reconstruction maps TL,TRT_L,T_R and the oriented face-to-volume scatter ℬ\mathcal B unify both spatial orders. The prescribed exterior value uinu_{in} of the inlet face is a constant, stored separately in bLb_L.

u →TL,TR (qL,qR) →H Hf →ℬ R=ℬH−S.u\ \xrightarrow{T_L,T_R}\ (q_L,q_R)\ \xrightarrow{H}\ H_f\ \xrightarrow{\mathcal B}\ R=\mathcal B H-S.

怎样使用逐句播放器:高亮行是刚执行的语句,变量表只显示已经算出的量,下层网格是执行后的累积数组。“下一步”前进一句;同一面内先得到左右迹,再算通量,最后分别更新左右残差。源项和入口约束也有独立步骤。符号 −1-1 只表示域外,访问数组之前会被排除。

Reading the statement player: the highlighted line has just executed, the variable table contains values already computed, and the lower mesh shows the accumulated array afterwards. Next advances one statement: left trace, right trace, flux, left update, right update. Sources and the inlet constraint have separate steps. Exterior index −1-1 is excluded before array access.

第 6 节Section 6

Jacobian 与 Gauss–Seidel 迭代

The Jacobian and Gauss–Seidel iteration

从 Newton 法得到线性修正方程

From Newton’s method to a linear correction

你熟悉的一元 Newton 法用切线逼近函数。对向量残差也是同样的思路:在当前状态附近作一阶 Taylor 展开,让线性近似的残差为零。矩阵 AijA_{ij} 表示第 jj 个未知量增加一点,会让第 ii 个残差改变多少。修正量记作 Δu\Delta u:它是求解器每一步的有限增量,不是求导记号。

Scalar Newton uses a tangent line to approximate a function. For a residual vector, use a first-order Taylor expansion at the current state and set the linear approximation to zero. The entry AijA_{ij} measures how residual ii responds to a small change in unknown jj. The correction is written Δu\Delta u: a finite solver increment, not a derivative symbol.

Rq(u+Δu)=Rq(u)+Aq(u)Δu+O(∥Δu∥2),Aq(u)Δu=−Rq(u).R_q(u+\Delta u)=R_q(u)+A_q(u)\Delta u+O(\|\Delta u\|^2),\qquad A_q(u)\Delta u=-R_q(u).

精确 Newton 会使用 AqA_q 并充分求解这个线性方程。本页为了展示实际求解器中常用的低阶近似,改用 P=A1(u)P=A_1(u),再用有限次 GS 近似解修正方程。每次更新后仍重新计算完整的 RqR_q。因此必须分清三层:空间格式决定 RqR_q;线性化决定 AqA_q;近似矩阵和 GS 决定修正怎样得到。

Exact Newton uses AqA_q and solves that linear system accurately. To illustrate a common low-order approximation, this page uses P=A1(u)P=A_1(u) and a finite number of GS sweeps. Every update is checked with the full residual RqR_q. Distinguish three levels: spatial discretisation defines RqR_q, differentiation defines AqA_q, and the approximate matrix plus GS determines the correction.

GS 中“立即使用新值”是什么意思

What does GS mean by using new values immediately?

[4−1−13][x0x1]=[12],x(0)=(0,0)T.\begin{bmatrix}4&-1\\-1&3\end{bmatrix}\begin{bmatrix}x_0\\x_1\end{bmatrix}=\begin{bmatrix}1\\2\end{bmatrix},\qquad x^{(0)}=(0,0)^T.

第一轮先算 x0=(1+x1old)/4=1/4x_0=(1+x_1^{old})/4=1/4,随后立即用这个新值算 x1=(2+x0new)/3=3/4x_1=(2+x_0^{new})/3=3/4。第二轮得到 x0=7/16x_0=7/16、x1=13/16x_1=13/16,逐步接近精确解 (5/11,9/11)(5/11,9/11)。如果第二行仍用旧的 x0=0x_0=0,就不是这次 GS 扫描。下方网格用绿色标出已经更新的位置。

The first sweep computes x0=(1+x1old)/4=1/4x_0=(1+x_1^{old})/4=1/4, then immediately uses it in x1=(2+x0new)/3=3/4x_1=(2+x_0^{new})/3=3/4. The next sweep gives x0=7/16x_0=7/16 and x1=13/16x_1=13/16, approaching (5/11,9/11)(5/11,9/11). Using the old x0=0x_0=0 in the second row would not be this GS sweep. The mesh below marks updated positions in green.

一个内部残差行怎样组成 Jacobian

How one interior residual row forms the Jacobian

先取一阶内部行。它包含右面的 +H+H 和左面的 −H-H;分别求导并保持符号,就得到三对角的三个系数。二阶时每个面还依赖远邻,通过重构权重再乘一次,就扩展到五对角。

For a first-order interior row, differentiate the right-face +H+H and the left-face −H-H with their signs intact. This gives the three tridiagonal coefficients. At second order each face also depends on farther neighbours; multiplication by reconstruction weights widens the row to five diagonals.

Ai,i−1=−cLi−1/2,Ai,i=cLi+1/2−cRi−1/2,Ai,i+1=cRi+1/2.A_{i,i-1}=-c_L^{i-1/2},\quad A_{i,i}=c_L^{i+1/2}-c_R^{i-1/2},\quad A_{i,i+1}=c_R^{i+1/2}.

这个公式只针对两个相邻面都用内部数值通量的平衡行。入口、出口和约束行必须由各自实际表达式求导,不能直接照抄内部公式。

This formula applies to a balance row whose two faces both use the interior numerical flux. Inlet, outlet and constraint rows must be differentiated from their actual expressions.

精确 Jacobian 是当前空间格式的残差导数 Aq=∂Rq/∂uA_q=\partial R_q/\partial u。一阶内部通常是三对角,二阶重构扩大为五对角;边界单边模板也必须求导。求解时只用在当前状态上计算的一阶 Jacobian P=A1(u)P=A_1(u) 构造修正步骤。

The exact Jacobian is Aq=∂Rq/∂uA_q=\partial R_q/\partial u for the selected spatial order. Interior first-order rows are generally tridiagonal; reconstruction widens second-order rows to five diagonals. One-sided boundary stencils must also be differentiated. The correction uses the first-order Jacobian P=A1(u)P=A_1(u) evaluated at the current state.

PkΔu=−Rq(uk),uk+1=uk+ω Δu,Pk=A1(uk),q∈{1,2}.P_k\Delta u=-R_q(u^k),\qquad u^{k+1}=u^k+\omega\,\Delta u,\qquad P_k=A_1(u^k),\quad q\in\{1,2\}.

一轮 GS 扫描怎样更新未知量

How one GS sweep updates the unknowns

把 P=diag⁡(P)+L+UP=\operatorname{diag}(P)+L+U 分为对角、严格下三角和严格上三角部分。内层从零修正开始做固定次数 GS 扫描;一轮扫描中,已经经过的行使用新值。默认一次扫描,属于不精确的线性求解;因此需要继续外层残差校正。

Split P=diag⁡(P)+L+UP=\operatorname{diag}(P)+L+U into diagonal, strictly lower and strictly upper parts. The inner solve starts from zero correction and performs a fixed number of GS sweeps. Already visited rows use new values. The default single sweep is an inexact linear solve, so outer residual corrections continue.

(diag⁡(P)+L)Δu(ℓ+1)=−Rq−UΔu(ℓ),Δui(ℓ+1)=−Rq,i−∑j<iPijΔuj(ℓ+1)−∑j>iPijΔuj(ℓ)Pii.(\operatorname{diag}(P)+L)\Delta u^{(\ell+1)}=-R_q-U\Delta u^{(\ell)},\qquad \Delta u_i^{(\ell+1)}=\frac{-R_{q,i}-\sum_{j<i}P_{ij}\Delta u_j^{(\ell+1)}-\sum_{j>i}P_{ij}\Delta u_j^{(\ell)}}{P_{ii}}.

求解用的矩阵与要求解的方程。一阶 Jacobian 给出修正方向,完整残差 RqR_q 决定何时收敛。二阶求解因此仍保留全部重构项。若修正使状态不再为正,就减小步长以留在当前解支。

The correction matrix and the equation being solved. The first-order Jacobian supplies a correction; the full residual RqR_q determines convergence. A second-order solve therefore retains every reconstruction term. Updates are damped when needed to stay on the positive solution branch.

下面先看两个矩阵的非零结构,再逐行播放一次 GS 扫描,最后观察解和残差怎样随迭代变化。

Below, compare the two matrix stencils, play one GS sweep row by row, then watch the solution and residual change over the iterations.

原始求解:外层更新与内层 GS

Primal solve: outer updates and inner GS

u = positive_initial_state()
repeat:
    r = residual(u, D, selected_order)
    if norm(r) <= tolerance: return u
    P = first_order_jacobian(u)
    delta = zeros(M)
    repeat fixed_number_of_sweeps:
        for i = 0,...,M-1:
            delta[i] = (-r[i] - Σ(j≠i) P[i,j]*delta[j]) / P[i,i]
    alpha = relaxation
    while min(u + alpha*delta) <= 0.05 and alpha > 1e-8: alpha = alpha/2
    u = u + alpha*delta
# At the iteration limit, report non-convergence.

数组 delta 存放修正量 Δu\Delta u。GS 行更新中的同一个数组同时保存“前面行的新值”和“后面行的旧值”。本页每次外层修正把 delta 重新置零;同一次修正内的第二轮 GS 则保留第一轮结果。默认一轮 GS 不等于精确求逆。

The array delta holds the correction Δu\Delta u. Within a GS row update, one array holds new values for visited rows and old values for unvisited rows. Each outer correction starts delta at zero; a second inner sweep retains the first sweep’s result. The default single sweep is not an exact inverse.

下面的动画把原始 GS 缺陷校正画成曲线。先逐行播放第一次修正:正向 GS 扫描从第 0 行走到最后一行,已经更新的一段先离开初值;之后每帧是一次外层迭代。虚线是收敛解 u∗u^*,右边是残差 ∥R(uk)∥2\|R(u^k)\|_2 的历史,下方的柱是各控制体的误差(对数刻度)。右图竖虚线是页面求解器按停止准则结束的位置;动画继续迭代,残差停在舍入平台上,说明迭代确实收敛到了机器精度。比较入口和出口两侧的柱哪边先变短:原始信息沿特征速度 u>0u>0 从入口流向出口。

The animation below draws the primal GS defect correction as a curve. The first correction is played row by row: the forward GS sweep runs from row 0 to the last row, and the updated part leaves the initial value first. After that, each frame is one outer iteration. The dashed curve is the converged solution u∗u^*, the right panel is the history of ∥R(uk)∥2\|R(u^k)\|_2, and the bars below show the error in each control volume on a log scale. The vertical dashed line on the right marks where the page's solver stops by its criterion; the animation keeps iterating until the residual rests on its round-off floor, which shows that the iteration has converged to machine precision. Compare which side of the bars shrinks first: primal information travels with the characteristic speed u>0u>0 from the inlet to the outlet.

符号提示:线性系统中的 AA(按空间阶数写作 AqA_q)是残差 Jacobian;P=A1(u)P=A_1(u) 是一阶近似矩阵;BsB_s 是从零修正开始、固定 ss 轮 GS 扫描构成的线性映射,一般不等于 P−1P^{-1}。

Notation: AA in a linear system (written AqA_q for spatial order qq) is the residual Jacobian; P=A1(u)P=A_1(u) is the first-order approximate matrix; BsB_s is the linear map of ss GS sweeps started from a zero correction, generally not P−1P^{-1}.

第 7 节Section 7

目标函数:希望解变成什么样

The objective: the field we want to match

把整条曲线的差异变成一个数

Turn a field mismatch into one number

如果要调整源项,让当前解接近给定目标场,就需要一个衡量差异的标量。平方差不受误差正负抵消影响,控制体宽度 WiW_i 使求和近似空间积分。前面的 1/21/2 只为了求导时消去平方带来的系数 2。

To adjust sources toward a target field, measure the mismatch by a scalar. Squaring prevents positive and negative errors from cancelling; volume weights WiW_i make the sum approximate an integral. The factor 1/21/2 cancels the factor 2 when differentiating the square.

J˙=12∑iWi 2(ui−uitgt)u˙i=∑igivi=gTv.\dot J=\frac12\sum_iW_i\,2(u_i-u^{\mathrm{tgt}}_i)\dot u_i=\sum_i g_i v_i=g^Tv.

这里 g=Jug=J_u 只描述目标函数对状态的直接敏感度,还不是对源项的梯度;目标场 utgtu^{\mathrm{tgt}} 冻结,没有切向量。源项改变后,所有状态会通过平衡方程联动;我们还需要计算 v=u˙v=\dot u,或用伴随把这层依赖消掉。

The vector g=Jug=J_u describes direct sensitivity to the state, not yet to the sources; the target field utgtu^{\mathrm{tgt}} is frozen and has no tangent. A source change affects the state through the balance equations. We must either compute v=u˙v=\dot u or use an adjoint to eliminate that dependence.

目标是让解接近目标源项 sin⁡(πx)\sin(\pi x) 产生的流场。每种离散格式先求出自己的目标场 uqtgtu^{\mathrm{tgt}}_q,然后在设计求导时保持它不变。切换阶数或边界格式会重建目标场;当前设计改变时它不重新求解。

The inverse objective matches the field generated by sin⁡(πx)\sin(\pi x). Each discretisation first computes its own target field uqtgtu^{\mathrm{tgt}}_q, which is then frozen during design differentiation. Changing spatial order or boundary closure rebuilds this target; changing the current design does not.

Jq(u)=12∑iWi(ui−uq,itgt)2,gi=∂Jq∂ui=Wi(ui−uq,itgt).J_q(u)=\frac12\sum_i W_i(u_i-u^{\mathrm{tgt}}_{q,i})^2,\qquad g_i=\frac{\partial J_q}{\partial u_i}=W_i(u_i-u^{\mathrm{tgt}}_{q,i}).

因此,在目标设计处,离散 JqJ_q 和梯度同时为零。第 15 节的精度比较另用统一连续参考,防止把各网格自己的目标当作误差基准。

Thus the discrete objective and gradient vanish at the target design. Section 15 uses a shared continuous reference for its accuracy comparisons, rather than treating each grid’s discrete target as its own error reference.

目标函数与状态梯度

Objective and state gradient

target = solve_primal(target_source, prescribed_inlet)  # compute once
J = 0
for i = 0,...,M-1:
    mismatch = u[i] - target[i]
    J += 0.5 * W[i] * mismatch*mismatch
    g[i] = W[i] * mismatch
# Reuse this frozen target for every design change.

从离散目标得到伴随右端

From a discrete objective to the adjoint right-hand side

不能只对连续积分写一个形式导数就结束。先确定代码实际使用的求积点、权重、重构状态及边界值,再对这个标量表达式求导。一般的求积和及其链式法则是:

A formal derivative of a continuous integral is not enough. First identify the quadrature points, weights, reconstructed states and boundary values actually used by the code, then differentiate that scalar expression. A general quadrature sum and its chain rule are:

J=∑iωi(D) θi(𝐮,D),J=\sum_i\omega_i(D)\,\theta_i(\mathbf u,D),
J˙=∑i[ωi θ˙i+θi ω˙i].\dot J=\sum_i\left[\omega_i\,\dot\theta_i+\theta_i\,\dot\omega_i\right].

平方跟踪目标是它的特例:θi=12ei2\theta_i=\tfrac12e_i^2,eie_i 是被跟踪量减去它的目标值,于是 θ˙i=ei e˙i\dot\theta_i=e_i\,\dot e_i;e˙i\dot e_i 在被跟踪量的切向量之外,只多出目标值本身的切向量(目标冻结时为零)。

A squared tracking objective is the special case θi=12ei2\theta_i=\tfrac12e_i^2, where eie_i is the tracked quantity minus its target; then θ˙i=ei e˙i\dot\theta_i=e_i\,\dot e_i, and e˙i\dot e_i adds to the tangent of the tracked quantity only the tangent of the target itself (zero for a frozen target).

本页第 7 节的跟踪目标 Jq=12∑iWi(ui−uq,itgt)2J_q=\tfrac12\sum_iW_i(u_i-u^{\mathrm{tgt}}_{q,i})^2 正是平方跟踪型:求积点就是存储点,ωi=Wi\omega_i=W_i,θi=12(ui−uq,itgt)2\theta_i=\tfrac12(u_i-u^{\mathrm{tgt}}_{q,i})^2;目标场 uqtgtu^{\mathrm{tgt}}_q 冻结,所以 θ˙i=(ui−uq,itgt) u˙i\dot\theta_i=(u_i-u^{\mathrm{tgt}}_{q,i})\,\dot u_i。它不显式依赖 DD,所以 ∇DJ=0\nabla_DJ=0。

The tracking objective Jq=12∑iWi(ui−uq,itgt)2J_q=\tfrac12\sum_iW_i(u_i-u^{\mathrm{tgt}}_{q,i})^2 of Section 7 is exactly of this form: the quadrature points are the stored points, ωi=Wi\omega_i=W_i and θi=12(ui−uq,itgt)2\theta_i=\tfrac12(u_i-u^{\mathrm{tgt}}_{q,i})^2; the target field uqtgtu^{\mathrm{tgt}}_q is frozen, so θ˙i=(ui−uq,itgt) u˙i\dot\theta_i=(u_i-u^{\mathrm{tgt}}_{q,i})\,\dot u_i. It has no explicit dependence on DD, so ∇DJ=0\nabla_DJ=0.

求 gg 时固定所有设计变量;求 ∇DJ\nabla_DJ 时固定全部离散状态槽位。本页网格不随设计变化,采样点固定,所以没有位置依赖项。目标中的体积权重已经进入 gg,不应在伴随右端再乘一次体积。

Hold every design variable fixed for gg, and every stored state entry fixed for ∇DJ\nabla_DJ. The mesh of this page does not change with the design, so the samples are fixed and there is no location dependence. Objective volume weights already enter gg and must not be multiplied into the adjoint right-hand side again.

伴随右端本身也可以由一次反向扫描得到:以 Jˉ=1\bar J=1 为种子,把上面的求积和逐项反过来执行,

The adjoint right-hand side is itself obtained by a reverse sweep: seed Jˉ=1\bar J=1 and run the terms of the quadrature sum above backwards,

Jˉ=1,θˉi=ωi Jˉ,𝐮ˉ+=(∂θi∂𝐮)Tθˉi,\bar{J}=1,\qquad \bar\theta_i=\omega_i\,\bar{J},\qquad \bar{\mathbf u}\mathrel{\text{+=}}\Big(\frac{\partial \theta_i}{\partial \mathbf u}\Big)^T\bar\theta_i,
Dˉ+=(∂θi∂D)Tθˉi+θi(∂ωi∂D)TJˉ.\bar D\mathrel{\text{+=}}\Big(\frac{\partial \theta_i}{\partial D}\Big)^T\bar\theta_i+\theta_i\Big(\frac{\partial\omega_i}{\partial D}\Big)^T\bar{J}.

从 𝐮ˉ←0\bar{\mathbf u}\leftarrow0、Dˉ←0\bar D\leftarrow0 开始,扫完全部求积点后 𝐮ˉ=g\bar{\mathbf u}=g、Dˉ=∇DJ\bar D=\nabla_DJ:一个状态被多个求积点读取,所以要先置零、再累加。前向的 J˙\dot J 与反向的这两个结果满足同一个对偶关系 Jˉ J˙=𝐮ˉT𝐮˙+DˉTD˙\bar J\,\dot J=\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D。

Starting from 𝐮ˉ←0\bar{\mathbf u}\leftarrow0 and Dˉ←0\bar D\leftarrow0, after all quadrature points 𝐮ˉ=g\bar{\mathbf u}=g and Dˉ=∇DJ\bar D=\nabla_DJ: a state is read by several quadrature points, so its adjoint is zeroed first and then accumulated. The forward J˙\dot J and these two reverse results satisfy one duality, Jˉ J˙=𝐮ˉT𝐮˙+DˉTD˙\bar J\,\dot J=\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D.

第 8 节Section 8

前向:沿着面循环传播切向量

Forward: propagate tangents through the face loop

本节实验:一次 tangent 计算给出残差对指定方向的变化,不是已经求出了状态对设计的敏感度。比较第 10 节的线性求解。

Experiment: One tangent application gives the residual change along a supplied direction. It is not yet the solved state sensitivity; compare the linear solve in Section 10.

矩阵乘法也可以写成方向导数

A matrix product can be evaluated as a directional derivative

令 u(α)=u+αvu(\alpha)=u+\alpha v,暂时固定设计输入 DD(D˙=0\dot D=0)。向量 vv 就是状态的切向量 u˙\dot u:它规定所有未知量同时沿哪个方向变化。对每个残差关于 α\alpha 求导,就得到残差的切向量 R˙=Av\dot R=Av。这只需要沿原来的计算过程应用链式法则,不必先存下整个矩阵;这就是 matrix-free(不显式组装矩阵)的含义,也就是自动微分的前向(tangent)模式:按原计算顺序,为每个中间量 xx 算出它的切向量 x˙\dot x。

Set u(α)=u+αvu(\alpha)=u+\alpha v while holding the design input DD fixed (D˙=0\dot D=0). The vector vv is the state tangent u˙\dot u: it specifies the direction in which all unknowns change together. Differentiating each residual with respect to α\alpha gives the residual tangent R˙=Av\dot R=Av. Apply the chain rule along the original computation without storing the full matrix: this is matrix-free evaluation, the forward (tangent) mode of automatic differentiation, which computes the tangent x˙\dot x of every intermediate xx in the order of the original computation.

记号:上点 x˙\dot x 与上横线 xˉ\bar x 是对离散程序求的前向与反向导数,与 Tapenade 等 AD 工具的变量 𝚡_𝚍\texttt{x\_d}、𝚡_𝚋\texttt{x\_b} 一一对应,下面的伪代码就这样命名。变分符号 δ\delta 只在附录 A 中出现,表示连续函数的变分。

Notation: the dot x˙\dot x and the bar xˉ\bar x are forward- and reverse-mode derivatives of the discrete program. They correspond one to one to the variables 𝚡_𝚍\texttt{x\_d} and 𝚡_𝚋\texttt{x\_b} of AD tools such as Tapenade, and the pseudocode below uses these names. The variation symbol δ\delta appears only in Appendix A, for variations of continuous functions.

R˙i=(Av)i=ddαRi(u+αv,D)∣α=0=∑j∂Ri∂ujvj.\dot R_i=(Av)_i=\left.\frac{d}{d\alpha}R_i(u+\alpha v,D)\right|_{\alpha=0}=\sum_j\frac{\partial R_i}{\partial u_j}v_j.
图 9 · 前向传播:状态切向量经过重构、通量偏导,最后变成残差切向量。状态切向量u˙​ = v面值切向量ȧ = T_L vḃ = T_R v通量切向量Ḣ = cL ȧ + cR ḃ残差切向量左加 Ḣ右减 Ḣ
图 9 · 前向传播:状态切向量经过重构、通量偏导,最后变成残差切向量。
Figure 9 · Forward propagation: the state tangent passes through reconstruction and flux derivatives to become the residual tangent.State tangentu˙​ = vTrace tangentsȧ = T_L vḃ = T_R vFlux tangentḢ = cL ȧ + cR ḃResidual tangentleft: +Ḣright: −Ḣ
Figure 9 · Forward propagation: the state tangent passes through reconstruction and flux derivatives to become the residual tangent.

先看最小的一步:H(a,b)=(a2+b2)/4−ε(b−a)H(a,b)=(a^2+b^2)/4-\varepsilon(b-a)。分别对两个输入求偏导,再乘各自的切向量并相加,就是 H˙\dot H。在二阶格式中,a˙,b˙\dot a,\dot b 又来自重构,所以必须继续用链式法则传播到模板上的每个 vjv_j。

Start with the smallest operation, H(a,b)=(a2+b2)/4−ε(b−a)H(a,b)=(a^2+b^2)/4-\varepsilon(b-a). Differentiate with respect to each input, multiply by its tangent, then add to obtain H˙\dot H. At second order, the trace tangents a˙,b˙\dot a,\dot b themselves come from reconstruction, so continue the chain rule to every stencil value vjv_j.

cL=∂H∂a=a2+ε,cR=∂H∂b=b2−ε,H˙=cLa˙+cRb˙.c_L=\frac{\partial H}{\partial a}=\frac a2+\varepsilon,\quad c_R=\frac{\partial H}{\partial b}=\frac b2-\varepsilon,\quad\dot H=c_L\dot a+c_R\dot b.

手算一个面:取 a=1.1,b=1.2,ε=0.8a=1.1,b=1.2,\varepsilon=0.8,则 H=0.5825H=0.5825、cL=1.35,cR=−0.2c_L=1.35,c_R=-0.2。给切向量 a˙=0.3,b˙=−0.1\dot a=0.3,\dot b=-0.1,得到 H˙=0.425\dot H=0.425。左残差的切向量加 0.4250.425,右残差的切向量减 0.4250.425。这是一组局部演算数据,不是下方收敛流场中的固定面值。

Work one face by hand: with a=1.1,b=1.2,ε=0.8a=1.1,b=1.2,\varepsilon=0.8, obtain H=0.5825H=0.5825, cL=1.35,cR=−0.2c_L=1.35,c_R=-0.2. The tangents a˙=0.3,b˙=−0.1\dot a=0.3,\dot b=-0.1 give H˙=0.425\dot H=0.425. Add this to the tangent of the left residual and subtract it from the right one. These are illustrative local values, not fixed values of a face in the converged field below.

从状态切向量到通量切向量

From state tangents to flux tangents

令状态的切向量 𝐮˙=v\dot{\mathbf u}=v,设计固定(D˙=0\dot D=0)。无斜率限制器时,重构矩阵与状态无关,因此 q˙L=TLv\dot q_L=T_Lv、q˙R=TRv\dot q_R=T_Rv。链式法则必须经过重构、通量和分发三步。

Let the state tangent be 𝐮˙=v\dot{\mathbf u}=v with the design fixed (D˙=0\dot D=0). Without a limiter, reconstruction matrices are state independent, so q˙L=TLv\dot q_L=T_Lv and q˙R=TRv\dot q_R=T_Rv. The chain rule must pass through reconstruction, flux evaluation and residual scatter.

R˙=Aqv=ℬ[diag⁡(cL)TLv+diag⁡(cR)TRv].\dot R=A_qv=\mathcal B\left[\operatorname{diag}(c_L)T_Lv+\operatorname{diag}(c_R)T_Rv\right].

二阶时,一个面依赖哪些未知量

Which unknowns a second-order face depends on

对一个内部二阶面,重构系数和该面通量的完整导数如下。这里 cL,cRc_L,c_R 在重构面值上计算,不能在未经重构的中心值上计算。

For an interior second-order face, the reconstruction and complete flux derivative are as follows. Evaluate cL,cRc_L,c_R at the reconstructed face states, not at the unreconstructed centre values.

qL=−14ui−1+ui+14ui+1,qR=14ui+ui+1−14ui+2.q_L=-\frac14u_{i-1}+u_i+\frac14u_{i+1},\qquad q_R=\frac14u_i+u_{i+1}-\frac14u_{i+2}.
H˙f=−cL4vi−1+(cL+cR4)vi+(cL4+cR)vi+1−cR4vi+2.\dot H_f=-\frac{c_L}{4}v_{i-1}+\left(c_L+\frac{c_R}{4}\right)v_i+\left(\frac{c_L}{4}+c_R\right)v_{i+1}-\frac{c_R}{4}v_{i+2}.

边界附近的二阶迹用单边斜率:入口内侧迹 uinR=(3u0−u1)/2u_{in}^R=(3u_0-u_1)/2,第一内部面左迹 qL=(u0+u1)/2q_L=(u_0+u_1)/2,最后内部面右迹 qR=(uM−2+uM−1)/2q_R=(u_{M-2}+u_{M-1})/2,出口迹 uout=(3uM−1−uM−2)/2u_{out}=(3u_{M-1}-u_{M-2})/2;一阶时每个迹都取所在单元的值。出口物理通量的切向量为 uoutu˙outu_{out}\dot u_{out},u˙out\dot u_{out} 按同一组权重传播。下面的播放器使用这些边界模板;上面的四点式只用于内部面。

Near the boundaries the second-order traces use one-sided slopes: inlet interior trace uinR=(3u0−u1)/2u_{in}^R=(3u_0-u_1)/2, left trace of the first interior face qL=(u0+u1)/2q_L=(u_0+u_1)/2, right trace of the last interior face qR=(uM−2+uM−1)/2q_R=(u_{M-2}+u_{M-1})/2, and outlet trace uout=(3uM−1−uM−2)/2u_{out}=(3u_{M-1}-u_{M-2})/2; at first order each trace is the value of its cell. The outlet physical-flux tangent is uoutu˙outu_{out}\dot u_{out}, with u˙out\dot u_{out} propagated through the same weights. The player below uses these boundary stencils; the four-point formula above applies only to interior faces.

播放器中的 vj=sin⁡(j+0.3)v_j=\sin(j+0.3) 是为了观察乘积而指定的测试种子 u˙=v\dot u=v,还不是第 10 节要求解的物理前向解。若选择单位向量 v=ejv=e_j,结果就是 Jacobian 的第 jj 列;一般 vv 是这些列的线性组合。伪代码就是 AD 工具生成的前向代码 𝚛𝚎𝚜𝚒𝚍𝚞𝚊𝚕_𝚍\texttt{residual\_d} 的样子:输入种子 𝚞_𝚍≡u˙\texttt{u\_d}\equiv\dot u,输出 𝚛𝚎𝚜_𝚍≡R˙\texttt{res\_d}\equiv\dot R。

The player uses the test seed u˙=v\dot u=v with vj=sin⁡(j+0.3)v_j=\sin(j+0.3) to illustrate a product. It is not the physical tangent solution of Section 10. Choosing v=ejv=e_j would return column jj of the Jacobian; a general vector combines its columns. The pseudocode is what an AD tool generates as forward code 𝚛𝚎𝚜𝚒𝚍𝚞𝚊𝚕_𝚍\texttt{residual\_d}: input seed 𝚞_𝚍≡u˙\texttt{u\_d}\equiv\dot u, output 𝚛𝚎𝚜_𝚍≡R˙\texttt{res\_d}\equiv\dot R.

逐句求导:残差代码的前向语句

The residual code differentiated statement by statement

前向模式对第 5 节残差代码的每一条赋值 y=ϕ(x1,…,xm)y=\phi(x_1,\ldots,x_m) 用一次链式法则,得到 y˙=∑k(∂ϕ/∂xk) x˙k\dot y=\sum_k(\partial\phi/\partial x_k)\,\dot x_k,放在原语句之后、按同样的顺序执行;本节末尾的推导把这条规则用到面循环的四条语句上。下表把它用到本页的每一条语句,同时允许状态种子 u˙\dot u 与设计种子 D˙\dot D:

Forward mode applies the chain rule once to every assignment y=ϕ(x1,…,xm)y=\phi(x_1,\ldots,x_m) of the residual code of Section 5, giving y˙=∑k(∂ϕ/∂xk) x˙k\dot y=\sum_k(\partial\phi/\partial x_k)\,\dot x_k, placed after it and executed in the same order; the derivation at the end of this section applies the rule to the four statements of the face loop. The table applies it to every statement of this page's code, with a state seed u˙\dot u and a design seed D˙\dot D both allowed:

#原始语句Primal statement前向语句(同一顺序)Tangent statement (same order)用到的局部导数Local derivative used
1R←0R\leftarrow0R˙←0\dot R\leftarrow0—
2a=∑j(TL)fjuja=\sum_j(T_L)_{fj}u_j;入口面 a=uina=u_{in}; inlet face: a=uina=u_{in}a˙=∑j(TL)fju˙j\dot a=\sum_j(T_L)_{fj}\dot u_j;入口面 a˙=u˙in\dot a=\dot u_{in}; inlet face: a˙=u˙in\dot a=\dot u_{in}(TL)fj(T_L)_{fj}
3b=∑j(TR)fjujb=\sum_j(T_R)_{fj}u_j;出口面 b=ab=a; outlet face: b=ab=ab˙=∑j(TR)fju˙j\dot b=\sum_j(T_R)_{fj}\dot u_j;出口面 b˙=a˙\dot b=\dot a; outlet face: b˙=a˙\dot b=\dot a(TR)fj(T_R)_{fj}
4Hf=H(a,b)H_f=H(a,b)H˙f=cLa˙+cRb˙\dot H_f=c_L\dot a+c_R\dot bcL, cRc_L,\ c_R(物理通量 cL=ac_L=a,cR=0c_R=0) (physical flux: cL=ac_L=a, cR=0c_R=0)
5RL+=HfR_L\mathrel{\text{+=}}H_fR˙L+=H˙f\dot R_L\mathrel{\text{+=}}\dot H_f11
6RR-=HfR_R\mathrel{\text{-=}}H_fR˙R-=H˙f\dot R_R\mathrel{\text{-=}}\dot H_f−1-1
7Ri-=Si, Si=hσiR_i\mathrel{\text{-=}}S_i,\ S_i=h\sigma_iR˙i-=S˙i, S˙i=hσ˙i\dot R_i\mathrel{\text{-=}}\dot S_i,\ \dot S_i=h\dot\sigma_i∂Si/∂σi=h\partial S_i/\partial\sigma_i=h

前向代码为什么按原顺序执行,见本节末尾的推导;除了当前面上的原始值 cL,cRc_L,c_R,不需要额外保存任何东西。设计只在两处进入:入口状态(格心页第 2 条语句的入口面)和源项(第 7 条)。取 u˙=v\dot u=v、D˙=0\dot D=0,这段代码返回 AvAv;取 u˙=0\dot u=0、D˙=ek\dot D=e_k,返回 RDR_D 的一列 f=RD ekf=R_D\,e_k,也就是第 10 节切向方程 Av=−fAv=-f 里的向量 ff(方程的右端是 −f-f;这个 ff 是向量,与面的下标 ff 无关)。

Why the tangent code runs in the order of the primal code is explained in the derivation at the end of this section; nothing has to be stored beyond the primal values cL,cRc_L,c_R at the current face. The design enters in two places only: the inlet state (the inlet face of statement 2 on the cell page) and the sources (statement 7). With u˙=v\dot u=v and D˙=0\dot D=0 the code returns AvAv; with u˙=0\dot u=0 and D˙=ek\dot D=e_k it returns one column f=RD ekf=R_D\,e_k of RDR_D, the vector ff of the tangent equation Av=−fAv=-f of Section 10 (whose right-hand side is −f-f; this ff is a vector, not the face label ff).

用本页的数值走一遍一个面:原始与前向

One face with the page’s own numbers: primal and tangent

取默认网格(N=8N=8)、设计种子 D˙=e0\dot D=e_0(σ0\sigma_0 增加一个单位),u˙=v\dot u=v 取第 10 节前向求解的收敛解,看面 f4f_{4}(x=0.5x=0.5,左右是单元 3、4)。下表是它的原始量与切向量;二阶时每个迹由三个未知量重构,所以 a˙,b˙\dot a,\dot b 也各由三个 vjv_j 组合。本节末尾的推导对同一个面取设计种子 D˙=ek\dot D=e_k(DkD_k 是入口值 uinu_{in}),所以那里的切向量与下表不同,原始值相同:

Take the default mesh (N=8N=8), the design seed D˙=e0\dot D=e_0 (a unit increase of σ0\sigma_0) and for u˙=v\dot u=v the converged solution of the tangent solve of Section 10, and look at face f4f_{4} (x=0.5x=0.5, between cells 3 and 4). The table lists its primal and tangent values; at second order each trace is reconstructed from three unknowns, so a˙,b˙\dot a,\dot b each combine three vjv_j as well. The derivation at the end of this section takes the same face with the design seed D˙=ek\dot D=e_k, where DkD_k is the inlet value uinu_{in}, so its tangents differ from the table's while the primal values are the same:

量Quantity公式Formula一阶First order二阶Second order
a=qLa=q_L∑j(TL)fjuj\sum_j(T_L)_{fj}u_j1.2182781.2182781.2241681.224168
b=qRb=q_R∑j(TR)fjuj\sum_j(T_R)_{fj}u_j1.2673971.2673971.2241101.224110
HfH_fH(a,b)H(a,b)0.7333290.7333290.7493050.749305
cLc_La/2+εa/2+\varepsilon1.4091391.4091391.4120841.412084
cRc_Rb/2−εb/2-\varepsilon−0.166302-0.166302−0.187945-0.187945
a˙\dot a∑j(TL)fjvj\sum_j(T_L)_{fj}v_j0.0802790.0802790.0787700.078770
b˙\dot b∑j(TR)fjvj\sum_j(T_R)_{fj}v_j0.0773650.0773650.0787900.078790
H˙f\dot H_fcLa˙+cRb˙c_L\dot a+c_R\dot b0.1002580.1002580.0964210.096421

所有面的 H˙f\dot H_f 放在一起还是一个检查。vv 满足切向方程时,每个控制体的切向残差 R˙i=H˙i+1/2−H˙i−1/2−S˙i\dot R_i=\dot H_{i+1/2}-\dot H_{i-1/2}-\dot S_i 都为零,所以面通量的切向量只在注入源项的控制体处跳变 S˙i\dot S_i,其余处处相等。这里种子只加在单元 0上,于是一阶时 H˙f\dot H_f 在第一个面上是 −0.024742-0.024742,其后每个面都是 0.1002580.100258,差值正好是 h=0.125h=0.125。图 13用中间的源项 σ3\sigma_{3} 把这个过程做成动画。

The H˙f\dot H_f of all faces together make one more check. When vv solves the tangent equation, the tangent residual R˙i=H˙i+1/2−H˙i−1/2−S˙i\dot R_i=\dot H_{i+1/2}-\dot H_{i-1/2}-\dot S_i of every control volume vanishes, so the face-flux tangent jumps by S˙i\dot S_i only where the seed injects source and is equal everywhere else. Here the seed acts on cell 0 only, so at first order H˙f\dot H_f is −0.024742-0.024742 on the first face and 0.1002580.100258 on every face after it: the difference is exactly h=0.125h=0.125. Figure 13 animates this for the middle source σ3\sigma_{3}.

局部导数怎样组成全局 Jacobian

How local derivatives form the global Jacobian

固定网格和当前可微分支,把一个面的所有输入记为 zfz_f;它包括两侧的重构状态,入口面上还有入口值 uinu_{in}。令 TfT_f、KfK_f 分别是 zfz_f 对存储状态和设计变量的导数,Hz,fH_{z,f} 是局部通量 Jacobian。于是每个残差行的导数按同一个有向面关联矩阵 ℬ\mathcal B 装配,这里的求和包括相应的边界通量映射。本页没有被替换的行,边界条件全部在边界面的通量里。前向模式按计算顺序传播切向量,状态种子是 v≡𝐮˙v\equiv\dot{\mathbf u},设计种子是 D˙\dot D:

At fixed mesh and differentiable branch, let zfz_f collect all face inputs: the reconstructed states of both sides and, on the inlet face, the inlet value uinu_{in}. Let TfT_f and KfK_f be the derivatives of zfz_f with respect to the stored states and the design variables, and Hz,fH_{z,f} the local flux Jacobian. Assemble each residual derivative with the same oriented face incidence ℬ\mathcal B, including the boundary flux maps. This page replaces no rows: every boundary condition sits in a boundary-face flux. Forward mode carries the tangents in the order of the computation, with the state seed v≡𝐮˙v\equiv\dot{\mathbf u} and the design seed D˙\dot D:

z˙f=Tfv+KfD˙,H˙f=Hz,fz˙f,\dot z_f=T_fv+K_f\dot D,\quad\dot H_f=H_{z,f}\dot z_f,
R˙i=∑fℬifH˙f−S˙i,\dot R_i=\sum_f\mathcal B_{if}\dot H_f-\dot S_i,
(A)ij=∑fℬifHz,f(Tf)j−∂Si∂uj,(A)_{ij}=\sum_f\mathcal B_{if}H_{z,f}(T_f)_j-\frac{\partial S_i}{\partial u_j},
(RD)ik=∑fℬifHz,f(Kf)k−∂Si∂Dk.(R_D)_{ik}=\sum_f\mathcal B_{if}H_{z,f}(K_f)_k-\frac{\partial S_i}{\partial D_k}.

无体源项时 Si=0S_i=0。本页的设计变量不改变几何:源项与入口值的依赖进入 KfK_f 与 SiS_i。二阶重构使 TfT_f 连接更多邻居,因此精确 Jacobian 的模板变宽;matrix-free 仍只需依次执行这些局部乘积,不必存储全局矩阵。

Set Si=0S_i=0 when there is no volume source. The design variables of this page do not change the geometry: the dependence on the sources and the inlet value enters KfK_f and SiS_i. Second-order reconstruction connects TfT_f to more neighbours and widens the exact Jacobian stencil. Matrix-free evaluation performs these local products without storing the global matrix.

从程序到切向量:面循环的四条语句逐条线性化

From program to tangent: linearise the four statements of the face loop one by one

上面的 z˙f\dot z_f、H˙f\dot H_f 与 R˙i\dot R_i 来自同一段短程序:残差代码对每个面依次执行四条语句。设 𝒮f\mathcal S_f 是面 ff 读取的存储位置(两侧的 LL、RR,以及重构需要时它们的邻居),把这些位置上的状态排成 𝐮f\mathbf u_f:

The z˙f\dot z_f, H˙f\dot H_f and R˙i\dot R_i above come from one short program: the residual code runs four statements on every face. Let 𝒮f\mathcal S_f be the stored locations that face ff reads (its two sides LL and RR, and their neighbours when the reconstruction needs them), and stack the states stored there as 𝐮f\mathbf u_f:

(1)𝐮f=𝒢f 𝐮,(1)\quad \mathbf u_f=\mathcal G_f\,\mathbf u,
(2)zf=zf(D,𝐮f),(2)\quad z_f=z_f(D,\mathbf u_f),
(3)Hf=H(zf),(3)\quad H_f=H(z_f),
(4)R+=ℬ:,f Hf.(4)\quad R\mathrel{\text{+=}}\mathcal B_{:,f}\,H_f.

(1)取值:𝒢f\mathcal G_f 的每一行只有一个 1,从全局数组里挑出 𝒮f\mathcal S_f 上的状态;(2)重构:由这些状态和设计变量算出面的全部输入 zfz_f,在本页是面两侧的状态 aa、bb(本页通量 H(a,b)H(a,b) 的两个参数;一阶时 Zf=IZ_f=I);(3)通量:Hf=H(zf)H_f=H(z_f);(4)分发:ℬ:,f\mathcal B_{:,f} 是有向关联矩阵 ℬ\mathcal B 的第 ff 列,在第 LL 行为 +1+1、第 RR 行为 −1-1,即 RL+=HfR_L\mathrel{\text{+=}}H_f、RR-=HfR_R\mathrel{\text{-=}}H_f(边界面只写一行)。这四条语句是残差代码的骨架;本页代码里的每一条语句(含边界面与源项)都在本节里逐句写出,并附各自的切向量语句。

(1) Gather: each row of 𝒢f\mathcal G_f holds a single 1 and picks one state of 𝒮f\mathcal S_f out of the global array. (2) Reconstruct: the states and the design give all inputs zfz_f of the face, on this page the face states aa and bb (the two arguments of this page's flux H(a,b)H(a,b); at first order Zf=IZ_f=I). (3) Flux: Hf=H(zf)H_f=H(z_f). (4) Scatter: ℬ:,f\mathcal B_{:,f} is column ff of the oriented incidence ℬ\mathcal B, +1+1 in row LL and −1-1 in row RR, that is RL+=HfR_L\mathrel{\text{+=}}H_f and RR-=HfR_R\mathrel{\text{-=}}H_f (a boundary face writes one row). These four statements are the skeleton of the residual code; every statement of this page's code, boundary faces and sources included, is written out with its tangent statement in this section.

前向模式对每条语句用一次链式法则:每条语句输出的切向量是它输入的切向量的线性组合,每个中间变量都有自己的切向量:

Forward mode applies the chain rule once per statement: the tangent of each output is a linear combination of the tangents of the statement's inputs, and every intermediate variable gets a tangent of its own:

(1)𝐮˙f=𝒢f 𝐮˙,(1)\quad \dot{\mathbf u}_f=\mathcal G_f\,\dot{\mathbf u},
(2)z˙f=Zf 𝐮˙f+Kf D˙,(2)\quad \dot z_f=Z_f\,\dot{\mathbf u}_f+K_f\,\dot D,
(3)H˙f=Hz,f z˙f,(3)\quad \dot H_f=H_{z,f}\,\dot z_f,
(4)R˙+=ℬ:,f H˙f,(4)\quad \dot R\mathrel{\text{+=}}\mathcal B_{:,f}\,\dot H_f,
Zf=∂zf∂𝐮f,Kf=∂zf∂D,Hz,f=∂H∂zf.Z_f=\frac{\partial z_f}{\partial \mathbf u_f},\qquad K_f=\frac{\partial z_f}{\partial D},\qquad H_{z,f}=\frac{\partial H}{\partial z_f}.

取值与分发都是线性的,它们的切向量就是同样的取值与分发作用在切向量上;只有重构与通量两条语句带来依赖当前状态的局部导数 ZfZ_f、KfK_f 与 Hz,fH_{z,f}。把(1)代入(2),(2)代入(3),(3)代入(4),对所有面求和,再减去体源项的切向量 S˙\dot S,就逐条语句得到 R˙=A𝐮˙+RDD˙\dot R=A\dot{\mathbf u}+R_D\dot D:

Gather and scatter are linear: their tangents are the same gather and scatter applied to the tangents. Only the reconstruct and flux statements bring local derivatives that depend on the current state: ZfZ_f, KfK_f and Hz,fH_{z,f}. Substituting (1) into (2), (2) into (3) and (3) into (4), summing over all faces and subtracting the tangent S˙\dot S of the volume source assembles R˙=A𝐮˙+RDD˙\dot R=A\dot{\mathbf u}+R_D\dot D statement by statement:

R˙=∑fℬ:,fHz,f(Zf𝒢f 𝐮˙+KfD˙)−S˙\dot R=\sum_f\mathcal B_{:,f}H_{z,f}\big(Z_f\mathcal G_f\,\dot{\mathbf u}+K_f\dot D\big)-\dot S
=(∑fℬ:,fHz,fZf𝒢f−∂S∂𝐮⏟A)𝐮˙=\Big(\underbrace{\sum_f\mathcal B_{:,f}H_{z,f}Z_f\mathcal G_f-\frac{\partial S}{\partial \mathbf u}}_{A}\Big)\dot{\mathbf u}
+(∑fℬ:,fHz,fKf−∂S∂D⏟RD)D˙.+\Big(\underbrace{\sum_f\mathcal B_{:,f}H_{z,f}K_f-\frac{\partial S}{\partial D}}_{R_D}\Big)\dot D.

上面的 TfT_f 就是 Zf𝒢fZ_f\mathcal G_f。所以一个面对 AA 的贡献是四个初等 Jacobian 的乘积

The TfT_f above is Zf𝒢fZ_f\mathcal G_f. One face therefore contributes to AA the product of four elementary Jacobians,

Af=ℬ:,f Hz,f Zf 𝒢f,A_f=\mathcal B_{:,f}\,H_{z,f}\,Z_f\,\mathcal G_f,
Af𝐮˙=ℬ:,f(Hz,f(Zf(𝒢f 𝐮˙))).A_f\dot{\mathbf u}=\mathcal B_{:,f}\Big(H_{z,f}\Big(Z_f\big(\mathcal G_f\,\dot{\mathbf u}\big)\Big)\Big).

它从右往左作用在 𝐮˙\dot{\mathbf u} 上,与程序执行的顺序相同:先取值,再重构、通量,最后分发。𝒮f\mathcal S_f 只有几个位置,所以 AfA_f 只有一小块非零,matrix-free 乘积就是按面执行这几次局部运算,不存储任何全局矩阵。下面的面板按这四条语句逐条播放本页一个面的数值。

It acts on 𝐮˙\dot{\mathbf u} from right to left, in the order in which the program runs: gather first, then reconstruct and flux, scatter last. 𝒮f\mathcal S_f holds only a few locations, so AfA_f has a small nonzero block, and the matrix-free product runs these local operations face by face without storing any global matrix. The panel below plays the four statements with the numbers of one face of this page.

本页一个面的算例:前向扫描

A worked example on this page: the forward sweep of one face

取本页面循环里单元 3 与 4 之间的内部面 ff(xf=0.5x_f=0.5);一阶重构只读两侧的状态,𝒮f={3,4}\mathcal S_f=\{3,4\}。设计种子取 D˙=ek\dot D=e_k,DkD_k 是入口值 uinu_{in};状态种子取本页前向求解对同一方向给出的解 𝐮˙=v\dot{\mathbf u}=v,于是下面每个切向量都是对 DkD_k 的导数。表中的数由本页代码在构建时算出:基准状态、前向求解与局部导数都用本页自己的程序。

Take the interior face ff of this page's face loop between cells 3 and 4 (xf=0.5x_f=0.5); first-order reconstruction reads only its two sides, 𝒮f={3,4}\mathcal S_f=\{3,4\}. Seed the design with D˙=ek\dot D=e_k, where DkD_k is the inlet value uinu_{in}, and the states with this page's tangent solution for the same direction, 𝐮˙=v\dot{\mathbf u}=v; every tangent below is then a derivative with respect to DkD_k. The numbers are computed by this page's code at build time: its baseline state, its tangent solve and its local derivatives.

语句Statement量Quantity原始值Primal value切向量Tangent
(1)取值(1) gatheru3u_{3}1.2180.8349
u4u_{4}1.2670.8046
设计种子design seedDk=uinD_k=u_{in}11
(2)重构(2) reconstructaa1.2180.8349
bb1.2670.8046
(3)通量(3) fluxHfH_f0.73331.043
(4)分发(4) scatterR3+=HfR_{3}\mathrel{\text{+=}}H_f0.73331.043
R4-=HfR_{4}\mathrel{\text{-=}}H_f−0.7333−1.043

DkD_k 不直接进入这个面:Kfek=0K_fe_k=0,H˙f\dot H_f 全部来自状态的切向量。uinu_{in} 只出现在入口面的通量里,所以 RD ekR_D\,e_k 只有第 0 行非零。

DkD_k does not enter this face directly: Kfek=0K_fe_k=0, and H˙f\dot H_f comes entirely from the state tangents. uinu_{in} appears only in the flux of the inlet face, so RD ekR_D\,e_k has a single nonzero entry, in row 0.

所有面与源项的这些贡献加起来是 Av+RD ekAv+R_D\,e_k,在收敛解上为零:这正是下面前向求解的方程。

Summed over all faces and the source terms, these contributions give Av+RD ekAv+R_D\,e_k, which vanishes at the converged solution: the tangent equation solved further on.

一个面的前向扫描
The forward sweep of one face

按程序顺序执行四条语句,箭头上是各自的初等 Jacobian;下方是它们的乘积与形状(行 × 列,本页这个面的尺寸)。

The four statements in program order, each arrow labelled with its elementary Jacobian; below, their product with the shapes (rows × columns, this face of this page).

全部存储状态all stored statesuu˙​面读取的状态states the face readsuf​u˙​f​面输入:重构值等face inputszf​z˙​f​面通量face fluxHf​H˙​f​两侧残差行rows L and RRL, RR​R˙​L, R˙​R​+=设计输入design inputDD˙​𝒢f​Zf​Hz,f​ℬ:,f​Kf​Af=ℬ:,f​8×1Hz,f​1×2Zf​2×2𝒢f​2×8u˙​R˙​从右往左作用:先取值(最右),最后分发(最左)applied right to left: the gather (rightmost) first, the scatter (leftmost) last全部状态all statesuu˙​模板上的状态stenciluf​u˙​f​面输入face inputszf​z˙​f​面通量face fluxHf​H˙​f​L、R 两行rows L, RRL, RR​R˙​L, R˙​R​+=设计输入design inputDD˙​𝒢f​Zf​Hz,f​ℬ:,f​Kf​Af=ℬ:,f​8×1Hz,f​1×2Zf​2×2𝒢f​2×8u˙​R˙​从右往左作用:applied right to left:先取值,最后分发the gather first, the scatter last
  1. 种子:D˙=ek\dot D=e_k(DkD_k 是入口值 uinu_{in}),𝐮˙=v\dot{\mathbf u}=v。在 𝒮f\mathcal S_f 上:u˙3=\dot u_{3}= 0.8349,u˙4=\dot u_{4}= 0.8046。

    Seeds: D˙=ek\dot D=e_k (DkD_k is the inlet value uinu_{in}) and 𝐮˙=v\dot{\mathbf u}=v. On 𝒮f\mathcal S_f: u˙3=\dot u_{3}= 0.8349, u˙4=\dot u_{4}= 0.8046.

  2. (1)取值:𝐮˙f=𝒢f𝐮˙\dot{\mathbf u}_f=\mathcal G_f\dot{\mathbf u},只挑出 𝒮f\mathcal S_f 上的 2 个状态的切向量(上一步列出的数)。

    (1) Gather: 𝐮˙f=𝒢f𝐮˙\dot{\mathbf u}_f=\mathcal G_f\dot{\mathbf u} picks the tangents of the 2 states of 𝒮f\mathcal S_f (the numbers of the previous step).

  3. (2)重构:z˙f=Zf𝐮˙f+KfD˙\dot z_f=Z_f\dot{\mathbf u}_f+K_f\dot D。a˙=\dot a= 0.8349,b˙=\dot b= 0.8046。Kfek=0K_fe_k=0:DkD_k 不进入这个面。

    (2) Reconstruct: z˙f=Zf𝐮˙f+KfD˙\dot z_f=Z_f\dot{\mathbf u}_f+K_f\dot D. a˙=\dot a= 0.8349, b˙=\dot b= 0.8046. Kfek=0K_fe_k=0: DkD_k does not enter this face.

  4. (3)通量:H˙f=Hz,fz˙f=\dot H_f=H_{z,f}\dot z_f= 1.043。

    (3) Flux: H˙f=Hz,fz˙f=\dot H_f=H_{z,f}\dot z_f= 1.043.

  5. (4)分发:R˙3+=H˙f=\dot R_{3}\mathrel{\text{+=}}\dot H_f= 1.043,R˙4-=H˙f\dot R_{4}\mathrel{\text{-=}}\dot H_f,即加上 −1.043。

    (4) Scatter: R˙3+=H˙f=\dot R_{3}\mathrel{\text{+=}}\dot H_f= 1.043 and R˙4-=H˙f\dot R_{4}\mathrel{\text{-=}}\dot H_f, that is −1.043 added.

第 9 节Section 9

伴随:把同一个面循环倒过来

Adjoint: reverse the same face loop

本节实验:只给一个输出非零权重,预测反向影响能传到哪些输入;共享输入应累加所有路径。

Experiment: Seed one output and predict which inputs receive reverse contributions. Shared inputs accumulate every path.

先转置一个面的局部矩阵

First transpose the local matrix of one face

先忽略重构,一个内部面只有左右两个输入与两个残差贡献。前向局部矩阵如下:第二行是第一行的相反数,因为相同通量在右控制体里带负号。把这个小矩阵转置后作用在残差种子上,就自然得到先作差、再乘通量偏导的伴随步骤。

First omit reconstruction. One interior face has two inputs and two residual contributions. The second row of its forward matrix is the negative of the first because the right volume receives the negative flux. Applying the transpose of this small matrix to the residual seeds naturally gives the adjoint operations: subtract the seeds, then multiply by flux derivatives.

[R˙LR˙R]=[cLcR−cL−cR][a˙b˙],[aˉbˉ]=[cL−cLcR−cR][RˉLRˉR]=[cLcR]Hˉ,Hˉ=RˉL−RˉR.\begin{bmatrix}\dot R_L\\\dot R_R\end{bmatrix}=\begin{bmatrix}c_L&c_R\\-c_L&-c_R\end{bmatrix}\begin{bmatrix}\dot a\\\dot b\end{bmatrix},\qquad \begin{bmatrix}\bar a\\\bar b\end{bmatrix}=\begin{bmatrix}c_L&-c_L\\c_R&-c_R\end{bmatrix}\begin{bmatrix}\bar R_L\\\bar R_R\end{bmatrix}=\begin{bmatrix}c_L\\c_R\end{bmatrix}\bar H,\qquad \bar H=\bar R_L-\bar R_R.

反向代码按与前向相反的顺序执行,每个输入的横线量都用 += 累加:由种子 RˉL=wL\bar R_L=w_L、RˉR=wR\bar R_R=w_R 先得到 Hˉ+=RˉL\bar H\mathrel{\text{+=}}\bar R_L、Hˉ-=RˉR\bar H\mathrel{\text{-=}}\bar R_R,再得到 aˉ+=cLHˉ\bar a\mathrel{\text{+=}}c_L\bar H、bˉ+=cRHˉ\bar b\mathrel{\text{+=}}c_R\bar H。给定第 8 节的局部数值,再取 wL=2,wR=−0.5w_L=2,w_R=-0.5,则 Hˉ=2.5\bar H=2.5、aˉ=3.375,bˉ=−0.5\bar a=3.375,\bar b=-0.5。检查对偶恒等式的两边:RˉLR˙L+RˉRR˙R=wLH˙+wR(−H˙)=1.0625\bar R_L\dot R_L+\bar R_R\dot R_R=w_L\dot H+w_R(-\dot H)=1.0625,而 aˉa˙+bˉb˙=1.0625\bar a\dot a+\bar b\dot b=1.0625。这就是转置关系在一个面上的具体含义。

The reverse code runs in the opposite order of the forward code and accumulates the bar of every input with +=: from the seeds RˉL=wL\bar R_L=w_L and RˉR=wR\bar R_R=w_R it forms Hˉ+=RˉL\bar H\mathrel{\text{+=}}\bar R_L and Hˉ-=RˉR\bar H\mathrel{\text{-=}}\bar R_R, then aˉ+=cLHˉ\bar a\mathrel{\text{+=}}c_L\bar H and bˉ+=cRHˉ\bar b\mathrel{\text{+=}}c_R\bar H. Use the local values from Section 8 and wL=2,wR=−0.5w_L=2,w_R=-0.5: then Hˉ=2.5\bar H=2.5, aˉ=3.375\bar a=3.375 and bˉ=−0.5\bar b=-0.5. Both sides of the duality identity agree: RˉLR˙L+RˉRR˙R=wLH˙+wR(−H˙)=1.0625\bar R_L\dot R_L+\bar R_R\dot R_R=w_L\dot H+w_R(-\dot H)=1.0625 and aˉa˙+bˉb˙=1.0625\bar a\dot a+\bar b\dot b=1.0625. This is the transpose identity on a single face.

图 10 · 伴随传播:从残差端出发,按相反的依赖顺序回到全部状态未知量。残差种子R¯​ = w:wL, wR转置分发H¯​ = wL − wR转置通量ā = cL H¯​b¯​ = cR H¯​转置重构ū += T_LT​ ā + T_RT​ b¯​
图 10 · 伴随传播:从残差端出发,按相反的依赖顺序回到全部状态未知量。
Figure 10 · Adjoint propagation: start at the residual seeds and reverse the dependency chain back to every state unknown.Residual seedsR¯​ = w: wL, wRTranspose scatterH¯​ = wL − wRTranspose fluxā = cL H¯​b¯​ = cR H¯​Transpose tracesū += T_LT​ ā + T_RT​ b¯​
Figure 10 · Adjoint propagation: start at the residual seeds and reverse the dependency chain back to every state unknown.

为什么二阶必须累加到远邻

Why second order reaches beyond the two neighbours

如果 a=−ui−1/4+ui+ui+1/4a=-u_{i-1}/4+u_i+u_{i+1}/4,反向传播就给这三个未知量的伴随分别累加:uˉi−1+=−aˉ/4\bar u_{i-1}\mathrel{\text{+=}}-\bar a/4、uˉi+=aˉ\bar u_i\mathrel{\text{+=}}\bar a、uˉi+1+=aˉ/4\bar u_{i+1}\mathrel{\text{+=}}\bar a/4。右迹也做同样的事。某个未知量可能从多个面、同一面的两条重构路径收到贡献,因此必须使用“+=”,不能后到的贡献覆盖先到的贡献。

For a=−ui−1/4+ui+ui+1/4a=-u_{i-1}/4+u_i+u_{i+1}/4, reverse propagation adds to the adjoints of those three unknowns: uˉi−1+=−aˉ/4\bar u_{i-1}\mathrel{\text{+=}}-\bar a/4, uˉi+=aˉ\bar u_i\mathrel{\text{+=}}\bar a and uˉi+1+=aˉ/4\bar u_{i+1}\mathrel{\text{+=}}\bar a/4. Apply the same rule to the right trace. An unknown can receive contributions from several faces and from both reconstruction paths on one face, so use += rather than overwriting earlier contributions.

从残差权重返回状态权重

From residual weights back to state weights

伴随是同一条计算链按相反顺序的转置。以残差种子 Rˉ=w\bar R=w 出发:先收集每个面上通量的伴随 Hˉf=wL−wR\bar H_f=w_L-w_R;再乘通量偏导,得到左右迹的伴随 qˉL=cLHˉf\bar q_L=c_L\bar H_f、qˉR=cRHˉf\bar q_R=c_R\bar H_f;最后通过 TLT,TRTT_L^T,T_R^T 累加(+=)到所有受影响未知量的伴随 𝐮ˉ\bar{\mathbf u}。不能只把结果放回面两侧的中心。

The adjoint is the transpose of the same chain, run in reverse order. Start from the residual seed Rˉ=w\bar R=w: gather the adjoint of each face flux, Hˉf=wL−wR\bar H_f=w_L-w_R; multiply by the two flux derivatives to get the trace adjoints qˉL=cLHˉf\bar q_L=c_L\bar H_f and qˉR=cRHˉf\bar q_R=c_R\bar H_f; then accumulate (+=) through TLT,TRTT_L^T,T_R^T into the adjoint 𝐮ˉ\bar{\mathbf u} of every affected unknown. Returning contributions only to the two adjacent centres would omit reconstruction derivatives.

𝐮ˉ=AqTw=TLT[cL⊙(ℬTw)]+TRT[cR⊙(ℬTw)],ℬTw=(Hˉf)f.\bar{\mathbf u}=A_q^Tw=T_L^T\left[c_L\odot(\mathcal B^Tw)\right]+T_R^T\left[c_R\odot(\mathcal B^Tw)\right],\qquad \mathcal B^Tw=(\bar H_f)_f.

图 11把 AvAv 与 ATwA^Tw 写成矩阵形式对照。前向模式一次扫描得到 AA 的各列按 vjv_j 加权之和:种子 v=ejv=e_j 恰好取出第 jj 列 ∂R/∂uj\partial R/\partial u_j。反向模式一次扫描得到各行按 wrw_r 加权之和:种子 w=erw=e_r 取出第 rr 行 ∂Rr/∂u\partial R_r/\partial u。所以前向模式适合“少输入、多输出”,反向模式适合“多输入、少输出”——一个目标对很多设计变量求导,正是后者。两种模式都不组装矩阵;图中的矩阵由本页的 Jacobian 画出,只为显示每一步用到哪些元素。

Figure 11 sets AvAv beside ATwA^Tw in matrix form. One forward sweep delivers the columns of AA weighted by vjv_j: the seed v=ejv=e_j picks out exactly column jj, ∂R/∂uj\partial R/\partial u_j. One reverse sweep delivers the rows weighted by wrw_r: the seed w=erw=e_r picks out row rr, ∂Rr/∂u\partial R_r/\partial u. Forward mode therefore suits few inputs and many outputs, reverse mode many inputs and few outputs, which is the case of one objective and many design variables. Neither mode assembles the matrix; the figure draws it from this page's Jacobian only to show which entries each step uses.

按列计算 Av、按行计算 Aᵀw · 两个方向都已完成:wᵀ(Av) = vᵀ(Aᵀw)两个方向都已完成:wT​(Av) = vT​(AT​w)A = ∂R/∂u(本页精确 Jacobian)1.60−0.24−1.331.60−0.22−1.361.60−0.19−1.381.60−0.17−1.411.60−0.14−1.431.60−0.12−1.461.60−0.10−1.481.50vwAvAT​w前向(第 8 节)Av = Σ vj​ × 第 j 列已加 8 / 8 列反向(第 9 节)AT​w = Σ wr​ × 第 r 行已加 8 / 8 行矩阵只为显示每一步用到的元素,两种模式都不组装它wT​(Av) = 3.573955vT​(AT​w) = 3.573955
图 11 · 按列计算 AvAv,按行计算 ATwA^Tw(格心,N = 8,一阶;播放器跟随页首的阶数、网格与边界选择)。中间是本页的精确 Jacobian AA(蓝正橙负);上方是种子 vv,左侧是种子 ww,与第 8、9 节的播放器相同。前向各步把第 jj 列乘 vjv_j 累加到右侧的 AvAv:种子 v=ejv=e_j 取出的正是第 jj 列 ∂R/∂uj\partial R/\partial u_j。反向各步把第 rr 行乘 wrw_r 累加到下方的 ATwA^Tw:种子 w=erw=e_r 取出的是第 rr 行 ∂Rr/∂u\partial R_r/\partial u。虚框是最终值;最后一帧 wT(Av)=vT(ATw)=3.573955w^T(Av)=v^T(A^Tw)=3.573955。
A v by columns, Aᵀw by rows · Both directions done: wᵀ(Av) = vᵀ(Aᵀw)Both directions done: wT​(Av) = vT​(AT​w)A = ∂R/∂u (this page’s exact Jacobian)1.60−0.24−1.331.60−0.22−1.361.60−0.19−1.381.60−0.17−1.411.60−0.14−1.431.60−0.12−1.461.60−0.10−1.481.50vwAvAT​wForward (Section 8)Av = Σ vj​ × column j8 of 8 columns addedReverse (Section 9)AT​w = Σ wr​ × row r8 of 8 rows addedThe matrix is drawn onlyto show the entries used;neither mode assembles itwT​(Av) = 3.573955vT​(AT​w) = 3.573955
Figure 11 · AvAv by columns, ATwA^Tw by rows (cell page, N = 8, first order; the player follows the order, mesh and boundary chosen at the top). Centre: this page's exact Jacobian AA (blue positive, orange negative); above it the seed vv, left of it the seed ww, the seeds of the players of Sections 8 and 9. Each forward step adds vjv_j times column jj into AvAv on the right: the seed v=ejv=e_j picks out exactly column jj, ∂R/∂uj\partial R/\partial u_j. Each reverse step adds wrw_r times row rr into ATwA^Tw below: the seed w=erw=e_r picks out row rr, ∂Rr/∂u\partial R_r/\partial u. Dashed frames are the final values; in the last frame wT(Av)=vT(ATw)=3.573955w^T(Av)=v^T(A^Tw)=3.573955.

入口面怎样反向求导

Differentiating the inlet face in reverse

格心入口面只有右侧控制体:R0R_0 中含 −Hin-H_{in},所以入口面通量的伴随是 Hˉin=−w0\bar H_{in}=-w_0。外侧状态 a=uina=u_{in} 是设计输入,不对应任何状态分量;内侧迹 uinRu_{in}^R 的伴随 cRinHˉinc_R^{in}\bar H_{in} 按重构权重累加到 uˉ0,uˉ1\bar u_0,\bar u_1,一阶时 (β0,β1)=(1,0)(\beta_0,\beta_1)=(1,0),二阶时为 (32,−12)(\tfrac32,-\tfrac12)。取 w=ψw=\psi 时,外侧一项 uˉin\bar u_{in} 就是第 12 节的入口导数 dJ/duin=−cLinψ0dJ/du_{in}=-c_L^{in}\psi_0。这个入口面也是第 16 节入口伴随边界层的来源。

The cell inlet face has a control volume on its right only: R0R_0 contains −Hin-H_{in}, so the adjoint of the inlet flux is Hˉin=−w0\bar H_{in}=-w_0. The exterior state a=uina=u_{in} is a design input and belongs to no state component; the interior-trace adjoint cRinHˉinc_R^{in}\bar H_{in} accumulates into uˉ0,uˉ1\bar u_0,\bar u_1 through the reconstruction weights, (β0,β1)=(1,0)(\beta_0,\beta_1)=(1,0) at first order and (32,−12)(\tfrac32,-\tfrac12) at second order. With w=ψw=\psi the exterior term uˉin\bar u_{in} is the inlet derivative of Section 12, dJ/duin=−cLinψ0dJ/du_{in}=-c_L^{in}\psi_0. This inlet face is also the source of the inlet adjoint boundary layer discussed in Section 16.

Hˉin=−w0,uˉin=cLinHˉin=−cLinw0,uˉ0+=β0cRinHˉin,uˉ1+=β1cRinHˉin.\bar H_{in}=-w_0,\qquad \bar u_{in}=c_L^{in}\bar H_{in}=-c_L^{in}w_0,\qquad \bar u_0\mathrel{\text{+=}}\beta_0c_R^{in}\bar H_{in},\quad \bar u_1\mathrel{\text{+=}}\beta_1c_R^{in}\bar H_{in}.

点积检验 wT(Aqv)=vT(AqTw)w^T(A_qv)=v^T(A_q^Tw),即 AD 的对偶恒等式 RˉTR˙=𝐮˙T𝐮ˉ\bar R^T\dot R=\dot{\mathbf u}^T\bar{\mathbf u},检查转置关系,但两个方向若同时漏掉重构,仍可能通过。第 14 节会用独立复数残差和总导数再做检验。

The dot test wT(Aqv)=vT(AqTw)w^T(A_qv)=v^T(A_q^Tw), the AD duality identity RˉTR˙=𝐮˙T𝐮ˉ\bar R^T\dot R=\dot{\mathbf u}^T\bar{\mathbf u}, checks transposition, but can pass if both operators omit reconstruction. Section 14 additionally checks an independent complex residual and total derivatives.

“倒过来”指依赖链的次序反转,不要求把相互独立的面编号倒序遍历。这里每个面读取固定的 uu 与种子 ww,并向独立的输出数组 uˉ\bar u 累加,正序或倒序遍历面都给出同一线性算子(浮点舍入次序可有微小差异)。GS 的行更新会读刚写入的新值,因而其反向扫描是另一件必须明确处理的事。伪代码就是 AD 工具生成的反向代码 𝚛𝚎𝚜𝚒𝚍𝚞𝚊𝚕_𝚋\texttt{residual\_b} 的样子:种子 𝚛𝚎𝚜_𝚋≡Rˉ\texttt{res\_b}\equiv\bar R,输出 𝚞_𝚋≡uˉ\texttt{u\_b}\equiv\bar u。

Reversal concerns dependency order, not necessarily the numbering of independent faces. Here each face reads the fixed uu and seed ww and adds to a separate output array uˉ\bar u; either face order gives the same linear operator up to rounding. GS row updates read freshly written values, so reversing their scan is a separate issue that must be treated explicitly. The pseudocode is what an AD tool generates as reverse code 𝚛𝚎𝚜𝚒𝚍𝚞𝚊𝚕_𝚋\texttt{residual\_b}: seed 𝚛𝚎𝚜_𝚋≡Rˉ\texttt{res\_b}\equiv\bar R, output 𝚞_𝚋≡uˉ\texttt{u\_b}\equiv\bar u.

逐句倒推:残差代码的反向语句

The residual code reversed statement by statement

下表从最后一条原始语句往回,列出第 8 节表中每条语句的伴随语句 xˉk+=(∂ϕ/∂xk) yˉ\bar x_k\mathrel{\text{+=}}(\partial\phi/\partial x_k)\,\bar y,种子是残差的伴随 Rˉ=w\bar R=w;面循环里,每个面内的语句也倒序执行。为什么倒序、为什么用 +=,见本节末尾的推导:

The table lists the adjoint statement xˉk+=(∂ϕ/∂xk) yˉ\bar x_k\mathrel{\text{+=}}(\partial\phi/\partial x_k)\,\bar y of every statement of the table in Section 8, from the last primal statement back to the first, with the residual adjoint Rˉ=w\bar R=w as the seed; inside the face loop the statements of each face also run in reverse. Why in this order and why with +=: see the derivation at the end of this section:

#原始语句Primal statement伴随语句(倒序执行)Adjoint statement (run in reverse order)
7Ri-=Si, Si=hσiR_i\mathrel{\text{-=}}S_i,\ S_i=h\sigma_iSˉi-=Rˉi, σˉi+=hSˉi\bar S_i\mathrel{\text{-=}}\bar R_i,\ \bar\sigma_i\mathrel{\text{+=}}h\bar S_i
6RR-=HfR_R\mathrel{\text{-=}}H_fHˉf-=RˉR\bar H_f\mathrel{\text{-=}}\bar R_R
5RL+=HfR_L\mathrel{\text{+=}}H_fHˉf+=RˉL\bar H_f\mathrel{\text{+=}}\bar R_L
4Hf=H(a,b)H_f=H(a,b)aˉ+=cLHˉf, bˉ+=cRHˉf, Hˉf←0\bar a\mathrel{\text{+=}}c_L\bar H_f,\ \bar b\mathrel{\text{+=}}c_R\bar H_f,\ \bar H_f\leftarrow0
3b=∑j(TR)fjujb=\sum_j(T_R)_{fj}u_j;出口面 b=ab=a; outlet face: b=ab=auˉj+=(TR)fj bˉ, bˉ←0\bar u_j\mathrel{\text{+=}}(T_R)_{fj}\,\bar b,\ \bar b\leftarrow0;出口面 aˉ+=bˉ\bar a\mathrel{\text{+=}}\bar b; outlet face: aˉ+=bˉ\bar a\mathrel{\text{+=}}\bar b
2a=∑j(TL)fjuja=\sum_j(T_L)_{fj}u_j;入口面 a=uina=u_{in}; inlet face: a=uina=u_{in}uˉj+=(TL)fj aˉ, aˉ←0\bar u_j\mathrel{\text{+=}}(T_L)_{fj}\,\bar a,\ \bar a\leftarrow0;入口面 uˉin+=aˉ\bar u_{in}\mathrel{\text{+=}}\bar a; inlet face: uˉin+=aˉ\bar u_{in}\mathrel{\text{+=}}\bar a
1R←0R\leftarrow0无:Rˉ\bar R 是种子nothing: Rˉ\bar R is the seed

xˉ\bar x 汇总的是加权输出 RˉTR\bar R^TR 对 xx 的敏感度。为什么倒序、为什么每个横线量都用 += 累加,本节末尾的推导按面循环的四条语句给出(程序里的一次扇出,在反向扫描里变成扇入)。本页的计数:一阶时 uju_j 被两个迹读取,二阶时被四个面上的六个迹读取。

xˉ\bar x collects the sensitivity of the weighted output RˉTR\bar R^TR to xx. Why the statements run in reverse order and why every bar accumulates with += is derived for the four statements of the face loop in the derivation at the end of this section (a fan-out of the program becomes a fan-in of the reverse sweep). The counts on this page: at first order uju_j is read by two traces, at second order by six traces on four faces.

每条语句都满足一个局部的对偶关系:对 y=ϕ(x)y=\phi(x),有 yˉ y˙=yˉ (∂ϕ/∂x) x˙=xˉ x˙\bar y\,\dot y=\bar y\,(\partial\phi/\partial x)\,\dot x=\bar x\,\dot x。把所有语句串起来,就是整段代码的对偶恒等式(D˙=0\dot D=0 时就是上面的点积检验),本节末尾的推导对面循环逐级证明它:

Every statement satisfies a local duality: for y=ϕ(x)y=\phi(x), yˉ y˙=yˉ (∂ϕ/∂x) x˙=xˉ x˙\bar y\,\dot y=\bar y\,(\partial\phi/\partial x)\,\dot x=\bar x\,\dot x. Chained over all the statements this is the duality identity of the whole code (with D˙=0\dot D=0 it is the dot test above), which the derivation at the end of this section proves step by step for the face loop:

RˉTR˙=𝐮ˉT𝐮˙+DˉTD˙.\bar R^T\dot R=\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D.

同一个面倒过来:伴随值与对偶检查

The same face in reverse: adjoint values and the duality check

继续第 8 节的面 f4f_{4},种子取第 11 节伴随方程的解 Rˉ=ψ\bar R=\psi。反向语句 6、5 先把两侧的种子收集成 Hˉf=ψL−ψR\bar H_f=\psi_L-\psi_R,语句 4 再乘上同一对偏导:

Continue with the face f4f_{4} of Section 8, now seeded with the solution of the adjoint equation of Section 11, Rˉ=ψ\bar R=\psi. The reverse statements 6 and 5 first gather the seeds of the two sides into Hˉf=ψL−ψR\bar H_f=\psi_L-\psi_R, then statement 4 multiplies by the same pair of partial derivatives:

量Quantity公式Formula一阶First order二阶Second order
Hˉf\bar H_fψL−ψR\psi_L-\psi_R0.0057740.0057740.0058050.005805
aˉ\bar acLHˉfc_L\bar H_f0.0081360.0081360.0081980.008198
bˉ\bar bcRHˉfc_R\bar H_f−0.000960-0.000960−0.001091-0.001091
HˉfH˙f\bar H_f\dot H_f输出端output side5.788580×10−45.788580\times10^{-4}5.597532×10−45.597532\times10^{-4}
aˉa˙+bˉb˙\bar a\dot a+\bar b\dot b输入端input side5.788580×10−45.788580\times10^{-4}5.597532×10−45.597532\times10^{-4}

两端一致到舍入误差:局部对偶关系在这个面上成立。它对整段代码同样成立,而且此时的数值有明确含义:vv 满足切向方程,所以 R˙=Av+RD e0=0\dot R=Av+R_D\,e_0=0;ψ\psi 满足伴随方程,所以 𝐮ˉ=ATψ=−g\bar{\mathbf u}=A^T\psi=-g。代入对偶恒等式得 0=RˉTR˙=−gTv+σˉ00=\bar R^T\dot R=-g^Tv+\bar\sigma_0,即 σˉ0=gTv\bar\sigma_0=g^Tv:伴随给出的梯度分量等于前向给出的方向导数。一阶时两者都是 −4.556093×10−3-4.556093\times10^{-3}(二阶 −3.794243×10−3-3.794243\times10^{-3})。本节末尾的推导对同一个面取同样的种子 Rˉ=ψ\bar R=\psi,设计种子则是 D˙=ek\dot D=e_k(Dk=uinD_k=u_{in}):Hˉf\bar H_f、aˉ\bar a、bˉ\bar b 相同,各级内积则是那个种子的。

The two sides agree to round-off: the local duality holds on this face. It holds for the whole code as well, and there the numbers mean something: vv solves the tangent equation, so R˙=Av+RD e0=0\dot R=Av+R_D\,e_0=0; ψ\psi solves the adjoint equation, so 𝐮ˉ=ATψ=−g\bar{\mathbf u}=A^T\psi=-g. The duality identity then reads 0=RˉTR˙=−gTv+σˉ00=\bar R^T\dot R=-g^Tv+\bar\sigma_0, i.e. σˉ0=gTv\bar\sigma_0=g^Tv: the gradient component from the adjoint equals the directional derivative from the tangent. At first order both are −4.556093×10−3-4.556093\times10^{-3} (at second order −3.794243×10−3-3.794243\times10^{-3}). The derivation at the end of this section reverses the same face with the same seed Rˉ=ψ\bar R=\psi and the design seed D˙=ek\dot D=e_k (Dk=uinD_k=u_{in}): Hˉf\bar H_f, aˉ\bar a and bˉ\bar b are the same, the inner products those of that seed.

图 12把点积检验拆成三层:残差层 ∑iwiR˙i\sum_iw_i\dot R_i、面层 ∑fHˉfH˙f\sum_f\bar H_f\dot H_f 与状态层 ∑jvjuˉj\sum_jv_j\bar u_j,种子与本节播放器相同,vj=sin⁡(j+0.3)v_j=\sin(j+0.3)、wi=cos⁡(i+0.4)w_i=\cos(i+0.4)。面层等于残差层,因为 R˙=ℬH˙\dot R=\mathcal B\dot H;状态层等于面层,因为每个面上 HˉfH˙f=aˉa˙+bˉb˙\bar H_f\dot H_f=\bar a\dot a+\bar b\dot b。逐项看,三层的各项并不相同;逐层求和,三个总和相等。

Figure 12 splits the dot test into three levels: the residual level ∑iwiR˙i\sum_iw_i\dot R_i, the face level ∑fHˉfH˙f\sum_f\bar H_f\dot H_f and the state level ∑jvjuˉj\sum_jv_j\bar u_j, with the seeds of this section's players, vj=sin⁡(j+0.3)v_j=\sin(j+0.3) and wi=cos⁡(i+0.4)w_i=\cos(i+0.4). The face level equals the residual level because R˙=ℬH˙\dot R=\mathcal B\dot H, and the state level equals the face level because HˉfH˙f=aˉa˙+bˉb˙\bar H_f\dot H_f=\bar a\dot a+\bar b\dot b on every face. Term by term the three levels differ; summed level by level, the three totals are equal.

点积检验的三层 · 三个总和相等,各层的单项并不相等三个总和相等,各层的单项并不相等x = 0x = 1wi​Ṙi​0.2200.1680.06221.090.3461.150.549H¯​Ḣ0.07370.1191.040.2440.04611.130.4360.523vj​ūj​0.3691.010.09030.1531.140.3230.476Σ = 3.578/8Σ = 3.579/9Σ = 3.578/8残差层面层状态层Σ wi​Ṙi​ = Σ H¯​Ḣ = Σ vj​ūj​ = 3.573955
图 12 · 点积检验的三层(格心,N = 8,一阶;种子 vj=sin⁡(j+0.3)v_j=\sin(j+0.3)、wi=cos⁡(i+0.4)w_i=\cos(i+0.4);播放器跟随页首的选择)。上:残差层各项 wiR˙iw_i\dot R_i,R˙=Av\dot R=Av 来自前向面循环;中:面层各项 HˉfH˙f\bar H_f\dot H_f,Hˉf=wL−wR\bar H_f=w_L-w_R;下:状态层各项 vjuˉjv_j\bar u_j,𝐮ˉ=ATw\bar{\mathbf u}=A^Tw 来自反向面循环。三层的单项各不相同,逐项累加后三个总和相同:3.5739553.573955。
The dot test in three levels · The three sums are equal; the individual terms are notThe three sums are equal; the individual terms are notx = 0x = 1wi​Ṙi​0.2200.1680.06221.090.3461.150.549H¯​Ḣ0.07370.1191.040.2440.04611.130.4360.523vj​ūj​0.3691.010.09030.1531.140.3230.476Σ = 3.578/8Σ = 3.579/9Σ = 3.578/8residualfacesstateΣ wi​Ṙi​ = Σ H¯​Ḣ = Σ vj​ūj​ = 3.573955
Figure 12 · The dot test in three levels (cell page, N = 8, first order; seeds vj=sin⁡(j+0.3)v_j=\sin(j+0.3) and wi=cos⁡(i+0.4)w_i=\cos(i+0.4); the player follows the choices at the top). Top: the residual-level terms wiR˙iw_i\dot R_i, with R˙=Av\dot R=Av from the forward face loop; middle: the face-level terms HˉfH˙f\bar H_f\dot H_f, with Hˉf=wL−wR\bar H_f=w_L-w_R; bottom: the state-level terms vjuˉjv_j\bar u_j, with 𝐮ˉ=ATw\bar{\mathbf u}=A^Tw from the reverse face loop. The terms of the three levels differ one by one; summed, the three totals are the same: 3.5739553.573955.

为什么反向累加给出矩阵转置

Why reverse accumulation gives the matrix transpose

把所有残差和状态分量展平后,下面只是交换两个有限求和的次序。残差种子 Rˉ=w\bar R=w 是任意给定的,尚未求解伴随方程;只有选择满足第 11 节线性方程的 w=ψw=\psi,才得到该目标对应的离散伴随解。取 D˙=0\dot D=0、𝐮˙=v\dot{\mathbf u}=v,前向得到 R˙=Av\dot R=Av;以 Rˉ=w\bar R=w 为种子反向扫描,得到 𝐮ˉ=ATw\bar{\mathbf u}=A^Tw。下式就是 AD 的对偶(点积)恒等式:

After flattening all residual and state components, the following simply exchanges two finite sums. The residual seed Rˉ=w\bar R=w is arbitrary and has not solved an adjoint equation; only w=ψw=\psi satisfying Section 11 gives the discrete adjoint solution for the chosen objective. With D˙=0\dot D=0 and 𝐮˙=v\dot{\mathbf u}=v the forward sweep gives R˙=Av\dot R=Av; the reverse sweep seeded with Rˉ=w\bar R=w gives 𝐮ˉ=ATw\bar{\mathbf u}=A^Tw. The identity below is the duality (dot-product) identity of AD:

RˉTR˙=wT(Av)=∑r=1nu∑j=1nuwr∂Rr∂ujvj\bar R^T\dot R=w^T(Av)=\sum_{r=1}^{n_u}\sum_{j=1}^{n_u}w_r\frac{\partial R_r}{\partial u_j}v_j
=∑j=1nuvj(∑r=1nu∂Rr∂ujwr)=vT(ATw)=𝐮˙T𝐮ˉ.=\sum_{j=1}^{n_u}v_j\left(\sum_{r=1}^{n_u}\frac{\partial R_r}{\partial u_j}w_r\right)=v^T(A^Tw)=\dot{\mathbf u}^T\bar{\mathbf u}.

内部面通量向左加、向右减,所以先把这两条路径合并成通量的伴随 Hˉf\bar H_f;之后按通量、重构、原始状态的相反依赖顺序传播。反向语句中的 += 很重要:同一状态会被多个面和多个求积点读取,这些贡献必须累加。这里没有分部积分,也没有另行猜测连续伴随边界条件。

An interior flux is added on the left and subtracted on the right, so combine those two paths into its adjoint Hˉf\bar H_f before reversing fluxes, reconstruction and stored states. The += is essential: multiple faces and quadrature points read the same state, and their contributions must accumulate. There is no integration by parts and no separately guessed continuous adjoint boundary condition.

RL+=Hf, RR-=HfR_L\mathrel{\text{+=}}H_f,\ R_R\mathrel{\text{-=}}H_f
⟹Hˉf=RˉL−RˉR=wL−wR,\Longrightarrow\quad\bar H_f=\bar R_L-\bar R_R=w_L-w_R,
z˙f=∑j∈𝒮f(Tf)j u˙j+KfD˙\dot z_f=\sum_{j\in\mathcal S_f}(T_f)_j\,\dot u_j+K_f\dot D
⟹uˉj+=(Tf)jT zˉf.\Longrightarrow\quad\bar u_j\mathrel{\text{+=}}(T_f)_j^T\,\bar z_f.

第 8 节局部链里的每个切向量都有对应的伴随。反向语句按与前向相反的顺序执行,每个输入的横线量都用 += 累加:

Every tangent of the local chain of Section 8 has its adjoint. The reverse statements run in the opposite order of the forward ones, and the bar of every input accumulates with +=:

𝐮ˉ←0, Dˉ←0;Hˉf=∑iℬifRˉi,\bar{\mathbf u}\leftarrow0,\ \bar D\leftarrow0;\quad \bar H_f=\sum_i\mathcal B_{if}\bar R_i,
Sˉi=−Rˉi,zˉf=Hz,fTHˉf,\bar S_i=-\bar R_i,\quad \bar z_f=H_{z,f}^T\bar H_f,
𝐮ˉ+=TfTzˉf+(∂S∂𝐮)TSˉ,\bar{\mathbf u}\mathrel{\text{+=}}T_f^T\bar z_f+\Big(\frac{\partial S}{\partial \mathbf u}\Big)^T\bar S,
Dˉ+=KfTzˉf+(∂S∂D)TSˉ.\bar D\mathrel{\text{+=}}K_f^T\bar z_f+\Big(\frac{\partial S}{\partial D}\Big)^T\bar S.

本页没有被替换的残差行:边界条件都在边界面的通量里,所以上面的面循环转置就是完整的 ATA^T。

This page replaces no residual rows: the boundary conditions all sit in boundary-face fluxes, so the transposed face loop above is the complete ATA^T.

从切向量到伴随:转置把语句顺序反过来

From tangent to adjoint: the transpose runs the statements backwards

一个面的切向量是四个初等 Jacobian 的乘积。乘积的转置等于各个因子的转置按相反的顺序相乘:

The tangent of one face is the product of four elementary Jacobians. The transpose of a product is the product of the transposes in the reverse order:

AfT=(ℬ:,f Hz,f Zf 𝒢f)T=𝒢fT ZfT Hz,fT ℬ:,fT,A_f^T=\big(\mathcal B_{:,f}\,H_{z,f}\,Z_f\,\mathcal G_f\big)^T=\mathcal G_f^T\,Z_f^T\,H_{z,f}^T\,\mathcal B_{:,f}^T,
(ℬ:,f Hz,f Kf)T=KfT Hz,fT ℬ:,fT.\big(\mathcal B_{:,f}\,H_{z,f}\,K_f\big)^T=K_f^T\,H_{z,f}^T\,\mathcal B_{:,f}^T.

仍从右往左读,这就是反向扫描的语句顺序:程序最后执行的分发最先处理,最先执行的取值最后处理。先把所有伴随置零,再以 Rˉ=w\bar R=w 为种子逐条执行:

Read again from right to left, this is the order of the reverse sweep: the scatter, which the program ran last, comes first, and the gather, which it ran first, comes last. Zero every adjoint, seed with Rˉ=w\bar R=w, and run:

(0)𝐮ˉ←0,  Dˉ←0,  Rˉ=w,(0)\quad \bar{\mathbf u}\leftarrow0,\ \ \bar D\leftarrow0,\ \ \bar R=w,
(4′)Hˉf=ℬ:,fTRˉ=RˉL−RˉR,(4')\quad \bar H_f=\mathcal B_{:,f}^T\bar R=\bar R_L-\bar R_R,
(3′)zˉf=Hz,fT Hˉf,(3')\quad \bar z_f=H_{z,f}^T\,\bar H_f,
(2′)𝐮ˉf=ZfT zˉf,Dˉ+=KfT zˉf,(2')\quad \bar{\mathbf u}_f=Z_f^T\,\bar z_f,\quad \bar D\mathrel{\text{+=}}K_f^T\,\bar z_f,
(1′)𝐮ˉ+=𝒢fT 𝐮ˉf.(1')\quad \bar{\mathbf u}\mathrel{\text{+=}}\mathcal G_f^T\,\bar{\mathbf u}_f.

面循环结束后,源项的一对也按同样的规则反过来:Sˉi=−Rˉi\bar S_i=-\bar R_i,𝐮ˉ+=(∂S/∂𝐮)TSˉ\bar{\mathbf u}\mathrel{\text{+=}}(\partial S/\partial \mathbf u)^T\bar S,Dˉ+=(∂S/∂D)TSˉ\bar D\mathrel{\text{+=}}(\partial S/\partial D)^T\bar S。本页代码里每一条语句的伴随语句(顺序与前向相反)也都在本节里逐句写出。

After the face loop the source is reversed by the same rule: Sˉi=−Rˉi\bar S_i=-\bar R_i, 𝐮ˉ+=(∂S/∂𝐮)TSˉ\bar{\mathbf u}\mathrel{\text{+=}}(\partial S/\partial \mathbf u)^T\bar S, Dˉ+=(∂S/∂D)TSˉ\bar D\mathrel{\text{+=}}(\partial S/\partial D)^T\bar S. The adjoint statement of every statement of this page's code, in the reverse order, is written out in this section as well.

分发与取值互为转置。分发 R+=ℬ:,fHfR\mathrel{\text{+=}}\mathcal B_{:,f}H_f 把一个面通量写进两行;它的转置 Hˉf=ℬ:,fTRˉ\bar H_f=\mathcal B_{:,f}^T\bar R 从这两行读回一个数,是一次取值。取值 𝐮f=𝒢f𝐮\mathbf u_f=\mathcal G_f\mathbf u 从全局数组读出几个状态;它的转置 𝒢fT\mathcal G_f^T 把它们的伴随加回原来的位置,是一次带累加的分发。

Scatter and gather are transposes of each other. The scatter R+=ℬ:,fHfR\mathrel{\text{+=}}\mathcal B_{:,f}H_f writes one face flux into two rows; its transpose Hˉf=ℬ:,fTRˉ\bar H_f=\mathcal B_{:,f}^T\bar R reads those two rows back into one number: a gather. The gather 𝐮f=𝒢f𝐮\mathbf u_f=\mathcal G_f\mathbf u reads a few states out of the global array; its transpose 𝒢fT\mathcal G_f^T adds their adjoints back into their places: a scatter with accumulation.

为什么要先置零、再用 += 累加:程序里的一次扇出(一个值被多处读取)在反向扫描里变成扇入。一个存储状态属于它每个相邻面的 𝒮f\mathcal S_f,设计变量也进入许多面与源项;链式法则对变量的每一次使用各给出一项,xˉ\bar x 是所有这些项的和:

Why zero first and then +=: a fan-out of the program (one value read in several places) becomes a fan-in of the reverse sweep. A stored state belongs to the 𝒮f\mathcal S_f of every neighbouring face, and the design enters many faces and the sources; the chain rule gives one term per use of a variable, and xˉ\bar x is the sum of all these terms:

uˉj=∑f: j∈𝒮f(𝒢fT 𝐮ˉf)j+(∂S∂uj)TSˉ.\bar u_j=\sum_{f:\ j\in\mathcal S_f}\big(\mathcal G_f^T\,\bar{\mathbf u}_f\big)_j+\Big(\frac{\partial S}{\partial u_j}\Big)^T\bar S.

xˉ←0\bar x\leftarrow0 让这个和从零开始,每个 += 加上一次使用的贡献。反过来,程序里的扇入(RLR_L 收集它各个面的通量)在反向扫描里变成扇出:RˉL\bar R_L 只被读取,不需要累加。

xˉ←0\bar x\leftarrow0 starts this sum at zero, and each += adds the term of one use. Conversely, a fan-in of the program (RLR_L collecting the fluxes of all its faces) becomes a fan-out of the reverse sweep: RˉL\bar R_L is only read and needs no accumulation.

对偶恒等式的逐步证明,以及它的数值形式:点积测试

The duality identity step by step, and its numerical form: the dot-product test

把同一个面的前向语句与反向语句配对。每一步只用一次转置的定义 ⟨xˉ,ℰx˙⟩=⟨ℰTxˉ,x˙⟩\langle\bar x,\mathcal E\dot x\rangle=\langle\mathcal E^T\bar x,\dot x\rangle,把一个初等 Jacobian 从内积的右边移到左边,所以内积在每一级都相同:

Pair the forward and reverse statements of the same face. Each step uses the definition of the transpose once, ⟨xˉ,ℰx˙⟩=⟨ℰTxˉ,x˙⟩\langle\bar x,\mathcal E\dot x\rangle=\langle\mathcal E^T\bar x,\dot x\rangle, to move one elementary Jacobian from the right of the inner product to the left, so the inner product is the same at every stage:

RˉT(ℬ:,f H˙f)=(ℬ:,fTRˉ)TH˙f=HˉfTH˙f,\bar R^T\big(\mathcal B_{:,f}\,\dot H_f\big)=\big(\mathcal B_{:,f}^T\bar R\big)^T\dot H_f=\bar H_f^T\dot H_f,
HˉfTH˙f=HˉfTHz,f z˙f=(Hz,fTHˉf)Tz˙f=zˉfTz˙f,\bar H_f^T\dot H_f=\bar H_f^TH_{z,f}\,\dot z_f=\big(H_{z,f}^T\bar H_f\big)^T\dot z_f=\bar z_f^T\dot z_f,
zˉfTz˙f=zˉfT(Zf𝐮˙f+KfD˙)\bar z_f^T\dot z_f=\bar z_f^T\big(Z_f\dot{\mathbf u}_f+K_f\dot D\big)
=𝐮ˉfT𝐮˙f+(KfTzˉf)TD˙,=\bar{\mathbf u}_f^T\dot{\mathbf u}_f+\big(K_f^T\bar z_f\big)^T\dot D,
𝐮ˉfT𝐮˙f=𝐮ˉfT𝒢f 𝐮˙=(𝒢fT𝐮ˉf)T𝐮˙.\bar{\mathbf u}_f^T\dot{\mathbf u}_f=\bar{\mathbf u}_f^T\mathcal G_f\,\dot{\mathbf u}=\big(\mathcal G_f^T\bar{\mathbf u}_f\big)^T\dot{\mathbf u}.

对所有面求和,再加上源项的一对 SˉTS˙\bar S^T\dot S(Sˉ=−Rˉ\bar S=-\bar R),右边的两项正好累加成反向扫描的输出 𝐮ˉ\bar{\mathbf u} 与 Dˉ\bar D:

Summed over all faces, with the pair of the source added (SˉTS˙\bar S^T\dot S with Sˉ=−Rˉ\bar S=-\bar R), the two terms on the right accumulate exactly into the outputs 𝐮ˉ\bar{\mathbf u} and Dˉ\bar D of the reverse sweep:

RˉTR˙=𝐮ˉT𝐮˙+DˉTD˙.\bar R^T\dot R=\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D.

这就是 AD 的点积测试:任取种子 (𝐮˙,D˙)(\dot{\mathbf u},\dot D) 与 Rˉ\bar R,前向扫描算出 R˙\dot R,反向扫描算出 (𝐮ˉ,Dˉ)(\bar{\mathbf u},\bar D),两边之差应在舍入误差量级;只要有一条反向语句的转置写错,差就是 O(1)O(1) 的。本页在构建时用它自己的 matrix-free 前向与反向乘积(nu=8n_u=8 个分量的确定性种子,设计方向取下面算例的 DkD_k)做了这个测试:RˉTR˙=−0.339597923255\bar R^T\dot R=-0.339597923255,𝐮ˉT𝐮˙+DˉTD˙=−0.339597923255\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D=-0.339597923255,相对差 7×10−167\times10^{-16}。

This is the dot-product test of AD: take any seeds (𝐮˙,D˙)(\dot{\mathbf u},\dot D) and Rˉ\bar R, run the forward sweep for R˙\dot R and the reverse sweep for (𝐮ˉ,Dˉ)(\bar{\mathbf u},\bar D); the two sides must agree to round-off, and a single wrong transpose in the reverse statements gives an O(1)O(1) difference. This page ran the test on its own matrix-free forward and reverse products at build time (deterministic seeds with nu=8n_u=8 components, the design direction the DkD_k of the example below): RˉTR˙=−0.339597923255\bar R^T\dot R=-0.339597923255 and 𝐮ˉT𝐮˙+DˉTD˙=−0.339597923255\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D=-0.339597923255, a relative difference of 7×10−167\times10^{-16}.

同一个面的反向扫描

The reverse sweep of the same face

反向种子取本页伴随求解的解 Rˉ=ψ\bar R=\psi,于是 𝐮ˉf\bar{\mathbf u}_f 与 Dˉk\bar D_k 是这个面对 ATψA^T\psi 与 RDTψR_D^T\psi 的贡献。下表按反向语句的顺序列出伴随值;最后一列是每一级的内积:先是 Rˉ3TR˙3+Rˉ4TR˙4\bar R_{3}^T\dot R_{3}+\bar R_{4}^T\dot R_{4},然后是 HˉfTH˙f\bar H_f^T\dot H_f、zˉfTz˙f\bar z_f^T\dot z_f,最后是 𝐮ˉfT𝐮˙f+DˉTD˙\bar{\mathbf u}_f^T\dot{\mathbf u}_f+\bar D^T\dot D,切向量取第 8 节“本页一个面的算例:前向扫描”里同一个面的前向算例的值。

Seed the reverse sweep with this page's adjoint solution, Rˉ=ψ\bar R=\psi, so that 𝐮ˉf\bar{\mathbf u}_f and Dˉk\bar D_k are this face's contributions to ATψA^T\psi and RDTψR_D^T\psi. The table lists the adjoints in the order of the reverse statements; the last column is the inner product at each stage: first Rˉ3TR˙3+Rˉ4TR˙4\bar R_{3}^T\dot R_{3}+\bar R_{4}^T\dot R_{4}, then HˉfTH˙f\bar H_f^T\dot H_f, zˉfTz˙f\bar z_f^T\dot z_f, and last 𝐮ˉfT𝐮˙f+DˉTD˙\bar{\mathbf u}_f^T\dot{\mathbf u}_f+\bar D^T\dot D, with the tangents of the forward example of the same face, the one under “A worked example on this page: the forward sweep of one face” in Section 8.

语句Statement量Quantity伴随Adjoint内积Inner product
(0)置零(0) zero𝐮ˉ←0, Dˉ←0\bar{\mathbf u}\leftarrow0,\ \bar D\leftarrow00
(4′)种子(4′) seedRˉ3=ψ3\bar R_{3}=\psi_30.048210.006020123527
Rˉ4=ψ4\bar R_{4}=\psi_40.04244
(4′)取值(4′) gatherHˉf=Rˉ3−Rˉ4\bar H_f=\bar R_{3}-\bar R_{4}0.0057740.006020123527
(3′)通量的转置(3′) flux, transposedaˉ\bar a0.0081360.006020123527
bˉ\bar b−9.602×10⁻⁴
(2′)(1′)加回(2′) (1′) add backuˉ3+=\bar u_{3}\mathrel{\text{+=}}0.0081360.006020123527
uˉ4+=\bar u_{4}\mathrel{\text{+=}}−9.602×10⁻⁴
Dˉk+=\bar D_k\mathrel{\text{+=}}0

4 级内积相同,最大相对差 4×10−164\times10^{-16}:对偶恒等式在这些数上成立;下面的面板按反向语句逐条播放这些数。这个面不直接依赖 DkD_k,所以 Dˉk=0\bar D_k=0;DkD_k 的伴随来自它真正进入的那些行。

The 4 inner products agree, with a largest relative difference of 4×10−164\times10^{-16}: the duality identity holds in these numbers. The panel below plays them statement by statement in reverse. This face does not depend on DkD_k directly, so Dˉk=0\bar D_k=0; the adjoint of DkD_k comes from the rows it does enter.

同一个面的反向扫描
The reverse sweep of the same face

箭头全部反向,每个初等 Jacobian 转置,顺序相反;存储状态与设计变量处用 += 累加。下方:转置的乘积与形状。

Every arrow reversed, every elementary Jacobian transposed, in the reverse order; the stored states and the design accumulate with +=. Below: the transposed product and its shapes.

全部存储状态all stored statesuu¯​+=面读取的状态states the face readsuf​u¯​f​面输入:重构值等face inputszf​z¯​f​面通量face fluxHf​H¯​f​两侧残差行rows L and RRL, RR​R¯​L, R¯​R​设计输入design inputDD¯​+=𝒢fT​ZfT​Hz,fT​ℬ:,fT​KfT​AfT=𝒢fT​8×2ZfT​2×2Hz,fT​2×1ℬ:,fT​1×8R¯​u¯​从右往左作用:先转置的分发(最右),最后转置的取值(最左)applied right to left: the transposed scatter (rightmost) first, the transposed gather last全部状态all statesuu¯​+=模板上的状态stenciluf​u¯​f​面输入face inputszf​z¯​f​面通量face fluxHf​H¯​f​L、R 两行rows L, RRL, RR​R¯​L, R¯​R​设计输入design inputDD¯​+=𝒢fT​ZfT​Hz,fT​ℬ:,fT​KfT​AfT=𝒢fT​8×2ZfT​2×2Hz,fT​2×1ℬ:,fT​1×8R¯​u¯​从右往左作用:applied right to left: the先转置的分发,最后转置的取值transposed scatter first, gather last
  1. (0)置零并给种子:𝐮ˉ←0\bar{\mathbf u}\leftarrow0,Dˉ←0\bar D\leftarrow0;Rˉ3=ψ3=\bar R_{3}=\psi_3= 0.04821,Rˉ4=ψ4=\bar R_{4}=\psi_4= 0.04244。

    (0) Zero and seed: 𝐮ˉ←0\bar{\mathbf u}\leftarrow0, Dˉ←0\bar D\leftarrow0; Rˉ3=ψ3=\bar R_{3}=\psi_3= 0.04821, Rˉ4=ψ4=\bar R_{4}=\psi_4= 0.04244.

  2. (4′)分发的转置是一次取值:Hˉf=Rˉ3−Rˉ4=\bar H_f=\bar R_{3}-\bar R_{4}= 0.005774。内积 Rˉ3TR˙3+Rˉ4TR˙4=HˉfTH˙f=\bar R_{3}^T\dot R_{3}+\bar R_{4}^T\dot R_{4}=\bar H_f^T\dot H_f= 0.006020123527。

    (4′) The transposed scatter is a gather: Hˉf=Rˉ3−Rˉ4=\bar H_f=\bar R_{3}-\bar R_{4}= 0.005774. Inner product Rˉ3TR˙3+Rˉ4TR˙4=HˉfTH˙f=\bar R_{3}^T\dot R_{3}+\bar R_{4}^T\dot R_{4}=\bar H_f^T\dot H_f= 0.006020123527.

  3. (3′)通量的转置:zˉf=Hz,fTHˉf\bar z_f=H_{z,f}^T\bar H_f,aˉ=\bar a= 0.008136,bˉ=\bar b= −9.602×10⁻⁴。内积 zˉfTz˙f=\bar z_f^T\dot z_f= 0.006020123527。

    (3′) The transposed flux: zˉf=Hz,fTHˉf\bar z_f=H_{z,f}^T\bar H_f, aˉ=\bar a= 0.008136, bˉ=\bar b= −9.602×10⁻⁴. Inner product zˉfTz˙f=\bar z_f^T\dot z_f= 0.006020123527.

  4. (2′)重构的转置:𝐮ˉf=ZfTzˉf\bar{\mathbf u}_f=Z_f^T\bar z_f,Dˉ+=KfTzˉf\bar D\mathrel{\text{+=}}K_f^T\bar z_f,这个面给出 Dˉk=\bar D_k= 0(Kfek=0K_fe_k=0)。

    (2′) The transposed reconstruction: 𝐮ˉf=ZfTzˉf\bar{\mathbf u}_f=Z_f^T\bar z_f and Dˉ+=KfTzˉf\bar D\mathrel{\text{+=}}K_f^T\bar z_f; this face gives Dˉk=\bar D_k= 0 (Kfek=0K_fe_k=0).

  5. (1′)取值的转置是带累加的分发:𝐮ˉ+=𝒢fT𝐮ˉf\bar{\mathbf u}\mathrel{\text{+=}}\mathcal G_f^T\bar{\mathbf u}_f,uˉ3+=\bar u_{3}\mathrel{\text{+=}} 0.008136,uˉ4+=\bar u_{4}\mathrel{\text{+=}} −9.602×10⁻⁴。内积 𝐮ˉfT𝐮˙f+DˉTD˙=\bar{\mathbf u}_f^T\dot{\mathbf u}_f+\bar D^T\dot D= 0.006020123527。

    (1′) The transposed gather is a scatter with accumulation: 𝐮ˉ+=𝒢fT𝐮ˉf\bar{\mathbf u}\mathrel{\text{+=}}\mathcal G_f^T\bar{\mathbf u}_f, uˉ3+=\bar u_{3}\mathrel{\text{+=}} 0.008136, uˉ4+=\bar u_{4}\mathrel{\text{+=}} −9.602×10⁻⁴. Inner product 𝐮ˉfT𝐮˙f+DˉTD˙=\bar{\mathbf u}_f^T\dot{\mathbf u}_f+\bar D^T\dot D= 0.006020123527.

  6. 对偶:4 级内积相同(最大相对差 4×10⁻¹⁶),RˉTR˙=𝐮ˉT𝐮˙+DˉTD˙\bar R^T\dot R=\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D 在这个面上逐级成立。

    Duality: the 4 inner products agree (largest relative difference 4×10⁻¹⁶); RˉTR˙=𝐮ˉT𝐮˙+DˉTD˙\bar R^T\dot R=\bar{\mathbf u}^T\dot{\mathbf u}+\bar D^T\dot D holds stage by stage on this face.

第 10 节Section 10

前向求解:设计改变后,解怎样变化

Forward solve: how the solution changes with the design

从方向乘积到一个需要求解的方程

From a directional product to an equation to solve

前面可以对任意向量 vv 计算 AvAv。现在给设计输入一个种子(方向)D˙\dot D:状态也随之变化,它的切向量是 u˙\dot u,且新状态仍要满足 R(u(D),D)=0R(u(D),D)=0。对这个恒等式前向求导(多元链式法则),得到两部分:经状态切向量的间接影响与设计直接进入残差的影响。

We can now evaluate AvAv for any supplied vv. Now seed the design input with a direction D˙\dot D: the state changes too, with tangent u˙\dot u, and must still satisfy R(u(D),D)=0R(u(D),D)=0. Differentiating this identity in forward mode (the multivariable chain rule) gives two terms: the indirect effect through the state tangent and the direct design effect in the residual.

0=R˙=Ru u˙+RD D˙=Aqv+f,f=RDD˙,v≡u˙.0=\dot R=R_u\,\dot u+R_D\,\dot D=A_qv+f,\qquad f=R_D\dot D,\quad v\equiv\dot u.

例如取种子 D˙=ek\dot D=e_k,即格心第 kk 个源项增加一个单位的方向,只有直接项 fk=−hf_k=-h 非零;随后 vv 的多个分量会响应。这区分了“输入在哪里施加”和“影响传播到哪里”。先固定收敛的原始解,再求这个线性系统,最后用 J˙=gTv\dot J=g^Tv 得到目标的方向导数。

For example, the seed D˙=ek\dot D=e_k, a unit change in cell source kk, gives a direct term fk=−hf_k=-h with other components zero, yet many state components can respond in vv. This separates where the input is applied from where its effects propagate. Freeze the converged primal state, solve the linear system, then obtain the objective directional derivative as J˙=gTv\dot J=g^Tv.

沿设计种子 D˙\dot D 对收敛残差 Rq(u(D),D)=0R_q(u(D),D)=0 前向求导,令 f=Rq,DD˙f=R_{q,D}\dot D,得到精确 tangent 方程,它的解是状态切向量 v≡𝐮˙v\equiv\dot{\mathbf u}。在该线性求解期间固定原始解、AqA_q 和一阶预处理矩阵 PP;修正量 Δv\Delta v 是迭代的增量,不是导数。

Differentiate the converged residual Rq(u(D),D)=0R_q(u(D),D)=0 forward along the design seed D˙\dot D and set f=Rq,DD˙f=R_{q,D}\dot D. The exact tangent equation below has the state tangent v≡𝐮˙v\equiv\dot{\mathbf u} as its solution. The primal state, exact AqA_q and first-order preconditioning matrix PP remain fixed during this linear solve; the correction Δv\Delta v is an iteration increment, not a derivative.

Aqv=−f,rt=Aqv+f,PΔv=−rt,vm+1=vm+ω Δv.A_qv=-f,\qquad r_t=A_qv+f,\qquad P\Delta v=-r_t,\qquad v^{m+1}=v^m+\omega\,\Delta v.

用一阶 Jacobian 求二阶方程的修正

A first-order Jacobian for corrections to the second-order equation

每次修正方程从零开始,用所选次数的 GS 扫描,得到线性算子 Bs≈P−1B_s\approx P^{-1}。一次扫描时 B1=(diag⁡(P)+L)−1B_1=(\operatorname{diag}(P)+L)^{-1}。精确求出修正方程时,可以把这一缺陷校正写成 Pvm+1=−f−(Aq−P)vmPv^{m+1}=-f-(A_q-P)v^m(ω=1\omega=1);有限次 GS 时应使用下面的实际迭代式。

Starting each correction from zero and taking the selected fixed number of GS sweeps defines a linear operator Bs≈P−1B_s\approx P^{-1}. One sweep gives B1=(diag⁡(P)+L)−1B_1=(\operatorname{diag}(P)+L)^{-1}. If the correction is solved exactly, this defect correction becomes Pvm+1=−f−(Aq−P)vmPv^{m+1}=-f-(A_q-P)v^m for ω=1\omega=1; with finite GS sweeps, the actual iteration is the following.

vm+1=vm−ωBs(Aqvm+f),em+1=(I−ωBsAq)em.v^{m+1}=v^m-\omega B_s(A_qv^m+f),\qquad e^{m+1}=(I-\omega B_sA_q)e^m.

收敛需要误差传播矩阵的谱半径小于 1;GS 或低阶预处理并不对任意二阶问题保证收敛。下面可更换设计变量,查看解及真实线性残差。

Convergence requires a spectral radius below 1 for the error propagator; GS or low-order preconditioning does not guarantee convergence for arbitrary second-order problems. Select a design variable below and inspect the solution and true linear residual.

前向固定点求解:每次检查精确线性残差

Forward fixed point: check the exact linear residual each time

f = design_derivative(u, D_d)          # D_d is the design seed Ḋ
v = zeros(M); P = first_order_jacobian(u)
repeat:
    r = forward_product(u, v) + f      # A v + f, one forward sweep
    if norm(r) <= tolerance: return v
    delta = GS(P, -r, zero_start, fixed_sweep_count)   # the correction Δv
    v = v + omega*delta

一次 GS 应用把右端向量变成修正,记为线性映射 BsB_s。将更新式代入误差 em=vm−v∗e^m=v^m-v^*,就得到误差传播矩阵;其谱半径小于 1 才保证对任意初始误差收敛。残差曲线可以观察本次求解,但不能替代一般收敛条件。

One GS application maps a right-hand side to a correction; call this linear map BsB_s. Substitute the update into em=vm−v∗e^m=v^m-v^* to obtain the error propagation matrix. A spectral radius below 1 guarantees convergence for every initial error. A residual plot describes this solve, not a general convergence theorem.

残差对设计变量的导数:逐列写出

The residual’s design derivative, column by column

切向方程 Av=−fAv=-f 里的向量 f=RD D˙f=R_D\,\dot D(右端是 −f-f)需要每一行残差在状态固定时对每个设计分量的偏导。读取设计的只有两类语句:源项积分,以及入口值(格心页是入口面的外侧状态)。所以 RDR_D 的每一列都很稀疏:

The vector f=RD D˙f=R_D\,\dot D of the tangent equation Av=−fAv=-f (its right-hand side is −f-f) needs the partial derivative of every residual row with respect to every design component, with the state held fixed. Only two kinds of statement read the design: the source integrals, and the inlet value (the exterior state of the inlet face on the cell page). So every column of RDR_D is sparse:

∂Ri∂σk=−h δik,∂Ri∂uin=−cLin δi0,cLin=uin2+ε=1.3.\frac{\partial R_i}{\partial\sigma_k}=-h\,\delta_{ik},\qquad \frac{\partial R_i}{\partial u_{in}}=-c_L^{in}\,\delta_{i0},\qquad c_L^{in}=\frac{u_{in}}2+\varepsilon=1.3.

“边界对照”的迎风入口面用物理通量,那里 cLin=uinc_L^{in}=u_{in}。在前向代码里,这两类列就是第 8 节表中取 u˙=0\dot u=0 的第 2 条语句(入口面 a˙=u˙in\dot a=\dot u_{in},H˙in=cLinu˙in\dot H_{in}=c_L^{in}\dot u_{in})和第 7 条语句(S˙i=hσ˙i\dot S_i=h\dot\sigma_i):设计种子直接改变的只有它自己的控制体,入口值则是入口面。

The upwind inlet of the boundary comparison uses the physical flux, and there cLin=uinc_L^{in}=u_{in}. In the tangent code these columns are statements 2 (the inlet face: a˙=u˙in\dot a=\dot u_{in}, H˙in=cLinu˙in\dot H_{in}=c_L^{in}\dot u_{in}) and 7 (S˙i=hσ˙i\dot S_i=h\dot\sigma_i) of the table in Section 8 with u˙=0\dot u=0: a design seed changes directly only its own control volume, and the inlet value only the inlet face.

图 13跟着一个种子走完代码与求解。取区间右端在 x=1/2x=1/2 的源项 σ3\sigma_{3}:先用 u˙=0\dot u=0 执行面循环,所有面的 H˙f\dot H_f 都是零,只有源项语句让控制体 3 的 R˙\dot R 不为零,这就是 ff。接着,前向 GS 从第 0 行扫到最后一行:右端在种子上游为零,而下三角求解只把已经更新的上游值传给下一行,所以上游各行保持为零,从第 3 行起向下游逐行点亮——这正是信息沿特征速度 u>0u>0 的传播方向。求解收敛后再以 u˙=v\dot u=v 执行一次面循环:每个面的 H˙f\dot H_f 都亮起来,只在种子处跳变,最后所有 R˙i\dot R_i 回到零。

Figure 13 follows one seed through the code and the solve. Take the source σ3\sigma_{3} of the interval ending at x=1/2x=1/2. First the face loop runs with u˙=0\dot u=0: every H˙f\dot H_f is zero, and only the source statement makes R˙\dot R of volume 3 nonzero; that is ff. Then the forward GS sweep runs from row 0 to the last row: the right-hand side is zero upstream of the seed, and the lower-triangular solve passes only already updated upstream values to the next row, so the rows upstream stay zero and from row 3 on they light up one after another downstream, the direction in which information travels with the characteristic speed u>0u>0. When the solve has converged, the face loop runs once more with u˙=v\dot u=v: every face flux tangent H˙f\dot H_f lights up, jumps only at the seed, and at the end every R˙i\dot R_i is back at zero.

一个设计种子的前向扫描 · ⑦ 源项语句 Ṙ −= Ṡ:全部 Ṙᵢ 回到 0⑦ 源项语句 Ṙ −= Ṡ:全部 Ṙi​ 回到 0x = 0x = 1σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​Ḋσ˙​₃ = 1u˙​0.01370.1000.09640.09310.09030.0891Ḣ0.1250.1250.1250.1250.125ṘḢ 只在注入源项的控制体处跳变 Ṡ(Ṡ₃ = 0.125),其余面相等
图 13 · 一个设计种子的前向扫描(格心,N = 8,一阶,种子 D˙=e3\dot D=e_{3}:源项 σ3\sigma_{3} 增加一个单位;播放器跟随页首的阶数、网格与边界选择)。从上到下:设计种子、状态切向量 𝐮˙\dot{\mathbf u}、各面的通量切向量 H˙f\dot H_f、各控制体的切向残差 R˙i\dot R_i;虚框是最终值。先以 𝐮˙=0\dot{\mathbf u}=0 执行面循环:所有 H˙f\dot H_f 都是零,只有源项语句让控制体 3 的 R˙\dot R 不为零,这就是 ff。接着是前向 GS 的第一轮扫描:种子上游的各行保持为零,从第 3 行起向下游逐行点亮。收敛后以 𝐮˙=v\dot{\mathbf u}=v 再执行面循环:H˙f\dot H_f 在种子上游等于 −0.000087-0.000087,下游等于 0.1249130.124913,差值正好是注入的源项 h=0.125h=0.125;最后全部 R˙i\dot R_i 回到零(最大 4.2×10−174.2\times10^{-17})。
The forward sweep of one design seed · ⑦ Source statement Ṙ −= Ṡ: every Ṙᵢ is back at 0⑦ Source statement Ṙ −= Ṡ: every Ṙi​ is back at 0x = 0x = 1σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​Ḋσ˙​₃ = 1u˙​0.01370.1000.09640.09310.09030.0891Ḣ0.1250.1250.1250.1250.125ṘḢ jumps only across the seeded volumes, by Ṡ (Ṡ₃ = 0.125); it is equal on the other faces
Figure 13 · The forward sweep of one design seed (cell page, N = 8, first order, seed D˙=e3\dot D=e_{3}: the source σ3\sigma_{3} up by one; the player follows the order, mesh and boundary chosen at the top). From top to bottom: the design seed, the state tangent 𝐮˙\dot{\mathbf u}, the flux tangent H˙f\dot H_f of every face and the tangent residual R˙i\dot R_i of every volume; dashed frames are the final values. First the face loop runs with 𝐮˙=0\dot{\mathbf u}=0: every H˙f\dot H_f is zero, and only the source statement makes R˙\dot R of volume 3 nonzero; that is ff. Then the first forward GS sweep: the rows upstream of the seed stay zero, and from row 3 on they light up one after another downstream. After convergence the face loop runs again with 𝐮˙=v\dot{\mathbf u}=v: H˙f\dot H_f is −0.000087-0.000087 upstream of the seed and 0.1249130.124913 downstream, the difference being exactly the injected source h=0.125h=0.125; at the end every R˙i\dot R_i is back at zero (at most 4.2×10−174.2\times10^{-17}).

把隐函数求导展开成可求解的系统

Expand implicit differentiation into a solvable system

设原始残差已经收敛,且当前光滑分支上的 AA 非奇异。取设计种子(方向)D˙\dot D,令 D(t)=D+t D˙D(t)=D+t\,\dot D;前向求解得到的状态切向量是 v≡𝐮˙=ddt𝐮(D+t D˙)∣t=0v\equiv\dot{\mathbf u}=\frac{d}{dt}\mathbf u(D+t\,\dot D)\big|_{t=0}。对每一个残差分量逐项求导,零残差的切向量仍为零:

Assume the primal residual is converged and AA is nonsingular on the current smooth branch. Choose a design seed (direction) D˙\dot D and set D(t)=D+t D˙D(t)=D+t\,\dot D; the state tangent returned by the tangent solve is v≡𝐮˙=ddt𝐮(D+t D˙)∣t=0v\equiv\dot{\mathbf u}=\frac{d}{dt}\mathbf u(D+t\,\dot D)\big|_{t=0}. Differentiate each residual component: the tangent of a zero residual is zero.

0=R˙r=∑j∂Rr∂ujvj0=\dot R_r=\sum_j\frac{\partial R_r}{\partial u_j}v_j
+∑k∂Rr∂DkD˙k(1≤r≤nu)+\sum_k\frac{\partial R_r}{\partial D_k}\dot D_k\quad(1\le r\le n_u)
⟹Av=−RD D˙.\Longrightarrow\quad Av=-R_D\,\dot D.
J^(D)=J(𝐮(D),D),\widehat{J}(D)=J(\mathbf u(D),D),
J˙=ddtJ^(D+t D˙)∣t=0=gTv+(∇DJ)T D˙.\dot J=\frac{d}{dt}\widehat{J}(D+t\,\dot D)\Big|_{t=0}=g^Tv+(\nabla_DJ)^T\,\dot D.

实现分三步:状态种子 𝐮˙=0\dot{\mathbf u}=0、设计种子 D˙\dot D,前向求值残差得到 R˙=RD D˙\dot R=R_D\,\dot D;用精确 AA 的乘积求解 vv;再以 𝐮˙=v\dot{\mathbf u}=v 和 D˙\dot D 为种子对目标前向求导,得到 J˙\dot J。一次 AvAv 只是算子调用(一次前向扫描),不是求解 vv。稳态导数的对象是收敛残差方程,不是 GS/GMRES 的迭代次数。在不可微点(如 limiter 的切换或分支判别处)以上普通导数需要另外讨论,不能无条件套用。

Implementation has three steps: seed the state with 𝐮˙=0\dot{\mathbf u}=0 and the design with D˙\dot D and evaluate the residual tangent R˙=RD D˙\dot R=R_D\,\dot D; solve for vv with exact AA products; then differentiate the objective forward with the seeds 𝐮˙=v\dot{\mathbf u}=v and D˙\dot D to obtain J˙\dot J. One AvAv is an operator application (one forward sweep), not the solution vv. The steady derivative concerns the converged residual equation, not the GS/GMRES iteration count. At nondifferentiable points (a limiter switch or a branch test) these ordinary derivatives need separate treatment.

用面循环的语句来看:右端 RDD˙R_D\dot D 是 𝐮˙=0\dot{\mathbf u}=0 时的一次前向扫描,只有 KfD˙K_f\dot D 与源项两条路径起作用;迭代中的每个 AvAv 是 D˙=0\dot D=0 时的一次前向扫描。一次前向求解的全部工作就是这段面循环以不同的种子反复执行。对 nan_a 个设计分量逐个求导,要做 nan_a 次这样的求解。

In terms of the face loop's statements: the right-hand side RDD˙R_D\dot D is one forward sweep with 𝐮˙=0\dot{\mathbf u}=0, in which only the paths KfD˙K_f\dot D and the source are active, and every AvAv of the iteration is one forward sweep with D˙=0\dot D=0. All the work of a tangent solve is this face loop run again and again with different seeds, and the derivatives with respect to all nan_a design components take nan_a such solves.

第 11 节Section 11

伴随求解:从目标函数向回传递

Adjoint solve: propagate back from the objective

为什么转置方程能省去很多前向求解

Why a transpose solve avoids many forward solves

如果有很多设计变量而只关心一个 JJ,逐个计算 vv 要解很多线性系统。利用 Av=−fAv=-f,找一个 ψ\psi 使 ATψ=−gA^T\psi=-g,便可把 gTvg^Tv 改写为只含 ff 的内积。整个推导只用矩阵转置恒等式。

With many design variables but one objective, solving for each vv requires many linear systems. Given Av=−fAv=-f, choose ψ\psi satisfying ATψ=−gA^T\psi=-g. A transpose identity rewrites gTvg^Tv as an inner product with ff, eliminating vv.

J˙=gTv=(−ATψ)Tv=−ψTAv=ψTf.\dot J=g^Tv=(-A^T\psi)^Tv=-\psi^TAv=\psi^Tf.

这里的两个负号相消。也可以采用相反号的伴随变量,但方程和梯度公式必须一起改变。本页统一用 ℒ=J+ψTR\mathcal L=J+\psi^TR,所以右端是 −g-g。伴随不是原始流场倒着求一次,而是一个由目标梯度驱动的转置线性问题。

The two minus signs cancel. An opposite adjoint sign convention is possible, but the equation and gradient formula must both change. This page uses ℒ=J+ψTR\mathcal L=J+\psi^TR, giving right-hand side −g-g. The adjoint is a transpose linear problem driven by the objective gradient, not the primal flow solved backwards.

图 14把代价直接数出来。用页首的线性求解器(默认 GS 固定点,每次一轮扫描),前向对每个设计分量解一次 Avk=−RD ekAv_k=-R_D\,e_k,每次只得到梯度的一个分量 gTvkg^Tv_k;伴随只解一次 ATψ=−gA^T\psi=-g,再做一次乘积 ψTRD\psi^TR_D 就得到全部分量。默认设置下 9 次前向求解共用 209 次 GS 扫描,伴随只用 24 次;两条路线给出的 9 个分量相差不超过 8.7×10−138.7\times10^{-13}。为什么伴随的代价与设计分量数无关,见本节末尾的推导与两条路线的代价表。

Figure 14 counts the cost. With the linear solver chosen at the top (by default the GS fixed point with one sweep per application), the tangent solves Avk=−RD ekAv_k=-R_D\,e_k once per design component and obtains one gradient component gTvkg^Tv_k each time; the adjoint solves ATψ=−gA^T\psi=-g once, and one product ψTRD\psi^TR_D then gives every component. With the default settings the 9 tangent solves take 209 GS sweeps in total and the adjoint only 24; the 9 components of the two routes differ by at most 8.7×10−138.7\times10^{-13}. Why the adjoint's cost does not depend on the number of design components is derived, with the cost table of the two routes, at the end of this section.

前向与伴随的代价 · 一次乘积 ψᵀRD 得到全部分量一次乘积 ψT​RD​ 得到全部分量前向σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​9/9 次求解,209 次 GS 扫描伴随1/1 次求解,24 次 GS 扫描条长:GS 扫描数(GS 固定点)另一刻度gT​vk​ψT​RD​σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​209 次对 24 次 GS 扫描;两组分量最大相差 8.7×10⁻¹³
图 14 · 前向与伴随的代价(格心,N = 8,一阶,页首的 GS 固定点、每次一轮扫描;播放器跟随页首的全部选择)。上:9 次前向求解 Avk=−RD ekAv_k=-R_D\,e_k,每段的长度是这次求解用的 GS 扫描数,每次只得到梯度的一个分量 gTvkg^Tv_k;下:1 次伴随求解 ATψ=−gA^T\psi=-g,再用一次乘积 ψTRD\psi^TR_D 得到全部 9 个分量(入口分量用另一个刻度)。合计 209 次对 24 次 GS 扫描;两组分量最大相差 8.7×10−138.7\times10^{-13},是线性求解停止容差的量级。
The cost of the tangent and the adjoint · One product ψᵀRD gives every componentOne product ψT​RD​ gives every componentTangentσ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​9 of 9 solves, 209 GS sweepsAdjoint1 of 1 solve, 24 GS sweepsbar length: GS sweeps (GS fixed point)own scalegT​vk​ψT​RD​σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​209 against 24 GS sweeps; the two sets of components differ by at most 8.7×10⁻¹³
Figure 14 · The cost of the tangent and the adjoint (cell page, N = 8, first order, the GS fixed point with one sweep per application chosen at the top; the player follows every choice at the top). Top: 9 tangent solves Avk=−RD ekAv_k=-R_D\,e_k, each segment as long as the GS sweeps of its solve, each giving one gradient component gTvkg^Tv_k; bottom: 1 adjoint solve ATψ=−gA^T\psi=-g, then one product ψTRD\psi^TR_D gives all 9 components (the inlet component on its own scale). In total 209 against 24 GS sweeps; the two sets of components differ by at most 8.7×10−138.7\times10^{-13}, the size of the stopping tolerance of the linear solves.

ψ 的含义:给一个控制体多加一点源项

What ψ means: add a little source to one control volume

把控制体 ii 的积分源项增加一个数 τ\tau,第 ii 行变成 Ri=Hi+1/2−Hi−1/2−(Si+τ)R_i=H_{i+1/2}-H_{i-1/2}-(S_i+\tau),其余行不变。这等于求解 R(u,D)=τeiR(u,D)=\tau e_i。按前向模式,给源项一个单位种子 S˙=ei\dot S=e_i(其余种子为零):残差的切向量 R˙=A𝐮˙−S˙\dot R=A\dot{\mathbf u}-\dot S 必须为零,所以 A𝐮˙=eiA\dot{\mathbf u}=e_i;再用伴随方程 ATψ=−gA^T\psi=-g,即 ψT=−gTA−1\psi^T=-g^TA^{-1}:

Increase the integrated source of control volume ii by a number τ\tau: row ii becomes Ri=Hi+1/2−Hi−1/2−(Si+τ)R_i=H_{i+1/2}-H_{i-1/2}-(S_i+\tau) and the other rows are unchanged. This is the same as solving R(u,D)=τeiR(u,D)=\tau e_i. In forward mode, give the source the unit seed S˙=ei\dot S=e_i (all other seeds zero): the residual tangent R˙=A𝐮˙−S˙\dot R=A\dot{\mathbf u}-\dot S must vanish, so A𝐮˙=eiA\dot{\mathbf u}=e_i; the adjoint equation ATψ=−gA^T\psi=-g, i.e. ψT=−gTA−1\psi^T=-g^TA^{-1}, then gives

J˙=gT𝐮˙=gTA−1ei=−ψTei=−ψi⟹ψi=−∂J∂Si.\dot J=g^T\dot{\mathbf u}=g^TA^{-1}e_i=-\psi^Te_i=-\psi_i\quad\Longrightarrow\quad\psi_i=-\frac{\partial J}{\partial S_i}.

所以 ψi\psi_i 是“控制体 ii 多注入单位积分源项时目标函数减少多少”。负号来自残差约定“流出减流入减源项”。若源项密度在宽度为 WiW_i 的控制体上均匀增加、种子为 s˙\dot s,则 S˙i=Wis˙\dot S_i=W_i\dot s,于是 J˙=−Wiψi s˙\dot J=-W_i\psi_i\,\dot s。格心页 Si=hσiS_i=h\sigma_i,这正是第 12 节的 dJ/dσi=−hψidJ/d\sigma_i=-h\psi_i。图 15不用伴随,直接测量这个关系:对每个 ii 解两次完整的非线性方程 R(u,D)=±τeiR(u,D)=\pm\tau e_i(步长 τ=1×10−4\tau=1\times10^{-4} 是一个数),用中心差分得到 −[J(τ)−J(−τ)]/(2τ)-[J(\tau)-J(-\tau)]/(2\tau)。两者的最大相对差是 2.7×10−82.7\times10^{-8},正是中心差分 O(τ2)O(\tau^2) 截断误差的量级。

So ψi\psi_i is the decrease of the objective when one unit of integrated source is injected into control volume ii. The minus sign comes from the residual convention "outflow minus inflow minus source". If the source density rises evenly over a volume of width WiW_i with the seed s˙\dot s, then S˙i=Wis˙\dot S_i=W_i\dot s and J˙=−Wiψi s˙\dot J=-W_i\psi_i\,\dot s. On the cell page Si=hσiS_i=h\sigma_i, which is exactly dJ/dσi=−hψidJ/d\sigma_i=-h\psi_i of Section 12. Figure 15 measures this relation without the adjoint: for every ii it solves the full nonlinear equations R(u,D)=±τeiR(u,D)=\pm\tau e_i twice (the step τ=1×10−4\tau=1\times10^{-4} is a number) and forms the central difference −[J(τ)−J(−τ)]/(2τ)-[J(\tau)-J(-\tau)]/(2\tau). The largest relative difference is 2.7×10−82.7\times10^{-8}, the size of the O(τ2)O(\tau^2) truncation error of a central difference.

图 15 · ψᵢ 的含义(N = 8,一阶)。红柱:伴随解 ψᵢ;蓝柱:不用伴随、直接测得的 −[J(τ) − J(−τ)]/(2τ),其中 J(±τ) 来自完整非线性方程 R(u, D) = ±τeᵢ 的重新求解(Newton,步长 τ = 1×10⁻⁴ 是一个数)。最大相对差 2.7×10⁻⁸,是中心差分 O(τ²) 截断误差的量级。橙色柱是 J 在各控制体测得的失配 gᵢ;ψᵢ 汇总 xᵢ 下游的失配,伴随信息从出口一侧回到入口。注入源项(种子 Ṡi​):u 在 xi​ 下游改变(原始信息从入口流向出口)ψi​ 汇总 xi​ 下游测得的失配 gj(伴随信息从出口一侧回到入口)0.000.020.040.036400.046310.049520.048230.042440.032950.021260.00967控制体 i(入口在左,出口在右)ψi​(伴随解)−[J(τ) − J(−τ)]/(2τ)(重解实测)J 在各控制体测得的失配 gi​ = Wi​(ui​ − ui​tgt)(柱高按比例)+−
图 15 · ψi 的含义(N = 8,一阶)。红柱:伴随解 ψi;蓝柱:不用伴随、直接测得的 −[J(τ) − J(−τ)]/(2τ),其中 J(±τ) 来自完整非线性方程 R(u, D) = ±τei 的重新求解(Newton,步长 τ = 1×10⁻⁴ 是一个数)。最大相对差 2.7×10⁻⁸,是中心差分 O(τ²) 截断误差的量级。橙色柱是 J 在各控制体测得的失配 gi;ψi 汇总 xi 下游的失配,伴随信息从出口一侧回到入口。
Figure 15 · What ψᵢ means (N = 8, first order). Red bars: the adjoint solution ψᵢ. Blue bars: −[J(τ) − J(−τ)]/(2τ) measured without the adjoint, where J(±τ) comes from re-solving the full nonlinear equations R(u, D) = ±τeᵢ (Newton; the step τ = 1×10⁻⁴ is a number). The largest relative difference is 2.7×10⁻⁸, the size of the O(τ²) truncation error of a central difference. Orange bars: the mismatch gᵢ measured by J in each volume; ψᵢ collects the mismatch downstream of xᵢ, so adjoint information travels back from the outlet side to the inlet.a source seed Ṡi​ at xi​ changes u downstream (inlet → outlet)ψi​ collects the mismatch gj downstream of xi​ (back from the outlet side)0.000.020.040.036400.046310.049520.048230.042440.032950.021260.00967control volume i (inlet left, outlet right)ψi​ (adjoint solution)−[J(τ) − J(−τ)]/(2τ) (re-solved)mismatch measured by J in each volume, gi​ = Wi​(ui​ − ui​tgt) (to scale)+−
Figure 15 · What ψi means (N = 8, first order). Red bars: the adjoint solution ψi. Blue bars: −[J(τ) − J(−τ)]/(2τ) measured without the adjoint, where J(±τ) comes from re-solving the full nonlinear equations R(u, D) = ±τei (Newton; the step τ = 1×10⁻⁴ is a number). The largest relative difference is 2.7×10⁻⁸, the size of the O(τ²) truncation error of a central difference. Orange bars: the mismatch gi measured by J in each volume; ψi collects the mismatch downstream of xi, so adjoint information travels back from the outlet side to the inlet.

图中的两个箭头说明方向。在 xix_i 注入的源项沿流动改变下游的 uu;JJ 在每个控制体测量失配 gj=Wj(uj−ujtgt)g_j=W_j(u_j-u^{\mathrm{tgt}}_j)(橙色柱),所以 ψi\psi_i 汇总的是 xix_i 下游的失配。出口附近下游几乎没有控制体,ψ\psi 很小;向入口方向每经过一个失配为负的控制体,ψ\psi 就增大,经过失配为正的控制体(这里在入口附近)则减小。这就是伴随“从目标函数向回传递”的含义,也是连续伴随从 λ(1)=0\lambda(1)=0 出发、向入口积分的离散版本。

The two arrows give the directions. A source injected at xix_i changes uu downstream along the flow; JJ measures the mismatch gj=Wj(uj−ujtgt)g_j=W_j(u_j-u^{\mathrm{tgt}}_j) in every volume (orange bars), so ψi\psi_i collects the mismatch downstream of xix_i. Near the outlet almost nothing lies downstream and ψ\psi is small; moving toward the inlet, ψ\psi grows across every volume with a negative mismatch and shrinks across a volume with a positive one (here near the inlet). This is what "propagating back from the objective" means, and it is the discrete counterpart of integrating the continuous adjoint from λ(1)=0\lambda(1)=0 toward the inlet.

采用拉格朗日函数 ℒ=J+ψTRq\mathcal L=J+\psi^TR_q,令 ℒ˙\dot{\mathcal L} 中状态切向量 𝐮˙\dot{\mathbf u} 的系数为零,得到 AqTψ=−gA_q^T\psi=-g。修正方程使用 PTP^T,并按相反方向扫描;修正量记作 Δψ\Delta\psi。

With ℒ=J+ψTRq\mathcal L=J+\psi^TR_q, setting the coefficient of the state tangent 𝐮˙\dot{\mathbf u} in ℒ˙\dot{\mathcal L} to zero gives AqTψ=−gA_q^T\psi=-g. The correction uses PTP^T, scans in the opposite direction and is written Δψ\Delta\psi.

PTΔψ=−(AqTψ+g),(diag⁡(P)+LT)Δψ(ℓ+1)=−(AqTψ+g)−UTΔψ(ℓ).P^T\Delta\psi=-(A_q^T\psi+g),\qquad (\operatorname{diag}(P)+L^T)\Delta\psi^{(\ell+1)}=-(A_q^T\psi+g)-U^T\Delta\psi^{(\ell)}.

相同固定次数、零初值的反向扫描恰好构成 BsTB_s^T,不是把矩阵转置后仍沿原方向扫描。于是实际更新和误差传播如下。

With the same fixed sweep count and zero initial correction, reverse sweeps implement BsTB_s^T. Transposing the matrix while retaining the original sweep direction would give a different preconditioner. The actual update and error propagation are therefore:

ψm+1=ψm−ωBsT(AqTψm+g),eam+1=(I−ωBsTAqT)eam.\psi^{m+1}=\psi^m-\omega B_s^T(A_q^T\psi^m+g),\qquad e_a^{m+1}=(I-\omega B_s^TA_q^T)e_a^m.

注意 I−ωBsTAqTI-\omega B_s^TA_q^T 并不直接等于 tangent 迭代矩阵的转置,因为乘法顺序不同。这里解的是残差伴随变量 ψ\psi;若逆向微分整个迭代程序,需要额外说明其迭代伴随变量与 ψ\psi 的转换。

Note that I−ωBsTAqTI-\omega B_s^TA_q^T is not directly the transpose of the tangent iteration matrix: the multiplication order differs. We solve for the residual adjoint ψ\psi. Reverse differentiation of the entire iteration program would require an additional relation between its iterative adjoint variable and ψ\psi.

伴随固定点求解与反向 GS

Adjoint fixed point and reverse GS

g = objective_state_gradient(u)
psi = zeros(M); PT = transpose(first_order_jacobian(u))
repeat:
    r = adjoint_product(u, psi) + g    # A^T psi + g, one reverse sweep seeded with psi
    if norm(r) <= tolerance: return psi
    delta = zeros(M)                   # the correction Δψ
    repeat fixed_number_of_sweeps:
        for i = M-1,...,0:
            delta[i] = (-r[i] - Σ(j≠i) PT[i,j]*delta[j]) / PT[i,i]
    psi = psi + omega*delta

一轮正向 GS 解下三角系统 (diag⁡(P)+L)x=b(\operatorname{diag}(P)+L)x=b,所以 B1=(diag⁡(P)+L)−1B_1=(\operatorname{diag}(P)+L)^{-1};转置后是 B1T=(diag⁡(P)+LT)−1B_1^T=(\operatorname{diag}(P)+L^T)^{-1},这是上三角系统,需要从最后一行往前解。对固定的多轮扫描,同样需要转置整个零初值映射,而不只是矩阵名加一个 T。下方检验也比较 wTBsvw^TB_sv 与 vTBsTwv^TB_s^Tw。

One forward GS sweep solves (diag⁡(P)+L)x=b(\operatorname{diag}(P)+L)x=b, so B1=(diag⁡(P)+L)−1B_1=(\operatorname{diag}(P)+L)^{-1}. Its transpose is B1T=(diag⁡(P)+LT)−1B_1^T=(\operatorname{diag}(P)+L^T)^{-1}, an upper-triangular solve performed from the last row backwards. With several fixed sweeps, transpose the entire zero-start map, not merely the matrix name. The page also checks wTBsv=vTBsTww^TB_sv=v^TB_s^Tw.

把伴随方程逐行写出来

The adjoint equation written out row by row

转置把第 6 节的每个系数移到镜像位置。一阶时,ATψ=−gA^T\psi=-g 的第 ii 行收集的是 AA 的第 ii 列的三个元素:

Transposition moves every coefficient of Section 6 to its mirror position. At first order, row ii of ATψ=−gA^T\psi=-g collects the three entries of column ii of AA:

(ATψ)i=Ai−1,iψi−1+Aiiψi+Ai+1,iψi+1=cRi−1/2ψi−1+(cLi+1/2−cRi−1/2)ψi−cLi+1/2ψi+1.(A^T\psi)_i=A_{i-1,i}\psi_{i-1}+A_{ii}\psi_i+A_{i+1,i}\psi_{i+1}=c_R^{i-1/2}\psi_{i-1}+\big(c_L^{i+1/2}-c_R^{i-1/2}\big)\psi_i-c_L^{i+1/2}\psi_{i+1}.

按面重新分组,就是反向面循环算出的结果:第 ii 行等于右面的 aˉ=cLHˉi+1/2\bar a=c_L\bar H_{i+1/2} 加上左面的 bˉ=cRHˉi−1/2\bar b=c_R\bar H_{i-1/2},其中 Hˉ\bar H 是 ψ\psi 跨过该面的差:

Regrouped by faces, this is exactly what the reverse face loop computes: row ii is the aˉ=cLHˉi+1/2\bar a=c_L\bar H_{i+1/2} of its right face plus the bˉ=cRHˉi−1/2\bar b=c_R\bar H_{i-1/2} of its left face, where Hˉ\bar H is the jump of ψ\psi across that face:

cLi+1/2 (ψi−ψi+1)+cRi−1/2 (ψi−1−ψi)=cLi+1/2Hˉi+1/2+cRi−1/2Hˉi−1/2=−gi.c_L^{i+1/2}\,(\psi_i-\psi_{i+1})+c_R^{i-1/2}\,(\psi_{i-1}-\psi_i)=c_L^{i+1/2}\bar H_{i+1/2}+c_R^{i-1/2}\bar H_{i-1/2}=-g_i.

一阶时这两个系数在同一个状态上计算:cLi+1/2=ui/2+εc_L^{i+1/2}=u_i/2+\varepsilon,cRi−1/2=ui/2−εc_R^{i-1/2}=u_i/2-\varepsilon,所以对角元是 2ε=1.62\varepsilon=1.6。代入并除以 −h-h(gi=h(ui−uitgt)g_i=h(u_i-u^{\mathrm{tgt}}_i)),第 ii 行成为

At first order the two coefficients are evaluated at the same state, cLi+1/2=ui/2+εc_L^{i+1/2}=u_i/2+\varepsilon and cRi−1/2=ui/2−εc_R^{i-1/2}=u_i/2-\varepsilon, so the diagonal entry is 2ε=1.62\varepsilon=1.6. Substituting and dividing by −h-h (with gi=h(ui−uitgt)g_i=h(u_i-u^{\mathrm{tgt}}_i)), row ii becomes

ui ψi+1−ψi−12h+εh ψi+1−2ψi+ψi−1h2=ui−uitgt.u_i\,\frac{\psi_{i+1}-\psi_{i-1}}{2h}+\varepsilon h\,\frac{\psi_{i+1}-2\psi_i+\psi_{i-1}}{h^2}=u_i-u^{\mathrm{tgt}}_i.

令 λ=−ψ\lambda=-\psi:第一项是 −uλ′-u\lambda' 的中心差分,第二项是 −εhλ′′-\varepsilon h\lambda''。所以离散伴随方程就是连续伴随方程 −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}}(第 16 节、附录 A)加上一个 O(h)O(h) 的数值耗散,一阶格式的伴随因此在远离入口处一阶收敛到 λ\lambda。入口处不然:第 16 节的入口伴随边界层使入口第一个控制体的误差不随加密消失,全域 L2L^2 误差只有半阶。这一行还说明了方向:把它解成 ψi=(cLψi+1−cRψi−1−gi)/(cL−cR)\psi_i=\big(c_L\psi_{i+1}-c_R\psi_{i-1}-g_i\big)/(c_L-c_R),下游邻居 ψi+1\psi_{i+1} 的权重 cL/(cL−cR)c_L/(c_L-c_R) 在本页各内部行上介于 0.8480.848 与 0.9250.925 之间(入口一侧的第 0 行取 ψ−1=0\psi_{-1}=0,权重是 0.8310.831):ψi\psi_i 主要由下游决定,所以从出口一侧开始的反向 GS 扫描收敛得快。

With λ=−ψ\lambda=-\psi the first term is a centred difference of −uλ′-u\lambda' and the second is −εhλ′′-\varepsilon h\lambda''. So the discrete adjoint equation is the continuous adjoint equation −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}} (Section 16, Appendix A) plus a numerical dissipation of order hh, and away from the inlet the adjoint of the first-order scheme converges to λ\lambda at first order. Not at the inlet: the inlet adjoint boundary layer of Section 16 keeps the error of the first control volume from vanishing under refinement, and the global L2L^2 error is only of order one half. The row also shows the direction: solved as ψi=(cLψi+1−cRψi−1−gi)/(cL−cR)\psi_i=\big(c_L\psi_{i+1}-c_R\psi_{i-1}-g_i\big)/(c_L-c_R), the weight cL/(cL−cR)c_L/(c_L-c_R) of the downstream neighbour ψi+1\psi_{i+1} lies between 0.8480.848 and 0.9250.925 on the interior rows of this page (row 0, on the inlet side, takes ψ−1=0\psi_{-1}=0 and has the weight 0.8310.831): ψi\psi_i is set mainly by what lies downstream, which is why the reverse GS sweep that starts on the outlet side converges fast.

两端的行不同(下式)。入口单元没有左邻:第 0 行里 cRinc_R^{in} 乘的是 ψ0\psi_0 本身,相当于在内部行的公式里取 ψ−1=0\psi_{-1}=0,即把入口外的伴随当作零,这正是第 16 节入口伴随边界层的来源。出口行只含物理通量的 uoutu_{out},所以 ψM−1=O(h)\psi_{M-1}=O(h),对应连续伴随的终端条件 λ(1)=0\lambda(1)=0。

The end rows differ (below). The inlet cell has no left neighbour: in row 0, cRinc_R^{in} multiplies ψ0\psi_0 itself, as if ψ−1=0\psi_{-1}=0 in the interior formula; the adjoint outside the inlet is taken to be zero, which is the source of the inlet adjoint boundary layer of Section 16. The outlet row holds only the uoutu_{out} of the physical flux, so ψM−1=O(h)\psi_{M-1}=O(h): the counterpart of the terminal condition λ(1)=0\lambda(1)=0 of the continuous adjoint.

(ATψ)0=cL1/2(ψ0−ψ1)+cRin(0−ψ0)=−g0,(ATψ)M−1=uoutψM−1+cRM−3/2(ψM−2−ψM−1)=−gM−1.\begin{aligned}(A^T\psi)_0&=c_L^{1/2}(\psi_0-\psi_1)+c_R^{in}(0-\psi_0)=-g_0,\\(A^T\psi)_{M-1}&=u_{out}\psi_{M-1}+c_R^{M-3/2}(\psi_{M-2}-\psi_{M-1})=-g_{M-1}.\end{aligned}

下面的动画用伴随 GS 固定点求解 ATψ=−gA^T\psi=-g(不论页首选择哪种线性求解器,这里都是 GS)。先逐行播放第一次修正:反向扫描从最后一行走到第 0 行,ψ\psi 从出口一侧开始出现,逐步延伸到入口。这与图 15的方向一致,也与连续伴随从 λ(1)=0\lambda(1)=0 出发、向入口积分的方向相同。之后每帧是一次外层迭代,下方的误差柱是出口一侧先变短;与上面的原始动画一样,迭代越过停止准则,一直做到残差停在舍入平台上。

The animation below solves ATψ=−gA^T\psi=-g with the adjoint GS fixed point (always GS here, whichever linear solver is selected at the top). The first correction is played row by row: the reverse sweep runs from the last row to row 0, so ψ\psi appears on the outlet side first and extends toward the inlet. This is the direction of Figure 15, and the direction in which the continuous adjoint is integrated from λ(1)=0\lambda(1)=0 toward the inlet. After that, each frame is one outer iteration, and the error bars below shrink on the outlet side first; as in the primal animation, the iteration continues past the stopping criterion until the residual rests on its round-off floor.

从有限维 Lagrangian 逐步消去状态敏感度

Eliminate state sensitivity from a finite-dimensional Lagrangian

ℒ(𝐮,D,ψ)=J(𝐮,D)+ψTR(𝐮,D).\mathcal L(\mathbf u,D,\psi)=J(\mathbf u,D)+\psi^TR(\mathbf u,D).

在满足 R=0R=0 的解上 ℒ=J\mathcal L=J。先把 ψ\psi 固定,沿切向量 𝐮˙\dot{\mathbf u}、D˙\dot D 对 ℒ\mathcal L 前向求导,再按 𝐮˙\dot{\mathbf u} 和 D˙\dot D 收集系数。即使 ψ\psi 随设计改变,额外的 ψ˙TR\dot\psi^TR 项也在收敛解上为零。

At a feasible state R=0R=0, ℒ=J\mathcal L=J. Hold ψ\psi fixed, differentiate ℒ\mathcal L forward along the tangents 𝐮˙\dot{\mathbf u} and D˙\dot D, then collect the coefficients of 𝐮˙\dot{\mathbf u} and D˙\dot D. Even if ψ\psi varies with the design, the extra ψ˙TR\dot\psi^TR term vanishes at the converged state.

ℒ˙=gT𝐮˙+(∇DJ)TD˙+ψT(A𝐮˙+RDD˙)\dot{\mathcal L}=g^T\dot{\mathbf u}+(\nabla_DJ)^T\dot D+\psi^T(A\dot{\mathbf u}+R_D\dot D)
=(g+ATψ)T𝐮˙+(∇DJ+RDTψ)TD˙.=(g+A^T\psi)^T\dot{\mathbf u}+(\nabla_DJ+R_D^T\psi)^T\dot D.

选择 ψ\psi 使未知的状态敏感度前面的系数为零。于是一个目标只需求解一次伴随线性系统,再进行设计反传;总设计梯度是列向量 ∇DJ^\nabla_D\widehat J。注意符号来自这里的加号约定,并不是因为“伴随一定带负号”。

Choose ψ\psi to cancel the coefficient of the unknown state sensitivity. One objective then requires one adjoint solve followed by design reverse accumulation; the total design gradient ∇DJ^\nabla_D\widehat J is a column. Its sign follows from the plus convention in this Lagrangian, not from a universal negative sign for adjoints.

ATψ=−g,∇DJ^=∇DJ+RDTψ,\boxed{A^T\psi=-g},\qquad\boxed{\nabla_D\widehat J=\nabla_DJ+R_D^T\psi},
dJ^dDk=∂J∂Dk+∑rψr∂Rr∂Dk.\frac{d\widehat J}{dD_k}=\frac{\partial J}{\partial D_k}+\sum_r\psi_r\frac{\partial R_r}{\partial D_k}.

用反向模式的横线记号:以 Rˉ=ψ\bar R=\psi 为种子运行残差的反向扫描,得到 𝐮ˉ=ATψ=−g\bar{\mathbf u}=A^T\psi=-g,这就是伴随方程。再把目标自己的反向语句(种子 Jˉ=1\bar J=1)累加进同一组横线量:它们给 𝐮ˉ\bar{\mathbf u} 加上 gg,给 Dˉ\bar D 加上 ∇DJ\nabla_DJ,于是 𝐮ˉ=g+ATψ=0\bar{\mathbf u}=g+A^T\psi=0。伴随方程说的正是状态的横线量总和为零:状态不再把敏感度往下传,同一次扫描的设计伴随 Dˉ=∇DJ+RDTψ\bar D=\nabla_DJ+R_D^T\psi 就是梯度 ∇DJ^\nabla_D\widehat J。也可以直接检查与 tangent 的等价性:利用 Av=−RD D˙Av=-R_D\,\dot D,gTv=−ψTAv=ψTRD D˙g^Tv=-\psi^TAv=\psi^TR_D\,\dot D,所以两条路线给出同一个 J˙=DˉTD˙\dot J=\bar D^T\dot D。更换为 ℒ=J−λTR\mathcal L=J-\lambda^TR 会得到 λ=−ψ\lambda=-\psi,但这只是两个离散乘子的符号约定,不能据此认定连续场 λ(x)\lambda(x) 与离散向量逐点相等。

In the bar notation of reverse mode: the residual's reverse sweep seeded with Rˉ=ψ\bar R=\psi returns 𝐮ˉ=ATψ=−g\bar{\mathbf u}=A^T\psi=-g, which is the adjoint equation. Accumulate the objective's own reverse statements (seeded with Jˉ=1\bar J=1) into the same bars: they add gg to 𝐮ˉ\bar{\mathbf u} and ∇DJ\nabla_DJ to Dˉ\bar D, so 𝐮ˉ=g+ATψ=0\bar{\mathbf u}=g+A^T\psi=0. The adjoint equation says exactly that the state's total bar vanishes: the state passes no sensitivity on, and the design adjoint of the same sweep, Dˉ=∇DJ+RDTψ\bar D=\nabla_DJ+R_D^T\psi, is the gradient ∇DJ^\nabla_D\widehat J. Check equivalence with the tangent: Av=−RD D˙Av=-R_D\,\dot D gives gTv=−ψTAv=ψTRD D˙g^Tv=-\psi^TAv=\psi^TR_D\,\dot D, so both routes give the same J˙=DˉTD˙\dot J=\bar D^T\dot D. Changing to ℒ=J−λTR\mathcal L=J-\lambda^TR yields λ=−ψ\lambda=-\psi, but that relates two discrete sign conventions; it does not prove pointwise equality with a continuous field λ(x)\lambda(x).

精确算子决定求解哪个方程;GS 固定点、GS 预处理 GMRES 与 LU(附录 C)决定如何求解。较便宜的近似 Jacobian(例如一阶或冻结部分系数的 Jacobian)可以用于迭代修正或预处理,但不能替代精确转置。应监控真残差 rψ=ATψ+gr_\psi=A^T\psi+g。若其不为零,当前伴随与精确 tangent 的方向导数差为 −rψTv-r_\psi^Tv,因此只看迭代次数或预处理残差不够。

The exact operator determines the equation; the GS fixed point, GS-preconditioned GMRES and LU (Appendix C) determine how it is solved. A cheaper approximate Jacobian (first order, or with some coefficients frozen) may supply corrections or preconditioning, but cannot replace the exact transpose. Monitor the true residual rψ=ATψ+gr_\psi=A^T\psi+g. If it is nonzero, the current adjoint directional derivative minus the exact tangent derivative is −rψTv-r_\psi^Tv; an iteration count or a preconditioned residual alone is insufficient.

为什么一次伴随求解能代替 nan_a 次前向求解

Why one adjoint solve replaces nan_a tangent solves

同样的结论也可以不经过 Lagrangian,直接由前向求解的两个关系得到:

The same result follows without a Lagrangian, directly from the two relations of the tangent solve:

J˙=gT𝐮˙+(∇DJ)TD˙,𝐮˙=−A−1RDD˙\dot J=g^T\dot{\mathbf u}+(\nabla_DJ)^T\dot D,\qquad \dot{\mathbf u}=-A^{-1}R_D\dot D
⟹J˙=(∇DJ)TD˙−gT A−1 RDD˙.\Longrightarrow\qquad \dot J=(\nabla_DJ)^T\dot D-g^T\,A^{-1}\,R_D\dot D.

最后一项是三个因子 gTg^T、A−1A^{-1}、RDD˙R_D\dot D 的乘积,括号可以从两边加。前向路线先算 A−1(RDD˙)A^{-1}(R_D\dot D):它依赖方向 D˙\dot D,每个设计分量都要解一次以 AA 为系数的方程,全部分量要解 nan_a 次。伴随路线先算行向量 gTA−1g^TA^{-1}:它与 D˙\dot D 无关,只要解一次。把这个行向量记作 −ψT-\psi^T:

The last term is the product of three factors, gTg^T, A−1A^{-1} and RDD˙R_D\dot D, which can be bracketed from either side. The tangent route computes A−1(RDD˙)A^{-1}(R_D\dot D) first: it depends on the direction D˙\dot D, so every design component needs one solve with AA, nan_a solves for all of them. The adjoint route computes the row gTA−1g^TA^{-1} first: it does not depend on D˙\dot D, so it is solved once. Call this row −ψT-\psi^T:

ψT=−gT A−1⟺ATψ=−g,\psi^T=-g^T\,A^{-1}\quad\Longleftrightarrow\quad A^T\psi=-g,
J˙=(∇DJ+RDTψ)TD˙=DˉTD˙.\dot J=\big(\nabla_DJ+R_D^T\psi\big)^T\dot D=\bar D^T\dot D.

这对每个方向 D˙\dot D 都成立,所以梯度就是上面方框里的 ∇DJ^=Dˉ\nabla_D\widehat J=\bar D:一次伴随求解之后,再以 Rˉ=ψ\bar R=\psi 为种子做一次反向扫描,就得到全部分量。两条路线的计算量(本页的 nan_a 与 nun_u 由本页代码数出,与页首路线图相同):

This holds for every direction D˙\dot D, so the gradient is the boxed ∇DJ^=Dˉ\nabla_D\widehat J=\bar D above: after one adjoint solve, one reverse sweep seeded with Rˉ=ψ\bar R=\psi gives every component. The cost of the two routes (nan_a and nun_u of this page, counted by its code, as in the page roadmap at the top):

前向(tangent)路线Tangent route伴随(adjoint)路线Adjoint route
每个目标的线性求解Linear solves per objectivena=9n_a=9 次,系数 AAna=9n_a=9, with AA1 次,系数 ATA^T1, with ATA^T
右端Right-hand sides−RD ek,-R_D\,e_k, k=0,…,na−1k=0,\ldots,n_a-1−g-g
求解之后After the solvesnan_a 次前向扫描,各得一个 J˙\dot Jnan_a forward sweeps, one J˙\dot J each1 次反向扫描,得到整个 Dˉ\bar D1 reverse sweep, the whole Dˉ\bar D
一次求解给出One solve gives一个分量 dJ^/dDkd\widehat{J}/dD_kone component dJ^/dDkd\widehat{J}/dD_k全部 nan_a 个分量all nan_a components
nJn_J 个目标时With nJn_J objectives仍是 nan_a 次(vv 与目标无关)still nan_a (vv does not depend on the objective)nJn_J 次nJn_J

本页 na=9n_a=9、nu=8n_u=8:前向路线要解 9 个 8 阶的线性方程组,伴随路线只解 1 个。设计变量多而目标少时(形状优化通常如此)伴随路线便宜得多;设计变量少而目标多时,前向路线更合适。

This page has na=9n_a=9 and nu=8n_u=8: the tangent route solves 9 linear systems of order 8, the adjoint route one. With many design variables and few objectives (the usual case in shape optimisation) the adjoint route is far cheaper; with few design variables and many objectives the tangent route is the better choice.

第 12 节Section 12

设计导数与反问题

Design derivatives and inverse design

一次伴随求解之后,每个导数只是内积

After one adjoint solve, each derivative is an inner product

前向模式要把设计种子依次取为单位向量 D˙=ek\dot D=e_k,才得到梯度的各个分量;反向模式一次给出全部分量 Dˉ\bar D。本例源项导数非常稀疏:格心一个源区间影响一个控制体;格点一个源区间影响左右两个半控制体,但入口约束行不保留源项。伴随把这些局部直接影响与全局目标权重配对。

Forward mode needs the design seed D˙=ek\dot D=e_k for each kk in turn to obtain the gradient components; reverse mode gives all of them, Dˉ\bar D, at once. Source derivatives are sparse: one cell interval affects one volume; one node-based interval affects two half-volumes, with the inlet constraint row excluding its source contribution. The adjoint pairs these local direct effects with global objective weights.

∂Ri∂σk=−hδik⟹dJdσk=−hψk.\frac{\partial R_i}{\partial\sigma_k}=-h\delta_{ik}\quad\Longrightarrow\quad\frac{dJ}{d\sigma_k}=-h\psi_k.

负梯度给出目标局部下降方向,但大步长可能越过线性近似有效的范围。演示中的 BFGS 利用历史梯度差构造逆 Hessian 近似,再用回溯缩短步长,直到目标充分下降。每个候选设计都需要重新解原始方程;伴随提供的是该设计处的梯度。

The negative gradient is a local descent direction, but a large step can leave the range where linear prediction is useful. The demo’s BFGS uses gradient differences to approximate an inverse Hessian, then backtracks until the objective decreases sufficiently. Each candidate design requires a fresh primal solution; its adjoint supplies the gradient there.

一次伴随求解即可计算所有源项和入口导数;本例目标函数对设计变量没有显式依赖,所以梯度只剩伴随与残差设计偏导的内积,也就是反向模式的设计伴随 Dˉ=Rq,DTψ\bar D=R_{q,D}^T\psi。tangent 对每个设计种子 D˙\dot D 单独求解,结果应与这个内积一致。

One adjoint solve yields the derivatives for every source and for the inlet. The objective has no explicit design dependence here, so the gradient is just the adjoint contracted with the residual design derivatives: the design adjoint Dˉ=Rq,DTψ\bar D=R_{q,D}^T\psi of reverse mode. A tangent solve handles one design seed D˙\dot D at a time and must agree with that contraction.

J˙=gTv=ψTRq,DD˙=DˉTD˙,dJdDk=ψT∂Rq∂Dk=Dˉk.\dot J=g^Tv=\psi^TR_{q,D}\dot D=\bar D^T\dot D,\qquad \frac{dJ}{dD_k}=\psi^T\frac{\partial R_q}{\partial D_k}=\bar D_k.

每个源项导数为 dJ/dσi=−hψidJ/d\sigma_i=-h\psi_i。入口导数来自边界通量的外部状态偏导:dJ/duin=−ψ0 ∂Hin/∂uindJ/du_{in}=-\psi_0\,\partial H_{in}/\partial u_{in}。外部入口状态与内部重构迹是不同的求导路径。

Source derivatives are dJ/dσi=−hψidJ/d\sigma_i=-h\psi_i. The inlet derivative comes from the boundary flux’s exterior-state derivative: dJ/duin=−ψ0 ∂Hin/∂uindJ/du_{in}=-\psi_0\,\partial H_{in}/\partial u_{in}. The exterior inlet state and the reconstructed interior trace have distinct differentiation paths.

设计伴随从哪里来

Where the design adjoint comes from

以 Rˉ=ψ\bar R=\psi 为种子运行第 9 节的反向代码,并让它同时累加设计输入的伴随。读取设计的只有两类语句,所以 Dˉ\bar D 只从两处收到贡献。源项来自反向语句 7:Sˉi=−ψi\bar S_i=-\psi_i,再按 SiS_i 对 σk\sigma_k 的偏导分给源区间,σˉk=hSˉk=−hψk\bar\sigma_k=h\bar S_k=-h\psi_k。入口值来自入口面上的反向语句 2,它把 aˉ\bar a 加到 uˉin\bar u_{in}:uˉin=cLinHˉin=−cLinψ0\bar u_{in}=c_L^{in}\bar H_{in}=-c_L^{in}\psi_0。合起来:

Run the reverse code of Section 9 with the seed Rˉ=ψ\bar R=\psi and let it also accumulate the adjoints of the design inputs. The design is read by two kinds of statement only, so Dˉ\bar D collects from two places. The sources come from reverse statement 7: Sˉi=−ψi\bar S_i=-\psi_i, passed on to the source intervals by the partial derivatives of SiS_i with respect to σk\sigma_k, σˉk=hSˉk=−hψk\bar\sigma_k=h\bar S_k=-h\psi_k; the inlet value comes from reverse statement 2 on the inlet face, which adds aˉ\bar a into uˉin\bar u_{in}: uˉin=cLinHˉin=−cLinψ0\bar u_{in}=c_L^{in}\bar H_{in}=-c_L^{in}\psi_0. Together:

Dˉ=(σˉ0,…,σˉN−1,uˉin)=RDTψ,dJdDk=Dˉk.\bar D=(\bar\sigma_0,\ldots,\bar\sigma_{N-1},\bar u_{in})=R_D^T\psi,\qquad \frac{dJ}{dD_k}=\bar D_k.

图 16把这一次反向扫描逐句播放:种子 ψ\psi 从残差端出发,状态伴随 uˉ\bar u 与设计伴随 Dˉ\bar D 用 += 逐步累加,最后 uˉ=−g\bar u=-g(伴随方程成立),Dˉ\bar D 就是梯度。图 17把 ψ\psi 与梯度的每个分量画成一张灵敏度图,并用中心差分逐个核对;与图 14的伴随结果一样,这些分量只用了一次伴随求解。

Figure 16 plays this reverse sweep statement by statement: the seed ψ\psi starts at the residual end, the state adjoint uˉ\bar u and the design adjoint Dˉ\bar D accumulate with +=, and at the end uˉ=−g\bar u=-g (the adjoint equation holds) and Dˉ\bar D is the gradient. Figure 17 draws ψ\psi and every gradient component as a sensitivity map and checks each by a central difference; like the adjoint results of Figure 14, these components cost a single adjoint solve.

以 R̄ = ψ 为种子的反向扫描 · 完成:ū = Aᵀψ = −g,D̄ = RDᵀψ 就是梯度完成:ū = AT​ψ = −g,D¯​ = RD​T​ψ 就是梯度x = 0x = 1R¯​0.03640.04630.04950.04820.04240.03290.02120.00965H¯​ū−0.00328−0.001880.002460.007890.01280.01570.01570.0124D¯​uin​σ¯​₀σ¯​₁σ¯​₂σ¯​₃σ¯​₄σ¯​₅σ¯​₆σ¯​₇max |ū + g| = 1.7×10⁻¹⁷,max |D¯​ − dJ/dD| = 0
图 16 · 以 Rˉ=ψ\bar R=\psi 为种子的反向扫描(格心,N = 8,一阶;播放器跟随页首的选择)。从上到下:残差种子 Rˉ\bar R、各面的 Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R、状态伴随 𝐮ˉ\bar{\mathbf u}、设计伴随 Dˉ=(σˉ0,…,σˉ7,uˉin)\bar D=(\bar\sigma_0,\ldots,\bar\sigma_{7},\bar u_{in});虚框是终值 ψ\psi、−g-g 与梯度。语句按原始代码的相反顺序执行:先是源项语句(Sˉi=−Rˉi\bar S_i=-\bar R_i,σˉ+=(∂S/∂σ)TSˉ\bar\sigma\mathrel{\text{+=}}(\partial S/\partial\sigma)^T\bar S),然后从最后一个面往回(aˉ=cLHˉf\bar a=c_L\bar H_f,bˉ=cRHˉf\bar b=c_R\bar H_f,𝐮ˉ+=TLTaˉ+TRTbˉ\bar{\mathbf u}\mathrel{\text{+=}}T_L^T\bar a+T_R^T\bar b;入口面另有 uˉin+=aˉ\bar u_{in}\mathrel{\text{+=}}\bar a)。一阶时每个 uˉj\bar u_j 从两个面收到贡献,都用 += 累加。最后 𝐮ˉ=ATψ=−g\bar{\mathbf u}=A^T\psi=-g(最大差 1.7×10−171.7\times10^{-17}),Dˉ=RDTψ\bar D=R_D^T\psi 就是梯度。
The reverse sweep seeded with R̄ = ψ · Done: ū = Aᵀψ = −g, and D̄ = RDᵀψ is the gradientDone: ū = AT​ψ = −g, and D¯​ = RD​T​ψ is the gradientx = 0x = 1R¯​0.03640.04630.04950.04820.04240.03290.02120.00965H¯​ū−0.00328−0.001880.002460.007890.01280.01570.01570.0124D¯​uin​σ¯​₀σ¯​₁σ¯​₂σ¯​₃σ¯​₄σ¯​₅σ¯​₆σ¯​₇max |ū + g| = 1.7×10⁻¹⁷, max |D¯​ − dJ/dD| = 0
Figure 16 · The reverse sweep seeded with Rˉ=ψ\bar R=\psi (cell page, N = 8, first order; the player follows the choices at the top). From top to bottom: the residual seed Rˉ\bar R, the face adjoints Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R, the state adjoint 𝐮ˉ\bar{\mathbf u} and the design adjoint Dˉ=(σˉ0,…,σˉ7,uˉin)\bar D=(\bar\sigma_0,\ldots,\bar\sigma_{7},\bar u_{in}); dashed frames are the final values ψ\psi, −g-g and the gradient. The statements run in the reverse order of the primal code: first the source statements (Sˉi=−Rˉi\bar S_i=-\bar R_i, σˉ+=(∂S/∂σ)TSˉ\bar\sigma\mathrel{\text{+=}}(\partial S/\partial\sigma)^T\bar S), then the faces from the last one back (aˉ=cLHˉf\bar a=c_L\bar H_f, bˉ=cRHˉf\bar b=c_R\bar H_f, 𝐮ˉ+=TLTaˉ+TRTbˉ\bar{\mathbf u}\mathrel{\text{+=}}T_L^T\bar a+T_R^T\bar b; the inlet face also uˉin+=aˉ\bar u_{in}\mathrel{\text{+=}}\bar a). At first order every uˉj\bar u_j receives contributions from two faces, accumulated with +=. At the end 𝐮ˉ=ATψ=−g\bar{\mathbf u}=A^T\psi=-g (largest difference 1.7×10−171.7\times10^{-17}), and Dˉ=RDTψ\bar D=R_D^T\psi is the gradient.
默认设计处的灵敏度图:伴随解与梯度的每个分量伴随解 ψi​(各控制体)0.000.020.0400.250.50.751梯度 dJ/dDk​ = D¯​k​(柱)与中心差分(圆)−6−4−20×10⁻³σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​−0.0474入口分量用另一个刻度;圆与柱的最大相对差 1.7×10⁻¹⁰
图 17 · 默认设计处的灵敏度图(格心,N = 8,一阶)。上:伴随解 ψi\psi_i;下:梯度的每个分量 Dˉk=dJ/dDk\bar D_k=dJ/dD_k,源项分量画在各自的区间上,入口分量画在左侧、用另一个刻度;圆圈是中心差分(τ=10−5\tau=10^{-5},每次重解完整非线性方程)测得的同一分量,最大相对差 1.7×10−101.7\times10^{-10}。全部分量都为负:增大任何一个源项或入口值都会使 JJ 减小。
The sensitivity map at the default design: the adjoint and every gradient componentadjoint solution ψi​ (per control volume)0.000.020.0400.250.50.751gradient dJ/dDk​ = D¯​k​ (bars) and central differences (circles)−6−4−20×10⁻³σ₀σ₁σ₂σ₃σ₄σ₅σ₆σ₇uin​−0.0474the inlet component on its own scale; circles and bars differ by at most 1.7×10⁻¹⁰ (relative)
Figure 17 · The sensitivity map at the default design (cell page, N = 8, first order). Top: the adjoint solution ψi\psi_i; bottom: every gradient component Dˉk=dJ/dDk\bar D_k=dJ/dD_k, the source components on their intervals and the inlet component on the left on its own scale; circles are the same components measured by central differences (τ=10−5\tau=10^{-5}, each re-solving the full nonlinear equations), largest relative difference 1.7×10−101.7\times10^{-10}. Every component is negative: raising any source or the inlet value lowers JJ.

用梯度调整源项

Use the gradient to adjust the source

反问题演示只优化源项,固定入口,避免入口与首源项近似互相补偿。每次 BFGS 步都重新收敛原始方程并计算伴随,使用 Armijo 回溯接受下降步;重置或切换格式会恢复基准设计。

The inverse-design demo optimises only sources and fixes the inlet, avoiding near compensation between the inlet and first source. Each BFGS step reconverges the primal and adjoint equations and uses Armijo backtracking to accept a descent step. Resetting or changing discretisation restores the base design.

第 13 节Section 13

有限差分:检查总导数

Finite differences: check total derivatives

用不经过伴随的计算作对照

Check with a computation that does not use the adjoint

有限差分直接改变设计、重新求解、再比较目标。步长 τ\tau 是一个数(不是网格宽度 hh,也不是求导记号)。对 J(D+τek)J(D+\tau e_k) 和 J(D−τek)J(D-\tau e_k) 分别作 Taylor 展开,相减后偶次项消去;除以 2τ2\tau 得到中心差商 FDτc\mathrm{FD}_\tau^{\mathrm c},它的首个截断误差是二阶。

Finite differences change the design, solve again, then compare objectives. The step τ\tau is a number (neither the mesh width hh nor a derivative symbol). Taylor-expand J(D+τek)J(D+\tau e_k) and J(D−τek)J(D-\tau e_k); subtraction cancels the even powers, and division by 2τ2\tau gives the central quotient FDτc\mathrm{FD}_\tau^{\mathrm c}, whose leading truncation error is second order.

FDτc=JDk+τ26JDkDkDk+O(τ4).\mathrm{FD}_\tau^{\mathrm c}=J_{D_k}+\frac{\tau^2}{6}J_{D_kD_kD_k}+O(\tau^4).

但两次目标都有舍入和求解误差,作差再除以很小的 τ\tau 会把这些误差放大。因此应看一组步长:先出现二阶下降区,再出现平台或回升。如果整段都不接近伴随,先检查原始残差是否收敛,以及两边是否使用同一个固定目标。

Both objective values also contain rounding and solve error; subtracting and dividing by tiny τ\tau amplifies it. Inspect a range of steps: a second-order region, then a plateau or rise. If none approaches the adjoint result, first check primal convergence and that both solves use the same frozen target.

中心差分的完整求解流程

Complete central-difference workflow

freeze target_field
for tau in decreasing_step_sizes:
    u_plus  = solve_primal(D + tau*e[k])
    u_minus = solve_primal(D - tau*e[k])
    FD_c = (J(u_plus) - J(u_minus)) / (2*tau)
    compare FD_c with adjoint_gradient[k]

固定目标场,对当前设计做正负扰动,每次重新收敛完整原始残差。中心差分误差先按 O(τ2)O(\tau^2) 减小,随后受舍入误差与求解容差影响。不能仅根据最小步长的结果判断伴随是否正确。

Freeze the target field, perturb the current design in both directions, and reconverge the full primal residual each time. Central-difference error initially decreases as O(τ2)O(\tau^2), then is affected by roundoff and solve tolerance. The smallest step alone is not a reliable adjoint check.

FDτ+=J(D+τek)−J(D)τ,FDτc=J(D+τek)−J(D−τek)2τ.\mathrm{FD}_\tau^+=\frac{J(D+\tau e_k)-J(D)}{\tau},\qquad \mathrm{FD}_\tau^{\mathrm c}=\frac{J(D+\tau e_k)-J(D-\tau e_k)}{2\tau}.

前向差分截断误差通常为 O(τ)O(\tau),中心差分通常为 O(τ2)O(\tau^2);两者都会在小步长下放大目标值的舍入和求解误差。这里的步长 τ\tau 是一个数,即设计变量的差分步长;它不是网格宽度 hh,也不是求导记号。目标场始终固定,不随着扰动重新生成。

Forward differences usually have O(τ)O(\tau) truncation error, central differences O(τ2)O(\tau^2). Both amplify objective roundoff and solve errors at small steps. Here the step τ\tau is a number, the finite-difference step of the design; it is neither the mesh width hh nor a derivative symbol. The target remains frozen throughout the check.

下面可独立选择任一源区间或入口值,也可取全部变量的最大误差。扫描覆盖 10−110^{-1} 到 10−1210^{-12},可比较前向和中心差分,并输入其他步长查看各分量的目标值及收敛残差。切换空间精度、网格或边界后,所有检查针对新的完整离散系统重新计算;切换语言或差分显示复用当前模型的缓存。

Choose any source interval or the inlet value locally, or inspect the maximum error over all variables. The sweep covers 10−110^{-1} through 10−1210^{-12}; compare forward and central differences and enter another step to inspect objectives and convergence residuals. Changing spatial order, mesh or boundary rebuilds checks for the new full discrete system. Language and display changes reuse the current model cache.

读图时先选入口、中部源项和末端源项,比较误差最低点是否相同,再切换全部变量。中心差分不一定在每个步长都更准确;误差已经进入平台时继续减小步长,可能使误差反而增加。本面板缓存基准、正扰动、负扰动三组解,方便直接切换两种差分公式。

Compare the inlet, a middle source and the final source before selecting all variables: their minimum-error steps can differ. Central differences need not be more accurate at every step. Reducing an already tiny step can increase error. This panel caches baseline, positive and negative perturbed states so either formula is immediately available.

四种误差要分开:空间离散误差由网格加密检查;非线性求解误差由原始残差检查;线性求解误差由 tangent/adjoint 真残差检查;求导实现由转置、差分与复数检查。点积相等只证明两条传播互为转置;共同遗漏同一项也可能通过。近零梯度分量请同时查看绝对误差。

Separate four errors: spatial error through refinement; nonlinear solve error through primal residuals; linear solve error through true tangent/adjoint residuals; derivative implementation through transpose, finite differences and complex checks. A dot test alone only establishes paired transposes; jointly omitting a term may still pass. Inspect absolute errors for near-zero gradient components.

Taylor 实验使用固定设计方向(种子)D˙\dot D,对每个步长 τ\tau 重新收敛原始问题,并保持目标场不变。E0=∣J(D+τD˙)−J(D)∣E_0=|J(D+\tau\dot D)-J(D)|,E1=∣J(D+τD˙)−J(D)−τJ˙∣E_1=|J(D+\tau\dot D)-J(D)-\tau\dot J|,其中 J˙=DˉTD˙\dot J=\bar D^T\dot D 是伴随梯度给出的方向导数。光滑且 J˙≠0\dot J\ne0 时,预期 E0E_0 为一阶、E1E_1 为二阶;驻点或特殊方向可能有更高阶。步长过小后,求解误差与浮点消减会破坏这些斜率,不能把这一段当作空间精度。

The Taylor experiment reconverges the primal for each step τ\tau along a fixed design direction (seed) D˙\dot D with a fixed target. E0=∣J(D+τD˙)−J(D)∣E_0=|J(D+\tau\dot D)-J(D)| and E1=∣J(D+τD˙)−J(D)−τJ˙∣E_1=|J(D+\tau\dot D)-J(D)-\tau\dot J|, where J˙=DˉTD˙\dot J=\bar D^T\dot D is the directional derivative from the adjoint gradient. For a smooth problem and nonzero J˙\dot J, expect first- and second-order behaviour; stationary points or special directions can give higher orders. At tiny steps, solve errors and cancellation spoil the slopes. These slopes are not spatial accuracy.

第 14 节Section 14

复数步长:用虚部检验导数

Complex step: check derivatives with an imaginary perturbation

为什么一个很小的虚部能携带导数

How a tiny imaginary part carries a derivative

把普通 Taylor 展开中的实步长换成 iτi\tau。对于可解析延拓的函数,虚部首项恰好是 τJDk\tau J_{D_k},再除以 τ\tau 就得到导数。这个过程没有把两个接近的实数相减,因此能使用很小的步长。

Replace the real Taylor step by iτi\tau. For an analytic extension, the leading imaginary part is τJDk\tau J_{D_k}; divide by τ\tau to obtain the derivative. There is no subtraction of nearly equal real values, allowing a very small step.

J(D+iτek)=J(D)+iτJDk−τ22JDkDk−iτ36JDkDkDk+⋯ .J(D+i\tau e_k)=J(D)+i\tau J_{D_k}-\frac{\tau^2}{2}J_{D_kD_k}-i\frac{\tau^3}{6}J_{D_kD_kD_k}+\cdots.

复数目标必须延续原来的代数平方 (u−utgt)2(u-u^{\mathrm{tgt}})^2,不能改成带共轭的模平方 ∣u−utgt∣2|u-u^{\mathrm{tgt}}|^2;后者不是这里需要的解析延拓,会抹去虚部导数。绝对值、比较分支和限制器也需要单独分析。

The complex objective must retain the algebraic square (u−utgt)2(u-u^{\mathrm{tgt}})^2, not the conjugate modulus square ∣u−utgt∣2|u-u^{\mathrm{tgt}}|^2. The latter is not the required analytic extension and removes the imaginary derivative. Absolute values, comparisons and limiters require separate analysis.

重构、固定耗散通量和目标函数在本例中均为解析运算。将设计扰动为 D+iτekD+i\tau e_k(步长 τ\tau 是一个实数),保持目标场为实数常量,用独立复数残差收敛复数状态。虚部残差按 τ\tau 缩放后也必须收敛;只检查实部会错误地提前停止。

Reconstruction, the fixed-dissipation flux and the objective are analytic here. Perturb the design to D+iτekD+i\tau e_k (the step τ\tau is a real number), keep the target real and fixed, and converge a complex state using an independent complex residual. The imaginary residual divided by τ\tau must also converge; checking only the real part would stop too early.

dJdDk=Im⁡J(D+iτek)τ+O(τ2),τ=10−30.\frac{dJ}{dD_k}=\frac{\operatorname{Im}J(D+i\tau e_k)}{\tau}+O(\tau^2),\qquad\tau=10^{-30}.

复数步长验证使用冻结在收敛实解处的实数 GS 预处理,不经过 GMRES 的范数和 Arnoldi 分支。它验证收敛离散方程的导数;不声称 GMRES 与复数迭代逐步相同。本例没有 minmod、绝对值通量开关或限幅分支;加入它们后必须重新分析可微性。

The complex-step check uses a real GS preconditioner frozen at the converged real state, bypassing GMRES norms and Arnoldi branches. It validates derivatives of the converged discrete equations; it does not claim iteration-by-iteration equality with GMRES. There are no minmod, absolute-value flux switches or clipping branches here; adding them requires a new differentiability analysis.

图 18把第 13 节的差分步长扫描延伸到复数步长。取源项 σ0\sigma_0(第 13、14 节面板的默认分量),格式同页首默认(N=8N=8,一阶)。所有目标值都来自本页的重新求解:前向与中心差分用第 13 节同一个原始求解器,每个扰动都重新收敛;复数步长用本节的复数迭代,固定做 60 次,远超停止准则,让缩放残差停在舍入平台上。误差都相对于直接法求出的伴随梯度。

Figure 18 extends the step sweep of Section 13 to the complex step. It uses the source σ0\sigma_0 (the default component of the Section 13 and 14 panels) and the default discretisation (N=8N=8, first order). Every objective value comes from this page's re-solves: the forward and central differences use the primal solver of Section 13, reconverged for every perturbation; the complex step uses this section's complex iteration, run for a fixed 60 iterations, far past the stopping criterion, so that the scaled residual rests on its round-off floor. Errors are relative to the adjoint gradient from a direct solve.

图 18 · 导数 dJ/dσ₀ 的相对误差与步长 τ(格心,N = 8,一阶)。前向差分(圆)按斜率 1 下降,中心差分(方块)按斜率 2 下降,二者分别在 τ ≈ 10⁻⁸ 和 τ ≈ 10⁻⁵ 到达最低点后随 τ 减小而回升(V 形)。复数步长(叉)在大步长时与中心差分重合,τ ≲ 3.2×10⁻⁷ 后停在约 8×10⁻¹⁶ 的平台,一直保持到 τ = 10⁻³⁰。虚线:斜率 1、2 与 εmach。10⁻¹⁵10⁻¹²10⁻⁹10⁻⁶10⁻³10⁰10⁻³⁰10⁻²⁵10⁻²⁰10⁻¹⁵10⁻¹⁰10⁻⁵10⁰步长 τ(对数)前向差分中心差分复数步长参考斜率 1 和 2εmach = 2.2×10⁻¹⁶相对误差 |FDτ − dJ/dσ₀| / |dJ/dσ₀|12前向差分最低点 τ = 10⁻⁸中心差分最低点 τ = 10⁻⁵复数步长平台 ≈ 8×10⁻¹⁶,一直到 τ = 10⁻³⁰
图 18 · 导数 dJ/dσ₀ 的相对误差与步长 τ(格心,N = 8,一阶)。前向差分(圆)按斜率 1 下降,中心差分(方块)按斜率 2 下降,二者分别在 τ ≈ 10⁻⁸ 和 τ ≈ 10⁻⁵ 到达最低点后随 τ 减小而回升(V 形)。复数步长(叉)在大步长时与中心差分重合,τ ≲ 3.2×10⁻⁷ 后停在约 8×10⁻¹⁶ 的平台,一直保持到 τ = 10⁻³⁰。虚线:斜率 1、2 与 εmach。
Figure 18 · Relative error of dJ/dσ₀ against the step τ (cell page, N = 8, first order). Forward differences (circles) fall with slope 1 and central differences (squares) with slope 2; they bottom out near τ ≈ 10⁻⁸ and τ ≈ 10⁻⁵ and rise again as τ shrinks (the V). The complex step (crosses) coincides with the central difference at large τ and settles for τ ≲ 3.2×10⁻⁷ on a plateau near 8×10⁻¹⁶, which it keeps down to τ = 10⁻³⁰. Dashed: slopes 1 and 2, and εmach.10⁻¹⁵10⁻¹²10⁻⁹10⁻⁶10⁻³10⁰10⁻³⁰10⁻²⁵10⁻²⁰10⁻¹⁵10⁻¹⁰10⁻⁵10⁰step τ (log)forward differencecentral differencecomplex stepreference slopes 1 and 2εmach = 2.2×10⁻¹⁶relative error |FDτ − dJ/dσ₀| / |dJ/dσ₀|12forward minimum τ = 10⁻⁸central minimum τ = 10⁻⁵complex-step plateau ≈ 8×10⁻¹⁶, down to τ = 10⁻³⁰
Figure 18 · Relative error of dJ/dσ₀ against the step τ (cell page, N = 8, first order). Forward differences (circles) fall with slope 1 and central differences (squares) with slope 2; they bottom out near τ ≈ 10⁻⁸ and τ ≈ 10⁻⁵ and rise again as τ shrinks (the V). The complex step (crosses) coincides with the central difference at large τ and settles for τ ≲ 3.2×10⁻⁷ on a plateau near 8×10⁻¹⁶, which it keeps down to τ = 10⁻³⁰. Dashed: slopes 1 and 2, and εmach.

前向和中心差分的误差先按斜率 1 和 2 下降,分别在 τ≈10−8\tau\approx 10^{-8} 和 τ≈10−5\tau\approx 10^{-5} 附近到达 V 形底部,然后回升:步长太小时,两个几乎相等的 JJ 相减,原始求解的收敛容差和舍入误差被 1/τ1/\tau 放大。复数步长没有这一步相减。它在大步长时与中心差分重合,因为两者的首项截断误差都是 τ2JDkDkDk/6\tau^2J_{D_kD_kD_k}/6(符号相反);τ≲3.2×10−7\tau\lesssim 3.2\times10^{-7} 以后停在 8×10−168\times10^{-16} 左右的平台,与 εmach=2.2×10−16\varepsilon_{mach}=2.2\times10^{-16} 同一量级,一直保持到 τ=10−30\tau=10^{-30}。

The forward and central errors first fall with slopes 1 and 2, reach the bottom of the V near τ≈10−8\tau\approx 10^{-8} and τ≈10−5\tau\approx 10^{-5}, and then rise: at tiny steps two nearly equal values of JJ are subtracted, and the convergence tolerance of the primal solve and round-off are amplified by 1/τ1/\tau. The complex step makes no such subtraction. At large τ\tau it coincides with the central difference, because both have the leading truncation error τ2JDkDkDk/6\tau^2J_{D_kD_kD_k}/6 (with opposite signs); for τ≲3.2×10−7\tau\lesssim 3.2\times10^{-7} it settles on a plateau near 8×10−168\times10^{-16}, the order of εmach=2.2×10−16\varepsilon_{mach}=2.2\times10^{-16}, and stays there down to τ=10−30\tau=10^{-30}.

点积检验通过了,为什么还要检查残差

Why a dot test is not enough

故障对照分别检查转置、残差微分与总导数。二阶时把两个方向都替换为一阶 Jacobian,点积检验仍然成立,但复数残差导数检验会失败;这是“自洽但求错导数”的例子。

The fault comparison checks transposition, residual differentiation and total derivatives separately. At second order, replacing both operators by a first-order Jacobian still passes a dot test, but fails the complex-residual derivative check: the pair is internally consistent yet differentiates the wrong residual.

复数步长求解:实部和缩放虚部同时收敛

Complex-step solve: converge real and scaled imaginary residuals

u_real = solve_primal(D); u_imag = zeros(M)
tau = 1e-30; D_imag = tau*e[k]
B = fixed_GS_preconditioner_at(u_real)
repeat:
    (r_real, r_imag) = complex_residual(u_real, u_imag, D, D_imag)
    if max(norm(r_real), norm(r_imag)/tau) <= tolerance: break
    u_real -= B(r_real)
    u_imag -= B(r_imag)
CS = Σ W[i]*(u_real[i] - target[i])*u_imag[i]/tau
# Target stays real and fixed; the objective uses an algebraic square.
第 15 节Section 15

网格收敛:一阶与二阶

Mesh convergence: first versus second order

本节实验:线性重构精确、PDE 解达到二阶、目标或梯度达到二阶,是三种不同的结论。分别检查重构、加密解和方向梯度。

Experiment: Exact linear reconstruction, second-order PDE solutions and second-order functionals or gradients are distinct claims. Check reconstruction, refined solutions and directional gradients separately.

先收敛迭代,再比较离散误差

Converge the iteration before measuring discretisation error

同一网格上把残差降到很小,只说明代数方程被解准了;它不说明这个离散解离连续解有多近。空间精度需要在越来越细的网格上重复求解。若 Eh≈ChqE_h\approx Ch^q,把 hh 减半,误差应约缩小为原来的 2−q2^{-q}:一阶约一半,二阶约四分之一。

A small residual on one mesh means the algebraic equations are solved accurately. It does not measure distance to the continuous solution. Spatial accuracy requires repeated solves on finer meshes. If Eh≈ChqE_h\approx Ch^q, halving hh reduces error by about 2−q2^{-q}: one-half for first order, one-quarter for second order.

格心值是平均值,所以参考值 uref,iu_{ref,i} 也必须是控制体上的精确平均,而不是直接拿中心点精确值来比。两者对光滑函数本身相差 O(h2)O(h^2),在研究二阶时不能忽略。格点值则与对应点值比较。

A cell unknown is an average, so its reference uref,iu_{ref,i} must be the exact volume average, not simply the exact centre value. These differ by O(h2)O(h^2) for smooth functions and cannot be interchanged in a second-order study. Node values are compared with point values.

uref,i=1h∫aibi1+x dx=23h[(1+bi)3/2−(1+ai)3/2].u_{ref,i}=\frac1h\int_{a_i}^{b_i}\sqrt{1+x}\,dx=\frac{2}{3h}\left[(1+b_i)^{3/2}-(1+a_i)^{3/2}\right].

把区间逐次二等分,在 N=8,16,32,64,128,256N=8,16,32,64,128,256 上分别求解,就能检查一阶与二阶格式的实际精度。格心未知量与精确单元平均值比较,格点未知量与精确节点值比较。采用控制体加权 L2L^2 误差,并报告相邻网格观测阶。

Halve the grid spacing successively and solve on N=8,16,32,64,128,256N=8,16,32,64,128,256 to measure the accuracy of both discretisations. Cell unknowns are compared with exact cell averages; node unknowns with exact point values. We report control-volume-weighted L2L^2 errors and observed orders between successive grids.

Eh=[∑iWi(uh,i−uref,i)2]1/2,pobs=log⁡2(Eh/Eh/2).E_h=\left[\sum_iW_i(u_{h,i}-u_{ref,i})^2\right]^{1/2},\qquad p_{obs}=\log_2(E_h/E_{h/2}).

tangent 加密使用固定的物理设计方向:每个源区间的种子 σ˙k=1\dot\sigma_k=1(源项密度处处增加 1),入口种子 u˙in=0.2\dot u_{in}=0.2;对应的连续切向量是 v(x)=(0.2+x)/u(x)v(x)=(0.2+x)/u(x)(附录 A 用连续函数的变分写同一个方向)。不能在不同网格上只给一个宽度随网格变化的源区间加种子,却把它称为同一个连续方向。目标误差使用共同的连续目标场投影和连续积分 JJ;它与第 7 节用于反问题的离散目标定义有意区分。

Tangent refinement uses a fixed physical design direction: the seed σ˙k=1\dot\sigma_k=1 on every source interval (the source density raised by one everywhere) and the inlet seed u˙in=0.2\dot u_{in}=0.2. The corresponding continuous tangent is v(x)=(0.2+x)/u(x)v(x)=(0.2+x)/u(x) (Appendix A writes the same direction as a variation of continuous functions). Seeding a single source interval whose width changes with the mesh would not represent the same continuous direction. Objective error uses a shared projected continuous target and a continuous integral for JJ, deliberately distinct from the discrete inverse-design target in Section 7.

空间误差图的横轴是网格宽度;求解器收敛图的横轴是迭代或工作量。这两种“收敛”不能混用。伴随全域误差还受边界闭合影响,第 16 节单独讨论。

Spatial-error plots use mesh width on the horizontal axis, whereas solver convergence uses iteration or work. These are different notions of convergence. Boundary closure also affects global adjoint errors, treated separately in Section 16.

第 16 节
Section 16

伴随一致性:结论与阅读范围

Adjoint consistency: conclusions and scope

连续终端值问题在当前正值光滑解上适定。离散伴随的内部收敛与全域收敛可以不同:入口闭合造成的局部边界层,不能当作连续问题不适定的证据。格点入口约束乘子须与场值分开比较。

The continuous terminal-value problem is well posed on the current smooth positive branch. Interior and global discrete-adjoint convergence can differ: a local layer caused by inlet closure does not prove continuous ill-posedness. Compare the node inlet constraint multiplier separately from field samples.

点积测试检查离散转置,差分和复数步长检查同一离散目标的梯度。独立连续参考回答另一问题:加密后是否趋向相同的连续灵敏度。误差不下降时,先核对边界、目标、乘子含义、正则性和参考误差,再讨论适定性。

Dot tests check the discrete transpose; finite differences and complex steps check the gradient of the same discrete objective. An independent continuous reference asks a different question: whether refinement approaches the same continuous sensitivity. A discrepancy calls for checking boundaries, objectives, multiplier interpretation, regularity and reference error before diagnosing well-posedness.

附录 A集中给出完整推导、参考实现、图与交互;可先继续阅读离散伴随主线。

Appendix A contains the full derivation, reference implementation, figures and interaction. You can continue the discrete-adjoint lesson first.

图 19在整个区间上比较离散伴随与连续伴随(一阶,N=8, 32, 128N=8,\ 32,\ 128)。远离入口,−ψh-\psi_h 随加密逼近 λ(x)\lambda(x):x≥0.25x\ge0.25 处的最大误差依次为 6.7×10−36.7\times10^{-3}、2.8×10−32.8\times10^{-3}、7.8×10−47.8\times10^{-4},最细两套网格之间的观测阶为 0.920.92,接近一阶格式的 1。入口第一个控制体的误差却不趋于零,而是趋向 −λ(0)r=0.0080-\lambda(0)r=0.0080(依次为 −0.0008-0.0008、0.00480.0048、0.00710.0071)。这是已知的入口伴随边界层:入口处的离散伴随方程缺少上游的一项,相当于入口外的伴随值取零。它不影响光滑方向导数的收敛,却使全域 L2L^2 误差只有半阶。附录 A 推导了这个极限,见图 A.3与图 A.5。

Figure 19 compares the discrete and the continuous adjoint over the whole domain (first order, N=8, 32, 128N=8,\ 32,\ 128). Away from the inlet, −ψh-\psi_h approaches λ(x)\lambda(x) under refinement: the largest error over x≥0.25x\ge0.25 is 6.7×10−36.7\times10^{-3}, 2.8×10−32.8\times10^{-3}, 7.8×10−47.8\times10^{-4}, with observed order 0.920.92 between the two finer meshes, close to the 1 of a first-order scheme. The error in the first control volume at the inlet does not go to zero; it tends to −λ(0)r=0.0080-\lambda(0)r=0.0080 (here −0.0008-0.0008, 0.00480.0048, 0.00710.0071). This is the known inlet adjoint boundary layer: the discrete adjoint equation at the inlet lacks the upstream term, as if the adjoint outside the inlet were zero. It does not spoil the convergence of derivatives in smooth directions, but it limits the global L2L^2 error to half order. Appendix A derives the limit; see Figure A.3 and Figure A.5.

图 19 · 离散伴随与连续伴随在整个区间上的比较(格心,一阶,N = 8、32、128)。(a) 远离入口,−ψ 随加密贴近 λ(x)。(b) 误差 ej = −ψj − λ(xj):x ≥ 0.25 处的最大误差依次为 6.7×10⁻³、2.8×10⁻³、7.8×10⁻⁴,最细两套网格之间的观测阶为 0.92;圈出的入口第一个控制体误差依次为 −0.0008、0.0048、0.0071,趋向 −λ(0)r = 0.0080,不随加密消失。−0.04−0.020.0000.250.50.751x连续 λ(x)−ψ, N = 8−ψ, N = 32−ψ, N = 128−0.0050.0000.0050.01000.250.50.751x(a) 全区间:连续 λ 与离散 −ψ(一阶)(b) 误差 ej = −ψj − λ(xj)−λ(0)r = 0.0080:第一个控制体的极限N = 8N = 32N = 128
图 19 · 离散伴随与连续伴随在整个区间上的比较(格心,一阶,N = 8、32、128)。(a) 远离入口,−ψ 随加密贴近 λ(x)。(b) 误差 ej = −ψj − λ(xj):x ≥ 0.25 处的最大误差依次为 6.7×10⁻³、2.8×10⁻³、7.8×10⁻⁴,最细两套网格之间的观测阶为 0.92;圈出的入口第一个控制体误差依次为 −0.0008、0.0048、0.0071,趋向 −λ(0)r = 0.0080,不随加密消失。
Figure 19 · The discrete and the continuous adjoint over the whole domain (cell page, first order, N = 8, 32, 128). (a) Away from the inlet, −ψ approaches λ(x) under refinement. (b) The error ej = −ψj − λ(xj): its maximum over x ≥ 0.25 is 6.7×10⁻³, 2.8×10⁻³, 7.8×10⁻⁴, with observed order 0.92 between the two finer meshes; the circled first control volume at the inlet has −0.0008, 0.0048, 0.0071, which tends to −λ(0)r = 0.0080 and does not vanish under refinement. −0.04−0.020.0000.250.50.751xcontinuous λ(x)−ψ, N = 8−ψ, N = 32−ψ, N = 128−0.0050.0000.0050.01000.250.50.751x(a) whole domain: continuous λ, discrete −ψ (1st order)(b) error ej = −ψj − λ(xj)−λ(0)r = 0.0080: limit of the first volumeN = 8N = 32N = 128
Figure 19 · The discrete and the continuous adjoint over the whole domain (cell page, first order, N = 8, 32, 128). (a) Away from the inlet, −ψ approaches λ(x) under refinement. (b) The error ej = −ψj − λ(xj): its maximum over x ≥ 0.25 is 6.7×10⁻³, 2.8×10⁻³, 7.8×10⁻⁴, with observed order 0.92 between the two finer meshes; the circled first control volume at the inlet has −0.0008, 0.0048, 0.0071, which tends to −λ(0)r = 0.0080 and does not vanish under refinement.

用伴随估计目标误差

Estimate objective error with the adjoint

伴随还可把细网格残差转换为目标误差估计。将粗解线性插值到细网格(2N2N 个区间)得到 u~\tilde u,在线性化点求解 Ah(u~)Tψh=−gh(u~)A_h(\tilde u)^T\psi_h=-g_h(\tilde u),则 Jh(uh)−Jh(u~)≈ψhTRh(u~)J_h(u_h)-J_h(\tilde u)\approx\psi_h^TR_h(\tilde u)。这个估计针对同一细网格目标,不把不同网格的离散目标直接相减当作误差。

The adjoint also converts a fine-grid residual into an objective-error estimate. Interpolate the coarse solution linearly onto the fine grid (2N2N intervals) to obtain u~\tilde u, and solve Ah(u~)Tψh=−gh(u~)A_h(\tilde u)^T\psi_h=-g_h(\tilde u). Then Jh(uh)−Jh(u~)≈ψhTRh(u~)J_h(u_h)-J_h(\tilde u)\approx\psi_h^TR_h(\tilde u). This estimate concerns the same fine-grid objective; it does not directly subtract objectives with different discrete targets.

离散乘子与连续场为什么不能直接混用

Why a discrete multiplier is not automatically a continuous field

正文使用普通数组点积定义 ATA^T。若另选带权内积,算子的伴随就会改变:状态权矩阵为 WUW_U、残差权矩阵为 WRW_R 时,带权伴随是 WU−1ATWRW_U^{-1}A^TW_R。代码的普通转置不能在没有换元说明的情况下被写成该带权算子。

The main derivation defines ATA^T using ordinary array dot products. A weighted inner product changes the adjoint: with state and residual weights WUW_U and WRW_R it is WU−1ATWRW_U^{-1}A^TW_R. The coded ordinary transpose cannot be relabelled as that weighted operator without a change of variables.

⟨Av,w⟩WR=vTATWRw=⟨v,A†w⟩WU,\langle Av,w\rangle_{W_R}=v^TA^TW_Rw=\langle v,A^\dagger w\rangle_{W_U},
A†=WU−1ATWR.A^\dagger=W_U^{-1}A^TW_R.

残差的归一化也会改变乘子的数值。若改为 R𝒩=𝒩−1RR_{\mathcal N}={\mathcal N}^{-1}R,𝒩{\mathcal N} 为可逆的行尺度矩阵(例如各控制体的体积),则在收敛解上 A𝒩=𝒩−1AA_{\mathcal N}={\mathcal N}^{-1}A,ψ𝒩=𝒩Tψ\psi_{\mathcal N}={\mathcal N}^T\psi;目标总梯度保持不变。即使 𝒩{\mathcal N} 随设计改变,由 𝒩˙\dot {\mathcal N} 产生的项也乘以 RR,在精确收敛时为零。把控制体积分残差改成体积平均残差后,不能仍直接比较原来的乘子曲线。

Residual normalization also changes multiplier values. For R𝒩=𝒩−1RR_{\mathcal N}={\mathcal N}^{-1}R with an invertible row-scaling matrix 𝒩{\mathcal N} (for example the control-volume sizes), at convergence A𝒩=𝒩−1AA_{\mathcal N}={\mathcal N}^{-1}A and ψ𝒩=𝒩Tψ\psi_{\mathcal N}={\mathcal N}^T\psi, while the total objective gradient is unchanged. Even when 𝒩{\mathcal N} depends on the design, the terms from 𝒩˙\dot {\mathcal N} multiply RR and vanish at exact convergence. Multipliers of integrated and volume-normalized residuals cannot be compared without rescaling.

连续附录从微分方程、积分目标和分部积分出发;正文从实际离散残差及其有限维链式法则出发。两者可以比较同一设计变量的目标总导数与加密极限,但还须匹配符号、变量、积分权重和边界约束。

The continuous appendix starts from differential equations, integral objectives and integration by parts; the main text starts from actual discrete residuals and finite-dimensional chain rules. Compare total derivatives of the same objective and design variable and their refinement limits, after matching signs, variables, weights and boundary constraints.

第 17 节Section 17

GS 固定点与 GS 预处理 GMRES

GS fixed point and GS-preconditioned GMRES

从固定方向修正到组合多个方向

From repeated corrections to combining search directions

把前向或伴随系统统一写成 Kx=bKx=b。GS 固定点每次采用 B(b−Kx)B(b-Kx) 作为修正。若误差中有衰减很慢的分量,就会需要很多轮。GMRES 保存已经产生的方向,在它们张成的子空间中选择使残差二范数最小的组合;GS 则先把每个新方向变成较容易被 KK 处理的预处理方向。

Write either linear system as Kx=bKx=b. GS fixed point repeatedly corrects by B(b−Kx)B(b-Kx). Slowly decaying error components can require many iterations. GMRES retains directions and chooses a combination minimising the residual norm in their span. GS first maps each new direction to a preconditioned direction for the action of KK.

图 20 · 一步右预处理 GMRES 的数据路径。当前基向量vj​GS 预处理zj​ = B vj​精确矩阵乘积w = K zj​正交化与最小二乘组合 z₀,…,zj​
图 20 · 一步右预处理 GMRES 的数据路径。
Figure 20 · Data path of one right-preconditioned GMRES step.Basis vectorvj​GS preconditionerzj​ = B vj​Exact productw = K zj​Orthogonalise / LScombine z₀,…,zj​
Figure 20 · Data path of one right-preconditioned GMRES step.

Arnoldi 正交化逐步建立正交基 VV,把大矩阵在这组方向上的作用压缩成小的 Hessenberg 矩阵。对小矩阵解最小二乘问题,就能选出组合系数。右预处理时实际更新使用 Z=BVZ=BV;保留真实残差 b−Kxb-Kx,便能与固定点方法按同一标准比较。

Arnoldi builds an orthonormal basis VV and represents the large operator on those directions by a small Hessenberg matrix. A small least-squares solve supplies the combination coefficients. With right preconditioning, the state update uses Z=BVZ=BV. Monitoring the true residual b−Kxb-Kx allows comparison with fixed point on the same basis.

固定点方法反复沿 GS 给出的修正方向更新。GMRES 则在 Krylov 子空间中组合方向,求使残差二范数最小的近似解。GMRES 仍是迭代法,不是直接分解求解器,也不保证每个小问题都比 GS 更省工作。

Fixed point repeatedly updates in the correction direction supplied by GS. GMRES combines directions in a Krylov subspace to minimise the residual 2-norm. It is still an iterative method, not a direct factorisation solver, and need not use less work than GS on every small problem.

AqBsz=−f,v=Bsz;AqTBsTy=−g,ψ=BsTy.A_qB_sz=-f,\quad v=B_sz;\qquad A_q^TB_s^Ty=-g,\quad\psi=B_s^Ty.

让 GS 做预处理

Use GS as the preconditioner

采用右预处理,BsB_s 是固定次数、零初值的 GS 扫描;精确算子始终是 AqA_q 或 AqTA_q^T,一阶 Jacobian 仅用于 GS 预处理。伴随使用对应反向扫描 BsTB_s^T。下式的 Hm+1,mH_{m+1,m} 是 Arnoldi 过程得到的 (m+1)×m(m+1)\times m 上 Hessenberg 矩阵。

Right preconditioning uses BsB_s, a fixed number of zero-initialised GS sweeps. The exact operator remains AqA_q or AqTA_q^T; the first-order Jacobian is used only in the GS preconditioner. The adjoint uses the corresponding reverse sweeps BsTB_s^T. Below, Hm+1,mH_{m+1,m} is the (m+1)×m(m+1)\times m upper Hessenberg matrix built by the Arnoldi process.

A^Vm=Vm+1Hm+1,m,A^=AqBs,zm=Vmym,ym=arg⁡min⁡y∥βe1−Hm+1,m y∥2.\widehat A V_m=V_{m+1}H_{m+1,m},\quad\widehat A=A_qB_s,\quad z_m=V_my_m,\quad y_m=\arg\min_y\|\beta e_1-H_{m+1,m}\,y\|_2.

每一步先计算 zj=Bsvjz_j=B_sv_j,再计算 w=Aqzjw=A_qz_j,用两遍修正 Gram–Schmidt 正交化,Givens 旋转更新小型最小二乘问题。达到重启长度后,用当前真实残差重新建立子空间。页面记录的是 ∥b−Aqx∥2\|b-A_qx\|_2,不是预处理残差。

Each step applies zj=Bsvjz_j=B_sv_j, then w=Aqzjw=A_qz_j, orthogonalises with two-pass modified Gram–Schmidt, and updates the small least-squares problem with Givens rotations. At the restart length, rebuild the subspace from the current true residual. The page records ∥b−Aqx∥2\|b-A_qx\|_2, not the preconditioned residual.

每次真实残差检查也计入矩阵向量乘次数。比较同时列出 GS 扫描次数与 Krylov 存储量。本实现固定预处理;若在一次线性求解内改变 GS 扫描次数或用变化的内层容差,应使用适合可变预处理的 FGMRES。

True-residual checks are included in matrix-vector-product counts. Comparisons also report GS sweep counts and Krylov storage. This implementation has fixed preconditioning; changing GS sweep counts or using a variable inner stopping tolerance during one linear solve calls for a method such as FGMRES.

重启右预处理 GMRES:与本页计算对应

Restarted right-preconditioned GMRES used on this page

K = exact_forward_or_adjoint_product
B = fixed_zero_start_GS_or_reverse_GS
x = zeros(M)
while norm(b - K(x)) > tolerance:
    x_base = x; r = b - K(x); beta = norm(r)
    V[0] = r / beta; g_small = beta*e[0]
    for j = 0,...,min(M,restart_length)-1:
        Z[j] = B(V[j]); w = K(Z[j])
        orthogonalise w against V[0:j] twice; store H[:,j]
        h_next = norm(w)
        if h_next > breakdown_threshold: V[j+1] = w/h_next
        apply previous Givens rotations to H[:,j]
        rotate H[j:j+1,j] and g_small[j:j+1] with a new rotation
        y = back_substitute(H[0:j,0:j], g_small[0:j])
        x = x_base + Σ Z[i]*y[i]
        if norm(b - K(x)) <= tolerance: return x
        if h_next <= breakdown_threshold: break
    # Restart with the true residual; enforce the total iteration limit.

比较工作量时,不能只比较迭代编号:GMRES 还做正交化、内积并保存基向量。页面同时列出精确矩阵乘积次数、GS 扫描次数、存储量与真实残差;重启长度是内存和收敛速度之间的一个选择。

Iteration counts alone do not compare work: GMRES also orthogonalises, takes inner products and stores basis vectors. The page reports exact products, GS sweeps, storage and true residuals. Restart length trades memory against convergence.

第 18 节Section 18

总结:同一套网格、通量与重构,三种传递方向

Summary: one mesh, flux and reconstruction, three directions of data flow

把前面的计算并排放在一起,可以看到:原始、前向和伴随使用同一张网格、同一个通量和同一套重构系数。改变的是传递的数据,以及收集与分发的方向。

Putting the calculations side by side shows that the primal, forward and adjoint share the mesh, flux and reconstruction coefficients. What changes is the data being propagated and the direction of gather and scatter.

图 21把这三种计算并排画出:方框是存储的数组,箭头是数据流,颜色表示角色。

Figure 21 draws the three calculations side by side: boxes are stored arrays, arrows are data flow, colours mark the role.

图 21 · 同一个面循环的三种用法。方框是存储的数组,箭头是数据流,颜色表示角色。原始(蓝):由 u 重构面迹、算通量、按左正右负分发成残差。前向(绿):用同样的重构权重和在原始迹上算出的 cL、cR,按同样顺序传播切向量(上点量,从 v 开始)。伴随(红):从右往左传播伴随(横线量):先由残差种子 w 收集面通量的伴随 wL − wR,乘 cL、cR,再用转置的重构权重累加回模板上每个未知量的伴随。原始传递数值uM 个值qL, qR每个面两个迹Hf每个面一个通量R = BH − SM 行TL, TRH(a, b)B: +L, −R前向传递切向量,顺序相同v = u˙​M 个切向量值q˙​L, q˙​R每个面两个Ḣf每个面一个Ṙ = A vM 行TL, TRcL, cRB: +L, −R伴随顺序相反,系数转置ū = AT​wM 个累加值q¯​L, q¯​R每个面两个H¯​f = wL − wR每个面一个R¯​ = wM 个种子TLT​, TRT​ (+=)cL, cRBT​: wL − wR三行共用网格、重构权重 TL、TR,以及在原始迹上算出的 cL = a/2 + ε、cR = b/2 − ε;变的只是传递的数据和方向。
图 21 · 同一个面循环的三种用法。方框是存储的数组,箭头是数据流,颜色表示角色。原始(蓝):由 u 重构面迹、算通量、按左正右负分发成残差。前向(绿):用同样的重构权重和在原始迹上算出的 cL、cR,按同样顺序传播切向量(上点量,从 v 开始)。伴随(红):从右往左传播伴随(横线量):先由残差种子 w 收集面通量的伴随 wL − wR,乘 cL、cR,再用转置的重构权重累加回模板上每个未知量的伴随。
Figure 21 · The same face loop used three ways. Boxes are stored arrays, arrows are data flow, colours mark the role. Primal (blue): reconstruct face traces from u, evaluate the flux, scatter it with + to the left and − to the right. Forward (green): propagate tangents (the dotted quantities, starting from v) in the same order with the same reconstruction weights and the cL, cR evaluated at the primal traces. Adjoint (red): right to left, propagate adjoints (the barred quantities): gather the face-flux adjoint wL − wR from the residual seed w, multiply by cL, cR, then accumulate through the transposed reconstruction weights into the adjoint of every unknown of the stencil.PrimalvaluesuM valuesqL, qRtwo traces per faceHfone flux per faceR = BH − SM rowsTL, TRH(a, b)B: +L, −RForwardtangents,same orderv = u˙​M tangent valuesq˙​L, q˙​Rtwo per faceḢfone per faceṘ = A vM rowsTL, TRcL, cRB: +L, −RAdjointreverse order,transposedū = AT​wM sumsq¯​L, q¯​Rtwo per faceH¯​f = wL − wRone per faceR¯​ = wM seedsTLT​, TRT​ (+=)cL, cRBT​: wL − wRAll three share the mesh, the weights TL, TR and cL = a/2 + ε, cR = b/2 − ε evaluatedat the primal traces; only the data and the direction change.
Figure 21 · The same face loop used three ways. Boxes are stored arrays, arrows are data flow, colours mark the role. Primal (blue): reconstruct face traces from u, evaluate the flux, scatter it with + to the left and − to the right. Forward (green): propagate tangents (the dotted quantities, starting from v) in the same order with the same reconstruction weights and the cL, cR evaluated at the primal traces. Adjoint (red): right to left, propagate adjoints (the barred quantities): gather the face-flux adjoint wL − wR from the residual seed w, multiply by cL, cR, then accumulate through the transposed reconstruction weights into the adjoint of every unknown of the stencil.
计算Calculation原始Primal前向(tangent)Forward (tangent)伴随(adjoint)Adjoint
结果ResultRq(u)R_q(u)R˙=Aqv\dot R=A_qv𝐮ˉ=AqTw\bar{\mathbf u}=A_q^Tw
面上数据Data on a faceqL,qR→Hfq_L,q_R \to H_fTLv,TRv→H˙fT_Lv,T_Rv \to \dot H_fHˉf=wL−wR→cLHˉf,cRHˉf\bar H_f=w_L-w_R \to c_L\bar H_f,c_R\bar H_f
重构的作用Role of reconstruction由未知量重构面值Unknowns to reconstructed face states用相同系数组合切向量Combine tangents with the same weights按转置权重累加(+=)到模板各点Accumulate (+=) to stencil points with transposed weights
要求解的方程Equation to solveRq(u)=0R_q(u)=0Aqv=−fA_qv=-fAqTψ=−gA_q^T\psi=-g
GS 修正GS correctionPΔu≈−RqP\Delta u\approx-R_qPΔv≈−(Aqv+f)P\Delta v\approx-(A_qv+f)PTΔψ≈−(AqTψ+g)P^T\Delta\psi\approx-(A_q^T\psi+g)
  • 空间精度。一阶在控制体内取常值;二阶把斜率带到面上。前向与伴随都要经过这一步重构,边界模板也包括在内。
  • Spatial accuracy. First order uses constant states; second order carries slopes to the faces. Both derivatives include this reconstruction and its boundary stencils.
  • 求解。一阶 Jacobian 与 GS 扫描给出修正。固定点迭代反复使用这个修正;GMRES 把它作为预处理,在 Krylov 子空间中组合方向。两者都以完整方程的残差判断收敛。
  • Solving. The first-order Jacobian and GS sweeps supply corrections. Fixed point repeatedly applies them; GMRES uses them as a preconditioner and combines directions in a Krylov subspace. Both check the residual of the full equation.
  • 梯度。前向求解给出一个设计种子 D˙\dot D 对应的状态切向量 v=𝐮˙v=\dot{\mathbf u};一次伴随求解可以计算所有源项和入口值对目标函数的影响,即设计伴随 Dˉ\bar D。
  • Gradients. A forward solve gives the state tangent v=𝐮˙v=\dot{\mathbf u} for one design seed D˙\dot D. One adjoint solve gives objective sensitivities to every source and to the inlet value, the design adjoint Dˉ\bar D.
  • 验证。点积检查转置关系,有限差分与复数步长检查导数,网格加密检查空间精度。它们各自回答不同的问题。
  • Verification. Dot products check transposition, finite differences and complex step check derivatives, and mesh refinement checks spatial accuracy. Each addresses a different question.
  • 连续伴随。本例正解分支上的连续伴随适定;离散伴随能否逼近它,还取决于边界处理及乘子的含义。内部误差、全域误差与入口灵敏度需要分别比较。
  • Continuous adjoint. The continuous adjoint is well posed on the positive branch. Its discrete approximation also depends on boundary treatment and the interpretation of multipliers. Interior error, global error and inlet sensitivity are compared separately.

交互面板中的解、残差、前向与伴随、梯度都由页面实时计算。网格加密图使用同一模型预先求出的数据。一阶/二阶、边界和语言切换会更新相应说明与结果。

Solutions, residuals, forward and adjoint products, and gradients in the interactive panels are computed live. Refinement plots use results computed in advance with the same model. Spatial-order, boundary and language controls update the corresponding explanations and results.

附录 A:连续伴随在简单算例中的验证与对比

Appendix A: verifying and comparing continuous adjoints on simple cases

本项目以离散伴随为主线:从可逐句检查的小模型,逐步走向工业级非结构网格 RANS 的完整离散伴随实现。连续伴随在简单算例中提供独立验证与对比,不是学习后续章节的前提。

This project follows discrete adjoints, from small models whose operations can be inspected statement by statement toward a complete industrial unstructured-grid RANS discrete adjoint. Continuous adjoints provide independent checks and comparisons for simple cases; they are not a prerequisite for later chapters.

这里的“走向”是项目目标,并不表示当前已实现 RANS。可以为选定的连续 RANS 模型推导形式伴随;但不能据此假定它严格对应工业求解器中的湍流闭合、壁面处理、限幅、边界与网格算法。最终需要验证的是实际离散残差及目标的导数。

RANS is the project destination, not a capability already implemented here. A formal continuous adjoint can be derived for a specified continuous RANS model; this does not establish strict equivalence to an industrial solver’s turbulence closure, wall treatment, limiters, boundary and mesh algorithms. The derivatives to verify are those of the implemented discrete residual and objective.

图 A.1 · 两条独立计算路径,在统一的目标、边界与误差范数下比较
Figure A.1 · Two independent paths compared with matched objectives, boundaries and norms

连续 PDE → 线性化与分部积分 → 连续边界条件 → 独立参考场 λ

Continuous PDE → linearize and integrate by parts → dual boundary conditions → independent λ

离散残差 Rh → 精确 Jacobian → 转置求解 → 守恒行上的 −ψh

Discrete residual Rh → exact Jacobian → transpose solve → −ψh on conservation rows

A.1 完整推导与适定性

A.1 Full derivation and well-posedness

以下第 1、2 部分摘自全项目通用的连续伴随说明,其中提到的二维物体、对流扩散和拟一维 Euler 算例见对应页面;第 3 部分是本页的一维 Burgers 推导。

1. 符号、积分测度与三类不同的检查

将定常 PDE 写为 R(u,D)=0R(u,D)=0,目标泛函记为 JJ,采用拉格朗日泛函

ℒ=J−∫ΩλTR dΩ.\mathcal L=J-\int_\Omega\lambda^T R\,d\Omega.

页面求解的是 AhTψh=−ghA_h^T\psi_h=-g_h。这里的连续乘子采用相反符号:在守恒方程上有 λh=−ψh\lambda_h=-\psi_h。这些残差已经是控制体积分后的守恒平衡,不需要再除以控制体的体积或面积。被代数边界约束替换的行对应约束乘子,其数值不是连续场的点值。

三类检查分别回答不同的问题:

  1. 转置/点积测试检查 AhTA_h^T 的实现。
  2. 对同一离散问题做有限差分,检查 dJh/dD\mathrm dJ_h/\mathrm dD。
  3. 将独立求得的连续伴随与一系列加密网格上的离散伴随比较,检查指定范数下的一致性。比较时必须匹配 PDE、边界条件、目标泛函、计算区域与目标数据。

有限组网格上误差下降是数值证据,不是收敛定理。即使积分范数下的误差下降,边界层或特征跳跃仍可能阻止最大范数下的收敛。

2. 适定性与伴随一致性不是同一件事

出现差异提供了诊断机会,但差异本身不能证明问题不适定。应先明确状态空间与边界迹空间、允许的数据、目标泛函以及范数。Hadamard 意义下的适定性要求解存在、唯一且连续依赖于数据。正确推导的连续伴随可以是适定的,而离散边界闭合却不一致;一个边界数据与目标泛函不匹配的适定方程,仍然是错误的伴随。约束乘子、有限正则性和参考解误差也会造成表观差异。

线性化、零空间与稳定性

把原始边界条件的齐次线性化约束纳入算子 𝒜:X→Y𝒜:X\to Y 的定义域 XX。如果该算子是适当空间之间的有界同构,则其 Banach 伴随也是相应对偶空间之间的有界同构。这给出一种充分的局部分析框架,并不是写出形式微分表达式就能得到的结论。对于无界 PDE 算子,必须先明确其定义域及图范数,或者采用适当的弱形式。对于非线性原始问题,仅有存在唯一性还不能保证解映射可微;需要在所选解支上检查局部光滑性及线性化可逆性。

对于 𝒜∗λ=g𝒜^*\lambda=g,原始算子的零模态给出必要的兼容性条件:

𝒜v=0⟹g[v]=⟨λ,𝒜v⟩=0.𝒜v=0\quad\Longrightarrow\quad g[v]=\langle\lambda,𝒜v\rangle=0.

若 ker⁡ 𝒜∗\ker\,𝒜^* 含有非零元素,则伴随一旦有解就不唯一。仅有与原始零空间正交的条件,并不能构成一般的无穷维存在性定理:还需要考虑值域闭性和稳定性。对齐次伴随边界数据,所需估计具有以下形式:

∥λ∥Y∗≤C∥g∥X∗\|\lambda\|_{Y^*}\le C\|g\|_{X^*}

如果边界数据非齐次,还应加上相应的边界迹范数。每个网格尺度下逆算子有界也不够:在正确加权的离散范数下,稳定界必须与网格尺度无关。不考虑积分残差和控制体权重的原始欧氏条件数,本身不能作为连续问题的诊断结论。

一个可显式求解的对流例子

取 c>0c>0,固定原始入口值,目标变分为 ∫01g v dx+b v(1)\int_0^1 g\,v\,\mathrm dx+b\,v(1)。对原始线性化 c v′c\,v' 使用 Green 恒等式,得到

−cλ′=g,cλ(1)=b,λ(x)=bc+1c∫x1g(s) ds.-c\lambda'=g,\qquad c\lambda(1)=b,\qquad \lambda(x)=\frac{b}{c}+\frac1c\int_x^1g(s)\,ds.
  • 缺少出口数据: 解可以加上任意常数,因此不唯一。
  • 额外照搬原始入口条件 λ(0)=0\lambda(0)=0: 存在性要求 b+∫g=0b+\int g=0。当 g=1, b=0g=1,\ b=0 时,正确解是 (1−x)/c(1-x)/c;额外入口条件使问题无解。
  • 失去一致稳定性: 有 ∥λ∥∞≤(∣b∣+∥g∥1)/c\|\lambda\|_\infty\le(|b|+\|g\|_1)/c。取 g=1, b=0g=1,\ b=0 就达到 1/c1/c 的放大倍数。每个固定的 c>0c>0 都可以对应适定问题,但对于这类数据,不能得到在 c→0c\to 0 时仍统一有效的界。速度为零时,原始算子退化。

在原二维物体算例中,法向指向流体计算域的外部,所以迎风物面是流体域的原始出流边界。一条已经携带 u=1u=1 的特征线,不可能在这里同时满足零物理通量。首先失败的是所假设的光滑正值原始问题,而不只是某个已经建立的连续伴随的离散逼近。在假定的壁面状态 u=0u=0 处,

δ(12u2β⋅n)=u(β⋅n)v=0\delta(\tfrac12u^2\beta\cdot n)=u(\beta\cdot n)v=0

对任意变分 vv 都成立:通量约束丢失了一阶约束信息,特征速度也同时退化。把壁面条件照抄到形式伴随上,不能恢复有效的光滑解支。弱解/熵解表述需要另行分析边界迹与可微性。

对于特征边界对照,每条内部射线都会在有限路程内到达出流边界。边界数据几乎处处确定内部场;切线或角点引出的跳跃可以在允许非光滑解的空间中处理,本身不意味着不适定。特征边界附近的比值 bu/(a⋅n)b_u/(a\cdot n) 需要兼容性分析:在这里的物面目标中,分子与分母具有可相消的法向因子,不能只看分母就断言伴随发散。

其余算例说明了什么

对流扩散。 正扩散系数提供椭圆主部,但这不是任意低阶项与 Robin 边界系数下的普遍可逆性证明。仍需检查齐次零空间,以及所用能量估计或最大值原理的假设。独立计算支持当前参数组,没有证明对所有参数都成立的定理。把目标所要求的壁面伴随值 1 改成 0,可能得到另一个适定问题,但会给出错误的目标灵敏度。在远场同时规定状态值和法向导数,一般会过约束。把被替换方程行的乘子当成壁面场值,则是另一类代数含义错误。

拟一维 Euler。 对所述正向亚声速分支,特征速度 u−c, u, u+cu-c,\ u,\ u+c 解释了原始入口两个条件、出口一个条件,以及伴随的相反条件数。条件数目正确是必要条件,不是充分条件:特征传播与边界约束共同组成的边值算子还必须可逆。声速点处通量 Jacobian BB 奇异,当前使用 (BT)−1(B^T)^{-1} 的表示失效,需要喉部兼容性/正则性分析。这不意味着所有跨声速伴随都不存在。激波还需要考虑激波位置变分及相应内部条件。这些现象超出当前数值证据的范围,可参考 Giles 与 Pierce 的连续喷管伴随分析。强、弱边界处理对数值伴随的不同影响,见 Duivesteijn 等人的研究。

因此,教学顺序应当是:先明确连续原始问题及所选解支;推导伴随,包括其边界定义域;分析存在性、唯一性和稳定性;随后才检查对该问题的一致、稳定逼近。反例应明确保留其失败的前提,不能悄悄替换模型后宣布验证成功。

3. 一维 Burgers:二阶重构、连续伴随与边界一致性

光滑正解分支与连续 tangent

考虑固定区间 [0,1] 上的定常方程,入口值为正。令源项的原函数从入口积分,选取正平方根:

(u22)′=s,u(0)=uin>0,u(x)=uin2+2∫0xs(t) dt.\left(\frac{u^2}{2}\right)'=s,\qquad u(0)=u_{in}>0, \qquad u(x)=\sqrt{u_{in}^2+2\int_0^x s(t)\,dt}.

只要根号内严格正且远离零,光滑解唯一,源项和入口扰动对解的映射稳定。令 v 为状态扰动,线性化得到:

(uv)′=δs,v(0)=δuin,v(x)=uinδuin+∫0xδs(t) dtu(x).(uv)'=\delta s,\quad v(0)=\delta u_{in},\qquad v(x)=\frac{u_{in}\delta u_{in}+\int_0^x\delta s(t)\,dt}{u(x)}.

教学基准采用 s=0.5、入口值 1,故 u(x)=sqrt(1+x)。目标场对应 s=sin(πx),在求导期间固定。网格加密的 tangent 参考使用同一个物理扰动 δs=1、δuin\delta u_{\mathrm{in}}=0.2,而不是随网格缩小的单个源项区间。

从变分到终端伴随方程

目标函数是固定目标场的平方误差积分。令连续伴随 λ 满足以下终端值问题:

J=12∫01(u−utgt)2 dx,δJ=∫01(u−utgt)v dx,−uλ′=u−utgt,λ(1)=0.J=\frac12\int_0^1(u-u^{\mathrm{tgt}})^2\,dx,\qquad \delta J=\int_0^1(u-u^{\mathrm{tgt}})v\,dx,\qquad -u\lambda'=u-u^{\mathrm{tgt}},\quad\lambda(1)=0.

这里是 -uλ′,不是 -(uλ)′。原因是 tangent 微分算子作用于乘积 uv;其形式转置由下面的积分分部直接确定:

∫01λ(uv)′ dx=[λuv]01−∫01uλ′v dx=−uinλ(0)δuin+δJ.\int_0^1\lambda(uv)'\,dx =[\lambda uv]_0^1-\int_0^1u\lambda'v\,dx =-u_{in}\lambda(0)\delta u_{in}+\delta J.

代入 tangent 方程即可消去 v,得到连续总导数及可直接计算的伴随积分:

λ(x)=∫x1u(t)−utgt(t)u(t) dt,δJ=∫01λ δs dx+uinλ(0)δuin.\lambda(x)=\int_x^1\frac{u(t)-u^{\mathrm{tgt}}(t)}{u(t)}\,dt,\qquad \delta J=\int_0^1\lambda\,\delta s\,dx+u_{in}\lambda(0)\delta u_{in}.

于是区间常源项的连续灵敏度是该区间内 λ 的积分,入口灵敏度是 uinu_{\mathrm{in}} λ(0)。页面使用复合五点 Gauss 积分计算参考值,独立于离散伴随线性求解。

适定性与离散不一致是不同问题

若 u≥c>0,则终端条件唯一确定 λ,且有稳定性估计:

∥λ∥L∞(0,1)≤∥u−utgtu∥L∞(0,1)≤c−1∥u−utgt∥L∞(0,1).\|\lambda\|_{L^\infty(0,1)} \le\left\|\frac{u-u^{\mathrm{tgt}}}{u}\right\|_{L^\infty(0,1)} \le c^{-1}\|u-u^{\mathrm{tgt}}\|_{L^\infty(0,1)}.

因此当前基准的连续伴随适定。离散伴随出现入口边界层,不应被解释为这个连续终端值问题不适定。若 u 接近或穿过零,以上稳定性界不再统一成立;若有激波,还需要弱解、激波位置变化及相应的伴随传递条件,不能套用这里的光滑推导。

二阶重构不会自动带来全域二阶伴随

格心未知量是单元平均值,格点未知量是节点值。二阶采用内部中心斜率、端点单边斜率,重构到真实面位置后计算通量。格心出口必须先重构到 x=1;格点第一内部对偶面位于 h/2,迎风通量也必须使用该位置的重构迹。

用 TLT_L、TRT_R 表示重构,用 B 表示有向面分发,忽略此行公式中单列的边界常数及约束替换,则精确二阶导数为:

R2=BH(TLu,TRu)−S,A2v=B{cL⊙TLv+cR⊙TRv},A2Tw=TLT{cL⊙BTw}+TRT{cR⊙BTw}.R_2=B H(T_Lu,T_Ru)-S,\qquad A_2v=B\{c_L\odot T_Lv+c_R\odot T_Rv\},\qquad A_2^Tw=T_L^T\{c_L\odot B^Tw\}+T_R^T\{c_R\odot B^Tw\}.

页面使用离散拉格朗日函数 J+ψTR,因此 A₂Tψ=-JuJ_u,平衡方程对应的连续场应比较 λ 与 -ψ。格点第 0 行是强约束,乘子 ψ₀ 不能直接当作控制体内 λ 的取值;改为比较入口灵敏度 -ψ₀ 与 uinu_{\mathrm{in}} λ(0)。

Q=I−e0e0T,A2=QAraw+e0e0T,A2Tw=ArawTQw+e0w0.Q=I-e_0e_0^T,\qquad A_2=Q A_{raw}+e_0e_0^T, \qquad A_2^Tw=A_{raw}^TQw+e_0w_0.

入口约束会移除该行的源项贡献;重构的转置又会把其他残差行的权重传回边界附近未知量。因此必须同时检查内部场、全域场、入口导数和首源项导数。当前计算显示:默认耗散入口下,二阶内部误差接近二阶,但全域误差受有限幅值局部边界层影响,接近半阶。格心迎风入口改善全域收敛;格点二阶迎风对照仍存在边界层。

首源项区间随网格缩小,其导数本身会趋于零;绝对差趋零不足以证明局部一致性。因此页面额外报告离散导数与连续区间积分的比值。对固定光滑物理扰动,则另外报告总方向导数误差。

GS 与 GMRES 只改变求解过程

令 P=A₁(u*) 是在收敛二阶状态处计算的一阶近似 Jacobian。每次从零开始执行固定次数 GS 扫描,形成线性近似逆 BsB_s。tangent 与 adjoint 的固定点迭代使用精确残差,GMRES 也使用精确二阶算子:

vm+1=vm−Bs(A2vm+f),ψm+1=ψm−BsT(A2Tψm+g),A2Bsz=−f,v=Bsz,A2TBsTy=−g,ψ=BsTy.v^{m+1}=v^m-B_s(A_2v^m+f),\qquad \psi^{m+1}=\psi^m-B_s^T(A_2^T\psi^m+g),\qquad A_2B_sz=-f,\quad v=B_sz,\qquad A_2^TB_s^Ty=-g,\quad\psi=B_s^Ty.

一次前向 GS 对应 B₁=(diag(P)+L)⁻¹;伴随采用对应的反向扫描 B₁T=(diag(P)+LT)⁻¹。这里的反向 GS 解的是 PT 系统。多次扫描也固定次数、零初值,以保持标准 GMRES 的预处理线性且固定。换成 GMRES 或把线性残差收敛得更小,都不能消除由离散边界闭合造成的伴随不一致。

交互、完整矩阵与实际网格收敛数据见格心页面和格点页面。

Parts 1 and 2 below come from the project-wide notes on continuous adjoints; the 2-D body, advection–diffusion and quasi-1-D Euler cases they mention have their own pages. Part 3 is the one-dimensional Burgers derivation for this page.

1. Signs, measures and three distinct checks

Write the steady PDE as R(u,D)=0R(u,D)=0, the functional as JJ, and use

ℒ=J−∫ΩλTR dΩ.\mathcal L=J-\int_\Omega\lambda^T R\,d\Omega.

The pages solve AhTψh=−ghA_h^T\psi_h=-g_h. Our continuous multiplier has the opposite sign: λh=−ψh\lambda_h=-\psi_h on conservation equations. Those residuals are integrated balances, so there is no additional division by control-volume measure. Rows replaced by algebraic boundary constraints carry constraint multipliers; their values are not point samples of the continuous field.

Three tests answer different questions:

  1. A transpose/dot test checks the implementation of AhTA_h^T.
  2. Finite differences of the same discrete problem check dJh/dD\mathrm dJ_h/\mathrm dD.
  3. Comparing an independently solved continuous dual with a sequence of refined discrete duals tests consistency in a stated norm. It requires matching PDE, boundary conditions, functional, domain, and target data.

A decreasing error on finitely many meshes is numerical evidence, not a theorem of convergence. Boundary layers or characteristic jumps can prevent maximum-norm convergence even when an integral norm decreases.

2. Well-posedness is not the same as adjoint consistency

A discrepancy is a diagnostic opportunity, not by itself proof of ill-posedness. Specify the state and trace spaces, admissible data, functional, and norms first. Hadamard well-posedness requires existence, uniqueness and continuous dependence. A correctly derived continuous dual can be well posed while a discrete boundary closure is inconsistent; a well-posed equation with the wrong objective boundary data is simply the wrong dual. Constraint multipliers, limited regularity and finite reference error are other sources of apparent disagreement.

Linearization, kernels and stability

Include the homogeneous linearized primal boundary constraints in the domain XX of the operator 𝒜:X→Y𝒜:X\to Y. If this is a bounded isomorphism between suitable spaces, its Banach adjoint is also a bounded isomorphism between the dual spaces. This is a sufficient local framework, not a conclusion obtained merely by writing a formal differential expression. An unbounded PDE operator must first be given a domain/graph norm or a suitable weak realization. For a nonlinear primal, existence and uniqueness alone do not guarantee a differentiable solution map; the local smoothness and invertibility hypotheses must be checked on the chosen branch.

For 𝒜∗λ=g𝒜^*\lambda=g, a primal null mode implies the necessary compatibility condition

𝒜v=0⟹g[v]=⟨λ,𝒜v⟩=0.𝒜v=0\quad\Longrightarrow\quad g[v]=\langle\lambda,𝒜v\rangle=0.

A nonzero element of ker⁡ 𝒜∗\ker\,𝒜^* makes a solvable dual nonunique. Orthogonality to the primal kernel alone is not a general infinite-dimensional existence theorem: closed range and stability still matter. The required estimate has the form

∥λ∥Y∗≤C∥g∥X∗\|\lambda\|_{Y^*}\le C\|g\|_{X^*}

for homogeneous dual boundary data; inhomogeneous boundary data contribute their appropriate trace norm. A bounded inverse at every mesh size is not enough either: the stability bound must be uniform in the properly weighted discrete norms. Raw Euclidean condition numbers without accounting for integrated residuals and control-volume weights are not themselves a continuum diagnosis.

An explicit transport example

For c>0c>0, fixed primal inflow and objective variation ∫01g v dx+b v(1)\int_0^1 g\,v\,\mathrm dx+b\,v(1), the Green identity for the primal linearization c v′c\,v' gives

−cλ′=g,cλ(1)=b,λ(x)=bc+1c∫x1g(s) ds.-c\lambda'=g,\qquad c\lambda(1)=b,\qquad \lambda(x)=\frac{b}{c}+\frac1c\int_x^1g(s)\,ds.
  • Missing outlet data: an arbitrary additive constant remains, so uniqueness fails.
  • An extra copied primal inlet datum λ(0)=0\lambda(0)=0: existence requires b+∫g=0b+\int g=0. With g=1, b=0g=1,\ b=0, the correct solution is (1−x)/c(1-x)/c; the extra inlet condition makes the problem unsolvable.
  • Loss of uniform stability: ∥λ∥∞≤(∣b∣+∥g∥1)/c\|\lambda\|_\infty\le(|b|+\|g\|_1)/c. Taking g=1, b=0g=1,\ b=0 attains the 1/c1/c amplification. Each fixed c>0c>0 is well posed, but there is no bound uniform as c→0c\to 0 for this data class. At zero speed the original operator degenerates.

In the original 2-D body example, normals point out of the fluid domain: the upwind body is therefore primal outflow. A characteristic already carrying u=1u=1 cannot satisfy zero physical flux there. This is first a failure of the assumed smooth positive primal problem, not merely a poor discrete approximation to an otherwise established dual. At a putative wall state u=0u=0,

δ(12u2β⋅n)=u(β⋅n)v=0\delta(\tfrac12u^2\beta\cdot n)=u(\beta\cdot n)v=0

for every variation vv: the flux constraint loses its first-order information while the characteristic speed degenerates. Copying the wall condition onto a formal dual cannot restore a valid smooth solution branch. Weak/entropy formulations would need separate trace and differentiability analysis.

For the characteristic-boundary contrast, every interior ray reaches outflow in finite distance. Boundary data determine the field almost everywhere; tangency/corner jumps are compatible with a nonsmooth solution space and do not by themselves imply ill-posedness. The ratio bu/(a⋅n)b_u/(a\cdot n) requires compatibility at characteristic boundaries: in this particular body objective its numerator and denominator contain a cancelling normal factor. One must not infer blow-up from the denominator alone.

What the other cases demonstrate

Advection–diffusion. Positive diffusion provides an elliptic principal part, not a universal invertibility proof for arbitrary lower-order and Robin coefficients. Homogeneous kernels and the assumptions of applicable energy or maximum-principle estimates still need attention. The independent solves support this parameter set; they do not prove a theorem for all parameters. Setting the wall dual to zero instead of the objective-required one can define a different well-posed problem, but yields the wrong functional sensitivity. Imposing both state and normal derivative at the far field is generally an overconstraint. Treating a replaced-row multiplier as a wall field sample is yet another, algebraic interpretation error.

Quasi-1-D Euler. The speeds u−c, u, u+cu-c,\ u,\ u+c explain the two inlet/one outlet primal conditions and reversed dual counts for the stated positive subsonic branch. Counts are necessary, not sufficient: propagation and the boundary constraints together must yield an invertible boundary-value operator. At a sonic point the flux Jacobian BB is singular, so the current representation using (BT)−1(B^T)^{-1} fails; throat compatibility/regularity analysis is required. This does not imply that all transonic adjoints fail to exist. Shocks require shock-position variations and internal conditions. These phenomena are outside the current numerical evidence; see the continuous nozzle analysis of Giles and Pierce. The distinct effects of strong/weak boundary treatment on numerical duals are discussed by Duivesteijn et al..

The teaching sequence is therefore: identify the continuous primal and chosen branch; derive the dual including its boundary domain; examine existence, uniqueness and stability; only then test consistent, stable approximation of that problem. A counterexample should retain its failed premise visibly, rather than silently substituting another model and declaring success.

3. One-dimensional Burgers: second-order reconstruction, continuous adjoints and boundary consistency

Smooth positive branch and the continuous tangent

Consider the steady equation on the fixed interval [0,1], with positive inlet data. Integrating the source from the inlet and choosing the positive square root gives:

(u22)′=s,u(0)=uin>0,u(x)=uin2+2∫0xs(t) dt.\left(\frac{u^2}{2}\right)'=s,\qquad u(0)=u_{in}>0, \qquad u(x)=\sqrt{u_{in}^2+2\int_0^x s(t)\,dt}.

As long as the radicand stays strictly positive and bounded away from zero, the smooth solution is unique and depends stably on source and inlet perturbations. Writing v for the state perturbation gives:

(uv)′=δs,v(0)=δuin,v(x)=uinδuin+∫0xδs(t) dtu(x).(uv)'=\delta s,\quad v(0)=\delta u_{in},\qquad v(x)=\frac{u_{in}\delta u_{in}+\int_0^x\delta s(t)\,dt}{u(x)}.

The teaching baseline uses s=0.5 and inlet value 1, hence u(x)=sqrt(1+x). The target corresponds to s=sin(πx) and remains fixed during differentiation. Tangent mesh refinement uses the same physical perturbation δs=1, δuin\delta u_{\mathrm{in}}=0.2, rather than a single source interval that shrinks with the mesh.

From variations to the terminal adjoint equation

The objective integrates the squared error against a frozen target. Define the continuous adjoint λ by the following terminal-value problem:

J=12∫01(u−utgt)2 dx,δJ=∫01(u−utgt)v dx,−uλ′=u−utgt,λ(1)=0.J=\frac12\int_0^1(u-u^{\mathrm{tgt}})^2\,dx,\qquad \delta J=\int_0^1(u-u^{\mathrm{tgt}})v\,dx,\qquad -u\lambda'=u-u^{\mathrm{tgt}},\quad\lambda(1)=0.

The operator is -uλ′, not -(uλ)′. The tangent differential operator acts on the product uv, and integration by parts directly determines its formal transpose:

∫01λ(uv)′ dx=[λuv]01−∫01uλ′v dx=−uinλ(0)δuin+δJ.\int_0^1\lambda(uv)'\,dx =[\lambda uv]_0^1-\int_0^1u\lambda'v\,dx =-u_{in}\lambda(0)\delta u_{in}+\delta J.

Substitution of the tangent equation eliminates v and gives both the continuous total derivative and an explicitly computable adjoint integral:

λ(x)=∫x1u(t)−utgt(t)u(t) dt,δJ=∫01λ δs dx+uinλ(0)δuin.\lambda(x)=\int_x^1\frac{u(t)-u^{\mathrm{tgt}}(t)}{u(t)}\,dt,\qquad \delta J=\int_0^1\lambda\,\delta s\,dx+u_{in}\lambda(0)\delta u_{in}.

Thus the continuous sensitivity to a constant source on an interval is the integral of λ over that interval; the inlet sensitivity is uinu_{\mathrm{in}} λ(0). The pages evaluate these references with composite five-point Gaussian quadrature, independently of the discrete adjoint linear solve.

Well-posedness and discrete inconsistency are different questions

If u≥c>0, the terminal condition uniquely determines λ, with the stability estimate:

∥λ∥L∞(0,1)≤∥u−utgtu∥L∞(0,1)≤c−1∥u−utgt∥L∞(0,1).\|\lambda\|_{L^\infty(0,1)} \le\left\|\frac{u-u^{\mathrm{tgt}}}{u}\right\|_{L^\infty(0,1)} \le c^{-1}\|u-u^{\mathrm{tgt}}\|_{L^\infty(0,1)}.

The continuous adjoint of the present baseline is therefore well posed. An inlet boundary layer in its discrete counterpart must not be interpreted as ill-posedness of this terminal-value problem. If u approaches or crosses zero, the bound is no longer uniform. Shocks require weak solutions, shock-location variations and corresponding adjoint transmission conditions; this smooth derivation cannot simply be reused.

Second-order reconstruction does not automatically give a globally second-order adjoint

Cell unknowns are cell averages; node unknowns are point values. Second order uses centred interior slopes and one-sided endpoint slopes, reconstructing to the actual face locations before evaluating fluxes. A cell outlet must reconstruct to x=1. The first internal node-based dual face is at h/2, and even its upwind flux must use a trace reconstructed to that location.

Let TLT_L and TRT_R denote reconstruction and B denote oriented face scatter. With boundary constants and constraint replacement treated separately, the exact second-order derivatives are:

R2=BH(TLu,TRu)−S,A2v=B{cL⊙TLv+cR⊙TRv},A2Tw=TLT{cL⊙BTw}+TRT{cR⊙BTw}.R_2=B H(T_Lu,T_Ru)-S,\qquad A_2v=B\{c_L\odot T_Lv+c_R\odot T_Rv\},\qquad A_2^Tw=T_L^T\{c_L\odot B^Tw\}+T_R^T\{c_R\odot B^Tw\}.

The discrete Lagrangian is J+ψTR, giving A₂Tψ=-JuJ_u. Balance rows therefore compare the continuous field λ with -ψ. Node row 0 is a strong constraint; ψ₀ is not a control-volume field value. Instead, compare the inlet sensitivities -ψ₀ and uinu_{\mathrm{in}} λ(0).

Q=I−e0e0T,A2=QAraw+e0e0T,A2Tw=ArawTQw+e0w0.Q=I-e_0e_0^T,\qquad A_2=Q A_{raw}+e_0e_0^T, \qquad A_2^Tw=A_{raw}^TQw+e_0w_0.

The inlet constraint removes its row’s source contribution, while transposed reconstruction propagates weights from other residual rows to near-boundary unknowns. Interior fields, global fields, inlet derivatives and first-source derivatives must therefore be checked separately. Current calculations show nearly second-order interior adjoint errors for second-order reconstruction with the default dissipative inlet, while finite-amplitude local boundary layers reduce global convergence toward order one-half. An upwind cell inlet improves global convergence; the second-order node-based upwind comparison retains a boundary layer.

The first source interval shrinks with the mesh, so its derivative itself tends to zero. A vanishing absolute difference is insufficient evidence of local consistency. The pages additionally report the discrete derivative divided by the continuous interval integral. Total directional-derivative errors are reported separately for a fixed smooth physical perturbation.

GS and GMRES change the solver only

Let P=A₁(u*) be the first-order approximate Jacobian evaluated at the converged second-order state. A fixed number of zero-initialised GS sweeps defines a linear approximate inverse BsB_s. Tangent and adjoint fixed-point iterations use the exact residual; GMRES also retains the exact second-order operator:

vm+1=vm−Bs(A2vm+f),ψm+1=ψm−BsT(A2Tψm+g),A2Bsz=−f,v=Bsz,A2TBsTy=−g,ψ=BsTy.v^{m+1}=v^m-B_s(A_2v^m+f),\qquad \psi^{m+1}=\psi^m-B_s^T(A_2^T\psi^m+g),\qquad A_2B_sz=-f,\quad v=B_sz,\qquad A_2^TB_s^Ty=-g,\quad\psi=B_s^Ty.

One forward GS sweep gives B₁=(diag(P)+L)⁻¹; the adjoint uses the corresponding reverse sweep B₁T=(diag(P)+LT)⁻¹ on the PT system. Multiple sweeps also use a fixed count and zero initial values so that standard GMRES sees fixed linear preconditioning. Switching to GMRES, or converging linear residuals more tightly, cannot remove adjoint inconsistency caused by discrete boundary closure.

Interactive examples, complete matrices and measured mesh-convergence data are available on the cell-centred page and the node-centred page.

A.2 从方程到可运行参考实现

A.2 From equations to a runnable reference

给定正值 u 和目标 utgt → 分部积分确定 λ(1)=0 → 从 x 到 1 求积 → 比较 −ψ 与源项/入口梯度

Specify positive u and target utgt → derive λ(1)=0 → integrate from x to 1 → compare −ψ and source/inlet gradients

下方代码可以完整复制到本地文件运行。它独立计算连续参考,不调用本页有限体积 Jacobian 的转置。

Copy the complete code below into a local file and run it. It computes the continuous reference independently of the page’s finite-volume Jacobian transpose.

node reference_burgers.js
展开完整参考代码Show complete reference code
'use strict';
const exact=(which,x,uin=1)=>Math.sqrt(uin*uin+(which==='target'?2*(1-Math.cos(Math.PI*x))/Math.PI:x));
// Composite five-point Gaussian quadrature, also used for exact cell averages.
const gn=[0,.5384693101056831,-.5384693101056831,.906179845938664,-.906179845938664], gw=[.5688888888888889,.4786286704993665,.4786286704993665,.2369268850561891,.2369268850561891];
function integrate(f,a,b,panels=8){let s=0;for(let k=0;k<panels;k++){const h=(b-a)/panels,c=a+(k+.5)*h;for(let j=0;j<5;j++)s+=h/2*gw[j]*f(c+h/2*gn[j]);}return s;}
const lambda=x=>integrate(t=>(exact('base',t)-exact('target',t))/exact('base',t),x,1);

console.log('x,lambda');
for(let i=0;i<=100;i++)console.log(i/100+','+lambda(i/100));

连续伴随、一致性与适定性

Continuous adjoint, consistency and well-posedness

从分部积分推导连续伴随

Derive the continuous adjoint by integration by parts

离散伴随先离散再转置;连续伴随先对微分方程求变分,再分部积分。两条路径不自动等价,尤其在边界附近。这里可以直接手算:对 F(u)=u2/2F(u)=u^2/2 求变分,得到 (uv)′=δs(uv)'=\delta s。乘以待定函数 λ\lambda 后积分,把导数从 uvuv 转到 λ\lambda。

A discrete adjoint discretises first and transposes afterwards. A continuous adjoint varies the differential equation and then integrates by parts. These routes need not agree automatically, especially at boundaries. Here the calculation is explicit: varying F(u)=u2/2F(u)=u^2/2 gives (uv)′=δs(uv)'=\delta s. Multiply by an unknown function λ\lambda and integrate to transfer the derivative from uvuv to λ\lambda.

∫01λ δs dx=∫01λ(uv)′ dx=[λuv]01−∫01uλ′v dx.\int_0^1\lambda\,\delta s\,dx=\int_0^1\lambda(uv)'\,dx=[\lambda uv]_0^1-\int_0^1u\lambda'v\,dx.

为了让最后的体积分等于 δJ=∫(u−utgt)v dx\delta J=\int(u-u^{\mathrm{tgt}})v\,dx,取 −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}}。原始出口的 v(1)v(1) 没有限制,所以其边界系数必须为零,即 λ(1)=0\lambda(1)=0。入口扰动是已知设计变化,剩下的边界项给出入口灵敏度。这解释了为什么伴随的已知值在原始出口。

Choose −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}} so the last volume integral equals δJ=∫(u−utgt)v dx\delta J=\int(u-u^{\mathrm{tgt}})v\,dx. The primal outlet perturbation v(1)v(1) is unconstrained, so its boundary coefficient must vanish: λ(1)=0\lambda(1)=0. The inlet perturbation is known design data, and its remaining boundary term yields inlet sensitivity. This explains why adjoint data belong at the primal outlet.

“一致性”和“适定性”分别检查什么?适定性问连续问题是否有解、解是否唯一、是否连续依赖数据。一致性比较离散方程及其解能否逼近指定连续问题。局部边界层可以来自离散边界处理;即使每个有限矩阵可逆,也不能仅据此断言连续极限稳定。

Well-posedness asks whether the continuous problem has a unique solution depending continuously on its data. Consistency compares a discretisation with the specified continuous problem. A local boundary layer can come from discrete boundary treatment; invertibility of every finite matrix alone does not establish stability of the continuous limit.

连续 tangent 为 (uv)′=δs(uv)'= \delta s,v(0)=δuinv(0)=\delta u_{in}。从 δJ=∫01(u−utgt)v dx\delta J=\int_0^1(u-u^{\mathrm{tgt}})v\,dx 出发,选择连续伴随满足下式。积分分部只产生 uλvu\lambda v 边界项,不能误写为 −(uλ)′=u−utgt-(u\lambda)'= u-u^{\mathrm{tgt}}。

The continuous tangent satisfies (uv)′=δs(uv)'=\delta s, v(0)=δuinv(0)=\delta u_{in}. Starting from δJ=∫01(u−utgt)v dx\delta J=\int_0^1(u-u^{\mathrm{tgt}})v\,dx, choose the following continuous adjoint. Integration by parts produces the boundary term uλvu\lambda v; the adjoint must not be incorrectly written as −(uλ)′=u−utgt-(u\lambda)'=u-u^{\mathrm{tgt}}.

−uλ′=u−utgt,λ(1)=0,λ(x)=∫x1u(t)−utgt(t)u(t) dt.-u\lambda'=u-u^{\mathrm{tgt}},\qquad\lambda(1)=0,\qquad\lambda(x)=\int_x^1\frac{u(t)-u^{\mathrm{tgt}}(t)}{u(t)}\,dt.
δJ=∫01λ δs dx+uinλ(0)δuin,λ ↔ −ψ.\delta J=\int_0^1\lambda\,\delta s\,dx+u_{in}\lambda(0)\delta u_{in},\qquad \lambda\ \leftrightarrow\ -\psi.

图解:原始信息向右,伴随信息向左

The picture: primal information moves right, adjoint information moves left

原始扰动沿特征速度 u>0u>0 从入口向右传播,所以只能在入口给定 v(0)=δuinv(0)=\delta u_{in};出口值 v(1)v(1) 是求解结果。伴随方程 −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}} 在同一特征上反向积分:它从出口出发,所以唯一需要的数据是出口值。边界项 [λuv]01[\lambda uv]_0^1 把两者联系起来:出口项含任意的 v(1)v(1),其系数必须为零;入口项含已知的设计扰动,于是成为入口灵敏度。

The primal perturbation travels with the characteristic speed u>0u>0 from the inlet to the right, so it can only be prescribed at the inlet, v(0)=δuinv(0)=\delta u_{in}; the outlet value v(1)v(1) is part of the solution. The adjoint equation −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}} integrates along the same characteristics in reverse, starting at the outlet, so its only datum is an outlet value. The boundary term [λuv]01[\lambda uv]_0^1 links the two: the outlet term contains the arbitrary v(1)v(1) and its coefficient must vanish; the inlet term contains the known design change and becomes the inlet sensitivity.

图 A.2 · 原始扰动从入口向右传播,入口值给定;伴随从出口向左传播,出口值 λ(1)=0 给定。分部积分的边界项 [λuv] 决定了这种分工。原始 tangent:(uv)′ = δs,特征速度 u > 0伴随:−uλ′ = u − utgt,沿同一特征反向v(0) = δuin​ 给定v(1) 由解决定λ(1) = 0 给定λ(0) 由解决定x = 0x = 1− λ(0) uin​ δuin​已知设计扰动 ⇒ 入口灵敏度λ(1) u(1) v(1)v(1) 任意 ⇒ λ(1) = 0[λuv]₀¹ = λ(1) u(1) v(1) − λ(0) uin​ δuin​
图 A.2 · 原始扰动从入口向右传播,入口值给定;伴随从出口向左传播,出口值 λ(1)=0 给定。分部积分的边界项 [λuv] 决定了这种分工。
Figure A.2 · The primal perturbation travels to the right from the given inlet value; the adjoint travels to the left from the given outlet value λ(1)=0. The boundary term [λuv] of integration by parts fixes this division.Primal tangent: (uv)′ = δs, characteristic speed u > 0Adjoint: −uλ′ = u − utgt, along the same characteristics in reversev(0) = δuin​ givenv(1) from the solutionλ(1) = 0 givenλ(0) from the solutionx = 0x = 1− λ(0) uin​ δuin​known change ⇒ inlet sensitivityλ(1) u(1) v(1)v(1) arbitrary ⇒ λ(1) = 0[λuv]₀¹ = λ(1) u(1) v(1) − λ(0) uin​ δuin​
Figure A.2 · The primal perturbation travels to the right from the given inlet value; the adjoint travels to the left from the given outlet value λ(1)=0. The boundary term [λuv] of integration by parts fixes this division.

本例的解、目标与连续伴随

The states and the continuous adjoint of this example

基准源项 s=0.5s=0.5、入口值 1 给出 u=1+xu=\sqrt{1+x};目标由 s=sin⁡πxs=\sin\pi x 生成,utgt=1+2(1−cos⁡πx)/πu^{\mathrm{tgt}}=\sqrt{1+2(1-\cos\pi x)/\pi}。积分 λ(x)=∫x1(u−utgt)/u dt\lambda(x)=\int_x^1(u-u^{\mathrm{tgt}})/u\,dt 在出口为零,在入口为 λ(0)=−0.0348\lambda(0)=-0.0348。右图把本页 N=16 的离散伴随画成 −ψ-\psi(页面的拉格朗日函数是 J+ψTRJ+\psi^TR,平衡行上 λ↔−ψ\lambda\leftrightarrow-\psi):内部两种阶数都贴近 λ\lambda,入口附近的几个点离开曲线,这就是下文分析的边界层。

The base source s=0.5s=0.5 with inlet value 1 gives u=1+xu=\sqrt{1+x}; the target comes from s=sin⁡πxs=\sin\pi x, utgt=1+2(1−cos⁡πx)/πu^{\mathrm{tgt}}=\sqrt{1+2(1-\cos\pi x)/\pi}. The integral λ(x)=∫x1(u−utgt)/u dt\lambda(x)=\int_x^1(u-u^{\mathrm{tgt}})/u\,dt vanishes at the outlet and equals λ(0)=−0.0348\lambda(0)=-0.0348 at the inlet. The right panel draws this page's N=16 discrete adjoint as −ψ-\psi (the page's Lagrangian is J+ψTRJ+\psi^TR, so λ↔−ψ\lambda\leftrightarrow-\psi on balance rows): both orders follow λ\lambda in the interior, and a few points near the inlet leave the curve. That departure is the boundary layer analysed below.

图 A.3 · 左:基准解 u(x)=√(1+x) 与目标 utgt(x)。右:连续伴随 λ(x) 与本页格心离散伴随 −ψ(N=16)。入口附近的偏离就是下文的伴随边界层;格心值画在单元中心。1.01.11.21.31.41.500.250.50.751x基准 u目标 utgt−0.05−0.04−0.03−0.02−0.010.0000.250.50.751x连续 λ一阶 −ψ二阶 −ψ(a) 原始状态(b) 伴随
图 A.3 · 左:基准解 u(x)=√(1+x) 与目标 utgt(x)。右:连续伴随 λ(x) 与本页格心离散伴随 −ψ(N=16)。入口附近的偏离就是下文的伴随边界层;格心值画在单元中心。
Figure A.3 · Left: base solution u(x)=√(1+x) and target utgt(x). Right: continuous adjoint λ(x) against this page's cell discrete adjoint −ψ (N=16). The departure near the inlet is the adjoint boundary layer analysed below. Cell values are drawn at cell centres.1.01.11.21.31.41.500.250.50.751xbase utarget utgt−0.05−0.04−0.03−0.02−0.010.0000.250.50.751xcontinuous λfirst order −ψsecond order −ψ(a) Primal states(b) Adjoint
Figure A.3 · Left: base solution u(x)=√(1+x) and target utgt(x). Right: continuous adjoint λ(x) against this page's cell discrete adjoint −ψ (N=16). The departure near the inlet is the adjoint boundary layer analysed below. Cell values are drawn at cell centres.

逐步推导:tangent 解、伴随积分与对偶恒等式

Step by step: the tangent solution, the adjoint integral and the duality identity

第一步,解 tangent。把 (uv)′=δs(uv)'=\delta s 从 0 积分到 xx,入口值 u(0)v(0)=uinδuinu(0)v(0)=u_{in}\delta u_{in} 直接进入:

Step 1, the tangent. Integrate (uv)′=δs(uv)'=\delta s from 0 to xx; the inlet value u(0)v(0)=uinδuinu(0)v(0)=u_{in}\delta u_{in} enters directly:

u(x)v(x)−uinδuin=∫0xδs dt⟹v(x)=uinδuin+∫0xδs dtu(x).\begin{aligned}u(x)v(x)-u_{in}\delta u_{in}&=\int_0^x\delta s\,dt\\\Longrightarrow\quad v(x)&=\frac{u_{in}\delta u_{in}+\int_0^x\delta s\,dt}{u(x)}.\end{aligned}

第二步,解伴随。记 w=(u−utgt)/uw=(u-u^{\mathrm{tgt}})/u,则 λ′=−w\lambda'=-w;从出口 λ(1)=0\lambda(1)=0 反向积分:

Step 2, the adjoint. With w=(u−utgt)/uw=(u-u^{\mathrm{tgt}})/u the equation reads λ′=−w\lambda'=-w; integrate backwards from the outlet value λ(1)=0\lambda(1)=0:

λ(x)=λ(1)+∫x1w(t) dt=∫x1w(t) dt,λ(0)=∫01w(t) dt.\begin{gathered}\lambda(x)=\lambda(1)+\int_x^1w(t)\,dt=\int_x^1w(t)\,dt,\\\lambda(0)=\int_0^1w(t)\,dt.\end{gathered}

第三步,不用分部积分也能验证对偶。把 tangent 代入 δJ=∫(u−utgt)v dx=∫w (uv) dx\delta J=\int(u-u^{\mathrm{tgt}})v\,dx=\int w\,(uv)\,dx,再交换二重积分的次序(积分区域是三角形 0≤x≤t≤10\le x\le t\le1):

Step 3 verifies the duality without integration by parts. Insert the tangent into δJ=∫(u−utgt)v dx=∫w (uv) dx\delta J=\int(u-u^{\mathrm{tgt}})v\,dx=\int w\,(uv)\,dx and exchange the order of the double integral over the triangle 0≤x≤t≤10\le x\le t\le1:

δJ=∫01w(t)[uinδuin+∫0tδs(x) dx]dt=uinδuin∫01w dt⏟λ(0)+∫01δs(x)∫x1w(t) dt⏟λ(x) dx.\begin{aligned}\delta J&=\int_0^1w(t)\Bigl[u_{in}\delta u_{in}+\int_0^t\delta s(x)\,dx\Bigr]dt\\&=u_{in}\delta u_{in}\underbrace{\int_0^1w\,dt}_{\lambda(0)}+\int_0^1\delta s(x)\underbrace{\int_x^1w(t)\,dt}_{\lambda(x)}\,dx.\end{aligned}

这正是 δJ=∫01λ δs dx+uinλ(0)δuin\delta J=\int_0^1\lambda\,\delta s\,dx+u_{in}\lambda(0)\delta u_{in}:分部积分与交换积分次序是同一个恒等式的两种写法。

This is exactly δJ=∫01λ δs dx+uinλ(0)δuin\delta J=\int_0^1\lambda\,\delta s\,dx+u_{in}\lambda(0)\delta u_{in}: integration by parts and the exchange of integration order are two forms of one identity.

第四步,代入本页的方向 δs=1\delta s=1、δuin=0.2\delta u_{in}=0.2。交换次序后 ∫01λ dx=∫01t w(t) dt=−0.030435\int_0^1\lambda\,dx=\int_0^1t\,w(t)\,dt=-0.030435,所以伴随给出 ∫λ dx+0.2λ(0)=−0.037393\int\lambda\,dx+0.2\lambda(0)=-0.037393;直接用 v(x)=(0.2+x)/1+xv(x)=(0.2+x)/\sqrt{1+x} 积分得到同一个数 −0.037393-0.037393。

Step 4 uses this page's direction δs=1\delta s=1, δuin=0.2\delta u_{in}=0.2. After the exchange, ∫01λ dx=∫01t w(t) dt=−0.030435\int_0^1\lambda\,dx=\int_0^1t\,w(t)\,dt=-0.030435, so the adjoint gives ∫λ dx+0.2λ(0)=−0.037393\int\lambda\,dx+0.2\lambda(0)=-0.037393; integrating directly with v(x)=(0.2+x)/1+xv(x)=(0.2+x)/\sqrt{1+x} gives the same number, −0.037393-0.037393.

离散问题有同样的恒等式,而且是代数恒等式:离散设计种子 D˙\dot D 取同一方向(σ˙k=1\dot\sigma_k=1、u˙in=0.2\dot u_{in}=0.2),ATψ=−gA^T\psi=-g、Av=−RDD˙Av=-R_D\dot D 给出 ψTRDD˙=−ψTAv=gTv\psi^TR_D\dot D=-\psi^TAv=g^Tv。下表对本页格式求出这个方向导数 ψTRDD˙\psi^TR_D\dot D,并与连续值比较;同时列出离散 tangent 的最大误差和两种算法之差。

The discrete problem has the same identity, now an algebraic one: with the discrete design seed D˙\dot D in the same direction (σ˙k=1\dot\sigma_k=1, u˙in=0.2\dot u_{in}=0.2), ATψ=−gA^T\psi=-g and Av=−RDD˙Av=-R_D\dot D give ψTRDD˙=−ψTAv=gTv\psi^TR_D\dot D=-\psi^TAv=g^Tv. The table evaluates the directional derivative ψTRDD˙\psi^TR_D\dot D with this page's scheme and compares it with the continuous value; it also lists the maximum error of the discrete tangent and the difference between the two algorithms.

阶数OrderNψTRDḊ与连续值之差Error实测阶Ratetangent 最大误差Tangent max error实测阶Rate|gTv − ψTRDḊ|
一阶first order32−0.04019355−2.80e−3—1.32e−2—6.94e−18
128−0.03815629−7.63e−40.9383.46e−30.9686.94e−18
512−0.03758781−1.95e−40.9858.75e−40.9922.08e−17
2048−0.03744194−4.90e−50.9962.20e−40.9989.02e−17
二阶second order32−0.03764968−2.57e−4—1.87e−4—2.08e−17
128−0.03740957−1.66e−51.9761.31e−51.9186.94e−18
512−0.03739402−1.04e−61.9968.46e−71.9751.94e−16
2048−0.03739305−6.53e−81.9995.34e−81.9923.47e−17

两种阶数的方向导数都按格式的阶收敛,而离散 tangent 在全域(包括入口)同样按格式的阶收敛。下一小节会看到,离散伴随在入口有一个不随加密消失的边界层;由上面的代数恒等式,同一个方向导数也等于 gTvg^Tv,而 vv 没有边界层,所以这个边界层不会降低光滑方向导数的收敛阶(它在二阶格式中的净贡献怎样抵消,见下一小节)。

For both orders the directional derivative converges at the order of the scheme, and so does the discrete tangent over the whole domain, inlet included. The next subsections show that the discrete adjoint carries an inlet boundary layer that does not vanish under refinement; by the algebraic identity above the same directional derivative also equals gTvg^Tv, and vv has no layer, so that layer cannot lower the convergence order of derivatives in smooth directions (how its net contribution cancels at second order is shown in the next subsection).

连续伴随是否适定

Is the continuous adjoint well posed?

当前 u≥1u\ge1,终端值问题唯一且稳定,∥λ∥∞≤∥(u−utgt)/u∥∞\|\lambda\|_\infty\le\|(u-u^{\mathrm{tgt}})/u\|_\infty。如果 uu 穿过零、出现激波或特征方向改变,需要重新分析正则性和边界条件。当前数值边界层本身不能证明连续伴随不适定。

Here u≥1u\ge1, so the terminal-value problem is unique and stable, with ∥λ∥∞≤∥(u−utgt)/u∥∞\|\lambda\|_\infty\le\|(u-u^{\mathrm{tgt}})/u\|_\infty. If uu crosses zero, shocks form or characteristic directions change, regularity and boundary conditions must be reconsidered. The numerical boundary layer here does not itself demonstrate an ill-posed continuous adjoint.

格点第 0 行是约束,其乘子不是控制体内的伴随场值,全域场误差因此排除该行;入口导数单独比较 −ψ0-\psi_0 与 uinλ(0)u_{in}\lambda(0)。内部误差固定取 0.15<x<0.850.15<x<0.85,全域误差则保留其他边界附近的控制体。

Node row 0 is a constraint, whose multiplier is not an adjoint field value in a control volume, so it is excluded from field-error norms. Its inlet derivative −ψ0-\psi_0 is compared separately with uinλ(0)u_{in}\lambda(0). The interior norm uses the fixed region 0.15<x<0.850.15<x<0.85; the global norm retains the other near-boundary volumes.

边界附近为什么可能不一致

Why discrepancies can remain near the boundary

默认耗散入口下,二阶格式的内部伴随可接近二阶,但全域 L2L^2 误差会因局部有限幅值边界层接近半阶。格心迎风入口明显改善一致性;格点二阶的重构及强行替换边界仍可保留边界层,不能沿用一阶迎风的结论。首源项导数与连续区间积分的比值还会检出局部不一致,即使光滑方向的总导数已经收敛。

With the default dissipative inlet, second-order interior adjoints can approach order two while the global L2L^2 error approaches order one-half because of a finite-amplitude local boundary layer. An upwind cell inlet improves consistency. Reconstruction and strong boundary-row replacement can retain a boundary layer in the second-order node scheme, so conclusions from first-order upwinding do not carry over automatically. The first source derivative divided by its continuous interval integral reveals local inconsistency even when derivatives in smooth directions converge.

入口伴随边界层:全域误差为什么只有半阶

The inlet adjoint boundary layer: why the global error is of order one half

先看一阶格式的内部行。ATψ=−gA^T\psi=-g 的第 jj 行来自 AA 的第 jj 列:左面 j−12j-\tfrac12 贡献 cR=uj/2−εc_R=u_j/2-\varepsilon,右面 j+12j+\tfrac12 贡献 cL=uj/2+εc_L=u_j/2+\varepsilon,对角元是二者之差 2ε2\varepsilon:

Start with an interior row of the first-order scheme. Row jj of ATψ=−gA^T\psi=-g is column jj of AA: the left face j−12j-\tfrac12 contributes cR=uj/2−εc_R=u_j/2-\varepsilon, the right face j+12j+\tfrac12 contributes cL=uj/2+εc_L=u_j/2+\varepsilon, and the diagonal is their difference 2ε2\varepsilon:

(uj2−ε)ψj−1+2εψj−(uj2+ε)ψj+1=−gj,gj=Wj(uj−ujtgt).\left(\tfrac{u_j}2-\varepsilon\right)\psi_{j-1}+2\varepsilon\psi_j-\left(\tfrac{u_j}2+\varepsilon\right)\psi_{j+1}=-g_j,\qquad g_j=W_j(u_j-u^{\mathrm{tgt}}_j).
图 A.4 · Aᵀψ 的第 j 行就是 A 的第 j 列:它列出 uj 影响的所有残差行。(a) 一阶时 uj 只进入自己两侧的两个面,影响 3 个残差行,转置行含 3 个 ψ;(b) 二阶重构让 uj 进入 4 个面,影响 5 个残差行。下行是入口处的同一个行。(a) 一阶uj−2​Rj−2​uj−1​Rj−1​uj​Rj​uj+1​Rj+1​uj+2​Rj+2​uj​ 进入 2 个面 → 3 个残差行转置行:ψj−1​, ψj​, ψj+1​(b) 二阶uj−2​Rj−2​uj−1​Rj−1​uj​Rj​uj+1​Rj+1​uj+2​Rj+2​uj​ 进入 4 个面 → 5 个残差行转置行:ψj−2​ 到 ψj+2​入口处的同一行(一阶)ψ₋₁ = 0ψ₀ψ₁入口面(红线)左侧没有控制体,没有 ψ₋₁,所以第 0 行是一般行取 ψ₋₁ = 0。
图 A.4 · ATψ 的第 j 行就是 A 的第 j 列:它列出 uj 影响的所有残差行。(a) 一阶时 uj 只进入自己两侧的两个面,影响 3 个残差行,转置行含 3 个 ψ;(b) 二阶重构让 uj 进入 4 个面,影响 5 个残差行。下行是入口处的同一个行。
Figure A.4 · Row j of Aᵀψ is column j of A: it lists every residual row that uj affects. (a) At first order uj enters only its own two faces and affects 3 residual rows, so the transposed row couples 3 values of ψ; (b) second-order reconstruction lets uj enter 4 faces and affect 5 rows. The bottom strip shows the same row at the inlet.(a) First orderuj−2​Rj−2​uj−1​Rj−1​uj​Rj​uj+1​Rj+1​uj+2​Rj+2​uj​ enters 2 faces → 3 residual rowstransposed row: ψj−1​, ψj​, ψj+1​(b) Second orderuj−2​Rj−2​uj−1​Rj−1​uj​Rj​uj+1​Rj+1​uj+2​Rj+2​uj​ enters 4 faces → 5 residual rowstransposed row: ψj−2​ to ψj+2​The same row at the inlet (first order)ψ₋₁ = 0ψ₀ψ₁There is no control volume left of the inlet face (red), hence no ψ₋₁,so row 0 is the general row with ψ₋₁ = 0.
Figure A.4 · Row j of ATψ is column j of A: it lists every residual row that uj affects. (a) At first order uj enters only its own two faces and affects 3 residual rows, so the transposed row couples 3 values of ψ; (b) second-order reconstruction lets uj enter 4 faces and affect 5 rows. The bottom strip shows the same row at the inlet.

代入 ψj±1=ψ±hψ′+O(h2)\psi_{j\pm1}=\psi\pm h\psi'+O(h^2),零阶项相消,剩下 −huψ′=−h(u−utgt)-hu\psi'=-h(u-u^{\mathrm{tgt}}):ψ=−λ\psi=-\lambda 正好满足连续伴随 −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}}。但这是三项递推,除了光滑解还有第二个解。冻结 uu、令 ψj=ρj\psi_j=\rho^j,得到

Substituting ψj±1=ψ±hψ′+O(h2)\psi_{j\pm1}=\psi\pm h\psi'+O(h^2) cancels the zeroth-order terms and leaves −huψ′=−h(u−utgt)-hu\psi'=-h(u-u^{\mathrm{tgt}}), so ψ=−λ\psi=-\lambda satisfies the continuous adjoint −uλ′=u−utgt-u\lambda'=u-u^{\mathrm{tgt}}. But a three-term recurrence has a second solution besides the smooth one. Freezing uu and setting ψj=ρj\psi_j=\rho^j gives

(u2+ε)ρ2−2ερ−(u2−ε)=0,ρ1=1,ρ2=r=ε−u/2ε+u/2.\left(\tfrac u2+\varepsilon\right)\rho^2-2\varepsilon\rho-\left(\tfrac u2-\varepsilon\right)=0,\qquad \rho_1=1,\qquad \rho_2=r=\frac{\varepsilon-u/2}{\varepsilon+u/2}.

本页 ε=0.8\varepsilon=0.8、入口 u(0)=uin=1u(0)=u_{in}=1,所以 r=3/13≈0.231r=3/13\approx0.231。由于 0<r<10<r<1,第二个解离开入口后每格缩小为原来的 0.231,只占几格宽。格心入口行正好是一般行取 ψ−1=0\psi_{-1}=0 的形式:入口面的 cRc_R 进入对角元,左侧没有未知量:

On this page ε=0.8\varepsilon=0.8 and the inlet state is u(0)=uin=1u(0)=u_{in}=1, so r=3/13≈0.231r=3/13\approx0.231. Since 0<r<10<r<1, the second solution shrinks by the factor 0.231 per cell away from the inlet and occupies only a few cells. The cell inlet row is exactly the general row with ψ−1=0\psi_{-1}=0: the inlet face puts its cRc_R into the diagonal, and there is no unknown on the left:

ψ−1=0:2εψ0−(u02+ε)ψ1=−g0.\psi_{-1}=0:\qquad 2\varepsilon\psi_0-\left(\tfrac{u_0}2+\varepsilon\right)\psi_1=-g_0.

连续伴随在入口不为零(本例 λ(0)=−0.0348\lambda(0)=-0.0348),光滑解满足不了这个条件,只能加上第二个解。于是在入口附近

The continuous adjoint does not vanish at the inlet (here λ(0)=−0.0348\lambda(0)=-0.0348), so the smooth solution cannot meet this condition and the second solution must be added. Near the inlet

ψj=−λ(xj)+λ(0) r j+1+O(h),\psi_j=-\lambda(x_j)+\lambda(0)\,r^{\,j+1}+O(h),
∥−ψ−λ∥Lh2≈(h∑j≥0λ(0)2r2(j+1))1/2=∣λ(0)∣ r1−r2 h1/2.\|{-\psi}-\lambda\|_{L^2_h}\approx\Bigl(h\sum_{j\ge0}\lambda(0)^2r^{2(j+1)}\Bigr)^{1/2}=\frac{|\lambda(0)|\,r}{\sqrt{1-r^2}}\,h^{1/2}.

这就是全域误差的半阶:边界层的幅值 ∣λ(0)∣r|\lambda(0)|r 不随加密减小,只有宽度随 hh 收窄;常数 ∣λ(0)∣r/1−r2=0.00825|\lambda(0)|r/\sqrt{1-r^2}=0.00825。离开入口几格后 rjr^{j} 已可忽略,所以内部误差仍按格式的阶收敛。

This is the half order of the global error: the layer amplitude ∣λ(0)∣r|\lambda(0)|r does not shrink under refinement; only its width shrinks with hh. The constant is ∣λ(0)∣r/1−r2=0.00825|\lambda(0)|r/\sqrt{1-r^2}=0.00825. A few cells away from the inlet rjr^{j} is negligible, so the interior error still converges at the order of the scheme.

图 A.5 · 入口附近的误差 ej = −ψj − λ(xj),三套网格。(a) 一阶:每条曲线是几何衰减 −λ(0)rʲ⁺¹(虚线)加上右端的水平段;水平段是内部误差,随 h 减小,几何部分的幅值与形状不随 N 改变。(b) 二阶:误差正负相间(复根);三套网格几乎重合。−0.0020.0000.0020.0040.0060.0080246810j(从单元 0 起)N = 64N = 256N = 1024−λ(0) rj+1​−0.0050.0000.0050246810j(从单元 0 起)N = 64N = 256N = 1024(a) 一阶(b) 二阶
图 A.5 · 入口附近的误差 ej = −ψj − λ(xj),三套网格。(a) 一阶:每条曲线是几何衰减 −λ(0)rj+1(虚线)加上右端的水平段;水平段是内部误差,随 h 减小,几何部分的幅值与形状不随 N 改变。(b) 二阶:误差正负相间(复根);三套网格几乎重合。
Figure A.5 · The error ej = −ψj − λ(xj) at the first unknowns on three meshes. (a) First order: each curve is the geometric decay −λ(0)rʲ⁺¹ (dashed) plus the level on the right; that level is the interior error and shrinks with h, while the amplitude and shape of the geometric part do not change with N. (b) Second order: the error alternates in sign (complex roots); the three meshes nearly coincide.−0.0020.0000.0020.0040.0060.0080246810j (from cell 0)N = 64N = 256N = 1024−λ(0) rj+1​−0.0050.0000.0050246810j (from cell 0)N = 64N = 256N = 1024(a) First order(b) Second order
Figure A.5 · The error ej = −ψj − λ(xj) at the first unknowns on three meshes. (a) First order: each curve is the geometric decay −λ(0)rj+1 (dashed) plus the level on the right; that level is the interior error and shrinks with h, while the amplitude and shape of the geometric part do not change with N. (b) Second order: the error alternates in sign (complex roots); the three meshes nearly coincide.

两个直接后果。第一个源项的导数 −hψ0≈hλ(0)(1−r)-h\psi_0\approx h\lambda(0)(1-r),与连续区间积分 hλ(0)h\lambda(0) 之比趋于 1−r=10/13≈0.7691-r=10/13\approx0.769,不趋于 1。入口导数却仍然收敛,因为入口面的偏导正好补回同一个因子:

Two consequences follow. The first source derivative −hψ0≈hλ(0)(1−r)-h\psi_0\approx h\lambda(0)(1-r) has ratio 1−r=10/13≈0.7691-r=10/13\approx0.769 to the continuous interval integral hλ(0)h\lambda(0), not 1. The inlet derivative still converges, because the inlet-face derivative restores exactly the same factor:

dJhduin=−cLinψ0≈(u2+ε)(1−r) λ(0)=u λ(0),\frac{dJ_h}{du_{in}}=-c_L^{in}\psi_0\approx\left(\tfrac u2+\varepsilon\right)(1-r)\,\lambda(0)=u\,\lambda(0),
(u2+ε)(1−r)=(u2+ε)(ε+u/2)−(ε−u/2)ε+u/2=u.\left(\tfrac u2+\varepsilon\right)(1-r)=\left(\tfrac u2+\varepsilon\right)\frac{(\varepsilon+u/2)-(\varepsilon-u/2)}{\varepsilon+u/2}=u.

迎风入口 Hin=uin2/2H_{in}=u_{in}^2/2 不依赖内侧迹,入口行变成 (u02+ε)(ψ0−ψ1)=−g0\left(\tfrac{u_0}2+\varepsilon\right)(\psi_0-\psi_1)=-g_0,即一般行取 ψ−1=ψ0\psi_{-1}=\psi_0。光滑解本身几乎满足这个条件,第二个解的系数只有 O(h)O(h),边界层消失,全域误差回到一阶(下表)。

The upwind inlet Hin=uin2/2H_{in}=u_{in}^2/2 does not depend on the interior trace, so the inlet row becomes (u02+ε)(ψ0−ψ1)=−g0\left(\tfrac{u_0}2+\varepsilon\right)(\psi_0-\psi_1)=-g_0, the general row with ψ−1=ψ0\psi_{-1}=\psi_0. The smooth solution nearly satisfies this already, the second solution keeps a coefficient of only O(h)O(h), the layer disappears and the global error returns to first order (table below).

二阶时一个面依赖四个未知量,转置的内部行变成五项递推,冻结系数后的特征方程为

At second order a face depends on four unknowns, so the transposed interior row is a five-term recurrence; with frozen coefficients its characteristic equation is

cLρ4−(5cL+cR)ρ3+3(cL−cR)ρ2+(cL+5cR)ρ−cR=0.c_L\rho^4-(5c_L+c_R)\rho^3+3(c_L-c_R)\rho^2+(c_L+5c_R)\rho-c_R=0.

在入口状态下根为 11、3.8053.805 和一对复根 −0.018±0.246i-0.018\pm0.246\mathrm i(模 0.2460.246)。复根对离开入口振荡衰减,入口面和端点单边斜率同样给它留下 O(1)O(1) 的系数,所以全域误差仍是半阶,只是常数不同(格心约 0.0116);内部误差和入口导数按二阶收敛。根 3.8053.805 属于出口一侧,本例出口端的误差只有 O(h2)O(h^2)。格心二阶的首源项比值同样趋于 1−r1-r。

At the inlet state the roots are 11, 3.8053.805 and a complex pair −0.018±0.246i-0.018\pm0.246\mathrm i of modulus 0.2460.246. The complex pair decays with oscillation away from the inlet, and the inlet face and the one-sided end slopes again leave it an O(1)O(1) coefficient, so the global error is still of order one half with a different constant (0.0116 on this page), while the interior error and the inlet derivative converge at second order. The root 3.8053.805 belongs to the outlet side; here the outlet error is only O(h2)O(h^2). The second-order cell first-source ratio also tends to 1−r1-r.

图 A.6 · 冻结系数后的伴随递推特征根(入口状态 u=1、ε=0.8)。圆圈:一阶的 1 与 r=0.231;方块:二阶的 1、3.805 和 −0.018 ± 0.246i。单位圆内的根离开入口衰减(入口层),圆外的根离开出口衰减(出口层),ρ=1 是光滑解。−101−101234Re ρIm ρ一阶二阶r = 0.231(入口层)−0.018 ± 0.246i(入口层)3.805(出口层)ρ = 1(光滑解)
图 A.6 · 冻结系数后的伴随递推特征根(入口状态 u=1、ε=0.8)。圆圈:一阶的 1 与 r=0.231;方块:二阶的 1、3.805 和 −0.018 ± 0.246i。单位圆内的根离开入口衰减(入口层),圆外的根离开出口衰减(出口层),ρ=1 是光滑解。
Figure A.6 · Roots of the frozen-coefficient adjoint recurrences at the inlet state (u=1, ε=0.8). Circles: first order, 1 and r=0.231; squares: second order, 1, 3.805 and −0.018 ± 0.246i. Roots inside the unit circle decay away from the inlet (inlet layer), roots outside decay away from the outlet (outlet layer), and ρ=1 is the smooth solution.−101−101234Re ρIm ρfirst ordersecond orderr = 0.231 (inlet layer)−0.018 ± 0.246i (inlet layer)3.805 (outlet layer)ρ = 1 (smooth)
Figure A.6 · Roots of the frozen-coefficient adjoint recurrences at the inlet state (u=1, ε=0.8). Circles: first order, 1 and r=0.231; squares: second order, 1, 3.805 and −0.018 ± 0.246i. Roots inside the unit circle decay away from the inlet (inlet layer), roots outside decay away from the outlet (outlet layer), and ρ=1 is the smooth solution.

第三个后果:光滑方向的导数为什么不受影响。设源项密度的设计方向是光滑函数 ff(种子 s˙=f\dot s=f)。把 −ψj=λ(xj)+ej-\psi_j=\lambda(x_j)+e_j 代入离散导数,误差分成边界层与求和公式两部分:

A third consequence: why derivatives in smooth directions are unaffected. Let the design direction of the source density be a smooth function ff (seed s˙=f\dot s=f). Substituting −ψj=λ(xj)+ej-\psi_j=\lambda(x_j)+e_j into the discrete derivative splits its error into a layer part and a quadrature part:

−∑jWjψjf(xj)−∫01λf dx=∑jWjejf(xj)⏟≈ hf(0)∑jej + interior+(∑jWjλ(xj)f(xj)−∫01λf dx)⏟quadrature.\begin{aligned}-\sum_jW_j\psi_jf(x_j)-\int_0^1\lambda f\,dx&=\underbrace{\sum_jW_je_jf(x_j)}_{\approx\,h f(0)\sum_je_j\ +\ \text{interior}}\\&\quad+\underbrace{\Bigl(\sum_jW_j\lambda(x_j)f(x_j)-\int_0^1\lambda f\,dx\Bigr)}_{\text{quadrature}}.\end{aligned}

格心的求和公式是中点公式,误差 O(h2)O(h^2)。一阶时边界层的和是 −λ(0)∑jrj+1=−λ(0)r/(1−r)≈0.0104-\lambda(0)\sum_jr^{j+1}=-\lambda(0)r/(1-r)\approx0.0104(实测 0.0104),贡献 O(h)O(h),与一阶格式本身的误差同阶。二阶时边界层正负相间:e0≈0.0080e_0\approx0.0080,e1≈−0.0083e_1\approx-0.0083,其余迅速衰减,它们的和只剩 O(h)O(h)。下表按全部单元计算 ∑jWjej\sum_jW_je_j:除以 h2h^2 后趋于常数,即边界层没有留下 O(h)O(h) 的净贡献。

The cell sum is the midpoint rule, with error O(h2)O(h^2). At first order the layer sums to −λ(0)∑jrj+1=−λ(0)r/(1−r)≈0.0104-\lambda(0)\sum_jr^{j+1}=-\lambda(0)r/(1-r)\approx0.0104 (measured 0.0104), an O(h)O(h) contribution of the same order as the first-order scheme error. At second order the layer alternates in sign: e0≈0.0080e_0\approx0.0080, e1≈−0.0083e_1\approx-0.0083, and the rest decays quickly, so its sum is only O(h)O(h). The table evaluates ∑jWjej\sum_jW_je_j over all cells: divided by h2h^2 it tends to a constant, so the layer leaves no net O(h)O(h) contribution.

二阶Second order NΣ WjejΣ Wjej / h²
256−2.563e−6−0.1680
1024−1.600e−7−0.1678
4096−9.996e−9−0.1677

这种抵消不是巧合。由 ψTRDD˙=gTv\psi^TR_D\dot D=g^Tv,同一个方向导数也等于 gTvg^Tv,而离散 tangent 没有边界层,按格式的阶收敛,所以边界层的净贡献只能按同样的阶消失。只落在第一个区间的方向(首源项)不是光滑方向,这种抵消不会发生,比值仍趋于上面给出的极限。

The cancellation is no accident. Because ψTRDD˙=gTv\psi^TR_D\dot D=g^Tv, the same directional derivative equals gTvg^Tv, and the discrete tangent has no layer and converges at the order of the scheme; the net contribution of the adjoint layer must therefore vanish at the same order. A direction confined to the first interval (the first source) is not a smooth direction, no such cancellation occurs, and its ratio keeps the limit given above.

下表由本页的离散格式在构建时直接计算(Newton 加带状直接求解,伴随解转置系统)。一阶耗散入口的全域误差与 h\sqrt h 之比趋于 0.00825,实测阶趋于 0.5;首源项比值趋于 0.769;入口导数误差按一阶减小。

The table is computed at build time from this page's discretisation (Newton with a banded direct solve; the adjoint solves the transposed system). For the first-order dissipative inlet the global error divided by h\sqrt h tends to 0.00825 and the measured order to 0.5; the first-source ratio tends to 0.769; the inlet-derivative error decreases at first order.

情形CaseN全域误差Global error全域误差Global error /√h实测阶Order内部误差Interior error首源项比值First-source ratio入口导数误差Inlet-derivative error
一阶,耗散入口first order, dissipative2565.79e−40.00927—2.61e−40.7821−5.85e−4
10242.66e−40.008520.5616.58e−50.7725−1.48e−4
40961.30e−40.008320.5171.65e−50.7700−3.70e−5
163846.46e−50.008270.5054.13e−60.7694−9.24e−6
一阶,迎风入口first order, upwind2561.25e−40.00201—1.02e−41.0035−1.24e−4
10243.14e−50.001000.9992.55e−51.0009−3.04e−5
40967.85e−60.000501.0006.39e−61.0002−7.56e−6
163841.96e−60.000251.0001.60e−61.0001−1.89e−6
二阶,耗散入口second order, dissipative2567.23e−40.01156—2.21e−60.7694−7.81e−6
10243.62e−40.011570.5001.37e−70.7692−4.93e−7
40961.81e−40.011570.5008.57e−90.7692−3.09e−8
163849.04e−50.011570.5005.35e−100.7692−1.93e−9

完整连续推导见 中文版连续伴随文档。下方结果均采用当前边界闭合;可在页首切换对照。

See the English continuous-adjoint derivation. The results below use the currently selected boundary closure; switch the comparison at the top of the page.

返回正文结论Return to the main conclusions

附录 B:间断、激波位移与离散伴随

Appendix B: discontinuities, shock motion and discrete adjoints

正文先在光滑解上学习离散伴随。本附录进一步问:解出现间断后,怎样构造守恒求解程序,又怎样对这个程序求导?我们使用独立的无源、非定常 Riemann 问题。两种页面在这里都采用同一个单元平均有限体积实现;格点页的正文离散并没有被悄悄替换。附录控制独立于页顶的稳态 GS/GMRES 控制。

The main chapters introduce discrete adjoints using smooth solutions. Here we ask how to build and differentiate a conservative solver when discontinuities occur. This independent experiment solves a source-free, unsteady Riemann problem. Both pages use the same cell-average finite-volume implementation in this appendix; the node-centred discretisation in the main text is unchanged. These controls are independent of the steady GS/GMRES controls above.

B.1 从强形式到熵解:为什么不能直接跨过间断求导

B.1 From the strong form to entropy solutions

ut+f(u)x=0,f(u)=u2/2,u(x,0;a)={uL,x<a,uR,x>a.u_t+f(u)_x=0,\quad f(u)=u^2/2,\qquad u(x,0;a)=\begin{cases}u_L,&x<a,\\u_R,&x>a.\end{cases}

在跳跃处,普通意义的空间导数不存在。对一个小控制体积分,再让它包围的激波宽度趋于零,左右通量差必须等于跳跃随界面移动带来的质量变化。因此得到 Rankine–Hugoniot 条件。它给出移动速度,却不能单独选出物理解。Burgers 的熵条件要求压缩激波两侧特征线进入激波;若左值小于右值,物理解是连续展开的稀疏波,而不是满足相同守恒条件的“膨胀激波”。

Ordinary spatial derivatives do not exist at a jump. Integrate over a small control volume enclosing the moving shock: the flux difference must balance the mass change caused by its motion. This gives the Rankine–Hugoniot condition. Conservation determines a speed but does not select the physical solution. Characteristics enter an admissible compressive Burgers shock; increasing Riemann data instead generate a continuous rarefaction, not an expansion shock.

s=f(uL)−f(uR)uL−uR=uL+uR2,xs(t)=a+st,uL>s>uR.s=\frac{f(u_L)-f(u_R)}{u_L-u_R}=\frac{u_L+u_R}{2},\quad x_s(t)=a+st,\quad u_L>s>u_R.
uL<uR:u(x,t)={uL,(x−a)/t<uL,(x−a)/t,uL≤(x−a)/t≤uR,uR,(x−a)/t>uR.u_L<u_R:\quad u(x,t)=\begin{cases}u_L,&(x-a)/t<u_L,\\(x-a)/t,&u_L\le (x-a)/t\le u_R,\\u_R,&(x-a)/t>u_R.\end{cases}

可对照 Clawpack 的 Burgers Riemann 问题。下方包括移动激波 (1,0)、驻定激波 (1,−1) 和稀疏波 (−0.5,0.5)。时间和位置范围保证解析波不碰到边界;左右边界分别以给定的常值外侧状态计算通量。

See the Clawpack Burgers Riemann chapter. The experiment includes a moving shock (1,0), a stationary shock (1,−1), and a rarefaction (−0.5,0.5). The permitted time and position ranges keep the exact waves away from boundaries. Boundary fluxes use the prescribed constant exterior states.

逐步推导:Rankine–Hugoniot 条件

Step by step: the Rankine–Hugoniot condition

守恒律的积分形式对任何控制体都成立,不需要 uu 可微:

The integral form of the conservation law holds for any control volume and does not need uu to be differentiable:

ddt∫x1x2u dx=f(u(x1,t))−f(u(x2,t)).\frac{d}{dt}\int_{x_1}^{x_2}u\,dx=f(u(x_1,t))-f(u(x_2,t)).

取 x1<xs(t)<x2x_1<x_s(t)<x_2,两侧分别是常值 uLu_L、uRu_R。积分等于两段长度乘各自的值,只有激波位置随时间变化:

Take x1<xs(t)<x2x_1<x_s(t)<x_2 with constant states uLu_L and uRu_R on the two sides. The integral is the two lengths times their values, and only the shock position depends on time:

∫x1x2u dx=(xs−x1)uL+(x2−xs)uR⟹ddt∫x1x2u dx=dxsdt (uL−uR).\begin{aligned}\int_{x_1}^{x_2}u\,dx&=(x_s-x_1)u_L+(x_2-x_s)u_R\\\Longrightarrow\quad\frac{d}{dt}\int_{x_1}^{x_2}u\,dx&=\frac{dx_s}{dt}\,(u_L-u_R).\end{aligned}

令两式相等,得到激波速度;对 f=u2/2f=u^2/2,分子可以分解,速度就是两侧状态的平均:

Equating the two expressions gives the shock speed; for f=u2/2f=u^2/2 the numerator factorises and the speed is the mean of the two states:

s=dxsdt=f(uL)−f(uR)uL−uR=(uL−uR)(uL+uR)/2uL−uR=uL+uR2.s=\frac{dx_s}{dt}=\frac{f(u_L)-f(u_R)}{u_L-u_R}=\frac{(u_L-u_R)(u_L+u_R)/2}{u_L-u_R}=\frac{u_L+u_R}{2}.
图 B.1 · 包住激波的控制体 [x₁,x₂]×[t,t+Δt]。左侧流入 f(uL)Δt,右侧流出 f(uR)Δt;激波右移 sΔt,把一条宽 sΔt 的带子从 uR 变成 uL,质量增加 (uL−uR)sΔt。两者相等就是 Rankine–Hugoniot 条件。uLuRsΔt 带激波 xs(t),斜率 dx/dt = sf(uL)Δtf(uR)Δtx₁x₂tt+Δtxs(t)xs(t)+sΔt
图 B.1 · 包住激波的控制体 [x₁,x₂]×[t,t+Δt]。左侧流入 f(uL)Δt,右侧流出 f(uR)Δt;激波右移 sΔt,把一条宽 sΔt 的带子从 uR 变成 uL,质量增加 (uL−uR)sΔt。两者相等就是 Rankine–Hugoniot 条件。
Figure B.1 · A control volume [x₁,x₂]×[t,t+Δt] around the shock. The left side admits f(uL)Δt and the right side releases f(uR)Δt; the shock moves right by sΔt and turns a strip of width sΔt from uR into uL, adding mass (uL−uR)sΔt. Equating the two gives the Rankine–Hugoniot condition.uLuRstrip sΔtshock xs(t), slope dx/dt = sf(uL)Δtf(uR)Δtx₁x₂tt+Δtxs(t)xs(t)+sΔt
Figure B.1 · A control volume [x₁,x₂]×[t,t+Δt] around the shock. The left side admits f(uL)Δt and the right side releases f(uR)Δt; the shock moves right by sΔt and turns a strip of width sΔt from uR into uL, adding mass (uL−uR)sΔt. Equating the two gives the Rankine–Hugoniot condition.

熵条件看特征速度 f′(u)=uf'(u)=u:两侧特征线都要进入激波,即 uL>s>uRu_L>s>u_R。代入 s=(uL+uR)/2s=(u_L+u_R)/2,它等价于 uL>uRu_L>u_R。若 uL<uRu_L<u_R,同样满足守恒的“膨胀激波”两侧特征线离开激波,不是物理解,物理解是下图 (c) 的稀疏波扇区。

The entropy condition concerns the characteristic speed f′(u)=uf'(u)=u: characteristics from both sides must enter the shock, uL>s>uRu_L>s>u_R. With s=(uL+uR)/2s=(u_L+u_R)/2 this is equivalent to uL>uRu_L>u_R. For uL<uRu_L<u_R an "expansion shock" would conserve mass but its characteristics leave it; the physical solution is the rarefaction fan of panel (c) below.

图 B.2 · 三个 Riemann 问题在 x–t 平面上的特征线 dx/dt = u(细线)与激波 x = a + st(粗红线)。(a)(b) 两侧特征线都进入激波,满足熵条件 uL > s > uR;(c) uL < uR,从 a 出发的一族特征线张开成稀疏波扇区,没有激波。横轴以 a 为中心,示意 t ∈ [0, 0.3]。(a) 移动激波 (1, 0),s = 0.5uL = 1uR = 0x = at(b) 驻定激波 (1, −1),s = 0uL = 1uR = −1x = a(c) 稀疏波 (−0.5, 0.5)uL = −0.5uR = 0.5x = a
图 B.2 · 三个 Riemann 问题在 x–t 平面上的特征线 dx/dt = u(细线)与激波 x = a + st(粗红线)。(a)(b) 两侧特征线都进入激波,满足熵条件 uL > s > uR;(c) uL < uR,从 a 出发的一族特征线张开成稀疏波扇区,没有激波。横轴以 a 为中心,示意 t ∈ [0, 0.3]。
Figure B.2 · Characteristics dx/dt = u (thin) and the shock x = a + st (thick red) of the three Riemann problems in the x–t plane. (a)(b) Characteristics from both sides enter the shock, as the entropy condition uL > s > uR requires; (c) uL < uR, and a family of characteristics from a opens into a rarefaction fan, with no shock. The x axis is centred on a; t ∈ [0, 0.3] is schematic.(a) moving shock (1, 0), s = 0.5uL = 1uR = 0x = at(b) stationary shock (1, −1), s = 0uL = 1uR = −1x = a(c) rarefaction (−0.5, 0.5)uL = −0.5uR = 0.5x = a
Figure B.2 · Characteristics dx/dt = u (thin) and the shock x = a + st (thick red) of the three Riemann problems in the x–t plane. (a)(b) Characteristics from both sides enter the shock, as the entropy condition uL > s > uR requires; (c) uL < uR, and a family of characteristics from a opens into a rarefaction fan, with no shock. The x axis is centred on a; t ∈ [0, 0.3] is schematic.

B.2 网格、初值投影与限幅重构

B.2 Mesh, initial projection and limited reconstruction

未知量是每个单元的平均值 ui,初始时刻记作 ui⁰。若初始跳跃切过一个单元,就按左右部分长度加权,不能简单按单元中心的位置赋值,否则小幅改变 a 时整个离散初值不变,计算出的导数会错误地归零。下面 θ 是左状态在该单元所占的比例。

The unknowns are the cell averages ui, written ui⁰ at the initial time. A cell cut by the initial jump receives a length-weighted average. Sampling the state only at the cell centre would make sufficiently small changes in a invisible, giving a misleading zero derivative. Here θ is the fraction occupied by the left state.

ui0=uR+(uL−uR)θi,θi=clip⁡ ⁣(a−xi−1/2h,0,1),∂ui0∂a={(uL−uR)/h,a∈(xi−1/2,xi+1/2),0,otherwise.u_i^0=u_R+(u_L-u_R)\theta_i,\quad\theta_i=\operatorname{clip}\!\left(\frac{a-x_{i-1/2}}h,0,1\right),\quad \frac{\partial u_i^0}{\partial a}=\begin{cases}(u_L-u_R)/h,&a\in(x_{i-1/2},x_{i+1/2}),\\0,&\text{otherwise}.\end{cases}

当 a 正好在网格面上,上式不再给出唯一的双侧导数:a 向左或向右移动时变化的是不同单元。实现选取右侧方向作为初始 tangent,界面会明确提示,并同时列出单侧差分。离散初值的向量导数随网格加密变得更尖,并不应期待它逐点收敛为普通函数。

At a mesh face there is no unique two-sided derivative of this projection: moving a to the left or to the right changes different cells. The implementation selects the right-side initial direction, displays a warning, and reports both one-sided differences. The initial tangent becomes more concentrated on refinement; pointwise convergence to an ordinary function is not expected.

di=minmod⁡(ui−ui−1,ui+1−ui),ui+1/2−=ui+12di,ui+1/2+=ui+1−12di+1.d_i=\operatorname{minmod}(u_i-u_{i-1},u_{i+1}-u_i),\quad u^-_{i+1/2}=u_i+\tfrac12d_i,\quad u^+_{i+1/2}=u_{i+1}-\tfrac12d_{i+1}.

一阶令 d=0。限幅模式在两侧差分异号或有零时令 d=0,否则取绝对值较小的差分;相等时选左差分。首末单元不重构。它在光滑、非极值区域允许二阶重构,在间断附近自动降低精度;不能把“二阶模式”理解成含激波解在所有范数下都有二阶收敛。下面的图保留每个网格面和每个单元平均值,选中单元还能查看具体限幅运算。

First order sets d=0. The limited mode chooses zero for opposite signs or a zero difference; otherwise it selects the smaller magnitude, choosing the left difference at a tie. Endpoint cells are not reconstructed. Second-order reconstruction is possible in smooth non-extremal regions, while accuracy is reduced near a discontinuity. This does not imply second-order convergence in every norm for solutions with shocks. The plot shows every mesh face and cell average; selecting a cell exposes its limiter calculation.

逐步推导:切割单元的初值导数

Step by step: the derivative of the cut-cell average

设跳跃 aa 落在第 ii 个单元 (xi−1/2,xi+1/2)(x_{i-1/2},x_{i+1/2}) 内。单元平均把两段分开积分:

Let the jump aa lie in cell ii, (xi−1/2,xi+1/2)(x_{i-1/2},x_{i+1/2}). The cell average integrates the two pieces separately:

ui0=1h[∫xi−1/2auL dx+∫axi+1/2uR dx]=uR+(uL−uR)a−xi−1/2h,∂ui0∂a=uL−uRh.\begin{gathered}u_i^0=\frac1h\Bigl[\int_{x_{i-1/2}}^{a}u_L\,dx+\int_a^{x_{i+1/2}}u_R\,dx\Bigr]=u_R+(u_L-u_R)\frac{a-x_{i-1/2}}h,\\\frac{\partial u_i^0}{\partial a}=\frac{u_L-u_R}h.\end{gathered}

其他单元不含 aa,导数为零。于是 h∑i∂ui0/∂a=uL−uRh\sum_i\partial u_i^0/\partial a=u_L-u_R:离散导数是一个高 (uL−uR)/h(u_L-u_R)/h、宽 hh 的尖峰,总质量与连续导数 [u]δ(x−a)[u]\delta(x-a) 相同。

No other cell contains aa, so their derivatives vanish. Hence h∑i∂ui0/∂a=uL−uRh\sum_i\partial u_i^0/\partial a=u_L-u_R: the discrete derivative is a spike of height (uL−uR)/h(u_L-u_R)/h and width hh whose total mass equals that of the continuous derivative [u]δ(x−a)[u]\delta(x-a).

图 B.3 · 初始跳跃位于第 i 个单元内部,左状态占比 θᵢ。初值的单元平均 uᵢ⁰ = uR + (uL − uR)θᵢ。把 a 右移一个小量 τ(一个数),只有这个单元的平均值改变 (uL − uR)τ/h,即 ∂uᵢ⁰/∂a = (uL − uR)/h;乘以 h 求和,离散导数的总质量是 uL − uR,与连续的 [u]δ(x − a) 相同。u⁰i−1​ = uLu⁰i+1​ = uRu⁰i​ = uR + (uL − uR)θi​θi​haa+τ面积 (uL−uR)τ单元 i−1单元 i+1实线:初值粗线:单元平均
图 B.3 · 初始跳跃位于第 i 个单元内部,左状态占比 θi。初值的单元平均 ui⁰ = uR + (uL − uR)θi。把 a 右移一个小量 τ(一个数),只有这个单元的平均值改变 (uL − uR)τ/h,即 ∂ui⁰/∂a = (uL − uR)/h;乘以 h 求和,离散导数的总质量是 uL − uR,与连续的 [u]δ(x − a) 相同。
Figure B.3 · The initial jump lies inside cell i, with the left state occupying the fraction θᵢ. The initial cell average is uᵢ⁰ = uR + (uL − uR)θᵢ. Moving a right by a small number τ changes only this average, by (uL − uR)τ/h, so ∂uᵢ⁰/∂a = (uL − uR)/h; summed with weight h, the discrete derivative carries total mass uL − uR, like the continuous [u]δ(x − a).u⁰i−1​ = uLu⁰i+1​ = uRu⁰i​ = uR + (uL − uR)θi​θi​haa+τarea (uL−uR)τcell i−1cell i+1solid: initial databars: cell averages
Figure B.3 · The initial jump lies inside cell i, with the left state occupying the fraction θi. The initial cell average is ui⁰ = uR + (uL − uR)θi. Moving a right by a small number τ changes only this average, by (uL − uR)τ/h, so ∂ui⁰/∂a = (uL − uR)/h; summed with weight h, the discrete derivative carries total mass uL − uR, like the continuous [u]δ(x − a).
图 B.4 · minmod 的三个分支。粗线是单元平均 uᵢ,红线是中间单元的重构 uᵢ ± dᵢ/2;圆点是两个面值。左分支 dᵢ = uᵢ − uᵢ₋₁、右分支 dᵢ = uᵢ₊₁ − uᵢ 都是线性的,它们的切向量 ḋᵢ 由相邻单元的切向量按同一式子组合;零分支给出 ḋᵢ = 0。(a) 左分支|左差| 较小i−1ii+1左差 0.3,右差 0.5 → d = 0.3(b) 右分支|右差| 较小i−1ii+1左差 0.6,右差 0.2 → d = 0.2(c) 零分支两差异号(极值)i−1ii+1左差 0.5,右差 −0.4 → d = 0.0
图 B.4 · minmod 的三个分支。粗线是单元平均 ui,红线是中间单元的重构 ui ± di/2;圆点是两个面值。左分支 di = ui − ui−1、右分支 di = ui+1 − ui 都是线性的,它们的切向量 ḋi 由相邻单元的切向量按同一式子组合;零分支给出 ḋi = 0。
Figure B.4 · The three minmod branches. Bars are cell averages uᵢ; the red segment is the middle cell's reconstruction uᵢ ± dᵢ/2, and the dots are its two face values. The left branch dᵢ = uᵢ − uᵢ₋₁ and the right branch dᵢ = uᵢ₊₁ − uᵢ are linear, so the tangent ḋᵢ combines the tangents of the neighbouring cells by the same formula; the zero branch gives ḋᵢ = 0.(a) left branchsmaller |left difference|i−1ii+1left 0.3, right 0.5 → d = 0.3(b) right branchsmaller |right difference|i−1ii+1left 0.6, right 0.2 → d = 0.2(c) zero branchopposite signs (extremum)i−1ii+1left 0.5, right −0.4 → d = 0.0
Figure B.4 · The three minmod branches. Bars are cell averages ui; the red segment is the middle cell's reconstruction ui ± di/2, and the dots are its two face values. The left branch di = ui − ui−1 and the right branch di = ui+1 − ui are linear, so the tangent ḋi combines the tangents of the neighbouring cells by the same formula; the zero branch gives ḋi = 0.

B.3 守恒通量与实际时间推进

B.3 Conservative flux and implemented time integration

H(u−,u+)=14((u−)2+(u+)2)−α2(u+−u−),α=1.2,ℒi(u)=−Hi+1/2−Hi−1/2h.H(u^-,u^+)=\tfrac14\bigl((u^-)^2+(u^+)^2\bigr)-\tfrac\alpha2(u^+-u^-),\quad \alpha=1.2,\qquad \mathcal L_i(u)=-\frac{H_{i+1/2}-H_{i-1/2}}h.

这是局部 Lax–Friedrichs 形式的数值通量(和正文一样记作 H,两侧迹记作 u⁻、u⁺),本实验把波速上界 α 固定为 1.2,而非每一步计算 max|u|。给定算例的状态范围不超过 1,因此这个选择覆盖波速,也避免把 max/绝对值的分支变化混入求导。每个内部面只计算一次通量,并以相反符号进入相邻单元;所以质量变化精确等于边界净通量,只有舍入误差。

This Lax–Friedrichs-form flux (written H as in the main text, with traces u⁻ and u⁺) uses a fixed wave-speed bound α=1.2 rather than recomputing max|u|. The supplied states have magnitude at most one, so this covers their wave speeds and avoids differentiating another max/absolute-value branch. Each internal face contributes with opposite signs to adjacent cells, making mass change equal the net boundary flux up to roundoff.

yn=un+Δt ℒ(un),un+1=12un+12(yn+Δt ℒ(yn)),αΔt/h≤0.35.y^n=u^n+\Delta t\,\mathcal L(u^n),\qquad u^{n+1}=\tfrac12u^n+\tfrac12\bigl(y^n+\Delta t\,\mathcal L(y^n)\bigr),\qquad \alpha\Delta t/h\le0.35.

使用两级 SSP-RK2;步数取整后重新设置 Δt,恰好到达终止时间 T。改变设计参数 a 不改变步数、Δt、网格或 α。这样比较的是同一个离散程序。这里是显式非定常算法,没有需要 GS 求解的稳态线性方程;若改为隐式推进,每个时间层仍可用正文的 GS/预处理 GMRES 求解,但还必须把所有时间层的依赖反向传播,不能只求一个最终稳态伴随。

The implementation uses two-stage SSP-RK2. An integer number of steps is chosen and Δt adjusted to reach T exactly. Changing a leaves the mesh, time schedule and α unchanged, so comparisons differentiate the same discrete program. This explicit transient algorithm has no steady linear system to solve with GS. An implicit version could use GS/preconditioned GMRES at each time level, but its adjoint must still reverse dependencies through all time levels.

这里 N 表示单元数,K 表示时间步数;本附录的 di 是单元内增量(等于斜率乘 h),与正文按长度求导的斜率符号不同。以下示意图标出两条必须同时保留的 RK 依赖路径。

Here N counts cells and K counts time steps. The appendix uses di for an increment (slope times h), unlike the spatial derivative used for slopes in the main text. The diagram shows both RK dependency paths that must be retained.

图 B.5 · SSP-RK2 一步的两条依赖路径。前向(tangent,上点量)沿箭头方向:uⁿ 经 I + ΔtAₙ 到 yⁿ,再经 ½(I + ΔtBₙ) 到 uⁿ⁺¹,同时 uⁿ 还以 ½I 直接进入 uⁿ⁺¹。伴随(横线量)逆着箭头走,每个系数取转置,两条路径的贡献相加。un​, u˙​n​, ūn​yn​, ẏn​, ȳn​un+1​, u˙​n+1​, ūn+1​I + ΔtAn​½(I + ΔtBn​)½ Iȳn​ = ½(I + ΔtBn​T​)ūn+1​(I + ΔtAn​T​)ȳn​+ ½ ūn+1​(直接路径)上:tangent(前向);下:adjoint(反向,转置)
图 B.5 · SSP-RK2 一步的两条依赖路径。前向(tangent,上点量)沿箭头方向:un 经 I + ΔtAn 到 yn,再经 ½(I + ΔtBn) 到 un+1,同时 un 还以 ½I 直接进入 un+1。伴随(横线量)逆着箭头走,每个系数取转置,两条路径的贡献相加。
Figure B.5 · The two dependency paths of one SSP-RK2 step. The tangent (dotted quantities) follows the arrows: uⁿ reaches yⁿ through I + ΔtAₙ and then uⁿ⁺¹ through ½(I + ΔtBₙ), while uⁿ also enters uⁿ⁺¹ directly with ½I. The adjoint (barred quantities) runs against the arrows with every coefficient transposed, and the two paths add.un​, u˙​n​, ūn​yn​, ẏn​, ȳn​un+1​, u˙​n+1​, ūn+1​I + ΔtAn​½(I + ΔtBn​)½ Iȳn​ = ½(I + ΔtBn​T​)ūn+1​(I + ΔtAn​T​)ȳn​+ ½ ūn+1​ (direct path)top: tangent (forward); bottom: adjoint (reverse, transposed)
Figure B.5 · The two dependency paths of one SSP-RK2 step. The tangent (dotted quantities) follows the arrows: un reaches yn through I + ΔtAn and then un+1 through ½(I + ΔtBn), while un also enters un+1 directly with ½I. The adjoint (barred quantities) runs against the arrows with every coefficient transposed, and the two paths add.

B.4 Matrix-free tangent:求导到每个选中的分支

B.4 Matrix-free tangent through each selected branch

H˙=(u−2+α2)u˙−+(u+2−α2)u˙+,ℒ˙i=(H˙i−1/2−H˙i+1/2)/h.\dot H=\Bigl(\frac{u^-}2+\frac\alpha2\Bigr)\dot u^-+\Bigl(\frac{u^+}2-\frac\alpha2\Bigr)\dot u^+,\quad \dot{\mathcal L}_i=(\dot H_{i-1/2}-\dot H_{i+1/2})/h.

先保存每个 RK 子阶段选中的 minmod 分支。左分支给出 d˙i=u˙i−u˙i−1\dot d_i=\dot u_i-\dot u_{i-1},右分支给出 d˙i=u˙i+1−u˙i\dot d_i=\dot u_{i+1}-\dot u_i,零分支给出 d˙i=0\dot d_i=0。再求左右重构值、面通量和单元残差的切向量。这是局部运算得到的 ℒ˙=ℒ′(u) u˙\dot{\mathcal L}=\mathcal L'(u)\,\dot u,不组装全局矩阵。注意:保存分支仍要对该分支里的差分求导;把整个限幅斜率当常数冻结通常不是原离散程序的导数。

Save the selected minmod branch at each RK stage. Its slope tangent is d˙i=u˙i−u˙i−1\dot d_i=\dot u_i-\dot u_{i-1} for the left branch, d˙i=u˙i+1−u˙i\dot d_i=\dot u_{i+1}-\dot u_i for the right branch, and d˙i=0\dot d_i=0 for the zero branch. Carry the tangents through the reconstructed traces, face fluxes and residual scatter. These local operations evaluate ℒ˙=ℒ′(u) u˙\dot{\mathcal L}=\mathcal L'(u)\,\dot u without a global matrix. Saving a branch still requires differentiating the difference inside it; freezing the whole limited slope generally differentiates a different program.

u˙0=∂au0 a˙  (a˙=1),y˙n=u˙n+Δt Anu˙n,u˙n+1=12u˙n+12(y˙n+Δt Bny˙n),An=ℒ′(un), Bn=ℒ′(yn).\dot u^0=\partial_a u^0\,\dot a\ \ (\dot a=1),\quad \dot y^n=\dot u^n+\Delta t\,A_n\dot u^n,\quad \dot u^{n+1}=\tfrac12\dot u^n+\tfrac12(\dot y^n+\Delta t\,B_n\dot y^n),\quad A_n=\mathcal L^{\prime}(u^n),\ B_n=\mathcal L^{\prime}(y^n).

在 minmod 的零点或等幅交界处,所保存的是一个选定分支的线性映射,不保证存在对所有方向都适用的经典导数。即使 tangent 与 adjoint 的点积完全相等,也只证明两段代码互为转置;还需要比较单侧差分、分支切换以及步长变化。不能直接把正文的复数步长算法套到比较运算和 minmod 上。

At zero or equal-magnitude limiter boundaries, the saved map is a selected-branch linearisation, not necessarily a classical derivative valid for every direction. A perfect tangent/adjoint dot test only establishes transposition of those maps. One-sided differences, branch changes and step-size dependence still matter. The complex-step procedure in the main text cannot be applied blindly through comparisons and minmod.

B.5 反向时间 adjoint:转置面循环和 RK 计算图

B.5 Reverse-time adjoint: face loops and the RK graph

Jh=h∑iq(xi)G(uiK),q(x)=1+x,G(u)=u or u2/2,uˉiK=∂Jh∂uiK=hq(xi)G′(uiK).J_h=h\sum_iq(x_i)G(u_i^K),\quad q(x)=1+x,\quad G(u)=u\ \text{or}\ u^2/2,\qquad \bar u_i^K=\frac{\partial J_h}{\partial u_i^K}=hq(x_i)G^{\prime}(u_i^K).

对一个面,从空间算子的种子 ℒˉ=w\bar{\mathcal L}=w 出发,面通量的伴随是 Hˉi+1/2=(wi+1−wi)/h\bar H_{i+1/2}=(w_{i+1}-w_i)/h;边界外侧种子为零。乘以通量对 u−u^-、u+u^+ 的偏导,累加到两侧单元的伴随,并把 ±12\pm\tfrac12 的贡献传给斜率的伴随 dˉ\bar d。最后按照保存的 minmod 分支,把 dˉ\bar d 加到对应的两个单元。这直接计算 ℒ′(u)Tw\mathcal L'(u)^Tw。随后的 RK 反传必须同时保留 unu^n 到最终平均值的直接路径与经过 yny^n 的路径。

For each face, start from the seed ℒˉ=w\bar{\mathcal L}=w of the spatial operator: the face-flux adjoint is Hˉi+1/2=(wi+1−wi)/h\bar H_{i+1/2}=(w_{i+1}-w_i)/h, with zero exterior seeds. Multiply by the flux derivatives with respect to u−u^- and u+u^+, accumulate into the adjoints of the two adjacent cells, and pass the ±12\pm\tfrac12 contributions to the slope adjoints dˉ\bar d. Reverse the saved slope branch into its two cells. This evaluates ℒ′(u)Tw\mathcal L'(u)^Tw directly. Reversing RK must include both the direct averaging path from unu^n and the path through yny^n.

yˉn=12(I+ΔtBnT)uˉn+1,uˉn=12uˉn+1+(I+ΔtAnT)yˉn,dJhda=(uˉ0)T∂au0=(uˉK)Tu˙K.\bar y^n=\tfrac12(I+\Delta t B_n^T)\bar u^{n+1},\qquad \bar u^n=\tfrac12\bar u^{n+1}+(I+\Delta t A_n^T)\bar y^n,\qquad \frac{dJ_h}{da}=(\bar u^0)^T\partial_a u^0=(\bar u^K)^T\dot u^K.

本实验存储每个时间层和两级重构分支,内存随“单元数×时间步数”增长。工业非定常伴随通常需要检查点和重算。图中画的是 ū/h,便于与连续伴随场比较;真正用于点积的是含 h 的 ū,不能再额外乘一次 h。

This experiment stores states and both reconstruction stages, using memory proportional to cells times steps. Large unsteady adjoints usually need checkpointing and recomputation. The plot displays ū/h for comparison with a continuous adjoint density; dot products use ū itself, which already contains h.

u = exact_cell_average(step(a))
for n = 0,...,K-1:
    A = stage(u)                  # flux + saved limiter branch
    y = u + dt*A.L
    B = stage(y)
    u = (u + y + dt*B.L)/2
    save(A, B)
u_b = h*q*Gprime(u)               # u_b = ū^K: the seed dJ_h/du^K
for n = K-1,...,0:                # reverse order: the adjoint of y first
    y_b = (u_b + dt*vjp(B[n],u_b))/2
    u_b = u_b/2 + y_b + dt*vjp(A[n],y_b)
gradient = dot(u_b, initial_direction(a))

逐步推导:一个面的反向传播

Step by step: reversing one face

前向计算中,内部面 i+12i+\tfrac12 的通量 Hi+1/2H_{i+1/2} 只进入两个单元的 ℒ\mathcal L。以 ℒˉ=w\bar{\mathcal L}=w 为种子,它的伴随收集这两条路径:

In the forward computation, the flux Hi+1/2H_{i+1/2} of the interior face i+12i+\tfrac12 enters ℒ\mathcal L of only two cells. With the seed ℒˉ=w\bar{\mathcal L}=w, its adjoint collects these two paths:

ℒi=⋯−Hi+1/2h,ℒi+1=Hi+1/2h+⋯⟹Hˉi+1/2=∑kwk∂ℒk∂Hi+1/2=wi+1−wih.\begin{gathered}\mathcal L_i=\cdots-\frac{H_{i+1/2}}h,\qquad \mathcal L_{i+1}=\frac{H_{i+1/2}}h+\cdots\\\Longrightarrow\quad \bar H_{i+1/2}=\sum_kw_k\frac{\partial\mathcal L_k}{\partial H_{i+1/2}}=\frac{w_{i+1}-w_i}h.\end{gathered}

再乘以通量对两侧迹的偏导,然后按重构 u−=ui+12diu^-=u_i+\tfrac12d_i、u+=ui+1−12di+1u^+=u_{i+1}-\tfrac12d_{i+1} 的转置累加(反向语句按与前向相反的顺序执行):

Multiply by the flux derivatives with respect to the two traces, then accumulate through the transposed reconstruction u−=ui+12diu^-=u_i+\tfrac12d_i, u+=ui+1−12di+1u^+=u_{i+1}-\tfrac12d_{i+1} (the reverse statements run in the opposite order of the forward ones):

uˉ−=Hˉi+1/2(u−2+α2),uˉ+=Hˉi+1/2(u+2−α2);uˉi+=uˉ−,  dˉi+=12uˉ−,uˉi+1+=uˉ+,  dˉi+1-=12uˉ+.\begin{gathered}\bar u^-=\bar H_{i+1/2}\Bigl(\frac{u^-}2+\frac\alpha2\Bigr),\qquad \bar u^+=\bar H_{i+1/2}\Bigl(\frac{u^+}2-\frac\alpha2\Bigr);\\\bar u_i\mathrel{\text{+=}}\bar u^-,\ \ \bar d_i\mathrel{\text{+=}}\tfrac12\bar u^-,\qquad \bar u_{i+1}\mathrel{\text{+=}}\bar u^+,\ \ \bar d_{i+1}\mathrel{\text{-=}}\tfrac12\bar u^+.\end{gathered}

最后把每个 dˉi\bar d_i 按保存的分支送回两个单元:左分支 di=ui−ui−1d_i=u_i-u_{i-1} 给出 uˉi+=dˉi\bar u_i\mathrel{\text{+=}}\bar d_i、uˉi−1-=dˉi\bar u_{i-1}\mathrel{\text{-=}}\bar d_i;右分支类推;零分支什么也不做。边界面的外侧状态是常数,没有对应的伴随。

Finally send each dˉi\bar d_i back to two cells through the saved branch: the left branch di=ui−ui−1d_i=u_i-u_{i-1} gives uˉi+=dˉi\bar u_i\mathrel{\text{+=}}\bar d_i and uˉi−1-=dˉi\bar u_{i-1}\mathrel{\text{-=}}\bar d_i; the right branch is analogous; the zero branch does nothing. Exterior states at boundary faces are constants and have no adjoint.

图 B.6 · 一个内部面 i+½ 的反向传播(𝓛′ᵀw 的一项)。每一步都是前向那一步的转置:面通量以 −1/h、+1/h 进入两侧残差,所以它的伴随是两侧种子之差除以 h;再乘以通量对左右迹的偏导,经重构与所选 minmod 分支回到单元平均。残差种子wi​, wi+1​面通量伴随H¯​ = (wi+1​ − wi​)/h两侧迹ū⁻ = H¯​(u⁻/2 + α/2)ū⁺ = H¯​(u⁺/2 − α/2)重构ūi​ += ū⁻, d¯​i​ += ū⁻/2ūi+1​ += ū⁺, d¯​i+1​ −= ū⁺/2minmod 分支左:ūi​ += d¯​i​, ūi−1​ −= d¯​i​右:ūi+1​ += d¯​i​, ūi​ −= d¯​i​箭头方向与前向计算相反:前向从单元平均出发,经重构、通量到残差。
图 B.6 · 一个内部面 i+½ 的反向传播(𝓛′Tw 的一项)。每一步都是前向那一步的转置:面通量以 −1/h、+1/h 进入两侧残差,所以它的伴随是两侧种子之差除以 h;再乘以通量对左右迹的偏导,经重构与所选 minmod 分支回到单元平均。
Figure B.6 · Reverse propagation through one interior face i+½ (one term of 𝓛′ᵀw). Each step is the transpose of the corresponding forward step: the face flux enters the two residuals with −1/h and +1/h, so its adjoint is the difference of the two seeds divided by h; multiplying by the flux derivatives with respect to the two traces and reversing the reconstruction and the selected minmod branch returns to the cell averages.residual seedswi​, wi+1​face-flux adjointH¯​ = (wi+1​ − wi​)/hthe two tracesū⁻ = H¯​(u⁻/2 + α/2)ū⁺ = H¯​(u⁺/2 − α/2)reconstructionūi​ += ū⁻, d¯​i​ += ū⁻/2ūi+1​ += ū⁺, d¯​i+1​ −= ū⁺/2minmod branchleft: ūi​ += d¯​i​, ūi−1​ −= d¯​i​right: ūi+1​ += d¯​i​, ūi​ −= d¯​i​The arrows run opposite to the forward computation,which goes from cell averages through reconstruction and flux to residuals.
Figure B.6 · Reverse propagation through one interior face i+½ (one term of 𝓛′Tw). Each step is the transpose of the corresponding forward step: the face flux enters the two residuals with −1/h and +1/h, so its adjoint is the difference of the two seeds divided by h; multiplying by the flux derivatives with respect to the two traces and reversing the reconstruction and the selected minmod branch returns to the cell averages.

逐步推导:SSP-RK2 的反向与点积守恒

Step by step: reversing SSP-RK2 and the conserved dot product

一步 SSP-RK2 的 tangent 是两个线性映射的组合:

The tangent of one SSP-RK2 step combines two linear maps:

y˙n=(I+ΔtAn)u˙n,u˙n+1=12u˙n+12(I+ΔtBn)y˙n.\dot y^n=(I+\Delta tA_n)\dot u^n,\qquad \dot u^{n+1}=\tfrac12\dot u^n+\tfrac12(I+\Delta tB_n)\dot y^n.

反向时,横线量是目标对该中间量的导数(伴随),语句按相反顺序执行:先算 yˉn\bar y^n,再算 uˉn\bar u^n。yny^n 只通过第二式影响 un+1u^{n+1};unu^n 既直接进入 un+1u^{n+1},又通过 yny^n 进入,所以两条路径相加:

In reverse, a barred quantity is the derivative of the objective with respect to an intermediate quantity (its adjoint), and the statements run in the opposite order: first yˉn\bar y^n, then uˉn\bar u^n. yny^n affects un+1u^{n+1} only through the second map; unu^n enters un+1u^{n+1} both directly and through yny^n, so the two paths add:

yˉn=(∂un+1∂yn)Tuˉn+1=12(I+ΔtBnT)uˉn+1,uˉn=12uˉn+1+(I+ΔtAnT)yˉn.\bar y^n=\Bigl(\frac{\partial u^{n+1}}{\partial y^n}\Bigr)^T\bar u^{n+1}=\tfrac12(I+\Delta tB_n^T)\bar u^{n+1},\qquad \bar u^n=\tfrac12\bar u^{n+1}+(I+\Delta tA_n^T)\bar y^n.

用这两式可以直接证明每一步的点积不变(AD 的对偶恒等式),于是 (uˉ0)Tu˙0=(uˉK)Tu˙K(\bar u^0)^T\dot u^0=(\bar u^K)^T\dot u^K,即 adjoint 与 tangent 给出同一个 dJh/dadJ_h/da:

These two formulas prove directly that each step preserves the dot product (the duality identity of AD), so (uˉ0)Tu˙0=(uˉK)Tu˙K(\bar u^0)^T\dot u^0=(\bar u^K)^T\dot u^K: the adjoint and the tangent give the same dJh/dadJ_h/da:

(uˉn+1)Tu˙n+1=12(uˉn+1)Tu˙n+12((I+ΔtBnT)uˉn+1)T⏟(yˉn)T(I+ΔtAn)u˙n=(12uˉn+1+(I+ΔtAnT)yˉn)Tu˙n=(uˉn)Tu˙n.\begin{aligned}(\bar u^{n+1})^T\dot u^{n+1}&=\tfrac12(\bar u^{n+1})^T\dot u^n+\underbrace{\tfrac12\bigl((I+\Delta tB_n^T)\bar u^{n+1}\bigr)^T}_{(\bar y^n)^T}(I+\Delta tA_n)\dot u^n\\&=\bigl(\tfrac12\bar u^{n+1}+(I+\Delta tA_n^T)\bar y^n\bigr)^T\dot u^n=(\bar u^n)^T\dot u^n.\end{aligned}

B.6 激波位移导数与连续伴随的适定性

B.6 Shock displacement and continuous-adjoint well-posedness

改变初始位置 a 会移动激波。把解写成两个常值和 Heaviside 阶跃,就会得到 Dirac 分布,而不是一个有界的普通 tangent 场。目标函数仍可有有限导数:应先对移动界面的积分求导,不能在激波处随意代入一个 G′(u)。以下方括号表示左值减右值。

Changing a moves the shock. Differentiating the Heaviside representation gives a Dirac distribution rather than a bounded tangent field. An integral objective can nevertheless have a finite derivative: differentiate the moving-interface integral instead of assigning an arbitrary G′(u) at the jump. Brackets below denote left minus right.

J(a)=G(uL)∫0a+sTq(x) dx+G(uR)∫a+sT1q(x) dx,∂axs=1.J(a)=G(u_L)\int_0^{a+sT}q(x)\,dx+G(u_R)\int_{a+sT}^1q(x)\,dx,\qquad \partial_a x_s=1.
∂au=[u]δ(x−xs),J=∫01q(x)G(u(x,T)) dx,dJda=q(xs)[G].\partial_a u=[u]\delta(x-x_s),\qquad J=\int_0^1q(x)G(u(x,T))\,dx,\qquad \frac{dJ}{da}=q(x_s)[G].

这个推导也解释了为何不能写成 G′(u(xs))·[u]:间断点的 u 没有唯一值,非线性 G 的跳跃是 [G]。对二次目标,[G]/[u]=(uL+uR)/2=s[G]/[u]=(u_L+u_R)/2=s。若忽略激波位移,即使两侧光滑区的线性化都正确,也会漏掉整个位置导数。

This derivation explains why G′(u(xs))·[u] is not a valid replacement: u has no unique value at the jump, and the nonlinear jump is [G]. For a quadratic objective, [G]/[u]=(uL+uR)/2=s[G]/[u]=(u_L+u_R)/2=s. Ignoring shock motion loses the entire position derivative even when both smooth-side linearisations are correct.

在两侧光滑区域形式上有 −λt−uλx=0,终端值为 qG′。但这不足以定义跨激波的伴随:必须包含界面移动的贡献。对此常值 Riemann 激波,把进入激波的初始特征线区域识别出来,得到下面的初始伴随平台。它满足 λ(a,0)·[u]=q(xs)[G],从而恢复位置导数。交互在初始时刻画出这个解析参考;稀疏波没有激波平台,不画此参考。

In smooth regions the formal adjoint is −λt−uλx=0 with terminal data qG′. These equations alone do not define an adjoint across the shock: interface motion must be included. For this constant-state Riemann shock, identifying initial characteristics captured by the shock gives the plateau below. Its value times [u] recovers q(xs)[G]. The interaction overlays this analytic reference at the initial time; no shock plateau is drawn for a rarefaction.

λ(x,0)={q(x+uLT)G′(uL),x<a+(s−uL)T,q(xs)[G]/[u],a+(s−uL)T<x<a+(s−uR)T,q(x+uRT)G′(uR),x>a+(s−uR)T.\lambda(x,0)=\begin{cases}q(x+u_LT)G^{\prime}(u_L),&x<a+(s-u_L)T,\\q(x_s)[G]/[u],&a+(s-u_L)T<x<a+(s-u_R)T,\\q(x+u_RT)G^{\prime}(u_R),&x>a+(s-u_R)T.\end{cases}

稀疏波可直接在扇区内求导:dJ/da=−∫扇区 q(x)G′((x−a)/T)/T dx,边缘因状态连续而没有额外跳跃项。驻定激波 (1,−1) 的二次目标有 [G]=0,所以连续位置导数恰为零;数值上涂抹的激波可能产生非零的位置灵敏度。这里的差异是需要研究的结果,不能通过修改解析答案来“对齐”。

For a rarefaction, dJ/da=−∫fan q(x)G′((x−a)/T)/T dx; continuity at fan edges eliminates jump contributions. For the stationary (1,−1) shock and quadratic objective, [G]=0 and the continuous position derivative is exactly zero. A numerically smeared shock may produce nonzero position sensitivity. Such discrepancies are a result to investigate, not a reason to adjust the analytic reference.

逐步推导:移动界面的求导(Leibniz 公式)

Step by step: differentiating a moving interface (Leibniz rule)

激波情形的目标由两段常值积分组成,积分上下限随 aa 移动。对积分限求导(Leibniz 公式),被积函数本身与 aa 无关:

For a shock the objective consists of two integrals of constants whose limits move with aa. Differentiating the limits (Leibniz rule), with integrands independent of aa:

dJda=G(uL)q(xs)∂xs∂a−G(uR)q(xs)∂xs∂a=q(xs)[G(uL)−G(uR)],∂xs∂a=1.\frac{dJ}{da}=G(u_L)q(x_s)\frac{\partial x_s}{\partial a}-G(u_R)q(x_s)\frac{\partial x_s}{\partial a}=q(x_s)\bigl[G(u_L)-G(u_R)\bigr],\qquad \frac{\partial x_s}{\partial a}=1.

本页默认 a=0.371a=0.371、T=0.15T=0.15:移动激波的 xs=a+sT=0.446x_s=a+sT=0.446,G=uG=u 时 dJ/da=1.446dJ/da=1.446,G=u2/2G=u^2/2 时为 0.7230.723;驻定激波 xs=ax_s=a,[G][G] 分别为 2 与 0,得到 2.7422.742 与 0。

With this page's defaults a=0.371a=0.371, T=0.15T=0.15 the moving shock has xs=a+sT=0.446x_s=a+sT=0.446, giving dJ/da=1.446dJ/da=1.446 for G=uG=u and 0.7230.723 for G=u2/2G=u^2/2; the stationary shock has xs=ax_s=a and [G][G] equal to 2 and 0, giving 2.7422.742 and 0.

逐步推导:伴随平台来自质量守恒

Step by step: the adjoint plateau from mass conservation

伴随值就是目标对初值局部扰动的灵敏度。在初始时刻 x0x_0 处加一小块质量 ϵ\epsilon,即 δu(x,0)=ϵ δ(x−x0)\delta u(x,0)=\epsilon\,\delta(x-x_0)。它沿特征线 dx/dt=udx/dt=u 平移。若在 TT 之前没有碰到激波,终端的目标变化是 ϵ q(x0+uT)G′(u)\epsilon\,q(x_0+uT)G'(u),这就是平台两侧的值。若它先进入激波,扰动被激波吸收,两侧状态恢复为 uLu_L、uRu_R,多出的质量只能表现为激波位置的移动:

An adjoint value is the sensitivity of the objective to a local change of the initial data. Add a small mass ϵ\epsilon at x0x_0 at the initial time, δu(x,0)=ϵ δ(x−x0)\delta u(x,0)=\epsilon\,\delta(x-x_0). It travels along the characteristic dx/dt=udx/dt=u. If it does not meet the shock before TT, the terminal change of the objective is ϵ q(x0+uT)G′(u)\epsilon\,q(x_0+uT)G'(u): these are the values on the two sides of the plateau. If it enters the shock first, the shock absorbs it, both states return to uLu_L and uRu_R, and the extra mass can only appear as a shift of the shock:

(uL−uR) δxs=ϵ⟹δJ=q(xs)[G] δxs=q(xs)[G][u] ϵ⟹λ(x0,0)=q(xs)[G][u].\begin{gathered}(u_L-u_R)\,\delta x_s=\epsilon\quad\Longrightarrow\quad \delta J=q(x_s)[G]\,\delta x_s=\frac{q(x_s)[G]}{[u]}\,\epsilon\\\Longrightarrow\quad \lambda(x_0,0)=\frac{q(x_s)[G]}{[u]}.\end{gathered}

这个值与 x0x_0 无关,只要 x0x_0 的特征线在 TT 之前进入激波。左侧特征线 x0+uLtx_0+u_Lt 在 t=Tt=T 恰好到达激波的起点是 x0=a+(s−uL)T=0.296x_0=a+(s-u_L)T=0.296,右侧是 a+(s−uR)T=0.446a+(s-u_R)T=0.446,两者之间就是平台。G=uG=u 时平台与两侧连续(值 1.446);G=u2/2G=u^2/2 时平台值 0.723,两侧分别是 q(x+T)q(x+T) 与 0,出现两个跳跃。平台值乘 [u][u] 正好是上面的 dJ/dadJ/da。

The value does not depend on x0x_0, provided the characteristic from x0x_0 enters the shock before TT. The left characteristic x0+uLtx_0+u_Lt reaches the shock exactly at t=Tt=T from x0=a+(s−uL)T=0.296x_0=a+(s-u_L)T=0.296, the right one from a+(s−uR)T=0.446a+(s-u_R)T=0.446; the plateau lies between them. For G=uG=u the plateau is continuous with both sides (value 1.446); for G=u2/2G=u^2/2 the plateau value is 0.723 while the sides are q(x+T)q(x+T) and 0, with two jumps. The plateau value times [u][u] is exactly the dJ/dadJ/da above.

图 B.7 · 移动激波 (1, 0),a = 0.371、T = 0.15。上:伴随沿特征线从 t = T 反向传到 t = 0;阴影三角形里的特征线在 T 之前进入激波,携带同一个激波值,形成初始平台 [0.296, 0.446]。下:初始伴随 λ(x,0),实线 G = u²/2,虚线 G = u。0.296a0.446t = 0t = T激波0.00.51.01.50.20.30.40.50.6xG = u²/2G = uλ(x, 0)
图 B.7 · 移动激波 (1, 0),a = 0.371、T = 0.15。上:伴随沿特征线从 t = T 反向传到 t = 0;阴影三角形里的特征线在 T 之前进入激波,携带同一个激波值,形成初始平台 [0.296, 0.446]。下:初始伴随 λ(x,0),实线 G = u²/2,虚线 G = u。
Figure B.7 · Moving shock (1, 0), a = 0.371, T = 0.15. Top: the adjoint travels back along characteristics from t = T to t = 0; characteristics in the shaded triangle reach the shock before T and carry one shock value, which forms the initial plateau [0.296, 0.446]. Bottom: the initial adjoint λ(x,0), solid for G = u²/2, dashed for G = u.0.296a0.446t = 0t = Tshock0.00.51.01.50.20.30.40.50.6xG = u²/2G = uλ(x, 0)
Figure B.7 · Moving shock (1, 0), a = 0.371, T = 0.15. Top: the adjoint travels back along characteristics from t = T to t = 0; characteristics in the shaded triangle reach the shock before T and carry one shock value, which forms the initial plateau [0.296, 0.446]. Bottom: the initial adjoint λ(x,0), solid for G = u²/2, dashed for G = u.

逐步推导:稀疏波的导数

Step by step: the derivative of the rarefaction

稀疏波有三段:扇区外两段常值,扇区内 u=(x−a)/Tu=(x-a)/T。三段的积分限都随 aa 移动:

The rarefaction has three pieces: constants outside the fan and u=(x−a)/Tu=(x-a)/T inside it. All the limits move with aa:

J(a)=G(uL)∫0xLq dx+∫xLxRq(x) G(x−aT)dx+G(uR)∫xR1q dx,xL,R=a+uL,RT.\begin{gathered}J(a)=G(u_L)\int_0^{x_L}q\,dx+\int_{x_L}^{x_R}q(x)\,G\Bigl(\frac{x-a}T\Bigr)dx+G(u_R)\int_{x_R}^1q\,dx,\\x_{L,R}=a+u_{L,R}T.\end{gathered}

扇区边缘状态连续,G((xL−a)/T)=G(uL)G((x_L-a)/T)=G(u_L),所以 Leibniz 公式产生的四个边界项两两抵消,只剩被积函数对 aa 的导数;再令 ξ=(x−a)/T\xi=(x-a)/T:

The state is continuous at the fan edges, G((xL−a)/T)=G(uL)G((x_L-a)/T)=G(u_L), so the four boundary terms of the Leibniz rule cancel in pairs and only the derivative of the integrand remains; then set ξ=(x−a)/T\xi=(x-a)/T:

dJda=−1T∫xLxRq(x) G′(x−aT)dx=−∫uLuRq(a+Tξ) G′(ξ) dξ.\begin{aligned}\frac{dJ}{da}&=-\frac1T\int_{x_L}^{x_R}q(x)\,G'\Bigl(\frac{x-a}T\Bigr)dx\\&=-\int_{u_L}^{u_R}q(a+T\xi)\,G'(\xi)\,d\xi.\end{aligned}

对 q=1+xq=1+x、uL=−uRu_L=-u_R:G=uG=u 时为 −(1+a)(uR−uL)=−1.371-(1+a)(u_R-u_L)=-1.371;G=u2/2G=u^2/2 时奇次项相消,只剩 −T∫ξ2dξ=−T(uR3−uL3)/3=−0.0125-T\int\xi^2d\xi=-T(u_R^3-u_L^3)/3=-0.0125。这些值就是 B.7 实验结果表中“连续位置导数”一行的数。

For q=1+xq=1+x and uL=−uRu_L=-u_R: G=uG=u gives −(1+a)(uR−uL)=−1.371-(1+a)(u_R-u_L)=-1.371; for G=u2/2G=u^2/2 the odd terms cancel and only −T∫ξ2dξ=−T(uR3−uL3)/3=−0.0125-T\int\xi^2d\xi=-T(u_R^3-u_L^3)/3=-0.0125 remains. These are the values in the “Continuous position derivative” row of the B.7 results table.

还要区分另一种适定性问题:对稳态、无源的 (1,−1) 问题,左右通量相同,很多激波位置都满足相同边界数据,位置没有被唯一选定。其平移方向可能对应零模,稳态线性化可能奇异。这里的非定常问题通过初始位置 a 选定轨迹,因此不能用稳态的不唯一性否定当前离散时间程序的求导。

A separate well-posedness issue arises for the source-free steady (1,−1) problem. Equal boundary fluxes admit multiple shock positions; translation may form a null mode and make the steady linearisation singular. The transient initial position a selects a trajectory. Steady nonuniqueness therefore does not invalidate differentiation of this specified discrete time program.

Giles 与 Ulbrich(2010,第二部分)研究了激波处离散伴随的收敛,并展示了原始解收敛而伴随平台不正确的情形;其证明针对特定数值耗散及随加密增长的激波覆盖点数。本附录的固定 α、minmod 方法不自动满足该定理的假设。因此下方同时显示离散验证和连续参考误差,不把前者当作后者的证明。

Giles and Ulbrich (2010, Part II) analyse discrete-adjoint convergence at shocks and exhibit incorrect adjoint plateaux despite primal convergence. Their proof uses particular dissipation scaling and an increasing number of points across the shock under refinement. That theorem does not automatically cover this fixed-α, minmod scheme. The experiment reports discrete verification separately from error against the continuous reference.

B.7 可执行实验:网格、限幅、时间反传和差分

B.7 Executable experiment: mesh, limiter, reverse time and FD

误差表中的 L1 是 hΣ|ui−精确单元平均i|,不是对分片常值数值解与点态精确解之差的连续积分;实测阶为 log₂(ENE_N/E2NE_{2N})。两种误差在切过激波的单元中不同。图中的连续伴随参考按单元中心采样。分支改变数统计的是任一 RK 子阶段发生变化的时间层,而不是改变的单元总数。

The tabulated L1 error is hΣ|ui−exact cell averagei|, not the continuous integral of the error between a piecewise-constant numerical field and the pointwise exact solution. Observed order is log₂(ENE_N/E2NE_{2N}); these error definitions differ in shock-cut cells. The continuous-adjoint reference is sampled at cell centres. Branch-change counts refer to time levels with a change in either RK stage, not the total number of switched cells.

使用顺序:先比较两种空间离散的原始解和网格细化误差;再拖动时间层,看 tangent 如何集中到激波附近、伴随如何反向形成平台;最后改变 a(也可输入恰好位于网格面的数),比较单侧差分与中心差分。限幅分支改变的时间层数仅作诊断:小数值尾部的分支变化不一定显著影响目标,但不能忽略真实的不光滑点。

Suggested sequence: compare primal solutions and refinement errors for both spatial schemes; move the time slider to see tangent concentration and reverse-time adjoint evolution; then vary a, including positions exactly on mesh faces, and compare one-sided and central differences. The count of time levels with changed limiter branches is diagnostic: switches in tiny numerical tails need not affect the objective significantly, but actual nonsmooth points cannot be ignored.

B.8 小结与实现范围

B.8 Summary and implementation scope

本附录完整实现了三个 Riemann 问题的守恒时间推进、初值位置 tangent、反向时间离散 adjoint,以及解析解、质量平衡、点积和步长扫描验证。它没有实现任意激波相互作用的连续伴随,也没有证明当前耗散下伴随随网格加密收敛。教学的主线仍是对实际离散程序建立可核查的伴随;简单连续解用于验证和揭示局限。走向工业非结构网格 RANS 时,同样要交代限幅、边界、湍流闭合和求解过程的具体求导约定,不能假定存在一个可直接作为严格基准的完整连续伴随。

This appendix implements conservative time integration, initial-position tangents, reverse-time discrete adjoints, and analytic-solution, mass-balance, dot-product and step-size checks for three Riemann problems. It does not implement continuous adjoints for arbitrary shock interactions or prove adjoint convergence for this dissipation. The teaching focus remains an auditable adjoint of the actual discrete program. Simple continuous solutions expose both agreement and limitations. Industrial unstructured RANS additionally requires explicit differentiation conventions for limiters, boundaries, turbulence closure and solver operations; a complete continuous adjoint cannot simply be assumed to provide a rigorous reference.

查看本实验完整数值实现(JavaScript)Full numerical implementation (JavaScript)
/* Appendix B: conservative unsteady cell averages, selected-branch JVP/VJP. */
(function(root){'use strict';
const zeros=n=>Array(n).fill(0),dot=(a,b)=>a.reduce((s,x,i)=>s+x*b[i],0);
const cases={shock:[1,0],stationary:[1,-1],fan:[-.5,.5]};
function model({n=80,T=.15,a=.371,kind='shock',order=1,alpha=1.2,objective='linear'}={}){
 if(![n,T,a,alpha].every(Number.isFinite)||!['linear','quadratic'].includes(objective)||!cases[kind]||![1,2].includes(order)||!Number.isInteger(n)||n<8||T<0||T>.25||a<.2||a>.6||alpha<1)throw Error('Invalid shock experiment');
 const [left,right]=cases[kind],h=1/n,x=Array.from({length:n},(_,i)=>(i+.5)*h),steps=T?Math.ceil(T/(.35*h/alpha)):0,dt=steps?T/steps:0;
 const G=u=>objective==='linear'?u:.5*u*u,Gp=u=>objective==='linear'?1:u,q=x=>1+x;
 function coordinate(position){const z=position/h,r=Math.round(z);return Math.abs(z-r)<=8*Number.EPSILON*Math.max(1,Math.abs(z))?r:z;}
 function initial(position=a){const z=coordinate(position);return x.map((_,i)=>right+(left-right)*Math.max(0,Math.min(1,z-i)));}
 function initialDirection(position=a){const v=zeros(n);v[Math.min(n-1,Math.floor(coordinate(position)))]=(left-right)/h;return v;}
 function stage(u){
  const slope=zeros(n),weights=Array.from({length:n},()=>[]),branch=Array(n).fill('zero');let ties=0;
  if(order===2)for(let i=1;i<n-1;i++){
   const l=u[i]-u[i-1],r=u[i+1]-u[i];
   if(l*r<=0){if(l===0||r===0)ties++;continue;}
   const pick=Math.abs(l)<=Math.abs(r);if(Math.abs(l)===Math.abs(r))ties++;
   slope[i]=pick?l:r;weights[i]=pick?[[i,1],[i-1,-1]]:[[i+1,1],[i,-1]];branch[i]=pick?'left':'right';
  }
  const flux=[],ca=[],cb=[];
  for(let f=0;f<=n;f++){const L=f?u[f-1]+.5*slope[f-1]:left,R=f<n?u[f]-.5*slope[f]:right;flux[f]=.25*(L*L+R*R)-alpha/2*(R-L);ca[f]=L/2+alpha/2;cb[f]=R/2-alpha/2;}
  return {u,slope,weights,branch,ties,flux,ca,cb,L:u.map((_,i)=>(flux[i]-flux[i+1])/h)};
 }
 function jvp(t,v){const ds=t.weights.map(w=>w.reduce((s,[j,c])=>s+c*v[j],0)),df=[];for(let f=0;f<=n;f++)df[f]=t.ca[f]*(f?v[f-1]+.5*ds[f-1]:0)+t.cb[f]*(f<n?v[f]-.5*ds[f]:0);return v.map((_,i)=>(df[i]-df[i+1])/h);}
 function vjp(t,w){const y=zeros(n),sb=zeros(n);for(let f=0;f<=n;f++){const eta=((f<n?w[f]:0)-(f?w[f-1]:0))/h;if(f){const b=t.ca[f]*eta;y[f-1]+=b;sb[f-1]+=.5*b;}if(f<n){const b=t.cb[f]*eta;y[f]+=b;sb[f]-=.5*b;}}for(let i=0;i<n;i++)for(const [j,c] of t.weights[i])y[j]+=c*sb[i];return y;}
 function run(position=a,keep=true){let u=initial(position);const history=keep?[u.slice()]:[],tape=[],branches=[];let conservation=0;
  for(let k=0;k<steps;k++){
   const A=stage(u),y=u.map((v,i)=>v+dt*A.L[i]),B=stage(y),next=u.map((v,i)=>.5*v+.5*(y[i]+dt*B.L[i]));
   const dm=h*next.reduce((s,v,i)=>s+v-u[i],0),boundary=dt/2*(A.flux[0]-A.flux[n]+B.flux[0]-B.flux[n]);conservation=Math.max(conservation,Math.abs(dm-boundary));
   branches.push(A.branch.join(',')+'|'+B.branch.join(','));if(keep)tape.push({A,B});u=next;if(keep)history.push(u.slice());
  }
  const J=h*u.reduce((s,v,i)=>s+q(x[i])*G(v),0);return {u,J,tape,history,branches,conservation};
 }
 function derivatives(run){let v=initialDirection(),tangent=[v.slice()];
  for(const {A,B} of run.tape){const av=jvp(A,v),vy=v.map((z,i)=>z+dt*av[i]),bv=jvp(B,vy);v=v.map((z,i)=>.5*z+.5*(vy[i]+dt*bv[i]));tangent.push(v.slice());}
  let w=run.u.map((u,i)=>h*q(x[i])*Gp(u));const dual=Array(steps+1);dual[steps]=w.slice();
  for(let k=steps-1;k>=0;k--){const {A,B}=run.tape[k],bw=vjp(B,w),wy=w.map((z,i)=>.5*z+.5*dt*bw[i]),aw=vjp(A,wy);w=w.map((z,i)=>.5*z+wy[i]+dt*aw[i]);dual[k]=w.slice();}
  return {tangent,dual,forward:dot(run.u.map((u,i)=>h*q(x[i])*Gp(u)),v),adjoint:dot(w,initialDirection())};
 }
 const speed=(left+right)/2;
 function exact(xx,time=T,position=a){if(time===0)return xx<position?left:right;if(left>right)return xx<position+speed*time?left:right;const z=(xx-position)/time;return z<left?left:z>right?right:z;}
 function integral(f,lo,hi){if(hi<=lo)return 0;const c=(lo+hi)/2,r=(hi-lo)/2,z=Math.sqrt(3/5);return r*(5/9*f(c-r*z)+8/9*f(c)+5/9*f(c+r*z));}
 function exactIntegral(f,lo,hi,time=T,position=a){const breaks=[lo,hi,...(left>right||time===0?[position+speed*time]:[position+left*time,position+right*time])].filter(v=>v>=lo&&v<=hi).sort((a,b)=>a-b);let s=0;for(let i=1;i<breaks.length;i++)s+=integral(xx=>f(xx,exact(xx,time,position)),breaks[i-1],breaks[i]);return s;}
 const exactAverages=time=>x.map((_,i)=>exactIntegral((xx,u)=>u,i*h,(i+1)*h,time)/h);
 function referenceGradient(){if(left>right||T===0)return q(a+speed*T)*(G(left)-G(right));return -integral(xx=>q(xx)*Gp((xx-a)/T)/T,a+left*T,a+right*T);}
 function continuousInitialDual(xx){if(left<=right)return null;const lo=a+(speed-left)*T,hi=a+(speed-right)*T;if(xx<lo)return q(xx+left*T)*Gp(left);if(xx>hi)return q(xx+right*T)*Gp(right);return q(a+speed*T)*(G(left)-G(right))/(left-right);}
 return {n,T,a,kind,order,alpha,objective,h,x,dt,steps,left,right,speed,G,Gp,initial,initialDirection,stage,jvp,vjp,run,derivatives,exact,exactAverages,referenceGradient,continuousInitialDual,exactJ:()=>exactIntegral((xx,u)=>q(xx)*G(u),0,1),aligned:Number.isInteger(coordinate(a))};
}
const api={model,dot};if(typeof module!=='undefined')module.exports=api;else root.BurgersShock=api;
})(typeof window!=='undefined'?window:globalThis);

附录 C:显式矩阵与 LU 直接伴随求解

Appendix C: explicit matrices and direct LU adjoint solves

C.1 一阶和二阶都可以直接求解

C.1 Direct solves for both first and second order

本附录不采用 matrix-free 求解。先按顶端选定的一阶或二阶离散,显式组装完整 Jacobian A,再存储 AT 并进行带部分主元的 LU 分解,最后以前代和回代求出伴随。默认只有 8 个格心未知量或 9 个格点未知量;即使切换到 32 个网格区间,稠密直接法的成本也很小。正文的 matrix-free GS/GMRES 保留用于理解大型问题的求解方式。

This appendix does not use a matrix-free solve. Explicitly assemble the complete Jacobian A for the selected first- or second-order discretisation, store AT, factor it with partial-pivot LU, and compute the adjoint by forward/back substitution. The default grid has only eight cell or nine node unknowns; even 32 intervals remain a small dense problem. The main matrix-free GS/GMRES path remains available to explain methods for larger systems.

A=Ru(u∗,D),ATψ=−JuT,dJdD=JD+RDTψ.A=R_u(u^*,D),\qquad A^T\psi=-J_u^T,\qquad \frac{dJ}{dD}=J_D+R_D^T\psi.

C.2 按面显式组装完整矩阵

C.2 Explicit face-by-face matrix assembly

每个面先求左右重构权重和通量偏导 cL、cR,把 cL·左权重+cR·右权重加到左残差行、减到右残差行。二阶权重会延伸到更远的邻点,必须保留全部项。格点入口最后把第 0 行替换为 [1,0,…]。源项不依赖状态,因而不贡献 A;它仍通过 RDR_D 进入最终设计梯度。这里调用完整矩阵组装 matrix(u,false),不使用一阶预处理近似。

For each face, evaluate reconstruction weights and flux partials cL,cR. Add cL times left weights plus cR times right weights to the left residual row and subtract them from the right row. Retain all extended second-order neighbours. On the node grid, replace row zero with [1,0,…] after assembly. The state-independent source contributes no A entries but still enters the design gradient through RDR_D. Use the complete assembly matrix(u,false), not the first-order preconditioner.

A = zeros(n,n)
for face f:
    q = face_values(u,f)
    for (row,sign) in [(left,+1),(right,-1)]:
        if row exists:
            for (j,w) in left_reconstruction:  A[row,j] += sign*q.cL*w
            for (j,w) in right_reconstruction: A[row,j] += sign*q.cR*w
if node_centred: A[0,:] = [1,0,...]
M = transpose(A)
LU, permutation = factor_with_partial_pivoting(M)
psi = triangular_solve(LU, permutation, -J_u)
图 C.1 · 本页格心格式在 N = 8 时的精确 Aᵀ(收敛解处)。蓝色为正、红色为负,颜色深浅按绝对值;空白为零。(a) 一阶是三对角;(b) 二阶重构把带宽加到上下各 2。第 0 行与最后一行来自入口面和出口面的单边处理。(a) 一阶1.60−1.33−.241.60−1.36−.221.60−1.38−.191.60−1.41−.171.60−1.43−.141.60−1.46−.121.60−1.48−.101.50(b) 二阶1.05−.94.34.25.90−1.64.35.07.031.23−1.69.35.06.071.23−1.72.36.05.111.23−1.76.37.05.151.23−1.80.37.04.191.20−2.13.03.281.81
图 C.1 · 本页格心格式在 N = 8 时的精确 AT(收敛解处)。蓝色为正、红色为负,颜色深浅按绝对值;空白为零。(a) 一阶是三对角;(b) 二阶重构把带宽加到上下各 2。第 0 行与最后一行来自入口面和出口面的单边处理。
Figure C.1 · The exact Aᵀ of this page's cell scheme at N = 8 (at the converged state). Blue is positive and red negative, shaded by magnitude; blank entries are zero. (a) First order is tridiagonal; (b) second-order reconstruction widens the band to 2 on each side. The first and last rows come from the one-sided inlet and outlet treatment.(a) first order1.60−1.33−.241.60−1.36−.221.60−1.38−.191.60−1.41−.171.60−1.43−.141.60−1.46−.121.60−1.48−.101.50(b) second order1.05−.94.34.25.90−1.64.35.07.031.23−1.69.35.06.071.23−1.72.36.05.111.23−1.76.37.05.151.23−1.80.37.04.191.20−2.13.03.281.81
Figure C.1 · The exact AT of this page's cell scheme at N = 8 (at the converged state). Blue is positive and red negative, shaded by magnitude; blank entries are zero. (a) First order is tridiagonal; (b) second-order reconstruction widens the band to 2 on each side. The first and last rows come from the one-sided inlet and outlet treatment.

C.3 LU 分解、行置换、前代与回代

C.3 LU factorisation, permutations and triangular solves

ΠAT=LU,Ly=Π(−JuT),Uψ=y.\Pi A^T=LU,\quad Ly=\Pi(-J_u^T),\quad U\psi=y.

每列从剩余行中选绝对值最大的主元,交换整行,并记录置换 Π。消元乘数保存在严格下三角 L(其对角线为 1),更新后的上三角保存 U。整行交换包括先前已经存储的 L 乘数;右端项也必须采用同一个置换。遇到数值奇异主元时报告失败,不人为替换零主元。不形成逆矩阵。

Choose the largest-magnitude remaining pivot in each column, swap whole rows and record Π. Store elimination multipliers below the diagonal (L has unit diagonal) and U on/above it. Full row swaps include previously stored L multipliers. Apply the same permutation to the right-hand side. Report numerically singular pivots instead of replacing zeros arbitrarily. Do not form a matrix inverse.

ℓik=mik/mkk,mij←mij−ℓikmkj,yi=(Πr)i−∑j<iℓijyj,ψi=(yi−∑j>iuijψj)/uii.\ell_{ik}=m_{ik}/m_{kk},\quad m_{ij}\leftarrow m_{ij}-\ell_{ik}m_{kj},\quad y_i=(\Pi r)_i-\sum_{j<i}\ell_{ij}y_j,\quad \psi_i=(y_i-\sum_{j>i}u_{ij}\psi_j)/u_{ii}.

下面的网格值、精确矩阵、消元步骤、置换右端、前代结果 y 和伴随 ψ 均现场计算。切换顶端阶数、网格数或入口闭合后自动重新组装和分解。消元步骤不是外层迭代,不存在 GS 意义下的收敛历史。

The mesh values, exact matrix, elimination steps, permuted right-hand side, intermediate y and adjoint ψ are computed live. Changing order, grid size or inlet closure automatically reassembles and refactors. Elimination stages are not outer iterations and do not form a GS convergence history.

逐步推导:一次分解同时服务 tangent 与 adjoint

Step by step: one factorisation serves both the tangent and the adjoint

本页分解的是 ATA^T。如果改为分解 AA,同一组因子也能解伴随:由 ΠA=LU\Pi A=LU 得 AT=UTLTΠA^T=U^TL^T\Pi,其中 UTU^T 是下三角、LTL^T 是单位上三角,所以伴随只是把两次三角求解的顺序和方向换过来:

This page factors ATA^T. Had it factored AA, the same factors would also solve the adjoint: ΠA=LU\Pi A=LU gives AT=UTLTΠA^T=U^TL^T\Pi, where UTU^T is lower triangular and LTL^T unit upper triangular, so the adjoint merely swaps the order and direction of the two triangular solves:

Ly=Πb,Uv=y⏟tangent Av=bUTz=r,LTy=z,ψ=ΠTy⏟adjoint ATψ=r.\underbrace{Ly=\Pi b,\quad Uv=y}_{\text{tangent }Av=b}\qquad\qquad \underbrace{U^Tz=r,\quad L^Ty=z,\quad \psi=\Pi^Ty}_{\text{adjoint }A^T\psi=r}.

分解本身约占 23n3\tfrac23n^3 次运算,每次三角求解对只需约 2n22n^2 次。多个目标函数(多个右端)共用一个 AA 时,只分解一次。

The factorisation costs about 23n3\tfrac23n^3 operations and each pair of triangular solves only about 2n22n^2. Several objectives (several right-hand sides) sharing one AA need one factorisation.

图 C.2 · 带宽 p = q = 2 的矩阵在第 k 步消元。主元 mkk(圈)下方只有 p 个非零元,得到 p 个乘数 ℓik;它们只更新右下 p×q 个元素(阴影)。每步约 2pq 次运算,全程约 2npq 次,而稠密矩阵约 2n³/3 次。主元 mkk乘数 ℓik = mik/mkk(存入 L)更新 mij −= ℓik mkj(p×q 个)已完成的 L、U 部分尚未处理的带内元素带外元素始终为零:不交换行时不产生填充。
图 C.2 · 带宽 p = q = 2 的矩阵在第 k 步消元。主元 mkk(圈)下方只有 p 个非零元,得到 p 个乘数 ℓik;它们只更新右下 p×q 个元素(阴影)。每步约 2pq 次运算,全程约 2npq 次,而稠密矩阵约 2n³/3 次。
Figure C.2 · Elimination step k for a band with p = q = 2. Below the pivot mkk (circled) there are only p nonzeros, giving p multipliers ℓik; they update only the p×q block to the lower right (shaded). Each step costs about 2pq operations and the whole factorisation about 2npq, against about 2n³/3 for a dense matrix.pivot mkkmultipliers ℓik = mik/mkk (stored in L)update mij −= ℓik mkj (p×q entries)finished parts of L and Uband entries still to processEntries outside the band stay zero: no fill without row swaps.
Figure C.2 · Elimination step k for a band with p = q = 2. Below the pivot mkk (circled) there are only p nonzeros, giving p multipliers ℓik; they update only the p×q block to the lower right (shaded). Each step costs about 2pq operations and the whole factorisation about 2npq, against about 2n³/3 for a dense matrix.

带状矩阵:本页的消元没有交换行,也没有填充

Banded matrices: on this page elimination swaps no rows and creates no fill

设 ATA^T 的下、上带宽为 pp、qq(一阶 p=q=1p=q=1,二阶 p=q=2p=q=2)。第 kk 步只有 pp 个乘数 ℓik=mik/mkk\ell_{ik}=m_{ik}/m_{kk} 非零,更新只触及 p×qp\times q 个元素,所以全程约 2npq2npq 次运算。部分主元若交换行,UU 的上带宽最多变为 p+qp+q。对本页的矩阵(格心,一阶与二阶,N=8N=8 与 3232),构建时的分解一次也没有交换行,LL 与 UU 的带宽与 ATA^T 相同,填充为零。这是对这些矩阵的计算结果,不是一般保证。

Let ATA^T have lower and upper bandwidths pp and qq (p=q=1p=q=1 at first order, p=q=2p=q=2 at second order). Step kk has only pp nonzero multipliers ℓik=mik/mkk\ell_{ik}=m_{ik}/m_{kk} and updates only p×qp\times q entries, so the whole factorisation costs about 2npq2npq operations. If partial pivoting swaps rows, the upper bandwidth of UU can grow to p+qp+q. For this page's matrices (cell, first and second order, N=8N=8 and 3232) the build-time factorisation never swapped a row: LL and UU keep the bandwidths of ATA^T and the fill is zero. This is a computed fact about these matrices, not a general guarantee.

C.4 小网格的速度、验证和适用范围

C.4 Small-grid performance, verification and scope

这里直接法求解阶段完全不调用 matrix-free 算子;求解完成后才用正文独立的反向算子检查真残差 ATψ+JuTJ_u^T。同时比较 GS、GMRES 的伴随和全部设计梯度。计时包含直接法的精确矩阵组装、转置、LU 和三角求解;迭代法包含低阶预处理组装及求解。共享已收敛流场,不计原始求解和绘图,每种方法预热后测 7 次取中位数。

The direct solve itself never calls a matrix-free operator. Only afterwards does the main reverse operator independently check ATψ+JuTJ_u^T. Compare adjoints and all gradients with GS and GMRES. Timing includes exact assembly, transpose, LU and triangular solves for the direct method, and low-order setup plus solution for iterative methods. Share the converged flow, exclude primal solves and rendering, warm up each method and report the median of seven runs.

η=∥ATψ+JuT∥∞∥AT∥∞∥ψ∥∞+∥JuT∥∞.\eta=\frac{\|A^T\psi+J_u^T\|_\infty}{\|A^T\|_\infty\|\psi\|_\infty+\|J_u^T\|_\infty}.

稠密存储为 O(n²)、LU 分解为 O(n³)、复用分解后每个新右端为 O(n²)。因此小系统直接法通常很快,但应以实测结果判断;计时分辨率、浏览器和求解器参数都会影响排名。若改变目标但保持同一个 A,可以复用 LU;若改变离散阶数或状态则必须重分解。工业大规模非结构网格系统需要考虑稀疏填充和内存,不能照搬稠密直接法。小后向误差也不等于病态系统具有小前向误差。

Dense storage costs O(n²), LU O(n³), and each new right-hand side with reused factors O(n²). Direct methods are often fast here, but the live measurements determine the ranking, which depends on timing resolution, browser and solver settings. Reuse LU for a new objective with unchanged A; refactor when order or state changes. Large industrial unstructured systems require attention to sparse fill and memory. Small backward error also does not guarantee small forward error in an ill-conditioned system.

逐步推导:后向误差为什么能换算成前向误差

Step by step: turning the backward error into a forward error

Rigal–Gaches 定理说明,上式的 η\eta 恰好是使 ψ\psi 成为某个扰动系统精确解的最小相对扰动:存在 EE、ff,

The Rigal–Gaches theorem says that η\eta above is exactly the smallest relative perturbation for which ψ\psi solves a perturbed system: there are EE and ff with

(AT+E)ψ=b+f,∥E∥≤η∥AT∥,∥f∥≤η∥b∥,b=−JuT.(A^T+E)\psi=b+f,\qquad \|E\|\le\eta\|A^T\|,\quad \|f\|\le\eta\|b\|,\qquad b=-J_u^T.

与精确解 ATψ∗=bA^T\psi^*=b 相减,得 AT(ψ−ψ∗)=f−EψA^T(\psi-\psi^*)=f-E\psi。两边乘 A−TA^{-T} 并取范数:

Subtracting the exact equation ATψ∗=bA^T\psi^*=b gives AT(ψ−ψ∗)=f−EψA^T(\psi-\psi^*)=f-E\psi. Multiply by A−TA^{-T} and take norms:

∥ψ−ψ∗∥∥ψ∥≤∥A−T∥ η(∥AT∥+∥b∥∥ψ∥)=κ(AT) η(1+∥b∥∥AT∥∥ψ∥)≲2 κ(AT) η.\frac{\|\psi-\psi^*\|}{\|\psi\|}\le\|A^{-T}\|\,\eta\Bigl(\|A^T\|+\frac{\|b\|}{\|\psi\|}\Bigr)=\kappa(A^T)\,\eta\Bigl(1+\frac{\|b\|}{\|A^T\|\|\psi\|}\Bigr)\lesssim2\,\kappa(A^T)\,\eta.

最后一步用了 ∥b∥=∥ATψ∗∥≤∥AT∥∥ψ∗∥≈∥AT∥∥ψ∥\|b\|=\|A^T\psi^*\|\le\|A^T\|\|\psi^*\|\approx\|A^T\|\|\psi\|。本页默认网格(格心,N=8N=8):一阶 κ∞(AT)=17.3\kappa_\infty(A^T)=17.3、η=5.0×10−17\eta=5.0\times10^{-17};二阶 κ∞(AT)=24.0\kappa_\infty(A^T)=24.0、η=6.5×10−17\eta=6.5\times10^{-17}。所以伴随的相对前向误差不超过约 3.1×10−153.1\times10^{-15}。条件数随网格加密增大(N=32N=32 时分别为 78 与 115),小后向误差并不自动意味着小前向误差。

The last step used ∥b∥=∥ATψ∗∥≤∥AT∥∥ψ∗∥≈∥AT∥∥ψ∥\|b\|=\|A^T\psi^*\|\le\|A^T\|\|\psi^*\|\approx\|A^T\|\|\psi\|. On this page's default mesh (cell, N=8N=8): first order κ∞(AT)=17.3\kappa_\infty(A^T)=17.3 and η=5.0×10−17\eta=5.0\times10^{-17}; second order κ∞(AT)=24.0\kappa_\infty(A^T)=24.0 and η=6.5×10−17\eta=6.5\times10^{-17}. The relative forward error of the adjoint is therefore at most about 3.1×10−153.1\times10^{-15}. The condition number grows under refinement (78 and 115 at N=32N=32), so a small backward error does not by itself mean a small forward error.

完整直接法实现Complete direct implementation
/* Explicit Burgers Jacobian assembly and partial-pivot LU: no matrix-free solve. */
(function(root){'use strict';const K=typeof module!=='undefined'?require('./core_1d'):root.FV1;
function factor(M,trace=false){const n=M.length,lu=M.map(r=>r.slice()),permutation=Array.from({length:n},(_,i)=>i),steps=[],scale=Math.max(...M.flat().map(Math.abs));if(!scale||!Number.isFinite(scale))throw Error('Invalid or zero matrix');for(let k=0;k<n;k++){let pivot=k;for(let i=k+1;i<n;i++)if(Math.abs(lu[i][k])>Math.abs(lu[pivot][k]))pivot=i;if(Math.abs(lu[pivot][k])<=Number.EPSILON*scale*n)throw Error('Numerically singular LU pivot at '+k);[lu[k],lu[pivot]]=[lu[pivot],lu[k]];[permutation[k],permutation[pivot]]=[permutation[pivot],permutation[k]];for(let i=k+1;i<n;i++){lu[i][k]/=lu[k][k];for(let j=k+1;j<n;j++)lu[i][j]-=lu[i][k]*lu[k][j];}if(trace)steps.push({k,pivot,lu:lu.map(r=>r.slice()),permutation:permutation.slice()});}return {lu,permutation,steps};}
function solve(F,b){const {lu,permutation}=F,n=b.length,y=permutation.map(i=>b[i]),x=Array(n).fill(0);for(let i=0;i<n;i++)for(let j=0;j<i;j++)y[i]-=lu[i][j]*y[j];for(let i=n-1;i>=0;i--){let r=y[i];for(let j=i+1;j<n;j++)r-=lu[i][j]*x[j];x[i]=r/lu[i][i];}return {x,y,permutedRhs:permutation.map(i=>b[i])};}
const transpose=A=>A[0].map((_,j)=>A.map(r=>r[j]));

function adjoint(F,u,trace=false){const A=Array.from({length:F.N},()=>Array(F.N).fill(0));F.matrix(u,false).forEach((row,i)=>row.forEach(([j,v])=>A[i][j]=v));const AT=transpose(A),rhs=F.dJdu(u).map(v=>-v),fac=factor(AT,trace),s=solve(fac,rhs),res=F.vjp(u,s.x).map((v,i)=>v-rhs[i]),max=a=>Math.max(...a.map(Math.abs)),den=Math.max(...AT.map(r=>r.reduce((s,v)=>s+Math.abs(v),0)))*max(s.x)+max(rhs);return {...s,A,AT,rhs,fac,residual:K.norm(res),backwardError:den?max(res)/den:0,gradient:F.gradient(u,s.x)};}
const api={factor,solve,transpose,adjoint};if(typeof module!=='undefined')module.exports=api;else root.BDirect=api;
})(typeof window!=='undefined'?window:null);

附录 D:光滑性、limiter 与求导收敛

Appendix D: smoothness, limiters and derivative convergence

先分辨四种不同问题:原始残差是否收敛、经典导数是否存在、固定线性系统是否解收敛、网格加密后是否逼近物理敏感度。实验不预设硬 limiter 必然发散,也不把光滑 limiter 当作收敛保证。

Separate four questions: convergence of the primal residual, existence of a classical derivative, convergence of a fixed linear solve, and approximation of physical sensitivities under refinement. A hard limiter need not diverge, and a smooth limiter does not guarantee convergence.

本附录独立于正文无黏源项模型,使用自己的参数和单元平均网格。格点页也显示相同的有限体积参考实验;不能把它误认为格点格式的验证。

This appendix has its own controls and cell-average grid, independent of the main inviscid source model. The node page displays the same finite-volume reference experiment; it is not validation of the node scheme.

D.1 光滑的线性分布,也会触及 minmod 切换

D.1 Even a smooth linear profile can reach a minmod switch

ui−1=uc−a,ui=uc+μ,ui+1=uc+a,si=minmod⁡(a+μ,a−μ)=a−∣μ∣(∣μ∣<a).u_{i-1}=u_c-a,\quad u_i=u_c+\mu,\quad u_{i+1}=u_c+a,\qquad s_i=\operatorname{minmod}(a+\mu,a-\mu)=a-|\mu|\quad(|\mu|<a).

μ=0 的左右导数分别为 +1 与 −1。程序相等时选择左分支,只给出该分支的线性化。中心差分得到零不代表导数存在。以下交互同时列出切换条件、局部 tangent 和反向累加权重。

At μ=0 the left and right derivatives are +1 and −1. The code selects the left branch at a tie, defining that branch’s linearization only. A zero central difference does not establish a derivative. Inspect the switch, local tangent and reverse accumulation weights below.

D.2 完整导数与冻结系数究竟差在哪里

D.2 Full derivatives versus frozen coefficients

uf=ui+ϕiΔi,u˙f=u˙i+ϕiΔ˙i+Δiϕ˙i.u_f=u_i+\phi_i\Delta_i,\qquad \dot u_f=\dot u_i+\phi_i\dot\Delta_i+\Delta_i\dot\phi_i.

冻结 limiter 系数删除最后一项;保存 minmod 的分支仍应对分支内部运算完整求导。两者都可能通过各自的转置点积测试,但冻结系数一般不是原始残差的完整导数。为了明确对比,冻结模式保持基态重构值相同,只替换线性化;它没有重新定义一个全局相同的可微残差。

Freezing the coefficient drops the final term. Saving a minmod branch still differentiates all operations within that branch. Both maps may pass their own transpose test, while coefficient freezing generally is not the full residual derivative. The frozen mode preserves the base reconstruction value and replaces only its linearization; it does not define the same globally differentiable residual.

sε(a,b)=ab(a+b)a2+b2+ε2,ε>0.s_\varepsilon(a,b)=\frac{ab(a+b)}{a^2+b^2+\varepsilon^2},\qquad \varepsilon>0.

光滑对照采用上述有理函数,对全部实数输入光滑;不再附加硬 min/max 或符号截断。它保持常值状态,但不是本页证明过的 TVD 或正性保持格式。ε 使用无量纲变量差分的尺度,改变 ε 就改变离散模型。Euler 若产生非正密度或压力、或超过固定耗散波速上界,将拒绝该状态而不静默裁剪。

The rational comparison is smooth for all real inputs, with no added hard min/max or sign clipping. It preserves constant states, but is not claimed to be TVD or positivity preserving. Epsilon uses the scale of nondimensional variable differences; changing it changes the discretization. Euler rejects nonpositive states or states beyond its fixed dissipation-speed bound rather than silently clipping them.

图 D.1 · 线性分布 uᵢ₋₁ = uc − a、uᵢ = uc + μ、uᵢ₊₁ = uc + a(a = 1)上的限幅斜率随 μ 的变化。minmod(a+μ, a−μ) 是帐篷形:μ = 0 处有尖点(左右导数 +1、−1),|μ| ≥ a 时两差异号,斜率为 0。有理对照 sε 处处光滑,μ = 0 处导数为 0;|μ| > a 时它变为负值,所以它不是 minmod 的光滑近似,而是另一个离散模型。−1.0−0.50.00.51.0−3−2−10123μminmod(a+μ, a−μ)sε, ε = 0.01sε, ε = 0.5斜率 +1斜率 −1两差异号:sε < 0
图 D.1 · 线性分布 ui−1 = uc − a、ui = uc + μ、ui+1 = uc + a(a = 1)上的限幅斜率随 μ 的变化。minmod(a+μ, a−μ) 是帐篷形:μ = 0 处有尖点(左右导数 +1、−1),|μ| ≥ a 时两差异号,斜率为 0。有理对照 sε 处处光滑,μ = 0 处导数为 0;|μ| > a 时它变为负值,所以它不是 minmod 的光滑近似,而是另一个离散模型。
Figure D.1 · Limited slope on the linear profile uᵢ₋₁ = uc − a, uᵢ = uc + μ, uᵢ₊₁ = uc + a (a = 1) as μ varies. minmod(a+μ, a−μ) is a tent: a kink at μ = 0 (one-sided derivatives +1 and −1), and slope 0 once |μ| ≥ a, where the two differences change sign. The rational comparison sε is smooth everywhere, with zero derivative at μ = 0; for |μ| > a it turns negative, so it is not a smooth approximation of minmod but a different discrete model.−1.0−0.50.00.51.0−3−2−10123μminmod(a+μ, a−μ)sε, ε = 0.01sε, ε = 0.5slope +1slope −1opposite signs: sε < 0
Figure D.1 · Limited slope on the linear profile ui−1 = uc − a, ui = uc + μ, ui+1 = uc + a (a = 1) as μ varies. minmod(a+μ, a−μ) is a tent: a kink at μ = 0 (one-sided derivatives +1 and −1), and slope 0 once |μ| ≥ a, where the two differences change sign. The rational comparison sε is smooth everywhere, with zero derivative at μ = 0; for |μ| > a it turns negative, so it is not a smooth approximation of minmod but a different discrete model.

逐步推导:minmod 的单侧导数与有理限幅的导数

Step by step: the one-sided derivatives of minmod and the derivatives of the rational limiter

在 D.1 的线性分布上,两侧差为 a+μa+\mu 与 a−μa-\mu。∣μ∣<a|\mu|<a 时二者同号,minmod 取绝对值较小者:

On the linear profile of D.1 the two differences are a+μa+\mu and a−μa-\mu. For ∣μ∣<a|\mu|<a they have the same sign and minmod takes the smaller magnitude:

s(μ)=a−∣μ∣,s(τ)−s(−τ)2τ=0,lim⁡τ→0+s(τ)−s(0)τ=−1,lim⁡τ→0+s(0)−s(−τ)τ=+1.\begin{gathered}s(\mu)=a-|\mu|,\qquad \frac{s(\tau)-s(-\tau)}{2\tau}=0,\\\lim_{\tau\to0^+}\frac{s(\tau)-s(0)}{\tau}=-1,\qquad \lim_{\tau\to0^+}\frac{s(0)-s(-\tau)}{\tau}=+1.\end{gathered}

中心差分为零,却既不是左导数也不是右导数;程序在相等时选左分支 d=a+μd=a+\mu,返回的线性化是 +1+1。有理对照把两个差 aa、bb 放进一个商,求导用商法则:

The central difference is zero, yet it is neither one-sided derivative; the program picks the left branch d=a+μd=a+\mu at the tie and returns the linearisation +1+1. The rational comparison puts the two differences aa, bb into one quotient, differentiated by the quotient rule:

sε=Na2+b2+ε2,N=ab(a+b),∂sε∂a=b(2a+b)(a2+b2+ε2)−2a⋅ab(a+b)(a2+b2+ε2)2,∂sε∂b=a(a+2b)(a2+b2+ε2)−2b⋅ab(a+b)(a2+b2+ε2)2.\begin{gathered}s_\varepsilon=\frac{N}{a^2+b^2+\varepsilon^2},\qquad N=ab(a+b),\\\frac{\partial s_\varepsilon}{\partial a}=\frac{b(2a+b)(a^2+b^2+\varepsilon^2)-2a\cdot ab(a+b)}{(a^2+b^2+\varepsilon^2)^2},\\\frac{\partial s_\varepsilon}{\partial b}=\frac{a(a+2b)(a^2+b^2+\varepsilon^2)-2b\cdot ab(a+b)}{(a^2+b^2+\varepsilon^2)^2}.\end{gathered}

在相等点 a=b=ma=b=m,两个偏导相同:(2m4+3m2ε2)/(2m2+ε2)2(2m^4+3m^2\varepsilon^2)/(2m^2+\varepsilon^2)^2,ε→0\varepsilon\to0 时趋于 12\tfrac12,也就是中心平均的导数。本页默认 ε=0.01\varepsilon=0.01、m=1m=1 时为 0.500020.50002。沿 D.1 的切片(两差为 a0+μa_0+\mu、a0−μa_0-\mu),dsε/dμ=∂asε−∂bsεds_\varepsilon/d\mu=\partial_as_\varepsilon-\partial_bs_\varepsilon 在 μ=0\mu=0 按对称性为零:有理函数没有尖点。minmod 在同一点的所选分支导数是 (∂a,∂b)=(1,0)(\partial_a,\partial_b)=(1,0),与 (12,12)(\tfrac12,\tfrac12) 不同:两个模型在光滑单调区域也给出不同的线性化。

At the tie a=b=ma=b=m the two partials coincide: (2m4+3m2ε2)/(2m2+ε2)2(2m^4+3m^2\varepsilon^2)/(2m^2+\varepsilon^2)^2, which tends to 12\tfrac12, the derivative of the central average, as ε→0\varepsilon\to0. With this page's default ε=0.01\varepsilon=0.01 and m=1m=1 it equals 0.500020.50002. Along the slice of D.1 (differences a0+μa_0+\mu and a0−μa_0-\mu), dsε/dμ=∂asε−∂bsεds_\varepsilon/d\mu=\partial_as_\varepsilon-\partial_bs_\varepsilon vanishes at μ=0\mu=0 by symmetry: the rational function has no kink. The selected-branch derivative of minmod at the same point is (∂a,∂b)=(1,0)(\partial_a,\partial_b)=(1,0), not (12,12)(\tfrac12,\tfrac12): the two models linearise differently even in a smooth monotone region.

D.3 无激波稳态 Burgers:分支、矩阵和误差

D.3 Smooth steady Burgers: branches, matrices and errors

(12u2−νux)x=s(x,a),u∗(x)=1+asin⁡(2π(x−0.07)),a=0.2.\left(\tfrac12u^2-\nu u_x\right)_x=s(x,a),\quad u_*(x)=1+a\sin(2\pi(x-0.07)),\quad a=0.2.

两端给定解析 Dirichlet 值,源项积分由解析通量端点差计算。每次参数扰动都同时改变源项和边界,但数值未知量仍由残差求解,未填入解析解。设计变量为振幅 a,目标 J=hΣ(1+xi)ui。一阶与二阶使用相同 Rusanov 波速 2 和中心黏性通量;边界扩散距离为 h/2,首末单元不重构。

Both ends use analytic Dirichlet data; integrated sources are analytic physical-flux differences. Perturbations change sources and boundaries together, but unknowns are solved from residuals, never filled with the analytic profile. The design variable is amplitude a and J=hΣ(1+xi)ui. Both orders use Rusanov speed 2 and centred viscous fluxes; boundary diffusion distances are h/2, with no endpoint-cell reconstruction.

U = sampled reference as an initial guess only
repeat until ||R(U,a)|| is small:
    A = exact selected-branch Jacobian
    d = LU(A, -R)                 # small scalar experiment
    line-search U + step*d        # reject failed convergence
A v = -R_a;  A^T psi = -J_U^T
g = J_a + psi^T R_a
compare full-rerun FD, branch changes and LU/GS/GMRES

本标量实验用小矩阵 LU 及回溯得到原始状态,以隔离导数线性系统的求解困难。随后在同一个固定状态上比较 GS 缺陷校正、GS 预处理 GMRES 与 LU。精确算子通过计算图 JVP/VJP;一阶矩阵仅构造预处理,伴随使用整个预处理映射的转置。

This scalar experiment uses small-matrix LU and backtracking for the primal, isolating difficulty in derivative linear solves. At the same fixed state, compare GS defect correction, GS-preconditioned GMRES and LU. Exact actions use graph JVP/VJP; a first-order matrix builds the preconditioner, whose entire map is transposed for the adjoint.

Av=−Ra,ATψ=−JUT,ek+1=(I−ωBA)ek.A v=-R_a,\quad A^T\psi=-J_U^T,\quad e^{k+1}=(I-\omega BA)e^k.

GS 发散而 LU 后向残差很小,不能解释成“导数不存在”。反过来,求解器收敛也不能证明 minmod 切换点存在唯一导数。下方显示线性真残差、无穷范数条件数、迭代矩阵范数、原始分支变化和完整重算差分。没有计算精确谱半径:范数大于 1 不是发散证明。

GS divergence with a small LU backward residual does not establish nonexistence of a derivative. Conversely, linear convergence does not establish a unique derivative at a minmod switch. Inspect true residuals, infinity-norm conditioning, iteration-matrix norm, primal branch changes and full-rerun differences. Exact spectral radii are not computed: a norm above one is not proof of divergence.

D.4 固定物理时间:峰值增长还是求导失稳

D.4 Fixed physical time: concentration or derivative instability

ut+(u2/2)x=νuxx,u(x,0)=12[1−tanh⁡(x−aδ)].u_t+(u^2/2)_x=\nu u_{xx},\qquad u(x,0)=\tfrac12\left[1-\tanh\left(\frac{x-a}{\delta}\right)\right].

独立非定常实验使用初值的精确单元平均,左/右外侧状态为 1/0,T=0.12。选择 ν、初始过渡层宽度 δ、网格及 limiter;只对位置 a=0.4 求导。SSP-RK2 的时间步数由网格、固定波速 1.5 和黏性稳定性上界确定,在位置差分中保持不变。所有 RK 子阶段的完整导数都进入 tangent 与反向时间 adjoint。

The independent transient experiment uses exact initial cell averages, exterior states 1/0 and T=0.12. Choose viscosity, initial width, grid and limiter; differentiate only position a=0.4. SSP-RK2 step count uses the mesh, fixed speed 1.5 and a viscous stability bound, and remains unchanged in position perturbations. Both RK stages contribute fully to the tangent and reverse-time adjoint.

U˙n+1=Φn,UU˙n,Uˉn=Φn,UTUˉn+1,UˉN=JUNT(vn≡U˙n).\dot U^{n+1}=\Phi_{n,U}\dot U^n,\qquad \bar U^n=\Phi_{n,U}^T\bar U^{n+1},\qquad \bar U^N=J_{U^N}^T\qquad(v^n\equiv\dot U^n).

黏性 ν 控制 PDE,ε 控制重构映射,δ 控制初始层宽度,三者不能混为一谈。先固定 ν、δ 加密,再改变 ν 或 δ。图同时显示 max|v|、hΣ|v| 和积分梯度;对于移动间断的无黏极限,tangent 可以集中为分布,峰值增大而积分敏感度仍有限。这里不给一般激波的连续伴随收敛保证。

Viscosity changes the PDE, epsilon changes reconstruction, and delta changes initial thickness. First refine at fixed viscosity and width, then vary one parameter. Compare max|v|, hΣ|v| and integral gradients. A moving discontinuity’s inviscid-limit tangent can concentrate as a distribution while integrated sensitivity stays finite. No general continuous-adjoint convergence guarantee is asserted.

逐步推导:初值导数为什么越来越尖

Step by step: why the initial derivative sharpens

对初值直接求导,tanh⁡\tanh 的导数是 sech⁡2\operatorname{sech}^2:

Differentiate the initial data directly; the derivative of tanh⁡\tanh is sech⁡2\operatorname{sech}^2:

∂u(x,0)∂a=12δsech⁡2(x−aδ),max⁡x∂u∂a=12δ,∫−∞∞∂u∂a dx=12[tanh⁡x−aδ]−∞∞=1.\begin{gathered}\frac{\partial u(x,0)}{\partial a}=\frac1{2\delta}\operatorname{sech}^2\Bigl(\frac{x-a}\delta\Bigr),\qquad \max_x\frac{\partial u}{\partial a}=\frac1{2\delta},\\\int_{-\infty}^{\infty}\frac{\partial u}{\partial a}\,dx=\frac12\Bigl[\tanh\frac{x-a}\delta\Bigr]_{-\infty}^{\infty}=1.\end{gathered}

峰值与 1/δ1/\delta 成正比,面积却恒为 uL−uR=1u_L-u_R=1:δ→0\delta\to0 时导数趋于 [u]δ(x−a)[u]\delta(x-a)。离散初值用精确单元平均,单元 ii 的导数是 [tanh⁡((x−a)/δ)]xi−1/2xi+1/2/(2h)\bigl[\tanh((x-a)/\delta)\bigr]_{x_{i-1/2}}^{x_{i+1/2}}/(2h),不超过 1/h1/h:峰值由 min⁡(1/(2δ),1/h)\min(1/(2\delta),1/h) 决定。所以“最大值随加密增大”是分布型导数的正常表现;应当检查的是积分量(例如 dJ/dadJ/da)是否收敛。

The peak is proportional to 1/δ1/\delta while the area stays uL−uR=1u_L-u_R=1: as δ→0\delta\to0 the derivative tends to [u]δ(x−a)[u]\delta(x-a). The discrete initial data use exact cell averages, whose derivative in cell ii is [tanh⁡((x−a)/δ)]xi−1/2xi+1/2/(2h)\bigl[\tanh((x-a)/\delta)\bigr]_{x_{i-1/2}}^{x_{i+1/2}}/(2h), at most 1/h1/h: the peak is set by min⁡(1/(2δ),1/h)\min(1/(2\delta),1/h). A maximum that grows under refinement is therefore normal for a distributional derivative; what must converge are integrated quantities such as dJ/dadJ/da.

图 D.2 · 初值 u(x,0) = ½[1 − tanh((x − a)/δ)] 对位置 a 的导数,δ = 0.05、0.025、0.0125。峰值 1/(2δ) 随 δ 减小而增大,曲线下面积始终是 uL − uR = 1:极限是分布 [u]δ(x − a),与附录 B 的激波位移导数相同。0102030400.20.30.40.50.6x∂u(x,0)/∂aδ = 0.05(峰值 10)δ = 0.025(峰值 20)δ = 0.0125(峰值 40)
图 D.2 · 初值 u(x,0) = ½[1 − tanh((x − a)/δ)] 对位置 a 的导数,δ = 0.05、0.025、0.0125。峰值 1/(2δ) 随 δ 减小而增大,曲线下面积始终是 uL − uR = 1:极限是分布 [u]δ(x − a),与附录 B 的激波位移导数相同。
Figure D.2 · Derivative of the initial data u(x,0) = ½[1 − tanh((x − a)/δ)] with respect to the position a, for δ = 0.05, 0.025, 0.0125. The peak 1/(2δ) grows as δ shrinks while the area under each curve stays uL − uR = 1: the limit is the distribution [u]δ(x − a), the same as the shock-displacement derivative of Appendix B.0102030400.20.30.40.50.6x∂u(x,0)/∂aδ = 0.05 (peak 10)δ = 0.025 (peak 20)δ = 0.0125 (peak 40)
Figure D.2 · Derivative of the initial data u(x,0) = ½[1 − tanh((x − a)/δ)] with respect to the position a, for δ = 0.05, 0.025, 0.0125. The peak 1/(2δ) grows as δ shrinks while the area under each curve stays uL − uR = 1: the limit is the distribution [u]δ(x − a), the same as the shock-displacement derivative of Appendix B.

D.5 阅读结果的顺序

D.5 How to interpret results

先检查原始真残差与状态有效性,再判断 limiter 分支和单侧导数,再比较同一个线性系统的 GS/GMRES/LU,最后进行网格加密及连续参考比较。两种转置矩阵有相同的二范数条件数;迭代差异要从预处理、右端项、非正规增长及停止标准解释,不能笼统归因于“伴随更不稳定”。

Check primal residual and admissibility first, then limiter branches and one-sided derivatives, then GS/GMRES/LU on the same system, and finally refinement against continuous references. A matrix and its transpose have the same 2-norm condition number; iteration differences require examining preconditioners, right-hand sides, nonnormal growth and stopping criteria, not a blanket claim that adjoints are less stable.

查看完整独立标量实验与线性诊断实现Complete scalar experiment and linear diagnostics
/* Small educational systems: matrix-free exact products; explicit matrices only for LU/conditioning diagnostics. */
(function(root){'use strict';
const Q=typeof module!=='undefined'?require('../q1d/reconstruction/math'):root.QMath;
const {norm,dot}=Q,zeros=n=>Array(n).fill(0),transpose=A=>A[0].map((_,j)=>A.map(r=>r[j])),mv=(A,x)=>A.map(r=>dot(r,x));
function factor(A){const n=A.length,lu=A.map(r=>r.slice()),p=Array.from({length:n},(_,i)=>i),scale=Math.max(...A.flat().map(Math.abs));for(let k=0;k<n;k++){let j=k;for(let i=k+1;i<n;i++)if(Math.abs(lu[i][k])>Math.abs(lu[j][k]))j=i;if(!Number.isFinite(lu[j][k])||Math.abs(lu[j][k])<1e-14*scale)throw Error('Singular LU pivot '+k);[lu[j],lu[k]]=[lu[k],lu[j]];[p[j],p[k]]=[p[k],p[j]];for(let i=k+1;i<n;i++){lu[i][k]/=lu[k][k];for(let j=k+1;j<n;j++)lu[i][j]-=lu[i][k]*lu[k][j];}}return {lu,p};}
function solve(F,b){const {lu,p}=F,n=b.length,x=p.map(i=>b[i]);for(let i=0;i<n;i++)for(let j=0;j<i;j++)x[i]-=lu[i][j]*x[j];for(let i=n-1;i>=0;i--){for(let j=i+1;j<n;j++)x[i]-=lu[i][j]*x[j];x[i]/=lu[i][i];}return x;}
function assemble(L,n){const A=Array.from({length:n},()=>zeros(n));for(let j=0;j<n;j++){const e=zeros(n);e[j]=1;L.jvp(e).R.forEach((v,i)=>A[i][j]=v);}return A;}
function limiter(a,b,kind='minmod',eps=.01){if(kind==='first')return {s:0,da:0,db:0,branch:'first',margin:1};if(kind==='none')return {s:(a+b)/2,da:.5,db:.5,branch:'unlimited',margin:1};if(kind==='smooth'){const den=a*a+b*b+eps*eps,num=a*b*(a+b);return {s:num/den,da:((2*a*b+b*b)*den-2*a*num)/(den*den),db:((a*a+2*a*b)*den-2*b*num)/(den*den),branch:'smooth',margin:1};}const margin=Math.min(Math.abs(a),Math.abs(b),Math.abs(Math.abs(a)-Math.abs(b)));if(a*b<=0)return {s:0,da:0,db:0,branch:'zero',margin};return Math.abs(a)<=Math.abs(b)?{s:a,da:1,db:0,branch:'left',margin}:{s:b,da:0,db:1,branch:'right',margin};}
function slope(o,a,b,kind,eps,mode='full'){const r=limiter(o.value(a),o.value(b),kind,eps),{c,add,mul,sub,div}=o;if(mode==='frozen'){const d=(o.value(a)+o.value(b))/2,phi=Math.abs(d)>1e-15?r.s/d:0;return add(c(r.s),mul(c(phi/2),add(sub(a,c(o.value(a))),sub(b,c(o.value(b))))));}if(kind==='smooth'){const den=add(add(mul(a,a),mul(b,b)),c(eps*eps));return div(mul(mul(a,b),add(a,b)),den);}return add(c(r.s),add(mul(c(r.da),sub(a,c(o.value(a)))),mul(c(r.db),sub(b,c(o.value(b))))));}
function operators(U,p,evalFn,mode='full'){const t=Q.tape();t.op.value=i=>t.val[i];const ui=U.map(t.op.input),pi=p.map(t.op.input),r=evalFn(t.op,ui,pi,mode);return {R:r.R.map(i=>t.val[i]),J:t.val[r.J],jvp:(v,dp=zeros(p.length))=>{const z=t.forward([...v,...dp]);return {R:r.R.map(i=>z[i]),J:z[r.J]};},vjp:(w,j=0)=>{const z=t.reverse([...r.R,r.J],[...w,j]);return {U:z.slice(0,U.length),p:z.slice(U.length)};}};}
const real={...Q.real,value:x=>x};
function gsMatrix(P,sweeps=2){const n=P.length,B=Array.from({length:n},()=>zeros(n));for(let k=0;k<n;k++){const b=zeros(n),x=zeros(n);b[k]=1;for(let s=0;s<sweeps;s++)for(let i=0;i<n;i++){let v=b[i];for(let j=0;j<n;j++)if(i!==j)v-=P[i][j]*x[j];if(Math.abs(P[i][i])<1e-14)throw Error('Zero GS diagonal');x[i]=v/P[i][i];}x.forEach((v,i)=>B[i][k]=v);}return B;}
function compare(L,P,rhs,adj=false,omega=.7,preAction=null){const n=rhs.length,A=assemble(L,n),AA=adj?transpose(A):A,F=factor(AA),exact=solve(F,rhs),B0=preAction?transpose(Array.from({length:n},(_,j)=>{const e=zeros(n);e[j]=1;return preAction(e);})):gsMatrix(P),B=adj?transpose(B0):B0,op=v=>adj?L.vjp(v).U:L.jvp(v).R,pre=v=>mv(B,v),gm=Q.gmres(op,rhs,pre,1e-11,n);let x=zeros(n),history=[],reason='limit';for(let k=0;k<250;k++){const ax=op(x),r=rhs.map((b,i)=>b-ax[i]),rn=norm(r);history.push(rn);if(rn<1e-10){reason='converged';break;}if(!Number.isFinite(rn)||rn>Math.max(1,norm(rhs))*1e8){reason='growth';break;}const d=pre(r);x=x.map((v,i)=>v+omega*d[i]);}
const inv=transpose(Array.from({length:n},(_,j)=>{const e=zeros(n);e[j]=1;return solve(F,e);})),ni=M=>Math.max(...M.map(r=>r.reduce((s,v)=>s+Math.abs(v),0))),cond=ni(AA)*ni(inv),M=AA.map((_,i)=>AA[0].map((_,j)=>(i===j?1:0)-omega*B[i].reduce((s,v,k)=>s+v*AA[k][j],0)));return {exact,luResidual:norm(op(exact).map((v,i)=>v-rhs[i])),gm,gs:{x,history,reason},conditionInfinity:cond,iterationNormInfinity:ni(M),A};}
const api={...Q,zeros,transpose,mv,factor,solve,assemble,limiter,slope,operators,real,gsMatrix,compare};if(typeof module!=='undefined')module.exports=api;else root.EduNumerics=api;
})(typeof window!=='undefined'?window:globalThis);

/* Independent scalar smoothness experiment. Cell averages, fixed mesh and fixed physical time grid. */
(function(root){'use strict';const N=typeof module!=='undefined'?require('./numerics'):root.EduNumerics,{real,zeros,norm}=N;
function model({n=16,kind='minmod',eps=.01,nu=.01,amplitude=.2,phase=.07,omega=.7}={}){
if(!Number.isInteger(n)||n<8||n>128||nu<0||nu>.1||eps<=0)throw Error('Invalid scalar experiment');const h=1/n,x=Array.from({length:n},(_,i)=>(i+.5)*h),params=[amplitude,phase],alpha=2;
function exact(o,x,p){return o.add(o.c(1),o.mul(p[0],o.sin(o.mul(o.c(2*Math.PI),o.sub(o.c(x),p[1])))));}
function derivative(o,x,p){return o.mul(o.mul(o.c(2*Math.PI),p[0]),o.cos(o.mul(o.c(2*Math.PI),o.sub(o.c(x),p[1]))));}
function evaluate(o,U,p,mode='full',lim=kind){const {c,add,sub,mul}=o,slopes=U.map((u,i)=>i===0||i===n-1?c(0):N.slope(o,sub(u,U[i-1]),sub(U[i+1],u),lim,eps,mode)),F=[];
for(let f=0;f<=n;f++){const L=f?add(U[f-1],mul(c(.5),slopes[f-1])):exact(o,0,p),R=f<n?sub(U[f],mul(c(.5),slopes[f])):exact(o,1,p);const left=f?U[f-1]:L,right=f<n?U[f]:R,distance=f===0||f===n?h/2:h;F[f]=sub(sub(mul(c(.25),add(mul(L,L),mul(R,R))),mul(c(alpha/2),sub(R,L))),mul(c(nu/distance),sub(right,left)));}
const physicalFlux=z=>{const u=exact(o,z,p);return sub(mul(c(.5),mul(u,u)),mul(c(nu),derivative(o,z,p)));};const R=U.map((u,i)=>sub(sub(F[i+1],F[i]),sub(physicalFlux((i+1)*h),physicalFlux(i*h)))),J=U.reduce((s,u,i)=>add(s,mul(c(h*(1+x[i])),u)),c(0));return {R,J};}
function linearize(U,p=params,mode='full',lim=kind){return N.operators(U,p,(o,u,p,m)=>evaluate(o,u,p,m,lim),mode);}
function branches(U){return U.map((u,i)=>i===0||i===n-1?'end':N.limiter(u-U[i-1],U[i+1]-u,kind,eps).branch);}
function solve(p=params,start){let U=start?start.slice():x.map(xx=>exact(real,xx,p)),history=[],switches=[],prev=branches(U),states=[];for(let k=0;k<70;k++){const L=linearize(U,p),r=norm(L.R);history.push(r);states.push(U.slice());const branch=branches(U);switches.push(branch.filter((v,i)=>v!==prev[i]).length);prev=branch;if(r<1e-11)return {U,history,switches,states,converged:true};const d=N.solve(N.factor(N.assemble(L,n)),L.R.map(v=>-v));let t=1,accepted=false;for(let j=0;j<25;j++){const v=U.map((u,i)=>u+t*d[i]);if(Math.min(...v)>.1&&Math.max(...v)<1.9&&norm(evaluate(real,v,p).R)<(1-1e-4*t)*r){U=v;accepted=true;break;}t*=.5;}if(!accepted)return {U,history,switches,states,converged:false,reason:'No residual-decreasing branch correction'};}return {U,history,switches,states,converged:false,reason:'Iteration limit'};}
function analyse(s,mode='full',parameter=0){if(!s.converged)throw Error('Steady primal has not converged');const L=linearize(s.U,params,mode),P=N.assemble(linearize(s.U,params,'full','first'),n),dp=[0,0];dp[parameter]=1;const d=L.jvp(zeros(n),dp),j=L.vjp(zeros(n),1),t=N.compare(L,P,d.R.map(v=>-v),false,omega),a=N.compare(L,P,j.U.map(v=>-v),true,omega),gradient=L.vjp(a.exact,1).p[parameter],tg=L.jvp(t.exact,dp).J,w=x.map(xx=>Math.cos(3*xx)),v=x.map(xx=>Math.sin(7*xx));return {L,t,a,gradient,tangentGradient:tg,dotError:Math.abs(N.dot(w,L.jvp(v).R)-N.dot(v,L.vjp(w).U))};}
return {n,h,x,params,kind,eps,nu,solve,analyse,linearize,branches,evaluate:(U,p=params)=>evaluate(real,U,p),exact:xx=>exact(real,xx,params)};
}
function transient({n=40,nu=.005,eps=.01,kind='minmod',width=.05,T=.12,position=.4}={}){
 if(n<8||n>160||nu<0||width<=0||eps<=0||T<0||T>.2)throw Error('Invalid transient experiment');const h=1/n,x=Array.from({length:n},(_,i)=>(i+.5)*h),alpha=1.5,steps=Math.max(1,Math.ceil(T/(.2/(alpha/h+2*nu/(h*h))))),dt=T/steps;
function spatial(o,U,p,mode){const {c,add,sub,mul}=o,sl=U.map((u,i)=>i===0||i===n-1?c(0):N.slope(o,sub(u,U[i-1]),sub(U[i+1],u),kind,eps,mode)),F=[];for(let f=0;f<=n;f++){const L=f?add(U[f-1],mul(c(.5),sl[f-1])):c(1),R=f<n?sub(U[f],mul(c(.5),sl[f])):c(0),ul=f?U[f-1]:c(1),ur=f<n?U[f]:c(0);F[f]=sub(sub(mul(c(.25),add(mul(L,L),mul(R,R))),mul(c(alpha/2),sub(R,L))),mul(c(nu/(f===0||f===n?h/2:h)),sub(ur,ul)));}return {R:U.map((_,i)=>mul(c(1/h),sub(F[i],F[i+1]))),J:c(0),boundaryFlux:sub(F[0],F[n])};}
// Exact cell averages of a tanh transition; initial derivative follows its moving centre.
const logcosh=z=>Math.abs(z)+Math.log1p(Math.exp(-2*Math.abs(z)))-Math.log(2),initial=a=>x.map((_,i)=>.5-.5*width/h*(logcosh(((i+1)*h-a)/width)-logcosh((i*h-a)/width))),initialDerivative=a=>x.map((_,i)=>.5/h*(Math.tanh(((i+1)*h-a)/width)-Math.tanh((i*h-a)/width)));
function run(a=position,keep=true,mode='full'){let U=initial(a),v=initialDerivative(a),history=[U.slice()],vs=[v.slice()],tape=[],massError=0;
for(let k=0;k<steps;k++){const A=N.operators(U,[],spatial,mode),y=U.map((u,i)=>u+dt*A.R[i]),B=N.operators(y,[],spatial,mode),next=U.map((u,i)=>.5*(u+y[i]+dt*B.R[i]));const va=A.jvp(v).R,vy=v.map((z,i)=>z+dt*va[i]),vb=B.jvp(vy).R;v=v.map((z,i)=>.5*(z+vy[i]+dt*vb[i]));massError=Math.max(massError,Math.abs(h*next.reduce((s,z,i)=>s+z-U[i],0)-.5*dt*(spatial(real,U,[],mode).boundaryFlux+spatial(real,y,[],mode).boundaryFlux)));U=next;if(keep){tape.push({A,B});history.push(U.slice());vs.push(v.slice());}}
const weights=x.map(xx=>h*(1+xx)),J=N.dot(weights,U);let w=weights.slice(),dual=Array(steps+1);dual[steps]=w.slice();if(keep)for(let k=steps-1;k>=0;k--){const {A,B}=tape[k],bw=B.vjp(w).U,wy=w.map((z,i)=>.5*(z+dt*bw[i])),aw=A.vjp(wy).U;w=w.map((z,i)=>.5*z+wy[i]+dt*aw[i]);dual[k]=w.slice();}return {U,J,history,vs,dual,forward:N.dot(weights,v),adjoint:keep?N.dot(w,initialDerivative(a)):null,maxTangent:Math.max(...v.map(Math.abs)),l1Tangent:h*v.reduce((s,z)=>s+Math.abs(z),0),massError};}
return {n,h,x,steps,dt,run,position,nu,width};}
const api={model,transient};if(typeof module!=='undefined')module.exports=api;else root.SmoothBurgers=api;
})(typeof window!=='undefined'?window:globalThis);