xref: /petsc/src/ts/tutorials/ex23fwdadj.c (revision 58d68138c660dfb4e9f5b03334792cd4f2ffd7cc)
1 static char help[] = "A toy example for testing forward and adjoint sensitivity analysis of an implicit ODE with a paramerized mass matrice.\n";
2 
3 /*
4   This example solves the simple ODE
5     c x' = b x, x(0) = a,
6   whose analytical solution is x(T)=a*exp(b/c*T), and calculates the derivative of x(T) w.r.t. c (by default) or w.r.t. b (can be enabled with command line option -der 2).
7 
8 */
9 
10 #include <petscts.h>
11 
12 typedef struct _n_User *User;
13 struct _n_User {
14   PetscReal a;
15   PetscReal b;
16   PetscReal c;
17   /* Sensitivity analysis support */
18   PetscInt  steps;
19   PetscReal ftime;
20   Mat       Jac;  /* Jacobian matrix */
21   Mat       Jacp; /* JacobianP matrix */
22   Vec       x;
23   Mat       sp;        /* forward sensitivity variables */
24   Vec       lambda[1]; /* adjoint sensitivity variables */
25   Vec       mup[1];    /* adjoint sensitivity variables */
26   PetscInt  der;
27 };
28 
29 static PetscErrorCode IFunction(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) {
30   User               user = (User)ctx;
31   const PetscScalar *x, *xdot;
32   PetscScalar       *f;
33 
34   PetscFunctionBeginUser;
35   PetscCall(VecGetArrayRead(X, &x));
36   PetscCall(VecGetArrayRead(Xdot, &xdot));
37   PetscCall(VecGetArrayWrite(F, &f));
38   f[0] = user->c * xdot[0] - user->b * x[0];
39   PetscCall(VecRestoreArrayRead(X, &x));
40   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
41   PetscCall(VecRestoreArrayWrite(F, &f));
42   PetscFunctionReturn(0);
43 }
44 
45 static PetscErrorCode IJacobian(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, void *ctx) {
46   User               user     = (User)ctx;
47   PetscInt           rowcol[] = {0};
48   PetscScalar        J[1][1];
49   const PetscScalar *x;
50 
51   PetscFunctionBeginUser;
52   PetscCall(VecGetArrayRead(X, &x));
53   J[0][0] = user->c * a - user->b * 1.0;
54   PetscCall(MatSetValues(B, 1, rowcol, 1, rowcol, &J[0][0], INSERT_VALUES));
55   PetscCall(VecRestoreArrayRead(X, &x));
56 
57   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
58   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
59   if (A != B) {
60     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
61     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
62   }
63   PetscFunctionReturn(0);
64 }
65 
66 static PetscErrorCode IJacobianP(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal shift, Mat A, void *ctx) {
67   User               user  = (User)ctx;
68   PetscInt           row[] = {0}, col[] = {0};
69   PetscScalar        J[1][1];
70   const PetscScalar *x, *xdot;
71   PetscReal          dt;
72 
73   PetscFunctionBeginUser;
74   PetscCall(VecGetArrayRead(X, &x));
75   PetscCall(VecGetArrayRead(Xdot, &xdot));
76   PetscCall(TSGetTimeStep(ts, &dt));
77   if (user->der == 1) J[0][0] = xdot[0];
78   if (user->der == 2) J[0][0] = -x[0];
79   PetscCall(MatSetValues(A, 1, row, 1, col, &J[0][0], INSERT_VALUES));
80   PetscCall(VecRestoreArrayRead(X, &x));
81 
82   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
83   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
84   PetscFunctionReturn(0);
85 }
86 
87 int main(int argc, char **argv) {
88   TS             ts;
89   PetscScalar   *x_ptr;
90   PetscMPIInt    size;
91   struct _n_User user;
92   PetscInt       rows, cols;
93 
94   PetscFunctionBeginUser;
95   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
96 
97   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
98   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "This is a uniprocessor example only!");
99 
100   user.a     = 2.0;
101   user.b     = 4.0;
102   user.c     = 3.0;
103   user.steps = 0;
104   user.ftime = 1.0;
105   user.der   = 1;
106   PetscCall(PetscOptionsGetInt(NULL, NULL, "-der", &user.der, NULL));
107 
108   rows = 1;
109   cols = 1;
110   PetscCall(MatCreate(PETSC_COMM_WORLD, &user.Jac));
111   PetscCall(MatSetSizes(user.Jac, PETSC_DECIDE, PETSC_DECIDE, 1, 1));
112   PetscCall(MatSetFromOptions(user.Jac));
113   PetscCall(MatSetUp(user.Jac));
114   PetscCall(MatCreateVecs(user.Jac, &user.x, NULL));
115 
116   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
117   PetscCall(TSSetType(ts, TSBEULER));
118   PetscCall(TSSetIFunction(ts, NULL, IFunction, &user));
119   PetscCall(TSSetIJacobian(ts, user.Jac, user.Jac, IJacobian, &user));
120   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP));
121   PetscCall(TSSetMaxTime(ts, user.ftime));
122 
123   PetscCall(VecGetArrayWrite(user.x, &x_ptr));
124   x_ptr[0] = user.a;
125   PetscCall(VecRestoreArrayWrite(user.x, &x_ptr));
126   PetscCall(TSSetTimeStep(ts, 0.001));
127 
128   /* Set up forward sensitivity */
129   PetscCall(MatCreate(PETSC_COMM_WORLD, &user.Jacp));
130   PetscCall(MatSetSizes(user.Jacp, PETSC_DECIDE, PETSC_DECIDE, rows, cols));
131   PetscCall(MatSetFromOptions(user.Jacp));
132   PetscCall(MatSetUp(user.Jacp));
133   PetscCall(MatCreateDense(PETSC_COMM_WORLD, PETSC_DECIDE, PETSC_DECIDE, rows, cols, NULL, &user.sp));
134   PetscCall(MatZeroEntries(user.sp));
135   PetscCall(TSForwardSetSensitivities(ts, cols, user.sp));
136   PetscCall(TSSetIJacobianP(ts, user.Jacp, IJacobianP, &user));
137 
138   PetscCall(TSSetSaveTrajectory(ts));
139   PetscCall(TSSetFromOptions(ts));
140 
141   PetscCall(TSSolve(ts, user.x));
142   PetscCall(TSGetSolveTime(ts, &user.ftime));
143   PetscCall(TSGetStepNumber(ts, &user.steps));
144   PetscCall(VecGetArray(user.x, &x_ptr));
145   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n ode solution %g\n", (double)PetscRealPart(x_ptr[0])));
146   PetscCall(VecRestoreArray(user.x, &x_ptr));
147   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n analytical solution %g\n", (double)(user.a * PetscExpReal(user.b / user.c * user.ftime))));
148 
149   if (user.der == 1) { PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n analytical derivative w.r.t. c %g\n", (double)(-user.a * user.ftime * user.b / (user.c * user.c) * PetscExpReal(user.b / user.c * user.ftime)))); }
150   if (user.der == 2) { PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n analytical derivative w.r.t. b %g\n", (double)(user.a * user.ftime / user.c * PetscExpReal(user.b / user.c * user.ftime)))); }
151   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n forward sensitivity:\n"));
152   PetscCall(MatView(user.sp, PETSC_VIEWER_STDOUT_WORLD));
153 
154   PetscCall(MatCreateVecs(user.Jac, &user.lambda[0], NULL));
155   /* Set initial conditions for the adjoint integration */
156   PetscCall(VecGetArrayWrite(user.lambda[0], &x_ptr));
157   x_ptr[0] = 1.0;
158   PetscCall(VecRestoreArrayWrite(user.lambda[0], &x_ptr));
159   PetscCall(MatCreateVecs(user.Jacp, &user.mup[0], NULL));
160   PetscCall(VecGetArrayWrite(user.mup[0], &x_ptr));
161   x_ptr[0] = 0.0;
162   PetscCall(VecRestoreArrayWrite(user.mup[0], &x_ptr));
163 
164   PetscCall(TSSetCostGradients(ts, 1, user.lambda, user.mup));
165   PetscCall(TSAdjointSolve(ts));
166 
167   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n adjoint sensitivity:\n"));
168   PetscCall(VecView(user.mup[0], PETSC_VIEWER_STDOUT_WORLD));
169 
170   PetscCall(MatDestroy(&user.Jac));
171   PetscCall(MatDestroy(&user.sp));
172   PetscCall(MatDestroy(&user.Jacp));
173   PetscCall(VecDestroy(&user.x));
174   PetscCall(VecDestroy(&user.lambda[0]));
175   PetscCall(VecDestroy(&user.mup[0]));
176   PetscCall(TSDestroy(&ts));
177 
178   PetscCall(PetscFinalize());
179   return 0;
180 }
181 
182 /*TEST
183 
184     test:
185       args: -ts_type beuler
186 
187     test:
188       suffix: 2
189       args: -ts_type cn
190       output_file: output/ex23fwdadj_1.out
191 
192 TEST*/
193