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: }