Using Automatic Differentiation for Adjoint CFD Code...

Preview:

Citation preview

Using Automatic Differentiationfor Adjoint CFD Code Development

Mike Giles Devendra Ghate Mihai Duta

Oxford University Computing Laboratory

post-SAROD Workshop – p. 1/24

Overview

why discrete adjoint?

automatic differentiation

an airfoil code example

post-SAROD Workshop – p. 2/24

Discrete Adjoints

Adjoint methods are very efficient for obtaining thesensitivity of one output (objective function) to many inputs(design parameters)

There are two adjoint approaches:

continuous: construct adjoint PDE and then discretise

discrete: discretise original nonlinear PDE, thenlinearise and use its adjoint/transpose

Both approximate the true gradient of the output – lattergives the gradient of the discrete output but this consistencyis unnecessary for many optimisers.

post-SAROD Workshop – p. 3/24

Discrete Adjoints

I prefer discrete adjoints because:

clear prescriptive process for constructing the discreteadjoint equations and boundary conditions;

usually guaranteed to get same iterative convergencerate as original nonlinear code;

Automatic Differentiation can simplify the developmentof the adjoint CFD code.

post-SAROD Workshop – p. 4/24

Discrete Adjoints

Suppose an input α leads to an output J :

α −→ X −→ U −→ J.

Defining α, X, U , J to be derivatives with respect to α,

X =∂X

∂αα, U =

∂U

∂XX, J =

∂J

∂UU,

=⇒ J =∂J

∂U

∂U

∂X

∂X

∂αα,

Note this calculation goes forward:

α −→ X −→ U −→ J ,

post-SAROD Workshop – p. 5/24

Discrete Adjoints

Now, defining α,X,U, J to be derivatives of J with respectto α,X,U, J ,

αdef=

(∂J

∂α

)T

=

(∂J

∂X

∂X

∂α

)T

=

(∂X

∂α

)T

X,

and similarly X =

(∂U

∂X

)T

U, U =

(∂U

∂J

)T

J,

=⇒ α =

(∂X

∂α

)T (∂U

∂X

)T (∂U

∂J

)T

J.

Note this calculation goes backward:

α ←− X ←− U ←− J.post-SAROD Workshop – p. 6/24

Automatic Differentiation

AD views each floating point operation as a separate stage:

X0 X1. . . XN−1 XN

- - - - $?

∂J/∂XN

%�

∂X1

∂X0

∂X2

∂X1

∂XN

∂XN−1

? ? ?

� � � �X0 X1. . . XN−1 XN

Note requirement to store data on forward pass in order touse partial derivatives on reverse pass.

post-SAROD Workshop – p. 7/24

Automatic Differentiation

To create key parts of our linear and adjoint CFD codes,we use AD software called Tapenade:

developed at INRIA by Laurent Hascöet and ValeriePascual

uses source code transformation, takes a Fortransubroutine as input and generates a new Fortransubroutine as output

given a subroutine which computes f(u), it can createroutines to evaluate

f =∂f

∂uu (forward mode)

u =

(∂f

∂u

)T

f (reverse mode)

post-SAROD Workshop – p. 8/24

Discrete Adjoints

Steady discrete CFD equations

N(U,X) = 0.

are often solved by iteration

Un+1 = Un − P (Un, X)N(Un, X),

The linearised equations

L U + N = 0, L ≡∂N

∂U, N =

∂N

∂XX,

can then be solved by the iteration

Un+1 = Un − P(

L Un + N)

.

post-SAROD Workshop – p. 9/24

Discrete Adjoints

SinceU = −L−1N ,

the adjoint equation is

N = − (LT )−1U =⇒ LTN + U = 0,

which can be solved iteratively using

Nn+1

= Nn− P T

(

LT Nn

+ U)

.

Written paper explains that P T corresponds to sametime-marching algorithm in simple cases, and we useAD to get code to evaluate LTN and U (and alsoL U and N for linear code).

post-SAROD Workshop – p. 10/24

Airfoil Code

Very simple 2D airfoil code:

cell-centred, unstructured quadrilateral grid

inviscid fluxes plus simple numerical smoothing

simple predictor/corrector time-marching with localtimesteps

Starting with a nonlinear solver, we use AD to generate thekey bits of both linear and adjoint codes.

post-SAROD Workshop – p. 11/24

Airfoil Code

Fortran files:

airfoil.Fnonlinear code

air_lin.Flinear code

air_adj.Fadjoint code

testlinadj.Fvalidation code

input.Finput/output routines

routines.Fnonlinear routines

post-SAROD Workshop – p. 12/24

Airfoil Code

Nonlinear routines:

TIME_CELL:computes the local area/timestep for a single cell

FLUX_FACE:computes the flux through a single regular face

FLUX_WALL:computes the flux for a single airfoil wall face

LIFT_WALL:computes lift contribution from a single airfoil wall face

post-SAROD Workshop – p. 13/24

Airfoil Code#ifdef COMPLEX

subroutine Cflux_wall(x1,x2,q,res)#else

subroutine flux_wall(x1,x2,q,res)#endifcc compute momentum flux from an individual wall facec

implicit none#include "const.inc"c

integer nc#ifdef COMPLEX

complex*16#else

real*8#endif

& x1(2),x2(2),q(4),res(4),& dx,dy, ri,u,v,p

cdx = x1(1) - x2(1)dy = x1(2) - x2(2)

cri = 1.d0/q(1)u = ri*q(2)v = ri*q(3)p = gm1*(ri*q(4) - 0.5*(u**2+v**2))

cres(2) = res(2) - p*dyres(3) = res(3) + p*dx

creturnend post-SAROD Workshop – p. 14/24

Airfoil Code

Different AD-generated versions of wall flux routine:

FLUX_WALL(x1,x2,q,res)

FLUX_WALL_D(x1,x2,q,qd,res,resd)

FLUX_WALL_DX(x1,x1d,x2,x2d,q,qd,res,resd)

FLUX_WALL_B(x1,x2,q,qb,res,resb)

FLUX_WALL_BX(x1,x1b,x2,x2b,q,qb,res,resb)

post-SAROD Workshop – p. 15/24

Airfoil Code

Part of Makefile:flux_wall_d.o: routines.F

${GCC} -E -C -P routines.F > routines.f;${TPN} -forward \

-head flux_wall \-output flux_wall \-vars "q res" \-outvars "q res" \routines.f;

${FC} ${FFLAGS} -c flux_wall_d.f;/bin/rm routines.f flux_wall_d.f *.msg

flux_wall_dx.o: routines.F${GCC} -E -C -P routines.F > routines.f;${TPN} -forward \

-head flux_wall \-output flux_wall \-vars "x1 x2 q res" \-outvars "x1 x2 q res" \-difffuncname "_dx" \routines.f;

${FC} ${FFLAGS} -c flux_wall_dx.f;/bin/rm routines.f flux_wall_dx.f *.msg

post-SAROD Workshop – p. 16/24

Airfoil Code

Part of Makefile:flux_wall_b.o: routines.F

${GCC} -E -C -P routines.F > routines.f;${TPN} -backward \

-head flux_wall \-output flux_wall \-vars "q res" \-outvars "q res" \routines.f;

${FC} ${FFLAGS} -c flux_wall_b.f;/bin/rm routines.f flux_wall_b.f *.msg

flux_wall_bx.o: routines.F${GCC} -E -C -P routines.F > routines.f;${TPN} -backward \

-head flux_wall \-output flux_wall \-vars "x1 x2 q res" \-outvars "x1 x2 q res" \-difffuncname "_bx" \routines.f;

${FC} ${FFLAGS} -c flux_wall_bx.f;/bin/rm routines.f flux_wall_bx.f *.msg

post-SAROD Workshop – p. 17/24

Nonlinear Code

define grid and initialise flow field

begin predictor/corrector time-marching loop

loop over cells to calculate timestep

call TIME_CELL

loop over regular faces to calculate flux

call FLUX_FACE

loop over airfoil faces to calculate flux

call FLUX_WALL

loop over cells to update solution

end time-marching loop

calculate lift

loop over boundary faces

call LIFT_WALL

post-SAROD Workshop – p. 18/24

Linear Code

define grid and initialise flow fielddefine grid perturbation

loop over cells -- perturbed timestepcall TIME_CELL_DX

loop over regular faces -- perturbed fluxcall FLUX_FACE_DX

loop over airfoil faces -- perturbed fluxcall FLUX_WALL_DX

begin predictor/corrector time-marching looploop over cells to calculate timestep

call TIME_CELL_Dloop over regular faces to calculate flux

call FLUX_FACE_Dloop over airfoil faces to calculate flux

call FLUX_WALL_Dloop over cells to update solution

end time-marching loop

calculate liftloop over boundary faces -- perturbed lift

call LIFT_WALL_DX

post-SAROD Workshop – p. 19/24

Adjoint Code

define grid and initialise flow field

calculate adjoint lift sensitivityloop over boundary faces

call LIFT_WALL_BX

begin predictor/corrector time-marching looploop over airfoil faces -- adjoint flux

call FLUX_WALL_Bloop over regular faces -- adjoint flux

call FLUX_FACE_Bloop over cells -- adjoint timestep calc

call TIME_CELL_Bloop over cells to update solution

end time-marching loop

loop over airfoil faces -- adjoint fluxcall FLUX_WALL_BX

loop over regular faces -- adjoint fluxcall FLUX_FACE_BX

loop over cells -- adjoint timestep calccall TIME_CELL_BX

loop over nodes to evaluate lift sensitivity

post-SAROD Workshop – p. 20/24

Airfoil Code

Validation is very important – can be done at various levelsstarting with the consistency of the individual nonlinear,linear and adjoint routines.

The “complex variable trick” gives

limε→0

I {f(u+iεu)}

ε=

∂f

∂uu

so the linear code can be checked against the nonlinear.

Also, by taking different unit vectors u can get∂f

∂u, and

compare to(

∂f

∂u

)T

from adjoint routine.

post-SAROD Workshop – p. 21/24

Airfoil Code

The next level is iterative equivalence between the linearand adjoint codes.

If both codes are initialised to zero (U0 = N0

= 0) then thepaper explains that

(Nn)T N

︸ ︷︷ ︸

adjoint code

= UTUn

︸ ︷︷ ︸

linear code

i.e. after the same number of iterations n, both codesshould give the same value for the linear sensitivity of theoutput (lift) to one input (angle of attack).

post-SAROD Workshop – p. 22/24

Airfoil Code

Final check is between linear sensitivity and nonlinear finitedifference

10−10

10−8

10−6

10−4

10−2

100

10−8

10−6

10−4

10−2

∆ α

rela

tive

erro

r

O(∆α2) error due to finite difference step size ∆α

O(ε/∆α) error due to finite machine precision εpost-SAROD Workshop – p. 23/24

Conclusions

I think discrete adjoints are preferable to continuousadjoints for practical reasons

clear prescriptive process;same iterative convergence rate;AD can simplify code development;

AD tools are now quite mature, and I personallyrecommend Tapenade

validation is also very important and can be done at anumber of different levels to give confidence in theadjoint code

finally, there will be a “hands-on” tutorial at ADA onWednesday

post-SAROD Workshop – p. 24/24

Recommended