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