Crank-Nicolson Unsteady Adjoint – EXPERIMENTAL, INCORRECT CODE
Current status
As of mid-September, 2017, development of PDESolver's unsteady adjoint has been tabled for the time being. The code is preserved, and is accessible with the run_flag
of 660. It runs with no errors, and produces a solution that qualititatively demonstrates properties of the correct unsteady adjoint. However, a test sensitivity check does not pass.
Notation for this section:
R is the residual
A is the design variable: the amplitude of a sin function that is an exact solution to the advection equation: \begin{equation} u = A \sin(-x + \omega t) \end{equation}
J is the objective function: \begin{equation} J = \int_{\Gamma_1} u^2 d\Gamma_1 \end{equation}
\Gamma_1 is the right domain boundary in a square domain
Some notes about current status that may be of assistance to further development or debugging:
dRdA makes physical sense, and FD and CS methods match
dJdu FD, CS, and analytical derivative match
J is verified by setting a polynomial solution for which SBP should be exact.
loaded checkpoints match forward solve
norm of Jacobian calculated from loaded checkpoint matches forward solve's
time stepping bookkeeping in forward and reverse appears correct
Adjoint initial condition equation appears to match the below derivation, as far as code-reading can show
While the test sensitivity check is incorrect at all time steps, the fact that it is incorrect at the adjoint initial condition indicates that the bug manifests itself before the main reverse-sweep time-stepping loop.
The test sensitivity check being performed is the comparison between these two derivatives:
\begin{equation} \frac{d J}{d A} &= \frac{\partial J}{\partial u} \frac{\partial u}{\partial A} \ \end{equation}
\begin{equation} \frac{d J}{d A} &= \psi^T \left( - \frac{\partial R}{\partial A}\right) \end{equation}
For the above, note that: \begin{equation} \frac{\partial J}{\partial A} = 0 \end{equation}
Unsteady adjoint derivation
The unsteady adjoint derivation starts with the generic Lagrangian equation:
\begin{equation} \mathcal{L}(u, \psi) = \psi^T R(u) + J(u) \end{equation}
In the discrete context of CN, all of these variables are global-in-time. That is, the adjoint vector contains the adjoint at time step 1 concatenated with the adjoint at time step 2, and so on, until time step $n$. Therefore, in this document we will rewrite the Lagrangian using bolded symbols to indicate that a vector or matrix is global-in-time, as there will also be corresponding variables specific to a particular time step:
\begin{equation} \boldsymbol{\mathcal{L}}(\boldsymbol{u}, \boldsymbol{\psi}) = \boldsymbol{\psi}^T \boldsymbol{R}(\boldsymbol{u}) + \boldsymbol{J}(\boldsymbol{u}) \end{equation}
The global-in-time residual discretized according to the Crank-Nicolson method is:
The global-in-time adjoint vector is:
Note that each time step's adjoint variable is a vector of length equal to the number of degrees of freedom in the mesh. And finally, the global-in-time objective function vector is:
Therefore, the full discrete Lagrangian is:
Taking the derivative of the Lagrangian with respect to the state at step $i$ yields, for values of i not equal to 0 or n:
Or, rearranging:
Initial Condition
The derivative of the Lagrangian with respect to the state at the final step $i = n$ is:
Therefore, the value of the adjoint at time step n, which is the initial condition for the reverse sweep, is:
Direct Solve
The method of performing a direct solve to advance the CN reverse sweep (as opposed to using Newton's method to converge each time step) starts with the restatement of the derivative of the Lagrangian at time step $i$:
Rearranging:
Grouping terms to isolate $\psi_i$:
Solving for $\psi_i$:
Therefore, $\psi_i$ is a function of 1) the Jacobian of the primal solution at step $i$, which is loaded from checkpointed data, 2) the derivative of the objective function with respect to the state, at step $i$, and 3) the adjoint solution at time step $i+1$. The adjoint solution sweep is thus stepped backwards in time, starting at time step $n$.
Checkpointing
Currently, all time steps are checkpointed. Eventually, Revolve will be implemented, for which a separate Julia package has been developed. See here for the publication discussing the Revolve algorithm.
Global-in-time Jacobian
For reference, the structure of the global-in-time Jacobian is shown here. It should never be formed except in the course of debugging very simple use cases, but it can be helpful for visualizing the matrix form of CN for all space and time.
(Work in progress)