xref: /petsc/src/ts/impls/implicit/theta/theta.c (revision 03047865b8d8757cf1cf9cda45785c1537b01dc1)
1 /*
2   Code for timestepping with implicit Theta method
3 */
4 #include <petsc/private/tsimpl.h> /*I   "petscts.h"   I*/
5 #include <petscsnes.h>
6 #include <petscdm.h>
7 #include <petscmat.h>
8 
9 typedef struct {
10   /* context for time stepping */
11   PetscReal    stage_time;
12   Vec          Stages[2];   /* Storage for stage solutions */
13   Vec          X0, X, Xdot; /* Storage for u^n, u^n + dt a_{11} k_1, and time derivative u^{n+1}_t */
14   Vec          affine;      /* Affine vector needed for residual at beginning of step in endpoint formulation */
15   PetscReal    Theta;
16   PetscReal    shift; /* Shift parameter for SNES Jacobian, used by forward, TLM and adjoint */
17   PetscInt     order;
18   PetscBool    endpoint;
19   PetscBool    extrapolate;
20   TSStepStatus status;
21   Vec          VecCostIntegral0; /* Backup for roll-backs due to events, used by cost integral */
22   PetscReal    ptime0;           /* Backup for ts->ptime, the start time of current time step, used by TLM and cost integral */
23   PetscReal    time_step0;       /* Backup for ts->timestep, the step size of current time step, used by TLM and cost integral*/
24 
25   /* context for sensitivity analysis */
26   PetscInt num_tlm;               /* Total number of tangent linear equations */
27   Vec     *VecsDeltaLam;          /* Increment of the adjoint sensitivity w.r.t IC at stage */
28   Vec     *VecsDeltaMu;           /* Increment of the adjoint sensitivity w.r.t P at stage */
29   Vec     *VecsSensiTemp;         /* Vector to be multiplied with Jacobian transpose */
30   Mat      MatFwdStages[2];       /* TLM Stages */
31   Mat      MatDeltaFwdSensip;     /* Increment of the forward sensitivity at stage */
32   Vec      VecDeltaFwdSensipCol;  /* Working vector for holding one column of the sensitivity matrix */
33   Mat      MatFwdSensip0;         /* backup for roll-backs due to events */
34   Mat      MatIntegralSensipTemp; /* Working vector for forward integral sensitivity */
35   Mat      MatIntegralSensip0;    /* backup for roll-backs due to events */
36   Vec     *VecsDeltaLam2;         /* Increment of the 2nd-order adjoint sensitivity w.r.t IC at stage */
37   Vec     *VecsDeltaMu2;          /* Increment of the 2nd-order adjoint sensitivity w.r.t P at stage */
38   Vec     *VecsSensi2Temp;        /* Working vectors that holds the residual for the second-order adjoint */
39   Vec     *VecsAffine;            /* Working vectors to store residuals */
40   /* context for error estimation */
41   Vec vec_sol_prev;
42   Vec vec_lte_work;
43 } TS_Theta;
44 
TSThetaGetX0AndXdot(TS ts,DM dm,Vec * X0,Vec * Xdot)45 static PetscErrorCode TSThetaGetX0AndXdot(TS ts, DM dm, Vec *X0, Vec *Xdot)
46 {
47   TS_Theta *th = (TS_Theta *)ts->data;
48 
49   PetscFunctionBegin;
50   if (X0) {
51     if (dm && dm != ts->dm) PetscCall(DMGetNamedGlobalVector(dm, "TSTheta_X0", X0));
52     else *X0 = ts->vec_sol;
53   }
54   if (Xdot) {
55     if (dm && dm != ts->dm) PetscCall(DMGetNamedGlobalVector(dm, "TSTheta_Xdot", Xdot));
56     else *Xdot = th->Xdot;
57   }
58   PetscFunctionReturn(PETSC_SUCCESS);
59 }
60 
TSThetaRestoreX0AndXdot(TS ts,DM dm,Vec * X0,Vec * Xdot)61 static PetscErrorCode TSThetaRestoreX0AndXdot(TS ts, DM dm, Vec *X0, Vec *Xdot)
62 {
63   PetscFunctionBegin;
64   if (X0) {
65     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSTheta_X0", X0));
66   }
67   if (Xdot) {
68     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSTheta_Xdot", Xdot));
69   }
70   PetscFunctionReturn(PETSC_SUCCESS);
71 }
72 
DMCoarsenHook_TSTheta(DM fine,DM coarse,PetscCtx ctx)73 static PetscErrorCode DMCoarsenHook_TSTheta(DM fine, DM coarse, PetscCtx ctx)
74 {
75   PetscFunctionBegin;
76   PetscFunctionReturn(PETSC_SUCCESS);
77 }
78 
DMRestrictHook_TSTheta(DM fine,Mat restrct,Vec rscale,Mat inject,DM coarse,PetscCtx ctx)79 static PetscErrorCode DMRestrictHook_TSTheta(DM fine, Mat restrct, Vec rscale, Mat inject, DM coarse, PetscCtx ctx)
80 {
81   TS  ts = (TS)ctx;
82   Vec X0, Xdot, X0_c, Xdot_c;
83 
84   PetscFunctionBegin;
85   PetscCall(TSThetaGetX0AndXdot(ts, fine, &X0, &Xdot));
86   PetscCall(TSThetaGetX0AndXdot(ts, coarse, &X0_c, &Xdot_c));
87   PetscCall(MatRestrict(restrct, X0, X0_c));
88   PetscCall(MatRestrict(restrct, Xdot, Xdot_c));
89   PetscCall(VecPointwiseMult(X0_c, rscale, X0_c));
90   PetscCall(VecPointwiseMult(Xdot_c, rscale, Xdot_c));
91   PetscCall(TSThetaRestoreX0AndXdot(ts, fine, &X0, &Xdot));
92   PetscCall(TSThetaRestoreX0AndXdot(ts, coarse, &X0_c, &Xdot_c));
93   PetscFunctionReturn(PETSC_SUCCESS);
94 }
95 
DMSubDomainHook_TSTheta(DM dm,DM subdm,PetscCtx ctx)96 static PetscErrorCode DMSubDomainHook_TSTheta(DM dm, DM subdm, PetscCtx ctx)
97 {
98   PetscFunctionBegin;
99   PetscFunctionReturn(PETSC_SUCCESS);
100 }
101 
DMSubDomainRestrictHook_TSTheta(DM dm,VecScatter gscat,VecScatter lscat,DM subdm,PetscCtx ctx)102 static PetscErrorCode DMSubDomainRestrictHook_TSTheta(DM dm, VecScatter gscat, VecScatter lscat, DM subdm, PetscCtx ctx)
103 {
104   TS  ts = (TS)ctx;
105   Vec X0, Xdot, X0_sub, Xdot_sub;
106 
107   PetscFunctionBegin;
108   PetscCall(TSThetaGetX0AndXdot(ts, dm, &X0, &Xdot));
109   PetscCall(TSThetaGetX0AndXdot(ts, subdm, &X0_sub, &Xdot_sub));
110 
111   PetscCall(VecScatterBegin(gscat, X0, X0_sub, INSERT_VALUES, SCATTER_FORWARD));
112   PetscCall(VecScatterEnd(gscat, X0, X0_sub, INSERT_VALUES, SCATTER_FORWARD));
113 
114   PetscCall(VecScatterBegin(gscat, Xdot, Xdot_sub, INSERT_VALUES, SCATTER_FORWARD));
115   PetscCall(VecScatterEnd(gscat, Xdot, Xdot_sub, INSERT_VALUES, SCATTER_FORWARD));
116 
117   PetscCall(TSThetaRestoreX0AndXdot(ts, dm, &X0, &Xdot));
118   PetscCall(TSThetaRestoreX0AndXdot(ts, subdm, &X0_sub, &Xdot_sub));
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
TSThetaEvaluateCostIntegral(TS ts)122 static PetscErrorCode TSThetaEvaluateCostIntegral(TS ts)
123 {
124   TS_Theta *th     = (TS_Theta *)ts->data;
125   TS        quadts = ts->quadraturets;
126 
127   PetscFunctionBegin;
128   if (th->endpoint) {
129     /* Evolve ts->vec_costintegral to compute integrals */
130     if (th->Theta != 1.0) {
131       PetscCall(TSComputeRHSFunction(quadts, th->ptime0, th->X0, ts->vec_costintegrand));
132       PetscCall(VecAXPY(quadts->vec_sol, th->time_step0 * (1.0 - th->Theta), ts->vec_costintegrand));
133     }
134     PetscCall(TSComputeRHSFunction(quadts, ts->ptime, ts->vec_sol, ts->vec_costintegrand));
135     PetscCall(VecAXPY(quadts->vec_sol, th->time_step0 * th->Theta, ts->vec_costintegrand));
136   } else {
137     PetscCall(TSComputeRHSFunction(quadts, th->stage_time, th->X, ts->vec_costintegrand));
138     PetscCall(VecAXPY(quadts->vec_sol, th->time_step0, ts->vec_costintegrand));
139   }
140   PetscFunctionReturn(PETSC_SUCCESS);
141 }
142 
TSForwardCostIntegral_Theta(TS ts)143 static PetscErrorCode TSForwardCostIntegral_Theta(TS ts)
144 {
145   TS_Theta *th     = (TS_Theta *)ts->data;
146   TS        quadts = ts->quadraturets;
147 
148   PetscFunctionBegin;
149   /* backup cost integral */
150   PetscCall(VecCopy(quadts->vec_sol, th->VecCostIntegral0));
151   PetscCall(TSThetaEvaluateCostIntegral(ts));
152   PetscFunctionReturn(PETSC_SUCCESS);
153 }
154 
TSAdjointCostIntegral_Theta(TS ts)155 static PetscErrorCode TSAdjointCostIntegral_Theta(TS ts)
156 {
157   TS_Theta *th = (TS_Theta *)ts->data;
158 
159   PetscFunctionBegin;
160   /* Like TSForwardCostIntegral(), the adjoint cost integral evaluation relies on ptime0 and time_step0. */
161   th->ptime0     = ts->ptime + ts->time_step;
162   th->time_step0 = -ts->time_step;
163   PetscCall(TSThetaEvaluateCostIntegral(ts));
164   PetscFunctionReturn(PETSC_SUCCESS);
165 }
166 
TSTheta_SNESSolve(TS ts,Vec b,Vec x)167 static PetscErrorCode TSTheta_SNESSolve(TS ts, Vec b, Vec x)
168 {
169   PetscInt nits, lits;
170 
171   PetscFunctionBegin;
172   PetscCall(SNESSolve(ts->snes, b, x));
173   PetscCall(SNESGetIterationNumber(ts->snes, &nits));
174   PetscCall(SNESGetLinearSolveIterations(ts->snes, &lits));
175   ts->snes_its += nits;
176   ts->ksp_its += lits;
177   PetscFunctionReturn(PETSC_SUCCESS);
178 }
179 
TSResizeRegister_Theta(TS ts,PetscBool reg)180 static PetscErrorCode TSResizeRegister_Theta(TS ts, PetscBool reg)
181 {
182   TS_Theta *th = (TS_Theta *)ts->data;
183 
184   PetscFunctionBegin;
185   if (reg) {
186     PetscCall(TSResizeRegisterVec(ts, "ts:theta:sol_prev", th->vec_sol_prev));
187     PetscCall(TSResizeRegisterVec(ts, "ts:theta:X0", th->X0));
188   } else {
189     PetscCall(TSResizeRetrieveVec(ts, "ts:theta:sol_prev", &th->vec_sol_prev));
190     PetscCall(PetscObjectReference((PetscObject)th->vec_sol_prev));
191     PetscCall(TSResizeRetrieveVec(ts, "ts:theta:X0", &th->X0));
192     PetscCall(PetscObjectReference((PetscObject)th->X0));
193   }
194   PetscFunctionReturn(PETSC_SUCCESS);
195 }
196 
TSStep_Theta(TS ts)197 static PetscErrorCode TSStep_Theta(TS ts)
198 {
199   TS_Theta *th         = (TS_Theta *)ts->data;
200   PetscInt  rejections = 0;
201   PetscBool stageok, accept = PETSC_TRUE;
202   PetscReal next_time_step = ts->time_step;
203 
204   PetscFunctionBegin;
205   if (!ts->steprollback) {
206     if (th->vec_sol_prev) PetscCall(VecCopy(th->X0, th->vec_sol_prev));
207     PetscCall(VecCopy(ts->vec_sol, th->X0));
208   }
209 
210   th->status = TS_STEP_INCOMPLETE;
211   while (!ts->reason && th->status != TS_STEP_COMPLETE) {
212     th->shift      = 1 / (th->Theta * ts->time_step);
213     th->stage_time = ts->ptime + (th->endpoint ? (PetscReal)1 : th->Theta) * ts->time_step;
214     PetscCall(VecCopy(th->X0, th->X));
215     if (th->extrapolate && !ts->steprestart) PetscCall(VecAXPY(th->X, 1 / th->shift, th->Xdot));
216     if (th->endpoint) { /* This formulation assumes linear time-independent mass matrix */
217       if (!th->affine) PetscCall(VecDuplicate(ts->vec_sol, &th->affine));
218       PetscCall(VecZeroEntries(th->Xdot));
219       PetscCall(TSComputeIFunction(ts, ts->ptime, th->X0, th->Xdot, th->affine, PETSC_FALSE));
220       PetscCall(VecScale(th->affine, (th->Theta - 1) / th->Theta));
221     }
222     PetscCall(TSPreStage(ts, th->stage_time));
223     PetscCall(TSTheta_SNESSolve(ts, th->endpoint ? th->affine : NULL, th->X));
224     PetscCall(TSPostStage(ts, th->stage_time, 0, &th->X));
225     PetscCall(TSAdaptCheckStage(ts->adapt, ts, th->stage_time, th->X, &stageok));
226     if (!stageok) goto reject_step;
227 
228     if (th->endpoint) {
229       PetscCall(VecCopy(th->X, ts->vec_sol));
230     } else {
231       PetscCall(VecAXPBYPCZ(th->Xdot, -th->shift, th->shift, 0, th->X0, th->X)); /* th->Xdot is needed by TSInterpolate_Theta */
232       if (th->Theta == 1.0) PetscCall(VecCopy(th->X, ts->vec_sol));              /* BEULER, stage already checked */
233       else {
234         PetscCall(VecAXPY(ts->vec_sol, ts->time_step, th->Xdot));
235         PetscCall(TSAdaptCheckStage(ts->adapt, ts, ts->ptime + ts->time_step, ts->vec_sol, &stageok));
236         if (!stageok) {
237           PetscCall(VecCopy(th->X0, ts->vec_sol));
238           goto reject_step;
239         }
240       }
241     }
242 
243     th->status = TS_STEP_PENDING;
244     PetscCall(TSAdaptChoose(ts->adapt, ts, ts->time_step, NULL, &next_time_step, &accept));
245     th->status = accept ? TS_STEP_COMPLETE : TS_STEP_INCOMPLETE;
246     if (!accept) {
247       PetscCall(VecCopy(th->X0, ts->vec_sol));
248       ts->time_step = next_time_step;
249       goto reject_step;
250     }
251 
252     if (ts->forward_solve || ts->costintegralfwd) { /* Save the info for the later use in cost integral evaluation */
253       th->ptime0     = ts->ptime;
254       th->time_step0 = ts->time_step;
255     }
256     ts->ptime += ts->time_step;
257     ts->time_step = next_time_step;
258     break;
259 
260   reject_step:
261     ts->reject++;
262     accept = PETSC_FALSE;
263     if (!ts->reason && ++rejections > ts->max_reject && ts->max_reject >= 0) {
264       ts->reason = TS_DIVERGED_STEP_REJECTED;
265       PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", step rejections %" PetscInt_FMT " greater than current TS allowed, stopping solve\n", ts->steps, rejections));
266     }
267   }
268   PetscFunctionReturn(PETSC_SUCCESS);
269 }
270 
TSAdjointStepBEuler_Private(TS ts)271 static PetscErrorCode TSAdjointStepBEuler_Private(TS ts)
272 {
273   TS_Theta      *th           = (TS_Theta *)ts->data;
274   TS             quadts       = ts->quadraturets;
275   Vec           *VecsDeltaLam = th->VecsDeltaLam, *VecsDeltaMu = th->VecsDeltaMu, *VecsSensiTemp = th->VecsSensiTemp;
276   Vec           *VecsDeltaLam2 = th->VecsDeltaLam2, *VecsDeltaMu2 = th->VecsDeltaMu2, *VecsSensi2Temp = th->VecsSensi2Temp;
277   PetscInt       nadj;
278   Mat            J, Jpre, quadJ = NULL, quadJp = NULL;
279   KSP            ksp;
280   PetscScalar   *xarr;
281   TSEquationType eqtype;
282   PetscBool      isexplicitode = PETSC_FALSE;
283   PetscReal      adjoint_time_step;
284 
285   PetscFunctionBegin;
286   PetscCall(TSGetEquationType(ts, &eqtype));
287   if (eqtype == TS_EQ_ODE_EXPLICIT) {
288     isexplicitode = PETSC_TRUE;
289     VecsDeltaLam  = ts->vecs_sensi;
290     VecsDeltaLam2 = ts->vecs_sensi2;
291   }
292   th->status = TS_STEP_INCOMPLETE;
293   PetscCall(SNESGetKSP(ts->snes, &ksp));
294   PetscCall(TSGetIJacobian(ts, &J, &Jpre, NULL, NULL));
295   if (quadts) {
296     PetscCall(TSGetRHSJacobian(quadts, &quadJ, NULL, NULL, NULL));
297     PetscCall(TSGetRHSJacobianP(quadts, &quadJp, NULL, NULL));
298   }
299 
300   th->stage_time    = ts->ptime;
301   adjoint_time_step = -ts->time_step; /* always positive since time_step is negative */
302 
303   /* Build RHS for first-order adjoint lambda_{n+1}/h + r_u^T(n+1) */
304   if (quadts) PetscCall(TSComputeRHSJacobian(quadts, th->stage_time, ts->vec_sol, quadJ, NULL));
305 
306   for (nadj = 0; nadj < ts->numcost; nadj++) {
307     PetscCall(VecCopy(ts->vecs_sensi[nadj], VecsSensiTemp[nadj]));
308     PetscCall(VecScale(VecsSensiTemp[nadj], 1. / adjoint_time_step)); /* lambda_{n+1}/h */
309     if (quadJ) {
310       PetscCall(MatDenseGetColumn(quadJ, nadj, &xarr));
311       PetscCall(VecPlaceArray(ts->vec_drdu_col, xarr));
312       PetscCall(VecAXPY(VecsSensiTemp[nadj], 1., ts->vec_drdu_col));
313       PetscCall(VecResetArray(ts->vec_drdu_col));
314       PetscCall(MatDenseRestoreColumn(quadJ, &xarr));
315     }
316   }
317 
318   /* Build LHS for first-order adjoint */
319   th->shift = 1. / adjoint_time_step;
320   PetscCall(TSComputeSNESJacobian(ts, ts->vec_sol, J, Jpre));
321   PetscCall(KSPSetOperators(ksp, J, Jpre));
322 
323   /* Solve stage equation LHS*lambda_s = RHS for first-order adjoint */
324   for (nadj = 0; nadj < ts->numcost; nadj++) {
325     KSPConvergedReason kspreason;
326     PetscCall(KSPSolveTranspose(ksp, VecsSensiTemp[nadj], VecsDeltaLam[nadj]));
327     PetscCall(KSPGetConvergedReason(ksp, &kspreason));
328     if (kspreason < 0) {
329       ts->reason = TSADJOINT_DIVERGED_LINEAR_SOLVE;
330       PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", %" PetscInt_FMT "th cost function, transposed linear solve fails, stopping 1st-order adjoint solve\n", ts->steps, nadj));
331     }
332   }
333 
334   if (ts->vecs_sensi2) { /* U_{n+1} */
335     /* Get w1 at t_{n+1} from TLM matrix */
336     PetscCall(MatDenseGetColumn(ts->mat_sensip, 0, &xarr));
337     PetscCall(VecPlaceArray(ts->vec_sensip_col, xarr));
338     /* lambda_s^T F_UU w_1 */
339     PetscCall(TSComputeIHessianProductFunctionUU(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_sensip_col, ts->vecs_fuu));
340     /* lambda_s^T F_UP w_2 */
341     PetscCall(TSComputeIHessianProductFunctionUP(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_dir, ts->vecs_fup));
342     for (nadj = 0; nadj < ts->numcost; nadj++) { /* compute the residual */
343       PetscCall(VecCopy(ts->vecs_sensi2[nadj], VecsSensi2Temp[nadj]));
344       PetscCall(VecScale(VecsSensi2Temp[nadj], 1. / adjoint_time_step));
345       PetscCall(VecAXPY(VecsSensi2Temp[nadj], -1., ts->vecs_fuu[nadj]));
346       if (ts->vecs_fup) PetscCall(VecAXPY(VecsSensi2Temp[nadj], -1., ts->vecs_fup[nadj]));
347     }
348     /* Solve stage equation LHS X = RHS for second-order adjoint */
349     for (nadj = 0; nadj < ts->numcost; nadj++) {
350       KSPConvergedReason kspreason;
351       PetscCall(KSPSolveTranspose(ksp, VecsSensi2Temp[nadj], VecsDeltaLam2[nadj]));
352       PetscCall(KSPGetConvergedReason(ksp, &kspreason));
353       if (kspreason < 0) {
354         ts->reason = TSADJOINT_DIVERGED_LINEAR_SOLVE;
355         PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", %" PetscInt_FMT "th cost function, transposed linear solve fails, stopping 2nd-order adjoint solve\n", ts->steps, nadj));
356       }
357     }
358   }
359 
360   /* Update sensitivities, and evaluate integrals if there is any */
361   if (!isexplicitode) {
362     th->shift = 0.0;
363     PetscCall(TSComputeSNESJacobian(ts, ts->vec_sol, J, Jpre));
364     PetscCall(KSPSetOperators(ksp, J, Jpre));
365     for (nadj = 0; nadj < ts->numcost; nadj++) {
366       /* Add f_U \lambda_s to the original RHS */
367       PetscCall(VecScale(VecsSensiTemp[nadj], -1.));
368       PetscCall(MatMultTransposeAdd(J, VecsDeltaLam[nadj], VecsSensiTemp[nadj], VecsSensiTemp[nadj]));
369       PetscCall(VecScale(VecsSensiTemp[nadj], -adjoint_time_step));
370       PetscCall(VecCopy(VecsSensiTemp[nadj], ts->vecs_sensi[nadj]));
371       if (ts->vecs_sensi2) {
372         PetscCall(MatMultTransposeAdd(J, VecsDeltaLam2[nadj], VecsSensi2Temp[nadj], VecsSensi2Temp[nadj]));
373         PetscCall(VecScale(VecsSensi2Temp[nadj], -adjoint_time_step));
374         PetscCall(VecCopy(VecsSensi2Temp[nadj], ts->vecs_sensi2[nadj]));
375       }
376     }
377   }
378   if (ts->vecs_sensip) {
379     PetscCall(VecAXPBYPCZ(th->Xdot, -1. / adjoint_time_step, 1.0 / adjoint_time_step, 0, th->X0, ts->vec_sol));
380     PetscCall(TSComputeIJacobianP(ts, th->stage_time, ts->vec_sol, th->Xdot, 1. / adjoint_time_step, ts->Jacp, PETSC_FALSE)); /* get -f_p */
381     if (quadts) PetscCall(TSComputeRHSJacobianP(quadts, th->stage_time, ts->vec_sol, quadJp));
382     if (ts->vecs_sensi2p) {
383       /* lambda_s^T F_PU w_1 */
384       PetscCall(TSComputeIHessianProductFunctionPU(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_sensip_col, ts->vecs_fpu));
385       /* lambda_s^T F_PP w_2 */
386       PetscCall(TSComputeIHessianProductFunctionPP(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_dir, ts->vecs_fpp));
387     }
388 
389     for (nadj = 0; nadj < ts->numcost; nadj++) {
390       PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam[nadj], VecsDeltaMu[nadj]));
391       PetscCall(VecAXPY(ts->vecs_sensip[nadj], -adjoint_time_step, VecsDeltaMu[nadj]));
392       if (quadJp) {
393         PetscCall(MatDenseGetColumn(quadJp, nadj, &xarr));
394         PetscCall(VecPlaceArray(ts->vec_drdp_col, xarr));
395         PetscCall(VecAXPY(ts->vecs_sensip[nadj], adjoint_time_step, ts->vec_drdp_col));
396         PetscCall(VecResetArray(ts->vec_drdp_col));
397         PetscCall(MatDenseRestoreColumn(quadJp, &xarr));
398       }
399       if (ts->vecs_sensi2p) {
400         PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam2[nadj], VecsDeltaMu2[nadj]));
401         PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step, VecsDeltaMu2[nadj]));
402         if (ts->vecs_fpu) PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step, ts->vecs_fpu[nadj]));
403         if (ts->vecs_fpp) PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step, ts->vecs_fpp[nadj]));
404       }
405     }
406   }
407 
408   if (ts->vecs_sensi2) {
409     PetscCall(VecResetArray(ts->vec_sensip_col));
410     PetscCall(MatDenseRestoreColumn(ts->mat_sensip, &xarr));
411   }
412   th->status = TS_STEP_COMPLETE;
413   PetscFunctionReturn(PETSC_SUCCESS);
414 }
415 
TSAdjointStep_Theta(TS ts)416 static PetscErrorCode TSAdjointStep_Theta(TS ts)
417 {
418   TS_Theta    *th           = (TS_Theta *)ts->data;
419   TS           quadts       = ts->quadraturets;
420   Vec         *VecsDeltaLam = th->VecsDeltaLam, *VecsDeltaMu = th->VecsDeltaMu, *VecsSensiTemp = th->VecsSensiTemp;
421   Vec         *VecsDeltaLam2 = th->VecsDeltaLam2, *VecsDeltaMu2 = th->VecsDeltaMu2, *VecsSensi2Temp = th->VecsSensi2Temp;
422   PetscInt     nadj;
423   Mat          J, Jpre, quadJ = NULL, quadJp = NULL;
424   KSP          ksp;
425   PetscScalar *xarr;
426   PetscReal    adjoint_time_step;
427   PetscReal    adjoint_ptime; /* end time of the adjoint time step (ts->ptime is the start time, usually ts->ptime is larger than adjoint_ptime) */
428 
429   PetscFunctionBegin;
430   if (th->Theta == 1.) {
431     PetscCall(TSAdjointStepBEuler_Private(ts));
432     PetscFunctionReturn(PETSC_SUCCESS);
433   }
434   th->status = TS_STEP_INCOMPLETE;
435   PetscCall(SNESGetKSP(ts->snes, &ksp));
436   PetscCall(TSGetIJacobian(ts, &J, &Jpre, NULL, NULL));
437   if (quadts) {
438     PetscCall(TSGetRHSJacobian(quadts, &quadJ, NULL, NULL, NULL));
439     PetscCall(TSGetRHSJacobianP(quadts, &quadJp, NULL, NULL));
440   }
441   /* If endpoint=1, th->ptime and th->X0 will be used; if endpoint=0, th->stage_time and th->X will be used. */
442   th->stage_time    = th->endpoint ? ts->ptime : (ts->ptime + (1. - th->Theta) * ts->time_step);
443   adjoint_ptime     = ts->ptime + ts->time_step;
444   adjoint_time_step = -ts->time_step; /* always positive since time_step is negative */
445 
446   if (!th->endpoint) {
447     /* recover th->X0 using vec_sol and the stage value th->X */
448     PetscCall(VecAXPBYPCZ(th->X0, 1.0 / (1.0 - th->Theta), th->Theta / (th->Theta - 1.0), 0, th->X, ts->vec_sol));
449   }
450 
451   /* Build RHS for first-order adjoint */
452   /* Cost function has an integral term */
453   if (quadts) {
454     if (th->endpoint) {
455       PetscCall(TSComputeRHSJacobian(quadts, th->stage_time, ts->vec_sol, quadJ, NULL));
456     } else {
457       PetscCall(TSComputeRHSJacobian(quadts, th->stage_time, th->X, quadJ, NULL));
458     }
459   }
460 
461   for (nadj = 0; nadj < ts->numcost; nadj++) {
462     PetscCall(VecCopy(ts->vecs_sensi[nadj], VecsSensiTemp[nadj]));
463     PetscCall(VecScale(VecsSensiTemp[nadj], 1. / (th->Theta * adjoint_time_step)));
464     if (quadJ) {
465       PetscCall(MatDenseGetColumn(quadJ, nadj, &xarr));
466       PetscCall(VecPlaceArray(ts->vec_drdu_col, xarr));
467       PetscCall(VecAXPY(VecsSensiTemp[nadj], 1., ts->vec_drdu_col));
468       PetscCall(VecResetArray(ts->vec_drdu_col));
469       PetscCall(MatDenseRestoreColumn(quadJ, &xarr));
470     }
471   }
472 
473   /* Build LHS for first-order adjoint */
474   th->shift = 1. / (th->Theta * adjoint_time_step);
475   if (th->endpoint) {
476     PetscCall(TSComputeSNESJacobian(ts, ts->vec_sol, J, Jpre));
477   } else {
478     PetscCall(TSComputeSNESJacobian(ts, th->X, J, Jpre));
479   }
480   PetscCall(KSPSetOperators(ksp, J, Jpre));
481 
482   /* Solve stage equation LHS*lambda_s = RHS for first-order adjoint */
483   for (nadj = 0; nadj < ts->numcost; nadj++) {
484     KSPConvergedReason kspreason;
485     PetscCall(KSPSolveTranspose(ksp, VecsSensiTemp[nadj], VecsDeltaLam[nadj]));
486     PetscCall(KSPGetConvergedReason(ksp, &kspreason));
487     if (kspreason < 0) {
488       ts->reason = TSADJOINT_DIVERGED_LINEAR_SOLVE;
489       PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", %" PetscInt_FMT "th cost function, transposed linear solve fails, stopping 1st-order adjoint solve\n", ts->steps, nadj));
490     }
491   }
492 
493   /* Second-order adjoint */
494   if (ts->vecs_sensi2) { /* U_{n+1} */
495     PetscCheck(th->endpoint, PetscObjectComm((PetscObject)ts), PETSC_ERR_SUP, "Operation not implemented in TS_Theta");
496     /* Get w1 at t_{n+1} from TLM matrix */
497     PetscCall(MatDenseGetColumn(ts->mat_sensip, 0, &xarr));
498     PetscCall(VecPlaceArray(ts->vec_sensip_col, xarr));
499     /* lambda_s^T F_UU w_1 */
500     PetscCall(TSComputeIHessianProductFunctionUU(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_sensip_col, ts->vecs_fuu));
501     PetscCall(VecResetArray(ts->vec_sensip_col));
502     PetscCall(MatDenseRestoreColumn(ts->mat_sensip, &xarr));
503     /* lambda_s^T F_UP w_2 */
504     PetscCall(TSComputeIHessianProductFunctionUP(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_dir, ts->vecs_fup));
505     for (nadj = 0; nadj < ts->numcost; nadj++) { /* compute the residual */
506       PetscCall(VecCopy(ts->vecs_sensi2[nadj], VecsSensi2Temp[nadj]));
507       PetscCall(VecScale(VecsSensi2Temp[nadj], th->shift));
508       PetscCall(VecAXPY(VecsSensi2Temp[nadj], -1., ts->vecs_fuu[nadj]));
509       if (ts->vecs_fup) PetscCall(VecAXPY(VecsSensi2Temp[nadj], -1., ts->vecs_fup[nadj]));
510     }
511     /* Solve stage equation LHS X = RHS for second-order adjoint */
512     for (nadj = 0; nadj < ts->numcost; nadj++) {
513       KSPConvergedReason kspreason;
514       PetscCall(KSPSolveTranspose(ksp, VecsSensi2Temp[nadj], VecsDeltaLam2[nadj]));
515       PetscCall(KSPGetConvergedReason(ksp, &kspreason));
516       if (kspreason < 0) {
517         ts->reason = TSADJOINT_DIVERGED_LINEAR_SOLVE;
518         PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", %" PetscInt_FMT "th cost function, transposed linear solve fails, stopping 2nd-order adjoint solve\n", ts->steps, nadj));
519       }
520     }
521   }
522 
523   /* Update sensitivities, and evaluate integrals if there is any */
524   if (th->endpoint) { /* two-stage Theta methods with th->Theta!=1, th->Theta==1 leads to BEuler */
525     th->shift      = 1. / ((th->Theta - 1.) * adjoint_time_step);
526     th->stage_time = adjoint_ptime;
527     PetscCall(TSComputeSNESJacobian(ts, th->X0, J, Jpre));
528     PetscCall(KSPSetOperators(ksp, J, Jpre));
529     /* R_U at t_n */
530     if (quadts) PetscCall(TSComputeRHSJacobian(quadts, adjoint_ptime, th->X0, quadJ, NULL));
531     for (nadj = 0; nadj < ts->numcost; nadj++) {
532       PetscCall(MatMultTranspose(J, VecsDeltaLam[nadj], ts->vecs_sensi[nadj]));
533       if (quadJ) {
534         PetscCall(MatDenseGetColumn(quadJ, nadj, &xarr));
535         PetscCall(VecPlaceArray(ts->vec_drdu_col, xarr));
536         PetscCall(VecAXPY(ts->vecs_sensi[nadj], -1., ts->vec_drdu_col));
537         PetscCall(VecResetArray(ts->vec_drdu_col));
538         PetscCall(MatDenseRestoreColumn(quadJ, &xarr));
539       }
540       PetscCall(VecScale(ts->vecs_sensi[nadj], 1. / th->shift));
541     }
542 
543     /* Second-order adjoint */
544     if (ts->vecs_sensi2) { /* U_n */
545       /* Get w1 at t_n from TLM matrix */
546       PetscCall(MatDenseGetColumn(th->MatFwdSensip0, 0, &xarr));
547       PetscCall(VecPlaceArray(ts->vec_sensip_col, xarr));
548       /* lambda_s^T F_UU w_1 */
549       PetscCall(TSComputeIHessianProductFunctionUU(ts, adjoint_ptime, th->X0, VecsDeltaLam, ts->vec_sensip_col, ts->vecs_fuu));
550       PetscCall(VecResetArray(ts->vec_sensip_col));
551       PetscCall(MatDenseRestoreColumn(th->MatFwdSensip0, &xarr));
552       /* lambda_s^T F_UU w_2 */
553       PetscCall(TSComputeIHessianProductFunctionUP(ts, adjoint_ptime, th->X0, VecsDeltaLam, ts->vec_dir, ts->vecs_fup));
554       for (nadj = 0; nadj < ts->numcost; nadj++) {
555         /* M^T Lambda_s + h(1-theta) F_U^T Lambda_s + h(1-theta) lambda_s^T F_UU w_1 + lambda_s^T F_UP w_2  */
556         PetscCall(MatMultTranspose(J, VecsDeltaLam2[nadj], ts->vecs_sensi2[nadj]));
557         PetscCall(VecAXPY(ts->vecs_sensi2[nadj], 1., ts->vecs_fuu[nadj]));
558         if (ts->vecs_fup) PetscCall(VecAXPY(ts->vecs_sensi2[nadj], 1., ts->vecs_fup[nadj]));
559         PetscCall(VecScale(ts->vecs_sensi2[nadj], 1. / th->shift));
560       }
561     }
562 
563     th->stage_time = ts->ptime; /* recover the old value */
564 
565     if (ts->vecs_sensip) { /* sensitivities wrt parameters */
566       /* U_{n+1} */
567       th->shift = 1.0 / (adjoint_time_step * th->Theta);
568       PetscCall(VecAXPBYPCZ(th->Xdot, -th->shift, th->shift, 0, th->X0, ts->vec_sol));
569       PetscCall(TSComputeIJacobianP(ts, th->stage_time, ts->vec_sol, th->Xdot, -1. / (th->Theta * adjoint_time_step), ts->Jacp, PETSC_FALSE));
570       if (quadts) PetscCall(TSComputeRHSJacobianP(quadts, th->stage_time, ts->vec_sol, quadJp));
571       for (nadj = 0; nadj < ts->numcost; nadj++) {
572         PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam[nadj], VecsDeltaMu[nadj]));
573         PetscCall(VecAXPY(ts->vecs_sensip[nadj], -adjoint_time_step * th->Theta, VecsDeltaMu[nadj]));
574         if (quadJp) {
575           PetscCall(MatDenseGetColumn(quadJp, nadj, &xarr));
576           PetscCall(VecPlaceArray(ts->vec_drdp_col, xarr));
577           PetscCall(VecAXPY(ts->vecs_sensip[nadj], adjoint_time_step * th->Theta, ts->vec_drdp_col));
578           PetscCall(VecResetArray(ts->vec_drdp_col));
579           PetscCall(MatDenseRestoreColumn(quadJp, &xarr));
580         }
581       }
582       if (ts->vecs_sensi2p) { /* second-order */
583         /* Get w1 at t_{n+1} from TLM matrix */
584         PetscCall(MatDenseGetColumn(ts->mat_sensip, 0, &xarr));
585         PetscCall(VecPlaceArray(ts->vec_sensip_col, xarr));
586         /* lambda_s^T F_PU w_1 */
587         PetscCall(TSComputeIHessianProductFunctionPU(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_sensip_col, ts->vecs_fpu));
588         PetscCall(VecResetArray(ts->vec_sensip_col));
589         PetscCall(MatDenseRestoreColumn(ts->mat_sensip, &xarr));
590 
591         /* lambda_s^T F_PP w_2 */
592         PetscCall(TSComputeIHessianProductFunctionPP(ts, th->stage_time, ts->vec_sol, VecsDeltaLam, ts->vec_dir, ts->vecs_fpp));
593         for (nadj = 0; nadj < ts->numcost; nadj++) {
594           /* Mu2 <- Mu2 + h theta F_P^T Lambda_s + h theta (lambda_s^T F_UU w_1 + lambda_s^T F_UP w_2)  */
595           PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam2[nadj], VecsDeltaMu2[nadj]));
596           PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step * th->Theta, VecsDeltaMu2[nadj]));
597           if (ts->vecs_fpu) PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step * th->Theta, ts->vecs_fpu[nadj]));
598           if (ts->vecs_fpp) PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step * th->Theta, ts->vecs_fpp[nadj]));
599         }
600       }
601 
602       /* U_s */
603       PetscCall(VecZeroEntries(th->Xdot));
604       PetscCall(TSComputeIJacobianP(ts, adjoint_ptime, th->X0, th->Xdot, 1. / ((th->Theta - 1.0) * adjoint_time_step), ts->Jacp, PETSC_FALSE));
605       if (quadts) PetscCall(TSComputeRHSJacobianP(quadts, adjoint_ptime, th->X0, quadJp));
606       for (nadj = 0; nadj < ts->numcost; nadj++) {
607         PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam[nadj], VecsDeltaMu[nadj]));
608         PetscCall(VecAXPY(ts->vecs_sensip[nadj], -adjoint_time_step * (1.0 - th->Theta), VecsDeltaMu[nadj]));
609         if (quadJp) {
610           PetscCall(MatDenseGetColumn(quadJp, nadj, &xarr));
611           PetscCall(VecPlaceArray(ts->vec_drdp_col, xarr));
612           PetscCall(VecAXPY(ts->vecs_sensip[nadj], adjoint_time_step * (1.0 - th->Theta), ts->vec_drdp_col));
613           PetscCall(VecResetArray(ts->vec_drdp_col));
614           PetscCall(MatDenseRestoreColumn(quadJp, &xarr));
615         }
616         if (ts->vecs_sensi2p) { /* second-order */
617           /* Get w1 at t_n from TLM matrix */
618           PetscCall(MatDenseGetColumn(th->MatFwdSensip0, 0, &xarr));
619           PetscCall(VecPlaceArray(ts->vec_sensip_col, xarr));
620           /* lambda_s^T F_PU w_1 */
621           PetscCall(TSComputeIHessianProductFunctionPU(ts, adjoint_ptime, th->X0, VecsDeltaLam, ts->vec_sensip_col, ts->vecs_fpu));
622           PetscCall(VecResetArray(ts->vec_sensip_col));
623           PetscCall(MatDenseRestoreColumn(th->MatFwdSensip0, &xarr));
624           /* lambda_s^T F_PP w_2 */
625           PetscCall(TSComputeIHessianProductFunctionPP(ts, adjoint_ptime, th->X0, VecsDeltaLam, ts->vec_dir, ts->vecs_fpp));
626           for (nadj = 0; nadj < ts->numcost; nadj++) {
627             /* Mu2 <- Mu2 + h(1-theta) F_P^T Lambda_s + h(1-theta) (lambda_s^T F_UU w_1 + lambda_s^T F_UP w_2) */
628             PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam2[nadj], VecsDeltaMu2[nadj]));
629             PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step * (1.0 - th->Theta), VecsDeltaMu2[nadj]));
630             if (ts->vecs_fpu) PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step * (1.0 - th->Theta), ts->vecs_fpu[nadj]));
631             if (ts->vecs_fpp) PetscCall(VecAXPY(ts->vecs_sensi2p[nadj], -adjoint_time_step * (1.0 - th->Theta), ts->vecs_fpp[nadj]));
632           }
633         }
634       }
635     }
636   } else { /* one-stage case */
637     th->shift = 0.0;
638     PetscCall(TSComputeSNESJacobian(ts, th->X, J, Jpre)); /* get -f_y */
639     PetscCall(KSPSetOperators(ksp, J, Jpre));
640     if (quadts) PetscCall(TSComputeRHSJacobian(quadts, th->stage_time, th->X, quadJ, NULL));
641     for (nadj = 0; nadj < ts->numcost; nadj++) {
642       PetscCall(MatMultTranspose(J, VecsDeltaLam[nadj], VecsSensiTemp[nadj]));
643       PetscCall(VecAXPY(ts->vecs_sensi[nadj], -adjoint_time_step, VecsSensiTemp[nadj]));
644       if (quadJ) {
645         PetscCall(MatDenseGetColumn(quadJ, nadj, &xarr));
646         PetscCall(VecPlaceArray(ts->vec_drdu_col, xarr));
647         PetscCall(VecAXPY(ts->vecs_sensi[nadj], adjoint_time_step, ts->vec_drdu_col));
648         PetscCall(VecResetArray(ts->vec_drdu_col));
649         PetscCall(MatDenseRestoreColumn(quadJ, &xarr));
650       }
651     }
652     if (ts->vecs_sensip) {
653       th->shift = 1.0 / (adjoint_time_step * th->Theta);
654       PetscCall(VecAXPBYPCZ(th->Xdot, -th->shift, th->shift, 0, th->X0, th->X));
655       PetscCall(TSComputeIJacobianP(ts, th->stage_time, th->X, th->Xdot, th->shift, ts->Jacp, PETSC_FALSE));
656       if (quadts) PetscCall(TSComputeRHSJacobianP(quadts, th->stage_time, th->X, quadJp));
657       for (nadj = 0; nadj < ts->numcost; nadj++) {
658         PetscCall(MatMultTranspose(ts->Jacp, VecsDeltaLam[nadj], VecsDeltaMu[nadj]));
659         PetscCall(VecAXPY(ts->vecs_sensip[nadj], -adjoint_time_step, VecsDeltaMu[nadj]));
660         if (quadJp) {
661           PetscCall(MatDenseGetColumn(quadJp, nadj, &xarr));
662           PetscCall(VecPlaceArray(ts->vec_drdp_col, xarr));
663           PetscCall(VecAXPY(ts->vecs_sensip[nadj], adjoint_time_step, ts->vec_drdp_col));
664           PetscCall(VecResetArray(ts->vec_drdp_col));
665           PetscCall(MatDenseRestoreColumn(quadJp, &xarr));
666         }
667       }
668     }
669   }
670 
671   th->status = TS_STEP_COMPLETE;
672   PetscFunctionReturn(PETSC_SUCCESS);
673 }
674 
TSInterpolate_Theta(TS ts,PetscReal t,Vec X)675 static PetscErrorCode TSInterpolate_Theta(TS ts, PetscReal t, Vec X)
676 {
677   TS_Theta *th = (TS_Theta *)ts->data;
678   PetscReal dt = t - ts->ptime;
679 
680   PetscFunctionBegin;
681   PetscCall(VecCopy(ts->vec_sol, th->X));
682   if (th->endpoint) dt *= th->Theta;
683   PetscCall(VecWAXPY(X, dt, th->Xdot, th->X));
684   PetscFunctionReturn(PETSC_SUCCESS);
685 }
686 
TSEvaluateWLTE_Theta(TS ts,NormType wnormtype,PetscInt * order,PetscReal * wlte)687 static PetscErrorCode TSEvaluateWLTE_Theta(TS ts, NormType wnormtype, PetscInt *order, PetscReal *wlte)
688 {
689   TS_Theta *th = (TS_Theta *)ts->data;
690   Vec       X  = ts->vec_sol;      /* X = solution */
691   Vec       Y  = th->vec_lte_work; /* Y = X + LTE  */
692   PetscReal wltea, wlter;
693 
694   PetscFunctionBegin;
695   if (!th->vec_sol_prev) {
696     *wlte = -1;
697     PetscFunctionReturn(PETSC_SUCCESS);
698   }
699   /* Cannot compute LTE in first step or in restart after event */
700   if (ts->steprestart) {
701     *wlte = -1;
702     PetscFunctionReturn(PETSC_SUCCESS);
703   }
704   /* Compute LTE using backward differences with non-constant time step */
705   {
706     PetscReal   h = ts->time_step, h_prev = ts->ptime - ts->ptime_prev;
707     PetscReal   a = 1 + h_prev / h;
708     PetscScalar scal[3];
709     Vec         vecs[3];
710 
711     scal[0] = -1 / a;
712     scal[1] = +1 / (a - 1);
713     scal[2] = -1 / (a * (a - 1));
714     vecs[0] = X;
715     vecs[1] = th->X0;
716     vecs[2] = th->vec_sol_prev;
717     PetscCall(VecCopy(X, Y));
718     PetscCall(VecMAXPY(Y, 3, scal, vecs));
719     PetscCall(TSErrorWeightedNorm(ts, X, Y, wnormtype, wlte, &wltea, &wlter));
720   }
721   if (order) *order = 2;
722   PetscFunctionReturn(PETSC_SUCCESS);
723 }
724 
TSRollBack_Theta(TS ts)725 static PetscErrorCode TSRollBack_Theta(TS ts)
726 {
727   TS_Theta *th     = (TS_Theta *)ts->data;
728   TS        quadts = ts->quadraturets;
729 
730   PetscFunctionBegin;
731   if (quadts && ts->costintegralfwd) PetscCall(VecCopy(th->VecCostIntegral0, quadts->vec_sol));
732   th->status = TS_STEP_INCOMPLETE;
733   if (ts->mat_sensip) PetscCall(MatCopy(th->MatFwdSensip0, ts->mat_sensip, SAME_NONZERO_PATTERN));
734   if (quadts && quadts->mat_sensip) PetscCall(MatCopy(th->MatIntegralSensip0, quadts->mat_sensip, SAME_NONZERO_PATTERN));
735   PetscFunctionReturn(PETSC_SUCCESS);
736 }
737 
TSForwardStep_Theta(TS ts)738 static PetscErrorCode TSForwardStep_Theta(TS ts)
739 {
740   TS_Theta    *th                   = (TS_Theta *)ts->data;
741   TS           quadts               = ts->quadraturets;
742   Mat          MatDeltaFwdSensip    = th->MatDeltaFwdSensip;
743   Vec          VecDeltaFwdSensipCol = th->VecDeltaFwdSensipCol;
744   PetscInt     ntlm;
745   KSP          ksp;
746   Mat          J, Jpre, quadJ = NULL, quadJp = NULL;
747   PetscScalar *barr, *xarr;
748   PetscReal    previous_shift;
749 
750   PetscFunctionBegin;
751   previous_shift = th->shift;
752   PetscCall(MatCopy(ts->mat_sensip, th->MatFwdSensip0, SAME_NONZERO_PATTERN));
753 
754   if (quadts && quadts->mat_sensip) PetscCall(MatCopy(quadts->mat_sensip, th->MatIntegralSensip0, SAME_NONZERO_PATTERN));
755   PetscCall(SNESGetKSP(ts->snes, &ksp));
756   PetscCall(TSGetIJacobian(ts, &J, &Jpre, NULL, NULL));
757   if (quadts) {
758     PetscCall(TSGetRHSJacobian(quadts, &quadJ, NULL, NULL, NULL));
759     PetscCall(TSGetRHSJacobianP(quadts, &quadJp, NULL, NULL));
760   }
761 
762   /* Build RHS */
763   if (th->endpoint) { /* 2-stage method*/
764     th->shift = 1. / ((th->Theta - 1.) * th->time_step0);
765     PetscCall(TSComputeIJacobian(ts, th->ptime0, th->X0, th->Xdot, th->shift, J, Jpre, PETSC_FALSE));
766     PetscCall(MatMatMult(J, ts->mat_sensip, MAT_REUSE_MATRIX, PETSC_DETERMINE, &MatDeltaFwdSensip));
767     PetscCall(MatScale(MatDeltaFwdSensip, (th->Theta - 1.) / th->Theta));
768 
769     /* Add the f_p forcing terms */
770     if (ts->Jacp) {
771       PetscCall(VecZeroEntries(th->Xdot));
772       PetscCall(TSComputeIJacobianP(ts, th->ptime0, th->X0, th->Xdot, th->shift, ts->Jacp, PETSC_FALSE));
773       PetscCall(MatAXPY(MatDeltaFwdSensip, (th->Theta - 1.) / th->Theta, ts->Jacp, SUBSET_NONZERO_PATTERN));
774       th->shift = previous_shift;
775       PetscCall(VecAXPBYPCZ(th->Xdot, -th->shift, th->shift, 0, th->X0, ts->vec_sol));
776       PetscCall(TSComputeIJacobianP(ts, th->stage_time, ts->vec_sol, th->Xdot, th->shift, ts->Jacp, PETSC_FALSE));
777       PetscCall(MatAXPY(MatDeltaFwdSensip, -1., ts->Jacp, SUBSET_NONZERO_PATTERN));
778     }
779   } else { /* 1-stage method */
780     th->shift = 0.0;
781     PetscCall(TSComputeIJacobian(ts, th->stage_time, th->X, th->Xdot, th->shift, J, Jpre, PETSC_FALSE));
782     PetscCall(MatMatMult(J, ts->mat_sensip, MAT_REUSE_MATRIX, PETSC_DETERMINE, &MatDeltaFwdSensip));
783     PetscCall(MatScale(MatDeltaFwdSensip, -1.));
784 
785     /* Add the f_p forcing terms */
786     if (ts->Jacp) {
787       th->shift = previous_shift;
788       PetscCall(VecAXPBYPCZ(th->Xdot, -th->shift, th->shift, 0, th->X0, th->X));
789       PetscCall(TSComputeIJacobianP(ts, th->stage_time, th->X, th->Xdot, th->shift, ts->Jacp, PETSC_FALSE));
790       PetscCall(MatAXPY(MatDeltaFwdSensip, -1., ts->Jacp, SUBSET_NONZERO_PATTERN));
791     }
792   }
793 
794   /* Build LHS */
795   th->shift = previous_shift; /* recover the previous shift used in TSStep_Theta() */
796   if (th->endpoint) {
797     PetscCall(TSComputeIJacobian(ts, th->stage_time, ts->vec_sol, th->Xdot, th->shift, J, Jpre, PETSC_FALSE));
798   } else {
799     PetscCall(TSComputeIJacobian(ts, th->stage_time, th->X, th->Xdot, th->shift, J, Jpre, PETSC_FALSE));
800   }
801   PetscCall(KSPSetOperators(ksp, J, Jpre));
802 
803   /*
804     Evaluate the first stage of integral gradients with the 2-stage method:
805     drdu|t_n*S(t_n) + drdp|t_n
806     This is done before the linear solve because the sensitivity variable S(t_n) will be propagated to S(t_{n+1})
807   */
808   if (th->endpoint) { /* 2-stage method only */
809     if (quadts && quadts->mat_sensip) {
810       PetscCall(TSComputeRHSJacobian(quadts, th->ptime0, th->X0, quadJ, NULL));
811       PetscCall(TSComputeRHSJacobianP(quadts, th->ptime0, th->X0, quadJp));
812       PetscCall(MatTransposeMatMult(ts->mat_sensip, quadJ, MAT_REUSE_MATRIX, PETSC_DETERMINE, &th->MatIntegralSensipTemp));
813       PetscCall(MatAXPY(th->MatIntegralSensipTemp, 1, quadJp, SAME_NONZERO_PATTERN));
814       PetscCall(MatAXPY(quadts->mat_sensip, th->time_step0 * (1. - th->Theta), th->MatIntegralSensipTemp, SAME_NONZERO_PATTERN));
815     }
816   }
817 
818   /* Solve the tangent linear equation for forward sensitivities to parameters */
819   for (ntlm = 0; ntlm < th->num_tlm; ntlm++) {
820     KSPConvergedReason kspreason;
821     PetscCall(MatDenseGetColumn(MatDeltaFwdSensip, ntlm, &barr));
822     PetscCall(VecPlaceArray(VecDeltaFwdSensipCol, barr));
823     if (th->endpoint) {
824       PetscCall(MatDenseGetColumn(ts->mat_sensip, ntlm, &xarr));
825       PetscCall(VecPlaceArray(ts->vec_sensip_col, xarr));
826       PetscCall(KSPSolve(ksp, VecDeltaFwdSensipCol, ts->vec_sensip_col));
827       PetscCall(VecResetArray(ts->vec_sensip_col));
828       PetscCall(MatDenseRestoreColumn(ts->mat_sensip, &xarr));
829     } else {
830       PetscCall(KSPSolve(ksp, VecDeltaFwdSensipCol, VecDeltaFwdSensipCol));
831     }
832     PetscCall(KSPGetConvergedReason(ksp, &kspreason));
833     if (kspreason < 0) {
834       ts->reason = TSFORWARD_DIVERGED_LINEAR_SOLVE;
835       PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", %" PetscInt_FMT "th tangent linear solve, linear solve fails, stopping tangent linear solve\n", ts->steps, ntlm));
836     }
837     PetscCall(VecResetArray(VecDeltaFwdSensipCol));
838     PetscCall(MatDenseRestoreColumn(MatDeltaFwdSensip, &barr));
839   }
840 
841   /*
842     Evaluate the second stage of integral gradients with the 2-stage method:
843     drdu|t_{n+1}*S(t_{n+1}) + drdp|t_{n+1}
844   */
845   if (quadts && quadts->mat_sensip) {
846     if (!th->endpoint) {
847       PetscCall(MatAXPY(ts->mat_sensip, 1, MatDeltaFwdSensip, SAME_NONZERO_PATTERN)); /* stage sensitivity */
848       PetscCall(TSComputeRHSJacobian(quadts, th->stage_time, th->X, quadJ, NULL));
849       PetscCall(TSComputeRHSJacobianP(quadts, th->stage_time, th->X, quadJp));
850       PetscCall(MatTransposeMatMult(ts->mat_sensip, quadJ, MAT_REUSE_MATRIX, PETSC_DETERMINE, &th->MatIntegralSensipTemp));
851       PetscCall(MatAXPY(th->MatIntegralSensipTemp, 1, quadJp, SAME_NONZERO_PATTERN));
852       PetscCall(MatAXPY(quadts->mat_sensip, th->time_step0, th->MatIntegralSensipTemp, SAME_NONZERO_PATTERN));
853       PetscCall(MatAXPY(ts->mat_sensip, (1. - th->Theta) / th->Theta, MatDeltaFwdSensip, SAME_NONZERO_PATTERN));
854     } else {
855       PetscCall(TSComputeRHSJacobian(quadts, th->stage_time, ts->vec_sol, quadJ, NULL));
856       PetscCall(TSComputeRHSJacobianP(quadts, th->stage_time, ts->vec_sol, quadJp));
857       PetscCall(MatTransposeMatMult(ts->mat_sensip, quadJ, MAT_REUSE_MATRIX, PETSC_DETERMINE, &th->MatIntegralSensipTemp));
858       PetscCall(MatAXPY(th->MatIntegralSensipTemp, 1, quadJp, SAME_NONZERO_PATTERN));
859       PetscCall(MatAXPY(quadts->mat_sensip, th->time_step0 * th->Theta, th->MatIntegralSensipTemp, SAME_NONZERO_PATTERN));
860     }
861   } else {
862     if (!th->endpoint) PetscCall(MatAXPY(ts->mat_sensip, 1. / th->Theta, MatDeltaFwdSensip, SAME_NONZERO_PATTERN));
863   }
864   PetscFunctionReturn(PETSC_SUCCESS);
865 }
866 
TSForwardGetStages_Theta(TS ts,PetscInt * ns,Mat * stagesensip[])867 static PetscErrorCode TSForwardGetStages_Theta(TS ts, PetscInt *ns, Mat *stagesensip[])
868 {
869   TS_Theta *th = (TS_Theta *)ts->data;
870 
871   PetscFunctionBegin;
872   if (ns) {
873     if (!th->endpoint && th->Theta != 1.0) *ns = 1; /* midpoint form */
874     else *ns = 2;                                   /* endpoint form */
875   }
876   if (stagesensip) {
877     if (!th->endpoint && th->Theta != 1.0) {
878       th->MatFwdStages[0] = th->MatDeltaFwdSensip;
879     } else {
880       th->MatFwdStages[0] = th->MatFwdSensip0;
881       th->MatFwdStages[1] = ts->mat_sensip; /* stiffly accurate */
882     }
883     *stagesensip = th->MatFwdStages;
884   }
885   PetscFunctionReturn(PETSC_SUCCESS);
886 }
887 
TSReset_Theta(TS ts)888 static PetscErrorCode TSReset_Theta(TS ts)
889 {
890   TS_Theta *th = (TS_Theta *)ts->data;
891 
892   PetscFunctionBegin;
893   PetscCall(VecDestroy(&th->X));
894   PetscCall(VecDestroy(&th->Xdot));
895   PetscCall(VecDestroy(&th->X0));
896   PetscCall(VecDestroy(&th->affine));
897 
898   PetscCall(VecDestroy(&th->vec_sol_prev));
899   PetscCall(VecDestroy(&th->vec_lte_work));
900 
901   PetscCall(VecDestroy(&th->VecCostIntegral0));
902   PetscFunctionReturn(PETSC_SUCCESS);
903 }
904 
TSAdjointReset_Theta(TS ts)905 static PetscErrorCode TSAdjointReset_Theta(TS ts)
906 {
907   TS_Theta *th = (TS_Theta *)ts->data;
908 
909   PetscFunctionBegin;
910   PetscCall(VecDestroyVecs(ts->numcost, &th->VecsDeltaLam));
911   PetscCall(VecDestroyVecs(ts->numcost, &th->VecsDeltaMu));
912   PetscCall(VecDestroyVecs(ts->numcost, &th->VecsDeltaLam2));
913   PetscCall(VecDestroyVecs(ts->numcost, &th->VecsDeltaMu2));
914   PetscCall(VecDestroyVecs(ts->numcost, &th->VecsSensiTemp));
915   PetscCall(VecDestroyVecs(ts->numcost, &th->VecsSensi2Temp));
916   PetscFunctionReturn(PETSC_SUCCESS);
917 }
918 
TSDestroy_Theta(TS ts)919 static PetscErrorCode TSDestroy_Theta(TS ts)
920 {
921   PetscFunctionBegin;
922   PetscCall(TSReset_Theta(ts));
923   if (ts->dm) {
924     PetscCall(DMCoarsenHookRemove(ts->dm, DMCoarsenHook_TSTheta, DMRestrictHook_TSTheta, ts));
925     PetscCall(DMSubDomainHookRemove(ts->dm, DMSubDomainHook_TSTheta, DMSubDomainRestrictHook_TSTheta, ts));
926   }
927   PetscCall(PetscFree(ts->data));
928   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaGetTheta_C", NULL));
929   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaSetTheta_C", NULL));
930   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaGetEndpoint_C", NULL));
931   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaSetEndpoint_C", NULL));
932   PetscFunctionReturn(PETSC_SUCCESS);
933 }
934 
935 /*
936   This defines the nonlinear equation that is to be solved with SNES
937   G(U) = F[t0+Theta*dt, U, (U-U0)*shift] = 0
938 
939   Note that U here is the stage argument. This means that U = U_{n+1} only if endpoint = true,
940   otherwise U = theta U_{n+1} + (1 - theta) U0, which for the case of implicit midpoint is
941   U = (U_{n+1} + U0)/2
942 */
SNESTSFormFunction_Theta(SNES snes,Vec x,Vec y,TS ts)943 static PetscErrorCode SNESTSFormFunction_Theta(SNES snes, Vec x, Vec y, TS ts)
944 {
945   TS_Theta *th = (TS_Theta *)ts->data;
946   Vec       X0, Xdot;
947   DM        dm, dmsave;
948   PetscReal shift = th->shift;
949 
950   PetscFunctionBegin;
951   PetscCall(SNESGetDM(snes, &dm));
952   /* When using the endpoint variant, this is actually 1/Theta * Xdot */
953   PetscCall(TSThetaGetX0AndXdot(ts, dm, &X0, &Xdot));
954   if (x != X0) {
955     PetscCall(VecAXPBYPCZ(Xdot, -shift, shift, 0, X0, x));
956   } else {
957     PetscCall(VecZeroEntries(Xdot));
958   }
959   /* DM monkey-business allows user code to call TSGetDM() inside of functions evaluated on levels of FAS */
960   dmsave = ts->dm;
961   ts->dm = dm;
962   PetscCall(TSComputeIFunction(ts, th->stage_time, x, Xdot, y, PETSC_FALSE));
963   ts->dm = dmsave;
964   PetscCall(TSThetaRestoreX0AndXdot(ts, dm, &X0, &Xdot));
965   PetscFunctionReturn(PETSC_SUCCESS);
966 }
967 
SNESTSFormJacobian_Theta(SNES snes,Vec x,Mat A,Mat B,TS ts)968 static PetscErrorCode SNESTSFormJacobian_Theta(SNES snes, Vec x, Mat A, Mat B, TS ts)
969 {
970   TS_Theta *th = (TS_Theta *)ts->data;
971   Vec       Xdot;
972   DM        dm, dmsave;
973   PetscReal shift = th->shift;
974 
975   PetscFunctionBegin;
976   PetscCall(SNESGetDM(snes, &dm));
977   /* Xdot has already been computed in SNESTSFormFunction_Theta (SNES guarantees this) */
978   PetscCall(TSThetaGetX0AndXdot(ts, dm, NULL, &Xdot));
979 
980   dmsave = ts->dm;
981   ts->dm = dm;
982   PetscCall(TSComputeIJacobian(ts, th->stage_time, x, Xdot, shift, A, B, PETSC_FALSE));
983   ts->dm = dmsave;
984   PetscCall(TSThetaRestoreX0AndXdot(ts, dm, NULL, &Xdot));
985   PetscFunctionReturn(PETSC_SUCCESS);
986 }
987 
TSForwardSetUp_Theta(TS ts)988 static PetscErrorCode TSForwardSetUp_Theta(TS ts)
989 {
990   TS_Theta *th     = (TS_Theta *)ts->data;
991   TS        quadts = ts->quadraturets;
992 
993   PetscFunctionBegin;
994   /* combine sensitivities to parameters and sensitivities to initial values into one array */
995   th->num_tlm = ts->num_parameters;
996   PetscCall(MatDuplicate(ts->mat_sensip, MAT_DO_NOT_COPY_VALUES, &th->MatDeltaFwdSensip));
997   if (quadts && quadts->mat_sensip) {
998     PetscCall(MatDuplicate(quadts->mat_sensip, MAT_DO_NOT_COPY_VALUES, &th->MatIntegralSensipTemp));
999     PetscCall(MatDuplicate(quadts->mat_sensip, MAT_DO_NOT_COPY_VALUES, &th->MatIntegralSensip0));
1000   }
1001   /* backup sensitivity results for roll-backs */
1002   PetscCall(MatDuplicate(ts->mat_sensip, MAT_DO_NOT_COPY_VALUES, &th->MatFwdSensip0));
1003 
1004   PetscCall(VecDuplicate(ts->vec_sol, &th->VecDeltaFwdSensipCol));
1005   PetscFunctionReturn(PETSC_SUCCESS);
1006 }
1007 
TSForwardReset_Theta(TS ts)1008 static PetscErrorCode TSForwardReset_Theta(TS ts)
1009 {
1010   TS_Theta *th     = (TS_Theta *)ts->data;
1011   TS        quadts = ts->quadraturets;
1012 
1013   PetscFunctionBegin;
1014   if (quadts && quadts->mat_sensip) {
1015     PetscCall(MatDestroy(&th->MatIntegralSensipTemp));
1016     PetscCall(MatDestroy(&th->MatIntegralSensip0));
1017   }
1018   PetscCall(VecDestroy(&th->VecDeltaFwdSensipCol));
1019   PetscCall(MatDestroy(&th->MatDeltaFwdSensip));
1020   PetscCall(MatDestroy(&th->MatFwdSensip0));
1021   PetscFunctionReturn(PETSC_SUCCESS);
1022 }
1023 
TSSetUp_Theta(TS ts)1024 static PetscErrorCode TSSetUp_Theta(TS ts)
1025 {
1026   TS_Theta *th     = (TS_Theta *)ts->data;
1027   TS        quadts = ts->quadraturets;
1028   PetscBool match;
1029 
1030   PetscFunctionBegin;
1031   if (!th->VecCostIntegral0 && quadts && ts->costintegralfwd) { /* back up cost integral */
1032     PetscCall(VecDuplicate(quadts->vec_sol, &th->VecCostIntegral0));
1033   }
1034   if (!th->X) PetscCall(VecDuplicate(ts->vec_sol, &th->X));
1035   if (!th->Xdot) PetscCall(VecDuplicate(ts->vec_sol, &th->Xdot));
1036   if (!th->X0) PetscCall(VecDuplicate(ts->vec_sol, &th->X0));
1037   if (th->endpoint) PetscCall(VecDuplicate(ts->vec_sol, &th->affine));
1038 
1039   th->order = (th->Theta == 0.5) ? 2 : 1;
1040   th->shift = 1 / (th->Theta * ts->time_step);
1041 
1042   PetscCall(TSGetDM(ts, &ts->dm));
1043   PetscCall(DMCoarsenHookAdd(ts->dm, DMCoarsenHook_TSTheta, DMRestrictHook_TSTheta, ts));
1044   PetscCall(DMSubDomainHookAdd(ts->dm, DMSubDomainHook_TSTheta, DMSubDomainRestrictHook_TSTheta, ts));
1045 
1046   PetscCall(TSGetAdapt(ts, &ts->adapt));
1047   PetscCall(TSAdaptCandidatesClear(ts->adapt));
1048   PetscCall(PetscObjectTypeCompare((PetscObject)ts->adapt, TSADAPTNONE, &match));
1049   if (!match) {
1050     if (!th->vec_sol_prev) PetscCall(VecDuplicate(ts->vec_sol, &th->vec_sol_prev));
1051     if (!th->vec_lte_work) PetscCall(VecDuplicate(ts->vec_sol, &th->vec_lte_work));
1052   }
1053   PetscCall(TSGetSNES(ts, &ts->snes));
1054 
1055   ts->stifflyaccurate = (!th->endpoint && th->Theta != 1.0) ? PETSC_FALSE : PETSC_TRUE;
1056   PetscFunctionReturn(PETSC_SUCCESS);
1057 }
1058 
TSAdjointSetUp_Theta(TS ts)1059 static PetscErrorCode TSAdjointSetUp_Theta(TS ts)
1060 {
1061   TS_Theta *th = (TS_Theta *)ts->data;
1062 
1063   PetscFunctionBegin;
1064   PetscCall(VecDuplicateVecs(ts->vecs_sensi[0], ts->numcost, &th->VecsDeltaLam));
1065   PetscCall(VecDuplicateVecs(ts->vecs_sensi[0], ts->numcost, &th->VecsSensiTemp));
1066   if (ts->vecs_sensip) PetscCall(VecDuplicateVecs(ts->vecs_sensip[0], ts->numcost, &th->VecsDeltaMu));
1067   if (ts->vecs_sensi2) {
1068     PetscCall(VecDuplicateVecs(ts->vecs_sensi[0], ts->numcost, &th->VecsDeltaLam2));
1069     PetscCall(VecDuplicateVecs(ts->vecs_sensi2[0], ts->numcost, &th->VecsSensi2Temp));
1070     /* hack ts to make implicit TS solver work when users provide only explicit versions of callbacks (RHSFunction,RHSJacobian,RHSHessian etc.) */
1071     if (!ts->ihessianproduct_fuu) ts->vecs_fuu = ts->vecs_guu;
1072     if (!ts->ihessianproduct_fup) ts->vecs_fup = ts->vecs_gup;
1073   }
1074   if (ts->vecs_sensi2p) {
1075     PetscCall(VecDuplicateVecs(ts->vecs_sensi2p[0], ts->numcost, &th->VecsDeltaMu2));
1076     /* hack ts to make implicit TS solver work when users provide only explicit versions of callbacks (RHSFunction,RHSJacobian,RHSHessian etc.) */
1077     if (!ts->ihessianproduct_fpu) ts->vecs_fpu = ts->vecs_gpu;
1078     if (!ts->ihessianproduct_fpp) ts->vecs_fpp = ts->vecs_gpp;
1079   }
1080   PetscFunctionReturn(PETSC_SUCCESS);
1081 }
1082 
TSSetFromOptions_Theta(TS ts,PetscOptionItems PetscOptionsObject)1083 static PetscErrorCode TSSetFromOptions_Theta(TS ts, PetscOptionItems PetscOptionsObject)
1084 {
1085   TS_Theta *th = (TS_Theta *)ts->data;
1086 
1087   PetscFunctionBegin;
1088   PetscOptionsHeadBegin(PetscOptionsObject, "Theta ODE solver options");
1089   {
1090     PetscCall(PetscOptionsReal("-ts_theta_theta", "Location of stage (0<Theta<=1)", "TSThetaSetTheta", th->Theta, &th->Theta, NULL));
1091     PetscCall(PetscOptionsBool("-ts_theta_endpoint", "Use the endpoint instead of midpoint form of the Theta method", "TSThetaSetEndpoint", th->endpoint, &th->endpoint, NULL));
1092     PetscCall(PetscOptionsBool("-ts_theta_initial_guess_extrapolate", "Extrapolate stage initial guess from previous solution (sometimes unstable)", "TSThetaSetExtrapolate", th->extrapolate, &th->extrapolate, NULL));
1093   }
1094   PetscOptionsHeadEnd();
1095   PetscFunctionReturn(PETSC_SUCCESS);
1096 }
1097 
TSView_Theta(TS ts,PetscViewer viewer)1098 static PetscErrorCode TSView_Theta(TS ts, PetscViewer viewer)
1099 {
1100   TS_Theta *th = (TS_Theta *)ts->data;
1101   PetscBool isascii;
1102 
1103   PetscFunctionBegin;
1104   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
1105   if (isascii) {
1106     PetscCall(PetscViewerASCIIPrintf(viewer, "  Theta=%g\n", (double)th->Theta));
1107     PetscCall(PetscViewerASCIIPrintf(viewer, "  Extrapolation=%s\n", th->extrapolate ? "yes" : "no"));
1108   }
1109   PetscFunctionReturn(PETSC_SUCCESS);
1110 }
1111 
TSThetaGetTheta_Theta(TS ts,PetscReal * theta)1112 static PetscErrorCode TSThetaGetTheta_Theta(TS ts, PetscReal *theta)
1113 {
1114   TS_Theta *th = (TS_Theta *)ts->data;
1115 
1116   PetscFunctionBegin;
1117   *theta = th->Theta;
1118   PetscFunctionReturn(PETSC_SUCCESS);
1119 }
1120 
TSThetaSetTheta_Theta(TS ts,PetscReal theta)1121 static PetscErrorCode TSThetaSetTheta_Theta(TS ts, PetscReal theta)
1122 {
1123   TS_Theta *th = (TS_Theta *)ts->data;
1124 
1125   PetscFunctionBegin;
1126   PetscCheck(theta > 0 && theta <= 1, PetscObjectComm((PetscObject)ts), PETSC_ERR_ARG_OUTOFRANGE, "Theta %g not in range (0,1]", (double)theta);
1127   th->Theta = theta;
1128   th->order = (th->Theta == 0.5) ? 2 : 1;
1129   PetscFunctionReturn(PETSC_SUCCESS);
1130 }
1131 
TSThetaGetEndpoint_Theta(TS ts,PetscBool * endpoint)1132 static PetscErrorCode TSThetaGetEndpoint_Theta(TS ts, PetscBool *endpoint)
1133 {
1134   TS_Theta *th = (TS_Theta *)ts->data;
1135 
1136   PetscFunctionBegin;
1137   *endpoint = th->endpoint;
1138   PetscFunctionReturn(PETSC_SUCCESS);
1139 }
1140 
TSThetaSetEndpoint_Theta(TS ts,PetscBool flg)1141 static PetscErrorCode TSThetaSetEndpoint_Theta(TS ts, PetscBool flg)
1142 {
1143   TS_Theta *th = (TS_Theta *)ts->data;
1144 
1145   PetscFunctionBegin;
1146   th->endpoint = flg;
1147   PetscFunctionReturn(PETSC_SUCCESS);
1148 }
1149 
1150 #if defined(PETSC_HAVE_COMPLEX)
TSComputeLinearStability_Theta(TS ts,PetscReal xr,PetscReal xi,PetscReal * yr,PetscReal * yi)1151 static PetscErrorCode TSComputeLinearStability_Theta(TS ts, PetscReal xr, PetscReal xi, PetscReal *yr, PetscReal *yi)
1152 {
1153   PetscComplex z  = xr + xi * PETSC_i, f;
1154   TS_Theta    *th = (TS_Theta *)ts->data;
1155 
1156   PetscFunctionBegin;
1157   f   = (1.0 + (1.0 - th->Theta) * z) / (1.0 - th->Theta * z);
1158   *yr = PetscRealPartComplex(f);
1159   *yi = PetscImaginaryPartComplex(f);
1160   PetscFunctionReturn(PETSC_SUCCESS);
1161 }
1162 #endif
1163 
TSGetStages_Theta(TS ts,PetscInt * ns,Vec * Y[])1164 static PetscErrorCode TSGetStages_Theta(TS ts, PetscInt *ns, Vec *Y[])
1165 {
1166   TS_Theta *th = (TS_Theta *)ts->data;
1167 
1168   PetscFunctionBegin;
1169   if (ns) {
1170     if (!th->endpoint && th->Theta != 1.0) *ns = 1; /* midpoint form */
1171     else *ns = 2;                                   /* endpoint form */
1172   }
1173   if (Y) {
1174     if (!th->endpoint && th->Theta != 1.0) {
1175       th->Stages[0] = th->X;
1176     } else {
1177       th->Stages[0] = th->X0;
1178       th->Stages[1] = ts->vec_sol; /* stiffly accurate */
1179     }
1180     *Y = th->Stages;
1181   }
1182   PetscFunctionReturn(PETSC_SUCCESS);
1183 }
1184 
1185 /*MC
1186       TSTHETA - DAE solver using the implicit Theta method
1187 
1188    Level: beginner
1189 
1190    Options Database Keys:
1191 +  -ts_theta_theta <Theta> - Location of stage (0<Theta<=1)
1192 .  -ts_theta_endpoint <flag> - Use the endpoint (like Crank-Nicholson) instead of midpoint form of the Theta method
1193 -  -ts_theta_initial_guess_extrapolate <flg> - Extrapolate stage initial guess from previous solution (sometimes unstable)
1194 
1195    Notes:
1196 .vb
1197   -ts_type theta -ts_theta_theta 1.0 corresponds to backward Euler (TSBEULER)
1198   -ts_type theta -ts_theta_theta 0.5 corresponds to the implicit midpoint rule
1199   -ts_type theta -ts_theta_theta 0.5 -ts_theta_endpoint corresponds to Crank-Nicholson (TSCN)
1200 .ve
1201 
1202    The endpoint variant of the Theta method and backward Euler can be applied to DAE. The midpoint variant is not suitable for DAEs because it is not stiffly accurate.
1203 
1204    The midpoint variant is cast as a 1-stage implicit Runge-Kutta method.
1205 
1206 .vb
1207   Theta | Theta
1208   -------------
1209         |  1
1210 .ve
1211 
1212    For the default Theta=0.5, this is also known as the implicit midpoint rule.
1213 
1214    When the endpoint variant is chosen, the method becomes a 2-stage method with first stage explicit:
1215 
1216 .vb
1217   0 | 0         0
1218   1 | 1-Theta   Theta
1219   -------------------
1220     | 1-Theta   Theta
1221 .ve
1222 
1223    For the default Theta=0.5, this is the trapezoid rule (also known as Crank-Nicolson, see TSCN).
1224 
1225    To apply a diagonally implicit RK method to DAE, the stage formula
1226 .vb
1227   Y_i = X + h sum_j a_ij Y'_j
1228 .ve
1229    is interpreted as a formula for Y'_i in terms of Y_i and known values (Y'_j, j<i)
1230 
1231 .seealso: [](ch_ts), `TSCreate()`, `TS`, `TSSetType()`, `TSCN`, `TSBEULER`, `TSThetaSetTheta()`, `TSThetaSetEndpoint()`
1232 M*/
TSCreate_Theta(TS ts)1233 PETSC_EXTERN PetscErrorCode TSCreate_Theta(TS ts)
1234 {
1235   TS_Theta *th;
1236 
1237   PetscFunctionBegin;
1238   ts->ops->reset          = TSReset_Theta;
1239   ts->ops->adjointreset   = TSAdjointReset_Theta;
1240   ts->ops->destroy        = TSDestroy_Theta;
1241   ts->ops->view           = TSView_Theta;
1242   ts->ops->setup          = TSSetUp_Theta;
1243   ts->ops->adjointsetup   = TSAdjointSetUp_Theta;
1244   ts->ops->adjointreset   = TSAdjointReset_Theta;
1245   ts->ops->step           = TSStep_Theta;
1246   ts->ops->interpolate    = TSInterpolate_Theta;
1247   ts->ops->evaluatewlte   = TSEvaluateWLTE_Theta;
1248   ts->ops->rollback       = TSRollBack_Theta;
1249   ts->ops->resizeregister = TSResizeRegister_Theta;
1250   ts->ops->setfromoptions = TSSetFromOptions_Theta;
1251   ts->ops->snesfunction   = SNESTSFormFunction_Theta;
1252   ts->ops->snesjacobian   = SNESTSFormJacobian_Theta;
1253 #if defined(PETSC_HAVE_COMPLEX)
1254   ts->ops->linearstability = TSComputeLinearStability_Theta;
1255 #endif
1256   ts->ops->getstages       = TSGetStages_Theta;
1257   ts->ops->adjointstep     = TSAdjointStep_Theta;
1258   ts->ops->adjointintegral = TSAdjointCostIntegral_Theta;
1259   ts->ops->forwardintegral = TSForwardCostIntegral_Theta;
1260   ts->default_adapt_type   = TSADAPTNONE;
1261 
1262   ts->ops->forwardsetup     = TSForwardSetUp_Theta;
1263   ts->ops->forwardreset     = TSForwardReset_Theta;
1264   ts->ops->forwardstep      = TSForwardStep_Theta;
1265   ts->ops->forwardgetstages = TSForwardGetStages_Theta;
1266 
1267   ts->usessnes = PETSC_TRUE;
1268 
1269   PetscCall(PetscNew(&th));
1270   ts->data = (void *)th;
1271 
1272   th->VecsDeltaLam   = NULL;
1273   th->VecsDeltaMu    = NULL;
1274   th->VecsSensiTemp  = NULL;
1275   th->VecsSensi2Temp = NULL;
1276 
1277   th->extrapolate = PETSC_FALSE;
1278   th->Theta       = 0.5;
1279   th->order       = 2;
1280   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaGetTheta_C", TSThetaGetTheta_Theta));
1281   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaSetTheta_C", TSThetaSetTheta_Theta));
1282   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaGetEndpoint_C", TSThetaGetEndpoint_Theta));
1283   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSThetaSetEndpoint_C", TSThetaSetEndpoint_Theta));
1284   PetscFunctionReturn(PETSC_SUCCESS);
1285 }
1286 
1287 /*@
1288   TSThetaGetTheta - Get the abscissa of the stage in (0,1] for `TSTHETA`
1289 
1290   Not Collective
1291 
1292   Input Parameter:
1293 . ts - timestepping context
1294 
1295   Output Parameter:
1296 . theta - stage abscissa
1297 
1298   Level: advanced
1299 
1300   Note:
1301   Use of this function is normally only required to hack `TSTHETA` to use a modified integration scheme.
1302 
1303 .seealso: [](ch_ts), `TSThetaSetTheta()`, `TSTHETA`
1304 @*/
TSThetaGetTheta(TS ts,PetscReal * theta)1305 PetscErrorCode TSThetaGetTheta(TS ts, PetscReal *theta)
1306 {
1307   PetscFunctionBegin;
1308   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
1309   PetscAssertPointer(theta, 2);
1310   PetscUseMethod(ts, "TSThetaGetTheta_C", (TS, PetscReal *), (ts, theta));
1311   PetscFunctionReturn(PETSC_SUCCESS);
1312 }
1313 
1314 /*@
1315   TSThetaSetTheta - Set the abscissa of the stage in (0,1]  for `TSTHETA`
1316 
1317   Not Collective
1318 
1319   Input Parameters:
1320 + ts    - timestepping context
1321 - theta - stage abscissa
1322 
1323   Options Database Key:
1324 . -ts_theta_theta <theta> - set theta
1325 
1326   Level: intermediate
1327 
1328 .seealso: [](ch_ts), `TSThetaGetTheta()`, `TSTHETA`, `TSCN`
1329 @*/
TSThetaSetTheta(TS ts,PetscReal theta)1330 PetscErrorCode TSThetaSetTheta(TS ts, PetscReal theta)
1331 {
1332   PetscFunctionBegin;
1333   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
1334   PetscTryMethod(ts, "TSThetaSetTheta_C", (TS, PetscReal), (ts, theta));
1335   PetscFunctionReturn(PETSC_SUCCESS);
1336 }
1337 
1338 /*@
1339   TSThetaGetEndpoint - Gets whether to use the endpoint variant of the method (e.g. trapezoid/Crank-Nicolson instead of midpoint rule) for `TSTHETA`
1340 
1341   Not Collective
1342 
1343   Input Parameter:
1344 . ts - timestepping context
1345 
1346   Output Parameter:
1347 . endpoint - `PETSC_TRUE` when using the endpoint variant
1348 
1349   Level: advanced
1350 
1351 .seealso: [](ch_ts), `TSThetaSetEndpoint()`, `TSTHETA`, `TSCN`
1352 @*/
TSThetaGetEndpoint(TS ts,PetscBool * endpoint)1353 PetscErrorCode TSThetaGetEndpoint(TS ts, PetscBool *endpoint)
1354 {
1355   PetscFunctionBegin;
1356   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
1357   PetscAssertPointer(endpoint, 2);
1358   PetscUseMethod(ts, "TSThetaGetEndpoint_C", (TS, PetscBool *), (ts, endpoint));
1359   PetscFunctionReturn(PETSC_SUCCESS);
1360 }
1361 
1362 /*@
1363   TSThetaSetEndpoint - Sets whether to use the endpoint variant of the method (e.g. trapezoid/Crank-Nicolson instead of midpoint rule) for `TSTHETA`
1364 
1365   Not Collective
1366 
1367   Input Parameters:
1368 + ts  - timestepping context
1369 - flg - `PETSC_TRUE` to use the endpoint variant
1370 
1371   Options Database Key:
1372 . -ts_theta_endpoint <flg> - use the endpoint variant
1373 
1374   Level: intermediate
1375 
1376 .seealso: [](ch_ts), `TSTHETA`, `TSCN`
1377 @*/
TSThetaSetEndpoint(TS ts,PetscBool flg)1378 PetscErrorCode TSThetaSetEndpoint(TS ts, PetscBool flg)
1379 {
1380   PetscFunctionBegin;
1381   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
1382   PetscTryMethod(ts, "TSThetaSetEndpoint_C", (TS, PetscBool), (ts, flg));
1383   PetscFunctionReturn(PETSC_SUCCESS);
1384 }
1385 
1386 /*
1387  * TSBEULER and TSCN are straightforward specializations of TSTHETA.
1388  * The creation functions for these specializations are below.
1389  */
1390 
TSSetUp_BEuler(TS ts)1391 static PetscErrorCode TSSetUp_BEuler(TS ts)
1392 {
1393   TS_Theta *th = (TS_Theta *)ts->data;
1394 
1395   PetscFunctionBegin;
1396   PetscCheck(th->Theta == 1.0, PetscObjectComm((PetscObject)ts), PETSC_ERR_OPT_OVERWRITE, "Can not change the default value (1) of theta when using backward Euler");
1397   PetscCheck(!th->endpoint, PetscObjectComm((PetscObject)ts), PETSC_ERR_OPT_OVERWRITE, "Can not change to the endpoint form of the Theta methods when using backward Euler");
1398   PetscCall(TSSetUp_Theta(ts));
1399   PetscFunctionReturn(PETSC_SUCCESS);
1400 }
1401 
TSView_BEuler(TS ts,PetscViewer viewer)1402 static PetscErrorCode TSView_BEuler(TS ts, PetscViewer viewer)
1403 {
1404   PetscFunctionBegin;
1405   PetscFunctionReturn(PETSC_SUCCESS);
1406 }
1407 
1408 /*MC
1409       TSBEULER - ODE solver using the implicit backward Euler method
1410 
1411   Level: beginner
1412 
1413   Note:
1414   `TSBEULER` is equivalent to `TSTHETA` with Theta=1.0 or `-ts_type theta -ts_theta_theta 1.0`
1415 
1416 .seealso: [](ch_ts), `TSCreate()`, `TS`, `TSSetType()`, `TSEULER`, `TSCN`, `TSTHETA`
1417 M*/
TSCreate_BEuler(TS ts)1418 PETSC_EXTERN PetscErrorCode TSCreate_BEuler(TS ts)
1419 {
1420   PetscFunctionBegin;
1421   PetscCall(TSCreate_Theta(ts));
1422   PetscCall(TSThetaSetTheta(ts, 1.0));
1423   PetscCall(TSThetaSetEndpoint(ts, PETSC_FALSE));
1424   ts->ops->setup = TSSetUp_BEuler;
1425   ts->ops->view  = TSView_BEuler;
1426   PetscFunctionReturn(PETSC_SUCCESS);
1427 }
1428 
TSSetUp_CN(TS ts)1429 static PetscErrorCode TSSetUp_CN(TS ts)
1430 {
1431   TS_Theta *th = (TS_Theta *)ts->data;
1432 
1433   PetscFunctionBegin;
1434   PetscCheck(th->Theta == 0.5, PetscObjectComm((PetscObject)ts), PETSC_ERR_OPT_OVERWRITE, "Can not change the default value (0.5) of theta when using Crank-Nicolson");
1435   PetscCheck(th->endpoint, PetscObjectComm((PetscObject)ts), PETSC_ERR_OPT_OVERWRITE, "Can not change to the midpoint form of the Theta methods when using Crank-Nicolson");
1436   PetscCall(TSSetUp_Theta(ts));
1437   PetscFunctionReturn(PETSC_SUCCESS);
1438 }
1439 
TSView_CN(TS ts,PetscViewer viewer)1440 static PetscErrorCode TSView_CN(TS ts, PetscViewer viewer)
1441 {
1442   PetscFunctionBegin;
1443   PetscFunctionReturn(PETSC_SUCCESS);
1444 }
1445 
1446 /*MC
1447       TSCN - ODE solver using the implicit Crank-Nicolson method.
1448 
1449   Level: beginner
1450 
1451   Notes:
1452   `TSCN` is equivalent to `TSTHETA` with Theta=0.5 and the "endpoint" option set. I.e.
1453 .vb
1454   -ts_type theta
1455   -ts_theta_theta 0.5
1456   -ts_theta_endpoint
1457 .ve
1458 
1459 .seealso: [](ch_ts), `TSCreate()`, `TS`, `TSSetType()`, `TSBEULER`, `TSTHETA`, `TSType`,
1460 M*/
TSCreate_CN(TS ts)1461 PETSC_EXTERN PetscErrorCode TSCreate_CN(TS ts)
1462 {
1463   PetscFunctionBegin;
1464   PetscCall(TSCreate_Theta(ts));
1465   PetscCall(TSThetaSetTheta(ts, 0.5));
1466   PetscCall(TSThetaSetEndpoint(ts, PETSC_TRUE));
1467   ts->ops->setup = TSSetUp_CN;
1468   ts->ops->view  = TSView_CN;
1469   PetscFunctionReturn(PETSC_SUCCESS);
1470 }
1471