Actual source code: tssen.c

  1: #include <petsc/private/tsimpl.h>
  2: #include <petscdraw.h>

  4: PetscLogEvent TS_AdjointStep, TS_ForwardStep, TS_JacobianPEval;

  6: /* #define TSADJOINT_STAGE */

  8: /* ------------------------ Sensitivity Context ---------------------------*/

 10: /*@C
 11:   TSSetRHSJacobianP - Sets the function that computes the Jacobian of G w.r.t. the parameters P where U_t = G(U,P,t), as well as the location to store the matrix.

 13:   Logically Collective

 15:   Input Parameters:
 16: + ts - `TS` context obtained from `TSCreate()`
 17: . Amat - JacobianP matrix
 18: . func - function
 19: - ctx - [optional] user-defined function context

 21:   Calling sequence of func:
 22: $ func (TS ts,PetscReal t,Vec y,Mat A,void *ctx);
 23: +   t - current timestep
 24: .   U - input vector (current ODE solution)
 25: .   A - output matrix
 26: -   ctx - [optional] user-defined function context

 28:   Level: intermediate

 30:   Note:
 31:     Amat has the same number of rows and the same row parallel layout as u, Amat has the same number of columns and parallel layout as p

 33: .seealso: [](chapter_ts), `TS`, `TSGetRHSJacobianP()`
 34: @*/
 35: PetscErrorCode TSSetRHSJacobianP(TS ts, Mat Amat, PetscErrorCode (*func)(TS, PetscReal, Vec, Mat, void *), void *ctx)
 36: {
 37:   PetscFunctionBegin;

 41:   ts->rhsjacobianp    = func;
 42:   ts->rhsjacobianpctx = ctx;
 43:   if (Amat) {
 44:     PetscCall(PetscObjectReference((PetscObject)Amat));
 45:     PetscCall(MatDestroy(&ts->Jacprhs));
 46:     ts->Jacprhs = Amat;
 47:   }
 48:   PetscFunctionReturn(PETSC_SUCCESS);
 49: }

 51: /*@C
 52:   TSGetRHSJacobianP - Gets the function that computes the Jacobian of G w.r.t. the parameters P where U_t = G(U,P,t), as well as the location to store the matrix.

 54:   Logically Collective

 56:   Input Parameter:
 57: . ts - `TS` context obtained from `TSCreate()`

 59:   Output Parameters:
 60: + Amat - JacobianP matrix
 61: . func - function
 62: - ctx - [optional] user-defined function context

 64:   Calling sequence of func:
 65: $ func (TS ts,PetscReal t,Vec y,Mat A,void *ctx);
 66: +   t - current timestep
 67: .   U - input vector (current ODE solution)
 68: .   A - output matrix
 69: -   ctx - [optional] user-defined function context

 71:   Level: intermediate

 73:   Note:
 74:     Amat has the same number of rows and the same row parallel layout as u, Amat has the same number of columns and parallel layout as p

 76: .seealso: [](chapter_ts), `TSSetRHSJacobianP()`, `TS`, `TSGetRHSJacobianP()`
 77: @*/
 78: PetscErrorCode TSGetRHSJacobianP(TS ts, Mat *Amat, PetscErrorCode (**func)(TS, PetscReal, Vec, Mat, void *), void **ctx)
 79: {
 80:   PetscFunctionBegin;
 81:   if (func) *func = ts->rhsjacobianp;
 82:   if (ctx) *ctx = ts->rhsjacobianpctx;
 83:   if (Amat) *Amat = ts->Jacprhs;
 84:   PetscFunctionReturn(PETSC_SUCCESS);
 85: }

 87: /*@C
 88:   TSComputeRHSJacobianP - Runs the user-defined JacobianP function.

 90:   Collective

 92:   Input Parameters:
 93: . ts   - The `TS` context obtained from `TSCreate()`

 95:   Level: developer

 97: .seealso: [](chapter_ts), `TSSetRHSJacobianP()`, `TS`
 98: @*/
 99: PetscErrorCode TSComputeRHSJacobianP(TS ts, PetscReal t, Vec U, Mat Amat)
100: {
101:   PetscFunctionBegin;
102:   if (!Amat) PetscFunctionReturn(PETSC_SUCCESS);

106:   if (ts->rhsjacobianp) PetscCallBack("TS callback JacobianP for sensitivity analysis", (*ts->rhsjacobianp)(ts, t, U, Amat, ts->rhsjacobianpctx));
107:   else {
108:     PetscBool assembled;
109:     PetscCall(MatZeroEntries(Amat));
110:     PetscCall(MatAssembled(Amat, &assembled));
111:     if (!assembled) {
112:       PetscCall(MatAssemblyBegin(Amat, MAT_FINAL_ASSEMBLY));
113:       PetscCall(MatAssemblyEnd(Amat, MAT_FINAL_ASSEMBLY));
114:     }
115:   }
116:   PetscFunctionReturn(PETSC_SUCCESS);
117: }

119: /*@C
120:   TSSetIJacobianP - Sets the function that computes the Jacobian of F w.r.t. the parameters P where F(Udot,U,t) = G(U,P,t), as well as the location to store the matrix.

122:   Logically Collective

124:   Input Parameters:
125: + ts - `TS` context obtained from `TSCreate()`
126: . Amat - JacobianP matrix
127: . func - function
128: - ctx - [optional] user-defined function context

130:   Calling sequence of func:
131: $ func (TS ts,PetscReal t,Vec y,Mat A,void *ctx);
132: +   t - current timestep
133: .   U - input vector (current ODE solution)
134: .   Udot - time derivative of state vector
135: .   shift - shift to apply, see note below
136: .   A - output matrix
137: -   ctx - [optional] user-defined function context

139:   Level: intermediate

141:   Note:
142:     Amat has the same number of rows and the same row parallel layout as u, Amat has the same number of columns and parallel layout as p

144: .seealso: [](chapter_ts), `TSSetRHSJacobianP()`, `TS`
145: @*/
146: PetscErrorCode TSSetIJacobianP(TS ts, Mat Amat, PetscErrorCode (*func)(TS, PetscReal, Vec, Vec, PetscReal, Mat, void *), void *ctx)
147: {
148:   PetscFunctionBegin;

152:   ts->ijacobianp    = func;
153:   ts->ijacobianpctx = ctx;
154:   if (Amat) {
155:     PetscCall(PetscObjectReference((PetscObject)Amat));
156:     PetscCall(MatDestroy(&ts->Jacp));
157:     ts->Jacp = Amat;
158:   }
159:   PetscFunctionReturn(PETSC_SUCCESS);
160: }

162: /*@C
163:   TSComputeIJacobianP - Runs the user-defined IJacobianP function.

165:   Collective

167:   Input Parameters:
168: + ts - the `TS` context
169: . t - current timestep
170: . U - state vector
171: . Udot - time derivative of state vector
172: . shift - shift to apply, see note below
173: - imex - flag indicates if the method is IMEX so that the RHSJacobianP should be kept separate

175:   Output Parameters:
176: . A - Jacobian matrix

178:   Level: developer

180: .seealso: [](chapter_ts), `TS`, `TSSetIJacobianP()`
181: @*/
182: PetscErrorCode TSComputeIJacobianP(TS ts, PetscReal t, Vec U, Vec Udot, PetscReal shift, Mat Amat, PetscBool imex)
183: {
184:   PetscFunctionBegin;
185:   if (!Amat) PetscFunctionReturn(PETSC_SUCCESS);

190:   PetscCall(PetscLogEventBegin(TS_JacobianPEval, ts, U, Amat, 0));
191:   if (ts->ijacobianp) PetscCallBack("TS callback JacobianP for sensitivity analysis", (*ts->ijacobianp)(ts, t, U, Udot, shift, Amat, ts->ijacobianpctx));
192:   if (imex) {
193:     if (!ts->ijacobianp) { /* system was written as Udot = G(t,U) */
194:       PetscBool assembled;
195:       PetscCall(MatZeroEntries(Amat));
196:       PetscCall(MatAssembled(Amat, &assembled));
197:       if (!assembled) {
198:         PetscCall(MatAssemblyBegin(Amat, MAT_FINAL_ASSEMBLY));
199:         PetscCall(MatAssemblyEnd(Amat, MAT_FINAL_ASSEMBLY));
200:       }
201:     }
202:   } else {
203:     if (ts->rhsjacobianp) PetscCall(TSComputeRHSJacobianP(ts, t, U, ts->Jacprhs));
204:     if (ts->Jacprhs == Amat) { /* No IJacobian, so we only have the RHS matrix */
205:       PetscCall(MatScale(Amat, -1));
206:     } else if (ts->Jacprhs) { /* Both IJacobian and RHSJacobian */
207:       MatStructure axpy = DIFFERENT_NONZERO_PATTERN;
208:       if (!ts->ijacobianp) { /* No IJacobianp provided, but we have a separate RHS matrix */
209:         PetscCall(MatZeroEntries(Amat));
210:       }
211:       PetscCall(MatAXPY(Amat, -1, ts->Jacprhs, axpy));
212:     }
213:   }
214:   PetscCall(PetscLogEventEnd(TS_JacobianPEval, ts, U, Amat, 0));
215:   PetscFunctionReturn(PETSC_SUCCESS);
216: }

218: /*@C
219:     TSSetCostIntegrand - Sets the routine for evaluating the integral term in one or more cost functions

221:     Logically Collective

223:     Input Parameters:
224: +   ts - the `TS` context obtained from `TSCreate()`
225: .   numcost - number of gradients to be computed, this is the number of cost functions
226: .   costintegral - vector that stores the integral values
227: .   rf - routine for evaluating the integrand function
228: .   drduf - function that computes the gradients of the r's with respect to u
229: .   drdpf - function that computes the gradients of the r's with respect to p, can be NULL if parametric sensitivity is not desired (mu=NULL)
230: .   fwd - flag indicating whether to evaluate cost integral in the forward run or the adjoint run
231: -   ctx - [optional] user-defined context for private data for the function evaluation routine (may be NULL)

233:     Calling sequence of rf:
234: $   PetscErrorCode rf(TS ts,PetscReal t,Vec U,Vec F,void *ctx);

236:     Calling sequence of drduf:
237: $   PetscErroCode drduf(TS ts,PetscReal t,Vec U,Vec *dRdU,void *ctx);

239:     Calling sequence of drdpf:
240: $   PetscErroCode drdpf(TS ts,PetscReal t,Vec U,Vec *dRdP,void *ctx);

242:     Level: deprecated

244:     Note:
245:     For optimization there is usually a single cost function (numcost = 1). For sensitivities there may be multiple cost functions

247: .seealso: [](chapter_ts), `TS`, `TSSetRHSJacobianP()`, `TSGetCostGradients()`, `TSSetCostGradients()`
248: @*/
249: PetscErrorCode TSSetCostIntegrand(TS ts, PetscInt numcost, Vec costintegral, PetscErrorCode (*rf)(TS, PetscReal, Vec, Vec, void *), PetscErrorCode (*drduf)(TS, PetscReal, Vec, Vec *, void *), PetscErrorCode (*drdpf)(TS, PetscReal, Vec, Vec *, void *), PetscBool fwd, void *ctx)
250: {
251:   PetscFunctionBegin;
254:   PetscCheck(!ts->numcost || ts->numcost == numcost, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "The number of cost functions (2nd parameter of TSSetCostIntegrand()) is inconsistent with the one set by TSSetCostGradients() or TSForwardSetIntegralGradients()");
255:   if (!ts->numcost) ts->numcost = numcost;

257:   if (costintegral) {
258:     PetscCall(PetscObjectReference((PetscObject)costintegral));
259:     PetscCall(VecDestroy(&ts->vec_costintegral));
260:     ts->vec_costintegral = costintegral;
261:   } else {
262:     if (!ts->vec_costintegral) { /* Create a seq vec if user does not provide one */
263:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, numcost, &ts->vec_costintegral));
264:     } else {
265:       PetscCall(VecSet(ts->vec_costintegral, 0.0));
266:     }
267:   }
268:   if (!ts->vec_costintegrand) {
269:     PetscCall(VecDuplicate(ts->vec_costintegral, &ts->vec_costintegrand));
270:   } else {
271:     PetscCall(VecSet(ts->vec_costintegrand, 0.0));
272:   }
273:   ts->costintegralfwd  = fwd; /* Evaluate the cost integral in forward run if fwd is true */
274:   ts->costintegrand    = rf;
275:   ts->costintegrandctx = ctx;
276:   ts->drdufunction     = drduf;
277:   ts->drdpfunction     = drdpf;
278:   PetscFunctionReturn(PETSC_SUCCESS);
279: }

281: /*@C
282:    TSGetCostIntegral - Returns the values of the integral term in the cost functions.
283:    It is valid to call the routine after a backward run.

285:    Not Collective

287:    Input Parameter:
288: .  ts - the `TS` context obtained from `TSCreate()`

290:    Output Parameter:
291: .  v - the vector containing the integrals for each cost function

293:    Level: intermediate

295: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, ``TSSetCostIntegrand()`
296: @*/
297: PetscErrorCode TSGetCostIntegral(TS ts, Vec *v)
298: {
299:   TS quadts;

301:   PetscFunctionBegin;
304:   PetscCall(TSGetQuadratureTS(ts, NULL, &quadts));
305:   *v = quadts->vec_sol;
306:   PetscFunctionReturn(PETSC_SUCCESS);
307: }

309: /*@C
310:    TSComputeCostIntegrand - Evaluates the integral function in the cost functions.

312:    Input Parameters:
313: +  ts - the `TS` context
314: .  t - current time
315: -  U - state vector, i.e. current solution

317:    Output Parameter:
318: .  Q - vector of size numcost to hold the outputs

320:    Level: deprecated

322:    Note:
323:    Most users should not need to explicitly call this routine, as it
324:    is used internally within the sensitivity analysis context.

326: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, `TSSetCostIntegrand()`
327: @*/
328: PetscErrorCode TSComputeCostIntegrand(TS ts, PetscReal t, Vec U, Vec Q)
329: {
330:   PetscFunctionBegin;

335:   PetscCall(PetscLogEventBegin(TS_FunctionEval, ts, U, Q, 0));
336:   if (ts->costintegrand) PetscCallBack("TS callback integrand in the cost function", (*ts->costintegrand)(ts, t, U, Q, ts->costintegrandctx));
337:   else PetscCall(VecZeroEntries(Q));
338:   PetscCall(PetscLogEventEnd(TS_FunctionEval, ts, U, Q, 0));
339:   PetscFunctionReturn(PETSC_SUCCESS);
340: }

342: /*@C
343:   TSComputeDRDUFunction - Deprecated, use `TSGetQuadratureTS()` then `TSComputeRHSJacobian()`

345:   Level: deprecated

347: @*/
348: PetscErrorCode TSComputeDRDUFunction(TS ts, PetscReal t, Vec U, Vec *DRDU)
349: {
350:   PetscFunctionBegin;
351:   if (!DRDU) PetscFunctionReturn(PETSC_SUCCESS);

355:   PetscCallBack("TS callback DRDU for sensitivity analysis", (*ts->drdufunction)(ts, t, U, DRDU, ts->costintegrandctx));
356:   PetscFunctionReturn(PETSC_SUCCESS);
357: }

359: /*@C
360:   TSComputeDRDPFunction - Deprecated, use `TSGetQuadratureTS()` then `TSComputeRHSJacobianP()`

362:   Level: deprecated

364: @*/
365: PetscErrorCode TSComputeDRDPFunction(TS ts, PetscReal t, Vec U, Vec *DRDP)
366: {
367:   PetscFunctionBegin;
368:   if (!DRDP) PetscFunctionReturn(PETSC_SUCCESS);

372:   PetscCallBack("TS callback DRDP for sensitivity analysis", (*ts->drdpfunction)(ts, t, U, DRDP, ts->costintegrandctx));
373:   PetscFunctionReturn(PETSC_SUCCESS);
374: }

376: /*@C
377:   TSSetIHessianProduct - Sets the function that computes the vector-Hessian-vector product. The Hessian is the second-order derivative of F (IFunction) w.r.t. the state variable.

379:   Logically Collective

381:   Input Parameters:
382: + ts - `TS` context obtained from `TSCreate()`
383: . ihp1 - an array of vectors storing the result of vector-Hessian-vector product for F_UU
384: . hessianproductfunc1 - vector-Hessian-vector product function for F_UU
385: . ihp2 - an array of vectors storing the result of vector-Hessian-vector product for F_UP
386: . hessianproductfunc2 - vector-Hessian-vector product function for F_UP
387: . ihp3 - an array of vectors storing the result of vector-Hessian-vector product for F_PU
388: . hessianproductfunc3 - vector-Hessian-vector product function for F_PU
389: . ihp4 - an array of vectors storing the result of vector-Hessian-vector product for F_PP
390: - hessianproductfunc4 - vector-Hessian-vector product function for F_PP

392:   Calling sequence of ihessianproductfunc:
393: $ ihessianproductfunc (TS ts,PetscReal t,Vec U,Vec *Vl,Vec Vr,Vec *VHV,void *ctx);
394: +   t - current timestep
395: .   U - input vector (current ODE solution)
396: .   Vl - an array of input vectors to be left-multiplied with the Hessian
397: .   Vr - input vector to be right-multiplied with the Hessian
398: .   VHV - an array of output vectors for vector-Hessian-vector product
399: -   ctx - [optional] user-defined function context

401:   Level: intermediate

403:   Notes:
404:   The first Hessian function and the working array are required.
405:   As an example to implement the callback functions, the second callback function calculates the vector-Hessian-vector product
406:   $ Vl_n^T*F_UP*Vr
407:   where the vector Vl_n (n-th element in the array Vl) and Vr are of size N and M respectively, and the Hessian F_UP is of size N x N x M.
408:   Each entry of F_UP corresponds to the derivative
409:   $ F_UP[i][j][k] = \frac{\partial^2 F[i]}{\partial U[j] \partial P[k]}.
410:   The result of the vector-Hessian-vector product for Vl_n needs to be stored in vector VHV_n with the j-th entry being
411:   $ VHV_n[j] = \sum_i \sum_k {Vl_n[i] * F_UP[i][j][k] * Vr[k]}
412:   If the cost function is a scalar, there will be only one vector in Vl and VHV.

414: .seealso: [](chapter_ts), `TS`
415: @*/
416: PetscErrorCode TSSetIHessianProduct(TS ts, Vec *ihp1, PetscErrorCode (*ihessianproductfunc1)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), Vec *ihp2, PetscErrorCode (*ihessianproductfunc2)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), Vec *ihp3, PetscErrorCode (*ihessianproductfunc3)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), Vec *ihp4, PetscErrorCode (*ihessianproductfunc4)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), void *ctx)
417: {
418:   PetscFunctionBegin;

422:   ts->ihessianproductctx = ctx;
423:   if (ihp1) ts->vecs_fuu = ihp1;
424:   if (ihp2) ts->vecs_fup = ihp2;
425:   if (ihp3) ts->vecs_fpu = ihp3;
426:   if (ihp4) ts->vecs_fpp = ihp4;
427:   ts->ihessianproduct_fuu = ihessianproductfunc1;
428:   ts->ihessianproduct_fup = ihessianproductfunc2;
429:   ts->ihessianproduct_fpu = ihessianproductfunc3;
430:   ts->ihessianproduct_fpp = ihessianproductfunc4;
431:   PetscFunctionReturn(PETSC_SUCCESS);
432: }

434: /*@C
435:   TSComputeIHessianProductFunctionUU - Runs the user-defined vector-Hessian-vector product function for Fuu.

437:   Collective

439:   Input Parameters:
440: . ts   - The `TS` context obtained from `TSCreate()`

442:   Level: developer

444:   Note:
445:   `TSComputeIHessianProductFunctionUU()` is typically used for sensitivity implementation,
446:   so most users would not generally call this routine themselves.

448: .seealso: [](chapter_ts), `TSSetIHessianProduct()`
449: @*/
450: PetscErrorCode TSComputeIHessianProductFunctionUU(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
451: {
452:   PetscFunctionBegin;
453:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

457:   if (ts->ihessianproduct_fuu) PetscCallBack("TS callback IHessianProduct 1 for sensitivity analysis", (*ts->ihessianproduct_fuu)(ts, t, U, Vl, Vr, VHV, ts->ihessianproductctx));

459:   /* does not consider IMEX for now, so either IHessian or RHSHessian will be calculated, using the same output VHV */
460:   if (ts->rhshessianproduct_guu) {
461:     PetscInt nadj;
462:     PetscCall(TSComputeRHSHessianProductFunctionUU(ts, t, U, Vl, Vr, VHV));
463:     for (nadj = 0; nadj < ts->numcost; nadj++) PetscCall(VecScale(VHV[nadj], -1));
464:   }
465:   PetscFunctionReturn(PETSC_SUCCESS);
466: }

468: /*@C
469:   TSComputeIHessianProductFunctionUP - Runs the user-defined vector-Hessian-vector product function for Fup.

471:   Collective

473:   Input Parameters:
474: . ts   - The `TS` context obtained from `TSCreate()`

476:   Level: developer

478:   Note:
479:   `TSComputeIHessianProductFunctionUP()` is typically used for sensitivity implementation,
480:   so most users would not generally call this routine themselves.

482: .seealso: [](chapter_ts), `TSSetIHessianProduct()`
483: @*/
484: PetscErrorCode TSComputeIHessianProductFunctionUP(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
485: {
486:   PetscFunctionBegin;
487:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

491:   if (ts->ihessianproduct_fup) PetscCallBack("TS callback IHessianProduct 2 for sensitivity analysis", (*ts->ihessianproduct_fup)(ts, t, U, Vl, Vr, VHV, ts->ihessianproductctx));

493:   /* does not consider IMEX for now, so either IHessian or RHSHessian will be calculated, using the same output VHV */
494:   if (ts->rhshessianproduct_gup) {
495:     PetscInt nadj;
496:     PetscCall(TSComputeRHSHessianProductFunctionUP(ts, t, U, Vl, Vr, VHV));
497:     for (nadj = 0; nadj < ts->numcost; nadj++) PetscCall(VecScale(VHV[nadj], -1));
498:   }
499:   PetscFunctionReturn(PETSC_SUCCESS);
500: }

502: /*@C
503:   TSComputeIHessianProductFunctionPU - Runs the user-defined vector-Hessian-vector product function for Fpu.

505:   Collective

507:   Input Parameters:
508: . ts   - The `TS` context obtained from `TSCreate()`

510:   Level: developer

512:   Note:
513:   `TSComputeIHessianProductFunctionPU()` is typically used for sensitivity implementation,
514:   so most users would not generally call this routine themselves.

516: .seealso: [](chapter_ts), `TSSetIHessianProduct()`
517: @*/
518: PetscErrorCode TSComputeIHessianProductFunctionPU(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
519: {
520:   PetscFunctionBegin;
521:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

525:   if (ts->ihessianproduct_fpu) PetscCallBack("TS callback IHessianProduct 3 for sensitivity analysis", (*ts->ihessianproduct_fpu)(ts, t, U, Vl, Vr, VHV, ts->ihessianproductctx));

527:   /* does not consider IMEX for now, so either IHessian or RHSHessian will be calculated, using the same output VHV */
528:   if (ts->rhshessianproduct_gpu) {
529:     PetscInt nadj;
530:     PetscCall(TSComputeRHSHessianProductFunctionPU(ts, t, U, Vl, Vr, VHV));
531:     for (nadj = 0; nadj < ts->numcost; nadj++) PetscCall(VecScale(VHV[nadj], -1));
532:   }
533:   PetscFunctionReturn(PETSC_SUCCESS);
534: }

536: /*@C
537:   TSComputeIHessianProductFunctionPP - Runs the user-defined vector-Hessian-vector product function for Fpp.

539:   Collective

541:   Input Parameters:
542: . ts   - The `TS` context obtained from `TSCreate()`

544:   Level: developer

546:   Note:
547:   `TSComputeIHessianProductFunctionPP()` is typically used for sensitivity implementation,
548:   so most users would not generally call this routine themselves.

550: .seealso: [](chapter_ts), `TSSetIHessianProduct()`
551: @*/
552: PetscErrorCode TSComputeIHessianProductFunctionPP(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
553: {
554:   PetscFunctionBegin;
555:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

559:   if (ts->ihessianproduct_fpp) PetscCallBack("TS callback IHessianProduct 3 for sensitivity analysis", (*ts->ihessianproduct_fpp)(ts, t, U, Vl, Vr, VHV, ts->ihessianproductctx));

561:   /* does not consider IMEX for now, so either IHessian or RHSHessian will be calculated, using the same output VHV */
562:   if (ts->rhshessianproduct_gpp) {
563:     PetscInt nadj;
564:     PetscCall(TSComputeRHSHessianProductFunctionPP(ts, t, U, Vl, Vr, VHV));
565:     for (nadj = 0; nadj < ts->numcost; nadj++) PetscCall(VecScale(VHV[nadj], -1));
566:   }
567:   PetscFunctionReturn(PETSC_SUCCESS);
568: }

570: /*@C
571:   TSSetRHSHessianProduct - Sets the function that computes the vector-Hessian-vector product. The Hessian is the second-order derivative of G (RHSFunction) w.r.t. the state variable.

573:   Logically Collective

575:   Input Parameters:
576: + ts - `TS` context obtained from `TSCreate()`
577: . rhshp1 - an array of vectors storing the result of vector-Hessian-vector product for G_UU
578: . hessianproductfunc1 - vector-Hessian-vector product function for G_UU
579: . rhshp2 - an array of vectors storing the result of vector-Hessian-vector product for G_UP
580: . hessianproductfunc2 - vector-Hessian-vector product function for G_UP
581: . rhshp3 - an array of vectors storing the result of vector-Hessian-vector product for G_PU
582: . hessianproductfunc3 - vector-Hessian-vector product function for G_PU
583: . rhshp4 - an array of vectors storing the result of vector-Hessian-vector product for G_PP
584: - hessianproductfunc4 - vector-Hessian-vector product function for G_PP

586:   Calling sequence of ihessianproductfunc:
587: $ rhshessianproductfunc (TS ts,PetscReal t,Vec U,Vec *Vl,Vec Vr,Vec *VHV,void *ctx);
588: +   t - current timestep
589: .   U - input vector (current ODE solution)
590: .   Vl - an array of input vectors to be left-multiplied with the Hessian
591: .   Vr - input vector to be right-multiplied with the Hessian
592: .   VHV - an array of output vectors for vector-Hessian-vector product
593: -   ctx - [optional] user-defined function context

595:   Level: intermediate

597:   Notes:
598:   The first Hessian function and the working array are required.
599:   As an example to implement the callback functions, the second callback function calculates the vector-Hessian-vector product
600:   $ Vl_n^T*G_UP*Vr
601:   where the vector Vl_n (n-th element in the array Vl) and Vr are of size N and M respectively, and the Hessian G_UP is of size N x N x M.
602:   Each entry of G_UP corresponds to the derivative
603:   $ G_UP[i][j][k] = \frac{\partial^2 G[i]}{\partial U[j] \partial P[k]}.
604:   The result of the vector-Hessian-vector product for Vl_n needs to be stored in vector VHV_n with j-th entry being
605:   $ VHV_n[j] = \sum_i \sum_k {Vl_n[i] * G_UP[i][j][k] * Vr[k]}
606:   If the cost function is a scalar, there will be only one vector in Vl and VHV.

608: .seealso: `TS`
609: @*/
610: PetscErrorCode TSSetRHSHessianProduct(TS ts, Vec *rhshp1, PetscErrorCode (*rhshessianproductfunc1)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), Vec *rhshp2, PetscErrorCode (*rhshessianproductfunc2)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), Vec *rhshp3, PetscErrorCode (*rhshessianproductfunc3)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), Vec *rhshp4, PetscErrorCode (*rhshessianproductfunc4)(TS, PetscReal, Vec, Vec *, Vec, Vec *, void *), void *ctx)
611: {
612:   PetscFunctionBegin;

616:   ts->rhshessianproductctx = ctx;
617:   if (rhshp1) ts->vecs_guu = rhshp1;
618:   if (rhshp2) ts->vecs_gup = rhshp2;
619:   if (rhshp3) ts->vecs_gpu = rhshp3;
620:   if (rhshp4) ts->vecs_gpp = rhshp4;
621:   ts->rhshessianproduct_guu = rhshessianproductfunc1;
622:   ts->rhshessianproduct_gup = rhshessianproductfunc2;
623:   ts->rhshessianproduct_gpu = rhshessianproductfunc3;
624:   ts->rhshessianproduct_gpp = rhshessianproductfunc4;
625:   PetscFunctionReturn(PETSC_SUCCESS);
626: }

628: /*@C
629:   TSComputeRHSHessianProductFunctionUU - Runs the user-defined vector-Hessian-vector product function for Guu.

631:   Collective

633:   Input Parameters:
634: . ts   - The `TS` context obtained from `TSCreate()`

636:   Level: developer

638:   Note:
639:   `TSComputeRHSHessianProductFunctionUU()` is typically used for sensitivity implementation,
640:   so most users would not generally call this routine themselves.

642: .seealso: [](chapter_ts), `TS`, `TSSetRHSHessianProduct()`
643: @*/
644: PetscErrorCode TSComputeRHSHessianProductFunctionUU(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
645: {
646:   PetscFunctionBegin;
647:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

651:   PetscCallBack("TS callback RHSHessianProduct 1 for sensitivity analysis", (*ts->rhshessianproduct_guu)(ts, t, U, Vl, Vr, VHV, ts->rhshessianproductctx));
652:   PetscFunctionReturn(PETSC_SUCCESS);
653: }

655: /*@C
656:   TSComputeRHSHessianProductFunctionUP - Runs the user-defined vector-Hessian-vector product function for Gup.

658:   Collective

660:   Input Parameters:
661: . ts   - The `TS` context obtained from `TSCreate()`

663:   Level: developer

665:   Note:
666:   `TSComputeRHSHessianProductFunctionUP()` is typically used for sensitivity implementation,
667:   so most users would not generally call this routine themselves.

669: .seealso: [](chapter_ts), `TS`, `TSSetRHSHessianProduct()`
670: @*/
671: PetscErrorCode TSComputeRHSHessianProductFunctionUP(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
672: {
673:   PetscFunctionBegin;
674:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

678:   PetscCallBack("TS callback RHSHessianProduct 2 for sensitivity analysis", (*ts->rhshessianproduct_gup)(ts, t, U, Vl, Vr, VHV, ts->rhshessianproductctx));
679:   PetscFunctionReturn(PETSC_SUCCESS);
680: }

682: /*@C
683:   TSComputeRHSHessianProductFunctionPU - Runs the user-defined vector-Hessian-vector product function for Gpu.

685:   Collective

687:   Input Parameters:
688: . ts   - The `TS` context obtained from `TSCreate()`

690:   Level: developer

692:   Note:
693:   `TSComputeRHSHessianProductFunctionPU()` is typically used for sensitivity implementation,
694:   so most users would not generally call this routine themselves.

696: .seealso: [](chapter_ts), `TSSetRHSHessianProduct()`
697: @*/
698: PetscErrorCode TSComputeRHSHessianProductFunctionPU(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
699: {
700:   PetscFunctionBegin;
701:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

705:   PetscCallBack("TS callback RHSHessianProduct 3 for sensitivity analysis", (*ts->rhshessianproduct_gpu)(ts, t, U, Vl, Vr, VHV, ts->rhshessianproductctx));
706:   PetscFunctionReturn(PETSC_SUCCESS);
707: }

709: /*@C
710:   TSComputeRHSHessianProductFunctionPP - Runs the user-defined vector-Hessian-vector product function for Gpp.

712:   Collective

714:   Input Parameters:
715: . ts   - The `TS` context obtained from `TSCreate()`

717:   Level: developer

719:   Note:
720:   `TSComputeRHSHessianProductFunctionPP()` is typically used for sensitivity implementation,
721:   so most users would not generally call this routine themselves.

723: .seealso: [](chapter_ts), `TSSetRHSHessianProduct()`
724: @*/
725: PetscErrorCode TSComputeRHSHessianProductFunctionPP(TS ts, PetscReal t, Vec U, Vec *Vl, Vec Vr, Vec *VHV)
726: {
727:   PetscFunctionBegin;
728:   if (!VHV) PetscFunctionReturn(PETSC_SUCCESS);

732:   PetscCallBack("TS callback RHSHessianProduct 3 for sensitivity analysis", (*ts->rhshessianproduct_gpp)(ts, t, U, Vl, Vr, VHV, ts->rhshessianproductctx));
733:   PetscFunctionReturn(PETSC_SUCCESS);
734: }

736: /* --------------------------- Adjoint sensitivity ---------------------------*/

738: /*@
739:    TSSetCostGradients - Sets the initial value of the gradients of the cost function w.r.t. initial values and w.r.t. the problem parameters
740:       for use by the `TS` adjoint routines.

742:    Logically Collective

744:    Input Parameters:
745: +  ts - the `TS` context obtained from `TSCreate()`
746: .  numcost - number of gradients to be computed, this is the number of cost functions
747: .  lambda - gradients with respect to the initial condition variables, the dimension and parallel layout of these vectors is the same as the ODE solution vector
748: -  mu - gradients with respect to the parameters, the number of entries in these vectors is the same as the number of parameters

750:    Level: beginner

752:    Notes:
753:     the entries in these vectors must be correctly initialized with the values lambda_i = df/dy|finaltime  mu_i = df/dp|finaltime

755:    After `TSAdjointSolve()` is called the lambda and the mu contain the computed sensitivities

757: .seealso: `TS`, `TSAdjointSolve()`, `TSGetCostGradients()`
758: @*/
759: PetscErrorCode TSSetCostGradients(TS ts, PetscInt numcost, Vec *lambda, Vec *mu)
760: {
761:   PetscFunctionBegin;
764:   ts->vecs_sensi  = lambda;
765:   ts->vecs_sensip = mu;
766:   PetscCheck(!ts->numcost || ts->numcost == numcost, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "The number of cost functions (2nd parameter of TSSetCostIntegrand()) is inconsistent with the one set by TSSetCostIntegrand");
767:   ts->numcost = numcost;
768:   PetscFunctionReturn(PETSC_SUCCESS);
769: }

771: /*@
772:    TSGetCostGradients - Returns the gradients from the `TSAdjointSolve()`

774:    Not Collective, but the vectors returned are parallel if `TS` is parallel

776:    Input Parameter:
777: .  ts - the `TS` context obtained from `TSCreate()`

779:    Output Parameters:
780: +  numcost - size of returned arrays
781: .  lambda - vectors containing the gradients of the cost functions with respect to the ODE/DAE solution variables
782: -  mu - vectors containing the gradients of the cost functions with respect to the problem parameters

784:    Level: intermediate

786: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, `TSSetCostGradients()`
787: @*/
788: PetscErrorCode TSGetCostGradients(TS ts, PetscInt *numcost, Vec **lambda, Vec **mu)
789: {
790:   PetscFunctionBegin;
792:   if (numcost) *numcost = ts->numcost;
793:   if (lambda) *lambda = ts->vecs_sensi;
794:   if (mu) *mu = ts->vecs_sensip;
795:   PetscFunctionReturn(PETSC_SUCCESS);
796: }

798: /*@
799:    TSSetCostHessianProducts - Sets the initial value of the Hessian-vector products of the cost function w.r.t. initial values and w.r.t. the problem parameters
800:    for use by the `TS` adjoint routines.

802:    Logically Collective

804:    Input Parameters:
805: +  ts - the `TS` context obtained from `TSCreate()`
806: .  numcost - number of cost functions
807: .  lambda2 - Hessian-vector product with respect to the initial condition variables, the dimension and parallel layout of these vectors is the same as the ODE solution vector
808: .  mu2 - Hessian-vector product with respect to the parameters, the number of entries in these vectors is the same as the number of parameters
809: -  dir - the direction vector that are multiplied with the Hessian of the cost functions

811:    Level: beginner

813:    Notes:
814:    Hessian of the cost function is completely different from Hessian of the ODE/DAE system

816:    For second-order adjoint, one needs to call this function and then `TSAdjointSetForward()` before `TSSolve()`.

818:    After `TSAdjointSolve()` is called, the lambda2 and the mu2 will contain the computed second-order adjoint sensitivities, and can be used to produce Hessian-vector product (not the full Hessian matrix). Users must provide a direction vector; it is usually generated by an optimization solver.

820:    Passing NULL for lambda2 disables the second-order calculation.

822: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, `TSAdjointSetForward()`
823: @*/
824: PetscErrorCode TSSetCostHessianProducts(TS ts, PetscInt numcost, Vec *lambda2, Vec *mu2, Vec dir)
825: {
826:   PetscFunctionBegin;
828:   PetscCheck(!ts->numcost || ts->numcost == numcost, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "The number of cost functions (2nd parameter of TSSetCostIntegrand()) is inconsistent with the one set by TSSetCostIntegrand");
829:   ts->numcost      = numcost;
830:   ts->vecs_sensi2  = lambda2;
831:   ts->vecs_sensi2p = mu2;
832:   ts->vec_dir      = dir;
833:   PetscFunctionReturn(PETSC_SUCCESS);
834: }

836: /*@
837:    TSGetCostHessianProducts - Returns the gradients from the `TSAdjointSolve()`

839:    Not Collective, but vectors returned are parallel if `TS` is parallel

841:    Input Parameter:
842: .  ts - the `TS` context obtained from `TSCreate()`

844:    Output Parameters:
845: +  numcost - number of cost functions
846: .  lambda2 - Hessian-vector product with respect to the initial condition variables, the dimension and parallel layout of these vectors is the same as the ODE solution vector
847: .  mu2 - Hessian-vector product with respect to the parameters, the number of entries in these vectors is the same as the number of parameters
848: -  dir - the direction vector that are multiplied with the Hessian of the cost functions

850:    Level: intermediate

852: .seealso: [](chapter_ts), `TSAdjointSolve()`, `TSSetCostHessianProducts()`
853: @*/
854: PetscErrorCode TSGetCostHessianProducts(TS ts, PetscInt *numcost, Vec **lambda2, Vec **mu2, Vec *dir)
855: {
856:   PetscFunctionBegin;
858:   if (numcost) *numcost = ts->numcost;
859:   if (lambda2) *lambda2 = ts->vecs_sensi2;
860:   if (mu2) *mu2 = ts->vecs_sensi2p;
861:   if (dir) *dir = ts->vec_dir;
862:   PetscFunctionReturn(PETSC_SUCCESS);
863: }

865: /*@
866:   TSAdjointSetForward - Trigger the tangent linear solver and initialize the forward sensitivities

868:   Logically Collective

870:   Input Parameters:
871: +  ts - the `TS` context obtained from `TSCreate()`
872: -  didp - the derivative of initial values w.r.t. parameters

874:   Level: intermediate

876:   Notes:
877:   When computing sensitivies w.r.t. initial condition, set didp to NULL so that the solver will take it as an identity matrix mathematically.
878:   `TS` adjoint does not reset the tangent linear solver automatically, `TSAdjointResetForward()` should be called to reset the tangent linear solver.

880: .seealso: [](chapter_ts), `TSAdjointSolve()`, `TSSetCostHessianProducts()`, `TSAdjointResetForward()`
881: @*/
882: PetscErrorCode TSAdjointSetForward(TS ts, Mat didp)
883: {
884:   Mat          A;
885:   Vec          sp;
886:   PetscScalar *xarr;
887:   PetscInt     lsize;

889:   PetscFunctionBegin;
890:   ts->forward_solve = PETSC_TRUE; /* turn on tangent linear mode */
891:   PetscCheck(ts->vecs_sensi2, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "Must call TSSetCostHessianProducts() first");
892:   PetscCheck(ts->vec_dir, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "Directional vector is missing. Call TSSetCostHessianProducts() to set it.");
893:   /* create a single-column dense matrix */
894:   PetscCall(VecGetLocalSize(ts->vec_sol, &lsize));
895:   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)ts), lsize, PETSC_DECIDE, PETSC_DECIDE, 1, NULL, &A));

897:   PetscCall(VecDuplicate(ts->vec_sol, &sp));
898:   PetscCall(MatDenseGetColumn(A, 0, &xarr));
899:   PetscCall(VecPlaceArray(sp, xarr));
900:   if (ts->vecs_sensi2p) { /* tangent linear variable initialized as 2*dIdP*dir */
901:     if (didp) {
902:       PetscCall(MatMult(didp, ts->vec_dir, sp));
903:       PetscCall(VecScale(sp, 2.));
904:     } else {
905:       PetscCall(VecZeroEntries(sp));
906:     }
907:   } else { /* tangent linear variable initialized as dir */
908:     PetscCall(VecCopy(ts->vec_dir, sp));
909:   }
910:   PetscCall(VecResetArray(sp));
911:   PetscCall(MatDenseRestoreColumn(A, &xarr));
912:   PetscCall(VecDestroy(&sp));

914:   PetscCall(TSForwardSetInitialSensitivities(ts, A)); /* if didp is NULL, identity matrix is assumed */

916:   PetscCall(MatDestroy(&A));
917:   PetscFunctionReturn(PETSC_SUCCESS);
918: }

920: /*@
921:   TSAdjointResetForward - Reset the tangent linear solver and destroy the tangent linear context

923:   Logically Collective

925:   Input Parameters:
926: .  ts - the `TS` context obtained from `TSCreate()`

928:   Level: intermediate

930: .seealso: [](chapter_ts), `TSAdjointSetForward()`
931: @*/
932: PetscErrorCode TSAdjointResetForward(TS ts)
933: {
934:   PetscFunctionBegin;
935:   ts->forward_solve = PETSC_FALSE; /* turn off tangent linear mode */
936:   PetscCall(TSForwardReset(ts));
937:   PetscFunctionReturn(PETSC_SUCCESS);
938: }

940: /*@
941:    TSAdjointSetUp - Sets up the internal data structures for the later use
942:    of an adjoint solver

944:    Collective

946:    Input Parameter:
947: .  ts - the `TS` context obtained from `TSCreate()`

949:    Level: advanced

951: .seealso: [](chapter_ts), `TSCreate()`, `TSAdjointStep()`, `TSSetCostGradients()`
952: @*/
953: PetscErrorCode TSAdjointSetUp(TS ts)
954: {
955:   TSTrajectory tj;
956:   PetscBool    match;

958:   PetscFunctionBegin;
960:   if (ts->adjointsetupcalled) PetscFunctionReturn(PETSC_SUCCESS);
961:   PetscCheck(ts->vecs_sensi, PetscObjectComm((PetscObject)ts), PETSC_ERR_ARG_WRONGSTATE, "Must call TSSetCostGradients() first");
962:   PetscCheck(!ts->vecs_sensip || ts->Jacp || ts->Jacprhs, PetscObjectComm((PetscObject)ts), PETSC_ERR_ARG_WRONGSTATE, "Must call TSSetRHSJacobianP() or TSSetIJacobianP() first");
963:   PetscCall(TSGetTrajectory(ts, &tj));
964:   PetscCall(PetscObjectTypeCompare((PetscObject)tj, TSTRAJECTORYBASIC, &match));
965:   if (match) {
966:     PetscBool solution_only;
967:     PetscCall(TSTrajectoryGetSolutionOnly(tj, &solution_only));
968:     PetscCheck(!solution_only, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "TSAdjoint cannot use the solution-only mode when choosing the Basic TSTrajectory type. Turn it off with -ts_trajectory_solution_only 0");
969:   }
970:   PetscCall(TSTrajectorySetUseHistory(tj, PETSC_FALSE)); /* not use TSHistory */

972:   if (ts->quadraturets) { /* if there is integral in the cost function */
973:     PetscCall(VecDuplicate(ts->vecs_sensi[0], &ts->vec_drdu_col));
974:     if (ts->vecs_sensip) PetscCall(VecDuplicate(ts->vecs_sensip[0], &ts->vec_drdp_col));
975:   }

977:   PetscTryTypeMethod(ts, adjointsetup);
978:   ts->adjointsetupcalled = PETSC_TRUE;
979:   PetscFunctionReturn(PETSC_SUCCESS);
980: }

982: /*@
983:   TSAdjointReset - Resets a `TS` adjoint context and removes any allocated `Vec`s and `Mat`s.

985:    Collective

987:    Input Parameter:
988: .  ts - the `TS` context obtained from `TSCreate()`

990:    Level: beginner

992: .seealso: [](chapter_ts), `TSCreate()`, `TSAdjointSetUp()`, `TSADestroy()`
993: @*/
994: PetscErrorCode TSAdjointReset(TS ts)
995: {
996:   PetscFunctionBegin;
998:   PetscTryTypeMethod(ts, adjointreset);
999:   if (ts->quadraturets) { /* if there is integral in the cost function */
1000:     PetscCall(VecDestroy(&ts->vec_drdu_col));
1001:     if (ts->vecs_sensip) PetscCall(VecDestroy(&ts->vec_drdp_col));
1002:   }
1003:   ts->vecs_sensi         = NULL;
1004:   ts->vecs_sensip        = NULL;
1005:   ts->vecs_sensi2        = NULL;
1006:   ts->vecs_sensi2p       = NULL;
1007:   ts->vec_dir            = NULL;
1008:   ts->adjointsetupcalled = PETSC_FALSE;
1009:   PetscFunctionReturn(PETSC_SUCCESS);
1010: }

1012: /*@
1013:    TSAdjointSetSteps - Sets the number of steps the adjoint solver should take backward in time

1015:    Logically Collective

1017:    Input Parameters:
1018: +  ts - the `TS` context obtained from `TSCreate()`
1019: -  steps - number of steps to use

1021:    Level: intermediate

1023:    Notes:
1024:     Normally one does not call this and `TSAdjointSolve()` integrates back to the original timestep. One can call this
1025:           so as to integrate back to less than the original timestep

1027: .seealso: [](chapter_ts), `TSAdjointSolve()`, `TS`, `TSSetExactFinalTime()`
1028: @*/
1029: PetscErrorCode TSAdjointSetSteps(TS ts, PetscInt steps)
1030: {
1031:   PetscFunctionBegin;
1034:   PetscCheck(steps >= 0, PetscObjectComm((PetscObject)ts), PETSC_ERR_ARG_OUTOFRANGE, "Cannot step back a negative number of steps");
1035:   PetscCheck(steps <= ts->steps, PetscObjectComm((PetscObject)ts), PETSC_ERR_ARG_OUTOFRANGE, "Cannot step back more than the total number of forward steps");
1036:   ts->adjoint_max_steps = steps;
1037:   PetscFunctionReturn(PETSC_SUCCESS);
1038: }

1040: /*@C
1041:   TSAdjointSetRHSJacobian - Deprecated, use `TSSetRHSJacobianP()`

1043:   Level: deprecated

1045: @*/
1046: PetscErrorCode TSAdjointSetRHSJacobian(TS ts, Mat Amat, PetscErrorCode (*func)(TS, PetscReal, Vec, Mat, void *), void *ctx)
1047: {
1048:   PetscFunctionBegin;

1052:   ts->rhsjacobianp    = func;
1053:   ts->rhsjacobianpctx = ctx;
1054:   if (Amat) {
1055:     PetscCall(PetscObjectReference((PetscObject)Amat));
1056:     PetscCall(MatDestroy(&ts->Jacp));
1057:     ts->Jacp = Amat;
1058:   }
1059:   PetscFunctionReturn(PETSC_SUCCESS);
1060: }

1062: /*@C
1063:   TSAdjointComputeRHSJacobian - Deprecated, use `TSComputeRHSJacobianP()`

1065:   Level: deprecated

1067: @*/
1068: PetscErrorCode TSAdjointComputeRHSJacobian(TS ts, PetscReal t, Vec U, Mat Amat)
1069: {
1070:   PetscFunctionBegin;

1075:   PetscCallBack("TS callback JacobianP for sensitivity analysis", (*ts->rhsjacobianp)(ts, t, U, Amat, ts->rhsjacobianpctx));
1076:   PetscFunctionReturn(PETSC_SUCCESS);
1077: }

1079: /*@
1080:   TSAdjointComputeDRDYFunction - Deprecated, use `TSGetQuadratureTS()` then `TSComputeRHSJacobian()`

1082:   Level: deprecated

1084: @*/
1085: PetscErrorCode TSAdjointComputeDRDYFunction(TS ts, PetscReal t, Vec U, Vec *DRDU)
1086: {
1087:   PetscFunctionBegin;

1091:   PetscCallBack("TS callback DRDY for sensitivity analysis", (*ts->drdufunction)(ts, t, U, DRDU, ts->costintegrandctx));
1092:   PetscFunctionReturn(PETSC_SUCCESS);
1093: }

1095: /*@
1096:   TSAdjointComputeDRDPFunction - Deprecated, use `TSGetQuadratureTS()` then `TSComputeRHSJacobianP()`

1098:   Level: deprecated

1100: @*/
1101: PetscErrorCode TSAdjointComputeDRDPFunction(TS ts, PetscReal t, Vec U, Vec *DRDP)
1102: {
1103:   PetscFunctionBegin;

1107:   PetscCallBack("TS callback DRDP for sensitivity analysis", (*ts->drdpfunction)(ts, t, U, DRDP, ts->costintegrandctx));
1108:   PetscFunctionReturn(PETSC_SUCCESS);
1109: }

1111: /*@C
1112:    TSAdjointMonitorSensi - monitors the first lambda sensitivity

1114:    Level: intermediate

1116: .seealso: [](chapter_ts), `TSAdjointMonitorSet()`
1117: @*/
1118: PetscErrorCode TSAdjointMonitorSensi(TS ts, PetscInt step, PetscReal ptime, Vec v, PetscInt numcost, Vec *lambda, Vec *mu, PetscViewerAndFormat *vf)
1119: {
1120:   PetscViewer viewer = vf->viewer;

1122:   PetscFunctionBegin;
1124:   PetscCall(PetscViewerPushFormat(viewer, vf->format));
1125:   PetscCall(VecView(lambda[0], viewer));
1126:   PetscCall(PetscViewerPopFormat(viewer));
1127:   PetscFunctionReturn(PETSC_SUCCESS);
1128: }

1130: /*@C
1131:    TSAdjointMonitorSetFromOptions - Sets a monitor function and viewer appropriate for the type indicated by the user

1133:    Collective

1135:    Input Parameters:
1136: +  ts - `TS` object you wish to monitor
1137: .  name - the monitor type one is seeking
1138: .  help - message indicating what monitoring is done
1139: .  manual - manual page for the monitor
1140: .  monitor - the monitor function
1141: -  monitorsetup - a function that is called once ONLY if the user selected this monitor that may set additional features of the `TS` or `PetscViewer` objects

1143:    Level: developer

1145: .seealso: [](chapter_ts), `PetscOptionsGetViewer()`, `PetscOptionsGetReal()`, `PetscOptionsHasName()`, `PetscOptionsGetString()`,
1146:           `PetscOptionsGetIntArray()`, `PetscOptionsGetRealArray()`, `PetscOptionsBool()`
1147:           `PetscOptionsInt()`, `PetscOptionsString()`, `PetscOptionsReal()`, `PetscOptionsBool()`,
1148:           `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
1149:           `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
1150:           `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
1151:           `PetscOptionsFList()`, `PetscOptionsEList()`
1152: @*/
1153: PetscErrorCode TSAdjointMonitorSetFromOptions(TS ts, const char name[], const char help[], const char manual[], PetscErrorCode (*monitor)(TS, PetscInt, PetscReal, Vec, PetscInt, Vec *, Vec *, PetscViewerAndFormat *), PetscErrorCode (*monitorsetup)(TS, PetscViewerAndFormat *))
1154: {
1155:   PetscViewer       viewer;
1156:   PetscViewerFormat format;
1157:   PetscBool         flg;

1159:   PetscFunctionBegin;
1160:   PetscCall(PetscOptionsGetViewer(PetscObjectComm((PetscObject)ts), ((PetscObject)ts)->options, ((PetscObject)ts)->prefix, name, &viewer, &format, &flg));
1161:   if (flg) {
1162:     PetscViewerAndFormat *vf;
1163:     PetscCall(PetscViewerAndFormatCreate(viewer, format, &vf));
1164:     PetscCall(PetscObjectDereference((PetscObject)viewer));
1165:     if (monitorsetup) PetscCall((*monitorsetup)(ts, vf));
1166:     PetscCall(TSAdjointMonitorSet(ts, (PetscErrorCode(*)(TS, PetscInt, PetscReal, Vec, PetscInt, Vec *, Vec *, void *))monitor, vf, (PetscErrorCode(*)(void **))PetscViewerAndFormatDestroy));
1167:   }
1168:   PetscFunctionReturn(PETSC_SUCCESS);
1169: }

1171: /*@C
1172:    TSAdjointMonitorSet - Sets an ADDITIONAL function that is to be used at every
1173:    timestep to display the iteration's  progress.

1175:    Logically Collective

1177:    Input Parameters:
1178: +  ts - the `TS` context obtained from `TSCreate()`
1179: .  adjointmonitor - monitoring routine
1180: .  adjointmctx - [optional] user-defined context for private data for the
1181:              monitor routine (use NULL if no context is desired)
1182: -  adjointmonitordestroy - [optional] routine that frees monitor context
1183:           (may be NULL)

1185:    Calling sequence of monitor:
1186: $    int adjointmonitor(TS ts,PetscInt steps,PetscReal time,Vec u,PetscInt numcost,Vec *lambda, Vec *mu,void *adjointmctx)

1188: +    ts - the `TS` context
1189: .    steps - iteration number (after the final time step the monitor routine is called with a step of -1, this is at the final time which may have
1190:                                been interpolated to)
1191: .    time - current time
1192: .    u - current iterate
1193: .    numcost - number of cost functionos
1194: .    lambda - sensitivities to initial conditions
1195: .    mu - sensitivities to parameters
1196: -    adjointmctx - [optional] adjoint monitoring context

1198:    Level: intermediate

1200:    Note:
1201:    This routine adds an additional monitor to the list of monitors that
1202:    already has been loaded.

1204:    Fortran Note:
1205:    Only a single monitor function can be set for each TS object

1207: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, `TSAdjointMonitorCancel()`
1208: @*/
1209: PetscErrorCode TSAdjointMonitorSet(TS ts, PetscErrorCode (*adjointmonitor)(TS, PetscInt, PetscReal, Vec, PetscInt, Vec *, Vec *, void *), void *adjointmctx, PetscErrorCode (*adjointmdestroy)(void **))
1210: {
1211:   PetscInt  i;
1212:   PetscBool identical;

1214:   PetscFunctionBegin;
1216:   for (i = 0; i < ts->numbermonitors; i++) {
1217:     PetscCall(PetscMonitorCompare((PetscErrorCode(*)(void))adjointmonitor, adjointmctx, adjointmdestroy, (PetscErrorCode(*)(void))ts->adjointmonitor[i], ts->adjointmonitorcontext[i], ts->adjointmonitordestroy[i], &identical));
1218:     if (identical) PetscFunctionReturn(PETSC_SUCCESS);
1219:   }
1220:   PetscCheck(ts->numberadjointmonitors < MAXTSMONITORS, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many adjoint monitors set");
1221:   ts->adjointmonitor[ts->numberadjointmonitors]          = adjointmonitor;
1222:   ts->adjointmonitordestroy[ts->numberadjointmonitors]   = adjointmdestroy;
1223:   ts->adjointmonitorcontext[ts->numberadjointmonitors++] = (void *)adjointmctx;
1224:   PetscFunctionReturn(PETSC_SUCCESS);
1225: }

1227: /*@C
1228:    TSAdjointMonitorCancel - Clears all the adjoint monitors that have been set on a time-step object.

1230:    Logically Collective

1232:    Input Parameters:
1233: .  ts - the `TS` context obtained from `TSCreate()`

1235:    Notes:
1236:    There is no way to remove a single, specific monitor.

1238:    Level: intermediate

1240: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, `TSAdjointMonitorSet()`
1241: @*/
1242: PetscErrorCode TSAdjointMonitorCancel(TS ts)
1243: {
1244:   PetscInt i;

1246:   PetscFunctionBegin;
1248:   for (i = 0; i < ts->numberadjointmonitors; i++) {
1249:     if (ts->adjointmonitordestroy[i]) PetscCall((*ts->adjointmonitordestroy[i])(&ts->adjointmonitorcontext[i]));
1250:   }
1251:   ts->numberadjointmonitors = 0;
1252:   PetscFunctionReturn(PETSC_SUCCESS);
1253: }

1255: /*@C
1256:    TSAdjointMonitorDefault - the default monitor of adjoint computations

1258:    Level: intermediate

1260: .seealso: [](chapter_ts), `TS`, `TSAdjointSolve()`, `TSAdjointMonitorSet()`
1261: @*/
1262: PetscErrorCode TSAdjointMonitorDefault(TS ts, PetscInt step, PetscReal ptime, Vec v, PetscInt numcost, Vec *lambda, Vec *mu, PetscViewerAndFormat *vf)
1263: {
1264:   PetscViewer viewer = vf->viewer;

1266:   PetscFunctionBegin;
1268:   PetscCall(PetscViewerPushFormat(viewer, vf->format));
1269:   PetscCall(PetscViewerASCIIAddTab(viewer, ((PetscObject)ts)->tablevel));
1270:   PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " TS dt %g time %g%s", step, (double)ts->time_step, (double)ptime, ts->steprollback ? " (r)\n" : "\n"));
1271:   PetscCall(PetscViewerASCIISubtractTab(viewer, ((PetscObject)ts)->tablevel));
1272:   PetscCall(PetscViewerPopFormat(viewer));
1273:   PetscFunctionReturn(PETSC_SUCCESS);
1274: }

1276: /*@C
1277:    TSAdjointMonitorDrawSensi - Monitors progress of the adjoint `TS` solvers by calling
1278:    `VecView()` for the sensitivities to initial states at each timestep

1280:    Collective

1282:    Input Parameters:
1283: +  ts - the `TS` context
1284: .  step - current time-step
1285: .  ptime - current time
1286: .  u - current state
1287: .  numcost - number of cost functions
1288: .  lambda - sensitivities to initial conditions
1289: .  mu - sensitivities to parameters
1290: -  dummy - either a viewer or NULL

1292:    Level: intermediate

1294: .seealso: [](chapter_ts), `TSAdjointSolve()`, `TSAdjointMonitorSet()`, `TSAdjointMonitorDefault()`, `VecView()`
1295: @*/
1296: PetscErrorCode TSAdjointMonitorDrawSensi(TS ts, PetscInt step, PetscReal ptime, Vec u, PetscInt numcost, Vec *lambda, Vec *mu, void *dummy)
1297: {
1298:   TSMonitorDrawCtx ictx = (TSMonitorDrawCtx)dummy;
1299:   PetscDraw        draw;
1300:   PetscReal        xl, yl, xr, yr, h;
1301:   char             time[32];

1303:   PetscFunctionBegin;
1304:   if (!(((ictx->howoften > 0) && (!(step % ictx->howoften))) || ((ictx->howoften == -1) && ts->reason))) PetscFunctionReturn(PETSC_SUCCESS);

1306:   PetscCall(VecView(lambda[0], ictx->viewer));
1307:   PetscCall(PetscViewerDrawGetDraw(ictx->viewer, 0, &draw));
1308:   PetscCall(PetscSNPrintf(time, 32, "Timestep %d Time %g", (int)step, (double)ptime));
1309:   PetscCall(PetscDrawGetCoordinates(draw, &xl, &yl, &xr, &yr));
1310:   h = yl + .95 * (yr - yl);
1311:   PetscCall(PetscDrawStringCentered(draw, .5 * (xl + xr), h, PETSC_DRAW_BLACK, time));
1312:   PetscCall(PetscDrawFlush(draw));
1313:   PetscFunctionReturn(PETSC_SUCCESS);
1314: }

1316: /*
1317:    TSAdjointSetFromOptions - Sets various `TS` adjoint parameters from user options.

1319:    Collective

1321:    Input Parameter:
1322: .  ts - the `TS` context

1324:    Options Database Keys:
1325: +  -ts_adjoint_solve <yes,no> After solving the ODE/DAE solve the adjoint problem (requires -ts_save_trajectory)
1326: .  -ts_adjoint_monitor - print information at each adjoint time step
1327: -  -ts_adjoint_monitor_draw_sensi - monitor the sensitivity of the first cost function wrt initial conditions (lambda[0]) graphically

1329:    Level: developer

1331:    Note:
1332:     This is not normally called directly by users

1334: .seealso: [](chapter_ts), `TSSetSaveTrajectory()`, `TSTrajectorySetUp()`
1335: */
1336: PetscErrorCode TSAdjointSetFromOptions(TS ts, PetscOptionItems *PetscOptionsObject)
1337: {
1338:   PetscBool tflg, opt;

1340:   PetscFunctionBegin;
1342:   PetscOptionsHeadBegin(PetscOptionsObject, "TS Adjoint options");
1343:   tflg = ts->adjoint_solve ? PETSC_TRUE : PETSC_FALSE;
1344:   PetscCall(PetscOptionsBool("-ts_adjoint_solve", "Solve the adjoint problem immediately after solving the forward problem", "", tflg, &tflg, &opt));
1345:   if (opt) {
1346:     PetscCall(TSSetSaveTrajectory(ts));
1347:     ts->adjoint_solve = tflg;
1348:   }
1349:   PetscCall(TSAdjointMonitorSetFromOptions(ts, "-ts_adjoint_monitor", "Monitor adjoint timestep size", "TSAdjointMonitorDefault", TSAdjointMonitorDefault, NULL));
1350:   PetscCall(TSAdjointMonitorSetFromOptions(ts, "-ts_adjoint_monitor_sensi", "Monitor sensitivity in the adjoint computation", "TSAdjointMonitorSensi", TSAdjointMonitorSensi, NULL));
1351:   opt = PETSC_FALSE;
1352:   PetscCall(PetscOptionsName("-ts_adjoint_monitor_draw_sensi", "Monitor adjoint sensitivities (lambda only) graphically", "TSAdjointMonitorDrawSensi", &opt));
1353:   if (opt) {
1354:     TSMonitorDrawCtx ctx;
1355:     PetscInt         howoften = 1;

1357:     PetscCall(PetscOptionsInt("-ts_adjoint_monitor_draw_sensi", "Monitor adjoint sensitivities (lambda only) graphically", "TSAdjointMonitorDrawSensi", howoften, &howoften, NULL));
1358:     PetscCall(TSMonitorDrawCtxCreate(PetscObjectComm((PetscObject)ts), NULL, NULL, PETSC_DECIDE, PETSC_DECIDE, 300, 300, howoften, &ctx));
1359:     PetscCall(TSAdjointMonitorSet(ts, TSAdjointMonitorDrawSensi, ctx, (PetscErrorCode(*)(void **))TSMonitorDrawCtxDestroy));
1360:   }
1361:   PetscFunctionReturn(PETSC_SUCCESS);
1362: }

1364: /*@
1365:    TSAdjointStep - Steps one time step backward in the adjoint run

1367:    Collective

1369:    Input Parameter:
1370: .  ts - the `TS` context obtained from `TSCreate()`

1372:    Level: intermediate

1374: .seealso: [](chapter_ts), `TSAdjointSetUp()`, `TSAdjointSolve()`
1375: @*/
1376: PetscErrorCode TSAdjointStep(TS ts)
1377: {
1378:   DM dm;

1380:   PetscFunctionBegin;
1382:   PetscCall(TSGetDM(ts, &dm));
1383:   PetscCall(TSAdjointSetUp(ts));
1384:   ts->steps--; /* must decrease the step index before the adjoint step is taken. */

1386:   ts->reason     = TS_CONVERGED_ITERATING;
1387:   ts->ptime_prev = ts->ptime;
1388:   PetscCall(PetscLogEventBegin(TS_AdjointStep, ts, 0, 0, 0));
1389:   PetscUseTypeMethod(ts, adjointstep);
1390:   PetscCall(PetscLogEventEnd(TS_AdjointStep, ts, 0, 0, 0));
1391:   ts->adjoint_steps++;

1393:   if (ts->reason < 0) {
1394:     PetscCheck(!ts->errorifstepfailed, PetscObjectComm((PetscObject)ts), PETSC_ERR_NOT_CONVERGED, "TSAdjointStep has failed due to %s", TSConvergedReasons[ts->reason]);
1395:   } else if (!ts->reason) {
1396:     if (ts->adjoint_steps >= ts->adjoint_max_steps) ts->reason = TS_CONVERGED_ITS;
1397:   }
1398:   PetscFunctionReturn(PETSC_SUCCESS);
1399: }

1401: /*@
1402:    TSAdjointSolve - Solves the discrete ajoint problem for an ODE/DAE

1404:    Collective
1405: `
1406:    Input Parameter:
1407: .  ts - the `TS` context obtained from `TSCreate()`

1409:    Options Database Key:
1410: . -ts_adjoint_view_solution <viewerinfo> - views the first gradient with respect to the initial values

1412:    Level: intermediate

1414:    Notes:
1415:    This must be called after a call to `TSSolve()` that solves the forward problem

1417:    By default this will integrate back to the initial time, one can use `TSAdjointSetSteps()` to step back to a later time

1419: .seealso: [](chapter_ts), `TSAdjointSolve()`, `TSCreate()`, `TSSetCostGradients()`, `TSSetSolution()`, `TSAdjointStep()`
1420: @*/
1421: PetscErrorCode TSAdjointSolve(TS ts)
1422: {
1423:   static PetscBool cite = PETSC_FALSE;
1424: #if defined(TSADJOINT_STAGE)
1425:   PetscLogStage adjoint_stage;
1426: #endif

1428:   PetscFunctionBegin;
1430:   PetscCall(PetscCitationsRegister("@article{Zhang2022tsadjoint,\n"
1431:                                    "  title         = {{PETSc TSAdjoint: A Discrete Adjoint ODE Solver for First-Order and Second-Order Sensitivity Analysis}},\n"
1432:                                    "  author        = {Zhang, Hong and Constantinescu, Emil M.  and Smith, Barry F.},\n"
1433:                                    "  journal       = {SIAM Journal on Scientific Computing},\n"
1434:                                    "  volume        = {44},\n"
1435:                                    "  number        = {1},\n"
1436:                                    "  pages         = {C1-C24},\n"
1437:                                    "  doi           = {10.1137/21M140078X},\n"
1438:                                    "  year          = {2022}\n}\n",
1439:                                    &cite));
1440: #if defined(TSADJOINT_STAGE)
1441:   PetscCall(PetscLogStageRegister("TSAdjoint", &adjoint_stage));
1442:   PetscCall(PetscLogStagePush(adjoint_stage));
1443: #endif
1444:   PetscCall(TSAdjointSetUp(ts));

1446:   /* reset time step and iteration counters */
1447:   ts->adjoint_steps     = 0;
1448:   ts->ksp_its           = 0;
1449:   ts->snes_its          = 0;
1450:   ts->num_snes_failures = 0;
1451:   ts->reject            = 0;
1452:   ts->reason            = TS_CONVERGED_ITERATING;

1454:   if (!ts->adjoint_max_steps) ts->adjoint_max_steps = ts->steps;
1455:   if (ts->adjoint_steps >= ts->adjoint_max_steps) ts->reason = TS_CONVERGED_ITS;

1457:   while (!ts->reason) {
1458:     PetscCall(TSTrajectoryGet(ts->trajectory, ts, ts->steps, &ts->ptime));
1459:     PetscCall(TSAdjointMonitor(ts, ts->steps, ts->ptime, ts->vec_sol, ts->numcost, ts->vecs_sensi, ts->vecs_sensip));
1460:     PetscCall(TSAdjointEventHandler(ts));
1461:     PetscCall(TSAdjointStep(ts));
1462:     if ((ts->vec_costintegral || ts->quadraturets) && !ts->costintegralfwd) PetscCall(TSAdjointCostIntegral(ts));
1463:   }
1464:   if (!ts->steps) {
1465:     PetscCall(TSTrajectoryGet(ts->trajectory, ts, ts->steps, &ts->ptime));
1466:     PetscCall(TSAdjointMonitor(ts, ts->steps, ts->ptime, ts->vec_sol, ts->numcost, ts->vecs_sensi, ts->vecs_sensip));
1467:   }
1468:   ts->solvetime = ts->ptime;
1469:   PetscCall(TSTrajectoryViewFromOptions(ts->trajectory, NULL, "-ts_trajectory_view"));
1470:   PetscCall(VecViewFromOptions(ts->vecs_sensi[0], (PetscObject)ts, "-ts_adjoint_view_solution"));
1471:   ts->adjoint_max_steps = 0;
1472: #if defined(TSADJOINT_STAGE)
1473:   PetscCall(PetscLogStagePop());
1474: #endif
1475:   PetscFunctionReturn(PETSC_SUCCESS);
1476: }

1478: /*@C
1479:    TSAdjointMonitor - Runs all user-provided adjoint monitor routines set using `TSAdjointMonitorSet()`

1481:    Collective

1483:    Input Parameters:
1484: +  ts - time stepping context obtained from `TSCreate()`
1485: .  step - step number that has just completed
1486: .  ptime - model time of the state
1487: .  u - state at the current model time
1488: .  numcost - number of cost functions (dimension of lambda  or mu)
1489: .  lambda - vectors containing the gradients of the cost functions with respect to the ODE/DAE solution variables
1490: -  mu - vectors containing the gradients of the cost functions with respect to the problem parameters

1492:    Level: developer

1494:    Note:
1495:    `TSAdjointMonitor()` is typically used automatically within the time stepping implementations.
1496:    Users would almost never call this routine directly.

1498: .seealso: `TSAdjointMonitorSet()`, `TSAdjointSolve()`
1499: @*/
1500: PetscErrorCode TSAdjointMonitor(TS ts, PetscInt step, PetscReal ptime, Vec u, PetscInt numcost, Vec *lambda, Vec *mu)
1501: {
1502:   PetscInt i, n = ts->numberadjointmonitors;

1504:   PetscFunctionBegin;
1507:   PetscCall(VecLockReadPush(u));
1508:   for (i = 0; i < n; i++) PetscCall((*ts->adjointmonitor[i])(ts, step, ptime, u, numcost, lambda, mu, ts->adjointmonitorcontext[i]));
1509:   PetscCall(VecLockReadPop(u));
1510:   PetscFunctionReturn(PETSC_SUCCESS);
1511: }

1513: /*@
1514:  TSAdjointCostIntegral - Evaluate the cost integral in the adjoint run.

1516:  Collective

1518:  Input Parameter:
1519:  .  ts - time stepping context

1521:  Level: advanced

1523:  Notes:
1524:  This function cannot be called until `TSAdjointStep()` has been completed.

1526:  .seealso: [](chapter_ts), `TSAdjointSolve()`, `TSAdjointStep()`
1527:  @*/
1528: PetscErrorCode TSAdjointCostIntegral(TS ts)
1529: {
1530:   PetscFunctionBegin;
1532:   PetscUseTypeMethod(ts, adjointintegral);
1533:   PetscFunctionReturn(PETSC_SUCCESS);
1534: }

1536: /* ------------------ Forward (tangent linear) sensitivity  ------------------*/

1538: /*@
1539:   TSForwardSetUp - Sets up the internal data structures for the later use
1540:   of forward sensitivity analysis

1542:   Collective

1544:   Input Parameter:
1545: . ts - the `TS` context obtained from `TSCreate()`

1547:   Level: advanced

1549: .seealso: [](chapter_ts), `TS`, `TSCreate()`, `TSDestroy()`, `TSSetUp()`
1550: @*/
1551: PetscErrorCode TSForwardSetUp(TS ts)
1552: {
1553:   PetscFunctionBegin;
1555:   if (ts->forwardsetupcalled) PetscFunctionReturn(PETSC_SUCCESS);
1556:   PetscTryTypeMethod(ts, forwardsetup);
1557:   PetscCall(VecDuplicate(ts->vec_sol, &ts->vec_sensip_col));
1558:   ts->forwardsetupcalled = PETSC_TRUE;
1559:   PetscFunctionReturn(PETSC_SUCCESS);
1560: }

1562: /*@
1563:   TSForwardReset - Reset the internal data structures used by forward sensitivity analysis

1565:   Collective

1567:   Input Parameter:
1568: . ts - the `TS` context obtained from `TSCreate()`

1570:   Level: advanced

1572: .seealso: [](chapter_ts), `TSCreate()`, `TSDestroy()`, `TSForwardSetUp()`
1573: @*/
1574: PetscErrorCode TSForwardReset(TS ts)
1575: {
1576:   TS quadts = ts->quadraturets;

1578:   PetscFunctionBegin;
1580:   PetscTryTypeMethod(ts, forwardreset);
1581:   PetscCall(MatDestroy(&ts->mat_sensip));
1582:   if (quadts) PetscCall(MatDestroy(&quadts->mat_sensip));
1583:   PetscCall(VecDestroy(&ts->vec_sensip_col));
1584:   ts->forward_solve      = PETSC_FALSE;
1585:   ts->forwardsetupcalled = PETSC_FALSE;
1586:   PetscFunctionReturn(PETSC_SUCCESS);
1587: }

1589: /*@
1590:   TSForwardSetIntegralGradients - Set the vectors holding forward sensitivities of the integral term.

1592:   Input Parameters:
1593: + ts - the `TS` context obtained from `TSCreate()`
1594: . numfwdint - number of integrals
1595: - vp - the vectors containing the gradients for each integral w.r.t. parameters

1597:   Level: deprecated

1599: .seealso: [](chapter_ts), `TSForwardGetSensitivities()`, `TSForwardSetIntegralGradients()`, `TSForwardGetIntegralGradients()`, `TSForwardStep()`
1600: @*/
1601: PetscErrorCode TSForwardSetIntegralGradients(TS ts, PetscInt numfwdint, Vec *vp)
1602: {
1603:   PetscFunctionBegin;
1605:   PetscCheck(!ts->numcost || ts->numcost == numfwdint, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "The number of cost functions (2nd parameter of TSSetCostIntegrand()) is inconsistent with the one set by TSSetCostIntegrand()");
1606:   if (!ts->numcost) ts->numcost = numfwdint;

1608:   ts->vecs_integral_sensip = vp;
1609:   PetscFunctionReturn(PETSC_SUCCESS);
1610: }

1612: /*@
1613:   TSForwardGetIntegralGradients - Returns the forward sensitivities ofthe integral term.

1615:   Input Parameter:
1616: . ts - the `TS` context obtained from `TSCreate()`

1618:   Output Parameter:
1619: . vp - the vectors containing the gradients for each integral w.r.t. parameters

1621:   Level: deprecated

1623: .seealso: [](chapter_ts), `TSForwardSetSensitivities()`, `TSForwardSetIntegralGradients()`, `TSForwardGetIntegralGradients()`, `TSForwardStep()`
1624: @*/
1625: PetscErrorCode TSForwardGetIntegralGradients(TS ts, PetscInt *numfwdint, Vec **vp)
1626: {
1627:   PetscFunctionBegin;
1630:   if (numfwdint) *numfwdint = ts->numcost;
1631:   if (vp) *vp = ts->vecs_integral_sensip;
1632:   PetscFunctionReturn(PETSC_SUCCESS);
1633: }

1635: /*@
1636:   TSForwardStep - Compute the forward sensitivity for one time step.

1638:   Collective

1640:   Input Parameter:
1641: . ts - time stepping context

1643:   Level: advanced

1645:   Notes:
1646:   This function cannot be called until `TSStep()` has been completed.

1648: .seealso: [](chapter_ts), `TSForwardSetSensitivities()`, `TSForwardGetSensitivities()`, `TSForwardSetIntegralGradients()`, `TSForwardGetIntegralGradients()`, `TSForwardSetUp()`
1649: @*/
1650: PetscErrorCode TSForwardStep(TS ts)
1651: {
1652:   PetscFunctionBegin;
1654:   PetscCall(PetscLogEventBegin(TS_ForwardStep, ts, 0, 0, 0));
1655:   PetscUseTypeMethod(ts, forwardstep);
1656:   PetscCall(PetscLogEventEnd(TS_ForwardStep, ts, 0, 0, 0));
1657:   PetscCheck(ts->reason >= 0 || !ts->errorifstepfailed, PetscObjectComm((PetscObject)ts), PETSC_ERR_NOT_CONVERGED, "TSFowardStep has failed due to %s", TSConvergedReasons[ts->reason]);
1658:   PetscFunctionReturn(PETSC_SUCCESS);
1659: }

1661: /*@
1662:   TSForwardSetSensitivities - Sets the initial value of the trajectory sensitivities of solution  w.r.t. the problem parameters and initial values.

1664:   Logically Collective

1666:   Input Parameters:
1667: + ts - the `TS` context obtained from `TSCreate()`
1668: . nump - number of parameters
1669: - Smat - sensitivities with respect to the parameters, the number of entries in these vectors is the same as the number of parameters

1671:   Level: beginner

1673:   Notes:
1674:   Forward sensitivity is also called 'trajectory sensitivity' in some fields such as power systems.
1675:   This function turns on a flag to trigger `TSSolve()` to compute forward sensitivities automatically.
1676:   You must call this function before `TSSolve()`.
1677:   The entries in the sensitivity matrix must be correctly initialized with the values S = dy/dp|startingtime.

1679: .seealso: [](chapter_ts), `TSForwardGetSensitivities()`, `TSForwardSetIntegralGradients()`, `TSForwardGetIntegralGradients()`, `TSForwardStep()`
1680: @*/
1681: PetscErrorCode TSForwardSetSensitivities(TS ts, PetscInt nump, Mat Smat)
1682: {
1683:   PetscFunctionBegin;
1686:   ts->forward_solve = PETSC_TRUE;
1687:   if (nump == PETSC_DEFAULT) {
1688:     PetscCall(MatGetSize(Smat, NULL, &ts->num_parameters));
1689:   } else ts->num_parameters = nump;
1690:   PetscCall(PetscObjectReference((PetscObject)Smat));
1691:   PetscCall(MatDestroy(&ts->mat_sensip));
1692:   ts->mat_sensip = Smat;
1693:   PetscFunctionReturn(PETSC_SUCCESS);
1694: }

1696: /*@
1697:   TSForwardGetSensitivities - Returns the trajectory sensitivities

1699:   Not Collective, but Smat returned is parallel if ts is parallel

1701:   Output Parameters:
1702: + ts - the `TS` context obtained from `TSCreate()`
1703: . nump - number of parameters
1704: - Smat - sensitivities with respect to the parameters, the number of entries in these vectors is the same as the number of parameters

1706:   Level: intermediate

1708: .seealso: [](chapter_ts), `TSForwardSetSensitivities()`, `TSForwardSetIntegralGradients()`, `TSForwardGetIntegralGradients()`, `TSForwardStep()`
1709: @*/
1710: PetscErrorCode TSForwardGetSensitivities(TS ts, PetscInt *nump, Mat *Smat)
1711: {
1712:   PetscFunctionBegin;
1714:   if (nump) *nump = ts->num_parameters;
1715:   if (Smat) *Smat = ts->mat_sensip;
1716:   PetscFunctionReturn(PETSC_SUCCESS);
1717: }

1719: /*@
1720:    TSForwardCostIntegral - Evaluate the cost integral in the forward run.

1722:    Collective

1724:    Input Parameter:
1725: .  ts - time stepping context

1727:    Level: advanced

1729:    Note:
1730:    This function cannot be called until `TSStep()` has been completed.

1732: .seealso: [](chapter_ts), `TS`, `TSSolve()`, `TSAdjointCostIntegral()`
1733: @*/
1734: PetscErrorCode TSForwardCostIntegral(TS ts)
1735: {
1736:   PetscFunctionBegin;
1738:   PetscUseTypeMethod(ts, forwardintegral);
1739:   PetscFunctionReturn(PETSC_SUCCESS);
1740: }

1742: /*@
1743:   TSForwardSetInitialSensitivities - Set initial values for tangent linear sensitivities

1745:   Collective

1747:   Input Parameters:
1748: + ts - the `TS` context obtained from `TSCreate()`
1749: - didp - parametric sensitivities of the initial condition

1751:   Level: intermediate

1753:   Notes:
1754:   `TSSolve()` allows users to pass the initial solution directly to `TS`. But the tangent linear variables cannot be initialized in this way.
1755:    This function is used to set initial values for tangent linear variables.

1757: .seealso: [](chapter_ts), `TS`, `TSForwardSetSensitivities()`
1758: @*/
1759: PetscErrorCode TSForwardSetInitialSensitivities(TS ts, Mat didp)
1760: {
1761:   PetscFunctionBegin;
1764:   if (!ts->mat_sensip) PetscCall(TSForwardSetSensitivities(ts, PETSC_DEFAULT, didp));
1765:   PetscFunctionReturn(PETSC_SUCCESS);
1766: }

1768: /*@
1769:    TSForwardGetStages - Get the number of stages and the tangent linear sensitivities at the intermediate stages

1771:    Input Parameter:
1772: .  ts - the `TS` context obtained from `TSCreate()`

1774:    Output Parameters:
1775: +  ns - number of stages
1776: -  S - tangent linear sensitivities at the intermediate stages

1778:    Level: advanced

1780: .seealso: `TS`
1781: @*/
1782: PetscErrorCode TSForwardGetStages(TS ts, PetscInt *ns, Mat **S)
1783: {
1784:   PetscFunctionBegin;

1787:   if (!ts->ops->getstages) *S = NULL;
1788:   else PetscUseTypeMethod(ts, forwardgetstages, ns, S);
1789:   PetscFunctionReturn(PETSC_SUCCESS);
1790: }

1792: /*@
1793:    TSCreateQuadratureTS - Create a sub-`TS` that evaluates integrals over time

1795:    Input Parameters:
1796: +  ts - the `TS` context obtained from `TSCreate()`
1797: -  fwd - flag indicating whether to evaluate cost integral in the forward run or the adjoint run

1799:    Output Parameters:
1800: .  quadts - the child `TS` context

1802:    Level: intermediate

1804: .seealso: [](chapter_ts), `TSGetQuadratureTS()`
1805: @*/
1806: PetscErrorCode TSCreateQuadratureTS(TS ts, PetscBool fwd, TS *quadts)
1807: {
1808:   char prefix[128];

1810:   PetscFunctionBegin;
1813:   PetscCall(TSDestroy(&ts->quadraturets));
1814:   PetscCall(TSCreate(PetscObjectComm((PetscObject)ts), &ts->quadraturets));
1815:   PetscCall(PetscObjectIncrementTabLevel((PetscObject)ts->quadraturets, (PetscObject)ts, 1));
1816:   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%squad_", ((PetscObject)ts)->prefix ? ((PetscObject)ts)->prefix : ""));
1817:   PetscCall(TSSetOptionsPrefix(ts->quadraturets, prefix));
1818:   *quadts = ts->quadraturets;

1820:   if (ts->numcost) {
1821:     PetscCall(VecCreateSeq(PETSC_COMM_SELF, ts->numcost, &(*quadts)->vec_sol));
1822:   } else {
1823:     PetscCall(VecCreateSeq(PETSC_COMM_SELF, 1, &(*quadts)->vec_sol));
1824:   }
1825:   ts->costintegralfwd = fwd;
1826:   PetscFunctionReturn(PETSC_SUCCESS);
1827: }

1829: /*@
1830:    TSGetQuadratureTS - Return the sub-`TS` that evaluates integrals over time

1832:    Input Parameter:
1833: .  ts - the `TS` context obtained from `TSCreate()`

1835:    Output Parameters:
1836: +  fwd - flag indicating whether to evaluate cost integral in the forward run or the adjoint run
1837: -  quadts - the child `TS` context

1839:    Level: intermediate

1841: .seealso: [](chapter_ts), `TSCreateQuadratureTS()`
1842: @*/
1843: PetscErrorCode TSGetQuadratureTS(TS ts, PetscBool *fwd, TS *quadts)
1844: {
1845:   PetscFunctionBegin;
1847:   if (fwd) *fwd = ts->costintegralfwd;
1848:   if (quadts) *quadts = ts->quadraturets;
1849:   PetscFunctionReturn(PETSC_SUCCESS);
1850: }

1852: /*@
1853:    TSComputeSNESJacobian - Compute the Jacobian needed for the `SNESSolve()` in `TS`

1855:    Collective

1857:    Input Parameters:
1858: +  ts - the `TS` context obtained from `TSCreate()`
1859: -  x - state vector

1861:    Output Parameters:
1862: +  J - Jacobian matrix
1863: -  Jpre - preconditioning matrix for J (may be same as J)

1865:    Level: developer

1867:    Note:
1868:    Uses finite differencing when `TS` Jacobian is not available.

1870: .seealso: `SNES`, `TS`, `SNESSetJacobian()`, TSSetRHSJacobian()`, `TSSetIJacobian()`
1871: @*/
1872: PetscErrorCode TSComputeSNESJacobian(TS ts, Vec x, Mat J, Mat Jpre)
1873: {
1874:   SNES snes                                          = ts->snes;
1875:   PetscErrorCode (*jac)(SNES, Vec, Mat, Mat, void *) = NULL;

1877:   PetscFunctionBegin;
1878:   /*
1879:     Unlike implicit methods, explicit methods do not have SNESMatFDColoring in the snes object
1880:     because SNESSolve() has not been called yet; so querying SNESMatFDColoring does not work for
1881:     explicit methods. Instead, we check the Jacobian compute function directly to determine if FD
1882:     coloring is used.
1883:   */
1884:   PetscCall(SNESGetJacobian(snes, NULL, NULL, &jac, NULL));
1885:   if (jac == SNESComputeJacobianDefaultColor) {
1886:     Vec f;
1887:     PetscCall(SNESSetSolution(snes, x));
1888:     PetscCall(SNESGetFunction(snes, &f, NULL, NULL));
1889:     /* Force MatFDColoringApply to evaluate the SNES residual function for the base vector */
1890:     PetscCall(SNESComputeFunction(snes, x, f));
1891:   }
1892:   PetscCall(SNESComputeJacobian(snes, x, J, Jpre));
1893:   PetscFunctionReturn(PETSC_SUCCESS);
1894: }