方程:一维定常黏性 Burgers
The equation: steady one-dimensional viscous Burgers
本节实验:读图 1 的下图,扩散通量比对流通量小多少?再读图 2:同一个 在不同 下需要什么样的源项?
Experiment: In the lower panel of Figure 1, how much smaller is the diffusive flux than the convective one? Then read Figure 2: what source does the same need at different ?
本节的内容与离散格式无关:方程、构造解、设计和目标对格心页(adjoint_burgers_viscous_cell.html)同样成立,两页解的是同一个问题。区别只在未知量放在哪里:那里在单元中心,本页在网格节点上(第 2 节)。两种格式给出不同数值之处,各节会指出并量出来。
Nothing in this section depends on the discretisation: the equation, the manufactured solution, the design and the objective are those of the cell-centred page (adjoint_burgers_viscous_cell.html), which solves the same problem. The two pages differ only in where the unknowns live: at the cell centres there, at the mesh nodes here (Section 2). Where the two schemes give different numbers, the sections say so and measure it.
从守恒律到定常方程
From a conservation law to the steady equation
黏性 Burgers 方程在其他一维页面的 Burgers 方程上增加了分子扩散。量 沿 以自身的值为速度被输运,同时按系数 扩散;写成守恒形式,总通量有两部分——对流通量 与扩散通量 ——区间内还有源项 :
The viscous Burgers equation adds molecular diffusion to the Burgers equation of the other one-dimensional pages. A quantity is carried along with its own value as the speed and spreads by diffusion with the coefficient . In conservation form the total flux has two parts, the convective flux and the diffusive flux , and a source acts inside the interval:
本页只研究固定正黏性下的定常解。令 ,并记总通量为 ,得到要离散的方程;因为含二阶导数,两端各需要一个给定值(无黏 Burgers 页面只给入口条件,不需要出口条件):
This page studies steady solutions at a fixed positive viscosity. Setting and writing for the total flux gives the equation to be discretised. Because it contains a second derivative it needs a prescribed value at each end (the inviscid Burgers pages, with an inlet condition only, needed none at the outlet):
使解光滑:陡变被扩散成薄层,层宽与 除以速度(或速度差)同量级。构造解本身没有层。 时这个问题的边界层只能出现在出口 (默认设计下层宽 ),入口 处的层属于伴随 (图 4,第 11、16 节与附录 A)。网格 Péclet 数 就是网格间距与这个层宽 之比: 表示一个网格间距比层还窄(第 3 节把它与格式的人工黏性联系起来)。激波属于一维无黏 Burgers 页(格点页与它的格心页)的附录 B, 与 的极限顺序见它的附录 D(D.4);本页的 始终固定。
With the solution is smooth: a steep change is spread into a thin layer whose width is of the order of divided by the speed (or the speed jump). The manufactured solution has no layer. With a boundary layer of this problem can only sit at the outlet end (width at the default design), while the layer at the inlet end belongs to the adjoint (Figure 4; Sections 11 and 16 and Appendix A). The mesh Péclet number is the mesh spacing in units of that layer width : means that one interval is narrower than the layer (Section 3 relates it to the artificial viscosity of the scheme). Shocks belong to Appendix B of the inviscid one-dimensional Burgers page (the node-centred page and its cell-centred partner) and the orders of the limits , to its Appendix D (D.4); here stays fixed.
一个构造解:先给解,再算源项
A manufactured solution: prescribe the solution, derive the source
要把每个导数都和精确答案比较,本页先规定解,再由解构造源项。取 为在平均值 1 上叠加的正弦波,振幅为 ,相位为 ; 对每个设计都是精确解。把它代入方程,就得到源项和两端的给定值:
To compare every derivative with an exact answer, the page prescribes the solution first and builds the source from it. Take to be a sine wave on the mean 1, with amplitude and phase ; it is the exact solution for every design. Inserting it into the equation defines the source and the two end values:
解不依赖 ,源项则依赖:其中 这一项就是源项必须补上的扩散(图 2)。 以 1 为周期,所以两端的给定值相等;默认值下 。
The solution does not depend on , but the source does: the term is the diffusion that the source has to supply (Figure 2). Because has period 1 the two end values are equal; at the defaults .
设计、目标和它们的精确导数
The design, the objective and their exact derivatives
设计输入是 ,共 3 个分量。 与 同时改变源项和两端的给定值; 改变物理通量本身,同时也改变源项。目标是解的一个加权积分,权重 :
The design input is with 3 components. Changing or changes the source and the two end values together; changing changes the physical flux itself and, through , the source. The objective is a weighted integral of the solution with the weight :
对构造解可以直接积分,目标和它的三个导数都有闭式( 不出现,因为 不依赖它):
For the manufactured solution the integral is exact, so the objective and its three derivatives have closed forms ( does not appear because does not depend on it):
默认设计下 ,梯度 。离散梯度随网格加密趋向的就是这些值(第 15 节的数据显示这一点;这是数值观察,不是证明)。有限网格上的离散目标 是另一个函数(第 7 节;本页里它是作用在节点值上的梯形公式,第 2 节),切线和伴随算出的是它的梯度,不是这个精确梯度;两者的比较见第 10 至 12 节、第 15 节和第 16 节。
At the defaults and the gradient is . These are the values that the discrete gradients approach as the mesh is refined, as the data of Section 15 show (a numerical observation, not a proof). On a finite mesh the discrete objective is a different function (Section 7; on this page it is the trapezoid rule applied to the nodal values, Section 2), and what the tangent and the adjoint compute is its gradient, not this exact one; the two are compared in Sections 10 to 12, 15 and 16.
图 1 的下图回答了实验的第一问:对流通量在 0.32 与 0.72 之间,扩散通量最大只有 0.0126,不超过对流通量的 4%。黏性几乎不改变解的通量,却决定了第 11、16 节里伴随场的结构;先记住这一点。上图里,每个节点一个点:8+1 个未知量中两端的两个恰好等于给定值,其余的点离空心圆的最远距离是 0.0278( 的粗网格;这个距离随网格加密怎样缩小,见第 15 节)。
The lower panel of Figure 1 answers the first question of the experiment: the convective flux lies between 0.32 and 0.72 and the diffusive flux is at most 0.0126, no more than 4% of it. Viscosity hardly changes the flux of the solution, yet it decides the structure of the adjoint field in Sections 11 and 16; keep that in mind. In the upper panel there is one dot per node: of the 8+1 unknowns the two at the ends equal the prescribed values exactly, and the other dots lie at most 0.0278 from their rings (a coarse mesh, ; how this distance shrinks under refinement is measured in Section 15).
用页首的控件改变 、重构和设计,下面的面板马上给出离散解、精确节点值、每个节点上的误差,以及离散目标的误差由哪两部分组成:
Change , the reconstruction and the design with the controls at the top, and the panel below shows at once the discrete solution, the exact nodal values, the error at every node, and the two parts that the error of the discrete objective is made of:
自己动手:改变 、、,看精确解、两部分通量和源项怎样变,并核对闭式的目标与梯度;再用下面的播放器,一步一步看源项是怎样由 造出来的。
Try it yourself: change , and , see how the exact solution, the two parts of the flux and the source change, and check the closed-form objective and gradient; then use the player below to watch the source being built from step by step.
先看一眼后面要反复用到的结论:解几乎感觉不到黏性(图 1),但它的灵敏度感觉得到。目标的伴随场 (第 11、12、16 节与附录 A)在 处有一个宽度约 的边界层, 越小越陡。离散伴随 (第 11 节)只定义在节点上:在内部节点上它近似 (图中的圆圈),而只有节点落进层里,它才能表示这个层的形状。默认网格(,)上,第一个内部节点 远在层外:,而 ,相差 0.29;其余内部节点上的差都不超过 0.093。(两端的 、 是两个约束行的乘子,不是 的值,图里不画;第 11 节说明它们是什么。)
A look ahead at a result used again and again: the solution hardly feels the viscosity (Figure 1), but its sensitivity does. The adjoint field of the objective (Sections 11, 12, 16 and Appendix A) has a boundary layer of width about at , steeper for smaller . The discrete adjoint (Section 11) is defined at the nodes only: at the interior nodes it approximates (the circles in the figure), and it can show the shape of such a layer only if nodes fall inside it. On the default mesh (, ) the first interior node is far outside the layer: while , a difference of 0.29; at the other interior nodes the difference is at most 0.093. (The end values , are the multipliers of the two constraint rows, not values of , so they are not drawn; Section 11 explains what they are.)
这个方程用来做什么
What this equation is for
黏性 Burgers 方程保留了流体动量方程的非线性对流项 ,又带上分子黏性项 ,是最简单的“对流加扩散”非线性方程。本课程把它放在无黏 Burgers 与一维 Navier–Stokes 之间,原因有五:
The viscous Burgers equation keeps the nonlinear convection term of the momentum equation and adds the molecular viscosity term : it is the simplest nonlinear convection–diffusion equation. The course puts it between the inviscid Burgers pages and the 1-D Navier–Stokes pages, for five reasons:
- 小。默认网格只有 9 个未知量(8+1 个节点值,其中两个由边界约束给定),每个数组和矩阵都能放在页面上,语句播放器也读得过来。Small. The default mesh has only 9 unknowns (8+1 nodal values, two of which the boundary constraints fix), so every array and matrix fits on the page and the statement players stay readable.
- 非线性。数值通量对迹的偏导 随状态变化,所以线性化(Jacobian)依赖于它取在哪个状态;切线与伴随都取在收敛的原始解上。Nonlinear. The derivative of the numerical flux with respect to a trace, , changes with the state, so the linearisation (the Jacobian) depends on where it is taken; the tangent and the adjoint are both evaluated at the converged primal solution.
- 有精确答案可比。构造解给出每个设计下的精确解、精确目标和精确梯度,所以每个导数、每个收敛阶都能和真值比较。Exact answers to compare with. The manufactured solution gives the exact solution, objective and gradient for every design, so every derivative and every convergence rate can be compared with the truth.
- 物理通量依赖导数 。面通量要用面两侧节点值的差商近似 ,间距在每个面上都是 ,边界面也不例外——这正是黏性 Navier–Stokes 页面每个面上要做的事,这里只有一个变量。The physical flux depends on the derivative . A face flux needs a difference quotient of the two nodal values on its sides to approximate , with the distance on every face, the boundary faces included: exactly what each face of the viscous Navier–Stokes pages does, here with one variable.
- 设计进入物理通量和源项。 改变源项和边界值, 改变通量本身;第 12 节逐项分清这三条路径。The design enters the physical flux and the source. change the source and the boundary values, changes the flux itself; Section 12 separates the three paths one by one.
本页的数值、图和表来自本页的代码(模型在构建页面时与独立的 Python 实现逐项对照,第 15 节的大网格数据来自 Python 程序)。数值检查是验证证据,不是数学证明;本页不声称非线性方程或格式的存在性、唯一性或收敛定理。本页证明了的恒等式和性质,在它们出现的地方标明。
The numbers, figures and tables of this page come from the page's own code (the model is held to an independent Python implementation when the page is built, and the large-grid data of Section 15 come from the Python program). Numerical checks are verification evidence, not mathematical proofs; the page claims no existence, uniqueness or convergence theorem for the nonlinear equation or for the scheme. The identities and properties it does prove are marked where they occur.
网格:节点与对偶控制体
The mesh: nodes and dual volumes
本节实验:数一数, 的网格有几个节点、几个对偶控制体、几个面?每个内部面和每个边界面各读哪些节点?哪两个控制体只有一半宽?先答,再对照图 5。
Experiment: Count the nodes, the dual volumes and the faces of the mesh with intervals. Which nodes does each interior face and each boundary face read, and which two dual volumes have only half the width? Answer first, then compare with Figure 5.
第 1 节给出了方程。本节把区间 分成 个等宽的区间,宽度 ,把未知量放在区间的端点(网格节点)上,让每个节点管着它周围的一段对偶控制体,再说明页面存哪些数据。
Section 1 gave the equation. This section divides into equal intervals of width , puts the unknowns at the interval end points (the mesh nodes), gives each node the dual volume around it as its control volume, and says what the page stores.
节点、对偶控制体和面
Nodes, dual volumes and faces
节点 在 。未知量 是解在这个节点上的点值,一共 个,两端的节点也在其中:
Node is at . The unknown is the point value of the solution at that node; there are of them, the two end nodes included:
每个节点管着它周围的一段,即对偶控制体 ,宽度是 (),两端的 与 只有 。控制体之间的界面是面:面 ()在节点 与节点 的中点 ,它是 的右面,也是 的左面。一共 个面,而且每个面两侧都有节点,最外面的两个面也不例外:没有面要去读给定值或半个间距的数据,第 3 节的面通量在边界面上没有特殊情形。
Each node controls the stretch around it, its dual volume , of width (); the two end volumes and have only . The interfaces between the volumes are the faces: face () is the midpoint of the interval between node and node ; it is the right face of and the left face of . There are faces and every one of them has a node on each side, the two outermost included: no face reads a prescribed value or half an interval of data, and the face flux of Section 3 has no special case at a boundary face.
两个端点节点的未知量在向量里,但它们那一行的方程不是通量平衡:第 行和第 行是边界约束 、(第 4 节)。所以 个未知量对应 个通量平衡(内部节点的对偶控制体)加 2 个约束。本页的格式不需要穿过 与 的通量,因为约束已经给出了端点值,它也从不计算这样的通量。
The two end nodes are unknowns of the vector, but the equations of their rows are not flux balances: row and row are the boundary constraints , (Section 4). So the unknowns are matched by flux balances (the dual volumes of the interior nodes) plus 2 constraints. The scheme needs no flux through the ends and of the interval, because the constraints already fix the end values, and it never computes one.
先看网格是怎样一步一步搭起来的:
First see how the mesh is built, step by step:
选一个面或一个节点,看它读哪些未知量;再换一种重构,数一数:
Select a face or a node and see which unknowns it reads; then change the reconstruction and count:
未知量的精确值:节点值,不是平均值
The exact values of the unknowns: nodal values, not means
对构造解,未知量 的精确值就是解在节点上的取值,不需要任何平均:
For the manufactured solution the exact value of the unknown is simply the solution at the node, with no averaging:
状态的误差始终拿离散解和这些精确节点值比较(它们也是 Newton 迭代的初值)。格心页的未知量是另一种对象:单元 上解的平均值。对这个正弦解,振荡部分的平均值是它在单元中心处的值乘以 ,等于把振幅从 缩小为 :
The errors of the state are always measured between the discrete solution and these exact nodal values (which are also the initial guess of the Newton iteration). The unknown of the cell-centred page is a different object: the mean value of the solution over a cell . For this sine wave the mean of the oscillating part over a cell is its value at the cell centre times , which reduces the amplitude from to :
默认网格上 ,振幅缩小 2.6%,平均值与中心值最多差 ; 与 时是 0.0013 与 0.00032,按 缩小(图 6)。所以同一个 在两页里给出的未知量向量并不相同,两页的状态误差是离各自精确向量(这里是采样值,那里是平均值)的距离:同一个 下两个状态误差不是同一个量的误差。两页都定义的量,如目标 和它的梯度,则可以直接比较。
At the defaults , a reduction of the amplitude by 2.6%, and a mean differs from the value at the centre by at most ; at and it is 0.0013 and 0.00032, shrinking like (Figure 6). So the same gives different unknown vectors on the two pages, and each page measures its state errors from its own exact vector (samples here, means there): two state errors at the same are not errors of one quantity. What both pages define, such as the objective and its gradient, can be compared directly.
记号
Notation
默认设置:,无限制器线性重构,,即页首控件的初始值;图和文字除另有说明外都取默认设置。
Default settings: , unlimited linear reconstruction and , the initial values of the controls at the top; the figures and the text use them unless another setting is stated.
按伴随优化文献的通常写法,设计变量记作 ,分量 、、。残差是 (两端两行是约束),状态 Jacobian 是 ,设计 Jacobian 是 ,离散目标是 ,伴随变量是 。
As is usual in the adjoint-optimization literature, the design variables are written , with components , , . The residual is (the two end rows are the constraints), the state Jacobian is , the design Jacobian is , the discrete objective is and the adjoint variable is .
切线(前向,tangent)与伴随(反向,adjoint)一律用自动微分的记号:程序变量 的切线写 ,伴随写 ,伴随累加写 ,伴随语句按逆序执行; 与 一一对应 Tapenade 等工具里的变量 x_d 与 x_b,本页的伪代码就用这些名字。变分符号 只出现在连续推导里(附录 A 与第 16 节中的连续推导:连续函数的变分);Newton 与 GS 的迭代增量写 。
The tangent (forward) and the adjoint (reverse) always use the notation of automatic differentiation: the forward derivative of a program variable is , its adjoint is , an adjoint accumulation is written and the statements run in reverse order. They correspond one to one to the variables x_d and x_b of AD tools such as Tapenade, and the pseudocode of this page uses these names. The variation symbol appears only in continuous derivations (Appendix A and the continuous derivation of Section 16: variations of continuous functions); the increments of the Newton and GS iterations are written .
有两个与黏性有关的量,不要混淆:物理黏性 (设计分量 ,进入物理通量 );Rusanov 数值通量里的耗散(第 3 节),其波速 是固定常数,一阶格式里它等效于随网格变化的人工黏性 ,不是设计变量。
Two quantities that both look like a viscosity must not be confused: the physical viscosity (the design component , which enters the physical flux ) and the dissipation of the Rusanov numerical flux (Section 3), whose speed is a fixed constant and which acts at first order as an artificial viscosity that changes with the mesh; it is not a design variable.
本页的主要符号
The main symbols of this page
| 符号Symbol | 含义Meaning | 大小Size | 定义处Defined in |
|---|---|---|---|
| 区间数、间距 、节点坐标 、面坐标intervals, spacing , node coordinates , face coordinates | — | 第 2 节Section 2 | |
| 节点 的对偶控制体及其宽度(内部 ,两端 )dual volume of node and its width ( inside, at the ends) | 第 2 节Section 2 | ||
| 状态:节点上的点值state: the point values at the nodes | 第 2 节Section 2 | ||
| 设计:振幅、相位、黏性design: amplitude, phase, viscosity | 3 | 第 1 节Section 1 | |
| 积分残差(不除以 );第 、 行是边界约束integrated residual (not divided by ); rows and are the boundary constraints | 第 4、5 节Sections 4 and 5 | ||
| 面通量:对流部分加黏性部分face flux: convective plus viscous part | 第 3 节Section 3 | ||
| 迹、未重构状态、距离()、traces, raw states, distance (), | — | 第 3 节Section 3 | |
| 节点 的斜率与两个单侧差slope at node and the two one-sided differences | 第 3 节Section 3 | ||
| 对迹的偏导derivatives of with respect to the traces | 第 3 节Section 3 | ||
| 精确通量 与积分源项 exact flux and integrated source | 第 3 节Section 3 | ||
| 给定的两端值(只进入约束行)prescribed end values (they enter the constraint rows only) | 2 | 第 1、4 节Sections 1 and 4 | |
| 状态与设计 Jacobianstate and design Jacobians | 第 6、8 节Sections 6 and 8 | ||
| 离散目标与状态梯度;discrete objective and state gradient; | 第 2、7 节Sections 2 and 7 | ||
| 切线与设计种子tangent and design seed | 第 8、10 节Sections 8 and 10 | ||
| 伴随变量、两个约束行的乘子、设计伴随(梯度)adjoint variable, the multipliers of the two constraint rows, design adjoint (the gradient) | 第 9、11 节Sections 9 and 11 | ||
| 连续伴随场the continuous adjoint field | — | 第 1、11、12、16 节与附录 ASections 1, 11, 12 and 16 and Appendix A | |
| 网格 Péclet 数mesh Péclet number | — | 第 1、3 节Sections 1 and 3 |
下标的约定: 总是节点(以及它的对偶控制体 ), 总是面; 是节点 的坐标,面 在 ,夹在节点 与 之间( 时面 在 )。伪代码里的数组 s[j] 存斜率 ,它不是第 1 节的源项 。
Subscript convention: always names a node (and its dual volume ) and a face; is the coordinate of node , and face is at , between nodes and (at face is at ). In the pseudocode the array s[j] stores the slope ; it is not the source of Section 1.
默认网格上的数组()
The arrays on the default mesh ()
| 节点 Node | 坐标 Coordinate | 对偶控制体宽度 Dual-volume width | 目标权重 Objective weight | 精确节点值 Exact nodal value | 离散解 (无限制器线性)Discrete (unlimited linear) |
|---|---|---|---|---|---|
| 0 | 0.0000 | 0.0625 | 0.06250 | 0.91484 | 0.91484 |
| 1 | 0.1250 | 0.1250 | 0.14063 | 1.06775 | 1.07014 |
| 2 | 0.2500 | 0.1250 | 0.15625 | 1.18097 | 1.17081 |
| 3 | 0.3750 | 0.1250 | 0.17188 | 1.18818 | 1.16556 |
| 4 | 0.5000 | 0.1250 | 0.18750 | 1.08516 | 1.05739 |
| 5 | 0.6250 | 0.1250 | 0.20313 | 0.93225 | 0.91290 |
| 6 | 0.7500 | 0.1250 | 0.21875 | 0.81903 | 0.82184 |
| 7 | 0.8750 | 0.1250 | 0.23438 | 0.81182 | 0.83279 |
| 8 | 1.0000 | 0.0625 | 0.12500 | 0.91484 | 0.91484 |
第 行和第 行的 等于给定值 、(约束行使它们在舍入误差内精确),其余是通量平衡的解。权重 是第 7 节离散目标 的系数,也是状态梯度 (目标对状态的导数;伴随方程 的右端是 )。它们正是 的梯形公式的权重:每个节点按它的对偶控制体的宽度计数,两端是半宽(图 7)。
The values in rows and equal the prescribed , (the constraint rows make them exact up to rounding) and the others solve the flux balances. The weights are the coefficients of the discrete objective of Section 7 and also the state gradient (the right-hand side of the adjoint equation is ). They are exactly the weights of the trapezoid rule for : each node counts with the width of its dual volume, half of it at the two ends (Figure 7).
作用在精确节点值上, 就是 的梯形公式,它与 的差可以写出来:梯形公式的误差是 ,而 的导数在两端的差是 ( 以 1 为周期,只剩权重 的那一项):
Applied to the exact nodal values, is the trapezoid rule for , and its difference from can be written down: the error of the trapezoid rule is , and for the jump of the derivative between the two ends is ( has period 1, so only the part from the weight is left):
默认设置下 ,,,差 ,公式给出 (比值 1.0104)。这是 本身的离散误差:即使状态是精确的,离散目标也比 大约差这么多,它在第 15、16 节里作为梯度误差的一部分重新出现。(这个公式是用 Euler–Maclaurin 公式推出的,本页对它做了数值核对,没有给出严格证明。)
At the defaults , and ; the difference is and the formula gives (ratio 1.0104). This is the discretisation error of itself: even with an exact state the discrete objective differs from by about this much, and it reappears in Sections 15 and 16 as part of the gradient error. (The formula follows from the Euler–Maclaurin formula; the page checks it numerically and gives no rigorous proof of it.)
后面各节要用到的所有数组和它们的形状都在图 8 里:左边是向量,右边是矩阵(颜色表示正负与大小,蓝正橙负)。它们都是本页在默认设计 和 下真实算出的量; 是 的五对角矩阵,第 行与第 行是单位行(约束), 只有 3 列,所以一次伴随求解(右端项 )给出梯度的全部 3 个分量,而切线求解要做 3 次。
All the arrays that the later sections use, with their shapes, are in Figure 8: vectors on the left, matrices on the right (colour shows sign and size: blue positive, orange negative). They are real quantities of this page at the default design and : is an five-band matrix whose rows and are identity rows (the constraints), and has only 3 columns, so one adjoint solve (right-hand side ) gives all 3 design components while the tangent solves must be done 3 times.
面上的通量:对流、黏性与重构
The flux on a face: convection, viscosity and reconstruction
本节实验:选定一个面和一种重构,这个面读哪几个节点?四个量 里哪些恰好等于节点值?先预测,再用本节的“一个面的量”面板(以及第 2 节的“一个面、一个节点读哪些数据”面板)验证。
Experiment: Select a face and a reconstruction: which nodes does the face read, and which of the four quantities are nodal values? Predict first, then check with the panel “The quantities of one face” of this section (and “What a face and a node read” in Section 2).
第 2 节在区间上放好了节点和它们的对偶控制体。本节把方程写成对偶控制体上的通量差,用数值通量把面两侧的数据变成一个面通量(对流部分和黏性部分),再说明二阶格式怎样由节点值重构出面上的状态,以及这些公式精确满足的性质:耗散的恒等式、通量的相容性、导数与模板。
Section 2 placed the nodes and their dual volumes. This section writes the equation as a flux difference over a dual volume, turns the data on the two sides of a face into one face flux (a convective part and a viscous part), then shows how the second-order scheme reconstructs face states from the nodal values and what the formulas satisfy exactly: the identities of the dissipation, the consistency of the flux, the derivatives and the stencils.
从方程到通量差
From the equation to a flux difference
第 1 节的方程是 ,。在内部节点 ()的对偶控制体 上积分,它的两个面在 :由微积分基本定理,通量的导数变成两端通量之差,等于控制体内的总源项:
The equation of Section 1 is with . Integrate it over the dual volume of an interior node (), whose two faces are at : by the fundamental theorem of calculus the derivative of the flux becomes the difference of the end fluxes, which equals the total source in the volume:
对构造解,源项是 ,所以一个控制体里的总源项就是精确通量 在它两个面上的差,不需要数值积分;第 5 节的残差用的就是这个精确的积分源项。两个端点的控制体 、 不做平衡:它们的行是边界约束(第 4 节),所以本页的格式没有 、 处的通量。离散化要做的只剩一件事:用面两侧节点的数据近似每个面上的通量(图 9)。
For the manufactured solution the source is , so the total source of a volume is exactly the difference of the exact flux at its two faces, with no numerical quadrature; the residual of Section 5 uses this exact integrated source. The two end volumes , do no balance: their rows are the boundary constraints (Section 4), so the scheme has no flux through or . What remains for the discretisation is to approximate the flux at each face from the data of the nodes on its two sides (Figure 9).
图里有一个值得记住的事实:收敛解的每个内部控制体都满足 ,所以通量误差 在所有 个面上是同一个常数 (默认设置下 ,见第 5 节):平衡方程只确定通量的差,这个常数由两端的约束决定。精确节点值就不是这样(它们的面误差 随面变化,见本节末尾的图 14)。
The figure shows a fact worth remembering: every interior dual volume of the converged solution satisfies , so the flux error is one and the same constant on all faces ( at the defaults, Section 5): the balance equations fix only the differences of the flux, and the constraints at the two ends decide the constant. The exact nodal values do not behave like this (their face errors change from face to face, see Figure 14 at the end of this section).
下面的面板对任意内部节点 和当前设计重复这个计算:源项的积分、两个面上精确通量之差、数值通量,以及收敛解与精确节点值各自的残差。
The panel below repeats this computation for any interior node and the current design: the integral of the source, the difference of the exact flux at the two faces, the numerical fluxes, and the residuals of the converged solution and of the exact nodal values.
一个面上的数值通量:对流加黏性
The numerical flux on a face: convective plus viscous
面 的通量分两部分,用不同的输入:对流部分用面两侧重构出的状态 (下面叫“迹”),黏性部分用两侧节点的未重构状态 、 和它们之间的距离 :
The flux of face has two parts that use different inputs: the convective part uses the states reconstructed on its two sides (called traces below) and the viscous part uses the raw states , of the two nodes and the distance between them:
本页其他地方(包括本节开头的公式), 与 表示总通量;面上的重构状态沿用其他一维页面的写法 。
Elsewhere on this page, including the first display of this section, and denote the total flux; for the face states the page follows the other 1-D pages and writes .
对流部分是 Rusanov 通量:两侧物理通量 的算术平均,减去与状态跳跃成比例的耗散。耗散项里的波速 取固定值,不随状态或设计变化,所以求导时没有隐藏的波速项(通常的波速 在 处不可导,用它构成的 Rusanov 通量在 处不可导)。固定的 是否够大,要看迹的大小:在一次扫描里(、三种重构、、、,共 576 个组合),571 个收敛,收敛的状态里迹都满足 ,最大的是 1.669;其余 5 个是 上 的极端设计:页面的 Newton 迭代把状态限制在 内(第 5 节),没有收敛。去掉这个范围限制,从精确节点值出发(不行就换别的初始向量)的 Newton 迭代对这几个设计都找到了离散方程的解,最小值 是:一阶 0.0919,光滑限制器( 与 )−1.180 与 −1.035,都在这个范围之外(页面那时会报告求解失败)。Rusanov 通量满足相容性 ,并且同一个 离开左边的控制体 、进入右边的控制体 ,所以内部通量在全局求和中抵消。黏性部分是物理通量 的中心差分近似,距离 在每个面上都一样,两个边界面也不例外(格心页的边界面读给定值,距离是 )。
The convective part is the Rusanov flux: the mean of the physical fluxes of the two sides, minus a dissipation proportional to the jump of the state. The dissipation speed is a fixed number that depends on neither the state nor the design, so differentiation hides no wave-speed term (the usual wave speed is not differentiable where , and the Rusanov flux built with it is not differentiable where ). Whether the fixed is large enough depends on the size of the traces: in a scan (, the three reconstructions, , , : 576 combinations) 571 converged, and in every converged state the traces satisfy , the largest being 1.669; the other 5 are extreme designs with on for which the page's Newton iteration, which keeps the state inside (Section 5), did not converge. Without that box a Newton iteration, started from the exact nodal values or, where that fails, from other starting vectors, finds a solution of the discrete equations for each of them, with the smallest value equal to 0.0919 at first order and −1.180 and −1.035 with the smooth limiter ( and ), outside the box in every case (the page then reports a failed solve). The flux is consistent, , and the same leaves the volume on the left and enters the volume on the right, so the interior fluxes cancel in a global sum. The viscous part is the central-difference approximation of the physical flux , with the distance on every face, the two boundary faces included (on the cell-centred page a boundary face reads a prescribed value at the distance ).
耗散的真面目:精确恒等式
What the dissipation really is: exact identities
耗散项 是人工黏性,它的作用可以写成精确的恒等式(下面的面板在当前状态上再验算一遍)。一阶时迹等于节点值,内部行 的通量差 是
The dissipation term is artificial viscosity, and what it does can be written as exact identities (the panel below checks them again on the current state). At first order the traces equal the nodal values and the flux difference of an interior row , , is
这正是物理黏性 再加人工黏性 的中心格式(除以 后是 的中心差分):一阶格式在全部 个内部行上就是黏性 的中心格式,紧邻边界的第 行与第 行也不例外,因为边界节点的值 就是向量里的普通分量(格心页的第一行与最后一行读给定值、距离 ,不是这个形式)。默认 时 ,是 的 12.5 倍,等价地 ,网格 Péclet 数 ; 要小到与 同量级(这里 ),人工黏性才不再主导。无限制器线性重构时,在两侧节点都做了重构的面上(),耗散项是三阶差分:
This is exactly the central scheme for the physical viscosity plus an artificial viscosity (divided by it is the central difference of ): on all interior rows the first-order scheme is the central scheme for the viscosity , the first row and the last row included, because the boundary values are ordinary entries of the vector (on the cell-centred page the first and the last row read a prescribed value at the distance and do not have this form). At the default one has , that is 12.5 times ; equivalently with the mesh Péclet number ; has to become comparable with (about here) before the artificial viscosity stops dominating. With unlimited linear reconstruction, on a face whose two nodes are both reconstructed () the dissipation of the face is a third difference:
行 (,即两个这样的面之差)的耗散是四阶差分,对光滑数据是 :
and that of a row (the difference of two such faces) is a fourth difference, for smooth data:
两个边界面不同:边界节点用单侧斜率, 而 (最后一个面对称),所以它们的耗散是二阶差分,对光滑数据是 :
The two boundary faces are different: the boundary node uses a one-sided slope, and (the last face is symmetric), so their dissipation is a second difference, for smooth data:
于是第 行与第 行的耗散是 与 ,对光滑数据约为 ,除以 后相当于只在这两行加了人工黏性 (一阶格式在每一行都是 );其余各行只有四阶的超黏性。光滑限制器时权重随状态变化,没有这样的闭式。(上面每个恒等式都在随机状态和设计上用模型验算过,面板在当前解上再验算一遍。)
So the dissipation of the first row and the last row is and , about for smooth data, which after division by is an artificial viscosity acting on these two rows only (the first-order scheme has on every row); all other rows have only a fourth-order hyperviscosity. With the smooth limiter the weights change with the state and there is no such closed form. (Each identity above was checked with the model on random states and designs, and the panel checks it once more on the current solution.)
格心页的端单元不重构,面 上有一个迹是单元平均值,耗散约为 ,相当于 的人工黏性,是一阶量。本页没有这样的面:边界节点的单侧斜率只有一阶精度,但它只乘外推距离 —— 是线性插值,在 处的误差是 ——所以边界面的迹同样是二阶精确的,面上的跳跃只有 。图 10 在精确节点值与精确单元平均值上量出跳跃 ,以一阶跳跃 为单位:无限制器线性重构时,本页的边界面随 加倍缩小到约一半( 时 0.0142, 时 0.0007),格心页的边界面趋于 (0.507 与 0.500);内部面两页都是 。光滑限制器的情形见下面。
The cell-centred page does not reconstruct its end cells: on faces one trace is a cell mean and the dissipation is about , an artificial viscosity and a first-order quantity. This page has no such face: the one-sided slope of a boundary node is only first-order accurate, but it is multiplied by the extrapolation distance only, is a linear interpolation whose error at is , so the trace of a boundary face is second-order accurate too, and the jump across it is only . Figure 10 measures the jump on the exact nodal values and on the exact cell means, in units of the first-order jump : with unlimited linear reconstruction the boundary faces of this page shrink to about half when doubles (0.0142 at and 0.0007 at ), those of the cell-centred page tend to (0.507 and 0.500); on the interior faces both pages are . The smooth limiter is discussed below.
网格能分辨黏性尺度吗?
Does the mesh resolve the viscous scale?
第 1 节说过,伴随在入口 有一个宽 的层。网格 Péclet 数 就是网格间距与这个宽度之比。默认设计下层宽 ,而 的间距是 0.125,:第一个内部节点远在层外。 要 ,页面的网格里只有 做到;层里至少有 3 个间距()要 ,页面上没有这样的网格。 时 就够 。下面的面板让你换 和 ,比较网格间距与层宽:
Section 1 said that the adjoint has a layer of width at the inlet . The mesh Péclet number is the mesh spacing divided by this width. At the default design the layer width is while the spacing of is 0.125 and : the first interior node is far outside the layer. needs , and among the meshes of the page only achieves it; at least 3 intervals inside the layer () need , which the page does not offer. For , is enough for . The panel below lets you change and and compare the mesh spacing with the layer width:
重构:从节点值到面上的状态
Reconstruction: from nodal values to face states
每个节点用一个斜率 把自己的值重构成一条线段,线段在它的对偶控制体两端的值就是相邻面的迹。斜率由两个单侧差 、 决定( 是跨过一个间距的变化量,不除以 )。两个边界节点只有一个邻居,用单侧差,限制器只在两侧邻居都在的节点上起作用:
Each node is reconstructed as a line segment with one slope , and the values of the segment at the two ends of its dual volume are the traces of the neighbouring faces. The slope is determined by the two one-sided differences and ( is the change across one spacing, not divided by ). A boundary node has only one neighbour and uses the one-sided difference; the limiter acts only on nodes that have both neighbours:
公式中的 first、none、smooth 是三种重构在代码里的名字,分别对应控制面板中的“一阶”“无限制器线性(二阶)”“光滑限制器”。“none”是无限制器的中心斜率,二阶精度,但不保证单调。边界节点的斜率 只是一阶精确的,但它只乘外推距离 : 在 处的误差是 ,所以边界面的迹和内部面的迹一样是二阶精确的(格心页的端单元不重构,迹只有一阶精度)。
In the formula, “first”, “none” and “smooth” are the code names of the three reconstructions (First order, Unlimited linear (second order) and Smooth limiter in the control panel). The slope “none” is the unlimited central slope: second-order accurate but not monotone. The slope of a boundary node is only first-order accurate, but it is multiplied by the extrapolation distance only: has an error at , so the traces of the boundary faces are second-order accurate like those of the interior faces (the end cells of the cell-centred page are not reconstructed and their traces are only first-order accurate).
无限制器线性重构时,迹是未知量的固定线性组合:内部面 上 、;两个边界面一侧是 、,另一侧与内部面同形。所以一个内部面依赖 4 个未知量,紧邻边界的面 与 依赖 3 个( 与 ),一阶时每个面只依赖 2 个。一个内部节点的残差行用面 与面 :涉及 共 5 个未知量,第 行与第 行是 4 个(一阶是 3 个),第 行与第 行是约束,只含自己。默认网格上由页面的前向代码量出():无限制器线性每个面依赖 3、4、4、4、4、4、4、3 个未知量,每行 1、4、5、5、5、5、5、4、1 个;一阶每个面 2、2、2、2、2、2、2、2,每行 1、3、3、3、3、3、3、3、1。光滑限制器时这些权重随状态变化,依赖的未知量相同。于是状态 Jacobian 有 个非零元(二阶,默认网格上 35 个),一阶有 个(23 个);独立的 Python 程序在 到 的各次运行里数出的非零元个数与这两个公式一致。
With unlimited linear reconstruction the traces are fixed linear combinations of the unknowns: on an interior face one has and ; on the two boundary faces one trace is and and the other has the interior form. So an interior face depends on 4 unknowns, the faces and next to a boundary on 3 ( and ), and at first order every face depends on only 2. The residual row of an interior node uses faces and : it touches the 5 unknowns , the first row and the last row touch 4 (3 at first order), and rows and are constraints and contain only themselves. Measured with the page's tangent code on the default mesh (): with unlimited linear reconstruction the faces touch 3, 4, 4, 4, 4, 4, 4, 3 unknowns and the rows 1, 4, 5, 5, 5, 5, 5, 4, 1; at first order the faces 2, 2, 2, 2, 2, 2, 2, 2 and the rows 1, 3, 3, 3, 3, 3, 3, 3, 1. With the smooth limiter these weights change with the state and the unknowns touched are the same. So the state Jacobian has nonzeros at second order (35 on the default mesh) and at first order (23); the independent Python program counts the nonzeros of its runs from to and finds these two formulas.
先让模板动起来:下面的计算器给定一个内部面两侧的四个节点值,同时算出三种重构的迹、通量和四个偏导。只动最左边的值,哪些量会变?
First see the stencil in action: given the four nodal values around an interior face, the calculator below computes the traces, the flux and the four partial derivatives of all three reconstructions at once. Move only the left-most value: what changes?
再用滑块看斜率:给定两个单侧差,三种重构在中间节点的控制体里画出什么线段,再看边界节点:
Then look at the slope with the sliders: given the two one-sided differences, what segment does each reconstruction draw in the dual volume of the middle node; then look at a boundary node:
光滑限制器:极值处与细网格上
The smooth limiter: at extrema and on fine meshes
把光滑斜率写成中心斜率乘一个因子 (它不是设计的相位 ):,,因子严格落在 内(van Albada 型的光滑有理斜率, 使它处处可微)。 同号、相近且远大于 时 接近 1,斜率回到中心斜率;有一个差分为零时 ;在极值处() 为负,斜率很小并且与中心斜率反向,而不是被截成零。本页不声称它具有 TVD 或保正性。默认设计的光滑解里(),节点 6 是一个局部极小:,,中心斜率 ,光滑斜率 ,因子 。
Write the smooth slope as the central slope times a factor (not the phase of the design): with , a factor strictly inside (of van Albada type, smooth because of ). It is close to 1 when have the same sign, are close to each other and are much larger than , so the slope returns to the central one; it is 0 when one difference vanishes; at an extremum () it is negative, so the slope is small and points against the central slope instead of being cut to zero. The page claims no TVD or positivity property for it. In the smooth solution of the default design () node 6 is a local minimum: , , the central slope is , the smooth slope and the factor .
是固定的,而 随 缩小,所以网格细到 时因子远小于 1,重构几乎被关掉( 时因子仍是 , 时是 )。在精确节点值上,斜率最陡的节点处 ,降到 的网格是 (图 11): 时因子最大值是 0.885,(全局控件的最细网格)时是 0.658, 时只有 0.029, 时是 0.0019。因子一路下降,光滑限制器就一路变成一阶格式,状态误差跟着变:L1 误差在 处停止下降(0.00254,一阶是 0.0382),此后一直在 0.0025 与 0.0031 之间,直到 ( 时 0.00286),然后像一阶格式那样重新下降,到 时与一阶格式只差 0.31%(图 11c)。边界节点不受限制器影响,所以细网格上边界面的跳跃趋于一阶跳跃的一半,而紧邻它的面已经是一阶的(图 10);这对误差阶的影响由第 15 节测量。
is fixed while shrink with , so when the mesh is fine enough that the factor is far below 1 and the reconstruction is almost switched off (the factor is still at and at ). On the exact nodal values, at the node of the steepest slope , and the mesh at which this falls to is (Figure 11): the largest factor is 0.885 at , 0.658 at (the finest mesh of the global controls), only 0.029 at and 0.0019 at . As the factor falls, the smooth limiter degenerates to the first-order scheme, and its state error turns with it: the L1 error stops decreasing at (0.00254, against 0.0382 at first order), then stays between 0.0025 and 0.0031 up to (0.00286 at ) and then falls again like the first-order error, from which it differs by only 0.31% at (Figure 11c). The boundary nodes are not touched by the limiter, so on fine meshes the jump across a boundary face tends to half the first-order jump while the face next to it is already first order (Figure 10); what this does to the orders of the errors is measured in Section 15.
一个面的计算
The computation of one face
下面的播放器把这个面的计算拆成七步,每一步写出当时的数值;这七步就是第 5 节、第 8 节里残差代码的语句 1a 至 3(取值与重构 1a–1c,通量 2,分发 3),只是把迹和通量的两个部分分开显示。按“播放”或用步进按钮,换面、换重构再看一遍;面 与面 的分发只进一个控制体,因为两个端点控制体不装配。
The player below splits the computation of this face into seven steps and writes the numbers of each step; the seven steps are statements 1a to 3 of the residual code of Section 5 and Section 8 (gather and reconstruct 1a–1c, flux 2, scatter 3), with the traces and the two parts of the flux shown separately. Press Play or use the step buttons, and change the face and the reconstruction to see it again; the scatter of face and face enters one volume only, because the two end volumes are not assembled.
一个面上的局部导数
Local derivatives on one face
第 8、9 节的切线与伴随,在面上只用到三类局部导数:数值通量对迹的偏导、对未重构状态的偏导,以及迹对未知量的偏导(此外还有通量对设计 的偏导,以及边界值和精确通量对设计的偏导)。数值通量是多项式,逐项求导:
On a face, the tangent and the adjoint of Sections 8 and 9 use only three kinds of local derivative: those of the numerical flux with respect to the traces, those with respect to the raw states, and those of the traces with respect to the unknowns (plus the derivative of the flux with respect to the design and those of the boundary values and the exact fluxes with respect to the design). The numerical flux is a polynomial, so it is differentiated term by term:
有两个性质可以检验这些公式。相容性 沿 求导,得 :两个偏导把波速分成上游和下游两份。当 时 :通量随左状态增加、随右状态减小。默认设计的各面上(无限制器线性) 在 1.401 与 1.597 之间, 在 −0.595 与 −0.403 之间。黏性偏导 是常数,在每个面上(边界面也一样)。
Two properties check these formulas. Consistency differentiated along gives : the two partial derivatives split the wave speed into an upstream and a downstream share. For one has : the flux increases with the left state and decreases with the right one. On the faces of the default design (unlimited linear) lies between 1.401 and 1.597 and between −0.595 and −0.403. The viscous partials are constants, on every face, the boundary faces included.
迹对未知量的导数由斜率的导数决定: 与 。内部节点的斜率只依赖 ,偏导 、 是:一阶 ;无限制器线性 ,与状态无关;光滑限制器(记 ,):
The derivatives of the traces with respect to the unknowns follow from those of the slopes: and . The slope of an interior node depends on only, and its partial derivatives , are: at first order; with unlimited linear reconstruction, independent of the state; and with the smooth limiter (write , ):
边界节点的斜率是单侧差,导数是常数:、,、(无限制器线性与光滑限制器;一阶全为 0)。无限制器线性重构时迹的导数就是上面的固定权重;光滑限制器时它们随状态变化。图 13 左: 随状态变化,灰带是本页各面迹的范围;右:每个面的通量对每个未知量的导数 ,一阶时每个面涉及 2 个未知量,无限制器线性时内部面涉及 4 个、紧邻边界的面 3 个。
The slope of a boundary node is a one-sided difference and its derivatives are constants: , , , (unlimited linear and smooth limiter; all 0 at first order). With unlimited linear reconstruction the derivatives of the traces are the fixed weights above; with the smooth limiter they change with the state. Figure 13 left: against the state, the grey band being the range of the traces of this page; right: the derivative of every face flux with respect to every unknown, ; a face touches 2 unknowns at first order, and with unlimited linear reconstruction an interior face 4 and a face next to a boundary 3.
一个面上的数值(面 ,)
One face with the page's numbers (face , )
下表是三种重构各自的收敛解在同一个面上的量;每种重构的解不同,所以连未重构状态 也不同。验证:, 由迹算出。
The table lists the quantities of the same face in the converged solution of each reconstruction; the solutions differ, so even the raw states differ. Check: , and follow from the traces.
| 量Quantity | 公式Formula | 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter |
|---|---|---|---|---|
| 0.813368 | 0.994223 | 0.986457 | ||
| 0.716329 | 0.971783 | 0.942295 | ||
| 0.81337, 0.71633 | 1.05739, 0.91290 | 1.05090, 0.88952 | ||
| 0.390712 | 0.505651 | 0.509416 | ||
| 0.007763 | 0.011559 | 0.012911 | ||
| 0.398475 | 0.517211 | 0.522326 | ||
| 1.4067, −0.6418 | 1.4971, −0.5141 | 1.4932, −0.5289 |
注意一阶时两个迹等于节点值,对流部分的跳跃 较大,人工黏性起作用;无限制器线性时跳跃小得多。换一个面、换一种重构试试:
Note that at first order the traces equal the nodal values, the jump of the convective part is larger and the artificial viscosity acts; with unlimited linear reconstruction the jump is much smaller. Try another position and another reconstruction:
面通量的相容性
The consistency of the face flux
把精确节点值代进面通量,得到的 就是第 4 节的面误差( 是精确通量);一个控制体的残差是它两个面的面误差之差:(第 5 节)。无限制器线性重构时,所有 个面上 ,两个边界面也一样: 时最大的面误差在内部面()上是 ,在边界面 、 上是 ;从 到 的观测阶是 2.00 与 2.00。一阶时 ,是 (观测阶 1.00)。格心页的无限制器线性重构在内部面()上同样是二阶,但在两端的面 上只有一阶(观测阶 1.00, 时这些面上的误差是 ,是本页边界面误差的 53 倍)。
Put the exact nodal values into the face flux: is the face error of Section 4 ( is the exact flux), and the residual of a volume is the difference of the face errors of its two faces, (Section 5). With unlimited linear reconstruction on all faces, the two boundary faces included: at the largest face error is on the interior faces () and on the boundary faces , ; the observed orders from to are 2.00 and 2.00. At first order , which is (observed order 1.00). The unlimited linear reconstruction of the cell-centred page is second order on its interior faces () too, but only first order on its end faces (observed order 1.00; at the error on these faces is , 53 times that of the boundary faces of this page).
第 4 节写两个端点的约束行,第 5 节把面的计算写成残差代码。
Section 4 writes the two constraint rows at the ends; Section 5 writes the face computation as the residual code.
边界条件:约束行
Boundary conditions: the constraint rows
本节实验:边界节点的行不是通量平衡,而是约束。先预测:把精确节点值 代入残差,哪些行不为零?紧邻边界的行与内部行相差几阶 ?这样整个解的精度阶会降低一阶吗?写下预测,再看图 20。
Experiment: The rows of the two boundary nodes are not flux balances but constraints. Predict first: when the exact nodal values are put into the residual, which rows are not zero, and by how many powers of do the rows next to the boundary differ from the interior rows? Does the error of the whole solution then lose an order? Write your prediction down, then look at Figure 20.
第 3 节写出了一个面上的通量。格点格式里每个面的两侧都有节点,两个边界面也不例外,所以边界不需要新的通量公式。边界上改变的只有两件事:边界节点的两行变成约束,边界节点的斜率只能用单侧差分。本节说明这两个约束行对残差、对 和对 做了什么,边界面读什么、有多准,以及为什么边界不会让解的精度降阶。格心页的两个边界面只有一阶精度,这里的边界面与内部面同阶;两种格式的差别在本节一一量出。
Section 3 wrote the flux of a face. On the node mesh every face has a node on each side, the two boundary faces included, so the boundary needs no new flux formula. Only two things change at the boundary: the rows of the two boundary nodes become constraints, and the slope of a boundary node can only be a one-sided difference. This section says what the two constraint rows do to the residual, to and to , what the boundary faces read and how accurate they are, and why the boundary does not cost an order of accuracy of the solution. The two end faces of the cell-centred page are only first-order accurate; the boundary faces here are as accurate as the interior ones, and the section measures where the two schemes differ.
为什么两端各要一个值
Why each end needs a value
把定常方程 从 积到 :
Integrate the steady equation from to :
第一式说明通量只剩一个待定常数 ;第二式是 的一阶常微分方程,又带来一个积分常数。两个常数要两个条件:,。没有黏性()时第二式变成代数关系 ,只剩常数 ,入口给一个值就够了——这就是无黏 Burgers 页面只给入口条件的原因。对构造解 ,,即 。第 5 节会看到,离散解满足同样形式的关系,只是多出一个常数。
The first relation says that the flux is fixed up to one constant, ; the second is a first-order ordinary differential equation for , which brings a second constant of integration. Two constants need two conditions: and . Without viscosity () the second relation becomes the algebraic , only the constant is left, and one inlet value is enough: this is why the inviscid Burgers pages prescribe an inlet value only. For the manufactured solution one has , that is . Section 5 shows that the discrete solution satisfies a relation of the same form, with an extra constant.
两个约束行
The two constraint rows
未知量向量有 个分量,两个边界值 也在其中。它们的行不是通量平衡,而是说“这个未知量就等于给定值”:
The vector of unknowns has entries, the two boundary values and included. Their rows are not flux balances; they say that the unknown is the prescribed value:
每个面把它的通量加到两侧的对偶控制体上,但两个半宽的边界控制体 不装配(第 5 节的语句 3 跳过它们)。给边界控制体写通量平衡,只会算出穿过入口的通量,那是输出而不是约束; 的值本来就已经给定。
A face adds its flux to the dual volumes on its two sides, but the two half-width boundary volumes are not assembled (statement 3 of Section 5 skips them). A flux balance for a boundary volume would only compute the flux through the inlet, an output rather than a constraint; the value of is already given.
约束行对 的作用很简单:第 行和第 行是单位行,,其余元素全为零(图 15 左图的实线框)。所以 Newton 修正满足 :第一步之后 精确成立,因为约束行是线性的;本页的初始猜测在两端已经精确(它取精确节点值),所以每一步的修正在两端都是零。边界节点仍然是内部行的邻居:二阶重构时 出现在第 行, 出现在第 行(图 15 的虚线框;一阶重构时 只出现在第 行, 只出现在第 行),它们是 的“边界列”。第 6 节会证明, 因此是分块三角的,,其中 是去掉两个约束行和两个边界列后的内部块。
The effect on is simple: rows and are identity rows, and nothing else (the solid frames in the left panel of Figure 15). So the Newton correction satisfies : after the first step holds exactly, because the constraint row is linear; the initial guess of this page is already exact at both ends (it is the vector of exact nodal values), so every correction is zero there. The boundary nodes remain neighbours of the interior rows: at second order occurs in rows and in rows (the dashed frames of Figure 15; at first order occurs in row only and in row only), the “boundary columns” of . Section 6 shows that is therefore block triangular, with , where is the interior block that remains when the two constraint rows and the two boundary columns are removed.
对 的作用:边界数据只进入这两个约束行。,又因为 的周期为 1,,所以
The effect on : the boundary data enter these two rows and nowhere else. , and since has period 1, , so
没有 分量: 不依赖 ,边界数据也不依赖。第 行里不出现 本身,只出现状态 ;设计变量通过 (含 )和通量里的 进入这些行。(格心页相反:那里 在边界面通量里, 的第一行带着它。)两个约束行的乘子,也就是伴随方程在这两行上的分量 ,会在第 9、11 节登场。
There is no component: does not depend on , so neither do the boundary data. Rows do not contain or themselves, only the states ; the design enters them through (through ) and through in the flux. (On the cell-centred page it is the other way round: there sits inside the end-face flux, and the first row of carries it.) The multipliers of the two constraint rows, the components of the adjoint on these rows, appear in Sections 9 and 11.
边界面读什么
What the boundary faces read
面 在节点 与节点 之间,面 在节点 与节点 之间。它们与内部面完全相同:两侧各有一个节点,黏性差商的距离是 ,没有半个间距。唯一特殊的是边界节点的斜率。内部节点有两个邻居,边界节点只有一个,所以用单侧差分:二阶重构取 、;一阶格式所有节点的斜率都是零;光滑限制器需要两个邻居,所以边界斜率不加限制。格式不造虚拟节点。无限制器线性重构时:
Face lies between node and node , face between node and node . They are exactly like the interior faces: a node on each side and the distance in the viscous quotient, no half-interval distance. The only special thing is the slope of a boundary node. An interior node has two neighbours, a boundary node has one, so it uses a one-sided difference: at second order and ; at first order every slope is zero; the smooth limiter needs both neighbours, so the boundary slopes are not limited. The scheme invents no ghost node. For unlimited linear reconstruction:
于是面 读三个数 ( 是约束给定的值),面 读 ;其余内部面读四个。注意 就是两个节点的平均,也就是线性插值在面中点的值。
So face reads three numbers ( is the value the constraint fixes) and face reads ; the other faces read four. Note that is just the mean of the two nodes, the value of the linear interpolant at the face midpoint.
边界面通量与它的导数
The boundary-face flux and its derivatives
面 的通量就是第 3 节的 ,只是输入里有一个是边界值:
The flux of face is the of Section 3, with one boundary value among its inputs:
它的导数只需要第 3 节的局部导数 、、 与上面的迹对 的导数:
Its derivatives need only the local derivatives of Section 3, , , , and the derivatives of the traces above with respect to :
在默认设计(,无限制器线性):,,,,,,,,,,,,。第 8 节的前向代码以 (以及右端的 )和 为种子,得到的 与这些公式一致(无限制器线性;另两种重构与用各自迹权重(第 3 节)写出的同一链式法则一致;三种重构的最大差不超过 )。
At the defaults (, unlimited linear): , , , , , , , , , , , , . The forward code of Section 8, seeded with (and at the right end) and , returns equal to these formulas for unlimited linear (for the other two reconstructions, equal to the same chain rule with their trace weights of Section 3; the largest difference over all three is at most ).
这些导数去了 的哪里? 含 ,所以第 行的 各含一项 :黏性部分给 加上 ,加强了对角元。设计变量通过 进入 的第 行(还有 的导数)。
Where do these derivatives go in ? contains , so each contain a term : the viscous part adds to and strengthens the diagonal. The design enters row of through (and through the derivatives of and ).
试一试:调节 与 ,看边界面通量的两部分、它的三个偏导数和单侧斜率;按钮把 设成精确节点值或求解后的值,也可以切换重构。
Try it: adjust and and watch the two parts of the boundary-face flux, its three partial derivatives and the one-sided slope; the buttons set to the exact nodal values or to the solved values, and the reconstruction can be switched.
单侧斜率的代价
What the one-sided slope costs
设 光滑。把 在 处展开:
Let be smooth. Expanding about gives
边界节点的斜率只有一阶精度,主误差为 。但斜率进入面通量时要乘上 (),所以面上的迹仍是二阶的;黏性差商则与内部面一样,是中心差商:
The slope of the boundary node is only first-order accurate, with leading error . But it enters the face flux multiplied by (), so the trace at the face is still second-order accurate, and the viscous quotient is the same centred quotient as at an interior face:
单侧斜率留下的痕迹在 Rusanov 项里。内部面的两个迹互为镜像(把 在面中点展开:,),所以 是三阶小量。面 的 用内部斜率 , 用单侧斜率 ,对称性不再成立:
The one-sided slope leaves its mark in the Rusanov term. At an interior face the two traces are mirror images (expand about the face midpoint: , ), so is of third order. At face the trace uses the interior slope and the one-sided slope , and the symmetry is gone:
它比内部面大一阶,但仍然是二阶。下表把这五个量的展开主项与精确节点值上的实测对照()。
That is one order larger than at an interior face, but still second order. The table compares the leading terms of the expansions of these five quantities with what is measured at the exact nodal values ().
| 量Quantity | 除以divided by | 展开的主项leading term | 的实测measured at |
|---|---|---|---|
| 1.6791 | |||
| 0.4195 | |||
| −1.8710 | |||
| −0.8377 | |||
| 处的 at | −11.226 |
面误差
The face errors
对每个面定义面误差 :精确节点值 上的数值通量减去精确通量。把上面的展开代进通量 (对流部分 加黏性部分),在面中点处展开,无限制器线性重构得到:
For every face define the face error : the numerical flux at the vector of exact nodal values minus the exact flux. Putting the expansions above into the flux (the convective part plus the viscous part) and expanding about the face midpoint, unlimited linear reconstruction gives:
推导:内部面 ,黏性差商 ,Rusanov 项只有 ,减去 即得第一行。面 里 、,所以 的 项抵消,剩下的是 Rusanov 项 和黏性项。边界面的面误差与内部面同阶,,只是系数不同:在默认设计,内部面误差在两端的极限值是 ,面 的系数是 0.8592,面 的系数是 −0.8218( 的实测:0.8571 与 −0.8251)。一阶格式的所有面(包括两个边界面)都是 :迹就是节点值,边界面没有任何特殊之处。
Derivation: at an interior face , the viscous quotient is , the Rusanov term is only , and subtracting gives the first line. At face one has and , so the terms of cancel and what remains is the Rusanov term and the viscous term. The face errors of the boundary faces are of the same order as those of the interior faces, , only the coefficients differ: at the default design the limit of the interior face error at the two ends is , the coefficient of face is 0.8592 and that of face is −0.8218 (measured at : 0.8571 and −0.8251). At first order every face, the two boundary faces included, has : the traces are the nodal values and nothing is special at the boundary.
截断误差:边界行与内部行
The truncation error: boundary rows against interior rows
把精确节点值代入残差,看它离零有多远,这就是截断误差。两个约束行恒为零:,因为给定的就是精确值,约束行在边界上没有任何误差。其余各行的源项是精确的(第 5 节),所以残差就是相邻两个面误差的差:
Put the exact nodal values into the residual and see how far it is from zero: that is the truncation error. The two constraint rows vanish identically, , because the prescribed values are the exact ones: the constraint rows carry no error at all. The source of the other rows is exact (Section 5), so the residual is the difference of two neighbouring face errors:
无限制器线性重构时,面 的 是 的光滑函数,大小 ;相邻两项之差再小一阶,所以第 行是 。第 行和第 行把一个边界面和一个内部面相比:两者都是 ,系数不同,所以这两行是 ,系数分别是 与 ( 的实测:−1.2238 与 −0.4549)。因此紧邻边界的两行比内部行低一阶,但仍然 :用残差密度 (通常意义的局部截断误差)看,每一行都趋于零, 时 。格心页的端行不是这样:那里 与 趋于常数 和 ,端行在逐点意义上是不相容的。
With unlimited linear reconstruction the of faces are a smooth function of of size ; the difference of two neighbours is one power of smaller, so rows are . Rows and compare a boundary face with an interior face: both are with different coefficients, so these two rows are , with the coefficients and (measured at : −1.2238 and −0.4549). So the two rows next to the boundary are one order less accurate than the interior rows, but still : in the residual density , the usual local truncation error, every row tends to zero, at . The end rows of the cell-centred page are not like this: there and tend to the constants and , and the end rows are inconsistent in the pointwise sense.
一阶格式没有这种区别:所有面都有 ,所以每个内部行(包括第 行和第 行)都是 。 时最大的内部行除以 是 7.8957(),第 行是 −3.3517()。光滑限制器在细网格上有一点特别: 固定,内部斜率的两个差分 ,限制器把内部斜率压到几乎为零,内部面于是像一阶面;而边界斜率是单侧的、不加限制,不会被压掉,所以边界面仍像带斜率的二阶面。两种面的误差差一个 :,( 的实测:−0.8284 与 0.3071)。所以光滑限制器在细网格上的两个边界行是 ,而内部行是 ;这是对极限情形的推导加实测,不是定理。
The first-order scheme has no such difference: every face has , so every interior row, rows and included, is . At the largest interior row divided by is 7.8957 () and row gives −3.3517 (). The smooth limiter has one peculiarity on fine meshes: is fixed, the two differences of an interior slope, , make the limiter squeeze the interior slopes to almost zero, so the interior faces behave like first-order faces; the boundary slopes are one-sided and unlimited and are not squeezed, so the boundary faces still behave like second-order faces with a slope. The face errors then differ by : and (measured at : −0.8284 and 0.3071). So with the smooth limiter on fine meshes the two boundary rows are and the interior rows ; this is a derivation of the limiting case plus measurement, not a theorem.
为什么边界行不会拖累精度阶
Why the boundary rows do not drag the order down
通量常数。 的第 行说 :面上的通量失配在全部 个面上是同一个常数。
The flux constant. Rows of say : the flux mismatch of a face is the same constant on all faces.
是一个常数(下标 H 表示通量,与第 3 节的偏导 无关)。 个方程确定 个内部未知量, 是离散问题多出来的那一个常数:数值解携带的是精确通量加一个常数(第 5 节再回到这一点)。默认设计():无限制器线性的 是 −4.81e−3,一阶是 −1.24e−1,光滑限制器是 3.08e−4(各面的失配与它相差不超过 )。加密时 ,但并不单调:无限制器线性的 变号 2 次,在 与 之间(−4.8e−3 到 1.6e−3)、在 与 之间(4.9e−7 到 −4.8e−7), 时是 −1.7e−8。所以在格点格式里, 不是决定误差大小的量。
is one constant (the subscript H stands for the flux; it has nothing to do with the partial derivatives of Section 3). equations fix the interior unknowns, and is the one constant the discrete problem leaves over: the numerical solution carries the exact flux plus a constant (Section 5 returns to this). At the defaults (): is −4.81e−3 for unlimited linear, −1.24e−1 for first order and 3.08e−4 for the smooth limiter (the mismatch of every face is within of it). Under refinement , but not monotonically: for unlimited linear it changes sign twice, between and (−4.8e−3 to 1.6e−3) and between and (4.9e−7 to −4.8e−7), and is −1.7e−8 at . So on the node mesh is not what sets the size of the error.
误差方程。设 。两个约束行都精确满足,所以 ;所有面上 :误差引起的通量增量,等于面误差取负号再加常数 。对一阶和无限制器线性重构, 是 的二次多项式(第 5 节),而二次函数的割线 Jacobian 就是它在中点的 Jacobian,所以精确地有 ,其中 。取第 行(,):
The error equation. Let . Both constraint rows hold exactly, so , and on every face : the flux increments caused by the error are the face errors with the sign changed, plus the constant . For first order and unlimited linear reconstruction is a quadratic polynomial in (Section 5), and the secant Jacobian of a quadratic function is its Jacobian at the midpoint, so exactly with . Taking rows (, ):
这是恒等式,不是近似(一阶与无限制器线性):实测 到 1024,由它解出的 与实际的 之差不超过 。光滑限制器的残差不是多项式,没有这个恒等式。把 记作 (在 之外取零),逐行写开,节点 的误差是各面误差的加权和,权是 相邻元素之差:
This is an identity, not an approximation (first order and unlimited linear): measured for to 1024, the it determines differs from the actual by at most . The residual of the smooth limiter is no polynomial and has no such identity. Writing for (zero outside ) and spelling the system out, the error at node is a weighted sum of the face errors, with weights the differences of neighbouring entries of :
只由矩阵决定:它是 与“相邻面误差之差”这个差分算子的复合在最大范数下的范数。误差由面误差本身控制,而不是由行的截断误差控制:行的截断误差是面误差的差,差分算子把它变小了一阶,而 把这一阶还给了它。
depends on the matrix only: it is the max-norm of composed with the difference of neighbouring face errors. The error is controlled by the face errors themselves, not by the truncation errors of the rows: the truncation error of a row is a difference of face errors, which makes it one power of smaller, and gives that power back.
紧邻边界的节点。面 读 ,,所以它的关系只含 (同样是精确的,用中点状态 上的 )。一阶重构时只含 :
The node next to the boundary. Face reads and , so its relation contains only (again exact, with at the midpoint state ). At first order it contains only:
这是格心页“除以一个大分母”论证的节点版本:面误差 除以一个随 增大的分母。 时分母约为 ,于是 :紧邻边界的节点特别准。 时 是 1.679e−9,而 是 1.666e−9(比值 1.0078)。两个关系式对 到 4096 都精确成立(两边之差不超过 )。但它不是精度阶的来源:在 小的粗网格上( 时 只有 0.08), 是 2.39e−3,并不比别的节点大( 是 2.78e−2)。整体误差由稳定性决定。
This is the node version of the “division by a large denominator” of the cell-centred page: the face error is divided by a denominator that grows with . For the denominator is about , so : the node next to the boundary is particularly accurate. At , is 1.679e−9 while is 1.666e−9 (ratio 1.0078). Both relations hold exactly for to 4096 (the two sides differ by at most ). But it is not the source of the order: on a coarse mesh with small ( is only 0.08 at ) one finds equal to 2.39e−3, which is no larger than the errors elsewhere ( is 2.78e−2). The error of the whole solution is set by stability.
稳定性:一阶格式的证明。设 是 的位于 Newton 范围 内的解(所有算出的解都是)。因为默认设计里 且 ,所以 ,于是对任何 都有 且 。一阶重构时每个面有 ,其中 ,,且 。于是 。从出口向入口解():
Stability: a proof at first order. Let be a solution of inside Newton's box (all computed solutions are). At the default design and , which gives , so and for every . At first order every face has with and , and . So . Solving from the outlet () towards the inlet:
所有的权都是正的。在 处,入口条件 表明 是各 的加权平均(权为正),所以 ,。权之和 满足 ,从 起归纳():
All weights are positive. At the inlet condition says that is a weighted mean of the with positive weights, so and . The sum of the weights satisfies , and induction from gives (with ):
于是 ,即
Hence , that is
这里完全没有用到边界的任何特殊之处,只用了 :一阶格式的 在所有面上成立,所以解的误差是 。数值检验: 到 1024,所有的权为正(最小的权也大于零),加权平均给出的 与面通量失配的平均相差不超过 ,证明里的上界 成立:0.2718 对 0.3953(),2.46e−3 对 3.07e−3(); 在 0.7741 到 0.7994 之间,接近 ,所以常数 约为 2.502 到 2.584;对任何 它都小于 (算出的解的 在 0.77 与 1.20 之间)。
Nothing special about the boundary was used, only : at first order holds on every face, so the error of the solution is . Numerical check, to 1024: all weights are positive (the smallest is still above zero), the given by the weighted mean differs from the mean face mismatch by at most , and the bound of the proof holds: 0.2718 against 0.3953 (), 2.46e−3 against 3.07e−3 (); lies between 0.7741 and 0.7994, close to , so the constant is about 2.502 to 2.584; for every it is below (the of the computed solutions lies between 0.77 and 1.20).
稳定性:无限制器线性只是数值观察。五对角矩阵没有这种符号结构(第 6 节),上面的证明不适用, 要直接算。实测(精确逆矩阵, 到 1024): 对一阶(至多 2.517)、无限制器线性(至多 3.429)和光滑限制器(在解处线性化,至多 3.568)都有界,并且都趋于约 ,也就是 (无限制器线性: 时 2.4981;一阶 2.4997)。 有界与 在所有 个面上成立(上面的展开)合在一起,给出无限制器线性的 :展开是推导, 有界是实测,不是定理。下表是无限制器线性的几个 。
Stability: an observation for unlimited linear. The five-diagonal matrix has no such sign structure (Section 6), the proof above does not apply, and has to be computed. Measured (exact inverse, to 1024): is bounded for first order (at most 2.517), unlimited linear (at most 3.429) and the smooth limiter (linearised at the solution, at most 3.568), and tends to about in all three, the value (unlimited linear: 2.4981 at ; first order 2.4997). A bounded together with on all faces (the expansions above) gives for unlimited linear: the expansions are a derivation, the boundedness of is a measurement, not a theorem. The table lists a few for unlimited linear.
| 8 | 2.94e−2 | −3.95e−3 | −2.94e−2 | −4.81e−3 | 2.39e−3 | 2.78e−2 | 1.01e−1 |
| 32 | 1.19e−3 | 5.67e−4 | −1.19e−3 | 5.82e−4 | 8.18e−4 | 1.79e−3 | 3.32e−3 |
| 128 | 7.23e−5 | 4.83e−5 | −5.66e−5 | 1.6e−5 | 3.42e−5 | 8.02e−5 | 1.81e−4 |
| 512 | 4.52e−6 | 3.21e−6 | −3.24e−6 | −4.8e−7 | 7.7e−7 | 4.16e−6 | 1.13e−5 |
下面的面板把整件事摆在一起:选择 与重构,看面误差 、常数 、实际误差 、由 算出的线性响应,以及上界 ;一阶还给出证明里的 。
The panel below puts it all together: choose and the reconstruction and see the face errors , the constant , the actual error , the linear response computed from , and the bound ; at first order it also shows the of the proof.
光滑限制器。 同样有界,但 固定,限制器在细网格上把内部斜率压掉,内部面误差只剩一阶(上面的 与内部行说明了同一件事),解的误差也就是一阶: 时 是 5.03e−3、6.5e−3、6.27e−3,几乎不下降,只有 才随 一阶下降(最后三次加密的观测阶:0.85、0.95、0.98)。固定 时,光滑限制器在细网格上不是二阶格式。
The smooth limiter. is bounded here too, but is fixed, the limiter squeezes the interior slopes on fine meshes, the interior face errors are only first order (the and interior rows above say the same), and so is the error of the solution: is 5.03e−3, 6.5e−3 and 6.27e−3 at , hardly falling, and only from does it fall like (observed orders of the last three doublings: 0.85, 0.95, 0.98). With fixed, the smooth limiter is not a second-order scheme on fine meshes.
与格心页对照。格心页同一个实验里(除另有说明外都是无限制器线性重构),两个边界面通量只有一阶(半格差商 ,端单元不重构):,第 行与第 行的残差密度趋于常数( 时 ,),解的误差靠“面误差除以一个与 有关的分母”和 来控制, 时相邻两次加密的观测阶只在 1.15 到 1.52 之间,直到 才接近 2。对无限制器线性,格点格式里全部 个面的误差都是 ,所有行的残差密度都趋于零,解的误差从粗网格起就是二阶:相邻两次加密的观测阶, 起是 1.83 到 2.36(:1.98)。同一网格上,对无限制器线性,格点格式的 比格心格式的 小得多: 时 2.78e−2 对 9.21e−2(3.3 倍), 时 3.95e−4 对 8e−3(20.2 倍), 时 7.31e−8 对 6.59e−6(90 倍)。这只对无限制器线性成立:一阶时格点格式在每个 上的误差都更大( 时 2.72e−1 对 1.93e−1, 时 6.15e−4 对 6.13e−4);光滑限制器的优势( 时 3.2 倍)到 时只剩 1.02 倍。(两个格式的未知量不同:格心的是单元平均,节点的是点值;二者各自对照自己的精确参考,比较的是各自的离散误差。)
Against the cell-centred page. In the same experiment on the cell-centred page (unlimited linear reconstruction unless another is named) the two end-face fluxes are only first order (the half-cell quotient is and the end cells are not reconstructed): , the residual densities of rows and tend to constants ( and at ), the error of the solution is controlled through “the face error divided by a denominator that depends on ” and , and for the observed orders of consecutive doublings lie only between 1.15 and 1.52, approaching 2 only for . For unlimited linear, on the node mesh the errors of all faces are , the residual densities of all rows tend to zero, and the error of the solution is second order from the coarse meshes on: the observed orders of consecutive doublings, from on, lie between 1.83 and 2.36 (1.98 for ). On the same mesh, for unlimited linear, the of the node scheme is much smaller than the of the cell scheme: 2.78e−2 against 9.21e−2 at (3.3 times), 3.95e−4 against 8e−3 at (20.2 times) and 7.31e−8 against 6.59e−6 at (90 times). This holds for unlimited linear only: at first order the node scheme has the larger error at every (2.72e−1 against 1.93e−1 at , 6.15e−4 against 6.13e−4 at ), and with the smooth limiter the advantage (3.2 times at ) has shrunk to 1.02 times at . (The unknowns differ, cell averages against point values; each scheme is compared with its own exact reference, so the comparison is between the two discretisation errors.)
独立 Python 程序的实测。独立的稀疏程序 python/solve_node.py 在 到 16384 上量过同一个残差(精确节点值代入,第 行与第 行的最大值),结论相同:下表取其中几个 。无限制器线性:边界相邻行 ,内部行 ;一阶:所有行 (残差密度 是 );光滑限制器( 固定):内部行 ,但紧邻边界的行只是 ,而且 时还不缩小。
Measured by the independent Python program. The independent sparse program python/solve_node.py measured the same residual at to 16384 (exact nodal values put in; the maximum over rows and over rows ) and finds the same: the table takes a few . Unlimited linear: rows next to the boundary , interior rows ; first order: every row (the residual density is ); smooth limiter ( fixed): interior rows , but the rows next to the boundary only , and for they do not shrink yet.
| 重构,行reconstruction, rows | ||||
|---|---|---|---|---|
| 一阶:行 1 与 N−1first order: rows 1 and N−1 | 4.64e−3 | 5.42e−5 | 8.07e−7 | 1.25e−8 |
| 一阶:行 2 … N−2first order: rows 2 … N−2 | 7.67e−3 | 1.20e−4 | 1.88e−6 | 2.94e−8 |
| 无限制器线性:行 1 与 N−1unlimited linear: rows 1 and N−1 | 1.05e−3 | 1.84e−5 | 2.92e−7 | 4.56e−9 |
| 无限制器线性:行 2 … N−2unlimited linear: rows 2 … N−2 | 2.28e−4 | 3.98e−7 | 7.75e−10 | 1.5e−12(此网格上受舍入限制)1.5e−12 (rounding-limited at this size) |
| 光滑限制器:行 1 与 N−1smooth limiter: rows 1 and N−1 | 2.00e−3 | 2.36e−3 | 4.03e−4 | 5.06e−5 |
| 光滑限制器:行 2 … N−2smooth limiter: rows 2 … N−2 | 5.39e−3 | 1.20e−4 | 1.88e−6 | 2.94e−8 |
最后一次加密()的观测阶:无限制器线性 2.00(边界相邻行)与 3.00(内部行),一阶 2.01 与 2.00,光滑限制器 1.00 与 2.00。光滑限制器的边界相邻行在 上是 2.0e−3、2.0e−3、2.6e−3、2.4e−3、1.5e−3:先不下降, 才开始按 下降。 时无限制器线性内部行的存储值 1.58e−12 受舍入限制(黏性差商是节点值之差除以 ),表中给出的 1.5e−12 是从 起按 律延续的值,独立的 50 位精度计算与它一致(到印出的位数)。与本页自己的计算比较:两者在 到 4096 上的相对差别不超过 (到所有印出的位数都相同)。
Observed orders over the last doubling (): unlimited linear 2.00 (rows next to the boundary) and 3.00 (interior rows), first order 2.01 and 2.00, smooth limiter 1.00 and 2.00. The rows next to the boundary of the smooth limiter are 2.0e−3, 2.0e−3, 2.6e−3, 2.4e−3, 1.5e−3 at : first they do not fall, and only from do they fall like . At the stored value of the interior rows of unlimited linear, 1.58e−12, is limited by rounding (the viscous quotient divides differences of nodal values by ); the table gives 1.5e−12, the law continued from , which an independent evaluation with 50 digits confirms to the digits printed. Against this page's own computation: the relative difference is at most for to 4096 (the two agree in every printed digit).
两处容易误读。第一,紧邻边界的两行 比内部行低一阶,并不说明边界拖累了精度:解的误差由面误差通过有界的 控制,而不是由行的截断误差控制; 是一行 的误差, 个内部行各是 ,它们对解的误差贡献同一个量级 。第二,粗网格上的观测阶还不是渐近阶: 时 比极限 大, 的数字不要当作格式的阶(第 15 节系统地测量阶)。
Two things are easily misread. First, that the two rows next to the boundary, and , are one order less accurate than the interior rows does not mean the boundary drags the accuracy down: the error of the solution is controlled by the face errors through the bounded , not by the truncation errors of the rows; is one row with an error , the interior rows are each , and they contribute the same order to the error of the solution. Second, the observed orders on coarse meshes are not yet asymptotic: at is above its limit , and the numbers for should not be taken for the order of the scheme (Section 15 measures the orders systematically).
用加密播放器一步步看:每一步 加倍,左图是截断误差的分布 ,中图是面误差 ,右图是解的误差 。
Step through it with the refinement player: each step doubles ; the left panel is the distribution of the truncation error , the middle panel the face errors , the right panel the error of the solution .
残差与 Newton 求解
The residual and the Newton solve
本节实验:第 4 节说,精确节点值上各面的通量误差 并不相等。那么解满足 时,各个面通量 满足什么?先预测,再看图 23。第二问: 时 Newton 法要迭代几次?残差是二次收敛的吗?
Experiment: Section 4 says that the face errors at the exact nodal values are not equal. What, then, do the face fluxes satisfy once holds? Predict, then look at Figure 23. Second question: how many Newton iterations does need, and does the residual fall quadratically?
第 3、4 节给出了每个面的通量和两个约束行。本节把它们装配成残差(它的零点就是离散解),说明“解出来了”是什么意思,再用 Newton 法解 。
Sections 3 and 4 gave the flux of every face and the two constraint rows. This section assembles them into the residual, the function whose zero is the discrete solution, says what “solved” means, and solves by Newton's method.
残差代码
The residual code
残差由一个面循环算出:每个面只访问一次,通量只算一次,然后分发给面两侧的两个节点(左节点加、右节点减),最后用两个约束行覆盖边界节点的行。下表是本页 model.js 中 residual 的语句,标号 0、1a、1b、1c、2、3、4a、4b、5 与第 8、9 节的前向代码和反向代码一致(反向代码以相反的次序执行同样的语句,标号加撇:):
The residual is computed by one loop over the faces: each face is visited once, its flux is computed once and scattered to the two nodes on its two sides (plus to the left node, minus to the right node), and finally the two constraint rows overwrite the rows of the boundary nodes. The table lists the statements of residual in this page's model.js; the labels 0, 1a, 1b, 1c, 2, 3, 4a, 4b, 5 are those of the forward and reverse codes of Sections 8 and 9 (the reverse code runs the same statements in the opposite order, with a prime on the label: ):
| # | 语句Statement | 它是什么What it is |
|---|---|---|
| 0 | 装配之前把残差置零the zeroing before the assembly | |
| 1a | 内部节点上的两个单侧差分the two one-sided differences at the interior nodes | |
| 1b | ;,(一阶:); , (first order: ) | 第 3 节的斜率;两个端点节点用单侧差分the slopes of Section 3; the two end nodes use one-sided differences |
| 1c | 面 的两个迹(重构)和两个未重构状态the two traces (reconstructed) and the two raw states of face | |
| 2 | ,, | 第 3 节的面通量the face flux of Section 3 |
| 3 | 分发:加到左边的节点、从右边的节点减去;两个边界面各只作用于一个内部节点the scatter: plus to the left node, minus to the right node; each of the two boundary faces acts on one interior node only | |
| 4a | 各个面上的精确通量the exact flux of every face | |
| 4b | 精确的积分源项(下面)the exact integrated source (below) | |
| 5 | 第 4 节的约束行,覆盖两个边界行the constraint rows of Section 4, overwriting the two boundary rows |
residual(U, D) -> res U = (U[0], ..., U[N]), D = (a, phi, nu)
0 res = 0; s = 0 (一阶:s[0] = s[N] = 0 保持不变)
for j = 1 .. N-1:
1a l = U[j] - U[j-1]; r = U[j+1] - U[j]
1b s[j] = sigma(l, r) 第 3 节的 sigma_j(不是源项 s(x))
1b s[0] = U[1] - U[0]; s[N] = U[N] - U[N-1] 二阶:端点的单侧斜率
for f = 0 .. N-1:
1c qL = U[f] + s[f]/2; UL = U[f]
qR = U[f+1] - s[f+1]/2; UR = U[f+1]
2 d = h; H = (qL^2 + qR^2)/4 - (qR - qL) - nu*(UR - UL)/d
3 if f >= 1: res[f] += H
if f <= N-2: res[f+1] -= H
for f = 0 .. N-1:
4a Q[f] = q*(x_{f+1/2}; D)
for j = 1 .. N-1:
4b res[j] -= Q[j] - Q[j-1]
5 uin = u*(0; D); uout = u*(1; D)
res[0] = U[0] - uin; res[N] = U[N] - uout
return resresidual(U, D) -> res U = (U[0], ..., U[N]), D = (a, phi, nu)
0 res = 0; s = 0 (s[0] = s[N] = 0 stay at first order)
for j = 1 .. N-1:
1a l = U[j] - U[j-1]; r = U[j+1] - U[j]
1b s[j] = sigma(l, r) sigma_j of Section 3 (not the source s(x))
1b s[0] = U[1] - U[0]; s[N] = U[N] - U[N-1] second order: one-sided end slopes
for f = 0 .. N-1:
1c qL = U[f] + s[f]/2; UL = U[f]
qR = U[f+1] - s[f+1]/2; UR = U[f+1]
2 d = h; H = (qL^2 + qR^2)/4 - (qR - qL) - nu*(UR - UL)/d
3 if f >= 1: res[f] += H
if f <= N-2: res[f+1] -= H
for f = 0 .. N-1:
4a Q[f] = q*(x_{f+1/2}; D)
for j = 1 .. N-1:
4b res[j] -= Q[j] - Q[j-1]
5 uin = u*(0; D); uout = u*(1; D)
res[0] = U[0] - uin; res[N] = U[N] - uout
return res语句 1c 里的面数据()就是第 3 节的取值与重构,语句 2 是第 3 节的通量(所有面的 ),语句 3 的两个 if 让两个边界面各只作用于一个内部节点(面 只从 减去,面 只加到 ),语句 4a、4b 是下面的精确源项,语句 5 是第 4 节的约束行。model.js 在一个节点循环里算斜率(1a、1b),在一个面循环里算出所有面的数组(1c、2),用第二个面循环按同样的顺序分发(3),再逐节点减去源项(4a、4b),最后写两个约束行(5);浮点运算的次序完全相同,所以下面的播放器按这段伪代码重放,得到的 与 model.js 逐位相同(已验证)。
The face data of statement 1c () are the gathering and reconstruction of Section 3, statement 2 is the flux of Section 3 ( for every face), the two ifs of statement 3 make each boundary face act on one interior node only (face only subtracts from , face only adds to ), statements 4a and 4b are the exact source below, and statement 5 is the constraint rows of Section 4. model.js computes the slopes in a node loop (1a, 1b), the arrays of all faces in a face loop (1c, 2), scatters them in a second face loop in the same order (3), subtracts the source node by node (4a, 4b) and writes the two constraint rows last (5); the floating-point operations happen in the same sequence, so the player below, which replays this pseudocode, gets the same as model.js bit for bit (verified).
精确的积分源项
The exact integrated source
节点 的残差是 在它的对偶控制体 上的积分。通量部分给出 (面 在它的右边,面 在它的左边);源项部分,因为构造时就令 (第 1 节),由微积分基本定理得
The residual of node is the integral of over its dual volume . The flux part gives (face is its right boundary, face its left boundary); for the source part, since by construction (Section 1), the fundamental theorem of calculus gives
这里不需要求积公式,对任何 都精确;两个边界节点的行是约束,不带源项。验算:在每个对偶控制体上用 20 点 Gauss 求积积分 ,与 的最大差不超过 ()。所以残差唯一的误差来源是数值通量,第 4 节才能写出 。
No quadrature rule is needed, and the source is exact for every ; the rows of the two boundary nodes are constraints and carry no source. Check: integrating over every dual volume with 20 Gauss points gives to within (). The numerical flux is therefore the only source of error in the residual, which is why Section 4 could write .
守恒:把第 行加起来,内部面的通量成对抵消,只剩两个边界面:
Conservation: adding rows , the fluxes of the interior faces cancel in pairs and only the two boundary faces remain:
这是并集 的通量平衡,穿过它两端的就是面 和面 (两个约束行不参与)。在 Newton 初值(精确节点值,)处,无限制器线性:等式两边都是 −0.025441574669;一阶是 0.057130192519,光滑限制器是 −0.062373925744(两边相同)。
This is the flux balance of the union : what crosses its two ends is face and face (the two constraint rows do not take part). At the Newton initial guess (the exact nodal values, ), unlimited linear: both sides are −0.025441574669; first order gives 0.057130192519 and the smooth limiter −0.062373925744 (the two sides agree).
“解出来了”是什么意思: 等价于各面通量误差相等
What “solved” means: makes the flux error one constant
第 行全为零,就是 对 都成立:全部 个面上的通量误差都相同。
Rows all vanishing says for : the flux mismatch is the same on all faces.
这里 是同一个常数(下标 表示通量,它与第 3 节的偏导 无关)。连续解的通量就是 (第 4 节开头);离散解的通量是精确通量加一个常数 : 个方程定下 个内部未知量,剩下的这个常数就是边界留下的痕迹(两个约束行不在其中:它们只定下 )。格心页是 个面、 个方程,这里是 个面、 个方程。默认设计(,取各面平均):无限制器线性的 是 −0.004807,一阶是 −0.123543,光滑限制器是 0.000308,各面与它之差不超过 。加密后 : 时无限制器线性是 −1.72e−8,一阶是 −2.85e−4(第 4 节列出了无限制器线性的 的两次变号:它不单调)。
Here is one and the same constant (the subscript stands for the flux; it has nothing to do with the partial derivatives of Section 3). The continuous solution has the flux (beginning of Section 4); the discrete solution has the exact flux plus a constant : equations fix the interior unknowns, and the constant that is left over is what the boundary leaves behind (the two constraint rows are not among them: they only fix and ). The cell-centred page has faces and equations, here there are faces and equations. At the defaults (, the mean over the faces) is −0.004807 for unlimited linear, −0.123543 for first order and 0.000308 for the smooth limiter, every face within of it. Under refinement : −1.72e−8 at for unlimited linear, −2.85e−4 for first order (Section 4 lists the two sign changes of of unlimited linear: it is not monotone).
实际中“解出来了”指 Newton 修正 (或 )。残差本身降不到舍入误差的底线以下:每个 是两个约 的通量之差,舍入误差约 到 。
In practice “solved” means a Newton correction (or ). The residual itself cannot fall below its rounding floor: each is a difference of fluxes of about , with rounding errors of about to .
下面的播放器一次执行一条语句:选择 Newton 初值、收敛解或精确节点值,看每个面的取值、通量和分发怎样一步步把 堆起来。
The player below executes the pseudocode one statement at a time: choose the Newton initial guess, the converged solution or the exact nodal values, and watch the gather, the flux and the scatter of each face build up step by step.
Newton 法,就是 model.solve 的做法
Newton's method as model.solve does it
线性化(,第 6 节):
Linearise (, Section 6):
令线性近似 为零,得到 Newton 修正 。本页的求解器 solve 逐条语句如下:
Setting the linear approximation to zero gives the Newton correction . The page's solver solve is, statement by statement:
solve(D):
U = (u*(x_0), ..., u*(x_N)) 精确节点值:只是初始猜测(R_0 = R_N = 0 已经成立)
for k = 0, 1, ..., 34:
R = residual(U, D); A = R_U(U, D) (N+1 次前向求值,第 6 节)
解 A dU = -R (LU;dU[0] = dU[N] = 0:约束行已经满足)
记录 ||R||_inf, ||dU||_inf
if ||dU||_inf <= 1e-13 or ||R||_inf < 1e-15: return U 已收敛(dU 不再加上)
t = 1
至多 25 次:
Ut = U + t dU
if 0.1 < min Ut, max Ut < 1.9 and ||R(Ut, D)||_inf < (1 - 1e-4 t) ||R||_inf:
U = Ut; 转到下一个 k
t = t/2
return U 线搜索失败
return U 达到迭代上限solve(D):
U = (u*(x_0), ..., u*(x_N)) exact nodal values: an initial guess only (R_0 = R_N = 0 already)
for k = 0, 1, ..., 34:
R = residual(U, D); A = R_U(U, D) (N+1 forward evaluations, Section 6)
solve A dU = -R (LU; dU[0] = dU[N] = 0: the constraint rows are satisfied)
record ||R||_inf, ||dU||_inf
if ||dU||_inf <= 1e-13 or ||R||_inf < 1e-15: return U converged (dU is not added)
t = 1
at most 25 times:
Ut = U + t dU
if 0.1 < min Ut, max Ut < 1.9 and ||R(Ut, D)||_inf < (1 - 1e-4 t) ||R||_inf:
U = Ut; go on with the next k
t = t/2
return U line search failed
return U iteration limit几点说明。初始猜测是精确节点值(页面知道精确解,但只把它当作起点;结果是离散解,不是 )。约束行是线性的,初始猜测又在两端精确,所以每个迭代值都有 ,每步修正在两个边界节点上为零:Newton 法实际只改变内部节点,用的是第 6 节的内部块 。先试整步,回溯把步长 减半(这里 是步长;第 8、10、11 节用 表示别的参数),直到试探状态在 之内而且残差降到旧值的 倍以下;修正量 时停止,返回最后一个迭代值(最后一次修正不加上)。默认设计下三种重构的每一步都是整步 (已验证;按同一算法重放线搜索,得到的每个迭代值逐位相同)。 时需要的迭代次数(计入最后那次只检查、不更新的迭代):一阶 5,无限制器线性 4,光滑限制器 6。
Some details. The initial guess is the vector of exact nodal values (the page knows the exact solution but uses it only as a starting point; the result is the discrete solution, not ). The constraint rows are linear and the initial guess is exact at both ends, so every iterate has and every correction vanishes at the two boundary nodes: Newton's method really changes the interior nodes only, with the interior block of Section 6. The full step is tried first and the backtracking halves the step length (here is the step length; Sections 8, 10 and 11 use for other parameters) until the trial state stays inside and the residual falls below times its old value; the iteration stops when the correction is and returns the last iterate (the last correction is not added). At the defaults every step of all three reconstructions is a full step (verified; replaying the line search of the same algorithm reproduces every iterate bit for bit). The number of iterations at (counting the last one, which only checks and does not update): 5 for first order, 4 for unlimited linear and 6 for the smooth limiter.
二次收敛,精确地写出来
Quadratic convergence, written out exactly
一阶与无限制器线性重构时,残差是 的二次多项式: 对迹是二次的,迹对 是线性的,两个约束行是线性的。所以 Taylor 展开止于二次项,整步之后的残差恰好就是二次余项:
For first-order and unlimited linear reconstruction the residual is a quadratic polynomial in : is quadratic in the traces, the traces are linear in and the two constraint rows are linear. The Taylor expansion therefore stops at the quadratic term, and after a full step the residual is exactly the quadratic remainder:
其中 是 的迹增量,。于是 。无限制器线性时每个迹增量是至多三个节点增量的组合(内部面的权是 ,边界面的权是 与 ),所以 ,;又因为 ,两个非负数之差不超过其中较大者:。简单的界是 (两个边界面也满足)。一阶时迹就是节点值, 与 共有的项 在差里抵消,每一行(第 行和第 行也一样)都是 ;Newton 修正在两个边界节点上为零,所以 ,:,对一般的 (取 )不能再小。两个边界附近的行与内部行是同一个公式,没有特殊的行。
where are the trace increments of , . Hence . For unlimited linear every trace increment is a combination of at most three nodal increments (weights at an interior face, and at the boundary faces), so and ; since , the difference of two of them is at most the larger one: . The simple bound is (the two boundary faces satisfy it too). At first order the traces are the nodal values, the term shared by and cancels in their difference, and every row, rows and included, is ; a Newton correction vanishes at the two boundary nodes, so and : , and no smaller constant works for a general (take ). The rows next to the boundary follow the same formula as the interior rows: no row is special.
这就是二次收敛。实测:余项恒等式在第 次迭代的整步上成立到 (,所有迭代,一阶与无限制器线性); 到 128 的全部 Newton 步上 的最大值是一阶 0.250、无限制器线性 0.444,都在各自的界以内; 时前几步的比值是一阶 0.203、0.235、0.250,无限制器线性 0.414、0.405。光滑斜率是有理函数,残差不是多项式,没有这样的恒等式;Taylor 定理仍然给出类似的界,但常数依赖斜率函数 的二阶导数,本页不给出它的值。光滑限制器在 需要 6 次迭代,前两步的残差只降到上一步的 0.53 倍和 0.05 倍,之后才二次收敛;上述比值的最大值是 62.99,没有一阶与无限制器线性那样小的普适常数。
This is quadratic convergence. Measured: the remainder identity holds on the full step of every iteration to within (, all iterations, first order and unlimited linear); over all Newton steps for to 128 the largest is 0.250 at first order and 0.444 for unlimited linear, both within their bounds; at the ratios of the first steps are 0.203, 0.235, 0.250 at first order and 0.414, 0.405 for unlimited linear. The smooth slope is a rational function, the residual is no polynomial and there is no such identity; Taylor's theorem still gives a bound of this kind, but its constant depends on the second derivatives of the slope function and this page does not give its value. With the smooth limiter Newton's method needed 6 iterations at , its residual fell only to 0.53 and 0.05 times the previous value in the first two steps, and quadratically after that; the largest of the ratios above is 62.99, with no small universal constant as at first order and for unlimited linear.
下面的播放器每步一个 Newton 迭代:迭代值与精确节点值的对照、修正量、步长、残差,以及这一步的 Newton 矩阵 (第 6 节的主题);表里还有整步之后的残差与二次余项的比较。
The player below takes one Newton iteration per step: the iterate against the exact nodal values, the correction, the step, the residual, and the Newton matrix of this step (the subject of Section 6); the table also compares the residual after a full step with the quadratic remainder.
停止准则要考虑舍入误差的底线: 在细网格上不一定总能达到。本页 到 4096、三种重构的全部 30 个 Newton 运行都按停止准则收敛了;全局控件的 最大取到 128,更细的网格要用独立的带状程序。但在最细的网格上,准则恰在舍入允许的边缘:用同样的规则、另一套舍入的独立 Python 复现,在一阶、 时因为线搜索在舍入误差底线上找不到下降的步而提前停止,状态与本页的相同到舍入精度。所以 的迭代次数取决于舍入,不是格式的性质,本页不引用它们。
A stopping rule must allow for the rounding floor: cannot always be reached on fine meshes. All 30 Newton runs of this page's study ( to 4096, three reconstructions) converged by the stopping rule; the global controls stop at , and finer meshes use the independent band program. On the finest meshes, however, the rule sits at the edge of what rounding allows: an independent Python replica with the same rules and other rounding stops first order at early, because its line search finds no decreasing step on the rounding floor, with a state that agrees with this page's to rounding accuracy. The number of iterations at therefore depends on rounding and is not a property of the scheme; this page does not quote it.
格式的相容性
Consistency of the scheme
相容性要问的是:精确解代入离散方程后留下的误差,是否随加密而消失。下表把第 4 节的测量写成残差密度 (无限制器线性):内部行 ,紧邻边界的两行 ,二者都趋于零, 也趋于零。所以在通常的逐点意义上,这个格式每一行都是相容的,这与格心页不同(那里端行的密度趋于常数,格式靠通量差分形式才收敛)。一阶重构同样(所有行 );光滑限制器在细网格上有两个边界行的密度趋于常数(第 4 节)。解的收敛要靠第 4 节的稳定性论证,而不只是相容性。
Consistency asks whether the exact solution satisfies the discrete equations up to an error that vanishes under refinement. The table writes the measurements of Section 4 as residual densities (unlimited linear): inside and in the two rows next to the boundary, both tending to zero, and tends to zero too. So in the usual pointwise sense every row of this scheme is consistent, unlike on the cell-centred page (where the densities of the end rows tend to constants and the scheme converges through its flux-difference form). First order is the same (every row ); the smooth limiter has two boundary rows whose densities tend to constants on fine meshes (Section 4). The convergence of the solution rests on the stability argument of Section 4, not on consistency alone.
| 8 | 1.64e−1 | −2.63e−2 | −1.68e−1 | −4.81e−3 |
| 32 | 7.30e−3 | −3.37e−2 | −1.21e−2 | 5.82e−4 |
| 128 | 4.14e−4 | −9.30e−3 | −3.32e−3 | 1.60e−5 |
| 512 | 2.53e−5 | −2.38e−3 | −8.74e−4 | −4.80e−7 |
| 2048 | 1.59e−6 | −5.97e−4 | −2.22e−4 | −6.29e−8 |
| 4096 | 3.97e−7 | −2.99e−4 | −1.11e−4 | −1.72e−8 |
Jacobian 与 Gauss–Seidel
The Jacobian and Gauss–Seidel
本节实验:先数一数:在第 行, 的非零元素在一阶重构和无限制器线性重构时各有几个?第 行和第 行呢?再预测:Gauss–Seidel 能不能解 Newton 修正方程? 和 各要几次扫描,是几次还是几百次?图 26、图 27、图 28 和下面的面板给出答案。
Experiment: Count first: how many nonzero entries does row of have at first order and with unlimited linear reconstruction, and how many do rows and have? Then predict: does Gauss–Seidel converge on the Newton correction system, and how many sweeps does it need for and for : a few, or hundreds? Figure 26, Figure 27, Figure 28 and the panels below answer.
第 5 节把 当作 Newton 法里的黑箱。本节把它造出来,看它的结构和可以证明的性质,再用 Gauss–Seidel(GS)近似地解 Newton 修正方程:以一阶矩阵作预处理,量出 GS 收敛得有多快。格点格式的 有两个与格心页不同的特点:两个约束行是单位行,一阶矩阵在边界上没有特殊的行。
Section 5 used as a black box inside Newton's method. This section builds it, looks at its structure and at the properties that can be proved, then solves the Newton correction system approximately with Gauss–Seidel (GS) on the first-order matrix and measures how fast GS converges. The node-centred differs from that of the cell-centred page in two ways: the two constraint rows are identity rows, and the first-order matrix has no special row at the boundary.
:前向代码的列
by columns of the forward code
第 列是残差对 的单位变化的响应,也就是第 8 节的前向代码以种子 、 的输出: 次前向求值得到整个矩阵(model.js 里的 jacState)。乘积不需要矩阵:一次前向求值就给出 (这里 是任意向量,不是后面各节的切线解 )。
Column of is the response of the residual to a unit change of , that is, the output of the forward code of Section 8 with the seed , : forward evaluations give the whole matrix (jacState in model.js). A product needs no matrix at all: one forward evaluation gives (here is any vector, not the tangent solutions of the later sections).
验算:由组装好的矩阵算的 与一次前向求值之差为 (三种重构,)。还可以做得更好:第 行只含 ,所以相隔 5 的两列从不共用一行;按 模 5 给列分组(每组一种“颜色”)、一组一组同时给种子,5 次前向求值就得到整条带,不论 多大(与逐列组装的差为 0)。两个单位行只含自己,不破坏这个分组。本页的细网格计算( 到 4096)用这种方式组装带,再用带状 LU 求解。
Check: from the assembled matrix and from one forward evaluation differ by (all three reconstructions, ). One can do even better: row involves only , so two columns five apart never share a row; grouping the columns by mod 5 (one “colour” per group) and seeding each group at once, 5 forward evaluations give the whole band, whatever is (difference from the column-by-column assembly 0). The two identity rows involve only themselves and do not disturb the grouping. The fine-mesh computations of this page ( up to 4096) assemble the band this way and solve with a band LU.
结构:单位行与五条对角线或三条
The structure: identity rows and five diagonals or three
第 行和第 行是约束的单位行(第 4 节)。内部行 :面 含 (无限制器线性、光滑限制器)或 (一阶,第 3 节),所以第 行含 (五条对角线)或 (三条);边界面读的节点少一个(面 读 ,面 读 ),所以二阶时第 行和第 行只有四个非零元素。 时每行的非零个数:一阶 1、3、3、3、3、3、3、3、1,无限制器线性与光滑限制器 1、4、5、5、5、5、5、4、1;总共 23()与 35()个。一阶时第 行有 3 个,二阶时 5 个。
Rows and are the identity rows of the constraints (Section 4). An interior row is : face involves (unlimited linear, smooth limiter) or (first order, Section 3), so row involves (five diagonals) or (three diagonals); the boundary faces read one node fewer (face reads and face reads ), so at second order rows and have only four nonzero entries. The number of nonzeros of each row at : 1, 3, 3, 3, 3, 3, 3, 3, 1 at first order, 1, 4, 5, 5, 5, 5, 5, 4, 1 for unlimited linear and the smooth limiter; in all 23 () and 35 (). Row has 3 at first order and 5 at second order.
一阶时第 3 节写出的内部行是黏性 的中心格式:
At first order the interior row written in Section 3 is the central scheme for the viscosity :
逐项求导,得到 Jacobian 的行;每一个内部行(包括第 行和第 行)都是这个公式,边界上没有特殊的行,第 行的 乘的是边界节点的值:
Differentiating it term by term gives the row of the Jacobian; every interior row, rows and included, is this formula, nothing is special at the boundary, and the of row multiplies the value of the boundary node:
格心页不同:那里第一行和最后一行是 这样的特殊公式。这些公式与前向代码组装的 一致(收敛解处与 Newton 初值处,每个内部行;最大差为 0)。
The cell-centred page differs: there the first and the last row are special formulas such as . These formulas agree with the assembled by the forward code (at the converged solution and at the Newton initial guess, every interior row; the largest difference is 0).
在下面的面板里选择重构( 由全局控件决定),看 、一阶矩阵 、二者之差和 的符号;还可以只看内部块 :
In the panel below change the reconstruction ( is set by the global controls) and look at , the first-order matrix , their difference and the signs of ; the interior block can be shown alone:
关于 能说什么
What can be said about
分块结构。把未知量排成 、内部未知量 、,单位行使 成为分块矩阵
Block structure. Ordering the unknowns as , the interior unknowns and , the identity rows make a block matrix
是边界节点 与 的列(落在内部行里的部分)。于是 , 的特征值是 和 的特征值, 的解是 ,,:约束行与内部方程解耦。Newton 法里 (第 5 节),每一步只解 ;下面的 GS 也只作用在这个内部块上。(第 11 节的转置方程 同样解耦:内部的 满足 ,然后 , 类似。)
where are the columns of the boundary nodes and (the part that lies in the interior rows). So , the eigenvalues of are and those of , and the solution of is , , : the constraint rows decouple from the interior equations. In Newton's method (Section 5), every step solves only, and the GS below acts on this interior block only. (The transposed system of Section 11 decouples in the same way: the interior satisfies , and then , similarly .)
符号。一阶时,只要 (Newton 的范围 保证了这一点),所有非对角元素为负、对角元素为正: 与 都是 Z 矩阵(没有正的非对角元素)。有重构时外对角线出现:无限制器线性的 、,所以出现正的非对角元素( 时 里有 12 个, 里有 10 个), 不再是 Z 矩阵;光滑限制器的外对角线带着 ,正负都可以( 里有 8 个正的非对角元素)。
Signs. At first order, as long as (Newton's range guarantees it), every off-diagonal entry is negative and every diagonal entry positive: and are Z-matrices (no positive off-diagonal entry). With reconstruction the outer diagonals appear: for unlimited linear and , so positive off-diagonal entries appear (12 in and 10 in at ) and is no longer a Z-matrix; for the smooth limiter the outer diagonals carry and have either sign (8 positive off-diagonal entries in ).
行和。一阶内部行的行和是 ,解下降的地方为负: 不是行对角占优的(一阶,, 的行和从 −0.119 到 1.601)。
Row sums. The row sum of a first-order interior row is , negative where the solution decreases: is not diagonally dominant by rows (at first order, , the row sums of range from −0.119 to 1.601).
列和。守恒体现在列和里:把内部行 加起来,(第 5 节的守恒)。两个边界面只读边界附近的节点(一阶读 与 ,二阶读 与 ),所以只有这些列的和不为零。实测 :一阶的非零列是 0、1、7、8,无限制器线性与光滑限制器是 0、1、2、6、7、8(其余列的和不超过 )。对 起作用的是一阶的两个:
Column sums. Conservation shows in the column sums: adding the interior rows gives (the conservation of Section 5). The two boundary faces read only nodes near the boundary (first order and , second order and ), so only these columns have a nonzero sum. Measured at : the nonzero columns are 0, 1, 7, 8 at first order and 0, 1, 2, 6, 7, 8 for unlimited linear and the smooth limiter (the other columns sum to at most ). The two that matter for at first order are:
实测(一阶,):第 列的和是 0.568912(公式 0.568912),第 列是 1.475199(公式 1.475199)。
Measured (first order, ): column sums to 0.568912 (formula 0.568912), column to 1.475199 (formula 1.475199).
一阶的 是 M 矩阵。定理(Varga):设 是对角元素为正的 Z 矩阵。若 不可约,按行弱对角占优,并且至少在一行严格对角占优,则 非奇异,且 的每个元素都是正的。
At first order is an M-matrix. Theorem (Varga). Let be a Z-matrix with positive diagonal. If is irreducible, weakly diagonally dominant by rows and strictly diagonally dominant in at least one row, then is nonsingular and every entry of is positive.
证明(一阶): 也是对角元素为正的 Z 矩阵,它的行和就是 的列和:第 列为 ,第 列和第 列为正(上面的两个公式)。对 Z 矩阵,行和非负恰好就是按行弱对角占优,所以 按行弱对角占优,在两行严格占优。 是三对角矩阵,上、下对角元素都不为零,所以 不可约。由定理 ,即 。对每个 的状态都成立。整个 是可约的(两个单位行与其余部分不相连),但由分块公式,、(只有 与 非零,都是负的)和 得 : 是(可约的)M 矩阵。实测: 的最小元素是 2.88e−3(), 时是 7.6e−26,仍为正(远离对角线的元素按因子连乘衰减),没有一个负元素(相对容差 )。
Proof (first order): is also a Z-matrix with positive diagonal, and its row sums are the column sums of : for columns , positive for columns and (the two formulas above). For such a matrix a nonnegative row sum is exactly weak diagonal dominance, so is weakly diagonally dominant by rows, strictly in two rows. is tridiagonal with nonzero sub- and superdiagonal entries, so is irreducible. By the theorem , that is . This holds for every state with . The whole is reducible (the two identity rows are not connected to the rest), but the block formula, , (only and are nonzero, both negative) and give : is a (reducible) M-matrix. Measured: the smallest entry of is 2.88e−3 at and 7.6e−26 at , still positive (entries far from the diagonal decay like a product of factors), and there is no negative entry (relative tolerance ).
二阶时 不是 Z 矩阵,定理不适用。 时无限制器线性的 有 11 个负元素,最小是 −0.0812; 的负元素个数是 11、57、193、442,到 时没有超过舍入水平的负元素(相对容差 )。光滑限制器:11、47、186、446、0()。所以二阶的 在对流占优的粗网格上有负元素,但并不总是如此。
At second order is not a Z-matrix and the theorem does not apply. At the of unlimited linear has 11 negative entries, the smallest −0.0812; for the number of negative entries is 11, 57, 193, 442, and at there is none above the rounding level (relative tolerance ). The smooth limiter: 11, 47, 186, 446, 0 for . So the inverse of the second-order has negative entries on coarse convection-dominated meshes, but not always.
条件数。(用带状 LU 逐列精确算出,不是估计)随 增长,表中是默认设计的收敛解(单位行的比例是 1):无限制器线性从 的 37.71 增长到 的 4.413e4;最后一次加密乘以 3.67,趋向扩散算子的 (每次加密乘以 4);粗网格上对流占优,增长较慢。内部块 的条件数与它相差不大(一阶 :25.01 对 35.72; 时前四位相同):约束行的单位比例没有恶化问题。LU 求解不在乎(误差约为 ),但迭代法会变慢,下面就看到。
Condition number. (computed exactly column by column with the band LU, not estimated) grows with ; the table is for the converged solution of the default design (the identity rows have scale 1): for unlimited linear it grows from 37.71 at to 4.413e4 at ; the last doubling multiplies it by 3.67, approaching the of a diffusion operator (a factor of 4 per doubling); on coarse meshes convection dominates and the growth is slower. The condition number of the interior block is close (first order : 25.01 against 35.72; the first four digits agree for ): the unit scale of the constraint rows does no harm. LU solves do not care (their error is about ), but iterative methods slow down, as we see next.
| 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter | |
|---|---|---|---|
| 8 | 3.572e1 | 3.771e1 | 5.489e1 |
| 16 | 7.673e1 | 8.387e1 | 1.245e2 |
| 32 | 1.675e2 | 1.775e2 | 2.337e2 |
| 64 | 4.048e2 | 4.126e2 | 4.546e2 |
| 128 | 1.109e3 | 1.121e3 | 1.145e3 |
| 256 | 3.448e3 | 3.495e3 | 3.494e3 |
| 512 | 1.193e4 | 1.201e4 | 1.196e4 |
| 1024 | 4.397e4 | 4.413e4 | 4.399e4 |
用一阶矩阵做 Gauss–Seidel
Gauss–Seidel with the first-order matrix
LU 精确地解 (稠密 ,带状 )。经典的替代做法是迭代:每一步只需要一个便宜的近似求解。为了与第 17 节一致,这里把线性方程组写成 ( 即 ),迭代值记作 (迭代次数 ),精确解 ,缺陷 (线性方程组的残差);它们与坐标 、第 3 节的单侧差分 没有关系。取同一状态下的一阶 Jacobian 的内部块 (三对角),拆成对角、严格下三角和严格上三角 (写成 以免与状态 混淆)。在 上从零开始做一次前向 GS 扫描,就是逐行解下三角方程组 ,记作 (这里 是 GS 映射,与附录 B 无关)。缺陷校正是
LU solves exactly (dense , banded ). The classical alternative is an iteration that needs only a cheap approximate solve at each step. To match Section 17, this section writes the linear system as ( meaning ), with the iterates (iteration counter ), the exact solution and the defect (the residual of the linear system); they have nothing to do with the coordinate or the one-sided differences of Section 3. Take the interior block of the first-order Jacobian at the same state (tridiagonal) and split it into its diagonal, strictly lower and strictly upper parts, (written so as not to clash with the state ). One forward GS sweep on from zero solves the lower-triangular system row by row; call this map (here is the GS map; it has nothing to do with Appendix B). Defect correction is
缺陷校正:用 P_II(一阶 Jacobian)和松弛因子 omega 解 A_II x = b
x = 0
for m = 0, 1, 2, ...:
r = b - A_II x 缺陷(只需要乘积 A_II x)
if ||r||_inf <= tol: stop
for i = 1 .. N-1: 在 P_II 上做一次从 dx = 0 开始的前向 GS 扫描
dx_i = (r_i - P_{i,i-1} dx_{i-1}) / P_ii (P 是三对角:只有 j = i-1)
x = x + omega dxdefect correction: solve A_II x = b with P_II (the first-order Jacobian) and the relaxation omega
x = 0
for m = 0, 1, 2, ...:
r = b - A_II x defect (needs the product A_II x only)
if ||r||_inf <= tol: stop
for i = 1 .. N-1: one forward GS sweep on P_II, from dx = 0
dx_i = (r_i - P_{i,i-1} dx_{i-1}) / P_ii (P is tridiagonal: only j = i-1)
x = x + omega dx一阶时()取 ,这就是 上的 Gauss–Seidel:,误差传播矩阵 。因为 是 M 矩阵(上面), 也是 M 矩阵,逆非负;又 ,所以 。由 Perron–Frobenius,谱半径 (本节里 一律表示谱半径;附录 B 里的 是另一个量)本身是 的特征值,有非负特征向量(最慢的误差模态)。它还是模为 的唯一特征值:三对角 的 Jacobi 矩阵相似于零对角、次对角元素非零的对称三对角矩阵,特征值成对 且都是单根,而 的非零特征值是 ,其余是零。这个分裂是 M 矩阵的正则分裂,由 Varga 定理 :在一阶方程组上,GS 对每个 和每个 的状态都收敛。 的最小元素是 0(它的第一列全为零: 的第一列是零)。
At first order () with this is Gauss–Seidel on : , with the error propagator . Since is an M-matrix (above), is an M-matrix too, with a nonnegative inverse; and , so . By Perron–Frobenius, the spectral radius (in this section always denotes a spectral radius; Appendix B uses for another quantity) is itself an eigenvalue of , with a nonnegative eigenvector (the slowest error mode). It is also the only eigenvalue of modulus : the Jacobi matrix of the tridiagonal is similar to a symmetric tridiagonal matrix with zero diagonal and nonzero off-diagonal entries, whose eigenvalues come in pairs and are all simple, and the nonzero eigenvalues of are the , the others zero. The splitting is a regular splitting of an M-matrix, so by Varga's theorem: on the first-order system GS converges for every and every state with . The smallest entry of is 0 (its first column is zero: the first column of is zero).
有多快?实测 (默认设计的收敛解,;一阶由 Jacobi 特征值精确算出,其余由 QR 算法)见下表,一阶随 趋于 1。把误差降到初值的 倍需要 次扫描:一阶 时 80 次, 时 659 次。GS 在细网格上很慢。
How fast? The measured (converged solution of the default design, ; exact from the Jacobi eigenvalues at first order, by the QR algorithm otherwise) is in the table; at first order it tends to 1 with . Reducing the error to of its initial size takes sweeps: 80 at first order for and 659 for . GS is slow on fine meshes.
| 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter | |
|---|---|---|---|
| 8 | 0.7496 | 0.6211 | 0.6819 * |
| 16 | 0.8361 | 0.6740 * | 0.7338 * |
| 32 | 0.8868 | 0.7558 * | 0.7757 * |
| 64 | 0.9313 | 0.8735 | 0.9001 |
| 128 | 0.9656 | 0.9478 | 0.9599 |
表中带 * 的是模最大的特征值为一对复数的情形。二阶时() 不再是非负的( 的无限制器线性,最小元素 −0.0670),没有定理保证收敛。默认设计下实测的 处处小于 1,而且在每个 上都小于一阶的值:迭代收敛,比一阶快;复数对出现在:无限制器线性 ,光滑限制器 。这是一个设计下的测量,不是保证。
An asterisk marks the cases where the eigenvalues of largest modulus are a complex pair. At second order () is no longer nonnegative (smallest entry −0.0670 for unlimited linear at ) and no theorem guarantees convergence. At the default design the measured is below 1 throughout and below the first-order value at every : the iteration converges, faster than at first order; the complex pairs occur for unlimited linear at and for the smooth limiter at . These are measurements at one design, not a guarantee.
松弛。。一阶时 的特征值是实数,落在 (它们是 Jacobi 特征值的平方 ,以及 ),所以 ,在 处最小(第一个 Newton 修正方程上: 时 1.53, 时 1.93):精确算术下超松弛更快。在这个方程上 需要 49、143、596 次扫描(一阶)与 38、81、423 次(无限制器线性)(),在每种情形都比 (68、189、779 与 53、111、553)少。但是不能只看 来加大 : 的零特征值是亏损的(代数重数 ,只有一个特征向量), 时它移到 ,迭代可能先增长许多个数量级再衰减:、 时 ,缺陷却在 131 次扫描内超过 (计算在此停止)。双精度下 本身在 不收敛;在 的网格上,一阶最快的收敛 是 1.45、1.60、1.60、1.65(,扫描 43、115、214、467 次)。
Relaxation. . At first order the eigenvalues of are real and lie in (they are the squares of the Jacobi eigenvalues, and ), so , smallest at (on the first Newton correction system 1.53 at and 1.93 at ): in exact arithmetic over-relaxation helps. On this system needs 49, 143, 596 sweeps at first order and 38, 81, 423 for unlimited linear (), fewer than in every case (68, 189, 779 and 53, 111, 553). But cannot be raised by looking at alone: the zero eigenvalue of is defective (algebraic multiplicity , a single eigenvector), for it moves to , and the iteration can grow by many orders of magnitude before it decays: at and one has , yet the defect exceeds within 131 sweeps (where the computation stops). In double precision itself does not converge at ; on the grid the fastest convergent at first order is 1.45, 1.60, 1.60, 1.65 for (43, 115, 214, 467 sweeps).
下面的播放器在一个真实的线性方程组上逐行执行 GS,方程组是第一个 Newton 修正方程 ( 是 Newton 初值):每一步更新一个内部节点,显示用到的数、新值、缺陷和与 LU 精确解的误差。
The player executes GS row by row on a real linear system, the first Newton correction system ( the Newton initial guess): each step updates one interior node and shows the numbers used, the new value, the defect and the error against the exact LU solution.
最后一个面板把迭代跑到底:选 ,看缺陷的历史,测量渐近收敛因子,并与 的全部特征值(复平面上的点)比较;它还画出最慢的误差模态。一阶时这个模态在出口 之前的最后几个节点( 时最大元素在内部节点 28,节点编号 )。这个位置属于 ,而不是扫描方向: 是对流占优的(; 时 在 1.37 与 1.63 之间),慢模态向下游增长。后向 GS 扫描(从 扫到 )的谱半径相同(两者的谱都由 与零组成; 的计算值之差不超过 ,那是舍入误差),它的最慢模态也在出口附近( 时在节点 30);顺着流动方向扫只是缩短暂态( 时 779 次对 872 次)。第 17 节在切线与伴随方程上比较 GS、GMRES(以 为预处理)和 LU。
The last panel runs the iteration to the end: choose , watch the history of the defect, measure the asymptotic convergence factor and compare it with all eigenvalues of (points in the complex plane); it also draws the slowest error mode. At first order this mode sits in the last nodes before the outlet end (at its largest entry is at interior node 28 of ). The location belongs to , not to the sweep: is convection-dominated (; at , lies between 1.37 and 1.63), so its slow modes grow downstream. A backward GS sweep (from to ) has the same spectral radius (both spectra consist of the squares and zeros; the computed spectral radii differ by at most at , which is rounding) and its slowest mode also sits at the outlet (node 30 at ); sweeping with the flow only shortens the transient (779 against 872 sweeps at ). Section 17 compares GS, GMRES (with as the preconditioner) and LU on the tangent and adjoint systems.
目标函数:一个线性泛函
The objective: a linear functional
本节实验: 用的是精确节点值和梯形权重。它等于 吗?如果不等, 时差多大,符号由什么决定?先预测,再看图 29。
Experiment: uses the exact nodal values and the trapezoid weights. Is it equal to ? If not, how large is the difference at , and what sets its sign? Predict, then look at Figure 29.
连续问题的目标是 (第 1 节)。本节把它变成节点值的函数,写出状态梯度 (伴随方程的右端项是 ),并找出离散目标的误差来自哪里。与格心页不同,这里没有“精确平均”可以用:未知量是点值,目标是对 的梯形公式,它对精确解也有一个求积误差,本节把它精确地写出来。
The objective of the continuous problem is (Section 1). This section turns it into a function of the nodal values, writes the state gradient (the right-hand side of the adjoint equation is ), and finds out where the error of the discrete objective comes from. Unlike on the cell-centred page there are no exact averages to use: the unknowns are point values, the objective is the trapezoid rule for , and it has a quadrature error even for the exact solution, which this section writes down exactly.
从积分到权重
From the integral to the weights
每个节点代表它的对偶控制体: 被当作在 上都成立, 的宽度 在内部节点是 ,在两个边界节点是 ;被积函数 在节点处取值。于是
Each node stands for its dual interval: is taken to hold over , whose width is at an interior node and at the two boundary nodes, and the integrand is sampled at the node. So
这正是对被积函数 的复合梯形公式:,;对偶宽度恰好就是梯形公式的权。 是线性的,它的状态梯度就是权向量本身,与状态无关;它没有对设计的显式依赖,二阶导数为零:
This is exactly the composite trapezoid rule for the integrand : with ; the dual widths are the weights of the trapezoid rule. is linear, its state gradient is the weight vector itself, independent of the state; it has no explicit dependence on the design, and its second derivative is zero:
所以伴随方程 的右端项 在每个状态和设计下都一样,变的只有 ; 对设计的导数只通过状态:,包括边界节点:它们以 进入 ,值 、 又随设计变化。 时权重在内部从 线性增长到 ,而两个端权是半个区间:,(格心页的权单调增长,这里端权不单调)。权之和是积分 :。
So the right-hand side of the adjoint equation is the same at every state and every design; only changes. The derivative of with respect to the design goes through the state only, , the boundary nodes included: they enter with , and their values , change with the design. At the weights grow linearly in the interior from to , while the two end weights are half intervals: and (on the cell-centred page the weights grow monotonically, here the end weights do not). The weights add up to the integral : .
权重规则是精确的吗?
Is the weight rule exact?
格心页里 精确成立: 个 次单位根之和为零。这个论证在这里不适用:被积函数 不是周期的(),梯形公式不再对它精确。要精确地写出误差,用同一个工具(单位根)做完整的计算。 里的常数 乘以 是线性函数,梯形公式对它精确,贡献 。正弦部分写成 ,,所以 ( 就是 次单位根;字母 留给第 6 节的松弛因子)。单位根和与它的“一阶矩”(带半权的和,,)是
On the cell-centred page holds exactly: the -th roots of unity add up to zero. That argument does not apply here: the integrand is not periodic (), and the trapezoid rule is no longer exact for it. To write the error exactly, do the whole calculation with the same tool, the roots of unity. In the constant times is a linear function, for which the trapezoid rule is exact and contributes . The sine part is with , so (the numbers are the -th roots of unity; the letter is kept for the relaxation factor of Section 6). The sum of the roots of unity and its “first moment” (sums with half weights at both ends, , ) are
其中带撇的和表示两端取半权。第一个和与格心页的“零”是同一件事;第二个和不为零,正是它留下了误差:梯形和的正弦部分与精确积分是
where the primed sums have half weights at both ends. The first sum is the same “zero” as on the cell-centred page; the second does not vanish, and it is what leaves the error: the sine part of the trapezoid sum and the exact integral are
两式相减:
Subtracting:
展开 ,得到
Expanding gives
这就是 Euler–Maclaurin 公式:误差由被积函数 的奇数阶导数在两端的跳跃决定,而 是周期的,所以这些跳跃就是 。求积误差不会消失:对每个 ,,它按 趋于零,系数是 ;默认设计里 ,所以梯形公式偏大。:(模型的精确节点值,求和得到),封闭公式给出 0.00149597(两者之差小于 ,是舍入);展开的三项是 0.00148052、0.00001522、0.00000022,前两项之和 0.00149574 的余项 2.3e−7 就是第三项 2.2e−7 的量级。把封闭公式与直接的梯形求和比较: 到 4096 的差别(相对)不超过 (最大的差别出现在最细的网格:那里求积误差最小,求和的舍入相对最重);对 (,)同样的推导给出 ,与直接求和之差不超过 。
This is the Euler–Maclaurin formula: the error is set by the jumps of the odd derivatives of the integrand between the two ends, and since is periodic the jumps are . The quadrature error does not go away: for every , , and it tends to zero like with the coefficient ; at the default design , so the trapezoid rule overestimates. At : (the model's exact nodal values, summed), the closed form gives 0.00149597 (they differ by less than , which is rounding); the three terms of the expansion are 0.00148052, 0.00001522 and 0.00000022, and the remainder 2.3e−7 after the first two, 0.00149574 together, is of the size of the third term 2.2e−7. Against the direct trapezoid sum the closed form differs (relatively) by at most for to 4096 (the largest difference is at the finest mesh, where the quadrature error is smallest and the rounding of the sum weighs most); for (, ) the same derivation gives , which differs from the direct sum by at most .
同一个误差也出现在梯度里。 对设计的导数就是封闭公式对 的导数:, 时是 ,对模型的中心差分()给出 。即使把精确节点值代入,离散目标的梯度也与连续梯度相差 ;离散伴随算的是 的导数,第 15、16 节把这一项和状态误差对梯度的影响分开。
The same error shows up in the gradient. The derivative of with respect to the design is the derivative of the closed form with respect to : , which at is , and central differences of the model () give . Even with the exact nodal values put in, the gradient of the discrete objective differs from the continuous gradient by ; the discrete adjoint computes the derivative of , and Sections 15 and 16 separate this term from the effect of the state error on the gradient.
下面的面板自己试:选被积函数里的谐波数 、权函数( 或 )、 与 ,看梯形和、精确积分、误差与封闭公式。权函数取 时被积函数是周期的,误差为零():这就是格心页的情形。
Try it yourself in the panel below: choose the harmonic number in the integrand, the weight function ( or ), and , and see the trapezoid sum, the exact integral, the error and the closed form. With the weight the integrand is periodic and the error vanishes (): that is the situation of the cell-centred page.
的误差:状态误差加求积误差
The error of : state error plus quadrature error
因为 对 是线性的,它的误差可以精确地分成两部分:
Because is linear in , its error splits exactly into two parts:
权都是正的,所以状态部分不超过 ;实测 在 到 4096 的最大值:一阶 0.745,无限制器线性 0.546,光滑限制器 0.740,都不超过 (正负误差相互抵消,所以通常远小于它)。默认设计 :, 是 1.280916(一阶)、1.463945(无限制器线性)和 1.462683(光滑限制器);无限制器线性的 ,其中状态部分 −0.008749,求积部分 0.001496。
The weights are all positive, so the state part is at most ; measured, the largest for to 4096 is 0.745 at first order, 0.546 for unlimited linear and 0.740 for the smooth limiter, none above (errors of both signs cancel, so it is usually far below). At the defaults () and is 1.280916 (first order), 1.463945 (unlimited linear) and 1.462683 (smooth limiter); for unlimited linear , made of the state part −0.008749 and the quadrature part 0.001496.
下面的面板对当前设计和当前解做这个分解:权重、贡献、两部分误差,以及每个节点对状态部分的贡献。
The panel below makes this decomposition for the current design and the current solution: the weights, the contributions, the two parts of the error and what every node contributes to the state part.
加密。两部分都是 (二阶重构),而且符号相反时会部分抵消。默认设计、无限制器线性: 时 ,状态部分是 −1.818e−8,求积部分是 5.648e−9(除以 :−0.2102、−0.3050、0.0948,最后一个就是极限 0.0948)。状态部分变号 2 次( 与 之间、 与 之间), 变号 2 次( 与 之间、 与 之间),所以相邻两次加密的观测阶很不规则(1.36、1.40、2.16、2.63、3.17、4.83、0.67、1.20、1.72;最后一次 1.72), 附近还没有进入渐近区。一阶:状态部分占优,观测阶 1.00;光滑限制器 0.97(第 4 节说明光滑限制器为什么在细网格上降为一阶)。独立 Python 程序(python/solve_node.py)存的 与本页相差不超过 (三种重构, 到 4096),它的 objective_quadrature_error 与封闭公式相差不超过 。
Refinement. Both parts are (second-order reconstruction), and they partly cancel when their signs are opposite. Default design, unlimited linear: at , , the state part is −1.818e−8 and the quadrature part 5.648e−9 (divided by : −0.2102, −0.3050, 0.0948, the last being the limit 0.0948). The state part changes sign twice (between and , between and ) and twice (between and , between and ), so the observed orders of consecutive doublings are irregular (1.36, 1.40, 2.16, 2.63, 3.17, 4.83, 0.67, 1.20, 1.72; 1.72 for the last) and the asymptotic range is not reached near . At first order the state part dominates, with observed order 1.00; the smooth limiter gives 0.97 (Section 4 explains why it drops to first order on fine meshes). The stored by the independent Python program (python/solve_node.py) differs from this page's by at most (three reconstructions, to 4096), and its objective_quadrature_error differs from the closed form by at most .
| 8 | −7.25e−3 | −8.75e−3 | 1.50e−3 | −0.4642 |
| 32 | 1.07e−3 | 9.77e−4 | 9.26e−5 | 1.0953 |
| 128 | 3.86e−5 | 3.28e−5 | 5.78e−6 | 0.6317 |
| 512 | 1.51e−7 | −2.10e−7 | 3.61e−7 | 0.0396 |
| 2048 | −4.12e−8 | −6.37e−8 | 2.26e−8 | −0.1726 |
| 4096 | −1.25e−8 | −1.82e−8 | 5.65e−9 | −0.2102 |
在浏览器里自己量一量(当前设计, 到 256,用带状 Newton 法求解),表里还有两部分误差:
Measure it yourself at the current design ( to 256, solved in the browser with the band Newton method); the table also lists the two parts of the error:
伴随方程的右端项是 :一个全为负的向量,内部分量的大小随 线性增长,两端是半权 ,与状态无关。第 9、11 节用它计算伴随 。
The right-hand side of the adjoint equation is : a negative vector whose interior entries grow in size linearly with , with the half weights at the two ends, and it does not depend on the state. Sections 9 and 11 use it to compute the adjoint .
前向:沿着面循环传播切向量
Forward: the tangent through the face loop
本节实验:只给一个未知量一个单位的扰动 ,设计不动:残差的哪几行会变、各变多少?先用第 3 节的面模板预测,再在本节的逐列播放器里验证。
Experiment: Seed one unknown, , and hold the design fixed: which rows of the residual change, and by how much? Predict from the face stencil of Section 3, then check with the column player of this section.
第 5–7 节写出了残差代码 (五条语句),解出了 ,定义了目标 。本节用前向模式对残差代码求导:输入是未知量与设计的一个联合方向 ,输出是每个残差沿这个方向的导数,全程不形成矩阵。第 9 节把同一段代码倒过来运行。
Sections 5–7 built the residual code of five statements, solved and defined the objective . This section differentiates the residual code in forward mode: a joint direction of the unknowns and the design goes in, the derivative of every residual along that direction comes out, and no matrix is formed. Section 9 runs the same code backwards.
矩阵乘积可以写成方向导数
A matrix product as a directional derivative
先固定设计(),让所有未知量沿方向 一起变化:。向量 就是状态切向量 ,它规定每个未知量随 变化的快慢。对每个残差在 处关于 求导,得到残差切向量 :一次矩阵向量乘积,却不需要矩阵本身。
First hold the design fixed () and move all unknowns together along a direction : . The vector is the state tangent ; it says how fast each unknown changes with . Differentiating each residual with respect to at gives the residual tangent : a matrix–vector product, computed without the matrix.
再让设计也动起来,,链式法则多出设计 Jacobian 一项。自动微分的前向(切线)模式就是这样计算的:残差代码里的每个中间量 都配一个切向量 ,按 本身的计算顺序算出。
Let the design move as well, , and the chain rule adds the design Jacobian. The forward (tangent) mode of automatic differentiation computes exactly this: every intermediate of the residual code gets a tangent , computed in the same order as itself.
记号:上点 与上横线 是离散程序的前向导数与反向(伴随)导数。代码沿用 Tapenade 等自动微分工具的命名:前向子程序 residual_d 读入种子 u_d 、D_d ,返回 res_d ;第 9 节的 residual_b 读入 res_b ,返回 u_b 与 D_b 。
Notation: the dot and the bar are the forward and the reverse (adjoint) derivatives of the discrete program. The code uses the names of AD tools such as Tapenade: the forward routine residual_d takes the seeds u_d and D_d and returns res_d ; the residual_b of Section 9 takes res_b and returns u_b and D_b .
代码里的运算与它们的求导规则
The rules for the operations of the code
残差代码的每条语句都由少数几种运算搭成。下表给出每种运算的前向规则(本节)和伴随规则(第 9 节):伴随规则是前向规则的转置,用的是同一批偏导数—— 在前向乘 ,在反向乘 。后面的语句表对每条语句用的正是这些规则,表里的“局部导数”就是这些偏导数在当前状态上的值。
Every statement of the residual code is built from a few operations. The table gives, for each, the tangent rule (this section) and the adjoint rule (Section 9): the adjoint rule is the transpose of the tangent rule and uses the same partial derivatives — multiplies going forward and going backward. The statement tables below apply exactly these rules, and the “local derivatives” of the tables are these partial derivatives at the current state.
| 运算Operation | 前向规则Tangent rule | 伴随规则(第 9 节)Adjoint rule (Section 9) | 在代码里In the code |
|---|---|---|---|
| 差 ;分发 ;源项 the differences ; the scatter ; the source | |||
| ( 是常数) ( a constant) | 系数 the factors | ||
| 限制器 the limiter | |||
| 里的 the of in | |||
| 里 的 the in inside | |||
| 整条语句:通量 、斜率 、a whole statement: the flux , the slope , |
一个变量被多条语句读(扇出)时,前向什么也不用做;反向每条读它的语句各给一项,所以伴随必须用 += 累加(第 9 节的扇入)。
When a variable is read by several statements (a fan-out), nothing special happens going forward; going backward every statement that reads it contributes one term, so the adjoint must accumulate with += (the fan-in of Section 9).
先看最小的一步:一个面的通量
The smallest step first: one face flux
第 3 节的通量有五个输入:两个迹、两个未重构状态和黏性,( 由网格决定,是常数)。对每个输入求偏导,乘以该输入的切向量,再相加:
The flux of Section 3 has five inputs: the two traces, the two raw states and the viscosity, ( is fixed by the mesh). Differentiate it with respect to each input, multiply by that input's tangent and add:
对流部分 与黏性部分 分别是第 3 节 、 的切向量。五个偏导数在第 3 节都已写出;最后一项值得单独说:黏性是设计变量,在通量中乘以差商 ,所以设计种子 直接作用在每一个面上,系数是 ; 和 根本不出现在通量里:本页的边界值 只进入两个约束行(语句 5),面看不到它们。
The convective part and the viscous part are the tangents of the and of Section 3, where all five partial derivatives were written out. The last term deserves a word of its own: the viscosity is a design variable that multiplies the difference quotient in the flux, so a design seed acts directly on every face, with the factor ; and do not appear in the flux at all: on this page the boundary data enter only the two constraint rows (statement 5), and the faces never see them.
手算一个面:取默认收敛解(无限制器线性)的面 ,这里 ,,,。(i)只扰动未知量 (面的左节点):它在 里的权重是 1,在 里是 (斜率 含有它),在 里是 1、在 里是 0,所以 ,,,,,,这正是第 3 节面 Jacobian 的元素 。(ii)只扰动黏性,:所有状态切向量为零,,这是这个面上 的显式路径。然后像 加到 、从 减去一样,把 加到 、从 减去。
Work one face by hand: face of the converged default solution (unlimited linear), where , , and . (i) Seed only the unknown (the left node of the face): its weight is 1 in , in (the slope contains it), 1 in and 0 in , so , , , , and : the entry of the face Jacobian of Section 3. (ii) Seed only the viscosity, : every state tangent vanishes and , the explicit -path of this face. Then is added to and subtracted from , exactly as is added to and subtracted from .
从状态切向量到通量切向量:一个面读哪些未知量
From state tangents to flux tangents: which unknowns a face reads
无限制器线性重构时,迹是未知量的固定线性组合(第 3 节),所以迹的切向量是状态切向量的同一组合。对两个节点都是内部节点的面():
With the unlimited linear reconstruction the traces are fixed linear combinations of the unknowns (Section 3), so their tangents are the same combinations of the state tangents. For a face whose two nodes are both interior ():
四个系数就是第 3 节面 Jacobian 的第 行:面 3 上是 、、、。黏性部分给中间两个系数中左边的加 、右边的加 ,不扩大模板。在默认状态下,每个面 上都有 和 ,所以这两项都被加强;但这不是对每个状态都成立: 可以变正,这时 把它拉向零。只要 ,它的绝对值就变小( 超过 时已经变号;、 时面 6 上 );否则它变号且绝对值变大(同一状态的面 5 上 )。靠近边界时面也没有特例:边界节点的斜率是单侧差 (第 3 节),所以 ,面 0 读 (),面 对称地读 。一阶时 ,每个面只读自己的两个节点 。光滑限制器时 中的权重不再是 ,而是 ,其中 是节点 上随状态变化的偏导数(下表语句 1b; 同理);边界节点的单侧差仍是线性的,所以两个边界节点上限制器什么也不改变。图 34 显示,在默认解的面 3 上,这些权重与 相差很远。
The four coefficients are row of the face Jacobian of Section 3: at face 3 they are , , and . The viscous part adds to the left and to the right central coefficient and widens nothing. At the default state and on every face , so both entries are strengthened; this does not hold for every state: can turn positive, and then pulls it towards zero. Its magnitude shrinks while (the sign already flips once exceeds ; face 6 for , : ); beyond that it changes sign and grows in magnitude (face 5 of the same state: ). Next to a boundary the faces are not special either: the slope at a boundary node is the one-sided difference (Section 3), so , face 0 reads () and face reads symmetrically. At first order and every face reads only its own two nodes . With the smooth limiter the weights in are no longer but with the state-dependent partial derivatives at node (statement 1b in the table below; alike); the one-sided differences of the two boundary nodes stay linear, so the limiter changes nothing at the boundary nodes themselves. Figure 34 shows that at face 3 of the default solution these weights are far from .
逐句求导:残差代码的前向语句
The residual code differentiated statement by statement
前向模式对第 5 节残差代码的每条赋值语句 (这里 是任意初等函数,不是设计的相位 )应用一次链式法则,,紧跟在原语句之后,按同样的顺序执行。下表对本页代码的每条语句都这样做,同时允许状态种子 与设计种子 都不为零(语句 0 是组装前的置零;语句 1c、2、3 在面循环 里):
Forward mode applies the chain rule once to every assignment of the residual code of Section 5 (here is any elementary function, not the phase of the design), , placed after it and executed in the same order. The table does it for every statement of this page's code, with a state seed and a design seed both allowed (statement 0 is the zeroing before the assembly; statements 1c, 2 and 3 run inside the face loop ):
| # | 原始语句Primal statement | 前向语句(同一顺序)Tangent statement (same order) | 所用局部导数Local derivative used |
|---|---|---|---|
| 0 | — | ||
| 1a | |||
| 1b | ;,(一阶:); , (first order: ) | :一阶 ,无限制器 ,光滑限制器随状态变化(第 3 节);两个端点 : first order, unlimited, state-dependent for the smooth limiter (Section 3); at the two end nodes | |
| 1c | |||
| 2 | |||
| 3 | |||
| 4 | (见下式); (below); | ||
| 5 | , 同理;,, alike; , | ,( 相同);, (the same for ); |
语句 4 的精确通量偏导数来自 ,其中 ,,:
The exact-flux partial derivatives of statement 4 come from , with , and :
设计进入代码的地方恰好有三处,每处是表中的一行:(i)边界值 依赖 和 ,只进入两个约束行(语句 5),面循环根本看不到它们;(ii)黏性进入每个面的通量(语句 2),通过 和显式项 ;(iii)精确通量 (语句 4)依赖全部三个分量,通过源项进入每个内部行。取 、,代码返回 ;取 、,返回 的第 列 。除了原始代码本来就有的数组,前向代码还多存斜率的切向量 (s_d)、精确通量的切向量 (Q_d)和输出 (以及两个数 、);局部导数()算出后立即使用,不必存下(存下 的是第 9 节的 residual_b)。
The design enters the code in exactly three places, each a row of the table: (i) the boundary data depend on and and enter only the two constraint rows (statement 5): the face loop never sees them; (ii) the viscosity enters the flux of every face (statement 2), through and the explicit term ; (iii) the exact fluxes (statement 4) depend on all three components and enter every interior row through the source. With and the code returns ; with and it returns the column . Besides the arrays the primal code already has, the tangent code keeps the slope tangents (s_d), the exact-flux tangents (Q_d) and the output (and the two numbers , ); the local derivatives () are used as soon as they are computed and need not be stored (it is the residual_b of Section 9 that keeps ).
为什么是同一顺序:一条前向语句需要它的输入的切向量,而这些切向量由前面原始语句的前向语句算出。所以前向代码可以和原始代码逐句交替、按原始顺序运行;必须倒着运行的是第 9 节的反向模式。
Why the same order: a tangent statement needs the tangents of its inputs, and those are computed by the tangent statements of the earlier primal statements. The tangent code can therefore run interleaved with the primal, statement by statement, in the primal's order; it is the reverse mode of Section 9 that must run backwards.
同一个问题的前向代码在本页更短:前向表有 8 行,格心页有 11 行(两边都把置零算在内),面循环里本页是 3 条语句(1c、2、3),格心页是 5 条(语句 5–9)。格心页里单独处理边界面的两条(面 0 取 ,面 取 )在这里没有对应物:每个面两侧都有节点,取值、重构和通量对所有面是同一段代码,边界值进入单独的约束行(只有分发,即语句 3,不写两个约束行)。下表把两份前向代码放在一起(行数数自两页的表;非零元数是 的收敛默认解上的 ):
The forward code of the same problem is shorter here: its tangent table has 8 rows against 11 on the cell-centred page (the zeroing counted in both), and the face loop has 3 statements (1c, 2, 3) against 5 (statements 5–9). The two statements of the cell page that treat the boundary faces separately (face 0 takes , face takes ) have no counterpart: every face has a node on each side, so gather, reconstruction and flux are the same code for all faces, and the boundary data enter a constraint row of their own (only the scatter, statement 3, skips the two constraint rows). The table sets the two forward codes side by side (the rows are counted in the two tables; the nonzeros are those of at the converged default solution for ):
| 格心页Cell-centred page | 本页(节点)This page (node-centred) | |
|---|---|---|
| 前向表的行数(含置零)rows of the tangent table (zeroing included) | 11 | 8 |
| 面循环里的语句数statements in the face loop | 5(其中两条在面数据上有边界特例)5 (two with a boundary special case in the face data) | 3(面数据没有特例)3 (none in the face data) |
| 面循环face loop | : 个面,其中两个的距离是 : faces, two of them at the distance | : 个面,距离都是 : faces, all at the distance |
| 进入where enter | 两个边界面(作为左、右状态)the two boundary faces (as the left and right state) | 只进入两个约束行 the two constraint rows only |
| 未知量与行unknowns and rows | 个单元平均, 个平衡行 cell averages, balance rows | 个节点值: 个平衡行和 2 个约束行 nodal values: balance rows and 2 constraint rows |
| 建出整个 的前向扫描次数tangent sweeps that build | ||
| 的非零元(一阶 | 无限制器线性)nonzeros of (first order | unlimited linear) | 22 | 34 | 23 | 35 |
写成自动微分工具的代码(原始语句同时执行;a_d, phi_d, nu_d 是 的三个分量,xf[f] 是面的位置 ):
As code, in the names of an AD tool (the primal statements run alongside; a_d, phi_d, nu_d are the three components of , xf[f] is the face position ):
residual_d(U, u_d, D, D_d) -> res, res_d
0 res = 0; res_d = 0; s = 0; s_d = 0 (一阶时 s[0] = s[N] = 0 保持不变)
for j = 1 .. N-1:
1a l = U[j] - U[j-1]; l_d = u_d[j] - u_d[j-1]
r = U[j+1] - U[j]; r_d = u_d[j+1] - u_d[j]
1b s[j] = sigma(l, r); s_d[j] = sl(l, r)*l_d + sr(l, r)*r_d
s[0] = U[1] - U[0]; s_d[0] = u_d[1] - u_d[0] (二阶:端点节点用
s[N] = U[N] - U[N-1]; s_d[N] = u_d[N] - u_d[N-1] 单侧差)
for f = 0 .. N-1:
1c qL = U[f] + s[f]/2; qL_d = u_d[f] + s_d[f]/2; UL = U[f]; UL_d = u_d[f]
qR = U[f+1] - s[f+1]/2; qR_d = u_d[f+1] - s_d[f+1]/2; UR = U[f+1]; UR_d = u_d[f+1]
2 cL = qL/2 + 1; cR = qR/2 - 1; kappa = nu/h
H = (qL^2 + qR^2)/4 - (qR - qL) - nu*(UR - UL)/h
H_d = cL*qL_d + cR*qR_d + kappa*(UL_d - UR_d) - nu_d*(UR - UL)/h
3 if f >= 1: res[f] += H; res_d[f] += H_d
if f <= N-2: res[f+1] -= H; res_d[f+1] -= H_d
for f = 0 .. N-1:
4a Q[f] = q*(xf[f]; D); Q_d[f] = dQda(xf[f])*a_d + dQdphi(xf[f])*phi_d + dQdnu(xf[f])*nu_d
for j = 1 .. N-1:
4b res[j] -= Q[j] - Q[j-1]; res_d[j] -= Q_d[j] - Q_d[j-1]
5 uin = u*(0; D); uout = u*(1; D)
uin_d = -sin(2 pi phi)*a_d - 2 pi a cos(2 pi phi)*phi_d; uout_d = uin_d
res[0] = U[0] - uin; res_d[0] = u_d[0] - uin_d
res[N] = U[N] - uout; res_d[N] = u_d[N] - uout_dresidual_d(U, u_d, D, D_d) -> res, res_d
0 res = 0; res_d = 0; s = 0; s_d = 0 (s[0] = s[N] = 0 stay at first order)
for j = 1 .. N-1:
1a l = U[j] - U[j-1]; l_d = u_d[j] - u_d[j-1]
r = U[j+1] - U[j]; r_d = u_d[j+1] - u_d[j]
1b s[j] = sigma(l, r); s_d[j] = sl(l, r)*l_d + sr(l, r)*r_d
s[0] = U[1] - U[0]; s_d[0] = u_d[1] - u_d[0] (second order: the one-sided
s[N] = U[N] - U[N-1]; s_d[N] = u_d[N] - u_d[N-1] differences of the end nodes)
for f = 0 .. N-1:
1c qL = U[f] + s[f]/2; qL_d = u_d[f] + s_d[f]/2; UL = U[f]; UL_d = u_d[f]
qR = U[f+1] - s[f+1]/2; qR_d = u_d[f+1] - s_d[f+1]/2; UR = U[f+1]; UR_d = u_d[f+1]
2 cL = qL/2 + 1; cR = qR/2 - 1; kappa = nu/h
H = (qL^2 + qR^2)/4 - (qR - qL) - nu*(UR - UL)/h
H_d = cL*qL_d + cR*qR_d + kappa*(UL_d - UR_d) - nu_d*(UR - UL)/h
3 if f >= 1: res[f] += H; res_d[f] += H_d
if f <= N-2: res[f+1] -= H; res_d[f+1] -= H_d
for f = 0 .. N-1:
4a Q[f] = q*(xf[f]; D); Q_d[f] = dQda(xf[f])*a_d + dQdphi(xf[f])*phi_d + dQdnu(xf[f])*nu_d
for j = 1 .. N-1:
4b res[j] -= Q[j] - Q[j-1]; res_d[j] -= Q_d[j] - Q_d[j-1]
5 uin = u*(0; D); uout = u*(1; D)
uin_d = -sin(2 pi phi)*a_d - 2 pi a cos(2 pi phi)*phi_d; uout_d = uin_d
res[0] = U[0] - uin; res_d[0] = u_d[0] - uin_d
res[N] = U[N] - uout; res_d[N] = u_d[N] - uout_d播放器在当前网格上逐面运行这段代码。选一个种子:单位状态种子 、光滑的测试种子 ,或设计种子 ;逐条语句前进,看 一点点填满(虚线框是最终值 )。
The player runs this code on the current mesh, face by face. Choose a seed: a unit state seed , the smooth test seed , or a design seed ; step through the statements and watch fill up (dashed frames: the final values ).
种子:A 的每一列是一次前向扫描
Seeds: every column of A is one tangent sweep
取种子 ,代码返回 ,即 Jacobian 的第 列:所有残差对一个未知量的导数。这次扫描碰到的面是模板含 的那些面(一阶 2 个,端点 1 个;二阶 4 个,节点 与 为 3 个,端点为 2 个),每个面写入它的两行(边界节点的行是约束行,面不写),所以这一列至多有 3 个(一阶)或 5 个(二阶)非零元: 的带宽来自一个面的模板。端点列 另有语句 5 给出的单位元。用 做 次扫描得到整个 ,再用 做三次得到 。下表列出各种子产生非零 的语句和 时 的非零元个数(计数来自前向代码本身):
With the seed the code returns , column of the Jacobian: the derivatives of all residuals with respect to one unknown. The faces it touches are those whose stencil contains (at first order two, and one at an end node; at second order four, three at nodes and , two at the end nodes), and each touched face writes its two target rows (the row of a boundary node is a constraint row, which the faces do not write), so the column has at most 3 (first order) or 5 (second order) nonzero entries: the bandwidth of comes from the stencil of one face. The end columns also hold the unit entry of statement 5. sweeps with the seeds build all of ; three more with build . The table lists, for each kind of seed, the statements that make nonzero and the number of nonzero entries of for (the counts come from the tangent code itself):
| 种子Seed | 产生非零 Ṙ 的语句Statements that make Ṙ nonzero | Ṙ 的非零元个数(一阶 | 无限制器线性)Nonzero entries of Ṙ (first order | unlimited linear) |
|---|---|---|
| 1, 2, 3 | 3 | 5 | |
| 1, 2, 3 | 3 | 4 | |
| 1, 2, 3 | 2 | 3 | |
| 1、2、3 和 5(约束行 )1, 2, 3 and 5 (the constraint row ) | 2 | 3 | |
| 4 和 5(源项与边界值)4 and 5 (the source and the boundary data) | 9 | 9 | |
| 2、3 和 4(每个面的显式项与源项;约束行不动)2, 3 and 4 (the explicit term of every face and the source; the constraint rows do not move) | 7 | 7 |
播放器在当前网格上逐列演示。设计种子 的列是满的:源项进入每个内部行,边界值进入两个约束行; 的列少两行,因为 不进入约束行。
The player steps through these sweeps on the current mesh. The columns of the design seeds are full: the source enters every interior row and the boundary data both constraint rows; the column of lacks two rows, because does not enter the constraint rows.
用本页的数字看两个面
Two faces with the page's own numbers
取默认网格()、设计种子 (振幅 ), 取第 10 节这个方向的切线解(),看内部面 (,节点 3 与 4 之间)和边界面 (,节点 0 与 1 之间)。每种重构有自己的收敛解和自己的 ,所以连原始值也不同:
Take the default mesh (), the design seed (the amplitude ) and for the solution of the tangent solve of Section 10 for that direction (), and look at the interior face (, between nodes 3 and 4) and the boundary face (, between nodes 0 and 1). Each reconstruction has its own converged solution and its own , so even the primal values differ:
| 量Quantity | 公式Formula | 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter |
|---|---|---|---|---|
| 0.953344 | 1.137201 | 1.138732 | ||
| 0.813368 | 1.120554 | 1.115342 | ||
| 0.95334, 0.81337 | 1.16556, 1.05739 | 1.16425, 1.05090 | ||
| 1.4767, −0.5933 | 1.5686, −0.4397 | 1.5694, −0.4423 | ||
| 0.543782 | 0.662517 | 0.667633 | ||
| −0.307277 | 0.694939 | 0.705642 | ||
| −1.104800 | 0.607562 | 0.581346 | ||
| −0.30728, −1.10480 | 0.83744, 0.28691 | 0.83652, 0.24290 | ||
| 0.265550 | 0.866966 | 0.897754 | ||
| 0.891329 | 0.891329 | 0.891329 | ||
| −0.625779 | −0.024362 | 0.006425 |
| 量Quantity | 公式Formula | 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter |
|---|---|---|---|---|
| 0.914844 | 0.992491 | 1.002135 | ||
| 1.022175 | 1.006147 | 1.021959 | ||
| 0.91484, 1.02218 | 0.91484, 1.07014 | 0.91484, 1.08943 | ||
| 0.0800 | 0.0800 | 0.0800 | ||
| 1.4574, −0.4889 | 1.4962, −0.4969 | 1.5011, −0.4890 | ||
| 0.354528 | 0.473263 | 0.478379 | ||
| −0.425779 | −0.039237 | −0.009935 | ||
| 0.141675 | 0.026629 | 0.044071 | ||
| −0.42578, 0.14168 | −0.42578, 0.34731 | −0.42578, 0.40591 | ||
| −0.735203 | −0.133787 | −0.103000 | ||
| −0.109425 | −0.109425 | −0.109425 | ||
| −0.625779 | −0.024362 | 0.006425 |
面 0 是一个普通的面:它的通量由语句 2 从 算出,与其他面一样;一阶时 ,二阶时 (单侧斜率 )。约束行 使收敛解满足 ,切线解保持这一点:对每种重构都有 ( 与格式无关)。 与内部面相同(格心页的边界面上是 ,大一倍)。
Face 0 is an ordinary face: statement 2 computes its flux from like that of every other face; at first order , at second order (the one-sided slope ). The constraint row makes the converged state satisfy , and the tangent solution keeps it so: for every reconstruction ( does not depend on the scheme). equals the interior value (on the cell-centred page the boundary face has , twice as large).
所有面的 还给出一个检验。第 5 节的内部行是面值之差,(),它的切向量也是。当 解出切线方程时,每一行都有 ,所以 在全部 个面上是同一个数:通量切向量跨过对偶控制体 的跳跃恰好等于源项切向量 ,即设计注入这个控制体的量的切向量。这里源项在每个控制体都依赖 、、,所以 每跨过一个控制体都会变,但它与 之差不变(图 36):无限制器线性重构时,在全部 个面上 ()、()、(),三种重构、三个方向的最大偏差是 。原始解有同样的结构(第 5 节):收敛时每个面上的 是同一个常数,无限制器线性重构时为 (一阶 ,光滑限制器 ),离散通量与精确通量只差这一个常数。
The of all faces make one more check. The interior rows of Section 5 are differences of face values, (), and so are their tangents. When solves the tangent equation, in every row, so is the same number on all faces: the flux tangent jumps across the dual volume by exactly the source tangent , the tangent of what the design injects into that volume. Here the source depends on , and in every volume, so changes across every volume, but its difference from does not (Figure 36): for the unlimited linear reconstruction (), () and () on all 8 faces, and the largest spread over the three reconstructions and the three directions is . The primal has the same structure (Section 5): at the converged state is one constant on every face, for the unlimited linear reconstruction ( first order, smooth limiter), the one constant by which the discrete flux misses the exact flux.
约束行的切向量
The constraint rows differentiated
语句 5 是 和 与状态相遇的唯一地方:,。 的第 0 行是 ,第 行是 (面循环从不写这两行); 的第 0 行与第 行相同( 以 1 为周期, 的偏导数与 的相同):
Statement 5 is the only place where and meet the state: and . Row 0 of is and row is (the face loop never writes these rows); rows 0 and of are equal ( has period 1, so has the same partial derivatives as ):
所以这两行里切线方程 的解是 :对振幅是 ,对相位是 ,对黏性是 0。单位行使这些端点值精确成立,面循环与源项只决定中间节点的切向量。
In these two rows the solution of the tangent equation is therefore : for the amplitude, for the phase and 0 for the viscosity. The unit rows make these end values exact; the face loop and the source decide only the tangents of the nodes in between.
图 37 把本页与格心页 的三列并排画出(,无限制器线性,默认设计)。内部行上两份代码减去的是同一个精确通量:本页是对偶控制体上的 ,格心页是单元上的 ,宽度都是 。差别在两端:本页是单位尺度的约束行 ,第 0 行是 ;格心页的端单元行是含边界通量的平衡行,边界值经边界面的系数(含 )进入,第 0 行是 。
Figure 37 draws the three columns of of this page and of the cell-centred page side by side (, unlimited linear, default design). In the interior rows the two codes subtract the same exact fluxes: over a dual volume here, over a cell there, both of width . The two ends differ: this page has the unit-scaled constraint rows , whose row 0 is ; the cell page has end-cell balance rows that contain the boundary flux, the boundary data entering through the coefficients of the boundary face (with ), and its row 0 is .
前向导数与有限差分
The tangent against finite differences
前向导数精确到舍入误差;残差的差商则不然。步长为 时的单侧差商与中心差商是
The tangent is exact up to rounding; a finite-difference quotient of the residual is not. The forward and the central quotient with a step are
的截断误差是 , 的是 ;两者都还有约 的舍入误差( 是机器精度,不是第 3 节限制器的常数 ),其中 (这里约 ;这个 是量级,不是附录 A 的配点数)是残差中相加各项的大小;这个舍入误差即使在 本身为零时(收敛状态正是如此)也不会变小。所以误差随 的双对数图一般呈 V 形:右支是截断,左支是舍入,没有一个 能达到前向导数的精度。
has a truncation error , one of , and both carry a rounding error of about ( is the machine epsilon, not the limiter constant of Section 3), where (about here; this is a size, not the number of collocation points of Appendix A) is the size of the terms summed into a residual; it does not shrink when itself is zero, as at the converged state. So the error against is in general a V in a log-log plot: truncation on the right, rounding on the left, and no reaches the tangent's accuracy.
本页的残差有一个特殊结构,实验会显示出来。一阶或无限制器线性重构时,通量是迹的二次式,迹是 的线性式, 是 的线性式、 是 的二次式, 只以 和 的形式出现:沿 、、 中的任何方向,残差都是 的至多二次的多项式。于是 根本没有截断误差(误差全是舍入,随 增大而减小);只动 、只把全部 平移同一个常数、或两者同时,残差甚至是线性的(平移使每个面的通量多出同一个 ,相邻面的通量之差把它消去),两种差商都没有截断误差。只有含 的方向(正弦、余弦),或者光滑限制器时改变斜率的状态方向(任何非常数的 ;限制器是 的有理函数),才让两种差商都出现完整的 V 形;只动 或 ,或只做常数平移时,光滑限制器的残差仍是二次或线性的,因为斜率不变。实验在当前状态上对 到 计算两种差商,并说明看到的是哪种形状。第 13 节在整个求解上使用这类差商,第 14 节用复数步长去掉舍入那一支。
The residual of this page has a special structure, which the lab shows. With the first-order or the unlimited linear reconstruction the flux is quadratic in the traces, the traces are linear in , is linear and quadratic in , and enters only as and : along any direction in , and the residual is a polynomial of degree at most 2 in . The central quotient then has no truncation error at all (its error is pure rounding, falling as grows), and along alone, along a constant shift of all of alone, or along both, the residual is even linear (a shift adds the same to the flux of every face, and the differences of neighbouring fluxes cancel it), so neither quotient has a truncation error. Only a direction containing (sines and cosines), or, with the smooth limiter, a state direction that changes the slopes (any non-constant : the limiter is a rational function of ), gives both quotients the full V; along or alone, or along a constant shift alone, even the smooth-limiter residual stays quadratic or linear, because the slopes do not change. The lab evaluates both quotients for from to on the current state and says which shape it sees. Section 13 uses such quotients on the whole solve, and Section 14 removes the rounding branch with the complex step.
局部导数怎样组成全局 Jacobian
How local derivatives form the global Jacobian
网格固定时,令 收集面 的通量的五个输入。它们来自这个面读的未知量(集合 :二阶时内部面 读节点 ,面 0 与面 读 3 个节点;一阶时读节点 )和设计。记 、 为 对这些未知量和对 的导数, 为五个通量导数组成的行, 为面与行的有向关联矩阵( 行 列:(),():面把通量加给左边的对偶控制体、从右边的减去;边界节点的行 0 与行 不由面写入,所以 的第一行和最后一行是零)。源项 ()用的是同一个关联矩阵,,再加上两个约束行,所以:
At a fixed mesh, let collect the five inputs of the flux of face . They come from the unknowns the face reads (the set : at second order the nodes for and three nodes for and ; at first order the nodes ) and from the design. Let and be the derivatives of with respect to those unknowns and to , the row of the five flux derivatives, and the oriented incidence of faces and rows ( rows, columns: (), (): a face adds its flux to the dual volume on its left and subtracts it from the one on its right; the rows 0 and of the boundary nodes are not written by the faces, so the first and the last row of are zero). The source () uses the same incidence, , and the two constraint rows are added, so that
里没有 一项:本页的源项只依赖设计;除了面的和, 只多出两个约束行的单位元。 在每个面上(边界面也一样)只有一行非零,即 那一行 ,所以 。设计的三条路径就是三项:每个面上的 乘 ,每个内部行的 ,以及两个约束行里的 、。图 38 画出 的和式中的一项。
There is no term in : the source of this page depends on the design only; besides the sum over the faces, gets only the unit entries of the two constraint rows. has one nonzero row on every face, boundary faces included, the -row , so . The three paths of the design are three terms: times on every face, in every interior row, and , in the two constraint rows. Figure 38 draws one term of the sum for .
从程序到前向导数:逐条线性化面循环的四步
From program to tangent: linearise the four steps of the face loop
上面的 、、 来自一段短程序:面循环在每个面上做四件事(语句 1c、2、3 以及它们读的斜率)。把这个面读的未知量排成 :
The , and above come from one short program: on every face the loop does four things (statements 1c, 2 and 3 and the slopes they read). Stack the unknowns the face reads as :
(1)取值: 的每一行只有一个 1,从全局数组中取出 的一个未知量。(2)重构:斜率、迹 、未重构状态 和黏性,即通量的全部五个输入。(3)通量。(4)分发: 的第 列,第 行为 ,第 行为 (边界节点的行除外)。前向模式对每条语句用一次链式法则,每个中间变量都有自己的切向量:
(1) Gather: each row of holds a single 1 and picks one unknown of out of the global array. (2) Reconstruct: the slopes, the traces , the raw states and the viscosity, all five inputs of the flux. (3) Flux. (4) Scatter: column of , in row and in row (except in the rows of the boundary nodes). Forward mode applies the chain rule once per statement, and every intermediate variable gets a tangent of its own:
取值和分发是线性的:它们的切向量就是把同样的取值和分发作用在切向量上。只有重构和通量带来依赖当前状态的局部导数( 总是如此, 只在光滑限制器时如此)。把(1)代入(2)、(2)代入(3)、(3)代入(4),一个面对 的贡献就是四个初等 Jacobian 的乘积,按程序运行的顺序从右向左作用:
Gather and scatter are linear: their tangents are the same gather and scatter applied to the tangents. Only reconstruct and flux bring local derivatives that depend on the current state ( with the smooth limiter, always). Substituting (1) into (2), (2) into (3) and (3) into (4) gives one face's contribution to as a product of four elementary Jacobians, applied right to left in the order the program runs:
算例:面 3 的前向扫描。设计种子取 (相位 ),未知量取同一方向的切线解 。 不直接进入任何一个面(每个面上 ),所以 完全来自状态切向量。数字(默认设计,无限制器线性):
A worked example: the forward sweep of face 3. Seed the design with (the phase ) and the unknowns with the tangent solution of the same direction, . The phase does not enter any face directly ( on every face), so comes entirely from the state tangents. The numbers (default design, unlimited linear):
| 语句Statement | 量Quantity | 原始值Primal value | 切向量Tangent |
|---|---|---|---|
| (1)取值(1) gather | 1.17081 | −0.21066 | |
| 1.16556 | 0.72421 | ||
| 1.05739 | 1.37729 | ||
| 0.91290 | 1.35702 | ||
| 设计种子design seed | 0.07 | 1 | |
| (2)重构(2) reconstruct | 1.13720, 1.12055 | 1.12120, 1.21908 | |
| 1.16556, 1.05739 | 0.72421, 1.37729 | ||
| 0.01 | 0 | ||
| (3)通量(3) flux | 0.66252 | 1.17041 | |
| (4)分发(4) scatter | 0.66252 | 1.17041 | |
| −0.66252 | −1.17041 |
对所有面、源项行和约束行求和,这些贡献给出 ,这就是切线方程。若取 (),这个面还会多出 带来的 一项。下面的播放器对任意面、任意方向重放这四步。
Summed over all faces, the source rows and the constraint rows, these contributions give : the tangent equation. With () this face would get the extra term from . The player below replays the four steps for any face and any direction.
前向求解用它做什么
What the tangent solve does with it
一次 residual_d 扫描是乘积,不是求解。第 10 节用它解切线方程,求出使 沿设计方向保持成立的状态切向量 :取种子 ,解 ,每个设计分量解一次;目标沿这个方向的导数是 。上面面表里的 就是这样的解,所以每个 都为零(上面检验的九个种子中最大的 是 )。
A sweep of residual_d is a product, not a solve. Section 10 uses it to solve the tangent equation for the state tangent that keeps along a design direction: with the seed it solves , one solve per design component; the derivative of the objective along that direction is . The of the face tables above was such a solution: that is why every vanished (the largest over the nine seeds of the check above is ).
伴随:把同一个面循环倒过来
Reverse: the adjoint of the same face loop
本节实验:给一个残差一个单位权重 ,运行反向代码后,哪些未知量会收到贡献?三个设计分量里哪些会?先从前向模板预测,再用反向播放器(或后面的“列与行”实验)检查。
Experiment: Put a unit weight on one residual, , and run the reverse code: which unknowns receive a contribution, and which of the three design components? Predict from the forward stencil, then check with the reverse player (or the row-and-column lab further down).
第 8 节把一个方向 沿残差代码向前推,得到 。本节把一个权重向量 沿同一段代码向后拉,得到 和 :这是反向(伴随)模式。取 (第 11 节伴随方程的解)时,一次扫描得到的 就是整个梯度 。
Section 8 pushed a direction forward through the residual code and obtained . This section pulls a weight vector backwards through the same code and obtains and : the reverse (adjoint) mode. With , the solution of the adjoint equation of Section 11, one sweep gives the whole gradient as .
伴随为什么存在:转置由内积定义
Why the adjoint exists: the transpose is defined by a dot product
线性映射 的转置 就是把它从内积的一边移到另一边的那个映射:对所有 和 ,
A linear map has a transpose , the map that moves it from one side of a dot product to the other: for all and ,
把这个定义用到第 8 节的线性映射 上。这个映射的转置把残差上的权重 映成输入上的权重 、,定义式就成了自动微分的对偶恒等式:
Apply this to the linear map of Section 8, . Its transpose maps a weight on the residuals to weights on the inputs, and , and the definition becomes the duality identity of AD:
是加权输出 对 的灵敏度。反向代码逐句转置程序来计算 和 ,不形成 或 ;它的代价是一次残差计算的一个不大的常数倍,与输入的个数无关(在本页的实现中实测约为 1 倍;后面“解出来的伴随,以及它省下的代价”一段末尾的计时实验会在你的浏览器里测量)。这个恒等式就是它的检验(后面的点积检验)。
is the sensitivity of the weighted output to . The reverse code computes and by transposing the program statement by statement, without forming or ; its cost is a small constant multiple of one residual evaluation, independent of the number of inputs (about one for this page's implementation, as measured; the cost lab at the end of “The solved adjoint, and what it saves” measures it in your browser). The identity is its test (the dot test further down).
先转置一个面的局部矩阵
First transpose the local matrix of one face
先不管重构。一个面有五个输入、两个输出:它对残差切向量的贡献 只落在第 、 两行,第二行是第一行的相反数(右边的控制体收到 );边界面只写其中一行,因为边界节点的行属于约束。这个贡献不是完整的 :相邻的面也往这两行写。把这个 局部矩阵的转置作用在两行的权重上,直接得到反向语句:先把两个种子相减,再乘以五个通量导数:
Leave the reconstruction aside for a moment. One face has five inputs and two outputs: its contribution to the residual tangent falls on the rows and only, the second the negative of the first (the right volume receives ); a boundary face writes only one of the two, because the rows of the boundary nodes belong to the constraints. It is not the whole of : the neighbouring faces write into the same rows. The transpose of this local matrix, applied to the weights of the two rows, gives the reverse statements directly: subtract the two seeds, then multiply by the five flux derivatives:
取默认解的面 3,两行的权重用第 11 节伴随解的值 、,于是 ,,,,,。配上第 8 节手算的切向量(种子 :,,,,),恒等式两边在这个面上相等(左边只是面 3 对第 3、4 行的贡献;完整的 还含面 2、面 4 的贡献):
Take face 3 of the default solution and, as the weights of its two rows, the values of the adjoint solution of Section 11, and : then , , , , and . With the hand-worked tangents of Section 8 (seed : , , , , ) both sides of the identity agree on this face (the left side is face 3's share of rows 3 and 4 only; the full also contain the contributions of faces 2 and 4):
逐句倒推:残差代码的反向语句
The residual code reversed statement by statement
下表对第 8 节表中的每条语句写出它的伴随语句 ,从最后一条原始语句倒回第一条;在面循环里,面按 倒序处理,每个面里的语句也倒着执行。种子是 ,其余伴随量(包括每个面、每个节点上的局部伴随 )都从零开始:
The table lists, for every statement of the table of Section 8, its adjoint statements , from the last primal statement back to the first; inside the face loop the faces run from down to 0 and the statements of each face also run backwards. The seed is ; every other adjoint (the local ones of each face and node, , included) starts at zero:
| # | 原始语句Primal statement | 伴随语句(倒序执行)Adjoint statements (run in reverse order) | 写入的伴随量Adjoints written |
|---|---|---|---|
| 5 | ,;,;再 ,, ; , ; then , | ||
| 4 | ,();再 (), (); then () | ||
| 3 | ;,(面循环 ); , (face loop ) | ||
| 2 | ,,,,, , , , | ||
| 1c | ,;,, ; , | ||
| 1b | ,;, ; | ,;,(二阶);,(,在面循环之后), ; , (second order); , (, after the face loop) | |
| 1a | |||
| 0 | 无: 是种子(扫描前 ,)nothing: is the seed (, before the sweep) | — |
有四点值得注意。(i)分发的伴随是取值:语句 3 把一个数写进两行,它的伴随 把两行读回一个数(边界面只有一行可读)。(ii)每个输入的伴随都用 += 累加: 被几个面读(还经过斜率), 被两个面读,设计被每个面和每一行读;链式法则对每次使用给出一项(见下图)。(iii)源项行也由同一个取值倒转:(约束行 0 与行 不参与),所以源项给设计伴随的贡献是 。(iv)约束行是 的唯一出口:,,减号来自 里的 ;语句 5 用了 ( 以 1 为周期)。反向语句要读的原始值()由收敛解 重算(或在一次前向扫描中存下)。
Four things to notice. (i) The adjoint of a scatter is a gather: statement 3 wrote one number into two rows, and its adjoint reads two rows back into one number (a boundary face has only one row to read). (ii) Every input adjoint accumulates with +=: is read by several faces (and through the slopes), by two faces, the design by every face and every row; the chain rule gives one term per use (the figure below). (iii) The source rows are reversed by the same gather: (the constraint rows 0 and do not enter), so the source contributes to the design adjoint. (iv) The constraint rows are the only exit of : and , the minus sign coming from the of ; statement 5 uses ( has period 1). The primal values the reverse statements read () are recomputed from the converged solution (or stored by a forward sweep).
写成自动微分工具生成的 residual_b:
As the residual_b an AD tool would generate:
residual_b(U, D, res_b) -> u_b, D_b # D_b = (a_b, phi_b, nu_b)
(原始语句 1a-1b 先运行:s[j] 以及斜率的偏导数 sl[j]、sr[j])
0 u_b = 0; D_b = 0; s_b = 0; Q_b = 0; uin_b = 0; uout_b = 0
5' u_b[0] += res_b[0]; uin_b -= res_b[0]
u_b[N] += res_b[N]; uout_b -= res_b[N]
a_b += -sin(2 pi phi)*(uin_b + uout_b)
phi_b += -2 pi a cos(2 pi phi)*(uin_b + uout_b)
for j = N-1 .. 1:
4b' Q_b[j] -= res_b[j]; Q_b[j-1] += res_b[j]
for f = N-1 .. 0:
4a' a_b += dQda(xf[f])*Q_b[f]; phi_b += dQdphi(xf[f])*Q_b[f]; nu_b += dQdnu(xf[f])*Q_b[f]
for f = N-1 .. 0:
(重算面 f 的 qL、qR、UL、UR、cL、cR:原始语句 1c 与 2)
3' H_b = 0
if f >= 1: H_b += res_b[f]
if f <= N-2: H_b -= res_b[f+1]
2' qL_b = cL*H_b; qR_b = cR*H_b; UL_b = nu/h*H_b; UR_b = -nu/h*H_b
nu_b += -(UR - UL)/h*H_b
1c' u_b[f] += qL_b + UL_b; s_b[f] += qL_b/2
u_b[f+1] += qR_b + UR_b; s_b[f+1] -= qR_b/2
1b' u_b[1] += s_b[0]; u_b[0] -= s_b[0] (二阶:两个端点节点)
u_b[N] += s_b[N]; u_b[N-1] -= s_b[N]
for j = N-1 .. 1:
1b' l_b = sl[j]*s_b[j]; r_b = sr[j]*s_b[j]
1a' u_b[j] += l_b - r_b; u_b[j-1] -= l_b; u_b[j+1] += r_bresidual_b(U, D, res_b) -> u_b, D_b # D_b = (a_b, phi_b, nu_b)
(the primal statements 1a-1b run first: s[j] and the slope partials sl[j], sr[j])
0 u_b = 0; D_b = 0; s_b = 0; Q_b = 0; uin_b = 0; uout_b = 0
5' u_b[0] += res_b[0]; uin_b -= res_b[0]
u_b[N] += res_b[N]; uout_b -= res_b[N]
a_b += -sin(2 pi phi)*(uin_b + uout_b)
phi_b += -2 pi a cos(2 pi phi)*(uin_b + uout_b)
for j = N-1 .. 1:
4b' Q_b[j] -= res_b[j]; Q_b[j-1] += res_b[j]
for f = N-1 .. 0:
4a' a_b += dQda(xf[f])*Q_b[f]; phi_b += dQdphi(xf[f])*Q_b[f]; nu_b += dQdnu(xf[f])*Q_b[f]
for f = N-1 .. 0:
(recompute qL, qR, UL, UR, cL, cR of face f: statements 1c and 2 of the primal)
3' H_b = 0
if f >= 1: H_b += res_b[f]
if f <= N-2: H_b -= res_b[f+1]
2' qL_b = cL*H_b; qR_b = cR*H_b; UL_b = nu/h*H_b; UR_b = -nu/h*H_b
nu_b += -(UR - UL)/h*H_b
1c' u_b[f] += qL_b + UL_b; s_b[f] += qL_b/2
u_b[f+1] += qR_b + UR_b; s_b[f+1] -= qR_b/2
1b' u_b[1] += s_b[0]; u_b[0] -= s_b[0] (second order: the end nodes)
u_b[N] += s_b[N]; u_b[N-1] -= s_b[N]
for j = N-1 .. 1:
1b' l_b = sl[j]*s_b[j]; r_b = sr[j]*s_b[j]
1a' u_b[j] += l_b - r_b; u_b[j-1] -= l_b; u_b[j+1] += r_b约束行的反向语句
The constraint rows reversed
语句 5 是反向代码最先遇到的语句。以 为种子:,,节点 同理( 是两个约束行的乘子,默认解里 ,)。于是 ,,再经 给设计伴随最先的两项:,。源项(语句 4′)和面通量里的黏性(语句 2′)补上其余部分;下表按语句拆开最终的 (无限制器线性,默认设计)。与前向对照:前向代码里 流进 ,反向代码里 经 流向设计。
Statement 5 is the first statement the reverse code meets. With the seed : and , and likewise at node ( and are the multipliers of the two constraint rows; at the default solution and ). So and , and through they give the design adjoint its first two terms, and . The source (statement 4′) and the viscous term of the face fluxes (statement 2′) add the rest; the table splits the final by statement (unlimited linear, default design). Compare with the forward code: there flows into , here flows through into the design.
| # | 加到 D̄ 的语句Statement that adds to D̄ | |||
|---|---|---|---|---|
| 5′ | 边界值 (经约束行)the boundary data (through the constraint rows) | −0.635725 | −1.697698 | 0.000000 |
| 4′ | 精确通量 (经源项)the exact fluxes (through the source) | 0.457664 | 2.117602 | 2.180107 |
| 2′ | 面通量里的黏性 the viscosity of the face fluxes | 0.000000 | 0.000000 | −2.176198 |
| 合计sum | −0.178061 | 0.419904 | 0.003909 |
三部分符号不同,部分抵消:默认解中仅数据语句给 ,源项给 ,合起来是 ;对 ,源项()与面通量()几乎抵消,剩 。第 12 节按设计进入残差的位置进一步拆分梯度。
The three parts have different signs and partly cancel: at the default solution the data statement alone gives , the source , together ; for the source () and the face fluxes () nearly cancel and leave . Section 12 splits the gradient further by the places where the design enters the residual.
约束行的乘子本身也有含义:随着 增大, 趋向连续伴随 (附录 A)在两端的边界通量 与 ;独立的 Python 运行给出 时 、, 时 、。这是数值观察,不是定理。
The multipliers of the constraint rows have a meaning of their own: as grows, and approach the boundary fluxes and of the continuous adjoint (Appendix A); the independent Python runs give and at , and and at . This is a numerical observation, not a theorem.
为什么用 +=:程序的扇出就是反向代码的扇入
Why +=: a fan-out of the program is a fan-in of the reverse code
二阶时,内部节点 ()被四个面读:作为面 的右状态和面 的左状态,此外还通过斜率 被面 和面 读(四个面里共 6 个迹、2 个未重构状态); 被三个面读,边界节点 被两个面读,并且还出现在自己的约束行里;一阶时每个内部节点被两个面读,边界节点被一个面读。链式法则对每次使用给出一项,所以 是一个和;每条反向语句必须把自己的项加上去(+=),而不是覆盖前面的项:
At second order an interior unknown () is read by four faces: as the right state of face and the left state of face , and through the slopes by the faces and as well (six traces and two raw states in all); and are read by three faces, the boundary nodes and by two faces and, besides, by their own constraint row; at first order every interior node is read by two faces and a boundary node by one. The chain rule gives one term per use, so is a sum, and every reverse statement must add its term (+=) instead of overwriting the earlier ones:
取种子 时这个和是 (第 11 节),所以扇入有已知的总和:在默认解的节点 4 上,四个面加上 、、、,和为 (图 40)。在边界节点 0 上,约束行先给出 ,面 0 和面 1 再加上 与 ,和是 :乘子 的大部分被面的项抵消。如果用覆盖代替累加,只会留下最后一个面的项:下面的点积检验实验可以植入这个错误。
With the seed the sum is (Section 11), so the fan-in has a known total: at node 4 of the default solution the four faces add , , , , which sum to (Figure 40). At the boundary node 0 the constraint row contributes first, faces 0 and 1 add and , and the sum is : most of the multiplier is cancelled by the face terms. Overwriting instead of adding keeps only the last face's term: the dot-test lab below lets you plant this bug.
分发的伴随是取值,取值的伴随是分发
The adjoint of a scatter is a gather (and of a gather, a scatter)
这是最该记住的一幅图。前向面循环读入这个面的未知量(取值),把通量写进两行残差(分发)。反向面循环读入两个残差权重(取值,即分发的转置),把这个面的伴随写回它读过的未知量的位置(带 += 的分发,即取值的转置)。循环还是同一个循环,只是两端交换了角色:
This is the picture to remember. The forward face loop reads the unknowns of the face (gather) and writes its flux into two residual rows (scatter). The reverse face loop reads two residual weights (a gather: the transposed scatter) and writes the face's adjoints back into the places of the unknowns it read (a scatter with +=: the transposed gather). It is the same loop; its two ends exchange their roles:
重构的转置也是同样的做法:前向用固定的权重(无限制器线性时 )把节点值合成迹,反向把迹的伴随用同样的权重分回节点。对内部面():,,,:前向矩阵 的转置,每个权重原样出现,只是乘在伴随上。面 3 上这四个份额是 、、、(图 41)。光滑限制器把这些常数换成状态相关的 ,边界节点的单侧斜率仍是 ,。
The reconstruction is transposed the same way: the forward code combines nodal values into traces with fixed weights ( for the unlimited linear reconstruction), the reverse code distributes the trace adjoints back to the nodes with the same weights. For an interior face (): , , , : the transpose of the forward matrix , every weight appearing unchanged but now multiplying an adjoint. At face 3 the four shares are , , and (Figure 41). The smooth limiter replaces these constants by the state-dependent , and the one-sided slope of a boundary node stays , .
面与面之间不读对方的结果:每个面只读固定的状态和种子,把自己的贡献用 += 加进共用的数组 、、。加法可交换(舍入误差除外),所以反向循环中各个面的先后顺序可以任意(点积实验提供正序 这一选项,检验照样通过)。必须倒过来的是相互依赖的语句的顺序:分发在通量之前,通量在重构之前;斜率语句在原始代码中位于面循环之前,在反向代码中必须位于面循环之后,等读 的面(内部节点两个,端点一个)都把贡献加进 之后再倒转。
The faces do not read each other's results: each face reads only the fixed state and the seed and adds its contribution (+=) into the shared arrays , and . Addition is commutative (up to rounding), so the face order of the reverse loop is free (the dot-test lab offers the forward order , and the test still passes); what must be reversed is the order of the statements that depend on each other: the scatter before the flux, the flux before the reconstruction; and the slope statements, which run before the face loop in the primal, run after it in reverse, once the faces that read (two for an interior node, one for an end node) have added their parts to .
反向播放器在当前网格上运行 residual_b。选种子(单位行 、目标的权重 、伴随解 或随机向量),从约束行开始,经过源项行和面 ,到斜率;柱条显示 和 的累加,刚更新的分量加框,虚线框是最终值 、。
The reverse player runs residual_b on the current mesh. Choose the seed (a unit row , the weights of the objective, the adjoint solution , or a random vector) and step from the constraint rows through the source rows and the faces to the slopes; the bars show and accumulating, the entries just updated framed, the final values and dashed.
用本页的数字反向走同样的面
The same faces in reverse, with the page's numbers
继续看第 8 节的面 3 和面 0,现在以第 11 节伴随方程的解为种子,。反向语句先把两个种子汇集成 ,再乘以同样的五个偏导数;最后两行把它们与第 8 节面表的切向量(种子 )配对:
Continue with the faces 3 and 0 of Section 8, now seeded with the solution of the adjoint equation of Section 11, . The reverse statements first gather the two seeds into , then multiply by the same five partial derivatives; the last two rows pair them with the tangents of the face table of Section 8 (seed ):
| 量Quantity | 公式Formula | 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter |
|---|---|---|---|---|
| −0.117835 | −0.146621 | −0.134035 | ||
| −0.174004 | −0.229989 | −0.210349 | ||
| 0.069914 | 0.064472 | 0.059287 | ||
| −0.009427 | −0.011730 | −0.010723 | ||
| −0.131953 | −0.126877 | −0.121541 | ||
| 输出端output side | −0.031291138 | −0.127115165 | −0.120330005 | |
| 输入端input side | −0.031291138 | −0.127115165 | −0.120330005 |
| 量Quantity | 公式Formula | 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter |
|---|---|---|---|---|
| 0.875879 | 1.709478 | 1.620440 | ||
| 1.276526 | 2.557799 | 2.432390 | ||
| −0.428228 | −0.849485 | −0.792429 | ||
| 0.070070 | 0.136758 | 0.129635 | ||
| −0.752072 | −2.123773 | −2.263204 | ||
| 输出端output side | −0.643949304 | −0.228706255 | −0.166904925 | |
| 输入端input side | −0.643949304 | −0.228706255 | −0.166904925 |
面 0 只有一行可读:(行 0 是约束行,不由面写入),它的两个左侧输入的伴随和其他面一样加进 与 。每个面上输出端与输入端在舍入误差内相等,这就是局部对偶。对整个代码求和时它有了意义: 解出切线方程,所以 ; 解出伴随方程,所以 。恒等式变成 ,即 ,也就是说,伴随给出的梯度分量等于前向给出的方向导数。无限制器线性时两者都是 (一阶 ,光滑限制器 );三种重构、三个分量上 与 相差不超过 。
Face 0 has only one row to read: (row 0 is a constraint row, which no face writes), and the adjoints of its two left inputs go into and like those of every other face. On each face the output and the input side agree to rounding: the local duality. Summed over the whole code it carries meaning: solves the tangent equation, so ; solves the adjoint equation, so . The identity then reads , that is : the gradient component from the adjoint equals the directional derivative from the tangent. For the unlimited linear reconstruction both are (first order , smooth limiter ); over the three reconstructions and the three components and differ by at most .
点积检验
The dot test
对偶恒等式也是检验反向代码的标准方法:取随机种子 和 ,用前向代码算 ,用反向代码算 ;两边必须在舍入误差内相等,而反向语句中一处错误的转置会让两边相差远大于舍入误差。种子要随机:例如取 时, 里的错误根本看不见。图 42 用一组固定的(非随机)种子把它拆成三个层次:残差层 ;语句层 再加两个约束行的 (因为 加约束行);输入层 。逐项不同,总和在舍入误差内相同。
The duality identity is also the standard test of a reverse code: take random seeds and , run the forward code for and the reverse code for ; the two sides must agree to rounding, and one wrong transpose in the reverse statements makes them differ far beyond rounding. The seeds must be random: with , for instance, an error in would be invisible. Figure 42 splits it into three levels with deterministic seeds: the residual level ; the statement level plus the two constraint terms (because plus the constraint rows); and the input level . Term by term the levels differ; the totals agree to rounding.
实验在当前网格、重构和设计上用随机种子重复它,还可以植入手写伴随代码里常见的错误:漏掉或写反的项出现在面循环里(把 += 写成 =、黏性偏导的符号、漏掉 、漏掉斜率的倒转),也出现在格点格式特有的语句里(漏掉端点的单侧斜率、漏掉约束行、约束行里 的符号写反、漏掉边界值对 的贡献),以及漏掉源项行。在默认设计下,对实验的全部网格与种子( 到 、三种重构、每种 20 组种子),正确代码两边的相对差至多为 ;植入错误后,两边的相对差在 到 之间(最小的是漏掉 :它只影响一个设计分量)。实验也说明为什么正序运行面循环不是错误,以及一阶时漏掉斜率的倒转(内部节点或端点)为什么不会被发现()。
The lab repeats it with random seeds on the current mesh, reconstruction and design. It also lets you plant one of the bugs a hand-written adjoint typically has: terms lost or reversed inside the face loop (= for +=, the sign of the viscous partials, a forgotten , the forgotten reversal of the slopes), bugs in the statements that are special to the node scheme (the forgotten one-sided slope of the end nodes, the forgotten constraint rows, the wrong sign of in them, the forgotten contribution of the boundary data to ), and the forgotten source rows. At the default design, over the lab's meshes and seeds ( to , the three reconstructions, 20 seed sets each) the two sides of the correct code differ by at most (relative), those of the planted bugs by between and (the smallest is the forgotten , which affects one design component only). The lab also shows why running the faces in forward order is not a bug, and why forgetting the slope reversal (interior or end nodes) goes unnoticed at first order ().
点积检验只能说明反向代码是前向代码的转置,不能说明二者都是残差的导数:如果两者漏掉同一项(比如斜率),检验照样通过。第 8 节中前向导数与差商的对比,以及第 14 节的复数步长,都把前向代码与残差本身对照。
The dot test checks that the reverse code is the transpose of the forward code, not that either is the derivative of the residual: if both forget the same term (say, the slopes) the test still passes. The comparison with finite differences in Section 8 and the complex step of Section 14 check the forward code against the residual itself.
大网格上的同一组恒等式
The same identities on large meshes
上面的实验用浏览器里的稠密求解,网格到 为止。本格式的独立稀疏 Python 实现(附录 C)在 到 上检验同样的转置关系。每次运行中,反向扫描与伴随求解给出的梯度相差至多 (二者用同一个 );另外三个量中前两个取决于线性求解本身的精度,最后一个只涉及存储的矩阵:,即伴随方程的残差,至多 ;三次前向求解与一次伴随求解的梯度相差至多 ;转置恒等式 (固定的测试向量 、;这是存储的矩阵 本身的恒等式,不是对 residual_b 的检验;误差以 为分母)至多成立到 (图 43)。这三个量随 增大: 时三者中最大的不超过 ,最大的运行()上是 。这是数值证据:它说明在这些网格上恒等式至少成立到 ,不证明任何东西。
The labs above use the dense solves of the browser and stop at . The independent sparse Python implementation of this scheme (Appendix C) checks the same transposition statements at to . In every run the reverse sweep and the adjoint solve give gradients that differ by at most (both use the same ); of the other three quantities the first two depend on the accuracy of the linear solves and the last involves only the stored matrix: , the residual of the adjoint equation, holds to at most ; the gradients of the three tangent solves and of the one adjoint solve differ by at most ; and the transposition identity for the fixed test vectors , (an identity of the stored matrix , not a test of residual_b; the error is divided by ) holds to at most (Figure 43). These three quantities grow with : at the largest of them is at most , in the largest run () it is . This is numerical evidence: it shows that the identities hold to at least on these meshes and proves nothing.
一列与一行:同一个矩阵的两种读法
A column and a row: one matrix read two ways
取种子 ,一次前向扫描返回 的第 列(第 8 节);取种子 ,一次反向扫描返回 ,即 的第 行,以及 ,即 的第 行。 次前向扫描或 次反向扫描得到同一个矩阵,一个按列读,一个按行读。实验逐步做两种扫描并逐元素比较。前向模式适合输入少、输出多的情形,反向模式适合输入多、输出少的情形:一个目标、许多设计变量。
With the seed one forward sweep returns column of (Section 8); with the seed one reverse sweep returns , row of , and , row of . forward sweeps or reverse sweeps build the same matrix, read by columns or by rows. The lab steps through both and compares them entry by entry. Forward mode suits few inputs and many outputs, reverse mode many inputs and few outputs: one objective and many design variables.
解出来的伴随,以及它省下的代价
The solved adjoint, and what it saves
目标沿设计方向的导数是 ,其中 (第 8 节)。写成 ,再把逆矩阵移到内积的另一边:,其中 ,也就是
The derivative of the objective along a design direction is with (Section 8). Write and move the inverse to the other side of the dot product: with , that is
( 不直接依赖设计,所以梯度里没有 一项。)反向代码提供这个乘积:以 为种子,它返回 (在解处等于 ,可作检验:三种重构的最大偏差是 )和 ,即梯度行的转置:一次扫描得到全部三个分量,其中包括约束行里 那两项。前向代码每个设计分量需要一次扫描(以及一次求解 ,第 10 节);反向代码不论有多少设计变量,只需一次伴随求解和一次扫描。这里有 3 个分量,所以是 3 比 1;对于有几百个设计变量的外形设计,就是几百比 1。第 11 节解伴随方程,第 12 节按设计进入残差的位置拆分梯度。图 44 把两条路线并排画出;下面的实验在你的浏览器里测量一次扫描相当于几次残差计算。
( has no direct design dependence, so the gradient has no term.) The reverse code supplies the product: seeded with it returns (equal to at the solution, a check: the largest deviation over the three reconstructions is ) and , the transpose of the gradient row: all three components from one sweep, the two terms of the constraint rows included. The forward code needs one sweep per design component (and one solve each, Section 10); the reverse code needs one adjoint solve and one sweep, whatever the number of design variables. Here there are 3 components, so the saving is 3 to 1; for a shape with hundreds of design variables it is hundreds to one. Section 11 solves the adjoint equation, and Section 12 splits the gradient by the places where the design enters the residual. Figure 44 draws the two routes side by side; the lab below measures, in your browser, how many residual evaluations one sweep costs.
从前向到伴随:转置让语句倒着执行
From tangent to adjoint: the transpose runs the statements backwards
先说明反向累加为什么给出矩阵的转置:展平之后,下面只是交换两个有限和( 同理):
First, why reverse accumulation gives the matrix transpose: after flattening, the following merely exchanges two finite sums ( alike):
一个面的前向导数是四个初等 Jacobian 的乘积(第 8 节)。乘积的转置是各转置按相反顺序相乘:
The tangent of one face is the product of four elementary Jacobians (Section 8). The transpose of a product is the product of the transposes in the reverse order:
从右往左读,就是反向扫描的顺序:程序最后执行的分发最先倒转,最先执行的取值最后倒转。把所有伴随置零,以 为种子,对每个面执行:
Read 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 , and run for every face:
源项行 也按同样的规则倒转:,(没有状态项:);两个约束行按 、 倒转。对偶恒等式逐级成立:每一步用一次转置的定义,把一个初等 Jacobian 移到内积的另一边,所以每一级的内积都相同:
The source rows are reversed by the same rule: , (no state term: ); the two constraint rows are reversed by , . The duality identity holds stage by stage: each step uses the definition of the transpose once to move one elementary Jacobian across the dot product, so the inner product is the same at every stage:
对所有面求和,再加上源项一对 和约束行的两对 ,右边正好累加成反向扫描的输出:。
Summed over the faces, with the source pair and the two constraint pairs added, the right side accumulates exactly into the outputs of the reverse sweep: .
算例:面 3 的反向扫描,种子 ,与第 8 节末尾的前向算例(种子 ,相位)配对。最后一列是每一级的内积;第一级 只用面 3 对第 3、4 行的贡献 ,而不是完整的 (后者在这个种子下为零):
A worked example: the reverse sweep of face 3 with the seed , paired with the forward example at the end of Section 8 (seed , the phase). The last column is the inner product at each stage; the first stage, , uses face 3's contributions to rows 3 and 4 only, not the full (which vanish for this seed):
| 语句Statement | 量Quantity | 伴随值Adjoint | 内积Inner product |
|---|---|---|---|
| (0)置零(0) zero | 0 | ||
| (4′)种子(4′) seed | −1.05504, −0.90842 | −0.1716056056 | |
| (4′)取值(4′) gather | −0.146621 | −0.1716056056 | |
| (3′)通量的转置(3′) flux, transposed | −0.229989, 0.064472 | −0.1716056056 | |
| −0.011730, 0.011730, −0.126877 | |||
| (2′)(1′)加回(2′)(1′) add back | 0.057497, −0.225601 | −0.1716056056 | |
| 0.018705, −0.016118 | |||
| 0 |
四个内积在舍入误差内相等(最大相对差 ),对偶在这个面上逐级成立。面 3 不直接依赖 (任何面都不直接依赖),所以它对 的贡献是 0; 只经过约束行和源项行进入 。这个面的 虽然不为零,但种子 的 ,所以 不进入这里的内积。下面的播放器对任意面重放这四步,并在每一步显示内积。
The four inner products agree to rounding (largest relative difference ): the duality holds stage by stage on this face. Face 3 does not depend on directly (no face does), so its contribution to is 0; the phase reaches only through the constraint rows and the source rows. The of this face is not zero, but the seed has , so it does not enter these inner products. The player below replays the four steps for any face and shows the inner product at each.
前向求解:解随设计怎样变化
The tangent solve: how the solution moves with the design
本节实验:振幅 增加 时, 网格上的每个 变化多少? 又变化多少?先猜哪几个节点变化最大,再读图 45,并用本节的前向求解面板核对。
Experiment: If the amplitude grows by , how much does each of the mesh move, and how much does move? Guess which nodes move most, then read Figure 45 and check the guess in this section's tangent-solve panel.
第 8 节逐条语句地对残差代码求导:给定种子 与 ,前向代码返回 。它算的是一个乘积,还没有回答“设计变了,解怎样变”。本节用同一段代码来回答这个问题:设计沿 移动时,什么样的 能让残差一直保持为零?
Section 8 differentiated the residual code statement by statement: for seeds and the tangent code returns . That is a product; it does not yet say how the solution moves when the design moves. This section puts that question to the same code: when the design moves along , which keeps the residual at zero?
从前向代码到一个线性方程组
From the tangent code to one linear system
收敛解随设计变化:在默认设计附近,每个设计 都有自己的离散解 ,满足 (Newton 迭代求出它,第 5 节)。让设计沿直线 移动, 是第 个分量的单位向量( 依次对应 ), 是实数。残差对每个 都是零,所以它对 的导数也是零。在 用链式法则求导——这正是对整个求解过程使用前向模式——得到两项:
The converged state depends on the design: near the default, every design has its own discrete solution with (Newton's method finds it, Section 5). Move the design along the straight line , where is the unit vector of component ( for ) and is a real number. The residual is zero for every , so its derivative with respect to is zero as well. The chain rule at , which is the forward mode applied to the whole solve, gives two terms:
第一项是设计经由状态产生的间接影响,第二项是设计直接改变残差的部分。于是状态切向量 是一个线性方程组的解:矩阵是收敛状态处的状态 Jacobian (,第一行与最后一行是两个约束的单位行,第 4 节),右端是残差自身对设计的导数:
The first term is the indirect effect of the design through the state, the second is the direct change of the residual. So the state tangent solves a linear system whose matrix is the state Jacobian at the converged state (; its first and last row are the identity rows of the two constraints, Section 4) and whose right-hand side is the residual's own design derivative:
用第 8 节的话说:,种子是 ;把 送进前向代码,返回的 是零。前向代码对给定的 计算 ,前向求解则是找出使 的那个 。本页对 做 LU 分解来解这个方程组(第 17 节改用 GS 与 GMRES)。这要求 在收敛状态处非奇异:本页每个状态上分解都成功——这是数值检查,不是定理。默认设计下三次求解的线性残差 低于 (舍入误差)。
In the words of Section 8: for the seed ; fed with the seeds , the tangent code returns . The tangent code evaluates for a given ; the tangent solve finds the that makes vanish. The page solves the system with an LU factorisation of (Section 17 uses GS and GMRES instead). This needs to be nonsingular at the converged state: the factorisation succeeds at every state of this page, a numerical check rather than a theorem. At the default design the linear residual of the three solves, , is below (round-off).
是什么
What is
有 个分量:它是每个节点值对设计分量 的响应,是整个离散解的灵敏度,不只是一个数。其中两个分量不用求解就知道:约束行说 、,所以 、 精确成立(这里与它们相差不到 )。目标的导数由 得到一个数:第 7 节的 不显含设计(权重 只依赖网格),所以 ,导数就是加权和
has entries: it is the response of every nodal value to the design component , the sensitivity of the whole discrete solution and not of one number. Two of its entries are known without solving anything: the constraint rows say and , so and exactly (here they agree to within ). One number follows from . The objective of Section 7 does not contain the design explicitly (the weights depend on the mesh only), so and its derivative is the weighted sum
默认设计(,无限制器线性重构)下三次求解给出 、、。它们是这个网格上离散目标 的精确导数,与第 1 节的连续梯度 比: 相差 23.6%, 相差 393%, 是 而不是零。格心页同一网格上, 与 的梯度误差分别是这里的 10 倍与 79 倍, 的梯度误差却比这里小(0.146 对 0.335;第 12 节的比较表)。这里 的梯度比较好,有两个原因,第 12 节都量过:默认相位恰好靠近 的梯度误差随相位变化的一个零点;两个边界节点用单侧斜率做了二阶重构(不重构它们时 的误差是 )。 的误差主要来自源项部分(第 12 节)。此外,一个正弦周期只有 8 个节点,而精确梯度只是两个 项之间很小的差。实验的答案在图 45 左图: 增加 时,变化最大的是 与 ,约 与 ; 变化约 。
At the default design (, unlimited linear reconstruction) the three solves give , and . These are the exact derivatives of the discrete objective on this mesh. Against the continuous gradient of Section 1: is 23.6% off, is 393% off, and is instead of zero. On the same mesh the cell-centred page's gradient errors for and are 10 and 79 times as large as these, but its error for is the smaller one (0.146 against 0.335; the comparison table of Section 12). The amplitude gradient is comparatively good here for two reasons that Section 12 measures: the default phase happens to lie near a zero of the error of the -gradient as a function of the phase, and the two boundary nodes are reconstructed at second order with a one-sided slope (with the boundary nodes not reconstructed the error at is ). The error of comes mainly from the source part (Section 12). In addition, 8 nodes per period of the sine are few, and the exact gradient is a small difference of two terms of order one. The left panel of Figure 45 answers the experiment: when grows by , the nodes that move most are and , by about and , and moves by about .
为什么 ?精确解 与 无关,离散解却有关:格式的误差依赖 (默认网格的网格 Péclet 数是 ), 一变,误差就变, 也跟着变。所以 在粗网格上不为零,而连续的 ;第 12 节把这个差别拆开来看。
Why is ? The exact solution does not depend on , but the discrete solution does: the error of the scheme depends on (the mesh Péclet number of the default mesh is ), so when changes the error changes and with it. That is why is not zero on a coarse mesh while the continuous ; Section 12 takes this difference apart.
三个右端项
The three right-hand sides
右端 同样由前向代码算出:取 、,返回的 就是 。代码里只有三处读取设计,所以右端只有三条路径:
The right-hand side comes from the tangent code as well: with and the returned is . Only three places of the code read the design, so the right-hand side has three paths:
- 约束行():、,其中 。边界值只出现在这两行里(每个面两侧都有节点,面通量不读边界值),所以右端的第一项与最后一项就是边界数据的导数 、。它们不随 变小,也不随 变大。The constraint rows (): and with . The boundary values occur only in these two rows (every face has a node on both sides, so no face flux reads a boundary value): the first and the last entry of the right-hand side are the derivatives and of the boundary data themselves. They neither shrink nor grow with .
- 源项行():第 行有 ,即精确通量 的变化。它是源项导数在一个对偶控制体上的积分,大小约与 成正比。The source rows (): rows have , the change of the exact flux . It is the integral of a derivative of the source over one dual volume, of a size proportional to .
- 通量本身(只有 ):每个面的黏性通量 显含 ,偏导 分发到面两侧的两行(只进入内部行)。The flux itself ( only): the viscous flux of every face contains explicitly; its partial derivative is scattered to the two rows beside the face (only interior rows receive it).
因为第 行和第 行是单位行,方程组可以先消去它们:、 已知,内部节点解 。边界值经 的第 列和第 列进入内部(这两列在第 、 行及第 、 行有非零元;一阶格式只在第 行和第 行):边界数据对内部来说就是一份额外的源项。
Since rows and are identity rows, the system can be reduced first: and are known, and the interior nodes solve . The boundary values enter the interior through columns and of (which have nonzero entries in rows and and in rows and ; only in rows and at first order): to the interior, the boundary data are one more source term.
为什么首末两行较大的右端项会影响整个解? 是一个叠加:右端项的第 个分量就像加在第 行上的一个量, 的第 列是状态对它的响应():内部行 上是对偶控制体 里多一份单位源项,第 行上是入口值多 ,第 行上是出口值多 。图 47 画出 时的 :源项几乎只向下游(矩阵对角线以下、剖面的右侧)改变状态——第 5 行的单位量使下游各内部节点变化 0.63–1.31,上游各节点最多变化 0.042(节点 5 自身变化的 6.6%);靠近出口时响应回落,到出口节点恰为零,因为 是给定的。所以第一行的右端项(入口值)影响整个区间,最后一行的右端项只影响出口附近。
Why do the large first and last right-hand-side entries affect the whole solution? is a superposition: the -th entry of the right-hand side acts like a quantity added to row , and column of is the response of the state to one unit in that row (): in an interior row a unit of extra source in dual volume , in row one more unit of inlet value, in row one more unit of outlet value. Figure 47 draws for : a source changes the state mainly downstream (below the diagonal of the matrix, to the right in the profiles): a unit in row 5 moves the downstream interior nodes by 0.63–1.31 and the upstream nodes by at most 0.042 (6.6% of the node's own response); near the outlet the response falls back and is exactly zero at the outlet node, because is prescribed. So the first right-hand-side entry (the inlet value) acts on the whole interval, the last one only near the outlet.
第 12 节把每一列拆成这些路径,并跟踪每条路径对梯度的贡献。下面的面板对当前设计做完整的前向求解,并用两次完整的非线性重解检查 :中心差商 ,步长取 ,这个值本身并无特别之处。默认设计下三种重构、三个分量的最大差低于 (第 13 节研究步长的选择)。
Section 12 splits every column into these paths and follows each into the gradient. The panel below performs the whole tangent solve for the current design and checks against two full nonlinear re-solves: the central difference , where the step is just a number. At the default design the largest difference over the three reconstructions and the three components is below (Section 13 studies the choice of the step).
“切线”的含义:扰动后重新求解
What “tangent” means: perturb and re-solve
切线解是一个预测:对很小的 ,。验证它只需要非线性求解器本身:在 上重新完整求解,再与预测比较。残差是 与 的光滑函数, 非奇异,所以 光滑(隐函数定理),余项是二阶的:
The tangent solution is a prediction: for small , . Checking it needs nothing but the nonlinear solver: solve the full equations again at and compare with the prediction. The residual is a smooth function of and and is nonsingular, so is smooth (implicit function theorem) and the remainder is of second order:
很小时,把 减半,误差约变为原来的四分之一(在本页测试的范围内,相邻两个步长的误差之比在 3.82–4.18 之间)。默认设计(,无限制器线性)下,在 ()、()、()范围内,比值 分别停留在 0.049–0.053、3.60–3.91、1.80–2.00:在双对数图里误差曲线的斜率接近 2,这就是“切线”的标志。在面板里选一个节点、一个步长;其后的播放器把整条剖面做成动画。
For small , halving divides the error by about four (by 3.82–4.18 in the range tested here). At the default design (, unlimited linear), for (), () and (), the ratio stays within 0.049–0.053, 3.60–3.91 and 1.80–2.00 respectively: on log–log axes the error curve has a slope close to 2, the signature of a tangent. Pick a node and a step in the panel; the player after it animates the whole profile.
切线只在 很小时才可靠:沿 走 (四分之一个周期)时,预测与重解相差 。导数描述无穷小变化的响应;大步长的优化器必须重新求解。另一个常见错误是在未收敛的状态上求导:只有在 处, 才是解的导数;Newton 迭代中途的 给出的是另一个函数的切线。
The tangent line is reliable only for small : a step in (a quarter of the period) misses the re-solved state by . A derivative describes the response to an infinitesimal change; an optimiser that takes large steps must re-solve. Another common slip is to differentiate at a state that has not converged: only where is the derivative of the solution; the of an intermediate Newton iterate gives the tangent of a different function.
连续切线问题
The continuous tangent
对第 1 节的连续问题用同样的方法求导,得到连续切线方程。记 。对 ,只有源项与两端的给定值依赖 ;对 ,通量 本身含 ,于是右端多出 :
Differentiating the continuous problem of Section 1 in the same way gives the continuous tangent equations. Write . For only the source and the two end values depend on ; for the flux contains itself, which adds to the right-hand side:
对构造解不需要求解: 对每个设计都是解,所以它对设计的导数就是切线问题的解:,,(解与 无关;事实上 ,右端为零)。这三个函数与格式无关。本页用一个独立的 Chebyshev 配点求解器()解前两个边值问题,与 、 的闭式相差不到 ; 的问题右端与边界值都恒为零,解自然是零,这一项检验是平凡的。离散的 是节点上的点值,与这些函数在节点上的值 、、 比较(本页的未知量是点值,所以不需要像格心页那样乘一个 因子;两个边界节点上的差恰为零);图 48 与下表给出收敛情况。
For the manufactured solution no solve is needed: solves the problem for every design, so its derivative with respect to the design solves the tangent problem: , , (the solution does not depend on ; indeed , so the right-hand side vanishes). These three functions do not depend on the scheme. An independent Chebyshev collocation solver () of the first two boundary-value problems reproduces the closed forms of and to within ; for the right-hand side and the boundary data vanish identically, so its zero solution is a trivial check. The discrete are point values at the nodes and are compared with these functions at the nodes, , and (the unknowns of this page are point values, so there is no factor as on the cell-centred page; the difference at the two boundary nodes is exactly zero); Figure 48 and the table below show the convergence.
| N | 格心:cell: | 格心:cell: | 格心:cell: | |||
|---|---|---|---|---|---|---|
| 8 | 1.4e−1 | 3.2e−1 | 1.9e−1 | 5.2e−1 | 3.4e−1 | 6.5e−1 |
| 32 | 9.6e−3 | 1.9e−2 | 6.5e−2 | 1.1e−1 | 5.0e−2 | 3.8e−1 |
| 128 | 4.2e−4 | 6.0e−4 | 3.8e−3 | 2.0e−2 | 1.3e−2 | 2.1e−1 |
| 512 | 2.0e−5 | 3.2e−5 | 8.9e−5 | 2.0e−3 | 1.6e−3 | 3.2e−2 |
| 2048 | 1.4e−6 | 2.4e−6 | 2.4e−6 | 1.5e−4 | 1.2e−4 | 2.7e−3 |
| 4096 | 3.5e−7 | 6.3e−7 | 7.6e−7 | 3.8e−5 | 3.2e−5 | 7.0e−4 |
无限制器线性重构时切线解的观测阶趋近 2( 到 之间为 1.98、1.96、1.66),一阶重构时趋近 1(1.00、1.00、1.01)——与状态本身的阶相同(第 15 节)。无限制器线性重构时,格点格式的切线比格心格式准得多: 时 与 的误差只有 0.26 与 0.29 倍( 是 0.97 倍,并不更好), 时三个分量分别是格心格式的 、、 倍(图 49)。 时格点格式的 仍是三者中最大的,但只是 误差的 2.2 倍(格心格式是 18 倍)。一阶重构时两种格式的切线误差相当(节点/格心: 时为 1.49、0.94、1.91, 时为 1.004、1.004、1.094)。
With unlimited linear reconstruction the observed orders of the tangents approach 2 (1.98, 1.96 and 1.66 between and ), with first-order reconstruction they approach 1 (1.00, 1.00, 1.01): the orders of the state itself (Section 15). With unlimited linear reconstruction the tangents of the node scheme are far more accurate than those of the cell scheme: at the errors of and are 0.26 and 0.29 times the cell scheme's (that of is 0.97 times, no better), at the three components are , and times theirs (Figure 49). At the node scheme's is still the largest of the three, but only 2.2 times the error of (18 times on the cell page). With first-order reconstruction the two schemes have about the same tangent error (node/cell 1.49, 0.94, 1.91 at ; 1.004, 1.004, 1.094 at ).
代价:每个设计分量解一次
The cost: one solve per design component
每次前向求解回答一个问题:对一个设计方向的响应。设计有 个分量,就要用同一个矩阵 、 个右端项解 次;这里 。好处是,每次求解给出整个向量 ,于是状态的任何其他目标,导数只需再做一次内积。当目标少而设计分量多时,第 11 节把计算倒过来:用 解一次,就得到 的全部分量(图 50)。
Each tangent solve answers one question: the response to one design direction. A design with components needs solves with the same matrix and right-hand sides; here . In return each solve gives the whole vector , so the derivative of any other objective of the state is one more dot product. When the objectives are few and the design components many, Section 11 turns the computation around: one solve with gives every component of at once (Figure 50).
伴随求解:一次转置求解得到全部梯度
The adjoint solve: one transposed solve for the whole gradient
本节实验:先预测 是正还是负、 在哪里最大,在图 52 里核对;再看两个约束行的乘子 、:连续伴随在两端是 ,它们是什么?它们离连续伴随的哪个量最近?
Experiment: Predict first: is positive or negative, and where is largest? Check it in Figure 52. Then look at the multipliers and of the two constraint rows: the continuous adjoint has at the ends, so what are they, and which quantity of the continuous adjoint are they close to?
第 10 节每个设计分量要解一次线性方程组。只有一个目标而设计分量很多时,这样做很浪费:我们要的只是 个数 ,而不是 个向量 。本节把求解移到内积的另一侧。
Section 10 needed one linear solve per design component. With one objective and many design components that is wasteful: what we want are numbers , not vectors . This section moves the solve to the other side of the dot product.
从 次前向求解到一次转置求解
From tangent solves to one transposed solve
对任意种子 ,前向求解给出 ,目标的方向导数是 。把前者代入后者,从左边把因子合并:
For any seed the tangent solve gives , and the directional derivative of the objective is . Insert the first into the second and group the factors from the left:
向量 (伴随)与 无关:用转置矩阵解一次,就对所有设计方向都适用。它有 个分量, 属于内部节点, 与 是两个约束行的乘子。梯度就是反向模式的设计伴随:
The vector , the adjoint, does not depend on : one solve with the transposed matrix serves every design direction. It has entries: belong to the interior nodes, and are the multipliers of the two constraint rows. The gradient is the design adjoint of reverse mode:
用第 9 节的话说:以 为种子运行反向代码,它返回 ( 满足伴随方程时正好等于 )和 ,即梯度。第 9 节的对偶恒等式 取 、、(于是 ),就是 :前向与伴随给出同一个数。默认设计下,三种重构的反向代码返回的 都满足 ;下表比较两条路线:
In the words of Section 9: run the reverse code with the seed ; it returns , which equals exactly when solves the adjoint equation, and , the gradient. The duality identity of Section 9 with , and (so that ) reads : the tangent and the adjoint give the same number. At the default design the reverse code returns with for each of the three reconstructions, and the table compares the two routes:
| 重构Reconstruction | 前向 Tangent | 伴随 Adjoint | 差Difference | |
|---|---|---|---|---|
| 一阶first order | −1.175176203765 | −1.175176203765 | −2.2e−16 | |
| 一阶first order | −0.234593749400 | −0.234593749400 | −3.3e−16 | |
| 一阶first order | 0.992574866977 | 0.992574866977 | 2.2e−16 | |
| 无限制器线性unlimited linear | −0.178061153176 | −0.178061153176 | −5e−16 | |
| 无限制器线性unlimited linear | 0.419903873475 | 0.419903873475 | −1.4e−15 | |
| 无限制器线性unlimited linear | 0.003908842857 | 0.003908842857 | −2.8e−17 | |
| 光滑限制器smooth limiter | −0.188210330105 | −0.188210330105 | 8.3e−16 | |
| 光滑限制器smooth limiter | 0.553467823395 | 0.553467823395 | −5.6e−16 | |
| 光滑限制器smooth limiter | −0.314999960484 | −0.314999960484 | −2.2e−16 |
右端的负号是一种约定:若写 ,梯度就是 。本页取 ,使 与附录 A 的连续形式 符号一致:内部节点上 与 同号,也不差一个因子 ( 是对偶控制体上的积分残差, 里已经含有 ):默认网格上 在内部节点取 0.82–1.21, 时在 取 0.989–0.996。
The minus sign on the right-hand side is a convention: with the gradient would be . This page keeps so that and its continuous counterpart of Appendix A carry the same sign: at the interior nodes and have the same sign, and there is no factor between them ( is a residual integrated over a dual volume, and already contains ): on the default mesh takes 0.82–1.21 at the interior nodes, and 0.989–0.996 at for .
转置矩阵:倒着走的面循环
The transposed matrix: the face loop run backwards
是 关于对角线的镜像: 的第 行是 的第 列,即未知量 进入的那些残差行。图 51 画出 时的两者。反向代码从不形成转置矩阵(本页的直接求解为了方便确实形成并分解了 ;只有用 residual_b 的无矩阵路线从不形成它):第 9 节的反向面循环从最后一个面走到第一个面,给面 种子 (面 只给 ,面 给 ,面 只给 :它两侧内部节点的伴随之差),再把 、 和 送回这个面读过的节点。每个面送回的正是它在 中各列的那几项,所以这个循环不形成矩阵就完成了与 的乘法。
is mirrored about its diagonal: row of is column of , the residual rows that the unknown enters. Figure 51 shows both for . The reverse code never forms a transposed matrix (the page's direct solves do form and factor , for convenience; only the matrix-free route through residual_b never forms it): the reverse face loop of Section 9 visits the faces from the last to the first and gives face the seed (face gets only , face gets , face gets only : the difference of the adjoints of the two interior nodes it separates), then sends , and back to the nodes the face read. Each face sends back exactly its own entries of the columns of , so the loop applies without forming it.
转置也颠倒了信息传播的方向。看一阶格式()的第 3 行: 的第 3 行是 (依次乘 ),上游的系数绝对值比下游的大:残差 主要取决于上游节点—— 时原始问题的信息从左向右传。 的第 3 行是 的第 3 列 (依次乘 ),大系数换到了下游一侧:伴随从右向左传,从 传向 。连续伴随方程 (附录 A)也是这样:它沿反方向输运,它的入口边界层(下文简称层)在 。
The transposition also reverses the direction in which information travels. Take row 3 of the first-order scheme (): row 3 of is (multiplying ); the upstream coefficient is larger in magnitude than the downstream one: the residual leans on the upstream node, and with the information of the primal problem travels from left to right. Row 3 of is column 3 of , (multiplying ): the large coefficient has moved to the downstream side, and the adjoint travels from right to left, from towards . The continuous adjoint equation of Appendix A does the same: it transports in the reverse direction, and its inlet boundary layer (from here on: the layer) sits at .
第 行和第 行还带来一个结构:它们是单位矩阵 的第 行与第 行,、,所以伴随方程的第 行里没有 与 (对三种重构都精确为零)。内部节点的伴随 先由 的内部块的转置单独决定;两个乘子随后由第 行和第 行读出——它们是被动的。下一小节把这件事用在 、 上。
Rows and bring a structure of their own: they are rows and of the identity matrix , and , so rows of the adjoint equation contain neither nor (exactly zero for all three reconstructions). The interior adjoint is determined first, by the transposed interior block of alone; the two multipliers are read off afterwards from rows and : they are passive. The next subsections use this for and .
伴随场
The adjoint field
图 52 是默认设计下的 。三种重构的 在每个节点都为负(两个乘子也是);无限制器线性时内部节点上 在节点 1 最大(),在节点 7 最小(), 更小。实线是连续伴随 (附录 A,由独立的 Chebyshev 求解器算出):内部节点上 ,同号、无因子 。两端的 、 不是场在 的值—— 在那里是零——而是约束行的乘子;它们在图右边单独画出。
Figure 52 shows at the default design. In all three reconstructions is negative at every node (the two multipliers included); with unlimited linear reconstruction is largest at the interior node 1 () and smallest at node 7 (), and is smaller still. The solid line is the continuous adjoint of Appendix A (computed by an independent Chebyshev solver): at the interior nodes , with the same sign and no factor . The end entries and are not the values of the field at (there is zero) but the multipliers of the constraint rows; they are drawn separately on the right.
的含义:在一行里加一点量
What means: add a little to one row
给第 行加上一个数:对内部行,这是给对偶控制体 的积分源项加 ——行 变成 ,其余行不变;对第 行, 就是把入口值加 ,对第 行是把出口值加 。前向种子 给出 ,即 ;用伴随方程 :
Add a number to row : for an interior row this is more integrated source in dual volume , so row becomes and the other rows do not change; for row , is more inlet value, for row it is more outlet value. The forward seed gives , that is , and with the adjoint equation, :
所以 是“第 行多一个单位量时 增加多少”。:任何一行加量都使 增大。特别是 与 是目标对两个边界值的导数(源项与另一个边界值不变):这就是乘子的意义。图 53 不用伴随直接测量这个关系:对每个 重新完整求解两次非线性方程(),中心差商与 的最大相对差为 。加在节点 1、2 的源项影响最大:它们向下游影响几乎所有节点,而 对整个区间加权求和;加在入口值上的影响 与它们同量级(入口值经整个区间向下游输运);加在出口值上的影响 很小。
So is how much grows when row receives one more unit; means that adding to any row raises . In particular and are the derivatives of the objective with respect to the two boundary values (the source and the other boundary value fixed): that is what the multipliers mean. Figure 53 measures this relation without the adjoint: for every it solves the full nonlinear equations twice (), and the central difference agrees with to a relative . Sources in nodes 1 and 2 act most: they influence nearly every node downstream, and sums over the whole interval; a unit more inlet value acts with , of the same order (the inlet value is transported downstream through the whole interval); a unit more outlet value acts with the small .
图 47 的影响矩阵给出同一件事的另一种读法: 是第 列——第 行加单位量在各节点引起的变化——按目标权重 加起来。切线解把扰动向下游送,伴随把下游的权重收集回来。在面板里换一行试试(第 行和第 行也可以):
The influence matrix of Figure 47 gives a second reading of the same fact: is column , the change that a unit in row causes in every node, summed with the objective weights . The tangent sends a disturbance downstream; the adjoint gathers the downstream weights back. Try another row in the panel (rows and included):
与连续伴随比较
Comparison with the continuous adjoint
在内部节点上, 逼近 。离开入口层()的最大差见下表;观测阶趋近 2(最后三次网格加倍 512→1024→2048→4096 的观测阶为 1.93、1.96、1.98)。入口层本身(宽约 , 的一个网格间距是它的 11 倍)没有被分辨,图 54 的下排显示: 与 时第一个内部节点已在层外,落在 的平台上。第 16 节系统地研究这种一致性。
At the interior nodes approaches . The largest difference away from the inlet layer () is in the table below; the observed order approaches 2 (1.93, 1.96, 1.98 for the last three doublings of the mesh, 512→1024→2048→4096). The inlet layer itself (width about ; one mesh interval of is 11 times as wide) is not resolved, and the bottom row of Figure 54 shows that at and the first interior node already lies outside it, on the plateau of . Section 16 studies this consistency systematically.
| N | |
|---|---|
| 8 | 2.9e−1 |
| 16 | 1.3e−1 |
| 32 | 1.9e−2 |
| 64 | 6.1e−3 |
| 256 | 6.3e−4 |
| 1024 | 4.6e−5 |
| 4096 | 3.0e−6 |
本页的新结果:约束行的乘子是连续伴随的边界通量
The new result of this page: the multipliers of the constraint rows are the boundary fluxes of the continuous adjoint
连续伴随在两端为零,梯度公式(附录 A、第 12 节)却含有它的导数:
The continuous adjoint vanishes at both ends, yet the gradient formula (Appendix A, Section 12) contains its derivative:
离散梯度(第 12 节)是
The discrete gradient (Section 12) is
逐项对照,边界项相符的条件是
Term by term, the boundary terms agree if
约束行的乘子是连续伴随在两端的(黏性)通量—— 为零,所以对流部分没有贡献。这不是假设,是可以测量的:默认设计下 ,(独立的 Chebyshev 求解器)。下表和图 55 给出无限制器线性重构下 、 随网格加密的变化,并与格心页中对应的量 、 比较。
The multiplier of a constraint row is the (viscous) flux of the continuous adjoint through that end: is zero there, so the convective part does not contribute. This is not an assumption, it can be measured: at the default design and (independent Chebyshev solver). The table and Figure 55 show and under refinement for unlimited linear reconstruction, against the corresponding quantities and of the cell-centred page.
| N | 相对误差relative error | 格心 的相对误差cell : relative error | 相对误差relative error | 格心 的相对误差cell : relative error | ||
|---|---|---|---|---|---|---|
| 8 | −1.40608 | −1.4e−2 | 4.8e−2 | −0.08701 | 2.9e0 | 4.2e0 |
| 16 | −1.40116 | −1.8e−2 | 2.9e−2 | −0.05158 | 1.3e0 | 2.0e0 |
| 32 | −1.41357 | −9.1e−3 | 1.3e−2 | −0.03521 | 6.0e−1 | 9.9e−1 |
| 64 | −1.42122 | −3.7e−3 | 4.7e−3 | −0.02736 | 2.4e−1 | 4.4e−1 |
| 256 | −1.42599 | −4.1e−4 | 2.7e−4 | −0.02261 | 2.6e−2 | 6.0e−2 |
| 1024 | −1.42653 | −3.0e−5 | 6.6e−6 | −0.02208 | 1.9e−3 | 5.0e−3 |
| 4096 | −1.42657 | −1.9e−6 | 1.3e−7 | −0.02204 | 1.2e−4 | 3.4e−4 |
测量结果。入口: 时 ,与 只差 1.4%——尽管入口层完全没有被分辨;相对误差随网格加密按二阶下降(最后三次网格加倍 512→1024→2048→4096 的观测阶 1.92、1.96、1.98);一阶重构时是一阶(1.00、1.00、1.00)。格心格式的 在 时差 4.8%:格点格式在 时更准, 时格心格式的 反而更准(:1.3e−7 对 1.9e−6)。出口: 时 是 的 3.95 倍——相对误差很大,但绝对误差只有 0.065,而被逼近的通量 本身只有 0.022;它同样按二阶收敛(1.93、1.96、1.98), 时差 24%, 时差 0.19%。格心格式的 在每个 上都更差( 时是 的 5.20 倍)。
The measurements. Inlet: at , is within 1.4% of , although the inlet layer is not resolved at all; the relative error falls at second order under refinement (observed 1.92, 1.96, 1.98 for the last three doublings of the mesh, 512→1024→2048→4096), at first order for first-order reconstruction (1.00, 1.00, 1.00). The cell scheme's is 4.8% off at : the node scheme is closer for , and from on the cell scheme's is the closer one (: 1.3e−7 against 1.9e−6). Outlet: at , is 3.95 times : a large relative error, but an absolute error of only 0.065, while the flux it approximates is only 0.022; it converges at second order too (1.93, 1.96, 1.98), and is 24% off at and 0.19% at . The cell scheme's is worse at every (5.20 times at ).
层没被分辨时入口乘子为什么还准:一种解读
Why the inlet multiplier is accurate before the layer is resolved: a reading
乘子不是 那样的差商,而是伴随方程第 行(对 来说)直接给出的数。第 行是 ,只有第 、 行含 ,于是
The multiplier is not a difference quotient like but the number that row of the adjoint equation gives directly. Row reads , and only rows and contain , so
乘子是紧邻边界的两个内部伴随值的加权和,权是边界附近面通量对边界值的灵敏度(对一阶格式第二项为零,只剩 :对流通量 加上穿过第一个区间的黏性通量 )。下表在 上算出三项:
The multiplier is a weighted sum of the two interior adjoint values next to the boundary, the weights being the sensitivities of the face fluxes there to the boundary value (at first order the second term is zero and only remains: the convective flux plus the viscous flux across the first interval). The table evaluates the three terms at :
| 行row | 第一个通量项first flux term | 第二个通量项second flux term | ||
|---|---|---|---|---|
| 入口:inlet: | −0.06250 | −1.87300 | 0.52943 | −1.40608 |
| 出口:outlet: | −0.12500 | −0.03574 | 0.07373 | −0.08701 |
这个公式并没有解释 为什么准。在粗网格上,这两个内部值并不准确( 时 ,而 ,差 21%; 差 5%)。把精确的 、 代入同一个公式得 ,与 差 22%,而用离散值得 ,差 1.4%: 的准确不是从 、 继承来的。下表对其他网格做同样的比较。
This formula does not explain why is accurate. On a coarse mesh the two interior values are not accurate (at : against , 21% off; 5% off). With the exact , in place of , the formula gives , 22% away from , while the discrete values give , 1.4% away: the accuracy of is not inherited from , . The table makes the same comparison on other meshes.
| N | 精确 :exact : | 相对误差relative error | 离散 :discrete : | 相对误差relative error | ||
|---|---|---|---|---|---|---|
| 8 | 2.1e−1 | 5.2e−2 | −1.1105 | −2.2e−1 | −1.4061 | −1.4e−2 |
| 16 | 1.5e−1 | 9.0e−2 | −1.1905 | −1.7e−1 | −1.4012 | −1.8e−2 |
| 32 | 7.5e−2 | 8.4e−2 | −1.3204 | −7.4e−2 | −1.4136 | −9.1e−3 |
| 64 | 4.2e−2 | 4.1e−2 | −1.3637 | −4.4e−2 | −1.4212 | −3.7e−3 |
| 256 | 4.4e−2 | 1.6e−2 | −1.3584 | −4.8e−2 | −1.4260 | −4.1e−4 |
一种启发式读法(不是定理),说明 为什么不会差得很远、 又是什么: 是入口值多一个单位时按目标权重加起来的响应(第 10 节、图 47)。连续问题里这个响应是第 10 节的切线方程取边界值 与 的解,它的加权积分是 (用 Chebyshev 配点求解器核对);它沿区间光滑,唯一的层在出口,宽 。这个层使加权总和减少约 (权重 在出口等于 ),只占总和的 1.5%,所以一个没有分辨这个层的网格在 上至多差几个百分点( 时差 1.4%)。实测的误差并不跟着这个份额走: 取 到 的每个值, 时 的相对误差都在 1.4% 到 1.5% 之间,而层的份额从总和的 0.3% 增大到 3.1%(见下面的局限)。 是出口值多一个单位的响应:它就是这个出口层,加权大小约 ,观测到的是 ; 没有分辨这个层, 差 295%。
A heuristic reading (not a theorem) of why cannot be far off and of what is: is the weighted response to one more unit of inlet value (Section 10, Figure 47). In the continuous problem that response solves the tangent equation of Section 10 with the boundary values and ; its weighted integral is (checked with a Chebyshev collocation solver), it is smooth along the interval, and its only layer is at the outlet, of width . That layer lowers the weighted total by about (the weight is at the outlet), only 1.5% of the total, so a mesh that does not resolve it can be wrong in by a few percent at most (1.4% at ). The observed error does not follow this deficit, though: for every from to the relative error of at stays between 1.4% and 1.5%, while the deficit grows from 0.3% to 3.1% of the total (the limits are below). is the weighted response to one more unit of outlet value: it is that outlet layer, of weighted size about against the observed ; does not resolve it, and is 295% off.
连续问题里还有一个对应的事实:在层外,伴随方程的外解满足 ,而入口的通量在 的首阶近似下是 ,与 相近:入口的灵敏度等于层外伴随的对流通量。这些是启发式的解读;上面的测量才是证据,解读不是定理。
The continuous problem has a matching fact: outside the layer the outer solution of the adjoint equation has , and to leading order in the inlet flux is , close to : the sensitivity to the inlet value equals the convective flux of the adjoint outside the layer. These are heuristic readings; the measurements above are the evidence, and a reading is not a theorem.
与格心页对照:那里的乘子是半个单元里的量,要写成 才与 相比,这个差商在 时只有 , 时 , 时 (),到 才与 相差不到 5%;真正进入梯度的是组合 ( 时差 4.8%)。本页没有半个单元,也没有这个组合:乘子就是进入梯度的量, 时差 1.4%。不要在任何一页上从粗网格的乘子读层的形状——它只告诉你通量。
Compare the cell page: there the multiplier lives in a half cell and has to be written as to be compared with ; that quotient is only at , at and at (), and comes within 5% of only from on; what actually enters the gradient there is the combination (4.8% off at ). This page has no half cell and no combination: the multiplier is the quantity that enters the gradient, 1.4% off at . On neither page should you read the shape of the layer off a coarse-mesh multiplier: it tells you the flux.
局限:以上是默认设计下的数值观察,不是定理。把 从 变到 (层宽变化 50 倍), 时 的绝对误差只在 0.0205 到 0.0252 之间, 的绝对误差在 0.039 到 0.068 之间:层宽只要小于一个网格间距(这里的每个 都是),这两个误差的大小就差不多,层未被分辨时本该如此。这是粗网格上的效应,这些扫描无法把它分成层的部分与非层的部分。下面的面板可以改 、重构与 :
The limits: these are numerical observations at the default design, not theorems. Changing from to (the layer width changes by a factor of 50) moves the absolute error of at only between 0.0205 and 0.0252, and that of between 0.039 and 0.068: of the same order for every layer width below one interval (every here), as expected when the layer is unresolved: a coarse-mesh effect that these sweeps cannot split into layer and non-layer parts. The panel below lets you change , the reconstruction and :
组装梯度:谁的贡献
Assembling the gradient: who contributes
是逐行的和。图 56 画出每一行的贡献 和累加和,两个约束行单独着色。 时 的梯度分成内部行的 与两个约束行的 ,合计 ; 的梯度是两个大数之差:内部行 ,约束行 ,合计只有 。 的两个约束行不贡献( 的 列在这两行为零)。与格心页不同,这里边界部分在每个网格上都恰好由首末两行承载: 时两部分是 与 ()、 与 ()。梯度是 的两部分之差,第 12 节把它们与连续梯度的两项一一对应。
is a sum over the rows. Figure 56 draws the contribution of every row and the running sum, with the two constraint rows coloured separately. At the -gradient splits into from the interior rows and from the two constraint rows, together ; the -gradient is the difference of two large numbers: from the interior rows, from the constraint rows, together only . The two constraint rows do not contribute to (the column of is zero in those rows). Unlike on the cell page, the boundary part is carried by the first and the last row exactly, on every mesh: at the two parts are and (), and (). The gradient is a difference of two parts of order one; Section 12 matches them with the two terms of the continuous gradient.
下面的面板把本节的内容放在一起,网格、重构与黏性都可以改(带状求解器, 到 ): 与 、各行的贡献,以及梯度的四种算法——前向、伴随、两次完整重解的中心差商和连续问题的精确梯度。
The panel below puts this section together, with the mesh, the reconstruction and the viscosity of your choice (banded solver, up to ): against , the contribution of each row, and the gradient by four routes: tangent, adjoint, central differences of two full re-solves, and the exact gradient of the continuous problem.
一次求解得到全部分量:代价
One solve for every component: the cost
两条路线的代价可以直接数。用不加预处理的 GMRES 解每个线性方程组,不形成矩阵,矩阵向量乘积只由代码给出:前向路线的每次迭代调用一次 residual_d(第 8 节),伴随路线的每次迭代调用一次 residual_b(第 9 节)。图 57 数出调用次数:设计只有 三个分量时(),前向路线的调用约是伴随路线的三倍;若再给每一行(包括两个约束行)一个源项分量(,上面“ 的含义”里的 ),前向路线的调用随 增长得快得多,而伴随路线的调用次数与 无关: 时是 3283 次对 64 次。 的求解与伴随求解每次约用 次迭代( 时为 64、64、63、64),行源项的求解在较大的网格上用得少些( 时 31–64 次,均值 48),所以 时调用次数之比是 51,而不是 。两条路线的梯度最大相差 。与直接求解相比,这个差全在前向路线的行源项分量上(最多差 );两条路线的 分量以及伴随路线的行分量与直接求解相差约 (舍入误差)。
The cost of the two routes can be counted directly. Solve every linear system with unpreconditioned GMRES, without forming the matrix (the matrix–vector products come from the code): each iteration of the tangent route calls residual_d once (Section 8), each iteration of the adjoint route calls residual_b once (Section 9). Figure 57 counts the calls. With the three design components () the tangent route makes about three times as many calls as the adjoint route. Give every row (the two constraint rows included) one more source component as well (, the of “What means” above), and the calls of the tangent route grow much faster with , while the calls of the adjoint route do not depend on : 3283 calls against 64 at . The solves for and the adjoint solve take about iterations each (64, 64, 63, 64 at ), while the solves for the extra row sources take fewer on the larger meshes (31–64, mean 48, at ), so with the ratio of the calls is 51 rather than . The gradients of the two routes differ by at most . Against a direct solve, all of it sits in the row-source components of the tangent route (off by up to ); the components of both routes and the row components of the adjoint route agree with the direct solve to about (round-off).
用 LU 分解直接求解时情况不同: 分解一次后(部分选主元:), 个前向右端项与一个伴随右端项( 用同一组因子)都只需三角求解(前代与回代),差别小得多。伴随的优势在矩阵不显式形成、每次求解都要迭代的大问题里才显著——这正是 CFD 的常态。若有 个目标,伴随路线要解 次,前向路线仍是 次:哪条路线便宜取决于 与 谁大。
With a direct LU solve the picture is different: once is factored (with partial pivoting, ), the tangent right-hand sides and the one adjoint right-hand side ( reuses the same factors) each cost only triangular solves, and the difference is small. The adjoint pays off in large problems where the matrix is never formed and every solve is an iteration, which is the normal case in CFD. With objectives the adjoint route needs solves and the tangent route still : which route is cheaper depends on whether or is larger.
设计导数:设计从哪里进入残差
The design derivatives: where the design enters the residual
本节实验:先预测振幅的梯度 ()主要来自内部各行(源项)还是两个约束行;再想一想:连续问题的 是精确的, 时 很小——它小是因为格式在 上很准吗?对照 (图 65)。
Experiment: Predict first: where does the amplitude gradient () come from, the interior rows (the source) or the two constraint rows? And the continuous exactly, while at is small: is it small because the scheme is accurate in ? Compare (Figure 65).
第 10 节与第 11 节把 当作一个整体使用。本节把它打开:每个设计分量从哪里进入残差,每条进入路径对梯度贡献多少,这些部分怎样收敛到连续梯度的两项,以及剩下的误差来自哪里。
Section 10 and Section 11 used as a whole. This section opens it: where each design component enters the residual, how much each path contributes to the gradient, how the pieces converge to the two terms of the continuous gradient, and where the remaining error comes from.
每个设计变量改变什么
What each design variable changes
构造解的公式(第 1 节)给出每个量对每个设计变量的精确导数。下表列出它们,最后一列是连续目标的精确梯度:
The formulas of the manufactured solution (Section 1) give the exact derivative of every quantity with respect to every design variable. The table lists them; the last column is the exact gradient of the continuous objective:
| 设计变量Design variable | (), | 进入残差的位置Where it enters the residual | |||
|---|---|---|---|---|---|
| 约束行 、(边界值)与源项行constraint rows , (boundary values) and source rows | |||||
| 同上likewise | |||||
| 源项行,以及每个面的通量:source rows, and the flux of every face: |
默认设计下精确梯度是 。 的一行最特别:解与目标都不依赖 ,但源项的精确通量 依赖( 是源项必须补上的扩散),离散通量也依赖。边界值 在格点格式里只进入两个约束行。
At the default design the exact gradient is . The row is the special one: neither the solution nor the objective depends on , but the exact flux of the source does ( is the diffusion the source has to supply), and so does the discrete flux. On the node scheme the boundary values enter only the two constraint rows.
矩阵
The matrix
是 矩阵,每列是第 10 节的一个右端项(差一个负号)。下表是 的 。 与 两列不含状态:第 、 行是 、,内部行只来自精确通量 ,所以三种重构的这两列完全相同(最大差 ); 列含 ,随重构而变,在第 、 行恰为零。
is an matrix; each column is one right-hand side of Section 10 (up to the sign). The table shows for . The and columns contain no state: rows and are and , the interior rows come only from the exact flux , so they are the same for the three reconstructions (largest difference ). The column contains , changes with the reconstruction, and is exactly zero in rows and .
| 行 Row | () () | () () | () () |
|---|---|---|---|
| 0 | 0.425779 | 1.137039 | 0.000000 |
| 1 | −0.826554 | −0.132004 | 0.111187 |
| 2 | −0.478357 | −0.957802 | −0.022857 |
| 3 | 0.304158 | −1.062330 | −0.081614 |
| 4 | 0.781016 | −0.350909 | −0.118902 |
| 5 | 0.646263 | 0.405868 | −0.101704 |
| 6 | 0.260423 | 0.731242 | 0.054198 |
| 7 | −0.123867 | 0.788466 | 0.336097 |
| 8 | 0.425779 | 1.137039 | 0.000000 |
每一列的三个部分
The three pieces of a column
取 、,第 8 节的前向面语句里 全为零(设计不进入任何迹),只剩 ,它只在 时不为零。按残差的分发方式进入各行,再加上源项语句与约束行,就是 的三个部分:
With and , the tangent face statement of Section 8 has all zero (the design enters no trace) and only remains, nonzero for only. Scattered the way the residual is scattered, together with the source statement and the constraint rows it gives the three pieces of :
第一、二部分在内部行(),第三部分只在两个约束行。格心页还要把边界部分再分成对流与黏性两块(它们来自边界面上的 与 );这里没有边界面的特殊处理,边界部分就是约束行本身。本页从前向代码自己的中间量读出这三部分;三部分之和与前向代码算出的 完全相同;check_d.js 还用独立的复数步长实现逐项核对了每一部分。图 58 把它们画成堆叠柱。
The first two pieces sit in the interior rows (), the third in the two constraint rows only. The cell-centred page has to split its boundary part into a convective and a viscous block (they come from and of the boundary face); here there is no special boundary face, and the boundary part is the constraint rows themselves. The page reads the three pieces off the intermediate values of the tangent code; their sum equals the of the tangent code exactly, and check_d.js holds each piece separately to an independent complex-step implementation. Figure 58 draws them as stacked bars.
梯度从哪里来
Where the gradient comes from
把伴随与每一部分做内积,梯度 就被分成同样的三部分 、、。第三部分有一个简单的形式:约束行只含 与 ,
Dotting the adjoint with every piece splits the gradient into the same three parts, , and . The third has a simple form: the constraint rows involve only and ,
是 对入口值的导数(源项与出口值不变), 同理(第 11 节)。默认设计下反向代码给出 、,对三种重构中的每一种都与 、 完全相同;边界部分与上式相差不到 。下表与图 59 给出梯度的三部分。
is the derivative of with respect to the inlet value (with the source and the outlet value fixed), and likewise (Section 11). At the default design the reverse code gives and , which equal and exactly for each of the three reconstructions; the boundary part agrees with the formula above to within . The table and Figure 59 give the three parts of the gradient.
| 重构Reconstruction | 分量Component | 梯度Gradient | |||
|---|---|---|---|---|---|
| 一阶first order | −0.44676 | 0.00000 | −0.72841 | −1.175176 | |
| 一阶first order | 1.71063 | 0.00000 | −1.94522 | −0.234594 | |
| 一阶first order | 1.62313 | −0.63055 | 0.00000 | 0.992575 | |
| 无限制器线性unlimited linear | 0.45766 | 0.00000 | −0.63573 | −0.178061 | |
| 无限制器线性unlimited linear | 2.11760 | 0.00000 | −1.69770 | 0.419904 | |
| 无限制器线性unlimited linear | 2.18011 | −2.17620 | 0.00000 | 0.003909 | |
| 光滑限制器smooth limiter | 0.46228 | 0.00000 | −0.65049 | −0.188210 | |
| 光滑限制器smooth limiter | 2.29061 | 0.00000 | −1.73714 | 0.553468 | |
| 光滑限制器smooth limiter | 2.33156 | −2.64656 | 0.00000 | −0.315000 |
实验的第一问有了答案:、无限制器线性时, 的梯度 主要来自边界部分 (绝对值比梯度本身还大),符号相反的源项部分 抵消了其中约 72%; 更极端: 与 相加只剩 。与格心页不同,这两部分在每个网格上都分别由内部各行与首末两行承载,不会在端单元里互相抵消。第二问: 的两部分 与 各自都是 , 时几乎抵消,剩下 ;这种抵消是偶然的—— 时剩 (后文)。
The first question of the experiment has its answer: at with unlimited linear reconstruction, the -gradient comes mainly from the boundary part , which is larger in magnitude than the gradient itself; the source part has the opposite sign and cancels about 72% of it. is more extreme: and add up to only . Unlike on the cell page, the two parts are carried on every mesh by the interior rows and by the first and last row respectively, and do not cancel inside an end cell. The second question: the two parts of , and , are each of order one and nearly cancel at , leaving ; that cancellation is accidental: leaves (below).
连续梯度:两个相互抵消的大项
The continuous gradient: two large terms that cancel
对连续问题做同样的分解。附录 A 由连续伴随 (,)推出梯度公式;它也有一个源项部分和一个边界部分,后者只有黏性:
The continuous problem splits the same way. Appendix A derives the gradient formula from the continuous adjoint (, ); it too has a source part and a boundary part, and the latter is purely viscous:
它与离散梯度逐项对应(第 11 节的新结果):、,源项积分 。用独立求出的 (Chebyshev 配点,)计算两项: 的源项积分 与边界项 , 的 与 ——各自都是 ,相加才得到小的精确梯度 (与闭式相差不到 )。边界项几乎全来自 : 对 、 分别是 与 , 只贡献 与 。 的两部分 与 逐点抵消:,源项补上的扩散恰好是通量里 的作用。
It corresponds term by term to the discrete gradient (the new result of Section 11): , , and the source integral . With an independently computed (Chebyshev collocation, ) the two terms are: for the source integral and the boundary term , for and : each of order one, and only their sum is the small exact gradient (within of the closed form). The boundary term comes almost entirely from : is for and for , while adds only and . The two parts of , and , cancel at every point: , the diffusion that the source supplies is exactly what does in the flux.
离散的两部分怎样收敛
How the discrete parts converge
离散梯度的边界部分是 ,它随乘子收敛到连续边界项(第 11 节:,);源项部分是 ,在 时收敛到 。图 60 用带状求解器从 测到 :两部分分别趋向连续的两项。
The boundary part of the discrete gradient is , which follows the multipliers to the continuous boundary term (Section 11: , ); the source part is , which tends to as . Figure 60 measures it with the banded solver from to : each part approaches its continuous term.
| N | ||||||
|---|---|---|---|---|---|---|
| 8 | 0.4577 | −0.6357 | 2.1176 | −1.6977 | −1.4061 | −0.0870 |
| 32 | 0.4782 | −0.6169 | 1.7486 | −1.6473 | −1.4136 | −0.0352 |
| 128 | 0.4730 | −0.6168 | 1.7325 | −1.6471 | −1.4247 | −0.0239 |
| 512 | 0.4728 | −0.6168 | 1.7323 | −1.6471 | −1.4264 | −0.0222 |
| 2048 | 0.4728 | −0.6168 | 1.7323 | −1.6471 | −1.4266 | −0.0220 |
| 4096 | 0.4728 | −0.6168 | 1.7323 | −1.6471 | −1.4266 | −0.0220 |
| 连续continuous | 0.4728 | −0.6168 | 1.7323 | −1.6471 | −1.4266 | −0.0220 |
边界部分与连续边界项的相对差在 时是 3.1%, 时是 1.4e−5, 时是 9.4e−7( 与 相同); 的源项部分与连续源项积分的相对差在 时是 3.2%, 时是 2.5e−3, 时是 4.1e−7; 的在 时差 22%, 时是 1.5e−3, 时是 1.1e−5。 时边界部分与 的源项部分都在几个百分点以内,尽管入口层没有被分辨; 的源项部分不是(22%,绝对误差 0.385,是精确梯度 0.0852 的 4.5 倍)。梯度是两个 项的小差,所以一项的几个百分点已经是梯度的很大一部分。
Relative to the continuous boundary term, the boundary part is off by 3.1% at , by 1.4e−5 at and by 9.4e−7 at (the same for and ); relative to the continuous source integral, the source part of is off by 3.2% at , by 2.5e−3 at and by 4.1e−7 at ; that of by 22% at , 1.5e−3 at and 1.1e−5 at . At the boundary part and the source part of are within a few percent although the inlet layer is not resolved; the source part of is not (22%, absolute error 0.385, 4.5 times the exact gradient 0.0852). The gradient is the small difference of two terms of order one, so a few percent of one term is a large share of it.
误差来自哪里
Where the error comes from
离散梯度 是离散目标的精确导数:前向、伴随、复数步长与差分在本页检验过的每个网格上都一致(第 13、14 节)。它与连续梯度的差是离散化误差,不是求导错误。格心页把粗网格上的大误差主要归因于两个未做重构的端单元;格点格式没有端单元,两个边界节点用单侧斜率做了二阶重构,那种解释在这里不适用。下面逐项测量,结论只限于所测范围,并写明局限。
The discrete gradient is the exact derivative of the discrete objective: tangent, adjoint, complex step and differences agree on every mesh tested on this page (Sections 13 and 14). Its difference from the continuous gradient is a discretisation error, not a differentiation error. The cell-centred page attributes the large coarse-mesh error mainly to its two unreconstructed end cells; the node scheme has no end cells, and its two boundary nodes are reconstructed at second order with a one-sided slope, so that explanation does not carry over. What follows measures it item by item; the conclusions hold for the ranges measured, and the limits are stated.
预算:源项部分、边界部分
The budget: source part and boundary part
把梯度的误差 拆成两部分各自的误差(源项部分减源项积分,边界部分减边界项; 则是源项部分与通量部分)。 时 的两个误差同号、大小相近(源项 ,边界 ),边界部分的误差又主要来自出口乘子(出口 ,入口 ):这是第 11 节里 在粗网格上相对误差大的后果。 的误差在 时几乎全来自源项部分(源项 ,边界 )。网格加密后, 的源项部分的误差在 到 、 时比边界部分大( 时大 9、71、141 倍), 的源项误差在每个网格上都更大; 的通量部分的误差在 到 、 时更大。图 61 画出全部。
Split the error of the gradient, , into the errors of its two parts (the source part minus the source integral, the boundary part minus the boundary term; for the source part and the flux part). At the two errors of have the same sign and similar size (source , boundary ), and the error of the boundary part comes mainly from the outlet multiplier (outlet , inlet ): the consequence of the large relative error of on coarse meshes (Section 11). The error of at comes almost entirely from the source part (source , boundary ). Under refinement the error of the source part of is the larger one for to , (9, 71 and 141 times the boundary part's at ), the source error of is the larger on every mesh, and the error of the flux part of is the larger for to , . Figure 61 shows all of it.
哪些控制体贡献误差
Which volumes contribute
源项部分是对内部节点的和,所以它的误差可以逐个对偶控制体地读出:。连续积分是在整个 上,而节点 与 的半个控制体没有行(它们是约束行),这两块 、 要另外算进去,否则逐控制体的误差加不起来。图 62 把全部画出:橙黄柱是 ,紫柱是两端的半个控制体(符号取负,即它们在误差里的份额),黑线是从入口起的累加和,终点就是源项部分的误差。
The source part is a sum over the interior nodes, so its error can be read off dual volume by dual volume: . The continuous integral runs over the whole of , whereas the two half volumes of nodes and have no row (they are constraint rows); the two pieces and must be counted separately, or the per-volume errors do not add up. Figure 62 draws everything: the amber bars are , the violet bars the two half volumes (sign reversed, i.e. their share of the error), the black line the running sum from the inlet, whose end point is the error of the source part.
时 的各项为 0.243、0.025、0.019、0.063、0.052、0.026、0.003(内部控制体)、(入口半个控制体)与 (出口半个控制体),大小从 到 。内部项与入口半个控制体互相抵消到 ,所以净值 的 95% 是出口半个控制体; 的入口半个控制体()占净值 的 73%。到 , 的控制体项加起来是 ,入口半个控制体拿走 ,两者净 ;其余控制体(含出口半个控制体)贡献 :总的源项误差 是两块符号相反、大小相近的贡献之和(: 与 )。入口附近的控制体项最大,这与入口层有关,但数据既不支持也不排除把误差归因于层: 时误差几乎不随 变,未被分辨的层同样如此(见下文)。
At the terms for are 0.243, 0.025, 0.019, 0.063, 0.052, 0.026, 0.003 (interior volumes), (inlet half volume) and (outlet half volume); they range in size from to . The interior terms and the inlet half volume cancel to , so the net is 95% the outlet half volume; for the inlet half volume () is 73% of the net . At the volumes with add up to and the inlet half volume takes away , a net of ; the other volumes (the outlet half volume included) contribute : the total source error is the sum of two contributions of opposite signs and similar size (: and ). The volume terms next to the inlet are the largest, which has to do with the inlet layer, but the data neither support nor exclude blaming the layer: for the errors hardly vary with , which an unresolved layer would also produce (below).
梯度误差是切线误差的加权和
The gradient error is the weighted error of the tangent
同一个误差还有另一种读法,它把本节和第 10 节连起来。对 、, 而连续值是 ,所以
The same error has a second reading, which ties this section to Section 10. For and , while the continuous value is , so
项 是梯形公式的误差(约为 乘以被积函数导数的端点差),项 是图 48 里画出的切线误差按目标权重求和。两项之和与梯度误差相差 (五个网格,、、)。 时 的误差 里梯形公式只占 ,其余 是切线误差; 没有梯形项, 就是 的加权和。
Term is the error of the trapezoid rule (about times the jump of the derivative of the integrand across the interval), term is the tangent error drawn in Figure 48, summed with the objective weights. The two terms add up to the gradient error to within (five meshes, , and ). At the trapezoid rule is only of the error of ; the rest, , is tangent error; has no trapezoid term, and is the weighted sum of .
| N | :: | :梯形: trapezoid | :总: total | :: | :梯形: trapezoid | :总: total |
|---|---|---|---|---|---|---|
| 8 | −4.2e−2 | 7.5e−3 | −3.4e−2 | 3.4e−1 | −4.4e−3 | 3.3e−1 |
| 16 | 1.2e−2 | 1.9e−3 | 1.4e−2 | 8.0e−2 | −1.1e−3 | 7.9e−2 |
| 32 | 4.9e−3 | 4.6e−4 | 5.3e−3 | 1.6e−2 | −2.7e−4 | 1.6e−2 |
| 64 | 1.1e−3 | 1.2e−4 | 1.2e−3 | 2.7e−3 | −6.8e−5 | 2.6e−3 |
| 128 | 1.6e−4 | 2.9e−5 | 1.9e−4 | 2.8e−4 | −1.7e−5 | 2.6e−4 |
相位、振幅、黏性:什么在变
Phase, amplitude, viscosity: what changes
默认设计只是一个点。 时基态是 , 与 无关,而 对 与 是线性的,所以 的误差恰好是 ,一条振幅为 的正弦(在 处核对到 )。振幅 是格式对这个波的分辨误差:格点格式 ,,,(格心格式 0.311、0.176、0.099、0.049)。默认相位 恰好落在误差的零点附近: 的误差 只是 的 12%。在 把 扫过一个周期(图 63), 时 的梯度误差在 与 之间变化;对 41 个相位取均方根,格点格式与格心格式在 时相当(:0.235 对 0.230;:0.272 对 0.281), 则是格点格式更差(0.576 对 0.369)。从 起格点格式在 、 上明显更好, 时好 18、18 倍; 上 时格点格式仍更差, 时相当, 时好 4.5 倍。
The default design is one point. For the base state is , does not depend on , and is linear in and , so the error of is exactly , a sinusoid of amplitude (checked at to within ). The amplitude is the resolution error of the scheme for this wave: for the node scheme , , , (cell scheme 0.311, 0.176, 0.099, 0.049). The default phase happens to lie near a zero of the error: the error is only 12% of . Sweeping through one period at (Figure 63), the error of the -gradient at ranges from to ; as a root mean square over 41 phases the node and the cell scheme are comparable at (: 0.235 against 0.230; : 0.272 against 0.281) and the node scheme is worse for (0.576 against 0.369). From on the node scheme is clearly better for and , by factors 18 and 18 at ; for it is still worse at , equal at and 4.5 times better at .
| N | 格点 node | 格点 node | 格点 node | 格心 cell | 格心 cell | 格心 cell |
|---|---|---|---|---|---|---|
| 8 | 2.3e−1 | 2.7e−1 | 5.8e−1 | 2.3e−1 | 2.8e−1 | 3.7e−1 |
| 16 | 5.3e−2 | 6.4e−2 | 2.2e−1 | 1.3e−1 | 1.6e−1 | 1.5e−1 |
| 32 | 1.1e−2 | 1.4e−2 | 8.6e−2 | 7.5e−2 | 8.8e−2 | 8.7e−2 |
| 64 | 2.0e−3 | 2.4e−3 | 2.9e−2 | 3.7e−2 | 4.4e−2 | 1.3e−1 |
振幅与黏性的影响小得多。 从 变到 (,), 的梯度误差只在 到 之间变化, 的误差近似与 成正比(误差除以 在 1.67 到 1.76 之间;连续梯度 也与 成正比)。 从 变到 (): 的梯度误差在 到 之间, 的在 到 之间;(层宽变化十倍)时 的这两个误差只变化 9% 与 20%;只有 和 (层宽是一个网格间距的 0.44 倍与 0.87 倍, 与 )时它们才明显变小。这并不能判定是否是未被分辨的层造成了这些误差: 时层比一个网格间距薄至少 5.7 倍(),由这样的层造成的误差同样与 无关; 和 时层仍比网格间距薄,但只薄 2.3 倍与 1.1 倍,网格 Péclet 数也随之改变,所以那里误差的减小无法归于两者之一。本节没有一个实验把层与粗网格上的其他误差分开。
Amplitude and viscosity matter much less. With from to (, ) the error of the -gradient moves only between and , and the error of the -gradient is nearly proportional to (the error divided by stays between 1.67 and 1.76; the continuous gradient is proportional to too). With from to () the error of the -gradient moves between and and that of between and ; for (the layer width changes tenfold) these two errors vary by only 9% and 20%; they fall markedly only at and , when the layer is 0.44 and 0.87 of an interval wide ( and ). This does not decide whether the unresolved layer causes these errors: for the layer is at least 5.7 times thinner than one interval () and an error caused by such a layer would also be independent of ; at and the layer is still thinner than the interval, but only by the factors 2.3 and 1.1, and the mesh Péclet number changes with it, so the fall of the errors there cannot be assigned to one of the two. No experiment of this section separates the layer from the other coarse-mesh errors.
边界重构的作用
The role of the boundary reconstruction
格点格式的两个边界节点用单侧差分 、 做斜率。作为实验,把它们改成 (边界节点不重构,像格心格式的端单元),其余语句、前向与反向代码都不变(这一行之外的 model.js 原样不动;audit/audit_d.py 用独立的实现重复了这个实验)。默认设计下 时 的梯度误差从 变成 , 时从 变成 ,误差下降的观测阶( 到 ,逐倍加密)是 1.20、1.19、1.28、1.45、1.63、1.78,只是缓慢地趋近二阶;标准格式的误差小得多,并在 处变号。这与格心页的端单元实验方向一致:边界旁的迹是一阶还是二阶,在两种格式里都主导粗网格上振幅梯度的误差。所以在默认设计上,格点格式的 梯度误差小,依赖于它的边界节点做了二阶重构(以及上面扫相位看到的:默认相位靠近误差的零点),不需要层被分辨来解释。
The two boundary nodes of the node scheme take their slopes from one-sided differences, and . As an experiment, set instead (boundary nodes not reconstructed, like the end cells of the cell-centred scheme), with every other statement and the tangent and reverse code unchanged (model.js is otherwise untouched; audit/audit_d.py repeats the experiment with an independent implementation). At the default design the error of the -gradient goes from to at and from to at , and the observed orders of its decay (successive doublings, to ) are 1.20, 1.19, 1.28, 1.45, 1.63, 1.78, a slow approach to second order; the error of the standard scheme is much smaller and changes sign at . This agrees in direction with the end-cell experiment of the cell-centred page: whether the traces next to the boundary are first- or second-order accurate dominates the coarse-mesh error of the amplitude gradient in both schemes. At the default design, then, the small -gradient error of the node scheme depends on the second-order reconstruction of its boundary nodes (and, as the phase sweep above shows, on the default phase lying near a zero of the error); a resolved layer is not needed to explain it.
局限。以上是数值观察,不是定理:只测了所写的网格( 到 )、三种重构、 的一个周期、 到 与 到 。“恰好是正弦”只对 严格成立(用 的算例核对),在 是近似;把粗网格误差说成“波没有被分辨”只是一个解读,没有单独测量;边界重构实验只改了边界节点的斜率一项,没有排除别的因素。上面的面板可以改 、重构、、、 自己查看。
The limits. These are numerical observations, not theorems: only the meshes written ( to ), the three reconstructions, one period of , to and to were measured. “Exactly a sinusoid” holds strictly only for (checked with the case) and is approximate at ; saying that the coarse-mesh error means that “the wave is not resolved” is a reading that was not measured separately; the boundary experiment changes one thing, the slope of the boundary nodes, and does not exclude others. The panel above lets you change , the reconstruction, , and and look for yourself.
黏性导数
The viscosity derivative
连续的 精确为零,离散的却不为零。 的 列有一个精确的形式:源项部分是
The continuous is exactly zero; the discrete one is not. The column of has an exact form: its source piece is
其通量部分是 。记面上导数近似的误差
and its flux piece is . With the error of the face gradient
(这里 与第 6 节的误差传播矩阵 无关; 把两个约束行的乘子置零——它们不含 ,所以不进入这个和。对 逐行核对,差不到 ;对六个网格核对求和公式,差不到 。)若面上的导数近似等于精确导数, 列就是零。 是面上导数近似的误差按伴随在该面的跳跃加权求和;伴随在入口层里跳得最厉害,所以从 起几乎全部来自入口附近: 的面在 时贡献 91%, 时 98%, 时 100%( 时总和已低于 ,份额 102% 已没有意义); 时第一个面已在 ,没有面落在 内。连续问题里只有一个类比,不能算作印证:通量部分写成 时, 中有 来自 ;写成 时,同一区间只给 ——积分“来自哪里”取决于它的写法。
(Here has nothing to do with the error propagator of Section 6; sets the multipliers of the two constraint rows to zero, because they contain no and do not enter this sum. Checked row by row for : the difference is below ; the summation formula is checked on six meshes to within .) If the discrete face gradients were exact, the column would vanish. is the sum of the face-gradient errors weighted by the jump of the adjoint across the face, and the adjoint jumps most inside the inlet layer, so from on almost everything comes from next to the inlet: the faces with give 91% at , 98% at and 100% at (at the total is already below and the share, 102%, means little); at the first face already lies at , so no face lies in . The continuous problem offers an analogy, not an agreement: written as , the flux part takes of its from ; written as , the same interval gives : where an integral comes from depends on how it is written.
数字。 时 很小,但那是两个 部分( 与 )的偶然抵消: 给出 ,这是整个序列的峰值(绝对值),随后绝对值下降,逐倍加密( 到 )的观测阶依次为 1.46、2.01、2.56、3.08、4.02——不是常数,因为误差正在走向变号——并在 与 之间变号(从 到 ; 时是 )。图 65 显示两部分的收敛:源项部分从 起与 相差不到 1%(0.97%),通量部分从 起如此(0.92%)。格心页上同一个量在 时还有 ,格点格式是 。
The numbers. At , is small, but only by an accidental cancellation of two parts of order one ( and ): gives , the peak of the whole sequence (in absolute value); afterwards its magnitude falls, with observed orders 1.46, 2.01, 2.56, 3.08, 4.02 over successive doublings ( to ; not constant, because the error is approaching a sign change), and the sign changes between and (from to ; at ). Figure 65 shows the convergence of the two parts: the source part is within 1% of from on (0.97%), the flux part from on (0.92%). On the cell page the same quantity is still at ; the node scheme has .
随黏性的变化比较复杂。图 66 对六个 画出它随 的变化: 与 时在 到 的每个网格上都是负的;变为正值的网格随 增大而变粗(:;:;:)。与格心页不同,它不只是网格 Péclet 数 的函数:同一个 Péclet 数下 不同的点相差 34% 到 8.5 倍(13 组,每组 2 到 3 个点),所以这里没有给出按 Péclet 数的整理。
The dependence of on the viscosity is more complicated. Figure 66 plots it against for six values of : for and it is negative on every mesh from to ; the first mesh on which it is positive moves to coarser meshes as grows (: ; : ; : ). Unlike on the cell page it is not a function of the mesh Péclet number alone: points with the same Péclet number and different differ by 34% up to a factor of 8.5 (13 groups of two or three points), so this page does not organise it by the Péclet number.
格点格式与格心格式:比较
Node against cell: the comparison
同一问题、同一设计、同一目标,两种格式的梯度放在一起。下表是 的三种重构;下面是无限制器线性重构的误差随网格的变化。数字是在默认设计()上量的;上一节说明了为什么一个设计不能代表全部。
The same problem, design and objective; the gradients of the two schemes side by side. The first table is for the three reconstructions, the second the errors of unlimited linear reconstruction under refinement. The numbers are measured at the default design (); the previous subsection showed why one design does not represent them all.
| 重构Reconstruction | 分量Component | 格点:梯度node: gradient | 格点:误差node: error | 格心:梯度cell: gradient | 格心:误差cell: error |
|---|---|---|---|---|---|
| 一阶first order | −1.175176 | −1.0312 | −0.745986 | −0.6020 | |
| 一阶first order | −0.234594 | −0.3197 | −0.357342 | −0.4425 | |
| 一阶first order | 0.992575 | 0.9926 | −0.245484 | −0.2455 | |
| 无限制器线性unlimited linear | −0.178061 | −0.0341 | −0.486650 | −0.3426 | |
| 无限制器线性unlimited linear | 0.419904 | 0.3347 | −0.061039 | −0.1462 | |
| 无限制器线性unlimited linear | 0.003909 | 0.0039 | −0.309871 | −0.3099 | |
| 光滑限制器smooth limiter | −0.188210 | −0.0442 | −0.656527 | −0.5125 | |
| 光滑限制器smooth limiter | 0.553468 | 0.4683 | −0.168097 | −0.2533 | |
| 光滑限制器smooth limiter | −0.315000 | −0.3150 | −0.290780 | −0.2908 |
| N | 格点 node | 格点 node | 格点 node | 格心 cell | 格心 cell | 格心 cell |
|---|---|---|---|---|---|---|
| 8 | −3.4e−2 | 3.3e−1 | 3.9e−3 | −3.4e−1 | −1.5e−1 | −3.1e−1 |
| 16 | 1.4e−2 | 7.9e−2 | −7.0e−2 | −2.1e−1 | 3.0e−2 | −8.7e−2 |
| 32 | 5.3e−3 | 1.6e−2 | −3.9e−2 | −1.1e−1 | 4.3e−2 | 9.5e−2 |
| 64 | 1.2e−3 | 2.6e−3 | −1.4e−2 | −4.9e−2 | 2.8e−2 | 1.8e−1 |
| 128 | 1.9e−4 | 2.6e−4 | −3.5e−3 | −2.0e−2 | 1.4e−2 | 1.6e−1 |
| 256 | 2.1e−5 | −1.5e−5 | −6.0e−4 | −6.8e−3 | 5.3e−3 | 8.5e−2 |
| 512 | 7.7e−7 | −1.7e−5 | −7.1e−5 | −2.1e−3 | 1.7e−3 | 3.3e−2 |
| 1024 | −4.4e−7 | −6.0e−6 | −4.4e−6 | −5.8e−4 | 4.9e−4 | 1.0e−2 |
| 2048 | −1.9e−7 | −1.7e−6 | 7.8e−7 | −1.5e−4 | 1.3e−4 | 3.0e−3 |
| 4096 | −5.9e−8 | −4.6e−7 | 4.4e−7 | −3.9e−5 | 3.5e−5 | 7.9e−4 |
在默认设计上量到的事实:无限制器线性, 时格点格式的 梯度误差是格心格式的 1/10, 的 1/79,但 是 2.3 倍( 对 )——相位梯度更差。从 到 ,格点格式在三个分量上都更好:格心误差至少是格点误差的 20()、2.7()、2.4()倍。格点格式的误差在 ()、()、()处变号,所以观测阶不规则,要到 才接近 2(独立 Python 实现的数据,附录 C: 到 之间为 1.94、1.98、1.85);一阶重构:格点格式在 上并不更好(每个网格上格心误差都是格点误差的 0.58–0.99 倍),在 上只有 时更好(格心误差是格点误差的 0.19–11.26 倍),在 上从 起更好(1.66–1945 倍; 时是 0.25 倍)。图 67 画出全部。
What was measured at the default design: for unlimited linear reconstruction the node scheme's -gradient error at is 1/10 of the cell scheme's and its error 1/79, but its error is 2.3 times as large ( against ): the phase gradient is worse. From to the node scheme is better in all three components: the cell error is at least 20 (), 2.7 () and 2.4 () times the node error. The node scheme's errors change sign at (), () and (), so the observed orders are erratic and approach 2 only for (data of the independent Python implementation, Appendix C: 1.94, 1.98, 1.85 between and ). For first-order reconstruction the node scheme is not better for (the cell error is 0.58–0.99 times the node error on every mesh), is better for only at (the cell error is 0.19–11.26 times the node error), and is better for from on (1.66–1945 times; 0.25 times at ). Figure 67 draws all of it.
光滑限制器在加密下的行为不同于无限制器线性。 时格点格式在 上好 11.6 倍,在 上差(格心误差是格点误差的 0.54 倍),在 上相当(0.92 倍); 与 时 上好 30 倍与 15 倍。但在细网格上这个优势消失:从 起, 与 上格心误差与格点误差之比只在 1.01–1.03 与 1.02–1.11 之间; 时格点格式的这两个误差分别是它自己一阶格式的误差的 98% 与 95%,在 到 之间按一阶速度下降(观测阶 0.96 与 0.90)。 的误差不同: 时是一阶格式的 3.2 倍,按 1.70 阶下降,是格心格式的 1.5 倍。图 68 画出三个分量;第 15 节研究三种重构的阶。
The smooth limiter behaves differently from unlimited linear under refinement. At the node scheme is 11.6 times better for , worse for (the cell error is 0.54 times the node error) and comparable for (0.92 times); at and it is 30 and 15 times better for . On fine meshes the advantage disappears: from on, the ratio of the cell error to the node error stays between 1.01 and 1.03 for and between 1.02 and 1.11 for ; at the node scheme's errors of the two components are 98% and 95% of those of its own first-order scheme, and they fall at the first-order rate between and (observed orders 0.96 and 0.90). The error of is different: at it is 3.2 times that of the first-order scheme, falls at order 1.70, and is 1.5 times the cell scheme's. Figure 68 draws the three components; Section 15 studies the orders of the three reconstructions.
本节说明了这个误差来自哪里,也说明了什么不能说:它没有被证明是入口层造成的( 从 到 变化十倍, 的误差只变化 9%,未被分辨的层同样如此);逐控制体的项很大并互相抵消,净值只是小的余数,余数落在哪个控制体随 与分量而变;它不取决于单个设计点(相位变化一周,误差变号),部分取决于边界节点的重构。第 15 节测量梯度怎样收敛,第 16 节系统地研究它与连续伴随的一致性。
This section showed where the error comes from, and what cannot be said: it is not shown to be caused by the inlet layer (a tenfold change of , from to , moves the error by only 9%, which an unresolved layer would also give); the per-volume terms are large and cancel, the net is a small remainder, and which volume carries the remainder changes with and with the component; it does not belong to one design point (the error changes sign over one period of the phase), and it depends in part on the reconstruction of the boundary nodes. Section 15 measures how the gradients converge, and Section 16 studies their consistency with the continuous adjoint systematically.
有限差分验证:每个扰动都重新求解
Finite-difference check: re-solve at every perturbed design
本节实验:先猜一猜,把步长 从 一路缩到 ,中心差分会不会越来越接近伴随梯度?在哪个 最准、能有几位有效数字一致?然后用本节的步长扫描实验检验。
Experiment: guess first: as the step shrinks from to , does the central difference keep getting closer to the adjoint gradient? At which is it most accurate, and how many significant digits agree? Then test the guess with the step-scan lab of this section.
第 10、11 节用两种方法算出了离散目标的梯度:三个前向求解 给出 ,一个伴随求解 给出 ;默认设计下三种重构的两组结果相差不超过 。但这两条路都用到求导代码(第 8 节的前向语句和第 9 节的反向语句)。有限差分不对任何代码求导:它只在附近的设计上重新运行原始求解器。Newton 迭代解到舍入水平之后,它的值与 Newton 矩阵无关(本页的矩阵由前向语句生成,但不动点 不依赖它),所以是对第 8–12 节的一次独立检验(下面的陷阱说明迭代没有收敛时会怎样)。
Sections 10 and 11 computed the gradient of the discrete objective in two ways: three tangent solves give , and one adjoint solve gives ; at the defaults the two differ by at most for all three reconstructions. Both paths use derivative code (the tangent statements of Section 8 and the reverse statements of Section 9). A finite difference differentiates no code: it only runs the primal solver again at nearby designs. Once Newton has converged to rounding its value does not depend on the Newton matrix (the page builds it from the tangent statements, but the fixed point does not depend on it), so it is an independent check of Sections 8–12 (the traps below show what happens when the iteration is not converged).
被求导的函数是 ,即这套网格、这种重构下离散解的目标值(权重 是梯形公式)。差分验证的是 ,不是连续问题的精确梯度。默认网格上两者相差很远:无限制器线性重构的伴随梯度是 ,精确梯度是 。这个差别是离散误差,第 15、16 节才讨论;本节只问一件事:伴随算出的数是不是 的导数。
The function being differentiated is , the objective of the discrete solution on this mesh with this reconstruction (the weights are the trapezoid rule). The finite difference checks , not the exact gradient of the continuous problem. On the default mesh the two are far apart: the adjoint gradient with unlimited linear reconstruction is , the exact gradient . That difference is discretisation error, the subject of Sections 15 and 16; this section asks one thing only: is the number the adjoint computes the derivative of ?
两个差商
Two difference quotients
记 (第 13 至 15 节里下标 、、 对应 :,,)。把 在 处做 Taylor 展开,单侧差商和中心差商分别是
Write (in Sections 13 to 15 the subscripts , , stand for : , , ). Expanding in a Taylor series about gives the one-sided and the central quotient:
中心差商里偶次项相减抵消,所以它的截断误差是二阶的。步长 只是一个数:它不是网格间距 ,也不是导数记号。
In the central quotient the even powers cancel, so its truncation error is second order. The step is just a number: it is neither the mesh spacing nor a derivative symbol.
每个扰动设计都要完整重新求解
Every perturbed design needs a full re-solve
本身不含 (第 7 节:),设计只通过状态影响目标。所以只改 、不重新求解,差商就是 ,这就是“冻结状态”的错误。每个扰动设计 都需要自己收敛的状态 ,网格、重构和 Newton 迭代都与基准求解相同(初值是扰动设计下 在各节点的值,修正量不超过 时停止)。设计也改变边界数据:、 进入两个约束行 、,所以两个边界节点的值也随 、 一起动。图 69 上图是改变振幅后的状态;下图说明状态差商 随 趋向第 10 节的切线解 :、、 时最大偏差为 、、,相邻两次之比为 10.2 和 10.0,约 10 倍(一阶)。在两个边界节点上偏差只有舍入:约束行对 是线性的,差商恰好等于 (最大偏差不超过 )。
contains no of its own (Section 7: ); the design acts on the objective only through the state. Change without solving again and the quotient is : the "frozen state" bug. Every perturbed design needs its own converged state , with the mesh, the reconstruction and the Newton iteration of the base solve (started from the values of at the nodes of the perturbed design, stopped when the correction is at most ). The design also changes the boundary data: and enter the two constraint rows and , so the two boundary nodes move with and too. The top panel of Figure 69 shows the state after a change of the amplitude; the bottom panel shows the state quotient approaching the tangent solution of Section 10 as : the largest deviation is , and for , and , smaller by factors of 10.2 and 10.0: about 10 each time (first order). At the two boundary nodes the deviation is rounding only: the constraint rows are linear in , so the quotient is exactly (largest deviation at most ).
完整的验证流程如下(伪代码;本页的扫描实验就是这样算的):
The complete check reads (pseudocode; the step-scan lab of this page computes exactly this):
U0 = solve(D) # Newton:初值 u*(x_j; D),修正量 |dU| <= 1e-13 时停止
J0 = w . U0
for k in (a, phi, nu):
for tau in 1e-1, 1e-2, ..., 1e-12:
Up = solve(D + tau e_k) # 完整的重新求解:状态会变
Um = solve(D - tau e_k) # 对 nu 只在 nu - tau > 0 时
FD_f = (w . Up - J0) / tau
FD_c = (w . Up - w . Um) / (2 tau)
与 Dbar_k = (R_D^T psi)_k 比较 # 第 11 节的伴随梯度U0 = solve(D) # Newton: initial guess u*(x_j; D), stop when |dU| <= 1e-13
J0 = w . U0
for k in (a, phi, nu):
for tau in 1e-1, 1e-2, ..., 1e-12:
Up = solve(D + tau e_k) # a full re-solve: the state changes
Um = solve(D - tau e_k) # for nu only if nu - tau > 0
FD_f = (w . Up - J0) / tau
FD_c = (w . Up - w . Um) / (2 tau)
compare with Dbar_k = (R_D^T psi)_k # the adjoint gradient of Section 11一次中心差分要 2 次非线性求解,三个分量 6 次;扫描 12 个步长最多 72 次( 的点不算),再加 1 次基准求解。伴随只要 1 次线性求解就给出全部三个分量(第 11 节)。这里设计只有 3 个分量,差分的负担不重;设计有成千上万个分量时,差分只能抽查少数几个方向(下面的实验可以选一个混合方向)。
One central difference costs 2 nonlinear solves, 6 for the three components; a scan over 12 steps costs up to 72 (the points with are skipped), plus the base solve. The adjoint gives all three components with one linear solve (Section 11). With 3 design components the finite differences are affordable here; with thousands of components they can only spot-check a few directions (the lab below can pick a mixed direction).
截断误差与舍入误差:V 形
Truncation against rounding: the V
两种误差相互竞争。截断误差随 减小(单侧差商斜率 1,中心差商斜率 2)。舍入误差正相反: 只有约 16 位有效数字,它的舍入误差 不随 变小,差商却要把它除以 :
Two errors compete. The truncation error shrinks with (slope 1 for the one-sided quotient, slope 2 for the central one). The rounding error does the opposite: carries about 16 significant digits, its rounding error does not shrink with , and the quotient divides it by :
在双对数图上右支是斜率 2 的直线,左支是斜率 的直线,合起来是一个 V 形。令两项之和对 的导数为零,得到最佳步长 。这些高阶导数可以由伴随梯度差分得到:在 两侧各做一次伴随求解(步长是 ;差分的是精确导数,不是目标值),两侧梯度的中心差分是 ,再用上 处的梯度做二阶差分得到 。默认设计下对 得 、,于是 ,最小误差约 。扫描中实际最好的是 ,误差不到 。图 70 把预测线和实测点画在一起。
On log-log axes the right branch is a line of slope 2 and the left branch a line of slope : together they form a V. Setting the derivative of the sum to zero gives the best step . The higher derivatives come from differencing the adjoint gradient: one adjoint solve on each side of (with the step ; what is differenced is an exact derivative, not the objective); the central difference of the two gradients is , and the second difference with the gradient at is . At the defaults this gives and for the amplitude, so and a smallest error of about . The best point of the actual scan is , with an error below . Figure 70 draws the predicted lines and the measured points together.
图 71 对三个分量各做一次扫描。右支都贴着斜率 1、2 的参考线: 时,实测中心差分误差与预测 之比与 1 的偏差,对 、、 三个分量都小于 。左支沿着舍入线上升: 的中心差分点没有一个高过舍入线的 2 倍(它们落在舍入线的 0.01 到 1.1 倍之间,因为 的舍入误差会部分抵消)。三个分量的右支高度差得很远,因为三阶导数差得很远:无限制器线性的 ,而 、。所以 的 V 形谷底在 , 和 的在 。 很小, 时 ,中心差分根本不能用,图和表里没有这些点。
Figure 71 scans each of the three components. All right branches follow the reference lines of slopes 1 and 2: at the measured central error over the prediction differs from 1 by less than for all three components , , . The left branches rise along the rounding line: no central-difference point with lies above 2 times it (the points lie between 0.01 and 1.1 times it, because the rounding errors of partly cancel). The heights of the three right branches are very different because the third derivatives are: for unlimited linear , but and . So the bottom of the V of is at , and that of and at . is small, and for the central difference cannot even be formed (), so those points are missing from the figure and the tables.
下表是三种重构、三个分量的中心差分误差 ( 是第 11 节的伴随梯度),粗体是每一列的最小值(接近舍入线的最佳误差随算术实现可差到 10 倍,只应按数量级引用):
The table lists the central-difference errors for the three reconstructions and components ( is the adjoint gradient of Section 11); bold is the smallest value of each column (entries near the rounding line change with the arithmetic by up to a factor 10: quote them by order of magnitude only):
| 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter | |||||||
|---|---|---|---|---|---|---|---|---|---|
| 5.6e−3 | 1.1e−2 | — | 9.3e−6 | 2.2e−2 | — | 9.9e−5 | 7.4e−2 | — | |
| 5.5e−5 | 1.1e−4 | — | 8.7e−8 | 2.2e−4 | — | 2.7e−7 | 6.2e−3 | — | |
| 5.5e−7 | 1.1e−6 | 1.1e−5 | 8.7e−10 | 2.2e−6 | 6.6e−6 | 2.8e−9 | 6.0e−5 | 2.8e−5 | |
| 5.5e−9 | 1.1e−8 | 1.1e−7 | 6.6e−12 | 2.2e−8 | 6.6e−8 | 2.9e−11 | 6.0e−7 | 2.8e−7 | |
| 5.1e−11 | 1.2e−10 | 1.1e−9 | 9.9e−12 | 2.4e−10 | 6.5e−10 | 5.3e−12 | 6.0e−9 | 2.8e−9 | |
| 1.4e−10 | 1.4e−10 | 1.2e−11 | 9.0e−11 | 8.5e−11 | 5.9e−11 | 1.2e−10 | 2.7e−10 | 1.9e−10 | |
| 1.5e−9 | 5.3e−10 | 8.8e−10 | 2.6e−9 | 6.9e−10 | 3.9e−10 | 5.5e−10 | 1.5e−9 | 2.3e−9 | |
| 8.1e−9 | 1.6e−8 | 1.1e−8 | 1.5e−9 | 4.9e−9 | 2.9e−9 | 1.6e−8 | 1.5e−8 | 1.2e−8 | |
| 8.6e−8 | 3.9e−8 | 6.7e−8 | 2.5e−7 | 1.6e−8 | 8.1e−8 | 1.0e−7 | 1.1e−7 | 1.6e−7 | |
| 1.5e−6 | 8.2e−7 | 4.4e−8 | 2.5e−7 | 2.4e−7 | 1.4e−6 | 2.4e−6 | 5.5e−7 | 8.2e−7 | |
| 重构Reconstruction | 分量Component | 伴随梯度 Adjoint gradient | 中心差分最佳 Best central | 误差Error | 单侧差分最佳 Best one-sided | 误差Error |
|---|---|---|---|---|---|---|
| 一阶first order | −1.175176 | 5.1e−11 | 1.9e−8 | |||
| 一阶first order | −0.234594 | 1.2e−10 | 4.9e−8 | |||
| 一阶first order | 0.992575 | 1.2e−11 | 2.3e−10 | |||
| 无限制器线性unlimited linear | −0.178061 | 6.6e−12 | 1.5e−9 | |||
| 无限制器线性unlimited linear | 0.419904 | 8.5e−11 | 1.6e−8 | |||
| 无限制器线性unlimited linear | 0.003909 | 5.9e−11 | 3.0e−8 | |||
| 光滑限制器smooth limiter | −0.188210 | 5.3e−12 | 8.3e−9 | |||
| 光滑限制器smooth limiter | 0.553468 | 2.7e−10 | 6.3e−8 | |||
| 光滑限制器smooth limiter | −0.315000 | 1.9e−10 | 2.3e−8 |
读表:中心差分在最佳步长处与伴随梯度相差 到 ,这些梯度有 7 到 10 位有效数字一致;单侧差分的最佳误差是 到 。最佳步长因分量和重构而异,事先并不知道,所以只算一个 的差分不够,要扫描。常用的折中 在九个序列上给出的误差最大是 ,出在光滑限制器的 分量(它的三阶导数 最大):九个序列里至少有 6 个的误差与预测的截断项 相差不到 20%,其余的接近舍入水平(其中有两个 分量:无限制器线性和光滑限制器,它们的三阶导数很小)。这个步长对 也要小心:。
Reading the tables: at its best step the central difference differs from the adjoint gradient by to , so 7 to 10 significant digits of these gradients agree; the best one-sided errors are to . The best step differs by component and reconstruction and is not known in advance, so a difference at one is not enough: scan. The common compromise gives a largest error of on the nine series, in the -component of the smooth limiter (which has the largest third derivative, ): at least 6 of the nine have an error within 20% of the predicted truncation term , and the others are near rounding level (they include the two -components, unlimited linear and smooth limiter, whose third derivatives are tiny). The step needs care for too: .
自己扫一遍:改变上方的网格、重构或设计(点“求解并更新”),选一个分量,看 V 形怎样移动;也可以选一个混合方向 ,用一对差分检验整个梯度在这个方向上的分量 。
Scan it yourself: change the mesh, the reconstruction or the design above (press Solve & update), pick a component and watch the V move; or pick the mixed direction , where one pair of differences checks the whole gradient along that direction, .
割线如何变成切线
How the secant turns into the tangent
单侧差商 有一个几何意义:它是曲线 上过 与 两点的割线的斜率;切线的斜率是 ,也就是伴随给出的 。下面的播放器把 每步缩小 10 倍:前两步割线与切线还看得出区别(在放大图里;主图里只有 分量看得出,、 分量在 时已经重合:斜率只差 0.6% 与 1.9%),之后在图上已经重合,差别只留在数字里——看一致的有效数字位数先增后减。
The one-sided quotient has a geometric meaning: it is the slope of the secant through the points and of the curve ; the slope of the tangent is , the of the adjoint. The player below divides by 10 at every step: for the first two steps the secant and the tangent can still be told apart (in the zoomed panel; in the main panel only for , since for and they already coincide at : the slopes differ by 0.6% and 1.9%), after that they coincide on the drawing and the difference lives only in the digits: watch the number of agreeing significant digits rise and then fall.
误差下限由谁决定:Newton 迭代的停止准则
What sets the error floor: the Newton stopping criterion
两个 都不是由精确离散解算出的:Newton 迭代在修正量不超过 时停止,最后一个修正量并不加上去。这个容差会不会就是误差下限?量一量。默认设计下基准求解的修正量依次是 、、、:二次收敛一步就越过了容差,返回的状态已经准确到舍入水平。所以默认容差 并不决定误差下限,舍入才决定(左支贴着舍入线,见上)。
Neither value of comes from the exact discrete solution: Newton stops when the correction is at most , and the last correction is not added. Is that tolerance the error floor? Measure it. At the defaults the corrections of the base solve are , , , : quadratic convergence jumps past the tolerance in one step, and the state returned is already accurate to rounding. So the default tolerance does not set the error floor; rounding does (the left branch follows the rounding line, see above).
放宽容差,情形就变了(图 72,分量 )。容差 时只加上两个修正量(第三个 已低于容差), 到 之间误差停在 附近(相对偏差小于 ),不再随 变化。原因是:只要两次求解的迭代步数相同,只迭代 步的 Newton 迭代值 (这里 是迭代步数;第 5 节记作 )本身就是 的光滑函数,差商算出的是它的导数,偏差是一个固定的量,而不是被 放大的噪声。这里 :沿 Newton 迭代逐步求导, 的导数与 相差的正是这个数。容差 时只加上一个修正量(第二个 已低于容差),平台落在 的偏差 上( 到 之间相对偏差小于 )。容差 和默认的 一样让三个修正量通过: 的导数只差 ,状态已收敛到舍入水平,曲线就是默认设置的 V 形。
Loosen the tolerance and the picture changes (Figure 72, component ). With the tolerance only two corrections are added (the third, , is below the tolerance), and from to the error stays near (relative deviation below ), no longer depending on . The reason: as long as both solves take the same number of steps, the Newton iterate after steps ( counts steps here; Section 5 writes ) is itself a smooth function of ; the quotient computes its derivative, and the deviation is a fixed bias, not noise amplified by . Here : differentiating along the Newton iteration, the derivative of differs from by exactly this number. With the tolerance only one correction is added (the second, , is below the tolerance), and the plateau sits at the bias of (relative deviation below from to ). The tolerance lets three corrections through, like the default : the derivative of differs by less than , the state is converged to rounding, and the curve is the V of the default.
热启动(以扰动前的解为初值)加一个绝对容差最危险:第一个修正量约为 ,它一旦小于容差,求解器就原样返回初值,两个 完全相等,差商为 0。容差 时 的点全是这样,误差就是梯度本身。
The most dangerous combination is a warm start (from the solution before the perturbation) with an absolute tolerance: the first correction is about , and once it is below the tolerance the solver returns its initial value unchanged, the two values of are identical and the quotient is 0. With the tolerance this happens at every : the error is the gradient itself.
差分验证的四个陷阱:(1)不重新求解,差商是 ;(2)容差太松,差商趋向只迭代 步的迭代值的导数,而不是 的导数,误差停在一个与 无关的平台上;(3)热启动加绝对容差,小 时求解器不动,差商为 0;(4)热启动只走一步时, 的差商(当 与 无关时)就是把 换成它的中心差分近似 后的切线结果。对 ( 是 的二次函数, 与 无关),它就是切线结果本身(、容差 时与 只差不到 );对 差 ;对 连 都变了(差 )。这时差分已不独立,Jacobian 里的错误会被它原样复制。可靠的做法:每个扰动点冷启动、收敛到舍入水平、扫描多个 。另外 的步长必须小于 本身。
Four traps of the finite-difference check: (1) without a re-solve the quotient is ; (2) with a loose tolerance the quotient tends to the derivative of the iterate after Newton steps, not to the derivative of , and the error stays on a plateau independent of ; (3) with a warm start and an absolute tolerance the solver does not move for small and the quotient is 0; (4) when a warm start takes a single step, the quotient of is (when does not depend on ) the tangent result with replaced by its central difference . For ( is quadratic in and does not depend on ) that is the tangent result itself (at and the tolerance it differs from by less than ); for the difference is ; for even changes (difference ). Then the difference is no longer independent and copies any error of the Jacobian. The safe recipe: a cold start at every perturbed design, convergence to rounding, a scan over many . And the step in must be smaller than itself.
最佳步长也随网格变化
The best step also changes with the mesh
在一个网格上选好的步长换一个网格未必还合适。光滑限制器的 分量(默认设计,完整重新求解的中心差分): 时 就够(误差 ),可是 到 时 的误差是 到 ,超过 ; 时这四个网格的误差都小于 。原因是 :光滑限制器在 方向上的三阶导数随网格变大。所以扫描要在每个网格上重做;独立的 Python 程序自己的梯度检验用的就是 ,在这四个网格的 轴上碰到了同样的截断误差(第 15 节)。
A step chosen on one mesh may not suit another. The -component of the smooth limiter (default design, central differences of full re-solves): at is enough (error ), but at to the error at is to , above ; at the error on these four meshes is below . The reason is : the third derivative of the smooth limiter in the direction grows on these meshes. So the scan has to be repeated on every mesh; the independent Python program's own gradient check uses and meets the same truncation error on the axis of these four meshes (Section 15).
| 的误差error at | 的误差error at | |
|---|---|---|
| 8 | 2.8e−9 | 1.9e−10 |
| 64 | 1.5e−8 | 3.4e−10 |
| 128 | 5.3e−8 | 4.0e−10 |
| 256 | 5.8e−8 | 4.4e−10 |
| 512 | 2.6e−8 | 2.6e−10 |
| 1024 | 8.4e−9 | 2.9e−10 |
差分能抓住的两类错误
Two kinds of error that a difference catches
伴随梯度是 ,它的每一项都来自 的某一行。格点格式里 有两部分:内部行 来自精确源项 和通量对 的显式依赖 ;两个约束行 、 则带着边界数据对设计的导数 、,乘上的是边界乘子 、。这两部分各漏掉一个,会得到什么?下表(无限制器线性,默认设计,):漏掉通量对 的显式导数,只有 分量变,伴随给出 ,而差分给出 (正确的伴随值是 );把边界数据当成常数(两个约束行不带 ), 分量不变,、 分量变成 、,差分给出 、。两个错误都远大于差分误差,一眼就能看出。注意第二个:边界乘子 、 不小,漏掉它们的贡献 对 分量是 ,对 分量是 。
The adjoint gradient is , and each term of it comes from a row of . On the node-centred page has two parts: the interior rows come from the exact source and the explicit dependence of the flux on , ; the two constraint rows and carry the derivatives of the boundary data, and , multiplied by the boundary multipliers and . Leave out one of the two parts: what happens? The table below (unlimited linear, default design, ): without the explicit -derivative of the flux only the -component changes, the adjoint gives and the difference (the correct adjoint value is ); with the boundary data treated as constants (the two constraint rows without ) the -component stays and the and components become and , where the difference gives and . Both errors are far larger than any difference error, visible at a glance. Note the second: the boundary multipliers and are not small, and the contribution that is lost, , is for the -component and for the -component.
| 分量Component | 伴随梯度(正确)Adjoint gradient (correct) | 最佳中心差分Best central difference | 漏掉通量的 项Without the term of the flux | 边界数据当常数Boundary data as constants |
|---|---|---|---|---|
| −0.178061 | −0.178061 | −0.178061 | 0.457664 | |
| 0.419904 | 0.419904 | 0.419904 | 2.117602 | |
| 0.003909 | 0.003909 | 2.180107 | 0.003909 |
选一个错误,看差分怎样揭穿它(实验里的“错误”是对 的列做的有意改动,伴随求解本身不变):
Pick a planted error and watch the difference expose it (the "errors" of the lab are deliberate changes to the columns of ; the adjoint solve itself is unchanged):
差分能做什么、不能做什么
What finite differences can and cannot do
- 能:只用原始求解器就检验整条链——前向语句、反向语句、(包括两个约束行)、转置和线性求解。上面两个错误都会被抓住。Can: check the whole chain with the primal solver only: tangent statements, reverse statements, (the constraint rows included), the transpose and the linear solves. Both errors above are caught.
- 不能:给出超过约 10 位的数字,而且只有扫描之后才知道哪个 可信;最佳 随分量、网格和重构变化。Cannot: deliver more than about 10 digits, and only a scan tells which to trust; the best changes with the component, the mesh and the reconstruction.
- 不划算:每个分量 2 次非线性求解;伴随 1 次线性求解给出全部分量。Not cheap: 2 nonlinear solves per component; the adjoint gives all components with one linear solve.
- 不回答离散误差的问题:差分与伴随都是 的导数,它们离精确梯度多远是第 15、16 节的事。第 14 节的复数步长去掉了 V 形的舍入支。Does not answer the question of discretisation error: the difference and the adjoint are both derivatives of ; how far they are from the exact gradient is the business of Sections 15 and 16. The complex step of Section 14 removes the rounding branch of the V.
复数步长验证:没有相减的差商
Complex-step check: a quotient without subtraction
本节实验:先算一算,在双精度里 等于多少?那么 时第 13 节的中心差分给出什么?复数步长 又给出什么?读完本节,用复数步长实验验证。
Experiment: work it out first: what is in double precision? Then what does the central difference of Section 13 return at ? And what does the complex step return? After the section, check with the complex-step lab.
第 13 节的差分受步长两头夹击: 大时截断误差大; 小时两个几乎相等的 相减,舍入被 放大。双精度里 就等于 本身,所以 的差分是 。复数步长去掉了相减这一步。
The differences of Section 13 are squeezed from both ends of the step: at large the truncation error is large; at small two nearly equal values of are subtracted and the rounding is amplified by . In double precision is itself, so the difference at is . The complex step removes the subtraction.
虚数方向上的一步
A step in the imaginary direction
设 作为 的函数可以解析延拓到复平面(下一小节说明为什么可以)。把实的步长换成虚的 ,在 处展开:
Suppose , as a function of , extends analytically into the complex plane (the next subsection says why it does). Replace the real step by the imaginary step and expand about :
虚部与实部分开:
Separate the imaginary and the real parts:
导数直接从虚部读出,不需要两个相近的数相减,舍入不再被 放大。函数里含有求解器时,这个舍入就是复数求解的舍入,是 的若干倍(按下表九个序列的平台计,是 1 到 60 倍,无限制器线性和光滑限制器的 分量最大):这里 在 0.14 到 1.31 之间;它不是 :无限制器线性的 只有 0.0039,却是由大得多的项相加而成的()。后面表中复数步长与伴随、切线梯度之差不超过 。截断误差 与中心差分的大小相同、符号相反。 时,无限制器线性的三个分量的截断误差都不超过 ,完全看不见。图 73 用一个看得见的 画出这一步。
The derivative is read off the imaginary part directly; nothing nearly equal is subtracted, so the rounding is no longer amplified by . When the function contains a solver, the rounding is that of the complex solve, a multiple of (1 to 60 on the nine series of the table below, largest for the components of unlimited linear and of the smooth limiter): here lies between 0.14 and 1.31; it is not : for unlimited linear is only 0.0039, a sum of much larger terms (). In the table below the complex step differs from the adjoint and the tangent gradients by at most . The truncation error has the size of the central difference's, with the opposite sign. At it is at most for the three components of the unlimited linear scheme: invisible. Figure 73 draws the step with a visible .
对代码的要求:只用解析运算
What the code must be: analytic operations only
复数步长要求程序的每一步都是某个解析函数在实轴上的限制。加、减、乘、除和 、 都是;绝对值、、、按符号分支的 if 都不是。第 5 节的残差只用了加减乘除,设计里的 、 通过 、 和边界数据 、 进入(两个约束行 、 对复数设计照样成立):Rusanov 速度是常数 ,没有 ,三种斜率是 、 和光滑限制器 。光滑限制器的分母在实轴上不小于 ,所以它在每个实状态附近都是解析的,没有开关:三种重构都可以做复数步长。两个边界节点上的单侧斜率是线性的,不引入开关。 可逆时,由隐函数定理,离散解随设计解析地变化, 也就可以解析延拓。minmod 之类的限制器在开关处不可导,复数步长会默默地跟着实部选中的分支走。
The complex step needs every operation of the program to be the restriction of an analytic function to the real axis. Addition, subtraction, multiplication, division, and are; an absolute value, , and an if on a sign are not. The residual of Section 5 uses only the four operations, and the and of the design enter through , and the boundary data , (the two constraint rows and hold for a complex design as they stand): the Rusanov speed is the constant , there is no , and the three slopes are , and the smooth limiter . The denominator of the smooth limiter is at least on the real axis, so it is analytic near every real state and has no switch: all three reconstructions can be complex-stepped. The one-sided slopes at the two boundary nodes are linear and introduce no switch. Where is invertible, the implicit function theorem makes the discrete solution an analytic function of the design, and with it . A limiter such as minmod is not differentiable at its switch, and the complex step would silently follow the branch the real parts select.
model.js 的 residualComplex 就是第 5 节的残差代码,只把算术换成复数:实部和虚部各存一个数组,语句一条不改;,。其中没有一行是手工求导的,所以它是对第 8 节前向语句的独立检验。
The function residualComplex of model.js is the residual code of Section 5 with the arithmetic replaced: the real and the imaginary parts are two arrays, and not one statement changes; and . Not one line of it is differentiated by hand, which makes it an independent check of the tangent statements of Section 8.
复数版本必须用代数平方 ,不能用模的平方 :共轭会把虚部消掉。目标 是线性的,所以 。
The complex version must use the algebraic square , never the squared modulus : the conjugate cancels the imaginary part. The objective is linear, so .
状态也变成复数
The state becomes complex
设计 让残差变成复数,离散解也随之是复数。在 处展开:
The design makes the residual complex, and with it the discrete solution. Expand about :
虚部除以 就是第 10 节的切线解。函数里含有求解器时,复数步长本身就是一次复数求解:要解复数残差 。用 Newton 法,Jacobian 取实部的 :不动点 与所用矩阵无关,舍去复 Jacobian 的 虚部不改变结果;但它使修正量的虚部改变 (只有接近收敛时才可忽略),并使虚部晚一步收敛。把残差的虚部展开:
Its imaginary part over is the tangent solution of Section 10. When the function contains a solver, the complex step is itself a complex solve: the complex residual must be solved. Use Newton with the real Jacobian : the fixed point does not depend on the matrix, so dropping the imaginary part of the complex Jacobian leaves the result unchanged; it changes the imaginary part of a correction by (negligible only near convergence) and makes the imaginary part lag one step. Expand the imaginary part of the residual:
令它为零,正是第 10 节的切线方程 :虚部的一次 Newton 修正就是一次前向求解(乘以 )。于是
Setting it to zero is exactly the tangent equation of Section 10: one Newton correction of the imaginary part is one tangent solve (times ). Hence
两个边界节点上这一步是精确的:约束行 对 的 Jacobian 行是单位行,所以虚部除以 在第一帧(初值 本身)就是 ,,与切线解在这两个节点上的值相同(三个分量的最大偏差不超过 )。
At the two boundary nodes the step is exact: the Jacobian row of the constraint row is a unit row, so the imaginary part over is and from the first frame on (the initial guess itself), the values of the tangent solution at those two nodes (largest deviation over the three components at most ).
下面的播放器从页面的初值 出发做复数 Newton 迭代(),每一步用当前实部的 Jacobian。 时实部与第 5 节的原始迭代一致到最后一位(; 时 项约为 ,远小于 实部的最后一位):一阶和无限制器线性逐位相同;光滑限制器的复数除法与实数除法的舍入可以差最后一位,所以在某些网格和设计上它的个别迭代值相差几个最后一位(默认设计、 上三种重构都逐位相同)。虚部晚一步: 恰好是在实部迭代值 处求得的切线解(每一步都取整步时成立,默认设计下正是如此;差不超过 )。残差实部在 3 次修正后到达舍入水平,虚部在 4 次修正后(图 74)。
The player below runs the complex Newton iteration from the page's initial guess (), with the Jacobian of the current real part at every step. At the real part is the primal iteration of Section 5 to the last bit (; at the terms, about , are far below the last bit of the real parts): bit for bit for first order and unlimited linear; with the smooth limiter the complex division rounds differently from the real one in the last place, so on some meshes and designs some of its iterates differ by a few units in the last place (at the default design and all three reconstructions are bit for bit). The imaginary part lags one step: is exactly the tangent solution at the real iterate (as long as every step is a full step, as at the defaults; to or better). The real residual reaches rounding after 3 corrections, the imaginary one after 4 (Figure 74).
图 75 把收敛后的虚部除以 与第 10 节的切线解画在一起:三个分量的最大差都不超过 。表中是三种重构下 的复数步长梯度,与伴随梯度、切线梯度相差都不超过 ,而第 13 节的中心差分即使在最佳步长处,误差也有 到 。
Figure 75 draws the converged imaginary part over over the tangent solution of Section 10: the largest differences for the three components are at most . The table lists the complex-step gradients at for the three reconstructions: they differ from the adjoint and the tangent gradients by at most , while the central difference of Section 13 gets only to to at its best step.
| 重构Reconstruction | 分量Component | () at | 与伴随之差Minus adjoint | 与切线之差Minus tangent | 中心差分最佳误差Best central FD |
|---|---|---|---|---|---|
| 一阶first order | −1.175176203765 | 4.4e−16 | 2.2e−16 | 5.1e−11 | |
| 一阶first order | −0.234593749400 | 3.9e−16 | 5.6e−17 | 1.2e−10 | |
| 一阶first order | 0.992574866977 | 8.9e−16 | 6.7e−16 | 1.2e−11 | |
| 无限制器线性unlimited linear | −0.178061153176 | 2.5e−16 | 7.5e−16 | 6.6e−12 | |
| 无限制器线性unlimited linear | 0.419903873475 | 6.7e−16 | 7.2e−16 | 8.5e−11 | |
| 无限制器线性unlimited linear | 0.003908842857 | 9.1e−16 | 8.8e−16 | 5.9e−11 | |
| 光滑限制器smooth limiter | −0.188210330105 | 5.6e−16 | 2.8e−16 | 5.3e−12 | |
| 光滑限制器smooth limiter | 0.553467823395 | 3.3e−16 | 8.9e−16 | 2.7e−10 | |
| 光滑限制器smooth limiter | −0.314999960484 | 3.7e−15 | 3.9e−15 | 1.9e−10 |
步长扫描:没有 V 形
The step scan: no V
把第 13 节的扫描延长到 (图 76)。 大时复数步长与中心差分的误差大小相同、符号相反: 时 、 分量的比值是 、; 时是 、。 时,分量 (无限制器线性)的复数步长误差一直低于 。三种重构的全部九个序列在 的所有点都停在舍入水平(不超过 ),一直到 。中心差分则在 到 见底后又回升。 很大时复数 Newton 迭代可能不收敛: 时,无限制器线性的 分量、光滑限制器的 分量和 分量不收敛,图里没有画这些点。
Extend the scan of Section 13 down to (Figure 76). At large the complex step and the central difference have errors of the same size and opposite sign: at their ratio is and for and , at and . For the complex-step error of component (unlimited linear) stays below . All nine series of the three reconstructions stay at rounding level at every point with (at most ), down to . The central difference instead bottoms out at to and rises again. At a very large the complex Newton iteration may not converge: at for the -component of unlimited linear, the -component of smooth limiter, the -component of smooth limiter; those points are not drawn.
附近:哪部分是截断,哪部分是舍入
Around : what is truncation and what is rounding
取 到 时,复数步长还剩一点截断误差,它藏在舍入误差里,双精度的数看不清。要看清,需要一个没有舍入的参照:把同一个复数求解用扩展精度重做(figs_e50.js:BigInt 定点数,小数部分 256 个二进制位;复数残差压到 以下,做法是用实 Jacobian 的双精度 LU 做迭代精化,残差用扩展精度算;检验用 mpmath 的 80 位十进制精度计算)。这样得到的 只剩截断误差( 取 的复数步长,它的截断误差小于 )。
For from to the complex step still holds a little truncation error, hidden in the rounding, so that the double-precision numbers cannot show it. To see it, take a reference without rounding: repeat the same complex solve in extended precision (figs_e50.js: BigInt fixed point with 256 fractional bits; the residual of the complex solve is brought below by iterative refinement, with the residual in extended precision and the corrections from the double-precision LU of the real Jacobian; the checks compare with mpmath at 80 digits). What remains of is truncation only ( is the complex step at , whose truncation error is below ).
第一,扩展精度的误差就是 Taylor 项: 时它与 之比与 1 相差不超过 ,九个序列都是(三阶导数本身由第 13 节的伴随梯度差分得到,所以这也是对它的一次独立检验)。截断误差的公式没有问题,复数步长没有别的误差来源。
First, the error in extended precision is the Taylor term: at its ratio to differs from 1 by at most for all nine series (the third derivative itself is differenced from adjoint gradients as in Section 13, so this also checks it independently). The formula for the truncation error is right, and the complex step has no other source of error.
第二,什么叫“还看得见截断误差”?这里取:扩展精度的误差大于这个序列在 时双精度的最大误差的两倍。 时截断误差已小于 ,剩下的全是舍入,这个最大误差就是该序列的舍入平台,九个序列的平台在 到 之间。按这个标准, 时有 7 个序列还看得见截断误差:一阶的 分量、一阶的 分量、一阶的 分量、无限制器线性的 分量、无限制器线性的 分量、光滑限制器的 分量、光滑限制器的 分量; 时有 0 个。 是过渡:九个序列里最大的截断项 是 (光滑限制器的 分量),与平台同量级。能不能看见取决于那一次的舍入,所以这一步不数个数;下表是 的九个序列,最后一列是扩展精度的误差与平台之比。
Second, what does "still shows truncation" mean? Here: the error in extended precision is larger than twice the largest double-precision error of that series at . There the truncation error is below and everything left is rounding, so that largest error is the rounding plateau of the series; the nine plateaus lie between and . By this criterion 7 series still show truncation at : the -component of first order, the -component of first order, the -component of first order, the -component of unlimited linear, the -component of unlimited linear, the -component of smooth limiter, the -component of smooth limiter; at the number is 0. is the transition: the largest truncation term of the nine series is (the -component of smooth limiter), about the size of the plateaus. Whether it can be seen depends on the rounding of that one run, so the page does not count series at this step; the table lists the nine series at , its last column being the error in extended precision over the plateau.
| 重构Reconstruction | 分量Component | Taylor 项 Taylor term | 扩展精度Extended | 只舍入实部Re U rounded | 双精度Double | 舍入平台Plateau | 扩展精度 / 平台Extended / plateau | |
|---|---|---|---|---|---|---|---|---|
| 一阶first order | −3.32e0 | 5.54e−17 | 5.54e−17 | 5.93e−17 | −4.44e−16 | 8.88e−16 | 0.1 | |
| 一阶first order | 6.86e0 | −1.14e−16 | −1.14e−16 | −1.18e−16 | −9.16e−16 | 9.44e−16 | 0.1 | |
| 一阶first order | −6.86e1 | 1.14e−15 | 1.14e−15 | 1.87e−15 | 2.44e−15 | 1.22e−15 | 0.9 | |
| 无限制器线性unlimited linear | 5.19e−3 | −8.65e−20 | −8.65e−20 | 2.17e−17 | 4.72e−16 | 3.89e−16 | 0.0 | |
| 无限制器线性unlimited linear | −1.34e1 | 2.23e−16 | 2.23e−16 | 9.54e−17 | −9.99e−16 | 1.39e−15 | 0.2 | |
| 无限制器线性unlimited linear | −3.94e1 | 6.56e−16 | 6.56e−16 | −3.35e−16 | −9.82e−16 | 1.12e−15 | 0.6 | |
| 光滑限制器smooth limiter | −1.65e−2 | 2.75e−19 | 2.75e−19 | 5.87e−17 | 3.05e−16 | 8.33e−16 | 0.0 | |
| 光滑限制器smooth limiter | 3.61e2 | −6.02e−15 | −6.02e−15 | −6.25e−15 | −4.55e−15 | 7.77e−16 | 7.8 | |
| 光滑限制器smooth limiter | 1.69e2 | −2.82e−15 | −2.82e−15 | −1.27e−15 | −2.28e−15 | 4.16e−15 | 0.7 |
第三,差距由谁承担?双精度与扩展精度的差,在 的所有点、所有序列上都不超过 ,而且不随 变小:这就是舍入。扩展精度里,实部的 偏移在 时只有 的 0.01 到 5.26 倍, 时只有 到 倍:双精度的状态只能把它表示成 0、1 个或几个最后一位。它只是截断误差进入虚部的两条通道之一,另一条是残差显含的 项;一阶和无限制器线性的 、 分量只有实部偏移这一条通道,两者的 分量里它只占 30% 和 22%。所以在它起主要作用的序列上, 取这么小时截断误差的数值是量化的,不可靠。另做一个实验:从扩展精度的精确解出发,只把状态实部舍入到双精度,其余一切仍是扩展精度。复数步长就有了一个与 无关的下限(表中“只舍入实部”一列, 时九个序列在 到 之间),对 分量和光滑限制器的 分量,这已是该序列舍入平台的相当一部分(比较表中的两列):导数是在一个只知道到双精度的状态上求的;平台的其余部分来自双精度下残差的计算和线性求解。图 77 画出其中三个序列。
Third, what carries the gap? The difference between double and extended precision is at most at every point with for every series, and it does not shrink with : that is rounding. In extended precision the shift of the real part is only 0.01 to 5.26 times at , and to times at : a double-precision state can represent it only as 0, 1 or a few units in the last place. It is one of two channels through which the truncation error enters the imaginary part, the other being the explicit term of the residual; for the and components of first order and unlimited linear it is the only channel, for the components of those two it carries only 30% and 22%. So for the series where it dominates, the numerical value of the truncation error at such small is quantised and not reliable. One more experiment: start from the exact solution in extended precision, round only the real part of the state to double, and keep everything else in extended precision. The complex step then has a floor independent of (the column "Re U rounded" of the table; at the nine series lie between and ), for the -components and the -component of the smooth limiter already a sizeable part of the rounding plateau of the series (compare the two columns of the table): the derivative is taken at a state that is known only to double precision; the rest of the plateau comes from evaluating the residual and solving the linear systems in double precision. Figure 77 draws three of the series.
自己改:选重构和分量,看扩展精度的误差怎样贴着 Taylor 线下降,双精度的点怎样停在平台上(扩展精度在浏览器里计算,只对 的网格)。
Try it: pick the reconstruction and the component and watch the error in extended precision follow the Taylor line while the double-precision points stop at the plateau (the extended-precision computation runs in the browser, for meshes with only).
用复数步长检验 Jacobian 的每一列
Every column of the Jacobians by the complex step
同一个复数程序还能给出 Jacobian 的列。给状态分量 一个虚扰动:,这就是 的列 ,没有用到任何前向语句;给设计虚扰动则得到 。默认设计下,所有列( 的 列、 的 3 列)与第 8 节前向语句算出的列的最大差:
The same complex program also gives the columns of the Jacobians. An imaginary perturbation of state component gives , column of , without any tangent statement; an imaginary perturbation of the design gives . At the defaults, the largest differences from the columns computed by the tangent statements of Section 8 (all columns of , all 3 columns of ) are:
| 重构Reconstruction | ( 列) ( columns) | (3 列) (3 columns) |
|---|---|---|
| 一阶first order | 2.2e−16 | 4.4e−16 |
| 无限制器线性unlimited linear | 4.4e−16 | 4.4e−16 |
| 光滑限制器smooth limiter | 4.4e−16 | 4.4e−16 |
这是对第 8、9 节的又一项独立检验:前向语句给出的 与“只换算术”的程序一致;第 9 节的反向语句又是它们的转置(点积检验)。选一列看看: 的第 行和第 行是单位行(只有对角元 ),边界节点的值不依赖别的节点;第 、 列在紧邻的行里也有元素(边界节点通过斜率和扩散进入相邻的行),内部的列在 到 行非零; 的第 、 行是 和 ( 列为零)。
This is one more independent check of Sections 8 and 9: the and of the tangent statements agree with the program whose only change is its arithmetic, and the reverse statements of Section 9 are their transpose (the dot-product test). Pick a column: rows and of are unit rows (the diagonal entry only), so a boundary value depends on no other node; columns and also have entries in the neighbouring rows (the boundary node enters them through the slopes and the diffusion), the interior columns are nonzero in rows to ; rows and of hold and (zero in the column).
代价与局限
Cost and limits
- 每个分量一次复数求解(中心差分要两次实数求解);复数运算比实数贵几倍(一次复数乘法是 4 次实数乘法);不用调步长, 永远可用。One complex solve per component (the central difference needs two real ones); complex arithmetic costs a few times more than real (one complex multiplication is 4 real ones); no step to tune: always works.
- 要求代码是解析的,并且要有一个复数版本的残差;遇到 、、限制器开关要逐个分析。It needs the code to be analytic and a complex version of the residual; an , a or a limiter switch must be analysed one by one.
- 和差分一样,求解次数随设计分量数增长,而伴随一次求解给出全部分量;它验证的也是 的导数,不回答离散误差的问题(第 15 节)。Like the difference, its number of solves grows with the number of design components, where the adjoint gives all of them with one solve; and it too verifies the derivative of , not the discretisation error (Section 15).
精度阶:网格加密下的误差
Order of accuracy: the errors under refinement
本节实验:先预测,网格加密一倍(),无限制器线性格式的误差缩小几倍?一阶格式呢?从 到 就能看到吗?三个梯度分量的误差也像状态误差那样表现良好吗?然后用收敛实验和加密播放器检验。
Experiment: predict first: when the mesh is refined once (), by what factor does the error of the unlimited linear scheme shrink, and that of the first-order scheme? Can you see it already from to ? Do the errors of the three gradient components behave as well as the state error? Then check with the convergence lab and the refinement player.
第 13、14 节检验了导数:伴随梯度就是 的导数(差分验证到误差不超过 ,复数步长到舍入水平)。本节问另一个问题:网格加密时,离散解、离散目标和离散梯度是否趋向构造解的精确值,趋向得多快。不同网格上的 是不同的函数,所以必须先在每个网格上把非线性方程解到舍入水平,再看离散误差。
Sections 13 and 14 checked the derivatives: the adjoint gradient is the derivative of (to or better by differences, to rounding by the complex step). This section asks a different question: as the mesh is refined, do the discrete solution, objective and gradient approach the exact values of the manufactured solution, and how fast? on different meshes are different functions, so on every mesh the nonlinear equations are first solved to rounding and only then is the discretisation error measured.
量什么、和什么比
What is measured, against what
未知量是节点上的点值,所以 与精确的节点值 比较(没有单元平均;第 2 节),目标与 比较,梯度与第 1 节的精确梯度比较。观测阶是相邻两套网格的误差之比以 2 为底的对数,写在细网格 的那一行( 是 个区间的网格上的某个误差):
The unknowns are point values at the nodes, so is compared with the exact nodal values (there are no cell averages; Section 2), the objective with and the gradient with the exact gradient of Section 1. The observed order is the base-2 logarithm of the ratio of the errors on two successive meshes, printed in the row of the finer mesh ( is one of these errors on the mesh with intervals):
两个边界节点由约束行定为 、,对 误差没有贡献。格点格式与格心格式有一个重要的区别:目标的权重 是梯形公式,所以哪怕状态是精确的节点值, 也不等于 。对精确节点值,Euler–Maclaurin 公式( 的导数在两端相差 ,因为 是周期的)给出 : 时实测与第一项之比是 ,去掉第一项后的余项在 到 的观测阶在 4.00 与 4.02 之间。这个量只含 ,三种重构的值完全相同。
The two boundary nodes are fixed by the constraint rows, and , and add nothing to the error. There is one important difference from the cell-centred scheme: the objective weights are the trapezoid rule, so even with the exact nodal values as the state is not . For the exact nodal values the Euler–Maclaurin formula (the derivative of differs by between the two ends, because is periodic) gives : at the measured value over the first term is , and the remainder after the first term has an observed order between 4.00 and 4.02 from to . This quantity contains only , so its value is identical for the three reconstructions.
于是目标误差分成两部分,梯度误差也一样:
So the objective error has two parts, and the gradient error splits the same way:
方括号是权重(梯形公式)的误差,与格式无关;其余是格式的:目标里是 的加权和,梯度里是切线解 的误差的加权和。梯度的权重部分对 约为 ,同样是 。 与 无关,所以 , 分量的权重部分恰好为 0,它的误差就是离散梯度 本身。图 78 把默认设计下无限制器线性的三个量拆开:除 (两部分几乎抵消)外,每套网格上格式的部分都比梯形部分大,目标大 1.97 到 10.56 倍( 分量 1.89 到 10.51 倍, 分量 2.62 到 76.69 倍); 时梯形误差占目标误差的 21%, 时占 45%( 分量:22% 与 48%)。梯形部分不能忽略,尤其是目标和 分量;下面几小节讨论格式的部分。
The brackets are the error of the weights (the trapezoid rule), which does not depend on the scheme; the rest belongs to the scheme: a weighted sum of for the objective, a weighted sum of the error of the tangent solution for the gradient. The part of the weights of the gradient is for about , again . As does not depend on , : the part of the weights of the -component is exactly 0 and its error is the discrete gradient itself. Figure 78 splits the three quantities for unlimited linear at the defaults: on every mesh except (where the two parts nearly cancel) the part of the scheme is larger than the trapezoid part, by a factor of 1.97 to 10.56 for the objective (1.89 to 10.51 for the -component, 2.62 to 76.69 for the -component); the trapezoid error is 21% of the objective error at and 45% at (for the -component 22% and 48%). The trapezoid part cannot be neglected, least of all for the objective and the -component; the subsections below deal with the part of the scheme.
| 目标 Objective | 梯度 分量Gradient component | 梯度 分量Gradient component | |||||||
|---|---|---|---|---|---|---|---|---|---|
| 总total | 梯形trapezoid | 格式scheme | 总total | 梯形trapezoid | 格式scheme | 总total | 梯形trapezoid | 格式scheme | |
| 8 | −7.3e−3 | 1.5e−3 | −8.7e−3 | −3.4e−2 | 7.5e−3 | −4.2e−2 | 3.3e−1 | −4.4e−3 | 3.4e−1 |
| 64 | 2.4e−4 | 2.3e−5 | 2.2e−4 | 1.2e−3 | 1.2e−4 | 1.1e−3 | 2.6e−3 | −6.8e−5 | 2.7e−3 |
| 512 | 1.5e−7 | 3.6e−7 | −2.1e−7 | 7.7e−7 | 1.8e−6 | −1.0e−6 | −1.7e−5 | −1.1e−6 | −1.6e−5 |
| 4096 | −1.3e−8 | 5.6e−9 | −1.8e−8 | −5.9e−8 | 2.8e−8 | −8.7e−8 | −4.6e−7 | −1.7e−8 | −4.5e−7 |
格式的内部:一阶就是多了黏性
The scheme in the interior: first order is extra viscosity
先看一阶重构(迹等于节点值,斜率为零)。每一个内部行 (边界面也是普通的面)都是
First the first-order reconstruction (the traces are the nodal values, the slopes are zero). Every row (the boundary faces are ordinary faces) is
除以 ,这正是 的中心差分格式:Rusanov 耗散 等于在物理黏性上加了一个数值黏性 。格式求解的是一个黏性错了 的问题,所以误差是 ;而且 时数值黏性比物理黏性还大(默认 :; 时两者相等)。无限制器线性重构时,面 的耗散项是一个三阶差分,
Divided by , this is the central-difference scheme for : the Rusanov dissipation adds a numerical viscosity to the physical one. The scheme solves a problem whose viscosity is wrong by , so its error is ; and for the numerical viscosity is larger than the physical one (default : ; the two are equal at ). With unlimited linear reconstruction the dissipation term of face is a third difference,
两个相邻的面相减,一行得到 倍的四阶差分(超黏性),没有二阶导数形式的人工黏性,内部是二阶格式。第 3 节给出并验算了这两个恒等式。把精确的节点值 代进离散方程,看残差 有多大,就能直接量出这些结论:约束行 恰为 0(舍入水平),第 至 行是面通量的积分截断误差。下面分两组量:内部的行 ,和紧挨边界的两行 。图 79 画出它们随 的变化,表是 的值。
Two neighbouring faces subtract, so a row receives times a fourth difference (a hyperviscosity): no artificial viscosity of second-derivative form, and the interior scheme is second order. Section 3 states and checks both identities. Insert the exact nodal values into the discrete equations and look at the size of the residual : that measures these conclusions directly. The constraint rows vanish (to rounding); the rows to are the integrated truncation errors of the face fluxes. The numbers come in two groups: the inner rows and the two rows next to the boundary, . Figure 79 draws them against ; the table lists the values at .
| 格式Scheme | 内部行 ()inner rows () | 阶order | 第 、 行()rows , () | 阶order |
|---|---|---|---|---|
| 一阶first order | 1.9e−6 | 2.00 | 8.1e−7 | 2.01 |
| 无限制器线性unlimited linear | 7.7e−10 | 3.00 | 2.9e−7 | 2.00 |
| 光滑限制器smooth limiter | 1.9e−6 | 2.00 | 4.0e−4 | 0.98 |
| 无限制器线性,边界斜率 unlimited linear, boundary slope | 7.7e−10 | 3.00 | 4.0e−4 | 1.00 |
| 无限制器线性,三点单侧差分unlimited linear, three-point one-sided | 7.7e−10 | 3.00 | 1.4e−9 | 3.00 |
读表(阶是 的观测阶):一阶每一行都是 (2.00,2.01);这个残差是积分量,除以 才是截断密度,它是 ,所以格式是一阶的。无限制器线性内部行是 (观测阶 3.00),紧挨边界的两行是 (2.00):边界节点上的单侧斜率 只有一阶精度,边界面的通量只有 。光滑限制器内部行是 (2.00),紧挨边界的两行在 到 都在 到 之间,不随 变小:边界节点的斜率不经限制器,而内部的斜率被 压小(后面“光滑限制器”小节),两者的差成了这两行的主要误差。把边界节点的斜率改成 (边界节点不重构)时这两行降为 (1.00),改成三点单侧差分 时升为 (3.00);本页取的两点单侧差分正好在中间。这三种边界斜率是本节后面的实验,不是本页的格式。独立的 Python 程序在 到 报告的 exact_nodal_residual_inner_rows_inf 和 ..._boundary_rows_inf 与这里的值(相对)相差不到 。
Reading the table (orders are the observed orders for ): first order is in every row (2.00, 2.01); the residual is an integrated quantity, and the truncation density, divided by , is , so the scheme is first order. Unlimited linear is in the inner rows (observed order 3.00) and in the two rows next to the boundary (2.00): the one-sided slope at the boundary node is only first-order accurate, and the flux of the boundary face is accurate only to . The smooth limiter is in the inner rows (2.00), but the two rows next to the boundary lie between and for all to , not shrinking with : the slope at the boundary node does not pass through the limiter, while the interior slopes are pushed down by (subsection "The smooth limiter" below), and the mismatch becomes the main error of these two rows. With the slope of the boundary node set to (the boundary nodes not reconstructed) the two rows drop to (1.00), with the three-point one-sided difference they rise to (3.00); the two-point one-sided difference of this page lies in between. These three boundary slopes are an experiment of the later part of this section, not the page's scheme. The independent Python program reports exact_nodal_residual_inner_rows_inf and ..._boundary_rows_inf for to , and they differ from the values here (relatively) by less than .
三种重构, 到
Three reconstructions, to
图 80 与下面的表是本页的模型在 上的结果(默认设计;每个网格都用与上方控件相同的 Newton 迭代解到舍入水平,线性方程用带状 LU 求解)。读法:
Figure 80 and the tables below are the results of this page's model on (default design; every mesh solved to rounding by the Newton iteration of the controls above, the linear systems by a banded LU). How to read them:
- 一阶:状态误差的观测阶从 0.79()、0.97()升到 的 1.01 并停在 1(:1.00)。First order: the observed order of the state error climbs from 0.79 () and 0.97 () to 1.01 at and stays at 1 (: 1.00).
- 无限制器线性:从第一次加密起就是二阶: 到 的每一次加密,状态误差的观测阶都在 1.89 到 2.22 之间, 时在 1.99 到 2.04 之间。格心格式的无限制器线性在同样的 、 只有 0.94、1.00(对精确单元平均),要到 才不低于 1.8:格心页把粗网格上的一阶表现归于它的端单元(那一页第 15 节的端单元实验),格点格式没有端单元。Unlimited linear: second order from the first refinement on: for every refinement from to the observed order of the state error lies between 1.89 and 2.22, and between 1.99 and 2.04 for . The unlimited linear scheme of the cell-centred page shows only 0.94 and 1.00 at the same , (against the exact cell averages) and reaches 1.8 only from : the cell-centred page traces the first-order look of its coarse meshes to its end cells (the end-cell experiment in Section 15 of that page), and this scheme has no end cells.
- 光滑限制器:状态误差在 到 之间停在 到 (不单调),之后下降, 时观测阶为 0.98,误差与一阶格式只差 1.3%:它在细网格上退化为一阶格式(见本节后面)。Smooth limiter: the state error stays between and for to (not monotone) and then falls; at its order is 0.98 and its error differs from the first-order one by only 1.3%: on fine meshes it degenerates to the first-order scheme (later in this section).
- 最大范数: 与 范数讲的是同一件事(收敛实验里两个都可以选):无限制器线性在 到 的观测阶在 1.83 到 2.36 之间, 时在 1.90 到 1.98 之间;一阶在 0.77 到 1.02 之间;光滑限制器在 是 0.98。The maximum norm: tells the same story as the norm (the convergence lab offers both): for unlimited linear the observed orders for to lie between 1.83 and 2.36, and between 1.90 and 1.98 for ; first order lies between 0.77 and 1.02; the smooth limiter ends at 0.98 at .
- 目标误差会变号:无限制器线性的 在 与 之间、 与 之间变号,所以那里的“阶”没有意义;本节的表只在两个误差同号的网格之间写阶。The objective error changes sign: for unlimited linear changes sign between and and between and , so the "order" means nothing there; the tables of this section print an order only between two meshes on which the error has the same sign.
| 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter | ||||
|---|---|---|---|---|---|---|
| 8 | 1.3e−1 | — | 1.3e−2 | — | 2.4e−2 | — |
| 16 | 7.5e−2 | 0.79 | 2.9e−3 | 2.22 | 6.9e−3 | 1.81 |
| 32 | 3.8e−2 | 0.97 | 7.7e−4 | 1.90 | 2.5e−3 | 1.44 |
| 64 | 1.9e−2 | 1.01 | 1.8e−4 | 2.06 | 2.7e−3 | −0.07 |
| 128 | 9.4e−3 | 1.01 | 4.1e−5 | 2.15 | 3.1e−3 | −0.22 |
| 256 | 4.7e−3 | 1.01 | 9.6e−6 | 2.10 | 2.9e−3 | 0.12 |
| 512 | 2.3e−3 | 1.00 | 2.3e−6 | 2.03 | 1.9e−3 | 0.55 |
| 1024 | 1.2e−3 | 1.00 | 5.9e−7 | 2.00 | 1.1e−3 | 0.84 |
| 2048 | 5.8e−4 | 1.00 | 1.5e−7 | 1.99 | 5.7e−4 | 0.94 |
| 4096 | 2.9e−4 | 1.00 | 3.7e−8 | 2.00 | 2.9e−4 | 0.98 |
| 一阶first order | 无限制器线性unlimited linear | 光滑限制器smooth limiter | ||||
|---|---|---|---|---|---|---|
| 8 | −1.9e−1 | — | −7.3e−3 | — | −8.5e−3 | — |
| 16 | −1.1e−1 | 0.74 | 2.8e−3 | — | 1.7e−3 | — |
| 32 | −5.9e−2 | 0.95 | 1.1e−3 | 1.40 | 5.1e−4 | 1.71 |
| 64 | −3.0e−2 | 1.00 | 2.4e−4 | 2.16 | −1.3e−3 | — |
| 128 | −1.5e−2 | 1.01 | 3.9e−5 | 2.63 | −3.3e−3 | −1.35 |
| 256 | −7.4e−3 | 1.01 | 4.3e−6 | 3.17 | −4.1e−3 | −0.30 |
| 512 | −3.7e−3 | 1.00 | 1.5e−7 | 4.83 | −3.0e−3 | 0.46 |
| 1024 | −1.8e−3 | 1.00 | −9.5e−8 | — | −1.7e−3 | 0.81 |
| 2048 | −9.2e−4 | 1.00 | −4.1e−8 | 1.20 | −8.8e−4 | 0.93 |
| 4096 | −4.6e−4 | 1.00 | −1.3e−8 | 1.72 | −4.5e−4 | 0.97 |
自己做一遍:选重构和误差量,网格从 加密到 (当前设计,在浏览器里计算;带状 LU 使每个网格的求解只需几毫秒)。梯度误差带符号:圈表示误差为负。还可以换一种边界斜率(本节后面的实验)。
Do it yourself: pick a reconstruction and an error, refine from to (the current design, computed in the browser; the banded LU keeps every solve to milliseconds). The gradient errors are signed: a ring means a negative error. You can also change the boundary slope (the experiment later in this section).
加密播放器一次只显示一个网格:离散解与精确节点值、误差的分布,以及到这一步为止的收敛曲线。
The refinement player shows one mesh at a time: the discrete solution against the exact nodal values, the distribution of the error, and the convergence curve up to this step.
梯度误差不单调:它们会变号
The gradient errors are not monotone: they change sign
状态误差表现良好,梯度误差则不然:带符号的误差 随 变号,误差在其间变号的两套网格之间,“观测阶”可以是负数,也可以远大于 2,没有意义。图 81(无限制器线性与一阶; 是本页的模型, 是独立的 Python 程序)和下面的表给出带符号的误差。无限制器线性: 分量在 与 之间、 与 之间变号, 分量在 与 之间变号, 分量在 与 之间、 与 之间变号;一阶: 分量在 与 之间变号, 分量在 与 之间变号;光滑限制器: 分量在 与 之间、 与 之间变号, 分量在 与 之间、 与 之间变号。所以三个分量的观测阶要到很细的网格上才可信:无限制器线性中,观测阶从 ()、()、()起才不低于 1.8; 时三个分量是 1.94、1.98、1.85,一阶是 1.00、1.00、0.99。不要用一个观测阶概括梯度误差:先看误差序列和符号,再看同号的网格之间的阶。
The state error is a well-behaved quantity, the gradient errors are not: the signed error changes sign as grows, and between two meshes with a zero in between the "observed order" can be negative or far above 2 and means nothing. Figure 81 (unlimited linear and first order; is this page's model, the independent Python program) and the table below give the signed errors. Unlimited linear: the -component changes sign between and and between and ; the -component changes sign between and ; the -component changes sign between and and between and . First order: the -component changes sign between and ; the -component changes sign between and . Smooth limiter: the -component changes sign between and and between and ; the -component changes sign between and and between and . So the observed orders of the three components can be trusted only on very fine meshes: for unlimited linear the observed order stays at or above 1.8 only from (), () and (); at the three components show 1.94, 1.98 and 1.85, first order 1.00, 1.00 and 0.99. Do not sum up a gradient error by one observed order: look at the error sequence and its signs first, then at the orders between meshes of the same sign.
| 梯度 分量的误差error of the gradient component | 梯度 分量的误差error of the gradient component | 梯度 分量的误差error of the gradient component | ||||
|---|---|---|---|---|---|---|
| 8 | −3.41e−2 | — | 3.35e−1 | — | 3.91e−3 | — |
| 16 | 1.39e−2 | — | 7.94e−2 | 2.08 | −6.98e−2 | — |
| 32 | 5.33e−3 | 1.38 | 1.61e−2 | 2.30 | −3.91e−2 | 0.84 |
| 64 | 1.18e−3 | 2.17 | 2.62e−3 | 2.62 | −1.42e−2 | 1.46 |
| 128 | 1.89e−4 | 2.65 | 2.61e−4 | 3.33 | −3.52e−3 | 2.01 |
| 256 | 2.09e−5 | 3.18 | −1.55e−5 | — | −5.97e−4 | 2.56 |
| 512 | 7.71e−7 | 4.76 | −1.68e−5 | −0.11 | −7.06e−5 | 3.08 |
| 1024 | −4.38e−7 | — | −5.99e−6 | 1.49 | −4.36e−6 | 4.02 |
| 2048 | −1.93e−7 | 1.19 | −1.73e−6 | 1.79 | 7.81e−7 | — |
| 4096 | −5.87e−8 | 1.71 | −4.62e−7 | 1.90 | 4.41e−7 | 0.83 |
| 8192 | −1.60e−8 | 1.87 | −1.19e−7 | 1.95 | 1.41e−7 | 1.64 |
| 16384 | −4.17e−9 | 1.94 | −3.03e−8 | 1.98 | 3.93e−8 | 1.85 |
| 65536 | −2.93e−10 | 1.92 | −1.89e−9 | 2.00 | 2.64e−9 | 1.95 |
表:无限制器线性,带符号的误差;阶只写在同号的相邻网格之间, 的阶是从 起两次加密的平均。
Table: unlimited linear, the signed errors; an order is printed only between neighbouring meshes of the same sign, and the order at is the average of two refinements from .
Python 程序算到 65536 个区间
The Python runs to 65536 intervals
发布仓库里的独立 Python 程序(python/burgers_viscous/node/solve_node.py,稀疏 Jacobian、SciPy 的 LU,没有用到本页的 JavaScript 模型的任何代码)把无限制器线性算到 和 ,一阶和光滑限制器算到 (图 82)。在共同的网格 到 上,本页模型与 Python 程序的状态误差相差不超过 ,目标误差相差不超过 ,梯度误差相差不超过 :两个程序都把 Newton 迭代解到舍入水平,差别来自舍入(Python 程序在修正量不超过 时停止并加上最后一个修正量,残差低于舍入下限 时也接受试探步, 的重新求解需要这一条, 时有没有它结果都一样)。状态误差的观测阶在 是:无限制器线性 2.00,一阶 1.00,光滑限制器 1.00;无限制器线性从 到 的平均阶是 2.00。梯度误差见上一小节。
The independent Python program of the publish repository (python/burgers_viscous/node/solve_node.py, a sparse Jacobian, SciPy's LU, not one line of this page's JavaScript model) ran unlimited linear to and , and first order and the smooth limiter to (Figure 82). On the common meshes to this page's model and the Python program differ by at most in the state errors, in the objective errors and in the gradient errors: both programs solve the Newton iteration to rounding, and the differences come from rounding (the Python program stops at a correction of at most and adds the last correction, and it also accepts a trial step when the residual is below the rounding floor , which the re-solves at need; at it changes nothing). The observed order of the state error at is 2.00 for unlimited linear, 1.00 for first order and 1.00 for the smooth limiter; the mean order of unlimited linear from to is 2.00. The gradient errors are in the previous subsection.
Python 程序自己的梯度检验(JSON 里的 gradient_check)是以扰动前的解为初值热启动、容差取默认的 、步长 的完整重新求解的中心差分(沿方向 ,并沿三个坐标轴):第一个修正量约为 ,远大于容差,所以不是第 13 节说的最危险的组合。沿方向的差分与它自己的伴随梯度相差 到 (三种重构的所有网格)。沿坐标轴的误差也只在一处超过 :光滑限制器的 轴在 到 是 到 。这是 的截断误差(第 13 节的 V 形),换成 就消失,见第 13 节的表。
The Python program's own gradient check (gradient_check in the JSON) is a central difference of complete re-solves at the step , warm-started from the unperturbed solution with the default tolerance (along the direction and along the three axes): the first correction is about , far above the tolerance, so this is not the most dangerous combination of Section 13. Along the direction the difference agrees with the program's own adjoint gradient to to (all meshes of the three reconstructions). Along the axes the error exceeds in one place only: the axis of the smooth limiter at to , where it is to . That is the truncation error (the V of Section 13), and it disappears with ; see the table of Section 13.
| 状态State | 目标Objective | 梯度误差Gradient errors | 求解Solve | |||||
|---|---|---|---|---|---|---|---|---|
| Newton 修正Newton corr. | 秒seconds | |||||||
| 32 | 7.7e−4 | — | 1.1e−3 | 5.33e−3 | 1.61e−2 | −3.91e−2 | 4.7e−16 | 0.0576 |
| 64 | 1.8e−4 | 2.06 | 2.4e−4 | 1.18e−3 | 2.62e−3 | −1.42e−2 | 4.3e−15 | 0.0205 |
| 128 | 4.1e−5 | 2.15 | 3.9e−5 | 1.89e−4 | 2.61e−4 | −3.52e−3 | 2.8e−16 | 0.0209 |
| 256 | 9.6e−6 | 2.10 | 4.3e−6 | 2.09e−5 | −1.55e−5 | −5.97e−4 | 2.8e−16 | 0.0220 |
| 512 | 2.3e−6 | 2.03 | 1.5e−7 | 7.71e−7 | −1.68e−5 | −7.06e−5 | 2.5e−16 | 0.0251 |
| 1024 | 5.9e−7 | 2.00 | 9.5e−8 | −4.38e−7 | −5.99e−6 | −4.36e−6 | 2.3e−16 | 0.0323 |
| 2048 | 1.5e−7 | 1.99 | 4.1e−8 | −1.93e−7 | −1.73e−6 | 7.81e−7 | 3.4e−14 | 0.0437 |
| 4096 | 3.7e−8 | 2.00 | 1.3e−8 | −5.87e−8 | −4.62e−7 | 4.41e−7 | 2.3e−15 | 0.0676 |
| 8192 | 9.2e−9 | 2.00 | 3.4e−9 | −1.60e−8 | −1.19e−7 | 1.41e−7 | 2.8e−16 | 0.1176 |
| 16384 | 2.3e−9 | 2.00 | 8.9e−10 | −4.17e−9 | −3.03e−8 | 3.93e−8 | 1.5e−16 | 0.2155 |
| 65536 | 1.4e−10 | 2.00 | 5.7e−11 | −2.93e−10 | −1.89e−9 | 2.64e−9 | 1.3e−16 | 0.7928 |
| 状态State | 目标Objective | 梯度误差Gradient errors | 求解Solve | |||||
|---|---|---|---|---|---|---|---|---|
| Newton 修正Newton corr. | 秒seconds | |||||||
| 32 | 3.8e−2 | — | 5.9e−2 | −3.13e−1 | 4.22e−2 | 9.22e−2 | 1.6e−16 | 0.0541 |
| 64 | 1.9e−2 | 1.01 | 3.0e−2 | −1.56e−1 | 3.50e−2 | 1.73e−2 | 6.8e−14 | 0.0198 |
| 128 | 9.4e−3 | 1.01 | 1.5e−2 | −7.78e−2 | 2.13e−2 | −7.80e−5 | 3.3e−16 | 0.0212 |
| 256 | 4.7e−3 | 1.01 | 7.4e−3 | −3.87e−2 | 1.17e−2 | −2.45e−3 | 2.1e−16 | 0.0201 |
| 512 | 2.3e−3 | 1.00 | 3.7e−3 | −1.93e−2 | 6.09e−3 | −1.86e−3 | 1.5e−16 | 0.0227 |
| 1024 | 1.2e−3 | 1.00 | 1.8e−3 | −9.64e−3 | 3.11e−3 | −1.09e−3 | 1.5e−16 | 0.0269 |
| 2048 | 5.8e−4 | 1.00 | 9.2e−4 | −4.81e−3 | 1.57e−3 | −5.86e−4 | 1.3e−16 | 0.0361 |
| 4096 | 2.9e−4 | 1.00 | 4.6e−4 | −2.41e−3 | 7.90e−4 | −3.03e−4 | 2.3e−14 | 0.0467 |
| 8192 | 1.5e−4 | 1.00 | 2.3e−4 | −1.20e−3 | 3.96e−4 | −1.54e−4 | 1.5e−15 | 0.0792 |
| 16384 | 7.3e−5 | 1.00 | 1.1e−4 | −6.01e−4 | 1.98e−4 | −7.77e−5 | 1.7e−16 | 0.1422 |
| 状态State | 目标Objective | 梯度误差Gradient errors | 求解Solve | |||||
|---|---|---|---|---|---|---|---|---|
| Newton 修正Newton corr. | 秒seconds | |||||||
| 32 | 2.5e−3 | — | 5.1e−4 | 7.17e−3 | 1.13e−2 | −7.43e−2 | 2.7e−16 | 0.0556 |
| 64 | 2.7e−3 | −0.07 | 1.3e−3 | 6.66e−3 | −4.68e−3 | −1.07e−1 | 2.9e−16 | 0.0230 |
| 128 | 3.1e−3 | −0.22 | 3.3e−3 | 3.96e−3 | −8.18e−3 | −1.38e−1 | 2.2e−16 | 0.0238 |
| 256 | 2.9e−3 | 0.12 | 4.1e−3 | −8.97e−3 | −1.60e−3 | −8.79e−2 | 2.7e−16 | 0.0259 |
| 512 | 1.9e−3 | 0.55 | 3.0e−3 | −1.28e−2 | 2.64e−3 | −3.34e−2 | 2.1e−16 | 0.0302 |
| 1024 | 1.1e−3 | 0.84 | 1.7e−3 | −8.46e−3 | 2.37e−3 | −1.06e−2 | 2.0e−16 | 0.0340 |
| 2048 | 5.7e−4 | 0.94 | 8.8e−4 | −4.60e−3 | 1.41e−3 | −3.18e−3 | 1.3e−16 | 0.0479 |
| 4096 | 2.9e−4 | 0.98 | 4.5e−4 | −2.36e−3 | 7.53e−4 | −9.81e−4 | 2.2e−14 | 0.0693 |
| 8192 | 1.5e−4 | 0.99 | 2.3e−4 | −1.19e−3 | 3.87e−4 | −3.27e−4 | 1.5e−15 | 0.1237 |
| 16384 | 7.3e−5 | 1.00 | 1.1e−4 | −5.99e−4 | 1.96e−4 | −1.21e−4 | 1.8e−16 | 0.2659 |
三张表依次是无限制器线性(含 个区间的一行,阶按两次加密平均)、一阶、光滑限制器;梯度误差带符号,阶见上一小节。二阶收敛不只是范数的性质:把误差分布乘以 ,四套网格的曲线逐渐重合(图 83: 依次为 1.17、1.23、1.24、1.25)。
The three tables are unlimited linear (with the row of intervals, its orders averaged over two refinements), first order and the smooth limiter; the gradient errors are signed, and their orders are in the previous subsection. Second-order convergence is not only a property of the norms: multiplied by , the error profiles of four meshes come together (Figure 83: is 1.17, 1.23, 1.24 and 1.25).
图 84 是程序在 JSON 里记录的每个网格的耗时(记录的平台字符串 macOS-26.6.2-arm64-arm-64bit 只写了操作系统和处理器架构,不是机器型号)。一个网格的耗时包括 Newton 求解、三个前向求解与一个伴随求解、差分检验的完整重新求解(四个方向,每个方向两次);三个加密文件(refinement_*.json)里的各个网格还包括与连续伴随的比较(求解一个连续伴随的边值问题), 个区间的那次单独运行不含这一项。稀疏 LU 使耗时大致随 线性增长: 个区间 0.79 秒。
Figure 84 shows the time per mesh that the program recorded in the JSON (the recorded platform string, macOS-26.6.2-arm64-arm-64bit, names the operating system and the architecture, not the machine). The time of a mesh includes the Newton solve, three tangent solves and one adjoint solve, and the solves of the difference check (two full re-solves per direction, along four directions); the meshes of the three refinement files (refinement_*.json) also include the comparison with the continuous adjoint (the solution of the continuous-adjoint boundary-value problem), and the row of intervals (a separate run) does not. The sparse LU keeps the time roughly linear in : 0.79 seconds for intervals.
观测阶如何依赖网格 Péclet 数
How the observed order depends on the mesh Péclet number
网格间距与黏性长度 之比就是网格 Péclet 数 (默认 , 时 )。把六个黏性 的加密以 为横轴画在一起(图 85,无限制器线性, 到 , 从 0.0028 到 23;每个点画在这一对网格中细网格的 处,最粗的网格是 45.7)。格心页上的结果是:状态误差的观测阶几乎只是 的函数, 时约为 1, 时趋于 2。在格点格式上,状态误差的观测阶( 的所有相邻网格,六个黏性)全在 1.75 与 2.22 之间: 时在 1.75 到 2.21, 时在 1.99 到 2.01。没有在 附近的过渡。同一个 处六个黏性的阶最多相差 0.22,不如格心格式齐(0.06),但相对于格心格式的阶随 的变化( 时在 0.96 到 1.13, 时在 1.91 到 2.00)很小:格点格式的阶对 的依赖远不如格心格式那样强。
The ratio of the mesh spacing to the viscous length is the mesh Péclet number (at the defaults , and at ). Draw the refinements of six viscosities with as the horizontal axis (Figure 85, unlimited linear, to , from 0.0028 to 23; every point is drawn at the of the finer mesh of its pair, and the coarsest mesh has 45.7). The result on the cell-centred page is that the observed order of the state error is almost a function of alone: about 1 for and tending to 2 as . On the node-centred scheme the observed order of the state error (all neighbouring meshes with , six viscosities) lies between 1.75 and 2.22: 1.75 to 2.21 for and 1.99 to 2.01 for . There is no transition near . At the same the orders of the six viscosities differ by at most 0.22, less tidy than the cell-centred scheme (0.06), but small against the change of the cell-centred order with (0.96 to 1.13 for , 1.91 to 2.00 for ): the order of the node-centred scheme does not depend on the way the cell-centred one does.
格心页把这个过渡归于它两端不重构的端单元(那一页的第 15 节做了端单元实验)。格点格式没有端单元:边界面用单侧斜率,边界旁的行是 (上面的残差表),边界节点这一行的贡献在下一小节量出。梯度误差没有画在 图里:它们的符号随黏性变化,观测阶不可读(上一小节)。
The cell-centred page traces this transition to its two end cells, which are not reconstructed (the end-cell experiment in Section 15 of that page). The node-centred scheme has no end cells: the boundary face uses a one-sided slope and the rows next to the boundary are (the residual table above); what the boundary rows contribute is measured in the next subsection. The gradient errors are not drawn in the figure: their signs change with the viscosity and their observed orders cannot be read (previous subsection).
自己选几个黏性:当前的 ,每个 从 加密到 (在浏览器里计算;默认的 时还可画出格心格式(无限制器线性)的状态误差作对照)。
Pick some viscosities: the current , every refined from to (computed in the browser; at the default the state error of the cell-centred scheme (unlimited linear) can be drawn for comparison).
边界行贡献什么
What the boundary rows contribute
格心页的端单元实验在这里没有对应物:格点格式没有端单元,两个边界节点的行是约束行。能贡献误差的是紧挨边界的第 行和第 行:它们的面通量用边界节点上的单侧斜率 ()重构边界面的迹。有两种办法量出它们的贡献。第一种:换边界斜率。只在本节做的实验(不是本页的格式;这些变体的 Jacobian 用复数步长得到,所以它们的切线与伴随梯度也是精确的):把 取为 (边界节点不重构),取为本页的两点单侧差分,取为三点单侧差分 ,或者取为精确的斜率 (只是为了知道更好的边界斜率最多能带来什么)。结果见下面三条和图 86。
The end-cell experiment of the cell-centred page has no counterpart here: the node-centred scheme has no end cells, and the rows of the two boundary nodes are constraint rows. What can contribute error is rows and , next to the boundary: their face fluxes reconstruct the trace of the boundary face with the one-sided slope () of the boundary node. Two ways measure their contribution. The first: change the boundary slope. An experiment of this section only (not the page's scheme; the Jacobians of the variants come from the complex step, so their tangent and adjoint gradients are exact too): take equal to (the boundary nodes are not reconstructed), to the two-point one-sided difference of this page, to the three-point one-sided difference , or to the exact slope (only to learn what a better boundary slope could gain at most). The results are in the three items below and in Figure 86.
- 斜率取 0:状态误差大得多,而且只慢慢地进入二阶: 时是本页的 6.2 倍, 时是 120 倍;观测阶从 的 1.15 升到 的 1.94。Slope : the state error is much larger and enters second order only slowly: 6.2 times that of the page at and 120 times at ; its observed order rises from 1.15 at to 1.94 at .
- 三点单侧差分: 时状态误差与本页的相差不到 1.4%( 时 8.0%);梯度 分量的误差在 是 (本页是 ),观测阶 2.05(本页 1.19):它从 起就是干净的二阶,本页的则迟迟没有。Three-point one-sided difference: for its state error differs from the page's by less than 1.4% (8.0% at ); the error of the gradient component is at (the page's is ) with an observed order of 2.05 (the page's 1.19): second order is clean from , the page's takes much longer.
- 精确的斜率: 时状态误差与三点单侧差分的相差不到 0.1%:再好的边界斜率也不会带来更多。The exact slope: for its state error differs from that of the three-point difference by less than 0.1%: no better boundary slope would bring more.
第二种:伴随加权残差。状态误差由残差决定: 而 ,在 处展开 (),所以 。乘上 ,并用第 11 节的伴随方程 ():
The second: the adjoint-weighted residual. The state error is set by the residual: while ; expanding about gives (), so . Multiply by and use the adjoint equation of Section 11 ():
格式给目标带来的误差,就是精确节点值的残差被伴随 加权后的和:每一行贡献 (约束行的残差为 0,不贡献)。右边用本页的伴随解 和精确节点值的残差算出,与左边 的比值在 是 , 时与 1 相差不超过 。把这个和分成两部分:紧挨边界的第 、 行,和内部各行(图 87,下表)。第 行的乘子 只有在伴随的边界层被分辨()之后才像 那样小:连续伴随 (附录 A)满足 ,,而 近似 (第 16 节把二者比较),所以细网格上 :实测 在 是 0.549、0.944、0.994( 在 是 1.71、1.22、0.26、0.035)。因此渐近地,第 1 行的截断误差 对目标的代价是 :本页 ,得 (边界行贡献的观测阶 ),斜率取 时 ,得 (),三点单侧差分 ,得 ();内部各行是 ()。
The error that the scheme brings to the objective is the sum of the residual of the exact nodal values weighted by the adjoint : every row contributes (the residuals of the constraint rows are 0 and contribute nothing). The right-hand side, computed from this page's adjoint solution and the residual of the exact nodal values, over the left-hand side is at and differs from 1 by at most for . Split the sum in two: rows and next to the boundary, and the inner rows (Figure 87, table below). The multiplier of row 1 is small like only once the layer of the adjoint is resolved (): the continuous adjoint (Appendix A) has and , and approximates (Section 16 compares them), so on fine meshes: measured is 0.549, 0.944, 0.994 at , while is 1.71, 1.22, 0.26, 0.035 at . So asymptotically, if the truncation error of row 1 is , its cost to the objective is : for the page gives (observed order of the boundary-row contribution ), for slope gives (), for the three-point difference gives (); the inner rows give ().
| 本页的格式The page | 边界斜率 Boundary slope | |||||||
|---|---|---|---|---|---|---|---|---|
| 第 1、N−1 行rows 1, N−1 | 内部各行inner rows | 估计 / 实际estimate / actual | 第 1、N−1 行rows 1, N−1 | 内部各行inner rows | 估计 / 实际estimate / actual | |||
| 16 | 2.5e−3 | 6.1e−3 | −3.7e−3 | 0.9953 | −5.6e−2 | −5.3e−2 | −3.7e−3 | 1.0167 |
| 64 | 2.2e−4 | 3.5e−4 | −1.3e−4 | 0.9998 | −1.1e−2 | −1.0e−2 | −1.3e−4 | 1.0019 |
| 256 | 2.9e−6 | 9.1e−6 | −6.2e−6 | 1.0000 | −1.3e−3 | −1.3e−3 | −6.1e−6 | 1.0001 |
| 1024 | −1.8e−7 | 1.6e−7 | −3.4e−7 | 1.0000 | −1.0e−4 | −1.0e−4 | −3.4e−7 | 1.0000 |
| 4096 | −1.8e−8 | 2.5e−9 | −2.1e−8 | 1.0000 | −6.9e−6 | −6.8e−6 | −2.1e−8 | 1.0000 |
所以:本页的两点单侧斜率够用——状态误差从 起与三点单侧差分的几乎一样,最终的阶是二——但它在粗网格上给目标和梯度误差带来一个三阶的、系数不小的项,使 时观测阶忽高忽低;边界节点不重构(斜率取 )则代价高得多。这一点与格心格式相反:格心页上不重构的端单元正是粗网格上误差的主要来源。
So: the page's two-point one-sided slope suffices (the state error is almost that of the three-point difference from , and the final order is two), but on coarse meshes it brings a third-order term with a sizeable coefficient into the objective and gradient errors, which makes the observed orders erratic for ; not reconstructing the boundary nodes (slope ) costs much more. That is the opposite of the cell-centred scheme, where the end cells that are not reconstructed are the main source of the error on coarse meshes.
自己试:选一个 和边界斜率,看每一行的贡献 沿 的分布,和它们的和与真实的误差比较。
Try it: pick an and a boundary slope and look at the contribution of every row along , and at their sum against the real error.
固定 的光滑限制器在细网格上是一阶的
With a fixed the smooth limiter is first order on fine meshes
光滑限制器的分母里有一个固定的 。网格加密时单侧差 越来越小,一旦比 小,就有
The denominator of the smooth limiter contains a fixed . Under refinement the one-sided differences keep shrinking, and once they are smaller than ,
斜率修正 变成 ,迹与一阶格式的迹只差 ,格式的主误差就是一阶格式的 。转折发生在 ,即 。图 88 在每个网格的解上量光滑斜率与中心斜率之比(内部节点的中位数): 时 0.78,先升到 的 0.91,然后一路降到 的 ( 时 0.19, 时 0.057,从 起不超过 0.015)。边界节点的斜率 不经过限制器:内部斜率被 压小以后,第 、 行的边界面与相邻的内部面不再匹配,这就是上面残差表里这两行在 不下降的原因之一(残差表只量了这一点,没有分离出别的原因)。
The slope correction becomes , the traces differ from the first-order ones by , and the leading error of the scheme is the error of the first-order scheme. The switch happens at , i.e. . Figure 88 measures the ratio of the smooth slope to the central slope on the solution of every mesh (the median over the interior nodes): 0.78 at , it first rises to 0.91 at , then falls all the way to at (0.19 at , 0.057 at , at most 0.015 from on). The slope of the boundary node does not pass through the limiter: once the interior slopes are pushed down by , the boundary face of rows and no longer matches its interior neighbour, which is one reason why these two rows of the residual table do not fall for (the residual table measures only that, and does not separate other reasons).
格点格式与格心格式:相同 N 上的比较
Node-centred against cell-centred at equal N
两个格式解同一个问题,用同样的面通量、同样的三种重构和同样的目标与设计。这里比较相同 (区间数)上的误差;格点格式有 个未知量,格心格式有 个。状态误差各对自己的精确值(节点值,单元平均);目标误差和梯度误差对同一个连续的 和 ,所以梯度的比较是直接的。下表是两者误差之比(格点 / 格心;小于 1 表示格点格式的误差更小),格心格式用格心页的模型算出(本页的构建阶段)。结果取决于重构、分量和 ,没有一个统一的结论(图 89 画出三个梯度分量的误差曲线):
The two schemes solve the same problem with the same face flux, the same three reconstructions and the same objective and design. The comparison here is of the errors at equal (the number of intervals); the node-centred scheme has unknowns, the cell-centred scheme . The state errors are each against their own exact values (nodal values, cell averages); the objective and gradient errors are against the same continuous and , so the comparison of the gradients is direct. The table lists the ratios of the errors (node / cell; below 1 means the node-centred error is smaller), the cell-centred scheme computed with the model of the cell-centred page (at the build of this page). The result depends on the reconstruction, the component and , and there is no single conclusion (Figure 89 draws the error curves of the three gradient components):
- 无限制器线性: 时梯度 分量的误差比是 0.099(格点格式小约一个量级), 分量 0.013,但 分量是 2.290(格点格式更大); 时 分量仍是格点格式更大。从 到 三个分量都是格点格式更小: 的比值在 到 之间, 在 到 , 在 到 ;Python 数据在 到 的 10 套网格上也是如此。状态误差比在 到 之间(格点格式更小)。Unlimited linear: at the error ratio is 0.099 for the gradient component (the node-centred error about an order of magnitude smaller) and 0.013 for , but 2.290 for (the node-centred error is larger); at the -component is still larger for the node-centred scheme. From to the node-centred error is smaller in all three components: the ratio of lies between and , that of between and , that of between and ; the Python data show the same on all 10 meshes from to . The ratio of the state errors lies between and (the node-centred error is smaller).
- 一阶:格点格式在 分量上不比格心格式好(比值在 到 之间, 时趋于 1),在 分量上比值在 到 之间(有好有坏),只在 分量上从 起更好( 时更差);状态误差比在 到 之间(格点格式略大,趋于 1)。First order: the node-centred scheme is no better than the cell-centred one in the -component (ratio between and , tending to 1 at ), its ratio in the -component lies between and (better in some places, worse in others), and it is better only in the -component, from on (worse at ); the ratio of the state errors lies between and (the node-centred error slightly larger, tending to 1).
- 光滑限制器:状态误差比在 到 之间,细网格上趋于 1(两个格式都变成一阶);梯度上(),格点格式在 分量上较好(比值 0.07 到 0.99), 分量有好有坏(0.22 到 1.31), 分量从 起更差(1.5 到 6.7)。Smooth limiter: the ratio of the state errors lies between and and tends to 1 on fine meshes (both schemes become first order); in the gradient () the node-centred scheme is better in the component (ratio 0.07 to 0.99), mixed in (0.22 to 1.31) and worse in from on (1.5 to 6.7).
| 重构Reconstruction | 梯度 gradient | 梯度 gradient | 梯度 gradient | 目标 objective | 状态 state | |
|---|---|---|---|---|---|---|
| 8 | 一阶first order | 1.713 | 0.723 | 4.043 | 1.673 | 1.473 |
| 8 | 无限制器线性unlimited linear | 0.099 | 2.290 | 0.013 | 0.115 | 0.254 |
| 8 | 光滑限制器smooth limiter | 0.086 | 1.849 | 1.083 | 0.094 | 0.343 |
| 32 | 一阶first order | 1.423 | 1.821 | 0.178 | 1.435 | 1.290 |
| 32 | 无限制器线性unlimited linear | 0.050 | 0.372 | 0.410 | 0.054 | 0.056 |
| 32 | 光滑限制器smooth limiter | 0.067 | 0.321 | 1.149 | 0.024 | 0.176 |
| 128 | 一阶first order | 1.169 | 1.919 | 5.1e−4 | 1.183 | 1.130 |
| 128 | 无限制器线性unlimited linear | 9.5e−3 | 0.019 | 0.022 | 9.9e−3 | 0.016 |
| 128 | 光滑限制器smooth limiter | 0.223 | 1.315 | 2.600 | 0.469 | 0.686 |
| 512 | 一阶first order | 1.051 | 1.179 | 0.096 | 1.056 | 1.043 |
| 512 | 无限制器线性unlimited linear | 3.7e−4 | 9.8e−3 | 2.1e−3 | 3.6e−4 | 8.4e−3 |
| 512 | 光滑限制器smooth limiter | 0.907 | 0.708 | 2.452 | 0.929 | 0.951 |
| 2048 | 一阶first order | 1.013 | 1.043 | 0.307 | 1.015 | 1.012 |
| 2048 | 无限制器线性unlimited linear | 1.3e−3 | 0.013 | 2.6e−4 | 1.3e−3 | 7.1e−3 |
| 2048 | 光滑限制器smooth limiter | 0.986 | 0.958 | 1.720 | 0.985 | 0.988 |
自己选:重构和量,看两个格式的曲线和比值(默认设计;格心格式的结果是构建页面时算好的)。
Pick a reconstruction and a quantity and see the two curves and their ratio (default design; the results of the cell-centred scheme were computed when the page was built).
观测阶说明了什么,没有说明什么
What the observed orders show, and what they do not
- 证实:状态误差的阶,无限制器线性趋于 2(Python,:2.00),一阶趋于 1(1.00),光滑限制器在细网格上趋于 1(1.00);本页模型在 到 上重现了这些数据(状态误差相差不超过 ,目标误差 ,梯度误差 )。Confirmed: the orders of the state error: unlimited linear tends to 2 (Python, : 2.00), first order to 1 (1.00), and the smooth limiter to 1 on fine meshes (1.00); this page's model reproduces these data on to (to at most in the state errors, in the objective errors and in the gradient errors).
- 梯度误差不单调。带符号的梯度误差会变号(上面的“梯度误差不单调”小节),观测阶要到很细的网格上才可读(无限制器线性: 从 、 从 、 从 起不低于 1.8);在那之前,只能看误差序列和符号。The gradient errors are not monotone. The signed gradient errors change sign (the subsection "The gradient errors are not monotone" above), and their observed orders can be read only on very fine meshes (unlimited linear: at or above 1.8 from for , for and for ); before that, only the error sequence and the signs can be read.
- 目标和梯度的误差里有权重的梯形误差,它与格式无关(二阶,上面的“量什么”小节),在默认设计下不能忽略。The objective and gradient errors contain the trapezoid error of the weights, which does not depend on the scheme (second order, the subsection "What is measured"), and which cannot be neglected at the default design.
- 边界行:本页的两点单侧边界斜率足以得到二阶,但在粗网格上给误差带来一个三阶项;不重构边界节点则代价高得多(上一小节)。The boundary rows: the page's two-point one-sided boundary slope suffices for second order but brings a third-order term into the errors on coarse meshes; not reconstructing the boundary nodes costs much more (the subsection above).
- 光滑限制器不是二阶格式。 固定时它在细网格上退化为一阶(上一小节)。The smooth limiter is not a second-order scheme. With a fixed it degenerates to first order on fine meshes (the subsection above).
- 两个格式谁更好取决于重构、分量和 :见上面的比较,不能概括成“格点格式更精确”。Which scheme is better depends on the reconstruction, the component and : see the comparison above; it cannot be summed up as "the node-centred scheme is more accurate".
数据的来源
Where the data come from
本节的 Python 数字都读自下列文件;构建页面时检查每个文件记录的实现哈希与当前的 solve_node.py(SHA-256 a656f7615237a805…)一致,并且标明 mathematical_proof = false。它们和本页的计算一样是数值证据,不是数学证明。
The Python numbers of this section are read from the files below; when the page is built, the implementation hash recorded in every file is checked against the current solve_node.py (SHA-256 a656f7615237a805…), and every file must say mathematical_proof = false. Like this page's own computations, they are numerical evidence, not mathematical proof.
| 文件File | 命令Command | 版本 · 生成时间Version · generated | 环境Environment | 文件 SHA-256File SHA-256 | 数学证明Mathematical proof |
|---|---|---|---|---|---|
examples/refinement_first.json | python3 solve_node.py --grids 32,64,128,256,512,1024,2048,4096,8192,16384 --nu 0.01 --amplitude 0.2 --phase 0.07 --reconstruction first --gradient-check --continuous-adjoint --output examples/refinement_first.json | 1.0.0 · 2026-10-03T22:54:00Z | Python 3.11.12 · NumPy 2.3.1 · SciPy 1.16.0 macOS-26.6.2-arm64-arm-64bit | 25f2c6c6fd4e3ea6… | 否(false)false |
examples/refinement_none.json | python3 solve_node.py --grids 32,64,128,256,512,1024,2048,4096,8192,16384 --nu 0.01 --amplitude 0.2 --phase 0.07 --reconstruction none --gradient-check --continuous-adjoint --output examples/refinement_none.json | 1.0.0 · 2026-10-03T22:53:59Z | Python 3.11.12 · NumPy 2.3.1 · SciPy 1.16.0 macOS-26.6.2-arm64-arm-64bit | 19cc697eb6fd3641… | 否(false)false |
examples/refinement_smooth.json | python3 solve_node.py --grids 32,64,128,256,512,1024,2048,4096,8192,16384 --nu 0.01 --amplitude 0.2 --phase 0.07 --reconstruction smooth --gradient-check --continuous-adjoint --output examples/refinement_smooth.json | 1.0.0 · 2026-10-03T22:54:01Z | Python 3.11.12 · NumPy 2.3.1 · SciPy 1.16.0 macOS-26.6.2-arm64-arm-64bit | 29bd3d9e934caf31… | 否(false)false |
examples/large_65536.json | python3 solve_node.py --cells 65536 --nu 0.01 --amplitude 0.2 --phase 0.07 --reconstruction none --gradient-check --output examples/large_65536.json | 1.0.0 · 2026-10-03T22:54:03Z | Python 3.11.12 · NumPy 2.3.1 · SciPy 1.16.0 macOS-26.6.2-arm64-arm-64bit | e2aa97c827d94747… | 否(false)false |
本节的结论都是在一个设计(,,;Péclet 研究另有五个 )上的数值观察。本页没有证明一般的收敛定理,也不声称这些阶对别的设计、别的源项或 成立。
The conclusions of this section are numerical observations at one design (, , ; five more values of in the Péclet study). The page proves no general convergence theorem and does not claim these orders for other designs, other sources, or .
伴随一致性:离散伴随会趋于连续伴随吗?
Adjoint consistency: does the discrete adjoint approach the continuous one?
本节实验:先估计一下, 时网格间距 是伴随层宽 的 11.4 倍。入口约束行的乘子 离 有多远——一成以内、大约一半,还是差几倍?再估计斜率 要多少个区间才进入 的 10% 以内——一百个左右,还是一千个左右?然后用本节的加密播放器验证。
Experiment: Estimate first: at the interval is 11.4 times the width of the adjoint's layer. How far is the multiplier of the inlet constraint row from : within 10%, about half, or off by a factor? And how many intervals does the slope need to come within 10% of , about a hundred or about a thousand? Then check with the refinement player of this section.
第 11 节解出了伴随 (),第 12 节用它给出离散目标 的梯度 (精确到舍入误差)。附录 A 则从微分方程出发,推出连续伴随 和连续梯度公式。两者回答不同的问题: 是有限网格上那段程序的导数, 是连续问题的导数。本节把它们放在一起比较:随网格加密,内部节点上的 是否趋于 ?两个约束行的乘子 、 趋于什么?离散梯度是否趋于连续梯度?粗网格上的差别从哪里来?
Section 11 solved for the adjoint () and Section 12 used it for the gradient of the discrete objective , exact up to rounding. Appendix A starts from the differential equation and derives the continuous adjoint and the continuous gradient formula. The two answer different questions: is a derivative of the program on a finite mesh, a derivative of the continuous problem. This section puts them side by side: does at the interior nodes approach as the mesh is refined? What do the multipliers , of the two constraint rows approach? Does the discrete gradient approach the continuous one? Where do the differences on coarse meshes come from?
连续伴随与连续梯度
The continuous adjoint and the continuous gradient
连续伴随(附录 A 用变分逐步推出)是一个线性两点边值问题。连续梯度有两部分:源项积分和边界项。这里 , 在 、 处就是边界值 、 的导数:
The continuous adjoint (derived step by step with variations in Appendix A) solves a linear two-point boundary-value problem. The continuous gradient has two parts, a source integral and a boundary term. Here , and at and is the derivative of the boundary values , :
对 ,通量本身含 ,公式变为 ;对这一族构造解它等于零。伴随方程的对流项 与状态方程符号相反: 从 向 传播,所以 要靠 处宽约 的边界层来满足。默认设计下(201 个点的 Chebyshev 配点),,;源项积分为 ,边界项为 ,两者之和与精确梯度 相差 量级(本页和 Python 审计都核对过)。注意两部分都是 ,梯度却小得多:它是两个大数之差。
For the flux itself contains and the formula becomes , which is zero for this family. The convection term of the adjoint equation has the opposite sign of the primal one: travels from towards , so is met by a boundary layer at of width about . At the defaults (Chebyshev collocation on 201 points) and ; the source integral is , the boundary term , and their sum differs from the exact gradient by about (checked by this page and by the Python audit). Note that both parts are while the gradient is much smaller: it is the difference of two large numbers.
约束行的乘子就是连续伴随的边界通量
The multipliers of the constraint rows are the boundary fluxes of the continuous adjoint
格点格式里两个边界行是约束 、(第 4 节),它们的乘子 、 是伴随向量的两个分量,没有连续伴随中的点值与之对应。先在连续问题里看一看:把边界条件也当作约束,用乘子 、 写拉格朗日函数, 仍满足 :
On the node-centred mesh the two boundary rows are the constraints and (Section 4), and their multipliers , are two components of the adjoint vector that have no point value of to correspond to. Look first at the continuous problem: treat the boundary conditions as constraints too, with multipliers , in the Lagrangian, while still holds:
对状态 做变分(这是连续推导,所以用 ):,对 分部积分两次并用 ,得到
Vary the state (this is a continuous derivation, so is used): ; integrating by parts twice and using gives
对任意 成立,给出伴随方程,并且乘子等于边界通量:,。梯度是
for every gives the adjoint equation and shows that the multipliers are the boundary fluxes: , . The gradient is
也就是上面的公式。离散拉格朗日函数是
the formula above. The discrete Lagrangian is
逐项对照。内部行 是对偶控制体 上的积分残差,,同样 ;所以 :同号,没有因子 (伴随方程也对得上:,右端项 ;如果残差除以控制体宽度成为“密度”形式,乘子就会变成 ,比较之前要先确认这种约定)。两个约束行的单位缩放使 、,期望是 ,;梯度的离散与连续形式也就一一对应:
Term by term. An interior row is an integrated residual over the dual volume , , and likewise ; so , with the same sign and no factor (the adjoint equations match too: and the right-hand side is ; had the residual been divided by the volume width, a density, the multiplier would be , so check this convention before comparing). The unit scaling of the two constraint rows makes and , and the expectation is , ; the discrete and the continuous forms of the gradient then correspond term by term:
(对 还有面通量里显含的 一项,见下面的“梯度误差的逐项分解”。)这里没有半个单元的因子,也没有格心页里半个单元上那种“入口值伴随”的换算:边界项就是乘子本身。这是由两个拉格朗日函数对照得到的预期,不是定理;下面逐项测量它,每一处都是数值证据。
(For there is also the explicit of the face flux, see “The gradient error, part by part” below.) There is no half-interval factor and no conversion like the “adjoint of the inlet value” of a half cell: the boundary term is the multiplier itself. This is what comparing the two Lagrangians leads one to expect, not a theorem; the rest of the section measures it, and every statement is numerical evidence.
“一致”在这里指什么,哪些只是证据
What “consistent” means here, and what is only evidence
本节说离散伴随与连续伴随一致,指的是固定设计、加密网格时测到四件事:(1) 内部节点上的逐点误差 和 L1 范数 趋于零;(2) 乘子 、 趋于 、;(3) 离散梯度 趋于连续梯度;(4) 它的源项部分和两个边界部分分别趋于源项积分和边界项。本节的测量用模型算到 ,用 Python 数据算到 (乘子到 )。这只是本问题上的数值证据,不是定理:本页不给出一般数据下的收敛定理。
In this section the discrete adjoint is called consistent with the continuous one when four things are measured at a fixed design under mesh refinement: (1) the pointwise error and the L1 norm at the interior nodes tend to zero; (2) the multipliers , tend to and ; (3) the discrete gradient tends to the continuous gradient; (4) its source part and its two boundary parts tend to the source integral and the boundary term separately. The measurements go to and to with the Python data (the multipliers to ). This is numerical evidence for this one problem, not a theorem: the page gives no convergence theorem for general data.
读图: 时 ,而 ;网格间距 是伴随层宽的 11.4 倍,第一个内部节点根本“看不见”这个层,它的误差最大。但乘子 与 只差 1.4%。换 、重构和设计,看误差的位置、大小和两个边界关系怎样变:
Reading the figure: at , while ; the interval is 11.4 times the width of the adjoint's layer, the first interior node does not see the layer at all, and its error is the largest. Yet the multiplier differs from by only 1.4%. Change , the reconstruction and the design in the panel below and watch where the error sits, how large it is and what the two boundary relations do:
加密:误差与收敛阶
Refinement: the errors and their orders
表中是内部节点上 的 L1 范数和最大值(模型算到 ;最后一列是 Python 数据的 adjoint_l1_reference_error,它的 由 SciPy 的 solve_bvp 独立求得;“阶”是相邻两层的观测阶 )。
The table lists the L1 norm and the maximum of at the interior nodes (the model up to ; the last column is the Python field adjoint_l1_reference_error, whose comes independently from SciPy's solve_bvp; “order” is the observed order between neighbouring levels).
| N | 一阶 L1first L1 | 阶order | 一阶最大值first max | 无限制器 L1none L1 | 阶order | 无限制器最大值none max | 光滑 L1smooth L1 | 阶order | Python L1(无限制器)Python L1 (none) |
|---|---|---|---|---|---|---|---|---|---|
| 8 | 1.06e−1 | 5.40e−1 | 9.12e−2 | 2.94e−1 | 1.11e−1 | — | |||
| 16 | 6.42e−2 | 0.72 | 5.73e−1 | 5.09e−2 | 0.84 | 2.26e−1 | 7.45e−2 | 0.58 | — |
| 32 | 4.52e−2 | 0.51 | 6.15e−1 | 2.12e−2 | 1.26 | 1.25e−1 | 2.37e−2 | 1.65 | 2.12e−2 |
| 64 | 2.73e−2 | 0.72 | 4.85e−1 | 7.66e−3 | 1.47 | 5.92e−2 | 9.47e−3 | 1.32 | 7.66e−3 |
| 128 | 1.50e−2 | 0.87 | 3.05e−1 | 2.54e−3 | 1.59 | 3.64e−2 | 5.34e−3 | 0.82 | 2.54e−3 |
| 256 | 7.85e−3 | 0.93 | 1.79e−1 | 8.00e−4 | 1.67 | 2.06e−2 | 3.72e−3 | 0.52 | 8.00e−4 |
| 512 | 4.01e−3 | 0.97 | 9.79e−2 | 2.32e−4 | 1.79 | 8.00e−3 | 3.09e−3 | 0.27 | 2.32e−4 |
| 1024 | 2.03e−3 | 0.98 | 5.15e−2 | 6.26e−5 | 1.89 | 2.50e−3 | 1.84e−3 | 0.75 | 6.26e−5 |
| 2048 | 1.02e−3 | 0.99 | 2.64e−2 | 1.63e−5 | 1.95 | 6.97e−4 | 9.81e−4 | 0.91 | 1.63e−5 |
| 4096 | 5.11e−4 | 1.00 | 1.34e−2 | 4.14e−6 | 1.97 | 1.84e−4 | 5.02e−4 | 0.97 | 4.14e−6 |
| 8192 | — | — | — | — | — | 1.04e−6 | |||
| 16384 | — | — | — | — | — | 2.62e−7 |
结论(测量):无限制器线性的离散伴随二阶收敛到连续伴随,一阶格式一阶收敛,最大误差和 L1 误差同阶。光滑限制器的 固定,网格足够细()后限制器几乎关闭,这时内部面的迹之差已和一阶格式相同: 时 在靠近两端的内部面上是 0.9985(全部内部面上介于 0.9981 与 1.0000 之间;一阶格式为 1.0000),所以它的收敛阶也降到一阶: 时 L1 的观测阶是 1.65, 时只有 0.27, 时为 0.97。粗网格上无限制器线性的最大误差总在第 1 或第 2 个内部节点(伴随的入口层所在处): 时 0.294, 时 0.226, 时 0.059;这些网格都分辨不出伴随的层()。作对比,格心页同一设计、 时最大误差是 0.554,L1 误差 0.125(本页 0.091); 时 L1 误差分别为 1.03e−5(格心页)与 4.14e−6(本页)。
Conclusion (measured): for unlimited linear reconstruction the discrete adjoint converges to the continuous one at second order, for the first-order scheme at first order, with the same order in the maximum and in the L1 norm. The smooth limiter has a fixed ; once the mesh is fine enough () the limiter is nearly off and the jump of the traces at an interior face is that of the first-order scheme: at the ratio is 0.9985 on the interior faces next to the ends (between 0.9981 and 1.0000 over all interior faces; 1.0000 for the first-order scheme), so its order also falls to one: the observed L1 order is 1.65 at , only 0.27 at and 0.97 at . On coarse meshes the maximum error of unlimited linear reconstruction is always at the first or second interior node, where the adjoint's inlet layer lies: 0.294 at , 0.226 at and 0.059 at ; none of these meshes resolves the adjoint's layer (). For comparison, on the cell-centred page the same design gives a maximum error of 0.554 and an L1 error of 0.125 at (this page: 0.091), and L1 errors of 1.03e−5 and 4.14e−6 at .
核心结果:入口与出口约束行的乘子
The central result: the multipliers of the inlet and outlet constraint rows
乘子 不是额外要解的未知量,它由伴随方程的第 0 行决定: 的第 0 行是单位行,第 0 列只在约束行本身和读到 的面所在的行里有非零元。于是 的第 0 行是 ,其中 。把 写成读到 的那些面通量的导数。分发的转置给出:通量和 里的系数是 ,其中 :通量和看到的是两端补零的 ,正是连续伴随的边界条件 。所以
The multiplier is not an extra unknown to be solved for: it is fixed by row 0 of the adjoint equation. Row 0 of is the unit row, and column 0 has nonzeros only in that row and in the rows of the faces that read . So row 0 of is , with . Write through the derivatives of the face fluxes that read . The transposed scatter gives the coefficients of the flux sum as with : the flux sum sees extended by zero at both ends, which is the boundary condition of the continuous adjoint. Hence
(第三项只在二阶格式里出现:面 1 通过节点 1 的斜率读到 。)黏性部分是 : 乘以 在入口处的单侧差商,也就是离散的 。其余是面 0 的对流通量与 Rusanov 耗散。一阶格式只有面 0 读到 (,),给出封闭形式;在出口,同样的论证给出 :
(The third term occurs only in the second-order schemes: face 1 reads through the slope of node 1.) The viscous part is : times the one-sided difference quotient of at the inlet, the discrete . The rest is the convective flux and the Rusanov dissipation of face 0. The first-order scheme has only face 0 reading (, ), which gives closed forms; at the outlet the same argument gives :
这两个等式是代数恒等式:本页在 、32、128 的收敛解上核对,相差不超过 。测量乘子与连续边界通量的关系(无限制器线性,默认设计;“黏性部分”是 ):
These two equalities are algebraic identities: this page checks them on the converged states at , 32 and 128, to within . The measured relation of the multiplier to the continuous boundary flux (unlimited linear, default design; the “viscous part” is ):
| N | 黏性部分 viscous part | 黏性部分占 的百分比its share of (%) | 斜率比 slope ratio | ||
|---|---|---|---|---|---|
| 8 | −1.4061 | 0.9856 | −0.1368 | 9.7 | 0.096 |
| 16 | −1.4012 | 0.9822 | −0.2727 | 19.5 | 0.191 |
| 32 | −1.4136 | 0.9909 | −0.4939 | 34.9 | 0.346 |
| 64 | −1.4212 | 0.9963 | −0.7832 | 55.1 | 0.549 |
| 128 | −1.4247 | 0.9987 | −1.0618 | 74.5 | 0.744 |
| 256 | −1.4260 | 0.9996 | −1.2501 | 87.7 | 0.876 |
| 512 | −1.4264 | 0.9999 | −1.3463 | 94.4 | 0.944 |
| 1024 | −1.4265 | 1.0000 | −1.3898 | 97.4 | 0.974 |
| 2048 | −1.4266 | 1.0000 | −1.4092 | 98.8 | 0.988 |
| 4096 | −1.4266 | 1.0000 | −1.4182 | 99.4 | 0.994 |
时 与 只差 1.4%,而斜率 只再现了 的 9.6%,黏性部分只占 的 9.7%:这时的乘子几乎全由面 0、面 1 的对流通量和 Rusanov 耗散承担,网格根本分辨不了层,但(在默认设计下)第一个控制体的通量收支是对的。斜率要到 才进入 10% 以内(一阶格式要到 ),黏性部分在 时占 98.8%,在 时占 99.4%。这是默认设计下的观测,不是对所有设计都成立的规律:、(, 不变)时 的乘子比是 2.35, 和 32 时是 1.06 和 1.01(上面的面板可以试别的设计)。下面的渐近分析给出一种启发式的读法(不是被分离出来的原因): 是外解的性质——层两侧的跳跃乘以 ,与黏性无关——所以通量收支里的对流部分把外解的值带对时,乘子就对,不必分辨层本身;最粗的网格上是否如此取决于设计。
At the multiplier differs from by only 1.4%, while the slope reproduces only 9.6% of and the viscous part is only 9.7% of : the multiplier is carried almost entirely by the convective flux and the Rusanov dissipation of faces 0 and 1. The mesh cannot resolve the layer, but (at the default design) the flux balance of the first volume is right. The slope comes within 10% only at (the first-order scheme needs ), and the viscous part is 98.8% of at and 99.4% at . This is an observation at the default design, not a rule for every design: for , (, same ) the multiplier ratio is 2.35 at , and 1.06 and 1.01 at and 32 (the panel above lets you try other designs). The asymptotics below give a heuristic reading (not an isolated cause): is a property of the outer solution, the jump across the layer times , independent of the viscosity, so when the convective part of the flux balance carries the value of the outer solution correctly the multiplier comes out right without resolving the layer; whether it does so on the coarsest mesh depends on the design.
误差与阶:下表比较两个乘子在一阶和无限制器线性格式下的误差(—:这两个网格上带符号的误差变号,不给出阶)。 的绝对误差同样收敛,但 很小,所以粗网格上的相对误差很大: 时 ,是连续值的 3.95 倍。无限制器线性时两个乘子的误差从 起大小相等、符号相反: 只有 的 0.004 倍()到 0.011 倍();一阶格式的两个误差从 起同号( 时 )。因为 ,梯度的 、 分量只通过两个乘子之和 看到它们。
The errors and orders: the table compares the two multipliers under the first-order and unlimited linear schemes (—: the signed error changes sign between the two meshes, so no order is printed). The absolute error of converges too, but is small, so on coarse meshes the relative error is large: at , is 3.95 times the continuous value. For unlimited linear the two errors are equal and opposite from on: is only 0.004 of at and 0.011 at ; for the first-order scheme the two errors have the same sign from on ( at ). Since , the and components of the gradient see the two multipliers only through their sum .
| N | 一阶 first | 阶order | 无限制器 none | 阶order | 一阶 first | 阶order | 无限制器 none | 阶order |
|---|---|---|---|---|---|---|---|---|
| 8 | 1.7e−2 | 2.0e−2 | 2.8e−1 | 6.5e−2 | ||||
| 16 | 6.9e−2 | — | 2.5e−2 | −0.31 | 1.4e−1 | 0.97 | 3.0e−2 | 1.14 |
| 32 | 4.8e−2 | 0.53 | 1.3e−2 | 0.97 | 7.1e−2 | 1.01 | 1.3e−2 | 1.17 |
| 64 | 2.6e−2 | 0.88 | 5.3e−3 | 1.28 | 3.5e−2 | 1.01 | 5.3e−3 | 1.31 |
| 128 | 1.3e−2 | 0.97 | 1.9e−3 | 1.51 | 1.7e−2 | 1.01 | 1.9e−3 | 1.51 |
| 256 | 6.6e−3 | 0.99 | 5.8e−4 | 1.70 | 8.7e−3 | 1.00 | 5.7e−4 | 1.71 |
| 512 | 3.3e−3 | 1.00 | 1.6e−4 | 1.85 | 4.3e−3 | 1.00 | 1.6e−4 | 1.85 |
| 1024 | 1.7e−3 | 1.00 | 4.2e−5 | 1.92 | 2.2e−3 | 1.00 | 4.2e−5 | 1.93 |
| 2048 | 8.3e−4 | 1.00 | 1.1e−5 | 1.96 | 1.1e−3 | 1.00 | 1.1e−5 | 1.96 |
| 4096 | 4.1e−4 | 1.00 | 2.7e−6 | 1.98 | 5.4e−4 | 1.00 | 2.7e−6 | 1.98 |
是单位缩放的约束行的性质。若把约束行乘以 (,解、、 和梯度都不变),则 变为 :、 时 变成 。比较乘子与 之前先确认约束行的缩放;梯度公式里 乘的是 对设计的导数,缩放互相抵消。
is a property of the unit-scaled constraint row. If the row is multiplied by (; the solution, , and the gradient do not change), then becomes : at with , becomes . Check the scaling of the constraint rows before comparing a multiplier with ; in the gradient formula multiplies the derivative of with respect to the design, and the scalings cancel.
边界面为什么重要:不取边界斜率的实验
Why the boundary faces matter: an experiment without boundary slopes
边界节点用单侧斜率 、(第 3 节),所以边界面的迹也有二阶精度。若不给边界节点斜率(,其他一切不变;这只是实验,不是本页的格式),两个边界面就只有一阶精度的迹——这正是格心页那两个不重构的端单元的情形。这里用同一段代码(切线、反向、复数语句都写出并通过点积检验和复数步长)看伴随和梯度:网格分辨伴随层的能力没变,误差却大幅上升:
The boundary nodes use the one-sided slopes , (Section 3), so the traces of the boundary faces are second-order accurate too. Without a slope at the boundary nodes (, everything else unchanged; an experiment only, not the page's scheme) the two boundary faces have first-order traces, which is the situation of the two unreconstructed end cells of the cell-centred page. Here the same code (tangent, reverse and complex statements all written out and passed through the dot test and complex step) is used for the adjoint and the gradient: the mesh resolves the adjoint's layer no better than before, but the errors rise sharply:
| N | (本页) (page) | (无边界斜率) (no boundary slope) | 梯度误差(本页)-gradient error (page) | 梯度误差(无边界斜率)-gradient error (no boundary slope) | (本页) (page) | (无边界斜率) (no boundary slope) |
|---|---|---|---|---|---|---|
| 8 | 0.294 | 0.321 | 3.41e−2 | 6.71e−1 | 0.9856 | 1.0471 |
| 16 | 0.226 | 0.419 | 1.39e−2 | 2.91e−1 | 0.9822 | 1.0159 |
| 32 | 0.125 | 0.462 | 5.33e−3 | 1.28e−1 | 0.9909 | 1.0054 |
| 64 | 0.059 | 0.355 | 1.18e−3 | 5.27e−2 | 0.9963 | 1.0015 |
没有边界斜率时, 分量的梯度误差(绝对值)在 时从 0.034 升到 0.671, 时从 升到 ;格心页在 时的 分量梯度误差是 0.343。渐近地它仍是二阶(最细两层之间状态误差的阶 1.97),但中间过渡很长: 到 1024 之间的阶只有 1.15 到 1.88, 时状态误差仍是本页的 123 倍。所以格点格式在粗网格上 分量梯度误差的优势(:0.034 对 0.343)主要来自边界面的二阶迹:格心页把端单元重构之后也只有 0.030。这是对两个实验的直接比较,对 分量(格点格式更差)和两种格式的其余差别什么也没有说。
Without the boundary slopes the -gradient error (absolute value) rises from 0.034 to 0.671 at and from to at ; on the cell-centred page the -gradient error at is 0.343. Asymptotically it is still second order (state-error order 1.97 between the two finest levels) but the transient is long: between and 1024 the orders are only 1.15 to 1.88, and at the state error is still 123 times the page's. So the advantage of the node scheme in the -gradient error on coarse meshes (0.034 against 0.343 at ) comes mainly from the second-order traces of its boundary faces: the cell page, once its end cells are reconstructed, reaches 0.030. This compares two experiments directly; it says nothing about the component (worse on the node scheme) or about the other differences between the schemes.
播放器逐层演示加密过程,可以在本页格式、没有边界斜率的实验和本节最后要讲的错误版本之间切换:
The player shows the refinement level by level; it switches between the page's scheme, the experiment without boundary slopes and the wrong version of the last part of this section:
一阶格式就是黏性为 ν + h 的中心格式
First order is the central scheme with viscosity ν + h
为什么一阶离散伴随的层那么宽?看一阶格式(,,)。格点格式里每一个内部行 的两个面都是普通面(,边界面也一样),代入第 3 节的通量:
Why is the layer of the first-order discrete adjoint so wide? Take the first-order scheme (, , ). On the node-centred mesh every interior row has two ordinary faces (, the boundary faces included); insert the flux of Section 3:
抵消,两个跳跃合成一个二阶差分,耗散与黏性合并:
The terms cancel, the two jumps combine into one second difference, and the dissipation joins the viscosity:
方括号正是 的中心差分:Rusanov 耗散系数 相当于额外黏性 (即 )。再看转置:第 列在行 里有三个非零元;约束行(第 0 行和第 行)里 ,所以伴随方程的行 是
The bracket is exactly the central difference of : the Rusanov dissipation coefficient acts as an added viscosity (that is ). Now transpose: column has three nonzero entries in rows ; in the constraint rows (rows 0 and ) , so the rows of the adjoint equation are
除以 :一阶离散伴随的全部内部各行,正是黏性为 、边界值为零的连续伴随 , 的中心格式(对流系数取离散状态 );两个乘子随后由第 0 行和第 行读出,即上面的封闭形式。这是恒等式,不是近似:Python 审计用 sympy 符号化简验证,本页在收敛解上逐行核对,差不超过 。所以一阶离散伴随的层宽约 而不是 : 时前者是后者的 13.5 倍。格心页里这个恒等式在两个端行失效(边界面距离 );这里没有例外。
Divide by : all interior rows of the first-order discrete adjoint are the central scheme for the continuous adjoint with viscosity and zero boundary values, , (with the discrete state as the convection coefficient); the two multipliers are then read off rows 0 and , the closed forms above. This is an identity, not an approximation: the Python audit proves it with sympy, and this page checks it row by row on the converged states, to within . The layer of the first-order discrete adjoint is therefore about wide instead of : 13.5 times wider at . On the cell-centred page the identity fails in the two end rows (boundary faces at distance ); here there is no exception.
二阶格式在内部没有这一项。无限制器线性重构 时,面 的迹之差是一个三阶差分,行 的耗散是一个四阶差分;两个边界面用单侧斜率,迹之差是二阶差分:
In the interior the second-order scheme has no such term. With unlimited linear reconstruction, , the jump of the traces of a face is a third difference and the dissipation of a row is a fourth difference; the two boundary faces use the one-sided slopes and their jump is a second difference:
这个四阶差分等于 :这是超黏性,单位长度上为 ,没有二阶导数型的人工黏性。第 1 行与第 行的耗散是 与 (sympy 证明;本页在随机状态上核对,差不超过 )。用精确节点值代入(), 在面 0 上是 −0.00018,在面 1、2 上是 ,在面 上是 0.00018:和一阶格式的迹之差(比值 1)相比,两个边界面小一个 的因子,内部面小一个 的因子,所以没有一个面带一阶格式那样大小为 的额外黏性。第 3 节把两个边界面的二阶差分读成只作用在第 1 行与第 行上、约为 的额外黏性,其余各行只有四阶超黏性。格心页的两个端单元则带额外黏性 (比值 0.5001)。光滑限制器在 时接近无限制器线性, 后内部面带 而两个边界面(单侧斜率不受限制)带 : 时的比值在靠近两端的内部面上是 0.9985(全部内部面上介于 0.9981 与 1.0000 之间),在边界面上是 0.4992。所以,一阶格式里每一行都有的额外黏性 (它把伴随的层加宽到 )在二阶格式里不存在;第 1 行与第 行上的 在粗网格上对入口附近的伴随起什么作用,这里没有分离。
The fourth difference is : a hyperviscosity, per unit length, with no second-derivative artificial viscosity. Rows 1 and are and (sympy proof; checked by this page on random states to within ). With the exact nodal values (), is −0.00018 on face 0, on faces 1 and 2 and 0.00018 on face : compared with the first-order jump (ratio 1) the jumps are smaller by a factor on the two boundary faces and on the interior faces, so no face carries an added viscosity of the size of the first-order scheme. Section 3 reads the second difference of the two boundary faces as an added viscosity of about acting on rows 1 and only; every other row has only the fourth-order hyperviscosity. The two end cells of the cell-centred page carry (ratio 0.5001). The smooth limiter is close to unlimited linear while ; once the interior faces carry and the two boundary faces (one-sided slope, not limited) : the ratios at are 0.9985 (interior faces next to the ends; between 0.9981 and 1.0000 over all interior faces) and 0.4992 (boundary faces). So the added viscosity of every row, which widens the layer of the first-order adjoint to , is absent at second order; what the of rows 1 and does to the adjoint next to the inlet on coarse meshes is not isolated here.
测量结果:到 为止两个最大偏差都在伴随的层里;到 的最大偏差在每个网格上都比到 的小( 时是后者的 1/3.8, 时是 1/26.2),但两者最后都按一阶下降( 到 4096 的观测阶 1.00 与 0.98); 时到 的最大偏差已离开层(在 )。等效黏性解释了层里的偏差,不是全部误差:在 L1 范数下两个距离之比只有 1.52(:2.03e−3 与 1.33e−3,最大偏差之比为 24.6):到 的 L1 距离有 92% 在层外,到 的有 73% 在层内(层取 )。层外 的那部分误差的来源这里没有分离。
Measured: up to both largest deviations sit in the adjoint's layer; the largest deviation from is smaller than that from on every mesh (3.8 times at , 26.2 times at ), but both finally fall at first order (observed orders 1.00 and 0.98 from to 4096), and at the largest deviation from has left the layer (it is at ). The effective viscosity explains the deviation inside the layer, not the whole error: in the L1 norm the two distances differ by a factor of only 1.52 (: 2.03e−3 and 1.33e−3, against 24.6 for the largest deviations): 92% of the L1 distance to lies outside the layer, while 73% of that to lies inside it (the layer taken as ). The source of the error outside the layer is not isolated here.
边界层、渐近行为与黏性 ν 的作用
The inlet layer, its asymptotics and the effect of ν
连续梯度的边界项含 ,而 越小, 越大。先看 的边界层结构(连续伴随问题的形式渐近分析,不是证明)。层外黏性项可以略去,,且 (伴随从 向上游传):
The boundary term of the continuous gradient contains , while grows as decreases. First the structure of the layer of (a formal asymptotic analysis of the continuous adjoint problem, not a proof). Outside the layer the viscous term can be dropped, , with (the adjoint travels upstream from ):
,而边界条件要求 ,所以 处必须有层。层内令 ,主导项满足 ,解为 ;由 及与 的匹配得到
while the boundary condition asks for , so there must be a layer at . Inside it, with , the leading terms satisfy , with the solutions ; and matching with give
斜率按 增长,而 却在 时保持 :它等于层两侧的跳跃 乘以 ,与黏性无关——这个量 正是本节的乘子 要逼近的。数值验证: 时 Chebyshev 解给出 ,渐近值为 −1.42887; 较小时差别约与 成正比(下表)。
The slope grows like , but stays as : it equals the jump across the layer times , independent of the viscosity. This is the quantity that the multiplier of this section has to approach. Numerically, at the Chebyshev solution gives against the asymptotic −1.42887; for small the difference is roughly proportional to (the table below).
把 从 0.1 减到 0.001(、 不变),连续伴随的层越来越薄, 按 增长, 则趋于渐近值 。差值除以 (表中“差 ”一列)从 时的 0.758 变到 时的 0.188。源项积分和边界项各自随 变化,它们的和——精确梯度——不变。最后一列是固定网格 (无限制器线性)上的乘子 : 从 0.1 减到 0.001, 从伴随层宽的 0.14 倍增大到 14.3 倍, 却始终在 0.9941 到 0.9991 之间,而斜率比从 0.95 降到 0.089。下表用 Chebyshev 解(201 个点;Python 审计比对 241 与 321 个点,差都小于 ),分量 :
Decrease from 0.1 to 0.001 (, fixed): the layer of the continuous adjoint gets thinner, grows like , and tends to the asymptotic value . The difference divided by (the column “gap”) goes from 0.758 at to 0.188 at . The source integral and the boundary term each change with ; their sum, the exact gradient, does not. The last column is the multiplier on the fixed mesh (unlimited linear): as falls from 0.1 to 0.001, grows from 0.14 to 14.3 times the width of the adjoint's layer, yet stays between 0.9941 and 0.9991, while the slope ratio falls from 0.95 to 0.089. The table uses the Chebyshev solutions (201 points; the Python audit compares 241 with 321 points and finds differences below ), component :
| 差 gap | 源项积分source integral | 边界项boundary term | 和 sum | () () | ||||
|---|---|---|---|---|---|---|---|---|
| 0.1 | −13.530 | −1.35304 | −1.42887 | 0.758 | 0.5257 | −0.6697 | −0.144008 | −1.3518 |
| 0.05 | −28.095 | −1.40474 | −1.42887 | 0.482 | 0.5018 | −0.6458 | −0.144008 | −1.4027 |
| 0.02 | −71.155 | −1.42310 | −1.42887 | 0.288 | 0.4808 | −0.6248 | −0.144008 | −1.4193 |
| 0.01 | −142.657 | −1.42657 | −1.42887 | 0.230 | 0.4728 | −0.6168 | −0.144008 | −1.4212 |
| 0.005 | −285.568 | −1.42784 | −1.42887 | 0.205 | 0.4686 | −0.6126 | −0.144008 | −1.4211 |
| 0.002 | −714.240 | −1.42848 | −1.42887 | 0.192 | 0.4661 | −0.6101 | −0.144008 | −1.4206 |
| 0.001 | −1428.677 | −1.42868 | −1.42887 | 0.188 | 0.4652 | −0.6092 | −0.144008 | −1.4203 |
在面板里拖动 ,同时看固定网格上的离散伴随: 越小层越难分辨,斜率 离 越远,而乘子 与 的比值几乎不动。
Drag in the panel and watch the discrete adjoint on a fixed mesh: the smaller , the harder the layer is to resolve and the further the slope is from , while the ratio of the multiplier to hardly moves.
梯度误差的逐项分解
The gradient error, part by part
第 12 节在固定网格上分清了设计进入残差的路径。对 与 ()只有两条:每个内部行的积分源项,以及两个约束行里的边界值。所以离散梯度与连续梯度可以逐项对齐(上面已写出)。对 还有面通量里显含的 一项:离散的是 ,连续的是 (它与源项部分的 导数相消)。三部分之和与 相差不到 (本页和审计都核对过);把梯度精确地分成内部行与两个约束行两部分,写在附录 A 的 A.7 里。下表是分量 (无限制器线性);连续值为:源项积分 0.4728,入口项 −0.6074,出口项 −0.0094,梯度 −0.1440:
Section 12 separated the paths by which the design enters the residual, on a fixed mesh. For and () there are only two: the integrated source of every interior row, and the boundary values in the two constraint rows. So the discrete and the continuous gradient can be lined up term by term (written out above). For there is also the explicit of the face flux: in the discrete gradient, in the continuous one (it cancels the -derivative of the source part). The three parts add up to to within (checked by this page and by the audit); the exact split of the gradient into the interior rows and the two constraint rows is written out in Appendix A (A.7). The table is for component (unlimited linear); the continuous values are: source integral 0.4728, inlet term −0.6074, outlet term −0.0094, gradient −0.1440:
| N | 源项行source rows | 入口项 inlet | 出口项 outlet | 与精确值之差minus exact | |
|---|---|---|---|---|---|
| 8 | 0.4577 | −0.5987 | −0.0370 | −0.1781 | −3.4e−2 |
| 16 | 0.4884 | −0.5966 | −0.0220 | −0.1301 | 1.4e−2 |
| 32 | 0.4782 | −0.6019 | −0.0150 | −0.1387 | 5.3e−3 |
| 64 | 0.4740 | −0.6051 | −0.0116 | −0.1428 | 1.2e−3 |
| 128 | 0.4730 | −0.6066 | −0.0102 | −0.1438 | 1.9e−4 |
| 256 | 0.4728 | −0.6072 | −0.0096 | −0.1440 | 2.1e−5 |
| 512 | 0.4728 | −0.6073 | −0.0094 | −0.1440 | 7.7e−7 |
| 1024 | 0.4728 | −0.6074 | −0.0094 | −0.1440 | −4.4e−7 |
分量 的连续值:源项积分 1.7323,入口项 −1.6221,出口项 −0.0251,梯度 0.0852:
The continuous values of component : source integral 1.7323, inlet term −1.6221, outlet term −0.0251, gradient 0.0852:
| N | 源项行source rows | 入口项 inlet | 出口项 outlet | 与精确值之差minus exact | |
|---|---|---|---|---|---|
| 8 | 2.1176 | −1.5988 | −0.0989 | 0.4199 | 3.3e−1 |
| 16 | 1.8164 | −1.5932 | −0.0587 | 0.1646 | 7.9e−2 |
| 32 | 1.7486 | −1.6073 | −0.0400 | 0.1013 | 1.6e−2 |
| 64 | 1.7349 | −1.6160 | −0.0311 | 0.0878 | 2.6e−3 |
| 128 | 1.7325 | −1.6199 | −0.0272 | 0.0854 | 2.6e−4 |
| 256 | 1.7323 | −1.6214 | −0.0257 | 0.0851 | −1.5e−5 |
| 512 | 1.7323 | −1.6219 | −0.0252 | 0.0851 | −1.7e−5 |
| 1024 | 1.7323 | −1.6220 | −0.0251 | 0.0851 | −6.0e−6 |
四点观察。(1) 时分量 的各部分误差是:源项行 −0.0151,入口项 0.0087,出口项 −0.0277:最大的是出口项,因为 是连续值的 3.95 倍(见“核心结果”一节);入口项相对最准,对应乘子 只差 1.4%。(2) 分量 在 时误差在源项行(0.385,入口项 0.023,出口项 −0.074)。(3) 源项行与连续的源项积分并不是逐个控制体对应的:连续的源项积分里,第一个半控制体 一项()在 时是 0.4306,占 91%,而离散格式没有第 0 个源项行(约束行不带源项)。把离散源项和里的 换成采样的 ,这个和从 0.4577 降到 0.0306,差 0.4271,是 一项的 99%:入口附近的内部节点( 偏离 达 21%)把这块缺失的源项补了回来。逐节点看, 在第 1 个节点上是 0.243,其余节点上最大 0.063。(4) 各部分都是 ,梯度却只有 −0.144: 时 ,是精确值的 1.24 倍; 是精确值的 4.9 倍。渐近地,入口项和出口项的误差二阶收敛( 到 4096 的阶:入口 1.98,出口 1.98),并且几乎大小相等、符号相反(见“核心结果”一节),所以梯度误差是源项行的误差加上两个边界项剩下的一小部分: 时源项行为 −7.1e−8,边界项合计 1.3e−8(梯度误差 −5.9e−8),而 时边界项的剩余 5.8e−7 反而大于源项行的误差 1.9e−7。源项行的误差在 与 16 之间变号,又在 与 1024 之间变号,它的观测阶(1.75)只有参考意义。
Four observations. (1) At the errors of the parts of component are: source rows −0.0151, inlet term 0.0087, outlet term −0.0277: the largest is the outlet term, because is 3.95 times its continuous value (see the subsection “The central result”); the inlet term is relatively the most accurate, matching the multiplier that is off by only 1.4%. (2) For component at the error is in the source rows (0.385, against 0.023 for the inlet term and −0.074 for the outlet term). (3) The source rows do not correspond to the continuous source integral volume by volume: in the continuous integral the first half volume contributes at , 91% of it, and the discrete scheme has no source row 0 (a constraint row carries no source). With the sampled in place of the discrete source sum falls from 0.4577 to 0.0306; the difference, 0.4271, is 99% of the term: the interior nodes next to the inlet ( is off from by 21%) make up for the missing source. Node by node, is 0.243 at node 1 and at most 0.063 at the others. (4) The parts are while the gradient is only −0.144: at , is 1.24 times the exact value and is 4.9 times. Asymptotically the errors of the inlet and outlet terms converge at second order (orders between and : inlet 1.98, outlet 1.98) and are nearly equal and opposite (see the subsection “The central result”), so the gradient error is the error of the source rows plus the small remainder of the two boundary terms: at the source rows give −7.1e−8 and the boundary terms together 1.3e−8 (gradient error −5.9e−8), but at the boundary remainder, 5.8e−7, is larger than the error of the source rows, 1.9e−7. The error of the source rows changes sign between and 16 and again between and 1024, so its observed order (1.75) is only indicative.
离散梯度趋于什么,又不是什么
What the discrete gradient converges to, and what it is not
是离散目标 的梯度,精确到舍入误差。离散目标本身就带着梯形公式的误差:对精确节点值,(Euler–Maclaurin: 是周期的,所以 在两端相差 ); 时左边是 1.50e−3,右边第一项是 1.48e−3。把这个目标对设计求导,即使状态是精确的,梯度也已带有 这一项。于是 分量的梯度误差分成两部分:梯形公式作用在精确节点灵敏度上的部分,加上切线解 的离散误差经 加权的余项:
is the gradient of the discrete objective , exact up to rounding. The discrete objective carries the error of the trapezoid rule itself: for the exact nodal values (Euler–Maclaurin: is periodic, so jumps by between the ends); at the left side is 1.50e−3 and the first term on the right 1.48e−3. Differentiated with respect to the design, this objective gives a gradient with the term even if the state were exact. The error of the component therefore splits into the part of the trapezoid rule applied to the exact nodal sensitivities, plus a remainder, the discretisation error of the tangent solution weighted with :
| N | 梯度误差-gradient error | 梯形公式的部分(精确节点灵敏度)trapezoid part (exact nodal sensitivities) | 余项(状态的离散误差)remainder (discretisation error of the state) | |
|---|---|---|---|---|
| 8 | −3.41e−2 | 7.48e−3 | 7.40e−3 | −4.15e−2 |
| 32 | 5.33e−3 | 4.63e−4 | 4.63e−4 | 4.86e−3 |
| 128 | 1.89e−4 | 2.89e−5 | 2.89e−5 | 1.60e−4 |
| 512 | 7.71e−7 | 1.81e−6 | 1.81e−6 | −1.04e−6 |
| 1024 | −4.38e−7 | 4.52e−7 | 4.52e−7 | −8.90e−7 |
| 2048 | −1.93e−7 | 1.13e−7 | 1.13e−7 | −3.05e−7 |
| 4096 | −5.87e−8 | 2.82e−8 | 2.82e−8 | −8.70e−8 |
梯形部分单调地按二阶下降,并且符号固定;余项的符号在 和 时与它相反(在 到 256 之间相同),所以 大到一定程度后两者部分相消: 分量的总误差在 与 16 之间变号(−3.4e−2 与 1.4e−2),又在 与 1024 之间变号(7.7e−7 与 −4.4e−7), 分量在 与 256 之间变号, 分量在 与 16 之间变号,又在 与 2048 之间变号;所以无限制器线性的梯度误差的观测阶在这些点附近是不规则的,没有意义。Python 数据最细两层( 到 16384)上三个分量的观测阶:无限制器线性 1.94、1.98、1.85,一阶 1.00、1.00、0.99,光滑限制器 0.99、0.98、1.43。 时梯形部分是总误差的 −22%, 时是 −48%。所以:离散梯度是这个网格上 的梯度;它趋于连续目标 的梯度,但在粗网格上与它相差很大( 时 、 分量分别差 10% 与 93%, 时差 0.8% 与 3.1%; 时层没被分辨): 时 ,连续值是 ; 分量差 0.335,是连续值的 3.9 倍(离散值是连续值的 4.9 倍)。和格心页比较,格点格式并不处处更好。无限制器线性、 时(格心页的梯度是 ): 分量的误差是格心页的 1/10.1, 分量是 1/79, 分量却更大(0.335,格心页 0.146)。在 Python 数据的各个网格上( 到 16384):无限制器线性在三个分量上的误差都比格心页小(节点与格心的误差之比: 从 3.7e−4 到 5.0e−2, 从 2.9e−3 到 3.7e−1, 从 2.6e−4 到 4.1e−1);一阶格式的 、 分量并不更好(之比 1.01 到 1.42 与 1.02 到 5.13),只有 分量更好。
The trapezoid part falls monotonically at second order and keeps its sign; the remainder has the opposite sign at and from on (the same sign from to 256), so for large the two partly cancel: the total error of the component changes sign between and 16 (−3.4e−2 and 1.4e−2) and again between and 1024 (7.7e−7 and −4.4e−7), that of between and 256, that of between and 16 and again between and 2048; so the observed orders of the gradient errors of unlimited linear reconstruction are erratic near these points and mean nothing there. Between the two finest levels of the Python data ( to 16384) the observed orders of the three components are 1.94, 1.98 and 1.85 for unlimited linear, 1.00, 1.00 and 0.99 for the first-order scheme, 0.99, 0.98 and 1.43 for the smooth limiter. At the trapezoid part is −22% of the total error, at it is −48%. So: the discrete gradient is the gradient of on this mesh; it approaches the gradient of the continuous objective , but on coarse meshes it is far from it (at the and components are off by 10% and 93%, at by 0.8% and 3.1%; the layer is unresolved for ): at it is against the continuous ; the component is off by 0.335, 3.9 times the continuous value (the discrete value is 4.9 times it). Compared with the cell-centred page the node scheme is not better everywhere. Unlimited linear at (the cell page's gradient is ): the component is 10.1 times more accurate and the component 79 times, but the component is worse (0.335 against 0.146). On every mesh of the Python data ( to 16384) the error of unlimited linear is smaller than the cell page's in all three components (ratios node/cell: from 3.7e−4 to 5.0e−2, from 2.9e−3 to 3.7e−1, from 2.6e−4 to 4.1e−1); for the first-order scheme the and components are not better (ratios 1.01 to 1.42 and 1.02 to 5.13), only the component is.
反面例子:边界面的距离取成 h/2
A counterexample: the boundary-face distance taken as h/2
一个看似无害的错误:把格心页的做法带过来,把两个边界面的黏性距离取成 (格心页的边界面离未知量只有半个单元),而格点格式里每个面两侧都是节点,距离处处是 (第 3 节)。错误版本的残差 只在两行不同:
A harmless-looking slip: carry over the practice of the cell-centred page and take the viscous distance of the two boundary faces as (on the cell-centred page the boundary face is half a cell from its unknown), while on the node mesh every face has a node on each side and the distance is everywhere (Section 3). The residual of the slip, , differs from the page's in two rows only:
它的切线、反向和复数语句写在本节的模块里,用点积检验和复数步长核对,二者都在舍入误差内一致(点积检验按 的标准,与模型同样的检查);Python 审计只改自己的距离数组,独立地复现了下面的每个数。
Its tangent, reverse and complex statements are written in this section's module and checked like the model's, by the dot test and complex step, both agreeing to rounding (the dot test is held to , as for the model); the Python audit reproduces every number below independently, changing only its distance array.
不相容:代入精确节点值,本页的 是 ( 时 ;内部各行是 ),错误版本的 却趋于常数 ( 时为 0.01137)。解仍然收敛,但只有一阶:错误版本相当于把面 0 的黏性导数加倍,状态在入口附近整体平移了 —— 时 :本页 ,错误版本 −0.496。
is inconsistent: with the exact nodal values inserted, the page's is ( at ; the interior rows are ), while the slip's tends to the constant (0.01137 at ). The solution still converges, but only at first order: the slip doubles the viscous derivative at face 0 and the state near the inlet is displaced as a whole by : at , is for the page and −0.496 for the slip.
教训有三条。第一,粗网格上看不出来:错误版本在 到 16 时的状态误差只是本页的 1.6 到 3.7 倍;把收敛阶测到足够细才暴露它: 到 128 之间状态误差的阶只有 0.26 到 0.74,最细两层之间 1.00,梯度 1.00,而本页是 2.00。第二,核心结果也看不出来:乘子 在错误版本里同样趋于 1( 时 0.9922, 时 0.99997)——因为乘子由第 0 行的通量收支决定,而这个收支在错误版本里仍然成立;斜率比 才直接显示第一个区间里伴随的斜率少了一半。第三,错误版本自己的切线、伴随和点积检验都完全正确, 的伴随值与切线值、复数步长值一致——它们检验的是程序的导数,而不检验程序离散的究竟是哪个方程。
Three lessons. First, it is invisible on coarse meshes: at to 16 the slip's state error is only 1.6 to 3.7 times the page's; it shows up when the order is measured far enough: between and 128 the state-error order is only 0.26 to 0.74, between the two finest levels 1.00 and for the gradient 1.00, against 2.00 for the page. Second, the central result does not see it either: the multiplier ratio tends to 1 for the slip too (0.9922 at , 0.99997 at ), because the multiplier is fixed by the flux balance of row 0 and that balance still holds in the slip; it is the slope ratio that shows directly that the slope of the adjoint in the first interval is halved. Third, the slip's own tangent, adjoint and dot test are all perfectly right and its adjoint and tangent values of agree with each other and with complex step: they test the derivative of the program, not which equation the program discretises.
本节测到的:内部节点上 (无限制器线性二阶、一阶格式一阶),两个约束行的乘子 、(同阶),离散梯度连同源项部分与边界部分都趋于连续值。但在粗网格上,梯度虽是 的导数(精确到舍入误差),却离连续梯度很远( 时 、 分量分别差 10% 与 93%, 时差 0.8% 与 3.1%; 时层没被分辨): 时 ,连续值为 0.085。本页格式的误差一部分来自出口乘子和入口附近节点的 偏差,一部分来自目标的梯形公式(见上面的分解)。边界斜率的作用很大(没有它们 分量的误差约为本页的 20 倍),但本页自身的粗网格误差有多少来自边界面,这里没有分离;这些都只是本问题上的数值证据。
What this section measured: at the interior nodes (second order for unlimited linear, first order for the first-order scheme), the multipliers of the two constraint rows tend to and (at the same order), and the discrete gradient, with its source part and its boundary parts, tends to the continuous values. But on coarse meshes the gradient, although it is the derivative of , exact up to rounding, is far from the continuous one (at the and components are off by 10% and 93%, at by 0.8% and 3.1%; the layer is unresolved for ): against 0.085 at . The error of the page's scheme comes partly from the outlet multiplier and the deviation of the nodes next to the inlet, partly from the trapezoid rule of the objective (the budgets above). The boundary slopes matter a great deal (without them the component is 20 times worse), but how much of the page's own coarse-mesh error is due to its boundary faces is not isolated here; all of this is numerical evidence for this one problem.
GS 与 GMRES:迭代及其转置
GS and GMRES: the iterations and their transposes
本节实验:先估计一下, 时(无限制器线性)切线方程的 GS 要迭代多少次?伴随方程是否一样多?然后用本节的收敛面板验证。
Experiment: Estimate first how many GS iterations the tangent system needs at (unlimited linear), and whether the adjoint system needs the same number. Then check with the convergence panel of this section.
第 10 节和第 11 节用 LU 解 与 。全局控件的网格很小,模型直接分解稠密矩阵;第 16、17 节的面板用带状 LU: 只有五条带( 个节点未知量),分解和回代都是 。二维、三维问题里直接分解会产生大量填充,迭代法取而代之。本节为本页自己的两个方程构造两种迭代,如实测量它们,并说明对伴随最重要的结构:伴随迭代是切线迭代的转置。
Section 10 and Section 11 solve and with LU. The meshes of the global controls are small and the model factors the dense matrix; the panels of Sections 16 and 17 use a band LU: has only five bands (on the nodal unknowns), so factorisation and solution cost . In two and three dimensions a direct factorisation fills in and iterations take over. This section builds two iterations for the page's own two systems, measures them honestly, and shows the structure that matters most for adjoints: the adjoint iteration is the transpose of the tangent iteration.
两个方程与直接解法
The two systems and the direct solve
两个方程都写成 :切线方程取 、、;伴随方程取 、、。本节里 只表示这个未知向量,不表示坐标, 也与第 8 节的 无关;结论里的 是 的特征值,不是第 16 节的乘子 。
Both systems are written : the tangent system has , , ; the adjoint system , , . In this section is only this unknown vector, never the coordinate, and has nothing to do with the of Section 8; the of the conclusions is an eigenvalue of , not the multiplier of Section 16.
有两条下带、两条上带(一阶格式各一条):一个面的通量涉及 ,一行用两个面。整条带只要前向代码的五次乘积,而不是 次:每个种子是每隔五列取一列的单位向量 之和(按 除以 5 的余数分成五组);同一种子所含各列在任一行中都不会同时有非零元,所以每次乘积给出带的五分之一(核对:与模型逐列求出的 Jacobian 完全相同,差为 0)。部分选主元的 LU 分解仍保持带状(行交换另需两条上带):(129 个未知量)时分解加一次回代共 1906 次运算(乘加 1522 次、除法 384 次,行交换 127 次),每个未知量约 14.8 次;伴随方程分解 ,代价相近(同一个 :1651 次运算,乘加 1267 次、除法 384 次,行交换 0 次)。
has two sub- and two superdiagonals (one each at first order): the flux of a face involves and a row uses two faces. The whole band takes five products of the tangent code, not : each seed is the sum of the unit vectors of every fifth column (one seed per residue of modulo 5); the columns summed in one seed never both have a nonzero in the same row, so each product delivers a fifth of the band (checked: identical to the model's column-by-column Jacobian, difference 0). LU with partial pivoting keeps the band (the row interchanges need two more superdiagonals): at (129 unknowns) factorisation and one solve take 1906 operations (1522 multiply-adds and 384 divisions, with 127 row interchanges), about 14.8 per unknown; the adjoint factorises at a similar cost (the same : 1651 operations, 1267 multiply-adds and 384 divisions, with 0 row interchanges).
格点格式还有一个格心页没有的结构:第 0 行和第 行是约束行(单位行),所以 ()、()。按(节点 0|内部节点 |节点 )分块,
The node scheme has a structure the cell-centred page lacks: rows 0 and are the constraint rows (unit rows), so () and (). Blocked as (node 0 | interior nodes | node ),
切线方程由边界数据驱动:、,然后 。伴随方程在内部是封闭的:,两个乘子随后由第 0 行和第 行读出,、。这和第 16 节的行 0 收支是同一件事。本页在 上核对了这两个分块算法:相对完整求解,差不超过 。迭代只在内部块上做实事:切线迭代里两个单位行第一次扫描就解完了;伴随迭代里两个乘子由内部值算出,并不反馈给内部。内部块单独迭代的谱半径与整个迭代的谱半径相差不超过 。
The tangent system is driven by the boundary data: , , then . The adjoint system is closed in the interior: , and the two multipliers follow from rows 0 and , , : this is the row-0 balance of Section 16. This page checks the two blocked algorithms at against the full solves, to within . An iteration has real work to do only on the interior block: in the tangent iteration the two unit rows are solved by the first sweep; in the adjoint iteration the two multipliers are computed from the interior values and do not feed back into it. The spectral radius of the iteration on the interior block alone differs from that of the whole iteration by less than .
以一阶 Jacobian 作预处理的 GS 缺陷校正
GS defect correction with the first-order Jacobian
设 是一阶残差在同一状态处的 Jacobian(三条带,两个约束行与 的相同;一阶格式时 )。在 上从零出发做一次 GS 扫描,按节点顺序依次求出(单位行给出 和 ):
Let be the Jacobian of the first-order residual at the same state (three bands, the two constraint rows as in ; for the first-order scheme). One GS sweep from zero on runs through the nodes in order (the unit rows give and ):
是 的严格下三角部分, 是扫描的输出(不是第 1 节的相位 )。缺陷校正把扫描 作用在精确算子的残差上:
with the strictly lower part of and the output of the sweep (not the phase of Section 1). The defect correction applies the sweep to the residual of the exact operator:
减去精确解 :误差满足 (切线方程)。对任意初值收敛当且仅当误差传播矩阵的谱半径 ;进入渐近阶段后,误差每迭代一次约乘以 。迭代只需要 :按无矩阵(matrix-free)方式实现时,切线方程用第 8 节的 residual_d,伴随方程用第 9 节的 residual_b;需要存储的只有 的三条带。
Subtract the solution : the error obeys (tangent system). The iteration converges from every start exactly when the spectral radius of the error propagation matrix is below one, ; once the asymptotic phase is reached, every iteration multiplies the error by about . The iteration needs only products : implemented matrix-free, the tangent system uses residual_d of Section 8 and the adjoint system residual_b of Section 9; only the three bands of are stored.
伴随迭代是切线迭代的转置
The adjoint iteration is the transpose of the tangent iteration
从零出发做 次切线迭代,结果对 是线性的:
Run the tangent iteration times from zero; the result is linear in :
逐项转置,并把 穿过幂次():
Transpose term by term and move through the power ():
这正是伴随方程迭代 的 步映射。而 :解 从最后一个节点开始,
This is the -step map of the adjoint iteration . And : solving starts from the last node,
即在 上反向扫描: 先出,内部节点从 往下,最后 用刚算出的内部值给出入口乘子——和上面的分块算法一致。由此得到四个结论,都已测量:
a backward sweep on : comes first, the interior nodes follow from downwards, and last uses the freshly computed interior values to give the inlet multiplier, as the blocked algorithm above does. Four consequences, all measured:
- 迭代的点积检验。对任意 、 和每个 ,。随机的 (,)测得最大相对差不超过 ;Python 审计显式构造两个 步矩阵(,,三种重构),差同样在舍入误差量级。Dot test of the iterations. for all , and every . With random (, ) the largest relative difference is below ; the Python audit forms both -step matrices explicitly (, , three reconstructions) and finds differences at the level of rounding.
- 截断的梯度在每一步都相等。令 、,则 ,也就是 :切线 GS 与伴随 GS 迭代 次后给出同一个导数,此时两者都还没收敛(图 98,最大差不超过 )。The truncated gradients agree at every step. With and , , which is : tangent GS and adjoint GS give the same derivative after iterations, while neither has converged (Figure 98, largest difference below ).
- 转置的扫描是反向的。伴随把信息从出口带向入口(第 16 节),反向扫描顺着这个方向。在 上做前向扫描也是伴随方程的一种合法迭代,但它不是切线迭代的转置。粗网格上它慢得多(:261 次,转置迭代 71 次),但网格 Péclet 数 降到约 1.5 以下后就不再如此(:前向扫描 268 次,转置迭代 267 次;:前向扫描 7882 次,转置迭代 7515 次;光滑限制器在 时前向扫描反而更快:278 次,转置迭代 295 次)。The transposed sweep runs backward. The adjoint carries information from the outlet to the inlet (Section 16), and the backward sweep follows that direction. A forward sweep on is also a valid iteration for the adjoint system, but it is not the transpose of the tangent iteration. On coarse meshes it is much slower (: 261 iterations against 71 for the transposed one), but not once the mesh Péclet number drops below about 1.5 (: forward sweep 268, transposed 267; : forward sweep 7882, transposed 7515; with the smooth limiter at the forward sweep is even faster: 278 against 295 for the transposed one).
- 同样的渐近速度。 与 相似,所以两个误差传播矩阵的特征值相同;测得的谱半径相差不超过 (受舍入误差限制:在双精度下,这类非对称矩阵的特征值只能算到这个精度,numpy 的 LAPACK 给出同类的差别)。迭代次数仍可略有不同(右端项不同,见下表)。The same asymptotic rate. is similar to , so the two error propagation matrices have the same eigenvalues; the measured spectral radii differ by less than (limited by rounding: in double precision the eigenvalues of such non-symmetric matrices cannot be computed more accurately, and numpy's LAPACK shows differences of the same kind). The iteration counts can still differ a little (different right-hand sides; table below).
GMRES
GMRES
GMRES 保留已经产生的方向,把它们组合起来。用同一个扫描 作右预处理,第 步的近似解为
GMRES keeps the directions it has generated and combines them. Right-preconditioned with the same sweep , the -th approximation is
用 Arnoldi 过程(修正 Gram–Schmidt 做两遍)和 Givens 旋转计算;本页每一步都记录真实残差 。重启版本每走固定步数(5、10 或 30 步)就从当前解重新开始,以限制存储。伴随版本用 与 。由定义可得两点:不重启时 GMRES 至多 步(方程的维数)就得到精确解(测量: 时 8 步, 时 16 步);GMRES 对 不是线性的, 通过极小化依赖 ,所以没有 GS 那样的 步转置:切线与伴随 GMRES 的导数在收敛前不同(、 时差可达 0.237),收敛后才一致(差不超过 )。
computed with the Arnoldi process (modified Gram–Schmidt, two passes) and Givens rotations; the page records the true residual after every step. The restarted versions start again from the current iterate after a fixed number of steps (5, 10 or 30) to bound the storage. The adjoint version uses and . Two facts follow from the definition: without restarts GMRES reaches the exact solution in at most steps (the dimension of the system; measured: 8 steps at , 16 at ); and GMRES is not linear in , since depends on through the minimisation, so there is no -step transposition as for GS: the tangent and adjoint GMRES derivatives differ before convergence (up to 0.237 for at ) and agree once both have converged (difference below ).
测量:迭代次数、谱半径、代价
Measurements: iterations, spectral radius, cost
下表给出的是使 的迭代次数(“切线/伴随”,切线方程取 ,即振幅 ),无限制器线性重构,默认设计;GMRES() 即不重启(至多 步得到精确解), 的次数见收敛面板;、512 只列重启的 GMRES。 是 的谱半径, 是由它预测的次数。Python 审计用自己的稠密 GS、GMRES(numpy)复现了每一个次数(重启的 GMRES 在 时相差不超过 2%),用 numpy 的特征值复现了每一个谱半径。
The table counts the iterations until (“tangent / adjoint”, the tangent system for , the amplitude ), unlimited linear reconstruction, default design; GMRES() is GMRES without restart (exact within steps); the counts for are in the convergence panel; for and 512 only the restarted GMRES is listed. is the spectral radius of , and the count it predicts. The Python audit reproduces every count with its own dense GS and GMRES (numpy; the restarted GMRES within 2% for ) and every spectral radius with numpy's eigenvalues.
| N | GS | GMRES(5) | GMRES(10) | GMRES(30) | GMRES() | ||||
|---|---|---|---|---|---|---|---|---|---|
| 8 | 52 / 54 | 69 / 71 | 118 / 121 | 0.6211 | 48 | 25 / 23 | 8 / 8 | 8 / 8 | 8 / 8 |
| 16 | 70 / 71 | 92 / 93 | 155 / 158 | 0.6740 | 58 | 50 / 42 | 42 / 33 | 16 / 16 | 16 / 16 |
| 32 | 114 / 117 | 148 / 152 | 248 / 255 | 0.7558 | 82 | 68 / 64 | 71 / 80 | 29 / 29 | 29 / 29 |
| 64 | 256 / 267 | 324 / 338 | 529 / 552 | 0.8735 | 170 | 98 / 93 | 124 / 129 | 90 / 88 | 48 / 45 |
| 128 | 660 / 702 | 829 / 882 | 1337 / 1423 | 0.9478 | 429 | 159 / 133 | 202 / 215 | 270 / 417 | 86 / 81 |
| 256 | 282 / 278 | 375 / 280 | 670 / 646 | ||||||
| 512 | 1176 / 915 | 687 / 671 | 961 / 838 |
三种重构对比(GS,,切线/伴随,以及 ):
The three reconstructions side by side (GS, , tangent / adjoint, and ):
| N | 一阶first | 无限制器none | 光滑smooth | (一阶) (first) | (无限制器) (none) | (光滑) (smooth) |
|---|---|---|---|---|---|---|
| 8 | 77 / 80 | 52 / 54 | 63 / 66 | 0.7496 | 0.6211 | 0.6819 |
| 16 | 117 / 132 | 70 / 71 | 79 / 82 | 0.8361 | 0.6740 | 0.7338 |
| 32 | 197 / 209 | 114 / 117 | 119 / 123 | 0.8868 | 0.7558 | 0.7757 |
| 64 | 373 / 388 | 256 / 267 | 283 / 295 | 0.9313 | 0.8735 | 0.9001 |
| 128 | 851 / 890 | 660 / 702 | 754 / 793 | 0.9656 | 0.9478 | 0.9599 |
结论(测量,不是定理):(1) GS 在测到的所有情形都收敛,但慢,而且网格越细越慢:无限制器线性 从 0.621()升到 0.948(),次数从 52 升到 660。(2) 欠松弛在这里没有帮助:设 是 中使 最大的特征值,即 。测得的所有情形里 ,即 都落在圆盘 内;对这样的 , 随 增大到 1 而减小,所以对每个 有 :欠松弛不可能减小 (在测到的情形里 在 从 1 降到 0.1 时还单调增大,步长取 0.05)。(3) 给出量级。无限制器线性的实际次数总是更多(:预测 429,实测 660):开始的 300 次迭代里残差平均每次只乘以 0.983,最后 300 次才是 0.950(),预测忽略了渐近阶段之前的过渡。只有一阶格式的切线方程在 与 16 时实测次数反而比预测略少(:实测 77,预测 80;:实测 117,预测 129),测量的 15 个情形里其余都更多。(4) GMRES 次数少得多(不重启时 伴随 81 次)。重启后的迭代次数随重启长度的变化却不单调: 时伴随 GMRES(30) 要 417 次,比 GMRES(10) 的 215 次和 GMRES(5) 的 133 次都多, 时 GMRES(5) 又最慢(915 次,GMRES(10) 671 次);更长的重启不保证更快,这里只有不重启的 GMRES 稳妥(至多 步)。(5) 代价:一次 GS 迭代约 次运算, 的带状 LU 分解加回代约 (:);GMRES 每一步再加上正交化,每个已存方向约 。一维问题里直接求解的代价只相当于约 1.8 次 GS 迭代(切线方程,)或约 1.6 次(伴随方程,):这里讲迭代是为了它们的结构,以及二维、三维问题,而不是为了一维里的速度。
Conclusions (measured, not theorems): (1) GS converges in every case measured, but slowly, and the more slowly the finer the mesh: for unlimited linear rises from 0.621 () to 0.948 () and the count from 52 to 660. (2) Under-relaxation does not help here: let be the eigenvalue of with the largest , so that . In every case measured , so lies in the disk ; for such , decreases as increases to 1, hence for every : under-relaxation cannot reduce (in the cases measured also increases monotonically as falls from 1 to 0.1, in steps of 0.05). (3) gives the order of magnitude. For unlimited linear the actual counts are always higher (at : 429 predicted, 660 measured): over the first 300 iterations the residual is multiplied on average by only 0.983 per iteration, over the last 300 by 0.950 (); the prediction ignores the transient before the asymptotic phase. Only the first-order scheme at and 16 needs slightly fewer iterations than predicted for the tangent system (: 77 measured, 80 predicted; : 117 measured, 129 predicted); in the other 13 of the 15 cases measured the count is higher. (4) GMRES needs far fewer iterations (81 for the adjoint at without restart). The restarted counts, however, are not monotone in the restart length: at the adjoint GMRES(30) needs 417, more than GMRES(10) with 215 and GMRES(5) with 133, and at GMRES(5) is the slowest (915, GMRES(10) 671); a longer restart guarantees nothing, and only unrestarted GMRES is dependable here (at most steps). (5) Cost: a GS iteration takes about operations, the band LU factorisation and solve of about (: ); a GMRES step adds the orthogonalisation, about per stored direction. In one dimension the direct solve costs about 1.8 GS iterations (tangent system, ) or about 1.6 (adjoint system, ): the iterations are taught here for their structure and for two and three dimensions, not for speed in one.
和格心页比较:同一设计下两种格式的 GS 次数几乎相同,例如无限制器线性 的切线/伴随次数:节点 660 / 702,格心 642 / 704;谱半径 0.9478 与 0.9481。节点矩阵多出的两行(切线方程里的约束行,伴随方程里两个由内部值算出的乘子)与内部解耦,不改变渐近速度。
Compared with the cell-centred page: for the same design the two schemes need almost the same number of GS iterations, for instance unlimited linear at , tangent / adjoint: nodes 660 / 702, cells 642 / 704; spectral radius 0.9478 against 0.9481. The two extra rows of the node matrix (the constraint rows in the tangent system, the two multipliers computed from the interior in the adjoint system) decouple from the interior and do not change the asymptotic rate.
| N | 伴随:反向扫描(转置)adjoint: backward sweep (transpose) | 伴随:前向扫描adjoint: forward sweep | |
|---|---|---|---|
| 16 | 5.72 | 71 | 261 |
| 64 | 1.43 | 267 | 268 |
| 128 | 0.71 | 702 | 689 |
| 256 | 0.36 | 2169 | 2288 |
| 512 | 0.18 | 7515 | 7882 |
不要把 GS 的“截断导数相等”套用到 GMRES 上:GS 的 步映射是线性的,转置关系在每一步成立;GMRES 不是线性映射,只有收敛后的结果满足对偶关系。用迭代解求梯度时,切线与伴随的结果只能在收敛后比较(GMRES),或在相同 、严格转置的迭代之间比较(GS,此时应在舍入误差内一致);这时的差别指向导数代码或转置的错误。
Do not carry the “equal truncated derivatives” of GS over to GMRES: the -step map of GS is linear and the transposition holds at every step; GMRES is not a linear map, and only its converged results satisfy the duality. When gradients come from iterative solves, compare the tangent and adjoint values only at convergence (GMRES) or at equal with exactly transposed iterations (GS, where they must agree to rounding); a difference there points to an error in the derivative code or the transposition.
总结:一个网格、一个通量、三个方向
Summary: one mesh, one flux, three directions
本节实验:对三个方向,各说出种子是什么、解哪个方程、得到什么;然后用本节的总览面板核对——它对当前控件把所有计算重做一遍。
Experiment: For each of the three directions, say what is seeded, which equation is solved and what comes out. Then check with the dashboard of this section, which redoes every calculation for the current controls.
本页只算了一件事:离散目标 对设计 的导数。它用三种方式计算,三者共用网格、面通量和重构;变化的只是传播的数据和数据流的方向。
The page computed one thing, the derivative of the discrete objective with respect to the design , in three ways that share the mesh, the face flux and the reconstruction; only the data that are propagated and the direction of the data flow change.
三个方向
Three directions
| 原始Primal | 前向(tangent)Forward (tangent) | 伴随(adjoint)Reverse (adjoint) | |
|---|---|---|---|
| 种子Seed | () () | (来自 ,) (from , ) | |
| 运行的代码Code that runs | 残差代码(取值、重构、通量、分发)the residual code (gather, reconstruct, flux, scatter) | residual_d,同序residual_d, same order | residual_b,逆序,累加 +=residual_b, reverse order, accumulating += |
| 要解的方程Equation solved | (Newton 法) (Newton) | ||
| 输出Output | ,, | ,以及乘子 , and the multipliers | |
| 每个梯度的求解次数Solves per gradient | —— | 每个设计分量一次(3)one per design component (3) | 1(与分量个数无关)1 (any number of components) |
| 本页位置Where | 第 5 节Section 5 | 第 8 节、第 10 节Section 8, Section 10 | 第 9 节、第 11 节、第 12 节Section 9, Section 11, Section 12 |
切线与伴随由对偶恒等式连在一起:对任意种子 (第 9 节的点积检验),在解上它变成 。代价的差别也由此而来:切线每个设计分量要一次求解,伴随每个目标要一次求解;本页有 3 个分量、1 个目标。
The tangent and the adjoint are tied by the duality identity: for any seeds (the dot test of Section 9), which at the solution becomes . The difference in cost comes from the same place: the tangent needs one solve per design component, the adjoint one solve per objective; this page has 3 components and 1 objective.
下面的播放器用当前控件,把这条链一步一步走一遍:每一步显示那一刻的数组。
The player below walks the chain step by step with the current controls, showing the arrays of each moment.
验证了什么,怎样验证
What was verified, and how
每一种检查回答一个不同的问题:点积检验问反向代码是不是前向代码的转置;“切线 = 伴随”问的是两个求解是否一致;复数步长和差分问导数是不是这个程序的导数;Python 数据问另一份独立实现是否给出同样的数;连续公式问连续推导本身;收敛阶问离散是否逼近连续。下表的数是本页在构建时运行各项检查所得的最大值,范围见各行所列的网格与种子(除非另注,取默认设计);各节打印的是它们自己的网格上的值,可能更小;“见”一栏给出解释该检查的小节;Python 审计(audit/audit_f.py 与 audit/ 中其他脚本)独立地复现了它们。点积检验的标准是 :随机状态上最坏的相对差在 量级, 低于舍入误差。
Each check answers a different question: the dot test asks whether the reverse code is the transpose of the tangent code; tangent = adjoint asks whether the two solves agree; complex step and finite differences ask whether the derivative is the derivative of this program; the Python data ask whether an independent implementation gives the same numbers; the continuous formula checks the continuous derivation itself; the orders ask whether the discretisation approaches the continuum. The numbers in the table are the largest values found by the checks that this page runs at build time, over the meshes and seeds named in each row (default design unless noted); the sections print the values for their own meshes, which can be smaller, and “See” names the section that explains the check; the Python audit (audit/audit_f.py and the other scripts in audit/) reproduces them independently. The dot test is held to : the worst relative difference over random states is of order , and is below rounding.
| 检查Check | 最大偏差Largest discrepancy | 见See |
|---|---|---|
| 点积检验:前向代码与反向代码(随机状态与种子,,相对)dot test of the tangent and reverse code (random states and seeds, , relative) | 2.1e−15 | 第 9 节Section 9 |
| 模型的代码与独立的 Python 参考实现(随机状态与种子:残差、前向与反向代码、Jacobian;相对,分母取 , 为参考值)the model code against the independent Python reference (random states and seeds: residual, tangent and reverse code, Jacobians; relative, denominator with the reference value) | 4.8e−15 | 第 8 节Section 8 |
| 切线梯度与伴随梯度之差(三种重构,)tangent gradient minus adjoint gradient (3 reconstructions, ) | 5.9e−15 | 第 11 节Section 11 |
| 贯穿整个求解的复数步长与伴随之差()complex step through the solve minus adjoint () | 1.2e−15 | 第 14 节Section 14 |
| 扰动解的复残差 complex residual of the perturbed state, | 1.1e−15 | 第 14 节Section 14 |
| GS 的 步转置(相对)-step transposition of GS (relative) | 2.7e−15 | 第 17 节Section 17 |
| 两个分块算法(内部块与第 0、 行)与完整求解之差()the two blocked algorithms (interior block, rows 0 and ) against the full solves () | 8.3e−16 | 第 17 节Section 17 |
| 中心差分(,完整重算,)与伴随之差central differences (, full re-solves, ) minus adjoint | 6.0e−9 | 第 13 节Section 13 |
| 与 Python 数据中的梯度之差(三种重构,)difference from the gradients of the Python data (3 reconstructions, ) | 8.9e−13 | 第 15 节Section 15 |
| 乘子 、 与 Python 数据之差(三种重构,)multipliers , against the Python data (3 reconstructions, ) | 3.8e−13 | 第 16 节Section 16 |
| 的 L1 范数与 Python 数据之差(相对)L1 norm of against the Python data (relative) | 4.2e−8 | 第 16 节Section 16 |
| 默认设计的 Newton 解、伴随、梯度与 Python 参考实现之差(,三种重构,相对)Newton solutions, adjoints and gradients at the defaults against the Python reference (, 3 reconstructions, relative) | 1.1e−13 | 第 11 节Section 11 |
| 连续梯度公式与精确梯度之差continuous gradient formula minus exact gradient | 1.0e−13 | 附录 AAppendix A |
收敛阶(Python 数据中最细的两层):无限制器线性的状态 L1 误差阶 2.00,梯度分量 的误差阶 1.94( 到 16384);一阶格式为 1.00 与 1.00( 到 16384)。无限制器线性的梯度误差在模型的网格上变号(、 分量不止一次),最后一次分别在 与 1024()、128 与 256()、1024 与 2048()之间,这些点附近的观测阶不规则(第 16 节)。伴随本身: 的 L1 误差阶,无限制器线性 1.97(模型,),1.99(Python,;见第 15、16 节)。
Orders of convergence (between the two finest levels of the Python data): for unlimited linear reconstruction the state L1 error converges at 2.00 and the error of the gradient component at 1.94 ( to 16384); for the first-order scheme at 1.00 and 1.00 ( to 16384). The gradient errors of unlimited linear reconstruction change sign on the meshes of the model (more than once for and ), for the last time between and 1024 (), between 128 and 256 () and between 1024 and 2048 (), and the observed orders are erratic around these meshes (Section 16). The adjoint itself: the L1 error of converges at 1.97 for unlimited linear (model, ) and 1.99 (Python, ; see Sections 15 and 16).
总览面板对当前控件把这些检查全部重做:原始解、切线与伴随梯度、对偶、点积检验、复数步长、中心差分、精确梯度、连续公式和两个乘子,放在一张表里。
The dashboard redoes all of these checks for the current controls: the primal solution, the tangent and adjoint gradients, the duality, the dot test, complex step, central differences, the exact gradient, the continuous formula and the two multipliers, in one table.
离散梯度是什么,不是什么
What the discrete gradient is, and what it is not
是离散目标 (即这个网格上这段程序的目标)的梯度,精确到舍入误差(上表)。它不是连续目标 的梯度,只有加密网格才能逼近后者(第 15 节、第 16 节)。默认设计、 时,离散梯度是 ,连续梯度(闭式)是 。误差有三个来源(第 16 节):出口乘子 在粗网格上相对误差很大,入口附近的节点( 偏离 )补上了没有源项行的第一个半控制体,目标的梯形公式带来 ; 分量最坏,差 0.335。用 的优化器优化的是这个网格上的 ;结果是否接近连续最优,取决于离散化在重要的地方是否足够准——这里是入口层附近的节点和边界面。
is the gradient of the discrete objective , the objective of this program on this mesh, exact up to rounding (table above). It is not the gradient of the continuous objective ; that one is approached only as the mesh is refined (Section 15, Section 16). At the defaults with the discrete gradient is and the continuous one (closed form) . The error has three sources (Section 16): the outlet multiplier , whose relative error is large on coarse meshes; the nodes next to the inlet ( is off from ), which make up for the first half volume that has no source row; and the trapezoid rule of the objective, . The component is the worst, off by 0.335. An optimiser that uses optimises on that mesh; whether the result is close to the continuous optimum depends on whether the discretisation is accurate where it matters, here the nodes near the inlet layer and the boundary faces.
格点格式与格心格式
The node-centred scheme against the cell-centred scheme
两个页面解同一个问题:同一个方程、构造解、设计、目标、Rusanov 通量和重构。差别在未知量放在哪里,以及由此带来的边界处理。下表左栏是格心页,右栏是本页;最后一栏是这一项差别在数字上的表现(格心页的数字来自它自己的独立审计)。结论不是“格点格式处处更好”:无限制器线性在 Python 数据的所有网格上三个分量的梯度误差都更小,但一阶格式的 、 分量并不更好, 时 分量反而更差。格心页的附录 B 在 到 64 上并排比较了两种格式(两张图、一张表和一个面板),这里不重复。
The two pages solve the same problem: the same equation, manufactured solution, design, objective, Rusanov flux and reconstructions. What differs is where the unknowns live and, with it, the treatment of the boundary. In the table the left column is the cell-centred page, the right column this page; the last column is what the difference does to the numbers (the cell page's numbers come from its own independent audit). The conclusion is not “the node scheme is better everywhere”: for unlimited linear reconstruction the gradient errors of all three components are smaller on every mesh of the Python data, but for the first-order scheme the and components are not better, and at the component is worse. Appendix B of the cell-centred page compares the two schemes side by side at to 64 (two figures, a table and a lab); this is not repeated here.
| 格心页Cell-centred page | 本页(节点)This page (nodes) | 数字上的表现What it does to the numbers | |
|---|---|---|---|
| 未知量与误差的参照Unknowns and the reference of the errors | 个单元平均;误差对精确单元平均 cell averages; errors against the exact cell averages | 个节点点值;误差对精确节点值 point values; errors against the exact nodal values | 、无限制器线性的状态 L1 误差:5.23e−2(格心)、1.33e−2(节点)state L1 error at , unlimited linear: 5.23e−2 (cells), 1.33e−2 (nodes) |
| 边界Boundary | 边界面距未知量 ,读 ;入口值伴随 boundary faces at from the unknown read ; the adjoint of the inlet value is | 第 0 行和第 行是约束行;梯度的边界项就是 rows 0 and are constraints; the boundary term of the gradient is | : 离 4.8%(格心); 离 1.4%(节点): is 4.8% from (cells); is 1.4% from (nodes) |
| 伴随的斜率The slope of the adjoint | 进入 10% 以内要 (格心)、(节点)within 10% needs (cells), (nodes) | ||
| 边界的重构Reconstruction at the boundary | 两个端单元不重构():迹只有一阶精度,带额外黏性 the two end cells are not reconstructed (): first-order traces, added viscosity | 边界节点取单侧斜率:迹二阶精度,边界面的跳跃比一阶格式小一个 因子(只在第 1 行与第 行上有约 的额外黏性,第 3 节)one-sided slopes at the boundary nodes: second-order traces, the jump across a boundary face is times the first-order one (an added viscosity of about on rows 1 and only, Section 3) | 的 分量梯度误差:0.343(格心)、0.034(节点);格点格式去掉边界斜率后 0.671-gradient error at : 0.343 (cells), 0.034 (nodes); the node scheme without its boundary slopes: 0.671 |
| 一阶格式First-order scheme | 内部行(除两个端行)是黏性 的中心格式the interior rows (except the two end rows) are the central scheme with viscosity | 所有内部行如此; 由第 0、 行读出all interior rows are; are read off rows 0 and | 时层宽是 的 13.5 倍(两页相同)at the layer is 13.5 times wider than (on both pages) |
| 默认设计、 的梯度误差()Gradient errors at the defaults, () | 0.343、0.146、0.3100.343, 0.146, 0.310 | 0.034、0.335、0.00390.034, 0.335, 0.0039 | 无限制器线性:、 节点好得多, 格心更好unlimited linear: much better for and on nodes, better for on cells |
| 加密时的梯度误差(Python 数据)Gradient errors under refinement (Python data) | —— | —— | 无限制器线性:三个分量在每个网格上都更好( 的误差之比 3.7e−4 到 5.0e−2);一阶:、 不更好(之比 1.01 到 1.42、1.02 到 5.13),只有 更好unlimited linear: better in all three components on every mesh (error ratio for from 3.7e−4 to 5.0e−2); first order: not better for and (ratios 1.01 to 1.42 and 1.02 to 5.13), only for |
| GS 迭代GS iterations | ,无限制器线性,切线/伴随:642 / 704,, unlimited linear, tangent / adjoint: 642 / 704, | 660 / 702,660 / 702, | 几乎相同almost the same |
| 错误的边界处理The boundary slip | 边界面距离取 :, 看不出boundary-face distance : , does not show it | 边界面距离取 :,乘子 看不出boundary-face distance : , the multiplier does not show it | 两个页面里都只有一阶收敛,导数检验全部通过first order only on both pages, every derivative check passes |
本页的局限
The limits of this page
- 固定的正黏性(控件中 ),没有激波; 与 的极限顺序没有处理(第 16 节只比较几个固定的 ,渐近分析也只是形式推导)。A fixed positive viscosity ( in the controls) and no shocks; the order of the limits , is not treated (Section 16 compares several fixed values of and gives formal asymptotics only).
- 一个光滑的构造解(周期的 ,所以 )、一个目标、三个设计分量;Rusanov 速度固定为 ,光滑限制器取 。 使 、 分量只通过两个乘子之和看到它们;无限制器线性时两个误差几乎等大反号,一阶格式时同号相加(第 16 节);一般的设计没有这种结构。One smooth manufactured solution (periodic , so ), one objective and three design components; the Rusanov speed is fixed at and the smooth limiter uses . That makes the and components see the two multipliers only through their sum; for unlimited linear reconstruction their errors are nearly equal and opposite, for the first-order scheme they have the same sign and add (Section 16); a general design would not have this structure.
- 本页不给出非线性问题或格式的存在性与唯一性定理;已证明的收敛估计只有第 4 节的一阶稳定性界(用面误差控制误差,要求解落在 Newton 法的范围 内);其余每个观测到的收敛阶都只是本问题上的数值证据(第 4–7 节与附录 A 的 A.4 中的证明针对那里写明的线性方程组、单步估计、该稳定性界和恒等式);第 16 节的渐近分析是形式推导,乘子与伴随的关系是观测,不是定理。No existence or uniqueness theorem for the nonlinear problem or the scheme is given, and the only convergence estimate that is proved is the first-order stability bound of Section 4 (an error bound by the face errors, valid for solutions inside Newton's box ); every other observed order is numerical evidence for this one problem (the proofs of Sections 4–7 and of A.4 in Appendix A concern the linear systems, one-step estimates, that bound and the identities named there); the asymptotics of Section 16 are formal, and the relation of the multipliers to the continuous adjoint is an observation, not a theorem.
- 第 17 节的迭代是为了讲清结构,没有调优;一维里带状 LU 更便宜。The iterations of Section 17 are there to show their structure and are not tuned; in one dimension the band LU is cheaper.
接下来读什么
Where to go next
- 附录 A:用变分推出连续伴随与梯度公式,以及它的边界项。Appendix A: the continuous adjoint and the gradient formula with its boundary term, derived with variations.
- 附录 B:一个 Dirichlet–Neumann 格点格式的基准算例。Appendix B: a Dirichlet–Neumann nodal benchmark.
- 附录 C:怎样复现本页的数字和 Python 数据。Appendix C: how to reproduce the numbers of this page and the Python data.
要记住的
What to remember
- 切线与伴随用自动微分的记号:切线是 、按原顺序;伴随是 、按逆序累加 ;二者由对偶 连在一起。The tangent and the adjoint use the notation of automatic differentiation: the tangent is in the original order, the adjoint accumulated with in reverse order; the duality ties them together.
- 核心就是一个面:取值、重构、通量、分发;切线与伴随复用同样的局部导数 和重构权重。边界行是约束行,不进入分发。One face is everything: gather, reconstruct, flux, scatter; the tangent and the adjoint reuse the same local derivatives and reconstruction weights. The boundary rows are constraints and take no part in the scatter.
- 伴随梯度是离散目标的导数,精确到舍入误差:用点积检验、复数步长和差分验证它;用加密和连续伴随检验它离连续梯度有多远。The adjoint gradient is the derivative of the discrete objective, exact up to rounding: verify it with the dot test, complex step and finite differences; check how far it is from the continuous gradient with refinement and the continuous adjoint.
- 积分形式的残差使人预期内部节点上 (同号、无因子 ),两个约束行的乘子趋于连续伴随的边界通量;本页的测量:、、,同阶(无限制器线性二阶,一阶格式一阶)。默认设计下 时 就只差 1.4%,尽管伴随的层(宽 )没有被分辨; 的相对误差则很大( 很小),别的设计在粗网格上也可能差得多(第 16 节)。分辨层需要 。An integrated residual leads one to expect at the interior nodes (the same sign, no factor ) and the multipliers of the two constraint rows to approach the boundary fluxes of the continuous adjoint; measured here: , and at the same order (second for unlimited linear, first for the first-order scheme). At the default design and , is already within 1.4% of although the adjoint's layer (width ) is not resolved; the relative error of is large ( is small), and other designs can be much worse on a coarse mesh (Section 16). Resolving the layer needs .
- 一阶格式等于黏性为 的中心格式(所有内部行)。一个把距离写错的边界面能让所有导数检验通过,却使二阶降为一阶:乘子比值看不出来,斜率 才看得出来。The first-order scheme is the central scheme with viscosity (in all interior rows). A boundary face with a wrong distance passes every derivative check and drops second order to first: the multiplier ratio does not show it, the slope does.
- 伴随迭代应当是切线迭代的转置:转置的 GS 反向扫描,截断导数在每一步都相等;GMRES 不是线性的,只有收敛后才满足对偶关系。The adjoint iteration should be the transpose of the tangent iteration: transposed GS sweeps backward and the truncated derivatives agree at every step; GMRES is not linear and satisfies the duality only at convergence.
附录 A:连续伴随
Appendix A: The continuous adjoint
本节实验: 越小,连续伴随在 处的斜率 越陡。梯度公式里的边界项 会随 消失吗?先猜,再用图 107 和本附录的 实验台验证。
Experiment: as decreases, the slope of the continuous adjoint at steepens. Does the boundary term of the gradient formula vanish as ? Guess first, then check with Figure 107 and the lab of this appendix.
正文各节对离散程序求导:前向用点 ,伴随用横线 。本附录换一个对象:直接对连续方程求伴随。这样得到的 正是离散伴随 在网格加密时应当逼近的函数:内部节点上 ,两个约束行的乘子 、 逼近边界通量 与 (第 16 节;A.7 先给出本页网格上的测量);推导也说明它的边界条件从哪里来。这里用变分记号:、、 是连续函数的变分; 只出现在本附录(以及明确说“连续”的句子里)。
The main sections differentiate the discrete code: the tangent with dots , the adjoint with bars . This appendix takes a different object, the continuous equation itself, and derives its adjoint. The result is the function that the discrete adjoint should approach under mesh refinement: at the interior nodes, and the multipliers , of the two constraint rows approach the boundary fluxes and (Section 16; A.7 gives the measurements on this page's mesh). The derivation also shows where the boundary conditions of come from. The notation here is variational: , , are variations of continuous functions; appears only in this appendix (and in sentences that say continuous).
A.1 状态方程、目标与变分
A.1 The state equation, the objective and their variations
连续问题就是第 1 节的方程:总通量 ,
The continuous problem is the equation of Section 1: with the total flux ,
设计改变 时,源项改变 ,两端给定值改变 、,状态随之改变 。把通量线性化:(最后一项只在 本身改变时出现,因为 写在通量里)。于是线性化的状态方程是
When the design changes by , the source changes by , the two prescribed end values by , , and the state follows with . Linearise the flux: (the last term appears only when itself changes, because sits inside the flux). The linearised state equation is therefore
每次只改变一个分量 ,除以 ,就得到连续切线解 :对 、 有 ,,;对 有 ,两端 。它是离散切线解 的连续对应物。
Moving one component at a time and dividing by gives the continuous tangent : for and , with and ; for , with at both ends. It is the continuous counterpart of the discrete tangent .
A.2 拉格朗日函数与两次分部积分
A.2 The Lagrangian and two integrations by parts
把方程乘以一个待定的函数 ,加到目标上:
Multiply the equation by a function , still to be chosen, and add it to the objective:
对状态方程的任何解,方括号内的量都为零,所以对任意 都有 ,变分也相同:。这正是正文 的连续版本,正负号约定一致(第 16 节比较 与 时无须变号)。对设计求变分:
On any solution of the state equation the bracket vanishes, so for every , and so do their variations: . This is the continuous version of the main text's with the same sign convention (Section 16 compares with without a change of sign). Vary the design:
中间一项含未知的 的导数。我们要把导数从 挪到 上,一步一步来:(1)分部积分一次;(2)展开括号;(3)对 再分部积分一次:
The middle term contains derivatives of the unknown . Move them from onto , one step at a time: (1) integrate by parts once; (2) expand the bracket; (3) integrate by parts again:
代回 ,按“内部”和“两端”整理:
Put this back into and sort it into the interior and the two ends:
A.3 选 λ:伴随方程及其边界条件
A.3 Choosing λ: the adjoint equation and its boundary conditions
只有解完切线问题才知道。我们选 ,让所有含 的项都消失,图 104 画出了这四项:
is known only after solving the tangent problem. Choose so that every term containing disappears; Figure 104 draws the four pieces:
- 内部。被积函数为零:。右端项 来自目标的权重。Interior. The integrand vanishes: . The right side comes from the weight of the objective.
- 两端的第一项 含扰动状态在两端的斜率 、——它们是切线问题的输出,事先不知道。唯一的办法是让它们的系数为零:。这样 也一起消失。The first end term contains the end slopes , of the perturbed state: they are outputs of the tangent problem, unknown beforehand. The only way out is to make their coefficients zero: . Then disappears as well.
- 两端的第二项 只含给定端值的变分,是已知的:它留下来,成为梯度的一部分。The second end term contains only variations of the prescribed end values, which are known: it stays and becomes part of the gradient.
二阶方程配两个条件,数目正好。若再对 加条件,问题就超定;少一个,就留下一个算不出的项。结果是连续伴随问题与梯度公式:
A second-order equation with two conditions: exactly the right number. One more condition on would overdetermine the problem; one fewer would leave a term that cannot be evaluated. The result is the continuous adjoint problem and the gradient formula:
分量没有边界项(端值不依赖 ),但多了物理通量本身的一项 。对构造解,源项 给出 ,所以在 上被积函数 处处为零,,与 一致。
The component has no boundary term (the end values do not depend on ) but one more term from the physical flux itself, . For the manufactured solution the source gives , so the integrand vanishes identically at and , in agreement with .
常见错误:漏掉通量项,只算 。默认设计下这个数是 1.8769,而正确答案是 0:通量项 恰好把它抵消(图 106 右)。离散的情形相同:第 12 节的 路径有一条直接穿过通量 。
A common mistake: dropping the flux term and computing only . At the defaults that number is 1.8769, whereas the correct answer is 0: the flux term cancels it exactly (Figure 106, right). The discrete case is the same: one of the paths of Section 12 runs straight through the flux .
下面的播放器把这三步逐行算出来:取一个检验函数 代替 ,用本页的 计算每一行的每一项。前六行对任何 都相等(只用到分部积分和伴随方程);第七行还要用切线方程,所以只对真正的切线解成立。
The player below evaluates the three steps line by line: a test function stands in for , and every term of every line is computed with this page's . The first six lines agree for any (they use only integration by parts and the adjoint equation); the seventh also uses the tangent equation, so it holds only for a true tangent.
A.4 伴随问题适定吗:能证明的部分
A.4 Is the adjoint problem well posed? What can be proved
(A.3–A.4 中 表示一般的函数——检验函数或 定义域里的元素; 才是切线解。)记状态线性算子 (带齐次端值 ),伴随算子 (带 )。下面三条中,(1)、(3) 在本页完整证明;(2) 证明一个方向,另一方向引用 Fredholm 择一定理。它们对任何连续的 和任何 成立,只有第三条要求 。
(In A.3–A.4, is a generic function, a test function or an element of the domain of ; the tangent is .) Write the linear state operator (with homogeneous end values ) and the adjoint operator (with ). Of the three statements below, (1) and (3) are proved in full here; (2) proves one direction and cites the Fredholm alternative for the other. They hold for every continuous and every , and only the third needs .
(1) 唯一可解。伴随方程对 是一阶的:记 ,则 等价于 。积分两次:
(1) Unique solvability. The adjoint equation is first order in : with , the equation is the same as . Integrate twice:
已经满足; 是关于 的一个线性方程,系数 ,所以 唯一确定,解存在且唯一。右端项为零时同一计算给出 、:在这组边界条件下,伴随齐次问题只有零解。对 积分一次得 常数 ,同样的积分因子对任意连续的 解出唯一的 ( 确定那个常数):状态算子 可逆。
holds by construction; is one linear equation for with the coefficient , so is determined uniquely and the solution exists and is unique. With a zero right side the same computation gives and : the adjoint operator has no nonzero homogeneous solution under these boundary conditions. Integrating once gives constant , and the same integrating factor solves it uniquely for every continuous ( fixes the constant): the state operator is invertible.
(2) 为什么两者总是同时可逆(简短论证)。A.2 的分部积分在 、 时两端项全为零,剩下 Green 恒等式 。若 而 ,则对所有 有 :任何满足 的右端项 都不在 的值域里, 不可逆。反方向( 不可逆 伴随有非零齐次解)是正则二阶边值问题的 Fredholm 择一定理,属于标准结论,本页不再证明;对本问题,(1) 已直接证明:状态算子与伴随算子的齐次问题都只有零解。离散的对应物是 与 秩相同。
(2) Why the two are always invertible together (a short argument). With and every end term of the integration by parts of A.2 vanishes, and what is left is Green's identity . If with , then for every : no right side with lies in the range of , and is not invertible. The converse ( not invertible a nonzero homogeneous solution of the adjoint problem) is the Fredholm alternative for regular second-order boundary-value problems, a standard result not re-proved here; for this problem (1) has already shown directly that the homogeneous state and adjoint problems have only the zero solution. The discrete counterpart is that and have the same rank.
(3) 最大值原理()。(a) 伴随方程的右端项 。在 的内部极大点 、,于是 ,与 矛盾;所以最大值只在两端取到,在 上 (这一步不需要 的符号)。(b) 若 ,取常数 ,则 ;同样的论证说明 没有内部极小,它的最小值在端点取到: 处是 , 处是 ,所以最小值是 。令 :
(3) The maximum principle (). (a) The right side of the adjoint equation, , is negative. At an interior maximum of , and , so , contradicting ; hence the maximum is attained only at the ends and on (this step needs no sign of ). (b) If , take a constant ; then , and the same argument shows that has no interior minimum: its minimum is attained at an end: the value is at and at , so the minimum is . Letting ,
默认设计 ,下界是 ,即 。它与 无关: 时 本身保持有界,变陡的只是 处的斜率——那是一个边界层,不是 的爆破。本附录中各个 的解都满足这两个不等式(check_g.js 逐点检查)。
At the defaults and the lower bound is , i.e. . It does not depend on : as the function itself stays bounded, and only its slope at steepens. That is a boundary layer, not a blow-up of . Every solution of this appendix, for every , satisfies the two inequalities (checked point by point by check_g.js).
本页没有证明的:离散伴随 收敛到 (第 16 节与 A.7 给的是数值证据);下面 A.6 的渐近展开(形式推导,用数值核对); 变号或出现激波时的任何结论。
Not proved on this page: the convergence of the discrete adjoint to (Section 16 and A.7 give numerical evidence); the asymptotic expansion of A.6 below (a formal derivation, checked numerically); anything for a that changes sign or contains a shock.
A.5 求解:本页的谱方法
A.5 Solving it: the page’s spectral solver
model.js 的 continuousAdjoint(D, M) 用 Chebyshev 配点法解这个两点边值问题:取 个点 ——它们在两端加密,正好落在边界层里;对过这些点的插值多项式求导,得到微分矩阵 与 (无衬线的 不是设计 );在内部点上要求 ,两端点上 ;解一个稠密线性方程组, 由 给出。它与有限体积模型只共用稠密 LU(experiments/numerics.js)和 的公式;离散方法(配点)与有限体积、与离散伴随无关。不共用方法的核对是下面的积分因子公式(不用配点,也不用 LU),以及附录 B 的 check_dn.py(用 ODE 积分器打靶)。Python 审计 audit/audit_g.py 里的 NumPy 求解器是同一种配点法的重新实现:它检验代码,不检验方法。
continuousAdjoint(D, M) of model.js solves this two-point boundary-value problem by Chebyshev collocation: take the points , which cluster at both ends, right inside the boundary layer; differentiating the interpolating polynomial through them gives the differentiation matrices and (the sans-serif is not the design ); require at the interior points and at the two end points; solve one dense linear system, and read from . With the finite-volume model it shares only the dense LU (experiments/numerics.js) and the formula of ; its discretisation (collocation) has nothing to do with the finite volumes or the discrete adjoint. The checks that do not share the method are the integrating-factor formula below (no collocation, no LU) and, for Appendix B, check_dn.py (shooting with an ODE integrator). The NumPy solver of the Python audit (audit/audit_g.py) is the same collocation written again: it checks the code, not the method.
收敛性:在 401 个采样点上, 与 的 最大相差不到 3e−13, 的相对差不到 3e−13。另一条完全独立的路径是 A.4 的积分因子公式,用 Gauss 求积直接计算:它与谱方法的 相对差不到 2e−14。Python 审计 audit/audit_g.py 用 NumPy 比较 与 (同一种配点法,所以这检验代码与收敛,不检验方法)。本附录正文与图里伴随的数值用 ;切线问题用 ;本页其余各节用 (201 个点,model.js 的默认值): 与 的 最大相差不到 2e−13, 的相对差不到 4e−14,所以那里打印的数字与本附录在所示位数上一致;实验台按 选 (96 到 256,保证边界层被分辨,见 check_g.js)。
Convergence: on 401 sample points, for and differs by at most 3e−13, and by at most 3e−13 relative. A second, completely independent path is the integrating-factor formula of A.4, evaluated by Gauss quadrature: its agrees with the spectral one to within 2e−14 relative. The Python audit audit/audit_g.py compares with in NumPy (the same collocation, so this checks the code and the convergence, not the method). The adjoint numbers in the text and figures of this appendix use ; the tangent problems use ; the other sections of this page use (201 points, the default of model.js): for and differs by at most 2e−13, and by at most 4e−14 relative, so the numbers printed there agree with this appendix to the digits shown; the labs choose from (96 to 256, enough to resolve the layer, as check_g.js verifies).
默认设计下 、。把梯度公式逐项算出(Clenshaw–Curtis 求积,用同一组点):“源项积分”是 (对 是 ,与图 106 右图相同;这一行的第三列放通量项 ),“边界项”是 。
At the defaults and . The gradient formula term by term (Clenshaw–Curtis quadrature on the same points): the source integral is (for , , as in the right panel of Figure 106, with the flux term in the third column of that row), the boundary term .
| 分量Component | 源项积分Source integral | 边界项Boundary term | 和Sum | 精确梯度Exact gradient | 和与精确之差Sum minus exact |
|---|---|---|---|---|---|
| 0.472777 | −0.616785 | −0.144007698 | −0.144007698 | −6e−14 | |
| 1.732274 | −1.647118 | 0.085155858 | 0.085155858 | 1e−15 | |
| 1.876920 | −1.876920 | 0 | 0 | 0 |
细看这张表。源项积分和边界项各自是 的量,和却小得多: 的两部分(1.73、−1.65)约是和的 20 倍,几乎抵消; 的两部分(0.47、−0.62)约是和的 3.3–4.3 倍;和与精确值相差不到 7e−14。所以边界项不能省:省掉它, 分量会错二十倍。
Read the table carefully. The source integral and the boundary term are each of order one while their sum is much smaller: for the two parts (1.73 and −1.65) are about 20 times the sum and nearly cancel; for (0.47 and −0.62) they are about 3.3–4.3 times the sum; the sums agree with the exact values to within 7e−14. So the boundary term cannot be dropped: without it the component would be wrong by a factor of twenty.
在任意设计下检查对偶:用本附录自己的配点法解三个连续切线问题,再与伴随公式比较。
Check the duality at any design: solve the three continuous tangent problems with this appendix's own collocation and compare with the adjoint formula.
默认设计下(切线问题取 ),切线解与精确导数 (、、)相差不到 2e−13;两条路径给出同一个导数:
At the defaults (tangent problems with ) the tangent solutions agree with the exact derivatives (, , ) to better than 2e−13, and the two paths give the same derivative:
| 分量Component | 切线解 Tangent | 伴随公式Adjoint formula | 两者之差Difference | 切线解的误差Error of the tangent |
|---|---|---|---|---|
| −0.1440076980 | −0.1440076980 | 5e−14 | 1e−13 | |
| 0.0851558583 | 0.0851558583 | 2e−13 | 2e−13 | |
| 0.0000000000 | 0.0000000000 | 0 | 0 |
这就是连续层次的对偶恒等式:左边一次切线求解给一个分量,右边一次伴随求解给全部分量。它与离散的 是同一件事:分部积分扮演了转置的角色。
This is the duality identity at the continuous level: on the left one tangent solve gives one component, on the right one adjoint solve gives all of them. It is the same thing as the discrete , with integration by parts playing the role of the transpose.
A.6 x = 0 处的边界层
A.6 The boundary layer at x = 0
为什么层在 ?伴随的对流项 ()把信息从 带向 ——与流动方向相反。所以 是伴随的“上游”, 的条件只能靠一个薄层来满足。
Why is the layer at ? The adjoint convection term (with ) carries information from towards , against the flow. So is upstream for the adjoint, and the condition at can only be met through a thin layer.
外解。略去 ,得 ;从上游端 积分,得
Outer solution. Drop : , integrated from the upstream end :
内解。在 附近 ,层内 变化快、 的作用可略,方程近似为 ,解为 。要求 、在层外与外解匹配,得复合近似:
Inner solution. Near , ; inside the layer varies fast and the source is negligible, so the equation is approximately , whose solutions are . Requiring and a match with the outer solution outside the layer gives the composite approximation:
实验的答案: 趋于一个非零常数,所以边界项 在 时不消失,而是趋于 。这也解释了离散的情形:本页网格上入口行 是约束,它的乘子 在梯度里乘 ,所以 就是 的离散对应, 本身逼近 (A.7)。它不是 的某个节点值,也不必除以网格间距。在默认设计下,要得到接近 的 ,网格不必分辨边界层: 时第一个区间是层宽 的 11.4 倍,而 与 的相对差,无限制器线性是 1.4%(A.7),一阶是 1.2%,光滑限制器是 5.4%。这是该设计下的观察,不是普遍规律(见第 16 节)。
The answer to the experiment: tends to a nonzero constant, so the boundary term does not vanish as ; it tends to . This also explains the discrete picture. On this page's mesh the inlet row is a constraint; its multiplier multiplies in the gradient, so is the discrete counterpart of , and itself approximates (A.7). It is not a nodal value of and needs no division by a mesh width. At the default design, getting close to does not require a mesh that resolves the layer: at the first interval is 11.4 times the layer width , and the relative difference between and is 1.4% for the unlimited linear reconstruction (A.7), 1.2% for first order and 5.4% for the smooth limiter. This is an observation at this design, not a rule (Section 16).
误差有多大。由 A.4, 的精确表达式为 ( 是 A.4 积分因子公式里 时的 )。对两个积分分别展开——分母用 Laplace 方法,;分子用外展开加端点修正——得到两项形式渐近展开
How large is the error. The exact formula is , where is the of the integrating-factor formula of A.4 with . Expanding the two integrals separately (Laplace's method for the denominator, ; an outer expansion with an end correction for the numerator) gives the formal two-term expansion
下表用谱方法核对(每个 一次求解):一项近似的误差除以 趋于 ,两项近似的剩余除以 保持有界。默认 时一项近似差 2.3e−3(约 0.16%)。
The table checks this against the spectral solver (one solve per ): the error of the one-term approximation divided by tends to , and the remainder of the two-term approximation divided by stays bounded. At the default the one-term approximation is off by 2.3e−3 (about 0.16%).
| 误差 error | 两项后误差 after two terms | ||||
|---|---|---|---|---|---|
| 0.1 | −13.5304 | −1.353042 | 7.58e−2 | 0.7582 | 5.74 |
| 0.03 | −47.2758 | −1.418273 | 1.06e−2 | 0.3531 | 5.61 |
| 0.01 | −142.6568 | −1.426568 | 2.30e−3 | 0.2297 | 4.50 |
| 0.003 | −476.0922 | −1.428277 | 5.89e−4 | 0.1962 | 3.82 |
| 0.001 | −1428.6769 | −1.428677 | 1.88e−4 | 0.1883 | 3.59 |
自己调 :实验台每次用本页的谱方法重新求解,画出 、层内放大与复合近似,并给出 、 和梯度的两部分。
Move yourself: the lab re-solves with this page's spectral solver each time, draws , the zoom into the layer with the composite approximation, and reports , and the two parts of the gradient.
播放器把 从 0.2 一步步减到 0.001:看层怎样变薄, 的点怎样落到渐近线上,而梯度的和始终不变。
The player lowers step by step from 0.2 to 0.001: watch the layer thin, the points of settle onto the asymptote, and the sum of the gradient stay put.
A.7 离散的对应:本页网格上的伴随
A.7 The discrete counterpart on this page’s mesh
第 9、11 节的离散伴随解 (),梯度是 。把这个乘积按行拆开。两个约束行对设计的依赖只经过边界数据,、,所以
The discrete adjoint of Sections 9 and 11 solves () and gives the gradient . Split that product by rows. The two constraint rows depend on the design only through the boundary data, and , so
内部行之和对应 A.3 的源项积分 (对偶控制体 的行里放的是源项在它上面的精确积分, 扮演 );剩下两项对应边界项 ,其中 ,。(对 分量,内部行之和还带着 A.3 的通量项,没有边界部分。)所以约束行的乘子就是整个边界项:不必再从边界面上补任何东西。
The sum over the interior rows is the counterpart of the source integral of A.3 (the row of the dual volume holds the exact integral of the source over it, and plays the role of ); the two remaining terms are the counterpart of the boundary term , with and . (For the component the interior sum also carries the flux term of A.3, and there is no boundary part.) The multiplier of a constraint row is therefore the whole boundary term: nothing has to be added from a boundary face.
为什么可以把 直接与 比较,符号相同、也没有网格宽度的因子?第 5 节的行是对偶控制体上的积分: 是流出 的数值通量净值减去源项在 上的精确积分,所以 ,而 。于是 时 ,与 A.2 一致。(附录 B 的行除以了 ,那里 : 的缩放跟着行的缩放走。)约束行的缩放是 1,所以 、 不受这个因子影响。
Why can be compared with directly, with the same sign and without a factor of the mesh width? The rows of Section 5 are integrals over the dual volumes: is the net numerical flux out of minus the exact integral of the source over , so , and . Hence when , as in A.2. (The rows of Appendix B are divided by ; there : the scaling of follows the scaling of the rows.) The constraint rows have unit scaling, so and are not affected by this factor.
下表给出默认设计下的两部分(无限制器线性重构,本页的模型, 到 256),最后一行是 A.5 的连续值。
The table gives the two parts at the default design (unlimited linear reconstruction, this page's model, to 256); the last row holds the continuous values of A.5.
| :内部行之和: interior rows | :两个约束行: the two constraint rows | :内部行之和: interior rows | :两个约束行: the two constraint rows | |||
|---|---|---|---|---|---|---|
| 8 | −1.40608 | −0.08701 | 0.45766 | −0.63573 | 2.11760 | −1.69770 |
| 16 | −1.40116 | −0.05158 | 0.48842 | −0.61855 | 1.81638 | −1.65182 |
| 32 | −1.41357 | −0.03521 | 0.47818 | −0.61686 | 1.74862 | −1.64732 |
| 64 | −1.42122 | −0.02736 | 0.47395 | −0.61678 | 1.73487 | −1.64710 |
| 128 | −1.42468 | −0.02391 | 0.47296 | −0.61678 | 1.73252 | −1.64711 |
| 256 | −1.42599 | −0.02261 | 0.47280 | −0.61678 | 1.73225 | −1.64711 |
| 连续continuous | −1.42657 | −0.02203 | 0.47278 | −0.61678 | 1.73227 | −1.64712 |
读法与 A.5 相同。边界部分是两个乘子之和,(入口值与出口值的导数相同): 时它与连续的边界项相差 3.1%(两个分量都如此), 单独与 相差 1.4%,尽管第一个区间是层宽的 11.4 倍。内部行之和反而是不太准的一部分: 的那一项在 时偏大 22%(2.1176 对 1.7323),又因为它与边界部分几乎抵消,所以 梯度成了 0.4199,而不是 0.0852:错了它自身的 3.9 倍。加密之后两部分都趋于连续值(最后一行);表里没有阶,阶是第 15、16 节的话题。
Read it as in A.5. The boundary part is the sum of two multipliers, (the derivatives of the inlet and outlet values are equal): at it is within 3.1% of the continuous boundary term, for both components, and alone is within 1.4% of , although the first interval is 11.4 times wider than the layer. The interior sum is the less accurate part: for it is 22% too large at (2.1176 against 1.7323), and because it nearly cancels against the boundary part, the gradient comes out as 0.4199 instead of 0.0852: wrong by 3.9 times its own size. Under refinement both parts approach the continuous values (last row); the table says nothing about orders, which are the subject of Sections 15 and 16.
独立的稀疏实现(附录 C)把这张表继续到 :、,与 和 相差 1.7e−7 与 1.7e−7;从 到 ,两个误差的观测阶是 2.00 与 2.00(数值证据,不是定理)。在共同的网格 上,它与本页模型的 、 相差不到 2e−14(三种重构,check_g.js)。
The independent sparse implementation (Appendix C) continues the table to : and , which differ from and by 1.7e−7 and 1.7e−7; between and the two errors have observed orders 2.00 and 2.00 (numerical evidence, not a theorem). On the common meshes it agrees with the , of this page's model to within 2e−14 (three reconstructions, check_g.js).
关于乘子有一个精确的结论: 的第 个方程(即 的第 列)写作 (约束行的对角元 ,第 1、2 行是仅有的另外两行含 )。一阶重构时 ,,于是
One statement about the multiplier is exact: equation of (column of ) reads (the constraint row has the diagonal , and rows 1 and 2 are the only others that contain ). With first-order reconstruction and , so
因子里的 是入口面上的黏性通量 (伴随在入口为零), 是中心对流通量, 是 Rusanov 耗散;网格没有分辨层时 很小,乘子主要由后两项决定。 时(一阶,默认设计):。二阶重构时同一个方程还多一项 ,恒等式的形状不变。
In the factor, is the viscous flux through the inlet face, (the adjoint vanishes at the inlet), is the central convective flux and the Rusanov dissipation; when the mesh does not resolve the layer, is small and the other two terms carry the multiplier. At (first order, default design): . With second-order reconstruction the same equation has one more term ; the shape of the identity is unchanged.
自己试:选 与重构,实验台在浏览器里重新解本页的格点格式,画出 与 ,给出两个乘子、梯度的两部分和格心页的对应值(只有默认设计有)。
Try it: choose and the reconstruction; the lab solves this page's node scheme again in the browser, draws against , and reports the two multipliers, the two parts of the gradient and the cell-centred page's counterparts (available at the default design only).
附录 B:Dirichlet–Neumann 格点基准
Appendix B: A Dirichlet–Neumann nodal benchmark
本节实验:这是另一个模型,未知量同样放在节点上,但行除以 , 处是 Neumann 条件。离散伴随 在每个节点上都逼近连续伴随吗?先预测最后一个 PDE 节点上的比值 ,再看图 111。
Experiment: a different model, with the unknowns at the nodes as on this page but the rows divided by and a Neumann condition at . Does the discrete adjoint approach the continuous adjoint at every node? Predict the ratio at the last PDE node first, then read Figure 111.
本附录讲的是另一个模型:它是为一篇论文建立的(Python 包的 paper_dn/ 文件夹,见附录 C)。它的网格与本页相同——未知量是节点值 ,控制体是对偶区间——代码也还是五条语句(取值、通量、分发、源项、覆盖)。不同的是:面通量是没有 Rusanov 耗散的中心通量(也不重构),每一行除以 (是密度,不是对偶控制体上的积分),源项在节点上采样(第 5 节用精确积分),出口是 Neumann 条件(一个单侧二阶差分;本页的出口是第二个 Dirichlet 约束),设计变量只有一个 ,目标里的权重是冻结的。为了不和正文的模型混淆,它用自己的字母:状态 、设计 (不是 )、权重 ;这些字母和 B.7 的 、、、 只在本附录里有效(第 4、6、7、16、17、18 节的 、、 另有含义),掩码 不是第 5 节的精确通量 ,这里的 、 同样是两个替换行的乘子,只是行的缩放不同(B.4)。下面的结论不适用于正文的模型,反过来也一样。
This appendix is about a different model, built for a paper (the folder paper_dn/ of the Python package, see Appendix C). Its mesh is that of this page, with the unknowns at the nodes as nodal values and the dual intervals as control volumes, and its code is again five statements (gather, flux, scatter, source, overwrite). What differs: the face flux is the central flux without Rusanov dissipation (and without reconstruction), every row is divided by (a density, not an integral over a dual volume), the source is sampled at the node (where Section 5 integrates it exactly), the outlet carries a Neumann condition (a one-sided second-order difference; the outlet of this page is a second Dirichlet constraint), there is a single design parameter , and the weight of the objective is frozen. To keep it apart from the main model it has its own letters: state , design (not ), weight ; these, and , , , of B.7, hold only in this appendix (Sections 4, 6, 7, 16, 17 and 18 use , , for other things), the mask is not the exact flux of Section 5, and , here are again the multipliers of the two replaced rows, with a different scaling of the rows (B.4). Nothing below transfers to the main model, nor the other way round.
为什么放在这里:它的精确解、源项、权重和主基准的伴随都是有理系数多项式,可以精确核对;它有两个被边界条件替换的行(一个 Dirichlet、一个 Neumann),正好用正文的自动微分记号写一遍,与第 4 节的约束行对照;它说明行的缩放怎样决定 的缩放(这里 ,第 16 节里 );还有一份理论审阅给出证明和误差界,本附录逐条核对。
Why it is here: its exact state, source, weight and primary adjoint are polynomials with rational coefficients and can be checked exactly; it has two rows replaced by boundary conditions (a Dirichlet and a Neumann one), which this appendix writes in the automatic-differentiation notation of the main text, to be compared with the constraint rows of Section 4; it shows how the scaling of the rows sets the scaling of (here , in Section 16 ); and a theory review supplies proofs and error bounds, which this appendix audits one by one.
B.1 模型与精确解
B.1 The model and its exact solution
对每个 都满足方程和两个边界条件( 在 为零)。黏性固定:主基准 ,次基准 ;设计的基点 。精确的切线解 与 无关。
satisfies the equation and both boundary conditions for every ( vanishes at ). The viscosity is fixed: for the primary case, for the secondary; the base design is . The exact tangent does not depend on .
目标是 ,权重 在 处构造一次,之后冻结: 改变时它不变。主基准的权重是“先给伴随,再算权重”(构造的伴随):取 ,( 使 满足 B.2 的出口条件),令 ,于是 恰好是 处的伴随。用有理数精确计算:
The objective is with a weight built once at and then frozen: it does not change when does. The primary weight is made by choosing the adjoint first (a manufactured adjoint): take with ( makes satisfy the outlet condition of B.2) and set , so that is exactly the adjoint at . In exact rational arithmetic:
论文和数据把这个权重叫 (目标名“q0”);本页的 是通量,所以写作 。 对 是线性的,精确梯度 对每个 都成立;但 只在 处是伴随。次基准用正文的权重 ,:,梯度 。
The paper and the data call this weight (objective “q0”); on this page is the flux, hence . is linear in , so the exact gradient holds for every , but is the adjoint only at . The secondary case uses the main text's weight with : , gradient .
B.2 Neumann 端的连续伴随
B.2 The continuous adjoint at a Neumann end
拉格朗日函数与附录 A 相同:。切线解 满足 ,(入口值 1 与 无关),。附录 A 的两次分部积分给出
The Lagrangian is that of Appendix A: . The tangent satisfies with (the inlet value 1 does not depend on ) and . The two integrations by parts of Appendix A give
代入 、,端点项只剩 。 和 都未知,所以两个系数都必须为零:
With and the end terms reduce to . Both and are unknown, so both coefficients must vanish:
对照附录 A:Dirichlet 端给 ,Neumann 端给 的 Robin 条件。梯度里没有边界项,因为边界数据不依赖 。精确核对: 在 处满足这三个条件,(check_dn.py 用 sympy 逐项验证)。
Compare with Appendix A: a Dirichlet end gives , a Neumann end a Robin condition for . There is no boundary term in the gradient because the boundary data do not depend on . Exact check: satisfies the three conditions at and (verified term by term with sympy by check_dn.py).
边界条件的乘子。若把 与 也作为约束,即在 中加上 (乘子 、),那么 、 都是自由的,它们的系数给出 、,并且 ——与附录 A 的边界项相同。B.4 里两个被替换行的乘子逼近的就是它们。
Multipliers of the boundary conditions. If and are also added to as constraints, i.e. is added to (multipliers , ), then and are free, and their coefficients give and , with : the boundary term of Appendix A again. These are what the multipliers of the two replaced rows approximate in B.4.
正负号约定。理论审阅(THEORY_REVIEW.md)用 (“物理”伴随):它的 满足 ,等于本页的 ,梯度写成 ;它的 (审阅把 记作 ,把本页的 、 记作 、;审阅在 约定下的连续密度 就是本页的 )就是本页的 ,离散密度 就是本页的 反号。两种约定给出同一个梯度,只是 的符号相反。本页沿用正文的 与 ,所以 与 同号。
The sign convention. The theory review (THEORY_REVIEW.md) uses (a “physical” adjoint): its solves and equals of this page, with the gradient written ; its (the review writes for , and , for this page's , ; its continuous density of the convention is this page's ) is this page's , and its discrete density is this page's with the sign flipped. Both conventions give the same gradient; only the sign of differs. This page keeps the main text's and , so and have the same sign.
B.3 格式:节点、中心通量、两个替换行
B.3 The scheme: nodes, central fluxes, two replaced rows
节点 ,,( 是区间数,与第 2 节相同);未知量 是节点值。相邻节点之间是面 ,面上的中心通量
Nodes , , ( counts intervals, as in Section 2); the unknowns are nodal values. Between two nodes sits the face with the central flux
就是第 3 节的 取 (没有 Rusanov 耗散,即没有 里的 项)、不重构(,)、。内部节点的行除以 (密度;第 5 节的行是对偶控制体上的积分),源项在节点上采样(第 5 节用精确积分):
which is the face flux of Section 3 with (no Rusanov dissipation, that is, without the term of ), no reconstruction (, ) and . The rows of the interior nodes are divided by (densities; the rows of Section 5 are integrals over the dual volumes), with the source sampled at the node (Section 5 integrates it exactly):
两端的行被边界条件替换:(Dirichlet:第 4 节的约束行,),( 的二阶单侧差分;本页在这里放的是 )。目标用梯形公式 ,, 与 处减半。Python 程序(benchmark.py)消去 ,只保留 :方程相同, 也相同(B.4);这里保留 ,让两个替换行写法一致。
The two end rows are replaced by the boundary conditions: (Dirichlet: the constraint row of Section 4 with ) and (the one-sided, second-order ; where this page puts ). The objective is the trapezoid rule with , halved at and . The Python code (benchmark.py) eliminates and keeps only : the same equations and the same (B.4); here is kept so that the two replaced rows read alike.
写成程序是五条语句(一个面的取值、通量、分发,源项,以及覆盖),形状与第 5 节的残差代码相同,只是没有重构,它的前向与伴随代码就是第 8、9 节的写法;覆盖的写法与其他一维格点页相同—— 屏蔽两个替换行,、 是两个约束,、 是通量对两侧状态的对流偏导(没有 Rusanov 耗散,所以没有第 3 节的 、 里的 ):
As code it is five statements (the gather, flux and scatter of one face, the source, and the overwrite), the shape of the residual code of Section 5 without reconstruction, with the tangent and reverse versions of Sections 8 and 9; the overwrite is written as on the node-centred pages of the other 1-D lessons: masks the two replaced rows, and are the two constraints, and , are the convective partial derivatives of the flux (without Rusanov dissipation, hence without the of , in Section 3) with respect to the two states:
| 原始语句Primal statement | 前向(tangent)Tangent | 伴随(逆序执行)Adjoint (run in reverse order) | |
|---|---|---|---|
| (1) | |||
| (2) | |||
| (3) | |||
| (4) | |||
| (5) |
覆盖(5)在程序最后执行,所以它的伴随语句(5′)最先执行:把种子分成两份。屏蔽后的种子 (保留第 到 行)交给转置的面循环与源项;两个约束种子 、 交给约束读到的节点:、,、、。写进替换行的源项因此没有权重。整段代码的对偶恒等式:
The overwrite (5) runs last in the code, so its adjoint statement (5′) runs first: it splits the seed in two. The masked seed (rows to ) goes to the transposed face loop and the sources; the two constraint seeds , go to the nodes the constraints read: , , , , . A source written into a replaced row therefore gets no weight. The duality identity of the whole code:
下面两个面板在 上执行这些语句:播放器逐条执行前向或伴随扫描;对偶面板用随机种子把恒等式分成三层核对。
The two panels below run these statements for : the player steps through the tangent or the adjoint sweep, and the duality panel checks the identity level by level with random seeds.
B.4 离散伴随:正负号约定、缩放与最后一个节点
B.4 The discrete adjoint: sign, scaling and the last node
记 ,(PDE 行上是 ,两个替换行上是 0)。切线方程 ,伴随方程 ,
With and ( on the PDE rows, 0 on the two replaced rows): tangent , adjoint , and
本页的 JavaScript 实现在所有存储的网格上两种算法相差不超过 5e−14。
On every stored grid the page's JavaScript implementation gives the two to within 5e−14.
缩放。,其中 是密度;连续的拉格朗日函数用梯形公式离散:
Scaling. with a density, while the continuous Lagrangian, discretised by the trapezoid rule, reads
所以 逼近 。第 5 节的行是对偶控制体上的积分(),所以那里 ,没有 (第 16 节,A.7)。 的缩放跟着行的缩放走;符号跟着拉格朗日函数的符号走(两处相同)。精确地说:记 ,把内部行乘以 得 、; 等价于 ,即 :(),,,而梯度 不变。所以 就是积分行版本的伴随,与第 16 节同标度;两个约束行没有缩放,乘子 、 在两种写法里是同一个数。
So approximates . The rows of Section 5 are integrals over the dual volumes (), so there without (Section 16, A.7). The scaling of follows the scaling of the rows; the sign follows the sign of the Lagrangian (the same in both places). To be exact: let and multiply the interior rows by , , ; then is equivalent to , that is : (), , , while the gradient does not change. So is the adjoint of the version with integral rows, on the scale of Section 16; the two constraint rows are not scaled, and the multipliers , are the same numbers in both versions.
下面的实验台把这个引理摆出来:选 与指数 ,把内部行乘以 ( 是基准自己的行, 是积分行),看 贴着 还是 ,而 、 和梯度一点没变。
The lab below shows the lemma at work: choose and an exponent , multiply the interior rows by ( is the benchmark's own rows, the integral rows), and watch follow or while , and the gradient do not move.
替换行的乘子不是 的值。 的第 、 个方程( 的第 、 列)给出 、,正是 B.2 的 、。主基准 、;在 上 、。
The multipliers of the replaced rows are not values of . Equations and of (columns and of ) give and : the and of B.2. For the primary case and ; at , and .
最后一个 PDE 节点。 的最后两个方程( 的最后两列)可以精确地解出(理论审阅第 6 节;check_dn.py 用 sympy 从残差重新推出):
The last PDE node. The last two equations of (the last two columns of ) can be solved exactly (section 6 of the theory review; check_dn.py derives them again from the residual with sympy):
若 ,第二式乘 后取极限得 ——也就是比值 (因为 ),第一式再给 。这里还用到 与 有界(审阅中也这样写)。前提 在数据上成立( 时 离 1 不到 3e−8,且 ),但没有证明:所以 是一个有条件的结论。
If , the second identity multiplied by gives in the limit, i.e. the ratio (as ), and the first then . This also uses and a bounded , as the review states. The premise holds on the data (at , is within 3e−8 of 1, and ) but is not proved: the is therefore a conditional result.
怎样理解:PDE 行到 为止。梯度 是 的一个求积公式,而端点 没有 PDE 行;梯形公式在端点的半个权重 就落在 上:。最后一个节点上的“层”是离散伴随携带端点权重的方式,梯度仍是二阶(实测)。后果体现在误差范数上: 在 PDE 节点 上是 (实测:在最细的两层网格之间,这些节点上的最大误差的阶是 2.00,主、次基准相同),在最后一个 PDE 节点 处趋于 ;两个替换行处,范数用 (数据上也是 )与 处的 0,而不是 、(它们不是 的值)。于是最大误差不收敛(趋于 ), 误差 (半阶),去掉这个节点则是二阶:
How to read it: the PDE rows stop at . The gradient is a quadrature of , and the end node has no PDE row; the half weight of the trapezoid rule at the end, , lands on : . The layer at the last node is how the discrete adjoint carries the end weight, and the gradient stays second order (measured). The consequence shows in the error norms: is at the PDE nodes (measured: between the two finest grids the maximum over these nodes has order 2.00 in both benchmarks) and tends to at the last PDE node ; at the two replaced rows the norms use (also on the data) and at , not , (they are not values of ). So the maximum error does not converge (it tends to ), the error is (half order), and without that node it is second order:
时:主基准比值 1.49945, 误差除以 为 0.03570,而 ;次基准比值 1.49890,,。去掉节点 后的 阶(相邻网格):主基准 2.01、2.00、2.00、2.00。这些是数值证据;理论审阅也明确说没有证明全场的收敛阶或这个层的下界。
At : the primary ratio is 1.49945 and the error divided by is 0.03570, against ; the secondary ratio is 1.49890, with against . The orders without node (successive grids), primary: 2.01, 2.00, 2.00, 2.00. These are numerical evidence; the theory review, too, says explicitly that neither the order of the whole field nor a lower bound of this layer is proved.
自己试:选网格、基准与 ,看状态、切线解和伴随,以及两个乘子与最后一个节点的比值。
Try it: choose the mesh, the case and , and look at the state, the tangent and the adjoint, the two multipliers and the ratio at the last node.
B.5 存储的加密结果
B.5 The stored refinement runs
下表来自 results/refinement_q0.json(主基准);数据由 benchmark.py 生成,带实现文件的 SHA-256(implementation_sha256);结果文件本身的 SHA-256 由 data.js 在构建时计算(附录 C)。所有误差都在完整网格上计算, 是节点上的梯形网格范数;伴随误差在 用 ,在 用 ,在 取 0(与 benchmark.py 相同;它存储的是相反的正负号)。本页自己的 JavaScript 格式在每个网格上重算了这些量:与存储值(目标、梯度、所有误差与采样剖面)的绝对差不到 2e−11。相对于 的梯度误差(1.4e−9 与 8.7e−10,是两个 数的微小差),差别可达 7e−3,所以表中最细网格梯度误差的阶(2.01)的最后一位是舍入噪声;对其余存储误差,本页重算的相对差都不到 9e−5。
The table comes from results/refinement_q0.json (primary case); the data were produced by benchmark.py and carry the SHA-256 of the implementation (implementation_sha256); the SHA-256 of the result file itself is computed by data.js at build time (Appendix C). Every error uses the full grid; is the trapezoidal grid norm over the nodes; the adjoint error is for , at and 0 at (as in benchmark.py, which stores the opposite sign). This page's own JavaScript scheme recomputes these quantities on every grid: it agrees with the stored values (objective, gradient, every error and the sampled profiles) to within 2e−11 in absolute terms. Relative to the gradient errors at (1.4e−9 and 8.7e−10, small differences of two numbers) the difference is up to 7e−3, so the last digit of the finest gradient-error order in the table (2.01) is rounding noise; for every other stored error the page's recomputation agrees to within 9e−5 relative.
| 状态 State | 阶Order | 目标误差Objective error | 梯度误差Gradient error | 阶Order | 伴随 Adjoint | 阶Order | ||
|---|---|---|---|---|---|---|---|---|
| 16 | 8.25e−5 | — | 5.30e−4 | 1.51e−3 | — | 7.92e−3 | — | 1.25191 |
| 32 | 2.06e−5 | 2.00 | 1.33e−4 | 3.78e−4 | 2.00 | 5.59e−3 | 0.50 | 1.32358 |
| 64 | 5.16e−6 | 2.00 | 3.31e−5 | 9.45e−5 | 2.00 | 4.13e−3 | 0.43 | 1.39078 |
| 128 | 1.29e−6 | 2.00 | 8.28e−6 | 2.36e−5 | 2.00 | 3.03e−3 | 0.45 | 1.43840 |
| 256 | 3.22e−7 | 2.00 | 2.07e−6 | 5.90e−6 | 2.00 | 2.18e−3 | 0.47 | 1.46715 |
| 512 | 8.06e−8 | 2.00 | 5.18e−7 | 1.48e−6 | 2.00 | 1.56e−3 | 0.48 | 1.48301 |
| 1024 | 2.01e−8 | 2.00 | 1.29e−7 | 3.69e−7 | 2.00 | 1.11e−3 | 0.49 | 1.49136 |
| 2048 | 5.04e−9 | 2.00 | 3.24e−8 | 9.22e−8 | 2.00 | 7.87e−4 | 0.50 | 1.49564 |
| 4096 | 1.26e−9 | 2.00 | 8.09e−9 | 2.31e−8 | 2.00 | 5.57e−4 | 0.50 | 1.49781 |
| 8192 | 3.15e−10 | 2.00 | 2.02e−9 | 5.77e−9 | 2.00 | 3.94e−4 | 0.50 | 1.49890 |
| 16384 | 7.87e−11 | 2.00 | 5.06e−10 | 1.44e−9 | 2.01 | 2.79e−4 | 0.50 | 1.49945 |
播放器沿着 逐帧前进,左边是存储剖面在 附近的伴随密度,右边是到当前帧为止的误差:
The player steps through ; on the left the adjoint density of the stored profile near , on the right the errors so far:
B.6 差分核对
B.6 The finite-difference check
数据里还有 上的步长扫描:每个步长 都完整地重新求解两次(,从基解热启动),中心差分与伴随梯度比较。
The data also hold a step scan at : for every step the nonlinear problem is re-solved twice (, warm-started from the base root) and the central difference is compared with the adjoint gradient.
B.7 理论审阅:哪些成立,核对了哪些
B.7 The theory review: what holds, what was checked
包里的 THEORY_REVIEW.md 给出一份手写的证明。它的离散部分在三个条件下成立:、、;对 ,主基准要 ,次基准要 (本页按条件重新算出)。证明的链条是:在精确解的节点值 处,约化 Jacobian 是非奇异 M 矩阵—— 时相邻非对角元严格为负(不可约),其余非对角元为零,行和非负,且第一行的行和 ;比较函数(闸函数) 给出 ;冻结 Jacobian 的 Newton 映射在半径 的球内是压缩映射(压缩因子 ,审阅记作 ),所以球内有唯一的离散根,且
THEORY_REVIEW.md in the package gives a handwritten proof. Its discrete part holds under three conditions: , and ; for this means for the primary case and for the secondary (recomputed here from the conditions). The chain of the proof: at the exact sample the reduced Jacobian is a nonsingular M-matrix: its neighbouring off-diagonal entries are strictly negative when (so it is irreducible), all other off-diagonal entries vanish, all row sums are nonnegative and the first, , is positive; the barrier (comparison) function gives ; the frozen Newton map is a contraction in the ball of radius (contraction factor , the review's ), so the ball holds a unique discrete root, and
其中 。梯度界只有 :它不预测实测的二阶。主基准写成显式常数:()。我们核对了什么,见下表:
with . The gradient bound is only : it does not predict the measured second order. For the primary case with explicit constants: (). What was checked, and how:
| 审阅的说法Claim of the review | 本页的核对Check on this page | 结论Status |
|---|---|---|
| 精确解、源项、()、、梯度 、、 与 Robin 条件exact state, source, (), , gradients , , and the Robin condition | sympy 有理数(check_dn.py A1–A6);本页 BigInt 有理数(figs_g.js)sympy rationals (check_dn.py A1–A6); BigInt rationals of this page (figs_g.js) | 精确成立holds exactly |
| Green 恒等式、伴随的两个边界条件、能量恒等式Green identity, the two adjoint boundary conditions, the energy identity | 对一般多项式的 sympy 恒等式(A7–A9)sympy identities for general polynomials (A7–A9) | 精确成立holds exactly |
| 精确解节点值的残差 、行和、约化的最后一行、比较函数(闸函数)不等式 exact-sample residual , row sums, the reduced last row, the barrier | 符号推导(A10–A13);数值,N = 16–1024(E1–E5)symbolic (A10–A13); numerical, N = 16–1024 (E1–E5) | 成立holds |
| M 矩阵、、球内唯一根、M-matrix, , unique root in the ball, | 读过论证;在每个满足条件、N ≤ 1024 的网格上数值核对(E1–E7)argument read; checked numerically on every grid meeting the conditions with N ≤ 1024 (E1–E7) | 成立;证明本页未重写holds; the proof is not rewritten here |
| 、、 的 界、目标的界, , the bound of , the objective bounds | 存储数据上每个满足条件的网格(D1–D5);界至少是实测误差的 48 倍(状态)和 598 倍(梯度)every grid of the stored data meeting the conditions (D1–D5); the bounds exceed the measured errors at least 48-fold (state) and 598-fold (gradient) | 成立(保守)holds (conservative) |
| 最后两列的精确恒等式; 层(以 为前提)the exact identities of the last two columns; the layer (with the premise ) | sympy(A14);数据与本页计算(C3)sympy (A14); data and this page’s runs (C3) | 恒等式成立;条件在数据上成立,未证明identities hold; the condition holds on the data, unproved |
| 梯度的二阶收敛、伴随场的阶、层的下界、全局唯一性、Newton 迭代进入该球、浮点执行second-order gradient, orders of the adjoint field, a lower bound of the layer, global uniqueness, the Newton program entering the ball, floating-point execution | 只有数值证据numerical evidence only | 审阅中明确列为未证明;本页也不证明stated as unproved in the review; not proved here either |
改一改条件看看:实验台对任意 、、 判断三个条件,算出 、 与三个界(状态、切线解、梯度),并用本页的格式算出实际误差。
Change the conditions: the lab judges the three conditions for any , and , evaluates , and the three bounds (state, tangent, gradient), and runs the page's scheme for the actual errors.
本附录的结论:审阅里本页能核对的代数与不等式全部成立,没有发现错误;需要保留的只是结论的强弱: 层与半阶 由最后一个节点完全解释,但以未证明的 为前提;梯度的严格界只有 ,而且很保守。数值结果是证据,不是证明。
The outcome of this appendix: every piece of algebra and every inequality of the review that this page can check holds, and no error was found. What needs saying is a matter of degree: the layer and the half-order are fully explained by the last node but rest on the unproved premise , and the rigorous gradient bound is only and very conservative. Numerical results are evidence, not proofs.
附录 C:复现与数据
Appendix C: Reproduction and data
本节实验:页面上的一个数字从哪里来?挑一个——比如附录 B 里 的梯度误差——按图 115 追到产生它的程序、记录它的文件和核对它的检查。
Experiment: where does a number on this page come from? Pick one, say the gradient error of Appendix B at , and follow Figure 115 to the program that produced it, the file that records it and the check that holds it.
本页的数字有三个来源:浏览器里的模型(model.js,正文各节与附录 A 的连续伴随)、附录的数值(figs_g.js),以及独立的 Python 包算出的大网格数据(格点格式的 node/、附录 B 的 paper_dn/,以及格心页的包,后者只用于两种格式的对比;A.7 的对比还在构建时运行格心页自己的模型,并把实验台要用的结果嵌入页面)。三者各有核对;本附录把它们列清楚,并给出复现的命令。
The numbers of this page have three sources: the model in the browser (model.js: the main sections and the continuous adjoint of Appendix A), the numerics of the appendices (figs_g.js), and the large-grid data of independent Python packages (node/ for the node scheme, paper_dn/ for Appendix B, and the package of the cell-centred page, read only for the comparisons of the two schemes; the comparison of A.7 also runs the model of the cell-centred page at build time and embeds what its lab needs). Each is checked; this appendix lists how, and gives the commands to reproduce them.
data.js 在每次构建时核对哈希与标注,构建程序把数字写进页面。中排:浏览器里运行的两份代码(model.js、figs_g.js)与构建程序 build.js。下排:把它们与独立计算核对的检查(箭头指向被核对的对象);check_g.js 与 check_dn.py 还把存储的 JSON 结果逐项重算。data.js checks hashes and labels at every build, and the builder writes the numbers into the page. Middle: the two pieces of code that run in the browser (model.js, figs_g.js) and the builder build.js. Bottom: the checks that hold them to independent computations (the arrows point at what is checked); check_g.js and check_dn.py also recompute the stored JSON results one by one.C.1 Python 包
C.1 The Python packages
格点格式的包位于 python/burgers_viscous/node/,附录 B 的包位于 python/burgers_viscous/paper_dn/,都与页面一起发布,只依赖 NumPy 与 SciPy(附录 B 的检查还要 SymPy)。格心页的包(python/burgers_viscous/ 里的 solve.py 与 examples/)只为两种格式的对比而读,见格心页的附录 C。各文件的作用:
The package of the node scheme is python/burgers_viscous/node/ and that of Appendix B is python/burgers_viscous/paper_dn/; both are published with the page and need only NumPy and SciPy (the checks of Appendix B also use SymPy). The package of the cell-centred page (solve.py and examples/ in python/burgers_viscous/) is read only for the comparisons of the two schemes; see Appendix C of the cell-centred page. What each file does:
| 文件File | 作用Role |
|---|---|
README.md | 方程、格式、命令与限制equation, scheme, commands and limits |
requirements.txt | 依赖:NumPy、SciPydependencies: NumPy, SciPy |
solve_node.py | 正文的格点格式(残差、Newton、前向与伴随、加密研究、完整重算的差分核对):稀疏矩阵实现,与 model.js 互相独立;写出 examples/ 的数据the node-centred model of the main text (residual, Newton, tangent and adjoint, refinement study, FD check with full re-solves): a sparse implementation, independent of model.js; writes the data of examples/ |
test_solver_node.py | 单元测试unit tests |
node/examples/ | 格点格式的存储数据 large_65536.json、refinement_first.json、refinement_none.json、refinement_smooth.json;其中四个另有 CSV 版本(large_65536.csv、refinement_first.csv、refinement_none.csv、refinement_smooth.csv)the stored data of the node scheme large_65536.json, refinement_first.json, refinement_none.json, refinement_smooth.json; four of them also as CSV (large_65536.csv, refinement_first.csv, refinement_none.csv, refinement_smooth.csv) |
paper_dn/benchmark.py | 附录 B 的格点基准(另一个模型)the nodal benchmark of Appendix B (a different model) |
paper_dn/test_dn.py | 格点基准的单元测试unit tests of the nodal benchmark |
paper_dn/check_algebra.py | 精确有理数核对(结果在 algebra_checks.json)exact rational checks (results in algebra_checks.json) |
paper_dn/THEORY_REVIEW.md | 理论审阅(附录 B.7 核对了它)the theory review (audited in Appendix B.7) |
paper_dn/README.md | 格点基准的说明与命令description and commands of the nodal benchmark |
paper_dn/results/ | 格点基准的结果:refinement_q0.json、refinement_one_x.json、refinement_q0.csv、refinement_one_x.csv、refinement_q0_fd.csv、refinement_one_x_fd.csvthe results of the nodal benchmark: refinement_q0.json, refinement_one_x.json, refinement_q0.csv, refinement_one_x.csv, refinement_q0_fd.csv, refinement_one_x_fd.csv |
与第 5 节的规则相比,有一处不同。solve_node.py 遵循第 5 节的 Newton 规则(回溯线搜索、正状态盒、修正量的容差),只多一条:试探步在 低于舍入下限时也被接受,这个下限是机器 epsilon 的 16 倍乘以 。它只对 的完整重算差分有用:没有它,这些重算的线搜索会失败;在 的各个网格上,有没有这一条,解、Newton 历史与差分检验的结果都逐位相同(audit/audit_g.py)。
One deviation from the rule of Section 5. solve_node.py follows the Newton rule of Section 5 (backtracking line search, the positive state box, the tolerance on the correction) with one more clause: a trial step is also accepted when is below the rounding floor, 16 times the machine epsilon times . It matters only for the full re-solves of the finite differences at , which fail in the line search without it; on every mesh the solution, the Newton history and the result of the finite-difference check are bit for bit the same with and without it (audit/audit_g.py).
独立的审计留在源码仓库里,不随页面发布(只有 python/ 作为 python/burgers_viscous/node/ 发布)。本页说“与独立计算核对”时指的就是下面这些文件:
The independent audit stays in the source repository and is not published with the page (only python/ is, as python/burgers_viscous/node/). These are the files that “held to an independent computation” refers to on this page:
| 文件(相对 viscous_node/)File (relative to viscous_node/) | 作用Role |
|---|---|
audit/ref_model.py | 格点格式的稠密独立参考:由规格逐条语句写成,前向与伴随语句手写,复数步长 Jacobian;只用于小 the dense independent reference of the node scheme, written from the specification statement by statement (tangent and reverse statements by hand, complex-step Jacobians); for small only |
audit/make_reference.py、audit/reference.jsonaudit/make_reference.py, audit/reference.json | 写出 check_model.js 读取的测试向量write the test vectors that check_model.js reads |
audit/audit_node.py | 参考对规格的核对(前向、伴随、复数步长、完整重算的有限差分、连续伴随、边界乘子);--sparse 另把稀疏实现与稠密参考逐项比较the checks of the reference against the specification (tangent, adjoint, complex step, finite differences with full re-solves, continuous adjoint, boundary multipliers); --sparse also compares the sparse implementation with the dense reference |
audit/make_facts.py、audit/FACTS.mdaudit/make_facts.py, audit/FACTS.md | 由稠密参考与稀疏实现算出的核实过的数字(阶、乘子、精确节点值的残差)the verified numbers computed from the dense reference and the sparse implementation (orders, multipliers, residuals of the exact nodal values) |
audit/audit_g.py、audit/reference_g.jsonaudit/audit_g.py, audit/reference_g.json | 附录 A、A.7 与 B.4 的独立审计(连续伴随、边界层常数、乘子与梯度的拆分、一阶恒等式、行缩放引理)the independent audit of Appendix A, A.7 and B.4 (continuous adjoint, layer constants, the multipliers and the split of the gradient, the first-order identity, the row-scaling lemma) |
audit_dn/check_dn.py、dn_scheme.pyaudit_dn/check_dn.py, dn_scheme.py | 附录 B 的独立审计:sympy 代数、另一套格点格式、存储的运行、理论审阅the independent audit of Appendix B: sympy algebra, a second nodal scheme, the stored runs, the theory review |
C.2 复现:命令与出处
C.2 Reproduction: commands and provenance
每个结果文件都记录了产生它的命令、程序版本、生成时间、Python/NumPy/SciPy 版本、平台和实现文件的 SHA-256,并标注 mathematical_proof = false。下面的命令直接取自这些记录(解释器路径换成 python3);格点格式的命令在包的 node/ 目录里运行,格点基准的命令在 paper_dn/ 里运行:
Every result file records the command that produced it, the program version, the generation time, the Python/NumPy/SciPy versions, the platform and the SHA-256 of the implementation, and is labelled mathematical_proof = false. The commands below are taken from these records (with the interpreter path replaced by python3); run the commands of the node scheme in the node/ directory of the package and those of the nodal benchmark in paper_dn/:
# node/examples/refinement_none.json (2026-10-03T22:53:59Z)
python3 solve_node.py \
--grids 32,64,128,256,512,1024,2048,4096,8192,16384 --nu 0.01 \
--amplitude 0.2 --phase 0.07 --reconstruction none \
--gradient-check --continuous-adjoint \
--output examples/refinement_none.json
# node/examples/refinement_first.json (2026-10-03T22:54:00Z)
python3 solve_node.py \
--grids 32,64,128,256,512,1024,2048,4096,8192,16384 --nu 0.01 \
--amplitude 0.2 --phase 0.07 --reconstruction first \
--gradient-check --continuous-adjoint \
--output examples/refinement_first.json
# node/examples/refinement_smooth.json (2026-10-03T22:54:01Z)
python3 solve_node.py \
--grids 32,64,128,256,512,1024,2048,4096,8192,16384 --nu 0.01 \
--amplitude 0.2 --phase 0.07 --reconstruction smooth \
--gradient-check --continuous-adjoint \
--output examples/refinement_smooth.json
# node/examples/large_65536.json (2026-10-03T22:54:03Z)
python3 solve_node.py --cells 65536 --nu 0.01 --amplitude 0.2 \
--phase 0.07 --reconstruction none --gradient-check \
--output examples/large_65536.json
# paper_dn/results/refinement_q0.json (2026-10-02T09:05:57Z)
python3 benchmark.py \
--grids 16,32,64,128,256,512,1024,2048,4096,8192,16384 --mu .2 \
--nu .1 --objective q0 --fd-scan --fd-intervals 256 \
--output results/refinement_q0.json
# paper_dn/results/refinement_one_x.json (2026-10-02T09:06:34Z)
python3 benchmark.py \
--grids 16,32,64,128,256,512,1024,2048,4096,8192,16384 --mu .2 \
--nu .05 --objective one_x --fd-scan --fd-intervals 256 \
--output results/refinement_one_x.json| 文件File | 页面上的用途Used in | 生成(UTC)Generated (UTC) | 版本Version | Python / NumPy / SciPyPython / NumPy / SciPy |
|---|---|---|---|---|
node/examples/refinement_none.json | 第 3、4、7、9、12、15–16、18 节,附录 A.7 及其图 108,C.2 的实验台与图 116(无限制器线性,N = 32 … 16384)Sections 3, 4, 7, 9, 12, 15–16 and 18, A.7 with Figure 108, the lab of C.2 and Figure 116 (unlimited linear, N = 32 … 16384) | 2026-10-03T22:53:59Z | 1.0.0 | 3.11.12 / 2.3.1 / 1.16.0 |
node/examples/refinement_first.json | 第 3、4、7、9、15–16、18 节,附录 A.7,C.2 的实验台与图 116(一阶,N = 32 … 16384)Sections 3, 4, 7, 9, 15–16 and 18, A.7, the lab of C.2 and Figure 116 (first order, N = 32 … 16384) | 2026-10-03T22:54:00Z | 1.0.0 | 3.11.12 / 2.3.1 / 1.16.0 |
node/examples/refinement_smooth.json | 第 3、4、7、9、15–16、18 节,附录 A.7,C.2 的实验台与图 116(光滑限制器,N = 32 … 16384)Sections 3, 4, 7, 9, 15–16 and 18, A.7, the lab of C.2 and Figure 116 (smooth limiter, N = 32 … 16384) | 2026-10-03T22:54:01Z | 1.0.0 | 3.11.12 / 2.3.1 / 1.16.0 |
node/examples/large_65536.json | 第 3、9、15–16 节与图 116(N = 65536)Sections 3, 9 and 15–16 and Figure 116 (N = 65536) | 2026-10-03T22:54:03Z | 1.0.0 | 3.11.12 / 2.3.1 / 1.16.0 |
paper_dn/results/refinement_q0.json | 附录 B、C.2 的实验台与图 116(主基准)Appendix B, the lab of C.2 and Figure 116 (primary case) | 2026-10-02T09:05:57Z | 1.0.0 | 3.14.3 / 2.4.6 / 1.18.1 |
paper_dn/results/refinement_one_x.json | 附录 B、C.2 的实验台与图 116(次基准)Appendix B, the lab of C.2 and Figure 116 (secondary case) | 2026-10-02T09:06:34Z | 1.0.0 | 3.14.3 / 2.4.6 / 1.18.1 |
examples/refinement_none.json | 格心页的包,用于两种格式的对比(N = 32 … 16384)the package of the cell-centred page, read for the comparisons of the two schemes (N = 32 … 16384) | 2026-10-02T08:35:50Z | 1.0.0 | 3.14.3 / 2.4.6 / 1.18.1 |
examples/refinement_first.json | 格心页的包,用于两种格式的对比(N = 32 … 4096)the package of the cell-centred page, read for the comparisons of the two schemes (N = 32 … 4096) | 2026-10-02T08:35:51Z | 1.0.0 | 3.14.3 / 2.4.6 / 1.18.1 |
examples/large_65536.json | 格心页的包,用于两种格式的对比(N = 65536)the package of the cell-centred page, read for the comparisons of the two schemes (N = 65536) | 2026-10-02T08:35:53Z | 1.0.0 | 3.14.3 / 2.4.6 / 1.18.1 |
SHA-256(构建时计算):页面读入的实现文件与结果文件。C.1 里的 CSV 文件与日志只是同一数据的另一种写法,本页不读取它们,这里也不计算它们的哈希。data.js 拒绝实现哈希与当前程序不符的数据,所以页面上的数字一定来自这些程序的当前版本。
SHA-256 (computed at build time): the implementation files and the result files the page reads. The CSV files and logs of C.1 are only another way of writing the same data; the page does not read them and no hash is computed for them here. data.js refuses data whose implementation hash differs from the current program, so the numbers on this page come from the current version of these programs.
| 文件(相对 python/burgers_viscous/)File (relative to python/burgers_viscous/) | SHA-256SHA-256 |
|---|---|
examples/large_65536.json | 1241daca39fb2433ca6510c4edf58fac5475abf88d3a294596f2dc73dac5cd17 |
examples/refinement_first.json | 867fa572cfd7972361d20f954bb14905ef2b07bcb00ace4ac12d2fffbf6ff048 |
examples/refinement_none.json | 1bfc1b10868196bfd8527baa5b28d758e2505fdc4b21a0d3b5dd2502a78ecdfa |
node/examples/large_65536.json | e2aa97c827d94747471641e19fb1b5e636e060b0eb90a522f87039fc4599eea0 |
node/examples/refinement_first.json | 25f2c6c6fd4e3ea604afed4e8ddfb7674dff21d6f7bdeae22c53353640421173 |
node/examples/refinement_none.json | 19cc697eb6fd3641b9f4e6a56f4491c32809b11279e3d02b188e07929f574768 |
node/examples/refinement_smooth.json | 29bd3d9e934caf315250f85f2e2e887b940fd9004b6763bcde0588801f5bbd82 |
node/solve_node.py | a656f7615237a80558d7b51524a964509e3ee76aeb8663da12d0b5a9c2dc87ac |
paper_dn/benchmark.py | faa8f226d5f118975413c411a70e6a4758e0042cb04b00c0b3cc3027e0827246 |
paper_dn/results/refinement_one_x.json | 24f1f2159bcabfa1408cc2124dd32bbe4cb733feb3bd298bad45f4ad25e430fd |
paper_dn/results/refinement_q0.json | c49c72beeef091fd130306cbb0b8c31cabecdf81fcfd9156f7154403f3d953ab |
solve.py | 0d36ac3febcd7e74c8f21cf95a1068554264c0982955dbbd6a7ebf9856586e3e |
elapsed_seconds)。格点基准的时间包括两次完整重算的差分求解。这些是实测,不是性能保证;本页不依赖它们。elapsed_seconds of the JSON). The times of the nodal benchmark include the two full re-solves of the finite differences. These are measurements, not performance guarantees; nothing on this page depends on them.在浏览器里复现:选一次存储的计算,实验台用本页的代码重新求解,并逐位比较目标与梯度。
Reproduce in the browser: pick a stored run, the lab re-solves it with this page's code and compares the objective and the gradient digit by digit.
C.3 本页自己的检查
C.3 The page’s own checks
在源码目录 viscous_node/ 里运行(Node.js;Python 3 需要 NumPy、SciPy、SymPy):
Run in the source directory viscous_node/ (Node.js; Python 3 with NumPy, SciPy and SymPy):
# the page and its data (data.js checks every hash and label) node build.js # the browser model against the independent Python reference, and planted faults node check_model.js node check_model.js --selftest # the numerics of the appendices (continuous adjoint, node-scheme multipliers, nodal benchmark, rationals, SHA-256) node check_g.js node check_g.js --selftest # the independent Python audit of the node scheme: the reference against the specification, and the sparse implementation against the reference python3 audit/audit_node.py python3 audit/audit_node.py --sparse python3 audit/make_facts.py # the independent Python audit of the appendices (the node reference of audit/ and the cell-centred reference) python3 audit/audit_g.py # the audit of Appendix B: sympy algebra, a second nodal scheme, the stored runs, the theory review python3 audit_dn/check_dn.py python3 audit_dn/check_dn.py --selftest # the independent Python reference (reference.json is what check_model.js reads) python3 audit/make_reference.py # every gate of the page, with and without the browser node tools/gates.js node tools/gates.js --quick # the built page in headless Chrome: console errors, empty labs, both languages node tools/probe.js # the tests of the Python packages themselves (in the package directories) python3 test_solver_node.py python3 -m unittest -v test_dn.py python3 check_algebra.py --check
C.4 各项说法如何核对
C.4 What is verified, and how
| 页面上的说法Claim on the page | 检查Check | 方式与容差How, tolerance |
|---|---|---|
| 残差、Jacobian、前向与伴随语句(正文模型)residual, Jacobians, tangent and reverse statements (main model) | check_model.js;audit/ref_model.pycheck_model.js; audit/ref_model.py | 与独立 Python 参考及复数步长比较,1e−12;点积检验 1e−14against the independent Python reference and complex step, 1e−12; the dot test to 1e−14 |
| 切线 = 伴随,与完整重算的差分比较tangent = adjoint, against FD with full re-solves | check_model.js;audit/audit_node.pycheck_model.js; audit/audit_node.py | 前向、伴随与反向代码 1e−12;与 的中心差分(完整重算)1e−8,光滑限制器的黏性轴要取 tangent, adjoint and reverse code 1e−12; central differences of with full re-solves 1e−8, where the viscosity axis of the smooth limiter needs |
| 稀疏实现(格点格式的存储数据)the sparse implementation (the stored data of the node scheme) | python/test_solver_node.py;audit/audit_node.py --sparse;check_g.js(A.7)python/test_solver_node.py; audit/audit_node.py --sparse; check_g.js (A.7) | 与稠密参考:Newton 解 1e−12, 1e−10,梯度 1e−11;与 model.js 在共同网格上的 、 见 A.7against the dense reference: Newton solution 1e−12, 1e−10, gradient 1e−11; against model.js on the common meshes, and as in A.7 |
| 连续伴随(附录 A 的谱方法)continuous adjoint (the spectral solver of Appendix A) | check_model.js;check_g.jscheck_model.js; check_g.js | 与 Python 求解器比较;与积分因子法比较 1e−10(相对)against the Python solver; against the integrating factor 1e−10 (relative) |
| 梯度公式与边界项、对偶恒等式gradient formula with its boundary term, the duality identity | check_g.js;audit/audit_g.pycheck_g.js; audit/audit_g.py | 与精确梯度比较 1e−12;切线解与伴随之差 1e−11against the exact gradient 1e−12; tangent against adjoint 1e−11 |
| 边界层的两项展开the two-term layer expansion | check_g.jscheck_g.js | 数值:误差 、剩余 有界(形式推导,非证明)numerical: error, remainder bounded (formal derivation, not a proof) |
| A.7 的乘子 、 与梯度的两部分、一阶恒等式the multipliers , of A.7, the two parts of the gradient, the first-order identity | check_g.js;audit/audit_g.pycheck_g.js; audit/audit_g.py | 与独立 Python 参考(节点与格心)比较 1e−9;恒等式 1e−12against the independent Python references (node and cell-centred) 1e−9; the identity 1e−12 |
| 格点基准的闭式(、、、梯度、)closed forms of the nodal benchmark (, , , gradients, ) | check_dn.py(sympy);check_g.js(BigInt)check_dn.py (sympy); check_g.js (BigInt) | 精确有理数exact rationals |
| 格点基准的格式与存储结果;行缩放的恒等式the nodal benchmark scheme and the stored runs; the row-scaling identity | check_g.js(JavaScript);check_dn.py(Python)check_g.js (JavaScript); check_dn.py (Python) | N = 16 … 16384,1e−9;缩放恒等式 1e−12N = 16 … 16384, 1e−9; the scaling identity 1e−12 |
| 理论审阅的不等式与中间结论inequalities and intermediate claims of the theory review | check_dn.py(D、E 组)check_dn.py (groups D, E) | D 组:每个满足条件的网格;E 组:其中 的网格group D: every grid meeting the conditions; group E: those with |
| 数据的出处与完整性provenance and integrity of the data | data.js(每次构建)data.js (every build) | SHA-256、标注、字段SHA-256, labels, fields |
| 页面能运行、手机上不横向滚动the page runs, no sideways scroll on phones | tools/probe.js;experiments/check_site_widths.jstools/probe.js; experiments/check_site_widths.js | 无控制台错误,375/360/320 pxno console error, 375/360/320 px |
这些检查都是数值证据:它们说明代码与公式一致、与独立计算一致、与存储的数据一致,但不证明非线性问题或格式的存在性、唯一性或收敛定理。本页的证明是附录 A.4、附录 B 的精确代数,以及各节自己写出证明的地方(第 4–7 节:第 4 节一阶的稳定性证明、第 5 节的单步余项恒等式、第 6 节一阶 Jacobian 的 M 矩阵性质与 GS 收敛、第 7 节的精确代数);其余标明为“未证明”的地方,就是未证明。
All these checks are numerical evidence: they show that the code agrees with the formulas, with independent computations and with the stored data, but they do not prove existence, uniqueness or convergence theorems for the nonlinear problem or the scheme. The proofs on this page are those of Appendix A.4, the exact algebra of Appendix B, and those the sections themselves write out (Sections 4–7: the first-order stability proof of Section 4, the one-step remainder identity of Section 5, the M-matrix property and the Gauss–Seidel convergence of the first-order Jacobian in Section 6, the exact algebra of Section 7); whatever is labelled unproved is unproved.
C.5 浏览器模型的源码
C.5 The source of the browser model
下面是页面运行的 model.js 全文,SHA-256 为 68bb86ba7c055c1c002943d31c048d74faf00c3ce55cf3aa2003fa138d4952d2(构建时计算)。实验台可以在你的浏览器里重新计算这个哈希。
Below is the full model.js the page runs, with SHA-256 68bb86ba7c055c1c002943d31c048d74faf00c3ce55cf3aa2003fa138d4952d2 (computed at build time). The lab can recompute this hash in your browser.
model.js 全文(点击展开)model.js in full (click to open)
'use strict';
/* The NODE-centred viscous Burgers model of the page: the residual code, its tangent code and its reverse code, written statement by
statement exactly as the page prints them (Sections 3, 5, 8, 9), and the solves built on them. Nothing here is generated by an AD tool:
viscous_node/audit/ (Python, written independently from SPEC.md: complex step, dot tests) holds every formula of this file to account, and
check_model.js compares this file with it.
Problem. q(u) = u^2/2 - nu u_x, q_x = s(x; D) on (0,1), u(0) = u_in, u(1) = u_out. Manufactured: u*(x; D) = 1 + a sin(2 pi (x - phi)),
D = (a, phi, nu), s = q*_x, u_in = u*(0), u_out = u*(1). Objective J = int_0^1 (1 + x) u dx = 3/2 - a cos(2 pi phi)/(2 pi).
Mesh. N intervals of width h = 1/N; nodes x_j = j h (j = 0..N); U_j = the POINT value at node j (N + 1 unknowns, the two boundary rows are constraints);
dual volume V_j = [x_j - h/2, x_j + h/2] intersected with [0,1]; face f (f = 0..N-1) between node f and node f+1, at x_{f+1/2} = (f + 1/2) h.
Face flux H_f = (qL^2 + qR^2)/4 - (qR - qL) - nu (UR - UL)/h (Rusanov, fixed speed 2, plus the central viscous flux)
Residual R_j = H_j - H_{j-1} - (Q_j - Q_{j-1}) (1 <= j <= N-1), Q_f = q*(x_{f+1/2}; D); R_0 = U_0 - u_in, R_N = U_N - u_out
Objective J_h = sum_j w_j U_j, w_j = W_j (1 + x_j) (the trapezoid rule for (1 + x) u). */
(function (root) {
const TWO_PI = 2 * Math.PI;
const NUM = typeof module !== 'undefined' ? require('../experiments/numerics') : root.EduNumerics;
/* ---------- the exact functions of the manufactured solution (D = [a, phi, nu]) ---------- */
const ustar = (x, D) => 1 + D[0] * Math.sin(TWO_PI * (x - D[1]));
const ustarX = (x, D) => TWO_PI * D[0] * Math.cos(TWO_PI * (x - D[1]));
const qstar = (x, D) => { const u = ustar(x, D); return 0.5 * u * u - D[2] * ustarX(x, D); };
/* tangent of Q = q*(x; D) for the seed Dd and its reverse (accumulated into Db) */
function qstarD(x, D, Dd) {
const z = TWO_PI * (x - D[1]), s = Math.sin(z), c = Math.cos(z), u = 1 + D[0] * s;
return (u * s - D[2] * TWO_PI * c) * Dd[0] + (u * (-TWO_PI * D[0] * c) - D[2] * TWO_PI * TWO_PI * D[0] * s) * Dd[1] + (-TWO_PI * D[0] * c) * Dd[2];
}
function qstarB(x, D, Qb, Db) {
const z = TWO_PI * (x - D[1]), s = Math.sin(z), c = Math.cos(z), u = 1 + D[0] * s;
Db[0] += (u * s - D[2] * TWO_PI * c) * Qb;
Db[1] += (u * (-TWO_PI * D[0] * c) - D[2] * TWO_PI * TWO_PI * D[0] * s) * Qb;
Db[2] += (-TWO_PI * D[0] * c) * Qb;
}
/* boundary data and their tangents: u_in = u*(0), u_out = u*(1) (equal, u* is periodic) */
const uin = D => ustar(0, D), uout = D => ustar(1, D);
const uinD = (D, Dd) => Math.sin(TWO_PI * (0 - D[1])) * Dd[0] + (-TWO_PI * D[0] * Math.cos(TWO_PI * (0 - D[1]))) * Dd[1];
const uoutD = (D, Dd) => Math.sin(TWO_PI * (1 - D[1])) * Dd[0] + (-TWO_PI * D[0] * Math.cos(TWO_PI * (1 - D[1]))) * Dd[1];
const exactObjective = D => 1.5 - D[0] * Math.cos(TWO_PI * D[1]) / TWO_PI;
const exactGradient = D => [-Math.cos(TWO_PI * D[1]) / TWO_PI, D[0] * Math.sin(TWO_PI * D[1]), 0];
/* the exact nodal values u*(x_j): the unknowns are point values, so there is no cell average on this page */
const exactNodal = (x, D) => x.map(xj => ustar(xj, D));
function make(opts) {
const N = opts.N, kind = opts.kind || 'none', eps = opts.eps === undefined ? 0.01 : opts.eps;
if (!Number.isInteger(N) || N < 4 || N > 4096) throw Error('viscous Burgers (nodes): N must be an integer in [4, 4096]');
if (!['first', 'none', 'smooth'].includes(kind)) throw Error('viscous Burgers (nodes): the reconstruction is first, none or smooth');
if (!(eps > 0)) throw Error('viscous Burgers (nodes): eps > 0');
const h = 1 / N, M = N + 1, x = Array.from({length: M}, (_, j) => j * h), xf = Array.from({length: N}, (_, f) => (f + 0.5) * h);
const W = x.map((_, j) => (j === 0 || j === N ? h / 2 : h)), w = x.map((xj, j) => W[j] * (1 + xj));
const zeros = n => new Array(n).fill(0);
const oneSided = kind !== 'first'; // the boundary nodes use the one-sided difference at second order, nothing at first order
/* the slope of an interior node from its two one-sided differences m = U_j - U_{j-1}, p = U_{j+1} - U_j, and its partial derivatives */
function sigma(m, p) {
if (kind === 'first') return 0;
if (kind === 'none') return 0.5 * (m + p);
return m * p * (m + p) / (m * m + p * p + eps * eps);
}
function sigmaD(m, p) {
if (kind === 'first') return [0, 0];
if (kind === 'none') return [0.5, 0.5];
const den = m * m + p * p + eps * eps, num = m * p * (m + p);
return [((2 * m * p + p * p) * den - 2 * m * num) / (den * den), ((m * m + 2 * m * p) * den - 2 * p * num) / (den * den)];
}
const dist = () => h;
/* ---------- statement 1-2 of the residual code: gather, reconstruct (slopes at the nodes), traces and flux of every face ---------- */
function faces(U, D) {
const nu = D[2], s = zeros(M), qL = zeros(N), qR = zeros(N), UL = zeros(N), UR = zeros(N), d = zeros(N), H = zeros(N);
for (let j = 1; j < N; j++) s[j] = sigma(U[j] - U[j - 1], U[j + 1] - U[j]);
s[0] = oneSided ? U[1] - U[0] : 0; s[N] = oneSided ? U[N] - U[N - 1] : 0;
for (let f = 0; f < N; f++) {
qL[f] = U[f] + 0.5 * s[f]; UL[f] = U[f]; qR[f] = U[f + 1] - 0.5 * s[f + 1]; UR[f] = U[f + 1]; d[f] = h;
H[f] = 0.25 * (qL[f] * qL[f] + qR[f] * qR[f]) - (qR[f] - qL[f]) - nu * (UR[f] - UL[f]) / h;
}
return {s, qL, qR, UL, UR, d, H};
}
/* the exact integrated source of the dual volume j: Q_j - Q_{j-1} (zero entries at the two boundary nodes, whose rows are constraints) */
const sourceRows = D => { const S = zeros(M); for (let j = 1; j < N; j++) S[j] = qstar(xf[j], D) - qstar(xf[j - 1], D); return S; };
/* ---------- PRIMAL: the residual code ---------- */
function residual(U, D) {
const F = faces(U, D), S = sourceRows(D), R = zeros(M);
for (let f = 0; f < N; f++) { if (f >= 1) R[f] += F.H[f]; if (f <= N - 2) R[f + 1] -= F.H[f]; }
for (let j = 1; j < N; j++) R[j] -= S[j];
R[0] = U[0] - uin(D); R[N] = U[N] - uout(D);
return R;
}
const objective = U => U.reduce((acc, u, j) => acc + w[j] * u, 0);
/* ---------- TANGENT: residual_d(U, Ud, D, Dd) = A Ud + R_D Dd, in the order of the primal code; tangentFaces returns every intermediate tangent ---------- */
function tangentFaces(U, Ud, D, Dd) {
const nu = D[2], nud = Dd[2], uind = uinD(D, Dd), uoutd = uoutD(D, Dd);
const s = zeros(M), sd = zeros(M);
for (let j = 1; j < N; j++) {
const m = U[j] - U[j - 1], p = U[j + 1] - U[j], md = Ud[j] - Ud[j - 1], pd = Ud[j + 1] - Ud[j], g = sigmaD(m, p);
s[j] = sigma(m, p); sd[j] = g[0] * md + g[1] * pd;
}
s[0] = oneSided ? U[1] - U[0] : 0; sd[0] = oneSided ? Ud[1] - Ud[0] : 0;
s[N] = oneSided ? U[N] - U[N - 1] : 0; sd[N] = oneSided ? Ud[N] - Ud[N - 1] : 0;
const T = {s, sd, qL: zeros(N), qLd: zeros(N), qR: zeros(N), qRd: zeros(N), UL: zeros(N), ULd: zeros(N), UR: zeros(N), URd: zeros(N),
cL: zeros(N), cR: zeros(N), Hd: zeros(N), Qd: zeros(N), Sd: zeros(M), uind, uoutd};
const Rd = zeros(M);
for (let f = 0; f < N; f++) {
const qL = U[f] + 0.5 * s[f], qLd = Ud[f] + 0.5 * sd[f], UL = U[f], ULd = Ud[f];
const qR = U[f + 1] - 0.5 * s[f + 1], qRd = Ud[f + 1] - 0.5 * sd[f + 1], UR = U[f + 1], URd = Ud[f + 1];
const cL = 0.5 * qL + 1, cR = 0.5 * qR - 1;
const Hd = cL * qLd + cR * qRd - nu * (URd - ULd) / h - nud * (UR - UL) / h;
T.qL[f] = qL; T.qLd[f] = qLd; T.qR[f] = qR; T.qRd[f] = qRd; T.UL[f] = UL; T.ULd[f] = ULd; T.UR[f] = UR; T.URd[f] = URd; T.cL[f] = cL; T.cR[f] = cR; T.Hd[f] = Hd;
if (f >= 1) Rd[f] += Hd;
if (f <= N - 2) Rd[f + 1] -= Hd;
}
for (let f = 0; f < N; f++) T.Qd[f] = qstarD(xf[f], D, Dd);
for (let j = 1; j < N; j++) { T.Sd[j] = T.Qd[j] - T.Qd[j - 1]; Rd[j] -= T.Sd[j]; }
Rd[0] = Ud[0] - uind; Rd[N] = Ud[N] - uoutd;
T.Rd = Rd;
return T;
}
const residualD = (U, Ud, D, Dd) => tangentFaces(U, Ud, D, Dd).Rd;
/* ---------- REVERSE: residual_b(U, D, Rb) = (A^T Rb, R_D^T Rb), the statements of residual_d in reverse order, accumulating with += ---------- */
function reverseFaces(U, D, Rb) {
const nu = D[2], Ub = zeros(M), Db = [0, 0, 0];
let uinb = 0, uoutb = 0;
Ub[N] += Rb[N]; uoutb -= Rb[N]; Ub[0] += Rb[0]; uinb -= Rb[0]; // the constraint rows (the last primal statement runs first)
const Qb = zeros(N); // the adjoint of Q_f: the statements R_j -= Q_j - Q_{j-1} run next in reverse
for (let j = N - 1; j >= 1; j--) { Qb[j] -= Rb[j]; Qb[j - 1] += Rb[j]; }
for (let f = N - 1; f >= 0; f--) qstarB(xf[f], D, Qb[f], Db);
const s = zeros(M), sm = zeros(M), sp = zeros(M), sb = zeros(M);
for (let j = 1; j < N; j++) { const m = U[j] - U[j - 1], p = U[j + 1] - U[j], g = sigmaD(m, p); s[j] = sigma(m, p); sm[j] = g[0]; sp[j] = g[1]; }
s[0] = oneSided ? U[1] - U[0] : 0; s[N] = oneSided ? U[N] - U[N - 1] : 0;
const B = {Qb, Hb: zeros(N), qLb: zeros(N), qRb: zeros(N), ULb: zeros(N), URb: zeros(N), sb, nub: zeros(N)};
for (let f = N - 1; f >= 0; f--) {
const qL = U[f] + 0.5 * s[f], UL = U[f], qR = U[f + 1] - 0.5 * s[f + 1], UR = U[f + 1];
const Hb = (f >= 1 ? Rb[f] : 0) - (f <= N - 2 ? Rb[f + 1] : 0);
const cL = 0.5 * qL + 1, cR = 0.5 * qR - 1, qLb = cL * Hb, qRb = cR * Hb, ULb = nu / h * Hb, URb = -nu / h * Hb;
B.Hb[f] = Hb; B.qLb[f] = qLb; B.qRb[f] = qRb; B.ULb[f] = ULb; B.URb[f] = URb; B.nub[f] = -(UR - UL) / h * Hb;
Db[2] += B.nub[f];
Ub[f] += qLb + ULb; sb[f] += 0.5 * qLb;
Ub[f + 1] += qRb + URb; sb[f + 1] += -0.5 * qRb;
}
for (let j = N - 1; j >= 1; j--) { const mb = sm[j] * sb[j], pb = sp[j] * sb[j]; Ub[j] += mb - pb; Ub[j - 1] += -mb; Ub[j + 1] += pb; }
if (oneSided) { Ub[N] += sb[N]; Ub[N - 1] += -sb[N]; Ub[1] += sb[0]; Ub[0] += -sb[0]; }
const zin = TWO_PI * (0 - D[1]), zout = TWO_PI * (1 - D[1]);
Db[0] += Math.sin(zin) * uinb + Math.sin(zout) * uoutb;
Db[1] += -TWO_PI * D[0] * (Math.cos(zin) * uinb + Math.cos(zout) * uoutb);
B.Ub = Ub; B.Db = Db; B.uinb = uinb; B.uoutb = uoutb;
return B;
}
const residualB = (U, D, Rb) => { const B = reverseFaces(U, D, Rb); return {Ub: B.Ub, Db: B.Db}; };
/* ---------- the Jacobians by columns of the tangent code ---------- */
function jacState(U, D) {
const A = Array.from({length: M}, () => zeros(M)), z = [0, 0, 0];
for (let j = 0; j < M; j++) { const e = zeros(M); e[j] = 1; const col = residualD(U, e, D, z); for (let i = 0; i < M; i++) A[i][j] = col[i]; }
return A;
}
function jacDesign(U, D) {
const z = zeros(M), cols = [0, 1, 2].map(k => residualD(U, z, D, [0, 1, 2].map(m => (m === k ? 1 : 0))));
return Array.from({length: M}, (_, i) => [cols[0][i], cols[1][i], cols[2][i]]);
}
const transpose = A => A[0].map((_, j) => A.map(r => r[j]));
const infnorm = v => v.reduce((m, z) => Math.max(m, Math.abs(z)), 0);
/* ---------- the primal solve: Newton with a backtracking line search and the positive box 0.1 < U < 1.9 ---------- */
function solve(D, o = {}) {
let U = o.U0 ? o.U0.slice() : x.map(xj => ustar(xj, D)); // the exact nodal values: an initial guess only
const history = [], states = [], tol = o.tol === undefined ? 1e-13 : o.tol, maxit = o.maxit || 35;
for (let it = 0; it < maxit; it++) {
const R = residual(U, D), A = jacState(U, D), dU = NUM.solve(NUM.factor(A), R.map(v => -v));
const rn = infnorm(R), dn = infnorm(dU);
history.push({iteration: it, residual: rn, correction: dn}); states.push(U.slice());
if (dn <= tol || rn < 1e-15) return {U, history, states, converged: true};
let step = 1, ok = false;
for (let k = 0; k < 25; k++) {
const T = U.map((u, i) => u + step * dU[i]);
if (Math.min(...T) > 0.1 && Math.max(...T) < 1.9 && infnorm(residual(T, D)) < (1 - 1e-4 * step) * rn) { U = T; ok = true; break; }
step *= 0.5;
}
if (!ok) return {U, history, states, converged: false, reason: 'line search failed'};
}
return {U, history, states, converged: false, reason: 'iteration limit'};
}
/* ---------- the tangent solves A v_k = -R_D e_k and the adjoint solve A^T psi = -g (g = w) ---------- */
function sensitivities(U, D) {
const A = jacState(U, D), RD = jacDesign(U, D), FA = NUM.factor(A), FAT = NUM.factor(transpose(A));
const v = [0, 1, 2].map(k => NUM.solve(FA, RD.map(r => -r[k])));
const psi = NUM.solve(FAT, w.map(z => -z));
const gradTangent = v.map(vk => vk.reduce((acc, z, i) => acc + w[i] * z, 0));
const gradAdjoint = [0, 1, 2].map(k => RD.reduce((acc, r, i) => acc + psi[i] * r[k], 0));
return {A, RD, v, psi, gradTangent, gradAdjoint};
}
/* ---------- complex step: the SAME residual code with complex arithmetic (real and imaginary parts as two arrays); only the smooth operations the
residual uses occur: + - * / and sin, cos of a complex argument ---------- */
function residualComplex(Ure, Uim, Dre, Dim) {
const C = {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] * b[0] + b[1] * b[1]; return [(a[0] * b[0] + a[1] * b[1]) / d, (a[1] * b[0] - a[0] * b[1]) / d]; },
sc: (t, a) => [t * a[0], t * a[1]], k: t => [t, 0],
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])]};
const U = Ure.map((r, i) => [r, Uim[i]]), a = [Dre[0], Dim[0]], phi = [Dre[1], Dim[1]], nu = [Dre[2], Dim[2]];
const ustarC = xx => C.add(C.k(1), C.mul(a, C.sin(C.sc(TWO_PI, C.sub(C.k(xx), phi)))));
const qstarC = xx => { const z = C.sc(TWO_PI, C.sub(C.k(xx), phi)), u = C.add(C.k(1), C.mul(a, C.sin(z))), ux = C.mul(C.sc(TWO_PI, a), C.cos(z)); return C.sub(C.sc(0.5, C.mul(u, u)), C.mul(nu, ux)); };
const sigmaC = (m, p) => {
if (kind === 'first') return C.k(0);
if (kind === 'none') return C.sc(0.5, C.add(m, p));
return C.div(C.mul(C.mul(m, p), C.add(m, p)), C.add(C.add(C.mul(m, m), C.mul(p, p)), C.k(eps * eps)));
};
const s = Array.from({length: M}, () => C.k(0));
for (let j = 1; j < N; j++) s[j] = sigmaC(C.sub(U[j], U[j - 1]), C.sub(U[j + 1], U[j]));
if (oneSided) { s[0] = C.sub(U[1], U[0]); s[N] = C.sub(U[N], U[N - 1]); }
const R = Array.from({length: M}, () => C.k(0));
for (let f = 0; f < N; f++) {
const qL = C.add(U[f], C.sc(0.5, s[f])), UL = U[f], qR = C.sub(U[f + 1], C.sc(0.5, s[f + 1])), UR = U[f + 1];
const H = C.sub(C.sub(C.sc(0.25, C.add(C.mul(qL, qL), C.mul(qR, qR))), C.sub(qR, qL)), C.sc(1 / h, C.mul(nu, C.sub(UR, UL))));
if (f >= 1) R[f] = C.add(R[f], H);
if (f <= N - 2) R[f + 1] = C.sub(R[f + 1], H);
}
for (let j = 1; j < N; j++) R[j] = C.sub(R[j], C.sub(qstarC(xf[j]), qstarC(xf[j - 1])));
R[0] = C.sub(U[0], ustarC(0)); R[N] = C.sub(U[N], ustarC(1));
return {re: R.map(z => z[0]), im: R.map(z => z[1])};
}
return {N, M, h, kind, eps, x, xf, w, W, dist, sigma, sigmaD, faces, sourceRows, residual, residualD, residualB, tangentFaces, reverseFaces, objective, jacState, jacDesign, transpose, solve, sensitivities, residualComplex,
exactNodal: D => exactNodal(x, D), exactAverage: D => exactNodal(x, D), infnorm};
}
/* ---------- the continuous adjoint, by an independent method: Chebyshev collocation of -u lam' - nu lam'' = -(1 + x), lam(0) = lam(1) = 0 ---------- */
function continuousAdjoint(D, M = 200) {
const xc = Array.from({length: M + 1}, (_, k) => Math.cos(Math.PI * k / M)), c = xc.map((_, k) => (k === 0 || k === M ? 2 : 1) * (k % 2 ? -1 : 1));
const Dm = Array.from({length: M + 1}, () => new Array(M + 1).fill(0));
for (let i = 0; i <= M; i++) { let row = 0; for (let j = 0; j <= M; j++) if (i !== j) { Dm[i][j] = (c[i] / c[j]) / (xc[i] - xc[j]); row += Dm[i][j]; } Dm[i][i] = -row; }
const x = xc.map(z => (z + 1) / 2), d1 = Dm.map(r => r.map(z => 2 * z));
const d2 = d1.map((r, i) => r.map((_, j) => r.reduce((acc, z, k) => acc + z * d1[k][j], 0)));
const L = d1.map((r, i) => r.map((z, j) => -ustar(x[i], D) * z - D[2] * d2[i][j])), rhs = x.map(z => -(1 + z));
for (const i of [0, M]) { L[i] = new Array(M + 1).fill(0); L[i][i] = 1; rhs[i] = 0; }
const lam = NUM.solve(NUM.factor(L), rhs), lamX = d1.map(r => r.reduce((acc, z, j) => acc + z * lam[j], 0));
/* barycentric interpolation of lam and lam_x at arbitrary points */
const bw = xc.map((_, k) => (k === 0 || k === M ? 0.5 : 1) * (k % 2 ? -1 : 1));
const interp = (f, z) => { let num = 0, den = 0; for (let k = 0; k <= M; k++) { const dz = z - x[k]; if (Math.abs(dz) < 1e-15) return f[k]; const t = bw[k] / dz; num += t * f[k]; den += t; } return num / den; };
return {x, lam, lamX, at: z => interp(lam, z), atX: z => interp(lamX, z)};
}
const api = {make, ustar, ustarX, qstar, qstarD, uin, uout, exactObjective, exactGradient, exactNodal, exactAverage: (x, h, D) => exactNodal(x, D), continuousAdjoint, TWO_PI,
DEFAULT_D: [0.2, 0.07, 0.01], DEFAULTS: {N: 8, kind: 'none'}};
if (typeof module !== 'undefined') module.exports = api; else root.ViscousBurgers = api;
})(typeof window !== 'undefined' ? window : globalThis);