← 首页← Home

二维 Euler 方程的离散伴随:格心格式

Discrete adjoints for two-dimensional Euler: cell-centred

本页先介绍二维可压缩 Euler 方程组和它的作用,再从二维标量的同一张混合网格出发,把一个标量换成四个守恒量,并接上拟一维 Euler 的热力学与特征边界。本页研究低马赫数、无激波的稳态绕流:逐面查看通量与导数,求解原始、前向和伴随方程,验证来流与物面坐标的压力载荷梯度,并在固定多边形的加密网格族上测量精度阶和伴随一致性。

After introducing the two-dimensional compressible Euler equations and what they are for, this page starts from the scalar example’s hybrid mesh, replaces one scalar by four conserved variables and extends quasi-one-dimensional Euler thermodynamics and characteristic boundaries. It studies low-Mach, shock-free steady flow: inspect face fluxes and derivatives, solve primal/tangent/adjoint equations, verify pressure-load gradients with respect to free-stream inputs and body coordinates, and measure the order of accuracy and adjoint consistency on a refined family with a fixed polygonal boundary.

全页路线图:从设计输入到梯度设计输入 DD = (M∞, θ, X)50 个分量(2 + 48)第 12 节求解R(U, D) = 0Newton,GS 预处理GMRES第 5、6 节状态 U96 个未知量24 个单元 × 4第 2 节目标 J物面压力合力y 方向 JyJUT = (∂J/∂U)T第 7 节梯度 dJ/dD50 个分量第 12 节前向(tangent)路线:从左往右,每个设计分量一次D˙ = ek第 k 个设计分量A U˙ = −RD D˙​一次线性求解U˙ = UDek = dU/dDk状态的切向量J˙ = JU U˙ + JD D˙​方向导数dJ/dDk梯度的一个分量50 个设计分量 → 50 次线性求解,每次得到梯度的一个分量(第 8、10 节)伴随(adjoint)路线:从右往左,只解一次dJ/dD = JD + ψTRD全部 50 个分量ψ伴随变量ATψ = −JUT一次转置求解JUT = (∂J/∂U)T目标对状态的导数1 次转置求解,再做乘积 ψTRD(不再求解),得到全部 50 个分量(第 9、11、12 节)代价:50 个设计分量 ⇒ 前向 50 次线性求解,伴随 1 次全页路线图:从设计输入到梯度原始计算前向 ×50伴随 ×1从上往下从下往上设计输入 DD = (M∞, θ, X)第 12 节求解R(U, D) = 0第 5、6 节状态 U96 个未知量第 2 节目标 JJUT = (∂J/∂U)T第 7 节梯度 dJ/dD50 个分量第 12 节D˙ = ek第 k 个设计分量A U˙ = −RD D˙​一次线性求解U˙ =UDek = dU/dDk状态的切向量J˙ = JU U˙ + JD D˙​方向导数dJ/dDk梯度的一个分量dJ/dD =JD + ψTRD全部 50 个分量ψ伴随变量ATψ = −JUT一次转置求解JUT = (∂J/∂U)T目标对状态的导数50 个设计分量:前向 50 次线性求解,伴随 1 次
Page roadmap: from the design input to the gradientdesign input DD = (M∞, θ, X)50 components(2 + 48)Section 12solveR(U, D) = 0Newton, GS-preconditioned GMRESSections 5–6state U96 unknowns24 cells × 4Section 2objective Jbody pressure force,y direction JyJUT = (∂J/∂U)TSection 7gradient dJ/dD50 componentsSection 12Tangent (forward) route: left to right, once per design componentD˙ = ekdesign component kA U˙ = −RD D˙​one linear solveU˙ = UDek = dU/dDktangent of the stateJ˙ = JU U˙ + JD D˙​directional derivativedJ/dDkone gradient component50 design components → 50 linear solves, each giving one gradient component (Sections 8 and 10)Adjoint route: right to left, a single solvedJ/dD = JD + ψTRDall 50 componentsψadjoint variableATψ = −JUTone transposed solveJUT = (∂J/∂U)Tobjective vs state1 transposed solve, then the product ψTRD (no further solve) gives all 50 components (Sections 9, 11 and 12)Cost: 50 design components ⇒ 50 tangent solves against 1 adjoint solvePage roadmap: from the design input to the gradientPrimalTangent ×50Adjoint ×1top to bottombottom to topdesign input DD = (M∞, θ, X)Section 12solveR(U, D) = 0Sections 5–6state U96 unknownsSection 2objective JJUT = (∂J/∂U)TSection 7gradient dJ/dD50 componentsSection 12D˙ = ekcomponent kA U˙ = −RD D˙​one linear solveU˙ =UDek = dU/dDkstate tangentJ˙ = JU U˙ + JD D˙​along component kdJ/dDkone componentdJ/dD =JD + ψTRDall 50 componentsψadjoint variableATψ = −JUTtransposed solveJUT = (∂J/∂U)Tobjective vs state50 design components:50 tangent solves, 1 adjoint solve
全页路线图 · 从设计输入到梯度。上排是原始计算:由设计输入求解离散方程,得到状态,再算出目标。前向(tangent)路线顺着计算方向,每个设计分量解一次线性方程,得到梯度的一个分量;伴随(adjoint)路线逆着计算方向,只解一次转置方程,再做一次乘积就得到全部分量。本页默认设置下有 50 个设计分量:50 次前向求解对 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 50 design components: 50 tangent solves against one adjoint solve. The section numbers under the boxes are links.

第 1 节Section 1

方程:二维定常可压缩 Euler 方程组

The equations: steady two-dimensional compressible Euler

从守恒律到 Euler 方程组

From conservation laws to the Euler equations

在流体中任取一块固定的区域。区域里的质量、两个方向的动量和总能量,只能通过它的边界改变:流体穿过边界时把这些量带进带出,边界上的压力推动流体并对它做功。不计黏性和热传导时,这四条守恒律就是二维可压缩 Euler 方程组。

Take any fixed region of the fluid. The mass, the momentum in two directions and the total energy inside it can change only through its boundary: the fluid carries these quantities in and out as it crosses the boundary, and the pressure on the boundary pushes the fluid and does work on it. Without viscosity and heat conduction, these four conservation laws are the two-dimensional compressible Euler equations.

拟一维 Euler 的三分量状态现在多了一个横向动量。二维没有变截面积源项:几何直接进入每个面的面积向量(第 2 节)。采用理想气体 γ=1.4\gamma=1.4,ρE\rho E 是单位体积总能量,包含两个方向的动能;压力计算漏掉横向动能,就会破坏原始方程及所有导数。

The quasi-one-dimensional Euler state gains a transverse momentum component. There is no variable-area source: geometry enters through each face area vector (Section 2). Use an ideal gas with γ=1.4\gamma=1.4. The total energy per unit volume ρE\rho E includes kinetic energy in both directions; omitting the transverse kinetic energy corrupts both the primal equations and their derivatives.

U=(ρ,  ρu,  ρv,  ρE)T,p=(γ−1)(ρE−12ρ(u2+v2)),c=γp/ρ.U=(\rho,\;\rho u,\;\rho v,\;\rho E)^T,\qquad p=(\gamma-1)\Bigl(\rho E-\tfrac12\rho(u^2+v^2)\Bigr),\qquad c=\sqrt{\gamma p/\rho}.
∂tU+∂xFx(U)+∂yFy(U)=0,Fx=[ρuρu2+pρuv(ρE+p)u],Fy=[ρvρuvρv2+p(ρE+p)v].\partial_tU+\partial_xF_x(U)+\partial_yF_y(U)=0,\qquad F_x=\begin{bmatrix}\rho u\\\rho u^2+p\\\rho uv\\(\rho E+p)u\end{bmatrix},\quad F_y=\begin{bmatrix}\rho v\\\rho uv\\\rho v^2+p\\(\rho E+p)v\end{bmatrix}.

第一行是质量守恒;第二、三行是两个方向的动量守恒,压力 pp 像通量一样出现在其中;第四行是能量守恒,能量通量 (ρE+p)𝒖(\rho E+p)\boldsymbol u 包括流体携带的总能量和压力所做的功。四个方程里有 ρ,u,v,E,p\rho,u,v,E,p 五个量,状态方程 p=(γ−1)(ρE−12ρ(u2+v2))p=(\gamma-1)\bigl(\rho E-\tfrac12\rho(u^2+v^2)\bigr) 把它们闭合;声速 c=γp/ρc=\sqrt{\gamma p/\rho} 决定下面的特征速度。

The first row is conservation of mass; the second and third are conservation of momentum in the two directions, where the pressure pp appears like a flux; the fourth is conservation of energy, whose flux (ρE+p)𝒖(\rho E+p)\boldsymbol u holds the total energy carried by the fluid and the work done by the pressure. The four equations contain five quantities ρ,u,v,E,p\rho,u,v,E,p; the equation of state p=(γ−1)(ρE−12ρ(u2+v2))p=(\gamma-1)\bigl(\rho E-\tfrac12\rho(u^2+v^2)\bigr) closes them, and the speed of sound c=γp/ρc=\sqrt{\gamma p/\rho} sets the characteristic speeds below.

对任意控制体 VV 积分,并用散度定理把通量散度的体积分变成边界积分,就得到积分形式。记 𝒏^=(n^x,n^y)\hat{\boldsymbol n}=(\hat n_x,\hat n_y) 为 VV 边界的单位外法向,un=𝒖⋅𝒏^u_n=\boldsymbol u\cdot\hat{\boldsymbol n} 为法向速度,Fn^F_{\hat n} 是穿过边界的法向通量:

Integrating over any control volume VV, and turning the volume integral of the flux divergence into a boundary integral with the divergence theorem, gives the integral form. With 𝒏^=(n^x,n^y)\hat{\boldsymbol n}=(\hat n_x,\hat n_y) the outward unit normal of the boundary of VV and un=𝒖⋅𝒏^u_n=\boldsymbol u\cdot\hat{\boldsymbol n} the normal velocity, Fn^F_{\hat n} is the normal flux through the boundary:

ddt∫VU dA+∮∂VFn^(U) ds=0,Fn^(U)=Fx(U) n^x+Fy(U) n^y=[ρunρu un+pn^xρv un+pn^y(ρE+p) un].\frac{d}{dt}\int_VU\,dA+\oint_{\partial V}F_{\hat n}(U)\,ds=0,\qquad F_{\hat n}(U)=F_x(U)\,\hat n_x+F_y(U)\,\hat n_y=\begin{bmatrix}\rho u_n\\\rho u\,u_n+p\hat n_x\\\rho v\,u_n+p\hat n_y\\(\rho E+p)\,u_n\end{bmatrix}.

本页只研究定常解。令 ∂tU=0\partial_tU=0,要离散的就是定常的积分形式:对计算域 Ω\Omega 中的每个控制体,穿过它边界的净通量为零。第 2 节把控制体取成网格的单元(格心)或中位对偶体(格点),第 3 节把边界积分写成各个面上的通量之和。

This page studies steady solutions only. With ∂tU=0\partial_tU=0, the equation to be discretised is the steady integral form: for every control volume in the domain Ω\Omega, the net flux through its boundary vanishes. Section 2 takes the control volumes to be the cells (cell-centred) or the median-dual volumes (node-centred) of a mesh, and Section 3 writes the boundary integral as a sum of fluxes over faces.

∮∂VFn^(U) ds=0,V⊂Ω.\oint_{\partial V}F_{\hat n}(U)\,ds=0,\qquad V\subset\Omega.

本页的来流马赫数低,绕流光滑、没有激波:第 3 节的一阶通量不需要限制器,第 14 节的复数步长也适用于整条计算链。激波及其伴随不在本页讨论。

The free-stream Mach number of this page is low and the flow past the body is smooth, without shocks: the first-order flux of Section 3 needs no limiter, and the complex step of Section 14 applies to the whole chain of computations. Shocks and their adjoints are not treated on this page.

图 1.1 把这个问题画在一张图上(基准状态 M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ,本页代码的收敛解,单元平均值;第 2 节起讲怎样算)。左边是计算域:物面八边形(琥珀色)与远场八边形(蓝色)之间的区域;每个控制体按当地马赫数 ∣𝒖∣/c|\boldsymbol u|/c 着色,短箭头是当地速度,远场的实线面是入流面,虚线面是出流面。来流从左下方流入:流体在物体上游一侧减速,在物体上下两侧加速;当地马赫数在 0.176 到 0.236 之间,最高值在物体下侧紧贴物面的控制体里,处处远小于 1。右边是物面压力:迎风一侧高于来流压力 p∞=1p_\infty=1,背风一侧低于它,流体在物体后面没有恢复压力。第 7 节说明这主要是本页固定数值耗散造成的离散误差,这里的阻力不为零也是因为它。

Figure 1.1 draws the problem in one picture (base state M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ; the converged solution of this page’s code, cell averages; Section 2 onwards shows how it is computed). On the left is the domain, the region between the body octagon (amber) and the far-field octagon (blue); every control volume is coloured by its local Mach number ∣𝒖∣/c|\boldsymbol u|/c, the short arrows are the local velocity, and the solid far-field faces are inflow faces, the dashed ones outflow faces. The free stream comes from the lower left: the fluid slows down on the upstream side of the body and speeds up above and below it; the local Mach number lies between 0.176 and 0.236, highest in a volume next to the wall on the lower side of the body, and far below 1 everywhere. On the right is the wall pressure: above the free-stream pressure p∞=1p_\infty=1 on the upstream side and below it on the downstream side, where the flow does not recover its pressure behind the body. Section 7 shows that this is mostly discretisation error caused by the page’s fixed numerical dissipation, which is also why the drag here is not zero.

来流free streamM∞ = 0.2, θ = 10°ρ∞ = p∞ = 10.800.850.900.951.001.051.101.150°90°180°270°360°面中点绕物体中心的角度angle of the face midpoint物面压力 p(每个面)wall pressure p (every face)p∞迎风upstream背风downstream0.1760.236当地马赫数 |u|/c(基准解)local Mach number |u|/c (baseline)物面:滑移壁面body: slip wall远场入流面far field, inflow face远场出流面far field, outflow face网格节点,坐标属于 Xmesh node, coordinates in X当地速度local velocity设计变量 D = (M∞, θ, X):2 个来流参数与 24 个节点的 48 个坐标,共 50 个分量design D = (M∞, θ, X): 2 free-stream inputs and the 48 coordinates of the 24 nodes, 50 components目标 J:物面压力合力的一个分量,默认 y 分量 Jy(第 7 节)objective J: one component of the pressure force on the body, by default Jy (Section 7)
图 1.1 · 本页的问题一览(基准状态 M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ,本页代码的收敛解,单元平均值)。左:计算域是物面八边形(琥珀色,滑移壁面)与远场八边形(蓝色)之间的区域;远场面分成入流面(实线)与出流面(虚线);每个控制体按当地马赫数 ∣𝒖∣/c|\boldsymbol u|/c 着色,短箭头是当地速度;黑点是 24 个网格节点,它们的坐标 XX 与 M∞M_\infty、θ\theta 一起构成设计变量 DD。右:物面上每个面的压力 pp(取它所属控制体的值),横轴是面中点绕物体中心的角度,虚线是来流压力 p∞p_\infty;迎风一侧最高,背风一侧最低。第 7 节的目标 JJ 由这些压力逐面求和得到。Figure 1.1 · The problem at a glance (base state M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ; the converged solution of this page's code, cell averages). Left: the domain is the region between the body octagon (amber, a slip wall) and the far-field octagon (blue); the far-field faces are inflow faces (solid) or outflow faces (dashed); every control volume is coloured by its local Mach number ∣𝒖∣/c|\boldsymbol u|/c, and the short arrows are the local velocity; the dots are the 24 mesh nodes, whose coordinates XX form the design variables DD together with M∞M_\infty and θ\theta. Right: the pressure pp on every wall face (the value of its control volume) against the angle of the face midpoint around the body centre; the dashed line is the free-stream pressure p∞p_\infty. The pressure is highest on the upstream side and lowest on the downstream side. The objective JJ of Section 7 is summed face by face from these pressures.

特征速度:一个边界要给几个条件

Characteristic speeds: how many conditions a boundary takes

把法向通量 Fn^F_{\hat n} 对 UU 求导,得到 4×44\times4 的 Jacobian ∂Fn^/∂U\partial F_{\hat n}/\partial U。它有四个实特征值:

Differentiating the normal flux Fn^F_{\hat n} with respect to UU gives the 4×44\times4 Jacobian ∂Fn^/∂U\partial F_{\hat n}/\partial U. It has four real eigenvalues:

λ1=un−c,λ2=λ3=un,λ4=un+c.\lambda_1=u_n-c,\qquad \lambda_2=\lambda_3=u_n,\qquad \lambda_4=u_n+c.

它们是小扰动沿 𝒏^\hat{\boldsymbol n} 传播的速度:两个声波以 un±cu_n\pm c 传播,另外两个量(熵与切向速度)以流体的法向速度 unu_n 随流输运。第 4 节由 Riemann 不变量得到同样的四个速度;第 8 节在代码的通量 Jacobian 上核对它们的和与积。

They are the speeds at which small disturbances travel along 𝒏^\hat{\boldsymbol n}: two sound waves move at un±cu_n\pm c, and two more quantities (the entropy and the tangential velocity) are carried with the normal velocity unu_n of the fluid. Section 4 finds the same four speeds from the Riemann invariants; Section 8 checks their sum and product on the flux Jacobian of the code.

在边界上取 𝒏^\hat{\boldsymbol n} 指向域外。速度为负的波从外部进入计算域,它携带的信息只能由边界提供;速度为正的波离开计算域,它的值由域内决定,若在边界上再指定它,问题就过定了。所以边界条件的个数等于负特征值的个数:

At a boundary take 𝒏^\hat{\boldsymbol n} pointing out of the domain. A wave with a negative speed enters the domain from outside, and the information it carries can only come from the boundary; a wave with a positive speed leaves the domain, its value is decided inside, and prescribing it at the boundary as well would over-determine the problem. The number of boundary conditions is therefore the number of negative eigenvalues:

  • 超声速入流(un<−cu_n<-c):四个特征值都为负,给 4 个条件,即整个状态;
  • 亚声速入流(−c<un<0-c<u_n<0):λ1,λ2,λ3<0\lambda_1,\lambda_2,\lambda_3<0,给 3 个条件;
  • 亚声速出流(0<un<c0<u_n<c):只有 λ1<0\lambda_1<0,给 1 个条件;
  • 超声速出流(un>cu_n>c):没有负特征值,不给条件;
  • 滑移壁面(un=0u_n=0):只有 λ1=−c<0\lambda_1=-c<0,给 1 个条件,就是无穿透条件 un=0u_n=0 本身;λ2=λ3=0\lambda_2=\lambda_3=0 的两个量沿壁面输运。
  • supersonic inflow (un<−cu_n<-c): all four eigenvalues are negative; 4 conditions, the whole state;
  • subsonic inflow (−c<un<0-c<u_n<0): λ1,λ2,λ3<0\lambda_1,\lambda_2,\lambda_3<0; 3 conditions;
  • subsonic outflow (0<un<c0<u_n<c): only λ1<0\lambda_1<0; 1 condition;
  • supersonic outflow (un>cu_n>c): no negative eigenvalue; no condition;
  • slip wall (un=0u_n=0): only λ1=−c<0\lambda_1=-c<0; 1 condition, the no-penetration condition un=0u_n=0 itself; the two quantities with λ2=λ3=0\lambda_2=\lambda_3=0 travel along the wall.

图 1.2 在本页的收敛解上画出这些速度。上半部分是三个边界面:远场入流面 f=44f=44 的四个速度中有 3 个为负,出流面 f=40f=40 只有 un−cu_n-c 为负,物面 f=32f=32 上 un=0u_n=0,只有 −c-c 为负。下半部分把条件个数画成法向马赫数 un/cu_n/c 的函数:本页全部 8 个远场面的 ∣un∣/c|u_n|/c 都不超过 0.18,远在亚声速带内。所以远场入流面从来流取 3 个条件,出流面取 1 个,物面只要 un=0u_n=0。第 4 节用 Riemann 不变量 r±=un±2c/(γ−1)r_\pm=u_n\pm2c/(\gamma-1)、熵函数 S=p/ργS=p/\rho^\gamma 与切向速度 utu_t 实现远场的这种取舍,并把 un=0u_n=0 写成只含压力的壁面通量。

Figure 1.2 draws these speeds on this page’s converged solution. The upper part shows three boundary faces: on the far-field inflow face f=44f=44, 3 of the 4 speeds are negative; on the outflow face f=40f=40, only un−cu_n-c is; and on the wall face f=32f=32, un=0u_n=0 and only −c-c is negative. The lower part draws the number of conditions as a function of the normal Mach number un/cu_n/c: all 8 far-field faces of this page have ∣un∣/c|u_n|/c at most 0.18, far inside the subsonic band. A far-field inflow face therefore takes 3 conditions from the free stream, an outflow face 1, and the wall only un=0u_n=0. Section 4 implements this choice at the far field with the Riemann invariants r±=un±2c/(γ−1)r_\pm=u_n\pm2c/(\gamma-1), the entropy function S=p/ργS=p/\rho^\gamma and the tangential velocity utu_t, and writes un=0u_n=0 as a wall flux that contains only the pressure.

远场入流面 f = 44far-field inflow face f = 44域内inside域外outside外法向normalun − c = −1.392un = −0.203un = −0.203un + c = 0.986外部给 3 个条件3 conditions from outside远场出流面 f = 40far-field outflow face f = 40域内inside域外outside外法向normalun − c = −0.969un = 0.210un = 0.210un + c = 1.389外部给 1 个条件1 condition from outside物面 f = 32(un = 0)wall face f = 32 (un = 0)域内inside物体body外法向normalun − c = −1.164un = 0un = 0un + c = 1.164外部给 1 个条件1 condition from outside亚声速 |un| < csubsonic |un| < c01234−2−1012超声速入流:4supersonic inflow: 4亚声速入流:3subsonic inflow: 3亚声速出流:1subsonic outflow: 1超声速出流:0supersonic outflow: 0法向马赫数 un/c(外法向指向域外)normal Mach number un/c (the normal points out of the domain)边界条件个数 = 负特征速度的个数boundary conditions = negative speeds本页 8 个远场面the 8 far-field faces of this page物面 un = 0wall un = 0
图 1.2 · 特征速度与边界条件的个数(基准状态的收敛解)。上:三个边界面上的四个特征速度 un−cu_n-c、unu_n、unu_n、un+cu_n+c,外法向指向右,箭头长度与速度成正比。速度为负的波(紫色)从外部进入计算域,要由边界给一个条件;速度为正的波(灰色)离开计算域。入流面 f=44f=44 与出流面 f=40f=40 是远场上流动进出最直接(∣un∣/c|u_n|/c 最大)的两个面,取第 4 节重建的边界状态 UbU_b;物面 f=32f=32 取 un=0u_n=0 与所属控制体的声速,两个 unu_n 波沿物面输运(灰点)。下:边界条件个数(负特征速度的个数)随法向马赫数 un/cu_n/c 的变化,在 un/c=−1,0,1u_n/c=-1,0,1 处跳变。蓝点是本页全部 8 个远场面,∣un∣/c≤0.18|u_n|/c\le0.18,都在亚声速带内:入流面 3 个条件,出流面 1 个;琥珀色方块是物面 un=0u_n=0,1 个条件。Figure 1.2 · Characteristic speeds and the number of boundary conditions (the converged base solution). Top: the four characteristic speeds un−cu_n-c, unu_n, unu_n, un+cu_n+c on three boundary faces, with the outward normal pointing right and arrows proportional to the speeds. A wave with a negative speed (violet) enters the domain from outside and needs one condition from the boundary; a wave with a positive speed (grey) leaves the domain. The inflow face f=44f=44 and the outflow face f=40f=40 are the far-field faces where the flow enters and leaves most directly (the largest ∣un∣/c|u_n|/c); they use the boundary state UbU_b rebuilt in Section 4; the wall face f=32f=32 uses un=0u_n=0 and the sound speed of its volume, and its two unu_n waves travel along the wall (grey dots). Bottom: the number of boundary conditions (negative speeds) against the normal Mach number un/cu_n/c; it jumps at un/c=−1,0,1u_n/c=-1,0,1. The blue dots are all 8 far-field faces of this page, with ∣un∣/c≤0.18|u_n|/c\le0.18, all inside the subsonic band: inflow faces take 3 conditions and outflow faces 1; the amber square is the wall, un=0u_n=0, 1 condition.

本页的问题:绕物体的亚声速流动

The problem on this page: subsonic flow past a body

计算域是一个闭合物体与外边界之间的区域(图 1.1 左)。物体是一条光滑的闭曲线,外边界是半径 1.9 的圆;计算实际使用两条曲线上各 8 个网格节点连成的八边形,第 2 节给出物面曲线并构造网格。物面是滑移壁面;外边界是远场,在那里流动应当回到均匀的来流。

The domain is the region between a closed body and an outer boundary (left of Figure 1.1). The body is a smooth closed curve and the outer boundary a circle of radius 1.9; the computation uses the octagons through the 8 mesh nodes on each of the two curves, and Section 2 gives the body curve and builds the mesh. The body is a slip wall; the outer boundary is the far field, where the flow should return to the uniform free stream.

来流固定为 ρ∞=1\rho_\infty=1、p∞=1p_\infty=1,速度大小为 M∞γM_\infty\sqrt\gamma,方向为攻角 θ\theta。于是 c∞=γc_\infty=\sqrt\gamma,∣𝒖∞∣/c∞=M∞|\boldsymbol u_\infty|/c_\infty=M_\infty 正好是来流马赫数。每次原始求解都从所有控制体都等于 U∞U_\infty 的均匀流出发。第 7 节的目标值,以及第 10–14 节对 M∞M_\infty、θ\theta 的导数,都以这个无量纲化为准。

The free stream is fixed at ρ∞=1\rho_\infty=1 and p∞=1p_\infty=1, with speed M∞γM_\infty\sqrt\gamma in the direction of the incidence θ\theta. Hence c∞=γc_\infty=\sqrt\gamma and ∣𝒖∞∣/c∞=M∞|\boldsymbol u_\infty|/c_\infty=M_\infty is exactly the free-stream Mach number. Every primal solve starts from the uniform state U∞U_\infty in all volumes. The objective values of Section 7 and the derivatives with respect to M∞M_\infty and θ\theta in Sections 10–14 refer to this scaling.

U∞=(1,  M∞γcos⁡θ,  M∞γsin⁡θ,  1γ−1+γM∞22)T.U_\infty=\Bigl(1,\;M_\infty\sqrt\gamma\cos\theta,\;M_\infty\sqrt\gamma\sin\theta,\;\frac1{\gamma-1}+\frac{\gamma M_\infty^2}2\Bigr)^T.

设计变量是 D=(M∞,θ,X)D=(M_\infty,\theta,X):两个来流参数,加上全部 24 个网格节点的坐标 XX(48 个数),共 50 个分量。按伴随优化文献的通常写法,设计变量记作 DD。移动物面节点改变物体的形状,移动远场节点改变外边界,移动其余节点只改变网格;它们都会改变离散解和目标。第 10 节给出这些分量的排列,第 12 节求对全部坐标的导数。

The design variables are D=(M∞,θ,X)D=(M_\infty,\theta,X): the two free-stream parameters and the coordinates XX of all 24 mesh nodes (48 numbers), 50 components in all. As is usual in the adjoint-optimization literature, the design variables are written DD. Moving a body node changes the shape of the body, moving a far-field node changes the outer boundary, and moving any other node changes only the mesh; each of them changes the discrete solution and the objective. Section 10 lists the components, and Section 12 differentiates with respect to all coordinates.

第 7 节给出设计的目的:目标 JJ 是流体作用在物体上的压力合力的一个分量,默认是 yy 分量 JyJ_y,页首可以改成 xx 分量 JxJ_x。它由物面压力(图 1.1 右)逐面求和得到;第 7 节还讨论在这些粗网格上,这个力有多少是物理的。

Section 7 states the purpose of the design: the objective JJ is one component of the pressure force that the fluid exerts on the body, by default the yy component JyJ_y; the header can switch it to the xx component JxJ_x. It is summed face by face from the wall pressures (right of Figure 1.1); Section 7 also discusses how much of this force is physics on these coarse meshes.

这组方程的作用

What these equations are for

在流体力学中,Euler 方程组描述不计黏性和热传导的可压缩流动。黏性只在物面附近的薄边界层和尾迹里起作用时,例如高雷诺数下的流线型物体,它给出其余区域的速度和压力;它包含声波,流速更高时还包含激波(本页不讨论);它也是 Navier–Stokes 方程组去掉黏性应力和热传导后剩下的部分。无黏流中物体受的力全部来自物面压力;另一方面,稳态、亚声速、无激波的无黏绕流阻力为零(d'Alembert 佯谬,第 7 节),这提醒我们它没有描述什么。气动外形设计常先用 Euler 方程组计算压力载荷,再用伴随求载荷对大量外形参数的梯度,这正是本页的内容。

In fluid dynamics the Euler equations describe compressible flow without viscosity and heat conduction. Where viscosity matters only in thin boundary layers next to walls and in wakes, as for streamlined bodies at high Reynolds numbers, they give the velocity and the pressure everywhere else; they contain sound waves and, at higher speeds, shock waves (not treated here); and they are what remains of the Navier–Stokes equations without viscous stresses and heat conduction. In inviscid flow the force on a body comes entirely from the wall pressure; on the other hand, steady, subsonic, shock-free inviscid flow past a body has zero drag (d’Alembert’s paradox, Section 7), a reminder of what the equations leave out. Aerodynamic shape design often computes pressure loads with the Euler equations first and then uses an adjoint for the gradient of a load with respect to many shape parameters, which is the subject of this page.

在本课程中,这组方程把前面两类页面接在一起:

In this course the equations join the two earlier kinds of page:

  • 同一张网格:二维标量页的混合网格与面积向量(第 2 节)、面循环(第 5 节)原样沿用,每个控制体的未知量从 1 个变成 4 个;
  • 同样的热力学与特征边界:状态方程、声速与 Riemann 不变量来自拟一维 Euler,二维的新内容是在远场先把速度分解到面的法向与切向(第 4 节);
  • 一个方程组:Jacobian 的每个非零元变成一个 4×44\times4 块(第 6 节),前向与伴随按块传播;
  • 几何也是设计变量:节点坐标进入面积向量、面长与单位法向,第 12 节对它们求导;
  • 一个光滑的小例子:96 个未知量,没有激波,每个导数都能用有限差分与复数步长核对(第 13、14 节)。
  • The same mesh. The hybrid mesh and the area vectors (Section 2) and the face loop (Section 5) of the 2-D scalar pages carry over unchanged; each control volume now holds 4 unknowns instead of 1.
  • The same thermodynamics and characteristic boundaries. The equation of state, the sound speed and the Riemann invariants come from quasi-1-D Euler; what is new in two dimensions is that the far field first splits the velocity into the normal and tangential components of a face (Section 4).
  • A system. Every nonzero entry of the Jacobian becomes a 4×44\times4 block (Section 6), and the tangent and the adjoint propagate blocks.
  • Geometry as design variables. The node coordinates enter the area vectors, face lengths and unit normals, and Section 12 differentiates through them.
  • A small, smooth example. 96 unknowns and no shocks, so every derivative can be checked with finite differences and complex steps (Sections 13 and 14).

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

Figure 1.3 places this page on the route through the site; every box links to Section 1 of its page. Each step to the right mainly adds one kind of ingredient: 1-D Burgers has a single nonlinear convection equation; quasi-1-D Euler replaces it by three equations (mass, momentum and energy) and brings in the pressure and a varying cross-section; this page 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 2-D scalar advection page uses the same mesh (see its Section 2), and the 1-D laminar N–S page adds viscosity and heat conduction to the Euler equations in one dimension.

图 1.3 · 从一维 Burgers 到二维 RANS–SA 的路线,本页居中(紫色)。每往右一步主要增加一类新内容;每点的未知量依次为 1、3、4、4、5 个,由各页的模型代码按默认设置数出。每个方框都链接到对应页面(同为格心格式)的第 1 节,即那一页的方程。每一页都用面循环组装残差,前向与伴随沿同一个循环求导。Figure 1.3 · The route from 1-D Burgers to 2-D RANS–SA, with this page in the middle (violet). Each step to the right mainly adds one kind of ingredient; the unknowns per point are 1, 3, 4, 4, 5, counted by each page's model code at its default settings. Every box links to Section 1 of its page (in the same, cell-centred scheme), where that page introduces its equations. Every page assembles its residual with a face loop, and the tangent and the adjoint differentiate that loop.

本页要回答的问题

The question this page answers

本页假设读者熟悉微积分、矩阵运算、Newton 法和 Krylov 迭代(GMRES),最好读过二维标量对流页(同一张网格与面循环)和拟一维 Euler 页(同样的热力学与特征边界);本页用到的内容都会重新给出。

This page assumes calculus, matrix algebra, Newton’s method and a Krylov solver (GMRES), and it helps to have read the 2-D scalar advection page (the same mesh and face loop) and the quasi-1-D Euler page (the same thermodynamics and characteristic boundaries); the parts this page uses are restated here.

本页只回答一个问题:设计变量 DD 改变一点,压力合力 JJ 改变多少?DD 有 50 个分量。前向(tangent)路线每个分量解一次线性方程,共 50 次;伴随路线只解一次转置方程,就得到全部分量(第 11 节)。页首的全页路线图把后面各节串成一条线。按页首默认设置实际计数,50 次前向求解共用 2400 次 GMRES 迭代,1 次伴随求解只用 52 次。

This page answers one question: how much does the pressure force JJ change when the design DD changes a little? DD has 50 components. The tangent (forward) route solves one linear system per component, 50 in all; the adjoint route solves a single transposed system and obtains every component (Section 11). The page roadmap at the top strings the later sections together. Counted with the default settings at the top of the page, the 50 tangent solves take 2400 GMRES iterations in total and the single adjoint solve only 52.

要在计算机上回答这个问题,先要把连续的方程组变成有限个未知量的代数方程。第 2 节沿用二维标量的网格,决定四个守恒量存放在哪里;第 3 节再把定常的积分形式写成各个面上的通量之和。

To answer it on a computer, the continuous equations must first become algebraic equations in finitely many unknowns. Section 2 reuses the mesh of the 2-D scalar pages and decides where the four conserved variables live; Section 3 then writes the steady integral form as a sum of fluxes over faces.

第 2 节Section 2

网格:沿用二维标量的混合 O 型网格

Mesh: the scalar example’s hybrid O-grid

第 1 节的方程组对计算域中的每一点都成立,计算机却只能存放有限个数。本节沿用二维标量页的网格,决定四个守恒量存放在哪里,以及每个面的面积向量怎样计算。

The equations of Section 1 hold at every point of the domain, but a computer stores finitely many numbers. This section reuses the mesh of the 2-D scalar pages and decides where the four conserved variables live and how the area vector of every face is computed.

基础网格的 24 个节点坐标、8 个四边形和 16 个三角形,逐点、逐单元与二维标量一致。节点分布在三圈上,每圈取 8 个角度 tj=2πj/8t_j=2\pi j/8:内圈取在下面的物面曲线上,外圈取在半径 1.9 的圆上,中圈按 0.45 的比例把两者线性混合。数组从 0 开始编号;边界的 R=−1R=-1 表示没有右控制体,绝不能拿它访问数组。

The 24 base nodes, 8 quadrilaterals and 16 triangles match the scalar example point for point and cell for cell. The nodes lie on three rings at the 8 angles tj=2πj/8t_j=2\pi j/8: the inner ring on the body curve below, the outer ring on the circle of radius 1.9, and the middle ring blends the two with weight 0.45. Arrays are zero-based. R=−1R=-1 marks an absent exterior control volume and must never be used as an array index.

x=0.1+r(t)cos⁡t,y=−0.04+r(t)sin⁡t,r(t)=0.55 (1+0.28cos⁡t+0.12sin⁡2t).x=0.1+r(t)\cos t,\quad y=-0.04+r(t)\sin t,\qquad r(t)=0.55\,\bigl(1+0.28\cos t+0.12\sin 2t\bigr).

图 2.1 把上式与三圈节点画在一起:虚线是曲线本身,实线是计算真正使用的多边形。

Figure 2.1 draws this curve together with the three rings of nodes: the dashed lines are the curves themselves, the solid lines the polygons the computation actually uses.

整个计算域the whole domain物面附近放大zoom on the bodyt0t1t2t3t4t5t6t7(0.1, −0.04)最大间隙 0.079largest gap 0.079物面曲线 r(t)body curve r(t)计算物面:内接八边形computational body: inscribed octagon中圈:0.55×物面点+0.45×远场点middle ring: 0.55 × body point + 0.45 × far point半径 1.9 的圆circle of radius 1.9远场八边形far-field octagon
图 2.1 · 网格的三圈节点:物面曲线、中圈与远场圆,以及计算实际使用的两个八边形。左图是整个计算域,每条径向线连接同一角度 tjt_j 上的物面点、中圈点与远场点(三点共线);右图放大物面:虚线是曲线 r(t)r(t),实线是连接相邻节点得到的内接八边形,紫色线段标出两者的最大间隙(本页代码计算)。之后所有导数都针对实线多边形,而不是虚线曲线。Figure 2.1 · The three node rings: body curve, middle ring and far circle, with the two octagons the computation actually uses. Left, the whole domain: each radial line joins the body, middle and far points at the same angle tjt_j (the three are collinear). Right, a zoom on the body: the dashed line is the curve r(t)r(t), the solid line the inscribed octagon obtained by joining neighbouring nodes, and the violet segment marks their largest gap (computed by this page's code). Every derivative on this page refers to the solid polygons, not to the dashed curves.

相邻节点用直线相连,所以计算中的物面是内接于这条曲线的八边形,远场是半径 1.9 的正八边形;本页所有导数都针对这两条多边形边界。第 15 节的加密网格保持这两个八边形不变。

Neighbouring nodes are joined by straight edges, so the computational body is an octagon inscribed in this curve and the far field is a regular octagon of radius 1.9. Every derivative on this page refers to these polygonal boundaries. The refined meshes of Section 15 keep both octagons unchanged.

每个原始单元保存四个守恒分量,单元平均值显示在顶点平均中心。每条边就是一个计算面;逆时针绕单元走时,面积向量指向流出该单元的方向。这里共有 24 个控制体、48 个面、96 个状态自由度。

Each original cell stores four conserved averages, displayed at its vertex-average centre. An edge is a computational face. Walking counterclockwise around a cell gives an outward area vector. There are 24 volumes, 48 faces and 96 state degrees of freedom.

图 2.2 左边给出这 24 个控制体的编号,右边给出它们在状态向量里的位置:控制体 ii 的四个分量占第 4i4i 到 4i+34i+3 个位置。残差 RR 和伴随 ψ\psi 按同样的顺序存放,所以三个向量的第 4i+k4i+k 个分量指同一个控制体、同一条守恒方程(k=0,1,2,3k=0,1,2,3 依次是质量、xx 动量、yy 动量、能量)。

The left of Figure 2.2 numbers the 24 control volumes and the right shows where they sit in the state vector: the four components of volume ii occupy positions 4i4i to 4i+34i+3. The residual RR and the adjoint ψ\psi are stored in the same order, so component 4i+k4i+k of all three vectors refers to the same volume and the same conservation equation (k=0,1,2,3k=0,1,2,3: mass, xx momentum, yy momentum, energy).

控制体编号(单元)volume numbers (cells)01234567891011121314151617181920212223状态向量 U 的存放顺序storage order of the state vector UρρuρvρEi全局下标global index00–314–728–11312–15416–19520–23624–27728–31832–35936–391040–431144–471248–511352–551456–591560–631664–671768–711872–751976–792080–832184–872288–912392–95U ∈ ℝ96控制体 0 的四个守恒量占第 0–3 个位置volume 0: its four conserved variables fill slots 0–3物面wall远场far field内部面interior face
图 2.2 · 格心格式的 24 个控制体编号(左)与状态向量 UU 的存放顺序(右)。每个单元就是一个控制体,编号写在顶点平均中心;控制体 ii 的四个守恒量 (ρ,ρu,ρv,ρE)i(\rho,\rho u,\rho v,\rho E)_i 依次存放在全局位置 4i4i 到 4i+34i+3,所以 UU 共有 96 个分量。紫色标出的控制体 0 在本节后面的图、第 3 节的面通量和第 5、8、9 节中反复使用。Figure 2.2 · The 24 cell-centred control volumes (left) and the storage order of the state vector UU (right). Every cell is a control volume, numbered at its vertex-average centre; the four conserved variables (ρ,ρu,ρv,ρE)i(\rho,\rho u,\rho v,\rho E)_i of volume ii occupy global positions 4i4i to 4i+34i+3, so UU has 96 components. Volume 0, marked in violet, reappears in the next figures of this section, in the face flux of Section 3 and in Sections 5, 8 and 9.
𝒏f=(ye−ys,  −(xe−xs)),ℓf=∥𝒏f∥,𝒏^f=𝒏f/ℓf,∑f∈∂Vi𝒏if=0.\boldsymbol n_f=\bigl(y_e-y_s,\;-(x_e-x_s)\bigr),\quad \ell_f=\|\boldsymbol n_f\|,\quad \hat{\boldsymbol n}_f=\boldsymbol n_f/\ell_f,\quad \sum_{f\in\partial V_i}\boldsymbol n_{if}=0.

这里 (xs,ys)(x_s,y_s) 与 (xe,ye)(x_e,y_e) 是面的起点和终点(格点页的对偶面按各段相加)。面积向量 𝒏f\boldsymbol n_f 已经包含面长;单位法向 𝒏^f\hat{\boldsymbol n}_f 只用于第 4 节的特征分解,两者不能混用。

Here (xs,ys)(x_s,y_s) and (xe,ye)(x_e,y_e) are the start and end points of the face (a dual face on the node page sums its segments). The area vector 𝒏f\boldsymbol n_f already includes the face length; the unit normal 𝒏^f\hat{\boldsymbol n}_f is used only for the characteristic split of Section 4, and the two must not be mixed.

图 2.3 以一个控制体为例画出上面的约定。

Figure 2.3 illustrates this convention for one control volume.

单元 0 与它的外法向面积向量cell 0 and its outward area vectorsf0f1f2f32L=0xsxe首尾相接:∑ n = 0head to tail: ∑ n = 0f32f1f2f0|∑ n| = 1×10⁻¹⁶(舍入误差)|∑ n| = 1×10⁻¹⁶ (round-off)nf = (ye − ys, −(xe − xs)):把 xs→xe 的边向量顺时针转 90°nf = (ye − ys, −(xe − xs)): the edge vector xs→xe turned clockwise by 90°面的 L 是第一次遇到该边的单元;外法向 = ±nf(本单元是 L 取 +,是 R 取 −)L is the first cell that meets the edge; outward vector = ±nf (+ if this cell is L, − if it is R)
图 2.3 · 单元 0 的每个面及其外法向面积向量(左),以及这些向量首尾相接后回到起点(右)。面积向量 𝒏f\boldsymbol n_f 由面的起点 𝒙s\boldsymbol x_s 与终点 𝒙e\boldsymbol x_e 按左下方公式得到,长度就是面长 ℓf\ell_f;它从 LL 指向 RR,所以对 RR 一侧要取负号才是外法向。紫色是第 3 节的面通量以及第 5、8、9 节使用的面 f=0f=0。右图说明 ∑f𝒏if=0\sum_f\boldsymbol n_{if}=0:均匀流经过闭合控制体的净通量为零,第 6 节的对角块和第 7 节的压力合力都要用到这一点。Figure 2.3 · Every face of cell 0 with its outward area vector (left), and the same vectors placed head to tail, returning to the start (right). The area vector 𝒏f\boldsymbol n_f follows from the start point 𝒙s\boldsymbol x_s and end point 𝒙e\boldsymbol x_e of the face by the formula at the bottom left, and its length is the face length ℓf\ell_f; it points from LL to RR, so the RR side takes a minus sign to obtain its outward vector. Violet marks face f=0f=0, used again for the face flux of Section 3 and in Sections 5, 8 and 9. The right panel shows ∑f𝒏if=0\sum_f\boldsymbol n_{if}=0: a uniform flow has zero net flux through a closed volume, a fact used by the diagonal blocks of Section 6 and the pressure force of Section 7.

逐步推导:每个控制体的面积向量之和为零

Step by step: the area vectors of every control volume sum to zero

第一步,单元是一个多边形。沿逆时针方向依次取边界折线的顶点 𝒙1,𝒙2,…\boldsymbol x_1,\boldsymbol x_2,\dots,绕一圈后回到 𝒙1\boldsymbol x_1;第 kk 段从 𝒙k\boldsymbol x_k 到 𝒙k+1\boldsymbol x_{k+1},它的外法向面积向量是把边向量顺时针转 90°:

Step 1, a cell is a polygon. Take the vertices of its boundary counterclockwise, 𝒙1,𝒙2,…\boldsymbol x_1,\boldsymbol x_2,\dots, returning to 𝒙1\boldsymbol x_1 after one loop; segment kk runs from 𝒙k\boldsymbol x_k to 𝒙k+1\boldsymbol x_{k+1}, and its outward area vector is the edge vector turned clockwise by 90°:

𝒏k=(𝒙k+1−𝒙k)⊥,𝒅⊥=(dy, −dx).\boldsymbol n_k=(\boldsymbol x_{k+1}-\boldsymbol x_k)^{\perp},\qquad \boldsymbol d^{\perp}=(d_y,\,-d_x).

第二步,⊥\perp 是线性运算;绕一圈的边向量首尾相消,和为零:

Step 2, ⊥\perp is linear, and the edge vectors of one closed loop telescope to zero:

∑k𝒏k=(∑k(𝒙k+1−𝒙k))⊥=𝟎.\sum_{k}\boldsymbol n_k=\Bigl(\sum_{k}(\boldsymbol x_{k+1}-\boldsymbol x_k)\Bigr)^{\perp}=\boldsymbol 0.

第三步,代码按面存储 𝒏f\boldsymbol n_f(从 LL 指向 RR),控制体 ii 的外法向是 𝒏if=ℬif𝒏f\boldsymbol n_{if}=\mathcal B_{if}\boldsymbol n_f,符号与第 5 节的 ℬif\mathcal B_{if} 相同。于是 ∑fℬif𝒏f=0\sum_f\mathcal B_{if}\boldsymbol n_f=0。物理通量对面积向量是线性的,所以均匀状态 UU 在每个不含边界面的控制体上给出 ∑fℬifF𝒏f(U)=F∑fℬif𝒏f(U)=0\sum_f\mathcal B_{if}F_{\boldsymbol n_f}(U)=F_{\sum_f\mathcal B_{if}\boldsymbol n_f}(U)=0,耗散项也因 UR−UL=0U_R-U_L=0 而为零:均匀状态在所有内部控制体上的残差都是零。第 6 节的对角块和第 7 节的压力合力都用到同一个事实。

Step 3, the code stores 𝒏f\boldsymbol n_f per face (pointing from LL to RR); the outward vector of volume ii is 𝒏if=ℬif𝒏f\boldsymbol n_{if}=\mathcal B_{if}\boldsymbol n_f, with the sign ℬif\mathcal B_{if} of Section 5. Hence ∑fℬif𝒏f=0\sum_f\mathcal B_{if}\boldsymbol n_f=0. The physical flux is linear in the area vector, so in every volume without boundary faces a uniform state UU gives ∑fℬifF𝒏f(U)=F∑fℬif𝒏f(U)=0\sum_f\mathcal B_{if}F_{\boldsymbol n_f}(U)=F_{\sum_f\mathcal B_{if}\boldsymbol n_f}(U)=0, and the dissipation vanishes because UR−UL=0U_R-U_L=0: a uniform state has zero residual in every interior volume. The diagonal blocks of Section 6 and the pressure force of Section 7 use the same fact.

核对:全部 24 个控制体上 ∣∑fℬif𝒏f∣|\sum_f\mathcal B_{if}\boldsymbol n_f| 的最大值为 2×10⁻¹⁶,而最长的面长为 1.454(本页代码在构建时计算)。

Check: the largest ∣∑fℬif𝒏f∣|\sum_f\mathcal B_{if}\boldsymbol n_f| over all 24 volumes is 2×10⁻¹⁶, while the longest face has length 1.454 (computed by this page’s code at build time).

点击下面面板中的控制体或面,可查看编号、法向和守恒量;上方可切换着色的物理量,色条给出数值范围,图例说明物面(琥珀色)、远场(蓝色)与内部面的画法。

Click a volume or a face in the panel below to inspect its index, normal and state. The selector changes the plotted quantity, the colour bar gives its range, and the legend explains how body faces (amber), far-field faces (blue) and interior faces are drawn.

图 2.4Figure 2.4

控制体、面和面积向量都已确定,状态也按控制体排好了顺序。第 3 节把第 1 节的法向通量写到每个面上,得到四分量的数值通量。

The control volumes, the faces and their area vectors are now fixed, and the state is stored volume by volume. Section 3 writes the normal flux of Section 1 on every face and obtains the four-component numerical flux.

第 3 节Section 3

面上的四分量通量

The four-component flux on a face

第 1 节的积分形式要求每个控制体边界上的净通量为零。控制体的边界由面组成;第 2 节中面 ff 的面积向量 𝒏f=ℓf𝒏^f\boldsymbol n_f=\ell_f\hat{\boldsymbol n}_f 已经包含面长,所以面上的物理通量就是第 1 节的 Fn^F_{\hat n} 乘以面长:

The integral form of Section 1 asks for zero net flux through the boundary of every control volume. That boundary consists of faces, and the Section 2 area vector 𝒏f=ℓf𝒏^f\boldsymbol n_f=\ell_f\hat{\boldsymbol n}_f of face ff already includes its length, so the physical flux through a face is the Fn^F_{\hat n} of Section 1 times the face length:

Fn(U)=Fx(U) nx+Fy(U) ny=ℓ[ρunρu un+pn^xρv un+pn^y(ρE+p) un],un=un^x+vn^y.F_n(U)=F_x(U)\,n_x+F_y(U)\,n_y=\ell\begin{bmatrix}\rho u_n\\\rho u\,u_n+p\hat n_x\\\rho v\,u_n+p\hat n_y\\(\rho E+p)\,u_n\end{bmatrix},\qquad u_n=u\hat n_x+v\hat n_y.

首版使用一阶分片常值重构与固定耗散系数 α=2\alpha=2 的 Rusanov 形式通量。固定 α\alpha 是本例明确选定的数值方法,因此它对状态的导数为零;不是对一个本应依赖状态的最大波速偷偷冻结导数。页面检查 ∣un∣+c<α|u_n|+c<\alpha。代价是耗散很强:M∞=0.2M_\infty=0.2 时 α\alpha 是来流速度的 8.5 倍。第 7 节和第 15 节量化这对压力和载荷意味着什么。

This first version uses piecewise-constant reconstruction and a Rusanov-form flux with the fixed dissipation coefficient α=2\alpha=2. A constant α\alpha is part of the specified method, so its state derivative is zero; it is not an omitted derivative of a state-dependent maximum wave speed. The page checks ∣un∣+c<α|u_n|+c<\alpha. The price is strong dissipation: at M∞=0.2M_\infty=0.2, α\alpha is 8.5 times the free-stream speed. Sections 7 and 15 quantify what this does to pressures and loads.

H(UL,UR,𝒏)=Fn(UL)+Fn(UR)2−αℓ2(UR−UL).H(U_L,U_R,\boldsymbol n)=\frac{F_n(U_L)+F_n(U_R)}2-\frac{\alpha\ell}2(U_R-U_L).

图 3.1 把上式拆成代码里的几步:两侧各自从守恒量算出原始变量和物理通量,再与只依赖 UR−ULU_R-U_L 的耗散项组合。图 3.2 在一个具体的面上给出两部分的数值。

Figure 3.1 splits this formula into the steps of the code: each side computes its primitive variables and physical flux from the conserved variables, which are then combined with a dissipation term that depends only on UR−ULU_R-U_L. Figure 3.2 gives the two parts on one particular face.

UL左侧 4 个守恒量4 conserved, leftYL=(ρ, u, v, p)L原始变量primitive variablesFn(UL)物理通量physical fluxUR右侧 4 个守恒量4 conserved, rightYR=(ρ, u, v, p)R原始变量primitive variablesFn(UR)物理通量physical fluxnf, ℓf面几何(第 2 节)geometry (Section 2)Hf=½(Fn(UL)+Fn(UR))−(αℓf/2)(UR−UL)RL += Hf,RR −= HfRL += Hf, RR −= Hfprim()Fn(·)prim()Fn(·)耗散项直接读 ULthe dissipation reads UL directly耗散项直接读 URthe dissipation reads UR directly
图 3.1 · 一个内部面的通量怎样从存储的守恒量算出。方框是数组,箭头是数据流:每一侧先由 prim() 把 UU 换成原始变量 Y=(ρ,u,v,p)Y=(\rho,u,v,p),再与面积向量 𝒏f\boldsymbol n_f 一起算物理通量 FnF_n;两侧通量取平均,再减去只依赖 UR−ULU_R-U_L 与面长 ℓf\ell_f 的耗散项(上下两条虚线直接读取守恒量,不经过 prim()),得到 HfH_f,最后向左加、向右减。第 8 节和第 9 节用同一张图说明 tangent 与伴随。Figure 3.1 · How the flux of one interior face is computed from the stored conserved variables. Boxes are arrays and arrows data flow: on each side prim() turns UU into the primitive variables Y=(ρ,u,v,p)Y=(\rho,u,v,p), which together with the area vector 𝒏f\boldsymbol n_f give the physical flux FnF_n; the two fluxes are averaged, and the dissipation term, which depends only on UR−ULU_R-U_L and the face length ℓf\ell_f, is subtracted (the dashed paths read the conserved variables directly, without prim()), giving HfH_f, which is added on the left and subtracted on the right. Sections 8 and 9 reuse this picture for the tangent and the adjoint.
ρ−0.0837−0.0359−0.12ρu−0.38−0.0268−0.407ρv0.299−0.003480.295ρE−0.29−0.125−0.416中心部分 ½(Fn(UL)+Fn(UR))central part ½(Fn(UL)+Fn(UR))耗散部分 −(αℓf/2)(UR−UL)dissipation −(αℓf/2)(UR−UL)数值通量 Hf(两者之和)numerical flux Hf (their sum)
图 3.2 · 面 f=0f=0(控制体 0 与 1 之间,即第 2 节网格图中紫色的面)上数值通量的四个分量,拆成中心部分与耗散部分(M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ 的收敛解)。每个分量有自己的纵轴,柱上标出数值。选这个面是因为在全部内部面中,它的耗散部分相对中心部分最大:两者模长之比为 0.23。耗散部分与 UR−ULU_R-U_L 成正比、符号相反:它总是把通量朝抹平两侧差别的方向修正,这就是数值粘性。Figure 3.2 · The four components of the numerical flux at face f=0f=0 (between volumes 0 and 1, the violet face of the mesh figures of Section 2), split into the central part and the dissipation (converged solution at M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ). Each component has its own vertical axis, and the bars carry their values. This face is the interior face where the dissipation is largest relative to the central part: the ratio of their norms is 0.23. The dissipation is proportional to UR−ULU_R-U_L with the opposite sign: it always corrects the flux towards smoothing out the difference between the two sides, which is the numerical viscosity.

逐步推导:压力、声速与它们对 UU 的梯度

Step by step: pressure, sound speed and their gradients with respect to UU

第一步,把速度写成守恒量之比 u=(ρu)/ρu=(\rho u)/\rho、v=(ρv)/ρv=(\rho v)/\rho,动能就是 ((ρu)2+(ρv)2)/(2ρ)((\rho u)^2+(\rho v)^2)/(2\rho)。代入第 1 节的状态方程,压力是四个守恒量的显式函数(代码 prim() 算出的 p):

Step 1, write the velocity as ratios of conserved variables, u=(ρu)/ρu=(\rho u)/\rho and v=(ρv)/ρv=(\rho v)/\rho; the kinetic energy is ((ρu)2+(ρv)2)/(2ρ)((\rho u)^2+(\rho v)^2)/(2\rho). Substituted into the equation of state of Section 1, the pressure is an explicit function of the four conserved variables (the p computed by prim() in the code):

p(U)=(γ−1)(ρE−(ρu)2+(ρv)22ρ).p(U)=(\gamma-1)\Bigl(\rho E-\frac{(\rho u)^2+(\rho v)^2}{2\rho}\Bigr).

第二步,对每个分量求偏导,其余三个固定。只有动能项依赖 ρ\rho、ρu\rho u、ρv\rho v:

Step 2, differentiate with respect to each component, holding the other three fixed. Only the kinetic energy depends on ρ\rho, ρu\rho u and ρv\rho v:

∂p∂ρ=(γ−1)(ρu)2+(ρv)22ρ2=γ−12(u2+v2),∂p∂(ρu)=−(γ−1)u,∂p∂(ρv)=−(γ−1)v,∂p∂(ρE)=γ−1,\frac{\partial p}{\partial\rho}=(\gamma-1)\frac{(\rho u)^2+(\rho v)^2}{2\rho^2}=\frac{\gamma-1}2(u^2+v^2),\quad\frac{\partial p}{\partial(\rho u)}=-(\gamma-1)u,\quad\frac{\partial p}{\partial(\rho v)}=-(\gamma-1)v,\quad\frac{\partial p}{\partial(\rho E)}=\gamma-1,
∂p∂U=(γ−1)(12(u2+v2),  −u,  −v,  1).\frac{\partial p}{\partial U}=(\gamma-1)\Bigl(\tfrac12(u^2+v^2),\;-u,\;-v,\;1\Bigr).

这正是第 8 节 p˙\dot p 公式中各项的系数。第三步,声速 c=γp/ρc=\sqrt{\gamma p/\rho}。对 c2=γp/ρc^2=\gamma p/\rho 两边求前向导数,2c c˙=γ(p˙/ρ−p ρ˙/ρ2)2c\,\dot c=\gamma(\dot p/\rho-p\,\dot\rho/\rho^2),再用 γp/ρ=c2\gamma p/\rho=c^2:

These are exactly the coefficients of the p˙\dot p formula of Section 8. Step 3, the sound speed is c=γp/ρc=\sqrt{\gamma p/\rho}. The forward derivative of c2=γp/ρc^2=\gamma p/\rho is 2c c˙=γ(p˙/ρ−p ρ˙/ρ2)2c\,\dot c=\gamma(\dot p/\rho-p\,\dot\rho/\rho^2), and with γp/ρ=c2\gamma p/\rho=c^2:

c˙=γ2ρc p˙−c2ρ ρ˙,∂c∂U=γ2ρc ∂p∂U−c2ρ (1,0,0,0).\dot c=\frac{\gamma}{2\rho c}\,\dot p-\frac{c}{2\rho}\,\dot\rho,\qquad \frac{\partial c}{\partial U}=\frac{\gamma}{2\rho c}\,\frac{\partial p}{\partial U}-\frac{c}{2\rho}\,(1,0,0,0).

第 4 节的壁面通量、第 7 节的目标函数、第 8 节的通量 Jacobian 和远场的 Riemann 不变量 r±=un±2c/(γ−1)r_\pm=u_n\pm2c/(\gamma-1) 都要用这两个梯度。

The wall flux of Section 4, the objective of Section 7, the flux Jacobian of Section 8 and the far-field Riemann invariants r±=un±2c/(γ−1)r_\pm=u_n\pm2c/(\gamma-1) all use these two gradients.

核对:第 4 节 riemann() 的计算图与残差共用同一个 prim();由它给出的 ∂(r+−r−)/∂U\partial(r_+-r_-)/\partial U 与 ∂S/∂U\partial S/\partial U 按 c=γ−14(r+−r−)c=\tfrac{\gamma-1}4(r_+-r_-)、p=ργSp=\rho^\gamma S 换算出 ∂c/∂U\partial c/\partial U 与 ∂p/∂U\partial p/\partial U,与上面两式在全部 24 个控制体的收敛状态上最大相差 4×10⁻¹⁶(本页代码在构建时计算)。

Check: the computation graph of riemann() in Section 4 shares prim() with the residual; its ∂(r+−r−)/∂U\partial(r_+-r_-)/\partial U and ∂S/∂U\partial S/\partial U, converted with c=γ−14(r+−r−)c=\tfrac{\gamma-1}4(r_+-r_-) and p=ργSp=\rho^\gamma S, give ∂c/∂U\partial c/\partial U and ∂p/∂U\partial p/\partial U, which differ from the two formulas above by at most 4×10⁻¹⁶ over the converged states of all 24 volumes (computed by this page’s code at build time).

# U = [rho, rho*u, rho*v, rho*E];  n = area vector (length l)
un_l = (U[1]*nx + U[2]*ny)/U[0]              # l*u_n
p = (gamma-1)*(U[3]-(U[1]**2+U[2]**2)/(2*U[0]))
Fn = [U[0]*un_l, U[1]*un_l+p*nx, U[2]*un_l+p*ny, (U[3]+p)*un_l]
H = (Fn(UL,n)+Fn(UR,n))/2 - alpha*length(n)*(UR-UL)/2
图 3.3Figure 3.3

内部面两侧都有控制体,ULU_L 与 URU_R 都是未知量。边界面只有一侧:物面外面是物体,远场外面没有网格。第 4 节给这两类面补上通量:物面只传递压力,远场按第 1 节的特征计数,从来流和域内各取一部分信息,重建外侧状态 UbU_b。

An interior face has a control volume on each side, and both ULU_L and URU_R are unknowns. A boundary face has only one: beyond the wall lies the body, and beyond the far field there is no mesh. Section 4 completes the flux on both kinds of face: the wall passes only the pressure, and the far field rebuilds an exterior state UbU_b from the free stream and the interior, taking from each as many pieces of information as the characteristic count of Section 1 allows.

第 4 节Section 4

边界:滑移壁面与亚声速特征远场

Boundaries: slip walls and subsonic characteristic far field

第 1 节按特征速度的符号数过条件:物面只要一个条件 un=0u_n=0,远场入流面要三个,出流面要一个。本节把它们写成边界面上的通量。

Section 1 counted the conditions by the signs of the characteristic speeds: the wall needs a single condition, un=0u_n=0, a far-field inflow face three and an outflow face one. This section turns them into fluxes on the boundary faces.

这里与二维标量最重要的区别在物面。无穿透只令质量和能量通量为零;压力仍传递两个方向的动量。壁面通量采用内侧压力 pLp_L,不需要把四个守恒量强行赋值。格心和格点都在边界面上弱施加这条通量规则;格点位于物面并不意味着可以把四行全换成 Dirichlet 约束。

The body boundary is the key difference from the scalar example. No penetration removes the mass and energy fluxes, but pressure still transfers momentum. The wall flux uses the interior pressure pLp_L without assigning all four conserved values. Both schemes impose this flux weakly; placing a node on the wall does not justify replacing all four equations by Dirichlet constraints.

Hwall=(0,  pLnx,  pLny,  0)T,H˙wall=(0,  nxp˙+p n˙x,  nyp˙+p n˙y,  0)T.H_{\mathrm{wall}}=(0,\;p_Ln_x,\;p_Ln_y,\;0)^T,\qquad \dot H_{\mathrm{wall}}=(0,\;n_x\dot p+p\,\dot n_x,\;n_y\dot p+p\,\dot n_y,\;0)^T.

上面的点表示前向(tangent)导数:x˙\dot x 是程序变量 xx 沿给定输入方向的导数,这里 p˙\dot p 来自状态,n˙x\dot n_x、n˙y\dot n_y 来自几何。反向(伴随)量写作 xˉ\bar x。第 8 节完整说明这套自动微分记号。

The dot marks a forward (tangent) derivative: x˙\dot x is the derivative of the program variable xx along a given input direction; here p˙\dot p comes from the state and n˙x\dot n_x, n˙y\dot n_y from the geometry. Reverse (adjoint) quantities are written xˉ\bar x. Section 8 sets out this algorithmic-differentiation notation in full.

图 4.1 在一个物面面上对比这条规则与内部面的公式。

Figure 4.1 compares this rule with the interior-face formula on one wall face.

物面单元 0 与物面面 f=32wall cell 0 and wall face f=32物体bodynfuLun,L = −0.23, |uL| = 0.237pL = 0.869同一个状态,两种通量one state, two fluxes分量component按内部公式 Fn(UL)interior formula Fn(UL)壁面通量 Hwallwall flux Hwallρ−0.1130ρu−0.472−0.445ρv−0.169−0.166ρE−0.38600000−0.005740.04820.00569−0.205−0.002140.0180.00213−0.07660000ρρuρvρEρρuρvρE∂Hwall/∂UL:只有中间两行非零∂Hwall/∂UL: only the middle two rows are nonzero
图 4.1 · 滑移壁面只传递压力。左:物面单元 0(在全部物面面中它的法向速度最大),离散状态的速度 𝒖L\boldsymbol u_L 并不平行于物面,法向分量 un,Lu_{n,L}(蓝色虚线)不为零。右上:同一个 ULU_L 若按内部面的公式算通量,会有质量和能量穿过物面;壁面通量只保留动量方程中的压力项 pL𝒏fp_L\boldsymbol n_f,质量和能量两行正好为零(琥珀色)。右下:壁面通量的 Jacobian,只有两行非零,每行都是 ∂p/∂U\partial p/\partial U 乘以 nxn_x 或 nyn_y(数值取自本页收敛解,M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ)。Figure 4.1 · A slip wall transmits only pressure. Left: wall cell 0 (the one with the largest normal velocity among all wall faces); the velocity 𝒖L\boldsymbol u_L of the discrete state is not parallel to the wall, and its normal component un,Lu_{n,L} (dashed blue) is not zero. Top right: evaluated with the interior-face formula, the same ULU_L would carry mass and energy through the wall; the wall flux keeps only the pressure term pL𝒏fp_L\boldsymbol n_f of the momentum equations, so the mass and energy rows are exactly zero (amber). Bottom right: the Jacobian of the wall flux has only two nonzero rows, each ∂p/∂U\partial p/\partial U times nxn_x or nyn_y (values from this page's converged solution, M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ).

逐步推导:壁面通量的 Jacobian 与它的转置

Step by step: the Jacobian of the wall flux and its transpose

第一步,壁面通量只通过压力依赖内侧状态,另外依赖面积向量(代码 flux() 中 bc 为 wall 的分支):

Step 1, the wall flux depends on the interior state only through the pressure, and otherwise on the area vector (the wall branch of flux() in the code):

Hwall(UL,𝒏)=(0,  p(UL) nx,  p(UL) ny,  0)T.H_{\mathrm{wall}}(U_L,\boldsymbol n)=\bigl(0,\;p(U_L)\,n_x,\;p(U_L)\,n_y,\;0\bigr)^T.

第二步,用第 3 节的 ∂p/∂U\partial p/\partial U 做链式法则。第 0 行与第 3 行恒为零,第 1、2 行是同一个行向量乘以 nxn_x、nyn_y,所以这个 4×44\times4 块的秩是 1:

Step 2, apply the chain rule with ∂p/∂U\partial p/\partial U from Section 3. Rows 0 and 3 are identically zero and rows 1 and 2 are the same row vector times nxn_x and nyn_y, so this 4×44\times4 block has rank one:

∂Hwall∂UL=[𝟎Tnx ∂p/∂Uny ∂p/∂U𝟎T]=(γ−1)[000012(u2+v2)nx−unx−vnxnx12(u2+v2)ny−uny−vnyny0000].\frac{\partial H_{\mathrm{wall}}}{\partial U_L}=\begin{bmatrix}\boldsymbol 0^T\\ n_x\,\partial p/\partial U\\ n_y\,\partial p/\partial U\\ \boldsymbol 0^T\end{bmatrix}=(\gamma-1)\begin{bmatrix}0&0&0&0\\ \tfrac12(u^2+v^2)n_x&-un_x&-vn_x&n_x\\ \tfrac12(u^2+v^2)n_y&-un_y&-vn_y&n_y\\ 0&0&0&0\end{bmatrix}.

第三步,转置。反向扫描把面通量的伴随 Hˉ\bar H(四个分量)乘以这个块的转置;两行非零的块转置后只剩一个组合:

Step 3, transpose. The reverse sweep multiplies the adjoint Hˉ\bar H of the face flux (four components) by the transposed block; with two nonzero rows, only one combination survives:

(∂Hwall∂UL)THˉ=(∂p∂U)Tpˉ,pˉ=nxHˉρu+nyHˉρv:UˉL+=(∂p∂U)Tpˉ.\Bigl(\frac{\partial H_{\mathrm{wall}}}{\partial U_L}\Bigr)^T\bar H=\Bigl(\frac{\partial p}{\partial U}\Bigr)^T\bar p,\qquad \bar p=n_x\bar H_{\rho u}+n_y\bar H_{\rho v}:\qquad \bar U_L\mathrel{\text{+=}}\Bigl(\frac{\partial p}{\partial U}\Bigr)^T\bar p.

质量和能量两个分量 Hˉρ\bar H_\rho、HˉρE\bar H_{\rho E} 根本不经过物面传回;经过物面的只有动量分量的伴随在法向上的投影 pˉ=nxHˉρu+nyHˉρv\bar p=n_x\bar H_{\rho u}+n_y\bar H_{\rho v},也就是壁面压力的伴随,它再按 ∂p/∂U\partial p/\partial U 分给四个守恒量。第 16 节的连续物面条件约束的正是同一个组合 λρun^x+λρvn^y\lambda_{\rho u}\hat n_x+\lambda_{\rho v}\hat n_y。第四步,几何导数:状态固定时 ∂Hwall/∂𝒏=p (𝒆ρu,𝒆ρv)\partial H_{\mathrm{wall}}/\partial\boldsymbol n=p\,(\boldsymbol e_{\rho u},\boldsymbol e_{\rho v}),即上面的 H˙wall\dot H_{\mathrm{wall}} 公式中的 p n˙xp\,\dot n_x、p n˙yp\,\dot n_y 两项。

The mass and energy components Hˉρ\bar H_\rho and HˉρE\bar H_{\rho E} do not return through the wall at all; only the normal projection of the momentum adjoints, pˉ=nxHˉρu+nyHˉρv\bar p=n_x\bar H_{\rho u}+n_y\bar H_{\rho v}, passes: it is the adjoint of the wall pressure, and ∂p/∂U\partial p/\partial U distributes it over the four conserved variables. The continuous wall condition of Section 16 constrains exactly the same combination λρun^x+λρvn^y\lambda_{\rho u}\hat n_x+\lambda_{\rho v}\hat n_y. Step 4, the geometry derivative: at fixed state ∂Hwall/∂𝒏=p (𝒆ρu,𝒆ρv)\partial H_{\mathrm{wall}}/\partial\boldsymbol n=p\,(\boldsymbol e_{\rho u},\boldsymbol e_{\rho v}), which are the terms p n˙xp\,\dot n_x and p n˙yp\,\dot n_y of the H˙wall\dot H_{\mathrm{wall}} formula above.

核对:在全部 8 个物面面上,计算图对单位种子的前向结果与上面的矩阵最大相差 7×10⁻¹⁸;以伴随解 ψL\psi_L 作为 Hˉ\bar H 时,计算图的逆序结果与第三步的公式最大相差 4×10⁻¹⁹(本页代码在构建时计算)。

Check: on all 8 wall faces the forward results of the computation graph for unit seeds differ from the matrix above by at most 7×10⁻¹⁸; with the adjoint ψL\psi_L as Hˉ\bar H, the reversed graph differs from the formula of Step 3 by at most 4×10⁻¹⁹ (computed by this page’s code at build time).

远场:先旋转到法向与切向

Far field: rotate to normal and tangential components

设 𝒏^\hat{\boldsymbol n} 为指向域外的单位法向,𝒕^=(−n^y,n^x)\hat{\boldsymbol t}=(-\hat n_y,\hat n_x) 为切向。速度分解为 un=𝒖⋅𝒏^u_n=\boldsymbol u\cdot\hat{\boldsymbol n} 与 ut=𝒖⋅𝒕^u_t=\boldsymbol u\cdot\hat{\boldsymbol t},反过来 𝒖=un𝒏^+ut𝒕^\boldsymbol u=u_n\hat{\boldsymbol n}+u_t\hat{\boldsymbol t}。

Let 𝒏^\hat{\boldsymbol n} be the unit normal pointing out of the domain and 𝒕^=(−n^y,n^x)\hat{\boldsymbol t}=(-\hat n_y,\hat n_x) the tangent. Split the velocity into un=𝒖⋅𝒏^u_n=\boldsymbol u\cdot\hat{\boldsymbol n} and ut=𝒖⋅𝒕^u_t=\boldsymbol u\cdot\hat{\boldsymbol t}; conversely 𝒖=un𝒏^+ut𝒕^\boldsymbol u=u_n\hat{\boldsymbol n}+u_t\hat{\boldsymbol t}.

un=un^x+vn^y,ut=−un^y+vn^x;u=unn^x−utn^y,v=unn^y+utn^x.u_n=u\hat n_x+v\hat n_y,\quad u_t=-u\hat n_y+v\hat n_x;\qquad u=u_n\hat n_x-u_t\hat n_y,\quad v=u_n\hat n_y+u_t\hat n_x.

Riemann 不变量从哪里来

Where the Riemann invariants come from

在面附近只保留沿 𝒏^\hat{\boldsymbol n} 的变化(坐标记为 ξ\xi),得到一维 Euler 方程。对等熵流,dp=c2dρdp=c^2d\rho,而 c2=γp/ρc^2=\gamma p/\rho 给出 dc=γ−12 c dρ/ρdc=\tfrac{\gamma-1}2\,c\,d\rho/\rho,所以 dp/(ρc)=2 dc/(γ−1)dp/(\rho c)=2\,dc/(\gamma-1)。把连续方程乘 c/ρc/\rho 后与动量方程相加或相减,得到两个沿特征输运的量;熵函数 SS 与切向速度则以速度 unu_n 随流输运。

Near the face keep only variations along 𝒏^\hat{\boldsymbol n} (coordinate ξ\xi); this gives the one-dimensional Euler equations. For isentropic flow dp=c2dρdp=c^2d\rho, and c2=γp/ρc^2=\gamma p/\rho gives dc=γ−12 c dρ/ρdc=\tfrac{\gamma-1}2\,c\,d\rho/\rho, so dp/(ρc)=2 dc/(γ−1)dp/(\rho c)=2\,dc/(\gamma-1). Adding or subtracting c/ρc/\rho times the continuity equation to the momentum equation gives two quantities transported along characteristics; the entropy function SS and the tangential velocity move with the flow at speed unu_n.

(∂t+(un±c)∂ξ)r±=0,r±=un±2cγ−1;(∂t+un∂ξ)S=0,(∂t+un∂ξ)ut=0,S=pργ.\bigl(\partial_t+(u_n\pm c)\partial_\xi\bigr)r_\pm=0,\quad r_\pm=u_n\pm\frac{2c}{\gamma-1};\qquad (\partial_t+u_n\partial_\xi)S=0,\quad(\partial_t+u_n\partial_\xi)u_t=0,\quad S=\frac p{\rho^\gamma}.

亚声速时 un+c>0u_n+c>0,r+r_+ 总从域内来;un−c<0u_n-c<0,r−r_- 总从域外来。SS 与 utu_t 的速度是 unu_n:入流面(un<0u_n<0)从来流取,出流面(un>0u_n>0)从域内取。所以入流面从外部取三项信息、出流面只取一项,不能像标量那样把整个状态一起复制。这个闭合只在局部一维、等熵的意义下是特征关系,不宣称是精确无反射边界。

For subsonic flow un+c>0u_n+c>0, so r+r_+ always comes from inside; un−c<0u_n-c<0, so r−r_- always comes from outside. SS and utu_t move at unu_n: they come from the free stream at an inflow face (un<0u_n<0) and from inside at an outflow face (un>0u_n>0). An inflow face therefore takes three pieces of outside information and an outflow face only one; copying the whole state as for the scalar is inappropriate. The closure is characteristic only in the local one-dimensional, isentropic sense; it is not claimed to be exactly non-reflecting.

重建边界状态

Rebuilding the boundary state

r+=un,L+2cLγ−1,r−=un,∞−2c∞γ−1,un,b=r++r−2,cb=γ−14(r+−r−).r_+=u_{n,L}+\frac{2c_L}{\gamma-1},\quad r_-=u_{n,\infty}-\frac{2c_\infty}{\gamma-1},\quad u_{n,b}=\frac{r_++r_-}2,\quad c_b=\frac{\gamma-1}4(r_+-r_-).
(Sb, ut,b)={(S∞, ut,∞),un<0(SL, ut,L),un>0,ρb=(cb2γSb)1/(γ−1),pb=Sbρbγ.(S_b,\,u_{t,b})=\begin{cases}(S_\infty,\,u_{t,\infty}),&u_n<0\\(S_L,\,u_{t,L}),&u_n>0\end{cases},\qquad \rho_b=\Bigl(\frac{c_b^2}{\gamma S_b}\Bigr)^{1/(\gamma-1)},\quad p_b=S_b\rho_b^\gamma.
ub=un,bn^x−ut,bn^y,vb=un,bn^y+ut,bn^x,Ub=(ρb,  ρbub,  ρbvb,  pbγ−1+ρb(ub2+vb2)2)T=:G(UL;U∞,𝒏^).u_b=u_{n,b}\hat n_x-u_{t,b}\hat n_y,\quad v_b=u_{n,b}\hat n_y+u_{t,b}\hat n_x,\qquad U_b=\Bigl(\rho_b,\;\rho_bu_b,\;\rho_bv_b,\;\frac{p_b}{\gamma-1}+\frac{\rho_b(u_b^2+v_b^2)}2\Bigr)^T=:G(U_L;U_\infty,\hat{\boldsymbol n}).

ρb\rho_b 的公式来自 c2=γp/ρ=γSργ−1c^2=\gamma p/\rho=\gamma S\rho^{\gamma-1}。远场面的通量仍是第 3 节的 H(UL,Ub)H(U_L,U_b)。下表用本页代码逐步算出一个入流面和一个出流面(M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ,与页首选择无关;γ=1.4\gamma=1.4 时 2/(γ−1)=52/(\gamma-1)=5、(γ−1)/4=1/10(\gamma-1)/4=1/10、1/(γ−1)=2.51/(\gamma-1)=2.5)。交互面板可以对任意远场面重复这些步骤。

The ρb\rho_b formula follows from c2=γp/ρ=γSργ−1c^2=\gamma p/\rho=\gamma S\rho^{\gamma-1}. A far-field face still uses the flux H(UL,Ub)H(U_L,U_b) of Section 3. The table applies these steps with this page’s code to one inflow and one outflow face (M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ, independent of the header; with γ=1.4\gamma=1.4, 2/(γ−1)=52/(\gamma-1)=5, (γ−1)/4=1/10(\gamma-1)/4=1/10 and 1/(γ−1)=2.51/(\gamma-1)=2.5). The interactive panel repeats the steps for any far-field face.

量quantity入流面inflow face出流面outflow face
面 ffface ff4240
分支(冻结)branch (frozen)入流inflow出流outflow
n^=(n^x,n^y)\hat n=(\hat n_x,\hat n_y)n^=(n^x,n^y)\hat n=(\hat n_x,\hat n_y)(−0.38268, 0.92388)(0.92388, 0.38268)
内侧 ρL,  pL\rho_L,\;p_Linterior ρL,  pL\rho_L,\;p_L1.0063, 1.0110.95654, 0.94891
内侧 un,L,  ut,L,  cLu_{n,L},\;u_{t,L},\;c_Linterior un,L,  ut,L,  cLu_{n,L},\;u_{t,L},\;c_L−0.039928, −0.2427, 1.1860.21195, −0.049215, 1.1785
来流 un,∞,  ut,∞,  c∞u_{n,\infty},\;u_{t,\infty},\;c_\inftyfree stream un,∞,  ut,∞,  c∞u_{n,\infty},\;u_{t,\infty},\;c_\infty−0.051219, −0.23103, 1.18320.23103, −0.051219, 1.1832
r+=un,L+5cLr_+=u_{n,L}+5c_Lr+=un,L+5cLr_+=u_{n,L}+5c_L5.896.1044
r−=un,∞−5c∞r_-=u_{n,\infty}-5c_\inftyr−=un,∞−5c∞r_-=u_{n,\infty}-5c_\infty−5.9673−5.685
un,b=(r++r−)/2u_{n,b}=(r_++r_-)/2un,b=(r++r−)/2u_{n,b}=(r_++r_-)/2−0.0386460.20966
cb=(r+−r−)/10c_b=(r_+-r_-)/10cb=(r+−r−)/10c_b=(r_+-r_-)/101.18571.1789
SbS_b(取自)SbS_b (taken from)1 (∞)1.0098 (L)
ut,bu_{t,b}(取自)ut,bu_{t,b} (taken from)−0.23103 (∞)−0.049215 (L)
ρb=(cb2/(γSb))2.5\rho_b=(c_b^2/(\gamma S_b))^{2.5}ρb=(cb2/(γSb))2.5\rho_b=(c_b^2/(\gamma S_b))^{2.5}1.01070.9584
pb=Sbρb1.4p_b=S_b\rho_b^{1.4}pb=Sbρb1.4p_b=S_b\rho_b^{1.4}1.0150.95149
ub,  vbu_b,\;v_bub,  vbu_b,\;v_b0.22824, 0.0527090.21254, 0.034765
UbU_bUbU_b(1.0107, 0.23067, 0.053271, 2.5652)(0.9584, 0.20369, 0.033319, 2.4009)

为什么入/出流分支冻结在基准来流

Why the inflow/outflow branch is frozen at the base free stream

入流还是出流是一个判断语句,在 un=0u_n=0 处不连续。如果每次计算残差都重新判断,RR 就不再是 UU 与设计变量的光滑函数,Newton、tangent、adjoint 和差分都可能跨过这个跳跃。本页在求解之前,用基准几何的法向和基准来流方向判断每个远场面(𝒏f⋅(cos⁡θ,sin⁡θ)<0\boldsymbol n_f\cdot(\cos\theta,\sin\theta)<0 为入流),然后在整个求解和全部求导中保持不变。这样 RR 在基准点附近是一个固定的光滑函数,对 θ\theta 求导也不会切换分支。页面再检查收敛后的 un,bu_{n,b} 与冻结的分支同号。若改变 M∞M_\infty、θ\theta 或几何使某个面接近掠入射,分支可能改变,就必须重新判断并重新求解,而不能外推当前的导数。下面的 θ\theta 滑块演示这一点。

Inflow versus outflow is an if-statement, discontinuous at un=0u_n=0. Re-deciding it at every residual evaluation would make RR a non-smooth function of UU and the design variables, and Newton, tangent, adjoint and finite differences could straddle the jump. Before the solve, this page classifies each far-field face with the base geometry normal and the base free-stream direction (𝒏f⋅(cos⁡θ,sin⁡θ)<0\boldsymbol n_f\cdot(\cos\theta,\sin\theta)<0 means inflow) and keeps that choice for the whole solve and every derivative. RR is then one fixed smooth function near the base point, and differentiating with respect to θ\theta never switches a branch. The page checks that the converged un,bu_{n,b} has the frozen sign. If M∞M_\infty, θ\theta or the geometry move a face towards grazing flow, the branch may change; then reclassify and re-solve instead of extrapolating the derivative. The θ\theta slider below shows this.

伴随怎样穿过边界状态

How the adjoint passes through the boundary state

远场面通量同时经由 ULU_L 与 Ub=G(UL)U_b=G(U_L) 依赖内侧状态。记 W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t) 为 Riemann 坐标,边界闭合就是 Wb=IoutWL+(I−Iout)W∞W_b=I_{\mathrm{out}}W_L+(I-I_{\mathrm{out}})W_\infty:对角矩阵 IoutI_{\mathrm{out}} 在出域特征(从域内取的量)处为 1、其余为 0,其余的量取自来流。于是:

A far-field flux depends on the interior state both through ULU_L and through Ub=G(UL)U_b=G(U_L). With the Riemann coordinates W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t), the closure is Wb=IoutWL+(I−Iout)W∞W_b=I_{\mathrm{out}}W_L+(I-I_{\mathrm{out}})W_\infty: the diagonal matrix IoutI_{\mathrm{out}} is 1 on the outgoing characteristics (taken from inside) and 0 elsewhere; the rest comes from the free stream. Hence:

∂H∂UL=HUL+HUbGU,HUL=12Fn,U(UL)+αℓ2I,HUb=12Fn,U(Ub)−αℓ2I,\frac{\partial H}{\partial U_L}=H_{U_L}+H_{U_b}G_U,\qquad H_{U_L}=\tfrac12F_{n,U}(U_L)+\tfrac{\alpha\ell}2I,\qquad H_{U_b}=\tfrac12F_{n,U}(U_b)-\tfrac{\alpha\ell}2I,
GU=∂U∂W∣UbIout ∂W∂U∣UL,Iout={diag(1,0,0,0),un<0diag(1,0,1,1),un>0,G_U=\frac{\partial U}{\partial W}\Big|_{U_b}I_{\mathrm{out}}\,\frac{\partial W}{\partial U}\Big|_{U_L},\qquad I_{\mathrm{out}}=\begin{cases}\mathrm{diag}(1,0,0,0),&u_n<0\\\mathrm{diag}(1,0,1,1),&u_n>0\end{cases},
(∂H∂UL)THˉ=HULTHˉ+GUT(HUbTHˉ):Uˉb=HUbTHˉ,UˉL+=HULTHˉ+GUTUˉb.\Bigl(\frac{\partial H}{\partial U_L}\Bigr)^T\bar H=H_{U_L}^T\bar H+G_U^T\bigl(H_{U_b}^T\bar H\bigr):\qquad \bar U_b=H_{U_b}^T\bar H,\quad \bar U_L\mathrel{\text{+=}}H_{U_L}^T\bar H+G_U^T\bar U_b.

这里 Fn,U=∂Fn/∂UF_{n,U}=\partial F_n/\partial U 是 4×44\times4 的局部通量 Jacobian,Hˉ\bar H 是该面通量 HH 的伴随(反向扫描交给这个面的种子,第 9 节)。反向时 Hˉ\bar H 先乘 HUbTH_{U_b}^T,得到边界状态的伴随 Uˉb\bar U_b,再经过 GUTG_U^T 加到 UˉL\bar U_L:因为 IoutI_{\mathrm{out}} 把来流分量置零,只有出域特征的分量能回到内侧状态。代码不手写这个矩阵,而是逆序执行 far() 的每个标量运算;附录 A 实测两者一致到舍入误差。

Here Fn,U=∂Fn/∂UF_{n,U}=\partial F_n/\partial U is the 4×44\times4 local flux Jacobian and Hˉ\bar H the adjoint of the face flux HH (the seed the reverse sweep hands to this face, Section 9). In reverse, Hˉ\bar H is first multiplied by HUbTH_{U_b}^T, giving the adjoint Uˉb\bar U_b of the boundary state, which then passes through GUTG_U^T into UˉL\bar U_L; because IoutI_{\mathrm{out}} zeroes the free-stream components, only outgoing characteristic components return to the interior state. The code never writes this matrix by hand: it reverses every scalar operation of far(), and Appendix A measures agreement to round-off.

交互面板:θ\theta 滑块(与页首无关)用新的来流方向重新判断远场面并重新求解。特征图画出所选入流面与出流面上的四个特征速度 un−cu_n-c、unu_n、unu_n、un+cu_n+c,箭头说明每个量来自域内还是域外。表中壁面行没有边界状态,所以不显示边界声速。下方的逐步表和 GUG_U 针对所选远场面现场计算,并把当前伴随解 ψL\psi_L 作为 Hˉ\bar H 展示两条反传路径。

Interactive panel: the θ\theta slider (independent of the header) reclassifies the far-field faces for the new free-stream direction and re-solves. The characteristic diagram draws the four speeds un−cu_n-c, unu_n, unu_n, un+cu_n+c at the selected inflow and outflow faces; arrows show whether each quantity comes from inside or outside. Wall rows have no boundary state, so they show no boundary sound speed. The step table and GUG_U below are computed live for the selected far-field face, with the current adjoint ψL\psi_L as Hˉ\bar H to show the two reverse paths.

图 4.2Figure 4.2
第 5 节Section 5

残差:同一个面,两个相反贡献

Residual: one face, two opposite contributions

先固定离散问题与符号

Fix the discrete problem and its notation

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

Each cell stores 4 unknowns. Below, 𝐔\mathbf U stacks all locations and components in code order. 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 是本页全部设计变量排成的向量:来流马赫数 M∞M_\infty、攻角 θ\theta 与全部节点坐标 XX。第 11 节的 ATψ=−JUTA^T\psi=-J_U^T 与 dJ/dD=JD+ψTRDdJ/dD=J_D+\psi^TR_D 就是下面方框里的两式(行、列只差一次转置)。局部通量 Jacobian(如 ∂Fn/∂U\partial F_n/\partial U)与全局残差 Jacobian AA 是不同的对象。每个位置的局部状态 UiU_i 是一个小列向量,全局的 𝐔\mathbf U 才是 Jacobian 的输入。

DD stacks all design variables of this page: the free-stream Mach number M∞M_\infty, the angle θ\theta and all node coordinates XX. The ATψ=−JUTA^T\psi=-J_U^T and dJ/dD=JD+ψTRDdJ/dD=J_D+\psi^TR_D of Section 11 are the two boxed formulas below (row and column forms differ by one transpose). A local flux Jacobian such as ∂Fn/∂U\partial F_n/\partial U is a different object from the global residual Jacobian AA. A local state UiU_i is a small column vector; the global 𝐔\mathbf U is the input of the Jacobian.

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},
JUT=(∂J∂𝐔)T∈ℝnu,JDT=(∂J∂D)T∈ℝna.J_U^T=\Big(\frac{\partial J}{\partial \mathbf U}\Big)^{T}\in\mathbb R^{n_u},\quad J_D^T=\Big(\frac{\partial J}{\partial D}\Big)^{T}\in\mathbb R^{n_a}.

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

Both JUTJ_U^T and JDTJ_D^T 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 of Section 16 and Appendix A.

一个控制体现在对应四条积分平衡方程。RiR_i 是四分量流出通量之和,稳态要求全部分量为零。内部面只计算一次 HH,并向左加、向右减。这使全域求和时内部贡献精确抵消,是几何导数和伴随也必须保留的计算结构。

Each volume now has four integrated balance equations. RiR_i sums its outward fluxes; steady state requires every component to vanish. An internal face computes HH once, adds it to the left and subtracts it from the right. Internal contributions cancel globally, a structure also preserved by geometry derivatives and adjoints.

Ri(U,D)=∑fℬifHf,ℬLf=+1,ℬRf=−1,R(U∗,D)=0.R_i(U,D)=\sum_f\mathcal B_{if}H_f,\qquad \mathcal B_{Lf}=+1,\quad \mathcal B_{Rf}=-1,\qquad R(U^*,D)=0.

DD 是全部设计变量(来流 M∞M_\infty、θ\theta 与节点坐标)排成的向量,第 10 节给出它的内容。这里没有除以控制体体积。若写非定常方程,则是 Vi dUi/dt+Ri=0V_i\,dU_i/dt+R_i=0。当前 Newton 法求解的对象是积分残差,其 Jacobian、收敛阈值和伴随乘子都沿用这一约定。播放器逐面显示读取、通量、左累加、右累加,四个分量可以分别查看;“播放”按钮会自动逐步前进。

DD stacks all design variables (the free-stream M∞M_\infty, θ\theta and the node coordinates); Section 10 lists them. Residuals are not divided by volume. An unsteady equation would be Vi dUi/dt+Ri=0V_i\,dU_i/dt+R_i=0. Newton solves the integrated residual, so its Jacobian, tolerances and adjoint multipliers use that convention. The player exposes gather, flux, left scatter and right scatter for each face, with all four components selectable; the Play button steps automatically.

图 5.1 把 ℬ\mathcal B 整个画出来,列的顺序就是面循环的顺序;图 5.2 放大其中一列:一个面的通量怎样进入 96 维的残差向量。

Figure 5.1 draws all of ℬ\mathcal B, with its columns in the order of the face loop; Figure 5.2 zooms into one column: how the flux of one face enters the 96-component residual vector.

内部面(32)interior faces (32)物面(8)wall (8)远场(8)far field (8)012345678910111213141516171819202122230816243240面 f(按面循环的顺序)face f (in the order of the face loop)控制体volumei𝓑if=+1:Hf 加到 L 行𝓑if=+1: Hf added to row L𝓑if=−1:从 R 行减去𝓑if=−1: subtracted from row R第 2 节的紫色面violet face of Section 2
图 5.1 · 残差公式 Ri=∑fℬifHfR_i=\sum_f\mathcal B_{if}H_f 中的有向关联矩阵 ℬ\mathcal B:每行一个控制体,每列一个面,列的顺序就是代码中面循环的顺序(先 32 个内部面,按 LL、再按 RR 排序;然后 8 个物面面,最后 8 个远场面)。内部面的一列恰好有一个 +1+1 和一个 −1-1,列和为零,所以全域求和时内部通量两两抵消;边界面的一列只有 +1+1。一行中的非零元就是该控制体的所有面。Figure 5.1 · The oriented incidence matrix ℬ\mathcal B of the residual formula Ri=∑fℬifHfR_i=\sum_f\mathcal B_{if}H_f: one row per control volume and one column per face, in the order of the code's face loop (first the 32 interior faces, sorted by LL and then RR; then the 8 wall faces; last the 8 far-field faces). An interior column holds exactly one +1+1 and one −1-1 and sums to zero, so interior fluxes cancel pairwise in a global sum; a boundary column holds a single +1+1. The nonzeros of a row are all the faces of that volume.
0148121620残差 Rresidual RR ∈ ℝ96控制体volume面 f=0 只算一次 Hfface f=0 computes Hf onceHf = (−0.12, −0.407,0.295, −0.416)RL += HfRR −= Hf
图 5.2 · 面 f=0f=0 对 96 维残差向量的贡献:数值通量 HfH_f 只算一次(这里是收敛解上的值),它的四个分量加到控制体 L=0L=0 的四行,再从控制体 R=1R=1 的四行减去。其余各块不受这个面影响。前向和伴随都保留这一结构:tangent 用同样的符号分发 H˙f\dot H_f,伴随则把两块的残差种子合成 Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R(第 9 节)。Figure 5.2 · The contribution of face f=0f=0 to the 96-component residual vector: the numerical flux HfH_f is computed once (here its value at the converged solution), its four components are added to the four rows of volume L=0L=0 and subtracted from the four rows of volume R=1R=1. No other block is touched by this face. The tangent and the adjoint keep this structure: the tangent scatters H˙f\dot H_f with the same signs, and the adjoint combines the two blocks' residual seeds into Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R (Section 9).

逐步推导:内部贡献在全域求和中相消

Step by step: interior contributions cancel in the global sum

第一步,把所有控制体的第 kk 个分量相加,并交换两个有限求和的次序:

Step 1, add component kk over all volumes and exchange the two finite sums:

∑iRi,k=∑i∑fℬifHf,k=∑fHf,k∑iℬif.\sum_iR_{i,k}=\sum_i\sum_f\mathcal B_{if}H_{f,k}=\sum_fH_{f,k}\sum_i\mathcal B_{if}.

第二步,内部面的一列恰有 +1+1 和 −1-1 各一个,列和为零;边界面的一列只有一个 +1+1。第三步,于是全域残差之和只剩边界通量:

Step 2, an interior column holds one +1+1 and one −1-1 and sums to zero; a boundary column holds a single +1+1. Step 3, so the sum of all residuals keeps only the boundary fluxes:

∑iRi=∑f∈wall ∪ farHf.\sum_iR_i=\sum_{f\in\mathrm{wall}\,\cup\,\mathrm{far}}H_f.

这就是离散守恒:内部面的通量只在控制体之间搬运守恒量,不产生也不消灭。它依赖“每个面只算一次 HfH_f”:如果左右两侧各算一次通量(例如用了不同的边界状态),抵消就不再精确。稳态时两边都为零,所以核对取 Newton 的第一个迭代 U1U^1,那里残差不为零。

This is discrete conservation: interior fluxes only move conserved quantities between volumes, creating or destroying nothing. It relies on computing HfH_f once per face: if each side computed its own flux (for example with different boundary states), the cancellation would no longer be exact. At the steady state both sides vanish, so the check uses the first Newton iterate U1U^1, where the residual is not zero.

核对:在 U1U^1 上 ∥R∥2=5.4×10−3\|R\|_2=5.4×10⁻³,四个分量的全域和为 (−7.3×10⁻⁵, −2.9×10⁻⁴, −7.8×10⁻⁴, 1.0×10⁻³),与边界通量之和最大相差 3×10⁻¹⁶(本页代码在构建时计算)。

Check: at U1U^1, ∥R∥2=5.4×10−3\|R\|_2=5.4×10⁻³; the global sums of the four components are (−7.3×10⁻⁵, −2.9×10⁻⁴, −7.8×10⁻⁴, 1.0×10⁻³) and differ from the sums of the boundary fluxes by at most 3×10⁻¹⁶ (computed by this page’s code at build time).

R[:] = 0
for face in faces:
    UL = U[L]; UR = U[R] if R >= 0 else boundary(UL)
    H = flux_or_wall(UL, UR, n)
    R[L,:] += H
    if R >= 0: R[R,:] -= H
图 5.3Figure 5.3
第 6 节Section 6

Jacobian、Newton 与 GS 预处理 GMRES

Jacobian, Newton and GS-preconditioned GMRES

标量 Jacobian 的一个非零数,现在变成一个 4×44\times4 状态块。内部通量把相邻两个控制体耦合;边界状态又引入内侧状态的链式导数。我们保持精确算子 matrix-free,用 Newton 解非线性残差,再由 GMRES 解每次线性修正。

A scalar Jacobian entry becomes a 4×44\times4 state block. Interior faces couple neighbouring volumes, while boundary states add chain-rule terms. The exact operator stays matrix-free. Newton solves the nonlinear residual and GMRES solves each linear correction.

A=∂R∂U,A(Uk) ΔU=−R(Uk),Uk+1=Uk+τ ΔU.A=\frac{\partial R}{\partial U},\qquad A(U^k)\,\Delta U=-R(U^k),\qquad U^{k+1}=U^k+\tau\,\Delta U.

GS 预处理只取固定数值耗散形成的图 Laplacian 近似:内部面给两侧对角加 αℓ/2\alpha\ell/2、相邻块加 −αℓI4/2-\alpha\ell I_4/2;边界只增加对角近似。取它的对角部分 diag⁡(P)\operatorname{diag}(P) 与严格下三角部分 NN,P=diag⁡(P)+NP=\operatorname{diag}(P)+N,做一次前向 GS 三角求解;伴随用 PTP^T 的反向扫描。这个 PP 省略了压力和特征边界导数,仅负责加速,最终线性残差仍用完整的 AA 或 ATA^T 检查。

The GS preconditioner approximates only the numerical dissipation: internal faces add αℓ/2\alpha\ell/2 to both diagonals and −αℓI4/2-\alpha\ell I_4/2 to neighbouring blocks; boundaries add a diagonal approximation. With its diagonal part diag⁡(P)\operatorname{diag}(P) and strictly lower part NN, one forward GS solve applies the inverse of P=diag⁡(P)+NP=\operatorname{diag}(P)+N. The adjoint uses a backward solve with PTP^T. Pressure and characteristic-boundary derivatives are omitted only from PP, not from the exact operator used to check convergence.

AP−1g=b,x=P−1g,ATP−Tg=b,x=P−Tg.AP^{-1}g=b,\quad x=P^{-1}g,\qquad A^TP^{-T}g=b,\quad x=P^{-T}g.

图 6.1 画出本页收敛解上 AA 的块结构与 PP 的块结构。

Figure 6.1 shows the block structure of AA at this page’s converged solution next to that of PP.

A 的块结构(24×24 个 4×4 块)block pattern of A (24×24 blocks of 4×4)004488121216162020GS 预处理 P = diag(P) + NGS preconditioner P = diag(P) + N004488121216162020对角块diagonal block相邻控制体的块(颜色越深,块范数越大)neighbour block (darker = larger block norm)N:严格下三角,−(αℓf/2) I4N: strictly lower, −(αℓf/2) I4A 中有、P 中略去的块block of A left out of P2.950.000.000.000.002.950.000.000.000.002.950.000.000.000.002.95A9,9(内部)A9,9 (interior)1.960.260.100.00−0.022.110.03−0.100.000.022.03−0.04−0.210.870.332.05A0,0(物面)A0,0 (wall)P9,9 = 2.95 I4P0,0 = 2.51 I4
图 6.1 · 本页格心格式在收敛解上的 96×96 Jacobian AA,按 24×24 个 4×44\times4 块画出(左,共 88 个非零块),以及 GS 预处理 P=diag⁡(P)+NP=\operatorname{diag}(P)+N 的块结构(中)。AA 的一个非对角块非零,当且仅当两个控制体共享一个面,所以块结构与网格的连接关系相同、并且对称;数值上 AA 并不对称。PP 只保留对角块和编号较小一侧的相邻块,每块都是标量乘 I4I_4;空心方框是 AA 中有而 PP 略去的块。右:内部控制体 9 的对角块正好是 P9,9P_{9,9},物面控制体 0 的对角块则是满的 4×44\times4 矩阵(第 6 节的逐步推导解释原因)。Figure 6.1 · The 96×96 Jacobian AA of this page's cell-centred scheme at the converged solution, drawn as 24×24 blocks of 4×44\times4 (left; 88 nonzero blocks), and the block structure of the GS preconditioner P=diag⁡(P)+NP=\operatorname{diag}(P)+N (middle). An off-diagonal block of AA is nonzero exactly when two volumes share a face, so the block pattern equals the mesh connectivity and is symmetric; the numbers in AA are not. PP keeps only the diagonal blocks and the neighbour blocks on the lower-numbered side, each a scalar times I4I_4; hollow squares are blocks of AA that PP leaves out. Right: the diagonal block of the interior volume 9 is exactly P9,9P_{9,9}, while the diagonal block of the wall volume 0 is a full 4×44\times4 matrix (the step-by-step derivation of Section 6 explains why).

逐步推导:内部控制体的对角块正好是 PiiP_{ii}

Step by step: the diagonal block of an interior volume is exactly PiiP_{ii}

第一步,控制体 ii 的对角块收集它的每个面对 UiU_i 的导数,符号就是 ℬif\mathcal B_{if}:ii 是 LL 时取 HULH_{U_L},是 RR 时取 −HUR-H_{U_R}。

Step 1, the diagonal block of volume ii collects the derivative of each of its faces with respect to UiU_i, with the sign ℬif\mathcal B_{if}: HULH_{U_L} where ii is LL, and −HUR-H_{U_R} where ii is RR.

Aii=∑f: L(f)=iHUL(f)−∑f: R(f)=iHUR(f).A_{ii}=\sum_{f:\,L(f)=i}H_{U_L}(f)-\sum_{f:\,R(f)=i}H_{U_R}(f).

第二步,代入第 8 节推导的线性化 HUL=12Fn,U(UL)+αℓf2IH_{U_L}=\tfrac12F_{n,U}(U_L)+\tfrac{\alpha\ell_f}2I、HUR=12Fn,U(UR)−αℓf2IH_{U_R}=\tfrac12F_{n,U}(U_R)-\tfrac{\alpha\ell_f}2I。两种情况都在 UiU_i 上求值,并且都可以用外法向 𝒏if=ℬif𝒏f\boldsymbol n_{if}=\mathcal B_{if}\boldsymbol n_f 写成同一个形式(Fn,UF_{n,U} 对 𝒏\boldsymbol n 是线性的,−F𝒏,U=F−𝒏,U-F_{\boldsymbol n,U}=F_{-\boldsymbol n,U}):

Step 2, insert the linearisation derived in Section 8, HUL=12Fn,U(UL)+αℓf2IH_{U_L}=\tfrac12F_{n,U}(U_L)+\tfrac{\alpha\ell_f}2I and HUR=12Fn,U(UR)−αℓf2IH_{U_R}=\tfrac12F_{n,U}(U_R)-\tfrac{\alpha\ell_f}2I. Both are evaluated at UiU_i, and both take the same form with the outward vector 𝒏if=ℬif𝒏f\boldsymbol n_{if}=\mathcal B_{if}\boldsymbol n_f (Fn,UF_{n,U} is linear in 𝒏\boldsymbol n, so −F𝒏,U=F−𝒏,U-F_{\boldsymbol n,U}=F_{-\boldsymbol n,U}):

Aii=∑f(12F𝒏if,U(Ui)+αℓf2I)=12F∑f𝒏if, U(Ui)+(∑fαℓf2)I.A_{ii}=\sum_f\Bigl(\tfrac12F_{\boldsymbol n_{if},U}(U_i)+\tfrac{\alpha\ell_f}2I\Bigr)=\tfrac12F_{\sum_f\boldsymbol n_{if},\,U}(U_i)+\Bigl(\sum_f\tfrac{\alpha\ell_f}2\Bigr)I.

第三步,第 2 节证明了 ∑f𝒏if=0\sum_f\boldsymbol n_{if}=0,第一项消失;对不含边界面的控制体 ii,剩下的正是 GS 预处理的对角元:

Step 3, Section 2 showed ∑f𝒏if=0\sum_f\boldsymbol n_{if}=0; the first term vanishes, and for a volume ii without boundary faces what remains is exactly the diagonal of the GS preconditioner:

Aii=Pii=(∑f∋iαℓf2)I4.A_{ii}=P_{ii}=\Bigl(\sum_{f\ni i}\frac{\alpha\ell_f}2\Bigr)I_4.

第四步,边界控制体不同:它的物面面用第 4 节的 ∂Hwall/∂UL\partial H_{\mathrm{wall}}/\partial U_L 代替 12Fn,U+αℓ2I\tfrac12F_{n,U}+\tfrac{\alpha\ell}2I,远场面多出 HUbGUH_{U_b}G_U,第二步的抵消不再成立,AiiA_{ii} 是满的 4×44\times4 块。第五步,非对角块 ALR=HUR=12Fn,U(UR)−αℓf2IA_{LR}=H_{U_R}=\tfrac12F_{n,U}(U_R)-\tfrac{\alpha\ell_f}2I:PP 只保留其中的耗散部分 −αℓf2I-\tfrac{\alpha\ell_f}2I,并且只放在下三角(编号较大的一行)。所以 PP 在内部控制体的对角上是精确的,非对角块只近似了耗散部分。

Step 4, boundary volumes differ: a wall face contributes ∂Hwall/∂UL\partial H_{\mathrm{wall}}/\partial U_L of Section 4 instead of 12Fn,U+αℓ2I\tfrac12F_{n,U}+\tfrac{\alpha\ell}2I, and a far-field face adds HUbGUH_{U_b}G_U; the cancellation of Step 2 fails and AiiA_{ii} is a full 4×44\times4 block. Step 5, an off-diagonal block is ALR=HUR=12Fn,U(UR)−αℓf2IA_{LR}=H_{U_R}=\tfrac12F_{n,U}(U_R)-\tfrac{\alpha\ell_f}2I: PP keeps only its dissipation part −αℓf2I-\tfrac{\alpha\ell_f}2I, and only in the lower triangle (the row with the larger index). So PP is exact on the diagonal of interior volumes and approximates the off-diagonal blocks by their dissipation part.

核对:8 个内部控制体上 max⁡∣Aii−Pii∣=4×10−16\max|A_{ii}-P_{ii}|=4×10⁻¹⁶;其余 16 个边界控制体中差别最小的一个也有 0.477(本页代码在构建时计算)。

Check: over the 8 interior volumes max⁡∣Aii−Pii∣=4×10−16\max|A_{ii}-P_{ii}|=4×10⁻¹⁶; among the other 16 boundary volumes even the smallest difference is 0.477 (computed by this page’s code at build time).

逐步推导:GS 三角求解、它的转置与伴随中的 PTP^T

Step by step: the GS triangular solve, its transpose, and PTP^T in the adjoint

第一步,P=diag⁡(P)+NP=\operatorname{diag}(P)+N 是块下三角矩阵(每块都是标量乘 I4I_4)。GMRES 每一步都要把 P−1P^{-1} 作用在一个向量 gg 上,也就是解 Px=gPx=g:按控制体编号从小到大做前向替换,这就是代码 precondition() 中不转置的分支:

Step 1, P=diag⁡(P)+NP=\operatorname{diag}(P)+N is block lower triangular (every block a scalar times I4I_4). Every GMRES step applies P−1P^{-1} to a vector gg, i.e. solves Px=gPx=g by forward substitution in increasing volume order, the non-transposed branch of precondition() in the code:

xi=Pii−1(gi−∑j<iNijxj),i=0,1,…,nu4−1.x_i=P_{ii}^{-1}\Bigl(g_i-\sum_{j<i}N_{ij}x_j\Bigr),\qquad i=0,1,\dots,\tfrac{n_u}4-1.

第二步,PT=diag⁡(P)+NTP^T=\operatorname{diag}(P)+N^T 是块上三角矩阵,PTx=gP^Tx=g 要按编号从大到小做后向替换。代码的转置分支先乘以 Pii−1P_{ii}^{-1},再把 NijxiN_{ij}x_i 从所有 j<ij<i 的分量中减去,是同一个后向替换的“按列”写法:

Step 2, PT=diag⁡(P)+NTP^T=\operatorname{diag}(P)+N^T is block upper triangular, so PTx=gP^Tx=g needs backward substitution in decreasing order. The transposed branch of the code first applies Pii−1P_{ii}^{-1} and then subtracts NijxiN_{ij}x_i from every component j<ij<i, the column-oriented form of the same backward substitution:

xi=Pii−1(gi−∑j>iNjixj),i=nu4−1,…,1,0.x_i=P_{ii}^{-1}\Bigl(g_i-\sum_{j>i}N_{ji}x_j\Bigr),\qquad i=\tfrac{n_u}4-1,\dots,1,0.

第三步,右预处理的 GMRES 求解上面的 AP−1g=bAP^{-1}g=b、x=P−1gx=P^{-1}g。伴随方程的矩阵是 ATA^T;既然 P≈AP\approx A,就有 PT≈ATP^T\approx A^T,所以伴随用 P−TP^{-T}。AA 与 PP 都不对称,拿 P−1P^{-1} 去预处理 ATA^T 就是用错了矩阵。第四步,GS 定常迭代 x←x+P−1(b−Ax)x\leftarrow x+P^{-1}(b-Ax) 的误差每步乘以 I−P−1AI-P^{-1}A,只有它的谱半径小于 1 才收敛,而且收敛速度就由谱半径决定;GMRES 用同样的矩阵乘积,却在已生成的 Krylov 子空间里使残差最小。

Step 3, right-preconditioned GMRES solves the AP−1g=bAP^{-1}g=b, x=P−1gx=P^{-1}g above. The adjoint matrix is ATA^T; since P≈AP\approx A, also PT≈ATP^T\approx A^T, so the adjoint uses P−TP^{-T}. Neither AA nor PP is symmetric, and preconditioning ATA^T with P−1P^{-1} would use the wrong matrix. Step 4, the stationary GS iteration x←x+P−1(b−Ax)x\leftarrow x+P^{-1}(b-Ax) multiplies its error by I−P−1AI-P^{-1}A in every step; it converges only if the spectral radius of that matrix is below one, and the radius sets its speed. GMRES uses the same matrix products but minimises the residual over the Krylov space built so far.

核对:对两个固定的向量 gg、g′g',⟨P−1g,g′⟩\langle P^{-1}g,g'\rangle 与 ⟨g,P−Tg′⟩\langle g,P^{-T}g'\rangle 的相对差为 6×10⁻¹⁶,所以代码的反向扫描正是 P−1P^{-1} 的转置;定常迭代的残差每步约乘以 0.936(Newton 方程)与 0.941(伴随方程),也就是每下降一个数量级约需 35 与 38 步(本页代码在构建时计算)。

Check: for two fixed vectors gg and g′g', ⟨P−1g,g′⟩\langle P^{-1}g,g'\rangle and ⟨g,P−Tg′⟩\langle g,P^{-T}g'\rangle differ by 6×10⁻¹⁶ relatively, so the backward sweep of the code is exactly the transpose of P−1P^{-1}; the stationary iteration multiplies the residual by about 0.936 per step (Newton system) and 0.941 (adjoint system), about 35 and 38 steps per decade (computed by this page’s code at build time).

图 6.2 把三种解法放在一起,都迭代到舍入误差的底部。

Figure 6.2 puts the three solvers side by side, each run to the round-off floor.

第一个 Newton 线性方程:A(U⁰)ΔU = −R(U⁰)first Newton system: A(U⁰)ΔU = −R(U⁰)10⁻¹⁵10⁻¹²10⁻⁹10⁻⁶10⁻³10⁰0100200300400迭代次数(每次一个矩阵乘积)iterations (one matrix product each)相对残差 ‖r‖/‖b‖relative residual ‖r‖/‖b‖GS 定常迭代GS stationary iterationGMRES(无预处理)GMRES (no preconditioner)GMRES + GS伴随方程:AT​ψ = −JUT​(用 PT​)adjoint system: AT​ψ = −JUT​ (with PT​)10⁻¹⁵10⁻¹²10⁻⁹10⁻⁶10⁻³10⁰0100200300400500迭代次数(每次一个矩阵乘积)iterations (one matrix product each)相对残差 ‖r‖/‖b‖relative residual ‖r‖/‖b‖GS 定常迭代GS stationary iterationGMRES(无预处理)GMRES (no preconditioner)GMRES + GS
图 6.2 · 同一个线性方程的三种解法,都迭代到舍入误差的底部:GS 定常迭代 x←x+P−1(b−Ax)x\leftarrow x+P^{-1}(b-Ax)(紫色),不带预处理的 GMRES(灰色虚线),以及本页使用的 GS 预处理 GMRES。左:第一步 Newton 的方程(从均匀来流 U0=U∞U^0=U_\infty 出发);右:收敛解上的伴随方程,定常迭代与预处理都用 PTP^T。GS 定常迭代每步的残差大约乘以 0.936(左)与 0.941(右),需要 404 与 476 步;GMRES 用同样的矩阵乘积次数,67/70 步(无预处理)与 54/57 步(GS 预处理)就到底。Figure 6.2 · Three solvers for the same linear system, each run to the round-off floor: the GS stationary iteration x←x+P−1(b−Ax)x\leftarrow x+P^{-1}(b-Ax) (violet), GMRES without preconditioning (dashed grey), and the GS-preconditioned GMRES this page uses. Left: the first Newton system (starting from the uniform free stream U0=U∞U^0=U_\infty); right: the adjoint system at the converged solution, where both the stationary iteration and the preconditioner use PTP^T. Each GS step multiplies the residual by about 0.936 (left) and 0.941 (right), so it needs 404 and 476 steps; GMRES, with the same count of matrix products, reaches the floor in 67/70 steps without preconditioning and 54/57 with GS.

修正若使密度或压力失去正值,或离开本例设定的亚声速范围,就减小步长 τ\tau;同时要求残差下降。这个线搜索只用于找到收敛解。后面的稳态 tangent/adjoint 对 R(U∗)=0R(U^*)=0 求导,不是对接受了几次回退这一离散逻辑求导。下图的播放器逐次显示 Newton 迭代的流场:从均匀来流 U∞U_\infty 出发,每一步都同时修正整个流场。

A line search reduces the step τ\tau if density or pressure loses positivity, the state leaves the permitted subsonic range, or the residual fails to decrease. It only locates the converged solution. The steady tangent/adjoint differentiates R(U∗)=0R(U^*)=0, not the discrete history of accepted line-search decisions. The player below shows the Newton iterates as fields: starting from the uniform U∞U_\infty, each step corrects the whole field at once.

图 6.3Figure 6.3
第 7 节Section 7

目标函数:物面压力合力

Objective: integrated pressure force on the body

从拟一维的推力目标推广到二维物面压力合力。𝒏f\boldsymbol n_f 指向流体域外、也就是物体内部,所以流体施加给物体的压力合力为 ∑p 𝒏f\sum p\,\boldsymbol n_f。页面可选固定坐标方向的 x 分量 JxJ_x 或 y 分量 JyJ_y(单位向量 𝒆d\boldsymbol e_d 取 𝒆x\boldsymbol e_x 或 𝒆y\boldsymbol e_y);这里没有除以动压或参考长度,也不是随攻角旋转的气动升阻力系数。

Extend the quasi-one-dimensional thrust objective to the pressure force on the body. 𝒏f\boldsymbol n_f points out of the fluid domain, into the body, so the pressure force of the fluid on the body is ∑p 𝒏f\sum p\,\boldsymbol n_f. Choose its fixed Cartesian x component JxJ_x or y component JyJ_y (unit vector 𝒆d\boldsymbol e_d equal to 𝒆x\boldsymbol e_x or 𝒆y\boldsymbol e_y). These are raw force integrals: they are not divided by the dynamic pressure or a reference length, and they are not lift or drag coefficients in axes rotated with the incidence.

J(U,D)=∑f∈wallp(UL(f)) 𝒏f⋅𝒆d,J˙=∑wall((𝒏f⋅𝒆d) p˙+p 𝒏˙f⋅𝒆d).J(U,D)=\sum_{f\in\mathrm{wall}}p\bigl(U_{L(f)}\bigr)\,\boldsymbol n_f\cdot\boldsymbol e_d,\qquad \dot J=\sum_{\mathrm{wall}}\Bigl((\boldsymbol n_f\cdot\boldsymbol e_d)\,\dot p+p\,\dot{\boldsymbol n}_f\cdot\boldsymbol e_d\Bigr).

第一项给状态偏导数 JUJ_U,第二项给固定状态下的直接几何偏导数 JDJ_D。遗漏第二项,会导致形状梯度即使求解器收敛也不正确。格点版本在每个边界半面用相应端点状态的压力,因此它和格心格式是两个明确不同的离散目标。若需要随来流旋转的阻力与升力,就把两个分量旋转到来流方向:

The first term gives the state derivative JUJ_U, the second the direct geometry derivative JDJ_D at fixed state. Omitting the latter gives an incorrect shape gradient even with fully converged solvers. The node scheme uses the associated endpoint pressure on each boundary half-face, so the two schemes define distinct discrete objectives. Drag and lift in the free-stream axes are the two components rotated into the free-stream direction:

J∥=Jxcos⁡θ+Jysin⁡θ,J⊥=−Jxsin⁡θ+Jycos⁡θ,J_\parallel=J_x\cos\theta+J_y\sin\theta,\qquad J_\perp=-J_x\sin\theta+J_y\cos\theta,

J∥J_\parallel 是沿来流方向的阻力,J⊥J_\perp 是垂直来流的升力;它们仍是每单位展长的力,而不是系数。

J∥J_\parallel is the drag along the free stream and J⊥J_\perp the lift normal to it; both are still forces per unit span, not coefficients.

图 7.1 画出 JJ 的每一项;图 7.2 按物面位置把这些项累加起来。

Figure 7.1 draws every term of JJ; Figure 7.2 adds the terms up along the wall.

每个物面面的 pf nfpf nf on every wall face(pf − p∞) nf(箭头比例放大 6.9 倍)(pf − p∞) nf (arrow scale × 6.9)J= 0.1= 0.1∑ nf = (−6×10⁻¹⁷, −6×10⁻¹⁷)Jx = 0.195,Jy = 0.00701(J 的箭头按 1/2 画)Jx = 0.195, Jy = 0.00701 (arrow J drawn at 1/2)
图 7.1 · 目标函数的每一项:物面面 ff 上的压力载荷 pf𝒏fp_f\boldsymbol n_f(左,箭头从面中点指向物体内部),以及减去来流压力后的 (pf−p∞)𝒏f(p_f-p_\infty)\boldsymbol n_f(右,放大后画出)与它们的合力 J=(Jx,Jy)J=(J_x,J_y)(紫色,箭头长度按 1/2 画出)。左图的箭头几乎等长、两两相对:物面是闭合多边形,∑f𝒏f=0\sum_f\boldsymbol n_f=0(左下,本页代码计算),所以均匀压力 p∞p_\infty 对合力没有贡献,JJ 完全由压力的变化 pf−p∞p_f-p_\infty 决定。两图各有一个长度为 0.1 的比例箭头。(M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ。)Figure 7.1 · Every term of the objective: the pressure load pf𝒏fp_f\boldsymbol n_f on wall face ff (left; arrows from the face midpoint into the body), and the load minus the free-stream pressure, (pf−p∞)𝒏f(p_f-p_\infty)\boldsymbol n_f (right, magnified), with their resultant J=(Jx,Jy)J=(J_x,J_y) (violet, its arrow drawn at 1/2 of the common scale). The arrows on the left are almost equal and opposite in pairs: the wall is a closed polygon, ∑f𝒏f=0\sum_f\boldsymbol n_f=0 (bottom left, computed by this page's code), so a uniform pressure p∞p_\infty contributes nothing and JJ is set entirely by the pressure changes pf−p∞p_f-p_\infty. Each panel has a scale arrow of length 0.1. (M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ.)
−0.19500.1950°90°180°270°360°面中点绕物体中心的角度angle of the face midpoint around the body centreJx 的逐面贡献(累加终点 Jx = 0.195)face contributions to Jx (running sum ends at 0.195)−0.045700.04570°90°180°270°360°面中点绕物体中心的角度angle of the face midpoint around the body centreJy 的逐面贡献(累加终点 Jy = 0.00701)face contributions to Jy (running sum ends at 0.00701)一个面的贡献 (pf − p∞)(nf·ed)one face: (pf − p∞)(nf·ed)按角度顺序的累加和running sum in angle order
图 7.2 · JxJ_x(左)与 JyJ_y(右)怎样由物面面逐项相加:柱是每个面的贡献 (pf−p∞) 𝒏f⋅𝒆d(p_f-p_\infty)\,\boldsymbol n_f\cdot\boldsymbol e_d,按面中点绕物体中心 (0.1,−0.04)(0.1,-0.04) 的角度排列;紫线是按这个顺序的累加和,终点就是目标值。JxJ_x 的各项都为正:法向指向下游(nx>0n_x>0,迎风一侧)的面上压力都高于 p∞p_\infty,法向指向上游的背风面上压力都低于 p∞p_\infty,两者都把物体推向下游,于是逐项累加;JyJ_y 的正负贡献大量抵消,最后的 JyJ_y 比最大的单项还小,这也是第 13 节的差分需要足够精确的原因之一。Figure 7.2 · How JxJ_x (left) and JyJ_y (right) add up over the wall faces: each bar is one face's contribution (pf−p∞) 𝒏f⋅𝒆d(p_f-p_\infty)\,\boldsymbol n_f\cdot\boldsymbol e_d, placed at the angle of the face midpoint around the body centre (0.1,−0.04)(0.1,-0.04); the violet line is the running sum in that order, ending at the objective. Every contribution to JxJ_x is positive: wherever the normal points downstream (nx>0n_x>0, the upstream side) the pressure is above p∞p_\infty, and on every downstream face, where the normal points upstream, it is below; both push the body downstream and the terms accumulate. For JyJ_y positive and negative contributions cancel heavily and the final JyJ_y is smaller than its largest single term, one reason why the differences of Section 13 must be accurate.

逐步推导:目标函数的偏导数 JUJ_U 与 JXJ_X

Step by step: the partial derivatives JUJ_U and JXJ_X of the objective

第一步,状态只通过物面单元的压力进入 JJ。对控制体 ii 的四个分量求偏导,用第 3 节的 ∂p/∂U\partial p/\partial U:

Step 1, the state enters JJ only through the pressure of the wall cells. Differentiate with respect to the four components of volume ii, using ∂p/∂U\partial p/\partial U of Section 3:

∂J∂Ui=(∑f∈wall, L(f)=i𝒏f⋅𝒆d)∂p∂U(Ui).\frac{\partial J}{\partial U_i}=\Bigl(\sum_{f\in\mathrm{wall},\,L(f)=i}\boldsymbol n_f\cdot\boldsymbol e_d\Bigr)\frac{\partial p}{\partial U}(U_i).

所以 JUJ_U 只在 8 个物面控制体的行上非零,并且每一行都与 ∂p/∂U\partial p/\partial U 成比例。第 11 节的伴随右端 −JUT-J_U^T 就是它。第二步,几何。面积向量 𝒏f=(𝒙e−𝒙s)⊥\boldsymbol n_f=(\boldsymbol x_e-\boldsymbol x_s)^{\perp}(𝒙s\boldsymbol x_s、𝒙e\boldsymbol x_e 是面的起点和终点),所以 𝒏f⋅𝒆y=−(xe−xs)\boldsymbol n_f\cdot\boldsymbol e_y=-(x_e-x_s)、𝒏f⋅𝒆x=ye−ys\boldsymbol n_f\cdot\boldsymbol e_x=y_e-y_s。状态固定时:

So JUJ_U is nonzero only in the rows of the 8 wall volumes, and every such row is proportional to ∂p/∂U\partial p/\partial U; the adjoint right-hand side −JUT-J_U^T of Section 11 is exactly this. Step 2, the geometry. The area vector is 𝒏f=(𝒙e−𝒙s)⊥\boldsymbol n_f=(\boldsymbol x_e-\boldsymbol x_s)^{\perp} (𝒙s\boldsymbol x_s, 𝒙e\boldsymbol x_e the start and end points of the face), so 𝒏f⋅𝒆y=−(xe−xs)\boldsymbol n_f\cdot\boldsymbol e_y=-(x_e-x_s) and 𝒏f⋅𝒆x=ye−ys\boldsymbol n_f\cdot\boldsymbol e_x=y_e-y_s. At fixed state:

Jy: ∂J∂xs=pf,  ∂J∂xe=−pf,  ∂J∂ys=∂J∂ye=0;Jx: ∂J∂ys=−pf,  ∂J∂ye=pf,  ∂J∂xs=∂J∂xe=0.J_y:\ \frac{\partial J}{\partial x_s}=p_f,\ \ \frac{\partial J}{\partial x_e}=-p_f,\ \ \frac{\partial J}{\partial y_s}=\frac{\partial J}{\partial y_e}=0;\qquad J_x:\ \frac{\partial J}{\partial y_s}=-p_f,\ \ \frac{\partial J}{\partial y_e}=p_f,\ \ \frac{\partial J}{\partial x_s}=\frac{\partial J}{\partial x_e}=0.

第三步,每个物面节点恰好是一个物面面的终点、下一个物面面的起点,把两项加起来:对 JyJ_y,节点 kk 的直接形状导数 ∂J/∂xk\partial J/\partial x_k 就是它两侧物面面压力之差,∂J/∂yk=0\partial J/\partial y_k=0。图 12.3 中紫色的“直接部分”就是这个量。第四步,JJ 不显式依赖 M∞M_\infty 和 θ\theta,所以 JM∞=Jθ=0J_{M_\infty}=J_\theta=0:这两个导数完全来自状态的响应(第 10 节)。

Step 3, every wall node is the end point of one wall face and the start point of the next, so the two terms add up: for JyJ_y, the direct shape derivative ∂J/∂xk\partial J/\partial x_k of node kk is the difference of the pressures on its two wall faces, and ∂J/∂yk=0\partial J/\partial y_k=0. The violet “direct part” in Figure 12.3 is this quantity. Step 4, JJ has no explicit dependence on M∞M_\infty and θ\theta, so JM∞=Jθ=0J_{M_\infty}=J_\theta=0: these two derivatives come entirely from the state response (Section 10).

核对:计算图对 JyJ_y 反向一次(种子 Rˉ=0\bar R=0、Jˉ=1\bar J=1)得到的 JUJ_U 与第一步的公式最大相差 7×10⁻¹⁸,对全部节点坐标的 JXJ_X 与第二、三步的公式完全相同,JM∞J_{M_\infty}、JθJ_\theta 为 0(本页代码在构建时计算)。

Check: one reverse sweep of the graph for JyJ_y (seeds Rˉ=0\bar R=0, Jˉ=1\bar J=1) gives a JUJ_U that differs from the formula of Step 1 by at most 7×10⁻¹⁸, and a JXJ_X over all node coordinates that is identical to Steps 2 and 3; JM∞J_{M_\infty} and JθJ_\theta are 0 (computed by this page’s code at build time).

这个力有多少是物理的

How much of this force is physics

来流动压 q∞=12ρ∞∣𝒖∞∣2=12γM∞2q_\infty=\tfrac12\rho_\infty|\boldsymbol u_\infty|^2=\tfrac12\gamma M_\infty^2,来流总压 p0∞=(1+γ−12M∞2)γ/(γ−1)p_{0\infty}=(1+\tfrac{\gamma-1}2M_\infty^2)^{\gamma/(\gamma-1)};M∞=0.2M_\infty=0.2 时分别为 0.028 与 1.0283。无损失(等熵)流中压力不可能超过总压,所以压力系数有上界:

The free-stream dynamic pressure is q∞=12ρ∞∣𝒖∞∣2=12γM∞2q_\infty=\tfrac12\rho_\infty|\boldsymbol u_\infty|^2=\tfrac12\gamma M_\infty^2 and the total pressure p0∞=(1+γ−12M∞2)γ/(γ−1)p_{0\infty}=(1+\tfrac{\gamma-1}2M_\infty^2)^{\gamma/(\gamma-1)}; at M∞=0.2M_\infty=0.2 they are 0.028 and 1.0283. In loss-free (isentropic) flow the pressure can never exceed the total pressure, so the pressure coefficient has an upper bound:

Cp=p−p∞q∞≤Cp,0=p0∞−1q∞=1.010.C_p=\frac{p-p_\infty}{q_\infty}\le C_{p,0}=\frac{p_{0\infty}-1}{q_\infty}=1.010.

本页在 M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ 的收敛解里,物面最大压力为 1.094,即 Cp=3.36C_p=3.36,高于总压。控制体 0 的速度 0.2366 与 ∣𝒖∞∣=0.2366|\boldsymbol u_\infty|=0.2366 几乎相同,按等熵关系压力应为 1.000,实际却只有 0.869:总压损失了 13%。按 d'Alembert 佯谬,无激波、无粘性的稳态亚声速绕流阻力为零;这里的阻力却是 J∥=0.193J_\parallel=0.193。

In this page’s converged solution at M∞=0.2M_\infty=0.2 and θ=10∘\theta=10^\circ, the largest wall pressure is 1.094, i.e. Cp=3.36C_p=3.36, above the total pressure. Also, volume 0 moves at 0.2366, almost exactly ∣𝒖∞∣=0.2366|\boldsymbol u_\infty|=0.2366; isentropic flow would give it the pressure 1.000, yet it has only 0.869: it has lost 13% of its total pressure. By d’Alembert’s paradox the steady, shock-free, inviscid subsonic flow around the body has zero drag, yet here the drag is J∥=0.193J_\parallel=0.193.

这些偏差来自固定耗散 α=2\alpha=2:它比来流速度大近一个数量级,相当于很大的数值粘性(第 15 节在加密网格上量化)。因此在本页这些网格上,力的数值主要是离散误差,不能当作物理载荷。但求导的对象就是这个离散目标 JJ 本身:第 10–14 节的 tangent、adjoint、差分与复数步长给出的,是离散 JJ 的精确导数,这正是本页要教的内容。离散 JJ 离物理有多远,是另一个由网格加密回答的问题。

These deviations come from the fixed dissipation α=2\alpha=2: it is almost an order of magnitude larger than the flow speed and acts like a large numerical viscosity (Section 15 quantifies it on refined meshes). On these meshes the forces are therefore mostly discretisation error and must not be read as physical loads. The derivatives, however, are taken of exactly this discrete objective JJ: the tangent, adjoint, finite differences and complex step of Sections 10–14 give the exact derivatives of the discrete JJ, which is what this page teaches. How far the discrete JJ is from physics is a separate question, answered by refinement.

图 7.3Figure 7.3

从离散目标得到伴随右端

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=∑fωf(D) ϕf(𝐔,D),J=\sum_f\omega_f(D)\,\phi_f(\mathbf U,D),
J˙=∑f[ωf ϕ˙f+ϕf ω˙f].\dot J=\sum_f\left[\omega_f\,\dot\phi_f+\phi_f\,\dot\omega_f\right].

本页的压力合力 J=∑f∈wallp(UL(f)) (𝒏f⋅𝒆d)J=\sum_{f\in\mathrm{wall}}p(U_{L(f)})\,(\boldsymbol n_f\cdot\boldsymbol e_d) 是这种加权和:ωf=1\omega_f=1,ϕf=p(UL(f)) (𝒏f⋅𝒆d)\phi_f=p(U_{L(f)})\,(\boldsymbol n_f\cdot\boldsymbol e_d);面积向量 𝒏f\boldsymbol n_f 随节点坐标变化,所以 ϕf\phi_f 同时依赖状态与设计变量。

The pressure force J=∑f∈wallp(UL(f)) (𝒏f⋅𝒆d)J=\sum_{f\in\mathrm{wall}}p(U_{L(f)})\,(\boldsymbol n_f\cdot\boldsymbol e_d) of this page is such a weighted sum: ωf=1\omega_f=1 and ϕf=p(UL(f)) (𝒏f⋅𝒆d)\phi_f=p(U_{L(f)})\,(\boldsymbol n_f\cdot\boldsymbol e_d); the area vector 𝒏f\boldsymbol n_f moves with the node coordinates, so ϕf\phi_f depends on both the state and the design variables.

求 JUTJ_U^T 时固定所有设计变量;求 JDTJ_D^T 时固定全部离散状态槽位。固定解析目标函数并不一定意味着移动网格上的目标采样值不变:如果采样点随设计移动,还要保留该位置依赖;固定网格及固定采样值时,相应项才为零。目标中的体积权重已经进入 JUTJ_U^T,不应在伴随右端再乘一次体积。

Hold every design variable fixed for JUTJ_U^T, and every stored state entry fixed for JDTJ_D^T. A fixed analytic target function need not have fixed samples on a moving mesh: if the evaluation locations move with the design, keep that dependence; the corresponding terms vanish only when the mesh and samples are fixed. Objective volume weights already enter JUTJ_U^T 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,ϕˉf=ωf Jˉ,𝐔ˉ+=(∂ϕf∂𝐔)Tϕˉf,\bar{J}=1,\qquad \bar\phi_f=\omega_f\,\bar{J},\qquad \bar{\mathbf U}\mathrel{\text{+=}}\Big(\frac{\partial \phi_f}{\partial \mathbf U}\Big)^T\bar\phi_f,
Dˉ+=(∂ϕf∂D)Tϕˉf+ϕf(∂ωf∂D)TJˉ.\bar D\mathrel{\text{+=}}\Big(\frac{\partial \phi_f}{\partial D}\Big)^T\bar\phi_f+\phi_f\Big(\frac{\partial\omega_f}{\partial D}\Big)^T\bar{J}.

从 𝐔ˉ←0\bar{\mathbf U}\leftarrow0、Dˉ←0\bar D\leftarrow0 开始,扫完全部求积点后 𝐔ˉ=JUT\bar{\mathbf U}=J_U^T、Dˉ=JDT\bar D=J_D^T:一个状态被多个求积点读取,所以要先置零、再累加。前向的 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 𝐔ˉ=JUT\bar{\mathbf U}=J_U^T and Dˉ=JDT\bar D=J_D^T: 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

前向:从热力学到 matrix-free 乘积

Tangent: from thermodynamics to the matrix-free product

先对压力和速度求导,再对法向速度、物理通量和耗散项求导。给定任意状态切向量 U˙\dot U(前向模式的种子),不必逐列构造 AA;每个面只读取左右两侧的四分量切向量,按同一计算图传播,然后依原残差符号分发,得到 R˙=AU˙\dot R=A\dot U。U˙\dot U 的四个分量写作 U˙=(ρ˙,U˙ρu,U˙ρv,U˙ρE)\dot U=(\dot\rho,\dot U_{\rho u},\dot U_{\rho v},\dot U_{\rho E})。

Differentiate pressure and velocity first, then the normal velocity, the physical flux and the dissipation. For an arbitrary state tangent U˙\dot U (the forward-mode seed), no Jacobian columns need be assembled: gather the two four-component tangents, propagate them through the face computation and scatter with the residual signs, which gives R˙=AU˙\dot R=A\dot U. The four components of U˙\dot U are written U˙=(ρ˙,U˙ρu,U˙ρv,U˙ρE)\dot U=(\dot\rho,\dot U_{\rho u},\dot U_{\rho v},\dot U_{\rho E}).

记号:本页的前向(tangent)与伴随推导采用自动微分的写法。程序变量 xx 的前向导数(切向量)记为 x˙\dot x,伴随记为 xˉ\bar x;反向扫描用 xˉ+=…\bar x\mathrel{\text{+=}}\dots 累加、用 xˉ←0\bar x\leftarrow0 清零,语句按与原始计算相反的次序执行。代码与播放器沿用 Tapenade 的命名:x_d 即 x˙\dot x,x_b 即 xˉ\bar x(例如 residual_d、residual_b)。变分符号 δ\delta 只出现在第 16 节与附录 A 的连续伴随里,表示连续函数的变分;点和横线是对离散代码的前向与反向求导。Newton 的修正量写作 ΔU\Delta U。

Notation: the tangent and adjoint derivations of this page use the notation of algorithmic differentiation. The forward derivative (tangent) of a program variable xx is x˙\dot x and its adjoint is xˉ\bar x; the reverse sweep accumulates with xˉ+=…\bar x\mathrel{\text{+=}}\dots, clears with xˉ←0\bar x\leftarrow0, and runs its statements in the reverse order of the primal computation. Code listings and players use Tapenade’s names: x_d is x˙\dot x and x_b is xˉ\bar x (residual_d, residual_b). The variation symbol δ\delta appears only in the continuous adjoint of Section 16 and Appendix A, where it is the variation of a continuous function; the dot and the bar are forward- and reverse-mode derivatives of the discrete code. Newton corrections are written ΔU\Delta U.

p˙=(γ−1)[U˙ρE−u U˙ρu−v U˙ρv+u2+v22 ρ˙],u˙=U˙ρu−u ρ˙ρ,v˙=U˙ρv−v ρ˙ρ.\dot p=(\gamma-1)\Bigl[\dot U_{\rho E}-u\,\dot U_{\rho u}-v\,\dot U_{\rho v}+\tfrac{u^2+v^2}2\,\dot\rho\Bigr],\quad \dot u=\frac{\dot U_{\rho u}-u\,\dot\rho}{\rho},\quad \dot v=\frac{\dot U_{\rho v}-v\,\dot\rho}{\rho}.
ℓ u˙n=nxu˙+nyv˙,F˙n=[ℓun ρ˙+ρ ℓu˙nℓun U˙ρu+ρu ℓu˙n+nxp˙ℓun U˙ρv+ρv ℓu˙n+nyp˙ℓun (U˙ρE+p˙)+(ρE+p) ℓu˙n].\ell\,\dot u_n=n_x\dot u+n_y\dot v,\qquad \dot F_n=\begin{bmatrix}\ell u_n\,\dot\rho+\rho\,\ell\dot u_n\\\ell u_n\,\dot U_{\rho u}+\rho u\,\ell\dot u_n+n_x\dot p\\\ell u_n\,\dot U_{\rho v}+\rho v\,\ell\dot u_n+n_y\dot p\\\ell u_n\,(\dot U_{\rho E}+\dot p)+(\rho E+p)\,\ell\dot u_n\end{bmatrix}.
H˙=12(Fn,U(UL) U˙L+Fn,U(UR) U˙R)−αℓ2(U˙R−U˙L).\dot H=\tfrac12\bigl(F_{n,U}(U_L)\,\dot U_L+F_{n,U}(U_R)\,\dot U_R\bigr)-\tfrac{\alpha\ell}{2}(\dot U_R-\dot U_L).

上式暂时固定几何(𝒏˙=0\dot{\boldsymbol n}=0),所以面积向量形式的法向速度 ℓun=unx+vny\ell u_n=un_x+vn_y 的切向量就是 ℓu˙n\ell\dot u_n。远场的 U˙R\dot U_R 就是第 4 节的 U˙b=GU U˙L\dot U_b=G_U\,\dot U_L,不能置零;壁面使用第 4 节的压力导数。当前实现把标量加、减、乘、除、幂和三角函数的局部偏导保存成计算图,再沿图向前传播;这是解析链式法则,不是有限差分,也不存全局 Jacobian。播放器可自动播放每个面的四个步骤。

These formulas hold geometry fixed (𝒏˙=0\dot{\boldsymbol n}=0), so the tangent of the normal velocity in area-vector form, ℓun=unx+vny\ell u_n=un_x+vn_y, is ℓu˙n\ell\dot u_n. At the far field U˙R\dot U_R is the U˙b=GU U˙L\dot U_b=G_U\,\dot U_L of Section 4, not zero; walls use the pressure derivative of Section 4. The implementation records local derivatives of scalar arithmetic, powers and trigonometric operations and propagates along that graph. This is analytic chain-rule differentiation, not finite differencing, and it stores no global Jacobian. The player can step through the four stages of every face automatically.

图 8.1 把这些公式放回第 3 节的计算图。

Figure 8.1 puts these formulas back into the computation graph of Section 3.

U˙L左侧的切向量tangent, leftY˙Lρ˙, u˙, v˙, p˙​F˙L+Fn˙(UL)U˙R右侧的切向量tangent, rightY˙Rρ˙, u˙, v˙, p˙​F˙R+Fn˙(UR)n˙f, ℓ˙f几何固定时为 00 at fixed geometryH˙f=½(F˙L+F˙R)−(αℓf/2)(U˙R−U˙L)−(αℓ˙f/2)(UR−UL)R˙L += H˙f,R˙R −= H˙fR˙L += H˙f, R˙R −= H˙f× YU× Fn,Y× YU× Fn,Y耗散项的 U˙Ldissipation term in U˙L耗散项的 U˙Rdissipation term in U˙R
图 8.1 · 与第 3 节同一张计算图,每个数组 xx 换成它的切向量(前向导数)x˙\dot x,箭头仍从左到右。每一步乘上本步的局部导数:YU=∂Y/∂UY_U=\partial Y/\partial U,Fn,Y=∂Fn/∂YF_{n,Y}=\partial F_n/\partial Y;几何变化只经过 𝒏˙f\dot{\boldsymbol n}_f、ℓ˙f\dot\ell_f 进入(第 12 节),固定几何时这两项为零。两条虚线是耗散项对 U˙L\dot U_L、U˙R\dot U_R 的直接依赖。Figure 8.1 · The computation graph of Section 3 with every array xx replaced by its tangent (forward derivative) x˙\dot x; the arrows still run left to right. Each step multiplies by its own local derivative, YU=∂Y/∂UY_U=\partial Y/\partial U and Fn,Y=∂Fn/∂YF_{n,Y}=\partial F_n/\partial Y; geometry changes enter only through 𝒏˙f\dot{\boldsymbol n}_f and ℓ˙f\dot\ell_f (Section 12), which vanish at fixed geometry. The two dashed paths are the direct dependence of the dissipation on U˙L\dot U_L and U˙R\dot U_R.

逐步推导:通量 Jacobian Fn,UF_{n,U} 的完整 4×44\times4 矩阵

Step by step: the full 4×44\times4 flux Jacobian Fn,UF_{n,U}

第一步,原始变量 Y=(ρ,u,v,p)TY=(\rho,u,v,p)^T 对守恒量的 Jacobian(代码 prim() 中的除法与第 3 节的 ∂p/∂U\partial p/\partial U):

Step 1, the Jacobian of the primitive variables Y=(ρ,u,v,p)TY=(\rho,u,v,p)^T with respect to the conserved ones (the divisions in prim() and ∂p/∂U\partial p/\partial U of Section 3):

YU=∂Y∂U=[1000−u/ρ1/ρ00−v/ρ01/ρ0γ−12(u2+v2)−(γ−1)u−(γ−1)vγ−1].Y_U=\frac{\partial Y}{\partial U}=\begin{bmatrix}1&0&0&0\\-u/\rho&1/\rho&0&0\\-v/\rho&0&1/\rho&0\\\tfrac{\gamma-1}2(u^2+v^2)&-(\gamma-1)u&-(\gamma-1)v&\gamma-1\end{bmatrix}.

第二步,用原始变量写物理通量。面积向量形式的法向速度记作 ℓun=unx+vny\ell u_n=un_x+vn_y,并利用 ρE+p=γγ−1p+12ρ(u2+v2)\rho E+p=\tfrac{\gamma}{\gamma-1}p+\tfrac12\rho(u^2+v^2):

Step 2, write the physical flux in primitive variables, with the normal velocity in area-vector form ℓun=unx+vny\ell u_n=un_x+vn_y and ρE+p=γγ−1p+12ρ(u2+v2)\rho E+p=\tfrac{\gamma}{\gamma-1}p+\tfrac12\rho(u^2+v^2):

Fn=[ρ ℓunρu ℓun+p nxρv ℓun+p ny(γγ−1p+12ρ(u2+v2))ℓun],Fn,Y=[ℓunρnxρny0u ℓunρ ℓun+ρu nxρu nynxv ℓunρv nxρ ℓun+ρv nyny12(u2+v2) ℓunρu ℓun+(ρE+p) nxρv ℓun+(ρE+p) nyγγ−1ℓun].F_n=\begin{bmatrix}\rho\,\ell u_n\\\rho u\,\ell u_n+p\,n_x\\\rho v\,\ell u_n+p\,n_y\\\bigl(\tfrac{\gamma}{\gamma-1}p+\tfrac12\rho(u^2+v^2)\bigr)\ell u_n\end{bmatrix},\qquad F_{n,Y}=\begin{bmatrix}\ell u_n&\rho n_x&\rho n_y&0\\u\,\ell u_n&\rho\,\ell u_n+\rho u\,n_x&\rho u\,n_y&n_x\\v\,\ell u_n&\rho v\,n_x&\rho\,\ell u_n+\rho v\,n_y&n_y\\\tfrac12(u^2+v^2)\,\ell u_n&\rho u\,\ell u_n+(\rho E+p)\,n_x&\rho v\,\ell u_n+(\rho E+p)\,n_y&\tfrac{\gamma}{\gamma-1}\ell u_n\end{bmatrix}.

每一列是对一个原始变量求偏导;例如能量通量对 uu 的导数,来自 12ρu2ℓun\tfrac12\rho u^2\ell u_n 中的 ρu ℓun\rho u\,\ell u_n 与 ℓun\ell u_n 本身对 uu 的导数 nxn_x 乘以 ρE+p\rho E+p。第三步,链式法则 Fn,U=Fn,YYUF_{n,U}=F_{n,Y}Y_U,逐项相乘并化简(E+p/ρE+p/\rho 是单位质量的总焓):

Each column is the partial derivative with respect to one primitive variable; for example, the derivative of the energy flux with respect to uu combines ρu ℓun\rho u\,\ell u_n from 12ρu2ℓun\tfrac12\rho u^2\ell u_n with the derivative nxn_x of ℓun\ell u_n itself times ρE+p\rho E+p. Step 3, the chain rule Fn,U=Fn,YYUF_{n,U}=F_{n,Y}Y_U, multiplied out and simplified (E+p/ρE+p/\rho is the total enthalpy per unit mass):

Fn,U=[0nxny0γ−12(u2+v2) nx−u ℓunℓun−(γ−2)u nxu ny−(γ−1)v nx(γ−1)nxγ−12(u2+v2) ny−v ℓunv nx−(γ−1)u nyℓun−(γ−2)v ny(γ−1)ny(γ−12(u2+v2)−E−pρ)ℓun(E+pρ)nx−(γ−1)u ℓun(E+pρ)ny−(γ−1)v ℓunγ ℓun].F_{n,U}=\begin{bmatrix}0&n_x&n_y&0\\\tfrac{\gamma-1}2(u^2+v^2)\,n_x-u\,\ell u_n&\ell u_n-(\gamma-2)u\,n_x&u\,n_y-(\gamma-1)v\,n_x&(\gamma-1)n_x\\\tfrac{\gamma-1}2(u^2+v^2)\,n_y-v\,\ell u_n&v\,n_x-(\gamma-1)u\,n_y&\ell u_n-(\gamma-2)v\,n_y&(\gamma-1)n_y\\\bigl(\tfrac{\gamma-1}2(u^2+v^2)-E-\tfrac p\rho\bigr)\ell u_n&\bigl(E+\tfrac p\rho\bigr)n_x-(\gamma-1)u\,\ell u_n&\bigl(E+\tfrac p\rho\bigr)n_y-(\gamma-1)v\,\ell u_n&\gamma\,\ell u_n\end{bmatrix}.

第四步,两个自检。每个元素都对 (nx,ny)(n_x,n_y) 线性,所以 Fn,UF_{n,U} 对面积向量是线性的(第 6 节用到);Euler 通量是 UU 的一次齐次函数:对任意标量 μ\mu,Fn(μU)=μFn(U)F_n(\mu U)=\mu F_n(U),对 μ\mu 求导得 Fn,U(U) U=Fn(U)F_{n,U}(U)\,U=F_n(U)。

Step 4, two self-checks. Every entry is linear in (nx,ny)(n_x,n_y), so Fn,UF_{n,U} is linear in the area vector (used in Section 6); and the Euler flux is homogeneous of degree one in UU: for any scalar μ\mu, Fn(μU)=μFn(U)F_n(\mu U)=\mu F_n(U), whose derivative with respect to μ\mu gives Fn,U(U) U=Fn(U)F_{n,U}(U)\,U=F_n(U).

核对:在全部 48 个面的两侧状态上,上面的矩阵与代码 fluxJacobian()(对 physical() 的计算图逐列求前向导数)最大相差 9×10⁻¹⁶(矩阵元素最大为 4.97),与 Fn,YYUF_{n,Y}Y_U 的乘积最大相差 2×10⁻¹⁵,齐次关系 Fn,UU−FnF_{n,U}U-F_n 最大为 4×10⁻¹⁶;sympy 另外做了符号核对(本页代码在构建时计算)。

Check: on both side states of all 48 faces, the matrix above differs from the code's fluxJacobian() (column-wise forward derivatives of the graph of physical()) by at most 9×10⁻¹⁶ (largest entry 4.97), from the product Fn,YYUF_{n,Y}Y_U by at most 2×10⁻¹⁵, and the homogeneity relation Fn,UU−FnF_{n,U}U-F_n is at most 4×10⁻¹⁶; sympy checks the same symbolically (computed by this page’s code at build time).

逐步推导:数值通量的线性化与它的特征值

Step by step: linearising the numerical flux, and its eigenvalues

第一步,固定几何,对 H=12(Fn(UL)+Fn(UR))−αℓ2(UR−UL)H=\tfrac12(F_n(U_L)+F_n(U_R))-\tfrac{\alpha\ell}2(U_R-U_L) 分别对 ULU_L、URU_R 求导:

Step 1, at fixed geometry, differentiate H=12(Fn(UL)+Fn(UR))−αℓ2(UR−UL)H=\tfrac12(F_n(U_L)+F_n(U_R))-\tfrac{\alpha\ell}2(U_R-U_L) with respect to ULU_L and URU_R:

HUL=12Fn,U(UL)+αℓ2I,HUR=12Fn,U(UR)−αℓ2I.H_{U_L}=\tfrac12F_{n,U}(U_L)+\tfrac{\alpha\ell}2I,\qquad H_{U_R}=\tfrac12F_{n,U}(U_R)-\tfrac{\alpha\ell}2I.

α\alpha 是格式中的常数,所以没有 ∂α/∂U\partial\alpha/\partial U 项,也没有需要冻结的开关。若改用随状态变化的 max⁡(∣un∣+c)\max(|u_n|+c),就要对 max 在哪一侧取得作分支选择,那个分支在两侧相等处不可微。第二步,Fn,UF_{n,U} 的特征值是法向的四个特征速度乘以面长:

α\alpha is a constant of the scheme, so there is no ∂α/∂U\partial\alpha/\partial U term and no switch to freeze. A state-dependent max⁡(∣un∣+c)\max(|u_n|+c) would require choosing on which side the max is attained, a branch that is not differentiable where both sides are equal. Step 2, the eigenvalues of Fn,UF_{n,U} are the four characteristic speeds in the normal direction times the face length:

eig⁡Fn,U={ℓun,  ℓun,  ℓ(un+c),  ℓ(un−c)},tr⁡Fn,U=4ℓun,det⁡Fn,U=ℓ4un2(un2−c2).\operatorname{eig}F_{n,U}=\{\ell u_n,\;\ell u_n,\;\ell(u_n+c),\;\ell(u_n-c)\},\qquad\operatorname{tr}F_{n,U}=4\ell u_n,\qquad\det F_{n,U}=\ell^4u_n^2(u_n^2-c^2).

第三步,加上 ±αℓ2I\pm\tfrac{\alpha\ell}2I 只把每个特征值平移同一个量。于是 HULH_{U_L} 的特征值是 ℓ2(un+α)\tfrac\ell2(u_n+\alpha)、ℓ2(un±c+α)\tfrac\ell2(u_n\pm c+\alpha),HURH_{U_R} 的是 ℓ2(un−α)\tfrac\ell2(u_n-\alpha)、ℓ2(un±c−α)\tfrac\ell2(u_n\pm c-\alpha)。页面检查的 α>∣un∣+c\alpha>|u_n|+c 恰好保证前者全为正、后者全为负:增大左侧状态使流出 LL 的通量增大,增大右侧状态使它减小,这是迎风思想的离散形式,也是第 6 节中耗散部分能主导对角块的原因。

Step 3, adding ±αℓ2I\pm\tfrac{\alpha\ell}2I shifts every eigenvalue by the same amount. The eigenvalues of HULH_{U_L} are ℓ2(un+α)\tfrac\ell2(u_n+\alpha) and ℓ2(un±c+α)\tfrac\ell2(u_n\pm c+\alpha), those of HURH_{U_R} are ℓ2(un−α)\tfrac\ell2(u_n-\alpha) and ℓ2(un±c−α)\tfrac\ell2(u_n\pm c-\alpha). The page’s check α>∣un∣+c\alpha>|u_n|+c guarantees exactly that the former are all positive and the latter all negative: raising the left state increases the flux out of LL, raising the right state decreases it. This is the discrete form of upwinding, and the reason why the dissipation can dominate the diagonal blocks in Section 6.

核对:在全部 48 个面的两侧状态上,迹的相对偏差最大 3×10⁻¹⁶,行列式的相对偏差最大 8×10⁻¹⁶;α−max⁡(∣un∣+c)=0.547\alpha-\max(|u_n|+c)=0.547,面积向量方向上的所有特征速度都在 α\alpha 以内;两个块与计算图对单位种子的前向结果最大相差 4×10⁻¹⁶(本页代码在构建时计算)。

Check: on both side states of all 48 faces the trace deviates by at most 3×10⁻¹⁶ relatively and the determinant by at most 8×10⁻¹⁶; α−max⁡(∣un∣+c)=0.547\alpha-\max(|u_n|+c)=0.547, so every characteristic speed normal to a face stays below α\alpha; the two blocks differ from the forward results of the graph for unit seeds by at most 4×10⁻¹⁶ (computed by this page’s code at build time).

图 8.2 在第 2 节的紫色面上给出这两个块的数值,以及它们在 AA 中的位置。

Figure 8.2 gives the values of the two blocks on the violet face of Section 2, and their places in AA.

0.512−0.1960.16400.0080.4430.041−0.0780.003−0.0210.4730.0660.141−0.6650.5620.454ρρuρvρEρρuρvρE∂H/∂UL = ½Fn,U(UL) + (αℓf/2)I−0.512−0.1960.16400.010−0.5920.047−0.0780.004−0.024−0.5560.0660.168−0.6840.579−0.579ρρuρvρEρρuρvρE∂H/∂UR = ½Fn,U(UR) − (αℓf/2)I它们在 A 中的位置where they go in ALRLR+∂H/∂UL+∂H/∂UR−∂H/∂UL−∂H/∂UR对角元 ±αℓf/2 = ±0.512;其余是 ½Fn,U 的元素diagonal ±αℓf/2 = ±0.512; the rest are entries of ½Fn,U
图 8.2 · 面 f=0f=0(第 2 节的紫色面)的两个 4×44\times4 块 HUL=∂Hf/∂ULH_{U_L}=\partial H_f/\partial U_L 与 HUR=∂Hf/∂URH_{U_R}=\partial H_f/\partial U_R,数值取自本页收敛解并与计算图的结果一致(蓝正红负,深浅按绝对值)。它们进入全局 Jacobian 的四个块:行 LL 加、行 RR 减,列由求导的是哪一侧状态决定。HULH_{U_L} 的对角元都带 +αℓf/2+\alpha\ell_f/2,HURH_{U_R} 的都带 −αℓf/2-\alpha\ell_f/2,这正是第 6 节 GS 预处理保留的部分。Figure 8.2 · The two 4×44\times4 blocks HUL=∂Hf/∂ULH_{U_L}=\partial H_f/\partial U_L and HUR=∂Hf/∂URH_{U_R}=\partial H_f/\partial U_R of face f=0f=0 (the violet face of Section 2), with values from this page's converged solution that agree with the computation graph (blue positive, red negative, shaded by magnitude). They enter four blocks of the global Jacobian: added in row LL, subtracted in row RR, with the column set by which side's state is differentiated. Every diagonal entry of HULH_{U_L} contains +αℓf/2+\alpha\ell_f/2 and every diagonal entry of HURH_{U_R} contains −αℓf/2-\alpha\ell_f/2, exactly the part the GS preconditioner of Section 6 keeps.

逐步推导:通量 Jacobian 的特征向量与 Riemann 坐标

Step by step: the eigenvectors of the flux Jacobian, and the Riemann coordinates

第一步,右特征向量。取单位法向 𝒏^\hat{\boldsymbol n}(Fn,U=ℓFn^,UF_{n,U}=\ell F_{\hat n,U}:特征向量相同,特征值乘以 ℓ\ell),记 ut=−un^y+vn^xu_t=-u\hat n_y+v\hat n_x 为第 4 节的切向速度。四个特征速度的右特征向量排成矩阵 Φ\Phi 的四列:

Step 1, the right eigenvectors. Take the unit normal 𝒏^\hat{\boldsymbol n} (Fn,U=ℓFn^,UF_{n,U}=\ell F_{\hat n,U}: the same eigenvectors, the eigenvalues times ℓ\ell) and write ut=−un^y+vn^xu_t=-u\hat n_y+v\hat n_x for the tangential velocity of Section 4. The right eigenvectors of the four characteristic speeds are the four columns of Φ\Phi:

Fn^,U Φ=Φ diag⁡(un−c,  un,  un,  un+c),Φ=[1101u−cn^xu−n^yu+cn^xv−cn^yvn^xv+cn^yE+pρ−cun12(u2+v2)utE+pρ+cun].F_{\hat n,U}\,\Phi=\Phi\,\operatorname{diag}(u_n-c,\;u_n,\;u_n,\;u_n+c),\qquad\Phi=\begin{bmatrix}1&1&0&1\\u-c\hat n_x&u&-\hat n_y&u+c\hat n_x\\v-c\hat n_y&v&\hat n_x&v+c\hat n_y\\E+\frac p\rho-cu_n&\tfrac12(u^2+v^2)&u_t&E+\frac p\rho+cu_n\end{bmatrix}.

每一列都可以用本节开头的 p˙\dot p、u˙\dot u、v˙\dot v 与 F˙n\dot F_n 公式验证:把这一列当作 U˙\dot U 代入。第三列(剪切波)给出 ρ˙=0\dot\rho=0、u˙=−n^y/ρ\dot u=-\hat n_y/\rho、v˙=n^x/ρ\dot v=\hat n_x/\rho,于是 p˙=(γ−1)(ut+un^y−vn^x)=0\dot p=(\gamma-1)(u_t+u\hat n_y-v\hat n_x)=0、u˙n=0\dot u_n=0,F˙n^=un (0,−n^y,n^x,ut)T\dot F_{\hat n}=u_n\,(0,-\hat n_y,\hat n_x,u_t)^T,正是 unu_n 乘这一列。第一、四列(两道声波)给出 p˙=c2\dot p=c^2、u˙n=∓c/ρ\dot u_n=\mp c/\rho;第二列(熵波)给出 p˙=0\dot p=0、u˙n=0\dot u_n=0,只改变密度。

Each column can be checked with the p˙\dot p, u˙\dot u, v˙\dot v and F˙n\dot F_n formulas at the start of this section: insert the column as U˙\dot U. The third column (shear wave) gives ρ˙=0\dot\rho=0, u˙=−n^y/ρ\dot u=-\hat n_y/\rho, v˙=n^x/ρ\dot v=\hat n_x/\rho, hence p˙=(γ−1)(ut+un^y−vn^x)=0\dot p=(\gamma-1)(u_t+u\hat n_y-v\hat n_x)=0, u˙n=0\dot u_n=0 and F˙n^=un (0,−n^y,n^x,ut)T\dot F_{\hat n}=u_n\,(0,-\hat n_y,\hat n_x,u_t)^T, which is unu_n times the column. The first and fourth columns (the two sound waves) give p˙=c2\dot p=c^2 and u˙n=∓c/ρ\dot u_n=\mp c/\rho; the second (entropy wave) gives p˙=0\dot p=0 and u˙n=0\dot u_n=0, a change of density only.

第二步,左特征向量。Φ−1\Phi^{-1} 的四行把任意切向量 U˙\dot U 分解成四个波各自携带的量(p˙\dot p、u˙n=u˙n^x+v˙n^y\dot u_n=\dot u\hat n_x+\dot v\hat n_y、u˙t=−u˙n^y+v˙n^x\dot u_t=-\dot u\hat n_y+\dot v\hat n_x 都由 YUU˙Y_U\dot U 得到):

Step 2, the left eigenvectors. The four rows of Φ−1\Phi^{-1} split any tangent U˙\dot U into the amounts the four waves carry (p˙\dot p, u˙n=u˙n^x+v˙n^y\dot u_n=\dot u\hat n_x+\dot v\hat n_y and u˙t=−u˙n^y+v˙n^x\dot u_t=-\dot u\hat n_y+\dot v\hat n_x all follow from YUU˙Y_U\dot U):

Φ−1U˙=(p˙−ρc u˙n2c2,  ρ˙−p˙c2,  ρ u˙t,  p˙+ρc u˙n2c2)T.\Phi^{-1}\dot U=\Bigl(\frac{\dot p-\rho c\,\dot u_n}{2c^2},\;\dot\rho-\frac{\dot p}{c^2},\;\rho\,\dot u_t,\;\frac{\dot p+\rho c\,\dot u_n}{2c^2}\Bigr)^T.

把第一步的四列逐一代入,这四个组合依次给出单位向量:例如沿第一列 p˙=c2\dot p=c^2、u˙n=−c/ρ\dot u_n=-c/\rho,第一个组合为 (c2+c2)/(2c2)=1(c^2+c^2)/(2c^2)=1,其余三个为 0。所以 Φ−1Φ=I\Phi^{-1}\Phi=I,并且 Fn^,U=Φdiag⁡(un−c,un,un,un+c)Φ−1F_{\hat n,U}=\Phi\operatorname{diag}(u_n-c,u_n,u_n,u_n+c)\Phi^{-1}。第三步,数值通量的两个块与 Fn,UF_{n,U} 共用这组特征向量:

Inserting the four columns of Step 1 one by one, the four combinations give the unit vectors in turn: along the first column, for instance, p˙=c2\dot p=c^2 and u˙n=−c/ρ\dot u_n=-c/\rho, so the first combination is (c2+c2)/(2c2)=1(c^2+c^2)/(2c^2)=1 and the other three vanish. Hence Φ−1Φ=I\Phi^{-1}\Phi=I and Fn^,U=Φdiag⁡(un−c,un,un,un+c)Φ−1F_{\hat n,U}=\Phi\operatorname{diag}(u_n-c,u_n,u_n,u_n+c)\Phi^{-1}. Step 3, the two blocks of the numerical flux share these eigenvectors with Fn,UF_{n,U}:

HUL=12Fn,U(UL)+αℓ2I=ΦL ℓ2diag⁡(un−c+α,  un+α,  un+α,  un+c+α) ΦL−1,H_{U_L}=\tfrac12F_{n,U}(U_L)+\tfrac{\alpha\ell}2I=\Phi_L\,\tfrac\ell2\operatorname{diag}\bigl(u_n-c+\alpha,\;u_n+\alpha,\;u_n+\alpha,\;u_n+c+\alpha\bigr)\,\Phi_L^{-1},

其中 ΦL\Phi_L 在 ULU_L 上求值;HURH_{U_R} 相同,只是 +α+\alpha 换成 −α-\alpha、在 URU_R 上求值。上一段推导的特征值就是这里的对角元;固定的 α\alpha 只平移特征值,不改变特征向量。第四步,Riemann 坐标。第 4 节的远场用 W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t)。由 p=Sργp=S\rho^\gamma 得 p˙=ργS˙+c2ρ˙\dot p=\rho^\gamma\dot S+c^2\dot\rho,再用第 3 节的 c˙\dot c,r±=un±2c/(γ−1)r_\pm=u_n\pm2c/(\gamma-1) 的切向量是:

with ΦL\Phi_L evaluated at ULU_L; HURH_{U_R} is the same with −α-\alpha for +α+\alpha, evaluated at URU_R. The eigenvalues of the previous derivation are the diagonal entries here; the fixed α\alpha shifts the eigenvalues and leaves the eigenvectors alone. Step 4, the Riemann coordinates. The far field of Section 4 uses W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t). From p=Sργp=S\rho^\gamma, p˙=ργS˙+c2ρ˙\dot p=\rho^\gamma\dot S+c^2\dot\rho; with the c˙\dot c of Section 3, the tangents of r±=un±2c/(γ−1)r_\pm=u_n\pm2c/(\gamma-1) are:

r˙±=u˙n±2c˙γ−1=u˙n±(p˙ρc+ργ−1(γ−1)c S˙),S˙=p˙−c2ρ˙ργ.\dot r_\pm=\dot u_n\pm\frac{2\dot c}{\gamma-1}=\dot u_n\pm\Bigl(\frac{\dot p}{\rho c}+\frac{\rho^{\gamma-1}}{(\gamma-1)c}\,\dot S\Bigr),\qquad\dot S=\frac{\dot p-c^2\dot\rho}{\rho^\gamma}.

u˙n±p˙/(ρc)\dot u_n\pm\dot p/(\rho c) 正是第二步中那道声波的组合,而 S˙=−c2ρ−γ(ρ˙−p˙/c2)\dot S=-c^2\rho^{-\gamma}(\dot\rho-\dot p/c^2) 只含熵波:r±r_\pm 除了自己的声波,还带着一部分熵波。Fn^,UF_{\hat n,U} 把每个波乘以它自己的速度,所以 (∂W/∂U)Fn^,UU˙(\partial W/\partial U)F_{\hat n,U}\dot U 的 r+r_+ 分量是

u˙n±p˙/(ρc)\dot u_n\pm\dot p/(\rho c) is exactly the combination of that sound wave in Step 2, while S˙=−c2ρ−γ(ρ˙−p˙/c2)\dot S=-c^2\rho^{-\gamma}(\dot\rho-\dot p/c^2) holds the entropy wave only: besides its own sound wave, r±r_\pm carries part of the entropy wave. Fn^,UF_{\hat n,U} multiplies every wave by its own speed, so the r+r_+ component of (∂W/∂U)Fn^,UU˙(\partial W/\partial U)F_{\hat n,U}\dot U is

(un+c)(u˙n+p˙/(ρc))+unργ−1S˙/((γ−1)c)=(un+c) r˙+−ργ−1S˙/(γ−1),(u_n+c)\bigl(\dot u_n+\dot p/(\rho c)\bigr)+u_n\rho^{\gamma-1}\dot S/((\gamma-1)c)=(u_n+c)\,\dot r_+-\rho^{\gamma-1}\dot S/(\gamma-1),

r−r_- 分量同理。在 WW 坐标下:

and likewise for r−r_-. In the coordinates WW:

∂W∂U Fn^,U(∂W∂U)−1=[un+c0−ργ−1γ−100un−c−ργ−1γ−1000un0000un].\frac{\partial W}{\partial U}\,F_{\hat n,U}\Bigl(\frac{\partial W}{\partial U}\Bigr)^{-1}=\begin{bmatrix}u_n+c&0&-\frac{\rho^{\gamma-1}}{\gamma-1}&0\\0&u_n-c&-\frac{\rho^{\gamma-1}}{\gamma-1}&0\\0&0&u_n&0\\0&0&0&u_n\end{bmatrix}.

第五步,这对远场意味着什么。第 3 列的两个非零元说明:只有当 SS 均匀(等熵流)时,r±r_\pm 才是纯粹的特征变量,第 4 节的闭合才严格按特征取舍。本页收敛解上 SS 在 1.0012 到 1.0103 之间(来流为 1),第 3 列的元素在 −2.56 到 −2.39 之间:数值耗散在物体附近产生熵,随流带到下游(图 8.3 下),所以这个闭合只是近似的特征闭合。这正是第 4 节说它“只在局部一维、等熵的意义下是特征关系”的量化。近似的是边界条件本身,不是它的导数:代码对这个闭合求出的 GUG_U 与它的转置是精确的(附录 A 实测)。

Step 5, what this means at the far field. The two nonzero entries of column 3 say that only when SS is uniform (isentropic flow) are r±r_\pm pure characteristic variables and does the closure of Section 4 select exactly by characteristics. On this page’s converged solution SS lies between 1.0012 and 1.0103 (1 in the free stream) and the entries of column 3 between −2.56 and −2.39: the numerical dissipation makes entropy near the body and the flow carries it downstream (bottom of Figure 8.3), so the closure is characteristic only approximately. This quantifies Section 4’s “characteristic only in the local one-dimensional, isentropic sense”. What is approximate is the boundary condition itself, not its derivative: the GUG_U the code computes for this closure, and its transpose, are exact (measured in Appendix A).

核对:在全部 48 个面的两侧状态(共 88 个)上,Fn^,UΦ−Φdiag⁡(… )F_{\hat n,U}\Phi-\Phi\operatorname{diag}(\dots) 最大为 2×10⁻¹⁵(Fn^,UF_{\hat n,U} 的元素最大为 3.62),Φ−1Φ−I\Phi^{-1}\Phi-I 最大为 6×10⁻¹⁶;由 riemann() 与 fluxJacobian() 两个计算图算出的 (∂W/∂U)Fn^,U(∂W/∂U)−1(\partial W/\partial U)F_{\hat n,U}(\partial W/\partial U)^{-1} 与上面的上三角矩阵最大相差 5×10⁻¹⁵;sympy(sym_ta_euler.py)另外做了符号核对(本页代码在构建时计算)。

Check: on both side states of all 48 faces (88 states), Fn^,UΦ−Φdiag⁡(… )F_{\hat n,U}\Phi-\Phi\operatorname{diag}(\dots) is at most 2×10⁻¹⁵ (largest entry of Fn^,UF_{\hat n,U}: 3.62) and Φ−1Φ−I\Phi^{-1}\Phi-I at most 6×10⁻¹⁶; the (∂W/∂U)Fn^,U(∂W/∂U)−1(\partial W/\partial U)F_{\hat n,U}(\partial W/\partial U)^{-1} of the two computation graphs riemann() and fluxJacobian() differs from the upper triangular matrix above by at most 5×10⁻¹⁵; sympy (sym_ta_euler.py) checks the same symbolically (computed by this page’s code at build time).

图 8.3 在第 2 节的紫色面上给出这三个矩阵的数值,以及收敛解上熵的分布。

Figure 8.3 gives the three matrices on the violet face of Section 2, and the entropy of the converged solution.

11011.130.235−0.643−0.657−0.7210.0278−0.7660.7763.600.0280−0.1723.23ρρuρvρEun−cununun+c−1.330000−0.1620000−0.16200001.00un−cununun+cun−cununun+c1.000−2.3900−1.33−2.39000−0.1620000−0.162r+r−Sutr+r−SutΦ:右特征向量(按列)Φ: right eigenvectors (columns)Φ⁻¹(Fn,U/ℓ)Φ:对角Φ⁻¹(Fn,U/ℓ)Φ: diagonalRiemann 坐标下:上三角Riemann coordinates: triangular(∂W/∂U)(Fn,U/ℓ)(∂W/∂U)⁻¹第 3 列:−ργ−1/(γ−1) = −2.39column 3: −ργ−1/(γ−1) = −2.39收敛解上每个控制体的 S − S∞(S∞ = 1)S − S∞ of every control volume (S∞ = 1)L=000.0103S 的范围:1.0012 到 1.0103S ranges from 1.0012 to 1.0103最大值在控制体 0(圆圈),紧贴物面、在下游一侧largest in volume 0 (circled), next to the wall, downstream熵在物体附近产生,随流带到下游entropy made near the body is carried downstreamS 不均匀,所以 r± 不是纯粹的特征变量S is not uniform: r± are not pure characteristic variables紫点:上面矩阵所取的状态(控制体 0)violet dot: the state of the matrices (volume 0)
图 8.3 · 通量 Jacobian 的特征结构,取第 2 节的紫色面(f=0f=0)的左侧状态(控制体 0)与单位法向(本页收敛解,M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ)。左上:右特征向量矩阵 Φ\Phi,四列依次对应特征速度 un−cu_n-c、unu_n(熵波)、unu_n(剪切波)、un+cu_n+c。中上:用左特征向量 Φ−1\Phi^{-1} 作相似变换后,计算图给出的 Fn,U/ℓF_{n,U}/\ell 成为对角阵,对角元就是四个特征速度(小于 10−1210^{-12} 的舍入值记为 0)。右上:换成第 4 节的 Riemann 坐标 W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t),同一个矩阵只是上三角:r±r_\pm 两行在熵 SS 那一列还有 −2.39(紫框)。下:数值耗散使收敛解的熵 S=p/ργS=p/\rho^\gamma 在每个控制体都高于来流值 1(熵在物体附近产生,随流带到下游一侧),所以远场用 r±r_\pm 构造的闭合只是近似的特征闭合。Figure 8.3 · The eigen-structure of the flux Jacobian at the left state of the violet face of Section 2 (f=0f=0, volume 0), with the unit normal (this page's converged solution, M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ). Top left: the right-eigenvector matrix Φ\Phi, whose four columns belong to the characteristic speeds un−cu_n-c, unu_n (entropy wave), unu_n (shear wave) and un+cu_n+c. Top middle: transformed with the left eigenvectors Φ−1\Phi^{-1}, the Fn,U/ℓF_{n,U}/\ell of the computation graph becomes diagonal, with the four speeds on the diagonal (round-off below 10−1210^{-12} shown as 0). Top right: in the Riemann coordinates W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t) of Section 4 the same matrix is only upper triangular: the rows of r±r_\pm carry −2.39 in the column of the entropy SS (violet box). Bottom: the numerical dissipation has raised the entropy S=p/ργS=p/\rho^\gamma of every control volume above its free-stream value 1 (it is made near the body and carried downstream), so the far-field closure built on r±r_\pm is characteristic only approximately.

逐步推导:面循环的每条语句与它的前向语句

Step by step: every statement of the face loop, and its tangent statement

第一步,前向模式按原始代码的顺序执行,每条赋值语句 x=x(a,b)x=x(a,b) 换成 x˙=∂x∂aa˙+∂x∂bb˙\dot x=\frac{\partial x}{\partial a}\dot a+\frac{\partial x}{\partial b}\dot b。种子是状态切向量 U˙\dot U 与设计方向 D˙=(M˙∞,θ˙,X˙)\dot D=(\dot M_\infty,\dot\theta,\dot X):X˙\dot X 经第 12 节的公式给出每个面的 𝒏˙\dot{\boldsymbol n},(M˙∞,θ˙)(\dot M_\infty,\dot\theta) 经第 10 节的公式给出 U˙∞\dot U_\infty。第二步,内部面(右侧的语句与左侧相同;作用在四分量向量上的写成 4×44\times4 块):

Step 1, the tangent mode runs in the order of the primal code, and every assignment x=x(a,b)x=x(a,b) becomes x˙=∂x∂aa˙+∂x∂bb˙\dot x=\frac{\partial x}{\partial a}\dot a+\frac{\partial x}{\partial b}\dot b. The seeds are the state tangent U˙\dot U and the design direction D˙=(M˙∞,θ˙,X˙)\dot D=(\dot M_\infty,\dot\theta,\dot X): X˙\dot X gives every face its 𝒏˙\dot{\boldsymbol n} through the formulas of Section 12, and (M˙∞,θ˙)(\dot M_\infty,\dot\theta) give U˙∞\dot U_\infty through those of Section 10. Step 2, an interior face (the right side repeats the statements of the left side; statements on four-component vectors are written as 4×44\times4 blocks):

步骤step原始语句primal statement前向语句tangent statement
读取两侧gatherUL=U[L]U_L=U[L]
UR=U[R]U_R=U[R]
U˙L=U˙[L]\dot U_L=\dot U[L]
U˙R=U˙[R]\dot U_R=\dot U[R]
原始变量(prim(),4×4 块)primitives (prim(), 4×4 block)YL=(ρ,u,v,p)LY_L=(\rho,u,v,p)_LY˙L=YU(UL) U˙L\dot Y_L=Y_U(U_L)\,\dot U_L
物理通量(physical(),4×4 块)physical flux (physical(), 4×4 block)FL=Fn(UL)F_L=F_n(U_L)F˙L=Fn,Y(UL) Y˙L+Fn˙(UL)\dot F_L=F_{n,Y}(U_L)\,\dot Y_L+F_{\dot n}(U_L)
面长face lengthℓ=∣𝒏∣\ell=|\boldsymbol n|ℓ˙=𝒏^⋅𝒏˙\dot\ell=\hat{\boldsymbol n}\cdot\dot{\boldsymbol n}
数值通量numerical fluxH=12(FL+FR)−αℓ2(UR−UL)H=\tfrac12(F_L+F_R)-\tfrac{\alpha\ell}2(U_R-U_L)H˙=12(F˙L+F˙R)−αℓ2(U˙R−U˙L)−αℓ˙2(UR−UL)\dot H=\tfrac12(\dot F_L+\dot F_R)-\tfrac{\alpha\ell}2(\dot U_R-\dot U_L)-\tfrac{\alpha\dot\ell}2(U_R-U_L)
分发scatterRL+=HR_L\mathrel{\text{+=}}H
RR-=HR_R\mathrel{\text{-=}}H
R˙L+=H˙\dot R_L\mathrel{\text{+=}}\dot H
R˙R-=H˙\dot R_R\mathrel{\text{-=}}\dot H

Fn,YY˙LF_{n,Y}\dot Y_L 与 YUU˙LY_U\dot U_L 就是本节前面推导的两个块;Fn˙(U)=Fx(U)n˙x+Fy(U)n˙yF_{\dot n}(U)=F_x(U)\dot n_x+F_y(U)\dot n_y 是几何的贡献(第 12 节)。第三步,物面面连同目标函数(目标在物面面上逐项累加):

Fn,YY˙LF_{n,Y}\dot Y_L and YUU˙LY_U\dot U_L are the two blocks derived earlier in this section; Fn˙(U)=Fx(U)n˙x+Fy(U)n˙yF_{\dot n}(U)=F_x(U)\dot n_x+F_y(U)\dot n_y is the geometry’s part (Section 12). Step 3, a wall face together with the objective, which accumulates term by term on the wall faces:

步骤step原始语句primal statement前向语句tangent statement
读取gatherUL=U[L]U_L=U[L]U˙L=U˙[L]\dot U_L=\dot U[L]
压力pressurep=p(UL)p=p(U_L)p˙=∂p∂U U˙L\dot p=\frac{\partial p}{\partial U}\,\dot U_L
壁面通量wall fluxH=(0,  pnx,  pny,  0)TH=(0,\;pn_x,\;pn_y,\;0)^TH˙=(0,  p˙nx+pn˙x,  p˙ny+pn˙y,  0)T\dot H=(0,\;\dot pn_x+p\dot n_x,\;\dot pn_y+p\dot n_y,\;0)^T
分发scatterRL+=HR_L\mathrel{\text{+=}}HR˙L+=H˙\dot R_L\mathrel{\text{+=}}\dot H
目标objectiveJ+=p 𝒏⋅𝒆dJ\mathrel{\text{+=}}p\,\boldsymbol n\cdot\boldsymbol e_dJ˙+=p˙ 𝒏⋅𝒆d+p 𝒏˙⋅𝒆d\dot J\mathrel{\text{+=}}\dot p\,\boldsymbol n\cdot\boldsymbol e_d+p\,\dot{\boldsymbol n}\cdot\boldsymbol e_d

第四步,远场面:代码 far() 先重建边界状态 UbU_b,再按内部面的公式算通量(UR=UbU_R=U_b)。⋆\star 在入流面是来流 ∞\infty,在出流面是内侧 LL;𝒕^=(−n^y,n^x)\hat{\boldsymbol t}=(-\hat n_y,\hat n_x),γ=1.4\gamma=1.4 时 2/(γ−1)=52/(\gamma-1)=5:

Step 4, a far-field face: far() in the code first rebuilds the boundary state UbU_b and then evaluates the interior-face flux with UR=UbU_R=U_b. ⋆\star is the free stream ∞\infty at an inflow face and the interior LL at an outflow face; 𝒕^=(−n^y,n^x)\hat{\boldsymbol t}=(-\hat n_y,\hat n_x), and with γ=1.4\gamma=1.4, 2/(γ−1)=52/(\gamma-1)=5:

步骤step原始语句primal statement前向语句tangent statement
单位法向unit normal𝒏^=𝒏/ℓ\hat{\boldsymbol n}=\boldsymbol n/\ell𝒏^˙=(𝒏˙−𝒏^ ℓ˙)/ℓ\dot{\hat{\boldsymbol n}}=(\dot{\boldsymbol n}-\hat{\boldsymbol n}\,\dot\ell)/\ell
来流free streamU∞=U∞(M∞,θ)U_\infty=U_\infty(M_\infty,\theta)U˙∞=∂U∞∂M∞M˙∞+∂U∞∂θθ˙\dot U_\infty=\frac{\partial U_\infty}{\partial M_\infty}\dot M_\infty+\frac{\partial U_\infty}{\partial\theta}\dot\theta
两侧原始变量与声速primitives and sound speed, both sidesYL,  cL;  Y∞,  c∞Y_L,\;c_L;\;Y_\infty,\;c_\inftyY˙=YUU˙\dot Y=Y_U\dot U
c˙=γ2ρcp˙−c2ρρ˙\dot c=\frac{\gamma}{2\rho c}\dot p-\frac{c}{2\rho}\dot\rho
法向与切向速度normal and tangential velocityun=𝒖⋅𝒏^u_n=\boldsymbol u\cdot\hat{\boldsymbol n}
ut=𝒖⋅𝒕^u_t=\boldsymbol u\cdot\hat{\boldsymbol t}
u˙n=𝒖˙⋅𝒏^+𝒖⋅𝒏^˙\dot u_n=\dot{\boldsymbol u}\cdot\hat{\boldsymbol n}+\boldsymbol u\cdot\dot{\hat{\boldsymbol n}}
u˙t=𝒖˙⋅𝒕^+𝒖⋅𝒕^˙\dot u_t=\dot{\boldsymbol u}\cdot\hat{\boldsymbol t}+\boldsymbol u\cdot\dot{\hat{\boldsymbol t}}
Riemann 不变量Riemann invariantsr+=un,L+5cLr_+=u_{n,L}+5c_L
r−=un,∞−5c∞r_-=u_{n,\infty}-5c_\infty
r˙+=u˙n,L+5c˙L\dot r_+=\dot u_{n,L}+5\dot c_L
r˙−=u˙n,∞−5c˙∞\dot r_-=\dot u_{n,\infty}-5\dot c_\infty
边界法向速度与声速boundary normal velocity and sound speedun,b=12(r++r−)u_{n,b}=\tfrac12(r_++r_-)
cb=110(r+−r−)c_b=\tfrac1{10}(r_+-r_-)
u˙n,b=12(r˙++r˙−)\dot u_{n,b}=\tfrac12(\dot r_++\dot r_-)
c˙b=110(r˙+−r˙−)\dot c_b=\tfrac1{10}(\dot r_+-\dot r_-)
迎风侧的熵与切向速度entropy and tangential velocity from the upwind sideSb=p⋆/ρ⋆γS_b=p_\star/\rho_\star^\gamma
ut,b=ut,⋆u_{t,b}=u_{t,\star}
S˙b=Sb(p˙⋆/p⋆−γρ˙⋆/ρ⋆)\dot S_b=S_b\bigl(\dot p_\star/p_\star-\gamma\dot\rho_\star/\rho_\star\bigr)
u˙t,b=u˙t,⋆\dot u_{t,b}=\dot u_{t,\star}
密度与压力density and pressureρb=(cb2/(γSb))2.5\rho_b=\bigl(c_b^2/(\gamma S_b)\bigr)^{2.5}
pb=Sbρb1.4p_b=S_b\rho_b^{1.4}
ρ˙b=2.5 ρb(2c˙b/cb−S˙b/Sb)\dot\rho_b=2.5\,\rho_b\bigl(2\dot c_b/c_b-\dot S_b/S_b\bigr)
p˙b=pb(S˙b/Sb+1.4 ρ˙b/ρb)\dot p_b=p_b\bigl(\dot S_b/S_b+1.4\,\dot\rho_b/\rho_b\bigr)
速度velocityub=un,bn^x−ut,bn^yu_b=u_{n,b}\hat n_x-u_{t,b}\hat n_y
vb=un,bn^y+ut,bn^xv_b=u_{n,b}\hat n_y+u_{t,b}\hat n_x
u˙b=u˙n,bn^x+un,bn^˙x−u˙t,bn^y−ut,bn^˙y\dot u_b=\dot u_{n,b}\hat n_x+u_{n,b}\dot{\hat n}_x-\dot u_{t,b}\hat n_y-u_{t,b}\dot{\hat n}_y
v˙b=u˙n,bn^y+un,bn^˙y+u˙t,bn^x+ut,bn^˙x\dot v_b=\dot u_{n,b}\hat n_y+u_{n,b}\dot{\hat n}_y+\dot u_{t,b}\hat n_x+u_{t,b}\dot{\hat n}_x
守恒量(4×4 块)conserved variables (4×4 block)Ub=U(ρb,ub,vb,pb)U_b=U(\rho_b,u_b,v_b,p_b)U˙b=YU(Ub)−1 Y˙b\dot U_b=Y_U(U_b)^{-1}\,\dot Y_b
数值通量numerical fluxH=H(UL,Ub,𝒏)H=H(U_L,U_b,\boldsymbol n)同内部面,U˙R=U˙b\dot U_R=\dot U_bas for an interior face, U˙R=U˙b\dot U_R=\dot U_b

第五步,把 U˙b\dot U_b 对 U˙L\dot U_L 的依赖单独拿出来,就是第 4 节的 GUG_U;把对 𝒏^˙\dot{\hat{\boldsymbol n}} 与 U˙∞\dot U_\infty 的依赖拿出来,就是远场对几何与来流的导数(第 12、10 节)。整张表是同一个程序的前向,不需要为边界单独推一套公式。用本节末尾推导的字母来说,这个闭合属于它的重构语句:边界状态是远场面的一个输入,所以那里的 ZfZ_f 对存储状态不是单位阵,而是带着这个 GUG_U;对单位法向与来流的导数则在 KfK_f 里。

Step 5, the part of U˙b\dot U_b that depends on U˙L\dot U_L is the GUG_U of Section 4; the parts that depend on 𝒏^˙\dot{\hat{\boldsymbol n}} and U˙∞\dot U_\infty are the far field’s derivatives with respect to the geometry and the free stream (Sections 12 and 10). The whole table is the tangent of one program; the boundary needs no separate set of formulas. In the letters of the derivation at the end of this section, the closure is part of its reconstruct statement: the boundary state is one of the face inputs there, so on a far-field face ZfZ_f is not the identity on the stored state but carries this GUG_U, and KfK_f carries the derivatives with respect to the unit normal and the free stream.

核对:把三张表的语句直接写成一段独立的程序(不经过计算图),用一组固定的种子 (U˙,D˙)(\dot U,\dot D)(D˙\dot D 的全部分量都不为零)在全部 48 个面上执行:共 1560 个中间量的切向量与对原始语句做复数步长(ε=10−30\varepsilon=10^{-30},独立的复数运算)的结果最大相对差为 3×10⁻¹⁵;每个面的 H˙\dot H 与计算图的 jvp 最大相差 4×10⁻¹⁵,目标的 J˙\dot J 相差 0(完全相同)(本页代码在构建时计算)。

Check: the statements of the three tables, written as an independent program (not through the computation graph) and run on all 48 faces with one fixed pair of seeds (U˙,D˙)(\dot U,\dot D) (every component of D˙\dot D nonzero): the tangents of all 1560 intermediate quantities differ from complex steps of the primal statements (ε=10−30\varepsilon=10^{-30}, independent complex arithmetic) by at most 3×10⁻¹⁵ relatively; every face’s H˙\dot H differs from the graph’s jvp by at most 4×10⁻¹⁵, and the objective’s J˙\dot J by 0 (identical) (computed by this page’s code at build time).

逐步推导:一个内部面与一个物面面的前向数值

Step by step: the tangent of one interior face and one wall face, in numbers

本节末尾的推导(图 8.5)把同样的语句用在另一个内部面上,即面循环里第一个两侧都不在物面上的内部面 f=16f=16(控制体 8 与 9 之间),种子取节点 17 的 x17x_{17};那里的数值属于那个面和那个种子,不是下面跟踪的紫色面与物面面的数值。

The derivation at the end of this section (Figure 8.5) runs the same statements through another interior face, the first one of the face loop whose two sides are off the wall, f=16f=16 (between volumes 8 and 9), with the seed x17x_{17} of node 17; its numbers belong to that face and that seed, not to the violet face and the wall face followed below.

第一步,取设计方向 D˙=ek\dot D=e_k,DkD_k 是物面节点 1 的横坐标 x1x_{1}。移动这个节点同时改变第 2 节的紫色面 f=0f=0(控制体 0 与 1 之间)与物面面 f=32f=32(控制体 0)的面积向量,所以两个面的 𝒏˙\dot{\boldsymbol n} 都不为零。状态切向量取第 10 节 tangent() 的解 U˙=dU/dx1\dot U=dU/dx_{1}(GMRES 47 次)。第二步,按上面的表逐条计算内部面(四个分量依次是 ρ,ρu,ρv,ρE\rho,\rho u,\rho v,\rho E;Y=(ρ,u,v,p)Y=(\rho,u,v,p)):

Step 1, take the design direction D˙=ek\dot D=e_k with DkD_k the xx coordinate x1x_{1} of wall node 1. Moving this node changes the area vectors of both the violet face of Section 2, f=0f=0 (between volumes 0 and 1), and the wall face f=32f=32 (volume 0), so both faces have a nonzero 𝒏˙\dot{\boldsymbol n}. The state tangent is the solution of tangent() in Section 10, U˙=dU/dx1\dot U=dU/dx_{1} (47 GMRES iterations). Step 2, the statements of the tables above on the interior face (the four components are ρ,ρu,ρv,ρE\rho,\rho u,\rho v,\rho E; Y=(ρ,u,v,p)Y=(\rho,u,v,p)):

量quantity符号symbol原始值primal value前向值tangent value
左侧状态left stateULU_L(0.898, 0.211, 0.02494, 2.198)(−0.009284, −0.01828, 0.05492, −0.03284)
右侧状态right stateURU_R(0.9682, 0.2635, 0.03173, 2.442)(0.006529, 0.006966, 0.04868, 0.01985)
面积向量、面长area vector, length𝒏,  ℓ\boldsymbol n,\;\ell(−0.3919, 0.3289, 0.5116)(0, −1, −0.6429)
左侧原始变量left primitivesYLY_L(0.898, 0.235, 0.02777, 0.8691)(−0.009284, −0.01793, 0.06145, −0.01213)
右侧原始变量right primitivesYRY_R(0.9682, 0.2721, 0.03277, 0.9624)(0.006529, 0.005359, 0.05006, 0.00664)
左侧物理通量left physical fluxFLF_L(−0.07451, −0.3581, 0.2838, −0.2544)(2.909×10⁻⁴, 0.006159, −0.8776, 0.002094)
右侧物理通量right physical fluxFRF_R(−0.09282, −0.4024, 0.3135, −0.3264)(−0.01845, −0.00812, −0.9654, −0.06521)
数值通量numerical fluxHH(−0.1196, −0.4071, 0.2952, −0.4155)(0.02797, 0.01981, −0.914, 0.09868)

第三步,物面面(目标 JyJ_y 在这个面上的一项是 p nyp\,n_y):

Step 3, the wall face (the term of the objective JyJ_y on this face is p nyp\,n_y):

量quantity符号symbol原始值primal value前向值tangent value
状态stateULU_L(0.898, 0.211, 0.02494, 2.198)(−0.009284, −0.01828, 0.05492, −0.03284)
压力pressurepp0.8691−0.01213
面积向量area vector𝒏\boldsymbol n(−0.5126, −0.1914)(0, 1)
壁面通量wall fluxHH(0, −0.4455, −0.1664, 0)(0, 0.006219, 0.8714, 0)
目标的这一项this term of the objectivep nyp\,n_y−0.16640.8714

内部面的 H˙\dot H 同时含三部分:状态经 YUY_U、Fn,YF_{n,Y} 传来的部分,面积向量经 Fn˙F_{\dot n} 传来的部分,以及面长经耗散传来的 −αℓ˙2(UR−UL)-\tfrac{\alpha\dot\ell}2(U_R-U_L)。物面面的 H˙\dot H 的第 0、3 个分量恒为零;p nyp\,n_y 的切向量 0.8714 中,p˙ ny\dot p\,n_y 与 p n˙yp\,\dot n_y 分别为 0.002322 与 0.8691。

The interior face’s H˙\dot H has three parts: the state’s through YUY_U and Fn,YF_{n,Y}, the area vector’s through Fn˙F_{\dot n}, and the face length’s through the dissipation, −αℓ˙2(UR−UL)-\tfrac{\alpha\dot\ell}2(U_R-U_L). Components 0 and 3 of the wall face’s H˙\dot H vanish identically; of the tangent 0.8714 of p nyp\,n_y, p˙ ny\dot p\,n_y contributes 0.002322 and p n˙yp\,\dot n_y 0.8691.

核对:两个面的 H˙\dot H 与计算图 jvp 在同一种子下给出的面切向量分别相差 1×10⁻¹⁶ 与 0(完全相同);全部面的 J˙\dot J 之和为 −0.0994786461,与一次伴随给出的 dJ/dx1=−0.0994786461dJ/dx_{1}=−0.0994786461 相差 3×10⁻¹³(本页代码在构建时计算)。

Check: the H˙\dot H of the two faces differ from the face tangents of the graph’s jvp with the same seeds by at most 1×10⁻¹⁶ and 0 (identical); the J˙\dot J summed over all faces is −0.0994786461, and the single adjoint solve gives dJ/dx1=−0.0994786461dJ/dx_{1}=−0.0994786461, a difference of 3×10⁻¹³ (computed by this page’s code at build time).

forward multiplication: z = a*b
    z_d = b*a_d + a*b_d
forward division: z = a/b
    z_d = a_d/b - a*b_d/(b*b)
# Seed: state_d = the chosen state tangent; D_d = 0 on every design input.
图 8.4Figure 8.4

局部导数怎样组成全局 Jacobian

How local derivatives form the global Jacobian

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

At fixed mesh and differentiable branch, let zfz_f collect all face inputs: the two states, the face geometry (the area vector and the face length) and the boundary data. 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 𝐔˙\dot{\mathbf U} and the design seed D˙\dot D:

z˙f=Tf 𝐔˙+KfD˙,H˙f=Hz,fz˙f,\dot z_f=T_f\,\dot{\mathbf U}+K_f\dot D,\quad\dot H_f=H_{z,f}\dot z_f,
R˙i=∑fℬifH˙f−Q˙i,\dot R_i=\sum_f\mathcal B_{if}\dot H_f-\dot Q_i,
(A)ij=∑fℬifHz,f(Tf)j−∂Qi∂Uj,(A)_{ij}=\sum_f\mathcal B_{if}H_{z,f}(T_f)_j-\frac{\partial Q_i}{\partial U_j},
(RD)ik=∑fℬifHz,f(Kf)k−∂Qi∂Dk.(R_D)_{ik}=\sum_f\mathcal B_{if}H_{z,f}(K_f)_k-\frac{\partial Q_i}{\partial D_k}.

无体源项时 Qi=0Q_i=0。面长度和法向量随节点坐标变化,它们的设计依赖属于 zfz_f,不能只对状态求导。本页是一阶格式,TfT_f 只连接面两侧的状态;matrix-free 仍只需依次执行这些局部乘积,不必存储全局矩阵。

Set Qi=0Q_i=0 when there is no volume source. Face lengths and normals move with the node coordinates; that design dependence belongs in zfz_f, so differentiating the states alone is insufficient. This page is first order, so TfT_f connects only the two states of a face. 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,在本页是两侧状态 ULU_L、URU_R,加上面积向量 𝒏f\boldsymbol n_f 与面长 ℓf\ell_f(一阶时对状态 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(边界面只写一行);本页每个位置有 4 个分量,±1\pm1 代表 ±I\pm I。这四条语句是残差代码的骨架;本页代码里的每一条语句(含边界面与源项)都在本节里逐句写出,并附各自的切向量语句。

(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 two states ULU_L, URU_R, plus the area vector 𝒏f\boldsymbol n_f and the face length ℓf\ell_f (at first order Zf=IZ_f=I on the states). (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); with 4 components per location, ±1\pm1 stands for ±I\pm I. 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),对所有面求和,再减去体源项的切向量 Q˙\dot Q,就逐条语句得到 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 Q˙\dot Q 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˙)−Q˙\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 Q
=(∑fℬ:,fHz,fZf𝒢f−∂Q∂𝐔⏟A)𝐔˙=\Big(\underbrace{\sum_f\mathcal B_{:,f}H_{z,f}Z_f\mathcal G_f-\frac{\partial Q}{\partial \mathbf U}}_{A}\Big)\dot{\mathbf U}
+(∑fℬ:,fHz,fKf−∂Q∂D⏟RD)D˙.+\Big(\underbrace{\sum_f\mathcal B_{:,f}H_{z,f}K_f-\frac{\partial Q}{\partial D}}_{R_D}\Big)\dot D.

上面的 TfT_f 就是 Zf𝒢fZ_f\mathcal G_f;一阶重构不改变取出的状态,ZfZ_f 在状态上是单位阵,TfT_f 就是取值。所以一个面对 AA 的贡献是四个初等 Jacobian 的乘积

The TfT_f above is Zf𝒢fZ_f\mathcal G_f; first-order reconstruction leaves the gathered states unchanged, so ZfZ_f is the identity on the states and TfT_f is the gather. 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 乘积就是按面执行这几次局部运算,不存储任何全局矩阵。图 8.5 按这四条语句逐条播放本页一个面的数值。

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. Figure 8.5 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

取面循环里第一个两侧都不在壁面上的内部面 f=16f=16(控制体 8 在左、9 在右);一阶格式只读两侧的状态,𝒮f={8,9}\mathcal S_f=\{8,9\}。设计种子取 D˙=ek\dot D=e_k,DkD_k 是节点 17 的 xx 坐标 x17x_{17};状态种子取本页前向求解对同一方向给出的解 𝐔˙=UD ek\dot{\mathbf U}=U_D\,e_k(UD=dU/dDU_D=dU/dD 是状态对设计变量的敏感度,A UD=−RDA\,U_D=-R_D,UD ekU_D\,e_k 是它的第 kk 列),于是下面每个切向量都是对 DkD_k 的导数。表中的数由本页代码在构建时算出:基准状态、前向求解与局部导数都用本页自己的程序。

Take the first interior face of the face loop whose two sides are off the wall, f=16f=16 (control volume 8 on the left, 9 on the right); the first-order scheme reads only its two sides, 𝒮f={8,9}\mathcal S_f=\{8,9\}. Seed the design with D˙=ek\dot D=e_k, where DkD_k is the xx-coordinate x17x_{17} of node 17, and the states with this page's tangent solution for the same direction, 𝐔˙=UD ek\dot{\mathbf U}=U_D\,e_k (UD=dU/dDU_D=dU/dD is the sensitivity of the states to the design, A UD=−RDA\,U_D=-R_D, and UD ekU_D\,e_k is its column kk); 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) gatherU8U_{8}0.9565
0.2053
0.03409
2.395
0.006583
0.006444
8.034×10⁻⁵
0.02249
U9U_{9}0.9468
0.2093
0.0228
2.362
0.007669
0.00581
−0.004944
0.02579
设计种子design seedDk=x17D_k=x_{17}1.3441
(2)重构(2) reconstruct𝒏f\boldsymbol n_f−1.366
0.0463
0
1
ℓf\ell_f1.3660.03389
(3)通量(3) fluxHfH_f−0.2685
−1.353
0.05065
−0.9375
0.01881
−0.00887
0.9512
0.06381
Hz,fZf𝐔˙fH_{z,f}Z_f\dot{\mathbf U}_f−0.009962
−0.01491
0.00783
−0.0366
Hz,fKfD˙H_{z,f}K_f\dot D0.02878
0.006044
0.9434
0.1004
(4)分发(4) scatterR8+=HfR_{8}\mathrel{\text{+=}}H_f−0.2685
−1.353
0.05065
−0.9375
0.01881
−0.00887
0.9512
0.06381
R9-=HfR_{9}\mathrel{\text{-=}}H_f0.2685
1.353
−0.05065
0.9375
−0.01881
0.00887
−0.9512
−0.06381

移动节点 17 会改变这个面的面积向量:𝒏˙f=(0, 1)\dot{\boldsymbol n}_f=(0,\,1),ℓ˙f=0.03389\dot\ell_f=0.03389,所以通量的切向量分成两部分:经过状态的 Hz,fZf𝐔˙fH_{z,f}Z_f\dot{\mathbf U}_f 与直接来自设计的 Hz,fKfD˙H_{z,f}K_f\dot D,表中两行相加就是 H˙f\dot H_f。

Moving node 17 changes the area vector of this face: 𝒏˙f=(0, 1)\dot{\boldsymbol n}_f=(0,\,1), ℓ˙f=0.03389\dot\ell_f=0.03389, so the flux tangent has two parts: Hz,fZf𝐔˙fH_{z,f}Z_f\dot{\mathbf U}_f through the states and Hz,fKfD˙H_{z,f}K_f\dot D straight from the design; the two rows of the table add up to H˙f\dot H_f.

所有面与源项的这些贡献加起来是 A 𝐔˙+RD ekA\,\dot{\mathbf U}+R_D\,e_k,在收敛解上为零:这正是下面前向求解的方程。

Summed over all faces and the source terms, these contributions give A 𝐔˙+RD ekA\,\dot{\mathbf U}+R_D\,e_k, which vanishes at the converged solution: the tangent equation solved further on.

图 8.5Figure 8.5
一个面的前向扫描
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​96×4Hz,f​4×11Zf​11×8𝒢f​8×96U˙​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​96×4Hz,f​4×11Zf​11×8𝒢f​8×96U˙​R˙​从右往左作用:applied right to left:先取值,最后分发the gather first, the scatter last
  1. 种子:D˙=ek\dot D=e_k(DkD_k 是节点 17 的 xx 坐标 x17x_{17}),𝐔˙=UD ek\dot{\mathbf U}=U_D\,e_k。在 𝒮f\mathcal S_f 上:U˙8=\dot U_{8}= (0.006583, 0.006444, 8.034×10⁻⁵, 0.02249),U˙9=\dot U_{9}= (0.007669, 0.00581, −0.004944, 0.02579)。

    Seeds: D˙=ek\dot D=e_k (DkD_k is the xx-coordinate x17x_{17} of node 17) and 𝐔˙=UD ek\dot{\mathbf U}=U_D\,e_k. On 𝒮f\mathcal S_f: U˙8=\dot U_{8}= (0.006583, 0.006444, 8.034×10⁻⁵, 0.02249), U˙9=\dot U_{9}= (0.007669, 0.00581, −0.004944, 0.02579).

  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。一阶格式,面输入中的状态就是取出的状态。移动节点 17 会改变这个面的面积向量:𝒏˙f=(0, 1)\dot{\boldsymbol n}_f=(0,\,1),ℓ˙f=0.03389\dot\ell_f=0.03389。

    (2) Reconstruct: z˙f=Zf𝐔˙f+KfD˙\dot z_f=Z_f\dot{\mathbf U}_f+K_f\dot D. First order: the states among the face inputs are the gathered states. Moving node 17 changes the area vector of this face: 𝒏˙f=(0, 1)\dot{\boldsymbol n}_f=(0,\,1), ℓ˙f=0.03389\dot\ell_f=0.03389.

  4. (3)通量:H˙f=Hz,fz˙f=\dot H_f=H_{z,f}\dot z_f= (0.01881, −0.00887, 0.9512, 0.06381),其中经过状态的部分 (−0.009962, −0.01491, 0.00783, −0.0366),直接来自设计的部分 (0.02878, 0.006044, 0.9434, 0.1004)。

    (3) Flux: H˙f=Hz,fz˙f=\dot H_f=H_{z,f}\dot z_f= (0.01881, −0.00887, 0.9512, 0.06381), of which (−0.009962, −0.01491, 0.00783, −0.0366) through the states and (0.02878, 0.006044, 0.9434, 0.1004) straight from the design.

  5. (4)分发:R˙8+=H˙f=\dot R_{8}\mathrel{\text{+=}}\dot H_f= (0.01881, −0.00887, 0.9512, 0.06381),R˙9-=H˙f\dot R_{9}\mathrel{\text{-=}}\dot H_f,即加上 (−0.01881, 0.00887, −0.9512, −0.06381)。

    (4) Scatter: R˙8+=H˙f=\dot R_{8}\mathrel{\text{+=}}\dot H_f= (0.01881, −0.00887, 0.9512, 0.06381) and R˙9-=H˙f\dot R_{9}\mathrel{\text{-=}}\dot H_f, that is (−0.01881, 0.00887, −0.9512, −0.06381) added.

第 9 节Section 9

伴随:转置同一个计算图

Adjoint: transpose the same computation graph

给残差一个四分量的伴随种子 Rˉ\bar R(反向模式的种子,可以任意给定),内部面通量的伴随就是 Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R。再分别乘左右通量导数的转置,累加到状态的伴随 Uˉ\bar U。标量页里的一次乘法变成块转置,但收集、计算、分发的顺序没有变,只是整体反过来执行。

Give the residual a four-component adjoint seed Rˉ\bar R (the reverse-mode seed, arbitrary); an internal face flux then has the adjoint Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R. Apply the transposed left and right flux derivatives and accumulate into the state adjoint Uˉ\bar U. Scalar multiplications become block transposes, while the gather/compute/scatter structure remains the same, only run in reverse.

Uˉ←0;∀f:  Hˉf=RˉL−RˉR,UˉL+=HULTHˉf,UˉR+=HURTHˉf(R=−1: RˉR≔0);Uˉ=ATRˉ.\bar U\leftarrow0;\qquad \forall f:\ \ \bar H_f=\bar R_L-\bar R_R,\quad \bar U_L\mathrel{\text{+=}}H_{U_L}^T\bar H_f,\quad \bar U_R\mathrel{\text{+=}}H_{U_R}^T\bar H_f\quad(R=-1:\ \bar R_R\coloneqq0);\qquad \bar U=A^T\bar R.

为什么语句要倒序执行,为什么伴随量要先清零、每一项贡献又都用“+=”累加,见本节末尾用面循环四条语句的转置所作的推导;本页里,对边界面(R=−1R=-1),式中的 HULH_{U_L} 是第 4 节该面的完整导数 ∂Hf/∂UL\partial H_f/\partial U_L:物面为 ∂Hwall/∂UL\partial H_{\mathrm{wall}}/\partial U_L,远场为 HUL+HUbGUH_{U_L}+H_{U_b}G_U,外侧状态的伴随 Uˉb\bar U_b 经过 GUTG_U^T 回到内侧;几何和来流参数的反传也不能丢掉。保存运算图后逆序访问每个算子,能自动完成这些汇合。

Why the statements run in reverse, and why the adjoints are cleared first and every contribution accumulates with +=, is explained with the transpose of the four face statements in the derivation at the end of this section; on this page, for a boundary face (R=−1R=-1), the HULH_{U_L} of the formula is that face’s complete derivative ∂Hf/∂UL\partial H_f/\partial U_L of Section 4: ∂Hwall/∂UL\partial H_{\mathrm{wall}}/\partial U_L at a wall and HUL+HUbGUH_{U_L}+H_{U_b}G_U at the far field, where the adjoint Uˉb\bar U_b of the exterior state returns through GUTG_U^T to the interior. Geometry and free-stream paths also remain. Reversing the recorded scalar operations handles these merges directly.

图 9.1 是同一张计算图的反向扫描。

Figure 9.1 is the reverse sweep of the same computation graph.

U¯L+= 两条路径+= two pathsY¯Lρ¯, u¯, v¯, p¯​F¯L= ½H¯fU¯R+= 两条路径+= two pathsY¯Rρ¯, u¯, v¯, p¯​F¯R= ½H¯fn¯f, ℓ¯f交给第 12 节to Section 12H¯f = R¯L − R¯R(R¯:残差的伴随种子)(R¯: the residual seed)Hf 的伴随:H¯fadjoint of Hf: H¯f× YUT× Fn,YT× YUT× Fn,YT× ½× ½× (+αℓf/2),加到 U¯L× (+αℓf/2), added to U¯L× (−αℓf/2),加到 U¯R× (−αℓf/2), added to U¯R
图 9.1 · 同一张计算图的反向扫描,箭头从右到左,每个数组 xx 换成它的伴随 xˉ\bar x。先把残差的伴随种子合成面通量的伴随 Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R;平均运算把 Fˉn=12Hˉf\bar F_n=\tfrac12\bar H_f 交给两侧通量,再依次乘 Fn,YTF_{n,Y}^T 与 YUTY_U^T(顺序与前向相反)。每个状态的伴随 Uˉ\bar U 收到两条路径(经 prim() 的一条和虚线的耗散一条),所以必须用 +=;几何的伴随 𝒏ˉf\bar{\boldsymbol n}_f、ℓˉf\bar\ell_f 留给第 12 节的形状导数。Figure 9.1 · The reverse sweep of the same graph, with arrows running right to left and every array xx replaced by its adjoint xˉ\bar x. The residual seeds first combine into the adjoint of the face flux, Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R; the average hands Fˉn=12Hˉf\bar F_n=\tfrac12\bar H_f to each side's flux, which is multiplied by Fn,YTF_{n,Y}^T and then YUTY_U^T (the reverse of the forward order). Each state adjoint Uˉ\bar U receives two paths (one through prim(), one through the dashed dissipation), hence +=; the geometry adjoints 𝒏ˉf\bar{\boldsymbol n}_f and ℓˉf\bar\ell_f are kept for the shape derivatives of Section 12.

逐步推导:逆序转置一个内部面

Step by step: transposing one interior face in reverse order

第一步,一个内部面对 R˙=AU˙\dot R=A\dot U 的贡献分三步:取出两侧的切向量 U˙L\dot U_L、U˙R\dot U_R,乘局部导数 HULH_{U_L}、HURH_{U_R},再按 ℬ\mathcal B 分发(行 LL 加、行 RR 减)。转置时,行与列交换,求和的次序也交换:

Step 1, one interior face contributes to R˙=AU˙\dot R=A\dot U in three steps: take the two tangents U˙L\dot U_L and U˙R\dot U_R, multiply by the local derivatives HULH_{U_L} and HURH_{U_R}, and scatter with ℬ\mathcal B (add in row LL, subtract in row RR). Transposition swaps rows and columns, and with them the order of the sums:

R˙i=∑fℬif(HUL(f) U˙L(f)+HUR(f) U˙R(f)),Uˉj=∑f: L(f)=jHUL(f)THˉf+∑f: R(f)=jHUR(f)THˉf,Hˉf=∑iℬifRˉi=RˉL−RˉR.\dot R_i=\sum_f\mathcal B_{if}\bigl(H_{U_L}(f)\,\dot U_{L(f)}+H_{U_R}(f)\,\dot U_{R(f)}\bigr),\qquad\bar U_j=\sum_{f:\,L(f)=j}H_{U_L}(f)^T\bar H_f+\sum_{f:\,R(f)=j}H_{U_R}(f)^T\bar H_f,\qquad\bar H_f=\sum_i\mathcal B_{if}\bar R_i=\bar R_L-\bar R_R.

第二步,转置把三步的次序倒过来:先按 ℬ\mathcal B 收集面通量的伴随 Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R,再乘两个转置块,最后把结果加回两侧(+=)。第三步,块内部也要倒序:Fn,U=Fn,YYUF_{n,U}=F_{n,Y}Y_U 的转置是 YUTFn,YTY_U^TF_{n,Y}^T。平均运算把 Fˉn=12Hˉf\bar F_n=\tfrac12\bar H_f 交给一侧的物理通量,先乘 Fn,YTF_{n,Y}^T 得到原始变量的伴随 Yˉ=(ρˉ,uˉ,vˉ,pˉ)\bar Y=(\bar\rho,\bar u,\bar v,\bar p),再乘 YUTY_U^T(即第 8 节 YUY_U 的转置,逐项写出):

Step 2, transposition reverses the three steps: first collect the adjoint of the face flux, Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R, with ℬ\mathcal B, then multiply by the two transposed blocks, and finally add the results back into the two sides (+=). Step 3, inside a block the order is reversed too: the transpose of Fn,U=Fn,YYUF_{n,U}=F_{n,Y}Y_U is YUTFn,YTY_U^TF_{n,Y}^T. The average hands Fˉn=12Hˉf\bar F_n=\tfrac12\bar H_f to the physical flux of one side; Fn,YTF_{n,Y}^T comes first and gives the primitive adjoints Yˉ=(ρˉ,uˉ,vˉ,pˉ)\bar Y=(\bar\rho,\bar u,\bar v,\bar p), followed by YUTY_U^T (the transpose of YUY_U from Section 8, written out):

Fˉn=12Hˉf,Yˉ=Fn,YTFˉn,\bar F_n=\tfrac12\bar H_f,\qquad\bar Y=F_{n,Y}^T\bar F_n,
Uˉρ=ρˉ−u uˉ+v vˉρ+γ−12(u2+v2) pˉ,Uˉρu=uˉρ−(γ−1)u pˉ,Uˉρv=vˉρ−(γ−1)v pˉ,UˉρE=(γ−1) pˉ.\begin{gathered}\bar U_\rho=\bar\rho-\frac{u\,\bar u+v\,\bar v}{\rho}+\tfrac{\gamma-1}2(u^2+v^2)\,\bar p,\qquad\bar U_{\rho u}=\frac{\bar u}{\rho}-(\gamma-1)u\,\bar p,\\\bar U_{\rho v}=\frac{\bar v}{\rho}-(\gamma-1)v\,\bar p,\qquad\bar U_{\rho E}=(\gamma-1)\,\bar p.\end{gathered}

第四步,加上耗散项的转置:UˉL\bar U_L 再加 αℓ2Hˉf\tfrac{\alpha\ell}2\bar H_f,UˉR\bar U_R 再减 αℓ2Hˉf\tfrac{\alpha\ell}2\bar H_f(II 的转置还是 II)。第五步,Uˉ=ATRˉ\bar U=A^T\bar R 是所有面贡献之和:先 Uˉ←0\bar U\leftarrow0,再逐面 UˉL+=…\bar U_L\mathrel{\text{+=}}\dots、UˉR+=…\bar U_R\mathrel{\text{+=}}\dots;面的访问次序不影响求和;为什么每个伴随量都要先清零、再用 += 累加,见本节末尾的推导。物面面的转置已在第 4 节给出,远场面的转置在第 4 节的 GUTG_U^T 部分。

Step 4, add the transpose of the dissipation: UˉL\bar U_L gains αℓ2Hˉf\tfrac{\alpha\ell}2\bar H_f and UˉR\bar U_R loses αℓ2Hˉf\tfrac{\alpha\ell}2\bar H_f (the transpose of II is II). Step 5, Uˉ=ATRˉ\bar U=A^T\bar R is the sum of all face contributions: first Uˉ←0\bar U\leftarrow0, then UˉL+=…\bar U_L\mathrel{\text{+=}}\dots and UˉR+=…\bar U_R\mathrel{\text{+=}}\dots face by face; the order of the faces does not change the sum; why every bar is cleared first and accumulates with += is explained in the derivation at the end of this section. The transpose of a wall face is given in Section 4, that of a far-field face in the GUTG_U^T part of Section 4.

核对:以 JyJ_y 的伴随解为种子 Rˉ=ψ\bar R=\psi,即 Hˉf=ψL−ψR\bar H_f=\psi_L-\psi_R,在全部 32 个内部面上,第三、四步的两段公式与计算图逆序(faceVjp)的结果最大相差 6×10⁻¹⁷(本页代码在构建时计算)。

Check: with the seed Rˉ=ψ\bar R=\psi of the adjoint for JyJ_y, i.e. Hˉf=ψL−ψR\bar H_f=\psi_L-\psi_R, the two-stage formulas of Steps 3 and 4 differ from the reversed graph (faceVjp) by at most 6×10⁻¹⁷ on all 32 interior faces (computed by this page’s code at build time).

图 9.2 在同一个面上给出转置块的位置和一次反向计算的数值。

Figure 9.2 shows where the transposed blocks sit and the numbers of one reverse evaluation on the same face.

这个面在 AT​ 中的四个块the four blocks of this face in AT​LRLR+(∂H/∂UL)T+(∂H/∂UR)T−(∂H/∂UL)T−(∂H/∂UR)T−0.160−0.0130.177−0.042ρρuρvρEH¯f = ψL − ψR−0.0880.0500.033−0.007U¯L += (∂H/∂UL)T H¯f0.0760.064−0.1500.037U¯R += (∂H/∂UR)T H¯f与计算图逆序(faceVjp)比较:against the reversed graph (faceVjp):最大差 6×10⁻¹⁷largest difference 6×10⁻¹⁷行 L:(∂H/∂UL)T R¯L − (∂H/∂UL)T R¯Rrow L: (∂H/∂UL)T R¯L − (∂H/∂UL)T R¯R= (∂H/∂UL)T H¯f
图 9.2 · 同一个面 f=0f=0 在 ATA^T 中的四个块(左):转置把第 8 节的块 (L,R)(L,R) 搬到 (R,L)(R,L),并把每块转置。matrix-free 的伴随不存这些块,而是像右边这样计算:先把两侧的残差种子合成面通量的伴随 Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R(这里取 Rˉ=ψ\bar R=\psi,即本页 JyJ_y 的伴随解,所以 Hˉf=ψL−ψR\bar H_f=\psi_L-\psi_R),再分别乘 HULTH_{U_L}^T 与 HURTH_{U_R}^T 累加到两侧的 UˉL\bar U_L、UˉR\bar U_R。行 LL 中两个块作用在 RˉL\bar R_L 与 RˉR\bar R_R 上,合起来正好是 HULTHˉfH_{U_L}^T\bar H_f。Figure 9.2 · The four blocks of the same face f=0f=0 in ATA^T (left): transposition moves the block (L,R)(L,R) of Section 8 to (R,L)(R,L) and transposes every block. The matrix-free adjoint stores none of them and computes as on the right: the two sides' residual seeds first combine into the adjoint of the face flux, Hˉf=RˉL−RˉR\bar H_f=\bar R_L-\bar R_R (here Rˉ=ψ\bar R=\psi, this page's adjoint for JyJ_y, so Hˉf=ψL−ψR\bar H_f=\psi_L-\psi_R), which is then multiplied by HULTH_{U_L}^T and HURTH_{U_R}^T and accumulated into UˉL\bar U_L and UˉR\bar U_R. In row LL the two blocks act on RˉL\bar R_L and RˉR\bar R_R, and together give exactly HULTHˉfH_{U_L}^T\bar H_f.

逐步推导:面循环的每条语句与它的伴随语句(逆序,+=)

Step by step: every statement of the face loop, and its adjoint statement (reverse order, +=)

第一步,反向模式按与第 8 节的表相反的顺序执行:语句 x=x(a,b)x=x(a,b) 换成 aˉ+=∂x∂axˉ\bar a\mathrel{\text{+=}}\frac{\partial x}{\partial a}\bar x、bˉ+=∂x∂bxˉ\bar b\mathrel{\text{+=}}\frac{\partial x}{\partial b}\bar x,4×44\times4 块换成它的转置。开始前 Uˉ←0\bar U\leftarrow0、𝒏ˉ←0\bar{\boldsymbol n}\leftarrow0、Xˉ←0\bar X\leftarrow0、Mˉ∞←0\bar M_\infty\leftarrow0、θˉ←0\bar\theta\leftarrow0;种子是残差的伴随 Rˉ\bar R,以及目标的 Jˉ\bar J。第二步,内部面(表从下往上是原始语句的顺序;右侧与左侧相同):

Step 1, the reverse mode runs the tables of Section 8 in the opposite order: a statement x=x(a,b)x=x(a,b) becomes aˉ+=∂x∂axˉ\bar a\mathrel{\text{+=}}\frac{\partial x}{\partial a}\bar x and bˉ+=∂x∂bxˉ\bar b\mathrel{\text{+=}}\frac{\partial x}{\partial b}\bar x, and a 4×44\times4 block becomes its transpose. Before the sweep, Uˉ←0\bar U\leftarrow0, 𝒏ˉ←0\bar{\boldsymbol n}\leftarrow0, Xˉ←0\bar X\leftarrow0, Mˉ∞←0\bar M_\infty\leftarrow0 and θˉ←0\bar\theta\leftarrow0; the seeds are the residual adjoint Rˉ\bar R and the objective’s Jˉ\bar J. Step 2, an interior face (read upwards, the table is in the primal order; the right side repeats the left side):

步骤step原始语句primal statement伴随语句(逆序)adjoint statement (reverse order)
分发scatterRL+=HR_L\mathrel{\text{+=}}H
RR-=HR_R\mathrel{\text{-=}}H
Hˉ=RˉL−RˉR\bar H=\bar R_L-\bar R_R
数值通量numerical fluxH=12(FL+FR)−αℓ2(UR−UL)H=\tfrac12(F_L+F_R)-\tfrac{\alpha\ell}2(U_R-U_L)FˉL=FˉR=12Hˉ\bar F_L=\bar F_R=\tfrac12\bar H
UˉL+=αℓ2Hˉ,  UˉR-=αℓ2Hˉ\bar U_L\mathrel{\text{+=}}\tfrac{\alpha\ell}2\bar H,\;\bar U_R\mathrel{\text{-=}}\tfrac{\alpha\ell}2\bar H
ℓˉ=−α2(UR−UL)THˉ\bar\ell=-\tfrac\alpha2(U_R-U_L)^T\bar H
面长face lengthℓ=∣𝒏∣\ell=|\boldsymbol n|𝒏ˉ+=𝒏^ ℓˉ\bar{\boldsymbol n}\mathrel{\text{+=}}\hat{\boldsymbol n}\,\bar\ell
物理通量(4×4 转置)physical flux (4×4 transpose)FL=Fn(UL)F_L=F_n(U_L)YˉL=Fn,Y(UL)TFˉL\bar Y_L=F_{n,Y}(U_L)^T\bar F_L
𝒏ˉ+=(Fx(UL)TFˉL,  Fy(UL)TFˉL)\bar{\boldsymbol n}\mathrel{\text{+=}}\bigl(F_x(U_L)^T\bar F_L,\;F_y(U_L)^T\bar F_L\bigr)
原始变量(4×4 转置)primitives (4×4 transpose)YL=(ρ,u,v,p)LY_L=(\rho,u,v,p)_LUˉL+=YU(UL)TYˉL\bar U_L\mathrel{\text{+=}}Y_U(U_L)^T\bar Y_L
读取gatherUL=U[L]U_L=U[L]Uˉ[L]+=UˉL\bar U[L]\mathrel{\text{+=}}\bar U_L

Fn,YTFˉLF_{n,Y}^T\bar F_L 与 YUTYˉLY_U^T\bar Y_L 就是本节前面逐项写出的两段;𝒏ˉ\bar{\boldsymbol n} 收集到的几何伴随留给第 12 节。第三步,物面面连同目标函数:

Fn,YTFˉLF_{n,Y}^T\bar F_L and YUTYˉLY_U^T\bar Y_L are the two stages written out earlier in this section; the geometry adjoint collected in 𝒏ˉ\bar{\boldsymbol n} is left for Section 12. Step 3, a wall face together with the objective:

步骤step原始语句primal statement伴随语句(逆序)adjoint statement (reverse order)
目标objectiveJ+=p 𝒏⋅𝒆dJ\mathrel{\text{+=}}p\,\boldsymbol n\cdot\boldsymbol e_dpˉ+=(𝒏⋅𝒆d) Jˉ\bar p\mathrel{\text{+=}}(\boldsymbol n\cdot\boldsymbol e_d)\,\bar J
𝒏ˉ+=p Jˉ 𝒆d\bar{\boldsymbol n}\mathrel{\text{+=}}p\,\bar J\,\boldsymbol e_d
分发scatterRL+=HR_L\mathrel{\text{+=}}HHˉ=RˉL\bar H=\bar R_L
壁面通量wall fluxH=(0,  pnx,  pny,  0)TH=(0,\;pn_x,\;pn_y,\;0)^Tpˉ+=nxHˉρu+nyHˉρv\bar p\mathrel{\text{+=}}n_x\bar H_{\rho u}+n_y\bar H_{\rho v}
𝒏ˉ+=p (Hˉρu,Hˉρv)\bar{\boldsymbol n}\mathrel{\text{+=}}p\,(\bar H_{\rho u},\bar H_{\rho v})
压力pressurep=p(UL)p=p(U_L)UˉL+=(∂p∂U)Tpˉ\bar U_L\mathrel{\text{+=}}\Bigl(\frac{\partial p}{\partial U}\Bigr)^T\bar p
读取gatherUL=U[L]U_L=U[L]Uˉ[L]+=UˉL\bar U[L]\mathrel{\text{+=}}\bar U_L

第四步,远场面,far() 的语句倒过来执行(⋆\star 同第 8 节):

Step 4, a far-field face, the statements of far() run backwards (⋆\star as in Section 8):

步骤step原始语句primal statement伴随语句(逆序)adjoint statement (reverse order)
数值通量numerical fluxH=H(UL,Ub,𝒏)H=H(U_L,U_b,\boldsymbol n)同内部面,右侧为 Uˉb=HUbTHˉ\bar U_b=H_{U_b}^T\bar Has for an interior face, with Uˉb=HUbTHˉ\bar U_b=H_{U_b}^T\bar H on the right
守恒量(4×4 转置)conserved variables (4×4 transpose)Ub=U(ρb,ub,vb,pb)U_b=U(\rho_b,u_b,v_b,p_b)Yˉb=YU(Ub)−T Uˉb\bar Y_b=Y_U(U_b)^{-T}\,\bar U_b
速度velocityub,  vbu_b,\;v_buˉn,b=uˉbn^x+vˉbn^y,  uˉt,b=−uˉbn^y+vˉbn^x\bar u_{n,b}=\bar u_b\hat n_x+\bar v_b\hat n_y,\;\bar u_{t,b}=-\bar u_b\hat n_y+\bar v_b\hat n_x
𝒏^ˉ+=un,b(uˉb,vˉb)+ut,b(vˉb,−uˉb)\bar{\hat{\boldsymbol n}}\mathrel{\text{+=}}u_{n,b}(\bar u_b,\bar v_b)+u_{t,b}(\bar v_b,-\bar u_b)
压力pressurepb=Sbρb1.4p_b=S_b\rho_b^{1.4}Sˉb+=ρb1.4 pˉb\bar S_b\mathrel{\text{+=}}\rho_b^{1.4}\,\bar p_b
ρˉb+=1.4 Sbρb0.4 pˉb\bar\rho_b\mathrel{\text{+=}}1.4\,S_b\rho_b^{0.4}\,\bar p_b
密度densityρb=(cb2/(γSb))2.5\rho_b=\bigl(c_b^2/(\gamma S_b)\bigr)^{2.5}cˉb=5ρbρˉb/cb\bar c_b=5\rho_b\bar\rho_b/c_b
Sˉb+=−2.5 ρbρˉb/Sb\bar S_b\mathrel{\text{+=}}-2.5\,\rho_b\bar\rho_b/S_b
迎风侧的熵与切向速度entropy and tangential velocity from the upwind sideSb=p⋆/ρ⋆γS_b=p_\star/\rho_\star^\gamma
ut,b=ut,⋆u_{t,b}=u_{t,\star}
pˉ⋆+=Sˉb/ρ⋆γ,  ρˉ⋆+=−γSbSˉb/ρ⋆\bar p_\star\mathrel{\text{+=}}\bar S_b/\rho_\star^\gamma,\;\bar\rho_\star\mathrel{\text{+=}}-\gamma S_b\bar S_b/\rho_\star
uˉt,⋆+=uˉt,b\bar u_{t,\star}\mathrel{\text{+=}}\bar u_{t,b}
边界法向速度与声速boundary normal velocity and sound speedun,b,  cbu_{n,b},\;c_brˉ±=12uˉn,b±110cˉb\bar r_\pm=\tfrac12\bar u_{n,b}\pm\tfrac1{10}\bar c_b
Riemann 不变量Riemann invariantsr+,  r−r_+,\;r_-uˉn,L+=rˉ+,  cˉL+=5rˉ+\bar u_{n,L}\mathrel{\text{+=}}\bar r_+,\;\bar c_L\mathrel{\text{+=}}5\bar r_+
uˉn,∞+=rˉ−,  cˉ∞+=−5rˉ−\bar u_{n,\infty}\mathrel{\text{+=}}\bar r_-,\;\bar c_\infty\mathrel{\text{+=}}-5\bar r_-
法向与切向速度(两侧)normal and tangential velocity (both sides)un,  utu_n,\;u_t𝒖ˉ+=𝒏^ uˉn+𝒕^ uˉt\bar{\boldsymbol u}\mathrel{\text{+=}}\hat{\boldsymbol n}\,\bar u_n+\hat{\boldsymbol t}\,\bar u_t
𝒏^ˉ+=uˉn 𝒖+uˉt (v,−u)\bar{\hat{\boldsymbol n}}\mathrel{\text{+=}}\bar u_n\,\boldsymbol u+\bar u_t\,(v,-u)
原始变量与声速(两侧)primitives and sound speed (both sides)Y,  cY,\;cρˉ+=−c2ρcˉ,  pˉ+=γ2ρccˉ\bar\rho\mathrel{\text{+=}}-\frac{c}{2\rho}\bar c,\;\bar p\mathrel{\text{+=}}\frac{\gamma}{2\rho c}\bar c
UˉL+=YUTYˉL,  Uˉ∞=YUTYˉ∞\bar U_L\mathrel{\text{+=}}Y_U^T\bar Y_L,\;\bar U_\infty=Y_U^T\bar Y_\infty
来流free streamU∞(M∞,θ)U_\infty(M_\infty,\theta)Mˉ∞+=(∂U∞∂M∞)TUˉ∞\bar M_\infty\mathrel{\text{+=}}\Bigl(\frac{\partial U_\infty}{\partial M_\infty}\Bigr)^T\bar U_\infty
θˉ+=(∂U∞∂θ)TUˉ∞\bar\theta\mathrel{\text{+=}}\Bigl(\frac{\partial U_\infty}{\partial\theta}\Bigr)^T\bar U_\infty
单位法向unit normal𝒏^=𝒏/ℓ\hat{\boldsymbol n}=\boldsymbol n/\ell𝒏ˉ+=(𝒏^ˉ−𝒏^ (𝒏^⋅𝒏^ˉ))/ℓ\bar{\boldsymbol n}\mathrel{\text{+=}}\bigl(\bar{\hat{\boldsymbol n}}-\hat{\boldsymbol n}\,(\hat{\boldsymbol n}\cdot\bar{\hat{\boldsymbol n}})\bigr)/\ell

第五步,读法。远场的 UˉL\bar U_L 有两条来源:通量直接给的 HULTHˉH_{U_L}^T\bar H,以及经 Uˉb\bar U_b 一路倒回 r+r_+(出流面还有 SS、utu_t)的那一条,它合起来就是第 4 节的 GUTUˉbG_U^T\bar U_b。入流面上 Sˉb\bar S_b 与 uˉt,b\bar u_{t,b} 流向来流 Uˉ∞\bar U_\infty,再变成 Mˉ∞\bar M_\infty、θˉ\bar\theta;rˉ−\bar r_- 永远流向来流。每一行都只是把前向那一行的系数转置,顺序倒过来,并且用 +=。用本节末尾推导的字母来说,经 GUTG_U^T 的那条路径属于远场面的 ZfTZ_f^T,通向设计变量的路径属于 KfTK_f^T。

Step 5, how to read it. The far field’s UˉL\bar U_L has two sources: the flux’s direct HULTHˉH_{U_L}^T\bar H, and the path back through Uˉb\bar U_b to r+r_+ (and at an outflow face to SS and utu_t), which together is the GUTUˉbG_U^T\bar U_b of Section 4. At an inflow face Sˉb\bar S_b and uˉt,b\bar u_{t,b} go to the free stream Uˉ∞\bar U_\infty and on to Mˉ∞\bar M_\infty and θˉ\bar\theta; rˉ−\bar r_- always goes to the free stream. Every row only transposes the coefficients of the corresponding tangent row, in reverse order and with +=. In the letters of the derivation at the end of this section, the path through GUTG_U^T belongs to the ZfTZ_f^T of a far-field face and the paths to the design to KfTK_f^T.

配上第 8 节表的前向种子,一个面的前向语句与逆序语句给出同一个内积(这个面没有的项为零:物面面没有右侧状态与来流,远场面没有右侧状态,只有物面面的 Jˉ\bar J 不为零):

Paired with the tangent seeds of the tables of Section 8, the forward and the reverse statements of one face give the same inner product (a term the face does not have is zero: a wall face has no right state and no free stream, a far-field face no right state, and only a wall face has Jˉ≠0\bar J\neq0):

HˉTH˙+JˉJ˙f=UˉLTU˙L+UˉRTU˙R+𝒏ˉT𝒏˙+Uˉ∞TU˙∞.\bar H^T\dot H+\bar J\dot J_f=\bar U_L^T\dot U_L+\bar U_R^T\dot U_R+\bar{\boldsymbol n}^T\dot{\boldsymbol n}+\bar U_\infty^T\dot U_\infty.

核对:把三张表的逆序语句写成独立的程序,以 Rˉ=ψ\bar R=\psi(JyJ_y 的伴随解)、物面上 Jˉ=1\bar J=1 在全部 48 个面上执行:状态伴随与计算图 faceVjp 最大相差 3×10⁻¹⁶,设计伴随(Mˉ∞\bar M_\infty、θˉ\bar\theta 与经第 12 节的 Xˉ\bar X)最大相差 6×10⁻¹⁷;每个面都满足上面的对偶恒等式,最大相对差 4×10⁻¹⁶(本页代码在构建时计算)。

Check: the reverse statements of the three tables, written as an independent program and run on all 48 faces with Rˉ=ψ\bar R=\psi (the adjoint for JyJ_y) and Jˉ=1\bar J=1 on the wall: the state adjoints differ from the graph’s faceVjp by at most 3×10⁻¹⁶, the design adjoints (Mˉ∞\bar M_\infty, θˉ\bar\theta and Xˉ\bar X through Section 12) by at most 6×10⁻¹⁷; every face satisfies the duality identity above to a relative difference of at most 4×10⁻¹⁶ (computed by this page’s code at build time).

逐步推导:同两个面的伴随数值与一个面上的对偶

Step by step: the adjoint of the same two faces in numbers, and the duality on one face

本节末尾的推导(图 9.5)用同一个种子 Rˉ=ψ\bar R=\psi 反向执行另一个面,即它自己的前向例子里的内部面 f=16f=16(控制体 8 与 9 之间);那里的数值属于那个面,不是这里跟踪的两个面。

The derivation at the end of this section (Figure 9.5) reverses another face with the same seed Rˉ=ψ\bar R=\psi, the interior face f=16f=16 (between volumes 8 and 9) of its own forward example; its numbers belong to that face, not to the two faces followed here.

第一步,种子取 Rˉ=ψ\bar R=\psi(JyJ_y 的伴随解),物面面还有目标的 Jˉ=1\bar J=1。按上面的表逆序计算第 8 节例子中的紫色面 f=0f=0:

Step 1, the seeds are Rˉ=ψ\bar R=\psi (the adjoint for JyJ_y) and, on the wall face, the objective’s Jˉ=1\bar J=1. The reverse statements of the tables above on the violet face f=0f=0 of the example in Section 8:

量quantity伴随语句adjoint statement值value
面通量的伴随flux adjointHˉ=ψL−ψR\bar H=\psi_L-\psi_R(−0.1605, −0.01275, 0.1773, −0.04249)
两侧物理通量的伴随adjoints of the two physical fluxesFˉL=FˉR=12Hˉ\bar F_L=\bar F_R=\tfrac12\bar H(−0.08024, −0.006373, 0.08867, −0.02124)
面长的伴随length adjointℓˉ=−α2(UR−UL)THˉ\bar\ell=-\tfrac\alpha2(U_R-U_L)^T\bar H0.02112
左侧原始变量的伴随left primitive adjointsYˉL=Fn,Y(UL)TFˉL\bar Y_L=F_{n,Y}(U_L)^T\bar F_L(0.006626, 0.05428, −0.05141, 0.03783)
右侧原始变量的伴随right primitive adjointsYˉR=Fn,Y(UR)TFˉR\bar Y_R=F_{n,Y}(U_R)^T\bar F_R(0.007656, 0.05948, −0.05714, 0.03879)
面积向量的伴随area-vector adjoint𝒏ˉ=𝒏^ℓˉ+∑s=L,R(Fx(Us)TFˉs,  Fy(Us)TFˉs)\bar{\boldsymbol n}=\hat{\boldsymbol n}\bar\ell+\sum_{s=L,R}\bigl(F_x(U_s)^T\bar F_s,\;F_y(U_s)^T\bar F_s\bigr)(−0.1004, 0.1673)
左侧状态的伴随left state adjointUˉL=YU(UL)TYˉL+αℓ2Hˉ\bar U_L=Y_U(U_L)^T\bar Y_L+\tfrac{\alpha\ell}2\bar H(−0.08767, 0.05037, 0.03307, −0.006607)
右侧状态的伴随right state adjointUˉR=YU(UR)TYˉR−αℓ2Hˉ\bar U_R=Y_U(U_R)^T\bar Y_R-\tfrac{\alpha\ell}2\bar H(0.07556, 0.06373, −0.1503, 0.03726)

第二步,同一个物面面 f=32f=32:

Step 2, the same wall face f=32f=32:

量quantity伴随语句adjoint statement值value
目标的种子objective seedJˉ\bar J1
面通量的伴随flux adjointHˉ=ψL\bar H=\psi_L(0.05729, −0.1031, 0.102, 0.03902)
压力的伴随pressure adjointpˉ=nyJˉ+nxHˉρu+nyHˉρv\bar p=n_y\bar J+n_x\bar H_{\rho u}+n_y\bar H_{\rho v}−0.1581
面积向量的伴随area-vector adjoint𝒏ˉ=p (Hˉρu,  Hˉρv+Jˉ)\bar{\boldsymbol n}=p\,(\bar H_{\rho u},\;\bar H_{\rho v}+\bar J)(−0.08959, 0.9577)
状态的伴随state adjointUˉL=(∂p/∂U)Tpˉ\bar U_L=(\partial p/\partial U)^T\bar p(−0.001771, 0.01486, 0.001756, −0.06324)

第三步,一个面上的对偶。把第 8 节这两个面的前向值与这里的伴随值配对,每个面单独满足对偶恒等式:

Step 3, the duality on one face. Pairing the tangent values of the two faces in Section 8 with the adjoint values here, each face satisfies the duality identity by itself:

HˉTH˙=−0.171=UˉLTU˙L⏟0.001926+UˉRTU˙R⏟−0.005638+𝒏ˉT𝒏˙⏟−0.1673,HˉTH˙+JˉJ˙f=0.9596=UˉLTU˙L⏟0.001918+𝒏ˉT𝒏˙⏟0.9577.\bar H^T\dot H=−0.171=\underbrace{\bar U_L^T\dot U_L}_{0.001926}+\underbrace{\bar U_R^T\dot U_R}_{−0.005638}+\underbrace{\bar{\boldsymbol n}^T\dot{\boldsymbol n}}_{−0.1673},\qquad\bar H^T\dot H+\bar J\dot J_f=0.9596=\underbrace{\bar U_L^T\dot U_L}_{0.001918}+\underbrace{\bar{\boldsymbol n}^T\dot{\boldsymbol n}}_{0.9577}.

第四步,对全部面求和。左边相加是 ψTR˙+J˙\psi^T\dot R+\dot J:U˙\dot U 满足前向方程,R˙=AU˙+RDD˙=0\dot R=A\dot U+R_D\dot D=0(数值上 ∥R˙∥=2×10−12\|\dot R\|=2×10⁻¹²,ψTR˙=−3×10−13\psi^T\dot R=−3×10⁻¹³),所以左边就是 J˙=−0.09947865\dot J=−0.09947865。右边相加是 UˉTU˙+DˉTD˙\bar U^T\dot U+\bar D^T\dot D:ψ\psi 满足伴随方程,Uˉ=ATψ+JUT=0\bar U=A^T\psi+J_U^T=0(最大 6×10⁻¹³),所以右边就是 Dˉk=−0.09947865\bar D_k=−0.09947865。前向与伴随给出同一个导数,因为它们是同一批乘积按两种次序求和。

Step 4, sum over all faces. The left sides add up to ψTR˙+J˙\psi^T\dot R+\dot J: U˙\dot U solves the tangent equation, so R˙=AU˙+RDD˙=0\dot R=A\dot U+R_D\dot D=0 (numerically ∥R˙∥=2×10−12\|\dot R\|=2×10⁻¹² and ψTR˙=−3×10−13\psi^T\dot R=−3×10⁻¹³), and the left side is J˙=−0.09947865\dot J=−0.09947865. The right sides add up to UˉTU˙+DˉTD˙\bar U^T\dot U+\bar D^T\dot D: ψ\psi solves the adjoint equation, so Uˉ=ATψ+JUT=0\bar U=A^T\psi+J_U^T=0 (at most 6×10⁻¹³), and the right side is Dˉk=−0.09947865\bar D_k=−0.09947865. The tangent and the adjoint give the same derivative because they sum the same products in two orders.

核对:两个面的伴随与计算图 faceVjp 分别相差 1×10⁻¹⁷ 与 1×10⁻¹⁹;两个面的对偶恒等式两边分别相差 3×10⁻¹⁷ 与 0(完全相同);J˙\dot J 与 Dˉk\bar D_k 相差 3×10⁻¹³(本页代码在构建时计算)。

Check: the adjoints of the two faces differ from the graph’s faceVjp by at most 1×10⁻¹⁷ and at most 1×10⁻¹⁹; the two sides of the face dualities differ by at most 3×10⁻¹⁷ and 0 (identical); J˙\dot J and Dˉk\bar D_k differ by 3×10⁻¹³ (computed by this page’s code at build time).

reverse multiplication: z = a*b
    a_b += b*z_b
    b_b += a*z_b
reverse division: z = a/b
    a_b += z_b/b
    b_b -= a*z_b/(b*b)
# Seed: residual_b = the chosen adjoint seed; every other _b starts at 0;
# then visit the recorded operations backwards.

前向与反向由对偶(点积)恒等式联系在一起:对任意种子 U˙\dot U、D˙\dot D 与 Rˉ\bar R,

The forward and reverse modes are tied together by the duality (dot-product) identity: for any seeds U˙\dot U, D˙\dot D and Rˉ\bar R,

RˉTR˙=UˉTU˙+DˉTD˙,R˙=AU˙+RDD˙,Uˉ=ATRˉ,Dˉ=RDTRˉ.\bar R^T\dot R=\bar U^T\dot U+\bar D^T\dot D,\qquad \dot R=A\dot U+R_D\dot D,\quad \bar U=A^T\bar R,\quad \bar D=R_D^T\bar R.

它的状态部分与设计部分就是两个点积测试:

Its state and design parts are the two dot tests:

RˉT(AU˙)=U˙T(ATRˉ),RˉT(RDD˙)=D˙T(RDTRˉ).\bar R^T(A\dot U)=\dot U^T(A^T\bar R),\qquad \bar R^T(R_D\dot D)=\dot D^T(R_D^T\bar R).

点积测试检查转置实现是否相互一致,但不能单独证明 AA 是所需残差的导数:两边若犯同一个错误,测试仍会通过。因此还要做残差方向差分、独立复数运算和全链路目标差分。下面的面板现场给出三个检查,都使用同一组确定的种子 U˙\dot U、D˙\dot D、Rˉ\bar R:状态块与参数/几何块的点积测试(给出两个内积本身及相对误差),以及残差方向中心差分的相对误差随 ε\varepsilon 的变化:

A dot test checks that the two implementations are transposes of each other, but it cannot alone establish that AA differentiates the intended residual: the same mistake on both sides would still pass. Residual directional differences, independent complex arithmetic and full objective reruns provide the other checks. The panel below computes three checks live, all with one fixed set of seeds U˙\dot U, D˙\dot D, Rˉ\bar R: the dot tests of the state block and of the parameter/geometry block (both inner products and their relative difference), and the relative error of the residual directional central difference as a function of ε\varepsilon:

eFD(ε)=∥[R(U+ε U˙,D+ε D˙)−R(U−ε U˙,D−ε D˙)]/(2ε)−R˙∥∥R˙∥,R˙=AU˙+RDD˙.e_{\mathrm{FD}}(\varepsilon)=\frac{\bigl\|\bigl[R(U+\varepsilon\,\dot U,D+\varepsilon\,\dot D)-R(U-\varepsilon\,\dot U,D-\varepsilon\,\dot D)\bigr]/(2\varepsilon)-\dot R\bigr\|}{\|\dot R\|},\qquad \dot R=A\dot U+R_D\dot D.

eFDe_{\mathrm{FD}} 先随 ε2\varepsilon^2 下降(截断误差),再因舍入误差回升;中间存在一段很小的平台,才说明 AA 与 RDR_D 确实是这个残差的导数。播放器可查看某个面的反传如何累加到各控制体。

eFDe_{\mathrm{FD}} first falls like ε2\varepsilon^2 (truncation) and then rises again with round-off; a small plateau in between shows that AA and RDR_D really are the derivatives of this residual. The player shows how a selected face accumulates reverse contributions into its volumes.

图 9.3Figure 9.3

图 9.4 用第 8、9 节的逐条语句构造了两种错误的伴随,把上面说的道理画出来:点积测试不能单独证明 AA 是所需残差的导数。漏掉一条反传路径的伴随立刻被点积测试抓住;前向和伴随犯同一个错误(都把远场状态当作常数),点积测试就检查不出来,只有差分能发现。

Figure 9.4 builds two wrong adjoints from the statements of Sections 8 and 9 and draws the point made above, that a dot test alone cannot prove that AA differentiates the intended residual. An adjoint that loses one return path is caught by the dot test at once; a tangent and an adjoint that make the same mistake (both treat the far-field state as a constant) pass it, and only the difference check finds the error.

10⁻¹⁶10⁻¹²10⁻⁸10⁻⁴10⁰本页的 tangent 与 adjointthis page’s tangent and adjoint(等于计算图的 jvp 与 vjp)(equal to the graph’s jvp and vjp)伴随漏掉 GUT​an adjoint without GUT​(前向不变)(the same tangent)两边都冻结 Ubboth freeze Ub(互为转置,但导数错)(transposes, wrong derivative)相对误差(对数坐标;恰好为 0 的点画在底线上)relative error (log scale; exact zeros sit on the bottom line)点积测试 |R¯T​Ṙ − (ŪT​U˙ + D¯T​Ḋ)| / |R¯T​Ṙ|dot test |R¯T​Ṙ − (ŪT​U˙ + D¯T​Ḋ)| / |R¯T​Ṙ|残差方向差分 eFD(ε = 10⁻⁵)residual difference eFD (ε = 10⁻⁵)
图 9.4 · 点积测试能检出什么、检不出什么。每组用同样的 16 组随机种子 (U˙,D˙,Rˉ)(\dot U,\dot D,\bar R)(本页收敛解,全部 48 个面);圆点是对偶恒等式的相对误差,方块是残差方向中心差分与前向 R˙\dot R 的相对差 eFDe_{\mathrm{FD}}。左:本页的前向与伴随(按第 8、9 节的逐条语句写成,与计算图的 jvp、vjp 一致),两项检查都在舍入与截断误差以内。中:前向不变,伴随在远场只漏掉一条反传路径,即边界状态伴随经 GUTG_U^T 回到内侧状态的那一条(去往来流与单位法向的两条路径都还在),点积测试立即失败(相对误差 8×10⁻² 到 2×10¹),差分检查仍然通过,因为它只检查前向。右:前向与伴随都把远场状态 UbU_b 当作常数,两者互为精确转置,点积测试照样通过(最大 5×10⁻¹⁵),但求的是另一个算子的导数,差分检查失败(3×10⁻¹ 到 9×10⁻¹)。两种检查谁也代替不了谁。Figure 9.4 · What the dot test catches and what it cannot. Each group uses the same 16 random seeds (U˙,D˙,Rˉ)(\dot U,\dot D,\bar R) (this page's converged solution, all 48 faces); dots are the relative error of the duality identity, squares the relative difference eFDe_{\mathrm{FD}} between the residual directional central difference and the tangent R˙\dot R. Left: this page's tangent and adjoint (written from the statements of Sections 8 and 9, equal to the graph's jvp and vjp); both checks sit at round-off and truncation level. Middle: the same tangent with an adjoint that forgets exactly one return path at the far field, the way of the boundary-state adjoint back to the interior state through GUTG_U^T (the paths to the free stream and to the unit normal are kept); the dot test fails at once (relative errors 8×10⁻² to 2×10¹) while the difference check still passes, because it checks only the tangent. Right: a tangent and an adjoint that both treat the far-field state UbU_b as a constant are exact transposes of each other, so the dot test passes (at most 5×10⁻¹⁵), yet they differentiate another operator and the difference check fails (3×10⁻¹ to 9×10⁻¹). Neither check can replace the other.

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

Why reverse accumulation gives the matrix transpose

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

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

RˉTR˙=RˉT(A 𝐔˙)=∑r=1nu∑j=1nuRˉr∂Rr∂UjU˙j\bar R^T\dot R=\bar R^T(A\,\dot{\mathbf U})=\sum_{r=1}^{n_u}\sum_{j=1}^{n_u}\bar R_r\frac{\partial R_r}{\partial U_j}\dot U_j
=∑j=1nuU˙j(∑r=1nu∂Rr∂UjRˉr)=𝐔˙T(ATRˉ)=𝐔˙T𝐔ˉ.=\sum_{j=1}^{n_u}\dot U_j\left(\sum_{r=1}^{n_u}\frac{\partial R_r}{\partial U_j}\bar R_r\right)=\dot{\mathbf U}^T(A^T\bar R)=\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,\Longrightarrow\quad\bar H_f=\bar R_L-\bar R_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,
Qˉi=−Rˉi,zˉf=Hz,fTHˉf,\bar Q_i=-\bar R_i,\quad \bar z_f=H_{z,f}^T\bar H_f,
𝐔ˉ+=TfTzˉf+(∂Q∂𝐔)TQˉ,\bar{\mathbf U}\mathrel{\text{+=}}T_f^T\bar z_f+\Big(\frac{\partial Q}{\partial \mathbf U}\Big)^T\bar Q,
Dˉ+=KfTzˉf+(∂Q∂D)TQˉ.\bar D\mathrel{\text{+=}}K_f^T\bar z_f+\Big(\frac{\partial Q}{\partial D}\Big)^T\bar Q.

本页没有被替换的残差行:边界条件都在边界面的通量里,所以上面的面循环转置就是完整的 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ˉ\bar R 为种子逐条执行:

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ˉ\bar R, and run:

(0)𝐔ˉ←0,  Dˉ←0,(0)\quad \bar{\mathbf U}\leftarrow0,\ \ \bar D\leftarrow0,
(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.

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

After the face loop the source is reversed by the same rule: Qˉi=−Rˉi\bar Q_i=-\bar R_i, 𝐔ˉ+=(∂Q/∂𝐔)TQˉ\bar{\mathbf U}\mathrel{\text{+=}}(\partial Q/\partial \mathbf U)^T\bar Q, Dˉ+=(∂Q/∂D)TQˉ\bar D\mathrel{\text{+=}}(\partial Q/\partial D)^T\bar Q. 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+(∂Q∂Uj)TQˉ.\bar U_j=\sum_{f:\ j\in\mathcal S_f}\big(\mathcal G_f^T\,\bar{\mathbf U}_f\big)_j+\Big(\frac{\partial Q}{\partial U_j}\Big)^T\bar Q.

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}.

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

Summed over all faces, with the pair of the source added (QˉTQ˙\bar Q^T\dot Q with Qˉ=−Rˉ\bar Q=-\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=96n_u=96 个分量的确定性种子,设计方向取下面算例的 DkD_k)做了这个测试:RˉTR˙=−4.33603085511\bar R^T\dot R=-4.33603085511,𝐔ˉT𝐔˙+DˉTD˙=−4.33603085511\bar{\mathbf U}^T\dot{\mathbf U}+\bar D^T\dot D=-4.33603085511,相对差 1×10−151\times10^{-15}。

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=96n_u=96 components, the design direction the DkD_k of the example below): RˉTR˙=−4.33603085511\bar R^T\dot R=-4.33603085511 and 𝐔ˉT𝐔˙+DˉTD˙=−4.33603085511\bar{\mathbf U}^T\dot{\mathbf U}+\bar D^T\dot D=-4.33603085511, a relative difference of 1×10−151\times10^{-15}.

同一个面的反向扫描

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ˉ8TR˙8+Rˉ9TR˙9\bar R_{8}^T\dot R_{8}+\bar R_{9}^T\dot R_{9},然后是 HˉfTH˙f\bar H_f^T\dot H_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ˉ8TR˙8+Rˉ9TR˙9\bar R_{8}^T\dot R_{8}+\bar R_{9}^T\dot R_{9}, then HˉfTH˙f\bar H_f^T\dot H_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ˉ8=ψ8\bar R_{8}=\psi_80.05906
−0.04941
0.06067
−0.00319
0.0100473996
Rˉ9=ψ9\bar R_{9}=\psi_90.1628
−0.09469
0.04838
−0.01428
(4′)取值(4′) gatherHˉf=Rˉ8−Rˉ9\bar H_f=\bar R_{8}-\bar R_{9}−0.1037
0.04528
0.01229
0.01109
0.0100473996
(3′)至(1′)加回(3′)–(1′) add backUˉ8+=\bar U_{8}\mathrel{\text{+=}}−0.1349
0.09547
0.0142
6.33×10⁻⁴
0.0100473996
Uˉ9+=\bar U_{9}\mathrel{\text{+=}}0.1488
−0.02838
−0.0196
−0.02973
Dˉk+=\bar D_k\mathrel{\text{+=}}0.009999

3 级内积相同,最大相对差 5×10−165\times10^{-16}:对偶恒等式在这些数上成立;图 9.5 按反向语句逐条播放这些数。Dˉk=0.009999\bar D_k=0.009999 是这个面对梯度分量 dJ^/dDkd\widehat{J}/dD_k 的贡献,经过 KfTzˉfK_f^T\bar z_f 这条直接路径得到。

The 3 inner products agree, with a largest relative difference of 5×10−165\times10^{-16}: the duality identity holds in these numbers. Figure 9.5 plays them statement by statement in reverse. Dˉk=0.009999\bar D_k=0.009999 is this face's contribution to the gradient component dJ^/dDkd\widehat{J}/dD_k, along the direct path KfTzˉfK_f^T\bar z_f.

图 9.5Figure 9.5
同一个面的反向扫描
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​96×8ZfT​8×11Hz,fT​11×4ℬ:,fT​4×96R¯​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​96×8ZfT​8×11Hz,fT​11×4ℬ:,fT​4×96R¯​U¯​从右往左作用:applied right to left: the先转置的分发,最后转置的取值transposed scatter first, gather last
  1. (0)置零并给种子:𝐔ˉ←0\bar{\mathbf U}\leftarrow0,Dˉ←0\bar D\leftarrow0;Rˉ8=ψ8=\bar R_{8}=\psi_8= (0.05906, −0.04941, 0.06067, −0.00319),Rˉ9=ψ9=\bar R_{9}=\psi_9= (0.1628, −0.09469, 0.04838, −0.01428)。

    (0) Zero and seed: 𝐔ˉ←0\bar{\mathbf U}\leftarrow0, Dˉ←0\bar D\leftarrow0; Rˉ8=ψ8=\bar R_{8}=\psi_8= (0.05906, −0.04941, 0.06067, −0.00319), Rˉ9=ψ9=\bar R_{9}=\psi_9= (0.1628, −0.09469, 0.04838, −0.01428).

  2. (4′)分发的转置是一次取值:Hˉf=Rˉ8−Rˉ9=\bar H_f=\bar R_{8}-\bar R_{9}= (−0.1037, 0.04528, 0.01229, 0.01109)。内积 Rˉ8TR˙8+Rˉ9TR˙9=HˉfTH˙f=\bar R_{8}^T\dot R_{8}+\bar R_{9}^T\dot R_{9}=\bar H_f^T\dot H_f= 0.0100473996。

    (4′) The transposed scatter is a gather: Hˉf=Rˉ8−Rˉ9=\bar H_f=\bar R_{8}-\bar R_{9}= (−0.1037, 0.04528, 0.01229, 0.01109). Inner product Rˉ8TR˙8+Rˉ9TR˙9=HˉfTH˙f=\bar R_{8}^T\dot R_{8}+\bar R_{9}^T\dot R_{9}=\bar H_f^T\dot H_f= 0.0100473996.

  3. (3′)通量的转置:zˉf=Hz,fTHˉf\bar z_f=H_{z,f}^T\bar H_f;一阶格式的面输入就是两侧状态,zˉf\bar z_f 直接给出它们的伴随,面积向量那一部分交给下一步的 KfTK_f^T。

    (3′) The transposed flux: zˉf=Hz,fTHˉf\bar z_f=H_{z,f}^T\bar H_f; at first order the face inputs are the two states, and zˉf\bar z_f is their adjoint, the area-vector part going on to KfTK_f^T in the next step.

  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.009999。

    (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.009999.

  5. (1′)取值的转置是带累加的分发:𝐔ˉ+=𝒢fT𝐔ˉf\bar{\mathbf U}\mathrel{\text{+=}}\mathcal G_f^T\bar{\mathbf U}_f,Uˉ8+=\bar U_{8}\mathrel{\text{+=}} (−0.1349, 0.09547, 0.0142, 6.33×10⁻⁴),Uˉ9+=\bar U_{9}\mathrel{\text{+=}} (0.1488, −0.02838, −0.0196, −0.02973)。内积 𝐔ˉfT𝐔˙f+DˉTD˙=\bar{\mathbf U}_f^T\dot{\mathbf U}_f+\bar D^T\dot D= 0.0100473996。

    (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ˉ8+=\bar U_{8}\mathrel{\text{+=}} (−0.1349, 0.09547, 0.0142, 6.33×10⁻⁴), Uˉ9+=\bar U_{9}\mathrel{\text{+=}} (0.1488, −0.02838, −0.0196, −0.02973). Inner product 𝐔ˉfT𝐔˙f+DˉTD˙=\bar{\mathbf U}_f^T\dot{\mathbf U}_f+\bar D^T\dot D= 0.0100473996.

  6. 对偶:3 级内积相同(最大相对差 5×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 3 inner products agree (largest relative difference 5×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

前向求解:输入改变后状态如何响应

Tangent solve: state response to a design input

设计变量向量是 D=(M∞,θ,X)D=(M_\infty,\theta,X),XX 是全部节点坐标按节点编号排成的向量。把稳态残差沿 DD 求导,得到 A UD=−RDA\,U_D=-R_D(UD=dU/dDU_D=dU/dD 的第 kk 列是对 DkD_k 的状态响应)。右端必须包括远场参数或网格变化对数值残差的直接影响。用与原始 Newton 相同的 GMRES/GS 算法求解,但基准流场保持不动。

The design vector is D=(M∞,θ,X)D=(M_\infty,\theta,X), where XX stacks all node coordinates in node order. Differentiating the steady residual with respect to DD gives A UD=−RDA\,U_D=-R_D (column kk of UD=dU/dDU_D=dU/dD is the state response to DkD_k). The right-hand side includes the direct effect of free-stream or mesh changes. Solve with the same GMRES/GS as Newton, at the fixed converged base state.

A UD=−RD,dJdD=JU UD+JD.A\,U_D=-R_D,\qquad \frac{dJ}{dD}=J_U\,U_D+J_D.

前向模式一次只沿一个设计方向 D˙\dot D 求导:状态的切向量 U˙=UDD˙\dot U=U_D\dot D 满足下面的方程,J˙\dot J 是目标的方向导数。取 D˙=ek\dot D=e_k,U˙\dot U 就是 UDU_D 的第 kk 列 UDek=dU/dDkU_D e_k=dU/dD_k,J˙=dJ/dDk\dot J=dJ/dD_k。前向求解的结果 U˙\dot U 就是代码 tangent() 返回的解数组 x。

The forward mode differentiates along one design direction D˙\dot D at a time: the state tangent U˙=UDD˙\dot U=U_D\dot D solves the equation below, and J˙\dot J is the directional derivative of the objective. With D˙=ek\dot D=e_k, U˙\dot U is column kk of UDU_D, UDek=dU/dDkU_D e_k=dU/dD_k, and J˙=dJ/dDk\dot J=dJ/dD_k. The result U˙\dot U of the tangent solve is the solution array x that tangent() returns in the code.

A U˙=−RD D˙,J˙=JU U˙+JD D˙.A\,\dot U=-R_D\,\dot D,\qquad \dot J=J_U\,\dot U+J_D\,\dot D.

逐步推导:来流参数的导数

Step by step: derivatives with respect to the free-stream parameters

第一步,第 1 节的来流状态(ρ∞=p∞=1\rho_\infty=p_\infty=1,速度大小 M∞γM_\infty\sqrt\gamma,能量 p/(γ−1)+12ρ∣𝒖∣2p/(\gamma-1)+\tfrac12\rho|\boldsymbol u|^2):

Step 1, the free-stream state of Section 1 (ρ∞=p∞=1\rho_\infty=p_\infty=1, speed M∞γM_\infty\sqrt\gamma, energy p/(γ−1)+12ρ∣𝒖∣2p/(\gamma-1)+\tfrac12\rho|\boldsymbol u|^2):

U∞(M∞,θ)=(1,  M∞γcos⁡θ,  M∞γsin⁡θ,  1γ−1+γM∞22)T.U_\infty(M_\infty,\theta)=\Bigl(1,\;M_\infty\sqrt\gamma\cos\theta,\;M_\infty\sqrt\gamma\sin\theta,\;\frac1{\gamma-1}+\frac{\gamma M_\infty^2}2\Bigr)^T.

第二步,逐分量求导(θ\theta 以弧度计):

Step 2, differentiate component by component (θ\theta in radians):

∂U∞∂M∞=(0,  γcos⁡θ,  γsin⁡θ,  γM∞)T,∂U∞∂θ=(0,  −M∞γsin⁡θ,  M∞γcos⁡θ,  0)T.\frac{\partial U_\infty}{\partial M_\infty}=\bigl(0,\;\sqrt\gamma\cos\theta,\;\sqrt\gamma\sin\theta,\;\gamma M_\infty\bigr)^T,\qquad\frac{\partial U_\infty}{\partial\theta}=\bigl(0,\;-M_\infty\sqrt\gamma\sin\theta,\;M_\infty\sqrt\gamma\cos\theta,\;0\bigr)^T.

第三步,U∞U_\infty 只通过远场边界状态 Ub=G(UL;U∞,𝒏^)U_b=G(U_L;U_\infty,\hat{\boldsymbol n}) 进入残差(入/出流分支已冻结),所以前向方程的右端只在远场控制体的行上非零:

Step 3, U∞U_\infty enters the residual only through the far-field boundary states Ub=G(UL;U∞,𝒏^)U_b=G(U_L;U_\infty,\hat{\boldsymbol n}) (with the inflow/outflow branch frozen), so the tangent right-hand side is nonzero only in the rows of the far-field volumes:

RM∞=∑f∈farℬ⋅f  HUb(f) ∂G∂U∞(f) ∂U∞∂M∞,A dUdM∞=−RM∞.R_{M_\infty}=\sum_{f\in\mathrm{far}}\mathcal B_{\cdot f}\;H_{U_b}(f)\,\frac{\partial G}{\partial U_\infty}(f)\,\frac{\partial U_\infty}{\partial M_\infty},\qquad A\,\frac{dU}{dM_\infty}=-R_{M_\infty}.

第四步,第 7 节已说明 JM∞=0J_{M_\infty}=0,所以 dJ/dM∞=JU dU/dM∞dJ/dM_\infty=J_U\,dU/dM_\infty:导数完全来自状态的响应。图 10.1 画出右端的非零位置,图 10.2 画出解出的 dU/dM∞dU/dM_\infty。

Step 4, Section 7 showed JM∞=0J_{M_\infty}=0, so dJ/dM∞=JU dU/dM∞dJ/dM_\infty=J_U\,dU/dM_\infty: the derivative comes entirely from the state response. Figure 10.1 shows where the right-hand side is nonzero and Figure 10.2 the solution dU/dM∞dU/dM_\infty.

核对:第二步的两个公式与对代码 free() 的四阶中心差分(步长 10−310^{-3})最大相差 1×10⁻¹³ 与 1×10⁻¹³;RM∞R_{M_\infty} 的非零行恰好是 8 个远场控制体;JU dU/dM∞=0.0298097877J_U\,dU/dM_\infty=0.0298097877,前向求解给出 0.0298097877,伴随给出 0.0298097877(本页代码在构建时计算)。

Check: the two formulas of Step 2 differ from fourth-order central differences of the code's free() (step 10−310^{-3}) by at most 1×10⁻¹³ and 1×10⁻¹³; the nonzero rows of RM∞R_{M_\infty} are exactly the 8 far-field volumes; JU dU/dM∞=0.0298097877J_U\,dU/dM_\infty=0.0298097877, the tangent solve gives 0.0298097877 and the adjoint 0.0298097877 (computed by this page’s code at build time).

RM∞ ≠ 0 的控制体(8 个)volumes with RM∞ ≠ 0 (8)Ry2 ≠ 0 的控制体(2 个)volumes with Ry2 ≠ 0 (2)y2
图 10.1 · 前向方程右端 RD=∂R/∂DR_D=\partial R/\partial D 在哪里非零(颜色越深,该控制体四个分量的模越大)。左:设计变量 M∞M_\infty 只通过远场边界状态 Ub=G(UL;U∞,𝒏^)U_b=G(U_L;U_\infty,\hat{\boldsymbol n}) 进入残差,所以只有 8 个远场控制体非零。右:物面节点 2 的坐标 y2y_{2} 只改变以它为端点的面的面积向量,所以只有 2 个相邻控制体非零。求解 A UD=−RDA\,U_D=-R_D 之后,这些局部的右端才扩散成图 10.2 那样的全场响应。Figure 10.1 · Where the tangent right-hand side RD=∂R/∂DR_D=\partial R/\partial D is nonzero (darker means a larger norm of the volume's four components). Left: the design variable M∞M_\infty enters the residual only through the far-field boundary states Ub=G(UL;U∞,𝒏^)U_b=G(U_L;U_\infty,\hat{\boldsymbol n}), so only the 8 far-field volumes are nonzero. Right: the coordinate y2y_{2} of wall node 2 changes only the area vectors of the faces that touch it, so only 2 neighbouring volumes are nonzero. Solving A UD=−RDA\,U_D=-R_D spreads these local right-hand sides into a whole-field response like the one in Figure 10.2.
∂ρ/∂M∞−0.55400.554∂ρu/∂M∞−1.3401.34∂ρv/∂M∞−0.34600.346∂ρE/∂M∞−1.4401.44
图 10.2 · 第 10 节的前向解 UM∞=dU/dM∞U_{M_\infty}=dU/dM_\infty 的四个分量,每个单元按它的值着色(蓝正红负,深浅按绝对值,各图有自己的色条)。这是一次 GMRES 求解 AUM∞=−RM∞AU_{M_\infty}=-R_{M_\infty} 的结果,右端只在远场控制体非零;可是响应遍布全场:来流马赫数变了,整个流场都要随之调整。由 p˙=(∂p/∂U) U˙\dot p=(\partial p/\partial U)\,\dot U(这里 U˙=UM∞\dot U=U_{M_\infty})算出物面控制体的 ∂p/∂M∞\partial p/\partial M_\infty:最大值 0.474 在迎风一侧(控制体 4),最小值 −0.671 在背风一侧(控制体 0)。马赫数增大时,物体前后的压力差变大。(M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ。)Figure 10.2 · The four components of the tangent solution UM∞=dU/dM∞U_{M_\infty}=dU/dM_\infty of Section 10, each cell coloured by its value (blue positive, red negative, shaded by magnitude; every panel has its own colour bar). It is the result of one GMRES solve of AUM∞=−RM∞AU_{M_\infty}=-R_{M_\infty}, whose right-hand side is nonzero only in the far-field volumes; yet the response covers the whole field: when the free-stream Mach number changes, the entire flow adjusts. From p˙=(∂p/∂U) U˙\dot p=(\partial p/\partial U)\,\dot U with U˙=UM∞\dot U=U_{M_\infty}, ∂p/∂M∞\partial p/\partial M_\infty in the wall volumes is largest, 0.474, on the upstream side (volume 4) and smallest, −0.671, on the downstream side (volume 0): as the Mach number grows, the pressure difference between the front and the back of the body grows. (M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ.)

前向模式分三步:以 U˙=0\dot U=0、D˙=ek\dot D=e_k 为种子,面循环求残差的切向量 R˙=RDD˙\dot R=R_D\dot D;用 GMRES 解 AU˙=−RDD˙A\dot U=-R_D\dot D;再求 J˙=JUU˙+JDD˙\dot J=J_U\dot U+J_D\dot D。图 10.3 逐帧演示这三步(可以播放、暂停、单步或拖动滑块):右端只在少数几个面上不为零,GMRES 却要把它变成整个流场的响应。

The tangent mode has three steps: with the seeds U˙=0\dot U=0 and D˙=ek\dot D=e_k, the face loop computes the residual tangent R˙=RDD˙\dot R=R_D\dot D; GMRES solves AU˙=−RDD˙A\dot U=-R_D\dot D; then J˙=JUU˙+JDD˙\dot J=J_U\dot U+J_D\dot D. Figure 10.3 shows the three steps frame by frame (play, pause, step or drag the slider): the right-hand side is nonzero on only a few faces, yet GMRES turns it into a response of the whole flow.

100 / 100
x₁所选分量:Ṙ(累加中)selected component: Ṙ (accumulating)所选分量:GMRES 迭代 U˙mselected component: the GMRES iterate U˙m−0.55500.555设计方向 Ḋ = ek:M∞design direction Ḋ = ek: M∞来流马赫数the free-stream Mach number伴随给出 dJ/dDk = 0.0298098the adjoint gives dJ/dDk = 0.0298098设计方向 Ḋ = ek:θdesign direction Ḋ = ek: θ攻角(弧度)the incidence (radian)伴随给出 dJ/dDk = 0.195885the adjoint gives dJ/dDk = 0.195885设计方向 Ḋ = ek:x₁design direction Ḋ = ek: x₁物面节点 1 的 x 坐标(紫圈)the x coordinate of wall node 1 (violet ring)伴随给出 dJ/dDk = −0.0994786the adjoint gives dJ/dDk = −0.0994786第 1 阶段:面循环,U˙ = 0stage 1: the face loop, U˙ = 0每个面算 Ḣf,累加成 Ṙ = RD Ḋeach face adds its Ḣf into Ṙ = RD Ḋ这个面的 Ḣf:Ḣf of this face:已访问的面中 Ḣf ≠ 0 的个数:visited faces with Ḣf ≠ 0:——第 2 阶段:GS 预处理 GMRESstage 2: GS-preconditioned GMRES解 A U˙ = −RD Ḋ;J˙m = JU U˙m + JD Ḋsolves A U˙ = −RD Ḋ; J˙m = JU U˙m + JD Ḋ真残差 ‖A U˙m + RD Ḋ‖:true residual ‖A U˙m + RD Ḋ‖:方向导数 J˙m:directional derivative J˙m:1.44×10⁻¹²0.0298098GMRES 第 52 次迭代(共 52 次)GMRES iteration 52 of 5210⁻¹²10⁻⁸10⁻⁴10⁰02040GMRES 迭代 mGMRES iteration m真残差(对数坐标)true residual (log scale)物面wall远场far field正在访问的面the face being visited
图 10.3 · 前向模式的逐帧演示(数据由本页代码在构建时算出,M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ,目标 JyJ_y)。上方选择设计方向 D˙=ek\dot D=e_k(M∞M_\infty、θ\theta 或物面节点 1 的 xx 坐标)与显示的守恒分量,播放、暂停或逐步查看。第 1 阶段,状态种子 U˙=0\dot U=0,面循环按代码的顺序访问全部 48 个面,每个面算出 H˙f\dot H_f,向左加、向右减,累加成前向方程的右端 R˙=RDD˙\dot R=R_D\dot D:对 M∞M_\infty 与 θ\theta 只有 8 个远场面的 H˙f\dot H_f 不为零,对节点坐标只有与它相邻的 3 个面。第 2 阶段,GS 预处理 GMRES 解 AU˙=−RDD˙A\dot U=-R_D\dot D,网格上是第 mm 次迭代 U˙m\dot U_m,右边是它的真残差与方向导数 J˙m=JUU˙m+JDD˙\dot J_m=J_U\dot U_m+J_D\dot D。最后一次迭代正是本页 tangent() 的解,J˙\dot J 与伴随给出的 dJ/dDkdJ/dD_k 相同。静态画面是 M∞M_\infty 方向的最后一帧(第 52 次迭代)。Figure 10.3 · The tangent mode frame by frame (data computed by this page's code at build time, M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ, objective JyJ_y). Above, choose the design direction D˙=ek\dot D=e_k (M∞M_\infty, θ\theta or the xx coordinate of wall node 1) and the conserved component shown; play, pause or step. Stage 1: with the state seed U˙=0\dot U=0, the face loop visits all 48 faces in the code's order; each face computes H˙f\dot H_f, adds it on the left and subtracts it on the right, building the tangent right-hand side R˙=RDD˙\dot R=R_D\dot D: for M∞M_\infty and θ\theta only the 8 far-field faces have a nonzero H˙f\dot H_f, for the node coordinate only the 3 faces next to it. Stage 2: GS-preconditioned GMRES solves AU˙=−RDD˙A\dot U=-R_D\dot D; the mesh shows the iterate U˙m\dot U_m, the right-hand side its true residual and the directional derivative J˙m=JUU˙m+JDD˙\dot J_m=J_U\dot U_m+J_D\dot D. The last iterate is exactly the solution tangent() returns, and its J˙\dot J equals the adjoint's dJ/dDkdJ/dD_k. The static drawing is the last frame for M∞M_\infty (iteration 52).

攻角在所有导数中以弧度为单位;页首用度数显示。几何参数只移动所选节点,其余节点固定,不包含额外的网格弹性变形。下图显示响应场的某个守恒分量,色条给出范围。

Incidence derivatives are per radian, although the header displays degrees. A coordinate parameter moves only that node; other nodes stay fixed, without an additional mesh-deformation model. The plot shows a chosen conserved component of the response, with a colour bar for its range.

图 10.4Figure 10.4

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

Expand implicit differentiation into a solvable system

设原始残差已经收敛,且当前光滑分支上的 AA 非奇异。取设计种子(方向)D˙\dot D,令 D(t)=D+t D˙D(t)=D+t\,\dot D;前向求解得到的状态切向量是 𝐔˙=ddt𝐔(D+t D˙)∣t=0\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 𝐔˙=ddt𝐔(D+t D˙)∣t=0\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∂UjU˙j0=\dot R_r=\sum_j\frac{\partial R_r}{\partial U_j}\dot U_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)
⟹A 𝐔˙=−RD D˙.\Longrightarrow\quad A\,\dot{\mathbf U}=-R_D\,\dot D.
J^(D)=J(𝐔(D),D),\widehat{J}(D)=J(\mathbf U(D),D),
J˙=ddtJ^(D+t D˙)∣t=0=JU 𝐔˙+JD D˙.\dot J=\frac{d}{dt}\widehat{J}(D+t\,\dot D)\Big|_{t=0}=J_U\,\dot{\mathbf U}+J_D\,\dot D.

实现分三步:状态种子 𝐔˙=0\dot{\mathbf U}=0、设计种子 D˙\dot D,前向求值残差得到 R˙=RD D˙\dot R=R_D\,\dot D;用精确 AA 的乘积求解 𝐔˙\dot{\mathbf U};再以 𝐔˙\dot{\mathbf U} 和 D˙\dot D 为种子对目标前向求导,得到 J˙\dot J。一次 A 𝐔˙A\,\dot{\mathbf U} 只是算子调用(一次前向扫描),不是求解 𝐔˙\dot{\mathbf U}。稳态导数的对象是收敛残差方程,不是 Newton/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 𝐔˙\dot{\mathbf U} with exact AA products; then differentiate the objective forward with the seeds 𝐔˙\dot{\mathbf U} and D˙\dot D to obtain J˙\dot J. One A 𝐔˙A\,\dot{\mathbf U} is an operator application (one forward sweep), not the solution 𝐔˙\dot{\mathbf U}. The steady derivative concerns the converged residual equation, not the Newton/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 与源项两条路径起作用;迭代中的每个 A 𝐔˙A\,\dot{\mathbf U} 是 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 A 𝐔˙A\,\dot{\mathbf U} 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: one solve, many design derivatives

采用与其他页面一致的拉格朗日函数 ℒ=J+ψTR\mathcal L=J+\psi^TR。选择 ATψ=−JUTA^T\psi=-J_U^T 后,状态响应 UDU_D 消掉;每个设计分量只需要直接目标导数加残差反传。换一个目标需要重新解一次伴随,但不需要重新求原始流场。

Use the same Lagrangian convention as the other pages: ℒ=J+ψTR\mathcal L=J+\psi^TR. Choosing ATψ=−JUTA^T\psi=-J_U^T eliminates UDU_D. Each design derivative then combines the direct objective derivative and a residual reverse product. A new objective needs another adjoint solve, but not another primal flow solve.

ATψ=−JUT,dJdD=JD+ψTRD.A^T\psi=-J_U^T,\qquad \frac{dJ}{dD}=J_D+\psi^TR_D.

用反向模式的记号:以 Rˉ=ψ\bar R=\psi 为残差种子做一次反向扫描,状态的伴随是 Uˉ=ATψ=−JUT\bar U=A^T\psi=-J_U^T;再加上目标自身的种子 Jˉ=1\bar J=1,设计变量的伴随 Dˉ=RDTψ+JDT\bar D=R_D^T\psi+J_D^T 就是梯度 (dJ/dD)T(dJ/dD)^T。代码 sensitivities() 的最后一步正是种子为 (Rˉ,Jˉ)=(ψ,1)(\bar R,\bar J)=(\psi,1) 的一次反向扫描。

In reverse-mode notation: one reverse sweep with the residual seed Rˉ=ψ\bar R=\psi gives the state adjoint Uˉ=ATψ=−JUT\bar U=A^T\psi=-J_U^T; adding the objective’s own seed Jˉ=1\bar J=1, the design adjoint Dˉ=RDTψ+JDT\bar D=R_D^T\psi+J_D^T is the gradient (dJ/dD)T(dJ/dD)^T. The last step of sensitivities() in the code is exactly one reverse sweep with the seeds (Rˉ,Jˉ)=(ψ,1)(\bar R,\bar J)=(\psi,1).

图 11.1 画出 JyJ_y 的伴随解。

Figure 11.1 shows the adjoint solution for JyJ_y.

ψρ−1.0401.04ψρu−0.10300.103ψρv−0.11700.117ψρE−0.26100.261
图 11.1 · 第 11 节的伴随解 ψ\psi(目标 JyJ_y)的四个分量,着色方式同上一节的前向解。伴随右端 −JUT-J_U^T 只在 8 个物面控制体非零,并且只进入与压力有关的组合;解却遍布全场。ψi\psi_i 的第 kk 个分量回答:把控制体 ii 第 kk 条守恒方程的残差加上一个小量 ε\varepsilon,JyJ_y 就改变 ε\varepsilon 乘以这个分量(一阶近似,第 11 节的逐步推导)。∣ψρv∣|\psi_{\rho v}| 最大的是控制体 11(−0.117)。Figure 11.1 · The four components of the adjoint solution ψ\psi of Section 11 (objective JyJ_y), coloured as the tangent solution of the previous section. The adjoint right-hand side −JUT-J_U^T is nonzero only in the 8 wall volumes, and only in the pressure-related combination; the solution covers the whole field. Component kk of ψi\psi_i answers: if the residual of conservation equation kk of volume ii is raised by a small ε\varepsilon, JyJ_y changes by ε\varepsilon times this component (to first order; the step-by-step derivation of Section 11). ∣ψρv∣|\psi_{\rho v}| is largest in volume 11 (−0.117).

逐步推导:伴随解的含义,以及一次求解得到全部导数

Step by step: what the adjoint solution means, and why one solve gives every derivative

第一步,设想给第 kk 条残差方程加一个常数 tt:R(U)+t ek=0R(U)+t\,e_k=0(eke_k 是第 kk 个单位向量),收敛解 UU 与目标 JJ 都随 tt 变化。在 t=0t=0 处对 tt 求导:

Step 1, imagine adding a constant tt to residual equation kk: R(U)+t ek=0R(U)+t\,e_k=0 (eke_k the kk-th unit vector), so that the converged UU and the objective JJ depend on tt. Differentiate with respect to tt at t=0t=0:

A dUdt=−ek,dJdt=JU dUdt=−JUA−1ek.A\,\frac{dU}{dt}=-e_k,\qquad\frac{dJ}{dt}=J_U\,\frac{dU}{dt}=-J_UA^{-1}e_k.

第二步,伴随方程 ATψ=−JUTA^T\psi=-J_U^T 转置后就是 ψT=−JUA−1\psi^T=-J_UA^{-1},代入:

Step 2, the adjoint equation ATψ=−JUTA^T\psi=-J_U^T, transposed, reads ψT=−JUA−1\psi^T=-J_UA^{-1}; substitute:

dJdt=ψTek=ψk.\frac{dJ}{dt}=\psi^Te_k=\psi_k.

所以 ψk\psi_k 是目标对第 kk 条方程“残差扰动”的灵敏度,与设计变量无关。第三步,每个设计变量 DjD_j 都只是以 RDjR_{D_j} 的形式扰动残差,于是 dJ/dDj=JDj+ψTRDjdJ/dD_j=J_{D_j}+\psi^TR_{D_j},这正是本节末尾 Lagrangian 推导得到的公式;右边对全部 jj 只需要一次残差的反向扫描 RDTψR_D^T\psi。第四步,计算量:前向方法每个设计变量解一次 A UDj=−RDjA\,U_{D_j}=-R_{D_j},伴随方法无论有多少设计变量都只解一次 ATψ=−JUTA^T\psi=-J_U^T。

So ψk\psi_k is the sensitivity of the objective to a “residual perturbation” of equation kk, independent of any design variable. Step 3, every design variable DjD_j perturbs the residual only through RDjR_{D_j}, hence dJ/dDj=JDj+ψTRDjdJ/dD_j=J_{D_j}+\psi^TR_{D_j}, the formula the Lagrangian derivation at the end of this section arrives at; for all jj at once the right-hand side needs one reverse sweep of the residual, RDTψR_D^T\psi. Step 4, the cost: the tangent method solves A UDj=−RDjA\,U_{D_j}=-R_{D_j} once per design variable, the adjoint method solves ATψ=−JUTA^T\psi=-J_U^T once, however many design variables there are.

核对:取 ∣ψ∣|\psi| 最大的分量(控制体 20 的 ρ 方程,ψk=−1.04155715\psi_k=−1.04155715),用本页的 forcing 接口取 t=±εt=\pm\varepsilon(ε=1×10−4\varepsilon=1×10⁻⁴)重新求解原始问题,中心差分 (J∣t=ε−J∣t=−ε)/(2ε)=−1.04155717(J|_{t=\varepsilon}-J|_{t=-\varepsilon})/(2\varepsilon)=−1.04155717;全部 50 个设计变量逐个做前向求解,与一次伴随给出的梯度最大相差 1×10⁻¹²(梯度元素最大为 0.196)(本页代码在构建时计算)。

Check: take the largest component of ∣ψ∣|\psi| (the ρ equation of volume 20, ψk=−1.04155715\psi_k=−1.04155715), set t=±εt=\pm\varepsilon through the page's forcing hook (ε=1×10−4\varepsilon=1×10⁻⁴) and re-solve the primal problem: the central difference (J∣t=ε−J∣t=−ε)/(2ε)=−1.04155717(J|_{t=\varepsilon}-J|_{t=-\varepsilon})/(2\varepsilon)=−1.04155717; one tangent solve for each of the 50 design variables differs from the gradient of the single adjoint solve by at most 1×10⁻¹² (largest gradient entry 0.196) (computed by this page’s code at build time).

图 11.2 把整页的三条计算链放在一起:前向每个设计方向要一次线性求解,伴随只要一次。

Figure 11.2 puts the three computation chains of the whole page side by side: the tangent needs one linear solve per design direction, the adjoint only one.

原始primalX, M∞, θ48+2nf, ℓf48U∞, UbHf4×48R96U*96J1Newton:R(U*) = 0Newton: R(U*) = 0前向(各数组的切向量 x˙)tangent (the tangent x˙ of each array)X˙, M˙∞, θ˙​n˙f, ℓ˙fU˙∞, U˙bH˙fR˙​U˙​J˙​GMRES:A U˙ = −RD D˙,每个方向一次GMRES: A U˙ = −RD D˙, once per direction伴随(各数组的伴随 x¯)adjoint (the adjoint x¯ of each array)X¯, M¯∞, θ¯​全部导数all derivativesn¯f, ℓ¯fU¯∞, U¯bH¯fR¯ = ψ伴随解adjoint solutionJUT​J 给 U¯ 的部分J’s part of U¯​J¯ = 1种子seedGMRES:AT​ψ = −JUT​,只需一次GMRES: AT​ψ = −JUT​, only once
图 11.2 · 整页的计算链:设计变量 → 面几何 → 边界状态 → 面通量 → 残差 → 收敛解 → 目标(第一行,方框下的数字是数组长度)。第二行是前向:每个设计方向 D˙\dot D 沿同一方向传播一次(每个数组 xx 换成切向量 x˙\dot x),需要一次线性求解。第三行是伴随:从目标的种子 Jˉ=1\bar J=1 出发,反向经过同样的数组,每个方框存的是对应数组的伴随 xˉ\bar x;一次求解 ATψ=−JUTA^T\psi=-J_U^T 就得到对全部 50 个设计变量的导数。Figure 11.2 · The computation chain of the whole page: design variables → face geometry → boundary states → face fluxes → residual → converged state → objective (top row; the numbers under the boxes are array lengths). The middle row is the tangent: each design direction D˙\dot D travels once along the same arrows (every array xx replaced by its tangent x˙\dot x) and needs one linear solve. The bottom row is the adjoint: starting from the seed Jˉ=1\bar J=1 of the objective it runs backwards through the same arrays, each box holding the adjoint xˉ\bar x of its array, and one solve of ATψ=−JUTA^T\psi=-J_U^T gives the derivatives with respect to all 50 design variables.

上面第一、二步给了 ψk\psi_k 一个具体的含义。图 11.3 换成物理的说法:给一个控制体加上质量源或动量源(只加在质量方程或动量方程的右端,能量方程不变),JJ 的变化率就是 −ψ-\psi 的相应分量,所以伴随的密度分量与动量分量可以直接读成“在哪里、朝哪个方向推流体,目标变化最大”,并用重新求解核对。

Steps 1 and 2 above give ψk\psi_k a concrete meaning. Figure 11.3 puts it physically: add a mass source or a momentum source to one control volume (a source on the right-hand side of the mass or of the momentum equations only; the energy equation is unchanged), and JJ changes at the rate of the corresponding component of −ψ-\psi. The density and momentum components of the adjoint can therefore be read as where to push the fluid, and in which direction, to change the objective most, which re-solves confirm.

目标 Jy 的伴随the adjoint of Jy−1.0401.04颜色:−ψρ(单位质量源的 dJ)colour: −ψρ (dJ per unit mass source)目标 Jx 的伴随the adjoint of Jx−1.4301.43颜色:−ψρ(单位质量源的 dJ)colour: −ψρ (dJ per unit mass source)箭头:−(ψρu, ψρv),加在该控制体动量方程上、使 J 增加最快的动量源方向arrows: −(ψρu, ψρv), the momentum source added to a volume that raises J fastest紫圈:箭头最长的控制体,右边的数字用重新求解检验violet ring: the longest arrow, checked by re-solving
图 11.3 · 伴随解告诉我们什么:给第 ii 个控制体的方程加一个源 ss,稳态方程 R(U)−s=0R(U)-s=0 的解随之改变,目标的变化率是 dJ/ds=−ψdJ/ds=-\psi(第 11 节逐步推导的第一、二步,源与残差扰动只差一个符号)。所以颜色 −ψρ-\psi_\rho 是每单位时间注入单位质量时 JJ 的变化,箭头 −(ψρu,ψρv)-(\psi_{\rho u},\psi_{\rho v}) 是加在该控制体动量方程上、使 JJ 增加最快的动量源方向(它只进动量方程;体积力还会在能量方程里做功,这里没有),长度是单位动量源引起的 dJdJ。左:JyJ_y;右:JxJ_x(本页收敛解,M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ)。两个目标的伴随完全不同:同一个流场,问题不同,灵敏度就不同。重新求解核对(中心差分,ε=10−4\varepsilon=10^{-4}):JyJ_y 在控制体 0 沿箭头加单位动量源,dJy/dε=0.1449969dJ_y/d\varepsilon=0.1449969,箭头长度 0.1449969;在控制体 20 注入质量,dJy/ds=1.0415572dJ_y/ds=1.0415572,−ψρ=1.0415572-\psi_\rho=1.0415572;JxJ_x 的对应数值为 0.1993297 与 0.1993297(控制体 6)。Figure 11.3 · What the adjoint says: add a source ss to the equations of volume ii; the solution of the steady equations R(U)−s=0R(U)-s=0 changes, and the objective changes at the rate dJ/ds=−ψdJ/ds=-\psi (Steps 1 and 2 of the derivation in Section 11; a source and a residual perturbation differ only in sign). So the colour −ψρ-\psi_\rho is the change of JJ per unit of mass injected per unit time, and the arrow −(ψρu,ψρv)-(\psi_{\rho u},\psi_{\rho v}) is the direction of the momentum source added to the volume that raises JJ fastest (it enters the momentum equations only; a body force would also do work in the energy equation, which this source does not), its length the dJdJ of a unit momentum source. Left: JyJ_y; right: JxJ_x (this page's converged solution, M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ). The two adjoints differ completely: the same flow, a different question, different sensitivities. Re-solves check this (central differences, ε=10−4\varepsilon=10^{-4}): a unit momentum source along the arrow in volume 0 gives dJy/dε=0.1449969dJ_y/d\varepsilon=0.1449969 against the arrow length 0.1449969; a mass source in volume 20 gives dJy/ds=1.0415572dJ_y/ds=1.0415572 against −ψρ=1.0415572-\psi_\rho=1.0415572; for JxJ_x the force check gives 0.1993297 against 0.1993297 (volume 6).

图 11.4 把第四步所说的代价数出来:前向路线每个设计变量一次线性求解,伴随路线每个目标一次。

Figure 11.4 counts the cost of Step 4: one linear solve per design variable on the tangent route, one per objective on the adjoint route.

0204060JyJxM∞, θ物面节点wall nodes中圈middle ring远场节点far nodes每次线性求解的 GMRES 迭代次数:前向 50 次,伴随 1 次GMRES iterations of each linear solve: 50 tangent, 1 adjoint设计变量 Dk(前向,每个一次求解)design variable Dk (tangent, one solve each)10¹10²10³10⁴125102050累计代价(对数坐标)cumulative cost (log–log)需要导数的设计变量个数design variables differentiated前向:2400tangent: 2400伴随(Jy):52adjoint (Jy): 52两个目标:104two objectives: 104
图 11.4 · 求全部 50 个设计变量的导数要多少次线性迭代(本页页首默认设置,每次 GMRES 迭代做一次精确矩阵乘积和一次 GS 扫描)。左:前向路线每个设计变量解一次 AU˙=−RDekA\dot U=-R_D e_k,每次 46 到 52 次迭代(绿);伴随路线对一个目标只解一次 ATψ=−JUTA^T\psi=-J_U^T,JyJ_y 与 JxJ_x 各 52 与 52 次(红)。右(对数坐标):累计代价随需要求导的设计变量个数变化:前向是斜率 1 的直线,增长到 2400 次;伴随是水平线,一个目标 52 次,两个目标 104 次,与设计变量个数无关。只求一个设计变量的导数时两条路线代价相当。反过来,前向的一次求解对全部目标都有效,伴随则每个目标解一次:设计变量多、目标少时伴随合算。Figure 11.4 · How many linear iterations the derivatives with respect to all 50 design variables take (the default settings at the top of the page; each GMRES iteration applies the exact matrix once and the GS sweep once). Left: the tangent route solves AU˙=−RDekA\dot U=-R_D e_k once per design variable, 46 to 52 iterations each (green); the adjoint route solves ATψ=−JUTA^T\psi=-J_U^T once per objective, 52 and 52 iterations for JyJ_y and JxJ_x (red). Right (log–log): the cumulative cost against the number of design variables differentiated: the tangent is a line of slope 1 that reaches 2400; the adjoint is flat, 52 for one objective and 104 for two, whatever the number of design variables. For a single design variable the two routes cost about the same. Conversely one tangent solve serves every objective and the adjoint needs one solve per objective: the adjoint pays off with many design variables and few objectives.

右预处理使用 P−TP^{-T},即前向 GS 三角算子的精确转置逆。它与完整 ATA^T 不同。停止条件检查未预处理的真实残差 ∥ATψ+JUT∥\|A^T\psi+J_U^T\|,避免预处理缩放掩盖误差。图中乘子对应四条积分平衡方程,不是压力或流速。

Right preconditioning uses P−TP^{-T}, the inverse transpose of the forward GS triangular operator. It is distinct from ATA^T. The stopping test uses the true unpreconditioned residual ∥ATψ+JUT∥\|A^T\psi+J_U^T\|, so preconditioner scaling cannot hide error. The four multiplier components correspond to integrated conservation equations, not to pressure or velocity.

图 11.5 逐帧演示这个伴随求解:每一帧是 GMRES 做了 mm 步之后的 ψm\psi_m,右边同时给出真残差,以及用 ψm\psi_m 算出的梯度的误差。

Figure 11.5 shows this adjoint solve frame by frame: each frame is the ψm\psi_m of GMRES after mm steps, with its true residual and the error of the gradient computed from ψm\psi_m on the right.

52 / 52
所选分量:GMRES 迭代 ψmselected component: the GMRES iterate ψm−1.0401.0410⁻¹²10⁻⁸10⁻⁴10⁰02040GMRES 迭代 mGMRES iteration m真残差 ‖AT​ψm + JUT​‖true residual ‖AT​ψm + JUT​‖10⁻¹⁴10⁻¹⁰10⁻⁶10⁻²02040GMRES 迭代 mGMRES iteration mdJ/dM∞ 的误差error of dJ/dM∞线:|dJ/dM∞(ψm) − dJ/dM∞|line: |dJ/dM∞(ψm) − dJ/dM∞|圈:|rmT​ U˙|,U˙ = dU/dM∞circles: |rmT​ U˙|, U˙ = dU/dM∞真残差:true residual:1.35×10⁻¹²ψm 给出的 dJ/dM∞:dJ/dM∞ from ψm:0.0298098它的误差:its error:0(dJ/dM∞(ψm) = JM∞ + ψmT​RM∞)(dJ/dM∞(ψm) = JM∞ + ψmT​RM∞)GMRES 第 52 次迭代(共 52 次)GMRES iteration 52 of 52
图 11.5 · 伴随方程 ATψ=−JUTA^T\psi=-J_U^T 的 GS 预处理 GMRES 迭代逐帧演示(JyJ_y,本页收敛解,数据在构建时算出;第 mm 帧是 GMRES 做 mm 步后的解 ψm\psi_m,第 52 步就是本页 sensitivities() 的解)。左:所选分量的 ψm\psi_m,信息从物面的右端 −JUT-J_U^T 出发,逐步传遍全场。右上:未预处理的真残差 rm=ATψm+JUTr_m=A^T\psi_m+J_U^T。右下:用 ψm\psi_m 读出的 dJ/dM∞=JM∞+ψmTRM∞dJ/dM_\infty=J_{M_\infty}+\psi_m^TR_{M_\infty} 的误差(线),它与 ∣rmTU˙∣|r_m^T\dot U|(圈,U˙=dU/dM∞\dot U=dU/dM_\infty 是第 10 节的前向解)逐点相同,最大差 1×10⁻¹²:梯度误差正是伴随残差在前向解上的投影(本节末尾 Lagrangian 推导的最后一段)。所以只看迭代次数或预处理残差不够,停止条件用的是真残差。Figure 11.5 · The GS-preconditioned GMRES iterations of the adjoint equation ATψ=−JUTA^T\psi=-J_U^T, frame by frame (JyJ_y, this page's converged solution, data computed at build time; frame mm is the GMRES solution ψm\psi_m after mm steps, and step 52 is the solution sensitivities() returns). Left: the chosen component of ψm\psi_m; the information starts from the right-hand side −JUT-J_U^T on the wall and spreads over the field. Top right: the true, unpreconditioned residual rm=ATψm+JUTr_m=A^T\psi_m+J_U^T. Bottom right: the error of dJ/dM∞=JM∞+ψmTRM∞dJ/dM_\infty=J_{M_\infty}+\psi_m^TR_{M_\infty} read from ψm\psi_m (line); it equals ∣rmTU˙∣|r_m^T\dot U| (circles, U˙=dU/dM∞\dot U=dU/dM_\infty the tangent solution of Section 10) point by point, to 1×10⁻¹²: the gradient error is the adjoint residual projected on the tangent solution (the last paragraph of the Lagrangian derivation at the end of this section). An iteration count or a preconditioned residual alone is therefore not enough; the stopping test uses the true residual.

解出 ψ\psi 之后,梯度只要一次反向扫描。图 11.6 逐个面演示这次扫描:四分量的 Uˉ\bar U 怎样一面一面累加、最后在每个控制体都抵消为零,形状梯度 Xˉ\bar X 又怎样在节点上长出来。

Once ψ\psi is known, the gradient needs one reverse sweep. Figure 11.6 runs this sweep face by face: how the four-component Uˉ\bar U accumulates face after face and finally cancels to zero in every control volume, and how the shape gradient Xˉ\bar X grows at the nodes.

48 / 48
所选分量:Ū 的部分和selected component: the partial sum of Ū−0.63200.632反向扫描:种子 (R¯, J¯) = (ψ, 1)reverse sweep: seeds (R¯, J¯) = (ψ, 1)面按与原始循环相反的次序访问faces visited in the reverse loop order逆序第 48/48 个面:f = 0(内部面)reverse face 48 of 48: f = 0 (interior)全部面都已处理的控制体(黑点):volumes with every face done (dots):24 / 24其中最大的 |Ūi|:their largest |Ūi|:5.57×10⁻¹³M¯∞:M¯∞:0.0298098θ¯:θ¯:0.195885终点:Ū = AT​ψ + JUT​ = 0(伴随方程)end: Ū = AT​ψ + JUT​ = 0 (the adjoint equation)D¯ = RDT​ψ + JDT​ = dJ/dD(梯度)D¯ = RDT​ψ + JDT​ = dJ/dD (the gradient)dJ/dM∞ = 0.0298098,dJ/dθ = 0.195885dJ/dM∞ = 0.0298098, dJ/dθ = 0.195885X¯ 的箭头:长 40 表示 0.139arrows of X¯: length 40 means 0.139(最终梯度的最大值;(the largest final value; a partial sum超过 64 的部分和画成 64)longer than 64 is drawn 64 long)物面wall远场far field正在反向处理的面the face being reversed
图 11.6 · sensitivities() 的最后一步:种子为 (Rˉ,Jˉ)=(ψ,1)(\bar R,\bar J)=(\psi,1) 的一次反向扫描,逐个面演示(JyJ_y,本页收敛解,数据在构建时由第 9 节的逆序语句算出,与计算图的 vjp 一致)。面按与原始循环相反的次序访问,每个面把它的贡献用 += 加到两侧的四分量 Uˉ\bar U、它依赖的节点的 Xˉ\bar X(红色箭头)以及远场面的 Mˉ∞\bar M_\infty、θˉ\bar\theta 上;物面面还带着目标自身的种子 Jˉ=1\bar J=1。一个控制体的全部面都处理完之后(黑点),它的 Uˉi=(ATψ+JUT)i\bar U_i=(A^T\psi+J_U^T)_i 就只剩舍入与伴随求解的残差:伴随方程说的正是每个控制体收到的伴随相互抵消。最后一帧 Uˉ\bar U 处处为零(最大 6×10⁻¹³),箭头就是对全部 24 个节点、共 48 个坐标的形状梯度 dJ/dXdJ/dX(包括内部与远场节点),Mˉ∞\bar M_\infty、θˉ\bar\theta 就是 dJ/dM∞dJ/dM_\infty、dJ/dθdJ/d\theta。扫描途中 Xˉ\bar X 的部分和最大可达最终最大值的 8.7 倍:一个节点周围各面的贡献都很大,彼此大量抵消(箭头按最终梯度定比例,超过 64 的画成 64)。Figure 11.6 · The last step of sensitivities(): one reverse sweep with the seeds (Rˉ,Jˉ)=(ψ,1)(\bar R,\bar J)=(\psi,1), face by face (JyJ_y, this page's converged solution; the data are computed at build time from the reverse statements of Section 9 and agree with the graph's vjp). The faces are visited in the reverse loop order; each face adds its contributions with += to the four-component Uˉ\bar U of its two sides, to the Xˉ\bar X of the nodes it depends on (red arrows) and, at the far field, to Mˉ∞\bar M_\infty and θˉ\bar\theta; wall faces also carry the objective's own seed Jˉ=1\bar J=1. Once every face of a volume is done (dots), its Uˉi=(ATψ+JUT)i\bar U_i=(A^T\psi+J_U^T)_i is down to round-off and the adjoint solver's residual: the adjoint equation says exactly that the adjoints a volume receives cancel. In the last frame Uˉ\bar U vanishes everywhere (at most 6×10⁻¹³), the arrows are the shape gradient dJ/dXdJ/dX with respect to all 48 coordinates of the 24 nodes (interior and far-field nodes included), and Mˉ∞\bar M_\infty, θˉ\bar\theta are dJ/dM∞dJ/dM_\infty, dJ/dθdJ/d\theta. During the sweep the partial sums of Xˉ\bar X reach 8.7 times the largest final value: the faces around a node contribute large terms that largely cancel (the arrows are scaled by the final gradient, and one longer than 64 is drawn 64 long).
图 11.7Figure 11.7

从有限维 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.

ℒ˙=JU𝐔˙+JDD˙+ψT(A𝐔˙+RDD˙)\dot{\mathcal L}=J_U\dot{\mathbf U}+J_D\dot D+\psi^T(A\dot{\mathbf U}+R_D\dot D)
=(JUT+ATψ)T𝐔˙+(JDT+RDTψ)TD˙.=(J_U^T+A^T\psi)^T\dot{\mathbf U}+(J_D^T+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ψ=−JUT,∇DJ^=JDT+RDTψ,\boxed{A^T\psi=-J_U^T},\qquad\boxed{\nabla_D\widehat J=J_D^T+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ψ=−JUT\bar{\mathbf U}=A^T\psi=-J_U^T,这就是伴随方程。再把目标自己的反向语句(种子 Jˉ=1\bar J=1)累加进同一组横线量:它们给 𝐔ˉ\bar{\mathbf U} 加上 JUTJ_U^T,给 Dˉ\bar D 加上 JDTJ_D^T,于是 𝐔ˉ=JUT+ATψ=0\bar{\mathbf U}=J_U^T+A^T\psi=0。伴随方程说的正是状态的横线量总和为零:状态不再把敏感度往下传,同一次扫描的设计伴随 Dˉ=JDT+RDTψ\bar D=J_D^T+R_D^T\psi 就是梯度 ∇DJ^\nabla_D\widehat J。也可以直接检查与 tangent 的等价性:利用 A 𝐔˙=−RD D˙A\,\dot{\mathbf U}=-R_D\,\dot D,JU 𝐔˙=−ψTA 𝐔˙=ψTRD D˙J_U\,\dot{\mathbf U}=-\psi^TA\,\dot{\mathbf U}=\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ψ=−JUT\bar{\mathbf U}=A^T\psi=-J_U^T, 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 JUTJ_U^T to 𝐔ˉ\bar{\mathbf U} and JDTJ_D^T to Dˉ\bar D, so 𝐔ˉ=JUT+ATψ=0\bar{\mathbf U}=J_U^T+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ˉ=JDT+RDTψ\bar D=J_D^T+R_D^T\psi, is the gradient ∇DJ^\nabla_D\widehat J. Check equivalence with the tangent: A 𝐔˙=−RD D˙A\,\dot{\mathbf U}=-R_D\,\dot D gives JU 𝐔˙=−ψTA 𝐔˙=ψTRD D˙J_U\,\dot{\mathbf U}=-\psi^TA\,\dot{\mathbf U}=\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).

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

The exact operator determines the equation; Newton iterations and GS-preconditioned GMRES 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ψ+JUTr_\psi=A^T\psi+J_U^T. If it is nonzero, the current adjoint directional derivative minus the exact tangent derivative is −rψT𝐔˙-r_\psi^T\dot{\mathbf U}; 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˙=JU𝐔˙+JDD˙,𝐔˙=−A−1RDD˙\dot J=J_U\dot{\mathbf U}+J_D\dot D,\qquad \dot{\mathbf U}=-A^{-1}R_D\dot D
⟹J˙=JDD˙−JU A−1 RDD˙.\Longrightarrow\qquad \dot J=J_D\dot D-J_U\,A^{-1}\,R_D\dot D.

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

The last term is the product of three factors, JUJ_U, 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 JUA−1J_UA^{-1} first: it does not depend on D˙\dot D, so it is solved once. Call this row −ψT-\psi^T:

ψT=−JU A−1⟺ATψ=−JUT,\psi^T=-J_U\,A^{-1}\quad\Longleftrightarrow\quad A^T\psi=-J_U^T,
J˙=(JDT+RDTψ)TD˙=DˉTD˙.\dot J=\big(J_D^T+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=50n_a=50 次,系数 AAna=50n_a=50, 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−JUT-J_U^T
求解之后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 次(UD ekU_D\,e_k 与目标无关)still nan_a (UD ekU_D\,e_k does not depend on the objective)nJn_J 次nJn_J

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

This page has na=50n_a=50 and nu=96n_u=96: the tangent route solves 50 linear systems of order 96, 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

几何导数:面法向、面长与直接压力载荷

Geometry derivatives: normals, lengths and direct pressure load

二维标量已经展示了移动节点如何改变法向。Euler 还需要把单位法向的变化传入远场特征旋转,把面长变化传入耗散。格点对偶面由边中点和顶点平均中心构造,因此一个节点还会影响相邻对偶分段。所有这些几何计算都进入同一条求导链。

As in the scalar example, moving a node changes face normals. Euler additionally differentiates unit normals in the characteristic rotations and lengths in the dissipation. Node-centred dual segments depend on edge midpoints and vertex-average cell centres, so one coordinate affects neighbouring segments. All these constructions enter the same derivative graph.

𝒏˙=(y˙e−y˙s,  −x˙e+x˙s),ℓ˙=𝒏^⋅𝒏˙,𝒏^˙=𝒏˙−𝒏^ ℓ˙ℓ.\dot{\boldsymbol n}=(\dot y_e-\dot y_s,\;-\dot x_e+\dot x_s),\quad\dot\ell=\hat{\boldsymbol n}\cdot\dot{\boldsymbol n},\quad\dot{\hat{\boldsymbol n}}=\frac{\dot{\boldsymbol n}-\hat{\boldsymbol n}\,\dot\ell}{\ell}.

图 12.1 用一个物面面说明上面三个公式的几何意义。

Figure 12.1 shows the geometric meaning of the three formulas above on one wall face.

xsxex˙enfn˙fℓ˙fnf = (xe − xs)⊥ , d⊥ = (dy, −dx)n˙f = (x˙e − x˙s)⊥沿 nf 的部分:ℓ˙f = nf·n˙f / ℓfpart along nf: ℓ˙f = nf·n˙f / ℓf垂直部分:转动单位法向perpendicular part: turns the unit normalnf 是端点坐标的线性函数:nf is linear in the endpoints:∂nf/∂xe = (0, −1),∂nf/∂ye = (1, 0)∂nf/∂xe = (0, −1), ∂nf/∂ye = (1, 0)∂nf/∂xs = (0, 1),∂nf/∂ys = (−1, 0)∂nf/∂xs = (0, 1), ∂nf/∂ys = (−1, 0)
图 12.1 · 移动面的一个端点怎样改变面积向量(物面面 f=32f=32,位移画得比实际导数所用的无穷小位移大,只为看清)。端点 𝒙e\boldsymbol x_e 沿 𝒙˙e\dot{\boldsymbol x}_e 移动时,面积向量的切向量是 𝒏˙f=(𝒙˙e)⊥\dot{\boldsymbol n}_f=(\dot{\boldsymbol x}_e)^{\perp},即把位移方向顺时针转 90°。𝒏˙f\dot{\boldsymbol n}_f 沿 𝒏^\hat{\boldsymbol n} 的部分改变面长(青色虚线),垂直部分转动单位法向;耗散项用前者,远场的特征旋转用后者。Figure 12.1 · How moving one endpoint of a face changes its area vector (wall face f=32f=32; the displacement is drawn much larger than the infinitesimal one of a derivative, only for visibility). When endpoint 𝒙e\boldsymbol x_e moves along 𝒙˙e\dot{\boldsymbol x}_e, the area vector has the tangent 𝒏˙f=(𝒙˙e)⊥\dot{\boldsymbol n}_f=(\dot{\boldsymbol x}_e)^{\perp}, the displacement direction turned clockwise by 90°. The part of 𝒏˙f\dot{\boldsymbol n}_f along 𝒏^\hat{\boldsymbol n} changes the face length (dashed teal) and the perpendicular part turns the unit normal; the dissipation uses the former, the far-field characteristic rotation the latter.

逐步推导:面积向量、面长与单位法向对坐标的导数

Step by step: derivatives of area vectors, face lengths and unit normals with respect to the coordinates

第一步,面积向量是端点坐标的线性函数,𝒏f=(𝒙e−𝒙s)⊥\boldsymbol n_f=(\boldsymbol x_e-\boldsymbol x_s)^{\perp},逐个坐标求导:

Step 1, the area vector is a linear function of the endpoint coordinates, 𝒏f=(𝒙e−𝒙s)⊥\boldsymbol n_f=(\boldsymbol x_e-\boldsymbol x_s)^{\perp}; differentiate coordinate by coordinate:

∂𝒏f∂xs=(0,1),∂𝒏f∂ys=(−1,0),∂𝒏f∂xe=(0,−1),∂𝒏f∂ye=(1,0).\frac{\partial\boldsymbol n_f}{\partial x_s}=(0,1),\quad\frac{\partial\boldsymbol n_f}{\partial y_s}=(-1,0),\quad\frac{\partial\boldsymbol n_f}{\partial x_e}=(0,-1),\quad\frac{\partial\boldsymbol n_f}{\partial y_e}=(1,0).

第二步,面长 ℓ=(nx2+ny2)1/2\ell=(n_x^2+n_y^2)^{1/2},链式法则给出沿法向的投影;第三步,单位法向 𝒏^=𝒏/ℓ\hat{\boldsymbol n}=\boldsymbol n/\ell,商的求导法则给出垂直部分:

Step 2, the face length ℓ=(nx2+ny2)1/2\ell=(n_x^2+n_y^2)^{1/2}; the chain rule gives the projection on the normal. Step 3, the unit normal 𝒏^=𝒏/ℓ\hat{\boldsymbol n}=\boldsymbol n/\ell; the quotient rule gives the perpendicular part:

ℓ˙=nxn˙x+nyn˙yℓ=𝒏^⋅𝒏˙,𝒏^˙=𝒏˙ℓ−𝒏 ℓ˙ℓ2=𝒏˙−𝒏^ ℓ˙ℓ,𝒏^⋅𝒏^˙=0.\dot\ell=\frac{n_x\dot n_x+n_y\dot n_y}{\ell}=\hat{\boldsymbol n}\cdot\dot{\boldsymbol n},\qquad\dot{\hat{\boldsymbol n}}=\frac{\dot{\boldsymbol n}}{\ell}-\frac{\boldsymbol n\,\dot\ell}{\ell^2}=\frac{\dot{\boldsymbol n}-\hat{\boldsymbol n}\,\dot\ell}{\ell},\qquad\hat{\boldsymbol n}\cdot\dot{\hat{\boldsymbol n}}=0.

第四步,图 12.2 汇总这些依赖:坐标经面积向量进入每个面的通量与物面目标,经面长进入耗散,经单位法向进入远场的特征旋转;反向时 Xˉ+=(∂𝒏f/∂X)T𝒏ˉf\bar X\mathrel{\text{+=}}(\partial\boldsymbol n_f/\partial X)^T\bar{\boldsymbol n}_f,对各面收集到的 𝒏ˉf\bar{\boldsymbol n}_f 求和。

Step 4, Figure 12.2 collects these dependencies: the coordinates enter every face flux and the wall objective through the area vectors, the dissipation through the face lengths and the far-field characteristic rotation through the unit normals; in reverse, Xˉ+=(∂𝒏f/∂X)T𝒏ˉf\bar X\mathrel{\text{+=}}(\partial\boldsymbol n_f/\partial X)^T\bar{\boldsymbol n}_f, summed over the 𝒏ˉf\bar{\boldsymbol n}_f collected on every face.

X(节点坐标)X (node coordinates)48 个数48 numbers面积向量 nfarea vectors nf48 个面48 facesℓf = |nf|、nf/ℓfℓf = |nf|, nf/ℓf非线性nonlinear体积 Vivolumes Vi稳态残差不用not used by steady RUb(远场状态)Ub (far-field state)G(UL; U∞, nf/ℓf)Hf(全部面)Hf (every face)Fn 对 nf 线性Fn linear in nfR96 个方程96 equationsJ = ∑ p nf·ed只在物面wall only反向:n¯f 先收集 Hf、Ub 与 J 三处的贡献,再乘 (∂n/∂X)T 加到 X¯​reverse: n¯f first collects the contributions of Hf, Ub and J, then (∂n/∂X)T adds it to X¯​
图 12.2 · 设计变量中的节点坐标 XX 怎样进入残差与目标。格心页的面积向量直接由面的两个端点得到;面长 ℓf\ell_f 与单位法向 𝒏^f\hat{\boldsymbol n}_f 是非线性的一步。面积向量进入每个面的物理通量(线性)和物面上的目标,面长进入耗散,单位法向进入远场的特征旋转。体积 ViV_i 虽然由同样的坐标算出,却不进入积分形式的稳态残差(灰色虚线),所以第 12 节没有体积导数。反向时每个 𝒏ˉf\bar{\boldsymbol n}_f 要收集三处的贡献,再经 (∂𝒏/∂X)T(\partial\boldsymbol n/\partial X)^T 回到节点。Figure 12.2 · How the node coordinates XX among the design variables enter the residual and the objective. On the cell page the area vector follows directly from the two endpoints of the face; the face length ℓf\ell_f and unit normal 𝒏^f\hat{\boldsymbol n}_f are the nonlinear step. Area vectors enter the physical flux of every face (linearly) and the objective on the wall, face lengths enter the dissipation, and unit normals the far-field characteristic rotation. The volumes ViV_i, although computed from the same coordinates, do not enter the integrated steady residual (dashed grey), so Section 12 has no volume derivatives. In reverse, every 𝒏ˉf\bar{\boldsymbol n}_f collects three contributions before (∂𝒏/∂X)T(\partial\boldsymbol n/\partial X)^T returns them to the nodes.

核对:𝒏f\boldsymbol n_f 对坐标是线性的,所以单位步长的中心差分没有截断误差:对全部面、全部 48 个坐标,它与上面的公式最大相差 2×10⁻¹⁶;ℓ˙\dot\ell 与 𝒏^˙\dot{\hat{\boldsymbol n}} 的公式与步长 10−610^{-6} 的中心差分最大相差 4×10⁻¹¹ 与 1×10⁻¹⁰(差分本身的误差)(本页代码在构建时计算)。

Check: 𝒏f\boldsymbol n_f is linear in the coordinates, so a central difference with unit step has no truncation error: over all faces and all 48 coordinates it differs from the formulas above by at most 2×10⁻¹⁶; the formulas for ℓ˙\dot\ell and 𝒏^˙\dot{\hat{\boldsymbol n}} differ from central differences with step 10−610^{-6} by at most 4×10⁻¹¹ and 1×10⁻¹⁰ (the error of the differences themselves) (computed by this page’s code at build time).

物理通量对面积向量是线性的:Fn(U)=Fx(U) nx+Fy(U) nyF_n(U)=F_x(U)\,n_x+F_y(U)\,n_y。所以状态固定时,面积向量的切向量 𝒏˙\dot{\boldsymbol n} 给出通量的切向量 Fn˙(U):=Fx(U) n˙x+Fy(U) n˙yF_{\dot n}(U):=F_x(U)\,\dot n_x+F_y(U)\,\dot n_y。于是固定状态下面通量的切向量为:

The physical flux is linear in the area vector: Fn(U)=Fx(U) nx+Fy(U) nyF_n(U)=F_x(U)\,n_x+F_y(U)\,n_y. At fixed state a tangent 𝒏˙\dot{\boldsymbol n} of the area vector therefore gives the flux tangent Fn˙(U):=Fx(U) n˙x+Fy(U) n˙yF_{\dot n}(U):=F_x(U)\,\dot n_x+F_y(U)\,\dot n_y. At fixed state the face flux then has the tangent:

H˙∣U=12(Fn˙(UL)+Fn˙(UR))−α2 ℓ˙ (UR−UL)+HUb U˙b∣UL.\left.\dot H\right|_U=\tfrac12\bigl(F_{\dot n}(U_L)+F_{\dot n}(U_R)\bigr)-\tfrac\alpha2\,\dot\ell\,(U_R-U_L)+H_{U_b}\,\dot U_b\big|_{U_L}.

最后一项只在远场出现:Ub=G(UL;U∞,𝒏^)U_b=G(U_L;U_\infty,\hat{\boldsymbol n}) 依赖单位法向(旋转与 r±r_\pm),所以即使 ULU_L 不变,𝒏^˙\dot{\hat{\boldsymbol n}} 也会给出 U˙b\dot U_b。几何分支、连通性和边界分类在这些微扰中保持不变;不允许跨过翻转单元或切换入流分类后还声称使用同一个导数。积分稳态残差没有体积除法,因此体积导数不出现在当前 RDR_D 中;若加入时间项,它就必须出现。

The last term applies at the far field only: Ub=G(UL;U∞,𝒏^)U_b=G(U_L;U_\infty,\hat{\boldsymbol n}) depends on the unit normal (rotation and r±r_\pm), so 𝒏^˙\dot{\hat{\boldsymbol n}} gives a U˙b\dot U_b even at fixed ULU_L. Connectivity and boundary classification stay fixed under these perturbations. Crossing an inverted cell or changing an inflow branch is outside this linearisation. Integrated steady residuals have no volume division, so volume derivatives do not appear in RDR_D; they would be required for time terms.

逐步推导:几何的伴随语句与一个物面节点的形状导数

Step by step: the adjoint statements of the geometry, and the shape derivative of one wall node

第一步,第 9 节的逆序语句把每个面的几何伴随收集到 𝒏ˉf\bar{\boldsymbol n}_f:内部面与远场面的物理通量给 (FxTFˉ,FyTFˉ)(F_x^T\bar F,F_y^T\bar F),耗散经 ℓˉ\bar\ell 给 𝒏^ℓˉ\hat{\boldsymbol n}\bar\ell,远场闭合经单位法向给 (𝒏^ˉ−𝒏^(𝒏^⋅𝒏^ˉ))/ℓ(\bar{\hat{\boldsymbol n}}-\hat{\boldsymbol n}(\hat{\boldsymbol n}\cdot\bar{\hat{\boldsymbol n}}))/\ell,物面通量给 p (Hˉρu,Hˉρv)p\,(\bar H_{\rho u},\bar H_{\rho v}),目标给 p Jˉ 𝒆dp\,\bar J\,\boldsymbol e_d。第二步,面积向量对坐标是线性的,上面“逐步推导:面积向量、面长与单位法向对坐标的导数”的第一步给出四个导数,转置后就是节点坐标的伴随语句:

Step 1, the reverse statements of Section 9 collect every face’s geometry adjoint in 𝒏ˉf\bar{\boldsymbol n}_f: the physical fluxes of interior and far-field faces give (FxTFˉ,FyTFˉ)(F_x^T\bar F,F_y^T\bar F), the dissipation gives 𝒏^ℓˉ\hat{\boldsymbol n}\bar\ell through ℓˉ\bar\ell, the far-field closure gives (𝒏^ˉ−𝒏^(𝒏^⋅𝒏^ˉ))/ℓ(\bar{\hat{\boldsymbol n}}-\hat{\boldsymbol n}(\hat{\boldsymbol n}\cdot\bar{\hat{\boldsymbol n}}))/\ell through the unit normal, the wall flux gives p (Hˉρu,Hˉρv)p\,(\bar H_{\rho u},\bar H_{\rho v}) and the objective p Jˉ 𝒆dp\,\bar J\,\boldsymbol e_d. Step 2, the area vector is linear in the coordinates, and the four derivatives that Step 1 of “Step by step: derivatives of area vectors, face lengths and unit normals with respect to the coordinates” above gives, transposed, are the adjoint statements of the node coordinates:

xˉs+=nˉy,yˉs+=−nˉx,xˉe+=−nˉy,yˉe+=nˉx.\bar x_s\mathrel{\text{+=}}\bar n_y,\qquad\bar y_s\mathrel{\text{+=}}-\bar n_x,\qquad\bar x_e\mathrel{\text{+=}}-\bar n_y,\qquad\bar y_e\mathrel{\text{+=}}\bar n_x.

第三步,一个节点的形状导数就是它所在的每个面贡献之和。以第 8、9 节的物面节点 1 为例,下表列出面积向量 𝒏f\boldsymbol n_f 依赖这个节点的全部 3 个面:𝒏ˉf\bar{\boldsymbol n}_f 分成种子 Rˉ=ψ\bar R=\psi 的部分与目标种子 Jˉ=1\bar J=1 的部分,∂𝒏f/∂(x1,y1)\partial\boldsymbol n_f/\partial(x_{1},y_{1}) 写成 (∂nx/∂x1,∂ny/∂x1,∂nx/∂y1,∂ny/∂y1)(\partial n_x/\partial x_{1},\partial n_y/\partial x_{1},\partial n_x/\partial y_{1},\partial n_y/\partial y_{1}),最后一列是这个面加到 (xˉ,yˉ)(\bar x,\bar y) 上的量:

Step 3, a node’s shape derivative is the sum of the contributions of the faces it belongs to. For wall node 1 of Sections 8 and 9, the table lists all 3 faces whose area vector depends on this node: 𝒏ˉf\bar{\boldsymbol n}_f is split into the part of the seed Rˉ=ψ\bar R=\psi and the part of the objective seed Jˉ=1\bar J=1, ∂𝒏f/∂(x1,y1)\partial\boldsymbol n_f/\partial(x_{1},y_{1}) is written (∂nx/∂x1,∂ny/∂x1,∂nx/∂y1,∂ny/∂y1)(\partial n_x/\partial x_{1},\partial n_y/\partial x_{1},\partial n_x/\partial y_{1},\partial n_y/\partial y_{1}), and the last column is what the face adds to (xˉ,yˉ)(\bar x,\bar y):

面face类型kind𝒏ˉf\bar{\boldsymbol n}_f(Rˉ=ψ\bar R=\psi)𝒏ˉf\bar{\boldsymbol n}_f (Rˉ=ψ\bar R=\psi)𝒏ˉf\bar{\boldsymbol n}_f(Jˉ=1\bar J=1)𝒏ˉf\bar{\boldsymbol n}_f (Jˉ=1\bar J=1)∂𝒏f/∂(x1,y1)\partial\boldsymbol n_f/\partial(x_{1},y_{1})(xˉ,yˉ)+=(\bar x,\bar y)\mathrel{\text{+=}}
0内部面interior(−0.1004, 0.1673)(0, 0)(0, −1, 1, 0)(−0.1673, −0.1004)
32物面面wall face(−0.08959, 0.08861)(0, 0.8691)(0, 1, −1, 0)(0.9577, 0.08959)
33物面面wall face(−0.08695, −0.07254)(0, 0.9624)(0, −1, 1, 0)(−0.8898, −0.08695)
合计sum(−0.09948, −0.09776)

合计 (xˉ,yˉ)=(−0.099479,  −0.097758)(\bar x,\bar y)=(−0.099479,\;−0.097758),正是一次伴随给出的 (dJ/dx1, dJ/dy1)(dJ/dx_{1},\,dJ/dy_{1})。其中只来自 Jˉ=1\bar J=1 的部分 (−0.093326,  0.000000)(−0.093326,\;0.000000) 就是第 7 节的直接导数 JXJ_X:JyJ_y 的 𝒏⋅𝒆y\boldsymbol n\cdot\boldsymbol e_y 只含端点的 xx 坐标,所以它的 yy 分量为零;其余来自 ψ\psi,即 ψTRX\psi^TR_X。

The sum (xˉ,yˉ)=(−0.099479,  −0.097758)(\bar x,\bar y)=(−0.099479,\;−0.097758) is exactly the (dJ/dx1, dJ/dy1)(dJ/dx_{1},\,dJ/dy_{1}) of the single adjoint solve. The part from Jˉ=1\bar J=1 alone, (−0.093326,  0.000000)(−0.093326,\;0.000000), is the direct derivative JXJ_X of Section 7: the 𝒏⋅𝒆y\boldsymbol n\cdot\boldsymbol e_y of JyJ_y contains only the xx coordinates of the endpoints, so its yy component is zero; the rest comes from ψ\psi, i.e. ψTRX\psi^TR_X.

核对:各面贡献之和与伴随梯度相差 1×10⁻¹⁷,Jˉ\bar J 部分与计算图反向一次(种子 Rˉ=0\bar R=0、Jˉ=1\bar J=1)给出的 JXJ_X 相差 0(完全相同)(本页代码在构建时计算)。

Check: the sum of the face contributions differs from the adjoint gradient by at most 1×10⁻¹⁷, and the Jˉ\bar J part from the JXJ_X of one reverse sweep of the graph (seeds Rˉ=0\bar R=0, Jˉ=1\bar J=1) by 0 (identical) (computed by this page’s code at build time).

图 12.3 把最终的形状梯度拆成两部分:第 7 节的直接部分 JXJ_X 与经残差传来的 ψTRX\psi^TR_X。

Figure 12.3 splits the final shape gradient into its two parts: the direct part JXJ_X of Section 7 and the part ψTRX\psi^TR_X that comes through the residual.

01234567= 0.1直接部分 JX(固定状态)direct part JX (fixed state)经残差 ψT RXthrough the residual ψT RX全导数 dJ/dX(两者之和)total dJ/dX (their sum)两部分之和与伴随梯度之差:(direct + via R) − adjoint:1×10⁻¹⁶
图 12.3 · JyJ_y 对每个物面节点坐标的导数 (dJ/dxi, dJ/dyi)(dJ/dx_i,\,dJ/dy_i) 画成最粗的箭头,并拆成两部分:固定状态时目标对面积向量的直接依赖 JXJ_X(紫色,第 7 节的逐步推导给出它的闭式)与经残差传来的 ψTRX\psi^TR_X(红色)。三种箭头共用一个比例(右下角的参考箭头)。直接部分只有 xx 分量,因为 JyJ_y 只依赖面积向量的 yy 分量,而它只含端点的 xx 坐标;两部分相差很大,漏掉任何一部分,形状梯度都会错。Figure 12.3 · The derivative of JyJ_y with respect to the coordinates of every wall node, (dJ/dxi, dJ/dyi)(dJ/dx_i,\,dJ/dy_i), drawn as the thickest arrows and split into two parts: the direct dependence of the objective on the area vectors at fixed state, JXJ_X (violet; the step-by-step derivation of Section 7 gives it in closed form), and the part that comes through the residual, ψTRX\psi^TR_X (red). All arrows share one scale (reference arrow at the bottom right). The direct part has only an xx component, because JyJ_y depends only on the yy component of the area vectors, which contains only the xx coordinates of the endpoints; the two parts differ a lot, and dropping either one gives a wrong shape gradient.
图 12.4Figure 12.4
第 13 节Section 13

有限差分:变量、步长与完整原始重算

Finite differences: variables, steps and full primal reruns

每个差分点都移动同一个设计变量,重新计算几何与边界状态,并把整个 Euler 原始问题重新收敛。不能只在固定 U∗U^* 上差分 JJ,那样只得到直接导数 JDJ_D。表中同时保留正负扰动目标值、原始残差与完整梯度;选择“全部变量”可检查来流与全部物面坐标。

Each difference perturbs the selected parameter, recomputes geometry and boundary states, and reconverges the full Euler problem. Differencing JJ at fixed U∗U^* would recover only the direct derivative JDJ_D. Tables retain both perturbed objectives, the primal residuals and the total gradients. The all-variable option checks the free-stream inputs and every body coordinate.

FDε+=J(D+εek)−J(D)ε,FDε0=J(D+εek)−J(D−εek)2ε.\mathrm{FD}_\varepsilon^+=\frac{J(D+\varepsilon e_k)-J(D)}\varepsilon,\qquad \mathrm{FD}_\varepsilon^0=\frac{J(D+\varepsilon e_k)-J(D-\varepsilon e_k)}{2\varepsilon}.

图 13.1 说明为什么每个差分点都必须重新收敛原始问题:只在固定的 U∗U^* 上差分 JJ,得到的只是直接偏导数 JDJ_D。

Figure 13.1 shows why every difference point must re-converge the primal problem: differencing JJ at the fixed U∗U^* returns only the direct partial derivative JDJ_D.

−0.1960.1960M∞θx2y2x6y6伴随全导数 dJ/dDadjoint total derivative dJ/dD固定 U* 差分 J(只得到 JD)difference of J at fixed U* (only JD)完整重算的中心差分central difference with full rerunsJy 对六个设计变量的导数(ε = 1×10⁻⁵)derivatives of Jy with respect to six design variables (ε = 1×10⁻⁵)
图 13.1 · 为什么差分必须重新求解原始问题。对六个设计变量(来流 M∞M_\infty、θ\theta 与物面节点 2、6 的坐标),比较伴随给出的全导数(红)、在固定的收敛状态 U∗U^* 上差分 JJ(紫)以及每个差分点都重新收敛 Euler 方程的中心差分(蓝)。紫色柱只等于直接偏导数 JDJ_D:JJ 不显式依赖 M∞M_\infty 和 θ\theta,所以这两个柱为零,而真正的导数并不为零;对坐标,紫色柱也只是第 12 节图中的直接部分。完整重算与伴随相符(差别在截断与求解误差之内)。Figure 13.1 · Why a difference must re-solve the primal problem. For six design variables (the free-stream M∞M_\infty and θ\theta and the coordinates of wall nodes 2 and 6), compare the adjoint total derivative (red), the difference of JJ at the fixed converged state U∗U^* (violet), and the central difference that re-converges the Euler equations at every perturbed point (blue). The violet bars equal only the direct partial derivative JDJ_D: JJ has no explicit dependence on M∞M_\infty and θ\theta, so those two bars are zero while the true derivatives are not; for the coordinates the violet bars are only the direct part of the Section 12 figure. The full reruns agree with the adjoint (to within truncation and solver error).

eke_k 是第 kk 个设计变量的单位方向,ε\varepsilon 是差分步长(与第 15 节的网格尺度 hh 无关)。大步长有截断误差;很小的步长放大舍入和原始求解残差。步长扫描按整数量级 ε=10−2,10−3,…,10−12\varepsilon=10^{-2},10^{-3},\dots,10^{-12} 均匀取点,横轴就是 ε\varepsilon。正确证据是存在合适的步长区间,而不是只挑一个好看的数字。误差可切换绝对值与相对分量误差;当真梯度接近零时,优先查看绝对误差。

eke_k is the unit direction of design variable kk and ε\varepsilon the difference step (unrelated to the mesh size hh of Section 15). Large steps incur truncation error; tiny steps amplify round-off and primal residuals. The step sweep uses every decade ε=10−2,10−3,…,10−12\varepsilon=10^{-2},10^{-3},\dots,10^{-12}, with ε\varepsilon on the horizontal axis. Look for a useful step-size range, not one favourable number. Absolute and component-relative errors are selectable; near a zero gradient the absolute error is more informative.

图 13.2Figure 13.2
第 14 节Section 14

复数步长:独立复数残差核的检查

Complex step: checking an independent complex residual kernel

光滑无激波分支没有限幅器或最大波速运算,适合复数步长检查。复数算术与实数求导图分开实现,几何面长使用 nx2+ny2\sqrt{n_x^2+n_y^2} 的解析延拓,不能使用取共轭的复数范数。物理公式和固定边界分支共用。

This smooth branch has no limiter or maximum wave-speed operation, which makes complex-step verification useful. Complex arithmetic is implemented separately from the real derivative tape. Geometry uses the analytic continuation of nx2+ny2\sqrt{n_x^2+n_y^2}, not a conjugating complex norm. Physical formulas and fixed boundary branches are shared.

Dc=D+iεek,dJdDk≈Im⁡J(Uc,Dc)ε,ε=10−25.D_c=D+i\varepsilon e_k,\qquad \frac{dJ}{dD_k}\approx\frac{\operatorname{Im}J(U_c,D_c)}\varepsilon,\qquad \varepsilon=10^{-25}.

逐步推导:方程组的复数步长展开

Step by step: the complex-step expansion for a system

第一步,解析函数在复数步长上的 Taylor 展开,虚部除以 ε\varepsilon 就是导数,误差是 ε2\varepsilon^2 量级,并且没有两个相近数相减:

Step 1, the Taylor expansion of an analytic function at a complex step; the imaginary part divided by ε\varepsilon is the derivative, with an error of order ε2\varepsilon^2 and no subtraction of nearly equal numbers:

f(x+iε)=f(x)+iεf′(x)−ε22f′′(x)−iε36f′′′(x)+…,Im⁡f(x+iε)ε=f′(x)−ε26f′′′(x)+…f(x+i\varepsilon)=f(x)+i\varepsilon f'(x)-\frac{\varepsilon^2}2f''(x)-i\frac{\varepsilon^3}6f'''(x)+\dots,\qquad\frac{\operatorname{Im}f(x+i\varepsilon)}{\varepsilon}=f'(x)-\frac{\varepsilon^2}6f'''(x)+\dots

第二步,残差。代码保持状态的实部等于收敛解 U∗U^*,令 Uc=U∗+iεU~U_c=U^*+i\varepsilon\tilde U、Dc=D+iεekD_c=D+i\varepsilon e_k。展开时实部的零阶项是 R(U∗,D)=0R(U^*,D)=0:

Step 2, the residual. The code keeps the real part of the state at the converged U∗U^* and sets Uc=U∗+iεU~U_c=U^*+i\varepsilon\tilde U, Dc=D+iεekD_c=D+i\varepsilon e_k. In the expansion the zeroth-order real term is R(U∗,D)=0R(U^*,D)=0:

R(Uc,Dc)=R(U∗,D)⏟=0+iε(AU~+RDek)+O(ε2),Im⁡R(Uc,Dc)ε=AU~+RDek+O(ε2).R(U_c,D_c)=\underbrace{R(U^*,D)}_{=0}+i\varepsilon\bigl(A\tilde U+R_D e_k\bigr)+O(\varepsilon^2),\qquad\frac{\operatorname{Im}R(U_c,D_c)}{\varepsilon}=A\tilde U+R_D e_k+O(\varepsilon^2).

第三步,代码用实数的 AA 迭代 U~←U~−A−1Im⁡R/ε\tilde U\leftarrow\tilde U-A^{-1}\operatorname{Im}R/\varepsilon,直到虚部残差收敛;此时 AU~=−RDek+O(ε2)A\tilde U=-R_D e_k+O(\varepsilon^2),U~\tilde U 就是 D˙=ek\dot D=e_k 的前向解 U˙=UDek=dU/dDk\dot U=U_D e_k=dU/dD_k,误差 O(ε2)O(\varepsilon^2)。第四步,目标同样展开:

Step 3, the code iterates U~←U~−A−1Im⁡R/ε\tilde U\leftarrow\tilde U-A^{-1}\operatorname{Im}R/\varepsilon with the real AA until the imaginary residual has converged; then AU~=−RDek+O(ε2)A\tilde U=-R_D e_k+O(\varepsilon^2), so U~\tilde U is the tangent solution U˙=UDek=dU/dDk\dot U=U_D e_k=dU/dD_k for D˙=ek\dot D=e_k, up to O(ε2)O(\varepsilon^2). Step 4, expand the objective in the same way:

Im⁡J(Uc,Dc)ε=JUU~+JDek+O(ε2)=dJdDk+O(ε2).\frac{\operatorname{Im}J(U_c,D_c)}{\varepsilon}=J_U\tilde U+J_D e_k+O(\varepsilon^2)=\frac{dJ}{dD_k}+O(\varepsilon^2).

第五步,复数残差的实部等于 −ε22-\tfrac{\varepsilon^2}2 乘以二阶项,并不为零;代码不去修正它,因为它只影响 O(ε2)O(\varepsilon^2),在 ε=10−25\varepsilon=10^{-25} 时远小于舍入误差。复数运算由独立的复数算子集执行,不经过实数计算图,所以它能检出计算图中的错误。

Step 5, the real part of the complex residual equals −ε22-\tfrac{\varepsilon^2}2 times second-order terms and is not zero; the code does not correct it, because it affects only O(ε2)O(\varepsilon^2), far below round-off at ε=10−25\varepsilon=10^{-25}. The complex arithmetic runs on an independent complex operator set, not through the real computation graph, so it can expose errors in that graph.

核对:ε=10−25\varepsilon=10^{-25} 时复数步长给出的 dJy/dM∞dJ_y/dM_\infty 与前向解的相对差为 4×10⁻¹²,最终虚部残差 4×10⁻¹³;ε=10−2\varepsilon=10^{-2} 与 10−310^{-3} 两处的误差之比为 100.01,正是 ε2\varepsilon^2 项所预言的 100(本页代码在构建时计算)。

Check: at ε=10−25\varepsilon=10^{-25} the complex-step dJy/dM∞dJ_y/dM_\infty differs from the tangent solution by 4×10⁻¹² relatively, with a final imaginary residual of 4×10⁻¹³; the errors at ε=10−2\varepsilon=10^{-2} and 10−310^{-3} have the ratio 100.01, the 100 predicted by the ε2\varepsilon^2 term (computed by this page’s code at build time).

图 14.1 把复数步长与第 13 节的差分放在同一张误差–步长图上。

Figure 14.1 puts the complex step and the differences of Section 13 on one error-versus-step plot.

10⁻¹¹10⁻⁹10⁻⁷10⁻⁵10⁻³10⁻¹10¹10⁻³⁰10⁻²⁷10⁻²⁴10⁻²¹10⁻¹⁸10⁻¹⁵10⁻¹²10⁻⁹10⁻⁶10⁻³10⁰步长 ε(对数坐标,右边是大步长)step ε (log scale, large steps on the right)相对误差(相对伴随梯度)relative error (against the adjoint gradient)斜率 2slope 2斜率 1slope 1前向差分(完整重算)forward difference (full reruns)中心差分(完整重算)central difference (full reruns)复数步长complex step
图 14.1 · dJy/dM∞dJ_y/dM_\infty 的三种近似与伴随梯度的相对误差,横轴是步长 ε\varepsilon(本页代码在构建时计算:每个差分点都完整重算原始问题,每个复数步长都迭代到虚部残差收敛)。前向差分的截断误差按 ε\varepsilon 下降,中心差分按 ε2\varepsilon^2 下降,最好的结果都在 ε=1×10−5\varepsilon=1×10⁻⁵ 处(6×10⁻⁶ 与 4×10⁻¹⁰);步长再小,两次原始求解各自的收敛误差(残差阈值 3×10−123\times10^{-12})除以 ε\varepsilon 后迅速放大。复数步长没有相减,误差按 ε2\varepsilon^2 下降后一直停在 4×10⁻¹¹ 附近,直到 ε=10−30\varepsilon=10^{-30}:这个平台来自线性求解的停止容差,而不是舍入误差。Figure 14.1 · Relative error of three approximations of dJy/dM∞dJ_y/dM_\infty against the adjoint gradient, with the step ε\varepsilon on the horizontal axis (computed by this page's code at build time: every difference point re-solves the primal problem in full, every complex step iterates until its imaginary residual has converged). The truncation error of the forward difference falls like ε\varepsilon and that of the central difference like ε2\varepsilon^2, both with their best result at ε=1×10−5\varepsilon=1×10⁻⁵ (6×10⁻⁶ and 4×10⁻¹⁰); for smaller steps the convergence error of the two primal solves (residual threshold 3×10−123\times10^{-12}) divided by ε\varepsilon grows quickly. The complex step subtracts nothing: its error falls like ε2\varepsilon^2 and then stays near 4×10⁻¹¹ all the way to ε=10−30\varepsilon=10^{-30}; this plateau comes from the stopping tolerance of the linear solves, not from round-off.

从已收敛实数场出发,直接计算复数残差,用实数 AA 作为虚部修正的迭代算子,直到 Im⁡R/ε\operatorname{Im}R/\varepsilon 收敛。这样独立的复数残差仍能检出错误的 AA;必须检查最终虚部残差,不能只做一次修正就宣称成功。它不是对 Newton 的回退逻辑做复数运算。

Starting from the converged real state, evaluate the complex residual and use the real AA as an iterative correction operator for the imaginary part until Im⁡R/ε\operatorname{Im}R/\varepsilon converges. The independent complex residual can still expose an incorrect AA; its final imaginary residual must be checked. This does not apply complex arithmetic to Newton line-search decisions.

图 14.2Figure 14.2
第 15 节Section 15

网格加密:固定多边形上的精度阶与载荷

Mesh refinement: order of accuracy on a fixed polygon, and the loads

伴随给出的是离散格式的精确导数,无论格式的精度阶是多少;精度阶决定离散解、以及由它算出的 JJ 和 dJ/dDdJ/dD 离连续问题有多远。本节先用制造解测量格式的精度阶,再在同一网格族上看物理算例的载荷。

The adjoint gives the exact derivative of the discrete scheme, whatever its order of accuracy; the order decides how far the discrete solution, and the JJ and dJ/dDdJ/dD computed from it, are from the continuous problem. This section first measures the order with a manufactured solution, then looks at the loads of the physical problem on the same mesh family.

网格族:均匀细分,区域不变

The mesh family: uniform subdivision, fixed domain

从第 2 节的 24 单元基础网格出发,每次加密把每个三角形经三条边的中点分成 4 个三角形,把每个四边形经四条边的中点和顶点平均点分成 4 个四边形,每条边界边在中点一分为二。新的边界节点都落在原来的八边形边上,所以物面和远场在每一层都是同一对八边形,区域完全不变,网格尺度逐层减半:hj=h1/2j−1h_j=h_1/2^{j-1}。5 层网格分别有 24、96、384、1536、6144 个单元、24、80、288、1088、4224 个节点。只有这样均匀细分出来的网格族才能拟合精度阶。早先的版本在每层按 8j8j 个角度重新采样物面曲线 r(t)r(t),每一层的区域都不同,载荷序列把几何变化和离散误差混在一起;本节的全部数据都改在这个固定多边形网格族上重新计算。

Starting from the 24-cell base mesh of Section 2, each refinement splits every triangle into 4 triangles through its edge midpoints, every quadrilateral into 4 quadrilaterals through its edge midpoints and its vertex average, and every boundary edge in two at its midpoint. The new boundary nodes lie on the old octagon edges, so the body and the far field are the same pair of octagons on every level, the domain never changes, and the mesh size halves from level to level: hj=h1/2j−1h_j=h_1/2^{j-1}. The 5 meshes have 24, 96, 384, 1536, 6144 cells and 24, 80, 288, 1088, 4224 nodes. An order may only be fitted on a family refined uniformly like this. An earlier version re-sampled the body curve r(t)r(t) at 8j8j angles on every level, so each level had a different domain and its force sequence mixed geometry changes with discretisation error; every number in this section is recomputed on the fixed-polygon family.

图 15.1 画出这个网格族的前三层。

Figure 15.1 shows the first three levels of this family.

第 1 层:24 个单元、24 个节点level 1: 24 cells, 24 nodes第 2 层:96 个单元、80 个节点level 2: 96 cells, 80 nodes第 3 层:384 个单元、288 个节点level 3: 384 cells, 288 nodes
图 15.1 · 第 15 节的网格族的前三层。每次加密把每个三角形经三条边的中点分成 4 个,把每个四边形经四条边的中点与顶点平均点分成 4 个;新的边界节点都落在原来的八边形边上,所以物面(琥珀色)和远场(蓝色)在每一层都是同一对八边形,琥珀色圆点是第 1 层的 8 个物面节点。网格尺度逐层减半,区域完全不变。Figure 15.1 · The first three levels of the mesh family of Section 15. Each refinement splits every triangle into 4 through its edge midpoints and every quadrilateral into 4 through its edge midpoints and vertex average; the new boundary nodes lie on the old octagon edges, so the body (amber) and the far field (blue) are the same pair of octagons on every level, and the amber dots are the 8 body nodes of level 1. The mesh size halves from level to level while the domain stays exactly the same.

制造解

The manufactured solution

取一个光滑、亚声速、处处为正的场作为精确解(均值沿 +x+x,使远场八边形的每个面都明确是入流或出流):

Take a smooth, subsonic, everywhere-positive field as the exact solution (its mean flow is along +x+x, so every face of the far-field octagon is clearly inflow or outflow):

ρex=1+0.1 sin⁡(0.8x+0.3) cos⁡(0.6y−0.4),uex=0.3+0.05 cos⁡(0.7x−0.2) sin⁡(0.9y+0.3),\rho_{\mathrm{ex}}=1+0.1\,\sin(0.8x+0.3)\,\cos(0.6y-0.4),\qquad u_{\mathrm{ex}}=0.3+0.05\,\cos(0.7x-0.2)\,\sin(0.9y+0.3),
vex=0.04 sin⁡(0.6x+0.5y+0.2),pex=1+0.1 cos⁡(0.5x−0.7y+0.1).v_{\mathrm{ex}}=0.04\,\sin(0.6x+0.5y+0.2),\qquad p_{\mathrm{ex}}=1+0.1\,\cos(0.5x-0.7y+0.1).

把它代入 Euler 方程得到源项 s=∂xFx(Uex)+∂yFy(Uex)s=\partial_xF_x(U_{\mathrm{ex}})+\partial_yF_y(U_{\mathrm{ex}})。源项由 sympy 对上式做符号求导并直接生成代码,不用差分,也不从离散残差反推;另用前向模式对偶数独立核对(随机点上最大相对差 1×10⁻¹⁶)。每个控制体的源项 Qi=∫Vis dAQ_i=\int_{V_i}s\,dA 在本格式实际的控制体(格心:单元;格点:中位对偶体)上用 6×66\times6 点折叠 Gauss 公式积分,并用散度定理 ∮∂ViFn(Uex) ds\oint_{\partial V_i}F_n(U_{\mathrm{ex}})\,ds 独立核对(最大差 2×10⁻¹³)。离散问题是 Ri(U)−Qi=0R_i(U)-Q_i=0。

Substituting it into the Euler equations gives the source s=∂xFx(Uex)+∂yFy(Uex)s=\partial_xF_x(U_{\mathrm{ex}})+\partial_yF_y(U_{\mathrm{ex}}). The source is differentiated symbolically by sympy and printed directly as code: no differencing, and nothing is reverse-engineered from the discrete residual. Forward-mode dual numbers check it independently (largest relative difference 1×10⁻¹⁶ over random points). Each volume’s source Qi=∫Vis dAQ_i=\int_{V_i}s\,dA is integrated over the scheme’s actual control volumes (cells for the cell scheme, median-dual volumes for the node scheme) with a 6×66\times6-point collapsed Gauss rule, and checked independently against the divergence theorem ∮∂ViFn(Uex) ds\oint_{\partial V_i}F_n(U_{\mathrm{ex}})\,ds (largest difference 2×10⁻¹³). The discrete problem is Ri(U)−Qi=0R_i(U)-Q_i=0.

边界数据只在这个研究里改动,页面的正常边界不变。制造解会穿过物面,所以物面在这里不能是滑移壁面;八边形物面与远场八边形的每个边界面都用第 4 节的特征闭合,只是把 U∞U_\infty 换成面中点的 UexU_{\mathrm{ex}},入/出流分支由它的法向速度决定。这样每个面从外部得到的条件个数正好等于入域特征的个数:入流面三个,出流面一个。反过来,如果在物面上照搬壁面通量 pL𝒏p_L\boldsymbol n 并把精确解的对流通量整体作为数据,那么在制造解从物面流出的面上,外部条件比入域特征多两个,这个边界就被过定了,所以本研究不这样做。离散问题 Ri(U)−Qi=0R_i(U)-Q_i=0 与连续问题因此只差离散化本身。

Only this study changes the boundary data; the page’s normal boundaries stay as they are. The manufactured field crosses the body, so the body cannot be a slip wall here. Every boundary face, on the body octagon and on the far-field octagon, uses the characteristic closure of Section 4 with UexU_{\mathrm{ex}} at the face midpoint in place of U∞U_\infty, and the branch set by its normal velocity. Each face then receives exactly as many outside conditions as it has incoming characteristics: three at inflow, one at outflow. If instead the wall flux pL𝒏p_L\boldsymbol n were kept and the whole convective flux of the exact solution supplied as data, a face where the manufactured field leaves through the body would receive two conditions more than it has incoming characteristics; that boundary would be over-determined, so this study does not do it. The discrete problem Ri(U)−Qi=0R_i(U)-Q_i=0 therefore differs from the continuous one only by the discretisation.

误差与观测阶

Errors and observed orders

误差在控制体上测量:格心与精确解的单元平均比较,格点与节点处的精确值比较(记为 ΠhUex\Pi_hU_{\mathrm{ex}})。四个守恒分量一起取按体积加权的 L2L_2 范数和最大范数。相邻两层的网格尺度正好差 2 倍,所以观测阶就是误差比的以 2 为底的对数:

Errors are measured per control volume: cell values against the exact cell averages, node values against the exact nodal values (written ΠhUex\Pi_hU_{\mathrm{ex}}). The four conserved components enter together, in the volume-weighted L2L_2 norm and in the max norm. Neighbouring levels differ in mesh size by exactly a factor of 2, so the observed order is the base-2 logarithm of the error ratio:

err2(h)=(∑i∣Vi∣ ∥Ui−(ΠhUex)i∥2∑i∣Vi∣)1/2,err∞(h)=max⁡i,k∣Ui,k−(ΠhUex)i,k∣,order=log⁡2err(h)err(h/2).\mathrm{err}_2(h)=\Bigl(\frac{\sum_i|V_i|\,\|U_i-(\Pi_hU_{\mathrm{ex}})_i\|^2}{\sum_i|V_i|}\Bigr)^{1/2},\quad \mathrm{err}_\infty(h)=\max_{i,k}\bigl|U_{i,k}-(\Pi_hU_{\mathrm{ex}})_{i,k}\bigr|,\quad \mathrm{order}=\log_2\frac{\mathrm{err}(h)}{\mathrm{err}(h/2)}.

每一层都用本页的 Newton 求解到残差低于 3×10−123\times10^{-12}(离线研究为了速度改用精确 Jacobian 的块 ILU 预处理,它只影响 GMRES 的迭代次数,不改变离散解)。本页格式的 L2L_2 观测阶依次为 0.48、0.61、0.77、0.80,最大范数为 0.34、0.51、0.70、0.78;格点格式的 L2L_2 阶为 0.35、0.52、0.71、0.85,最大范数为 0.33、0.51、0.64、0.69。最细一层的 L2L_2 误差是最粗一层的 0.158 倍。

Every level is solved by this page’s Newton method until the residual is below 3×10−123\times10^{-12} (for speed the offline study preconditions GMRES with a block ILU of the exact Jacobian; it changes only the number of GMRES iterations, not the discrete solution). The L2L_2 orders of this page’s scheme are 0.48, 0.61, 0.77, 0.80, the max-norm orders 0.34, 0.51, 0.70, 0.78; the node-centred scheme has L2L_2 orders 0.35, 0.52, 0.71, 0.85 and max-norm orders 0.33, 0.51, 0.64, 0.69. The finest-level L2L_2 error is 0.158 times the coarsest.

格式是一阶的:耗散项 αℓ2(UR−UL)\tfrac{\alpha\ell}2(U_R-U_L) 相当于数值粘性 νnum≈α∣𝒅∣/2\nu_{\mathrm{num}}\approx\alpha|\boldsymbol d|/2(∣𝒅∣|\boldsymbol d| 为面两侧存储点的距离),随 hh 线性减小,误差也就是 O(h)O(h)。两种格式、两种范数的观测阶都小于 1;L2L_2 阶逐层增大(最细一对网格上本页格式为 0.80)。粗网格的单元尺寸与制造解的变化尺度相比还不小,数值粘性仍然很大,误差还没有进入渐近区,所以观测阶从下方逐步接近 1。

The scheme is first order: the dissipation αℓ2(UR−UL)\tfrac{\alpha\ell}2(U_R-U_L) acts like a numerical viscosity νnum≈α∣𝒅∣/2\nu_{\mathrm{num}}\approx\alpha|\boldsymbol d|/2 (∣𝒅∣|\boldsymbol d| the distance between the two stored points of a face), which falls linearly with hh, so the error is O(h)O(h). Both schemes have observed orders below 1 in both norms; the L2L_2 order grows level by level (0.80 for this page’s scheme on the finest pair). On coarse meshes the cells are not yet small compared with the length over which the manufactured field varies, the numerical viscosity is still large, the error has not reached the asymptotic range, and the observed order approaches 1 from below.

图 15.2Figure 15.2

同一网格族上的物理载荷

The physical loads on the same family

同一网格族、页面的正常边界、θ=10∘\theta=10^\circ,三个来流马赫数。内部面上 νnum\nu_{\mathrm{num}} 的中位数从第 1 层的 0.604 降到第 5 层的 0.0393;以物面尺寸 ℓbody=1.232\ell_{\mathrm{body}}=1.232 和来流速度估计的“数值雷诺数” ∣𝒖∞∣ℓbody/νnum|\boldsymbol u_\infty|\ell_{\mathrm{body}}/\nu_{\mathrm{num}}(M∞=0.2M_\infty=0.2)只有 0.5、0.9、1.9、3.7、7.4。这只是量级估计,但足以说明:即使在最细一层,离散流动也更像一个强粘性流动,而不是无粘 Euler 流动。

Same family, the page’s normal boundaries, θ=10∘\theta=10^\circ, three free-stream Mach numbers. The median of νnum\nu_{\mathrm{num}} over interior faces falls from 0.604 on level 1 to 0.0393 on level 5; the “numerical Reynolds number” ∣𝒖∞∣ℓbody/νnum|\boldsymbol u_\infty|\ell_{\mathrm{body}}/\nu_{\mathrm{num}} built with the body size ℓbody=1.232\ell_{\mathrm{body}}=1.232 (M∞=0.2M_\infty=0.2) is only 0.5, 0.9, 1.9, 3.7, 7.4. It is only an order-of-magnitude estimate, but it makes the point: even on the finest level the discrete flow behaves more like a strongly viscous flow than an inviscid Euler flow.

载荷与此一致(M∞=0.2M_\infty=0.2)。沿来流的阻力 J∥J_\parallel 逐层为 0.193、0.254、0.236、0.176、0.124,第 2 层之后逐层下降;最细一层的 J∥/q∞=4.42J_\parallel/q_\infty=4.42,远不是 d'Alembert 的零。x 方向合力 JxJ_x 为 0.195、0.257、0.240、0.180、0.128;y 方向合力 JyJ_y 为 0.00701、0.00588、0.000866、−0.00495、−0.0118,数值很小,并且在加密中变号,只有用单独的纵轴才看得清。物面最大 CpC_p 为 3.36、4.36、4.28、3.60、2.88,始终远高于等熵上界 Cp,0=1.010C_{p,0}=1.010;最低的总压比 p0/p0∞p_0/p_{0\infty} 为 0.870、0.809、0.812、0.842、0.863。格心与格点两个离散目标逐渐接近:JxJ_x 的相对差从第 1 层的 23% 降到第 5 层的 9%,JyJ_y 从 39% 降到 6%。

The loads agree (M∞=0.2M_\infty=0.2). The drag along the free stream J∥J_\parallel is 0.193, 0.254, 0.236, 0.176, 0.124 level by level, falling after level 2; on the finest level J∥/q∞=4.42J_\parallel/q_\infty=4.42, nowhere near d’Alembert’s zero. The x force JxJ_x is 0.195, 0.257, 0.240, 0.180, 0.128; the y force JyJ_y is 0.00701, 0.00588, 0.000866, −0.00495, −0.0118, small and changing sign under refinement, visible only on its own vertical axis. The largest wall CpC_p is 3.36, 4.36, 4.28, 3.60, 2.88, always far above the isentropic bound Cp,0=1.010C_{p,0}=1.010; the lowest total-pressure ratio p0/p0∞p_0/p_{0\infty} is 0.870, 0.809, 0.812, 0.842, 0.863. The cell-centred and node-centred objectives approach each other: their relative difference in JxJ_x falls from 23% on level 1 to 9% on level 5, in JyJ_y from 39% to 6%.

压力偏差怎样随马赫数变化?在第 1 层,三个马赫数下的 M∞⋅max⁡CpM_\infty\cdot\max C_p 只相差 0.8%:物面压力偏差 max⁡(p−p∞)\max(p-p_\infty) 与 M∞M_\infty 成正比,也就是按 ρc∣𝒖∣\rho c|\boldsymbol u| 而不是按动压 12ρ∣𝒖∣2\tfrac12\rho|\boldsymbol u|^2 标度,所以 CpC_p 的偏差约与 1/M∞1/M_\infty 成正比,马赫数越低越大。这是迎风型耗散在低马赫数下的已知行为。加密后这一部分减小,最细一层三者相差 23.9%。

How does the pressure error depend on the Mach number? On level 1 the three values of M∞⋅max⁡CpM_\infty\cdot\max C_p differ by only 0.8%: the wall pressure excess max⁡(p−p∞)\max(p-p_\infty) is proportional to M∞M_\infty, i.e. it scales like ρc∣𝒖∣\rho c|\boldsymbol u| rather than the dynamic pressure 12ρ∣𝒖∣2\tfrac12\rho|\boldsymbol u|^2, so the CpC_p error is roughly proportional to 1/M∞1/M_\infty and grows as the Mach number falls. This is the known low-Mach behaviour of upwind-type dissipation. Refinement shrinks this part; on the finest level the three differ by 23.9%.

所以,在这些网格上载荷主要是离散误差:J∥J_\parallel 既不是物理阻力,也不是激波或粘性阻力。八边形的尖角使连续无粘解在角点附近奇异,这也减慢载荷的收敛;制造解是光滑的,所以它测得的观测阶(从下方接近 1)反映的才是格式本身的精度。所有这些都不影响第 10–14 节的结论:tangent、adjoint、差分与复数步长求的,都是这个离散 JJ 的精确导数。

On these meshes the loads are therefore mostly discretisation error: J∥J_\parallel is neither physical drag nor shock or viscous drag. The corners of the octagon make the continuous inviscid solution singular near them, which further slows the convergence of the loads; the manufactured solution is smooth, so the orders it measures (approaching 1 from below) reflect the scheme’s own accuracy. None of this affects Sections 10–14: the tangent, adjoint, finite differences and complex step all compute exact derivatives of this discrete JJ.

图 15.3Figure 15.3
第 16 节Section 16

连续伴随:边界条件与验证范围

Continuous adjoint: boundary conditions and verification scope

本节与附录 A 回到连续问题,δ\delta 表示连续解的变分(第 8 节的记号约定),而不是离散代码的前向导数。对光滑稳态 Euler,令 Fx,U=∂Fx/∂UF_{x,U}=\partial F_x/\partial U、Fy,U=∂Fy/∂UF_{y,U}=\partial F_y/\partial U 为 4×44\times4 通量 Jacobian(与全局残差 Jacobian AA 是不同的对象),线性化为 ∂x(Fx,UδU)+∂y(Fy,UδU)=0\partial_x(F_{x,U}\delta U)+\partial_y(F_{y,U}\delta U)=0。乘以连续乘子 λ\lambda 并分部积分,域内伴随方程是 Fx,UT∂xλ+Fy,UT∂yλ=0F_{x,U}^T\partial_x\lambda+F_{y,U}^T\partial_y\lambda=0;边界上留下 λTFn^,UδU\lambda^TF_{\hat n,U}\delta U,它必须与目标的变分一起处理,不能把原始的四个边界状态直接复制给 λ\lambda。

This section and Appendix A return to the continuous problem: δ\delta denotes a variation of the continuous solution (the notation of Section 8), not a forward derivative of the discrete code. For smooth steady Euler flow let Fx,U=∂Fx/∂UF_{x,U}=\partial F_x/\partial U and Fy,U=∂Fy/∂UF_{y,U}=\partial F_y/\partial U be the 4×44\times4 flux Jacobians (different objects from the global residual Jacobian AA). Linearisation gives ∂x(Fx,UδU)+∂y(Fy,UδU)=0\partial_x(F_{x,U}\delta U)+\partial_y(F_{y,U}\delta U)=0. Multiplying by a continuous multiplier λ\lambda and integrating by parts gives the interior adjoint equation Fx,UT∂xλ+Fy,UT∂yλ=0F_{x,U}^T\partial_x\lambda+F_{y,U}^T\partial_y\lambda=0, and leaves λTFn^,UδU\lambda^TF_{\hat n,U}\delta U on the boundary. That term must be combined with the variation of the objective; copying the four primal boundary values to λ\lambda is invalid.

∫ΩλT∇⋅(FUδU) dΩ=∮∂ΩλTFn^,U δU ds−∫ΩδUT(Fx,UT∂xλ+Fy,UT∂yλ) dΩ.\int_\Omega\lambda^T\nabla\cdot(F_U\delta U)\,d\Omega=\oint_{\partial\Omega}\lambda^TF_{\hat n,U}\,\delta U\,ds-\int_\Omega\delta U^T\bigl(F_{x,U}^T\partial_x\lambda+F_{y,U}^T\partial_y\lambda\bigr)\,d\Omega.

采用本页的加号拉格朗日约定。物面上可容许的变分保持 un=0u_n=0,物理壁面通量的变分是 (0,n^xδp,n^yδp,0)(0,\hat n_x\delta p,\hat n_y\delta p,0),而 δJ=∫(𝒏^⋅𝒆d) δp ds\delta J=\int(\hat{\boldsymbol n}\cdot\boldsymbol e_d)\,\delta p\,ds。消去 δp\delta p 的系数,在物面 Γw\Gamma_w 上得到一个条件:

Use this page’s plus-sign Lagrangian. Admissible wall variations keep un=0u_n=0, the physical wall flux varies by (0,n^xδp,n^yδp,0)(0,\hat n_x\delta p,\hat n_y\delta p,0), and δJ=∫(𝒏^⋅𝒆d) δp ds\delta J=\int(\hat{\boldsymbol n}\cdot\boldsymbol e_d)\,\delta p\,ds. Cancelling the coefficient of δp\delta p gives one condition on the wall Γw\Gamma_w:

λρun^x+λρvn^y=−𝒏^⋅𝒆d(𝒙∈Γw).\lambda_{\rho u}\hat n_x+\lambda_{\rho v}\hat n_y=-\hat{\boldsymbol n}\cdot\boldsymbol e_d\qquad(\boldsymbol x\in\Gamma_w).

远场对应第 4 节的特征闭合。可容许的变分只能改变出域的量,也就是 GUG_U 的值域;要求所有这样的 δU\delta U 都使边界项为零,得到(un<0u_n<0 为入流面,un>0u_n>0 为出流面):

The far field corresponds to the characteristic closure of Section 4. Admissible variations may change only the outgoing quantities, i.e. they lie in the range of GUG_U; requiring the boundary term to vanish for every such δU\delta U gives (un<0u_n<0 at inflow faces, un>0u_n>0 at outflow faces):

GUT Fn^,UT λ=0⟺{λTFn^,U ∂U/∂r+=0,un<0λTFn^,U ∂U/∂r+=λTFn^,U ∂U/∂S=λTFn^,U ∂U/∂ut=0,un>0G_U^T\,F_{\hat n,U}^T\,\lambda=0\quad\Longleftrightarrow\quad \begin{cases}\lambda^TF_{\hat n,U}\,\partial U/\partial r_+=0,&u_n<0\\\lambda^TF_{\hat n,U}\,\partial U/\partial r_+=\lambda^TF_{\hat n,U}\,\partial U/\partial S=\lambda^TF_{\hat n,U}\,\partial U/\partial u_t=0,&u_n>0\end{cases}

入流面一个条件、出流面三个条件:正好等于原始问题出域特征的个数,因为伴随信息沿相反方向传播。代码实际做的是第 4 节的面转置 HULTHˉ+GUTHUbTHˉH_{U_L}^T\bar H+G_U^TH_{U_b}^T\bar H;在边界状态满足闭合的极限下,它的 GUTG_U^T 投影正好是上面的连续条件,其余部分是格式对伴随出域分量的数值闭合(附录 A 推导并实测)。

One condition at an inflow face and three at an outflow face: exactly the number of outgoing primal characteristics, because adjoint information travels the opposite way. What the code actually does is the face transpose HULTHˉ+GUTHUbTHˉH_{U_L}^T\bar H+G_U^TH_{U_b}^T\bar H of Section 4; in the limit where the boundary state satisfies the closure, its GUTG_U^T projection is exactly the continuous condition above, and the remainder is the scheme’s numerical closure for the adjoint’s outgoing components (derived and measured in Appendix A).

一致性检查(附录 A):在第 15 节的细分网格族上,把离散伴随 ψ\psi 代入物面条件,逐面计算缺陷 ζf=ψρun^x+ψρvn^y+𝒏^f⋅𝒆d\zeta_f=\psi_{\rho u}\hat n_x+\psi_{\rho v}\hat n_y+\hat{\boldsymbol n}_f\cdot\boldsymbol e_d。积分残差不除体积、目标按面积向量求和,所以 ψ\psi 与 λ\lambda 同量纲,不需要额外按面或体积缩放。结果分两部分:在每条八边形边中间一半的面上(远离角点的固定区域),对 JyJ_y 本页格式的缺陷 L2L_2 范数逐层下降为 0.641、0.499、0.346、0.214、0.105,对 JxJ_x 为 0.686、0.487、0.347、0.183、0.080,离散伴随在这里逐步满足连续物面条件;但整个物面上的最大缺陷为 0.92、0.82、0.91、1.28、1.96,没有下降,它出现在角点旁,那里法向跳变,连续条件本身就不连续。远场条件的检查结果不一致(见附录 A)。因此本页只宣称:远离角点时物面条件的一致性在所测网格上得到数值支持;不宣称离散伴随整体收敛到连续伴随。

Consistency check (Appendix A): on the subdivided family of Section 15, insert the discrete adjoint ψ\psi into the wall condition and compute the defect ζf=ψρun^x+ψρvn^y+𝒏^f⋅𝒆d\zeta_f=\psi_{\rho u}\hat n_x+\psi_{\rho v}\hat n_y+\hat{\boldsymbol n}_f\cdot\boldsymbol e_d face by face. Residuals are integrated, not divided by volume, and the objective sums area vectors, so ψ\psi has the units of λ\lambda and needs no extra scaling per face or volume. The result has two parts. On the middle half of every octagon edge (a fixed region away from the corners), the L2L_2 norm of the defect for this page’s scheme falls level by level: 0.641, 0.499, 0.346, 0.214, 0.105 for JyJ_y and 0.686, 0.487, 0.347, 0.183, 0.080 for JxJ_x; there the discrete adjoint approaches the continuous wall condition. Over the whole wall, however, the largest defect is 0.92, 0.82, 0.91, 1.28, 1.96 and does not fall; it sits next to the corners, where the normal jumps and the continuous condition itself is discontinuous. The far-field check is mixed (Appendix A). This page therefore claims only that the wall condition is numerically supported away from the corners on the tested meshes; it does not claim that the discrete adjoint converges to the continuous adjoint as a whole.

本页没有把同一个离散矩阵的转置再包装成“独立连续伴随”。当前验证对象是完整离散 Euler 的状态、几何和来流导数,它们对离散 JJ 是精确的;连续伴随只给出上面的方程和边界条件,本页没有独立求解它,也没有证明离散伴随在加密下收敛到它。

This page does not relabel a transpose of the same discrete matrix as an independent continuous adjoint. It verifies the discrete state, geometry and free-stream derivatives, which are exact for the discrete JJ. The continuous adjoint appears only through the equations and boundary conditions above; this page neither solves it independently nor proves that the discrete adjoint converges to it under refinement.

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

Why a discrete multiplier is not automatically a continuous field

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

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

⟨A 𝐔˙,Rˉ⟩MR=𝐔˙TATMRRˉ=⟨𝐔˙,A†Rˉ⟩MU,\langle A\,\dot{\mathbf U},\bar R\rangle_{M_R}=\dot{\mathbf U}^TA^TM_R\bar R=\langle \dot{\mathbf U},A^\dagger \bar R\rangle_{M_U},
A†=MU−1ATMR.A^\dagger=M_U^{-1}A^TM_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

总结:从标量面循环到 Euler 方程组

Summary: from scalar face loops to Euler systems

几何、面方向、守恒分发和反向累加沿用二维标量;热力学和多特征边界承接拟一维 Euler。新增的难点是四分量耦合、压力壁面通量、特征状态对法向的依赖,以及多条反传路径。核心仍是同一条链:节点 → 面几何 → 边界状态与通量 → 残差 → 收敛解 → 压力目标。

Geometry, face orientation, conservative scatter and reverse accumulation follow the scalar pages. Thermodynamics and characteristic boundaries extend quasi-one-dimensional Euler. The new issues are four-component coupling, pressure wall fluxes, normal-dependent characteristic states and merged reverse paths. The chain remains nodes → face geometry → boundary states and fluxes → residual → converged state → pressure objective.

首版提供一阶、亚声速分支的原始求解、matrix-free tangent/adjoint、来流与几何梯度、全链路差分和复数残差验证;制造解在固定多边形的均匀细分网格族上给出从下方逐步接近一阶的观测阶。固定耗散 α=2\alpha=2 使这些网格上的压力与载荷主要是离散误差;离散伴随在远离角点处逐步满足连续物面条件,但没有整体验证与连续伴随一致。这些限制都不影响导数对离散目标的精确性。没有加入激波、限制器、二阶重构或湍流闭合。下一步扩展应逐项增加离散算子,并让验证始终针对实际执行的程序。

This first version provides first-order subsonic primal solves, matrix-free tangent/adjoint operators, free-stream and geometry gradients, full-rerun finite differences and complex-residual checks; a manufactured solution on a uniformly subdivided fixed-polygon family gives observed orders approaching first order from below. The fixed dissipation α=2\alpha=2 makes pressures and loads on these meshes mostly discretisation error; the discrete adjoint approaches the continuous wall condition away from the corners, but its consistency with the continuous adjoint as a whole is not verified. Neither limitation affects the exactness of the derivatives of the discrete objective. Shocks, limiters, second-order reconstruction and turbulence closure are not implemented. Each future operator should be added together with verification of the program actually executed.

下面是本页完整的数值核心。它的计算图 tape() 就是前向与反向两种模式:forward() 的数组 d 存放每个运算结果的切向量 x˙\dot x,reverse() 的数组 w 存放伴随 xˉ\bar x(按与记录相反的次序用 += 累加);jvp、vjp 与 faceVjp 是对应的乘积 R˙=AU˙+RDD˙\dot R=A\dot U+R_D\dot D、(Uˉ,Dˉ)=(ATRˉ,RDTRˉ)(\bar U,\bar D)=(A^T\bar R,R_D^T\bar R) 与单个面的反向扫描。

Below is this page’s complete numerical core. Its computation graph tape() provides both modes: the array d of forward() holds the tangent x˙\dot x of every operation, and the array w of reverse() holds the adjoint xˉ\bar x (accumulated with += in the reverse order of recording); jvp, vjp and faceVjp are the corresponding products R˙=AU˙+RDD˙\dot R=A\dot U+R_D\dot D, (Uˉ,Dˉ)=(ATRˉ,RDTRˉ)(\bar U,\bar D)=(A^T\bar R,R_D^T\bar R) and the reverse sweep of one face.

展开本页完整数值核心Complete numerical core
/* Smooth 2-D Euler: a scalar-operation tape gives exact matrix-free JVP/VJP.
   Scalar face algebra also runs with independent complex arithmetic. */
(function(root){'use strict';
const gamma=1.4,alpha=2,zeros=n=>Array(n).fill(0),dot=(a,b)=>a.reduce((s,x,i)=>s+x*b[i],0),norm=a=>Math.sqrt(dot(a,a));
const real={c:x=>x,add:(a,b)=>a+b,sub:(a,b)=>a-b,mul:(a,b)=>a*b,div:(a,b)=>a/b,pow:(a,p)=>a**p,sin:Math.sin,cos:Math.cos};
function tape(){const val=[],pa=[],pb=[],da=[],db=[],inputs=[];function node(v,a=-1,b=-1,x=0,y=0){const i=val.length;val.push(v);pa.push(a);pb.push(b);da.push(x);db.push(y);return i;}const op={c:x=>node(x),input:x=>{const i=node(x);inputs.push(i);return i;},add:(a,b)=>node(val[a]+val[b],a,b,1,1),sub:(a,b)=>node(val[a]-val[b],a,b,1,-1),mul:(a,b)=>node(val[a]*val[b],a,b,val[b],val[a]),div:(a,b)=>node(val[a]/val[b],a,b,1/val[b],-val[a]/val[b]**2),pow:(a,p)=>node(val[a]**p,a,-1,p*val[a]**(p-1)),sin:a=>node(Math.sin(val[a]),a,-1,Math.cos(val[a])),cos:a=>node(Math.cos(val[a]),a,-1,-Math.sin(val[a]))};
function forward(seed){const d=new Float64Array(val.length);inputs.forEach((i,k)=>d[i]=seed[k]||0);for(let i=0;i<val.length;i++){if(pa[i]>=0)d[i]+=da[i]*d[pa[i]];if(pb[i]>=0)d[i]+=db[i]*d[pb[i]];}return d;}
function reverse(outputs,seeds){const w=new Float64Array(val.length);outputs.forEach((i,k)=>w[i]+=seeds[k]);for(let i=val.length-1;i>=0;i--){if(pa[i]>=0)w[pa[i]]+=da[i]*w[i];if(pb[i]>=0)w[pb[i]]+=db[i]*w[i];}return inputs.map(i=>w[i]);}
return {op,val,forward,reverse};}
const complex={c:x=>[x,0],add:(a,b)=>[a[0]+b[0],a[1]+b[1]],sub:(a,b)=>[a[0]-b[0],a[1]-b[1]],mul:(a,b)=>[a[0]*b[0]-a[1]*b[1],a[0]*b[1]+a[1]*b[0]],div:(a,b)=>{const d=b[0]**2+b[1]**2;return [(a[0]*b[0]+a[1]*b[1])/d,(a[1]*b[0]-a[0]*b[1])/d];},pow:(a,p)=>{const r=Math.hypot(...a)**p,t=p*Math.atan2(a[1],a[0]);return [r*Math.cos(t),r*Math.sin(t)];},sin:a=>[Math.sin(a[0])*Math.cosh(a[1]),Math.cos(a[0])*Math.sinh(a[1])],cos:a=>[Math.cos(a[0])*Math.cosh(a[1]),-Math.sin(a[0])*Math.sinh(a[1])]};
function mesh(level=1){ // level 1 is indexed-identical to the scalar pages
 // Level 1: angles t_j=2*pi*j/8. Ring 0 samples the body curve, ring 2 the circle of radius 1.9, ring 1 blends them.
 // Level l>1 subdivides level l-1 uniformly: every edge is halved at its midpoint, every quadrilateral gains its
 // vertex average and every triangle is split through its edge midpoints. The body and the far boundary therefore
 // stay the two level-1 octagons, the domain never changes, and the mesh size halves from level to level.
 if(!Number.isInteger(level)||level<1||level>6)throw Error('Unsupported mesh level');
 let X=[],L=1;const mid=(a,b)=>[.5*(a[0]+b[0]),.5*(a[1]+b[1])];
 for(let r=0;r<=2;r++){const k=r,z=k<=1?.45*k:.45+.55*(k-1);for(let j=0;j<8;j++){const t=2*Math.PI*j/8,rad=.55*(1+.28*Math.cos(t)+.12*Math.sin(2*t)),b=[.1+rad*Math.cos(t),-.04+rad*Math.sin(t)],o=[1.9*Math.cos(t),1.9*Math.sin(t)];X.push(b.map((x,d)=>(1-z)*x+z*o[d]));}}
 for(let l=1;l<level;l++){const nt=8*L,old=(r,j)=>X[r*nt+(j%nt)],Y=[];
  for(let R=0;R<=4*L;R++)for(let J=0;J<2*nt;J++){const r=R>>1,j=J>>1;
   Y.push(R%2===0&&J%2===0?old(r,j):R%2===0?mid(old(r,j),old(r,j+1)):J%2===0?mid(old(r,j),old(r+1,j))
    :r<L?[0,1].map(d=>.25*(old(r,j)[d]+old(r+1,j)[d]+old(r+1,j+1)[d]+old(r,j+1)[d])):mid(old(r,j),old(r+1,j+1)));}
  X=Y;L*=2;}
 const nt=8*L,C=[],id=(r,j)=>r*nt+(j%nt);for(let r=0;r<2*L;r++)for(let j=0;j<nt;j++){const a=id(r,j),b=id(r,j+1),c=id(r+1,j+1),d=id(r+1,j);if(r<L)C.push([a,d,c,b]);else C.push([a,d,c],[a,c,b]);}return {X,C,nt,level};}
function topology(mesh,scheme){const edges=[],map=new Map();mesh.C.forEach((cell,c)=>cell.forEach((a,k)=>{const b=cell[(k+1)%cell.length],key=[Math.min(a,b),Math.max(a,b)].join('-');if(map.has(key))edges[map.get(key)].R=c;else{map.set(key,edges.length);edges.push({a,b,L:c,R:-1});}}));for(const e of edges)e.bc=e.R>=0?'interior':e.a<mesh.nt&&e.b<mesh.nt?'wall':'far';
 let faces;if(scheme==='cell')faces=edges.map(e=>({...e,terms:[[e.a,e.b,1]],segs:[[e.a,e.b]]}));
 else{const off=mesh.X.length;faces=edges.map(e=>{const a=Math.min(e.a,e.b),b=Math.max(e.a,e.b),mid=off+mesh.C.length+edges.indexOf(e),terms=[],segs=[];for(const c of [e.L,e.R].filter(i=>i>=0)){const cell=mesh.C[c],s=cell[(cell.indexOf(a)+1)%cell.length]===b?1:-1;terms.push([mid,off+c,s]);segs.push([mid,off+c]);}return {a,b,L:a,R:b,bc:'interior',terms,segs};});for(const e of edges.filter(e=>e.R<0)){const mid=off+mesh.C.length+edges.indexOf(e);for(const a of [e.a,e.b])faces.push({L:a,R:-1,bc:e.bc,terms:[[e.a,e.b,.5]],segs:[[a,mid]]});}}
 const rank={interior:0,wall:1,far:2};faces.sort((a,b)=>rank[a.bc]-rank[b.bc]||a.L-b.L||a.R-b.R);return {edges,faces};}
function model({scheme='cell',level=1,M=.2,angle=10,objective='lift',coords,mms}={}){
 if(!['cell','node'].includes(scheme)||!Number.isInteger(level)||level<1||level>6||!Number.isFinite(M)||M<0||M>.3||!Number.isFinite(angle))throw Error('Unsupported Euler model settings');
 const grid=mesh(level);if(coords)grid.X=coords.map(p=>p.slice());const topo=topology(grid,scheme),nc=scheme==='cell'?grid.C.length:grid.X.length,nd=4*nc,params=[M,angle*Math.PI/180,...grid.X.flat()];
 // Incoming entropy/vorticity branch is tied to the BASE free-stream normal.
 const geo=geometry(real,params.slice(2));topo.faces.forEach((f,i)=>f.inflow=geo.faces[i].n[0]*Math.cos(params[1])+geo.faces[i].n[1]*Math.sin(params[1])<0);
 // Manufactured-solution mode (order study only): every boundary face listed in data.exterior uses the far-field
 // characteristic closure with that constant exterior state in place of U_inf (its own normal velocity fixes the
 // branch; listed wall faces use the same closure), and a constant source is subtracted from the residual.
 const data=mms?mms({grid,topo,geo,scheme,nc,nd,physics:physics(real)}):null;
 if(data)topo.faces.forEach((f,i)=>{if(f.bc!=='interior'&&data.exterior[i]){f.exterior=data.exterior[i];const z=physics(real).prim(f.exterior);f.inflow=z.u*geo.faces[i].n[0]+z.v*geo.faces[i].n[1]<0;}});
 function geometry(o,Xflat){const {c,add,sub,mul,pow}=o,scale=(a,s)=>a.map(v=>mul(v,c(s))),avg=pts=>[0,1].map(k=>mul(pts.reduce((s,p)=>add(s,p[k]),c(0)),c(1/pts.length))),X=Array.from({length:grid.X.length},(_,i)=>Xflat.slice(2*i,2*i+2)),cent=grid.C.map(cell=>avg(cell.map(i=>X[i]))),mid=topo.edges.map(e=>avg([X[e.a],X[e.b]])),points=[...X,...cent,...mid];
 const area=poly=>mul(poly.reduce((s,p,i)=>{const q=poly[(i+1)%poly.length];return add(s,sub(mul(p[0],q[1]),mul(p[1],q[0])));},c(0)),c(.5)),vol=Array.from({length:nc},()=>c(0)),polys=Array.from({length:nc},()=>[]);
 grid.C.forEach((cell,j)=>{if(scheme==='cell'){vol[j]=area(cell.map(i=>X[i]));polys[j].push(cell.map(i=>X[i]));}else cell.forEach((a,k)=>{const next=cell[(k+1)%cell.length],prev=cell[(k-1+cell.length)%cell.length],poly=[X[a],avg([X[a],X[next]]),cent[j],avg([X[prev],X[a]])];vol[a]=add(vol[a],area(poly));polys[a].push(poly);});});
 const faces=topo.faces.map(f=>{let n=[c(0),c(0)];for(const [a,b,s] of f.terms){n[0]=add(n[0],mul(sub(points[b][1],points[a][1]),c(s)));n[1]=sub(n[1],mul(sub(points[b][0],points[a][0]),c(s)));}return {n,len:pow(add(mul(n[0],n[0]),mul(n[1],n[1])),.5),segs:f.segs.map(([a,b])=>[points[a],points[b]])};});return {X,cent,points,vol,polys,faces,locations:scheme==='cell'?cent:X};}
 function physics(o){const {c,add,sub,mul,div,pow,sin,cos}=o,scale=(v,s)=>v.map(x=>mul(x,s)),sum=(a,b)=>a.map((x,i)=>add(x,b[i]));
 function prim(U){const rho=U[0],u=div(U[1],rho),v=div(U[2],rho),p=mul(c(gamma-1),sub(U[3],mul(c(.5),add(mul(U[1],u),mul(U[2],v))))),a=pow(div(mul(c(gamma),p),rho),.5);return {rho,u,v,p,a};}
 function cons(r,u,v,p){return [r,mul(r,u),mul(r,v),add(div(p,c(gamma-1)),mul(mul(c(.5),r),add(mul(u,u),mul(v,v))))];}
 function physical(U,n){const q=prim(U),un=add(mul(q.u,n[0]),mul(q.v,n[1]));return [mul(q.rho,un),add(mul(U[1],un),mul(q.p,n[0])),add(mul(U[2],un),mul(q.p,n[1])),mul(add(U[3],q.p),un)];}
 function far(UL,UI,n,len,inflow,log){const q=prim(UL),z=prim(UI),nx=div(n[0],len),ny=div(n[1],len),normal=q=>add(mul(q.u,nx),mul(q.v,ny)),tan=q=>sub(mul(q.v,nx),mul(q.u,ny)),rp=add(normal(q),mul(c(2/(gamma-1)),q.a)),rm=sub(normal(z),mul(c(2/(gamma-1)),z.a)),un=mul(c(.5),add(rp,rm)),sound=mul(c((gamma-1)/4),sub(rp,rm)),up=inflow?z:q,S=div(up.p,pow(up.rho,gamma)),rho=pow(div(mul(sound,sound),mul(c(gamma),S)),1/(gamma-1)),ut=tan(up),u=sub(mul(un,nx),mul(ut,ny)),v=add(mul(un,ny),mul(ut,nx)),pb=mul(S,pow(rho,gamma));
 if(log)Object.assign(log,{nx,ny,unL:normal(q),unInf:normal(z),utL:tan(q),utInf:tan(z),cL:q.a,cInf:z.a,SL:div(q.p,pow(q.rho,gamma)),SInf:div(z.p,pow(z.rho,gamma)),rp,rm,un,c:sound,S,rho,ut,u,v,p:pb});
 return cons(rho,u,v,pb);}
 function free(p){return cons(c(1),mul(mul(p[0],c(Math.sqrt(gamma))),cos(p[1])),mul(mul(p[0],c(Math.sqrt(gamma))),sin(p[1])),c(1));}
 function flux(UL,UR,f,g,UI){const q=prim(UL);if(f.bc==='wall'&&!f.exterior)return {H:[c(0),mul(q.p,g.n[0]),mul(q.p,g.n[1]),c(0)],UR:UL};const B=f.R>=0?UR:far(UL,f.exterior?f.exterior.map(c):UI,g.n,g.len,f.inflow),FL=physical(UL,g.n),FR=physical(B,g.n);return {UR:B,H:FL.map((x,k)=>sub(mul(c(.5),add(x,FR[k])),mul(mul(c(alpha/2),g.len),sub(B[k],UL[k]))))};}
 return {prim,cons,physical,far,free,flux};}
 function evaluate(o,U,p){const {c,add,sub,mul}=o,g=geometry(o,p.slice(2)),ph=physics(o),UI=ph.free(p),R=Array.from({length:nd},()=>c(0)),local=[];let J=c(0);
 topo.faces.forEach((f,j)=>{const L=U.slice(4*f.L,4*f.L+4),RR=f.R>=0?U.slice(4*f.R,4*f.R+4):null,q=ph.flux(L,RR,f,g.faces[j],UI);q.H.forEach((h,k)=>{R[4*f.L+k]=add(R[4*f.L+k],h);if(f.R>=0)R[4*f.R+k]=sub(R[4*f.R+k],h);});if(f.bc==='wall')J=add(J,mul(ph.prim(L).p,g.faces[j].n[objective==='drag'?0:1]));local.push({...q,UL:L});});if(data)for(let i=0;i<nd;i++)R[i]=sub(R[i],c(data.forcing[i]));return {R,J,g,local};}
 function linearize(U,p=params){const t=tape(),u=U.map(t.op.input),pp=p.map(t.op.input),out=evaluate(t.op,u,pp),outids=[...out.R,out.J];return {R:out.R.map(i=>t.val[i]),J:t.val[out.J],size:t.val.length,faceVjp:(j,w)=>{const b=t.reverse(out.local[j].H,w);return {U:b.slice(0,nd),p:b.slice(nd)};},jvp:(v,dp=zeros(p.length))=>{const d=t.forward([...v,...dp]);return {R:out.R.map(i=>d[i]),J:d[out.J],faces:out.local.map(q=>q.H.map(i=>d[i]))};},vjp:(w,bJ=0)=>{const b=t.reverse(outids,[...w,bJ]);return {U:b.slice(0,nd),p:b.slice(nd)};}};}
 function initial(p=params){return Array.from({length:nc},()=>physics(real).free(p)).flat();}
 function valid(U,p=params){const ph=physics(real);return Array.from({length:nc},(_,i)=>ph.prim(U.slice(i*4,i*4+4))).every(q=>q.rho>.05&&q.p>.05&&Math.hypot(q.u,q.v)/q.a<.85);}
 function precondition(g,transpose=false){const diag=zeros(nc),lower=Array.from({length:nc},()=>[]);topo.faces.forEach((f,j)=>{const a=alpha*g.faces[j].len/2;diag[f.L]+=a;if(f.R>=0){diag[f.R]+=a;const l=Math.max(f.L,f.R),r=Math.min(f.L,f.R);lower[l].push([r,-a]);}});return rhs=>{const z=rhs.slice();if(transpose){for(let i=nc-1;i>=0;i--)for(let k=0;k<4;k++){z[4*i+k]/=diag[i];for(const [j,c] of lower[i])z[4*j+k]-=c*z[4*i+k];}return z;}for(let i=0;i<nc;i++)for(let k=0;k<4;k++){for(const [j,c] of lower[i])z[4*i+k]-=c*z[4*j+k];z[4*i+k]/=diag[i];}return z;};}
 function diagnostics(U,p=params){const out=evaluate(real,U,p),ph=physics(real);let maxSpeed=0,maxMach=0,minRho=Infinity,minP=Infinity,mismatches=0;
 out.local.forEach((q,j)=>[q.UL,q.UR].forEach((u,k)=>{const z=ph.prim(u),g=out.g.faces[j],un=(z.u*g.n[0]+z.v*g.n[1])/g.len;maxSpeed=Math.max(maxSpeed,Math.abs(un)+z.a);maxMach=Math.max(maxMach,Math.hypot(z.u,z.v)/z.a);minRho=Math.min(minRho,z.rho);minP=Math.min(minP,z.p);if(k===1&&(topo.faces[j].bc==='far'||topo.faces[j].exterior)&&(un<0)!==topo.faces[j].inflow)mismatches++;}));return {maxSpeed,maxMach,minRho,minP,mismatches,minVolume:Math.min(...out.g.vol)};}
 function solve(p=params,start,opts={}){let U=start?start.slice():initial(p);const history=[],states=[],P=precondition(geometry(real,p.slice(2)));for(let it=0;it<35;it++){const A=linearize(U,p),rn=norm(A.R);history.push(rn);states.push(U.slice());if(rn<3e-12){const check=diagnostics(U,p);if(opts.check!==false&&!(check.maxSpeed<alpha&&check.maxMach<.85&&check.minRho>.05&&check.minP>.05&&check.minVolume>0&&check.mismatches===0))throw Error('State leaves supported smooth subsonic boundary branch');return {U,J:A.J,history,states,converged:true,A,diagnostics:check};}const l=gmres(v=>A.jvp(v).R,A.R.map(x=>-x),opts.precondition?opts.precondition(A,false):P,Math.min(1e-12,rn*1e-3),nd);if(!l.converged&&l.residual>rn*.01)throw Error('Euler Newton linear solve failed: '+l.residual);let step=1,next;while(step>1e-7){next=U.map((x,i)=>x+step*l.x[i]);if(valid(next,p)&&norm(evaluate(real,next,p).R)<(1-1e-4*step)*rn)break;step*=.5;}if(step<=1e-7)throw Error('Euler positivity/residual line search failed');U=next;}throw Error('Euler primal did not converge');}
 function sensitivities(sol,p=params,opts={}){const A=sol.A||linearize(sol.U,p),P=opts.precondition?opts.precondition(A,true):precondition(geometry(real,p.slice(2)),true),Jg=A.vjp(zeros(nd),1),adj=gmres(v=>A.vjp(v).U,Jg.U.map(x=>-x),P,2e-12,nd);if(!adj.converged)throw Error('Euler adjoint failed');const total=A.vjp(adj.x,1);return {psi:adj.x,gradient:total.p,history:adj.history,residual:adj.residual};}
 function tangent(sol,k,p=params){const A=sol.A||linearize(sol.U,p),dp=zeros(p.length);dp[k]=1;const direct=A.jvp(zeros(nd),dp),P=precondition(geometry(real,p.slice(2))),l=gmres(v=>A.jvp(v).R,direct.R.map(x=>-x),P,2e-12,nd);if(!l.converged)throw Error('Euler tangent failed');return {...l,gradient:A.jvp(l.x,dp).J};}
 function complexGradient(sol,k,h=1e-25,p=params){const A=sol.A||linearize(sol.U,p),pp=p.map((x,i)=>[x,i===k?h:0]),U=sol.U.map(x=>[x,0]),P=precondition(geometry(real,p.slice(2)));let im=zeros(nd),history=[];for(let it=0;it<4;it++){const out=evaluate(complex,U,pp),rhs=out.R.map(x=>-x[1]/h),rn=norm(rhs);history.push(rn);if(rn<2e-12)break;const l=gmres(v=>A.jvp(v).R,rhs,P,1e-12,nd);im=im.map((x,i)=>x+l.x[i]);U.forEach((x,i)=>x[1]=h*im[i]);}const out=evaluate(complex,U,pp),residual=norm(out.R.map(x=>x[1]/h));if(residual>2e-10)throw Error('complex residual not converged');return {gradient:out.J[1]/h,residual,history};}
 function boundary(j,U,p=params){ // one far face: intermediate values, U_b and G_U=dU_b/dU_L (by the same far())
 const f=topo.faces[j];if(f.bc!=='far'&&!f.exterior)throw Error('Not a far-field face');const g=geometry(real,p.slice(2)).faces[j],ph=physics(real),UL=U.slice(4*f.L,4*f.L+4),UI=f.exterior||ph.free(p),log={},Ub=ph.far(UL,UI,g.n,g.len,f.inflow,log),t=tape(),x=UL.map(t.op.input),out=physics(t.op).far(x,UI.map(t.op.c),g.n.map(t.op.c),t.op.c(g.len),f.inflow),cols=[0,1,2,3].map(k=>{const d=t.forward([0,1,2,3].map(i=>+(i===k)));return out.map(o=>d[o]);});
 return {face:j,inflow:f.inflow,UL,UI,Ub,log,n:g.n,len:g.len,G:[0,1,2,3].map(r=>cols.map(col=>col[r]))};}
 function riemann(U,n,len){ // Riemann coordinates W=(r+,r-,S,u_t) of a state at a face and dW/dU (same prim())
 const t=tape(),x=U.map(t.op.input),{c,add,sub,mul,div,pow}=t.op,q=physics(t.op).prim(x),nx=c(n[0]/len),ny=c(n[1]/len),un=add(mul(q.u,nx),mul(q.v,ny)),ut=sub(mul(q.v,nx),mul(q.u,ny)),k=c(2/(gamma-1)),out=[add(un,mul(k,q.a)),sub(un,mul(k,q.a)),div(q.p,pow(q.rho,gamma)),ut],cols=[0,1,2,3].map(j=>{const d=t.forward([0,1,2,3].map(i=>+(i===j)));return out.map(o=>d[o]);});
 return {W:out.map(o=>t.val[o]),dW:[0,1,2,3].map(r=>cols.map(col=>col[r]))};}
 function fluxJacobian(U,n){const t=tape(),x=U.map(t.op.input),out=physics(t.op).physical(x,n.map(t.op.c)),cols=[0,1,2,3].map(k=>{const d=t.forward([0,1,2,3].map(i=>+(i===k)));return out.map(o=>d[o]);});return [0,1,2,3].map(r=>cols.map(col=>col[r]));}
 return {scheme,grid,topo,nc,nd,params,data,boundary,riemann,fluxJacobian,diagnostics,preconditioner:transpose=>precondition(geo,transpose),geometry:()=>geometry(real,params.slice(2)),geometryAt:p=>geometry(real,p.slice(2)),physics:physics(real),evaluate:(U,p=params)=>evaluate(real,U,p),linearize,initial,solve,sensitivities,tangent,complexGradient,valid};
}
function gmres(A,b,P=x=>x.slice(),tol=1e-11,max=b.length){const n=b.length,beta=norm(b);if(beta<=tol)return {x:zeros(n),residual:beta,history:[beta],converged:true};const V=[b.map(x=>x/beta)],Z=[],H=[],cs=zeros(max),sn=zeros(max),g=zeros(max+1),history=[beta];g[0]=beta;let m=0;
 for(let j=0;j<max;j++){const h=zeros(j+2);H.push(h);Z.push(P(V[j]));let w=A(Z[j]);for(let pass=0;pass<2;pass++)for(let i=0;i<=j;i++){const hij=dot(V[i],w);h[i]+=hij;w=w.map((x,k)=>x-hij*V[i][k]);}h[j+1]=norm(w);const nextnorm=h[j+1];if(nextnorm>1e-16)V.push(w.map(x=>x/nextnorm));for(let i=0;i<j;i++){const a=h[i],b=h[i+1];h[i]=cs[i]*a+sn[i]*b;h[i+1]=-sn[i]*a+cs[i]*b;}const a=h[j],bb=h[j+1],r=Math.hypot(a,bb);cs[j]=r?a/r:1;sn[j]=r?bb/r:0;h[j]=r;h[j+1]=0;g[j+1]=-sn[j]*g[j];g[j]*=cs[j];m=j+1;history.push(Math.abs(g[j+1]));if(Math.abs(g[j+1])<tol||nextnorm<=1e-16)break;}
 const y=zeros(m);for(let i=m-1;i>=0;i--){let a=g[i];for(let j=i+1;j<m;j++)a-=H[j][i]*y[j];y[i]=a/H[i][i];}const x=zeros(n);for(let i=0;i<m;i++)for(let j=0;j<n;j++)x[j]+=Z[i][j]*y[i];const residual=norm(A(x).map((z,i)=>z-b[i]));return {x,residual,history,converged:residual<=Math.max(tol*5,1e-13)};}
const api={model,mesh,gamma,alpha,gmres,dot,norm,real,complex,tape};if(typeof module!=='undefined')module.exports=api;else root.Euler2D=api;
})(typeof window!=='undefined'?window:globalThis);
附录 AAppendix A

附录 A:连续伴随与一致性检查

Appendix A: continuous adjoint and consistency check

A.1 变分与域内伴随方程

A.1 Variation and the interior adjoint equation

连续问题:在 Ω\Omega 内 ∇⋅F(U)=0\nabla\cdot F(U)=0;物面 Γw\Gamma_w 上 un=0u_n=0;远场 Γ∞\Gamma_\infty 上入域特征量取来流值;目标 J=∫Γwp 𝒏^⋅𝒆d dsJ=\int_{\Gamma_w}p\,\hat{\boldsymbol n}\cdot\boldsymbol e_d\,ds(𝒏^\hat{\boldsymbol n} 指向流体域外)。拉格朗日函数 ℒ=J+∫ΩλT∇⋅F(U) dΩ\mathcal L=J+\int_\Omega\lambda^T\nabla\cdot F(U)\,d\Omega,在固定几何与固定来流下变分:

Continuous problem: ∇⋅F(U)=0\nabla\cdot F(U)=0 in Ω\Omega; un=0u_n=0 on the wall Γw\Gamma_w; incoming characteristic quantities take free-stream values on the far field Γ∞\Gamma_\infty; objective J=∫Γwp 𝒏^⋅𝒆d dsJ=\int_{\Gamma_w}p\,\hat{\boldsymbol n}\cdot\boldsymbol e_d\,ds (𝒏^\hat{\boldsymbol n} out of the fluid). With ℒ=J+∫ΩλT∇⋅F(U) dΩ\mathcal L=J+\int_\Omega\lambda^T\nabla\cdot F(U)\,d\Omega, vary at fixed geometry and free stream:

δℒ=δJ+∮∂ΩλTFn^,U δU ds−∫ΩδUT(Fx,UT∂xλ+Fy,UT∂yλ) dΩ.\delta\mathcal L=\delta J+\oint_{\partial\Omega}\lambda^TF_{\hat n,U}\,\delta U\,ds-\int_\Omega\delta U^T\bigl(F_{x,U}^T\partial_x\lambda+F_{y,U}^T\partial_y\lambda\bigr)\,d\Omega.

域内项对任意 δU\delta U 为零,给出伴随方程 Fx,UT∂xλ+Fy,UT∂yλ=0F_{x,U}^T\partial_x\lambda+F_{y,U}^T\partial_y\lambda=0。边界项只对可容许的 δU\delta U 要求为零,由此得到物面与远场条件。

The interior term vanishes for every δU\delta U, which gives the adjoint equation Fx,UT∂xλ+Fy,UT∂yλ=0F_{x,U}^T\partial_x\lambda+F_{y,U}^T\partial_y\lambda=0. The boundary terms need to vanish only for admissible δU\delta U, which gives the wall and far-field conditions.

A.2 物面条件

A.2 The wall condition

在物面上 un=0u_n=0,并且可容许变分保持 δun=0\delta u_n=0。由第 1 节的 Fn^F_{\hat n},Fn^,UδU=(0,n^xδp,n^yδp,0)TF_{\hat n,U}\delta U=(0,\hat n_x\delta p,\hat n_y\delta p,0)^T,于是物面上的边界项与目标变分合并为 ∫Γw(λρun^x+λρvn^y+𝒏^⋅𝒆d) δp ds\int_{\Gamma_w}(\lambda_{\rho u}\hat n_x+\lambda_{\rho v}\hat n_y+\hat{\boldsymbol n}\cdot\boldsymbol e_d)\,\delta p\,ds。δp\delta p 任意,所以得到第 16 节的物面条件。它只约束 λ\lambda 的一个组合,与滑移壁面只有一个入域特征相对应。

On the wall un=0u_n=0, and admissible variations keep δun=0\delta u_n=0. From Fn^F_{\hat n} of Section 1, Fn^,UδU=(0,n^xδp,n^yδp,0)TF_{\hat n,U}\delta U=(0,\hat n_x\delta p,\hat n_y\delta p,0)^T, so the wall boundary term and the objective variation combine into ∫Γw(λρun^x+λρvn^y+𝒏^⋅𝒆d) δp ds\int_{\Gamma_w}(\lambda_{\rho u}\hat n_x+\lambda_{\rho v}\hat n_y+\hat{\boldsymbol n}\cdot\boldsymbol e_d)\,\delta p\,ds. Since δp\delta p is arbitrary, the wall condition of Section 16 follows. It constrains only one combination of λ\lambda, matching the single incoming characteristic of a slip wall.

A.3 远场条件:连续形式与代码中的离散形式

A.3 The far-field condition: continuous form and the discrete form in the code

用 Riemann 坐标 W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t) 表示,闭合是 Wb=IoutWL+(I−Iout)W∞W_b=I_{\mathrm{out}}W_L+(I-I_{\mathrm{out}})W_\infty,所以对任意 ULU_L 都有 GU=∂U∂W∣Ub Iout ∂W∂U∣ULG_U=\frac{\partial U}{\partial W}\big|_{U_b}\,I_{\mathrm{out}}\,\frac{\partial W}{\partial U}\big|_{U_L}(∂U/∂W=(∂W/∂U)−1\partial U/\partial W=(\partial W/\partial U)^{-1})。GG 满足 G(G(U))=G(U)G(G(U))=G(U),在满足闭合的状态(Ub=ULU_b=U_L)上 GUG_U 是投影:GU2=GUG_U^2=G_U,秩等于出域特征个数(入流 1、出流 3)。连续问题的可容许变分正是 GUG_U 的值域,于是远场条件是 λTFn^,UGU=0\lambda^TF_{\hat n,U}G_U=0,即第 16 节的 GUTFn^,UTλ=0G_U^TF_{\hat n,U}^T\lambda=0;由 ∂W/∂U\partial W/\partial U 可逆,它等价于对每个出域坐标 kk 有 λTFn^,U ∂U/∂Wk=0\lambda^TF_{\hat n,U}\,\partial U/\partial W_k=0。

In the Riemann coordinates W=(r+,r−,S,ut)W=(r_+,r_-,S,u_t) the closure reads Wb=IoutWL+(I−Iout)W∞W_b=I_{\mathrm{out}}W_L+(I-I_{\mathrm{out}})W_\infty, so GU=∂U∂W∣Ub Iout ∂W∂U∣ULG_U=\frac{\partial U}{\partial W}\big|_{U_b}\,I_{\mathrm{out}}\,\frac{\partial W}{\partial U}\big|_{U_L} for every ULU_L (∂U/∂W=(∂W/∂U)−1\partial U/\partial W=(\partial W/\partial U)^{-1}). GG satisfies G(G(U))=G(U)G(G(U))=G(U), and at a state that satisfies the closure (Ub=ULU_b=U_L) GUG_U is a projection: GU2=GUG_U^2=G_U, with rank equal to the number of outgoing characteristics (1 at inflow, 3 at outflow). The admissible variations of the continuous problem are exactly the range of GUG_U, so the far-field condition is λTFn^,UGU=0\lambda^TF_{\hat n,U}G_U=0, i.e. the GUTFn^,UTλ=0G_U^TF_{\hat n,U}^T\lambda=0 of Section 16; since ∂W/∂U\partial W/\partial U is invertible, it is equivalent to λTFn^,U ∂U/∂Wk=0\lambda^TF_{\hat n,U}\,\partial U/\partial W_k=0 for every outgoing coordinate kk.

代码中远场面对其控制体伴随方程的贡献是第 4 节给出的:

In the code a far-field face contributes the following to its volume’s adjoint equation (Section 4):

[12Fn,U(UL)T+αℓ2I]Hˉ+GUT[12Fn,U(Ub)T−αℓ2I]Hˉ.\bigl[\tfrac12F_{n,U}(U_L)^T+\tfrac{\alpha\ell}2I\bigr]\bar H+G_U^T\bigl[\tfrac12F_{n,U}(U_b)^T-\tfrac{\alpha\ell}2I\bigr]\bar H.

在 Ub=ULU_b=U_L 的极限下它等于 12(I+GUT)Fn,UTHˉ+αℓ2(I−GUT)Hˉ\tfrac12(I+G_U^T)F_{n,U}^T\bar H+\tfrac{\alpha\ell}2(I-G_U^T)\bar H;左乘投影 GUTG_U^T 得到 GUTFn,UTHˉG_U^TF_{n,U}^T\bar H,正是连续远场条件的表达式,而 (I−GUT)(I-G_U^T) 部分是格式自己给伴随出域分量加的迎风型数值闭合。

In the limit Ub=ULU_b=U_L this equals 12(I+GUT)Fn,UTHˉ+αℓ2(I−GUT)Hˉ\tfrac12(I+G_U^T)F_{n,U}^T\bar H+\tfrac{\alpha\ell}2(I-G_U^T)\bar H. Multiplying by the projection GUTG_U^T leaves GUTFn,UTHˉG_U^TF_{n,U}^T\bar H, exactly the expression of the continuous far-field condition, while the (I−GUT)(I-G_U^T) part is the scheme’s own upwind-type numerical closure for the adjoint’s outgoing components.

这些结论在本页第 1 层网格的收敛解上实测(M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ,全部远场面):GUG_U 与 (∂U/∂W) Iout (∂W/∂U)(\partial U/\partial W)\,I_{\mathrm{out}}\,(\partial W/\partial U) 最大差 6×10⁻¹⁵(GUG_U 元素最大为 4.28);G(G(U))−G(U)G(G(U))-G(U) 最大 1×10⁻¹⁵;闭合状态上 GU2−GUG_U^2-G_U 最大 5×10⁻¹⁵,迹在入流面为 1、出流面为 3(偏差小于 2×10⁻¹⁵);上面的面转置公式与计算图逆序结果最大差 2×10⁻¹⁵。

These statements are measured on this page’s level-1 converged solution (M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ, every far-field face): GUG_U and (∂U/∂W) Iout (∂W/∂U)(\partial U/\partial W)\,I_{\mathrm{out}}\,(\partial W/\partial U) differ by at most 6×10⁻¹⁵ (largest entry of GUG_U: 4.28); G(G(U))−G(U)G(G(U))-G(U) is at most 1×10⁻¹⁵; at the closure state GU2−GUG_U^2-G_U is at most 5×10⁻¹⁵ and the trace is 1 on inflow faces and 3 on outflow faces (deviation below 2×10⁻¹⁵); the face transpose formula above and the reversed computation graph differ by at most 2×10⁻¹⁵.

A.4 一致性检查

A.4 The consistency check

在第 15 节的 5 层细分网格上(M∞=0.2M_\infty=0.2、θ=10∘\theta=10^\circ,正常边界),对两个目标求离散伴随,并计算:物面缺陷 ζf\zeta_f(第 16 节)的面长加权 L2L_2 范数与最大值;只取每条八边形边中间一半的面(固定区域,远离角点)的 L2L_2 范数;远场的比值:

On the 5 subdivided levels of Section 15 (M∞=0.2M_\infty=0.2, θ=10∘\theta=10^\circ, normal boundaries), solve the discrete adjoint for both objectives and compute: the length-weighted L2L_2 norm and the maximum of the wall defect ζf\zeta_f (Section 16); its L2L_2 norm over only the middle half of every octagon edge (a fixed region away from the corners); the far-field ratio:

ζfar=(∑f∥GUTFn^,UTψL∥2/∑f∥Fn^,UTψL∥2)1/2;\zeta_{\mathrm{far}}=\bigl(\sum_f\|G_U^TF_{\hat n,U}^T\psi_L\|^2/\sum_f\|F_{\hat n,U}^T\psi_L\|^2\bigr)^{1/2};

以及 max⁡i∥ψi∥\max_i\|\psi_i\|。若离散伴随收敛到满足这些条件的连续伴随,中间一半的缺陷和 ζfar\zeta_{\mathrm{far}} 都应随 hh 趋于零,max⁡∣ψ∣\max|\psi| 应保持有界;角点处连续条件本身跳变,所以整个物面上的最大缺陷不必趋于零。

and max⁡i∥ψi∥\max_i\|\psi_i\|. If the discrete adjoint converged to a continuous adjoint satisfying these conditions, the middle-half defect and ζfar\zeta_{\mathrm{far}} would tend to zero with hh and max⁡∣ψ∣\max|\psi| would stay bounded; at the corners the continuous condition itself jumps, so the largest defect over the whole wall need not tend to zero.

结果(本页格式)。整个物面上缺陷的 L2L_2 范数对 JyJ_y 为 0.64、0.51、0.41、0.41、0.44,对 JxJ_x 为 0.69、0.55、0.40、0.30、0.25;最大缺陷对 JyJ_y 为 0.92、0.82、0.91、1.28、1.96,出现在角点旁(最细一层在 46.0°,最近的角点在 45°)。只取每条边中间一半的面,L2L_2 缺陷对 JyJ_y 为 0.641、0.499、0.346、0.214、0.105,对 JxJ_x 为 0.686、0.487、0.347、0.183、0.080,逐层下降,最细一对网格上的观测阶分别为 1.02 与 1.19;格点格式的对应值(JyJ_y)为 0.585、0.354、0.239、0.129、0.055。远场比值 ζfar\zeta_{\mathrm{far}} 对 JyJ_y 为 1.31、0.92、0.58、0.42、0.35,逐层下降;对 JxJ_x 为 0.19、0.31、0.38、0.38、0.36,没有逐层下降,最细一层是第 1 层的 1.93 倍。max⁡i∥ψi∥\max_i\|\psi_i\| 对 JyJ_y 为 1.08、2.08、3.12、3.73、4.10,最细一层出现在 (0.45, −1.70),靠近下方的远场边。

Results (this page’s scheme). Over the whole wall the L2L_2 norm of the defect is 0.64, 0.51, 0.41, 0.41, 0.44 for JyJ_y and 0.69, 0.55, 0.40, 0.30, 0.25 for JxJ_x; the largest defect for JyJ_y is 0.92, 0.82, 0.91, 1.28, 1.96 and sits next to a corner (at 46.0° on the finest level, nearest corner 45°). Restricted to the middle half of every edge, the L2L_2 defect is 0.641, 0.499, 0.346, 0.214, 0.105 for JyJ_y and 0.686, 0.487, 0.347, 0.183, 0.080 for JxJ_x, falling level by level, with observed orders 1.02 and 1.19 on the finest pair; the node-centred scheme gives 0.585, 0.354, 0.239, 0.129, 0.055 (JyJ_y). The far-field ratio ζfar\zeta_{\mathrm{far}} is 1.31, 0.92, 0.58, 0.42, 0.35 for JyJ_y, falling level by level, and 0.19, 0.31, 0.38, 0.38, 0.36 for JxJ_x, not falling level by level; its finest value is 1.93 times the first. max⁡i∥ψi∥\max_i\|\psi_i\| is 1.08, 2.08, 3.12, 3.73, 4.10 for JyJ_y; on the finest level it sits at (0.45, −1.70), next to the lower far-field edge.

结论:远离角点时,离散伴随在所测网格上逐步满足连续物面条件,这是本页能给出的一致性证据。角点旁的缺陷不下降,符合连续条件在角点处不连续、连续伴随在角点附近奇异的预期;JxJ_x 的远场比值没有逐层下降,原因本页没有查明。所以本页不宣称离散伴随整体收敛到连续伴随。

Conclusion: away from the corners the discrete adjoint approaches the continuous wall condition on the tested meshes; this is the consistency evidence this page can offer. The defect next to the corners does not fall, as expected where the continuous condition is discontinuous and the continuous adjoint is singular; the far-field ratio for JxJ_x does not fall level by level, for reasons this page has not established. The page therefore does not claim that the discrete adjoint converges to the continuous adjoint as a whole.

图 A.1Figure A.1

A.5 验证范围

A.5 Scope of the verification

已验证:离散残差、目标与它们的全部导数(状态、来流、几何)彼此一致,tangent 与 adjoint 在舍入误差内相同,完整重算差分与独立复数步长在截断误差与线性求解容差之内与它们一致;远场闭合的 GUG_U 结构与面转置公式;制造解的观测阶从下方逐步接近一阶;远离角点时物面伴随条件的缺陷逐层下降。未验证:离散伴随在角点附近和远场(JxJ_x)上与连续伴随一致;离散载荷收敛到物理载荷。要检验这些,可能需要更弱的耗散(例如随状态变化的波速或二阶重构)、光滑物面和更远的远场;它们属于后续工作,而不是本页离散求导的缺陷。

Verified: the discrete residual and objective and all their derivatives (state, free stream, geometry) agree, with the tangent and the adjoint equal to round-off, and the full-rerun differences and the independent complex step agreeing with them within truncation error and the linear-solver tolerance; the structure of GUG_U and the face transpose of the far-field closure; observed orders of the manufactured solution approaching first order from below; a wall-condition defect that falls level by level away from the corners. Not verified: consistency of the discrete adjoint with the continuous adjoint next to the corners and at the far field (JxJ_x); convergence of the discrete loads to physical loads. Checking these would likely need weaker dissipation (for example a state-dependent wave speed or second-order reconstruction), a smooth body and a more distant far field; they are future work, not defects of this page’s discrete differentiation.